2024-06-08 13:25:09 -04:00
|
|
|
/*
|
|
|
|
|
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
|
|
|
|
* running old operating systems and software designed for IBM
|
|
|
|
|
* PC systems and compatibles from 1981 through fairly recent
|
|
|
|
|
* system designs based on the PCI bus.
|
|
|
|
|
*
|
|
|
|
|
* This file is part of the 86Box distribution.
|
|
|
|
|
*
|
|
|
|
|
* 86Box VM manager client socket module
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* Authors: cold-brewed
|
|
|
|
|
*
|
|
|
|
|
* Copyright 2024 cold-brewed
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "qt_vmmanager_clientsocket.hpp"
|
|
|
|
|
#include "qt_vmmanager_protocol.hpp"
|
|
|
|
|
#include <QJsonArray>
|
|
|
|
|
#include <QJsonDocument>
|
|
|
|
|
#include <QJsonObject>
|
|
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
|
#include "86box/plat.h"
|
2025-08-28 17:56:33 +06:00
|
|
|
#include "86box/config.h"
|
2024-06-08 13:25:09 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VMManagerClientSocket::VMManagerClientSocket(QObject* obj) : server_connected(false)
|
|
|
|
|
{
|
|
|
|
|
socket = new QLocalSocket;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
VMManagerClientSocket::dataReady()
|
|
|
|
|
{
|
|
|
|
|
// emit signal?
|
|
|
|
|
QDataStream stream(socket);
|
|
|
|
|
stream.setVersion(QDataStream::Qt_5_7);
|
|
|
|
|
QByteArray jsonData;
|
|
|
|
|
for (;;) {
|
|
|
|
|
// start a transaction
|
|
|
|
|
stream.startTransaction();
|
|
|
|
|
// try to read the data
|
|
|
|
|
stream >> jsonData;
|
|
|
|
|
if (stream.commitTransaction()) {
|
|
|
|
|
// first try to successfully read some data
|
|
|
|
|
// need to also make sure it's valid json
|
|
|
|
|
QJsonParseError parse_error{};
|
|
|
|
|
// try to create a document with the data received
|
|
|
|
|
const QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parse_error);
|
|
|
|
|
if (parse_error.error == QJsonParseError::NoError) {
|
|
|
|
|
// the data received was valid json
|
|
|
|
|
if (jsonDoc.isObject()) {
|
|
|
|
|
// and is a valid json object
|
|
|
|
|
// parse the json
|
|
|
|
|
jsonReceived(jsonDoc.object());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// If the received data isn't valid json,
|
|
|
|
|
// loop and try to read more if available
|
|
|
|
|
} else {
|
|
|
|
|
// read failed, socket is reverted to its previous state (before the transaction)
|
|
|
|
|
// Exit the loop and wait for more data to become available
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
VMManagerClientSocket::IPCConnect(const QString &server)
|
|
|
|
|
{
|
|
|
|
|
server_name = server;
|
|
|
|
|
connect(socket, &QLocalSocket::connected, this, &VMManagerClientSocket::connected);
|
|
|
|
|
connect(socket, &QLocalSocket::disconnected, this, &VMManagerClientSocket::disconnected);
|
|
|
|
|
connect(socket, &QLocalSocket::errorOccurred, this, &VMManagerClientSocket::connectionError);
|
|
|
|
|
connect(socket, &QLocalSocket::readyRead, this, &VMManagerClientSocket::dataReady);
|
|
|
|
|
|
|
|
|
|
socket->connectToServer(server_name);
|
|
|
|
|
|
|
|
|
|
if(!socket->isValid()) {
|
|
|
|
|
qInfo("Could not connect to server: %s", qPrintable(socket->errorString()));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
qInfo("Connection Successful");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
VMManagerClientSocket::connected() const
|
|
|
|
|
{
|
|
|
|
|
// TODO: signal
|
|
|
|
|
qDebug("Connected to %s", qPrintable(server_name));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
VMManagerClientSocket::disconnected() const
|
|
|
|
|
{
|
|
|
|
|
// TODO: signal
|
|
|
|
|
qDebug("Disconnected from %s", qPrintable(server_name));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
VMManagerClientSocket::sendMessage(const VMManagerProtocol::ClientMessage protocol_message) const
|
|
|
|
|
{
|
|
|
|
|
sendMessageFull(protocol_message, QStringList(), QJsonObject());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
VMManagerClientSocket::sendMessageWithList(const VMManagerProtocol::ClientMessage protocol_message, const QStringList &list) const
|
|
|
|
|
{
|
|
|
|
|
sendMessageFull(protocol_message, list, QJsonObject());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
VMManagerClientSocket::sendMessageWithObject(const VMManagerProtocol::ClientMessage protocol_message, const QJsonObject &json) const
|
|
|
|
|
{
|
|
|
|
|
sendMessageFull(protocol_message, QStringList(), json);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
VMManagerClientSocket::sendMessageFull(const VMManagerProtocol::ClientMessage protocol_message, const QStringList &list, const QJsonObject &json) const
|
|
|
|
|
{
|
|
|
|
|
QDataStream clientStream(socket);
|
|
|
|
|
clientStream.setVersion(QDataStream::Qt_5_7);
|
|
|
|
|
auto packet = new VMManagerProtocol(VMManagerProtocol::Sender::Client);
|
|
|
|
|
auto jsonMessage = packet->protocolClientMessage(protocol_message);
|
|
|
|
|
if (!list.isEmpty()) {
|
|
|
|
|
jsonMessage["list"] = QJsonArray::fromStringList(list);
|
|
|
|
|
}
|
|
|
|
|
// TODO: Add the logic for including objects
|
|
|
|
|
if(!json.isEmpty()) {
|
|
|
|
|
jsonMessage["params"] = json;
|
|
|
|
|
}
|
|
|
|
|
clientStream << QJsonDocument(jsonMessage).toJson(QJsonDocument::Compact);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
VMManagerClientSocket::jsonReceived(const QJsonObject &json)
|
|
|
|
|
{
|
|
|
|
|
// The serialization portion has already validated the message as json.
|
|
|
|
|
// Ensure it has the required fields
|
|
|
|
|
if (!VMManagerProtocol::hasRequiredFields(json)) {
|
|
|
|
|
// TODO: Error handling of some sort, emit signals
|
|
|
|
|
qDebug("Invalid message received from client: required fields missing. Object:");
|
|
|
|
|
qDebug() << json;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// qDebug() << Q_FUNC_INFO << json;
|
|
|
|
|
|
|
|
|
|
// Parsing happens here. When adding new types, make sure to first add them
|
|
|
|
|
// to VMManagerProtocol::ManagerMessage and then add it to the list here.
|
|
|
|
|
// If a signal needs to be emitted, add that as well and connect to slots
|
|
|
|
|
// as appropriate.
|
|
|
|
|
|
|
|
|
|
switch (VMManagerProtocol::getManagerMessageType(json)) {
|
|
|
|
|
case VMManagerProtocol::ManagerMessage::Pause:
|
|
|
|
|
qDebug("Pause command received from manager");
|
|
|
|
|
emit pause();
|
|
|
|
|
break;
|
|
|
|
|
case VMManagerProtocol::ManagerMessage::ResetVM:
|
|
|
|
|
qDebug("Reset VM command received from manager");
|
|
|
|
|
emit resetVM();
|
|
|
|
|
break;
|
|
|
|
|
case VMManagerProtocol::ManagerMessage::ShowSettings:
|
|
|
|
|
qDebug("Show settings command received from manager");
|
|
|
|
|
emit showsettings();
|
|
|
|
|
break;
|
|
|
|
|
case VMManagerProtocol::ManagerMessage::CtrlAltDel:
|
|
|
|
|
qDebug("CtrlAltDel command received from manager");
|
|
|
|
|
emit ctrlaltdel();
|
|
|
|
|
break;
|
|
|
|
|
case VMManagerProtocol::ManagerMessage::RequestShutdown:
|
|
|
|
|
qDebug("RequestShutdown command received from manager");
|
|
|
|
|
emit request_shutdown();
|
|
|
|
|
break;
|
|
|
|
|
case VMManagerProtocol::ManagerMessage::ForceShutdown:
|
|
|
|
|
qDebug("ForceShutdown command received from manager");
|
|
|
|
|
emit force_shutdown();
|
|
|
|
|
break;
|
|
|
|
|
case VMManagerProtocol::ManagerMessage::RequestStatus:
|
|
|
|
|
qDebug("Status request command received from manager");
|
|
|
|
|
break;
|
2025-08-28 17:56:33 +06:00
|
|
|
case VMManagerProtocol::ManagerMessage::GlobalConfigurationChanged:
|
|
|
|
|
{
|
|
|
|
|
config_load_global();
|
|
|
|
|
#ifdef Q_OS_WINDOWS
|
|
|
|
|
void selectDarkMode();
|
|
|
|
|
selectDarkMode();
|
|
|
|
|
#endif
|
|
|
|
|
break;
|
|
|
|
|
}
|
2024-06-08 13:25:09 -04:00
|
|
|
default:
|
|
|
|
|
qDebug("Unknown client message type received:");
|
|
|
|
|
qDebug() << json;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
VMManagerClientSocket::connectionError(const QLocalSocket::LocalSocketError socketError)
|
|
|
|
|
{
|
|
|
|
|
qInfo("A connection error has occurred: ");
|
|
|
|
|
switch (socketError) {
|
|
|
|
|
case QLocalSocket::ServerNotFoundError:
|
|
|
|
|
qInfo("Server not found");
|
|
|
|
|
break;
|
|
|
|
|
case QLocalSocket::ConnectionRefusedError:
|
|
|
|
|
qInfo("Connection refused");
|
|
|
|
|
break;
|
|
|
|
|
case QLocalSocket::PeerClosedError:
|
|
|
|
|
qInfo("Peer closed");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
qInfo() << "QLocalSocket::LocalSocketError " << socketError;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
VMManagerClientSocket::eventFilter(QObject *obj, QEvent *event)
|
|
|
|
|
{
|
|
|
|
|
if (socket->state() == QLocalSocket::ConnectedState) {
|
|
|
|
|
VMManagerProtocol::RunningState running_state;
|
|
|
|
|
if (event->type() == QEvent::WindowBlocked) {
|
|
|
|
|
running_state = dopause ? VMManagerProtocol::RunningState::PausedWaiting : VMManagerProtocol::RunningState::RunningWaiting;
|
|
|
|
|
clientRunningStateChanged(running_state);
|
2025-07-11 18:04:58 +06:00
|
|
|
window_blocked = true;
|
2024-06-08 13:25:09 -04:00
|
|
|
} else if (event->type() == QEvent::WindowUnblocked) {
|
2025-07-11 22:21:32 +06:00
|
|
|
window_blocked = false;
|
2024-06-08 13:25:09 -04:00
|
|
|
running_state = dopause ? VMManagerProtocol::RunningState::Paused : VMManagerProtocol::RunningState::Running;
|
|
|
|
|
clientRunningStateChanged(running_state);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return QObject::eventFilter(obj, event);
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-11 18:04:58 +06:00
|
|
|
void
|
|
|
|
|
VMManagerClientSocket::sendWinIdMessage(WId id)
|
|
|
|
|
{
|
|
|
|
|
QJsonObject extra_object;
|
|
|
|
|
extra_object["params"] = static_cast<int>(id);
|
|
|
|
|
sendMessageWithObject(VMManagerProtocol::ClientMessage::WinIdMessage, extra_object);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-08 13:25:09 -04:00
|
|
|
void
|
|
|
|
|
VMManagerClientSocket::clientRunningStateChanged(VMManagerProtocol::RunningState state) const
|
|
|
|
|
{
|
|
|
|
|
QJsonObject extra_object;
|
2025-07-11 18:04:58 +06:00
|
|
|
if ((state == VMManagerProtocol::RunningState::Paused
|
|
|
|
|
|| state == VMManagerProtocol::RunningState::Running) && window_blocked) {
|
|
|
|
|
state = (state == VMManagerProtocol::RunningState::Paused) ? VMManagerProtocol::RunningState::PausedWaiting : VMManagerProtocol::RunningState::RunningWaiting;
|
|
|
|
|
}
|
2024-06-08 13:25:09 -04:00
|
|
|
extra_object["status"] = static_cast<int>(state);
|
|
|
|
|
sendMessageWithObject(VMManagerProtocol::ClientMessage::RunningStateChanged, extra_object);
|
|
|
|
|
}
|
2025-08-02 00:34:53 +05:00
|
|
|
|
2025-08-28 17:56:33 +06:00
|
|
|
void
|
|
|
|
|
VMManagerClientSocket::globalConfigurationChanged() const
|
|
|
|
|
{
|
|
|
|
|
sendMessage(VMManagerProtocol::ClientMessage::GlobalConfigurationChanged);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-02 00:34:53 +05:00
|
|
|
void
|
|
|
|
|
VMManagerClientSocket::configurationChanged() const
|
|
|
|
|
{
|
|
|
|
|
sendMessage(VMManagerProtocol::ClientMessage::ConfigurationChanged);
|
|
|
|
|
}
|