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 | |
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 = 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_MOUSE_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 | (_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_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 | |
1619 | void |
1620 | X11_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 | |
1641 | void |
1642 | X11_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 | |
1677 | SDL_bool |
1678 | X11_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 | |
1704 | int |
1705 | X11_SetWindowHitTest(SDL_Window *window, SDL_bool enabled) |
1706 | { |
1707 | return 0; /* just succeed, the real work is done elsewhere. */ |
1708 | } |
1709 | |
1710 | void |
1711 | X11_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 | |