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