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, ¢er_x, ¢er_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