SDL_x11window.c (58584B)
1 /* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org> 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 #if SDL_VIDEO_DRIVER_X11 24 25 #include "SDL_hints.h" 26 #include "../SDL_sysvideo.h" 27 #include "../SDL_pixels_c.h" 28 #include "../../events/SDL_keyboard_c.h" 29 #include "../../events/SDL_mouse_c.h" 30 31 #include "SDL_x11video.h" 32 #include "SDL_x11mouse.h" 33 #include "SDL_x11shape.h" 34 #include "SDL_x11xinput2.h" 35 36 #if SDL_VIDEO_OPENGL_EGL 37 #include "SDL_x11opengles.h" 38 #endif 39 40 #include "SDL_timer.h" 41 #include "SDL_syswm.h" 42 43 #define _NET_WM_STATE_REMOVE 0l 44 #define _NET_WM_STATE_ADD 1l 45 46 static Bool isMapNotify(Display *dpy, XEvent *ev, XPointer win) 47 { 48 return ev->type == MapNotify && ev->xmap.window == *((Window*)win); 49 } 50 static Bool isUnmapNotify(Display *dpy, XEvent *ev, XPointer win) 51 { 52 return ev->type == UnmapNotify && ev->xunmap.window == *((Window*)win); 53 } 54 55 /* 56 static Bool isConfigureNotify(Display *dpy, XEvent *ev, XPointer win) 57 { 58 return ev->type == ConfigureNotify && ev->xconfigure.window == *((Window*)win); 59 } 60 static Bool 61 X11_XIfEventTimeout(Display *display, XEvent *event_return, Bool (*predicate)(), XPointer arg, int timeoutMS) 62 { 63 Uint32 start = SDL_GetTicks(); 64 65 while (!X11_XCheckIfEvent(display, event_return, predicate, arg)) { 66 if (SDL_TICKS_PASSED(SDL_GetTicks(), start + timeoutMS)) { 67 return False; 68 } 69 } 70 return True; 71 } 72 */ 73 74 static SDL_bool 75 X11_IsWindowLegacyFullscreen(_THIS, SDL_Window * window) 76 { 77 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 78 return (data->fswindow != 0); 79 } 80 81 static SDL_bool 82 X11_IsWindowMapped(_THIS, SDL_Window * window) 83 { 84 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 85 SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata; 86 XWindowAttributes attr; 87 88 X11_XGetWindowAttributes(videodata->display, data->xwindow, &attr); 89 if (attr.map_state != IsUnmapped) { 90 return SDL_TRUE; 91 } else { 92 return SDL_FALSE; 93 } 94 } 95 96 #if 0 97 static SDL_bool 98 X11_IsActionAllowed(SDL_Window *window, Atom action) 99 { 100 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 101 Atom _NET_WM_ALLOWED_ACTIONS = data->videodata->_NET_WM_ALLOWED_ACTIONS; 102 Atom type; 103 Display *display = data->videodata->display; 104 int form; 105 unsigned long remain; 106 unsigned long len, i; 107 Atom *list; 108 SDL_bool ret = SDL_FALSE; 109 110 if (X11_XGetWindowProperty(display, data->xwindow, _NET_WM_ALLOWED_ACTIONS, 0, 1024, False, XA_ATOM, &type, &form, &len, &remain, (unsigned char **)&list) == Success) 111 { 112 for (i=0; i<len; ++i) 113 { 114 if (list[i] == action) { 115 ret = SDL_TRUE; 116 break; 117 } 118 } 119 X11_XFree(list); 120 } 121 return ret; 122 } 123 #endif /* 0 */ 124 125 void 126 X11_SetNetWMState(_THIS, Window xwindow, Uint32 flags) 127 { 128 SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata; 129 Display *display = videodata->display; 130 /* !!! FIXME: just dereference videodata below instead of copying to locals. */ 131 Atom _NET_WM_STATE = videodata->_NET_WM_STATE; 132 /* Atom _NET_WM_STATE_HIDDEN = videodata->_NET_WM_STATE_HIDDEN; */ 133 Atom _NET_WM_STATE_FOCUSED = videodata->_NET_WM_STATE_FOCUSED; 134 Atom _NET_WM_STATE_MAXIMIZED_VERT = videodata->_NET_WM_STATE_MAXIMIZED_VERT; 135 Atom _NET_WM_STATE_MAXIMIZED_HORZ = videodata->_NET_WM_STATE_MAXIMIZED_HORZ; 136 Atom _NET_WM_STATE_FULLSCREEN = videodata->_NET_WM_STATE_FULLSCREEN; 137 Atom _NET_WM_STATE_ABOVE = videodata->_NET_WM_STATE_ABOVE; 138 Atom _NET_WM_STATE_SKIP_TASKBAR = videodata->_NET_WM_STATE_SKIP_TASKBAR; 139 Atom _NET_WM_STATE_SKIP_PAGER = videodata->_NET_WM_STATE_SKIP_PAGER; 140 Atom atoms[16]; 141 int count = 0; 142 143 /* The window manager sets this property, we shouldn't set it. 144 If we did, this would indicate to the window manager that we don't 145 actually want to be mapped during X11_XMapRaised(), which would be bad. 146 * 147 if (flags & SDL_WINDOW_HIDDEN) { 148 atoms[count++] = _NET_WM_STATE_HIDDEN; 149 } 150 */ 151 152 if (flags & SDL_WINDOW_ALWAYS_ON_TOP) { 153 atoms[count++] = _NET_WM_STATE_ABOVE; 154 } 155 if (flags & SDL_WINDOW_SKIP_TASKBAR) { 156 atoms[count++] = _NET_WM_STATE_SKIP_TASKBAR; 157 atoms[count++] = _NET_WM_STATE_SKIP_PAGER; 158 } 159 if (flags & SDL_WINDOW_INPUT_FOCUS) { 160 atoms[count++] = _NET_WM_STATE_FOCUSED; 161 } 162 if (flags & SDL_WINDOW_MAXIMIZED) { 163 atoms[count++] = _NET_WM_STATE_MAXIMIZED_VERT; 164 atoms[count++] = _NET_WM_STATE_MAXIMIZED_HORZ; 165 } 166 if (flags & SDL_WINDOW_FULLSCREEN) { 167 atoms[count++] = _NET_WM_STATE_FULLSCREEN; 168 } 169 170 SDL_assert(count <= SDL_arraysize(atoms)); 171 172 if (count > 0) { 173 X11_XChangeProperty(display, xwindow, _NET_WM_STATE, XA_ATOM, 32, 174 PropModeReplace, (unsigned char *)atoms, count); 175 } else { 176 X11_XDeleteProperty(display, xwindow, _NET_WM_STATE); 177 } 178 } 179 180 Uint32 181 X11_GetNetWMState(_THIS, Window xwindow) 182 { 183 SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata; 184 Display *display = videodata->display; 185 Atom _NET_WM_STATE = videodata->_NET_WM_STATE; 186 Atom _NET_WM_STATE_HIDDEN = videodata->_NET_WM_STATE_HIDDEN; 187 Atom _NET_WM_STATE_FOCUSED = videodata->_NET_WM_STATE_FOCUSED; 188 Atom _NET_WM_STATE_MAXIMIZED_VERT = videodata->_NET_WM_STATE_MAXIMIZED_VERT; 189 Atom _NET_WM_STATE_MAXIMIZED_HORZ = videodata->_NET_WM_STATE_MAXIMIZED_HORZ; 190 Atom _NET_WM_STATE_FULLSCREEN = videodata->_NET_WM_STATE_FULLSCREEN; 191 Atom actualType; 192 int actualFormat; 193 unsigned long i, numItems, bytesAfter; 194 unsigned char *propertyValue = NULL; 195 long maxLength = 1024; 196 Uint32 flags = 0; 197 198 if (X11_XGetWindowProperty(display, xwindow, _NET_WM_STATE, 199 0l, maxLength, False, XA_ATOM, &actualType, 200 &actualFormat, &numItems, &bytesAfter, 201 &propertyValue) == Success) { 202 Atom *atoms = (Atom *) propertyValue; 203 int maximized = 0; 204 int fullscreen = 0; 205 206 for (i = 0; i < numItems; ++i) { 207 if (atoms[i] == _NET_WM_STATE_HIDDEN) { 208 flags |= SDL_WINDOW_HIDDEN; 209 } else if (atoms[i] == _NET_WM_STATE_FOCUSED) { 210 flags |= SDL_WINDOW_INPUT_FOCUS; 211 } else if (atoms[i] == _NET_WM_STATE_MAXIMIZED_VERT) { 212 maximized |= 1; 213 } else if (atoms[i] == _NET_WM_STATE_MAXIMIZED_HORZ) { 214 maximized |= 2; 215 } else if ( atoms[i] == _NET_WM_STATE_FULLSCREEN) { 216 fullscreen = 1; 217 } 218 } 219 if (maximized == 3) { 220 flags |= SDL_WINDOW_MAXIMIZED; 221 } 222 223 if (fullscreen == 1) { 224 flags |= SDL_WINDOW_FULLSCREEN; 225 } 226 227 /* If the window is unmapped, numItems will be zero and _NET_WM_STATE_HIDDEN 228 * will not be set. Do an additional check to see if the window is unmapped 229 * and mark it as SDL_WINDOW_HIDDEN if it is. 230 */ 231 { 232 XWindowAttributes attr; 233 SDL_memset(&attr,0,sizeof(attr)); 234 X11_XGetWindowAttributes(videodata->display, xwindow, &attr); 235 if (attr.map_state == IsUnmapped) { 236 flags |= SDL_WINDOW_HIDDEN; 237 } 238 } 239 X11_XFree(propertyValue); 240 } 241 242 /* FIXME, check the size hints for resizable */ 243 /* flags |= SDL_WINDOW_RESIZABLE; */ 244 245 return flags; 246 } 247 248 static int 249 SetupWindowData(_THIS, SDL_Window * window, Window w, BOOL created) 250 { 251 SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata; 252 SDL_WindowData *data; 253 int numwindows = videodata->numwindows; 254 int windowlistlength = videodata->windowlistlength; 255 SDL_WindowData **windowlist = videodata->windowlist; 256 257 /* Allocate the window data */ 258 data = (SDL_WindowData *) SDL_calloc(1, sizeof(*data)); 259 if (!data) { 260 return SDL_OutOfMemory(); 261 } 262 data->window = window; 263 data->xwindow = w; 264 #ifdef X_HAVE_UTF8_STRING 265 if (SDL_X11_HAVE_UTF8 && videodata->im) { 266 data->ic = 267 X11_XCreateIC(videodata->im, XNClientWindow, w, XNFocusWindow, w, 268 XNInputStyle, XIMPreeditNothing | XIMStatusNothing, 269 NULL); 270 } 271 #endif 272 data->created = created; 273 data->videodata = videodata; 274 275 /* Associate the data with the window */ 276 277 if (numwindows < windowlistlength) { 278 windowlist[numwindows] = data; 279 videodata->numwindows++; 280 } else { 281 windowlist = 282 (SDL_WindowData **) SDL_realloc(windowlist, 283 (numwindows + 284 1) * sizeof(*windowlist)); 285 if (!windowlist) { 286 SDL_free(data); 287 return SDL_OutOfMemory(); 288 } 289 windowlist[numwindows] = data; 290 videodata->numwindows++; 291 videodata->windowlistlength++; 292 videodata->windowlist = windowlist; 293 } 294 295 /* Fill in the SDL window with the window data */ 296 { 297 XWindowAttributes attrib; 298 299 X11_XGetWindowAttributes(data->videodata->display, w, &attrib); 300 window->x = attrib.x; 301 window->y = attrib.y; 302 window->w = attrib.width; 303 window->h = attrib.height; 304 if (attrib.map_state != IsUnmapped) { 305 window->flags |= SDL_WINDOW_SHOWN; 306 } else { 307 window->flags &= ~SDL_WINDOW_SHOWN; 308 } 309 data->visual = attrib.visual; 310 data->colormap = attrib.colormap; 311 } 312 313 window->flags |= X11_GetNetWMState(_this, w); 314 315 { 316 Window FocalWindow; 317 int RevertTo=0; 318 X11_XGetInputFocus(data->videodata->display, &FocalWindow, &RevertTo); 319 if (FocalWindow==w) 320 { 321 window->flags |= SDL_WINDOW_INPUT_FOCUS; 322 } 323 324 if (window->flags & SDL_WINDOW_INPUT_FOCUS) { 325 SDL_SetKeyboardFocus(data->window); 326 } 327 328 if (window->flags & SDL_WINDOW_INPUT_GRABBED) { 329 /* Tell x11 to clip mouse */ 330 } 331 } 332 333 /* All done! */ 334 window->driverdata = data; 335 return 0; 336 } 337 338 static void 339 SetWindowBordered(Display *display, int screen, Window window, SDL_bool border) 340 { 341 /* 342 * this code used to check for KWM_WIN_DECORATION, but KDE hasn't 343 * supported it for years and years. It now respects _MOTIF_WM_HINTS. 344 * Gnome is similar: just use the Motif atom. 345 */ 346 347 Atom WM_HINTS = X11_XInternAtom(display, "_MOTIF_WM_HINTS", True); 348 if (WM_HINTS != None) { 349 /* Hints used by Motif compliant window managers */ 350 struct 351 { 352 unsigned long flags; 353 unsigned long functions; 354 unsigned long decorations; 355 long input_mode; 356 unsigned long status; 357 } MWMHints = { 358 (1L << 1), 0, border ? 1 : 0, 0, 0 359 }; 360 361 X11_XChangeProperty(display, window, WM_HINTS, WM_HINTS, 32, 362 PropModeReplace, (unsigned char *) &MWMHints, 363 sizeof(MWMHints) / sizeof(long)); 364 } else { /* set the transient hints instead, if necessary */ 365 X11_XSetTransientForHint(display, window, RootWindow(display, screen)); 366 } 367 } 368 369 int 370 X11_CreateWindow(_THIS, SDL_Window * window) 371 { 372 SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; 373 SDL_DisplayData *displaydata = 374 (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata; 375 SDL_WindowData *windowdata; 376 Display *display = data->display; 377 int screen = displaydata->screen; 378 Visual *visual; 379 int depth; 380 XSetWindowAttributes xattr; 381 Window w; 382 XSizeHints *sizehints; 383 XWMHints *wmhints; 384 XClassHint *classhints; 385 Atom _NET_WM_BYPASS_COMPOSITOR; 386 Atom _NET_WM_WINDOW_TYPE; 387 Atom wintype; 388 const char *wintype_name = NULL; 389 long compositor = 1; 390 Atom _NET_WM_PID; 391 long fevent = 0; 392 393 #if SDL_VIDEO_OPENGL_GLX || SDL_VIDEO_OPENGL_EGL 394 const char *forced_visual_id = SDL_GetHint(SDL_HINT_VIDEO_X11_WINDOW_VISUALID); 395 396 if (forced_visual_id != NULL && forced_visual_id[0] != '\0') 397 { 398 XVisualInfo *vi, template; 399 int nvis; 400 401 SDL_zero(template); 402 template.visualid = SDL_strtol(forced_visual_id, NULL, 0); 403 vi = X11_XGetVisualInfo(display, VisualIDMask, &template, &nvis); 404 if (vi) { 405 visual = vi->visual; 406 depth = vi->depth; 407 X11_XFree(vi); 408 } 409 else 410 { 411 return -1; 412 } 413 } 414 else if ((window->flags & SDL_WINDOW_OPENGL) && 415 !SDL_getenv("SDL_VIDEO_X11_VISUALID")) { 416 XVisualInfo *vinfo = NULL; 417 418 #if SDL_VIDEO_OPENGL_EGL 419 if (((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) || 420 SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_FORCE_EGL, SDL_FALSE)) 421 #if SDL_VIDEO_OPENGL_GLX 422 && ( !_this->gl_data || X11_GL_UseEGL(_this) ) 423 #endif 424 ) { 425 vinfo = X11_GLES_GetVisual(_this, display, screen); 426 } else 427 #endif 428 { 429 #if SDL_VIDEO_OPENGL_GLX 430 vinfo = X11_GL_GetVisual(_this, display, screen); 431 #endif 432 } 433 434 if (!vinfo) { 435 return -1; 436 } 437 visual = vinfo->visual; 438 depth = vinfo->depth; 439 X11_XFree(vinfo); 440 } else 441 #endif 442 { 443 visual = displaydata->visual; 444 depth = displaydata->depth; 445 } 446 447 xattr.override_redirect = ((window->flags & SDL_WINDOW_TOOLTIP) || (window->flags & SDL_WINDOW_POPUP_MENU)) ? True : False; 448 xattr.background_pixmap = None; 449 xattr.border_pixel = 0; 450 451 if (visual->class == DirectColor) { 452 XColor *colorcells; 453 int i; 454 int ncolors; 455 int rmax, gmax, bmax; 456 int rmask, gmask, bmask; 457 int rshift, gshift, bshift; 458 459 xattr.colormap = 460 X11_XCreateColormap(display, RootWindow(display, screen), 461 visual, AllocAll); 462 463 /* If we can't create a colormap, then we must die */ 464 if (!xattr.colormap) { 465 return SDL_SetError("Could not create writable colormap"); 466 } 467 468 /* OK, we got a colormap, now fill it in as best as we can */ 469 colorcells = SDL_malloc(visual->map_entries * sizeof(XColor)); 470 if (!colorcells) { 471 return SDL_OutOfMemory(); 472 } 473 ncolors = visual->map_entries; 474 rmax = 0xffff; 475 gmax = 0xffff; 476 bmax = 0xffff; 477 478 rshift = 0; 479 rmask = visual->red_mask; 480 while (0 == (rmask & 1)) { 481 rshift++; 482 rmask >>= 1; 483 } 484 485 gshift = 0; 486 gmask = visual->green_mask; 487 while (0 == (gmask & 1)) { 488 gshift++; 489 gmask >>= 1; 490 } 491 492 bshift = 0; 493 bmask = visual->blue_mask; 494 while (0 == (bmask & 1)) { 495 bshift++; 496 bmask >>= 1; 497 } 498 499 /* build the color table pixel values */ 500 for (i = 0; i < ncolors; i++) { 501 Uint32 red = (rmax * i) / (ncolors - 1); 502 Uint32 green = (gmax * i) / (ncolors - 1); 503 Uint32 blue = (bmax * i) / (ncolors - 1); 504 505 Uint32 rbits = (rmask * i) / (ncolors - 1); 506 Uint32 gbits = (gmask * i) / (ncolors - 1); 507 Uint32 bbits = (bmask * i) / (ncolors - 1); 508 509 Uint32 pix = 510 (rbits << rshift) | (gbits << gshift) | (bbits << bshift); 511 512 colorcells[i].pixel = pix; 513 514 colorcells[i].red = red; 515 colorcells[i].green = green; 516 colorcells[i].blue = blue; 517 518 colorcells[i].flags = DoRed | DoGreen | DoBlue; 519 } 520 521 X11_XStoreColors(display, xattr.colormap, colorcells, ncolors); 522 523 SDL_free(colorcells); 524 } else { 525 xattr.colormap = 526 X11_XCreateColormap(display, RootWindow(display, screen), 527 visual, AllocNone); 528 } 529 530 w = X11_XCreateWindow(display, RootWindow(display, screen), 531 window->x, window->y, window->w, window->h, 532 0, depth, InputOutput, visual, 533 (CWOverrideRedirect | CWBackPixmap | CWBorderPixel | 534 CWColormap), &xattr); 535 if (!w) { 536 return SDL_SetError("Couldn't create window"); 537 } 538 539 SetWindowBordered(display, screen, w, 540 (window->flags & SDL_WINDOW_BORDERLESS) == 0); 541 542 sizehints = X11_XAllocSizeHints(); 543 /* Setup the normal size hints */ 544 sizehints->flags = 0; 545 if (!(window->flags & SDL_WINDOW_RESIZABLE)) { 546 sizehints->min_width = sizehints->max_width = window->w; 547 sizehints->min_height = sizehints->max_height = window->h; 548 sizehints->flags |= (PMaxSize | PMinSize); 549 } 550 sizehints->x = window->x; 551 sizehints->y = window->y; 552 sizehints->flags |= USPosition; 553 554 /* Setup the input hints so we get keyboard input */ 555 wmhints = X11_XAllocWMHints(); 556 wmhints->input = True; 557 wmhints->window_group = data->window_group; 558 wmhints->flags = InputHint | WindowGroupHint; 559 560 /* Setup the class hints so we can get an icon (AfterStep) */ 561 classhints = X11_XAllocClassHint(); 562 classhints->res_name = data->classname; 563 classhints->res_class = data->classname; 564 565 /* Set the size, input and class hints, and define WM_CLIENT_MACHINE and WM_LOCALE_NAME */ 566 X11_XSetWMProperties(display, w, NULL, NULL, NULL, 0, sizehints, wmhints, classhints); 567 568 X11_XFree(sizehints); 569 X11_XFree(wmhints); 570 X11_XFree(classhints); 571 /* Set the PID related to the window for the given hostname, if possible */ 572 if (data->pid > 0) { 573 long pid = (long) data->pid; 574 _NET_WM_PID = X11_XInternAtom(display, "_NET_WM_PID", False); 575 X11_XChangeProperty(display, w, _NET_WM_PID, XA_CARDINAL, 32, PropModeReplace, 576 (unsigned char *) &pid, 1); 577 } 578 579 /* Set the window manager state */ 580 X11_SetNetWMState(_this, w, window->flags); 581 582 compositor = 2; /* don't disable compositing except for "normal" windows */ 583 584 if (window->flags & SDL_WINDOW_UTILITY) { 585 wintype_name = "_NET_WM_WINDOW_TYPE_UTILITY"; 586 } else if (window->flags & SDL_WINDOW_TOOLTIP) { 587 wintype_name = "_NET_WM_WINDOW_TYPE_TOOLTIP"; 588 } else if (window->flags & SDL_WINDOW_POPUP_MENU) { 589 wintype_name = "_NET_WM_WINDOW_TYPE_POPUP_MENU"; 590 } else { 591 wintype_name = "_NET_WM_WINDOW_TYPE_NORMAL"; 592 compositor = 1; /* disable compositing for "normal" windows */ 593 } 594 595 /* Let the window manager know what type of window we are. */ 596 _NET_WM_WINDOW_TYPE = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE", False); 597 wintype = X11_XInternAtom(display, wintype_name, False); 598 X11_XChangeProperty(display, w, _NET_WM_WINDOW_TYPE, XA_ATOM, 32, 599 PropModeReplace, (unsigned char *)&wintype, 1); 600 if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, SDL_TRUE)) { 601 _NET_WM_BYPASS_COMPOSITOR = X11_XInternAtom(display, "_NET_WM_BYPASS_COMPOSITOR", False); 602 X11_XChangeProperty(display, w, _NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32, 603 PropModeReplace, 604 (unsigned char *)&compositor, 1); 605 } 606 607 { 608 Atom protocols[3]; 609 int proto_count = 0; 610 611 protocols[proto_count++] = data->WM_DELETE_WINDOW; /* Allow window to be deleted by the WM */ 612 protocols[proto_count++] = data->WM_TAKE_FOCUS; /* Since we will want to set input focus explicitly */ 613 614 /* Default to using ping if there is no hint */ 615 if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_NET_WM_PING, SDL_TRUE)) { 616 protocols[proto_count++] = data->_NET_WM_PING; /* Respond so WM knows we're alive */ 617 } 618 619 SDL_assert(proto_count <= sizeof(protocols) / sizeof(protocols[0])); 620 621 X11_XSetWMProtocols(display, w, protocols, proto_count); 622 } 623 624 if (SetupWindowData(_this, window, w, SDL_TRUE) < 0) { 625 X11_XDestroyWindow(display, w); 626 return -1; 627 } 628 windowdata = (SDL_WindowData *) window->driverdata; 629 630 #if SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2 || SDL_VIDEO_OPENGL_EGL 631 if ((window->flags & SDL_WINDOW_OPENGL) && 632 ((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) || 633 SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_FORCE_EGL, SDL_FALSE)) 634 #if SDL_VIDEO_OPENGL_GLX 635 && ( !_this->gl_data || X11_GL_UseEGL(_this) ) 636 #endif 637 ) { 638 #if SDL_VIDEO_OPENGL_EGL 639 if (!_this->egl_data) { 640 return -1; 641 } 642 643 /* Create the GLES window surface */ 644 windowdata->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) w); 645 646 if (windowdata->egl_surface == EGL_NO_SURFACE) { 647 return SDL_SetError("Could not create GLES window surface"); 648 } 649 #else 650 return SDL_SetError("Could not create GLES window surface (EGL support not configured)"); 651 #endif /* SDL_VIDEO_OPENGL_EGL */ 652 } 653 #endif 654 655 656 #ifdef X_HAVE_UTF8_STRING 657 if (SDL_X11_HAVE_UTF8 && windowdata->ic) { 658 X11_XGetICValues(windowdata->ic, XNFilterEvents, &fevent, NULL); 659 } 660 #endif 661 662 X11_Xinput2SelectTouch(_this, window); 663 664 X11_XSelectInput(display, w, 665 (FocusChangeMask | EnterWindowMask | LeaveWindowMask | 666 ExposureMask | ButtonPressMask | ButtonReleaseMask | 667 PointerMotionMask | KeyPressMask | KeyReleaseMask | 668 PropertyChangeMask | StructureNotifyMask | 669 KeymapStateMask | fevent)); 670 671 X11_XFlush(display); 672 673 return 0; 674 } 675 676 int 677 X11_CreateWindowFrom(_THIS, SDL_Window * window, const void *data) 678 { 679 Window w = (Window) data; 680 681 window->title = X11_GetWindowTitle(_this, w); 682 683 if (SetupWindowData(_this, window, w, SDL_FALSE) < 0) { 684 return -1; 685 } 686 return 0; 687 } 688 689 char * 690 X11_GetWindowTitle(_THIS, Window xwindow) 691 { 692 SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; 693 Display *display = data->display; 694 int status, real_format; 695 Atom real_type; 696 unsigned long items_read, items_left; 697 unsigned char *propdata; 698 char *title = NULL; 699 700 status = X11_XGetWindowProperty(display, xwindow, data->_NET_WM_NAME, 701 0L, 8192L, False, data->UTF8_STRING, &real_type, &real_format, 702 &items_read, &items_left, &propdata); 703 if (status == Success && propdata) { 704 title = SDL_strdup(SDL_static_cast(char*, propdata)); 705 X11_XFree(propdata); 706 } else { 707 status = X11_XGetWindowProperty(display, xwindow, XA_WM_NAME, 708 0L, 8192L, False, XA_STRING, &real_type, &real_format, 709 &items_read, &items_left, &propdata); 710 if (status == Success && propdata) { 711 title = SDL_iconv_string("UTF-8", "", SDL_static_cast(char*, propdata), items_read+1); 712 X11_XFree(propdata); 713 } else { 714 title = SDL_strdup(""); 715 } 716 } 717 return title; 718 } 719 720 void 721 X11_SetWindowTitle(_THIS, SDL_Window * window) 722 { 723 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 724 Display *display = data->videodata->display; 725 XTextProperty titleprop; 726 Status status; 727 const char *title = window->title ? window->title : ""; 728 char *title_locale = NULL; 729 730 #ifdef X_HAVE_UTF8_STRING 731 Atom _NET_WM_NAME = data->videodata->_NET_WM_NAME; 732 #endif 733 734 title_locale = SDL_iconv_utf8_locale(title); 735 if (!title_locale) { 736 SDL_OutOfMemory(); 737 return; 738 } 739 740 status = X11_XStringListToTextProperty(&title_locale, 1, &titleprop); 741 SDL_free(title_locale); 742 if (status) { 743 X11_XSetTextProperty(display, data->xwindow, &titleprop, XA_WM_NAME); 744 X11_XFree(titleprop.value); 745 } 746 #ifdef X_HAVE_UTF8_STRING 747 if (SDL_X11_HAVE_UTF8) { 748 status = X11_Xutf8TextListToTextProperty(display, (char **) &title, 1, 749 XUTF8StringStyle, &titleprop); 750 if (status == Success) { 751 X11_XSetTextProperty(display, data->xwindow, &titleprop, 752 _NET_WM_NAME); 753 X11_XFree(titleprop.value); 754 } 755 } 756 #endif 757 758 X11_XFlush(display); 759 } 760 761 void 762 X11_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon) 763 { 764 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 765 Display *display = data->videodata->display; 766 Atom _NET_WM_ICON = data->videodata->_NET_WM_ICON; 767 768 if (icon) { 769 int propsize; 770 long *propdata; 771 772 /* Set the _NET_WM_ICON property */ 773 SDL_assert(icon->format->format == SDL_PIXELFORMAT_ARGB8888); 774 propsize = 2 + (icon->w * icon->h); 775 propdata = SDL_malloc(propsize * sizeof(long)); 776 if (propdata) { 777 int x, y; 778 Uint32 *src; 779 long *dst; 780 781 propdata[0] = icon->w; 782 propdata[1] = icon->h; 783 dst = &propdata[2]; 784 for (y = 0; y < icon->h; ++y) { 785 src = (Uint32*)((Uint8*)icon->pixels + y * icon->pitch); 786 for (x = 0; x < icon->w; ++x) { 787 *dst++ = *src++; 788 } 789 } 790 X11_XChangeProperty(display, data->xwindow, _NET_WM_ICON, XA_CARDINAL, 791 32, PropModeReplace, (unsigned char *) propdata, 792 propsize); 793 } 794 SDL_free(propdata); 795 } else { 796 X11_XDeleteProperty(display, data->xwindow, _NET_WM_ICON); 797 } 798 X11_XFlush(display); 799 } 800 801 void 802 X11_SetWindowPosition(_THIS, SDL_Window * window) 803 { 804 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 805 Display *display = data->videodata->display; 806 unsigned int childCount; 807 Window childReturn, root, parent; 808 Window* children; 809 XWindowAttributes attrs; 810 int orig_x, orig_y; 811 Uint32 timeout; 812 813 X11_XSync(display, False); 814 X11_XQueryTree(display, data->xwindow, &root, &parent, &children, &childCount); 815 X11_XGetWindowAttributes(display, data->xwindow, &attrs); 816 X11_XTranslateCoordinates(display, parent, DefaultRootWindow(display), 817 attrs.x, attrs.y, &orig_x, &orig_y, &childReturn); 818 819 /*Attempt to move the window*/ 820 X11_XMoveWindow(display, data->xwindow, window->x - data->border_left, window->y - data->border_top); 821 822 /* Wait a brief time to see if the window manager decided to let this move happen. 823 If the window changes at all, even to an unexpected value, we break out. */ 824 timeout = SDL_GetTicks() + 100; 825 while (SDL_TRUE) { 826 int x, y; 827 X11_XSync(display, False); 828 X11_XGetWindowAttributes(display, data->xwindow, &attrs); 829 X11_XTranslateCoordinates(display, parent, DefaultRootWindow(display), 830 attrs.x, attrs.y, &x, &y, &childReturn); 831 832 if ((x != orig_x) || (y != orig_y)) { 833 window->x = x; 834 window->y = y; 835 break; /* window moved, time to go. */ 836 } else if ((x == window->x) && (y == window->y)) { 837 break; /* we're at the place we wanted to be anyhow, drop out. */ 838 } 839 840 if (SDL_TICKS_PASSED(SDL_GetTicks(), timeout)) { 841 break; 842 } 843 844 SDL_Delay(10); 845 } 846 } 847 848 void 849 X11_SetWindowMinimumSize(_THIS, SDL_Window * window) 850 { 851 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 852 Display *display = data->videodata->display; 853 854 if (window->flags & SDL_WINDOW_RESIZABLE) { 855 XSizeHints *sizehints = X11_XAllocSizeHints(); 856 long userhints; 857 858 X11_XGetWMNormalHints(display, data->xwindow, sizehints, &userhints); 859 860 sizehints->min_width = window->min_w; 861 sizehints->min_height = window->min_h; 862 sizehints->flags |= PMinSize; 863 864 X11_XSetWMNormalHints(display, data->xwindow, sizehints); 865 866 X11_XFree(sizehints); 867 868 /* See comment in X11_SetWindowSize. */ 869 X11_XResizeWindow(display, data->xwindow, window->w, window->h); 870 X11_XMoveWindow(display, data->xwindow, window->x - data->border_left, window->y - data->border_top); 871 X11_XRaiseWindow(display, data->xwindow); 872 } 873 874 X11_XFlush(display); 875 } 876 877 void 878 X11_SetWindowMaximumSize(_THIS, SDL_Window * window) 879 { 880 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 881 Display *display = data->videodata->display; 882 883 if (window->flags & SDL_WINDOW_RESIZABLE) { 884 XSizeHints *sizehints = X11_XAllocSizeHints(); 885 long userhints; 886 887 X11_XGetWMNormalHints(display, data->xwindow, sizehints, &userhints); 888 889 sizehints->max_width = window->max_w; 890 sizehints->max_height = window->max_h; 891 sizehints->flags |= PMaxSize; 892 893 X11_XSetWMNormalHints(display, data->xwindow, sizehints); 894 895 X11_XFree(sizehints); 896 897 /* See comment in X11_SetWindowSize. */ 898 X11_XResizeWindow(display, data->xwindow, window->w, window->h); 899 X11_XMoveWindow(display, data->xwindow, window->x - data->border_left, window->y - data->border_top); 900 X11_XRaiseWindow(display, data->xwindow); 901 } 902 903 X11_XFlush(display); 904 } 905 906 void 907 X11_SetWindowSize(_THIS, SDL_Window * window) 908 { 909 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 910 Display *display = data->videodata->display; 911 XWindowAttributes attrs; 912 int orig_w, orig_h; 913 Uint32 timeout; 914 915 X11_XSync(display, False); 916 X11_XGetWindowAttributes(display, data->xwindow, &attrs); 917 orig_w = attrs.width; 918 orig_h = attrs.height; 919 920 if (SDL_IsShapedWindow(window)) { 921 X11_ResizeWindowShape(window); 922 } 923 if (!(window->flags & SDL_WINDOW_RESIZABLE)) { 924 /* Apparently, if the X11 Window is set to a 'non-resizable' window, you cannot resize it using the X11_XResizeWindow, thus 925 we must set the size hints to adjust the window size. */ 926 XSizeHints *sizehints = X11_XAllocSizeHints(); 927 long userhints; 928 929 X11_XGetWMNormalHints(display, data->xwindow, sizehints, &userhints); 930 931 sizehints->min_width = sizehints->max_width = window->w; 932 sizehints->min_height = sizehints->max_height = window->h; 933 sizehints->flags |= PMinSize | PMaxSize; 934 935 X11_XSetWMNormalHints(display, data->xwindow, sizehints); 936 937 X11_XFree(sizehints); 938 939 /* From Pierre-Loup: 940 WMs each have their little quirks with that. When you change the 941 size hints, they get a ConfigureNotify event with the 942 WM_NORMAL_SIZE_HINTS Atom. They all save the hints then, but they 943 don't all resize the window right away to enforce the new hints. 944 945 Some of them resize only after: 946 - A user-initiated move or resize 947 - A code-initiated move or resize 948 - Hiding & showing window (Unmap & map) 949 950 The following move & resize seems to help a lot of WMs that didn't 951 properly update after the hints were changed. We don't do a 952 hide/show, because there are supposedly subtle problems with doing so 953 and transitioning from windowed to fullscreen in Unity. 954 */ 955 X11_XResizeWindow(display, data->xwindow, window->w, window->h); 956 X11_XMoveWindow(display, data->xwindow, window->x - data->border_left, window->y - data->border_top); 957 X11_XRaiseWindow(display, data->xwindow); 958 } else { 959 X11_XResizeWindow(display, data->xwindow, window->w, window->h); 960 } 961 962 /* Wait a brief time to see if the window manager decided to let this resize happen. 963 If the window changes at all, even to an unexpected value, we break out. */ 964 timeout = SDL_GetTicks() + 100; 965 while (SDL_TRUE) { 966 X11_XSync(display, False); 967 X11_XGetWindowAttributes(display, data->xwindow, &attrs); 968 969 if ((attrs.width != orig_w) || (attrs.height != orig_h)) { 970 window->w = attrs.width; 971 window->h = attrs.height; 972 break; /* window changed, time to go. */ 973 } else if ((attrs.width == window->w) && (attrs.height == window->h)) { 974 break; /* we're at the place we wanted to be anyhow, drop out. */ 975 } 976 977 if (SDL_TICKS_PASSED(SDL_GetTicks(), timeout)) { 978 break; 979 } 980 981 SDL_Delay(10); 982 } 983 } 984 985 int 986 X11_GetWindowBordersSize(_THIS, SDL_Window * window, int *top, int *left, int *bottom, int *right) 987 { 988 SDL_WindowData *data = (SDL_WindowData *)window->driverdata; 989 990 *left = data->border_left; 991 *right = data->border_right; 992 *top = data->border_top; 993 *bottom = data->border_bottom; 994 995 return 0; 996 } 997 998 int 999 X11_SetWindowOpacity(_THIS, SDL_Window * window, float opacity) 1000 { 1001 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 1002 Display *display = data->videodata->display; 1003 Atom _NET_WM_WINDOW_OPACITY = data->videodata->_NET_WM_WINDOW_OPACITY; 1004 1005 if (opacity == 1.0f) { 1006 X11_XDeleteProperty(display, data->xwindow, _NET_WM_WINDOW_OPACITY); 1007 } else { 1008 const Uint32 FullyOpaque = 0xFFFFFFFF; 1009 const long alpha = (long) ((double)opacity * (double)FullyOpaque); 1010 X11_XChangeProperty(display, data->xwindow, _NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32, 1011 PropModeReplace, (unsigned char *)&alpha, 1); 1012 } 1013 1014 return 0; 1015 } 1016 1017 int 1018 X11_SetWindowModalFor(_THIS, SDL_Window * modal_window, SDL_Window * parent_window) { 1019 SDL_WindowData *data = (SDL_WindowData *) modal_window->driverdata; 1020 SDL_WindowData *parent_data = (SDL_WindowData *) parent_window->driverdata; 1021 Display *display = data->videodata->display; 1022 1023 X11_XSetTransientForHint(display, data->xwindow, parent_data->xwindow); 1024 return 0; 1025 } 1026 1027 int 1028 X11_SetWindowInputFocus(_THIS, SDL_Window * window) 1029 { 1030 if (X11_IsWindowMapped(_this, window)) { 1031 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 1032 Display *display = data->videodata->display; 1033 X11_XSetInputFocus(display, data->xwindow, RevertToNone, CurrentTime); 1034 X11_XFlush(display); 1035 return 0; 1036 } 1037 return -1; 1038 } 1039 1040 void 1041 X11_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered) 1042 { 1043 const SDL_bool focused = ((window->flags & SDL_WINDOW_INPUT_FOCUS) != 0); 1044 const SDL_bool visible = ((window->flags & SDL_WINDOW_HIDDEN) == 0); 1045 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 1046 SDL_DisplayData *displaydata = 1047 (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata; 1048 Display *display = data->videodata->display; 1049 XEvent event; 1050 1051 SetWindowBordered(display, displaydata->screen, data->xwindow, bordered); 1052 X11_XFlush(display); 1053 1054 if (visible) { 1055 XWindowAttributes attr; 1056 do { 1057 X11_XSync(display, False); 1058 X11_XGetWindowAttributes(display, data->xwindow, &attr); 1059 } while (attr.map_state != IsViewable); 1060 1061 if (focused) { 1062 X11_XSetInputFocus(display, data->xwindow, RevertToParent, CurrentTime); 1063 } 1064 } 1065 1066 /* make sure these don't make it to the real event queue if they fired here. */ 1067 X11_XSync(display, False); 1068 X11_XCheckIfEvent(display, &event, &isUnmapNotify, (XPointer)&data->xwindow); 1069 X11_XCheckIfEvent(display, &event, &isMapNotify, (XPointer)&data->xwindow); 1070 } 1071 1072 void 1073 X11_SetWindowResizable(_THIS, SDL_Window * window, SDL_bool resizable) 1074 { 1075 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 1076 Display *display = data->videodata->display; 1077 1078 XSizeHints *sizehints = X11_XAllocSizeHints(); 1079 long userhints; 1080 1081 X11_XGetWMNormalHints(display, data->xwindow, sizehints, &userhints); 1082 1083 if (resizable) { 1084 /* FIXME: Is there a better way to get max window size from X? -flibit */ 1085 const int maxsize = 0x7FFFFFFF; 1086 sizehints->min_width = window->min_w; 1087 sizehints->min_height = window->min_h; 1088 sizehints->max_width = (window->max_w == 0) ? maxsize : window->max_w; 1089 sizehints->max_height = (window->max_h == 0) ? maxsize : window->max_h; 1090 } else { 1091 sizehints->min_width = window->w; 1092 sizehints->min_height = window->h; 1093 sizehints->max_width = window->w; 1094 sizehints->max_height = window->h; 1095 } 1096 sizehints->flags |= PMinSize | PMaxSize; 1097 1098 X11_XSetWMNormalHints(display, data->xwindow, sizehints); 1099 1100 X11_XFree(sizehints); 1101 1102 /* See comment in X11_SetWindowSize. */ 1103 X11_XResizeWindow(display, data->xwindow, window->w, window->h); 1104 X11_XMoveWindow(display, data->xwindow, window->x - data->border_left, window->y - data->border_top); 1105 X11_XRaiseWindow(display, data->xwindow); 1106 1107 X11_XFlush(display); 1108 } 1109 1110 void 1111 X11_ShowWindow(_THIS, SDL_Window * window) 1112 { 1113 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 1114 Display *display = data->videodata->display; 1115 XEvent event; 1116 1117 if (!X11_IsWindowMapped(_this, window)) { 1118 X11_XMapRaised(display, data->xwindow); 1119 /* Blocking wait for "MapNotify" event. 1120 * We use X11_XIfEvent because pXWindowEvent takes a mask rather than a type, 1121 * and XCheckTypedWindowEvent doesn't block */ 1122 if(!(window->flags & SDL_WINDOW_FOREIGN)) 1123 X11_XIfEvent(display, &event, &isMapNotify, (XPointer)&data->xwindow); 1124 X11_XFlush(display); 1125 } 1126 1127 if (!data->videodata->net_wm) { 1128 /* no WM means no FocusIn event, which confuses us. Force it. */ 1129 X11_XSetInputFocus(display, data->xwindow, RevertToNone, CurrentTime); 1130 X11_XFlush(display); 1131 } 1132 } 1133 1134 void 1135 X11_HideWindow(_THIS, SDL_Window * window) 1136 { 1137 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 1138 SDL_DisplayData *displaydata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata; 1139 Display *display = data->videodata->display; 1140 XEvent event; 1141 1142 if (X11_IsWindowMapped(_this, window)) { 1143 X11_XWithdrawWindow(display, data->xwindow, displaydata->screen); 1144 /* Blocking wait for "UnmapNotify" event */ 1145 if(!(window->flags & SDL_WINDOW_FOREIGN)) 1146 X11_XIfEvent(display, &event, &isUnmapNotify, (XPointer)&data->xwindow); 1147 X11_XFlush(display); 1148 } 1149 } 1150 1151 static void 1152 SetWindowActive(_THIS, SDL_Window * window) 1153 { 1154 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 1155 SDL_DisplayData *displaydata = 1156 (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata; 1157 Display *display = data->videodata->display; 1158 Atom _NET_ACTIVE_WINDOW = data->videodata->_NET_ACTIVE_WINDOW; 1159 1160 if (X11_IsWindowMapped(_this, window)) { 1161 XEvent e; 1162 1163 /*printf("SDL Window %p: sending _NET_ACTIVE_WINDOW with timestamp %lu\n", window, data->user_time);*/ 1164 1165 SDL_zero(e); 1166 e.xany.type = ClientMessage; 1167 e.xclient.message_type = _NET_ACTIVE_WINDOW; 1168 e.xclient.format = 32; 1169 e.xclient.window = data->xwindow; 1170 e.xclient.data.l[0] = 1; /* source indication. 1 = application */ 1171 e.xclient.data.l[1] = data->user_time; 1172 e.xclient.data.l[2] = 0; 1173 1174 X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0, 1175 SubstructureNotifyMask | SubstructureRedirectMask, &e); 1176 1177 X11_XFlush(display); 1178 } 1179 } 1180 1181 void 1182 X11_RaiseWindow(_THIS, SDL_Window * window) 1183 { 1184 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 1185 Display *display = data->videodata->display; 1186 1187 X11_XRaiseWindow(display, data->xwindow); 1188 SetWindowActive(_this, window); 1189 X11_XFlush(display); 1190 } 1191 1192 static void 1193 SetWindowMaximized(_THIS, SDL_Window * window, SDL_bool maximized) 1194 { 1195 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 1196 SDL_DisplayData *displaydata = 1197 (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata; 1198 Display *display = data->videodata->display; 1199 Atom _NET_WM_STATE = data->videodata->_NET_WM_STATE; 1200 Atom _NET_WM_STATE_MAXIMIZED_VERT = data->videodata->_NET_WM_STATE_MAXIMIZED_VERT; 1201 Atom _NET_WM_STATE_MAXIMIZED_HORZ = data->videodata->_NET_WM_STATE_MAXIMIZED_HORZ; 1202 1203 if (maximized) { 1204 window->flags |= SDL_WINDOW_MAXIMIZED; 1205 } else { 1206 window->flags &= ~SDL_WINDOW_MAXIMIZED; 1207 } 1208 1209 if (X11_IsWindowMapped(_this, window)) { 1210 XEvent e; 1211 1212 SDL_zero(e); 1213 e.xany.type = ClientMessage; 1214 e.xclient.message_type = _NET_WM_STATE; 1215 e.xclient.format = 32; 1216 e.xclient.window = data->xwindow; 1217 e.xclient.data.l[0] = 1218 maximized ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; 1219 e.xclient.data.l[1] = _NET_WM_STATE_MAXIMIZED_VERT; 1220 e.xclient.data.l[2] = _NET_WM_STATE_MAXIMIZED_HORZ; 1221 e.xclient.data.l[3] = 0l; 1222 1223 X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0, 1224 SubstructureNotifyMask | SubstructureRedirectMask, &e); 1225 } else { 1226 X11_SetNetWMState(_this, data->xwindow, window->flags); 1227 } 1228 X11_XFlush(display); 1229 } 1230 1231 void 1232 X11_MaximizeWindow(_THIS, SDL_Window * window) 1233 { 1234 SetWindowMaximized(_this, window, SDL_TRUE); 1235 } 1236 1237 void 1238 X11_MinimizeWindow(_THIS, SDL_Window * window) 1239 { 1240 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 1241 SDL_DisplayData *displaydata = 1242 (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata; 1243 Display *display = data->videodata->display; 1244 1245 X11_XIconifyWindow(display, data->xwindow, displaydata->screen); 1246 X11_XFlush(display); 1247 } 1248 1249 void 1250 X11_RestoreWindow(_THIS, SDL_Window * window) 1251 { 1252 SetWindowMaximized(_this, window, SDL_FALSE); 1253 X11_ShowWindow(_this, window); 1254 SetWindowActive(_this, window); 1255 } 1256 1257 /* This asks the Window Manager to handle fullscreen for us. This is the modern way. */ 1258 static void 1259 X11_SetWindowFullscreenViaWM(_THIS, SDL_Window * window, SDL_VideoDisplay * _display, SDL_bool fullscreen) 1260 { 1261 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 1262 SDL_DisplayData *displaydata = (SDL_DisplayData *) _display->driverdata; 1263 Display *display = data->videodata->display; 1264 Atom _NET_WM_STATE = data->videodata->_NET_WM_STATE; 1265 Atom _NET_WM_STATE_FULLSCREEN = data->videodata->_NET_WM_STATE_FULLSCREEN; 1266 1267 if (X11_IsWindowMapped(_this, window)) { 1268 XEvent e; 1269 1270 if (!(window->flags & SDL_WINDOW_RESIZABLE)) { 1271 /* Compiz refuses fullscreen toggle if we're not resizable, so update the hints so we 1272 can be resized to the fullscreen resolution (or reset so we're not resizable again) */ 1273 XSizeHints *sizehints = X11_XAllocSizeHints(); 1274 long flags = 0; 1275 X11_XGetWMNormalHints(display, data->xwindow, sizehints, &flags); 1276 /* set the resize flags on */ 1277 if (fullscreen) { 1278 /* we are going fullscreen so turn the flags off */ 1279 sizehints->flags &= ~(PMinSize | PMaxSize); 1280 } else { 1281 /* Reset the min/max width height to make the window non-resizable again */ 1282 sizehints->flags |= PMinSize | PMaxSize; 1283 sizehints->min_width = sizehints->max_width = window->windowed.w; 1284 sizehints->min_height = sizehints->max_height = window->windowed.h; 1285 } 1286 X11_XSetWMNormalHints(display, data->xwindow, sizehints); 1287 X11_XFree(sizehints); 1288 } 1289 1290 SDL_zero(e); 1291 e.xany.type = ClientMessage; 1292 e.xclient.message_type = _NET_WM_STATE; 1293 e.xclient.format = 32; 1294 e.xclient.window = data->xwindow; 1295 e.xclient.data.l[0] = 1296 fullscreen ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; 1297 e.xclient.data.l[1] = _NET_WM_STATE_FULLSCREEN; 1298 e.xclient.data.l[3] = 0l; 1299 1300 X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0, 1301 SubstructureNotifyMask | SubstructureRedirectMask, &e); 1302 1303 /* Fullscreen windows sometimes end up being marked maximized by 1304 window managers. Force it back to how we expect it to be. */ 1305 if (!fullscreen && ((window->flags & SDL_WINDOW_MAXIMIZED) == 0)) { 1306 SDL_zero(e); 1307 e.xany.type = ClientMessage; 1308 e.xclient.message_type = _NET_WM_STATE; 1309 e.xclient.format = 32; 1310 e.xclient.window = data->xwindow; 1311 e.xclient.data.l[0] = _NET_WM_STATE_REMOVE; 1312 e.xclient.data.l[1] = data->videodata->_NET_WM_STATE_MAXIMIZED_VERT; 1313 e.xclient.data.l[2] = data->videodata->_NET_WM_STATE_MAXIMIZED_HORZ; 1314 e.xclient.data.l[3] = 0l; 1315 X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0, 1316 SubstructureNotifyMask | SubstructureRedirectMask, &e); 1317 } 1318 } else { 1319 Uint32 flags; 1320 1321 flags = window->flags; 1322 if (fullscreen) { 1323 flags |= SDL_WINDOW_FULLSCREEN; 1324 } else { 1325 flags &= ~SDL_WINDOW_FULLSCREEN; 1326 } 1327 X11_SetNetWMState(_this, data->xwindow, flags); 1328 } 1329 1330 if (data->visual->class == DirectColor) { 1331 if ( fullscreen ) { 1332 X11_XInstallColormap(display, data->colormap); 1333 } else { 1334 X11_XUninstallColormap(display, data->colormap); 1335 } 1336 } 1337 1338 X11_XFlush(display); 1339 } 1340 1341 /* This handles fullscreen itself, outside the Window Manager. */ 1342 static void 1343 X11_BeginWindowFullscreenLegacy(_THIS, SDL_Window * window, SDL_VideoDisplay * _display) 1344 { 1345 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 1346 SDL_DisplayData *displaydata = (SDL_DisplayData *) _display->driverdata; 1347 Visual *visual = data->visual; 1348 Display *display = data->videodata->display; 1349 const int screen = displaydata->screen; 1350 Window root = RootWindow(display, screen); 1351 const int def_vis = (visual == DefaultVisual(display, screen)); 1352 unsigned long xattrmask = 0; 1353 XSetWindowAttributes xattr; 1354 XEvent ev; 1355 SDL_Rect rect; 1356 1357 if ( data->fswindow ) { 1358 return; /* already fullscreen, I hope. */ 1359 } 1360 1361 X11_GetDisplayBounds(_this, _display, &rect); 1362 1363 SDL_zero(xattr); 1364 xattr.override_redirect = True; 1365 xattrmask |= CWOverrideRedirect; 1366 xattr.background_pixel = def_vis ? BlackPixel(display, screen) : 0; 1367 xattrmask |= CWBackPixel; 1368 xattr.border_pixel = 0; 1369 xattrmask |= CWBorderPixel; 1370 xattr.colormap = data->colormap; 1371 xattrmask |= CWColormap; 1372 1373 data->fswindow = X11_XCreateWindow(display, root, 1374 rect.x, rect.y, rect.w, rect.h, 0, 1375 displaydata->depth, InputOutput, 1376 visual, xattrmask, &xattr); 1377 1378 X11_XSelectInput(display, data->fswindow, StructureNotifyMask); 1379 X11_XSetWindowBackground(display, data->fswindow, 0); 1380 X11_XInstallColormap(display, data->colormap); 1381 X11_XClearWindow(display, data->fswindow); 1382 X11_XMapRaised(display, data->fswindow); 1383 1384 /* Make sure the fswindow is in view by warping mouse to the corner */ 1385 X11_XUngrabPointer(display, CurrentTime); 1386 X11_XWarpPointer(display, None, root, 0, 0, 0, 0, rect.x, rect.y); 1387 1388 /* Wait to be mapped, filter Unmap event out if it arrives. */ 1389 X11_XIfEvent(display, &ev, &isMapNotify, (XPointer)&data->fswindow); 1390 X11_XCheckIfEvent(display, &ev, &isUnmapNotify, (XPointer)&data->fswindow); 1391 1392 #if SDL_VIDEO_DRIVER_X11_XVIDMODE 1393 if ( displaydata->use_vidmode ) { 1394 X11_XF86VidModeLockModeSwitch(display, screen, True); 1395 } 1396 #endif 1397 1398 SetWindowBordered(display, displaydata->screen, data->xwindow, SDL_FALSE); 1399 1400 /* Center actual window within our cover-the-screen window. */ 1401 X11_XReparentWindow(display, data->xwindow, data->fswindow, 1402 (rect.w - window->w) / 2, (rect.h - window->h) / 2); 1403 1404 /* Move the mouse to the upper left to make sure it's on-screen */ 1405 X11_XWarpPointer(display, None, root, 0, 0, 0, 0, rect.x, rect.y); 1406 1407 /* Center mouse in the fullscreen window. */ 1408 rect.x += (rect.w / 2); 1409 rect.y += (rect.h / 2); 1410 X11_XWarpPointer(display, None, root, 0, 0, 0, 0, rect.x, rect.y); 1411 1412 /* Wait to be mapped, filter Unmap event out if it arrives. */ 1413 X11_XIfEvent(display, &ev, &isMapNotify, (XPointer)&data->xwindow); 1414 X11_XCheckIfEvent(display, &ev, &isUnmapNotify, (XPointer)&data->xwindow); 1415 1416 SDL_UpdateWindowGrab(window); 1417 } 1418 1419 static void 1420 X11_EndWindowFullscreenLegacy(_THIS, SDL_Window * window, SDL_VideoDisplay * _display) 1421 { 1422 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 1423 SDL_DisplayData *displaydata = (SDL_DisplayData *) _display->driverdata; 1424 Display *display = data->videodata->display; 1425 const int screen = displaydata->screen; 1426 Window root = RootWindow(display, screen); 1427 Window fswindow = data->fswindow; 1428 XEvent ev; 1429 1430 if (!data->fswindow) { 1431 return; /* already not fullscreen, I hope. */ 1432 } 1433 1434 data->fswindow = None; 1435 1436 #if SDL_VIDEO_DRIVER_X11_VIDMODE 1437 if ( displaydata->use_vidmode ) { 1438 X11_XF86VidModeLockModeSwitch(display, screen, False); 1439 } 1440 #endif 1441 1442 SDL_UpdateWindowGrab(window); 1443 1444 X11_XReparentWindow(display, data->xwindow, root, window->x, window->y); 1445 1446 /* flush these events so they don't confuse normal event handling */ 1447 X11_XSync(display, False); 1448 X11_XCheckIfEvent(display, &ev, &isMapNotify, (XPointer)&data->xwindow); 1449 X11_XCheckIfEvent(display, &ev, &isUnmapNotify, (XPointer)&data->xwindow); 1450 1451 SetWindowBordered(display, screen, data->xwindow, 1452 (window->flags & SDL_WINDOW_BORDERLESS) == 0); 1453 1454 X11_XWithdrawWindow(display, fswindow, screen); 1455 1456 /* Wait to be unmapped. */ 1457 X11_XIfEvent(display, &ev, &isUnmapNotify, (XPointer)&fswindow); 1458 X11_XDestroyWindow(display, fswindow); 1459 } 1460 1461 1462 void 1463 X11_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * _display, SDL_bool fullscreen) 1464 { 1465 /* !!! FIXME: SDL_Hint? */ 1466 SDL_bool legacy = SDL_FALSE; 1467 const char *env = SDL_getenv("SDL_VIDEO_X11_LEGACY_FULLSCREEN"); 1468 if (env) { 1469 legacy = SDL_atoi(env); 1470 } else { 1471 SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata; 1472 SDL_DisplayData *displaydata = (SDL_DisplayData *) _display->driverdata; 1473 if ( displaydata->use_vidmode ) { 1474 legacy = SDL_TRUE; /* the new stuff only works with XRandR. */ 1475 } else if ( !videodata->net_wm ) { 1476 legacy = SDL_TRUE; /* The window manager doesn't support it */ 1477 } else { 1478 /* !!! FIXME: look at the window manager name, and blacklist certain ones? */ 1479 /* http://stackoverflow.com/questions/758648/find-the-name-of-the-x-window-manager */ 1480 legacy = SDL_FALSE; /* try the new way. */ 1481 } 1482 } 1483 1484 if (legacy) { 1485 if (fullscreen) { 1486 X11_BeginWindowFullscreenLegacy(_this, window, _display); 1487 } else { 1488 X11_EndWindowFullscreenLegacy(_this, window, _display); 1489 } 1490 } else { 1491 X11_SetWindowFullscreenViaWM(_this, window, _display, fullscreen); 1492 } 1493 } 1494 1495 1496 int 1497 X11_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp) 1498 { 1499 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 1500 Display *display = data->videodata->display; 1501 Visual *visual = data->visual; 1502 Colormap colormap = data->colormap; 1503 XColor *colorcells; 1504 int ncolors; 1505 int rmask, gmask, bmask; 1506 int rshift, gshift, bshift; 1507 int i; 1508 1509 if (visual->class != DirectColor) { 1510 return SDL_SetError("Window doesn't have DirectColor visual"); 1511 } 1512 1513 ncolors = visual->map_entries; 1514 colorcells = SDL_malloc(ncolors * sizeof(XColor)); 1515 if (!colorcells) { 1516 return SDL_OutOfMemory(); 1517 } 1518 1519 rshift = 0; 1520 rmask = visual->red_mask; 1521 while (0 == (rmask & 1)) { 1522 rshift++; 1523 rmask >>= 1; 1524 } 1525 1526 gshift = 0; 1527 gmask = visual->green_mask; 1528 while (0 == (gmask & 1)) { 1529 gshift++; 1530 gmask >>= 1; 1531 } 1532 1533 bshift = 0; 1534 bmask = visual->blue_mask; 1535 while (0 == (bmask & 1)) { 1536 bshift++; 1537 bmask >>= 1; 1538 } 1539 1540 /* build the color table pixel values */ 1541 for (i = 0; i < ncolors; i++) { 1542 Uint32 rbits = (rmask * i) / (ncolors - 1); 1543 Uint32 gbits = (gmask * i) / (ncolors - 1); 1544 Uint32 bbits = (bmask * i) / (ncolors - 1); 1545 Uint32 pix = (rbits << rshift) | (gbits << gshift) | (bbits << bshift); 1546 1547 colorcells[i].pixel = pix; 1548 1549 colorcells[i].red = ramp[(0 * 256) + i]; 1550 colorcells[i].green = ramp[(1 * 256) + i]; 1551 colorcells[i].blue = ramp[(2 * 256) + i]; 1552 1553 colorcells[i].flags = DoRed | DoGreen | DoBlue; 1554 } 1555 1556 X11_XStoreColors(display, colormap, colorcells, ncolors); 1557 X11_XFlush(display); 1558 SDL_free(colorcells); 1559 1560 return 0; 1561 } 1562 1563 void 1564 X11_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed) 1565 { 1566 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 1567 Display *display = data->videodata->display; 1568 SDL_bool oldstyle_fullscreen; 1569 SDL_bool grab_keyboard; 1570 1571 /* ICCCM2.0-compliant window managers can handle fullscreen windows 1572 If we're using XVidMode to change resolution we need to confine 1573 the cursor so we don't pan around the virtual desktop. 1574 */ 1575 oldstyle_fullscreen = X11_IsWindowLegacyFullscreen(_this, window); 1576 1577 if (oldstyle_fullscreen || grabbed) { 1578 /* If the window is unmapped, XGrab calls return GrabNotViewable, 1579 so when we get a MapNotify later, we'll try to update the grab as 1580 appropriate. */ 1581 if (window->flags & SDL_WINDOW_HIDDEN) { 1582 return; 1583 } 1584 1585 /* Try to grab the mouse */ 1586 if (!data->videodata->broken_pointer_grab) { 1587 const unsigned int mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask; 1588 int attempts; 1589 int result; 1590 1591 /* Try for up to 5000ms (5s) to grab. If it still fails, stop trying. */ 1592 for (attempts = 0; attempts < 100; attempts++) { 1593 result = X11_XGrabPointer(display, data->xwindow, True, mask, GrabModeAsync, 1594 GrabModeAsync, data->xwindow, None, CurrentTime); 1595 if (result == GrabSuccess) { 1596 break; 1597 } 1598 SDL_Delay(50); 1599 } 1600 1601 if (result != GrabSuccess) { 1602 SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "The X server refused to let us grab the mouse. You might experience input bugs."); 1603 data->videodata->broken_pointer_grab = SDL_TRUE; /* don't try again. */ 1604 } 1605 } 1606 1607 /* Raise the window if we grab the mouse */ 1608 X11_XRaiseWindow(display, data->xwindow); 1609 1610 /* Now grab the keyboard */ 1611 if (SDL_GetHintBoolean(SDL_HINT_GRAB_KEYBOARD, SDL_FALSE)) { 1612 grab_keyboard = SDL_TRUE; 1613 } else { 1614 /* We need to do this with the old style override_redirect 1615 fullscreen window otherwise we won't get keyboard focus. 1616 */ 1617 grab_keyboard = oldstyle_fullscreen; 1618 } 1619 if (grab_keyboard) { 1620 X11_XGrabKeyboard(display, data->xwindow, True, GrabModeAsync, 1621 GrabModeAsync, CurrentTime); 1622 } 1623 } else { 1624 X11_XUngrabPointer(display, CurrentTime); 1625 X11_XUngrabKeyboard(display, CurrentTime); 1626 } 1627 X11_XSync(display, False); 1628 } 1629 1630 void 1631 X11_DestroyWindow(_THIS, SDL_Window * window) 1632 { 1633 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 1634 1635 if (data) { 1636 SDL_VideoData *videodata = (SDL_VideoData *) data->videodata; 1637 Display *display = videodata->display; 1638 int numwindows = videodata->numwindows; 1639 SDL_WindowData **windowlist = videodata->windowlist; 1640 int i; 1641 1642 if (windowlist) { 1643 for (i = 0; i < numwindows; ++i) { 1644 if (windowlist[i] && (windowlist[i]->window == window)) { 1645 windowlist[i] = windowlist[numwindows - 1]; 1646 windowlist[numwindows - 1] = NULL; 1647 videodata->numwindows--; 1648 break; 1649 } 1650 } 1651 } 1652 #ifdef X_HAVE_UTF8_STRING 1653 if (data->ic) { 1654 X11_XDestroyIC(data->ic); 1655 } 1656 #endif 1657 if (data->created) { 1658 X11_XDestroyWindow(display, data->xwindow); 1659 X11_XFlush(display); 1660 } 1661 SDL_free(data); 1662 } 1663 window->driverdata = NULL; 1664 } 1665 1666 SDL_bool 1667 X11_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info) 1668 { 1669 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 1670 Display *display; 1671 1672 if (!data) { 1673 /* This sometimes happens in SDL_IBus_UpdateTextRect() while creating the window */ 1674 SDL_SetError("Window not initialized"); 1675 return SDL_FALSE; 1676 } 1677 1678 display = data->videodata->display; 1679 1680 if (info->version.major == SDL_MAJOR_VERSION && 1681 info->version.minor == SDL_MINOR_VERSION) { 1682 info->subsystem = SDL_SYSWM_X11; 1683 info->info.x11.display = display; 1684 info->info.x11.window = data->xwindow; 1685 return SDL_TRUE; 1686 } else { 1687 SDL_SetError("Application not compiled with SDL %d.%d", 1688 SDL_MAJOR_VERSION, SDL_MINOR_VERSION); 1689 return SDL_FALSE; 1690 } 1691 } 1692 1693 int 1694 X11_SetWindowHitTest(SDL_Window *window, SDL_bool enabled) 1695 { 1696 return 0; /* just succeed, the real work is done elsewhere. */ 1697 } 1698 1699 void 1700 X11_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept) 1701 { 1702 SDL_WindowData *data = (SDL_WindowData *) window->driverdata; 1703 Display *display = data->videodata->display; 1704 Atom XdndAware = X11_XInternAtom(display, "XdndAware", False); 1705 1706 if (accept) { 1707 Atom xdnd_version = 5; 1708 X11_XChangeProperty(display, data->xwindow, XdndAware, XA_ATOM, 32, 1709 PropModeReplace, (unsigned char*)&xdnd_version, 1); 1710 } else { 1711 X11_XDeleteProperty(display, data->xwindow, XdndAware); 1712 } 1713 } 1714 1715 #endif /* SDL_VIDEO_DRIVER_X11 */ 1716 1717 /* vi: set ts=4 sw=4 expandtab: */