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