RetroArch/deps/switchres/display_windows.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

312 lines
9.4 KiB
C++

/**************************************************************
display_windows.cpp - Display manager for Windows
---------------------------------------------------------
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 "display_windows.h"
#include "log.h"
typedef struct ENUM_INFO
{
int index;
HMONITOR h_monitor;
} ENUM_INFO;
//============================================================
// windows_display::windows_display
//============================================================
windows_display::windows_display(display_settings *ds)
{
// Get display settings
m_ds = *ds;
}
//============================================================
// windows_display::~windows_display
//============================================================
windows_display::~windows_display()
{
// Restore previous settings
if (!m_ds.keep_changes) ChangeDisplaySettingsExA(m_device_name, NULL, NULL, 0, 0);
}
//============================================================
// windows_display::init
//============================================================
int CALLBACK monitor_by_index(HMONITOR h_monitor, HDC, LPRECT, LPARAM data)
{
ENUM_INFO *mon_info = (ENUM_INFO*) data;
if (--mon_info->index < 0)
{
mon_info->h_monitor = h_monitor;
return false;
}
return true;
}
bool windows_display::init(void*)
{
char display[32] = {};
// If monitor is passed by index, find the matching device
if (strlen(m_ds.screen) == 1)
{
int monitor_index = m_ds.screen[0] - '0';
if (monitor_index < 0 || monitor_index > 9)
{
log_error("Switchres: bad monitor index %d\n", monitor_index);
return false;
}
ENUM_INFO mon_info;
mon_info.index = monitor_index;
mon_info.h_monitor = NULL;
EnumDisplayMonitors(NULL, NULL, monitor_by_index, (LPARAM)&mon_info);
if (mon_info.h_monitor != NULL)
{
MONITORINFOEXA info = {};
info.cbSize = sizeof(info);
GetMonitorInfoA(mon_info.h_monitor, &info);
snprintf(display, sizeof(display) -1, "%s", info.szDevice);
log_info("display %s\n", display);
}
else
{
log_error("Swichres: couldn't find handle for monitor index %d\n", monitor_index);
return false;
}
}
else
strncpy(display, m_ds.screen, sizeof(display)-1);
// Find the display by device name, or "auto" for primary display
DISPLAY_DEVICEA lpDisplayDevice[DISPLAY_MAX];
int idev = 0;
int found = -1;
while (idev < DISPLAY_MAX)
{
memset(&lpDisplayDevice[idev], 0, sizeof(DISPLAY_DEVICEA));
lpDisplayDevice[idev].cb = sizeof(DISPLAY_DEVICEA);
if (EnumDisplayDevicesA(NULL, idev, &lpDisplayDevice[idev], 0) == FALSE)
break;
if ((!strcmp(display, "auto") && (lpDisplayDevice[idev].StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE))
|| !strcmp(display, lpDisplayDevice[idev].DeviceName))
found = idev;
idev++;
}
if (found != -1)
{
strncpy(m_device_name, lpDisplayDevice[found].DeviceName, sizeof(m_device_name) -1);
strncpy(m_device_id, lpDisplayDevice[found].DeviceID, sizeof(m_device_id) -1);
log_verbose("Switchres: %s: %s (%s)\n", m_device_name, lpDisplayDevice[found].DeviceString, m_device_id);
char *pch;
int i;
for (i = 0; i < idev; i++)
{
pch = strstr(lpDisplayDevice[i].DeviceString, lpDisplayDevice[found].DeviceString);
if (pch)
{
found = i;
break;
}
}
char *chsrc, *chdst;
chdst = m_device_key;
for (chsrc = lpDisplayDevice[i].DeviceKey + 18; *chsrc != 0; chsrc++)
*chdst++ = *chsrc;
*chdst = 0;
}
else
{
log_verbose("Switchres: Failed obtaining default video registry key\n");
return false;
}
log_verbose("Switchres: Device key: %s\n", m_device_key);
// Initialize custom video
int method = CUSTOM_VIDEO_TIMING_AUTO;
if(!strcmp(m_ds.api, "powerstrip")) method = CUSTOM_VIDEO_TIMING_POWERSTRIP;
strcpy(m_ds.vs.device_reg_key, m_device_key);
// Create custom video backend
set_factory(new custom_video);
set_custom_video(factory()->make(m_device_name, m_device_id, method, &m_ds.vs));
if (video()) video()->init();
// Build our display's mode list
video_modes.clear();
backup_modes.clear();
get_desktop_mode();
get_available_video_modes();
if (!strcmp(m_ds.monitor, "lcd")) auto_specs();
filter_modes();
return true;
}
//============================================================
// windows_display::set_mode
//============================================================
bool windows_display::set_mode(modeline *mode)
{
if (mode && set_desktop_mode(mode, (m_ds.keep_changes? CDS_UPDATEREGISTRY : CDS_FULLSCREEN) | CDS_RESET))
{
set_current_mode(mode);
return true;
}
return false;
}
//============================================================
// windows_display::get_desktop_mode
//============================================================
bool windows_display::get_desktop_mode()
{
memset(&m_devmode, 0, sizeof(DEVMODEA));
m_devmode.dmSize = sizeof(DEVMODEA);
if (EnumDisplaySettingsExA(!strcmp(m_device_name, "auto")?NULL:m_device_name, ENUM_CURRENT_SETTINGS, &m_devmode, 0))
{
desktop_mode.width = m_devmode.dmDisplayOrientation == DMDO_DEFAULT || m_devmode.dmDisplayOrientation == DMDO_180? m_devmode.dmPelsWidth:m_devmode.dmPelsHeight;
desktop_mode.height = m_devmode.dmDisplayOrientation == DMDO_DEFAULT || m_devmode.dmDisplayOrientation == DMDO_180? m_devmode.dmPelsHeight:m_devmode.dmPelsWidth;
desktop_mode.refresh = m_devmode.dmDisplayFrequency;
desktop_mode.interlace = (m_devmode.dmDisplayFlags & DM_INTERLACED)?1:0;
return true;
}
return false;
}
//============================================================
// windows_display::set_desktop_mode
//============================================================
bool windows_display::set_desktop_mode(modeline *mode, int flags)
{
if (mode)
{
DEVMODEA lpDevMode;
memset(&lpDevMode, 0, sizeof(DEVMODEA));
lpDevMode.dmSize = sizeof(DEVMODEA);
lpDevMode.dmPelsWidth = mode->type & MODE_ROTATED? mode->height : mode->width;
lpDevMode.dmPelsHeight = mode->type & MODE_ROTATED? mode->width : mode->height;
lpDevMode.dmDisplayFrequency = (int)mode->refresh;
lpDevMode.dmDisplayFlags = mode->interlace? DM_INTERLACED : 0;
lpDevMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS;
log_info("set_desktop_mode: %s (%dx%d@%d) flags(%x)\n", m_device_name, (int)lpDevMode.dmPelsWidth, (int)lpDevMode.dmPelsHeight, (int)lpDevMode.dmDisplayFrequency, (int)lpDevMode.dmDisplayFlags);
int result = ChangeDisplaySettingsExA(m_device_name, &lpDevMode, NULL, flags, 0);
if (result == DISP_CHANGE_SUCCESSFUL)
return true;
log_error("ChangeDisplaySettingsExA error(%x)\n", (int)result);
}
return false;
}
//============================================================
// windows_display::restore_desktop_mode
//============================================================
bool windows_display::restore_desktop_mode()
{
if (ChangeDisplaySettingsExA(m_device_name, &m_devmode, NULL, 0, 0) == DISP_CHANGE_SUCCESSFUL)
return true;
return false;
}
//============================================================
// windows_display::get_available_video_modes
//============================================================
int windows_display::get_available_video_modes()
{
int iModeNum = 0, j = 0, k = 0;
DEVMODEA lpDevMode;
memset(&lpDevMode, 0, sizeof(DEVMODEA));
lpDevMode.dmSize = sizeof(DEVMODEA);
log_verbose("Switchres: Searching for custom video modes...\n");
while (EnumDisplaySettingsExA(m_device_name, iModeNum, &lpDevMode, m_ds.lock_unsupported_modes?0:EDS_RAWMODE) != 0)
{
if (lpDevMode.dmBitsPerPel == 32 && lpDevMode.dmDisplayFixedOutput == DMDFO_DEFAULT)
{
modeline m;
memset(&m, 0, sizeof(struct modeline));
m.interlace = (lpDevMode.dmDisplayFlags & DM_INTERLACED)?1:0;
m.width = lpDevMode.dmDisplayOrientation == DMDO_DEFAULT || lpDevMode.dmDisplayOrientation == DMDO_180? lpDevMode.dmPelsWidth:lpDevMode.dmPelsHeight;
m.height = lpDevMode.dmDisplayOrientation == DMDO_DEFAULT || lpDevMode.dmDisplayOrientation == DMDO_180? lpDevMode.dmPelsHeight:lpDevMode.dmPelsWidth;
m.refresh = lpDevMode.dmDisplayFrequency;
m.hactive = m.width;
m.vactive = m.height;
m.vfreq = m.refresh;
m.type |= lpDevMode.dmDisplayOrientation == DMDO_90 || lpDevMode.dmDisplayOrientation == DMDO_270? MODE_ROTATED : MODE_OK;
for (auto &mode : video_modes) if (mode.width == m.width && mode.height == m.height && mode.refresh == m.refresh && m.interlace == mode.interlace) goto found;
if (m.width == desktop_mode.width && m.height == desktop_mode.height && m.refresh == desktop_mode.refresh && m.interlace == desktop_mode.interlace)
{
m.type |= MODE_DESKTOP;
if (m.type & MODE_ROTATED) set_desktop_is_rotated(true);
if (current_mode() == nullptr)
set_current_mode(&m);
}
log_verbose("Switchres: [%3d] %4dx%4d @%3d%s%s %s: ", k, m.width, m.height, m.refresh, m.interlace?"i":"p", m.type & MODE_DESKTOP?"*":"", m.type & MODE_ROTATED?"rot":"");
if (video() && video()->get_timing(&m))
{
j++;
log_mode(&m);
}
else
{
m.type |= CUSTOM_VIDEO_TIMING_SYSTEM;
log_verbose("system mode\n");
}
// Save our desktop mode now that we queried detailed timings
if (m.type & MODE_DESKTOP) desktop_mode = m;
video_modes.push_back(m);
backup_modes.push_back(m);
k++;
}
found:
iModeNum++;
}
k--;
log_verbose("Switchres: Found %d custom of %d active video modes\n", j, k);
return k;
}