Bump switchres to 2.2.1 (#16782)

* Remove switchres before bump

* Squashed 'deps/switchres/' content from commit 725e4d484a

git-subtree-dir: deps/switchres
git-subtree-split: 725e4d484a33632618dd44cdc2a61948dd833282
This commit is contained in:
Subs 2024-07-18 15:25:07 +02:00 committed by GitHub
parent bd69602686
commit f1a37f7c75
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 326 additions and 121 deletions

View File

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

View File

@ -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 <command> Launch <command>
-m, --monitor <preset> Monitor preset (generic_15, arcade_15, pal, ntsc, etc.)
-a --aspect <num:den> Monitor aspect ratio
-r --rotated Original mode's native orientation is rotated
-d, --display <OS_display_name> Use target display (Windows: \\\\.\\DISPLAY1, ... Linux: VGA-0, ...)
-r --rotated Rotate axes, preserving aspect ratio
-d, --display <display_index> Use target display (index = 0, 1, 2...)
-f, --force <w>x<h>@<r> Force a specific video mode from display mode list
-i, --ini <file.ini> Specify an ini file
-b, --backend <api_name> Specify the api name
-k, --keep Keep changes on exit (warning: this disables cleanup)
-g, --geometry <adjustment> Adjust geometry of generated modeline
adjustment = <h_size>:<h_shift>:<v_shift>
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.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 <stdio.h>
#include <stdlib.h>
#include <switchres/switchres_wrapper.h>
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);
}

View File

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

View File

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

View File

@ -117,6 +117,7 @@ typedef struct generator_settings
int v_shift_correct;
int pixel_precision;
int interlace_force_even;
int scale_proportional;
} generator_settings;
//============================================================

View File

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

View File

@ -27,7 +27,7 @@
//============================================================
#ifndef SWITCHRES_VERSION
#define SWITCHRES_VERSION "2.1.0"
#define SWITCHRES_VERSION "2.2.1"
#endif

View File

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

View File

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

View File

@ -16,6 +16,7 @@
#include <cstring>
#include <getopt.h>
#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 <w>x<h>@<r>\n");
log_error("Error: use format <w>x<h>@<r>\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 <command> Launch <command>\n"
" -m, --monitor <preset> Monitor preset (generic_15, arcade_15, pal, ntsc, etc.)\n"
" -a, --aspect <num:den> Monitor aspect ratio\n"
" -r, --rotated Original mode's native orientation is rotated\n"
" -d, --display <OS_display_name> Use target display (Windows: \\\\.\\DISPLAY1, ... Linux: VGA-0, ...)\n"
" -r, --rotated Rotate axes, preserving aspect ratio.\n"
" -d, --display <display_index> Use target display (index = 0, 1, 2...)\n"
" -f, --force <w>x<h>@<r> Force a specific video mode from display mode list\n"
" -i, --ini <file.ini> Specify an ini file\n"
" -b, --backend <api_name> 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 <h_size>:<h_shift>:<v_shift> Adjust geometry of generated modeline\n"
" --modeline <\"pclk hdisp hsst hsend htot vdisp vsst vsend vtot flags\"> Force an XFree86 modeline\n"
" -g, --geometry <adjustment> Adjust geometry of generated modeline\n"
" adjustment = <h_size>:<h_shift>:<v_shift>\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);