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