1/*
2    SDL - Simple DirectMedia Layer
3    Copyright (C) 1997-2006 Sam Lantinga
4
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19    Sam Lantinga
20    slouken@libsdl.org
21*/
22#include "SDL_config.h"
23
24/* Utilities for getting and setting the X display mode */
25
26#include <stdio.h>
27
28#include "SDL_timer.h"
29#include "SDL_events.h"
30#include "../../events/SDL_events_c.h"
31#include "SDL_x11video.h"
32#include "SDL_x11wm_c.h"
33#include "SDL_x11modes_c.h"
34#include "SDL_x11image_c.h"
35
36/*#define X11MODES_DEBUG*/
37
38#define MAX(a, b)        (a > b ? a : b)
39
40#if SDL_VIDEO_DRIVER_X11_XRANDR
41static int cmpmodelist(const void *va, const void *vb)
42{
43    const SDL_Rect *a = *(const SDL_Rect **)va;
44    const SDL_Rect *b = *(const SDL_Rect **)vb;
45    if ( a->w == b->w )
46        return b->h - a->h;
47    else
48        return b->w - a->w;
49}
50#endif
51
52#if SDL_VIDEO_DRIVER_X11_VIDMODE
53Bool SDL_NAME(XF86VidModeGetModeInfo)(Display *dpy, int scr, SDL_NAME(XF86VidModeModeInfo) *info)
54{
55    SDL_NAME(XF86VidModeModeLine) *l = (SDL_NAME(XF86VidModeModeLine)*)((char*)info + sizeof info->dotclock);
56    return SDL_NAME(XF86VidModeGetModeLine)(dpy, scr, (int*)&info->dotclock, l);
57}
58#endif /* SDL_VIDEO_DRIVER_X11_VIDMODE */
59
60#if SDL_VIDEO_DRIVER_X11_VIDMODE
61static void save_mode(_THIS)
62{
63    SDL_memset(&saved_mode, 0, sizeof(saved_mode));
64    SDL_NAME(XF86VidModeGetModeInfo)(SDL_Display,SDL_Screen,&saved_mode);
65    SDL_NAME(XF86VidModeGetViewPort)(SDL_Display,SDL_Screen,&saved_view.x,&saved_view.y);
66}
67#endif
68
69#if SDL_VIDEO_DRIVER_X11_VIDMODE
70static void restore_mode(_THIS)
71{
72    SDL_NAME(XF86VidModeModeLine) mode;
73    int unused;
74
75    if ( SDL_NAME(XF86VidModeGetModeLine)(SDL_Display, SDL_Screen, &unused, &mode) ) {
76        if ( (saved_mode.hdisplay != mode.hdisplay) ||
77             (saved_mode.vdisplay != mode.vdisplay) ) {
78            SDL_NAME(XF86VidModeSwitchToMode)(SDL_Display, SDL_Screen, &saved_mode);
79        }
80    }
81    if ( (saved_view.x != 0) || (saved_view.y != 0) ) {
82        SDL_NAME(XF86VidModeSetViewPort)(SDL_Display, SDL_Screen, saved_view.x, saved_view.y);
83    }
84}
85#endif
86
87#if SDL_VIDEO_DRIVER_X11_VIDMODE
88static int cmpmodes(const void *va, const void *vb)
89{
90    const SDL_NAME(XF86VidModeModeInfo) *a = *(const SDL_NAME(XF86VidModeModeInfo)**)va;
91    const SDL_NAME(XF86VidModeModeInfo) *b = *(const SDL_NAME(XF86VidModeModeInfo)**)vb;
92    if ( a->hdisplay == b->hdisplay )
93        return b->vdisplay - a->vdisplay;
94    else
95        return b->hdisplay - a->hdisplay;
96}
97#endif
98
99static void get_real_resolution(_THIS, int* w, int* h);
100
101static void set_best_resolution(_THIS, int width, int height)
102{
103#if SDL_VIDEO_DRIVER_X11_VIDMODE
104    if ( use_vidmode ) {
105        SDL_NAME(XF86VidModeModeLine) mode;
106        SDL_NAME(XF86VidModeModeInfo) **modes;
107        int i;
108        int nmodes;
109        int best = -1;
110
111        if ( SDL_NAME(XF86VidModeGetModeLine)(SDL_Display, SDL_Screen, &i, &mode) &&
112             SDL_NAME(XF86VidModeGetAllModeLines)(SDL_Display,SDL_Screen,&nmodes,&modes) ) {
113            for ( i = 0; i < nmodes ; i++ ) {
114                if ( (modes[i]->hdisplay == width) &&
115                     (modes[i]->vdisplay == height) ) {
116                    best = i;
117                    break;
118                }
119                if ( modes[i]->hdisplay >= width &&
120                     modes[i]->vdisplay >= height ) {
121                    if ( best < 0 ||
122                         (modes[i]->hdisplay < modes[best]->hdisplay &&
123                          modes[i]->vdisplay <= modes[best]->vdisplay) ||
124                         (modes[i]->vdisplay < modes[best]->vdisplay &&
125                          modes[i]->hdisplay <= modes[best]->hdisplay) ) {
126                        best = i;
127                    }
128                }
129            }
130            if ( best >= 0 &&
131                 ((modes[best]->hdisplay != mode.hdisplay) ||
132                  (modes[best]->vdisplay != mode.vdisplay)) ) {
133#ifdef X11MODES_DEBUG
134                printf("Best Mode %d: %d x %d @ %d\n", best,
135                        modes[best]->hdisplay, modes[best]->vdisplay,
136                        (modes[best]->htotal && modes[best]->vtotal) ? (1000 * modes[best]->dotclock / (modes[best]->htotal * modes[best]->vtotal)) : 0 );
137#endif
138                SDL_NAME(XF86VidModeSwitchToMode)(SDL_Display, SDL_Screen, modes[best]);
139            }
140            XFree(modes);
141        }
142    }
143#endif /* SDL_VIDEO_DRIVER_X11_VIDMODE */
144
145                                /* XiG */
146#if SDL_VIDEO_DRIVER_X11_XME
147    if ( use_xme && SDL_modelist ) {
148        int i;
149
150#ifdef X11MODES_DEBUG
151        fprintf(stderr, "XME: set_best_resolution(): w = %d, h = %d\n",
152                width, height);
153#endif
154        for ( i=0; SDL_modelist[i]; ++i ) {
155            if ( (SDL_modelist[i]->w >= width) &&
156                 (SDL_modelist[i]->h >= height) ) {
157                break;
158            }
159        }
160
161        if ( SDL_modelist[i] ) { /* found one, lets try it */
162            int w, h;
163
164            /* check current mode so we can avoid uneccessary mode changes */
165            get_real_resolution(this, &w, &h);
166
167            if ( (SDL_modelist[i]->w != w) || (SDL_modelist[i]->h != h) ) {
168#ifdef X11MODES_DEBUG
169                fprintf(stderr, "XME: set_best_resolution: "
170                        "XiGMiscChangeResolution: %d %d\n",
171                        SDL_modelist[i]->w, SDL_modelist[i]->h);
172#endif
173                XiGMiscChangeResolution(SDL_Display,
174                                        SDL_Screen,
175                                        0, /* view */
176                                        SDL_modelist[i]->w,
177                                        SDL_modelist[i]->h,
178                                        0);
179                XSync(SDL_Display, False);
180            }
181        }
182    }
183#endif /* SDL_VIDEO_DRIVER_X11_XME */
184
185#if SDL_VIDEO_DRIVER_X11_XRANDR
186    if ( use_xrandr && SDL_modelist ) {
187#ifdef X11MODES_DEBUG
188        fprintf(stderr, "XRANDR: set_best_resolution(): w = %d, h = %d\n",
189                width, height);
190#endif
191        int i, nsizes;
192        XRRScreenSize *sizes;
193
194        /* find the smallest resolution that is at least as big as the user requested */
195        sizes = XRRConfigSizes(screen_config, &nsizes);
196        for ( i = (nsizes-1); i >= 0; i-- ) {
197            if ( (SDL_modelist[i]->w >= width) &&
198                 (SDL_modelist[i]->h >= height) ) {
199                break;
200            }
201        }
202
203        if ( i >= 0 && SDL_modelist[i] ) { /* found one, lets try it */
204            int w, h;
205
206            /* check current mode so we can avoid uneccessary mode changes */
207            get_real_resolution(this, &w, &h);
208
209            if ( (SDL_modelist[i]->w != w) || (SDL_modelist[i]->h != h) ) {
210                int size_id;
211
212#ifdef X11MODES_DEBUG
213                fprintf(stderr, "XRANDR: set_best_resolution: "
214                        "XXRSetScreenConfig: %d %d\n",
215                        SDL_modelist[i]->w, SDL_modelist[i]->h);
216#endif
217
218                /* find the matching size entry index */
219                for ( size_id = 0; size_id < nsizes; ++size_id ) {
220                    if ( (sizes[size_id].width == SDL_modelist[i]->w) &&
221                         (sizes[size_id].height == SDL_modelist[i]->h) )
222                        break;
223                }
224
225                XRRSetScreenConfig(SDL_Display, screen_config, SDL_Root,
226                                   size_id, saved_rotation, CurrentTime);
227            }
228        }
229    }
230#endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
231}
232
233static void get_real_resolution(_THIS, int* w, int* h)
234{
235#if SDL_VIDEO_DRIVER_X11_XME
236    if ( use_xme ) {
237        int ractive;
238        XiGMiscResolutionInfo *modelist;
239
240        XiGMiscQueryResolutions(SDL_Display, SDL_Screen,
241                                0, /* view */
242                                &ractive, &modelist);
243        *w = modelist[ractive].width;
244        *h = modelist[ractive].height;
245#ifdef X11MODES_DEBUG
246        fprintf(stderr, "XME: get_real_resolution: w = %d h = %d\n", *w, *h);
247#endif
248        XFree(modelist);
249        return;
250    }
251#endif /* SDL_VIDEO_DRIVER_X11_XME */
252
253#if SDL_VIDEO_DRIVER_X11_VIDMODE
254    if ( use_vidmode ) {
255        SDL_NAME(XF86VidModeModeLine) mode;
256        int unused;
257
258        if ( SDL_NAME(XF86VidModeGetModeLine)(SDL_Display, SDL_Screen, &unused, &mode) ) {
259            *w = mode.hdisplay;
260            *h = mode.vdisplay;
261            return;
262        }
263    }
264#endif /* SDL_VIDEO_DRIVER_X11_VIDMODE */
265
266#if SDL_VIDEO_DRIVER_X11_XRANDR
267    if ( use_xrandr ) {
268        int nsizes;
269        XRRScreenSize* sizes;
270
271        sizes = XRRConfigSizes(screen_config, &nsizes);
272        if ( nsizes > 0 ) {
273            int cur_size;
274            Rotation cur_rotation;
275
276            cur_size = XRRConfigCurrentConfiguration(screen_config, &cur_rotation);
277            if ( cur_size >= 0 && cur_size < nsizes ) {
278                *w = sizes[cur_size].width;
279                *h = sizes[cur_size].height;
280            }
281#ifdef X11MODES_DEBUG
282            fprintf(stderr, "XRANDR: get_real_resolution: w = %d h = %d\n", *w, *h);
283#endif
284            return;
285        }
286    }
287#endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
288
289#if SDL_VIDEO_DRIVER_X11_XINERAMA
290    if ( use_xinerama ) {
291        *w = xinerama_info.width;
292        *h = xinerama_info.height;
293        return;
294    }
295#endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
296
297    *w = DisplayWidth(SDL_Display, SDL_Screen);
298    *h = DisplayHeight(SDL_Display, SDL_Screen);
299}
300
301/* Called after mapping a window - waits until the window is mapped */
302void X11_WaitMapped(_THIS, Window win)
303{
304    XEvent event;
305    do {
306        XMaskEvent(SDL_Display, StructureNotifyMask, &event);
307    } while ( (event.type != MapNotify) || (event.xmap.event != win) );
308}
309
310/* Called after unmapping a window - waits until the window is unmapped */
311void X11_WaitUnmapped(_THIS, Window win)
312{
313    XEvent event;
314    do {
315        XMaskEvent(SDL_Display, StructureNotifyMask, &event);
316    } while ( (event.type != UnmapNotify) || (event.xunmap.event != win) );
317}
318
319static void move_cursor_to(_THIS, int x, int y)
320{
321    XWarpPointer(SDL_Display, None, SDL_Root, 0, 0, 0, 0, x, y);
322}
323
324static int add_visual(_THIS, int depth, int class)
325{
326    XVisualInfo vi;
327    if(XMatchVisualInfo(SDL_Display, SDL_Screen, depth, class, &vi)) {
328        int n = this->hidden->nvisuals;
329        this->hidden->visuals[n].depth = vi.depth;
330        this->hidden->visuals[n].visual = vi.visual;
331        this->hidden->nvisuals++;
332    }
333    return(this->hidden->nvisuals);
334}
335static int add_visual_byid(_THIS, const char *visual_id)
336{
337    XVisualInfo *vi, template;
338    int nvis;
339
340    if ( visual_id ) {
341        SDL_memset(&template, 0, (sizeof template));
342        template.visualid = SDL_strtol(visual_id, NULL, 0);
343        vi = XGetVisualInfo(SDL_Display, VisualIDMask, &template, &nvis);
344        if ( vi ) {
345            int n = this->hidden->nvisuals;
346            this->hidden->visuals[n].depth = vi->depth;
347            this->hidden->visuals[n].visual = vi->visual;
348            this->hidden->nvisuals++;
349            XFree(vi);
350        }
351    }
352    return(this->hidden->nvisuals);
353}
354
355/* Global for the error handler */
356int vm_event, vm_error = -1;
357
358#if SDL_VIDEO_DRIVER_X11_XINERAMA
359static int CheckXinerama(_THIS, int *major, int *minor)
360{
361    const char *env;
362
363    /* Default the extension not available */
364    *major = *minor = 0;
365
366    /* Allow environment override */
367    env = getenv("SDL_VIDEO_X11_XINERAMA");
368    if ( env && !SDL_atoi(env) ) {
369        return 0;
370    }
371
372    /* Query the extension version */
373    if ( !SDL_NAME(XineramaQueryExtension)(SDL_Display, major, minor) ||
374         !SDL_NAME(XineramaIsActive)(SDL_Display) ) {
375        return 0;
376    }
377    return 1;
378}
379#endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
380
381#if SDL_VIDEO_DRIVER_X11_XRANDR
382static int CheckXRandR(_THIS, int *major, int *minor)
383{
384    const char *env;
385
386    /* Default the extension not available */
387    *major = *minor = 0;
388
389    /* Allow environment override */
390    env = getenv("SDL_VIDEO_X11_XRANDR");
391    if ( env && !SDL_atoi(env) ) {
392        return 0;
393    }
394
395    /* This defaults off now, due to KDE window maximize problems */
396    if ( !env ) {
397        return 0;
398    }
399
400    if ( !SDL_X11_HAVE_XRANDR ) {
401        return 0;
402    }
403
404    /* Query the extension version */
405    if ( !XRRQueryVersion(SDL_Display, major, minor) ) {
406        return 0;
407    }
408    return 1;
409}
410#endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
411
412#if SDL_VIDEO_DRIVER_X11_VIDMODE
413static int CheckVidMode(_THIS, int *major, int *minor)
414{
415    const char *env;
416
417    /* Default the extension not available */
418    *major = *minor = 0;
419
420    /* Allow environment override */
421    env = getenv("SDL_VIDEO_X11_VIDMODE");
422    if ( env && !SDL_atoi(env) ) {
423        return 0;
424    }
425
426    /* Metro-X 4.3.0 and earlier has a broken implementation of
427       XF86VidModeGetAllModeLines() - it hangs the client.
428     */
429    if ( SDL_strcmp(ServerVendor(SDL_Display), "Metro Link Incorporated") == 0 ) {
430        FILE *metro_fp;
431
432        metro_fp = fopen("/usr/X11R6/lib/X11/Metro/.version", "r");
433        if ( metro_fp != NULL ) {
434            int major, minor, patch, version;
435            major = 0; minor = 0; patch = 0;
436            fscanf(metro_fp, "%d.%d.%d", &major, &minor, &patch);
437            fclose(metro_fp);
438            version = major*100+minor*10+patch;
439            if ( version < 431 ) {
440                return 0;
441            }
442        }
443    }
444
445    /* Query the extension version */
446    vm_error = -1;
447    if ( !SDL_NAME(XF86VidModeQueryExtension)(SDL_Display, &vm_event, &vm_error) ||
448         !SDL_NAME(XF86VidModeQueryVersion)(SDL_Display, major, minor) ) {
449        return 0;
450    }
451    return 1;
452}
453#endif /* SDL_VIDEO_DRIVER_X11_VIDMODE */
454
455#if SDL_VIDEO_DRIVER_X11_XME
456static int CheckXME(_THIS, int *major, int *minor)
457{
458    const char *env;
459
460    /* Default the extension not available */
461    *major = *minor = 0;
462
463    /* Allow environment override */
464    env = getenv("SDL_VIDEO_X11_VIDMODE");
465    if ( env && !SDL_atoi(env) ) {
466        return 0;
467    }
468
469    /* Query the extension version */
470    if ( !XiGMiscQueryVersion(SDL_Display, major, minor) ) {
471        return 0;
472    }
473    return 1;
474}
475#endif /* SDL_VIDEO_DRIVER_X11_XME */
476
477int X11_GetVideoModes(_THIS)
478{
479#if SDL_VIDEO_DRIVER_X11_XINERAMA
480    int xinerama_major, xinerama_minor;
481#endif
482#if SDL_VIDEO_DRIVER_X11_XRANDR
483    int xrandr_major, xrandr_minor;
484    int nsizes;
485    XRRScreenSize *sizes;
486#endif
487#if SDL_VIDEO_DRIVER_X11_VIDMODE
488    int vm_major, vm_minor;
489    int nmodes;
490    SDL_NAME(XF86VidModeModeInfo) **modes;
491#endif
492#if SDL_VIDEO_DRIVER_X11_XME
493    int xme_major, xme_minor;
494    int ractive, nummodes;
495    XiGMiscResolutionInfo *modelist;
496#endif
497    int i, n;
498    int screen_w;
499    int screen_h;
500
501    use_xinerama = 0;
502    use_xrandr = 0;
503    use_vidmode = 0;
504    use_xme = 0;
505    screen_w = DisplayWidth(SDL_Display, SDL_Screen);
506    screen_h = DisplayHeight(SDL_Display, SDL_Screen);
507
508#if SDL_VIDEO_DRIVER_X11_XINERAMA
509    /* Query Xinerama extention */
510    if ( CheckXinerama(this, &xinerama_major, &xinerama_minor) ) {
511        /* Find out which screen is the desired one */
512        int desired = 0;
513        int screens;
514        int w, h;
515        SDL_NAME(XineramaScreenInfo) *xinerama;
516
517        const char *variable = SDL_getenv("SDL_VIDEO_FULLSCREEN_HEAD");
518        if ( variable ) {
519                desired = SDL_atoi(variable);
520        }
521#ifdef X11MODES_DEBUG
522        printf("X11 detected Xinerama:\n");
523#endif
524        xinerama = SDL_NAME(XineramaQueryScreens)(SDL_Display, &screens);
525        for ( i = 0; i < screens; i++ ) {
526#ifdef X11MODES_DEBUG
527            printf("xinerama %d: %dx%d+%d+%d\n",
528                xinerama[i].screen_number,
529                xinerama[i].width, xinerama[i].height,
530                xinerama[i].x_org, xinerama[i].y_org);
531#endif
532            if ( xinerama[i].screen_number == desired ) {
533                use_xinerama = 1;
534                xinerama_info = xinerama[i];
535            }
536        }
537        XFree(xinerama);
538
539        if ( use_xinerama ) {
540            SDL_modelist = (SDL_Rect **)SDL_malloc(3*sizeof(SDL_Rect *));
541            if ( !SDL_modelist ) {
542                SDL_OutOfMemory();
543                return -1;
544            }
545
546            /* Add the full xinerama mode */
547            n = 0;
548            w = xinerama_info.width;
549            h = xinerama_info.height;
550            if ( screen_w > w || screen_h > h) {
551                SDL_modelist[n] = (SDL_Rect *)SDL_malloc(sizeof(SDL_Rect));
552                if ( SDL_modelist[n] ) {
553                    SDL_modelist[n]->x = 0;
554                    SDL_modelist[n]->y = 0;
555                    SDL_modelist[n]->w = screen_w;
556                    SDL_modelist[n]->h = screen_h;
557                    ++n;
558                }
559            }
560
561            /* Add the head xinerama mode */
562            SDL_modelist[n] = (SDL_Rect *)SDL_malloc(sizeof(SDL_Rect));
563            if ( SDL_modelist[n] ) {
564                SDL_modelist[n]->x = 0;
565                SDL_modelist[n]->y = 0;
566                SDL_modelist[n]->w = w;
567                SDL_modelist[n]->h = h;
568                ++n;
569            }
570            SDL_modelist[n] = NULL;
571        }
572    }
573#endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
574
575#if SDL_VIDEO_DRIVER_X11_XRANDR
576    /* XRandR */
577    /* require at least XRandR v1.0 (arbitrary) */
578    if ( CheckXRandR(this, &xrandr_major, &xrandr_minor) && (xrandr_major >= 1) )
579    {
580#ifdef X11MODES_DEBUG
581        fprintf(stderr, "XRANDR: XRRQueryVersion: V%d.%d\n",
582                xrandr_major, xrandr_minor);
583#endif
584
585        /* save the screen configuration since we must reference it
586           each time we toggle modes.
587        */
588        screen_config = XRRGetScreenInfo(SDL_Display, SDL_Root);
589
590        /* retrieve the list of resolution */
591        sizes = XRRConfigSizes(screen_config, &nsizes);
592        if (nsizes > 0) {
593            if ( SDL_modelist ) {
594                for ( i = 0; SDL_modelist[i]; ++i ) {
595                    SDL_free(SDL_modelist[i]);
596                }
597                SDL_free(SDL_modelist);
598            }
599            SDL_modelist = (SDL_Rect **)malloc((nsizes+1)*sizeof(SDL_Rect *));
600            if ( !SDL_modelist ) {
601                SDL_OutOfMemory();
602                return -1;
603            }
604            for ( i=0; i < nsizes; i++ ) {
605                if ((SDL_modelist[i] =
606                     (SDL_Rect *)malloc(sizeof(SDL_Rect))) == NULL)
607                    break;
608#ifdef X11MODES_DEBUG
609                fprintf(stderr, "XRANDR: mode = %4d, w = %4d, h = %4d\n",
610                        i, sizes[i].width, sizes[i].height);
611#endif
612
613                SDL_modelist[i]->x = 0;
614                SDL_modelist[i]->y = 0;
615                SDL_modelist[i]->w = sizes[i].width;
616                SDL_modelist[i]->h = sizes[i].height;
617
618            }
619            /* sort the mode list descending as SDL expects */
620            SDL_qsort(SDL_modelist, nsizes, sizeof *SDL_modelist, cmpmodelist);
621            SDL_modelist[i] = NULL; /* terminator */
622
623            use_xrandr = xrandr_major * 100 + xrandr_minor;
624            saved_size_id = XRRConfigCurrentConfiguration(screen_config, &saved_rotation);
625        }
626    }
627#endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
628
629#if SDL_VIDEO_DRIVER_X11_VIDMODE
630    /* XVidMode */
631    if ( !use_xrandr &&
632#if SDL_VIDEO_DRIVER_X11_XINERAMA
633         (!use_xinerama || xinerama_info.screen_number == 0) &&
634#endif
635         CheckVidMode(this, &vm_major, &vm_minor) &&
636         SDL_NAME(XF86VidModeGetAllModeLines)(SDL_Display, SDL_Screen,&nmodes,&modes) )
637    {
638#ifdef X11MODES_DEBUG
639        printf("VidMode modes: (unsorted)\n");
640        for ( i = 0; i < nmodes; ++i ) {
641            printf("Mode %d: %d x %d @ %d\n", i,
642                    modes[i]->hdisplay, modes[i]->vdisplay,
643                    (modes[i]->htotal && modes[i]->vtotal) ? (1000 * modes[i]->dotclock / (modes[i]->htotal * modes[i]->vtotal)) : 0 );
644        }
645#endif
646        if ( SDL_modelist ) {
647            for ( i = 0; SDL_modelist[i]; ++i ) {
648                SDL_free(SDL_modelist[i]);
649            }
650            SDL_free(SDL_modelist);
651        }
652        SDL_modelist = (SDL_Rect **)SDL_malloc((nmodes+2)*sizeof(SDL_Rect *));
653        if ( !SDL_modelist ) {
654            SDL_OutOfMemory();
655            return -1;
656        }
657        SDL_qsort(modes, nmodes, sizeof *modes, cmpmodes);
658        n = 0;
659        for ( i=0; i<nmodes; ++i ) {
660            int w, h;
661
662            /* Eliminate duplicate modes with different refresh rates */
663            if ( i > 0 &&
664                 modes[i]->hdisplay == modes[i-1]->hdisplay &&
665                 modes[i]->vdisplay == modes[i-1]->vdisplay ) {
666                    continue;
667            }
668
669            /* Check to see if we should add the screen size (Xinerama) */
670            w = modes[i]->hdisplay;
671            h = modes[i]->vdisplay;
672            if ( (screen_w * screen_h) >= (w * h) ) {
673                if ( (screen_w != w) || (screen_h != h) ) {
674                    SDL_modelist[n] = (SDL_Rect *)SDL_malloc(sizeof(SDL_Rect));
675                    if ( SDL_modelist[n] ) {
676                        SDL_modelist[n]->x = 0;
677                        SDL_modelist[n]->y = 0;
678                        SDL_modelist[n]->w = screen_w;
679                        SDL_modelist[n]->h = screen_h;
680                        ++n;
681                    }
682                }
683                screen_w = 0;
684                screen_h = 0;
685            }
686
687            /* Add the size from the video mode list */
688            SDL_modelist[n] = (SDL_Rect *)SDL_malloc(sizeof(SDL_Rect));
689            if ( SDL_modelist[n] == NULL ) {
690                break;
691            }
692            SDL_modelist[n]->x = 0;
693            SDL_modelist[n]->y = 0;
694            SDL_modelist[n]->w = w;
695            SDL_modelist[n]->h = h;
696            ++n;
697        }
698        SDL_modelist[n] = NULL;
699        XFree(modes);
700
701        use_vidmode = vm_major * 100 + vm_minor;
702        save_mode(this);
703    }
704#endif /* SDL_VIDEO_DRIVER_X11_VIDMODE */
705
706#if SDL_VIDEO_DRIVER_X11_XME
707    /* XiG */
708    modelist = NULL;
709    /* first lets make sure we have the extension, and it's at least v2.0 */
710    if ( CheckXME(this, &xme_major, &xme_minor) && xme_major >= 2 &&
711         (nummodes = XiGMiscQueryResolutions(SDL_Display, SDL_Screen,
712                                             0, /* view */
713                                             &ractive, &modelist)) > 1 )
714    {                                /* then we actually have some */
715        int j;
716
717        /* We get the list already sorted in descending order.
718           We'll copy it in reverse order so SDL is happy */
719#ifdef X11MODES_DEBUG
720        fprintf(stderr, "XME: nummodes = %d, active mode = %d\n",
721                nummodes, ractive);
722#endif
723        if ( SDL_modelist ) {
724            for ( i = 0; SDL_modelist[i]; ++i ) {
725                SDL_free(SDL_modelist[i]);
726            }
727            SDL_free(SDL_modelist);
728        }
729        SDL_modelist = (SDL_Rect **)SDL_malloc((nummodes+1)*sizeof(SDL_Rect *));
730        if ( !SDL_modelist ) {
731            SDL_OutOfMemory();
732            return -1;
733        }
734        for ( i=0, j=nummodes-1; j>=0; i++, j-- ) {
735            if ((SDL_modelist[i] =
736                 (SDL_Rect *)SDL_malloc(sizeof(SDL_Rect))) == NULL)
737              break;
738#ifdef X11MODES_DEBUG
739            fprintf(stderr, "XME: mode = %4d, w = %4d, h = %4d\n",
740                   i, modelist[i].width, modelist[i].height);
741#endif
742
743            SDL_modelist[i]->x = 0;
744            SDL_modelist[i]->y = 0;
745            SDL_modelist[i]->w = modelist[j].width;
746            SDL_modelist[i]->h = modelist[j].height;
747
748        }
749        SDL_modelist[i] = NULL; /* terminator */
750
751        use_xme = xme_major * 100 + xme_minor;
752        saved_res = modelist[ractive]; /* save the current resolution */
753    }
754    if ( modelist ) {
755        XFree(modelist);
756    }
757#endif /* SDL_VIDEO_DRIVER_X11_XME */
758
759    {
760	/* It's interesting to note that if we allow 32 bit depths,
761	   we get a visual with an alpha mask on composite servers.
762        static int depth_list[] = { 32, 24, 16, 15, 8 };
763	*/
764        static int depth_list[] = { 24, 16, 15, 8 };
765        int j, np;
766        int use_directcolor = 1;
767        XPixmapFormatValues *pf;
768
769        /* Search for the visuals in deepest-first order, so that the first
770           will be the richest one */
771        if ( SDL_getenv("SDL_VIDEO_X11_NODIRECTCOLOR") ) {
772                use_directcolor = 0;
773        }
774        this->hidden->nvisuals = 0;
775        if ( ! add_visual_byid(this, SDL_getenv("SDL_VIDEO_X11_VISUALID")) ) {
776                for ( i=0; i<SDL_arraysize(depth_list); ++i ) {
777                        if ( depth_list[i] > 8 ) {
778                                if ( use_directcolor ) {
779                                        add_visual(this, depth_list[i], DirectColor);
780                                }
781                                add_visual(this, depth_list[i], TrueColor);
782                        } else {
783                                add_visual(this, depth_list[i], PseudoColor);
784                                add_visual(this, depth_list[i], StaticColor);
785                        }
786                }
787        }
788        if ( this->hidden->nvisuals == 0 ) {
789            SDL_SetError("Found no sufficiently capable X11 visuals");
790            return -1;
791        }
792
793        /* look up the pixel quantum for each depth */
794        pf = XListPixmapFormats(SDL_Display, &np);
795        for(i = 0; i < this->hidden->nvisuals; i++) {
796            int d = this->hidden->visuals[i].depth;
797            for(j = 0; j < np; j++)
798                if(pf[j].depth == d)
799                    break;
800            this->hidden->visuals[i].bpp = j < np ? pf[j].bits_per_pixel : d;
801        }
802
803        XFree(pf);
804    }
805
806    if ( SDL_modelist == NULL ) {
807        SDL_modelist = (SDL_Rect **)SDL_malloc((1+1)*sizeof(SDL_Rect *));
808        if ( !SDL_modelist ) {
809            SDL_OutOfMemory();
810            return -1;
811        }
812        n = 0;
813        SDL_modelist[n] = (SDL_Rect *)SDL_malloc(sizeof(SDL_Rect));
814        if ( SDL_modelist[n] ) {
815            SDL_modelist[n]->x = 0;
816            SDL_modelist[n]->y = 0;
817            SDL_modelist[n]->w = screen_w;
818            SDL_modelist[n]->h = screen_h;
819            ++n;
820        }
821        SDL_modelist[n] = NULL;
822    }
823
824#ifdef X11MODES_DEBUG
825    if ( use_xinerama ) {
826        printf("Xinerama is enabled\n");
827    }
828
829    if ( use_xrandr ) {
830        printf("XRandR is enabled\n");
831    }
832
833    if ( use_vidmode ) {
834        printf("VidMode is enabled\n");
835    }
836
837    if ( use_xme ) {
838        printf("Xi Graphics XME fullscreen is enabled\n");
839    }
840
841    if ( SDL_modelist ) {
842        printf("X11 video mode list:\n");
843        for ( i=0; SDL_modelist[i]; ++i ) {
844            printf("\t%dx%d\n", SDL_modelist[i]->w, SDL_modelist[i]->h);
845        }
846    }
847#endif /* X11MODES_DEBUG */
848
849    return 0;
850}
851
852int X11_SupportedVisual(_THIS, SDL_PixelFormat *format)
853{
854    int i;
855    for(i = 0; i < this->hidden->nvisuals; i++)
856        if(this->hidden->visuals[i].bpp == format->BitsPerPixel)
857            return 1;
858    return 0;
859}
860
861SDL_Rect **X11_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags)
862{
863    if ( X11_SupportedVisual(this, format) ) {
864        if ( flags & SDL_FULLSCREEN ) {
865            return(SDL_modelist);
866        } else {
867            return((SDL_Rect **)-1);
868        }
869    } else {
870        return((SDL_Rect **)0);
871    }
872}
873
874void X11_FreeVideoModes(_THIS)
875{
876    int i;
877
878    if ( SDL_modelist ) {
879        for ( i=0; SDL_modelist[i]; ++i ) {
880            SDL_free(SDL_modelist[i]);
881        }
882        SDL_free(SDL_modelist);
883        SDL_modelist = NULL;
884    }
885
886#if SDL_VIDEO_DRIVER_X11_XRANDR
887    /* Free the Xrandr screen configuration */
888    if ( screen_config ) {
889        XRRFreeScreenConfigInfo(screen_config);
890        screen_config = NULL;
891    }
892#endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
893}
894
895int X11_ResizeFullScreen(_THIS)
896{
897    int x = 0, y = 0;
898    int real_w, real_h;
899    int screen_w;
900    int screen_h;
901
902    screen_w = DisplayWidth(SDL_Display, SDL_Screen);
903    screen_h = DisplayHeight(SDL_Display, SDL_Screen);
904
905#if SDL_VIDEO_DRIVER_X11_XINERAMA
906    if ( use_xinerama &&
907         window_w <= xinerama_info.width &&
908         window_h <= xinerama_info.height ) {
909        x = xinerama_info.x_org;
910        y = xinerama_info.y_org;
911    }
912#endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
913
914    if ( currently_fullscreen ) {
915        /* Switch resolution and cover it with the FSwindow */
916        move_cursor_to(this, x, y);
917        set_best_resolution(this, window_w, window_h);
918        move_cursor_to(this, x, y);
919        get_real_resolution(this, &real_w, &real_h);
920        if ( window_w > real_w ) {
921            real_w = MAX(real_w, screen_w);
922        }
923        if ( window_h > real_h ) {
924            real_h = MAX(real_h, screen_h);
925        }
926        XMoveResizeWindow(SDL_Display, FSwindow, x, y, real_w, real_h);
927        move_cursor_to(this, real_w/2, real_h/2);
928
929        /* Center and reparent the drawing window */
930        x = (real_w - window_w)/2;
931        y = (real_h - window_h)/2;
932        XReparentWindow(SDL_Display, SDL_Window, FSwindow, x, y);
933        /* FIXME: move the mouse to the old relative location */
934        XSync(SDL_Display, True);   /* Flush spurious mode change events */
935    }
936    return(1);
937}
938
939void X11_QueueEnterFullScreen(_THIS)
940{
941    switch_waiting = 0x01 | SDL_FULLSCREEN;
942    switch_time = SDL_GetTicks() + 1500;
943#if 0 /* This causes a BadMatch error if the window is iconified (not needed) */
944    XSetInputFocus(SDL_Display, WMwindow, RevertToNone, CurrentTime);
945#endif
946}
947
948int X11_EnterFullScreen(_THIS)
949{
950    int okay;
951#if 0
952    Window tmpwin, *windows;
953    int i, nwindows;
954#endif
955    int x = 0, y = 0;
956    int real_w, real_h;
957    int screen_w;
958    int screen_h;
959
960    okay = 1;
961    if ( currently_fullscreen ) {
962        return(okay);
963    }
964
965    /* Ungrab the input so that we can move the mouse around */
966    X11_GrabInputNoLock(this, SDL_GRAB_OFF);
967
968#if SDL_VIDEO_DRIVER_X11_XINERAMA
969    if ( use_xinerama &&
970         window_w <= xinerama_info.width &&
971         window_h <= xinerama_info.height ) {
972        x = xinerama_info.x_org;
973        y = xinerama_info.y_org;
974    }
975#endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
976
977    /* Map the fullscreen window to blank the screen */
978    screen_w = DisplayWidth(SDL_Display, SDL_Screen);
979    screen_h = DisplayHeight(SDL_Display, SDL_Screen);
980    get_real_resolution(this, &real_w, &real_h);
981    if ( window_w > real_w ) {
982        real_w = MAX(real_w, screen_w);
983    }
984    if ( window_h > real_h ) {
985        real_h = MAX(real_h, screen_h);
986    }
987    XMoveResizeWindow(SDL_Display, FSwindow,
988                      x, y, real_w, real_h);
989    XMapRaised(SDL_Display, FSwindow);
990    X11_WaitMapped(this, FSwindow);
991
992#if 0 /* This seems to break WindowMaker in focus-follows-mouse mode */
993    /* Make sure we got to the top of the window stack */
994    if ( XQueryTree(SDL_Display, SDL_Root, &tmpwin, &tmpwin,
995                            &windows, &nwindows) && windows ) {
996        /* If not, try to put us there - if fail... oh well */
997        if ( windows[nwindows-1] != FSwindow ) {
998            tmpwin = windows[nwindows-1];
999            for ( i=0; i<nwindows; ++i ) {
1000                if ( windows[i] == FSwindow ) {
1001                    SDL_memcpy(&windows[i], &windows[i+1],
1002                           (nwindows-i-1)*sizeof(windows[i]));
1003                    break;
1004                }
1005            }
1006            windows[nwindows-1] = FSwindow;
1007            XRestackWindows(SDL_Display, windows, nwindows);
1008            XSync(SDL_Display, False);
1009        }
1010        XFree(windows);
1011    }
1012#else
1013    XRaiseWindow(SDL_Display, FSwindow);
1014#endif
1015
1016#if SDL_VIDEO_DRIVER_X11_VIDMODE
1017    /* Save the current video mode */
1018    if ( use_vidmode ) {
1019        SDL_NAME(XF86VidModeLockModeSwitch)(SDL_Display, SDL_Screen, True);
1020    }
1021#endif
1022    currently_fullscreen = 1;
1023
1024    /* Set the new resolution */
1025    okay = X11_ResizeFullScreen(this);
1026    if ( ! okay ) {
1027        X11_LeaveFullScreen(this);
1028    }
1029    /* Set the colormap */
1030    if ( SDL_XColorMap ) {
1031        XInstallColormap(SDL_Display, SDL_XColorMap);
1032    }
1033    if ( okay ) {
1034        X11_GrabInputNoLock(this, this->input_grab | SDL_GRAB_FULLSCREEN);
1035    }
1036
1037    /* We may need to refresh the screen at this point (no backing store)
1038       We also don't get an event, which is why we explicitly refresh. */
1039    if ( this->screen ) {
1040        if ( this->screen->flags & SDL_OPENGL ) {
1041            SDL_PrivateExpose();
1042        } else {
1043            X11_RefreshDisplay(this);
1044        }
1045    }
1046
1047    return(okay);
1048}
1049
1050int X11_LeaveFullScreen(_THIS)
1051{
1052    if ( currently_fullscreen ) {
1053        XReparentWindow(SDL_Display, SDL_Window, WMwindow, 0, 0);
1054#if SDL_VIDEO_DRIVER_X11_VIDMODE
1055        if ( use_vidmode ) {
1056            restore_mode(this);
1057            SDL_NAME(XF86VidModeLockModeSwitch)(SDL_Display, SDL_Screen, False);
1058        }
1059#endif
1060
1061#if SDL_VIDEO_DRIVER_X11_XME
1062        if ( use_xme ) {
1063            int rw, rh;
1064
1065            /* check current mode so we can avoid uneccessary mode changes */
1066            get_real_resolution(this, &rw, &rh);
1067
1068            if (rw != saved_res.width || rh != saved_res.height) {
1069                XiGMiscChangeResolution(SDL_Display,
1070                                        SDL_Screen,
1071                                        0, /* view */
1072                                        saved_res.width,
1073                                        saved_res.height,
1074                                        0);
1075                XSync(SDL_Display, False);
1076            }
1077        }
1078#endif
1079
1080#if SDL_VIDEO_DRIVER_X11_XRANDR
1081        if ( use_xrandr ) {
1082            XRRSetScreenConfig(SDL_Display, screen_config, SDL_Root,
1083                               saved_size_id, saved_rotation, CurrentTime);
1084        }
1085#endif
1086
1087        XUnmapWindow(SDL_Display, FSwindow);
1088        X11_WaitUnmapped(this, FSwindow);
1089        XSync(SDL_Display, True);   /* Flush spurious mode change events */
1090        currently_fullscreen = 0;
1091    }
1092    /* If we get popped out of fullscreen mode for some reason, input_grab
1093       will still have the SDL_GRAB_FULLSCREEN flag set, since this is only
1094       temporary.  In this case, release the grab unless the input has been
1095       explicitly grabbed.
1096     */
1097    X11_GrabInputNoLock(this, this->input_grab & ~SDL_GRAB_FULLSCREEN);
1098
1099    /* We may need to refresh the screen at this point (no backing store)
1100       We also don't get an event, which is why we explicitly refresh. */
1101    if ( this->screen ) {
1102        if ( this->screen->flags & SDL_OPENGL ) {
1103            SDL_PrivateExpose();
1104        } else {
1105            X11_RefreshDisplay(this);
1106        }
1107    }
1108
1109    return(0);
1110}
1111