1//========================================================================
2// GLFW 3.2 X11 - www.glfw.org
3//------------------------------------------------------------------------
4// Copyright (c) 2002-2006 Marcus Geelnard
5// Copyright (c) 2006-2016 Camilla Berglund <elmindreda@glfw.org>
6//
7// This software is provided 'as-is', without any express or implied
8// warranty. In no event will the authors be held liable for any damages
9// arising from the use of this software.
10//
11// Permission is granted to anyone to use this software for any purpose,
12// including commercial applications, and to alter it and redistribute it
13// freely, subject to the following restrictions:
14//
15// 1. The origin of this software must not be misrepresented; you must not
16// claim that you wrote the original software. If you use this software
17// in a product, an acknowledgment in the product documentation would
18// be appreciated but is not required.
19//
20// 2. Altered source versions must be plainly marked as such, and must not
21// be misrepresented as being the original software.
22//
23// 3. This notice may not be removed or altered from any source
24// distribution.
25//
26//========================================================================
27
28#include "internal.h"
29
30#include <X11/cursorfont.h>
31#include <X11/Xmd.h>
32
33#include <sys/select.h>
34
35#include <string.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <limits.h>
39#include <errno.h>
40#include <assert.h>
41
42// Action for EWMH client messages
43#define _NET_WM_STATE_REMOVE 0
44#define _NET_WM_STATE_ADD 1
45#define _NET_WM_STATE_TOGGLE 2
46
47// Additional mouse button names for XButtonEvent
48#define Button6 6
49#define Button7 7
50
51
52// Wait for data to arrive using select
53// This avoids blocking other threads via the per-display Xlib lock that also
54// covers GLX functions
55//
56static GLFWbool waitForEvent(double* timeout)
57{
58 fd_set fds;
59 const int fd = ConnectionNumber(_glfw.x11.display);
60 int count = fd + 1;
61
62 FD_ZERO(&fds);
63 FD_SET(fd, &fds);
64#if defined(__linux__)
65 FD_SET(_glfw.linux_js.inotify, &fds);
66
67 if (fd < _glfw.linux_js.inotify)
68 count = _glfw.linux_js.inotify + 1;
69#endif
70 for (;;)
71 {
72 if (timeout)
73 {
74 const long seconds = (long) *timeout;
75 const long microseconds = (long) ((*timeout - seconds) * 1e6);
76 struct timeval tv = { seconds, microseconds };
77 const uint64_t base = _glfwPlatformGetTimerValue();
78
79 const int result = select(count, &fds, NULL, NULL, &tv);
80 const int error = errno;
81
82 *timeout -= (_glfwPlatformGetTimerValue() - base) /
83 (double) _glfwPlatformGetTimerFrequency();
84
85 if (result > 0)
86 return GLFW_TRUE;
87 if ((result == -1 && error == EINTR) || *timeout <= 0.0)
88 return GLFW_FALSE;
89 }
90 else if (select(count, &fds, NULL, NULL, NULL) != -1 || errno != EINTR)
91 return GLFW_TRUE;
92 }
93}
94
95// Waits until a VisibilityNotify event arrives for the specified window or the
96// timeout period elapses (ICCCM section 4.2.2)
97//
98static GLFWbool waitForVisibilityNotify(_GLFWwindow* window)
99{
100 XEvent dummy;
101 double timeout = 0.1;
102
103 while (!XCheckTypedWindowEvent(_glfw.x11.display,
104 window->x11.handle,
105 VisibilityNotify,
106 &dummy))
107 {
108 if (!waitForEvent(&timeout))
109 return GLFW_FALSE;
110 }
111
112 return GLFW_TRUE;
113}
114
115// Returns whether the window is iconified
116//
117static int getWindowState(_GLFWwindow* window)
118{
119 int result = WithdrawnState;
120 struct {
121 CARD32 state;
122 Window icon;
123 } *state = NULL;
124
125 if (_glfwGetWindowPropertyX11(window->x11.handle,
126 _glfw.x11.WM_STATE,
127 _glfw.x11.WM_STATE,
128 (unsigned char**) &state) >= 2)
129 {
130 result = state->state;
131 }
132
133 XFree(state);
134 return result;
135}
136
137// Returns whether the event is a selection event
138//
139static Bool isSelectionEvent(Display* display, XEvent* event, XPointer pointer)
140{
141 return event->type == SelectionRequest ||
142 event->type == SelectionNotify ||
143 event->type == SelectionClear;
144}
145
146// Returns whether it is a _NET_FRAME_EXTENTS event for the specified window
147//
148static Bool isFrameExtentsEvent(Display* display, XEvent* event, XPointer pointer)
149{
150 _GLFWwindow* window = (_GLFWwindow*) pointer;
151 return event->type == PropertyNotify &&
152 event->xproperty.state == PropertyNewValue &&
153 event->xproperty.window == window->x11.handle &&
154 event->xproperty.atom == _glfw.x11.NET_FRAME_EXTENTS;
155}
156
157// Translates a GLFW standard cursor to a font cursor shape
158//
159static int translateCursorShape(int shape)
160{
161 switch (shape)
162 {
163 case GLFW_ARROW_CURSOR:
164 return XC_left_ptr;
165 case GLFW_IBEAM_CURSOR:
166 return XC_xterm;
167 case GLFW_CROSSHAIR_CURSOR:
168 return XC_crosshair;
169 case GLFW_HAND_CURSOR:
170 return XC_hand1;
171 case GLFW_HRESIZE_CURSOR:
172 return XC_sb_h_double_arrow;
173 case GLFW_VRESIZE_CURSOR:
174 return XC_sb_v_double_arrow;
175 }
176
177 return 0;
178}
179
180// Translates an X event modifier state mask
181//
182static int translateState(int state)
183{
184 int mods = 0;
185
186 if (state & ShiftMask)
187 mods |= GLFW_MOD_SHIFT;
188 if (state & ControlMask)
189 mods |= GLFW_MOD_CONTROL;
190 if (state & Mod1Mask)
191 mods |= GLFW_MOD_ALT;
192 if (state & Mod4Mask)
193 mods |= GLFW_MOD_SUPER;
194
195 return mods;
196}
197
198// Translates an X11 key code to a GLFW key token
199//
200static int translateKey(int scancode)
201{
202 // Use the pre-filled LUT (see createKeyTables() in x11_init.c)
203 if (scancode < 0 || scancode > 255)
204 return GLFW_KEY_UNKNOWN;
205
206 return _glfw.x11.publicKeys[scancode];
207}
208
209// Return the GLFW window corresponding to the specified X11 window
210//
211static _GLFWwindow* findWindowByHandle(Window handle)
212{
213 _GLFWwindow* window;
214
215 if (XFindContext(_glfw.x11.display,
216 handle,
217 _glfw.x11.context,
218 (XPointer*) &window) != 0)
219 {
220 return NULL;
221 }
222
223 return window;
224}
225
226// Sends an EWMH or ICCCM event to the window manager
227//
228static void sendEventToWM(_GLFWwindow* window, Atom type,
229 long a, long b, long c, long d, long e)
230{
231 XEvent event;
232 memset(&event, 0, sizeof(event));
233
234 event.type = ClientMessage;
235 event.xclient.window = window->x11.handle;
236 event.xclient.format = 32; // Data is 32-bit longs
237 event.xclient.message_type = type;
238 event.xclient.data.l[0] = a;
239 event.xclient.data.l[1] = b;
240 event.xclient.data.l[2] = c;
241 event.xclient.data.l[3] = d;
242 event.xclient.data.l[4] = e;
243
244 XSendEvent(_glfw.x11.display, _glfw.x11.root,
245 False,
246 SubstructureNotifyMask | SubstructureRedirectMask,
247 &event);
248}
249
250// Updates the normal hints according to the window settings
251//
252static void updateNormalHints(_GLFWwindow* window, int width, int height)
253{
254 XSizeHints* hints = XAllocSizeHints();
255
256 if (!window->monitor)
257 {
258 if (window->resizable)
259 {
260 if (window->minwidth != GLFW_DONT_CARE &&
261 window->minheight != GLFW_DONT_CARE)
262 {
263 hints->flags |= PMinSize;
264 hints->min_width = window->minwidth;
265 hints->min_height = window->minheight;
266 }
267
268 if (window->maxwidth != GLFW_DONT_CARE &&
269 window->maxheight != GLFW_DONT_CARE)
270 {
271 hints->flags |= PMaxSize;
272 hints->max_width = window->maxwidth;
273 hints->max_height = window->maxheight;
274 }
275
276 if (window->numer != GLFW_DONT_CARE &&
277 window->denom != GLFW_DONT_CARE)
278 {
279 hints->flags |= PAspect;
280 hints->min_aspect.x = hints->max_aspect.x = window->numer;
281 hints->min_aspect.y = hints->max_aspect.y = window->denom;
282 }
283 }
284 else
285 {
286 hints->flags |= (PMinSize | PMaxSize);
287 hints->min_width = hints->max_width = width;
288 hints->min_height = hints->max_height = height;
289 }
290 }
291
292 hints->flags |= PWinGravity;
293 hints->win_gravity = StaticGravity;
294
295 XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints);
296 XFree(hints);
297}
298
299// Updates the full screen status of the window
300//
301static void updateWindowMode(_GLFWwindow* window)
302{
303 if (window->monitor)
304 {
305 if (_glfw.x11.xinerama.available &&
306 _glfw.x11.NET_WM_FULLSCREEN_MONITORS)
307 {
308 sendEventToWM(window,
309 _glfw.x11.NET_WM_FULLSCREEN_MONITORS,
310 window->monitor->x11.index,
311 window->monitor->x11.index,
312 window->monitor->x11.index,
313 window->monitor->x11.index,
314 0);
315 }
316
317 if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN)
318 {
319 sendEventToWM(window,
320 _glfw.x11.NET_WM_STATE,
321 _NET_WM_STATE_ADD,
322 _glfw.x11.NET_WM_STATE_FULLSCREEN,
323 0, 1, 0);
324 }
325 else
326 {
327 // This is the butcher's way of removing window decorations
328 // Setting the override-redirect attribute on a window makes the
329 // window manager ignore the window completely (ICCCM, section 4)
330 // The good thing is that this makes undecorated full screen windows
331 // easy to do; the bad thing is that we have to do everything
332 // manually and some things (like iconify/restore) won't work at
333 // all, as those are tasks usually performed by the window manager
334
335 XSetWindowAttributes attributes;
336 attributes.override_redirect = True;
337 XChangeWindowAttributes(_glfw.x11.display,
338 window->x11.handle,
339 CWOverrideRedirect,
340 &attributes);
341
342 window->x11.overrideRedirect = GLFW_TRUE;
343 }
344
345 // Enable compositor bypass
346 {
347 const unsigned long value = 1;
348
349 XChangeProperty(_glfw.x11.display, window->x11.handle,
350 _glfw.x11.NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32,
351 PropModeReplace, (unsigned char*) &value, 1);
352 }
353 }
354 else
355 {
356 if (_glfw.x11.xinerama.available &&
357 _glfw.x11.NET_WM_FULLSCREEN_MONITORS)
358 {
359 XDeleteProperty(_glfw.x11.display, window->x11.handle,
360 _glfw.x11.NET_WM_FULLSCREEN_MONITORS);
361 }
362
363 if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN)
364 {
365 sendEventToWM(window,
366 _glfw.x11.NET_WM_STATE,
367 _NET_WM_STATE_REMOVE,
368 _glfw.x11.NET_WM_STATE_FULLSCREEN,
369 0, 1, 0);
370 }
371 else
372 {
373 XSetWindowAttributes attributes;
374 attributes.override_redirect = False;
375 XChangeWindowAttributes(_glfw.x11.display,
376 window->x11.handle,
377 CWOverrideRedirect,
378 &attributes);
379
380 window->x11.overrideRedirect = GLFW_FALSE;
381 }
382
383 // Disable compositor bypass
384 {
385 XDeleteProperty(_glfw.x11.display, window->x11.handle,
386 _glfw.x11.NET_WM_BYPASS_COMPOSITOR);
387 }
388 }
389}
390
391// Splits and translates a text/uri-list into separate file paths
392// NOTE: This function destroys the provided string
393//
394static char** parseUriList(char* text, int* count)
395{
396 const char* prefix = "file://";
397 char** paths = NULL;
398 char* line;
399
400 *count = 0;
401
402 while ((line = strtok(text, "\r\n")))
403 {
404 text = NULL;
405
406 if (line[0] == '#')
407 continue;
408
409 if (strncmp(line, prefix, strlen(prefix)) == 0)
410 line += strlen(prefix);
411
412 (*count)++;
413
414 char* path = calloc(strlen(line) + 1, 1);
415 paths = realloc(paths, *count * sizeof(char*));
416 paths[*count - 1] = path;
417
418 while (*line)
419 {
420 if (line[0] == '%' && line[1] && line[2])
421 {
422 const char digits[3] = { line[1], line[2], '\0' };
423 *path = strtol(digits, NULL, 16);
424 line += 2;
425 }
426 else
427 *path = *line;
428
429 path++;
430 line++;
431 }
432 }
433
434 return paths;
435}
436
437// Centers the cursor over the window client area
438//
439static void centerCursor(_GLFWwindow* window)
440{
441 int width, height;
442 _glfwPlatformGetWindowSize(window, &width, &height);
443 _glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0);
444}
445
446// Updates the cursor image according to its cursor mode
447//
448static void updateCursorImage(_GLFWwindow* window)
449{
450 if (window->cursorMode == GLFW_CURSOR_NORMAL)
451 {
452 if (window->cursor)
453 {
454 XDefineCursor(_glfw.x11.display, window->x11.handle,
455 window->cursor->x11.handle);
456 }
457 else
458 XUndefineCursor(_glfw.x11.display, window->x11.handle);
459 }
460 else
461 XDefineCursor(_glfw.x11.display, window->x11.handle, _glfw.x11.cursor);
462}
463
464// Create the X11 window (and its colormap)
465//
466static GLFWbool createNativeWindow(_GLFWwindow* window,
467 const _GLFWwndconfig* wndconfig,
468 Visual* visual, int depth)
469{
470 // Create a colormap based on the visual used by the current context
471 window->x11.colormap = XCreateColormap(_glfw.x11.display,
472 _glfw.x11.root,
473 visual,
474 AllocNone);
475
476 // Create the actual window
477 {
478 XSetWindowAttributes wa;
479 const unsigned long wamask = CWBorderPixel | CWColormap | CWEventMask;
480
481 wa.colormap = window->x11.colormap;
482 wa.border_pixel = 0;
483 wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask |
484 PointerMotionMask | ButtonPressMask | ButtonReleaseMask |
485 ExposureMask | FocusChangeMask | VisibilityChangeMask |
486 EnterWindowMask | LeaveWindowMask | PropertyChangeMask;
487
488 _glfwGrabErrorHandlerX11();
489
490 window->x11.handle = XCreateWindow(_glfw.x11.display,
491 _glfw.x11.root,
492 0, 0,
493 wndconfig->width, wndconfig->height,
494 0, // Border width
495 depth, // Color depth
496 InputOutput,
497 visual,
498 wamask,
499 &wa);
500
501 _glfwReleaseErrorHandlerX11();
502
503 if (!window->x11.handle)
504 {
505 _glfwInputErrorX11(GLFW_PLATFORM_ERROR,
506 "X11: Failed to create window");
507 return GLFW_FALSE;
508 }
509
510 XSaveContext(_glfw.x11.display,
511 window->x11.handle,
512 _glfw.x11.context,
513 (XPointer) window);
514 }
515
516 if (!wndconfig->decorated)
517 {
518 struct
519 {
520 unsigned long flags;
521 unsigned long functions;
522 unsigned long decorations;
523 long input_mode;
524 unsigned long status;
525 } hints;
526
527 hints.flags = 2; // Set decorations
528 hints.decorations = 0; // No decorations
529
530 XChangeProperty(_glfw.x11.display, window->x11.handle,
531 _glfw.x11.MOTIF_WM_HINTS,
532 _glfw.x11.MOTIF_WM_HINTS, 32,
533 PropModeReplace,
534 (unsigned char*) &hints,
535 sizeof(hints) / sizeof(long));
536 }
537
538 if (_glfw.x11.NET_WM_STATE && !window->monitor)
539 {
540 Atom states[3];
541 int count = 0;
542
543 if (wndconfig->floating)
544 {
545 if (_glfw.x11.NET_WM_STATE_ABOVE)
546 states[count++] = _glfw.x11.NET_WM_STATE_ABOVE;
547 }
548
549 if (wndconfig->maximized)
550 {
551 if (_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
552 _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
553 {
554 states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT;
555 states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ;
556 }
557 }
558
559 if (count)
560 {
561 XChangeProperty(_glfw.x11.display, window->x11.handle,
562 _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
563 PropModeReplace, (unsigned char*) &states, count);
564 }
565 }
566
567 // Declare the WM protocols supported by GLFW
568 {
569 Atom protocols[] =
570 {
571 _glfw.x11.WM_DELETE_WINDOW,
572 _glfw.x11.NET_WM_PING
573 };
574
575 XSetWMProtocols(_glfw.x11.display, window->x11.handle,
576 protocols, sizeof(protocols) / sizeof(Atom));
577 }
578
579 // Declare our PID
580 {
581 const pid_t pid = getpid();
582
583 XChangeProperty(_glfw.x11.display, window->x11.handle,
584 _glfw.x11.NET_WM_PID, XA_CARDINAL, 32,
585 PropModeReplace,
586 (unsigned char*) &pid, 1);
587 }
588
589 if (_glfw.x11.NET_WM_WINDOW_TYPE && _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL)
590 {
591 Atom type = _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL;
592 XChangeProperty(_glfw.x11.display, window->x11.handle,
593 _glfw.x11.NET_WM_WINDOW_TYPE, XA_ATOM, 32,
594 PropModeReplace, (unsigned char*) &type, 1);
595 }
596
597 // Set ICCCM WM_HINTS property
598 {
599 XWMHints* hints = XAllocWMHints();
600 if (!hints)
601 {
602 _glfwInputError(GLFW_OUT_OF_MEMORY,
603 "X11: Failed to allocate WM hints");
604 return GLFW_FALSE;
605 }
606
607 hints->flags = StateHint;
608 hints->initial_state = NormalState;
609
610 XSetWMHints(_glfw.x11.display, window->x11.handle, hints);
611 XFree(hints);
612 }
613
614 updateNormalHints(window, wndconfig->width, wndconfig->height);
615
616 // Set ICCCM WM_CLASS property
617 // HACK: Until a mechanism for specifying the application name is added, the
618 // initial window title is used as the window class name
619 if (strlen(wndconfig->title))
620 {
621 XClassHint* hint = XAllocClassHint();
622 hint->res_name = (char*) wndconfig->title;
623 hint->res_class = (char*) wndconfig->title;
624
625 XSetClassHint(_glfw.x11.display, window->x11.handle, hint);
626 XFree(hint);
627 }
628
629 if (_glfw.x11.XdndAware)
630 {
631 // Announce support for Xdnd (drag and drop)
632 const Atom version = 5;
633 XChangeProperty(_glfw.x11.display, window->x11.handle,
634 _glfw.x11.XdndAware, XA_ATOM, 32,
635 PropModeReplace, (unsigned char*) &version, 1);
636 }
637
638 _glfwPlatformSetWindowTitle(window, wndconfig->title);
639
640 if (_glfw.x11.im)
641 {
642 window->x11.ic = XCreateIC(_glfw.x11.im,
643 XNInputStyle,
644 XIMPreeditNothing | XIMStatusNothing,
645 XNClientWindow,
646 window->x11.handle,
647 XNFocusWindow,
648 window->x11.handle,
649 NULL);
650 }
651
652 _glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos);
653 _glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height);
654
655 return GLFW_TRUE;
656}
657
658// Set the specified property to the selection converted to the requested target
659//
660static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
661{
662 int i;
663 const Atom formats[] = { _glfw.x11.UTF8_STRING,
664 _glfw.x11.COMPOUND_STRING,
665 XA_STRING };
666 const int formatCount = sizeof(formats) / sizeof(formats[0]);
667
668 if (request->property == None)
669 {
670 // The requester is a legacy client (ICCCM section 2.2)
671 // We don't support legacy clients, so fail here
672 return None;
673 }
674
675 if (request->target == _glfw.x11.TARGETS)
676 {
677 // The list of supported targets was requested
678
679 const Atom targets[] = { _glfw.x11.TARGETS,
680 _glfw.x11.MULTIPLE,
681 _glfw.x11.UTF8_STRING,
682 _glfw.x11.COMPOUND_STRING,
683 XA_STRING };
684
685 XChangeProperty(_glfw.x11.display,
686 request->requestor,
687 request->property,
688 XA_ATOM,
689 32,
690 PropModeReplace,
691 (unsigned char*) targets,
692 sizeof(targets) / sizeof(targets[0]));
693
694 return request->property;
695 }
696
697 if (request->target == _glfw.x11.MULTIPLE)
698 {
699 // Multiple conversions were requested
700
701 Atom* targets;
702 unsigned long i, count;
703
704 count = _glfwGetWindowPropertyX11(request->requestor,
705 request->property,
706 _glfw.x11.ATOM_PAIR,
707 (unsigned char**) &targets);
708
709 for (i = 0; i < count; i += 2)
710 {
711 int j;
712
713 for (j = 0; j < formatCount; j++)
714 {
715 if (targets[i] == formats[j])
716 break;
717 }
718
719 if (j < formatCount)
720 {
721 XChangeProperty(_glfw.x11.display,
722 request->requestor,
723 targets[i + 1],
724 targets[i],
725 8,
726 PropModeReplace,
727 (unsigned char*) _glfw.x11.clipboardString,
728 strlen(_glfw.x11.clipboardString));
729 }
730 else
731 targets[i + 1] = None;
732 }
733
734 XChangeProperty(_glfw.x11.display,
735 request->requestor,
736 request->property,
737 _glfw.x11.ATOM_PAIR,
738 32,
739 PropModeReplace,
740 (unsigned char*) targets,
741 count);
742
743 XFree(targets);
744
745 return request->property;
746 }
747
748 if (request->target == _glfw.x11.SAVE_TARGETS)
749 {
750 // The request is a check whether we support SAVE_TARGETS
751 // It should be handled as a no-op side effect target
752
753 XChangeProperty(_glfw.x11.display,
754 request->requestor,
755 request->property,
756 _glfw.x11.NULL_,
757 32,
758 PropModeReplace,
759 NULL,
760 0);
761
762 return request->property;
763 }
764
765 // Conversion to a data target was requested
766
767 for (i = 0; i < formatCount; i++)
768 {
769 if (request->target == formats[i])
770 {
771 // The requested target is one we support
772
773 XChangeProperty(_glfw.x11.display,
774 request->requestor,
775 request->property,
776 request->target,
777 8,
778 PropModeReplace,
779 (unsigned char*) _glfw.x11.clipboardString,
780 strlen(_glfw.x11.clipboardString));
781
782 return request->property;
783 }
784 }
785
786 // The requested target is not supported
787
788 return None;
789}
790
791static void handleSelectionClear(XEvent* event)
792{
793 free(_glfw.x11.clipboardString);
794 _glfw.x11.clipboardString = NULL;
795}
796
797static void handleSelectionRequest(XEvent* event)
798{
799 const XSelectionRequestEvent* request = &event->xselectionrequest;
800
801 XEvent reply;
802 memset(&reply, 0, sizeof(reply));
803
804 reply.xselection.property = writeTargetToProperty(request);
805 reply.xselection.type = SelectionNotify;
806 reply.xselection.display = request->display;
807 reply.xselection.requestor = request->requestor;
808 reply.xselection.selection = request->selection;
809 reply.xselection.target = request->target;
810 reply.xselection.time = request->time;
811
812 XSendEvent(_glfw.x11.display, request->requestor, False, 0, &reply);
813}
814
815static void pushSelectionToManager(_GLFWwindow* window)
816{
817 XConvertSelection(_glfw.x11.display,
818 _glfw.x11.CLIPBOARD_MANAGER,
819 _glfw.x11.SAVE_TARGETS,
820 None,
821 window->x11.handle,
822 CurrentTime);
823
824 for (;;)
825 {
826 XEvent event;
827
828 while (XCheckIfEvent(_glfw.x11.display, &event, isSelectionEvent, NULL))
829 {
830 switch (event.type)
831 {
832 case SelectionRequest:
833 handleSelectionRequest(&event);
834 break;
835
836 case SelectionClear:
837 handleSelectionClear(&event);
838 break;
839
840 case SelectionNotify:
841 {
842 if (event.xselection.target == _glfw.x11.SAVE_TARGETS)
843 {
844 // This means one of two things; either the selection was
845 // not owned, which means there is no clipboard manager, or
846 // the transfer to the clipboard manager has completed
847 // In either case, it means we are done here
848 return;
849 }
850
851 break;
852 }
853 }
854 }
855
856 waitForEvent(NULL);
857 }
858}
859
860// Make the specified window and its video mode active on its monitor
861//
862static GLFWbool acquireMonitor(_GLFWwindow* window)
863{
864 GLFWbool status;
865
866 if (_glfw.x11.saver.count == 0)
867 {
868 // Remember old screen saver settings
869 XGetScreenSaver(_glfw.x11.display,
870 &_glfw.x11.saver.timeout,
871 &_glfw.x11.saver.interval,
872 &_glfw.x11.saver.blanking,
873 &_glfw.x11.saver.exposure);
874
875 // Disable screen saver
876 XSetScreenSaver(_glfw.x11.display, 0, 0, DontPreferBlanking,
877 DefaultExposures);
878 }
879
880 if (!window->monitor->window)
881 _glfw.x11.saver.count++;
882
883 status = _glfwSetVideoModeX11(window->monitor, &window->videoMode);
884
885 if (window->x11.overrideRedirect)
886 {
887 int xpos, ypos;
888 GLFWvidmode mode;
889
890 // Manually position the window over its monitor
891 _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos);
892 _glfwPlatformGetVideoMode(window->monitor, &mode);
893
894 XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
895 xpos, ypos, mode.width, mode.height);
896 }
897
898 _glfwInputMonitorWindowChange(window->monitor, window);
899 return status;
900}
901
902// Remove the window and restore the original video mode
903//
904static void releaseMonitor(_GLFWwindow* window)
905{
906 if (window->monitor->window != window)
907 return;
908
909 _glfwInputMonitorWindowChange(window->monitor, NULL);
910 _glfwRestoreVideoModeX11(window->monitor);
911
912 _glfw.x11.saver.count--;
913
914 if (_glfw.x11.saver.count == 0)
915 {
916 // Restore old screen saver settings
917 XSetScreenSaver(_glfw.x11.display,
918 _glfw.x11.saver.timeout,
919 _glfw.x11.saver.interval,
920 _glfw.x11.saver.blanking,
921 _glfw.x11.saver.exposure);
922 }
923}
924
925// Decode a Unicode code point from a UTF-8 stream
926// Based on cutef8 by Jeff Bezanson (Public Domain)
927//
928#if defined(X_HAVE_UTF8_STRING)
929static unsigned int decodeUTF8(const char** s)
930{
931 unsigned int ch = 0, count = 0;
932 static const unsigned int offsets[] =
933 {
934 0x00000000u, 0x00003080u, 0x000e2080u,
935 0x03c82080u, 0xfa082080u, 0x82082080u
936 };
937
938 do
939 {
940 ch = (ch << 6) + (unsigned char) **s;
941 (*s)++;
942 count++;
943 } while ((**s & 0xc0) == 0x80);
944
945 assert(count <= 6);
946 return ch - offsets[count - 1];
947}
948#endif /*X_HAVE_UTF8_STRING*/
949
950// Process the specified X event
951//
952static void processEvent(XEvent *event)
953{
954 _GLFWwindow* window = NULL;
955 int keycode = 0;
956 Bool filtered = False;
957
958 // HACK: Save scancode as some IMs clear the field in XFilterEvent
959 if (event->type == KeyPress || event->type == KeyRelease)
960 keycode = event->xkey.keycode;
961
962 if (_glfw.x11.im)
963 filtered = XFilterEvent(event, None);
964
965 if (_glfw.x11.randr.available)
966 {
967 if (event->type == _glfw.x11.randr.eventBase + RRNotify)
968 {
969 XRRUpdateConfiguration(event);
970 _glfwInputMonitorChange();
971 return;
972 }
973 }
974
975 if (event->type != GenericEvent)
976 {
977 window = findWindowByHandle(event->xany.window);
978 if (window == NULL)
979 {
980 // This is an event for a window that has already been destroyed
981 return;
982 }
983 }
984
985 switch (event->type)
986 {
987 case KeyPress:
988 {
989 const int key = translateKey(keycode);
990 const int mods = translateState(event->xkey.state);
991 const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT));
992
993 if (window->x11.ic)
994 {
995 // HACK: Ignore duplicate key press events generated by ibus
996 // Corresponding release events are filtered out by the
997 // GLFW key repeat logic
998 if (window->x11.lastKeyCode != keycode ||
999 window->x11.lastKeyTime != event->xkey.time)
1000 {
1001 if (keycode)
1002 _glfwInputKey(window, key, keycode, GLFW_PRESS, mods);
1003 }
1004
1005 window->x11.lastKeyCode = keycode;
1006 window->x11.lastKeyTime = event->xkey.time;
1007
1008 if (!filtered)
1009 {
1010 int count;
1011 Status status;
1012#if defined(X_HAVE_UTF8_STRING)
1013 char buffer[100];
1014 char* chars = buffer;
1015
1016 count = Xutf8LookupString(window->x11.ic,
1017 &event->xkey,
1018 buffer, sizeof(buffer) - 1,
1019 NULL, &status);
1020
1021 if (status == XBufferOverflow)
1022 {
1023 chars = calloc(count + 1, 1);
1024 count = Xutf8LookupString(window->x11.ic,
1025 &event->xkey,
1026 chars, count,
1027 NULL, &status);
1028 }
1029
1030 if (status == XLookupChars || status == XLookupBoth)
1031 {
1032 const char* c = chars;
1033 chars[count] = '\0';
1034 while (c - chars < count)
1035 _glfwInputChar(window, decodeUTF8(&c), mods, plain);
1036 }
1037#else /*X_HAVE_UTF8_STRING*/
1038 wchar_t buffer[16];
1039 wchar_t* chars = buffer;
1040
1041 count = XwcLookupString(window->x11.ic,
1042 &event->xkey,
1043 buffer, sizeof(buffer) / sizeof(wchar_t),
1044 NULL, &status);
1045
1046 if (status == XBufferOverflow)
1047 {
1048 chars = calloc(count, sizeof(wchar_t));
1049 count = XwcLookupString(window->x11.ic,
1050 &event->xkey,
1051 chars, count,
1052 NULL, &status);
1053 }
1054
1055 if (status == XLookupChars || status == XLookupBoth)
1056 {
1057 int i;
1058 for (i = 0; i < count; i++)
1059 _glfwInputChar(window, chars[i], mods, plain);
1060 }
1061#endif /*X_HAVE_UTF8_STRING*/
1062
1063 if (chars != buffer)
1064 free(chars);
1065 }
1066 }
1067 else
1068 {
1069 KeySym keysym;
1070 XLookupString(&event->xkey, NULL, 0, &keysym, NULL);
1071
1072 _glfwInputKey(window, key, keycode, GLFW_PRESS, mods);
1073
1074 const long character = _glfwKeySym2Unicode(keysym);
1075 if (character != -1)
1076 _glfwInputChar(window, character, mods, plain);
1077 }
1078
1079 return;
1080 }
1081
1082 case KeyRelease:
1083 {
1084 const int key = translateKey(keycode);
1085 const int mods = translateState(event->xkey.state);
1086
1087 if (!_glfw.x11.xkb.detectable)
1088 {
1089 // HACK: Key repeat events will arrive as KeyRelease/KeyPress
1090 // pairs with similar or identical time stamps
1091 // The key repeat logic in _glfwInputKey expects only key
1092 // presses to repeat, so detect and discard release events
1093 if (XEventsQueued(_glfw.x11.display, QueuedAfterReading))
1094 {
1095 XEvent next;
1096 XPeekEvent(_glfw.x11.display, &next);
1097
1098 if (next.type == KeyPress &&
1099 next.xkey.window == event->xkey.window &&
1100 next.xkey.keycode == keycode)
1101 {
1102 // HACK: The time of repeat events sometimes doesn't
1103 // match that of the press event, so add an
1104 // epsilon
1105 // Toshiyuki Takahashi can press a button
1106 // 16 times per second so it's fairly safe to
1107 // assume that no human is pressing the key 50
1108 // times per second (value is ms)
1109 if ((next.xkey.time - event->xkey.time) < 20)
1110 {
1111 // This is very likely a server-generated key repeat
1112 // event, so ignore it
1113 return;
1114 }
1115 }
1116 }
1117 }
1118
1119 _glfwInputKey(window, key, keycode, GLFW_RELEASE, mods);
1120 return;
1121 }
1122
1123 case ButtonPress:
1124 {
1125 const int mods = translateState(event->xbutton.state);
1126
1127 if (event->xbutton.button == Button1)
1128 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, mods);
1129 else if (event->xbutton.button == Button2)
1130 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, mods);
1131 else if (event->xbutton.button == Button3)
1132 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS, mods);
1133
1134 // Modern X provides scroll events as mouse button presses
1135 else if (event->xbutton.button == Button4)
1136 _glfwInputScroll(window, 0.0, 1.0);
1137 else if (event->xbutton.button == Button5)
1138 _glfwInputScroll(window, 0.0, -1.0);
1139 else if (event->xbutton.button == Button6)
1140 _glfwInputScroll(window, 1.0, 0.0);
1141 else if (event->xbutton.button == Button7)
1142 _glfwInputScroll(window, -1.0, 0.0);
1143
1144 else
1145 {
1146 // Additional buttons after 7 are treated as regular buttons
1147 // We subtract 4 to fill the gap left by scroll input above
1148 _glfwInputMouseClick(window,
1149 event->xbutton.button - Button1 - 4,
1150 GLFW_PRESS,
1151 mods);
1152 }
1153
1154 return;
1155 }
1156
1157 case ButtonRelease:
1158 {
1159 const int mods = translateState(event->xbutton.state);
1160
1161 if (event->xbutton.button == Button1)
1162 {
1163 _glfwInputMouseClick(window,
1164 GLFW_MOUSE_BUTTON_LEFT,
1165 GLFW_RELEASE,
1166 mods);
1167 }
1168 else if (event->xbutton.button == Button2)
1169 {
1170 _glfwInputMouseClick(window,
1171 GLFW_MOUSE_BUTTON_MIDDLE,
1172 GLFW_RELEASE,
1173 mods);
1174 }
1175 else if (event->xbutton.button == Button3)
1176 {
1177 _glfwInputMouseClick(window,
1178 GLFW_MOUSE_BUTTON_RIGHT,
1179 GLFW_RELEASE,
1180 mods);
1181 }
1182 else if (event->xbutton.button > Button7)
1183 {
1184 // Additional buttons after 7 are treated as regular buttons
1185 // We subtract 4 to fill the gap left by scroll input above
1186 _glfwInputMouseClick(window,
1187 event->xbutton.button - Button1 - 4,
1188 GLFW_RELEASE,
1189 mods);
1190 }
1191
1192 return;
1193 }
1194
1195 case EnterNotify:
1196 {
1197 // HACK: This is a workaround for WMs (KWM, Fluxbox) that otherwise
1198 // ignore the defined cursor for hidden cursor mode
1199 if (window->cursorMode == GLFW_CURSOR_HIDDEN)
1200 _glfwPlatformSetCursorMode(window, GLFW_CURSOR_HIDDEN);
1201
1202 _glfwInputCursorEnter(window, GLFW_TRUE);
1203 return;
1204 }
1205
1206 case LeaveNotify:
1207 {
1208 _glfwInputCursorEnter(window, GLFW_FALSE);
1209 return;
1210 }
1211
1212 case MotionNotify:
1213 {
1214 const int x = event->xmotion.x;
1215 const int y = event->xmotion.y;
1216
1217 if (x != window->x11.warpCursorPosX || y != window->x11.warpCursorPosY)
1218 {
1219 // The cursor was moved by something other than GLFW
1220
1221 if (window->cursorMode == GLFW_CURSOR_DISABLED)
1222 {
1223 if (_glfw.x11.disabledCursorWindow != window)
1224 return;
1225
1226 const int dx = x - window->x11.lastCursorPosX;
1227 const int dy = y - window->x11.lastCursorPosY;
1228
1229 _glfwInputCursorPos(window,
1230 window->virtualCursorPosX + dx,
1231 window->virtualCursorPosY + dy);
1232 }
1233 else
1234 _glfwInputCursorPos(window, x, y);
1235 }
1236
1237 window->x11.lastCursorPosX = x;
1238 window->x11.lastCursorPosY = y;
1239 return;
1240 }
1241
1242 case ConfigureNotify:
1243 {
1244 if (event->xconfigure.width != window->x11.width ||
1245 event->xconfigure.height != window->x11.height)
1246 {
1247 _glfwInputFramebufferSize(window,
1248 event->xconfigure.width,
1249 event->xconfigure.height);
1250
1251 _glfwInputWindowSize(window,
1252 event->xconfigure.width,
1253 event->xconfigure.height);
1254
1255 window->x11.width = event->xconfigure.width;
1256 window->x11.height = event->xconfigure.height;
1257 }
1258
1259 if (event->xconfigure.x != window->x11.xpos ||
1260 event->xconfigure.y != window->x11.ypos)
1261 {
1262 if (window->x11.overrideRedirect || event->xany.send_event)
1263 {
1264 _glfwInputWindowPos(window,
1265 event->xconfigure.x,
1266 event->xconfigure.y);
1267
1268 window->x11.xpos = event->xconfigure.x;
1269 window->x11.ypos = event->xconfigure.y;
1270 }
1271 }
1272
1273 return;
1274 }
1275
1276 case ClientMessage:
1277 {
1278 // Custom client message, probably from the window manager
1279
1280 if (filtered)
1281 return;
1282
1283 if (event->xclient.message_type == None)
1284 return;
1285
1286 if (event->xclient.message_type == _glfw.x11.WM_PROTOCOLS)
1287 {
1288 const Atom protocol = event->xclient.data.l[0];
1289 if (protocol == None)
1290 return;
1291
1292 if (protocol == _glfw.x11.WM_DELETE_WINDOW)
1293 {
1294 // The window manager was asked to close the window, for example by
1295 // the user pressing a 'close' window decoration button
1296 _glfwInputWindowCloseRequest(window);
1297 }
1298 else if (protocol == _glfw.x11.NET_WM_PING)
1299 {
1300 // The window manager is pinging the application to ensure it's
1301 // still responding to events
1302
1303 XEvent reply = *event;
1304 reply.xclient.window = _glfw.x11.root;
1305
1306 XSendEvent(_glfw.x11.display, _glfw.x11.root,
1307 False,
1308 SubstructureNotifyMask | SubstructureRedirectMask,
1309 &reply);
1310 }
1311 }
1312 else if (event->xclient.message_type == _glfw.x11.XdndEnter)
1313 {
1314 // A drag operation has entered the window
1315 // TODO: Check if UTF-8 string is supported by the source
1316 }
1317 else if (event->xclient.message_type == _glfw.x11.XdndDrop)
1318 {
1319 // The drag operation has finished dropping on
1320 // the window, ask to convert it to a UTF-8 string
1321 _glfw.x11.xdnd.source = event->xclient.data.l[0];
1322 XConvertSelection(_glfw.x11.display,
1323 _glfw.x11.XdndSelection,
1324 _glfw.x11.UTF8_STRING,
1325 _glfw.x11.XdndSelection,
1326 window->x11.handle, CurrentTime);
1327 }
1328 else if (event->xclient.message_type == _glfw.x11.XdndPosition)
1329 {
1330 // The drag operation has moved over the window
1331 const int absX = (event->xclient.data.l[2] >> 16) & 0xFFFF;
1332 const int absY = (event->xclient.data.l[2]) & 0xFFFF;
1333 int x, y;
1334
1335 _glfwPlatformGetWindowPos(window, &x, &y);
1336 _glfwInputCursorPos(window, absX - x, absY - y);
1337
1338 // Reply that we are ready to copy the dragged data
1339 XEvent reply;
1340 memset(&reply, 0, sizeof(reply));
1341
1342 reply.type = ClientMessage;
1343 reply.xclient.window = event->xclient.data.l[0];
1344 reply.xclient.message_type = _glfw.x11.XdndStatus;
1345 reply.xclient.format = 32;
1346 reply.xclient.data.l[0] = window->x11.handle;
1347 reply.xclient.data.l[1] = 1; // Always accept the dnd with no rectangle
1348 reply.xclient.data.l[2] = 0; // Specify an empty rectangle
1349 reply.xclient.data.l[3] = 0;
1350 reply.xclient.data.l[4] = _glfw.x11.XdndActionCopy;
1351
1352 XSendEvent(_glfw.x11.display, event->xclient.data.l[0],
1353 False, NoEventMask, &reply);
1354 XFlush(_glfw.x11.display);
1355 }
1356
1357 return;
1358 }
1359
1360 case SelectionNotify:
1361 {
1362 if (event->xselection.property)
1363 {
1364 // The converted data from the drag operation has arrived
1365 char* data;
1366 const int result =
1367 _glfwGetWindowPropertyX11(event->xselection.requestor,
1368 event->xselection.property,
1369 event->xselection.target,
1370 (unsigned char**) &data);
1371
1372 if (result)
1373 {
1374 int i, count;
1375 char** paths = parseUriList(data, &count);
1376
1377 _glfwInputDrop(window, count, (const char**) paths);
1378
1379 for (i = 0; i < count; i++)
1380 free(paths[i]);
1381 free(paths);
1382 }
1383
1384 XFree(data);
1385
1386 XEvent reply;
1387 memset(&reply, 0, sizeof(reply));
1388
1389 reply.type = ClientMessage;
1390 reply.xclient.window = _glfw.x11.xdnd.source;
1391 reply.xclient.message_type = _glfw.x11.XdndFinished;
1392 reply.xclient.format = 32;
1393 reply.xclient.data.l[0] = window->x11.handle;
1394 reply.xclient.data.l[1] = result;
1395 reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy;
1396
1397 // Reply that all is well
1398 XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
1399 False, NoEventMask, &reply);
1400 XFlush(_glfw.x11.display);
1401 }
1402
1403 return;
1404 }
1405
1406 case FocusIn:
1407 {
1408 if (window->cursorMode == GLFW_CURSOR_DISABLED)
1409 _glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED);
1410
1411 if (event->xfocus.mode == NotifyGrab ||
1412 event->xfocus.mode == NotifyUngrab)
1413 {
1414 // Ignore focus events from popup indicator windows, window menu
1415 // key chords and window dragging
1416 return;
1417 }
1418
1419 if (window->x11.ic)
1420 XSetICFocus(window->x11.ic);
1421
1422 _glfwInputWindowFocus(window, GLFW_TRUE);
1423 return;
1424 }
1425
1426 case FocusOut:
1427 {
1428 if (window->cursorMode == GLFW_CURSOR_DISABLED)
1429 _glfwPlatformSetCursorMode(window, GLFW_CURSOR_NORMAL);
1430
1431 if (event->xfocus.mode == NotifyGrab ||
1432 event->xfocus.mode == NotifyUngrab)
1433 {
1434 // Ignore focus events from popup indicator windows, window menu
1435 // key chords and window dragging
1436 return;
1437 }
1438
1439 if (window->x11.ic)
1440 XUnsetICFocus(window->x11.ic);
1441
1442 if (window->monitor && window->autoIconify)
1443 _glfwPlatformIconifyWindow(window);
1444
1445 _glfwInputWindowFocus(window, GLFW_FALSE);
1446 return;
1447 }
1448
1449 case Expose:
1450 {
1451 _glfwInputWindowDamage(window);
1452 return;
1453 }
1454
1455 case PropertyNotify:
1456 {
1457 if (event->xproperty.atom == _glfw.x11.WM_STATE &&
1458 event->xproperty.state == PropertyNewValue)
1459 {
1460 const int state = getWindowState(window);
1461 if (state == IconicState)
1462 {
1463 if (window->monitor)
1464 releaseMonitor(window);
1465
1466 _glfwInputWindowIconify(window, GLFW_TRUE);
1467 }
1468 else if (state == NormalState)
1469 {
1470 if (window->monitor)
1471 acquireMonitor(window);
1472
1473 _glfwInputWindowIconify(window, GLFW_FALSE);
1474 }
1475 }
1476
1477 return;
1478 }
1479
1480 case SelectionClear:
1481 {
1482 handleSelectionClear(event);
1483 return;
1484 }
1485
1486 case SelectionRequest:
1487 {
1488 handleSelectionRequest(event);
1489 return;
1490 }
1491
1492 case DestroyNotify:
1493 return;
1494 }
1495}
1496
1497
1498//////////////////////////////////////////////////////////////////////////
1499////// GLFW internal API //////
1500//////////////////////////////////////////////////////////////////////////
1501
1502// Retrieve a single window property of the specified type
1503// Inspired by fghGetWindowProperty from freeglut
1504//
1505unsigned long _glfwGetWindowPropertyX11(Window window,
1506 Atom property,
1507 Atom type,
1508 unsigned char** value)
1509{
1510 Atom actualType;
1511 int actualFormat;
1512 unsigned long itemCount, bytesAfter;
1513
1514 XGetWindowProperty(_glfw.x11.display,
1515 window,
1516 property,
1517 0,
1518 LONG_MAX,
1519 False,
1520 type,
1521 &actualType,
1522 &actualFormat,
1523 &itemCount,
1524 &bytesAfter,
1525 value);
1526
1527 if (type != AnyPropertyType && actualType != type)
1528 return 0;
1529
1530 return itemCount;
1531}
1532
1533
1534//////////////////////////////////////////////////////////////////////////
1535////// GLFW platform API //////
1536//////////////////////////////////////////////////////////////////////////
1537
1538int _glfwPlatformCreateWindow(_GLFWwindow* window,
1539 const _GLFWwndconfig* wndconfig,
1540 const _GLFWctxconfig* ctxconfig,
1541 const _GLFWfbconfig* fbconfig)
1542{
1543 Visual* visual;
1544 int depth;
1545
1546 if (ctxconfig->client == GLFW_NO_API)
1547 {
1548 visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen);
1549 depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen);
1550 }
1551 else
1552 {
1553 if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
1554 {
1555 if (!_glfwInitGLX())
1556 return GLFW_FALSE;
1557 if (!_glfwChooseVisualGLX(ctxconfig, fbconfig, &visual, &depth))
1558 return GLFW_FALSE;
1559 }
1560 else
1561 {
1562 if (!_glfwInitEGL())
1563 return GLFW_FALSE;
1564 if (!_glfwChooseVisualEGL(ctxconfig, fbconfig, &visual, &depth))
1565 return GLFW_FALSE;
1566 }
1567 }
1568
1569 if (!createNativeWindow(window, wndconfig, visual, depth))
1570 return GLFW_FALSE;
1571
1572 if (ctxconfig->client != GLFW_NO_API)
1573 {
1574 if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
1575 {
1576 if (!_glfwCreateContextGLX(window, ctxconfig, fbconfig))
1577 return GLFW_FALSE;
1578 }
1579 else
1580 {
1581 if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
1582 return GLFW_FALSE;
1583 }
1584 }
1585
1586 if (window->monitor)
1587 {
1588 _glfwPlatformShowWindow(window);
1589 updateWindowMode(window);
1590 if (!acquireMonitor(window))
1591 return GLFW_FALSE;
1592
1593 centerCursor(window);
1594 }
1595
1596 XFlush(_glfw.x11.display);
1597 return GLFW_TRUE;
1598}
1599
1600void _glfwPlatformDestroyWindow(_GLFWwindow* window)
1601{
1602 if (_glfw.x11.disabledCursorWindow == window)
1603 _glfw.x11.disabledCursorWindow = NULL;
1604
1605 if (window->monitor)
1606 releaseMonitor(window);
1607
1608 if (window->x11.ic)
1609 {
1610 XDestroyIC(window->x11.ic);
1611 window->x11.ic = NULL;
1612 }
1613
1614 if (window->context.destroy)
1615 window->context.destroy(window);
1616
1617 if (window->x11.handle)
1618 {
1619 if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) ==
1620 window->x11.handle)
1621 {
1622 pushSelectionToManager(window);
1623 }
1624
1625 XDeleteContext(_glfw.x11.display, window->x11.handle, _glfw.x11.context);
1626 XUnmapWindow(_glfw.x11.display, window->x11.handle);
1627 XDestroyWindow(_glfw.x11.display, window->x11.handle);
1628 window->x11.handle = (Window) 0;
1629 }
1630
1631 if (window->x11.colormap)
1632 {
1633 XFreeColormap(_glfw.x11.display, window->x11.colormap);
1634 window->x11.colormap = (Colormap) 0;
1635 }
1636
1637 XFlush(_glfw.x11.display);
1638}
1639
1640void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
1641{
1642#if defined(X_HAVE_UTF8_STRING)
1643 Xutf8SetWMProperties(_glfw.x11.display,
1644 window->x11.handle,
1645 title, title,
1646 NULL, 0,
1647 NULL, NULL, NULL);
1648#else
1649 // This may be a slightly better fallback than using XStoreName and
1650 // XSetIconName, which always store their arguments using STRING
1651 XmbSetWMProperties(_glfw.x11.display,
1652 window->x11.handle,
1653 title, title,
1654 NULL, 0,
1655 NULL, NULL, NULL);
1656#endif
1657
1658 XChangeProperty(_glfw.x11.display, window->x11.handle,
1659 _glfw.x11.NET_WM_NAME, _glfw.x11.UTF8_STRING, 8,
1660 PropModeReplace,
1661 (unsigned char*) title, strlen(title));
1662
1663 XChangeProperty(_glfw.x11.display, window->x11.handle,
1664 _glfw.x11.NET_WM_ICON_NAME, _glfw.x11.UTF8_STRING, 8,
1665 PropModeReplace,
1666 (unsigned char*) title, strlen(title));
1667
1668 XFlush(_glfw.x11.display);
1669}
1670
1671void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
1672 int count, const GLFWimage* images)
1673{
1674 if (count)
1675 {
1676 int i, j, longCount = 0;
1677
1678 for (i = 0; i < count; i++)
1679 longCount += 2 + images[i].width * images[i].height;
1680
1681 long* icon = calloc(longCount, sizeof(long));
1682 long* target = icon;
1683
1684 for (i = 0; i < count; i++)
1685 {
1686 *target++ = images[i].width;
1687 *target++ = images[i].height;
1688
1689 for (j = 0; j < images[i].width * images[i].height; j++)
1690 {
1691 *target++ = (images[i].pixels[j * 4 + 0] << 16) |
1692 (images[i].pixels[j * 4 + 1] << 8) |
1693 (images[i].pixels[j * 4 + 2] << 0) |
1694 (images[i].pixels[j * 4 + 3] << 24);
1695 }
1696 }
1697
1698 XChangeProperty(_glfw.x11.display, window->x11.handle,
1699 _glfw.x11.NET_WM_ICON,
1700 XA_CARDINAL, 32,
1701 PropModeReplace,
1702 (unsigned char*) icon,
1703 longCount);
1704
1705 free(icon);
1706 }
1707 else
1708 {
1709 XDeleteProperty(_glfw.x11.display, window->x11.handle,
1710 _glfw.x11.NET_WM_ICON);
1711 }
1712
1713 XFlush(_glfw.x11.display);
1714}
1715
1716void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
1717{
1718 Window dummy;
1719 int x, y;
1720
1721 XTranslateCoordinates(_glfw.x11.display, window->x11.handle, _glfw.x11.root,
1722 0, 0, &x, &y, &dummy);
1723
1724 if (xpos)
1725 *xpos = x;
1726 if (ypos)
1727 *ypos = y;
1728}
1729
1730void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos)
1731{
1732 // HACK: Explicitly setting PPosition to any value causes some WMs, notably
1733 // Compiz and Metacity, to honor the position of unmapped windows
1734 if (!_glfwPlatformWindowVisible(window))
1735 {
1736 long supplied;
1737 XSizeHints* hints = XAllocSizeHints();
1738
1739 if (XGetWMNormalHints(_glfw.x11.display, window->x11.handle, hints, &supplied))
1740 {
1741 hints->flags |= PPosition;
1742 hints->x = hints->y = 0;
1743
1744 XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints);
1745 }
1746
1747 XFree(hints);
1748 }
1749
1750 XMoveWindow(_glfw.x11.display, window->x11.handle, xpos, ypos);
1751 XFlush(_glfw.x11.display);
1752}
1753
1754void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
1755{
1756 XWindowAttributes attribs;
1757 XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &attribs);
1758
1759 if (width)
1760 *width = attribs.width;
1761 if (height)
1762 *height = attribs.height;
1763}
1764
1765void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
1766{
1767 if (window->monitor)
1768 {
1769 if (window->monitor->window == window)
1770 acquireMonitor(window);
1771 }
1772 else
1773 {
1774 if (!window->resizable)
1775 updateNormalHints(window, width, height);
1776
1777 XResizeWindow(_glfw.x11.display, window->x11.handle, width, height);
1778 }
1779
1780 XFlush(_glfw.x11.display);
1781}
1782
1783void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
1784 int minwidth, int minheight,
1785 int maxwidth, int maxheight)
1786{
1787 int width, height;
1788 _glfwPlatformGetWindowSize(window, &width, &height);
1789 updateNormalHints(window, width, height);
1790 XFlush(_glfw.x11.display);
1791}
1792
1793void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom)
1794{
1795 int width, height;
1796 _glfwPlatformGetWindowSize(window, &width, &height);
1797 updateNormalHints(window, width, height);
1798 XFlush(_glfw.x11.display);
1799}
1800
1801void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height)
1802{
1803 _glfwPlatformGetWindowSize(window, width, height);
1804}
1805
1806void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
1807 int* left, int* top,
1808 int* right, int* bottom)
1809{
1810 long* extents = NULL;
1811
1812 if (window->monitor || !window->decorated)
1813 return;
1814
1815 if (_glfw.x11.NET_FRAME_EXTENTS == None)
1816 return;
1817
1818 if (!_glfwPlatformWindowVisible(window) &&
1819 _glfw.x11.NET_REQUEST_FRAME_EXTENTS)
1820 {
1821 XEvent event;
1822 double timeout = 0.5;
1823
1824 // Ensure _NET_FRAME_EXTENTS is set, allowing glfwGetWindowFrameSize to
1825 // function before the window is mapped
1826 sendEventToWM(window, _glfw.x11.NET_REQUEST_FRAME_EXTENTS,
1827 0, 0, 0, 0, 0);
1828
1829 // HACK: Use a timeout because earlier versions of some window managers
1830 // (at least Unity, Fluxbox and Xfwm) failed to send the reply
1831 // They have been fixed but broken versions are still in the wild
1832 // If you are affected by this and your window manager is NOT
1833 // listed above, PLEASE report it to their and our issue trackers
1834 while (!XCheckIfEvent(_glfw.x11.display,
1835 &event,
1836 isFrameExtentsEvent,
1837 (XPointer) window))
1838 {
1839 if (!waitForEvent(&timeout))
1840 {
1841 _glfwInputError(GLFW_PLATFORM_ERROR,
1842 "X11: The window manager has a broken _NET_REQUEST_FRAME_EXTENTS implementation; please report this issue");
1843 return;
1844 }
1845 }
1846 }
1847
1848 if (_glfwGetWindowPropertyX11(window->x11.handle,
1849 _glfw.x11.NET_FRAME_EXTENTS,
1850 XA_CARDINAL,
1851 (unsigned char**) &extents) == 4)
1852 {
1853 if (left)
1854 *left = extents[0];
1855 if (top)
1856 *top = extents[2];
1857 if (right)
1858 *right = extents[1];
1859 if (bottom)
1860 *bottom = extents[3];
1861 }
1862
1863 if (extents)
1864 XFree(extents);
1865}
1866
1867void _glfwPlatformIconifyWindow(_GLFWwindow* window)
1868{
1869 if (window->x11.overrideRedirect)
1870 {
1871 // Override-redirect windows cannot be iconified or restored, as those
1872 // tasks are performed by the window manager
1873 _glfwInputError(GLFW_PLATFORM_ERROR,
1874 "X11: Iconification of full screen windows requires a WM that supports EWMH full screen");
1875 return;
1876 }
1877
1878 XIconifyWindow(_glfw.x11.display, window->x11.handle, _glfw.x11.screen);
1879 XFlush(_glfw.x11.display);
1880}
1881
1882void _glfwPlatformRestoreWindow(_GLFWwindow* window)
1883{
1884 if (window->x11.overrideRedirect)
1885 {
1886 // Override-redirect windows cannot be iconified or restored, as those
1887 // tasks are performed by the window manager
1888 _glfwInputError(GLFW_PLATFORM_ERROR,
1889 "X11: Iconification of full screen windows requires a WM that supports EWMH full screen");
1890 return;
1891 }
1892
1893 if (_glfwPlatformWindowIconified(window))
1894 {
1895 XMapWindow(_glfw.x11.display, window->x11.handle);
1896 waitForVisibilityNotify(window);
1897 }
1898 else if (_glfwPlatformWindowVisible(window))
1899 {
1900 if (_glfw.x11.NET_WM_STATE &&
1901 _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
1902 _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
1903 {
1904 sendEventToWM(window,
1905 _glfw.x11.NET_WM_STATE,
1906 _NET_WM_STATE_REMOVE,
1907 _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
1908 _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
1909 1, 0);
1910 }
1911 }
1912
1913 XFlush(_glfw.x11.display);
1914}
1915
1916void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
1917{
1918 if (_glfw.x11.NET_WM_STATE &&
1919 _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
1920 _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
1921 {
1922 sendEventToWM(window,
1923 _glfw.x11.NET_WM_STATE,
1924 _NET_WM_STATE_ADD,
1925 _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
1926 _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
1927 1, 0);
1928 XFlush(_glfw.x11.display);
1929 }
1930}
1931
1932void _glfwPlatformShowWindow(_GLFWwindow* window)
1933{
1934 if (_glfwPlatformWindowVisible(window))
1935 return;
1936
1937 XMapWindow(_glfw.x11.display, window->x11.handle);
1938 waitForVisibilityNotify(window);
1939}
1940
1941void _glfwPlatformHideWindow(_GLFWwindow* window)
1942{
1943 XUnmapWindow(_glfw.x11.display, window->x11.handle);
1944 XFlush(_glfw.x11.display);
1945}
1946
1947void _glfwPlatformFocusWindow(_GLFWwindow* window)
1948{
1949 if (_glfw.x11.NET_ACTIVE_WINDOW)
1950 sendEventToWM(window, _glfw.x11.NET_ACTIVE_WINDOW, 1, 0, 0, 0, 0);
1951 else
1952 {
1953 XRaiseWindow(_glfw.x11.display, window->x11.handle);
1954 XSetInputFocus(_glfw.x11.display, window->x11.handle,
1955 RevertToParent, CurrentTime);
1956 }
1957
1958 XFlush(_glfw.x11.display);
1959}
1960
1961void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
1962 _GLFWmonitor* monitor,
1963 int xpos, int ypos,
1964 int width, int height,
1965 int refreshRate)
1966{
1967 if (window->monitor == monitor)
1968 {
1969 if (monitor)
1970 {
1971 if (monitor->window == window)
1972 acquireMonitor(window);
1973 }
1974 else
1975 {
1976 XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
1977 xpos, ypos, width, height);
1978 }
1979
1980 return;
1981 }
1982
1983 if (window->monitor)
1984 releaseMonitor(window);
1985
1986 _glfwInputWindowMonitorChange(window, monitor);
1987 updateNormalHints(window, width, height);
1988 updateWindowMode(window);
1989
1990 if (window->monitor)
1991 {
1992 XMapRaised(_glfw.x11.display, window->x11.handle);
1993 if (waitForVisibilityNotify(window))
1994 acquireMonitor(window);
1995 }
1996 else
1997 {
1998 XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
1999 xpos, ypos, width, height);
2000 }
2001
2002 XFlush(_glfw.x11.display);
2003}
2004
2005int _glfwPlatformWindowFocused(_GLFWwindow* window)
2006{
2007 Window focused;
2008 int state;
2009
2010 XGetInputFocus(_glfw.x11.display, &focused, &state);
2011 return window->x11.handle == focused;
2012}
2013
2014int _glfwPlatformWindowIconified(_GLFWwindow* window)
2015{
2016 return getWindowState(window) == IconicState;
2017}
2018
2019int _glfwPlatformWindowVisible(_GLFWwindow* window)
2020{
2021 XWindowAttributes wa;
2022 XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &wa);
2023 return wa.map_state == IsViewable;
2024}
2025
2026int _glfwPlatformWindowMaximized(_GLFWwindow* window)
2027{
2028 Atom* states;
2029 unsigned long i;
2030 GLFWbool maximized = GLFW_FALSE;
2031 const unsigned long count =
2032 _glfwGetWindowPropertyX11(window->x11.handle,
2033 _glfw.x11.NET_WM_STATE,
2034 XA_ATOM,
2035 (unsigned char**) &states);
2036
2037 for (i = 0; i < count; i++)
2038 {
2039 if (states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
2040 states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
2041 {
2042 maximized = GLFW_TRUE;
2043 break;
2044 }
2045 }
2046
2047 XFree(states);
2048 return maximized;
2049}
2050
2051void _glfwPlatformPollEvents(void)
2052{
2053 _glfwPollJoystickEvents();
2054
2055 int count = XPending(_glfw.x11.display);
2056 while (count--)
2057 {
2058 XEvent event;
2059 XNextEvent(_glfw.x11.display, &event);
2060 processEvent(&event);
2061 }
2062
2063 if (_glfw.x11.disabledCursorWindow)
2064 centerCursor(_glfw.x11.disabledCursorWindow);
2065
2066 XFlush(_glfw.x11.display);
2067}
2068
2069void _glfwPlatformWaitEvents(void)
2070{
2071 while (!XPending(_glfw.x11.display))
2072 waitForEvent(NULL);
2073
2074 _glfwPlatformPollEvents();
2075}
2076
2077void _glfwPlatformWaitEventsTimeout(double timeout)
2078{
2079 while (!XPending(_glfw.x11.display))
2080 {
2081 if (!waitForEvent(&timeout))
2082 break;
2083 }
2084
2085 _glfwPlatformPollEvents();
2086}
2087
2088void _glfwPlatformPostEmptyEvent(void)
2089{
2090 XEvent event;
2091 _GLFWwindow* window = _glfw.windowListHead;
2092
2093 memset(&event, 0, sizeof(event));
2094 event.type = ClientMessage;
2095 event.xclient.window = window->x11.handle;
2096 event.xclient.format = 32; // Data is 32-bit longs
2097 event.xclient.message_type = _glfw.x11.NULL_;
2098
2099 XSendEvent(_glfw.x11.display, window->x11.handle, False, 0, &event);
2100 XFlush(_glfw.x11.display);
2101}
2102
2103void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
2104{
2105 Window root, child;
2106 int rootX, rootY, childX, childY;
2107 unsigned int mask;
2108
2109 XQueryPointer(_glfw.x11.display, window->x11.handle,
2110 &root, &child,
2111 &rootX, &rootY, &childX, &childY,
2112 &mask);
2113
2114 if (xpos)
2115 *xpos = childX;
2116 if (ypos)
2117 *ypos = childY;
2118}
2119
2120void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
2121{
2122 // Store the new position so it can be recognized later
2123 window->x11.warpCursorPosX = (int) x;
2124 window->x11.warpCursorPosY = (int) y;
2125
2126 XWarpPointer(_glfw.x11.display, None, window->x11.handle,
2127 0,0,0,0, (int) x, (int) y);
2128 XFlush(_glfw.x11.display);
2129}
2130
2131void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
2132{
2133 if (mode == GLFW_CURSOR_DISABLED)
2134 {
2135 _glfw.x11.disabledCursorWindow = window;
2136 _glfwPlatformGetCursorPos(window,
2137 &_glfw.x11.restoreCursorPosX,
2138 &_glfw.x11.restoreCursorPosY);
2139 centerCursor(window);
2140 XGrabPointer(_glfw.x11.display, window->x11.handle, True,
2141 ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
2142 GrabModeAsync, GrabModeAsync,
2143 window->x11.handle, _glfw.x11.cursor, CurrentTime);
2144 }
2145 else if (_glfw.x11.disabledCursorWindow == window)
2146 {
2147 _glfw.x11.disabledCursorWindow = NULL;
2148 XUngrabPointer(_glfw.x11.display, CurrentTime);
2149 _glfwPlatformSetCursorPos(window,
2150 _glfw.x11.restoreCursorPosX,
2151 _glfw.x11.restoreCursorPosY);
2152 }
2153
2154 updateCursorImage(window);
2155 XFlush(_glfw.x11.display);
2156}
2157
2158const char* _glfwPlatformGetKeyName(int key, int scancode)
2159{
2160 KeySym keysym;
2161 int extra;
2162
2163 if (!_glfw.x11.xkb.available)
2164 return NULL;
2165
2166 if (key != GLFW_KEY_UNKNOWN)
2167 scancode = _glfw.x11.nativeKeys[key];
2168
2169 if (!_glfwIsPrintable(_glfw.x11.publicKeys[scancode]))
2170 return NULL;
2171
2172 keysym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, 0, 0);
2173 if (keysym == NoSymbol)
2174 return NULL;
2175
2176 XkbTranslateKeySym(_glfw.x11.display, &keysym, 0,
2177 _glfw.x11.keyName, sizeof(_glfw.x11.keyName),
2178 &extra);
2179
2180 if (!strlen(_glfw.x11.keyName))
2181 return NULL;
2182
2183 return _glfw.x11.keyName;
2184}
2185
2186int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
2187 const GLFWimage* image,
2188 int xhot, int yhot)
2189{
2190 cursor->x11.handle = _glfwCreateCursorX11(image, xhot, yhot);
2191 if (!cursor->x11.handle)
2192 return GLFW_FALSE;
2193
2194 return GLFW_TRUE;
2195}
2196
2197int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape)
2198{
2199 cursor->x11.handle = XCreateFontCursor(_glfw.x11.display,
2200 translateCursorShape(shape));
2201 if (!cursor->x11.handle)
2202 {
2203 _glfwInputError(GLFW_PLATFORM_ERROR,
2204 "X11: Failed to create standard cursor");
2205 return GLFW_FALSE;
2206 }
2207
2208 return GLFW_TRUE;
2209}
2210
2211void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
2212{
2213 if (cursor->x11.handle)
2214 XFreeCursor(_glfw.x11.display, cursor->x11.handle);
2215}
2216
2217void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
2218{
2219 if (window->cursorMode == GLFW_CURSOR_NORMAL)
2220 {
2221 updateCursorImage(window);
2222 XFlush(_glfw.x11.display);
2223 }
2224}
2225
2226void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string)
2227{
2228 free(_glfw.x11.clipboardString);
2229 _glfw.x11.clipboardString = strdup(string);
2230
2231 XSetSelectionOwner(_glfw.x11.display,
2232 _glfw.x11.CLIPBOARD,
2233 window->x11.handle, CurrentTime);
2234
2235 if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) !=
2236 window->x11.handle)
2237 {
2238 _glfwInputError(GLFW_PLATFORM_ERROR,
2239 "X11: Failed to become owner of clipboard selection");
2240 }
2241}
2242
2243const char* _glfwPlatformGetClipboardString(_GLFWwindow* window)
2244{
2245 size_t i;
2246 const Atom formats[] = { _glfw.x11.UTF8_STRING,
2247 _glfw.x11.COMPOUND_STRING,
2248 XA_STRING };
2249 const size_t formatCount = sizeof(formats) / sizeof(formats[0]);
2250
2251 if (findWindowByHandle(XGetSelectionOwner(_glfw.x11.display,
2252 _glfw.x11.CLIPBOARD)))
2253 {
2254 // Instead of doing a large number of X round-trips just to put this
2255 // string into a window property and then read it back, just return it
2256 return _glfw.x11.clipboardString;
2257 }
2258
2259 free(_glfw.x11.clipboardString);
2260 _glfw.x11.clipboardString = NULL;
2261
2262 for (i = 0; i < formatCount; i++)
2263 {
2264 char* data;
2265 XEvent event;
2266
2267 XConvertSelection(_glfw.x11.display,
2268 _glfw.x11.CLIPBOARD,
2269 formats[i],
2270 _glfw.x11.GLFW_SELECTION,
2271 window->x11.handle, CurrentTime);
2272
2273 while (!XCheckTypedEvent(_glfw.x11.display, SelectionNotify, &event))
2274 waitForEvent(NULL);
2275
2276 if (event.xselection.property == None)
2277 continue;
2278
2279 if (_glfwGetWindowPropertyX11(event.xselection.requestor,
2280 event.xselection.property,
2281 event.xselection.target,
2282 (unsigned char**) &data))
2283 {
2284 _glfw.x11.clipboardString = strdup(data);
2285 }
2286
2287 XFree(data);
2288
2289 XDeleteProperty(_glfw.x11.display,
2290 event.xselection.requestor,
2291 event.xselection.property);
2292
2293 if (_glfw.x11.clipboardString)
2294 break;
2295 }
2296
2297 if (_glfw.x11.clipboardString == NULL)
2298 {
2299 _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
2300 "X11: Failed to convert clipboard to string");
2301 }
2302
2303 return _glfw.x11.clipboardString;
2304}
2305
2306char** _glfwPlatformGetRequiredInstanceExtensions(uint32_t* count)
2307{
2308 char** extensions;
2309
2310 *count = 0;
2311
2312 if (!_glfw.vk.KHR_xcb_surface || !_glfw.x11.x11xcb.handle)
2313 {
2314 if (!_glfw.vk.KHR_xlib_surface)
2315 return NULL;
2316 }
2317
2318 extensions = calloc(2, sizeof(char*));
2319 extensions[0] = strdup("VK_KHR_surface");
2320
2321 if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
2322 extensions[1] = strdup("VK_KHR_xcb_surface");
2323 else
2324 extensions[1] = strdup("VK_KHR_xlib_surface");
2325
2326 *count = 2;
2327 return extensions;
2328}
2329
2330int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
2331 VkPhysicalDevice device,
2332 uint32_t queuefamily)
2333{
2334 VisualID visualID = XVisualIDFromVisual(DefaultVisual(_glfw.x11.display,
2335 _glfw.x11.screen));
2336
2337 if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
2338 {
2339 PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR vkGetPhysicalDeviceXcbPresentationSupportKHR =
2340 (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)
2341 vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR");
2342 if (!vkGetPhysicalDeviceXcbPresentationSupportKHR)
2343 {
2344 _glfwInputError(GLFW_API_UNAVAILABLE,
2345 "X11: Vulkan instance missing VK_KHR_xcb_surface extension");
2346 return GLFW_FALSE;
2347 }
2348
2349 xcb_connection_t* connection =
2350 _glfw.x11.x11xcb.XGetXCBConnection(_glfw.x11.display);
2351 if (!connection)
2352 {
2353 _glfwInputError(GLFW_PLATFORM_ERROR,
2354 "X11: Failed to retrieve XCB connection");
2355 return GLFW_FALSE;
2356 }
2357
2358 return vkGetPhysicalDeviceXcbPresentationSupportKHR(device,
2359 queuefamily,
2360 connection,
2361 visualID);
2362 }
2363 else
2364 {
2365 PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR =
2366 (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)
2367 vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR");
2368 if (!vkGetPhysicalDeviceXlibPresentationSupportKHR)
2369 {
2370 _glfwInputError(GLFW_API_UNAVAILABLE,
2371 "X11: Vulkan instance missing VK_KHR_xlib_surface extension");
2372 return GLFW_FALSE;
2373 }
2374
2375 return vkGetPhysicalDeviceXlibPresentationSupportKHR(device,
2376 queuefamily,
2377 _glfw.x11.display,
2378 visualID);
2379 }
2380}
2381
2382VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
2383 _GLFWwindow* window,
2384 const VkAllocationCallbacks* allocator,
2385 VkSurfaceKHR* surface)
2386{
2387 if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle)
2388 {
2389 VkResult err;
2390 VkXcbSurfaceCreateInfoKHR sci;
2391 PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR;
2392
2393 xcb_connection_t* connection =
2394 _glfw.x11.x11xcb.XGetXCBConnection(_glfw.x11.display);
2395 if (!connection)
2396 {
2397 _glfwInputError(GLFW_PLATFORM_ERROR,
2398 "X11: Failed to retrieve XCB connection");
2399 return VK_ERROR_EXTENSION_NOT_PRESENT;
2400 }
2401
2402 vkCreateXcbSurfaceKHR = (PFN_vkCreateXcbSurfaceKHR)
2403 vkGetInstanceProcAddr(instance, "vkCreateXcbSurfaceKHR");
2404 if (!vkCreateXcbSurfaceKHR)
2405 {
2406 _glfwInputError(GLFW_API_UNAVAILABLE,
2407 "X11: Vulkan instance missing VK_KHR_xcb_surface extension");
2408 return VK_ERROR_EXTENSION_NOT_PRESENT;
2409 }
2410
2411 memset(&sci, 0, sizeof(sci));
2412 sci.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
2413 sci.connection = connection;
2414 sci.window = window->x11.handle;
2415
2416 err = vkCreateXcbSurfaceKHR(instance, &sci, allocator, surface);
2417 if (err)
2418 {
2419 _glfwInputError(GLFW_PLATFORM_ERROR,
2420 "X11: Failed to create Vulkan XCB surface: %s",
2421 _glfwGetVulkanResultString(err));
2422 }
2423
2424 return err;
2425 }
2426 else
2427 {
2428 VkResult err;
2429 VkXlibSurfaceCreateInfoKHR sci;
2430 PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR;
2431
2432 vkCreateXlibSurfaceKHR = (PFN_vkCreateXlibSurfaceKHR)
2433 vkGetInstanceProcAddr(instance, "vkCreateXlibSurfaceKHR");
2434 if (!vkCreateXlibSurfaceKHR)
2435 {
2436 _glfwInputError(GLFW_API_UNAVAILABLE,
2437 "X11: Vulkan instance missing VK_KHR_xlib_surface extension");
2438 return VK_ERROR_EXTENSION_NOT_PRESENT;
2439 }
2440
2441 memset(&sci, 0, sizeof(sci));
2442 sci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
2443 sci.dpy = _glfw.x11.display;
2444 sci.window = window->x11.handle;
2445
2446 err = vkCreateXlibSurfaceKHR(instance, &sci, allocator, surface);
2447 if (err)
2448 {
2449 _glfwInputError(GLFW_PLATFORM_ERROR,
2450 "X11: Failed to create Vulkan X11 surface: %s",
2451 _glfwGetVulkanResultString(err));
2452 }
2453
2454 return err;
2455 }
2456}
2457
2458
2459//////////////////////////////////////////////////////////////////////////
2460////// GLFW native API //////
2461//////////////////////////////////////////////////////////////////////////
2462
2463GLFWAPI Display* glfwGetX11Display(void)
2464{
2465 _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
2466 return _glfw.x11.display;
2467}
2468
2469GLFWAPI Window glfwGetX11Window(GLFWwindow* handle)
2470{
2471 _GLFWwindow* window = (_GLFWwindow*) handle;
2472 _GLFW_REQUIRE_INIT_OR_RETURN(None);
2473 return window->x11.handle;
2474}
2475
2476