1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22
23 #if SDL_VIDEO_DRIVER_WINDOWS
24
25 #include "../../core/windows/SDL_windows.h"
26
27 #include "SDL_assert.h"
28 #include "../SDL_sysvideo.h"
29 #include "../SDL_pixels_c.h"
30 #include "../../events/SDL_keyboard_c.h"
31 #include "../../events/SDL_mouse_c.h"
32
33 #include "../../joystick/windows/SDL_rawinputjoystick_c.h"
34 #include "SDL_windowsvideo.h"
35 #include "SDL_windowswindow.h"
36 #include "SDL_hints.h"
37 #include "SDL_timer.h"
38
39 /* Dropfile support */
40 #include <shellapi.h>
41
42 /* This is included after SDL_windowsvideo.h, which includes windows.h */
43 #include "SDL_syswm.h"
44
45 /* Windows CE compatibility */
46 #ifndef SWP_NOCOPYBITS
47 #define SWP_NOCOPYBITS 0
48 #endif
49
50 /* Fake window to help with DirectInput events. */
51 HWND SDL_HelperWindow = NULL;
52 static WCHAR *SDL_HelperWindowClassName = TEXT("SDLHelperWindowInputCatcher");
53 static WCHAR *SDL_HelperWindowName = TEXT("SDLHelperWindowInputMsgWindow");
54 static ATOM SDL_HelperWindowClass = 0;
55
56 /* For borderless Windows, still want the following flags:
57 - WS_CAPTION: this seems to enable the Windows minimize animation
58 - WS_SYSMENU: enables system context menu on task bar
59 - WS_MINIMIZEBOX: window will respond to Windows minimize commands sent to all windows, such as windows key + m, shaking title bar, etc.
60 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
61 */
62
63 #define STYLE_BASIC (WS_CLIPSIBLINGS | WS_CLIPCHILDREN)
64 #define STYLE_FULLSCREEN (WS_POPUP)
65 #define STYLE_BORDERLESS (WS_POPUP)
66 #define STYLE_BORDERLESS_WINDOWED (WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX)
67 #define STYLE_NORMAL (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX)
68 #define STYLE_RESIZABLE (WS_THICKFRAME | WS_MAXIMIZEBOX)
69 #define STYLE_MASK (STYLE_FULLSCREEN | STYLE_BORDERLESS | STYLE_NORMAL | STYLE_RESIZABLE)
70
71 static DWORD
GetWindowStyle(SDL_Window * window)72 GetWindowStyle(SDL_Window * window)
73 {
74 DWORD style = 0;
75
76 if (window->flags & SDL_WINDOW_FULLSCREEN) {
77 style |= STYLE_FULLSCREEN;
78 } else {
79 if (window->flags & SDL_WINDOW_BORDERLESS) {
80 /* SDL 2.1:
81 This behavior more closely matches other platform where the window is borderless
82 but still interacts with the window manager (e.g. task bar shows above it, it can
83 be resized to fit within usable desktop area, etc.) so this should be the behavior
84 for a future SDL release.
85
86 If you want a borderless window the size of the desktop that looks like a fullscreen
87 window, then you should use the SDL_WINDOW_FULLSCREEN_DESKTOP flag.
88 */
89 if (SDL_GetHintBoolean("SDL_BORDERLESS_WINDOWED_STYLE", SDL_FALSE)) {
90 style |= STYLE_BORDERLESS_WINDOWED;
91 } else {
92 style |= STYLE_BORDERLESS;
93 }
94 } else {
95 style |= STYLE_NORMAL;
96 }
97
98 if (window->flags & SDL_WINDOW_RESIZABLE) {
99 /* You can have a borderless resizable window, but Windows doesn't always draw it correctly,
100 see https://bugzilla.libsdl.org/show_bug.cgi?id=4466
101 */
102 if (!(window->flags & SDL_WINDOW_BORDERLESS) ||
103 SDL_GetHintBoolean("SDL_BORDERLESS_RESIZABLE_STYLE", SDL_FALSE)) {
104 style |= STYLE_RESIZABLE;
105 }
106 }
107
108 /* Need to set initialize minimize style, or when we call ShowWindow with WS_MINIMIZE it will activate a random window */
109 if (window->flags & SDL_WINDOW_MINIMIZED) {
110 style |= WS_MINIMIZE;
111 }
112 }
113 return style;
114 }
115
116 static void
WIN_AdjustWindowRectWithStyle(SDL_Window * window,DWORD style,BOOL menu,int * x,int * y,int * width,int * height,SDL_bool use_current)117 WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL menu, int *x, int *y, int *width, int *height, SDL_bool use_current)
118 {
119 RECT rect;
120
121 rect.left = 0;
122 rect.top = 0;
123 rect.right = (use_current ? window->w : window->windowed.w);
124 rect.bottom = (use_current ? window->h : window->windowed.h);
125
126 /* 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
127 expanding the window client area to the previous window + chrome size, so shouldn't need to adjust the window size for the set styles.
128 */
129 if (!(window->flags & SDL_WINDOW_BORDERLESS))
130 AdjustWindowRectEx(&rect, style, menu, 0);
131
132 *x = (use_current ? window->x : window->windowed.x) + rect.left;
133 *y = (use_current ? window->y : window->windowed.y) + rect.top;
134 *width = (rect.right - rect.left);
135 *height = (rect.bottom - rect.top);
136 }
137
138 static void
WIN_AdjustWindowRect(SDL_Window * window,int * x,int * y,int * width,int * height,SDL_bool use_current)139 WIN_AdjustWindowRect(SDL_Window *window, int *x, int *y, int *width, int *height, SDL_bool use_current)
140 {
141 SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
142 HWND hwnd = data->hwnd;
143 DWORD style;
144 BOOL menu;
145
146 style = GetWindowLong(hwnd, GWL_STYLE);
147 menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL);
148 WIN_AdjustWindowRectWithStyle(window, style, menu, x, y, width, height, use_current);
149 }
150
151 static void
WIN_SetWindowPositionInternal(_THIS,SDL_Window * window,UINT flags)152 WIN_SetWindowPositionInternal(_THIS, SDL_Window * window, UINT flags)
153 {
154 SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
155 HWND hwnd = data->hwnd;
156 HWND top;
157 int x, y;
158 int w, h;
159
160 /* Figure out what the window area will be */
161 if (SDL_ShouldAllowTopmost() && ((window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_INPUT_FOCUS)) == (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_INPUT_FOCUS) || (window->flags & SDL_WINDOW_ALWAYS_ON_TOP))) {
162 top = HWND_TOPMOST;
163 } else {
164 top = HWND_NOTOPMOST;
165 }
166
167 WIN_AdjustWindowRect(window, &x, &y, &w, &h, SDL_TRUE);
168
169 data->expected_resize = SDL_TRUE;
170 SetWindowPos(hwnd, top, x, y, w, h, flags);
171 data->expected_resize = SDL_FALSE;
172 }
173
174 static int
SetupWindowData(_THIS,SDL_Window * window,HWND hwnd,HWND parent,SDL_bool created)175 SetupWindowData(_THIS, SDL_Window * window, HWND hwnd, HWND parent, SDL_bool created)
176 {
177 SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
178 SDL_WindowData *data;
179
180 /* Allocate the window data */
181 data = (SDL_WindowData *) SDL_calloc(1, sizeof(*data));
182 if (!data) {
183 return SDL_OutOfMemory();
184 }
185 data->window = window;
186 data->hwnd = hwnd;
187 data->parent = parent;
188 data->hdc = GetDC(hwnd);
189 data->hinstance = (HINSTANCE) GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
190 data->created = created;
191 data->mouse_button_flags = 0;
192 data->last_pointer_update = (LPARAM)-1;
193 data->videodata = videodata;
194 data->initializing = SDL_TRUE;
195
196 window->driverdata = data;
197
198 /* Associate the data with the window */
199 if (!SetProp(hwnd, TEXT("SDL_WindowData"), data)) {
200 ReleaseDC(hwnd, data->hdc);
201 SDL_free(data);
202 return WIN_SetError("SetProp() failed");
203 }
204
205 /* Set up the window proc function */
206 #ifdef GWLP_WNDPROC
207 data->wndproc = (WNDPROC) GetWindowLongPtr(hwnd, GWLP_WNDPROC);
208 if (data->wndproc == WIN_WindowProc) {
209 data->wndproc = NULL;
210 } else {
211 SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) WIN_WindowProc);
212 }
213 #else
214 data->wndproc = (WNDPROC) GetWindowLong(hwnd, GWL_WNDPROC);
215 if (data->wndproc == WIN_WindowProc) {
216 data->wndproc = NULL;
217 } else {
218 SetWindowLong(hwnd, GWL_WNDPROC, (LONG_PTR) WIN_WindowProc);
219 }
220 #endif
221
222 /* Fill in the SDL window with the window data */
223 {
224 RECT rect;
225 if (GetClientRect(hwnd, &rect)) {
226 int w = rect.right;
227 int h = rect.bottom;
228 if ((window->windowed.w && window->windowed.w != w) || (window->windowed.h && window->windowed.h != h)) {
229 /* We tried to create a window larger than the desktop and Windows didn't allow it. Override! */
230 int x, y;
231 /* Figure out what the window area will be */
232 WIN_AdjustWindowRect(window, &x, &y, &w, &h, SDL_FALSE);
233 SetWindowPos(hwnd, HWND_NOTOPMOST, x, y, w, h, SWP_NOCOPYBITS | SWP_NOZORDER | SWP_NOACTIVATE);
234 } else {
235 window->w = w;
236 window->h = h;
237 }
238 }
239 }
240 {
241 POINT point;
242 point.x = 0;
243 point.y = 0;
244 if (ClientToScreen(hwnd, &point)) {
245 window->x = point.x;
246 window->y = point.y;
247 }
248 }
249 {
250 DWORD style = GetWindowLong(hwnd, GWL_STYLE);
251 if (style & WS_VISIBLE) {
252 window->flags |= SDL_WINDOW_SHOWN;
253 } else {
254 window->flags &= ~SDL_WINDOW_SHOWN;
255 }
256 if (style & WS_POPUP) {
257 window->flags |= SDL_WINDOW_BORDERLESS;
258 } else {
259 window->flags &= ~SDL_WINDOW_BORDERLESS;
260 }
261 if (style & WS_THICKFRAME) {
262 window->flags |= SDL_WINDOW_RESIZABLE;
263 } else {
264 window->flags &= ~SDL_WINDOW_RESIZABLE;
265 }
266 #ifdef WS_MAXIMIZE
267 if (style & WS_MAXIMIZE) {
268 window->flags |= SDL_WINDOW_MAXIMIZED;
269 } else
270 #endif
271 {
272 window->flags &= ~SDL_WINDOW_MAXIMIZED;
273 }
274 #ifdef WS_MINIMIZE
275 if (style & WS_MINIMIZE) {
276 window->flags |= SDL_WINDOW_MINIMIZED;
277 } else
278 #endif
279 {
280 window->flags &= ~SDL_WINDOW_MINIMIZED;
281 }
282 }
283 if (GetFocus() == hwnd) {
284 window->flags |= SDL_WINDOW_INPUT_FOCUS;
285 SDL_SetKeyboardFocus(data->window);
286
287 if (window->flags & SDL_WINDOW_INPUT_GRABBED) {
288 RECT rect;
289 GetClientRect(hwnd, &rect);
290 ClientToScreen(hwnd, (LPPOINT) & rect);
291 ClientToScreen(hwnd, (LPPOINT) & rect + 1);
292 ClipCursor(&rect);
293 }
294 }
295
296 /* Enable multi-touch */
297 if (videodata->RegisterTouchWindow) {
298 videodata->RegisterTouchWindow(hwnd, (TWF_FINETOUCH|TWF_WANTPALM));
299 }
300
301 data->initializing = SDL_FALSE;
302
303 /* All done! */
304 return 0;
305 }
306
307
308
309 int
WIN_CreateWindow(_THIS,SDL_Window * window)310 WIN_CreateWindow(_THIS, SDL_Window * window)
311 {
312 HWND hwnd, parent = NULL;
313 DWORD style = STYLE_BASIC;
314 int x, y;
315 int w, h;
316
317 if (window->flags & SDL_WINDOW_SKIP_TASKBAR) {
318 parent = CreateWindow(SDL_Appname, TEXT(""), STYLE_BASIC, 0, 0, 32, 32, NULL, NULL, SDL_Instance, NULL);
319 }
320
321 style |= GetWindowStyle(window);
322
323 /* Figure out what the window area will be */
324 WIN_AdjustWindowRectWithStyle(window, style, FALSE, &x, &y, &w, &h, SDL_FALSE);
325
326 hwnd =
327 CreateWindow(SDL_Appname, TEXT(""), style, x, y, w, h, parent, NULL,
328 SDL_Instance, NULL);
329 if (!hwnd) {
330 return WIN_SetError("Couldn't create window");
331 }
332
333 WIN_PumpEvents(_this);
334
335 if (SetupWindowData(_this, window, hwnd, parent, SDL_TRUE) < 0) {
336 DestroyWindow(hwnd);
337 if (parent) {
338 DestroyWindow(parent);
339 }
340 return -1;
341 }
342
343 /* Inform Windows of the frame change so we can respond to WM_NCCALCSIZE */
344 SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
345
346 if (window->flags & SDL_WINDOW_MINIMIZED) {
347 ShowWindow(hwnd, SW_SHOWMINNOACTIVE);
348 }
349
350 if (!(window->flags & SDL_WINDOW_OPENGL)) {
351 return 0;
352 }
353
354 /* The rest of this macro mess is for OpenGL or OpenGL ES windows */
355 #if SDL_VIDEO_OPENGL_ES2
356 if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES
357 #if SDL_VIDEO_OPENGL_WGL
358 && (!_this->gl_data || WIN_GL_UseEGL(_this))
359 #endif /* SDL_VIDEO_OPENGL_WGL */
360 ) {
361 #if SDL_VIDEO_OPENGL_EGL
362 if (WIN_GLES_SetupWindow(_this, window) < 0) {
363 WIN_DestroyWindow(_this, window);
364 return -1;
365 }
366 return 0;
367 #else
368 return SDL_SetError("Could not create GLES window surface (EGL support not configured)");
369 #endif /* SDL_VIDEO_OPENGL_EGL */
370 }
371 #endif /* SDL_VIDEO_OPENGL_ES2 */
372
373 #if SDL_VIDEO_OPENGL_WGL
374 if (WIN_GL_SetupWindow(_this, window) < 0) {
375 WIN_DestroyWindow(_this, window);
376 return -1;
377 }
378 #else
379 return SDL_SetError("Could not create GL window (WGL support not configured)");
380 #endif
381
382 return 0;
383 }
384
385 int
WIN_CreateWindowFrom(_THIS,SDL_Window * window,const void * data)386 WIN_CreateWindowFrom(_THIS, SDL_Window * window, const void *data)
387 {
388 HWND hwnd = (HWND) data;
389 LPTSTR title;
390 int titleLen;
391 SDL_bool isstack;
392
393 /* Query the title from the existing window */
394 titleLen = GetWindowTextLength(hwnd);
395 title = SDL_small_alloc(TCHAR, titleLen + 1, &isstack);
396 if (title) {
397 titleLen = GetWindowText(hwnd, title, titleLen + 1);
398 } else {
399 titleLen = 0;
400 }
401 if (titleLen > 0) {
402 window->title = WIN_StringToUTF8(title);
403 }
404 if (title) {
405 SDL_small_free(title, isstack);
406 }
407
408 if (SetupWindowData(_this, window, hwnd, GetParent(hwnd), SDL_FALSE) < 0) {
409 return -1;
410 }
411
412 #if SDL_VIDEO_OPENGL_WGL
413 {
414 const char *hint = SDL_GetHint(SDL_HINT_VIDEO_WINDOW_SHARE_PIXEL_FORMAT);
415 if (hint) {
416 /* This hint is a pointer (in string form) of the address of
417 the window to share a pixel format with
418 */
419 SDL_Window *otherWindow = NULL;
420 SDL_sscanf(hint, "%p", (void**)&otherWindow);
421
422 /* Do some error checking on the pointer */
423 if (otherWindow != NULL && otherWindow->magic == &_this->window_magic) {
424 /* If the otherWindow has SDL_WINDOW_OPENGL set, set it for the new window as well */
425 if (otherWindow->flags & SDL_WINDOW_OPENGL) {
426 window->flags |= SDL_WINDOW_OPENGL;
427 if (!WIN_GL_SetPixelFormatFrom(_this, otherWindow, window)) {
428 return -1;
429 }
430 }
431 }
432 }
433 }
434 #endif
435 return 0;
436 }
437
438 void
WIN_SetWindowTitle(_THIS,SDL_Window * window)439 WIN_SetWindowTitle(_THIS, SDL_Window * window)
440 {
441 HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd;
442 LPTSTR title = WIN_UTF8ToString(window->title);
443 SetWindowText(hwnd, title);
444 SDL_free(title);
445 }
446
447 void
WIN_SetWindowIcon(_THIS,SDL_Window * window,SDL_Surface * icon)448 WIN_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
449 {
450 HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd;
451 HICON hicon = NULL;
452 BYTE *icon_bmp;
453 int icon_len, mask_len, y;
454 SDL_RWops *dst;
455 SDL_bool isstack;
456
457 /* Create temporary buffer for ICONIMAGE structure */
458 mask_len = (icon->h * (icon->w + 7)/8);
459 icon_len = 40 + icon->h * icon->w * sizeof(Uint32) + mask_len;
460 icon_bmp = SDL_small_alloc(BYTE, icon_len, &isstack);
461 dst = SDL_RWFromMem(icon_bmp, icon_len);
462 if (!dst) {
463 SDL_small_free(icon_bmp, isstack);
464 return;
465 }
466
467 /* Write the BITMAPINFO header */
468 SDL_WriteLE32(dst, 40);
469 SDL_WriteLE32(dst, icon->w);
470 SDL_WriteLE32(dst, icon->h * 2);
471 SDL_WriteLE16(dst, 1);
472 SDL_WriteLE16(dst, 32);
473 SDL_WriteLE32(dst, BI_RGB);
474 SDL_WriteLE32(dst, icon->h * icon->w * sizeof(Uint32));
475 SDL_WriteLE32(dst, 0);
476 SDL_WriteLE32(dst, 0);
477 SDL_WriteLE32(dst, 0);
478 SDL_WriteLE32(dst, 0);
479
480 /* Write the pixels upside down into the bitmap buffer */
481 SDL_assert(icon->format->format == SDL_PIXELFORMAT_ARGB8888);
482 y = icon->h;
483 while (y--) {
484 Uint8 *src = (Uint8 *) icon->pixels + y * icon->pitch;
485 SDL_RWwrite(dst, src, icon->w * sizeof(Uint32), 1);
486 }
487
488 /* Write the mask */
489 SDL_memset(icon_bmp + icon_len - mask_len, 0xFF, mask_len);
490
491 hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000);
492
493 SDL_RWclose(dst);
494 SDL_small_free(icon_bmp, isstack);
495
496 /* Set the icon for the window */
497 SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM) hicon);
498
499 /* Set the icon in the task manager (should we do this?) */
500 SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM) hicon);
501 }
502
503 void
WIN_SetWindowPosition(_THIS,SDL_Window * window)504 WIN_SetWindowPosition(_THIS, SDL_Window * window)
505 {
506 WIN_SetWindowPositionInternal(_this, window, SWP_NOCOPYBITS | SWP_NOSIZE | SWP_NOACTIVATE);
507 }
508
509 void
WIN_SetWindowSize(_THIS,SDL_Window * window)510 WIN_SetWindowSize(_THIS, SDL_Window * window)
511 {
512 WIN_SetWindowPositionInternal(_this, window, SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOACTIVATE);
513 }
514
515 int
WIN_GetWindowBordersSize(_THIS,SDL_Window * window,int * top,int * left,int * bottom,int * right)516 WIN_GetWindowBordersSize(_THIS, SDL_Window * window, int *top, int *left, int *bottom, int *right)
517 {
518 HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd;
519 RECT rcClient, rcWindow;
520 POINT ptDiff;
521
522 /* rcClient stores the size of the inner window, while rcWindow stores the outer size relative to the top-left
523 * screen position; so the top/left values of rcClient are always {0,0} and bottom/right are {height,width} */
524 GetClientRect(hwnd, &rcClient);
525 GetWindowRect(hwnd, &rcWindow);
526
527 /* convert the top/left values to make them relative to
528 * the window; they will end up being slightly negative */
529 ptDiff.y = rcWindow.top;
530 ptDiff.x = rcWindow.left;
531
532 ScreenToClient(hwnd, &ptDiff);
533
534 rcWindow.top = ptDiff.y;
535 rcWindow.left = ptDiff.x;
536
537 /* convert the bottom/right values to make them relative to the window,
538 * these will be slightly bigger than the inner width/height */
539 ptDiff.y = rcWindow.bottom;
540 ptDiff.x = rcWindow.right;
541
542 ScreenToClient(hwnd, &ptDiff);
543
544 rcWindow.bottom = ptDiff.y;
545 rcWindow.right = ptDiff.x;
546
547 /* Now that both the inner and outer rects use the same coordinate system we can substract them to get the border size.
548 * Keep in mind that the top/left coordinates of rcWindow are negative because the border lies slightly before {0,0},
549 * so switch them around because SDL2 wants them in positive. */
550 *top = rcClient.top - rcWindow.top;
551 *left = rcClient.left - rcWindow.left;
552 *bottom = rcWindow.bottom - rcClient.bottom;
553 *right = rcWindow.right - rcClient.right;
554
555 return 0;
556 }
557
558 void
WIN_ShowWindow(_THIS,SDL_Window * window)559 WIN_ShowWindow(_THIS, SDL_Window * window)
560 {
561 DWORD style;
562 HWND hwnd;
563 int nCmdShow;
564
565 hwnd = ((SDL_WindowData *)window->driverdata)->hwnd;
566 nCmdShow = SW_SHOW;
567 style = GetWindowLong(hwnd, GWL_EXSTYLE);
568 if (style & WS_EX_NOACTIVATE) {
569 nCmdShow = SW_SHOWNOACTIVATE;
570 }
571 ShowWindow(hwnd, nCmdShow);
572 }
573
574 void
WIN_HideWindow(_THIS,SDL_Window * window)575 WIN_HideWindow(_THIS, SDL_Window * window)
576 {
577 HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd;
578 ShowWindow(hwnd, SW_HIDE);
579 }
580
581 void
WIN_RaiseWindow(_THIS,SDL_Window * window)582 WIN_RaiseWindow(_THIS, SDL_Window * window)
583 {
584 HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd;
585 SetForegroundWindow(hwnd);
586 }
587
588 void
WIN_MaximizeWindow(_THIS,SDL_Window * window)589 WIN_MaximizeWindow(_THIS, SDL_Window * window)
590 {
591 SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
592 HWND hwnd = data->hwnd;
593 data->expected_resize = SDL_TRUE;
594 ShowWindow(hwnd, SW_MAXIMIZE);
595 data->expected_resize = SDL_FALSE;
596 }
597
598 void
WIN_MinimizeWindow(_THIS,SDL_Window * window)599 WIN_MinimizeWindow(_THIS, SDL_Window * window)
600 {
601 HWND hwnd = ((SDL_WindowData *) window->driverdata)->hwnd;
602 ShowWindow(hwnd, SW_MINIMIZE);
603 }
604
605 void
WIN_SetWindowBordered(_THIS,SDL_Window * window,SDL_bool bordered)606 WIN_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered)
607 {
608 SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
609 HWND hwnd = data->hwnd;
610 DWORD style;
611
612 style = GetWindowLong(hwnd, GWL_STYLE);
613 style &= ~STYLE_MASK;
614 style |= GetWindowStyle(window);
615
616 data->in_border_change = SDL_TRUE;
617 SetWindowLong(hwnd, GWL_STYLE, style);
618 WIN_SetWindowPositionInternal(_this, window, SWP_NOCOPYBITS | SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE);
619 data->in_border_change = SDL_FALSE;
620 }
621
622 void
WIN_SetWindowResizable(_THIS,SDL_Window * window,SDL_bool resizable)623 WIN_SetWindowResizable(_THIS, SDL_Window * window, SDL_bool resizable)
624 {
625 SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
626 HWND hwnd = data->hwnd;
627 DWORD style;
628
629 style = GetWindowLong(hwnd, GWL_STYLE);
630 style &= ~STYLE_MASK;
631 style |= GetWindowStyle(window);
632
633 SetWindowLong(hwnd, GWL_STYLE, style);
634 }
635
636 void
WIN_RestoreWindow(_THIS,SDL_Window * window)637 WIN_RestoreWindow(_THIS, SDL_Window * window)
638 {
639 SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
640 HWND hwnd = data->hwnd;
641 data->expected_resize = SDL_TRUE;
642 ShowWindow(hwnd, SW_RESTORE);
643 data->expected_resize = SDL_FALSE;
644 }
645
646 void
WIN_SetWindowFullscreen(_THIS,SDL_Window * window,SDL_VideoDisplay * display,SDL_bool fullscreen)647 WIN_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
648 {
649 SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
650 HWND hwnd = data->hwnd;
651 SDL_Rect bounds;
652 DWORD style;
653 HWND top;
654 int x, y;
655 int w, h;
656
657 if (SDL_ShouldAllowTopmost() && ((window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_INPUT_FOCUS)) == (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_INPUT_FOCUS) || window->flags & SDL_WINDOW_ALWAYS_ON_TOP)) {
658 top = HWND_TOPMOST;
659 } else {
660 top = HWND_NOTOPMOST;
661 }
662
663 style = GetWindowLong(hwnd, GWL_STYLE);
664 style &= ~STYLE_MASK;
665 style |= GetWindowStyle(window);
666
667 WIN_GetDisplayBounds(_this, display, &bounds);
668
669 if (fullscreen) {
670 x = bounds.x;
671 y = bounds.y;
672 w = bounds.w;
673 h = bounds.h;
674
675 /* Unset the maximized flag. This fixes
676 https://bugzilla.libsdl.org/show_bug.cgi?id=3215
677 */
678 if (style & WS_MAXIMIZE) {
679 data->windowed_mode_was_maximized = SDL_TRUE;
680 style &= ~WS_MAXIMIZE;
681 }
682 } else {
683 BOOL menu;
684
685 /* Restore window-maximization state, as applicable.
686 Special care is taken to *not* do this if and when we're
687 alt-tab'ing away (to some other window; as indicated by
688 in_window_deactivation), otherwise
689 https://bugzilla.libsdl.org/show_bug.cgi?id=3215 can reproduce!
690 */
691 if (data->windowed_mode_was_maximized && !data->in_window_deactivation) {
692 style |= WS_MAXIMIZE;
693 data->windowed_mode_was_maximized = SDL_FALSE;
694 }
695
696 menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL);
697 WIN_AdjustWindowRectWithStyle(window, style, menu, &x, &y, &w, &h, SDL_FALSE);
698 }
699 SetWindowLong(hwnd, GWL_STYLE, style);
700 data->expected_resize = SDL_TRUE;
701 SetWindowPos(hwnd, top, x, y, w, h, SWP_NOCOPYBITS | SWP_NOACTIVATE);
702 data->expected_resize = SDL_FALSE;
703 }
704
705 int
WIN_SetWindowGammaRamp(_THIS,SDL_Window * window,const Uint16 * ramp)706 WIN_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp)
707 {
708 SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
709 SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata;
710 HDC hdc;
711 BOOL succeeded = FALSE;
712
713 hdc = CreateDC(data->DeviceName, NULL, NULL, NULL);
714 if (hdc) {
715 succeeded = SetDeviceGammaRamp(hdc, (LPVOID)ramp);
716 if (!succeeded) {
717 WIN_SetError("SetDeviceGammaRamp()");
718 }
719 DeleteDC(hdc);
720 }
721 return succeeded ? 0 : -1;
722 }
723
724 int
WIN_GetWindowGammaRamp(_THIS,SDL_Window * window,Uint16 * ramp)725 WIN_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp)
726 {
727 SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
728 SDL_DisplayData *data = (SDL_DisplayData *) display->driverdata;
729 HDC hdc;
730 BOOL succeeded = FALSE;
731
732 hdc = CreateDC(data->DeviceName, NULL, NULL, NULL);
733 if (hdc) {
734 succeeded = GetDeviceGammaRamp(hdc, (LPVOID)ramp);
735 if (!succeeded) {
736 WIN_SetError("GetDeviceGammaRamp()");
737 }
738 DeleteDC(hdc);
739 }
740 return succeeded ? 0 : -1;
741 }
742
743 void
WIN_SetWindowGrab(_THIS,SDL_Window * window,SDL_bool grabbed)744 WIN_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
745 {
746 WIN_UpdateClipCursor(window);
747
748 if (window->flags & SDL_WINDOW_FULLSCREEN) {
749 UINT flags = SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOSIZE;
750
751 if (!(window->flags & SDL_WINDOW_SHOWN)) {
752 flags |= SWP_NOACTIVATE;
753 }
754 WIN_SetWindowPositionInternal(_this, window, flags);
755 }
756 }
757
758 void
WIN_DestroyWindow(_THIS,SDL_Window * window)759 WIN_DestroyWindow(_THIS, SDL_Window * window)
760 {
761 SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
762
763 if (data) {
764 ReleaseDC(data->hwnd, data->hdc);
765 RemoveProp(data->hwnd, TEXT("SDL_WindowData"));
766 if (data->created) {
767 DestroyWindow(data->hwnd);
768 if (data->parent) {
769 DestroyWindow(data->parent);
770 }
771 } else {
772 /* Restore any original event handler... */
773 if (data->wndproc != NULL) {
774 #ifdef GWLP_WNDPROC
775 SetWindowLongPtr(data->hwnd, GWLP_WNDPROC,
776 (LONG_PTR) data->wndproc);
777 #else
778 SetWindowLong(data->hwnd, GWL_WNDPROC,
779 (LONG_PTR) data->wndproc);
780 #endif
781 }
782 }
783 SDL_free(data);
784 }
785 window->driverdata = NULL;
786 }
787
788 SDL_bool
WIN_GetWindowWMInfo(_THIS,SDL_Window * window,SDL_SysWMinfo * info)789 WIN_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
790 {
791 const SDL_WindowData *data = (const SDL_WindowData *) window->driverdata;
792 if (info->version.major <= SDL_MAJOR_VERSION) {
793 int versionnum = SDL_VERSIONNUM(info->version.major, info->version.minor, info->version.patch);
794
795 info->subsystem = SDL_SYSWM_WINDOWS;
796 info->info.win.window = data->hwnd;
797
798 if (versionnum >= SDL_VERSIONNUM(2, 0, 4)) {
799 info->info.win.hdc = data->hdc;
800 }
801
802 if (versionnum >= SDL_VERSIONNUM(2, 0, 5)) {
803 info->info.win.hinstance = data->hinstance;
804 }
805
806 return SDL_TRUE;
807 } else {
808 SDL_SetError("Application not compiled with SDL %d.%d",
809 SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
810 return SDL_FALSE;
811 }
812 }
813
SDL_HelperWindowProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)814 static LRESULT CALLBACK SDL_HelperWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
815 {
816 #if SDL_JOYSTICK_RAWINPUT
817 if (RAWINPUT_WindowProc(hWnd, msg, wParam, lParam) == 0) {
818 return 0;
819 }
820 #endif
821 return DefWindowProc(hWnd, msg, wParam, lParam);
822 }
823
824 /*
825 * Creates a HelperWindow used for DirectInput and RawInput events.
826 */
827 int
SDL_HelperWindowCreate(void)828 SDL_HelperWindowCreate(void)
829 {
830 HINSTANCE hInstance = GetModuleHandle(NULL);
831 WNDCLASS wce;
832
833 /* Make sure window isn't created twice. */
834 if (SDL_HelperWindow != NULL) {
835 return 0;
836 }
837
838 /* Create the class. */
839 SDL_zero(wce);
840 wce.lpfnWndProc = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_RAWINPUT, SDL_TRUE) ? SDL_HelperWindowProc : DefWindowProc;
841 wce.lpszClassName = (LPCWSTR) SDL_HelperWindowClassName;
842 wce.hInstance = hInstance;
843
844 /* Register the class. */
845 SDL_HelperWindowClass = RegisterClass(&wce);
846 if (SDL_HelperWindowClass == 0 && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) {
847 return WIN_SetError("Unable to create Helper Window Class");
848 }
849
850 /* Create the window. */
851 SDL_HelperWindow = CreateWindowEx(0, SDL_HelperWindowClassName,
852 SDL_HelperWindowName,
853 WS_OVERLAPPED, CW_USEDEFAULT,
854 CW_USEDEFAULT, CW_USEDEFAULT,
855 CW_USEDEFAULT, HWND_MESSAGE, NULL,
856 hInstance, NULL);
857 if (SDL_HelperWindow == NULL) {
858 UnregisterClass(SDL_HelperWindowClassName, hInstance);
859 return WIN_SetError("Unable to create Helper Window");
860 }
861
862 return 0;
863 }
864
865
866 /*
867 * Destroys the HelperWindow previously created with SDL_HelperWindowCreate.
868 */
869 void
SDL_HelperWindowDestroy(void)870 SDL_HelperWindowDestroy(void)
871 {
872 HINSTANCE hInstance = GetModuleHandle(NULL);
873
874 /* Destroy the window. */
875 if (SDL_HelperWindow != NULL) {
876 if (DestroyWindow(SDL_HelperWindow) == 0) {
877 WIN_SetError("Unable to destroy Helper Window");
878 return;
879 }
880 SDL_HelperWindow = NULL;
881 }
882
883 /* Unregister the class. */
884 if (SDL_HelperWindowClass != 0) {
885 if ((UnregisterClass(SDL_HelperWindowClassName, hInstance)) == 0) {
886 WIN_SetError("Unable to destroy Helper Window Class");
887 return;
888 }
889 SDL_HelperWindowClass = 0;
890 }
891 }
892
WIN_OnWindowEnter(_THIS,SDL_Window * window)893 void WIN_OnWindowEnter(_THIS, SDL_Window * window)
894 {
895 SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
896
897 if (!data || !data->hwnd) {
898 /* The window wasn't fully initialized */
899 return;
900 }
901
902 if (window->flags & SDL_WINDOW_ALWAYS_ON_TOP) {
903 WIN_SetWindowPositionInternal(_this, window, SWP_NOCOPYBITS | SWP_NOSIZE | SWP_NOACTIVATE);
904 }
905
906 #ifdef WM_MOUSELEAVE
907 {
908 TRACKMOUSEEVENT trackMouseEvent;
909
910 trackMouseEvent.cbSize = sizeof(TRACKMOUSEEVENT);
911 trackMouseEvent.dwFlags = TME_LEAVE;
912 trackMouseEvent.hwndTrack = data->hwnd;
913
914 TrackMouseEvent(&trackMouseEvent);
915 }
916 #endif /* WM_MOUSELEAVE */
917 }
918
919 void
WIN_UpdateClipCursor(SDL_Window * window)920 WIN_UpdateClipCursor(SDL_Window *window)
921 {
922 SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
923 SDL_Mouse *mouse = SDL_GetMouse();
924 RECT rect, clipped_rect;
925
926 if (data->in_title_click || data->focus_click_pending) {
927 return;
928 }
929 if (data->skip_update_clipcursor) {
930 return;
931 }
932 if (!GetClipCursor(&clipped_rect)) {
933 return;
934 }
935
936 if ((mouse->relative_mode || (window->flags & SDL_WINDOW_INPUT_GRABBED)) &&
937 (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
938 if (mouse->relative_mode && !mouse->relative_mode_warp) {
939 if (GetWindowRect(data->hwnd, &rect)) {
940 LONG cx, cy;
941
942 cx = (rect.left + rect.right) / 2;
943 cy = (rect.top + rect.bottom) / 2;
944
945 /* Make an absurdly small clip rect */
946 rect.left = cx - 1;
947 rect.right = cx + 1;
948 rect.top = cy - 1;
949 rect.bottom = cy + 1;
950
951 if (SDL_memcmp(&rect, &clipped_rect, sizeof(rect)) != 0) {
952 if (ClipCursor(&rect)) {
953 data->cursor_clipped_rect = rect;
954 }
955 }
956 }
957 } else {
958 if (GetClientRect(data->hwnd, &rect) && !IsRectEmpty(&rect)) {
959 ClientToScreen(data->hwnd, (LPPOINT) & rect);
960 ClientToScreen(data->hwnd, (LPPOINT) & rect + 1);
961 if (SDL_memcmp(&rect, &clipped_rect, sizeof(rect)) != 0) {
962 if (ClipCursor(&rect)) {
963 data->cursor_clipped_rect = rect;
964 }
965 }
966 }
967 }
968 } else if (SDL_memcmp(&clipped_rect, &data->cursor_clipped_rect, sizeof(clipped_rect)) == 0) {
969 ClipCursor(NULL);
970 SDL_zero(data->cursor_clipped_rect);
971 }
972 data->last_updated_clipcursor = SDL_GetTicks();
973 }
974
975 int
WIN_SetWindowHitTest(SDL_Window * window,SDL_bool enabled)976 WIN_SetWindowHitTest(SDL_Window *window, SDL_bool enabled)
977 {
978 return 0; /* just succeed, the real work is done elsewhere. */
979 }
980
981 int
WIN_SetWindowOpacity(_THIS,SDL_Window * window,float opacity)982 WIN_SetWindowOpacity(_THIS, SDL_Window * window, float opacity)
983 {
984 const SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
985 const HWND hwnd = data->hwnd;
986 const LONG style = GetWindowLong(hwnd, GWL_EXSTYLE);
987
988 SDL_assert(style != 0);
989
990 if (opacity == 1.0f) {
991 /* want it fully opaque, just mark it unlayered if necessary. */
992 if (style & WS_EX_LAYERED) {
993 if (SetWindowLong(hwnd, GWL_EXSTYLE, style & ~WS_EX_LAYERED) == 0) {
994 return WIN_SetError("SetWindowLong()");
995 }
996 }
997 } else {
998 const BYTE alpha = (BYTE) ((int) (opacity * 255.0f));
999 /* want it transparent, mark it layered if necessary. */
1000 if ((style & WS_EX_LAYERED) == 0) {
1001 if (SetWindowLong(hwnd, GWL_EXSTYLE, style | WS_EX_LAYERED) == 0) {
1002 return WIN_SetError("SetWindowLong()");
1003 }
1004 }
1005
1006 if (SetLayeredWindowAttributes(hwnd, 0, alpha, LWA_ALPHA) == 0) {
1007 return WIN_SetError("SetLayeredWindowAttributes()");
1008 }
1009 }
1010
1011 return 0;
1012 }
1013
1014 void
WIN_AcceptDragAndDrop(SDL_Window * window,SDL_bool accept)1015 WIN_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept)
1016 {
1017 const SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1018 DragAcceptFiles(data->hwnd, accept ? TRUE : FALSE);
1019 }
1020
1021 #endif /* SDL_VIDEO_DRIVER_WINDOWS */
1022
1023 /* vi: set ts=4 sw=4 expandtab: */
1024