/* * 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 system module * * * * Authors: cold-brewed * * Copyright 2024 cold-brewed */ #include #include #include #include #include #include #include #include #include #include #include "qt_vmmanager_system.hpp" // #include "qt_vmmanager_details_section.hpp" #include "qt_vmmanager_detailsection.hpp" extern "C" { #include <86box/86box.h> #include <86box/device.h> #include <86box/video.h> // #include <86box/vid_xga_device.h> #include <86box/machine.h> #include <86box/plat.h> #include <86box/sound.h> #include #include <86box/thread.h> // required for network.h #include <86box/timer.h> // required for network.h and fdd.h #include <86box/cdrom.h> #include <86box/scsi.h> #include <86box/fdd.h> #include <86box/gameport.h> #include <86box/midi.h> #include <86box/network.h> #include <86box/mouse.h> } VMManagerSystem::VMManagerSystem(const QString &sysconfig_file) { // The 86Box configuration file config_file = QFileInfo(sysconfig_file); // The default name of the system. This is the name of the directory // that contains the 86box configuration file config_name = config_file.dir().dirName(); // The full path of the directory that contains the 86box configuration file config_dir = shortened_dir = config_file.dir().path(); process_status = ProcessStatus::Stopped; // Main 86Box uses usr_path for UUID which includes the trailing slash. // Make sure to append the slash here so the UUIDs will match auto uuid_path = config_dir; if (!uuid_path.endsWith("/")) { uuid_path.append("/"); } // In the configuration file the UUID is used as a unique value uuid = QUuid::createUuidV5(QUuid{}, uuid_path).toString(QUuid::WithoutBraces); // That unique value is used to map the information to each individual system. config_settings = new VMManagerConfig(VMManagerConfig::ConfigType::System, uuid); // On non-windows platforms, shortened_dir will replace the home directory path with ~ // and be used as the tool tip in the list view #if not defined(Q_OS_WINDOWS) if (config_dir.startsWith(QDir::homePath())) { shortened_dir.replace(QDir::homePath(), "~"); } #endif loadSettings(); setupPaths(); // Paths must be setup before vars! setupVars(); serverIsRunning = false; window_obscured = false; find86BoxBinary(); platform = QApplication::platformName(); process = new QProcess(); connect(process, &QProcess::stateChanged, this, &VMManagerSystem::processStatusChanged); // Server type for this instance (Standard should always be used instead of Legacy) socket_server_type = VMManagerServerSocket::ServerType::Standard; socket_server = new VMManagerServerSocket(config_file, socket_server_type); // NOTE: When unique names or UUIDs are written to the individual VM config file, use that // here instead of the auto-generated unique_name // Save settings once everything is initialized saveSettings(); } VMManagerSystem::~VMManagerSystem() { delete socket_server; } QVector VMManagerSystem::scanForConfigs(const QString &searchPath) { QElapsedTimer scanTimer; scanTimer.start(); QVector system_configs; const auto config = new VMManagerConfig(VMManagerConfig::ConfigType::General); auto systemDirConfig = config->getStringValue("system_directory"); const auto config_file_name = QString("86box.cfg"); const QStringList filters = {config_file_name}; QStringList matches; // TODO: Preferences. Once I get the CLI args worked out. // For now it just takes vmm_path from the CLI QString search_directory; // if(searchPath.isEmpty()) { // // If the location isn't specified in function call, use the one loaded // // from the config file // search_directory = systemDirConfig; // } else { // search_directory = searchPath; // } search_directory = searchPath.isEmpty()? vmm_path : searchPath; if(!QDir(search_directory).exists()) { qWarning() << "Path" << search_directory << "does not exist. Cannot continue"; return {}; } QDirIterator dir_iterator(search_directory, filters, QDir::Files, QDirIterator::Subdirectories); qInfo("Searching %s for %s", qPrintable(search_directory), qPrintable(config_file_name)); QElapsedTimer timer; timer.start(); while (dir_iterator.hasNext()) { QString filename = dir_iterator.next(); matches.append(filename); } const auto scanElapsed = timer.elapsed(); qDebug().noquote().nospace() << "Found " << matches.size() << " configs in " << search_directory <<". Scan took " << scanElapsed << " ms"; timer.restart(); // foreach (QFileInfo hit, matches) { // system_configs.append(new VMManagerSystem(hit)); // } for (const auto &filename : matches) { system_configs.append(new VMManagerSystem(filename)); } auto elapsed = timer.elapsed(); qDebug() << "Load loop took" << elapsed << "ms for" << matches.size() << "loads"; qDebug() << "Overall scan time was" << scanTimer.elapsed() << "ms, average" << elapsed / matches.size() << "ms / load"; return system_configs; } QString VMManagerSystem::generateTemporaryFilename() { QTemporaryFile tempFile; // File will be closed once the QTemporaryFile object goes out of scope tempFile.setAutoRemove(true); tempFile.open(); return tempFile.fileName(); } QFileInfoList VMManagerSystem::getScreenshots() { // Don't bother unless the directory exists if(!screenshot_directory.exists()) { return {}; } auto screen_scan_dir = QDir(screenshot_directory.path(), "Monitor_1*", QDir::SortFlag::LocaleAware | QDir::SortFlag::IgnoreCase, QDir::Files); auto screenshot_files = screen_scan_dir.entryInfoList(); return screenshot_files; } void VMManagerSystem::loadSettings() { // First, load the information from the 86box.cfg QSettings settings(config_file.filePath(), QSettings::IniFormat); if (settings.status() != QSettings::NoError) { qWarning() << "Error loading" << config_file.path() << " status:" << settings.status(); } // qInfo() << "Loaded "<< config_file.filePath() << "status:" << settings.status(); // Clear out the config hash in case the config is reloaded for (const auto &outer_key : config_hash.keys()) { config_hash[outer_key].clear(); } // General for (const auto &key_name : settings.childKeys()) { config_hash["General"][key_name] = settings.value(key_name).toString(); } for (auto &group_name : settings.childGroups()) { settings.beginGroup(group_name); for (const auto &key_name : settings.allKeys()) { QString setting_value; // QSettings will interpret lines with commas as QStringList. // Check for it and join them back to a string. if (settings.value(key_name).type() == QVariant::StringList) { setting_value = settings.value(key_name).toStringList().join(", "); } else { setting_value = settings.value(key_name).toString(); } config_hash[group_name][key_name] = setting_value; } settings.endGroup(); } // Next, load the information from the vmm config for this system // Display name auto loadedDisplayName = config_settings->getStringValue("display_name"); if (!loadedDisplayName.isEmpty()) { displayName = loadedDisplayName; } else { displayName = config_name; } // Notes auto loadedNotes = config_settings->getStringValue("notes"); if (!loadedNotes.isEmpty()) { notes = loadedNotes; } // Timestamp auto loadedTimestamp = config_settings->getStringValue("timestamp"); if (!loadedTimestamp.isEmpty()) { // Make sure it is valid if (auto newTimestamp = QDateTime::fromString(loadedTimestamp, Qt::ISODate); newTimestamp.isValid()) { lastUsedTimestamp = newTimestamp; } } // Icon auto loadedIcon = config_settings->getStringValue("icon"); if (!loadedIcon.isEmpty()) { icon = loadedIcon; } } void VMManagerSystem::saveSettings() { if(!isValid()) { return; } config_settings->setStringValue("system_name", config_name); config_settings->setStringValue("config_file", config_file.canonicalFilePath()); config_settings->setStringValue("config_dir", config_file.canonicalPath()); if (displayName != config_name) { config_settings->setStringValue("display_name", displayName); } else { config_settings->remove("display_name"); } config_settings->setStringValue("notes", notes); if(lastUsedTimestamp.isValid()) { config_settings->setStringValue("timestamp", lastUsedTimestamp.toString(Qt::ISODate)); } config_settings->setStringValue("icon", icon); generateSearchTerms(); } void VMManagerSystem::generateSearchTerms() { searchTerms.clear(); for (const auto &config_key : config_hash.keys()) { // searchTerms.append(config_hash[config_key].values()); // brute force temporarily don't add paths for(const auto &value: config_hash[config_key].values()) { if(!value.startsWith("/")) { searchTerms.append(value); } } } searchTerms.append(display_table.values()); searchTerms.append(displayName); searchTerms.append(config_name); QRegularExpression whitespaceRegex("\\s+"); searchTerms.append(notes.split(whitespaceRegex)); } void VMManagerSystem::updateTimestamp() { lastUsedTimestamp = QDateTime::currentDateTimeUtc(); saveSettings(); } QString VMManagerSystem::getAll(const QString& category) const { auto value = config_hash[category].keys().join(", "); return value; } QHash> VMManagerSystem::getConfigHash() const { return config_hash; } void VMManagerSystem::setDisplayName(const QString &newDisplayName) { // If blank, reset to the default if (newDisplayName.isEmpty()) { displayName = config_name; } else { displayName = newDisplayName; } saveSettings(); } void VMManagerSystem::setNotes(const QString &newNotes) { notes = newNotes; saveSettings(); } bool VMManagerSystem::isValid() const { return config_file.exists() && config_file.isFile() && config_file.size() != 0; } bool VMManagerSystem::isProcessRunning() const { return process->processId() != 0; } qint64 VMManagerSystem::processId() const { return process->processId(); } QHash VMManagerSystem::getCategory(const QString &category) const { return config_hash[category]; } void VMManagerSystem::find86BoxBinary() { // We'll use our own self to launch the VMs main_binary = QFileInfo(QCoreApplication::applicationFilePath()); } bool VMManagerSystem::has86BoxBinary() { return main_binary.exists(); } void VMManagerSystem::launchMainProcess() { if(!has86BoxBinary()) { qWarning("No binary found! returning"); return; } // start the server first to get the socket name if (!serverIsRunning) { if(!startServer()) { // FIXME: Better error handling qInfo("Failed to start VM Manager server"); return; } } setProcessEnvVars(); QString program = main_binary.filePath(); QStringList args; args << "-P" << config_dir; args << "--vmname" << displayName; process->setProgram(program); process->setArguments(args); qDebug() << Q_FUNC_INFO << " Full Command:" << process->program() << " " << process->arguments(); process->start(); updateTimestamp(); } void VMManagerSystem::startButtonPressed() { launchMainProcess(); } void VMManagerSystem::launchSettings() { if(!has86BoxBinary()) { qWarning("No binary found! returning"); return; } // If the system is already running, instruct it to show settings if (process->processId() != 0) { socket_server->serverSendMessage(VMManagerProtocol::ManagerMessage::ShowSettings); return; } // Otherwise, launch the system with the settings parameter setProcessEnvVars(); QString program = main_binary.filePath(); QStringList open_command_args; QStringList args; args << "-P" << config_dir << "-S"; process->setProgram(program); process->setArguments(args); qDebug() << Q_FUNC_INFO << " Full Command:" << process->program() << " " << process->arguments(); process->start(); } void VMManagerSystem::setupPaths() { // application_temp_directory.setPath(QStandardPaths::writableLocation(QStandardPaths::TempLocation)); // standard_temp_directory.setPath(QStandardPaths::writableLocation(QStandardPaths::TempLocation)); // QString temp_subdir = QApplication::applicationName(); // if (!application_temp_directory.exists(temp_subdir)) { // // FIXME: error checking // application_temp_directory.mkdir(temp_subdir); // } // // QT always replaces `/` with native separators, so it is safe to use here for all platforms // application_temp_directory.setPath(application_temp_directory.path() + "/" + temp_subdir); // app_data_directory.setPath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); // // TODO: come back here and update with the new plat_get_global_* // if (!app_data_directory.exists()) { // // FIXME: Error checking // app_data_directory.mkpath(app_data_directory.path()); // } screenshot_directory.setPath(config_dir + "/" + "screenshots"); } void VMManagerSystem::setupVars() { unique_name = QCryptographicHash::hash(config_file.path().toUtf8().constData(), QCryptographicHash::Algorithm::Sha256).toHex().right(9); // unique_name = "aaaaaa"; // Set up the display vars // This will likely get moved out to its own class // This will likely get moved out to its own class auto machine_config = getCategory("Machine"); auto video_config = getCategory("Video"); auto disk_config = getCategory("Hard disks"); auto audio_config = getCategory("Sound"); auto network_config = getCategory("Network"); auto input_config = getCategory("Input devices"); auto floppy_cdrom_config = getCategory("Floppy and CD-ROM drives"); auto scsi_config = getCategory("Storage controllers"); auto ports_config = getCategory("Ports (COM & LPT)"); // auto general_config = getCategory("General"); // auto config_uuid = QString("Not set"); // if(!general_config["uuid"].isEmpty()) { // config_uuid = general_config["uuid"]; // qDebug() << "btw config dir:" << config_dir; // } // qDebug() << "Generated UUID:" << uuid; // qDebug() << "Config file UUID:" << config_uuid; auto machine_name = QString(); int i = 0; int ram_granularity = 0; // Machine for (int ci = 0; ci < machine_count(); ++ci) { if (machine_available(ci)) { if (machines[ci].internal_name == machine_config["machine"]) { machine_name = machines[ci].name; ram_granularity = machines[ci].ram.step; } } } display_table[Display::Name::Machine] = machine_name; // CPU: Combine name with speed auto cpu_name = QString(); while (cpu_families[i].package != 0) { if (cpu_families[i].internal_name == machine_config["cpu_family"]) { cpu_name = QString("%1 %2").arg(cpu_families[i].manufacturer, cpu_families[i].name); } i++; } int speed_display = machine_config["cpu_speed"].toInt() / 1000000; cpu_name.append(QString::number(speed_display).prepend(" / ")); cpu_name.append(QCoreApplication::translate("", "MHz").prepend(' ')); display_table[Display::Name::CPU] = cpu_name; // Memory int divisor = (ram_granularity < 1024) ? 1 : 1024; QString display_unit = (divisor == 1) ? "KB" : "MB"; auto mem_display = QString::number(machine_config["mem_size"].toInt() / divisor); mem_display.append(QCoreApplication::translate("", display_unit.toUtf8().constData()).prepend(' ')); display_table[Display::Name::Memory] = mem_display; // Video card int video_int = video_get_video_from_internal_name(video_config["gfxcard"].toUtf8().data()); const device_t* video_dev = video_card_getdevice(video_int); display_table[Display::Name::Video] = DeviceConfig::DeviceName(video_dev, video_get_internal_name(video_int), 1); if (!video_config["voodoo"].isEmpty()) { // FIXME: Come back to this later to add more for secondary video // display_table[Display::Name::Video].append(" (with voodoo)"); display_table[Display::Name::Voodoo] = "Voodoo enabled"; } // Drives // First the number of disks QMap disks; for(const auto& key: disk_config.keys()) { // Assuming the format hdd_NN_* QStringList pieces = key.split('_'); QString disk = QString("%1_%2").arg(pieces.at(0), pieces.at(1)); if(!disk.isEmpty()) { disks[disk] = 1; } } // Next, the types QHash bus_types; for (const auto& key: disks.keys()) { auto disk_parameter_key = QString("%1_parameters").arg(key); QStringList pieces = disk_config[disk_parameter_key].split(","); QString bus_type = pieces.value(pieces.length() - 1).trimmed(); bus_types[bus_type] = 1; } QString disks_display = tr("%n disk(s)", "", disks.count()); if (disks.count()) { disks_display.append(" / ").append(bus_types.keys().join(", ").toUpper()); } // display_table[Display::Name::Disks] = disks_display; // Drives QString new_disk_display; for (const auto& key: disks.keys()) { auto disk_parameter_key = QString("%1_parameters").arg(key); // Converting a string to an int back to a string to remove the zero (e.g. 01 to 1) auto disk_number = QString::number(key.split("_").last().toInt()); QStringList pieces = disk_config[disk_parameter_key].split(","); QString sectors = pieces.value(0).trimmed(); QString heads = pieces.value(1).trimmed(); QString cylinders = pieces.value(2).trimmed(); QString bus_type = pieces.value(pieces.length() - 1).trimmed(); // Add separator for each subsequent value, skipping the first if(!new_disk_display.isEmpty()) { new_disk_display.append(QString("%1").arg(VMManagerDetailSection::sectionSeparator)); } int diskSizeRaw = (cylinders.toInt() * heads.toInt() * sectors.toInt()) >> 11; QString diskSizeFinal; QString unit = "MiB"; if(diskSizeRaw > 1000) { unit = "GiB"; diskSizeFinal = QString::number(diskSizeRaw * 1.0 / 1000, 'f', 1); } else { diskSizeFinal = QString::number(diskSizeRaw); } // Only prefix each disk when there are multiple disks QString diskNumberDisplay = disks.count() > 1 ? QString("Disk %1: ").arg(disk_number) : ""; new_disk_display.append(QString("%1%2 %3 (%4)").arg(diskNumberDisplay, diskSizeFinal, unit, bus_type.toUpper())); } if(new_disk_display.isEmpty()) { new_disk_display = "No disks"; } display_table[Display::Name::Disks] = new_disk_display; // Floppy & CD-ROM QStringList floppyDevices; QStringList cdromDevices; static auto floppy_match = QRegularExpression("fdd_\\d\\d_type", QRegularExpression::CaseInsensitiveOption); static auto cdrom_match = QRegularExpression("cdrom_\\d\\d_type", QRegularExpression::CaseInsensitiveOption); for(const auto& key: floppy_cdrom_config.keys()) { if(key.contains(floppy_match)) { // auto device_number = key.split("_").at(1); auto floppy_internal_name = QString(floppy_cdrom_config[key]); // Not interested in the nones if(floppy_internal_name == "none") { continue; } auto floppy_type = fdd_get_from_internal_name(floppy_internal_name.toUtf8().data()); if(auto fddName = QString(fdd_getname(floppy_type)); !fddName.isEmpty()) { floppyDevices.append(fddName); } } if(key.contains(cdrom_match)) { auto device_number = key.split("_").at(1); auto cdrom_internal_name = QString(floppy_cdrom_config[key]); auto cdrom_type = cdrom_get_from_internal_name(cdrom_internal_name.toUtf8().data()); auto cdrom_speed_key = QString("cdrom_%1_speed").arg(device_number); auto cdrom_parameters_key = QString("cdrom_%1_parameters").arg(device_number); auto cdrom_speed = QString(floppy_cdrom_config[cdrom_speed_key]); auto cdrom_parameters = QString(floppy_cdrom_config[cdrom_parameters_key]); auto cdrom_bus = cdrom_parameters.split(",").at(1).trimmed().toUpper(); if(cdrom_type != -1) { if(!cdrom_speed.isEmpty()) { cdrom_speed = QString("%1x ").arg(cdrom_speed); } if(!cdrom_bus.isEmpty()) { cdrom_bus = QString(" (%1)").arg(cdrom_bus); } cdromDevices.append(QString("%1%2 %3 %4%5").arg(cdrom_speed, cdrom_drive_types[cdrom_type].vendor, cdrom_drive_types[cdrom_type].model, cdrom_drive_types[cdrom_type].revision, cdrom_bus)); } } } display_table[Display::Name::Floppy] = floppyDevices.join(VMManagerDetailSection::sectionSeparator); display_table[Display::Name::CD] = cdromDevices.join(VMManagerDetailSection::sectionSeparator); // SCSI controllers QStringList scsiControllers; static auto scsi_match = QRegularExpression("scsicard_\\d", QRegularExpression::CaseInsensitiveOption); for(const auto& key: scsi_config.keys()) { if(key.contains(scsi_match)) { auto device_number = key.split("_").at(1); auto scsi_internal_name = QString(scsi_config[key]); auto scsi_id = scsi_card_get_from_internal_name(scsi_internal_name.toUtf8().data()); auto scsi_device = scsi_card_getdevice(scsi_id); auto scsi_name = QString(scsi_device->name); if(!scsi_name.isEmpty()) { scsiControllers.append(scsi_name); } } } display_table[Display::Name::SCSIController] = scsiControllers.join(VMManagerDetailSection::sectionSeparator); // Audio int sound_int = sound_card_get_from_internal_name(audio_config["sndcard"].toUtf8().data()); const device_t* audio_dev = sound_card_getdevice(sound_int); display_table[Display::Name::Audio] = DeviceConfig::DeviceName(audio_dev, sound_card_get_internal_name(sound_int), 1); // MIDI QString midiOutDev; if(auto midi_out_device = QString(audio_config["midi_device"]); !midi_out_device.isEmpty()) { auto midi_device_int = midi_out_device_get_from_internal_name(midi_out_device.toUtf8().data()); auto midi_out = midi_out_device_getdevice(midi_device_int); if(auto midiDevName = QString(midi_out->name); !midiDevName.isEmpty()) { midiOutDev = midiDevName; } } display_table[Display::Name::MidiOut] = midiOutDev; // midi_device = mt32 (output) // mpu401_standalone = 1 // midi_in_device (input) // Network QString nicList; static auto nic_match = QRegularExpression("net_\\d\\d_card", QRegularExpression::CaseInsensitiveOption); for(const auto& key: network_config.keys()) { if(key.contains(nic_match)) { auto device_number = key.split("_").at(1); auto nic_internal_name = QString(network_config[key]); auto nic_id = network_card_get_from_internal_name(nic_internal_name.toUtf8().data()); auto nic = network_card_getdevice(nic_id); auto nic_name = QString(nic->name); // Add separator for each subsequent value, skipping the first if(!nicList.isEmpty()) { nicList.append(QString("%1").arg(VMManagerDetailSection::sectionSeparator)); } auto net_type_key = QString("net_%1_net_type").arg(device_number); auto net_type = network_config[net_type_key]; if (!net_type.isEmpty()) { nicList.append(nic_name + " (" + net_type + ")"); } else { nicList.append(nic_name); } } } if(nicList.isEmpty()) { nicList = "None"; } display_table[Display::Name::NIC] = nicList; // Input (Mouse) auto mouse_internal_name = input_config["mouse_type"]; auto mouse_dev = mouse_get_from_internal_name(mouse_internal_name.toUtf8().data()); auto mouse_dev_name = mouse_get_name(mouse_dev); display_table[Display::Name::Mouse] = mouse_dev_name; // Input (joystick) QString joystickDevice; if(auto joystick_internal = QString(input_config["joystick_type"]); !joystick_internal.isEmpty()) { auto joystick_dev = joystick_get_from_internal_name(joystick_internal.toUtf8().data()); if (auto joystickName = QString(joystick_get_name(joystick_dev)); !joystickName.isEmpty()) { joystickDevice = joystickName; } } display_table[Display::Name::Joystick] = joystickDevice; // # Ports // Serial // By default serial 1 and 2 are enabled unless otherwise specified static auto serial_match = QRegularExpression("serial\\d_enabled", QRegularExpression::CaseInsensitiveOption); QList serial_enabled = {true, true, false, false}; // Parallel // By default lpt 1 is enabled unless otherwise specified static auto lpt_match = QRegularExpression("lpt\\d_enabled", QRegularExpression::CaseInsensitiveOption); QList lpt_enabled = {true, false, false, false}; for (const auto &key: ports_config.keys()) { if (key.contains(serial_match)) { if (auto serial_dev = key.split("_").at(0); !serial_dev.isEmpty()) { auto serial_num = serial_dev.at(serial_dev.size() - 1); // qDebug() << "serial is set" << key << ":" << ports_config[key]; if(serial_num.isDigit() && serial_num.digitValue() >= 1 && serial_num.digitValue() <= 4) { // Already verified that it is a digit with isDigit() serial_enabled[serial_num.digitValue() - 1] = ports_config[key].toInt() == 1; } } } if (key.contains(lpt_match)) { if (auto lpt_dev = key.split("_").at(0); !lpt_dev.isEmpty()) { auto lpt_num = lpt_dev.at(lpt_dev.size() - 1); // qDebug() << "lpt is set" << key << ":" << ports_config[key]; if (lpt_num.isDigit() && lpt_num.digitValue() >= 1 && lpt_num.digitValue() <= 4) { lpt_enabled[lpt_num.digitValue() - 1] = ports_config[key].toInt() == 1; } } } } // qDebug() << "ports final" << serial_enabled << lpt_enabled; QStringList serialFinal; QStringList lptFinal; int portIndex = 0; for(const auto &serialNum: serial_enabled) { if (serial_enabled[portIndex]) { serialFinal.append(QString("COM%1").arg(portIndex + 1)); } ++portIndex; } portIndex = 0; for (const auto &lptNum: lpt_enabled) { if (lpt_enabled[portIndex]) { lptFinal.append(QString("LPT%1").arg(portIndex + 1)); } ++portIndex; } display_table[Display::Name::Serial] = serialFinal.empty() ? tr("None") : serialFinal.join(", "); display_table[Display::Name::Parallel] = lptFinal.empty() ? tr("None") : lptFinal.join(", "); } bool VMManagerSystem::startServer() { if (socket_server->startServer()) { serverIsRunning = true; connect(socket_server, &VMManagerServerSocket::dataReceived, this, &VMManagerSystem::dataReceived); connect(socket_server, &VMManagerServerSocket::windowStatusChanged, this, &VMManagerSystem::windowStatusChangeReceived); connect(socket_server, &VMManagerServerSocket::runningStatusChanged, this, &VMManagerSystem::runningStatusChangeReceived); return true; } else { return false; } } void VMManagerSystem::setProcessEnvVars() { QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); QString env_var_name = (socket_server_type == VMManagerServerSocket::ServerType::Standard) ? "VMM_86BOX_SOCKET" : "86BOX_MANAGER_SOCKET"; env.insert(env_var_name, socket_server->getSocketPath()); process->setProcessEnvironment(env); } void VMManagerSystem::restartButtonPressed() { socket_server->serverSendMessage(VMManagerProtocol::ManagerMessage::ResetVM); } void VMManagerSystem::pauseButtonPressed() { socket_server->serverSendMessage(VMManagerProtocol::ManagerMessage::Pause); } void VMManagerSystem::dataReceived() { qInfo() << Q_FUNC_INFO << "Note: Respond to data received events here."; } void VMManagerSystem::windowStatusChangeReceived(int status) { window_obscured = status; emit windowStatusChanged(); processStatusChanged(); } QString VMManagerSystem::getDisplayValue(Display::Name key) { return (display_table.contains(key)) ? display_table[key] : ""; } void VMManagerSystem::shutdownRequestButtonPressed() { socket_server->serverSendMessage(VMManagerProtocol::ManagerMessage::RequestShutdown); } void VMManagerSystem::shutdownForceButtonPressed() { socket_server->serverSendMessage(VMManagerProtocol::ManagerMessage::ForceShutdown); } void VMManagerSystem::processStatusChanged() { // set to running if the process is running and the state is stopped if (process->state() == QProcess::ProcessState::Running) { if (process_status == VMManagerSystem::ProcessStatus::Stopped) { process_status = VMManagerSystem::ProcessStatus::Running; } } else if (process->state() == QProcess::ProcessState::NotRunning) { process_status = VMManagerSystem::ProcessStatus::Stopped; } emit itemDataChanged(); emit clientProcessStatusChanged(); } void VMManagerSystem::statusRefresh() { processStatusChanged(); } QString VMManagerSystem::processStatusToString(VMManagerSystem::ProcessStatus status) { // QMetaEnum qme = QMetaEnum::fromType(); // return qme.valueToKey(static_cast(status)); switch (status) { case VMManagerSystem::ProcessStatus::Stopped: return tr("Powered Off"); case VMManagerSystem::ProcessStatus::Running: return tr("Running"); case VMManagerSystem::ProcessStatus::Paused: return tr("Paused"); case VMManagerSystem::ProcessStatus::PausedWaiting: case VMManagerSystem::ProcessStatus::RunningWaiting: return tr("Paused (Waiting)"); default: return tr("Unknown Status"); } } QString VMManagerSystem::getProcessStatusString() const { return processStatusToString(process_status); } VMManagerSystem::ProcessStatus VMManagerSystem::getProcessStatus() const { return process_status; } // Maps VMManagerProtocol::RunningState to VMManagerSystem::ProcessStatus void VMManagerSystem::runningStatusChangeReceived(VMManagerProtocol::RunningState state) { if(state == VMManagerProtocol::RunningState::Running) { process_status = VMManagerSystem::ProcessStatus::Running; } else if(state == VMManagerProtocol::RunningState::Paused) { process_status = VMManagerSystem::ProcessStatus::Paused; } else if(state == VMManagerProtocol::RunningState::RunningWaiting) { process_status = VMManagerSystem::ProcessStatus::RunningWaiting; } else if(state == VMManagerProtocol::RunningState::PausedWaiting) { process_status = VMManagerSystem::ProcessStatus::PausedWaiting; } else { process_status = VMManagerSystem::ProcessStatus::Unknown; } processStatusChanged(); } void VMManagerSystem::reloadConfig() { loadSettings(); setupVars(); } QDateTime VMManagerSystem::timestamp() { return lastUsedTimestamp; } void VMManagerSystem::setIcon(const QString &newIcon) { icon = newIcon; saveSettings(); emit itemDataChanged(); }