x11_window.c (102783B)
1 //======================================================================== 2 // GLFW 3.4 X11 - www.glfw.org 3 //------------------------------------------------------------------------ 4 // Copyright (c) 2002-2006 Marcus Geelnard 5 // Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org> 6 // 7 // This software is provided 'as-is', without any express or implied 8 // warranty. In no event will the authors be held liable for any damages 9 // arising from the use of this software. 10 // 11 // Permission is granted to anyone to use this software for any purpose, 12 // including commercial applications, and to alter it and redistribute it 13 // freely, subject to the following restrictions: 14 // 15 // 1. The origin of this software must not be misrepresented; you must not 16 // claim that you wrote the original software. If you use this software 17 // in a product, an acknowledgment in the product documentation would 18 // be appreciated but is not required. 19 // 20 // 2. Altered source versions must be plainly marked as such, and must not 21 // be misrepresented as being the original software. 22 // 23 // 3. This notice may not be removed or altered from any source 24 // distribution. 25 // 26 //======================================================================== 27 // It is fine to use C99 in this file because it will not be built with VS 28 //======================================================================== 29 30 #include "internal.h" 31 32 #include <X11/cursorfont.h> 33 #include <X11/Xmd.h> 34 35 #include <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 // Clear its handle when the input context has been destroyed 594 // 595 static void inputContextDestroyCallback(XIC ic, XPointer clientData, XPointer callData) 596 { 597 _GLFWwindow* window = (_GLFWwindow*) clientData; 598 window->x11.ic = NULL; 599 } 600 601 // Create the X11 window (and its colormap) 602 // 603 static GLFWbool createNativeWindow(_GLFWwindow* window, 604 const _GLFWwndconfig* wndconfig, 605 Visual* visual, int depth) 606 { 607 int width = wndconfig->width; 608 int height = wndconfig->height; 609 610 if (wndconfig->scaleToMonitor) 611 { 612 width *= _glfw.x11.contentScaleX; 613 height *= _glfw.x11.contentScaleY; 614 } 615 616 // Create a colormap based on the visual used by the current context 617 window->x11.colormap = XCreateColormap(_glfw.x11.display, 618 _glfw.x11.root, 619 visual, 620 AllocNone); 621 622 window->x11.transparent = _glfwIsVisualTransparentX11(visual); 623 624 XSetWindowAttributes wa = { 0 }; 625 wa.colormap = window->x11.colormap; 626 wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask | 627 PointerMotionMask | ButtonPressMask | ButtonReleaseMask | 628 ExposureMask | FocusChangeMask | VisibilityChangeMask | 629 EnterWindowMask | LeaveWindowMask | PropertyChangeMask; 630 631 _glfwGrabErrorHandlerX11(); 632 633 window->x11.parent = _glfw.x11.root; 634 window->x11.handle = XCreateWindow(_glfw.x11.display, 635 _glfw.x11.root, 636 0, 0, // Position 637 width, height, 638 0, // Border width 639 depth, // Color depth 640 InputOutput, 641 visual, 642 CWBorderPixel | CWColormap | CWEventMask, 643 &wa); 644 645 _glfwReleaseErrorHandlerX11(); 646 647 if (!window->x11.handle) 648 { 649 _glfwInputErrorX11(GLFW_PLATFORM_ERROR, 650 "X11: Failed to create window"); 651 return GLFW_FALSE; 652 } 653 654 XSaveContext(_glfw.x11.display, 655 window->x11.handle, 656 _glfw.x11.context, 657 (XPointer) window); 658 659 if (!wndconfig->decorated) 660 _glfwPlatformSetWindowDecorated(window, GLFW_FALSE); 661 662 if (_glfw.x11.NET_WM_STATE && !window->monitor) 663 { 664 Atom states[3]; 665 int count = 0; 666 667 if (wndconfig->floating) 668 { 669 if (_glfw.x11.NET_WM_STATE_ABOVE) 670 states[count++] = _glfw.x11.NET_WM_STATE_ABOVE; 671 } 672 673 if (wndconfig->maximized) 674 { 675 if (_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT && 676 _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) 677 { 678 states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT; 679 states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ; 680 window->x11.maximized = GLFW_TRUE; 681 } 682 } 683 684 if (count) 685 { 686 XChangeProperty(_glfw.x11.display, window->x11.handle, 687 _glfw.x11.NET_WM_STATE, XA_ATOM, 32, 688 PropModeReplace, (unsigned char*) states, count); 689 } 690 } 691 692 // Declare the WM protocols supported by GLFW 693 { 694 Atom protocols[] = 695 { 696 _glfw.x11.WM_DELETE_WINDOW, 697 _glfw.x11.NET_WM_PING 698 }; 699 700 XSetWMProtocols(_glfw.x11.display, window->x11.handle, 701 protocols, sizeof(protocols) / sizeof(Atom)); 702 } 703 704 // Declare our PID 705 { 706 const long pid = getpid(); 707 708 XChangeProperty(_glfw.x11.display, window->x11.handle, 709 _glfw.x11.NET_WM_PID, XA_CARDINAL, 32, 710 PropModeReplace, 711 (unsigned char*) &pid, 1); 712 } 713 714 if (_glfw.x11.NET_WM_WINDOW_TYPE && _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL) 715 { 716 Atom type = _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL; 717 XChangeProperty(_glfw.x11.display, window->x11.handle, 718 _glfw.x11.NET_WM_WINDOW_TYPE, XA_ATOM, 32, 719 PropModeReplace, (unsigned char*) &type, 1); 720 } 721 722 // Set ICCCM WM_HINTS property 723 { 724 XWMHints* hints = XAllocWMHints(); 725 if (!hints) 726 { 727 _glfwInputError(GLFW_OUT_OF_MEMORY, 728 "X11: Failed to allocate WM hints"); 729 return GLFW_FALSE; 730 } 731 732 hints->flags = StateHint; 733 hints->initial_state = NormalState; 734 735 XSetWMHints(_glfw.x11.display, window->x11.handle, hints); 736 XFree(hints); 737 } 738 739 updateNormalHints(window, width, height); 740 741 // Set ICCCM WM_CLASS property 742 { 743 XClassHint* hint = XAllocClassHint(); 744 745 if (strlen(wndconfig->x11.instanceName) && 746 strlen(wndconfig->x11.className)) 747 { 748 hint->res_name = (char*) wndconfig->x11.instanceName; 749 hint->res_class = (char*) wndconfig->x11.className; 750 } 751 else 752 { 753 const char* resourceName = getenv("RESOURCE_NAME"); 754 if (resourceName && strlen(resourceName)) 755 hint->res_name = (char*) resourceName; 756 else if (strlen(wndconfig->title)) 757 hint->res_name = (char*) wndconfig->title; 758 else 759 hint->res_name = (char*) "glfw-application"; 760 761 if (strlen(wndconfig->title)) 762 hint->res_class = (char*) wndconfig->title; 763 else 764 hint->res_class = (char*) "GLFW-Application"; 765 } 766 767 XSetClassHint(_glfw.x11.display, window->x11.handle, hint); 768 XFree(hint); 769 } 770 771 // Announce support for Xdnd (drag and drop) 772 { 773 const Atom version = _GLFW_XDND_VERSION; 774 XChangeProperty(_glfw.x11.display, window->x11.handle, 775 _glfw.x11.XdndAware, XA_ATOM, 32, 776 PropModeReplace, (unsigned char*) &version, 1); 777 } 778 779 if (_glfw.x11.im) 780 _glfwCreateInputContextX11(window); 781 782 _glfwPlatformSetWindowTitle(window, wndconfig->title); 783 _glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos); 784 _glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height); 785 786 return GLFW_TRUE; 787 } 788 789 // Set the specified property to the selection converted to the requested target 790 // 791 static Atom writeTargetToProperty(const XSelectionRequestEvent* request) 792 { 793 int i; 794 char* selectionString = NULL; 795 const Atom formats[] = { _glfw.x11.UTF8_STRING, XA_STRING }; 796 const int formatCount = sizeof(formats) / sizeof(formats[0]); 797 798 if (request->selection == _glfw.x11.PRIMARY) 799 selectionString = _glfw.x11.primarySelectionString; 800 else 801 selectionString = _glfw.x11.clipboardString; 802 803 if (request->property == None) 804 { 805 // The requester is a legacy client (ICCCM section 2.2) 806 // We don't support legacy clients, so fail here 807 return None; 808 } 809 810 if (request->target == _glfw.x11.TARGETS) 811 { 812 // The list of supported targets was requested 813 814 const Atom targets[] = { _glfw.x11.TARGETS, 815 _glfw.x11.MULTIPLE, 816 _glfw.x11.UTF8_STRING, 817 XA_STRING }; 818 819 XChangeProperty(_glfw.x11.display, 820 request->requestor, 821 request->property, 822 XA_ATOM, 823 32, 824 PropModeReplace, 825 (unsigned char*) targets, 826 sizeof(targets) / sizeof(targets[0])); 827 828 return request->property; 829 } 830 831 if (request->target == _glfw.x11.MULTIPLE) 832 { 833 // Multiple conversions were requested 834 835 Atom* targets; 836 unsigned long i, count; 837 838 count = _glfwGetWindowPropertyX11(request->requestor, 839 request->property, 840 _glfw.x11.ATOM_PAIR, 841 (unsigned char**) &targets); 842 843 for (i = 0; i < count; i += 2) 844 { 845 int j; 846 847 for (j = 0; j < formatCount; j++) 848 { 849 if (targets[i] == formats[j]) 850 break; 851 } 852 853 if (j < formatCount) 854 { 855 XChangeProperty(_glfw.x11.display, 856 request->requestor, 857 targets[i + 1], 858 targets[i], 859 8, 860 PropModeReplace, 861 (unsigned char *) selectionString, 862 strlen(selectionString)); 863 } 864 else 865 targets[i + 1] = None; 866 } 867 868 XChangeProperty(_glfw.x11.display, 869 request->requestor, 870 request->property, 871 _glfw.x11.ATOM_PAIR, 872 32, 873 PropModeReplace, 874 (unsigned char*) targets, 875 count); 876 877 XFree(targets); 878 879 return request->property; 880 } 881 882 if (request->target == _glfw.x11.SAVE_TARGETS) 883 { 884 // The request is a check whether we support SAVE_TARGETS 885 // It should be handled as a no-op side effect target 886 887 XChangeProperty(_glfw.x11.display, 888 request->requestor, 889 request->property, 890 _glfw.x11.NULL_, 891 32, 892 PropModeReplace, 893 NULL, 894 0); 895 896 return request->property; 897 } 898 899 // Conversion to a data target was requested 900 901 for (i = 0; i < formatCount; i++) 902 { 903 if (request->target == formats[i]) 904 { 905 // The requested target is one we support 906 907 XChangeProperty(_glfw.x11.display, 908 request->requestor, 909 request->property, 910 request->target, 911 8, 912 PropModeReplace, 913 (unsigned char *) selectionString, 914 strlen(selectionString)); 915 916 return request->property; 917 } 918 } 919 920 // The requested target is not supported 921 922 return None; 923 } 924 925 static void handleSelectionClear(XEvent* event) 926 { 927 if (event->xselectionclear.selection == _glfw.x11.PRIMARY) 928 { 929 free(_glfw.x11.primarySelectionString); 930 _glfw.x11.primarySelectionString = NULL; 931 } 932 else 933 { 934 free(_glfw.x11.clipboardString); 935 _glfw.x11.clipboardString = NULL; 936 } 937 } 938 939 static void handleSelectionRequest(XEvent* event) 940 { 941 const XSelectionRequestEvent* request = &event->xselectionrequest; 942 943 XEvent reply = { SelectionNotify }; 944 reply.xselection.property = writeTargetToProperty(request); 945 reply.xselection.display = request->display; 946 reply.xselection.requestor = request->requestor; 947 reply.xselection.selection = request->selection; 948 reply.xselection.target = request->target; 949 reply.xselection.time = request->time; 950 951 XSendEvent(_glfw.x11.display, request->requestor, False, 0, &reply); 952 } 953 954 static const char* getSelectionString(Atom selection) 955 { 956 char** selectionString = NULL; 957 const Atom targets[] = { _glfw.x11.UTF8_STRING, XA_STRING }; 958 const size_t targetCount = sizeof(targets) / sizeof(targets[0]); 959 960 if (selection == _glfw.x11.PRIMARY) 961 selectionString = &_glfw.x11.primarySelectionString; 962 else 963 selectionString = &_glfw.x11.clipboardString; 964 965 if (XGetSelectionOwner(_glfw.x11.display, selection) == 966 _glfw.x11.helperWindowHandle) 967 { 968 // Instead of doing a large number of X round-trips just to put this 969 // string into a window property and then read it back, just return it 970 return *selectionString; 971 } 972 973 free(*selectionString); 974 *selectionString = NULL; 975 976 for (size_t i = 0; i < targetCount; i++) 977 { 978 char* data; 979 Atom actualType; 980 int actualFormat; 981 unsigned long itemCount, bytesAfter; 982 XEvent notification, dummy; 983 984 XConvertSelection(_glfw.x11.display, 985 selection, 986 targets[i], 987 _glfw.x11.GLFW_SELECTION, 988 _glfw.x11.helperWindowHandle, 989 CurrentTime); 990 991 while (!XCheckTypedWindowEvent(_glfw.x11.display, 992 _glfw.x11.helperWindowHandle, 993 SelectionNotify, 994 ¬ification)) 995 { 996 waitForEvent(NULL); 997 } 998 999 if (notification.xselection.property == None) 1000 continue; 1001 1002 XCheckIfEvent(_glfw.x11.display, 1003 &dummy, 1004 isSelPropNewValueNotify, 1005 (XPointer) ¬ification); 1006 1007 XGetWindowProperty(_glfw.x11.display, 1008 notification.xselection.requestor, 1009 notification.xselection.property, 1010 0, 1011 LONG_MAX, 1012 True, 1013 AnyPropertyType, 1014 &actualType, 1015 &actualFormat, 1016 &itemCount, 1017 &bytesAfter, 1018 (unsigned char**) &data); 1019 1020 if (actualType == _glfw.x11.INCR) 1021 { 1022 size_t size = 1; 1023 char* string = NULL; 1024 1025 for (;;) 1026 { 1027 while (!XCheckIfEvent(_glfw.x11.display, 1028 &dummy, 1029 isSelPropNewValueNotify, 1030 (XPointer) ¬ification)) 1031 { 1032 waitForEvent(NULL); 1033 } 1034 1035 XFree(data); 1036 XGetWindowProperty(_glfw.x11.display, 1037 notification.xselection.requestor, 1038 notification.xselection.property, 1039 0, 1040 LONG_MAX, 1041 True, 1042 AnyPropertyType, 1043 &actualType, 1044 &actualFormat, 1045 &itemCount, 1046 &bytesAfter, 1047 (unsigned char**) &data); 1048 1049 if (itemCount) 1050 { 1051 size += itemCount; 1052 string = realloc(string, size); 1053 string[size - itemCount - 1] = '\0'; 1054 strcat(string, data); 1055 } 1056 1057 if (!itemCount) 1058 { 1059 if (targets[i] == XA_STRING) 1060 { 1061 *selectionString = convertLatin1toUTF8(string); 1062 free(string); 1063 } 1064 else 1065 *selectionString = string; 1066 1067 break; 1068 } 1069 } 1070 } 1071 else if (actualType == targets[i]) 1072 { 1073 if (targets[i] == XA_STRING) 1074 *selectionString = convertLatin1toUTF8(data); 1075 else 1076 *selectionString = _glfw_strdup(data); 1077 } 1078 1079 XFree(data); 1080 1081 if (*selectionString) 1082 break; 1083 } 1084 1085 if (!*selectionString) 1086 { 1087 _glfwInputError(GLFW_FORMAT_UNAVAILABLE, 1088 "X11: Failed to convert selection to string"); 1089 } 1090 1091 return *selectionString; 1092 } 1093 1094 // Make the specified window and its video mode active on its monitor 1095 // 1096 static void acquireMonitor(_GLFWwindow* window) 1097 { 1098 if (_glfw.x11.saver.count == 0) 1099 { 1100 // Remember old screen saver settings 1101 XGetScreenSaver(_glfw.x11.display, 1102 &_glfw.x11.saver.timeout, 1103 &_glfw.x11.saver.interval, 1104 &_glfw.x11.saver.blanking, 1105 &_glfw.x11.saver.exposure); 1106 1107 // Disable screen saver 1108 XSetScreenSaver(_glfw.x11.display, 0, 0, DontPreferBlanking, 1109 DefaultExposures); 1110 } 1111 1112 if (!window->monitor->window) 1113 _glfw.x11.saver.count++; 1114 1115 _glfwSetVideoModeX11(window->monitor, &window->videoMode); 1116 1117 if (window->x11.overrideRedirect) 1118 { 1119 int xpos, ypos; 1120 GLFWvidmode mode; 1121 1122 // Manually position the window over its monitor 1123 _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); 1124 _glfwPlatformGetVideoMode(window->monitor, &mode); 1125 1126 XMoveResizeWindow(_glfw.x11.display, window->x11.handle, 1127 xpos, ypos, mode.width, mode.height); 1128 } 1129 1130 _glfwInputMonitorWindow(window->monitor, window); 1131 } 1132 1133 // Remove the window and restore the original video mode 1134 // 1135 static void releaseMonitor(_GLFWwindow* window) 1136 { 1137 if (window->monitor->window != window) 1138 return; 1139 1140 _glfwInputMonitorWindow(window->monitor, NULL); 1141 _glfwRestoreVideoModeX11(window->monitor); 1142 1143 _glfw.x11.saver.count--; 1144 1145 if (_glfw.x11.saver.count == 0) 1146 { 1147 // Restore old screen saver settings 1148 XSetScreenSaver(_glfw.x11.display, 1149 _glfw.x11.saver.timeout, 1150 _glfw.x11.saver.interval, 1151 _glfw.x11.saver.blanking, 1152 _glfw.x11.saver.exposure); 1153 } 1154 } 1155 1156 // Process the specified X event 1157 // 1158 static void processEvent(XEvent *event) 1159 { 1160 int keycode = 0; 1161 Bool filtered = False; 1162 1163 // HACK: Save scancode as some IMs clear the field in XFilterEvent 1164 if (event->type == KeyPress || event->type == KeyRelease) 1165 keycode = event->xkey.keycode; 1166 1167 filtered = XFilterEvent(event, None); 1168 1169 if (_glfw.x11.randr.available) 1170 { 1171 if (event->type == _glfw.x11.randr.eventBase + RRNotify) 1172 { 1173 XRRUpdateConfiguration(event); 1174 _glfwPollMonitorsX11(); 1175 return; 1176 } 1177 } 1178 1179 if (_glfw.x11.xkb.available) 1180 { 1181 if (event->type == _glfw.x11.xkb.eventBase + XkbEventCode) 1182 { 1183 if (((XkbEvent*) event)->any.xkb_type == XkbStateNotify && 1184 (((XkbEvent*) event)->state.changed & XkbGroupStateMask)) 1185 { 1186 _glfw.x11.xkb.group = ((XkbEvent*) event)->state.group; 1187 } 1188 } 1189 } 1190 1191 if (event->type == GenericEvent) 1192 { 1193 if (_glfw.x11.xi.available) 1194 { 1195 _GLFWwindow* window = _glfw.x11.disabledCursorWindow; 1196 1197 if (window && 1198 window->rawMouseMotion && 1199 event->xcookie.extension == _glfw.x11.xi.majorOpcode && 1200 XGetEventData(_glfw.x11.display, &event->xcookie) && 1201 event->xcookie.evtype == XI_RawMotion) 1202 { 1203 XIRawEvent* re = event->xcookie.data; 1204 if (re->valuators.mask_len) 1205 { 1206 const double* values = re->raw_values; 1207 double xpos = window->virtualCursorPosX; 1208 double ypos = window->virtualCursorPosY; 1209 1210 if (XIMaskIsSet(re->valuators.mask, 0)) 1211 { 1212 xpos += *values; 1213 values++; 1214 } 1215 1216 if (XIMaskIsSet(re->valuators.mask, 1)) 1217 ypos += *values; 1218 1219 _glfwInputCursorPos(window, xpos, ypos); 1220 } 1221 } 1222 1223 XFreeEventData(_glfw.x11.display, &event->xcookie); 1224 } 1225 1226 return; 1227 } 1228 1229 if (event->type == SelectionClear) 1230 { 1231 handleSelectionClear(event); 1232 return; 1233 } 1234 else if (event->type == SelectionRequest) 1235 { 1236 handleSelectionRequest(event); 1237 return; 1238 } 1239 1240 _GLFWwindow* window = NULL; 1241 if (XFindContext(_glfw.x11.display, 1242 event->xany.window, 1243 _glfw.x11.context, 1244 (XPointer*) &window) != 0) 1245 { 1246 // This is an event for a window that has already been destroyed 1247 return; 1248 } 1249 1250 switch (event->type) 1251 { 1252 case ReparentNotify: 1253 { 1254 window->x11.parent = event->xreparent.parent; 1255 return; 1256 } 1257 1258 case KeyPress: 1259 { 1260 const int key = translateKey(keycode); 1261 const int mods = translateState(event->xkey.state); 1262 const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); 1263 1264 if (window->x11.ic) 1265 { 1266 // HACK: Ignore duplicate key press events generated by ibus 1267 // These have the same timestamp as the original event 1268 // Corresponding release events are filtered out 1269 // implicitly by the GLFW key repeat logic 1270 if (window->x11.lastKeyTime < event->xkey.time) 1271 { 1272 if (keycode) 1273 _glfwInputKey(window, key, keycode, GLFW_PRESS, mods); 1274 1275 window->x11.lastKeyTime = event->xkey.time; 1276 } 1277 1278 if (!filtered) 1279 { 1280 int count; 1281 Status status; 1282 #if defined(X_HAVE_UTF8_STRING) 1283 char buffer[100]; 1284 char* chars = buffer; 1285 1286 count = Xutf8LookupString(window->x11.ic, 1287 &event->xkey, 1288 buffer, sizeof(buffer) - 1, 1289 NULL, &status); 1290 1291 if (status == XBufferOverflow) 1292 { 1293 chars = calloc(count + 1, 1); 1294 count = Xutf8LookupString(window->x11.ic, 1295 &event->xkey, 1296 chars, count, 1297 NULL, &status); 1298 } 1299 1300 if (status == XLookupChars || status == XLookupBoth) 1301 { 1302 const char* c = chars; 1303 chars[count] = '\0'; 1304 while (c - chars < count) 1305 _glfwInputChar(window, decodeUTF8(&c), mods, plain); 1306 } 1307 #else /*X_HAVE_UTF8_STRING*/ 1308 wchar_t buffer[16]; 1309 wchar_t* chars = buffer; 1310 1311 count = XwcLookupString(window->x11.ic, 1312 &event->xkey, 1313 buffer, 1314 sizeof(buffer) / sizeof(wchar_t), 1315 NULL, 1316 &status); 1317 1318 if (status == XBufferOverflow) 1319 { 1320 chars = calloc(count, sizeof(wchar_t)); 1321 count = XwcLookupString(window->x11.ic, 1322 &event->xkey, 1323 chars, count, 1324 NULL, &status); 1325 } 1326 1327 if (status == XLookupChars || status == XLookupBoth) 1328 { 1329 int i; 1330 for (i = 0; i < count; i++) 1331 _glfwInputChar(window, chars[i], mods, plain); 1332 } 1333 #endif /*X_HAVE_UTF8_STRING*/ 1334 1335 if (chars != buffer) 1336 free(chars); 1337 } 1338 } 1339 else 1340 { 1341 KeySym keysym; 1342 XLookupString(&event->xkey, NULL, 0, &keysym, NULL); 1343 1344 _glfwInputKey(window, key, keycode, GLFW_PRESS, mods); 1345 1346 const long character = _glfwKeySym2Unicode(keysym); 1347 if (character != -1) 1348 _glfwInputChar(window, character, mods, plain); 1349 } 1350 1351 return; 1352 } 1353 1354 case KeyRelease: 1355 { 1356 const int key = translateKey(keycode); 1357 const int mods = translateState(event->xkey.state); 1358 1359 if (!_glfw.x11.xkb.detectable) 1360 { 1361 // HACK: Key repeat events will arrive as KeyRelease/KeyPress 1362 // pairs with similar or identical time stamps 1363 // The key repeat logic in _glfwInputKey expects only key 1364 // presses to repeat, so detect and discard release events 1365 if (XEventsQueued(_glfw.x11.display, QueuedAfterReading)) 1366 { 1367 XEvent next; 1368 XPeekEvent(_glfw.x11.display, &next); 1369 1370 if (next.type == KeyPress && 1371 next.xkey.window == event->xkey.window && 1372 next.xkey.keycode == keycode) 1373 { 1374 // HACK: The time of repeat events sometimes doesn't 1375 // match that of the press event, so add an 1376 // epsilon 1377 // Toshiyuki Takahashi can press a button 1378 // 16 times per second so it's fairly safe to 1379 // assume that no human is pressing the key 50 1380 // times per second (value is ms) 1381 if ((next.xkey.time - event->xkey.time) < 20) 1382 { 1383 // This is very likely a server-generated key repeat 1384 // event, so ignore it 1385 return; 1386 } 1387 } 1388 } 1389 } 1390 1391 _glfwInputKey(window, key, keycode, GLFW_RELEASE, mods); 1392 return; 1393 } 1394 1395 case ButtonPress: 1396 { 1397 const int mods = translateState(event->xbutton.state); 1398 1399 if (event->xbutton.button == Button1) 1400 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, mods); 1401 else if (event->xbutton.button == Button2) 1402 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, mods); 1403 else if (event->xbutton.button == Button3) 1404 _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS, mods); 1405 1406 // Modern X provides scroll events as mouse button presses 1407 else if (event->xbutton.button == Button4) 1408 _glfwInputScroll(window, 0.0, 1.0); 1409 else if (event->xbutton.button == Button5) 1410 _glfwInputScroll(window, 0.0, -1.0); 1411 else if (event->xbutton.button == Button6) 1412 _glfwInputScroll(window, 1.0, 0.0); 1413 else if (event->xbutton.button == Button7) 1414 _glfwInputScroll(window, -1.0, 0.0); 1415 1416 else 1417 { 1418 // Additional buttons after 7 are treated as regular buttons 1419 // We subtract 4 to fill the gap left by scroll input above 1420 _glfwInputMouseClick(window, 1421 event->xbutton.button - Button1 - 4, 1422 GLFW_PRESS, 1423 mods); 1424 } 1425 1426 return; 1427 } 1428 1429 case ButtonRelease: 1430 { 1431 const int mods = translateState(event->xbutton.state); 1432 1433 if (event->xbutton.button == Button1) 1434 { 1435 _glfwInputMouseClick(window, 1436 GLFW_MOUSE_BUTTON_LEFT, 1437 GLFW_RELEASE, 1438 mods); 1439 } 1440 else if (event->xbutton.button == Button2) 1441 { 1442 _glfwInputMouseClick(window, 1443 GLFW_MOUSE_BUTTON_MIDDLE, 1444 GLFW_RELEASE, 1445 mods); 1446 } 1447 else if (event->xbutton.button == Button3) 1448 { 1449 _glfwInputMouseClick(window, 1450 GLFW_MOUSE_BUTTON_RIGHT, 1451 GLFW_RELEASE, 1452 mods); 1453 } 1454 else if (event->xbutton.button > Button7) 1455 { 1456 // Additional buttons after 7 are treated as regular buttons 1457 // We subtract 4 to fill the gap left by scroll input above 1458 _glfwInputMouseClick(window, 1459 event->xbutton.button - Button1 - 4, 1460 GLFW_RELEASE, 1461 mods); 1462 } 1463 1464 return; 1465 } 1466 1467 case EnterNotify: 1468 { 1469 // XEnterWindowEvent is XCrossingEvent 1470 const int x = event->xcrossing.x; 1471 const int y = event->xcrossing.y; 1472 1473 // HACK: This is a workaround for WMs (KWM, Fluxbox) that otherwise 1474 // ignore the defined cursor for hidden cursor mode 1475 if (window->cursorMode == GLFW_CURSOR_HIDDEN) 1476 updateCursorImage(window); 1477 1478 _glfwInputCursorEnter(window, GLFW_TRUE); 1479 _glfwInputCursorPos(window, x, y); 1480 1481 window->x11.lastCursorPosX = x; 1482 window->x11.lastCursorPosY = y; 1483 return; 1484 } 1485 1486 case LeaveNotify: 1487 { 1488 _glfwInputCursorEnter(window, GLFW_FALSE); 1489 return; 1490 } 1491 1492 case MotionNotify: 1493 { 1494 const int x = event->xmotion.x; 1495 const int y = event->xmotion.y; 1496 1497 if (x != window->x11.warpCursorPosX || 1498 y != window->x11.warpCursorPosY) 1499 { 1500 // The cursor was moved by something other than GLFW 1501 1502 if (window->cursorMode == GLFW_CURSOR_DISABLED) 1503 { 1504 if (_glfw.x11.disabledCursorWindow != window) 1505 return; 1506 if (window->rawMouseMotion) 1507 return; 1508 1509 const int dx = x - window->x11.lastCursorPosX; 1510 const int dy = y - window->x11.lastCursorPosY; 1511 1512 _glfwInputCursorPos(window, 1513 window->virtualCursorPosX + dx, 1514 window->virtualCursorPosY + dy); 1515 } 1516 else 1517 _glfwInputCursorPos(window, x, y); 1518 } 1519 1520 window->x11.lastCursorPosX = x; 1521 window->x11.lastCursorPosY = y; 1522 return; 1523 } 1524 1525 case ConfigureNotify: 1526 { 1527 if (event->xconfigure.width != window->x11.width || 1528 event->xconfigure.height != window->x11.height) 1529 { 1530 _glfwInputFramebufferSize(window, 1531 event->xconfigure.width, 1532 event->xconfigure.height); 1533 1534 _glfwInputWindowSize(window, 1535 event->xconfigure.width, 1536 event->xconfigure.height); 1537 1538 window->x11.width = event->xconfigure.width; 1539 window->x11.height = event->xconfigure.height; 1540 } 1541 1542 int xpos = event->xconfigure.x; 1543 int ypos = event->xconfigure.y; 1544 1545 // NOTE: ConfigureNotify events from the server are in local 1546 // coordinates, so if we are reparented we need to translate 1547 // the position into root (screen) coordinates 1548 if (!event->xany.send_event && window->x11.parent != _glfw.x11.root) 1549 { 1550 Window dummy; 1551 XTranslateCoordinates(_glfw.x11.display, 1552 window->x11.parent, 1553 _glfw.x11.root, 1554 xpos, ypos, 1555 &xpos, &ypos, 1556 &dummy); 1557 } 1558 1559 if (xpos != window->x11.xpos || ypos != window->x11.ypos) 1560 { 1561 _glfwInputWindowPos(window, xpos, ypos); 1562 window->x11.xpos = xpos; 1563 window->x11.ypos = ypos; 1564 } 1565 1566 return; 1567 } 1568 1569 case ClientMessage: 1570 { 1571 // Custom client message, probably from the window manager 1572 1573 if (filtered) 1574 return; 1575 1576 if (event->xclient.message_type == None) 1577 return; 1578 1579 if (event->xclient.message_type == _glfw.x11.WM_PROTOCOLS) 1580 { 1581 const Atom protocol = event->xclient.data.l[0]; 1582 if (protocol == None) 1583 return; 1584 1585 if (protocol == _glfw.x11.WM_DELETE_WINDOW) 1586 { 1587 // The window manager was asked to close the window, for 1588 // example by the user pressing a 'close' window decoration 1589 // button 1590 _glfwInputWindowCloseRequest(window); 1591 } 1592 else if (protocol == _glfw.x11.NET_WM_PING) 1593 { 1594 // The window manager is pinging the application to ensure 1595 // it's still responding to events 1596 1597 XEvent reply = *event; 1598 reply.xclient.window = _glfw.x11.root; 1599 1600 XSendEvent(_glfw.x11.display, _glfw.x11.root, 1601 False, 1602 SubstructureNotifyMask | SubstructureRedirectMask, 1603 &reply); 1604 } 1605 } 1606 else if (event->xclient.message_type == _glfw.x11.XdndEnter) 1607 { 1608 // A drag operation has entered the window 1609 unsigned long i, count; 1610 Atom* formats = NULL; 1611 const GLFWbool list = event->xclient.data.l[1] & 1; 1612 1613 _glfw.x11.xdnd.source = event->xclient.data.l[0]; 1614 _glfw.x11.xdnd.version = event->xclient.data.l[1] >> 24; 1615 _glfw.x11.xdnd.format = None; 1616 1617 if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION) 1618 return; 1619 1620 if (list) 1621 { 1622 count = _glfwGetWindowPropertyX11(_glfw.x11.xdnd.source, 1623 _glfw.x11.XdndTypeList, 1624 XA_ATOM, 1625 (unsigned char**) &formats); 1626 } 1627 else 1628 { 1629 count = 3; 1630 formats = (Atom*) event->xclient.data.l + 2; 1631 } 1632 1633 for (i = 0; i < count; i++) 1634 { 1635 if (formats[i] == _glfw.x11.text_uri_list) 1636 { 1637 _glfw.x11.xdnd.format = _glfw.x11.text_uri_list; 1638 break; 1639 } 1640 } 1641 1642 if (list && formats) 1643 XFree(formats); 1644 } 1645 else if (event->xclient.message_type == _glfw.x11.XdndDrop) 1646 { 1647 // The drag operation has finished by dropping on the window 1648 Time time = CurrentTime; 1649 1650 if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION) 1651 return; 1652 1653 if (_glfw.x11.xdnd.format) 1654 { 1655 if (_glfw.x11.xdnd.version >= 1) 1656 time = event->xclient.data.l[2]; 1657 1658 // Request the chosen format from the source window 1659 XConvertSelection(_glfw.x11.display, 1660 _glfw.x11.XdndSelection, 1661 _glfw.x11.xdnd.format, 1662 _glfw.x11.XdndSelection, 1663 window->x11.handle, 1664 time); 1665 } 1666 else if (_glfw.x11.xdnd.version >= 2) 1667 { 1668 XEvent reply = { ClientMessage }; 1669 reply.xclient.window = _glfw.x11.xdnd.source; 1670 reply.xclient.message_type = _glfw.x11.XdndFinished; 1671 reply.xclient.format = 32; 1672 reply.xclient.data.l[0] = window->x11.handle; 1673 reply.xclient.data.l[1] = 0; // The drag was rejected 1674 reply.xclient.data.l[2] = None; 1675 1676 XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, 1677 False, NoEventMask, &reply); 1678 XFlush(_glfw.x11.display); 1679 } 1680 } 1681 else if (event->xclient.message_type == _glfw.x11.XdndPosition) 1682 { 1683 // The drag operation has moved over the window 1684 const int xabs = (event->xclient.data.l[2] >> 16) & 0xffff; 1685 const int yabs = (event->xclient.data.l[2]) & 0xffff; 1686 Window dummy; 1687 int xpos, ypos; 1688 1689 if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION) 1690 return; 1691 1692 XTranslateCoordinates(_glfw.x11.display, 1693 _glfw.x11.root, 1694 window->x11.handle, 1695 xabs, yabs, 1696 &xpos, &ypos, 1697 &dummy); 1698 1699 _glfwInputCursorPos(window, xpos, ypos); 1700 1701 XEvent reply = { ClientMessage }; 1702 reply.xclient.window = _glfw.x11.xdnd.source; 1703 reply.xclient.message_type = _glfw.x11.XdndStatus; 1704 reply.xclient.format = 32; 1705 reply.xclient.data.l[0] = window->x11.handle; 1706 reply.xclient.data.l[2] = 0; // Specify an empty rectangle 1707 reply.xclient.data.l[3] = 0; 1708 1709 if (_glfw.x11.xdnd.format) 1710 { 1711 // Reply that we are ready to copy the dragged data 1712 reply.xclient.data.l[1] = 1; // Accept with no rectangle 1713 if (_glfw.x11.xdnd.version >= 2) 1714 reply.xclient.data.l[4] = _glfw.x11.XdndActionCopy; 1715 } 1716 1717 XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, 1718 False, NoEventMask, &reply); 1719 XFlush(_glfw.x11.display); 1720 } 1721 1722 return; 1723 } 1724 1725 case SelectionNotify: 1726 { 1727 if (event->xselection.property == _glfw.x11.XdndSelection) 1728 { 1729 // The converted data from the drag operation has arrived 1730 char* data; 1731 const unsigned long result = 1732 _glfwGetWindowPropertyX11(event->xselection.requestor, 1733 event->xselection.property, 1734 event->xselection.target, 1735 (unsigned char**) &data); 1736 1737 if (result) 1738 { 1739 int i, count; 1740 char** paths = parseUriList(data, &count); 1741 1742 _glfwInputDrop(window, count, (const char**) paths); 1743 1744 for (i = 0; i < count; i++) 1745 free(paths[i]); 1746 free(paths); 1747 } 1748 1749 if (data) 1750 XFree(data); 1751 1752 if (_glfw.x11.xdnd.version >= 2) 1753 { 1754 XEvent reply = { ClientMessage }; 1755 reply.xclient.window = _glfw.x11.xdnd.source; 1756 reply.xclient.message_type = _glfw.x11.XdndFinished; 1757 reply.xclient.format = 32; 1758 reply.xclient.data.l[0] = window->x11.handle; 1759 reply.xclient.data.l[1] = result; 1760 reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy; 1761 1762 XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, 1763 False, NoEventMask, &reply); 1764 XFlush(_glfw.x11.display); 1765 } 1766 } 1767 1768 return; 1769 } 1770 1771 case FocusIn: 1772 { 1773 if (event->xfocus.mode == NotifyGrab || 1774 event->xfocus.mode == NotifyUngrab) 1775 { 1776 // Ignore focus events from popup indicator windows, window menu 1777 // key chords and window dragging 1778 return; 1779 } 1780 1781 if (window->cursorMode == GLFW_CURSOR_DISABLED) 1782 disableCursor(window); 1783 1784 if (window->x11.ic) 1785 XSetICFocus(window->x11.ic); 1786 1787 _glfwInputWindowFocus(window, GLFW_TRUE); 1788 return; 1789 } 1790 1791 case FocusOut: 1792 { 1793 if (event->xfocus.mode == NotifyGrab || 1794 event->xfocus.mode == NotifyUngrab) 1795 { 1796 // Ignore focus events from popup indicator windows, window menu 1797 // key chords and window dragging 1798 return; 1799 } 1800 1801 if (window->cursorMode == GLFW_CURSOR_DISABLED) 1802 enableCursor(window); 1803 1804 if (window->x11.ic) 1805 XUnsetICFocus(window->x11.ic); 1806 1807 if (window->monitor && window->autoIconify) 1808 _glfwPlatformIconifyWindow(window); 1809 1810 _glfwInputWindowFocus(window, GLFW_FALSE); 1811 return; 1812 } 1813 1814 case Expose: 1815 { 1816 _glfwInputWindowDamage(window); 1817 return; 1818 } 1819 1820 case PropertyNotify: 1821 { 1822 if (event->xproperty.state != PropertyNewValue) 1823 return; 1824 1825 if (event->xproperty.atom == _glfw.x11.WM_STATE) 1826 { 1827 const int state = getWindowState(window); 1828 if (state != IconicState && state != NormalState) 1829 return; 1830 1831 const GLFWbool iconified = (state == IconicState); 1832 if (window->x11.iconified != iconified) 1833 { 1834 if (window->monitor) 1835 { 1836 if (iconified) 1837 releaseMonitor(window); 1838 else 1839 acquireMonitor(window); 1840 } 1841 1842 window->x11.iconified = iconified; 1843 _glfwInputWindowIconify(window, iconified); 1844 } 1845 } 1846 else if (event->xproperty.atom == _glfw.x11.NET_WM_STATE) 1847 { 1848 const GLFWbool maximized = _glfwPlatformWindowMaximized(window); 1849 if (window->x11.maximized != maximized) 1850 { 1851 window->x11.maximized = maximized; 1852 _glfwInputWindowMaximize(window, maximized); 1853 } 1854 } 1855 1856 return; 1857 } 1858 1859 case DestroyNotify: 1860 return; 1861 } 1862 } 1863 1864 1865 ////////////////////////////////////////////////////////////////////////// 1866 ////// GLFW internal API ////// 1867 ////////////////////////////////////////////////////////////////////////// 1868 1869 // Retrieve a single window property of the specified type 1870 // Inspired by fghGetWindowProperty from freeglut 1871 // 1872 unsigned long _glfwGetWindowPropertyX11(Window window, 1873 Atom property, 1874 Atom type, 1875 unsigned char** value) 1876 { 1877 Atom actualType; 1878 int actualFormat; 1879 unsigned long itemCount, bytesAfter; 1880 1881 XGetWindowProperty(_glfw.x11.display, 1882 window, 1883 property, 1884 0, 1885 LONG_MAX, 1886 False, 1887 type, 1888 &actualType, 1889 &actualFormat, 1890 &itemCount, 1891 &bytesAfter, 1892 value); 1893 1894 return itemCount; 1895 } 1896 1897 GLFWbool _glfwIsVisualTransparentX11(Visual* visual) 1898 { 1899 if (!_glfw.x11.xrender.available) 1900 return GLFW_FALSE; 1901 1902 XRenderPictFormat* pf = XRenderFindVisualFormat(_glfw.x11.display, visual); 1903 return pf && pf->direct.alphaMask; 1904 } 1905 1906 // Push contents of our selection to clipboard manager 1907 // 1908 void _glfwPushSelectionToManagerX11(void) 1909 { 1910 XConvertSelection(_glfw.x11.display, 1911 _glfw.x11.CLIPBOARD_MANAGER, 1912 _glfw.x11.SAVE_TARGETS, 1913 None, 1914 _glfw.x11.helperWindowHandle, 1915 CurrentTime); 1916 1917 for (;;) 1918 { 1919 XEvent event; 1920 1921 while (XCheckIfEvent(_glfw.x11.display, &event, isSelectionEvent, NULL)) 1922 { 1923 switch (event.type) 1924 { 1925 case SelectionRequest: 1926 handleSelectionRequest(&event); 1927 break; 1928 1929 case SelectionClear: 1930 handleSelectionClear(&event); 1931 break; 1932 1933 case SelectionNotify: 1934 { 1935 if (event.xselection.target == _glfw.x11.SAVE_TARGETS) 1936 { 1937 // This means one of two things; either the selection 1938 // was not owned, which means there is no clipboard 1939 // manager, or the transfer to the clipboard manager has 1940 // completed 1941 // In either case, it means we are done here 1942 return; 1943 } 1944 1945 break; 1946 } 1947 } 1948 } 1949 1950 waitForEvent(NULL); 1951 } 1952 } 1953 1954 void _glfwCreateInputContextX11(_GLFWwindow* window) 1955 { 1956 XIMCallback callback; 1957 callback.callback = (XIMProc) inputContextDestroyCallback; 1958 callback.client_data = (XPointer) window; 1959 1960 window->x11.ic = XCreateIC(_glfw.x11.im, 1961 XNInputStyle, 1962 XIMPreeditNothing | XIMStatusNothing, 1963 XNClientWindow, 1964 window->x11.handle, 1965 XNFocusWindow, 1966 window->x11.handle, 1967 XNDestroyCallback, 1968 &callback, 1969 NULL); 1970 1971 if (window->x11.ic) 1972 { 1973 XWindowAttributes attribs; 1974 XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &attribs); 1975 1976 unsigned long filter = 0; 1977 if (XGetICValues(window->x11.ic, XNFilterEvents, &filter, NULL) == NULL) 1978 { 1979 XSelectInput(_glfw.x11.display, 1980 window->x11.handle, 1981 attribs.your_event_mask | filter); 1982 } 1983 } 1984 } 1985 1986 1987 ////////////////////////////////////////////////////////////////////////// 1988 ////// GLFW platform API ////// 1989 ////////////////////////////////////////////////////////////////////////// 1990 1991 int _glfwPlatformCreateWindow(_GLFWwindow* window, 1992 const _GLFWwndconfig* wndconfig, 1993 const _GLFWctxconfig* ctxconfig, 1994 const _GLFWfbconfig* fbconfig) 1995 { 1996 Visual* visual; 1997 int depth; 1998 1999 if (ctxconfig->client != GLFW_NO_API) 2000 { 2001 if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) 2002 { 2003 if (!_glfwInitGLX()) 2004 return GLFW_FALSE; 2005 if (!_glfwChooseVisualGLX(wndconfig, ctxconfig, fbconfig, &visual, &depth)) 2006 return GLFW_FALSE; 2007 } 2008 else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) 2009 { 2010 if (!_glfwInitEGL()) 2011 return GLFW_FALSE; 2012 if (!_glfwChooseVisualEGL(wndconfig, ctxconfig, fbconfig, &visual, &depth)) 2013 return GLFW_FALSE; 2014 } 2015 else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) 2016 { 2017 if (!_glfwInitOSMesa()) 2018 return GLFW_FALSE; 2019 } 2020 } 2021 2022 if (ctxconfig->client == GLFW_NO_API || 2023 ctxconfig->source == GLFW_OSMESA_CONTEXT_API) 2024 { 2025 visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen); 2026 depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen); 2027 } 2028 2029 if (!createNativeWindow(window, wndconfig, visual, depth)) 2030 return GLFW_FALSE; 2031 2032 if (ctxconfig->client != GLFW_NO_API) 2033 { 2034 if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) 2035 { 2036 if (!_glfwCreateContextGLX(window, ctxconfig, fbconfig)) 2037 return GLFW_FALSE; 2038 } 2039 else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) 2040 { 2041 if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) 2042 return GLFW_FALSE; 2043 } 2044 else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) 2045 { 2046 if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) 2047 return GLFW_FALSE; 2048 } 2049 } 2050 2051 if (window->monitor) 2052 { 2053 _glfwPlatformShowWindow(window); 2054 updateWindowMode(window); 2055 acquireMonitor(window); 2056 } 2057 2058 XFlush(_glfw.x11.display); 2059 return GLFW_TRUE; 2060 } 2061 2062 void _glfwPlatformDestroyWindow(_GLFWwindow* window) 2063 { 2064 if (_glfw.x11.disabledCursorWindow == window) 2065 _glfw.x11.disabledCursorWindow = NULL; 2066 2067 if (window->monitor) 2068 releaseMonitor(window); 2069 2070 if (window->x11.ic) 2071 { 2072 XDestroyIC(window->x11.ic); 2073 window->x11.ic = NULL; 2074 } 2075 2076 if (window->context.destroy) 2077 window->context.destroy(window); 2078 2079 if (window->x11.handle) 2080 { 2081 XDeleteContext(_glfw.x11.display, window->x11.handle, _glfw.x11.context); 2082 XUnmapWindow(_glfw.x11.display, window->x11.handle); 2083 XDestroyWindow(_glfw.x11.display, window->x11.handle); 2084 window->x11.handle = (Window) 0; 2085 } 2086 2087 if (window->x11.colormap) 2088 { 2089 XFreeColormap(_glfw.x11.display, window->x11.colormap); 2090 window->x11.colormap = (Colormap) 0; 2091 } 2092 2093 XFlush(_glfw.x11.display); 2094 } 2095 2096 void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) 2097 { 2098 #if defined(X_HAVE_UTF8_STRING) 2099 Xutf8SetWMProperties(_glfw.x11.display, 2100 window->x11.handle, 2101 title, title, 2102 NULL, 0, 2103 NULL, NULL, NULL); 2104 #else 2105 // This may be a slightly better fallback than using XStoreName and 2106 // XSetIconName, which always store their arguments using STRING 2107 XmbSetWMProperties(_glfw.x11.display, 2108 window->x11.handle, 2109 title, title, 2110 NULL, 0, 2111 NULL, NULL, NULL); 2112 #endif 2113 2114 XChangeProperty(_glfw.x11.display, window->x11.handle, 2115 _glfw.x11.NET_WM_NAME, _glfw.x11.UTF8_STRING, 8, 2116 PropModeReplace, 2117 (unsigned char*) title, strlen(title)); 2118 2119 XChangeProperty(_glfw.x11.display, window->x11.handle, 2120 _glfw.x11.NET_WM_ICON_NAME, _glfw.x11.UTF8_STRING, 8, 2121 PropModeReplace, 2122 (unsigned char*) title, strlen(title)); 2123 2124 XFlush(_glfw.x11.display); 2125 } 2126 2127 void _glfwPlatformSetWindowIcon(_GLFWwindow* window, 2128 int count, const GLFWimage* images) 2129 { 2130 if (count) 2131 { 2132 int i, j, longCount = 0; 2133 2134 for (i = 0; i < count; i++) 2135 longCount += 2 + images[i].width * images[i].height; 2136 2137 long* icon = calloc(longCount, sizeof(long)); 2138 long* target = icon; 2139 2140 for (i = 0; i < count; i++) 2141 { 2142 *target++ = images[i].width; 2143 *target++ = images[i].height; 2144 2145 for (j = 0; j < images[i].width * images[i].height; j++) 2146 { 2147 *target++ = (images[i].pixels[j * 4 + 0] << 16) | 2148 (images[i].pixels[j * 4 + 1] << 8) | 2149 (images[i].pixels[j * 4 + 2] << 0) | 2150 (images[i].pixels[j * 4 + 3] << 24); 2151 } 2152 } 2153 2154 XChangeProperty(_glfw.x11.display, window->x11.handle, 2155 _glfw.x11.NET_WM_ICON, 2156 XA_CARDINAL, 32, 2157 PropModeReplace, 2158 (unsigned char*) icon, 2159 longCount); 2160 2161 free(icon); 2162 } 2163 else 2164 { 2165 XDeleteProperty(_glfw.x11.display, window->x11.handle, 2166 _glfw.x11.NET_WM_ICON); 2167 } 2168 2169 XFlush(_glfw.x11.display); 2170 } 2171 2172 void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) 2173 { 2174 Window dummy; 2175 int x, y; 2176 2177 XTranslateCoordinates(_glfw.x11.display, window->x11.handle, _glfw.x11.root, 2178 0, 0, &x, &y, &dummy); 2179 2180 if (xpos) 2181 *xpos = x; 2182 if (ypos) 2183 *ypos = y; 2184 } 2185 2186 void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) 2187 { 2188 // HACK: Explicitly setting PPosition to any value causes some WMs, notably 2189 // Compiz and Metacity, to honor the position of unmapped windows 2190 if (!_glfwPlatformWindowVisible(window)) 2191 { 2192 long supplied; 2193 XSizeHints* hints = XAllocSizeHints(); 2194 2195 if (XGetWMNormalHints(_glfw.x11.display, window->x11.handle, hints, &supplied)) 2196 { 2197 hints->flags |= PPosition; 2198 hints->x = hints->y = 0; 2199 2200 XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints); 2201 } 2202 2203 XFree(hints); 2204 } 2205 2206 XMoveWindow(_glfw.x11.display, window->x11.handle, xpos, ypos); 2207 XFlush(_glfw.x11.display); 2208 } 2209 2210 void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) 2211 { 2212 XWindowAttributes attribs; 2213 XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &attribs); 2214 2215 if (width) 2216 *width = attribs.width; 2217 if (height) 2218 *height = attribs.height; 2219 } 2220 2221 void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) 2222 { 2223 if (window->monitor) 2224 { 2225 if (window->monitor->window == window) 2226 acquireMonitor(window); 2227 } 2228 else 2229 { 2230 if (!window->resizable) 2231 updateNormalHints(window, width, height); 2232 2233 XResizeWindow(_glfw.x11.display, window->x11.handle, width, height); 2234 } 2235 2236 XFlush(_glfw.x11.display); 2237 } 2238 2239 void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, 2240 int minwidth, int minheight, 2241 int maxwidth, int maxheight) 2242 { 2243 int width, height; 2244 _glfwPlatformGetWindowSize(window, &width, &height); 2245 updateNormalHints(window, width, height); 2246 XFlush(_glfw.x11.display); 2247 } 2248 2249 void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) 2250 { 2251 int width, height; 2252 _glfwPlatformGetWindowSize(window, &width, &height); 2253 updateNormalHints(window, width, height); 2254 XFlush(_glfw.x11.display); 2255 } 2256 2257 void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) 2258 { 2259 _glfwPlatformGetWindowSize(window, width, height); 2260 } 2261 2262 void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, 2263 int* left, int* top, 2264 int* right, int* bottom) 2265 { 2266 long* extents = NULL; 2267 2268 if (window->monitor || !window->decorated) 2269 return; 2270 2271 if (_glfw.x11.NET_FRAME_EXTENTS == None) 2272 return; 2273 2274 if (!_glfwPlatformWindowVisible(window) && 2275 _glfw.x11.NET_REQUEST_FRAME_EXTENTS) 2276 { 2277 XEvent event; 2278 double timeout = 0.5; 2279 2280 // Ensure _NET_FRAME_EXTENTS is set, allowing glfwGetWindowFrameSize to 2281 // function before the window is mapped 2282 sendEventToWM(window, _glfw.x11.NET_REQUEST_FRAME_EXTENTS, 2283 0, 0, 0, 0, 0); 2284 2285 // HACK: Use a timeout because earlier versions of some window managers 2286 // (at least Unity, Fluxbox and Xfwm) failed to send the reply 2287 // They have been fixed but broken versions are still in the wild 2288 // If you are affected by this and your window manager is NOT 2289 // listed above, PLEASE report it to their and our issue trackers 2290 while (!XCheckIfEvent(_glfw.x11.display, 2291 &event, 2292 isFrameExtentsEvent, 2293 (XPointer) window)) 2294 { 2295 if (!waitForEvent(&timeout)) 2296 { 2297 _glfwInputError(GLFW_PLATFORM_ERROR, 2298 "X11: The window manager has a broken _NET_REQUEST_FRAME_EXTENTS implementation; please report this issue"); 2299 return; 2300 } 2301 } 2302 } 2303 2304 if (_glfwGetWindowPropertyX11(window->x11.handle, 2305 _glfw.x11.NET_FRAME_EXTENTS, 2306 XA_CARDINAL, 2307 (unsigned char**) &extents) == 4) 2308 { 2309 if (left) 2310 *left = extents[0]; 2311 if (top) 2312 *top = extents[2]; 2313 if (right) 2314 *right = extents[1]; 2315 if (bottom) 2316 *bottom = extents[3]; 2317 } 2318 2319 if (extents) 2320 XFree(extents); 2321 } 2322 2323 void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, 2324 float* xscale, float* yscale) 2325 { 2326 if (xscale) 2327 *xscale = _glfw.x11.contentScaleX; 2328 if (yscale) 2329 *yscale = _glfw.x11.contentScaleY; 2330 } 2331 2332 void _glfwPlatformIconifyWindow(_GLFWwindow* window) 2333 { 2334 if (window->x11.overrideRedirect) 2335 { 2336 // Override-redirect windows cannot be iconified or restored, as those 2337 // tasks are performed by the window manager 2338 _glfwInputError(GLFW_PLATFORM_ERROR, 2339 "X11: Iconification of full screen windows requires a WM that supports EWMH full screen"); 2340 return; 2341 } 2342 2343 XIconifyWindow(_glfw.x11.display, window->x11.handle, _glfw.x11.screen); 2344 XFlush(_glfw.x11.display); 2345 } 2346 2347 void _glfwPlatformRestoreWindow(_GLFWwindow* window) 2348 { 2349 if (window->x11.overrideRedirect) 2350 { 2351 // Override-redirect windows cannot be iconified or restored, as those 2352 // tasks are performed by the window manager 2353 _glfwInputError(GLFW_PLATFORM_ERROR, 2354 "X11: Iconification of full screen windows requires a WM that supports EWMH full screen"); 2355 return; 2356 } 2357 2358 if (_glfwPlatformWindowIconified(window)) 2359 { 2360 XMapWindow(_glfw.x11.display, window->x11.handle); 2361 waitForVisibilityNotify(window); 2362 } 2363 else if (_glfwPlatformWindowVisible(window)) 2364 { 2365 if (_glfw.x11.NET_WM_STATE && 2366 _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT && 2367 _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) 2368 { 2369 sendEventToWM(window, 2370 _glfw.x11.NET_WM_STATE, 2371 _NET_WM_STATE_REMOVE, 2372 _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT, 2373 _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ, 2374 1, 0); 2375 } 2376 } 2377 2378 XFlush(_glfw.x11.display); 2379 } 2380 2381 void _glfwPlatformMaximizeWindow(_GLFWwindow* window) 2382 { 2383 if (!_glfw.x11.NET_WM_STATE || 2384 !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT || 2385 !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) 2386 { 2387 return; 2388 } 2389 2390 if (_glfwPlatformWindowVisible(window)) 2391 { 2392 sendEventToWM(window, 2393 _glfw.x11.NET_WM_STATE, 2394 _NET_WM_STATE_ADD, 2395 _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT, 2396 _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ, 2397 1, 0); 2398 } 2399 else 2400 { 2401 Atom* states = NULL; 2402 unsigned long count = 2403 _glfwGetWindowPropertyX11(window->x11.handle, 2404 _glfw.x11.NET_WM_STATE, 2405 XA_ATOM, 2406 (unsigned char**) &states); 2407 2408 // NOTE: We don't check for failure as this property may not exist yet 2409 // and that's fine (and we'll create it implicitly with append) 2410 2411 Atom missing[2] = 2412 { 2413 _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT, 2414 _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ 2415 }; 2416 unsigned long missingCount = 2; 2417 2418 for (unsigned long i = 0; i < count; i++) 2419 { 2420 for (unsigned long j = 0; j < missingCount; j++) 2421 { 2422 if (states[i] == missing[j]) 2423 { 2424 missing[j] = missing[missingCount - 1]; 2425 missingCount--; 2426 } 2427 } 2428 } 2429 2430 if (states) 2431 XFree(states); 2432 2433 if (!missingCount) 2434 return; 2435 2436 XChangeProperty(_glfw.x11.display, window->x11.handle, 2437 _glfw.x11.NET_WM_STATE, XA_ATOM, 32, 2438 PropModeAppend, 2439 (unsigned char*) missing, 2440 missingCount); 2441 } 2442 2443 XFlush(_glfw.x11.display); 2444 } 2445 2446 void _glfwPlatformShowWindow(_GLFWwindow* window) 2447 { 2448 if (_glfwPlatformWindowVisible(window)) 2449 return; 2450 2451 XMapWindow(_glfw.x11.display, window->x11.handle); 2452 waitForVisibilityNotify(window); 2453 } 2454 2455 void _glfwPlatformHideWindow(_GLFWwindow* window) 2456 { 2457 XUnmapWindow(_glfw.x11.display, window->x11.handle); 2458 XFlush(_glfw.x11.display); 2459 } 2460 2461 void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) 2462 { 2463 if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION) 2464 return; 2465 2466 sendEventToWM(window, 2467 _glfw.x11.NET_WM_STATE, 2468 _NET_WM_STATE_ADD, 2469 _glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION, 2470 0, 1, 0); 2471 } 2472 2473 void _glfwPlatformFocusWindow(_GLFWwindow* window) 2474 { 2475 if (_glfw.x11.NET_ACTIVE_WINDOW) 2476 sendEventToWM(window, _glfw.x11.NET_ACTIVE_WINDOW, 1, 0, 0, 0, 0); 2477 else if (_glfwPlatformWindowVisible(window)) 2478 { 2479 XRaiseWindow(_glfw.x11.display, window->x11.handle); 2480 XSetInputFocus(_glfw.x11.display, window->x11.handle, 2481 RevertToParent, CurrentTime); 2482 } 2483 2484 XFlush(_glfw.x11.display); 2485 } 2486 2487 void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, 2488 _GLFWmonitor* monitor, 2489 int xpos, int ypos, 2490 int width, int height, 2491 int refreshRate) 2492 { 2493 if (window->monitor == monitor) 2494 { 2495 if (monitor) 2496 { 2497 if (monitor->window == window) 2498 acquireMonitor(window); 2499 } 2500 else 2501 { 2502 if (!window->resizable) 2503 updateNormalHints(window, width, height); 2504 2505 XMoveResizeWindow(_glfw.x11.display, window->x11.handle, 2506 xpos, ypos, width, height); 2507 } 2508 2509 XFlush(_glfw.x11.display); 2510 return; 2511 } 2512 2513 if (window->monitor) 2514 releaseMonitor(window); 2515 2516 _glfwInputWindowMonitor(window, monitor); 2517 updateNormalHints(window, width, height); 2518 2519 if (window->monitor) 2520 { 2521 if (!_glfwPlatformWindowVisible(window)) 2522 { 2523 XMapRaised(_glfw.x11.display, window->x11.handle); 2524 waitForVisibilityNotify(window); 2525 } 2526 2527 updateWindowMode(window); 2528 acquireMonitor(window); 2529 } 2530 else 2531 { 2532 updateWindowMode(window); 2533 XMoveResizeWindow(_glfw.x11.display, window->x11.handle, 2534 xpos, ypos, width, height); 2535 } 2536 2537 XFlush(_glfw.x11.display); 2538 } 2539 2540 int _glfwPlatformWindowFocused(_GLFWwindow* window) 2541 { 2542 Window focused; 2543 int state; 2544 2545 XGetInputFocus(_glfw.x11.display, &focused, &state); 2546 return window->x11.handle == focused; 2547 } 2548 2549 int _glfwPlatformWindowIconified(_GLFWwindow* window) 2550 { 2551 return getWindowState(window) == IconicState; 2552 } 2553 2554 int _glfwPlatformWindowVisible(_GLFWwindow* window) 2555 { 2556 XWindowAttributes wa; 2557 XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &wa); 2558 return wa.map_state == IsViewable; 2559 } 2560 2561 int _glfwPlatformWindowMaximized(_GLFWwindow* window) 2562 { 2563 Atom* states; 2564 unsigned long i; 2565 GLFWbool maximized = GLFW_FALSE; 2566 2567 if (!_glfw.x11.NET_WM_STATE || 2568 !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT || 2569 !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) 2570 { 2571 return maximized; 2572 } 2573 2574 const unsigned long count = 2575 _glfwGetWindowPropertyX11(window->x11.handle, 2576 _glfw.x11.NET_WM_STATE, 2577 XA_ATOM, 2578 (unsigned char**) &states); 2579 2580 for (i = 0; i < count; i++) 2581 { 2582 if (states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT || 2583 states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) 2584 { 2585 maximized = GLFW_TRUE; 2586 break; 2587 } 2588 } 2589 2590 if (states) 2591 XFree(states); 2592 2593 return maximized; 2594 } 2595 2596 int _glfwPlatformWindowHovered(_GLFWwindow* window) 2597 { 2598 Window w = _glfw.x11.root; 2599 while (w) 2600 { 2601 Window root; 2602 int rootX, rootY, childX, childY; 2603 unsigned int mask; 2604 2605 if (!XQueryPointer(_glfw.x11.display, w, 2606 &root, &w, &rootX, &rootY, &childX, &childY, &mask)) 2607 { 2608 return GLFW_FALSE; 2609 } 2610 2611 if (w == window->x11.handle) 2612 return GLFW_TRUE; 2613 } 2614 2615 return GLFW_FALSE; 2616 } 2617 2618 int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) 2619 { 2620 if (!window->x11.transparent) 2621 return GLFW_FALSE; 2622 2623 return XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx) != None; 2624 } 2625 2626 void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) 2627 { 2628 int width, height; 2629 _glfwPlatformGetWindowSize(window, &width, &height); 2630 updateNormalHints(window, width, height); 2631 } 2632 2633 void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) 2634 { 2635 struct 2636 { 2637 unsigned long flags; 2638 unsigned long functions; 2639 unsigned long decorations; 2640 long input_mode; 2641 unsigned long status; 2642 } hints = {0}; 2643 2644 hints.flags = MWM_HINTS_DECORATIONS; 2645 hints.decorations = enabled ? MWM_DECOR_ALL : 0; 2646 2647 XChangeProperty(_glfw.x11.display, window->x11.handle, 2648 _glfw.x11.MOTIF_WM_HINTS, 2649 _glfw.x11.MOTIF_WM_HINTS, 32, 2650 PropModeReplace, 2651 (unsigned char*) &hints, 2652 sizeof(hints) / sizeof(long)); 2653 } 2654 2655 void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) 2656 { 2657 if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_ABOVE) 2658 return; 2659 2660 if (_glfwPlatformWindowVisible(window)) 2661 { 2662 const long action = enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; 2663 sendEventToWM(window, 2664 _glfw.x11.NET_WM_STATE, 2665 action, 2666 _glfw.x11.NET_WM_STATE_ABOVE, 2667 0, 1, 0); 2668 } 2669 else 2670 { 2671 Atom* states = NULL; 2672 unsigned long i, count; 2673 2674 count = _glfwGetWindowPropertyX11(window->x11.handle, 2675 _glfw.x11.NET_WM_STATE, 2676 XA_ATOM, 2677 (unsigned char**) &states); 2678 2679 // NOTE: We don't check for failure as this property may not exist yet 2680 // and that's fine (and we'll create it implicitly with append) 2681 2682 if (enabled) 2683 { 2684 for (i = 0; i < count; i++) 2685 { 2686 if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE) 2687 break; 2688 } 2689 2690 if (i < count) 2691 return; 2692 2693 XChangeProperty(_glfw.x11.display, window->x11.handle, 2694 _glfw.x11.NET_WM_STATE, XA_ATOM, 32, 2695 PropModeAppend, 2696 (unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE, 2697 1); 2698 } 2699 else if (states) 2700 { 2701 for (i = 0; i < count; i++) 2702 { 2703 if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE) 2704 break; 2705 } 2706 2707 if (i == count) 2708 return; 2709 2710 states[i] = states[count - 1]; 2711 count--; 2712 2713 XChangeProperty(_glfw.x11.display, window->x11.handle, 2714 _glfw.x11.NET_WM_STATE, XA_ATOM, 32, 2715 PropModeReplace, (unsigned char*) states, count); 2716 } 2717 2718 if (states) 2719 XFree(states); 2720 } 2721 2722 XFlush(_glfw.x11.display); 2723 } 2724 2725 float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) 2726 { 2727 float opacity = 1.f; 2728 2729 if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx)) 2730 { 2731 CARD32* value = NULL; 2732 2733 if (_glfwGetWindowPropertyX11(window->x11.handle, 2734 _glfw.x11.NET_WM_WINDOW_OPACITY, 2735 XA_CARDINAL, 2736 (unsigned char**) &value)) 2737 { 2738 opacity = (float) (*value / (double) 0xffffffffu); 2739 } 2740 2741 if (value) 2742 XFree(value); 2743 } 2744 2745 return opacity; 2746 } 2747 2748 void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) 2749 { 2750 const CARD32 value = (CARD32) (0xffffffffu * (double) opacity); 2751 XChangeProperty(_glfw.x11.display, window->x11.handle, 2752 _glfw.x11.NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32, 2753 PropModeReplace, (unsigned char*) &value, 1); 2754 } 2755 2756 void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) 2757 { 2758 if (!_glfw.x11.xi.available) 2759 return; 2760 2761 if (_glfw.x11.disabledCursorWindow != window) 2762 return; 2763 2764 if (enabled) 2765 enableRawMouseMotion(window); 2766 else 2767 disableRawMouseMotion(window); 2768 } 2769 2770 GLFWbool _glfwPlatformRawMouseMotionSupported(void) 2771 { 2772 return _glfw.x11.xi.available; 2773 } 2774 2775 void _glfwPlatformPollEvents(void) 2776 { 2777 _GLFWwindow* window; 2778 2779 #if defined(__linux__) 2780 _glfwDetectJoystickConnectionLinux(); 2781 #endif 2782 XPending(_glfw.x11.display); 2783 2784 while (QLength(_glfw.x11.display)) 2785 { 2786 XEvent event; 2787 XNextEvent(_glfw.x11.display, &event); 2788 processEvent(&event); 2789 } 2790 2791 window = _glfw.x11.disabledCursorWindow; 2792 if (window) 2793 { 2794 int width, height; 2795 _glfwPlatformGetWindowSize(window, &width, &height); 2796 2797 // NOTE: Re-center the cursor only if it has moved since the last call, 2798 // to avoid breaking glfwWaitEvents with MotionNotify 2799 if (window->x11.lastCursorPosX != width / 2 || 2800 window->x11.lastCursorPosY != height / 2) 2801 { 2802 _glfwPlatformSetCursorPos(window, width / 2, height / 2); 2803 } 2804 } 2805 2806 XFlush(_glfw.x11.display); 2807 } 2808 2809 void _glfwPlatformWaitEvents(void) 2810 { 2811 while (!XPending(_glfw.x11.display)) 2812 waitForEvent(NULL); 2813 2814 _glfwPlatformPollEvents(); 2815 } 2816 2817 void _glfwPlatformWaitEventsTimeout(double timeout) 2818 { 2819 while (!XPending(_glfw.x11.display)) 2820 { 2821 if (!waitForEvent(&timeout)) 2822 break; 2823 } 2824 2825 _glfwPlatformPollEvents(); 2826 } 2827 2828 void _glfwPlatformPostEmptyEvent(void) 2829 { 2830 XEvent event = { ClientMessage }; 2831 event.xclient.window = _glfw.x11.helperWindowHandle; 2832 event.xclient.format = 32; // Data is 32-bit longs 2833 event.xclient.message_type = _glfw.x11.NULL_; 2834 2835 XSendEvent(_glfw.x11.display, _glfw.x11.helperWindowHandle, False, 0, &event); 2836 XFlush(_glfw.x11.display); 2837 } 2838 2839 void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) 2840 { 2841 Window root, child; 2842 int rootX, rootY, childX, childY; 2843 unsigned int mask; 2844 2845 XQueryPointer(_glfw.x11.display, window->x11.handle, 2846 &root, &child, 2847 &rootX, &rootY, &childX, &childY, 2848 &mask); 2849 2850 if (xpos) 2851 *xpos = childX; 2852 if (ypos) 2853 *ypos = childY; 2854 } 2855 2856 void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) 2857 { 2858 // Store the new position so it can be recognized later 2859 window->x11.warpCursorPosX = (int) x; 2860 window->x11.warpCursorPosY = (int) y; 2861 2862 XWarpPointer(_glfw.x11.display, None, window->x11.handle, 2863 0,0,0,0, (int) x, (int) y); 2864 XFlush(_glfw.x11.display); 2865 } 2866 2867 void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) 2868 { 2869 if (mode == GLFW_CURSOR_DISABLED) 2870 { 2871 if (_glfwPlatformWindowFocused(window)) 2872 disableCursor(window); 2873 } 2874 else if (_glfw.x11.disabledCursorWindow == window) 2875 enableCursor(window); 2876 else 2877 updateCursorImage(window); 2878 2879 XFlush(_glfw.x11.display); 2880 } 2881 2882 const char* _glfwPlatformGetScancodeName(int scancode) 2883 { 2884 if (!_glfw.x11.xkb.available) 2885 return NULL; 2886 2887 if (scancode < 0 || scancode > 0xff || 2888 _glfw.x11.keycodes[scancode] == GLFW_KEY_UNKNOWN) 2889 { 2890 _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode"); 2891 return NULL; 2892 } 2893 2894 const int key = _glfw.x11.keycodes[scancode]; 2895 const KeySym keysym = XkbKeycodeToKeysym(_glfw.x11.display, 2896 scancode, _glfw.x11.xkb.group, 0); 2897 if (keysym == NoSymbol) 2898 return NULL; 2899 2900 const long ch = _glfwKeySym2Unicode(keysym); 2901 if (ch == -1) 2902 return NULL; 2903 2904 const size_t count = encodeUTF8(_glfw.x11.keynames[key], (unsigned int) ch); 2905 if (count == 0) 2906 return NULL; 2907 2908 _glfw.x11.keynames[key][count] = '\0'; 2909 return _glfw.x11.keynames[key]; 2910 } 2911 2912 int _glfwPlatformGetKeyScancode(int key) 2913 { 2914 return _glfw.x11.scancodes[key]; 2915 } 2916 2917 int _glfwPlatformCreateCursor(_GLFWcursor* cursor, 2918 const GLFWimage* image, 2919 int xhot, int yhot) 2920 { 2921 cursor->x11.handle = _glfwCreateCursorX11(image, xhot, yhot); 2922 if (!cursor->x11.handle) 2923 return GLFW_FALSE; 2924 2925 return GLFW_TRUE; 2926 } 2927 2928 int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) 2929 { 2930 if (_glfw.x11.xcursor.handle) 2931 { 2932 char* theme = XcursorGetTheme(_glfw.x11.display); 2933 if (theme) 2934 { 2935 const int size = XcursorGetDefaultSize(_glfw.x11.display); 2936 const char* name = NULL; 2937 2938 if (shape == GLFW_ARROW_CURSOR) 2939 name = "default"; 2940 else if (shape == GLFW_IBEAM_CURSOR) 2941 name = "text"; 2942 else if (shape == GLFW_CROSSHAIR_CURSOR) 2943 name = "crosshair"; 2944 else if (shape == GLFW_POINTING_HAND_CURSOR) 2945 name = "pointer"; 2946 else if (shape == GLFW_RESIZE_EW_CURSOR) 2947 name = "ew-resize"; 2948 else if (shape == GLFW_RESIZE_NS_CURSOR) 2949 name = "ns-resize"; 2950 else if (shape == GLFW_RESIZE_NWSE_CURSOR) 2951 name = "nwse-resize"; 2952 else if (shape == GLFW_RESIZE_NESW_CURSOR) 2953 name = "nesw-resize"; 2954 else if (shape == GLFW_RESIZE_ALL_CURSOR) 2955 name = "all-scroll"; 2956 else if (shape == GLFW_NOT_ALLOWED_CURSOR) 2957 name = "not-allowed"; 2958 2959 XcursorImage* image = XcursorLibraryLoadImage(name, theme, size); 2960 if (image) 2961 { 2962 cursor->x11.handle = XcursorImageLoadCursor(_glfw.x11.display, image); 2963 XcursorImageDestroy(image); 2964 } 2965 } 2966 } 2967 2968 if (!cursor->x11.handle) 2969 { 2970 unsigned int native = 0; 2971 2972 if (shape == GLFW_ARROW_CURSOR) 2973 native = XC_left_ptr; 2974 else if (shape == GLFW_IBEAM_CURSOR) 2975 native = XC_xterm; 2976 else if (shape == GLFW_CROSSHAIR_CURSOR) 2977 native = XC_crosshair; 2978 else if (shape == GLFW_POINTING_HAND_CURSOR) 2979 native = XC_hand2; 2980 else if (shape == GLFW_RESIZE_EW_CURSOR) 2981 native = XC_sb_h_double_arrow; 2982 else if (shape == GLFW_RESIZE_NS_CURSOR) 2983 native = XC_sb_v_double_arrow; 2984 else if (shape == GLFW_RESIZE_ALL_CURSOR) 2985 native = XC_fleur; 2986 else 2987 { 2988 _glfwInputError(GLFW_CURSOR_UNAVAILABLE, 2989 "X11: Standard cursor shape unavailable"); 2990 return GLFW_FALSE; 2991 } 2992 2993 cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, native); 2994 if (!cursor->x11.handle) 2995 { 2996 _glfwInputError(GLFW_PLATFORM_ERROR, 2997 "X11: Failed to create standard cursor"); 2998 return GLFW_FALSE; 2999 } 3000 } 3001 3002 return GLFW_TRUE; 3003 } 3004 3005 void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) 3006 { 3007 if (cursor->x11.handle) 3008 XFreeCursor(_glfw.x11.display, cursor->x11.handle); 3009 } 3010 3011 void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) 3012 { 3013 if (window->cursorMode == GLFW_CURSOR_NORMAL) 3014 { 3015 updateCursorImage(window); 3016 XFlush(_glfw.x11.display); 3017 } 3018 } 3019 3020 void _glfwPlatformSetClipboardString(const char* string) 3021 { 3022 char* copy = _glfw_strdup(string); 3023 free(_glfw.x11.clipboardString); 3024 _glfw.x11.clipboardString = copy; 3025 3026 XSetSelectionOwner(_glfw.x11.display, 3027 _glfw.x11.CLIPBOARD, 3028 _glfw.x11.helperWindowHandle, 3029 CurrentTime); 3030 3031 if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) != 3032 _glfw.x11.helperWindowHandle) 3033 { 3034 _glfwInputError(GLFW_PLATFORM_ERROR, 3035 "X11: Failed to become owner of clipboard selection"); 3036 } 3037 } 3038 3039 const char* _glfwPlatformGetClipboardString(void) 3040 { 3041 return getSelectionString(_glfw.x11.CLIPBOARD); 3042 } 3043 3044 void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) 3045 { 3046 if (!_glfw.vk.KHR_surface) 3047 return; 3048 3049 if (!_glfw.vk.KHR_xcb_surface || !_glfw.x11.x11xcb.handle) 3050 { 3051 if (!_glfw.vk.KHR_xlib_surface) 3052 return; 3053 } 3054 3055 extensions[0] = "VK_KHR_surface"; 3056 3057 // NOTE: VK_KHR_xcb_surface is preferred due to some early ICDs exposing but 3058 // not correctly implementing VK_KHR_xlib_surface 3059 if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) 3060 extensions[1] = "VK_KHR_xcb_surface"; 3061 else 3062 extensions[1] = "VK_KHR_xlib_surface"; 3063 } 3064 3065 int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, 3066 VkPhysicalDevice device, 3067 uint32_t queuefamily) 3068 { 3069 VisualID visualID = XVisualIDFromVisual(DefaultVisual(_glfw.x11.display, 3070 _glfw.x11.screen)); 3071 3072 if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) 3073 { 3074 PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR 3075 vkGetPhysicalDeviceXcbPresentationSupportKHR = 3076 (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR) 3077 vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR"); 3078 if (!vkGetPhysicalDeviceXcbPresentationSupportKHR) 3079 { 3080 _glfwInputError(GLFW_API_UNAVAILABLE, 3081 "X11: Vulkan instance missing VK_KHR_xcb_surface extension"); 3082 return GLFW_FALSE; 3083 } 3084 3085 xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display); 3086 if (!connection) 3087 { 3088 _glfwInputError(GLFW_PLATFORM_ERROR, 3089 "X11: Failed to retrieve XCB connection"); 3090 return GLFW_FALSE; 3091 } 3092 3093 return vkGetPhysicalDeviceXcbPresentationSupportKHR(device, 3094 queuefamily, 3095 connection, 3096 visualID); 3097 } 3098 else 3099 { 3100 PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR 3101 vkGetPhysicalDeviceXlibPresentationSupportKHR = 3102 (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR) 3103 vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR"); 3104 if (!vkGetPhysicalDeviceXlibPresentationSupportKHR) 3105 { 3106 _glfwInputError(GLFW_API_UNAVAILABLE, 3107 "X11: Vulkan instance missing VK_KHR_xlib_surface extension"); 3108 return GLFW_FALSE; 3109 } 3110 3111 return vkGetPhysicalDeviceXlibPresentationSupportKHR(device, 3112 queuefamily, 3113 _glfw.x11.display, 3114 visualID); 3115 } 3116 } 3117 3118 VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, 3119 _GLFWwindow* window, 3120 const VkAllocationCallbacks* allocator, 3121 VkSurfaceKHR* surface) 3122 { 3123 if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) 3124 { 3125 VkResult err; 3126 VkXcbSurfaceCreateInfoKHR sci; 3127 PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR; 3128 3129 xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display); 3130 if (!connection) 3131 { 3132 _glfwInputError(GLFW_PLATFORM_ERROR, 3133 "X11: Failed to retrieve XCB connection"); 3134 return VK_ERROR_EXTENSION_NOT_PRESENT; 3135 } 3136 3137 vkCreateXcbSurfaceKHR = (PFN_vkCreateXcbSurfaceKHR) 3138 vkGetInstanceProcAddr(instance, "vkCreateXcbSurfaceKHR"); 3139 if (!vkCreateXcbSurfaceKHR) 3140 { 3141 _glfwInputError(GLFW_API_UNAVAILABLE, 3142 "X11: Vulkan instance missing VK_KHR_xcb_surface extension"); 3143 return VK_ERROR_EXTENSION_NOT_PRESENT; 3144 } 3145 3146 memset(&sci, 0, sizeof(sci)); 3147 sci.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; 3148 sci.connection = connection; 3149 sci.window = window->x11.handle; 3150 3151 err = vkCreateXcbSurfaceKHR(instance, &sci, allocator, surface); 3152 if (err) 3153 { 3154 _glfwInputError(GLFW_PLATFORM_ERROR, 3155 "X11: Failed to create Vulkan XCB surface: %s", 3156 _glfwGetVulkanResultString(err)); 3157 } 3158 3159 return err; 3160 } 3161 else 3162 { 3163 VkResult err; 3164 VkXlibSurfaceCreateInfoKHR sci; 3165 PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR; 3166 3167 vkCreateXlibSurfaceKHR = (PFN_vkCreateXlibSurfaceKHR) 3168 vkGetInstanceProcAddr(instance, "vkCreateXlibSurfaceKHR"); 3169 if (!vkCreateXlibSurfaceKHR) 3170 { 3171 _glfwInputError(GLFW_API_UNAVAILABLE, 3172 "X11: Vulkan instance missing VK_KHR_xlib_surface extension"); 3173 return VK_ERROR_EXTENSION_NOT_PRESENT; 3174 } 3175 3176 memset(&sci, 0, sizeof(sci)); 3177 sci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; 3178 sci.dpy = _glfw.x11.display; 3179 sci.window = window->x11.handle; 3180 3181 err = vkCreateXlibSurfaceKHR(instance, &sci, allocator, surface); 3182 if (err) 3183 { 3184 _glfwInputError(GLFW_PLATFORM_ERROR, 3185 "X11: Failed to create Vulkan X11 surface: %s", 3186 _glfwGetVulkanResultString(err)); 3187 } 3188 3189 return err; 3190 } 3191 } 3192 3193 3194 ////////////////////////////////////////////////////////////////////////// 3195 ////// GLFW native API ////// 3196 ////////////////////////////////////////////////////////////////////////// 3197 3198 GLFWAPI Display* glfwGetX11Display(void) 3199 { 3200 _GLFW_REQUIRE_INIT_OR_RETURN(NULL); 3201 return _glfw.x11.display; 3202 } 3203 3204 GLFWAPI Window glfwGetX11Window(GLFWwindow* handle) 3205 { 3206 _GLFWwindow* window = (_GLFWwindow*) handle; 3207 _GLFW_REQUIRE_INIT_OR_RETURN(None); 3208 return window->x11.handle; 3209 } 3210 3211 GLFWAPI void glfwSetX11SelectionString(const char* string) 3212 { 3213 _GLFW_REQUIRE_INIT(); 3214 3215 free(_glfw.x11.primarySelectionString); 3216 _glfw.x11.primarySelectionString = _glfw_strdup(string); 3217 3218 XSetSelectionOwner(_glfw.x11.display, 3219 _glfw.x11.PRIMARY, 3220 _glfw.x11.helperWindowHandle, 3221 CurrentTime); 3222 3223 if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.PRIMARY) != 3224 _glfw.x11.helperWindowHandle) 3225 { 3226 _glfwInputError(GLFW_PLATFORM_ERROR, 3227 "X11: Failed to become owner of primary selection"); 3228 } 3229 } 3230 3231 GLFWAPI const char* glfwGetX11SelectionString(void) 3232 { 3233 _GLFW_REQUIRE_INIT_OR_RETURN(NULL); 3234 return getSelectionString(_glfw.x11.PRIMARY); 3235 } 3236