From: VMware, Inc <> Date: Wed, 20 Jan 2010 21:29:46 +0000 (-0800) Subject: resolutionX11.c: Add support for autofit via RandR >= 1.2. X-Git-Tag: 2010.01.19-226760~26 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5310a7dd2a8aad0fccb7ce95de86cf61ef68b0a0;p=thirdparty%2Fopen-vm-tools.git resolutionX11.c: Add support for autofit via RandR >= 1.2. The new WIP Linux guest drivers no longer support the VMWARE_CTRL X11 protocol extension but instead expose multiple displays via the RandR (Resize and Rotate) extension as outputs LVDS1..LVDS. The requested topology is implemented using modes called 'autofit-WWWWxHHHH' generated as needed for each output. Signed-off-by: Marcelo Vanzin --- diff --git a/open-vm-tools/lib/resolution/resolutionX11.c b/open-vm-tools/lib/resolution/resolutionX11.c index 87785a3fd..12e49ce3e 100644 --- a/open-vm-tools/lib/resolution/resolutionX11.c +++ b/open-vm-tools/lib/resolution/resolutionX11.c @@ -44,11 +44,18 @@ #include "libvmwarectrl.h" #include "str.h" #include "strutil.h" +#include "util.h" #define VMWAREDRV_PATH_64 "/usr/X11R6/lib64/modules/drivers/vmware_drv.o" #define VMWAREDRV_PATH "/usr/X11R6/lib/modules/drivers/vmware_drv.o" #define VERSION_STRING "VMware Guest X Server" +#define RR12_OUTPUT_FORMAT "LVDS%u" +#define RR12_MODE_FORMAT "autofit-%ux%u" +#define RR12_MODE_MAXLEN (sizeof RR12_MODE_FORMAT + 2 * (10 - 2) + 1) +#define RR12_DEFAULT_DPI 96.0 +#define MILLIS_PER_INCH 25.4 + /* * Describes the state of the X11 back-end of lib/resolution. @@ -60,6 +67,11 @@ typedef struct { // TRUE if VMwareCtrl extension available Bool canUseVMwareCtrlTopologySet; // TRUE if VMwareCtrl extension supports topology set + Bool canUseRandR12; // TRUE if RandR extension >= 1.2 available + unsigned int topologyDisplays; + // Number of displays in current topology + unsigned int topologyWidth; // Total width of current topology + unsigned int topologyHeight; // Total height of current topology } ResolutionInfoX11Type; @@ -76,6 +88,11 @@ ResolutionInfoX11Type resolutionInfoX11; static Bool ResolutionCanSet(void); static Bool TopologyCanSet(void); +#ifndef NO_MULTIMON +static Bool RandR12_SetTopology(unsigned int ndisplays, + xXineramaScreenInfo *displays, + unsigned int width, unsigned int height); +#endif static Bool SelectResolution(uint32 width, uint32 height); @@ -123,6 +140,7 @@ ResolutionBackendInit(InitHandle handle) // IN: User's X11 display. resInfoX->canUseVMwareCtrl = VMwareCtrl_QueryVersion(resInfoX->display, &dummy1, &dummy2); resInfoX->canUseVMwareCtrlTopologySet = FALSE; + resInfoX->canUseRandR12 = FALSE; resInfo->canSetResolution = ResolutionCanSet(); resInfo->canSetTopology = TopologyCanSet(); @@ -280,18 +298,23 @@ ResolutionSetTopology(unsigned int ndisplays, displays[i].y_org -= minY; } - if (!VMwareCtrl_SetTopology(resInfoX->display, DefaultScreen(resInfoX->display), displays, - ndisplays)) { - Debug("Failed to set topology in the driver.\n"); - goto out; - } + if (resInfoX->canUseVMwareCtrlTopologySet) { + if (!VMwareCtrl_SetTopology(resInfoX->display, DefaultScreen(resInfoX->display), + displays, ndisplays)) { + Debug("Failed to set topology in the driver.\n"); + goto out; + } - if (!SelectResolution(maxX - minX, maxY - minY)) { - Debug("Failed to set new resolution.\n"); - goto out; - } + if (!SelectResolution(maxX - minX, maxY - minY)) { + Debug("Failed to set new resolution.\n"); + goto out; + } - success = TRUE; + success = TRUE; + } else if (resInfoX->canUseRandR12) { + success = RandR12_SetTopology(ndisplays, displays, + maxX - minX, maxY - minY); + } out: free(displays); @@ -347,6 +370,54 @@ ResolutionCanSet(void) return TRUE; } +#ifndef NO_MULTIMON + /* See if RandR >= 1.2 can be used: The extension version is high enough and + * all output names match the expected format. + */ + if (major > 1 || (major == 1 && minor >= 2)) { + XRRScreenResources* xrrRes; + XRROutputInfo* xrrOutput; + unsigned int num; + int i; + + XGrabServer(resInfoX->display); + + xrrRes = XRRGetScreenResources(resInfoX->display, resInfoX->rootWindow); + + if (xrrRes) { + for (i = 0; i < xrrRes->noutput; i++) { + xrrOutput = XRRGetOutputInfo(resInfoX->display, xrrRes, + xrrRes->outputs[i]); + if (!xrrOutput) { + break; + } + + if (sscanf(xrrOutput->name, RR12_OUTPUT_FORMAT, &num) != 1 || + num < 1) { + XRRFreeOutputInfo(xrrOutput); + break; + } + + XRRFreeOutputInfo(xrrOutput); + } + + if (i == xrrRes->noutput) { + resInfoX->canUseRandR12 = TRUE; + } else { + Debug("RandR >= 1.2 not usable\n"); + } + + XRRFreeScreenResources(xrrRes); + } + + XUngrabServer(resInfoX->display); + + if (resInfoX->canUseRandR12) { + return TRUE; + } + } +#endif // ifndef NO_MULTIMON + /* * XXX: This check does not work with XOrg 6.9/7.0 for two reasons: Both * versions now use .so for the driver extension and 7.0 moves the drivers @@ -430,10 +501,15 @@ TopologyCanSet(void) ResolutionInfoX11Type *resInfoX = &resolutionInfoX11; #ifdef NO_MULTIMON resInfoX->canUseVMwareCtrlTopologySet = FALSE; + return FALSE; #else int major; int minor; + if (resInfoX->canUseRandR12) { + return TRUE; + } + if (resInfoX->canUseVMwareCtrl && XineramaQueryVersion(resInfoX->display, &major, &minor)) { /* @@ -443,12 +519,288 @@ TopologyCanSet(void) } else { resInfoX->canUseVMwareCtrlTopologySet = FALSE; } -#endif return resInfoX->canUseVMwareCtrlTopologySet; +#endif } +#ifndef NO_MULTIMON + +/* + *----------------------------------------------------------------------------- + * + * RandR12_SetTopology -- + * + * Employs the RandR 1.2 extension to set a new display topology. + * + * Results: + * TRUE if operation succeeded, FALSE otherwise. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static Bool +RandR12_SetTopology(unsigned int ndisplays, + // IN: number of elements in topology + xXineramaScreenInfo *displays, + // IN: array of display geometries + unsigned int width, + // IN: total width of topology + unsigned int height) + // IN: total height of topology +{ + ResolutionInfoX11Type *resInfoX = &resolutionInfoX11; + int minWidth, minHeight, maxWidth, maxHeight; + XRRScreenResources* xrrRes = NULL; + XRROutputInfo** xrrOutputs; + unsigned int numOutputs; + unsigned int* outputMap; + XRRCrtcInfo** xrrCrtcs; + XRRScreenConfiguration* xrrConfig = NULL; + XRRScreenSize* xrrSizes; + Rotation xrrCurRotation; + uint32 xrrNumSizes; + SizeID currentSize; + XRRModeInfo xrrModes[ndisplays]; + char name[RR12_MODE_MAXLEN]; + float dpi; + int i, j, k; + Bool success = FALSE; + + if (!XRRGetScreenSizeRange(resInfoX->display, resInfoX->rootWindow, + &minWidth, &minHeight, &maxWidth, &maxHeight) || + width < minWidth || height < minHeight || + width > maxWidth || height > maxHeight) { + return FALSE; + } + + /* Grab the server for two reasons: + * - Avoid race conditions with other clients changing RandR configuration. + * - Make our changes appear as atomic as possible to other clients. + */ + XGrabServer(resInfoX->display); + + xrrRes = XRRGetScreenResources(resInfoX->display, resInfoX->rootWindow); + if (!xrrRes) { + goto error; + } + + xrrCrtcs = Util_SafeCalloc(sizeof *xrrCrtcs, xrrRes->ncrtc); + xrrOutputs = Util_SafeCalloc(sizeof *xrrOutputs, xrrRes->noutput); + outputMap = Util_SafeCalloc(sizeof *outputMap, xrrRes->noutput); + + /* RandR may enumerate outputs differently from the host. Apply the nth + * topology rectangle to the output called LVDS. + */ + for (i = 0; i < xrrRes->noutput; i++) { + outputMap[i] = i; + } + + numOutputs = 0; + for (i = 0; i < xrrRes->noutput; i++) { + unsigned int num; + + xrrOutputs[i] = XRRGetOutputInfo(resInfoX->display, xrrRes, + xrrRes->outputs[i]); + + if (!xrrOutputs[i]) { + goto error; + } + + if (sscanf(xrrOutputs[i]->name, RR12_OUTPUT_FORMAT, &num) != 1 || + num > ndisplays) { + continue; + } + + outputMap[num - 1] = i; + + if (num > numOutputs) { + numOutputs = num; + } + } + + /* Disable any CRTCs which won't be used or wont't fit in new screen size. */ + for (i = 0; i < xrrRes->ncrtc; i++) { + xrrCrtcs[i] = XRRGetCrtcInfo(resInfoX->display, xrrRes, + xrrRes->crtcs[i]); + + if (!xrrCrtcs[i]) { + goto error; + } + + for (j = 0; j < numOutputs; j++) { + if (xrrOutputs[outputMap[j]]->crtc == xrrRes->crtcs[i]) { + break; + } + } + + if (xrrCrtcs[i]->mode == None || + (j < numOutputs && + (xrrCrtcs[i]->x + xrrCrtcs[i]->width) <= width && + (xrrCrtcs[i]->y + xrrCrtcs[i]->height) <= height)) { + continue; + } + + if (XRRSetCrtcConfig(resInfoX->display, xrrRes, xrrRes->crtcs[i], + xrrCrtcs[i]->timestamp, 0, 0, None, RR_Rotate_0, NULL, 0) + != Success) { + goto error; + } + } + + /* Set new screen size. */ + xrrConfig = XRRGetScreenInfo(resInfoX->display, resInfoX->rootWindow); + xrrSizes = XRRConfigSizes(xrrConfig, &xrrNumSizes); + currentSize = XRRConfigCurrentConfiguration(xrrConfig, &xrrCurRotation); + + if (xrrSizes[currentSize].mheight > 0) { + dpi = MILLIS_PER_INCH * xrrSizes[currentSize].height / xrrSizes[currentSize].mheight; + + if (!dpi) { + dpi = RR12_DEFAULT_DPI; + } + } else { + dpi = RR12_DEFAULT_DPI; + } + + XRRSetScreenSize(resInfoX->display, resInfoX->rootWindow, width, height, + (MILLIS_PER_INCH * width) / dpi, + (MILLIS_PER_INCH * height) / dpi); + + /* Set new topology. */ + for (i = 0; i < numOutputs; i++) { + memset(xrrModes + i, 0, sizeof xrrModes[0]); + + xrrModes[i].width = displays[i].width; + xrrModes[i].height = displays[i].height; + + /* Look for existing matching autofit mode. */ + for (j = 0; j < i && !xrrModes[i].id; j++) { + if (xrrModes[j].id && + xrrModes[j].width == displays[i].width && + xrrModes[j].height == displays[i].height) { + xrrModes[i].id = xrrModes[j].id; + break; + } + } + + for (j = 0; j < xrrRes->nmode && !xrrModes[i].id; j++) { + unsigned int w, h; + + if (sscanf(xrrRes->modes[j].name, RR12_MODE_FORMAT, &w, &h) == 2 && + w == displays[i].width && h == displays[i].height) { + xrrModes[i].id = xrrRes->modes[j].id; + break; + } + } + + /* If no luck, create new autofit mode. */ + if (!xrrModes[i].id) { + sprintf(name, RR12_MODE_FORMAT, displays[i].width, + displays[i].height); + xrrModes[i].name = name; + xrrModes[i].nameLength = strlen(xrrModes[i].name); + xrrModes[i].id = XRRCreateMode(resInfoX->display, resInfoX->rootWindow, + xrrModes + i); + } + + if (xrrModes[i].id == None) { + continue; + } + + /* Set autofit mode. */ + if (xrrOutputs[outputMap[i]]->crtc == None) { + xrrOutputs[outputMap[i]]->crtc = xrrOutputs[outputMap[i]]->crtcs[0]; + } + + for (j = 0; j < xrrOutputs[outputMap[i]]->nmode; j++) { + if (xrrModes[i].id == xrrOutputs[outputMap[i]]->modes[j]) { + break; + } + } + if (j == xrrOutputs[outputMap[i]]->nmode) { + XRRAddOutputMode(resInfoX->display, xrrRes->outputs[outputMap[i]], + xrrModes[i].id); + } + if (XRRSetCrtcConfig(resInfoX->display, xrrRes, + xrrOutputs[outputMap[i]]->crtc, xrrCrtcs[i]->timestamp, + displays[i].x_org, displays[i].y_org, xrrModes[i].id, + RR_Rotate_0, xrrRes->outputs + outputMap[i], 1) + != Success) { + goto error; + } + } + + /* Delete unused autofit modes. */ + for (i = 0; i < xrrRes->nmode; i++) { + unsigned int w, h; + Bool destroy = TRUE; + + if (sscanf(xrrRes->modes[i].name, RR12_MODE_FORMAT, &w, &h) != 2) { + continue; + } + + for (j = 0; j < xrrRes->noutput; j++) { + if (j < numOutputs && + w == displays[j].width && h == displays[j].height) { + destroy = FALSE; + continue; + } + + for (k = 0; k < xrrOutputs[outputMap[j]]->nmode; k++) { + if (xrrOutputs[outputMap[j]]->modes[k] == xrrRes->modes[i].id) { + XRRDeleteOutputMode(resInfoX->display, + xrrRes->outputs[outputMap[j]], + xrrOutputs[outputMap[j]]->modes[k]); + break; + } + } + } + + if (destroy) { + XRRDestroyMode(resInfoX->display, xrrRes->modes[i].id); + } + } + + resInfoX->topologyDisplays = ndisplays; + resInfoX->topologyWidth = width; + resInfoX->topologyHeight = height; + + success = TRUE; + +error: + XUngrabServer(resInfoX->display); + + if (xrrConfig) { + XRRFreeScreenConfigInfo(xrrConfig); + } + if (xrrRes) { + for (i = 0; i < xrrRes->noutput; i++) { + if (xrrOutputs[i]) { + XRRFreeOutputInfo(xrrOutputs[i]); + } + } + for (i = 0; i < xrrRes->ncrtc; i++) { + if (xrrCrtcs[i]) { + XRRFreeCrtcInfo(xrrCrtcs[i]); + } + } + free(outputMap); + free(xrrOutputs); + free(xrrCrtcs); + XRRFreeScreenResources(xrrRes); + } + + return success; +} + +#endif // ifndef NO_MULTIMON + + /*----------------------------------------------------------------------------- * * SelectResolution -- @@ -479,6 +831,25 @@ SelectResolution(uint32 width, // IN uint64 bestFitSize = 0; uint64 potentialSize; +#ifndef NO_MULTIMON + if (resInfoX->canUseRandR12) { + if (resInfoX->topologyDisplays != 1 || + resInfoX->topologyWidth != width || + resInfoX->topologyHeight != height) { + xXineramaScreenInfo display; + + display.x_org = 0; + display.y_org = 0; + display.width = width; + display.height = height; + + return RandR12_SetTopology(1, &display, width, height); + } + + return TRUE; + } +#endif + xrrConfig = XRRGetScreenInfo(resInfoX->display, resInfoX->rootWindow); xrrSizes = XRRConfigSizes(xrrConfig, &xrrNumSizes); XRRConfigCurrentConfiguration(xrrConfig, &xrrCurRotation); diff --git a/open-vm-tools/services/plugins/resolutionSet/resolutionX11.c b/open-vm-tools/services/plugins/resolutionSet/resolutionX11.c index c4ed0fde8..7452e9168 100644 --- a/open-vm-tools/services/plugins/resolutionSet/resolutionX11.c +++ b/open-vm-tools/services/plugins/resolutionSet/resolutionX11.c @@ -46,11 +46,18 @@ #include "libvmwarectrl.h" #include "str.h" #include "strutil.h" +#include "util.h" #define VMWAREDRV_PATH_64 "/usr/X11R6/lib64/modules/drivers/vmware_drv.o" #define VMWAREDRV_PATH "/usr/X11R6/lib/modules/drivers/vmware_drv.o" #define VERSION_STRING "VMware Guest X Server" +#define RR12_OUTPUT_FORMAT "LVDS%u" +#define RR12_MODE_FORMAT "autofit-%ux%u" +#define RR12_MODE_MAXLEN (sizeof RR12_MODE_FORMAT + 2 * (10 - 2) + 1) +#define RR12_DEFAULT_DPI 96.0 +#define MILLIS_PER_INCH 25.4 + /** * Describes the state of the X11 back-end of lib/resolution. @@ -62,6 +69,11 @@ typedef struct { // TRUE if VMwareCtrl extension available Bool canUseVMwareCtrlTopologySet; // TRUE if VMwareCtrl extension supports topology set + Bool canUseRandR12; // TRUE if RandR extension >= 1.2 available + unsigned int topologyDisplays; + // Number of displays in current topology + unsigned int topologyWidth; // Total width of current topology + unsigned int topologyHeight; // Total height of current topology } ResolutionInfoX11Type; @@ -77,6 +89,11 @@ ResolutionInfoX11Type resolutionInfoX11; static Bool ResolutionCanSet(void); static Bool TopologyCanSet(void); +#ifndef NO_MULTIMON +static Bool RandR12_SetTopology(unsigned int ndisplays, + xXineramaScreenInfo *displays, + unsigned int width, unsigned int height); +#endif static Bool SelectResolution(uint32 width, uint32 height); @@ -115,6 +132,7 @@ ResolutionBackendInit(InitHandle handle) resInfoX->canUseVMwareCtrl = VMwareCtrl_QueryVersion(resInfoX->display, &dummy1, &dummy2); resInfoX->canUseVMwareCtrlTopologySet = FALSE; + resInfoX->canUseRandR12 = FALSE; resInfo->canSetResolution = ResolutionCanSet(); resInfo->canSetTopology = TopologyCanSet(); @@ -241,18 +259,23 @@ ResolutionSetTopology(unsigned int ndisplays, displays[i].y_org -= minY; } - if (!VMwareCtrl_SetTopology(resInfoX->display, DefaultScreen(resInfoX->display), displays, - ndisplays)) { - Debug("Failed to set topology in the driver.\n"); - goto out; - } + if (resInfoX->canUseVMwareCtrlTopologySet) { + if (!VMwareCtrl_SetTopology(resInfoX->display, DefaultScreen(resInfoX->display), + displays, ndisplays)) { + Debug("Failed to set topology in the driver.\n"); + goto out; + } - if (!SelectResolution(maxX - minX, maxY - minY)) { - Debug("Failed to set new resolution.\n"); - goto out; - } + if (!SelectResolution(maxX - minX, maxY - minY)) { + Debug("Failed to set new resolution.\n"); + goto out; + } - success = TRUE; + success = TRUE; + } else if (resInfoX->canUseRandR12) { + success = RandR12_SetTopology(ndisplays, displays, + maxX - minX, maxY - minY); + } out: free(displays); @@ -299,6 +322,54 @@ ResolutionCanSet(void) return TRUE; } +#ifndef NO_MULTIMON + /* See if RandR >= 1.2 can be used: The extension version is high enough and + * all output names match the expected format. + */ + if (major > 1 || (major == 1 && minor >= 2)) { + XRRScreenResources* xrrRes; + XRROutputInfo* xrrOutput; + unsigned int num; + int i; + + XGrabServer(resInfoX->display); + + xrrRes = XRRGetScreenResources(resInfoX->display, resInfoX->rootWindow); + + if (xrrRes) { + for (i = 0; i < xrrRes->noutput; i++) { + xrrOutput = XRRGetOutputInfo(resInfoX->display, xrrRes, + xrrRes->outputs[i]); + if (!xrrOutput) { + break; + } + + if (sscanf(xrrOutput->name, RR12_OUTPUT_FORMAT, &num) != 1 || + num < 1) { + XRRFreeOutputInfo(xrrOutput); + break; + } + + XRRFreeOutputInfo(xrrOutput); + } + + if (i == xrrRes->noutput) { + resInfoX->canUseRandR12 = TRUE; + } else { + Debug("RandR >= 1.2 not usable\n"); + } + + XRRFreeScreenResources(xrrRes); + } + + XUngrabServer(resInfoX->display); + + if (resInfoX->canUseRandR12) { + return TRUE; + } + } +#endif // ifndef NO_MULTIMON + /* * XXX: This check does not work with XOrg 6.9/7.0 for two reasons: Both * versions now use .so for the driver extension and 7.0 moves the drivers @@ -373,10 +444,15 @@ TopologyCanSet(void) ResolutionInfoX11Type *resInfoX = &resolutionInfoX11; #ifdef NO_MULTIMON resInfoX->canUseVMwareCtrlTopologySet = FALSE; + return FALSE; #else int major; int minor; + if (resInfoX->canUseRandR12) { + return TRUE; + } + if (resInfoX->canUseVMwareCtrl && XineramaQueryVersion(resInfoX->display, &major, &minor)) { /* @@ -386,11 +462,277 @@ TopologyCanSet(void) } else { resInfoX->canUseVMwareCtrlTopologySet = FALSE; } -#endif return resInfoX->canUseVMwareCtrlTopologySet; +#endif +} + + +#ifndef NO_MULTIMON + +/** + * Employs the RandR 1.2 extension to set a new display topology. + * + * @return TRUE if operation succeeded, FALSE otherwise. + */ + +static Bool +RandR12_SetTopology(unsigned int ndisplays, + // IN: number of elements in topology + xXineramaScreenInfo *displays, + // IN: array of display geometries + unsigned int width, + // IN: total width of topology + unsigned int height) + // IN: total height of topology +{ + ResolutionInfoX11Type *resInfoX = &resolutionInfoX11; + int minWidth, minHeight, maxWidth, maxHeight; + XRRScreenResources* xrrRes = NULL; + XRROutputInfo** xrrOutputs; + unsigned int numOutputs; + unsigned int* outputMap; + XRRCrtcInfo** xrrCrtcs; + XRRScreenConfiguration* xrrConfig = NULL; + XRRScreenSize* xrrSizes; + Rotation xrrCurRotation; + uint32 xrrNumSizes; + SizeID currentSize; + XRRModeInfo xrrModes[ndisplays]; + char name[RR12_MODE_MAXLEN]; + float dpi; + int i, j, k; + Bool success = FALSE; + + if (!XRRGetScreenSizeRange(resInfoX->display, resInfoX->rootWindow, + &minWidth, &minHeight, &maxWidth, &maxHeight) || + width < minWidth || height < minHeight || + width > maxWidth || height > maxHeight) { + return FALSE; + } + + /* Grab the server for two reasons: + * - Avoid race conditions with other clients changing RandR configuration. + * - Make our changes appear as atomic as possible to other clients. + */ + XGrabServer(resInfoX->display); + + xrrRes = XRRGetScreenResources(resInfoX->display, resInfoX->rootWindow); + if (!xrrRes) { + goto error; + } + + xrrCrtcs = Util_SafeCalloc(sizeof *xrrCrtcs, xrrRes->ncrtc); + xrrOutputs = Util_SafeCalloc(sizeof *xrrOutputs, xrrRes->noutput); + outputMap = Util_SafeCalloc(sizeof *outputMap, xrrRes->noutput); + + /* RandR may enumerate outputs differently from the host. Apply the nth + * topology rectangle to the output called LVDS. + */ + for (i = 0; i < xrrRes->noutput; i++) { + outputMap[i] = i; + } + + numOutputs = 0; + for (i = 0; i < xrrRes->noutput; i++) { + unsigned int num; + + xrrOutputs[i] = XRRGetOutputInfo(resInfoX->display, xrrRes, + xrrRes->outputs[i]); + + if (!xrrOutputs[i]) { + goto error; + } + + if (sscanf(xrrOutputs[i]->name, RR12_OUTPUT_FORMAT, &num) != 1 || + num > ndisplays) { + continue; + } + + outputMap[num - 1] = i; + + if (num > numOutputs) { + numOutputs = num; + } + } + + /* Disable any CRTCs which won't be used or wont't fit in new screen size. */ + for (i = 0; i < xrrRes->ncrtc; i++) { + xrrCrtcs[i] = XRRGetCrtcInfo(resInfoX->display, xrrRes, + xrrRes->crtcs[i]); + + if (!xrrCrtcs[i]) { + goto error; + } + + for (j = 0; j < numOutputs; j++) { + if (xrrOutputs[outputMap[j]]->crtc == xrrRes->crtcs[i]) { + break; + } + } + + if (xrrCrtcs[i]->mode == None || + (j < numOutputs && + (xrrCrtcs[i]->x + xrrCrtcs[i]->width) <= width && + (xrrCrtcs[i]->y + xrrCrtcs[i]->height) <= height)) { + continue; + } + + if (XRRSetCrtcConfig(resInfoX->display, xrrRes, xrrRes->crtcs[i], + xrrCrtcs[i]->timestamp, 0, 0, None, RR_Rotate_0, NULL, 0) + != Success) { + goto error; + } + } + + /* Set new screen size. */ + xrrConfig = XRRGetScreenInfo(resInfoX->display, resInfoX->rootWindow); + xrrSizes = XRRConfigSizes(xrrConfig, &xrrNumSizes); + currentSize = XRRConfigCurrentConfiguration(xrrConfig, &xrrCurRotation); + + if (xrrSizes[currentSize].mheight > 0) { + dpi = MILLIS_PER_INCH * xrrSizes[currentSize].height / xrrSizes[currentSize].mheight; + + if (!dpi) { + dpi = RR12_DEFAULT_DPI; + } + } else { + dpi = RR12_DEFAULT_DPI; + } + + XRRSetScreenSize(resInfoX->display, resInfoX->rootWindow, width, height, + (MILLIS_PER_INCH * width) / dpi, + (MILLIS_PER_INCH * height) / dpi); + + /* Set new topology. */ + for (i = 0; i < numOutputs; i++) { + memset(xrrModes + i, 0, sizeof xrrModes[0]); + + xrrModes[i].width = displays[i].width; + xrrModes[i].height = displays[i].height; + + /* Look for existing matching autofit mode. */ + for (j = 0; j < i && !xrrModes[i].id; j++) { + if (xrrModes[j].id && + xrrModes[j].width == displays[i].width && + xrrModes[j].height == displays[i].height) { + xrrModes[i].id = xrrModes[j].id; + break; + } + } + + for (j = 0; j < xrrRes->nmode && !xrrModes[i].id; j++) { + unsigned int w, h; + + if (sscanf(xrrRes->modes[j].name, RR12_MODE_FORMAT, &w, &h) == 2 && + w == displays[i].width && h == displays[i].height) { + xrrModes[i].id = xrrRes->modes[j].id; + break; + } + } + + /* If no luck, create new autofit mode. */ + if (!xrrModes[i].id) { + Str_Sprintf(name, sizeof name, RR12_MODE_FORMAT, displays[i].width, + displays[i].height); + xrrModes[i].name = name; + xrrModes[i].nameLength = strlen(xrrModes[i].name); + xrrModes[i].id = XRRCreateMode(resInfoX->display, resInfoX->rootWindow, + xrrModes + i); + } + + if (xrrModes[i].id == None) { + continue; + } + + /* Set autofit mode. */ + if (xrrOutputs[outputMap[i]]->crtc == None) { + xrrOutputs[outputMap[i]]->crtc = xrrOutputs[outputMap[i]]->crtcs[0]; + } + + for (j = 0; j < xrrOutputs[outputMap[i]]->nmode; j++) { + if (xrrModes[i].id == xrrOutputs[outputMap[i]]->modes[j]) { + break; + } + } + if (j == xrrOutputs[outputMap[i]]->nmode) { + XRRAddOutputMode(resInfoX->display, xrrRes->outputs[outputMap[i]], + xrrModes[i].id); + } + if (XRRSetCrtcConfig(resInfoX->display, xrrRes, + xrrOutputs[outputMap[i]]->crtc, xrrCrtcs[i]->timestamp, + displays[i].x_org, displays[i].y_org, xrrModes[i].id, + RR_Rotate_0, xrrRes->outputs + outputMap[i], 1) + != Success) { + goto error; + } + } + + /* Delete unused autofit modes. */ + for (i = 0; i < xrrRes->nmode; i++) { + unsigned int w, h; + Bool destroy = TRUE; + + if (sscanf(xrrRes->modes[i].name, RR12_MODE_FORMAT, &w, &h) != 2) { + continue; + } + + for (j = 0; j < xrrRes->noutput; j++) { + if (j < numOutputs && + w == displays[j].width && h == displays[j].height) { + destroy = FALSE; + continue; + } + + for (k = 0; k < xrrOutputs[outputMap[j]]->nmode; k++) { + if (xrrOutputs[outputMap[j]]->modes[k] == xrrRes->modes[i].id) { + XRRDeleteOutputMode(resInfoX->display, + xrrRes->outputs[outputMap[j]], + xrrOutputs[outputMap[j]]->modes[k]); + break; + } + } + } + + if (destroy) { + XRRDestroyMode(resInfoX->display, xrrRes->modes[i].id); + } + } + + resInfoX->topologyDisplays = ndisplays; + resInfoX->topologyWidth = width; + resInfoX->topologyHeight = height; + + success = TRUE; + +error: + XUngrabServer(resInfoX->display); + + if (xrrConfig) { + XRRFreeScreenConfigInfo(xrrConfig); + } + if (xrrRes) { + for (i = 0; i < xrrRes->noutput; i++) { + if (xrrOutputs[i]) { + XRRFreeOutputInfo(xrrOutputs[i]); + } + } + for (i = 0; i < xrrRes->ncrtc; i++) { + if (xrrCrtcs[i]) { + XRRFreeCrtcInfo(xrrCrtcs[i]); + } + } + free(outputMap); + free(xrrOutputs); + free(xrrCrtcs); + XRRFreeScreenResources(xrrRes); + } + + return success; } +#endif // ifndef NO_MULTIMON + /** * Given a width and height, find the biggest resolution that will "fit". @@ -415,6 +757,25 @@ SelectResolution(uint32 width, uint64 bestFitSize = 0; uint64 potentialSize; +#ifndef NO_MULTIMON + if (resInfoX->canUseRandR12) { + if (resInfoX->topologyDisplays != 1 || + resInfoX->topologyWidth != width || + resInfoX->topologyHeight != height) { + xXineramaScreenInfo display; + + display.x_org = 0; + display.y_org = 0; + display.width = width; + display.height = height; + + return RandR12_SetTopology(1, &display, width, height); + } + + return TRUE; + } +#endif + xrrConfig = XRRGetScreenInfo(resInfoX->display, resInfoX->rootWindow); xrrSizes = XRRConfigSizes(xrrConfig, &xrrNumSizes); XRRConfigCurrentConfiguration(xrrConfig, &xrrCurRotation);