/* RetroArch - A frontend for libretro. * Copyright (C) 2011-2017 - Daniel De Matteis * * RetroArch is free software: you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. * * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with RetroArch. * If not, see . */ #if !defined(_XBOX) #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0601 /* Windows 7 */ #endif #if !defined(_MSC_VER) || _WIN32_WINNT >= 0x0601 #undef WINVER #define WINVER 0x0601 #endif #define IDI_ICON 1 #include #endif /* !defined(_XBOX) */ #include #include #include #ifdef HAVE_CONFIG_H #include "../../config.h" #endif #include "win32_common.h" #ifdef HAVE_GDI #include "gdi_common.h" #endif #include "../../frontend/frontend_driver.h" #include "../../configuration.h" #include "../../verbosity.h" #include "../../paths.h" #include "../../retroarch.h" #include "../../tasks/task_content.h" #include "../../tasks/tasks_internal.h" #include "../../core_info.h" #if !defined(_XBOX) #include #include #include "../../input/input_keymaps.h" #include #ifdef HAVE_MENU #include "../../menu/menu_driver.h" #endif #include /* Assume W-functions do not work below Win2K and Xbox platforms */ #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX) #ifndef LEGACY_WIN32 #define LEGACY_WIN32 #endif #endif #ifdef LEGACY_WIN32 #define DragQueryFileR DragQueryFile #else #define DragQueryFileR DragQueryFileW #endif /* For some reason this is missing from mingw winuser.h */ #ifndef EDS_ROTATEDMODE #define EDS_ROTATEDMODE 4 #endif /* These are defined in later SDKs, thus ifdeffed. */ #ifndef WM_MOUSEHWHEEL #define WM_MOUSEHWHEEL 0x20e #endif #ifndef WM_MOUSEWHEEL #define WM_MOUSEWHEEL 0x020A #endif #ifndef WM_POINTERUPDATE #define WM_POINTERUPDATE 0x0245 #endif #ifndef WM_POINTERDOWN #define WM_POINTERDOWN 0x0246 #endif #ifndef WM_POINTERUP #define WM_POINTERUP 0x0247 #endif const GUID GUID_DEVINTERFACE_HID = { 0x4d1e55b2, 0xf16f, 0x11Cf, { 0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } }; #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x501 static HDEVNOTIFY notification_handler; #endif #ifdef HAVE_DINPUT extern bool dinput_handle_message(void *dinput, UINT message, WPARAM wParam, LPARAM lParam); #endif typedef struct DISPLAYCONFIG_RATIONAL_CUSTOM { UINT32 Numerator; UINT32 Denominator; } DISPLAYCONFIG_RATIONAL_CUSTOM; typedef struct DISPLAYCONFIG_2DREGION_CUSTOM { UINT32 cx; UINT32 cy; } DISPLAYCONFIG_2DREGION_CUSTOM; typedef struct DISPLAYCONFIG_VIDEO_SIGNAL_INFO_CUSTOM { UINT64 pixelRate; DISPLAYCONFIG_RATIONAL_CUSTOM hSyncFreq; DISPLAYCONFIG_RATIONAL_CUSTOM vSyncFreq; DISPLAYCONFIG_2DREGION_CUSTOM activeSize; DISPLAYCONFIG_2DREGION_CUSTOM totalSize; union { struct { UINT32 videoStandard :16; UINT32 vSyncFreqDivider :6; UINT32 reserved :10; } AdditionalSignalInfo; UINT32 videoStandard; } dummyunionname; UINT32 scanLineOrdering; } DISPLAYCONFIG_VIDEO_SIGNAL_INFO_CUSTOM; typedef struct DISPLAYCONFIG_TARGET_MODE_CUSTOM { DISPLAYCONFIG_VIDEO_SIGNAL_INFO_CUSTOM targetVideoSignalInfo; } DISPLAYCONFIG_TARGET_MODE_CUSTOM; typedef struct DISPLAYCONFIG_PATH_SOURCE_INFO_CUSTOM { LUID adapterId; UINT32 id; union { UINT32 modeInfoIdx; struct { UINT32 cloneGroupId :16; UINT32 sourceModeInfoIdx :16; } dummystructname; } dummyunionname; UINT32 statusFlags; } DISPLAYCONFIG_PATH_SOURCE_INFO_CUSTOM; typedef struct DISPLAYCONFIG_DESKTOP_IMAGE_INFO_CUSTOM { POINTL PathSourceSize; RECTL DesktopImageRegion; RECTL DesktopImageClip; } DISPLAYCONFIG_DESKTOP_IMAGE_INFO_CUSTOM; typedef struct DISPLAYCONFIG_SOURCE_MODE_CUSTOM { UINT32 width; UINT32 height; UINT32 pixelFormat; POINTL position; } DISPLAYCONFIG_SOURCE_MODE_CUSTOM; typedef struct DISPLAYCONFIG_MODE_INFO_CUSTOM { UINT32 infoType; UINT32 id; LUID adapterId; union { DISPLAYCONFIG_TARGET_MODE_CUSTOM targetMode; DISPLAYCONFIG_SOURCE_MODE_CUSTOM sourceMode; DISPLAYCONFIG_DESKTOP_IMAGE_INFO_CUSTOM desktopImageInfo; } dummyunionname; } DISPLAYCONFIG_MODE_INFO_CUSTOM; typedef struct DISPLAYCONFIG_PATH_TARGET_INFO_CUSTOM { LUID adapterId; UINT32 id; union { UINT32 modeInfoIdx; struct { UINT32 desktopModeInfoIdx :16; UINT32 targetModeInfoIdx :16; } dummystructname; } dummyunionname; UINT32 outputTechnology; UINT32 rotation; UINT32 scaling; DISPLAYCONFIG_RATIONAL_CUSTOM refreshRate; UINT32 scanLineOrdering; BOOL targetAvailable; UINT32 statusFlags; } DISPLAYCONFIG_PATH_TARGET_INFO_CUSTOM; typedef struct DISPLAYCONFIG_PATH_INFO_CUSTOM { DISPLAYCONFIG_PATH_SOURCE_INFO_CUSTOM sourceInfo; DISPLAYCONFIG_PATH_TARGET_INFO_CUSTOM targetInfo; UINT32 flags; } DISPLAYCONFIG_PATH_INFO_CUSTOM; typedef LONG (WINAPI *QUERYDISPLAYCONFIG)(UINT32, UINT32*, DISPLAYCONFIG_PATH_INFO_CUSTOM*, UINT32*, DISPLAYCONFIG_MODE_INFO_CUSTOM*, UINT32*); typedef LONG (WINAPI *GETDISPLAYCONFIGBUFFERSIZES)(UINT32, UINT32*, UINT32*); bool g_win32_restore_desktop = false; static bool taskbar_is_created = false; bool g_win32_inited = false; typedef struct win32_common_state { int pos_x; int pos_y; unsigned pos_width; unsigned pos_height; unsigned taskbar_message; bool quit; unsigned monitor_count; bool resized; } win32_common_state_t; static win32_common_state_t win32_st = { CW_USEDEFAULT, /* pos_x */ CW_USEDEFAULT, /* pos_y */ 0, /* pos_width */ 0, /* pos_height */ 0, /* taskbar_message */ false, /* quit */ 0, /* monitor_count */ false /* resized */ }; unsigned g_win32_resize_width = 0; unsigned g_win32_resize_height = 0; ui_window_win32_t main_window; /* Power Request APIs */ #if !defined(_XBOX) && (_MSC_VER == 1310) typedef struct _REASON_CONTEXT { ULONG Version; DWORD Flags; union { struct { HMODULE LocalizedReasonModule; ULONG LocalizedreasonId; ULONG ReasonStringCount; LPWSTR *ReasonStrings; } Detailed; LPWSTR SimpleReasonString; } Reason; } REASON_CONTEXT, *PREASON_CONTEXT; typedef enum _POWER_REQUEST_TYPE { PowerRequestDisplayRequired, PowerRequestSystemRequired, PowerRequestAwayModeRequired, PowerRequestExecutionRequired } POWER_REQUEST_TYPE, *PPOWER_REQUEST_TYPE; #define POWER_REQUEST_CONTEXT_VERSION 0 #define POWER_REQUEST_CONTEXT_SIMPLE_STRING 1 #define POWER_REQUEST_CONTEXT_DETAILED_STRING 2 #endif #ifdef _WIN32_WINNT_WIN7 typedef REASON_CONTEXT POWER_REQUEST_CONTEXT, *PPOWER_REQUEST_CONTEXT, *LPPOWER_REQUEST_CONTEXT; #endif #ifndef MAX_MONITORS #define MAX_MONITORS 9 #endif #if defined(_MSC_VER) && _MSC_VER <= 1200 #define INT_PTR_COMPAT int #else #define INT_PTR_COMPAT INT_PTR #endif static HMONITOR win32_monitor_last; static HMONITOR win32_monitor_all[MAX_MONITORS]; bool win32_taskbar_is_created(void) { return taskbar_is_created; } static INT_PTR_COMPAT CALLBACK pick_core_proc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { size_t list_size; core_info_list_t *core_info_list = NULL; const core_info_t *core_info = NULL; switch (message) { case WM_INITDIALOG: { HWND hwndList; unsigned i; /* Add items to list. */ core_info_get_list(&core_info_list); core_info_list_get_supported_cores(core_info_list, path_get(RARCH_PATH_CONTENT), &core_info, &list_size); hwndList = GetDlgItem(hDlg, ID_CORELISTBOX); for (i = 0; i < list_size; i++) { const core_info_t *info = (const core_info_t*)&core_info[i]; SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)info->display_name); } SetFocus(hwndList); return TRUE; } case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: case IDCANCEL: EndDialog(hDlg, LOWORD(wParam)); break; case ID_CORELISTBOX: switch (HIWORD(wParam)) { case LBN_SELCHANGE: { const core_info_t *info = NULL; HWND hwndList = GetDlgItem( hDlg, ID_CORELISTBOX); int lbItem = (int) SendMessage(hwndList, LB_GETCURSEL, 0, 0); core_info_get_list(&core_info_list); core_info_list_get_supported_cores(core_info_list, path_get(RARCH_PATH_CONTENT), &core_info, &list_size); info = (const core_info_t*)&core_info[lbItem]; path_set(RARCH_PATH_CORE, info->path); } break; } return TRUE; } } return FALSE; } static BOOL CALLBACK win32_monitor_enum_proc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { win32_common_state_t *g_win32 = (win32_common_state_t*)&win32_st; win32_monitor_all[g_win32->monitor_count++] = hMonitor; return TRUE; } void win32_monitor_from_window(void) { #ifndef _XBOX ui_window_t *window = NULL; win32_monitor_last = MonitorFromWindow(main_window.hwnd, MONITOR_DEFAULTTONEAREST); window = (ui_window_t*)ui_companion_driver_get_window_ptr(); if (window) window->destroy(&main_window); #endif } int win32_change_display_settings(const char *str, void *devmode_data, unsigned flags) { #if _WIN32_WINDOWS >= 0x0410 || _WIN32_WINNT >= 0x0410 /* Windows 98 and later codepath */ return ChangeDisplaySettingsEx(str, (DEVMODE*)devmode_data, NULL, flags, NULL); #else /* Windows 95 / NT codepath */ return ChangeDisplaySettings((DEVMODE*)devmode_data, flags); #endif } void win32_monitor_get_info(void) { MONITORINFOEX current_mon; memset(¤t_mon, 0, sizeof(current_mon)); current_mon.cbSize = sizeof(MONITORINFOEX); GetMonitorInfo(win32_monitor_last, (LPMONITORINFO)¤t_mon); win32_change_display_settings(current_mon.szDevice, NULL, 0); } void win32_monitor_info(void *data, void *hm_data, unsigned *mon_id) { unsigned i; settings_t *settings = config_get_ptr(); MONITORINFOEX *mon = (MONITORINFOEX*)data; HMONITOR *hm_to_use = (HMONITOR*)hm_data; unsigned fs_monitor = settings->uints.video_monitor_index; win32_common_state_t *g_win32 = (win32_common_state_t*)&win32_st; if (!win32_monitor_last) win32_monitor_last = MonitorFromWindow(GetDesktopWindow(), MONITOR_DEFAULTTONEAREST); *hm_to_use = win32_monitor_last; if (fs_monitor && fs_monitor <= g_win32->monitor_count && win32_monitor_all[fs_monitor - 1]) { *hm_to_use = win32_monitor_all[fs_monitor - 1]; *mon_id = fs_monitor - 1; } else { for (i = 0; i < g_win32->monitor_count; i++) { if (win32_monitor_all[i] != *hm_to_use) continue; *mon_id = i; break; } } if (hm_to_use) { memset(mon, 0, sizeof(*mon)); mon->cbSize = sizeof(MONITORINFOEX); GetMonitorInfo(*hm_to_use, (LPMONITORINFO)mon); } } bool win32_load_content_from_gui(const char *szFilename) { /* poll list of current cores */ size_t list_size; content_ctx_info_t content_info = { 0 }; core_info_list_t *core_info_list = NULL; const core_info_t *core_info = NULL; core_info_get_list(&core_info_list); if (!core_info_list) return false; core_info_list_get_supported_cores(core_info_list, (const char*)szFilename, &core_info, &list_size); if (!list_size) return false; path_set(RARCH_PATH_CONTENT, szFilename); if (!path_is_empty(RARCH_PATH_CONTENT)) { unsigned i; core_info_t *current_core = NULL; core_info_get_current_core(¤t_core); /*we already have path for libretro core */ for (i = 0; i < list_size; i++) { const core_info_t *info = (const core_info_t*)&core_info[i]; if (string_is_equal(path_get(RARCH_PATH_CORE), info->path)) { /* Our previous core supports the current rom */ task_push_load_content_with_current_core_from_companion_ui( NULL, &content_info, CORE_TYPE_PLAIN, NULL, NULL); return true; } } } /* Poll for cores for current rom since none exist. */ if (list_size == 1) { /*pick core that only exists and is bound to work. Ish. */ const core_info_t *info = (const core_info_t*)&core_info[0]; if (info) { task_push_load_content_with_new_core_from_companion_ui( info->path, NULL, NULL, NULL, NULL, &content_info, NULL, NULL); return true; } } else { bool okay = false; settings_t *settings = config_get_ptr(); bool video_is_fs = settings->bools.video_fullscreen; /* Fullscreen: Show mouse cursor for dialog */ if (video_is_fs) video_driver_show_mouse(); /* Pick one core that could be compatible, ew */ if (DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_PICKCORE), main_window.hwnd, pick_core_proc, (LPARAM)NULL) == IDOK) { task_push_load_content_with_current_core_from_companion_ui( NULL, &content_info, CORE_TYPE_PLAIN, NULL, NULL); okay = true; } /* Fullscreen: Hide mouse cursor after dialog */ if (video_is_fs) video_driver_hide_mouse(); return okay; } return false; } static bool win32_drag_query_file(HWND hwnd, WPARAM wparam) { if (DragQueryFileR((HDROP)wparam, 0xFFFFFFFF, NULL, 0)) { bool okay = false; #ifdef LEGACY_WIN32 char szFilename[1024]; szFilename[0] = '\0'; DragQueryFileR((HDROP)wparam, 0, szFilename, sizeof(szFilename)); #else wchar_t wszFilename[4096]; char *szFilename = NULL; wszFilename[0] = L'\0'; DragQueryFileR((HDROP)wparam, 0, wszFilename, sizeof(wszFilename)); szFilename = utf16_to_utf8_string_alloc(wszFilename); #endif okay = win32_load_content_from_gui(szFilename); #ifndef LEGACY_WIN32 if (szFilename) free(szFilename); #endif return okay; } return false; } static void win32_save_position(void) { RECT rect; WINDOWPLACEMENT placement; win32_common_state_t *g_win32 = (win32_common_state_t*)&win32_st; settings_t *settings = config_get_ptr(); int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME); int title_bar_height = GetSystemMetrics(SM_CYCAPTION); int menu_bar_height = GetSystemMetrics(SM_CYMENU); bool window_save_positions = settings->bools.video_window_save_positions; bool video_fullscreen = settings->bools.video_fullscreen; bool ui_menubar_enable = settings->bools.ui_menubar_enable; memset(&placement, 0, sizeof(placement)); placement.length = sizeof(placement); GetWindowPlacement(main_window.hwnd, &placement); g_win32->pos_x = placement.rcNormalPosition.left; g_win32->pos_y = placement.rcNormalPosition.top; if (GetWindowRect(main_window.hwnd, &rect)) { g_win32->pos_width = rect.right - rect.left; g_win32->pos_height = rect.bottom - rect.top; } if (window_save_positions) { if ( !video_fullscreen && !retroarch_is_forced_fullscreen() && !retroarch_is_switching_display_mode()) { settings->uints.window_position_x = g_win32->pos_x; settings->uints.window_position_y = g_win32->pos_y; settings->uints.window_position_width = g_win32->pos_width - border_thickness * 2; settings->uints.window_position_height = g_win32->pos_height - border_thickness * 2 - title_bar_height - (ui_menubar_enable ? menu_bar_height : 0); } } } static bool win32_browser( HWND owner, char *filename, size_t filename_size, const char *extensions, const char *title, const char *initial_dir) { bool result = false; const ui_browser_window_t *browser = ui_companion_driver_get_browser_window_ptr(); if (browser) { ui_browser_window_state_t browser_state; /* These need to be big enough to hold the * path/name of any file the user may select. * FIXME: We should really handle the * error case when this isn't big enough. */ char new_title[PATH_MAX]; char new_file[32768]; new_title[0] = '\0'; new_file[0] = '\0'; if (!string_is_empty(title)) strlcpy(new_title, title, sizeof(new_title)); if (filename && *filename) strlcpy(new_file, filename, sizeof(new_file)); /* OPENFILENAME.lpstrFilters is actually const, * so this cast should be safe */ browser_state.filters = (char*)extensions; browser_state.title = new_title; browser_state.startdir = strdup(initial_dir); browser_state.path = new_file; browser_state.window = owner; result = browser->open(&browser_state); if (filename && browser_state.path) strlcpy(filename, browser_state.path, filename_size); free(browser_state.startdir); } return result; } static LRESULT win32_menu_loop(HWND owner, WPARAM wparam) { WPARAM mode = wparam & 0xffff; switch (mode) { case ID_M_LOAD_CORE: { char win32_file[PATH_MAX_LENGTH] = {0}; settings_t *settings = config_get_ptr(); char *title_cp = NULL; size_t converted = 0; const char *extensions = "Libretro core (.dll)\0*.dll\0All Files\0*.*\0\0"; const char *title = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_LIST); const char *initial_dir = settings->paths.directory_libretro; /* Convert UTF8 to UTF16, then back to the * local code page. * This is needed for proper multi-byte * string display until Unicode is * fully supported. */ wchar_t *title_wide = utf8_to_utf16_string_alloc(title); if (title_wide) title_cp = utf16_to_utf8_string_alloc(title_wide); if (!win32_browser(owner, win32_file, sizeof(win32_file), extensions, title_cp, initial_dir)) { if (title_wide) free(title_wide); if (title_cp) free(title_cp); break; } if (title_wide) free(title_wide); if (title_cp) free(title_cp); path_set(RARCH_PATH_CORE, win32_file); command_event(CMD_EVENT_LOAD_CORE, NULL); } break; case ID_M_LOAD_CONTENT: { char win32_file[PATH_MAX_LENGTH] = {0}; char *title_cp = NULL; size_t converted = 0; const char *extensions = "All Files (*.*)\0*.*\0\0"; const char *title = msg_hash_to_str( MENU_ENUM_LABEL_VALUE_LOAD_CONTENT_LIST); settings_t *settings = config_get_ptr(); const char *initial_dir = settings->paths.directory_menu_content; /* Convert UTF8 to UTF16, then back to the * local code page. * This is needed for proper multi-byte * string display until Unicode is * fully supported. */ wchar_t *title_wide = utf8_to_utf16_string_alloc(title); if (title_wide) title_cp = utf16_to_utf8_string_alloc(title_wide); if (!win32_browser(owner, win32_file, sizeof(win32_file), extensions, title_cp, initial_dir)) { if (title_wide) free(title_wide); if (title_cp) free(title_cp); break; } if (title_wide) free(title_wide); if (title_cp) free(title_cp); win32_load_content_from_gui(win32_file); } break; case ID_M_RESET: command_event(CMD_EVENT_RESET, NULL); break; case ID_M_MUTE_TOGGLE: command_event(CMD_EVENT_AUDIO_MUTE_TOGGLE, NULL); break; case ID_M_MENU_TOGGLE: command_event(CMD_EVENT_MENU_TOGGLE, NULL); break; case ID_M_PAUSE_TOGGLE: command_event(CMD_EVENT_PAUSE_TOGGLE, NULL); break; case ID_M_LOAD_STATE: command_event(CMD_EVENT_LOAD_STATE, NULL); break; case ID_M_SAVE_STATE: command_event(CMD_EVENT_SAVE_STATE, NULL); break; case ID_M_DISK_CYCLE: command_event(CMD_EVENT_DISK_EJECT_TOGGLE, NULL); break; case ID_M_DISK_NEXT: command_event(CMD_EVENT_DISK_NEXT, NULL); break; case ID_M_DISK_PREV: command_event(CMD_EVENT_DISK_PREV, NULL); break; case ID_M_FULL_SCREEN: command_event(CMD_EVENT_FULLSCREEN_TOGGLE, NULL); break; case ID_M_MOUSE_GRAB: command_event(CMD_EVENT_GRAB_MOUSE_TOGGLE, NULL); break; case ID_M_TAKE_SCREENSHOT: command_event(CMD_EVENT_TAKE_SCREENSHOT, NULL); break; case ID_M_QUIT: PostMessage(owner, WM_CLOSE, 0, 0); break; case ID_M_TOGGLE_DESKTOP: command_event(CMD_EVENT_UI_COMPANION_TOGGLE, NULL); break; default: if (mode >= ID_M_WINDOW_SCALE_1X && mode <= ID_M_WINDOW_SCALE_10X) { unsigned idx = (mode - (ID_M_WINDOW_SCALE_1X-1)); rarch_ctl(RARCH_CTL_SET_WINDOWED_SCALE, &idx); command_event(CMD_EVENT_RESIZE_WINDOWED_SCALE, NULL); } else if (mode == ID_M_STATE_INDEX_AUTO) { signed idx = -1; settings_t *settings = config_get_ptr(); configuration_set_int( settings, settings->ints.state_slot, idx); } else if (mode >= (ID_M_STATE_INDEX_AUTO+1) && mode <= (ID_M_STATE_INDEX_AUTO+10)) { signed idx = (mode - (ID_M_STATE_INDEX_AUTO+1)); settings_t *settings = config_get_ptr(); configuration_set_int( settings, settings->ints.state_slot, idx); } break; } return 0L; } static LRESULT CALLBACK wnd_proc_common( bool *quit, HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { bool keydown = true; win32_common_state_t *g_win32 = (win32_common_state_t*)&win32_st; switch (message) { case WM_NCLBUTTONDBLCLK: #if _WIN32_WINNT >= 0x0500 /* 2K */ if (g_win32->taskbar_message && message == g_win32->taskbar_message) taskbar_is_created = true; #endif #ifdef HAVE_DINPUT if (input_get_ptr() == &input_dinput) { void* input_data = input_get_data(); if (input_data && dinput_handle_message(input_data, message, wparam, lparam)) return 0; } #endif break; case WM_SYSCOMMAND: /* Prevent screensavers, etc, while running. */ switch (wparam) { case SC_SCREENSAVE: case SC_MONITORPOWER: *quit = true; break; } break; case WM_DROPFILES: win32_drag_query_file(hwnd, wparam); DragFinish((HDROP)wparam); break; case WM_CHAR: *quit = true; { uint16_t mod = 0; if (GetKeyState(VK_SHIFT) & 0x80) mod |= RETROKMOD_SHIFT; if (GetKeyState(VK_CONTROL) & 0x80) mod |= RETROKMOD_CTRL; if (GetKeyState(VK_MENU) & 0x80) mod |= RETROKMOD_ALT; if (GetKeyState(VK_CAPITAL) & 0x81) mod |= RETROKMOD_CAPSLOCK; if (GetKeyState(VK_SCROLL) & 0x81) mod |= RETROKMOD_SCROLLOCK; if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x80) mod |= RETROKMOD_META; /* Seems to be hard to synchronize * WM_CHAR and WM_KEYDOWN properly. */ input_keyboard_event(true, RETROK_UNKNOWN, wparam, mod, RETRO_DEVICE_KEYBOARD); } return TRUE; case WM_KEYUP: case WM_SYSKEYUP: /* Key released */ keydown = false; /* fall-through */ case WM_KEYDOWN: case WM_SYSKEYDOWN: *quit = true; { uint16_t mod = 0; unsigned keycode = 0; unsigned keysym = (lparam >> 16) & 0xff; #if _WIN32_WINNT >= 0x0501 /* XP */ settings_t *settings = config_get_ptr(); #endif if (GetKeyState(VK_SHIFT) & 0x80) mod |= RETROKMOD_SHIFT; if (GetKeyState(VK_CONTROL) & 0x80) mod |= RETROKMOD_CTRL; if (GetKeyState(VK_MENU) & 0x80) mod |= RETROKMOD_ALT; if (GetKeyState(VK_CAPITAL) & 0x81) mod |= RETROKMOD_CAPSLOCK; if (GetKeyState(VK_SCROLL) & 0x81) mod |= RETROKMOD_SCROLLOCK; if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x80) mod |= RETROKMOD_META; #if _WIN32_WINNT >= 0x0501 /* XP */ if (settings && string_is_equal(settings->arrays.input_driver, "raw")) keysym = (unsigned)wparam; else #endif { #ifdef HAVE_DINPUT /* extended keys will map to dinput if the high bit is set */ if (input_get_ptr() == &input_dinput && (lparam >> 24 & 0x1)) keysym |= 0x80; #else /* fix key binding issues on winraw when DirectInput is not available */ #endif } keycode = input_keymaps_translate_keysym_to_rk(keysym); input_keyboard_event(keydown, keycode, 0, mod, RETRO_DEVICE_KEYBOARD); if (message != WM_SYSKEYDOWN) return 0; if ( wparam == VK_F10 || wparam == VK_MENU || wparam == VK_RSHIFT ) return 0; } return DefWindowProc(hwnd, message, wparam, lparam); case WM_CLOSE: case WM_DESTROY: case WM_QUIT: g_win32->quit = true; *quit = true; /* fall-through */ case WM_MOVE: win32_save_position(); break; case WM_SIZE: /* Do not send resize message if we minimize. */ if ( wparam != SIZE_MAXHIDE && wparam != SIZE_MINIMIZED) { if (LOWORD(lparam) != g_win32_resize_width || HIWORD(lparam) != g_win32_resize_height) { g_win32_resize_width = LOWORD(lparam); g_win32_resize_height = HIWORD(lparam); g_win32->resized = true; } } *quit = true; break; case WM_COMMAND: { settings_t *settings = config_get_ptr(); bool ui_menubar_enable = settings ? settings->bools.ui_menubar_enable : false; if (ui_menubar_enable) win32_menu_loop(main_window.hwnd, wparam); } break; } return 0; } #if defined(HAVE_D3D) || defined (HAVE_D3D10) || defined (HAVE_D3D11) || defined (HAVE_D3D12) LRESULT CALLBACK WndProcD3D(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { LRESULT ret; bool quit = false; win32_common_state_t *g_win32 = (win32_common_state_t*)&win32_st; switch (message) { case WM_MOUSEMOVE: case WM_POINTERDOWN: case WM_POINTERUP: case WM_POINTERUPDATE: case WM_DEVICECHANGE: case WM_MOUSEWHEEL: case WM_MOUSEHWHEEL: case WM_NCLBUTTONDBLCLK: #if _WIN32_WINNT >= 0x0500 /* 2K */ if (g_win32->taskbar_message && message == g_win32->taskbar_message) taskbar_is_created = true; #endif #ifdef HAVE_DINPUT if (input_get_ptr() == &input_dinput) { void* input_data = input_get_data(); if (input_data && dinput_handle_message(input_data, message, wparam, lparam)) return 0; } #endif break; case WM_DROPFILES: case WM_SYSCOMMAND: case WM_CHAR: case WM_KEYDOWN: case WM_KEYUP: case WM_SYSKEYUP: case WM_SYSKEYDOWN: case WM_CLOSE: case WM_DESTROY: case WM_QUIT: case WM_MOVE: case WM_SIZE: case WM_COMMAND: ret = wnd_proc_common(&quit, hwnd, message, wparam, lparam); if (quit) return ret; #if _WIN32_WINNT >= 0x0500 /* 2K */ if (g_win32->taskbar_message && message == g_win32->taskbar_message) taskbar_is_created = true; #endif break; case WM_CREATE: if (DragAcceptFiles_func) DragAcceptFiles_func(hwnd, true); g_win32_inited = true; return 0; } return DefWindowProc(hwnd, message, wparam, lparam); } #endif #if defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGL_CORE) || defined(HAVE_VULKAN) LRESULT CALLBACK WndProcWGL(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { LRESULT ret; bool quit = false; win32_common_state_t *g_win32 = (win32_common_state_t*)&win32_st; switch (message) { case WM_MOUSEMOVE: case WM_POINTERDOWN: case WM_POINTERUP: case WM_POINTERUPDATE: case WM_DEVICECHANGE: case WM_MOUSEWHEEL: case WM_MOUSEHWHEEL: case WM_NCLBUTTONDBLCLK: #if _WIN32_WINNT >= 0x0500 /* 2K */ if (g_win32->taskbar_message && message == g_win32->taskbar_message) taskbar_is_created = true; #endif #ifdef HAVE_DINPUT { void* input_data = input_get_data(); if (input_data && dinput_handle_message(input_data, message, wparam, lparam)) return 0; } #endif break; case WM_DROPFILES: case WM_SYSCOMMAND: case WM_CHAR: case WM_KEYDOWN: case WM_KEYUP: case WM_SYSKEYUP: case WM_SYSKEYDOWN: case WM_CLOSE: case WM_DESTROY: case WM_QUIT: case WM_MOVE: case WM_SIZE: case WM_COMMAND: ret = wnd_proc_common(&quit, hwnd, message, wparam, lparam); if (quit) return ret; #if _WIN32_WINNT >= 0x0500 /* 2K */ if (g_win32->taskbar_message && message == g_win32->taskbar_message) taskbar_is_created = true; #endif break; case WM_CREATE: create_graphics_context(hwnd, &g_win32->quit); if (DragAcceptFiles_func) DragAcceptFiles_func(hwnd, true); return 0; } return DefWindowProc(hwnd, message, wparam, lparam); } #endif #ifdef HAVE_GDI LRESULT CALLBACK WndProcGDI(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { LRESULT ret; bool quit = false; win32_common_state_t *g_win32 = (win32_common_state_t*)&win32_st; switch (message) { case WM_MOUSEMOVE: case WM_POINTERDOWN: case WM_POINTERUP: case WM_POINTERUPDATE: case WM_DEVICECHANGE: case WM_MOUSEWHEEL: case WM_MOUSEHWHEEL: case WM_NCLBUTTONDBLCLK: #if _WIN32_WINNT >= 0x0500 /* 2K */ if (g_win32->taskbar_message && message == g_win32->taskbar_message) taskbar_is_created = true; #endif #ifdef HAVE_DINPUT { void* input_data = input_get_data(); if (input_data && dinput_handle_message(input_data, message, wparam, lparam)) return 0; } #endif break; case WM_PAINT: { gdi_t *gdi = (gdi_t*)video_driver_get_ptr(false); if (gdi && gdi->memDC) { gdi->bmp_old = (HBITMAP)SelectObject(gdi->memDC, gdi->bmp); /* Draw video content */ StretchBlt( gdi->winDC, 0, 0, gdi->screen_width, gdi->screen_height, gdi->memDC, 0, 0, gdi->video_width, gdi->video_height, SRCCOPY); SelectObject(gdi->memDC, gdi->bmp_old); } #if _WIN32_WINNT >= 0x0500 /* 2K */ if ( g_win32->taskbar_message && message == g_win32->taskbar_message) taskbar_is_created = true; #endif break; } case WM_DROPFILES: case WM_SYSCOMMAND: case WM_CHAR: case WM_KEYDOWN: case WM_KEYUP: case WM_SYSKEYUP: case WM_SYSKEYDOWN: case WM_CLOSE: case WM_DESTROY: case WM_QUIT: case WM_MOVE: case WM_SIZE: case WM_COMMAND: ret = wnd_proc_common(&quit, hwnd, message, wparam, lparam); if (quit) return ret; #if _WIN32_WINNT >= 0x0500 /* 2K */ if (g_win32->taskbar_message && message == g_win32->taskbar_message) taskbar_is_created = true; #endif break; case WM_CREATE: create_gdi_context(hwnd, &g_win32->quit); if (DragAcceptFiles_func) DragAcceptFiles_func(hwnd, true); return 0; } return DefWindowProc(hwnd, message, wparam, lparam); } #endif bool win32_window_create(void *data, unsigned style, RECT *mon_rect, unsigned width, unsigned height, bool fullscreen) { win32_common_state_t *g_win32 = (win32_common_state_t*)&win32_st; settings_t *settings = config_get_ptr(); #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500 /* 2K */ DEV_BROADCAST_DEVICEINTERFACE notification_filter; unsigned window_opacity = settings->uints.video_window_opacity; bool window_show_decor = settings->bools.video_window_show_decorations; #endif #ifndef _XBOX bool window_save_positions = settings->bools.video_window_save_positions; unsigned user_width = width; unsigned user_height = height; if (window_save_positions && !fullscreen) { user_width = g_win32->pos_width; user_height = g_win32->pos_height; } main_window.hwnd = CreateWindowEx(0, msg_hash_to_str(MSG_PROGRAM), msg_hash_to_str(MSG_PROGRAM), style, fullscreen ? mon_rect->left : g_win32->pos_x, fullscreen ? mon_rect->top : g_win32->pos_y, user_width, user_height, NULL, NULL, NULL, data); if (!main_window.hwnd) return false; #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500 /* 2K */ g_win32->taskbar_message = RegisterWindowMessage("TaskbarButtonCreated"); memset(¬ification_filter, 0, sizeof(notification_filter)); notification_filter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE); notification_filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; notification_filter.dbcc_classguid = GUID_DEVINTERFACE_HID; notification_handler = RegisterDeviceNotification( main_window.hwnd, ¬ification_filter, DEVICE_NOTIFY_WINDOW_HANDLE); if (!notification_handler) RARCH_ERR("Error registering for notifications\n"); #endif video_driver_display_type_set(RARCH_DISPLAY_WIN32); video_driver_display_set(0); video_driver_display_userdata_set((uintptr_t)&main_window); video_driver_window_set((uintptr_t)main_window.hwnd); #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500 /* 2K */ if (!window_show_decor) SetWindowLongPtr(main_window.hwnd, GWL_STYLE, WS_POPUP); /* Windows 2000 and above use layered windows to enable transparency */ if (window_opacity < 100) { SetWindowLongPtr(main_window.hwnd, GWL_EXSTYLE, GetWindowLongPtr(main_window.hwnd, GWL_EXSTYLE) | WS_EX_LAYERED); SetLayeredWindowAttributes(main_window.hwnd, 0, (255 * window_opacity) / 100, LWA_ALPHA); } #endif #endif return true; } #endif bool win32_get_metrics(void *data, enum display_metric_types type, float *value) { #if !defined(_XBOX) HDC monitor = GetDC(NULL); int pixels_x = GetDeviceCaps(monitor, HORZRES); int pixels_y = GetDeviceCaps(monitor, VERTRES); int physical_width = GetDeviceCaps(monitor, HORZSIZE); int physical_height = GetDeviceCaps(monitor, VERTSIZE); ReleaseDC(NULL, monitor); switch (type) { case DISPLAY_METRIC_PIXEL_WIDTH: *value = pixels_x; return true; case DISPLAY_METRIC_PIXEL_HEIGHT: *value = pixels_y; return true; case DISPLAY_METRIC_MM_WIDTH: *value = physical_width; return true; case DISPLAY_METRIC_MM_HEIGHT: *value = physical_height; return true; case DISPLAY_METRIC_DPI: /* 25.4 mm in an inch. */ *value = 254 * pixels_x / physical_width / 10; return true; case DISPLAY_METRIC_NONE: default: *value = 0; break; } #endif return false; } void win32_monitor_init(void) { win32_common_state_t *g_win32 = (win32_common_state_t*)&win32_st; #if !defined(_XBOX) g_win32->monitor_count = 0; EnumDisplayMonitors(NULL, NULL, win32_monitor_enum_proc, 0); #endif g_win32->quit = false; } static bool win32_monitor_set_fullscreen( unsigned width, unsigned height, unsigned refresh, char *dev_name) { #if !defined(_XBOX) DEVMODE devmode; memset(&devmode, 0, sizeof(devmode)); devmode.dmSize = sizeof(DEVMODE); devmode.dmPelsWidth = width; devmode.dmPelsHeight = height; devmode.dmDisplayFrequency = refresh; devmode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY; RARCH_LOG("Setting fullscreen to %ux%u @ %uHz on device %s.\n", width, height, refresh, dev_name); return win32_change_display_settings(dev_name, &devmode, CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL; #endif } void win32_show_cursor(void *data, bool state) { #if !defined(_XBOX) if (state) while (ShowCursor(TRUE) < 0); else while (ShowCursor(FALSE) >= 0); #endif } void win32_check_window(bool *quit, bool *resize, unsigned *width, unsigned *height) { #if !defined(_XBOX) win32_common_state_t *g_win32 = (win32_common_state_t*)&win32_st; bool video_is_threaded = video_driver_is_threaded(); if (video_is_threaded) ui_companion_win32.application->process_events(); *quit = g_win32->quit; if (g_win32->resized) { *resize = true; *width = g_win32_resize_width; *height = g_win32_resize_height; g_win32->resized = false; } #endif } bool win32_suppress_screensaver(void *data, bool enable) { #if !defined(_XBOX) if (enable) { char tmp[PATH_MAX_LENGTH]; int major = 0; int minor = 0; const frontend_ctx_driver_t *frontend = frontend_get_ptr(); if (!frontend) return false; if (frontend->get_os) frontend->get_os(tmp, sizeof(tmp), &major, &minor); if (major * 100 + minor >= 601) { #if _WIN32_WINNT >= 0x0601 /* Windows 7, 8, 10 codepath */ typedef HANDLE(WINAPI * PowerCreateRequestPtr)(REASON_CONTEXT *context); typedef BOOL(WINAPI * PowerSetRequestPtr)(HANDLE PowerRequest, POWER_REQUEST_TYPE RequestType); PowerCreateRequestPtr powerCreateRequest; PowerSetRequestPtr powerSetRequest; HMODULE kernel32 = GetModuleHandle("kernel32.dll"); if (kernel32) { powerCreateRequest = (PowerCreateRequestPtr)GetProcAddress( kernel32, "PowerCreateRequest"); powerSetRequest = (PowerSetRequestPtr)GetProcAddress( kernel32, "PowerSetRequest"); if (powerCreateRequest && powerSetRequest) { POWER_REQUEST_CONTEXT RequestContext; HANDLE Request; RequestContext.Version = POWER_REQUEST_CONTEXT_VERSION; RequestContext.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING; RequestContext.Reason.SimpleReasonString = (LPWSTR) L"RetroArch running"; Request = powerCreateRequest(&RequestContext); powerSetRequest( Request, PowerRequestDisplayRequired); return true; } } #endif } else if (major * 100 + minor >= 410) { #if _WIN32_WINDOWS >= 0x0410 || _WIN32_WINNT >= 0x0410 /* 98 / 2K / XP / Vista codepath */ SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED); return true; #endif } else { /* 95 / NT codepath */ /* No way to block the screensaver. */ return true; } } #endif return false; } void win32_set_style(MONITORINFOEX *current_mon, HMONITOR *hm_to_use, unsigned *width, unsigned *height, bool fullscreen, bool windowed_full, RECT *rect, RECT *mon_rect, DWORD *style) { #if !defined(_XBOX) win32_common_state_t *g_win32 = (win32_common_state_t*)&win32_st; bool position_set_from_config = false; settings_t *settings = config_get_ptr(); bool video_window_save_positions = settings->bools.video_window_save_positions; float video_refresh = settings->floats.video_refresh_rate; unsigned swap_interval = settings->uints.video_swap_interval; bool bfi = settings->bools.video_black_frame_insertion; unsigned window_position_x = settings->uints.window_position_x; unsigned window_position_y = settings->uints.window_position_y; unsigned window_position_width = settings->uints.window_position_width; unsigned window_position_height = settings->uints.window_position_height; if (fullscreen) { /* Windows only reports the refresh rates for modelines as * an integer, so video_refresh_rate needs to be rounded. Also, account * for black frame insertion using video_refresh_rate set to half * of the display refresh rate, as well as higher vsync swap intervals. */ float refresh_mod = bfi ? 2.0f : 1.0f; unsigned refresh = roundf(video_refresh * refresh_mod * swap_interval); if (windowed_full) { *style = WS_EX_TOPMOST | WS_POPUP; g_win32_resize_width = *width = mon_rect->right - mon_rect->left; g_win32_resize_height = *height = mon_rect->bottom - mon_rect->top; } else { *style = WS_POPUP | WS_VISIBLE; if (!win32_monitor_set_fullscreen(*width, *height, refresh, current_mon->szDevice)) { } /* Display settings might have changed, get new coordinates. */ GetMonitorInfo(*hm_to_use, (LPMONITORINFO)current_mon); *mon_rect = current_mon->rcMonitor; } } else { *style = WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; rect->right = *width; rect->bottom = *height; AdjustWindowRect(rect, *style, FALSE); if (video_window_save_positions) { /* Set position from config */ int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME); int title_bar_height = GetSystemMetrics(SM_CYCAPTION); g_win32->pos_x = window_position_x; g_win32->pos_y = window_position_y; g_win32->pos_width = window_position_width + border_thickness * 2; g_win32->pos_height = window_position_height + border_thickness * 2 + title_bar_height; if (g_win32->pos_width != 0 && g_win32->pos_height != 0) position_set_from_config = true; } if (position_set_from_config) { g_win32_resize_width = *width = g_win32->pos_width; g_win32_resize_height = *height = g_win32->pos_height; } else { g_win32_resize_width = *width = rect->right - rect->left; g_win32_resize_height = *height = rect->bottom - rect->top; } } #endif } void win32_set_window(unsigned *width, unsigned *height, bool fullscreen, bool windowed_full, void *rect_data) { #if !defined(_XBOX) RECT *rect = (RECT*)rect_data; if (!fullscreen || windowed_full) { settings_t *settings = config_get_ptr(); const ui_window_t *window = ui_companion_driver_get_window_ptr(); bool ui_menubar_enable = settings->bools.ui_menubar_enable; if (!fullscreen && ui_menubar_enable) { RECT rc_temp; rc_temp.left = 0; rc_temp.top = 0; rc_temp.right = (LONG)*height; rc_temp.bottom = 0x7FFF; SetMenu(main_window.hwnd, LoadMenu(GetModuleHandle(NULL),MAKEINTRESOURCE(IDR_MENU))); SendMessage(main_window.hwnd, WM_NCCALCSIZE, FALSE, (LPARAM)&rc_temp); g_win32_resize_height = *height += rc_temp.top + rect->top; SetWindowPos(main_window.hwnd, NULL, 0, 0, *width, *height, SWP_NOMOVE); } ShowWindow(main_window.hwnd, SW_RESTORE); UpdateWindow(main_window.hwnd); SetForegroundWindow(main_window.hwnd); if (window) window->set_focused(&main_window); } win32_show_cursor(NULL, !fullscreen); #endif } bool win32_set_video_mode(void *data, unsigned width, unsigned height, bool fullscreen) { #if !defined(_XBOX) DWORD style; MSG msg; RECT mon_rect; RECT rect; MONITORINFOEX current_mon; int res = 0; unsigned mon_id = 0; HMONITOR hm_to_use = NULL; win32_common_state_t *g_win32 = (win32_common_state_t*)&win32_st; settings_t *settings = config_get_ptr(); bool windowed_full = settings->bools.video_windowed_fullscreen; rect.left = 0; rect.top = 0; rect.right = 0; rect.bottom = 0; win32_monitor_info(¤t_mon, &hm_to_use, &mon_id); mon_rect = current_mon.rcMonitor; g_win32_resize_width = width; g_win32_resize_height = height; win32_set_style(¤t_mon, &hm_to_use, &width, &height, fullscreen, windowed_full, &rect, &mon_rect, &style); if (!win32_window_create(data, style, &mon_rect, width, height, fullscreen)) return false; win32_set_window(&width, &height, fullscreen, windowed_full, &rect); /* Wait until context is created (or failed to do so ...). * Please don't remove the (res = ) as GetMessage can return -1. */ while (!g_win32_inited && !g_win32->quit && (res = GetMessage(&msg, main_window.hwnd, 0, 0)) != 0) { if (res == -1) { RARCH_ERR("GetMessage error code %d\n", GetLastError()); break; } TranslateMessage(&msg); DispatchMessage(&msg); } if (g_win32->quit) return false; #endif return true; } #ifdef _XBOX static HANDLE GetFocus(void) { return main_window.hwnd; } static HWND GetForegroundWindow(void) { return main_window.hwnd; } BOOL IsIconic(HWND hwnd) { return FALSE; } #endif bool win32_has_focus(void *data) { if (g_win32_inited) if (GetForegroundWindow() == main_window.hwnd) return true; return false; } HWND win32_get_window(void) { #ifdef _XBOX return NULL; #else return main_window.hwnd; #endif } void win32_window_reset(void) { win32_common_state_t *g_win32 = (win32_common_state_t*)&win32_st; g_win32->quit = false; g_win32_restore_desktop = false; } void win32_destroy_window(void) { #ifndef _XBOX UnregisterClass(msg_hash_to_str(MSG_PROGRAM), GetModuleHandle(NULL)); #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x500 /* 2K */ UnregisterDeviceNotification(notification_handler); #endif #endif main_window.hwnd = NULL; } void win32_get_video_output_prev( unsigned *width, unsigned *height) { DEVMODE dm; unsigned i; bool found = false; unsigned prev_width = 0; unsigned prev_height = 0; unsigned curr_width = 0; unsigned curr_height = 0; if (win32_get_video_output(&dm, -1, sizeof(dm))) { curr_width = dm.dmPelsWidth; curr_height = dm.dmPelsHeight; } for (i = 0; win32_get_video_output(&dm, i, sizeof(dm)); i++) { if ( dm.dmPelsWidth == curr_width && dm.dmPelsHeight == curr_height) { if ( prev_width != curr_width && prev_height != curr_height) { found = true; break; } } prev_width = dm.dmPelsWidth; prev_height = dm.dmPelsHeight; } if (found) { *width = prev_width; *height = prev_height; } } float win32_get_refresh_rate(void *data) { float refresh_rate = 0.0f; #if _WIN32_WINNT >= 0x0601 || _WIN32_WINDOWS >= 0x0601 /* Win 7 */ OSVERSIONINFO version_info; UINT32 TopologyID; unsigned int NumPathArrayElements = 0; unsigned int NumModeInfoArrayElements = 0; DISPLAYCONFIG_PATH_INFO_CUSTOM *PathInfoArray = NULL; DISPLAYCONFIG_MODE_INFO_CUSTOM *ModeInfoArray = NULL; int result = 0; #ifdef HAVE_DYNAMIC static QUERYDISPLAYCONFIG pQueryDisplayConfig; static GETDISPLAYCONFIGBUFFERSIZES pGetDisplayConfigBufferSizes; if (!pQueryDisplayConfig) pQueryDisplayConfig = (QUERYDISPLAYCONFIG)GetProcAddress(GetModuleHandle("user32.dll"), "QueryDisplayConfig"); if (!pGetDisplayConfigBufferSizes) pGetDisplayConfigBufferSizes = (GETDISPLAYCONFIGBUFFERSIZES)GetProcAddress(GetModuleHandle("user32.dll"), "GetDisplayConfigBufferSizes"); #else static QUERYDISPLAYCONFIG pQueryDisplayConfig = QueryDisplayConfig; static GETDISPLAYCONFIGBUFFERSIZES pGetDisplayConfigBufferSizes = GetDisplayConfigBufferSizes; #endif version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (!GetVersionEx(&version_info)) return refresh_rate; if (version_info.dwMajorVersion < 6 || (version_info.dwMajorVersion == 6 && version_info.dwMinorVersion < 1)) return refresh_rate; result = pGetDisplayConfigBufferSizes(QDC_DATABASE_CURRENT, &NumPathArrayElements, &NumModeInfoArrayElements); if (result != ERROR_SUCCESS) return refresh_rate; PathInfoArray = (DISPLAYCONFIG_PATH_INFO_CUSTOM *) malloc(sizeof(DISPLAYCONFIG_PATH_INFO_CUSTOM) * NumPathArrayElements); ModeInfoArray = (DISPLAYCONFIG_MODE_INFO_CUSTOM *) malloc(sizeof(DISPLAYCONFIG_MODE_INFO_CUSTOM) * NumModeInfoArrayElements); result = pQueryDisplayConfig(QDC_DATABASE_CURRENT, &NumPathArrayElements, PathInfoArray, &NumModeInfoArrayElements, ModeInfoArray, &TopologyID); if (result == ERROR_SUCCESS && NumPathArrayElements >= 1) refresh_rate = (float) PathInfoArray[0].targetInfo.refreshRate.Numerator / PathInfoArray[0].targetInfo.refreshRate.Denominator; free(ModeInfoArray); free(PathInfoArray); #endif return refresh_rate; } void win32_get_video_output_next( unsigned *width, unsigned *height) { DEVMODE dm; int i; bool found = false; unsigned curr_width = 0; unsigned curr_height = 0; if (win32_get_video_output(&dm, -1, sizeof(dm))) { curr_width = dm.dmPelsWidth; curr_height = dm.dmPelsHeight; } for (i = 0; win32_get_video_output(&dm, i, sizeof(dm)); i++) { if (found) { *width = dm.dmPelsWidth; *height = dm.dmPelsHeight; break; } if ( dm.dmPelsWidth == curr_width && dm.dmPelsHeight == curr_height) found = true; } } static BOOL win32_internal_get_video_output(DWORD iModeNum, DEVMODE *dm) { #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0500 /* 2K */ return EnumDisplaySettingsEx(NULL, iModeNum, dm, EDS_ROTATEDMODE); #else return EnumDisplaySettings(NULL, iModeNum, dm); #endif } bool win32_get_video_output(DEVMODE *dm, int mode, size_t len) { memset(dm, 0, len); dm->dmSize = len; if (win32_internal_get_video_output((mode == -1) ? ENUM_CURRENT_SETTINGS : mode, dm) == 0) return false; return true; } void win32_get_video_output_size(unsigned *width, unsigned *height) { DEVMODE dm; if (win32_get_video_output(&dm, -1, sizeof(dm))) { *width = dm.dmPelsWidth; *height = dm.dmPelsHeight; } } void win32_setup_pixel_format(HDC hdc, bool supports_gl) { PIXELFORMATDESCRIPTOR pfd = {0}; pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); pfd.nVersion = 1; pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER; pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = 32; pfd.cDepthBits = 0; pfd.cStencilBits = 0; pfd.iLayerType = PFD_MAIN_PLANE; if (supports_gl) pfd.dwFlags |= PFD_SUPPORT_OPENGL; SetPixelFormat(hdc, ChoosePixelFormat(hdc, &pfd), &pfd); }