[CRT] Add KMS modeswitch (#15131)

* Prepare to update deps/switchres

* Squashed 'deps/switchres/' content from commit ca72648b32

git-subtree-dir: deps/switchres
git-subtree-split: ca72648b3253eca8c5addf64d1e4aa1c43f5db94

* Add CRT modeswitching to KMS
Display the real refresh rate
Enable the CRT SwitchRes menu
Add another switchres.ini path for Lakka
This commit is contained in:
Subs 2023-03-25 11:57:10 +01:00 committed by GitHub
parent d118259589
commit f24893bcb1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 8167 additions and 1205 deletions

View File

@ -0,0 +1,64 @@
name: Switchres CIv2
on: [push, pull_request]
jobs:
buildx86_64:
runs-on: ubuntu-latest
name: ${{ matrix.platform.name }} ${{ matrix.config.name }}
strategy:
matrix:
platform:
- { 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'
run: sudo apt-get install libxrandr-dev libdrm-dev libsdl2-dev
- name: Install Windows dependencies
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
wget https://github.com/libsdl-org/SDL/releases/download/release-2.26.2/SDL2-devel-2.26.2-mingw.tar.gz
tar xvzf SDL2-devel-2.26.2-mingw.tar.gz
sudo make -C SDL2-2.26.2 CROSS_PATH=/usr ARCHITECTURES=x86_64-w64-mingw32 cross
wget https://github.com/libsdl-org/SDL_ttf/releases/download/release-2.20.1/SDL2_ttf-devel-2.20.1-mingw.tar.gz
tar xvzf SDL2_ttf-devel-2.20.1-mingw.tar.gz
sudo make -C SDL2_ttf-2.20.1 CROSS_PATH=/usr ARCHITECTURES=x86_64-w64-mingw32 cross
- name: Build libs and binary
run: |
make libswitchres ${{matrix.platform.make_opts}}
make ${{matrix.platform.make_opts}}
- name: Build grid.exe (Windows only)
if: matrix.platform.name == 'Windows MINGW'
run: |
make ${{matrix.platform.make_opts}} clean
make ${{matrix.platform.make_opts}} grid
- name: Prepare artifacts
run: mkdir artifacts && cp -v libswitchres.{so,a,dll,lib} switchres{,.exe,.ini} grid{,.exe} /usr/x86_64-w64-mingw32/bin/SDL2{,_ttf}.dll /usr/x86_64-w64-mingw32/lib/libwinpthread-1.dll artifacts/ || true
- name: Upload artifact
uses: actions/upload-artifact@v3.1.0
with:
name: switchres-${{matrix.platform.name}}-x86_64
path: artifacts/
win32-build-x86_64-geometry:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Build geometry.exe
run: |
pip install pyinstaller
pyinstaller --onefile geometry.py --icon=tv.ico
- name: Upload artifact
uses: actions/upload-artifact@v3.1.0
with:
name: geometry-win32-x86_64
path: dist/

10
deps/switchres/.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
*.o
*.exe
*.a
*.so.*
*.lib
*.dll
switchres_main
grid
switchres
switchres.pc

59
deps/switchres/.gitlab-ci.yml vendored Normal file
View File

@ -0,0 +1,59 @@
# This file is a template, and might need editing before it works on your project.
# use the official gcc image, based on debian
# can use verions as well, like gcc:5.2
# see https://hub.docker.com/_/gcc/
image: gcc:latest
before_script:
- apt update
- apt -y install make
.pre_requisites_linux: &prerequisiteslinux
before_script:
- apt update
- apt -y install make
.pre_requisites_win32: &prerequisiteswin32
image: "ubuntu:rolling"
before_script:
- apt update
- apt -y install make mingw-w64
linux:x86_64:standalone:
stage: build
<<: *prerequisiteslinux
script:
- make
linux:x86_64:lib:
stage: build
<<: *prerequisiteslinux
script:
- make libswitchres
win32:x86_64:standalone:
stage: build
<<: *prerequisiteswin32
script:
- make PLATFORM=NT CROSS_COMPILE=x86_64-w64-mingw32-
win32:x86_64:lib:
stage: build
<<: *prerequisiteswin32
script:
- make PLATFORM=NT CROSS_COMPILE=x86_64-w64-mingw32- libswitchres
win32:i686:standalone:
stage: build
<<: *prerequisiteswin32
script:
- make PLATFORM=NT CROSS_COMPILE=i686-w64-mingw32-
win32:i686:lib:
stage: build
<<: *prerequisiteswin32
script:
- make PLATFORM=NT CROSS_COMPILE=i686-w64-mingw32- libswitchres

View File

@ -19,6 +19,7 @@
#include <cstring>
#include "modeline.h"
#define CUSTOM_VIDEO_TIMING_MASK 0x00000ff0
#define CUSTOM_VIDEO_TIMING_AUTO 0x00000000
#define CUSTOM_VIDEO_TIMING_SYSTEM 0x00000010
@ -61,7 +62,7 @@ public:
delete m_custom_video;
m_custom_video = nullptr;
}
}
};
custom_video *make(char *device_name, char *device_id, int method, custom_video_settings *vs);
virtual const char *api_name() { return "empty"; }
@ -101,7 +102,6 @@ private:
custom_video *m_custom_video = 0;
int m_custom_method;
};
#endif

755
deps/switchres/custom_video_drmkms.cpp vendored Normal file → Executable file

File diff suppressed because it is too large Load Diff

22
deps/switchres/custom_video_drmkms.h vendored Normal file → Executable file
View File

@ -26,23 +26,31 @@ class drmkms_timing : public custom_video
drmkms_timing(char *device_name, custom_video_settings *vs);
~drmkms_timing();
const char *api_name() { return "DRMKMS"; }
int caps() { return CUSTOM_VIDEO_CAPS_ADD; }
int caps() { return m_caps; }
bool init();
bool add_mode(modeline *mode);
bool delete_mode(modeline *mode);
bool update_mode(modeline *mode);
bool process_modelist(std::vector<modeline *>);
bool get_timing(modeline *mode);
bool set_timing(modeline *mode);
private:
/*
* Consider m_id as the "display number": 1 for the 1st, 2 for the second etc...
*/
int m_id = 0;
int m_drm_fd = 0;
int m_drm_fd = -1;
drmModeCrtc *mp_crtc_desktop = NULL;
int m_card_id = 0;
int drm_master_hook(int fd);
bool m_kernel_user_modes = false;
bool can_drop_master = true;
int m_hook_fd = -1;
int m_caps = 0;
char m_device_name[32];
unsigned int m_desktop_output = 0;
@ -56,6 +64,7 @@ class drmkms_timing : public custom_video
__typeof__(drmFreeVersion) *p_drmFreeVersion;
__typeof__(drmModeGetResources) *p_drmModeGetResources;
__typeof__(drmModeGetConnector) *p_drmModeGetConnector;
__typeof__(drmModeGetConnectorCurrent) *p_drmModeGetConnectorCurrent;
__typeof__(drmModeFreeConnector) *p_drmModeFreeConnector;
__typeof__(drmModeFreeResources) *p_drmModeFreeResources;
__typeof__(drmModeGetEncoder) *p_drmModeGetEncoder;
@ -64,6 +73,7 @@ class drmkms_timing : public custom_video
__typeof__(drmModeSetCrtc) *p_drmModeSetCrtc;
__typeof__(drmModeFreeCrtc) *p_drmModeFreeCrtc;
__typeof__(drmModeAttachMode) *p_drmModeAttachMode;
__typeof__(drmModeDetachMode) *p_drmModeDetachMode;
__typeof__(drmModeAddFB) *p_drmModeAddFB;
__typeof__(drmModeRmFB) *p_drmModeRmFB;
__typeof__(drmModeGetFB) *p_drmModeGetFB;
@ -76,6 +86,12 @@ class drmkms_timing : public custom_video
__typeof__(drmIsMaster) *p_drmIsMaster;
__typeof__(drmSetMaster) *p_drmSetMaster;
__typeof__(drmDropMaster) *p_drmDropMaster;
bool test_kernel_user_modes();
bool kms_has_mode(modeline*);
void list_drm_modes();
int get_master_fd();
};
#endif

View File

@ -132,21 +132,21 @@ xrandr_timing::xrandr_timing(char *device_name, custom_video_settings *vs)
if (p_XOpenDisplay == NULL)
{
log_error("XRANDR: <%d> (xrandr_timing) [ERROR] missing func %s in %s\n", m_id, "XOpenDisplay", "X11_LIBRARY");
throw new std::exception();
throw std::exception();
}
else
{
if (!XOpenDisplay(NULL))
{
log_verbose("XRANDR: <%d> (xrandr_timing) X server not found\n", m_id);
throw new std::exception();
throw std::exception();
}
}
}
else
{
log_error("XRANDR: <%d> (xrandr_timing) [ERROR] missing %s library\n", m_id, "X11_LIBRARY");
throw new std::exception();
throw std::exception();
}
s_total_managed_screen++;
@ -161,6 +161,8 @@ xrandr_timing::~xrandr_timing()
s_total_managed_screen--;
if (s_total_managed_screen == 0)
{
s_id = 0;
if (sp_desktop_crtc)
delete[]sp_desktop_crtc;

View File

@ -13,13 +13,18 @@
**************************************************************/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "display.h"
#if defined(_WIN32)
#include "display_windows.h"
#elif defined(__linux__)
#include <string.h>
#include "display_linux.h"
#endif
#ifdef SR_WITH_SDL2
#include "display_sdl2.h"
#endif
#include "log.h"
//============================================================
@ -30,12 +35,32 @@ display_manager *display_manager::make(display_settings *ds)
{
display_manager *display = nullptr;
if (!strcmp(ds->screen, "dummy"))
{
display = new dummy_display(ds);
return display;
}
#ifdef SR_WITH_SDL2
try
{
display = new sdl2_display(ds);
}
catch (...) {};
if (!display)
{
#endif
#if defined(_WIN32)
display = new windows_display(ds);
#elif defined(__linux__)
display = new linux_display(ds);
#endif
#ifdef SR_WITH_SDL2
}
#endif
return display;
}
@ -45,6 +70,9 @@ display_manager *display_manager::make(display_settings *ds)
void display_manager::parse_options()
{
log_verbose("Switchres: display[%d] options: monitor[%s] generation[%s]\n",
m_index, m_ds.monitor, m_ds.modeline_generation?"on":"off");
// Get user_mode as <w>x<h>@<r>
set_user_mode(&m_ds.user_mode);
@ -54,6 +82,7 @@ void display_manager::parse_options()
{
if (modeline_parse(m_ds.user_modeline, &user_mode))
{
memset(&range[0], 0, sizeof(struct monitor_range) * MAX_RANGES);
user_mode.type |= MODE_USER_DEF;
set_user_mode(&user_mode);
}
@ -66,29 +95,38 @@ void display_manager::parse_options()
monitor_show_range(range);
}
else
{
char default_monitor[] = "generic_15";
set_preset(m_ds.monitor);
}
memset(&range[0], 0, sizeof(struct monitor_range) * MAX_RANGES);
//============================================================
// display_manager::set_preset
//============================================================
if (!strcmp(m_ds.monitor, "custom"))
for (int i = 0; i < MAX_RANGES; i++) monitor_fill_range(&range[i], m_ds.crt_range[i]);
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]);
else if (!strcmp(m_ds.monitor, "lcd"))
monitor_fill_lcd_range(&range[0], m_ds.lcd_range);
memset(&range[0], 0, sizeof(struct monitor_range) * MAX_RANGES);
else if (monitor_set_preset(m_ds.monitor, range) == 0)
monitor_set_preset(default_monitor, range);
}
if (!strcmp(preset, "custom"))
for (int i = 0; i < MAX_RANGES; i++) monitor_fill_range(&range[i], m_ds.crt_range[i]);
else if (!strcmp(preset, "lcd"))
monitor_fill_lcd_range(&range[0], m_ds.lcd_range);
else if (monitor_set_preset(preset, range) == 0)
monitor_set_preset("generic_15", range);
}
//============================================================
// display_manager::init
//============================================================
bool display_manager::init()
bool display_manager::init(void* pf_data)
{
sprintf(m_ds.screen, "ram");
m_pf_data = pf_data;
return true;
}
@ -99,7 +137,7 @@ bool display_manager::init()
int display_manager::caps()
{
if (video())
if (video() != nullptr)
return video()->caps();
else
return CUSTOM_VIDEO_CAPS_ADD;
@ -111,11 +149,8 @@ int display_manager::caps()
bool display_manager::add_mode(modeline *mode)
{
if (video() == nullptr)
return false;
// Add new mode
if (!video()->add_mode(mode))
if (video() != nullptr && !video()->add_mode(mode))
{
log_verbose("Switchres: error adding mode ");
log_mode(mode);
@ -136,10 +171,7 @@ bool display_manager::add_mode(modeline *mode)
bool display_manager::delete_mode(modeline *mode)
{
if (video() == nullptr)
return false;
if (!video()->delete_mode(mode))
if (video() != nullptr && !video()->delete_mode(mode))
{
log_verbose("Switchres: error deleting mode ");
log_mode(mode);
@ -148,6 +180,10 @@ bool display_manager::delete_mode(modeline *mode)
log_verbose("Switchres: deleted ");
log_mode(mode);
ptrdiff_t i = mode - &video_modes[0];
video_modes.erase(video_modes.begin() + i);
return true;
}
@ -157,11 +193,8 @@ bool display_manager::delete_mode(modeline *mode)
bool display_manager::update_mode(modeline *mode)
{
if (video() == nullptr)
return false;
// Apply new timings
if (!video()->update_mode(mode))
if (video() != nullptr && !video()->update_mode(mode))
{
log_verbose("Switchres: error updating mode ");
log_mode(mode);
@ -191,7 +224,7 @@ bool display_manager::set_mode(modeline *)
void display_manager::log_mode(modeline *mode)
{
char modeline_txt[256];
log_verbose("%s timing %s\n", video()->api_name(), modeline_print(mode, modeline_txt, MS_FULL));
log_verbose("%s timing %s\n", video() != nullptr? video()->api_name() : "dummy", modeline_print(mode, modeline_txt, MS_FULL));
}
//============================================================
@ -227,9 +260,6 @@ bool display_manager::flush_modes()
bool error = false;
std::vector<modeline *> modified_modes = {};
if (video() == nullptr)
return false;
// Loop through our mode table to collect all pending changes
for (auto &mode : video_modes)
if (mode.type & (MODE_UPDATE | MODE_ADD | MODE_DELETE))
@ -238,7 +268,8 @@ bool display_manager::flush_modes()
// Flush pending changes to driver
if (modified_modes.size() > 0)
{
video()->process_modelist(modified_modes);
if (video() != nullptr)
video()->process_modelist(modified_modes);
// Log error/success result for each mode
for (auto &mode : modified_modes)
@ -259,7 +290,7 @@ bool display_manager::flush_modes()
if (video_modes[i].type & MODE_DELETE)
{
video_modes.erase(video_modes.begin() + i);
m_best_mode = 0;
m_selected_mode = 0;
}
else
video_modes[i].type &= ~(MODE_UPDATE | MODE_ADD);
@ -319,25 +350,32 @@ bool display_manager::filter_modes()
// display_manager::get_video_mode
//============================================================
modeline *display_manager::get_mode(int width, int height, float refresh, bool interlaced)
modeline *display_manager::get_mode(int width, int height, float refresh, int flags)
{
modeline s_mode = {};
modeline t_mode = {};
modeline best_mode = {};
char result[256]={'\x00'};
log_verbose("Switchres: Calculating best video mode for %dx%d@%.6f%s orientation: %s\n",
width, height, refresh, interlaced?"i":"", rotation()?"rotated":"normal");
bool rotated = flags & SR_MODE_ROTATED;
bool interlaced = flags & SR_MODE_INTERLACED;
log_info("Switchres: Calculating best video mode for %dx%d@%.6f%s orientation: %s\n",
width, height, refresh, interlaced?"i":"", rotated?"rotated":"normal");
best_mode.result.weight |= R_OUT_OF_RANGE;
s_mode.interlace = interlaced;
s_mode.vfreq = refresh;
s_mode.hactive = normalize(width, 8);
s_mode.hactive = width;
s_mode.vactive = height;
if (rotation()) std::swap(s_mode.hactive, s_mode.vactive);
if (rotated)
{
std::swap(s_mode.hactive, s_mode.vactive);
s_mode.type |= MODE_ROTATED;
}
// Create a dummy mode entry if allowed
if (caps() & CUSTOM_VIDEO_CAPS_ADD && m_ds.modeline_generation)
@ -357,63 +395,66 @@ modeline *display_manager::get_mode(int width, int height, float refresh, bool i
mode.type & MODE_DISABLED?" - locked":"");
// now get the mode if allowed
if (!(mode.type & MODE_DISABLED))
if (mode.type & MODE_DISABLED)
continue;
for (int i = 0 ; i < MAX_RANGES ; i++)
{
for (int i = 0 ; i < MAX_RANGES ; i++)
if (range[i].hfreq_min == 0)
continue;
t_mode = mode;
// init all editable fields with source or user values
if (t_mode.type & X_RES_EDITABLE)
t_mode.hactive = m_user_mode.width? m_user_mode.width : s_mode.hactive;
if (t_mode.type & Y_RES_EDITABLE)
t_mode.vactive = m_user_mode.height? m_user_mode.height : s_mode.vactive;
if (t_mode.type & V_FREQ_EDITABLE)
{
if (range[i].hfreq_min)
{
t_mode = mode;
// If user's vfreq is defined, it means we have an user modeline, so force it
if (m_user_mode.vfreq)
modeline_copy_timings(&t_mode, &m_user_mode);
else
t_mode.vfreq = s_mode.vfreq;
}
// init all editable fields with source or user values
if (t_mode.type & X_RES_EDITABLE)
t_mode.hactive = m_user_mode.width? m_user_mode.width : s_mode.hactive;
// lock resolution fields if required
if (m_user_mode.width) t_mode.type &= ~X_RES_EDITABLE;
if (m_user_mode.height) t_mode.type &= ~Y_RES_EDITABLE;
if (m_user_mode.vfreq) t_mode.type &= ~V_FREQ_EDITABLE;
if (t_mode.type & Y_RES_EDITABLE)
t_mode.vactive = m_user_mode.height? m_user_mode.height : s_mode.vactive;
modeline_create(&s_mode, &t_mode, &range[i], &m_ds.gs);
t_mode.range = i;
if (t_mode.type & V_FREQ_EDITABLE)
{
// If user's vfreq is defined, it means we have an user modeline, so force it
if (m_user_mode.vfreq)
t_mode = m_user_mode;
else
t_mode.vfreq = s_mode.vfreq;
}
log_verbose("%s\n", modeline_result(&t_mode, result));
// lock resolution fields if required
if (m_user_mode.width) t_mode.type &= ~X_RES_EDITABLE;
if (m_user_mode.height) t_mode.type &= ~Y_RES_EDITABLE;
if (m_user_mode.vfreq) t_mode.type &= ~V_FREQ_EDITABLE;
modeline_create(&s_mode, &t_mode, &range[i], &m_ds.gs);
t_mode.range = i;
log_verbose("%s\n", modeline_result(&t_mode, result));
if (modeline_compare(&t_mode, &best_mode))
{
best_mode = t_mode;
m_best_mode = &mode;
}
}
if (modeline_compare(&t_mode, &best_mode))
{
best_mode = t_mode;
m_selected_mode = &mode;
}
}
}
// If we didn't need to create a new mode, remove our dummy entry
if (caps() & CUSTOM_VIDEO_CAPS_ADD && m_ds.modeline_generation && m_best_mode != &video_modes.back())
if (caps() & CUSTOM_VIDEO_CAPS_ADD && m_ds.modeline_generation && m_selected_mode != &video_modes.back())
video_modes.pop_back();
// If we didn't find a suitable mode, exit now
if (best_mode.result.weight & R_OUT_OF_RANGE)
{
m_best_mode = 0;
m_selected_mode = 0;
log_error("Switchres: could not find a video mode that meets your specs\n");
return nullptr;
}
log_verbose("\nSwitchres: %s (%dx%d@%.6f)->(%dx%d@%.6f)\n", rotation()?"rotated":"normal",
if ((best_mode.type & V_FREQ_EDITABLE) && !(best_mode.result.weight & R_OUT_OF_RANGE))
modeline_adjust(&best_mode, range[best_mode.range].hfreq_max, &m_ds.gs);
log_verbose("\nSwitchres: %s (%dx%d@%.6f)->(%dx%d@%.6f)\n", rotated?"rotated":"normal",
width, height, refresh, best_mode.hactive, best_mode.vactive, best_mode.vfreq);
log_verbose("%s\n", modeline_result(&best_mode, result));
@ -427,9 +468,9 @@ modeline *display_manager::get_mode(int width, int height, float refresh, bool i
best_mode.height = best_mode.vactive;
best_mode.refresh = int(best_mode.vfreq);
// lock new mode
best_mode.type &= ~(X_RES_EDITABLE | Y_RES_EDITABLE | (caps() & CUSTOM_VIDEO_CAPS_UPDATE? 0 : V_FREQ_EDITABLE));
best_mode.type &= ~(X_RES_EDITABLE | Y_RES_EDITABLE);
}
else if (modeline_is_different(&best_mode, m_best_mode) != 0)
else if (modeline_is_different(&best_mode, m_selected_mode) != 0)
best_mode.type |= MODE_UPDATE;
char modeline[256]={'\x00'};
@ -437,10 +478,14 @@ modeline *display_manager::get_mode(int width, int height, float refresh, bool i
}
// Check if new best mode is different than previous one
m_switching_required = (m_current_mode != m_best_mode || best_mode.type & MODE_UPDATE);
m_switching_required = (m_current_mode != m_selected_mode || best_mode.type & MODE_UPDATE);
*m_best_mode = best_mode;
return m_best_mode;
// Add id to mode
if (best_mode.id == 0)
best_mode.id = ++m_id_counter;
*m_selected_mode = best_mode;
return m_selected_mode;
}
//============================================================
@ -479,3 +524,24 @@ bool display_manager::auto_specs()
return true;
}
//============================================================
// display_manager::get_aspect
//============================================================
double display_manager::get_aspect(const char* aspect)
{
int num, den;
if (sscanf(aspect, "%d:%d", &num, &den) == 2)
{
if (den == 0)
{
log_error("Error: denominator can't be zero\n");
return STANDARD_CRT_ASPECT;
}
return (double(num)/double(den));
}
log_error("Error: use format --aspect <num:den>\n");
return STANDARD_CRT_ASPECT;
}

View File

@ -19,6 +19,10 @@
#include "modeline.h"
#include "custom_video.h"
// Mode flags
#define SR_MODE_INTERLACED 1<<0
#define SR_MODE_ROTATED 1<<1
typedef struct display_settings
{
char screen[32];
@ -52,20 +56,22 @@ public:
display_manager *make(display_settings *ds);
void parse_options();
virtual bool init();
virtual bool init(void* = nullptr);
virtual int caps();
// getters
int index() const { return m_index; }
custom_video *factory() const { return m_factory; }
custom_video *video() const { return m_video; }
bool has_ini() const { return m_has_ini; }
// getters (modes)
modeline user_mode() const { return m_user_mode; }
modeline *best_mode() const { return m_best_mode; }
modeline *selected_mode() const { return m_selected_mode; }
modeline *current_mode() const { return m_current_mode; }
int index() const { return m_index; }
bool desktop_is_rotated() const { return m_desktop_is_rotated; }
// getters (display manager)
const char *set_monitor() { return (const char*) &m_ds.monitor; }
const char *monitor() { return (const char*) &m_ds.monitor; }
const char *user_modeline() { return (const char*) &m_ds.user_modeline; }
const char *crt_range(int i) { return (const char*) &m_ds.crt_range[i]; }
const char *lcd_range() { return (const char*) &m_ds.lcd_range; }
@ -76,6 +82,7 @@ public:
bool lock_system_modes() { return m_ds.lock_system_modes; }
bool refresh_dont_care() { return m_ds.refresh_dont_care; }
bool keep_changes() { return m_ds.keep_changes; }
bool desktop_is_rotated() const { return m_desktop_is_rotated; }
// getters (modeline generator)
bool interlace() { return m_ds.gs.interlace; }
@ -83,47 +90,89 @@ public:
double dotclock_min() { return m_ds.gs.pclock_min; }
double refresh_tolerance() { return m_ds.gs.refresh_tolerance; }
int super_width() { return m_ds.gs.super_width; }
bool rotation() { return m_ds.gs.rotation; }
double monitor_aspect() { return m_ds.gs.monitor_aspect; }
double h_size() { return m_ds.gs.h_size; }
int h_shift() { return m_ds.gs.h_shift; }
int v_shift() { return m_ds.gs.v_shift; }
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; }
// getters (modeline result)
bool got_mode() { return (m_best_mode != nullptr); }
int width() { return m_best_mode != nullptr? m_best_mode->width : 0; }
int height() { return m_best_mode != nullptr? m_best_mode->height : 0; }
int refresh() { return m_best_mode != nullptr? m_best_mode->refresh : 0; }
double v_freq() { return m_best_mode != nullptr? m_best_mode->vfreq : 0; }
double h_freq() { return m_best_mode != nullptr? m_best_mode->hfreq : 0; }
int x_scale() { return m_best_mode != nullptr? m_best_mode->result.x_scale : 0; }
int y_scale() { return m_best_mode != nullptr? m_best_mode->result.y_scale : 0; }
int v_scale() { return m_best_mode != nullptr? m_best_mode->result.v_scale : 0; }
bool is_interlaced() { return m_best_mode != nullptr? m_best_mode->interlace : false; }
bool is_doublescanned() { return m_best_mode != nullptr? m_best_mode->doublescan : false; }
bool is_stretched() { return m_best_mode != nullptr? m_best_mode->result.weight & R_RES_STRETCH : false; }
bool is_refresh_off() { return m_best_mode != nullptr? m_best_mode->result.weight & R_V_FREQ_OFF : false; }
bool got_mode() { return (m_selected_mode != nullptr); }
int width() { return m_selected_mode != nullptr? m_selected_mode->width : 0; }
int height() { return m_selected_mode != nullptr? m_selected_mode->height : 0; }
int refresh() { return m_selected_mode != nullptr? m_selected_mode->refresh : 0; }
double v_freq() { return m_selected_mode != nullptr? m_selected_mode->vfreq : 0; }
double h_freq() { return m_selected_mode != nullptr? m_selected_mode->hfreq : 0; }
double x_scale() { return m_selected_mode != nullptr? m_selected_mode->result.x_scale : 0; }
double y_scale() { return m_selected_mode != nullptr? m_selected_mode->result.y_scale : 0; }
double v_scale() { return m_selected_mode != nullptr? m_selected_mode->result.v_scale : 0; }
bool is_interlaced() { return m_selected_mode != nullptr? m_selected_mode->interlace : false; }
bool is_doublescanned() { return m_selected_mode != nullptr? m_selected_mode->doublescan : false; }
bool is_stretched() { return m_selected_mode != nullptr? m_selected_mode->result.weight & R_RES_STRETCH : false; }
bool is_refresh_off() { return m_selected_mode != nullptr? m_selected_mode->result.weight & R_V_FREQ_OFF : false; }
bool is_switching_required() { return m_switching_required; }
bool is_mode_updated() { return m_best_mode != nullptr? m_best_mode->type & MODE_UPDATE : false; }
bool is_mode_new() { return m_best_mode != nullptr? m_best_mode->type & MODE_ADD : false; }
bool is_mode_updated() { return m_selected_mode != nullptr? m_selected_mode->type & MODE_UPDATE : false; }
bool is_mode_new() { return m_selected_mode != nullptr? m_selected_mode->type & MODE_ADD : false; }
// getters (custom_video backend)
bool screen_compositing() { return m_ds.vs.screen_compositing; }
bool screen_reordering() { return m_ds.vs.screen_reordering; }
bool allow_hardware_refresh() { return m_ds.vs.allow_hardware_refresh; }
const char *custom_timing() { return (const char*) &m_ds.vs.custom_timing; }
// setters
void set_index(int index) { m_index = index; }
void set_factory(custom_video *factory) { m_factory = factory; }
void set_custom_video(custom_video *video) { m_video = video; }
void set_user_mode(modeline *mode) { m_user_mode = *mode; filter_modes(); }
void set_has_ini(bool value) { m_has_ini = value; }
// setters (modes)
void set_user_mode(modeline *mode) { m_ds.user_mode = m_user_mode = *mode; filter_modes(); }
void set_selected_mode(modeline *mode) { m_selected_mode = mode; }
void set_current_mode(modeline *mode) { m_current_mode = mode; }
void set_index(int index) { m_index = index; }
// setters (display_manager)
void set_monitor(const char *preset) { 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); }
void set_screen(const char *screen) { strncpy(m_ds.screen, screen, sizeof(m_ds.screen)-1); }
void set_api(const char *api) { strncpy(m_ds.api, api, sizeof(m_ds.api)-1); }
void set_modeline_generation(bool value) { m_ds.modeline_generation = value; }
void set_lock_unsupported_modes(bool value) { m_ds.lock_unsupported_modes = value; }
void set_lock_system_modes(bool value) { m_ds.lock_system_modes = value; }
void set_refresh_dont_care(bool value) { m_ds.refresh_dont_care = value; }
void set_keep_changes(bool value) { m_ds.keep_changes = value; }
void set_desktop_is_rotated(bool value) { m_desktop_is_rotated = value; }
void set_rotation(bool value) { m_ds.gs.rotation = value; }
void set_monitor_aspect(float aspect) { m_ds.gs.monitor_aspect = aspect; }
// setters (modeline generator)
void set_interlace(bool value) { m_ds.gs.interlace = value; }
void set_doublescan(bool value) { m_ds.gs.doublescan = value; }
void set_dotclock_min(double value) { m_ds.gs.pclock_min = value * 1000000; }
void set_refresh_tolerance(double value) { m_ds.gs.refresh_tolerance = value; }
void set_super_width(int value) { m_ds.gs.super_width = value; }
void set_monitor_aspect(double value) { m_ds.gs.monitor_aspect = value; }
void set_monitor_aspect(const char* aspect) { set_monitor_aspect(get_aspect(aspect)); }
void set_h_size(double value) { m_ds.gs.h_size = value; }
void set_h_shift(int value) { m_ds.gs.h_shift = value; }
void set_v_shift(int value) { m_ds.gs.v_shift = value; }
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; }
// setters (custom_video backend)
void set_screen_compositing(bool value) { m_ds.vs.screen_compositing = value; }
void set_screen_reordering(bool value) { m_ds.vs.screen_reordering = value; }
void set_allow_hardware_refresh(bool value) { m_ds.vs.allow_hardware_refresh = value; }
void set_custom_timing(const char *custom_timing) { strncpy(m_ds.vs.custom_timing, custom_timing, sizeof(m_ds.vs.custom_timing)-1); }
// options
display_settings m_ds = {};
// mode setting interface
modeline *get_mode(int width, int height, float refresh, bool interlaced);
modeline *get_mode(int width, int height, float refresh, int flags);
bool add_mode(modeline *mode);
bool delete_mode(modeline *mode);
bool update_mode(modeline *mode);
@ -151,12 +200,26 @@ private:
custom_video *m_video = 0;
modeline m_user_mode = {};
modeline *m_best_mode = 0;
modeline *m_selected_mode = 0;
modeline *m_current_mode = 0;
int m_index = 0;
bool m_desktop_is_rotated = 0;
bool m_switching_required = 0;
bool m_has_ini = 0;
int m_id_counter = 0;
void set_preset(const char *preset);
double get_aspect(const char* aspect);
protected:
void* m_pf_data = nullptr;
};
class dummy_display : public display_manager
{
public:
dummy_display(display_settings *ds) { m_ds = *ds; };
};
#endif

View File

@ -42,8 +42,9 @@ linux_display::~linux_display()
// linux_display::init
//============================================================
bool linux_display::init()
bool linux_display::init(void* pfdata)
{
m_pf_data = pfdata;
// Initialize custom video
int method = CUSTOM_VIDEO_TIMING_AUTO;
@ -146,7 +147,7 @@ int linux_display::get_available_video_modes()
// get next mode
video()->get_timing(&mode);
if (mode.type == 0 || mode.platform_data == 0)
if (mode.type == 0)
break;
// set the desktop mode
@ -155,6 +156,8 @@ int linux_display::get_available_video_modes()
memcpy(&desktop_mode, &mode, sizeof(modeline));
if (current_mode() == nullptr)
set_current_mode(&mode);
if (mode.type & MODE_ROTATED) set_desktop_is_rotated(true);
}
video_modes.push_back(mode);

View File

@ -19,7 +19,7 @@ class linux_display : public display_manager
public:
linux_display(display_settings *ds);
~linux_display();
bool init();
bool init(void* = nullptr);
bool set_mode(modeline *mode);
private:

304
deps/switchres/display_sdl2.cpp vendored Normal file
View File

@ -0,0 +1,304 @@
/**************************************************************
display_linux.cpp - Display manager for Linux
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#include <stdio.h>
#include <string.h>
#include <SDL.h>
#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
//============================================================
sdl2_display::sdl2_display(display_settings *ds)
{
// First, we need to fin an active SDL2 window
if (SDL_WasInit(SDL_INIT_VIDEO) != 0) {
log_verbose("Switchres/SDL2: (%s): SDL2 video is initialized\n", __FUNCTION__);
}
else
{
log_verbose("Switchres/SDL2: (%s): SDL2 video wasn't initialized\n", __FUNCTION__);
throw std::exception();
}
// For now, only allow the SDL2 display manager for the KMSDRM backend
if ( strcmp("KMSDRM", SDL_GetCurrentVideoDriver()) != 0 )
{
log_info("Switchres/SDL2: (%s): SDL2 is only available for KMSDRM for now.\n", __FUNCTION__);
throw std::exception();
}
// Get display settings
m_ds = *ds;
}
//============================================================
// sdl2_display::~sdl2_display
//============================================================
sdl2_display::~sdl2_display()
{
}
//============================================================
// sdl2_display::init
//============================================================
bool sdl2_display::init(void* pf_data)
{
m_sdlwindow = (SDL_Window*) pf_data;
// Initialize custom video
int method = CUSTOM_VIDEO_TIMING_AUTO;
#ifdef SR_WITH_XRANDR
if (!strcmp(m_ds.api, "xrandr"))
method = CUSTOM_VIDEO_TIMING_XRANDR;
#endif
#ifdef SR_WITH_KMSDRM
if (!strcmp(m_ds.api, "drmkms"))
method = CUSTOM_VIDEO_TIMING_DRMKMS;
#endif
set_factory(new custom_video);
set_custom_video(factory()->make(m_ds.screen, NULL, method, &m_ds.vs));
if (!video() or !video()->init())
return false;
// Build our display's mode list
video_modes.clear();
backup_modes.clear();
//No need to call get_desktop_mode() SDL2 will restore the desktop mode itself
get_available_video_modes();
if (!strcmp(m_ds.monitor, "lcd")) auto_specs();
filter_modes();
//SDL_LogSetAllPriority(SDL_LOG_PRIORITY_DEBUG);
SDL_Window* window = NULL;
Uint32 id = 0;
/*
// Alternative method, only when the render has been created
if( SDL_GL_GetCurrentWindow() != NULL)
log_verbose("Swithres/SDL2: (%s) SDL_GL_GetCurrentWindow(); OK !\n", __FUNCTION__);
*/
if (pf_data == nullptr or pf_data == NULL)
{
int screen = atoi(m_ds.screen);
while( (window = SDL_GetWindowFromID(++id)) )
{
log_verbose("Switchres/SDL2: (%s:%d) SDL display id vs SR display id: %d vs %d (window id: %d)\n", __FUNCTION__, __LINE__, SDL_GetWindowDisplayIndex(window), screen, id);
if (SDL_GetWindowDisplayIndex(window) == screen)
{
log_verbose("Switchres/SDL2: (%s:%d) Found a display-matching SDL window\n ", __FUNCTION__, __LINE__);
m_sdlwindow = window;
return true;
}
}
log_verbose("Switchres/SDL2: (%s:%d) No SDL window matching the display found\n ", __FUNCTION__, __LINE__);
}
else
{
id = SDL_GetWindowID((SDL_Window*)pf_data);
if( id )
{
log_verbose("Switchres/SDL2: (%s:%d) got a valid SDL_Window pointer (window id: %d)\n", __FUNCTION__, __LINE__, id);
m_sdlwindow = (SDL_Window*)pf_data;
}
else
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;
}
//============================================================
// sdl2_display::set_mode
//============================================================
bool sdl2_display::set_mode(modeline *mode)
{
// Call SDL2
SDL_DisplayMode target, closest;
target.w = mode->width;
target.h = mode->height;
target.format = 0; // don't care
target.refresh_rate = mode->refresh;
/*
* Circumventing an annoying choice of SDL2: fullscreen modes are mutually exclusive
* which means you can't switch from one to another. If required, need to remove the flag, then
* set the new one. This will sadly trigger a window resizing
*/
if ( (SDL_GetWindowFlags(m_sdlwindow) & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP)
{
if ( SDL_SetWindowFullscreen(m_sdlwindow, 0) != 0 )
{
log_error("Swithres/SDL2: (%s) Couldn't reset the fullscreen mode. %s\n", __FUNCTION__, SDL_GetError());
return false;
}
}
if ( (SDL_GetWindowFlags(m_sdlwindow) & SDL_WINDOW_FULLSCREEN) != SDL_WINDOW_FULLSCREEN )
{
// Now we set the right mode that allows SDL2 modeswitches
if ( SDL_SetWindowFullscreen(m_sdlwindow, SDL_WINDOW_FULLSCREEN) != 0 )
{
log_error("Swithres/SDL2: (%s) Couldn't set the window to FULLSCREEN. %s\n", __FUNCTION__, SDL_GetError());
return false;
}
}
// We may first check if the mode was already added, so we don't force a probe of all modes
if ( SDL_GetClosestDisplayMode(SDL_GetWindowDisplayIndex(m_sdlwindow), &target, &closest) == NULL )
{
// If the returned pointer is null, no match was found.
log_error("Swithres/SDL2: (%s) No suitable display mode was found! %s\n\n", __FUNCTION__, SDL_GetError());
return false;
}
log_verbose(" Received: \t%dx%dpx @ %dhz \n", closest.w, closest.h, closest.refresh_rate);
if ( SDL_SetWindowDisplayMode(m_sdlwindow, &closest) != 0 )
{
log_error("Swithres/SDL2: (%s) Failed to switch mode: %s\n", __FUNCTION__, SDL_GetError());
return false;
}
log_verbose("Swithres/SDL2: (%s) SDL2 display mode changed for window/display %d/%d!\n", __FUNCTION__, SDL_GetWindowID(m_sdlwindow), SDL_GetWindowDisplayIndex(m_sdlwindow));
log_verbose(" to %dx%d@%d\n",closest.w, closest.h, closest.refresh_rate);
set_current_mode(mode);
return true;
}
//============================================================
// sdl2_display::get_desktop_mode
//============================================================
bool sdl2_display::get_desktop_mode()
{
if (video() == NULL)
return false;
return true;
}
//============================================================
// sdl2_display::get_available_video_modes
//============================================================
int sdl2_display::get_available_video_modes()
{
if (video() == NULL)
return false;
// loop through all modes until NULL mode type is received
for (;;)
{
modeline mode;
memset(&mode, 0, sizeof(struct modeline));
// get next mode
video()->get_timing(&mode);
if (mode.type == 0)
break;
// set the desktop mode
if (mode.type & MODE_DESKTOP)
{
memcpy(&desktop_mode, &mode, sizeof(modeline));
if (current_mode() == nullptr)
set_current_mode(&mode);
if (mode.type & MODE_ROTATED) set_desktop_is_rotated(true);
}
video_modes.push_back(mode);
backup_modes.push_back(mode);
log_verbose("Switchres/SDL2: [%3ld] %4dx%4d @%3d%s%s %s: ", video_modes.size(), mode.width, mode.height, mode.refresh, mode.interlace ? "i" : "p", mode.type & MODE_DESKTOP ? "*" : "", mode.type & MODE_ROTATED ? "rot" : "");
log_mode(&mode);
};
return true;
}

32
deps/switchres/display_sdl2.h vendored Normal file
View File

@ -0,0 +1,32 @@
/**************************************************************
display_linux.h - Display manager for Linux
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#include "display.h"
#include "SDL.h"
#include "SDL_syswm.h"
class sdl2_display : public display_manager
{
public:
sdl2_display(display_settings *ds);
~sdl2_display();
bool init(void* pf_data);
bool set_mode(modeline *mode);
private:
SDL_Window* m_sdlwindow = NULL;
bool get_desktop_mode();
int get_available_video_modes();
};

View File

@ -57,7 +57,7 @@ int CALLBACK monitor_by_index(HMONITOR h_monitor, HDC, LPRECT, LPARAM data)
return true;
}
bool windows_display::init()
bool windows_display::init(void*)
{
char display[32] = {};

View File

@ -29,7 +29,7 @@ class windows_display : public display_manager
public:
windows_display(display_settings *ds);
~windows_display();
bool init();
bool init(void* = nullptr);
bool set_mode(modeline *mode);
private:

211
deps/switchres/drm_hook.cpp vendored Normal file
View File

@ -0,0 +1,211 @@
/**************************************************************
drm_hook.cpp - Linux DRM/KMS library hook
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2022 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#include <stdio.h>
#include <stdint.h>
#include <dlfcn.h>
#include <cstring>
// DRM headers
#include <xf86drm.h>
#include <xf86drmMode.h>
#define MAX_CONNECTORS 10
bool hook_connector(drmModeConnectorPtr conn);
drmModeConnectorPtr get_connector(uint32_t connector_id);
typedef struct connector_hook
{
drmModeConnector conn;
drmModeModeInfo modes[128];
uint32_t props[128];
uint64_t prop_values[128];
uint32_t encoders[128];
} connector_hook;
connector_hook connector[MAX_CONNECTORS];
int m_num_connectors = 0;
//============================================================
// drmModeGetConnector
//============================================================
drmModeConnectorPtr drmModeGetConnector(int fd, uint32_t connector_id)
{
static void* (*my_get_connector)(int, uint32_t) = NULL;
if (!my_get_connector)
my_get_connector = (void*(*)(int, uint32_t))dlsym(RTLD_NEXT, "drmModeGetConnector");
// Allow hook detection (original func would return NULL)
if (fd == -1)
return &connector[0].conn;
drmModeConnectorPtr conn = get_connector(connector_id);
if (conn != NULL)
// already hooked
return conn;
else
{
// attempt connector hook
conn = (drmModeConnectorPtr)my_get_connector(fd, connector_id);
if (!conn) return NULL;
if (hook_connector(conn))
{
conn = get_connector(connector_id);
printf("Switchres: returning hooked connector %d\n", conn->connector_id);
}
}
return conn;
}
//============================================================
// drmModeGetConnectorCurrent
//============================================================
drmModeConnectorPtr drmModeGetConnectorCurrent(int fd, uint32_t connector_id)
{
static void* (*my_get_connector_current)(int, uint32_t) = NULL;
if (!my_get_connector_current)
my_get_connector_current = (void*(*)(int, uint32_t))dlsym(RTLD_NEXT, "drmModeGetConnectorCurrent");
// Allow hook detection (original func would return NULL)
if (fd == -1)
return &connector[0].conn;
drmModeConnectorPtr conn = get_connector(connector_id);
if (conn != NULL)
// already hooked
return conn;
else
{
// attempt connector hook
conn = (drmModeConnectorPtr)my_get_connector_current(fd, connector_id);
if (!conn) return NULL;
if (hook_connector(conn))
{
conn = get_connector(connector_id);
printf("Switchres: returning hooked connector %d\n", conn->connector_id);
}
}
return conn;
}
//============================================================
// drmModeFreeConnector
//============================================================
void drmModeFreeConnector(drmModeConnectorPtr ptr)
{
static void (*my_free_connector)(drmModeConnectorPtr) = NULL;
if (!my_free_connector)
my_free_connector = (void (*)(drmModeConnectorPtr)) dlsym(RTLD_NEXT, "drmModeFreeConnector");
// Skip our hooked connector
for (int i = 0; i < m_num_connectors; i++)
if (ptr == &connector[i].conn)
return;
my_free_connector(ptr);
}
//============================================================
// hook_connector
//============================================================
bool hook_connector(drmModeConnectorPtr conn)
{
if (conn == NULL)
return false;
if (m_num_connectors >= MAX_CONNECTORS)
return false;
if (conn->count_modes == 0)
return false;
connector_hook *conn_hook = &connector[m_num_connectors++];
drmModeConnectorPtr my_conn = &conn_hook->conn;
printf("Switchres: hooking connector %d\n", conn->connector_id);
*my_conn = *conn;
drmModeModeInfo *my_modes = conn_hook->modes;
memcpy(my_modes, conn->modes, sizeof(drmModeModeInfo) * conn->count_modes);
my_conn->modes = my_modes;
uint32_t *my_encoders = conn_hook->encoders;
memcpy(my_encoders, conn->encoders, sizeof(uint32_t) * conn->count_encoders);
my_conn->encoders = my_encoders;
uint32_t *my_props = conn_hook->props;
memcpy(my_props, conn->props, sizeof(uint32_t) * conn->count_props);
my_conn->props = my_props;
uint64_t *my_prop_values = conn_hook->prop_values;
memcpy(my_prop_values, conn->prop_values, sizeof(uint64_t) * conn->count_props);
my_conn->prop_values = my_prop_values;
drmModeFreeConnector(conn);
bool found = false;
drmModeModeInfo *mode = NULL;
for (int i = 0; i < my_conn->count_modes; i++)
{
mode = &my_modes[i];
if (mode->type & DRM_MODE_TYPE_PREFERRED)
{
found = true;
break;
}
}
// If preferred mode not found, default to first entry
if (!found)
mode = &my_modes[0];
// Add dummy mode to mode list (preferred mode with hdisplay +1, vfresh +1)
drmModeModeInfo *dummy_mode = &my_modes[my_conn->count_modes];
*dummy_mode = *mode;
dummy_mode->vrefresh++;
dummy_mode->hdisplay++;
dummy_mode->type |= (1<<7);
my_conn->count_modes++;
return true;
}
//============================================================
// get_connector
//============================================================
drmModeConnectorPtr get_connector(uint32_t connector_id)
{
for (int i = 0; i < m_num_connectors; i++)
if (connector[i].conn.connector_id == connector_id)
return &connector[i].conn;
return NULL;
}

View File

@ -22,7 +22,7 @@
// edid_from_modeline
//============================================================
int edid_from_modeline(modeline *mode, monitor_range *range, char *name, edid_block *edid)
int edid_from_modeline(modeline *mode, monitor_range *range, const char *name, edid_block *edid)
{
if (!edid) return 0;

View File

@ -32,6 +32,6 @@ typedef struct edid_block
// PROTOTYPES
//============================================================
int edid_from_modeline(modeline *mode, monitor_range *range, char *name, edid_block *edid);
int edid_from_modeline(modeline *mode, monitor_range *range, const char *name, edid_block *edid);
#endif

56
deps/switchres/examples/README.md vendored Normal file
View File

@ -0,0 +1,56 @@
# ANY OS - BASIC INFORMATION
## Build libswitchres
It supports cross compilation, and will build both dynamic and static libs as per the target OS
```bash
make libswitchres
```
## Basic usage as a client with examples
libswitchres can be called in 2 different ways (with example code):
* `test_dlopen.c` -> by explicitely opening a .so/.dll, import the srlib object and call associated functions
* `test_liblink.c` -> by simply linking libswitchres at build time
These options are generic whether you build for Linux or Windows
* -I ../ (to get libswitchres_wrapper.h)
* -L ../ or -L ./ (for win32, when the dll has been copied in the examples folder)
* -lswitchres to link the lib if not manually opening it in the code
#please note#: static libs aven't been tested yet
# LINUX
You'll need a few extra parameters for gcc:
* -ldl (will try later to find a way to statically link libdl.a)
When running, dont forget to add before the binary LD_LIBRARY_PATH=<libswitchres.so pass, even if it's ./>:$LD_LIBRARY_PATH
## Examples:
```bash
make libswitchres
cd examples
g++ -o linux_dl_test test_dlopen.cpp -I ../ -ldl
LD_LIBRARY_PATH=../:$LD_LIBRARY_PATH ./linux_dl_test
g++ -o linux_link_lib test_liblink.cpp -I ../ -L../ -lswitchres -ldl
LD_LIBRARY_PATH=../:$LD_LIBRARY_PATH ./linux_link_lib
```
# WINDOWS
Pretty much the same as Linux, but with mingw64. The resulting exe and dll can be tested with wine
## Examples (cross-building from windows)
```
make PLATFORM=NT CROSS_COMPILE=x86_64-w64-mingw32- libswitchres
(copy the dll to examples)
x86_64-w64-mingw32-g++-win32 test_dlopen.cpp -o w32_loaddll.exe -I ../ -static-libgcc -static-libstdc++
w32_loaddll.exe
x86_64-w64-mingw32-g++-win32 test_liblink.cpp -o w32_linkdll.exe -I ../ -static-libgcc -static-libstdc++ -L ./ -lswitchres
w32_linkdll.exe
```
Note that, when building w32_linkdll.exe, I couldn't point to another dir else than ./ with -L

View File

@ -0,0 +1,31 @@
#include <stdio.h>
#include <stdlib.h>
#include <switchres/switchres_wrapper.h>
int main(int argc, char** argv)
{
sr_mode srm;
int ret;
sr_init();
sr_set_log_level(3);
sr_set_disp(-1);
sr_set_monitor("arcade_31");
sr_init_disp("0", NULL);
sr_set_disp(-1);
sr_set_monitor("pc_31_120");
sr_init_disp("1", NULL);
sr_set_disp(0);
ret = sr_switch_to_mode(640, 480, 57, 0, &srm);
sr_set_disp(1);
ret = sr_switch_to_mode(320, 240, 58, 0, &srm);
printf("Press any key to quit.\n");
getchar();
sr_deinit();
}

84
deps/switchres/examples/test_dlopen.cpp vendored Normal file
View File

@ -0,0 +1,84 @@
#include <stdio.h>
#include <stdlib.h>
#ifdef __cplusplus
#include <cstring> // required for strcpy
#endif
#ifdef __linux__
#define LIBSWR "libswitchres.so"
#elif _WIN32
#define LIBSWR "libswitchres.dll"
#endif
#include <switchres/switchres_wrapper.h>
int main(int argc, char** argv)
{
const char* err_msg;
printf("About to open %s.\n", LIBSWR);
// Load the lib
LIBTYPE dlp = OPENLIB(LIBSWR);
// Loading failed, inform and exit
if (!dlp)
{
printf("Loading %s failed.\n", LIBSWR);
printf("Error: %s\n", LIBERROR());
exit(EXIT_FAILURE);
}
printf("Loading %s succeded.\n", LIBSWR);
// Load the init()
LIBERROR();
srAPI* SRobj = (srAPI*) LIBFUNC(dlp, "srlib");
if ((err_msg = LIBERROR()) != NULL)
{
printf("Failed to load srAPI: %s\n", err_msg);
CLOSELIB(dlp);
exit(EXIT_FAILURE);
}
// Testing the function
printf("Init a new switchres_manager object:\n");
SRobj->init();
SRobj->init_disp(NULL, NULL);
// Call mode + get result values
int w = 384, h = 224;
double rr = 59.583393;
int interlace = 0, ret;
sr_mode srm;
printf("Original requested mode: %dx%d@%f%s\n", w, h, rr, interlace? "i":"");
ret = SRobj->add_mode(w, h, rr, interlace, &srm);
if (!ret)
{
printf("ERROR: Couldn't add the required mode. Exiting!\n");
SRobj->deinit();
exit(1);
}
printf("Got mode: %dx%d%c@%f\n", srm.width, srm.height, srm.interlace, srm.refresh);
printf("Press any key to switch to new mode\n");
getchar();
ret = SRobj->switch_to_mode(srm.width, srm.height, rr, srm.interlace, &srm);
if (!ret)
{
printf("ERROR: Couldn't switch to the required mode. Exiting!\n");
SRobj->deinit();
exit(1);
}
printf("Press any key to quit.\n");
getchar();
// Clean the mess, kiss goodnight SR
SRobj->deinit();
// We're done, let's close
CLOSELIB(dlp);
}

View File

@ -0,0 +1,36 @@
#include <stdio.h>
#include <stdlib.h>
#include <switchres/switchres_wrapper.h>
int main(int argc, char** argv)
{
sr_mode srm;
int ret;
sr_init();
sr_init_disp(NULL, NULL);
ret = sr_add_mode(384, 224, 59.63, 0, &srm);
if (!ret)
{
printf("ERROR: Couldn't add the required mode. Exiting!\n");
sr_deinit();
exit(1);
}
printf("SR returned resolution: %dx%d@%f%s\n", srm.width, srm.height, srm.refresh, srm.interlace? "i" : "");
printf("Press any key to switch to new mode\n");
getchar();
ret = sr_switch_to_mode(384, 224, 59.63, 0, &srm);
if (!ret)
{
printf("ERROR: Couldn't switch to the required mode. Exiting!\n");
sr_deinit();
exit(1);
}
printf("Press any key to quit.\n");
getchar();
sr_deinit();
}

3776
deps/switchres/font.h vendored Normal file

File diff suppressed because it is too large Load Diff

324
deps/switchres/geometry.py vendored Normal file
View File

@ -0,0 +1,324 @@
import argparse
import subprocess
import sys
import time
import os
import logging
import platform
CTR_modifier = 1<<7
class mode:
def __init__(self, w:int = 640, h:int = 480, rr:float = 60):
self.width = int(w)
self.height = int(h)
self.refresh_rate = float(rr)
def __str__(self):
return "{}x{}@{}".format(self.width, self.height, self.refresh_rate)
def __repr__(self):
return "Mode: {}".format(str(self))
class geometry:
def __init__(self, h_size:float = 1.0, h_shift:int = 0, v_shift:int = 0):
self.h_size = float(h_size)
self.h_shift = int(h_shift)
self.v_shift = int(v_shift)
def __str__(self):
return "{}:{}:{}".format(self.h_size, self.h_shift, self.v_shift)
def __eq__(self, other):
return self.h_size == other.h_size and self.h_shift == other.h_shift and self.v_shift == other.v_shift
def set_geometry(self, h_size:float, h_shift:int, v_shift:int):
self.h_size = float(h_size)
self.h_shift = int(h_shift)
self.v_shift = int(v_shift)
@classmethod
def set_from_string(cls, geom:str):
"""
geom should be in the form of 1.0:-5:2
"""
hsize, hshift, vshift = geom.split(':')
return cls(hsize, hshift, vshift)
def inc_hsize(self, step = 0.01, factor:int = 1): self.h_size += step * factor
def inc_hshift(self, step = 1, factor:int = 1): self.h_shift += step * factor
def inc_vshift(self, step = 1, factor:int = 1): self.v_shift += step * factor
def dec_hsize(self, step = 0.01, factor:int = 1): self.h_size -= step * factor
def dec_hshift(self, step = 1, factor:int = 1): self.h_shift -= step * factor
def dec_vshift(self, step = 1, factor:int = 1): self.v_shift -= step * factor
class crt_range:
def __init__(self, HfreqMin:float = 0.0, HfreqMax:float = 0.0, VfreqMin:float = 0.0, VfreqMax:float = 0.0, HFrontPorch:float = 0.0, HSyncPulse:float = 0.0, HBackPorch:float = 0.0, VFrontPorch:float = 0.0, VSyncPulse:float = 0.0, VBackPorch:float = 0.0, HSyncPol:int = 0, VSyncPol:int = 0, ProgressiveLinesMin:int = 0, ProgressiveLinesMax:int = 0, InterlacedLinesMin:int = 0, InterlacedLinesMax:int = 0):
self.HfreqMin = float(HfreqMin)
self.HfreqMax = float(HfreqMax)
self.VfreqMin = float(VfreqMin)
self.VfreqMax = float(VfreqMax)
self.HFrontPorch = float(HFrontPorch)
self.HSyncPulse = float(HSyncPulse)
self.HBackPorch = float(HBackPorch)
self.VFrontPorch = float(VFrontPorch)
self.VSyncPulse = float(VSyncPulse)
self.VBackPorch = float(VBackPorch)
self.HSyncPol = int(HSyncPol)
self.VSyncPol = int(VSyncPol)
self.ProgressiveLinesMin = int(ProgressiveLinesMin)
self.ProgressiveLinesMax = int(ProgressiveLinesMax)
self.InterlacedLinesMin = int(InterlacedLinesMin)
self.InterlacedLinesMax = int(InterlacedLinesMax)
@classmethod
def set_from_string(cls, range:str):
"""
range is in the form of 15625.00-16200.00,49.50-65.00,2.000,4.700,8.000,0.064,0.192,1.024,0,0,192,288,448,576
"""
HfreqRange, VfregRange, HFrontPorch, HSyncPulse, HBackPorch, VFrontPorch, VSyncPulse, VBackPorch, HSyncPol, VSyncPol, ProgressiveLinesMin, ProgressiveLinesMax, InterlacedLinesMin, InterlacedLinesMax = range.split(',')
HfreqMin, HfreqMax = HfreqRange.split('-')
VfreqMin, VfreqMax = VfregRange.split('-')
return cls(HfreqMin, HfreqMax, VfreqMin, VfreqMax, HFrontPorch, HSyncPulse, HBackPorch, VFrontPorch, VSyncPulse, VBackPorch, HSyncPol, VSyncPol, ProgressiveLinesMin, ProgressiveLinesMax, InterlacedLinesMin, InterlacedLinesMax)
def new_geometry_from_string(self, adjusted_geometry: str):
"""
range is in the shape of the switchres output: "H: 2.004, 4.696, 8.015 V: 0.447, 0.383, 2.425"
"""
#hfp, self.HSyncPulse, self.HBackPorch, vfp, self.VSyncPulse, self.VBackPorch = adjusted_geometry.split(', ')
hfp, self.HSyncPulse, hbp_and_vfp, self.VSyncPulse, self.VBackPorch = adjusted_geometry.split(', ')
self.HFrontPorch = hfp[3:]
self.HBackPorch, _, self.VFrontPorch = hbp_and_vfp.split(' ')
def __str__(self):
return "{}-{},{}-{},{},{},{},{},{},{},{},{},{},{},{},{}".format(
self.HfreqMin, self.HfreqMax, self.VfreqMin, self.VfreqMax, self.HFrontPorch, self.HSyncPulse, self.HBackPorch, self.VFrontPorch, self.VSyncPulse, self.VBackPorch, self.HSyncPol, self.VSyncPol, self.ProgressiveLinesMin, self.ProgressiveLinesMax, self.InterlacedLinesMin, self.InterlacedLinesMax)
def switchres_output_get_monitor_range(output:str):
for l in output.splitlines():
# The line to parse looks like:
# Switchres: Monitor range 15625.00-16200.00,49.50-65.00,2.000,4.700,8.000,0.064,0.192,1.024,0,0,192,288,448,576
if l[0:25] != "Switchres: Monitor range " : continue
logging.debug("Found! -> {}".format(l[25:]))
logging.debug(crt_range().set_from_string(l[25:]))
return crt_range().set_from_string(l[25:])
logging.warning("Couldn't find the monitor range!")
return None
def switchres_output_get_adjusted_crt_geometry(output:str):
for l in output.splitlines():
# The line to parse looks like:
# Adjusted geometry (1.000:0:0) H: 2.004, 4.696, 8.015 V: 0.223, 0.191, 1.212
# We need what is behind H:
if l[0:19] != "Adjusted geometry (" : continue
Hpos = l.find('H: ')
logging.debug("Found! -> {}".format(l[Hpos:]))
return l[Hpos:]
logging.warning("Couldn't find the adjusted crt geometry!")
return None
def switchres_output_get_adjusted_geometry(output:str):
for l in output.splitlines():
# The line to parse looks like:
# Adjusted geometry (1.000:0:0) H: 2.004, 4.696, 8.015 V: 0.223, 0.191, 1.212
# We need what is between parenthesis
if l[0:19] != "Adjusted geometry (" : continue
Hpos = l.find('H: ')
logging.debug("Found! -> {}".format(l[Hpos:]))
return l[19:Hpos - 2]
logging.warning("Couldn't find the adjusted geometry!")
return None
def switchres_output_get_command_exit_code(output:str):
for l in output.splitlines():
# The line to parse looks like:
# Process exited with value 256
if l[0:26] != "Process exited with value " : continue
logging.debug("Found! -> {}".format(l[26:]))
return int(l[26:])
logging.warning("Couldn't find the app exit code!")
return None
def launch_switchres(mode:mode, geom:geometry = geometry(), switchres_command:str = "switchres", launch_command:str = "grid", display:int = 0):
return_list = dict()
# The command line may not require launching a program, just to get the crt_range for example
cmd = [ switchres_command.split(" ")[0], str(mode.width), str(mode.height), str(mode.refresh_rate), '-v' ]
if switchres_command.split(" ")[1:]:
cmd.extend(switchres_command.split(" ")[1:])
if display > 0:
cmd.extend(['-d', str(display)])
if launch_command:
if display > 0:
launch_command += " {}".format(display)
cmd.extend(['-s', '-l', launch_command])
else:
cmd.extend(['-c'])
cmd.extend(['-g', str(geom)])
logging.debug("Calling: {} with text: {}".format(" ".join(cmd), os.getenv('GRID_TEXT')))
return_status = subprocess.run(cmd, capture_output=True, text=True)
logging.debug(return_status.stdout)
default_crt_range = switchres_output_get_monitor_range(return_status.stdout)
adjusted_geometry = switchres_output_get_adjusted_crt_geometry(return_status.stdout)
user_crt_range = default_crt_range
grid_return = None
if launch_command:
grid_return = switchres_output_get_command_exit_code(return_status.stdout)
user_crt_range.new_geometry_from_string(adjusted_geometry)
return_list['exit_code'] = grid_return
return_list['new_crt_range'] = user_crt_range
return_list['default_crt_range'] = default_crt_range
return_list['geometry'] = switchres_output_get_adjusted_geometry(return_status.stdout)
return return_list
def update_switchres_ini(range: crt_range, inifile:str = ''):
if not inifile:
sys.exit(0)
logging.info("Updating {} with crt_range {} (NOT YET IMPLEMENTED)".format(inifile, str(range)))
def readjust_geometry(geom: geometry, range:crt_range, return_code:int):
wanted_factor = 10 if return_code & CTR_modifier else 1
factor_message = " with CTRL pressed" if return_code & CTR_modifier else ''
# Disable the modifier
return_code = return_code & ~CTR_modifier
# This syntax requires python >= 3.10
match return_code:
# Pressed PAGEUP
case 68:
geom.inc_hsize(factor = wanted_factor)
# Pressed PAGEDOWN
case 69:
geom.dec_hsize(factor = wanted_factor)
# Pressed LEFT
case 64:
geom.dec_hshift(factor = wanted_factor)
# Pressed RIGHT
case 65:
geom.inc_hshift(factor = wanted_factor)
# Pressed DOWN
case 67:
geom.inc_vshift(factor = wanted_factor)
# Pressed UP
case 66:
geom.dec_vshift(factor = wanted_factor)
# Pressed ESCAPE / Q
case 1:
logging.info("Aborted!")
sys.exit(1)
# Pressed ENTER / RETURN
case 0:
logging.info("Finished!")
logging.info("Final geometry: {}".format(str(geom)))
logging.info("Final crt_range: {}".format(str(range)))
#update_switchres_ini(range, switchres_ini)
sys.exit(0)
# Pressed DEL / BACKSPACE
case 2:
geom = geometry(1.0, 0, 0)
# Pressed R
case 3:
logging.debug("Refreshing with the same geometry values if your screen was scrolling")
logging.debug("Readjusted geometry: {}".format(str(geom)))
return geom
def set_grid_text(top_txt:str = '', bottom_txt:str = '', geom:geometry = geometry()):
help_txt = """H size: {}
H shift: {}
V shift: {}
Arrows: shift screen - Page Up/Down: H size
ENTER: validate - ESC: cancel - DEL: reinit - R: reload
CTRL+key: step x10""".format(geom.h_size, geom.h_shift, geom.v_shift)
os.environ['GRID_TEXT'] = "\n \n{}".format("\n \n".join(filter(None, [top_txt, help_txt, bottom_txt])))
logging.debug(os.getenv('GRID_TEXT'))
def switchres_geometry_loop(mode: mode, switchres_command:str = "switchres", launch_command:str = "grid", display_nr:int = 0, geom:geometry = geometry()):
working_geometry = geom
top_txt = ''
while True:
# This launch is to confirm the geometry
sr_launch_return = launch_switchres(mode, working_geometry, switchres_command, launch_command = "", display = display_nr)
ret_geom = geometry.set_from_string(sr_launch_return['geometry'])
if ret_geom != working_geometry:
top_txt = "Geometry readjusted, was out of CRT range bounds"
logging.info("Warning: you've reached a crt_range limit, can't go further in the last direction. Setting back to {}".format(str(ret_geom)))
working_geometry = ret_geom
os.environ['GRID_TEXT'] = "\n".join([os.getenv('GRID_TEXT') or "", "({})".format(str(geom))])
set_grid_text(top_txt, '', working_geometry)
# Now is the real launch with the grid
sr_launch_return = launch_switchres(mode, working_geometry, switchres_command, launch_command, display_nr)
grid_return_code = sr_launch_return['exit_code']
sr_geometry = geometry.set_from_string(sr_launch_return['geometry'])
working_geometry = readjust_geometry(sr_geometry, sr_launch_return['new_crt_range'], grid_return_code)
os.environ['GRID_TEXT'] = ""
top_txt = ''
time.sleep(2)
#
# MAIN
#
# The default switchres.ini file depends on the platform
if platform.system() == 'Linux':
default_switchres_ini = '/etc/switchres.ini'
else:
default_switchres_ini = 'switchres.ini'
parser = argparse.ArgumentParser(description='Switchres wrapper to adjust a crt_range for switchres.ini')
parser.add_argument('mode', metavar='N', type=float, nargs=3,
help='width height refresh_rate')
parser.add_argument('-l', '--launch', metavar='launch', type=str, default='grid',
help='The program you want to launch')
# parser.add_argument('-i', '--ini', metavar='ini', type=str, default=default_switchres_ini,
# help='The switchres.ini file to edit')
parser.add_argument('-s', '--switchres', metavar='binary', type=str, default='switchres',
help='The switchres binary to use')
parser.add_argument('-d', '--display', metavar='display', type=int, default=0,
help='Set the display to calibrate')
#parser.add_argument('-m', '--monitor', metavar='monitor', type=str, default='arcade_15',
# help='The monitor preset base, to override the switchres.ini (NOT YET IMPLEMENTED)')
parser.add_argument('-g', '--geometry', metavar='geometry', type=str, default='1.0:0:0',
help='Start with a predefined geometry')
parser.add_argument('-v', '--verbose', action='count', default=0, help='Verbose mode')
if sys.version_info.major < 3:
raise Exception("Must use Python 3.7 or later")
if sys.version_info.minor < 10:
raise Exception("Must use Python 3.7 or later")
#args = parser.parse_args(['320', '240', '59.94'])
args = parser.parse_args()
# Set log level according to wanted verbosity
loggingLevel = logging.INFO
logging.basicConfig(stream=sys.stdout, level=loggingLevel, format='%(message)s')
if args.verbose > 0:
loggingLevel = logging.DEBUG
logger = logging.getLogger()
logger.setLevel(loggingLevel)
for handler in logger.handlers:
handler.setLevel(loggingLevel)
for handler in logging.root.handlers[:]:
logging.root.removeHandler(handler)
logging.basicConfig(stream=sys.stdout, level=loggingLevel,
format='[%(levelname)s] %(filename)s/%(funcName)s(%(lineno)d): %(message)s')
logging.debug("Specified logging level: {}".format(args.verbose))
logging.debug("Command line arguments: {}".format(args))
logging.debug(args)
command_mode = mode(args.mode[0], args.mode[1], args.mode[2])
geometry_arg = geometry.set_from_string(args.geometry)
switchres_geometry_loop(command_mode, args.switchres, args.launch, args.display, geometry_arg)

410
deps/switchres/grid.cpp vendored Normal file
View File

@ -0,0 +1,410 @@
/**************************************************************
grid.cpp - Simple test grid
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2022 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse,
Radek Dutkiewicz
**************************************************************/
// To add additional text lines:
// export GRID_TEXT="$(echo -e "\nArrows - screen position, Page Up/Down - H size\n\nH size: 1.0\nH position: 0\nV position: 0")"
// To remove additional lines:
// unset GRID_TEXT
#define SDL_MAIN_HANDLED
#define NUM_GRIDS 2
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include "font.h"
#include <string.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <math.h>
#define FONT_SIZE 11
enum GRID_ADJUST
{
LEFT = 64,
RIGHT,
UP,
DOWN,
H_SIZE_INC,
H_SIZE_DEC
};
typedef struct grid_display
{
int index;
int width;
int height;
SDL_Window *window;
SDL_Renderer *renderer;
std::vector<SDL_Texture*> textures;
} GRID_DISPLAY;
SDL_Surface *surface;
TTF_Font *font;
std::vector<std::string> grid_texts;
//============================================================
// draw_grid
//============================================================
void draw_grid(int num_grid, int width, int height, SDL_Renderer *renderer, std::vector<SDL_Texture*> textures)
{
// Clean the surface
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer);
SDL_Rect rect {0, 0, width, height};
switch (num_grid)
{
case 0:
// 16 x 12 squares
{
// Fill the screen with red
rect = {0, 0, width, height};
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
SDL_RenderFillRect(renderer, &rect);
// Draw white rectangle
rect = {width / 32, height / 24 , width - width / 16, height - height / 12};
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderFillRect(renderer, &rect);
// Draw grid using black rectangles
SDL_Rect rects[16 * 12];
// Set the thickness of horizontal and vertical lines based on the screen resolution
int line_w = round(float(width) / 320.0);
int line_h = round(float(height) / 240.0);
if ( line_w < 1 ) line_w = 1;
if ( line_h < 1 ) line_h = 1;
float rect_w = (width - line_w * 17) / 16.0;
float rect_h = (height - line_h * 13) / 12.0;
for (int i = 0; i < 16; i++)
{
int x_pos1 = ceil(i * rect_w);
int x_pos2 = ceil((i+1) * rect_w);
for (int j = 0; j < 12; j++)
{
int y_pos1 = ceil(j * rect_h);
int y_pos2 = ceil((j+1) * rect_h);
rects[i + j * 16] = {x_pos1 + (i+1) * line_w , y_pos1 + (j+1) * line_h, x_pos2 - x_pos1, y_pos2 - y_pos1};
}
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderFillRects(renderer, rects, 16 * 12);
}
break;
case 1:
// cps2 grid
// Draw outer rectangle
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderDrawRect(renderer, &rect);
for (int i = 0; i < width / 16; i++)
{
for (int j = 0; j < height / 16; j++)
{
if (i == 0 || j == 0 || i == (width / 16) - 1 || j == (height / 16) - 1)
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
else
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
rect = {i * 16, j * 16, 16, 16};
SDL_RenderDrawRect(renderer, &rect);
rect = {i * 16 + 7, j * 16 + 7, 2, 2};
SDL_RenderDrawRect(renderer, &rect);
}
}
break;
}
// Compute text scaling factors
int text_scale_w = std::max(1, (int)floor(width / 320 + 0.5));
int text_scale_h = std::max(1, (int)floor(height / 240 + 0.5));
SDL_Rect text_frame = {0, 0, 0, 0};
// Compute text frame size
for (SDL_Texture *t : textures)
{
int texture_width = 0;
int texture_height = 0;
SDL_QueryTexture(t, NULL, NULL, &texture_width, &texture_height);
text_frame.w = std::max(text_frame.w, texture_width);
text_frame.h += texture_height;
}
text_frame.w *= text_scale_w;
text_frame.h *= text_scale_h;
text_frame.w += FONT_SIZE * text_scale_w; // margin x
text_frame.h += FONT_SIZE * text_scale_h; // margin y
text_frame.x = ceil((width - text_frame.w ) / 2);
text_frame.y = ceil((height - text_frame.h ) / 2);
// Draw Text background
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderFillRect(renderer, &text_frame);
// Draw Text frame
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderDrawRect(renderer, &text_frame);
// Draw text lines
SDL_Rect text_rect = {0, text_frame.y + FONT_SIZE * text_scale_h / 2, 0, 0};
for (SDL_Texture *t : textures)
{
int texture_width = 0;
int texture_height = 0;
SDL_QueryTexture(t, NULL, NULL, &texture_width, &texture_height);
text_rect.w = texture_width *= text_scale_w;
text_rect.h = texture_height *= text_scale_h;
text_rect.x = ceil((width - text_rect.w ) / 2);
SDL_RenderCopy(renderer, t, NULL, &text_rect);
text_rect.y += text_rect.h;
}
SDL_RenderPresent(renderer);
}
//============================================================
// main
//============================================================
int main(int argc, char **argv)
{
SDL_Window* win_array[10] = {};
GRID_DISPLAY display_array[10] = {};
int display_total = 0;
// Initialize SDL
if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
printf("error initializing SDL: %s\n", SDL_GetError());
return 1;
}
// Get target displays
if (argc > 1)
{
// Parse command line for display indexes
int display_index = 0;
int num_displays = SDL_GetNumVideoDisplays();
for (int arg = 1; arg < argc; arg++)
{
sscanf(argv[arg], "%d", &display_index);
if (display_index < 0 || display_index > num_displays - 1)
{
printf("error, bad display_index: %d\n", display_index);
return 1;
}
display_array[display_total].index = display_index;
display_total++;
}
}
else
{
// No display specified, use default
display_array[0].index = 0;
display_total = 1;
}
// Initialize text
TTF_Init();
SDL_RWops* font_resource = SDL_RWFromConstMem(ATTRACTPLUS_TTF, (sizeof(ATTRACTPLUS_TTF)) / (sizeof(ATTRACTPLUS_TTF[0])));
font = TTF_OpenFontRW(font_resource, 1, FONT_SIZE);
grid_texts.push_back(" "); // empty line for screen resolution
char *grid_text = getenv("GRID_TEXT");
if ( grid_text != NULL )
{
char* p = strtok(grid_text, "\n");
while(p)
{
grid_texts.push_back(p);
p = strtok(NULL, "\n");
}
}
// Create windows
for (int disp = 0; disp < display_total; disp++)
{
// Get target display size
SDL_DisplayMode dm;
SDL_GetCurrentDisplayMode(display_array[disp].index, &dm);
SDL_ShowCursor(SDL_DISABLE);
display_array[disp].width = dm.w;
display_array[disp].height = dm.h;
// Create window
display_array[disp].window = SDL_CreateWindow("Switchres test grid", SDL_WINDOWPOS_CENTERED_DISPLAY(display_array[disp].index), SDL_WINDOWPOS_CENTERED, dm.w, dm.h, SDL_WINDOW_FULLSCREEN_DESKTOP);
// Required by Window multi-monitor
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
// Create renderer
display_array[disp].renderer = SDL_CreateRenderer(display_array[disp].window, -1, SDL_RENDERER_ACCELERATED);
SDL_RenderPresent(display_array[disp].renderer);
// Render first text
grid_texts[0] = "Mode: " + std::to_string(dm.w) + " x " + std::to_string(dm.h) + " @ " + std::to_string(dm.refresh_rate);
for ( int i = 0; i < grid_texts.size(); i++ )
{
surface = TTF_RenderText_Solid(font, grid_texts[i].c_str(), {255, 255, 255});
display_array[disp].textures.push_back(SDL_CreateTextureFromSurface(display_array[disp].renderer, surface));
}
// Draw grid
draw_grid(0, display_array[disp].width, display_array[disp].height, display_array[disp].renderer, display_array[disp].textures);
}
// Wait for escape key
bool close = false;
int num_grid = 0;
int return_code = 0;
int CTRL_modifier = 0;
while (!close)
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
close = true;
break;
case SDL_KEYDOWN:
if (event.key.keysym.mod & KMOD_LCTRL || event.key.keysym.mod & KMOD_RCTRL)
CTRL_modifier = 1<<7;
switch (event.key.keysym.scancode)
{
case SDL_SCANCODE_ESCAPE:
case SDL_SCANCODE_Q:
close = true;
return_code = 1;
break;
case SDL_SCANCODE_BACKSPACE:
case SDL_SCANCODE_DELETE:
close = true;
return_code = 2;
break;
case SDL_SCANCODE_R:
close = true;
return_code = 3;
break;
case SDL_SCANCODE_RETURN:
case SDL_SCANCODE_KP_ENTER:
close = true;
break;
case SDL_SCANCODE_TAB:
num_grid ++;
for (int disp = 0; disp < display_total; disp++)
draw_grid(num_grid % NUM_GRIDS, display_array[disp].width, display_array[disp].height, display_array[disp].renderer, display_array[disp].textures);
break;
case SDL_SCANCODE_LEFT:
close = true;
return_code = GRID_ADJUST::LEFT;
break;
case SDL_SCANCODE_RIGHT:
close = true;
return_code = GRID_ADJUST::RIGHT;
break;
case SDL_SCANCODE_UP:
close = true;
return_code = GRID_ADJUST::UP;
break;
case SDL_SCANCODE_DOWN:
close = true;
return_code = GRID_ADJUST::DOWN;
break;
case SDL_SCANCODE_PAGEUP:
close = true;
return_code = GRID_ADJUST::H_SIZE_INC;
break;
case SDL_SCANCODE_PAGEDOWN:
close = true;
return_code = GRID_ADJUST::H_SIZE_DEC;
break;
default:
break;
}
}
}
}
// Destroy font
SDL_FreeSurface(surface);
TTF_CloseFont(font);
TTF_Quit();
// Destroy all windows
for (int disp = 0; disp < display_total; disp++)
{
for (SDL_Texture *t : display_array[disp].textures)
SDL_DestroyTexture(t);
SDL_DestroyWindow(display_array[disp].window);
}
SDL_Quit();
return return_code | CTRL_modifier;
}

View File

@ -75,4 +75,4 @@ void set_log_verbosity(int level)
if (level >= SR_DEBUG)
log_verbose = log_verbose_bak;
}
}

159
deps/switchres/makefile vendored Normal file
View File

@ -0,0 +1,159 @@
PLATFORM := $(shell uname)
MAIN = switchres_main
STANDALONE = switchres
TARGET_LIB = libswitchres
DRMHOOK_LIB = libdrmhook
GRID = grid
SRC = monitor.cpp modeline.cpp switchres.cpp display.cpp custom_video.cpp log.cpp switchres_wrapper.cpp edid.cpp
OBJS = $(SRC:.cpp=.o)
CROSS_COMPILE ?=
CXX ?= g++
AR ?= ar
LDFLAGS = -shared
FINAL_CXX=$(CROSS_COMPILE)$(CXX)
FINAL_AR=$(CROSS_COMPILE)$(AR)
CPPFLAGS = -O3 -Wall -Wextra
PKG_CONFIG=pkg-config
INSTALL=install
LN=ln
DESTDIR ?=
PREFIX ?= /usr
INCDIR = $(DESTDIR)$(PREFIX)/include
LIBDIR = $(DESTDIR)$(PREFIX)/lib
BINDIR = $(DESTDIR)$(PREFIX)/bin
PKGDIR = $(LIBDIR)/pkgconfig
ifneq ($(DEBUG),)
CPPFLAGS += -g
endif
# If the version is not set at make, read it from switchres.h
ifeq ($(VERSION),)
VERSION:=$(shell grep -E "^\#define SWITCHRES_VERSION" switchres.h | grep -oE "[0-9]+\.[0-9]+\.[0-9]+" )
else
CPPFLAGS += -DSWITCHRES_VERSION="\"$(VERSION)\""
endif
VERSION_MAJOR := $(firstword $(subst ., ,$(VERSION)))
VERSION_MINOR := $(word 2,$(subst ., ,$(VERSION)))
VERSION_PATCH := $(word 3,$(subst ., ,$(VERSION)))
$(info Switchres $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH))
# Linux
ifeq ($(PLATFORM),Linux)
SRC += display_linux.cpp
HAS_VALID_XRANDR := $(shell $(PKG_CONFIG) --silence-errors --libs xrandr; echo $$?)
ifeq ($(HAS_VALID_XRANDR),1)
$(info Switchres needs xrandr. X support is disabled)
else
$(info X support enabled)
CPPFLAGS += -DSR_WITH_XRANDR
SRC += custom_video_xrandr.cpp
endif
HAS_VALID_DRMKMS := $(shell $(PKG_CONFIG) --silence-errors --libs "libdrm >= 2.4.98"; echo $$?)
ifeq ($(HAS_VALID_DRMKMS),1)
$(info Switchres needs libdrm >= 2.4.98. KMS support is disabled)
else
$(info KMS support enabled)
CPPFLAGS += -DSR_WITH_KMSDRM
EXTRA_LIBS = libdrm
SRC += custom_video_drmkms.cpp
endif
# SDL2 misses a test for drm as drm.h is required
HAS_VALID_SDL2 := $(shell $(PKG_CONFIG) --silence-errors --libs "sdl2 >= 2.0.16"; echo $$?)
ifeq ($(HAS_VALID_SDL2),1)
$(info Switchres needs SDL2 >= 2.0.16. SDL2 support is disabled)
else
$(info SDL2 support enabled)
CPPFLAGS += -DSR_WITH_SDL2 $(pkg-config --cflags sdl2)
EXTRA_LIBS += sdl2
SRC += display_sdl2.cpp
endif
ifneq (,$(EXTRA_LIBS))
CPPFLAGS += $(shell $(PKG_CONFIG) --cflags $(EXTRA_LIBS))
LIBS += $(shell $(PKG_CONFIG) --libs $(EXTRA_LIBS))
endif
CPPFLAGS += -fPIC
LIBS += -ldl
REMOVE = rm -f
STATIC_LIB_EXT = a
DYNAMIC_LIB_EXT = so.$(VERSION)
LINKER_NAME := $(TARGET_LIB).so
REAL_SO_NAME := $(LINKER_NAME).$(VERSION)
SO_NAME := $(LINKER_NAME).$(VERSION_MAJOR)
LIB_CPPFLAGS := -Wl,-soname,$(SO_NAME)
# Windows
else ifneq (,$(findstring NT,$(PLATFORM)))
SRC += display_windows.cpp custom_video_ati_family.cpp custom_video_ati.cpp custom_video_adl.cpp custom_video_pstrip.cpp resync_windows.cpp
WIN_ONLY_FLAGS = -static-libgcc -static-libstdc++
CPPFLAGS += -static $(WIN_ONLY_FLAGS)
LIBS =
#REMOVE = del /f
REMOVE = rm -f
STATIC_LIB_EXT = lib
DYNAMIC_LIB_EXT = dll
endif
define SR_PKG_CONFIG
prefix=$(PREFIX)
exec_prefix=$${prefix}
includedir=$${prefix}/include
libdir=$${exec_prefix}/lib
Name: libswitchres
Description: A modeline generator for CRT monitors
Version: $(VERSION)
Cflags: -I$${includedir}/switchres
Libs: -L$${libdir} -ldl -lswitchres
endef
%.o : %.cpp
$(FINAL_CXX) -c $(CPPFLAGS) $< -o $@
all: $(SRC:.cpp=.o) $(MAIN).cpp $(TARGET_LIB) prepare_pkg_config
@echo $(OSFLAG)
$(FINAL_CXX) $(CPPFLAGS) $(CXXFLAGS) $(SRC:.cpp=.o) $(MAIN).cpp $(LIBS) -o $(STANDALONE)
$(TARGET_LIB): $(OBJS)
$(FINAL_CXX) $(LDFLAGS) $(CPPFLAGS) $(LIB_CPPFLAGS) -o $@.$(DYNAMIC_LIB_EXT) $^
$(FINAL_CXX) -c $(CPPFLAGS) -DSR_WIN32_STATIC switchres_wrapper.cpp -o switchres_wrapper.o
$(FINAL_AR) rcs $@.$(STATIC_LIB_EXT) $(^)
$(DRMHOOK_LIB):
$(FINAL_CXX) drm_hook.cpp -shared -ldl -fPIC -I/usr/include/libdrm -o libdrmhook.so
$(GRID):
$(FINAL_CXX) grid.cpp $(WIN_ONLY_FLAGS) -lSDL2 -lSDL2_ttf -o grid
clean:
$(REMOVE) $(OBJS) $(STANDALONE) $(TARGET_LIB).*
$(REMOVE) switchres.pc
prepare_pkg_config:
$(file > switchres.pc,$(SR_PKG_CONFIG))
install:
$(INSTALL) -Dm644 $(TARGET_LIB).$(DYNAMIC_LIB_EXT) $(LIBDIR)/$(TARGET_LIB).$(DYNAMIC_LIB_EXT)
$(INSTALL) -Dm644 switchres_defines.h $(INCDIR)/switchres/switchres_defines.h
$(INSTALL) -Dm644 switchres_wrapper.h $(INCDIR)/switchres/switchres_wrapper.h
$(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)
endif
uninstall:
$(REMOVE) $(LIBDIR)/$(TARGET_LIB).*
$(REMOVE) $(PKGDIR)/switchres.pc

View File

@ -47,12 +47,16 @@ int modeline_create(modeline *s_mode, modeline *t_mode, monitor_range *range, ge
int x_scale = 0;
int y_scale = 0;
int v_scale = 0;
double x_fscale = 0;
double y_fscale = 0;
double v_fscale = 0;
double x_diff = 0;
double y_diff = 0;
double v_diff = 0;
double y_ratio = 0;
double x_ratio = 0;
double borders = 0;
int rotation = s_mode->type & MODE_ROTATED;
double source_aspect = rotation? 1.0 / (STANDARD_CRT_ASPECT) : (STANDARD_CRT_ASPECT);
t_mode->result.weight = 0;
// ≈≈≈ Vertical refresh ≈≈≈
@ -165,7 +169,7 @@ int modeline_create(modeline *s_mode, modeline *t_mode, monitor_range *range, ge
if (t_mode->type & X_RES_EDITABLE)
{
x_scale = y_scale;
double aspect_corrector = max(1.0f, cs->monitor_aspect / (cs->rotation? (1.0/(STANDARD_CRT_ASPECT)) : (STANDARD_CRT_ASPECT)));
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);
}
@ -176,7 +180,7 @@ int modeline_create(modeline *s_mode, modeline *t_mode, monitor_range *range, ge
// if the source width fits our xres, try applying integer scaling
if (x_scale)
{
x_scale = scale_into_aspect(s_mode->hactive, t_mode->hactive, cs->rotation?1.0/(STANDARD_CRT_ASPECT):STANDARD_CRT_ASPECT, cs->monitor_aspect, &x_diff);
x_scale = scale_into_aspect(s_mode->hactive, t_mode->hactive, source_aspect, cs->monitor_aspect, &x_diff);
if (x_diff > 15.0 && t_mode->hactive < cs->super_width)
t_mode->result.weight |= R_RES_STRETCH;
}
@ -203,16 +207,16 @@ int modeline_create(modeline *s_mode, modeline *t_mode, monitor_range *range, ge
t_mode->hactive = max(t_mode->hactive, normalize(STANDARD_CRT_ASPECT * t_mode->vactive, 8));
// calculate integer scale for prescaling
x_scale = max(1, scale_into_aspect(s_mode->hactive, t_mode->hactive, cs->rotation?1.0/(STANDARD_CRT_ASPECT):STANDARD_CRT_ASPECT, cs->monitor_aspect, &x_diff));
x_scale = max(1, scale_into_aspect(s_mode->hactive, t_mode->hactive, source_aspect, cs->monitor_aspect, &x_diff));
y_scale = max(1, floor(double(t_mode->vactive) / s_mode->vactive));
scan_factor = interlace;
doublescan = 1;
}
x_ratio = double(t_mode->hactive) / s_mode->hactive;
y_ratio = double(t_mode->vactive) / s_mode->vactive;
v_scale = max(round_near(vfreq_real / s_mode->vfreq), 1);
x_fscale = double(t_mode->hactive) / s_mode->hactive * source_aspect / cs->monitor_aspect;
y_fscale = double(t_mode->vactive) / s_mode->vactive;
v_fscale = vfreq_real / s_mode->vfreq;
v_diff = (vfreq_real / v_scale) - s_mode->vfreq;
if (fabs(v_diff) > cs->refresh_tolerance)
t_mode->result.weight |= R_V_FREQ_OFF;
@ -224,12 +228,13 @@ int modeline_create(modeline *s_mode, modeline *t_mode, monitor_range *range, ge
double margin = 0;
double vblank_lines = 0;
double vvt_ini = 0;
double interlace_incr = !cs->interlace_force_even && interlace == 2? 0.5 : 0;
// Get resulting refresh
t_mode->vfreq = vfreq_real;
// Get total vertical lines
vvt_ini = total_lines_for_yres(t_mode->vactive, t_mode->vfreq, range, borders, scan_factor) + (interlace == 2?0.5:0);
vvt_ini = total_lines_for_yres(t_mode->vactive, t_mode->vfreq, range, borders, scan_factor) + interlace_incr;
// Calculate horizontal frequency
t_mode->hfreq = t_mode->vfreq * vvt_ini;
@ -246,6 +251,7 @@ int modeline_create(modeline *s_mode, modeline *t_mode, monitor_range *range, ge
if (t_mode->type & X_RES_EDITABLE)
{
x_scale *= 2;
x_fscale *= 2;
t_mode->hactive *= 2;
goto horizontal_values;
}
@ -258,10 +264,13 @@ int modeline_create(modeline *s_mode, modeline *t_mode, monitor_range *range, ge
// Vertical blanking
t_mode->vtotal = vvt_ini * scan_factor;
vblank_lines = int(t_mode->hfreq * (range->vertical_blank + borders)) + (interlace == 2?0.5:0);
vblank_lines = round_near(t_mode->hfreq * (range->vertical_blank + borders)) + interlace_incr;
margin = (t_mode->vtotal - t_mode->vactive - vblank_lines * scan_factor) / (cs->v_shift_correct? 1 : 2);
t_mode->vbegin = t_mode->vactive + max(round_near(t_mode->hfreq * range->vfront_porch * scan_factor + margin), 1);
double v_front_porch = margin + t_mode->hfreq * range->vfront_porch * scan_factor + interlace_incr;
int (*pf_round)(double) = interlace == 2? (cs->interlace_force_even? round_near_even : round_near_odd) : round_near;
t_mode->vbegin = t_mode->vactive + max(pf_round(v_front_porch), 1);
t_mode->vend = t_mode->vbegin + max(round_near(t_mode->hfreq * range->vsync_pulse * scan_factor), 1);
// Recalculate final vfreq
@ -269,29 +278,18 @@ int modeline_create(modeline *s_mode, modeline *t_mode, monitor_range *range, ge
t_mode->hsync = range->hsync_polarity;
t_mode->vsync = range->vsync_polarity;
t_mode->interlace = interlace == 2?1:0;
t_mode->doublescan = doublescan == 1?0:1;
// Apply interlace fixes
if (cs->interlace_force_even && interlace == 2)
{
t_mode->vbegin = (t_mode->vbegin / 2) * 2;
t_mode->vend = (t_mode->vend / 2) * 2;
t_mode->vtotal++;
}
t_mode->interlace = interlace == 2? 1 : 0;
t_mode->doublescan = doublescan == 1? 0 : 1;
}
// finally, store result
t_mode->result.scan_penalty = (s_mode->interlace != t_mode->interlace? 1:0) + (s_mode->doublescan != t_mode->doublescan? 1:0);
t_mode->result.x_scale = x_scale;
t_mode->result.y_scale = y_scale;
t_mode->result.v_scale = v_scale;
t_mode->result.scan_penalty = (s_mode->interlace != t_mode->interlace? 1 : 0) + (s_mode->doublescan != t_mode->doublescan? 1 : 0);
t_mode->result.x_scale = t_mode->result.weight & R_RES_STRETCH || t_mode->hactive >= cs->super_width ? x_fscale : (double)x_scale;
t_mode->result.y_scale = t_mode->result.weight & R_RES_STRETCH? y_fscale : (double)y_scale;
t_mode->result.v_scale = v_fscale;
t_mode->result.x_diff = x_diff;
t_mode->result.y_diff = y_diff;
t_mode->result.v_diff = v_diff;
t_mode->result.x_ratio = x_ratio;
t_mode->result.y_ratio = y_ratio;
t_mode->result.v_ratio = 0;
return 0;
}
@ -478,9 +476,9 @@ char * modeline_result(modeline *mode, char *result)
sprintf(result, " out of range");
else
sprintf(result, "%4d x%4d_%3.6f%s%s %3.6f [%s] scale(%d, %d, %d) diff(%.2f, %.2f, %.4f) ratio(%.3f, %.3f)",
sprintf(result, "%4d x%4d_%3.6f%s%s %3.6f [%s] scale(%.3f, %.3f, %.3f) diff(%.3f, %.3f, %.3f)",
mode->hactive, mode->vactive, mode->vfreq, mode->interlace?"i":"p", mode->doublescan?"d":"", mode->hfreq/1000, mode->result.weight & R_RES_STRETCH?"fract":"integ",
mode->result.x_scale, mode->result.y_scale, mode->result.v_scale, mode->result.x_diff, mode->result.y_diff, mode->result.v_diff, mode->result.x_ratio, mode->result.y_ratio);
mode->result.x_scale, mode->result.y_scale, mode->result.v_scale, mode->result.x_diff, mode->result.y_diff, mode->result.v_diff);
return result;
}
@ -490,7 +488,7 @@ char * modeline_result(modeline *mode, char *result)
int modeline_compare(modeline *t, modeline *best)
{
bool vector = (t->hactive == (int)t->result.x_ratio);
bool vector = (t->hactive == (int)t->result.x_scale);
if (t->result.weight < best->result.weight)
return 1;
@ -502,12 +500,12 @@ int modeline_compare(modeline *t, modeline *best)
if (t->result.weight & R_RES_STRETCH || vector)
{
double t_y_score = t->result.y_ratio * (t->interlace?(2.0/3.0):1.0);
double b_y_score = best->result.y_ratio * (best->interlace?(2.0/3.0):1.0);
double t_y_score = t->result.y_scale * (t->interlace?(2.0/3.0):1.0);
double b_y_score = best->result.y_scale * (best->interlace?(2.0/3.0):1.0);
if ((t_v_diff < b_v_diff) ||
((t_v_diff == b_v_diff) && (t_y_score > b_y_score)) ||
((t_v_diff == b_v_diff) && (t_y_score == b_y_score) && (t->result.x_ratio > best->result.x_ratio)))
((t_v_diff == b_v_diff) && (t_y_score == b_y_score) && (t->result.x_scale > best->result.x_scale)))
return 1;
}
else
@ -626,7 +624,7 @@ int modeline_parse(const char *user_modeline, modeline *mode)
if (e != 9)
{
log_error("SwitchRes: missing parameter in user modeline\n %s\n", user_modeline);
log_error("Switchres: missing parameter in user modeline\n %s\n", user_modeline);
memset(mode, 0, sizeof(struct modeline));
return false;
}
@ -638,7 +636,7 @@ int modeline_parse(const char *user_modeline, modeline *mode)
mode->refresh = mode->vfreq;
mode->width = mode->hactive;
mode->height = mode->vactive;
log_verbose("SwitchRes: user modeline %s\n", modeline_print(mode, modeline_txt, MS_FULL));
log_verbose("Switchres: user modeline %s\n", modeline_print(mode, modeline_txt, MS_FULL));
return true;
}
@ -649,38 +647,153 @@ int modeline_parse(const char *user_modeline, modeline *mode)
int modeline_to_monitor_range(monitor_range *range, modeline *mode)
{
if (range->vfreq_min == 0)
{
range->vfreq_min = mode->vfreq - 0.2;
range->vfreq_max = mode->vfreq + 0.2;
}
range->vfreq_min = mode->vfreq - 0.2;
range->vfreq_max = mode->vfreq + 0.2;
double line_time = 1 / mode->hfreq;
double pixel_time = line_time / mode->htotal * 1000000;
double interlace_factor = mode->interlace? 0.5 : 1.0;
range->hfront_porch = pixel_time * (mode->hbegin - mode->hactive);
range->hsync_pulse = pixel_time * (mode->hend - mode->hbegin);
range->hback_porch = pixel_time * (mode->htotal - mode->hend);
range->vfront_porch = line_time * (mode->vbegin - mode->vactive);
range->vsync_pulse = line_time * (mode->vend - mode->vbegin);
range->vback_porch = line_time * (mode->vtotal - mode->vend);
// We floor the vertical fields to remove the half line from interlaced modes, because
// the modeline generator will add it automatically. Otherwise it would be added twice.
range->vfront_porch = line_time * floor((mode->vbegin - mode->vactive) * interlace_factor);
range->vsync_pulse = line_time * floor((mode->vend - mode->vbegin) * interlace_factor);
range->vback_porch = line_time * floor((mode->vtotal - mode->vend) * interlace_factor);
range->vertical_blank = range->vfront_porch + range->vsync_pulse + range->vback_porch;
range->hsync_polarity = mode->hsync;
range->vsync_polarity = mode->vsync;
range->progressive_lines_min = mode->interlace?0:mode->vactive;
range->progressive_lines_max = mode->interlace?0:mode->vactive;
range->interlaced_lines_min = mode->interlace?mode->vactive:0;
range->interlaced_lines_max= mode->interlace?mode->vactive:0;
range->progressive_lines_min = mode->interlace? 0 : mode->vactive;
range->progressive_lines_max = mode->interlace? 0 : mode->vactive;
range->interlaced_lines_min = mode->interlace? mode->vactive : 0;
range->interlaced_lines_max= mode->interlace? mode->vactive : 0;
range->hfreq_min = range->vfreq_min * mode->vtotal;
range->hfreq_max = range->vfreq_max * mode->vtotal;
range->hfreq_min = range->vfreq_min * mode->vtotal * interlace_factor;
range->hfreq_max = range->vfreq_max * mode->vtotal * interlace_factor;
return 1;
}
//============================================================
// modeline_adjust
//============================================================
int modeline_adjust(modeline *mode, double hfreq_max, generator_settings *cs)
{
// If input values are out of range, they are fixed within range and returned in the cs struct.
// H size ajdustment, valid values 0.5-2.0
if (cs->h_size != 1.0f)
{
if (cs->h_size > 2.0f)
cs->h_size = 2.0f;
else if (cs->h_size < 0.5f)
cs->h_size = 0.5f;
monitor_range range;
memset(&range, 0, sizeof(monitor_range));
modeline_to_monitor_range(&range, mode);
range.hfront_porch /= cs->h_size;
range.hback_porch /= cs->h_size;
modeline_create(mode, mode, &range, cs);
}
// H shift adjustment, positive or negative value
if (cs->h_shift != 0)
{
if (cs->h_shift >= mode->hbegin - mode->hactive)
cs->h_shift = mode->hbegin - mode->hactive - 1;
else if (cs->h_shift <= mode->hend - mode->htotal)
cs->h_shift = mode->hend - mode->htotal + 1;
mode->hbegin -= cs->h_shift;
mode->hend -= cs->h_shift;
}
// V shift adjustment, positive or negative value
if (cs->v_shift != 0)
{
int vactive = mode->vactive;
int vbegin = mode->vbegin;
int vend = mode->vend;
int vtotal = mode->vtotal;
if (mode->interlace)
{
vactive >>= 1;
vbegin >>= 1;
vend >>= 1;
vtotal >>= 1;
}
int v_front_porch = vbegin - vactive;
int v_back_porch = vend - vtotal;
int max_vtotal = hfreq_max / mode->vfreq;
int border = max_vtotal - vtotal;
int padding = 0;
// v_shift positive
if (cs->v_shift >= v_front_porch)
{
int v_front_porch_ex = v_front_porch + border;
if (cs->v_shift >= v_front_porch_ex)
cs->v_shift = v_front_porch_ex - 1;
padding = cs->v_shift - v_front_porch + 1;
vbegin += padding;
vend += padding;
vtotal += padding;
}
// v_shift negative
else if (cs->v_shift <= v_back_porch + 1)
{
int v_back_porch_ex = v_back_porch - border;
if (cs->v_shift <= v_back_porch_ex + 1)
cs->v_shift = v_back_porch_ex + 2;
padding = -(cs->v_shift - v_back_porch - 2);
vtotal += padding;
}
vbegin -= cs->v_shift;
vend -= cs->v_shift;
if (mode->interlace)
{
vbegin = (vbegin << 1) | (mode->vbegin & 1);
vend = (vend << 1) | (mode->vend & 1);
vtotal = (vtotal << 1) | (mode->vtotal & 1);
}
mode->vbegin = vbegin;
mode->vend = vend;
mode->vtotal = vtotal;
if (padding != 0)
{
mode->hfreq = mode->vfreq * mode->vtotal / (mode->interlace? 2.0 : 1.0);
monitor_range range;
memset(&range, 0, sizeof(monitor_range));
modeline_to_monitor_range(&range, mode);
monitor_show_range(&range);
modeline_create(mode, mode, &range, cs);
}
}
return 0;
}
//============================================================
// modeline_is_different
//============================================================
@ -691,6 +804,16 @@ int modeline_is_different(modeline *n, modeline *p)
return memcmp(n, p, offsetof(struct modeline, vfreq));
}
//============================================================
// modeline_copy_timings
//============================================================
void modeline_copy_timings(modeline *n, modeline *p)
{
// Only copy relevant timing fields
memcpy(n, p, offsetof(struct modeline, width));
}
//============================================================
// monitor_fill_vesa_gtf
//============================================================
@ -751,6 +874,24 @@ int round_near(double number)
return number < 0.0 ? ceil(number - 0.5) : floor(number + 0.5);
}
//============================================================
// round_near_odd
//============================================================
int round_near_odd(double number)
{
return int(ceil(number)) % 2 == 0? floor(number) : ceil(number);
}
//============================================================
// round_near_even
//============================================================
int round_near_even(double number)
{
return int(ceil(number)) % 2 == 1? floor(number) : ceil(number);
}
//============================================================
// normalize
//============================================================

View File

@ -63,15 +63,12 @@ typedef struct mode_result
{
int weight;
int scan_penalty;
int x_scale;
int y_scale;
int v_scale;
double x_scale;
double y_scale;
double v_scale;
double x_diff;
double y_diff;
double v_diff;
double x_ratio;
double y_ratio;
double v_ratio;
} mode_result;
typedef struct modeline
@ -98,6 +95,7 @@ typedef struct modeline
int refresh;
int refresh_label;
//
int id;
int type;
int range;
uint64_t platform_data;
@ -110,10 +108,12 @@ typedef struct generator_settings
int interlace;
int doublescan;
uint64_t pclock_min;
bool rotation;
double monitor_aspect;
double refresh_tolerance;
int super_width;
double h_size;
int h_shift;
int v_shift;
int v_shift_correct;
int pixel_precision;
int interlace_force_even;
@ -130,9 +130,13 @@ char * modeline_result(modeline *mode, char *result);
int modeline_vesa_gtf(modeline *m);
int modeline_parse(const char *user_modeline, modeline *mode);
int modeline_to_monitor_range(monitor_range *range, modeline *mode);
int modeline_adjust(modeline *mode, double hfreq_max, generator_settings *cs);
int modeline_is_different(modeline *n, modeline *p);
void modeline_copy_timings(modeline *n, modeline *p);
int round_near(double number);
int round_near_odd(double number);
int round_near_even(double number);
int normalize(int a, int b);
int real_res(int x);

View File

@ -35,6 +35,9 @@ int monitor_fill_range(monitor_range *range, const char *specs_line)
{
monitor_range new_range;
if (strlen(specs_line) == 0)
return 0;
if (strcmp(specs_line, "auto")) {
int e = sscanf(specs_line, "%lf-%lf,%lf-%lf,%lf,%lf,%lf,%lf,%lf,%lf,%d,%d,%d,%d,%d,%d",
&new_range.hfreq_min, &new_range.hfreq_max,
@ -75,6 +78,9 @@ int monitor_fill_range(monitor_range *range, const char *specs_line)
int monitor_fill_lcd_range(monitor_range *range, const char *specs_line)
{
if (strlen(specs_line) == 0)
return 0;
if (strcmp(specs_line, "auto"))
{
if (sscanf(specs_line, "%lf-%lf", &range->vfreq_min, &range->vfreq_max) == 2)
@ -115,7 +121,7 @@ int monitor_show_range(monitor_range *range)
// monitor_set_preset
//============================================================
int monitor_set_preset(char *type, monitor_range *range)
int monitor_set_preset(const char *type, monitor_range *range)
{
// PAL TV - 50 Hz/625
if (!strcmp(type, "pal"))

View File

@ -55,7 +55,7 @@ typedef struct monitor_range
int monitor_fill_range(monitor_range *range, const char *specs_line);
int monitor_show_range(monitor_range *range);
int monitor_set_preset(char *type, monitor_range *range);
int monitor_set_preset(const char *type, monitor_range *range);
int monitor_fill_lcd_range(monitor_range *range, const char *specs_line);
int monitor_fill_vesa_gtf(monitor_range *range, const char *max_lines);
int monitor_fill_vesa_range(monitor_range *range, int lines_min, int lines_max);

View File

@ -23,10 +23,8 @@ const string WHITESPACE = " \n\r\t\f\v";
#if defined(_WIN32)
#define SR_CONFIG_PATHS ";.\\;.\\ini\\;"
#elif defined(__linux__)
#define SR_CONFIG_PATHS ";./;./ini/;/etc/;"
#else
#define SR_CONFIG_PATHS ";./"
#define SR_CONFIG_PATHS ";./;./ini/;/etc/;"
#endif
//============================================================
@ -83,33 +81,34 @@ constexpr unsigned int s2i(const char* str, int h = 0)
switchres_manager::switchres_manager()
{
// Set Switchres default config options
set_monitor("generic_15");
set_modeline("auto");
set_lcd_range("auto");
for (int i = 0; i++ < MAX_RANGES;) set_crt_range(i, "auto");
// Set display manager default options
set_screen("auto");
set_modeline_generation(true);
set_lock_unsupported_modes(true);
set_lock_system_modes(true);
set_refresh_dont_care(false);
// Set modeline generator default options
set_interlace(true);
set_doublescan(true);
set_dotclock_min(0.0f);
set_rotation(false);
set_monitor_aspect(STANDARD_CRT_ASPECT);
set_refresh_tolerance(2.0f);
set_super_width(2560);
set_v_shift_correct(0);
set_pixel_precision(1);
set_interlace_force_even(0);
// Create our display manager
m_display_factory = new display_manager();
m_current_display = m_display_factory;
// Set display manager default options
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");
display()->set_screen("auto");
display()->set_modeline_generation(true);
display()->set_lock_unsupported_modes(true);
display()->set_lock_system_modes(true);
display()->set_refresh_dont_care(false);
// Set modeline generator default options
display()->set_interlace(true);
display()->set_doublescan(true);
display()->set_dotclock_min(0.0f);
display()->set_monitor_aspect(STANDARD_CRT_ASPECT);
display()->set_refresh_tolerance(2.0f);
display()->set_super_width(2560);
display()->set_h_shift(0);
display()->set_v_shift(0);
display()->set_h_size(1.0f);
display()->set_v_shift_correct(0);
display()->set_pixel_precision(1);
display()->set_interlace_force_even(0);
// Set logger properties
set_log_info_fn((void*)printf);
@ -134,26 +133,30 @@ switchres_manager::~switchres_manager()
// switchres_manager::add_display
//============================================================
display_manager* switchres_manager::add_display()
display_manager* switchres_manager::add_display(bool parse_options)
{
// Parse display specific ini, if it exists
display_settings base_ds = ds;
char file_name[32] = {0};
sprintf(file_name, "display%d.ini", (int)displays.size());
parse_config(file_name);
bool has_ini = parse_config(file_name);
// Create new display
display_manager *display = m_display_factory->make(&ds);
display_manager *display = m_display_factory->make(&m_display_factory->m_ds);
if (display == nullptr)
{
log_error("Switchres: error adding display\n");
return nullptr;
}
m_current_display = display;
display->set_index(displays.size());
display->set_has_ini(has_ini);
displays.push_back(display);
log_verbose("Switchres(v%s) display[%d]: monitor[%s] generation[%s]\n",
SWITCHRES_VERSION, display->index(), ds.monitor, ds.modeline_generation?"on":"off");
log_verbose("Switchres(v%s) add display[%d]\n", SWITCHRES_VERSION, display->index());
display->parse_options();
// restore base display settings
ds = base_ds;
if (parse_options)
display->parse_options();
return display;
}
@ -197,185 +200,212 @@ bool switchres_manager::parse_config(const char *file_name)
string key, value;
if(get_value(line, key, value))
{
switch (s2i(key.c_str()))
{
// Switchres options
case s2i("verbose"):
if (atoi(value.c_str())) set_log_verbose_fn((void*)printf);
break;
case s2i("monitor"):
transform(value.begin(), value.end(), value.begin(), ::tolower);
set_monitor(value.c_str());
break;
case s2i("crt_range0"):
set_crt_range(0, value.c_str());
break;
case s2i("crt_range1"):
set_crt_range(1, value.c_str());
break;
case s2i("crt_range2"):
set_crt_range(2, value.c_str());
break;
case s2i("crt_range3"):
set_crt_range(3, value.c_str());
break;
case s2i("crt_range4"):
set_crt_range(4, value.c_str());
break;
case s2i("crt_range5"):
set_crt_range(5, value.c_str());
break;
case s2i("crt_range6"):
set_crt_range(6, value.c_str());
break;
case s2i("crt_range7"):
set_crt_range(7, value.c_str());
break;
case s2i("crt_range8"):
set_crt_range(8, value.c_str());
break;
case s2i("crt_range9"):
set_crt_range(9, value.c_str());
break;
case s2i("lcd_range"):
set_lcd_range(value.c_str());
break;
case s2i("modeline"):
set_modeline(value.c_str());
break;
case s2i("user_mode"):
{
modeline user_mode = {};
if (strcmp(value.c_str(), "auto"))
{
if (sscanf(value.c_str(), "%dx%d@%d", &user_mode.width, &user_mode.height, &user_mode.refresh) < 1)
{
log_error("Error: use format resolution <w>x<h>@<r>\n");
break;
}
}
set_user_mode(&user_mode);
break;
}
// Display options
case s2i("display"):
set_screen(value.c_str());
break;
case s2i("api"):
set_api(value.c_str());
break;
case s2i("modeline_generation"):
set_modeline_generation(atoi(value.c_str()));
break;
case s2i("lock_unsupported_modes"):
set_lock_unsupported_modes(atoi(value.c_str()));
break;
case s2i("lock_system_modes"):
set_lock_system_modes(atoi(value.c_str()));
break;
case s2i("refresh_dont_care"):
set_refresh_dont_care(atoi(value.c_str()));
break;
case s2i("keep_changes"):
set_keep_changes(atoi(value.c_str()));
break;
// Modeline generation options
case s2i("interlace"):
set_interlace(atoi(value.c_str()));
break;
case s2i("doublescan"):
set_doublescan(atoi(value.c_str()));
break;
case s2i("dotclock_min"):
{
double pclock_min = 0.0f;
sscanf(value.c_str(), "%lf", &pclock_min);
set_dotclock_min(pclock_min);
break;
}
case s2i("sync_refresh_tolerance"):
{
double refresh_tolerance = 0.0f;
sscanf(value.c_str(), "%lf", &refresh_tolerance);
set_refresh_tolerance(refresh_tolerance);
break;
}
case s2i("super_width"):
{
int super_width = 0;
sscanf(value.c_str(), "%d", &super_width);
set_super_width(super_width);
break;
}
case s2i("aspect"):
set_monitor_aspect(get_aspect(value.c_str()));
break;
case s2i("v_shift_correct"):
set_v_shift_correct(atoi(value.c_str()));
break;
case s2i("pixel_precision"):
set_pixel_precision(atoi(value.c_str()));
break;
case s2i("interlace_force_even"):
set_interlace_force_even(atoi(value.c_str()));
break;
// Custom video backend options
case s2i("screen_compositing"):
set_screen_compositing(atoi(value.c_str()));
break;
case s2i("screen_reordering"):
set_screen_reordering(atoi(value.c_str()));
break;
case s2i("allow_hardware_refresh"):
set_allow_hardware_refresh(atoi(value.c_str()));
break;
case s2i("custom_timing"):
set_custom_timing(value.c_str());
break;
// Various
case s2i("verbosity"):
{
int verbosity_level = 1;
sscanf(value.c_str(), "%d", &verbosity_level);
set_log_level(verbosity_level);
break;
}
default:
log_error("Invalid option %s\n", key.c_str());
break;
}
}
set_option(key.c_str(), value.c_str());
}
config_file.close();
return true;
}
//============================================================
// switchres_manager::get_aspect
// switchres_manager::set_option
//============================================================
double switchres_manager::get_aspect(const char* aspect)
void switchres_manager::set_option(const char* key, const char* value)
{
int num, den;
if (sscanf(aspect, "%d:%d", &num, &den) == 2)
switch (s2i(key))
{
if (den == 0)
// Switchres options
case s2i("verbose"):
if (atoi(value)) set_log_verbose_fn((void*)printf);
break;
case s2i("monitor"):
display()->set_monitor(value);
break;
case s2i("crt_range0"):
display()->set_crt_range(0, value);
break;
case s2i("crt_range1"):
display()->set_crt_range(1, value);
break;
case s2i("crt_range2"):
display()->set_crt_range(2, value);
break;
case s2i("crt_range3"):
display()->set_crt_range(3, value);
break;
case s2i("crt_range4"):
display()->set_crt_range(4, value);
break;
case s2i("crt_range5"):
display()->set_crt_range(5, value);
break;
case s2i("crt_range6"):
display()->set_crt_range(6, value);
break;
case s2i("crt_range7"):
display()->set_crt_range(7, value);
break;
case s2i("crt_range8"):
display()->set_crt_range(8, value);
break;
case s2i("crt_range9"):
display()->set_crt_range(9, value);
break;
case s2i("lcd_range"):
display()->set_lcd_range(value);
break;
case s2i("modeline"):
display()->set_modeline(value);
break;
case s2i("user_mode"):
{
log_error("Error: denominator can't be zero\n");
return STANDARD_CRT_ASPECT;
modeline user_mode = {};
if (strcmp(value, "auto"))
{
if (sscanf(value, "%dx%d@%d", &user_mode.width, &user_mode.height, &user_mode.refresh) < 1)
{
log_error("Error: use format resolution <w>x<h>@<r>\n");
break;
}
}
display()->set_user_mode(&user_mode);
break;
}
return (double(num)/double(den));
}
log_error("Error: use format --aspect <num:den>\n");
return STANDARD_CRT_ASPECT;
// Display options
case s2i("display"):
display()->set_screen(value);
break;
case s2i("api"):
display()->set_api(value);
break;
case s2i("modeline_generation"):
display()->set_modeline_generation(atoi(value));
break;
case s2i("lock_unsupported_modes"):
display()->set_lock_unsupported_modes(atoi(value));
break;
case s2i("lock_system_modes"):
display()->set_lock_system_modes(atoi(value));
break;
case s2i("refresh_dont_care"):
display()->set_refresh_dont_care(atoi(value));
break;
case s2i("keep_changes"):
display()->set_keep_changes(atoi(value));
break;
// Modeline generation options
case s2i("interlace"):
display()->set_interlace(atoi(value));
break;
case s2i("doublescan"):
display()->set_doublescan(atoi(value));
break;
case s2i("dotclock_min"):
{
double pclock_min = 0.0f;
sscanf(value, "%lf", &pclock_min);
display()->set_dotclock_min(pclock_min);
break;
}
case s2i("sync_refresh_tolerance"):
{
double refresh_tolerance = 0.0f;
sscanf(value, "%lf", &refresh_tolerance);
display()->set_refresh_tolerance(refresh_tolerance);
break;
}
case s2i("super_width"):
{
int super_width = 0;
sscanf(value, "%d", &super_width);
display()->set_super_width(super_width);
break;
}
case s2i("aspect"):
display()->set_monitor_aspect(value);
break;
case s2i("h_size"):
{
double h_size = 1.0f;
sscanf(value, "%lf", &h_size);
display()->set_h_size(h_size);
break;
}
case s2i("h_shift"):
{
int h_shift = 0;
sscanf(value, "%d", &h_shift);
display()->set_h_shift(h_shift);
break;
}
case s2i("v_shift"):
{
int v_shift = 0;
sscanf(value, "%d", &v_shift);
display()->set_v_shift(v_shift);
break;
}
case s2i("v_shift_correct"):
display()->set_v_shift_correct(atoi(value));
break;
case s2i("pixel_precision"):
display()->set_pixel_precision(atoi(value));
break;
case s2i("interlace_force_even"):
display()->set_interlace_force_even(atoi(value));
break;
// Custom video backend options
case s2i("screen_compositing"):
display()->set_screen_compositing(atoi(value));
break;
case s2i("screen_reordering"):
display()->set_screen_reordering(atoi(value));
break;
case s2i("allow_hardware_refresh"):
display()->set_allow_hardware_refresh(atoi(value));
break;
case s2i("custom_timing"):
display()->set_custom_timing(value);
break;
// Various
case s2i("verbosity"):
{
int verbosity_level = 1;
sscanf(value, "%d", &verbosity_level);
set_log_level(verbosity_level);
break;
}
default:
log_error("Invalid option %s\n", key);
break;
}
}
//============================================================
// switchres_manager::set_current_display
//============================================================
void switchres_manager::set_current_display(int index)
{
int disp_index;
if (index == -1)
{
m_current_display = m_display_factory;
return;
}
else if (index < 0 || index >= (int)displays.size())
disp_index = 0;
else
disp_index = index;
m_current_display = displays[disp_index];
}

View File

@ -26,16 +26,9 @@
// CONSTANTS
//============================================================
#define SWITCHRES_VERSION "2.002"
//============================================================
// TYPE DEFINITIONS
//============================================================
typedef struct config_settings
{
bool mode_switching;
} config_settings;
#ifndef SWITCHRES_VERSION
#define SWITCHRES_VERSION "2.1.0"
#endif
class switchres_manager
@ -46,8 +39,10 @@ public:
~switchres_manager();
// getters
display_manager *display() const { return displays[0]; }
display_manager *display() const { return m_current_display; }
display_manager *display(int i) const { return i < (int)displays.size()? displays[i] : nullptr; }
display_manager *display_factory() const { return m_display_factory; }
static char* get_version() { return (char*) SWITCHRES_VERSION; };
// setters (log manager)
void set_log_level(int log_level);
@ -55,55 +50,20 @@ public:
void set_log_info_fn(void *func_ptr);
void set_log_error_fn(void *func_ptr);
// setters (display manager)
void set_monitor(const char *preset) { strncpy(ds.monitor, preset, sizeof(ds.monitor)-1); }
void set_modeline(const char *modeline) { strncpy(ds.user_modeline, modeline, sizeof(ds.user_modeline)-1); }
void set_user_mode(modeline *user_mode) { ds.user_mode = *user_mode;}
void set_crt_range(int i, const char *range) { strncpy(ds.crt_range[i], range, sizeof(ds.crt_range[i])-1); }
void set_lcd_range(const char *range) { strncpy(ds.lcd_range, range, sizeof(ds.lcd_range)-1); }
void set_screen(const char *screen) { strncpy(ds.screen, screen, sizeof(ds.screen)-1); }
void set_api(const char *api) { strncpy(ds.api, api, sizeof(ds.api)-1); }
void set_modeline_generation(bool value) { ds.modeline_generation = value; }
void set_lock_unsupported_modes(bool value) { ds.lock_unsupported_modes = value; }
void set_lock_system_modes(bool value) { ds.lock_system_modes = value; }
void set_refresh_dont_care(bool value) { ds.refresh_dont_care = value; }
void set_keep_changes(bool value) { ds.keep_changes = value; }
// setters (modeline generator)
void set_interlace(bool value) { ds.gs.interlace = value; }
void set_doublescan(bool value) { ds.gs.doublescan = value; }
void set_dotclock_min(double value) { ds.gs.pclock_min = value * 1000000; }
void set_refresh_tolerance(double value) { ds.gs.refresh_tolerance = value; }
void set_super_width(int value) { ds.gs.super_width = value; }
void set_rotation(bool value) { ds.gs.rotation = value; }
void set_monitor_aspect(double value) { ds.gs.monitor_aspect = value; }
void set_monitor_aspect(const char* aspect) { set_monitor_aspect(get_aspect(aspect)); }
void set_v_shift_correct(int value) { ds.gs.v_shift_correct = value; }
void set_pixel_precision(int value) { ds.gs.pixel_precision = value; }
void set_interlace_force_even(int value) { ds.gs.interlace_force_even = value; }
// setters (custom_video backend)
void set_screen_compositing(bool value) { ds.vs.screen_compositing = value; }
void set_screen_reordering(bool value) { ds.vs.screen_reordering = value; }
void set_allow_hardware_refresh(bool value) { ds.vs.allow_hardware_refresh = value; }
void set_custom_timing(const char *custom_timing) { strncpy(ds.vs.custom_timing, custom_timing, sizeof(ds.vs.custom_timing)-1); }
void set_current_display(int index);
void set_option(const char* key, const char* value);
// interface
display_manager* add_display();
display_manager* add_display(bool parse_options = true);
bool parse_config(const char *file_name);
//settings
config_settings cs = {};
display_settings ds = {};
// display list
std::vector<display_manager *> displays;
private:
display_manager *m_display_factory = 0;
double get_aspect(const char* aspect);
display_manager *m_current_display = 0;
};

View File

@ -110,6 +110,11 @@
# [Experimental] Attempts to compensate consumer TVs vertical centering issues
v_shift_correct 0
# Apply geometry correction to calculated modelines
h_size 1.0
h_shift 0
v_shift 0
# Calculate horizontal borders with 1-pixel precision, instead of the default 8-pixels blocks that were required by old drivers.
# Greatly improves horizontal centering of video modes.
pixel_precision 1

41
deps/switchres/switchres_defines.h vendored Normal file
View File

@ -0,0 +1,41 @@
#define SR_DEFINES
#define SR_OPT_MONITOR "monitor"
#define SR_OPT_CRT_RANGE0 "crt_range0"
#define SR_OPT_CRT_RANGE1 "crt_range1"
#define SR_OPT_CRT_RANGE2 "crt_range2"
#define SR_OPT_CRT_RANGE3 "crt_range3"
#define SR_OPT_CRT_RANGE4 "crt_range4"
#define SR_OPT_CRT_RANGE5 "crt_range5"
#define SR_OPT_CRT_RANGE6 "crt_range6"
#define SR_OPT_CRT_RANGE7 "crt_range7"
#define SR_OPT_CRT_RANGE8 "crt_range8"
#define SR_OPT_CRT_RANGE9 "crt_range9"
#define SR_OPT_LCD_RANGE "lcd_range"
#define SR_OPT_MODELINE "modeline"
#define SR_OPT_USER_MODE "user_mode"
#define SR_OPT_DISPLAY "display"
#define SR_OPT_API "api"
#define SR_OPT_LOCK_UNSUPPORTED_MODES "lock_unsupported_modes"
#define SR_OPT_LOCK_SYSTEM_MODES "lock_system_modes"
#define SR_OPT_REFRESH_DONT_CARE "refresh_dont_care"
#define SR_OPT_KEEP_CHANGES "keep_changes"
#define SR_OPT_MODELINE_GENERATION "modeline_generation"
#define SR_OPT_INTERLACE "interlace"
#define SR_OPT_DOUBLESCAN "doublescan"
#define SR_OPT_DOTCLOCK_MIN "dotclock_min"
#define SR_OPT_SYNC_REFRESH_TOLERANCE "sync_refresh_tolerance"
#define SR_OPT_SUPER_WIDTH "super_width"
#define SR_OPT_ASPECT "aspect"
#define SR_OPT_V_SHIFT_CORRECT "v_shift_correct"
#define SR_OPT_H_SIZE "h_size"
#define SR_OPT_H_SHIFT "h_shift"
#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_SCREEN_COMPOSITING "screen_compositing"
#define SR_OPT_SCREEN_REORDERING "screen_reordering"
#define SR_OPT_ALLOW_HARDWARE_REFRESH "allow_hardware_refresh"
#define SR_OPT_CUSTOM_TIMING "custom_timing"
#define SR_OPT_VERBOSE "verbose"
#define SR_OPT_VERBOSITY "verbosity"

View File

@ -23,6 +23,10 @@ using namespace std;
int show_version();
int show_usage();
enum
{
OPT_MODELINE = 128
};
//============================================================
// main
@ -32,6 +36,7 @@ int main(int argc, char **argv)
{
switchres_manager switchres;
display_manager* df = switchres.display_factory();
switchres.parse_config("switchres.ini");
@ -50,8 +55,11 @@ int main(int argc, char **argv)
bool launch_flag = false;
bool force_flag = false;
bool interlaced_flag = false;
bool rotated_flag = false;
bool user_ini_flag = false;
bool keep_changes_flag = false;
bool geometry_flag = false;
int status_code = 0;
string ini_file;
string launch_command;
@ -75,11 +83,13 @@ int main(int argc, char **argv)
{"verbose", no_argument, 0, 'v'},
{"backend", required_argument, 0, 'b'},
{"keep", no_argument, 0, 'k'},
{"geometry", required_argument, 0, 'g'},
{"modeline", required_argument, 0, OPT_MODELINE},
{0, 0, 0, 0}
};
int option_index = 0;
int c = getopt_long(argc, argv, "vhcsl:m:a:erd:f:i:b:k", long_options, &option_index);
int c = getopt_long(argc, argv, "vhcsl:m:a:erd:f:i:b:kg:", long_options, &option_index);
if (c == -1)
break;
@ -92,6 +102,10 @@ 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);
@ -117,22 +131,22 @@ int main(int argc, char **argv)
break;
case 'm':
switchres.set_monitor(optarg);
df->set_monitor(optarg);
break;
case 'r':
switchres.set_rotation(true);
rotated_flag = true;
break;
case 'd':
// Add new display in multi-monitor case
if (index > 0) switchres.add_display();
index ++;
switchres.set_screen(optarg);
df->set_screen(optarg);
break;
case 'a':
switchres.set_monitor_aspect(optarg);
df->set_monitor_aspect(optarg);
break;
case 'e':
@ -151,12 +165,22 @@ int main(int argc, char **argv)
break;
case 'b':
switchres.set_api(optarg);
df->set_api(optarg);
break;
case 'k':
keep_changes_flag = true;
switchres.set_keep_changes(true);
df->set_keep_changes(true);
break;
case 'g':
double h_size; int h_shift, v_shift;
if (sscanf(optarg, "%lf:%d:%d", &h_size, &h_shift, &v_shift) < 3)
log_error("Error: use format --geometry <h_size>:<h_shift>:<v_shift>\n");
geometry_flag = true;
df->set_h_size(h_size);
df->set_h_shift(h_shift);
df->set_v_shift(v_shift);
break;
default:
@ -185,6 +209,12 @@ int main(int argc, char **argv)
height = atoi(argv[optind + 1]);
refresh = atof(argv[optind + 2]);
if (width <= 0 || height <= 0 || refresh <= 0.0f)
{
log_error("Error: wrong video mode request: %sx%s@%s\n", argv[optind], argv[optind + 1], argv[optind + 2]);
goto usage;
}
char scan_mode = argv[optind + 2][strlen(argv[optind + 2]) -1];
if (scan_mode == 'i')
interlaced_flag = true;
@ -193,6 +223,9 @@ int main(int argc, char **argv)
if (user_ini_flag)
switchres.parse_config(ini_file.c_str());
if (calculate_flag)
switchres.display()->set_screen("dummy");
switchres.add_display();
if (force_flag)
@ -208,21 +241,32 @@ int main(int argc, char **argv)
{
for (auto &display : switchres.displays)
{
modeline *mode = display->get_mode(width, height, refresh, interlaced_flag);
int flags = (interlaced_flag? SR_MODE_INTERLACED : 0) | (rotated_flag? SR_MODE_ROTATED : 0);
modeline *mode = display->get_mode(width, height, refresh, flags);
if (mode) display->flush_modes();
if (mode && geometry_flag)
{
monitor_range range = {};
modeline_to_monitor_range(&range, mode);
log_info("Adjusted geometry (%.3f:%d:%d) H: %.3f, %.3f, %.3f V: %.3f, %.3f, %.3f\n",
display->h_size(), display->h_shift(), display->v_shift(),
range.hfront_porch, range.hsync_pulse, range.hback_porch,
range.vfront_porch * 1000, range.vsync_pulse * 1000, range.vback_porch * 1000);
}
}
if (edid_flag)
{
edid_block edid = {};
modeline *mode = switchres.display()->best_mode();
modeline *mode = switchres.display()->selected_mode();
if (mode)
{
monitor_range *range = &switchres.display()->range[mode->range];
edid_from_modeline(mode, range, switchres.ds.monitor, &edid);
edid_from_modeline(mode, range, switchres.display()->monitor(), &edid);
char file_name[sizeof(switchres.ds.monitor) + 4];
sprintf(file_name, "%s.bin", switchres.ds.monitor);
char file_name[strlen(switchres.display()->monitor()) + 4];
sprintf(file_name, "%s.bin", switchres.display()->monitor());
FILE *file = fopen(file_name, "wb");
if (file)
@ -234,7 +278,7 @@ int main(int argc, char **argv)
}
}
if (switch_flag) for (auto &display : switchres.displays) display->set_mode(display->best_mode());
if (switch_flag) for (auto &display : switchres.displays) display->set_mode(display->selected_mode());
if (switch_flag && !launch_flag && !keep_changes_flag)
{
@ -244,12 +288,15 @@ int main(int argc, char **argv)
if (launch_flag)
{
int status_code = system(launch_command.c_str());
status_code = system(launch_command.c_str());
#ifdef __linux__
status_code = WEXITSTATUS(status_code);
#endif
log_info("Process exited with value %d\n", status_code);
}
}
return (0);
return (status_code);
usage:
show_usage();
@ -290,14 +337,16 @@ int show_usage()
" -s, --switch Switch to video mode\n"
" -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"
" -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"
" -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"
};
log_info("%s", usage);

View File

@ -7,7 +7,7 @@
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2021 Chris Kennedy, Antonio Giner,
Copyright 2010-2022 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
@ -22,204 +22,414 @@
extern "C" {
#endif
#define SR_ACTION_ADD 1<<0
#define SR_ACTION_GET_FROM_ID 1<<1
#define SR_ACTION_FLUSH 1<<2
#define SR_ACTION_SWITCH 1<<3
//============================================================
// PROTOTYPES
//============================================================
int sr_mode_internal(int width, int height, double refresh, int flags, sr_mode *srm, int action, const char *caller);
void modeline_to_sr_mode(modeline* m, sr_mode* srm);
//============================================================
// GLOBALS
//============================================================
// Switchres manager object
switchres_manager* swr;
MODULE_API void sr_init() {
//============================================================
// Start of Switchres API
//============================================================
//============================================================
// sr_init
//============================================================
MODULE_API void sr_init()
{
setlocale(LC_NUMERIC, "C");
swr = new switchres_manager;
swr->parse_config("switchres.ini");
}
MODULE_API void sr_load_ini(char* config) {
swr->parse_config(config);
swr->display()->m_ds = swr->ds;
swr->display()->parse_options();
//============================================================
// sr_get_version
//============================================================
MODULE_API char* sr_get_version() {
return switchres_manager::get_version();
}
//============================================================
// sr_deinit
//============================================================
MODULE_API unsigned char sr_init_disp(const char* scr) {
if (scr)
swr->set_screen(scr);
swr->add_display();
if (!swr->display()->init())
return 0;
return 1;
}
MODULE_API void sr_deinit() {
MODULE_API void sr_deinit()
{
delete swr;
}
MODULE_API void sr_set_monitor(const char *preset) {
swr->set_monitor(preset);
//============================================================
// sr_init_disp
//============================================================
MODULE_API int sr_init_disp(const char* screen, void* pfdata)
{
if (screen)
swr->display_factory()->set_screen(screen);
display_manager *disp = swr->add_display();
if (disp == nullptr)
{
log_error("%s: error, couldn't add a display\n", __FUNCTION__);
return -1;
}
if (!disp->init(pfdata))
{
log_error("%s: error, couldn't init the display\n", __FUNCTION__);
return -1;
}
return disp->index();
}
MODULE_API void sr_set_user_mode(int width, int height, int refresh) {
//============================================================
// sr_init_disp
//============================================================
MODULE_API void sr_set_disp(int index)
{
swr->set_current_display(index);
}
//============================================================
// sr_load_ini
//============================================================
MODULE_API void sr_load_ini(char* config)
{
swr->parse_config(config);
swr->display()->parse_options();
}
//============================================================
// sr_set_option
//============================================================
MODULE_API void sr_set_option(const char* key, const char* value)
{
swr->set_option(key, value);
}
//============================================================
// sr_get_state
//============================================================
MODULE_API void sr_get_state(sr_state *state)
{
if (state == nullptr)
return;
*state = {};
sprintf(state->monitor, "%s", swr->display()->monitor());
state->modeline_generation = swr->display()->modeline_generation();
state->desktop_is_rotated = swr->display()->desktop_is_rotated();
state->interlace = swr->display()->interlace();
state->doublescan = swr->display()->doublescan();
state->dotclock_min = swr->display()->dotclock_min();
state->refresh_tolerance = swr->display()->refresh_tolerance();
state->super_width = swr->display()->super_width();
state->monitor_aspect = swr->display()->monitor_aspect();
state->h_size = swr->display()->h_size();
state->h_shift = swr->display()->h_shift();
state->v_shift = swr->display()->v_shift();
state->pixel_precision = swr->display()->pixel_precision();
state->selected_mode = swr->display()->selected_mode() == nullptr? -1 : swr->display()->selected_mode()->id;
state->current_mode = swr->display()->current_mode() == nullptr? -1 : swr->display()->current_mode()->id;
}
//============================================================
// sr_set_monitor
//============================================================
MODULE_API void sr_set_monitor(const char *preset)
{
swr->display()->set_monitor(preset);
}
//============================================================
// sr_set_user_mode
//============================================================
MODULE_API void sr_set_user_mode(int width, int height, int refresh)
{
modeline user_mode = {};
user_mode.width = width;
user_mode.height = height;
user_mode.refresh = refresh;
swr->set_user_mode(&user_mode);
swr->display()->set_user_mode(&user_mode);
}
void disp_best_mode_to_sr_mode(display_manager* disp, sr_mode* srm)
//============================================================
// sr_get_mode
//============================================================
MODULE_API int sr_get_mode(int id, sr_mode *srm)
{
srm->width = disp->width();
srm->height = disp->height();
srm->refresh = disp->v_freq();
srm->is_refresh_off = (disp->is_refresh_off() ? 1 : 0);
srm->is_stretched = (disp->is_stretched() ? 1 : 0);
srm->x_scale = disp->x_scale();
srm->y_scale = disp->y_scale();
srm->interlace = (disp->is_interlaced() ? 105 : 0);
if (srm == nullptr)
return 0;
*srm = {};
srm->id = id;
return sr_mode_internal(0, 0, 0, 0, srm, SR_ACTION_GET_FROM_ID, __FUNCTION__);
}
bool sr_refresh_display(display_manager *disp)
//============================================================
// sr_add_mode
//============================================================
MODULE_API int sr_add_mode(int width, int height, double refresh, int flags, sr_mode *srm)
{
if (disp->is_mode_updated())
{
if (disp->update_mode(disp->best_mode()))
{
log_info("sr_refresh_display: mode was updated\n");
return true;
}
}
else if (disp->is_mode_new())
{
if (disp->add_mode(disp->best_mode()))
{
log_info("sr_refresh_display: mode was added\n");
return true;
}
}
else
{
log_info("sr_refresh_display: no refresh required\n");
return true;
}
log_error("sr_refresh_display: error refreshing display\n");
return false;
bool flush = !(flags & SR_MODE_DONT_FLUSH);
return sr_mode_internal(width, height, refresh, flags, srm, SR_ACTION_ADD | (flush? SR_ACTION_FLUSH: 0), __FUNCTION__);
}
MODULE_API unsigned char sr_add_mode(int width, int height, double refresh, unsigned char interlace, sr_mode *return_mode) {
//============================================================
// sr_flush
//============================================================
log_verbose("Inside sr_add_mode(%dx%d@%f%s)\n", width, height, refresh, interlace > 0? "i":"");
display_manager *disp = swr->display();
if (disp == nullptr)
{
log_error("sr_add_mode: error, didn't get a display\n");
return 0;
}
disp->get_mode(width, height, refresh, (interlace > 0? true : false));
if (disp->got_mode())
{
log_verbose("sr_add_mode: got mode %dx%d@%f type(%x)\n", disp->width(), disp->height(), disp->v_freq(), disp->best_mode()->type);
if (return_mode != nullptr) disp_best_mode_to_sr_mode(disp, return_mode);
if (sr_refresh_display(disp))
return 1;
}
printf("sr_add_mode: error adding mode\n");
return 0;
MODULE_API int sr_flush()
{
return sr_mode_internal(0, 0, 0, 0, 0, SR_ACTION_FLUSH, __FUNCTION__);
}
MODULE_API unsigned char sr_switch_to_mode(int width, int height, double refresh, unsigned char interlace, sr_mode *return_mode) {
//============================================================
// sr_switch_to_mode
//============================================================
log_verbose("Inside sr_switch_to_mode(%dx%d@%f%s)\n", width, height, refresh, interlace > 0? "i":"");
display_manager *disp = swr->display();
if (disp == nullptr)
{
log_error("sr_switch_to_mode: error, didn't get a display\n");
return 0;
}
disp->get_mode(width, height, refresh, (interlace > 0? true : false));
if (disp->got_mode())
{
log_verbose("sr_switch_to_mode: got mode %dx%d@%f type(%x)\n", disp->width(), disp->height(), disp->v_freq(), disp->best_mode()->type);
if (return_mode != nullptr) disp_best_mode_to_sr_mode(disp, return_mode);
if (!sr_refresh_display(disp))
return 0;
}
if (disp->is_switching_required())
{
if (disp->set_mode(disp->best_mode()))
{
log_info("sr_switch_to_mode: successfully switched to %dx%d@%f\n", disp->width(), disp->height(), disp->v_freq());
return 1;
}
}
else
{
log_info("sr_switch_to_mode: switching not required\n");
return 1;
}
log_error("sr_switch_to_mode: error switching to mode\n");
return 0;
MODULE_API int sr_switch_to_mode(int width, int height, double refresh, int flags, sr_mode *srm)
{
return sr_mode_internal(width, height, refresh, flags, srm, SR_ACTION_ADD | SR_ACTION_FLUSH | SR_ACTION_SWITCH, __FUNCTION__);
}
MODULE_API void sr_set_rotation (unsigned char r) {
if (r > 0)
{
swr->set_rotation(true);
}
else
{
swr->set_rotation(false);
}
//============================================================
// sr_set_mode
//============================================================
MODULE_API int sr_set_mode(int id)
{
sr_mode srm = {};
srm.id = id;
return sr_mode_internal(0, 0, 0, 0, &srm, SR_ACTION_GET_FROM_ID | SR_ACTION_SWITCH, __FUNCTION__);
}
MODULE_API void sr_set_log_level (int l) {
//============================================================
// sr_set_log_level
//============================================================
MODULE_API void sr_set_log_level(int l)
{
swr->set_log_level(l);
}
MODULE_API void sr_set_log_callback_info (void * f) {
//============================================================
// sr_set_log_callbacks
//============================================================
MODULE_API void sr_set_log_callback_info(void * f)
{
swr->set_log_info_fn((void *)f);
}
MODULE_API void sr_set_log_callback_debug (void * f) {
MODULE_API void sr_set_log_callback_debug(void * f)
{
swr->set_log_verbose_fn((void *)f);
}
MODULE_API void sr_set_log_callback_error (void * f) {
MODULE_API void sr_set_log_callback_error(void * f)
{
swr->set_log_error_fn((void *)f);
}
MODULE_API srAPI srlib = {
//============================================================
// srlib
//============================================================
MODULE_API srAPI srlib =
{
sr_init,
sr_load_ini,
sr_get_version,
sr_deinit,
sr_init_disp,
sr_set_disp,
sr_get_mode,
sr_add_mode,
sr_switch_to_mode,
sr_flush,
sr_set_mode,
sr_set_monitor,
sr_set_rotation,
sr_set_user_mode,
sr_set_option,
sr_get_state,
sr_set_log_level,
sr_set_log_callback_error,
sr_set_log_callback_info,
sr_set_log_callback_debug,
};
//============================================================
// End of Switchres API
//============================================================
//============================================================
// sr_mode_internal
//============================================================
int sr_mode_internal(int width, int height, double refresh, int flags, sr_mode *srm, int action, const char *caller)
{
display_manager *disp = swr->display();
if (disp == nullptr)
{
log_error("%s: error, didn't get a display\n", caller);
return 0;
}
if (action & SR_ACTION_ADD)
{
if (srm == nullptr)
{
log_error("%s: error, invalid sr_mode pointer\n", caller);
return 0;
}
disp->get_mode(width, height, refresh, flags);
if (disp->got_mode())
{
log_verbose("%s: got mode %dx%d@%f type(%x)\n", caller, disp->width(), disp->height(), disp->v_freq(), disp->selected_mode()->type);
modeline_to_sr_mode(disp->selected_mode(), srm);
}
else
{
log_error("%s: error getting mode\n", caller);
return 0;
}
}
else if (action & SR_ACTION_GET_FROM_ID)
{
bool found = false;
for (auto mode : disp->video_modes)
{
if (mode.id == srm->id)
{
found = true;
disp->set_selected_mode(&mode);
log_verbose("%s: got mode %dx%d@%f type(%x)\n", caller, disp->width(), disp->height(), disp->v_freq(), disp->selected_mode()->type);
modeline_to_sr_mode(disp->selected_mode(), srm);
break;
}
}
if (!found)
{
log_error("%s: mode ID %d not found\n", caller, srm->id);
return 0;
}
}
if (action & SR_ACTION_FLUSH)
{
if (!disp->flush_modes())
{
log_error("%s: error flushing display\n", caller);
return 0;
}
}
if (action & SR_ACTION_SWITCH)
{
if (disp->is_switching_required() || disp->current_mode() != disp->selected_mode())
{
if (disp->set_mode(disp->selected_mode()))
log_info("%s: successfully switched to %dx%d@%f\n", caller, disp->width(), disp->height(), disp->v_freq());
else
{
log_error("%s: error switching to %dx%d@%f\n", caller, disp->width(), disp->height(), disp->v_freq());
return 0;
}
}
else
log_info("%s: switching not required\n", caller);
}
return 1;
}
//============================================================
// modeline_to_sr_mode
//============================================================
void modeline_to_sr_mode(modeline* m, sr_mode* srm)
{
srm->width = m->hactive;
srm->height = m->vactive;
srm->refresh = m->refresh;
//
srm->vfreq = m->vfreq;
srm->hfreq = m->hfreq;
//
srm->pclock = m->pclock;
srm->hbegin = m->hbegin;
srm->hend = m->hend;
srm->htotal = m->htotal;
srm->vbegin = m->vbegin;
srm->vend = m->vend;
srm->vtotal = m->vtotal;
srm->interlace = m->interlace;
srm->doublescan = m->doublescan;
srm->hsync = m->hsync;
srm->vsync = m->vsync;
//
srm->is_refresh_off = m->result.weight & R_V_FREQ_OFF ? 1 : 0;
srm->is_stretched = m->result.weight & R_RES_STRETCH ? 1 : 0;
srm->x_scale = m->result.x_scale;
srm->y_scale = m->result.y_scale;
srm->v_scale = m->result.v_scale;
srm->id = m->id;
}
#ifdef __cplusplus
}
#endif

View File

@ -12,6 +12,11 @@
**************************************************************/
#include <stdint.h>
#ifndef SR_DEFINES
#include "switchres_defines.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
@ -74,52 +79,109 @@ char* LIBERROR()
#define LIBSWR "libswitchres.dll"
#endif
/* Mode flags */
#define SR_MODE_INTERLACED 1<<0
#define SR_MODE_ROTATED 1<<1
#define SR_MODE_DONT_FLUSH 1<<16
/* That's all the exposed data from Switchres calculation */
typedef struct MODULE_API {
int width;
int height;
double refresh;
unsigned char is_refresh_off;
unsigned char is_stretched;
int x_scale;
int y_scale;
unsigned char interlace;
typedef struct MODULE_API
{
int width;
int height;
int refresh;
//
double vfreq;
double hfreq;
//
uint64_t pclock;
int hbegin;
int hend;
int htotal;
int vbegin;
int vend;
int vtotal;
int interlace;
int doublescan;
int hsync;
int vsync;
//
int is_refresh_off;
int is_stretched;
double x_scale;
double y_scale;
double v_scale;
int id;
} sr_mode;
/* Used to retrieve SR settings and state */
typedef struct MODULE_API
{
char monitor[32];
int modeline_generation;
int desktop_is_rotated;
int interlace;
int doublescan;
double dotclock_min;
double refresh_tolerance;
int super_width;
double monitor_aspect;
double h_size;
double h_shift;
double v_shift;
int pixel_precision;
int selected_mode;
int current_mode;
} sr_state;
/* Declaration of the wrapper functions */
MODULE_API void sr_init();
MODULE_API char* sr_get_version();
MODULE_API void sr_load_ini(char* config);
MODULE_API void sr_deinit();
MODULE_API unsigned char sr_init_disp(const char* src);
MODULE_API unsigned char sr_add_mode(int, int, double, unsigned char, sr_mode*);
MODULE_API unsigned char sr_switch_to_mode(int, int, double, unsigned char, sr_mode*);
MODULE_API int sr_init_disp(const char*, void*);
MODULE_API void sr_set_disp(int);
MODULE_API int sr_get_mode(int, sr_mode*);
MODULE_API int sr_add_mode(int, int, double, int, sr_mode*);
MODULE_API int sr_switch_to_mode(int, int, double, int, sr_mode*);
MODULE_API int sr_flush();
MODULE_API int sr_set_mode(int);
MODULE_API void sr_set_monitor(const char*);
MODULE_API void sr_set_rotation(unsigned char);
MODULE_API void sr_set_user_mode(int, int, int);
MODULE_API void sr_set_option(const char* key, const char* value);
MODULE_API void sr_get_state(sr_state *state);
/* Logging related functions */
MODULE_API void sr_set_log_level (int);
MODULE_API void sr_set_log_level(int);
MODULE_API void sr_set_log_callback_error(void *);
MODULE_API void sr_set_log_callback_info(void *);
MODULE_API void sr_set_log_callback_debug(void *);
/* Others */
MODULE_API void sr_set_sdl_window(void *);
/* Inspired by https://stackoverflow.com/a/1067684 */
typedef struct MODULE_API {
void (*init)(void);
void (*sr_sr_load_ini)(char*);
void (*deinit)(void);
unsigned char (*sr_init_disp)(const char*);
unsigned char (*sr_add_mode)(int, int, double, unsigned char, sr_mode*);
unsigned char (*sr_switch_to_mode)(int, int, double, unsigned char, sr_mode*);
void (*sr_set_monitor)(const char*);
void (*sr_set_rotation)(unsigned char);
void (*sr_set_user_mode)(int, int, int);
void (*sr_set_log_level) (int);
void (*sr_set_log_callback_error)(void *);
void (*sr_set_log_callback_info)(void *);
void (*sr_set_log_callback_debug)(void *);
typedef struct MODULE_API
{
void (*init)(void);
void (*load_ini)(char*);
char* (*sr_get_version)(void);
void (*deinit)(void);
int (*init_disp)(const char*, void*);
void (*set_disp)(int);
int (*get_mode)(int, sr_mode*);
int (*add_mode)(int, int, double, int, sr_mode*);
int (*switch_to_mode)(int, int, double, int, sr_mode*);
int (*flush)(void);
int (*set_mode)(int);
void (*set_monitor)(const char*);
void (*set_user_mode)(int, int, int);
void (*set_option)(const char*, const char*);
void (*get_state)(sr_state*);
void (*set_log_level) (int);
void (*set_log_callback_error)(void *);
void (*set_log_callback_info)(void *);
void (*set_log_callback_debug)(void *);
} srAPI;

47
deps/switchres/tests/test_dlopen.cpp vendored Normal file
View File

@ -0,0 +1,47 @@
#include <stdio.h>
#include <stdlib.h>
#ifdef __cplusplus
#include <cstring> // required for strcpy
#endif
#ifdef __linux__
#define LIBSWR "libswitchres.so"
#elif _WIN32
#define LIBSWR "libswitchres.dll"
#endif
#include <switchres/switchres_wrapper.h>
int main(int argc, char** argv) {
const char* err_msg;
printf("About to open %s.\n", LIBSWR);
// Load the lib
LIBTYPE dlp = OPENLIB(LIBSWR);
// Loading failed, inform and exit
if (!dlp) {
printf("Loading %s failed.\n", LIBSWR);
printf("Error: %s\n", LIBERROR());
exit(EXIT_FAILURE);
}
printf("Loading %s succeded.\n", LIBSWR);
// Load the init()
LIBERROR();
srAPI* SRobj = (srAPI*)LIBFUNC(dlp, "srlib");
if ((err_msg = LIBERROR()) != NULL) {
printf("Failed to load srAPI: %s\n", err_msg);
CLOSELIB(dlp);
exit(EXIT_FAILURE);
}
// Testing the function
printf("Switchres version: %s\n" , SRobj->sr_get_version());
// We're done, let's closer
CLOSELIB(dlp);
}

7
deps/switchres/tests/test_liblink.cpp vendored Normal file
View File

@ -0,0 +1,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <switchres/switchres_wrapper.h>
int main(int argc, char** argv) {
printf("Switchres version: %s\n" , sr_get_version());
}

BIN
deps/switchres/tv.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@ -113,376 +113,28 @@ typedef struct hdmi_timings
static enum gfx_ctx_api drm_api = GFX_CTX_NONE;
static drmModeModeInfo gfx_ctx_crt_switch_mode;
static bool switch_mode = false;
/* Load custom HDMI timings from config */
static bool gfx_ctx_drm_load_mode(drmModeModeInfoPtr modeInfo)
static float mode_vrefresh(drmModeModeInfo *mode)
{
settings_t *settings = config_get_ptr();
char *crt_switch_timings = settings->arrays.crt_switch_timings;
if (modeInfo && !string_is_empty(crt_switch_timings))
{
hdmi_timings_t timings;
int ret = sscanf(crt_switch_timings, "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
&timings.h_active_pixels, &timings.h_sync_polarity, &timings.h_front_porch,
&timings.h_sync_pulse, &timings.h_back_porch,
&timings.v_active_lines, &timings.v_sync_polarity, &timings.v_front_porch,
&timings.v_sync_pulse, &timings.v_back_porch,
&timings.v_sync_offset_a, &timings.v_sync_offset_b, &timings.pixel_rep, &timings.frame_rate,
&timings.interlaced, &timings.pixel_freq, &timings.aspect_ratio);
if (ret != 17)
{
RARCH_ERR("[DRM]: malformed mode requested: %s\n", crt_switch_timings);
return false;
}
memset(modeInfo, 0, sizeof(drmModeModeInfo));
modeInfo->clock = timings.pixel_freq / 1000;
modeInfo->hdisplay = timings.h_active_pixels;
modeInfo->hsync_start = modeInfo->hdisplay + timings.h_front_porch;
modeInfo->hsync_end = modeInfo->hsync_start + timings.h_sync_pulse;
modeInfo->htotal = modeInfo->hsync_end + timings.h_back_porch;
modeInfo->hskew = 0;
modeInfo->vdisplay = timings.v_active_lines;
modeInfo->vsync_start = modeInfo->vdisplay + (timings.v_front_porch * (timings.interlaced ? 2 : 1));
modeInfo->vsync_end = modeInfo->vsync_start + (timings.v_sync_pulse * (timings.interlaced ? 2 : 1));
modeInfo->vtotal = modeInfo->vsync_end + (timings.v_back_porch * (timings.interlaced ? 2 : 1));
modeInfo->vscan = 0; /* TODO: ?? */
modeInfo->vrefresh = timings.frame_rate;
modeInfo->flags = timings.interlaced ? DRM_MODE_FLAG_INTERLACE : 0;
modeInfo->flags |= timings.v_sync_polarity ? DRM_MODE_FLAG_NVSYNC : DRM_MODE_FLAG_PVSYNC;
modeInfo->flags |= timings.h_sync_polarity ? DRM_MODE_FLAG_NHSYNC : DRM_MODE_FLAG_PHSYNC;
modeInfo->type = 0;
snprintf(modeInfo->name, DRM_DISPLAY_MODE_LEN, "CRT_%ux%u_%u",
modeInfo->hdisplay, modeInfo->vdisplay, modeInfo->vrefresh);
return true;
}
return false;
return mode->clock * 1000.00 / (mode->htotal * mode->vtotal);
}
static void drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
static void dump_mode(drmModeModeInfo *mode, int index)
{
struct drm_fb *fb = (struct drm_fb*)data;
if (fb && fb->fb_id)
drmModeRmFB(g_drm_fd, fb->fb_id);
free(fb);
}
static struct drm_fb *drm_fb_get_from_bo(struct gbm_bo *bo)
{
int ret;
unsigned width, height, stride, handle;
struct drm_fb *fb = (struct drm_fb*)calloc(1, sizeof(*fb));
fb->bo = bo;
width = gbm_bo_get_width(bo);
height = gbm_bo_get_height(bo);
stride = gbm_bo_get_stride(bo);
handle = gbm_bo_get_handle(bo).u32;
RARCH_LOG("[KMS]: New FB: %ux%u (stride: %u).\n",
width, height, stride);
ret = drmModeAddFB(g_drm_fd, width, height, 24, 32,
stride, handle, &fb->fb_id);
if (ret < 0)
goto error;
gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
return fb;
error:
RARCH_ERR("[KMS]: Failed to create FB.\n");
free(fb);
return NULL;
}
static void gfx_ctx_drm_swap_interval(void *data, int interval)
{
gfx_ctx_drm_data_t *drm = (gfx_ctx_drm_data_t*)data;
drm->interval = interval;
if (interval > 1)
RARCH_WARN("[KMS]: Swap intervals > 1 currently not supported. Will use swap interval of 1.\n");
}
static void gfx_ctx_drm_check_window(void *data, bool *quit,
bool *resize, unsigned *width, unsigned *height)
{
*resize = false;
*quit = (bool)frontend_driver_get_signal_handler_state();
}
static void drm_flip_handler(int fd, unsigned frame,
unsigned sec, unsigned usec, void *data)
{
#if 0
static unsigned first_page_flip;
static unsigned last_page_flip;
if (!first_page_flip)
first_page_flip = frame;
if (last_page_flip)
{
unsigned missed = frame - last_page_flip - 1;
if (missed)
RARCH_LOG("[KMS]: Missed %u VBlank(s) (Frame: %u, DRM frame: %u).\n",
missed, frame - first_page_flip, frame);
}
last_page_flip = frame;
#endif
*(bool*)data = false;
}
static bool gfx_ctx_drm_wait_flip(gfx_ctx_drm_data_t *drm, bool block)
{
int timeout = 0;
if (!drm->waiting_for_flip)
return false;
if (block)
timeout = -1;
while (drm->waiting_for_flip)
{
if (!drm_wait_flip(timeout))
break;
}
if (drm->waiting_for_flip)
return true;
/* Page flip has taken place. */
/* This buffer is not on-screen anymore. Release it to GBM. */
gbm_surface_release_buffer(drm->gbm_surface, drm->bo);
/* This buffer is being shown now. */
drm->bo = drm->next_bo;
return false;
}
static bool gfx_ctx_drm_queue_flip(gfx_ctx_drm_data_t *drm)
{
struct drm_fb *fb = NULL;
drm->next_bo = gbm_surface_lock_front_buffer(drm->gbm_surface);
fb = (struct drm_fb*)gbm_bo_get_user_data(drm->next_bo);
if (!fb)
fb = (struct drm_fb*)drm_fb_get_from_bo(drm->next_bo);
if (drmModePageFlip(g_drm_fd, g_crtc_id, fb->fb_id,
DRM_MODE_PAGE_FLIP_EVENT, &drm->waiting_for_flip) == 0)
return true;
/* Failed to queue page flip. */
return false;
}
static void gfx_ctx_drm_swap_buffers(void *data)
{
gfx_ctx_drm_data_t *drm = (gfx_ctx_drm_data_t*)data;
settings_t *settings = config_get_ptr();
unsigned max_swapchain_images = settings->uints.video_max_swapchain_images;
#ifdef HAVE_EGL
egl_swap_buffers(&drm->egl);
#endif
/* I guess we have to wait for flip to have taken
* place before another flip can be queued up.
*
* If true, we are still waiting for a flip
* (nonblocking mode, so just drop the frame). */
if (gfx_ctx_drm_wait_flip(drm, drm->interval))
return;
drm->waiting_for_flip = gfx_ctx_drm_queue_flip(drm);
/* Triple-buffered page flips */
if (max_swapchain_images >= 3 &&
gbm_surface_has_free_buffers(drm->gbm_surface))
return;
gfx_ctx_drm_wait_flip(drm, true);
}
static void gfx_ctx_drm_get_video_size(void *data,
unsigned *width, unsigned *height)
{
gfx_ctx_drm_data_t *drm = (gfx_ctx_drm_data_t*)data;
if (!drm)
return;
*width = drm->fb_width;
*height = drm->fb_height;
}
static void free_drm_resources(gfx_ctx_drm_data_t *drm)
{
if (!drm)
return;
/* Restore original CRTC. */
drm_restore_crtc();
if (drm->gbm_surface)
gbm_surface_destroy(drm->gbm_surface);
if (drm->gbm_dev)
gbm_device_destroy(drm->gbm_dev);
drm_free();
if (drm->fd >= 0)
{
if (g_drm_fd >= 0)
{
drmDropMaster(g_drm_fd);
close(drm->fd);
}
}
drm->gbm_surface = NULL;
drm->gbm_dev = NULL;
g_drm_fd = -1;
}
static void gfx_ctx_drm_destroy_resources(gfx_ctx_drm_data_t *drm)
{
if (!drm)
return;
/* Make sure we acknowledge all page-flips. */
gfx_ctx_drm_wait_flip(drm, true);
#ifdef HAVE_EGL
egl_destroy(&drm->egl);
#endif
free_drm_resources(drm);
g_drm_mode = NULL;
g_crtc_id = 0;
g_connector_id = 0;
drm->fb_width = 0;
drm->fb_height = 0;
drm->bo = NULL;
drm->next_bo = NULL;
}
static void *gfx_ctx_drm_init(void *video_driver)
{
int fd, i;
unsigned monitor_index;
unsigned gpu_index = 0;
const char *gpu = NULL;
struct string_list *gpu_descriptors = NULL;
settings_t *settings = config_get_ptr();
gfx_ctx_drm_data_t *drm = (gfx_ctx_drm_data_t*)
calloc(1, sizeof(gfx_ctx_drm_data_t));
unsigned video_monitor_index = settings->uints.video_monitor_index;
if (!drm)
return NULL;
drm->fd = -1;
gpu_descriptors = dir_list_new("/dev/dri", NULL, false, true, false, false);
nextgpu:
free_drm_resources(drm);
if (!gpu_descriptors || gpu_index == gpu_descriptors->size)
{
RARCH_ERR("[KMS]: Couldn't find a suitable DRM device.\n");
goto error;
}
gpu = gpu_descriptors->elems[gpu_index++].data;
drm->fd = open(gpu, O_RDWR);
if (drm->fd < 0)
{
RARCH_WARN("[KMS]: Couldn't open DRM device.\n");
goto nextgpu;
}
fd = drm->fd;
if (!drm_get_resources(fd))
goto nextgpu;
if (!drm_get_connector(fd, video_monitor_index))
goto nextgpu;
if (!drm_get_encoder(fd))
goto nextgpu;
drm_setup(fd);
/* Choose the optimal video mode for get_video_size():
- custom timings from configuration
- else the current video mode from the CRTC
- otherwise pick first connector mode */
if (gfx_ctx_drm_load_mode(&gfx_ctx_crt_switch_mode))
{
drm->fb_width = gfx_ctx_crt_switch_mode.hdisplay;
drm->fb_height = gfx_ctx_crt_switch_mode.vdisplay;
}
else if (g_orig_crtc->mode_valid)
{
drm->fb_width = g_orig_crtc->mode.hdisplay;
drm->fb_height = g_orig_crtc->mode.vdisplay;
}
else
{
drm->fb_width = g_drm_connector->modes[0].hdisplay;
drm->fb_height = g_drm_connector->modes[0].vdisplay;
}
drmSetMaster(g_drm_fd);
drm->gbm_dev = gbm_create_device(fd);
if (!drm->gbm_dev)
{
RARCH_WARN("[KMS]: Couldn't create GBM device.\n");
goto nextgpu;
}
dir_list_free(gpu_descriptors);
/* Setup the flip handler. */
g_drm_fds.fd = fd;
g_drm_fds.events = POLLIN;
g_drm_evctx.version = DRM_EVENT_CONTEXT_VERSION;
g_drm_evctx.page_flip_handler = drm_flip_handler;
g_drm_fd = fd;
return drm;
error:
dir_list_free(gpu_descriptors);
gfx_ctx_drm_destroy_resources(drm);
if (drm)
free(drm);
return NULL;
RARCH_LOG("Mode details: #%i %s %.2f %d %d %d %d %d %d %d %d %d\n",
index,
mode->name,
mode_vrefresh(mode),
mode->hdisplay,
mode->hsync_start,
mode->hsync_end,
mode->htotal,
mode->vdisplay,
mode->vsync_start,
mode->vsync_end,
mode->vtotal,
mode->clock);
}
static EGLint *gfx_ctx_drm_egl_fill_attribs(
@ -687,6 +339,463 @@ error:
}
#endif
/* Get the mode from video_state */
bool gfx_ctx_drm_get_mode_from_video_state(drmModeModeInfoPtr modeInfo)
{
video_driver_state_t *video_st = video_state_get_ptr();
if (video_st->crt_switch_st.vdisplay < 1)
{
return false;
}
modeInfo->clock = video_st->crt_switch_st.clock;
modeInfo->hdisplay = video_st->crt_switch_st.hdisplay;
modeInfo->hsync_start = video_st->crt_switch_st.hsync_start;
modeInfo->hsync_end = video_st->crt_switch_st.hsync_end;
modeInfo->htotal = video_st->crt_switch_st.htotal;
modeInfo->vdisplay = video_st->crt_switch_st.vdisplay;
modeInfo->vsync_start = video_st->crt_switch_st.vsync_start;
modeInfo->vsync_end = video_st->crt_switch_st.vsync_end;
modeInfo->vtotal = video_st->crt_switch_st.vtotal;
modeInfo->flags = (video_st->crt_switch_st.interlace ? DRM_MODE_FLAG_INTERLACE : 0)
| (video_st->crt_switch_st.doublescan ? DRM_MODE_FLAG_DBLSCAN : 0)
| (video_st->crt_switch_st.hsync ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC)
| (video_st->crt_switch_st.vsync ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC);
modeInfo->hskew = 0;
modeInfo->vscan = 0;
modeInfo->vrefresh = video_st->crt_switch_st.vrefresh;
modeInfo->type = DRM_MODE_TYPE_USERDEF;
snprintf(modeInfo->name, 45, "RetroArch_CRT-%dx%d@%.02f%s"
, video_st->crt_switch_st.hdisplay
, video_st->crt_switch_st.vdisplay
, mode_vrefresh(modeInfo)
, video_st->crt_switch_st.interlace ? "i" : "");
dump_mode(modeInfo, 0);
/* consider the mode read and removed */
video_st->crt_switch_st.vdisplay = 0;
return true;
}
/* Load custom hdmi timings from config */
static bool gfx_ctx_drm_load_mode(drmModeModeInfoPtr modeInfo)
{
settings_t *settings = config_get_ptr();
char *crt_switch_timings = settings->arrays.crt_switch_timings;
if(modeInfo && !string_is_empty(crt_switch_timings))
{
hdmi_timings_t timings;
int ret = sscanf(crt_switch_timings, "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
&timings.h_active_pixels, &timings.h_sync_polarity, &timings.h_front_porch,
&timings.h_sync_pulse, &timings.h_back_porch,
&timings.v_active_lines, &timings.v_sync_polarity, &timings.v_front_porch,
&timings.v_sync_pulse, &timings.v_back_porch,
&timings.v_sync_offset_a, &timings.v_sync_offset_b, &timings.pixel_rep, &timings.frame_rate,
&timings.interlaced, &timings.pixel_freq, &timings.aspect_ratio);
if (ret != 17)
{
RARCH_ERR("[DRM]: malformed mode requested: %s\n", crt_switch_timings);
return false;
}
memset(modeInfo, 0, sizeof(drmModeModeInfo));
modeInfo->clock = timings.pixel_freq / 1000;
modeInfo->hdisplay = timings.h_active_pixels;
modeInfo->hsync_start = modeInfo->hdisplay + timings.h_front_porch;
modeInfo->hsync_end = modeInfo->hsync_start + timings.h_sync_pulse;
modeInfo->htotal = modeInfo->hsync_end + timings.h_back_porch;
modeInfo->hskew = 0;
modeInfo->vdisplay = timings.v_active_lines;
modeInfo->vsync_start = modeInfo->vdisplay + (timings.v_front_porch * (timings.interlaced ? 2 : 1));
modeInfo->vsync_end = modeInfo->vsync_start + (timings.v_sync_pulse * (timings.interlaced ? 2 : 1));
modeInfo->vtotal = modeInfo->vsync_end + (timings.v_back_porch * (timings.interlaced ? 2 : 1));
modeInfo->vscan = 0; /* TODO: ?? */
modeInfo->vrefresh = timings.frame_rate;
modeInfo->flags = timings.interlaced ? DRM_MODE_FLAG_INTERLACE : 0;
modeInfo->flags |= timings.v_sync_polarity ? DRM_MODE_FLAG_NVSYNC : DRM_MODE_FLAG_PVSYNC;
modeInfo->flags |= timings.h_sync_polarity ? DRM_MODE_FLAG_NHSYNC : DRM_MODE_FLAG_PHSYNC;
modeInfo->type = 0;
snprintf(modeInfo->name, DRM_DISPLAY_MODE_LEN, "CRT_%ux%u_%u",
modeInfo->hdisplay, modeInfo->vdisplay, modeInfo->vrefresh);
return true;
}
return false;
}
static void drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
{
struct drm_fb *fb = (struct drm_fb*)data;
if (fb && fb->fb_id)
drmModeRmFB(g_drm_fd, fb->fb_id);
free(fb);
}
static struct drm_fb *drm_fb_get_from_bo(struct gbm_bo *bo)
{
int ret;
unsigned width, height, stride, handle;
struct drm_fb *fb = (struct drm_fb*)calloc(1, sizeof(*fb));
fb->bo = bo;
width = gbm_bo_get_width(bo);
height = gbm_bo_get_height(bo);
stride = gbm_bo_get_stride(bo);
handle = gbm_bo_get_handle(bo).u32;
RARCH_LOG("[KMS]: New FB: %ux%u (stride: %u).\n",
width, height, stride);
ret = drmModeAddFB(g_drm_fd, width, height, 24, 32,
stride, handle, &fb->fb_id);
if (ret < 0)
goto error;
gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
return fb;
error:
RARCH_ERR("[KMS]: Failed to create FB.\n");
free(fb);
return NULL;
}
static void gfx_ctx_drm_swap_interval(void *data, int interval)
{
gfx_ctx_drm_data_t *drm = (gfx_ctx_drm_data_t*)data;
drm->interval = interval;
if (interval > 1)
RARCH_WARN("[KMS]: Swap intervals > 1 currently not supported. Will use swap interval of 1.\n");
}
static void gfx_ctx_drm_check_window(void *data, bool *quit,
bool *resize, unsigned *width, unsigned *height)
{
*resize = false;
*quit = (bool)frontend_driver_get_signal_handler_state();
*width = g_drm_mode->hdisplay;
*height = g_drm_mode->vdisplay;
}
static void drm_flip_handler(int fd, unsigned frame,
unsigned sec, unsigned usec, void *data)
{
#if 0
static unsigned first_page_flip;
static unsigned last_page_flip;
if (!first_page_flip)
first_page_flip = frame;
if (last_page_flip)
{
unsigned missed = frame - last_page_flip - 1;
if (missed)
RARCH_LOG("[KMS]: Missed %u VBlank(s) (Frame: %u, DRM frame: %u).\n",
missed, frame - first_page_flip, frame);
}
last_page_flip = frame;
#endif
*(bool*)data = false;
}
static bool gfx_ctx_drm_wait_flip(gfx_ctx_drm_data_t *drm, bool block)
{
int timeout = 0;
if (!drm->waiting_for_flip)
return false;
if (block)
timeout = -1;
while (drm->waiting_for_flip)
{
if (!drm_wait_flip(timeout))
break;
}
if (drm->waiting_for_flip)
return true;
/* Page flip has taken place. */
/* This buffer is not on-screen anymore. Release it to GBM. */
gbm_surface_release_buffer(drm->gbm_surface, drm->bo);
/* This buffer is being shown now. */
drm->bo = drm->next_bo;
return false;
}
static bool gfx_ctx_drm_queue_flip(gfx_ctx_drm_data_t *drm)
{
struct drm_fb *fb = NULL;
drm->next_bo = gbm_surface_lock_front_buffer(drm->gbm_surface);
fb = (struct drm_fb*)gbm_bo_get_user_data(drm->next_bo);
if (!fb)
fb = (struct drm_fb*)drm_fb_get_from_bo(drm->next_bo);
if (switch_mode)
{
RARCH_DBG("[KMS]: modeswitch detected, creating the new CRTC\n");
drmModeSetCrtc(g_drm_fd, g_crtc_id, fb->fb_id, 0, 0, &g_connector_id, 1, g_drm_mode);
switch_mode = false;
}
if (drmModePageFlip(g_drm_fd, g_crtc_id, fb->fb_id,
DRM_MODE_PAGE_FLIP_EVENT, &drm->waiting_for_flip) == 0)
return true;
/* Failed to queue page flip. */
return false;
}
static void gfx_ctx_drm_swap_buffers(void *data)
{
gfx_ctx_drm_data_t *drm = (gfx_ctx_drm_data_t*)data;
settings_t *settings = config_get_ptr();
unsigned max_swapchain_images = settings->uints.video_max_swapchain_images;
/* Recreate the surface */
//*
if(switch_mode)
{
RARCH_DBG("[KMS]: modeswitch detected, doing GBM and EGL stuff\n");
if (drm->gbm_surface)
{
if (drm->bo)
gbm_surface_release_buffer(drm->gbm_surface, drm->bo);
if (drm->next_bo)
gbm_surface_release_buffer(drm->gbm_surface, drm->bo);
egl_ctx_data_t *egl = &drm->egl;
eglDestroySurface(egl->dpy, egl->surf);
gbm_surface_destroy(drm->gbm_surface);
}
drm->gbm_surface = gbm_surface_create(
drm->gbm_dev,
drm->fb_width,
drm->fb_height,
GBM_FORMAT_XRGB8888,
GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
if (!drm->gbm_surface)
{
RARCH_ERR("[KMS/EGL]: Couldn't create GBM surface.\n");
}
/* Creates an EGL surface and make it current */
egl_create_surface(&drm->egl, (EGLNativeWindowType)drm->gbm_surface);
egl_bind_hw_render(&drm->egl, true);
}
#ifdef HAVE_EGL
egl_swap_buffers(&drm->egl);
#endif
/* I guess we have to wait for flip to have taken
* place before another flip can be queued up.
*
* If true, we are still waiting for a flip
* (nonblocking mode, so just drop the frame). */
if (gfx_ctx_drm_wait_flip(drm, drm->interval))
return;
drm->waiting_for_flip = gfx_ctx_drm_queue_flip(drm);
/* Triple-buffered page flips */
if (max_swapchain_images >= 3 &&
gbm_surface_has_free_buffers(drm->gbm_surface))
return;
gfx_ctx_drm_wait_flip(drm, true);
}
static void gfx_ctx_drm_get_video_size(void *data,
unsigned *width, unsigned *height)
{
gfx_ctx_drm_data_t *drm = (gfx_ctx_drm_data_t*)data;
if (!drm)
return;
*width = drm->fb_width;
*height = drm->fb_height;
}
static void free_drm_resources(gfx_ctx_drm_data_t *drm)
{
if (!drm)
return;
/* Restore original CRTC. */
drm_restore_crtc();
if (drm->gbm_surface)
gbm_surface_destroy(drm->gbm_surface);
if (drm->gbm_dev)
gbm_device_destroy(drm->gbm_dev);
drm_free();
if (drm->fd >= 0)
{
if (g_drm_fd >= 0)
{
drmDropMaster(g_drm_fd);
close(drm->fd);
}
}
drm->gbm_surface = NULL;
drm->gbm_dev = NULL;
g_drm_fd = -1;
}
static void gfx_ctx_drm_destroy_resources(gfx_ctx_drm_data_t *drm)
{
if (!drm)
return;
/* Make sure we acknowledge all page-flips. */
gfx_ctx_drm_wait_flip(drm, true);
#ifdef HAVE_EGL
egl_destroy(&drm->egl);
#endif
free_drm_resources(drm);
g_drm_mode = NULL;
g_crtc_id = 0;
g_connector_id = 0;
drm->fb_width = 0;
drm->fb_height = 0;
drm->bo = NULL;
drm->next_bo = NULL;
}
static void *gfx_ctx_drm_init(void *video_driver)
{
int fd, i;
unsigned monitor_index;
unsigned gpu_index = 0;
const char *gpu = NULL;
struct string_list *gpu_descriptors = NULL;
settings_t *settings = config_get_ptr();
gfx_ctx_drm_data_t *drm = (gfx_ctx_drm_data_t*)
calloc(1, sizeof(gfx_ctx_drm_data_t));
unsigned video_monitor_index = settings->uints.video_monitor_index;
if (!drm)
return NULL;
drm->fd = -1;
gpu_descriptors = dir_list_new("/dev/dri", NULL, false, true, false, false);
nextgpu:
free_drm_resources(drm);
if (!gpu_descriptors || gpu_index == gpu_descriptors->size)
{
RARCH_ERR("[KMS]: Couldn't find a suitable DRM device.\n");
goto error;
}
gpu = gpu_descriptors->elems[gpu_index++].data;
drm->fd = open(gpu, O_RDWR);
if (drm->fd < 0)
{
RARCH_WARN("[KMS]: Couldn't open DRM device.\n");
goto nextgpu;
}
fd = drm->fd;
if (!drm_get_resources(fd))
goto nextgpu;
if (!drm_get_connector(fd, video_monitor_index))
goto nextgpu;
if (!drm_get_encoder(fd))
goto nextgpu;
drm_setup(fd);
/* Choose the optimal video mode for get_video_size():
- video mode issued by switchres through the CRT module
- custom timings from configuration
- else the current video mode from the CRTC
- otherwise pick first connector mode */
if (gfx_ctx_drm_get_mode_from_video_state(&gfx_ctx_crt_switch_mode))
{
drm->fb_width = gfx_ctx_crt_switch_mode.hdisplay;
drm->fb_height = gfx_ctx_crt_switch_mode.vdisplay;
}
else if (gfx_ctx_drm_load_mode(&gfx_ctx_crt_switch_mode))
{
drm->fb_width = gfx_ctx_crt_switch_mode.hdisplay;
drm->fb_height = gfx_ctx_crt_switch_mode.vdisplay;
}
else if (g_orig_crtc->mode_valid)
{
drm->fb_width = g_orig_crtc->mode.hdisplay;
drm->fb_height = g_orig_crtc->mode.vdisplay;
}
else
{
drm->fb_width = g_drm_connector->modes[0].hdisplay;
drm->fb_height = g_drm_connector->modes[0].vdisplay;
}
drmSetMaster(g_drm_fd);
drm->gbm_dev = gbm_create_device(fd);
if (!drm->gbm_dev)
{
RARCH_WARN("[KMS]: Couldn't create GBM device.\n");
goto nextgpu;
}
dir_list_free(gpu_descriptors);
/* Setup the flip handler. */
g_drm_fds.fd = fd;
g_drm_fds.events = POLLIN;
g_drm_evctx.version = DRM_EVENT_CONTEXT_VERSION;
g_drm_evctx.page_flip_handler = drm_flip_handler;
g_drm_fd = fd;
return drm;
error:
dir_list_free(gpu_descriptors);
gfx_ctx_drm_destroy_resources(drm);
if (drm)
free(drm);
return NULL;
}
static bool gfx_ctx_drm_set_video_mode(void *data,
unsigned width, unsigned height,
bool fullscreen)
@ -703,7 +812,6 @@ static bool gfx_ctx_drm_set_video_mode(void *data,
return false;
frontend_driver_install_signal_handler();
/* If we use black frame insertion,
* we fake a 60 Hz monitor for 120 Hz one,
* etc, so try to match that. */
@ -712,6 +820,16 @@ static bool gfx_ctx_drm_set_video_mode(void *data,
/* Find desired video mode, and use that.
* If not fullscreen, we get desired windowed size,
* which is not appropriate. */
if(gfx_ctx_drm_get_mode_from_video_state(&gfx_ctx_crt_switch_mode))
{
RARCH_DBG("[KMS]: New mode detected: %dx%d\n", gfx_ctx_crt_switch_mode.hdisplay, gfx_ctx_crt_switch_mode.vdisplay);
g_drm_mode = &gfx_ctx_crt_switch_mode;
drm->fb_width = gfx_ctx_crt_switch_mode.hdisplay;
drm->fb_height = gfx_ctx_crt_switch_mode.vdisplay;
switch_mode = true;
/* Let's exit, since modeswitching will happen while swapping buffers */
return true;
}
if ((width == 0 && height == 0) || !fullscreen)
g_drm_mode = &g_drm_connector->modes[0];
else
@ -732,6 +850,7 @@ static bool gfx_ctx_drm_set_video_mode(void *data,
drmModeModeInfo *mode = NULL;
float minimum_fps_diff = 0.0f;
float mode_vrefresh = 0.0f;
g_drm_mode = 0;
/* Find best match. */
for (i = 0; i < g_drm_connector->count_modes; i++)
@ -934,6 +1053,8 @@ static uint32_t gfx_ctx_drm_get_flags(void *data)
else
BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_GLSL);
BIT32_SET(flags, GFX_CTX_FLAGS_CRT_SWITCHRES);
return flags;
}

View File

@ -33,7 +33,7 @@
#include "../paths.h"
#include "gfx_display.h"
#if !defined(HAVE_VIDEOCORE)
#if !defined(HAVE_VIDEOCORE)
#include "../deps/switchres/switchres_wrapper.h"
static sr_mode srm;
#endif
@ -45,6 +45,7 @@ static sr_mode srm;
/* Forward declarations */
static void crt_check_hh_core(videocrt_switch_t *p_switch);
static void crt_adjust_sr_ini(videocrt_switch_t *p_switch);
static void get_modeline_for_kms(videocrt_switch_t *p_switch, sr_mode* srm);
/* Global local variables */
static bool ini_overrides_loaded = false;
@ -58,11 +59,12 @@ static void crt_rpi_switch(videocrt_switch_t *p_switch,int width, int height, fl
static bool crt_check_for_changes(videocrt_switch_t *p_switch)
{
if ((p_switch->ra_tmp_height != p_switch->ra_core_height) ||
(p_switch->ra_core_width != p_switch->ra_tmp_width) ||
(p_switch->center_adjust != p_switch->tmp_center_adjust||
p_switch->porch_adjust != p_switch->tmp_porch_adjust ) ||
(p_switch->ra_core_hz != p_switch->ra_tmp_core_hz))
if ((p_switch->ra_core_height != p_switch->ra_tmp_height) ||
(p_switch->ra_core_width != p_switch->ra_tmp_width) ||
(p_switch->center_adjust != p_switch->tmp_center_adjust||
p_switch->porch_adjust != p_switch->tmp_porch_adjust ) ||
(p_switch->ra_core_hz != p_switch->ra_tmp_core_hz) ||
(p_switch->rotated != p_switch->tmp_rotated))
return true;
return false;
}
@ -74,6 +76,7 @@ static void crt_store_temp_changes(videocrt_switch_t *p_switch)
p_switch->tmp_center_adjust = p_switch->center_adjust;
p_switch->tmp_porch_adjust = p_switch->porch_adjust;
p_switch->ra_tmp_core_hz = p_switch->ra_core_hz;
p_switch->tmp_rotated = p_switch->rotated;
}
static void switch_crt_hz(videocrt_switch_t *p_switch)
@ -93,11 +96,11 @@ static void crt_aspect_ratio_switch(
(float)p_switch->fly_aspect);
RARCH_LOG("[CRT]: Setting Video Screen Size to: %dx%d \n",
width, height);
video_driver_set_size(width , height);
video_driver_set_size(width , height);
video_driver_set_viewport(width , height,1,1);
video_driver_apply_state_changes();
}
static void set_aspect(videocrt_switch_t *p_switch,
@ -125,17 +128,10 @@ static void set_aspect(videocrt_switch_t *p_switch,
patched_height = height;
}
if (srm_width >= 1920)
{
srm_xscale = (float)srm_width/width;
RARCH_LOG("[CRT]: Super resolution detected. Fractal scaling @ X:%f Y:%d \n", srm_xscale, (int)srm_yscale);
}
if (srm_width >= 1920 && !srm_isstretched)
RARCH_LOG("[CRT]: Super resolution detected. Fractal scaling @ X:%f Y:%f \n", srm_xscale, srm_yscale);
else if (srm_isstretched && srm_width > 0 )
{
srm_xscale = (float)srm_width / width;
srm_yscale = (float)srm_height / height;
RARCH_LOG("[CRT]: Resolution is stretched. Fractal scaling @ X:%f Y:%f \n", srm_xscale, srm_yscale);
}
scaled_width = roundf(patched_width * srm_xscale);
scaled_height = roundf(patched_height * srm_yscale);
@ -143,26 +139,41 @@ static void set_aspect(videocrt_switch_t *p_switch,
crt_aspect_ratio_switch(p_switch, scaled_width, scaled_height, srm_width, srm_height);
}
#if !defined(HAVE_VIDEOCORE)
static bool is_kms_driver_context()
{
gfx_ctx_ident_t gfxctx;
video_context_driver_get_ident(&gfxctx);
RARCH_LOG("[CRT] Video context is: %s\n", gfxctx.ident);
if (strncmp(gfxctx.ident, "kms",3) == 0)
return true;
return false;
}
#if !defined(HAVE_VIDEOCORE)
static bool crt_sr2_init(videocrt_switch_t *p_switch,
int monitor_index, unsigned int crt_mode, unsigned int super_width)
{
const char* err_msg;
char* mode;
char index[10];
char ra_config_path[PATH_MAX_LENGTH];
char sr_ini_file[PATH_MAX_LENGTH];
if (monitor_index+1 >= 0 && monitor_index+1 < 10)
snprintf(index, sizeof(index), "%d", monitor_index);
else
strlcpy(index, "0", sizeof(index));
p_switch->kms_ctx = is_kms_driver_context();
if (!p_switch->sr2_active)
{
sr_init();
#if (__STDC_VERSION__ >= 199409L) /* no logs for C98 or less */
sr_set_log_callback_info(RARCH_LOG);
sr_set_log_callback_debug(RARCH_DBG);
sr_set_log_callback_error(RARCH_ERR);
sr_set_log_callback_info(RARCH_LOG);
sr_set_log_callback_debug(RARCH_DBG);
sr_set_log_callback_error(RARCH_ERR);
#endif
switch (crt_mode)
@ -189,35 +200,50 @@ static bool crt_sr2_init(videocrt_switch_t *p_switch,
if (super_width >2)
sr_set_user_mode(super_width, 0, 0);
if (monitor_index+1 > 0)
if (p_switch->kms_ctx)
p_switch->rtn = sr_init_disp("dummy", NULL);
else if (monitor_index+1 > 0)
{
RARCH_LOG("[CRT]: RA Monitor Index Manual: %s\n", &index[0]);
p_switch->rtn = sr_init_disp(index);
RARCH_LOG("[CRT]: SR Disp Monitor Index Manual: %s \n", &index[0]);
RARCH_LOG("[CRT]: Monitor Index Manual: %s\n", &index[0]);
p_switch->rtn = sr_init_disp(index, NULL);
}
if (monitor_index == -1)
else
{
RARCH_LOG("[CRT]: RA Monitor Index Auto: %s\n","auto");
p_switch->rtn = sr_init_disp("auto");
RARCH_LOG("[CRT]: SR Disp Monitor Index Auto: Auto \n");
RARCH_LOG("[CRT]: Monitor Index Auto: %s\n","auto");
p_switch->rtn = sr_init_disp("auto", NULL);
}
RARCH_LOG("[CRT]: SR rtn %d \n", p_switch->rtn);
if (p_switch->rtn == 1)
if (p_switch->rtn >= 0)
{
core_name[0] = '\0';
content_dir[0] = '\0';
/* For Lakka, check a switchres.ini next to user's retroarch.cfg */
fill_pathname_application_data(ra_config_path, PATH_MAX_LENGTH);
fill_pathname_join(sr_ini_file,
ra_config_path, "switchres.ini", sizeof(sr_ini_file));
if (path_is_valid(sr_ini_file))
{
RARCH_LOG("[CRT]: Loading switchres.ini override file from %s \n", sr_ini_file);
sr_load_ini(sr_ini_file);
}
}
}
if (p_switch->rtn == 1)
if (p_switch->rtn >= 0 && !p_switch->kms_ctx)
{
p_switch->sr2_active = true;
return true;
}
else if (p_switch->rtn >= 0 && p_switch->kms_ctx)
{
p_switch->sr2_active = true;
RARCH_LOG("[CRT]: KMS context detected, keeping SR alive\n");
return true;
}
RARCH_ERR("[CRT]: error at init, CRT modeswitching disabled\n");
sr_deinit();
p_switch->sr2_active = false;
@ -233,16 +259,16 @@ static void switch_res_crt(
{
char current_core_name[NAME_MAX_LENGTH];
char current_content_dir[PATH_MAX_LENGTH];
unsigned char interlace = 0, ret;
int flags = 0, ret;
const char *err_msg = NULL;
int w = native_width;
int h = height;
double rr = p_switch->ra_core_hz;
/* Check if SR2 is loaded, if not, load it */
if (crt_sr2_init(p_switch, monitor_index, crt_mode, super_width))
if (crt_sr2_init(p_switch, monitor_index, crt_mode, super_width))
{
/* Check for core and content changes in case we need
/* Check for core and content changes in case we need
to make any adjustments */
if (crt_switch_core_name())
strlcpy(current_core_name, crt_switch_core_name(),
@ -254,10 +280,10 @@ static void switch_res_crt(
path_get(RARCH_PATH_CONTENT),
sizeof(current_content_dir));
if ( !string_is_equal(core_name, current_core_name)
if ( !string_is_equal(core_name, current_core_name)
|| !string_is_equal(content_dir, current_content_dir))
{
/* A core or content change was detected,
/* A core or content change was detected,
we update the current values and make adjustments */
strlcpy(core_name, current_core_name, sizeof(core_name));
strlcpy(content_dir, current_content_dir, sizeof(content_dir));
@ -266,18 +292,37 @@ static void switch_res_crt(
crt_check_hh_core(p_switch);
}
if (!(ret = sr_switch_to_mode(w, h, rr, interlace, &srm)))
RARCH_LOG("[CRT]: SR failed to switch mode");
p_switch->sr_core_hz = srm.refresh;
if (p_switch->rotated)
flags |= SR_MODE_ROTATED;
set_aspect(p_switch, w , h, srm.width, srm.height,
bool swap_w_h = p_switch->rotated ^ retroarch_get_rotation();
ret = sr_add_mode(swap_w_h? h : w, swap_w_h? w : h, rr, flags, &srm);
if (!ret)
RARCH_LOG("[CRT]: SR failed to add mode");
if (p_switch->kms_ctx)
{
RARCH_LOG("[CRT]: KMS -> use sr_add_mode\n");
//settings_t *settings = config_get_ptr();
get_modeline_for_kms(p_switch, &srm);
// Need trigger the context set video mode
//crt_switch_driver_refresh();
video_driver_set_video_mode(srm.width, srm.height, true);
} else {
ret = sr_set_mode(srm.id);
}
if (!p_switch->kms_ctx && !ret)
RARCH_LOG("[CRT]: SR failed to switch mode");
p_switch->sr_core_hz = (float)srm.vfreq;
set_aspect(p_switch, retroarch_get_rotation()? h : w , retroarch_get_rotation()? w : h, srm.width, srm.height,
(float)srm.x_scale, (float)srm.y_scale, srm.is_stretched);
}
else
{
set_aspect(p_switch, width , height, width, height,
(float)1, (float)1, false);
video_driver_set_size(width , height);
video_driver_set_size(width , height);
video_driver_apply_state_changes();
}
}
@ -295,9 +340,9 @@ void crt_destroy_modes(videocrt_switch_t *p_switch)
static void crt_check_hh_core(videocrt_switch_t *p_switch)
{
p_switch->hh_core = false;
}
}
#if !defined(HAVE_VIDEOCORE)
#if !defined(HAVE_VIDEOCORE)
static void crt_fix_hh_res(videocrt_switch_t *p_switch,
int native_width, int width, int height,
int crt_mode, int monitor_index, int super_width)
@ -315,7 +360,7 @@ static void crt_fix_hh_res(videocrt_switch_t *p_switch,
void crt_switch_res_core(
videocrt_switch_t *p_switch,
unsigned native_width, unsigned width, unsigned height,
float hz, unsigned crt_mode,
float hz, bool rotated, unsigned crt_mode,
int crt_switch_center_adjust,
int crt_switch_porch_adjust,
int monitor_index, bool dynamic,
@ -342,7 +387,7 @@ void crt_switch_res_core(
if (height != 4 )
{
p_switch->menu_active = false;
p_switch->porch_adjust = crt_switch_porch_adjust;
p_switch->porch_adjust = crt_switch_porch_adjust;
p_switch->ra_core_height = height;
p_switch->ra_core_hz = hz;
@ -350,11 +395,13 @@ void crt_switch_res_core(
p_switch->center_adjust = crt_switch_center_adjust;
p_switch->index = monitor_index;
p_switch->rotated = rotated;
/* Detect resolution change and switch */
if (crt_check_for_changes(p_switch))
{
RARCH_LOG("[CRT]: Requested Resolution: %dx%d@%f \n", native_width, height, hz);
RARCH_LOG("[CRT]: Requested Resolution: %dx%d@%f orientation: %s\n",
native_width, height, hz, rotated? "rotated" : "normal");
#if defined(HAVE_VIDEOCORE)
crt_rpi_switch(p_switch, width, height, hz, 0, native_width);
#else
@ -380,7 +427,7 @@ void crt_switch_res_core(
}
}
}
void crt_adjust_sr_ini(videocrt_switch_t *p_switch)
@ -390,8 +437,8 @@ void crt_adjust_sr_ini(videocrt_switch_t *p_switch)
if (p_switch->sr2_active)
{
/* First we reload the base switchres.ini file
to undo any overrides that might have been
/* First we reload the base switchres.ini file
to undo any overrides that might have been
loaded for another core */
if (ini_overrides_loaded)
{
@ -402,7 +449,7 @@ void crt_adjust_sr_ini(videocrt_switch_t *p_switch)
if (core_name[0] != '\0')
{
/* Then we look for config/Core Name/Core Name.switchres.ini
/* Then we look for config/Core Name/Core Name.switchres.ini
and load it, overriding any variables it specifies */
config_directory[0] = '\0';
fill_pathname_application_special(config_directory,
@ -434,6 +481,27 @@ void crt_adjust_sr_ini(videocrt_switch_t *p_switch)
}
}
void get_modeline_for_kms(videocrt_switch_t *p_switch, sr_mode* srm)
{
RARCH_LOG("[SUBS] Setting video_st->crt_switch_st\n");
p_switch->clock = srm->pclock / 1000;
p_switch->hdisplay = srm->width;
p_switch->hsync_start = srm->hbegin;
p_switch->hsync_end = srm->hend;
p_switch->htotal = srm->htotal;
p_switch->vdisplay = srm->height;
p_switch->vsync_start = srm->vbegin;
p_switch->vsync_end = srm->vend;
p_switch->vtotal = srm->vtotal;
p_switch->vrefresh = srm->refresh;
p_switch->hskew = 0;
p_switch->vscan = 0;
p_switch->interlace = srm->interlace;
p_switch->doublescan = srm->doublescan;
p_switch->hsync = srm->hsync;
p_switch->vsync = srm->vsync;
}
/* only used for RPi3 */
#if defined(HAVE_VIDEOCORE)
static void crt_rpi_switch(videocrt_switch_t *p_switch,
@ -473,21 +541,21 @@ static void crt_rpi_switch(videocrt_switch_t *p_switch,
/* set core refresh from hz */
video_monitor_set_refresh_rate(hz);
set_aspect(p_switch, width,
set_aspect(p_switch, width,
height, width, height,
(float)1, (float)1, false);
w = width;
while (w < 1920)
while (w < 1920)
w = w+width;
if (w > 2000)
w =w- width;
width = w;
crt_aspect_ratio_switch(p_switch, width,height,width,height);
/* following code is the mode line generator */
hfp = ((width * 0.044) + (width / 112));
hbp = ((width * 0.172) + (width /64));

View File

@ -56,7 +56,19 @@ typedef struct videocrt_switch
bool menu_active;
bool hh_core;
bool rotated;
bool tmp_rotated;
bool kms_ctx;
/* Part of drmModeModeInfo struct from xf86drmMode.h */
uint32_t clock;
uint16_t hdisplay, hsync_start, hsync_end, htotal, hskew;
uint16_t vdisplay, vsync_start, vsync_end, vtotal, vscan;
uint32_t vrefresh;
int interlace;
int doublescan;
int hsync;
int vsync;
} videocrt_switch_t;
void crt_switch_res_core(
@ -65,6 +77,7 @@ void crt_switch_res_core(
unsigned width,
unsigned height,
float hz,
bool rotated,
unsigned crt_mode,
int crt_switch_center_adjust,
int crt_switch_porch_adjust,

View File

@ -226,7 +226,8 @@ enum display_flags
GFX_CTX_FLAGS_SHADERS_HLSL,
GFX_CTX_FLAGS_SHADERS_SLANG,
GFX_CTX_FLAGS_SCREENSHOTS_SUPPORTED,
GFX_CTX_FLAGS_OVERLAY_BEHIND_MENU_SUPPORTED
GFX_CTX_FLAGS_OVERLAY_BEHIND_MENU_SUPPORTED,
GFX_CTX_FLAGS_CRT_SWITCHRES
};
enum shader_uniform_type

View File

@ -387,7 +387,7 @@ const video_driver_t *video_drivers[] = {
};
static video_driver_state_t video_driver_st = { 0 };
static const video_display_server_t *current_display_server =
static const video_display_server_t *current_display_server =
&dispserv_null;
struct retro_hw_render_callback *video_driver_get_hw_context(void)
@ -403,7 +403,7 @@ video_driver_state_t *video_state_get_ptr(void)
void crt_switch_driver_refresh(void)
{
video_driver_reinit(DRIVERS_CMD_ALL);
video_driver_reinit(DRIVER_VIDEO_MASK);
}
@ -743,7 +743,7 @@ void video_context_driver_destroy(gfx_ctx_driver_t *ctx_driver)
ctx_driver->get_video_output_size = NULL;
ctx_driver->get_video_output_prev = NULL;
ctx_driver->get_video_output_next = NULL;
ctx_driver->get_metrics =
ctx_driver->get_metrics =
video_context_driver_get_metrics_null;
ctx_driver->translate_aspect = NULL;
ctx_driver->update_window_title = NULL;
@ -1646,6 +1646,7 @@ bool video_driver_set_video_mode(unsigned width,
if ( video_st->poke
&& video_st->poke->set_video_mode)
{
RARCH_LOG("[SUBS] video_driver_set_video_mode\n");
video_st->poke->set_video_mode(video_st->data,
width, height, fullscreen);
return true;
@ -1686,7 +1687,7 @@ void *video_driver_read_frame_raw(unsigned *width,
unsigned *height, size_t *pitch)
{
video_driver_state_t *video_st = &video_driver_st;
if ( video_st->current_video
if ( video_st->current_video
&& video_st->current_video->read_frame_raw)
return video_st->current_video->read_frame_raw(
video_st->data, width,
@ -1698,7 +1699,7 @@ void video_driver_set_filtering(unsigned index,
bool smooth, bool ctx_scaling)
{
video_driver_state_t *video_st = &video_driver_st;
if ( video_st->poke
if ( video_st->poke
&& video_st->poke->set_filtering)
video_st->poke->set_filtering(
video_st->data,
@ -1708,7 +1709,7 @@ void video_driver_set_filtering(unsigned index,
void video_driver_set_hdr_max_nits(float max_nits)
{
video_driver_state_t *video_st = &video_driver_st;
if ( video_st->poke
if ( video_st->poke
&& video_st->poke->set_hdr_max_nits)
video_st->poke->set_hdr_max_nits(video_st->data, max_nits);
}
@ -1716,7 +1717,7 @@ void video_driver_set_hdr_max_nits(float max_nits)
void video_driver_set_hdr_paper_white_nits(float paper_white_nits)
{
video_driver_state_t *video_st = &video_driver_st;
if ( video_st->poke
if ( video_st->poke
&& video_st->poke->set_hdr_paper_white_nits)
video_st->poke->set_hdr_paper_white_nits(video_st->data, paper_white_nits);
}
@ -1724,7 +1725,7 @@ void video_driver_set_hdr_paper_white_nits(float paper_white_nits)
void video_driver_set_hdr_contrast(float contrast)
{
video_driver_state_t *video_st = &video_driver_st;
if ( video_st->poke
if ( video_st->poke
&& video_st->poke->set_hdr_contrast)
video_st->poke->set_hdr_contrast(video_st->data,
VIDEO_HDR_MAX_CONTRAST - contrast);
@ -1733,27 +1734,27 @@ void video_driver_set_hdr_contrast(float contrast)
void video_driver_set_hdr_expand_gamut(bool expand_gamut)
{
video_driver_state_t *video_st = &video_driver_st;
if ( video_st->poke
if ( video_st->poke
&& video_st->poke->set_hdr_expand_gamut)
video_st->poke->set_hdr_expand_gamut(video_st->data, expand_gamut);
}
/* Use this value as a replacement for anywhere
* where a pure white colour value is used in the UI.
/* Use this value as a replacement for anywhere
* where a pure white colour value is used in the UI.
*
* When HDR is turned on 1,1,1,1 should never really
* be used as this is peak brightness and could cause
* damage to displays over long periods of time
* and be quite hard to look at on really bright displays.
* When HDR is turned on 1,1,1,1 should never really
* be used as this is peak brightness and could cause
* damage to displays over long periods of time
* and be quite hard to look at on really bright displays.
*
* Use paper white instead which is always defined as
* 0.5, 0.5, 0.5, 1.0 or in other words is the top of
* Use paper white instead which is always defined as
* 0.5, 0.5, 0.5, 1.0 or in other words is the top of
* the old SDR (Standard Dynamic Range) range
*/
unsigned video_driver_get_hdr_paper_white(void)
{
/* 0.5, 0.5, 0.5, 1 */
if ( video_driver_supports_hdr()
if ( video_driver_supports_hdr()
&& config_get_ptr()->bools.video_hdr_enable)
return 0x7f7f7fff;
return 0xffffffff;
@ -1764,36 +1765,36 @@ float *video_driver_get_hdr_paper_white_float(void)
{
static float paper_white[4] = { 0.5f, 0.5f, 0.5f, 1.0f};
static float sdr_white [4] = { 1.0f, 1.0f, 1.0f, 1.0f};
if ( video_driver_supports_hdr()
if ( video_driver_supports_hdr()
&& config_get_ptr()->bools.video_hdr_enable)
return paper_white;
return sdr_white;
}
/* This is useful to create a HDR (High Dynamic Range) white
* based off of some passed in nit level - say you want a
* slightly brighter than paper white value for some parts
* of the UI
/* This is useful to create a HDR (High Dynamic Range) white
* based off of some passed in nit level - say you want a
* slightly brighter than paper white value for some parts
* of the UI
*/
float video_driver_get_hdr_luminance(float nits)
{
settings_t *settings = config_get_ptr();
if (video_driver_supports_hdr() && settings->bools.video_hdr_enable)
{
float luminance = nits /
float luminance = nits /
settings->floats.video_hdr_paper_white_nits;
return luminance / (1.0f + luminance);
}
return nits;
}
/* Get reinhard tone mapped colour value for UI elements
* when using HDR and its inverse tonemapper - normally don't use
* but useful if you want a specific colour to look the same
/* Get reinhard tone mapped colour value for UI elements
* when using HDR and its inverse tonemapper - normally don't use
* but useful if you want a specific colour to look the same
* after inverse tonemapping has been applied */
unsigned video_driver_get_hdr_color(unsigned color)
{
if ( video_driver_supports_hdr()
if ( video_driver_supports_hdr()
&& config_get_ptr()->bools.video_hdr_enable)
{
float luminance;
@ -1812,9 +1813,9 @@ unsigned video_driver_get_hdr_color(unsigned color)
convert_yxy_to_rgb(rgb, yxy);
return ( (unsigned)(saturate_value(rgb[0]) * 255.0f) << 24)
| ((unsigned)(saturate_value(rgb[1]) * 255.0f) << 16)
| ((unsigned)(saturate_value(rgb[2]) * 255.0f) << 8)
return ( (unsigned)(saturate_value(rgb[0]) * 255.0f) << 24)
| ((unsigned)(saturate_value(rgb[1]) * 255.0f) << 16)
| ((unsigned)(saturate_value(rgb[2]) * 255.0f) << 8)
| (color & 0xFF);
}
return color;
@ -2249,7 +2250,7 @@ void video_driver_update_viewport(
void video_driver_show_mouse(void)
{
video_driver_state_t *video_st = &video_driver_st;
if ( video_st->poke
if ( video_st->poke
&& video_st->poke->show_mouse)
video_st->poke->show_mouse(video_st->data, true);
}
@ -2257,7 +2258,7 @@ void video_driver_show_mouse(void)
void video_driver_hide_mouse(void)
{
video_driver_state_t *video_st = &video_driver_st;
if ( video_st->poke
if ( video_st->poke
&& video_st->poke->show_mouse)
video_st->poke->show_mouse(video_st->data, false);
}
@ -2645,7 +2646,7 @@ void video_driver_cached_frame(void)
if (runloop_st->current_core.flags & RETRO_CORE_FLAG_INITED)
cbs->frame_cb(
(video_st->frame_cache_data != RETRO_HW_FRAME_BUFFER_VALID)
? video_st->frame_cache_data
? video_st->frame_cache_data
: NULL,
video_st->frame_cache_width,
video_st->frame_cache_height,
@ -3317,7 +3318,7 @@ bool video_driver_init_internal(bool *video_is_threaded, bool verbosity_enabled)
aspectratio_lut[new_aspect_idx].value);
}
if ( settings->bools.video_fullscreen
if ( settings->bools.video_fullscreen
|| (video_st->flags & VIDEO_FLAG_FORCE_FULLSCREEN))
{
width = settings->uints.video_fullscreen_x;
@ -4117,8 +4118,8 @@ void video_driver_frame(const void *data, unsigned width,
/* TODO/FIXME - add OSD chat text here */
}
if (render_frame
&& video_st->current_video
if (render_frame
&& video_st->current_video
&& video_st->current_video->frame)
{
if (video_st->current_video->frame(
@ -4187,6 +4188,7 @@ void video_driver_frame(const void *data, unsigned width,
native_width, width,
height,
video_st->core_hz,
video_st->av_info.geometry.aspect_ratio < 1.0 ? true : false,
video_info.crt_switch_resolution,
video_info.crt_switch_center_adjust,
video_info.crt_switch_porch_adjust,

View File

@ -6880,6 +6880,14 @@ unsigned menu_displaylist_build_list(
PARSE_ACTION, false) == 0)
count++;
}
else if (video_context_driver_get_flags(&flags))
{
if (BIT32_GET(flags.flags, GFX_CTX_FLAGS_CRT_SWITCHRES))
if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list,
MENU_ENUM_LABEL_CRT_SWITCHRES_SETTINGS,
PARSE_ACTION, false) == 0)
count++;
}
if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list,
MENU_ENUM_LABEL_VIDEO_OUTPUT_SETTINGS,