src/video/windows/SDL_windowswindow.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 30 Mar 2020 14:26:21 -0700
changeset 13682 bd45c7649f7b
parent 13633 340324c76848
permissions -rw-r--r--
SDL: plumb previously unused digital trigger bits for PS4 controllers. Victrix fight stick only sets these bits and doesn't send the analog values

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