RetroArch/deps/switchres/display_windows.cpp
Ben Templeman 197203d09b Fixed monitor index corruption on Windows and added correct fractal scalling. only used when required.
Updated log defines to match SR upstream.

Added new SR_CONFIG_PATHS for non Winddows and Linux systems.
Not that SR works on them but to fix RA compile issues

Updated SR2 code base to latest. Added supprt for windows monitor indexing.
Fixed monitor index bug where index 1 was not being used corretly
and "auto" was not being sent.

Updated swithres for x86 windows fix
fixed SR2 auto issue

Fixed auto monitor bug

Fixed monitor index corruption on Windows

Fixxed buffer size bug

Added correct fractal scalling. only used when required.
2021-07-07 18:06:47 +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()
{
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;
}