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