x11_window.c (101173B)
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 return; 1200 } 1201 } 1202 1203 if (event->type == GenericEvent) 1204 { 1205 if (_glfw.x11.xi.available) 1206 { 1207 _GLFWwindow* window = _glfw.x11.disabledCursorWindow; 1208 1209 if (window && 1210 window->rawMouseMotion && 1211 event->xcookie.extension == _glfw.x11.xi.majorOpcode && 1212 XGetEventData(_glfw.x11.display, &event->xcookie) && 1213 event->xcookie.evtype == XI_RawMotion) 1214 { 1215 XIRawEvent* re = event->xcookie.data; 1216 if (re->valuators.mask_len) 1217 { 1218 const double* values = re->raw_values; 1219 double xpos = window->virtualCursorPosX; 1220 double ypos = window->virtualCursorPosY; 1221 1222 if (XIMaskIsSet(re->valuators.mask, 0)) 1223 { 1224 xpos += *values; 1225 values++; 1226 } 1227 1228 if (XIMaskIsSet(re->valuators.mask, 1)) 1229 ypos += *values; 1230 1231 _glfwInputCursorPos(window, xpos, ypos); 1232 } 1233 } 1234 1235 XFreeEventData(_glfw.x11.display, &event->xcookie); 1236 } 1237 1238 return; 1239 } 1240 1241 if (event->type == SelectionClear) 1242 { 1243 handleSelectionClear(event); 1244 return; 1245 } 1246 else if (event->type == SelectionRequest) 1247 { 1248 handleSelectionRequest(event); 1249 return; 1250 } 1251 1252 _GLFWwindow* window = NULL; 1253 if (XFindContext(_glfw.x11.display, 1254 event->xany.window, 1255 _glfw.x11.context, 1256 (XPointer*) &window) != 0) 1257 { 1258 // This is an event for a window that has already been destroyed 1259 return; 1260 } 1261 1262 switch (event->type) 1263 { 1264 case ReparentNotify: 1265 { 1266 window->x11.parent = event->xreparent.parent; 1267 return; 1268 } 1269 1270 case KeyPress: 1271 { 1272 const int key = translateKey(keycode); 1273 const int mods = translateState(event->xkey.state); 1274 const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); 1275 1276 if (window->x11.ic) 1277 { 1278 // HACK: Do not report the key press events duplicated by XIM 1279 // Duplicate key releases are filtered out implicitly by 1280 // the GLFW key repeat logic in _glfwInputKey 1281 // A timestamp per key is used to handle simultaneous keys 1282 // NOTE: Always allow the first event for each key through 1283 // (the server never sends a timestamp of zero) 1284 // NOTE: Timestamp difference is compared to handle wrap-around 1285 Time diff = event->xkey.time - window->x11.keyPressTimes[keycode]; 1286 if (diff == event->xkey.time || (diff > 0 && diff < (1 << 31))) 1287 { 1288 if (keycode) 1289 _glfwInputKey(window, key, keycode, GLFW_PRESS, mods); 1290 1291 window->x11.keyPressTimes[keycode] = event->xkey.time; 1292 } 1293 1294 if (!filtered) 1295 { 1296 int count; 1297 Status status; 1298 #if defined(X_HAVE_UTF8_STRING) 1299 char buffer[100]; 1300 char* chars = buffer; 1301 1302 count = Xutf8LookupString(window->x11.ic, 1303 &event->xkey, 1304 buffer, sizeof(buffer) - 1, 1305 NULL, &status); 1306 1307 if (status == XBufferOverflow) 1308 { 1309 chars = calloc(count + 1, 1); 1310 count = Xutf8LookupString(window->x11.ic, 1311 &event->xkey, 1312 chars, count, 1313 NULL, &status); 1314 } 1315 1316 if (status == XLookupChars || status == XLookupBoth) 1317 { 1318 const char* c = chars; 1319 chars[count] = '\0'; 1320 while (c - chars < count) 1321 _glfwInputChar(window, decodeUTF8(&c), mods, plain); 1322 } 1323 #else /*X_HAVE_UTF8_STRING*/ 1324 wchar_t buffer[16]; 1325 wchar_t* chars = buffer; 1326 1327 count = XwcLookupString(window->x11.ic, 1328 &event->xkey, 1329 buffer, 1330 sizeof(buffer) / sizeof(wchar_t), 1331 NULL, 1332 &status); 1333 1334 if (status == XBufferOverflow) 1335 { 1336 chars = calloc(count, sizeof(wchar_t)); 1337 count = XwcLookupString(window->x11.ic, 1338 &event->xkey, 1339 chars, count, 1340 NULL, &status); 1341 } 1342 1343 if (status == XLookupChars || status == XLookupBoth) 1344 { 1345 int i; 1346 for (i = 0; i < count; i++) 1347 _glfwInputChar(window, chars[i], mods, plain); 1348 } 1349 #endif /*X_HAVE_UTF8_STRING*/ 1350 1351 if (chars != buffer) 1352 free(chars); 1353 } 1354 } 1355 else 1356 { 1357 KeySym keysym; 1358 XLookupString(&event->xkey, NULL, 0, &keysym, NULL); 1359 1360 _glfwInputKey(window, key, keycode, GLFW_PRESS, mods); 1361 1362 const long character = _glfwKeySym2Unicode(keysym); 1363 if (character != -1) 1364 _glfwInputChar(window, character, mods, plain); 1365 } 1366 1367 return; 1368 } 1369 1370 case KeyRelease: 1371 { 1372 const int key = translateKey(keycode); 1373 const int mods = translateState(event->xkey.state); 1374 1375 if (!_glfw.x11.xkb.detectable) 1376 { 1377 // HACK: Key repeat events will arrive as KeyRelease/KeyPress 1378 // pairs with similar or identical time stamps 1379 // The key repeat logic in _glfwInputKey expects only key 1380 // presses to repeat, so detect and discard release events 1381 if (XEventsQueued(_glfw.x11.display, QueuedAfterReading)) 1382 { 1383 XEvent next; 1384 XPeekEvent(_glfw.x11.display, &next); 1385 1386 if (next.type == KeyPress && 1387 next.xkey.window == event->xkey.window && 1388 next.xkey.keycode == keycode) 1389 { 1390 // HACK: The time of repeat events sometimes doesn't 1391 // match that of the press event, so add an 1392 // epsilon 1393 // Toshiyuki Takahashi can press a button 1394 // 16 times per second so it's fairly safe to 1395 // assume that no human is pressing the key 50 1396 // times per second (value is ms) 1397 if ((next.xkey.time - event->xkey.time) < 20) 1398 { 1399 // This is very likely a server-generated key repeat 1400 // event, so ignore it 1401 return; 1402 } 1403 } 1404 } 1405 } 1406 1407 _glfwInputKey(window, key, keycode, GLFW_RELEASE, mods); 1408 return; 1409 } 1410 1411 case ButtonPress: 1412 { 1413 const int mods = translateState(event->xbutton.state); 1414 1415 if (event->xbutton.button == Button1) 1416 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, mods); 1417 else if (event->xbutton.button == Button2) 1418 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, mods); 1419 else if (event->xbutton.button == Button3) 1420 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS, mods); 1421 1422 // Modern X provides scroll events as mouse button presses 1423 else if (event->xbutton.button == Button4) 1424 _glfwInputScroll(window, 0.0, 1.0); 1425 else if (event->xbutton.button == Button5) 1426 _glfwInputScroll(window, 0.0, -1.0); 1427 else if (event->xbutton.button == Button6) 1428 _glfwInputScroll(window, 1.0, 0.0); 1429 else if (event->xbutton.button == Button7) 1430 _glfwInputScroll(window, -1.0, 0.0); 1431 1432 else 1433 { 1434 // Additional buttons after 7 are treated as regular buttons 1435 // We subtract 4 to fill the gap left by scroll input above 1436 _glfwInputMouseClick(window, 1437 event->xbutton.button - Button1 - 4, 1438 GLFW_PRESS, 1439 mods); 1440 } 1441 1442 return; 1443 } 1444 1445 case ButtonRelease: 1446 { 1447 const int mods = translateState(event->xbutton.state); 1448 1449 if (event->xbutton.button == Button1) 1450 { 1451 _glfwInputMouseClick(window, 1452 GLFW_MOUSE_BUTTON_LEFT, 1453 GLFW_RELEASE, 1454 mods); 1455 } 1456 else if (event->xbutton.button == Button2) 1457 { 1458 _glfwInputMouseClick(window, 1459 GLFW_MOUSE_BUTTON_MIDDLE, 1460 GLFW_RELEASE, 1461 mods); 1462 } 1463 else if (event->xbutton.button == Button3) 1464 { 1465 _glfwInputMouseClick(window, 1466 GLFW_MOUSE_BUTTON_RIGHT, 1467 GLFW_RELEASE, 1468 mods); 1469 } 1470 else if (event->xbutton.button > Button7) 1471 { 1472 // Additional buttons after 7 are treated as regular buttons 1473 // We subtract 4 to fill the gap left by scroll input above 1474 _glfwInputMouseClick(window, 1475 event->xbutton.button - Button1 - 4, 1476 GLFW_RELEASE, 1477 mods); 1478 } 1479 1480 return; 1481 } 1482 1483 case EnterNotify: 1484 { 1485 // XEnterWindowEvent is XCrossingEvent 1486 const int x = event->xcrossing.x; 1487 const int y = event->xcrossing.y; 1488 1489 // HACK: This is a workaround for WMs (KWM, Fluxbox) that otherwise 1490 // ignore the defined cursor for hidden cursor mode 1491 if (window->cursorMode == GLFW_CURSOR_HIDDEN) 1492 updateCursorImage(window); 1493 1494 _glfwInputCursorEnter(window, GLFW_TRUE); 1495 _glfwInputCursorPos(window, x, y); 1496 1497 window->x11.lastCursorPosX = x; 1498 window->x11.lastCursorPosY = y; 1499 return; 1500 } 1501 1502 case LeaveNotify: 1503 { 1504 _glfwInputCursorEnter(window, GLFW_FALSE); 1505 return; 1506 } 1507 1508 case MotionNotify: 1509 { 1510 const int x = event->xmotion.x; 1511 const int y = event->xmotion.y; 1512 1513 if (x != window->x11.warpCursorPosX || 1514 y != window->x11.warpCursorPosY) 1515 { 1516 // The cursor was moved by something other than GLFW 1517 1518 if (window->cursorMode == GLFW_CURSOR_DISABLED) 1519 { 1520 if (_glfw.x11.disabledCursorWindow != window) 1521 return; 1522 if (window->rawMouseMotion) 1523 return; 1524 1525 const int dx = x - window->x11.lastCursorPosX; 1526 const int dy = y - window->x11.lastCursorPosY; 1527 1528 _glfwInputCursorPos(window, 1529 window->virtualCursorPosX + dx, 1530 window->virtualCursorPosY + dy); 1531 } 1532 else 1533 _glfwInputCursorPos(window, x, y); 1534 } 1535 1536 window->x11.lastCursorPosX = x; 1537 window->x11.lastCursorPosY = y; 1538 return; 1539 } 1540 1541 case ConfigureNotify: 1542 { 1543 if (event->xconfigure.width != window->x11.width || 1544 event->xconfigure.height != window->x11.height) 1545 { 1546 _glfwInputFramebufferSize(window, 1547 event->xconfigure.width, 1548 event->xconfigure.height); 1549 1550 _glfwInputWindowSize(window, 1551 event->xconfigure.width, 1552 event->xconfigure.height); 1553 1554 window->x11.width = event->xconfigure.width; 1555 window->x11.height = event->xconfigure.height; 1556 } 1557 1558 int xpos = event->xconfigure.x; 1559 int ypos = event->xconfigure.y; 1560 1561 // NOTE: ConfigureNotify events from the server are in local 1562 // coordinates, so if we are reparented we need to translate 1563 // the position into root (screen) coordinates 1564 if (!event->xany.send_event && window->x11.parent != _glfw.x11.root) 1565 { 1566 _glfwGrabErrorHandlerX11(); 1567 1568 Window dummy; 1569 XTranslateCoordinates(_glfw.x11.display, 1570 window->x11.parent, 1571 _glfw.x11.root, 1572 xpos, ypos, 1573 &xpos, &ypos, 1574 &dummy); 1575 1576 _glfwReleaseErrorHandlerX11(); 1577 if (_glfw.x11.errorCode == BadWindow) 1578 return; 1579 } 1580 1581 if (xpos != window->x11.xpos || ypos != window->x11.ypos) 1582 { 1583 _glfwInputWindowPos(window, xpos, ypos); 1584 window->x11.xpos = xpos; 1585 window->x11.ypos = ypos; 1586 } 1587 1588 return; 1589 } 1590 1591 case ClientMessage: 1592 { 1593 // Custom client message, probably from the window manager 1594 1595 if (filtered) 1596 return; 1597 1598 if (event->xclient.message_type == None) 1599 return; 1600 1601 if (event->xclient.message_type == _glfw.x11.WM_PROTOCOLS) 1602 { 1603 const Atom protocol = event->xclient.data.l[0]; 1604 if (protocol == None) 1605 return; 1606 1607 if (protocol == _glfw.x11.WM_DELETE_WINDOW) 1608 { 1609 // The window manager was asked to close the window, for 1610 // example by the user pressing a 'close' window decoration 1611 // button 1612 _glfwInputWindowCloseRequest(window); 1613 } 1614 else if (protocol == _glfw.x11.NET_WM_PING) 1615 { 1616 // The window manager is pinging the application to ensure 1617 // it's still responding to events 1618 1619 XEvent reply = *event; 1620 reply.xclient.window = _glfw.x11.root; 1621 1622 XSendEvent(_glfw.x11.display, _glfw.x11.root, 1623 False, 1624 SubstructureNotifyMask | SubstructureRedirectMask, 1625 &reply); 1626 } 1627 } 1628 else if (event->xclient.message_type == _glfw.x11.XdndEnter) 1629 { 1630 // A drag operation has entered the window 1631 unsigned long i, count; 1632 Atom* formats = NULL; 1633 const GLFWbool list = event->xclient.data.l[1] & 1; 1634 1635 _glfw.x11.xdnd.source = event->xclient.data.l[0]; 1636 _glfw.x11.xdnd.version = event->xclient.data.l[1] >> 24; 1637 _glfw.x11.xdnd.format = None; 1638 1639 if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION) 1640 return; 1641 1642 if (list) 1643 { 1644 count = _glfwGetWindowPropertyX11(_glfw.x11.xdnd.source, 1645 _glfw.x11.XdndTypeList, 1646 XA_ATOM, 1647 (unsigned char**) &formats); 1648 } 1649 else 1650 { 1651 count = 3; 1652 formats = (Atom*) event->xclient.data.l + 2; 1653 } 1654 1655 for (i = 0; i < count; i++) 1656 { 1657 if (formats[i] == _glfw.x11.text_uri_list) 1658 { 1659 _glfw.x11.xdnd.format = _glfw.x11.text_uri_list; 1660 break; 1661 } 1662 } 1663 1664 if (list && formats) 1665 XFree(formats); 1666 } 1667 else if (event->xclient.message_type == _glfw.x11.XdndDrop) 1668 { 1669 // The drag operation has finished by dropping on the window 1670 Time time = CurrentTime; 1671 1672 if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION) 1673 return; 1674 1675 if (_glfw.x11.xdnd.format) 1676 { 1677 if (_glfw.x11.xdnd.version >= 1) 1678 time = event->xclient.data.l[2]; 1679 1680 // Request the chosen format from the source window 1681 XConvertSelection(_glfw.x11.display, 1682 _glfw.x11.XdndSelection, 1683 _glfw.x11.xdnd.format, 1684 _glfw.x11.XdndSelection, 1685 window->x11.handle, 1686 time); 1687 } 1688 else if (_glfw.x11.xdnd.version >= 2) 1689 { 1690 XEvent reply = { ClientMessage }; 1691 reply.xclient.window = _glfw.x11.xdnd.source; 1692 reply.xclient.message_type = _glfw.x11.XdndFinished; 1693 reply.xclient.format = 32; 1694 reply.xclient.data.l[0] = window->x11.handle; 1695 reply.xclient.data.l[1] = 0; // The drag was rejected 1696 reply.xclient.data.l[2] = None; 1697 1698 XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, 1699 False, NoEventMask, &reply); 1700 XFlush(_glfw.x11.display); 1701 } 1702 } 1703 else if (event->xclient.message_type == _glfw.x11.XdndPosition) 1704 { 1705 // The drag operation has moved over the window 1706 const int xabs = (event->xclient.data.l[2] >> 16) & 0xffff; 1707 const int yabs = (event->xclient.data.l[2]) & 0xffff; 1708 Window dummy; 1709 int xpos, ypos; 1710 1711 if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION) 1712 return; 1713 1714 XTranslateCoordinates(_glfw.x11.display, 1715 _glfw.x11.root, 1716 window->x11.handle, 1717 xabs, yabs, 1718 &xpos, &ypos, 1719 &dummy); 1720 1721 _glfwInputCursorPos(window, xpos, ypos); 1722 1723 XEvent reply = { ClientMessage }; 1724 reply.xclient.window = _glfw.x11.xdnd.source; 1725 reply.xclient.message_type = _glfw.x11.XdndStatus; 1726 reply.xclient.format = 32; 1727 reply.xclient.data.l[0] = window->x11.handle; 1728 reply.xclient.data.l[2] = 0; // Specify an empty rectangle 1729 reply.xclient.data.l[3] = 0; 1730 1731 if (_glfw.x11.xdnd.format) 1732 { 1733 // Reply that we are ready to copy the dragged data 1734 reply.xclient.data.l[1] = 1; // Accept with no rectangle 1735 if (_glfw.x11.xdnd.version >= 2) 1736 reply.xclient.data.l[4] = _glfw.x11.XdndActionCopy; 1737 } 1738 1739 XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, 1740 False, NoEventMask, &reply); 1741 XFlush(_glfw.x11.display); 1742 } 1743 1744 return; 1745 } 1746 1747 case SelectionNotify: 1748 { 1749 if (event->xselection.property == _glfw.x11.XdndSelection) 1750 { 1751 // The converted data from the drag operation has arrived 1752 char* data; 1753 const unsigned long result = 1754 _glfwGetWindowPropertyX11(event->xselection.requestor, 1755 event->xselection.property, 1756 event->xselection.target, 1757 (unsigned char**) &data); 1758 1759 if (result) 1760 { 1761 int i, count; 1762 char** paths = parseUriList(data, &count); 1763 1764 _glfwInputDrop(window, count, (const char**) paths); 1765 1766 for (i = 0; i < count; i++) 1767 free(paths[i]); 1768 free(paths); 1769 } 1770 1771 if (data) 1772 XFree(data); 1773 1774 if (_glfw.x11.xdnd.version >= 2) 1775 { 1776 XEvent reply = { ClientMessage }; 1777 reply.xclient.window = _glfw.x11.xdnd.source; 1778 reply.xclient.message_type = _glfw.x11.XdndFinished; 1779 reply.xclient.format = 32; 1780 reply.xclient.data.l[0] = window->x11.handle; 1781 reply.xclient.data.l[1] = result; 1782 reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy; 1783 1784 XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, 1785 False, NoEventMask, &reply); 1786 XFlush(_glfw.x11.display); 1787 } 1788 } 1789 1790 return; 1791 } 1792 1793 case FocusIn: 1794 { 1795 if (event->xfocus.mode == NotifyGrab || 1796 event->xfocus.mode == NotifyUngrab) 1797 { 1798 // Ignore focus events from popup indicator windows, window menu 1799 // key chords and window dragging 1800 return; 1801 } 1802 1803 if (window->cursorMode == GLFW_CURSOR_DISABLED) 1804 disableCursor(window); 1805 1806 if (window->x11.ic) 1807 XSetICFocus(window->x11.ic); 1808 1809 _glfwInputWindowFocus(window, GLFW_TRUE); 1810 return; 1811 } 1812 1813 case FocusOut: 1814 { 1815 if (event->xfocus.mode == NotifyGrab || 1816 event->xfocus.mode == NotifyUngrab) 1817 { 1818 // Ignore focus events from popup indicator windows, window menu 1819 // key chords and window dragging 1820 return; 1821 } 1822 1823 if (window->cursorMode == GLFW_CURSOR_DISABLED) 1824 enableCursor(window); 1825 1826 if (window->x11.ic) 1827 XUnsetICFocus(window->x11.ic); 1828 1829 if (window->monitor && window->autoIconify) 1830 _glfwPlatformIconifyWindow(window); 1831 1832 _glfwInputWindowFocus(window, GLFW_FALSE); 1833 return; 1834 } 1835 1836 case Expose: 1837 { 1838 _glfwInputWindowDamage(window); 1839 return; 1840 } 1841 1842 case PropertyNotify: 1843 { 1844 if (event->xproperty.state != PropertyNewValue) 1845 return; 1846 1847 if (event->xproperty.atom == _glfw.x11.WM_STATE) 1848 { 1849 const int state = getWindowState(window); 1850 if (state != IconicState && state != NormalState) 1851 return; 1852 1853 const GLFWbool iconified = (state == IconicState); 1854 if (window->x11.iconified != iconified) 1855 { 1856 if (window->monitor) 1857 { 1858 if (iconified) 1859 releaseMonitor(window); 1860 else 1861 acquireMonitor(window); 1862 } 1863 1864 window->x11.iconified = iconified; 1865 _glfwInputWindowIconify(window, iconified); 1866 } 1867 } 1868 else if (event->xproperty.atom == _glfw.x11.NET_WM_STATE) 1869 { 1870 const GLFWbool maximized = _glfwPlatformWindowMaximized(window); 1871 if (window->x11.maximized != maximized) 1872 { 1873 window->x11.maximized = maximized; 1874 _glfwInputWindowMaximize(window, maximized); 1875 } 1876 } 1877 1878 return; 1879 } 1880 1881 case DestroyNotify: 1882 return; 1883 } 1884 } 1885 1886 1887 ////////////////////////////////////////////////////////////////////////// 1888 ////// GLFW internal API ////// 1889 ////////////////////////////////////////////////////////////////////////// 1890 1891 // Retrieve a single window property of the specified type 1892 // Inspired by fghGetWindowProperty from freeglut 1893 // 1894 unsigned long _glfwGetWindowPropertyX11(Window window, 1895 Atom property, 1896 Atom type, 1897 unsigned char** value) 1898 { 1899 Atom actualType; 1900 int actualFormat; 1901 unsigned long itemCount, bytesAfter; 1902 1903 XGetWindowProperty(_glfw.x11.display, 1904 window, 1905 property, 1906 0, 1907 LONG_MAX, 1908 False, 1909 type, 1910 &actualType, 1911 &actualFormat, 1912 &itemCount, 1913 &bytesAfter, 1914 value); 1915 1916 return itemCount; 1917 } 1918 1919 GLFWbool _glfwIsVisualTransparentX11(Visual* visual) 1920 { 1921 if (!_glfw.x11.xrender.available) 1922 return GLFW_FALSE; 1923 1924 XRenderPictFormat* pf = XRenderFindVisualFormat(_glfw.x11.display, visual); 1925 return pf && pf->direct.alphaMask; 1926 } 1927 1928 // Push contents of our selection to clipboard manager 1929 // 1930 void _glfwPushSelectionToManagerX11(void) 1931 { 1932 XConvertSelection(_glfw.x11.display, 1933 _glfw.x11.CLIPBOARD_MANAGER, 1934 _glfw.x11.SAVE_TARGETS, 1935 None, 1936 _glfw.x11.helperWindowHandle, 1937 CurrentTime); 1938 1939 for (;;) 1940 { 1941 XEvent event; 1942 1943 while (XCheckIfEvent(_glfw.x11.display, &event, isSelectionEvent, NULL)) 1944 { 1945 switch (event.type) 1946 { 1947 case SelectionRequest: 1948 handleSelectionRequest(&event); 1949 break; 1950 1951 case SelectionClear: 1952 handleSelectionClear(&event); 1953 break; 1954 1955 case SelectionNotify: 1956 { 1957 if (event.xselection.target == _glfw.x11.SAVE_TARGETS) 1958 { 1959 // This means one of two things; either the selection 1960 // was not owned, which means there is no clipboard 1961 // manager, or the transfer to the clipboard manager has 1962 // completed 1963 // In either case, it means we are done here 1964 return; 1965 } 1966 1967 break; 1968 } 1969 } 1970 } 1971 1972 waitForEvent(NULL); 1973 } 1974 } 1975 1976 1977 ////////////////////////////////////////////////////////////////////////// 1978 ////// GLFW platform API ////// 1979 ////////////////////////////////////////////////////////////////////////// 1980 1981 int _glfwPlatformCreateWindow(_GLFWwindow* window, 1982 const _GLFWwndconfig* wndconfig, 1983 const _GLFWctxconfig* ctxconfig, 1984 const _GLFWfbconfig* fbconfig) 1985 { 1986 Visual* visual = NULL; 1987 int depth; 1988 1989 if (ctxconfig->client != GLFW_NO_API) 1990 { 1991 if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) 1992 { 1993 if (!_glfwInitGLX()) 1994 return GLFW_FALSE; 1995 if (!_glfwChooseVisualGLX(wndconfig, ctxconfig, fbconfig, &visual, &depth)) 1996 return GLFW_FALSE; 1997 } 1998 else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) 1999 { 2000 if (!_glfwInitEGL()) 2001 return GLFW_FALSE; 2002 if (!_glfwChooseVisualEGL(wndconfig, ctxconfig, fbconfig, &visual, &depth)) 2003 return GLFW_FALSE; 2004 } 2005 else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) 2006 { 2007 if (!_glfwInitOSMesa()) 2008 return GLFW_FALSE; 2009 } 2010 } 2011 2012 if (!visual) 2013 { 2014 visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen); 2015 depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen); 2016 } 2017 2018 if (!createNativeWindow(window, wndconfig, visual, depth)) 2019 return GLFW_FALSE; 2020 2021 if (ctxconfig->client != GLFW_NO_API) 2022 { 2023 if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) 2024 { 2025 if (!_glfwCreateContextGLX(window, ctxconfig, fbconfig)) 2026 return GLFW_FALSE; 2027 } 2028 else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) 2029 { 2030 if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) 2031 return GLFW_FALSE; 2032 } 2033 else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) 2034 { 2035 if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) 2036 return GLFW_FALSE; 2037 } 2038 } 2039 2040 if (window->monitor) 2041 { 2042 _glfwPlatformShowWindow(window); 2043 updateWindowMode(window); 2044 acquireMonitor(window); 2045 } 2046 2047 XFlush(_glfw.x11.display); 2048 return GLFW_TRUE; 2049 } 2050 2051 void _glfwPlatformDestroyWindow(_GLFWwindow* window) 2052 { 2053 if (_glfw.x11.disabledCursorWindow == window) 2054 _glfw.x11.disabledCursorWindow = NULL; 2055 2056 if (window->monitor) 2057 releaseMonitor(window); 2058 2059 if (window->x11.ic) 2060 { 2061 XDestroyIC(window->x11.ic); 2062 window->x11.ic = NULL; 2063 } 2064 2065 if (window->context.destroy) 2066 window->context.destroy(window); 2067 2068 if (window->x11.handle) 2069 { 2070 XDeleteContext(_glfw.x11.display, window->x11.handle, _glfw.x11.context); 2071 XUnmapWindow(_glfw.x11.display, window->x11.handle); 2072 XDestroyWindow(_glfw.x11.display, window->x11.handle); 2073 window->x11.handle = (Window) 0; 2074 } 2075 2076 if (window->x11.colormap) 2077 { 2078 XFreeColormap(_glfw.x11.display, window->x11.colormap); 2079 window->x11.colormap = (Colormap) 0; 2080 } 2081 2082 XFlush(_glfw.x11.display); 2083 } 2084 2085 void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) 2086 { 2087 #if defined(X_HAVE_UTF8_STRING) 2088 Xutf8SetWMProperties(_glfw.x11.display, 2089 window->x11.handle, 2090 title, title, 2091 NULL, 0, 2092 NULL, NULL, NULL); 2093 #else 2094 // This may be a slightly better fallback than using XStoreName and 2095 // XSetIconName, which always store their arguments using STRING 2096 XmbSetWMProperties(_glfw.x11.display, 2097 window->x11.handle, 2098 title, title, 2099 NULL, 0, 2100 NULL, NULL, NULL); 2101 #endif 2102 2103 XChangeProperty(_glfw.x11.display, window->x11.handle, 2104 _glfw.x11.NET_WM_NAME, _glfw.x11.UTF8_STRING, 8, 2105 PropModeReplace, 2106 (unsigned char*) title, strlen(title)); 2107 2108 XChangeProperty(_glfw.x11.display, window->x11.handle, 2109 _glfw.x11.NET_WM_ICON_NAME, _glfw.x11.UTF8_STRING, 8, 2110 PropModeReplace, 2111 (unsigned char*) title, strlen(title)); 2112 2113 XFlush(_glfw.x11.display); 2114 } 2115 2116 void _glfwPlatformSetWindowIcon(_GLFWwindow* window, 2117 int count, const GLFWimage* images) 2118 { 2119 if (count) 2120 { 2121 int i, j, longCount = 0; 2122 2123 for (i = 0; i < count; i++) 2124 longCount += 2 + images[i].width * images[i].height; 2125 2126 long* icon = calloc(longCount, sizeof(long)); 2127 long* target = icon; 2128 2129 for (i = 0; i < count; i++) 2130 { 2131 *target++ = images[i].width; 2132 *target++ = images[i].height; 2133 2134 for (j = 0; j < images[i].width * images[i].height; j++) 2135 { 2136 *target++ = (images[i].pixels[j * 4 + 0] << 16) | 2137 (images[i].pixels[j * 4 + 1] << 8) | 2138 (images[i].pixels[j * 4 + 2] << 0) | 2139 (images[i].pixels[j * 4 + 3] << 24); 2140 } 2141 } 2142 2143 XChangeProperty(_glfw.x11.display, window->x11.handle, 2144 _glfw.x11.NET_WM_ICON, 2145 XA_CARDINAL, 32, 2146 PropModeReplace, 2147 (unsigned char*) icon, 2148 longCount); 2149 2150 free(icon); 2151 } 2152 else 2153 { 2154 XDeleteProperty(_glfw.x11.display, window->x11.handle, 2155 _glfw.x11.NET_WM_ICON); 2156 } 2157 2158 XFlush(_glfw.x11.display); 2159 } 2160 2161 void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) 2162 { 2163 Window dummy; 2164 int x, y; 2165 2166 XTranslateCoordinates(_glfw.x11.display, window->x11.handle, _glfw.x11.root, 2167 0, 0, &x, &y, &dummy); 2168 2169 if (xpos) 2170 *xpos = x; 2171 if (ypos) 2172 *ypos = y; 2173 } 2174 2175 void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) 2176 { 2177 // HACK: Explicitly setting PPosition to any value causes some WMs, notably 2178 // Compiz and Metacity, to honor the position of unmapped windows 2179 if (!_glfwPlatformWindowVisible(window)) 2180 { 2181 long supplied; 2182 XSizeHints* hints = XAllocSizeHints(); 2183 2184 if (XGetWMNormalHints(_glfw.x11.display, window->x11.handle, hints, &supplied)) 2185 { 2186 hints->flags |= PPosition; 2187 hints->x = hints->y = 0; 2188 2189 XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints); 2190 } 2191 2192 XFree(hints); 2193 } 2194 2195 XMoveWindow(_glfw.x11.display, window->x11.handle, xpos, ypos); 2196 XFlush(_glfw.x11.display); 2197 } 2198 2199 void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) 2200 { 2201 XWindowAttributes attribs; 2202 XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &attribs); 2203 2204 if (width) 2205 *width = attribs.width; 2206 if (height) 2207 *height = attribs.height; 2208 } 2209 2210 void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) 2211 { 2212 if (window->monitor) 2213 { 2214 if (window->monitor->window == window) 2215 acquireMonitor(window); 2216 } 2217 else 2218 { 2219 if (!window->resizable) 2220 updateNormalHints(window, width, height); 2221 2222 XResizeWindow(_glfw.x11.display, window->x11.handle, width, height); 2223 } 2224 2225 XFlush(_glfw.x11.display); 2226 } 2227 2228 void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, 2229 int minwidth, int minheight, 2230 int maxwidth, int maxheight) 2231 { 2232 int width, height; 2233 _glfwPlatformGetWindowSize(window, &width, &height); 2234 updateNormalHints(window, width, height); 2235 XFlush(_glfw.x11.display); 2236 } 2237 2238 void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) 2239 { 2240 int width, height; 2241 _glfwPlatformGetWindowSize(window, &width, &height); 2242 updateNormalHints(window, width, height); 2243 XFlush(_glfw.x11.display); 2244 } 2245 2246 void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) 2247 { 2248 _glfwPlatformGetWindowSize(window, width, height); 2249 } 2250 2251 void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, 2252 int* left, int* top, 2253 int* right, int* bottom) 2254 { 2255 long* extents = NULL; 2256 2257 if (window->monitor || !window->decorated) 2258 return; 2259 2260 if (_glfw.x11.NET_FRAME_EXTENTS == None) 2261 return; 2262 2263 if (!_glfwPlatformWindowVisible(window) && 2264 _glfw.x11.NET_REQUEST_FRAME_EXTENTS) 2265 { 2266 XEvent event; 2267 double timeout = 0.5; 2268 2269 // Ensure _NET_FRAME_EXTENTS is set, allowing glfwGetWindowFrameSize to 2270 // function before the window is mapped 2271 sendEventToWM(window, _glfw.x11.NET_REQUEST_FRAME_EXTENTS, 2272 0, 0, 0, 0, 0); 2273 2274 // HACK: Use a timeout because earlier versions of some window managers 2275 // (at least Unity, Fluxbox and Xfwm) failed to send the reply 2276 // They have been fixed but broken versions are still in the wild 2277 // If you are affected by this and your window manager is NOT 2278 // listed above, PLEASE report it to their and our issue trackers 2279 while (!XCheckIfEvent(_glfw.x11.display, 2280 &event, 2281 isFrameExtentsEvent, 2282 (XPointer) window)) 2283 { 2284 if (!waitForEvent(&timeout)) 2285 { 2286 _glfwInputError(GLFW_PLATFORM_ERROR, 2287 "X11: The window manager has a broken _NET_REQUEST_FRAME_EXTENTS implementation; please report this issue"); 2288 return; 2289 } 2290 } 2291 } 2292 2293 if (_glfwGetWindowPropertyX11(window->x11.handle, 2294 _glfw.x11.NET_FRAME_EXTENTS, 2295 XA_CARDINAL, 2296 (unsigned char**) &extents) == 4) 2297 { 2298 if (left) 2299 *left = extents[0]; 2300 if (top) 2301 *top = extents[2]; 2302 if (right) 2303 *right = extents[1]; 2304 if (bottom) 2305 *bottom = extents[3]; 2306 } 2307 2308 if (extents) 2309 XFree(extents); 2310 } 2311 2312 void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, 2313 float* xscale, float* yscale) 2314 { 2315 if (xscale) 2316 *xscale = _glfw.x11.contentScaleX; 2317 if (yscale) 2318 *yscale = _glfw.x11.contentScaleY; 2319 } 2320 2321 void _glfwPlatformIconifyWindow(_GLFWwindow* window) 2322 { 2323 if (window->x11.overrideRedirect) 2324 { 2325 // Override-redirect windows cannot be iconified or restored, as those 2326 // tasks are performed by the window manager 2327 _glfwInputError(GLFW_PLATFORM_ERROR, 2328 "X11: Iconification of full screen windows requires a WM that supports EWMH full screen"); 2329 return; 2330 } 2331 2332 XIconifyWindow(_glfw.x11.display, window->x11.handle, _glfw.x11.screen); 2333 XFlush(_glfw.x11.display); 2334 } 2335 2336 void _glfwPlatformRestoreWindow(_GLFWwindow* window) 2337 { 2338 if (window->x11.overrideRedirect) 2339 { 2340 // Override-redirect windows cannot be iconified or restored, as those 2341 // tasks are performed by the window manager 2342 _glfwInputError(GLFW_PLATFORM_ERROR, 2343 "X11: Iconification of full screen windows requires a WM that supports EWMH full screen"); 2344 return; 2345 } 2346 2347 if (_glfwPlatformWindowIconified(window)) 2348 { 2349 XMapWindow(_glfw.x11.display, window->x11.handle); 2350 waitForVisibilityNotify(window); 2351 } 2352 else if (_glfwPlatformWindowVisible(window)) 2353 { 2354 if (_glfw.x11.NET_WM_STATE && 2355 _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT && 2356 _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) 2357 { 2358 sendEventToWM(window, 2359 _glfw.x11.NET_WM_STATE, 2360 _NET_WM_STATE_REMOVE, 2361 _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT, 2362 _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ, 2363 1, 0); 2364 } 2365 } 2366 2367 XFlush(_glfw.x11.display); 2368 } 2369 2370 void _glfwPlatformMaximizeWindow(_GLFWwindow* window) 2371 { 2372 if (!_glfw.x11.NET_WM_STATE || 2373 !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT || 2374 !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) 2375 { 2376 return; 2377 } 2378 2379 if (_glfwPlatformWindowVisible(window)) 2380 { 2381 sendEventToWM(window, 2382 _glfw.x11.NET_WM_STATE, 2383 _NET_WM_STATE_ADD, 2384 _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT, 2385 _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ, 2386 1, 0); 2387 } 2388 else 2389 { 2390 Atom* states = NULL; 2391 unsigned long count = 2392 _glfwGetWindowPropertyX11(window->x11.handle, 2393 _glfw.x11.NET_WM_STATE, 2394 XA_ATOM, 2395 (unsigned char**) &states); 2396 2397 // NOTE: We don't check for failure as this property may not exist yet 2398 // and that's fine (and we'll create it implicitly with append) 2399 2400 Atom missing[2] = 2401 { 2402 _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT, 2403 _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ 2404 }; 2405 unsigned long missingCount = 2; 2406 2407 for (unsigned long i = 0; i < count; i++) 2408 { 2409 for (unsigned long j = 0; j < missingCount; j++) 2410 { 2411 if (states[i] == missing[j]) 2412 { 2413 missing[j] = missing[missingCount - 1]; 2414 missingCount--; 2415 } 2416 } 2417 } 2418 2419 if (states) 2420 XFree(states); 2421 2422 if (!missingCount) 2423 return; 2424 2425 XChangeProperty(_glfw.x11.display, window->x11.handle, 2426 _glfw.x11.NET_WM_STATE, XA_ATOM, 32, 2427 PropModeAppend, 2428 (unsigned char*) missing, 2429 missingCount); 2430 } 2431 2432 XFlush(_glfw.x11.display); 2433 } 2434 2435 void _glfwPlatformShowWindow(_GLFWwindow* window) 2436 { 2437 if (_glfwPlatformWindowVisible(window)) 2438 return; 2439 2440 XMapWindow(_glfw.x11.display, window->x11.handle); 2441 waitForVisibilityNotify(window); 2442 } 2443 2444 void _glfwPlatformHideWindow(_GLFWwindow* window) 2445 { 2446 XUnmapWindow(_glfw.x11.display, window->x11.handle); 2447 XFlush(_glfw.x11.display); 2448 } 2449 2450 void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) 2451 { 2452 if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION) 2453 return; 2454 2455 sendEventToWM(window, 2456 _glfw.x11.NET_WM_STATE, 2457 _NET_WM_STATE_ADD, 2458 _glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION, 2459 0, 1, 0); 2460 } 2461 2462 void _glfwPlatformFocusWindow(_GLFWwindow* window) 2463 { 2464 if (_glfw.x11.NET_ACTIVE_WINDOW) 2465 sendEventToWM(window, _glfw.x11.NET_ACTIVE_WINDOW, 1, 0, 0, 0, 0); 2466 else if (_glfwPlatformWindowVisible(window)) 2467 { 2468 XRaiseWindow(_glfw.x11.display, window->x11.handle); 2469 XSetInputFocus(_glfw.x11.display, window->x11.handle, 2470 RevertToParent, CurrentTime); 2471 } 2472 2473 XFlush(_glfw.x11.display); 2474 } 2475 2476 void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, 2477 _GLFWmonitor* monitor, 2478 int xpos, int ypos, 2479 int width, int height, 2480 int refreshRate) 2481 { 2482 if (window->monitor == monitor) 2483 { 2484 if (monitor) 2485 { 2486 if (monitor->window == window) 2487 acquireMonitor(window); 2488 } 2489 else 2490 { 2491 if (!window->resizable) 2492 updateNormalHints(window, width, height); 2493 2494 XMoveResizeWindow(_glfw.x11.display, window->x11.handle, 2495 xpos, ypos, width, height); 2496 } 2497 2498 XFlush(_glfw.x11.display); 2499 return; 2500 } 2501 2502 if (window->monitor) 2503 { 2504 _glfwPlatformSetWindowDecorated(window, window->decorated); 2505 _glfwPlatformSetWindowFloating(window, window->floating); 2506 releaseMonitor(window); 2507 } 2508 2509 _glfwInputWindowMonitor(window, monitor); 2510 updateNormalHints(window, width, height); 2511 2512 if (window->monitor) 2513 { 2514 if (!_glfwPlatformWindowVisible(window)) 2515 { 2516 XMapRaised(_glfw.x11.display, window->x11.handle); 2517 waitForVisibilityNotify(window); 2518 } 2519 2520 updateWindowMode(window); 2521 acquireMonitor(window); 2522 } 2523 else 2524 { 2525 updateWindowMode(window); 2526 XMoveResizeWindow(_glfw.x11.display, window->x11.handle, 2527 xpos, ypos, width, height); 2528 } 2529 2530 XFlush(_glfw.x11.display); 2531 } 2532 2533 int _glfwPlatformWindowFocused(_GLFWwindow* window) 2534 { 2535 Window focused; 2536 int state; 2537 2538 XGetInputFocus(_glfw.x11.display, &focused, &state); 2539 return window->x11.handle == focused; 2540 } 2541 2542 int _glfwPlatformWindowIconified(_GLFWwindow* window) 2543 { 2544 return getWindowState(window) == IconicState; 2545 } 2546 2547 int _glfwPlatformWindowVisible(_GLFWwindow* window) 2548 { 2549 XWindowAttributes wa; 2550 XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &wa); 2551 return wa.map_state == IsViewable; 2552 } 2553 2554 int _glfwPlatformWindowMaximized(_GLFWwindow* window) 2555 { 2556 Atom* states; 2557 unsigned long i; 2558 GLFWbool maximized = GLFW_FALSE; 2559 2560 if (!_glfw.x11.NET_WM_STATE || 2561 !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT || 2562 !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) 2563 { 2564 return maximized; 2565 } 2566 2567 const unsigned long count = 2568 _glfwGetWindowPropertyX11(window->x11.handle, 2569 _glfw.x11.NET_WM_STATE, 2570 XA_ATOM, 2571 (unsigned char**) &states); 2572 2573 for (i = 0; i < count; i++) 2574 { 2575 if (states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT || 2576 states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) 2577 { 2578 maximized = GLFW_TRUE; 2579 break; 2580 } 2581 } 2582 2583 if (states) 2584 XFree(states); 2585 2586 return maximized; 2587 } 2588 2589 int _glfwPlatformWindowHovered(_GLFWwindow* window) 2590 { 2591 Window w = _glfw.x11.root; 2592 while (w) 2593 { 2594 Window root; 2595 int rootX, rootY, childX, childY; 2596 unsigned int mask; 2597 2598 _glfwGrabErrorHandlerX11(); 2599 2600 const Bool result = XQueryPointer(_glfw.x11.display, w, 2601 &root, &w, &rootX, &rootY, 2602 &childX, &childY, &mask); 2603 2604 _glfwReleaseErrorHandlerX11(); 2605 2606 if (_glfw.x11.errorCode == BadWindow) 2607 w = _glfw.x11.root; 2608 else if (!result) 2609 return GLFW_FALSE; 2610 else if (w == window->x11.handle) 2611 return GLFW_TRUE; 2612 } 2613 2614 return GLFW_FALSE; 2615 } 2616 2617 int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) 2618 { 2619 if (!window->x11.transparent) 2620 return GLFW_FALSE; 2621 2622 return XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx) != None; 2623 } 2624 2625 void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) 2626 { 2627 int width, height; 2628 _glfwPlatformGetWindowSize(window, &width, &height); 2629 updateNormalHints(window, width, height); 2630 } 2631 2632 void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) 2633 { 2634 struct 2635 { 2636 unsigned long flags; 2637 unsigned long functions; 2638 unsigned long decorations; 2639 long input_mode; 2640 unsigned long status; 2641 } hints = {0}; 2642 2643 hints.flags = MWM_HINTS_DECORATIONS; 2644 hints.decorations = enabled ? MWM_DECOR_ALL : 0; 2645 2646 XChangeProperty(_glfw.x11.display, window->x11.handle, 2647 _glfw.x11.MOTIF_WM_HINTS, 2648 _glfw.x11.MOTIF_WM_HINTS, 32, 2649 PropModeReplace, 2650 (unsigned char*) &hints, 2651 sizeof(hints) / sizeof(long)); 2652 } 2653 2654 void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) 2655 { 2656 if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_ABOVE) 2657 return; 2658 2659 if (_glfwPlatformWindowVisible(window)) 2660 { 2661 const long action = enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; 2662 sendEventToWM(window, 2663 _glfw.x11.NET_WM_STATE, 2664 action, 2665 _glfw.x11.NET_WM_STATE_ABOVE, 2666 0, 1, 0); 2667 } 2668 else 2669 { 2670 Atom* states = NULL; 2671 unsigned long i, count; 2672 2673 count = _glfwGetWindowPropertyX11(window->x11.handle, 2674 _glfw.x11.NET_WM_STATE, 2675 XA_ATOM, 2676 (unsigned char**) &states); 2677 2678 // NOTE: We don't check for failure as this property may not exist yet 2679 // and that's fine (and we'll create it implicitly with append) 2680 2681 if (enabled) 2682 { 2683 for (i = 0; i < count; i++) 2684 { 2685 if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE) 2686 break; 2687 } 2688 2689 if (i < count) 2690 return; 2691 2692 XChangeProperty(_glfw.x11.display, window->x11.handle, 2693 _glfw.x11.NET_WM_STATE, XA_ATOM, 32, 2694 PropModeAppend, 2695 (unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE, 2696 1); 2697 } 2698 else if (states) 2699 { 2700 for (i = 0; i < count; i++) 2701 { 2702 if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE) 2703 break; 2704 } 2705 2706 if (i == count) 2707 return; 2708 2709 states[i] = states[count - 1]; 2710 count--; 2711 2712 XChangeProperty(_glfw.x11.display, window->x11.handle, 2713 _glfw.x11.NET_WM_STATE, XA_ATOM, 32, 2714 PropModeReplace, (unsigned char*) states, count); 2715 } 2716 2717 if (states) 2718 XFree(states); 2719 } 2720 2721 XFlush(_glfw.x11.display); 2722 } 2723 2724 float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) 2725 { 2726 float opacity = 1.f; 2727 2728 if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx)) 2729 { 2730 CARD32* value = NULL; 2731 2732 if (_glfwGetWindowPropertyX11(window->x11.handle, 2733 _glfw.x11.NET_WM_WINDOW_OPACITY, 2734 XA_CARDINAL, 2735 (unsigned char**) &value)) 2736 { 2737 opacity = (float) (*value / (double) 0xffffffffu); 2738 } 2739 2740 if (value) 2741 XFree(value); 2742 } 2743 2744 return opacity; 2745 } 2746 2747 void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) 2748 { 2749 const CARD32 value = (CARD32) (0xffffffffu * (double) opacity); 2750 XChangeProperty(_glfw.x11.display, window->x11.handle, 2751 _glfw.x11.NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32, 2752 PropModeReplace, (unsigned char*) &value, 1); 2753 } 2754 2755 void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) 2756 { 2757 if (!_glfw.x11.xi.available) 2758 return; 2759 2760 if (_glfw.x11.disabledCursorWindow != window) 2761 return; 2762 2763 if (enabled) 2764 enableRawMouseMotion(window); 2765 else 2766 disableRawMouseMotion(window); 2767 } 2768 2769 GLFWbool _glfwPlatformRawMouseMotionSupported(void) 2770 { 2771 return _glfw.x11.xi.available; 2772 } 2773 2774 void _glfwPlatformPollEvents(void) 2775 { 2776 _GLFWwindow* window; 2777 2778 #if defined(__linux__) 2779 _glfwDetectJoystickConnectionLinux(); 2780 #endif 2781 XPending(_glfw.x11.display); 2782 2783 while (XQLength(_glfw.x11.display)) 2784 { 2785 XEvent event; 2786 XNextEvent(_glfw.x11.display, &event); 2787 processEvent(&event); 2788 } 2789 2790 window = _glfw.x11.disabledCursorWindow; 2791 if (window) 2792 { 2793 int width, height; 2794 _glfwPlatformGetWindowSize(window, &width, &height); 2795 2796 // NOTE: Re-center the cursor only if it has moved since the last call, 2797 // to avoid breaking glfwWaitEvents with MotionNotify 2798 if (window->x11.lastCursorPosX != width / 2 || 2799 window->x11.lastCursorPosY != height / 2) 2800 { 2801 _glfwPlatformSetCursorPos(window, width / 2, height / 2); 2802 } 2803 } 2804 2805 XFlush(_glfw.x11.display); 2806 } 2807 2808 void _glfwPlatformWaitEvents(void) 2809 { 2810 while (!XPending(_glfw.x11.display)) 2811 waitForEvent(NULL); 2812 2813 _glfwPlatformPollEvents(); 2814 } 2815 2816 void _glfwPlatformWaitEventsTimeout(double timeout) 2817 { 2818 while (!XPending(_glfw.x11.display)) 2819 { 2820 if (!waitForEvent(&timeout)) 2821 break; 2822 } 2823 2824 _glfwPlatformPollEvents(); 2825 } 2826 2827 void _glfwPlatformPostEmptyEvent(void) 2828 { 2829 XEvent event = { ClientMessage }; 2830 event.xclient.window = _glfw.x11.helperWindowHandle; 2831 event.xclient.format = 32; // Data is 32-bit longs 2832 event.xclient.message_type = _glfw.x11.NULL_; 2833 2834 XSendEvent(_glfw.x11.display, _glfw.x11.helperWindowHandle, False, 0, &event); 2835 XFlush(_glfw.x11.display); 2836 } 2837 2838 void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) 2839 { 2840 Window root, child; 2841 int rootX, rootY, childX, childY; 2842 unsigned int mask; 2843 2844 XQueryPointer(_glfw.x11.display, window->x11.handle, 2845 &root, &child, 2846 &rootX, &rootY, &childX, &childY, 2847 &mask); 2848 2849 if (xpos) 2850 *xpos = childX; 2851 if (ypos) 2852 *ypos = childY; 2853 } 2854 2855 void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) 2856 { 2857 // Store the new position so it can be recognized later 2858 window->x11.warpCursorPosX = (int) x; 2859 window->x11.warpCursorPosY = (int) y; 2860 2861 XWarpPointer(_glfw.x11.display, None, window->x11.handle, 2862 0,0,0,0, (int) x, (int) y); 2863 XFlush(_glfw.x11.display); 2864 } 2865 2866 void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) 2867 { 2868 if (mode == GLFW_CURSOR_DISABLED) 2869 { 2870 if (_glfwPlatformWindowFocused(window)) 2871 disableCursor(window); 2872 } 2873 else if (_glfw.x11.disabledCursorWindow == window) 2874 enableCursor(window); 2875 else 2876 updateCursorImage(window); 2877 2878 XFlush(_glfw.x11.display); 2879 } 2880 2881 const char* _glfwPlatformGetScancodeName(int scancode) 2882 { 2883 if (!_glfw.x11.xkb.available) 2884 return NULL; 2885 2886 if (scancode < 0 || scancode > 0xff || 2887 _glfw.x11.keycodes[scancode] == GLFW_KEY_UNKNOWN) 2888 { 2889 _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode"); 2890 return NULL; 2891 } 2892 2893 const int key = _glfw.x11.keycodes[scancode]; 2894 const KeySym keysym = XkbKeycodeToKeysym(_glfw.x11.display, 2895 scancode, _glfw.x11.xkb.group, 0); 2896 if (keysym == NoSymbol) 2897 return NULL; 2898 2899 const long ch = _glfwKeySym2Unicode(keysym); 2900 if (ch == -1) 2901 return NULL; 2902 2903 const size_t count = encodeUTF8(_glfw.x11.keynames[key], (unsigned int) ch); 2904 if (count == 0) 2905 return NULL; 2906 2907 _glfw.x11.keynames[key][count] = '\0'; 2908 return _glfw.x11.keynames[key]; 2909 } 2910 2911 int _glfwPlatformGetKeyScancode(int key) 2912 { 2913 return _glfw.x11.scancodes[key]; 2914 } 2915 2916 int _glfwPlatformCreateCursor(_GLFWcursor* cursor, 2917 const GLFWimage* image, 2918 int xhot, int yhot) 2919 { 2920 cursor->x11.handle = _glfwCreateCursorX11(image, xhot, yhot); 2921 if (!cursor->x11.handle) 2922 return GLFW_FALSE; 2923 2924 return GLFW_TRUE; 2925 } 2926 2927 int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) 2928 { 2929 int native = 0; 2930 2931 if (shape == GLFW_ARROW_CURSOR) 2932 native = XC_left_ptr; 2933 else if (shape == GLFW_IBEAM_CURSOR) 2934 native = XC_xterm; 2935 else if (shape == GLFW_CROSSHAIR_CURSOR) 2936 native = XC_crosshair; 2937 else if (shape == GLFW_HAND_CURSOR) 2938 native = XC_hand2; 2939 else if (shape == GLFW_HRESIZE_CURSOR) 2940 native = XC_sb_h_double_arrow; 2941 else if (shape == GLFW_VRESIZE_CURSOR) 2942 native = XC_sb_v_double_arrow; 2943 else 2944 return GLFW_FALSE; 2945 2946 cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, native); 2947 if (!cursor->x11.handle) 2948 { 2949 _glfwInputError(GLFW_PLATFORM_ERROR, 2950 "X11: Failed to create standard cursor"); 2951 return GLFW_FALSE; 2952 } 2953 2954 return GLFW_TRUE; 2955 } 2956 2957 void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) 2958 { 2959 if (cursor->x11.handle) 2960 XFreeCursor(_glfw.x11.display, cursor->x11.handle); 2961 } 2962 2963 void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) 2964 { 2965 if (window->cursorMode == GLFW_CURSOR_NORMAL) 2966 { 2967 updateCursorImage(window); 2968 XFlush(_glfw.x11.display); 2969 } 2970 } 2971 2972 void _glfwPlatformSetClipboardString(const char* string) 2973 { 2974 char* copy = _glfw_strdup(string); 2975 free(_glfw.x11.clipboardString); 2976 _glfw.x11.clipboardString = copy; 2977 2978 XSetSelectionOwner(_glfw.x11.display, 2979 _glfw.x11.CLIPBOARD, 2980 _glfw.x11.helperWindowHandle, 2981 CurrentTime); 2982 2983 if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) != 2984 _glfw.x11.helperWindowHandle) 2985 { 2986 _glfwInputError(GLFW_PLATFORM_ERROR, 2987 "X11: Failed to become owner of clipboard selection"); 2988 } 2989 } 2990 2991 const char* _glfwPlatformGetClipboardString(void) 2992 { 2993 return getSelectionString(_glfw.x11.CLIPBOARD); 2994 } 2995 2996 void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) 2997 { 2998 if (!_glfw.vk.KHR_surface) 2999 return; 3000 3001 if (!_glfw.vk.KHR_xcb_surface || !_glfw.x11.x11xcb.handle) 3002 { 3003 if (!_glfw.vk.KHR_xlib_surface) 3004 return; 3005 } 3006 3007 extensions[0] = "VK_KHR_surface"; 3008 3009 // NOTE: VK_KHR_xcb_surface is preferred due to some early ICDs exposing but 3010 // not correctly implementing VK_KHR_xlib_surface 3011 if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) 3012 extensions[1] = "VK_KHR_xcb_surface"; 3013 else 3014 extensions[1] = "VK_KHR_xlib_surface"; 3015 } 3016 3017 int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, 3018 VkPhysicalDevice device, 3019 uint32_t queuefamily) 3020 { 3021 VisualID visualID = XVisualIDFromVisual(DefaultVisual(_glfw.x11.display, 3022 _glfw.x11.screen)); 3023 3024 if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) 3025 { 3026 PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR 3027 vkGetPhysicalDeviceXcbPresentationSupportKHR = 3028 (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR) 3029 vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR"); 3030 if (!vkGetPhysicalDeviceXcbPresentationSupportKHR) 3031 { 3032 _glfwInputError(GLFW_API_UNAVAILABLE, 3033 "X11: Vulkan instance missing VK_KHR_xcb_surface extension"); 3034 return GLFW_FALSE; 3035 } 3036 3037 xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display); 3038 if (!connection) 3039 { 3040 _glfwInputError(GLFW_PLATFORM_ERROR, 3041 "X11: Failed to retrieve XCB connection"); 3042 return GLFW_FALSE; 3043 } 3044 3045 return vkGetPhysicalDeviceXcbPresentationSupportKHR(device, 3046 queuefamily, 3047 connection, 3048 visualID); 3049 } 3050 else 3051 { 3052 PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR 3053 vkGetPhysicalDeviceXlibPresentationSupportKHR = 3054 (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR) 3055 vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR"); 3056 if (!vkGetPhysicalDeviceXlibPresentationSupportKHR) 3057 { 3058 _glfwInputError(GLFW_API_UNAVAILABLE, 3059 "X11: Vulkan instance missing VK_KHR_xlib_surface extension"); 3060 return GLFW_FALSE; 3061 } 3062 3063 return vkGetPhysicalDeviceXlibPresentationSupportKHR(device, 3064 queuefamily, 3065 _glfw.x11.display, 3066 visualID); 3067 } 3068 } 3069 3070 VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, 3071 _GLFWwindow* window, 3072 const VkAllocationCallbacks* allocator, 3073 VkSurfaceKHR* surface) 3074 { 3075 if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) 3076 { 3077 VkResult err; 3078 VkXcbSurfaceCreateInfoKHR sci; 3079 PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR; 3080 3081 xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display); 3082 if (!connection) 3083 { 3084 _glfwInputError(GLFW_PLATFORM_ERROR, 3085 "X11: Failed to retrieve XCB connection"); 3086 return VK_ERROR_EXTENSION_NOT_PRESENT; 3087 } 3088 3089 vkCreateXcbSurfaceKHR = (PFN_vkCreateXcbSurfaceKHR) 3090 vkGetInstanceProcAddr(instance, "vkCreateXcbSurfaceKHR"); 3091 if (!vkCreateXcbSurfaceKHR) 3092 { 3093 _glfwInputError(GLFW_API_UNAVAILABLE, 3094 "X11: Vulkan instance missing VK_KHR_xcb_surface extension"); 3095 return VK_ERROR_EXTENSION_NOT_PRESENT; 3096 } 3097 3098 memset(&sci, 0, sizeof(sci)); 3099 sci.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; 3100 sci.connection = connection; 3101 sci.window = window->x11.handle; 3102 3103 err = vkCreateXcbSurfaceKHR(instance, &sci, allocator, surface); 3104 if (err) 3105 { 3106 _glfwInputError(GLFW_PLATFORM_ERROR, 3107 "X11: Failed to create Vulkan XCB surface: %s", 3108 _glfwGetVulkanResultString(err)); 3109 } 3110 3111 return err; 3112 } 3113 else 3114 { 3115 VkResult err; 3116 VkXlibSurfaceCreateInfoKHR sci; 3117 PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR; 3118 3119 vkCreateXlibSurfaceKHR = (PFN_vkCreateXlibSurfaceKHR) 3120 vkGetInstanceProcAddr(instance, "vkCreateXlibSurfaceKHR"); 3121 if (!vkCreateXlibSurfaceKHR) 3122 { 3123 _glfwInputError(GLFW_API_UNAVAILABLE, 3124 "X11: Vulkan instance missing VK_KHR_xlib_surface extension"); 3125 return VK_ERROR_EXTENSION_NOT_PRESENT; 3126 } 3127 3128 memset(&sci, 0, sizeof(sci)); 3129 sci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; 3130 sci.dpy = _glfw.x11.display; 3131 sci.window = window->x11.handle; 3132 3133 err = vkCreateXlibSurfaceKHR(instance, &sci, allocator, surface); 3134 if (err) 3135 { 3136 _glfwInputError(GLFW_PLATFORM_ERROR, 3137 "X11: Failed to create Vulkan X11 surface: %s", 3138 _glfwGetVulkanResultString(err)); 3139 } 3140 3141 return err; 3142 } 3143 } 3144 3145 3146 ////////////////////////////////////////////////////////////////////////// 3147 ////// GLFW native API ////// 3148 ////////////////////////////////////////////////////////////////////////// 3149 3150 GLFWAPI Display* glfwGetX11Display(void) 3151 { 3152 _GLFW_REQUIRE_INIT_OR_RETURN(NULL); 3153 return _glfw.x11.display; 3154 } 3155 3156 GLFWAPI Window glfwGetX11Window(GLFWwindow* handle) 3157 { 3158 _GLFWwindow* window = (_GLFWwindow*) handle; 3159 _GLFW_REQUIRE_INIT_OR_RETURN(None); 3160 return window->x11.handle; 3161 } 3162 3163 GLFWAPI void glfwSetX11SelectionString(const char* string) 3164 { 3165 _GLFW_REQUIRE_INIT(); 3166 3167 free(_glfw.x11.primarySelectionString); 3168 _glfw.x11.primarySelectionString = _glfw_strdup(string); 3169 3170 XSetSelectionOwner(_glfw.x11.display, 3171 _glfw.x11.PRIMARY, 3172 _glfw.x11.helperWindowHandle, 3173 CurrentTime); 3174 3175 if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.PRIMARY) != 3176 _glfw.x11.helperWindowHandle) 3177 { 3178 _glfwInputError(GLFW_PLATFORM_ERROR, 3179 "X11: Failed to become owner of primary selection"); 3180 } 3181 } 3182 3183 GLFWAPI const char* glfwGetX11SelectionString(void) 3184 { 3185 _GLFW_REQUIRE_INIT_OR_RETURN(NULL); 3186 return getSelectionString(_glfw.x11.PRIMARY); 3187 } 3188