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