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