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