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