diff --git a/src/86box.c b/src/86box.c index f8c3365ef..d773e35cb 100644 --- a/src/86box.c +++ b/src/86box.c @@ -188,7 +188,6 @@ int gfxcard[GFXCARD_MAX] = { 0, 0 }; /* (C) graphic int show_second_monitors = 1; /* (C) show non-primary monitors */ int sound_is_float = 1; /* (C) sound uses FP values */ int voodoo_enabled = 0; /* (C) video option */ -int lba_enhancer_enabled = 0; /* (C) enable Vision Systems LBA Enhancer */ int ibm8514_standalone_enabled = 0; /* (C) video option */ int xga_standalone_enabled = 0; /* (C) video option */ int da2_standalone_enabled = 0; /* (C) video option */ @@ -1535,9 +1534,6 @@ pc_reset_hard_init(void) if (unittester_enabled) device_add(&unittester_device); - if (lba_enhancer_enabled) - device_add(&lba_enhancer_device); - if (novell_keycard_enabled) device_add(&novell_keycard_device); diff --git a/src/config.c b/src/config.c index 76abbae23..acfbec758 100644 --- a/src/config.c +++ b/src/config.c @@ -265,7 +265,9 @@ load_machine(void) if (p != NULL) { migrate_from = p; /* Migrate renamed machines. */ - if (!strcmp(p, "430nx")) + if (!strcmp(p, "tandy")) + machine = machine_get_machine_from_internal_name("tandy1000sx"); + else if (!strcmp(p, "430nx")) machine = machine_get_machine_from_internal_name("586ip"); else if (!strcmp(p, "586mc1")) machine = machine_get_machine_from_internal_name("586is"); @@ -992,11 +994,6 @@ load_storage_controllers(void) } } } - - lba_enhancer_enabled = !!ini_section_get_int(cat, "lba_enhancer_enabled", 0); - - if (!lba_enhancer_enabled) - ini_section_delete_var(cat, "lba_enhancer_enabled"); } /* Load "Hard Disks" section. */ @@ -1213,14 +1210,20 @@ load_floppy_and_cdrom_drives(void) unsigned int board = 0; unsigned int dev = 0; int c; - int d = 0; + int d; int count = cdrom_get_type_count(); memset(temp, 0x00, sizeof(temp)); for (c = 0; c < FDD_NUM; c++) { sprintf(temp, "fdd_%02i_type", c + 1); p = ini_section_get_string(cat, temp, (c < 2) ? "525_2dd" : "none"); - fdd_set_type(c, fdd_get_from_internal_name(p)); + if (!strcmp(p, "525_2hd_ps2")) + d = fdd_get_from_internal_name("525_2hd"); + else if (!strcmp(p, "35_2hd_ps2")) + d = fdd_get_from_internal_name("35_2hd"); + else + d = fdd_get_from_internal_name(p); + fdd_set_type(c, d); if (fdd_get_type(c) > 13) fdd_set_type(c, 13); @@ -1299,6 +1302,7 @@ load_floppy_and_cdrom_drives(void) ini_section_delete_var(cat, temp); sprintf(temp, "cdrom_%02i_parameters", c + 1); + d = 0; p = ini_section_get_string(cat, temp, NULL); if (p != NULL) sscanf(p, "%01u, %s", &d, s); @@ -1727,6 +1731,17 @@ load_other_peripherals(void) ini_section_delete_var(cat, temp); } + /* Backwards compatibility for standalone LBA Enhancer from v4.2 and older. */ + if (ini_section_get_int(ini_find_section(config, "Storage controllers"), "lba_enhancer_enabled", 0) == 1) { + /* Migrate to the first available ISA ROM slot. */ + for (uint8_t c = 0; c < ISAROM_MAX; c++) { + if (!isarom_type[c]) { + isarom_type[c] = isarom_get_from_internal_name("lba_enhancer"); + break; + } + } + } + p = ini_section_get_string(cat, "isartc_type", "none"); isartc_type = isartc_get_from_internal_name(p); @@ -2697,10 +2712,19 @@ save_storage_controllers(void) } } - if (lba_enhancer_enabled == 0) + /* Downgrade compatibility for standalone LBA Enhancer from v4.2 and older. */ + int card_id = isarom_get_from_internal_name("lba_enhancer"); + for (c = 0; c < ISAROM_MAX; c++) { + if (isarom_type[c] == card_id) { + /* A special value of 2 still enables the cards on older versions, + but lets newer versions know that they've already been migrated. */ + ini_section_set_int(cat, "lba_enhancer_enabled", 2); + card_id = 0; /* mark as found */ + break; + } + } + if (card_id > 0) /* not found */ ini_section_delete_var(cat, "lba_enhancer_enabled"); - else - ini_section_set_int(cat, "lba_enhancer_enabled", 1); ini_delete_section_if_empty(config, cat); } diff --git a/src/device/isarom.c b/src/device/isarom.c index cdf5a61c6..823bfcbe1 100644 --- a/src/device/isarom.c +++ b/src/device/isarom.c @@ -26,9 +26,14 @@ #include <86box/nvr.h> #include <86box/isarom.h> -#define ISAROM_CARD 0 -#define ISAROM_CARD_DUAL 1 -#define ISAROM_CARD_QUAD 2 +enum { + ISAROM_CARD = 0, + ISAROM_CARD_DUAL, + ISAROM_CARD_QUAD, + ISAROM_CARD_LBA_ENHANCER +}; + +#define BIOS_LBA_ENHANCER "roms/hdd/misc/lbaenhancer.bin" #ifdef ENABLE_ISAROM_LOG int isarom_do_log = ENABLE_ISAROM_LOG; @@ -56,7 +61,7 @@ typedef struct isarom_t { uint32_t size; uint32_t len; char nvr_path[64]; - uint8_t wp; + uint8_t writable; } socket[4]; uint8_t inst; uint8_t type; @@ -65,11 +70,14 @@ typedef struct isarom_t { static inline uint8_t get_limit(uint8_t type) { - if (type == ISAROM_CARD_DUAL) - return 2; - if (type == ISAROM_CARD_QUAD) - return 4; - return 1; + switch (type) { + case ISAROM_CARD_DUAL: + return 2; + case ISAROM_CARD_QUAD: + return 4; + default: + return 1; + } } static inline void @@ -92,12 +100,13 @@ isarom_close(void *priv) if (!priv) return; - for (uint8_t i = 0; i < get_limit(dev->type); i++) - if (dev->socket[i].rom.rom) { + for (uint8_t i = 0; i < get_limit(dev->type); i++) { + if (dev->socket[i].writable) { isarom_log("isarom[%u]: saving NVR for socket %u -> %s (%u bytes)\n", dev->inst, i, dev->socket[i].nvr_path, dev->socket[i].size); isarom_save_nvr(dev->socket[i].nvr_path, dev->socket[i].rom.rom, dev->socket[i].size); } + } free(dev); } @@ -115,32 +124,42 @@ isarom_init(const device_t *info) isarom_log("isarom[%u]: initializing device (type=%u)\n", dev->inst, dev->type); for (uint8_t i = 0; i < get_limit(dev->type); i++) { - char key_fn[12]; - char key_addr[14]; - char key_size[14]; - char key_writes[22]; + char str[22]; char suffix[4] = ""; if (i > 0) snprintf(suffix, sizeof(suffix), "%d", i + 1); - snprintf(key_fn, sizeof(key_fn), "bios_fn%s", suffix); - snprintf(key_addr, sizeof(key_addr), "bios_addr%s", suffix); - snprintf(key_size, sizeof(key_size), "bios_size%s", suffix); - snprintf(key_writes, sizeof(key_writes), "rom_writes_enabled%s", suffix); + snprintf(str, sizeof(str), "bios_addr%s", suffix); + dev->socket[i].addr = device_get_config_hex20(str); - dev->socket[i].fn = device_get_config_string(key_fn); - dev->socket[i].addr = device_get_config_hex20(key_addr); - dev->socket[i].size = device_get_config_int(key_size); - // Note: 2K is the smallest ROM I've found, but 86box's memory granularity is 4k, the number below is fine - // as we'll end up allocating no less than 4k due to the device config limits. - dev->socket[i].len = (dev->socket[i].size > 2048) ? dev->socket[i].size - 1 : 0; - dev->socket[i].wp = (uint8_t) device_get_config_int(key_writes) ? 1 : 0; + switch (dev->type) { + case ISAROM_CARD_LBA_ENHANCER: + dev->socket[i].fn = BIOS_LBA_ENHANCER; + dev->socket[i].size = 0x4000; + break; - isarom_log("isarom[%u]: socket %u: addr=0x%05X size=%u wp=%u fn=%s\n", + default: + snprintf(str, sizeof(str), "bios_fn%s", suffix); + dev->socket[i].fn = device_get_config_string(str); + + snprintf(str, sizeof(str), "bios_size%s", suffix); + dev->socket[i].size = device_get_config_int(str); + + snprintf(str, sizeof(str), "rom_writes_enabled%s", suffix); + if (device_get_config_int(str)) + dev->socket[i].writable = 1; + break; + } + + /* Note: 2K is the smallest ROM I've found, but 86Box's memory granularity is 4K, the number + below is fine as we'll end up allocating no less than 4K due to the device config limits. */ + dev->socket[i].len = (dev->socket[i].size > 0) ? ((dev->socket[i].size - 1) | MEM_GRANULARITY_MASK) : 0; + + isarom_log("isarom[%u]: socket %u: addr=0x%05X size=%u writable=%u fn=%s\n", dev->inst, i, dev->socket[i].addr, dev->socket[i].size, - dev->socket[i].wp, dev->socket[i].fn ? dev->socket[i].fn : "(null)"); + dev->socket[i].writable, dev->socket[i].fn ? dev->socket[i].fn : "(null)"); - if (dev->socket[i].addr != 0 && dev->socket[i].fn != NULL) { + if ((dev->socket[i].addr != 0) && (dev->socket[i].fn != NULL)) { rom_init(&dev->socket[i].rom, dev->socket[i].fn, dev->socket[i].addr, @@ -151,7 +170,7 @@ isarom_init(const device_t *info) isarom_log("isarom[%u]: ROM initialized for socket %u\n", dev->inst, i); - if (dev->socket[i].wp) { + if (dev->socket[i].writable) { mem_mapping_set_write_handler(&dev->socket[i].rom.mapping, rom_write, rom_writew, rom_writel); snprintf(dev->socket[i].nvr_path, sizeof(dev->socket[i].nvr_path), "isarom_%i_%i.nvr", dev->inst, i + 1); FILE *fp = nvr_fopen(dev->socket[i].nvr_path, "rb"); @@ -168,6 +187,12 @@ isarom_init(const device_t *info) return dev; } +static int +isarom_lba_enhancer_available(void) +{ + return rom_present(BIOS_LBA_ENHANCER); +} + #define BIOS_FILE_FILTER "ROM files (*.bin *.rom)|*.bin,*.rom" #define BIOS_ADDR_SELECTION { \ @@ -444,7 +469,7 @@ static const device_config_t isarom_quad_config[] = { .type = CONFIG_FNAME, .default_string = NULL, .default_int = 0, - .file_filter = "ROM files (*.bin *.rom)|*.bin,*.rom", + .file_filter = BIOS_FILE_FILTER, .spinner = { 0 }, .selection = { }, .bios = { { 0 } } @@ -528,6 +553,29 @@ static const device_config_t isarom_quad_config[] = { }, { .name = "", .description = "", .type = CONFIG_END } }; + +static const device_config_t lba_enhancer_config[] = { + { + .name = "bios_addr", + .description = "BIOS Address", + .type = CONFIG_HEX20, + .default_string = NULL, + .default_int = 0xc8000, + .file_filter = NULL, + .spinner = { 0 }, + .selection = { + { .description = "C800H", .value = 0xc8000 }, + { .description = "CC00H", .value = 0xcc000 }, + { .description = "D000H", .value = 0xd0000 }, + { .description = "D400H", .value = 0xd4000 }, + { .description = "D800H", .value = 0xd8000 }, + { .description = "DC00H", .value = 0xdc000 }, + { .description = "" } + }, + .bios = { { 0 } } + }, + { .name = "", .description = "", .type = CONFIG_END } +}; // clang-format on static const device_t isarom_device = { @@ -572,15 +620,30 @@ static const device_t isarom_quad_device = { .config = isarom_quad_config }; +static const device_t lba_enhancer_device = { + .name = "Vision Systems LBA Enhancer", + .internal_name = "lba_enhancer", + .flags = DEVICE_ISA, + .local = ISAROM_CARD_LBA_ENHANCER, + .init = isarom_init, + .close = isarom_close, + .reset = NULL, + .available = isarom_lba_enhancer_available, + .speed_changed = NULL, + .force_redraw = NULL, + .config = lba_enhancer_config +}; + static const struct { const device_t *dev; } boards[] = { // clang-format off - { &device_none }, - { &isarom_device }, - { &isarom_dual_device }, - { &isarom_quad_device }, - { NULL } + { &device_none }, + { &isarom_device }, + { &isarom_dual_device }, + { &isarom_quad_device }, + { &lba_enhancer_device }, + { NULL } // clang-format on }; diff --git a/src/disk/CMakeLists.txt b/src/disk/CMakeLists.txt index bdbb9e74c..48d9e61ff 100644 --- a/src/disk/CMakeLists.txt +++ b/src/disk/CMakeLists.txt @@ -35,7 +35,6 @@ add_library(hdd OBJECT hdc_ide_sff8038i.c hdc_ide_um8673f.c hdc_ide_w83769f.c - lba_enhancer.c ) add_library(zip OBJECT zip.c) diff --git a/src/disk/lba_enhancer.c b/src/disk/lba_enhancer.c deleted file mode 100644 index ef9e167d3..000000000 --- a/src/disk/lba_enhancer.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * 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. - * - * Vision Systems LBA Enhancer emulation. - * - * - * - * Authors: Cacodemon345 - * - * Copyright 2024 Cacodemon345 - */ - -#include -#include -#include -#include -#include -#define HAVE_STDARG_H -#include <86box/86box.h> -#include <86box/io.h> -#include <86box/device.h> -#include <86box/mem.h> -#include <86box/rom.h> -#include <86box/plat_unused.h> - -typedef struct lba_enhancer_t -{ - rom_t rom; -} lba_enhancer_t; - -#define BIOS_LBA_ENHANCER "roms/hdd/misc/lbaenhancer.bin" - -void -lba_enhancer_close(void* priv) -{ - free(priv); - - return; -} - -void * -lba_enhancer_init(UNUSED(const device_t *info)) -{ - lba_enhancer_t *dev = (lba_enhancer_t *) calloc(1, sizeof(lba_enhancer_t)); - - rom_init(&dev->rom, BIOS_LBA_ENHANCER, - device_get_config_hex20("bios_addr"), 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL); - - return dev; -} - -static int -lba_enhancer_available(void) -{ - return rom_present(BIOS_LBA_ENHANCER); -} - -// clang-format off -static const device_config_t lba_enhancer_config[] = { - { - .name = "bios_addr", - .description = "BIOS Address", - .type = CONFIG_HEX20, - .default_string = NULL, - .default_int = 0xc8000, - .file_filter = NULL, - .spinner = { 0 }, - .selection = { - { .description = "C800H", .value = 0xc8000 }, - { .description = "CC00H", .value = 0xcc000 }, - { .description = "D000H", .value = 0xd0000 }, - { .description = "D400H", .value = 0xd4000 }, - { .description = "D800H", .value = 0xd8000 }, - { .description = "DC00H", .value = 0xdc000 }, - { .description = "" } - }, - .bios = { { 0 } } - }, - { .name = "", .description = "", .type = CONFIG_END } -}; -// clang-format on - -const device_t lba_enhancer_device = { - .name = "Vision Systems LBA Enhancer", - .internal_name = "lba_enhancer", - .flags = DEVICE_ISA16, - .local = 0, - .init = lba_enhancer_init, - .close = lba_enhancer_close, - .reset = NULL, - .available = lba_enhancer_available, - .speed_changed = NULL, - .force_redraw = NULL, - .config = lba_enhancer_config -}; diff --git a/src/floppy/fdd.c b/src/floppy/fdd.c index 31811069f..1623f932a 100644 --- a/src/floppy/fdd.c +++ b/src/floppy/fdd.c @@ -212,18 +212,9 @@ int fdd_get_from_internal_name(char *s) { int c = 0; - char *n; - - /* TODO: Remove this once the migration period is over. */ - if (!strcmp(s, "525_2hd_ps2")) - n = "525_2hd"; - else if (!strcmp(s, "35_2hd_ps2")) - n = "35_2hd"; - else - n = s; while (strlen(drive_types[c].internal_name)) { - if (!strcmp((char *) drive_types[c].internal_name, n)) + if (!strcmp((char *) drive_types[c].internal_name, s)) return c; c++; } diff --git a/src/include/86box/86box.h b/src/include/86box/86box.h index 55ba98567..4494290d6 100644 --- a/src/include/86box/86box.h +++ b/src/include/86box/86box.h @@ -149,7 +149,6 @@ extern int fpu_type; /* (C) fpu type */ extern int fpu_softfloat; /* (C) fpu uses softfloat */ extern int time_sync; /* (C) enable time sync */ extern int hdd_format_type; /* (C) hard disk file format */ -extern int lba_enhancer_enabled; /* (C) enable Vision Systems LBA Enhancer */ extern int confirm_reset; /* (C) enable reset confirmation */ extern int confirm_exit; /* (C) enable exit confirmation */ extern int confirm_save; /* (C) enable save confirmation */ diff --git a/src/include/86box/hdc.h b/src/include/86box/hdc.h index 0a5985370..ca020b4b2 100644 --- a/src/include/86box/hdc.h +++ b/src/include/86box/hdc.h @@ -112,9 +112,6 @@ extern const device_t xtide_acculogic_device; /* xtide_ps2 */ extern const device_t xtide_at_ps2_device; /* xtide_at_ps2 */ extern const device_t xtide_at_ps2_2ch_device; /* xtide_at_ps2_2ch */ -/* Miscellaneous */ -extern const device_t lba_enhancer_device; - extern void hdc_init(void); extern void hdc_reset(void); diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index 87dd38786..94c03ebe6 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -1564,7 +1564,7 @@ const machine_t machines[] = { }, { .name = "[8088] Tandy 1000 SX", - .internal_name = "tandy", + .internal_name = "tandy1000sx", .type = MACHINE_TYPE_8088, .chipset = MACHINE_CHIPSET_PROPRIETARY, .init = machine_tandy1000sx_init, diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt index 076d5cc42..c71273af8 100644 --- a/src/qt/CMakeLists.txt +++ b/src/qt/CMakeLists.txt @@ -455,6 +455,7 @@ endif() if (UNIX AND NOT APPLE AND NOT HAIKU) target_sources(ui PRIVATE x11_util.c) + target_link_libraries(plat PRIVATE ${CMAKE_DL_LIBS}) find_package(X11 REQUIRED) target_link_libraries(ui PRIVATE X11::X11 X11::Xi) diff --git a/src/qt/qt_main.cpp b/src/qt/qt_main.cpp index d02ed6b67..a08c2d6c1 100644 --- a/src/qt/qt_main.cpp +++ b/src/qt/qt_main.cpp @@ -56,6 +56,10 @@ extern "C" { #include <86box/gdbstub.h> #include <86box/version.h> #include <86box/renderdefs.h> +#ifdef Q_OS_LINUX +#define GAMEMODE_AUTO +#include "../unix/gamemode/gamemode_client.h" +#endif } #ifdef Q_OS_WINDOWS diff --git a/src/qt/qt_settingsstoragecontrollers.cpp b/src/qt/qt_settingsstoragecontrollers.cpp index d282bcb8b..5c9b24c46 100644 --- a/src/qt/qt_settingsstoragecontrollers.cpp +++ b/src/qt/qt_settingsstoragecontrollers.cpp @@ -62,7 +62,6 @@ SettingsStorageControllers::save() ide_ter_enabled = ui->checkBoxTertiaryIDE->isChecked() ? 1 : 0; ide_qua_enabled = ui->checkBoxQuaternaryIDE->isChecked() ? 1 : 0; cassette_enable = ui->checkBoxCassette->isChecked() ? 1 : 0; - lba_enhancer_enabled = ui->checkBoxLbaEnhancer->isChecked() ? 1 : 0; } void @@ -232,9 +231,6 @@ SettingsStorageControllers::onCurrentMachineChanged(int machineId) ui->checkBoxCassette->setChecked(false); ui->checkBoxCassette->setEnabled(false); } - - ui->checkBoxLbaEnhancer->setChecked(lba_enhancer_enabled > 0 && device_available(&lba_enhancer_device)); - ui->pushButtonConfigureLbaEnhancer->setEnabled(ui->checkBoxLbaEnhancer->isChecked()); } void @@ -365,15 +361,3 @@ SettingsStorageControllers::on_pushButtonSCSI4_clicked() { DeviceConfig::ConfigureDevice(scsi_card_getdevice(ui->comboBoxSCSI4->currentData().toInt()), 4); } - -void -SettingsStorageControllers::on_checkBoxLbaEnhancer_stateChanged(int arg1) -{ - ui->pushButtonConfigureLbaEnhancer->setEnabled(arg1 != 0); -} - -void -SettingsStorageControllers::on_pushButtonConfigureLbaEnhancer_clicked() -{ - DeviceConfig::ConfigureDevice(&lba_enhancer_device); -} diff --git a/src/qt/qt_settingsstoragecontrollers.hpp b/src/qt/qt_settingsstoragecontrollers.hpp index 7c1c56086..074b48dbd 100644 --- a/src/qt/qt_settingsstoragecontrollers.hpp +++ b/src/qt/qt_settingsstoragecontrollers.hpp @@ -44,9 +44,6 @@ private slots: void on_comboBoxSCSI4_currentIndexChanged(int index); void on_pushButtonSCSI4_clicked(); - void on_checkBoxLbaEnhancer_stateChanged(int arg1); - void on_pushButtonConfigureLbaEnhancer_clicked(); - private: Ui::SettingsStorageControllers *ui; int machineId = 0; diff --git a/src/qt/qt_settingsstoragecontrollers.ui b/src/qt/qt_settingsstoragecontrollers.ui index cd1bf9baf..b9eb310ba 100644 --- a/src/qt/qt_settingsstoragecontrollers.ui +++ b/src/qt/qt_settingsstoragecontrollers.ui @@ -257,43 +257,6 @@ - - - - 0 - - - 0 - - - - - Vision Systems LBA Enhancer - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Configure - - - - - diff --git a/src/unix/gamemode/gamemode_client.h b/src/unix/gamemode/gamemode_client.h new file mode 100644 index 000000000..49c34fb9f --- /dev/null +++ b/src/unix/gamemode/gamemode_client.h @@ -0,0 +1,376 @@ +/* + +Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Feral Interactive nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + */ +#ifndef CLIENT_GAMEMODE_H +#define CLIENT_GAMEMODE_H +/* + * GameMode supports the following client functions + * Requests are refcounted in the daemon + * + * int gamemode_request_start() - Request gamemode starts + * 0 if the request was sent successfully + * -1 if the request failed + * + * int gamemode_request_end() - Request gamemode ends + * 0 if the request was sent successfully + * -1 if the request failed + * + * GAMEMODE_AUTO can be defined to make the above two functions apply during static init and + * destruction, as appropriate. In this configuration, errors will be printed to stderr + * + * int gamemode_query_status() - Query the current status of gamemode + * 0 if gamemode is inactive + * 1 if gamemode is active + * 2 if gamemode is active and this client is registered + * -1 if the query failed + * + * int gamemode_request_start_for(pid_t pid) - Request gamemode starts for another process + * 0 if the request was sent successfully + * -1 if the request failed + * -2 if the request was rejected + * + * int gamemode_request_end_for(pid_t pid) - Request gamemode ends for another process + * 0 if the request was sent successfully + * -1 if the request failed + * -2 if the request was rejected + * + * int gamemode_query_status_for(pid_t pid) - Query status of gamemode for another process + * 0 if gamemode is inactive + * 1 if gamemode is active + * 2 if gamemode is active and this client is registered + * -1 if the query failed + * + * const char* gamemode_error_string() - Get an error string + * returns a string describing any of the above errors + * + * Note: All the above requests can be blocking - dbus requests can and will block while the daemon + * handles the request. It is not recommended to make these calls in performance critical code + */ + +#include +#include + +#include +#include + +#include + +#include + +static char internal_gamemode_client_error_string[512] = { 0 }; + +/** + * Load libgamemode dynamically to dislodge us from most dependencies. + * This allows clients to link and/or use this regardless of runtime. + * See SDL2 for an example of the reasoning behind this in terms of + * dynamic versioning as well. + */ +static volatile int internal_libgamemode_loaded = 1; + +/* Typedefs for the functions to load */ +typedef int (*api_call_return_int)(void); +typedef const char *(*api_call_return_cstring)(void); +typedef int (*api_call_pid_return_int)(pid_t); + +/* Storage for functors */ +static api_call_return_int REAL_internal_gamemode_request_start = NULL; +static api_call_return_int REAL_internal_gamemode_request_end = NULL; +static api_call_return_int REAL_internal_gamemode_query_status = NULL; +static api_call_return_cstring REAL_internal_gamemode_error_string = NULL; +static api_call_pid_return_int REAL_internal_gamemode_request_start_for = NULL; +static api_call_pid_return_int REAL_internal_gamemode_request_end_for = NULL; +static api_call_pid_return_int REAL_internal_gamemode_query_status_for = NULL; + +/** + * Internal helper to perform the symbol binding safely. + * + * Returns 0 on success and -1 on failure + */ +__attribute__((always_inline)) static inline int internal_bind_libgamemode_symbol( + void *handle, const char *name, void **out_func, size_t func_size, bool required) +{ + void *symbol_lookup = NULL; + char *dl_error = NULL; + + /* Safely look up the symbol */ + symbol_lookup = dlsym(handle, name); + dl_error = dlerror(); + if (required && (dl_error || !symbol_lookup)) { + snprintf(internal_gamemode_client_error_string, + sizeof(internal_gamemode_client_error_string), + "dlsym failed - %s", + dl_error); + return -1; + } + + /* Have the symbol correctly, copy it to make it usable */ + memcpy(out_func, &symbol_lookup, func_size); + return 0; +} + +/** + * Loads libgamemode and needed functions + * + * Returns 0 on success and -1 on failure + */ +__attribute__((always_inline)) static inline int internal_load_libgamemode(void) +{ + /* We start at 1, 0 is a success and -1 is a fail */ + if (internal_libgamemode_loaded != 1) { + return internal_libgamemode_loaded; + } + + /* Anonymous struct type to define our bindings */ + struct binding { + const char *name; + void **functor; + size_t func_size; + bool required; + } bindings[] = { + { "real_gamemode_request_start", + (void **)&REAL_internal_gamemode_request_start, + sizeof(REAL_internal_gamemode_request_start), + true }, + { "real_gamemode_request_end", + (void **)&REAL_internal_gamemode_request_end, + sizeof(REAL_internal_gamemode_request_end), + true }, + { "real_gamemode_query_status", + (void **)&REAL_internal_gamemode_query_status, + sizeof(REAL_internal_gamemode_query_status), + false }, + { "real_gamemode_error_string", + (void **)&REAL_internal_gamemode_error_string, + sizeof(REAL_internal_gamemode_error_string), + true }, + { "real_gamemode_request_start_for", + (void **)&REAL_internal_gamemode_request_start_for, + sizeof(REAL_internal_gamemode_request_start_for), + false }, + { "real_gamemode_request_end_for", + (void **)&REAL_internal_gamemode_request_end_for, + sizeof(REAL_internal_gamemode_request_end_for), + false }, + { "real_gamemode_query_status_for", + (void **)&REAL_internal_gamemode_query_status_for, + sizeof(REAL_internal_gamemode_query_status_for), + false }, + }; + + void *libgamemode = NULL; + + /* Try and load libgamemode */ + libgamemode = dlopen("libgamemode.so.0", RTLD_NOW); + if (!libgamemode) { + /* Attempt to load unversioned library for compatibility with older + * versions (as of writing, there are no ABI changes between the two - + * this may need to change if ever ABI-breaking changes are made) */ + libgamemode = dlopen("libgamemode.so", RTLD_NOW); + if (!libgamemode) { + snprintf(internal_gamemode_client_error_string, + sizeof(internal_gamemode_client_error_string), + "dlopen failed - %s", + dlerror()); + internal_libgamemode_loaded = -1; + return -1; + } + } + + /* Attempt to bind all symbols */ + for (size_t i = 0; i < sizeof(bindings) / sizeof(bindings[0]); i++) { + struct binding *binder = &bindings[i]; + + if (internal_bind_libgamemode_symbol(libgamemode, + binder->name, + binder->functor, + binder->func_size, + binder->required)) { + internal_libgamemode_loaded = -1; + return -1; + }; + } + + /* Success */ + internal_libgamemode_loaded = 0; + return 0; +} + +/** + * Redirect to the real libgamemode + */ +__attribute__((always_inline)) static inline const char *gamemode_error_string(void) +{ + /* If we fail to load the system gamemode, or we have an error string already, return our error + * string instead of diverting to the system version */ + if (internal_load_libgamemode() < 0 || internal_gamemode_client_error_string[0] != '\0') { + return internal_gamemode_client_error_string; + } + + /* Assert for static analyser that the function is not NULL */ + assert(REAL_internal_gamemode_error_string != NULL); + + return REAL_internal_gamemode_error_string(); +} + +/** + * Redirect to the real libgamemode + * Allow automatically requesting game mode + * Also prints errors as they happen. + */ +#ifdef GAMEMODE_AUTO +__attribute__((constructor)) +#else +__attribute__((always_inline)) static inline +#endif +int gamemode_request_start(void) +{ + /* Need to load gamemode */ + if (internal_load_libgamemode() < 0) { +#ifdef GAMEMODE_AUTO + fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); +#endif + return -1; + } + + /* Assert for static analyser that the function is not NULL */ + assert(REAL_internal_gamemode_request_start != NULL); + + if (REAL_internal_gamemode_request_start() < 0) { +#ifdef GAMEMODE_AUTO + fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); +#endif + return -1; + } + + return 0; +} + +/* Redirect to the real libgamemode */ +#ifdef GAMEMODE_AUTO +__attribute__((destructor)) +#else +__attribute__((always_inline)) static inline +#endif +int gamemode_request_end(void) +{ + /* Need to load gamemode */ + if (internal_load_libgamemode() < 0) { +#ifdef GAMEMODE_AUTO + fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); +#endif + return -1; + } + + /* Assert for static analyser that the function is not NULL */ + assert(REAL_internal_gamemode_request_end != NULL); + + if (REAL_internal_gamemode_request_end() < 0) { +#ifdef GAMEMODE_AUTO + fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string()); +#endif + return -1; + } + + return 0; +} + +/* Redirect to the real libgamemode */ +__attribute__((always_inline)) static inline int gamemode_query_status(void) +{ + /* Need to load gamemode */ + if (internal_load_libgamemode() < 0) { + return -1; + } + + if (REAL_internal_gamemode_query_status == NULL) { + snprintf(internal_gamemode_client_error_string, + sizeof(internal_gamemode_client_error_string), + "gamemode_query_status missing (older host?)"); + return -1; + } + + return REAL_internal_gamemode_query_status(); +} + +/* Redirect to the real libgamemode */ +__attribute__((always_inline)) static inline int gamemode_request_start_for(pid_t pid) +{ + /* Need to load gamemode */ + if (internal_load_libgamemode() < 0) { + return -1; + } + + if (REAL_internal_gamemode_request_start_for == NULL) { + snprintf(internal_gamemode_client_error_string, + sizeof(internal_gamemode_client_error_string), + "gamemode_request_start_for missing (older host?)"); + return -1; + } + + return REAL_internal_gamemode_request_start_for(pid); +} + +/* Redirect to the real libgamemode */ +__attribute__((always_inline)) static inline int gamemode_request_end_for(pid_t pid) +{ + /* Need to load gamemode */ + if (internal_load_libgamemode() < 0) { + return -1; + } + + if (REAL_internal_gamemode_request_end_for == NULL) { + snprintf(internal_gamemode_client_error_string, + sizeof(internal_gamemode_client_error_string), + "gamemode_request_end_for missing (older host?)"); + return -1; + } + + return REAL_internal_gamemode_request_end_for(pid); +} + +/* Redirect to the real libgamemode */ +__attribute__((always_inline)) static inline int gamemode_query_status_for(pid_t pid) +{ + /* Need to load gamemode */ + if (internal_load_libgamemode() < 0) { + return -1; + } + + if (REAL_internal_gamemode_query_status_for == NULL) { + snprintf(internal_gamemode_client_error_string, + sizeof(internal_gamemode_client_error_string), + "gamemode_query_status_for missing (older host?)"); + return -1; + } + + return REAL_internal_gamemode_query_status_for(pid); +} + +#endif // CLIENT_GAMEMODE_H diff --git a/src/unix/unix.c b/src/unix/unix.c index 2bbbda067..f3580cd19 100644 --- a/src/unix/unix.c +++ b/src/unix/unix.c @@ -576,7 +576,7 @@ main_thread(UNUSED(void *param)) old_time = new_time; if (drawits > 0 && !dopause) { /* Yes, so do one frame now. */ - drawits -= 10; + drawits -= force_10ms ? 10 : 1; if (drawits > 50) drawits = 0; @@ -584,7 +584,7 @@ main_thread(UNUSED(void *param)) pc_run(); /* Every 200 frames we save the machine status. */ - if (++frames >= 200 && nvr_dosave) { + if (++frames >= (force_10ms ? 200 : 2000) && nvr_dosave) { nvr_save(); nvr_dosave = 0; frames = 0;