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