1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2021 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
46static Bool isMapNotify(Display *dpy, XEvent *ev, XPointer win)
47{
48 return ev->type == MapNotify && ev->xmap.window == *((Window*)win);
49}
50static Bool isUnmapNotify(Display *dpy, XEvent *ev, XPointer win)
51{
52 return ev->type == UnmapNotify && ev->xunmap.window == *((Window*)win);
53}
54
55/*
56static Bool isConfigureNotify(Display *dpy, XEvent *ev, XPointer win)
57{
58 return ev->type == ConfigureNotify && ev->xconfigure.window == *((Window*)win);
59}
60static Bool
61X11_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
74static SDL_bool
75X11_IsWindowLegacyFullscreen(_THIS, SDL_Window * window)
76{
77 SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
78 return (data->fswindow != 0);
79}
80
81static SDL_bool
82X11_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
97static SDL_bool
98X11_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
125void
126X11_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
180Uint32
181X11_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
248static int
249SetupWindowData(_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_MOUSE_GRABBED) {
329 /* Tell x11 to clip mouse */
330 }
331 }
332
333 /* All done! */
334 window->driverdata = data;
335 return 0;
336}
337
338static void
339SetWindowBordered(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
369int
370X11_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
676int
677X11_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
689char *
690X11_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
720void
721X11_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
761void
762X11_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
801void
802X11_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
848void
849X11_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
877void
878X11_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
906void
907X11_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
985int
986X11_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
998int
999X11_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
1017int
1018X11_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
1027int
1028X11_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
1040void
1041X11_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
1072void
1073X11_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
1110void
1111X11_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
1134void
1135X11_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
1151static void
1152SetWindowActive(_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
1181void
1182X11_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
1192static void
1193SetWindowMaximized(_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
1231void
1232X11_MaximizeWindow(_THIS, SDL_Window * window)
1233{
1234 SetWindowMaximized(_this, window, SDL_TRUE);
1235}
1236
1237void
1238X11_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
1249void
1250X11_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. */
1258static void
1259X11_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. */
1342static void
1343X11_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
1419static void
1420X11_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
1462void
1463X11_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
1496int
1497X11_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
1563void
1564X11_SetWindowMouseGrab(_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
1570 /* ICCCM2.0-compliant window managers can handle fullscreen windows
1571 If we're using XVidMode to change resolution we need to confine
1572 the cursor so we don't pan around the virtual desktop.
1573 */
1574 oldstyle_fullscreen = X11_IsWindowLegacyFullscreen(_this, window);
1575
1576 if (oldstyle_fullscreen || grabbed) {
1577 /* If the window is unmapped, XGrab calls return GrabNotViewable,
1578 so when we get a MapNotify later, we'll try to update the grab as
1579 appropriate. */
1580 if (window->flags & SDL_WINDOW_HIDDEN) {
1581 return;
1582 }
1583
1584 /* Try to grab the mouse */
1585 if (!data->videodata->broken_pointer_grab) {
1586 const unsigned int mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask;
1587 int attempts;
1588 int result;
1589
1590 /* Try for up to 5000ms (5s) to grab. If it still fails, stop trying. */
1591 for (attempts = 0; attempts < 100; attempts++) {
1592 result = X11_XGrabPointer(display, data->xwindow, True, mask, GrabModeAsync,
1593 GrabModeAsync, data->xwindow, None, CurrentTime);
1594 if (result == GrabSuccess) {
1595 break;
1596 }
1597 SDL_Delay(50);
1598 }
1599
1600 if (result != GrabSuccess) {
1601 SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "The X server refused to let us grab the mouse. You might experience input bugs.");
1602 data->videodata->broken_pointer_grab = SDL_TRUE; /* don't try again. */
1603 }
1604 }
1605
1606 /* Raise the window if we grab the mouse */
1607 X11_XRaiseWindow(display, data->xwindow);
1608
1609 /* Now grab the keyboard on old-style fullscreen */
1610 if (oldstyle_fullscreen) {
1611 X11_SetWindowKeyboardGrab(_this, window, SDL_TRUE);
1612 }
1613 } else {
1614 X11_XUngrabPointer(display, CurrentTime);
1615 }
1616 X11_XSync(display, False);
1617}
1618
1619void
1620X11_SetWindowKeyboardGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
1621{
1622 SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1623 Display *display = data->videodata->display;
1624
1625 if (grabbed) {
1626 /* If the window is unmapped, XGrab calls return GrabNotViewable,
1627 so when we get a MapNotify later, we'll try to update the grab as
1628 appropriate. */
1629 if (window->flags & SDL_WINDOW_HIDDEN) {
1630 return;
1631 }
1632
1633 X11_XGrabKeyboard(display, data->xwindow, True, GrabModeAsync,
1634 GrabModeAsync, CurrentTime);
1635 } else {
1636 X11_XUngrabKeyboard(display, CurrentTime);
1637 }
1638 X11_XSync(display, False);
1639}
1640
1641void
1642X11_DestroyWindow(_THIS, SDL_Window * window)
1643{
1644 SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1645
1646 if (data) {
1647 SDL_VideoData *videodata = (SDL_VideoData *) data->videodata;
1648 Display *display = videodata->display;
1649 int numwindows = videodata->numwindows;
1650 SDL_WindowData **windowlist = videodata->windowlist;
1651 int i;
1652
1653 if (windowlist) {
1654 for (i = 0; i < numwindows; ++i) {
1655 if (windowlist[i] && (windowlist[i]->window == window)) {
1656 windowlist[i] = windowlist[numwindows - 1];
1657 windowlist[numwindows - 1] = NULL;
1658 videodata->numwindows--;
1659 break;
1660 }
1661 }
1662 }
1663#ifdef X_HAVE_UTF8_STRING
1664 if (data->ic) {
1665 X11_XDestroyIC(data->ic);
1666 }
1667#endif
1668 if (data->created) {
1669 X11_XDestroyWindow(display, data->xwindow);
1670 X11_XFlush(display);
1671 }
1672 SDL_free(data);
1673 }
1674 window->driverdata = NULL;
1675}
1676
1677SDL_bool
1678X11_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
1679{
1680 SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1681 Display *display;
1682
1683 if (!data) {
1684 /* This sometimes happens in SDL_IBus_UpdateTextRect() while creating the window */
1685 SDL_SetError("Window not initialized");
1686 return SDL_FALSE;
1687 }
1688
1689 display = data->videodata->display;
1690
1691 if (info->version.major == SDL_MAJOR_VERSION &&
1692 info->version.minor == SDL_MINOR_VERSION) {
1693 info->subsystem = SDL_SYSWM_X11;
1694 info->info.x11.display = display;
1695 info->info.x11.window = data->xwindow;
1696 return SDL_TRUE;
1697 } else {
1698 SDL_SetError("Application not compiled with SDL %d.%d",
1699 SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
1700 return SDL_FALSE;
1701 }
1702}
1703
1704int
1705X11_SetWindowHitTest(SDL_Window *window, SDL_bool enabled)
1706{
1707 return 0; /* just succeed, the real work is done elsewhere. */
1708}
1709
1710void
1711X11_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept)
1712{
1713 SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
1714 Display *display = data->videodata->display;
1715 Atom XdndAware = X11_XInternAtom(display, "XdndAware", False);
1716
1717 if (accept) {
1718 Atom xdnd_version = 5;
1719 X11_XChangeProperty(display, data->xwindow, XdndAware, XA_ATOM, 32,
1720 PropModeReplace, (unsigned char*)&xdnd_version, 1);
1721 } else {
1722 X11_XDeleteProperty(display, data->xwindow, XdndAware);
1723 }
1724}
1725
1726#endif /* SDL_VIDEO_DRIVER_X11 */
1727
1728/* vi: set ts=4 sw=4 expandtab: */
1729