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 //
waitForEvent(double * timeout)56 static 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 //
waitForVisibilityNotify(_GLFWwindow * window)98 static 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 //
getWindowState(_GLFWwindow * window)117 static 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 //
isSelectionEvent(Display * display,XEvent * event,XPointer pointer)139 static 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 //
isFrameExtentsEvent(Display * display,XEvent * event,XPointer pointer)148 static 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 //
translateCursorShape(int shape)159 static 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 //
translateState(int state)182 static 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 //
translateKey(int scancode)200 static 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 //
findWindowByHandle(Window handle)211 static _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 //
sendEventToWM(_GLFWwindow * window,Atom type,long a,long b,long c,long d,long e)228 static 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 //
updateNormalHints(_GLFWwindow * window,int width,int height)252 static 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 //
updateWindowMode(_GLFWwindow * window)301 static 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 //
parseUriList(char * text,int * count)394 static 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 //
centerCursor(_GLFWwindow * window)439 static 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 //
updateCursorImage(_GLFWwindow * window)448 static 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 //
createNativeWindow(_GLFWwindow * window,const _GLFWwndconfig * wndconfig,Visual * visual,int depth)466 static 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 //
writeTargetToProperty(const XSelectionRequestEvent * request)660 static 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
handleSelectionClear(XEvent * event)791 static void handleSelectionClear(XEvent* event)
792 {
793 free(_glfw.x11.clipboardString);
794 _glfw.x11.clipboardString = NULL;
795 }
796
handleSelectionRequest(XEvent * event)797 static 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
pushSelectionToManager(_GLFWwindow * window)815 static 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 //
acquireMonitor(_GLFWwindow * window)862 static 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 //
releaseMonitor(_GLFWwindow * window)904 static 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)
decodeUTF8(const char ** s)929 static 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 //
processEvent(XEvent * event)952 static 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 //
_glfwGetWindowPropertyX11(Window window,Atom property,Atom type,unsigned char ** value)1505 unsigned 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
_glfwPlatformCreateWindow(_GLFWwindow * window,const _GLFWwndconfig * wndconfig,const _GLFWctxconfig * ctxconfig,const _GLFWfbconfig * fbconfig)1538 int _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
_glfwPlatformDestroyWindow(_GLFWwindow * window)1600 void _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
_glfwPlatformSetWindowTitle(_GLFWwindow * window,const char * title)1640 void _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
_glfwPlatformSetWindowIcon(_GLFWwindow * window,int count,const GLFWimage * images)1671 void _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
_glfwPlatformGetWindowPos(_GLFWwindow * window,int * xpos,int * ypos)1716 void _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
_glfwPlatformSetWindowPos(_GLFWwindow * window,int xpos,int ypos)1730 void _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
_glfwPlatformGetWindowSize(_GLFWwindow * window,int * width,int * height)1754 void _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
_glfwPlatformSetWindowSize(_GLFWwindow * window,int width,int height)1765 void _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
_glfwPlatformSetWindowSizeLimits(_GLFWwindow * window,int minwidth,int minheight,int maxwidth,int maxheight)1783 void _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
_glfwPlatformSetWindowAspectRatio(_GLFWwindow * window,int numer,int denom)1793 void _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
_glfwPlatformGetFramebufferSize(_GLFWwindow * window,int * width,int * height)1801 void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height)
1802 {
1803 _glfwPlatformGetWindowSize(window, width, height);
1804 }
1805
_glfwPlatformGetWindowFrameSize(_GLFWwindow * window,int * left,int * top,int * right,int * bottom)1806 void _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
_glfwPlatformIconifyWindow(_GLFWwindow * window)1867 void _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
_glfwPlatformRestoreWindow(_GLFWwindow * window)1882 void _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
_glfwPlatformMaximizeWindow(_GLFWwindow * window)1916 void _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
_glfwPlatformShowWindow(_GLFWwindow * window)1932 void _glfwPlatformShowWindow(_GLFWwindow* window)
1933 {
1934 if (_glfwPlatformWindowVisible(window))
1935 return;
1936
1937 XMapWindow(_glfw.x11.display, window->x11.handle);
1938 waitForVisibilityNotify(window);
1939 }
1940
_glfwPlatformHideWindow(_GLFWwindow * window)1941 void _glfwPlatformHideWindow(_GLFWwindow* window)
1942 {
1943 XUnmapWindow(_glfw.x11.display, window->x11.handle);
1944 XFlush(_glfw.x11.display);
1945 }
1946
_glfwPlatformFocusWindow(_GLFWwindow * window)1947 void _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
_glfwPlatformSetWindowMonitor(_GLFWwindow * window,_GLFWmonitor * monitor,int xpos,int ypos,int width,int height,int refreshRate)1961 void _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
_glfwPlatformWindowFocused(_GLFWwindow * window)2005 int _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
_glfwPlatformWindowIconified(_GLFWwindow * window)2014 int _glfwPlatformWindowIconified(_GLFWwindow* window)
2015 {
2016 return getWindowState(window) == IconicState;
2017 }
2018
_glfwPlatformWindowVisible(_GLFWwindow * window)2019 int _glfwPlatformWindowVisible(_GLFWwindow* window)
2020 {
2021 XWindowAttributes wa;
2022 XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &wa);
2023 return wa.map_state == IsViewable;
2024 }
2025
_glfwPlatformWindowMaximized(_GLFWwindow * window)2026 int _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
_glfwPlatformPollEvents(void)2051 void _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
_glfwPlatformWaitEvents(void)2069 void _glfwPlatformWaitEvents(void)
2070 {
2071 while (!XPending(_glfw.x11.display))
2072 waitForEvent(NULL);
2073
2074 _glfwPlatformPollEvents();
2075 }
2076
_glfwPlatformWaitEventsTimeout(double timeout)2077 void _glfwPlatformWaitEventsTimeout(double timeout)
2078 {
2079 while (!XPending(_glfw.x11.display))
2080 {
2081 if (!waitForEvent(&timeout))
2082 break;
2083 }
2084
2085 _glfwPlatformPollEvents();
2086 }
2087
_glfwPlatformPostEmptyEvent(void)2088 void _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
_glfwPlatformGetCursorPos(_GLFWwindow * window,double * xpos,double * ypos)2103 void _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
_glfwPlatformSetCursorPos(_GLFWwindow * window,double x,double y)2120 void _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
_glfwPlatformSetCursorMode(_GLFWwindow * window,int mode)2131 void _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
_glfwPlatformGetKeyName(int key,int scancode)2158 const 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
_glfwPlatformCreateCursor(_GLFWcursor * cursor,const GLFWimage * image,int xhot,int yhot)2186 int _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
_glfwPlatformCreateStandardCursor(_GLFWcursor * cursor,int shape)2197 int _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
_glfwPlatformDestroyCursor(_GLFWcursor * cursor)2211 void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
2212 {
2213 if (cursor->x11.handle)
2214 XFreeCursor(_glfw.x11.display, cursor->x11.handle);
2215 }
2216
_glfwPlatformSetCursor(_GLFWwindow * window,_GLFWcursor * cursor)2217 void _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
_glfwPlatformSetClipboardString(_GLFWwindow * window,const char * string)2226 void _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
_glfwPlatformGetClipboardString(_GLFWwindow * window)2243 const 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
_glfwPlatformGetRequiredInstanceExtensions(uint32_t * count)2306 char** _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
_glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,VkPhysicalDevice device,uint32_t queuefamily)2330 int _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
_glfwPlatformCreateWindowSurface(VkInstance instance,_GLFWwindow * window,const VkAllocationCallbacks * allocator,VkSurfaceKHR * surface)2382 VkResult _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
glfwGetX11Display(void)2463 GLFWAPI Display* glfwGetX11Display(void)
2464 {
2465 _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
2466 return _glfw.x11.display;
2467 }
2468
glfwGetX11Window(GLFWwindow * handle)2469 GLFWAPI 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