diff --git a/deps/switchres/.github/workflows/build.yml b/deps/switchres/.github/workflows/build.yml index f363537afa..23a5081dbd 100644 --- a/deps/switchres/.github/workflows/build.yml +++ b/deps/switchres/.github/workflows/build.yml @@ -9,16 +9,16 @@ jobs: strategy: matrix: platform: - - { name: Linux GCC } - - { name: Windows MINGW, make_opts: PLATFORM=NT CROSS_COMPILE=x86_64-w64-mingw32- } + - { name: linux_gcc } + - { name: windows_mingw, make_opts: PLATFORM=NT CROSS_COMPILE=x86_64-w64-mingw32- } steps: - uses: actions/checkout@v3 - name: Install Linux dependencies - if: matrix.platform.name == 'Linux GCC' + if: matrix.platform.name == 'linux_gcc' run: sudo apt-get install libxrandr-dev libdrm-dev libsdl2-dev - name: Install Windows dependencies - if: matrix.platform.name == 'Windows MINGW' + if: matrix.platform.name == 'windows_mingw' run: | sudo apt-get install mingw-w64 wget tar sudo update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix @@ -33,7 +33,7 @@ jobs: make libswitchres ${{matrix.platform.make_opts}} make ${{matrix.platform.make_opts}} - name: Build grid.exe (Windows only) - if: matrix.platform.name == 'Windows MINGW' + if: matrix.platform.name == 'windows_mingw' run: | make ${{matrix.platform.make_opts}} clean make ${{matrix.platform.make_opts}} grid @@ -62,3 +62,32 @@ jobs: with: name: geometry-win32-x86_64 path: dist/ + + release: + + runs-on: ubuntu-latest + needs: [buildx86_64, win32-build-x86_64-geometry] + if: startsWith(github.ref, 'refs/tags/v') + steps: + - name: Get version + id: get_version + run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/} + - name: Download artifacts + uses: actions/download-artifact@v2.0.5 + - name: Make packages + run: | + tag="${GITHUB_REF#refs/tags/}" + mv ./geometry-win32-x86_64/geometry.exe ./switchres-windows_mingw-x86_64 + 7z a "switchres-${tag}-windows_mingw-x86_64.7z" ./switchres-windows_mingw-x86_64 + tar cvjf switchres-${tag}-linux_gcc-x86_64.tar.bz2 ./switchres-linux_gcc-x86_64 + - name: Create release + uses: softprops/action-gh-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + name: Switchres ${{ steps.get_version.outputs.VERSION }} + draft: true + prerelease: false + files: | + ./*.bz2 + ./*.7z diff --git a/deps/switchres/README.md b/deps/switchres/README.md index 9ee0fc8e11..5eaed04ff5 100644 --- a/deps/switchres/README.md +++ b/deps/switchres/README.md @@ -1,4 +1,4 @@ -# What is Switchres 2.0 +# What is Switchres Switchres is a modeline generation engine for emulation. Its purpose is on-the-fly creation of fully customized video modes that accurately reproduce those of the emulated systems. Based on a monitor profile, it will provide the best video mode for a given width, height, and refresh rate. @@ -7,7 +7,7 @@ Switchres features the most versatile modeline generation ever, ranging from 15- Switchres can be integrated into open-source emulators either as a library, or used as a standalone emulator launcher. It's written in C++ and a C wrapper is also available. -Switchres 2.0 is a rewrite of the original Switchres code used in GroovyMAME. It currently supports mode switching on the following platforms, with their respective backends: +Switchres is a rewrite of the original Switchres code used in GroovyMAME. It currently supports mode switching on the following platforms, with their respective backends: - **Windows**: - AMD ADL (AMD Radeon HD 5000+) - ATI legacy (ATI Radeon pre-HD 5000) @@ -37,12 +37,17 @@ Options: -l, --launch Launch -m, --monitor Monitor preset (generic_15, arcade_15, pal, ntsc, etc.) -a --aspect Monitor aspect ratio - -r --rotated Original mode's native orientation is rotated - -d, --display Use target display (Windows: \\\\.\\DISPLAY1, ... Linux: VGA-0, ...) + -r --rotated Rotate axes, preserving aspect ratio + -d, --display Use target display (index = 0, 1, 2...) -f, --force x@ Force a specific video mode from display mode list -i, --ini Specify an ini file - -b, --backend Specify the api name -k, --keep Keep changes on exit (warning: this disables cleanup) + -g, --geometry Adjust geometry of generated modeline + adjustment = :: + e.g. switchres 640 480 60 -c -g 1.1:-1:2 + +For more options, refer to switchres.ini. All options in switchres.ini can be applied in +command line as long options, e.g.: switchres 256 224 57.55 -c --dotclock_min 8.0 ``` A default `switchres.ini` file will be searched in the current working directory, then in `.\ini` on Windows, `./ini` then `/etc` on Linux. The repo has a switchres.ini example. diff --git a/deps/switchres/custom_video_drmkms.cpp b/deps/switchres/custom_video_drmkms.cpp index f71c49ca1d..c7c2d0a3b1 100755 --- a/deps/switchres/custom_video_drmkms.cpp +++ b/deps/switchres/custom_video_drmkms.cpp @@ -47,11 +47,22 @@ #define drmModeFreePlaneResources p_drmModeFreePlaneResources #define drmIoctl p_drmIoctl #define drmGetCap p_drmGetCap +#define drmGetDevices2 p_drmGetDevices2 #define drmIsMaster p_drmIsMaster #define drmSetMaster p_drmSetMaster #define drmDropMaster p_drmDropMaster # define MAX_CARD_ID 10 +# define MAX_DRM_DEVICES 16 + +// To enable libdrmhook: make SR_WITH_DRMHOOK=1 +#ifdef SR_WITH_DRMHOOK + #define hook_handle RTLD_DEFAULT + #define hook_log " (will attempt hook)" +#else + #define hook_handle mp_drm_handle + #define hook_log "" +#endif //============================================================ // shared the privileges of the master fd @@ -212,6 +223,13 @@ bool drmkms_timing::test_kernel_user_modes() // Count the number of existing modes, so it should be +1 when attaching // a new mode. Could also check the mode name, still better conn = drmModeGetConnector(fd, m_desktop_output); + if (!conn) + { + log_verbose("DRM/KMS: <%d> (%s) Cannot get connector\n", m_id, __FUNCTION__); + m_kernel_user_modes = false; + return false; + } + first_modes_count = conn->count_modes; ret = drmModeAttachMode(fd, m_desktop_output, &mode); drmModeFreeConnector(conn); @@ -329,7 +347,7 @@ drmkms_timing::~drmkms_timing() bool drmkms_timing::init() { - log_verbose("DRM/KMS: <%d> (init) loading DRM/KMS library\n", m_id); + log_verbose("DRM/KMS: <%d> (init) loading DRM/KMS library%s\n", m_id, hook_log); mp_drm_handle = dlopen("libdrm.so", RTLD_NOW); if (mp_drm_handle) { @@ -354,21 +372,21 @@ bool drmkms_timing::init() return false; } - p_drmModeGetConnector = (__typeof__(drmModeGetConnector)) dlsym(RTLD_DEFAULT, "drmModeGetConnector"); + p_drmModeGetConnector = (__typeof__(drmModeGetConnector)) dlsym(hook_handle, "drmModeGetConnector"); if (p_drmModeGetConnector == NULL) { log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeGetConnector", "DRM_LIBRARY"); return false; } - p_drmModeGetConnectorCurrent = (__typeof__(drmModeGetConnectorCurrent)) dlsym(RTLD_DEFAULT, "drmModeGetConnectorCurrent"); + p_drmModeGetConnectorCurrent = (__typeof__(drmModeGetConnectorCurrent)) dlsym(hook_handle, "drmModeGetConnectorCurrent"); if (p_drmModeGetConnectorCurrent == NULL) { log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeGetConnectorCurrent", "DRM_LIBRARY"); return false; } - p_drmModeFreeConnector = (__typeof__(drmModeFreeConnector)) dlsym(RTLD_DEFAULT, "drmModeFreeConnector"); + p_drmModeFreeConnector = (__typeof__(drmModeFreeConnector)) dlsym(hook_handle, "drmModeFreeConnector"); if (p_drmModeFreeConnector == NULL) { log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmModeFreeConnector", "DRM_LIBRARY"); @@ -494,6 +512,13 @@ bool drmkms_timing::init() return false; } + p_drmGetDevices2 = (__typeof__(drmGetDevices2)) dlsym(mp_drm_handle, "drmGetDevices2"); + if (p_drmGetDevices2 == NULL) + { + log_error("DRM/KMS: <%d> (init) [ERROR] missing func %s in %s", m_id, "drmGetDevices2", "DRM_LIBRARY"); + return false; + } + p_drmIsMaster = (__typeof__(drmIsMaster)) dlsym(mp_drm_handle, "drmIsMaster"); if (p_drmIsMaster == NULL) { @@ -529,14 +554,31 @@ bool drmkms_timing::init() else if (strlen(m_device_name) == 1 && m_device_name[0] >= '0' && m_device_name[0] <= '9') screen_pos = m_device_name[0] - '0'; - char drm_name[15] = "/dev/dri/card_"; + // Get an array of drm devices to check + drmDevicePtr devices[MAX_DRM_DEVICES]; + int num_devices = drmGetDevices2(0, NULL, 0); + + if (num_devices > MAX_DRM_DEVICES) + num_devices = MAX_DRM_DEVICES; + + int ret = drmGetDevices2(0, devices, num_devices); + if (ret < 0) + { + log_error("DRM/KMS: drmGetDevices2() returned an error %d\n", ret); + return false; + } + + char *drm_name; drmModeRes *p_res; drmModeConnector *p_connector; int output_position = 0; - for (int num = 0; !m_desktop_output && num < MAX_CARD_ID; num++) + for (int num = 0; num < num_devices; num++) { - drm_name[13] = '0' + num; + // Skip non-primary nodes + if (devices[num]->available_nodes & (1 << DRM_NODE_PRIMARY)) + drm_name = devices[num]->nodes[DRM_NODE_PRIMARY]; + else continue; if (!access(drm_name, F_OK) == 0) { @@ -554,7 +596,10 @@ bool drmkms_timing::init() log_error("DRM/KMS: <%d> (init) [ERROR] ioctl DRM_CAP_DUMB_BUFFER\n", m_id); if (!check_dumb) + { log_error("DRM/KMS: <%d> (init) [ERROR] dumb buffer not supported\n", m_id); + continue; + } p_res = drmModeGetResources(m_drm_fd); @@ -582,6 +627,7 @@ bool drmkms_timing::init() } m_desktop_output = p_connector->connector_id; m_card_id = num; + strcpy(m_drm_name, drm_name); log_verbose("DRM/KMS: <%d> (init) card %d connector %d id %d name %s selected as primary output\n", m_id, num, i, m_desktop_output, connector_name); drmModeEncoder *p_encoder = drmModeGetEncoder(m_drm_fd, p_connector->encoder_id); @@ -601,7 +647,10 @@ bool drmkms_timing::init() } } if (!mp_crtc_desktop) + { + m_desktop_output = 0; log_error("DRM/KMS: <%d> (init) [ERROR] no crtc found\n", m_id); + } drmModeFreeEncoder(p_encoder); } output_position++; @@ -649,9 +698,14 @@ bool drmkms_timing::init() s_shared_count[m_card_id] = 2; } if (!drmIsMaster(m_drm_fd)) + { + m_desktop_output = 0; log_error("DRM/KMS: <%d> (%s) [ERROR] limited DRM rights on this screen\n", m_id, __FUNCTION__); + } } } + // If we're here and we have a valid output, we're done. + if (m_desktop_output) break; } } @@ -697,9 +751,8 @@ bool drmkms_timing::init() int drmkms_timing::get_master_fd() { - const size_t path_length = 15; - char dev_path[path_length]; - char procpath[50]; + const size_t path_length = 20; + char procpath[path_length]; char fullpath[512]; char* actualpath; struct stat st; @@ -721,10 +774,9 @@ int drmkms_timing::get_master_fd() return -1; } - snprintf(dev_path, path_length, "/dev/dri/card%d", m_card_id); - if (!access(dev_path, F_OK) == 0) + if (!access(m_drm_name, F_OK) == 0) { - log_error("DRM/KMS: <%d> (%s) [ERROR] Device %s doesn't exist\n", m_id, __FUNCTION__, dev_path); + log_error("DRM/KMS: <%d> (%s) [ERROR] Device %s doesn't exist\n", m_id, __FUNCTION__, m_drm_name); return -1; } @@ -749,7 +801,7 @@ int drmkms_timing::get_master_fd() continue; actualpath = realpath(fullpath, NULL); // Only check the device we expect - if (strncmp(dev_path, actualpath, path_length) != 0) + if (strncmp(m_drm_name, actualpath, path_length) != 0) { free(actualpath); continue; @@ -770,16 +822,16 @@ int drmkms_timing::get_master_fd() // CASE 3: m_drm_fd is not a master (and probably not even a valid FD), the currend pid doesn't have master rights // Or master is owned by a 3rd party app (like a frontend ...) - log_verbose("DRM/KMS: <%d> (%s) Couldn't find a master FD, opening default /dev/dri/card%d\n", m_id, __FUNCTION__, m_card_id); + log_verbose("DRM/KMS: <%d> (%s) Couldn't find a master FD, opening default %s\n", m_id, __FUNCTION__, m_drm_name); // mark our former hook as invalid m_hook_fd = -1; - fd = open(dev_path, O_RDWR | O_CLOEXEC); + fd = open(m_drm_name, O_RDWR | O_CLOEXEC); if (fd < 0) { // Oh, we're totally screwed here, worst possible scenario - log_error("DRM/KMS: <%d> (%s) Can't open /dev/dri/card%d, can't get master rights\n", m_id, __FUNCTION__, m_card_id); + log_error("DRM/KMS: <%d> (%s) Can't open %s, can't get master rights\n", m_id, __FUNCTION__, m_drm_name); return -1; } diff --git a/deps/switchres/custom_video_drmkms.h b/deps/switchres/custom_video_drmkms.h index 43e2a3acf4..c4b526afb1 100755 --- a/deps/switchres/custom_video_drmkms.h +++ b/deps/switchres/custom_video_drmkms.h @@ -53,6 +53,7 @@ class drmkms_timing : public custom_video int m_caps = 0; char m_device_name[32]; + char m_drm_name[32]; unsigned int m_desktop_output = 0; int m_video_modes_position = 0; @@ -83,6 +84,7 @@ class drmkms_timing : public custom_video __typeof__(drmModeFreePlaneResources) *p_drmModeFreePlaneResources; __typeof__(drmIoctl) *p_drmIoctl; __typeof__(drmGetCap) *p_drmGetCap; + __typeof__(drmGetDevices2) *p_drmGetDevices2; __typeof__(drmIsMaster) *p_drmIsMaster; __typeof__(drmSetMaster) *p_drmSetMaster; __typeof__(drmDropMaster) *p_drmDropMaster; diff --git a/deps/switchres/display.cpp b/deps/switchres/display.cpp index 2fc58634bc..6f70997f81 100644 --- a/deps/switchres/display.cpp +++ b/deps/switchres/display.cpp @@ -104,7 +104,6 @@ void display_manager::parse_options() void display_manager::set_preset(const char *preset) { - strncpy(m_ds.monitor, preset, sizeof(m_ds.monitor)-1); for (size_t i = 0; i < strlen(m_ds.monitor); i++) m_ds.monitor[i] = tolower(m_ds.monitor[i]); memset(&range[0], 0, sizeof(struct monitor_range) * MAX_RANGES); diff --git a/deps/switchres/display.h b/deps/switchres/display.h index ddcaf3e3d0..e3acf7f6ed 100644 --- a/deps/switchres/display.h +++ b/deps/switchres/display.h @@ -97,6 +97,7 @@ public: int v_shift_correct() { return m_ds.gs.v_shift_correct; } int pixel_precision() { return m_ds.gs.pixel_precision; } int interlace_force_even() { return m_ds.gs.interlace_force_even; } + int scale_proportional() { return m_ds.gs.scale_proportional; } // getters (modeline result) bool got_mode() { return (m_selected_mode != nullptr); } @@ -134,7 +135,7 @@ public: void set_current_mode(modeline *mode) { m_current_mode = mode; } // setters (display_manager) - void set_monitor(const char *preset) { set_preset(preset); } + void set_monitor(const char *preset) { strncpy(m_ds.monitor, preset, sizeof(m_ds.monitor)-1); set_preset(preset); } void set_modeline(const char *modeline) { strncpy(m_ds.user_modeline, modeline, sizeof(m_ds.user_modeline)-1); } void set_crt_range(int i, const char *range) { strncpy(m_ds.crt_range[i], range, sizeof(m_ds.crt_range[i])-1); } void set_lcd_range(const char *range) { strncpy(m_ds.lcd_range, range, sizeof(m_ds.lcd_range)-1); } @@ -161,6 +162,7 @@ public: void set_v_shift_correct(int value) { m_ds.gs.v_shift_correct = value; } void set_pixel_precision(int value) { m_ds.gs.pixel_precision = value; } void set_interlace_force_even(int value) { m_ds.gs.interlace_force_even = value; } + void set_scale_proportional(int value) { m_ds.gs.scale_proportional = value; } // setters (custom_video backend) void set_screen_compositing(bool value) { m_ds.vs.screen_compositing = value; } diff --git a/deps/switchres/display_sdl2.cpp b/deps/switchres/display_sdl2.cpp index 5416fe17a4..d8bf892aed 100644 --- a/deps/switchres/display_sdl2.cpp +++ b/deps/switchres/display_sdl2.cpp @@ -19,65 +19,6 @@ #include "display_sdl2.h" #include "log.h" -//============================================================ -// custom_video::get_sdl_hwinfo_from_sdl_window -//============================================================ -void get_sdl_hwinfo_from_sdl_window(SDL_Window* window) -{ - SDL_SysWMinfo m_sdlwminfo; - - SDL_VERSION(&m_sdlwminfo.version); - if(! SDL_GetWindowWMInfo(window, &m_sdlwminfo)) - { - log_error("Couldn't get the SDL WMInfo\n"); - return; - } - const char *subsystem = "an unsupported or unknown system!"; - switch((int)m_sdlwminfo.subsystem) - { - case SDL_SYSWM_UNKNOWN: - case SDL_SYSWM_COCOA: - case SDL_SYSWM_UIKIT: -#if SDL_VERSION_ATLEAST(2, 0, 2) - case SDL_SYSWM_WAYLAND: -#endif - case SDL_SYSWM_MIR: -#if SDL_VERSION_ATLEAST(2, 0, 3) - case SDL_SYSWM_WINRT: -#endif -#if SDL_VERSION_ATLEAST(2, 0, 4) - case SDL_SYSWM_ANDROID: -#endif -#if SDL_VERSION_ATLEAST(2, 0, 5) - case SDL_SYSWM_VIVANTE: -#endif -#if SDL_VERSION_ATLEAST(2, 0, 6) - case SDL_SYSWM_OS2: -#endif -#if SDL_VERSION_ATLEAST(2, 0, 12) - case SDL_SYSWM_HAIKU: -#endif - case SDL_SYSWM_DIRECTFB: - break; - case SDL_SYSWM_WINDOWS: - subsystem = "Microsoft Windows(TM)"; - break; - case SDL_SYSWM_X11: - subsystem = "X Window System"; - break; -#if SDL_VERSION_ATLEAST(2, 0, 16) - case SDL_SYSWM_KMSDRM: - subsystem = "KMSDRM"; - break; -#endif - } - log_info("Switchres/SDL2: Detected SDL version %d.%d.%d on %s\n", - (int)m_sdlwminfo.version.major, - (int)m_sdlwminfo.version.minor, - (int)m_sdlwminfo.version.patch, - subsystem); -} - //============================================================ // sdl2_display::sdl2_display @@ -147,6 +88,11 @@ bool sdl2_display::init(void* pf_data) //SDL_LogSetAllPriority(SDL_LOG_PRIORITY_DEBUG); + // Get SDL version information + SDL_version version; + SDL_GetVersion(&version); + log_info("Switchres/SDL2: Detected SDL version %d.%d.%d\n", (int)version.major, (int)version.minor, (int)version.patch); + SDL_Window* window = NULL; Uint32 id = 0; @@ -185,9 +131,6 @@ bool sdl2_display::init(void* pf_data) log_verbose("Switchres/SDL2: (%s:%d) No SDL2 window found, don't expect things to work good\n", __FUNCTION__, __LINE__); } - if(m_sdlwindow) - get_sdl_hwinfo_from_sdl_window(m_sdlwindow); - // Need a check to see if SDL2 can refresh the modelist return true; } diff --git a/deps/switchres/examples/switch_refresh.cpp b/deps/switchres/examples/switch_refresh.cpp new file mode 100644 index 0000000000..48e9d01a4d --- /dev/null +++ b/deps/switchres/examples/switch_refresh.cpp @@ -0,0 +1,48 @@ +// Test switching of refresh rate only +// Requires working update method +// +// Build: g++ -o switch_refresh switch_refresh.cpp -I ../ -L ../ -ldl -lswitchres -lSDL2 -lSDL2_ttf -ldrm + +#include +#include +#include + +int main(int argc, char** argv) +{ + sr_mode srm; + + sr_set_log_level(3); + sr_init(); + sr_set_disp(-1); + sr_set_monitor("arcade_31"); + sr_init_disp("1", NULL); + + printf("Testing first refresh (50Hz). Press any key...\n"); + getchar(); + + if (!sr_add_mode(648, 480, 50, 0, &srm)) + goto error; + + if (!sr_set_mode(srm.id)) + goto error; + + printf("Testing second refresh (60Hz). Press any key...\n"); + getchar(); + + if(!sr_add_mode(648, 480, 60, 0, &srm)) + goto error; + + if(!sr_set_mode(srm.id)) + goto error; + + printf("Success. Press any key to quit.\n"); + getchar(); + + sr_deinit(); + exit(0); + +error: + printf("ERROR: Exiting!\n"); + sr_deinit(); + exit(1); +} diff --git a/deps/switchres/makefile b/deps/switchres/makefile index 89f52e0a78..6811ca43fd 100644 --- a/deps/switchres/makefile +++ b/deps/switchres/makefile @@ -64,6 +64,9 @@ else CPPFLAGS += -DSR_WITH_KMSDRM EXTRA_LIBS = libdrm SRC += custom_video_drmkms.cpp + ifeq ($(SR_WITH_DRMHOOK),1) + CPPFLAGS += -DSR_WITH_DRMHOOK + endif endif # SDL2 misses a test for drm as drm.h is required @@ -150,8 +153,8 @@ install: $(INSTALL) -Dm644 switchres.h $(INCDIR)/switchres/switchres.h $(INSTALL) -Dm644 switchres.pc $(PKGDIR)/switchres.pc ifneq ($(SO_NAME),) - $(LN) -s $(REAL_SO_NAME) $(LIBDIR)/$(SO_NAME) - $(LN) -s $(SO_NAME) $(LIBDIR)/$(LINKER_NAME) + $(LN) -s -f $(REAL_SO_NAME) $(LIBDIR)/$(SO_NAME) + $(LN) -s -f $(SO_NAME) $(LIBDIR)/$(LINKER_NAME) endif uninstall: diff --git a/deps/switchres/modeline.cpp b/deps/switchres/modeline.cpp index 35a19e3100..879d8c7819 100644 --- a/deps/switchres/modeline.cpp +++ b/deps/switchres/modeline.cpp @@ -168,9 +168,9 @@ int modeline_create(modeline *s_mode, modeline *t_mode, monitor_range *range, ge // if we can, let's apply the same scaling to both directions if (t_mode->type & X_RES_EDITABLE) { - x_scale = y_scale; + x_scale = cs->scale_proportional? y_scale : 1; double aspect_corrector = max(1.0f, cs->monitor_aspect / source_aspect); - t_mode->hactive = normalize(double(t_mode->hactive) * double(x_scale) * aspect_corrector, 8); + t_mode->hactive = normalize(double(t_mode->hactive) * double(x_scale) * aspect_corrector, cs->pixel_precision? 1 : 8); } // otherwise, try to get the best out of our current xres @@ -204,7 +204,7 @@ int modeline_create(modeline *s_mode, modeline *t_mode, monitor_range *range, ge // check if we can create a normal aspect resolution if (t_mode->type & X_RES_EDITABLE) - t_mode->hactive = max(t_mode->hactive, normalize(STANDARD_CRT_ASPECT * t_mode->vactive, 8)); + t_mode->hactive = max(t_mode->hactive, normalize(STANDARD_CRT_ASPECT * t_mode->vactive, cs->pixel_precision? 1 : 8)); // calculate integer scale for prescaling x_scale = max(1, scale_into_aspect(s_mode->hactive, t_mode->hactive, source_aspect, cs->monitor_aspect, &x_diff)); diff --git a/deps/switchres/modeline.h b/deps/switchres/modeline.h index ac71ddcfb1..afa269c33e 100644 --- a/deps/switchres/modeline.h +++ b/deps/switchres/modeline.h @@ -117,6 +117,7 @@ typedef struct generator_settings int v_shift_correct; int pixel_precision; int interlace_force_even; + int scale_proportional; } generator_settings; //============================================================ diff --git a/deps/switchres/switchres.cpp b/deps/switchres/switchres.cpp index 64736d848e..5623117cdc 100644 --- a/deps/switchres/switchres.cpp +++ b/deps/switchres/switchres.cpp @@ -89,7 +89,7 @@ switchres_manager::switchres_manager() display()->set_monitor("generic_15"); display()->set_modeline("auto"); display()->set_lcd_range("auto"); - for (int i = 0; i++ < MAX_RANGES;) display()->set_crt_range(i, "auto"); + for (int i = 0; i < MAX_RANGES; i++) display()->set_crt_range(i, "auto"); display()->set_screen("auto"); display()->set_modeline_generation(true); display()->set_lock_unsupported_modes(true); @@ -109,6 +109,7 @@ switchres_manager::switchres_manager() display()->set_v_shift_correct(0); display()->set_pixel_precision(1); display()->set_interlace_force_even(0); + display()->set_scale_proportional(1); // Set logger properties set_log_info_fn((void*)printf); @@ -359,6 +360,10 @@ void switchres_manager::set_option(const char* key, const char* value) display()->set_interlace_force_even(atoi(value)); break; + case s2i("scale_proportional"): + display()->set_scale_proportional(atoi(value)); + break; + // Custom video backend options case s2i("screen_compositing"): display()->set_screen_compositing(atoi(value)); diff --git a/deps/switchres/switchres.h b/deps/switchres/switchres.h index d6af27e3cf..80233b0498 100644 --- a/deps/switchres/switchres.h +++ b/deps/switchres/switchres.h @@ -27,7 +27,7 @@ //============================================================ #ifndef SWITCHRES_VERSION -#define SWITCHRES_VERSION "2.1.0" +#define SWITCHRES_VERSION "2.2.1" #endif diff --git a/deps/switchres/switchres.ini b/deps/switchres/switchres.ini index 8c7b1ea3c7..e191c161cc 100644 --- a/deps/switchres/switchres.ini +++ b/deps/switchres/switchres.ini @@ -122,6 +122,9 @@ # Calculate all vertical values of interlaced modes as even numbers. Required by AMD APU hardware on Linux interlace_force_even 0 +# Scale both axes by the same factor, when integer scaling is applied + scale_proportional 1 + # # Custom video backend config diff --git a/deps/switchres/switchres_defines.h b/deps/switchres/switchres_defines.h index 9a9e96759d..42470eda9b 100644 --- a/deps/switchres/switchres_defines.h +++ b/deps/switchres/switchres_defines.h @@ -33,6 +33,7 @@ #define SR_OPT_V_SHIFT "v_shift" #define SR_OPT_PIXEL_PRECISION "pixel_precision" #define SR_OPT_INTERLACE_FORCE_EVEN "interlace_force_even" +#define SR_OPT_SCALE_PROPORTIONAL "scale_proportional" #define SR_OPT_SCREEN_COMPOSITING "screen_compositing" #define SR_OPT_SCREEN_REORDERING "screen_reordering" #define SR_OPT_ALLOW_HARDWARE_REFRESH "allow_hardware_refresh" diff --git a/deps/switchres/switchres_main.cpp b/deps/switchres/switchres_main.cpp index c793cbf1a0..ac16a94003 100644 --- a/deps/switchres/switchres_main.cpp +++ b/deps/switchres/switchres_main.cpp @@ -16,6 +16,7 @@ #include #include #include "switchres.h" +#include "switchres_defines.h" #include "log.h" using namespace std; @@ -25,7 +26,42 @@ int show_usage(); enum { - OPT_MODELINE = 128 + OPT_CRT_RANGE0 = 128, + OPT_CRT_RANGE1, + OPT_CRT_RANGE2, + OPT_CRT_RANGE3, + OPT_CRT_RANGE4, + OPT_CRT_RANGE5, + OPT_CRT_RANGE6, + OPT_CRT_RANGE7, + OPT_CRT_RANGE8, + OPT_CRT_RANGE9, + OPT_LCD_RANGE, + OPT_MODELINE, + OPT_USER_MODE, + OPT_API, + OPT_LOCK_UNSUPPORTED_MODES, + OPT_LOCK_SYSTEM_MODES, + OPT_REFRESH_DONT_CARE, + OPT_KEEP_CHANGES, + OPT_MODELINE_GENERATION, + OPT_INTERLACE, + OPT_DOUBLESCAN, + OPT_DOTCLOCK_MIN, + OPT_SYNC_REFRESH_TOLERANCE, + OPT_SUPER_WIDTH, + OPT_V_SHIFT_CORRECT, + OPT_H_SIZE, + OPT_H_SHIFT, + OPT_V_SHIFT, + OPT_PIXEL_PRECISION, + OPT_INTERLACE_FORCE_EVEN, + OPT_SCALE_PROPORTIONAL, + OPT_SCREEN_COMPOSITING, + OPT_SCREEN_REORDERING, + OPT_ALLOW_HARDWARE_REFRESH, + OPT_CUSTOM_TIMING, + OPT_VERBOSITY }; //============================================================ @@ -68,28 +104,65 @@ int main(int argc, char **argv) { static struct option long_options[] = { + // Options unique to standalone Switchres {"version", no_argument, &version_flag, '1'}, {"help", no_argument, 0, 'h'}, {"calc", no_argument, 0, 'c'}, {"switch", no_argument, 0, 's'}, {"launch", required_argument, 0, 'l'}, - {"monitor", required_argument, 0, 'm'}, - {"aspect", required_argument, 0, 'a'}, {"edid", no_argument, 0, 'e'}, {"rotated", no_argument, 0, 'r'}, - {"display", required_argument, 0, 'd'}, - {"force", required_argument, 0, 'f'}, + {"force", required_argument, 0, 'f'}, // equ. --user_mode {"ini", required_argument, 0, 'i'}, - {"verbose", no_argument, 0, 'v'}, - {"backend", required_argument, 0, 'b'}, - {"keep", no_argument, 0, 'k'}, + {"keep", no_argument, 0, 'k'}, // equ. --keep_changes {"geometry", required_argument, 0, 'g'}, - {"modeline", required_argument, 0, OPT_MODELINE}, + // Options available in short and long forms + {SR_OPT_VERBOSE, no_argument, 0, 'v'}, + {SR_OPT_DISPLAY, required_argument, 0, 'd'}, + {SR_OPT_MONITOR, required_argument, 0, 'm'}, + {SR_OPT_ASPECT, required_argument, 0, 'a'}, + // Long options, from switchres.ini + {SR_OPT_CRT_RANGE0, required_argument, 0, OPT_CRT_RANGE0}, + {SR_OPT_CRT_RANGE1, required_argument, 0, OPT_CRT_RANGE1}, + {SR_OPT_CRT_RANGE2, required_argument, 0, OPT_CRT_RANGE2}, + {SR_OPT_CRT_RANGE3, required_argument, 0, OPT_CRT_RANGE3}, + {SR_OPT_CRT_RANGE4, required_argument, 0, OPT_CRT_RANGE4}, + {SR_OPT_CRT_RANGE5, required_argument, 0, OPT_CRT_RANGE5}, + {SR_OPT_CRT_RANGE6, required_argument, 0, OPT_CRT_RANGE6}, + {SR_OPT_CRT_RANGE7, required_argument, 0, OPT_CRT_RANGE7}, + {SR_OPT_CRT_RANGE8, required_argument, 0, OPT_CRT_RANGE8}, + {SR_OPT_CRT_RANGE9, required_argument, 0, OPT_CRT_RANGE9}, + {SR_OPT_LCD_RANGE, required_argument, 0, OPT_LCD_RANGE}, + {SR_OPT_MODELINE, required_argument, 0, OPT_MODELINE}, + {SR_OPT_USER_MODE, required_argument, 0, OPT_USER_MODE}, + {SR_OPT_API, required_argument, 0, OPT_API}, + {SR_OPT_LOCK_UNSUPPORTED_MODES, required_argument, 0, OPT_LOCK_UNSUPPORTED_MODES}, + {SR_OPT_LOCK_SYSTEM_MODES, required_argument, 0, OPT_LOCK_SYSTEM_MODES}, + {SR_OPT_REFRESH_DONT_CARE, required_argument, 0, OPT_REFRESH_DONT_CARE}, + {SR_OPT_KEEP_CHANGES, required_argument, 0, OPT_KEEP_CHANGES}, + {SR_OPT_MODELINE_GENERATION, required_argument, 0, OPT_MODELINE_GENERATION}, + {SR_OPT_INTERLACE, required_argument, 0, OPT_INTERLACE}, + {SR_OPT_DOUBLESCAN, required_argument, 0, OPT_DOUBLESCAN}, + {SR_OPT_DOTCLOCK_MIN, required_argument, 0, OPT_DOTCLOCK_MIN}, + {SR_OPT_SYNC_REFRESH_TOLERANCE, required_argument, 0, OPT_SYNC_REFRESH_TOLERANCE}, + {SR_OPT_SUPER_WIDTH, required_argument, 0, OPT_SUPER_WIDTH}, + {SR_OPT_V_SHIFT_CORRECT, required_argument, 0, OPT_V_SHIFT_CORRECT}, + {SR_OPT_H_SIZE, required_argument, 0, OPT_H_SIZE}, + {SR_OPT_H_SHIFT, required_argument, 0, OPT_H_SHIFT}, + {SR_OPT_V_SHIFT, required_argument, 0, OPT_V_SHIFT}, + {SR_OPT_PIXEL_PRECISION, required_argument, 0, OPT_PIXEL_PRECISION}, + {SR_OPT_INTERLACE_FORCE_EVEN, required_argument, 0, OPT_INTERLACE_FORCE_EVEN}, + {SR_OPT_SCALE_PROPORTIONAL, required_argument, 0, OPT_SCALE_PROPORTIONAL}, + {SR_OPT_SCREEN_COMPOSITING, required_argument, 0, OPT_SCREEN_COMPOSITING}, + {SR_OPT_SCREEN_REORDERING, required_argument, 0, OPT_SCREEN_REORDERING}, + {SR_OPT_ALLOW_HARDWARE_REFRESH, required_argument, 0, OPT_ALLOW_HARDWARE_REFRESH}, + {SR_OPT_CUSTOM_TIMING, required_argument, 0, OPT_CUSTOM_TIMING}, + {SR_OPT_VERBOSITY, required_argument, 0, OPT_VERBOSITY}, {0, 0, 0, 0} }; int option_index = 0; - int c = getopt_long(argc, argv, "vhcsl:m:a:erd:f:i:b:kg:", long_options, &option_index); + int c = getopt_long(argc, argv, "vhcsl:m:a:erd:f:i:kg:", long_options, &option_index); if (c == -1) break; @@ -102,10 +175,6 @@ int main(int argc, char **argv) switch (c) { - case OPT_MODELINE: - df->set_modeline(optarg); - break; - case 'v': switchres.set_log_level(3); switchres.set_log_error_fn((void*)printf); @@ -142,7 +211,8 @@ int main(int argc, char **argv) // Add new display in multi-monitor case if (index > 0) switchres.add_display(); index ++; - df->set_screen(optarg); + switchres.set_current_display(-1); + switchres.set_option(SR_OPT_DISPLAY, optarg); break; case 'a': @@ -154,9 +224,10 @@ int main(int argc, char **argv) break; case 'f': + case OPT_USER_MODE: force_flag = true; if (sscanf(optarg, "%dx%d@%d", &user_mode.width, &user_mode.height, &user_mode.refresh) < 1) - log_error("Error: use format --force x@\n"); + log_error("Error: use format x@\n"); break; case 'i': @@ -183,6 +254,45 @@ int main(int argc, char **argv) df->set_v_shift(v_shift); break; + // Long options + case OPT_CRT_RANGE0: + case OPT_CRT_RANGE1: + case OPT_CRT_RANGE2: + case OPT_CRT_RANGE3: + case OPT_CRT_RANGE4: + case OPT_CRT_RANGE5: + case OPT_CRT_RANGE6: + case OPT_CRT_RANGE7: + case OPT_CRT_RANGE8: + case OPT_CRT_RANGE9: + case OPT_LCD_RANGE: + case OPT_MODELINE: + case OPT_API: + case OPT_LOCK_UNSUPPORTED_MODES: + case OPT_LOCK_SYSTEM_MODES: + case OPT_REFRESH_DONT_CARE: + case OPT_KEEP_CHANGES: + case OPT_MODELINE_GENERATION: + case OPT_INTERLACE: + case OPT_DOUBLESCAN: + case OPT_DOTCLOCK_MIN: + case OPT_SYNC_REFRESH_TOLERANCE: + case OPT_SUPER_WIDTH: + case OPT_V_SHIFT_CORRECT: + case OPT_H_SIZE: + case OPT_H_SHIFT: + case OPT_V_SHIFT: + case OPT_PIXEL_PRECISION: + case OPT_INTERLACE_FORCE_EVEN: + case OPT_SCALE_PROPORTIONAL: + case OPT_SCREEN_COMPOSITING: + case OPT_SCREEN_REORDERING: + case OPT_ALLOW_HARDWARE_REFRESH: + case OPT_CUSTOM_TIMING: + case OPT_VERBOSITY: + switchres.set_option(long_options[option_index].name, optarg); + break; + default: return 0; } @@ -265,7 +375,7 @@ int main(int argc, char **argv) monitor_range *range = &switchres.display()->range[mode->range]; edid_from_modeline(mode, range, switchres.display()->monitor(), &edid); - char file_name[strlen(switchres.display()->monitor()) + 4]; + char file_name[strlen(switchres.display()->monitor()) + 5]; sprintf(file_name, "%s.bin", switchres.display()->monitor()); FILE *file = fopen(file_name, "wb"); @@ -313,7 +423,7 @@ int show_version() { "Switchres " SWITCHRES_VERSION "\n" "Modeline generation engine for emulation\n" - "Copyright (C) 2010-2021 - Chris Kennedy, Antonio Giner, Alexandre Wodarczyk, Gil Delescluse\n" + "Copyright (C) 2010-2024 - Chris Kennedy, Antonio Giner, Alexandre Wodarczyk, Gil Delescluse\n" "License GPL-2.0+\n" "This is free software: you are free to change and redistribute it.\n" "There is NO WARRANTY, to the extent permitted by law.\n" @@ -338,15 +448,17 @@ int show_usage() " -l, --launch Launch \n" " -m, --monitor Monitor preset (generic_15, arcade_15, pal, ntsc, etc.)\n" " -a, --aspect Monitor aspect ratio\n" - " -r, --rotated Original mode's native orientation is rotated\n" - " -d, --display Use target display (Windows: \\\\.\\DISPLAY1, ... Linux: VGA-0, ...)\n" + " -r, --rotated Rotate axes, preserving aspect ratio.\n" + " -d, --display Use target display (index = 0, 1, 2...)\n" " -f, --force x@ Force a specific video mode from display mode list\n" " -i, --ini Specify an ini file\n" - " -b, --backend Specify the api name\n" " -e, --edid Create an EDID binary with calculated video modes\n" " -k, --keep Keep changes on exit (warning: this disables cleanup)\n" - " -g, --geometry :: Adjust geometry of generated modeline\n" - " --modeline <\"pclk hdisp hsst hsend htot vdisp vsst vsend vtot flags\"> Force an XFree86 modeline\n" + " -g, --geometry Adjust geometry of generated modeline\n" + " adjustment = ::\n" + " e.g. switchres 640 480 60 -c -g 1.1:-1:2\n\n" + "For more options, refer to switchres.ini. All options in switchres.ini can be applied in\n" + "command line as long options, e.g.: switchres 256 224 57.55 -c --dotclock_min 8.0\n\n" }; log_info("%s", usage);