Files
86Box/src/qt/qt_mainwindow.cpp

2003 lines
54 KiB
C++
Raw Normal View History

2022-02-07 15:00:02 +06:00
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Main window module.
*
*
*
* Authors: Joakim L. Gilje <jgilje@jgilje.net>
* Cacodemon345
* Teemu Korhonen
2022-02-07 15:35:15 +06:00
* dob205
2022-02-07 15:00:02 +06:00
*
* Copyright 2021 Joakim L. Gilje
* Copyright 2021-2022 Cacodemon345
* Copyright 2021-2022 Teemu Korhonen
2022-02-07 15:35:15 +06:00
* Copyright 2022 dob205
2022-02-07 15:00:02 +06:00
*/
#include <QDebug>
2021-11-25 10:20:56 +01:00
#include "qt_mainwindow.hpp"
#include "ui_qt_mainwindow.h"
2021-12-12 01:16:27 +06:00
#include "qt_specifydimensions.h"
2021-12-12 16:22:35 +06:00
#include "qt_soundgain.hpp"
#include "qt_progsettings.hpp"
2022-05-23 17:29:45 +06:00
#include "qt_mcadevicelist.hpp"
2021-12-12 01:16:27 +06:00
2021-12-27 16:32:03 +06:00
#include "qt_rendererstack.hpp"
#include "qt_renderercommon.hpp"
2021-12-27 16:32:03 +06:00
2021-11-25 10:20:56 +01:00
extern "C" {
#include <86box/86box.h>
#include <86box/config.h>
#include <86box/keyboard.h>
2021-11-25 10:20:56 +01:00
#include <86box/plat.h>
#include <86box/discord.h>
#include <86box/video.h>
#include <86box/machine.h>
#include <86box/vid_ega.h>
#include <86box/version.h>
2022-02-04 15:19:18 +06:00
extern int qt_nvr_save(void);
2022-02-04 15:19:18 +06:00
#ifdef MTR_ENABLED
#include <minitrace/minitrace.h>
#endif
2021-11-25 10:20:56 +01:00
};
2021-12-06 10:41:14 +01:00
#include <QGuiApplication>
2021-11-25 10:20:56 +01:00
#include <QWindow>
#include <QTimer>
#include <QThread>
2021-11-25 10:20:56 +01:00
#include <QKeyEvent>
2021-11-29 12:25:27 +01:00
#include <QMessageBox>
2021-12-05 15:57:46 +06:00
#include <QFocusEvent>
2021-12-09 16:27:53 +06:00
#include <QApplication>
#include <QPushButton>
#include <QDesktopServices>
#include <QUrl>
2021-12-12 01:16:27 +06:00
#include <QCheckBox>
2021-12-18 01:03:05 +06:00
#include <QActionGroup>
2021-12-27 14:25:18 +02:00
#include <QOpenGLContext>
2021-12-30 23:36:49 +06:00
#include <QScreen>
2022-01-13 09:28:38 +02:00
#include <QString>
2021-11-25 10:20:56 +01:00
#include <array>
2021-12-06 10:41:14 +01:00
#include <unordered_map>
2021-11-25 10:20:56 +01:00
#include "qt_settings.hpp"
#include "qt_machinestatus.hpp"
#include "qt_mediamenu.hpp"
#include "qt_util.hpp"
#if defined __unix__ && !defined __HAIKU__
2021-12-27 16:32:03 +06:00
#ifdef WAYLAND
#include "wl_mouse.hpp"
#endif
#include <X11/Xlib.h>
#include <X11/keysym.h>
2021-12-05 15:57:46 +06:00
#undef KeyPress
#undef KeyRelease
#endif
#ifdef Q_OS_MACOS
// The namespace is required to avoid clashing typedefs; we only use this
// header for its #defines anyway.
namespace IOKit {
#include <IOKit/hidsystem/IOLLEvent.h>
}
#endif
#ifdef __HAIKU__
#include <os/AppKit.h>
#include <os/InterfaceKit.h>
extern MainWindow* main_window;
filter_result keyb_filter(BMessage *message, BHandler **target, BMessageFilter *filter)
{
if (message->what == B_KEY_DOWN || message->what == B_KEY_UP
|| message->what == B_UNMAPPED_KEY_DOWN || message->what == B_UNMAPPED_KEY_UP)
{
int key_state = 0, key_scancode = 0;
key_state = message->what == B_KEY_DOWN || message->what == B_UNMAPPED_KEY_DOWN;
message->FindInt32("key", &key_scancode);
QGuiApplication::postEvent(main_window, new QKeyEvent(key_state ? QEvent::KeyPress : QEvent::KeyRelease, 0, QGuiApplication::keyboardModifiers(), key_scancode, 0, 0));
if (key_scancode == 0x68 && key_state)
{
QGuiApplication::postEvent(main_window, new QKeyEvent(QEvent::KeyRelease, 0, QGuiApplication::keyboardModifiers(), key_scancode, 0, 0));
}
}
return B_DISPATCH_MESSAGE;
}
static BMessageFilter* filter;
#endif
extern void qt_mouse_capture(int);
extern "C" void qt_blit(int x, int y, int w, int h, int monitor_index);
2021-11-25 10:20:56 +01:00
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
mm = std::make_shared<MediaMenu>(this);
MediaMenu::ptr = mm;
status = std::make_unique<MachineStatus>(this);
2021-11-25 10:20:56 +01:00
#ifdef __HAIKU__
filter = new BMessageFilter(B_PROGRAMMED_DELIVERY, B_ANY_SOURCE, keyb_filter);
((BWindow*)this->winId())->AddFilter(filter);
#endif
setUnifiedTitleAndToolBarOnMac(true);
2021-11-25 10:20:56 +01:00
ui->setupUi(this);
ui->stackedWidget->setMouseTracking(true);
2021-12-12 01:16:27 +06:00
statusBar()->setVisible(!hide_status_bar);
statusBar()->setStyleSheet("QStatusBar::item {border: None; } QStatusBar QLabel { margin-right: 2px; margin-bottom: 1px; }");
2022-01-09 17:00:16 +02:00
ui->toolBar->setVisible(!hide_tool_bar);
2022-07-04 01:50:42 +06:00
renderers[0].reset(nullptr);
auto toolbar_spacer = new QWidget();
toolbar_spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
ui->toolBar->addWidget(toolbar_spacer);
auto toolbar_label = new QLabel();
ui->toolBar->addWidget(toolbar_label);
2022-02-07 12:16:07 +06:00
#ifdef RELEASE_BUILD
this->setWindowIcon(QIcon(":/settings/win/icons/86Box-green.ico"));
#elif defined ALPHA_BUILD
2022-02-07 20:31:56 +06:00
this->setWindowIcon(QIcon(":/settings/win/icons/86Box-red.ico"));
2022-02-07 12:16:07 +06:00
#elif defined BETA_BUILD
2021-12-09 16:27:53 +06:00
this->setWindowIcon(QIcon(":/settings/win/icons/86Box-yellow.ico"));
2022-02-07 12:16:07 +06:00
#else
this->setWindowIcon(QIcon(":/settings/win/icons/86Box-gray.ico"));
#endif
this->setWindowFlag(Qt::MSWindowsFixedSizeDialogHint, vid_resize != 1);
this->setWindowFlag(Qt::WindowMaximizeButtonHint, vid_resize == 1);
2021-12-09 16:27:53 +06:00
QString vmname(vm_name);
2022-02-10 20:21:00 +06:00
if (vmname.at(vmname.size() - 1) == '"' || vmname.at(vmname.size() - 1) == '\'') vmname.truncate(vmname.size() - 1);
this->setWindowTitle(QString("%1 - %2 %3").arg(vmname, EMU_NAME, EMU_VERSION_FULL));
connect(this, &MainWindow::showMessageForNonQtThread, this, &MainWindow::showMessage_, Qt::BlockingQueuedConnection);
2021-11-29 12:25:27 +01:00
connect(this, &MainWindow::setTitle, this, [this,toolbar_label](const QString& title) {
2022-02-13 00:36:47 +06:00
if (dopause && !hide_tool_bar)
{
toolbar_label->setText(toolbar_label->text() + tr(" - PAUSED"));
2022-02-13 00:36:47 +06:00
return;
}
if (!hide_tool_bar)
2022-02-20 02:26:27 -05:00
#ifdef _WIN32
toolbar_label->setText(title);
#else
{
/* get the percentage and mouse message, TODO: refactor ui_window_title() */
auto parts = title.split(" - ");
if (parts.size() >= 2) {
if (parts.size() < 5)
toolbar_label->setText(parts[1]);
else
toolbar_label->setText(QString("%1 - %2").arg(parts[1], parts.last()));
}
}
#endif
ui->actionPause->setChecked(dopause);
});
2021-12-01 15:55:41 +06:00
connect(this, &MainWindow::getTitleForNonQtThread, this, &MainWindow::getTitle_, Qt::BlockingQueuedConnection);
2021-12-12 01:16:27 +06:00
connect(this, &MainWindow::updateMenuResizeOptions, [this]() {
ui->actionResizable_window->setEnabled(vid_resize != 2);
ui->actionResizable_window->setChecked(vid_resize == 1);
ui->menuWindow_scale_factor->setEnabled(vid_resize == 0);
});
connect(this, &MainWindow::updateWindowRememberOption, [this]() {
ui->actionRemember_size_and_position->setChecked(window_remember);
});
emit updateMenuResizeOptions();
connect(this, &MainWindow::pollMouse, ui->stackedWidget, &RendererStack::mousePoll, Qt::DirectConnection);
2021-11-25 10:20:56 +01:00
connect(this, &MainWindow::setMouseCapture, this, [this](bool state) {
2021-11-25 10:20:56 +01:00
mouse_capture = state ? 1 : 0;
qt_mouse_capture(mouse_capture);
if (mouse_capture) {
this->grabKeyboard();
2022-04-21 13:56:39 -03:00
if (ui->stackedWidget->mouse_capture_func)
ui->stackedWidget->mouse_capture_func(this->windowHandle());
} else {
this->releaseKeyboard();
2022-04-21 13:56:39 -03:00
if (ui->stackedWidget->mouse_uncapture_func)
ui->stackedWidget->mouse_uncapture_func();
}
2021-11-25 10:20:56 +01:00
});
connect(qApp, &QGuiApplication::applicationStateChanged, [this](Qt::ApplicationState state) {
if (mouse_capture && state != Qt::ApplicationState::ApplicationActive)
emit setMouseCapture(false);
});
2021-11-25 10:20:56 +01:00
connect(this, &MainWindow::resizeContents, this, [this](int w, int h) {
2022-07-04 17:54:38 +06:00
if (!QApplication::platformName().contains("eglfs") && vid_resize != 1) {
2022-03-23 16:00:36 +06:00
w = (w / (!dpi_scale ? util::screenOfWidget(this)->devicePixelRatio() : 1.));
2022-02-20 02:26:27 -05:00
2022-03-23 16:00:36 +06:00
int modifiedHeight = (h / (!dpi_scale ? util::screenOfWidget(this)->devicePixelRatio() : 1.))
2022-01-09 17:00:16 +02:00
+ menuBar()->height()
+ (statusBar()->height() * !hide_status_bar)
+ (ui->toolBar->height() * !hide_tool_bar);
2022-02-20 02:26:27 -05:00
ui->stackedWidget->resize(w, h);
2022-02-11 00:52:25 +06:00
setFixedSize(w, modifiedHeight);
}
2021-11-25 10:20:56 +01:00
});
2022-07-04 17:54:38 +06:00
connect(this, &MainWindow::resizeContentsMonitor, this, [this](int w, int h, int monitor_index)
{
if (!QApplication::platformName().contains("eglfs") && vid_resize != 1) {
qDebug() << "Resize";
2022-07-04 17:54:38 +06:00
w = (w / (!dpi_scale ? util::screenOfWidget(renderers[monitor_index].get())->devicePixelRatio() : 1.));
int modifiedHeight = (h / (!dpi_scale ? util::screenOfWidget(renderers[monitor_index].get())->devicePixelRatio() : 1.));
renderers[monitor_index]->setFixedSize(w, modifiedHeight);
}
});
connect(ui->menubar, &QMenuBar::triggered, this, [this] {
2021-11-25 10:20:56 +01:00
config_save();
if (QApplication::activeWindow() == this)
{
ui->stackedWidget->setFocusRenderer();
}
2021-11-25 10:20:56 +01:00
});
connect(this, &MainWindow::updateStatusBarPanes, this, [this] {
refreshMediaMenu();
});
connect(this, &MainWindow::updateStatusBarPanes, this, &MainWindow::refreshMediaMenu);
connect(this, &MainWindow::updateStatusBarTip, status.get(), &MachineStatus::updateTip);
connect(this, &MainWindow::updateStatusBarActivity, status.get(), &MachineStatus::setActivity);
connect(this, &MainWindow::updateStatusBarEmpty, status.get(), &MachineStatus::setEmpty);
connect(this, &MainWindow::statusBarMessage, status.get(), &MachineStatus::message, Qt::QueuedConnection);
2021-11-28 20:49:05 +01:00
2021-11-25 10:20:56 +01:00
ui->actionKeyboard_requires_capture->setChecked(kbd_req_capture);
ui->actionRight_CTRL_is_left_ALT->setChecked(rctrl_is_lalt);
2021-12-12 01:16:27 +06:00
ui->actionResizable_window->setChecked(vid_resize == 1);
ui->actionRemember_size_and_position->setChecked(window_remember);
2021-12-06 21:58:41 +01:00
ui->menuWindow_scale_factor->setEnabled(vid_resize == 0);
2021-12-12 01:16:27 +06:00
ui->actionHiDPI_scaling->setChecked(dpi_scale);
ui->actionHide_status_bar->setChecked(hide_status_bar);
2022-01-09 17:00:16 +02:00
ui->actionHide_tool_bar->setChecked(hide_tool_bar);
2021-12-12 01:16:27 +06:00
ui->actionUpdate_status_bar_icons->setChecked(update_icons);
ui->actionEnable_Discord_integration->setChecked(enable_discord);
#if defined Q_OS_WINDOWS || defined Q_OS_MACOS
/* Make the option visible only if ANGLE is loaded. */
ui->actionHardware_Renderer_OpenGL_ES->setVisible(QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES);
if (QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGLES && vid_api == 2) vid_api = 1;
#endif
2022-03-01 13:31:19 +06:00
ui->actionHardware_Renderer_OpenGL->setVisible(QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGLES);
if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES && vid_api == 1) vid_api = 0;
if ((QApplication::platformName().contains("eglfs") || QApplication::platformName() == "haiku")) {
if (vid_api >= 1) fprintf(stderr, "OpenGL renderers are unsupported on %s.\n", QApplication::platformName().toUtf8().data());
vid_api = 0;
ui->actionHardware_Renderer_OpenGL->setVisible(false);
ui->actionHardware_Renderer_OpenGL_ES->setVisible(false);
2022-04-21 16:32:46 +06:00
ui->actionVulkan->setVisible(false);
ui->actionOpenGL_3_0_Core->setVisible(false);
}
2022-06-22 16:36:38 +06:00
#if !defined Q_OS_WINDOWS
ui->actionDirect3D_9->setVisible(false);
if (vid_api == 5) vid_api = 0;
#endif
#if !QT_CONFIG(vulkan)
if (vid_api == 4) vid_api = 0;
ui->actionVulkan->setVisible(false);
#endif
2021-12-18 01:03:05 +06:00
QActionGroup* actGroup = nullptr;
2021-12-18 01:03:05 +06:00
actGroup = new QActionGroup(this);
actGroup->addAction(ui->actionSoftware_Renderer);
actGroup->addAction(ui->actionHardware_Renderer_OpenGL);
actGroup->addAction(ui->actionHardware_Renderer_OpenGL_ES);
2021-12-27 16:32:03 +06:00
actGroup->addAction(ui->actionOpenGL_3_0_Core);
2022-04-21 16:32:46 +06:00
actGroup->addAction(ui->actionVulkan);
2022-06-22 16:36:38 +06:00
actGroup->addAction(ui->actionDirect3D_9);
actGroup->setExclusive(true);
connect(actGroup, &QActionGroup::triggered, [this](QAction* action) {
vid_api = action->property("vid_api").toInt();
2022-07-05 00:21:30 +06:00
RendererStack::Renderer newVidApi = RendererStack::Renderer::Software;
switch (vid_api)
{
case 0:
2022-07-05 00:21:30 +06:00
newVidApi = RendererStack::Renderer::Software;
break;
case 1:
2022-07-05 00:21:30 +06:00
newVidApi = (RendererStack::Renderer::OpenGL);
break;
case 2:
2022-07-05 00:21:30 +06:00
newVidApi = (RendererStack::Renderer::OpenGLES);
break;
case 3:
2022-07-05 00:21:30 +06:00
newVidApi = (RendererStack::Renderer::OpenGL3);
break;
2022-04-21 16:32:46 +06:00
case 4:
2022-07-05 00:21:30 +06:00
newVidApi = (RendererStack::Renderer::Vulkan);
2022-04-21 16:32:46 +06:00
break;
2022-06-22 16:36:38 +06:00
case 5:
2022-07-05 00:21:30 +06:00
newVidApi = (RendererStack::Renderer::Direct3D9);
2022-06-22 16:36:38 +06:00
break;
}
2022-07-05 00:21:30 +06:00
ui->stackedWidget->switchRenderer(newVidApi);
for (int i = 1; i < MONITORS_NUM; i++) {
if (renderers[i]) renderers[i]->switchRenderer(newVidApi);
}
});
connect(ui->stackedWidget, &RendererStack::rendererChanged, [this]() {
ui->actionRenderer_options->setVisible(ui->stackedWidget->hasOptions());
});
/* Trigger initial renderer switch */
for (auto action : actGroup->actions())
if (action->property("vid_api").toInt() == vid_api) {
action->setChecked(true);
emit actGroup->triggered(action);
break;
}
2021-12-06 21:58:41 +01:00
switch (scale) {
case 0:
ui->action0_5x->setChecked(true);
break;
case 1:
ui->action1x->setChecked(true);
break;
case 2:
ui->action1_5x->setChecked(true);
break;
case 3:
ui->action2x->setChecked(true);
break;
}
2021-12-18 01:03:05 +06:00
actGroup = new QActionGroup(this);
actGroup->addAction(ui->action0_5x);
actGroup->addAction(ui->action1x);
actGroup->addAction(ui->action1_5x);
actGroup->addAction(ui->action2x);
2021-12-06 22:02:18 +01:00
switch (video_filter_method) {
case 0:
ui->actionNearest->setChecked(true);
break;
case 1:
ui->actionLinear->setChecked(true);
break;
}
2021-12-18 01:03:05 +06:00
actGroup = new QActionGroup(this);
actGroup->addAction(ui->actionNearest);
actGroup->addAction(ui->actionLinear);
2021-12-07 13:47:42 +01:00
switch (video_fullscreen_scale) {
case FULLSCR_SCALE_FULL:
ui->actionFullScreen_stretch->setChecked(true);
break;
case FULLSCR_SCALE_43:
ui->actionFullScreen_43->setChecked(true);
break;
case FULLSCR_SCALE_KEEPRATIO:
ui->actionFullScreen_keepRatio->setChecked(true);
break;
case FULLSCR_SCALE_INT:
ui->actionFullScreen_int->setChecked(true);
break;
}
2021-12-18 01:03:05 +06:00
actGroup = new QActionGroup(this);
actGroup->addAction(ui->actionFullScreen_stretch);
actGroup->addAction(ui->actionFullScreen_43);
actGroup->addAction(ui->actionFullScreen_keepRatio);
actGroup->addAction(ui->actionFullScreen_int);
switch (video_grayscale) {
case 0:
ui->actionRGB_Color->setChecked(true);
break;
case 1:
ui->actionRGB_Grayscale->setChecked(true);
break;
case 2:
ui->actionAmber_monitor->setChecked(true);
break;
case 3:
ui->actionGreen_monitor->setChecked(true);
break;
case 4:
ui->actionWhite_monitor->setChecked(true);
break;
}
2021-12-18 01:03:05 +06:00
actGroup = new QActionGroup(this);
actGroup->addAction(ui->actionRGB_Grayscale);
actGroup->addAction(ui->actionAmber_monitor);
actGroup->addAction(ui->actionGreen_monitor);
actGroup->addAction(ui->actionWhite_monitor);
actGroup->addAction(ui->actionRGB_Color);
switch (video_graytype) {
case 0:
ui->actionBT601_NTSC_PAL->setChecked(true);
break;
case 1:
ui->actionBT709_HDTV->setChecked(true);
break;
case 2:
ui->actionAverage->setChecked(true);
break;
}
2021-12-18 01:03:05 +06:00
actGroup = new QActionGroup(this);
actGroup->addAction(ui->actionBT601_NTSC_PAL);
actGroup->addAction(ui->actionBT709_HDTV);
actGroup->addAction(ui->actionAverage);
if (force_43 > 0) {
ui->actionForce_4_3_display_ratio->setChecked(true);
}
if (enable_overscan > 0) {
ui->actionCGA_PCjr_Tandy_EGA_S_VGA_overscan->setChecked(true);
}
if (vid_cga_contrast > 0) {
ui->actionChange_contrast_for_monochrome_display->setChecked(true);
}
#ifdef Q_OS_MACOS
ui->actionFullscreen->setShortcutVisibleInContextMenu(true);
ui->actionCtrl_Alt_Del->setShortcutVisibleInContextMenu(true);
ui->actionTake_screenshot->setShortcutVisibleInContextMenu(true);
#endif
video_setblit(qt_blit);
if (start_in_fullscreen) {
connect(ui->stackedWidget, &RendererStack::blitToRenderer, this, [this] () {
if (start_in_fullscreen) {
QTimer::singleShot(100, ui->actionFullscreen, &QAction::trigger);
start_in_fullscreen = 0;
}
});
}
2022-02-04 15:19:18 +06:00
#ifdef MTR_ENABLED
{
ui->actionBegin_trace->setVisible(true);
ui->actionEnd_trace->setVisible(true);
ui->actionBegin_trace->setShortcut(QKeySequence(Qt::Key_Control + Qt::Key_T));
ui->actionEnd_trace->setShortcut(QKeySequence(Qt::Key_Control + Qt::Key_T));
ui->actionEnd_trace->setDisabled(true);
2022-02-04 15:19:18 +06:00
static auto init_trace = [&]
{
mtr_init("trace.json");
mtr_start();
};
static auto shutdown_trace = [&]
{
mtr_stop();
mtr_shutdown();
};
#ifdef Q_OS_MACOS
ui->actionBegin_trace->setShortcutVisibleInContextMenu(true);
ui->actionEnd_trace->setShortcutVisibleInContextMenu(true);
2022-02-04 15:19:18 +06:00
#endif
static bool trace = false;
connect(ui->actionBegin_trace, &QAction::triggered, this, [this]
2022-02-04 15:19:18 +06:00
{
if (trace) return;
ui->actionBegin_trace->setDisabled(true);
ui->actionEnd_trace->setDisabled(false);
2022-02-04 15:19:18 +06:00
init_trace();
trace = true;
});
connect(ui->actionEnd_trace, &QAction::triggered, this, [this]
2022-02-04 15:19:18 +06:00
{
if (!trace) return;
ui->actionBegin_trace->setDisabled(false);
ui->actionEnd_trace->setDisabled(true);
2022-02-04 15:19:18 +06:00
shutdown_trace();
trace = false;
});
}
#endif
2022-02-09 00:47:05 +06:00
setContextMenuPolicy(Qt::PreventContextMenu);
2022-07-04 01:50:42 +06:00
connect(this, &MainWindow::initRendererMonitor, this, &MainWindow::initRendererMonitorSlot);
connect(this, &MainWindow::initRendererMonitorForNonQtThread, this, &MainWindow::initRendererMonitorSlot, Qt::BlockingQueuedConnection);
connect(this, &MainWindow::destroyRendererMonitor, this, &MainWindow::destroyRendererMonitorSlot);
connect(this, &MainWindow::destroyRendererMonitorForNonQtThread, this, &MainWindow::destroyRendererMonitorSlot, Qt::BlockingQueuedConnection);
2021-11-25 10:20:56 +01:00
}
2021-12-12 01:16:27 +06:00
void MainWindow::closeEvent(QCloseEvent *event) {
if (mouse_capture) {
event->ignore();
return;
}
if (confirm_exit && confirm_exit_cmdl && cpu_thread_run)
2021-12-12 01:16:27 +06:00
{
QMessageBox questionbox(QMessageBox::Icon::Question, "86Box", tr("Are you sure you want to exit 86Box?"), QMessageBox::Yes | QMessageBox::No, this);
QCheckBox *chkbox = new QCheckBox(tr("Don't show this message again"));
2021-12-12 01:16:27 +06:00
questionbox.setCheckBox(chkbox);
chkbox->setChecked(!confirm_exit);
2021-12-12 01:16:27 +06:00
QObject::connect(chkbox, &QCheckBox::stateChanged, [](int state) {
confirm_exit = (state == Qt::CheckState::Unchecked);
});
questionbox.exec();
if (questionbox.result() == QMessageBox::No) {
confirm_exit = true;
event->ignore();
return;
}
}
if (window_remember) {
window_w = ui->stackedWidget->width();
window_h = ui->stackedWidget->height();
if (!QApplication::platformName().contains("wayland")) {
window_x = this->geometry().x();
window_y = this->geometry().y();
}
for (int i = 1; i < MONITORS_NUM; i++) {
if (renderers[i]) {
monitor_settings[i].mon_window_w = renderers[i]->geometry().width();
monitor_settings[i].mon_window_h = renderers[i]->geometry().height();
if (!QApplication::platformName().contains("wayland")) continue;
monitor_settings[i].mon_window_x = renderers[i]->geometry().x();
monitor_settings[i].mon_window_y = renderers[i]->geometry().y();
}
}
2021-12-12 01:16:27 +06:00
}
2022-04-21 13:56:39 -03:00
if (ui->stackedWidget->mouse_exit_func)
ui->stackedWidget->mouse_exit_func();
ui->stackedWidget->switchRenderer(RendererStack::Renderer::Software);
qt_nvr_save();
config_save();
QApplication::processEvents();
cpu_thread_run = 0;
2021-12-12 01:16:27 +06:00
event->accept();
}
2022-07-04 01:50:42 +06:00
void MainWindow::initRendererMonitorSlot(int monitor_index)
{
auto& secondaryRenderer = this->renderers[monitor_index];
secondaryRenderer.reset(new RendererStack(nullptr, monitor_index));
if (secondaryRenderer) {
connect(this, &MainWindow::pollMouse, secondaryRenderer.get(), &RendererStack::mousePoll, Qt::DirectConnection);
connect(secondaryRenderer.get(), &RendererStack::rendererChanged, this, [this, monitor_index]
{
this->renderers[monitor_index]->show();
});
secondaryRenderer->setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint);
2022-07-04 01:50:42 +06:00
secondaryRenderer->setWindowTitle(QObject::tr("86Box Monitor #") + QString::number(monitor_index + 1));
2022-07-04 01:50:42 +06:00
if (vid_resize == 2) {
secondaryRenderer->setFixedSize(fixed_size_x, fixed_size_y);
}
secondaryRenderer->show();
if (window_remember) {
secondaryRenderer->setGeometry(monitor_settings[monitor_index].mon_window_x < 120 ? 120 : monitor_settings[monitor_index].mon_window_x,
monitor_settings[monitor_index].mon_window_y < 120 ? 120 : monitor_settings[monitor_index].mon_window_y,
monitor_settings[monitor_index].mon_window_w > 2048 ? 2048 : monitor_settings[monitor_index].mon_window_w,
monitor_settings[monitor_index].mon_window_h > 2048 ? 2048 : monitor_settings[monitor_index].mon_window_h);
}
2022-07-04 01:50:42 +06:00
secondaryRenderer->switchRenderer((RendererStack::Renderer)vid_api);
secondaryRenderer->setWindowIcon(this->windowIcon());
}
}
void MainWindow::destroyRendererMonitorSlot(int monitor_index)
{
if (this->renderers[monitor_index]) {
if (window_remember) {
monitor_settings[monitor_index].mon_window_w = renderers[monitor_index]->geometry().width();
monitor_settings[monitor_index].mon_window_h = renderers[monitor_index]->geometry().height();
monitor_settings[monitor_index].mon_window_x = renderers[monitor_index]->geometry().x();
monitor_settings[monitor_index].mon_window_y = renderers[monitor_index]->geometry().y();
}
config_save();
2022-07-04 01:50:42 +06:00
this->renderers[monitor_index].release()->deleteLater();
}
}
2021-11-25 10:20:56 +01:00
MainWindow::~MainWindow() {
delete ui;
}
2021-12-12 01:16:27 +06:00
void MainWindow::showEvent(QShowEvent *event) {
if (shownonce) return;
shownonce = true;
2021-12-12 01:16:27 +06:00
if (window_remember && !QApplication::platformName().contains("wayland")) {
fprintf(stderr, "Geom: %i, %i, %i, %i\n", window_x, window_y, window_w, window_h);
setGeometry(window_x, window_y, window_w, window_h + menuBar()->height() + (hide_status_bar ? 0 : statusBar()->height()) + (hide_tool_bar ? 0 : ui->toolBar->height()));
2021-12-12 01:16:27 +06:00
}
if (vid_resize == 2) {
2022-01-09 17:00:16 +02:00
setFixedSize(fixed_size_x, fixed_size_y
+ menuBar()->height()
+ (hide_status_bar ? 0 : statusBar()->height())
+ (hide_tool_bar ? 0 : ui->toolBar->height()));
2022-07-04 01:50:42 +06:00
monitors[0].mon_scrnsz_x = fixed_size_x;
monitors[0].mon_scrnsz_y = fixed_size_y;
2021-12-12 01:16:27 +06:00
}
}
2021-11-25 10:20:56 +01:00
void MainWindow::on_actionKeyboard_requires_capture_triggered() {
kbd_req_capture ^= 1;
}
void MainWindow::on_actionRight_CTRL_is_left_ALT_triggered() {
rctrl_is_lalt ^= 1;
}
void MainWindow::on_actionHard_Reset_triggered() {
if (confirm_reset)
{
QMessageBox questionbox(QMessageBox::Icon::Question, "86Box", tr("Are you sure you want to hard reset the emulated machine?"), QMessageBox::NoButton, this);
questionbox.addButton(tr("Don't reset"), QMessageBox::AcceptRole);
questionbox.addButton(tr("Reset"), QMessageBox::RejectRole);
QCheckBox *chkbox = new QCheckBox(tr("Don't show this message again"));
questionbox.setCheckBox(chkbox);
chkbox->setChecked(!confirm_reset);
QObject::connect(chkbox, &QCheckBox::stateChanged, [](int state) {
confirm_reset = (state == Qt::CheckState::Unchecked);
});
questionbox.exec();
if (questionbox.result() == QDialog::Rejected)
{
confirm_reset = true;
return;
}
}
config_changed = 2;
2021-11-25 10:20:56 +01:00
pc_reset_hard();
}
void MainWindow::on_actionCtrl_Alt_Del_triggered() {
pc_send_cad();
}
void MainWindow::on_actionCtrl_Alt_Esc_triggered() {
pc_send_cae();
}
void MainWindow::on_actionPause_triggered() {
plat_pause(dopause ^ 1);
}
void MainWindow::on_actionExit_triggered() {
close();
}
void MainWindow::on_actionSettings_triggered() {
int currentPause = dopause;
plat_pause(1);
Settings settings(this);
settings.setModal(true);
settings.setWindowModality(Qt::WindowModal);
2021-11-25 10:20:56 +01:00
settings.exec();
switch (settings.result()) {
case QDialog::Accepted:
/*
pc_reset_hard_close();
settings.save();
config_changed = 2;
pc_reset_hard_init();
*/
settings.save();
config_changed = 2;
pc_reset_hard();
break;
case QDialog::Rejected:
break;
}
plat_pause(currentPause);
2021-11-25 10:20:56 +01:00
}
#if defined(__unix__) && !defined(__HAIKU__)
std::array<uint32_t, 256> x11_to_xt_base
{
0,
0,
0,
0,
0,
0,
0,
0,
0,
0x01,
0x02,
0x03,
0x04,
0x05,
0x06,
0x07,
0x08,
0x09,
0x0A,
0x0B,
0x0C,
0x0D,
0x0E,
0x0F,
0x10,
0x11,
0x12,
0x13,
0x14,
0x15,
0x16,
0x17,
0x18,
0x19,
0x1A,
0x1B,
0x1C,
0x1D,
0x1E,
0x1F,
0x20,
0x21,
0x22,
0x23,
0x24,
0x25,
0x26,
0x27,
0x28,
0x29,
0x2A,
0x2B,
0x2C,
0x2D,
0x2E,
0x2F,
0x30,
0x31,
0x32,
0x33,
0x34,
0x35,
0x36,
0x37,
0x38,
0x39,
0x3A,
0x3B,
0x3C,
0x3D,
0x3E,
0x3F,
0x40,
0x41,
0x42,
0x43,
0x44,
0x45,
0x46,
0x47,
0x48,
0x49,
0x4A,
0x4B,
0x4C,
0x4D,
0x4E,
0x4F,
0x50,
0x51,
0x52,
0x53,
0x54,
0x55,
0x56,
0x57,
0x58,
0x147,
0x148,
0x149,
0,
0x14B,
0,
0x14D,
0x14F,
0x150,
0x151,
0x152,
0x153,
0x11C,
0x11D,
0, // Pause/Break key.
0x137,
0x135,
0x138,
0, // Ditto as above comment.
0x15B,
0x15C,
0x15D,
};
std::array<uint32_t, 256> x11_to_xt_2
{
0,
0,
0,
0,
0,
0,
0,
0,
0,
0x01,
0x02,
0x03,
0x04,
0x05,
0x06,
0x07,
0x08,
0x09,
0x0A,
0x0B,
0x0C,
0x0D,
0x0E,
0x0F,
0x10,
0x11,
0x12,
0x13,
0x14,
0x15,
0x16,
0x17,
0x18,
0x19,
0x1A,
0x1B,
0x1C,
0x1D,
0x1E,
0x1F,
0x20,
0x21,
0x22,
0x23,
0x24,
0x25,
0x26,
0x27,
0x28,
0x29,
0x2A,
0x2B,
0x2C,
0x2D,
0x2E,
0x2F,
0x30,
0x31,
0x32,
0x33,
0x34,
0x35,
0x36,
0x37,
0x38,
0x39,
0x3A,
0x3B,
0x3C,
0x3D,
0x3E,
0x3F,
0x40,
0x41,
0x42,
0x43,
0x44,
0x45,
0x46,
0x47,
0x48,
0x49,
0x4A,
0x4B,
0x4C,
0x4D,
0x4E,
0x4F,
0x50,
0x51,
0x52,
0x53,
0x54,
0x55,
0x56,
0x57,
0x58,
0,
0,
0,
0,
0,
0,
0,
0x11C,
0x11D,
0x135,
0x137,
0x138,
0,
0x147,
0x148,
0x149,
0x14B,
0x14D,
0x14F,
0x150,
0x151,
0x152,
0x153
};
std::array<uint32_t, 256> x11_to_xt_vnc
{
0,
0,
0,
0,
0,
0,
0,
0,
0x1D,
0x11D,
0x2A,
0x36,
0,
0,
0x38,
0x138,
0x39,
0x0B,
0x02,
0x03,
0x04,
0x05,
0x06,
0x07,
0x08,
0x09,
0x0A,
0x0C,
0x0D,
0x1A,
0x1B,
0x27,
0x28,
0x29,
0x33,
0x34,
0x35,
0x2B,
0x1E,
0x30,
0x2E,
0x20,
0x12,
0x21,
0x22,
0x23,
0x17,
0x24,
0x25,
0x26,
0x32,
0x31,
0x18,
0x19,
0x10,
0x13,
0x1F,
0x14,
0x16,
0x2F,
0x11,
0x2D,
0x15,
0x2C,
0x0E,
0x1C,
0x0F,
0x01,
0x153,
0x147,
0x14F,
0x149,
0x151,
0x148,
0x150,
0x14B,
0x14D,
};
#endif
#ifdef Q_OS_MACOS
std::array<uint32_t, 256> darwin_to_xt
{
0x1E,
0x1F,
0x20,
0x21,
0x23,
0x22,
0x2C,
0x2D,
0x2E,
0x2F,
0x2B,
0x30,
0x10,
0x11,
0x12,
0x13,
0x15,
0x14,
0x02,
0x03,
0x04,
0x05,
0x07,
0x06,
0x0D,
0x0A,
0x08,
0x0C,
0x09,
0x0B,
0x1B,
0x18,
0x16,
0x1A,
0x17,
0x19,
0x1C,
0x26,
0x24,
0x28,
0x25,
0x27,
0x2B,
0x33,
0x35,
0x31,
0x32,
0x34,
0x0F,
0x39,
0x29,
0x0E,
0x11C,
0x01,
0x15C,
0x15B,
0x2A,
0x3A,
0x38,
0x1D,
0x36,
0x138,
0x11D,
0x15C,
0,
0x53,
0,
0x37,
0,
0x4E,
0,
0x45,
0x130,
0x12E,
0x120,
0x135,
0x11C,
0,
0x4A,
0,
0,
0,
0x52,
0x4F,
0x50,
0x51,
0x4B,
0x4C,
0x4D,
0x47,
0,
0x48,
0x49,
0,
0,
0,
0x3F,
0x40,
0x41,
0x3D,
0x42,
0x43,
0,
0x57,
0,
0x137,
0,
0x46,
0,
0x44,
0x15D,
0x58,
0,
0, // Pause/Break key.
0x152,
0x147,
0x149,
0x153,
0x3E,
0x14F,
0x3C,
0x151,
0x3B,
0x14B,
0x14D,
0x150,
0x148,
0,
2021-11-25 10:20:56 +01:00
};
#endif
2021-11-25 10:20:56 +01:00
#if defined(__unix__) && !defined(__HAIKU__)
static std::unordered_map<uint32_t, uint16_t> evdev_to_xt =
{
{96, 0x11C},
{97, 0x11D},
{98, 0x135},
{99, 0x71},
{100, 0x138},
{101, 0x1C},
{102, 0x147},
{103, 0x148},
{104, 0x149},
{105, 0x14B},
{106, 0x14D},
{107, 0x14F},
{108, 0x150},
{109, 0x151},
{110, 0x152},
{111, 0x153}
};
#endif
#ifdef __HAIKU__
static std::unordered_map<uint8_t, uint16_t> be_to_xt =
{
{0x01, 0x01},
{B_F1_KEY, 0x3B},
{B_F2_KEY, 0x3C},
{B_F3_KEY, 0x3D},
{B_F4_KEY, 0x3E},
{B_F5_KEY, 0x3F},
{B_F6_KEY, 0x40},
{B_F7_KEY, 0x41},
{B_F8_KEY, 0x42},
{B_F9_KEY, 0x43},
{B_F10_KEY, 0x44},
{B_F11_KEY, 0x57},
{B_F12_KEY, 0x58},
{0x11, 0x29},
{0x12, 0x02},
{0x13, 0x03},
{0x14, 0x04},
{0x15, 0x05},
{0x16, 0x06},
{0x17, 0x07},
{0x18, 0x08},
{0x19, 0x09},
{0x1A, 0x0A},
{0x1B, 0x0B},
{0x1C, 0x0C},
{0x1D, 0x0D},
{0x1E, 0x0E},
{0x1F, 0x152},
{0x20, 0x147},
{0x21, 0x149},
{0x22, 0x45},
{0x23, 0x135},
{0x24, 0x37},
{0x25, 0x4A},
{0x26, 0x0F},
{0x27, 0x10},
{0x28, 0x11},
{0x29, 0x12},
{0x2A, 0x13},
{0x2B, 0x14},
{0x2C, 0x15},
{0x2D, 0x16},
{0x2E, 0x17},
{0x2F, 0x18},
{0x30, 0x19},
{0x31, 0x1A},
{0x32, 0x1B},
{0x33, 0x2B},
{0x34, 0x153},
{0x35, 0x14F},
{0x36, 0x151},
{0x37, 0x47},
{0x38, 0x48},
{0x39, 0x49},
{0x3A, 0x4E},
{0x3B, 0x3A},
{0x3C, 0x1E},
{0x3D, 0x1F},
{0x3E, 0x20},
{0x3F, 0x21},
{0x40, 0x22},
{0x41, 0x23},
{0x42, 0x24},
{0x43, 0x25},
{0x44, 0x26},
{0x45, 0x27},
{0x46, 0x28},
{0x47, 0x1C},
{0x48, 0x4B},
{0x49, 0x4C},
{0x4A, 0x4D},
{0x4B, 0x2A},
{0x4C, 0x2C},
{0x4D, 0x2D},
{0x4E, 0x2E},
{0x4F, 0x2F},
{0x50, 0x30},
{0x51, 0x31},
{0x52, 0x32},
{0x53, 0x33},
{0x54, 0x34},
{0x55, 0x35},
{0x56, 0x36},
{0x57, 0x148},
{0x58, 0x51},
{0x59, 0x50},
{0x5A, 0x4F},
{0x5B, 0x11C},
{0x5C, 0x1D},
{0x5D, 0x38},
{0x5E, 0x39},
{0x5F, 0x138},
{0x60, 0x11D},
{0x61, 0x14B},
{0x62, 0x150},
{0x63, 0x14D},
{0x64, 0x52},
{0x65, 0x53},
{0x0e, 0x137},
{0x0f, 0x46},
{0x66, 0x15B},
{0x67, 0x15C},
{0x68, 0x15D},
{0x69, 0x56}
};
#endif
#if defined(__unix__) && !defined(__HAIKU__)
static std::array<uint32_t, 256>& selected_keycode = x11_to_xt_base;
#endif
uint16_t x11_keycode_to_keysym(uint32_t keycode)
{
uint16_t finalkeycode = 0;
2021-12-02 19:35:08 +02:00
#if defined(Q_OS_WINDOWS)
finalkeycode = (keycode & 0xFFFF);
#elif defined(Q_OS_MACOS)
finalkeycode = darwin_to_xt[keycode];
#elif defined(__HAIKU__)
finalkeycode = be_to_xt[keycode];
#else
static Display* x11display = nullptr;
if (QApplication::platformName().contains("wayland"))
{
selected_keycode = x11_to_xt_2;
}
else if (QApplication::platformName().contains("eglfs"))
{
keycode -= 8;
if (keycode <= 88) finalkeycode = keycode;
else finalkeycode = evdev_to_xt[keycode];
}
else if (!x11display)
{
x11display = XOpenDisplay(nullptr);
if (XKeysymToKeycode(x11display, XK_Home) == 110)
{
selected_keycode = x11_to_xt_2;
}
else if (XKeysymToKeycode(x11display, XK_Home) == 69)
{
selected_keycode = x11_to_xt_vnc;
}
}
if (!QApplication::platformName().contains("eglfs")) finalkeycode = selected_keycode[keycode];
#endif
if (rctrl_is_lalt && finalkeycode == 0x11D)
{
finalkeycode = 0x38;
}
return finalkeycode;
}
2021-11-25 10:20:56 +01:00
#ifdef Q_OS_MACOS
// These modifiers are listed as "device-dependent" in IOLLEvent.h, but
// that's followed up with "(really?)". It's the only way to distinguish
// left and right modifiers with Qt 6 on macOS, so let's just roll with it.
static std::unordered_map<uint32_t, uint16_t> mac_modifiers_to_xt = {
{NX_DEVICELCTLKEYMASK, 0x1D},
{NX_DEVICELSHIFTKEYMASK, 0x2A},
{NX_DEVICERSHIFTKEYMASK, 0x36},
{NX_DEVICELCMDKEYMASK, 0x15B},
{NX_DEVICERCMDKEYMASK, 0x15C},
{NX_DEVICELALTKEYMASK, 0x38},
{NX_DEVICERALTKEYMASK, 0x138},
{NX_DEVICE_ALPHASHIFT_STATELESS_MASK, 0x3A},
{NX_DEVICERCTLKEYMASK, 0x11D},
};
void MainWindow::processMacKeyboardInput(bool down, const QKeyEvent* event) {
// Per QTBUG-69608 (https://bugreports.qt.io/browse/QTBUG-69608),
// QKeyEvents QKeyEvents for presses/releases of modifiers on macOS give
// nativeVirtualKey() == 0 (at least in Qt 6). Handle this by manually
// processing the nativeModifiers(). We need to check whether the key() is
// a known modifier because because kVK_ANSI_A is also 0, so the
// nativeVirtualKey() == 0 condition is ambiguous...
if (event->nativeVirtualKey() == 0
&& (event->key() == Qt::Key_Shift
|| event->key() == Qt::Key_Control
|| event->key() == Qt::Key_Meta
|| event->key() == Qt::Key_Alt
|| event->key() == Qt::Key_AltGr
|| event->key() == Qt::Key_CapsLock)) {
// We only process one modifier at a time since events from Qt seem to
// always be non-coalesced (NX_NONCOALESCEDMASK is always set).
uint32_t changed_modifiers = last_modifiers ^ event->nativeModifiers();
for (auto const& pair : mac_modifiers_to_xt) {
if (changed_modifiers & pair.first) {
last_modifiers ^= pair.first;
keyboard_input(down, pair.second);
return;
}
}
// Caps Lock seems to be delivered as a single key press event when
// enabled and a single key release event when disabled, so we can't
// detect Caps Lock being held down; just send an infinitesimally-long
// press and release as a compromise.
//
// The event also doesn't get delivered if you turn Caps Lock off after
// turning it on when the window isn't focused. Doing better than this
// probably requires bypassing Qt input processing.
//
// It's possible that other lock keys get delivered in this way, but
// standard Apple keyboards don't have them, so this is untested.
if (event->key() == Qt::Key_CapsLock) {
keyboard_input(1, 0x3A);
keyboard_input(0, 0x3A);
}
} else {
keyboard_input(down, x11_keycode_to_keysym(event->nativeVirtualKey()));
}
}
#endif
2021-11-25 10:20:56 +01:00
void MainWindow::on_actionFullscreen_triggered() {
2021-11-30 21:26:51 +01:00
if (video_fullscreen > 0) {
showNormal();
2022-06-22 16:36:38 +06:00
if (vid_api == 5) ui->stackedWidget->switchRenderer(RendererStack::Renderer::Direct3D9);
ui->menubar->show();
if (!hide_status_bar) ui->statusbar->show();
2022-01-09 17:00:16 +02:00
if (!hide_tool_bar) ui->toolBar->show();
2021-11-30 21:26:51 +01:00
video_fullscreen = 0;
if (vid_resize != 1) {
2022-01-09 17:00:16 +02:00
if (vid_resize == 2) setFixedSize(fixed_size_x, fixed_size_y
+ menuBar()->height()
+ (!hide_status_bar ? statusBar()->height() : 0)
+ (!hide_tool_bar ? ui->toolBar->height() : 0));
2022-07-04 01:50:42 +06:00
emit resizeContents(monitors[0].mon_scrnsz_x, monitors[0].mon_scrnsz_y);
}
2021-11-30 21:26:51 +01:00
} else {
if (video_fullscreen_first)
{
bool wasCaptured = mouse_capture == 1;
QMessageBox questionbox(QMessageBox::Icon::Information, tr("Entering fullscreen mode"), tr("Press Ctrl+Alt+PgDn to return to windowed mode."), QMessageBox::Ok, this);
QCheckBox *chkbox = new QCheckBox(tr("Don't show this message again"));
questionbox.setCheckBox(chkbox);
chkbox->setChecked(!video_fullscreen_first);
QObject::connect(chkbox, &QCheckBox::stateChanged, [](int state) {
video_fullscreen_first = (state == Qt::CheckState::Unchecked);
});
questionbox.exec();
config_save();
/* (re-capture mouse after dialog. */
if (wasCaptured)
emit setMouseCapture(true);
}
video_fullscreen = 1;
setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
ui->menubar->hide();
ui->statusbar->hide();
2022-01-09 17:00:16 +02:00
ui->toolBar->hide();
2021-11-30 21:26:51 +01:00
showFullScreen();
}
ui->stackedWidget->onResize(width(), height());
2021-11-25 10:20:56 +01:00
}
2021-12-01 15:55:41 +06:00
void MainWindow::getTitle_(wchar_t *title)
{
this->windowTitle().toWCharArray(title);
}
void MainWindow::getTitle(wchar_t *title)
{
if (QThread::currentThread() == this->thread()) {
getTitle_(title);
} else {
emit getTitleForNonQtThread(title);
}
}
2021-12-05 15:57:46 +06:00
bool MainWindow::eventFilter(QObject* receiver, QEvent* event)
{
if (!dopause && (mouse_capture || !kbd_req_capture)) {
if (event->type() == QEvent::Shortcut) {
auto shortcutEvent = (QShortcutEvent*)event;
if (shortcutEvent->key() == ui->actionExit->shortcut()) {
event->accept();
return true;
}
}
2021-12-05 15:57:46 +06:00
if (event->type() == QEvent::KeyPress) {
event->accept();
this->keyPressEvent((QKeyEvent *) event);
2021-12-05 15:57:46 +06:00
return true;
}
if (event->type() == QEvent::KeyRelease) {
event->accept();
this->keyReleaseEvent((QKeyEvent *) event);
2021-12-05 15:57:46 +06:00
return true;
}
}
2021-12-06 21:26:17 +01:00
if (receiver == this) {
2022-02-04 15:19:18 +06:00
static auto curdopause = dopause;
if (event->type() == QEvent::WindowBlocked) {
curdopause = dopause;
plat_pause(1);
emit setMouseCapture(false);
} else if (event->type() == QEvent::WindowUnblocked) {
plat_pause(curdopause);
}
2022-02-04 15:19:18 +06:00
}
2021-12-05 15:57:46 +06:00
return QMainWindow::eventFilter(receiver, event);
}
void MainWindow::refreshMediaMenu() {
mm->refresh(ui->menuMedia);
status->refresh(ui->statusbar);
ui->actionMCA_devices->setVisible(machine_has_bus(machine, MACHINE_BUS_MCA));
}
void MainWindow::showMessage(const QString& header, const QString& message) {
if (QThread::currentThread() == this->thread()) {
showMessage_(header, message);
} else {
emit showMessageForNonQtThread(header, message);
}
}
void MainWindow::showMessage_(const QString &header, const QString &message) {
QMessageBox box(QMessageBox::Warning, header, message, QMessageBox::NoButton, this);
box.setTextFormat(Qt::TextFormat::RichText);
box.exec();
}
void MainWindow::keyPressEvent(QKeyEvent* event)
{
if (send_keyboard_input && !(kbd_req_capture && !mouse_capture && !video_fullscreen))
{
// Windows keys in Qt have one-to-one mapping.
if (event->key() == Qt::Key_Super_L || event->key() == Qt::Key_Super_R) {
keyboard_input(1, event->key() == Qt::Key_Super_L ? 0x15B : 0x15C);
} else
#ifdef Q_OS_MACOS
processMacKeyboardInput(true, event);
#else
keyboard_input(1, x11_keycode_to_keysym(event->nativeScanCode()));
#endif
}
if ((video_fullscreen > 0) && keyboard_isfsexit()) {
ui->actionFullscreen->trigger();
}
if (keyboard_ismsexit()) {
plat_mouse_capture(0);
}
2021-12-05 15:57:46 +06:00
event->accept();
}
2022-07-04 01:50:42 +06:00
void MainWindow::blitToWidget(int x, int y, int w, int h, int monitor_index)
{
2022-07-04 01:50:42 +06:00
if (monitor_index >= 1) {
if (renderers[monitor_index]) renderers[monitor_index]->blit(x, y, w, h);
else video_blit_complete_monitor(monitor_index);
}
else ui->stackedWidget->blit(x, y, w, h);
}
void MainWindow::keyReleaseEvent(QKeyEvent* event)
{
if (!send_keyboard_input)
return;
if (event->key() == Qt::Key_Super_L || event->key() == Qt::Key_Super_R) {
keyboard_input(0, event->key() == Qt::Key_Super_L ? 0x15B : 0x15C);
} else
#ifdef Q_OS_MACOS
processMacKeyboardInput(false, event);
#else
keyboard_input(0, x11_keycode_to_keysym(event->nativeScanCode()));
#endif
}
2021-12-12 01:16:27 +06:00
QSize MainWindow::getRenderWidgetSize()
{
return ui->stackedWidget->size();
}
2021-12-05 15:57:46 +06:00
void MainWindow::focusInEvent(QFocusEvent* event)
{
2021-12-31 16:47:49 +06:00
this->grabKeyboard();
2021-12-05 15:57:46 +06:00
}
void MainWindow::focusOutEvent(QFocusEvent* event)
{
this->releaseKeyboard();
}
2021-12-06 21:26:17 +01:00
void MainWindow::on_actionResizable_window_triggered(bool checked) {
if (checked) {
vid_resize = 1;
setWindowFlag(Qt::WindowMaximizeButtonHint);
setWindowFlag(Qt::MSWindowsFixedSizeDialogHint, false);
2021-12-06 21:26:17 +01:00
setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
2022-07-05 00:21:30 +06:00
for (int i = 1; i < MONITORS_NUM; i++) {
if (monitors[i].target_buffer) {
renderers[i]->setWindowFlag(Qt::WindowMaximizeButtonHint);
renderers[i]->setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
}
}
2021-12-06 21:26:17 +01:00
} else {
vid_resize = 0;
setWindowFlag(Qt::WindowMaximizeButtonHint, false);
setWindowFlag(Qt::MSWindowsFixedSizeDialogHint);
2022-07-05 00:21:30 +06:00
for (int i = 1; i < MONITORS_NUM; i++) {
if (monitors[i].target_buffer) {
renderers[i]->setWindowFlag(Qt::WindowMaximizeButtonHint, false);
2022-07-05 00:21:30 +06:00
emit resizeContentsMonitor(monitors[i].mon_scrnsz_x, monitors[i].mon_scrnsz_y, i);
}
}
2021-12-06 21:26:17 +01:00
}
show();
2021-12-06 21:58:41 +01:00
ui->menuWindow_scale_factor->setEnabled(! checked);
2022-07-04 01:50:42 +06:00
emit resizeContents(monitors[0].mon_scrnsz_x, monitors[0].mon_scrnsz_y);
2022-07-05 00:21:30 +06:00
ui->stackedWidget->switchRenderer((RendererStack::Renderer)vid_api);
for (int i = 1; i < MONITORS_NUM; i++) {
if (monitors[i].target_buffer) {
renderers[i]->show();
renderers[i]->switchRenderer((RendererStack::Renderer)vid_api);
QApplication::processEvents();
}
}
2021-12-06 21:26:17 +01:00
}
2021-12-06 21:45:40 +01:00
static void
video_toggle_option(QAction* action, int *val)
{
startblit();
*val ^= 1;
video_copy = (video_grayscale || invert_display) ? video_transform_copy : memcpy;
2021-12-06 21:45:40 +01:00
action->setChecked(*val > 0 ? true : false);
endblit();
config_save();
device_force_redraw();
2022-07-05 00:21:30 +06:00
for (int i = 0; i < MONITORS_NUM; i++) {
if (monitors[i].target_buffer) video_force_resize_set_monitor(1, i);
}
2021-12-06 21:45:40 +01:00
}
void MainWindow::on_actionInverted_VGA_monitor_triggered() {
video_toggle_option(ui->actionInverted_VGA_monitor, &invert_display);
}
2021-12-06 21:58:41 +01:00
static void update_scaled_checkboxes(Ui::MainWindow* ui, QAction* selected) {
ui->action0_5x->setChecked(ui->action0_5x == selected);
ui->action1x->setChecked(ui->action1x == selected);
ui->action1_5x->setChecked(ui->action1_5x == selected);
ui->action2x->setChecked(ui->action2x == selected);
reset_screen_size();
device_force_redraw();
2022-07-05 00:21:30 +06:00
for (int i = 0; i < MONITORS_NUM; i++) {
if (monitors[i].target_buffer) video_force_resize_set_monitor(1, i);
}
2021-12-06 21:58:41 +01:00
config_save();
}
void MainWindow::on_action0_5x_triggered() {
scale = 0;
update_scaled_checkboxes(ui, ui->action0_5x);
}
void MainWindow::on_action1x_triggered() {
scale = 1;
update_scaled_checkboxes(ui, ui->action1x);
}
void MainWindow::on_action1_5x_triggered() {
scale = 2;
update_scaled_checkboxes(ui, ui->action1_5x);
}
void MainWindow::on_action2x_triggered() {
scale = 3;
update_scaled_checkboxes(ui, ui->action2x);
}
2021-12-06 22:02:18 +01:00
void MainWindow::on_actionNearest_triggered() {
video_filter_method = 0;
ui->actionLinear->setChecked(false);
}
void MainWindow::on_actionLinear_triggered() {
video_filter_method = 1;
ui->actionNearest->setChecked(false);
}
2021-12-07 13:47:42 +01:00
static void update_fullscreen_scale_checkboxes(Ui::MainWindow* ui, QAction* selected) {
ui->actionFullScreen_stretch->setChecked(ui->actionFullScreen_stretch == selected);
ui->actionFullScreen_43->setChecked(ui->actionFullScreen_43 == selected);
ui->actionFullScreen_keepRatio->setChecked(ui->actionFullScreen_keepRatio == selected);
ui->actionFullScreen_int->setChecked(ui->actionFullScreen_int == selected);
if (video_fullscreen > 0) {
auto widget = ui->stackedWidget->currentWidget();
ui->stackedWidget->onResize(widget->width(), widget->height());
2021-12-07 13:47:42 +01:00
}
device_force_redraw();
config_save();
}
void MainWindow::on_actionFullScreen_stretch_triggered() {
video_fullscreen_scale = FULLSCR_SCALE_FULL;
update_fullscreen_scale_checkboxes(ui, ui->actionFullScreen_stretch);
}
void MainWindow::on_actionFullScreen_43_triggered() {
video_fullscreen_scale = FULLSCR_SCALE_43;
update_fullscreen_scale_checkboxes(ui, ui->actionFullScreen_43);
}
void MainWindow::on_actionFullScreen_keepRatio_triggered() {
video_fullscreen_scale = FULLSCR_SCALE_KEEPRATIO;
update_fullscreen_scale_checkboxes(ui, ui->actionFullScreen_keepRatio);
}
void MainWindow::on_actionFullScreen_int_triggered() {
video_fullscreen_scale = FULLSCR_SCALE_INT;
update_fullscreen_scale_checkboxes(ui, ui->actionFullScreen_int);
}
static void update_greyscale_checkboxes(Ui::MainWindow* ui, QAction* selected, int value) {
ui->actionRGB_Color->setChecked(ui->actionRGB_Color == selected);
ui->actionRGB_Grayscale->setChecked(ui->actionRGB_Grayscale == selected);
ui->actionAmber_monitor->setChecked(ui->actionAmber_monitor == selected);
ui->actionGreen_monitor->setChecked(ui->actionGreen_monitor == selected);
ui->actionWhite_monitor->setChecked(ui->actionWhite_monitor == selected);
startblit();
video_grayscale = value;
video_copy = (video_grayscale || invert_display) ? video_transform_copy : memcpy;
endblit();
device_force_redraw();
config_save();
}
void MainWindow::on_actionRGB_Color_triggered() {
update_greyscale_checkboxes(ui, ui->actionRGB_Color, 0);
}
void MainWindow::on_actionRGB_Grayscale_triggered() {
update_greyscale_checkboxes(ui, ui->actionRGB_Grayscale, 1);
}
void MainWindow::on_actionAmber_monitor_triggered() {
update_greyscale_checkboxes(ui, ui->actionAmber_monitor, 2);
}
void MainWindow::on_actionGreen_monitor_triggered() {
update_greyscale_checkboxes(ui, ui->actionGreen_monitor, 3);
}
void MainWindow::on_actionWhite_monitor_triggered() {
update_greyscale_checkboxes(ui, ui->actionWhite_monitor, 4);
}
static void update_greyscale_type_checkboxes(Ui::MainWindow* ui, QAction* selected, int value) {
ui->actionBT601_NTSC_PAL->setChecked(ui->actionBT601_NTSC_PAL == selected);
ui->actionBT709_HDTV->setChecked(ui->actionBT709_HDTV == selected);
ui->actionAverage->setChecked(ui->actionAverage == selected);
video_graytype = value;
device_force_redraw();
config_save();
}
void MainWindow::on_actionBT601_NTSC_PAL_triggered() {
update_greyscale_type_checkboxes(ui, ui->actionBT601_NTSC_PAL, 0);
}
void MainWindow::on_actionBT709_HDTV_triggered() {
update_greyscale_type_checkboxes(ui, ui->actionBT709_HDTV, 1);
}
void MainWindow::on_actionAverage_triggered() {
update_greyscale_type_checkboxes(ui, ui->actionAverage, 2);
}
2021-12-09 16:27:53 +06:00
void MainWindow::on_actionAbout_Qt_triggered()
{
QApplication::aboutQt();
}
void MainWindow::on_actionAbout_86Box_triggered()
{
QMessageBox msgBox;
msgBox.setTextFormat(Qt::RichText);
QString githash;
#ifdef EMU_GIT_HASH
githash = QString(" [%1]").arg(EMU_GIT_HASH);
#endif
msgBox.setText(QString("<b>%3%1%2</b>").arg(EMU_VERSION_FULL, githash, tr("86Box v")));
msgBox.setInformativeText(tr("An emulator of old computers\n\nAuthors: Sarah Walker, Miran Grca, Fred N. van Kempen (waltje), SA1988, Tiseno100, reenigne, leilei, JohnElliott, greatpsycho, and others.\n\nReleased under the GNU General Public License version 2 or later. See LICENSE for more information."));
2021-12-09 16:27:53 +06:00
msgBox.setWindowTitle("About 86Box");
msgBox.addButton("OK", QMessageBox::ButtonRole::AcceptRole);
2022-02-15 20:44:13 -03:00
auto webSiteButton = msgBox.addButton(EMU_SITE, QMessageBox::ButtonRole::HelpRole);
2021-12-09 16:27:53 +06:00
webSiteButton->connect(webSiteButton, &QPushButton::released, []()
{
2022-02-15 20:44:13 -03:00
QDesktopServices::openUrl(QUrl("https://" EMU_SITE));
2021-12-09 16:27:53 +06:00
});
2022-02-07 12:30:56 +06:00
#ifdef RELEASE_BUILD
msgBox.setIconPixmap(QIcon(":/settings/win/icons/86Box-green.ico").pixmap(32, 32));
#elif defined ALPHA_BUILD
2022-02-07 20:31:56 +06:00
msgBox.setIconPixmap(QIcon(":/settings/win/icons/86Box-red.ico").pixmap(32, 32));
2022-02-07 12:30:56 +06:00
#elif defined BETA_BUILD
2021-12-09 16:27:53 +06:00
msgBox.setIconPixmap(QIcon(":/settings/win/icons/86Box-yellow.ico").pixmap(32, 32));
2022-02-07 12:30:56 +06:00
#else
msgBox.setIconPixmap(QIcon(":/settings/win/icons/86Box-gray.ico").pixmap(32, 32));
#endif
2022-02-06 01:50:30 +06:00
msgBox.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint);
2021-12-09 16:27:53 +06:00
msgBox.exec();
}
void MainWindow::on_actionDocumentation_triggered()
{
2022-02-15 20:44:13 -03:00
QDesktopServices::openUrl(QUrl(EMU_DOCS_URL));
2021-12-09 16:27:53 +06:00
}
2021-12-09 19:36:35 +01:00
void MainWindow::on_actionCGA_PCjr_Tandy_EGA_S_VGA_overscan_triggered() {
update_overscan = 1;
video_toggle_option(ui->actionCGA_PCjr_Tandy_EGA_S_VGA_overscan, &enable_overscan);
}
void MainWindow::on_actionChange_contrast_for_monochrome_display_triggered() {
vid_cga_contrast ^= 1;
cgapal_rebuild();
config_save();
}
void MainWindow::on_actionForce_4_3_display_ratio_triggered() {
video_toggle_option(ui->actionForce_4_3_display_ratio, &force_43);
}
2021-12-12 01:16:27 +06:00
void MainWindow::on_actionRemember_size_and_position_triggered()
{
window_remember ^= 1;
window_w = ui->stackedWidget->width();
window_h = ui->stackedWidget->height();
if (!QApplication::platformName().contains("wayland")) {
window_x = geometry().x();
window_y = geometry().y();
}
for (int i = 1; i < MONITORS_NUM; i++) {
if (window_remember && renderers[i]) {
monitor_settings[i].mon_window_w = renderers[i]->geometry().width();
monitor_settings[i].mon_window_h = renderers[i]->geometry().height();
monitor_settings[i].mon_window_x = renderers[i]->geometry().x();
monitor_settings[i].mon_window_y = renderers[i]->geometry().y();
}
}
2021-12-12 01:16:27 +06:00
ui->actionRemember_size_and_position->setChecked(window_remember);
}
void MainWindow::on_actionSpecify_dimensions_triggered()
{
SpecifyDimensions dialog(this);
dialog.setWindowModality(Qt::WindowModal);
dialog.exec();
}
void MainWindow::on_actionHiDPI_scaling_triggered()
{
dpi_scale ^= 1;
ui->actionHiDPI_scaling->setChecked(dpi_scale);
2022-07-04 01:50:42 +06:00
emit resizeContents(monitors[0].mon_scrnsz_x, monitors[0].mon_scrnsz_y);
2021-12-12 01:16:27 +06:00
}
void MainWindow::on_actionHide_status_bar_triggered()
{
hide_status_bar ^= 1;
ui->actionHide_status_bar->setChecked(hide_status_bar);
statusBar()->setVisible(!hide_status_bar);
2022-01-09 17:00:16 +02:00
if (vid_resize >= 2) {
setFixedSize(fixed_size_x, fixed_size_y
+ menuBar()->height()
+ (hide_status_bar ? 0 : statusBar()->height())
+ (hide_tool_bar ? 0 : ui->toolBar->height()));
} else {
int vid_resize_orig = vid_resize;
vid_resize = 0;
2022-07-04 01:50:42 +06:00
emit resizeContents(monitors[0].mon_scrnsz_x, monitors[0].mon_scrnsz_y);
2022-01-09 17:00:16 +02:00
vid_resize = vid_resize_orig;
}
}
void MainWindow::on_actionHide_tool_bar_triggered()
{
hide_tool_bar ^= 1;
ui->actionHide_tool_bar->setChecked(hide_tool_bar);
ui->toolBar->setVisible(!hide_tool_bar);
if (vid_resize >= 2) {
setFixedSize(fixed_size_x, fixed_size_y
+ menuBar()->height()
+ (hide_status_bar ? 0 : statusBar()->height())
+ (hide_tool_bar ? 0 : ui->toolBar->height()));
} else {
2021-12-12 01:16:27 +06:00
int vid_resize_orig = vid_resize;
vid_resize = 0;
2022-07-04 01:50:42 +06:00
emit resizeContents(monitors[0].mon_scrnsz_x, monitors[0].mon_scrnsz_y);
2021-12-12 01:16:27 +06:00
vid_resize = vid_resize_orig;
}
}
void MainWindow::on_actionUpdate_status_bar_icons_triggered()
{
update_icons ^= 1;
ui->actionUpdate_status_bar_icons->setChecked(update_icons);
}
void MainWindow::on_actionTake_screenshot_triggered()
{
startblit();
screenshots++;
endblit();
device_force_redraw();
}
2021-12-12 16:22:35 +06:00
void MainWindow::on_actionSound_gain_triggered()
{
SoundGain gain(this);
gain.exec();
}
void MainWindow::setSendKeyboardInput(bool enabled)
{
send_keyboard_input = enabled;
2021-12-18 01:03:05 +06:00
}
2021-12-27 16:32:03 +06:00
void MainWindow::on_actionPreferences_triggered()
{
ProgSettings progsettings(this);
progsettings.exec();
}
void MainWindow::on_actionEnable_Discord_integration_triggered(bool checked)
{
enable_discord = checked;
if(enable_discord) {
discord_init();
discord_update_activity(dopause);
} else
discord_close();
}
void MainWindow::showSettings()
{
if (findChild<Settings*>() == nullptr)
ui->actionSettings->trigger();
}
void MainWindow::hardReset()
{
ui->actionHard_Reset->trigger();
}
void MainWindow::togglePause()
{
ui->actionPause->trigger();
}
void MainWindow::changeEvent(QEvent* event)
{
2022-01-27 21:26:30 +02:00
#ifdef Q_OS_WINDOWS
if (event->type() == QEvent::LanguageChange)
{
auto font_name = tr("FONT_NAME");
auto font_size = tr("FONT_SIZE");
QApplication::setFont(QFont(font_name, font_size.toInt()));
}
2022-01-27 21:26:30 +02:00
#endif
QWidget::changeEvent(event);
}
void MainWindow::on_actionRenderer_options_triggered()
{
auto dlg = ui->stackedWidget->getOptions(this);
if (dlg)
dlg->exec();
}
2022-05-23 17:29:45 +06:00
void MainWindow::on_actionMCA_devices_triggered()
{
auto dlg = new MCADeviceList(this);
if (dlg)
dlg->exec();
}