vmm preview
25
src/86box.c
@@ -251,6 +251,8 @@ struct accelKey def_acc_keys[NUM_ACCELS] = {
|
||||
.seq="Ctrl+Alt+M" }
|
||||
};
|
||||
|
||||
char vmm_path[1024] = { '\0'}; /* TEMPORARY - VM manager path to scan for VMs */
|
||||
int vmm_enabled = 0;
|
||||
|
||||
/* Statistics. */
|
||||
extern int mmuflush;
|
||||
@@ -598,8 +600,8 @@ pc_show_usage(char *s)
|
||||
#ifdef _WIN32
|
||||
"-D or --debug\t\t\t- force debug output logging\n"
|
||||
#endif
|
||||
#if 0
|
||||
"-E or --nographic\t\t- forces the old behavior\n"
|
||||
#if 1
|
||||
"-E or --vmmpath\t\t- vm manager path\n"
|
||||
#endif
|
||||
"-F or --fullscreen\t\t- start in fullscreen mode\n"
|
||||
"-G or --lang langid\t\t- start with specified language\n"
|
||||
@@ -732,13 +734,14 @@ usage:
|
||||
} else if (!strcasecmp(argv[c], "--debug") || !strcasecmp(argv[c], "-D")) {
|
||||
force_debug = 1;
|
||||
#endif
|
||||
#ifdef ENABLE_NG
|
||||
} else if (!strcasecmp(argv[c], "--nographic") || !strcasecmp(argv[c], "-E")) {
|
||||
/* Currently does nothing, but if/when we implement a built-in manager,
|
||||
it's going to force the manager not to run, allowing the old usage
|
||||
without parameter. */
|
||||
ng = 1;
|
||||
#endif
|
||||
//#ifdef ENABLE_NG
|
||||
} else if (!strcasecmp(argv[c], "--vmmpath") ||
|
||||
!strcasecmp(argv[c], "-E")) {
|
||||
/* Using this variable for vm manager path
|
||||
Temporary solution!*/
|
||||
if ((c+1) == argc) goto usage;
|
||||
strcpy(vmm_path, argv[++c]);
|
||||
//#endif
|
||||
} else if (!strcasecmp(argv[c], "--fullscreen") || !strcasecmp(argv[c], "-F")) {
|
||||
start_in_fullscreen = 1;
|
||||
} else if (!strcasecmp(argv[c], "--logfile") || !strcasecmp(argv[c], "-L")) {
|
||||
@@ -1025,6 +1028,10 @@ usage:
|
||||
}
|
||||
|
||||
pclog("# Configuration file: %s\n#\n\n", cfg_path);
|
||||
if (strlen(vmm_path) != 0) {
|
||||
vmm_enabled = 1;
|
||||
pclog("# VM Manager enabled. Path: %s\n", vmm_path);
|
||||
}
|
||||
/*
|
||||
* We are about to read the configuration file, which MAY
|
||||
* put data into global variables (the hard- and floppy
|
||||
|
||||
@@ -176,6 +176,8 @@ extern char usr_path[1024]; /* path (dir) of user data */
|
||||
extern char cfg_path[1024]; /* full path of config file */
|
||||
extern int open_dir_usr_path; /* default file open dialog directory of usr_path */
|
||||
extern char uuid[MAX_UUID_LEN]; /* UUID or machine identifier */
|
||||
extern char vmm_path[1024]; /* VM Manager path to scan (temporary) */
|
||||
extern int vmm_enabled;
|
||||
#ifndef USE_NEW_DYNAREC
|
||||
extern FILE *stdlog; /* file to log output to */
|
||||
#endif
|
||||
|
||||
@@ -189,6 +189,44 @@ add_library(ui STATIC
|
||||
qt_mediahistorymanager.cpp
|
||||
qt_mediahistorymanager.hpp
|
||||
|
||||
qt_updatecheck.cpp
|
||||
qt_updatecheck.hpp
|
||||
qt_updatecheckdialog.cpp
|
||||
qt_updatecheckdialog.hpp
|
||||
qt_updatedetails.cpp
|
||||
qt_updatedetails.hpp
|
||||
qt_downloader.cpp
|
||||
qt_downloader.hpp
|
||||
|
||||
qt_vmmanager_clientsocket.cpp
|
||||
qt_vmmanager_clientsocket.hpp
|
||||
qt_vmmanager_serversocket.cpp
|
||||
qt_vmmanager_serversocket.hpp
|
||||
qt_vmmanager_protocol.cpp
|
||||
qt_vmmanager_protocol.hpp
|
||||
qt_vmmanager_details.hpp
|
||||
qt_vmmanager_details.cpp
|
||||
qt_vmmanager_details.ui
|
||||
qt_vmmanager_addmachine.cpp
|
||||
qt_vmmanager_addmachine.hpp
|
||||
qt_vmmanager_detailsection.cpp
|
||||
qt_vmmanager_detailsection.hpp
|
||||
qt_vmmanager_listviewdelegate.hpp
|
||||
qt_vmmanager_listviewdelegate.cpp
|
||||
qt_vmmanager_preferences.cpp
|
||||
qt_vmmanager_preferences.hpp
|
||||
qt_vmmanager_main.hpp
|
||||
qt_vmmanager_main.cpp
|
||||
qt_vmmanager_main.ui
|
||||
qt_vmmanager_model.cpp
|
||||
qt_vmmanager_model.hpp
|
||||
qt_vmmanager_system.cpp
|
||||
qt_vmmanager_system.hpp
|
||||
qt_vmmanager_config.cpp
|
||||
qt_vmmanager_config.hpp
|
||||
qt_vmmanager_mainwindow.cpp
|
||||
qt_vmmanager_mainwindow.hpp
|
||||
|
||||
../qt_resources.qrc
|
||||
./qdarkstyle/dark/darkstyle.qrc
|
||||
|
||||
|
||||
BIN
src/qt/assets/86box-wizard.png
Normal file
|
After Width: | Height: | Size: 8.1 KiB |
BIN
src/qt/assets/systemicons/cpq_deskpro.png
Normal file
|
After Width: | Height: | Size: 239 KiB |
BIN
src/qt/assets/systemicons/cpq_port_386.png
Normal file
|
After Width: | Height: | Size: 245 KiB |
BIN
src/qt/assets/systemicons/cpq_port_II.png
Normal file
|
After Width: | Height: | Size: 158 KiB |
BIN
src/qt/assets/systemicons/cpq_port_III.png
Normal file
|
After Width: | Height: | Size: 198 KiB |
BIN
src/qt/assets/systemicons/cpq_portable.png
Normal file
|
After Width: | Height: | Size: 137 KiB |
BIN
src/qt/assets/systemicons/cpq_pres_2240.png
Normal file
|
After Width: | Height: | Size: 162 KiB |
BIN
src/qt/assets/systemicons/cpq_pres_4500.png
Normal file
|
After Width: | Height: | Size: 129 KiB |
BIN
src/qt/assets/systemicons/ibm330.png
Normal file
|
After Width: | Height: | Size: 182 KiB |
BIN
src/qt/assets/systemicons/ibm_at.png
Normal file
|
After Width: | Height: | Size: 276 KiB |
BIN
src/qt/assets/systemicons/ibm_pc_81.png
Normal file
|
After Width: | Height: | Size: 289 KiB |
BIN
src/qt/assets/systemicons/ibm_pc_82.png
Normal file
|
After Width: | Height: | Size: 289 KiB |
BIN
src/qt/assets/systemicons/ibm_pcjr.png
Normal file
|
After Width: | Height: | Size: 289 KiB |
BIN
src/qt/assets/systemicons/ibm_ps2_m70.png
Normal file
|
After Width: | Height: | Size: 214 KiB |
BIN
src/qt/assets/systemicons/ibm_ps2_m80.png
Normal file
|
After Width: | Height: | Size: 259 KiB |
BIN
src/qt/assets/systemicons/ibm_psvp_486.png
Normal file
|
After Width: | Height: | Size: 212 KiB |
BIN
src/qt/assets/systemicons/ibm_psvp_p60.png
Normal file
|
After Width: | Height: | Size: 212 KiB |
BIN
src/qt/assets/systemicons/ibm_xt_82.png
Normal file
|
After Width: | Height: | Size: 289 KiB |
BIN
src/qt/assets/systemicons/ibm_xt_86.png
Normal file
|
After Width: | Height: | Size: 289 KiB |
BIN
src/qt/assets/systemicons/olivetti_m19.png
Normal file
|
After Width: | Height: | Size: 257 KiB |
BIN
src/qt/assets/systemicons/olivetti_m21.png
Normal file
|
After Width: | Height: | Size: 317 KiB |
BIN
src/qt/assets/systemicons/olivetti_m24.png
Normal file
|
After Width: | Height: | Size: 204 KiB |
BIN
src/qt/assets/systemicons/olivetti_m24sp.png
Normal file
|
After Width: | Height: | Size: 204 KiB |
BIN
src/qt/assets/systemicons/os_archlinux_x2.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
src/qt/assets/systemicons/os_cloud_x2.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
src/qt/assets/systemicons/os_debian_x2.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
src/qt/assets/systemicons/os_dos_x2.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
src/qt/assets/systemicons/os_fedora_x2.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
src/qt/assets/systemicons/os_freebsd_x2.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
src/qt/assets/systemicons/os_gentoo_x2.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
src/qt/assets/systemicons/os_jrockitve_x2.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
src/qt/assets/systemicons/os_l4_x2.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
src/qt/assets/systemicons/os_linux22_x2.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
src/qt/assets/systemicons/os_linux24_x2.png
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
src/qt/assets/systemicons/os_linux26_x2.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
src/qt/assets/systemicons/os_linux_x2.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
src/qt/assets/systemicons/os_macosx_x2.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
src/qt/assets/systemicons/os_mandriva_x2.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
src/qt/assets/systemicons/os_netbsd_x2.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
src/qt/assets/systemicons/os_netware_x2.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
src/qt/assets/systemicons/os_openbsd_x2.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
src/qt/assets/systemicons/os_opensuse_x2.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
src/qt/assets/systemicons/os_oracle_x2.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
src/qt/assets/systemicons/os_oraclesolaris_x2.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
src/qt/assets/systemicons/os_os2_other_x2.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
src/qt/assets/systemicons/os_os2ecs_x2.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
src/qt/assets/systemicons/os_os2warp3_x2.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
src/qt/assets/systemicons/os_os2warp45_x2.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
src/qt/assets/systemicons/os_os2warp4_x2.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
src/qt/assets/systemicons/os_other_x2.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
src/qt/assets/systemicons/os_qnx_x2.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
BIN
src/qt/assets/systemicons/os_redhat_x2.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
src/qt/assets/systemicons/os_solaris_x2.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
src/qt/assets/systemicons/os_turbolinux_x2.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
src/qt/assets/systemicons/os_ubuntu_x2.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
src/qt/assets/systemicons/os_win10_x2.png
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
BIN
src/qt/assets/systemicons/os_win2k3_x2.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
src/qt/assets/systemicons/os_win2k8_x2.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
src/qt/assets/systemicons/os_win2k_x2.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
src/qt/assets/systemicons/os_win31_x2.png
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
BIN
src/qt/assets/systemicons/os_win7_x2.png
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
src/qt/assets/systemicons/os_win81_x2.png
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
BIN
src/qt/assets/systemicons/os_win8_x2.png
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
BIN
src/qt/assets/systemicons/os_win95_x2.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
src/qt/assets/systemicons/os_win98_x2.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
BIN
src/qt/assets/systemicons/os_win_other_x2.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
src/qt/assets/systemicons/os_winme_x2.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
src/qt/assets/systemicons/os_winnt4_x2.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
src/qt/assets/systemicons/os_winvista_x2.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
src/qt/assets/systemicons/os_winxp_x2.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
src/qt/assets/systemicons/os_xandros_x2.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
src/qt/assets/systemicons/pb_bora_pro.png
Normal file
|
After Width: | Height: | Size: 169 KiB |
BIN
src/qt/assets/systemicons/pb_pb410.png
Normal file
|
After Width: | Height: | Size: 205 KiB |
BIN
src/qt/assets/systemicons/pb_pb640.png
Normal file
|
After Width: | Height: | Size: 173 KiB |
BIN
src/qt/assets/systemicons/pb_pb680.png
Normal file
|
After Width: | Height: | Size: 169 KiB |
BIN
src/qt/assets/systemicons/tandy_1000.png
Normal file
|
After Width: | Height: | Size: 156 KiB |
BIN
src/qt/assets/systemicons/tandy_1000_hx.png
Normal file
|
After Width: | Height: | Size: 175 KiB |
BIN
src/qt/assets/systemicons/tandy_1000_sl2.png
Normal file
|
After Width: | Height: | Size: 209 KiB |
BIN
src/qt/assets/systemicons/toshiba_t1000.png
Normal file
|
After Width: | Height: | Size: 153 KiB |
BIN
src/qt/assets/systemicons/toshiba_t1200.png
Normal file
|
After Width: | Height: | Size: 285 KiB |
BIN
src/qt/assets/systemicons/toshiba_t1200_hdd.png
Normal file
|
After Width: | Height: | Size: 285 KiB |
BIN
src/qt/icons/green-square-16.png
Normal file
|
After Width: | Height: | Size: 115 B |
BIN
src/qt/icons/pause-16.png
Normal file
|
After Width: | Height: | Size: 123 B |
BIN
src/qt/icons/play-16.png
Normal file
|
After Width: | Height: | Size: 156 B |
BIN
src/qt/icons/red-power-16.png
Normal file
|
After Width: | Height: | Size: 174 B |
BIN
src/qt/icons/red-square-16.png
Normal file
|
After Width: | Height: | Size: 118 B |
BIN
src/qt/icons/stop-16.png
Normal file
|
After Width: | Height: | Size: 120 B |
BIN
src/qt/icons/yellow-square-16.png
Normal file
|
After Width: | Height: | Size: 117 B |
95
src/qt/qt_downloader.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Downloader module
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: cold-brewed
|
||||
*
|
||||
* Copyright 2024 cold-brewed
|
||||
*/
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QNetworkReply>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include "qt_downloader.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <86box/plat.h>
|
||||
}
|
||||
|
||||
Downloader::
|
||||
Downloader(const DownloadLocation downloadLocation, QObject *parent)
|
||||
: QObject(parent)
|
||||
, file(nullptr)
|
||||
, reply(nullptr)
|
||||
, variantData(QVariant::Invalid)
|
||||
{
|
||||
char PATHBUF[256];
|
||||
switch (downloadLocation) {
|
||||
case DownloadLocation::Data:
|
||||
plat_get_global_data_dir(PATHBUF, 255);
|
||||
break;
|
||||
case DownloadLocation::Config:
|
||||
plat_get_global_config_dir(PATHBUF, 255);
|
||||
break;
|
||||
case DownloadLocation::Temp:
|
||||
plat_get_temp_dir(PATHBUF, 255);
|
||||
break;
|
||||
}
|
||||
downloadDirectory = QDir(PATHBUF);
|
||||
}
|
||||
|
||||
Downloader::~Downloader() { delete file; }
|
||||
|
||||
void Downloader::download(const QUrl &url, const QString &filepath, const QVariant &varData) {
|
||||
|
||||
variantData = varData;
|
||||
// temporary until I get the plat stuff fixed
|
||||
// const auto global_dir = temporaryGetGlobalDataDir();
|
||||
// qDebug() << "I was passed filepath " << filepath;
|
||||
// Join with filename to create final file
|
||||
// const auto final_path = QDir(global_dir).filePath(filepath);
|
||||
const auto final_path = downloadDirectory.filePath(filepath);
|
||||
|
||||
file = new QFile(final_path);
|
||||
if(!file->open(QIODevice::WriteOnly)) {
|
||||
qWarning() << "Unable to open file " << final_path;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto nam = new QNetworkAccessManager(this);
|
||||
// Create the network request and execute
|
||||
const auto request = QNetworkRequest(url);
|
||||
reply = nam->get(request);
|
||||
// Connect to the finished signal
|
||||
connect(reply, &QNetworkReply::finished, this, &Downloader::onResult);
|
||||
}
|
||||
|
||||
void
|
||||
Downloader::onResult()
|
||||
{
|
||||
if (reply->error()) {
|
||||
qWarning() << "Error returned from QNetworkRequest: " << reply->errorString();
|
||||
emit errorOccurred(reply->errorString());
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
file->write(reply->readAll());
|
||||
file->flush();
|
||||
file->close();
|
||||
|
||||
reply->deleteLater();
|
||||
qDebug() << Q_FUNC_INFO << "Downloaded complete: file written to " << file->fileName();
|
||||
emit downloadCompleted(file->fileName(), variantData);
|
||||
}
|
||||
|
||||
57
src/qt/qt_downloader.hpp
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Header for the downloader module
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: cold-brewed
|
||||
*
|
||||
* Copyright 2024 cold-brewed
|
||||
*/
|
||||
|
||||
#ifndef QT_DOWNLOADER_HPP
|
||||
#define QT_DOWNLOADER_HPP
|
||||
|
||||
#include <QDir>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QString>
|
||||
#include <QFile>
|
||||
|
||||
|
||||
class Downloader : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum class DownloadLocation {
|
||||
Data, // AppDataLocation via plat_get_global_data_dir()
|
||||
Config, // AppConfigLocation via plat_get_global_config_dir()
|
||||
Temp // TempLocation via plat_get_temp_dir()
|
||||
};
|
||||
explicit Downloader(DownloadLocation downloadLocation = DownloadLocation::Data, QObject *parent = nullptr);
|
||||
~Downloader() final;
|
||||
|
||||
void download(const QUrl &url, const QString &filepath, const QVariant &varData = QVariant::Invalid);
|
||||
|
||||
signals:
|
||||
// Signal emitted when the download is successful
|
||||
void downloadCompleted(QString filename, QVariant varData);
|
||||
// Signal emitted when an error occurs
|
||||
void errorOccurred(const QString&);
|
||||
|
||||
private slots:
|
||||
void onResult();
|
||||
|
||||
private:
|
||||
QFile *file;
|
||||
QNetworkAccessManager nam;
|
||||
QNetworkReply *reply;
|
||||
QVariant variantData;
|
||||
QDir downloadDirectory;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -77,6 +77,8 @@ extern "C" {
|
||||
#include "qt_styleoverride.hpp"
|
||||
#include "qt_unixmanagerfilter.hpp"
|
||||
#include "qt_util.hpp"
|
||||
#include "qt_vmmanager_clientsocket.hpp"
|
||||
#include "qt_vmmanager_mainwindow.hpp"
|
||||
|
||||
// Void Cast
|
||||
#define VC(x) const_cast<wchar_t *>(x)
|
||||
@@ -662,6 +664,19 @@ 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
|
||||
@@ -758,6 +773,36 @@ main(int argc, char *argv[])
|
||||
socket.connectToServer(qgetenv("86BOX_MANAGER_SOCKET"));
|
||||
}
|
||||
|
||||
VMManagerClientSocket manager_socket;
|
||||
if (qgetenv("VMM_86BOX_SOCKET").size()) {
|
||||
manager_socket.IPCConnect(qgetenv("VMM_86BOX_SOCKET"));
|
||||
QObject::connect(&manager_socket, &VMManagerClientSocket::pause, main_window, &MainWindow::togglePause);
|
||||
QObject::connect(&manager_socket, &VMManagerClientSocket::resetVM, main_window, &MainWindow::hardReset);
|
||||
QObject::connect(&manager_socket, &VMManagerClientSocket::showsettings, main_window, &MainWindow::showSettings);
|
||||
QObject::connect(&manager_socket, &VMManagerClientSocket::ctrlaltdel, []() { pc_send_cad(); });
|
||||
QObject::connect(&manager_socket, &VMManagerClientSocket::request_shutdown, main_window, &MainWindow::close);
|
||||
QObject::connect(&manager_socket, &VMManagerClientSocket::force_shutdown, []() {
|
||||
do_stop();
|
||||
emit main_window->close();
|
||||
});
|
||||
QObject::connect(main_window, &MainWindow::vmmRunningStateChanged, &manager_socket, &VMManagerClientSocket::clientRunningStateChanged);
|
||||
main_window->installEventFilter(&manager_socket);
|
||||
}
|
||||
|
||||
/* Warn the user about unsupported configs */
|
||||
if (cpu_override) {
|
||||
QMessageBox warningbox(QMessageBox::Icon::Warning, QObject::tr("You are loading an unsupported configuration"),
|
||||
QObject::tr("CPU type filtering based on selected machine is disabled for this emulated machine.\n\nThis makes it possible to choose a CPU that is otherwise incompatible with the selected machine. However, you may run into incompatibilities with the machine BIOS or other software.\n\nEnabling this setting is not officially supported and any bug reports filed may be closed as invalid."),
|
||||
QMessageBox::NoButton, main_window);
|
||||
warningbox.addButton(QObject::tr("Continue"), QMessageBox::AcceptRole);
|
||||
warningbox.addButton(QObject::tr("Exit"), QMessageBox::RejectRole);
|
||||
warningbox.exec();
|
||||
if (warningbox.result() == QDialog::Accepted) {
|
||||
confirm_exit_cmdl = 0; /* skip the confirmation prompt without touching the config */
|
||||
emit main_window->close();
|
||||
}
|
||||
}
|
||||
|
||||
// pc_reset_hard_init();
|
||||
|
||||
QTimer onesec;
|
||||
|
||||
@@ -2131,6 +2131,7 @@ MainWindow::updateUiPauseState()
|
||||
QString(tr("Pause execution"));
|
||||
ui->actionPause->setIcon(pause_icon);
|
||||
ui->actionPause->setToolTip(tooltip_text);
|
||||
emit vmmRunningStateChanged(static_cast<VMManagerProtocol::RunningState>(dopause));
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
|
||||
#include "qt_vmmanager_protocol.hpp"
|
||||
|
||||
class MediaMenu;
|
||||
class RendererStack;
|
||||
|
||||
@@ -63,6 +65,8 @@ signals:
|
||||
|
||||
void showMessageForNonQtThread(int flags, const QString &header, const QString &message, bool richText, std::atomic_bool* done);
|
||||
void getTitleForNonQtThread(wchar_t *title);
|
||||
|
||||
void vmmRunningStateChanged(VMManagerProtocol::RunningState state);
|
||||
public slots:
|
||||
void showSettings();
|
||||
void hardReset();
|
||||
|
||||
360
src/qt/qt_updatecheck.cpp
Normal file
@@ -0,0 +1,360 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Update check module
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: cold-brewed
|
||||
*
|
||||
* Copyright 2024 cold-brewed
|
||||
*/
|
||||
|
||||
#include <QDebug>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QStandardPaths>
|
||||
#include <QTimer>
|
||||
|
||||
#include "qt_updatecheck.hpp"
|
||||
#include "qt_downloader.hpp"
|
||||
#include "qt_updatedetails.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <86box/version.h>
|
||||
}
|
||||
|
||||
UpdateCheck::
|
||||
UpdateCheck(const UpdateChannel channel, QObject *parent) : QObject(parent)
|
||||
{
|
||||
updateChannel = channel;
|
||||
currentVersion = getCurrentVersion(channel);
|
||||
}
|
||||
|
||||
UpdateCheck::~
|
||||
UpdateCheck()
|
||||
= default;
|
||||
|
||||
void
|
||||
UpdateCheck::checkForUpdates()
|
||||
{
|
||||
if (updateChannel == UpdateChannel::Stable) {
|
||||
const auto githubDownloader = new Downloader(Downloader::DownloadLocation::Temp);
|
||||
connect(githubDownloader, &Downloader::downloadCompleted, this, &UpdateCheck::githubDownloadComplete);
|
||||
connect(githubDownloader, &Downloader::errorOccurred, this, &UpdateCheck::generalDownloadError);
|
||||
githubDownloader->download(QUrl(githubReleaseApi), "github_releases.json");
|
||||
} else {
|
||||
const auto jenkinsDownloader = new Downloader(Downloader::DownloadLocation::Temp);
|
||||
connect(jenkinsDownloader, &Downloader::downloadCompleted, this, &UpdateCheck::jenkinsDownloadComplete);
|
||||
connect(jenkinsDownloader, &Downloader::errorOccurred, this, &UpdateCheck::generalDownloadError);
|
||||
jenkinsDownloader->download(jenkinsLatestNReleasesUrl(10), "jenkins_list.json");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
UpdateCheck::jenkinsDownloadComplete(const QString &filename, const QVariant &varData)
|
||||
{
|
||||
auto generalError = tr("Unable to determine release information");
|
||||
auto jenkinsReleaseListResult = parseJenkinsJson(filename);
|
||||
auto latestVersion = 0; // NOLINT (Default value as a fallback)
|
||||
|
||||
if(!jenkinsReleaseListResult.has_value() || jenkinsReleaseListResult.value().isEmpty()) {
|
||||
generalDownloadError(generalError);
|
||||
return;
|
||||
}
|
||||
const auto jenkinsReleaseList = jenkinsReleaseListResult.value();
|
||||
latestVersion = jenkinsReleaseListResult->first().buildNumber;
|
||||
|
||||
// If we can't determine the local build (blank current version), always show an update as available.
|
||||
// Callers can adjust accordingly.
|
||||
// Otherwise, do a comparison with EMU_BUILD_NUM
|
||||
bool updateAvailable = false;
|
||||
bool upToDate = true;
|
||||
if(currentVersion.isEmpty() || EMU_BUILD_NUM < latestVersion) {
|
||||
updateAvailable = true;
|
||||
upToDate = false;
|
||||
}
|
||||
|
||||
const auto updateResult = UpdateResult {
|
||||
.channel = updateChannel,
|
||||
.updateAvailable = updateAvailable,
|
||||
.upToDate = upToDate,
|
||||
.currentVersion = currentVersion,
|
||||
.latestVersion = QString::number(latestVersion),
|
||||
.githubInfo = {},
|
||||
.jenkinsInfo = jenkinsReleaseList,
|
||||
};
|
||||
|
||||
emit updateCheckComplete(updateResult);
|
||||
}
|
||||
|
||||
void
|
||||
UpdateCheck::generalDownloadError(const QString &error)
|
||||
{
|
||||
emit updateCheckError(error);
|
||||
}
|
||||
|
||||
void
|
||||
UpdateCheck::githubDownloadComplete(const QString &filename, const QVariant &varData)
|
||||
{
|
||||
const auto generalError = tr("Unable to determine release information");
|
||||
const auto githubReleaseListResult = parseGithubJson(filename);
|
||||
QString latestVersion = "0.0";
|
||||
if(!githubReleaseListResult.has_value() || githubReleaseListResult.value().isEmpty()) {
|
||||
generalDownloadError(generalError);
|
||||
}
|
||||
auto githubReleaseList = githubReleaseListResult.value();
|
||||
// Warning: this check (using the tag name) relies on a consistent naming scheme: "v<number>"
|
||||
// where <number> is the release number. For example, 4.2 from v4.2 as the tag name.
|
||||
// Another option would be parsing the name field which is generally "86Box <number>" but
|
||||
// either option requires a consistent naming scheme.
|
||||
latestVersion = githubReleaseList.first().tag_name.replace("v", "");
|
||||
for (const auto &release: githubReleaseList) {
|
||||
qDebug().noquote().nospace() << release.name << ": " << release.html_url << " (" << release.created_at << ")";
|
||||
}
|
||||
|
||||
// const auto updateDetails = new UpdateDetails(githubReleaseList, currentVersion);
|
||||
bool updateAvailable = false;
|
||||
bool upToDate = true;
|
||||
if(currentVersion.isEmpty() || (versionCompare(currentVersion, latestVersion) < 0)) {
|
||||
updateAvailable = true;
|
||||
upToDate = false;
|
||||
}
|
||||
|
||||
const auto updateResult = UpdateResult {
|
||||
.channel = updateChannel,
|
||||
.updateAvailable = updateAvailable,
|
||||
.upToDate = upToDate,
|
||||
.currentVersion = currentVersion,
|
||||
.latestVersion = latestVersion,
|
||||
.githubInfo = githubReleaseList,
|
||||
.jenkinsInfo = {},
|
||||
};
|
||||
|
||||
emit updateCheckComplete(updateResult);
|
||||
|
||||
}
|
||||
|
||||
QUrl
|
||||
UpdateCheck::jenkinsLatestNReleasesUrl(const int &count)
|
||||
{
|
||||
const auto urlPath = QString("https://ci.86box.net/job/86box/api/json?tree=builds[number,result,timestamp,changeSets[items[commitId,affectedPaths,author[fullName],msg,id]]]{0,%1}").arg(count);
|
||||
return { urlPath };
|
||||
}
|
||||
|
||||
QString
|
||||
UpdateCheck::getCurrentVersion(const UpdateChannel &updateChannel)
|
||||
{
|
||||
if (updateChannel == UpdateChannel::Stable) {
|
||||
return {EMU_VERSION};
|
||||
}
|
||||
// If EMU_BUILD_NUM is anything other than the default of zero it was set by the build process
|
||||
if constexpr (EMU_BUILD_NUM != 0) {
|
||||
return QString::number(EMU_BUILD_NUM); // NOLINT because EMU_BUILD_NUM is defined as 0 by default and is set at build time
|
||||
}
|
||||
// EMU_BUILD_NUM is not set, most likely a local build
|
||||
return {}; // NOLINT (Having EMU_BUILD_NUM assigned to a default number throws off the linter)
|
||||
}
|
||||
|
||||
std::optional<QList<UpdateCheck::JenkinsReleaseInfo>>
|
||||
UpdateCheck::parseJenkinsJson(const QString &filename)
|
||||
{
|
||||
QList<JenkinsReleaseInfo> releaseInfoList;
|
||||
QFile json_file(filename);
|
||||
if (!json_file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
qWarning() << "Couldn't open the json file: error" << json_file.error();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const QString read_file = json_file.readAll();
|
||||
json_file.close();
|
||||
|
||||
const auto json_doc = QJsonDocument::fromJson(read_file.toUtf8());
|
||||
|
||||
if (json_doc.isNull()) {
|
||||
qWarning("Failed to create QJsonDocument, possibly invalid JSON");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (!json_doc.isObject()) {
|
||||
qWarning("JSON does not have the expected format (object in root), cannot continue");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto json_object = json_doc.object();
|
||||
|
||||
// The json contains multiple release
|
||||
if(json_object.contains("builds") && json_object["builds"].isArray()) {
|
||||
|
||||
QJsonArray builds = json_object["builds"].toArray();
|
||||
for (const auto &each_build: builds) {
|
||||
if (auto build = parseJenkinsRelease(each_build.toObject()); build.has_value() && build.value().result == "SUCCESS") {
|
||||
releaseInfoList.append(build.value());
|
||||
}
|
||||
}
|
||||
} else if(json_object.contains("changeSets") && json_object["changeSets"].isArray()) {
|
||||
// The json contains only one release, as obtained by the lastSuccessfulBuild api
|
||||
if (const auto build = parseJenkinsRelease(json_object); build.has_value()) {
|
||||
releaseInfoList.append(build.value());
|
||||
}
|
||||
} else {
|
||||
qWarning("JSON is missing data or has invalid data, cannot continue");
|
||||
qDebug() << json_object;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return releaseInfoList;
|
||||
}
|
||||
|
||||
std::optional<UpdateCheck::JenkinsReleaseInfo>
|
||||
UpdateCheck::parseJenkinsRelease(const QJsonObject &json)
|
||||
{
|
||||
// The root should contain number, result, and timestamp.
|
||||
if (!json.contains("number") || !json.contains("result") || !json.contains("timestamp")) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto releaseInfo = JenkinsReleaseInfo {
|
||||
.buildNumber = json["number"].toInt(),
|
||||
.result = json["result"].toString(),
|
||||
.timestamp = static_cast<qint64>(json["timestamp"].toDouble())
|
||||
};
|
||||
|
||||
// Overview
|
||||
// Each build should contain a changeSets object with an array. Only the first element is needed.
|
||||
// The first element should be an object containing an items object with an array.
|
||||
// Each array element in the items object has information releated to the build. More or less: commit data
|
||||
// In jq parlance it would be similar to `builds[].changeSets[0].items[]`
|
||||
|
||||
// To break down the somewhat complicated if-init statement below:
|
||||
// * Get the object for `changeSets`
|
||||
// * Convert the value to array
|
||||
// * Grab the first element in the array
|
||||
// Proceed if
|
||||
// * the element (first in changeSets) is an object that contains the key `items`
|
||||
if (const auto changeSet = json["changeSets"].toArray().first(); changeSet.isObject() && changeSet.toObject().contains("items")) {
|
||||
// Then proceed to process each `items` array element
|
||||
for (const auto &item : changeSet.toObject()["items"].toArray()) {
|
||||
auto itemObject = item.toObject();
|
||||
// Basic validation
|
||||
if (!itemObject.contains("commitId") || !itemObject.contains("msg") || !itemObject.contains("affectedPaths")) {
|
||||
return std::nullopt;
|
||||
}
|
||||
// Convert the paths for each commit to a string list
|
||||
QStringList paths;
|
||||
for (const auto &each_path : itemObject["affectedPaths"].toArray().toVariantList()) {
|
||||
if (each_path.type() == QVariant::String) {
|
||||
paths.append(each_path.toString());
|
||||
}
|
||||
}
|
||||
// Build the structure
|
||||
const auto releaseItem = JenkinsChangeSetItem {
|
||||
.buildId = itemObject["commitId"].toString(),
|
||||
.author = itemObject["author"].toObject()["fullName"].toString(),
|
||||
.message = itemObject["msg"].toString(),
|
||||
.affectedPaths = paths,
|
||||
};
|
||||
releaseInfo.changeSetItems.append(releaseItem);
|
||||
}
|
||||
} else {
|
||||
qWarning("Could not parse release information, possibly invalid JSON");
|
||||
}
|
||||
return releaseInfo;
|
||||
}
|
||||
|
||||
std::optional<QList<UpdateCheck::GithubReleaseInfo>>
|
||||
UpdateCheck::parseGithubJson(const QString &filename)
|
||||
{
|
||||
QList<GithubReleaseInfo> releaseInfoList;
|
||||
QFile json_file(filename);
|
||||
if (!json_file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
qWarning("Couldn't open the json file: error %d", json_file.error());
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const QString read_file = json_file.readAll();
|
||||
json_file.close();
|
||||
|
||||
const auto json_doc = QJsonDocument::fromJson(read_file.toUtf8());
|
||||
|
||||
if (json_doc.isNull()) {
|
||||
qWarning("Failed to create QJsonDocument, possibly invalid JSON");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (!json_doc.isArray()) {
|
||||
qWarning("JSON does not have the expected format (array in root), cannot continue");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto release_array = json_doc.array();
|
||||
|
||||
for (const auto &each_release: release_array) {
|
||||
if (auto release = parseGithubRelease(each_release.toObject()); release.has_value()) {
|
||||
releaseInfoList.append(release.value());
|
||||
}
|
||||
}
|
||||
return releaseInfoList;
|
||||
}
|
||||
std::optional<UpdateCheck::GithubReleaseInfo>
|
||||
UpdateCheck::parseGithubRelease(const QJsonObject &json)
|
||||
{
|
||||
// Perform some basic validation
|
||||
if (!json.contains("name") || !json.contains("tag_name") || !json.contains("html_url")) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto githubRelease = GithubReleaseInfo {
|
||||
.name = json["name"].toString(),
|
||||
.tag_name = json["tag_name"].toString(),
|
||||
.html_url = json["html_url"].toString(),
|
||||
.target_commitish = json["target_commitish"].toString(),
|
||||
.created_at = json["created_at"].toString(),
|
||||
.published_at = json["published_at"].toString(),
|
||||
.body = json["body"].toString(),
|
||||
};
|
||||
|
||||
return githubRelease;
|
||||
}
|
||||
|
||||
// A simple method to compare version numbers
|
||||
// Should work for comparing x.y.z and x.y. Missing
|
||||
// values (parts) will be treated as zeroes
|
||||
int
|
||||
UpdateCheck::versionCompare(const QString &version1, const QString &version2)
|
||||
{
|
||||
// Split both
|
||||
QStringList v1List = version1.split('.');
|
||||
QStringList v2List = version2.split('.');
|
||||
|
||||
// Out of the two versions get the maximum amount of "parts"
|
||||
const int maxParts = std::max(v1List.size(), v2List.size());
|
||||
|
||||
// Initialize both with zeros
|
||||
QVector<int> v1Parts(maxParts, 0);
|
||||
QVector<int> v2Parts(maxParts, 0);
|
||||
|
||||
for (int i = 0; i < v1List.size(); ++i) {
|
||||
v1Parts[i] = v1List[i].toInt();
|
||||
}
|
||||
|
||||
for (int i = 0; i < v2List.size(); ++i) {
|
||||
v2Parts[i] = v2List[i].toInt();
|
||||
}
|
||||
|
||||
for (int i = 0; i < maxParts; ++i) {
|
||||
// First version is greater
|
||||
if (v1Parts[i] > v2Parts[i])
|
||||
return 1;
|
||||
// First version is less
|
||||
if (v1Parts[i] < v2Parts[i])
|
||||
return -1;
|
||||
}
|
||||
// They are equal
|
||||
return 0;
|
||||
}
|
||||
104
src/qt/qt_updatecheck.hpp
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Header for the update check module
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: cold-brewed
|
||||
*
|
||||
* Copyright 2024 cold-brewed
|
||||
*/
|
||||
|
||||
#ifndef QT_UPDATECHECK_HPP
|
||||
#define QT_UPDATECHECK_HPP
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QObject>
|
||||
#include <QUrl>
|
||||
#include <QWidget>
|
||||
|
||||
#include <optional>
|
||||
|
||||
class UpdateCheck final : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum class UpdateChannel {
|
||||
Stable,
|
||||
CI,
|
||||
};
|
||||
|
||||
struct JenkinsChangeSetItem {
|
||||
QString buildId; // sha hash
|
||||
QString author; // github username
|
||||
QString message; // commit message
|
||||
QStringList affectedPaths; // list of files in the change
|
||||
};
|
||||
|
||||
struct JenkinsReleaseInfo {
|
||||
int buildNumber = 0;
|
||||
QString result;
|
||||
qint64 timestamp = 0;
|
||||
QList<JenkinsChangeSetItem> changeSetItems;
|
||||
};
|
||||
|
||||
struct GithubReleaseInfo {
|
||||
QString name;
|
||||
QString tag_name;
|
||||
QString html_url;
|
||||
QString target_commitish;
|
||||
QString created_at;
|
||||
QString published_at;
|
||||
QString body;
|
||||
};
|
||||
|
||||
struct UpdateResult {
|
||||
UpdateChannel channel;
|
||||
bool updateAvailable = false;
|
||||
bool upToDate = false;
|
||||
QString currentVersion;
|
||||
QString latestVersion;
|
||||
QList<GithubReleaseInfo> githubInfo;
|
||||
QList<JenkinsReleaseInfo> jenkinsInfo;
|
||||
};
|
||||
|
||||
explicit UpdateCheck(UpdateChannel channel, QObject *parent = nullptr);
|
||||
~UpdateCheck() override;
|
||||
void checkForUpdates();
|
||||
static int versionCompare(const QString &version1, const QString &version2);
|
||||
[[nodiscard]] static QString getCurrentVersion(const UpdateChannel &updateChannel = UpdateChannel::Stable);
|
||||
|
||||
signals:
|
||||
// void updateCheckComplete(const UpdateCheck::UpdateChannel &channel, const QVariant &updateData);
|
||||
void updateCheckComplete(const UpdateCheck::UpdateResult &result);
|
||||
void updateCheckError(const QString &errorMsg);
|
||||
|
||||
private:
|
||||
UpdateChannel updateChannel = UpdateChannel::Stable;
|
||||
|
||||
const QUrl githubReleaseApi = QUrl("https://api.github.com/repos/86box/86Box/releases");
|
||||
const QUrl jenkinsLatestApi = QUrl("https://ci.86box.net/job/86box/lastSuccessfulBuild/api/json");
|
||||
QString jenkinsLatestVersion;
|
||||
QString currentVersion;
|
||||
|
||||
static QUrl jenkinsLatestNReleasesUrl(const int &count);
|
||||
|
||||
static std::optional<QList<JenkinsReleaseInfo>> parseJenkinsJson(const QString &filename);
|
||||
static std::optional<JenkinsReleaseInfo> parseJenkinsRelease(const QJsonObject &json);
|
||||
|
||||
static std::optional<QList<GithubReleaseInfo>> parseGithubJson(const QString &filename);
|
||||
static std::optional<GithubReleaseInfo> parseGithubRelease(const QJsonObject &json);
|
||||
|
||||
|
||||
private slots:
|
||||
void jenkinsDownloadComplete(const QString &filename, const QVariant& varData);
|
||||
void githubDownloadComplete(const QString &filename, const QVariant& varData);
|
||||
void generalDownloadError(const QString &error);
|
||||
};
|
||||
|
||||
#endif // QT_UPDATECHECK_HPP
|
||||
90
src/qt/qt_updatecheckdialog.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Update check dialog module
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: cold-brewed
|
||||
*
|
||||
* Copyright 2024 cold-brewed
|
||||
*/
|
||||
|
||||
#include <QDir>
|
||||
#include <QTimer>
|
||||
|
||||
#include "qt_updatecheckdialog.hpp"
|
||||
#include "ui_qt_updatecheckdialog.h"
|
||||
#include "qt_updatedetails.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <86box/version.h>
|
||||
}
|
||||
|
||||
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);
|
||||
updateChannel = channel;
|
||||
currentVersion = UpdateCheck::getCurrentVersion(updateChannel);
|
||||
connect(updateCheck, &UpdateCheck::updateCheckError, [=](const QString &errorMsg) {
|
||||
generalDownloadError(errorMsg);
|
||||
});
|
||||
connect(updateCheck, &UpdateCheck::updateCheckComplete, this, &UpdateCheckDialog::downloadComplete);
|
||||
|
||||
QTimer::singleShot(0, [this] {
|
||||
updateCheck->checkForUpdates();
|
||||
});
|
||||
}
|
||||
|
||||
UpdateCheckDialog::~
|
||||
UpdateCheckDialog()
|
||||
= default;
|
||||
|
||||
void
|
||||
UpdateCheckDialog::generalDownloadError(const QString &error) const
|
||||
{
|
||||
ui->progressBar->setMaximum(100);
|
||||
ui->progressBar->setValue(100);
|
||||
ui->statusLabel->setVisible(true);
|
||||
const auto statusText = tr("There was an error checking for updates:\n\n%1\n\nPlease try again later.").arg(error);
|
||||
ui->statusLabel->setText(statusText);
|
||||
ui->buttonBox->setStandardButtons(QDialogButtonBox::Ok);
|
||||
}
|
||||
|
||||
void
|
||||
UpdateCheckDialog::downloadComplete(const UpdateCheck::UpdateResult &result)
|
||||
{
|
||||
if (result.upToDate) {
|
||||
upToDate();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto updateDetails = new UpdateDetails(result);
|
||||
connect(updateDetails, &QDialog::accepted, [this] {
|
||||
accept();
|
||||
});
|
||||
connect(updateDetails, &QDialog::rejected, [this] {
|
||||
reject();
|
||||
});
|
||||
updateDetails->exec();
|
||||
}
|
||||
|
||||
void
|
||||
UpdateCheckDialog::upToDate()
|
||||
{
|
||||
ui->titleLabel->setText(tr("Update check complete"));
|
||||
ui->progressBar->setMaximum(100);
|
||||
ui->progressBar->setValue(100);
|
||||
ui->statusLabel->setVisible(true);
|
||||
const auto statusText = tr("You are running the latest %1 version of 86Box: %2").arg(updateChannel == UpdateCheck::UpdateChannel::Stable ? "stable" : "beta", currentVersion);
|
||||
ui->statusLabel->setText(statusText);
|
||||
ui->buttonBox->setStandardButtons(QDialogButtonBox::Ok);
|
||||
}
|
||||
47
src/qt/qt_updatecheckdialog.hpp
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Header for the update check dialog module
|
||||
*
|
||||
*
|
||||
*
|
||||
* Authors: cold-brewed
|
||||
*
|
||||
* Copyright 2024 cold-brewed
|
||||
*/
|
||||
|
||||
#ifndef QT_UPDATECHECKDIALOG_HPP
|
||||
#define QT_UPDATECHECKDIALOG_HPP
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include <qt_updatecheck.hpp>
|
||||
|
||||
namespace Ui {
|
||||
class UpdateCheckDialog;
|
||||
}
|
||||
|
||||
class UpdateCheckDialog final : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit UpdateCheckDialog(UpdateCheck::UpdateChannel channel, QWidget *parent = nullptr);
|
||||
~UpdateCheckDialog() override;
|
||||
|
||||
private:
|
||||
Ui::UpdateCheckDialog *ui;
|
||||
UpdateCheck::UpdateChannel updateChannel = UpdateCheck::UpdateChannel::Stable;
|
||||
UpdateCheck *updateCheck;
|
||||
QString currentVersion;
|
||||
void upToDate();
|
||||
|
||||
private slots:
|
||||
void downloadComplete(const UpdateCheck::UpdateResult &result);
|
||||
void generalDownloadError(const QString &error) const;
|
||||
};
|
||||
|
||||
#endif // QT_UPDATECHECKDIALOG_HPP
|
||||