diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt index 65ae7c643..65a4a0cbf 100644 --- a/src/qt/CMakeLists.txt +++ b/src/qt/CMakeLists.txt @@ -4,14 +4,12 @@ set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) -find_package(PkgConfig) find_package(Threads REQUIRED) add_library(plat STATIC qt.c qt_main.cpp qt_platform.cpp cpp11_thread.cpp) add_library(ui STATIC qt_ui.cpp qt_cdrom.c - qt_sdl.c qt_mainwindow.cpp qt_mainwindow.hpp @@ -23,6 +21,8 @@ add_library(ui STATIC qt_rendererstack.cpp qt_rendererstack.hpp qt_rendererstack.ui + qt_renderercomon.cpp + qt_renderercomon.hpp qt_softwarerenderer.cpp qt_softwarerenderer.hpp qt_hardwarerenderer.cpp @@ -114,20 +114,6 @@ else() qt_import_plugins(plat INCLUDE Qt5::QICOPlugin) endif() -if (PkgConfig_FOUND) - pkg_check_modules(RTMIDI rtmidi) - if (RTMIDI_FOUND) - target_include_directories(plat PRIVATE ${RTMIDI_INCLUDE_DIRS}) - target_link_directories(plat INTERFACE ${RTMIDI_LIBRARY_DIRS}) - target_link_libraries(plat PRIVATE ${RTMIDI_LIBRARIES}) - target_sources(plat PRIVATE rtmidi_midi.cpp) - else() - target_sources(plat PRIVATE qt_midi.cpp) - endif() -else() - target_sources(plat PRIVATE qt_midi.cpp) -endif() - if (UNIX AND NOT APPLE) find_package(X11 REQUIRED) target_link_libraries(ui PRIVATE X11::X11) diff --git a/src/qt/qt.c b/src/qt/qt.c index 2ebab6f69..5aca045dc 100644 --- a/src/qt/qt.c +++ b/src/qt/qt.c @@ -3,6 +3,7 @@ * implemented in Qt */ +#include #include #include @@ -12,8 +13,6 @@ #include <86box/timer.h> #include <86box/nvr.h> -#include "qt_sdl.h" - int qt_nvr_save(void) { return nvr_save(); } @@ -62,9 +61,36 @@ wchar_t* plat_get_string(int i) int plat_vidapi(char* api) { + if (!strcasecmp(api, "default") || !strcasecmp(api, "system")) { + return 0; + } else if (!strcasecmp(api, "qt_software")) { + return 0; + } else if (!strcasecmp(api, "qt_opengl")) { + return 1; + } else if (!strcasecmp(api, "qt_opengles")) { + return 2; + } + return 0; } char* plat_vidapi_name(int api) { - return "default"; + char* name = "default"; + + switch (api) { + case 0: + name = "qt_software"; + break; + case 1: + name = "qt_opengl"; + break; + case 2: + name = "qt_opengles"; + break; + default: + fatal("Unknown renderer: %i\n", api); + break; + } + + return name; } diff --git a/src/qt/qt_hardwarerenderer.cpp b/src/qt/qt_hardwarerenderer.cpp index d9a206238..0659e41a5 100644 --- a/src/qt/qt_hardwarerenderer.cpp +++ b/src/qt/qt_hardwarerenderer.cpp @@ -1,4 +1,5 @@ #include "qt_hardwarerenderer.hpp" +#include extern "C" { #include <86box/86box.h> @@ -14,14 +15,8 @@ void HardwareRenderer::initializeGL() initializeOpenGLFunctions(); } -void HardwareRenderer::paintGL() -{ - QPainter painter(this); - painter.setRenderHint(QPainter::SmoothPixmapTransform, video_filter_method > 0 ? true : false); - painter.drawImage(QRect(0, 0, width(), height()), image, QRect(sx, sy, sw, sh)); - // "release" image, reducing it's refcount, so renderstack::blit() - // won't have to reallocate - image = QImage(); +void HardwareRenderer::paintGL() { + onPaint(this); } void HardwareRenderer::setRenderType(RenderType type) { @@ -32,7 +27,7 @@ void HardwareRenderer::setRenderType(RenderType type) { format.setRenderableType(QSurfaceFormat::OpenGL); break; case RenderType::OpenGLES: - setTextureFormat(GL_RGBA); + setTextureFormat((QApplication::platformName().contains("wayland") || QApplication::platformName() == "cocoa") ? GL_RGB : GL_RGBA); format.setRenderableType(QSurfaceFormat::OpenGLES); break; } @@ -41,9 +36,11 @@ void HardwareRenderer::setRenderType(RenderType type) { void HardwareRenderer::onBlit(const QImage& img, int x, int y, int w, int h) { image = img; - sx = x; - sy = y; - sw = w; - sh = h; + source.setRect(x, y, w, h); update(); } + +void HardwareRenderer::resizeEvent(QResizeEvent *event) { + onResize(width(), height()); + QOpenGLWidget::resizeEvent(event); +} diff --git a/src/qt/qt_hardwarerenderer.hpp b/src/qt/qt_hardwarerenderer.hpp index 4d7f68a08..3a47c6284 100644 --- a/src/qt/qt_hardwarerenderer.hpp +++ b/src/qt/qt_hardwarerenderer.hpp @@ -10,11 +10,13 @@ #include #include +#include "qt_renderercomon.hpp" + #ifdef WAYLAND #include "wl_mouse.hpp" #endif -class HardwareRenderer : public QOpenGLWidget, protected QOpenGLFunctions +class HardwareRenderer : public QOpenGLWidget, protected QOpenGLFunctions, public RendererCommon { Q_OBJECT @@ -49,7 +51,6 @@ public: public slots: void onBlit(const QImage& img, int, int, int, int); -private: - QImage image; - int sx, sy, sw, sh; +protected: + void resizeEvent(QResizeEvent *event) override; }; diff --git a/src/qt/qt_main.cpp b/src/qt/qt_main.cpp index 48ca388ac..431da1fd6 100644 --- a/src/qt/qt_main.cpp +++ b/src/qt/qt_main.cpp @@ -21,12 +21,7 @@ Q_IMPORT_PLUGIN(QWindowsVistaStylePlugin) #include -#define SDL_MAIN_HANDLED -#include "SDL.h" -#include "SDL_mutex.h" -#include "SDL_timer.h" #include "qt_mainwindow.hpp" -#include "qt_sdl.h" #include "cocoa_mouse.hpp" @@ -88,12 +83,6 @@ main_thread_fn() is_quit = 1; } -uint32_t timer_onesec(uint32_t interval, void* param) -{ - pc_onesec(); - return interval; -} - int main(int argc, char* argv[]) { QApplication app(argc, argv); #ifdef __APPLE__ @@ -101,7 +90,12 @@ int main(int argc, char* argv[]) { app.installNativeEventFilter(&cocoafilter); #endif elapsed_timer.start(); - SDL_Init(SDL_INIT_TIMER); + + pc_init(argc, argv); + if (! pc_init_modules()) { + ui_msgbox_header(MBX_FATAL, VC(L"No ROMs found."), VC(L"86Box could not find any usable ROM images.\n\nPlease download a ROM set and extract it into the \"roms\" directory.")); + return 6; + } main_window = new MainWindow(); main_window->show(); @@ -114,16 +108,15 @@ int main(int argc, char* argv[]) { } main_window->setFocusPolicy(Qt::StrongFocus); - pc_init(argc, argv); - if (! pc_init_modules()) { - ui_msgbox_header(MBX_FATAL, VC(L"No ROMs found."), VC(L"86Box could not find any usable ROM images.\n\nPlease download a ROM set and extract it into the \"roms\" directory.")); - return 6; - } pc_reset_hard_init(); /* Set the PAUSE mode depending on the renderer. */ // plat_pause(0); - SDL_AddTimer(1000, timer_onesec, nullptr); + QTimer onesec; + QObject::connect(&onesec, &QTimer::timeout, &app, [] { + pc_onesec(); + }); + onesec.start(1000); /* Initialize the rendering window, or fullscreen. */ QTimer::singleShot(50, []() { plat_resize(640, 480); } ); diff --git a/src/qt/qt_mainwindow.cpp b/src/qt/qt_mainwindow.cpp index 93d5bf1d1..54c29645b 100644 --- a/src/qt/qt_mainwindow.cpp +++ b/src/qt/qt_mainwindow.cpp @@ -7,8 +7,6 @@ extern "C" { #include <86box/keyboard.h> #include <86box/plat.h> #include <86box/video.h> - -#include "qt_sdl.h" }; #include @@ -79,8 +77,13 @@ MainWindow::MainWindow(QWidget *parent) : connect(this, &MainWindow::resizeContents, this, [this](int w, int h) { if (!QApplication::platformName().contains("eglfs")) { + int modifiedHeight = h + menuBar()->height() + statusBar()->height(); ui->stackedWidget->resize(w, h); - resize(w, h + menuBar()->height() + statusBar()->height()); + if (vid_resize == 0) { + setFixedSize(w, modifiedHeight); + } else { + resize(w, modifiedHeight); + } } }); @@ -98,6 +101,86 @@ MainWindow::MainWindow(QWidget *parent) : ui->actionKeyboard_requires_capture->setChecked(kbd_req_capture); ui->actionRight_CTRL_is_left_ALT->setChecked(rctrl_is_lalt); + ui->actionResizable_window->setChecked(vid_resize > 0); + ui->menuWindow_scale_factor->setEnabled(vid_resize == 0); + switch (vid_api) { + case 0: + ui->stackedWidget->setCurrentIndex(0); + ui->actionSoftware_Renderer->setChecked(true); + break; + case 1: + ui->stackedWidget->setCurrentIndex(1); + ui->actionHardware_Renderer_OpenGL->setChecked(true); + break; + case 2: + ui->stackedWidget->setCurrentIndex(2); + ui->actionHardware_Renderer_OpenGL_ES->setChecked(true); + break; + } + 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; + } + switch (video_filter_method) { + case 0: + ui->actionNearest->setChecked(true); + break; + case 1: + ui->actionLinear->setChecked(true); + break; + } + 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; + } + 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; + } + 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; + } setFocusPolicy(Qt::StrongFocus); ui->gles->setFocusPolicy(Qt::NoFocus); @@ -696,6 +779,10 @@ void MainWindow::on_actionFullscreen_triggered() { showFullScreen(); video_fullscreen = 1; } + + auto widget = ui->stackedWidget->currentWidget(); + auto rc = dynamic_cast(widget); + rc->onResize(widget->width(), widget->height()); } void MainWindow::setTitle_(const wchar_t *title) @@ -740,6 +827,7 @@ bool MainWindow::eventFilter(QObject* receiver, QEvent* event) return true; } } + return QMainWindow::eventFilter(receiver, event); } @@ -794,14 +882,23 @@ void MainWindow::keyReleaseEvent(QKeyEvent* event) void MainWindow::on_actionSoftware_Renderer_triggered() { ui->stackedWidget->setCurrentIndex(0); + ui->actionHardware_Renderer_OpenGL->setChecked(false); + ui->actionHardware_Renderer_OpenGL_ES->setChecked(false); + vid_api = 0; } void MainWindow::on_actionHardware_Renderer_OpenGL_triggered() { ui->stackedWidget->setCurrentIndex(1); + ui->actionSoftware_Renderer->setChecked(false); + ui->actionHardware_Renderer_OpenGL_ES->setChecked(false); + vid_api = 1; } void MainWindow::on_actionHardware_Renderer_OpenGL_ES_triggered() { ui->stackedWidget->setCurrentIndex(2); + ui->actionSoftware_Renderer->setChecked(false); + ui->actionHardware_Renderer_OpenGL->setChecked(false); + vid_api = 2; } void MainWindow::focusInEvent(QFocusEvent* event) @@ -813,3 +910,166 @@ void MainWindow::focusOutEvent(QFocusEvent* event) { this->releaseKeyboard(); } + +void MainWindow::on_actionResizable_window_triggered(bool checked) { + if (checked) { + vid_resize = 1; + setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); + } else { + vid_resize = 0; + } + ui->menuWindow_scale_factor->setEnabled(! checked); + emit resizeContents(scrnsz_x, scrnsz_y); +} + +static void +video_toggle_option(QAction* action, int *val) +{ + startblit(); + *val ^= 1; + video_copy = (video_grayscale || invert_display) ? video_transform_copy : memcpy; + action->setChecked(*val > 0 ? true : false); + endblit(); + config_save(); + device_force_redraw(); +} + +void MainWindow::on_actionInverted_VGA_monitor_triggered() { + video_toggle_option(ui->actionInverted_VGA_monitor, &invert_display); +} + +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(); + video_force_resize_set(1); + doresize = 1; + 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); +} + +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); +} + +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(); + auto rc = dynamic_cast(widget); + rc->onResize(widget->width(), widget->height()); + } + + 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); +} diff --git a/src/qt/qt_mainwindow.hpp b/src/qt/qt_mainwindow.hpp index 6c56cfd6a..972b2a161 100644 --- a/src/qt/qt_mainwindow.hpp +++ b/src/qt/qt_mainwindow.hpp @@ -56,6 +56,26 @@ private slots: void on_actionHardware_Renderer_OpenGL_ES_triggered(); void on_actionHardware_Renderer_OpenGL_triggered(); void on_actionSoftware_Renderer_triggered(); + void on_actionResizable_window_triggered(bool checked); + void on_actionInverted_VGA_monitor_triggered(); + void on_action0_5x_triggered(); + void on_action1x_triggered(); + void on_action1_5x_triggered(); + void on_action2x_triggered(); + void on_actionLinear_triggered(); + void on_actionNearest_triggered(); + void on_actionFullScreen_int_triggered(); + void on_actionFullScreen_keepRatio_triggered(); + void on_actionFullScreen_43_triggered(); + void on_actionFullScreen_stretch_triggered(); + void on_actionWhite_monitor_triggered(); + void on_actionGreen_monitor_triggered(); + void on_actionAmber_monitor_triggered(); + void on_actionRGB_Grayscale_triggered(); + void on_actionRGB_Color_triggered(); + void on_actionAverage_triggered(); + void on_actionBT709_HDTV_triggered(); + void on_actionBT601_NTSC_PAL_triggered(); void refreshMediaMenu(); void showMessage_(const QString& header, const QString& message); diff --git a/src/qt/qt_mainwindow.ui b/src/qt/qt_mainwindow.ui index 3541a5736..af8a8a2bd 100644 --- a/src/qt/qt_mainwindow.ui +++ b/src/qt/qt_mainwindow.ui @@ -54,7 +54,7 @@ 0 0 724 - 20 + 19 @@ -83,10 +83,83 @@ View + + + Renderer + + + + + + + + Window scale factor + + + + + + + + + Filter method + + + + + + + Fullscreen stretch mode + + + + + + + + + EGA/(S)VGA settings + + + + VGA screen type + + + + + + + + + + Grayscale conversion type + + + + + + + + + + + + + + + + + + + + + - - - + + + + + @@ -157,20 +230,242 @@ + + true + Software Renderer + + true + Hardware Renderer (OpenGL) + + true + Hardware Renderer (OpenGL ES) + + + true + + + Hide status bar + + + + + true + + + Resizable window + + + + + true + + + Remember size and position + + + + + Specify dimensions... + + + + + true + + + Force 4:3 display ratio + + + + + true + + + HiDPI scaling + + + + + true + + + CGA/PCjr/Tandy/EGA/(S)VGA overscan + + + + + true + + + Change contrast for monochrome display + + + + + true + + + 0.5x + + + + + true + + + 1x + + + + + true + + + 1.5x + + + + + true + + + 2x + + + + + true + + + Nearest + + + + + true + + + Linear + + + + + true + + + Full screen stretch + + + + + true + + + 4:3 + + + + + true + + + Square pixels (Keep ratio) + + + + + true + + + Integer scale + + + + + true + + + Inverted VGA monitor + + + + + true + + + RGB Color + + + + + true + + + RGB Grayscale + + + + + true + + + Amber monitor + + + + + true + + + Green monitor + + + + + true + + + White monitor + + + + + true + + + BT601 (NTSC/PAL) + + + + + true + + + BT709 (HDTV) + + + + + true + + + Average + + diff --git a/src/qt/qt_platform.cpp b/src/qt/qt_platform.cpp index 357d9e0c3..0f83a8583 100644 --- a/src/qt/qt_platform.cpp +++ b/src/qt/qt_platform.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include @@ -21,6 +22,8 @@ // static QByteArray buf; extern QElapsedTimer elapsed_timer; QElapsedTimer elapsed_timer; + +static std::atomic_int blitmx_contention = 0; static std::mutex blitmx; class CharPointer { @@ -408,12 +411,24 @@ void joystick_close(void) {} void joystick_process(void) {} void startblit() { + blitmx_contention++; + if (blitmx.try_lock()) { + return; + } + blitmx.lock(); } void endblit() { + blitmx_contention--; blitmx.unlock(); + if (blitmx_contention > 0) { + // a deadlock has been observed on linux when toggling via video_toggle_option + // because the mutex is typically unfair on linux + // => sleep if there's contention + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } } } diff --git a/src/qt/qt_renderercomon.cpp b/src/qt/qt_renderercomon.cpp new file mode 100644 index 000000000..173ae1abe --- /dev/null +++ b/src/qt/qt_renderercomon.cpp @@ -0,0 +1,89 @@ +#include "qt_renderercomon.hpp" + +#include +#include + +#include + +extern "C" { +#include <86box/86box.h> +#include <86box/video.h> +} + +RendererCommon::RendererCommon() = default; + +void RendererCommon::onPaint(QPaintDevice* device) { + QPainter painter(device); + painter.setRenderHint(QPainter::SmoothPixmapTransform, video_filter_method > 0 ? true : false); + painter.drawImage(destination, image, source); + // "release" image, reducing it's refcount, so renderstack::blit() + // won't have to reallocate + image = QImage(); +} + +static void integer_scale(double *d, double *g) { + double ratio; + + if (*d > *g) { + ratio = std::floor(*d / *g); + *d = *g * ratio; + } else { + ratio = std::ceil(*d / *g); + *d = *g / ratio; + } +} + +void RendererCommon::onResize(int width, int height) { + if (video_fullscreen == 0) { + destination.setRect(0, 0, width, height); + return; + } + double dx, dy, dw, dh, gsr; + + double hw = width; + double hh = height; + double gw = source.width(); + double gh = source.height(); + double hsr = hw / hh; + + switch (video_fullscreen_scale) { + case FULLSCR_SCALE_INT: + gsr = gw / gh; + if (gsr <= hsr) { + dw = hh * gsr; + dh = hh; + } else { + dw = hw; + dh = hw / gsr; + } + integer_scale(&dw, &gw); + integer_scale(&dh, &gh); + dx = (hw - dw) / 2.0; + dy = (hh - dh) / 2.0; + destination.setRect(dx, dy, dw, dh); + break; + case FULLSCR_SCALE_43: + case FULLSCR_SCALE_KEEPRATIO: + if (video_fullscreen_scale == FULLSCR_SCALE_43) { + gsr = 4.0 / 3.0; + } else { + gsr = gw / gh; + } + + if (gsr <= hsr) { + dw = hh * gsr; + dh = hh; + } else { + dw = hw; + dh = hw / gsr; + } + dx = (hw - dw) / 2.0; + dy = (hh - dh) / 2.0; + destination.setRect(dx, dy, dw, dh); + break; + case FULLSCR_SCALE_FULL: + default: + destination.setRect(0, 0, hw, hh); + break; + } +} diff --git a/src/qt/qt_renderercomon.hpp b/src/qt/qt_renderercomon.hpp new file mode 100644 index 000000000..6eed9c9b2 --- /dev/null +++ b/src/qt/qt_renderercomon.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +class QWidget; + +class RendererCommon +{ +public: + RendererCommon(); + + void onResize(int width, int height); +protected: + void onPaint(QPaintDevice* device); + + QImage image; + QRect source, destination; +}; diff --git a/src/qt/qt_rendererstack.cpp b/src/qt/qt_rendererstack.cpp index cbc0b5e44..bb80944de 100644 --- a/src/qt/qt_rendererstack.cpp +++ b/src/qt/qt_rendererstack.cpp @@ -4,6 +4,8 @@ #include "qt_softwarerenderer.hpp" #include "qt_hardwarerenderer.hpp" +#include + #ifdef __APPLE__ #include #endif @@ -99,11 +101,11 @@ void RendererStack::wheelEvent(QWheelEvent *event) { if (mouse_capture) { - mousedata.deltay += event->pixelDelta().y(); + mousedata.deltaz += event->pixelDelta().y(); } } -int ignoreNextMouseEvent = 0; +int ignoreNextMouseEvent = 1; void RendererStack::mouseMoveEvent(QMouseEvent *event) { if (QApplication::platformName().contains("wayland")) @@ -120,12 +122,25 @@ void RendererStack::mouseMoveEvent(QMouseEvent *event) if (ignoreNextMouseEvent) { oldPos = event->pos(); ignoreNextMouseEvent--; event->accept(); return; } mousedata.deltax += event->pos().x() - oldPos.x(); mousedata.deltay += event->pos().y() - oldPos.y(); - QCursor::setPos(mapToGlobal(QPoint(width() / 2, height() / 2))); + if (event->globalPos().x() == 0 || event->globalPos().y() == 0) leaveEvent((QEvent*)event); + if (event->globalPos().x() == (screen()->geometry().width() - 1) || event->globalPos().y() == (screen()->geometry().height() - 1)) leaveEvent((QEvent*)event); oldPos = event->pos(); - ignoreNextMouseEvent = 1; #endif } +void RendererStack::leaveEvent(QEvent* event) +{ + if (QApplication::platformName().contains("wayland")) + { + event->accept(); + return; + } + if (!mouse_capture) return; + QCursor::setPos(mapToGlobal(QPoint(width() / 2, height() / 2))); + ignoreNextMouseEvent = 2; + event->accept(); +} + // called from blitter thread void RendererStack::blit(int x, int y, int w, int h) { diff --git a/src/qt/qt_rendererstack.hpp b/src/qt/qt_rendererstack.hpp index 05dd5db5c..239bd5bef 100644 --- a/src/qt/qt_rendererstack.hpp +++ b/src/qt/qt_rendererstack.hpp @@ -3,6 +3,7 @@ #include #include +#include namespace Ui { class RendererStack; @@ -20,6 +21,7 @@ public: void mouseReleaseEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; void wheelEvent(QWheelEvent *event) override; + void leaveEvent(QEvent *event) override; void keyPressEvent(QKeyEvent* event) override { event->ignore(); diff --git a/src/qt/qt_softwarerenderer.cpp b/src/qt/qt_softwarerenderer.cpp index cf69ca2fb..0565d2d6c 100644 --- a/src/qt/qt_softwarerenderer.cpp +++ b/src/qt/qt_softwarerenderer.cpp @@ -1,26 +1,19 @@ #include "qt_softwarerenderer.hpp" -extern "C" { -#include <86box/86box.h> -} -#include - SoftwareRenderer::SoftwareRenderer(QWidget *parent) : QWidget(parent) {} void SoftwareRenderer::paintEvent(QPaintEvent *event) { (void) event; - - QPainter painter(this); - painter.setRenderHint(QPainter::SmoothPixmapTransform, video_filter_method > 0 ? true : false); - painter.drawImage(QRect(0, 0, width(), height()), image, QRect(sx, sy, sw, sh)); - image = QImage(); + onPaint(this); } void SoftwareRenderer::onBlit(const QImage& img, int x, int y, int w, int h) { image = img; - sx = x; - sy = y; - sw = w; - sh = h; + source.setRect(x, y, w, h); update(); } + +void SoftwareRenderer::resizeEvent(QResizeEvent *event) { + onResize(width(), height()); + QWidget::resizeEvent(event); +} diff --git a/src/qt/qt_softwarerenderer.hpp b/src/qt/qt_softwarerenderer.hpp index 2a1075023..873e9f732 100644 --- a/src/qt/qt_softwarerenderer.hpp +++ b/src/qt/qt_softwarerenderer.hpp @@ -2,21 +2,20 @@ #define SOFTWARERENDERER_HPP #include +#include "qt_renderercomon.hpp" -class SoftwareRenderer : public QWidget +class SoftwareRenderer : public QWidget, public RendererCommon { Q_OBJECT public: explicit SoftwareRenderer(QWidget *parent = nullptr); void paintEvent(QPaintEvent *event) override; - public slots: void onBlit(const QImage& img, int, int, int, int); -private: - QImage image; - int sx, sy, sw, sh; +protected: + void resizeEvent(QResizeEvent *event) override; }; #endif // SOFTWARERENDERER_HPP diff --git a/src/qt/qt_ui.cpp b/src/qt/qt_ui.cpp index e69a954c2..41d608216 100644 --- a/src/qt/qt_ui.cpp +++ b/src/qt/qt_ui.cpp @@ -2,15 +2,12 @@ #include #include +#include #include #include "qt_mainwindow.hpp" -std::atomic_int resize_pending = 0; -std::atomic_int resize_w = 0; -std::atomic_int resize_h = 0; - MainWindow* main_window = nullptr; extern "C" { @@ -46,10 +43,6 @@ void mouse_poll() { } void plat_resize(int w, int h) { - resize_w = w; - resize_h = h; - resize_pending = 1; - main_window->resizeContents(w, h); } @@ -68,7 +61,13 @@ int ui_msgbox_header(int flags, void *header, void* message) { auto hdr = QString::fromWCharArray(reinterpret_cast(header)); auto msg = QString::fromWCharArray(reinterpret_cast(message)); - main_window->showMessage(hdr, msg); + // any error in early init + if (main_window == nullptr) { + QMessageBox::critical(nullptr, hdr, msg); + } else { + // else scope it to main_window + main_window->showMessage(hdr, msg); + } return 0; }