mirror of
https://github.com/libretro/RetroArch.git
synced 2024-11-27 02:00:41 +00:00
[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:
parent
d118259589
commit
f24893bcb1
64
deps/switchres/.github/workflows/build.yml
vendored
Normal file
64
deps/switchres/.github/workflows/build.yml
vendored
Normal 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
10
deps/switchres/.gitignore
vendored
Normal 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
59
deps/switchres/.gitlab-ci.yml
vendored
Normal 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
|
4
deps/switchres/custom_video.h
vendored
4
deps/switchres/custom_video.h
vendored
@ -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
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
22
deps/switchres/custom_video_drmkms.h
vendored
Normal file → Executable 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
|
||||
|
8
deps/switchres/custom_video_xrandr.cpp
vendored
8
deps/switchres/custom_video_xrandr.cpp
vendored
@ -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;
|
||||
|
||||
|
222
deps/switchres/display.cpp
vendored
222
deps/switchres/display.cpp
vendored
@ -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;
|
||||
}
|
||||
|
117
deps/switchres/display.h
vendored
117
deps/switchres/display.h
vendored
@ -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
|
||||
|
7
deps/switchres/display_linux.cpp
vendored
7
deps/switchres/display_linux.cpp
vendored
@ -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);
|
||||
|
2
deps/switchres/display_linux.h
vendored
2
deps/switchres/display_linux.h
vendored
@ -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
304
deps/switchres/display_sdl2.cpp
vendored
Normal 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
32
deps/switchres/display_sdl2.h
vendored
Normal 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();
|
||||
};
|
2
deps/switchres/display_windows.cpp
vendored
2
deps/switchres/display_windows.cpp
vendored
@ -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] = {};
|
||||
|
||||
|
2
deps/switchres/display_windows.h
vendored
2
deps/switchres/display_windows.h
vendored
@ -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
211
deps/switchres/drm_hook.cpp
vendored
Normal 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;
|
||||
}
|
2
deps/switchres/edid.cpp
vendored
2
deps/switchres/edid.cpp
vendored
@ -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;
|
||||
|
||||
|
2
deps/switchres/edid.h
vendored
2
deps/switchres/edid.h
vendored
@ -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
56
deps/switchres/examples/README.md
vendored
Normal 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
|
31
deps/switchres/examples/multi_monitor.cpp
vendored
Normal file
31
deps/switchres/examples/multi_monitor.cpp
vendored
Normal 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
84
deps/switchres/examples/test_dlopen.cpp
vendored
Normal 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);
|
||||
}
|
36
deps/switchres/examples/test_liblink.cpp
vendored
Normal file
36
deps/switchres/examples/test_liblink.cpp
vendored
Normal 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
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
324
deps/switchres/geometry.py
vendored
Normal 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
410
deps/switchres/grid.cpp
vendored
Normal 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;
|
||||
}
|
2
deps/switchres/log.cpp
vendored
2
deps/switchres/log.cpp
vendored
@ -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
159
deps/switchres/makefile
vendored
Normal 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
|
239
deps/switchres/modeline.cpp
vendored
239
deps/switchres/modeline.cpp
vendored
@ -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
|
||||
//============================================================
|
||||
|
18
deps/switchres/modeline.h
vendored
18
deps/switchres/modeline.h
vendored
@ -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);
|
||||
|
||||
|
8
deps/switchres/monitor.cpp
vendored
8
deps/switchres/monitor.cpp
vendored
@ -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"))
|
||||
|
2
deps/switchres/monitor.h
vendored
2
deps/switchres/monitor.h
vendored
@ -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);
|
||||
|
442
deps/switchres/switchres.cpp
vendored
442
deps/switchres/switchres.cpp
vendored
@ -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];
|
||||
}
|
||||
|
60
deps/switchres/switchres.h
vendored
60
deps/switchres/switchres.h
vendored
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
5
deps/switchres/switchres.ini
vendored
5
deps/switchres/switchres.ini
vendored
@ -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
41
deps/switchres/switchres_defines.h
vendored
Normal 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"
|
83
deps/switchres/switchres_main.cpp
vendored
83
deps/switchres/switchres_main.cpp
vendored
@ -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);
|
||||
|
462
deps/switchres/switchres_wrapper.cpp
vendored
462
deps/switchres/switchres_wrapper.cpp
vendored
@ -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
|
||||
|
118
deps/switchres/switchres_wrapper.h
vendored
118
deps/switchres/switchres_wrapper.h
vendored
@ -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
47
deps/switchres/tests/test_dlopen.cpp
vendored
Normal 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
7
deps/switchres/tests/test_liblink.cpp
vendored
Normal 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
BIN
deps/switchres/tv.ico
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user