| /* |
| Simple DirectMedia Layer |
| Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org> |
| |
| This software is provided 'as-is', without any express or implied |
| warranty. In no event will the authors be held liable for any damages |
| arising from the use of this software. |
| |
| Permission is granted to anyone to use this software for any purpose, |
| including commercial applications, and to alter it and redistribute it |
| freely, subject to the following restrictions: |
| |
| 1. The origin of this software must not be misrepresented; you must not |
| claim that you wrote the original software. If you use this software |
| in a product, an acknowledgment in the product documentation would be |
| appreciated but is not required. |
| 2. Altered source versions must be plainly marked as such, and must not be |
| misrepresented as being the original software. |
| 3. This notice may not be removed or altered from any source distribution. |
| */ |
| #include "SDL_config.h" |
| |
| #if SDL_VIDEO_DRIVER_X11 |
| |
| #include "SDL_hints.h" |
| #include "SDL_x11video.h" |
| #include "edid.h" |
| |
| /*#define X11MODES_DEBUG*/ |
| |
| /* I'm becoming more and more convinced that the application should never |
| * use XRandR, and it's the window manager's responsibility to track and |
| * manage display modes for fullscreen windows. Right now XRandR is completely |
| * broken with respect to window manager behavior on every window manager that |
| * I can find. For example, on Unity 3D if you show a fullscreen window while |
| * the resolution is changing (within ~250 ms) your window will retain the |
| * fullscreen state hint but be decorated and windowed. |
| * |
| * However, many people swear by it, so let them swear at it. :) |
| */ |
| /*#define XRANDR_DISABLED_BY_DEFAULT*/ |
| |
| |
| static int |
| get_visualinfo(Display * display, int screen, XVisualInfo * vinfo) |
| { |
| const char *visual_id = SDL_getenv("SDL_VIDEO_X11_VISUALID"); |
| int depth; |
| |
| /* Look for an exact visual, if requested */ |
| if (visual_id) { |
| XVisualInfo *vi, template; |
| int nvis; |
| |
| SDL_zero(template); |
| template.visualid = SDL_strtol(visual_id, NULL, 0); |
| vi = XGetVisualInfo(display, VisualIDMask, &template, &nvis); |
| if (vi) { |
| *vinfo = *vi; |
| XFree(vi); |
| return 0; |
| } |
| } |
| |
| depth = DefaultDepth(display, screen); |
| if ((X11_UseDirectColorVisuals() && |
| XMatchVisualInfo(display, screen, depth, DirectColor, vinfo)) || |
| XMatchVisualInfo(display, screen, depth, TrueColor, vinfo) || |
| XMatchVisualInfo(display, screen, depth, PseudoColor, vinfo) || |
| XMatchVisualInfo(display, screen, depth, StaticColor, vinfo)) { |
| return 0; |
| } |
| return -1; |
| } |
| |
| int |
| X11_GetVisualInfoFromVisual(Display * display, Visual * visual, XVisualInfo * vinfo) |
| { |
| XVisualInfo *vi; |
| int nvis; |
| |
| vinfo->visualid = XVisualIDFromVisual(visual); |
| vi = XGetVisualInfo(display, VisualIDMask, vinfo, &nvis); |
| if (vi) { |
| *vinfo = *vi; |
| XFree(vi); |
| return 0; |
| } |
| return -1; |
| } |
| |
| Uint32 |
| X11_GetPixelFormatFromVisualInfo(Display * display, XVisualInfo * vinfo) |
| { |
| if (vinfo->class == DirectColor || vinfo->class == TrueColor) { |
| int bpp; |
| Uint32 Rmask, Gmask, Bmask, Amask; |
| |
| Rmask = vinfo->visual->red_mask; |
| Gmask = vinfo->visual->green_mask; |
| Bmask = vinfo->visual->blue_mask; |
| if (vinfo->depth == 32) { |
| Amask = (0xFFFFFFFF & ~(Rmask | Gmask | Bmask)); |
| } else { |
| Amask = 0; |
| } |
| |
| bpp = vinfo->depth; |
| if (bpp == 24) { |
| int i, n; |
| XPixmapFormatValues *p = XListPixmapFormats(display, &n); |
| if (p) { |
| for (i = 0; i < n; ++i) { |
| if (p[i].depth == 24) { |
| bpp = p[i].bits_per_pixel; |
| break; |
| } |
| } |
| XFree(p); |
| } |
| } |
| |
| return SDL_MasksToPixelFormatEnum(bpp, Rmask, Gmask, Bmask, Amask); |
| } |
| |
| if (vinfo->class == PseudoColor || vinfo->class == StaticColor) { |
| switch (vinfo->depth) { |
| case 8: |
| return SDL_PIXELTYPE_INDEX8; |
| case 4: |
| if (BitmapBitOrder(display) == LSBFirst) { |
| return SDL_PIXELFORMAT_INDEX4LSB; |
| } else { |
| return SDL_PIXELFORMAT_INDEX4MSB; |
| } |
| break; |
| case 1: |
| if (BitmapBitOrder(display) == LSBFirst) { |
| return SDL_PIXELFORMAT_INDEX1LSB; |
| } else { |
| return SDL_PIXELFORMAT_INDEX1MSB; |
| } |
| break; |
| } |
| } |
| |
| return SDL_PIXELFORMAT_UNKNOWN; |
| } |
| |
| /* Global for the error handler */ |
| int vm_event, vm_error = -1; |
| |
| #if SDL_VIDEO_DRIVER_X11_XINERAMA |
| static SDL_bool |
| CheckXinerama(Display * display, int *major, int *minor) |
| { |
| int event_base = 0; |
| int error_base = 0; |
| const char *env; |
| |
| /* Default the extension not available */ |
| *major = *minor = 0; |
| |
| /* Allow environment override */ |
| env = SDL_GetHint(SDL_HINT_VIDEO_X11_XINERAMA); |
| if (env && !SDL_atoi(env)) { |
| #ifdef X11MODES_DEBUG |
| printf("Xinerama disabled due to hint\n"); |
| #endif |
| return SDL_FALSE; |
| } |
| |
| if (!SDL_X11_HAVE_XINERAMA) { |
| #ifdef X11MODES_DEBUG |
| printf("Xinerama support not available\n"); |
| #endif |
| return SDL_FALSE; |
| } |
| |
| /* Query the extension version */ |
| if (!XineramaQueryExtension(display, &event_base, &error_base) || |
| !XineramaQueryVersion(display, major, minor) || |
| !XineramaIsActive(display)) { |
| #ifdef X11MODES_DEBUG |
| printf("Xinerama not active on the display\n"); |
| #endif |
| return SDL_FALSE; |
| } |
| #ifdef X11MODES_DEBUG |
| printf("Xinerama available at version %d.%d!\n", *major, *minor); |
| #endif |
| return SDL_TRUE; |
| } |
| #endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */ |
| |
| #if SDL_VIDEO_DRIVER_X11_XRANDR |
| static SDL_bool |
| CheckXRandR(Display * display, int *major, int *minor) |
| { |
| const char *env; |
| |
| /* Default the extension not available */ |
| *major = *minor = 0; |
| |
| /* Allow environment override */ |
| env = SDL_GetHint(SDL_HINT_VIDEO_X11_XRANDR); |
| #ifdef XRANDR_DISABLED_BY_DEFAULT |
| if (!env || !SDL_atoi(env)) { |
| #ifdef X11MODES_DEBUG |
| printf("XRandR disabled by default due to window manager issues\n"); |
| #endif |
| return SDL_FALSE; |
| } |
| #else |
| if (env && !SDL_atoi(env)) { |
| #ifdef X11MODES_DEBUG |
| printf("XRandR disabled due to hint\n"); |
| #endif |
| return SDL_FALSE; |
| } |
| #endif /* XRANDR_ENABLED_BY_DEFAULT */ |
| |
| if (!SDL_X11_HAVE_XRANDR) { |
| #ifdef X11MODES_DEBUG |
| printf("XRandR support not available\n"); |
| #endif |
| return SDL_FALSE; |
| } |
| |
| /* Query the extension version */ |
| if (!XRRQueryVersion(display, major, minor)) { |
| #ifdef X11MODES_DEBUG |
| printf("XRandR not active on the display\n"); |
| #endif |
| return SDL_FALSE; |
| } |
| #ifdef X11MODES_DEBUG |
| printf("XRandR available at version %d.%d!\n", *major, *minor); |
| #endif |
| return SDL_TRUE; |
| } |
| |
| #define XRANDR_ROTATION_LEFT (1 << 1) |
| #define XRANDR_ROTATION_RIGHT (1 << 3) |
| |
| static int |
| CalculateXRandRRefreshRate(const XRRModeInfo *info) |
| { |
| return (info->hTotal |
| && info->vTotal) ? (info->dotClock / (info->hTotal * info->vTotal)) : 0; |
| } |
| |
| static SDL_bool |
| SetXRandRModeInfo(Display *display, XRRScreenResources *res, XRROutputInfo *output_info, |
| RRMode modeID, SDL_DisplayMode *mode) |
| { |
| int i; |
| for (i = 0; i < res->nmode; ++i) { |
| if (res->modes[i].id == modeID) { |
| XRRCrtcInfo *crtc; |
| Rotation rotation = 0; |
| const XRRModeInfo *info = &res->modes[i]; |
| |
| crtc = XRRGetCrtcInfo(display, res, output_info->crtc); |
| if (crtc) { |
| rotation = crtc->rotation; |
| XRRFreeCrtcInfo(crtc); |
| } |
| |
| if (rotation & (XRANDR_ROTATION_LEFT|XRANDR_ROTATION_RIGHT)) { |
| mode->w = info->height; |
| mode->h = info->width; |
| } else { |
| mode->w = info->width; |
| mode->h = info->height; |
| } |
| mode->refresh_rate = CalculateXRandRRefreshRate(info); |
| ((SDL_DisplayModeData*)mode->driverdata)->xrandr_mode = modeID; |
| #ifdef X11MODES_DEBUG |
| printf("XRandR mode %d: %dx%d@%dHz\n", (int) modeID, mode->w, mode->h, mode->refresh_rate); |
| #endif |
| return SDL_TRUE; |
| } |
| } |
| return SDL_FALSE; |
| } |
| #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */ |
| |
| #if SDL_VIDEO_DRIVER_X11_XVIDMODE |
| static SDL_bool |
| CheckVidMode(Display * display, int *major, int *minor) |
| { |
| const char *env; |
| |
| /* Default the extension not available */ |
| *major = *minor = 0; |
| |
| /* Allow environment override */ |
| env = SDL_GetHint(SDL_HINT_VIDEO_X11_XVIDMODE); |
| if (env && !SDL_atoi(env)) { |
| #ifdef X11MODES_DEBUG |
| printf("XVidMode disabled due to hint\n"); |
| #endif |
| return SDL_FALSE; |
| } |
| |
| if (!SDL_X11_HAVE_XVIDMODE) { |
| #ifdef X11MODES_DEBUG |
| printf("XVidMode support not available\n"); |
| #endif |
| return SDL_FALSE; |
| } |
| |
| /* Query the extension version */ |
| vm_error = -1; |
| if (!XF86VidModeQueryExtension(display, &vm_event, &vm_error) |
| || !XF86VidModeQueryVersion(display, major, minor)) { |
| #ifdef X11MODES_DEBUG |
| printf("XVidMode not active on the display\n"); |
| #endif |
| return SDL_FALSE; |
| } |
| #ifdef X11MODES_DEBUG |
| printf("XVidMode available at version %d.%d!\n", *major, *minor); |
| #endif |
| return SDL_TRUE; |
| } |
| |
| static |
| Bool XF86VidModeGetModeInfo(Display * dpy, int scr, |
| XF86VidModeModeInfo* info) |
| { |
| Bool retval; |
| int dotclock; |
| XF86VidModeModeLine l; |
| SDL_zerop(info); |
| SDL_zero(l); |
| retval = XF86VidModeGetModeLine(dpy, scr, &dotclock, &l); |
| info->dotclock = dotclock; |
| info->hdisplay = l.hdisplay; |
| info->hsyncstart = l.hsyncstart; |
| info->hsyncend = l.hsyncend; |
| info->htotal = l.htotal; |
| info->hskew = l.hskew; |
| info->vdisplay = l.vdisplay; |
| info->vsyncstart = l.vsyncstart; |
| info->vsyncend = l.vsyncend; |
| info->vtotal = l.vtotal; |
| info->flags = l.flags; |
| info->privsize = l.privsize; |
| info->private = l.private; |
| return retval; |
| } |
| |
| static int |
| CalculateXVidModeRefreshRate(const XF86VidModeModeInfo * info) |
| { |
| return (info->htotal |
| && info->vtotal) ? (1000 * info->dotclock / (info->htotal * |
| info->vtotal)) : 0; |
| } |
| |
| SDL_bool |
| SetXVidModeModeInfo(const XF86VidModeModeInfo *info, SDL_DisplayMode *mode) |
| { |
| mode->w = info->hdisplay; |
| mode->h = info->vdisplay; |
| mode->refresh_rate = CalculateXVidModeRefreshRate(info); |
| ((SDL_DisplayModeData*)mode->driverdata)->vm_mode = *info; |
| return SDL_TRUE; |
| } |
| #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */ |
| |
| int |
| X11_InitModes(_THIS) |
| { |
| SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; |
| int screen, screencount; |
| #if SDL_VIDEO_DRIVER_X11_XINERAMA |
| int xinerama_major, xinerama_minor; |
| int use_xinerama = 0; |
| XineramaScreenInfo *xinerama = NULL; |
| #endif |
| #if SDL_VIDEO_DRIVER_X11_XRANDR |
| int xrandr_major, xrandr_minor; |
| int use_xrandr = 0; |
| XRRScreenResources *res = NULL; |
| #endif |
| #if SDL_VIDEO_DRIVER_X11_XVIDMODE |
| int vm_major, vm_minor; |
| int use_vidmode = 0; |
| #endif |
| |
| #if SDL_VIDEO_DRIVER_X11_XINERAMA |
| /* Query Xinerama extention |
| * NOTE: This works with Nvidia Twinview correctly, but you need version 302.17 (released on June 2012) |
| * or newer of the Nvidia binary drivers |
| */ |
| if (CheckXinerama(data->display, &xinerama_major, &xinerama_minor)) { |
| xinerama = XineramaQueryScreens(data->display, &screencount); |
| if (xinerama) { |
| use_xinerama = xinerama_major * 100 + xinerama_minor; |
| } |
| } |
| if (!xinerama) { |
| screencount = ScreenCount(data->display); |
| } |
| #else |
| screencount = ScreenCount(data->display); |
| #endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */ |
| |
| #if SDL_VIDEO_DRIVER_X11_XRANDR |
| /* require at least XRandR v1.2 */ |
| if (CheckXRandR(data->display, &xrandr_major, &xrandr_minor) && |
| (xrandr_major >= 2 || (xrandr_major == 1 && xrandr_minor >= 2))) { |
| use_xrandr = xrandr_major * 100 + xrandr_minor; |
| } |
| #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */ |
| |
| #if SDL_VIDEO_DRIVER_X11_XVIDMODE |
| if (CheckVidMode(data->display, &vm_major, &vm_minor)) { |
| use_vidmode = vm_major * 100 + vm_minor; |
| } |
| #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */ |
| |
| for (screen = 0; screen < screencount; ++screen) { |
| XVisualInfo vinfo; |
| SDL_VideoDisplay display; |
| SDL_DisplayData *displaydata; |
| SDL_DisplayMode mode; |
| SDL_DisplayModeData *modedata; |
| XPixmapFormatValues *pixmapFormats; |
| char display_name[128]; |
| int i, n; |
| |
| #if SDL_VIDEO_DRIVER_X11_XINERAMA |
| if (xinerama) { |
| if (get_visualinfo(data->display, 0, &vinfo) < 0) { |
| continue; |
| } |
| } else { |
| if (get_visualinfo(data->display, screen, &vinfo) < 0) { |
| continue; |
| } |
| } |
| #else |
| if (get_visualinfo(data->display, screen, &vinfo) < 0) { |
| continue; |
| } |
| #endif |
| |
| displaydata = (SDL_DisplayData *) SDL_calloc(1, sizeof(*displaydata)); |
| if (!displaydata) { |
| continue; |
| } |
| display_name[0] = '\0'; |
| |
| mode.format = X11_GetPixelFormatFromVisualInfo(data->display, &vinfo); |
| if (SDL_ISPIXELFORMAT_INDEXED(mode.format)) { |
| /* We don't support palettized modes now */ |
| SDL_free(displaydata); |
| continue; |
| } |
| #if SDL_VIDEO_DRIVER_X11_XINERAMA |
| if (xinerama) { |
| mode.w = xinerama[screen].width; |
| mode.h = xinerama[screen].height; |
| } else { |
| mode.w = DisplayWidth(data->display, screen); |
| mode.h = DisplayHeight(data->display, screen); |
| } |
| #else |
| mode.w = DisplayWidth(data->display, screen); |
| mode.h = DisplayHeight(data->display, screen); |
| #endif |
| mode.refresh_rate = 0; |
| |
| modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData)); |
| if (!modedata) { |
| SDL_free(displaydata); |
| continue; |
| } |
| mode.driverdata = modedata; |
| |
| #if SDL_VIDEO_DRIVER_X11_XINERAMA |
| /* Most of SDL's calls to X11 are unwaware of Xinerama, and to X11 standard calls, when Xinerama is active, |
| * there's only one screen available. So we force the screen number to zero and |
| * let Xinerama specific code handle specific functionality using displaydata->xinerama_info |
| */ |
| if (use_xinerama) { |
| displaydata->screen = 0; |
| displaydata->use_xinerama = use_xinerama; |
| displaydata->xinerama_info = xinerama[screen]; |
| displaydata->xinerama_screen = screen; |
| } |
| else displaydata->screen = screen; |
| #else |
| displaydata->screen = screen; |
| #endif |
| displaydata->visual = vinfo.visual; |
| displaydata->depth = vinfo.depth; |
| |
| displaydata->scanline_pad = SDL_BYTESPERPIXEL(mode.format) * 8; |
| pixmapFormats = XListPixmapFormats(data->display, &n); |
| if (pixmapFormats) { |
| for (i = 0; i < n; ++i) { |
| if (pixmapFormats[i].depth == displaydata->depth) { |
| displaydata->scanline_pad = pixmapFormats[i].scanline_pad; |
| break; |
| } |
| } |
| XFree(pixmapFormats); |
| } |
| |
| #if SDL_VIDEO_DRIVER_X11_XINERAMA |
| if (use_xinerama) { |
| displaydata->x = xinerama[screen].x_org; |
| displaydata->y = xinerama[screen].y_org; |
| } |
| else |
| #endif |
| { |
| displaydata->x = 0; |
| displaydata->y = 0; |
| } |
| |
| #if SDL_VIDEO_DRIVER_X11_XRANDR |
| if (use_xrandr) { |
| res = XRRGetScreenResources(data->display, RootWindow(data->display, displaydata->screen)); |
| } |
| if (res) { |
| XRROutputInfo *output_info; |
| XRRCrtcInfo *crtc; |
| int output; |
| Atom EDID = XInternAtom(data->display, "EDID", False); |
| Atom *props; |
| int nprop; |
| unsigned long width_mm; |
| unsigned long height_mm; |
| int inches = 0; |
| |
| for (output = 0; output < res->noutput; output++) { |
| output_info = XRRGetOutputInfo(data->display, res, res->outputs[output]); |
| if (!output_info || !output_info->crtc || |
| output_info->connection == RR_Disconnected) { |
| XRRFreeOutputInfo(output_info); |
| continue; |
| } |
| |
| /* Is this the output that corresponds to the current screen? |
| We're checking the crtc position, but that may not be a valid test |
| in all cases. Anybody want to give this some love? |
| */ |
| crtc = XRRGetCrtcInfo(data->display, res, output_info->crtc); |
| if (!crtc || crtc->x != displaydata->x || crtc->y != displaydata->y) { |
| XRRFreeOutputInfo(output_info); |
| XRRFreeCrtcInfo(crtc); |
| continue; |
| } |
| |
| displaydata->use_xrandr = use_xrandr; |
| displaydata->xrandr_output = res->outputs[output]; |
| SetXRandRModeInfo(data->display, res, output_info, crtc->mode, &mode); |
| |
| /* Get the name of this display */ |
| width_mm = output_info->mm_width; |
| height_mm = output_info->mm_height; |
| inches = (int)((sqrt(width_mm * width_mm + |
| height_mm * height_mm) / 25.4f) + 0.5f); |
| SDL_strlcpy(display_name, output_info->name, sizeof(display_name)); |
| |
| /* See if we can get the EDID data for the real monitor name */ |
| props = XRRListOutputProperties(data->display, res->outputs[output], &nprop); |
| for (i = 0; i < nprop; ++i) { |
| unsigned char *prop; |
| int actual_format; |
| unsigned long nitems, bytes_after; |
| Atom actual_type; |
| |
| if (props[i] == EDID) { |
| if (XRRGetOutputProperty(data->display, |
| res->outputs[output], props[i], |
| 0, 100, False, False, |
| AnyPropertyType, |
| &actual_type, &actual_format, |
| &nitems, &bytes_after, &prop) == Success ) { |
| MonitorInfo *info = decode_edid(prop); |
| if (info) { |
| #ifdef X11MODES_DEBUG |
| printf("Found EDID data for %s\n", output_info->name); |
| dump_monitor_info(info); |
| #endif |
| SDL_strlcpy(display_name, info->dsc_product_name, sizeof(display_name)); |
| free(info); |
| } |
| XFree(prop); |
| } |
| break; |
| } |
| } |
| if (props) { |
| XFree(props); |
| } |
| |
| if (*display_name && inches) { |
| size_t len = SDL_strlen(display_name); |
| SDL_snprintf(&display_name[len], sizeof(display_name)-len, " %d\"", inches); |
| } |
| #ifdef X11MODES_DEBUG |
| printf("Display name: %s\n", display_name); |
| #endif |
| |
| XRRFreeOutputInfo(output_info); |
| XRRFreeCrtcInfo(crtc); |
| break; |
| } |
| #ifdef X11MODES_DEBUG |
| if (output == res->noutput) { |
| printf("Couldn't find XRandR CRTC at %d,%d\n", displaydata->x, displaydata->y); |
| } |
| #endif |
| XRRFreeScreenResources(res); |
| } |
| #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */ |
| |
| #if SDL_VIDEO_DRIVER_X11_XVIDMODE |
| if (!displaydata->use_xrandr && |
| #if SDL_VIDEO_DRIVER_X11_XINERAMA |
| /* XVidMode only works on the screen at the origin */ |
| (!displaydata->use_xinerama || |
| (displaydata->x == 0 && displaydata->y == 0)) && |
| #endif |
| use_vidmode) { |
| displaydata->use_vidmode = use_vidmode; |
| if (displaydata->use_xinerama) { |
| displaydata->vidmode_screen = 0; |
| } else { |
| displaydata->vidmode_screen = screen; |
| } |
| XF86VidModeGetModeInfo(data->display, displaydata->vidmode_screen, &modedata->vm_mode); |
| } |
| #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */ |
| |
| SDL_zero(display); |
| if (*display_name) { |
| display.name = display_name; |
| } |
| display.desktop_mode = mode; |
| display.current_mode = mode; |
| display.driverdata = displaydata; |
| SDL_AddVideoDisplay(&display); |
| } |
| |
| #if SDL_VIDEO_DRIVER_X11_XINERAMA |
| if (xinerama) XFree(xinerama); |
| #endif |
| |
| if (_this->num_displays == 0) { |
| return SDL_SetError("No available displays"); |
| } |
| return 0; |
| } |
| |
| void |
| X11_GetDisplayModes(_THIS, SDL_VideoDisplay * sdl_display) |
| { |
| Display *display = ((SDL_VideoData *) _this->driverdata)->display; |
| SDL_DisplayData *data = (SDL_DisplayData *) sdl_display->driverdata; |
| #if SDL_VIDEO_DRIVER_X11_XVIDMODE |
| int nmodes; |
| XF86VidModeModeInfo ** modes; |
| #endif |
| int screen_w; |
| int screen_h; |
| SDL_DisplayMode mode; |
| SDL_DisplayModeData *modedata; |
| |
| /* Unfortunately X11 requires the window to be created with the correct |
| * visual and depth ahead of time, but the SDL API allows you to create |
| * a window before setting the fullscreen display mode. This means that |
| * we have to use the same format for all windows and all display modes. |
| * (or support recreating the window with a new visual behind the scenes) |
| */ |
| mode.format = sdl_display->current_mode.format; |
| mode.driverdata = NULL; |
| |
| screen_w = DisplayWidth(display, data->screen); |
| screen_h = DisplayHeight(display, data->screen); |
| |
| #if SDL_VIDEO_DRIVER_X11_XINERAMA |
| if (data->use_xinerama) { |
| if (data->use_vidmode && !data->xinerama_info.x_org && !data->xinerama_info.y_org && |
| (screen_w > data->xinerama_info.width || screen_h > data->xinerama_info.height)) { |
| /* Add the full (both screens combined) xinerama mode only on the display that starts at 0,0 |
| * if we're using vidmode. |
| */ |
| mode.w = screen_w; |
| mode.h = screen_h; |
| mode.refresh_rate = 0; |
| modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData)); |
| if (modedata) { |
| *modedata = *(SDL_DisplayModeData *)sdl_display->desktop_mode.driverdata; |
| } |
| mode.driverdata = modedata; |
| SDL_AddDisplayMode(sdl_display, &mode); |
| } |
| else if (!data->use_xrandr) |
| { |
| /* Add the current mode of each monitor otherwise if we can't get them from xrandr */ |
| mode.w = data->xinerama_info.width; |
| mode.h = data->xinerama_info.height; |
| mode.refresh_rate = 0; |
| modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData)); |
| if (modedata) { |
| *modedata = *(SDL_DisplayModeData *)sdl_display->desktop_mode.driverdata; |
| } |
| mode.driverdata = modedata; |
| SDL_AddDisplayMode(sdl_display, &mode); |
| } |
| |
| } |
| #endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */ |
| |
| #if SDL_VIDEO_DRIVER_X11_XRANDR |
| if (data->use_xrandr) { |
| XRRScreenResources *res; |
| |
| res = XRRGetScreenResources (display, RootWindow(display, data->screen)); |
| if (res) { |
| SDL_DisplayModeData *modedata; |
| XRROutputInfo *output_info; |
| int i; |
| |
| output_info = XRRGetOutputInfo(display, res, data->xrandr_output); |
| if (output_info && output_info->connection != RR_Disconnected) { |
| for (i = 0; i < output_info->nmode; ++i) { |
| modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData)); |
| if (!modedata) { |
| continue; |
| } |
| mode.driverdata = modedata; |
| |
| if (SetXRandRModeInfo(display, res, output_info, output_info->modes[i], &mode)) { |
| SDL_AddDisplayMode(sdl_display, &mode); |
| } else { |
| SDL_free(modedata); |
| } |
| } |
| } |
| XRRFreeOutputInfo(output_info); |
| XRRFreeScreenResources(res); |
| } |
| return; |
| } |
| #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */ |
| |
| #if SDL_VIDEO_DRIVER_X11_XVIDMODE |
| if (data->use_vidmode && |
| XF86VidModeGetAllModeLines(display, data->vidmode_screen, &nmodes, &modes)) { |
| int i; |
| |
| #ifdef X11MODES_DEBUG |
| printf("VidMode modes: (unsorted)\n"); |
| for (i = 0; i < nmodes; ++i) { |
| printf("Mode %d: %d x %d @ %d, flags: 0x%x\n", i, |
| modes[i]->hdisplay, modes[i]->vdisplay, |
| CalculateXVidModeRefreshRate(modes[i]), modes[i]->flags); |
| } |
| #endif |
| for (i = 0; i < nmodes; ++i) { |
| modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData)); |
| if (!modedata) { |
| continue; |
| } |
| mode.driverdata = modedata; |
| |
| if (SetXVidModeModeInfo(modes[i], &mode)) { |
| SDL_AddDisplayMode(sdl_display, &mode); |
| } else { |
| SDL_free(modedata); |
| } |
| } |
| XFree(modes); |
| return; |
| } |
| #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */ |
| |
| if (!data->use_xrandr && !data->use_vidmode) { |
| /* Add the desktop mode */ |
| mode = sdl_display->desktop_mode; |
| modedata = (SDL_DisplayModeData *) SDL_calloc(1, sizeof(SDL_DisplayModeData)); |
| if (modedata) { |
| *modedata = *(SDL_DisplayModeData *)sdl_display->desktop_mode.driverdata; |
| } |
| mode.driverdata = modedata; |
| SDL_AddDisplayMode(sdl_display, &mode); |
| } |
| } |
| |
| int |
| X11_SetDisplayMode(_THIS, SDL_VideoDisplay * sdl_display, SDL_DisplayMode * mode) |
| { |
| Display *display = ((SDL_VideoData *) _this->driverdata)->display; |
| SDL_DisplayData *data = (SDL_DisplayData *) sdl_display->driverdata; |
| SDL_DisplayModeData *modedata = (SDL_DisplayModeData *)mode->driverdata; |
| |
| #if SDL_VIDEO_DRIVER_X11_XRANDR |
| if (data->use_xrandr) { |
| XRRScreenResources *res; |
| XRROutputInfo *output_info; |
| XRRCrtcInfo *crtc; |
| Status status; |
| |
| res = XRRGetScreenResources (display, RootWindow(display, data->screen)); |
| if (!res) { |
| return SDL_SetError("Couldn't get XRandR screen resources"); |
| } |
| |
| output_info = XRRGetOutputInfo(display, res, data->xrandr_output); |
| if (!output_info || output_info->connection == RR_Disconnected) { |
| XRRFreeScreenResources(res); |
| return SDL_SetError("Couldn't get XRandR output info"); |
| } |
| |
| crtc = XRRGetCrtcInfo(display, res, output_info->crtc); |
| if (!crtc) { |
| XRRFreeOutputInfo(output_info); |
| XRRFreeScreenResources(res); |
| return SDL_SetError("Couldn't get XRandR crtc info"); |
| } |
| |
| status = XRRSetCrtcConfig (display, res, output_info->crtc, CurrentTime, |
| crtc->x, crtc->y, modedata->xrandr_mode, crtc->rotation, |
| &data->xrandr_output, 1); |
| |
| XRRFreeCrtcInfo(crtc); |
| XRRFreeOutputInfo(output_info); |
| XRRFreeScreenResources(res); |
| |
| if (status != Success) { |
| return SDL_SetError("XRRSetCrtcConfig failed"); |
| } |
| } |
| #endif /* SDL_VIDEO_DRIVER_X11_XRANDR */ |
| |
| #if SDL_VIDEO_DRIVER_X11_XVIDMODE |
| if (data->use_vidmode) { |
| XF86VidModeSwitchToMode(display, data->vidmode_screen, &modedata->vm_mode); |
| } |
| #endif /* SDL_VIDEO_DRIVER_X11_XVIDMODE */ |
| |
| return 0; |
| } |
| |
| void |
| X11_QuitModes(_THIS) |
| { |
| } |
| |
| int |
| X11_GetDisplayBounds(_THIS, SDL_VideoDisplay * sdl_display, SDL_Rect * rect) |
| { |
| SDL_DisplayData *data = (SDL_DisplayData *) sdl_display->driverdata; |
| |
| rect->x = data->x; |
| rect->y = data->y; |
| rect->w = sdl_display->current_mode.w; |
| rect->h = sdl_display->current_mode.h; |
| |
| #if SDL_VIDEO_DRIVER_X11_XINERAMA |
| /* Get the real current bounds of the display */ |
| if (data->use_xinerama) { |
| Display *display = ((SDL_VideoData *) _this->driverdata)->display; |
| int screencount; |
| XineramaScreenInfo *xinerama = XineramaQueryScreens(display, &screencount); |
| if (xinerama) { |
| rect->x = xinerama[data->xinerama_screen].x_org; |
| rect->y = xinerama[data->xinerama_screen].y_org; |
| XFree(xinerama); |
| } |
| } |
| #endif /* SDL_VIDEO_DRIVER_X11_XINERAMA */ |
| return 0; |
| } |
| |
| #endif /* SDL_VIDEO_DRIVER_X11 */ |
| |
| /* vi: set ts=4 sw=4 expandtab: */ |