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