1 #include "allegro5/allegro.h"
2 #include "allegro5/allegro_opengl.h"
3 #include "allegro5/internal/aintern_bitmap.h"
4 #include "allegro5/internal/aintern_opengl.h"
5 #include "allegro5/internal/aintern_x.h"
6 #include "allegro5/internal/aintern_xcursor.h"
7 #include "allegro5/internal/aintern_xclipboard.h"
8 #include "allegro5/internal/aintern_xdisplay.h"
9 #include "allegro5/internal/aintern_xfullscreen.h"
10 #include "allegro5/internal/aintern_xglx_config.h"
11 #include "allegro5/internal/aintern_xsystem.h"
12 #include "allegro5/internal/aintern_xtouch.h"
13 #include "allegro5/internal/aintern_xwindow.h"
14 #include "allegro5/platform/aintxglx.h"
15
16 #include <X11/Xatom.h>
17 #ifdef ALLEGRO_XWINDOWS_WITH_XINPUT2
18 #include <X11/extensions/XInput2.h>
19 #endif
20
21 #ifdef ALLEGRO_XWINDOWS_WITH_XPM
22 #include <X11/xpm.h>
23 #endif
24
25 #include "xicon.h"
26
27 ALLEGRO_DEBUG_CHANNEL("display")
28
29 static ALLEGRO_DISPLAY_INTERFACE xdpy_vt;
30 static const ALLEGRO_XWIN_DISPLAY_OVERRIDABLE_INTERFACE default_overridable_vt;
31 static const ALLEGRO_XWIN_DISPLAY_OVERRIDABLE_INTERFACE *gtk_override_vt = NULL;
32
33 static void xdpy_destroy_display(ALLEGRO_DISPLAY *d);
34 static bool xdpy_acknowledge_resize(ALLEGRO_DISPLAY *d);
35
36
37 /* XXX where does this belong? */
_al_xglx_use_adapter(ALLEGRO_SYSTEM_XGLX * s,int adapter)38 static void _al_xglx_use_adapter(ALLEGRO_SYSTEM_XGLX *s, int adapter)
39 {
40 ALLEGRO_DEBUG("use adapter %i\n", adapter);
41 s->adapter_use_count++;
42 s->adapter_map[adapter]++;
43 }
44
45
_al_xglx_unuse_adapter(ALLEGRO_SYSTEM_XGLX * s,int adapter)46 static void _al_xglx_unuse_adapter(ALLEGRO_SYSTEM_XGLX *s, int adapter)
47 {
48 ALLEGRO_DEBUG("unuse adapter %i\n", adapter);
49 s->adapter_use_count--;
50 s->adapter_map[adapter]--;
51 }
52
53
check_adapter_use_count(ALLEGRO_SYSTEM_XGLX * system)54 static bool check_adapter_use_count(ALLEGRO_SYSTEM_XGLX *system)
55 {
56 /* If we're in multi-head X mode, bail out if we try to use more than one
57 * display as there are bugs in X/glX that cause us to hang in X if we try
58 * to use more than one.
59 * If we're in real xinerama mode, also bail out, X makes mouse use evil.
60 */
61 bool true_xinerama_active = false;
62 #ifdef ALLEGRO_XWINDOWS_WITH_XINERAMA
63 bool xrandr_active = false;
64 #ifdef ALLEGRO_XWINDOWS_WITH_XRANDR
65 xrandr_active = system->xrandr_available;
66 #endif
67 true_xinerama_active = !xrandr_active && system->xinerama_available;
68 #endif
69
70 if ((true_xinerama_active || ScreenCount(system->x11display) > 1)
71 && system->adapter_use_count > 0)
72 {
73 int i, adapter_use_count = 0;
74
75 /* XXX magic constant */
76 for (i = 0; i < 32; i++) {
77 if (system->adapter_map[i])
78 adapter_use_count++;
79 }
80
81 if (adapter_use_count > 1) {
82 ALLEGRO_ERROR("Use of more than one adapter at once in "
83 "multi-head X or X with true Xinerama active is not possible.\n");
84 return false;
85 }
86 }
87
88 return true;
89 }
90
91
query_glx_version(ALLEGRO_SYSTEM_XGLX * system)92 static int query_glx_version(ALLEGRO_SYSTEM_XGLX *system)
93 {
94 int major, minor;
95 int version;
96
97 glXQueryVersion(system->x11display, &major, &minor);
98 version = major * 100 + minor * 10;
99 ALLEGRO_INFO("GLX %.1f.\n", version / 100.f);
100 return version;
101 }
102
103
xdpy_swap_control(ALLEGRO_DISPLAY * display,int vsync_setting)104 static int xdpy_swap_control(ALLEGRO_DISPLAY *display, int vsync_setting)
105 {
106 /* We set the swap interval to 0 if vsync is forced off, and to 1
107 * if it is forced on.
108 * http://www.opengl.org/registry/specs/SGI/swap_control.txt
109 * If the option is set to 0, we simply use the system default. The
110 * above extension specifies vsync on as default though, so in the
111 * end with GLX we can't force vsync on, just off.
112 */
113 ALLEGRO_DEBUG("requested vsync=%d.\n", vsync_setting);
114
115 if (vsync_setting) {
116 if (display->ogl_extras->extension_list->ALLEGRO_GLX_SGI_swap_control) {
117 int x = (vsync_setting == 2) ? 0 : 1;
118 if (glXSwapIntervalSGI(x)) {
119 ALLEGRO_WARN("glXSwapIntervalSGI(%d) failed.\n", x);
120 }
121 }
122 else {
123 ALLEGRO_WARN("no vsync, GLX_SGI_swap_control missing.\n");
124 /* According to the specification that means it's on, but
125 * the driver might have disabled it. So we do not know.
126 */
127 vsync_setting = 0;
128 }
129 }
130
131 return vsync_setting;
132 }
133
should_bypass_compositor(int flags)134 static bool should_bypass_compositor(int flags)
135 {
136 const char* value = al_get_config_value(al_get_system_config(), "x11", "bypass_compositor");
137 if (value && strcmp(value, "always") == 0) {
138 return true;
139 }
140 if (value && strcmp(value, "never") == 0) {
141 return false;
142 }
143 // default to "fullscreen_only"
144 return (flags & ALLEGRO_FULLSCREEN) || (flags & ALLEGRO_FULLSCREEN_WINDOW);
145 }
146
set_compositor_bypass_flag(ALLEGRO_DISPLAY * display)147 static void set_compositor_bypass_flag(ALLEGRO_DISPLAY *display)
148 {
149 ALLEGRO_SYSTEM_XGLX *system = (ALLEGRO_SYSTEM_XGLX *)al_get_system_driver();
150 ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)display;
151 const long _NET_WM_BYPASS_COMPOSITOR_HINT_ON = should_bypass_compositor(display->flags);
152 Atom _NET_WM_BYPASS_COMPOSITOR;
153
154 _NET_WM_BYPASS_COMPOSITOR = XInternAtom(system->x11display,
155 "_NET_WM_BYPASS_COMPOSITOR",
156 False);
157 XChangeProperty(system->x11display, glx->window, _NET_WM_BYPASS_COMPOSITOR,
158 XA_CARDINAL, 32, PropModeReplace,
159 (unsigned char *)&_NET_WM_BYPASS_COMPOSITOR_HINT_ON, 1);
160 }
161
162
xdpy_create_display_window(ALLEGRO_SYSTEM_XGLX * system,ALLEGRO_DISPLAY_XGLX * d,int w,int h,int adapter)163 static bool xdpy_create_display_window(ALLEGRO_SYSTEM_XGLX *system,
164 ALLEGRO_DISPLAY_XGLX *d, int w, int h, int adapter)
165 {
166 ALLEGRO_DISPLAY *display = (ALLEGRO_DISPLAY *)d;
167
168 /* Create a colormap. */
169 Colormap cmap = XCreateColormap(system->x11display,
170 RootWindow(system->x11display, d->xvinfo->screen),
171 d->xvinfo->visual, AllocNone);
172
173 /* Create an X11 window */
174 XSetWindowAttributes swa;
175 int mask = CWBorderPixel | CWColormap | CWEventMask;
176 swa.colormap = cmap;
177 swa.border_pixel = 0;
178 swa.event_mask =
179 KeyPressMask |
180 KeyReleaseMask |
181 StructureNotifyMask |
182 EnterWindowMask |
183 LeaveWindowMask |
184 FocusChangeMask |
185 ExposureMask |
186 PropertyChangeMask |
187 ButtonPressMask |
188 ButtonReleaseMask |
189 PointerMotionMask;
190
191 /* For a non-compositing window manager, a black background can look
192 * less broken if the application doesn't react to expose events fast
193 * enough. However in some cases like resizing, the black background
194 * causes horrible flicker.
195 */
196 if (!(display->flags & ALLEGRO_RESIZABLE)) {
197 mask |= CWBackPixel;
198 swa.background_pixel = BlackPixel(system->x11display, d->xvinfo->screen);
199 }
200
201 int x_off = INT_MAX;
202 int y_off = INT_MAX;
203 if (display->flags & ALLEGRO_FULLSCREEN) {
204 _al_xglx_get_display_offset(system, d->adapter, &x_off, &y_off);
205 }
206 else {
207 /* We want new_display_adapter's offset to add to the
208 * new_window_position.
209 */
210 int xscr_x = 0;
211 int xscr_y = 0;
212 al_get_new_window_position(&x_off, &y_off);
213
214 if (adapter >= 0) {
215 /* Non default adapter. I'm assuming this means the user wants the
216 * window to be placed on the adapter offset by new display pos.
217 */
218 _al_xglx_get_display_offset(system, d->adapter, &xscr_x, &xscr_y);
219 if (x_off != INT_MAX)
220 x_off += xscr_x;
221 if (y_off != INT_MAX)
222 y_off += xscr_y;
223 }
224 }
225
226 d->window = XCreateWindow(system->x11display,
227 RootWindow(system->x11display, d->xvinfo->screen),
228 x_off != INT_MAX ? x_off : 0,
229 y_off != INT_MAX ? y_off : 0,
230 w, h, 0, d->xvinfo->depth,
231 InputOutput, d->xvinfo->visual, mask, &swa);
232
233 ALLEGRO_DEBUG("Window ID: %ld\n", (long)d->window);
234
235 /* Try to set full screen mode if requested, fail if we can't. */
236 if (display->flags & ALLEGRO_FULLSCREEN) {
237 /* According to the spec, the window manager is supposed to disable
238 * window decorations when _NET_WM_STATE_FULLSCREEN is in effect.
239 * However, some WMs may not be fully compliant, e.g. Fluxbox.
240 */
241 _al_xwin_set_frame(display, false);
242 _al_xwin_set_above(display, 1);
243 if (!_al_xglx_fullscreen_set_mode(system, d, w, h, 0,
244 display->refresh_rate)) {
245 ALLEGRO_DEBUG("xdpy: failed to set fullscreen mode.\n");
246 return false;
247 }
248 }
249
250 if (display->flags & ALLEGRO_FRAMELESS) {
251 _al_xwin_set_frame(display, false);
252 }
253
254 ALLEGRO_DEBUG("X11 window created.\n");
255
256 /* Set the PID related to the window. */
257 Atom _NET_WM_PID = XInternAtom(system->x11display, "_NET_WM_PID", False);
258 int pid = getpid();
259 XChangeProperty(system->x11display, d->window, _NET_WM_PID, XA_CARDINAL,
260 32, PropModeReplace, (unsigned char *)&pid, 1);
261
262 _al_xwin_set_size_hints(display, x_off, y_off);
263
264 /* Let the window manager know we're a "normal" window */
265 Atom _NET_WM_WINDOW_TYPE;
266 Atom _NET_WM_WINDOW_TYPE_NORMAL;
267
268 _NET_WM_WINDOW_TYPE = XInternAtom(system->x11display, "_NET_WM_WINDOW_TYPE",
269 False);
270 _NET_WM_WINDOW_TYPE_NORMAL = XInternAtom(system->x11display,
271 "_NET_WM_WINDOW_TYPE_NORMAL",
272 False);
273 XChangeProperty(system->x11display, d->window, _NET_WM_WINDOW_TYPE, XA_ATOM,
274 32, PropModeReplace,
275 (unsigned char *)&_NET_WM_WINDOW_TYPE_NORMAL, 1);
276
277 /* This seems like a good idea */
278 set_compositor_bypass_flag(display);
279
280 #ifdef ALLEGRO_XWINDOWS_WITH_XINPUT2
281 /* listen for touchscreen events */
282 XIEventMask event_mask;
283 event_mask.deviceid = XIAllMasterDevices;
284 event_mask.mask_len = XIMaskLen(XI_TouchEnd);
285 event_mask.mask = (unsigned char*)al_calloc(3, sizeof(char));
286 XISetMask(event_mask.mask, XI_TouchBegin);
287 XISetMask(event_mask.mask, XI_TouchUpdate);
288 XISetMask(event_mask.mask, XI_TouchEnd);
289
290 XISelectEvents(system->x11display, d->window, &event_mask, 1);
291
292 al_free(event_mask.mask);
293 #endif
294
295 return true;
296 }
297
298
xdpy_create_display_locked(ALLEGRO_SYSTEM_XGLX * system,int flags,int w,int h,int adapter)299 static ALLEGRO_DISPLAY_XGLX *xdpy_create_display_locked(
300 ALLEGRO_SYSTEM_XGLX *system, int flags, int w, int h, int adapter)
301 {
302 ALLEGRO_DISPLAY_XGLX *d = al_calloc(1, sizeof *d);
303 ALLEGRO_DISPLAY *display = (ALLEGRO_DISPLAY *)d;
304 ALLEGRO_OGL_EXTRAS *ogl = al_calloc(1, sizeof *ogl);
305 display->ogl_extras = ogl;
306
307 d->glx_version = query_glx_version(system);
308
309 display->w = w;
310 display->h = h;
311 display->vt = _al_display_xglx_driver();
312 display->refresh_rate = al_get_new_display_refresh_rate();
313 display->flags = flags;
314 // FIXME: default? Is this the right place to set this?
315 display->flags |= ALLEGRO_OPENGL;
316 #ifdef ALLEGRO_CFG_OPENGLES2
317 display->flags |= ALLEGRO_PROGRAMMABLE_PIPELINE;
318 #endif
319 #ifdef ALLEGRO_CFG_OPENGLES
320 display->flags |= ALLEGRO_OPENGL_ES_PROFILE;
321 #endif
322
323 /* Store our initial virtual adapter, used by fullscreen and positioning
324 * code.
325 */
326 ALLEGRO_DEBUG("selected adapter %i\n", adapter);
327 if (adapter < 0)
328 d->adapter = _al_xglx_get_default_adapter(system);
329 else
330 d->adapter = adapter;
331
332 ALLEGRO_DEBUG("xdpy: selected adapter %i\n", d->adapter);
333 _al_xglx_use_adapter(system, d->adapter);
334 if (!check_adapter_use_count(system)) {
335 goto EarlyError;
336 }
337
338 /* Store our initial X Screen, used by window creation, fullscreen, and glx
339 * visual code.
340 */
341 d->xscreen = _al_xglx_get_xscreen(system, d->adapter);
342 ALLEGRO_DEBUG("xdpy: selected xscreen %i\n", d->xscreen);
343
344 d->wm_delete_window_atom = None;
345
346 d->is_mapped = false;
347 _al_cond_init(&d->mapped);
348
349 d->is_selectioned = false;
350 _al_cond_init(&d->selectioned);
351
352
353 d->resize_count = 0;
354 d->programmatic_resize = false;
355
356 _al_xglx_config_select_visual(d);
357
358 if (!d->xvinfo) {
359 ALLEGRO_ERROR("FIXME: Need better visual selection.\n");
360 ALLEGRO_ERROR("No matching visual found.\n");
361 goto EarlyError;
362 }
363
364 ALLEGRO_INFO("Selected X11 visual %lu.\n", d->xvinfo->visualid);
365
366 /* Add ourself to the list of displays. */
367 ALLEGRO_DISPLAY_XGLX **add;
368 add = _al_vector_alloc_back(&system->system.displays);
369 *add = d;
370
371 /* Each display is an event source. */
372 _al_event_source_init(&display->es);
373
374 if (!xdpy_create_display_window(system, d, w, h, adapter)) {
375 goto LateError;
376 }
377
378 /* Send any pending requests to the X server.
379 * This is necessary to make the window ID immediately valid
380 * for a GtkSocket.
381 */
382 XSync(system->x11display, False);
383
384 if (display->flags & ALLEGRO_GTK_TOPLEVEL_INTERNAL) {
385 ASSERT(gtk_override_vt);
386 if (!gtk_override_vt->create_display_hook(display, w, h)) {
387 goto LateError;
388 }
389 }
390 else {
391 default_overridable_vt.set_window_title(display, al_get_new_window_title());
392 if (!default_overridable_vt.create_display_hook(display, w, h)) {
393 goto LateError;
394 }
395 }
396
397 /* overridable_vt should be set by the create_display_hook. */
398 ASSERT(d->overridable_vt);
399
400 /* Send any pending requests to the X server. */
401 XSync(system->x11display, False);
402
403 /* To avoid race conditions where some X11 functions fail before the window
404 * is mapped, we wait here until it is mapped. Note that the thread is
405 * locked, so the event could not possibly have been processed yet in the
406 * events thread. So as long as no other map events occur, the condition
407 * should only be signalled when our window gets mapped.
408 */
409 while (!d->is_mapped) {
410 _al_cond_wait(&d->mapped, &system->lock);
411 }
412 /* In tiling WMs, we might get resize events pretty much immediately after
413 * Window creation. This location seems to catch them reliably, tested with
414 * dwm, awesome, xmonad and i3. */
415 if ((display->flags & ALLEGRO_RESIZABLE) && d->resize_count > 0) {
416 xdpy_acknowledge_resize(display);
417 }
418
419 /* We can do this at any time, but if we already have a mapped
420 * window when switching to fullscreen it will use the same
421 * monitor (with the MetaCity version I'm using here right now).
422 */
423 if ((display->flags & ALLEGRO_FULLSCREEN_WINDOW)) {
424 ALLEGRO_INFO("Toggling fullscreen flag for %d x %d window.\n",
425 display->w, display->h);
426 _al_xwin_reset_size_hints(display);
427 _al_xwin_set_fullscreen_window(display, 2);
428 _al_xwin_set_size_hints(display, INT_MAX, INT_MAX);
429
430 XWindowAttributes xwa;
431 XGetWindowAttributes(system->x11display, d->window, &xwa);
432 display->w = xwa.width;
433 display->h = xwa.height;
434 ALLEGRO_INFO("Using ALLEGRO_FULLSCREEN_WINDOW of %d x %d\n",
435 display->w, display->h);
436 }
437
438 if (display->flags & ALLEGRO_FULLSCREEN) {
439 /* kwin wants these here */
440 /* metacity wants these here too */
441 /* XXX compiz is quiky, can't seem to find a combination of hints that
442 * make sure we are layerd over panels, and are positioned properly */
443
444 //_al_xwin_set_fullscreen_window(display, 1);
445 _al_xwin_set_above(display, 1);
446
447 _al_xglx_fullscreen_to_display(system, d);
448
449 /* Grab mouse if we only have one display, ungrab it if we have more than
450 * one.
451 */
452 if (_al_vector_size(&system->system.displays) == 1) {
453 al_grab_mouse(display);
454 }
455 else if (_al_vector_size(&system->system.displays) > 1) {
456 al_ungrab_mouse();
457 }
458 }
459
460 if (flags & ALLEGRO_MAXIMIZED) {
461 _al_xwin_maximize(display, true);
462 }
463
464 if (!_al_xglx_config_create_context(d)) {
465 goto LateError;
466 }
467
468 /* Make our GLX context current for reading and writing in the current
469 * thread.
470 */
471 if (d->fbc) {
472 if (!glXMakeContextCurrent(system->gfxdisplay, d->glxwindow,
473 d->glxwindow, d->context)) {
474 ALLEGRO_ERROR("glXMakeContextCurrent failed\n");
475 }
476 }
477 else {
478 if (!glXMakeCurrent(system->gfxdisplay, d->glxwindow, d->context)) {
479 ALLEGRO_ERROR("glXMakeCurrent failed\n");
480 }
481 }
482
483 _al_ogl_manage_extensions(display);
484 _al_ogl_set_extensions(ogl->extension_api);
485
486 /* Print out OpenGL version info */
487 ALLEGRO_INFO("OpenGL Version: %s\n", (const char*)glGetString(GL_VERSION));
488 ALLEGRO_INFO("Vendor: %s\n", (const char*)glGetString(GL_VENDOR));
489 ALLEGRO_INFO("Renderer: %s\n", (const char*)glGetString(GL_RENDERER));
490
491 /* Fill in opengl version */
492 const int v = display->ogl_extras->ogl_info.version;
493 display->extra_settings.settings[ALLEGRO_OPENGL_MAJOR_VERSION] = (v >> 24) & 0xFF;
494 display->extra_settings.settings[ALLEGRO_OPENGL_MINOR_VERSION] = (v >> 16) & 0xFF;
495
496 if (display->ogl_extras->ogl_info.version < _ALLEGRO_OPENGL_VERSION_1_2) {
497 ALLEGRO_EXTRA_DISPLAY_SETTINGS *eds = _al_get_new_display_settings();
498 if (eds->required & (1<<ALLEGRO_COMPATIBLE_DISPLAY)) {
499 ALLEGRO_ERROR("Allegro requires at least OpenGL version 1.2 to work.\n");
500 goto LateError;
501 }
502 display->extra_settings.settings[ALLEGRO_COMPATIBLE_DISPLAY] = 0;
503 }
504
505 if (display->extra_settings.settings[ALLEGRO_COMPATIBLE_DISPLAY])
506 _al_ogl_setup_gl(display);
507
508 /* vsync */
509 int vsync_setting = _al_get_new_display_settings()->settings[ALLEGRO_VSYNC];
510 vsync_setting = xdpy_swap_control(display, vsync_setting);
511 display->extra_settings.settings[ALLEGRO_VSYNC] = vsync_setting;
512
513 d->invisible_cursor = None; /* Will be created on demand. */
514 d->current_cursor = None; /* Initially, we use the root cursor. */
515 d->cursor_hidden = false;
516
517 d->icon = None;
518 d->icon_mask = None;
519
520 return d;
521
522 EarlyError:
523 al_free(d);
524 al_free(ogl);
525 return NULL;
526
527 LateError:
528 xdpy_destroy_display(display);
529 return NULL;
530 }
531
set_initial_icon(Display * x11display,Window window)532 static void set_initial_icon(Display *x11display, Window window)
533 {
534 #ifdef ALLEGRO_XWINDOWS_WITH_XPM
535 XWMHints *wm_hints;
536
537 if (x11_xpm == NULL)
538 return;
539
540 wm_hints = XAllocWMHints();
541
542 wm_hints->flags |= IconPixmapHint | IconMaskHint;
543 XpmCreatePixmapFromData(x11display, window, x11_xpm,
544 &wm_hints->icon_pixmap, &wm_hints->icon_mask, NULL);
545
546 XSetWMHints(x11display, window, wm_hints);
547 XFree(wm_hints);
548 #else
549 (void)x11display;
550 (void)window;
551 #endif
552 }
553
xdpy_create_display_hook_default(ALLEGRO_DISPLAY * display,int w,int h)554 static bool xdpy_create_display_hook_default(ALLEGRO_DISPLAY *display,
555 int w, int h)
556 {
557 ALLEGRO_SYSTEM_XGLX *system = (ALLEGRO_SYSTEM_XGLX *)al_get_system_driver();
558 ALLEGRO_DISPLAY_XGLX *d = (ALLEGRO_DISPLAY_XGLX *)display;
559 (void)w;
560 (void)h;
561
562 set_initial_icon(system->x11display, d->window);
563
564 XLockDisplay(system->x11display);
565
566 XMapWindow(system->x11display, d->window);
567 ALLEGRO_DEBUG("X11 window mapped.\n");
568
569 d->wm_delete_window_atom = XInternAtom(system->x11display,
570 "WM_DELETE_WINDOW", False);
571 XSetWMProtocols(system->x11display, d->window, &d->wm_delete_window_atom, 1);
572
573 XUnlockDisplay(system->x11display);
574
575 d->overridable_vt = &default_overridable_vt;
576
577 return true;
578 }
579
580
581 /* Create a new X11 display, which maps directly to a GLX window. */
xdpy_create_display(int w,int h)582 static ALLEGRO_DISPLAY *xdpy_create_display(int w, int h)
583 {
584 ALLEGRO_SYSTEM_XGLX *system = (ALLEGRO_SYSTEM_XGLX *)al_get_system_driver();
585 ALLEGRO_DISPLAY_XGLX *display;
586 int flags;
587 int adapter;
588
589 if (system->x11display == NULL) {
590 ALLEGRO_WARN("Not connected to X server.\n");
591 return NULL;
592 }
593
594 if (w <= 0 || h <= 0) {
595 ALLEGRO_ERROR("Invalid window size %dx%d\n", w, h);
596 return NULL;
597 }
598
599 flags = al_get_new_display_flags();
600 if (flags & ALLEGRO_GTK_TOPLEVEL_INTERNAL) {
601 if (gtk_override_vt == NULL) {
602 ALLEGRO_ERROR("GTK requested but unavailable\n");
603 return NULL;
604 }
605 if (flags & ALLEGRO_FULLSCREEN) {
606 ALLEGRO_ERROR("GTK incompatible with fullscreen\n");
607 return NULL;
608 }
609 }
610
611 _al_mutex_lock(&system->lock);
612
613 adapter = al_get_new_display_adapter();
614 display = xdpy_create_display_locked(system, flags, w, h, adapter);
615
616 _al_mutex_unlock(&system->lock);
617
618 return (ALLEGRO_DISPLAY *)display;
619 }
620
621
convert_display_bitmaps_to_memory_bitmap(ALLEGRO_DISPLAY * d)622 static void convert_display_bitmaps_to_memory_bitmap(ALLEGRO_DISPLAY *d)
623 {
624 ALLEGRO_DEBUG("converting display bitmaps to memory bitmaps.\n");
625
626 while (d->bitmaps._size > 0) {
627 ALLEGRO_BITMAP **bptr = _al_vector_ref_back(&d->bitmaps);
628 ALLEGRO_BITMAP *b = *bptr;
629 _al_convert_to_memory_bitmap(b);
630 }
631 }
632
633
transfer_display_bitmaps_to_any_other_display(ALLEGRO_SYSTEM_XGLX * s,ALLEGRO_DISPLAY * d)634 static void transfer_display_bitmaps_to_any_other_display(
635 ALLEGRO_SYSTEM_XGLX *s, ALLEGRO_DISPLAY *d)
636 {
637 size_t i;
638 ALLEGRO_DISPLAY *living = NULL;
639 ASSERT(s->system.displays._size > 1);
640
641 for (i = 0; i < s->system.displays._size; i++) {
642 ALLEGRO_DISPLAY **slot = _al_vector_ref(&s->system.displays, i);
643 living = *slot;
644 if (living != d)
645 break;
646 }
647
648 ALLEGRO_DEBUG("transferring display bitmaps to other display.\n");
649
650 for (i = 0; i < d->bitmaps._size; i++) {
651 ALLEGRO_BITMAP **add = _al_vector_alloc_back(&(living->bitmaps));
652 ALLEGRO_BITMAP **ref = _al_vector_ref(&d->bitmaps, i);
653 *add = *ref;
654 (*add)->_display = living;
655 }
656 }
657
658
restore_mode_if_last_fullscreen_display(ALLEGRO_SYSTEM_XGLX * s,ALLEGRO_DISPLAY_XGLX * d)659 static void restore_mode_if_last_fullscreen_display(ALLEGRO_SYSTEM_XGLX *s,
660 ALLEGRO_DISPLAY_XGLX *d)
661 {
662 bool last_fullscreen = true;
663 size_t i;
664
665 /* If any other fullscreen display is still active on the same adapter,
666 * we must not touch the video mode.
667 */
668 for (i = 0; i < s->system.displays._size; i++) {
669 ALLEGRO_DISPLAY_XGLX **slot = _al_vector_ref(&s->system.displays, i);
670 ALLEGRO_DISPLAY_XGLX *living = *slot;
671
672 if (living == d)
673 continue;
674
675 /* Check for fullscreen displays on the same adapter. */
676 if (living->adapter == d->adapter
677 && (living->display.flags & ALLEGRO_FULLSCREEN)) {
678 last_fullscreen = false;
679 }
680 }
681
682 if (last_fullscreen) {
683 ALLEGRO_DEBUG("restore mode.\n");
684 _al_xglx_restore_video_mode(s, d->adapter);
685 }
686 else {
687 ALLEGRO_DEBUG("*not* restoring mode.\n");
688 }
689 }
690
691
xdpy_destroy_display_hook_default(ALLEGRO_DISPLAY * d,bool is_last)692 static void xdpy_destroy_display_hook_default(ALLEGRO_DISPLAY *d, bool is_last)
693 {
694 ALLEGRO_SYSTEM_XGLX *s = (ALLEGRO_SYSTEM_XGLX *)al_get_system_driver();
695 ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)d;
696 (void)is_last;
697
698 if (glx->context) {
699 glXDestroyContext(s->gfxdisplay, glx->context);
700 glx->context = NULL;
701 ALLEGRO_DEBUG("destroy context.\n");
702 }
703
704 if (glx->fbc) {
705 al_free(glx->fbc);
706 glx->fbc = NULL;
707 XFree(glx->xvinfo);
708 glx->xvinfo = NULL;
709 }
710 else if (glx->xvinfo) {
711 al_free(glx->xvinfo);
712 glx->xvinfo = NULL;
713 }
714
715 if ((glx->glxwindow) && (glx->glxwindow != glx->window)) {
716 glXDestroyWindow(s->x11display, glx->glxwindow);
717 glx->glxwindow = 0;
718 ALLEGRO_DEBUG("destroy glx window\n");
719 }
720
721 _al_cond_destroy(&glx->mapped);
722 _al_cond_destroy(&glx->selectioned);
723
724 ALLEGRO_DEBUG("destroy window.\n");
725 XDestroyWindow(s->x11display, glx->window);
726
727 _al_xglx_unuse_adapter(s, glx->adapter);
728
729 if (d->flags & ALLEGRO_FULLSCREEN) {
730 restore_mode_if_last_fullscreen_display(s, glx);
731 }
732 }
733
734
xdpy_destroy_display(ALLEGRO_DISPLAY * d)735 static void xdpy_destroy_display(ALLEGRO_DISPLAY *d)
736 {
737 ALLEGRO_SYSTEM_XGLX *s = (ALLEGRO_SYSTEM_XGLX *)al_get_system_driver();
738 ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)d;
739 ALLEGRO_OGL_EXTRAS *ogl = d->ogl_extras;
740 bool is_last;
741
742 ALLEGRO_DEBUG("destroying display.\n");
743
744 /* If we're the last display, convert all bitmaps to display independent
745 * (memory) bitmaps. Otherwise, pass all bitmaps to any other living
746 * display. We assume all displays are compatible.)
747 */
748 is_last = (s->system.displays._size == 1);
749 if (is_last)
750 convert_display_bitmaps_to_memory_bitmap(d);
751 else
752 transfer_display_bitmaps_to_any_other_display(s, d);
753
754 _al_ogl_unmanage_extensions(d);
755 ALLEGRO_DEBUG("unmanaged extensions.\n");
756
757 _al_mutex_lock(&s->lock);
758 _al_vector_find_and_delete(&s->system.displays, &d);
759
760 if (ogl->backbuffer) {
761 _al_ogl_destroy_backbuffer(ogl->backbuffer);
762 ogl->backbuffer = NULL;
763 ALLEGRO_DEBUG("destroy backbuffer.\n");
764 }
765
766 if (glx->overridable_vt) {
767 glx->overridable_vt->destroy_display_hook(d, is_last);
768 }
769
770 if (s->mouse_grab_display == d) {
771 s->mouse_grab_display = NULL;
772 }
773
774 _al_vector_free(&d->bitmaps);
775 _al_event_source_free(&d->es);
776
777 al_free(d->ogl_extras);
778 al_free(d->vertex_cache);
779 al_free(d);
780
781 _al_mutex_unlock(&s->lock);
782
783 ALLEGRO_DEBUG("destroy display finished.\n");
784 }
785
786
xdpy_make_current(ALLEGRO_DISPLAY * d)787 static bool xdpy_make_current(ALLEGRO_DISPLAY *d)
788 {
789 ALLEGRO_SYSTEM_XGLX *system = (ALLEGRO_SYSTEM_XGLX *)al_get_system_driver();
790 ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)d;
791
792 /* Make our GLX context current for reading and writing in the current
793 * thread.
794 */
795 if (glx->fbc) {
796 return glXMakeContextCurrent(system->gfxdisplay, glx->glxwindow,
797 glx->glxwindow, glx->context);
798 }
799 else {
800 return glXMakeCurrent(system->gfxdisplay, glx->glxwindow, glx->context);
801 }
802 }
803
804
xdpy_set_current_display(ALLEGRO_DISPLAY * d)805 static bool xdpy_set_current_display(ALLEGRO_DISPLAY *d)
806 {
807 bool rc;
808
809 rc = xdpy_make_current(d);
810 if (rc) {
811 ALLEGRO_OGL_EXTRAS *ogl = d->ogl_extras;
812 _al_ogl_set_extensions(ogl->extension_api);
813 _al_ogl_update_render_state(d);
814 }
815
816 return rc;
817 }
818
819
xdpy_unset_current_display(ALLEGRO_DISPLAY * d)820 static void xdpy_unset_current_display(ALLEGRO_DISPLAY *d)
821 {
822 ALLEGRO_SYSTEM_XGLX *system = (ALLEGRO_SYSTEM_XGLX *)al_get_system_driver();
823 glXMakeContextCurrent(system->gfxdisplay, None, None, NULL);
824 (void)d;
825 }
826
827
xdpy_flip_display(ALLEGRO_DISPLAY * d)828 static void xdpy_flip_display(ALLEGRO_DISPLAY *d)
829 {
830 ALLEGRO_SYSTEM_XGLX *system = (ALLEGRO_SYSTEM_XGLX *)al_get_system_driver();
831 ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)d;
832
833 int e = glGetError();
834 if (e) {
835 ALLEGRO_ERROR("OpenGL error was not 0: %s\n", _al_gl_error_string(e));
836 }
837
838 if (d->extra_settings.settings[ALLEGRO_SINGLE_BUFFER])
839 glFlush();
840 else
841 glXSwapBuffers(system->gfxdisplay, glx->glxwindow);
842 }
843
844
xdpy_update_display_region(ALLEGRO_DISPLAY * d,int x,int y,int w,int h)845 static void xdpy_update_display_region(ALLEGRO_DISPLAY *d, int x, int y,
846 int w, int h)
847 {
848 (void)x;
849 (void)y;
850 (void)w;
851 (void)h;
852 xdpy_flip_display(d);
853 }
854
855
xdpy_acknowledge_resize(ALLEGRO_DISPLAY * d)856 static bool xdpy_acknowledge_resize(ALLEGRO_DISPLAY *d)
857 {
858 ALLEGRO_SYSTEM_XGLX *system = (ALLEGRO_SYSTEM_XGLX *)al_get_system_driver();
859 ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)d;
860 XWindowAttributes xwa;
861 unsigned int w, h;
862
863 _al_mutex_lock(&system->lock);
864
865 /* glXQueryDrawable is GLX 1.3+. */
866 /*
867 glXQueryDrawable(system->x11display, glx->glxwindow, GLX_WIDTH, &w);
868 glXQueryDrawable(system->x11display, glx->glxwindow, GLX_HEIGHT, &h);
869 */
870
871 XGetWindowAttributes(system->x11display, glx->window, &xwa);
872 w = xwa.width;
873 h = xwa.height;
874
875 if ((int)w != d->w || (int)h != d->h) {
876 d->w = w;
877 d->h = h;
878
879 ALLEGRO_DEBUG("xdpy: acknowledge_resize (%d, %d)\n", d->w, d->h);
880
881 /* No context yet means this is a stray call happening during
882 * initialization.
883 */
884 if (glx->context) {
885 _al_ogl_setup_gl(d);
886 }
887
888 _al_xwin_check_maximized(d);
889 }
890
891 _al_mutex_unlock(&system->lock);
892
893 return true;
894 }
895
896
897 /* Note: The system mutex must be locked (exactly once) so when we
898 * wait for the condition variable it gets auto-unlocked. For a
899 * nested lock that would not be the case.
900 */
_al_display_xglx_await_resize(ALLEGRO_DISPLAY * d,int old_resize_count,bool delay_hack)901 void _al_display_xglx_await_resize(ALLEGRO_DISPLAY *d, int old_resize_count,
902 bool delay_hack)
903 {
904 ALLEGRO_SYSTEM_XGLX *system = (void *)al_get_system_driver();
905 ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)d;
906 ALLEGRO_TIMEOUT timeout;
907
908 ALLEGRO_DEBUG("Awaiting resize event\n");
909
910 XSync(system->x11display, False);
911
912 /* Wait until we are actually resized.
913 * Don't wait forever if an event never comes.
914 */
915 al_init_timeout(&timeout, 1.0);
916 while (old_resize_count == glx->resize_count) {
917 if (_al_cond_timedwait(&system->resized, &system->lock, &timeout) == -1) {
918 ALLEGRO_ERROR("Timeout while waiting for resize event.\n");
919 return;
920 }
921 }
922
923 /* XXX: This hack helps when toggling between fullscreen windows and not,
924 * on various window managers.
925 */
926 if (delay_hack) {
927 al_rest(0.2);
928 }
929
930 xdpy_acknowledge_resize(d);
931 }
932
933
xdpy_resize_display_default(ALLEGRO_DISPLAY * d,int w,int h)934 static bool xdpy_resize_display_default(ALLEGRO_DISPLAY *d, int w, int h)
935 {
936 ALLEGRO_SYSTEM_XGLX *system = (ALLEGRO_SYSTEM_XGLX *)al_get_system_driver();
937 ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)d;
938 XWindowAttributes xwa;
939 int attempts;
940 bool ret = false;
941
942 _al_mutex_lock(&system->lock);
943
944 /* It seems some X servers will treat the resize as a no-op if the window is
945 * already the right size, so check for it to avoid a deadlock later.
946 */
947 XGetWindowAttributes(system->x11display, glx->window, &xwa);
948 if (xwa.width == w && xwa.height == h) {
949 _al_mutex_unlock(&system->lock);
950 return false;
951 }
952
953 if (d->flags & ALLEGRO_FULLSCREEN) {
954 _al_xwin_set_fullscreen_window(d, 0);
955 if (!_al_xglx_fullscreen_set_mode(system, glx, w, h, 0, 0)) {
956 ret = false;
957 goto skip_resize;
958 }
959 attempts = 3;
960 }
961 else {
962 attempts = 1;
963 }
964
965 /* Hack: try multiple times to resize the window, with delays. KDE reacts
966 * slowly to the video mode change, and won't resize our window until a
967 * while after. It would be better to wait for some sort of event rather
968 * than just waiting some amount of time, but I didn't manage to find that
969 * event. --pw
970 */
971 for (; attempts >= 0; attempts--) {
972 const int old_resize_count = glx->resize_count;
973 ALLEGRO_DEBUG("calling XResizeWindow, attempts=%d\n", attempts);
974 _al_xwin_reset_size_hints(d);
975 glx->programmatic_resize = true;
976 XResizeWindow(system->x11display, glx->window, w, h);
977 _al_display_xglx_await_resize(d, old_resize_count,
978 (d->flags & ALLEGRO_FULLSCREEN));
979 glx->programmatic_resize = false;
980 _al_xwin_set_size_hints(d, INT_MAX, INT_MAX);
981
982 if (d->w == w && d->h == h) {
983 ret = true;
984 break;
985 }
986
987 /* Wait before trying again. */
988 al_rest(0.333);
989 }
990
991 if (attempts == 0) {
992 ALLEGRO_ERROR("XResizeWindow didn't work; giving up\n");
993 }
994
995 skip_resize:
996
997 if (d->flags & ALLEGRO_FULLSCREEN) {
998 _al_xwin_set_fullscreen_window(d, 1);
999 _al_xwin_set_above(d, 1);
1000 _al_xglx_fullscreen_to_display(system, glx);
1001 ALLEGRO_DEBUG("xdpy: resize fullscreen?\n");
1002 }
1003
1004 _al_mutex_unlock(&system->lock);
1005 return ret;
1006 }
1007
1008
xdpy_resize_display(ALLEGRO_DISPLAY * d,int w,int h)1009 static bool xdpy_resize_display(ALLEGRO_DISPLAY *d, int w, int h)
1010 {
1011 ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)d;
1012
1013 /* A fullscreen-window can't be resized. */
1014 if (d->flags & ALLEGRO_FULLSCREEN_WINDOW)
1015 return false;
1016
1017 return glx->overridable_vt->resize_display(d, w, h);
1018 }
1019
1020
_al_xglx_display_configure(ALLEGRO_DISPLAY * d,int x,int y,int width,int height,bool setglxy)1021 void _al_xglx_display_configure(ALLEGRO_DISPLAY *d, int x, int y,
1022 int width, int height, bool setglxy)
1023 {
1024 ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)d;
1025
1026 ALLEGRO_EVENT_SOURCE *es = &glx->display.es;
1027 _al_event_source_lock(es);
1028
1029 /* Generate a resize event if the size has changed non-programmatically.
1030 * We cannot asynchronously change the display size here yet, since the user
1031 * will only know about a changed size after receiving the resize event.
1032 * Here we merely add the event to the queue.
1033 */
1034 if (!glx->programmatic_resize &&
1035 (d->w != width ||
1036 d->h != height)) {
1037 if (_al_event_source_needs_to_generate_event(es)) {
1038 ALLEGRO_EVENT event;
1039 event.display.type = ALLEGRO_EVENT_DISPLAY_RESIZE;
1040 event.display.timestamp = al_get_time();
1041 event.display.x = x;
1042 event.display.y = y;
1043 event.display.width = width;
1044 event.display.height = height;
1045 _al_event_source_emit_event(es, &event);
1046 }
1047 }
1048
1049 if (setglxy) {
1050 glx->x = x;
1051 glx->y = y;
1052 }
1053
1054 ALLEGRO_SYSTEM_XGLX *system = (ALLEGRO_SYSTEM_XGLX*)al_get_system_driver();
1055 ALLEGRO_MONITOR_INFO mi;
1056 int center_x = (glx->x + (glx->x + width)) / 2;
1057 int center_y = (glx->y + (glx->y + height)) / 2;
1058
1059 _al_xglx_get_monitor_info(system, glx->adapter, &mi);
1060
1061 ALLEGRO_DEBUG("xconfigure event! %ix%i\n", x, y);
1062
1063 /* check if we're no longer inside the stored adapter */
1064 if ((center_x < mi.x1 && center_x > mi.x2) ||
1065 (center_y < mi.y1 && center_y > mi.x2))
1066 {
1067 int new_adapter = _al_xglx_get_adapter(system, glx, true);
1068 if (new_adapter != glx->adapter) {
1069 ALLEGRO_DEBUG("xdpy: adapter change!\n");
1070 _al_xglx_unuse_adapter(system, glx->adapter);
1071 if (d->flags & ALLEGRO_FULLSCREEN)
1072 _al_xglx_restore_video_mode(system, glx->adapter);
1073 glx->adapter = new_adapter;
1074 _al_xglx_use_adapter(system, glx->adapter);
1075 }
1076
1077 }
1078
1079 _al_xwin_check_maximized(d);
1080
1081 _al_event_source_unlock(es);
1082 }
1083
1084
1085 /* Handle an X11 configure event. [X11 thread]
1086 * Only called from the event handler with the system locked.
1087 */
_al_xglx_display_configure_event(ALLEGRO_DISPLAY * d,XEvent * xevent)1088 void _al_xglx_display_configure_event(ALLEGRO_DISPLAY *d, XEvent *xevent)
1089 {
1090 /* We receive two configure events when toggling the window frame.
1091 * We ignore the first one as it has bogus coordinates.
1092 * The only way to tell them apart seems to be the send_event field.
1093 * Unfortunately, we also end up ignoring the only event we receive in
1094 * response to a XMoveWindow request so we have to compensate for that.
1095 */
1096 bool setglxy = (xevent->xconfigure.send_event);
1097 _al_xglx_display_configure(d, xevent->xconfigure.x, xevent->xconfigure.y,
1098 xevent->xconfigure.width, xevent->xconfigure.height, setglxy);
1099 }
1100
1101
1102
1103 /* Handle X11 switch event. [X11 thread]
1104 */
_al_xwin_display_switch_handler(ALLEGRO_DISPLAY * display,XFocusChangeEvent * xevent)1105 void _al_xwin_display_switch_handler(ALLEGRO_DISPLAY *display,
1106 XFocusChangeEvent *xevent)
1107 {
1108 /* Anything but NotifyNormal seem to indicate the switch is not "real".
1109 * TODO: Find out details?
1110 */
1111 if (xevent->mode != NotifyNormal)
1112 return;
1113
1114 _al_xwin_display_switch_handler_inner(display, (xevent->type == FocusIn));
1115 }
1116
1117
1118
1119 /* Handle X11 switch event. [X11 thread]
1120 */
_al_xwin_display_switch_handler_inner(ALLEGRO_DISPLAY * display,bool focus_in)1121 void _al_xwin_display_switch_handler_inner(ALLEGRO_DISPLAY *display, bool focus_in)
1122 {
1123 ALLEGRO_EVENT_SOURCE *es = &display->es;
1124 _al_event_source_lock(es);
1125 if (_al_event_source_needs_to_generate_event(es)) {
1126 ALLEGRO_EVENT event;
1127 if (focus_in)
1128 event.display.type = ALLEGRO_EVENT_DISPLAY_SWITCH_IN;
1129 else
1130 event.display.type = ALLEGRO_EVENT_DISPLAY_SWITCH_OUT;
1131 event.display.timestamp = al_get_time();
1132 _al_event_source_emit_event(es, &event);
1133 }
1134 _al_event_source_unlock(es);
1135 }
1136
1137
1138
_al_xwin_display_expose(ALLEGRO_DISPLAY * display,XExposeEvent * xevent)1139 void _al_xwin_display_expose(ALLEGRO_DISPLAY *display,
1140 XExposeEvent *xevent)
1141 {
1142 ALLEGRO_EVENT_SOURCE *es = &display->es;
1143 _al_event_source_lock(es);
1144 if (_al_event_source_needs_to_generate_event(es)) {
1145 ALLEGRO_EVENT event;
1146 event.display.type = ALLEGRO_EVENT_DISPLAY_EXPOSE;
1147 event.display.timestamp = al_get_time();
1148 event.display.x = xevent->x;
1149 event.display.y = xevent->y;
1150 event.display.width = xevent->width;
1151 event.display.height = xevent->height;
1152 _al_event_source_emit_event(es, &event);
1153 }
1154 _al_event_source_unlock(es);
1155 }
1156
1157
xdpy_is_compatible_bitmap(ALLEGRO_DISPLAY * display,ALLEGRO_BITMAP * bitmap)1158 static bool xdpy_is_compatible_bitmap(ALLEGRO_DISPLAY *display,
1159 ALLEGRO_BITMAP *bitmap)
1160 {
1161 /* All GLX bitmaps are compatible. */
1162 (void)display;
1163 (void)bitmap;
1164 return true;
1165 }
1166
1167
xdpy_set_window_title_default(ALLEGRO_DISPLAY * display,const char * title)1168 static void xdpy_set_window_title_default(ALLEGRO_DISPLAY *display, const char *title)
1169 {
1170 ALLEGRO_SYSTEM_XGLX *system = (ALLEGRO_SYSTEM_XGLX *)al_get_system_driver();
1171 ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)display;
1172
1173 {
1174 Atom WM_NAME = XInternAtom(system->x11display, "WM_NAME", False);
1175 Atom _NET_WM_NAME = XInternAtom(system->x11display, "_NET_WM_NAME", False);
1176 char *list[1] = { (char *) title };
1177 XTextProperty property;
1178
1179 Xutf8TextListToTextProperty(system->x11display, list, 1, XUTF8StringStyle,
1180 &property);
1181 XSetTextProperty(system->x11display, glx->window, &property, WM_NAME);
1182 XSetTextProperty(system->x11display, glx->window, &property, _NET_WM_NAME);
1183 XSetTextProperty(system->x11display, glx->window, &property, XA_WM_NAME);
1184 XFree(property.value);
1185 }
1186 {
1187 XClassHint *hint = XAllocClassHint();
1188 if (hint) {
1189 ALLEGRO_PATH *exepath = al_get_standard_path(ALLEGRO_EXENAME_PATH);
1190 // hint doesn't use a const char*, so we use strdup to create a non const string
1191 hint->res_name = strdup(al_get_path_basename(exepath));
1192 hint->res_class = strdup(al_get_path_basename(exepath));
1193 XSetClassHint(system->x11display, glx->window, hint);
1194 free(hint->res_name);
1195 free(hint->res_class);
1196 XFree(hint);
1197 al_destroy_path(exepath);
1198 }
1199 }
1200 }
1201
1202
xdpy_set_window_title(ALLEGRO_DISPLAY * display,const char * title)1203 static void xdpy_set_window_title(ALLEGRO_DISPLAY *display, const char *title)
1204 {
1205 ALLEGRO_SYSTEM_XGLX *system = (ALLEGRO_SYSTEM_XGLX *)al_get_system_driver();
1206 ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)display;
1207
1208 _al_mutex_lock(&system->lock);
1209 glx->overridable_vt->set_window_title(display, title);
1210 _al_mutex_unlock(&system->lock);
1211 }
1212
1213
xdpy_set_window_position_default(ALLEGRO_DISPLAY * display,int x,int y)1214 static void xdpy_set_window_position_default(ALLEGRO_DISPLAY *display,
1215 int x, int y)
1216 {
1217 ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)display;
1218 ALLEGRO_SYSTEM_XGLX *system = (void *)al_get_system_driver();
1219 Window root, parent, child, *children;
1220 unsigned int n;
1221
1222 _al_mutex_lock(&system->lock);
1223
1224 /* To account for the window border, we have to find the parent window which
1225 * draws the border. If the parent is the root though, then we should not
1226 * translate.
1227 */
1228 XQueryTree(system->x11display, glx->window, &root, &parent, &children, &n);
1229 if (parent != root) {
1230 XTranslateCoordinates(system->x11display, parent, glx->window,
1231 x, y, &x, &y, &child);
1232 }
1233
1234 XMoveWindow(system->x11display, glx->window, x, y);
1235 XFlush(system->x11display);
1236
1237 /* We have to store these immediately, as we will ignore the XConfigureEvent
1238 * that we receive in response. _al_display_xglx_configure() knows why.
1239 */
1240 glx->x = x;
1241 glx->y = y;
1242
1243 _al_mutex_unlock(&system->lock);
1244 }
1245
1246
xdpy_set_window_position(ALLEGRO_DISPLAY * display,int x,int y)1247 static void xdpy_set_window_position(ALLEGRO_DISPLAY *display, int x, int y)
1248 {
1249 ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)display;
1250 glx->overridable_vt->set_window_position(display, x, y);
1251 }
1252
1253
xdpy_get_window_position(ALLEGRO_DISPLAY * display,int * x,int * y)1254 static void xdpy_get_window_position(ALLEGRO_DISPLAY *display, int *x, int *y)
1255 {
1256 ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)display;
1257 /* We could also query the X11 server, but it just would take longer, and
1258 * would not be synchronized to our events. The latter can be an advantage
1259 * or disadvantage.
1260 */
1261 *x = glx->x;
1262 *y = glx->y;
1263 }
1264
1265
xdpy_set_window_constraints_default(ALLEGRO_DISPLAY * display,int min_w,int min_h,int max_w,int max_h)1266 static bool xdpy_set_window_constraints_default(ALLEGRO_DISPLAY *display,
1267 int min_w, int min_h, int max_w, int max_h)
1268 {
1269 ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)display;
1270
1271 glx->display.min_w = min_w;
1272 glx->display.min_h = min_h;
1273 glx->display.max_w = max_w;
1274 glx->display.max_h = max_h;
1275
1276 return true;
1277 }
1278
1279
xdpy_set_window_constraints(ALLEGRO_DISPLAY * display,int min_w,int min_h,int max_w,int max_h)1280 static bool xdpy_set_window_constraints(ALLEGRO_DISPLAY *display,
1281 int min_w, int min_h, int max_w, int max_h)
1282 {
1283 ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)display;
1284 return glx->overridable_vt->set_window_constraints(display,
1285 min_w, min_h, max_w, max_h);
1286 }
1287
1288
xdpy_get_window_constraints(ALLEGRO_DISPLAY * display,int * min_w,int * min_h,int * max_w,int * max_h)1289 static bool xdpy_get_window_constraints(ALLEGRO_DISPLAY *display,
1290 int *min_w, int *min_h, int *max_w, int * max_h)
1291 {
1292 ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)display;
1293
1294 *min_w = glx->display.min_w;
1295 *min_h = glx->display.min_h;
1296 *max_w = glx->display.max_w;
1297 *max_h = glx->display.max_h;
1298
1299 return true;
1300 }
1301
1302
xdpy_apply_window_constraints(ALLEGRO_DISPLAY * display,bool onoff)1303 static void xdpy_apply_window_constraints(ALLEGRO_DISPLAY *display,
1304 bool onoff)
1305 {
1306 int posX;
1307 int posY;
1308 ALLEGRO_SYSTEM_XGLX *system = (ALLEGRO_SYSTEM_XGLX *)al_get_system_driver();
1309
1310 _al_mutex_lock(&system->lock);
1311
1312 if (onoff) {
1313 al_get_window_position(display, &posX, &posY);
1314 _al_xwin_set_size_hints(display, posX, posY);
1315 }
1316 else {
1317 _al_xwin_reset_size_hints(display);
1318 }
1319
1320 _al_mutex_unlock(&system->lock);
1321 al_resize_display(display, display->w, display->h);
1322 }
1323
1324
xdpy_set_fullscreen_window_default(ALLEGRO_DISPLAY * display,bool onoff)1325 static void xdpy_set_fullscreen_window_default(ALLEGRO_DISPLAY *display, bool onoff)
1326 {
1327 ALLEGRO_SYSTEM_XGLX *system = (void *)al_get_system_driver();
1328 if (onoff == !(display->flags & ALLEGRO_FULLSCREEN_WINDOW)) {
1329 _al_mutex_lock(&system->lock);
1330 _al_xwin_reset_size_hints(display);
1331 _al_xwin_set_fullscreen_window(display, 2);
1332 /* XXX Technically, the user may fiddle with the _NET_WM_STATE_FULLSCREEN
1333 * property outside of Allegro so this flag may not be in sync with
1334 * reality.
1335 */
1336 display->flags ^= ALLEGRO_FULLSCREEN_WINDOW;
1337 _al_xwin_set_size_hints(display, INT_MAX, INT_MAX);
1338
1339 set_compositor_bypass_flag(display);
1340 _al_mutex_unlock(&system->lock);
1341 }
1342 }
1343
1344
xdpy_set_fullscreen_window(ALLEGRO_DISPLAY * display,bool onoff)1345 static void xdpy_set_fullscreen_window(ALLEGRO_DISPLAY *display, bool onoff)
1346 {
1347 ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)display;
1348 glx->overridable_vt->set_fullscreen_window(display, onoff);
1349 }
1350
1351
xdpy_set_display_flag(ALLEGRO_DISPLAY * display,int flag,bool flag_onoff)1352 static bool xdpy_set_display_flag(ALLEGRO_DISPLAY *display, int flag,
1353 bool flag_onoff)
1354 {
1355 switch (flag) {
1356 case ALLEGRO_FRAMELESS:
1357 /* The ALLEGRO_FRAMELESS flag is backwards. */
1358 _al_xwin_set_frame(display, !flag_onoff);
1359 return true;
1360 case ALLEGRO_FULLSCREEN_WINDOW:
1361 xdpy_set_fullscreen_window(display, flag_onoff);
1362 return true;
1363 case ALLEGRO_MAXIMIZED:
1364 _al_xwin_maximize(display, flag_onoff);
1365 return true;
1366 }
1367 return false;
1368 }
1369
1370
xdpy_wait_for_vsync(ALLEGRO_DISPLAY * display)1371 static bool xdpy_wait_for_vsync(ALLEGRO_DISPLAY *display)
1372 {
1373 (void) display;
1374
1375 if (al_get_opengl_extension_list()->ALLEGRO_GLX_SGI_video_sync) {
1376 unsigned int count;
1377 glXGetVideoSyncSGI(&count);
1378 glXWaitVideoSyncSGI(2, (count+1) & 1, &count);
1379 return true;
1380 }
1381
1382 return false;
1383 }
1384
1385
1386 /* Obtain a reference to this driver. */
_al_display_xglx_driver(void)1387 ALLEGRO_DISPLAY_INTERFACE *_al_display_xglx_driver(void)
1388 {
1389 if (xdpy_vt.create_display)
1390 return &xdpy_vt;
1391
1392 xdpy_vt.create_display = xdpy_create_display;
1393 xdpy_vt.destroy_display = xdpy_destroy_display;
1394 xdpy_vt.set_current_display = xdpy_set_current_display;
1395 xdpy_vt.unset_current_display = xdpy_unset_current_display;
1396 xdpy_vt.flip_display = xdpy_flip_display;
1397 xdpy_vt.update_display_region = xdpy_update_display_region;
1398 xdpy_vt.acknowledge_resize = xdpy_acknowledge_resize;
1399 xdpy_vt.create_bitmap = _al_ogl_create_bitmap;
1400 xdpy_vt.get_backbuffer = _al_ogl_get_backbuffer;
1401 xdpy_vt.set_target_bitmap = _al_ogl_set_target_bitmap;
1402 xdpy_vt.is_compatible_bitmap = xdpy_is_compatible_bitmap;
1403 xdpy_vt.resize_display = xdpy_resize_display;
1404 xdpy_vt.set_icons = _al_xwin_set_icons;
1405 xdpy_vt.set_window_title = xdpy_set_window_title;
1406 xdpy_vt.set_window_position = xdpy_set_window_position;
1407 xdpy_vt.get_window_position = xdpy_get_window_position;
1408 xdpy_vt.set_window_constraints = xdpy_set_window_constraints;
1409 xdpy_vt.get_window_constraints = xdpy_get_window_constraints;
1410 xdpy_vt.apply_window_constraints = xdpy_apply_window_constraints;
1411 xdpy_vt.set_display_flag = xdpy_set_display_flag;
1412 xdpy_vt.wait_for_vsync = xdpy_wait_for_vsync;
1413 xdpy_vt.update_render_state = _al_ogl_update_render_state;
1414
1415 _al_xwin_add_cursor_functions(&xdpy_vt);
1416 _al_xwin_add_clipboard_functions(&xdpy_vt);
1417 _al_ogl_add_drawing_functions(&xdpy_vt);
1418
1419 return &xdpy_vt;
1420 }
1421
1422
1423 static const ALLEGRO_XWIN_DISPLAY_OVERRIDABLE_INTERFACE default_overridable_vt =
1424 {
1425 xdpy_create_display_hook_default,
1426 xdpy_destroy_display_hook_default,
1427 xdpy_resize_display_default,
1428 xdpy_set_window_title_default,
1429 xdpy_set_fullscreen_window_default,
1430 xdpy_set_window_position_default,
1431 xdpy_set_window_constraints_default
1432 };
1433
1434
_al_xwin_set_gtk_display_overridable_interface(uint32_t check_version,const ALLEGRO_XWIN_DISPLAY_OVERRIDABLE_INTERFACE * vt)1435 bool _al_xwin_set_gtk_display_overridable_interface(uint32_t check_version,
1436 const ALLEGRO_XWIN_DISPLAY_OVERRIDABLE_INTERFACE *vt)
1437 {
1438 /* The version of the native dialogs addon must exactly match the core
1439 * library version.
1440 */
1441 if (vt && check_version == ALLEGRO_VERSION_INT) {
1442 ALLEGRO_DEBUG("GTK vtable made available\n");
1443 gtk_override_vt = vt;
1444 return true;
1445 }
1446
1447 ALLEGRO_DEBUG("GTK vtable reset\n");
1448 gtk_override_vt = NULL;
1449 return (vt == NULL);
1450 }
1451
1452
1453 /* vim: set sts=3 sw=3 et: */
1454