src/video/windows/SDL_windowswindow.c
author Ryan C. Gordon <icculus@icculus.org>
Wed, 26 Jun 2019 01:29:01 -0400
changeset 12906 bd5a067d1fd8
parent 12503 806492103856
permissions -rw-r--r--
windows: Call GetWindowText() with the correct parameters (thanks, Zebediah!)

GetWindowText() wants you to tell it the size of the buffer--including the
terminating NULL char--but we weren't counting that last char, losing the
last char of the string in the process. This was only seen with the special
case of SDL_CreateWindowFrom() to use an existing native window, not
the usual SDL_CreateWindow() codepath.

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