1 #include <X11/Xlib.h>
2 
3 #include "allegro5/allegro.h"
4 #include "allegro5/internal/aintern_x.h"
5 #include "allegro5/internal/aintern_xdisplay.h"
6 #include "allegro5/internal/aintern_xfullscreen.h"
7 #include "allegro5/internal/aintern_xsystem.h"
8 
9 ALLEGRO_DEBUG_CHANNEL("display")
10 
11 /* globals - this might be better in ALLEGRO_SYSTEM_XGLX */
12 _ALLEGRO_XGLX_MMON_INTERFACE _al_xglx_mmon_interface;
13 
14 /* generic multi-head x */
_al_xsys_mheadx_get_default_adapter(ALLEGRO_SYSTEM_XGLX * s)15 int _al_xsys_mheadx_get_default_adapter(ALLEGRO_SYSTEM_XGLX *s)
16 {
17    int i;
18 
19    ALLEGRO_DEBUG("mhead get default adapter\n");
20 
21    if (ScreenCount(s->x11display) == 1)
22       return 0;
23 
24    _al_mutex_lock(&s->lock);
25 
26    Window focus;
27    int revert_to = 0;
28    XWindowAttributes attr;
29    Screen *focus_screen;
30 
31    if (!XGetInputFocus(s->x11display, &focus, &revert_to)) {
32       ALLEGRO_ERROR("XGetInputFocus failed!");
33       _al_mutex_unlock(&s->lock);
34       return 0;
35    }
36 
37    if (focus == None) {
38       ALLEGRO_ERROR("XGetInputFocus returned None!\n");
39       _al_mutex_unlock(&s->lock);
40       return 0;
41    }
42    else if (focus == PointerRoot) {
43       ALLEGRO_DEBUG("XGetInputFocus returned PointerRoot.\n");
44       /* XXX TEST THIS >:( */
45       Window root, child;
46       int root_x, root_y;
47       int win_x, win_y;
48       unsigned int mask;
49 
50       if (XQueryPointer(s->x11display, focus, &root, &child, &root_x, &root_y, &win_x, &win_y, &mask) == False) {
51          ALLEGRO_ERROR("XQueryPointer failed :(");
52          _al_mutex_unlock(&s->lock);
53          return 0;
54       }
55 
56       focus = root;
57    }
58    else {
59       ALLEGRO_DEBUG("XGetInputFocus returned %i!\n", (int)focus);
60    }
61 
62    XGetWindowAttributes(s->x11display, focus, &attr);
63    focus_screen = attr.screen;
64 
65    int ret = 0;
66    for (i = 0; i < ScreenCount(s->x11display); i++) {
67       if (ScreenOfDisplay(s->x11display, i) == focus_screen) {
68          _al_mutex_unlock(&s->lock);
69          ret = i;
70          break;
71       }
72    }
73 
74    _al_mutex_unlock(&s->lock);
75    return ret;
76 }
77 
78 /* in pure multi-head mode, allegro's virtual adapters map directly to X Screens. */
_al_xsys_mheadx_get_xscreen(ALLEGRO_SYSTEM_XGLX * s,int adapter)79 int _al_xsys_mheadx_get_xscreen(ALLEGRO_SYSTEM_XGLX *s, int adapter)
80 {
81    (void)s;
82    ALLEGRO_DEBUG("mhead get screen %i\n", adapter);
83    return adapter;
84 }
85 
86 /*
87 Returns the parent window of "window" (i.e. the ancestor of window
88 that is a direct child of the root, or window itself if it is a direct child).
89 If window is the root window, returns window.
90 */
get_toplevel_parent(ALLEGRO_SYSTEM_XGLX * s,Window window)91 static Window get_toplevel_parent(ALLEGRO_SYSTEM_XGLX *s, Window window)
92 {
93    Window parent;
94    Window root;
95    Window * children;
96    unsigned int num_children;
97 
98    while (1) {
99       /* XXX enlightenment shows some very strange errors here,
100        * for some reason 'window' isn't valid when the mouse happens
101        * to be over the windeco when this is called.. */
102       if (0 == XQueryTree(s->x11display, window, &root, &parent, &children, &num_children)) {
103          ALLEGRO_ERROR("XQueryTree error\n");
104          return None;
105       }
106       if (children) { /* must test for NULL */
107          XFree(children);
108       }
109       if (window == root || parent == root) {
110          return window;
111       }
112       else {
113          window = parent;
114       }
115    }
116 
117    return None;
118 }
119 
120 /* used for xinerama and pure xrandr modes */
_al_xsys_get_active_window_center(ALLEGRO_SYSTEM_XGLX * s,int * x,int * y)121 void _al_xsys_get_active_window_center(ALLEGRO_SYSTEM_XGLX *s, int *x, int *y)
122 {
123    Window focus;
124    int revert_to = 0;
125 
126    _al_mutex_lock(&s->lock);
127 
128    if (!XGetInputFocus(s->x11display, &focus, &revert_to)) {
129       ALLEGRO_ERROR("XGetInputFocus failed!\n");
130       _al_mutex_unlock(&s->lock);
131       return;
132    }
133 
134    if (focus == None || focus == PointerRoot) {
135       ALLEGRO_DEBUG("XGetInputFocus returned special window, selecting default root!\n");
136       focus = DefaultRootWindow(s->x11display);
137    }
138    else {
139       /* this horribleness is due to toolkits like GTK (and probably Qt) creating
140        * a 1x1 window under the window you're looking at that actually accepts
141        * all input, so we need to grab the top level parent window rather than
142        * whatever happens to have focus */
143 
144       focus = get_toplevel_parent(s, focus);
145    }
146 
147    ALLEGRO_DEBUG("XGetInputFocus returned %i\n", (int)focus);
148 
149    XWindowAttributes attr;
150 
151    if (XGetWindowAttributes(s->x11display, focus, &attr) == 0) {
152       ALLEGRO_ERROR("XGetWindowAttributes failed :(\n");
153       _al_mutex_unlock(&s->lock);
154       return;
155    }
156 
157    _al_mutex_unlock(&s->lock);
158 
159    /* check the center of the window with focus
160     * might be a bit more useful than just checking the top left */
161    ALLEGRO_DEBUG("focus geom: %ix%i %ix%i\n", attr.x, attr.y, attr.width, attr.height);
162    *x = (attr.x + (attr.x + attr.width)) / 2;
163    *y = (attr.y + (attr.y + attr.height)) / 2;
164 }
165 
166 /*---------------------------------------------------------------------------
167  *
168  * Xinerama
169  *
170  */
171 
172 #ifdef ALLEGRO_XWINDOWS_WITH_XINERAMA
173 
xinerama_init(ALLEGRO_SYSTEM_XGLX * s)174 static void xinerama_init(ALLEGRO_SYSTEM_XGLX *s)
175 {
176    int event_base = 0;
177    int error_base = 0;
178 
179    /* init xinerama info to defaults */
180    s->xinerama_available = 0;
181    s->xinerama_screen_count = 0;
182    s->xinerama_screen_info = NULL;
183 
184    _al_mutex_lock(&s->lock);
185 
186    if (XineramaQueryExtension(s->x11display, &event_base, &error_base)) {
187       int minor_version = 0, major_version = 0;
188       int status = XineramaQueryVersion(s->x11display, &major_version, &minor_version);
189       ALLEGRO_INFO("Xinerama version: %i.%i\n", major_version, minor_version);
190 
191       if (status && !XineramaIsActive(s->x11display)) {
192          ALLEGRO_WARN("Xinerama is not active\n");
193       }
194       else {
195          s->xinerama_screen_info = XineramaQueryScreens(s->x11display, &s->xinerama_screen_count);
196          if (!s->xinerama_screen_info) {
197             ALLEGRO_ERROR("Xinerama failed to query screens.\n");
198          }
199          else {
200             s->xinerama_available = 1;
201             ALLEGRO_INFO("Xinerama is active\n");
202          }
203       }
204    }
205 
206    if (!s->xinerama_available) {
207       ALLEGRO_WARN("Xinerama extension is not available.\n");
208    }
209 
210    _al_mutex_unlock(&s->lock);
211 }
212 
xinerama_exit(ALLEGRO_SYSTEM_XGLX * s)213 static void xinerama_exit(ALLEGRO_SYSTEM_XGLX *s)
214 {
215    if (!s->xinerama_available)
216       return;
217 
218    ALLEGRO_DEBUG("xfullscreen: xinerama exit.\n");
219    if (s->xinerama_screen_info)
220       XFree(s->xinerama_screen_info);
221 
222    s->xinerama_available = 0;
223    s->xinerama_screen_count = 0;
224    s->xinerama_screen_info = NULL;
225 }
226 
227 #ifdef ALLEGRO_XWINDOWS_WITH_XF86VIDMODE
228 
xinerama_get_display_offset(ALLEGRO_SYSTEM_XGLX * s,int adapter,int * x,int * y)229 static void xinerama_get_display_offset(ALLEGRO_SYSTEM_XGLX *s, int adapter, int *x, int *y)
230 {
231    ALLEGRO_ASSERT(adapter >= 0 && adapter < s->xinerama_screen_count);
232    *x = s->xinerama_screen_info[adapter].x_org;
233    *y = s->xinerama_screen_info[adapter].y_org;
234    ALLEGRO_DEBUG("xinerama dpy off %ix%i\n", *x, *y);
235 }
236 
xinerama_get_monitor_info(ALLEGRO_SYSTEM_XGLX * s,int adapter,ALLEGRO_MONITOR_INFO * mi)237 static bool xinerama_get_monitor_info(ALLEGRO_SYSTEM_XGLX *s, int adapter, ALLEGRO_MONITOR_INFO *mi)
238 {
239    if (adapter < 0 || adapter >= s->xinerama_screen_count)
240       return false;
241 
242    mi->x1 = s->xinerama_screen_info[adapter].x_org;
243    mi->y1 = s->xinerama_screen_info[adapter].y_org;
244    mi->x2 = mi->x1 + s->xinerama_screen_info[adapter].width;
245    mi->y2 = mi->y1 + s->xinerama_screen_info[adapter].height;
246    return true;
247 }
248 
xinerama_get_mode(ALLEGRO_SYSTEM_XGLX * s,int adapter,int i,ALLEGRO_DISPLAY_MODE * mode)249 static ALLEGRO_DISPLAY_MODE *xinerama_get_mode(ALLEGRO_SYSTEM_XGLX *s, int adapter, int i, ALLEGRO_DISPLAY_MODE *mode)
250 {
251    if (adapter < 0 || adapter >= s->xinerama_screen_count)
252       return NULL;
253 
254    if (i != 0)
255       return NULL;
256 
257    mode->width = s->xinerama_screen_info[adapter].width;
258    mode->height = s->xinerama_screen_info[adapter].height;
259    mode->format = 0;
260    mode->refresh_rate = 0;
261 
262    return mode;
263 }
264 
xinerama_get_default_adapter(ALLEGRO_SYSTEM_XGLX * s)265 static int xinerama_get_default_adapter(ALLEGRO_SYSTEM_XGLX *s)
266 {
267    int center_x = 0, center_y = 0;
268    ALLEGRO_DEBUG("xinerama get default adapter\n");
269 
270    _al_xsys_get_active_window_center(s, &center_x, &center_y);
271    ALLEGRO_DEBUG("xinerama got active center: %ix%i\n", center_x, center_y);
272 
273    int i;
274    for (i = 0; i < s->xinerama_screen_count; i++) {
275       if (center_x >= s->xinerama_screen_info[i].x_org && center_x <= s->xinerama_screen_info[i].x_org + s->xinerama_screen_info[i].width &&
276          center_y >= s->xinerama_screen_info[i].y_org && center_y <= s->xinerama_screen_info[i].y_org + s->xinerama_screen_info[i].height)
277       {
278          ALLEGRO_DEBUG("center is inside (%i) %ix%i %ix%i\n", i, s->xinerama_screen_info[i].x_org, s->xinerama_screen_info[i].y_org, s->xinerama_screen_info[i].width, s->xinerama_screen_info[i].height);
279          return i;
280       }
281    }
282 
283    ALLEGRO_DEBUG("xinerama returning default 0\n");
284    return 0;
285 }
286 
287 /* similar to multi-head x, but theres only one X Screen, so we return 0 always */
xinerama_get_xscreen(ALLEGRO_SYSTEM_XGLX * s,int adapter)288 static int xinerama_get_xscreen(ALLEGRO_SYSTEM_XGLX *s, int adapter)
289 {
290    (void)s;
291    (void)adapter;
292    return 0;
293 }
294 
295 #endif /* ALLEGRO_XWINDOWS_WITH_XF86VIDMODE */
296 
297 #endif /* ALLEGRO_XWINDOWS_WITH_XINERAMA */
298 
299 
300 
301 /*---------------------------------------------------------------------------
302  *
303  * XF86VidMode
304  *
305  */
306 
307 #ifdef ALLEGRO_XWINDOWS_WITH_XF86VIDMODE
308 
309 // XXX retest under multi-head!
xfvm_get_num_modes(ALLEGRO_SYSTEM_XGLX * s,int adapter)310 static int xfvm_get_num_modes(ALLEGRO_SYSTEM_XGLX *s, int adapter)
311 {
312 #ifdef ALLEGRO_XWINDOWS_WITH_XINERAMA
313    if (s->xinerama_available && s->xinerama_screen_count != s->xfvm_screen_count) {
314       if (adapter < 0 || adapter > s->xinerama_screen_count)
315          return 0;
316 
317       /* due to braindeadedness of the NVidia binary driver we can't know what an individual
318        * monitor's modes are, as the NVidia binary driver only reports combined "BigDesktop"
319        * or "TwinView" modes to user-space. There is no way to set modes on individual screens.
320        * As such, we can only do one thing here and report one single mode,
321        * which will end up being the xinerama size for the requested adapter */
322       return 1;
323    }
324 #endif
325 
326    if (adapter < 0 || adapter > s->xfvm_screen_count)
327       return 0;
328 
329    return s->xfvm_screen[adapter].mode_count;
330 }
331 
xfvm_get_mode(ALLEGRO_SYSTEM_XGLX * s,int adapter,int i,ALLEGRO_DISPLAY_MODE * mode)332 static ALLEGRO_DISPLAY_MODE *xfvm_get_mode(ALLEGRO_SYSTEM_XGLX *s, int adapter, int i, ALLEGRO_DISPLAY_MODE *mode)
333 {
334    int denom;
335 
336 #ifdef ALLEGRO_XWINDOWS_WITH_XINERAMA
337    /* TwinView gives us one large screen via xfvm, and no way to
338     * properly change modes on individual monitors, so we want to query
339     * xinerama for the lone mode. */
340    if (s->xinerama_available && s->xfvm_screen_count != s->xinerama_screen_count) {
341       return xinerama_get_mode(s, adapter, i, mode);
342    }
343 #endif
344 
345    if (adapter < 0 || adapter > s->xfvm_screen_count)
346       return NULL;
347 
348    if (i < 0 || i > s->xfvm_screen[adapter].mode_count)
349       return NULL;
350 
351    mode->width = s->xfvm_screen[adapter].modes[i]->hdisplay;
352    mode->height = s->xfvm_screen[adapter].modes[i]->vdisplay;
353    mode->format = 0;
354    denom = s->xfvm_screen[adapter].modes[i]->htotal * s->xfvm_screen[adapter].modes[i]->vtotal;
355    if (denom > 0)
356       mode->refresh_rate = s->xfvm_screen[adapter].modes[i]->dotclock * 1000L / denom;
357    else
358       mode->refresh_rate = 0;
359 
360    return mode;
361 }
362 
xfvm_set_mode(ALLEGRO_SYSTEM_XGLX * s,ALLEGRO_DISPLAY_XGLX * d,int w,int h,int format,int refresh_rate)363 static bool xfvm_set_mode(ALLEGRO_SYSTEM_XGLX *s, ALLEGRO_DISPLAY_XGLX *d, int w, int h, int format, int refresh_rate)
364 {
365    int mode_idx = -1;
366    int adapter = _al_xglx_get_adapter(s, d, false);
367 
368 #ifdef ALLEGRO_XWINDOWS_WITH_XINERAMA
369    /* TwinView workarounds, nothing to do here, since we can't really change or restore modes */
370    if (s->xinerama_available && s->xinerama_screen_count != s->xfvm_screen_count) {
371       /* at least pretend we set a mode if its the current mode */
372       if (s->xinerama_screen_info[adapter].width != w || s->xinerama_screen_info[adapter].height != h)
373          return false;
374 
375       return true;
376    }
377 #endif
378 
379    mode_idx = _al_xglx_fullscreen_select_mode(s, adapter, w, h, format, refresh_rate);
380    if (mode_idx == -1)
381       return false;
382 
383    if (!XF86VidModeSwitchToMode(s->x11display, adapter, s->xfvm_screen[adapter].modes[mode_idx])) {
384       ALLEGRO_ERROR("xfullscreen: XF86VidModeSwitchToMode failed\n");
385       return false;
386    }
387 
388    return true;
389 }
390 
xfvm_store_video_mode(ALLEGRO_SYSTEM_XGLX * s)391 static void xfvm_store_video_mode(ALLEGRO_SYSTEM_XGLX *s)
392 {
393    int n;
394 
395    ALLEGRO_DEBUG("xfullscreen: xfvm_store_video_mode\n");
396 
397 #ifdef ALLEGRO_XWINDOWS_WITH_XINERAMA
398    /* TwinView workarounds, nothing to do here, since we can't really change or restore modes */
399    if (s->xinerama_available && s->xinerama_screen_count != s->xfvm_screen_count) {
400       return;
401    }
402 #endif
403 
404    // save all original modes
405    int i;
406    for (i = 0; i < s->xfvm_screen_count; i++) {
407       n = xfvm_get_num_modes(s, i);
408       if (n == 0) {
409          /* XXX what to do here? */
410          continue;
411       }
412 
413       s->xfvm_screen[i].original_mode = s->xfvm_screen[i].modes[0];
414 
415       int j;
416       for (j = 0; j <  s->xfvm_screen[i].mode_count; j++) {
417          ALLEGRO_DEBUG("xfvm: screen[%d] mode[%d] = (%d, %d)\n",
418             i, j, s->xfvm_screen[i].modes[j]->hdisplay, s->xfvm_screen[i].modes[j]->vdisplay);
419       }
420       ALLEGRO_INFO("xfvm: screen[%d] original mode = (%d, %d)\n",
421          i, s->xfvm_screen[i].original_mode->hdisplay, s->xfvm_screen[i].original_mode->vdisplay);
422    }
423 }
424 
xfvm_restore_video_mode(ALLEGRO_SYSTEM_XGLX * s,int adapter)425 static void xfvm_restore_video_mode(ALLEGRO_SYSTEM_XGLX *s, int adapter)
426 {
427    Bool ok;
428 
429 #ifdef ALLEGRO_XWINDOWS_WITH_XINERAMA
430    /* TwinView workarounds, nothing to do here, since we can't really change or restore modes */
431    if (s->xinerama_available && s->xinerama_screen_count != s->xfvm_screen_count) {
432       return;
433    }
434 #endif
435 
436    if (adapter < 0 || adapter > s->xfvm_screen_count)
437       return;
438 
439    ASSERT(s->xfvm_screen[adapter].original_mode);
440    ALLEGRO_DEBUG("xfullscreen: xfvm_restore_video_mode (%d, %d)\n",
441       s->xfvm_screen[adapter].original_mode->hdisplay, s->xfvm_screen[adapter].original_mode->vdisplay);
442 
443    ok = XF86VidModeSwitchToMode(s->x11display, adapter, s->xfvm_screen[adapter].original_mode);
444    if (!ok) {
445       ALLEGRO_ERROR("xfullscreen: XF86VidModeSwitchToMode failed\n");
446    }
447 
448    if (s->mouse_grab_display) {
449       XUngrabPointer(s->gfxdisplay, CurrentTime);
450       s->mouse_grab_display = NULL;
451    }
452 
453    /* This is needed, at least on my machine, or the program may terminate
454     * before the screen mode is actually reset. --pw
455     */
456    /* can we move this into shutdown_system? It could speed up mode restores -TF */
457    XFlush(s->gfxdisplay);
458 }
459 
xfvm_get_display_offset(ALLEGRO_SYSTEM_XGLX * s,int adapter,int * x,int * y)460 static void xfvm_get_display_offset(ALLEGRO_SYSTEM_XGLX *s, int adapter, int *x, int *y)
461 {
462    int tmp_x = 0, tmp_y = 0;
463 
464 #ifdef ALLEGRO_XWINDOWS_WITH_XINERAMA
465    if (s->xinerama_available) {
466       xinerama_get_display_offset(s, adapter, &tmp_x, &tmp_y);
467    } //else
468 #else
469    (void)s;
470    (void)adapter;
471 #endif
472    /* don't set the output params if function fails */
473    /* XXX I don't think this part makes sense at all.
474     * in multi-head mode, the origin is always 0x0
475     * in Xinerama, its caught by xinerama, and xfvm is NEVER
476     * used when xrandr is active -TF */
477    //if (!XF86VidModeGetViewPort(s->x11display, adapter, &tmp_x, &tmp_y))
478    //   return;
479 
480    *x = tmp_x;
481    *y = tmp_y;
482 
483    ALLEGRO_DEBUG("xfvm dpy off %ix%i\n", *x, *y);
484 }
485 
xfvm_get_num_adapters(ALLEGRO_SYSTEM_XGLX * s)486 static int xfvm_get_num_adapters(ALLEGRO_SYSTEM_XGLX *s)
487 {
488 #ifdef ALLEGRO_XWINDOWS_WITH_XINERAMA
489    if (s->xinerama_available) {
490       return s->xinerama_screen_count;
491    }
492 #endif
493    return s->xfvm_screen_count;
494 }
495 
xfvm_get_monitor_info(ALLEGRO_SYSTEM_XGLX * s,int adapter,ALLEGRO_MONITOR_INFO * mi)496 static bool xfvm_get_monitor_info(ALLEGRO_SYSTEM_XGLX *s, int adapter, ALLEGRO_MONITOR_INFO *mi)
497 {
498 #ifdef ALLEGRO_XWINDOWS_WITH_XINERAMA
499    if (s->xinerama_available) {
500       return xinerama_get_monitor_info(s, adapter, mi);
501    }
502 #endif
503 
504    if (adapter < 0 || adapter > s->xfvm_screen_count)
505       return false;
506 
507    XWindowAttributes xwa;
508    Window root;
509 
510    _al_mutex_lock(&s->lock);
511    root = RootWindow(s->x11display, adapter);
512    XGetWindowAttributes(s->x11display, root, &xwa);
513    _al_mutex_unlock(&s->lock);
514 
515    /* under plain X, each screen has its own origin,
516       and theres no way to figure out orientation
517       or relative position */
518    mi->x1 = 0;
519    mi->y1 = 0;
520    mi->x2 = xwa.width;
521    mi->y2 = xwa.height;
522    return true;
523 }
524 
xfvm_get_default_adapter(ALLEGRO_SYSTEM_XGLX * s)525 static int xfvm_get_default_adapter(ALLEGRO_SYSTEM_XGLX *s)
526 {
527    ALLEGRO_DEBUG("xfvm get default adapter\n");
528 
529 #ifdef ALLEGRO_XWINDOWS_WITH_XINERAMA
530    if (s->xinerama_available) {
531       return xinerama_get_default_adapter(s);
532    }
533 #endif
534 
535    return _al_xsys_mheadx_get_default_adapter(s);
536 }
537 
xfvm_get_xscreen(ALLEGRO_SYSTEM_XGLX * s,int adapter)538 static int xfvm_get_xscreen(ALLEGRO_SYSTEM_XGLX *s, int adapter)
539 {
540    ALLEGRO_DEBUG("xfvm get xscreen for adapter %i\n", adapter);
541 
542 #ifdef ALLEGRO_XWINDOWS_WITH_XINERAMA
543    if (s->xinerama_available) {
544       return xinerama_get_xscreen(s, adapter);
545    }
546 #endif
547 
548    return _al_xsys_mheadx_get_xscreen(s, adapter);
549 }
550 
xfvm_post_setup(ALLEGRO_SYSTEM_XGLX * s,ALLEGRO_DISPLAY_XGLX * d)551 static void xfvm_post_setup(ALLEGRO_SYSTEM_XGLX *s,
552    ALLEGRO_DISPLAY_XGLX *d)
553 {
554    int x = 0, y = 0;
555    XWindowAttributes xwa;
556 
557 #ifdef ALLEGRO_XWINDOWS_WITH_XINERAMA
558    /* TwinView workarounds, nothing to do here, since we can't really change or restore modes */
559    if (s->xinerama_available && s->xinerama_screen_count != s->xfvm_screen_count) {
560       return;
561    }
562 #endif
563 
564    int adapter = _al_xglx_get_adapter(s, d, false);
565 
566    XGetWindowAttributes(s->x11display, d->window, &xwa);
567    xfvm_get_display_offset(s, adapter, &x, &y);
568 
569    /* some window managers like to move our window even if we explicitly tell it not to
570     * so we need to get the correct offset here */
571    x = xwa.x - x;
572    y = xwa.y - y;
573 
574    ALLEGRO_DEBUG("xfvm set view port: %ix%i\n", x, y);
575 
576    XF86VidModeSetViewPort(s->x11display, adapter, x, y);
577 }
578 
579 
xfvm_init(ALLEGRO_SYSTEM_XGLX * s)580 static void xfvm_init(ALLEGRO_SYSTEM_XGLX *s)
581 {
582    int event_base = 0;
583    int error_base = 0;
584 
585    /* init xfvm info to defaults */
586    s->xfvm_available = 0;
587    s->xfvm_screen_count = 0;
588    s->xfvm_screen = NULL;
589 
590    _al_mutex_lock(&s->lock);
591 
592    if (XF86VidModeQueryExtension(s->x11display, &event_base, &error_base)) {
593       int minor_version = 0, major_version = 0;
594       int status = XF86VidModeQueryVersion(s->x11display, &major_version, &minor_version);
595       ALLEGRO_INFO("XF86VidMode version: %i.%i\n", major_version, minor_version);
596 
597       if (!status) {
598          ALLEGRO_WARN("XF86VidMode not available, XF86VidModeQueryVersion failed.\n");
599       }
600       else {
601          // I don't actually know what versions are required here, just going to assume any is ok for now.
602          ALLEGRO_INFO("XF86VidMode %i.%i is active\n", major_version, minor_version);
603          s->xfvm_available = 1;
604       }
605    }
606    else {
607       ALLEGRO_WARN("XF86VidMode extension is not available.\n");
608    }
609 
610    if (s->xfvm_available) {
611       int num_screens;
612 #ifdef ALLEGRO_XWINDOWS_WITH_XINERAMA
613       /* This is some fun stuff right here, if XRANDR is available, we can't use the xinerama screen count
614        * and we really want the xrandr init in xglx_initialize to come last so it overrides xf86vm if available
615        * and I really don't want to add more XRRQuery* or #ifdefs here.
616        * I don't think XRandR can be disabled once its loaded,
617        * so just seeing if its in the extension list should be fine. */
618       /* interesting thing to note is if XRandR is available, that means we have TwinView,
619        * and not multi-head Xinerama mode, as True Xinerama disables XRandR on my NVidia.
620        * which means all of those xfvm_screen_count != xinerama_screen_count tests
621        * only apply to TwinView. As this code below sets xfvm_screen_count to xinerama_screen_count
622        * making all those compares fail, and make us fall back to the normal xfvm multi-head code. */
623       /* second note, if FakeXinerama is disabled on TwinView setups, we will end up using
624        * XRandR, as there is no other way to detect TwinView outside of libNVCtrl */
625 
626       int ext_op, ext_evt, ext_err;
627       Bool ext_ret = XQueryExtension(s->x11display, "RANDR", &ext_op, &ext_evt, &ext_err);
628 
629       if (s->xinerama_available && ext_ret == False) {
630          num_screens = s->xinerama_screen_count;
631       }
632       else
633 #endif
634       {
635          num_screens = ScreenCount(s->x11display);
636       }
637 
638       ALLEGRO_DEBUG("XF86VidMode Got %d screens.\n", num_screens);
639       s->xfvm_screen_count = num_screens;
640 
641       s->xfvm_screen = al_calloc(num_screens, sizeof(*s->xfvm_screen));
642       if (!s->xfvm_screen) {
643          ALLEGRO_ERROR("XF86VidMode: failed to allocate screen array.\n");
644          s->xfvm_available = 0;
645       }
646       else {
647          int i;
648          for (i = 0; i < num_screens; i++) {
649             ALLEGRO_DEBUG("XF86VidMode GetAllModeLines on screen %d.\n", i);
650             if (!XF86VidModeGetAllModeLines(s->x11display, i, &(s->xfvm_screen[i].mode_count), &(s->xfvm_screen[i].modes))) {
651                /* XXX what to do here? */
652             }
653          }
654 
655          _al_xglx_mmon_interface.get_num_display_modes = xfvm_get_num_modes;
656          _al_xglx_mmon_interface.get_display_mode      = xfvm_get_mode;
657          _al_xglx_mmon_interface.set_mode              = xfvm_set_mode;
658          _al_xglx_mmon_interface.store_mode            = xfvm_store_video_mode;
659          _al_xglx_mmon_interface.restore_mode          = xfvm_restore_video_mode;
660          _al_xglx_mmon_interface.get_display_offset    = xfvm_get_display_offset;
661          _al_xglx_mmon_interface.get_num_adapters      = xfvm_get_num_adapters;
662          _al_xglx_mmon_interface.get_monitor_info      = xfvm_get_monitor_info;
663          _al_xglx_mmon_interface.get_default_adapter   = xfvm_get_default_adapter;
664          _al_xglx_mmon_interface.get_xscreen           = xfvm_get_xscreen;
665          _al_xglx_mmon_interface.post_setup            = xfvm_post_setup;
666       }
667    }
668 
669    _al_mutex_unlock(&s->lock);
670 }
671 
xfvm_exit(ALLEGRO_SYSTEM_XGLX * s)672 static void xfvm_exit(ALLEGRO_SYSTEM_XGLX *s)
673 {
674    int adapter;
675    ALLEGRO_DEBUG("xfullscreen: XFVM exit\n");
676 
677    for (adapter = 0; adapter < s->xfvm_screen_count; adapter++) {
678       if (s->xfvm_screen[adapter].mode_count > 0) {
679          int i;
680          for (i = 0; i < s->xfvm_screen[adapter].mode_count; i++) {
681             if (s->xfvm_screen[adapter].modes[i]->privsize > 0) {
682                //XFree(s->xfvm_screen[adapter].modes[i]->private);
683             }
684          }
685          //XFree(s->xfvm_screen[adapter].modes);
686       }
687 
688       s->xfvm_screen[adapter].mode_count = 0;
689       s->xfvm_screen[adapter].modes = NULL;
690       s->xfvm_screen[adapter].original_mode = NULL;
691 
692       ALLEGRO_DEBUG("xfullscreen: XFVM freed adapter %d.\n", adapter);
693    }
694 
695    al_free(s->xfvm_screen);
696    s->xfvm_screen = NULL;
697 }
698 
699 #endif /* ALLEGRO_XWINDOWS_WITH_XF86VIDMODE */
700 
701 
702 
703 /*---------------------------------------------------------------------------
704  *
705  * Generic multi-monitor interface
706  *
707  */
708 
init_mmon_interface(ALLEGRO_SYSTEM_XGLX * s)709 static bool init_mmon_interface(ALLEGRO_SYSTEM_XGLX *s)
710 {
711    if (s->x11display == NULL) {
712       ALLEGRO_WARN("Not connected to X server.\n");
713       return false;
714    }
715 
716    if (s->mmon_interface_inited)
717       return true;
718 
719    /* Shouldn't we avoid initing any more of these than we need? */
720    /* nope, no way to tell which is going to be used on any given system
721     * this way, xrandr always overrides everything else should it succeed.
722     * And when xfvm is chosen, it needs xinerama inited,
723     * incase there are multiple screens.
724     */
725 
726 #ifdef ALLEGRO_XWINDOWS_WITH_XINERAMA
727    xinerama_init(s);
728 #endif
729 
730 #ifdef ALLEGRO_XWINDOWS_WITH_XF86VIDMODE
731    xfvm_init(s);
732 #endif
733 
734 #ifdef ALLEGRO_XWINDOWS_WITH_XRANDR
735    _al_xsys_xrandr_init(s);
736 #endif
737 
738    if (_al_xglx_mmon_interface.store_mode)
739       _al_xglx_mmon_interface.store_mode(s);
740 
741    s->mmon_interface_inited = true;
742 
743    return true;
744 }
745 
_al_xsys_mmon_exit(ALLEGRO_SYSTEM_XGLX * s)746 void _al_xsys_mmon_exit(ALLEGRO_SYSTEM_XGLX *s)
747 {
748    if (!s->mmon_interface_inited)
749       return;
750 
751 #ifdef ALLEGRO_XWINDOWS_WITH_XINERAMA
752    xinerama_exit(s);
753 #endif
754 
755 #ifdef ALLEGRO_XWINDOWS_WITH_XF86VIDMODE
756    xfvm_exit(s);
757 #endif
758 
759 #ifdef ALLEGRO_XWINDOWS_WITH_XRANDR
760    _al_xsys_xrandr_exit(s);
761 #endif
762 
763    s->mmon_interface_inited = false;
764 }
765 
_al_xglx_get_num_display_modes(ALLEGRO_SYSTEM_XGLX * s,int adapter)766 int _al_xglx_get_num_display_modes(ALLEGRO_SYSTEM_XGLX *s, int adapter)
767 {
768    if (!init_mmon_interface(s))
769       return 0;
770 
771    if (adapter < 0)
772       adapter = _al_xglx_get_default_adapter(s);
773 
774    if (!_al_xglx_mmon_interface.get_num_display_modes) {
775       if (adapter != 0)
776          return 0;
777 
778       return 1;
779    }
780 
781    return _al_xglx_mmon_interface.get_num_display_modes(s, adapter);
782 }
783 
_al_xglx_get_display_mode(ALLEGRO_SYSTEM_XGLX * s,int adapter,int index,ALLEGRO_DISPLAY_MODE * mode)784 ALLEGRO_DISPLAY_MODE *_al_xglx_get_display_mode(ALLEGRO_SYSTEM_XGLX *s, int adapter, int index,
785    ALLEGRO_DISPLAY_MODE *mode)
786 {
787    if (!init_mmon_interface(s))
788       return NULL;
789 
790    if (adapter < 0)
791       adapter = _al_xglx_get_default_adapter(s);
792 
793    if (!_al_xglx_mmon_interface.get_display_mode) {
794       mode->width = DisplayWidth(s->x11display, DefaultScreen(s->x11display));
795       mode->height = DisplayHeight(s->x11display, DefaultScreen(s->x11display));
796       mode->format = 0;
797       mode->refresh_rate = 0;
798       return NULL;
799    }
800 
801    return _al_xglx_mmon_interface.get_display_mode(s, adapter, index, mode);
802 }
803 
_al_xglx_fullscreen_select_mode(ALLEGRO_SYSTEM_XGLX * s,int adapter,int w,int h,int format,int refresh_rate)804 int _al_xglx_fullscreen_select_mode(ALLEGRO_SYSTEM_XGLX *s, int adapter, int w, int h, int format, int refresh_rate)
805 {
806    int i;
807    int n;
808 
809    if (!init_mmon_interface(s))
810       return -1;
811 
812    if (adapter < 0)
813       adapter = _al_xglx_get_default_adapter(s);
814 
815    n = _al_xglx_get_num_display_modes(s, adapter);
816    if (!n)
817       return -1;
818 
819    /* Find all modes with correct parameters. */
820    ALLEGRO_DISPLAY_MODE mode = {0, 0, 0, 0};
821    int possible_modes[n];
822    int possible_count = 0;
823    for (i = 0; i < n; i++) {
824       if (!_al_xglx_get_display_mode(s, adapter, i, &mode)) {
825          continue;
826       }
827       if (mode.width == w && mode.height == h &&
828          (format == 0 || mode.format == format) &&
829          (refresh_rate == 0 || mode.refresh_rate == refresh_rate))
830       {
831          possible_modes[possible_count++] = i;
832       }
833    }
834    if (!possible_count)
835       return -1;
836 
837    /* Choose mode with highest refresh rate. */
838    int best_mode = possible_modes[0];
839    _al_xglx_get_display_mode(s, adapter, best_mode, &mode);
840    for (i = 1; i < possible_count; i++) {
841       ALLEGRO_DISPLAY_MODE mode2;
842       if (!_al_xglx_get_display_mode(s, adapter, possible_modes[i], &mode2))  {
843          continue;
844       }
845       if (mode2.refresh_rate > mode.refresh_rate) {
846          mode = mode2;
847          best_mode = possible_modes[i];
848       }
849    }
850 
851    ALLEGRO_INFO("best mode [%d] = (%d, %d)\n", best_mode, mode.width, mode.height);
852 
853    return best_mode;
854 }
855 
_al_xglx_fullscreen_set_mode(ALLEGRO_SYSTEM_XGLX * s,ALLEGRO_DISPLAY_XGLX * d,int w,int h,int format,int refresh_rate)856 bool _al_xglx_fullscreen_set_mode(ALLEGRO_SYSTEM_XGLX *s,
857    ALLEGRO_DISPLAY_XGLX *d, int w, int h, int format, int refresh_rate)
858 {
859    if (!init_mmon_interface(s))
860       return false;
861 
862    if (!_al_xglx_mmon_interface.set_mode)
863       return false;
864 
865    return _al_xglx_mmon_interface.set_mode(s, d, w, h, format, refresh_rate);
866 }
867 
_al_xglx_fullscreen_to_display(ALLEGRO_SYSTEM_XGLX * s,ALLEGRO_DISPLAY_XGLX * d)868 void _al_xglx_fullscreen_to_display(ALLEGRO_SYSTEM_XGLX *s,
869    ALLEGRO_DISPLAY_XGLX *d)
870 {
871    if (!init_mmon_interface(s))
872       return;
873 
874    if (!_al_xglx_mmon_interface.post_setup)
875       return;
876 
877    _al_xglx_mmon_interface.post_setup(s, d);
878 
879 }
880 
_al_xglx_store_video_mode(ALLEGRO_SYSTEM_XGLX * s)881 void _al_xglx_store_video_mode(ALLEGRO_SYSTEM_XGLX *s)
882 {
883    if (!init_mmon_interface(s))
884       return;
885 
886    if (!_al_xglx_mmon_interface.store_mode)
887       return;
888 
889    _al_xglx_mmon_interface.store_mode(s);
890 }
891 
_al_xglx_restore_video_mode(ALLEGRO_SYSTEM_XGLX * s,int adapter)892 void _al_xglx_restore_video_mode(ALLEGRO_SYSTEM_XGLX *s, int adapter)
893 {
894    if (!init_mmon_interface(s))
895       return;
896 
897    if (!_al_xglx_mmon_interface.restore_mode)
898       return;
899 
900    _al_xglx_mmon_interface.restore_mode(s, adapter);
901 }
902 
_al_xglx_get_display_offset(ALLEGRO_SYSTEM_XGLX * s,int adapter,int * x,int * y)903 void _al_xglx_get_display_offset(ALLEGRO_SYSTEM_XGLX *s, int adapter, int *x, int *y)
904 {
905    if (!init_mmon_interface(s))
906       return;
907 
908    if (!_al_xglx_mmon_interface.get_display_offset)
909       return;
910 
911    _al_xglx_mmon_interface.get_display_offset(s, adapter, x, y);
912 }
913 
_al_xglx_get_monitor_info(ALLEGRO_SYSTEM_XGLX * s,int adapter,ALLEGRO_MONITOR_INFO * info)914 bool _al_xglx_get_monitor_info(ALLEGRO_SYSTEM_XGLX *s, int adapter, ALLEGRO_MONITOR_INFO *info)
915 {
916    if (!init_mmon_interface(s))
917       return false;
918 
919    if (!_al_xglx_mmon_interface.get_monitor_info) {
920       _al_mutex_lock(&s->lock);
921       info->x1 = 0;
922       info->y1 = 0;
923       info->x2 = DisplayWidth(s->x11display, DefaultScreen(s->x11display));
924       info->y2 = DisplayHeight(s->x11display, DefaultScreen(s->x11display));
925       _al_mutex_unlock(&s->lock);
926       return true;
927    }
928 
929    return _al_xglx_mmon_interface.get_monitor_info(s, adapter, info);
930 }
931 
_al_xglx_get_num_video_adapters(ALLEGRO_SYSTEM_XGLX * s)932 int _al_xglx_get_num_video_adapters(ALLEGRO_SYSTEM_XGLX *s)
933 {
934    if (!init_mmon_interface(s))
935       return 0;
936 
937    if (!_al_xglx_mmon_interface.get_num_adapters)
938       return 1;
939 
940    return _al_xglx_mmon_interface.get_num_adapters(s);
941 }
942 
_al_xglx_get_default_adapter(ALLEGRO_SYSTEM_XGLX * s)943 int _al_xglx_get_default_adapter(ALLEGRO_SYSTEM_XGLX *s)
944 {
945    ALLEGRO_DEBUG("get default adapter\n");
946 
947    if (!init_mmon_interface(s))
948       return 0;
949 
950    if (!_al_xglx_mmon_interface.get_default_adapter)
951       return 0;
952 
953    return _al_xglx_mmon_interface.get_default_adapter(s);
954 }
955 
_al_xglx_get_xscreen(ALLEGRO_SYSTEM_XGLX * s,int adapter)956 int _al_xglx_get_xscreen(ALLEGRO_SYSTEM_XGLX *s, int adapter)
957 {
958    ALLEGRO_DEBUG("get xscreen\n");
959 
960    if (!init_mmon_interface(s))
961       return 0;
962 
963    if (!_al_xglx_mmon_interface.get_xscreen)
964       return 0;
965 
966    return _al_xglx_mmon_interface.get_xscreen(s, adapter);
967 }
968 
_al_xglx_get_adapter(ALLEGRO_SYSTEM_XGLX * s,ALLEGRO_DISPLAY_XGLX * d,bool recalc)969 int _al_xglx_get_adapter(ALLEGRO_SYSTEM_XGLX *s, ALLEGRO_DISPLAY_XGLX *d, bool recalc)
970 {
971    if (!init_mmon_interface(s))
972       return 0;
973 
974    if (d->adapter >= 0 && !recalc)
975       return d->adapter;
976 
977    if (!_al_xglx_mmon_interface.get_adapter)
978       return 0;
979 
980    return _al_xglx_mmon_interface.get_adapter(s, d);
981 }
982 
_al_xglx_handle_mmon_event(ALLEGRO_SYSTEM_XGLX * s,ALLEGRO_DISPLAY_XGLX * d,XEvent * e)983 void _al_xglx_handle_mmon_event(ALLEGRO_SYSTEM_XGLX *s, ALLEGRO_DISPLAY_XGLX *d, XEvent *e)
984 {
985    ALLEGRO_DEBUG("got event %i\n", e->type);
986    // if we haven't setup the mmon interface, just bail
987    if (!s->mmon_interface_inited)
988       return;
989 
990    // bail if the current mmon interface doesn't implement the handle_xevent method
991    if (!_al_xglx_mmon_interface.handle_xevent)
992       return;
993 
994    _al_xglx_mmon_interface.handle_xevent(s, d, e);
995 }
996 
997 /* vim: set sts=3 sw=3 et: */
998