SDL_windowswindow.c 57 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any damages
  6. arising from the use of this software.
  7. Permission is granted to anyone to use this software for any purpose,
  8. including commercial applications, and to alter it and redistribute it
  9. freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you must not
  11. claim that you wrote the original software. If you use this software
  12. in a product, an acknowledgment in the product documentation would be
  13. appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and must not be
  15. misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source distribution.
  17. */
  18. #include "SDL_internal.h"
  19. #ifdef SDL_VIDEO_DRIVER_WINDOWS
  20. #include "../../core/windows/SDL_windows.h"
  21. #include "../SDL_sysvideo.h"
  22. #include "../SDL_pixels_c.h"
  23. #include "../../events/SDL_keyboard_c.h"
  24. #include "../../events/SDL_mouse_c.h"
  25. #include "../../events/SDL_windowevents_c.h"
  26. #include "../../SDL_hints_c.h"
  27. #include "SDL_windowsvideo.h"
  28. #include "SDL_windowswindow.h"
  29. /* Dropfile support */
  30. #include <shellapi.h>
  31. /* Dark mode support */
  32. #ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
  33. #define DWMWA_USE_IMMERSIVE_DARK_MODE 20
  34. #endif
  35. typedef HRESULT (WINAPI *DwmSetWindowAttribute_t)(HWND hwnd, DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute);
  36. /* Transparent window support */
  37. #ifndef DWM_BB_ENABLE
  38. #define DWM_BB_ENABLE 0x00000001
  39. #endif
  40. #ifndef DWM_BB_BLURREGION
  41. #define DWM_BB_BLURREGION 0x00000002
  42. #endif
  43. typedef struct
  44. {
  45. DWORD flags;
  46. BOOL enable;
  47. HRGN blur_region;
  48. BOOL transition_on_maxed;
  49. } DWM_BLURBEHIND;
  50. typedef HRESULT(WINAPI *DwmEnableBlurBehindWindow_t)(HWND hwnd, const DWM_BLURBEHIND *pBlurBehind);
  51. /* Windows CE compatibility */
  52. #ifndef SWP_NOCOPYBITS
  53. #define SWP_NOCOPYBITS 0
  54. #endif
  55. /* An undocumented message to create a popup system menu
  56. * - wParam is always 0
  57. * - lParam = MAKELONG(x, y) where x and y are the screen coordinates where the menu should be displayed
  58. */
  59. #ifndef WM_POPUPSYSTEMMENU
  60. #define WM_POPUPSYSTEMMENU 0x313
  61. #endif
  62. /* #define HIGHDPI_DEBUG */
  63. /* Fake window to help with DirectInput events. */
  64. HWND SDL_HelperWindow = NULL;
  65. static const TCHAR *SDL_HelperWindowClassName = TEXT("SDLHelperWindowInputCatcher");
  66. static const TCHAR *SDL_HelperWindowName = TEXT("SDLHelperWindowInputMsgWindow");
  67. static ATOM SDL_HelperWindowClass = 0;
  68. /* For borderless Windows, still want the following flag:
  69. - WS_MINIMIZEBOX: window will respond to Windows minimize commands sent to all windows, such as windows key + m, shaking title bar, etc.
  70. Additionally, non-fullscreen windows can add:
  71. - WS_CAPTION: this seems to enable the Windows minimize animation
  72. - WS_SYSMENU: enables system context menu on task bar
  73. This will also cause the task bar to overlap the window and other windowed behaviors, so only use this for windows that shouldn't appear to be fullscreen
  74. */
  75. #define STYLE_BASIC (WS_CLIPSIBLINGS | WS_CLIPCHILDREN)
  76. #define STYLE_FULLSCREEN (WS_POPUP | WS_MINIMIZEBOX)
  77. #define STYLE_BORDERLESS (WS_POPUP | WS_MINIMIZEBOX)
  78. #define STYLE_BORDERLESS_WINDOWED (WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX)
  79. #define STYLE_NORMAL (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX)
  80. #define STYLE_RESIZABLE (WS_THICKFRAME | WS_MAXIMIZEBOX)
  81. #define STYLE_MASK (STYLE_FULLSCREEN | STYLE_BORDERLESS | STYLE_NORMAL | STYLE_RESIZABLE)
  82. static DWORD GetWindowStyle(SDL_Window *window)
  83. {
  84. DWORD style = 0;
  85. if (SDL_WINDOW_IS_POPUP(window)) {
  86. style |= WS_POPUP;
  87. } else if (window->flags & SDL_WINDOW_FULLSCREEN) {
  88. style |= STYLE_FULLSCREEN;
  89. } else {
  90. if (window->flags & SDL_WINDOW_BORDERLESS) {
  91. /* This behavior more closely matches other platform where the window is borderless
  92. but still interacts with the window manager (e.g. task bar shows above it, it can
  93. be resized to fit within usable desktop area, etc.)
  94. */
  95. if (SDL_GetHintBoolean("SDL_BORDERLESS_WINDOWED_STYLE", SDL_TRUE)) {
  96. style |= STYLE_BORDERLESS_WINDOWED;
  97. } else {
  98. style |= STYLE_BORDERLESS;
  99. }
  100. } else {
  101. style |= STYLE_NORMAL;
  102. }
  103. if (window->flags & SDL_WINDOW_RESIZABLE) {
  104. /* You can have a borderless resizable window, but Windows doesn't always draw it correctly,
  105. see https://bugzilla.libsdl.org/show_bug.cgi?id=4466
  106. */
  107. if (!(window->flags & SDL_WINDOW_BORDERLESS) ||
  108. SDL_GetHintBoolean("SDL_BORDERLESS_RESIZABLE_STYLE", SDL_FALSE)) {
  109. style |= STYLE_RESIZABLE;
  110. }
  111. }
  112. /* Need to set initialize minimize style, or when we call ShowWindow with WS_MINIMIZE it will activate a random window */
  113. if (window->flags & SDL_WINDOW_MINIMIZED) {
  114. style |= WS_MINIMIZE;
  115. }
  116. }
  117. return style;
  118. }
  119. static DWORD GetWindowStyleEx(SDL_Window *window)
  120. {
  121. DWORD style = 0;
  122. if (SDL_WINDOW_IS_POPUP(window) || (window->flags & SDL_WINDOW_UTILITY)) {
  123. style |= WS_EX_TOOLWINDOW;
  124. }
  125. if (SDL_WINDOW_IS_POPUP(window) || (window->flags & SDL_WINDOW_NOT_FOCUSABLE)) {
  126. style |= WS_EX_NOACTIVATE;
  127. }
  128. return style;
  129. }
  130. /**
  131. * Returns arguments to pass to SetWindowPos - the window rect, including frame, in Windows coordinates.
  132. * Can be called before we have a HWND.
  133. */
  134. static int WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, DWORD styleEx, BOOL menu, int *x, int *y, int *width, int *height, SDL_WindowRect rect_type)
  135. {
  136. SDL_VideoData *videodata = SDL_GetVideoDevice() ? SDL_GetVideoDevice()->driverdata : NULL;
  137. RECT rect;
  138. /* Client rect, in points */
  139. switch (rect_type) {
  140. case SDL_WINDOWRECT_CURRENT:
  141. SDL_RelativeToGlobalForWindow(window, window->x, window->y, x, y);
  142. *width = window->w;
  143. *height = window->h;
  144. break;
  145. case SDL_WINDOWRECT_WINDOWED:
  146. SDL_RelativeToGlobalForWindow(window, window->windowed.x, window->windowed.y, x, y);
  147. *width = window->windowed.w;
  148. *height = window->windowed.h;
  149. break;
  150. case SDL_WINDOWRECT_FLOATING:
  151. SDL_RelativeToGlobalForWindow(window, window->floating.x, window->floating.y, x, y);
  152. *width = window->floating.w;
  153. *height = window->floating.h;
  154. break;
  155. default:
  156. /* Should never be here */
  157. SDL_assert_release(SDL_FALSE);
  158. }
  159. /* Copy the client size in pixels into this rect structure,
  160. which we'll then adjust with AdjustWindowRectEx */
  161. rect.left = 0;
  162. rect.top = 0;
  163. rect.right = *width;
  164. rect.bottom = *height;
  165. /* borderless windows will have WM_NCCALCSIZE return 0 for the non-client area. When this happens, it looks like windows will send a resize message
  166. expanding the window client area to the previous window + chrome size, so shouldn't need to adjust the window size for the set styles.
  167. */
  168. if (!(window->flags & SDL_WINDOW_BORDERLESS)) {
  169. #if defined(__XBOXONE__) || defined(__XBOXSERIES__)
  170. AdjustWindowRectEx(&rect, style, menu, 0);
  171. #else
  172. if (WIN_IsPerMonitorV2DPIAware(SDL_GetVideoDevice())) {
  173. /* With per-monitor v2, the window border/titlebar size depend on the DPI, so we need to call AdjustWindowRectExForDpi instead of
  174. AdjustWindowRectEx. */
  175. if (videodata) {
  176. UINT frame_dpi;
  177. SDL_WindowData *data = window->driverdata;
  178. frame_dpi = (data && videodata->GetDpiForWindow) ? videodata->GetDpiForWindow(data->hwnd) : USER_DEFAULT_SCREEN_DPI;
  179. if (videodata->AdjustWindowRectExForDpi(&rect, style, menu, styleEx, frame_dpi) == 0) {
  180. return WIN_SetError("AdjustWindowRectExForDpi()");
  181. }
  182. }
  183. } else {
  184. if (AdjustWindowRectEx(&rect, style, menu, styleEx) == 0) {
  185. return WIN_SetError("AdjustWindowRectEx()");
  186. }
  187. }
  188. #endif
  189. }
  190. /* Final rect in Windows screen space, including the frame */
  191. *x += rect.left;
  192. *y += rect.top;
  193. *width = (rect.right - rect.left);
  194. *height = (rect.bottom - rect.top);
  195. #ifdef HIGHDPI_DEBUG
  196. SDL_Log("WIN_AdjustWindowRectWithStyle: in: %d, %d, %dx%d, returning: %d, %d, %dx%d, used dpi %d for frame calculation",
  197. (rect_type == SDL_WINDOWRECT_FLOATING ? window->floating.x : rect_type == SDL_WINDOWRECT_WINDOWED ? window->windowed.x : window->x),
  198. (rect_type == SDL_WINDOWRECT_FLOATING ? window->floating.y : rect_type == SDL_WINDOWRECT_WINDOWED ? window->windowed.y : window->y),
  199. (rect_type == SDL_WINDOWRECT_FLOATING ? window->floating.w : rect_type == SDL_WINDOWRECT_WINDOWED ? window->windowed.w : window->w),
  200. (rect_type == SDL_WINDOWRECT_FLOATING ? window->floating.h : rect_type == SDL_WINDOWRECT_WINDOWED ? window->windowed.h : window->h),
  201. *x, *y, *width, *height, frame_dpi);
  202. #endif
  203. return 0;
  204. }
  205. int WIN_AdjustWindowRect(SDL_Window *window, int *x, int *y, int *width, int *height, SDL_WindowRect rect_type)
  206. {
  207. SDL_WindowData *data = window->driverdata;
  208. HWND hwnd = data->hwnd;
  209. DWORD style, styleEx;
  210. BOOL menu;
  211. style = GetWindowLong(hwnd, GWL_STYLE);
  212. styleEx = GetWindowLong(hwnd, GWL_EXSTYLE);
  213. #if defined(__XBOXONE__) || defined(__XBOXSERIES__)
  214. menu = FALSE;
  215. #else
  216. menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL);
  217. #endif
  218. return WIN_AdjustWindowRectWithStyle(window, style, styleEx, menu, x, y, width, height, rect_type);
  219. }
  220. int WIN_AdjustWindowRectForHWND(HWND hwnd, LPRECT lpRect, UINT frame_dpi)
  221. {
  222. SDL_VideoDevice *videodevice = SDL_GetVideoDevice();
  223. SDL_VideoData *videodata = videodevice ? videodevice->driverdata : NULL;
  224. DWORD style, styleEx;
  225. BOOL menu;
  226. style = GetWindowLong(hwnd, GWL_STYLE);
  227. styleEx = GetWindowLong(hwnd, GWL_EXSTYLE);
  228. #if defined(__XBOXONE__) || defined(__XBOXSERIES__)
  229. menu = FALSE;
  230. #else
  231. menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL);
  232. #endif
  233. #if defined(__XBOXONE__) || defined(__XBOXSERIES__)
  234. AdjustWindowRectEx(lpRect, style, menu, styleEx);
  235. #else
  236. if (WIN_IsPerMonitorV2DPIAware(videodevice)) {
  237. /* With per-monitor v2, the window border/titlebar size depend on the DPI, so we need to call AdjustWindowRectExForDpi instead of AdjustWindowRectEx. */
  238. if (!frame_dpi) {
  239. frame_dpi = videodata->GetDpiForWindow ? videodata->GetDpiForWindow(hwnd) : USER_DEFAULT_SCREEN_DPI;
  240. }
  241. if (!videodata->AdjustWindowRectExForDpi(lpRect, style, menu, styleEx, frame_dpi)) {
  242. return WIN_SetError("AdjustWindowRectExForDpi()");
  243. }
  244. } else {
  245. if (!AdjustWindowRectEx(lpRect, style, menu, styleEx)) {
  246. return WIN_SetError("AdjustWindowRectEx()");
  247. }
  248. }
  249. #endif
  250. return 0;
  251. }
  252. int WIN_SetWindowPositionInternal(SDL_Window *window, UINT flags, SDL_WindowRect rect_type)
  253. {
  254. SDL_Window *child_window;
  255. SDL_WindowData *data = window->driverdata;
  256. HWND hwnd = data->hwnd;
  257. HWND top;
  258. int x, y;
  259. int w, h;
  260. int result = 0;
  261. /* Figure out what the window area will be */
  262. if (SDL_ShouldAllowTopmost() && (window->flags & SDL_WINDOW_ALWAYS_ON_TOP)) {
  263. top = HWND_TOPMOST;
  264. } else {
  265. top = HWND_NOTOPMOST;
  266. }
  267. WIN_AdjustWindowRect(window, &x, &y, &w, &h, rect_type);
  268. data->expected_resize = SDL_TRUE;
  269. if (SetWindowPos(hwnd, top, x, y, w, h, flags) == 0) {
  270. result = WIN_SetError("SetWindowPos()");
  271. }
  272. data->expected_resize = SDL_FALSE;
  273. /* Update any child windows */
  274. for (child_window = window->first_child; child_window; child_window = child_window->next_sibling) {
  275. if (WIN_SetWindowPositionInternal(child_window, flags, SDL_WINDOWRECT_CURRENT) < 0) {
  276. result = -1;
  277. }
  278. }
  279. return result;
  280. }
  281. static void SDLCALL WIN_MouseRelativeModeCenterChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
  282. {
  283. SDL_WindowData *data = (SDL_WindowData *)userdata;
  284. data->mouse_relative_mode_center = SDL_GetStringBoolean(hint, SDL_TRUE);
  285. }
  286. static int SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, HWND hwnd, HWND parent)
  287. {
  288. SDL_VideoData *videodata = _this->driverdata;
  289. SDL_WindowData *data;
  290. /* Allocate the window data */
  291. data = (SDL_WindowData *)SDL_calloc(1, sizeof(*data));
  292. if (!data) {
  293. return -1;
  294. }
  295. data->window = window;
  296. data->hwnd = hwnd;
  297. data->parent = parent;
  298. #if defined(__XBOXONE__) || defined(__XBOXSERIES__)
  299. data->hdc = (HDC)data->hwnd;
  300. #else
  301. data->hdc = GetDC(hwnd);
  302. #endif
  303. data->hinstance = (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
  304. data->mouse_button_flags = (WPARAM)-1;
  305. data->last_pointer_update = (LPARAM)-1;
  306. data->videodata = videodata;
  307. data->initializing = SDL_TRUE;
  308. data->last_displayID = window->last_displayID;
  309. if (SDL_GetHintBoolean("SDL_WINDOW_RETAIN_CONTENT", SDL_FALSE)) {
  310. data->copybits_flag = 0;
  311. } else {
  312. data->copybits_flag = SWP_NOCOPYBITS;
  313. }
  314. #ifdef HIGHDPI_DEBUG
  315. SDL_Log("SetupWindowData: initialized data->scaling_dpi to %d", data->scaling_dpi);
  316. #endif
  317. SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER, WIN_MouseRelativeModeCenterChanged, data);
  318. window->driverdata = data;
  319. #if !defined(__XBOXONE__) && !defined(__XBOXSERIES__)
  320. /* Associate the data with the window */
  321. if (!SetProp(hwnd, TEXT("SDL_WindowData"), data)) {
  322. ReleaseDC(hwnd, data->hdc);
  323. SDL_free(data);
  324. return WIN_SetError("SetProp() failed");
  325. }
  326. #endif
  327. /* Set up the window proc function */
  328. #ifdef GWLP_WNDPROC
  329. data->wndproc = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_WNDPROC);
  330. if (data->wndproc == WIN_WindowProc) {
  331. data->wndproc = NULL;
  332. } else {
  333. SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)WIN_WindowProc);
  334. }
  335. #else
  336. data->wndproc = (WNDPROC)GetWindowLong(hwnd, GWL_WNDPROC);
  337. if (data->wndproc == WIN_WindowProc) {
  338. data->wndproc = NULL;
  339. } else {
  340. SetWindowLong(hwnd, GWL_WNDPROC, (LONG_PTR)WIN_WindowProc);
  341. }
  342. #endif
  343. /* Fill in the SDL window with the window data */
  344. {
  345. RECT rect;
  346. if (GetClientRect(hwnd, &rect)) {
  347. int w = rect.right;
  348. int h = rect.bottom;
  349. if (window->flags & SDL_WINDOW_EXTERNAL) {
  350. window->floating.w = window->windowed.w = window->w = w;
  351. window->floating.h = window->windowed.h = window->h = h;
  352. } else if ((window->windowed.w && window->windowed.w != w) || (window->windowed.h && window->windowed.h != h)) {
  353. /* We tried to create a window larger than the desktop and Windows didn't allow it. Override! */
  354. int x, y;
  355. /* Figure out what the window area will be */
  356. WIN_AdjustWindowRect(window, &x, &y, &w, &h, SDL_WINDOWRECT_FLOATING);
  357. data->expected_resize = SDL_TRUE;
  358. SetWindowPos(hwnd, NULL, x, y, w, h, data->copybits_flag | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE);
  359. data->expected_resize = SDL_FALSE;
  360. } else {
  361. window->w = w;
  362. window->h = h;
  363. }
  364. }
  365. }
  366. #if !defined(__XBOXONE__) && !defined(__XBOXSERIES__)
  367. {
  368. POINT point;
  369. point.x = 0;
  370. point.y = 0;
  371. if (ClientToScreen(hwnd, &point)) {
  372. if (window->flags & SDL_WINDOW_EXTERNAL) {
  373. window->floating.x = window->windowed.x = point.x;
  374. window->floating.y = window->windowed.y = point.y;
  375. }
  376. window->x = point.x;
  377. window->y = point.y;
  378. }
  379. }
  380. WIN_UpdateWindowICCProfile(window, SDL_FALSE);
  381. #endif
  382. {
  383. DWORD style = GetWindowLong(hwnd, GWL_STYLE);
  384. if (style & WS_VISIBLE) {
  385. window->flags &= ~SDL_WINDOW_HIDDEN;
  386. } else {
  387. window->flags |= SDL_WINDOW_HIDDEN;
  388. }
  389. if (style & WS_POPUP) {
  390. window->flags |= SDL_WINDOW_BORDERLESS;
  391. } else {
  392. window->flags &= ~SDL_WINDOW_BORDERLESS;
  393. }
  394. if (style & WS_THICKFRAME) {
  395. window->flags |= SDL_WINDOW_RESIZABLE;
  396. } else {
  397. window->flags &= ~SDL_WINDOW_RESIZABLE;
  398. }
  399. #ifdef WS_MAXIMIZE
  400. if (style & WS_MAXIMIZE) {
  401. window->flags |= SDL_WINDOW_MAXIMIZED;
  402. } else
  403. #endif
  404. {
  405. window->flags &= ~SDL_WINDOW_MAXIMIZED;
  406. }
  407. #ifdef WS_MINIMIZE
  408. if (style & WS_MINIMIZE) {
  409. window->flags |= SDL_WINDOW_MINIMIZED;
  410. } else
  411. #endif
  412. {
  413. window->flags &= ~SDL_WINDOW_MINIMIZED;
  414. }
  415. }
  416. #if defined(__XBOXONE__) || defined(__XBOXSERIES__)
  417. window->flags |= SDL_WINDOW_INPUT_FOCUS;
  418. #else
  419. if (GetFocus() == hwnd) {
  420. window->flags |= SDL_WINDOW_INPUT_FOCUS;
  421. SDL_SetKeyboardFocus(window);
  422. WIN_UpdateClipCursor(window);
  423. }
  424. #endif
  425. if (window->flags & SDL_WINDOW_ALWAYS_ON_TOP) {
  426. WIN_SetWindowAlwaysOnTop(_this, window, SDL_TRUE);
  427. } else {
  428. WIN_SetWindowAlwaysOnTop(_this, window, SDL_FALSE);
  429. }
  430. #if !defined(__XBOXONE__) && !defined(__XBOXSERIES__)
  431. /* Enable multi-touch */
  432. if (videodata->RegisterTouchWindow) {
  433. videodata->RegisterTouchWindow(hwnd, (TWF_FINETOUCH | TWF_WANTPALM));
  434. }
  435. #endif
  436. if (data->parent && !window->parent) {
  437. data->destroy_parent_with_window = SDL_TRUE;
  438. }
  439. data->initializing = SDL_FALSE;
  440. #if !defined(__XBOXONE__) && !defined(__XBOXSERIES__)
  441. if (window->flags & SDL_WINDOW_EXTERNAL) {
  442. /* Query the title from the existing window */
  443. LPTSTR title;
  444. int titleLen;
  445. SDL_bool isstack;
  446. titleLen = GetWindowTextLength(hwnd);
  447. title = SDL_small_alloc(TCHAR, titleLen + 1, &isstack);
  448. if (title) {
  449. titleLen = GetWindowText(hwnd, title, titleLen + 1);
  450. } else {
  451. titleLen = 0;
  452. }
  453. if (titleLen > 0) {
  454. window->title = WIN_StringToUTF8(title);
  455. }
  456. if (title) {
  457. SDL_small_free(title, isstack);
  458. }
  459. }
  460. #endif /*!defined(__XBOXONE__) && !defined(__XBOXSERIES__)*/
  461. SDL_PropertiesID props = SDL_GetWindowProperties(window);
  462. SDL_SetProperty(props, SDL_PROPERTY_WINDOW_WIN32_HWND_POINTER, data->hwnd);
  463. SDL_SetProperty(props, SDL_PROPERTY_WINDOW_WIN32_HDC_POINTER, data->hdc);
  464. SDL_SetProperty(props, SDL_PROPERTY_WINDOW_WIN32_INSTANCE_POINTER, data->hinstance);
  465. /* All done! */
  466. return 0;
  467. }
  468. static void CleanupWindowData(SDL_VideoDevice *_this, SDL_Window *window)
  469. {
  470. SDL_WindowData *data = window->driverdata;
  471. if (data) {
  472. SDL_DelHintCallback(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER, WIN_MouseRelativeModeCenterChanged, data);
  473. #if !defined(__XBOXONE__) && !defined(__XBOXSERIES__)
  474. if (data->ICMFileName) {
  475. SDL_free(data->ICMFileName);
  476. }
  477. if (data->keyboard_hook) {
  478. UnhookWindowsHookEx(data->keyboard_hook);
  479. }
  480. ReleaseDC(data->hwnd, data->hdc);
  481. RemoveProp(data->hwnd, TEXT("SDL_WindowData"));
  482. #endif
  483. if (!(window->flags & SDL_WINDOW_EXTERNAL)) {
  484. DestroyWindow(data->hwnd);
  485. if (data->destroy_parent_with_window && data->parent) {
  486. DestroyWindow(data->parent);
  487. }
  488. } else {
  489. /* Restore any original event handler... */
  490. if (data->wndproc) {
  491. #ifdef GWLP_WNDPROC
  492. SetWindowLongPtr(data->hwnd, GWLP_WNDPROC,
  493. (LONG_PTR)data->wndproc);
  494. #else
  495. SetWindowLong(data->hwnd, GWL_WNDPROC,
  496. (LONG_PTR)data->wndproc);
  497. #endif
  498. }
  499. }
  500. #if !defined(__XBOXONE__) && !defined(__XBOXSERIES__)
  501. SDL_free(data->rawinput);
  502. #endif /*!defined(__XBOXONE__) && !defined(__XBOXSERIES__)*/
  503. SDL_free(data);
  504. }
  505. window->driverdata = NULL;
  506. }
  507. static void WIN_ConstrainPopup(SDL_Window *window)
  508. {
  509. /* Clamp popup windows to the output borders */
  510. if (SDL_WINDOW_IS_POPUP(window)) {
  511. SDL_Window *w;
  512. SDL_DisplayID displayID;
  513. SDL_Rect rect;
  514. int abs_x = window->floating.x;
  515. int abs_y = window->floating.y;
  516. int offset_x = 0, offset_y = 0;
  517. /* Calculate the total offset from the parents */
  518. for (w = window->parent; w->parent; w = w->parent) {
  519. offset_x += w->x;
  520. offset_y += w->y;
  521. }
  522. offset_x += w->x;
  523. offset_y += w->y;
  524. abs_x += offset_x;
  525. abs_y += offset_y;
  526. /* Constrain the popup window to the display of the toplevel parent */
  527. displayID = SDL_GetDisplayForWindow(w);
  528. SDL_GetDisplayBounds(displayID, &rect);
  529. if (abs_x + window->floating.w > rect.x + rect.w) {
  530. abs_x -= (abs_x + window->floating.w) - (rect.x + rect.w);
  531. }
  532. if (abs_y + window->floating.h > rect.y + rect.h) {
  533. abs_y -= (abs_y + window->floating.h) - (rect.y + rect.h);
  534. }
  535. abs_x = SDL_max(abs_x, rect.x);
  536. abs_y = SDL_max(abs_y, rect.y);
  537. window->floating.x = abs_x - offset_x;
  538. window->floating.y = abs_y - offset_y;
  539. }
  540. }
  541. static void WIN_SetKeyboardFocus(SDL_Window *window)
  542. {
  543. SDL_Window *topmost = window;
  544. /* Find the topmost parent */
  545. while (topmost->parent) {
  546. topmost = topmost->parent;
  547. }
  548. topmost->driverdata->keyboard_focus = window;
  549. SDL_SetKeyboardFocus(window);
  550. }
  551. int WIN_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props)
  552. {
  553. HWND hwnd = (HWND)SDL_GetProperty(create_props, SDL_PROPERTY_WINDOW_CREATE_WIN32_HWND_POINTER, SDL_GetProperty(create_props, "sdl2-compat.external_window", NULL));
  554. HWND parent = NULL;
  555. if (hwnd) {
  556. window->flags |= SDL_WINDOW_EXTERNAL;
  557. if (SetupWindowData(_this, window, hwnd, parent) < 0) {
  558. return -1;
  559. }
  560. } else {
  561. DWORD style = STYLE_BASIC;
  562. DWORD styleEx = 0;
  563. int x, y;
  564. int w, h;
  565. if (SDL_WINDOW_IS_POPUP(window)) {
  566. parent = window->parent->driverdata->hwnd;
  567. } else if (window->flags & SDL_WINDOW_UTILITY) {
  568. parent = CreateWindow(SDL_Appname, TEXT(""), STYLE_BASIC, 0, 0, 32, 32, NULL, NULL, SDL_Instance, NULL);
  569. }
  570. style |= GetWindowStyle(window);
  571. styleEx |= GetWindowStyleEx(window);
  572. /* Figure out what the window area will be */
  573. WIN_ConstrainPopup(window);
  574. WIN_AdjustWindowRectWithStyle(window, style, styleEx, FALSE, &x, &y, &w, &h, SDL_WINDOWRECT_FLOATING);
  575. hwnd = CreateWindowEx(styleEx, SDL_Appname, TEXT(""), style,
  576. x, y, w, h, parent, NULL, SDL_Instance, NULL);
  577. if (!hwnd) {
  578. return WIN_SetError("Couldn't create window");
  579. }
  580. WIN_UpdateDarkModeForHWND(hwnd);
  581. WIN_PumpEvents(_this);
  582. if (SetupWindowData(_this, window, hwnd, parent) < 0) {
  583. DestroyWindow(hwnd);
  584. if (parent) {
  585. DestroyWindow(parent);
  586. }
  587. return -1;
  588. }
  589. /* Inform Windows of the frame change so we can respond to WM_NCCALCSIZE */
  590. SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE);
  591. if (window->flags & SDL_WINDOW_MINIMIZED) {
  592. /* TODO: We have to clear SDL_WINDOW_HIDDEN here to ensure the window flags match the window state. The
  593. window is already shown after this and windows with WS_MINIMIZE do not generate a WM_SHOWWINDOW. This
  594. means you can't currently create a window that is initially hidden and is minimized when shown.
  595. */
  596. window->flags &= ~SDL_WINDOW_HIDDEN;
  597. ShowWindow(hwnd, SW_SHOWMINNOACTIVE);
  598. }
  599. }
  600. #if !defined(__XBOXONE__) && !defined(__XBOXSERIES__)
  601. /* FIXME: does not work on all hardware configurations with different renders (i.e. hybrid GPUs) */
  602. if (window->flags & SDL_WINDOW_TRANSPARENT) {
  603. void *handle = SDL_LoadObject("dwmapi.dll");
  604. if (handle) {
  605. DwmEnableBlurBehindWindow_t DwmEnableBlurBehindWindowFunc = (DwmEnableBlurBehindWindow_t)SDL_LoadFunction(handle, "DwmEnableBlurBehindWindow");
  606. if (DwmEnableBlurBehindWindowFunc) {
  607. /* The region indicates which part of the window will be blurred and rest will be transparent. This
  608. is because the alpha value of the window will be used for non-blurred areas
  609. We can use (-1, -1, 0, 0) boundary to make sure no pixels are being blurred
  610. */
  611. HRGN rgn = CreateRectRgn(-1, -1, 0, 0);
  612. DWM_BLURBEHIND bb = {DWM_BB_ENABLE | DWM_BB_BLURREGION, TRUE, rgn, FALSE};
  613. DwmEnableBlurBehindWindowFunc(hwnd, &bb);
  614. DeleteObject(rgn);
  615. }
  616. SDL_UnloadObject(handle);
  617. }
  618. }
  619. HWND share_hwnd = (HWND)SDL_GetProperty(create_props, SDL_PROPERTY_WINDOW_CREATE_WIN32_PIXEL_FORMAT_HWND_POINTER, NULL);
  620. if (share_hwnd) {
  621. HDC hdc = GetDC(share_hwnd);
  622. int pixel_format = GetPixelFormat(hdc);
  623. PIXELFORMATDESCRIPTOR pfd;
  624. SDL_zero(pfd);
  625. DescribePixelFormat(hdc, pixel_format, sizeof(pfd), &pfd);
  626. ReleaseDC(share_hwnd, hdc);
  627. if (!SetPixelFormat(window->driverdata->hdc, pixel_format, &pfd)) {
  628. WIN_DestroyWindow(_this, window);
  629. return WIN_SetError("SetPixelFormat()");
  630. }
  631. } else {
  632. if (!(window->flags & SDL_WINDOW_OPENGL)) {
  633. return 0;
  634. }
  635. /* The rest of this macro mess is for OpenGL or OpenGL ES windows */
  636. #ifdef SDL_VIDEO_OPENGL_ES2
  637. if ((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES ||
  638. SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, SDL_FALSE)) &&
  639. #ifdef SDL_VIDEO_OPENGL_WGL
  640. (!_this->gl_data || WIN_GL_UseEGL(_this))
  641. #endif /* SDL_VIDEO_OPENGL_WGL */
  642. ) {
  643. #ifdef SDL_VIDEO_OPENGL_EGL
  644. if (WIN_GLES_SetupWindow(_this, window) < 0) {
  645. WIN_DestroyWindow(_this, window);
  646. return -1;
  647. }
  648. return 0;
  649. #else
  650. return SDL_SetError("Could not create GLES window surface (EGL support not configured)");
  651. #endif /* SDL_VIDEO_OPENGL_EGL */
  652. }
  653. #endif /* SDL_VIDEO_OPENGL_ES2 */
  654. #ifdef SDL_VIDEO_OPENGL_WGL
  655. if (WIN_GL_SetupWindow(_this, window) < 0) {
  656. WIN_DestroyWindow(_this, window);
  657. return -1;
  658. }
  659. #else
  660. return SDL_SetError("Could not create GL window (WGL support not configured)");
  661. #endif
  662. }
  663. #endif /*!defined(__XBOXONE__) && !defined(__XBOXSERIES__)*/
  664. return 0;
  665. }
  666. void WIN_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window)
  667. {
  668. #if !defined(__XBOXONE__) && !defined(__XBOXSERIES__)
  669. HWND hwnd = window->driverdata->hwnd;
  670. LPTSTR title = WIN_UTF8ToString(window->title);
  671. SetWindowText(hwnd, title);
  672. SDL_free(title);
  673. #endif
  674. }
  675. int WIN_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon)
  676. {
  677. #if !defined(__XBOXONE__) && !defined(__XBOXSERIES__)
  678. HWND hwnd = window->driverdata->hwnd;
  679. HICON hicon = NULL;
  680. BYTE *icon_bmp;
  681. int icon_len, mask_len, row_len, y;
  682. BITMAPINFOHEADER *bmi;
  683. Uint8 *dst;
  684. SDL_bool isstack;
  685. int retval = 0;
  686. /* Create temporary buffer for ICONIMAGE structure */
  687. SDL_COMPILE_TIME_ASSERT(WIN_SetWindowIcon_uses_BITMAPINFOHEADER_to_prepare_an_ICONIMAGE, sizeof(BITMAPINFOHEADER) == 40);
  688. mask_len = (icon->h * (icon->w + 7) / 8);
  689. icon_len = sizeof(BITMAPINFOHEADER) + icon->h * icon->w * sizeof(Uint32) + mask_len;
  690. icon_bmp = SDL_small_alloc(BYTE, icon_len, &isstack);
  691. /* Write the BITMAPINFO header */
  692. bmi = (BITMAPINFOHEADER *)icon_bmp;
  693. bmi->biSize = SDL_SwapLE32(sizeof(BITMAPINFOHEADER));
  694. bmi->biWidth = SDL_SwapLE32(icon->w);
  695. bmi->biHeight = SDL_SwapLE32(icon->h * 2);
  696. bmi->biPlanes = SDL_SwapLE16(1);
  697. bmi->biBitCount = SDL_SwapLE16(32);
  698. bmi->biCompression = SDL_SwapLE32(BI_RGB);
  699. bmi->biSizeImage = SDL_SwapLE32(icon->h * icon->w * sizeof(Uint32));
  700. bmi->biXPelsPerMeter = SDL_SwapLE32(0);
  701. bmi->biYPelsPerMeter = SDL_SwapLE32(0);
  702. bmi->biClrUsed = SDL_SwapLE32(0);
  703. bmi->biClrImportant = SDL_SwapLE32(0);
  704. /* Write the pixels upside down into the bitmap buffer */
  705. SDL_assert(icon->format->format == SDL_PIXELFORMAT_ARGB8888);
  706. dst = &icon_bmp[sizeof(BITMAPINFOHEADER)];
  707. row_len = icon->w * sizeof(Uint32);
  708. y = icon->h;
  709. while (y--) {
  710. Uint8 *src = (Uint8 *)icon->pixels + y * icon->pitch;
  711. SDL_memcpy(dst, src, row_len);
  712. dst += row_len;
  713. }
  714. /* Write the mask */
  715. SDL_memset(icon_bmp + icon_len - mask_len, 0xFF, mask_len);
  716. hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000);
  717. SDL_small_free(icon_bmp, isstack);
  718. if (!hicon) {
  719. retval = SDL_SetError("SetWindowIcon() failed, error %08X", (unsigned int)GetLastError());
  720. }
  721. /* Set the icon for the window */
  722. SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hicon);
  723. /* Set the icon in the task manager (should we do this?) */
  724. SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hicon);
  725. return retval;
  726. #else
  727. return SDL_Unsupported();
  728. #endif
  729. }
  730. int WIN_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window)
  731. {
  732. /* HighDPI support: removed SWP_NOSIZE. If the move results in a DPI change, we need to allow
  733. * the window to resize (e.g. AdjustWindowRectExForDpi frame sizes are different).
  734. */
  735. if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
  736. if (!(window->flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED))) {
  737. WIN_ConstrainPopup(window);
  738. return WIN_SetWindowPositionInternal(window,
  739. window->driverdata->copybits_flag | SWP_NOZORDER | SWP_NOOWNERZORDER |
  740. SWP_NOACTIVATE, SDL_WINDOWRECT_FLOATING);
  741. } else {
  742. window->driverdata->floating_rect_pending = SDL_TRUE;
  743. }
  744. } else {
  745. return SDL_UpdateFullscreenMode(window, SDL_TRUE, SDL_TRUE);
  746. }
  747. return 0;
  748. }
  749. void WIN_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
  750. {
  751. if (!(window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED))) {
  752. WIN_SetWindowPositionInternal(window, window->driverdata->copybits_flag | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE, SDL_WINDOWRECT_FLOATING);
  753. } else {
  754. window->driverdata->floating_rect_pending = SDL_TRUE;
  755. }
  756. }
  757. int WIN_GetWindowBordersSize(SDL_VideoDevice *_this, SDL_Window *window, int *top, int *left, int *bottom, int *right)
  758. {
  759. #if defined(__XBOXONE__) || defined(__XBOXSERIES__)
  760. HWND hwnd = window->driverdata->hwnd;
  761. RECT rcClient;
  762. /* rcClient stores the size of the inner window, while rcWindow stores the outer size relative to the top-left
  763. * screen position; so the top/left values of rcClient are always {0,0} and bottom/right are {height,width} */
  764. GetClientRect(hwnd, &rcClient);
  765. *top = rcClient.top;
  766. *left = rcClient.left;
  767. *bottom = rcClient.bottom;
  768. *right = rcClient.right;
  769. return 0;
  770. #else /*!defined(__XBOXONE__) && !defined(__XBOXSERIES__)*/
  771. HWND hwnd = window->driverdata->hwnd;
  772. RECT rcClient, rcWindow;
  773. POINT ptDiff;
  774. /* rcClient stores the size of the inner window, while rcWindow stores the outer size relative to the top-left
  775. * screen position; so the top/left values of rcClient are always {0,0} and bottom/right are {height,width} */
  776. if (!GetClientRect(hwnd, &rcClient)) {
  777. return SDL_SetError("GetClientRect() failed, error %08X", (unsigned int)GetLastError());
  778. }
  779. if (!GetWindowRect(hwnd, &rcWindow)) {
  780. return SDL_SetError("GetWindowRect() failed, error %08X", (unsigned int)GetLastError());
  781. }
  782. /* convert the top/left values to make them relative to
  783. * the window; they will end up being slightly negative */
  784. ptDiff.y = rcWindow.top;
  785. ptDiff.x = rcWindow.left;
  786. if (!ScreenToClient(hwnd, &ptDiff)) {
  787. return SDL_SetError("ScreenToClient() failed, error %08X", (unsigned int)GetLastError());
  788. }
  789. rcWindow.top = ptDiff.y;
  790. rcWindow.left = ptDiff.x;
  791. /* convert the bottom/right values to make them relative to the window,
  792. * these will be slightly bigger than the inner width/height */
  793. ptDiff.y = rcWindow.bottom;
  794. ptDiff.x = rcWindow.right;
  795. if (!ScreenToClient(hwnd, &ptDiff)) {
  796. return SDL_SetError("ScreenToClient() failed, error %08X", (unsigned int)GetLastError());
  797. }
  798. rcWindow.bottom = ptDiff.y;
  799. rcWindow.right = ptDiff.x;
  800. /* Now that both the inner and outer rects use the same coordinate system we can subtract them to get the border size.
  801. * Keep in mind that the top/left coordinates of rcWindow are negative because the border lies slightly before {0,0},
  802. * so switch them around because SDL3 wants them in positive. */
  803. *top = rcClient.top - rcWindow.top;
  804. *left = rcClient.left - rcWindow.left;
  805. *bottom = rcWindow.bottom - rcClient.bottom;
  806. *right = rcWindow.right - rcClient.right;
  807. return 0;
  808. #endif /*!defined(__XBOXONE__) && !defined(__XBOXSERIES__)*/
  809. }
  810. void WIN_GetWindowSizeInPixels(SDL_VideoDevice *_this, SDL_Window *window, int *w, int *h)
  811. {
  812. const SDL_WindowData *data = window->driverdata;
  813. HWND hwnd = data->hwnd;
  814. RECT rect;
  815. if (GetClientRect(hwnd, &rect) && !WIN_IsRectEmpty(&rect)) {
  816. *w = rect.right;
  817. *h = rect.bottom;
  818. } else {
  819. *w = window->last_pixel_w;
  820. *h = window->last_pixel_h;
  821. }
  822. }
  823. void WIN_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
  824. {
  825. DWORD style;
  826. HWND hwnd;
  827. SDL_bool bActivate = SDL_GetHintBoolean(SDL_HINT_WINDOW_ACTIVATE_WHEN_SHOWN, SDL_TRUE);
  828. if (window->parent) {
  829. /* Update our position in case our parent moved while we were hidden */
  830. WIN_SetWindowPosition(_this, window);
  831. }
  832. hwnd = window->driverdata->hwnd;
  833. style = GetWindowLong(hwnd, GWL_EXSTYLE);
  834. if (style & WS_EX_NOACTIVATE) {
  835. bActivate = SDL_FALSE;
  836. }
  837. if (bActivate) {
  838. ShowWindow(hwnd, SW_SHOW);
  839. } else {
  840. /* Use SetWindowPos instead of ShowWindow to avoid activating the parent window if this is a child window */
  841. SetWindowPos(hwnd, NULL, 0, 0, 0, 0, window->driverdata->copybits_flag | SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
  842. }
  843. if (window->flags & SDL_WINDOW_POPUP_MENU && bActivate) {
  844. if (window->parent == SDL_GetKeyboardFocus()) {
  845. WIN_SetKeyboardFocus(window);
  846. }
  847. }
  848. }
  849. void WIN_HideWindow(SDL_VideoDevice *_this, SDL_Window *window)
  850. {
  851. HWND hwnd = window->driverdata->hwnd;
  852. ShowWindow(hwnd, SW_HIDE);
  853. /* Transfer keyboard focus back to the parent */
  854. if (window->flags & SDL_WINDOW_POPUP_MENU) {
  855. if (window == SDL_GetKeyboardFocus()) {
  856. SDL_Window *new_focus = window->parent;
  857. /* Find the highest level window that isn't being hidden or destroyed. */
  858. while (new_focus->parent && (new_focus->is_hiding || new_focus->is_destroying)) {
  859. new_focus = new_focus->parent;
  860. }
  861. WIN_SetKeyboardFocus(new_focus);
  862. }
  863. }
  864. }
  865. void WIN_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window)
  866. {
  867. #if !defined(__XBOXONE__) && !defined(__XBOXSERIES__)
  868. /* If desired, raise the window more forcefully.
  869. * Technique taken from http://stackoverflow.com/questions/916259/ .
  870. * Specifically, http://stackoverflow.com/a/34414846 .
  871. *
  872. * The issue is that Microsoft has gone through a lot of trouble to make it
  873. * nearly impossible to programmatically move a window to the foreground,
  874. * for "security" reasons. Apparently, the following song-and-dance gets
  875. * around their objections. */
  876. SDL_bool bForce = SDL_GetHintBoolean(SDL_HINT_FORCE_RAISEWINDOW, SDL_FALSE);
  877. SDL_bool bActivate = SDL_GetHintBoolean(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED, SDL_TRUE);
  878. HWND hCurWnd = NULL;
  879. DWORD dwMyID = 0u;
  880. DWORD dwCurID = 0u;
  881. SDL_WindowData *data = window->driverdata;
  882. HWND hwnd = data->hwnd;
  883. if (bForce) {
  884. hCurWnd = GetForegroundWindow();
  885. dwMyID = GetCurrentThreadId();
  886. dwCurID = GetWindowThreadProcessId(hCurWnd, NULL);
  887. ShowWindow(hwnd, SW_RESTORE);
  888. AttachThreadInput(dwCurID, dwMyID, TRUE);
  889. SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
  890. if (!SDL_ShouldAllowTopmost() || !(window->flags & SDL_WINDOW_ALWAYS_ON_TOP)) {
  891. SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
  892. }
  893. }
  894. if (bActivate) {
  895. SetForegroundWindow(hwnd);
  896. if (window->flags & SDL_WINDOW_POPUP_MENU) {
  897. if (window->parent == SDL_GetKeyboardFocus()) {
  898. WIN_SetKeyboardFocus(window);
  899. }
  900. }
  901. } else {
  902. SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, data->copybits_flag | SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_NOACTIVATE);
  903. }
  904. if (bForce) {
  905. AttachThreadInput(dwCurID, dwMyID, FALSE);
  906. SetFocus(hwnd);
  907. SetActiveWindow(hwnd);
  908. }
  909. #endif /*!defined(__XBOXONE__) && !defined(__XBOXSERIES__)*/
  910. }
  911. void WIN_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
  912. {
  913. SDL_WindowData *data = window->driverdata;
  914. HWND hwnd = data->hwnd;
  915. data->expected_resize = SDL_TRUE;
  916. ShowWindow(hwnd, SW_MAXIMIZE);
  917. data->expected_resize = SDL_FALSE;
  918. /* Clamp the maximized window size to the max window size.
  919. * This is automatic if maximizing from the window controls.
  920. */
  921. if (window->max_w || window->max_h) {
  922. int fx, fy, fw, fh;
  923. window->windowed.w = window->max_w ? SDL_min(window->w, window->max_w) : window->windowed.w;
  924. window->windowed.h = window->max_h ? SDL_min(window->h, window->max_h) : window->windowed.h;
  925. WIN_AdjustWindowRect(window, &fx, &fy, &fw, &fh, SDL_WINDOWRECT_WINDOWED);
  926. data->expected_resize = SDL_TRUE;
  927. SetWindowPos(hwnd, HWND_TOP, fx, fy, fw, fh, data->copybits_flag | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOACTIVATE);
  928. data->expected_resize = SDL_FALSE;
  929. }
  930. }
  931. void WIN_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
  932. {
  933. HWND hwnd = window->driverdata->hwnd;
  934. ShowWindow(hwnd, SW_MINIMIZE);
  935. }
  936. void WIN_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool bordered)
  937. {
  938. SDL_WindowData *data = window->driverdata;
  939. HWND hwnd = data->hwnd;
  940. DWORD style;
  941. style = GetWindowLong(hwnd, GWL_STYLE);
  942. style &= ~STYLE_MASK;
  943. style |= GetWindowStyle(window);
  944. data->in_border_change = SDL_TRUE;
  945. SetWindowLong(hwnd, GWL_STYLE, style);
  946. WIN_SetWindowPositionInternal(window, data->copybits_flag | SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE, SDL_WINDOWRECT_CURRENT);
  947. data->in_border_change = SDL_FALSE;
  948. }
  949. void WIN_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool resizable)
  950. {
  951. SDL_WindowData *data = window->driverdata;
  952. HWND hwnd = data->hwnd;
  953. DWORD style;
  954. style = GetWindowLong(hwnd, GWL_STYLE);
  955. style &= ~STYLE_MASK;
  956. style |= GetWindowStyle(window);
  957. SetWindowLong(hwnd, GWL_STYLE, style);
  958. }
  959. void WIN_SetWindowAlwaysOnTop(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool on_top)
  960. {
  961. WIN_SetWindowPositionInternal(window, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE, SDL_WINDOWRECT_CURRENT);
  962. }
  963. void WIN_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window)
  964. {
  965. SDL_WindowData *data = window->driverdata;
  966. HWND hwnd = data->hwnd;
  967. data->expected_resize = SDL_TRUE;
  968. ShowWindow(hwnd, SW_RESTORE);
  969. data->expected_resize = SDL_FALSE;
  970. }
  971. /**
  972. * Reconfigures the window to fill the given display, if fullscreen is true, otherwise restores the window.
  973. */
  974. int WIN_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen)
  975. {
  976. #if !defined(__XBOXONE__) && !defined(__XBOXSERIES__)
  977. SDL_DisplayData *displaydata = display->driverdata;
  978. SDL_WindowData *data = window->driverdata;
  979. HWND hwnd = data->hwnd;
  980. MONITORINFO minfo;
  981. DWORD style, styleEx;
  982. HWND top;
  983. int x, y;
  984. int w, h;
  985. #ifdef HIGHDPI_DEBUG
  986. SDL_Log("WIN_SetWindowFullscreen: %d", (int)fullscreen);
  987. #endif
  988. /* Early out if already not in fullscreen, or the styling on
  989. * external windows may end up being overridden.
  990. */
  991. if (!(window->flags & SDL_WINDOW_FULLSCREEN) && !fullscreen) {
  992. return 0;
  993. }
  994. if (SDL_ShouldAllowTopmost() && (window->flags & SDL_WINDOW_ALWAYS_ON_TOP)) {
  995. top = HWND_TOPMOST;
  996. } else {
  997. top = HWND_NOTOPMOST;
  998. }
  999. /* Use GetMonitorInfo instead of WIN_GetDisplayBounds because we want the
  1000. monitor bounds in Windows coordinates (pixels) rather than SDL coordinates (points). */
  1001. SDL_zero(minfo);
  1002. minfo.cbSize = sizeof(MONITORINFO);
  1003. if (!GetMonitorInfo(displaydata->MonitorHandle, &minfo)) {
  1004. SDL_SetError("GetMonitorInfo failed");
  1005. return -1;
  1006. }
  1007. SDL_SendWindowEvent(window, fullscreen ? SDL_EVENT_WINDOW_ENTER_FULLSCREEN : SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, 0, 0);
  1008. style = GetWindowLong(hwnd, GWL_STYLE);
  1009. style &= ~STYLE_MASK;
  1010. style |= GetWindowStyle(window);
  1011. styleEx = GetWindowLong(hwnd, GWL_EXSTYLE);
  1012. if (fullscreen) {
  1013. x = minfo.rcMonitor.left;
  1014. y = minfo.rcMonitor.top;
  1015. w = minfo.rcMonitor.right - minfo.rcMonitor.left;
  1016. h = minfo.rcMonitor.bottom - minfo.rcMonitor.top;
  1017. /* Unset the maximized flag. This fixes
  1018. https://bugzilla.libsdl.org/show_bug.cgi?id=3215
  1019. */
  1020. if (style & WS_MAXIMIZE) {
  1021. data->windowed_mode_was_maximized = SDL_TRUE;
  1022. style &= ~WS_MAXIMIZE;
  1023. }
  1024. } else {
  1025. BOOL menu;
  1026. /* Restore window-maximization state, as applicable.
  1027. Special care is taken to *not* do this if and when we're
  1028. alt-tab'ing away (to some other window; as indicated by
  1029. in_window_deactivation), otherwise
  1030. https://bugzilla.libsdl.org/show_bug.cgi?id=3215 can reproduce!
  1031. */
  1032. if (data->windowed_mode_was_maximized && !data->in_window_deactivation) {
  1033. style |= WS_MAXIMIZE;
  1034. }
  1035. menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL);
  1036. WIN_AdjustWindowRectWithStyle(window, style, styleEx, menu,
  1037. &x, &y,
  1038. &w, &h,
  1039. data->windowed_mode_was_maximized ? SDL_WINDOWRECT_WINDOWED : SDL_WINDOWRECT_FLOATING);
  1040. data->windowed_mode_was_maximized = SDL_FALSE;
  1041. }
  1042. SetWindowLong(hwnd, GWL_STYLE, style);
  1043. data->expected_resize = SDL_TRUE;
  1044. SetWindowPos(hwnd, top, x, y, w, h, data->copybits_flag | SWP_NOACTIVATE);
  1045. data->expected_resize = SDL_FALSE;
  1046. #ifdef HIGHDPI_DEBUG
  1047. SDL_Log("WIN_SetWindowFullscreen: %d finished. Set window to %d,%d, %dx%d", (int)fullscreen, x, y, w, h);
  1048. #endif
  1049. #endif /*!defined(__XBOXONE__) && !defined(__XBOXSERIES__)*/
  1050. return 0;
  1051. }
  1052. #if !defined(__XBOXONE__) && !defined(__XBOXSERIES__)
  1053. void WIN_UpdateWindowICCProfile(SDL_Window *window, SDL_bool send_event)
  1054. {
  1055. SDL_WindowData *data = window->driverdata;
  1056. SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window);
  1057. if (displaydata) {
  1058. HDC hdc = CreateDCW(displaydata->DeviceName, NULL, NULL, NULL);
  1059. if (hdc) {
  1060. WCHAR fileName[MAX_PATH];
  1061. DWORD fileNameSize = SDL_arraysize(fileName);
  1062. if (GetICMProfileW(hdc, &fileNameSize, fileName)) {
  1063. /* fileNameSize includes '\0' on return */
  1064. if (!data->ICMFileName ||
  1065. SDL_wcscmp(data->ICMFileName, fileName) != 0) {
  1066. if (data->ICMFileName) {
  1067. SDL_free(data->ICMFileName);
  1068. }
  1069. data->ICMFileName = SDL_wcsdup(fileName);
  1070. if (send_event) {
  1071. SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_ICCPROF_CHANGED, 0, 0);
  1072. }
  1073. }
  1074. }
  1075. DeleteDC(hdc);
  1076. }
  1077. }
  1078. }
  1079. void *WIN_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size)
  1080. {
  1081. SDL_WindowData *data = window->driverdata;
  1082. char *filename_utf8;
  1083. void *iccProfileData = NULL;
  1084. filename_utf8 = WIN_StringToUTF8(data->ICMFileName);
  1085. if (filename_utf8) {
  1086. iccProfileData = SDL_LoadFile(filename_utf8, size);
  1087. if (!iccProfileData) {
  1088. SDL_SetError("Could not open ICC profile");
  1089. }
  1090. SDL_free(filename_utf8);
  1091. }
  1092. return iccProfileData;
  1093. }
  1094. static void WIN_GrabKeyboard(SDL_Window *window)
  1095. {
  1096. SDL_WindowData *data = window->driverdata;
  1097. HMODULE module;
  1098. if (data->keyboard_hook) {
  1099. return;
  1100. }
  1101. /* SetWindowsHookEx() needs to know which module contains the hook we
  1102. want to install. This is complicated by the fact that SDL can be
  1103. linked statically or dynamically. Fortunately XP and later provide
  1104. this nice API that will go through the loaded modules and find the
  1105. one containing our code.
  1106. */
  1107. if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
  1108. (LPTSTR)WIN_KeyboardHookProc,
  1109. &module)) {
  1110. return;
  1111. }
  1112. /* Capture a snapshot of the current keyboard state before the hook */
  1113. if (!GetKeyboardState(data->videodata->pre_hook_key_state)) {
  1114. return;
  1115. }
  1116. /* To grab the keyboard, we have to install a low-level keyboard hook to
  1117. intercept keys that would normally be captured by the OS. Intercepting
  1118. all key events on the system is rather invasive, but it's what Microsoft
  1119. actually documents that you do to capture these.
  1120. */
  1121. data->keyboard_hook = SetWindowsHookEx(WH_KEYBOARD_LL, WIN_KeyboardHookProc, module, 0);
  1122. }
  1123. void WIN_UngrabKeyboard(SDL_Window *window)
  1124. {
  1125. SDL_WindowData *data = window->driverdata;
  1126. if (data->keyboard_hook) {
  1127. UnhookWindowsHookEx(data->keyboard_hook);
  1128. data->keyboard_hook = NULL;
  1129. }
  1130. }
  1131. void WIN_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window)
  1132. {
  1133. WIN_UpdateClipCursor(window);
  1134. }
  1135. void WIN_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed)
  1136. {
  1137. WIN_UpdateClipCursor(window);
  1138. }
  1139. void WIN_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed)
  1140. {
  1141. if (grabbed) {
  1142. WIN_GrabKeyboard(window);
  1143. } else {
  1144. WIN_UngrabKeyboard(window);
  1145. }
  1146. }
  1147. #endif /*!defined(__XBOXONE__) && !defined(__XBOXSERIES__)*/
  1148. void WIN_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
  1149. {
  1150. CleanupWindowData(_this, window);
  1151. }
  1152. /*
  1153. * Creates a HelperWindow used for DirectInput.
  1154. */
  1155. int SDL_HelperWindowCreate(void)
  1156. {
  1157. HINSTANCE hInstance = GetModuleHandle(NULL);
  1158. WNDCLASS wce;
  1159. /* Make sure window isn't created twice. */
  1160. if (SDL_HelperWindow != NULL) {
  1161. return 0;
  1162. }
  1163. /* Create the class. */
  1164. SDL_zero(wce);
  1165. wce.lpfnWndProc = DefWindowProc;
  1166. wce.lpszClassName = SDL_HelperWindowClassName;
  1167. wce.hInstance = hInstance;
  1168. /* Register the class. */
  1169. SDL_HelperWindowClass = RegisterClass(&wce);
  1170. if (SDL_HelperWindowClass == 0 && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) {
  1171. return WIN_SetError("Unable to create Helper Window Class");
  1172. }
  1173. /* Create the window. */
  1174. SDL_HelperWindow = CreateWindowEx(0, SDL_HelperWindowClassName,
  1175. SDL_HelperWindowName,
  1176. WS_OVERLAPPED, CW_USEDEFAULT,
  1177. CW_USEDEFAULT, CW_USEDEFAULT,
  1178. CW_USEDEFAULT, HWND_MESSAGE, NULL,
  1179. hInstance, NULL);
  1180. if (!SDL_HelperWindow) {
  1181. UnregisterClass(SDL_HelperWindowClassName, hInstance);
  1182. return WIN_SetError("Unable to create Helper Window");
  1183. }
  1184. return 0;
  1185. }
  1186. /*
  1187. * Destroys the HelperWindow previously created with SDL_HelperWindowCreate.
  1188. */
  1189. void SDL_HelperWindowDestroy(void)
  1190. {
  1191. HINSTANCE hInstance = GetModuleHandle(NULL);
  1192. /* Destroy the window. */
  1193. if (SDL_HelperWindow != NULL) {
  1194. if (DestroyWindow(SDL_HelperWindow) == 0) {
  1195. WIN_SetError("Unable to destroy Helper Window");
  1196. return;
  1197. }
  1198. SDL_HelperWindow = NULL;
  1199. }
  1200. /* Unregister the class. */
  1201. if (SDL_HelperWindowClass != 0) {
  1202. if ((UnregisterClass(SDL_HelperWindowClassName, hInstance)) == 0) {
  1203. WIN_SetError("Unable to destroy Helper Window Class");
  1204. return;
  1205. }
  1206. SDL_HelperWindowClass = 0;
  1207. }
  1208. }
  1209. #if !defined(__XBOXONE__) && !defined(__XBOXSERIES__)
  1210. void WIN_OnWindowEnter(SDL_VideoDevice *_this, SDL_Window *window)
  1211. {
  1212. SDL_WindowData *data = window->driverdata;
  1213. if (!data || !data->hwnd) {
  1214. /* The window wasn't fully initialized */
  1215. return;
  1216. }
  1217. if (window->flags & SDL_WINDOW_ALWAYS_ON_TOP) {
  1218. WIN_SetWindowPositionInternal(window, data->copybits_flag | SWP_NOSIZE | SWP_NOACTIVATE, SDL_WINDOWRECT_CURRENT);
  1219. }
  1220. }
  1221. static BOOL GetClientScreenRect(HWND hwnd, RECT *rect)
  1222. {
  1223. return GetClientRect(hwnd, rect) && /* RECT( left , top , right , bottom ) */
  1224. ClientToScreen(hwnd, (LPPOINT)rect) && /* POINT( left , top ) */
  1225. ClientToScreen(hwnd, (LPPOINT)rect + 1); /* POINT( right , bottom ) */
  1226. }
  1227. void WIN_UpdateClipCursor(SDL_Window *window)
  1228. {
  1229. SDL_WindowData *data = window->driverdata;
  1230. SDL_Mouse *mouse = SDL_GetMouse();
  1231. RECT rect, clipped_rect;
  1232. if (data->in_title_click || data->focus_click_pending) {
  1233. return;
  1234. }
  1235. if (data->skip_update_clipcursor) {
  1236. return;
  1237. }
  1238. if (!GetClipCursor(&clipped_rect)) {
  1239. return;
  1240. }
  1241. if ((mouse->relative_mode || (window->flags & SDL_WINDOW_MOUSE_GRABBED) ||
  1242. (window->mouse_rect.w > 0 && window->mouse_rect.h > 0)) &&
  1243. (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
  1244. if (mouse->relative_mode && !mouse->relative_mode_warp && data->mouse_relative_mode_center) {
  1245. if (GetClientScreenRect(data->hwnd, &rect)) {
  1246. /* WIN_WarpCursor() jitters by +1, and remote desktop warp wobble is +/- 1 */
  1247. LONG remote_desktop_adjustment = GetSystemMetrics(SM_REMOTESESSION) ? 2 : 0;
  1248. LONG cx, cy;
  1249. cx = (rect.left + rect.right) / 2;
  1250. cy = (rect.top + rect.bottom) / 2;
  1251. /* Make an absurdly small clip rect */
  1252. rect.left = cx - remote_desktop_adjustment;
  1253. rect.right = cx + 1 + remote_desktop_adjustment;
  1254. rect.top = cy;
  1255. rect.bottom = cy + 1;
  1256. if (SDL_memcmp(&rect, &clipped_rect, sizeof(rect)) != 0) {
  1257. if (ClipCursor(&rect)) {
  1258. data->cursor_clipped_rect = rect;
  1259. }
  1260. }
  1261. }
  1262. } else {
  1263. if (GetClientScreenRect(data->hwnd, &rect)) {
  1264. if (window->mouse_rect.w > 0 && window->mouse_rect.h > 0) {
  1265. SDL_Rect mouse_rect_win_client;
  1266. RECT mouse_rect, intersection;
  1267. /* mouse_rect_win_client is the mouse rect in Windows client space */
  1268. mouse_rect_win_client = window->mouse_rect;
  1269. /* mouse_rect is the rect in Windows screen space */
  1270. mouse_rect.left = rect.left + mouse_rect_win_client.x;
  1271. mouse_rect.top = rect.top + mouse_rect_win_client.y;
  1272. mouse_rect.right = mouse_rect.left + mouse_rect_win_client.w;
  1273. mouse_rect.bottom = mouse_rect.top + mouse_rect_win_client.h;
  1274. if (IntersectRect(&intersection, &rect, &mouse_rect)) {
  1275. SDL_memcpy(&rect, &intersection, sizeof(rect));
  1276. } else if (window->flags & SDL_WINDOW_MOUSE_GRABBED) {
  1277. /* Mouse rect was invalid, just do the normal grab */
  1278. } else {
  1279. SDL_zero(rect);
  1280. }
  1281. }
  1282. if (SDL_memcmp(&rect, &clipped_rect, sizeof(rect)) != 0) {
  1283. if (!WIN_IsRectEmpty(&rect)) {
  1284. if (ClipCursor(&rect)) {
  1285. data->cursor_clipped_rect = rect;
  1286. }
  1287. } else {
  1288. ClipCursor(NULL);
  1289. SDL_zero(data->cursor_clipped_rect);
  1290. }
  1291. }
  1292. }
  1293. }
  1294. } else {
  1295. POINT first, second;
  1296. first.x = clipped_rect.left;
  1297. first.y = clipped_rect.top;
  1298. second.x = clipped_rect.right - 1;
  1299. second.y = clipped_rect.bottom - 1;
  1300. if (PtInRect(&data->cursor_clipped_rect, first) &&
  1301. PtInRect(&data->cursor_clipped_rect, second)) {
  1302. ClipCursor(NULL);
  1303. SDL_zero(data->cursor_clipped_rect);
  1304. }
  1305. }
  1306. data->last_updated_clipcursor = SDL_GetTicks();
  1307. }
  1308. int WIN_SetWindowHitTest(SDL_Window *window, SDL_bool enabled)
  1309. {
  1310. return 0; /* just succeed, the real work is done elsewhere. */
  1311. }
  1312. #endif /*!defined(__XBOXONE__) && !defined(__XBOXSERIES__)*/
  1313. int WIN_SetWindowOpacity(SDL_VideoDevice *_this, SDL_Window *window, float opacity)
  1314. {
  1315. #if defined(__XBOXONE__) || defined(__XBOXSERIES__)
  1316. return -1;
  1317. #else
  1318. const SDL_WindowData *data = window->driverdata;
  1319. HWND hwnd = data->hwnd;
  1320. const LONG style = GetWindowLong(hwnd, GWL_EXSTYLE);
  1321. SDL_assert(style != 0);
  1322. if (opacity == 1.0f) {
  1323. /* want it fully opaque, just mark it unlayered if necessary. */
  1324. if (style & WS_EX_LAYERED) {
  1325. if (SetWindowLong(hwnd, GWL_EXSTYLE, style & ~WS_EX_LAYERED) == 0) {
  1326. return WIN_SetError("SetWindowLong()");
  1327. }
  1328. }
  1329. } else {
  1330. const BYTE alpha = (BYTE)((int)(opacity * 255.0f));
  1331. /* want it transparent, mark it layered if necessary. */
  1332. if (!(style & WS_EX_LAYERED)) {
  1333. if (SetWindowLong(hwnd, GWL_EXSTYLE, style | WS_EX_LAYERED) == 0) {
  1334. return WIN_SetError("SetWindowLong()");
  1335. }
  1336. }
  1337. if (SetLayeredWindowAttributes(hwnd, 0, alpha, LWA_ALPHA) == 0) {
  1338. return WIN_SetError("SetLayeredWindowAttributes()");
  1339. }
  1340. }
  1341. return 0;
  1342. #endif /*!defined(__XBOXONE__) && !defined(__XBOXSERIES__)*/
  1343. }
  1344. #if !defined(__XBOXONE__) && !defined(__XBOXSERIES__)
  1345. void WIN_AcceptDragAndDrop(SDL_Window *window, SDL_bool accept)
  1346. {
  1347. const SDL_WindowData *data = window->driverdata;
  1348. DragAcceptFiles(data->hwnd, accept ? TRUE : FALSE);
  1349. }
  1350. int WIN_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation)
  1351. {
  1352. FLASHWINFO desc;
  1353. SDL_zero(desc);
  1354. desc.cbSize = sizeof(desc);
  1355. desc.hwnd = window->driverdata->hwnd;
  1356. switch (operation) {
  1357. case SDL_FLASH_CANCEL:
  1358. desc.dwFlags = FLASHW_STOP;
  1359. break;
  1360. case SDL_FLASH_BRIEFLY:
  1361. desc.dwFlags = FLASHW_TRAY;
  1362. desc.uCount = 1;
  1363. break;
  1364. case SDL_FLASH_UNTIL_FOCUSED:
  1365. desc.dwFlags = (FLASHW_TRAY | FLASHW_TIMERNOFG);
  1366. break;
  1367. default:
  1368. return SDL_Unsupported();
  1369. }
  1370. FlashWindowEx(&desc);
  1371. return 0;
  1372. }
  1373. void WIN_ShowWindowSystemMenu(SDL_Window *window, int x, int y)
  1374. {
  1375. const SDL_WindowData *data = window->driverdata;
  1376. POINT pt;
  1377. pt.x = x;
  1378. pt.y = y;
  1379. ClientToScreen(data->hwnd, &pt);
  1380. SendMessage(data->hwnd, WM_POPUPSYSTEMMENU, 0, MAKELPARAM(pt.x, pt.y));
  1381. }
  1382. int WIN_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool focusable)
  1383. {
  1384. SDL_WindowData *data = window->driverdata;
  1385. HWND hwnd = data->hwnd;
  1386. const LONG style = GetWindowLong(hwnd, GWL_EXSTYLE);
  1387. SDL_assert(style != 0);
  1388. if (focusable) {
  1389. if (style & WS_EX_NOACTIVATE) {
  1390. if (SetWindowLong(hwnd, GWL_EXSTYLE, style & ~WS_EX_NOACTIVATE) == 0) {
  1391. return WIN_SetError("SetWindowLong()");
  1392. }
  1393. }
  1394. } else {
  1395. if (!(style & WS_EX_NOACTIVATE)) {
  1396. if (SetWindowLong(hwnd, GWL_EXSTYLE, style | WS_EX_NOACTIVATE) == 0) {
  1397. return WIN_SetError("SetWindowLong()");
  1398. }
  1399. }
  1400. }
  1401. return 0;
  1402. }
  1403. #endif /*!defined(__XBOXONE__) && !defined(__XBOXSERIES__)*/
  1404. void WIN_UpdateDarkModeForHWND(HWND hwnd)
  1405. {
  1406. void *handle = SDL_LoadObject("dwmapi.dll");
  1407. if (handle) {
  1408. DwmSetWindowAttribute_t DwmSetWindowAttributeFunc = (DwmSetWindowAttribute_t)SDL_LoadFunction(handle, "DwmSetWindowAttribute");
  1409. if (DwmSetWindowAttributeFunc) {
  1410. /* FIXME: Do we need to traverse children? */
  1411. BOOL value = (SDL_GetSystemTheme() == SDL_SYSTEM_THEME_DARK) ? TRUE : FALSE;
  1412. DwmSetWindowAttributeFunc(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));
  1413. }
  1414. SDL_UnloadObject(handle);
  1415. }
  1416. }
  1417. #endif /* SDL_VIDEO_DRIVER_WINDOWS */