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

     1  //========================================================================
     2  // GLFW 3.4 X11 - 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  // It is fine to use C99 in this file because it will not be built with VS
    28  //========================================================================
    29  
    30  #include "internal.h"
    31  
    32  #include <X11/cursorfont.h>
    33  #include <X11/Xmd.h>
    34  
    35  #include <poll.h>
    36  
    37  #include <string.h>
    38  #include <stdio.h>
    39  #include <stdlib.h>
    40  #include <limits.h>
    41  #include <errno.h>
    42  #include <assert.h>
    43  
    44  // Action for EWMH client messages
    45  #define _NET_WM_STATE_REMOVE        0
    46  #define _NET_WM_STATE_ADD           1
    47  #define _NET_WM_STATE_TOGGLE        2
    48  
    49  // Additional mouse button names for XButtonEvent
    50  #define Button6            6
    51  #define Button7            7
    52  
    53  // Motif WM hints flags
    54  #define MWM_HINTS_DECORATIONS   2
    55  #define MWM_DECOR_ALL           1
    56  
    57  #define _GLFW_XDND_VERSION 5
    58  
    59  // Wait for event data to arrive on the X11 display socket
    60  // This avoids blocking other threads via the per-display Xlib lock that also
    61  // covers GLX functions
    62  //
    63  static GLFWbool waitForX11Event(double* timeout)
    64  {
    65      struct pollfd fd = { ConnectionNumber(_glfw.x11.display), POLLIN };
    66  
    67      while (!XPending(_glfw.x11.display))
    68      {
    69          if (!_glfwPollPOSIX(&fd, 1, timeout))
    70              return GLFW_FALSE;
    71      }
    72  
    73      return GLFW_TRUE;
    74  }
    75  
    76  // Wait for event data to arrive on any event file descriptor
    77  // This avoids blocking other threads via the per-display Xlib lock that also
    78  // covers GLX functions
    79  //
    80  static GLFWbool waitForAnyEvent(double* timeout)
    81  {
    82      nfds_t count = 2;
    83      struct pollfd fds[3] =
    84      {
    85          { ConnectionNumber(_glfw.x11.display), POLLIN },
    86          { _glfw.x11.emptyEventPipe[0], POLLIN }
    87      };
    88  
    89  #if defined(__linux__)
    90      if (_glfw.joysticksInitialized)
    91          fds[count++] = (struct pollfd) { _glfw.linjs.inotify, POLLIN };
    92  #endif
    93  
    94      while (!XPending(_glfw.x11.display))
    95      {
    96          if (!_glfwPollPOSIX(fds, count, timeout))
    97              return GLFW_FALSE;
    98  
    99          for (int i = 1; i < count; i++)
   100          {
   101              if (fds[i].revents & POLLIN)
   102                  return GLFW_TRUE;
   103          }
   104      }
   105  
   106      return GLFW_TRUE;
   107  }
   108  
   109  // Writes a byte to the empty event pipe
   110  //
   111  static void writeEmptyEvent(void)
   112  {
   113      for (;;)
   114      {
   115          const char byte = 0;
   116          const ssize_t result = write(_glfw.x11.emptyEventPipe[1], &byte, 1);
   117          if (result == 1 || (result == -1 && errno != EINTR))
   118              break;
   119      }
   120  }
   121  
   122  // Drains available data from the empty event pipe
   123  //
   124  static void drainEmptyEvents(void)
   125  {
   126      for (;;)
   127      {
   128          char dummy[64];
   129          const ssize_t result = read(_glfw.x11.emptyEventPipe[0], dummy, sizeof(dummy));
   130          if (result == -1 && errno != EINTR)
   131              break;
   132      }
   133  }
   134  
   135  // Waits until a VisibilityNotify event arrives for the specified window or the
   136  // timeout period elapses (ICCCM section 4.2.2)
   137  //
   138  static GLFWbool waitForVisibilityNotify(_GLFWwindow* window)
   139  {
   140      XEvent dummy;
   141      double timeout = 0.1;
   142  
   143      while (!XCheckTypedWindowEvent(_glfw.x11.display,
   144                                     window->x11.handle,
   145                                     VisibilityNotify,
   146                                     &dummy))
   147      {
   148          if (!waitForX11Event(&timeout))
   149              return GLFW_FALSE;
   150      }
   151  
   152      return GLFW_TRUE;
   153  }
   154  
   155  // Returns whether the window is iconified
   156  //
   157  static int getWindowState(_GLFWwindow* window)
   158  {
   159      int result = WithdrawnState;
   160      struct {
   161          CARD32 state;
   162          Window icon;
   163      } *state = NULL;
   164  
   165      if (_glfwGetWindowPropertyX11(window->x11.handle,
   166                                    _glfw.x11.WM_STATE,
   167                                    _glfw.x11.WM_STATE,
   168                                    (unsigned char**) &state) >= 2)
   169      {
   170          result = state->state;
   171      }
   172  
   173      if (state)
   174          XFree(state);
   175  
   176      return result;
   177  }
   178  
   179  // Returns whether the event is a selection event
   180  //
   181  static Bool isSelectionEvent(Display* display, XEvent* event, XPointer pointer)
   182  {
   183      if (event->xany.window != _glfw.x11.helperWindowHandle)
   184          return False;
   185  
   186      return event->type == SelectionRequest ||
   187             event->type == SelectionNotify ||
   188             event->type == SelectionClear;
   189  }
   190  
   191  // Returns whether it is a _NET_FRAME_EXTENTS event for the specified window
   192  //
   193  static Bool isFrameExtentsEvent(Display* display, XEvent* event, XPointer pointer)
   194  {
   195      _GLFWwindow* window = (_GLFWwindow*) pointer;
   196      return event->type == PropertyNotify &&
   197             event->xproperty.state == PropertyNewValue &&
   198             event->xproperty.window == window->x11.handle &&
   199             event->xproperty.atom == _glfw.x11.NET_FRAME_EXTENTS;
   200  }
   201  
   202  // Returns whether it is a property event for the specified selection transfer
   203  //
   204  static Bool isSelPropNewValueNotify(Display* display, XEvent* event, XPointer pointer)
   205  {
   206      XEvent* notification = (XEvent*) pointer;
   207      return event->type == PropertyNotify &&
   208             event->xproperty.state == PropertyNewValue &&
   209             event->xproperty.window == notification->xselection.requestor &&
   210             event->xproperty.atom == notification->xselection.property;
   211  }
   212  
   213  // Translates an X event modifier state mask
   214  //
   215  static int translateState(int state)
   216  {
   217      int mods = 0;
   218  
   219      if (state & ShiftMask)
   220          mods |= GLFW_MOD_SHIFT;
   221      if (state & ControlMask)
   222          mods |= GLFW_MOD_CONTROL;
   223      if (state & Mod1Mask)
   224          mods |= GLFW_MOD_ALT;
   225      if (state & Mod4Mask)
   226          mods |= GLFW_MOD_SUPER;
   227      if (state & LockMask)
   228          mods |= GLFW_MOD_CAPS_LOCK;
   229      if (state & Mod2Mask)
   230          mods |= GLFW_MOD_NUM_LOCK;
   231  
   232      return mods;
   233  }
   234  
   235  // Translates an X11 key code to a GLFW key token
   236  //
   237  static int translateKey(int scancode)
   238  {
   239      // Use the pre-filled LUT (see createKeyTables() in x11_init.c)
   240      if (scancode < 0 || scancode > 255)
   241          return GLFW_KEY_UNKNOWN;
   242  
   243      return _glfw.x11.keycodes[scancode];
   244  }
   245  
   246  // Sends an EWMH or ICCCM event to the window manager
   247  //
   248  static void sendEventToWM(_GLFWwindow* window, Atom type,
   249                            long a, long b, long c, long d, long e)
   250  {
   251      XEvent event = { ClientMessage };
   252      event.xclient.window = window->x11.handle;
   253      event.xclient.format = 32; // Data is 32-bit longs
   254      event.xclient.message_type = type;
   255      event.xclient.data.l[0] = a;
   256      event.xclient.data.l[1] = b;
   257      event.xclient.data.l[2] = c;
   258      event.xclient.data.l[3] = d;
   259      event.xclient.data.l[4] = e;
   260  
   261      XSendEvent(_glfw.x11.display, _glfw.x11.root,
   262                 False,
   263                 SubstructureNotifyMask | SubstructureRedirectMask,
   264                 &event);
   265  }
   266  
   267  // Updates the normal hints according to the window settings
   268  //
   269  static void updateNormalHints(_GLFWwindow* window, int width, int height)
   270  {
   271      XSizeHints* hints = XAllocSizeHints();
   272  
   273      long supplied;
   274      XGetWMNormalHints(_glfw.x11.display, window->x11.handle, hints, &supplied);
   275  
   276      hints->flags &= ~(PMinSize | PMaxSize | PAspect);
   277  
   278      if (!window->monitor)
   279      {
   280          if (window->resizable)
   281          {
   282              if (window->minwidth != GLFW_DONT_CARE &&
   283                  window->minheight != GLFW_DONT_CARE)
   284              {
   285                  hints->flags |= PMinSize;
   286                  hints->min_width = window->minwidth;
   287                  hints->min_height = window->minheight;
   288              }
   289  
   290              if (window->maxwidth != GLFW_DONT_CARE &&
   291                  window->maxheight != GLFW_DONT_CARE)
   292              {
   293                  hints->flags |= PMaxSize;
   294                  hints->max_width = window->maxwidth;
   295                  hints->max_height = window->maxheight;
   296              }
   297  
   298              if (window->numer != GLFW_DONT_CARE &&
   299                  window->denom != GLFW_DONT_CARE)
   300              {
   301                  hints->flags |= PAspect;
   302                  hints->min_aspect.x = hints->max_aspect.x = window->numer;
   303                  hints->min_aspect.y = hints->max_aspect.y = window->denom;
   304              }
   305          }
   306          else
   307          {
   308              hints->flags |= (PMinSize | PMaxSize);
   309              hints->min_width  = hints->max_width  = width;
   310              hints->min_height = hints->max_height = height;
   311          }
   312      }
   313  
   314      XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints);
   315      XFree(hints);
   316  }
   317  
   318  // Updates the full screen status of the window
   319  //
   320  static void updateWindowMode(_GLFWwindow* window)
   321  {
   322      if (window->monitor)
   323      {
   324          if (_glfw.x11.xinerama.available &&
   325              _glfw.x11.NET_WM_FULLSCREEN_MONITORS)
   326          {
   327              sendEventToWM(window,
   328                            _glfw.x11.NET_WM_FULLSCREEN_MONITORS,
   329                            window->monitor->x11.index,
   330                            window->monitor->x11.index,
   331                            window->monitor->x11.index,
   332                            window->monitor->x11.index,
   333                            0);
   334          }
   335  
   336          if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN)
   337          {
   338              sendEventToWM(window,
   339                            _glfw.x11.NET_WM_STATE,
   340                            _NET_WM_STATE_ADD,
   341                            _glfw.x11.NET_WM_STATE_FULLSCREEN,
   342                            0, 1, 0);
   343          }
   344          else
   345          {
   346              // This is the butcher's way of removing window decorations
   347              // Setting the override-redirect attribute on a window makes the
   348              // window manager ignore the window completely (ICCCM, section 4)
   349              // The good thing is that this makes undecorated full screen windows
   350              // easy to do; the bad thing is that we have to do everything
   351              // manually and some things (like iconify/restore) won't work at
   352              // all, as those are tasks usually performed by the window manager
   353  
   354              XSetWindowAttributes attributes;
   355              attributes.override_redirect = True;
   356              XChangeWindowAttributes(_glfw.x11.display,
   357                                      window->x11.handle,
   358                                      CWOverrideRedirect,
   359                                      &attributes);
   360  
   361              window->x11.overrideRedirect = GLFW_TRUE;
   362          }
   363  
   364          // Enable compositor bypass
   365          if (!window->x11.transparent)
   366          {
   367              const unsigned long value = 1;
   368  
   369              XChangeProperty(_glfw.x11.display,  window->x11.handle,
   370                              _glfw.x11.NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32,
   371                              PropModeReplace, (unsigned char*) &value, 1);
   372          }
   373      }
   374      else
   375      {
   376          if (_glfw.x11.xinerama.available &&
   377              _glfw.x11.NET_WM_FULLSCREEN_MONITORS)
   378          {
   379              XDeleteProperty(_glfw.x11.display, window->x11.handle,
   380                              _glfw.x11.NET_WM_FULLSCREEN_MONITORS);
   381          }
   382  
   383          if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN)
   384          {
   385              sendEventToWM(window,
   386                            _glfw.x11.NET_WM_STATE,
   387                            _NET_WM_STATE_REMOVE,
   388                            _glfw.x11.NET_WM_STATE_FULLSCREEN,
   389                            0, 1, 0);
   390          }
   391          else
   392          {
   393              XSetWindowAttributes attributes;
   394              attributes.override_redirect = False;
   395              XChangeWindowAttributes(_glfw.x11.display,
   396                                      window->x11.handle,
   397                                      CWOverrideRedirect,
   398                                      &attributes);
   399  
   400              window->x11.overrideRedirect = GLFW_FALSE;
   401          }
   402  
   403          // Disable compositor bypass
   404          if (!window->x11.transparent)
   405          {
   406              XDeleteProperty(_glfw.x11.display, window->x11.handle,
   407                              _glfw.x11.NET_WM_BYPASS_COMPOSITOR);
   408          }
   409      }
   410  }
   411  
   412  // Decode a Unicode code point from a UTF-8 stream
   413  // Based on cutef8 by Jeff Bezanson (Public Domain)
   414  //
   415  static uint32_t decodeUTF8(const char** s)
   416  {
   417      uint32_t codepoint = 0, count = 0;
   418      static const uint32_t offsets[] =
   419      {
   420          0x00000000u, 0x00003080u, 0x000e2080u,
   421          0x03c82080u, 0xfa082080u, 0x82082080u
   422      };
   423  
   424      do
   425      {
   426          codepoint = (codepoint << 6) + (unsigned char) **s;
   427          (*s)++;
   428          count++;
   429      } while ((**s & 0xc0) == 0x80);
   430  
   431      assert(count <= 6);
   432      return codepoint - offsets[count - 1];
   433  }
   434  
   435  // Convert the specified Latin-1 string to UTF-8
   436  //
   437  static char* convertLatin1toUTF8(const char* source)
   438  {
   439      size_t size = 1;
   440      const char* sp;
   441  
   442      for (sp = source;  *sp;  sp++)
   443          size += (*sp & 0x80) ? 2 : 1;
   444  
   445      char* target = _glfw_calloc(size, 1);
   446      char* tp = target;
   447  
   448      for (sp = source;  *sp;  sp++)
   449          tp += _glfwEncodeUTF8(tp, *sp);
   450  
   451      return target;
   452  }
   453  
   454  // Updates the cursor image according to its cursor mode
   455  //
   456  static void updateCursorImage(_GLFWwindow* window)
   457  {
   458      if (window->cursorMode == GLFW_CURSOR_NORMAL ||
   459          window->cursorMode == GLFW_CURSOR_CAPTURED)
   460      {
   461          if (window->cursor)
   462          {
   463              XDefineCursor(_glfw.x11.display, window->x11.handle,
   464                            window->cursor->x11.handle);
   465          }
   466          else
   467              XUndefineCursor(_glfw.x11.display, window->x11.handle);
   468      }
   469      else
   470      {
   471          XDefineCursor(_glfw.x11.display, window->x11.handle,
   472                        _glfw.x11.hiddenCursorHandle);
   473      }
   474  }
   475  
   476  // Grabs the cursor and confines it to the window
   477  //
   478  static void captureCursor(_GLFWwindow* window)
   479  {
   480      XGrabPointer(_glfw.x11.display, window->x11.handle, True,
   481                   ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
   482                   GrabModeAsync, GrabModeAsync,
   483                   window->x11.handle,
   484                   None,
   485                   CurrentTime);
   486  }
   487  
   488  // Ungrabs the cursor
   489  //
   490  static void releaseCursor(void)
   491  {
   492      XUngrabPointer(_glfw.x11.display, CurrentTime);
   493  }
   494  
   495  // Enable XI2 raw mouse motion events
   496  //
   497  static void enableRawMouseMotion(_GLFWwindow* window)
   498  {
   499      XIEventMask em;
   500      unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 };
   501  
   502      em.deviceid = XIAllMasterDevices;
   503      em.mask_len = sizeof(mask);
   504      em.mask = mask;
   505      XISetMask(mask, XI_RawMotion);
   506  
   507      XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);
   508  }
   509  
   510  // Disable XI2 raw mouse motion events
   511  //
   512  static void disableRawMouseMotion(_GLFWwindow* window)
   513  {
   514      XIEventMask em;
   515      unsigned char mask[] = { 0 };
   516  
   517      em.deviceid = XIAllMasterDevices;
   518      em.mask_len = sizeof(mask);
   519      em.mask = mask;
   520  
   521      XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);
   522  }
   523  
   524  // Apply disabled cursor mode to a focused window
   525  //
   526  static void disableCursor(_GLFWwindow* window)
   527  {
   528      if (window->rawMouseMotion)
   529          enableRawMouseMotion(window);
   530  
   531      _glfw.x11.disabledCursorWindow = window;
   532      _glfwGetCursorPosX11(window,
   533                           &_glfw.x11.restoreCursorPosX,
   534                           &_glfw.x11.restoreCursorPosY);
   535      updateCursorImage(window);
   536      _glfwCenterCursorInContentArea(window);
   537      captureCursor(window);
   538  }
   539  
   540  // Exit disabled cursor mode for the specified window
   541  //
   542  static void enableCursor(_GLFWwindow* window)
   543  {
   544      if (window->rawMouseMotion)
   545          disableRawMouseMotion(window);
   546  
   547      _glfw.x11.disabledCursorWindow = NULL;
   548      releaseCursor();
   549      _glfwSetCursorPosX11(window,
   550                           _glfw.x11.restoreCursorPosX,
   551                           _glfw.x11.restoreCursorPosY);
   552      updateCursorImage(window);
   553  }
   554  
   555  // Clear its handle when the input context has been destroyed
   556  //
   557  static void inputContextDestroyCallback(XIC ic, XPointer clientData, XPointer callData)
   558  {
   559      _GLFWwindow* window = (_GLFWwindow*) clientData;
   560      window->x11.ic = NULL;
   561  }
   562  
   563  // Create the X11 window (and its colormap)
   564  //
   565  static GLFWbool createNativeWindow(_GLFWwindow* window,
   566                                     const _GLFWwndconfig* wndconfig,
   567                                     Visual* visual, int depth)
   568  {
   569      int width = wndconfig->width;
   570      int height = wndconfig->height;
   571  
   572      if (wndconfig->scaleToMonitor)
   573      {
   574          width *= _glfw.x11.contentScaleX;
   575          height *= _glfw.x11.contentScaleY;
   576      }
   577  
   578      int xpos = 0, ypos = 0;
   579  
   580      if (wndconfig->xpos != GLFW_ANY_POSITION && wndconfig->ypos != GLFW_ANY_POSITION)
   581      {
   582          xpos = wndconfig->xpos;
   583          ypos = wndconfig->ypos;
   584      }
   585  
   586      // Create a colormap based on the visual used by the current context
   587      window->x11.colormap = XCreateColormap(_glfw.x11.display,
   588                                             _glfw.x11.root,
   589                                             visual,
   590                                             AllocNone);
   591  
   592      window->x11.transparent = _glfwIsVisualTransparentX11(visual);
   593  
   594      XSetWindowAttributes wa = { 0 };
   595      wa.colormap = window->x11.colormap;
   596      wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask |
   597                      PointerMotionMask | ButtonPressMask | ButtonReleaseMask |
   598                      ExposureMask | FocusChangeMask | VisibilityChangeMask |
   599                      EnterWindowMask | LeaveWindowMask | PropertyChangeMask;
   600  
   601      _glfwGrabErrorHandlerX11();
   602  
   603      window->x11.parent = _glfw.x11.root;
   604      window->x11.handle = XCreateWindow(_glfw.x11.display,
   605                                         _glfw.x11.root,
   606                                         xpos, ypos,
   607                                         width, height,
   608                                         0,      // Border width
   609                                         depth,  // Color depth
   610                                         InputOutput,
   611                                         visual,
   612                                         CWBorderPixel | CWColormap | CWEventMask,
   613                                         &wa);
   614  
   615      _glfwReleaseErrorHandlerX11();
   616  
   617      if (!window->x11.handle)
   618      {
   619          _glfwInputErrorX11(GLFW_PLATFORM_ERROR,
   620                             "X11: Failed to create window");
   621          return GLFW_FALSE;
   622      }
   623  
   624      XSaveContext(_glfw.x11.display,
   625                   window->x11.handle,
   626                   _glfw.x11.context,
   627                   (XPointer) window);
   628  
   629      if (!wndconfig->decorated)
   630          _glfwSetWindowDecoratedX11(window, GLFW_FALSE);
   631  
   632      if (_glfw.x11.NET_WM_STATE && !window->monitor)
   633      {
   634          Atom states[3];
   635          int count = 0;
   636  
   637          if (wndconfig->floating)
   638          {
   639              if (_glfw.x11.NET_WM_STATE_ABOVE)
   640                  states[count++] = _glfw.x11.NET_WM_STATE_ABOVE;
   641          }
   642  
   643          if (wndconfig->maximized)
   644          {
   645              if (_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
   646                  _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
   647              {
   648                  states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT;
   649                  states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ;
   650                  window->x11.maximized = GLFW_TRUE;
   651              }
   652          }
   653  
   654          if (count)
   655          {
   656              XChangeProperty(_glfw.x11.display, window->x11.handle,
   657                              _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
   658                              PropModeReplace, (unsigned char*) states, count);
   659          }
   660      }
   661  
   662      // Declare the WM protocols supported by GLFW
   663      {
   664          Atom protocols[] =
   665          {
   666              _glfw.x11.WM_DELETE_WINDOW,
   667              _glfw.x11.NET_WM_PING
   668          };
   669  
   670          XSetWMProtocols(_glfw.x11.display, window->x11.handle,
   671                          protocols, sizeof(protocols) / sizeof(Atom));
   672      }
   673  
   674      // Declare our PID
   675      {
   676          const long pid = getpid();
   677  
   678          XChangeProperty(_glfw.x11.display,  window->x11.handle,
   679                          _glfw.x11.NET_WM_PID, XA_CARDINAL, 32,
   680                          PropModeReplace,
   681                          (unsigned char*) &pid, 1);
   682      }
   683  
   684      if (_glfw.x11.NET_WM_WINDOW_TYPE && _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL)
   685      {
   686          Atom type = _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL;
   687          XChangeProperty(_glfw.x11.display,  window->x11.handle,
   688                          _glfw.x11.NET_WM_WINDOW_TYPE, XA_ATOM, 32,
   689                          PropModeReplace, (unsigned char*) &type, 1);
   690      }
   691  
   692      // Set ICCCM WM_HINTS property
   693      {
   694          XWMHints* hints = XAllocWMHints();
   695          if (!hints)
   696          {
   697              _glfwInputError(GLFW_OUT_OF_MEMORY,
   698                              "X11: Failed to allocate WM hints");
   699              return GLFW_FALSE;
   700          }
   701  
   702          hints->flags = StateHint;
   703          hints->initial_state = NormalState;
   704  
   705          XSetWMHints(_glfw.x11.display, window->x11.handle, hints);
   706          XFree(hints);
   707      }
   708  
   709      // Set ICCCM WM_NORMAL_HINTS property
   710      {
   711          XSizeHints* hints = XAllocSizeHints();
   712          if (!hints)
   713          {
   714              _glfwInputError(GLFW_OUT_OF_MEMORY, "X11: Failed to allocate size hints");
   715              return GLFW_FALSE;
   716          }
   717  
   718          if (!wndconfig->resizable)
   719          {
   720              hints->flags |= (PMinSize | PMaxSize);
   721              hints->min_width  = hints->max_width  = width;
   722              hints->min_height = hints->max_height = height;
   723          }
   724  
   725          // HACK: Explicitly setting PPosition to any value causes some WMs, notably
   726          //       Compiz and Metacity, to honor the position of unmapped windows
   727          if (wndconfig->xpos != GLFW_ANY_POSITION && wndconfig->ypos != GLFW_ANY_POSITION)
   728          {
   729              hints->flags |= PPosition;
   730              hints->x = 0;
   731              hints->y = 0;
   732          }
   733  
   734          hints->flags |= PWinGravity;
   735          hints->win_gravity = StaticGravity;
   736  
   737          XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints);
   738          XFree(hints);
   739      }
   740  
   741      // Set ICCCM WM_CLASS property
   742      {
   743          XClassHint* hint = XAllocClassHint();
   744  
   745          if (strlen(wndconfig->x11.instanceName) &&
   746              strlen(wndconfig->x11.className))
   747          {
   748              hint->res_name = (char*) wndconfig->x11.instanceName;
   749              hint->res_class = (char*) wndconfig->x11.className;
   750          }
   751          else
   752          {
   753              const char* resourceName = getenv("RESOURCE_NAME");
   754              if (resourceName && strlen(resourceName))
   755                  hint->res_name = (char*) resourceName;
   756              else if (strlen(wndconfig->title))
   757                  hint->res_name = (char*) wndconfig->title;
   758              else
   759                  hint->res_name = (char*) "glfw-application";
   760  
   761              if (strlen(wndconfig->title))
   762                  hint->res_class = (char*) wndconfig->title;
   763              else
   764                  hint->res_class = (char*) "GLFW-Application";
   765          }
   766  
   767          XSetClassHint(_glfw.x11.display, window->x11.handle, hint);
   768          XFree(hint);
   769      }
   770  
   771      // Announce support for Xdnd (drag and drop)
   772      {
   773          const Atom version = _GLFW_XDND_VERSION;
   774          XChangeProperty(_glfw.x11.display, window->x11.handle,
   775                          _glfw.x11.XdndAware, XA_ATOM, 32,
   776                          PropModeReplace, (unsigned char*) &version, 1);
   777      }
   778  
   779      if (_glfw.x11.im)
   780          _glfwCreateInputContextX11(window);
   781  
   782      _glfwSetWindowTitleX11(window, wndconfig->title);
   783      _glfwGetWindowPosX11(window, &window->x11.xpos, &window->x11.ypos);
   784      _glfwGetWindowSizeX11(window, &window->x11.width, &window->x11.height);
   785  
   786      return GLFW_TRUE;
   787  }
   788  
   789  // Set the specified property to the selection converted to the requested target
   790  //
   791  static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
   792  {
   793      char* selectionString = NULL;
   794      const Atom formats[] = { _glfw.x11.UTF8_STRING, XA_STRING };
   795      const int formatCount = sizeof(formats) / sizeof(formats[0]);
   796  
   797      if (request->selection == _glfw.x11.PRIMARY)
   798          selectionString = _glfw.x11.primarySelectionString;
   799      else
   800          selectionString = _glfw.x11.clipboardString;
   801  
   802      if (request->property == None)
   803      {
   804          // The requester is a legacy client (ICCCM section 2.2)
   805          // We don't support legacy clients, so fail here
   806          return None;
   807      }
   808  
   809      if (request->target == _glfw.x11.TARGETS)
   810      {
   811          // The list of supported targets was requested
   812  
   813          const Atom targets[] = { _glfw.x11.TARGETS,
   814                                   _glfw.x11.MULTIPLE,
   815                                   _glfw.x11.UTF8_STRING,
   816                                   XA_STRING };
   817  
   818          XChangeProperty(_glfw.x11.display,
   819                          request->requestor,
   820                          request->property,
   821                          XA_ATOM,
   822                          32,
   823                          PropModeReplace,
   824                          (unsigned char*) targets,
   825                          sizeof(targets) / sizeof(targets[0]));
   826  
   827          return request->property;
   828      }
   829  
   830      if (request->target == _glfw.x11.MULTIPLE)
   831      {
   832          // Multiple conversions were requested
   833  
   834          Atom* targets;
   835          const unsigned long count =
   836              _glfwGetWindowPropertyX11(request->requestor,
   837                                        request->property,
   838                                        _glfw.x11.ATOM_PAIR,
   839                                        (unsigned char**) &targets);
   840  
   841          for (unsigned long i = 0;  i < count;  i += 2)
   842          {
   843              int j;
   844  
   845              for (j = 0;  j < formatCount;  j++)
   846              {
   847                  if (targets[i] == formats[j])
   848                      break;
   849              }
   850  
   851              if (j < formatCount)
   852              {
   853                  XChangeProperty(_glfw.x11.display,
   854                                  request->requestor,
   855                                  targets[i + 1],
   856                                  targets[i],
   857                                  8,
   858                                  PropModeReplace,
   859                                  (unsigned char *) selectionString,
   860                                  strlen(selectionString));
   861              }
   862              else
   863                  targets[i + 1] = None;
   864          }
   865  
   866          XChangeProperty(_glfw.x11.display,
   867                          request->requestor,
   868                          request->property,
   869                          _glfw.x11.ATOM_PAIR,
   870                          32,
   871                          PropModeReplace,
   872                          (unsigned char*) targets,
   873                          count);
   874  
   875          XFree(targets);
   876  
   877          return request->property;
   878      }
   879  
   880      if (request->target == _glfw.x11.SAVE_TARGETS)
   881      {
   882          // The request is a check whether we support SAVE_TARGETS
   883          // It should be handled as a no-op side effect target
   884  
   885          XChangeProperty(_glfw.x11.display,
   886                          request->requestor,
   887                          request->property,
   888                          _glfw.x11.NULL_,
   889                          32,
   890                          PropModeReplace,
   891                          NULL,
   892                          0);
   893  
   894          return request->property;
   895      }
   896  
   897      // Conversion to a data target was requested
   898  
   899      for (int i = 0;  i < formatCount;  i++)
   900      {
   901          if (request->target == formats[i])
   902          {
   903              // The requested target is one we support
   904  
   905              XChangeProperty(_glfw.x11.display,
   906                              request->requestor,
   907                              request->property,
   908                              request->target,
   909                              8,
   910                              PropModeReplace,
   911                              (unsigned char *) selectionString,
   912                              strlen(selectionString));
   913  
   914              return request->property;
   915          }
   916      }
   917  
   918      // The requested target is not supported
   919  
   920      return None;
   921  }
   922  
   923  static void handleSelectionRequest(XEvent* event)
   924  {
   925      const XSelectionRequestEvent* request = &event->xselectionrequest;
   926  
   927      XEvent reply = { SelectionNotify };
   928      reply.xselection.property = writeTargetToProperty(request);
   929      reply.xselection.display = request->display;
   930      reply.xselection.requestor = request->requestor;
   931      reply.xselection.selection = request->selection;
   932      reply.xselection.target = request->target;
   933      reply.xselection.time = request->time;
   934  
   935      XSendEvent(_glfw.x11.display, request->requestor, False, 0, &reply);
   936  }
   937  
   938  static const char* getSelectionString(Atom selection)
   939  {
   940      char** selectionString = NULL;
   941      const Atom targets[] = { _glfw.x11.UTF8_STRING, XA_STRING };
   942      const size_t targetCount = sizeof(targets) / sizeof(targets[0]);
   943  
   944      if (selection == _glfw.x11.PRIMARY)
   945          selectionString = &_glfw.x11.primarySelectionString;
   946      else
   947          selectionString = &_glfw.x11.clipboardString;
   948  
   949      if (XGetSelectionOwner(_glfw.x11.display, selection) ==
   950          _glfw.x11.helperWindowHandle)
   951      {
   952          // Instead of doing a large number of X round-trips just to put this
   953          // string into a window property and then read it back, just return it
   954          return *selectionString;
   955      }
   956  
   957      _glfw_free(*selectionString);
   958      *selectionString = NULL;
   959  
   960      for (size_t i = 0;  i < targetCount;  i++)
   961      {
   962          char* data;
   963          Atom actualType;
   964          int actualFormat;
   965          unsigned long itemCount, bytesAfter;
   966          XEvent notification, dummy;
   967  
   968          XConvertSelection(_glfw.x11.display,
   969                            selection,
   970                            targets[i],
   971                            _glfw.x11.GLFW_SELECTION,
   972                            _glfw.x11.helperWindowHandle,
   973                            CurrentTime);
   974  
   975          while (!XCheckTypedWindowEvent(_glfw.x11.display,
   976                                         _glfw.x11.helperWindowHandle,
   977                                         SelectionNotify,
   978                                         &notification))
   979          {
   980              waitForX11Event(NULL);
   981          }
   982  
   983          if (notification.xselection.property == None)
   984              continue;
   985  
   986          XCheckIfEvent(_glfw.x11.display,
   987                        &dummy,
   988                        isSelPropNewValueNotify,
   989                        (XPointer) &notification);
   990  
   991          XGetWindowProperty(_glfw.x11.display,
   992                             notification.xselection.requestor,
   993                             notification.xselection.property,
   994                             0,
   995                             LONG_MAX,
   996                             True,
   997                             AnyPropertyType,
   998                             &actualType,
   999                             &actualFormat,
  1000                             &itemCount,
  1001                             &bytesAfter,
  1002                             (unsigned char**) &data);
  1003  
  1004          if (actualType == _glfw.x11.INCR)
  1005          {
  1006              size_t size = 1;
  1007              char* string = NULL;
  1008  
  1009              for (;;)
  1010              {
  1011                  while (!XCheckIfEvent(_glfw.x11.display,
  1012                                        &dummy,
  1013                                        isSelPropNewValueNotify,
  1014                                        (XPointer) &notification))
  1015                  {
  1016                      waitForX11Event(NULL);
  1017                  }
  1018  
  1019                  XFree(data);
  1020                  XGetWindowProperty(_glfw.x11.display,
  1021                                     notification.xselection.requestor,
  1022                                     notification.xselection.property,
  1023                                     0,
  1024                                     LONG_MAX,
  1025                                     True,
  1026                                     AnyPropertyType,
  1027                                     &actualType,
  1028                                     &actualFormat,
  1029                                     &itemCount,
  1030                                     &bytesAfter,
  1031                                     (unsigned char**) &data);
  1032  
  1033                  if (itemCount)
  1034                  {
  1035                      size += itemCount;
  1036                      string = _glfw_realloc(string, size);
  1037                      string[size - itemCount - 1] = '\0';
  1038                      strcat(string, data);
  1039                  }
  1040  
  1041                  if (!itemCount)
  1042                  {
  1043                      if (string)
  1044                      {
  1045                          if (targets[i] == XA_STRING)
  1046                          {
  1047                              *selectionString = convertLatin1toUTF8(string);
  1048                              _glfw_free(string);
  1049                          }
  1050                          else
  1051                              *selectionString = string;
  1052                      }
  1053  
  1054                      break;
  1055                  }
  1056              }
  1057          }
  1058          else if (actualType == targets[i])
  1059          {
  1060              if (targets[i] == XA_STRING)
  1061                  *selectionString = convertLatin1toUTF8(data);
  1062              else
  1063                  *selectionString = _glfw_strdup(data);
  1064          }
  1065  
  1066          XFree(data);
  1067  
  1068          if (*selectionString)
  1069              break;
  1070      }
  1071  
  1072      if (!*selectionString)
  1073      {
  1074          _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
  1075                          "X11: Failed to convert selection to string");
  1076      }
  1077  
  1078      return *selectionString;
  1079  }
  1080  
  1081  // Make the specified window and its video mode active on its monitor
  1082  //
  1083  static void acquireMonitor(_GLFWwindow* window)
  1084  {
  1085      if (_glfw.x11.saver.count == 0)
  1086      {
  1087          // Remember old screen saver settings
  1088          XGetScreenSaver(_glfw.x11.display,
  1089                          &_glfw.x11.saver.timeout,
  1090                          &_glfw.x11.saver.interval,
  1091                          &_glfw.x11.saver.blanking,
  1092                          &_glfw.x11.saver.exposure);
  1093  
  1094          // Disable screen saver
  1095          XSetScreenSaver(_glfw.x11.display, 0, 0, DontPreferBlanking,
  1096                          DefaultExposures);
  1097      }
  1098  
  1099      if (!window->monitor->window)
  1100          _glfw.x11.saver.count++;
  1101  
  1102      _glfwSetVideoModeX11(window->monitor, &window->videoMode);
  1103  
  1104      if (window->x11.overrideRedirect)
  1105      {
  1106          int xpos, ypos;
  1107          GLFWvidmode mode;
  1108  
  1109          // Manually position the window over its monitor
  1110          _glfwGetMonitorPosX11(window->monitor, &xpos, &ypos);
  1111          _glfwGetVideoModeX11(window->monitor, &mode);
  1112  
  1113          XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
  1114                            xpos, ypos, mode.width, mode.height);
  1115      }
  1116  
  1117      _glfwInputMonitorWindow(window->monitor, window);
  1118  }
  1119  
  1120  // Remove the window and restore the original video mode
  1121  //
  1122  static void releaseMonitor(_GLFWwindow* window)
  1123  {
  1124      if (window->monitor->window != window)
  1125          return;
  1126  
  1127      _glfwInputMonitorWindow(window->monitor, NULL);
  1128      _glfwRestoreVideoModeX11(window->monitor);
  1129  
  1130      _glfw.x11.saver.count--;
  1131  
  1132      if (_glfw.x11.saver.count == 0)
  1133      {
  1134          // Restore old screen saver settings
  1135          XSetScreenSaver(_glfw.x11.display,
  1136                          _glfw.x11.saver.timeout,
  1137                          _glfw.x11.saver.interval,
  1138                          _glfw.x11.saver.blanking,
  1139                          _glfw.x11.saver.exposure);
  1140      }
  1141  }
  1142  
  1143  // Process the specified X event
  1144  //
  1145  static void processEvent(XEvent *event)
  1146  {
  1147      int keycode = 0;
  1148      Bool filtered = False;
  1149  
  1150      // HACK: Save scancode as some IMs clear the field in XFilterEvent
  1151      if (event->type == KeyPress || event->type == KeyRelease)
  1152          keycode = event->xkey.keycode;
  1153  
  1154      filtered = XFilterEvent(event, None);
  1155  
  1156      if (_glfw.x11.randr.available)
  1157      {
  1158          if (event->type == _glfw.x11.randr.eventBase + RRNotify)
  1159          {
  1160              XRRUpdateConfiguration(event);
  1161              _glfwPollMonitorsX11();
  1162              return;
  1163          }
  1164      }
  1165  
  1166      if (_glfw.x11.xkb.available)
  1167      {
  1168          if (event->type == _glfw.x11.xkb.eventBase + XkbEventCode)
  1169          {
  1170              if (((XkbEvent*) event)->any.xkb_type == XkbStateNotify &&
  1171                  (((XkbEvent*) event)->state.changed & XkbGroupStateMask))
  1172              {
  1173                  _glfw.x11.xkb.group = ((XkbEvent*) event)->state.group;
  1174              }
  1175  
  1176              return;
  1177          }
  1178      }
  1179  
  1180      if (event->type == GenericEvent)
  1181      {
  1182          if (_glfw.x11.xi.available)
  1183          {
  1184              _GLFWwindow* window = _glfw.x11.disabledCursorWindow;
  1185  
  1186              if (window &&
  1187                  window->rawMouseMotion &&
  1188                  event->xcookie.extension == _glfw.x11.xi.majorOpcode &&
  1189                  XGetEventData(_glfw.x11.display, &event->xcookie) &&
  1190                  event->xcookie.evtype == XI_RawMotion)
  1191              {
  1192                  XIRawEvent* re = event->xcookie.data;
  1193                  if (re->valuators.mask_len)
  1194                  {
  1195                      const double* values = re->raw_values;
  1196                      double xpos = window->virtualCursorPosX;
  1197                      double ypos = window->virtualCursorPosY;
  1198  
  1199                      if (XIMaskIsSet(re->valuators.mask, 0))
  1200                      {
  1201                          xpos += *values;
  1202                          values++;
  1203                      }
  1204  
  1205                      if (XIMaskIsSet(re->valuators.mask, 1))
  1206                          ypos += *values;
  1207  
  1208                      _glfwInputCursorPos(window, xpos, ypos);
  1209                  }
  1210              }
  1211  
  1212              XFreeEventData(_glfw.x11.display, &event->xcookie);
  1213          }
  1214  
  1215          return;
  1216      }
  1217  
  1218      if (event->type == SelectionRequest)
  1219      {
  1220          handleSelectionRequest(event);
  1221          return;
  1222      }
  1223  
  1224      _GLFWwindow* window = NULL;
  1225      if (XFindContext(_glfw.x11.display,
  1226                       event->xany.window,
  1227                       _glfw.x11.context,
  1228                       (XPointer*) &window) != 0)
  1229      {
  1230          // This is an event for a window that has already been destroyed
  1231          return;
  1232      }
  1233  
  1234      switch (event->type)
  1235      {
  1236          case ReparentNotify:
  1237          {
  1238              window->x11.parent = event->xreparent.parent;
  1239              return;
  1240          }
  1241  
  1242          case KeyPress:
  1243          {
  1244              const int key = translateKey(keycode);
  1245              const int mods = translateState(event->xkey.state);
  1246              const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT));
  1247  
  1248              if (window->x11.ic)
  1249              {
  1250                  // HACK: Do not report the key press events duplicated by XIM
  1251                  //       Duplicate key releases are filtered out implicitly by
  1252                  //       the GLFW key repeat logic in _glfwInputKey
  1253                  //       A timestamp per key is used to handle simultaneous keys
  1254                  // NOTE: Always allow the first event for each key through
  1255                  //       (the server never sends a timestamp of zero)
  1256                  // NOTE: Timestamp difference is compared to handle wrap-around
  1257                  Time diff = event->xkey.time - window->x11.keyPressTimes[keycode];
  1258                  if (diff == event->xkey.time || (diff > 0 && diff < ((Time)1 << 31)))
  1259                  {
  1260                      if (keycode)
  1261                          _glfwInputKey(window, key, keycode, GLFW_PRESS, mods);
  1262  
  1263                      window->x11.keyPressTimes[keycode] = event->xkey.time;
  1264                  }
  1265  
  1266                  if (!filtered)
  1267                  {
  1268                      int count;
  1269                      Status status;
  1270                      char buffer[100];
  1271                      char* chars = buffer;
  1272  
  1273                      count = Xutf8LookupString(window->x11.ic,
  1274                                                &event->xkey,
  1275                                                buffer, sizeof(buffer) - 1,
  1276                                                NULL, &status);
  1277  
  1278                      if (status == XBufferOverflow)
  1279                      {
  1280                          chars = _glfw_calloc(count + 1, 1);
  1281                          count = Xutf8LookupString(window->x11.ic,
  1282                                                    &event->xkey,
  1283                                                    chars, count,
  1284                                                    NULL, &status);
  1285                      }
  1286  
  1287                      if (status == XLookupChars || status == XLookupBoth)
  1288                      {
  1289                          const char* c = chars;
  1290                          chars[count] = '\0';
  1291                          while (c - chars < count)
  1292                              _glfwInputChar(window, decodeUTF8(&c), mods, plain);
  1293                      }
  1294  
  1295                      if (chars != buffer)
  1296                          _glfw_free(chars);
  1297                  }
  1298              }
  1299              else
  1300              {
  1301                  KeySym keysym;
  1302                  XLookupString(&event->xkey, NULL, 0, &keysym, NULL);
  1303  
  1304                  _glfwInputKey(window, key, keycode, GLFW_PRESS, mods);
  1305  
  1306                  const uint32_t codepoint = _glfwKeySym2Unicode(keysym);
  1307                  if (codepoint != GLFW_INVALID_CODEPOINT)
  1308                      _glfwInputChar(window, codepoint, mods, plain);
  1309              }
  1310  
  1311              return;
  1312          }
  1313  
  1314          case KeyRelease:
  1315          {
  1316              const int key = translateKey(keycode);
  1317              const int mods = translateState(event->xkey.state);
  1318  
  1319              if (!_glfw.x11.xkb.detectable)
  1320              {
  1321                  // HACK: Key repeat events will arrive as KeyRelease/KeyPress
  1322                  //       pairs with similar or identical time stamps
  1323                  //       The key repeat logic in _glfwInputKey expects only key
  1324                  //       presses to repeat, so detect and discard release events
  1325                  if (XEventsQueued(_glfw.x11.display, QueuedAfterReading))
  1326                  {
  1327                      XEvent next;
  1328                      XPeekEvent(_glfw.x11.display, &next);
  1329  
  1330                      if (next.type == KeyPress &&
  1331                          next.xkey.window == event->xkey.window &&
  1332                          next.xkey.keycode == keycode)
  1333                      {
  1334                          // HACK: The time of repeat events sometimes doesn't
  1335                          //       match that of the press event, so add an
  1336                          //       epsilon
  1337                          //       Toshiyuki Takahashi can press a button
  1338                          //       16 times per second so it's fairly safe to
  1339                          //       assume that no human is pressing the key 50
  1340                          //       times per second (value is ms)
  1341                          if ((next.xkey.time - event->xkey.time) < 20)
  1342                          {
  1343                              // This is very likely a server-generated key repeat
  1344                              // event, so ignore it
  1345                              return;
  1346                          }
  1347                      }
  1348                  }
  1349              }
  1350  
  1351              _glfwInputKey(window, key, keycode, GLFW_RELEASE, mods);
  1352              return;
  1353          }
  1354  
  1355          case ButtonPress:
  1356          {
  1357              const int mods = translateState(event->xbutton.state);
  1358  
  1359              if (event->xbutton.button == Button1)
  1360                  _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, mods);
  1361              else if (event->xbutton.button == Button2)
  1362                  _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, mods);
  1363              else if (event->xbutton.button == Button3)
  1364                  _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS, mods);
  1365  
  1366              // Modern X provides scroll events as mouse button presses
  1367              else if (event->xbutton.button == Button4)
  1368                  _glfwInputScroll(window, 0.0, 1.0);
  1369              else if (event->xbutton.button == Button5)
  1370                  _glfwInputScroll(window, 0.0, -1.0);
  1371              else if (event->xbutton.button == Button6)
  1372                  _glfwInputScroll(window, 1.0, 0.0);
  1373              else if (event->xbutton.button == Button7)
  1374                  _glfwInputScroll(window, -1.0, 0.0);
  1375  
  1376              else
  1377              {
  1378                  // Additional buttons after 7 are treated as regular buttons
  1379                  // We subtract 4 to fill the gap left by scroll input above
  1380                  _glfwInputMouseClick(window,
  1381                                       event->xbutton.button - Button1 - 4,
  1382                                       GLFW_PRESS,
  1383                                       mods);
  1384              }
  1385  
  1386              return;
  1387          }
  1388  
  1389          case ButtonRelease:
  1390          {
  1391              const int mods = translateState(event->xbutton.state);
  1392  
  1393              if (event->xbutton.button == Button1)
  1394              {
  1395                  _glfwInputMouseClick(window,
  1396                                       GLFW_MOUSE_BUTTON_LEFT,
  1397                                       GLFW_RELEASE,
  1398                                       mods);
  1399              }
  1400              else if (event->xbutton.button == Button2)
  1401              {
  1402                  _glfwInputMouseClick(window,
  1403                                       GLFW_MOUSE_BUTTON_MIDDLE,
  1404                                       GLFW_RELEASE,
  1405                                       mods);
  1406              }
  1407              else if (event->xbutton.button == Button3)
  1408              {
  1409                  _glfwInputMouseClick(window,
  1410                                       GLFW_MOUSE_BUTTON_RIGHT,
  1411                                       GLFW_RELEASE,
  1412                                       mods);
  1413              }
  1414              else if (event->xbutton.button > Button7)
  1415              {
  1416                  // Additional buttons after 7 are treated as regular buttons
  1417                  // We subtract 4 to fill the gap left by scroll input above
  1418                  _glfwInputMouseClick(window,
  1419                                       event->xbutton.button - Button1 - 4,
  1420                                       GLFW_RELEASE,
  1421                                       mods);
  1422              }
  1423  
  1424              return;
  1425          }
  1426  
  1427          case EnterNotify:
  1428          {
  1429              // XEnterWindowEvent is XCrossingEvent
  1430              const int x = event->xcrossing.x;
  1431              const int y = event->xcrossing.y;
  1432  
  1433              // HACK: This is a workaround for WMs (KWM, Fluxbox) that otherwise
  1434              //       ignore the defined cursor for hidden cursor mode
  1435              if (window->cursorMode == GLFW_CURSOR_HIDDEN)
  1436                  updateCursorImage(window);
  1437  
  1438              _glfwInputCursorEnter(window, GLFW_TRUE);
  1439              _glfwInputCursorPos(window, x, y);
  1440  
  1441              window->x11.lastCursorPosX = x;
  1442              window->x11.lastCursorPosY = y;
  1443              return;
  1444          }
  1445  
  1446          case LeaveNotify:
  1447          {
  1448              _glfwInputCursorEnter(window, GLFW_FALSE);
  1449              return;
  1450          }
  1451  
  1452          case MotionNotify:
  1453          {
  1454              const int x = event->xmotion.x;
  1455              const int y = event->xmotion.y;
  1456  
  1457              if (x != window->x11.warpCursorPosX ||
  1458                  y != window->x11.warpCursorPosY)
  1459              {
  1460                  // The cursor was moved by something other than GLFW
  1461  
  1462                  if (window->cursorMode == GLFW_CURSOR_DISABLED)
  1463                  {
  1464                      if (_glfw.x11.disabledCursorWindow != window)
  1465                          return;
  1466                      if (window->rawMouseMotion)
  1467                          return;
  1468  
  1469                      const int dx = x - window->x11.lastCursorPosX;
  1470                      const int dy = y - window->x11.lastCursorPosY;
  1471  
  1472                      _glfwInputCursorPos(window,
  1473                                          window->virtualCursorPosX + dx,
  1474                                          window->virtualCursorPosY + dy);
  1475                  }
  1476                  else
  1477                      _glfwInputCursorPos(window, x, y);
  1478              }
  1479  
  1480              window->x11.lastCursorPosX = x;
  1481              window->x11.lastCursorPosY = y;
  1482              return;
  1483          }
  1484  
  1485          case ConfigureNotify:
  1486          {
  1487              if (event->xconfigure.width != window->x11.width ||
  1488                  event->xconfigure.height != window->x11.height)
  1489              {
  1490                  _glfwInputFramebufferSize(window,
  1491                                            event->xconfigure.width,
  1492                                            event->xconfigure.height);
  1493  
  1494                  _glfwInputWindowSize(window,
  1495                                       event->xconfigure.width,
  1496                                       event->xconfigure.height);
  1497  
  1498                  window->x11.width = event->xconfigure.width;
  1499                  window->x11.height = event->xconfigure.height;
  1500              }
  1501  
  1502              int xpos = event->xconfigure.x;
  1503              int ypos = event->xconfigure.y;
  1504  
  1505              // NOTE: ConfigureNotify events from the server are in local
  1506              //       coordinates, so if we are reparented we need to translate
  1507              //       the position into root (screen) coordinates
  1508              if (!event->xany.send_event && window->x11.parent != _glfw.x11.root)
  1509              {
  1510                  _glfwGrabErrorHandlerX11();
  1511  
  1512                  Window dummy;
  1513                  XTranslateCoordinates(_glfw.x11.display,
  1514                                        window->x11.parent,
  1515                                        _glfw.x11.root,
  1516                                        xpos, ypos,
  1517                                        &xpos, &ypos,
  1518                                        &dummy);
  1519  
  1520                  _glfwReleaseErrorHandlerX11();
  1521                  if (_glfw.x11.errorCode == BadWindow)
  1522                      return;
  1523              }
  1524  
  1525              if (xpos != window->x11.xpos || ypos != window->x11.ypos)
  1526              {
  1527                  _glfwInputWindowPos(window, xpos, ypos);
  1528                  window->x11.xpos = xpos;
  1529                  window->x11.ypos = ypos;
  1530              }
  1531  
  1532              return;
  1533          }
  1534  
  1535          case ClientMessage:
  1536          {
  1537              // Custom client message, probably from the window manager
  1538  
  1539              if (filtered)
  1540                  return;
  1541  
  1542              if (event->xclient.message_type == None)
  1543                  return;
  1544  
  1545              if (event->xclient.message_type == _glfw.x11.WM_PROTOCOLS)
  1546              {
  1547                  const Atom protocol = event->xclient.data.l[0];
  1548                  if (protocol == None)
  1549                      return;
  1550  
  1551                  if (protocol == _glfw.x11.WM_DELETE_WINDOW)
  1552                  {
  1553                      // The window manager was asked to close the window, for
  1554                      // example by the user pressing a 'close' window decoration
  1555                      // button
  1556                      _glfwInputWindowCloseRequest(window);
  1557                  }
  1558                  else if (protocol == _glfw.x11.NET_WM_PING)
  1559                  {
  1560                      // The window manager is pinging the application to ensure
  1561                      // it's still responding to events
  1562  
  1563                      XEvent reply = *event;
  1564                      reply.xclient.window = _glfw.x11.root;
  1565  
  1566                      XSendEvent(_glfw.x11.display, _glfw.x11.root,
  1567                                 False,
  1568                                 SubstructureNotifyMask | SubstructureRedirectMask,
  1569                                 &reply);
  1570                  }
  1571              }
  1572              else if (event->xclient.message_type == _glfw.x11.XdndEnter)
  1573              {
  1574                  // A drag operation has entered the window
  1575                  unsigned long count;
  1576                  Atom* formats = NULL;
  1577                  const GLFWbool list = event->xclient.data.l[1] & 1;
  1578  
  1579                  _glfw.x11.xdnd.source  = event->xclient.data.l[0];
  1580                  _glfw.x11.xdnd.version = event->xclient.data.l[1] >> 24;
  1581                  _glfw.x11.xdnd.format  = None;
  1582  
  1583                  if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
  1584                      return;
  1585  
  1586                  if (list)
  1587                  {
  1588                      count = _glfwGetWindowPropertyX11(_glfw.x11.xdnd.source,
  1589                                                        _glfw.x11.XdndTypeList,
  1590                                                        XA_ATOM,
  1591                                                        (unsigned char**) &formats);
  1592                  }
  1593                  else
  1594                  {
  1595                      count = 3;
  1596                      formats = (Atom*) event->xclient.data.l + 2;
  1597                  }
  1598  
  1599                  for (unsigned int i = 0;  i < count;  i++)
  1600                  {
  1601                      if (formats[i] == _glfw.x11.text_uri_list)
  1602                      {
  1603                          _glfw.x11.xdnd.format = _glfw.x11.text_uri_list;
  1604                          break;
  1605                      }
  1606                  }
  1607  
  1608                  if (list && formats)
  1609                      XFree(formats);
  1610              }
  1611              else if (event->xclient.message_type == _glfw.x11.XdndDrop)
  1612              {
  1613                  // The drag operation has finished by dropping on the window
  1614                  Time time = CurrentTime;
  1615  
  1616                  if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
  1617                      return;
  1618  
  1619                  if (_glfw.x11.xdnd.format)
  1620                  {
  1621                      if (_glfw.x11.xdnd.version >= 1)
  1622                          time = event->xclient.data.l[2];
  1623  
  1624                      // Request the chosen format from the source window
  1625                      XConvertSelection(_glfw.x11.display,
  1626                                        _glfw.x11.XdndSelection,
  1627                                        _glfw.x11.xdnd.format,
  1628                                        _glfw.x11.XdndSelection,
  1629                                        window->x11.handle,
  1630                                        time);
  1631                  }
  1632                  else if (_glfw.x11.xdnd.version >= 2)
  1633                  {
  1634                      XEvent reply = { ClientMessage };
  1635                      reply.xclient.window = _glfw.x11.xdnd.source;
  1636                      reply.xclient.message_type = _glfw.x11.XdndFinished;
  1637                      reply.xclient.format = 32;
  1638                      reply.xclient.data.l[0] = window->x11.handle;
  1639                      reply.xclient.data.l[1] = 0; // The drag was rejected
  1640                      reply.xclient.data.l[2] = None;
  1641  
  1642                      XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
  1643                                 False, NoEventMask, &reply);
  1644                      XFlush(_glfw.x11.display);
  1645                  }
  1646              }
  1647              else if (event->xclient.message_type == _glfw.x11.XdndPosition)
  1648              {
  1649                  // The drag operation has moved over the window
  1650                  const int xabs = (event->xclient.data.l[2] >> 16) & 0xffff;
  1651                  const int yabs = (event->xclient.data.l[2]) & 0xffff;
  1652                  Window dummy;
  1653                  int xpos, ypos;
  1654  
  1655                  if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
  1656                      return;
  1657  
  1658                  XTranslateCoordinates(_glfw.x11.display,
  1659                                        _glfw.x11.root,
  1660                                        window->x11.handle,
  1661                                        xabs, yabs,
  1662                                        &xpos, &ypos,
  1663                                        &dummy);
  1664  
  1665                  _glfwInputCursorPos(window, xpos, ypos);
  1666  
  1667                  XEvent reply = { ClientMessage };
  1668                  reply.xclient.window = _glfw.x11.xdnd.source;
  1669                  reply.xclient.message_type = _glfw.x11.XdndStatus;
  1670                  reply.xclient.format = 32;
  1671                  reply.xclient.data.l[0] = window->x11.handle;
  1672                  reply.xclient.data.l[2] = 0; // Specify an empty rectangle
  1673                  reply.xclient.data.l[3] = 0;
  1674  
  1675                  if (_glfw.x11.xdnd.format)
  1676                  {
  1677                      // Reply that we are ready to copy the dragged data
  1678                      reply.xclient.data.l[1] = 1; // Accept with no rectangle
  1679                      if (_glfw.x11.xdnd.version >= 2)
  1680                          reply.xclient.data.l[4] = _glfw.x11.XdndActionCopy;
  1681                  }
  1682  
  1683                  XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
  1684                             False, NoEventMask, &reply);
  1685                  XFlush(_glfw.x11.display);
  1686              }
  1687  
  1688              return;
  1689          }
  1690  
  1691          case SelectionNotify:
  1692          {
  1693              if (event->xselection.property == _glfw.x11.XdndSelection)
  1694              {
  1695                  // The converted data from the drag operation has arrived
  1696                  char* data;
  1697                  const unsigned long result =
  1698                      _glfwGetWindowPropertyX11(event->xselection.requestor,
  1699                                                event->xselection.property,
  1700                                                event->xselection.target,
  1701                                                (unsigned char**) &data);
  1702  
  1703                  if (result)
  1704                  {
  1705                      int count;
  1706                      char** paths = _glfwParseUriList(data, &count);
  1707  
  1708                      _glfwInputDrop(window, count, (const char**) paths);
  1709  
  1710                      for (int i = 0;  i < count;  i++)
  1711                          _glfw_free(paths[i]);
  1712                      _glfw_free(paths);
  1713                  }
  1714  
  1715                  if (data)
  1716                      XFree(data);
  1717  
  1718                  if (_glfw.x11.xdnd.version >= 2)
  1719                  {
  1720                      XEvent reply = { ClientMessage };
  1721                      reply.xclient.window = _glfw.x11.xdnd.source;
  1722                      reply.xclient.message_type = _glfw.x11.XdndFinished;
  1723                      reply.xclient.format = 32;
  1724                      reply.xclient.data.l[0] = window->x11.handle;
  1725                      reply.xclient.data.l[1] = result;
  1726                      reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy;
  1727  
  1728                      XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
  1729                                 False, NoEventMask, &reply);
  1730                      XFlush(_glfw.x11.display);
  1731                  }
  1732              }
  1733  
  1734              return;
  1735          }
  1736  
  1737          case FocusIn:
  1738          {
  1739              if (event->xfocus.mode == NotifyGrab ||
  1740                  event->xfocus.mode == NotifyUngrab)
  1741              {
  1742                  // Ignore focus events from popup indicator windows, window menu
  1743                  // key chords and window dragging
  1744                  return;
  1745              }
  1746  
  1747              if (window->cursorMode == GLFW_CURSOR_DISABLED)
  1748                  disableCursor(window);
  1749              else if (window->cursorMode == GLFW_CURSOR_CAPTURED)
  1750                  captureCursor(window);
  1751  
  1752              if (window->x11.ic)
  1753                  XSetICFocus(window->x11.ic);
  1754  
  1755              _glfwInputWindowFocus(window, GLFW_TRUE);
  1756              return;
  1757          }
  1758  
  1759          case FocusOut:
  1760          {
  1761              if (event->xfocus.mode == NotifyGrab ||
  1762                  event->xfocus.mode == NotifyUngrab)
  1763              {
  1764                  // Ignore focus events from popup indicator windows, window menu
  1765                  // key chords and window dragging
  1766                  return;
  1767              }
  1768  
  1769              if (window->cursorMode == GLFW_CURSOR_DISABLED)
  1770                  enableCursor(window);
  1771              else if (window->cursorMode == GLFW_CURSOR_CAPTURED)
  1772                  releaseCursor();
  1773  
  1774              if (window->x11.ic)
  1775                  XUnsetICFocus(window->x11.ic);
  1776  
  1777              if (window->monitor && window->autoIconify)
  1778                  _glfwIconifyWindowX11(window);
  1779  
  1780              _glfwInputWindowFocus(window, GLFW_FALSE);
  1781              return;
  1782          }
  1783  
  1784          case Expose:
  1785          {
  1786              _glfwInputWindowDamage(window);
  1787              return;
  1788          }
  1789  
  1790          case PropertyNotify:
  1791          {
  1792              if (event->xproperty.state != PropertyNewValue)
  1793                  return;
  1794  
  1795              if (event->xproperty.atom == _glfw.x11.WM_STATE)
  1796              {
  1797                  const int state = getWindowState(window);
  1798                  if (state != IconicState && state != NormalState)
  1799                      return;
  1800  
  1801                  const GLFWbool iconified = (state == IconicState);
  1802                  if (window->x11.iconified != iconified)
  1803                  {
  1804                      if (window->monitor)
  1805                      {
  1806                          if (iconified)
  1807                              releaseMonitor(window);
  1808                          else
  1809                              acquireMonitor(window);
  1810                      }
  1811  
  1812                      window->x11.iconified = iconified;
  1813                      _glfwInputWindowIconify(window, iconified);
  1814                  }
  1815              }
  1816              else if (event->xproperty.atom == _glfw.x11.NET_WM_STATE)
  1817              {
  1818                  const GLFWbool maximized = _glfwWindowMaximizedX11(window);
  1819                  if (window->x11.maximized != maximized)
  1820                  {
  1821                      window->x11.maximized = maximized;
  1822                      _glfwInputWindowMaximize(window, maximized);
  1823                  }
  1824              }
  1825  
  1826              return;
  1827          }
  1828  
  1829          case DestroyNotify:
  1830              return;
  1831      }
  1832  }
  1833  
  1834  
  1835  //////////////////////////////////////////////////////////////////////////
  1836  //////                       GLFW internal API                      //////
  1837  //////////////////////////////////////////////////////////////////////////
  1838  
  1839  // Retrieve a single window property of the specified type
  1840  // Inspired by fghGetWindowProperty from freeglut
  1841  //
  1842  unsigned long _glfwGetWindowPropertyX11(Window window,
  1843                                          Atom property,
  1844                                          Atom type,
  1845                                          unsigned char** value)
  1846  {
  1847      Atom actualType;
  1848      int actualFormat;
  1849      unsigned long itemCount, bytesAfter;
  1850  
  1851      XGetWindowProperty(_glfw.x11.display,
  1852                         window,
  1853                         property,
  1854                         0,
  1855                         LONG_MAX,
  1856                         False,
  1857                         type,
  1858                         &actualType,
  1859                         &actualFormat,
  1860                         &itemCount,
  1861                         &bytesAfter,
  1862                         value);
  1863  
  1864      return itemCount;
  1865  }
  1866  
  1867  GLFWbool _glfwIsVisualTransparentX11(Visual* visual)
  1868  {
  1869      if (!_glfw.x11.xrender.available)
  1870          return GLFW_FALSE;
  1871  
  1872      XRenderPictFormat* pf = XRenderFindVisualFormat(_glfw.x11.display, visual);
  1873      return pf && pf->direct.alphaMask;
  1874  }
  1875  
  1876  // Push contents of our selection to clipboard manager
  1877  //
  1878  void _glfwPushSelectionToManagerX11(void)
  1879  {
  1880      XConvertSelection(_glfw.x11.display,
  1881                        _glfw.x11.CLIPBOARD_MANAGER,
  1882                        _glfw.x11.SAVE_TARGETS,
  1883                        None,
  1884                        _glfw.x11.helperWindowHandle,
  1885                        CurrentTime);
  1886  
  1887      for (;;)
  1888      {
  1889          XEvent event;
  1890  
  1891          while (XCheckIfEvent(_glfw.x11.display, &event, isSelectionEvent, NULL))
  1892          {
  1893              switch (event.type)
  1894              {
  1895                  case SelectionRequest:
  1896                      handleSelectionRequest(&event);
  1897                      break;
  1898  
  1899                  case SelectionNotify:
  1900                  {
  1901                      if (event.xselection.target == _glfw.x11.SAVE_TARGETS)
  1902                      {
  1903                          // This means one of two things; either the selection
  1904                          // was not owned, which means there is no clipboard
  1905                          // manager, or the transfer to the clipboard manager has
  1906                          // completed
  1907                          // In either case, it means we are done here
  1908                          return;
  1909                      }
  1910  
  1911                      break;
  1912                  }
  1913              }
  1914          }
  1915  
  1916          waitForX11Event(NULL);
  1917      }
  1918  }
  1919  
  1920  void _glfwCreateInputContextX11(_GLFWwindow* window)
  1921  {
  1922      XIMCallback callback;
  1923      callback.callback = (XIMProc) inputContextDestroyCallback;
  1924      callback.client_data = (XPointer) window;
  1925  
  1926      window->x11.ic = XCreateIC(_glfw.x11.im,
  1927                                 XNInputStyle,
  1928                                 XIMPreeditNothing | XIMStatusNothing,
  1929                                 XNClientWindow,
  1930                                 window->x11.handle,
  1931                                 XNFocusWindow,
  1932                                 window->x11.handle,
  1933                                 XNDestroyCallback,
  1934                                 &callback,
  1935                                 NULL);
  1936  
  1937      if (window->x11.ic)
  1938      {
  1939          XWindowAttributes attribs;
  1940          XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &attribs);
  1941  
  1942          unsigned long filter = 0;
  1943          if (XGetICValues(window->x11.ic, XNFilterEvents, &filter, NULL) == NULL)
  1944          {
  1945              XSelectInput(_glfw.x11.display,
  1946                           window->x11.handle,
  1947                           attribs.your_event_mask | filter);
  1948          }
  1949      }
  1950  }
  1951  
  1952  
  1953  //////////////////////////////////////////////////////////////////////////
  1954  //////                       GLFW platform API                      //////
  1955  //////////////////////////////////////////////////////////////////////////
  1956  
  1957  GLFWbool _glfwCreateWindowX11(_GLFWwindow* window,
  1958                                const _GLFWwndconfig* wndconfig,
  1959                                const _GLFWctxconfig* ctxconfig,
  1960                                const _GLFWfbconfig* fbconfig)
  1961  {
  1962      Visual* visual = NULL;
  1963      int depth;
  1964  
  1965      if (ctxconfig->client != GLFW_NO_API)
  1966      {
  1967          if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
  1968          {
  1969              if (!_glfwInitGLX())
  1970                  return GLFW_FALSE;
  1971              if (!_glfwChooseVisualGLX(wndconfig, ctxconfig, fbconfig, &visual, &depth))
  1972                  return GLFW_FALSE;
  1973          }
  1974          else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
  1975          {
  1976              if (!_glfwInitEGL())
  1977                  return GLFW_FALSE;
  1978              if (!_glfwChooseVisualEGL(wndconfig, ctxconfig, fbconfig, &visual, &depth))
  1979                  return GLFW_FALSE;
  1980          }
  1981          else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
  1982          {
  1983              if (!_glfwInitOSMesa())
  1984                  return GLFW_FALSE;
  1985          }
  1986      }
  1987  
  1988      if (!visual)
  1989      {
  1990          visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen);
  1991          depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen);
  1992      }
  1993  
  1994      if (!createNativeWindow(window, wndconfig, visual, depth))
  1995          return GLFW_FALSE;
  1996  
  1997      if (ctxconfig->client != GLFW_NO_API)
  1998      {
  1999          if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
  2000          {
  2001              if (!_glfwCreateContextGLX(window, ctxconfig, fbconfig))
  2002                  return GLFW_FALSE;
  2003          }
  2004          else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
  2005          {
  2006              if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
  2007                  return GLFW_FALSE;
  2008          }
  2009          else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
  2010          {
  2011              if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
  2012                  return GLFW_FALSE;
  2013          }
  2014  
  2015          if (!_glfwRefreshContextAttribs(window, ctxconfig))
  2016              return GLFW_FALSE;
  2017      }
  2018  
  2019      if (wndconfig->mousePassthrough)
  2020          _glfwSetWindowMousePassthroughX11(window, GLFW_TRUE);
  2021  
  2022      if (window->monitor)
  2023      {
  2024          _glfwShowWindowX11(window);
  2025          updateWindowMode(window);
  2026          acquireMonitor(window);
  2027  
  2028          if (wndconfig->centerCursor)
  2029              _glfwCenterCursorInContentArea(window);
  2030      }
  2031      else
  2032      {
  2033          if (wndconfig->visible)
  2034          {
  2035              _glfwShowWindowX11(window);
  2036              if (wndconfig->focused)
  2037                  _glfwFocusWindowX11(window);
  2038          }
  2039      }
  2040  
  2041      XFlush(_glfw.x11.display);
  2042      return GLFW_TRUE;
  2043  }
  2044  
  2045  void _glfwDestroyWindowX11(_GLFWwindow* window)
  2046  {
  2047      if (_glfw.x11.disabledCursorWindow == window)
  2048          enableCursor(window);
  2049  
  2050      if (window->monitor)
  2051          releaseMonitor(window);
  2052  
  2053      if (window->x11.ic)
  2054      {
  2055          XDestroyIC(window->x11.ic);
  2056          window->x11.ic = NULL;
  2057      }
  2058  
  2059      if (window->context.destroy)
  2060          window->context.destroy(window);
  2061  
  2062      if (window->x11.handle)
  2063      {
  2064          XDeleteContext(_glfw.x11.display, window->x11.handle, _glfw.x11.context);
  2065          XUnmapWindow(_glfw.x11.display, window->x11.handle);
  2066          XDestroyWindow(_glfw.x11.display, window->x11.handle);
  2067          window->x11.handle = (Window) 0;
  2068      }
  2069  
  2070      if (window->x11.colormap)
  2071      {
  2072          XFreeColormap(_glfw.x11.display, window->x11.colormap);
  2073          window->x11.colormap = (Colormap) 0;
  2074      }
  2075  
  2076      XFlush(_glfw.x11.display);
  2077  }
  2078  
  2079  void _glfwSetWindowTitleX11(_GLFWwindow* window, const char* title)
  2080  {
  2081      if (_glfw.x11.xlib.utf8)
  2082      {
  2083          Xutf8SetWMProperties(_glfw.x11.display,
  2084                               window->x11.handle,
  2085                               title, title,
  2086                               NULL, 0,
  2087                               NULL, NULL, NULL);
  2088      }
  2089  
  2090      XChangeProperty(_glfw.x11.display,  window->x11.handle,
  2091                      _glfw.x11.NET_WM_NAME, _glfw.x11.UTF8_STRING, 8,
  2092                      PropModeReplace,
  2093                      (unsigned char*) title, strlen(title));
  2094  
  2095      XChangeProperty(_glfw.x11.display,  window->x11.handle,
  2096                      _glfw.x11.NET_WM_ICON_NAME, _glfw.x11.UTF8_STRING, 8,
  2097                      PropModeReplace,
  2098                      (unsigned char*) title, strlen(title));
  2099  
  2100      XFlush(_glfw.x11.display);
  2101  }
  2102  
  2103  void _glfwSetWindowIconX11(_GLFWwindow* window, int count, const GLFWimage* images)
  2104  {
  2105      if (count)
  2106      {
  2107          int longCount = 0;
  2108  
  2109          for (int i = 0;  i < count;  i++)
  2110              longCount += 2 + images[i].width * images[i].height;
  2111  
  2112          unsigned long* icon = _glfw_calloc(longCount, sizeof(unsigned long));
  2113          unsigned long* target = icon;
  2114  
  2115          for (int i = 0;  i < count;  i++)
  2116          {
  2117              *target++ = images[i].width;
  2118              *target++ = images[i].height;
  2119  
  2120              for (int j = 0;  j < images[i].width * images[i].height;  j++)
  2121              {
  2122                  *target++ = (((unsigned long) images[i].pixels[j * 4 + 0]) << 16) |
  2123                              (((unsigned long) images[i].pixels[j * 4 + 1]) <<  8) |
  2124                              (((unsigned long) images[i].pixels[j * 4 + 2]) <<  0) |
  2125                              (((unsigned long) images[i].pixels[j * 4 + 3]) << 24);
  2126              }
  2127          }
  2128  
  2129          // NOTE: XChangeProperty expects 32-bit values like the image data above to be
  2130          //       placed in the 32 least significant bits of individual longs.  This is
  2131          //       true even if long is 64-bit and a WM protocol calls for "packed" data.
  2132          //       This is because of a historical mistake that then became part of the Xlib
  2133          //       ABI.  Xlib will pack these values into a regular array of 32-bit values
  2134          //       before sending it over the wire.
  2135          XChangeProperty(_glfw.x11.display, window->x11.handle,
  2136                          _glfw.x11.NET_WM_ICON,
  2137                          XA_CARDINAL, 32,
  2138                          PropModeReplace,
  2139                          (unsigned char*) icon,
  2140                          longCount);
  2141  
  2142          _glfw_free(icon);
  2143      }
  2144      else
  2145      {
  2146          XDeleteProperty(_glfw.x11.display, window->x11.handle,
  2147                          _glfw.x11.NET_WM_ICON);
  2148      }
  2149  
  2150      XFlush(_glfw.x11.display);
  2151  }
  2152  
  2153  void _glfwGetWindowPosX11(_GLFWwindow* window, int* xpos, int* ypos)
  2154  {
  2155      Window dummy;
  2156      int x, y;
  2157  
  2158      XTranslateCoordinates(_glfw.x11.display, window->x11.handle, _glfw.x11.root,
  2159                            0, 0, &x, &y, &dummy);
  2160  
  2161      if (xpos)
  2162          *xpos = x;
  2163      if (ypos)
  2164          *ypos = y;
  2165  }
  2166  
  2167  void _glfwSetWindowPosX11(_GLFWwindow* window, int xpos, int ypos)
  2168  {
  2169      // HACK: Explicitly setting PPosition to any value causes some WMs, notably
  2170      //       Compiz and Metacity, to honor the position of unmapped windows
  2171      if (!_glfwWindowVisibleX11(window))
  2172      {
  2173          long supplied;
  2174          XSizeHints* hints = XAllocSizeHints();
  2175  
  2176          if (XGetWMNormalHints(_glfw.x11.display, window->x11.handle, hints, &supplied))
  2177          {
  2178              hints->flags |= PPosition;
  2179              hints->x = hints->y = 0;
  2180  
  2181              XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints);
  2182          }
  2183  
  2184          XFree(hints);
  2185      }
  2186  
  2187      XMoveWindow(_glfw.x11.display, window->x11.handle, xpos, ypos);
  2188      XFlush(_glfw.x11.display);
  2189  }
  2190  
  2191  void _glfwGetWindowSizeX11(_GLFWwindow* window, int* width, int* height)
  2192  {
  2193      XWindowAttributes attribs;
  2194      XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &attribs);
  2195  
  2196      if (width)
  2197          *width = attribs.width;
  2198      if (height)
  2199          *height = attribs.height;
  2200  }
  2201  
  2202  void _glfwSetWindowSizeX11(_GLFWwindow* window, int width, int height)
  2203  {
  2204      if (window->monitor)
  2205      {
  2206          if (window->monitor->window == window)
  2207              acquireMonitor(window);
  2208      }
  2209      else
  2210      {
  2211          if (!window->resizable)
  2212              updateNormalHints(window, width, height);
  2213  
  2214          XResizeWindow(_glfw.x11.display, window->x11.handle, width, height);
  2215      }
  2216  
  2217      XFlush(_glfw.x11.display);
  2218  }
  2219  
  2220  void _glfwSetWindowSizeLimitsX11(_GLFWwindow* window,
  2221                                   int minwidth, int minheight,
  2222                                   int maxwidth, int maxheight)
  2223  {
  2224      int width, height;
  2225      _glfwGetWindowSizeX11(window, &width, &height);
  2226      updateNormalHints(window, width, height);
  2227      XFlush(_glfw.x11.display);
  2228  }
  2229  
  2230  void _glfwSetWindowAspectRatioX11(_GLFWwindow* window, int numer, int denom)
  2231  {
  2232      int width, height;
  2233      _glfwGetWindowSizeX11(window, &width, &height);
  2234      updateNormalHints(window, width, height);
  2235      XFlush(_glfw.x11.display);
  2236  }
  2237  
  2238  void _glfwGetFramebufferSizeX11(_GLFWwindow* window, int* width, int* height)
  2239  {
  2240      _glfwGetWindowSizeX11(window, width, height);
  2241  }
  2242  
  2243  void _glfwGetWindowFrameSizeX11(_GLFWwindow* window,
  2244                                  int* left, int* top,
  2245                                  int* right, int* bottom)
  2246  {
  2247      long* extents = NULL;
  2248  
  2249      if (window->monitor || !window->decorated)
  2250          return;
  2251  
  2252      if (_glfw.x11.NET_FRAME_EXTENTS == None)
  2253          return;
  2254  
  2255      if (!_glfwWindowVisibleX11(window) &&
  2256          _glfw.x11.NET_REQUEST_FRAME_EXTENTS)
  2257      {
  2258          XEvent event;
  2259          double timeout = 0.5;
  2260  
  2261          // Ensure _NET_FRAME_EXTENTS is set, allowing glfwGetWindowFrameSize to
  2262          // function before the window is mapped
  2263          sendEventToWM(window, _glfw.x11.NET_REQUEST_FRAME_EXTENTS,
  2264                        0, 0, 0, 0, 0);
  2265  
  2266          // HACK: Use a timeout because earlier versions of some window managers
  2267          //       (at least Unity, Fluxbox and Xfwm) failed to send the reply
  2268          //       They have been fixed but broken versions are still in the wild
  2269          //       If you are affected by this and your window manager is NOT
  2270          //       listed above, PLEASE report it to their and our issue trackers
  2271          while (!XCheckIfEvent(_glfw.x11.display,
  2272                                &event,
  2273                                isFrameExtentsEvent,
  2274                                (XPointer) window))
  2275          {
  2276              if (!waitForX11Event(&timeout))
  2277              {
  2278                  _glfwInputError(GLFW_PLATFORM_ERROR,
  2279                                  "X11: The window manager has a broken _NET_REQUEST_FRAME_EXTENTS implementation; please report this issue");
  2280                  return;
  2281              }
  2282          }
  2283      }
  2284  
  2285      if (_glfwGetWindowPropertyX11(window->x11.handle,
  2286                                    _glfw.x11.NET_FRAME_EXTENTS,
  2287                                    XA_CARDINAL,
  2288                                    (unsigned char**) &extents) == 4)
  2289      {
  2290          if (left)
  2291              *left = extents[0];
  2292          if (top)
  2293              *top = extents[2];
  2294          if (right)
  2295              *right = extents[1];
  2296          if (bottom)
  2297              *bottom = extents[3];
  2298      }
  2299  
  2300      if (extents)
  2301          XFree(extents);
  2302  }
  2303  
  2304  void _glfwGetWindowContentScaleX11(_GLFWwindow* window, float* xscale, float* yscale)
  2305  {
  2306      if (xscale)
  2307          *xscale = _glfw.x11.contentScaleX;
  2308      if (yscale)
  2309          *yscale = _glfw.x11.contentScaleY;
  2310  }
  2311  
  2312  void _glfwIconifyWindowX11(_GLFWwindow* window)
  2313  {
  2314      if (window->x11.overrideRedirect)
  2315      {
  2316          // Override-redirect windows cannot be iconified or restored, as those
  2317          // tasks are performed by the window manager
  2318          _glfwInputError(GLFW_PLATFORM_ERROR,
  2319                          "X11: Iconification of full screen windows requires a WM that supports EWMH full screen");
  2320          return;
  2321      }
  2322  
  2323      XIconifyWindow(_glfw.x11.display, window->x11.handle, _glfw.x11.screen);
  2324      XFlush(_glfw.x11.display);
  2325  }
  2326  
  2327  void _glfwRestoreWindowX11(_GLFWwindow* window)
  2328  {
  2329      if (window->x11.overrideRedirect)
  2330      {
  2331          // Override-redirect windows cannot be iconified or restored, as those
  2332          // tasks are performed by the window manager
  2333          _glfwInputError(GLFW_PLATFORM_ERROR,
  2334                          "X11: Iconification of full screen windows requires a WM that supports EWMH full screen");
  2335          return;
  2336      }
  2337  
  2338      if (_glfwWindowIconifiedX11(window))
  2339      {
  2340          XMapWindow(_glfw.x11.display, window->x11.handle);
  2341          waitForVisibilityNotify(window);
  2342      }
  2343      else if (_glfwWindowVisibleX11(window))
  2344      {
  2345          if (_glfw.x11.NET_WM_STATE &&
  2346              _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
  2347              _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
  2348          {
  2349              sendEventToWM(window,
  2350                            _glfw.x11.NET_WM_STATE,
  2351                            _NET_WM_STATE_REMOVE,
  2352                            _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
  2353                            _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
  2354                            1, 0);
  2355          }
  2356      }
  2357  
  2358      XFlush(_glfw.x11.display);
  2359  }
  2360  
  2361  void _glfwMaximizeWindowX11(_GLFWwindow* window)
  2362  {
  2363      if (!_glfw.x11.NET_WM_STATE ||
  2364          !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
  2365          !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
  2366      {
  2367          return;
  2368      }
  2369  
  2370      if (_glfwWindowVisibleX11(window))
  2371      {
  2372          sendEventToWM(window,
  2373                      _glfw.x11.NET_WM_STATE,
  2374                      _NET_WM_STATE_ADD,
  2375                      _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
  2376                      _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
  2377                      1, 0);
  2378      }
  2379      else
  2380      {
  2381          Atom* states = NULL;
  2382          unsigned long count =
  2383              _glfwGetWindowPropertyX11(window->x11.handle,
  2384                                        _glfw.x11.NET_WM_STATE,
  2385                                        XA_ATOM,
  2386                                        (unsigned char**) &states);
  2387  
  2388          // NOTE: We don't check for failure as this property may not exist yet
  2389          //       and that's fine (and we'll create it implicitly with append)
  2390  
  2391          Atom missing[2] =
  2392          {
  2393              _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
  2394              _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ
  2395          };
  2396          unsigned long missingCount = 2;
  2397  
  2398          for (unsigned long i = 0;  i < count;  i++)
  2399          {
  2400              for (unsigned long j = 0;  j < missingCount;  j++)
  2401              {
  2402                  if (states[i] == missing[j])
  2403                  {
  2404                      missing[j] = missing[missingCount - 1];
  2405                      missingCount--;
  2406                  }
  2407              }
  2408          }
  2409  
  2410          if (states)
  2411              XFree(states);
  2412  
  2413          if (!missingCount)
  2414              return;
  2415  
  2416          XChangeProperty(_glfw.x11.display, window->x11.handle,
  2417                          _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
  2418                          PropModeAppend,
  2419                          (unsigned char*) missing,
  2420                          missingCount);
  2421      }
  2422  
  2423      XFlush(_glfw.x11.display);
  2424  }
  2425  
  2426  void _glfwShowWindowX11(_GLFWwindow* window)
  2427  {
  2428      if (_glfwWindowVisibleX11(window))
  2429          return;
  2430  
  2431      XMapWindow(_glfw.x11.display, window->x11.handle);
  2432      waitForVisibilityNotify(window);
  2433  }
  2434  
  2435  void _glfwHideWindowX11(_GLFWwindow* window)
  2436  {
  2437      XUnmapWindow(_glfw.x11.display, window->x11.handle);
  2438      XFlush(_glfw.x11.display);
  2439  }
  2440  
  2441  void _glfwRequestWindowAttentionX11(_GLFWwindow* window)
  2442  {
  2443      if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION)
  2444          return;
  2445  
  2446      sendEventToWM(window,
  2447                    _glfw.x11.NET_WM_STATE,
  2448                    _NET_WM_STATE_ADD,
  2449                    _glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION,
  2450                    0, 1, 0);
  2451  }
  2452  
  2453  void _glfwFocusWindowX11(_GLFWwindow* window)
  2454  {
  2455      if (_glfw.x11.NET_ACTIVE_WINDOW)
  2456          sendEventToWM(window, _glfw.x11.NET_ACTIVE_WINDOW, 1, 0, 0, 0, 0);
  2457      else if (_glfwWindowVisibleX11(window))
  2458      {
  2459          XRaiseWindow(_glfw.x11.display, window->x11.handle);
  2460          XSetInputFocus(_glfw.x11.display, window->x11.handle,
  2461                         RevertToParent, CurrentTime);
  2462      }
  2463  
  2464      XFlush(_glfw.x11.display);
  2465  }
  2466  
  2467  void _glfwSetWindowMonitorX11(_GLFWwindow* window,
  2468                                _GLFWmonitor* monitor,
  2469                                int xpos, int ypos,
  2470                                int width, int height,
  2471                                int refreshRate)
  2472  {
  2473      if (window->monitor == monitor)
  2474      {
  2475          if (monitor)
  2476          {
  2477              if (monitor->window == window)
  2478                  acquireMonitor(window);
  2479          }
  2480          else
  2481          {
  2482              if (!window->resizable)
  2483                  updateNormalHints(window, width, height);
  2484  
  2485              XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
  2486                                xpos, ypos, width, height);
  2487          }
  2488  
  2489          XFlush(_glfw.x11.display);
  2490          return;
  2491      }
  2492  
  2493      if (window->monitor)
  2494      {
  2495          _glfwSetWindowDecoratedX11(window, window->decorated);
  2496          _glfwSetWindowFloatingX11(window, window->floating);
  2497          releaseMonitor(window);
  2498      }
  2499  
  2500      _glfwInputWindowMonitor(window, monitor);
  2501      updateNormalHints(window, width, height);
  2502  
  2503      if (window->monitor)
  2504      {
  2505          if (!_glfwWindowVisibleX11(window))
  2506          {
  2507              XMapRaised(_glfw.x11.display, window->x11.handle);
  2508              waitForVisibilityNotify(window);
  2509          }
  2510  
  2511          updateWindowMode(window);
  2512          acquireMonitor(window);
  2513      }
  2514      else
  2515      {
  2516          updateWindowMode(window);
  2517          XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
  2518                            xpos, ypos, width, height);
  2519      }
  2520  
  2521      XFlush(_glfw.x11.display);
  2522  }
  2523  
  2524  GLFWbool _glfwWindowFocusedX11(_GLFWwindow* window)
  2525  {
  2526      Window focused;
  2527      int state;
  2528  
  2529      XGetInputFocus(_glfw.x11.display, &focused, &state);
  2530      return window->x11.handle == focused;
  2531  }
  2532  
  2533  GLFWbool _glfwWindowIconifiedX11(_GLFWwindow* window)
  2534  {
  2535      return getWindowState(window) == IconicState;
  2536  }
  2537  
  2538  GLFWbool _glfwWindowVisibleX11(_GLFWwindow* window)
  2539  {
  2540      XWindowAttributes wa;
  2541      XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &wa);
  2542      return wa.map_state == IsViewable;
  2543  }
  2544  
  2545  GLFWbool _glfwWindowMaximizedX11(_GLFWwindow* window)
  2546  {
  2547      Atom* states;
  2548      GLFWbool maximized = GLFW_FALSE;
  2549  
  2550      if (!_glfw.x11.NET_WM_STATE ||
  2551          !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
  2552          !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
  2553      {
  2554          return maximized;
  2555      }
  2556  
  2557      const unsigned long count =
  2558          _glfwGetWindowPropertyX11(window->x11.handle,
  2559                                    _glfw.x11.NET_WM_STATE,
  2560                                    XA_ATOM,
  2561                                    (unsigned char**) &states);
  2562  
  2563      for (unsigned long i = 0;  i < count;  i++)
  2564      {
  2565          if (states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
  2566              states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
  2567          {
  2568              maximized = GLFW_TRUE;
  2569              break;
  2570          }
  2571      }
  2572  
  2573      if (states)
  2574          XFree(states);
  2575  
  2576      return maximized;
  2577  }
  2578  
  2579  GLFWbool _glfwWindowHoveredX11(_GLFWwindow* window)
  2580  {
  2581      Window w = _glfw.x11.root;
  2582      while (w)
  2583      {
  2584          Window root;
  2585          int rootX, rootY, childX, childY;
  2586          unsigned int mask;
  2587  
  2588          _glfwGrabErrorHandlerX11();
  2589  
  2590          const Bool result = XQueryPointer(_glfw.x11.display, w,
  2591                                            &root, &w, &rootX, &rootY,
  2592                                            &childX, &childY, &mask);
  2593  
  2594          _glfwReleaseErrorHandlerX11();
  2595  
  2596          if (_glfw.x11.errorCode == BadWindow)
  2597              w = _glfw.x11.root;
  2598          else if (!result)
  2599              return GLFW_FALSE;
  2600          else if (w == window->x11.handle)
  2601              return GLFW_TRUE;
  2602      }
  2603  
  2604      return GLFW_FALSE;
  2605  }
  2606  
  2607  GLFWbool _glfwFramebufferTransparentX11(_GLFWwindow* window)
  2608  {
  2609      if (!window->x11.transparent)
  2610          return GLFW_FALSE;
  2611  
  2612      return XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx) != None;
  2613  }
  2614  
  2615  void _glfwSetWindowResizableX11(_GLFWwindow* window, GLFWbool enabled)
  2616  {
  2617      int width, height;
  2618      _glfwGetWindowSizeX11(window, &width, &height);
  2619      updateNormalHints(window, width, height);
  2620  }
  2621  
  2622  void _glfwSetWindowDecoratedX11(_GLFWwindow* window, GLFWbool enabled)
  2623  {
  2624      struct
  2625      {
  2626          unsigned long flags;
  2627          unsigned long functions;
  2628          unsigned long decorations;
  2629          long input_mode;
  2630          unsigned long status;
  2631      } hints = {0};
  2632  
  2633      hints.flags = MWM_HINTS_DECORATIONS;
  2634      hints.decorations = enabled ? MWM_DECOR_ALL : 0;
  2635  
  2636      XChangeProperty(_glfw.x11.display, window->x11.handle,
  2637                      _glfw.x11.MOTIF_WM_HINTS,
  2638                      _glfw.x11.MOTIF_WM_HINTS, 32,
  2639                      PropModeReplace,
  2640                      (unsigned char*) &hints,
  2641                      sizeof(hints) / sizeof(long));
  2642  }
  2643  
  2644  void _glfwSetWindowFloatingX11(_GLFWwindow* window, GLFWbool enabled)
  2645  {
  2646      if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_ABOVE)
  2647          return;
  2648  
  2649      if (_glfwWindowVisibleX11(window))
  2650      {
  2651          const long action = enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
  2652          sendEventToWM(window,
  2653                        _glfw.x11.NET_WM_STATE,
  2654                        action,
  2655                        _glfw.x11.NET_WM_STATE_ABOVE,
  2656                        0, 1, 0);
  2657      }
  2658      else
  2659      {
  2660          Atom* states = NULL;
  2661          const unsigned long count =
  2662              _glfwGetWindowPropertyX11(window->x11.handle,
  2663                                        _glfw.x11.NET_WM_STATE,
  2664                                        XA_ATOM,
  2665                                        (unsigned char**) &states);
  2666  
  2667          // NOTE: We don't check for failure as this property may not exist yet
  2668          //       and that's fine (and we'll create it implicitly with append)
  2669  
  2670          if (enabled)
  2671          {
  2672              unsigned long i;
  2673  
  2674              for (i = 0;  i < count;  i++)
  2675              {
  2676                  if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE)
  2677                      break;
  2678              }
  2679  
  2680              if (i == count)
  2681              {
  2682                  XChangeProperty(_glfw.x11.display, window->x11.handle,
  2683                                  _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
  2684                                  PropModeAppend,
  2685                                  (unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE,
  2686                                  1);
  2687              }
  2688          }
  2689          else if (states)
  2690          {
  2691              for (unsigned long i = 0;  i < count;  i++)
  2692              {
  2693                  if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE)
  2694                  {
  2695                      states[i] = states[count - 1];
  2696                      XChangeProperty(_glfw.x11.display, window->x11.handle,
  2697                                      _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
  2698                                      PropModeReplace, (unsigned char*) states, count - 1);
  2699                      break;
  2700                  }
  2701              }
  2702          }
  2703  
  2704          if (states)
  2705              XFree(states);
  2706      }
  2707  
  2708      XFlush(_glfw.x11.display);
  2709  }
  2710  
  2711  void _glfwSetWindowMousePassthroughX11(_GLFWwindow* window, GLFWbool enabled)
  2712  {
  2713      if (!_glfw.x11.xshape.available)
  2714          return;
  2715  
  2716      if (enabled)
  2717      {
  2718          Region region = XCreateRegion();
  2719          XShapeCombineRegion(_glfw.x11.display, window->x11.handle,
  2720                              ShapeInput, 0, 0, region, ShapeSet);
  2721          XDestroyRegion(region);
  2722      }
  2723      else
  2724      {
  2725          XShapeCombineMask(_glfw.x11.display, window->x11.handle,
  2726                            ShapeInput, 0, 0, None, ShapeSet);
  2727      }
  2728  }
  2729  
  2730  float _glfwGetWindowOpacityX11(_GLFWwindow* window)
  2731  {
  2732      float opacity = 1.f;
  2733  
  2734      if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx))
  2735      {
  2736          CARD32* value = NULL;
  2737  
  2738          if (_glfwGetWindowPropertyX11(window->x11.handle,
  2739                                        _glfw.x11.NET_WM_WINDOW_OPACITY,
  2740                                        XA_CARDINAL,
  2741                                        (unsigned char**) &value))
  2742          {
  2743              opacity = (float) (*value / (double) 0xffffffffu);
  2744          }
  2745  
  2746          if (value)
  2747              XFree(value);
  2748      }
  2749  
  2750      return opacity;
  2751  }
  2752  
  2753  void _glfwSetWindowOpacityX11(_GLFWwindow* window, float opacity)
  2754  {
  2755      const CARD32 value = (CARD32) (0xffffffffu * (double) opacity);
  2756      XChangeProperty(_glfw.x11.display, window->x11.handle,
  2757                      _glfw.x11.NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32,
  2758                      PropModeReplace, (unsigned char*) &value, 1);
  2759  }
  2760  
  2761  void _glfwSetRawMouseMotionX11(_GLFWwindow *window, GLFWbool enabled)
  2762  {
  2763      if (!_glfw.x11.xi.available)
  2764          return;
  2765  
  2766      if (_glfw.x11.disabledCursorWindow != window)
  2767          return;
  2768  
  2769      if (enabled)
  2770          enableRawMouseMotion(window);
  2771      else
  2772          disableRawMouseMotion(window);
  2773  }
  2774  
  2775  GLFWbool _glfwRawMouseMotionSupportedX11(void)
  2776  {
  2777      return _glfw.x11.xi.available;
  2778  }
  2779  
  2780  void _glfwPollEventsX11(void)
  2781  {
  2782      drainEmptyEvents();
  2783  
  2784  #if defined(__linux__)
  2785      if (_glfw.joysticksInitialized)
  2786          _glfwDetectJoystickConnectionLinux();
  2787  #endif
  2788      XPending(_glfw.x11.display);
  2789  
  2790      while (QLength(_glfw.x11.display))
  2791      {
  2792          XEvent event;
  2793          XNextEvent(_glfw.x11.display, &event);
  2794          processEvent(&event);
  2795      }
  2796  
  2797      _GLFWwindow* window = _glfw.x11.disabledCursorWindow;
  2798      if (window)
  2799      {
  2800          int width, height;
  2801          _glfwGetWindowSizeX11(window, &width, &height);
  2802  
  2803          // NOTE: Re-center the cursor only if it has moved since the last call,
  2804          //       to avoid breaking glfwWaitEvents with MotionNotify
  2805          if (window->x11.lastCursorPosX != width / 2 ||
  2806              window->x11.lastCursorPosY != height / 2)
  2807          {
  2808              _glfwSetCursorPosX11(window, width / 2, height / 2);
  2809          }
  2810      }
  2811  
  2812      XFlush(_glfw.x11.display);
  2813  }
  2814  
  2815  void _glfwWaitEventsX11(void)
  2816  {
  2817      waitForAnyEvent(NULL);
  2818      _glfwPollEventsX11();
  2819  }
  2820  
  2821  void _glfwWaitEventsTimeoutX11(double timeout)
  2822  {
  2823      waitForAnyEvent(&timeout);
  2824      _glfwPollEventsX11();
  2825  }
  2826  
  2827  void _glfwPostEmptyEventX11(void)
  2828  {
  2829      writeEmptyEvent();
  2830  }
  2831  
  2832  void _glfwGetCursorPosX11(_GLFWwindow* window, double* xpos, double* ypos)
  2833  {
  2834      Window root, child;
  2835      int rootX, rootY, childX, childY;
  2836      unsigned int mask;
  2837  
  2838      XQueryPointer(_glfw.x11.display, window->x11.handle,
  2839                    &root, &child,
  2840                    &rootX, &rootY, &childX, &childY,
  2841                    &mask);
  2842  
  2843      if (xpos)
  2844          *xpos = childX;
  2845      if (ypos)
  2846          *ypos = childY;
  2847  }
  2848  
  2849  void _glfwSetCursorPosX11(_GLFWwindow* window, double x, double y)
  2850  {
  2851      // Store the new position so it can be recognized later
  2852      window->x11.warpCursorPosX = (int) x;
  2853      window->x11.warpCursorPosY = (int) y;
  2854  
  2855      XWarpPointer(_glfw.x11.display, None, window->x11.handle,
  2856                   0,0,0,0, (int) x, (int) y);
  2857      XFlush(_glfw.x11.display);
  2858  }
  2859  
  2860  void _glfwSetCursorModeX11(_GLFWwindow* window, int mode)
  2861  {
  2862      if (_glfwWindowFocusedX11(window))
  2863      {
  2864          if (mode == GLFW_CURSOR_DISABLED)
  2865          {
  2866              _glfwGetCursorPosX11(window,
  2867                                   &_glfw.x11.restoreCursorPosX,
  2868                                   &_glfw.x11.restoreCursorPosY);
  2869              _glfwCenterCursorInContentArea(window);
  2870              if (window->rawMouseMotion)
  2871                  enableRawMouseMotion(window);
  2872          }
  2873          else if (_glfw.x11.disabledCursorWindow == window)
  2874          {
  2875              if (window->rawMouseMotion)
  2876                  disableRawMouseMotion(window);
  2877          }
  2878  
  2879          if (mode == GLFW_CURSOR_DISABLED || mode == GLFW_CURSOR_CAPTURED)
  2880              captureCursor(window);
  2881          else
  2882              releaseCursor();
  2883  
  2884          if (mode == GLFW_CURSOR_DISABLED)
  2885              _glfw.x11.disabledCursorWindow = window;
  2886          else if (_glfw.x11.disabledCursorWindow == window)
  2887          {
  2888              _glfw.x11.disabledCursorWindow = NULL;
  2889              _glfwSetCursorPosX11(window,
  2890                                   _glfw.x11.restoreCursorPosX,
  2891                                   _glfw.x11.restoreCursorPosY);
  2892          }
  2893      }
  2894  
  2895      updateCursorImage(window);
  2896      XFlush(_glfw.x11.display);
  2897  }
  2898  
  2899  const char* _glfwGetScancodeNameX11(int scancode)
  2900  {
  2901      if (!_glfw.x11.xkb.available)
  2902          return NULL;
  2903  
  2904      if (scancode < 0 || scancode > 0xff ||
  2905          _glfw.x11.keycodes[scancode] == GLFW_KEY_UNKNOWN)
  2906      {
  2907          _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode %i", scancode);
  2908          return NULL;
  2909      }
  2910  
  2911      const int key = _glfw.x11.keycodes[scancode];
  2912      const KeySym keysym = XkbKeycodeToKeysym(_glfw.x11.display,
  2913                                               scancode, _glfw.x11.xkb.group, 0);
  2914      if (keysym == NoSymbol)
  2915          return NULL;
  2916  
  2917      const uint32_t codepoint = _glfwKeySym2Unicode(keysym);
  2918      if (codepoint == GLFW_INVALID_CODEPOINT)
  2919          return NULL;
  2920  
  2921      const size_t count = _glfwEncodeUTF8(_glfw.x11.keynames[key], codepoint);
  2922      if (count == 0)
  2923          return NULL;
  2924  
  2925      _glfw.x11.keynames[key][count] = '\0';
  2926      return _glfw.x11.keynames[key];
  2927  }
  2928  
  2929  int _glfwGetKeyScancodeX11(int key)
  2930  {
  2931      return _glfw.x11.scancodes[key];
  2932  }
  2933  
  2934  GLFWbool _glfwCreateCursorX11(_GLFWcursor* cursor,
  2935                                const GLFWimage* image,
  2936                                int xhot, int yhot)
  2937  {
  2938      cursor->x11.handle = _glfwCreateNativeCursorX11(image, xhot, yhot);
  2939      if (!cursor->x11.handle)
  2940          return GLFW_FALSE;
  2941  
  2942      return GLFW_TRUE;
  2943  }
  2944  
  2945  GLFWbool _glfwCreateStandardCursorX11(_GLFWcursor* cursor, int shape)
  2946  {
  2947      if (_glfw.x11.xcursor.handle)
  2948      {
  2949          char* theme = XcursorGetTheme(_glfw.x11.display);
  2950          if (theme)
  2951          {
  2952              const int size = XcursorGetDefaultSize(_glfw.x11.display);
  2953              const char* name = NULL;
  2954  
  2955              switch (shape)
  2956              {
  2957                  case GLFW_ARROW_CURSOR:
  2958                      name = "default";
  2959                      break;
  2960                  case GLFW_IBEAM_CURSOR:
  2961                      name = "text";
  2962                      break;
  2963                  case GLFW_CROSSHAIR_CURSOR:
  2964                      name = "crosshair";
  2965                      break;
  2966                  case GLFW_POINTING_HAND_CURSOR:
  2967                      name = "pointer";
  2968                      break;
  2969                  case GLFW_RESIZE_EW_CURSOR:
  2970                      name = "ew-resize";
  2971                      break;
  2972                  case GLFW_RESIZE_NS_CURSOR:
  2973                      name = "ns-resize";
  2974                      break;
  2975                  case GLFW_RESIZE_NWSE_CURSOR:
  2976                      name = "nwse-resize";
  2977                      break;
  2978                  case GLFW_RESIZE_NESW_CURSOR:
  2979                      name = "nesw-resize";
  2980                      break;
  2981                  case GLFW_RESIZE_ALL_CURSOR:
  2982                      name = "all-scroll";
  2983                      break;
  2984                  case GLFW_NOT_ALLOWED_CURSOR:
  2985                      name = "not-allowed";
  2986                      break;
  2987              }
  2988  
  2989              XcursorImage* image = XcursorLibraryLoadImage(name, theme, size);
  2990              if (image)
  2991              {
  2992                  cursor->x11.handle = XcursorImageLoadCursor(_glfw.x11.display, image);
  2993                  XcursorImageDestroy(image);
  2994              }
  2995          }
  2996      }
  2997  
  2998      if (!cursor->x11.handle)
  2999      {
  3000          unsigned int native = 0;
  3001  
  3002          switch (shape)
  3003          {
  3004              case GLFW_ARROW_CURSOR:
  3005                  native = XC_left_ptr;
  3006                  break;
  3007              case GLFW_IBEAM_CURSOR:
  3008                  native = XC_xterm;
  3009                  break;
  3010              case GLFW_CROSSHAIR_CURSOR:
  3011                  native = XC_crosshair;
  3012                  break;
  3013              case GLFW_POINTING_HAND_CURSOR:
  3014                  native = XC_hand2;
  3015                  break;
  3016              case GLFW_RESIZE_EW_CURSOR:
  3017                  native = XC_sb_h_double_arrow;
  3018                  break;
  3019              case GLFW_RESIZE_NS_CURSOR:
  3020                  native = XC_sb_v_double_arrow;
  3021                  break;
  3022              case GLFW_RESIZE_ALL_CURSOR:
  3023                  native = XC_fleur;
  3024                  break;
  3025              default:
  3026                  _glfwInputError(GLFW_CURSOR_UNAVAILABLE,
  3027                                  "X11: Standard cursor shape unavailable");
  3028                  return GLFW_FALSE;
  3029          }
  3030  
  3031          cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, native);
  3032          if (!cursor->x11.handle)
  3033          {
  3034              _glfwInputError(GLFW_PLATFORM_ERROR,
  3035                              "X11: Failed to create standard cursor");
  3036              return GLFW_FALSE;
  3037          }
  3038      }
  3039  
  3040      return GLFW_TRUE;
  3041  }
  3042  
  3043  void _glfwDestroyCursorX11(_GLFWcursor* cursor)
  3044  {
  3045      if (cursor->x11.handle)
  3046          XFreeCursor(_glfw.x11.display, cursor->x11.handle);
  3047  }
  3048  
  3049  void _glfwSetCursorX11(_GLFWwindow* window, _GLFWcursor* cursor)
  3050  {
  3051      if (window->cursorMode == GLFW_CURSOR_NORMAL ||
  3052          window->cursorMode == GLFW_CURSOR_CAPTURED)
  3053      {
  3054          updateCursorImage(window);
  3055          XFlush(_glfw.x11.display);
  3056      }
  3057  }
  3058  
  3059  void _glfwSetClipboardStringX11(const char* string)
  3060  {
  3061      char* copy = _glfw_strdup(string);
  3062      _glfw_free(_glfw.x11.clipboardString);
  3063      _glfw.x11.clipboardString = copy;
  3064  
  3065      XSetSelectionOwner(_glfw.x11.display,
  3066                         _glfw.x11.CLIPBOARD,
  3067                         _glfw.x11.helperWindowHandle,
  3068                         CurrentTime);
  3069  
  3070      if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) !=
  3071          _glfw.x11.helperWindowHandle)
  3072      {
  3073          _glfwInputError(GLFW_PLATFORM_ERROR,
  3074                          "X11: Failed to become owner of clipboard selection");
  3075      }
  3076  }
  3077  
  3078  const char* _glfwGetClipboardStringX11(void)
  3079  {
  3080      return getSelectionString(_glfw.x11.CLIPBOARD);
  3081  }
  3082  
  3083  EGLenum _glfwGetEGLPlatformX11(EGLint** attribs)
  3084  {
  3085      if (_glfw.egl.ANGLE_platform_angle)
  3086      {
  3087          int type = 0;
  3088  
  3089          if (_glfw.egl.ANGLE_platform_angle_opengl)
  3090          {
  3091              if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_OPENGL)
  3092                  type = EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE;
  3093          }
  3094  
  3095          if (_glfw.egl.ANGLE_platform_angle_vulkan)
  3096          {
  3097              if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_VULKAN)
  3098                  type = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE;
  3099          }
  3100  
  3101          if (type)
  3102          {
  3103              *attribs = _glfw_calloc(5, sizeof(EGLint));
  3104              (*attribs)[0] = EGL_PLATFORM_ANGLE_TYPE_ANGLE;
  3105              (*attribs)[1] = type;
  3106              (*attribs)[2] = EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE;
  3107              (*attribs)[3] = EGL_PLATFORM_X11_EXT;
  3108              (*attribs)[4] = EGL_NONE;
  3109              return EGL_PLATFORM_ANGLE_ANGLE;
  3110          }
  3111      }
  3112  
  3113      if (_glfw.egl.EXT_platform_base && _glfw.egl.EXT_platform_x11)
  3114          return EGL_PLATFORM_X11_EXT;
  3115  
  3116      return 0;
  3117  }
  3118  
  3119  EGLNativeDisplayType _glfwGetEGLNativeDisplayX11(void)
  3120  {
  3121      return _glfw.x11.display;
  3122  }
  3123  
  3124  EGLNativeWindowType _glfwGetEGLNativeWindowX11(_GLFWwindow* window)
  3125  {
  3126      if (_glfw.egl.platform)
  3127          return &window->x11.handle;
  3128      else
  3129          return (EGLNativeWindowType) window->x11.handle;
  3130  }
  3131  
  3132  void _glfwGetRequiredInstanceExtensionsX11(char** extensions)
  3133  {
  3134      if (!_glfw.vk.KHR_surface)
  3135          return;
  3136  
  3137      if (!_glfw.vk.KHR_xcb_surface || !_glfw.x11.x11xcb.handle)
  3138      {
  3139          if (!_glfw.vk.KHR_xlib_surface)
  3140              return;
  3141      }
  3142  
  3143      extensions[0] = "VK_KHR_surface";
  3144  
  3145      // NOTE: VK_KHR_xcb_surface is preferred due to some early ICDs exposing but
  3146      //       not correctly implementing VK_KHR_xlib_surface
  3147      if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
  3148          extensions[1] = "VK_KHR_xcb_surface";
  3149      else
  3150          extensions[1] = "VK_KHR_xlib_surface";
  3151  }
  3152  
  3153  GLFWbool _glfwGetPhysicalDevicePresentationSupportX11(VkInstance instance,
  3154                                                        VkPhysicalDevice device,
  3155                                                        uint32_t queuefamily)
  3156  {
  3157      VisualID visualID = XVisualIDFromVisual(DefaultVisual(_glfw.x11.display,
  3158                                                            _glfw.x11.screen));
  3159  
  3160      if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
  3161      {
  3162          PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR
  3163              vkGetPhysicalDeviceXcbPresentationSupportKHR =
  3164              (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)
  3165              vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR");
  3166          if (!vkGetPhysicalDeviceXcbPresentationSupportKHR)
  3167          {
  3168              _glfwInputError(GLFW_API_UNAVAILABLE,
  3169                              "X11: Vulkan instance missing VK_KHR_xcb_surface extension");
  3170              return GLFW_FALSE;
  3171          }
  3172  
  3173          xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display);
  3174          if (!connection)
  3175          {
  3176              _glfwInputError(GLFW_PLATFORM_ERROR,
  3177                              "X11: Failed to retrieve XCB connection");
  3178              return GLFW_FALSE;
  3179          }
  3180  
  3181          return vkGetPhysicalDeviceXcbPresentationSupportKHR(device,
  3182                                                              queuefamily,
  3183                                                              connection,
  3184                                                              visualID);
  3185      }
  3186      else
  3187      {
  3188          PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR
  3189              vkGetPhysicalDeviceXlibPresentationSupportKHR =
  3190              (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)
  3191              vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR");
  3192          if (!vkGetPhysicalDeviceXlibPresentationSupportKHR)
  3193          {
  3194              _glfwInputError(GLFW_API_UNAVAILABLE,
  3195                              "X11: Vulkan instance missing VK_KHR_xlib_surface extension");
  3196              return GLFW_FALSE;
  3197          }
  3198  
  3199          return vkGetPhysicalDeviceXlibPresentationSupportKHR(device,
  3200                                                               queuefamily,
  3201                                                               _glfw.x11.display,
  3202                                                               visualID);
  3203      }
  3204  }
  3205  
  3206  VkResult _glfwCreateWindowSurfaceX11(VkInstance instance,
  3207                                       _GLFWwindow* window,
  3208                                       const VkAllocationCallbacks* allocator,
  3209                                       VkSurfaceKHR* surface)
  3210  {
  3211      if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
  3212      {
  3213          VkResult err;
  3214          VkXcbSurfaceCreateInfoKHR sci;
  3215          PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR;
  3216  
  3217          xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display);
  3218          if (!connection)
  3219          {
  3220              _glfwInputError(GLFW_PLATFORM_ERROR,
  3221                              "X11: Failed to retrieve XCB connection");
  3222              return VK_ERROR_EXTENSION_NOT_PRESENT;
  3223          }
  3224  
  3225          vkCreateXcbSurfaceKHR = (PFN_vkCreateXcbSurfaceKHR)
  3226              vkGetInstanceProcAddr(instance, "vkCreateXcbSurfaceKHR");
  3227          if (!vkCreateXcbSurfaceKHR)
  3228          {
  3229              _glfwInputError(GLFW_API_UNAVAILABLE,
  3230                              "X11: Vulkan instance missing VK_KHR_xcb_surface extension");
  3231              return VK_ERROR_EXTENSION_NOT_PRESENT;
  3232          }
  3233  
  3234          memset(&sci, 0, sizeof(sci));
  3235          sci.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
  3236          sci.connection = connection;
  3237          sci.window = window->x11.handle;
  3238  
  3239          err = vkCreateXcbSurfaceKHR(instance, &sci, allocator, surface);
  3240          if (err)
  3241          {
  3242              _glfwInputError(GLFW_PLATFORM_ERROR,
  3243                              "X11: Failed to create Vulkan XCB surface: %s",
  3244                              _glfwGetVulkanResultString(err));
  3245          }
  3246  
  3247          return err;
  3248      }
  3249      else
  3250      {
  3251          VkResult err;
  3252          VkXlibSurfaceCreateInfoKHR sci;
  3253          PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR;
  3254  
  3255          vkCreateXlibSurfaceKHR = (PFN_vkCreateXlibSurfaceKHR)
  3256              vkGetInstanceProcAddr(instance, "vkCreateXlibSurfaceKHR");
  3257          if (!vkCreateXlibSurfaceKHR)
  3258          {
  3259              _glfwInputError(GLFW_API_UNAVAILABLE,
  3260                              "X11: Vulkan instance missing VK_KHR_xlib_surface extension");
  3261              return VK_ERROR_EXTENSION_NOT_PRESENT;
  3262          }
  3263  
  3264          memset(&sci, 0, sizeof(sci));
  3265          sci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
  3266          sci.dpy = _glfw.x11.display;
  3267          sci.window = window->x11.handle;
  3268  
  3269          err = vkCreateXlibSurfaceKHR(instance, &sci, allocator, surface);
  3270          if (err)
  3271          {
  3272              _glfwInputError(GLFW_PLATFORM_ERROR,
  3273                              "X11: Failed to create Vulkan X11 surface: %s",
  3274                              _glfwGetVulkanResultString(err));
  3275          }
  3276  
  3277          return err;
  3278      }
  3279  }
  3280  
  3281  
  3282  //////////////////////////////////////////////////////////////////////////
  3283  //////                        GLFW native API                       //////
  3284  //////////////////////////////////////////////////////////////////////////
  3285  
  3286  GLFWAPI Display* glfwGetX11Display(void)
  3287  {
  3288      _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
  3289  
  3290      if (_glfw.platform.platformID != GLFW_PLATFORM_X11)
  3291      {
  3292          _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "X11: Platform not initialized");
  3293          return NULL;
  3294      }
  3295  
  3296      return _glfw.x11.display;
  3297  }
  3298  
  3299  GLFWAPI Window glfwGetX11Window(GLFWwindow* handle)
  3300  {
  3301      _GLFWwindow* window = (_GLFWwindow*) handle;
  3302      _GLFW_REQUIRE_INIT_OR_RETURN(None);
  3303  
  3304      if (_glfw.platform.platformID != GLFW_PLATFORM_X11)
  3305      {
  3306          _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "X11: Platform not initialized");
  3307          return None;
  3308      }
  3309  
  3310      return window->x11.handle;
  3311  }
  3312  
  3313  GLFWAPI void glfwSetX11SelectionString(const char* string)
  3314  {
  3315      _GLFW_REQUIRE_INIT();
  3316  
  3317      if (_glfw.platform.platformID != GLFW_PLATFORM_X11)
  3318      {
  3319          _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "X11: Platform not initialized");
  3320          return;
  3321      }
  3322  
  3323      _glfw_free(_glfw.x11.primarySelectionString);
  3324      _glfw.x11.primarySelectionString = _glfw_strdup(string);
  3325  
  3326      XSetSelectionOwner(_glfw.x11.display,
  3327                         _glfw.x11.PRIMARY,
  3328                         _glfw.x11.helperWindowHandle,
  3329                         CurrentTime);
  3330  
  3331      if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.PRIMARY) !=
  3332          _glfw.x11.helperWindowHandle)
  3333      {
  3334          _glfwInputError(GLFW_PLATFORM_ERROR,
  3335                          "X11: Failed to become owner of primary selection");
  3336      }
  3337  }
  3338  
  3339  GLFWAPI const char* glfwGetX11SelectionString(void)
  3340  {
  3341      _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
  3342  
  3343      if (_glfw.platform.platformID != GLFW_PLATFORM_X11)
  3344      {
  3345          _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, "X11: Platform not initialized");
  3346          return NULL;
  3347      }
  3348  
  3349      return getSelectionString(_glfw.x11.PRIMARY);
  3350  }
  3351