From 03006ca078bbc5616274b403cb38f138994977d5 Mon Sep 17 00:00:00 2001 From: Jasmine Iwanek Date: Mon, 11 Aug 2025 21:59:36 -0400 Subject: [PATCH 1/5] More progress towards 2nd gameport support --- src/86box.c | 7 ++-- src/config.c | 46 ++++++++++++----------- src/game/gameport.c | 44 ++++++++++++---------- src/include/86box/gameport.h | 8 ++-- src/machine/m_europc.c | 2 +- src/machine/m_xt_olivetti.c | 2 +- src/qt/qt_joystickconfiguration.cpp | 11 +++--- src/qt/qt_joystickconfiguration.hpp | 3 +- src/qt/qt_settingsinput.cpp | 57 ++++++++++++++--------------- src/qt/qt_settingsinput.hpp | 10 ++--- src/qt/qt_settingsinput.ui | 12 +++--- src/qt/sdl_joystick.c | 52 +++++++++++++------------- src/qt/win_joystick_rawinput.c | 44 +++++++++++----------- src/unix/unix.c | 2 +- 14 files changed, 155 insertions(+), 145 deletions(-) diff --git a/src/86box.c b/src/86box.c index a30ff970f..bb3f3dc3e 100644 --- a/src/86box.c +++ b/src/86box.c @@ -1593,8 +1593,9 @@ pc_reset_hard_init(void) the chances of the SCSI controller ending up on the bridge. */ video_voodoo_init(); - if (joystick_type) - gameport_update_joystick_type(); /* installs game port if no device provides one, must be late */ + /* installs first game port if no device provides one, must be late */ + if (joystick_type[0]) + gameport_update_joystick_type(0); ui_sb_update_panes(); @@ -1802,7 +1803,7 @@ pc_run(void) #ifdef USE_GDBSTUB /* avoid a KBC FIFO overflow when CPU emulation is stalled */ } #endif - joystick_process(); + joystick_process(0); // Gameport 0 endblit(); /* Done with this frame, update statistics. */ diff --git a/src/config.c b/src/config.c index 7f4279a02..345e2a483 100644 --- a/src/config.c +++ b/src/config.c @@ -529,62 +529,63 @@ load_input_devices(void) else mouse_type = 0; + uint8_t joy_insn = 0; p = ini_section_get_string(cat, "joystick_type", NULL); if (p != NULL) { - joystick_type = joystick_get_from_internal_name(p); + joystick_type[joy_insn] = joystick_get_from_internal_name(p); - if (!joystick_type) { + if (!joystick_type[joy_insn]) { /* Try to read an integer for backwards compatibility with old configs */ if (!strcmp(p, "0")) /* Workaround for ini_section_get_int returning 0 on non-integer data */ - joystick_type = joystick_get_from_internal_name("2axis_2button"); + joystick_type[joy_insn] = joystick_get_from_internal_name("2axis_2button"); else { int js = ini_section_get_int(cat, "joystick_type", 8); switch (js) { case JS_TYPE_2AXIS_4BUTTON: - joystick_type = joystick_get_from_internal_name("2axis_4button"); + joystick_type[joy_insn] = joystick_get_from_internal_name("2axis_4button"); break; case JS_TYPE_2AXIS_6BUTTON: - joystick_type = joystick_get_from_internal_name("2axis_6button"); + joystick_type[joy_insn] = joystick_get_from_internal_name("2axis_6button"); break; case JS_TYPE_2AXIS_8BUTTON: - joystick_type = joystick_get_from_internal_name("2axis_8button"); + joystick_type[joy_insn] = joystick_get_from_internal_name("2axis_8button"); break; case JS_TYPE_4AXIS_4BUTTON: - joystick_type = joystick_get_from_internal_name("4axis_4button"); + joystick_type[joy_insn] = joystick_get_from_internal_name("4axis_4button"); break; case JS_TYPE_CH_FLIGHTSTICK_PRO: - joystick_type = joystick_get_from_internal_name("ch_flightstick_pro"); + joystick_type[joy_insn] = joystick_get_from_internal_name("ch_flightstick_pro"); break; case JS_TYPE_SIDEWINDER_PAD: - joystick_type = joystick_get_from_internal_name("sidewinder_pad"); + joystick_type[joy_insn] = joystick_get_from_internal_name("sidewinder_pad"); break; case JS_TYPE_THRUSTMASTER_FCS: - joystick_type = joystick_get_from_internal_name("thrustmaster_fcs"); + joystick_type[joy_insn] = joystick_get_from_internal_name("thrustmaster_fcs"); break; default: - joystick_type = JS_TYPE_NONE; + joystick_type[joy_insn] = JS_TYPE_NONE; break; } } } } else - joystick_type = JS_TYPE_NONE; + joystick_type[joy_insn] = JS_TYPE_NONE; - for (int js = 0; js < joystick_get_max_joysticks(joystick_type); js++) { + for (int js = 0; js < joystick_get_max_joysticks(joystick_type[joy_insn]); js++) { sprintf(temp, "joystick_%i_nr", js); joystick_state[0][js].plat_joystick_nr = ini_section_get_int(cat, temp, 0); if (joystick_state[0][js].plat_joystick_nr) { - for (int axis_nr = 0; axis_nr < joystick_get_axis_count(joystick_type); axis_nr++) { + for (int axis_nr = 0; axis_nr < joystick_get_axis_count(joystick_type[joy_insn]); axis_nr++) { sprintf(temp, "joystick_%i_axis_%i", js, axis_nr); joystick_state[0][js].axis_mapping[axis_nr] = ini_section_get_int(cat, temp, axis_nr); } - for (int button_nr = 0; button_nr < joystick_get_button_count(joystick_type); button_nr++) { + for (int button_nr = 0; button_nr < joystick_get_button_count(joystick_type[joy_insn]); button_nr++) { sprintf(temp, "joystick_%i_button_%i", js, button_nr); joystick_state[0][js].button_mapping[button_nr] = ini_section_get_int(cat, temp, button_nr); } - for (int pov_nr = 0; pov_nr < joystick_get_pov_count(joystick_type); pov_nr++) { + for (int pov_nr = 0; pov_nr < joystick_get_pov_count(joystick_type[joy_insn]); pov_nr++) { sprintf(temp, "joystick_%i_pov_%i", js, pov_nr); p = ini_section_get_string(cat, temp, "0, 0"); joystick_state[0][js].pov_mapping[pov_nr][0] = joystick_state[0][js].pov_mapping[pov_nr][1] = 0; @@ -2552,7 +2553,8 @@ save_input_devices(void) ini_section_set_string(cat, "mouse_type", mouse_get_internal_name(mouse_type)); - if (!joystick_type) { + uint8_t joy_insn = 0; + if (!joystick_type[joy_insn]) { ini_section_delete_var(cat, "joystick_type"); for (int js = 0; js < MAX_PLAT_JOYSTICKS; js++) { @@ -2573,22 +2575,22 @@ save_input_devices(void) } } } else { - ini_section_set_string(cat, "joystick_type", joystick_get_internal_name(joystick_type)); + ini_section_set_string(cat, "joystick_type", joystick_get_internal_name(joystick_type[joy_insn])); - for (int js = 0; js < joystick_get_max_joysticks(joystick_type); js++) { + for (int js = 0; js < joystick_get_max_joysticks(joystick_type[joy_insn]); js++) { sprintf(tmp2, "joystick_%i_nr", js); ini_section_set_int(cat, tmp2, joystick_state[0][js].plat_joystick_nr); if (joystick_state[0][js].plat_joystick_nr) { - for (int axis_nr = 0; axis_nr < joystick_get_axis_count(joystick_type); axis_nr++) { + for (int axis_nr = 0; axis_nr < joystick_get_axis_count(joystick_type[joy_insn]); axis_nr++) { sprintf(tmp2, "joystick_%i_axis_%i", js, axis_nr); ini_section_set_int(cat, tmp2, joystick_state[0][js].axis_mapping[axis_nr]); } - for (int button_nr = 0; button_nr < joystick_get_button_count(joystick_type); button_nr++) { + for (int button_nr = 0; button_nr < joystick_get_button_count(joystick_type[joy_insn]); button_nr++) { sprintf(tmp2, "joystick_%i_button_%i", js, button_nr); ini_section_set_int(cat, tmp2, joystick_state[0][js].button_mapping[button_nr]); } - for (int pov_nr = 0; pov_nr < joystick_get_pov_count(joystick_type); pov_nr++) { + for (int pov_nr = 0; pov_nr < joystick_get_pov_count(joystick_type[joy_insn]); pov_nr++) { sprintf(tmp2, "joystick_%i_pov_%i", js, pov_nr); sprintf(temp, "%i, %i", joystick_state[0][js].pov_mapping[pov_nr][0], joystick_state[0][js].pov_mapping[pov_nr][1]); diff --git a/src/game/gameport.c b/src/game/gameport.c index 45f34f1e1..f17e39efe 100644 --- a/src/game/gameport.c +++ b/src/game/gameport.c @@ -65,7 +65,7 @@ typedef struct _joystick_instance_ { void *dat; } joystick_instance_t; -int joystick_type = JS_TYPE_NONE; +int joystick_type[GAMEPORT_MAX] = { JS_TYPE_NONE, JS_TYPE_NONE }; static const joystick_t joystick_none = { .name = "None", @@ -299,17 +299,17 @@ timer_over(void *priv) } void -gameport_update_joystick_type(void) +gameport_update_joystick_type(uint8_t gp) { /* Add a standalone game port if a joystick is enabled but no other game ports exist. */ if (standalone_gameport_type) gameport_add(standalone_gameport_type); /* Reset the joystick interface. */ - if (joystick_instance[0]) { - joystick_instance[0]->intf->close(joystick_instance[0]->dat); - joystick_instance[0]->intf = joysticks[joystick_type].joystick; - joystick_instance[0]->dat = joystick_instance[0]->intf->init(); + if (joystick_instance[gp]) { + joystick_instance[gp]->intf->close(joystick_instance[gp]->dat); + joystick_instance[gp]->intf = joysticks[joystick_type[gp]].joystick; + joystick_instance[gp]->dat = joystick_instance[gp]->intf->init(); } } @@ -393,24 +393,27 @@ gameport_init(const device_t *info) { gameport_t *dev = calloc(1, sizeof(gameport_t)); + // TODO: Later we'll actually support more than one gameport + uint8_t joy_insn = 0; + /* Allocate global instance. */ - if (!joystick_instance[0] && joystick_type) { - joystick_instance[0] = calloc(1, sizeof(joystick_instance_t)); + if (!joystick_instance[joy_insn] && joystick_type[joy_insn]) { + joystick_instance[joy_insn] = calloc(1, sizeof(joystick_instance_t)); // For each analog joystick axis for (uint8_t i = 0; i < 4; i++) { - joystick_instance[0]->axis[i].joystick = joystick_instance[0]; + joystick_instance[joy_insn]->axis[i].joystick = joystick_instance[joy_insn]; - joystick_instance[0]->axis[i].axis_nr = i; + joystick_instance[joy_insn]->axis[i].axis_nr = i; - timer_add(&joystick_instance[0]->axis[i].timer, timer_over, &joystick_instance[0]->axis[i], 0); + timer_add(&joystick_instance[joy_insn]->axis[i].timer, timer_over, &joystick_instance[joy_insn]->axis[i], 0); } - joystick_instance[0]->intf = joysticks[joystick_type].joystick; - joystick_instance[0]->dat = joystick_instance[0]->intf->init(); + joystick_instance[joy_insn]->intf = joysticks[joystick_type[joy_insn]].joystick; + joystick_instance[joy_insn]->dat = joystick_instance[joy_insn]->intf->init(); } - dev->joystick = joystick_instance[0]; + dev->joystick = joystick_instance[joy_insn]; /* Map game port to the default address. Not applicable on PnP-only ports. */ dev->len = (info->local >> 16) & 0xff; @@ -475,15 +478,18 @@ gameport_close(void *priv) { gameport_t *dev = (gameport_t *) priv; + // TODO: Later we'll actually support more than one gameport + uint8_t joy_insn = 0; + /* If this port was active, remove it from the active ports list. */ gameport_remap(dev, 0); /* Free the global instance here, if it wasn't already freed. */ - if (joystick_instance[0]) { - joystick_instance[0]->intf->close(joystick_instance[0]->dat); + if (joystick_instance[joy_insn]) { + joystick_instance[joy_insn]->intf->close(joystick_instance[joy_insn]->dat); - free(joystick_instance[0]); - joystick_instance[0] = NULL; + free(joystick_instance[joy_insn]); + joystick_instance[joy_insn] = NULL; } free(dev); @@ -793,7 +799,7 @@ gameport_available(int port) /* UI */ const device_t * -gameport_getdevice(int port) +gameport_get_device(int port) { return (gameports[port].device); } diff --git a/src/include/86box/gameport.h b/src/include/86box/gameport.h index a990d018e..f9d7810e0 100644 --- a/src/include/86box/gameport.h +++ b/src/include/86box/gameport.h @@ -119,7 +119,7 @@ extern "C" { extern int gameport_available(int port); #ifdef EMU_DEVICE_H -extern const device_t *gameport_getdevice(int port); +extern const device_t *gameport_get_device(int port); #endif extern int gameport_has_config(int port); extern const char *gameport_get_internal_name(int port); @@ -151,11 +151,11 @@ extern plat_joystick_state_t plat_joystick_state[MAX_PLAT_JOYSTICKS]; extern joystick_state_t joystick_state[GAMEPORT_MAX][MAX_JOYSTICKS]; extern int joysticks_present; -extern int joystick_type; +extern int joystick_type[GAMEPORT_MAX]; extern void joystick_init(void); extern void joystick_close(void); -extern void joystick_process(void); +extern void joystick_process(uint8_t gp); extern const char *joystick_get_name(int js); extern const char *joystick_get_internal_name(int js); @@ -168,7 +168,7 @@ extern const char *joystick_get_axis_name(int js, int id); extern const char *joystick_get_button_name(int js, int id); extern const char *joystick_get_pov_name(int js, int id); -extern void gameport_update_joystick_type(void); +extern void gameport_update_joystick_type(uint8_t gp); extern void gameport_remap(void *priv, uint16_t address); extern void *gameport_add(const device_t *gameport_type); diff --git a/src/machine/m_europc.c b/src/machine/m_europc.c index 4f3a3b8ff..d85563241 100644 --- a/src/machine/m_europc.c +++ b/src/machine/m_europc.c @@ -625,7 +625,7 @@ europc_boot(UNUSED(const device_t *info)) mouse_bus_set_irq(sys->mouse, 2); /* Configure the port for (Bus Mouse Compatible) Mouse. */ b |= 0x01; - } else if (joystick_type) + } else if (joystick_type[0]) b |= 0x02; /* enable port as joysticks */ sys->nvr.regs[MRTC_CONF_C] = b; diff --git a/src/machine/m_xt_olivetti.c b/src/machine/m_xt_olivetti.c index ef10ab5dc..079cd4555 100644 --- a/src/machine/m_xt_olivetti.c +++ b/src/machine/m_xt_olivetti.c @@ -2397,7 +2397,7 @@ machine_xt_m240_init(const machine_t *model) if (fdc_current[0] == FDC_INTERNAL) device_add(&fdc_at_device); /* io.c logs clearly show it using port 3F7 */ - if (joystick_type) + if (joystick_type[0]) device_add(&gameport_200_device); nmi_init(); diff --git a/src/qt/qt_joystickconfiguration.cpp b/src/qt/qt_joystickconfiguration.cpp index 62a9302d1..2d8910f4a 100644 --- a/src/qt/qt_joystickconfiguration.cpp +++ b/src/qt/qt_joystickconfiguration.cpp @@ -27,10 +27,11 @@ extern "C" { #include #include "qt_models_common.hpp" -JoystickConfiguration::JoystickConfiguration(int type, int joystick_nr, QWidget *parent) +JoystickConfiguration::JoystickConfiguration(int type, uint8_t gameport_nr, int joystick_nr, QWidget *parent) : QDialog(parent) , ui(new Ui::JoystickConfiguration) , type(type) + , gameport_nr(gameport_nr) , joystick_nr(joystick_nr) { ui->setupUi(this); @@ -41,7 +42,7 @@ JoystickConfiguration::JoystickConfiguration(int type, int joystick_nr, QWidget Models::AddEntry(model, plat_joystick_state[c].name, c + 1); } - ui->comboBoxDevice->setCurrentIndex(joystick_state[0][joystick_nr].plat_joystick_nr); + ui->comboBoxDevice->setCurrentIndex(joystick_state[gameport_nr][joystick_nr].plat_joystick_nr); layout()->setSizeConstraint(QLayout::SetFixedSize); } @@ -119,7 +120,7 @@ JoystickConfiguration::on_comboBoxDevice_currentIndexChanged(int index) } int nr_axes = plat_joystick_state[joystick].nr_axes; - int mapping = joystick_state[0][joystick_nr].axis_mapping[c]; + int mapping = joystick_state[gameport_nr][joystick_nr].axis_mapping[c]; if (mapping & POV_X) cbox->setCurrentIndex(nr_axes + (mapping & 3) * 2); else if (mapping & POV_Y) @@ -147,7 +148,7 @@ JoystickConfiguration::on_comboBoxDevice_currentIndexChanged(int index) Models::AddEntry(model, plat_joystick_state[joystick].button[d].name, 0); } - cbox->setCurrentIndex(joystick_state[0][joystick_nr].button_mapping[c]); + cbox->setCurrentIndex(joystick_state[gameport_nr][joystick_nr].button_mapping[c]); ui->ct->addWidget(label, row, 0); ui->ct->addWidget(cbox, row, 1); @@ -179,7 +180,7 @@ JoystickConfiguration::on_comboBoxDevice_currentIndexChanged(int index) Models::AddEntry(model, plat_joystick_state[joystick].axis[d].name, 0); } - int mapping = joystick_state[0][joystick_nr].pov_mapping[c / 2][c & 1]; + int mapping = joystick_state[gameport_nr][joystick_nr].pov_mapping[c / 2][c & 1]; int nr_povs = plat_joystick_state[joystick].nr_povs; if (mapping & POV_X) cbox->setCurrentIndex((mapping & 3) * 2); diff --git a/src/qt/qt_joystickconfiguration.hpp b/src/qt/qt_joystickconfiguration.hpp index 0b185ad73..bff38992a 100644 --- a/src/qt/qt_joystickconfiguration.hpp +++ b/src/qt/qt_joystickconfiguration.hpp @@ -11,7 +11,7 @@ class JoystickConfiguration : public QDialog { Q_OBJECT public: - explicit JoystickConfiguration(int type, int joystick_nr, QWidget *parent = nullptr); + explicit JoystickConfiguration(int type, uint8_t gameport_nr, int joystick_nr, QWidget *parent = nullptr); ~JoystickConfiguration(); int selectedDevice(); @@ -25,6 +25,7 @@ private: Ui::JoystickConfiguration *ui; QList widgets; int type; + int gameport_nr; int joystick_nr; }; diff --git a/src/qt/qt_settingsinput.cpp b/src/qt/qt_settingsinput.cpp index d8cc7c029..50e3662e9 100644 --- a/src/qt/qt_settingsinput.cpp +++ b/src/qt/qt_settingsinput.cpp @@ -8,8 +8,6 @@ * * Mouse/Joystick configuration UI module. * - * - * * Authors: Joakim L. Gilje * * Copyright 2021 Joakim L. Gilje @@ -95,7 +93,7 @@ SettingsInput::save() keyboard_type = ui->comboBoxKeyboard->currentData().toInt(); mouse_type = ui->comboBoxMouse->currentData().toInt(); - joystick_type = ui->comboBoxJoystick->currentData().toInt(); + joystick_type[0] = ui->comboBoxJoystick0->currentData().toInt(); // Copy accelerators from working set to global set for(int x = 0; x < NUM_ACCELS; x++) { @@ -179,21 +177,22 @@ SettingsInput::onCurrentMachineChanged(int machineId) ui->comboBoxMouse->setCurrentIndex(-1); ui->comboBoxMouse->setCurrentIndex(selectedRow); + // Joysticks int i = 0; const char *joyName = joystick_get_name(i); - auto *joystickModel = ui->comboBoxJoystick->model(); + auto *joystickModel = ui->comboBoxJoystick0->model(); removeRows = joystickModel->rowCount(); selectedRow = 0; while (joyName) { int row = Models::AddEntry(joystickModel, tr(joyName).toUtf8().data(), i); - if (i == joystick_type) + if (i == joystick_type[0]) selectedRow = row - removeRows; ++i; joyName = joystick_get_name(i); } joystickModel->removeRows(0, removeRows); - ui->comboBoxJoystick->setCurrentIndex(selectedRow); + ui->comboBoxJoystick0->setCurrentIndex(selectedRow); } void @@ -309,11 +308,11 @@ SettingsInput::on_comboBoxMouse_currentIndexChanged(int index) } void -SettingsInput::on_comboBoxJoystick_currentIndexChanged(int index) +SettingsInput::on_comboBoxJoystick0_currentIndexChanged(int index) { - int joystickId = ui->comboBoxJoystick->currentData().toInt(); + int joystickId = ui->comboBoxJoystick0->currentData().toInt(); for (int i = 0; i < MAX_JOYSTICKS; ++i) { - auto *btn = findChild(QString("pushButtonJoystick%1").arg(i + 1)); + auto *btn = findChild(QString("pushButtonJoystick0%1").arg(i + 1)); if (btn == nullptr) continue; @@ -336,10 +335,10 @@ SettingsInput::on_pushButtonConfigureMouse_clicked() } static int -get_axis(JoystickConfiguration &jc, int axis, int joystick_nr) +get_axis(JoystickConfiguration &jc, int axis, uint8_t gameport_nr, int joystick_nr) { int axis_sel = jc.selectedAxis(axis); - int nr_axes = plat_joystick_state[joystick_state[0][joystick_nr].plat_joystick_nr - 1].nr_axes; + int nr_axes = plat_joystick_state[joystick_state[gameport_nr][joystick_nr].plat_joystick_nr - 1].nr_axes; if (axis_sel < nr_axes) return axis_sel; @@ -352,10 +351,10 @@ get_axis(JoystickConfiguration &jc, int axis, int joystick_nr) } static int -get_pov(JoystickConfiguration &jc, int pov, int joystick_nr) +get_pov(JoystickConfiguration &jc, int pov, uint8_t gameport_nr, int joystick_nr) { int pov_sel = jc.selectedPov(pov); - int nr_povs = plat_joystick_state[joystick_state[0][joystick_nr].plat_joystick_nr - 1].nr_povs * 2; + int nr_povs = plat_joystick_state[joystick_state[gameport_nr][joystick_nr].plat_joystick_nr - 1].nr_povs * 2; if (pov_sel < nr_povs) { if (pov_sel & 1) @@ -368,9 +367,9 @@ get_pov(JoystickConfiguration &jc, int pov, int joystick_nr) } static void -updateJoystickConfig(int type, int joystick_nr, QWidget *parent) +updateJoystickConfig(int type, uint8_t gameport_nr, int joystick_nr, QWidget *parent) { - JoystickConfiguration jc(type, joystick_nr, parent); + JoystickConfiguration jc(type, gameport_nr, joystick_nr, parent); switch (jc.exec()) { case QDialog::Rejected: return; @@ -378,43 +377,43 @@ updateJoystickConfig(int type, int joystick_nr, QWidget *parent) break; } - joystick_state[0][joystick_nr].plat_joystick_nr = jc.selectedDevice(); - if (joystick_state[0][joystick_nr].plat_joystick_nr) { + joystick_state[gameport_nr][joystick_nr].plat_joystick_nr = jc.selectedDevice(); + if (joystick_state[gameport_nr][joystick_nr].plat_joystick_nr) { for (int axis_nr = 0; axis_nr < joystick_get_axis_count(type); axis_nr++) { - joystick_state[0][joystick_nr].axis_mapping[axis_nr] = get_axis(jc, axis_nr, joystick_nr); + joystick_state[gameport_nr][joystick_nr].axis_mapping[axis_nr] = get_axis(jc, axis_nr, gameport_nr, joystick_nr); } for (int button_nr = 0; button_nr < joystick_get_button_count(type); button_nr++) { - joystick_state[0][joystick_nr].button_mapping[button_nr] = jc.selectedButton(button_nr); + joystick_state[gameport_nr][joystick_nr].button_mapping[button_nr] = jc.selectedButton(button_nr); } for (int pov_nr = 0; pov_nr < joystick_get_pov_count(type) * 2; pov_nr += 2) { - joystick_state[0][joystick_nr].pov_mapping[pov_nr][0] = get_pov(jc, pov_nr, joystick_nr); - joystick_state[0][joystick_nr].pov_mapping[pov_nr][1] = get_pov(jc, pov_nr + 1, joystick_nr); + joystick_state[gameport_nr][joystick_nr].pov_mapping[pov_nr][0] = get_pov(jc, pov_nr, gameport_nr, joystick_nr); + joystick_state[gameport_nr][joystick_nr].pov_mapping[pov_nr][1] = get_pov(jc, pov_nr + 1, gameport_nr, joystick_nr); } } } void -SettingsInput::on_pushButtonJoystick1_clicked() +SettingsInput::on_pushButtonJoystick01_clicked() { - updateJoystickConfig(ui->comboBoxJoystick->currentData().toInt(), 0, this); + updateJoystickConfig(ui->comboBoxJoystick0->currentData().toInt(), 0, 0, this); } void -SettingsInput::on_pushButtonJoystick2_clicked() +SettingsInput::on_pushButtonJoystick02_clicked() { - updateJoystickConfig(ui->comboBoxJoystick->currentData().toInt(), 1, this); + updateJoystickConfig(ui->comboBoxJoystick0->currentData().toInt(), 0, 1, this); } void -SettingsInput::on_pushButtonJoystick3_clicked() +SettingsInput::on_pushButtonJoystick03_clicked() { - updateJoystickConfig(ui->comboBoxJoystick->currentData().toInt(), 2, this); + updateJoystickConfig(ui->comboBoxJoystick0->currentData().toInt(), 0, 2, this); } void -SettingsInput::on_pushButtonJoystick4_clicked() +SettingsInput::on_pushButtonJoystick04_clicked() { - updateJoystickConfig(ui->comboBoxJoystick->currentData().toInt(), 3, this); + updateJoystickConfig(ui->comboBoxJoystick0->currentData().toInt(), 0, 3, this); } diff --git a/src/qt/qt_settingsinput.hpp b/src/qt/qt_settingsinput.hpp index 9a7702b4b..d5e8d58e1 100644 --- a/src/qt/qt_settingsinput.hpp +++ b/src/qt/qt_settingsinput.hpp @@ -32,11 +32,11 @@ private slots: void on_comboBoxMouse_currentIndexChanged(int index); void on_pushButtonConfigureMouse_clicked(); - void on_comboBoxJoystick_currentIndexChanged(int index); - void on_pushButtonJoystick1_clicked(); - void on_pushButtonJoystick2_clicked(); - void on_pushButtonJoystick3_clicked(); - void on_pushButtonJoystick4_clicked(); + void on_comboBoxJoystick0_currentIndexChanged(int index); + void on_pushButtonJoystick01_clicked(); + void on_pushButtonJoystick02_clicked(); + void on_pushButtonJoystick03_clicked(); + void on_pushButtonJoystick04_clicked(); void on_tableKeys_cellDoubleClicked(int row, int col); void on_tableKeys_currentCellChanged(int currentRow, int currentColumn, int previousRow, int previousColumn); diff --git a/src/qt/qt_settingsinput.ui b/src/qt/qt_settingsinput.ui index 8436c3a9d..02b3e388c 100644 --- a/src/qt/qt_settingsinput.ui +++ b/src/qt/qt_settingsinput.ui @@ -90,42 +90,42 @@ - + Joystick: - + 30 - + Joystick 1... - + Joystick 2... - + Joystick 3... - + Joystick 4... diff --git a/src/qt/sdl_joystick.c b/src/qt/sdl_joystick.c index 75234bca3..f1ba0380f 100644 --- a/src/qt/sdl_joystick.c +++ b/src/qt/sdl_joystick.c @@ -91,7 +91,7 @@ joystick_close(void) } static int -joystick_get_axis(int gameport, int joystick_nr, int mapping) +joystick_get_axis(int joystick_nr, int mapping) { if (mapping & POV_X) { switch (plat_joystick_state[joystick_nr].p[mapping & 3]) { @@ -128,9 +128,9 @@ joystick_get_axis(int gameport, int joystick_nr, int mapping) } void -joystick_process(void) +joystick_process(uint8_t gp) { - if (!joystick_type) + if (!joystick_type[gp]) return; SDL_JoystickUpdate(); @@ -146,44 +146,44 @@ joystick_process(void) #if 0 pclog("joystick %i - x=%i y=%i b[0]=%i b[1]=%i %i\n", js, - joystick_state[0][js].x, - joystick_state[0][js].y, - joystick_state[0][js].b[0], - joystick_state[0][js].b[1], - joysticks_present); + joystick_state[gp][js].x, + joystick_state[gp][js].y, + joystick_state[gp][js].b[0], + joystick_state[gp][js].b[1], + joysticks_present[gp]); #endif } - for (int js = 0; js < joystick_get_max_joysticks(joystick_type); js++) { - if (joystick_state[0][js].plat_joystick_nr) { - int joystick_nr = joystick_state[0][js].plat_joystick_nr - 1; + for (int js = 0; js < joystick_get_max_joysticks(joystick_type[gp]); js++) { + if (joystick_state[gp][js].plat_joystick_nr) { + int joystick_nr = joystick_state[gp][js].plat_joystick_nr - 1; - for (int axis_nr = 0; axis_nr < joystick_get_axis_count(joystick_type); axis_nr++) - joystick_state[0][js].axis[axis_nr] = joystick_get_axis(0, joystick_nr, joystick_state[0][js].axis_mapping[axis_nr]); + for (int axis_nr = 0; axis_nr < joystick_get_axis_count(joystick_type[gp]); axis_nr++) + joystick_state[gp][js].axis[axis_nr] = joystick_get_axis(joystick_nr, joystick_state[gp][js].axis_mapping[axis_nr]); - for (int button_nr = 0; button_nr < joystick_get_button_count(joystick_type); button_nr++) - joystick_state[0][js].button[button_nr] = plat_joystick_state[joystick_nr].b[joystick_state[0][js].button_mapping[button_nr]]; + for (int button_nr = 0; button_nr < joystick_get_button_count(joystick_type[gp]); button_nr++) + joystick_state[gp][js].button[button_nr] = plat_joystick_state[joystick_nr].b[joystick_state[gp][js].button_mapping[button_nr]]; - for (int pov_nr = 0; pov_nr < joystick_get_pov_count(joystick_type); pov_nr++) { - int x = joystick_get_axis(0, joystick_nr, joystick_state[0][js].pov_mapping[pov_nr][0]); - int y = joystick_get_axis(0, joystick_nr, joystick_state[0][js].pov_mapping[pov_nr][1]); + for (int pov_nr = 0; pov_nr < joystick_get_pov_count(joystick_type[gp]); pov_nr++) { + int x = joystick_get_axis(joystick_nr, joystick_state[gp][js].pov_mapping[pov_nr][0]); + int y = joystick_get_axis(joystick_nr, joystick_state[gp][js].pov_mapping[pov_nr][1]); double angle = (atan2((double) y, (double) x) * 360.0) / (2 * M_PI); double magnitude = sqrt((double) x * (double) x + (double) y * (double) y); if (magnitude < 16384) - joystick_state[0][js].pov[pov_nr] = -1; + joystick_state[gp][js].pov[pov_nr] = -1; else - joystick_state[0][js].pov[pov_nr] = ((int) angle + 90 + 360) % 360; + joystick_state[gp][js].pov[pov_nr] = ((int) angle + 90 + 360) % 360; } } else { - for (int axis_nr = 0; axis_nr < joystick_get_axis_count(joystick_type); axis_nr++) - joystick_state[0][js].axis[axis_nr] = 0; + for (int axis_nr = 0; axis_nr < joystick_get_axis_count(joystick_type[gp]); axis_nr++) + joystick_state[gp][js].axis[axis_nr] = 0; - for (int button_nr = 0; button_nr < joystick_get_button_count(joystick_type); button_nr++) - joystick_state[0][js].button[button_nr] = 0; + for (int button_nr = 0; button_nr < joystick_get_button_count(joystick_type[gp]); button_nr++) + joystick_state[gp][js].button[button_nr] = 0; - for (int pov_nr = 0; pov_nr < joystick_get_pov_count(joystick_type); pov_nr++) - joystick_state[0][js].pov[pov_nr] = -1; + for (int pov_nr = 0; pov_nr < joystick_get_pov_count(joystick_type[gp]); pov_nr++) + joystick_state[gp][js].pov[pov_nr] = -1; } } } diff --git a/src/qt/win_joystick_rawinput.c b/src/qt/win_joystick_rawinput.c index c775b4bb3..24ad87f63 100644 --- a/src/qt/win_joystick_rawinput.c +++ b/src/qt/win_joystick_rawinput.c @@ -454,7 +454,7 @@ win_joystick_handle(PRAWINPUT raw) plat_joystick_state[j].a[axis_nr] = value; #if 0 - joystick_log("%s %-06d ", plat_joystick_state[0][j].axis[axis_nr].name, plat_joystick_state[j].a[axis_nr]); + joystick_log("%s %-06d ", plat_joystick_state[j].axis[axis_nr].name, plat_joystick_state[j].a[axis_nr]); #endif } @@ -476,7 +476,7 @@ win_joystick_handle(PRAWINPUT raw) plat_joystick_state[j].p[pov_nr] = value; #if 0 - joystick_log("%s %-3d ", plat_joystick_state[0][j].pov[pov_nr].name, plat_joystick_state[j].p[pov_nr]); + joystick_log("%s %-3d ", plat_joystick_state[j].pov[pov_nr].name, plat_joystick_state[j].p[pov_nr]); #endif } #if 0 @@ -505,41 +505,41 @@ joystick_get_axis(int joystick_nr, int mapping) } void -joystick_process(void) +joystick_process(uint8_t gp) { - if (joystick_type == JS_TYPE_NONE) + if (joystick_type[gp] == JS_TYPE_NONE) return; - for (int js = 0; js < joystick_get_max_joysticks(joystick_type); js++) { - if (joystick_state[0][js].plat_joystick_nr) { - int joystick_nr = joystick_state[0][js].plat_joystick_nr - 1; + for (int js = 0; js < joystick_get_max_joysticks(joystick_type[gp]); js++) { + if (joystick_state[gp][js].plat_joystick_nr) { + int joystick_nr = joystick_state[gp][js].plat_joystick_nr - 1; - for (int axis_nr = 0; axis_nr < joystick_get_axis_count(joystick_type); axis_nr++) - joystick_state[0][js].axis[axis_nr] = joystick_get_axis(joystick_nr, joystick_state[0][js].axis_mapping[axis_nr]); + for (int axis_nr = 0; axis_nr < joystick_get_axis_count(joystick_type[gp]); axis_nr++) + joystick_state[gp][js].axis[axis_nr] = joystick_get_axis(joystick_nr, joystick_state[gp][js].axis_mapping[axis_nr]); - for (int button_nr = 0; button_nr < joystick_get_button_count(joystick_type); button_nr++) - joystick_state[0][js].button[button_nr] = plat_joystick_state[joystick_nr].b[joystick_state[0][js].button_mapping[button_nr]]; + for (int button_nr = 0; button_nr < joystick_get_button_count(joystick_type[gp]); button_nr++) + joystick_state[gp][js].button[button_nr] = plat_joystick_state[joystick_nr].b[joystick_state[gp][js].button_mapping[button_nr]]; - for (int pov_nr = 0; pov_nr < joystick_get_pov_count(joystick_type); pov_nr++) { - int x = joystick_get_axis(joystick_nr, joystick_state[0][js].pov_mapping[pov_nr][0]); - int y = joystick_get_axis(joystick_nr, joystick_state[0][js].pov_mapping[pov_nr][1]); + for (int pov_nr = 0; pov_nr < joystick_get_pov_count(joystick_type[gp]); pov_nr++) { + int x = joystick_get_axis(joystick_nr, joystick_state[gp][js].pov_mapping[pov_nr][0]); + int y = joystick_get_axis(joystick_nr, joystick_state[gp][js].pov_mapping[pov_nr][1]); double angle = (atan2((double) y, (double) x) * 360.0) / (2 * M_PI); double magnitude = sqrt((double) x * (double) x + (double) y * (double) y); if (magnitude < 16384) - joystick_state[0][js].pov[pov_nr] = -1; + joystick_state[gp][js].pov[pov_nr] = -1; else - joystick_state[0][js].pov[pov_nr] = ((int) angle + 90 + 360) % 360; + joystick_state[gp][js].pov[pov_nr] = ((int) angle + 90 + 360) % 360; } } else { - for (int axis_nr = 0; axis_nr < joystick_get_axis_count(joystick_type); axis_nr++) - joystick_state[0][js].axis[axis_nr] = 0; + for (int axis_nr = 0; axis_nr < joystick_get_axis_count(joystick_type[gp]); axis_nr++) + joystick_state[gp][js].axis[axis_nr] = 0; - for (int button_nr = 0; button_nr < joystick_get_button_count(joystick_type); button_nr++) - joystick_state[0][js].button[button_nr] = 0; + for (int button_nr = 0; button_nr < joystick_get_button_count(joystick_type[gp]); button_nr++) + joystick_state[gp][js].button[button_nr] = 0; - for (int pov_nr = 0; pov_nr < joystick_get_pov_count(joystick_type); pov_nr++) - joystick_state[0][js].pov[pov_nr] = -1; + for (int pov_nr = 0; pov_nr < joystick_get_pov_count(joystick_type[gp]); pov_nr++) + joystick_state[gp][js].pov[pov_nr] = -1; } } } diff --git a/src/unix/unix.c b/src/unix/unix.c index d35543548..bc9b9fedf 100644 --- a/src/unix/unix.c +++ b/src/unix/unix.c @@ -1484,7 +1484,7 @@ joystick_close(void) } void -joystick_process(void) +joystick_process(uint8_t gp) { /* No-op. */ } From b04d9e1b06dac71dde577c39f2cb2d728fa13dd7 Mon Sep 17 00:00:00 2001 From: Jasmine Iwanek Date: Wed, 13 Aug 2025 00:45:15 -0400 Subject: [PATCH 2/5] Clean up some tabs to spaces --- src/86box.c | 79 ++++++++++++++++++++++--------------- src/qt/qt_settingsinput.hpp | 10 ++--- 2 files changed, 52 insertions(+), 37 deletions(-) diff --git a/src/86box.c b/src/86box.c index bb3f3dc3e..cf060854d 100644 --- a/src/86box.c +++ b/src/86box.c @@ -234,29 +234,46 @@ struct accelKey acc_keys[NUM_ACCELS]; // Default accelerator key values struct accelKey def_acc_keys[NUM_ACCELS] = { - { .name="send_ctrl_alt_del", .desc="Send Control+Alt+Del", - .seq="Ctrl+F12" }, - - { .name="send_ctrl_alt_esc", .desc="Send Control+Alt+Escape", - .seq="Ctrl+F10" }, - - { .name="fullscreen", .desc="Toggle fullscreen", - .seq="Ctrl+Alt+PgUp" }, - - { .name="screenshot", .desc="Screenshot", - .seq="Ctrl+F11" }, - - { .name="release_mouse", .desc="Release mouse pointer", - .seq="Ctrl+End" }, - - { .name="hard_reset", .desc="Hard reset", - .seq="Ctrl+Alt+F12" }, - - { .name="pause", .desc="Toggle pause", - .seq="Ctrl+Alt+F1" }, - - { .name="mute", .desc="Toggle mute", - .seq="Ctrl+Alt+M" } + { + .name="send_ctrl_alt_del", + .desc="Send Control+Alt+Del", + .seq="Ctrl+F12" + }, + { + .name="send_ctrl_alt_esc", + .desc="Send Control+Alt+Escape", + .seq="Ctrl+F10" + }, + { + .name="fullscreen", + .desc="Toggle fullscreen", + .seq="Ctrl+Alt+PgUp" + }, + { + .name="screenshot", + .desc="Screenshot", + .seq="Ctrl+F11" + }, + { + .name="release_mouse", + .desc="Release mouse pointer", + .seq="Ctrl+End" + }, + { + .name="hard_reset", + .desc="Hard reset", + .seq="Ctrl+Alt+F12" + }, + { + .name="pause", + .desc="Toggle pause", + .seq="Ctrl+Alt+F1" + }, + { + .name="mute", + .desc="Toggle mute", + .seq="Ctrl+Alt+M" + } }; char vmm_path[1024] = { '\0' }; /* VM manager path to scan for VMs */ @@ -2008,13 +2025,11 @@ do_pause(int p) // Helper to find an accelerator key and return it's index in acc_keys int FindAccelerator(const char *name) { - for(int x=0;x Date: Sat, 23 Aug 2025 10:07:03 -0300 Subject: [PATCH 3/5] Manager: Don't clear path if the file dialog is closed --- src/qt/qt_vmmanager_preferences.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/qt/qt_vmmanager_preferences.cpp b/src/qt/qt_vmmanager_preferences.cpp index 0f01fb45e..49b7b4a1f 100644 --- a/src/qt/qt_vmmanager_preferences.cpp +++ b/src/qt/qt_vmmanager_preferences.cpp @@ -78,7 +78,8 @@ void VMManagerPreferences::chooseDirectoryLocation() { const auto directory = QFileDialog::getExistingDirectory(this, tr("Choose directory"), ui->systemDirectory->text()); - ui->systemDirectory->setText(QDir::toNativeSeparators(directory)); + if (!directory.isEmpty()) + ui->systemDirectory->setText(QDir::toNativeSeparators(directory)); } void From 5de3af3df71b9d42dfb84eb58c93ecc91f90d84f Mon Sep 17 00:00:00 2001 From: Alexander Babikov Date: Tue, 5 Aug 2025 20:16:09 +0500 Subject: [PATCH 4/5] Manager: Fix style not reacting to Windows light/dark mode change Add a native event filter for dark mode update, move the function that queries the current theme to qt_util.cpp and make widgets with custom stylesheets update their style on update --- src/qt/CMakeLists.txt | 2 + src/qt/qt_main.cpp | 22 +++-- src/qt/qt_styleoverride.cpp | 4 +- src/qt/qt_util.cpp | 31 +++++++ src/qt/qt_util.hpp | 1 + src/qt/qt_vmmanager_details.cpp | 67 +++++++++++---- src/qt/qt_vmmanager_details.hpp | 10 +++ src/qt/qt_vmmanager_detailsection.cpp | 37 +++++++-- src/qt/qt_vmmanager_detailsection.hpp | 4 + src/qt/qt_vmmanager_listviewdelegate.cpp | 7 +- src/qt/qt_vmmanager_main.cpp | 10 ++- src/qt/qt_vmmanager_main.hpp | 3 + src/qt/qt_vmmanager_mainwindow.cpp | 12 +++ src/qt/qt_vmmanager_mainwindow.hpp | 6 ++ src/qt/qt_vmmanager_windarkmodefilter.cpp | 99 +++++++++++++++++++++++ src/qt/qt_vmmanager_windarkmodefilter.hpp | 47 +++++++++++ src/qt/qt_winrawinputfilter.cpp | 39 +++------ 17 files changed, 337 insertions(+), 64 deletions(-) create mode 100644 src/qt/qt_vmmanager_windarkmodefilter.cpp create mode 100644 src/qt/qt_vmmanager_windarkmodefilter.hpp diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt index 1e3a5bdd4..1a004d958 100644 --- a/src/qt/CMakeLists.txt +++ b/src/qt/CMakeLists.txt @@ -374,6 +374,8 @@ if (WIN32) target_sources(ui PRIVATE qt_winrawinputfilter.hpp qt_winrawinputfilter.cpp + qt_vmmanager_windarkmodefilter.hpp + qt_vmmanager_windarkmodefilter.cpp qt_winmanagerfilter.hpp qt_winmanagerfilter.cpp ) diff --git a/src/qt/qt_main.cpp b/src/qt/qt_main.cpp index 3dd63389e..6a2ba1a3a 100644 --- a/src/qt/qt_main.cpp +++ b/src/qt/qt_main.cpp @@ -66,6 +66,7 @@ extern "C" { # include "qt_rendererstack.hpp" # include "qt_winrawinputfilter.hpp" # include "qt_winmanagerfilter.hpp" +# include "qt_vmmanager_windarkmodefilter.hpp" # include <86box/win.h> # include # include @@ -514,10 +515,6 @@ main_thread_fn() static std::thread *main_thread; -#ifdef Q_OS_WINDOWS -extern bool windows_is_light_theme(); -#endif - int main(int argc, char *argv[]) { @@ -548,7 +545,7 @@ main(int argc, char *argv[]) } QApplication::setAttribute(Qt::AA_NativeWindows); - if (!windows_is_light_theme()) { + if (!util::isWindowsLightTheme()) { QFile f(":qdarkstyle/dark/darkstyle.qss"); if (!f.exists()) { @@ -558,6 +555,10 @@ main(int argc, char *argv[]) QTextStream ts(&f); qApp->setStyleSheet(ts.readAll()); } + QPalette palette(qApp->palette()); + palette.setColor(QPalette::Link, Qt::white); + palette.setColor(QPalette::LinkVisited, Qt::lightGray); + qApp->setPalette(palette); } #endif @@ -632,8 +633,19 @@ main(int argc, char *argv[]) // QApplication::setApplicationDisplayName("86Box VM Manager"); // vmm.show(); // vmm.exec(); +#ifdef Q_OS_WINDOWS + auto darkModeFilter = std::unique_ptr(new WindowsDarkModeFilter()); + if (darkModeFilter) { + qApp->installNativeEventFilter(darkModeFilter.get()); + } + QTimer::singleShot(0, [&darkModeFilter] { +#else QTimer::singleShot(0, [] { +#endif const auto vmm_main_window = new VMManagerMainWindow(); +#ifdef Q_OS_WINDOWS + darkModeFilter.get()->setWindow(vmm_main_window); +#endif vmm_main_window->show(); }); QApplication::exec(); diff --git a/src/qt/qt_styleoverride.cpp b/src/qt/qt_styleoverride.cpp index 1319f0dae..2da0342bb 100644 --- a/src/qt/qt_styleoverride.cpp +++ b/src/qt/qt_styleoverride.cpp @@ -15,6 +15,7 @@ * Copyright 2022 Teemu Korhonen */ #include "qt_styleoverride.hpp" +#include "qt_util.hpp" #include #include @@ -64,8 +65,7 @@ StyleOverride::polish(QWidget *widget) } widget->setWindowFlag(Qt::WindowContextHelpButtonHint, false); #ifdef Q_OS_WINDOWS - extern bool windows_is_light_theme(); - BOOL DarkMode = !windows_is_light_theme(); + BOOL DarkMode = !util::isWindowsLightTheme(); DwmSetWindowAttribute((HWND)widget->winId(), DWMWA_USE_IMMERSIVE_DARK_MODE, (LPCVOID)&DarkMode, sizeof(DarkMode)); #endif } diff --git a/src/qt/qt_util.cpp b/src/qt/qt_util.cpp index bc9b9f1f8..201b61b3f 100644 --- a/src/qt/qt_util.cpp +++ b/src/qt/qt_util.cpp @@ -27,6 +27,7 @@ #include "qt_util.hpp" #ifdef Q_OS_WINDOWS +# include # include # ifndef DWMWA_WINDOW_CORNER_PREFERENCE # define DWMWA_WINDOW_CORNER_PREFERENCE 33 @@ -62,6 +63,36 @@ screenOfWidget(QWidget *widget) } #ifdef Q_OS_WINDOWS + +bool +isWindowsLightTheme(void) { + // based on https://stackoverflow.com/questions/51334674/how-to-detect-windows-10-light-dark-mode-in-win32-application + + // The value is expected to be a REG_DWORD, which is a signed 32-bit little-endian + auto buffer = std::vector(4); + auto cbData = static_cast(buffer.size() * sizeof(char)); + auto res = RegGetValueW( + HKEY_CURRENT_USER, + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", + L"AppsUseLightTheme", + RRF_RT_REG_DWORD, // expected value type + nullptr, + buffer.data(), + &cbData); + + if (res != ERROR_SUCCESS) { + return 1; + } + + // convert bytes written to our buffer to an int, assuming little-endian + auto i = int(buffer[3] << 24 | + buffer[2] << 16 | + buffer[1] << 8 | + buffer[0]); + + return i == 1; +} + void setWin11RoundedCorners(WId hwnd, bool enable) { diff --git a/src/qt/qt_util.hpp b/src/qt/qt_util.hpp index de3457a88..a2ca44425 100644 --- a/src/qt/qt_util.hpp +++ b/src/qt/qt_util.hpp @@ -15,6 +15,7 @@ QString DlgFilter(QStringList extensions, bool last = false); /* Returns screen the widget is on */ QScreen *screenOfWidget(QWidget *widget); #ifdef Q_OS_WINDOWS +bool isWindowsLightTheme(void); void setWin11RoundedCorners(WId hwnd, bool enable); #endif QString currentUuid(); diff --git a/src/qt/qt_vmmanager_details.cpp b/src/qt/qt_vmmanager_details.cpp index 56cfa8758..65608cf0a 100644 --- a/src/qt/qt_vmmanager_details.cpp +++ b/src/qt/qt_vmmanager_details.cpp @@ -19,12 +19,19 @@ #include #include +#include "qt_util.hpp" #include "qt_vmmanager_details.hpp" #include "ui_qt_vmmanager_details.h" +#define TOOLBUTTON_STYLESHEET_LIGHT "QToolButton {background: transparent; border: none; padding: 5px} QToolButton:hover {background: palette(midlight)} QToolButton:pressed {background: palette(mid)}" #ifdef Q_OS_WINDOWS -extern bool windows_is_light_theme(); +# define TOOLBUTTON_STYLESHEET_DARK "QToolButton {padding: 5px}" +# define SCREENSHOTBORDER_STYLESHEET_DARK "QLabel { border: 1px solid gray }" +#else +# define TOOLBUTTON_STYLESHEET_DARK "QToolButton {background: transparent; border: none; padding: 5px} QToolButton:hover {background: palette(dark)} QToolButton:pressed {background: palette(mid)}" #endif +#define SCROLLAREA_STYLESHEET_LIGHT "QWidget {background-color: palette(light)} QScrollBar{ background-color: none }" +#define SYSTEMLABEL_STYLESHEET_LIGHT "background-color: palette(midlight);" using namespace VMManager; @@ -100,18 +107,14 @@ VMManagerDetails::VMManagerDetails(QWidget *parent) : QString toolButtonStyleSheet; // Simple method to try and determine if light mode is enabled #ifdef Q_OS_WINDOWS - const bool lightMode = windows_is_light_theme(); + const bool lightMode = util::isWindowsLightTheme(); #else const bool lightMode = QApplication::palette().window().color().value() > QApplication::palette().windowText().color().value(); #endif if (lightMode) { - toolButtonStyleSheet = "QToolButton {background: transparent; border: none; padding: 5px} QToolButton:hover {background: palette(midlight)} QToolButton:pressed {background: palette(mid)}"; + toolButtonStyleSheet = TOOLBUTTON_STYLESHEET_LIGHT; } else { -#ifndef Q_OS_WINDOWS - toolButtonStyleSheet = "QToolButton {background: transparent; border: none; padding: 5px} QToolButton:hover {background: palette(dark)} QToolButton:pressed {background: palette(mid)}"; -#else - toolButtonStyleSheet = "QToolButton {padding: 5px}"; -#endif + toolButtonStyleSheet = TOOLBUTTON_STYLESHEET_DARK; } ui->ssNavTBHolder->setStyleSheet(toolButtonStyleSheet); @@ -150,6 +153,17 @@ VMManagerDetails::VMManagerDetails(QWidget *parent) : ui->notesTextEdit->setEnabled(false); +#ifdef Q_OS_WINDOWS + connect(this, &VMManagerDetails::styleUpdated, systemSection, &VMManagerDetailSection::updateStyle); + connect(this, &VMManagerDetails::styleUpdated, videoSection, &VMManagerDetailSection::updateStyle); + connect(this, &VMManagerDetails::styleUpdated, storageSection, &VMManagerDetailSection::updateStyle); + connect(this, &VMManagerDetails::styleUpdated, audioSection, &VMManagerDetailSection::updateStyle); + connect(this, &VMManagerDetails::styleUpdated, networkSection, &VMManagerDetailSection::updateStyle); + connect(this, &VMManagerDetails::styleUpdated, inputSection, &VMManagerDetailSection::updateStyle); + connect(this, &VMManagerDetails::styleUpdated, portsSection, &VMManagerDetailSection::updateStyle); + connect(this, &VMManagerDetails::styleUpdated, otherSection, &VMManagerDetailSection::updateStyle); +#endif + sysconfig = new VMManagerSystem(); } @@ -163,12 +177,11 @@ VMManagerDetails::updateData(VMManagerSystem *passed_sysconfig) { // Set the scrollarea background but also set the scroll bar to none. Otherwise it will also // set the scrollbar background to the same. #ifdef Q_OS_WINDOWS - extern bool windows_is_light_theme(); - if (windows_is_light_theme()) + if (util::isWindowsLightTheme()) #endif { - ui->scrollArea->setStyleSheet("QWidget {background-color: palette(light)} QScrollBar{ background-color: none }"); - ui->systemLabel->setStyleSheet("background-color: palette(midlight);"); + ui->scrollArea->setStyleSheet(SCROLLAREA_STYLESHEET_LIGHT); + ui->systemLabel->setStyleSheet(SYSTEMLABEL_STYLESHEET_LIGHT); } // Margins are a little different on macos #ifdef Q_OS_MACOS @@ -331,8 +344,8 @@ VMManagerDetails::updateScreenshots(VMManagerSystem *passed_sysconfig) { ui->screenshot->setEnabled(false); ui->screenshot->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); #ifdef Q_OS_WINDOWS - if (!windows_is_light_theme()) { - ui->screenshot->setStyleSheet("QLabel { border: 1px solid gray }"); + if (!util::isWindowsLightTheme()) { + ui->screenshot->setStyleSheet(SCREENSHOTBORDER_STYLESHEET_DARK); } else { ui->screenshot->setStyleSheet(""); } @@ -390,6 +403,32 @@ VMManagerDetails::updateWindowStatus() updateProcessStatus(); } +#ifdef Q_OS_WINDOWS +void +VMManagerDetails::updateStyle() +{ + QString toolButtonStyleSheet; + const bool lightMode = util::isWindowsLightTheme(); + if (lightMode) { + toolButtonStyleSheet = TOOLBUTTON_STYLESHEET_LIGHT; + ui->scrollArea->setStyleSheet(SCROLLAREA_STYLESHEET_LIGHT); + ui->systemLabel->setStyleSheet(SYSTEMLABEL_STYLESHEET_LIGHT); + if (!ui->screenshot->isEnabled()) + ui->screenshot->setStyleSheet(""); + } else { + toolButtonStyleSheet = TOOLBUTTON_STYLESHEET_DARK; + ui->scrollArea->setStyleSheet(""); + ui->systemLabel->setStyleSheet(""); + if (!ui->screenshot->isEnabled()) + ui->screenshot->setStyleSheet(SCREENSHOTBORDER_STYLESHEET_DARK); + } + ui->ssNavTBHolder->setStyleSheet(toolButtonStyleSheet); + ui->toolButtonHolder->setStyleSheet(toolButtonStyleSheet); + + emit styleUpdated(); +} +#endif + QWidget * VMManagerDetails::createHorizontalLine(const int leftSpacing, const int rightSpacing) { diff --git a/src/qt/qt_vmmanager_details.hpp b/src/qt/qt_vmmanager_details.hpp index 0a6c2d35a..ac7cda35b 100644 --- a/src/qt/qt_vmmanager_details.hpp +++ b/src/qt/qt_vmmanager_details.hpp @@ -42,8 +42,18 @@ public: void updateProcessStatus(); void updateWindowStatus(); + +#ifdef Q_OS_WINDOWS + void updateStyle(); +#endif + // CollapseButton *systemCollapseButton; +#ifdef Q_OS_WINDOWS +signals: + void styleUpdated(); +#endif + private: Ui::VMManagerDetails *ui; VMManagerSystem *sysconfig; diff --git a/src/qt/qt_vmmanager_detailsection.cpp b/src/qt/qt_vmmanager_detailsection.cpp index 043342f1e..ab9a4b5ff 100644 --- a/src/qt/qt_vmmanager_detailsection.cpp +++ b/src/qt/qt_vmmanager_detailsection.cpp @@ -19,6 +19,15 @@ #include "ui_qt_vmmanager_detailsection.h" #include +#include "qt_util.hpp" + +#define HEADER_STYLESHEET_LIGHT "background-color: palette(midlight);" +#ifdef Q_OS_WINDOWS +# define HEADER_STYLESHEET_DARK "background-color: #616161;" +# define BACKGROUND_STYLESHEET_DARK "background-color: #272727;" +#else +# define HEADER_STYLESHEET_DARK "background-color: palette(mid);" +#endif const QString VMManagerDetailSection::sectionSeparator = ";"; using namespace VMManager; @@ -40,21 +49,18 @@ VMManagerDetailSection(const QString §ionName) // Simple method to try and determine if light mode is enabled on the host #ifdef Q_OS_WINDOWS - extern bool windows_is_light_theme(); - const bool lightMode = windows_is_light_theme(); + const bool lightMode = util::isWindowsLightTheme(); #else const bool lightMode = QApplication::palette().window().color().value() > QApplication::palette().windowText().color().value(); #endif // Alternate layout - if ( lightMode) { - ui->collapseButtonHolder->setStyleSheet("background-color: palette(midlight);"); + if (lightMode) { + ui->collapseButtonHolder->setStyleSheet(HEADER_STYLESHEET_LIGHT); } else { #ifdef Q_OS_WINDOWS - ui->outerFrame->setStyleSheet("background-color: #272727;"); - ui->collapseButtonHolder->setStyleSheet("background-color: #616161;"); -#else - ui->collapseButtonHolder->setStyleSheet("background-color: palette(mid);"); + ui->outerFrame->setStyleSheet(BACKGROUND_STYLESHEET_DARK); #endif + ui->collapseButtonHolder->setStyleSheet(HEADER_STYLESHEET_DARK); } const auto sectionLabel = new QLabel(sectionName); sectionLabel->setStyleSheet(sectionLabel->styleSheet().append("font-weight: bold;")); @@ -214,6 +220,21 @@ VMManagerDetailSection::clear() ui->detailFrame->setLayout(frameGridLayout); } +#ifdef Q_OS_WINDOWS +void +VMManagerDetailSection::updateStyle() +{ + const bool lightMode = util::isWindowsLightTheme(); + if (lightMode) { + ui->outerFrame->setStyleSheet(""); + ui->collapseButtonHolder->setStyleSheet(HEADER_STYLESHEET_LIGHT); + } else { + ui->outerFrame->setStyleSheet(BACKGROUND_STYLESHEET_DARK); + ui->collapseButtonHolder->setStyleSheet(HEADER_STYLESHEET_DARK); + } +} +#endif + // QT for Linux and Windows doesn't have the same default margins as QT on MacOS. // For consistency in appearance we'll have to return the margins on a per-OS basis QMargins diff --git a/src/qt/qt_vmmanager_detailsection.hpp b/src/qt/qt_vmmanager_detailsection.hpp index a7da9470e..3df7ce64c 100644 --- a/src/qt/qt_vmmanager_detailsection.hpp +++ b/src/qt/qt_vmmanager_detailsection.hpp @@ -74,6 +74,10 @@ public: static const QString sectionSeparator; +#ifdef Q_OS_WINDOWS +public slots: + void updateStyle(); +#endif private: enum class MarginSection { diff --git a/src/qt/qt_vmmanager_listviewdelegate.cpp b/src/qt/qt_vmmanager_listviewdelegate.cpp index c5bcbbdd9..c5d2e1dc9 100644 --- a/src/qt/qt_vmmanager_listviewdelegate.cpp +++ b/src/qt/qt_vmmanager_listviewdelegate.cpp @@ -18,13 +18,10 @@ #include +#include "qt_util.hpp" #include "qt_vmmanager_listviewdelegate.hpp" #include "qt_vmmanager_model.hpp" -#ifdef Q_OS_WINDOWS -extern bool windows_is_light_theme(); -#endif - // Thanks to scopchanov https://github.com/scopchanov/SO-MessageLog // from https://stackoverflow.com/questions/53105343/is-it-possible-to-add-a-custom-widget-into-a-qlistview @@ -52,7 +49,7 @@ void VMManagerListViewDelegate::paint(QPainter *painter, const QStyleOptionViewI const QModelIndex &index) const { bool windows_light_mode = true; #ifdef Q_OS_WINDOWS - windows_light_mode = windows_is_light_theme(); + windows_light_mode = util::isWindowsLightTheme(); #endif QStyleOptionViewItem opt(option); initStyleOption(&opt, index); diff --git a/src/qt/qt_vmmanager_main.cpp b/src/qt/qt_vmmanager_main.cpp index 8859d5cec..f55c90389 100644 --- a/src/qt/qt_vmmanager_main.cpp +++ b/src/qt/qt_vmmanager_main.cpp @@ -349,7 +349,7 @@ illegal_chars: }); // Initial default details view - vm_details = new VMManagerDetails(); + vm_details = new VMManagerDetails(ui->detailsArea); ui->detailsArea->layout()->addWidget(vm_details); const QItemSelectionModel *selection_model = ui->listView->selectionModel(); @@ -785,6 +785,14 @@ VMManagerMain::onLanguageUpdated() vm_details->updateData(selected_sysconfig); } +#ifdef Q_OS_WINDOWS +void +VMManagerMain::onDarkModeUpdated() +{ + vm_details->updateStyle(); +} +#endif + int VMManagerMain::getActiveMachineCount() { diff --git a/src/qt/qt_vmmanager_main.hpp b/src/qt/qt_vmmanager_main.hpp index 8ec1129ae..be43da705 100644 --- a/src/qt/qt_vmmanager_main.hpp +++ b/src/qt/qt_vmmanager_main.hpp @@ -80,6 +80,9 @@ public slots: void modelDataChange(); void onPreferencesUpdated(); void onLanguageUpdated(); +#ifdef Q_OS_WINDOWS + void onDarkModeUpdated(); +#endif void onConfigUpdated(const QString &uuid); int getActiveMachineCount(); diff --git a/src/qt/qt_vmmanager_mainwindow.cpp b/src/qt/qt_vmmanager_mainwindow.cpp index af9f3082c..11eba670b 100644 --- a/src/qt/qt_vmmanager_mainwindow.cpp +++ b/src/qt/qt_vmmanager_mainwindow.cpp @@ -116,6 +116,9 @@ VMManagerMainWindow(QWidget *parent) // Inform the main view when preferences are updated connect(this, &VMManagerMainWindow::preferencesUpdated, vmm, &VMManagerMain::onPreferencesUpdated); connect(this, &VMManagerMainWindow::languageUpdated, vmm, &VMManagerMain::onLanguageUpdated); +#ifdef Q_OS_WINDOWS + connect(this, &VMManagerMainWindow::darkModeUpdated, vmm, &VMManagerMain::onDarkModeUpdated); +#endif } @@ -178,6 +181,15 @@ VMManagerMainWindow::updateLanguage() emit languageUpdated(); } + +#ifdef Q_OS_WINDOWS +void +VMManagerMainWindow::updateDarkMode() +{ + emit darkModeUpdated(); +} +#endif + void VMManagerMainWindow::changeEvent(QEvent *event) { diff --git a/src/qt/qt_vmmanager_mainwindow.hpp b/src/qt/qt_vmmanager_mainwindow.hpp index 2f2d34d25..bde74765c 100644 --- a/src/qt/qt_vmmanager_mainwindow.hpp +++ b/src/qt/qt_vmmanager_mainwindow.hpp @@ -37,6 +37,9 @@ public: signals: void preferencesUpdated(); void languageUpdated(); +#ifdef Q_OS_WINDOWS + void darkModeUpdated(); +#endif private: Ui::VMManagerMainWindow *ui; @@ -48,6 +51,9 @@ public slots: void setStatusLeft(const QString &text) const; void setStatusRight(const QString &text) const; void updateLanguage(); +#ifdef Q_OS_WINDOWS + void updateDarkMode(); +#endif private slots: void vmmSelectionChanged(const QModelIndex ¤tSelection, QProcess::ProcessState processState) const; diff --git a/src/qt/qt_vmmanager_windarkmodefilter.cpp b/src/qt/qt_vmmanager_windarkmodefilter.cpp new file mode 100644 index 000000000..195419ad9 --- /dev/null +++ b/src/qt/qt_vmmanager_windarkmodefilter.cpp @@ -0,0 +1,99 @@ +/* + * 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. + * + * Generic Windows native event filter for dark mode handling + * + * + * + * Authors: Teemu Korhonen + * Cacodemon345 + * + * Copyright 2021 Teemu Korhonen + * Copyright 2024-2025 Cacodemon345. + */ + +#include "qt_vmmanager_windarkmodefilter.hpp" + +#include +#include +#include +#include +#include + +#include +#include +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +#include <86box/86box.h> +#include <86box/plat.h> + +#include "qt_util.hpp" + +static bool NewDarkMode = FALSE; + +void +WindowsDarkModeFilter::setWindow(VMManagerMainWindow *window) +{ + this->window = window; +} + +bool +WindowsDarkModeFilter::nativeEventFilter(const QByteArray &eventType, void *message, result_t *result) +{ + if ((window != nullptr) && (eventType == "windows_generic_MSG")) { + MSG *msg = static_cast(message); + + if ((msg != nullptr) && (msg->message == WM_SETTINGCHANGE)) { + if ((((void *) msg->lParam) != nullptr) && + (wcscmp(L"ImmersiveColorSet", (wchar_t*)msg->lParam) == 0)) { + + bool OldDarkMode = NewDarkMode; + + if (!util::isWindowsLightTheme()) { + QFile f(":qdarkstyle/dark/darkstyle.qss"); + + if (!f.exists()) + printf("Unable to set stylesheet, file not found\n"); + else { + f.open(QFile::ReadOnly | QFile::Text); + QTextStream ts(&f); + qApp->setStyleSheet(ts.readAll()); + } + QPalette palette(qApp->palette()); + palette.setColor(QPalette::Link, Qt::white); + palette.setColor(QPalette::LinkVisited, Qt::lightGray); + qApp->setPalette(palette); + window->resize(window->size()); + + NewDarkMode = TRUE; + } else { + qApp->setStyleSheet(""); + QPalette palette(qApp->palette()); + palette.setColor(QPalette::Link, Qt::blue); + palette.setColor(QPalette::LinkVisited, Qt::magenta); + qApp->setPalette(palette); + window->resize(window->size()); + NewDarkMode = FALSE; + } + window->updateDarkMode(); + + if (NewDarkMode != OldDarkMode) QTimer::singleShot(1000, [this] () { + BOOL DarkMode = NewDarkMode; + DwmSetWindowAttribute((HWND) window->winId(), + DWMWA_USE_IMMERSIVE_DARK_MODE, + (LPCVOID) &DarkMode, + sizeof(DarkMode)); + }); + } + } + } + + return false; +} diff --git a/src/qt/qt_vmmanager_windarkmodefilter.hpp b/src/qt/qt_vmmanager_windarkmodefilter.hpp new file mode 100644 index 000000000..4f6b28a6e --- /dev/null +++ b/src/qt/qt_vmmanager_windarkmodefilter.hpp @@ -0,0 +1,47 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Header file for Windows dark mode native messages filter + * + * + * + * Authors: Teemu Korhonen + * + * Copyright 2022 Teemu Korhonen + */ + +#ifndef QT_WINDOWSDARKMODEEVENTFILTER_HPP +#define QT_WINDOWSDARKMODEEVENTFILTER_HPP + +#include +#include +#include +#include +#include + +#include "qt_vmmanager_mainwindow.hpp" + +#if QT_VERSION_MAJOR >= 6 +# define result_t qintptr +#else +# define result_t long +#endif + +class WindowsDarkModeFilter : public QObject, public QAbstractNativeEventFilter { + Q_OBJECT + +public: + WindowsDarkModeFilter() = default; + void setWindow(VMManagerMainWindow *window); + bool nativeEventFilter(const QByteArray &eventType, void *message, result_t *result) override; + +private: + VMManagerMainWindow *window; +}; + +#endif // QT_WINDOWSDARKMODEEVENTFILTER_HPP diff --git a/src/qt/qt_winrawinputfilter.cpp b/src/qt/qt_winrawinputfilter.cpp index eca77d15d..9b7adce1d 100644 --- a/src/qt/qt_winrawinputfilter.cpp +++ b/src/qt/qt_winrawinputfilter.cpp @@ -71,38 +71,11 @@ extern void win_keyboard_handle(uint32_t scancode, int up, int e0, int e1); #include #include "qt_rendererstack.hpp" +#include "qt_util.hpp" #include "ui_qt_mainwindow.h" static bool NewDarkMode = FALSE; -bool windows_is_light_theme() { - // based on https://stackoverflow.com/questions/51334674/how-to-detect-windows-10-light-dark-mode-in-win32-application - - // The value is expected to be a REG_DWORD, which is a signed 32-bit little-endian - auto buffer = std::vector(4); - auto cbData = static_cast(buffer.size() * sizeof(char)); - auto res = RegGetValueW( - HKEY_CURRENT_USER, - L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", - L"AppsUseLightTheme", - RRF_RT_REG_DWORD, // expected value type - nullptr, - buffer.data(), - &cbData); - - if (res != ERROR_SUCCESS) { - return 1; - } - - // convert bytes written to our buffer to an int, assuming little-endian - auto i = int(buffer[3] << 24 | - buffer[2] << 16 | - buffer[1] << 8 | - buffer[0]); - - return i == 1; -} - struct { HANDLE done_event = 0, ready_event = 0; @@ -365,7 +338,7 @@ WindowsRawInputFilter::nativeEventFilter(const QByteArray &eventType, void *mess } #endif - if (!windows_is_light_theme()) { + if (!util::isWindowsLightTheme()) { QFile f(":qdarkstyle/dark/darkstyle.qss"); if (!f.exists()) @@ -375,9 +348,17 @@ WindowsRawInputFilter::nativeEventFilter(const QByteArray &eventType, void *mess QTextStream ts(&f); qApp->setStyleSheet(ts.readAll()); } + QPalette palette(qApp->palette()); + palette.setColor(QPalette::Link, Qt::white); + palette.setColor(QPalette::LinkVisited, Qt::lightGray); + qApp->setPalette(palette); NewDarkMode = TRUE; } else { qApp->setStyleSheet(""); + QPalette palette(qApp->palette()); + palette.setColor(QPalette::Link, Qt::blue); + palette.setColor(QPalette::LinkVisited, Qt::magenta); + qApp->setPalette(palette); NewDarkMode = FALSE; } From 13e94b45a47d35ee9dc7630742fc9cb7a3d694f5 Mon Sep 17 00:00:00 2001 From: Kappa971 <62349018+Kappa971@users.noreply.github.com> Date: Sat, 23 Aug 2025 18:05:50 +0200 Subject: [PATCH 5/5] Update Italian translation --- src/qt/languages/it-IT.po | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/qt/languages/it-IT.po b/src/qt/languages/it-IT.po index b47632c2b..a8256ff60 100644 --- a/src/qt/languages/it-IT.po +++ b/src/qt/languages/it-IT.po @@ -247,7 +247,7 @@ msgid "&Rewind to the beginning" msgstr "Ri&avvolgi all'inizio" msgid "&Fast forward to the end" -msgstr "A&vanti veloce alla fine" +msgstr "A&vanti veloce fino alla fine" msgid "E&ject" msgstr "&Espelli" @@ -1075,7 +1075,7 @@ msgid "Start" msgstr "Avvia" msgid "Not running" -msgstr "Sospeso" +msgstr "Inattivo" msgid "Running" msgstr "In esecuzione" @@ -1141,7 +1141,7 @@ msgid "New configuration" msgstr "Nuova configurazione" msgid "Complete" -msgstr "Completare" +msgstr "Completamento" msgid "The wizard will now launch the configuration for the new system." msgstr "La procedura guidata avvierà ora la configurazione del nuovo sistema." @@ -2932,7 +2932,7 @@ msgid "The system will not be added." msgstr "Il sistema non verrà aggiunto." msgid "&Update mouse every CPU frame" -msgstr "&Aggiorano stato mouse ad ogni blocco CPU" +msgstr "&Aggiorna stato del mouse ad ogni blocco della CPU" msgid "Hue" msgstr "Tinta" @@ -2950,7 +2950,7 @@ msgid "Sharpness" msgstr "Nitidezza" msgid "&CGA composite settings..." -msgstr "Impostazioni del modo composito &CGA..." +msgstr "Impostazioni video composito &CGA..." msgid "CGA composite settings" -msgstr "Impostazioni del modo composito CGA" +msgstr "Impostazioni video composito CGA"