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>
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.
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:
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.
20 // 2. Altered source versions must be plainly marked as such, and must not
21 // be misrepresented as being the original software.
23 // 3. This notice may not be removed or altered from any source
26 //========================================================================
27 // It is fine to use C99 in this file because it will not be built with VS
28 //========================================================================
32 #include <X11/cursorfont.h>
35 #include <sys/select.h>
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
49 // Additional mouse button names for XButtonEvent
53 // Motif WM hints flags
54 #define MWM_HINTS_DECORATIONS 2
55 #define MWM_DECOR_ALL 1
57 #define _GLFW_XDND_VERSION 5
60 // Wait for data to arrive using select
61 // This avoids blocking other threads via the per-display Xlib lock that also
62 // covers GLX functions
64 static GLFWbool waitForEvent(double* timeout)
67 const int fd = ConnectionNumber(_glfw.x11.display);
70 #if defined(__linux__)
71 if (_glfw.linjs.inotify > fd)
72 count = _glfw.linjs.inotify + 1;
78 #if defined(__linux__)
79 if (_glfw.linjs.inotify > 0)
80 FD_SET(_glfw.linjs.inotify, &fds);
85 const long seconds = (long) *timeout;
86 const long microseconds = (long) ((*timeout - seconds) * 1e6);
87 struct timeval tv = { seconds, microseconds };
88 const uint64_t base = _glfwPlatformGetTimerValue();
90 const int result = select(count, &fds, NULL, NULL, &tv);
91 const int error = errno;
93 *timeout -= (_glfwPlatformGetTimerValue() - base) /
94 (double) _glfwPlatformGetTimerFrequency();
98 if ((result == -1 && error == EINTR) || *timeout <= 0.0)
101 else if (select(count, &fds, NULL, NULL, NULL) != -1 || errno != EINTR)
106 // Waits until a VisibilityNotify event arrives for the specified window or the
107 // timeout period elapses (ICCCM section 4.2.2)
109 static GLFWbool waitForVisibilityNotify(_GLFWwindow* window)
112 double timeout = 0.1;
114 while (!XCheckTypedWindowEvent(_glfw.x11.display,
119 if (!waitForEvent(&timeout))
126 // Returns whether the window is iconified
128 static int getWindowState(_GLFWwindow* window)
130 int result = WithdrawnState;
136 if (_glfwGetWindowPropertyX11(window->x11.handle,
139 (unsigned char**) &state) >= 2)
141 result = state->state;
150 // Returns whether the event is a selection event
152 static Bool isSelectionEvent(Display* display, XEvent* event, XPointer pointer)
154 if (event->xany.window != _glfw.x11.helperWindowHandle)
157 return event->type == SelectionRequest ||
158 event->type == SelectionNotify ||
159 event->type == SelectionClear;
162 // Returns whether it is a _NET_FRAME_EXTENTS event for the specified window
164 static Bool isFrameExtentsEvent(Display* display, XEvent* event, XPointer pointer)
166 _GLFWwindow* window = (_GLFWwindow*) pointer;
167 return event->type == PropertyNotify &&
168 event->xproperty.state == PropertyNewValue &&
169 event->xproperty.window == window->x11.handle &&
170 event->xproperty.atom == _glfw.x11.NET_FRAME_EXTENTS;
173 // Returns whether it is a property event for the specified selection transfer
175 static Bool isSelPropNewValueNotify(Display* display, XEvent* event, XPointer pointer)
177 XEvent* notification = (XEvent*) pointer;
178 return event->type == PropertyNotify &&
179 event->xproperty.state == PropertyNewValue &&
180 event->xproperty.window == notification->xselection.requestor &&
181 event->xproperty.atom == notification->xselection.property;
184 // Translates an X event modifier state mask
186 static int translateState(int state)
190 if (state & ShiftMask)
191 mods |= GLFW_MOD_SHIFT;
192 if (state & ControlMask)
193 mods |= GLFW_MOD_CONTROL;
194 if (state & Mod1Mask)
195 mods |= GLFW_MOD_ALT;
196 if (state & Mod4Mask)
197 mods |= GLFW_MOD_SUPER;
198 if (state & LockMask)
199 mods |= GLFW_MOD_CAPS_LOCK;
200 if (state & Mod2Mask)
201 mods |= GLFW_MOD_NUM_LOCK;
206 // Translates an X11 key code to a GLFW key token
208 static int translateKey(int scancode)
210 // Use the pre-filled LUT (see createKeyTables() in x11_init.c)
211 if (scancode < 0 || scancode > 255)
212 return GLFW_KEY_UNKNOWN;
214 return _glfw.x11.keycodes[scancode];
217 // Sends an EWMH or ICCCM event to the window manager
219 static void sendEventToWM(_GLFWwindow* window, Atom type,
220 long a, long b, long c, long d, long e)
222 XEvent event = { ClientMessage };
223 event.xclient.window = window->x11.handle;
224 event.xclient.format = 32; // Data is 32-bit longs
225 event.xclient.message_type = type;
226 event.xclient.data.l[0] = a;
227 event.xclient.data.l[1] = b;
228 event.xclient.data.l[2] = c;
229 event.xclient.data.l[3] = d;
230 event.xclient.data.l[4] = e;
232 XSendEvent(_glfw.x11.display, _glfw.x11.root,
234 SubstructureNotifyMask | SubstructureRedirectMask,
238 // Updates the normal hints according to the window settings
240 static void updateNormalHints(_GLFWwindow* window, int width, int height)
242 XSizeHints* hints = XAllocSizeHints();
244 if (!window->monitor)
246 if (window->resizable)
248 if (window->minwidth != GLFW_DONT_CARE &&
249 window->minheight != GLFW_DONT_CARE)
251 hints->flags |= PMinSize;
252 hints->min_width = window->minwidth;
253 hints->min_height = window->minheight;
256 if (window->maxwidth != GLFW_DONT_CARE &&
257 window->maxheight != GLFW_DONT_CARE)
259 hints->flags |= PMaxSize;
260 hints->max_width = window->maxwidth;
261 hints->max_height = window->maxheight;
264 if (window->numer != GLFW_DONT_CARE &&
265 window->denom != GLFW_DONT_CARE)
267 hints->flags |= PAspect;
268 hints->min_aspect.x = hints->max_aspect.x = window->numer;
269 hints->min_aspect.y = hints->max_aspect.y = window->denom;
274 hints->flags |= (PMinSize | PMaxSize);
275 hints->min_width = hints->max_width = width;
276 hints->min_height = hints->max_height = height;
280 hints->flags |= PWinGravity;
281 hints->win_gravity = StaticGravity;
283 XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints);
287 // Updates the full screen status of the window
289 static void updateWindowMode(_GLFWwindow* window)
293 if (_glfw.x11.xinerama.available &&
294 _glfw.x11.NET_WM_FULLSCREEN_MONITORS)
296 sendEventToWM(window,
297 _glfw.x11.NET_WM_FULLSCREEN_MONITORS,
298 window->monitor->x11.index,
299 window->monitor->x11.index,
300 window->monitor->x11.index,
301 window->monitor->x11.index,
305 if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN)
307 sendEventToWM(window,
308 _glfw.x11.NET_WM_STATE,
310 _glfw.x11.NET_WM_STATE_FULLSCREEN,
315 // This is the butcher's way of removing window decorations
316 // Setting the override-redirect attribute on a window makes the
317 // window manager ignore the window completely (ICCCM, section 4)
318 // The good thing is that this makes undecorated full screen windows
319 // easy to do; the bad thing is that we have to do everything
320 // manually and some things (like iconify/restore) won't work at
321 // all, as those are tasks usually performed by the window manager
323 XSetWindowAttributes attributes;
324 attributes.override_redirect = True;
325 XChangeWindowAttributes(_glfw.x11.display,
330 window->x11.overrideRedirect = GLFW_TRUE;
333 // Enable compositor bypass
334 if (!window->x11.transparent)
336 const unsigned long value = 1;
338 XChangeProperty(_glfw.x11.display, window->x11.handle,
339 _glfw.x11.NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32,
340 PropModeReplace, (unsigned char*) &value, 1);
345 if (_glfw.x11.xinerama.available &&
346 _glfw.x11.NET_WM_FULLSCREEN_MONITORS)
348 XDeleteProperty(_glfw.x11.display, window->x11.handle,
349 _glfw.x11.NET_WM_FULLSCREEN_MONITORS);
352 if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN)
354 sendEventToWM(window,
355 _glfw.x11.NET_WM_STATE,
356 _NET_WM_STATE_REMOVE,
357 _glfw.x11.NET_WM_STATE_FULLSCREEN,
362 XSetWindowAttributes attributes;
363 attributes.override_redirect = False;
364 XChangeWindowAttributes(_glfw.x11.display,
369 window->x11.overrideRedirect = GLFW_FALSE;
372 // Disable compositor bypass
373 if (!window->x11.transparent)
375 XDeleteProperty(_glfw.x11.display, window->x11.handle,
376 _glfw.x11.NET_WM_BYPASS_COMPOSITOR);
381 // Splits and translates a text/uri-list into separate file paths
382 // NOTE: This function destroys the provided string
384 static char** parseUriList(char* text, int* count)
386 const char* prefix = "file://";
392 while ((line = strtok(text, "\r\n")))
399 if (strncmp(line, prefix, strlen(prefix)) == 0)
401 line += strlen(prefix);
402 // TODO: Validate hostname
409 char* path = calloc(strlen(line) + 1, 1);
410 paths = realloc(paths, *count * sizeof(char*));
411 paths[*count - 1] = path;
415 if (line[0] == '%' && line[1] && line[2])
417 const char digits[3] = { line[1], line[2], '\0' };
418 *path = strtol(digits, NULL, 16);
432 // Encode a Unicode code point to a UTF-8 stream
433 // Based on cutef8 by Jeff Bezanson (Public Domain)
435 static size_t encodeUTF8(char* s, unsigned int ch)
440 s[count++] = (char) ch;
443 s[count++] = (ch >> 6) | 0xc0;
444 s[count++] = (ch & 0x3f) | 0x80;
446 else if (ch < 0x10000)
448 s[count++] = (ch >> 12) | 0xe0;
449 s[count++] = ((ch >> 6) & 0x3f) | 0x80;
450 s[count++] = (ch & 0x3f) | 0x80;
452 else if (ch < 0x110000)
454 s[count++] = (ch >> 18) | 0xf0;
455 s[count++] = ((ch >> 12) & 0x3f) | 0x80;
456 s[count++] = ((ch >> 6) & 0x3f) | 0x80;
457 s[count++] = (ch & 0x3f) | 0x80;
463 // Decode a Unicode code point from a UTF-8 stream
464 // Based on cutef8 by Jeff Bezanson (Public Domain)
466 static unsigned int decodeUTF8(const char** s)
468 unsigned int ch = 0, count = 0;
469 static const unsigned int offsets[] =
471 0x00000000u, 0x00003080u, 0x000e2080u,
472 0x03c82080u, 0xfa082080u, 0x82082080u
477 ch = (ch << 6) + (unsigned char) **s;
480 } while ((**s & 0xc0) == 0x80);
483 return ch - offsets[count - 1];
486 // Convert the specified Latin-1 string to UTF-8
488 static char* convertLatin1toUTF8(const char* source)
493 for (sp = source; *sp; sp++)
494 size += (*sp & 0x80) ? 2 : 1;
496 char* target = calloc(size, 1);
499 for (sp = source; *sp; sp++)
500 tp += encodeUTF8(tp, *sp);
505 // Updates the cursor image according to its cursor mode
507 static void updateCursorImage(_GLFWwindow* window)
509 if (window->cursorMode == GLFW_CURSOR_NORMAL)
513 XDefineCursor(_glfw.x11.display, window->x11.handle,
514 window->cursor->x11.handle);
517 XUndefineCursor(_glfw.x11.display, window->x11.handle);
521 XDefineCursor(_glfw.x11.display, window->x11.handle,
522 _glfw.x11.hiddenCursorHandle);
526 // Enable XI2 raw mouse motion events
528 static void enableRawMouseMotion(_GLFWwindow* window)
531 unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 };
533 em.deviceid = XIAllMasterDevices;
534 em.mask_len = sizeof(mask);
536 XISetMask(mask, XI_RawMotion);
538 XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);
541 // Disable XI2 raw mouse motion events
543 static void disableRawMouseMotion(_GLFWwindow* window)
546 unsigned char mask[] = { 0 };
548 em.deviceid = XIAllMasterDevices;
549 em.mask_len = sizeof(mask);
552 XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);
555 // Apply disabled cursor mode to a focused window
557 static void disableCursor(_GLFWwindow* window)
559 if (window->rawMouseMotion)
560 enableRawMouseMotion(window);
562 _glfw.x11.disabledCursorWindow = window;
563 _glfwPlatformGetCursorPos(window,
564 &_glfw.x11.restoreCursorPosX,
565 &_glfw.x11.restoreCursorPosY);
566 updateCursorImage(window);
567 _glfwCenterCursorInContentArea(window);
568 XGrabPointer(_glfw.x11.display, window->x11.handle, True,
569 ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
570 GrabModeAsync, GrabModeAsync,
572 _glfw.x11.hiddenCursorHandle,
576 // Exit disabled cursor mode for the specified window
578 static void enableCursor(_GLFWwindow* window)
580 if (window->rawMouseMotion)
581 disableRawMouseMotion(window);
583 _glfw.x11.disabledCursorWindow = NULL;
584 XUngrabPointer(_glfw.x11.display, CurrentTime);
585 _glfwPlatformSetCursorPos(window,
586 _glfw.x11.restoreCursorPosX,
587 _glfw.x11.restoreCursorPosY);
588 updateCursorImage(window);
591 // Clear its handle when the input context has been destroyed
593 static void inputContextDestroyCallback(XIC ic, XPointer clientData, XPointer callData)
595 _GLFWwindow* window = (_GLFWwindow*) clientData;
596 window->x11.ic = NULL;
599 // Create the X11 window (and its colormap)
601 static GLFWbool createNativeWindow(_GLFWwindow* window,
602 const _GLFWwndconfig* wndconfig,
603 Visual* visual, int depth)
605 int width = wndconfig->width;
606 int height = wndconfig->height;
608 if (wndconfig->scaleToMonitor)
610 width *= _glfw.x11.contentScaleX;
611 height *= _glfw.x11.contentScaleY;
614 // Create a colormap based on the visual used by the current context
615 window->x11.colormap = XCreateColormap(_glfw.x11.display,
620 window->x11.transparent = _glfwIsVisualTransparentX11(visual);
622 XSetWindowAttributes wa = { 0 };
623 wa.colormap = window->x11.colormap;
624 wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask |
625 PointerMotionMask | ButtonPressMask | ButtonReleaseMask |
626 ExposureMask | FocusChangeMask | VisibilityChangeMask |
627 EnterWindowMask | LeaveWindowMask | PropertyChangeMask;
629 _glfwGrabErrorHandlerX11();
631 window->x11.parent = _glfw.x11.root;
632 window->x11.handle = XCreateWindow(_glfw.x11.display,
637 depth, // Color depth
640 CWBorderPixel | CWColormap | CWEventMask,
643 _glfwReleaseErrorHandlerX11();
645 if (!window->x11.handle)
647 _glfwInputErrorX11(GLFW_PLATFORM_ERROR,
648 "X11: Failed to create window");
652 XSaveContext(_glfw.x11.display,
657 if (!wndconfig->decorated)
658 _glfwPlatformSetWindowDecorated(window, GLFW_FALSE);
660 if (_glfw.x11.NET_WM_STATE && !window->monitor)
665 if (wndconfig->floating)
667 if (_glfw.x11.NET_WM_STATE_ABOVE)
668 states[count++] = _glfw.x11.NET_WM_STATE_ABOVE;
671 if (wndconfig->maximized)
673 if (_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
674 _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
676 states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT;
677 states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ;
678 window->x11.maximized = GLFW_TRUE;
684 XChangeProperty(_glfw.x11.display, window->x11.handle,
685 _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
686 PropModeReplace, (unsigned char*) states, count);
690 // Declare the WM protocols supported by GLFW
694 _glfw.x11.WM_DELETE_WINDOW,
695 _glfw.x11.NET_WM_PING
698 XSetWMProtocols(_glfw.x11.display, window->x11.handle,
699 protocols, sizeof(protocols) / sizeof(Atom));
704 const long pid = getpid();
706 XChangeProperty(_glfw.x11.display, window->x11.handle,
707 _glfw.x11.NET_WM_PID, XA_CARDINAL, 32,
709 (unsigned char*) &pid, 1);
712 if (_glfw.x11.NET_WM_WINDOW_TYPE && _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL)
714 Atom type = _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL;
715 XChangeProperty(_glfw.x11.display, window->x11.handle,
716 _glfw.x11.NET_WM_WINDOW_TYPE, XA_ATOM, 32,
717 PropModeReplace, (unsigned char*) &type, 1);
720 // Set ICCCM WM_HINTS property
722 XWMHints* hints = XAllocWMHints();
725 _glfwInputError(GLFW_OUT_OF_MEMORY,
726 "X11: Failed to allocate WM hints");
730 hints->flags = StateHint;
731 hints->initial_state = NormalState;
733 XSetWMHints(_glfw.x11.display, window->x11.handle, hints);
737 updateNormalHints(window, width, height);
739 // Set ICCCM WM_CLASS property
741 XClassHint* hint = XAllocClassHint();
743 if (strlen(wndconfig->x11.instanceName) &&
744 strlen(wndconfig->x11.className))
746 hint->res_name = (char*) wndconfig->x11.instanceName;
747 hint->res_class = (char*) wndconfig->x11.className;
751 const char* resourceName = getenv("RESOURCE_NAME");
752 if (resourceName && strlen(resourceName))
753 hint->res_name = (char*) resourceName;
754 else if (strlen(wndconfig->title))
755 hint->res_name = (char*) wndconfig->title;
757 hint->res_name = (char*) "glfw-application";
759 if (strlen(wndconfig->title))
760 hint->res_class = (char*) wndconfig->title;
762 hint->res_class = (char*) "GLFW-Application";
765 XSetClassHint(_glfw.x11.display, window->x11.handle, hint);
769 // Announce support for Xdnd (drag and drop)
771 const Atom version = _GLFW_XDND_VERSION;
772 XChangeProperty(_glfw.x11.display, window->x11.handle,
773 _glfw.x11.XdndAware, XA_ATOM, 32,
774 PropModeReplace, (unsigned char*) &version, 1);
778 _glfwCreateInputContextX11(window);
780 _glfwPlatformSetWindowTitle(window, wndconfig->title);
781 _glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos);
782 _glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height);
787 // Set the specified property to the selection converted to the requested target
789 static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
792 char* selectionString = NULL;
793 const Atom formats[] = { _glfw.x11.UTF8_STRING, XA_STRING };
794 const int formatCount = sizeof(formats) / sizeof(formats[0]);
796 if (request->selection == _glfw.x11.PRIMARY)
797 selectionString = _glfw.x11.primarySelectionString;
799 selectionString = _glfw.x11.clipboardString;
801 if (request->property == None)
803 // The requester is a legacy client (ICCCM section 2.2)
804 // We don't support legacy clients, so fail here
808 if (request->target == _glfw.x11.TARGETS)
810 // The list of supported targets was requested
812 const Atom targets[] = { _glfw.x11.TARGETS,
814 _glfw.x11.UTF8_STRING,
817 XChangeProperty(_glfw.x11.display,
823 (unsigned char*) targets,
824 sizeof(targets) / sizeof(targets[0]));
826 return request->property;
829 if (request->target == _glfw.x11.MULTIPLE)
831 // Multiple conversions were requested
834 unsigned long i, count;
836 count = _glfwGetWindowPropertyX11(request->requestor,
839 (unsigned char**) &targets);
841 for (i = 0; i < count; i += 2)
845 for (j = 0; j < formatCount; j++)
847 if (targets[i] == formats[j])
853 XChangeProperty(_glfw.x11.display,
859 (unsigned char *) selectionString,
860 strlen(selectionString));
863 targets[i + 1] = None;
866 XChangeProperty(_glfw.x11.display,
872 (unsigned char*) targets,
877 return request->property;
880 if (request->target == _glfw.x11.SAVE_TARGETS)
882 // The request is a check whether we support SAVE_TARGETS
883 // It should be handled as a no-op side effect target
885 XChangeProperty(_glfw.x11.display,
894 return request->property;
897 // Conversion to a data target was requested
899 for (i = 0; i < formatCount; i++)
901 if (request->target == formats[i])
903 // The requested target is one we support
905 XChangeProperty(_glfw.x11.display,
911 (unsigned char *) selectionString,
912 strlen(selectionString));
914 return request->property;
918 // The requested target is not supported
923 static void handleSelectionClear(XEvent* event)
925 if (event->xselectionclear.selection == _glfw.x11.PRIMARY)
927 free(_glfw.x11.primarySelectionString);
928 _glfw.x11.primarySelectionString = NULL;
932 free(_glfw.x11.clipboardString);
933 _glfw.x11.clipboardString = NULL;
937 static void handleSelectionRequest(XEvent* event)
939 const XSelectionRequestEvent* request = &event->xselectionrequest;
941 XEvent reply = { SelectionNotify };
942 reply.xselection.property = writeTargetToProperty(request);
943 reply.xselection.display = request->display;
944 reply.xselection.requestor = request->requestor;
945 reply.xselection.selection = request->selection;
946 reply.xselection.target = request->target;
947 reply.xselection.time = request->time;
949 XSendEvent(_glfw.x11.display, request->requestor, False, 0, &reply);
952 static const char* getSelectionString(Atom selection)
954 char** selectionString = NULL;
955 const Atom targets[] = { _glfw.x11.UTF8_STRING, XA_STRING };
956 const size_t targetCount = sizeof(targets) / sizeof(targets[0]);
958 if (selection == _glfw.x11.PRIMARY)
959 selectionString = &_glfw.x11.primarySelectionString;
961 selectionString = &_glfw.x11.clipboardString;
963 if (XGetSelectionOwner(_glfw.x11.display, selection) ==
964 _glfw.x11.helperWindowHandle)
966 // Instead of doing a large number of X round-trips just to put this
967 // string into a window property and then read it back, just return it
968 return *selectionString;
971 free(*selectionString);
972 *selectionString = NULL;
974 for (size_t i = 0; i < targetCount; i++)
979 unsigned long itemCount, bytesAfter;
980 XEvent notification, dummy;
982 XConvertSelection(_glfw.x11.display,
985 _glfw.x11.GLFW_SELECTION,
986 _glfw.x11.helperWindowHandle,
989 while (!XCheckTypedWindowEvent(_glfw.x11.display,
990 _glfw.x11.helperWindowHandle,
997 if (notification.xselection.property == None)
1000 XCheckIfEvent(_glfw.x11.display,
1002 isSelPropNewValueNotify,
1003 (XPointer) ¬ification);
1005 XGetWindowProperty(_glfw.x11.display,
1006 notification.xselection.requestor,
1007 notification.xselection.property,
1016 (unsigned char**) &data);
1018 if (actualType == _glfw.x11.INCR)
1021 char* string = NULL;
1025 while (!XCheckIfEvent(_glfw.x11.display,
1027 isSelPropNewValueNotify,
1028 (XPointer) ¬ification))
1034 XGetWindowProperty(_glfw.x11.display,
1035 notification.xselection.requestor,
1036 notification.xselection.property,
1045 (unsigned char**) &data);
1050 string = realloc(string, size);
1051 string[size - itemCount - 1] = '\0';
1052 strcat(string, data);
1057 if (targets[i] == XA_STRING)
1059 *selectionString = convertLatin1toUTF8(string);
1063 *selectionString = string;
1069 else if (actualType == targets[i])
1071 if (targets[i] == XA_STRING)
1072 *selectionString = convertLatin1toUTF8(data);
1074 *selectionString = _glfw_strdup(data);
1079 if (*selectionString)
1083 if (!*selectionString)
1085 _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
1086 "X11: Failed to convert selection to string");
1089 return *selectionString;
1092 // Make the specified window and its video mode active on its monitor
1094 static void acquireMonitor(_GLFWwindow* window)
1096 if (_glfw.x11.saver.count == 0)
1098 // Remember old screen saver settings
1099 XGetScreenSaver(_glfw.x11.display,
1100 &_glfw.x11.saver.timeout,
1101 &_glfw.x11.saver.interval,
1102 &_glfw.x11.saver.blanking,
1103 &_glfw.x11.saver.exposure);
1105 // Disable screen saver
1106 XSetScreenSaver(_glfw.x11.display, 0, 0, DontPreferBlanking,
1110 if (!window->monitor->window)
1111 _glfw.x11.saver.count++;
1113 _glfwSetVideoModeX11(window->monitor, &window->videoMode);
1115 if (window->x11.overrideRedirect)
1120 // Manually position the window over its monitor
1121 _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos);
1122 _glfwPlatformGetVideoMode(window->monitor, &mode);
1124 XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
1125 xpos, ypos, mode.width, mode.height);
1128 _glfwInputMonitorWindow(window->monitor, window);
1131 // Remove the window and restore the original video mode
1133 static void releaseMonitor(_GLFWwindow* window)
1135 if (window->monitor->window != window)
1138 _glfwInputMonitorWindow(window->monitor, NULL);
1139 _glfwRestoreVideoModeX11(window->monitor);
1141 _glfw.x11.saver.count--;
1143 if (_glfw.x11.saver.count == 0)
1145 // Restore old screen saver settings
1146 XSetScreenSaver(_glfw.x11.display,
1147 _glfw.x11.saver.timeout,
1148 _glfw.x11.saver.interval,
1149 _glfw.x11.saver.blanking,
1150 _glfw.x11.saver.exposure);
1154 // Process the specified X event
1156 static void processEvent(XEvent *event)
1159 Bool filtered = False;
1161 // HACK: Save scancode as some IMs clear the field in XFilterEvent
1162 if (event->type == KeyPress || event->type == KeyRelease)
1163 keycode = event->xkey.keycode;
1165 filtered = XFilterEvent(event, None);
1167 if (_glfw.x11.randr.available)
1169 if (event->type == _glfw.x11.randr.eventBase + RRNotify)
1171 XRRUpdateConfiguration(event);
1172 _glfwPollMonitorsX11();
1177 if (_glfw.x11.xkb.available)
1179 if (event->type == _glfw.x11.xkb.eventBase + XkbEventCode)
1181 if (((XkbEvent*) event)->any.xkb_type == XkbStateNotify &&
1182 (((XkbEvent*) event)->state.changed & XkbGroupStateMask))
1184 _glfw.x11.xkb.group = ((XkbEvent*) event)->state.group;
1191 if (event->type == GenericEvent)
1193 if (_glfw.x11.xi.available)
1195 _GLFWwindow* window = _glfw.x11.disabledCursorWindow;
1198 window->rawMouseMotion &&
1199 event->xcookie.extension == _glfw.x11.xi.majorOpcode &&
1200 XGetEventData(_glfw.x11.display, &event->xcookie) &&
1201 event->xcookie.evtype == XI_RawMotion)
1203 XIRawEvent* re = event->xcookie.data;
1204 if (re->valuators.mask_len)
1206 const double* values = re->raw_values;
1207 double xpos = window->virtualCursorPosX;
1208 double ypos = window->virtualCursorPosY;
1210 if (XIMaskIsSet(re->valuators.mask, 0))
1216 if (XIMaskIsSet(re->valuators.mask, 1))
1219 _glfwInputCursorPos(window, xpos, ypos);
1223 XFreeEventData(_glfw.x11.display, &event->xcookie);
1229 if (event->type == SelectionClear)
1231 handleSelectionClear(event);
1234 else if (event->type == SelectionRequest)
1236 handleSelectionRequest(event);
1240 _GLFWwindow* window = NULL;
1241 if (XFindContext(_glfw.x11.display,
1244 (XPointer*) &window) != 0)
1246 // This is an event for a window that has already been destroyed
1250 switch (event->type)
1252 case ReparentNotify:
1254 window->x11.parent = event->xreparent.parent;
1260 const int key = translateKey(keycode);
1261 const int mods = translateState(event->xkey.state);
1262 const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT));
1266 // HACK: Do not report the key press events duplicated by XIM
1267 // Duplicate key releases are filtered out implicitly by
1268 // the GLFW key repeat logic in _glfwInputKey
1269 // A timestamp per key is used to handle simultaneous keys
1270 // NOTE: Always allow the first event for each key through
1271 // (the server never sends a timestamp of zero)
1272 // NOTE: Timestamp difference is compared to handle wrap-around
1273 Time diff = event->xkey.time - window->x11.keyPressTimes[keycode];
1274 if (diff == event->xkey.time || (diff > 0 && diff < (1 << 31)))
1277 _glfwInputKey(window, key, keycode, GLFW_PRESS, mods);
1279 window->x11.keyPressTimes[keycode] = event->xkey.time;
1287 char* chars = buffer;
1289 count = Xutf8LookupString(window->x11.ic,
1291 buffer, sizeof(buffer) - 1,
1294 if (status == XBufferOverflow)
1296 chars = calloc(count + 1, 1);
1297 count = Xutf8LookupString(window->x11.ic,
1303 if (status == XLookupChars || status == XLookupBoth)
1305 const char* c = chars;
1306 chars[count] = '\0';
1307 while (c - chars < count)
1308 _glfwInputChar(window, decodeUTF8(&c), mods, plain);
1311 if (chars != buffer)
1318 XLookupString(&event->xkey, NULL, 0, &keysym, NULL);
1320 _glfwInputKey(window, key, keycode, GLFW_PRESS, mods);
1322 const long character = _glfwKeySym2Unicode(keysym);
1323 if (character != -1)
1324 _glfwInputChar(window, character, mods, plain);
1332 const int key = translateKey(keycode);
1333 const int mods = translateState(event->xkey.state);
1335 if (!_glfw.x11.xkb.detectable)
1337 // HACK: Key repeat events will arrive as KeyRelease/KeyPress
1338 // pairs with similar or identical time stamps
1339 // The key repeat logic in _glfwInputKey expects only key
1340 // presses to repeat, so detect and discard release events
1341 if (XEventsQueued(_glfw.x11.display, QueuedAfterReading))
1344 XPeekEvent(_glfw.x11.display, &next);
1346 if (next.type == KeyPress &&
1347 next.xkey.window == event->xkey.window &&
1348 next.xkey.keycode == keycode)
1350 // HACK: The time of repeat events sometimes doesn't
1351 // match that of the press event, so add an
1353 // Toshiyuki Takahashi can press a button
1354 // 16 times per second so it's fairly safe to
1355 // assume that no human is pressing the key 50
1356 // times per second (value is ms)
1357 if ((next.xkey.time - event->xkey.time) < 20)
1359 // This is very likely a server-generated key repeat
1360 // event, so ignore it
1367 _glfwInputKey(window, key, keycode, GLFW_RELEASE, mods);
1373 const int mods = translateState(event->xbutton.state);
1375 if (event->xbutton.button == Button1)
1376 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, mods);
1377 else if (event->xbutton.button == Button2)
1378 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, mods);
1379 else if (event->xbutton.button == Button3)
1380 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS, mods);
1382 // Modern X provides scroll events as mouse button presses
1383 else if (event->xbutton.button == Button4)
1384 _glfwInputScroll(window, 0.0, 1.0);
1385 else if (event->xbutton.button == Button5)
1386 _glfwInputScroll(window, 0.0, -1.0);
1387 else if (event->xbutton.button == Button6)
1388 _glfwInputScroll(window, 1.0, 0.0);
1389 else if (event->xbutton.button == Button7)
1390 _glfwInputScroll(window, -1.0, 0.0);
1394 // Additional buttons after 7 are treated as regular buttons
1395 // We subtract 4 to fill the gap left by scroll input above
1396 _glfwInputMouseClick(window,
1397 event->xbutton.button - Button1 - 4,
1407 const int mods = translateState(event->xbutton.state);
1409 if (event->xbutton.button == Button1)
1411 _glfwInputMouseClick(window,
1412 GLFW_MOUSE_BUTTON_LEFT,
1416 else if (event->xbutton.button == Button2)
1418 _glfwInputMouseClick(window,
1419 GLFW_MOUSE_BUTTON_MIDDLE,
1423 else if (event->xbutton.button == Button3)
1425 _glfwInputMouseClick(window,
1426 GLFW_MOUSE_BUTTON_RIGHT,
1430 else if (event->xbutton.button > Button7)
1432 // Additional buttons after 7 are treated as regular buttons
1433 // We subtract 4 to fill the gap left by scroll input above
1434 _glfwInputMouseClick(window,
1435 event->xbutton.button - Button1 - 4,
1445 // XEnterWindowEvent is XCrossingEvent
1446 const int x = event->xcrossing.x;
1447 const int y = event->xcrossing.y;
1449 // HACK: This is a workaround for WMs (KWM, Fluxbox) that otherwise
1450 // ignore the defined cursor for hidden cursor mode
1451 if (window->cursorMode == GLFW_CURSOR_HIDDEN)
1452 updateCursorImage(window);
1454 _glfwInputCursorEnter(window, GLFW_TRUE);
1455 _glfwInputCursorPos(window, x, y);
1457 window->x11.lastCursorPosX = x;
1458 window->x11.lastCursorPosY = y;
1464 _glfwInputCursorEnter(window, GLFW_FALSE);
1470 const int x = event->xmotion.x;
1471 const int y = event->xmotion.y;
1473 if (x != window->x11.warpCursorPosX ||
1474 y != window->x11.warpCursorPosY)
1476 // The cursor was moved by something other than GLFW
1478 if (window->cursorMode == GLFW_CURSOR_DISABLED)
1480 if (_glfw.x11.disabledCursorWindow != window)
1482 if (window->rawMouseMotion)
1485 const int dx = x - window->x11.lastCursorPosX;
1486 const int dy = y - window->x11.lastCursorPosY;
1488 _glfwInputCursorPos(window,
1489 window->virtualCursorPosX + dx,
1490 window->virtualCursorPosY + dy);
1493 _glfwInputCursorPos(window, x, y);
1496 window->x11.lastCursorPosX = x;
1497 window->x11.lastCursorPosY = y;
1501 case ConfigureNotify:
1503 if (event->xconfigure.width != window->x11.width ||
1504 event->xconfigure.height != window->x11.height)
1506 _glfwInputFramebufferSize(window,
1507 event->xconfigure.width,
1508 event->xconfigure.height);
1510 _glfwInputWindowSize(window,
1511 event->xconfigure.width,
1512 event->xconfigure.height);
1514 window->x11.width = event->xconfigure.width;
1515 window->x11.height = event->xconfigure.height;
1518 int xpos = event->xconfigure.x;
1519 int ypos = event->xconfigure.y;
1521 // NOTE: ConfigureNotify events from the server are in local
1522 // coordinates, so if we are reparented we need to translate
1523 // the position into root (screen) coordinates
1524 if (!event->xany.send_event && window->x11.parent != _glfw.x11.root)
1526 _glfwGrabErrorHandlerX11();
1529 XTranslateCoordinates(_glfw.x11.display,
1536 _glfwReleaseErrorHandlerX11();
1537 if (_glfw.x11.errorCode == BadWindow)
1541 if (xpos != window->x11.xpos || ypos != window->x11.ypos)
1543 _glfwInputWindowPos(window, xpos, ypos);
1544 window->x11.xpos = xpos;
1545 window->x11.ypos = ypos;
1553 // Custom client message, probably from the window manager
1558 if (event->xclient.message_type == None)
1561 if (event->xclient.message_type == _glfw.x11.WM_PROTOCOLS)
1563 const Atom protocol = event->xclient.data.l[0];
1564 if (protocol == None)
1567 if (protocol == _glfw.x11.WM_DELETE_WINDOW)
1569 // The window manager was asked to close the window, for
1570 // example by the user pressing a 'close' window decoration
1572 _glfwInputWindowCloseRequest(window);
1574 else if (protocol == _glfw.x11.NET_WM_PING)
1576 // The window manager is pinging the application to ensure
1577 // it's still responding to events
1579 XEvent reply = *event;
1580 reply.xclient.window = _glfw.x11.root;
1582 XSendEvent(_glfw.x11.display, _glfw.x11.root,
1584 SubstructureNotifyMask | SubstructureRedirectMask,
1588 else if (event->xclient.message_type == _glfw.x11.XdndEnter)
1590 // A drag operation has entered the window
1591 unsigned long i, count;
1592 Atom* formats = NULL;
1593 const GLFWbool list = event->xclient.data.l[1] & 1;
1595 _glfw.x11.xdnd.source = event->xclient.data.l[0];
1596 _glfw.x11.xdnd.version = event->xclient.data.l[1] >> 24;
1597 _glfw.x11.xdnd.format = None;
1599 if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
1604 count = _glfwGetWindowPropertyX11(_glfw.x11.xdnd.source,
1605 _glfw.x11.XdndTypeList,
1607 (unsigned char**) &formats);
1612 formats = (Atom*) event->xclient.data.l + 2;
1615 for (i = 0; i < count; i++)
1617 if (formats[i] == _glfw.x11.text_uri_list)
1619 _glfw.x11.xdnd.format = _glfw.x11.text_uri_list;
1624 if (list && formats)
1627 else if (event->xclient.message_type == _glfw.x11.XdndDrop)
1629 // The drag operation has finished by dropping on the window
1630 Time time = CurrentTime;
1632 if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
1635 if (_glfw.x11.xdnd.format)
1637 if (_glfw.x11.xdnd.version >= 1)
1638 time = event->xclient.data.l[2];
1640 // Request the chosen format from the source window
1641 XConvertSelection(_glfw.x11.display,
1642 _glfw.x11.XdndSelection,
1643 _glfw.x11.xdnd.format,
1644 _glfw.x11.XdndSelection,
1648 else if (_glfw.x11.xdnd.version >= 2)
1650 XEvent reply = { ClientMessage };
1651 reply.xclient.window = _glfw.x11.xdnd.source;
1652 reply.xclient.message_type = _glfw.x11.XdndFinished;
1653 reply.xclient.format = 32;
1654 reply.xclient.data.l[0] = window->x11.handle;
1655 reply.xclient.data.l[1] = 0; // The drag was rejected
1656 reply.xclient.data.l[2] = None;
1658 XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
1659 False, NoEventMask, &reply);
1660 XFlush(_glfw.x11.display);
1663 else if (event->xclient.message_type == _glfw.x11.XdndPosition)
1665 // The drag operation has moved over the window
1666 const int xabs = (event->xclient.data.l[2] >> 16) & 0xffff;
1667 const int yabs = (event->xclient.data.l[2]) & 0xffff;
1671 if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
1674 XTranslateCoordinates(_glfw.x11.display,
1681 _glfwInputCursorPos(window, xpos, ypos);
1683 XEvent reply = { ClientMessage };
1684 reply.xclient.window = _glfw.x11.xdnd.source;
1685 reply.xclient.message_type = _glfw.x11.XdndStatus;
1686 reply.xclient.format = 32;
1687 reply.xclient.data.l[0] = window->x11.handle;
1688 reply.xclient.data.l[2] = 0; // Specify an empty rectangle
1689 reply.xclient.data.l[3] = 0;
1691 if (_glfw.x11.xdnd.format)
1693 // Reply that we are ready to copy the dragged data
1694 reply.xclient.data.l[1] = 1; // Accept with no rectangle
1695 if (_glfw.x11.xdnd.version >= 2)
1696 reply.xclient.data.l[4] = _glfw.x11.XdndActionCopy;
1699 XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
1700 False, NoEventMask, &reply);
1701 XFlush(_glfw.x11.display);
1707 case SelectionNotify:
1709 if (event->xselection.property == _glfw.x11.XdndSelection)
1711 // The converted data from the drag operation has arrived
1713 const unsigned long result =
1714 _glfwGetWindowPropertyX11(event->xselection.requestor,
1715 event->xselection.property,
1716 event->xselection.target,
1717 (unsigned char**) &data);
1722 char** paths = parseUriList(data, &count);
1724 _glfwInputDrop(window, count, (const char**) paths);
1726 for (i = 0; i < count; i++)
1734 if (_glfw.x11.xdnd.version >= 2)
1736 XEvent reply = { ClientMessage };
1737 reply.xclient.window = _glfw.x11.xdnd.source;
1738 reply.xclient.message_type = _glfw.x11.XdndFinished;
1739 reply.xclient.format = 32;
1740 reply.xclient.data.l[0] = window->x11.handle;
1741 reply.xclient.data.l[1] = result;
1742 reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy;
1744 XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
1745 False, NoEventMask, &reply);
1746 XFlush(_glfw.x11.display);
1755 if (event->xfocus.mode == NotifyGrab ||
1756 event->xfocus.mode == NotifyUngrab)
1758 // Ignore focus events from popup indicator windows, window menu
1759 // key chords and window dragging
1763 if (window->cursorMode == GLFW_CURSOR_DISABLED)
1764 disableCursor(window);
1767 XSetICFocus(window->x11.ic);
1769 _glfwInputWindowFocus(window, GLFW_TRUE);
1775 if (event->xfocus.mode == NotifyGrab ||
1776 event->xfocus.mode == NotifyUngrab)
1778 // Ignore focus events from popup indicator windows, window menu
1779 // key chords and window dragging
1783 if (window->cursorMode == GLFW_CURSOR_DISABLED)
1784 enableCursor(window);
1787 XUnsetICFocus(window->x11.ic);
1789 if (window->monitor && window->autoIconify)
1790 _glfwPlatformIconifyWindow(window);
1792 _glfwInputWindowFocus(window, GLFW_FALSE);
1798 _glfwInputWindowDamage(window);
1802 case PropertyNotify:
1804 if (event->xproperty.state != PropertyNewValue)
1807 if (event->xproperty.atom == _glfw.x11.WM_STATE)
1809 const int state = getWindowState(window);
1810 if (state != IconicState && state != NormalState)
1813 const GLFWbool iconified = (state == IconicState);
1814 if (window->x11.iconified != iconified)
1816 if (window->monitor)
1819 releaseMonitor(window);
1821 acquireMonitor(window);
1824 window->x11.iconified = iconified;
1825 _glfwInputWindowIconify(window, iconified);
1828 else if (event->xproperty.atom == _glfw.x11.NET_WM_STATE)
1830 const GLFWbool maximized = _glfwPlatformWindowMaximized(window);
1831 if (window->x11.maximized != maximized)
1833 window->x11.maximized = maximized;
1834 _glfwInputWindowMaximize(window, maximized);
1847 //////////////////////////////////////////////////////////////////////////
1848 ////// GLFW internal API //////
1849 //////////////////////////////////////////////////////////////////////////
1851 // Retrieve a single window property of the specified type
1852 // Inspired by fghGetWindowProperty from freeglut
1854 unsigned long _glfwGetWindowPropertyX11(Window window,
1857 unsigned char** value)
1861 unsigned long itemCount, bytesAfter;
1863 XGetWindowProperty(_glfw.x11.display,
1879 GLFWbool _glfwIsVisualTransparentX11(Visual* visual)
1881 if (!_glfw.x11.xrender.available)
1884 XRenderPictFormat* pf = XRenderFindVisualFormat(_glfw.x11.display, visual);
1885 return pf && pf->direct.alphaMask;
1888 // Push contents of our selection to clipboard manager
1890 void _glfwPushSelectionToManagerX11(void)
1892 XConvertSelection(_glfw.x11.display,
1893 _glfw.x11.CLIPBOARD_MANAGER,
1894 _glfw.x11.SAVE_TARGETS,
1896 _glfw.x11.helperWindowHandle,
1903 while (XCheckIfEvent(_glfw.x11.display, &event, isSelectionEvent, NULL))
1907 case SelectionRequest:
1908 handleSelectionRequest(&event);
1911 case SelectionClear:
1912 handleSelectionClear(&event);
1915 case SelectionNotify:
1917 if (event.xselection.target == _glfw.x11.SAVE_TARGETS)
1919 // This means one of two things; either the selection
1920 // was not owned, which means there is no clipboard
1921 // manager, or the transfer to the clipboard manager has
1923 // In either case, it means we are done here
1936 void _glfwCreateInputContextX11(_GLFWwindow* window)
1938 XIMCallback callback;
1939 callback.callback = (XIMProc) inputContextDestroyCallback;
1940 callback.client_data = (XPointer) window;
1942 window->x11.ic = XCreateIC(_glfw.x11.im,
1944 XIMPreeditNothing | XIMStatusNothing,
1955 XWindowAttributes attribs;
1956 XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &attribs);
1958 unsigned long filter = 0;
1959 if (XGetICValues(window->x11.ic, XNFilterEvents, &filter, NULL) == NULL)
1961 XSelectInput(_glfw.x11.display,
1963 attribs.your_event_mask | filter);
1969 //////////////////////////////////////////////////////////////////////////
1970 ////// GLFW platform API //////
1971 //////////////////////////////////////////////////////////////////////////
1973 int _glfwPlatformCreateWindow(_GLFWwindow* window,
1974 const _GLFWwndconfig* wndconfig,
1975 const _GLFWctxconfig* ctxconfig,
1976 const _GLFWfbconfig* fbconfig)
1978 Visual* visual = NULL;
1981 if (ctxconfig->client != GLFW_NO_API)
1983 if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
1985 if (!_glfwInitGLX())
1987 if (!_glfwChooseVisualGLX(wndconfig, ctxconfig, fbconfig, &visual, &depth))
1990 else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
1992 if (!_glfwInitEGL())
1994 if (!_glfwChooseVisualEGL(wndconfig, ctxconfig, fbconfig, &visual, &depth))
1997 else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
1999 if (!_glfwInitOSMesa())
2006 visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen);
2007 depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen);
2010 if (!createNativeWindow(window, wndconfig, visual, depth))
2013 if (ctxconfig->client != GLFW_NO_API)
2015 if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
2017 if (!_glfwCreateContextGLX(window, ctxconfig, fbconfig))
2020 else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
2022 if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
2025 else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
2027 if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
2032 if (window->monitor)
2034 _glfwPlatformShowWindow(window);
2035 updateWindowMode(window);
2036 acquireMonitor(window);
2039 XFlush(_glfw.x11.display);
2043 void _glfwPlatformDestroyWindow(_GLFWwindow* window)
2045 if (_glfw.x11.disabledCursorWindow == window)
2046 _glfw.x11.disabledCursorWindow = NULL;
2048 if (window->monitor)
2049 releaseMonitor(window);
2053 XDestroyIC(window->x11.ic);
2054 window->x11.ic = NULL;
2057 if (window->context.destroy)
2058 window->context.destroy(window);
2060 if (window->x11.handle)
2062 XDeleteContext(_glfw.x11.display, window->x11.handle, _glfw.x11.context);
2063 XUnmapWindow(_glfw.x11.display, window->x11.handle);
2064 XDestroyWindow(_glfw.x11.display, window->x11.handle);
2065 window->x11.handle = (Window) 0;
2068 if (window->x11.colormap)
2070 XFreeColormap(_glfw.x11.display, window->x11.colormap);
2071 window->x11.colormap = (Colormap) 0;
2074 XFlush(_glfw.x11.display);
2077 void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
2079 if (_glfw.x11.xlib.utf8)
2081 Xutf8SetWMProperties(_glfw.x11.display,
2088 XChangeProperty(_glfw.x11.display, window->x11.handle,
2089 _glfw.x11.NET_WM_NAME, _glfw.x11.UTF8_STRING, 8,
2091 (unsigned char*) title, strlen(title));
2093 XChangeProperty(_glfw.x11.display, window->x11.handle,
2094 _glfw.x11.NET_WM_ICON_NAME, _glfw.x11.UTF8_STRING, 8,
2096 (unsigned char*) title, strlen(title));
2098 XFlush(_glfw.x11.display);
2101 void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
2102 int count, const GLFWimage* images)
2106 int i, j, longCount = 0;
2108 for (i = 0; i < count; i++)
2109 longCount += 2 + images[i].width * images[i].height;
2111 long* icon = calloc(longCount, sizeof(long));
2112 long* target = icon;
2114 for (i = 0; i < count; i++)
2116 *target++ = images[i].width;
2117 *target++ = images[i].height;
2119 for (j = 0; j < images[i].width * images[i].height; j++)
2121 *target++ = (images[i].pixels[j * 4 + 0] << 16) |
2122 (images[i].pixels[j * 4 + 1] << 8) |
2123 (images[i].pixels[j * 4 + 2] << 0) |
2124 (images[i].pixels[j * 4 + 3] << 24);
2128 XChangeProperty(_glfw.x11.display, window->x11.handle,
2129 _glfw.x11.NET_WM_ICON,
2132 (unsigned char*) icon,
2139 XDeleteProperty(_glfw.x11.display, window->x11.handle,
2140 _glfw.x11.NET_WM_ICON);
2143 XFlush(_glfw.x11.display);
2146 void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
2151 XTranslateCoordinates(_glfw.x11.display, window->x11.handle, _glfw.x11.root,
2152 0, 0, &x, &y, &dummy);
2160 void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos)
2162 // HACK: Explicitly setting PPosition to any value causes some WMs, notably
2163 // Compiz and Metacity, to honor the position of unmapped windows
2164 if (!_glfwPlatformWindowVisible(window))
2167 XSizeHints* hints = XAllocSizeHints();
2169 if (XGetWMNormalHints(_glfw.x11.display, window->x11.handle, hints, &supplied))
2171 hints->flags |= PPosition;
2172 hints->x = hints->y = 0;
2174 XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints);
2180 XMoveWindow(_glfw.x11.display, window->x11.handle, xpos, ypos);
2181 XFlush(_glfw.x11.display);
2184 void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
2186 XWindowAttributes attribs;
2187 XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &attribs);
2190 *width = attribs.width;
2192 *height = attribs.height;
2195 void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
2197 if (window->monitor)
2199 if (window->monitor->window == window)
2200 acquireMonitor(window);
2204 if (!window->resizable)
2205 updateNormalHints(window, width, height);
2207 XResizeWindow(_glfw.x11.display, window->x11.handle, width, height);
2210 XFlush(_glfw.x11.display);
2213 void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
2214 int minwidth, int minheight,
2215 int maxwidth, int maxheight)
2218 _glfwPlatformGetWindowSize(window, &width, &height);
2219 updateNormalHints(window, width, height);
2220 XFlush(_glfw.x11.display);
2223 void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom)
2226 _glfwPlatformGetWindowSize(window, &width, &height);
2227 updateNormalHints(window, width, height);
2228 XFlush(_glfw.x11.display);
2231 void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height)
2233 _glfwPlatformGetWindowSize(window, width, height);
2236 void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
2237 int* left, int* top,
2238 int* right, int* bottom)
2240 long* extents = NULL;
2242 if (window->monitor || !window->decorated)
2245 if (_glfw.x11.NET_FRAME_EXTENTS == None)
2248 if (!_glfwPlatformWindowVisible(window) &&
2249 _glfw.x11.NET_REQUEST_FRAME_EXTENTS)
2252 double timeout = 0.5;
2254 // Ensure _NET_FRAME_EXTENTS is set, allowing glfwGetWindowFrameSize to
2255 // function before the window is mapped
2256 sendEventToWM(window, _glfw.x11.NET_REQUEST_FRAME_EXTENTS,
2259 // HACK: Use a timeout because earlier versions of some window managers
2260 // (at least Unity, Fluxbox and Xfwm) failed to send the reply
2261 // They have been fixed but broken versions are still in the wild
2262 // If you are affected by this and your window manager is NOT
2263 // listed above, PLEASE report it to their and our issue trackers
2264 while (!XCheckIfEvent(_glfw.x11.display,
2266 isFrameExtentsEvent,
2269 if (!waitForEvent(&timeout))
2271 _glfwInputError(GLFW_PLATFORM_ERROR,
2272 "X11: The window manager has a broken _NET_REQUEST_FRAME_EXTENTS implementation; please report this issue");
2278 if (_glfwGetWindowPropertyX11(window->x11.handle,
2279 _glfw.x11.NET_FRAME_EXTENTS,
2281 (unsigned char**) &extents) == 4)
2288 *right = extents[1];
2290 *bottom = extents[3];
2297 void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
2298 float* xscale, float* yscale)
2301 *xscale = _glfw.x11.contentScaleX;
2303 *yscale = _glfw.x11.contentScaleY;
2306 void _glfwPlatformIconifyWindow(_GLFWwindow* window)
2308 if (window->x11.overrideRedirect)
2310 // Override-redirect windows cannot be iconified or restored, as those
2311 // tasks are performed by the window manager
2312 _glfwInputError(GLFW_PLATFORM_ERROR,
2313 "X11: Iconification of full screen windows requires a WM that supports EWMH full screen");
2317 XIconifyWindow(_glfw.x11.display, window->x11.handle, _glfw.x11.screen);
2318 XFlush(_glfw.x11.display);
2321 void _glfwPlatformRestoreWindow(_GLFWwindow* window)
2323 if (window->x11.overrideRedirect)
2325 // Override-redirect windows cannot be iconified or restored, as those
2326 // tasks are performed by the window manager
2327 _glfwInputError(GLFW_PLATFORM_ERROR,
2328 "X11: Iconification of full screen windows requires a WM that supports EWMH full screen");
2332 if (_glfwPlatformWindowIconified(window))
2334 XMapWindow(_glfw.x11.display, window->x11.handle);
2335 waitForVisibilityNotify(window);
2337 else if (_glfwPlatformWindowVisible(window))
2339 if (_glfw.x11.NET_WM_STATE &&
2340 _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
2341 _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
2343 sendEventToWM(window,
2344 _glfw.x11.NET_WM_STATE,
2345 _NET_WM_STATE_REMOVE,
2346 _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
2347 _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
2352 XFlush(_glfw.x11.display);
2355 void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
2357 if (!_glfw.x11.NET_WM_STATE ||
2358 !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
2359 !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
2364 if (_glfwPlatformWindowVisible(window))
2366 sendEventToWM(window,
2367 _glfw.x11.NET_WM_STATE,
2369 _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
2370 _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
2375 Atom* states = NULL;
2376 unsigned long count =
2377 _glfwGetWindowPropertyX11(window->x11.handle,
2378 _glfw.x11.NET_WM_STATE,
2380 (unsigned char**) &states);
2382 // NOTE: We don't check for failure as this property may not exist yet
2383 // and that's fine (and we'll create it implicitly with append)
2387 _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
2388 _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ
2390 unsigned long missingCount = 2;
2392 for (unsigned long i = 0; i < count; i++)
2394 for (unsigned long j = 0; j < missingCount; j++)
2396 if (states[i] == missing[j])
2398 missing[j] = missing[missingCount - 1];
2410 XChangeProperty(_glfw.x11.display, window->x11.handle,
2411 _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
2413 (unsigned char*) missing,
2417 XFlush(_glfw.x11.display);
2420 void _glfwPlatformShowWindow(_GLFWwindow* window)
2422 if (_glfwPlatformWindowVisible(window))
2425 XMapWindow(_glfw.x11.display, window->x11.handle);
2426 waitForVisibilityNotify(window);
2429 void _glfwPlatformHideWindow(_GLFWwindow* window)
2431 XUnmapWindow(_glfw.x11.display, window->x11.handle);
2432 XFlush(_glfw.x11.display);
2435 void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
2437 if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION)
2440 sendEventToWM(window,
2441 _glfw.x11.NET_WM_STATE,
2443 _glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION,
2447 void _glfwPlatformFocusWindow(_GLFWwindow* window)
2449 if (_glfw.x11.NET_ACTIVE_WINDOW)
2450 sendEventToWM(window, _glfw.x11.NET_ACTIVE_WINDOW, 1, 0, 0, 0, 0);
2451 else if (_glfwPlatformWindowVisible(window))
2453 XRaiseWindow(_glfw.x11.display, window->x11.handle);
2454 XSetInputFocus(_glfw.x11.display, window->x11.handle,
2455 RevertToParent, CurrentTime);
2458 XFlush(_glfw.x11.display);
2461 void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
2462 _GLFWmonitor* monitor,
2464 int width, int height,
2467 if (window->monitor == monitor)
2471 if (monitor->window == window)
2472 acquireMonitor(window);
2476 if (!window->resizable)
2477 updateNormalHints(window, width, height);
2479 XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
2480 xpos, ypos, width, height);
2483 XFlush(_glfw.x11.display);
2487 if (window->monitor)
2488 releaseMonitor(window);
2490 _glfwInputWindowMonitor(window, monitor);
2491 updateNormalHints(window, width, height);
2493 if (window->monitor)
2495 if (!_glfwPlatformWindowVisible(window))
2497 XMapRaised(_glfw.x11.display, window->x11.handle);
2498 waitForVisibilityNotify(window);
2501 updateWindowMode(window);
2502 acquireMonitor(window);
2506 updateWindowMode(window);
2507 XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
2508 xpos, ypos, width, height);
2511 XFlush(_glfw.x11.display);
2514 int _glfwPlatformWindowFocused(_GLFWwindow* window)
2519 XGetInputFocus(_glfw.x11.display, &focused, &state);
2520 return window->x11.handle == focused;
2523 int _glfwPlatformWindowIconified(_GLFWwindow* window)
2525 return getWindowState(window) == IconicState;
2528 int _glfwPlatformWindowVisible(_GLFWwindow* window)
2530 XWindowAttributes wa;
2531 XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &wa);
2532 return wa.map_state == IsViewable;
2535 int _glfwPlatformWindowMaximized(_GLFWwindow* window)
2539 GLFWbool maximized = GLFW_FALSE;
2541 if (!_glfw.x11.NET_WM_STATE ||
2542 !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
2543 !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
2548 const unsigned long count =
2549 _glfwGetWindowPropertyX11(window->x11.handle,
2550 _glfw.x11.NET_WM_STATE,
2552 (unsigned char**) &states);
2554 for (i = 0; i < count; i++)
2556 if (states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
2557 states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
2559 maximized = GLFW_TRUE;
2570 int _glfwPlatformWindowHovered(_GLFWwindow* window)
2572 Window w = _glfw.x11.root;
2576 int rootX, rootY, childX, childY;
2579 _glfwGrabErrorHandlerX11();
2581 const Bool result = XQueryPointer(_glfw.x11.display, w,
2582 &root, &w, &rootX, &rootY,
2583 &childX, &childY, &mask);
2585 _glfwReleaseErrorHandlerX11();
2587 if (_glfw.x11.errorCode == BadWindow)
2591 else if (w == window->x11.handle)
2598 int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
2600 if (!window->x11.transparent)
2603 return XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx) != None;
2606 void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
2609 _glfwPlatformGetWindowSize(window, &width, &height);
2610 updateNormalHints(window, width, height);
2613 void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled)
2617 unsigned long flags;
2618 unsigned long functions;
2619 unsigned long decorations;
2621 unsigned long status;
2624 hints.flags = MWM_HINTS_DECORATIONS;
2625 hints.decorations = enabled ? MWM_DECOR_ALL : 0;
2627 XChangeProperty(_glfw.x11.display, window->x11.handle,
2628 _glfw.x11.MOTIF_WM_HINTS,
2629 _glfw.x11.MOTIF_WM_HINTS, 32,
2631 (unsigned char*) &hints,
2632 sizeof(hints) / sizeof(long));
2635 void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled)
2637 if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_ABOVE)
2640 if (_glfwPlatformWindowVisible(window))
2642 const long action = enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
2643 sendEventToWM(window,
2644 _glfw.x11.NET_WM_STATE,
2646 _glfw.x11.NET_WM_STATE_ABOVE,
2651 Atom* states = NULL;
2652 unsigned long i, count;
2654 count = _glfwGetWindowPropertyX11(window->x11.handle,
2655 _glfw.x11.NET_WM_STATE,
2657 (unsigned char**) &states);
2659 // NOTE: We don't check for failure as this property may not exist yet
2660 // and that's fine (and we'll create it implicitly with append)
2664 for (i = 0; i < count; i++)
2666 if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE)
2673 XChangeProperty(_glfw.x11.display, window->x11.handle,
2674 _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
2676 (unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE,
2681 for (i = 0; i < count; i++)
2683 if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE)
2690 states[i] = states[count - 1];
2693 XChangeProperty(_glfw.x11.display, window->x11.handle,
2694 _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
2695 PropModeReplace, (unsigned char*) states, count);
2702 XFlush(_glfw.x11.display);
2705 void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, GLFWbool enabled)
2707 if (!_glfw.x11.xshape.available)
2712 Region region = XCreateRegion();
2713 XShapeCombineRegion(_glfw.x11.display, window->x11.handle,
2714 ShapeInput, 0, 0, region, ShapeSet);
2715 XDestroyRegion(region);
2719 XShapeCombineMask(_glfw.x11.display, window->x11.handle,
2720 ShapeInput, 0, 0, None, ShapeSet);
2724 float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
2726 float opacity = 1.f;
2728 if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx))
2730 CARD32* value = NULL;
2732 if (_glfwGetWindowPropertyX11(window->x11.handle,
2733 _glfw.x11.NET_WM_WINDOW_OPACITY,
2735 (unsigned char**) &value))
2737 opacity = (float) (*value / (double) 0xffffffffu);
2747 void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
2749 const CARD32 value = (CARD32) (0xffffffffu * (double) opacity);
2750 XChangeProperty(_glfw.x11.display, window->x11.handle,
2751 _glfw.x11.NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32,
2752 PropModeReplace, (unsigned char*) &value, 1);
2755 void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled)
2757 if (!_glfw.x11.xi.available)
2760 if (_glfw.x11.disabledCursorWindow != window)
2764 enableRawMouseMotion(window);
2766 disableRawMouseMotion(window);
2769 GLFWbool _glfwPlatformRawMouseMotionSupported(void)
2771 return _glfw.x11.xi.available;
2774 void _glfwPlatformPollEvents(void)
2776 _GLFWwindow* window;
2778 #if defined(__linux__)
2779 if (_glfw.joysticksInitialized)
2780 _glfwDetectJoystickConnectionLinux();
2782 XPending(_glfw.x11.display);
2784 while (QLength(_glfw.x11.display))
2787 XNextEvent(_glfw.x11.display, &event);
2788 processEvent(&event);
2791 window = _glfw.x11.disabledCursorWindow;
2795 _glfwPlatformGetWindowSize(window, &width, &height);
2797 // NOTE: Re-center the cursor only if it has moved since the last call,
2798 // to avoid breaking glfwWaitEvents with MotionNotify
2799 if (window->x11.lastCursorPosX != width / 2 ||
2800 window->x11.lastCursorPosY != height / 2)
2802 _glfwPlatformSetCursorPos(window, width / 2, height / 2);
2806 XFlush(_glfw.x11.display);
2809 void _glfwPlatformWaitEvents(void)
2811 while (!XPending(_glfw.x11.display))
2814 _glfwPlatformPollEvents();
2817 void _glfwPlatformWaitEventsTimeout(double timeout)
2819 while (!XPending(_glfw.x11.display))
2821 if (!waitForEvent(&timeout))
2825 _glfwPlatformPollEvents();
2828 void _glfwPlatformPostEmptyEvent(void)
2830 XEvent event = { ClientMessage };
2831 event.xclient.window = _glfw.x11.helperWindowHandle;
2832 event.xclient.format = 32; // Data is 32-bit longs
2833 event.xclient.message_type = _glfw.x11.NULL_;
2835 XSendEvent(_glfw.x11.display, _glfw.x11.helperWindowHandle, False, 0, &event);
2836 XFlush(_glfw.x11.display);
2839 void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
2842 int rootX, rootY, childX, childY;
2845 XQueryPointer(_glfw.x11.display, window->x11.handle,
2847 &rootX, &rootY, &childX, &childY,
2856 void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
2858 // Store the new position so it can be recognized later
2859 window->x11.warpCursorPosX = (int) x;
2860 window->x11.warpCursorPosY = (int) y;
2862 XWarpPointer(_glfw.x11.display, None, window->x11.handle,
2863 0,0,0,0, (int) x, (int) y);
2864 XFlush(_glfw.x11.display);
2867 void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
2869 if (mode == GLFW_CURSOR_DISABLED)
2871 if (_glfwPlatformWindowFocused(window))
2872 disableCursor(window);
2874 else if (_glfw.x11.disabledCursorWindow == window)
2875 enableCursor(window);
2877 updateCursorImage(window);
2879 XFlush(_glfw.x11.display);
2882 const char* _glfwPlatformGetScancodeName(int scancode)
2884 if (!_glfw.x11.xkb.available)
2887 if (scancode < 0 || scancode > 0xff ||
2888 _glfw.x11.keycodes[scancode] == GLFW_KEY_UNKNOWN)
2890 _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode");
2894 const int key = _glfw.x11.keycodes[scancode];
2895 const KeySym keysym = XkbKeycodeToKeysym(_glfw.x11.display,
2896 scancode, _glfw.x11.xkb.group, 0);
2897 if (keysym == NoSymbol)
2900 const long ch = _glfwKeySym2Unicode(keysym);
2904 const size_t count = encodeUTF8(_glfw.x11.keynames[key], (unsigned int) ch);
2908 _glfw.x11.keynames[key][count] = '\0';
2909 return _glfw.x11.keynames[key];
2912 int _glfwPlatformGetKeyScancode(int key)
2914 return _glfw.x11.scancodes[key];
2917 int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
2918 const GLFWimage* image,
2921 cursor->x11.handle = _glfwCreateCursorX11(image, xhot, yhot);
2922 if (!cursor->x11.handle)
2928 int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
2930 if (_glfw.x11.xcursor.handle)
2932 char* theme = XcursorGetTheme(_glfw.x11.display);
2935 const int size = XcursorGetDefaultSize(_glfw.x11.display);
2936 const char* name = NULL;
2938 if (shape == GLFW_ARROW_CURSOR)
2940 else if (shape == GLFW_IBEAM_CURSOR)
2942 else if (shape == GLFW_CROSSHAIR_CURSOR)
2944 else if (shape == GLFW_POINTING_HAND_CURSOR)
2946 else if (shape == GLFW_RESIZE_EW_CURSOR)
2948 else if (shape == GLFW_RESIZE_NS_CURSOR)
2950 else if (shape == GLFW_RESIZE_NWSE_CURSOR)
2951 name = "nwse-resize";
2952 else if (shape == GLFW_RESIZE_NESW_CURSOR)
2953 name = "nesw-resize";
2954 else if (shape == GLFW_RESIZE_ALL_CURSOR)
2955 name = "all-scroll";
2956 else if (shape == GLFW_NOT_ALLOWED_CURSOR)
2957 name = "not-allowed";
2959 XcursorImage* image = XcursorLibraryLoadImage(name, theme, size);
2962 cursor->x11.handle = XcursorImageLoadCursor(_glfw.x11.display, image);
2963 XcursorImageDestroy(image);
2968 if (!cursor->x11.handle)
2970 unsigned int native = 0;
2972 if (shape == GLFW_ARROW_CURSOR)
2973 native = XC_left_ptr;
2974 else if (shape == GLFW_IBEAM_CURSOR)
2976 else if (shape == GLFW_CROSSHAIR_CURSOR)
2977 native = XC_crosshair;
2978 else if (shape == GLFW_POINTING_HAND_CURSOR)
2980 else if (shape == GLFW_RESIZE_EW_CURSOR)
2981 native = XC_sb_h_double_arrow;
2982 else if (shape == GLFW_RESIZE_NS_CURSOR)
2983 native = XC_sb_v_double_arrow;
2984 else if (shape == GLFW_RESIZE_ALL_CURSOR)
2988 _glfwInputError(GLFW_CURSOR_UNAVAILABLE,
2989 "X11: Standard cursor shape unavailable");
2993 cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, native);
2994 if (!cursor->x11.handle)
2996 _glfwInputError(GLFW_PLATFORM_ERROR,
2997 "X11: Failed to create standard cursor");
3005 void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
3007 if (cursor->x11.handle)
3008 XFreeCursor(_glfw.x11.display, cursor->x11.handle);
3011 void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
3013 if (window->cursorMode == GLFW_CURSOR_NORMAL)
3015 updateCursorImage(window);
3016 XFlush(_glfw.x11.display);
3020 void _glfwPlatformSetClipboardString(const char* string)
3022 char* copy = _glfw_strdup(string);
3023 free(_glfw.x11.clipboardString);
3024 _glfw.x11.clipboardString = copy;
3026 XSetSelectionOwner(_glfw.x11.display,
3027 _glfw.x11.CLIPBOARD,
3028 _glfw.x11.helperWindowHandle,
3031 if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) !=
3032 _glfw.x11.helperWindowHandle)
3034 _glfwInputError(GLFW_PLATFORM_ERROR,
3035 "X11: Failed to become owner of clipboard selection");
3039 const char* _glfwPlatformGetClipboardString(void)
3041 return getSelectionString(_glfw.x11.CLIPBOARD);
3044 EGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs)
3046 if (_glfw.egl.ANGLE_platform_angle)
3050 if (_glfw.egl.ANGLE_platform_angle_opengl)
3052 if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_OPENGL)
3053 type = EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE;
3056 if (_glfw.egl.ANGLE_platform_angle_vulkan)
3058 if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_VULKAN)
3059 type = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE;
3064 *attribs = calloc(5, sizeof(EGLint));
3065 (*attribs)[0] = EGL_PLATFORM_ANGLE_TYPE_ANGLE;
3066 (*attribs)[1] = type;
3067 (*attribs)[2] = EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE;
3068 (*attribs)[3] = EGL_PLATFORM_X11_EXT;
3069 (*attribs)[4] = EGL_NONE;
3070 return EGL_PLATFORM_ANGLE_ANGLE;
3074 if (_glfw.egl.EXT_platform_base && _glfw.egl.EXT_platform_x11)
3075 return EGL_PLATFORM_X11_EXT;
3080 EGLNativeDisplayType _glfwPlatformGetEGLNativeDisplay(void)
3082 return _glfw.x11.display;
3085 EGLNativeWindowType _glfwPlatformGetEGLNativeWindow(_GLFWwindow* window)
3087 if (_glfw.egl.platform)
3088 return &window->x11.handle;
3090 return (EGLNativeWindowType) window->x11.handle;
3093 void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
3095 if (!_glfw.vk.KHR_surface)
3098 if (!_glfw.vk.KHR_xcb_surface || !_glfw.x11.x11xcb.handle)
3100 if (!_glfw.vk.KHR_xlib_surface)
3104 extensions[0] = "VK_KHR_surface";
3106 // NOTE: VK_KHR_xcb_surface is preferred due to some early ICDs exposing but
3107 // not correctly implementing VK_KHR_xlib_surface
3108 if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
3109 extensions[1] = "VK_KHR_xcb_surface";
3111 extensions[1] = "VK_KHR_xlib_surface";
3114 int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
3115 VkPhysicalDevice device,
3116 uint32_t queuefamily)
3118 VisualID visualID = XVisualIDFromVisual(DefaultVisual(_glfw.x11.display,
3121 if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
3123 PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR
3124 vkGetPhysicalDeviceXcbPresentationSupportKHR =
3125 (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)
3126 vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR");
3127 if (!vkGetPhysicalDeviceXcbPresentationSupportKHR)
3129 _glfwInputError(GLFW_API_UNAVAILABLE,
3130 "X11: Vulkan instance missing VK_KHR_xcb_surface extension");
3134 xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display);
3137 _glfwInputError(GLFW_PLATFORM_ERROR,
3138 "X11: Failed to retrieve XCB connection");
3142 return vkGetPhysicalDeviceXcbPresentationSupportKHR(device,
3149 PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR
3150 vkGetPhysicalDeviceXlibPresentationSupportKHR =
3151 (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)
3152 vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR");
3153 if (!vkGetPhysicalDeviceXlibPresentationSupportKHR)
3155 _glfwInputError(GLFW_API_UNAVAILABLE,
3156 "X11: Vulkan instance missing VK_KHR_xlib_surface extension");
3160 return vkGetPhysicalDeviceXlibPresentationSupportKHR(device,
3167 VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
3168 _GLFWwindow* window,
3169 const VkAllocationCallbacks* allocator,
3170 VkSurfaceKHR* surface)
3172 if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
3175 VkXcbSurfaceCreateInfoKHR sci;
3176 PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR;
3178 xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display);
3181 _glfwInputError(GLFW_PLATFORM_ERROR,
3182 "X11: Failed to retrieve XCB connection");
3183 return VK_ERROR_EXTENSION_NOT_PRESENT;
3186 vkCreateXcbSurfaceKHR = (PFN_vkCreateXcbSurfaceKHR)
3187 vkGetInstanceProcAddr(instance, "vkCreateXcbSurfaceKHR");
3188 if (!vkCreateXcbSurfaceKHR)
3190 _glfwInputError(GLFW_API_UNAVAILABLE,
3191 "X11: Vulkan instance missing VK_KHR_xcb_surface extension");
3192 return VK_ERROR_EXTENSION_NOT_PRESENT;
3195 memset(&sci, 0, sizeof(sci));
3196 sci.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
3197 sci.connection = connection;
3198 sci.window = window->x11.handle;
3200 err = vkCreateXcbSurfaceKHR(instance, &sci, allocator, surface);
3203 _glfwInputError(GLFW_PLATFORM_ERROR,
3204 "X11: Failed to create Vulkan XCB surface: %s",
3205 _glfwGetVulkanResultString(err));
3213 VkXlibSurfaceCreateInfoKHR sci;
3214 PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR;
3216 vkCreateXlibSurfaceKHR = (PFN_vkCreateXlibSurfaceKHR)
3217 vkGetInstanceProcAddr(instance, "vkCreateXlibSurfaceKHR");
3218 if (!vkCreateXlibSurfaceKHR)
3220 _glfwInputError(GLFW_API_UNAVAILABLE,
3221 "X11: Vulkan instance missing VK_KHR_xlib_surface extension");
3222 return VK_ERROR_EXTENSION_NOT_PRESENT;
3225 memset(&sci, 0, sizeof(sci));
3226 sci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
3227 sci.dpy = _glfw.x11.display;
3228 sci.window = window->x11.handle;
3230 err = vkCreateXlibSurfaceKHR(instance, &sci, allocator, surface);
3233 _glfwInputError(GLFW_PLATFORM_ERROR,
3234 "X11: Failed to create Vulkan X11 surface: %s",
3235 _glfwGetVulkanResultString(err));
3243 //////////////////////////////////////////////////////////////////////////
3244 ////// GLFW native API //////
3245 //////////////////////////////////////////////////////////////////////////
3247 GLFWAPI Display* glfwGetX11Display(void)
3249 _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
3250 return _glfw.x11.display;
3253 GLFWAPI Window glfwGetX11Window(GLFWwindow* handle)
3255 _GLFWwindow* window = (_GLFWwindow*) handle;
3256 _GLFW_REQUIRE_INIT_OR_RETURN(None);
3257 return window->x11.handle;
3260 GLFWAPI void glfwSetX11SelectionString(const char* string)
3262 _GLFW_REQUIRE_INIT();
3264 free(_glfw.x11.primarySelectionString);
3265 _glfw.x11.primarySelectionString = _glfw_strdup(string);
3267 XSetSelectionOwner(_glfw.x11.display,
3269 _glfw.x11.helperWindowHandle,
3272 if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.PRIMARY) !=
3273 _glfw.x11.helperWindowHandle)
3275 _glfwInputError(GLFW_PLATFORM_ERROR,
3276 "X11: Failed to become owner of primary selection");
3280 GLFWAPI const char* glfwGetX11SelectionString(void)
3282 _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
3283 return getSelectionString(_glfw.x11.PRIMARY);