comparison src/video/x11/SDL_x11modes.c @ 0:74212992fb08

Initial revision
author Sam Lantinga <slouken@lokigames.com>
date Thu, 26 Apr 2001 16:45:43 +0000
parents
children 71774090f286
comparison
equal deleted inserted replaced
-1:000000000000 0:74212992fb08
1 /*
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 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 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19 Sam Lantinga
20 slouken@devolution.com
21 */
22
23 #ifdef SAVE_RCSID
24 static char rcsid =
25 "@(#) $Id$";
26 #endif
27
28 /* Utilities for getting and setting the X display mode */
29
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include "SDL_timer.h"
34 #include "SDL_error.h"
35 #include "SDL_events.h"
36 #include "SDL_events_c.h"
37 #include "SDL_x11video.h"
38 #include "SDL_x11wm_c.h"
39 #include "SDL_x11modes_c.h"
40
41
42 #ifdef XFREE86_VM
43 Bool XVidMode(GetModeInfo, (Display *dpy, int scr, XF86VidModeModeInfo *info))
44 {
45 XF86VidModeModeLine *l = (XF86VidModeModeLine*)((char*)info + sizeof info->dotclock);
46 return XVidMode(GetModeLine, (dpy, scr, &info->dotclock, l));
47 }
48 #endif /* XFREE86_VM */
49
50 #ifdef XFREE86_VM
51 static void save_mode(_THIS)
52 {
53 memset(&saved_mode, 0, sizeof(saved_mode));
54 XVidMode(GetModeInfo, (SDL_Display,SDL_Screen,&saved_mode));
55 XVidMode(GetViewPort, (SDL_Display,SDL_Screen,&saved_view.x,&saved_view.y));
56 }
57 #endif
58
59 #ifdef XFREE86_VM
60 static void restore_mode(_THIS)
61 {
62 XF86VidModeModeLine mode;
63 int unused;
64
65 if ( XVidMode(GetModeLine, (SDL_Display, SDL_Screen, &unused, &mode)) ) {
66 if ( (saved_mode.hdisplay != mode.hdisplay) ||
67 (saved_mode.vdisplay != mode.vdisplay) ) {
68 XVidMode(SwitchToMode, (SDL_Display, SDL_Screen, &saved_mode));
69 }
70 }
71 if ( (saved_view.x != 0) || (saved_view.y != 0) ) {
72 XVidMode(SetViewPort, (SDL_Display, SDL_Screen, saved_view.x, saved_view.y));
73 }
74 }
75 #endif
76
77 #ifdef XFREE86_VM
78 static int cmpmodes(const void *va, const void *vb)
79 {
80 XF86VidModeModeInfo *a = *(XF86VidModeModeInfo**)va;
81 XF86VidModeModeInfo *b = *(XF86VidModeModeInfo**)vb;
82 if(a->hdisplay > b->hdisplay)
83 return -1;
84 return b->vdisplay - a->vdisplay;
85 }
86 #endif
87
88 static void set_best_resolution(_THIS, int width, int height)
89 {
90 #ifdef XFREE86_VM
91 if ( use_vidmode ) {
92 XF86VidModeModeLine mode;
93 XF86VidModeModeInfo **modes;
94 int i;
95 int nmodes;
96
97 if ( XVidMode(GetModeLine, (SDL_Display, SDL_Screen, &i, &mode)) &&
98 XVidMode(GetAllModeLines, (SDL_Display,SDL_Screen,&nmodes,&modes))){
99 qsort(modes, nmodes, sizeof *modes, cmpmodes);
100 #ifdef XFREE86_DEBUG
101 printf("Available modes:\n");
102 for ( i = 0; i < nmodes; ++i ) {
103 printf("Mode %d: %dx%d\n", i, modes[i]->hdisplay, modes[i]->vdisplay);
104 }
105 #endif
106 for ( i = nmodes-1; i > 0 ; --i ) {
107 if ( (modes[i]->hdisplay >= width) &&
108 (modes[i]->vdisplay >= height) )
109 break;
110 }
111 if ( (modes[i]->hdisplay != mode.hdisplay) ||
112 (modes[i]->vdisplay != mode.vdisplay) ) {
113 XVidMode(SwitchToMode, (SDL_Display, SDL_Screen, modes[i]));
114 }
115 XFree(modes);
116 }
117 }
118 #endif /* XFREE86_VM */
119 }
120
121 static void get_real_resolution(_THIS, int* w, int* h)
122 {
123 #ifdef XFREE86_VM
124 if ( use_vidmode ) {
125 XF86VidModeModeLine mode;
126 int unused;
127
128 if ( XVidMode(GetModeLine, (SDL_Display, SDL_Screen, &unused, &mode)) ) {
129 *w = mode.hdisplay;
130 *h = mode.vdisplay;
131 return;
132 }
133 }
134 #endif
135 *w = DisplayWidth(SDL_Display, SDL_Screen);
136 *h = DisplayHeight(SDL_Display, SDL_Screen);
137 }
138
139 /* Called after mapping a window - waits until the window is mapped */
140 void X11_WaitMapped(_THIS, Window win)
141 {
142 XEvent event;
143 do {
144 XMaskEvent(SDL_Display, StructureNotifyMask, &event);
145 } while ( (event.type != MapNotify) || (event.xmap.event != win) );
146 }
147
148 /* Called after unmapping a window - waits until the window is unmapped */
149 void X11_WaitUnmapped(_THIS, Window win)
150 {
151 XEvent event;
152 do {
153 XMaskEvent(SDL_Display, StructureNotifyMask, &event);
154 } while ( (event.type != UnmapNotify) || (event.xunmap.event != win) );
155 }
156
157 static void move_cursor_to(_THIS, int x, int y)
158 {
159 XWarpPointer(SDL_Display, None, SDL_Root, 0, 0, 0, 0, x, y);
160 }
161
162 static int add_visual(_THIS, int depth, int class)
163 {
164 XVisualInfo vi;
165 if(XMatchVisualInfo(SDL_Display, SDL_Screen, depth, class, &vi)) {
166 int n = this->hidden->nvisuals;
167 this->hidden->visuals[n].depth = vi.depth;
168 this->hidden->visuals[n].visual = vi.visual;
169 this->hidden->nvisuals++;
170 }
171 return(this->hidden->nvisuals);
172 }
173 static int add_visual_byid(_THIS, const char *visual_id)
174 {
175 XVisualInfo *vi, template;
176 int nvis;
177
178 if ( visual_id ) {
179 memset(&template, 0, (sizeof template));
180 template.visualid = strtol(visual_id, NULL, 0);
181 vi = XGetVisualInfo(SDL_Display, VisualIDMask, &template, &nvis);
182 if ( vi ) {
183 int n = this->hidden->nvisuals;
184 this->hidden->visuals[n].depth = vi->depth;
185 this->hidden->visuals[n].visual = vi->visual;
186 this->hidden->nvisuals++;
187 XFree(vi);
188 }
189 }
190 return(this->hidden->nvisuals);
191 }
192
193 /* Global for the error handler */
194 int vm_event, vm_error = -1;
195
196 int X11_GetVideoModes(_THIS)
197 {
198 #ifdef XFREE86_VM
199 int buggy_X11;
200 int vm_major, vm_minor;
201 int nmodes;
202 XF86VidModeModeInfo **modes;
203 #endif
204 int i;
205
206 vm_error = -1;
207 use_vidmode = 0;
208 #ifdef XFREE86_VM
209 /* Metro-X 4.3.0 and earlier has a broken implementation of
210 XF86VidModeGetAllModeLines() - it hangs the client.
211 */
212 buggy_X11 = 0;
213 if ( strcmp(ServerVendor(SDL_Display), "Metro Link Incorporated") == 0 ) {
214 FILE *metro_fp;
215
216 metro_fp = fopen("/usr/X11R6/lib/X11/Metro/.version", "r");
217 if ( metro_fp != NULL ) {
218 int major, minor, patch, version;
219 major = 0; minor = 0; patch = 0;
220 fscanf(metro_fp, "%d.%d.%d", &major, &minor, &patch);
221 version = major*100+minor*10+patch;
222 if ( version < 431 ) {
223 buggy_X11 = 1;
224 }
225 fclose(metro_fp);
226 }
227 }
228 #if defined(__alpha__) || defined(__powerpc__)
229 /* The alpha and PPC XFree86 servers are also buggy */
230 buggy_X11 = 1;
231 #endif
232 /* Enumerate the available fullscreen modes */
233 if ( ! buggy_X11 ) {
234 if ( XVidMode(QueryExtension, (SDL_Display, &vm_event, &vm_error)) &&
235 XVidMode(QueryVersion, (SDL_Display, &vm_major, &vm_minor)) ) {
236 #ifdef BROKEN_XFREE86_4001
237 #ifdef X_XF86VidModeGetDotClocks /* Compiled under XFree86 4.0 */
238 /* Earlier X servers hang when doing vidmode */
239 if ( vm_major < 2 ) {
240 #ifdef DEBUG_XF86
241 printf("Compiled under XFree86 4.0, server is XFree86 3.X\n");
242 #endif
243 buggy_X11 = 1;
244 }
245 #else
246 /* XFree86 3.X code works with XFree86 4.0 servers */;
247 #endif /* XFree86 4.0 */
248 #endif /* XFree86 4.02 and newer are fixed wrt backwards compatibility */
249 } else {
250 buggy_X11 = 1;
251 }
252 }
253 if ( ! buggy_X11 &&
254 XVidMode(GetAllModeLines, (SDL_Display, SDL_Screen,&nmodes,&modes)) ) {
255
256 qsort(modes, nmodes, sizeof *modes, cmpmodes);
257 SDL_modelist = (SDL_Rect **)malloc((nmodes+1)*sizeof(SDL_Rect *));
258 if ( SDL_modelist ) {
259 for ( i=0; i<nmodes; ++i ) {
260 SDL_modelist[i] = (SDL_Rect *)malloc(sizeof(SDL_Rect));
261 if ( SDL_modelist[i] == NULL ) {
262 break;
263 }
264 SDL_modelist[i]->x = 0;
265 SDL_modelist[i]->y = 0;
266 SDL_modelist[i]->w = modes[i]->hdisplay;
267 SDL_modelist[i]->h = modes[i]->vdisplay;
268 }
269 SDL_modelist[i] = NULL;
270 }
271 XFree(modes);
272
273 use_vidmode = 1;
274 save_mode(this);
275 }
276 #endif /* XFREE86_VM */
277
278 {
279 static int depth_list[] = { 32, 24, 16, 15, 8 };
280 int j, np;
281 int use_directcolor = 1;
282 XPixmapFormatValues *pf;
283
284 /* Search for the visuals in deepest-first order, so that the first
285 will be the richest one */
286 if ( getenv("SDL_VIDEO_X11_NODIRECTCOLOR") ) {
287 use_directcolor = 0;
288 }
289 this->hidden->nvisuals = 0;
290 if ( ! add_visual_byid(this, getenv("SDL_VIDEO_X11_VISUALID")) ) {
291 for ( i=0; i<SDL_TABLESIZE(depth_list); ++i ) {
292 if ( depth_list[i] > 8 ) {
293 if ( use_directcolor ) {
294 add_visual(this, depth_list[i], DirectColor);
295 }
296 add_visual(this, depth_list[i], TrueColor);
297 } else {
298 add_visual(this, depth_list[i], PseudoColor);
299 add_visual(this, depth_list[i], StaticColor);
300 }
301 }
302 }
303 if ( this->hidden->nvisuals == 0 ) {
304 SDL_SetError("Found no sufficiently capable X11 visuals");
305 return -1;
306 }
307
308 /* look up the pixel quantum for each depth */
309 pf = XListPixmapFormats(SDL_Display, &np);
310 for(i = 0; i < this->hidden->nvisuals; i++) {
311 int d = this->hidden->visuals[i].depth;
312 for(j = 0; j < np; j++)
313 if(pf[j].depth == d)
314 break;
315 this->hidden->visuals[i].bpp = j < np ? pf[j].bits_per_pixel : d;
316 }
317
318 XFree(pf);
319 }
320
321 if ( SDL_modelist == NULL ) {
322 SDL_modelist = (SDL_Rect **)malloc((1+1)*sizeof(SDL_Rect *));
323 i = 0;
324 if ( SDL_modelist ) {
325 SDL_modelist[i] = (SDL_Rect *)malloc(sizeof(SDL_Rect));
326 if ( SDL_modelist[i] ) {
327 SDL_modelist[i]->x = 0;
328 SDL_modelist[i]->y = 0;
329 SDL_modelist[i]->w = DisplayWidth(SDL_Display, SDL_Screen);
330 SDL_modelist[i]->h = DisplayHeight(SDL_Display, SDL_Screen);
331 ++i;
332 }
333 SDL_modelist[i] = NULL;
334 }
335 }
336
337 #ifdef DEBUG_XF86
338 if ( use_vidmode ) {
339 fprintf(stderr, "XFree86 VidMode is enabled\n");
340 }
341 if ( SDL_modelist ) {
342 fprintf(stderr, "X11 video mode list:\n");
343 for ( i=0; SDL_modelist[i]; ++i ) {
344 fprintf(stderr, "\t%dx%d\n",
345 SDL_modelist[i]->w, SDL_modelist[i]->h);
346 }
347 }
348 #endif /* DEBUG_XF86 */
349 return 0;
350 }
351
352 int X11_SupportedVisual(_THIS, SDL_PixelFormat *format)
353 {
354 int i;
355 for(i = 0; i < this->hidden->nvisuals; i++)
356 if(this->hidden->visuals[i].bpp == format->BitsPerPixel)
357 return 1;
358 return 0;
359 }
360
361 SDL_Rect **X11_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags)
362 {
363 if ( X11_SupportedVisual(this, format) ) {
364 if ( flags & SDL_FULLSCREEN ) {
365 return(SDL_modelist);
366 } else {
367 return((SDL_Rect **)-1);
368 }
369 } else {
370 return((SDL_Rect **)0);
371 }
372 }
373
374 void X11_FreeVideoModes(_THIS)
375 {
376 int i;
377
378 if ( SDL_modelist ) {
379 for ( i=0; SDL_modelist[i]; ++i ) {
380 free(SDL_modelist[i]);
381 }
382 free(SDL_modelist);
383 SDL_modelist = NULL;
384 }
385 }
386
387 int X11_ResizeFullScreen(_THIS)
388 {
389 int x, y;
390 int real_w, real_h;
391
392 if ( currently_fullscreen ) {
393 /* Switch resolution and cover it with the FSwindow */
394 move_cursor_to(this, 0, 0);
395 set_best_resolution(this, current_w, current_h);
396 move_cursor_to(this, 0, 0);
397 get_real_resolution(this, &real_w, &real_h);
398 XResizeWindow(SDL_Display, FSwindow, real_w, real_h);
399 move_cursor_to(this, real_w/2, real_h/2);
400
401 /* Center and reparent the drawing window */
402 x = (real_w - current_w)/2;
403 y = (real_h - current_h)/2;
404 XReparentWindow(SDL_Display, SDL_Window, FSwindow, x, y);
405 /* FIXME: move the mouse to the old relative location */
406 XSync(SDL_Display, True); /* Flush spurious mode change events */
407 }
408 return(1);
409 }
410
411 void X11_QueueEnterFullScreen(_THIS)
412 {
413 switch_waiting = 0x01 | SDL_FULLSCREEN;
414 switch_time = SDL_GetTicks() + 1500;
415 #if 0 /* This causes a BadMatch error if the window is iconified (not needed) */
416 XSetInputFocus(SDL_Display, WMwindow, RevertToNone, CurrentTime);
417 #endif
418 }
419
420 int X11_EnterFullScreen(_THIS)
421 {
422 int okay;
423 #if 0
424 Window tmpwin, *windows;
425 int i, nwindows;
426 #endif
427
428 okay = 1;
429 if ( ! currently_fullscreen ) {
430 int real_w, real_h;
431
432 /* Map the fullscreen window to blank the screen */
433 get_real_resolution(this, &real_w, &real_h);
434 XResizeWindow(SDL_Display, FSwindow, real_w, real_h);
435 XMapRaised(SDL_Display, FSwindow);
436 X11_WaitMapped(this, FSwindow);
437
438 #if 0 /* This seems to break WindowMaker in focus-follows-mouse mode */
439 /* Make sure we got to the top of the window stack */
440 if ( XQueryTree(SDL_Display, SDL_Root, &tmpwin, &tmpwin,
441 &windows, &nwindows) && windows ) {
442 /* If not, try to put us there - if fail... oh well */
443 if ( windows[nwindows-1] != FSwindow ) {
444 tmpwin = windows[nwindows-1];
445 for ( i=0; i<nwindows; ++i ) {
446 if ( windows[i] == FSwindow ) {
447 memcpy(&windows[i], &windows[i+1],
448 (nwindows-i-1)*sizeof(windows[i]));
449 break;
450 }
451 }
452 windows[nwindows-1] = FSwindow;
453 XRestackWindows(SDL_Display, windows, nwindows);
454 XSync(SDL_Display, False);
455 }
456 XFree(windows);
457 }
458 #else
459 XRaiseWindow(SDL_Display, FSwindow);
460 #endif
461
462 /* Grab the mouse on the fullscreen window
463 The event handling will know when we become active, and then
464 enter fullscreen mode if we can't grab the mouse this time.
465 */
466 #ifdef GRAB_FULLSCREEN
467 if ( (XGrabPointer(SDL_Display, FSwindow, True, 0,
468 GrabModeAsync, GrabModeAsync,
469 FSwindow, None, CurrentTime) != GrabSuccess) ||
470 (XGrabKeyboard(SDL_Display, WMwindow, True,
471 GrabModeAsync, GrabModeAsync, CurrentTime) != 0) ) {
472 #else
473 if ( XGrabPointer(SDL_Display, FSwindow, True, 0,
474 GrabModeAsync, GrabModeAsync,
475 FSwindow, None, CurrentTime) != GrabSuccess ) {
476 #endif
477 /* We lost the grab, so try again later */
478 XUnmapWindow(SDL_Display, FSwindow);
479 X11_WaitUnmapped(this, FSwindow);
480 X11_QueueEnterFullScreen(this);
481 return(0);
482 }
483 #ifdef GRAB_FULLSCREEN
484 SDL_PrivateAppActive(1, SDL_APPINPUTFOCUS);
485 #endif
486
487 #ifdef XFREE86_VM
488 /* Save the current video mode */
489 if ( use_vidmode ) {
490 XVidMode(LockModeSwitch, (SDL_Display, SDL_Screen, True));
491 }
492 #endif
493 currently_fullscreen = 1;
494
495 /* Set the new resolution */
496 okay = X11_ResizeFullScreen(this);
497 if ( ! okay ) {
498 X11_LeaveFullScreen(this);
499 }
500 /* Set the colormap */
501 if ( SDL_XColorMap ) {
502 XInstallColormap(SDL_Display, SDL_XColorMap);
503 }
504 }
505 X11_GrabInputNoLock(this, this->input_grab | SDL_GRAB_FULLSCREEN);
506 return(okay);
507 }
508
509 int X11_LeaveFullScreen(_THIS)
510 {
511 if ( currently_fullscreen ) {
512 XReparentWindow(SDL_Display, SDL_Window, WMwindow, 0, 0);
513 #ifdef XFREE86_VM
514 if ( use_vidmode ) {
515 restore_mode(this);
516 XVidMode(LockModeSwitch, (SDL_Display, SDL_Screen, False));
517 }
518 #endif
519 XUnmapWindow(SDL_Display, FSwindow);
520 X11_WaitUnmapped(this, FSwindow);
521 #ifdef GRAB_FULLSCREEN
522 XUngrabKeyboard(SDL_Display, CurrentTime);
523 #endif
524 XSync(SDL_Display, True); /* Flush spurious mode change events */
525 currently_fullscreen = 0;
526 }
527 /* If we get popped out of fullscreen mode for some reason, input_grab
528 will still have the SDL_GRAB_FULLSCREEN flag set, since this is only
529 temporary. In this case, release the grab unless the input has been
530 explicitly grabbed.
531 */
532 X11_GrabInputNoLock(this, this->input_grab & ~SDL_GRAB_FULLSCREEN);
533 return(0);
534 }