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