Atlas - SDL_x11window.c
Home / ext / SDL / src / video / x11 Lines: 2 | Size: 88998 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "SDL_internal.h" 22 23#ifdef SDL_VIDEO_DRIVER_X11 24 25#include "../SDL_sysvideo.h" 26#include "../SDL_pixels_c.h" 27#include "../../events/SDL_keyboard_c.h" 28#include "../../events/SDL_mouse_c.h" 29#include "../../events/SDL_events_c.h" 30#include "../../core/unix/SDL_appid.h" 31 32#include "SDL_x11video.h" 33#include "SDL_x11mouse.h" 34#include "SDL_x11xinput2.h" 35#include "SDL_x11xfixes.h" 36 37#ifdef SDL_VIDEO_OPENGL_EGL 38#include "SDL_x11opengles.h" 39#endif 40 41#include "SDL_x11xsync.h" 42 43#define _NET_WM_STATE_REMOVE 0l 44#define _NET_WM_STATE_ADD 1l 45 46#define CHECK_WINDOW_DATA(window) \ 47 if (!window) { \ 48 return SDL_SetError("Invalid window"); \ 49 } \ 50 if (!window->internal) { \ 51 return SDL_SetError("Invalid window driver data"); \ 52 } 53 54#define CHECK_DISPLAY_DATA(display) \ 55 if (!_display) { \ 56 return SDL_SetError("Invalid display"); \ 57 } \ 58 if (!_display->internal) { \ 59 return SDL_SetError("Invalid display driver data"); \ 60 } 61 62static Bool isMapNotify(Display *dpy, XEvent *ev, XPointer win) // NOLINT(readability-non-const-parameter): cannot make XPointer a const pointer due to typedef 63{ 64 return ev->type == MapNotify && ev->xmap.window == *((Window *)win); 65} 66static Bool isUnmapNotify(Display *dpy, XEvent *ev, XPointer win) // NOLINT(readability-non-const-parameter): cannot make XPointer a const pointer due to typedef 67{ 68 return ev->type == UnmapNotify && ev->xunmap.window == *((Window *)win); 69} 70 71/* 72static Bool isConfigureNotify(Display *dpy, XEvent *ev, XPointer win) 73{ 74 return ev->type == ConfigureNotify && ev->xconfigure.window == *((Window *)win); 75} 76static Bool X11_XIfEventTimeout(Display *display, XEvent *event_return, Bool (*predicate)(), XPointer arg, int timeoutMS) 77{ 78 Uint64 start = SDL_GetTicks(); 79 80 while (!X11_XCheckIfEvent(display, event_return, predicate, arg)) { 81 if (SDL_GetTicks() >= (start + timeoutMS)) { 82 return False; 83 } 84 } 85 return True; 86} 87*/ 88 89static bool X11_CheckCurrentDesktop(const char *name) 90{ 91 SDL_Environment *env = SDL_GetEnvironment(); 92 const char *desktopVar = SDL_GetEnvironmentVariable(env, "DESKTOP_SESSION"); 93 if (desktopVar && SDL_strcasecmp(desktopVar, name) == 0) { 94 return true; 95 } 96 97 desktopVar = SDL_GetEnvironmentVariable(env, "XDG_CURRENT_DESKTOP"); 98 99 if (desktopVar && SDL_strcasestr(desktopVar, name)) { 100 return true; 101 } 102 103 return false; 104} 105 106static bool X11_IsWindowMapped(SDL_VideoDevice *_this, SDL_Window *window) 107{ 108 SDL_WindowData *data = window->internal; 109 SDL_VideoData *videodata = _this->internal; 110 XWindowAttributes attr; 111 112 X11_XGetWindowAttributes(videodata->display, data->xwindow, &attr); 113 if (attr.map_state != IsUnmapped) { 114 return true; 115 } else { 116 return false; 117 } 118} 119 120static bool X11_IsDisplayOk(Display *display) 121{ 122 if (display->flags & XlibDisplayIOError) { 123 return false; 124 } 125 return true; 126} 127 128#if 0 129static bool X11_IsActionAllowed(SDL_Window *window, Atom action) 130{ 131 SDL_WindowData *data = window->internal; 132 Atom _NET_WM_ALLOWED_ACTIONS = data->videodata->_NET_WM_ALLOWED_ACTIONS; 133 Atom type; 134 Display *display = data->videodata->display; 135 int form; 136 unsigned long remain; 137 unsigned long len, i; 138 Atom *list; 139 bool ret = false; 140 141 if (X11_XGetWindowProperty(display, data->xwindow, _NET_WM_ALLOWED_ACTIONS, 0, 1024, False, XA_ATOM, &type, &form, &len, &remain, (unsigned char **)&list) == Success) { 142 for (i=0; i<len; ++i) { 143 if (list[i] == action) { 144 ret = true; 145 break; 146 } 147 } 148 X11_XFree(list); 149 } 150 return ret; 151} 152#endif // 0 153 154static void X11_FlushPendingEvents(SDL_VideoDevice *_this, SDL_Window *window) 155{ 156 // Serialize and restore the pending flags, as they may be overwritten while flushing. 157 const bool last_position_pending = window->last_position_pending; 158 const bool last_size_pending = window->last_size_pending; 159 160 X11_SyncWindow(_this, window); 161 162 window->last_position_pending = last_position_pending; 163 window->last_size_pending = last_size_pending; 164} 165 166void X11_SetNetWMState(SDL_VideoDevice *_this, Window xwindow, SDL_WindowFlags flags) 167{ 168 SDL_VideoData *videodata = _this->internal; 169 Display *display = videodata->display; 170 // !!! FIXME: just dereference videodata below instead of copying to locals. 171 Atom _NET_WM_STATE = videodata->atoms._NET_WM_STATE; 172 // Atom _NET_WM_STATE_HIDDEN = videodata->atoms._NET_WM_STATE_HIDDEN; 173 Atom _NET_WM_STATE_FOCUSED = videodata->atoms._NET_WM_STATE_FOCUSED; 174 Atom _NET_WM_STATE_MAXIMIZED_VERT = videodata->atoms._NET_WM_STATE_MAXIMIZED_VERT; 175 Atom _NET_WM_STATE_MAXIMIZED_HORZ = videodata->atoms._NET_WM_STATE_MAXIMIZED_HORZ; 176 Atom _NET_WM_STATE_FULLSCREEN = videodata->atoms._NET_WM_STATE_FULLSCREEN; 177 Atom _NET_WM_STATE_ABOVE = videodata->atoms._NET_WM_STATE_ABOVE; 178 Atom _NET_WM_STATE_SKIP_TASKBAR = videodata->atoms._NET_WM_STATE_SKIP_TASKBAR; 179 Atom _NET_WM_STATE_SKIP_PAGER = videodata->atoms._NET_WM_STATE_SKIP_PAGER; 180 Atom _NET_WM_STATE_MODAL = videodata->atoms._NET_WM_STATE_MODAL; 181 Atom atoms[16]; 182 int count = 0; 183 184 /* The window manager sets this property, we shouldn't set it. 185 If we did, this would indicate to the window manager that we don't 186 actually want to be mapped during X11_XMapRaised(), which would be bad. 187 * 188 if ((flags & SDL_WINDOW_HIDDEN) != 0) { 189 atoms[count++] = _NET_WM_STATE_HIDDEN; 190 } 191 */ 192 193 if (flags & SDL_WINDOW_ALWAYS_ON_TOP) { 194 atoms[count++] = _NET_WM_STATE_ABOVE; 195 } 196 if (flags & SDL_WINDOW_UTILITY) { 197 atoms[count++] = _NET_WM_STATE_SKIP_TASKBAR; 198 atoms[count++] = _NET_WM_STATE_SKIP_PAGER; 199 } 200 if (flags & SDL_WINDOW_INPUT_FOCUS) { 201 atoms[count++] = _NET_WM_STATE_FOCUSED; 202 } 203 if (flags & SDL_WINDOW_MAXIMIZED) { 204 atoms[count++] = _NET_WM_STATE_MAXIMIZED_VERT; 205 atoms[count++] = _NET_WM_STATE_MAXIMIZED_HORZ; 206 } 207 if (flags & SDL_WINDOW_FULLSCREEN) { 208 atoms[count++] = _NET_WM_STATE_FULLSCREEN; 209 } 210 if (flags & SDL_WINDOW_MODAL) { 211 atoms[count++] = _NET_WM_STATE_MODAL; 212 } 213 214 SDL_assert(count <= SDL_arraysize(atoms)); 215 216 if (count > 0) { 217 X11_XChangeProperty(display, xwindow, _NET_WM_STATE, XA_ATOM, 32, 218 PropModeReplace, (unsigned char *)atoms, count); 219 } else { 220 X11_XDeleteProperty(display, xwindow, _NET_WM_STATE); 221 } 222} 223 224static void X11_ConstrainPopup(SDL_Window *window, bool output_to_pending) 225{ 226 // Clamp popup windows to the output borders 227 if (SDL_WINDOW_IS_POPUP(window)) { 228 SDL_Window *w; 229 SDL_DisplayID displayID; 230 SDL_Rect rect; 231 int abs_x = window->last_position_pending ? window->pending.x : window->floating.x; 232 int abs_y = window->last_position_pending ? window->pending.y : window->floating.y; 233 int offset_x = 0, offset_y = 0; 234 235 if (window->constrain_popup) { 236 // Calculate the total offset from the parents 237 for (w = window->parent; SDL_WINDOW_IS_POPUP(w); w = w->parent) { 238 offset_x += w->x; 239 offset_y += w->y; 240 } 241 242 offset_x += w->x; 243 offset_y += w->y; 244 abs_x += offset_x; 245 abs_y += offset_y; 246 247 displayID = SDL_GetDisplayForWindow(w); 248 249 SDL_GetDisplayBounds(displayID, &rect); 250 if (abs_x + window->w > rect.x + rect.w) { 251 abs_x -= (abs_x + window->w) - (rect.x + rect.w); 252 } 253 if (abs_y + window->h > rect.y + rect.h) { 254 abs_y -= (abs_y + window->h) - (rect.y + rect.h); 255 } 256 abs_x = SDL_max(abs_x, rect.x); 257 abs_y = SDL_max(abs_y, rect.y); 258 } 259 260 if (output_to_pending) { 261 window->pending.x = abs_x - offset_x; 262 window->pending.y = abs_y - offset_y; 263 } else { 264 window->floating.x = window->windowed.x = abs_x - offset_x; 265 window->floating.y = window->windowed.y = abs_y - offset_y; 266 } 267 } 268} 269 270static void X11_SetKeyboardFocus(SDL_Window *window, bool set_active_focus) 271{ 272 SDL_Window *toplevel = window; 273 274 // Find the toplevel parent 275 while (SDL_WINDOW_IS_POPUP(toplevel)) { 276 toplevel = toplevel->parent; 277 } 278 279 toplevel->keyboard_focus = window; 280 281 if (set_active_focus && !window->is_hiding && !window->is_destroying) { 282 SDL_SetKeyboardFocus(window); 283 } 284} 285 286SDL_WindowFlags X11_GetNetWMState(SDL_VideoDevice *_this, SDL_Window *window, Window xwindow) 287{ 288 SDL_VideoData *videodata = _this->internal; 289 Display *display = videodata->display; 290 Atom _NET_WM_STATE = videodata->atoms._NET_WM_STATE; 291 Atom _NET_WM_STATE_HIDDEN = videodata->atoms._NET_WM_STATE_HIDDEN; 292 Atom _NET_WM_STATE_FOCUSED = videodata->atoms._NET_WM_STATE_FOCUSED; 293 Atom _NET_WM_STATE_MAXIMIZED_VERT = videodata->atoms._NET_WM_STATE_MAXIMIZED_VERT; 294 Atom _NET_WM_STATE_MAXIMIZED_HORZ = videodata->atoms._NET_WM_STATE_MAXIMIZED_HORZ; 295 Atom _NET_WM_STATE_FULLSCREEN = videodata->atoms._NET_WM_STATE_FULLSCREEN; 296 Atom actualType; 297 int actualFormat; 298 unsigned long i, numItems, bytesAfter; 299 unsigned char *propertyValue = NULL; 300 long maxLength = 1024; 301 SDL_WindowFlags flags = 0; 302 303 if (X11_XGetWindowProperty(display, xwindow, _NET_WM_STATE, 304 0l, maxLength, False, XA_ATOM, &actualType, 305 &actualFormat, &numItems, &bytesAfter, 306 &propertyValue) == Success) { 307 Atom *atoms = (Atom *)propertyValue; 308 int maximized = 0; 309 int fullscreen = 0; 310 311 for (i = 0; i < numItems; ++i) { 312 if (atoms[i] == _NET_WM_STATE_HIDDEN) { 313 flags |= SDL_WINDOW_MINIMIZED | SDL_WINDOW_OCCLUDED; 314 } else if (atoms[i] == _NET_WM_STATE_FOCUSED) { 315 flags |= SDL_WINDOW_INPUT_FOCUS; 316 } else if (atoms[i] == _NET_WM_STATE_MAXIMIZED_VERT) { 317 maximized |= 1; 318 } else if (atoms[i] == _NET_WM_STATE_MAXIMIZED_HORZ) { 319 maximized |= 2; 320 } else if (atoms[i] == _NET_WM_STATE_FULLSCREEN) { 321 fullscreen = 1; 322 } 323 } 324 325 if (fullscreen == 1) { 326 if (window->flags & SDL_WINDOW_FULLSCREEN) { 327 // Pick whatever state the window expects 328 flags |= (window->flags & SDL_WINDOW_FULLSCREEN); 329 } else { 330 // Assume we're fullscreen desktop 331 flags |= SDL_WINDOW_FULLSCREEN; 332 } 333 } 334 335 if (maximized == 3) { 336 /* Fullscreen windows are maximized on some window managers, 337 and this is functional behavior - if maximized is removed, 338 the windows remain floating centered and not covering the 339 rest of the desktop. So we just won't change the maximize 340 state for fullscreen windows here, otherwise SDL would think 341 we're always maximized when fullscreen and not restore the 342 correct state when leaving fullscreen. 343 */ 344 if (fullscreen) { 345 flags |= (window->flags & SDL_WINDOW_MAXIMIZED); 346 } else { 347 flags |= SDL_WINDOW_MAXIMIZED; 348 } 349 } 350 351 /* If the window is unmapped, numItems will be zero and _NET_WM_STATE_HIDDEN 352 * will not be set. Do an additional check to see if the window is unmapped 353 * and mark it as SDL_WINDOW_HIDDEN if it is. 354 */ 355 { 356 XWindowAttributes attr; 357 SDL_memset(&attr, 0, sizeof(attr)); 358 X11_XGetWindowAttributes(videodata->display, xwindow, &attr); 359 if (attr.map_state == IsUnmapped) { 360 flags |= SDL_WINDOW_HIDDEN; 361 } 362 } 363 X11_XFree(propertyValue); 364 } 365 366 // FIXME, check the size hints for resizable 367 // flags |= SDL_WINDOW_RESIZABLE; 368 369 return flags; 370} 371 372static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, Window w) 373{ 374 SDL_VideoData *videodata = _this->internal; 375 SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window); 376 SDL_WindowData *data; 377 int numwindows = videodata->numwindows; 378 int windowlistlength = videodata->windowlistlength; 379 SDL_WindowData **windowlist = videodata->windowlist; 380 381 // Allocate the window data 382 data = (SDL_WindowData *)SDL_calloc(1, sizeof(*data)); 383 if (!data) { 384 return false; 385 } 386 data->videodata = videodata; 387 data->window = window; 388 data->xwindow = w; 389 data->hit_test_result = SDL_HITTEST_NORMAL; 390 391 X11_CreateInputContext(data); 392 393 // Associate the data with the window 394 395 if (numwindows < windowlistlength) { 396 windowlist[numwindows] = data; 397 videodata->numwindows++; 398 } else { 399 SDL_WindowData ** new_windowlist = (SDL_WindowData **)SDL_realloc(windowlist, (numwindows + 1) * sizeof(*windowlist)); 400 if (!new_windowlist) { 401 goto error_cleanup; 402 } 403 windowlist = new_windowlist; 404 windowlist[numwindows] = data; 405 videodata->numwindows++; 406 videodata->windowlistlength++; 407 videodata->windowlist = windowlist; 408 } 409 410 // Fill in the SDL window with the window data 411 { 412 XWindowAttributes attrib; 413 414 X11_XGetWindowAttributes(data->videodata->display, w, &attrib); 415 if (!SDL_WINDOW_IS_POPUP(window)) { 416 window->x = window->windowed.x = window->floating.x = attrib.x; 417 window->y = window->windowed.y = window->floating.y = attrib.y - data->border_top; 418 } 419 window->w = window->windowed.w = window->floating.w = attrib.width; 420 window->h = window->windowed.h = window->floating.h = attrib.height; 421 if (attrib.map_state != IsUnmapped) { 422 window->flags &= ~SDL_WINDOW_HIDDEN; 423 } else { 424 window->flags |= SDL_WINDOW_HIDDEN; 425 } 426 data->visual = attrib.visual; 427 data->colormap = attrib.colormap; 428 } 429 430 window->flags |= X11_GetNetWMState(_this, window, w); 431 432 { 433 Window FocalWindow; 434 int RevertTo = 0; 435 X11_XGetInputFocus(data->videodata->display, &FocalWindow, &RevertTo); 436 if (FocalWindow == w) { 437 window->flags |= SDL_WINDOW_INPUT_FOCUS; 438 } 439 440 if (window->flags & SDL_WINDOW_INPUT_FOCUS) { 441 SDL_SetKeyboardFocus(data->window); 442 } 443 444 if (window->flags & SDL_WINDOW_MOUSE_GRABBED) { 445 // Tell x11 to clip mouse 446 } 447 } 448 449 if (window->flags & SDL_WINDOW_EXTERNAL) { 450 // Query the title from the existing window 451 window->title = X11_GetWindowTitle(_this, w); 452 } 453 454 SDL_PropertiesID props = SDL_GetWindowProperties(window); 455 int screen = (displaydata ? displaydata->screen : 0); 456 SDL_SetPointerProperty(props, SDL_PROP_WINDOW_X11_DISPLAY_POINTER, data->videodata->display); 457 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_X11_SCREEN_NUMBER, screen); 458 SDL_SetNumberProperty(props, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, data->xwindow); 459 460 461#if defined(SDL_VIDEO_OPENGL_ES) || defined(SDL_VIDEO_OPENGL_ES2) || defined(SDL_VIDEO_OPENGL_EGL) 462 if ((window->flags & SDL_WINDOW_OPENGL) && 463 ((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) || 464 SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, false)) 465#ifdef SDL_VIDEO_OPENGL_GLX 466 && (!_this->gl_data || X11_GL_UseEGL(_this)) 467#endif 468 ) { 469#ifdef SDL_VIDEO_OPENGL_EGL 470 if (!_this->egl_data) { 471 goto error_cleanup; 472 } 473 474 // Create the GLES window surface 475 data->egl_surface = SDL_EGL_CreateSurface(_this, window, (NativeWindowType)w); 476 477 if (data->egl_surface == EGL_NO_SURFACE) { 478 SDL_SetError("Could not create GLES window surface"); 479 goto error_cleanup; 480 } 481#else 482 SDL_SetError("Could not create GLES window surface (EGL support not configured)"); 483 goto error_cleanup; 484#endif // SDL_VIDEO_OPENGL_EGL 485 } 486#endif 487 488 // All done! 489 window->internal = data; 490 return true; 491 492error_cleanup: 493 X11_DestroyInputContext(data); 494 SDL_free(data); 495 return false; 496} 497 498static void SetupWindowInput(SDL_VideoDevice *_this, SDL_Window *window) 499{ 500 long fevent = 0; 501 SDL_WindowData *data = window->internal; 502 Window xwindow = data->xwindow; 503 504#ifdef X_HAVE_UTF8_STRING 505 if (SDL_X11_HAVE_UTF8 && data->ic) { 506 X11_XGetICValues(data->ic, XNFilterEvents, &fevent, NULL); 507 } 508#endif 509 510 X11_Xinput2Select(_this, window); 511 512 { 513 unsigned int x11_keyboard_events = KeyPressMask | KeyReleaseMask; 514 unsigned int x11_pointer_events = ButtonPressMask | ButtonReleaseMask | PointerMotionMask; 515 516 X11_Xinput2SelectMouseAndKeyboard(_this, window); 517 518 // If XInput2 can handle pointer and keyboard events, we don't track them here 519 if (data->xinput2_keyboard_enabled) { 520 x11_keyboard_events = 0; 521 } 522 if (data->xinput2_mouse_enabled) { 523 x11_pointer_events = 0; 524 } 525 526 X11_XSelectInput(data->videodata->display, xwindow, 527 (FocusChangeMask | EnterWindowMask | LeaveWindowMask | ExposureMask | 528 x11_keyboard_events | x11_pointer_events | 529 PropertyChangeMask | StructureNotifyMask | 530 KeymapStateMask | fevent)); 531 } 532} 533 534static void SetWindowBordered(Display *display, int screen, Window window, bool border) 535{ 536 /* 537 * this code used to check for KWM_WIN_DECORATION, but KDE hasn't 538 * supported it for years and years. It now respects _MOTIF_WM_HINTS. 539 * Gnome is similar: just use the Motif atom. 540 */ 541 542 Atom WM_HINTS = X11_XInternAtom(display, "_MOTIF_WM_HINTS", True); 543 if (WM_HINTS != None) { 544 // Hints used by Motif compliant window managers 545 struct 546 { 547 unsigned long flags; 548 unsigned long functions; 549 unsigned long decorations; 550 long input_mode; 551 unsigned long status; 552 } MWMHints = { 553 (1L << 1), 0, border ? 1 : 0, 0, 0 554 }; 555 556 X11_XChangeProperty(display, window, WM_HINTS, WM_HINTS, 32, 557 PropModeReplace, (unsigned char *)&MWMHints, 558 sizeof(MWMHints) / sizeof(long)); 559 } else { // set the transient hints instead, if necessary 560 X11_XSetTransientForHint(display, window, RootWindow(display, screen)); 561 } 562} 563 564bool X11_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props) 565{ 566 Window w = (Window)SDL_GetNumberProperty(create_props, SDL_PROP_WINDOW_CREATE_X11_WINDOW_NUMBER, 567 (Window)SDL_GetPointerProperty(create_props, "sdl2-compat.external_window", NULL)); 568 if (w) { 569 window->flags |= SDL_WINDOW_EXTERNAL; 570 571 if (!SetupWindowData(_this, window, w)) { 572 return false; 573 } 574 575 if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_EXTERNAL_WINDOW_INPUT, true)) { 576 SetupWindowInput(_this, window); 577 } 578 return true; 579 } 580 581 SDL_VideoData *data = _this->internal; 582 SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window); 583 if (!displaydata) { 584 return SDL_SetError("Could not find display info"); 585 } 586 587 const bool force_override_redirect = SDL_GetHintBoolean(SDL_HINT_X11_FORCE_OVERRIDE_REDIRECT, false); 588 const bool use_resize_sync = !!(window->flags & SDL_WINDOW_OPENGL); // Doesn't work well with Vulkan 589 SDL_WindowData *windowdata; 590 Display *display = data->display; 591 int screen = displaydata->screen; 592 Visual *visual; 593 int depth; 594 XSetWindowAttributes xattr; 595 XSizeHints *sizehints; 596 XWMHints *wmhints; 597 XClassHint *classhints; 598 Atom _NET_WM_BYPASS_COMPOSITOR; 599 Atom _NET_WM_WINDOW_TYPE; 600 Atom wintype; 601 const char *wintype_name = NULL; 602 long compositor = 1; 603 Atom _NET_WM_PID; 604 const char *hint = NULL; 605 int win_x, win_y; 606 bool undefined_position = false; 607 608#if defined(SDL_VIDEO_OPENGL_GLX) || defined(SDL_VIDEO_OPENGL_EGL) 609 const int transparent = (window->flags & SDL_WINDOW_TRANSPARENT) ? true : false; 610 const char *forced_visual_id = SDL_GetHint(SDL_HINT_VIDEO_X11_WINDOW_VISUALID); 611 const char *display_visual_id = SDL_GetHint(SDL_HINT_VIDEO_X11_VISUALID); 612 613 if (forced_visual_id && *forced_visual_id) { 614 XVisualInfo *vi, template; 615 int nvis; 616 617 SDL_zero(template); 618 template.visualid = SDL_strtol(forced_visual_id, NULL, 0); 619 vi = X11_XGetVisualInfo(display, VisualIDMask, &template, &nvis); 620 if (vi) { 621 visual = vi->visual; 622 depth = vi->depth; 623 X11_XFree(vi); 624 } else { 625 return false; 626 } 627 } else if ((window->flags & SDL_WINDOW_OPENGL) && 628 (!display_visual_id || !*display_visual_id)) { 629 XVisualInfo *vinfo = NULL; 630 631#ifdef SDL_VIDEO_OPENGL_EGL 632 if (((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) || 633 SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, false)) 634#ifdef SDL_VIDEO_OPENGL_GLX 635 && (!_this->gl_data || X11_GL_UseEGL(_this)) 636#endif 637 ) { 638 vinfo = X11_GLES_GetVisual(_this, display, screen, transparent); 639 } else 640#endif 641 { 642#ifdef SDL_VIDEO_OPENGL_GLX 643 vinfo = X11_GL_GetVisual(_this, display, screen, transparent); 644#endif 645 } 646 647 if (!vinfo) { 648 return false; 649 } 650 visual = vinfo->visual; 651 depth = vinfo->depth; 652 X11_XFree(vinfo); 653 } else 654#endif 655 { 656 visual = displaydata->visual; 657 depth = displaydata->depth; 658 } 659 660 xattr.override_redirect = ((window->flags & SDL_WINDOW_TOOLTIP) || (window->flags & SDL_WINDOW_POPUP_MENU) || force_override_redirect) ? True : False; 661 xattr.backing_store = NotUseful; 662 xattr.background_pixmap = None; 663 xattr.border_pixel = 0; 664 665 if (visual->class == DirectColor) { 666 XColor *colorcells; 667 int i; 668 int ncolors; 669 int rmax, gmax, bmax; 670 int rmask, gmask, bmask; 671 int rshift, gshift, bshift; 672 673 xattr.colormap = 674 X11_XCreateColormap(display, RootWindow(display, screen), 675 visual, AllocAll); 676 677 // If we can't create a colormap, then we must die 678 if (!xattr.colormap) { 679 return SDL_SetError("Could not create writable colormap"); 680 } 681 682 // OK, we got a colormap, now fill it in as best as we can 683 colorcells = SDL_malloc(visual->map_entries * sizeof(XColor)); 684 if (!colorcells) { 685 return false; 686 } 687 ncolors = visual->map_entries; 688 rmax = 0xffff; 689 gmax = 0xffff; 690 bmax = 0xffff; 691 692 rshift = 0; 693 rmask = visual->red_mask; 694 while (0 == (rmask & 1)) { 695 rshift++; 696 rmask >>= 1; 697 } 698 699 gshift = 0; 700 gmask = visual->green_mask; 701 while (0 == (gmask & 1)) { 702 gshift++; 703 gmask >>= 1; 704 } 705 706 bshift = 0; 707 bmask = visual->blue_mask; 708 while (0 == (bmask & 1)) { 709 bshift++; 710 bmask >>= 1; 711 } 712 713 // build the color table pixel values 714 for (i = 0; i < ncolors; i++) { 715 Uint32 red = (rmax * i) / (ncolors - 1); 716 Uint32 green = (gmax * i) / (ncolors - 1); 717 Uint32 blue = (bmax * i) / (ncolors - 1); 718 719 Uint32 rbits = (rmask * i) / (ncolors - 1); 720 Uint32 gbits = (gmask * i) / (ncolors - 1); 721 Uint32 bbits = (bmask * i) / (ncolors - 1); 722 723 Uint32 pix = 724 (rbits << rshift) | (gbits << gshift) | (bbits << bshift); 725 726 colorcells[i].pixel = pix; 727 728 colorcells[i].red = red; 729 colorcells[i].green = green; 730 colorcells[i].blue = blue; 731 732 colorcells[i].flags = DoRed | DoGreen | DoBlue; 733 } 734 735 X11_XStoreColors(display, xattr.colormap, colorcells, ncolors); 736 737 SDL_free(colorcells); 738 } else { 739 xattr.colormap = 740 X11_XCreateColormap(display, RootWindow(display, screen), 741 visual, AllocNone); 742 } 743 744 if (window->undefined_x && window->undefined_y && 745 window->displayID == SDL_GetPrimaryDisplay()) { 746 undefined_position = true; 747 } 748 749 if (SDL_WINDOW_IS_POPUP(window)) { 750 X11_ConstrainPopup(window, false); 751 } 752 SDL_RelativeToGlobalForWindow(window, 753 window->floating.x, window->floating.y, 754 &win_x, &win_y); 755 756 /* Always create this with the window->floating.* fields; if we're creating a windowed mode window, 757 * that's fine. If we're creating a maximized or fullscreen window, the window manager will want to 758 * know these values so it can use them if we go _back_ to the base floating windowed mode. SDL manages 759 * migration to fullscreen after CreateSDLWindow returns, which will put all the SDL_Window fields and 760 * system state as expected. 761 */ 762 w = X11_XCreateWindow(display, RootWindow(display, screen), 763 win_x, win_y, window->floating.w, window->floating.h, 764 0, depth, InputOutput, visual, 765 (CWOverrideRedirect | CWBackPixmap | CWBorderPixel | 766 CWBackingStore | CWColormap), 767 &xattr); 768 if (!w) { 769 return SDL_SetError("Couldn't create window"); 770 } 771 772 /* Don't set the borderless flag if we're about to go fullscreen. 773 * This prevents the window manager from moving a full-screen borderless 774 * window to a different display before we actually go fullscreen. 775 */ 776 if (!(window->pending_flags & SDL_WINDOW_FULLSCREEN)) { 777 SetWindowBordered(display, screen, w, !(window->flags & SDL_WINDOW_BORDERLESS)); 778 } 779 780 sizehints = X11_XAllocSizeHints(); 781 // Setup the normal size hints 782 sizehints->flags = 0; 783 if (!(window->flags & SDL_WINDOW_RESIZABLE)) { 784 sizehints->min_width = sizehints->max_width = window->floating.w; 785 sizehints->min_height = sizehints->max_height = window->floating.h; 786 sizehints->flags |= (PMaxSize | PMinSize); 787 } 788 if (!undefined_position) { 789 sizehints->x = win_x; 790 sizehints->y = win_y; 791 sizehints->flags |= USPosition; 792 } 793 794 // Setup the input hints so we get keyboard input 795 wmhints = X11_XAllocWMHints(); 796 wmhints->input = !(window->flags & SDL_WINDOW_NOT_FOCUSABLE) ? True : False; 797 wmhints->window_group = data->window_group; 798 wmhints->flags = InputHint | WindowGroupHint; 799 800 // Setup the class hints so we can get an icon (AfterStep) 801 classhints = X11_XAllocClassHint(); 802 classhints->res_name = (char *)SDL_GetExeName(); 803 classhints->res_class = (char *)SDL_GetAppID(); 804 805 // Set the size, input and class hints, and define WM_CLIENT_MACHINE and WM_LOCALE_NAME 806 X11_XSetWMProperties(display, w, NULL, NULL, NULL, 0, sizehints, wmhints, classhints); 807 808 X11_XFree(sizehints); 809 X11_XFree(wmhints); 810 X11_XFree(classhints); 811 // Set the PID related to the window for the given hostname, if possible 812 if (data->pid > 0) { 813 long pid = (long)data->pid; 814 _NET_WM_PID = X11_XInternAtom(display, "_NET_WM_PID", False); 815 X11_XChangeProperty(display, w, _NET_WM_PID, XA_CARDINAL, 32, PropModeReplace, 816 (unsigned char *)&pid, 1); 817 } 818 819 // Set the window manager state 820 X11_SetNetWMState(_this, w, window->flags); 821 822 compositor = 2; // don't disable compositing except for "normal" windows 823 hint = SDL_GetHint(SDL_HINT_X11_WINDOW_TYPE); 824 if (window->flags & SDL_WINDOW_UTILITY) { 825 wintype_name = "_NET_WM_WINDOW_TYPE_UTILITY"; 826 } else if (window->flags & SDL_WINDOW_TOOLTIP) { 827 wintype_name = "_NET_WM_WINDOW_TYPE_TOOLTIP"; 828 } else if (window->flags & SDL_WINDOW_POPUP_MENU) { 829 wintype_name = "_NET_WM_WINDOW_TYPE_POPUP_MENU"; 830 } else if (hint && *hint) { 831 wintype_name = hint; 832 } else { 833 wintype_name = "_NET_WM_WINDOW_TYPE_NORMAL"; 834 compositor = 1; // disable compositing for "normal" windows 835 } 836 837 // Let the window manager know what type of window we are. 838 _NET_WM_WINDOW_TYPE = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE", False); 839 wintype = X11_XInternAtom(display, wintype_name, False); 840 X11_XChangeProperty(display, w, _NET_WM_WINDOW_TYPE, XA_ATOM, 32, 841 PropModeReplace, (unsigned char *)&wintype, 1); 842 if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, true)) { 843 _NET_WM_BYPASS_COMPOSITOR = X11_XInternAtom(display, "_NET_WM_BYPASS_COMPOSITOR", False); 844 X11_XChangeProperty(display, w, _NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32, 845 PropModeReplace, 846 (unsigned char *)&compositor, 1); 847 } 848 849 { 850 Atom protocols[4]; 851 int proto_count = 0; 852 853 protocols[proto_count++] = data->atoms.WM_DELETE_WINDOW; // Allow window to be deleted by the WM 854 protocols[proto_count++] = data->atoms.WM_TAKE_FOCUS; // Since we will want to set input focus explicitly 855 856 // Default to using ping if there is no hint 857 if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_NET_WM_PING, true)) { 858 protocols[proto_count++] = data->atoms._NET_WM_PING; // Respond so WM knows we're alive 859 } 860 861#ifdef SDL_VIDEO_DRIVER_X11_XSYNC 862 if (use_resize_sync) { 863 protocols[proto_count++] = data->atoms._NET_WM_SYNC_REQUEST; /* Respond after completing resize */ 864 } 865#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ 866 867 SDL_assert(proto_count <= sizeof(protocols) / sizeof(protocols[0])); 868 869 X11_XSetWMProtocols(display, w, protocols, proto_count); 870 } 871 872 if (!SetupWindowData(_this, window, w)) { 873 X11_XDestroyWindow(display, w); 874 return false; 875 } 876 windowdata = window->internal; 877 878 // Set the parent if this is a non-popup window. 879 if (!SDL_WINDOW_IS_POPUP(window) && window->parent) { 880 X11_XSetTransientForHint(display, w, window->parent->internal->xwindow); 881 } 882 883 // Set the flag if the borders were forced on when creating a fullscreen window for later removal. 884 windowdata->fullscreen_borders_forced_on = !!(window->pending_flags & SDL_WINDOW_FULLSCREEN) && 885 !!(window->flags & SDL_WINDOW_BORDERLESS); 886 887#ifdef SDL_VIDEO_DRIVER_X11_XSYNC 888 if (use_resize_sync) { 889 X11_InitResizeSync(window); 890 } 891#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ 892 893#ifdef SDL_VIDEO_DRIVER_X11_XSHAPE 894 // Tooltips do not receive input 895 if (window->flags & SDL_WINDOW_TOOLTIP) { 896 Region region = X11_XCreateRegion(); 897 X11_XShapeCombineRegion(display, w, ShapeInput, 0, 0, region, ShapeSet); 898 X11_XDestroyRegion(region); 899 } 900#endif 901 902 SetupWindowInput(_this, window); 903 904 // For _ICC_PROFILE. 905 X11_XSelectInput(display, RootWindow(display, screen), PropertyChangeMask); 906 907 X11_XFlush(display); 908 909 return true; 910} 911 912char *X11_GetWindowTitle(SDL_VideoDevice *_this, Window xwindow) 913{ 914 SDL_VideoData *data = _this->internal; 915 Display *display = data->display; 916 int status, real_format; 917 Atom real_type; 918 unsigned long items_read, items_left; 919 unsigned char *propdata; 920 char *title = NULL; 921 922 status = X11_XGetWindowProperty(display, xwindow, data->atoms._NET_WM_NAME, 923 0L, 8192L, False, data->atoms.UTF8_STRING, &real_type, &real_format, 924 &items_read, &items_left, &propdata); 925 if (status == Success && propdata) { 926 title = SDL_strdup(SDL_static_cast(char *, propdata)); 927 X11_XFree(propdata); 928 } else { 929 status = X11_XGetWindowProperty(display, xwindow, XA_WM_NAME, 930 0L, 8192L, False, XA_STRING, &real_type, &real_format, 931 &items_read, &items_left, &propdata); 932 if (status == Success && propdata) { 933 title = SDL_iconv_string("UTF-8", "", SDL_static_cast(char *, propdata), items_read + 1); 934 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Failed to convert WM_NAME title expecting UTF8! Title: %s", title); 935 X11_XFree(propdata); 936 } else { 937 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Could not get any window title response from Xorg, returning empty string!"); 938 title = SDL_strdup(""); 939 } 940 } 941 return title; 942} 943 944void X11_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window) 945{ 946 SDL_WindowData *data = window->internal; 947 Window xwindow = data->xwindow; 948 Display *display = data->videodata->display; 949 char *title = window->title ? window->title : ""; 950 951 SDL_X11_SetWindowTitle(display, xwindow, title); 952} 953 954static bool caught_x11_error = false; 955static int X11_CatchAnyError(Display *d, XErrorEvent *e) 956{ 957 /* this may happen during tumultuous times when we are polling anyhow, 958 so just note we had an error and return control. */ 959 caught_x11_error = true; 960 return 0; 961} 962 963static void X11_ExternalResizeMoveSync(SDL_Window *window) 964{ 965 SDL_WindowData *data = window->internal; 966 Display *display = data->videodata->display; 967 int (*prev_handler)(Display *, XErrorEvent *); 968 unsigned int childCount; 969 Window childReturn, root, parent; 970 Window *children; 971 XWindowAttributes attrs; 972 Uint64 timeout = 0; 973 int x, y; 974 const bool send_move = !!(data->pending_operation & X11_PENDING_OP_MOVE); 975 const bool send_resize = !!(data->pending_operation & X11_PENDING_OP_RESIZE); 976 977 X11_XSync(display, False); 978 X11_XQueryTree(display, data->xwindow, &root, &parent, &children, &childCount); 979 prev_handler = X11_XSetErrorHandler(X11_CatchAnyError); 980 981 /* Wait a brief time to see if the window manager decided to let the move or resize happen. 982 * If the window changes at all, even to an unexpected value, we break out. 983 */ 984 timeout = SDL_GetTicksNS() + SDL_MS_TO_NS(100); 985 while (true) { 986 caught_x11_error = false; 987 X11_XSync(display, False); 988 X11_XGetWindowAttributes(display, data->xwindow, &attrs); 989 X11_XTranslateCoordinates(display, parent, DefaultRootWindow(display), 990 attrs.x, attrs.y, &x, &y, &childReturn); 991 SDL_GlobalToRelativeForWindow(window, x, y, &x, &y); 992 993 if (!caught_x11_error) { 994 if ((data->pending_operation & X11_PENDING_OP_MOVE) && (x == data->expected.x + data->border_left && y == data->expected.y + data->border_top)) { 995 data->pending_operation &= ~X11_PENDING_OP_MOVE; 996 } 997 if ((data->pending_operation & X11_PENDING_OP_RESIZE) && (attrs.width == data->expected.w && attrs.height == data->expected.h)) { 998 data->pending_operation &= ~X11_PENDING_OP_RESIZE; 999 } 1000 1001 if (data->pending_operation == X11_PENDING_OP_NONE) { 1002 break; 1003 } 1004 } 1005 1006 if (SDL_GetTicksNS() >= timeout) { 1007 // Timed out without the expected values. Update the requested data so future sync calls won't block. 1008 data->pending_operation &= ~(X11_PENDING_OP_MOVE | X11_PENDING_OP_RESIZE); 1009 data->expected.x = x; 1010 data->expected.y = y; 1011 data->expected.w = attrs.width; 1012 data->expected.h = attrs.height; 1013 break; 1014 } 1015 1016 SDL_Delay(10); 1017 } 1018 1019 if (!caught_x11_error) { 1020 if (send_move) { 1021 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, x, y); 1022 } 1023 if (send_resize) { 1024 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, attrs.width, attrs.height); 1025 } 1026 } 1027 1028 X11_XSetErrorHandler(prev_handler); 1029 caught_x11_error = false; 1030} 1031 1032/* Wait a brief time, or not, to see if the window manager decided to move/resize the window. 1033 * Send MOVED and RESIZED window events */ 1034static bool X11_SyncWindowTimeout(SDL_VideoDevice *_this, SDL_Window *window, Uint64 param_timeout) 1035{ 1036 SDL_WindowData *data = window->internal; 1037 Display *display = data->videodata->display; 1038 int (*prev_handler)(Display *, XErrorEvent *); 1039 Uint64 timeout = 0; 1040 bool force_exit = false; 1041 bool result = true; 1042 1043 X11_XSync(display, False); 1044 prev_handler = X11_XSetErrorHandler(X11_CatchAnyError); 1045 1046 if (param_timeout) { 1047 timeout = SDL_GetTicksNS() + param_timeout; 1048 } 1049 1050 while (true) { 1051 X11_XSync(display, False); 1052 X11_PumpEvents(_this); 1053 1054 if ((data->pending_operation & X11_PENDING_OP_MOVE) && (window->x == data->expected.x + data->border_left && window->y == data->expected.y + data->border_top)) { 1055 data->pending_operation &= ~X11_PENDING_OP_MOVE; 1056 } 1057 if ((data->pending_operation & X11_PENDING_OP_RESIZE) && (window->w == data->expected.w && window->h == data->expected.h)) { 1058 data->pending_operation &= ~X11_PENDING_OP_RESIZE; 1059 } 1060 1061 if (data->pending_operation == X11_PENDING_OP_NONE) { 1062 if (force_exit || 1063 (window->x == data->expected.x + data->border_left && window->y == data->expected.y + data->border_top && 1064 window->w == data->expected.w && window->h == data->expected.h)) { 1065 // The window is in the expected state and nothing is pending. Done. 1066 break; 1067 } 1068 1069 /* No operations are pending, but the window still isn't in the expected state. 1070 * Try one more time before exiting. 1071 */ 1072 force_exit = true; 1073 } 1074 1075 if (SDL_GetTicksNS() >= timeout) { 1076 // Timed out without the expected values. Update the requested data so future sync calls won't block. 1077 data->expected.x = window->x; 1078 data->expected.y = window->y; 1079 data->expected.w = window->w; 1080 data->expected.h = window->h; 1081 1082 result = false; 1083 break; 1084 } 1085 1086 SDL_Delay(10); 1087 } 1088 1089 data->pending_operation = X11_PENDING_OP_NONE; 1090 1091 if (!caught_x11_error) { 1092 X11_PumpEvents(_this); 1093 } else { 1094 result = false; 1095 } 1096 1097 X11_XSetErrorHandler(prev_handler); 1098 caught_x11_error = false; 1099 1100 return result; 1101} 1102 1103bool X11_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon) 1104{ 1105 SDL_WindowData *data = window->internal; 1106 Display *display = data->videodata->display; 1107 Atom _NET_WM_ICON = data->videodata->atoms._NET_WM_ICON; 1108 int (*prevHandler)(Display *, XErrorEvent *) = NULL; 1109 bool result = true; 1110 1111 if (icon) { 1112 int x, y; 1113 int propsize; 1114 long *propdata; 1115 Uint32 *src; 1116 long *dst; 1117 1118 // Set the _NET_WM_ICON property 1119 SDL_assert(icon->format == SDL_PIXELFORMAT_ARGB8888); 1120 propsize = 2 + (icon->w * icon->h); 1121 propdata = SDL_malloc(propsize * sizeof(long)); 1122 1123 if (!propdata) { 1124 return false; 1125 } 1126 1127 X11_XSync(display, False); 1128 prevHandler = X11_XSetErrorHandler(X11_CatchAnyError); 1129 1130 propdata[0] = icon->w; 1131 propdata[1] = icon->h; 1132 dst = &propdata[2]; 1133 1134 for (y = 0; y < icon->h; ++y) { 1135 src = (Uint32 *)((Uint8 *)icon->pixels + y * icon->pitch); 1136 for (x = 0; x < icon->w; ++x) { 1137 *dst++ = *src++; 1138 } 1139 } 1140 1141 X11_XChangeProperty(display, data->xwindow, _NET_WM_ICON, XA_CARDINAL, 1142 32, PropModeReplace, (unsigned char *)propdata, 1143 propsize); 1144 SDL_free(propdata); 1145 1146 if (caught_x11_error) { 1147 result = SDL_SetError("An error occurred while trying to set the window's icon"); 1148 } 1149 } 1150 1151 X11_XFlush(display); 1152 1153 if (prevHandler) { 1154 X11_XSetErrorHandler(prevHandler); 1155 caught_x11_error = false; 1156 } 1157 1158 return result; 1159} 1160 1161void X11_UpdateWindowPosition(SDL_Window *window, bool use_current_position) 1162{ 1163 SDL_WindowData *data = window->internal; 1164 Display *display = data->videodata->display; 1165 const int rel_x = use_current_position ? window->x : window->pending.x; 1166 const int rel_y = use_current_position ? window->y : window->pending.y; 1167 1168 SDL_RelativeToGlobalForWindow(window, 1169 rel_x - data->border_left, rel_y - data->border_top, 1170 &data->expected.x, &data->expected.y); 1171 1172 // Attempt to move the window 1173 if (window->flags & SDL_WINDOW_HIDDEN) { 1174 window->internal->pending_position = true; 1175 } else { 1176 data->pending_operation |= X11_PENDING_OP_MOVE; 1177 X11_XMoveWindow(display, data->xwindow, data->expected.x, data->expected.y); 1178 } 1179} 1180 1181bool X11_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window) 1182{ 1183 // Sync any pending fullscreen or maximize events. 1184 if (window->internal->pending_operation & (X11_PENDING_OP_FULLSCREEN | X11_PENDING_OP_MAXIMIZE)) { 1185 X11_FlushPendingEvents(_this, window); 1186 } 1187 1188 // Set the position as pending if the window is maximized with a restore pending. 1189 if (window->flags & SDL_WINDOW_MAXIMIZED) { 1190 if (window->internal->pending_operation & X11_PENDING_OP_RESTORE) { 1191 window->internal->pending_position = true; 1192 } 1193 return true; 1194 } 1195 1196 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { 1197 if (SDL_WINDOW_IS_POPUP(window)) { 1198 X11_ConstrainPopup(window, true); 1199 } 1200 X11_UpdateWindowPosition(window, false); 1201 } else { 1202 SDL_UpdateFullscreenMode(window, SDL_FULLSCREEN_OP_UPDATE, true); 1203 } 1204 return true; 1205} 1206 1207void X11_SetWindowMinMax(SDL_Window *window, bool use_current) 1208{ 1209 SDL_WindowData *data = window->internal; 1210 Display *display = data->videodata->display; 1211 XSizeHints *sizehints = X11_XAllocSizeHints(); 1212 long hint_flags = 0; 1213 1214 X11_XGetWMNormalHints(display, data->xwindow, sizehints, &hint_flags); 1215 sizehints->flags &= ~(PMinSize | PMaxSize | PAspect); 1216 1217 if (data->window->flags & SDL_WINDOW_RESIZABLE) { 1218 if (data->window->min_w || data->window->min_h) { 1219 sizehints->flags |= PMinSize; 1220 sizehints->min_width = data->window->min_w; 1221 sizehints->min_height = data->window->min_h; 1222 } 1223 if (data->window->max_w || data->window->max_h) { 1224 sizehints->flags |= PMaxSize; 1225 sizehints->max_width = data->window->max_w; 1226 sizehints->max_height = data->window->max_h; 1227 } 1228 if (data->window->min_aspect > 0.0f || data->window->max_aspect > 0.0f) { 1229 sizehints->flags |= PAspect; 1230 SDL_CalculateFraction(data->window->min_aspect, &sizehints->min_aspect.x, &sizehints->min_aspect.y); 1231 SDL_CalculateFraction(data->window->max_aspect, &sizehints->max_aspect.x, &sizehints->max_aspect.y); 1232 } 1233 } else { 1234 // Set the min/max to the same values to make the window non-resizable 1235 sizehints->flags |= PMinSize | PMaxSize; 1236 if (use_current) { 1237 sizehints->min_width = sizehints->max_width = window->last_size_pending ? window->pending.w : data->window->floating.w; 1238 sizehints->min_height = sizehints->max_height = window->last_size_pending ? window->pending.h : data->window->floating.h; 1239 } else { 1240 sizehints->min_width = sizehints->max_width = window->last_size_pending ? window->pending.w : data->window->windowed.w; 1241 sizehints->min_height = sizehints->max_height = window->last_size_pending ? window->pending.h : data->window->windowed.h; 1242 } 1243 } 1244 1245 X11_XSetWMNormalHints(display, data->xwindow, sizehints); 1246 X11_XFree(sizehints); 1247} 1248 1249void X11_SetWindowMinimumSize(SDL_VideoDevice *_this, SDL_Window *window) 1250{ 1251 if (window->internal->pending_operation & X11_PENDING_OP_FULLSCREEN) { 1252 X11_SyncWindow(_this, window); 1253 } 1254 1255 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { 1256 X11_SetWindowMinMax(window, true); 1257 } 1258} 1259 1260void X11_SetWindowMaximumSize(SDL_VideoDevice *_this, SDL_Window *window) 1261{ 1262 if (window->internal->pending_operation & X11_PENDING_OP_FULLSCREEN) { 1263 X11_SyncWindow(_this, window); 1264 } 1265 1266 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { 1267 X11_SetWindowMinMax(window, true); 1268 } 1269} 1270 1271void X11_SetWindowAspectRatio(SDL_VideoDevice *_this, SDL_Window *window) 1272{ 1273 if (window->internal->pending_operation & X11_PENDING_OP_FULLSCREEN) { 1274 X11_SyncWindow(_this, window); 1275 } 1276 1277 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { 1278 X11_SetWindowMinMax(window, true); 1279 } 1280} 1281 1282void X11_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) 1283{ 1284 SDL_WindowData *data = window->internal; 1285 Display *display = data->videodata->display; 1286 1287 /* Wait for pending maximize and fullscreen operations to complete, as these windows 1288 * don't get size changes. 1289 */ 1290 if (data->pending_operation & (X11_PENDING_OP_MAXIMIZE | X11_PENDING_OP_FULLSCREEN)) { 1291 X11_FlushPendingEvents(_this, window); 1292 } 1293 1294 // Set the size as pending if the window is being restored. 1295 if (window->flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_FULLSCREEN)) { 1296 // New size will be set when the window is restored. 1297 if (data->pending_operation & X11_PENDING_OP_RESTORE) { 1298 data->pending_size = true; 1299 } else { 1300 // Can't resize the window. 1301 window->last_size_pending = false; 1302 } 1303 return; 1304 } 1305 1306 if (!(window->flags & SDL_WINDOW_RESIZABLE)) { 1307 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { 1308 /* Apparently, if the X11 Window is set to a 'non-resizable' window, you cannot resize it using the X11_XResizeWindow, thus 1309 * we must set the size hints to adjust the window size. 1310 */ 1311 XSizeHints *sizehints = X11_XAllocSizeHints(); 1312 long userhints; 1313 int dest_x, dest_y; 1314 1315 X11_XGetWMNormalHints(display, data->xwindow, sizehints, &userhints); 1316 1317 data->expected.w = sizehints->min_width = sizehints->max_width = window->pending.w; 1318 data->expected.h = sizehints->min_height = sizehints->max_height = window->pending.h; 1319 sizehints->flags |= PMinSize | PMaxSize; 1320 data->pending_operation |= X11_PENDING_OP_RESIZE; 1321 1322 X11_XSetWMNormalHints(display, data->xwindow, sizehints); 1323 1324 /* From Pierre-Loup: 1325 WMs each have their little quirks with that. When you change the 1326 size hints, they get a ConfigureNotify event with the 1327 WM_NORMAL_SIZE_HINTS Atom. They all save the hints then, but they 1328 don't all resize the window right away to enforce the new hints. 1329 1330 Some of them resize only after: 1331 - A user-initiated move or resize 1332 - A code-initiated move or resize 1333 - Hiding & showing window (Unmap & map) 1334 1335 The following move & resize seems to help a lot of WMs that didn't 1336 properly update after the hints were changed. We don't do a 1337 hide/show, because there are supposedly subtle problems with doing so 1338 and transitioning from windowed to fullscreen in Unity. 1339 */ 1340 X11_XResizeWindow(display, data->xwindow, window->pending.w, window->pending.h); 1341 const int x = window->last_position_pending ? window->pending.x : window->x; 1342 const int y = window->last_position_pending ? window->pending.y : window->y; 1343 SDL_RelativeToGlobalForWindow(window, 1344 x - data->border_left, 1345 y - data->border_top, 1346 &dest_x, &dest_y); 1347 X11_XMoveWindow(display, data->xwindow, dest_x, dest_y); 1348 X11_XRaiseWindow(display, data->xwindow); 1349 1350 X11_XFree(sizehints); 1351 } 1352 } else { 1353 data->expected.w = window->pending.w; 1354 data->expected.h = window->pending.h; 1355 data->pending_operation |= X11_PENDING_OP_RESIZE; 1356 X11_XResizeWindow(display, data->xwindow, data->expected.w, data->expected.h); 1357 } 1358 1359 /* External windows may call this to update the renderer size, but not pump SDL events, 1360 * so the size event needs to be synthesized for external windows. If it is wrong, the 1361 * true size will be sent if/when events are processed. 1362 */ 1363 if (window->flags & SDL_WINDOW_EXTERNAL) { 1364 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, window->pending.w, window->pending.h); 1365 } 1366} 1367 1368bool X11_GetWindowBordersSize(SDL_VideoDevice *_this, SDL_Window *window, int *top, int *left, int *bottom, int *right) 1369{ 1370 SDL_WindowData *data = window->internal; 1371 1372 *left = data->border_left; 1373 *right = data->border_right; 1374 *top = data->border_top; 1375 *bottom = data->border_bottom; 1376 1377 return true; 1378} 1379 1380bool X11_SetWindowOpacity(SDL_VideoDevice *_this, SDL_Window *window, float opacity) 1381{ 1382 SDL_WindowData *data = window->internal; 1383 Display *display = data->videodata->display; 1384 Atom _NET_WM_WINDOW_OPACITY = data->videodata->atoms._NET_WM_WINDOW_OPACITY; 1385 1386 if (opacity == 1.0f) { 1387 X11_XDeleteProperty(display, data->xwindow, _NET_WM_WINDOW_OPACITY); 1388 } else { 1389 const Uint32 FullyOpaque = 0xFFFFFFFF; 1390 const long alpha = (long)((double)opacity * (double)FullyOpaque); 1391 X11_XChangeProperty(display, data->xwindow, _NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32, 1392 PropModeReplace, (unsigned char *)&alpha, 1); 1393 } 1394 1395 return true; 1396} 1397 1398bool X11_SetWindowParent(SDL_VideoDevice *_this, SDL_Window *window, SDL_Window *parent) 1399{ 1400 SDL_WindowData *data = window->internal; 1401 SDL_WindowData *parent_data = parent ? parent->internal : NULL; 1402 SDL_VideoData *video_data = _this->internal; 1403 Display *display = video_data->display; 1404 1405 if (parent_data) { 1406 X11_XSetTransientForHint(display, data->xwindow, parent_data->xwindow); 1407 } else { 1408 X11_XDeleteProperty(display, data->xwindow, video_data->atoms.WM_TRANSIENT_FOR); 1409 } 1410 1411 return true; 1412} 1413 1414bool X11_SetWindowModal(SDL_VideoDevice *_this, SDL_Window *window, bool modal) 1415{ 1416 SDL_WindowData *data = window->internal; 1417 SDL_VideoData *video_data = _this->internal; 1418 SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window); 1419 Display *display = video_data->display; 1420 Uint32 flags = window->flags; 1421 Atom _NET_WM_STATE = data->videodata->atoms._NET_WM_STATE; 1422 Atom _NET_WM_STATE_MODAL = data->videodata->atoms._NET_WM_STATE_MODAL; 1423 1424 if (modal) { 1425 flags |= SDL_WINDOW_MODAL; 1426 } else { 1427 flags &= ~SDL_WINDOW_MODAL; 1428 X11_XDeleteProperty(display, data->xwindow, video_data->atoms.WM_TRANSIENT_FOR); 1429 } 1430 1431 if (X11_IsWindowMapped(_this, window)) { 1432 XEvent e; 1433 1434 SDL_zero(e); 1435 e.xany.type = ClientMessage; 1436 e.xclient.message_type = _NET_WM_STATE; 1437 e.xclient.format = 32; 1438 e.xclient.window = data->xwindow; 1439 e.xclient.data.l[0] = modal ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; 1440 e.xclient.data.l[1] = _NET_WM_STATE_MODAL; 1441 e.xclient.data.l[3] = 0l; 1442 1443 X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0, 1444 SubstructureNotifyMask | SubstructureRedirectMask, &e); 1445 } else { 1446 X11_SetNetWMState(_this, data->xwindow, flags); 1447 } 1448 1449 X11_XFlush(display); 1450 1451 return true; 1452} 1453 1454void X11_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, bool bordered) 1455{ 1456 const bool focused = (window->flags & SDL_WINDOW_INPUT_FOCUS) != 0; 1457 const bool visible = !(window->flags & SDL_WINDOW_HIDDEN) && !window->is_hiding; 1458 SDL_WindowData *data = window->internal; 1459 SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window); 1460 Display *display = data->videodata->display; 1461 XEvent event; 1462 1463 if (data->pending_operation & X11_PENDING_OP_FULLSCREEN) { 1464 X11_SyncWindow(_this, window); 1465 } 1466 1467 // If the window is fullscreen, the resize capability will be set/cleared when it is returned to windowed mode. 1468 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { 1469 SetWindowBordered(display, displaydata->screen, data->xwindow, bordered); 1470 X11_XFlush(display); 1471 1472 if (visible) { 1473 XWindowAttributes attr; 1474 do { 1475 X11_XSync(display, False); 1476 X11_XGetWindowAttributes(display, data->xwindow, &attr); 1477 } while (attr.map_state != IsViewable); 1478 1479 if (focused) { 1480 X11_XSetInputFocus(display, data->xwindow, RevertToParent, CurrentTime); 1481 } 1482 } 1483 1484 // make sure these don't make it to the real event queue if they fired here. 1485 X11_XSync(display, False); 1486 X11_XCheckIfEvent(display, &event, &isUnmapNotify, (XPointer)&data->xwindow); 1487 X11_XCheckIfEvent(display, &event, &isMapNotify, (XPointer)&data->xwindow); 1488 1489 // Turning the borders off doesn't send an extent event, so they must be cleared here. 1490 X11_GetBorderValues(data); 1491 1492 // Make sure the window manager didn't resize our window for the difference. 1493 X11_XResizeWindow(display, data->xwindow, window->floating.w, window->floating.h); 1494 X11_XSync(display, False); 1495 } else { 1496 // If fullscreen, set a flag to toggle the borders when returning to windowed mode. 1497 data->toggle_borders = true; 1498 data->fullscreen_borders_forced_on = false; 1499 } 1500} 1501 1502void X11_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, bool resizable) 1503{ 1504 SDL_WindowData *data = window->internal; 1505 1506 if (data->pending_operation & X11_PENDING_OP_FULLSCREEN) { 1507 X11_SyncWindow(_this, window); 1508 } 1509 1510 // If the window is fullscreen, the resize capability will be set/cleared when it is returned to windowed mode. 1511 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { 1512 X11_SetWindowMinMax(window, true); 1513 } 1514} 1515 1516void X11_SetWindowAlwaysOnTop(SDL_VideoDevice *_this, SDL_Window *window, bool on_top) 1517{ 1518 SDL_WindowData *data = window->internal; 1519 SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window); 1520 Display *display = data->videodata->display; 1521 Atom _NET_WM_STATE = data->videodata->atoms._NET_WM_STATE; 1522 Atom _NET_WM_STATE_ABOVE = data->videodata->atoms._NET_WM_STATE_ABOVE; 1523 1524 if (X11_IsWindowMapped(_this, window)) { 1525 XEvent e; 1526 1527 SDL_zero(e); 1528 e.xany.type = ClientMessage; 1529 e.xclient.message_type = _NET_WM_STATE; 1530 e.xclient.format = 32; 1531 e.xclient.window = data->xwindow; 1532 e.xclient.data.l[0] = 1533 on_top ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; 1534 e.xclient.data.l[1] = _NET_WM_STATE_ABOVE; 1535 e.xclient.data.l[3] = 0l; 1536 1537 X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0, 1538 SubstructureNotifyMask | SubstructureRedirectMask, &e); 1539 } else { 1540 X11_SetNetWMState(_this, data->xwindow, window->flags); 1541 } 1542 X11_XFlush(display); 1543} 1544 1545void X11_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) 1546{ 1547 SDL_WindowData *data = window->internal; 1548 Display *display = data->videodata->display; 1549 bool bActivate = SDL_GetHintBoolean(SDL_HINT_WINDOW_ACTIVATE_WHEN_SHOWN, true); 1550 bool set_position = false; 1551 XEvent event; 1552 1553 // If the window was previously shown, pump events to avoid possible positioning issues. 1554 if (data->was_shown) { 1555 X11_PumpEvents(_this); 1556 } 1557 1558 if (SDL_WINDOW_IS_POPUP(window)) { 1559 // Update the position in case the parent moved while we were hidden 1560 X11_ConstrainPopup(window, true); 1561 data->pending_position = true; 1562 set_position = true; 1563 } 1564 1565 /* Whether XMapRaised focuses the window is based on the window type and it is 1566 * wm specific. There isn't much we can do here */ 1567 (void)bActivate; 1568 1569 if (!X11_IsWindowMapped(_this, window)) { 1570 X11_XMapRaised(display, data->xwindow); 1571 /* Blocking wait for "MapNotify" event. 1572 * We use X11_XIfEvent because pXWindowEvent takes a mask rather than a type, 1573 * and XCheckTypedWindowEvent doesn't block */ 1574 if (!(window->flags & SDL_WINDOW_EXTERNAL) && X11_IsDisplayOk(display)) { 1575 X11_XIfEvent(display, &event, &isMapNotify, (XPointer)&data->xwindow); 1576 } 1577 X11_XFlush(display); 1578 set_position = data->pending_position || 1579 (!(window->flags & SDL_WINDOW_BORDERLESS) && !window->undefined_x && !window->undefined_y); 1580 } 1581 1582 if (!data->videodata->net_wm) { 1583 // no WM means no FocusIn event, which confuses us. Force it. 1584 X11_XSync(display, False); 1585 X11_XSetInputFocus(display, data->xwindow, RevertToNone, CurrentTime); 1586 X11_XFlush(display); 1587 } 1588 1589 // Grabbing popup menus get keyboard focus. 1590 if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) { 1591 X11_SetKeyboardFocus(window, true); 1592 } 1593 1594 // Get some valid border values, if we haven't received them yet 1595 if (data->border_left == 0 && data->border_right == 0 && data->border_top == 0 && data->border_bottom == 0) { 1596 X11_GetBorderValues(data); 1597 } 1598 1599 if (set_position) { 1600 // Apply the window position, accounting for offsets due to the borders appearing, but only when initially mapping. 1601 const int tx = (data->pending_position ? window->pending.x : window->x) - (data->was_shown ? 0 : data->border_left); 1602 const int ty = (data->pending_position ? window->pending.y : window->y) - (data->was_shown ? 0 : data->border_top); 1603 int x, y; 1604 1605 SDL_RelativeToGlobalForWindow(window, tx, ty, &x, &y); 1606 1607 data->pending_position = false; 1608 X11_XMoveWindow(display, data->xwindow, x, y); 1609 } 1610 1611 /* XMonad ignores size hints and shrinks the client area to overlay borders on fixed-size windows, 1612 * even if no borders were requested, resulting in the window client area being smaller than 1613 * requested. Calling XResizeWindow after mapping seems to fix it, even though resizing fixed-size 1614 * windows in this manner doesn't work on any other window manager. 1615 */ 1616 if (!(window->flags & SDL_WINDOW_RESIZABLE) && X11_CheckCurrentDesktop("xmonad")) { 1617 X11_XResizeWindow(display, data->xwindow, window->w, window->h); 1618 } 1619 1620 /* Some window managers can send garbage coordinates while mapping the window, so don't emit size and position 1621 * events during the initial configure events. 1622 */ 1623 data->size_move_event_flags = X11_SIZE_MOVE_EVENTS_DISABLE; 1624 X11_XSync(display, False); 1625 X11_PumpEvents(_this); 1626 data->size_move_event_flags = 0; 1627 1628 /* A MapNotify or PropertyNotify may not have arrived, so ensure that the shown event is dispatched 1629 * to apply pending state before clearing the flag. 1630 */ 1631 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_SHOWN, 0, 0); 1632 data->was_shown = true; 1633 1634 // If a configure event was received (type is non-zero), send the final window size and coordinates. 1635 if (data->last_xconfigure.type) { 1636 int x, y; 1637 SDL_GlobalToRelativeForWindow(data->window, data->last_xconfigure.x, data->last_xconfigure.y, &x, &y); 1638 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, data->last_xconfigure.width, data->last_xconfigure.height); 1639 SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, x, y); 1640 } 1641} 1642 1643void X11_HideWindow(SDL_VideoDevice *_this, SDL_Window *window) 1644{ 1645 SDL_WindowData *data = window->internal; 1646 SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window); 1647 int screen = (displaydata ? displaydata->screen : 0); 1648 Display *display = data->videodata->display; 1649 XEvent event; 1650 1651 if (X11_IsWindowMapped(_this, window)) { 1652 X11_XWithdrawWindow(display, data->xwindow, screen); 1653 // Blocking wait for "UnmapNotify" event 1654 if (!(window->flags & SDL_WINDOW_EXTERNAL) && X11_IsDisplayOk(display)) { 1655 X11_XIfEvent(display, &event, &isUnmapNotify, (XPointer)&data->xwindow); 1656 } 1657 X11_XFlush(display); 1658 } 1659 1660 // Transfer keyboard focus back to the parent 1661 if ((window->flags & SDL_WINDOW_POPUP_MENU) && !(window->flags & SDL_WINDOW_NOT_FOCUSABLE)) { 1662 SDL_Window *new_focus; 1663 const bool set_focus = SDL_ShouldRelinquishPopupFocus(window, &new_focus); 1664 X11_SetKeyboardFocus(new_focus, set_focus); 1665 } 1666 1667 X11_XSync(display, False); 1668 X11_PumpEvents(_this); 1669} 1670 1671static bool X11_SetWindowActive(SDL_VideoDevice *_this, SDL_Window *window) 1672{ 1673 CHECK_WINDOW_DATA(window); 1674 1675 SDL_WindowData *data = window->internal; 1676 SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window); 1677 Display *display = data->videodata->display; 1678 Atom _NET_ACTIVE_WINDOW = data->videodata->atoms._NET_ACTIVE_WINDOW; 1679 1680 if (X11_IsWindowMapped(_this, window)) { 1681 XEvent e; 1682 1683 // printf("SDL Window %p: sending _NET_ACTIVE_WINDOW with timestamp %lu\n", window, data->user_time); 1684 1685 SDL_zero(e); 1686 e.xany.type = ClientMessage; 1687 e.xclient.message_type = _NET_ACTIVE_WINDOW; 1688 e.xclient.format = 32; 1689 e.xclient.window = data->xwindow; 1690 e.xclient.data.l[0] = 1; // source indication. 1 = application 1691 e.xclient.data.l[1] = data->user_time; 1692 e.xclient.data.l[2] = 0; 1693 1694 X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0, 1695 SubstructureNotifyMask | SubstructureRedirectMask, &e); 1696 1697 X11_XFlush(display); 1698 } 1699 return true; 1700} 1701 1702void X11_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window) 1703{ 1704 SDL_WindowData *data = window->internal; 1705 Display *display = data->videodata->display; 1706 bool bActivate = SDL_GetHintBoolean(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED, true); 1707 1708 X11_XRaiseWindow(display, data->xwindow); 1709 if (bActivate) { 1710 X11_SetWindowActive(_this, window); 1711 } 1712 X11_XFlush(display); 1713} 1714 1715static bool X11_SetWindowMaximized(SDL_VideoDevice *_this, SDL_Window *window, bool maximized) 1716{ 1717 CHECK_WINDOW_DATA(window); 1718 1719 SDL_WindowData *data = window->internal; 1720 SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window); 1721 Display *display = data->videodata->display; 1722 Atom _NET_WM_STATE = data->videodata->atoms._NET_WM_STATE; 1723 Atom _NET_WM_STATE_MAXIMIZED_VERT = data->videodata->atoms._NET_WM_STATE_MAXIMIZED_VERT; 1724 Atom _NET_WM_STATE_MAXIMIZED_HORZ = data->videodata->atoms._NET_WM_STATE_MAXIMIZED_HORZ; 1725 1726 if (window->flags & SDL_WINDOW_FULLSCREEN) { 1727 /* Fullscreen windows are maximized on some window managers, 1728 and this is functional behavior, so don't remove that state 1729 now, we'll take care of it when we leave fullscreen mode. 1730 */ 1731 return true; 1732 } 1733 1734 if (X11_IsWindowMapped(_this, window)) { 1735 XEvent e; 1736 1737 SDL_zero(e); 1738 e.xany.type = ClientMessage; 1739 e.xclient.message_type = _NET_WM_STATE; 1740 e.xclient.format = 32; 1741 e.xclient.window = data->xwindow; 1742 e.xclient.data.l[0] = 1743 maximized ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; 1744 e.xclient.data.l[1] = _NET_WM_STATE_MAXIMIZED_VERT; 1745 e.xclient.data.l[2] = _NET_WM_STATE_MAXIMIZED_HORZ; 1746 e.xclient.data.l[3] = 0l; 1747 1748 if (maximized) { 1749 SDL_DisplayID displayID = SDL_GetDisplayForWindow(window); 1750 SDL_Rect bounds; 1751 1752 SDL_zero(bounds); 1753 SDL_GetDisplayUsableBounds(displayID, &bounds); 1754 1755 data->expected.x = bounds.x + data->border_left; 1756 data->expected.y = bounds.y + data->border_top; 1757 data->expected.w = bounds.w - (data->border_left + data->border_right); 1758 data->expected.h = bounds.h - (data->border_top + data->border_bottom); 1759 } else { 1760 data->expected.x = window->floating.x; 1761 data->expected.y = window->floating.y; 1762 data->expected.w = window->floating.w; 1763 data->expected.h = window->floating.h; 1764 } 1765 1766 X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0, 1767 SubstructureNotifyMask | SubstructureRedirectMask, &e); 1768 } else { 1769 X11_SetNetWMState(_this, data->xwindow, window->flags); 1770 } 1771 X11_XFlush(display); 1772 1773 return true; 1774} 1775 1776void X11_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window) 1777{ 1778 if (window->internal->pending_operation & (X11_PENDING_OP_FULLSCREEN | X11_PENDING_OP_MINIMIZE)) { 1779 SDL_SyncWindow(window); 1780 } 1781 1782 if (window->flags & SDL_WINDOW_FULLSCREEN) { 1783 // If fullscreen, just toggle the restored state. 1784 window->internal->window_was_maximized = true; 1785 return; 1786 } 1787 1788 if (!(window->flags & SDL_WINDOW_MINIMIZED)) { 1789 window->internal->pending_operation |= X11_PENDING_OP_MAXIMIZE; 1790 X11_SetWindowMaximized(_this, window, true); 1791 } 1792} 1793 1794void X11_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window) 1795{ 1796 SDL_WindowData *data = window->internal; 1797 SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window); 1798 Display *display = data->videodata->display; 1799 1800 if (data->pending_operation & SDL_WINDOW_FULLSCREEN) { 1801 SDL_SyncWindow(window); 1802 } 1803 1804 data->pending_operation |= X11_PENDING_OP_MINIMIZE; 1805 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { 1806 data->window_was_maximized = !!(window->flags & SDL_WINDOW_MAXIMIZED); 1807 } 1808 X11_XIconifyWindow(display, data->xwindow, displaydata->screen); 1809 X11_XFlush(display); 1810} 1811 1812void X11_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window) 1813{ 1814 // Don't restore the window the first time it is being shown. 1815 if (!window->internal->was_shown) { 1816 return; 1817 } 1818 1819 if (window->internal->pending_operation & (X11_PENDING_OP_FULLSCREEN | X11_PENDING_OP_MAXIMIZE | X11_PENDING_OP_MINIMIZE)) { 1820 SDL_SyncWindow(window); 1821 } 1822 1823 if ((window->flags & SDL_WINDOW_FULLSCREEN) && !(window->flags & SDL_WINDOW_MINIMIZED)) { 1824 // If fullscreen and not minimized, just toggle the restored state. 1825 window->internal->window_was_maximized = false; 1826 return; 1827 } 1828 1829 if (window->flags & (SDL_WINDOW_MINIMIZED | SDL_WINDOW_MAXIMIZED) || 1830 (window->internal->pending_operation & X11_PENDING_OP_MINIMIZE)) { 1831 window->internal->pending_operation |= X11_PENDING_OP_RESTORE; 1832 } 1833 1834 // If the window was minimized while maximized, restore as maximized. 1835 const bool maximize = !!(window->flags & SDL_WINDOW_MINIMIZED) && window->internal->window_was_maximized; 1836 X11_SetWindowMaximized(_this, window, maximize); 1837 X11_ShowWindow(_this, window); 1838 X11_SetWindowActive(_this, window); 1839} 1840 1841// This asks the Window Manager to handle fullscreen for us. This is the modern way. 1842static SDL_FullscreenResult X11_SetWindowFullscreenViaWM(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *_display, SDL_FullscreenOp fullscreen) 1843{ 1844 CHECK_WINDOW_DATA(window); 1845 CHECK_DISPLAY_DATA(_display); 1846 1847 SDL_WindowData *data = window->internal; 1848 SDL_DisplayData *displaydata = _display->internal; 1849 Display *display = data->videodata->display; 1850 Atom _NET_WM_STATE = data->videodata->atoms._NET_WM_STATE; 1851 Atom _NET_WM_STATE_FULLSCREEN = data->videodata->atoms._NET_WM_STATE_FULLSCREEN; 1852 1853 if (!data->was_shown && fullscreen == SDL_FULLSCREEN_OP_LEAVE) { 1854 return SDL_FULLSCREEN_SUCCEEDED; 1855 } 1856 1857 if (X11_IsWindowMapped(_this, window)) { 1858 XEvent e; 1859 1860 // Flush any pending fullscreen events. 1861 if (data->pending_operation & (X11_PENDING_OP_FULLSCREEN | X11_PENDING_OP_MAXIMIZE | X11_PENDING_OP_MOVE)) { 1862 X11_SyncWindow(_this, window); 1863 } 1864 1865 if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { 1866 if (fullscreen == SDL_FULLSCREEN_OP_UPDATE) { 1867 // Request was out of date; set -1 to signal the video core to undo a mode switch. 1868 return SDL_FULLSCREEN_FAILED; 1869 } else if (fullscreen == SDL_FULLSCREEN_OP_LEAVE) { 1870 // Nothing to do. 1871 return SDL_FULLSCREEN_SUCCEEDED; 1872 } 1873 } 1874 1875 if (fullscreen && !(window->flags & SDL_WINDOW_RESIZABLE)) { 1876 /* Compiz refuses fullscreen toggle if we're not resizable, so update the hints so we 1877 can be resized to the fullscreen resolution (or reset so we're not resizable again) */ 1878 XSizeHints *sizehints = X11_XAllocSizeHints(); 1879 long flags = 0; 1880 X11_XGetWMNormalHints(display, data->xwindow, sizehints, &flags); 1881 // we are going fullscreen so turn the flags off 1882 sizehints->flags &= ~(PMinSize | PMaxSize | PAspect); 1883 X11_XSetWMNormalHints(display, data->xwindow, sizehints); 1884 X11_XFree(sizehints); 1885 } 1886 1887 SDL_zero(e); 1888 e.xany.type = ClientMessage; 1889 e.xclient.message_type = _NET_WM_STATE; 1890 e.xclient.format = 32; 1891 e.xclient.window = data->xwindow; 1892 e.xclient.data.l[0] = 1893 fullscreen ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; 1894 e.xclient.data.l[1] = _NET_WM_STATE_FULLSCREEN; 1895 e.xclient.data.l[3] = 0l; 1896 1897 X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0, 1898 SubstructureNotifyMask | SubstructureRedirectMask, &e); 1899 1900 if (!!(window->flags & SDL_WINDOW_FULLSCREEN) != fullscreen) { 1901 data->pending_operation |= X11_PENDING_OP_FULLSCREEN; 1902 } 1903 1904 // Set the position so the window will be on the target display 1905 if (fullscreen) { 1906 SDL_DisplayID current = SDL_GetDisplayForWindowPosition(window); 1907 SDL_copyp(&data->requested_fullscreen_mode, &window->current_fullscreen_mode); 1908 if (fullscreen != !!(window->flags & SDL_WINDOW_FULLSCREEN)) { 1909 data->window_was_maximized = !!(window->flags & SDL_WINDOW_MAXIMIZED); 1910 } 1911 data->expected.x = displaydata->x; 1912 data->expected.y = displaydata->y; 1913 data->expected.w = _display->current_mode->w; 1914 data->expected.h = _display->current_mode->h; 1915 1916 // Only move the window if it isn't fullscreen or already on the target display. 1917 if (!(window->flags & SDL_WINDOW_FULLSCREEN) || (!current || current != _display->id)) { 1918 X11_XMoveWindow(display, data->xwindow, displaydata->x, displaydata->y); 1919 data->pending_operation |= X11_PENDING_OP_MOVE; 1920 } 1921 } else { 1922 SDL_zero(data->requested_fullscreen_mode); 1923 1924 /* Fullscreen windows sometimes end up being marked maximized by 1925 * window managers. Force it back to how we expect it to be. 1926 */ 1927 SDL_zero(e); 1928 e.xany.type = ClientMessage; 1929 e.xclient.message_type = _NET_WM_STATE; 1930 e.xclient.format = 32; 1931 e.xclient.window = data->xwindow; 1932 if (data->window_was_maximized) { 1933 e.xclient.data.l[0] = _NET_WM_STATE_ADD; 1934 } else { 1935 e.xclient.data.l[0] = _NET_WM_STATE_REMOVE; 1936 } 1937 e.xclient.data.l[1] = data->videodata->atoms._NET_WM_STATE_MAXIMIZED_VERT; 1938 e.xclient.data.l[2] = data->videodata->atoms._NET_WM_STATE_MAXIMIZED_HORZ; 1939 e.xclient.data.l[3] = 0l; 1940 X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0, 1941 SubstructureNotifyMask | SubstructureRedirectMask, &e); 1942 } 1943 } else { 1944 SDL_WindowFlags flags; 1945 1946 flags = window->flags; 1947 if (fullscreen) { 1948 flags |= SDL_WINDOW_FULLSCREEN; 1949 } else { 1950 flags &= ~SDL_WINDOW_FULLSCREEN; 1951 } 1952 X11_SetNetWMState(_this, data->xwindow, flags); 1953 } 1954 1955 if (data->visual->class == DirectColor) { 1956 if (fullscreen) { 1957 X11_XInstallColormap(display, data->colormap); 1958 } else { 1959 X11_XUninstallColormap(display, data->colormap); 1960 } 1961 } 1962 1963 return SDL_FULLSCREEN_PENDING; 1964} 1965 1966SDL_FullscreenResult X11_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *_display, SDL_FullscreenOp fullscreen) 1967{ 1968 return X11_SetWindowFullscreenViaWM(_this, window, _display, fullscreen); 1969} 1970 1971typedef struct 1972{ 1973 unsigned char *data; 1974 int format, count; 1975 Atom type; 1976} SDL_x11Prop; 1977 1978/* Reads property 1979 Must call X11_XFree on results 1980 */ 1981static void X11_ReadProperty(SDL_x11Prop *p, Display *disp, Window w, Atom prop) 1982{ 1983 unsigned char *ret = NULL; 1984 Atom type; 1985 int fmt; 1986 unsigned long count; 1987 unsigned long bytes_left; 1988 int bytes_fetch = 0; 1989 1990 do { 1991 if (ret) { 1992 X11_XFree(ret); 1993 } 1994 X11_XGetWindowProperty(disp, w, prop, 0, bytes_fetch, False, AnyPropertyType, &type, &fmt, &count, &bytes_left, &ret); 1995 bytes_fetch += bytes_left; 1996 } while (bytes_left != 0); 1997 1998 p->data = ret; 1999 p->format = fmt; 2000 p->count = count; 2001 p->type = type; 2002} 2003 2004void *X11_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size) 2005{ 2006 SDL_WindowData *data = window->internal; 2007 Display *display = data->videodata->display; 2008 XWindowAttributes attributes; 2009 Atom icc_profile_atom; 2010 char icc_atom_string[sizeof("_ICC_PROFILE_") + 12]; 2011 void *ret_icc_profile_data = NULL; 2012 CARD8 *icc_profile_data; 2013 int real_format; 2014 unsigned long real_nitems; 2015 SDL_x11Prop atomProp; 2016 2017 X11_XGetWindowAttributes(display, data->xwindow, &attributes); 2018 if (X11_XScreenNumberOfScreen(attributes.screen) > 0) { 2019 (void)SDL_snprintf(icc_atom_string, sizeof("_ICC_PROFILE_") + 12, "%s%d", "_ICC_PROFILE_", X11_XScreenNumberOfScreen(attributes.screen)); 2020 } else { 2021 SDL_strlcpy(icc_atom_string, "_ICC_PROFILE", sizeof("_ICC_PROFILE")); 2022 } 2023 X11_XGetWindowAttributes(display, RootWindowOfScreen(attributes.screen), &attributes); 2024 2025 icc_profile_atom = X11_XInternAtom(display, icc_atom_string, True); 2026 if (icc_profile_atom == None) { 2027 SDL_SetError("Screen is not calibrated."); 2028 return NULL; 2029 } 2030 2031 X11_ReadProperty(&atomProp, display, RootWindowOfScreen(attributes.screen), icc_profile_atom); 2032 real_format = atomProp.format; 2033 real_nitems = atomProp.count; 2034 icc_profile_data = atomProp.data; 2035 if (real_format == None) { 2036 SDL_SetError("Screen is not calibrated."); 2037 return NULL; 2038 } 2039 2040 ret_icc_profile_data = SDL_malloc(real_nitems); 2041 if (!ret_icc_profile_data) { 2042 return NULL; 2043 } 2044 2045 SDL_memcpy(ret_icc_profile_data, icc_profile_data, real_nitems); 2046 *size = real_nitems; 2047 X11_XFree(icc_profile_data); 2048 2049 return ret_icc_profile_data; 2050} 2051 2052bool X11_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed) 2053{ 2054 SDL_WindowData *data = window->internal; 2055 Display *display; 2056 2057 if (!data) { 2058 return SDL_SetError("Invalid window data"); 2059 } 2060 data->mouse_grabbed = false; 2061 data->pending_grab = false; 2062 2063 display = data->videodata->display; 2064 2065 if (grabbed) { 2066 /* If the window is unmapped, XGrab calls return GrabNotViewable, 2067 so when we get a MapNotify later, we'll try to update the grab as 2068 appropriate. */ 2069 if (window->flags & SDL_WINDOW_HIDDEN) { 2070 return true; 2071 } 2072 2073 /* If XInput2 is enabled, it will grab the pointer on button presses, 2074 * which results in XGrabPointer returning AlreadyGrabbed. If buttons 2075 * are currently pressed, clear any existing grabs before attempting 2076 * the confinement grab. 2077 */ 2078 if (data->xinput2_mouse_enabled && SDL_GetMouseState(NULL, NULL)) { 2079 data->pending_grab = true; 2080 return true; 2081 } 2082 2083 // Try to grab the mouse 2084 if (!data->videodata->broken_pointer_grab) { 2085 const unsigned int mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask; 2086 int attempts; 2087 int result = 0; 2088 2089 // Try for up to 5000ms (5s) to grab. If it still fails, stop trying. 2090 for (attempts = 0; attempts < 100; attempts++) { 2091 result = X11_XGrabPointer(display, data->xwindow, False, mask, GrabModeAsync, 2092 GrabModeAsync, data->xwindow, None, CurrentTime); 2093 if (result == GrabSuccess) { 2094 data->mouse_grabbed = true; 2095 break; 2096 } 2097 SDL_Delay(50); 2098 } 2099 2100 if (result != GrabSuccess) { 2101 data->videodata->broken_pointer_grab = true; // don't try again. 2102 } 2103 } 2104 2105 X11_Xinput2GrabTouch(_this, window); 2106 2107 // Raise the window if we grab the mouse 2108 X11_XRaiseWindow(display, data->xwindow); 2109 } else { 2110 X11_XUngrabPointer(display, CurrentTime); 2111 2112 X11_Xinput2UngrabTouch(_this, window); 2113 } 2114 X11_XSync(display, False); 2115 2116 if (!data->videodata->broken_pointer_grab) { 2117 return true; 2118 } else { 2119 return SDL_SetError("The X server refused to let us grab the mouse. You might experience input bugs."); 2120 } 2121} 2122 2123bool X11_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed) 2124{ 2125 SDL_WindowData *data = window->internal; 2126 Display *display; 2127 2128 if (!data) { 2129 return SDL_SetError("Invalid window data"); 2130 } 2131 2132 display = data->videodata->display; 2133 2134 if (grabbed) { 2135 /* If the window is unmapped, XGrab calls return GrabNotViewable, 2136 so when we get a MapNotify later, we'll try to update the grab as 2137 appropriate. */ 2138 if (window->flags & SDL_WINDOW_HIDDEN) { 2139 return true; 2140 } 2141 2142 /* GNOME needs the _XWAYLAND_MAY_GRAB_KEYBOARD message on XWayland: 2143 * 2144 * - message_type set to "_XWAYLAND_MAY_GRAB_KEYBOARD" 2145 * - window set to the xid of the window on which the grab is to be issued 2146 * - data.l[0] to a non-zero value 2147 * 2148 * The dconf setting `org/gnome/mutter/wayland/xwayland-allow-grabs` must be enabled as well. 2149 * 2150 * https://gitlab.gnome.org/GNOME/mutter/-/commit/5f132f39750f684c3732b4346dec810cd218d609 2151 */ 2152 if (_this->internal->is_xwayland) { 2153 Atom _XWAYLAND_MAY_GRAB_ATOM = X11_XInternAtom(display, "_XWAYLAND_MAY_GRAB_KEYBOARD", False); 2154 2155 if (_XWAYLAND_MAY_GRAB_ATOM != None) { 2156 XClientMessageEvent client_message; 2157 client_message.type = ClientMessage; 2158 client_message.window = data->xwindow; 2159 client_message.format = 32; 2160 client_message.message_type = _XWAYLAND_MAY_GRAB_ATOM; 2161 client_message.data.l[0] = 1; 2162 client_message.data.l[1] = CurrentTime; 2163 2164 X11_XSendEvent(display, DefaultRootWindow(display), False, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent *)&client_message); 2165 X11_XFlush(display); 2166 } 2167 } 2168 2169 X11_XGrabKeyboard(display, data->xwindow, True, GrabModeAsync, 2170 GrabModeAsync, CurrentTime); 2171 } else { 2172 X11_XUngrabKeyboard(display, CurrentTime); 2173 } 2174 X11_XSync(display, False); 2175 2176 return true; 2177} 2178 2179void X11_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window) 2180{ 2181 SDL_WindowData *data = window->internal; 2182 2183 if (data) { 2184 SDL_VideoData *videodata = data->videodata; 2185 Display *display = videodata->display; 2186 int numwindows = videodata->numwindows; 2187 SDL_WindowData **windowlist = videodata->windowlist; 2188 int i; 2189 2190 if (windowlist) { 2191 for (i = 0; i < numwindows; ++i) { 2192 if (windowlist[i] && (windowlist[i]->window == window)) { 2193 windowlist[i] = windowlist[numwindows - 1]; 2194 windowlist[numwindows - 1] = NULL; 2195 videodata->numwindows--; 2196 break; 2197 } 2198 } 2199 } 2200 2201 X11_DestroyInputContext(data); 2202 2203#ifdef SDL_VIDEO_DRIVER_X11_XSYNC 2204 X11_TermResizeSync(window); 2205#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ 2206 2207 if (!(window->flags & SDL_WINDOW_EXTERNAL)) { 2208 X11_XDestroyWindow(display, data->xwindow); 2209 X11_XFlush(display); 2210 } 2211 SDL_free(data); 2212 2213#ifdef SDL_VIDEO_DRIVER_X11_XFIXES 2214 // If the pointer barriers are active for this, deactivate it. 2215 if (videodata->active_cursor_confined_window == window) { 2216 X11_DestroyPointerBarrier(_this, window); 2217 } 2218#endif // SDL_VIDEO_DRIVER_X11_XFIXES 2219 } 2220 window->internal = NULL; 2221} 2222 2223bool X11_SetWindowHitTest(SDL_Window *window, bool enabled) 2224{ 2225 return true; // just succeed, the real work is done elsewhere. 2226} 2227 2228void X11_AcceptDragAndDrop(SDL_Window *window, bool accept) 2229{ 2230 SDL_WindowData *data = window->internal; 2231 Display *display = data->videodata->display; 2232 Atom XdndAware = data->videodata->atoms.XdndAware; 2233 2234 if (accept) { 2235 Atom xdnd_version = 5; 2236 X11_XChangeProperty(display, data->xwindow, XdndAware, XA_ATOM, 32, 2237 PropModeReplace, (unsigned char *)&xdnd_version, 1); 2238 } else { 2239 X11_XDeleteProperty(display, data->xwindow, XdndAware); 2240 } 2241} 2242 2243bool X11_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation) 2244{ 2245 SDL_WindowData *data = window->internal; 2246 Display *display = data->videodata->display; 2247 XWMHints *wmhints; 2248 2249 wmhints = X11_XGetWMHints(display, data->xwindow); 2250 if (!wmhints) { 2251 return SDL_SetError("Couldn't get WM hints"); 2252 } 2253 2254 wmhints->flags &= ~XUrgencyHint; 2255 data->flashing_window = false; 2256 data->flash_cancel_time = 0; 2257 2258 switch (operation) { 2259 case SDL_FLASH_CANCEL: 2260 // Taken care of above 2261 break; 2262 case SDL_FLASH_BRIEFLY: 2263 if (!(window->flags & SDL_WINDOW_INPUT_FOCUS)) { 2264 wmhints->flags |= XUrgencyHint; 2265 data->flashing_window = true; 2266 // On Ubuntu 21.04 this causes a dialog to pop up, so leave it up for a full second so users can see it 2267 data->flash_cancel_time = SDL_GetTicks() + 1000; 2268 } 2269 break; 2270 case SDL_FLASH_UNTIL_FOCUSED: 2271 if (!(window->flags & SDL_WINDOW_INPUT_FOCUS)) { 2272 wmhints->flags |= XUrgencyHint; 2273 data->flashing_window = true; 2274 } 2275 break; 2276 default: 2277 break; 2278 } 2279 2280 X11_XSetWMHints(display, data->xwindow, wmhints); 2281 X11_XFree(wmhints); 2282 return true; 2283} 2284 2285bool SDL_X11_SetWindowTitle(Display *display, Window xwindow, char *title) 2286{ 2287 Atom _NET_WM_NAME = X11_XInternAtom(display, "_NET_WM_NAME", False); 2288 XTextProperty titleprop; 2289 int conv = X11_XmbTextListToTextProperty(display, &title, 1, XTextStyle, &titleprop); 2290 Status status; 2291 2292 if (X11_XSupportsLocale() != True) { 2293 return SDL_SetError("Current locale not supported by X server, cannot continue."); 2294 } 2295 2296 if (conv == 0) { 2297 X11_XSetTextProperty(display, xwindow, &titleprop, XA_WM_NAME); 2298 X11_XFree(titleprop.value); 2299 // we know this can't be a locale error as we checked X locale validity 2300 } else if (conv < 0) { 2301 return SDL_OutOfMemory(); 2302 } else { // conv > 0 2303 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "%d characters were not convertible to the current locale!", conv); 2304 return true; 2305 } 2306 2307#ifdef X_HAVE_UTF8_STRING 2308 status = X11_Xutf8TextListToTextProperty(display, &title, 1, XUTF8StringStyle, &titleprop); 2309 if (status == Success) { 2310 X11_XSetTextProperty(display, xwindow, &titleprop, _NET_WM_NAME); 2311 X11_XFree(titleprop.value); 2312 } else { 2313 return SDL_SetError("Failed to convert title to UTF8! Bad encoding, or bad Xorg encoding? Window title: «%s»", title); 2314 } 2315#endif 2316 2317 X11_XFlush(display); 2318 return true; 2319} 2320 2321void X11_ShowWindowSystemMenu(SDL_Window *window, int x, int y) 2322{ 2323 SDL_WindowData *data = window->internal; 2324 SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window); 2325 Display *display = data->videodata->display; 2326 Window root = RootWindow(display, displaydata->screen); 2327 XClientMessageEvent e; 2328 Window childReturn; 2329 int wx, wy; 2330 2331 SDL_zero(e); 2332 X11_XTranslateCoordinates(display, data->xwindow, root, x, y, &wx, &wy, &childReturn); 2333 2334 e.type = ClientMessage; 2335 e.window = data->xwindow; 2336 e.message_type = X11_XInternAtom(display, "_GTK_SHOW_WINDOW_MENU", 0); 2337 e.data.l[0] = 0; // GTK device ID (unused) 2338 e.data.l[1] = wx; // X coordinate relative to root 2339 e.data.l[2] = wy; // Y coordinate relative to root 2340 e.format = 32; 2341 2342 X11_XSendEvent(display, root, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&e); 2343 X11_XFlush(display); 2344} 2345 2346bool X11_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window) 2347{ 2348 SDL_WindowData *data = window->internal; 2349 2350 // If the window is external and has only a pending resize or move event, use the special external sync path to avoid processing events. 2351 if ((window->flags & SDL_WINDOW_EXTERNAL) && 2352 (data->pending_operation & ~(X11_PENDING_OP_RESIZE | X11_PENDING_OP_MOVE)) == X11_PENDING_OP_NONE) { 2353 X11_ExternalResizeMoveSync(window); 2354 return true; 2355 } 2356 2357 const Uint64 current_time = SDL_GetTicksNS(); 2358 Uint64 timeout = 0; 2359 2360 // Allow time for any pending mode switches to complete. 2361 for (int i = 0; i < _this->num_displays; ++i) { 2362 if (_this->displays[i]->internal->mode_switch_deadline_ns && 2363 current_time < _this->displays[i]->internal->mode_switch_deadline_ns) { 2364 timeout = SDL_max(_this->displays[i]->internal->mode_switch_deadline_ns - current_time, timeout); 2365 } 2366 } 2367 2368 /* 100ms is fine for most cases, but, for some reason, maximizing 2369 * a window can take a very long time. 2370 */ 2371 timeout += window->internal->pending_operation & X11_PENDING_OP_MAXIMIZE ? SDL_MS_TO_NS(1000) : SDL_MS_TO_NS(100); 2372 2373 return X11_SyncWindowTimeout(_this, window, timeout); 2374} 2375 2376bool X11_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable) 2377{ 2378 if (!SDL_WINDOW_IS_POPUP(window)) { 2379 SDL_WindowData *data = window->internal; 2380 Display *display = data->videodata->display; 2381 XWMHints *wmhints; 2382 2383 wmhints = X11_XGetWMHints(display, data->xwindow); 2384 if (!wmhints) { 2385 return SDL_SetError("Couldn't get WM hints"); 2386 } 2387 2388 wmhints->input = focusable ? True : False; 2389 wmhints->flags |= InputHint; 2390 2391 X11_XSetWMHints(display, data->xwindow, wmhints); 2392 X11_XFree(wmhints); 2393 } else if (window->flags & SDL_WINDOW_POPUP_MENU) { 2394 if (!(window->flags & SDL_WINDOW_HIDDEN)) { 2395 if (!focusable && (window->flags & SDL_WINDOW_INPUT_FOCUS)) { 2396 SDL_Window *new_focus; 2397 const bool set_focus = SDL_ShouldRelinquishPopupFocus(window, &new_focus); 2398 X11_SetKeyboardFocus(new_focus, set_focus); 2399 } else if (focusable) { 2400 if (SDL_ShouldFocusPopup(window)) { 2401 X11_SetKeyboardFocus(window, true); 2402 } 2403 } 2404 } 2405 2406 return true; 2407 } 2408 2409 return true; 2410} 2411 2412#endif // SDL_VIDEO_DRIVER_X11 2413[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.