RetroArch/deps/switchres/switchres_wrapper.cpp
Subs f24893bcb1
[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
2023-03-25 11:57:10 +01:00

436 lines
11 KiB
C++

/**************************************************************
switchres_wrapper.cpp - Switchres C wrapper API
---------------------------------------------------------
Switchres Modeline generation engine for emulation
License GPL-2.0+
Copyright 2010-2022 Chris Kennedy, Antonio Giner,
Alexandre Wodarczyk, Gil Delescluse
**************************************************************/
#define MODULE_API_EXPORTS
#include "switchres.h"
#include "switchres_wrapper.h"
#include "log.h"
#include <stdio.h>
#include <locale>
#ifdef __cplusplus
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;
//============================================================
// Start of Switchres API
//============================================================
//============================================================
// sr_init
//============================================================
MODULE_API void sr_init()
{
setlocale(LC_NUMERIC, "C");
swr = new switchres_manager;
swr->parse_config("switchres.ini");
}
//============================================================
// sr_get_version
//============================================================
MODULE_API char* sr_get_version() {
return switchres_manager::get_version();
}
//============================================================
// sr_deinit
//============================================================
MODULE_API void sr_deinit()
{
delete swr;
}
//============================================================
// 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();
}
//============================================================
// 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->display()->set_user_mode(&user_mode);
}
//============================================================
// sr_get_mode
//============================================================
MODULE_API int sr_get_mode(int id, sr_mode *srm)
{
if (srm == nullptr)
return 0;
*srm = {};
srm->id = id;
return sr_mode_internal(0, 0, 0, 0, srm, SR_ACTION_GET_FROM_ID, __FUNCTION__);
}
//============================================================
// sr_add_mode
//============================================================
MODULE_API int sr_add_mode(int width, int height, double refresh, int flags, sr_mode *srm)
{
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__);
}
//============================================================
// sr_flush
//============================================================
MODULE_API int sr_flush()
{
return sr_mode_internal(0, 0, 0, 0, 0, SR_ACTION_FLUSH, __FUNCTION__);
}
//============================================================
// sr_switch_to_mode
//============================================================
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__);
}
//============================================================
// 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__);
}
//============================================================
// sr_set_log_level
//============================================================
MODULE_API void sr_set_log_level(int l)
{
swr->set_log_level(l);
}
//============================================================
// 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)
{
swr->set_log_verbose_fn((void *)f);
}
MODULE_API void sr_set_log_callback_error(void * f)
{
swr->set_log_error_fn((void *)f);
}
//============================================================
// 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_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