Atlas - x11_window.c

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