Merge pull request #5858 from lemondrops/manager-misc

Manager fixes and improvements
This commit is contained in:
Miran Grča
2025-08-01 14:06:29 +02:00
committed by GitHub
51 changed files with 428 additions and 205 deletions

View File

@@ -88,6 +88,8 @@ add_library(ui STATIC
qt_openglrenderer.cpp
qt_openglrenderer.hpp
qt_glsl_parser.cpp
qt_about.cpp
qt_about.hpp
qt_settings.cpp
qt_settings.hpp

View File

@@ -30,7 +30,7 @@ msgstr ""
msgid "Re&sume"
msgstr ""
msgid "E&xit..."
msgid "E&xit"
msgstr ""
msgid "&View"

View File

@@ -30,8 +30,8 @@ msgstr "&Pausa"
msgid "Re&sume"
msgstr ""
msgid "E&xit..."
msgstr "&Sortir ..."
msgid "E&xit"
msgstr "&Sortir"
msgid "&View"
msgstr "&Vista"

View File

@@ -30,7 +30,7 @@ msgstr "P&ozastavit"
msgid "Re&sume"
msgstr ""
msgid "E&xit..."
msgid "E&xit"
msgstr "&Ukončit"
msgid "&View"

View File

@@ -30,8 +30,8 @@ msgstr "&Pause"
msgid "Re&sume"
msgstr ""
msgid "E&xit..."
msgstr "Be&enden..."
msgid "E&xit"
msgstr "Be&enden"
msgid "&View"
msgstr "&Ansicht"

View File

@@ -30,8 +30,8 @@ msgstr "&Pausa"
msgid "Re&sume"
msgstr ""
msgid "E&xit..."
msgstr "&Salir..."
msgid "E&xit"
msgstr "&Salir"
msgid "&View"
msgstr "&Vista"

View File

@@ -30,8 +30,8 @@ msgstr "&Tauko"
msgid "Re&sume"
msgstr ""
msgid "E&xit..."
msgstr "&Poistu..."
msgid "E&xit"
msgstr "&Poistu"
msgid "&View"
msgstr "&Näytä"

View File

@@ -30,8 +30,8 @@ msgstr "&Pause"
msgid "Re&sume"
msgstr ""
msgid "E&xit..."
msgstr "&Quitter..."
msgid "E&xit"
msgstr "&Quitter"
msgid "&View"
msgstr "&Vue"

View File

@@ -30,8 +30,8 @@ msgstr "&Pauza"
msgid "Re&sume"
msgstr ""
msgid "E&xit..."
msgstr "Iz&laz..."
msgid "E&xit"
msgstr "Iz&laz"
msgid "&View"
msgstr "&Pogled"

View File

@@ -30,8 +30,8 @@ msgstr "&Szüneteltetés"
msgid "Re&sume"
msgstr ""
msgid "E&xit..."
msgstr "&Kilépés..."
msgid "E&xit"
msgstr "&Kilépés"
msgid "&View"
msgstr "&Nézet"

View File

@@ -30,8 +30,8 @@ msgstr "&Pausa"
msgid "Re&sume"
msgstr ""
msgid "E&xit..."
msgstr "E&sci..."
msgid "E&xit"
msgstr "E&sci"
msgid "&View"
msgstr "&Visualizza"

View File

@@ -30,8 +30,8 @@ msgstr "一時停止(&P)"
msgid "Re&sume"
msgstr ""
msgid "E&xit..."
msgstr "終了(&X)..."
msgid "E&xit"
msgstr "終了(&X)"
msgid "&View"
msgstr "表示(&V)"

View File

@@ -30,8 +30,8 @@ msgstr "일시정지(&P)"
msgid "Re&sume"
msgstr ""
msgid "E&xit..."
msgstr "끝내기(&X)..."
msgid "E&xit"
msgstr "끝내기(&X)"
msgid "&View"
msgstr "표시(&V)"

View File

@@ -30,8 +30,8 @@ msgstr "&Pauze"
msgid "Re&sume"
msgstr ""
msgid "E&xit..."
msgstr "&Afsluiten..."
msgid "E&xit"
msgstr "&Afsluiten"
msgid "&View"
msgstr "&Beeld"

View File

@@ -30,8 +30,8 @@ msgstr "&Pauza"
msgid "Re&sume"
msgstr ""
msgid "E&xit..."
msgstr "W&yjdź..."
msgid "E&xit"
msgstr "W&yjdź"
msgid "&View"
msgstr "&Widok"

View File

@@ -30,8 +30,8 @@ msgstr "&Pausar"
msgid "Re&sume"
msgstr ""
msgid "E&xit..."
msgstr "&Sair..."
msgid "E&xit"
msgstr "&Sair"
msgid "&View"
msgstr "&Exibir"

View File

@@ -30,8 +30,8 @@ msgstr "&Pausa"
msgid "Re&sume"
msgstr ""
msgid "E&xit..."
msgstr "&Sair..."
msgid "E&xit"
msgstr "&Sair"
msgid "&View"
msgstr "&Ver"

View File

@@ -30,8 +30,8 @@ msgstr "&Пауза"
msgid "Re&sume"
msgstr "В&озобновить"
msgid "E&xit..."
msgstr "&Выход..."
msgid "E&xit"
msgstr "&Выход"
msgid "&View"
msgstr "&Вид"

View File

@@ -30,7 +30,7 @@ msgstr "P&ozastaviť"
msgid "Re&sume"
msgstr ""
msgid "E&xit..."
msgid "E&xit"
msgstr "&Ukončiť"
msgid "&View"

View File

@@ -30,8 +30,8 @@ msgstr "&Premor"
msgid "Re&sume"
msgstr ""
msgid "E&xit..."
msgstr "Iz&hod..."
msgid "E&xit"
msgstr "Iz&hod"
msgid "&View"
msgstr "&Pogled"

View File

@@ -30,8 +30,8 @@ msgstr "&Pausa"
msgid "Re&sume"
msgstr ""
msgid "E&xit..."
msgstr "A&vsluta..."
msgid "E&xit"
msgstr "A&vsluta"
msgid "&View"
msgstr "&Visa"

View File

@@ -30,8 +30,8 @@ msgstr "&Duraklat"
msgid "Re&sume"
msgstr ""
msgid "E&xit..."
msgstr "&Çıkış yap..."
msgid "E&xit"
msgstr "&Çıkış yap"
msgid "&View"
msgstr "&Görünüm"

View File

@@ -30,8 +30,8 @@ msgstr "&Пауза"
msgid "Re&sume"
msgstr ""
msgid "E&xit..."
msgstr "&Вихід..."
msgid "E&xit"
msgstr "&Вихід"
msgid "&View"
msgstr "&Вигляд"

View File

@@ -30,8 +30,8 @@ msgstr "Tạm &dừng"
msgid "Re&sume"
msgstr ""
msgid "E&xit..."
msgstr "Th&oát..."
msgid "E&xit"
msgstr "Th&oát"
msgid "&View"
msgstr "&Xem"

View File

@@ -30,8 +30,8 @@ msgstr "暂停(&P)"
msgid "Re&sume"
msgstr ""
msgid "E&xit..."
msgstr "退出(&X)..."
msgid "E&xit"
msgstr "退出(&X)"
msgid "&View"
msgstr "查看(&V)"

View File

@@ -30,8 +30,8 @@ msgstr "暫停(&P)"
msgid "Re&sume"
msgstr ""
msgid "E&xit..."
msgstr "退出(&X)..."
msgid "E&xit"
msgstr "退出(&X)"
msgid "&View"
msgstr "檢視(&V)"

75
src/qt/qt_about.cpp Normal file
View File

@@ -0,0 +1,75 @@
/*
* 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.
*
* About dialog module.
*
*
*
* Authors: Joakim L. Gilje <jgilje@jgilje.net>
* Cacodemon345
* Teemu Korhonen
* dob205
*
* Copyright 2021 Joakim L. Gilje
* Copyright 2021-2022 Cacodemon345
* Copyright 2021-2022 Teemu Korhonen
* Copyright 2022 dob205
*/
#include "qt_about.hpp"
extern "C" {
#include <86box/86box.h>
#include <86box/version.h>
}
#include <QMessageBox>
#include <QIcon>
#include <QPushButton>
#include <QUrl>
#include <QDesktopServices>
About::About(QWidget *parent)
{
setTextFormat(Qt::RichText);
QString versioninfo;
#ifdef EMU_GIT_HASH
versioninfo = QString(" [%1]").arg(EMU_GIT_HASH);
#endif
#ifdef USE_DYNAREC
# ifdef USE_NEW_DYNAREC
# define DYNAREC_STR "new dynarec"
# else
# define DYNAREC_STR "old dynarec"
# endif
#else
# define DYNAREC_STR "no dynarec"
#endif
versioninfo.append(QString(" [%1, %2]").arg(QSysInfo::buildCpuArchitecture(), tr(DYNAREC_STR)));
setText(QString("<b>%3%1%2</b>").arg(EMU_VERSION_FULL, versioninfo, tr("86Box v")));
setInformativeText(tr("An emulator of old computers\n\nAuthors: Miran Grča (OBattler), RichardG867, Jasmine Iwanek, TC1995, coldbrewed, Teemu Korhonen (Manaatti), Joakim L. Gilje, Adrien Moulin (elyosh), Daniel Balsom (gloriouscow), Cacodemon345, Fred N. van Kempen (waltje), Tiseno100, reenigne, and others.\n\nWith previous core contributions from Sarah Walker, leilei, JohnElliott, greatpsycho, and others.\n\nReleased under the GNU General Public License version 2 or later. See LICENSE for more information."));
setWindowTitle(tr("About 86Box"));
const auto closeButton = addButton("OK", QMessageBox::ButtonRole::AcceptRole);
setEscapeButton(closeButton);
const auto webSiteButton = addButton(EMU_SITE, QMessageBox::ButtonRole::HelpRole);
webSiteButton->connect(webSiteButton, &QPushButton::released, []() {
QDesktopServices::openUrl(QUrl("https://" EMU_SITE));
});
#ifdef RELEASE_BUILD
setIconPixmap(QIcon(":/settings/qt/icons/86Box-green.ico").pixmap(32, 32));
#elif defined ALPHA_BUILD
setIconPixmap(QIcon(":/settings/qt/icons/86Box-red.ico").pixmap(32, 32));
#elif defined BETA_BUILD
setIconPixmap(QIcon(":/settings/qt/icons/86Box-yellow.ico").pixmap(32, 32));
#else
setIconPixmap(QIcon(":/settings/qt/icons/86Box-gray.ico").pixmap(32, 32));
#endif
setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint);
}
About::~About()
= default;

13
src/qt/qt_about.hpp Normal file
View File

@@ -0,0 +1,13 @@
#ifndef QT_ABOUT_HPP
#define QT_ABOUT_HPP
#include <QMessageBox>
class About final : public QMessageBox {
Q_OBJECT
public:
explicit About(QWidget *parent = nullptr);
~About() override;
};
#endif // QT_ABOUT_HPP

View File

@@ -561,13 +561,11 @@ main(int argc, char *argv[])
}
#endif
qt_set_sequence_auto_mnemonic(false);
Q_INIT_RESOURCE(qt_resources);
Q_INIT_RESOURCE(qt_translations);
QSurfaceFormat fmt = QSurfaceFormat::defaultFormat();
fmt.setSwapInterval(0);
QSurfaceFormat::setDefaultFormat(fmt);
app.setStyle(new StyleOverride());
#ifdef __APPLE__
CocoaEventFilter cocoafilter;
@@ -586,6 +584,10 @@ main(int argc, char *argv[])
return 0;
}
if (!vmm_enabled)
qt_set_sequence_auto_mnemonic(false);
app.setStyle(new StyleOverride());
bool startMaximized = window_remember && monitor_settings[0].mon_window_maximized;
fprintf(stderr, "Qt: version %s, platform \"%s\"\n", qVersion(), QApplication::platformName().toUtf8().data());
ProgSettings::loadTranslators(&app);
@@ -619,6 +621,19 @@ main(int argc, char *argv[])
return 6;
}
if (vmm_enabled) {
// VMManagerMain vmm;
// // Hackish until there is a proper solution
// QApplication::setApplicationName("86Box VM Manager");
// QApplication::setApplicationDisplayName("86Box VM Manager");
// vmm.show();
// vmm.exec();
const auto vmm_main_window = new VMManagerMainWindow();
vmm_main_window->show();
QApplication::exec();
return 0;
}
// UUID / copy / move detection
if(!util::compareUuid()) {
QMessageBox movewarnbox;
@@ -661,6 +676,11 @@ main(int argc, char *argv[])
#endif
if (settings_only) {
VMManagerClientSocket manager_socket;
if (qgetenv("VMM_86BOX_SOCKET").size()) {
manager_socket.IPCConnect(qgetenv("VMM_86BOX_SOCKET"));
manager_socket.clientRunningStateChanged(VMManagerProtocol::RunningState::PausedWaiting);
}
Settings settings;
if (settings.exec() == QDialog::Accepted) {
settings.save();
@@ -681,19 +701,6 @@ main(int argc, char *argv[])
return 0;
}
if (vmm_enabled) {
// VMManagerMain vmm;
// // Hackish until there is a proper solution
// QApplication::setApplicationName("86Box VM Manager");
// QApplication::setApplicationDisplayName("86Box VM Manager");
// vmm.show();
// vmm.exec();
const auto vmm_main_window = new VMManagerMainWindow();
vmm_main_window->show();
QApplication::exec();
return 0;
}
#ifdef DISCORD
discord_load();
#endif

View File

@@ -97,6 +97,7 @@ extern bool cpu_thread_running;
#include <unordered_map>
#include "qt_settings.hpp"
#include "qt_about.hpp"
#include "qt_machinestatus.hpp"
#include "qt_mediamenu.hpp"
#include "qt_util.hpp"
@@ -1908,42 +1909,8 @@ MainWindow::on_actionAbout_Qt_triggered()
void
MainWindow::on_actionAbout_86Box_triggered()
{
QMessageBox msgBox;
msgBox.setTextFormat(Qt::RichText);
QString versioninfo;
#ifdef EMU_GIT_HASH
versioninfo = QString(" [%1]").arg(EMU_GIT_HASH);
#endif
#ifdef USE_DYNAREC
# ifdef USE_NEW_DYNAREC
# define DYNAREC_STR "new dynarec"
# else
# define DYNAREC_STR "old dynarec"
# endif
#else
# define DYNAREC_STR "no dynarec"
#endif
versioninfo.append(QString(" [%1, %2]").arg(QSysInfo::buildCpuArchitecture(), tr(DYNAREC_STR)));
msgBox.setText(QString("<b>%3%1%2</b>").arg(EMU_VERSION_FULL, versioninfo, tr("86Box v")));
msgBox.setInformativeText(tr("An emulator of old computers\n\nAuthors: Miran Grča (OBattler), RichardG867, Jasmine Iwanek, TC1995, coldbrewed, Teemu Korhonen (Manaatti), Joakim L. Gilje, Adrien Moulin (elyosh), Daniel Balsom (gloriouscow), Cacodemon345, Fred N. van Kempen (waltje), Tiseno100, reenigne, and others.\n\nWith previous core contributions from Sarah Walker, leilei, JohnElliott, greatpsycho, and others.\n\nReleased under the GNU General Public License version 2 or later. See LICENSE for more information."));
msgBox.setWindowTitle(tr("About 86Box"));
const auto closeButton = msgBox.addButton("OK", QMessageBox::ButtonRole::AcceptRole);
msgBox.setEscapeButton(closeButton);
const auto webSiteButton = msgBox.addButton(EMU_SITE, QMessageBox::ButtonRole::HelpRole);
webSiteButton->connect(webSiteButton, &QPushButton::released, []() {
QDesktopServices::openUrl(QUrl("https://" EMU_SITE));
});
#ifdef RELEASE_BUILD
msgBox.setIconPixmap(QIcon(":/settings/qt/icons/86Box-green.ico").pixmap(32, 32));
#elif defined ALPHA_BUILD
msgBox.setIconPixmap(QIcon(":/settings/qt/icons/86Box-red.ico").pixmap(32, 32));
#elif defined BETA_BUILD
msgBox.setIconPixmap(QIcon(":/settings/qt/icons/86Box-yellow.ico").pixmap(32, 32));
#else
msgBox.setIconPixmap(QIcon(":/settings/qt/icons/86Box-gray.ico").pixmap(32, 32));
#endif
msgBox.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint);
msgBox.exec();
const auto msgBox = new About(this);
msgBox->exec();
}
void

View File

@@ -354,7 +354,7 @@
</action>
<action name="actionExit">
<property name="text">
<string>Exit</string>
<string>E&amp;xit</string>
</property>
<property name="menuRole">
<enum>QAction::QuitRole</enum>

View File

@@ -22,6 +22,10 @@
#include <QIcon>
#include <QStyleOption>
extern "C" {
#include <86box/86box.h>
}
#ifdef Q_OS_WINDOWS
#include <dwmapi.h>
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
@@ -37,7 +41,7 @@ StyleOverride::styleHint(
QStyleHintReturn *returnData) const
{
/* Disable using menu with alt key */
if (hint == QStyle::SH_MenuBar_AltKeyNavigation)
if (!vmm_enabled && (hint == QStyle::SH_MenuBar_AltKeyNavigation))
return 0;
return QProxyStyle::styleHint(hint, option, widget, returnData);

View File

@@ -30,8 +30,8 @@ UpdateCheckDialog::
UpdateCheckDialog(const UpdateCheck::UpdateChannel channel, QWidget *parent) : QDialog(parent), ui(new Ui::UpdateCheckDialog), updateCheck(new UpdateCheck(channel))
{
ui->setupUi(this);
setWindowTitle(tr("Update check"));
ui->statusLabel->setHidden(true);
this->setFixedSize(400, 130);
updateChannel = channel;
currentVersion = UpdateCheck::getCurrentVersion(updateChannel);
connect(updateCheck, &UpdateCheck::updateCheckError, [=](const QString &errorMsg) {
@@ -67,7 +67,7 @@ UpdateCheckDialog::downloadComplete(const UpdateCheck::UpdateResult &result)
return;
}
const auto updateDetails = new UpdateDetails(result);
const auto updateDetails = new UpdateDetails(result, this);
connect(updateDetails, &QDialog::accepted, [this] {
accept();
});

View File

@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>350</width>
<height>134</height>
<width>400</width>
<height>130</height>
</rect>
</property>
<property name="sizePolicy">
@@ -17,7 +17,7 @@
</sizepolicy>
</property>
<property name="windowTitle">
<string>Dialog</string>
<string>Update check</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>

View File

@@ -23,10 +23,9 @@
UpdateDetails::
UpdateDetails(const UpdateCheck::UpdateResult &updateResult, QWidget *parent) : ui(new Ui::UpdateDetails)
UpdateDetails(const UpdateCheck::UpdateResult &updateResult, QWidget *parent) : QDialog(parent), ui(new Ui::UpdateDetails)
{
ui->setupUi(this);
setWindowTitle("86Box Update");
ui->updateTitle->setText("<b>An update to 86Box is available!</b>");
QString currentVersionText;
QString releaseType = updateResult.channel == UpdateCheck::UpdateChannel::Stable ? tr("version") : tr("build");

View File

@@ -17,7 +17,7 @@
</size>
</property>
<property name="windowTitle">
<string>Dialog</string>
<string>86Box Update</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>

View File

@@ -117,11 +117,16 @@ DlgFilter(QStringList extensions, bool last)
QString currentUuid()
{
auto configPath = QFileInfo(cfg_path).dir().canonicalPath();
if(!configPath.endsWith("/")) {
configPath.append("/");
return generateUuid(QString(cfg_path));
}
QString generateUuid(const QString &path)
{
auto dirPath = QFileInfo(path).dir().canonicalPath();
if(!dirPath.endsWith("/")) {
dirPath.append("/");
}
return QUuid::createUuidV5(QUuid{}, configPath).toString(QUuid::WithoutBraces);
return QUuid::createUuidV5(QUuid{}, dirPath).toString(QUuid::WithoutBraces);
}
bool compareUuid()

View File

@@ -18,6 +18,7 @@ QScreen *screenOfWidget(QWidget *widget);
void setWin11RoundedCorners(WId hwnd, bool enable);
#endif
QString currentUuid();
QString generateUuid(const QString &path);
void storeCurrentUuid();
bool compareUuid();
void generateNewMacAdresses();

View File

@@ -62,11 +62,14 @@ VMManagerAddMachine(QWidget *parent) : QWizard(parent)
setOption(HaveHelpButton, false);
// setPixmap(LogoPixmap, QPixmap(":/settings/qt/icons/86Box-gray.ico"));
#if 0
connect(this, &QWizard::helpRequested, this, &VMManagerAddMachine::showHelp);
#endif
setWindowTitle(tr("Add new system wizard"));
}
#if 0
void
VMManagerAddMachine::showHelp()
{
@@ -92,6 +95,7 @@ VMManagerAddMachine::showHelp()
QMessageBox::information(this, tr("Add new system wizard help"), message);
lastHelpMessage = message;
}
#endif
IntroPage::
IntroPage(QWidget *parent)
@@ -139,6 +143,17 @@ WithExistingConfigPage(QWidget *parent)
topLabel->setWordWrap(true);
existingConfiguration = new QPlainTextEdit();
const auto monospaceFont = new QFont();
#ifdef Q_OS_WINDOWS
monospaceFont->setFamily("Consolas");
#elif defined(Q_OS_MACOS)
monospaceFont->setFamily("Menlo");
#else
monospaceFont->setFamily("Monospace");
#endif
monospaceFont->setStyleHint(QFont::Monospace);
monospaceFont->setFixedPitch(true);
existingConfiguration->setFont(*monospaceFont);
connect(existingConfiguration, &QPlainTextEdit::textChanged, this, &WithExistingConfigPage::completeChanged);
registerField("existingConfiguration*", this, "configuration");

View File

@@ -42,8 +42,10 @@ public:
explicit VMManagerAddMachine(QWidget *parent = nullptr);
#if 0
private slots:
void showHelp();
#endif
};
class IntroPage : public QWizardPage {

View File

@@ -60,7 +60,7 @@ VMManagerDetails::VMManagerDetails(QWidget *parent) :
ui->leftColumn->layout()->addWidget(networkSection);
// ui->leftColumn->layout()->addWidget(createHorizontalLine());
inputSection = new VMManagerDetailSection(tr("Input Devices", "Header for Input section in VM Manager Details"));
inputSection = new VMManagerDetailSection(tr("Input devices", "Header for Input section in VM Manager Details"));
ui->leftColumn->layout()->addWidget(inputSection);
// ui->leftColumn->layout()->addWidget(createHorizontalLine());

View File

@@ -216,7 +216,7 @@ VMManagerDetailSection::setSections()
labelKey->setFont(smaller_font);
labelValue->setFont(smaller_font);
labelKey->setText(section.name + ":");
labelKey->setText(QCoreApplication::translate("", QString(section.name + ":").toUtf8().data()));
labelValue->setText(line);
if(!keyAdded) {
frameGridLayout->addWidget(labelKey, row, 0, Qt::AlignLeft);

View File

@@ -32,7 +32,6 @@
VMManagerMain::VMManagerMain(QWidget *parent) :
QWidget(parent), ui(new Ui::VMManagerMain), selected_sysconfig(new VMManagerSystem) {
ui->setupUi(this);
this->setWindowTitle("86Box VM Manager");
// Set up the main listView
ui->listView->setItemDelegate(new VMManagerListViewDelegate);
@@ -53,7 +52,7 @@ VMManagerMain::VMManagerMain(QWidget *parent) :
if (indexAt.isValid()) {
QMenu contextMenu(tr("Context Menu"), ui->listView);
QAction nameChangeAction(tr("Change display name"));
QAction nameChangeAction(tr("Change &display name..."));
contextMenu.addAction(&nameChangeAction);
// Use a lambda to call a function so indexAt can be passed
connect(&nameChangeAction, &QAction::triggered, ui->listView, [this, indexAt] {
@@ -61,7 +60,7 @@ VMManagerMain::VMManagerMain(QWidget *parent) :
});
nameChangeAction.setEnabled(!selected_sysconfig->window_obscured);
QAction openSystemFolderAction(tr("Open folder"));
QAction openSystemFolderAction(tr("&Open folder..."));
contextMenu.addAction(&openSystemFolderAction);
connect(&openSystemFolderAction, &QAction::triggered, [indexAt] {
if (const auto configDir = indexAt.data(VMManagerModel::Roles::ConfigDir).toString(); !configDir.isEmpty()) {
@@ -73,7 +72,19 @@ VMManagerMain::VMManagerMain(QWidget *parent) :
}
});
QAction setSystemIcon(tr("Set icon"));
QAction openPrinterFolderAction(tr("Open &printer tray..."));
contextMenu.addAction(&openPrinterFolderAction);
connect(&openPrinterFolderAction, &QAction::triggered, [indexAt] {
if (const auto printerDir = indexAt.data(VMManagerModel::Roles::ConfigDir).toString() + QString("/printer/"); !printerDir.isEmpty()) {
QDir dir(printerDir);
if (!dir.exists())
dir.mkpath(".");
QDesktopServices::openUrl(QUrl(QString("file:///") + dir.canonicalPath()));
}
});
QAction setSystemIcon(tr("Set &icon..."));
contextMenu.addAction(&setSystemIcon);
connect(&setSystemIcon, &QAction::triggered, [this] {
IconSelectionDialog dialog(":/systemicons/");
@@ -88,7 +99,7 @@ VMManagerMain::VMManagerMain(QWidget *parent) :
QAction killIcon(tr("&Kill"));
contextMenu.addAction(&killIcon);
connect(&killIcon, &QAction::triggered, [this, parent] {
QMessageBox msgbox(QMessageBox::Warning, tr("Warning"), tr("Killing a virtual machine can cause data loss. Only do this if 86Box.exe process gets stuck.\n\nDo you really wish to kill the virtual machine \"%1\"?").arg(selected_sysconfig->displayName), QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No, parent);
QMessageBox msgbox(QMessageBox::Warning, tr("Warning"), tr("Killing a virtual machine can cause data loss. Only do this if the 86Box process gets stuck.\n\nDo you really wish to kill the virtual machine \"%1\"?").arg(selected_sysconfig->displayName), QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No, parent);
msgbox.exec();
if (msgbox.result() == QMessageBox::Yes) {
selected_sysconfig->process->kill();
@@ -98,7 +109,7 @@ VMManagerMain::VMManagerMain(QWidget *parent) :
contextMenu.addSeparator();
QAction showRawConfigFile(tr("Show config file"));
QAction showRawConfigFile(tr("Show &config file"));
contextMenu.addAction(&showRawConfigFile);
connect(&showRawConfigFile, &QAction::triggered, [this, indexAt] {
if (const auto configFile = indexAt.data(VMManagerModel::Roles::ConfigFile).toString(); !configFile.isEmpty()) {
@@ -123,6 +134,8 @@ VMManagerMain::VMManagerMain(QWidget *parent) :
ui->listView->setCurrentIndex(first_index);
}
connect(ui->listView, &QListView::doubleClicked, this, &VMManagerMain::startButtonPressed);
// Load and apply settings
loadSettings();
@@ -182,19 +195,6 @@ VMManagerMain::settingsButtonPressed() {
return;
}
selected_sysconfig->launchSettings();
// If the process is already running, the system will be instructed to open its settings window.
// Otherwise the process will be launched and will need to be tracked here.
if (!selected_sysconfig->isProcessRunning()) {
connect(selected_sysconfig->process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
[=](const int exitCode, const QProcess::ExitStatus exitStatus){
if (exitCode != 0 || exitStatus != QProcess::NormalExit) {
qInfo().nospace().noquote() << "Abnormal program termination while launching settings: exit code " << exitCode << ", exit status " << exitStatus;
return;
}
selected_sysconfig->reloadConfig();
vm_details->updateData(selected_sysconfig);
});
}
}
void
@@ -470,6 +470,12 @@ VMManagerMain::onPreferencesUpdated()
}
}
int
VMManagerMain::getActiveMachineCount()
{
return vm_model->getActiveMachineCount();
}
#if EMU_BUILD_NUM != 0
void
VMManagerMain::backgroundUpdateCheckStart() const
@@ -520,10 +526,21 @@ VMManagerMain::showTextFileContents(const QString &title, const QString &path)
displayFile.close();
const auto textDisplayDialog = new QDialog(this);
textDisplayDialog->setFixedSize(QSize(540, 360));
textDisplayDialog->setMinimumSize(QSize(540, 360));
textDisplayDialog->setWindowTitle(QString("%1 - %2").arg(title, fi.fileName()));
const auto textEdit = new QPlainTextEdit();
const auto monospaceFont = new QFont();
#ifdef Q_OS_WINDOWS
monospaceFont->setFamily("Consolas");
#elif defined(Q_OS_MACOS)
monospaceFont->setFamily("Menlo");
#else
monospaceFont->setFamily("Monospace");
#endif
monospaceFont->setStyleHint(QFont::Monospace);
monospaceFont->setFixedPitch(true);
textEdit->setFont(*monospaceFont);
textEdit->setReadOnly(true);
textEdit->setPlainText(configFileContents);
const auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok);

View File

@@ -27,6 +27,7 @@
extern "C" {
#include <86box/86box.h> // for vmm_path
#include <86box/version.h>
}
#if EMU_BUILD_NUM != 0
@@ -77,6 +78,7 @@ public slots:
#endif
void modelDataChange();
void onPreferencesUpdated();
int getActiveMachineCount();
private:
Ui::VMManagerMain *ui;

View File

@@ -22,10 +22,13 @@
#if EMU_BUILD_NUM != 0
# include "qt_updatecheckdialog.hpp"
#endif
#include "qt_about.hpp"
#include <QLineEdit>
#include <QStringListModel>
#include <QCompleter>
#include <QCloseEvent>
#include <QDesktopServices>
VMManagerMainWindow::
VMManagerMainWindow(QWidget *parent)
@@ -39,15 +42,15 @@ VMManagerMainWindow(QWidget *parent)
// Connect signals from the VMManagerMain widget
connect(vmm, &VMManagerMain::selectionChanged, this, &VMManagerMainWindow::vmmSelectionChanged);
setWindowTitle(tr("86Box VM Manager"));
setWindowTitle(tr("%1 VM Manager").arg(EMU_NAME));
setCentralWidget(vmm);
// Set up the buttons
connect(ui->actionNew_Machine, &QAction::triggered, vmm, &VMManagerMain::newMachineWizard);
connect(ui->actionStartPause, &QAction::triggered, vmm, &VMManagerMain::startButtonPressed);
connect(ui->actionSettings, &QAction::triggered, vmm, &VMManagerMain::settingsButtonPressed);
connect(ui->actionHard_Reset, &QAction::triggered, vmm, &VMManagerMain::restartButtonPressed);
connect(ui->actionForce_Shutdown, &QAction::triggered, vmm, &VMManagerMain::shutdownForceButtonPressed);
connect(ui->actionNew_Machine, &QAction::triggered, vmm, &VMManagerMain::newMachineWizard);
// Set up menu actions
// (Disable this if the EMU_BUILD_NUM == 0)
@@ -94,6 +97,9 @@ VMManagerMainWindow(QWidget *parent)
auto *completerModel = new QStringListModel(allStrings, completer);
completer->setModel(completerModel);
searchBar->setCompleter(completer);
#ifdef Q_OS_WINDOWS
ui->toolBar->setBackgroundRole(QPalette::Light);
#endif
ui->toolBar->setVisible(false);
// END REMOVE
@@ -157,6 +163,15 @@ VMManagerMainWindow::saveSettings() const
void
VMManagerMainWindow::closeEvent(QCloseEvent *event)
{
int running = vmm->getActiveMachineCount();
if (running > 0) {
QMessageBox warningbox(QMessageBox::Icon::Warning, tr("%1 VM Manager").arg(EMU_NAME), tr("%1 machine(s) are currently active. Are you sure you want to exit the VM manager anyway?").arg(running), QMessageBox::Yes | QMessageBox::No, this);
warningbox.exec();
if (warningbox.result() == QMessageBox::No) {
event->ignore();
return;
}
}
saveSettings();
QMainWindow::closeEvent(event);
}
@@ -181,13 +196,32 @@ VMManagerMainWindow::checkForUpdatesTriggered()
# ifdef RELEASE_BUILD
updateChannel = UpdateCheck::UpdateChannel::Stable;
# endif
const auto updateCheck = new UpdateCheckDialog(updateChannel);
const auto updateCheck = new UpdateCheckDialog(updateChannel, this);
updateCheck->exec();
}
#endif
void VMManagerMainWindow::on_actionExit_triggered()
void
VMManagerMainWindow::on_actionExit_triggered()
{
this->close();
}
void
VMManagerMainWindow::on_actionAbout_Qt_triggered()
{
QApplication::aboutQt();
}
void
VMManagerMainWindow::on_actionAbout_86Box_triggered()
{
const auto msgBox = new About(this);
msgBox->exec();
}
void
VMManagerMainWindow::on_actionDocumentation_triggered()
{
QDesktopServices::openUrl(QUrl(EMU_DOCS_URL));
}

View File

@@ -51,10 +51,13 @@ private slots:
void vmmSelectionChanged(const QModelIndex &currentSelection, QProcess::ProcessState processState) const;
void preferencesTriggered();
#if EMU_BUILD_NUM != 0
static void checkForUpdatesTriggered();
void checkForUpdatesTriggered();
#endif
void on_actionExit_triggered();
void on_actionDocumentation_triggered();
void on_actionAbout_86Box_triggered();
void on_actionAbout_Qt_triggered();
protected:
void closeEvent(QCloseEvent *event) override;

View File

@@ -25,30 +25,45 @@
</property>
<widget class="QMenu" name="menuTools">
<property name="title">
<string>Tools</string>
<string>&amp;Tools</string>
</property>
<addaction name="actionPreferences"/>
<addaction name="actionCheck_for_updates"/>
</widget>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
<string>&amp;File</string>
</property>
<addaction name="actionNew_Machine"/>
<addaction name="separator"/>
<addaction name="actionExit"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
<string>&amp;Help</string>
</property>
<addaction name="actionDocumentation"/>
<addaction name="actionAbout_86Box"/>
<addaction name="actionAbout_Qt"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuTools"/>
<addaction name="menuHelp"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<widget class="QToolBar" name="toolBar">
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="windowTitle">
<string>toolBar</string>
</property>
<property name="movable">
<bool>false</bool>
</property>
<property name="allowedAreas">
<set>Qt::TopToolBarArea</set>
</property>
<property name="iconSize">
<size>
<width>16</width>
@@ -64,18 +79,14 @@
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="actionNew_Machine"/>
<addaction name="separator"/>
<addaction name="actionStartPause"/>
<addaction name="actionHard_Reset"/>
<addaction name="actionForce_Shutdown"/>
<addaction name="actionCtrl_Alt_Del"/>
<addaction name="actionSettings"/>
<addaction name="actionNew_Machine"/>
</widget>
<action name="actionDo_something">
<property name="text">
<string>Do something</string>
</property>
</action>
<action name="actionStartPause">
<property name="checkable">
<bool>true</bool>
@@ -85,7 +96,7 @@
<normaloff>:/menuicons/qt/icons/run.ico</normaloff>:/menuicons/qt/icons/run.ico</iconset>
</property>
<property name="text">
<string>Start</string>
<string>&amp;Start</string>
</property>
<property name="iconVisibleInMenu">
<bool>false</bool>
@@ -112,7 +123,7 @@
<normaloff>:/menuicons/qt/icons/acpi_shutdown.ico</normaloff>:/menuicons/qt/icons/acpi_shutdown.ico</iconset>
</property>
<property name="text">
<string>Force shutdown</string>
<string>&amp;Force shutdown</string>
</property>
<property name="toolTip">
<string>Force shutdown</string>
@@ -169,18 +180,18 @@
<normaloff>:/settings/qt/icons/86Box-yellow.ico</normaloff>:/settings/qt/icons/86Box-yellow.ico</iconset>
</property>
<property name="text">
<string>New Machine</string>
<string>&amp;New machine...</string>
</property>
<property name="toolTip">
<string>New Machine</string>
<string>New machine...</string>
</property>
</action>
<action name="actionPreferences">
<property name="text">
<string>Preferences</string>
<string>&amp;Preferences...</string>
</property>
<property name="toolTip">
<string>Preferences</string>
<string>Preferences...</string>
</property>
<property name="menuRole">
<enum>QAction::PreferencesRole</enum>
@@ -195,7 +206,7 @@
<normaloff>:/menuicons/qt/icons/run.ico</normaloff>:/menuicons/qt/icons/run.ico</iconset>
</property>
<property name="text">
<string>Start</string>
<string>&amp;Start</string>
</property>
<property name="iconVisibleInMenu">
<bool>false</bool>
@@ -203,20 +214,41 @@
</action>
<action name="actionCheck_for_updates">
<property name="text">
<string>Check for updates</string>
<string>&amp;Check for updates...</string>
</property>
</action>
<action name="actionExit">
<property name="icon">
<iconset theme="QIcon::ApplicationExit"/>
</property>
<property name="text">
<string>&amp;Exit</string>
<string>E&amp;xit</string>
</property>
<property name="menuRole">
<enum>QAction::QuitRole</enum>
</property>
</action>
<action name="actionDocumentation">
<property name="text">
<string>&amp;Documentation...</string>
</property>
</action>
<action name="actionAbout_86Box">
<property name="text">
<string>&amp;About 86Box...</string>
</property>
<property name="menuRole">
<enum>QAction::AboutRole</enum>
</property>
</action>
<action name="actionAbout_Qt">
<property name="text">
<string>About &amp;Qt</string>
</property>
<property name="visible">
<bool>false</bool>
</property>
<property name="menuRole">
<enum>QAction::AboutQtRole</enum>
</property>
</action>
</widget>
<resources>
<include location="../qt_resources.qrc"/>

View File

@@ -160,4 +160,15 @@ VMManagerModel::getProcessStats()
}
}
return stats;
}
int
VMManagerModel::getActiveMachineCount()
{
int running = 0;
for (const auto& system: machines) {
if (system->getProcessStatus() != VMManagerSystem::ProcessStatus::Stopped)
running++;
}
return running;
}

View File

@@ -57,6 +57,7 @@ public:
void reload(QWidget* parent = nullptr);
void updateDisplayName(const QModelIndex &index, const QString &newDisplayName);
QHash <QString, int> getProcessStats();
int getActiveMachineCount();
signals:
void systemDataChanged();

View File

@@ -28,6 +28,7 @@
#include <QElapsedTimer>
#include <QProgressDialog>
#include <QWindow>
#include "qt_util.hpp"
#include "qt_vmmanager_system.hpp"
// #include "qt_vmmanager_details_section.hpp"
#include "qt_vmmanager_detailsection.hpp"
@@ -67,16 +68,10 @@ VMManagerSystem::VMManagerSystem(const QString &sysconfig_file) {
// 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();
config_dir = shortened_dir = config_file.dir().absolutePath();
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);
uuid = util::generateUuid(sysconfig_file);
// That unique value is used to map the information to each individual system.
config_settings = new VMManagerConfig(VMManagerConfig::ConfigType::System, uuid);
@@ -126,6 +121,7 @@ VMManagerSystem::scanForConfigs(QWidget* parent, const QString &searchPath)
progDialog.setMinimum(0);
progDialog.setMaximum(0);
progDialog.setWindowFlags(progDialog.windowFlags() & ~Qt::WindowCloseButtonHint);
progDialog.setFixedSize(progDialog.sizeHint());
QElapsedTimer scanTimer;
scanTimer.start();
QVector<VMManagerSystem *> system_configs;
@@ -407,16 +403,33 @@ VMManagerSystem::launchMainProcess() {
return;
}
}
// If the system is already running, bring it to front
if (process->processId() != 0) {
#ifdef Q_OS_WINDOWS
if (this->id) {
SetForegroundWindow((HWND)this->id);
}
#endif
return;
}
setProcessEnvVars();
QString program = main_binary.filePath();
QStringList args;
args << "-P" << config_dir;
args << "--vmpath" << config_dir;
args << "--vmname" << displayName;
process->setProgram(program);
process->setArguments(args);
qDebug() << Q_FUNC_INFO << " Full Command:" << process->program() << " " << process->arguments();
process->start();
updateTimestamp();
connect(process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
[=](const int exitCode, const QProcess::ExitStatus exitStatus){
if (exitCode != 0 || exitStatus != QProcess::NormalExit) {
qInfo().nospace().noquote() << "Abnormal program termination while launching main process: exit code " << exitCode << ", exit status " << exitStatus;
return;
}
});
}
void
@@ -431,6 +444,15 @@ VMManagerSystem::launchSettings() {
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;
}
}
// If the system is already running, instruct it to show settings
if (process->processId() != 0) {
#ifdef Q_OS_WINDOWS
@@ -448,11 +470,19 @@ VMManagerSystem::launchSettings() {
QString program = main_binary.filePath();
QStringList open_command_args;
QStringList args;
args << "-P" << config_dir << "-S";
args << "--vmpath" << config_dir << "--settings";
process->setProgram(program);
process->setArguments(args);
qDebug() << Q_FUNC_INFO << " Full Command:" << process->program() << " " << process->arguments();
process->start();
connect(process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
[=](const int exitCode, const QProcess::ExitStatus exitStatus){
if (exitCode != 0 || exitStatus != QProcess::NormalExit) {
qInfo().nospace().noquote() << "Abnormal program termination while launching settings: exit code " << exitCode << ", exit status " << exitStatus;
return;
}
});
}
void