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