This commit is contained in:
ts-korhonen
2021-12-08 17:38:54 +02:00
16 changed files with 802 additions and 93 deletions

View File

@@ -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)

View File

@@ -3,6 +3,7 @@
* implemented in Qt
*/
#include <strings.h>
#include <stdint.h>
#include <wchar.h>
@@ -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;
}

View File

@@ -1,4 +1,5 @@
#include "qt_hardwarerenderer.hpp"
#include <QApplication>
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);
}

View File

@@ -10,11 +10,13 @@
#include <mutex>
#include <QApplication>
#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;
};

View File

@@ -21,12 +21,7 @@ Q_IMPORT_PLUGIN(QWindowsVistaStylePlugin)
#include <thread>
#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); } );

View File

@@ -7,8 +7,6 @@ extern "C" {
#include <86box/keyboard.h>
#include <86box/plat.h>
#include <86box/video.h>
#include "qt_sdl.h"
};
#include <QGuiApplication>
@@ -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<RendererCommon*>(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<RendererCommon*>(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);
}

View File

@@ -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);

View File

@@ -54,7 +54,7 @@
<x>0</x>
<y>0</y>
<width>724</width>
<height>20</height>
<height>19</height>
</rect>
</property>
<widget class="QMenu" name="menuAction">
@@ -83,10 +83,83 @@
<property name="title">
<string>View</string>
</property>
<widget class="QMenu" name="menuRenderer">
<property name="title">
<string>Renderer</string>
</property>
<addaction name="actionSoftware_Renderer"/>
<addaction name="actionHardware_Renderer_OpenGL"/>
<addaction name="actionHardware_Renderer_OpenGL_ES"/>
</widget>
<widget class="QMenu" name="menuWindow_scale_factor">
<property name="title">
<string>Window scale factor</string>
</property>
<addaction name="action0_5x"/>
<addaction name="action1x"/>
<addaction name="action1_5x"/>
<addaction name="action2x"/>
</widget>
<widget class="QMenu" name="menuFilter_method">
<property name="title">
<string>Filter method</string>
</property>
<addaction name="actionNearest"/>
<addaction name="actionLinear"/>
</widget>
<widget class="QMenu" name="menuFullscreen_stretch_mode">
<property name="title">
<string>Fullscreen stretch mode</string>
</property>
<addaction name="actionFullScreen_stretch"/>
<addaction name="actionFullScreen_43"/>
<addaction name="actionFullScreen_keepRatio"/>
<addaction name="actionFullScreen_int"/>
</widget>
<widget class="QMenu" name="menuEGA_S_VGA_settings">
<property name="title">
<string>EGA/(S)VGA settings</string>
</property>
<widget class="QMenu" name="menuVGA_screen_type">
<property name="title">
<string>VGA screen type</string>
</property>
<addaction name="actionRGB_Color"/>
<addaction name="actionRGB_Grayscale"/>
<addaction name="actionAmber_monitor"/>
<addaction name="actionGreen_monitor"/>
<addaction name="actionWhite_monitor"/>
</widget>
<widget class="QMenu" name="menuGrayscale_conversion_type">
<property name="title">
<string>Grayscale conversion type</string>
</property>
<addaction name="actionBT601_NTSC_PAL"/>
<addaction name="actionBT709_HDTV"/>
<addaction name="actionAverage"/>
</widget>
<addaction name="actionInverted_VGA_monitor"/>
<addaction name="menuVGA_screen_type"/>
<addaction name="menuGrayscale_conversion_type"/>
</widget>
<addaction name="actionHide_status_bar"/>
<addaction name="separator"/>
<addaction name="actionResizable_window"/>
<addaction name="actionRemember_size_and_position"/>
<addaction name="separator"/>
<addaction name="menuRenderer"/>
<addaction name="actionSpecify_dimensions"/>
<addaction name="actionForce_4_3_display_ratio"/>
<addaction name="menuWindow_scale_factor"/>
<addaction name="menuFilter_method"/>
<addaction name="actionHiDPI_scaling"/>
<addaction name="separator"/>
<addaction name="actionFullscreen"/>
<addaction name="actionSoftware_Renderer"/>
<addaction name="actionHardware_Renderer_OpenGL"/>
<addaction name="actionHardware_Renderer_OpenGL_ES"/>
<addaction name="menuFullscreen_stretch_mode"/>
<addaction name="menuEGA_S_VGA_settings"/>
<addaction name="separator"/>
<addaction name="actionCGA_PCjr_Tandy_EGA_S_VGA_overscan"/>
<addaction name="actionChange_contrast_for_monochrome_display"/>
</widget>
<widget class="QMenu" name="menuMedia">
<property name="title">
@@ -157,20 +230,242 @@
</property>
</action>
<action name="actionSoftware_Renderer">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Software Renderer</string>
</property>
</action>
<action name="actionHardware_Renderer_OpenGL">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Hardware Renderer (OpenGL)</string>
</property>
</action>
<action name="actionHardware_Renderer_OpenGL_ES">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Hardware Renderer (OpenGL ES)</string>
</property>
</action>
<action name="actionHide_status_bar">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Hide status bar</string>
</property>
</action>
<action name="actionResizable_window">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Resizable window</string>
</property>
</action>
<action name="actionRemember_size_and_position">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Remember size and position</string>
</property>
</action>
<action name="actionSpecify_dimensions">
<property name="text">
<string>Specify dimensions...</string>
</property>
</action>
<action name="actionForce_4_3_display_ratio">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Force 4:3 display ratio</string>
</property>
</action>
<action name="actionHiDPI_scaling">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>HiDPI scaling</string>
</property>
</action>
<action name="actionCGA_PCjr_Tandy_EGA_S_VGA_overscan">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>CGA/PCjr/Tandy/EGA/(S)VGA overscan</string>
</property>
</action>
<action name="actionChange_contrast_for_monochrome_display">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Change contrast for monochrome display</string>
</property>
</action>
<action name="action0_5x">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>0.5x</string>
</property>
</action>
<action name="action1x">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>1x</string>
</property>
</action>
<action name="action1_5x">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>1.5x</string>
</property>
</action>
<action name="action2x">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>2x</string>
</property>
</action>
<action name="actionNearest">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Nearest</string>
</property>
</action>
<action name="actionLinear">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Linear</string>
</property>
</action>
<action name="actionFullScreen_stretch">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Full screen stretch</string>
</property>
</action>
<action name="actionFullScreen_43">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>4:3</string>
</property>
</action>
<action name="actionFullScreen_keepRatio">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Square pixels (Keep ratio)</string>
</property>
</action>
<action name="actionFullScreen_int">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Integer scale</string>
</property>
</action>
<action name="actionInverted_VGA_monitor">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Inverted VGA monitor</string>
</property>
</action>
<action name="actionRGB_Color">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>RGB Color</string>
</property>
</action>
<action name="actionRGB_Grayscale">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>RGB Grayscale</string>
</property>
</action>
<action name="actionAmber_monitor">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Amber monitor</string>
</property>
</action>
<action name="actionGreen_monitor">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Green monitor</string>
</property>
</action>
<action name="actionWhite_monitor">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>White monitor</string>
</property>
</action>
<action name="actionBT601_NTSC_PAL">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>BT601 (NTSC/PAL)</string>
</property>
</action>
<action name="actionBT709_HDTV">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>BT709 (HDTV)</string>
</property>
</action>
<action name="actionAverage">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Average</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>

View File

@@ -1,6 +1,7 @@
#include <cstdio>
#include <mutex>
#include <thread>
#include <memory>
#include <algorithm>
@@ -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));
}
}
}

View File

@@ -0,0 +1,89 @@
#include "qt_renderercomon.hpp"
#include <QPainter>
#include <QWidget>
#include <cmath>
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;
}
}

View File

@@ -0,0 +1,19 @@
#pragma once
#include <QRect>
#include <QImage>
class QWidget;
class RendererCommon
{
public:
RendererCommon();
void onResize(int width, int height);
protected:
void onPaint(QPaintDevice* device);
QImage image;
QRect source, destination;
};

View File

@@ -4,6 +4,8 @@
#include "qt_softwarerenderer.hpp"
#include "qt_hardwarerenderer.hpp"
#include <QScreen>
#ifdef __APPLE__
#include <CoreGraphics/CoreGraphics.h>
#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)
{

View File

@@ -3,6 +3,7 @@
#include <QStackedWidget>
#include <QKeyEvent>
#include <QEvent>
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();

View File

@@ -1,26 +1,19 @@
#include "qt_softwarerenderer.hpp"
extern "C" {
#include <86box/86box.h>
}
#include <QPainter>
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);
}

View File

@@ -2,21 +2,20 @@
#define SOFTWARERENDERER_HPP
#include <QWidget>
#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

View File

@@ -2,15 +2,12 @@
#include <QDebug>
#include <QThread>
#include <QMessageBox>
#include <QStatusBar>
#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<const wchar_t*>(header));
auto msg = QString::fromWCharArray(reinterpret_cast<const wchar_t*>(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;
}