comparison src/video/x11/SDL_x11modes.c @ 2873:b33e38aaa027

Progress on fullscreen mode switching on X11
author Sam Lantinga <slouken@libsdl.org>
date Tue, 16 Dec 2008 17:41:03 +0000
parents b801df19835f
children 36e312e0fac0
comparison
equal deleted inserted replaced
2872:762f0663c36a 2873:b33e38aaa027
21 */ 21 */
22 #include "SDL_config.h" 22 #include "SDL_config.h"
23 23
24 #include "SDL_x11video.h" 24 #include "SDL_x11video.h"
25 25
26 //#define X11MODES_DEBUG
27 #undef SDL_VIDEO_DRIVER_X11_XINERAMA
28 #undef SDL_VIDEO_DRIVER_X11_XRANDR
29 #undef SDL_VIDEO_DRIVER_X11_VIDMODE
26 30
27 static int 31 static int
28 get_visualinfo(Display * display, int screen, XVisualInfo * vinfo) 32 get_visualinfo(Display * display, int screen, XVisualInfo * vinfo)
29 { 33 {
30 const char *visual_id = SDL_getenv("SDL_VIDEO_X11_VISUALID"); 34 const char *visual_id = SDL_getenv("SDL_VIDEO_X11_VISUALID");
151 display.driverdata = displaydata; 155 display.driverdata = displaydata;
152 SDL_AddVideoDisplay(&display); 156 SDL_AddVideoDisplay(&display);
153 } 157 }
154 } 158 }
155 159
160 /* Global for the error handler */
161 int vm_event, vm_error = -1;
162
163 #if SDL_VIDEO_DRIVER_X11_XINERAMA
164 static SDL_bool
165 CheckXinerama(Display *display, int *major, int *minor)
166 {
167 const char *env;
168
169 /* Default the extension not available */
170 *major = *minor = 0;
171
172 /* Allow environment override */
173 env = getenv("SDL_VIDEO_X11_XINERAMA");
174 if (env && !SDL_atoi(env)) {
175 return SDL_FALSE;
176 }
177
178 /* Query the extension version */
179 if (!SDL_NAME(XineramaQueryExtension)(display, major, minor) ||
180 !SDL_NAME(XineramaIsActive)(display)) {
181 return SDL_FALSE;
182 }
183 return SDL_TRUE;
184 }
185 #endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
186
187 #if SDL_VIDEO_DRIVER_X11_XRANDR
188 static SDL_bool
189 CheckXRandR(Display *display, int *major, int *minor)
190 {
191 const char *env;
192
193 /* Default the extension not available */
194 *major = *minor = 0;
195
196 /* Allow environment override */
197 env = getenv("SDL_VIDEO_X11_XRANDR");
198 if (env && !SDL_atoi(env)) {
199 return SDL_FALSE;
200 }
201
202 if (!SDL_X11_HAVE_XRANDR) {
203 return SDL_FALSE;
204 }
205
206 /* Query the extension version */
207 if (!XRRQueryVersion(display, major, minor)) {
208 return SDL_FALSE;
209 }
210 return SDL_TRUE;
211 }
212 #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
213
214 #if SDL_VIDEO_DRIVER_X11_VIDMODE
215 static SDL_bool
216 CheckVidMode(Display *display, int *major, int *minor)
217 {
218 const char *env;
219
220 /* Default the extension not available */
221 *major = *minor = 0;
222
223 /* Allow environment override */
224 env = getenv("SDL_VIDEO_X11_VIDMODE");
225 if (env && !SDL_atoi(env)) {
226 return SDL_FALSE;
227 }
228
229 /* Query the extension version */
230 vm_error = -1;
231 if (!SDL_NAME(XF86VidModeQueryExtension)(display, &vm_event, &vm_error) ||
232 !SDL_NAME(XF86VidModeQueryVersion)(display, major, minor)) {
233 return SDL_FALSE;
234 }
235 return SDL_TRUE;
236 }
237
238 Bool SDL_NAME(XF86VidModeGetModeInfo)(Display *dpy, int scr, SDL_NAME(XF86VidModeModeInfo) *info)
239 {
240 Bool retval;
241 int dotclock;
242 SDL_NAME(XF86VidModeModeLine) l;
243 SDL_zerop(info);
244 SDL_zero(l);
245 retval = SDL_NAME(XF86VidModeGetModeLine)(dpy, scr, &dotclock, &l);
246 info->dotclock = dotclock;
247 info->hdisplay = l.hdisplay;
248 info->hsyncstart = l.hsyncstart;
249 info->hsyncend = l.hsyncend;
250 info->htotal = l.htotal;
251 info->hskew = l.hskew;
252 info->vdisplay = l.vdisplay;
253 info->vsyncstart = l.vsyncstart;
254 info->vsyncend = l.vsyncend;
255 info->vtotal = l.vtotal;
256 info->flags = l.flags;
257 info->privsize = l.privsize;
258 info->private = l.private;
259 return retval;
260 }
261
262 static int
263 calculate_rate(SDL_NAME(XF86VidModeModeInfo) *info)
264 {
265 return (info->htotal && info->vtotal) ? (1000 * info->dotclock / (info->htotal * info->vtotal)) : 0;
266 }
267
268 static void
269 save_mode(Display *display, SDL_DisplayData *data)
270 {
271 SDL_NAME(XF86VidModeGetModeInfo)(display, data->screen, &data->saved_mode);
272 SDL_NAME(XF86VidModeGetViewPort)(display, data->screen, &data->saved_view.x,&data->saved_view.y);
273 }
274
275 static void
276 restore_mode(Display *display, SDL_DisplayData *data)
277 {
278 SDL_NAME(XF86VidModeModeInfo) mode;
279
280 if (SDL_NAME(XF86VidModeGetModeInfo)(display, data->screen, &mode)) {
281 if (SDL_memcmp(&mode, &data->saved_mode, sizeof(mode)) != 0) {
282 SDL_NAME(XF86VidModeSwitchToMode)(display, data->screen, &data->saved_mode);
283 }
284 }
285 if ((data->saved_view.x != 0) || (data->saved_view.y != 0)) {
286 SDL_NAME(XF86VidModeSetViewPort)(display, data->screen, data->saved_view.x, data->saved_view.y);
287 }
288 }
289 #endif /* SDL_VIDEO_DRIVER_X11_VIDMODE */
290
156 void 291 void
157 X11_GetDisplayModes(_THIS) 292 X11_GetDisplayModes(_THIS)
158 { 293 {
159 Display *display = ((SDL_VideoData *) _this->driverdata)->display; 294 Display *display = ((SDL_VideoData *) _this->driverdata)->display;
160 SDL_DisplayData *data = (SDL_DisplayData *) SDL_CurrentDisplay.driverdata; 295 SDL_DisplayData *data = (SDL_DisplayData *) SDL_CurrentDisplay.driverdata;
296 #if SDL_VIDEO_DRIVER_X11_XINERAMA
297 int xinerama_major, xinerama_minor;
298 int screens;
299 SDL_NAME(XineramaScreenInfo) *xinerama;
300 #endif
301 #if SDL_VIDEO_DRIVER_X11_XRANDR
302 int xrandr_major, xrandr_minor;
303 int nsizes, nrates;
304 XRRScreenSize *sizes;
305 short *rates;
306 #endif
307 #if SDL_VIDEO_DRIVER_X11_VIDMODE
308 int vm_major, vm_minor;
309 int nmodes;
310 SDL_NAME(XF86VidModeModeInfo) **modes;
311 #endif
312 int screen_w;
313 int screen_h;
161 SDL_DisplayMode mode; 314 SDL_DisplayMode mode;
162 315
316 /* Unfortunately X11 requires the window to be created with the correct
317 * visual and depth ahead of time, but the SDL API allows you to create
318 * a window before setting the fullscreen display mode. This means that
319 * we have to use the same format for all windows and all display modes.
320 * (or support recreating the window with a new visual behind the scenes)
321 */
322 mode.format = SDL_CurrentDisplay.current_mode.format;
323 mode.driverdata = NULL;
324
325 data->use_xinerama = 0;
326 data->use_xrandr = 0;
327 data->use_vidmode = 0;
328 screen_w = DisplayWidth(display, data->screen);
329 screen_h = DisplayHeight(display, data->screen);
330
331 #if SDL_VIDEO_DRIVER_X11_XINERAMA
332 /* Query Xinerama extention */
333 if (CheckXinerama(display, &xinerama_major, &xinerama_minor)) {
334 #ifdef X11MODES_DEBUG
335 printf("X11 detected Xinerama:\n");
336 #endif
337 xinerama = SDL_NAME(XineramaQueryScreens)(display, &screens);
338 if (xinerama) {
339 int i;
340 for (i = 0; i < screens; i++) {
341 #ifdef X11MODES_DEBUG
342 printf("xinerama %d: %dx%d+%d+%d\n",
343 xinerama[i].screen_number,
344 xinerama[i].width, xinerama[i].height,
345 xinerama[i].x_org, xinerama[i].y_org);
346 #endif
347 if (xinerama[i].screen_number == data->screen) {
348 data->use_xinerama = xinerama_major * 100 + xinerama_minor;
349 data->xinerama_info = xinerama[i];
350 }
351 }
352 XFree(xinerama);
353 }
354
355 if (data->use_xinerama) {
356 /* Add the full xinerama mode */
357 if (screen_w > data->xinerama_info.width ||
358 screen_h > data->xinerama_info.height) {
359 mode.w = screen_w;
360 mode.h = screen_h;
361 mode.refresh_rate = 0;
362 SDL_AddDisplayMode(_this->current_display, &mode);
363 }
364
365 /* Add the head xinerama mode */
366 mode.w = data->xinerama_info.width;
367 mode.h = data->xinerama_info.height;
368 mode.refresh_rate = 0;
369 SDL_AddDisplayMode(_this->current_display, &mode);
370 }
371 }
372 #endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
373
374 #if SDL_VIDEO_DRIVER_X11_XRANDR
375 /* XRandR */
376 /* require at least XRandR v1.0 (arbitrary) */
377 if (CheckXRandR(display, &xrandr_major, &xrandr_minor) && xrandr_major >= 1) {
378 #ifdef X11MODES_DEBUG
379 fprintf(stderr, "XRANDR: XRRQueryVersion: V%d.%d\n",
380 xrandr_major, xrandr_minor);
381 #endif
382
383 /* save the screen configuration since we must reference it
384 each time we toggle modes.
385 */
386 data->screen_config = XRRGetScreenInfo(display, RootWindow(display, data->screen));
387
388 /* retrieve the list of resolution */
389 sizes = XRRConfigSizes(data->screen_config, &nsizes);
390 if (nsizes > 0) {
391 int i, j;
392 for ( i=0; i < nsizes; i++) {
393 mode.w = sizes[i].width;
394 mode.h = sizes[i].height;
395
396 rates = XRRConfigRates(data->screen_config, i, &nrates);
397 for (j = 0; j < nrates; ++j) {
398 mode.refresh_rate = rates[j];
399 #ifdef X11MODES_DEBUG
400 fprintf(stderr, "XRANDR: mode = %4d[%d], w = %4d, h = %4d, rate = %4d\n",
401 i, j, mode.w, mode.h, mode.refresh_rate);
402 #endif
403 SDL_AddDisplayMode(_this->current_display, &mode);
404 }
405 }
406
407 data->use_xrandr = xrandr_major * 100 + xrandr_minor;
408 data->saved_size = XRRConfigCurrentConfiguration(data->screen_config, &data->saved_rotation);
409 data->saved_rate = XRRConfigCurrentRate(data->screen_config);
410 }
411 }
412 #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
413
414 #if SDL_VIDEO_DRIVER_X11_VIDMODE
415 /* XVidMode */
416 if (!data->use_xrandr &&
417 #if SDL_VIDEO_DRIVER_X11_XINERAMA
418 (!data->use_xinerama || data->xinerama_info.screen_number == 0) &&
419 #endif
420 CheckVidMode(display, &vm_major, &vm_minor) &&
421 SDL_NAME(XF86VidModeGetAllModeLines)(display, data->screen, &nmodes, &modes) )
422 {
423 int i;
424
425 #ifdef X11MODES_DEBUG
426 printf("VidMode modes: (unsorted)\n");
427 for (i = 0; i < nmodes; ++i) {
428 printf("Mode %d: %d x %d @ %d\n", i,
429 modes[i]->hdisplay, modes[i]->vdisplay,
430 calculate_rate(modes[i]));
431 }
432 #endif
433 for (i = 0; i < nmodes; ++i) {
434 mode.w = modes[i]->hdisplay;
435 mode.h = modes[i]->vdisplay;
436 mode.refresh_rate = calculate_rate(modes[i]);
437 SDL_AddDisplayMode(_this->current_display, &mode);
438 }
439 XFree(modes);
440
441 data->use_vidmode = vm_major * 100 + vm_minor;
442 save_mode(display, data);
443 }
444 #endif /* SDL_VIDEO_DRIVER_X11_VIDMODE */
445
446 if (!data->use_xrandr && !data->use_vidmode) {
447 mode.w = screen_w;
448 mode.h = screen_h;
449 mode.refresh_rate = 0;
450 SDL_AddDisplayMode(_this->current_display, &mode);
451 }
452
453 #ifdef X11MODES_DEBUG
454 if (data->use_xinerama) {
455 printf("Xinerama is enabled\n");
456 }
457
458 if (data->use_xrandr) {
459 printf("XRandR is enabled\n");
460 }
461
462 if (data->use_vidmode) {
463 printf("VidMode is enabled\n");
464 }
465 #endif /* X11MODES_DEBUG */
466 }
467
468 static void
469 get_real_resolution(Display *display, SDL_DisplayData *data, int *w, int *h, int *rate)
470 {
471 #if SDL_VIDEO_DRIVER_X11_XRANDR
472 if (data->use_xrandr) {
473 int nsizes;
474 XRRScreenSize *sizes;
475
476 sizes = XRRConfigSizes(data->screen_config, &nsizes);
477 if (nsizes > 0) {
478 int cur_size;
479 Rotation cur_rotation;
480
481 cur_size = XRRConfigCurrentConfiguration(data->screen_config, &cur_rotation);
482 *w = sizes[cur_size].width;
483 *h = sizes[cur_size].height;
484 *rate = XRRConfigCurrentRate(data->screen_config);
485 #ifdef X11MODES_DEBUG
486 fprintf(stderr, "XRANDR: get_real_resolution: w = %d, h = %d, rate = %d\n", *w, *h, *rate);
487 #endif
488 return;
489 }
490 }
491 #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
492
493 #if SDL_VIDEO_DRIVER_X11_VIDMODE
494 if (data->use_vidmode) {
495 SDL_NAME(XF86VidModeModeInfo) mode;
496
497 if (SDL_NAME(XF86VidModeGetModeInfo)(display, data->screen, &mode)) {
498 *w = mode.hdisplay;
499 *h = mode.vdisplay;
500 *rate = calculate_rate(&mode);
501 return;
502 }
503 }
504 #endif /* SDL_VIDEO_DRIVER_X11_VIDMODE */
505
506 #if SDL_VIDEO_DRIVER_X11_XINERAMA
507 if (data->use_xinerama) {
508 *w = data->xinerama_info.width;
509 *h = data->xinerama_info.height;
510 *rate = 0;
511 return;
512 }
513 #endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */
514
515 *w = DisplayWidth(display, data->screen);
516 *h = DisplayHeight(display, data->screen);
517 *rate = 0;
518 }
519
520 static void
521 set_best_resolution(Display *display, SDL_DisplayData *data, int w, int h, int rate)
522 {
523 int real_w, real_h, real_rate;
524
525 /* check current mode so we can avoid uneccessary mode changes */
526 get_real_resolution(display, data, &real_w, &real_h, &real_rate);
527 if (w == real_w && h == real_h && (!rate || rate == real_rate)) {
528 return;
529 }
530
531 #if SDL_VIDEO_DRIVER_X11_XRANDR
532 if (data->use_xrandr) {
533 #ifdef X11MODES_DEBUG
534 fprintf(stderr, "XRANDR: set_best_resolution(): w = %d, h = %d\n",
535 w, h);
536 #endif
537 int i, nsizes, nrates;
538 int best;
539 int best_rate;
540 XRRScreenSize *sizes;
541 short *rates;
542
543 /* find the smallest resolution that is at least as big as the user requested */
544 best = -1;
545 sizes = XRRConfigSizes(data->screen_config, &nsizes);
546 for (i = 0; i < nsizes; ++i) {
547 if (sizes[i].width < w || sizes[i].height < h) {
548 continue;
549 }
550 if (sizes[i].width == w && sizes[i].height == h) {
551 best = i;
552 break;
553 }
554 if (best == -1 ||
555 (sizes[i].width < sizes[best].width) ||
556 (sizes[i].width == sizes[best].width && sizes[i].height < sizes[best].height)) {
557 best = i;
558 }
559 }
560
561 if (best >= 0) {
562 best_rate = 0;
563 rates = XRRConfigRates(data->screen_config, best, &nrates);
564 for (i = 0; i < nrates; ++i) {
565 if (rates[i] == rate) {
566 best_rate = rate;
567 break;
568 }
569 if (!rate) {
570 /* Higher is better, right? */
571 if (rates[i] > best_rate) {
572 best_rate = rates[i];
573 }
574 } else {
575 if (SDL_abs(rates[i]-rate) < SDL_abs(best_rate-rate)) {
576 best_rate = rates[i];
577 }
578 }
579 }
580 XRRSetScreenConfigAndRate(display, data->screen_config, RootWindow(display, data->screen), best, data->saved_rotation, best_rate, CurrentTime);
581 }
582 return;
583 }
584 #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */
585
586 #if SDL_VIDEO_DRIVER_X11_VIDMODE
587 if (data->use_vidmode) {
588 SDL_NAME(XF86VidModeModeInfo) **modes;
589 int i, nmodes;
590 int best;
591
592 if (SDL_NAME(XF86VidModeGetAllModeLines)(display,data->screen,&nmodes,&modes)) {
593 best = -1;
594 for (i = 0; i < nmodes; ++i) {
595 if (modes[i]->hdisplay < w || modes[i]->vdisplay < h) {
596 continue;
597 }
598 if (best == -1 ||
599 (modes[i]->hdisplay < modes[best]->hdisplay) ||
600 (modes[i]->hdisplay == modes[best]->hdisplay && modes[i]->vdisplay < modes[best]->vdisplay)) {
601 best = i;
602 continue;
603 }
604 if ((modes[i]->hdisplay == modes[best]->hdisplay) &&
605 (modes[i]->vdisplay == modes[best]->vdisplay)) {
606 if (!rate) {
607 /* Higher is better, right? */
608 if (calculate_rate(modes[i]) > calculate_rate(modes[best])) {
609 best = i;
610 }
611 } else {
612 if (SDL_abs(calculate_rate(modes[i])-rate) < SDL_abs(calculate_rate(modes[best])-rate)) {
613 best = i;
614 }
615 }
616 }
617 }
618 if (best >= 0) {
619 #ifdef X11MODES_DEBUG
620 printf("Best Mode %d: %d x %d @ %d\n", best,
621 modes[best]->hdisplay, modes[best]->vdisplay,
622 calculate_rate(modes[best]));
623 #endif
624 SDL_NAME(XF86VidModeSwitchToMode)(display, data->screen, modes[best]);
625 }
626 XFree(modes);
627 }
628 return;
629 }
630 #endif /* SDL_VIDEO_DRIVER_X11_VIDMODE */
163 } 631 }
164 632
165 int 633 int
166 X11_SetDisplayMode(_THIS, SDL_DisplayMode * mode) 634 X11_SetDisplayMode(_THIS, SDL_DisplayMode * mode)
167 { 635 {
168 //SDL_DisplayModeData *data = (SDL_DisplayModeData *) mode->driverdata; 636 Display *display = ((SDL_VideoData *) _this->driverdata)->display;
169 return -1; 637 SDL_DisplayData *data = (SDL_DisplayData *) SDL_CurrentDisplay.driverdata;
638
639 set_best_resolution(display, data, mode->w, mode->h, mode->refresh_rate);
640 return 0;
170 } 641 }
171 642
172 void 643 void
173 X11_QuitModes(_THIS) 644 X11_QuitModes(_THIS)
174 { 645 {