GCC Code Coverage Report


Directory: ./
File: submodules/raylib/src/external/glfw/src/x11_window.c
Date: 2023-09-29 04:53:15
Exec Total Coverage
Lines: 0 1238 0.0%
Branches: 0 851 0.0%

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