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