github.com/Konstantin8105/c4go@v0.0.0-20240505174241-768bb1c65a51/tests/raylib/external/glfw/src/win32_window.c (about)

     1  //========================================================================
     2  // GLFW 3.4 Win32 - www.glfw.org
     3  //------------------------------------------------------------------------
     4  // Copyright (c) 2002-2006 Marcus Geelnard
     5  // Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
     6  //
     7  // This software is provided 'as-is', without any express or implied
     8  // warranty. In no event will the authors be held liable for any damages
     9  // arising from the use of this software.
    10  //
    11  // Permission is granted to anyone to use this software for any purpose,
    12  // including commercial applications, and to alter it and redistribute it
    13  // freely, subject to the following restrictions:
    14  //
    15  // 1. The origin of this software must not be misrepresented; you must not
    16  //    claim that you wrote the original software. If you use this software
    17  //    in a product, an acknowledgment in the product documentation would
    18  //    be appreciated but is not required.
    19  //
    20  // 2. Altered source versions must be plainly marked as such, and must not
    21  //    be misrepresented as being the original software.
    22  //
    23  // 3. This notice may not be removed or altered from any source
    24  //    distribution.
    25  //
    26  //========================================================================
    27  // Please use C89 style variable declarations in this file because VS 2010
    28  //========================================================================
    29  
    30  #include "internal.h"
    31  
    32  #include <limits.h>
    33  #include <stdlib.h>
    34  #include <string.h>
    35  #include <windowsx.h>
    36  #include <shellapi.h>
    37  
    38  // Returns the window style for the specified window
    39  //
    40  static DWORD getWindowStyle(const _GLFWwindow* window)
    41  {
    42      DWORD style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
    43  
    44      if (window->monitor)
    45          style |= WS_POPUP;
    46      else
    47      {
    48          style |= WS_SYSMENU | WS_MINIMIZEBOX;
    49  
    50          if (window->decorated)
    51          {
    52              style |= WS_CAPTION;
    53  
    54              if (window->resizable)
    55                  style |= WS_MAXIMIZEBOX | WS_THICKFRAME;
    56          }
    57          else
    58              style |= WS_POPUP;
    59      }
    60  
    61      return style;
    62  }
    63  
    64  // Returns the extended window style for the specified window
    65  //
    66  static DWORD getWindowExStyle(const _GLFWwindow* window)
    67  {
    68      DWORD style = WS_EX_APPWINDOW;
    69  
    70      if (window->monitor || window->floating)
    71          style |= WS_EX_TOPMOST;
    72  
    73      return style;
    74  }
    75  
    76  // Returns the image whose area most closely matches the desired one
    77  //
    78  static const GLFWimage* chooseImage(int count, const GLFWimage* images,
    79                                      int width, int height)
    80  {
    81      int i, leastDiff = INT_MAX;
    82      const GLFWimage* closest = NULL;
    83  
    84      for (i = 0;  i < count;  i++)
    85      {
    86          const int currDiff = abs(images[i].width * images[i].height -
    87                                   width * height);
    88          if (currDiff < leastDiff)
    89          {
    90              closest = images + i;
    91              leastDiff = currDiff;
    92          }
    93      }
    94  
    95      return closest;
    96  }
    97  
    98  // Creates an RGBA icon or cursor
    99  //
   100  static HICON createIcon(const GLFWimage* image, int xhot, int yhot, GLFWbool icon)
   101  {
   102      int i;
   103      HDC dc;
   104      HICON handle;
   105      HBITMAP color, mask;
   106      BITMAPV5HEADER bi;
   107      ICONINFO ii;
   108      unsigned char* target = NULL;
   109      unsigned char* source = image->pixels;
   110  
   111      ZeroMemory(&bi, sizeof(bi));
   112      bi.bV5Size        = sizeof(bi);
   113      bi.bV5Width       = image->width;
   114      bi.bV5Height      = -image->height;
   115      bi.bV5Planes      = 1;
   116      bi.bV5BitCount    = 32;
   117      bi.bV5Compression = BI_BITFIELDS;
   118      bi.bV5RedMask     = 0x00ff0000;
   119      bi.bV5GreenMask   = 0x0000ff00;
   120      bi.bV5BlueMask    = 0x000000ff;
   121      bi.bV5AlphaMask   = 0xff000000;
   122  
   123      dc = GetDC(NULL);
   124      color = CreateDIBSection(dc,
   125                               (BITMAPINFO*) &bi,
   126                               DIB_RGB_COLORS,
   127                               (void**) &target,
   128                               NULL,
   129                               (DWORD) 0);
   130      ReleaseDC(NULL, dc);
   131  
   132      if (!color)
   133      {
   134          _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
   135                               "Win32: Failed to create RGBA bitmap");
   136          return NULL;
   137      }
   138  
   139      mask = CreateBitmap(image->width, image->height, 1, 1, NULL);
   140      if (!mask)
   141      {
   142          _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
   143                               "Win32: Failed to create mask bitmap");
   144          DeleteObject(color);
   145          return NULL;
   146      }
   147  
   148      for (i = 0;  i < image->width * image->height;  i++)
   149      {
   150          target[0] = source[2];
   151          target[1] = source[1];
   152          target[2] = source[0];
   153          target[3] = source[3];
   154          target += 4;
   155          source += 4;
   156      }
   157  
   158      ZeroMemory(&ii, sizeof(ii));
   159      ii.fIcon    = icon;
   160      ii.xHotspot = xhot;
   161      ii.yHotspot = yhot;
   162      ii.hbmMask  = mask;
   163      ii.hbmColor = color;
   164  
   165      handle = CreateIconIndirect(&ii);
   166  
   167      DeleteObject(color);
   168      DeleteObject(mask);
   169  
   170      if (!handle)
   171      {
   172          if (icon)
   173          {
   174              _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
   175                                   "Win32: Failed to create icon");
   176          }
   177          else
   178          {
   179              _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
   180                                   "Win32: Failed to create cursor");
   181          }
   182      }
   183  
   184      return handle;
   185  }
   186  
   187  // Translate content area size to full window size according to styles and DPI
   188  //
   189  static void getFullWindowSize(DWORD style, DWORD exStyle,
   190                                int contentWidth, int contentHeight,
   191                                int* fullWidth, int* fullHeight,
   192                                UINT dpi)
   193  {
   194      RECT rect = { 0, 0, contentWidth, contentHeight };
   195  
   196      if (_glfwIsWindows10Version1607OrGreaterWin32())
   197          AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, dpi);
   198      else
   199          AdjustWindowRectEx(&rect, style, FALSE, exStyle);
   200  
   201      *fullWidth = rect.right - rect.left;
   202      *fullHeight = rect.bottom - rect.top;
   203  }
   204  
   205  // Enforce the content area aspect ratio based on which edge is being dragged
   206  //
   207  static void applyAspectRatio(_GLFWwindow* window, int edge, RECT* area)
   208  {
   209      int xoff, yoff;
   210      UINT dpi = USER_DEFAULT_SCREEN_DPI;
   211      const float ratio = (float) window->numer / (float) window->denom;
   212  
   213      if (_glfwIsWindows10Version1607OrGreaterWin32())
   214          dpi = GetDpiForWindow(window->win32.handle);
   215  
   216      getFullWindowSize(getWindowStyle(window), getWindowExStyle(window),
   217                        0, 0, &xoff, &yoff, dpi);
   218  
   219      if (edge == WMSZ_LEFT  || edge == WMSZ_BOTTOMLEFT ||
   220          edge == WMSZ_RIGHT || edge == WMSZ_BOTTOMRIGHT)
   221      {
   222          area->bottom = area->top + yoff +
   223              (int) ((area->right - area->left - xoff) / ratio);
   224      }
   225      else if (edge == WMSZ_TOPLEFT || edge == WMSZ_TOPRIGHT)
   226      {
   227          area->top = area->bottom - yoff -
   228              (int) ((area->right - area->left - xoff) / ratio);
   229      }
   230      else if (edge == WMSZ_TOP || edge == WMSZ_BOTTOM)
   231      {
   232          area->right = area->left + xoff +
   233              (int) ((area->bottom - area->top - yoff) * ratio);
   234      }
   235  }
   236  
   237  // Updates the cursor image according to its cursor mode
   238  //
   239  static void updateCursorImage(_GLFWwindow* window)
   240  {
   241      if (window->cursorMode == GLFW_CURSOR_NORMAL ||
   242          window->cursorMode == GLFW_CURSOR_CAPTURED)
   243      {
   244          if (window->cursor)
   245              SetCursor(window->cursor->win32.handle);
   246          else
   247              SetCursor(LoadCursorW(NULL, IDC_ARROW));
   248      }
   249      else
   250          SetCursor(NULL);
   251  }
   252  
   253  // Sets the cursor clip rect to the window content area
   254  //
   255  static void captureCursor(_GLFWwindow* window)
   256  {
   257      RECT clipRect;
   258      GetClientRect(window->win32.handle, &clipRect);
   259      ClientToScreen(window->win32.handle, (POINT*) &clipRect.left);
   260      ClientToScreen(window->win32.handle, (POINT*) &clipRect.right);
   261      ClipCursor(&clipRect);
   262      _glfw.win32.capturedCursorWindow = window;
   263  }
   264  
   265  // Disabled clip cursor
   266  //
   267  static void releaseCursor(void)
   268  {
   269      ClipCursor(NULL);
   270      _glfw.win32.capturedCursorWindow = NULL;
   271  }
   272  
   273  // Enables WM_INPUT messages for the mouse for the specified window
   274  //
   275  static void enableRawMouseMotion(_GLFWwindow* window)
   276  {
   277      const RAWINPUTDEVICE rid = { 0x01, 0x02, 0, window->win32.handle };
   278  
   279      if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
   280      {
   281          _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
   282                               "Win32: Failed to register raw input device");
   283      }
   284  }
   285  
   286  // Disables WM_INPUT messages for the mouse
   287  //
   288  static void disableRawMouseMotion(_GLFWwindow* window)
   289  {
   290      const RAWINPUTDEVICE rid = { 0x01, 0x02, RIDEV_REMOVE, NULL };
   291  
   292      if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
   293      {
   294          _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
   295                               "Win32: Failed to remove raw input device");
   296      }
   297  }
   298  
   299  // Apply disabled cursor mode to a focused window
   300  //
   301  static void disableCursor(_GLFWwindow* window)
   302  {
   303      _glfw.win32.disabledCursorWindow = window;
   304      _glfwGetCursorPosWin32(window,
   305                             &_glfw.win32.restoreCursorPosX,
   306                             &_glfw.win32.restoreCursorPosY);
   307      updateCursorImage(window);
   308      _glfwCenterCursorInContentArea(window);
   309      captureCursor(window);
   310  
   311      if (window->rawMouseMotion)
   312          enableRawMouseMotion(window);
   313  }
   314  
   315  // Exit disabled cursor mode for the specified window
   316  //
   317  static void enableCursor(_GLFWwindow* window)
   318  {
   319      if (window->rawMouseMotion)
   320          disableRawMouseMotion(window);
   321  
   322      _glfw.win32.disabledCursorWindow = NULL;
   323      releaseCursor();
   324      _glfwSetCursorPosWin32(window,
   325                             _glfw.win32.restoreCursorPosX,
   326                             _glfw.win32.restoreCursorPosY);
   327      updateCursorImage(window);
   328  }
   329  
   330  // Returns whether the cursor is in the content area of the specified window
   331  //
   332  static GLFWbool cursorInContentArea(_GLFWwindow* window)
   333  {
   334      RECT area;
   335      POINT pos;
   336  
   337      if (!GetCursorPos(&pos))
   338          return GLFW_FALSE;
   339  
   340      if (WindowFromPoint(pos) != window->win32.handle)
   341          return GLFW_FALSE;
   342  
   343      GetClientRect(window->win32.handle, &area);
   344      ClientToScreen(window->win32.handle, (POINT*) &area.left);
   345      ClientToScreen(window->win32.handle, (POINT*) &area.right);
   346  
   347      return PtInRect(&area, pos);
   348  }
   349  
   350  // Update native window styles to match attributes
   351  //
   352  static void updateWindowStyles(const _GLFWwindow* window)
   353  {
   354      RECT rect;
   355      DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE);
   356      style &= ~(WS_OVERLAPPEDWINDOW | WS_POPUP);
   357      style |= getWindowStyle(window);
   358  
   359      GetClientRect(window->win32.handle, &rect);
   360  
   361      if (_glfwIsWindows10Version1607OrGreaterWin32())
   362      {
   363          AdjustWindowRectExForDpi(&rect, style, FALSE,
   364                                   getWindowExStyle(window),
   365                                   GetDpiForWindow(window->win32.handle));
   366      }
   367      else
   368          AdjustWindowRectEx(&rect, style, FALSE, getWindowExStyle(window));
   369  
   370      ClientToScreen(window->win32.handle, (POINT*) &rect.left);
   371      ClientToScreen(window->win32.handle, (POINT*) &rect.right);
   372      SetWindowLongW(window->win32.handle, GWL_STYLE, style);
   373      SetWindowPos(window->win32.handle, HWND_TOP,
   374                   rect.left, rect.top,
   375                   rect.right - rect.left, rect.bottom - rect.top,
   376                   SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOZORDER);
   377  }
   378  
   379  // Update window framebuffer transparency
   380  //
   381  static void updateFramebufferTransparency(const _GLFWwindow* window)
   382  {
   383      BOOL composition, opaque;
   384      DWORD color;
   385  
   386      if (!IsWindowsVistaOrGreater())
   387          return;
   388  
   389      if (FAILED(DwmIsCompositionEnabled(&composition)) || !composition)
   390         return;
   391  
   392      if (IsWindows8OrGreater() ||
   393          (SUCCEEDED(DwmGetColorizationColor(&color, &opaque)) && !opaque))
   394      {
   395          HRGN region = CreateRectRgn(0, 0, -1, -1);
   396          DWM_BLURBEHIND bb = {0};
   397          bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
   398          bb.hRgnBlur = region;
   399          bb.fEnable = TRUE;
   400  
   401          DwmEnableBlurBehindWindow(window->win32.handle, &bb);
   402          DeleteObject(region);
   403      }
   404      else
   405      {
   406          // HACK: Disable framebuffer transparency on Windows 7 when the
   407          //       colorization color is opaque, because otherwise the window
   408          //       contents is blended additively with the previous frame instead
   409          //       of replacing it
   410          DWM_BLURBEHIND bb = {0};
   411          bb.dwFlags = DWM_BB_ENABLE;
   412          DwmEnableBlurBehindWindow(window->win32.handle, &bb);
   413      }
   414  }
   415  
   416  // Retrieves and translates modifier keys
   417  //
   418  static int getKeyMods(void)
   419  {
   420      int mods = 0;
   421  
   422      if (GetKeyState(VK_SHIFT) & 0x8000)
   423          mods |= GLFW_MOD_SHIFT;
   424      if (GetKeyState(VK_CONTROL) & 0x8000)
   425          mods |= GLFW_MOD_CONTROL;
   426      if (GetKeyState(VK_MENU) & 0x8000)
   427          mods |= GLFW_MOD_ALT;
   428      if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x8000)
   429          mods |= GLFW_MOD_SUPER;
   430      if (GetKeyState(VK_CAPITAL) & 1)
   431          mods |= GLFW_MOD_CAPS_LOCK;
   432      if (GetKeyState(VK_NUMLOCK) & 1)
   433          mods |= GLFW_MOD_NUM_LOCK;
   434  
   435      return mods;
   436  }
   437  
   438  static void fitToMonitor(_GLFWwindow* window)
   439  {
   440      MONITORINFO mi = { sizeof(mi) };
   441      GetMonitorInfoW(window->monitor->win32.handle, &mi);
   442      SetWindowPos(window->win32.handle, HWND_TOPMOST,
   443                   mi.rcMonitor.left,
   444                   mi.rcMonitor.top,
   445                   mi.rcMonitor.right - mi.rcMonitor.left,
   446                   mi.rcMonitor.bottom - mi.rcMonitor.top,
   447                   SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS);
   448  }
   449  
   450  // Make the specified window and its video mode active on its monitor
   451  //
   452  static void acquireMonitor(_GLFWwindow* window)
   453  {
   454      if (!_glfw.win32.acquiredMonitorCount)
   455      {
   456          SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED);
   457  
   458          // HACK: When mouse trails are enabled the cursor becomes invisible when
   459          //       the OpenGL ICD switches to page flipping
   460          SystemParametersInfoW(SPI_GETMOUSETRAILS, 0, &_glfw.win32.mouseTrailSize, 0);
   461          SystemParametersInfoW(SPI_SETMOUSETRAILS, 0, 0, 0);
   462      }
   463  
   464      if (!window->monitor->window)
   465          _glfw.win32.acquiredMonitorCount++;
   466  
   467      _glfwSetVideoModeWin32(window->monitor, &window->videoMode);
   468      _glfwInputMonitorWindow(window->monitor, window);
   469  }
   470  
   471  // Remove the window and restore the original video mode
   472  //
   473  static void releaseMonitor(_GLFWwindow* window)
   474  {
   475      if (window->monitor->window != window)
   476          return;
   477  
   478      _glfw.win32.acquiredMonitorCount--;
   479      if (!_glfw.win32.acquiredMonitorCount)
   480      {
   481          SetThreadExecutionState(ES_CONTINUOUS);
   482  
   483          // HACK: Restore mouse trail length saved in acquireMonitor
   484          SystemParametersInfoW(SPI_SETMOUSETRAILS, _glfw.win32.mouseTrailSize, 0, 0);
   485      }
   486  
   487      _glfwInputMonitorWindow(window->monitor, NULL);
   488      _glfwRestoreVideoModeWin32(window->monitor);
   489  }
   490  
   491  // Manually maximize the window, for when SW_MAXIMIZE cannot be used
   492  //
   493  static void maximizeWindowManually(_GLFWwindow* window)
   494  {
   495      RECT rect;
   496      DWORD style;
   497      MONITORINFO mi = { sizeof(mi) };
   498  
   499      GetMonitorInfoW(MonitorFromWindow(window->win32.handle,
   500                                        MONITOR_DEFAULTTONEAREST), &mi);
   501  
   502      rect = mi.rcWork;
   503  
   504      if (window->maxwidth != GLFW_DONT_CARE && window->maxheight != GLFW_DONT_CARE)
   505      {
   506          rect.right = _glfw_min(rect.right, rect.left + window->maxwidth);
   507          rect.bottom = _glfw_min(rect.bottom, rect.top + window->maxheight);
   508      }
   509  
   510      style = GetWindowLongW(window->win32.handle, GWL_STYLE);
   511      style |= WS_MAXIMIZE;
   512      SetWindowLongW(window->win32.handle, GWL_STYLE, style);
   513  
   514      if (window->decorated)
   515      {
   516          const DWORD exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE);
   517  
   518          if (_glfwIsWindows10Version1607OrGreaterWin32())
   519          {
   520              const UINT dpi = GetDpiForWindow(window->win32.handle);
   521              AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, dpi);
   522              OffsetRect(&rect, 0, GetSystemMetricsForDpi(SM_CYCAPTION, dpi));
   523          }
   524          else
   525          {
   526              AdjustWindowRectEx(&rect, style, FALSE, exStyle);
   527              OffsetRect(&rect, 0, GetSystemMetrics(SM_CYCAPTION));
   528          }
   529  
   530          rect.bottom = _glfw_min(rect.bottom, mi.rcWork.bottom);
   531      }
   532  
   533      SetWindowPos(window->win32.handle, HWND_TOP,
   534                   rect.left,
   535                   rect.top,
   536                   rect.right - rect.left,
   537                   rect.bottom - rect.top,
   538                   SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED);
   539  }
   540  
   541  // Window procedure for user-created windows
   542  //
   543  static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
   544  {
   545      _GLFWwindow* window = GetPropW(hWnd, L"GLFW");
   546      if (!window)
   547      {
   548          if (uMsg == WM_NCCREATE)
   549          {
   550              if (_glfwIsWindows10Version1607OrGreaterWin32())
   551              {
   552                  const CREATESTRUCTW* cs = (const CREATESTRUCTW*) lParam;
   553                  const _GLFWwndconfig* wndconfig = cs->lpCreateParams;
   554  
   555                  // On per-monitor DPI aware V1 systems, only enable
   556                  // non-client scaling for windows that scale the client area
   557                  // We need WM_GETDPISCALEDSIZE from V2 to keep the client
   558                  // area static when the non-client area is scaled
   559                  if (wndconfig && wndconfig->scaleToMonitor)
   560                      EnableNonClientDpiScaling(hWnd);
   561              }
   562          }
   563  
   564          return DefWindowProcW(hWnd, uMsg, wParam, lParam);
   565      }
   566  
   567      switch (uMsg)
   568      {
   569          case WM_MOUSEACTIVATE:
   570          {
   571              // HACK: Postpone cursor disabling when the window was activated by
   572              //       clicking a caption button
   573              if (HIWORD(lParam) == WM_LBUTTONDOWN)
   574              {
   575                  if (LOWORD(lParam) != HTCLIENT)
   576                      window->win32.frameAction = GLFW_TRUE;
   577              }
   578  
   579              break;
   580          }
   581  
   582          case WM_CAPTURECHANGED:
   583          {
   584              // HACK: Disable the cursor once the caption button action has been
   585              //       completed or cancelled
   586              if (lParam == 0 && window->win32.frameAction)
   587              {
   588                  if (window->cursorMode == GLFW_CURSOR_DISABLED)
   589                      disableCursor(window);
   590                  else if (window->cursorMode == GLFW_CURSOR_CAPTURED)
   591                      captureCursor(window);
   592  
   593                  window->win32.frameAction = GLFW_FALSE;
   594              }
   595  
   596              break;
   597          }
   598  
   599          case WM_SETFOCUS:
   600          {
   601              _glfwInputWindowFocus(window, GLFW_TRUE);
   602  
   603              // HACK: Do not disable cursor while the user is interacting with
   604              //       a caption button
   605              if (window->win32.frameAction)
   606                  break;
   607  
   608              if (window->cursorMode == GLFW_CURSOR_DISABLED)
   609                  disableCursor(window);
   610              else if (window->cursorMode == GLFW_CURSOR_CAPTURED)
   611                  captureCursor(window);
   612  
   613              return 0;
   614          }
   615  
   616          case WM_KILLFOCUS:
   617          {
   618              if (window->cursorMode == GLFW_CURSOR_DISABLED)
   619                  enableCursor(window);
   620              else if (window->cursorMode == GLFW_CURSOR_CAPTURED)
   621                  releaseCursor();
   622  
   623              if (window->monitor && window->autoIconify)
   624                  _glfwIconifyWindowWin32(window);
   625  
   626              _glfwInputWindowFocus(window, GLFW_FALSE);
   627              return 0;
   628          }
   629  
   630          case WM_SYSCOMMAND:
   631          {
   632              switch (wParam & 0xfff0)
   633              {
   634                  case SC_SCREENSAVE:
   635                  case SC_MONITORPOWER:
   636                  {
   637                      if (window->monitor)
   638                      {
   639                          // We are running in full screen mode, so disallow
   640                          // screen saver and screen blanking
   641                          return 0;
   642                      }
   643                      else
   644                          break;
   645                  }
   646  
   647                  // User trying to access application menu using ALT?
   648                  case SC_KEYMENU:
   649                  {
   650                      if (!window->win32.keymenu)
   651                          return 0;
   652  
   653                      break;
   654                  }
   655              }
   656              break;
   657          }
   658  
   659          case WM_CLOSE:
   660          {
   661              _glfwInputWindowCloseRequest(window);
   662              return 0;
   663          }
   664  
   665          case WM_INPUTLANGCHANGE:
   666          {
   667              _glfwUpdateKeyNamesWin32();
   668              break;
   669          }
   670  
   671          case WM_CHAR:
   672          case WM_SYSCHAR:
   673          {
   674              if (wParam >= 0xd800 && wParam <= 0xdbff)
   675                  window->win32.highSurrogate = (WCHAR) wParam;
   676              else
   677              {
   678                  uint32_t codepoint = 0;
   679  
   680                  if (wParam >= 0xdc00 && wParam <= 0xdfff)
   681                  {
   682                      if (window->win32.highSurrogate)
   683                      {
   684                          codepoint += (window->win32.highSurrogate - 0xd800) << 10;
   685                          codepoint += (WCHAR) wParam - 0xdc00;
   686                          codepoint += 0x10000;
   687                      }
   688                  }
   689                  else
   690                      codepoint = (WCHAR) wParam;
   691  
   692                  window->win32.highSurrogate = 0;
   693                  _glfwInputChar(window, codepoint, getKeyMods(), uMsg != WM_SYSCHAR);
   694              }
   695  
   696              if (uMsg == WM_SYSCHAR && window->win32.keymenu)
   697                  break;
   698  
   699              return 0;
   700          }
   701  
   702          case WM_UNICHAR:
   703          {
   704              if (wParam == UNICODE_NOCHAR)
   705              {
   706                  // WM_UNICHAR is not sent by Windows, but is sent by some
   707                  // third-party input method engine
   708                  // Returning TRUE here announces support for this message
   709                  return TRUE;
   710              }
   711  
   712              _glfwInputChar(window, (uint32_t) wParam, getKeyMods(), GLFW_TRUE);
   713              return 0;
   714          }
   715  
   716          case WM_KEYDOWN:
   717          case WM_SYSKEYDOWN:
   718          case WM_KEYUP:
   719          case WM_SYSKEYUP:
   720          {
   721              int key, scancode;
   722              const int action = (HIWORD(lParam) & KF_UP) ? GLFW_RELEASE : GLFW_PRESS;
   723              const int mods = getKeyMods();
   724  
   725              scancode = (HIWORD(lParam) & (KF_EXTENDED | 0xff));
   726              if (!scancode)
   727              {
   728                  // NOTE: Some synthetic key messages have a scancode of zero
   729                  // HACK: Map the virtual key back to a usable scancode
   730                  scancode = MapVirtualKeyW((UINT) wParam, MAPVK_VK_TO_VSC);
   731              }
   732  
   733              // HACK: Alt+PrtSc has a different scancode than just PrtSc
   734              if (scancode == 0x54)
   735                  scancode = 0x137;
   736  
   737              // HACK: Ctrl+Pause has a different scancode than just Pause
   738              if (scancode == 0x146)
   739                  scancode = 0x45;
   740  
   741              // HACK: CJK IME sets the extended bit for right Shift
   742              if (scancode == 0x136)
   743                  scancode = 0x36;
   744  
   745              key = _glfw.win32.keycodes[scancode];
   746  
   747              // The Ctrl keys require special handling
   748              if (wParam == VK_CONTROL)
   749              {
   750                  if (HIWORD(lParam) & KF_EXTENDED)
   751                  {
   752                      // Right side keys have the extended key bit set
   753                      key = GLFW_KEY_RIGHT_CONTROL;
   754                  }
   755                  else
   756                  {
   757                      // NOTE: Alt Gr sends Left Ctrl followed by Right Alt
   758                      // HACK: We only want one event for Alt Gr, so if we detect
   759                      //       this sequence we discard this Left Ctrl message now
   760                      //       and later report Right Alt normally
   761                      MSG next;
   762                      const DWORD time = GetMessageTime();
   763  
   764                      if (PeekMessageW(&next, NULL, 0, 0, PM_NOREMOVE))
   765                      {
   766                          if (next.message == WM_KEYDOWN ||
   767                              next.message == WM_SYSKEYDOWN ||
   768                              next.message == WM_KEYUP ||
   769                              next.message == WM_SYSKEYUP)
   770                          {
   771                              if (next.wParam == VK_MENU &&
   772                                  (HIWORD(next.lParam) & KF_EXTENDED) &&
   773                                  next.time == time)
   774                              {
   775                                  // Next message is Right Alt down so discard this
   776                                  break;
   777                              }
   778                          }
   779                      }
   780  
   781                      // This is a regular Left Ctrl message
   782                      key = GLFW_KEY_LEFT_CONTROL;
   783                  }
   784              }
   785              else if (wParam == VK_PROCESSKEY)
   786              {
   787                  // IME notifies that keys have been filtered by setting the
   788                  // virtual key-code to VK_PROCESSKEY
   789                  break;
   790              }
   791  
   792              if (action == GLFW_RELEASE && wParam == VK_SHIFT)
   793              {
   794                  // HACK: Release both Shift keys on Shift up event, as when both
   795                  //       are pressed the first release does not emit any event
   796                  // NOTE: The other half of this is in _glfwPollEventsWin32
   797                  _glfwInputKey(window, GLFW_KEY_LEFT_SHIFT, scancode, action, mods);
   798                  _glfwInputKey(window, GLFW_KEY_RIGHT_SHIFT, scancode, action, mods);
   799              }
   800              else if (wParam == VK_SNAPSHOT)
   801              {
   802                  // HACK: Key down is not reported for the Print Screen key
   803                  _glfwInputKey(window, key, scancode, GLFW_PRESS, mods);
   804                  _glfwInputKey(window, key, scancode, GLFW_RELEASE, mods);
   805              }
   806              else
   807                  _glfwInputKey(window, key, scancode, action, mods);
   808  
   809              break;
   810          }
   811  
   812          case WM_LBUTTONDOWN:
   813          case WM_RBUTTONDOWN:
   814          case WM_MBUTTONDOWN:
   815          case WM_XBUTTONDOWN:
   816          case WM_LBUTTONUP:
   817          case WM_RBUTTONUP:
   818          case WM_MBUTTONUP:
   819          case WM_XBUTTONUP:
   820          {
   821              int i, button, action;
   822  
   823              if (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP)
   824                  button = GLFW_MOUSE_BUTTON_LEFT;
   825              else if (uMsg == WM_RBUTTONDOWN || uMsg == WM_RBUTTONUP)
   826                  button = GLFW_MOUSE_BUTTON_RIGHT;
   827              else if (uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONUP)
   828                  button = GLFW_MOUSE_BUTTON_MIDDLE;
   829              else if (GET_XBUTTON_WPARAM(wParam) == XBUTTON1)
   830                  button = GLFW_MOUSE_BUTTON_4;
   831              else
   832                  button = GLFW_MOUSE_BUTTON_5;
   833  
   834              if (uMsg == WM_LBUTTONDOWN || uMsg == WM_RBUTTONDOWN ||
   835                  uMsg == WM_MBUTTONDOWN || uMsg == WM_XBUTTONDOWN)
   836              {
   837                  action = GLFW_PRESS;
   838              }
   839              else
   840                  action = GLFW_RELEASE;
   841  
   842              for (i = 0;  i <= GLFW_MOUSE_BUTTON_LAST;  i++)
   843              {
   844                  if (window->mouseButtons[i] == GLFW_PRESS)
   845                      break;
   846              }
   847  
   848              if (i > GLFW_MOUSE_BUTTON_LAST)
   849                  SetCapture(hWnd);
   850  
   851              _glfwInputMouseClick(window, button, action, getKeyMods());
   852  
   853              for (i = 0;  i <= GLFW_MOUSE_BUTTON_LAST;  i++)
   854              {
   855                  if (window->mouseButtons[i] == GLFW_PRESS)
   856                      break;
   857              }
   858  
   859              if (i > GLFW_MOUSE_BUTTON_LAST)
   860                  ReleaseCapture();
   861  
   862              if (uMsg == WM_XBUTTONDOWN || uMsg == WM_XBUTTONUP)
   863                  return TRUE;
   864  
   865              return 0;
   866          }
   867  
   868          case WM_MOUSEMOVE:
   869          {
   870              const int x = GET_X_LPARAM(lParam);
   871              const int y = GET_Y_LPARAM(lParam);
   872  
   873              if (!window->win32.cursorTracked)
   874              {
   875                  TRACKMOUSEEVENT tme;
   876                  ZeroMemory(&tme, sizeof(tme));
   877                  tme.cbSize = sizeof(tme);
   878                  tme.dwFlags = TME_LEAVE;
   879                  tme.hwndTrack = window->win32.handle;
   880                  TrackMouseEvent(&tme);
   881  
   882                  window->win32.cursorTracked = GLFW_TRUE;
   883                  _glfwInputCursorEnter(window, GLFW_TRUE);
   884              }
   885  
   886              if (window->cursorMode == GLFW_CURSOR_DISABLED)
   887              {
   888                  const int dx = x - window->win32.lastCursorPosX;
   889                  const int dy = y - window->win32.lastCursorPosY;
   890  
   891                  if (_glfw.win32.disabledCursorWindow != window)
   892                      break;
   893                  if (window->rawMouseMotion)
   894                      break;
   895  
   896                  _glfwInputCursorPos(window,
   897                                      window->virtualCursorPosX + dx,
   898                                      window->virtualCursorPosY + dy);
   899              }
   900              else
   901                  _glfwInputCursorPos(window, x, y);
   902  
   903              window->win32.lastCursorPosX = x;
   904              window->win32.lastCursorPosY = y;
   905  
   906              return 0;
   907          }
   908  
   909          case WM_INPUT:
   910          {
   911              UINT size = 0;
   912              HRAWINPUT ri = (HRAWINPUT) lParam;
   913              RAWINPUT* data = NULL;
   914              int dx, dy;
   915  
   916              if (_glfw.win32.disabledCursorWindow != window)
   917                  break;
   918              if (!window->rawMouseMotion)
   919                  break;
   920  
   921              GetRawInputData(ri, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER));
   922              if (size > (UINT) _glfw.win32.rawInputSize)
   923              {
   924                  _glfw_free(_glfw.win32.rawInput);
   925                  _glfw.win32.rawInput = _glfw_calloc(size, 1);
   926                  _glfw.win32.rawInputSize = size;
   927              }
   928  
   929              size = _glfw.win32.rawInputSize;
   930              if (GetRawInputData(ri, RID_INPUT,
   931                                  _glfw.win32.rawInput, &size,
   932                                  sizeof(RAWINPUTHEADER)) == (UINT) -1)
   933              {
   934                  _glfwInputError(GLFW_PLATFORM_ERROR,
   935                                  "Win32: Failed to retrieve raw input data");
   936                  break;
   937              }
   938  
   939              data = _glfw.win32.rawInput;
   940              if (data->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE)
   941              {
   942                  dx = data->data.mouse.lLastX - window->win32.lastCursorPosX;
   943                  dy = data->data.mouse.lLastY - window->win32.lastCursorPosY;
   944              }
   945              else
   946              {
   947                  dx = data->data.mouse.lLastX;
   948                  dy = data->data.mouse.lLastY;
   949              }
   950  
   951              _glfwInputCursorPos(window,
   952                                  window->virtualCursorPosX + dx,
   953                                  window->virtualCursorPosY + dy);
   954  
   955              window->win32.lastCursorPosX += dx;
   956              window->win32.lastCursorPosY += dy;
   957              break;
   958          }
   959  
   960          case WM_MOUSELEAVE:
   961          {
   962              window->win32.cursorTracked = GLFW_FALSE;
   963              _glfwInputCursorEnter(window, GLFW_FALSE);
   964              return 0;
   965          }
   966  
   967          case WM_MOUSEWHEEL:
   968          {
   969              _glfwInputScroll(window, 0.0, (SHORT) HIWORD(wParam) / (double) WHEEL_DELTA);
   970              return 0;
   971          }
   972  
   973          case WM_MOUSEHWHEEL:
   974          {
   975              // This message is only sent on Windows Vista and later
   976              // NOTE: The X-axis is inverted for consistency with macOS and X11
   977              _glfwInputScroll(window, -((SHORT) HIWORD(wParam) / (double) WHEEL_DELTA), 0.0);
   978              return 0;
   979          }
   980  
   981          case WM_ENTERSIZEMOVE:
   982          case WM_ENTERMENULOOP:
   983          {
   984              if (window->win32.frameAction)
   985                  break;
   986  
   987              // HACK: Enable the cursor while the user is moving or
   988              //       resizing the window or using the window menu
   989              if (window->cursorMode == GLFW_CURSOR_DISABLED)
   990                  enableCursor(window);
   991              else if (window->cursorMode == GLFW_CURSOR_CAPTURED)
   992                  releaseCursor();
   993  
   994              break;
   995          }
   996  
   997          case WM_EXITSIZEMOVE:
   998          case WM_EXITMENULOOP:
   999          {
  1000              if (window->win32.frameAction)
  1001                  break;
  1002  
  1003              // HACK: Disable the cursor once the user is done moving or
  1004              //       resizing the window or using the menu
  1005              if (window->cursorMode == GLFW_CURSOR_DISABLED)
  1006                  disableCursor(window);
  1007              else if (window->cursorMode == GLFW_CURSOR_CAPTURED)
  1008                  captureCursor(window);
  1009  
  1010              break;
  1011          }
  1012  
  1013          case WM_SIZE:
  1014          {
  1015              const int width = LOWORD(lParam);
  1016              const int height = HIWORD(lParam);
  1017              const GLFWbool iconified = wParam == SIZE_MINIMIZED;
  1018              const GLFWbool maximized = wParam == SIZE_MAXIMIZED ||
  1019                                         (window->win32.maximized &&
  1020                                          wParam != SIZE_RESTORED);
  1021  
  1022              if (_glfw.win32.capturedCursorWindow == window)
  1023                  captureCursor(window);
  1024  
  1025              if (window->win32.iconified != iconified)
  1026                  _glfwInputWindowIconify(window, iconified);
  1027  
  1028              if (window->win32.maximized != maximized)
  1029                  _glfwInputWindowMaximize(window, maximized);
  1030  
  1031              if (width != window->win32.width || height != window->win32.height)
  1032              {
  1033                  window->win32.width = width;
  1034                  window->win32.height = height;
  1035  
  1036                  _glfwInputFramebufferSize(window, width, height);
  1037                  _glfwInputWindowSize(window, width, height);
  1038              }
  1039  
  1040              if (window->monitor && window->win32.iconified != iconified)
  1041              {
  1042                  if (iconified)
  1043                      releaseMonitor(window);
  1044                  else
  1045                  {
  1046                      acquireMonitor(window);
  1047                      fitToMonitor(window);
  1048                  }
  1049              }
  1050  
  1051              window->win32.iconified = iconified;
  1052              window->win32.maximized = maximized;
  1053              return 0;
  1054          }
  1055  
  1056          case WM_MOVE:
  1057          {
  1058              if (_glfw.win32.capturedCursorWindow == window)
  1059                  captureCursor(window);
  1060  
  1061              // NOTE: This cannot use LOWORD/HIWORD recommended by MSDN, as
  1062              // those macros do not handle negative window positions correctly
  1063              _glfwInputWindowPos(window,
  1064                                  GET_X_LPARAM(lParam),
  1065                                  GET_Y_LPARAM(lParam));
  1066              return 0;
  1067          }
  1068  
  1069          case WM_SIZING:
  1070          {
  1071              if (window->numer == GLFW_DONT_CARE ||
  1072                  window->denom == GLFW_DONT_CARE)
  1073              {
  1074                  break;
  1075              }
  1076  
  1077              applyAspectRatio(window, (int) wParam, (RECT*) lParam);
  1078              return TRUE;
  1079          }
  1080  
  1081          case WM_GETMINMAXINFO:
  1082          {
  1083              int xoff, yoff;
  1084              UINT dpi = USER_DEFAULT_SCREEN_DPI;
  1085              MINMAXINFO* mmi = (MINMAXINFO*) lParam;
  1086  
  1087              if (window->monitor)
  1088                  break;
  1089  
  1090              if (_glfwIsWindows10Version1607OrGreaterWin32())
  1091                  dpi = GetDpiForWindow(window->win32.handle);
  1092  
  1093              getFullWindowSize(getWindowStyle(window), getWindowExStyle(window),
  1094                                0, 0, &xoff, &yoff, dpi);
  1095  
  1096              if (window->minwidth != GLFW_DONT_CARE &&
  1097                  window->minheight != GLFW_DONT_CARE)
  1098              {
  1099                  mmi->ptMinTrackSize.x = window->minwidth + xoff;
  1100                  mmi->ptMinTrackSize.y = window->minheight + yoff;
  1101              }
  1102  
  1103              if (window->maxwidth != GLFW_DONT_CARE &&
  1104                  window->maxheight != GLFW_DONT_CARE)
  1105              {
  1106                  mmi->ptMaxTrackSize.x = window->maxwidth + xoff;
  1107                  mmi->ptMaxTrackSize.y = window->maxheight + yoff;
  1108              }
  1109  
  1110              if (!window->decorated)
  1111              {
  1112                  MONITORINFO mi;
  1113                  const HMONITOR mh = MonitorFromWindow(window->win32.handle,
  1114                                                        MONITOR_DEFAULTTONEAREST);
  1115  
  1116                  ZeroMemory(&mi, sizeof(mi));
  1117                  mi.cbSize = sizeof(mi);
  1118                  GetMonitorInfoW(mh, &mi);
  1119  
  1120                  mmi->ptMaxPosition.x = mi.rcWork.left - mi.rcMonitor.left;
  1121                  mmi->ptMaxPosition.y = mi.rcWork.top - mi.rcMonitor.top;
  1122                  mmi->ptMaxSize.x = mi.rcWork.right - mi.rcWork.left;
  1123                  mmi->ptMaxSize.y = mi.rcWork.bottom - mi.rcWork.top;
  1124              }
  1125  
  1126              return 0;
  1127          }
  1128  
  1129          case WM_PAINT:
  1130          {
  1131              _glfwInputWindowDamage(window);
  1132              break;
  1133          }
  1134  
  1135          case WM_ERASEBKGND:
  1136          {
  1137              return TRUE;
  1138          }
  1139  
  1140          case WM_NCACTIVATE:
  1141          case WM_NCPAINT:
  1142          {
  1143              // Prevent title bar from being drawn after restoring a minimized
  1144              // undecorated window
  1145              if (!window->decorated)
  1146                  return TRUE;
  1147  
  1148              break;
  1149          }
  1150  
  1151          case WM_DWMCOMPOSITIONCHANGED:
  1152          case WM_DWMCOLORIZATIONCOLORCHANGED:
  1153          {
  1154              if (window->win32.transparent)
  1155                  updateFramebufferTransparency(window);
  1156              return 0;
  1157          }
  1158  
  1159          case WM_GETDPISCALEDSIZE:
  1160          {
  1161              if (window->win32.scaleToMonitor)
  1162                  break;
  1163  
  1164              // Adjust the window size to keep the content area size constant
  1165              if (_glfwIsWindows10Version1703OrGreaterWin32())
  1166              {
  1167                  RECT source = {0}, target = {0};
  1168                  SIZE* size = (SIZE*) lParam;
  1169  
  1170                  AdjustWindowRectExForDpi(&source, getWindowStyle(window),
  1171                                           FALSE, getWindowExStyle(window),
  1172                                           GetDpiForWindow(window->win32.handle));
  1173                  AdjustWindowRectExForDpi(&target, getWindowStyle(window),
  1174                                           FALSE, getWindowExStyle(window),
  1175                                           LOWORD(wParam));
  1176  
  1177                  size->cx += (target.right - target.left) -
  1178                              (source.right - source.left);
  1179                  size->cy += (target.bottom - target.top) -
  1180                              (source.bottom - source.top);
  1181                  return TRUE;
  1182              }
  1183  
  1184              break;
  1185          }
  1186  
  1187          case WM_DPICHANGED:
  1188          {
  1189              const float xscale = HIWORD(wParam) / (float) USER_DEFAULT_SCREEN_DPI;
  1190              const float yscale = LOWORD(wParam) / (float) USER_DEFAULT_SCREEN_DPI;
  1191  
  1192              // Resize windowed mode windows that either permit rescaling or that
  1193              // need it to compensate for non-client area scaling
  1194              if (!window->monitor &&
  1195                  (window->win32.scaleToMonitor ||
  1196                   _glfwIsWindows10Version1703OrGreaterWin32()))
  1197              {
  1198                  RECT* suggested = (RECT*) lParam;
  1199                  SetWindowPos(window->win32.handle, HWND_TOP,
  1200                               suggested->left,
  1201                               suggested->top,
  1202                               suggested->right - suggested->left,
  1203                               suggested->bottom - suggested->top,
  1204                               SWP_NOACTIVATE | SWP_NOZORDER);
  1205              }
  1206  
  1207              _glfwInputWindowContentScale(window, xscale, yscale);
  1208              break;
  1209          }
  1210  
  1211          case WM_SETCURSOR:
  1212          {
  1213              if (LOWORD(lParam) == HTCLIENT)
  1214              {
  1215                  updateCursorImage(window);
  1216                  return TRUE;
  1217              }
  1218  
  1219              break;
  1220          }
  1221  
  1222          case WM_DROPFILES:
  1223          {
  1224              HDROP drop = (HDROP) wParam;
  1225              POINT pt;
  1226              int i;
  1227  
  1228              const int count = DragQueryFileW(drop, 0xffffffff, NULL, 0);
  1229              char** paths = _glfw_calloc(count, sizeof(char*));
  1230  
  1231              // Move the mouse to the position of the drop
  1232              DragQueryPoint(drop, &pt);
  1233              _glfwInputCursorPos(window, pt.x, pt.y);
  1234  
  1235              for (i = 0;  i < count;  i++)
  1236              {
  1237                  const UINT length = DragQueryFileW(drop, i, NULL, 0);
  1238                  WCHAR* buffer = _glfw_calloc((size_t) length + 1, sizeof(WCHAR));
  1239  
  1240                  DragQueryFileW(drop, i, buffer, length + 1);
  1241                  paths[i] = _glfwCreateUTF8FromWideStringWin32(buffer);
  1242  
  1243                  _glfw_free(buffer);
  1244              }
  1245  
  1246              _glfwInputDrop(window, count, (const char**) paths);
  1247  
  1248              for (i = 0;  i < count;  i++)
  1249                  _glfw_free(paths[i]);
  1250              _glfw_free(paths);
  1251  
  1252              DragFinish(drop);
  1253              return 0;
  1254          }
  1255      }
  1256  
  1257      return DefWindowProcW(hWnd, uMsg, wParam, lParam);
  1258  }
  1259  
  1260  // Creates the GLFW window
  1261  //
  1262  static int createNativeWindow(_GLFWwindow* window,
  1263                                const _GLFWwndconfig* wndconfig,
  1264                                const _GLFWfbconfig* fbconfig)
  1265  {
  1266      int xpos, ypos, fullWidth, fullHeight;
  1267      WCHAR* wideTitle;
  1268      DWORD style = getWindowStyle(window);
  1269      DWORD exStyle = getWindowExStyle(window);
  1270  
  1271      if (!_glfw.win32.mainWindowClass)
  1272      {
  1273          WNDCLASSEXW wc = { sizeof(wc) };
  1274          wc.style         = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
  1275          wc.lpfnWndProc   = windowProc;
  1276          wc.hInstance     = _glfw.win32.instance;
  1277          wc.hCursor       = LoadCursorW(NULL, IDC_ARROW);
  1278  #if defined(_GLFW_WNDCLASSNAME)
  1279          wc.lpszClassName = _GLFW_WNDCLASSNAME;
  1280  #else
  1281          wc.lpszClassName = L"GLFW30";
  1282  #endif
  1283          // Load user-provided icon if available
  1284          wc.hIcon = LoadImageW(GetModuleHandleW(NULL),
  1285                                L"GLFW_ICON", IMAGE_ICON,
  1286                                0, 0, LR_DEFAULTSIZE | LR_SHARED);
  1287          if (!wc.hIcon)
  1288          {
  1289              // No user-provided icon found, load default icon
  1290              wc.hIcon = LoadImageW(NULL,
  1291                                    IDI_APPLICATION, IMAGE_ICON,
  1292                                    0, 0, LR_DEFAULTSIZE | LR_SHARED);
  1293          }
  1294  
  1295          _glfw.win32.mainWindowClass = RegisterClassExW(&wc);
  1296          if (!_glfw.win32.mainWindowClass)
  1297          {
  1298              _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
  1299                                   "Win32: Failed to register window class");
  1300              return GLFW_FALSE;
  1301          }
  1302      }
  1303  
  1304      if (window->monitor)
  1305      {
  1306          MONITORINFO mi = { sizeof(mi) };
  1307          GetMonitorInfoW(window->monitor->win32.handle, &mi);
  1308  
  1309          // NOTE: This window placement is temporary and approximate, as the
  1310          //       correct position and size cannot be known until the monitor
  1311          //       video mode has been picked in _glfwSetVideoModeWin32
  1312          xpos = mi.rcMonitor.left;
  1313          ypos = mi.rcMonitor.top;
  1314          fullWidth  = mi.rcMonitor.right - mi.rcMonitor.left;
  1315          fullHeight = mi.rcMonitor.bottom - mi.rcMonitor.top;
  1316      }
  1317      else
  1318      {
  1319          RECT rect = { 0, 0, wndconfig->width, wndconfig->height };
  1320  
  1321          window->win32.maximized = wndconfig->maximized;
  1322          if (wndconfig->maximized)
  1323              style |= WS_MAXIMIZE;
  1324  
  1325          AdjustWindowRectEx(&rect, style, FALSE, exStyle);
  1326  
  1327          if (wndconfig->xpos == GLFW_ANY_POSITION && wndconfig->ypos == GLFW_ANY_POSITION)
  1328          {
  1329              xpos = CW_USEDEFAULT;
  1330              ypos = CW_USEDEFAULT;
  1331          }
  1332          else
  1333          {
  1334              xpos = wndconfig->xpos + rect.left;
  1335              ypos = wndconfig->ypos + rect.top;
  1336          }
  1337  
  1338          fullWidth = rect.right - rect.left;
  1339          fullHeight = rect.bottom - rect.top;
  1340      }
  1341  
  1342      wideTitle = _glfwCreateWideStringFromUTF8Win32(wndconfig->title);
  1343      if (!wideTitle)
  1344          return GLFW_FALSE;
  1345  
  1346      window->win32.handle = CreateWindowExW(exStyle,
  1347                                             MAKEINTATOM(_glfw.win32.mainWindowClass),
  1348                                             wideTitle,
  1349                                             style,
  1350                                             xpos, ypos,
  1351                                             fullWidth, fullHeight,
  1352                                             NULL, // No parent window
  1353                                             NULL, // No window menu
  1354                                             _glfw.win32.instance,
  1355                                             (LPVOID) wndconfig);
  1356  
  1357      _glfw_free(wideTitle);
  1358  
  1359      if (!window->win32.handle)
  1360      {
  1361          _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
  1362                               "Win32: Failed to create window");
  1363          return GLFW_FALSE;
  1364      }
  1365  
  1366      SetPropW(window->win32.handle, L"GLFW", window);
  1367  
  1368      if (IsWindows7OrGreater())
  1369      {
  1370          ChangeWindowMessageFilterEx(window->win32.handle,
  1371                                      WM_DROPFILES, MSGFLT_ALLOW, NULL);
  1372          ChangeWindowMessageFilterEx(window->win32.handle,
  1373                                      WM_COPYDATA, MSGFLT_ALLOW, NULL);
  1374          ChangeWindowMessageFilterEx(window->win32.handle,
  1375                                      WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL);
  1376      }
  1377  
  1378      window->win32.scaleToMonitor = wndconfig->scaleToMonitor;
  1379      window->win32.keymenu = wndconfig->win32.keymenu;
  1380  
  1381      if (!window->monitor)
  1382      {
  1383          RECT rect = { 0, 0, wndconfig->width, wndconfig->height };
  1384          WINDOWPLACEMENT wp = { sizeof(wp) };
  1385          const HMONITOR mh = MonitorFromWindow(window->win32.handle,
  1386                                                MONITOR_DEFAULTTONEAREST);
  1387  
  1388          // Adjust window rect to account for DPI scaling of the window frame and
  1389          // (if enabled) DPI scaling of the content area
  1390          // This cannot be done until we know what monitor the window was placed on
  1391          // Only update the restored window rect as the window may be maximized
  1392  
  1393          if (wndconfig->scaleToMonitor)
  1394          {
  1395              float xscale, yscale;
  1396              _glfwGetHMONITORContentScaleWin32(mh, &xscale, &yscale);
  1397  
  1398              if (xscale > 0.f && yscale > 0.f)
  1399              {
  1400                  rect.right = (int) (rect.right * xscale);
  1401                  rect.bottom = (int) (rect.bottom * yscale);
  1402              }
  1403          }
  1404  
  1405          if (_glfwIsWindows10Version1607OrGreaterWin32())
  1406          {
  1407              AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle,
  1408                                       GetDpiForWindow(window->win32.handle));
  1409          }
  1410          else
  1411              AdjustWindowRectEx(&rect, style, FALSE, exStyle);
  1412  
  1413          GetWindowPlacement(window->win32.handle, &wp);
  1414          OffsetRect(&rect,
  1415                     wp.rcNormalPosition.left - rect.left,
  1416                     wp.rcNormalPosition.top - rect.top);
  1417  
  1418          wp.rcNormalPosition = rect;
  1419          wp.showCmd = SW_HIDE;
  1420          SetWindowPlacement(window->win32.handle, &wp);
  1421  
  1422          // Adjust rect of maximized undecorated window, because by default Windows will
  1423          // make such a window cover the whole monitor instead of its workarea
  1424  
  1425          if (wndconfig->maximized && !wndconfig->decorated)
  1426          {
  1427              MONITORINFO mi = { sizeof(mi) };
  1428              GetMonitorInfoW(mh, &mi);
  1429  
  1430              SetWindowPos(window->win32.handle, HWND_TOP,
  1431                           mi.rcWork.left,
  1432                           mi.rcWork.top,
  1433                           mi.rcWork.right - mi.rcWork.left,
  1434                           mi.rcWork.bottom - mi.rcWork.top,
  1435                           SWP_NOACTIVATE | SWP_NOZORDER);
  1436          }
  1437      }
  1438  
  1439      DragAcceptFiles(window->win32.handle, TRUE);
  1440  
  1441      if (fbconfig->transparent)
  1442      {
  1443          updateFramebufferTransparency(window);
  1444          window->win32.transparent = GLFW_TRUE;
  1445      }
  1446  
  1447      _glfwGetWindowSizeWin32(window, &window->win32.width, &window->win32.height);
  1448  
  1449      return GLFW_TRUE;
  1450  }
  1451  
  1452  GLFWbool _glfwCreateWindowWin32(_GLFWwindow* window,
  1453                                  const _GLFWwndconfig* wndconfig,
  1454                                  const _GLFWctxconfig* ctxconfig,
  1455                                  const _GLFWfbconfig* fbconfig)
  1456  {
  1457      if (!createNativeWindow(window, wndconfig, fbconfig))
  1458          return GLFW_FALSE;
  1459  
  1460      if (ctxconfig->client != GLFW_NO_API)
  1461      {
  1462          if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
  1463          {
  1464              if (!_glfwInitWGL())
  1465                  return GLFW_FALSE;
  1466              if (!_glfwCreateContextWGL(window, ctxconfig, fbconfig))
  1467                  return GLFW_FALSE;
  1468          }
  1469          else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
  1470          {
  1471              if (!_glfwInitEGL())
  1472                  return GLFW_FALSE;
  1473              if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
  1474                  return GLFW_FALSE;
  1475          }
  1476          else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
  1477          {
  1478              if (!_glfwInitOSMesa())
  1479                  return GLFW_FALSE;
  1480              if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
  1481                  return GLFW_FALSE;
  1482          }
  1483  
  1484          if (!_glfwRefreshContextAttribs(window, ctxconfig))
  1485              return GLFW_FALSE;
  1486      }
  1487  
  1488      if (wndconfig->mousePassthrough)
  1489          _glfwSetWindowMousePassthroughWin32(window, GLFW_TRUE);
  1490  
  1491      if (window->monitor)
  1492      {
  1493          _glfwShowWindowWin32(window);
  1494          _glfwFocusWindowWin32(window);
  1495          acquireMonitor(window);
  1496          fitToMonitor(window);
  1497  
  1498          if (wndconfig->centerCursor)
  1499              _glfwCenterCursorInContentArea(window);
  1500      }
  1501      else
  1502      {
  1503          if (wndconfig->visible)
  1504          {
  1505              _glfwShowWindowWin32(window);
  1506              if (wndconfig->focused)
  1507                  _glfwFocusWindowWin32(window);
  1508          }
  1509      }
  1510  
  1511      return GLFW_TRUE;
  1512  }
  1513  
  1514  void _glfwDestroyWindowWin32(_GLFWwindow* window)
  1515  {
  1516      if (window->monitor)
  1517          releaseMonitor(window);
  1518  
  1519      if (window->context.destroy)
  1520          window->context.destroy(window);
  1521  
  1522      if (_glfw.win32.disabledCursorWindow == window)
  1523          enableCursor(window);
  1524  
  1525      if (_glfw.win32.capturedCursorWindow == window)
  1526          releaseCursor();
  1527  
  1528      if (window->win32.handle)
  1529      {
  1530          RemovePropW(window->win32.handle, L"GLFW");
  1531          DestroyWindow(window->win32.handle);
  1532          window->win32.handle = NULL;
  1533      }
  1534  
  1535      if (window->win32.bigIcon)
  1536          DestroyIcon(window->win32.bigIcon);
  1537  
  1538      if (window->win32.smallIcon)
  1539          DestroyIcon(window->win32.smallIcon);
  1540  }
  1541  
  1542  void _glfwSetWindowTitleWin32(_GLFWwindow* window, const char* title)
  1543  {
  1544      WCHAR* wideTitle = _glfwCreateWideStringFromUTF8Win32(title);
  1545      if (!wideTitle)
  1546          return;
  1547  
  1548      SetWindowTextW(window->win32.handle, wideTitle);
  1549      _glfw_free(wideTitle);
  1550  }
  1551  
  1552  void _glfwSetWindowIconWin32(_GLFWwindow* window, int count, const GLFWimage* images)
  1553  {
  1554      HICON bigIcon = NULL, smallIcon = NULL;
  1555  
  1556      if (count)
  1557      {
  1558          const GLFWimage* bigImage = chooseImage(count, images,
  1559                                                  GetSystemMetrics(SM_CXICON),
  1560                                                  GetSystemMetrics(SM_CYICON));
  1561          const GLFWimage* smallImage = chooseImage(count, images,
  1562                                                    GetSystemMetrics(SM_CXSMICON),
  1563                                                    GetSystemMetrics(SM_CYSMICON));
  1564  
  1565          bigIcon = createIcon(bigImage, 0, 0, GLFW_TRUE);
  1566          smallIcon = createIcon(smallImage, 0, 0, GLFW_TRUE);
  1567      }
  1568      else
  1569      {
  1570          bigIcon = (HICON) GetClassLongPtrW(window->win32.handle, GCLP_HICON);
  1571          smallIcon = (HICON) GetClassLongPtrW(window->win32.handle, GCLP_HICONSM);
  1572      }
  1573  
  1574      SendMessageW(window->win32.handle, WM_SETICON, ICON_BIG, (LPARAM) bigIcon);
  1575      SendMessageW(window->win32.handle, WM_SETICON, ICON_SMALL, (LPARAM) smallIcon);
  1576  
  1577      if (window->win32.bigIcon)
  1578          DestroyIcon(window->win32.bigIcon);
  1579  
  1580      if (window->win32.smallIcon)
  1581          DestroyIcon(window->win32.smallIcon);
  1582  
  1583      if (count)
  1584      {
  1585          window->win32.bigIcon = bigIcon;
  1586          window->win32.smallIcon = smallIcon;
  1587      }
  1588  }
  1589  
  1590  void _glfwGetWindowPosWin32(_GLFWwindow* window, int* xpos, int* ypos)
  1591  {
  1592      POINT pos = { 0, 0 };
  1593      ClientToScreen(window->win32.handle, &pos);
  1594  
  1595      if (xpos)
  1596          *xpos = pos.x;
  1597      if (ypos)
  1598          *ypos = pos.y;
  1599  }
  1600  
  1601  void _glfwSetWindowPosWin32(_GLFWwindow* window, int xpos, int ypos)
  1602  {
  1603      RECT rect = { xpos, ypos, xpos, ypos };
  1604  
  1605      if (_glfwIsWindows10Version1607OrGreaterWin32())
  1606      {
  1607          AdjustWindowRectExForDpi(&rect, getWindowStyle(window),
  1608                                   FALSE, getWindowExStyle(window),
  1609                                   GetDpiForWindow(window->win32.handle));
  1610      }
  1611      else
  1612      {
  1613          AdjustWindowRectEx(&rect, getWindowStyle(window),
  1614                             FALSE, getWindowExStyle(window));
  1615      }
  1616  
  1617      SetWindowPos(window->win32.handle, NULL, rect.left, rect.top, 0, 0,
  1618                   SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE);
  1619  }
  1620  
  1621  void _glfwGetWindowSizeWin32(_GLFWwindow* window, int* width, int* height)
  1622  {
  1623      RECT area;
  1624      GetClientRect(window->win32.handle, &area);
  1625  
  1626      if (width)
  1627          *width = area.right;
  1628      if (height)
  1629          *height = area.bottom;
  1630  }
  1631  
  1632  void _glfwSetWindowSizeWin32(_GLFWwindow* window, int width, int height)
  1633  {
  1634      if (window->monitor)
  1635      {
  1636          if (window->monitor->window == window)
  1637          {
  1638              acquireMonitor(window);
  1639              fitToMonitor(window);
  1640          }
  1641      }
  1642      else
  1643      {
  1644          RECT rect = { 0, 0, width, height };
  1645  
  1646          if (_glfwIsWindows10Version1607OrGreaterWin32())
  1647          {
  1648              AdjustWindowRectExForDpi(&rect, getWindowStyle(window),
  1649                                       FALSE, getWindowExStyle(window),
  1650                                       GetDpiForWindow(window->win32.handle));
  1651          }
  1652          else
  1653          {
  1654              AdjustWindowRectEx(&rect, getWindowStyle(window),
  1655                                 FALSE, getWindowExStyle(window));
  1656          }
  1657  
  1658          SetWindowPos(window->win32.handle, HWND_TOP,
  1659                       0, 0, rect.right - rect.left, rect.bottom - rect.top,
  1660                       SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER);
  1661      }
  1662  }
  1663  
  1664  void _glfwSetWindowSizeLimitsWin32(_GLFWwindow* window,
  1665                                     int minwidth, int minheight,
  1666                                     int maxwidth, int maxheight)
  1667  {
  1668      RECT area;
  1669  
  1670      if ((minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE) &&
  1671          (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE))
  1672      {
  1673          return;
  1674      }
  1675  
  1676      GetWindowRect(window->win32.handle, &area);
  1677      MoveWindow(window->win32.handle,
  1678                 area.left, area.top,
  1679                 area.right - area.left,
  1680                 area.bottom - area.top, TRUE);
  1681  }
  1682  
  1683  void _glfwSetWindowAspectRatioWin32(_GLFWwindow* window, int numer, int denom)
  1684  {
  1685      RECT area;
  1686  
  1687      if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE)
  1688          return;
  1689  
  1690      GetWindowRect(window->win32.handle, &area);
  1691      applyAspectRatio(window, WMSZ_BOTTOMRIGHT, &area);
  1692      MoveWindow(window->win32.handle,
  1693                 area.left, area.top,
  1694                 area.right - area.left,
  1695                 area.bottom - area.top, TRUE);
  1696  }
  1697  
  1698  void _glfwGetFramebufferSizeWin32(_GLFWwindow* window, int* width, int* height)
  1699  {
  1700      _glfwGetWindowSizeWin32(window, width, height);
  1701  }
  1702  
  1703  void _glfwGetWindowFrameSizeWin32(_GLFWwindow* window,
  1704                                    int* left, int* top,
  1705                                    int* right, int* bottom)
  1706  {
  1707      RECT rect;
  1708      int width, height;
  1709  
  1710      _glfwGetWindowSizeWin32(window, &width, &height);
  1711      SetRect(&rect, 0, 0, width, height);
  1712  
  1713      if (_glfwIsWindows10Version1607OrGreaterWin32())
  1714      {
  1715          AdjustWindowRectExForDpi(&rect, getWindowStyle(window),
  1716                                   FALSE, getWindowExStyle(window),
  1717                                   GetDpiForWindow(window->win32.handle));
  1718      }
  1719      else
  1720      {
  1721          AdjustWindowRectEx(&rect, getWindowStyle(window),
  1722                             FALSE, getWindowExStyle(window));
  1723      }
  1724  
  1725      if (left)
  1726          *left = -rect.left;
  1727      if (top)
  1728          *top = -rect.top;
  1729      if (right)
  1730          *right = rect.right - width;
  1731      if (bottom)
  1732          *bottom = rect.bottom - height;
  1733  }
  1734  
  1735  void _glfwGetWindowContentScaleWin32(_GLFWwindow* window, float* xscale, float* yscale)
  1736  {
  1737      const HANDLE handle = MonitorFromWindow(window->win32.handle,
  1738                                              MONITOR_DEFAULTTONEAREST);
  1739      _glfwGetHMONITORContentScaleWin32(handle, xscale, yscale);
  1740  }
  1741  
  1742  void _glfwIconifyWindowWin32(_GLFWwindow* window)
  1743  {
  1744      ShowWindow(window->win32.handle, SW_MINIMIZE);
  1745  }
  1746  
  1747  void _glfwRestoreWindowWin32(_GLFWwindow* window)
  1748  {
  1749      ShowWindow(window->win32.handle, SW_RESTORE);
  1750  }
  1751  
  1752  void _glfwMaximizeWindowWin32(_GLFWwindow* window)
  1753  {
  1754      if (IsWindowVisible(window->win32.handle))
  1755          ShowWindow(window->win32.handle, SW_MAXIMIZE);
  1756      else
  1757          maximizeWindowManually(window);
  1758  }
  1759  
  1760  void _glfwShowWindowWin32(_GLFWwindow* window)
  1761  {
  1762      ShowWindow(window->win32.handle, SW_SHOWNA);
  1763  }
  1764  
  1765  void _glfwHideWindowWin32(_GLFWwindow* window)
  1766  {
  1767      ShowWindow(window->win32.handle, SW_HIDE);
  1768  }
  1769  
  1770  void _glfwRequestWindowAttentionWin32(_GLFWwindow* window)
  1771  {
  1772      FlashWindow(window->win32.handle, TRUE);
  1773  }
  1774  
  1775  void _glfwFocusWindowWin32(_GLFWwindow* window)
  1776  {
  1777      BringWindowToTop(window->win32.handle);
  1778      SetForegroundWindow(window->win32.handle);
  1779      SetFocus(window->win32.handle);
  1780  }
  1781  
  1782  void _glfwSetWindowMonitorWin32(_GLFWwindow* window,
  1783                                  _GLFWmonitor* monitor,
  1784                                  int xpos, int ypos,
  1785                                  int width, int height,
  1786                                  int refreshRate)
  1787  {
  1788      if (window->monitor == monitor)
  1789      {
  1790          if (monitor)
  1791          {
  1792              if (monitor->window == window)
  1793              {
  1794                  acquireMonitor(window);
  1795                  fitToMonitor(window);
  1796              }
  1797          }
  1798          else
  1799          {
  1800              RECT rect = { xpos, ypos, xpos + width, ypos + height };
  1801  
  1802              if (_glfwIsWindows10Version1607OrGreaterWin32())
  1803              {
  1804                  AdjustWindowRectExForDpi(&rect, getWindowStyle(window),
  1805                                           FALSE, getWindowExStyle(window),
  1806                                           GetDpiForWindow(window->win32.handle));
  1807              }
  1808              else
  1809              {
  1810                  AdjustWindowRectEx(&rect, getWindowStyle(window),
  1811                                     FALSE, getWindowExStyle(window));
  1812              }
  1813  
  1814              SetWindowPos(window->win32.handle, HWND_TOP,
  1815                           rect.left, rect.top,
  1816                           rect.right - rect.left, rect.bottom - rect.top,
  1817                           SWP_NOCOPYBITS | SWP_NOACTIVATE | SWP_NOZORDER);
  1818          }
  1819  
  1820          return;
  1821      }
  1822  
  1823      if (window->monitor)
  1824          releaseMonitor(window);
  1825  
  1826      _glfwInputWindowMonitor(window, monitor);
  1827  
  1828      if (window->monitor)
  1829      {
  1830          MONITORINFO mi = { sizeof(mi) };
  1831          UINT flags = SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOCOPYBITS;
  1832  
  1833          if (window->decorated)
  1834          {
  1835              DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE);
  1836              style &= ~WS_OVERLAPPEDWINDOW;
  1837              style |= getWindowStyle(window);
  1838              SetWindowLongW(window->win32.handle, GWL_STYLE, style);
  1839              flags |= SWP_FRAMECHANGED;
  1840          }
  1841  
  1842          acquireMonitor(window);
  1843  
  1844          GetMonitorInfoW(window->monitor->win32.handle, &mi);
  1845          SetWindowPos(window->win32.handle, HWND_TOPMOST,
  1846                       mi.rcMonitor.left,
  1847                       mi.rcMonitor.top,
  1848                       mi.rcMonitor.right - mi.rcMonitor.left,
  1849                       mi.rcMonitor.bottom - mi.rcMonitor.top,
  1850                       flags);
  1851      }
  1852      else
  1853      {
  1854          HWND after;
  1855          RECT rect = { xpos, ypos, xpos + width, ypos + height };
  1856          DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE);
  1857          UINT flags = SWP_NOACTIVATE | SWP_NOCOPYBITS;
  1858  
  1859          if (window->decorated)
  1860          {
  1861              style &= ~WS_POPUP;
  1862              style |= getWindowStyle(window);
  1863              SetWindowLongW(window->win32.handle, GWL_STYLE, style);
  1864  
  1865              flags |= SWP_FRAMECHANGED;
  1866          }
  1867  
  1868          if (window->floating)
  1869              after = HWND_TOPMOST;
  1870          else
  1871              after = HWND_NOTOPMOST;
  1872  
  1873          if (_glfwIsWindows10Version1607OrGreaterWin32())
  1874          {
  1875              AdjustWindowRectExForDpi(&rect, getWindowStyle(window),
  1876                                       FALSE, getWindowExStyle(window),
  1877                                       GetDpiForWindow(window->win32.handle));
  1878          }
  1879          else
  1880          {
  1881              AdjustWindowRectEx(&rect, getWindowStyle(window),
  1882                                 FALSE, getWindowExStyle(window));
  1883          }
  1884  
  1885          SetWindowPos(window->win32.handle, after,
  1886                       rect.left, rect.top,
  1887                       rect.right - rect.left, rect.bottom - rect.top,
  1888                       flags);
  1889      }
  1890  }
  1891  
  1892  GLFWbool _glfwWindowFocusedWin32(_GLFWwindow* window)
  1893  {
  1894      return window->win32.handle == GetActiveWindow();
  1895  }
  1896  
  1897  GLFWbool _glfwWindowIconifiedWin32(_GLFWwindow* window)
  1898  {
  1899      return IsIconic(window->win32.handle);
  1900  }
  1901  
  1902  GLFWbool _glfwWindowVisibleWin32(_GLFWwindow* window)
  1903  {
  1904      return IsWindowVisible(window->win32.handle);
  1905  }
  1906  
  1907  GLFWbool _glfwWindowMaximizedWin32(_GLFWwindow* window)
  1908  {
  1909      return IsZoomed(window->win32.handle);
  1910  }
  1911  
  1912  GLFWbool _glfwWindowHoveredWin32(_GLFWwindow* window)
  1913  {
  1914      return cursorInContentArea(window);
  1915  }
  1916  
  1917  GLFWbool _glfwFramebufferTransparentWin32(_GLFWwindow* window)
  1918  {
  1919      BOOL composition, opaque;
  1920      DWORD color;
  1921  
  1922      if (!window->win32.transparent)
  1923          return GLFW_FALSE;
  1924  
  1925      if (!IsWindowsVistaOrGreater())
  1926          return GLFW_FALSE;
  1927  
  1928      if (FAILED(DwmIsCompositionEnabled(&composition)) || !composition)
  1929          return GLFW_FALSE;
  1930  
  1931      if (!IsWindows8OrGreater())
  1932      {
  1933          // HACK: Disable framebuffer transparency on Windows 7 when the
  1934          //       colorization color is opaque, because otherwise the window
  1935          //       contents is blended additively with the previous frame instead
  1936          //       of replacing it
  1937          if (FAILED(DwmGetColorizationColor(&color, &opaque)) || opaque)
  1938              return GLFW_FALSE;
  1939      }
  1940  
  1941      return GLFW_TRUE;
  1942  }
  1943  
  1944  void _glfwSetWindowResizableWin32(_GLFWwindow* window, GLFWbool enabled)
  1945  {
  1946      updateWindowStyles(window);
  1947  }
  1948  
  1949  void _glfwSetWindowDecoratedWin32(_GLFWwindow* window, GLFWbool enabled)
  1950  {
  1951      updateWindowStyles(window);
  1952  }
  1953  
  1954  void _glfwSetWindowFloatingWin32(_GLFWwindow* window, GLFWbool enabled)
  1955  {
  1956      const HWND after = enabled ? HWND_TOPMOST : HWND_NOTOPMOST;
  1957      SetWindowPos(window->win32.handle, after, 0, 0, 0, 0,
  1958                   SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
  1959  }
  1960  
  1961  void _glfwSetWindowMousePassthroughWin32(_GLFWwindow* window, GLFWbool enabled)
  1962  {
  1963      COLORREF key = 0;
  1964      BYTE alpha = 0;
  1965      DWORD flags = 0;
  1966      DWORD exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE);
  1967  
  1968      if (exStyle & WS_EX_LAYERED)
  1969          GetLayeredWindowAttributes(window->win32.handle, &key, &alpha, &flags);
  1970  
  1971      if (enabled)
  1972          exStyle |= (WS_EX_TRANSPARENT | WS_EX_LAYERED);
  1973      else
  1974      {
  1975          exStyle &= ~WS_EX_TRANSPARENT;
  1976          // NOTE: Window opacity also needs the layered window style so do not
  1977          //       remove it if the window is alpha blended
  1978          if (exStyle & WS_EX_LAYERED)
  1979          {
  1980              if (!(flags & LWA_ALPHA))
  1981                  exStyle &= ~WS_EX_LAYERED;
  1982          }
  1983      }
  1984  
  1985      SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle);
  1986  
  1987      if (enabled)
  1988          SetLayeredWindowAttributes(window->win32.handle, key, alpha, flags);
  1989  }
  1990  
  1991  float _glfwGetWindowOpacityWin32(_GLFWwindow* window)
  1992  {
  1993      BYTE alpha;
  1994      DWORD flags;
  1995  
  1996      if ((GetWindowLongW(window->win32.handle, GWL_EXSTYLE) & WS_EX_LAYERED) &&
  1997          GetLayeredWindowAttributes(window->win32.handle, NULL, &alpha, &flags))
  1998      {
  1999          if (flags & LWA_ALPHA)
  2000              return alpha / 255.f;
  2001      }
  2002  
  2003      return 1.f;
  2004  }
  2005  
  2006  void _glfwSetWindowOpacityWin32(_GLFWwindow* window, float opacity)
  2007  {
  2008      LONG exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE);
  2009      if (opacity < 1.f || (exStyle & WS_EX_TRANSPARENT))
  2010      {
  2011          const BYTE alpha = (BYTE) (255 * opacity);
  2012          exStyle |= WS_EX_LAYERED;
  2013          SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle);
  2014          SetLayeredWindowAttributes(window->win32.handle, 0, alpha, LWA_ALPHA);
  2015      }
  2016      else if (exStyle & WS_EX_TRANSPARENT)
  2017      {
  2018          SetLayeredWindowAttributes(window->win32.handle, 0, 0, 0);
  2019      }
  2020      else
  2021      {
  2022          exStyle &= ~WS_EX_LAYERED;
  2023          SetWindowLongW(window->win32.handle, GWL_EXSTYLE, exStyle);
  2024      }
  2025  }
  2026  
  2027  void _glfwSetRawMouseMotionWin32(_GLFWwindow *window, GLFWbool enabled)
  2028  {
  2029      if (_glfw.win32.disabledCursorWindow != window)
  2030          return;
  2031  
  2032      if (enabled)
  2033          enableRawMouseMotion(window);
  2034      else
  2035          disableRawMouseMotion(window);
  2036  }
  2037  
  2038  GLFWbool _glfwRawMouseMotionSupportedWin32(void)
  2039  {
  2040      return GLFW_TRUE;
  2041  }
  2042  
  2043  void _glfwPollEventsWin32(void)
  2044  {
  2045      MSG msg;
  2046      HWND handle;
  2047      _GLFWwindow* window;
  2048  
  2049      while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
  2050      {
  2051          if (msg.message == WM_QUIT)
  2052          {
  2053              // NOTE: While GLFW does not itself post WM_QUIT, other processes
  2054              //       may post it to this one, for example Task Manager
  2055              // HACK: Treat WM_QUIT as a close on all windows
  2056  
  2057              window = _glfw.windowListHead;
  2058              while (window)
  2059              {
  2060                  _glfwInputWindowCloseRequest(window);
  2061                  window = window->next;
  2062              }
  2063          }
  2064          else
  2065          {
  2066              TranslateMessage(&msg);
  2067              DispatchMessageW(&msg);
  2068          }
  2069      }
  2070  
  2071      // HACK: Release modifier keys that the system did not emit KEYUP for
  2072      // NOTE: Shift keys on Windows tend to "stick" when both are pressed as
  2073      //       no key up message is generated by the first key release
  2074      // NOTE: Windows key is not reported as released by the Win+V hotkey
  2075      //       Other Win hotkeys are handled implicitly by _glfwInputWindowFocus
  2076      //       because they change the input focus
  2077      // NOTE: The other half of this is in the WM_*KEY* handler in windowProc
  2078      handle = GetActiveWindow();
  2079      if (handle)
  2080      {
  2081          window = GetPropW(handle, L"GLFW");
  2082          if (window)
  2083          {
  2084              int i;
  2085              const int keys[4][2] =
  2086              {
  2087                  { VK_LSHIFT, GLFW_KEY_LEFT_SHIFT },
  2088                  { VK_RSHIFT, GLFW_KEY_RIGHT_SHIFT },
  2089                  { VK_LWIN, GLFW_KEY_LEFT_SUPER },
  2090                  { VK_RWIN, GLFW_KEY_RIGHT_SUPER }
  2091              };
  2092  
  2093              for (i = 0;  i < 4;  i++)
  2094              {
  2095                  const int vk = keys[i][0];
  2096                  const int key = keys[i][1];
  2097                  const int scancode = _glfw.win32.scancodes[key];
  2098  
  2099                  if ((GetKeyState(vk) & 0x8000))
  2100                      continue;
  2101                  if (window->keys[key] != GLFW_PRESS)
  2102                      continue;
  2103  
  2104                  _glfwInputKey(window, key, scancode, GLFW_RELEASE, getKeyMods());
  2105              }
  2106          }
  2107      }
  2108  
  2109      window = _glfw.win32.disabledCursorWindow;
  2110      if (window)
  2111      {
  2112          int width, height;
  2113          _glfwGetWindowSizeWin32(window, &width, &height);
  2114  
  2115          // NOTE: Re-center the cursor only if it has moved since the last call,
  2116          //       to avoid breaking glfwWaitEvents with WM_MOUSEMOVE
  2117          if (window->win32.lastCursorPosX != width / 2 ||
  2118              window->win32.lastCursorPosY != height / 2)
  2119          {
  2120              _glfwSetCursorPosWin32(window, width / 2, height / 2);
  2121          }
  2122      }
  2123  }
  2124  
  2125  void _glfwWaitEventsWin32(void)
  2126  {
  2127      WaitMessage();
  2128  
  2129      _glfwPollEventsWin32();
  2130  }
  2131  
  2132  void _glfwWaitEventsTimeoutWin32(double timeout)
  2133  {
  2134      MsgWaitForMultipleObjects(0, NULL, FALSE, (DWORD) (timeout * 1e3), QS_ALLEVENTS);
  2135  
  2136      _glfwPollEventsWin32();
  2137  }
  2138  
  2139  void _glfwPostEmptyEventWin32(void)
  2140  {
  2141      PostMessageW(_glfw.win32.helperWindowHandle, WM_NULL, 0, 0);
  2142  }
  2143  
  2144  void _glfwGetCursorPosWin32(_GLFWwindow* window, double* xpos, double* ypos)
  2145  {
  2146      POINT pos;
  2147  
  2148      if (GetCursorPos(&pos))
  2149      {
  2150          ScreenToClient(window->win32.handle, &pos);
  2151  
  2152          if (xpos)
  2153              *xpos = pos.x;
  2154          if (ypos)
  2155              *ypos = pos.y;
  2156      }
  2157  }
  2158  
  2159  void _glfwSetCursorPosWin32(_GLFWwindow* window, double xpos, double ypos)
  2160  {
  2161      POINT pos = { (int) xpos, (int) ypos };
  2162  
  2163      // Store the new position so it can be recognized later
  2164      window->win32.lastCursorPosX = pos.x;
  2165      window->win32.lastCursorPosY = pos.y;
  2166  
  2167      ClientToScreen(window->win32.handle, &pos);
  2168      SetCursorPos(pos.x, pos.y);
  2169  }
  2170  
  2171  void _glfwSetCursorModeWin32(_GLFWwindow* window, int mode)
  2172  {
  2173      if (_glfwWindowFocusedWin32(window))
  2174      {
  2175          if (mode == GLFW_CURSOR_DISABLED)
  2176          {
  2177              _glfwGetCursorPosWin32(window,
  2178                                     &_glfw.win32.restoreCursorPosX,
  2179                                     &_glfw.win32.restoreCursorPosY);
  2180              _glfwCenterCursorInContentArea(window);
  2181              if (window->rawMouseMotion)
  2182                  enableRawMouseMotion(window);
  2183          }
  2184          else if (_glfw.win32.disabledCursorWindow == window)
  2185          {
  2186              if (window->rawMouseMotion)
  2187                  disableRawMouseMotion(window);
  2188          }
  2189  
  2190          if (mode == GLFW_CURSOR_DISABLED || mode == GLFW_CURSOR_CAPTURED)
  2191              captureCursor(window);
  2192          else
  2193              releaseCursor();
  2194  
  2195          if (mode == GLFW_CURSOR_DISABLED)
  2196              _glfw.win32.disabledCursorWindow = window;
  2197          else if (_glfw.win32.disabledCursorWindow == window)
  2198          {
  2199              _glfw.win32.disabledCursorWindow = NULL;
  2200              _glfwSetCursorPosWin32(window,
  2201                                     _glfw.win32.restoreCursorPosX,
  2202                                     _glfw.win32.restoreCursorPosY);
  2203          }
  2204      }
  2205  
  2206      if (cursorInContentArea(window))
  2207          updateCursorImage(window);
  2208  }
  2209  
  2210  const char* _glfwGetScancodeNameWin32(int scancode)
  2211  {
  2212      if (scancode < 0 || scancode > (KF_EXTENDED | 0xff) ||
  2213          _glfw.win32.keycodes[scancode] == GLFW_KEY_UNKNOWN)
  2214      {
  2215          _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode %i", scancode);
  2216          return NULL;
  2217      }
  2218  
  2219      return _glfw.win32.keynames[_glfw.win32.keycodes[scancode]];
  2220  }
  2221  
  2222  int _glfwGetKeyScancodeWin32(int key)
  2223  {
  2224      return _glfw.win32.scancodes[key];
  2225  }
  2226  
  2227  GLFWbool _glfwCreateCursorWin32(_GLFWcursor* cursor,
  2228                                  const GLFWimage* image,
  2229                                  int xhot, int yhot)
  2230  {
  2231      cursor->win32.handle = (HCURSOR) createIcon(image, xhot, yhot, GLFW_FALSE);
  2232      if (!cursor->win32.handle)
  2233          return GLFW_FALSE;
  2234  
  2235      return GLFW_TRUE;
  2236  }
  2237  
  2238  GLFWbool _glfwCreateStandardCursorWin32(_GLFWcursor* cursor, int shape)
  2239  {
  2240      int id = 0;
  2241  
  2242      switch (shape)
  2243      {
  2244          case GLFW_ARROW_CURSOR:
  2245              id = OCR_NORMAL;
  2246              break;
  2247          case GLFW_IBEAM_CURSOR:
  2248              id = OCR_IBEAM;
  2249              break;
  2250          case GLFW_CROSSHAIR_CURSOR:
  2251              id = OCR_CROSS;
  2252              break;
  2253          case GLFW_POINTING_HAND_CURSOR:
  2254              id = OCR_HAND;
  2255              break;
  2256          case GLFW_RESIZE_EW_CURSOR:
  2257              id = OCR_SIZEWE;
  2258              break;
  2259          case GLFW_RESIZE_NS_CURSOR:
  2260              id = OCR_SIZENS;
  2261              break;
  2262          case GLFW_RESIZE_NWSE_CURSOR:
  2263              id = OCR_SIZENWSE;
  2264              break;
  2265          case GLFW_RESIZE_NESW_CURSOR:
  2266              id = OCR_SIZENESW;
  2267              break;
  2268          case GLFW_RESIZE_ALL_CURSOR:
  2269              id = OCR_SIZEALL;
  2270              break;
  2271          case GLFW_NOT_ALLOWED_CURSOR:
  2272              id = OCR_NO;
  2273              break;
  2274          default:
  2275              _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Unknown standard cursor");
  2276              return GLFW_FALSE;
  2277      }
  2278  
  2279      cursor->win32.handle = LoadImageW(NULL,
  2280                                        MAKEINTRESOURCEW(id), IMAGE_CURSOR, 0, 0,
  2281                                        LR_DEFAULTSIZE | LR_SHARED);
  2282      if (!cursor->win32.handle)
  2283      {
  2284          _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
  2285                               "Win32: Failed to create standard cursor");
  2286          return GLFW_FALSE;
  2287      }
  2288  
  2289      return GLFW_TRUE;
  2290  }
  2291  
  2292  void _glfwDestroyCursorWin32(_GLFWcursor* cursor)
  2293  {
  2294      if (cursor->win32.handle)
  2295          DestroyIcon((HICON) cursor->win32.handle);
  2296  }
  2297  
  2298  void _glfwSetCursorWin32(_GLFWwindow* window, _GLFWcursor* cursor)
  2299  {
  2300      if (cursorInContentArea(window))
  2301          updateCursorImage(window);
  2302  }
  2303  
  2304  void _glfwSetClipboardStringWin32(const char* string)
  2305  {
  2306      int characterCount;
  2307      HANDLE object;
  2308      WCHAR* buffer;
  2309  
  2310      characterCount = MultiByteToWideChar(CP_UTF8, 0, string, -1, NULL, 0);
  2311      if (!characterCount)
  2312          return;
  2313  
  2314      object = GlobalAlloc(GMEM_MOVEABLE, characterCount * sizeof(WCHAR));
  2315      if (!object)
  2316      {
  2317          _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
  2318                               "Win32: Failed to allocate global handle for clipboard");
  2319          return;
  2320      }
  2321  
  2322      buffer = GlobalLock(object);
  2323      if (!buffer)
  2324      {
  2325          _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
  2326                               "Win32: Failed to lock global handle");
  2327          GlobalFree(object);
  2328          return;
  2329      }
  2330  
  2331      MultiByteToWideChar(CP_UTF8, 0, string, -1, buffer, characterCount);
  2332      GlobalUnlock(object);
  2333  
  2334      if (!OpenClipboard(_glfw.win32.helperWindowHandle))
  2335      {
  2336          _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
  2337                               "Win32: Failed to open clipboard");
  2338          GlobalFree(object);
  2339          return;
  2340      }
  2341  
  2342      EmptyClipboard();
  2343      SetClipboardData(CF_UNICODETEXT, object);
  2344      CloseClipboard();
  2345  }
  2346  
  2347  const char* _glfwGetClipboardStringWin32(void)
  2348  {
  2349      HANDLE object;
  2350      WCHAR* buffer;
  2351  
  2352      if (!OpenClipboard(_glfw.win32.helperWindowHandle))
  2353      {
  2354          _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
  2355                               "Win32: Failed to open clipboard");
  2356          return NULL;
  2357      }
  2358  
  2359      object = GetClipboardData(CF_UNICODETEXT);
  2360      if (!object)
  2361      {
  2362          _glfwInputErrorWin32(GLFW_FORMAT_UNAVAILABLE,
  2363                               "Win32: Failed to convert clipboard to string");
  2364          CloseClipboard();
  2365          return NULL;
  2366      }
  2367  
  2368      buffer = GlobalLock(object);
  2369      if (!buffer)
  2370      {
  2371          _glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
  2372                               "Win32: Failed to lock global handle");
  2373          CloseClipboard();
  2374          return NULL;
  2375      }
  2376  
  2377      _glfw_free(_glfw.win32.clipboardString);
  2378      _glfw.win32.clipboardString = _glfwCreateUTF8FromWideStringWin32(buffer);
  2379  
  2380      GlobalUnlock(object);
  2381      CloseClipboard();
  2382  
  2383      return _glfw.win32.clipboardString;
  2384  }
  2385  
  2386  EGLenum _glfwGetEGLPlatformWin32(EGLint** attribs)
  2387  {
  2388      if (_glfw.egl.ANGLE_platform_angle)
  2389      {
  2390          int type = 0;
  2391  
  2392          if (_glfw.egl.ANGLE_platform_angle_opengl)
  2393          {
  2394              if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_OPENGL)
  2395                  type = EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE;
  2396              else if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_OPENGLES)
  2397                  type = EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE;
  2398          }
  2399  
  2400          if (_glfw.egl.ANGLE_platform_angle_d3d)
  2401          {
  2402              if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_D3D9)
  2403                  type = EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE;
  2404              else if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_D3D11)
  2405                  type = EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE;
  2406          }
  2407  
  2408          if (_glfw.egl.ANGLE_platform_angle_vulkan)
  2409          {
  2410              if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_VULKAN)
  2411                  type = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE;
  2412          }
  2413  
  2414          if (type)
  2415          {
  2416              *attribs = _glfw_calloc(3, sizeof(EGLint));
  2417              (*attribs)[0] = EGL_PLATFORM_ANGLE_TYPE_ANGLE;
  2418              (*attribs)[1] = type;
  2419              (*attribs)[2] = EGL_NONE;
  2420              return EGL_PLATFORM_ANGLE_ANGLE;
  2421          }
  2422      }
  2423  
  2424      return 0;
  2425  }
  2426  
  2427  EGLNativeDisplayType _glfwGetEGLNativeDisplayWin32(void)
  2428  {
  2429      return GetDC(_glfw.win32.helperWindowHandle);
  2430  }
  2431  
  2432  EGLNativeWindowType _glfwGetEGLNativeWindowWin32(_GLFWwindow* window)
  2433  {
  2434      return window->win32.handle;
  2435  }
  2436  
  2437  void _glfwGetRequiredInstanceExtensionsWin32(char** extensions)
  2438  {
  2439      if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_win32_surface)
  2440          return;
  2441  
  2442      extensions[0] = "VK_KHR_surface";
  2443      extensions[1] = "VK_KHR_win32_surface";
  2444  }
  2445  
  2446  GLFWbool _glfwGetPhysicalDevicePresentationSupportWin32(VkInstance instance,
  2447                                                          VkPhysicalDevice device,
  2448                                                          uint32_t queuefamily)
  2449  {
  2450      PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR
  2451          vkGetPhysicalDeviceWin32PresentationSupportKHR =
  2452          (PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)
  2453          vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWin32PresentationSupportKHR");
  2454      if (!vkGetPhysicalDeviceWin32PresentationSupportKHR)
  2455      {
  2456          _glfwInputError(GLFW_API_UNAVAILABLE,
  2457                          "Win32: Vulkan instance missing VK_KHR_win32_surface extension");
  2458          return GLFW_FALSE;
  2459      }
  2460  
  2461      return vkGetPhysicalDeviceWin32PresentationSupportKHR(device, queuefamily);
  2462  }
  2463  
  2464  VkResult _glfwCreateWindowSurfaceWin32(VkInstance instance,
  2465                                         _GLFWwindow* window,
  2466                                         const VkAllocationCallbacks* allocator,
  2467                                         VkSurfaceKHR* surface)
  2468  {
  2469      VkResult err;
  2470      VkWin32SurfaceCreateInfoKHR sci;
  2471      PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR;
  2472  
  2473      vkCreateWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR)
  2474          vkGetInstanceProcAddr(instance, "vkCreateWin32SurfaceKHR");
  2475      if (!vkCreateWin32SurfaceKHR)
  2476      {
  2477          _glfwInputError(GLFW_API_UNAVAILABLE,
  2478                          "Win32: Vulkan instance missing VK_KHR_win32_surface extension");
  2479          return VK_ERROR_EXTENSION_NOT_PRESENT;
  2480      }
  2481  
  2482      memset(&sci, 0, sizeof(sci));
  2483      sci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
  2484      sci.hinstance = _glfw.win32.instance;
  2485      sci.hwnd = window->win32.handle;
  2486  
  2487      err = vkCreateWin32SurfaceKHR(instance, &sci, allocator, surface);
  2488      if (err)
  2489      {
  2490          _glfwInputError(GLFW_PLATFORM_ERROR,
  2491                          "Win32: Failed to create Vulkan surface: %s",
  2492                          _glfwGetVulkanResultString(err));
  2493      }
  2494  
  2495      return err;
  2496  }
  2497  
  2498  GLFWAPI HWND glfwGetWin32Window(GLFWwindow* handle)
  2499  {
  2500      _GLFWwindow* window = (_GLFWwindow*) handle;
  2501      _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
  2502  
  2503      if (_glfw.platform.platformID != GLFW_PLATFORM_WIN32)
  2504      {
  2505          _glfwInputError(GLFW_PLATFORM_UNAVAILABLE,
  2506                          "Win32: Platform not initialized");
  2507          return NULL;
  2508      }
  2509  
  2510      return window->win32.handle;
  2511  }
  2512