summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBryce Harrington <bryce@canonical.com>2011-12-01 14:38:08 -0800
committerBryce Harrington <bryce@canonical.com>2012-01-06 12:34:48 -0800
commit4c819b778118943f1cba573b8fa3eb40f4dd05d3 (patch)
tree9e6afa5c1e1e3a8b0fa429458a958b5a92583bfa
parent5cbb84015ef9d1915d73abf63e22791d1b3ad5be (diff)
Import xrandr.c as an implementation example.
Over the course of this development effort, code will be refactored out of xrandr.c into the library source. As this is done, the corresponding code will be excised from this copy of xrandr.c and replaced with the new API calls. This commit pulls in the unchanged xrandr.c from the xrandr package. This will enable before/after testing of xrandr behavior to catch functional regressions. Ultimately, prior to libXrandrUtils 1.0.0, this work should be complete and xrandr.c migrated back home to the xrandr package. Signed-off-by: Bryce Harrington <bryce@canonical.com>
-rw-r--r--Makefile.am2
-rw-r--r--configure.ac1
-rw-r--r--examples/Makefile.am13
-rw-r--r--examples/xrandr.c3433
4 files changed, 3448 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am
index 1b5ac68..607dfc1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -19,7 +19,7 @@
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
-SUBDIRS = man src
+SUBDIRS = examples man src
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = xrandr-utils.pc
diff --git a/configure.ac b/configure.ac
index f2c350f..b3694f8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -55,6 +55,7 @@ PKG_CHECK_MODULES(RANDR_UTILS, x11 xrandr >= 1.3 xrender xproto >= 7.0.17)
AC_CONFIG_FILES([
Makefile
+ examples/Makefile
man/Makefile
src/Makefile
xrandr-utils.pc])
diff --git a/examples/Makefile.am b/examples/Makefile.am
new file mode 100644
index 0000000..c993ccc
--- /dev/null
+++ b/examples/Makefile.am
@@ -0,0 +1,13 @@
+bin_PROGRAMS = xrandr
+
+AM_CFLAGS = \
+ $(RANDR_UTILS_CFLAGS) \
+ $(MALLOC_ZERO_CFLAGS) \
+ $(CWARNFLAGS)
+xrandr_LDADD = @RANDR_UTILS_LIBS@
+
+INCLUDES = -I$(top_srcdir)/include/X11/extensions
+
+xrandr_SOURCES = \
+ xrandr.c
+
diff --git a/examples/xrandr.c b/examples/xrandr.c
new file mode 100644
index 0000000..35dff3e
--- /dev/null
+++ b/examples/xrandr.c
@@ -0,0 +1,3433 @@
+/*
+ * Copyright © 2001 Keith Packard, member of The XFree86 Project, Inc.
+ * Copyright © 2002 Hewlett Packard Company, Inc.
+ * Copyright © 2006 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no representations
+ * about the suitability of this software for any purpose. It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ *
+ * Thanks to Jim Gettys who wrote most of the client side code,
+ * and part of the server code for randr.
+ */
+
+#include <stdio.h>
+#include <X11/Xlib.h>
+#include <X11/Xlibint.h>
+#include <X11/Xproto.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/Xrandr.h>
+#include <X11/extensions/Xrender.h> /* we share subpixel information */
+#include <strings.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <math.h>
+
+#include "config.h"
+
+static char *program_name;
+static Display *dpy;
+static Window root;
+static int screen = -1;
+static Bool verbose = False;
+static Bool automatic = False;
+static Bool properties = False;
+static Bool grab_server = True;
+static Bool no_primary = False;
+
+static char *direction[5] = {
+ "normal",
+ "left",
+ "inverted",
+ "right",
+ "\n"};
+
+static char *reflections[5] = {
+ "normal",
+ "x",
+ "y",
+ "xy",
+ "\n"};
+
+/* subpixel order */
+static char *order[6] = {
+ "unknown",
+ "horizontal rgb",
+ "horizontal bgr",
+ "vertical rgb",
+ "vertical bgr",
+ "no subpixels"};
+
+static const struct {
+ char *string;
+ unsigned long flag;
+} mode_flags[] = {
+ { "+HSync", RR_HSyncPositive },
+ { "-HSync", RR_HSyncNegative },
+ { "+VSync", RR_VSyncPositive },
+ { "-VSync", RR_VSyncNegative },
+ { "Interlace", RR_Interlace },
+ { "DoubleScan", RR_DoubleScan },
+ { "CSync", RR_CSync },
+ { "+CSync", RR_CSyncPositive },
+ { "-CSync", RR_CSyncNegative },
+ { NULL, 0 }
+};
+
+static void _X_NORETURN
+usage(void)
+{
+ fprintf(stderr, "usage: %s [options]\n", program_name);
+ fprintf(stderr, " where options are:\n");
+ fprintf(stderr, " -display <display> or -d <display>\n");
+ fprintf(stderr, " -help\n");
+ fprintf(stderr, " -o <normal,inverted,left,right,0,1,2,3>\n");
+ fprintf(stderr, " or --orientation <normal,inverted,left,right,0,1,2,3>\n");
+ fprintf(stderr, " -q or --query\n");
+ fprintf(stderr, " -s <size>/<width>x<height> or --size <size>/<width>x<height>\n");
+ fprintf(stderr, " -r <rate> or --rate <rate> or --refresh <rate>\n");
+ fprintf(stderr, " -v or --version\n");
+ fprintf(stderr, " -x (reflect in x)\n");
+ fprintf(stderr, " -y (reflect in y)\n");
+ fprintf(stderr, " --screen <screen>\n");
+ fprintf(stderr, " --verbose\n");
+ fprintf(stderr, " --current\n");
+ fprintf(stderr, " --dryrun\n");
+ fprintf(stderr, " --nograb\n");
+ fprintf(stderr, " --prop or --properties\n");
+ fprintf(stderr, " --fb <width>x<height>\n");
+ fprintf(stderr, " --fbmm <width>x<height>\n");
+ fprintf(stderr, " --dpi <dpi>/<output>\n");
+ fprintf(stderr, " --output <output>\n");
+ fprintf(stderr, " --auto\n");
+ fprintf(stderr, " --mode <mode>\n");
+ fprintf(stderr, " --preferred\n");
+ fprintf(stderr, " --pos <x>x<y>\n");
+ fprintf(stderr, " --rate <rate> or --refresh <rate>\n");
+ fprintf(stderr, " --reflect normal,x,y,xy\n");
+ fprintf(stderr, " --rotate normal,inverted,left,right\n");
+ fprintf(stderr, " --left-of <output>\n");
+ fprintf(stderr, " --right-of <output>\n");
+ fprintf(stderr, " --above <output>\n");
+ fprintf(stderr, " --below <output>\n");
+ fprintf(stderr, " --same-as <output>\n");
+ fprintf(stderr, " --set <property> <value>\n");
+ fprintf(stderr, " --scale <x>x<y>\n");
+ fprintf(stderr, " --transform <a>,<b>,<c>,<d>,<e>,<f>,<g>,<h>,<i>\n");
+ fprintf(stderr, " --off\n");
+ fprintf(stderr, " --crtc <crtc>\n");
+ fprintf(stderr, " --panning <w>x<h>[+<x>+<y>[/<track:w>x<h>+<x>+<y>[/<border:l>/<t>/<r>/<b>]]]\n");
+ fprintf(stderr, " --gamma <r>:<g>:<b>\n");
+ fprintf(stderr, " --primary\n");
+ fprintf(stderr, " --noprimary\n");
+ fprintf(stderr, " --newmode <name> <clock MHz>\n");
+ fprintf(stderr, " <hdisp> <hsync-start> <hsync-end> <htotal>\n");
+ fprintf(stderr, " <vdisp> <vsync-start> <vsync-end> <vtotal>\n");
+ fprintf(stderr, " [flags...]\n");
+ fprintf(stderr, " Valid flags: +HSync -HSync +VSync -VSync\n");
+ fprintf(stderr, " +CSync -CSync CSync Interlace DoubleScan\n");
+ fprintf(stderr, " --rmmode <name>\n");
+ fprintf(stderr, " --addmode <output> <name>\n");
+ fprintf(stderr, " --delmode <output> <name>\n");
+
+ exit(1);
+ /*NOTREACHED*/
+}
+
+static void _X_NORETURN
+fatal (const char *format, ...)
+{
+ va_list ap;
+
+ va_start (ap, format);
+ fprintf (stderr, "%s: ", program_name);
+ vfprintf (stderr, format, ap);
+ va_end (ap);
+ exit (1);
+ /*NOTREACHED*/
+}
+
+static void
+warning (const char *format, ...)
+{
+ va_list ap;
+
+ va_start (ap, format);
+ fprintf (stderr, "%s: ", program_name);
+ vfprintf (stderr, format, ap);
+ va_end (ap);
+}
+
+/* Because fmin requires C99 suppport */
+static inline double dmin (double x, double y)
+{
+ return x < y ? x : y;
+}
+
+static char *
+rotation_name (Rotation rotation)
+{
+ int i;
+
+ if ((rotation & 0xf) == 0)
+ return "normal";
+ for (i = 0; i < 4; i++)
+ if (rotation & (1 << i))
+ return direction[i];
+ return "invalid rotation";
+}
+
+static char *
+reflection_name (Rotation rotation)
+{
+ rotation &= (RR_Reflect_X|RR_Reflect_Y);
+ switch (rotation) {
+ case 0:
+ return "none";
+ case RR_Reflect_X:
+ return "X axis";
+ case RR_Reflect_Y:
+ return "Y axis";
+ case RR_Reflect_X|RR_Reflect_Y:
+ return "X and Y axis";
+ }
+ return "invalid reflection";
+}
+
+typedef enum _relation {
+ relation_left_of,
+ relation_right_of,
+ relation_above,
+ relation_below,
+ relation_same_as,
+} relation_t;
+
+typedef struct {
+ int x, y, width, height;
+} rectangle_t;
+
+typedef struct {
+ int x1, y1, x2, y2;
+} box_t;
+
+typedef struct {
+ int x, y;
+} point_t;
+
+typedef enum _changes {
+ changes_none = 0,
+ changes_crtc = (1 << 0),
+ changes_mode = (1 << 1),
+ changes_relation = (1 << 2),
+ changes_position = (1 << 3),
+ changes_rotation = (1 << 4),
+ changes_reflection = (1 << 5),
+ changes_automatic = (1 << 6),
+ changes_refresh = (1 << 7),
+ changes_property = (1 << 8),
+ changes_transform = (1 << 9),
+ changes_panning = (1 << 10),
+ changes_gamma = (1 << 11),
+ changes_primary = (1 << 12),
+} changes_t;
+
+typedef enum _name_kind {
+ name_none = 0,
+ name_string = (1 << 0),
+ name_xid = (1 << 1),
+ name_index = (1 << 2),
+ name_preferred = (1 << 3),
+} name_kind_t;
+
+typedef struct {
+ name_kind_t kind;
+ char *string;
+ XID xid;
+ int index;
+} name_t;
+
+typedef struct _crtc crtc_t;
+typedef struct _output output_t;
+typedef struct _transform transform_t;
+typedef struct _umode umode_t;
+typedef struct _output_prop output_prop_t;
+
+struct _transform {
+ XTransform transform;
+ char *filter;
+ int nparams;
+ XFixed *params;
+};
+
+struct _crtc {
+ name_t crtc;
+ Bool changing;
+ XRRCrtcInfo *crtc_info;
+
+ XRRModeInfo *mode_info;
+ XRRPanning *panning_info;
+ int x;
+ int y;
+ Rotation rotation;
+ output_t **outputs;
+ int noutput;
+ transform_t current_transform, pending_transform;
+};
+
+struct _output_prop {
+ struct _output_prop *next;
+ char *name;
+ char *value;
+};
+
+struct _output {
+ struct _output *next;
+
+ changes_t changes;
+
+ output_prop_t *props;
+
+ name_t output;
+ XRROutputInfo *output_info;
+
+ name_t crtc;
+ crtc_t *crtc_info;
+ crtc_t *current_crtc_info;
+
+ name_t mode;
+ double refresh;
+ XRRModeInfo *mode_info;
+
+ name_t addmode;
+
+ relation_t relation;
+ char *relative_to;
+
+ int x, y;
+ Rotation rotation;
+
+ XRRPanning panning;
+
+ Bool automatic;
+ transform_t transform;
+
+ struct {
+ float red;
+ float green;
+ float blue;
+ } gamma;
+
+ float brightness;
+
+ Bool primary;
+
+ Bool found;
+};
+
+typedef enum _umode_action {
+ umode_create, umode_destroy, umode_add, umode_delete
+} umode_action_t;
+
+
+struct _umode {
+ struct _umode *next;
+
+ umode_action_t action;
+ XRRModeInfo mode;
+ name_t output;
+ name_t name;
+};
+
+static char *connection[3] = {
+ "connected",
+ "disconnected",
+ "unknown connection"};
+
+#define OUTPUT_NAME 1
+
+#define CRTC_OFF 2
+#define CRTC_UNSET 3
+#define CRTC_INDEX 0x40000000
+
+#define MODE_NAME 1
+#define MODE_OFF 2
+#define MODE_UNSET 3
+#define MODE_PREF 4
+
+#define POS_UNSET -1
+
+static output_t *outputs = NULL;
+static output_t **outputs_tail = &outputs;
+static crtc_t *crtcs;
+static umode_t *umodes;
+static int num_crtcs;
+static XRRScreenResources *res;
+static int fb_width = 0, fb_height = 0;
+static int fb_width_mm = 0, fb_height_mm = 0;
+static double dpi = 0;
+static char *dpi_output = NULL;
+static Bool dryrun = False;
+static int minWidth, maxWidth, minHeight, maxHeight;
+static Bool has_1_2 = False;
+static Bool has_1_3 = False;
+
+static int
+mode_height (XRRModeInfo *mode_info, Rotation rotation)
+{
+ switch (rotation & 0xf) {
+ case RR_Rotate_0:
+ case RR_Rotate_180:
+ return mode_info->height;
+ case RR_Rotate_90:
+ case RR_Rotate_270:
+ return mode_info->width;
+ default:
+ return 0;
+ }
+}
+
+static int
+mode_width (XRRModeInfo *mode_info, Rotation rotation)
+{
+ switch (rotation & 0xf) {
+ case RR_Rotate_0:
+ case RR_Rotate_180:
+ return mode_info->width;
+ case RR_Rotate_90:
+ case RR_Rotate_270:
+ return mode_info->height;
+ default:
+ return 0;
+ }
+}
+
+static Bool
+transform_point (XTransform *transform, double *xp, double *yp)
+{
+ double vector[3];
+ double result[3];
+ int i, j;
+ double v;
+
+ vector[0] = *xp;
+ vector[1] = *yp;
+ vector[2] = 1;
+ for (j = 0; j < 3; j++)
+ {
+ v = 0;
+ for (i = 0; i < 3; i++)
+ v += (XFixedToDouble (transform->matrix[j][i]) * vector[i]);
+ if (v > 32767 || v < -32767)
+ return False;
+ result[j] = v;
+ }
+ if (!result[2])
+ return False;
+ for (j = 0; j < 2; j++)
+ vector[j] = result[j] / result[2];
+ *xp = vector[0];
+ *yp = vector[1];
+ return True;
+}
+
+static void
+path_bounds (XTransform *transform, point_t *points, int npoints, box_t *box)
+{
+ int i;
+ box_t point;
+
+ for (i = 0; i < npoints; i++) {
+ double x, y;
+ x = points[i].x;
+ y = points[i].y;
+ transform_point (transform, &x, &y);
+ point.x1 = floor (x);
+ point.y1 = floor (y);
+ point.x2 = ceil (x);
+ point.y2 = ceil (y);
+ if (i == 0)
+ *box = point;
+ else {
+ if (point.x1 < box->x1) box->x1 = point.x1;
+ if (point.y1 < box->y1) box->y1 = point.y1;
+ if (point.x2 > box->x2) box->x2 = point.x2;
+ if (point.y2 > box->y2) box->y2 = point.y2;
+ }
+ }
+}
+
+static void
+mode_geometry (XRRModeInfo *mode_info, Rotation rotation,
+ XTransform *transform,
+ box_t *bounds)
+{
+ point_t rect[4];
+ int width = mode_width (mode_info, rotation);
+ int height = mode_height (mode_info, rotation);
+
+ rect[0].x = 0;
+ rect[0].y = 0;
+ rect[1].x = width;
+ rect[1].y = 0;
+ rect[2].x = width;
+ rect[2].y = height;
+ rect[3].x = 0;
+ rect[3].y = height;
+ path_bounds (transform, rect, 4, bounds);
+}
+
+/* v refresh frequency in Hz */
+static double
+mode_refresh (XRRModeInfo *mode_info)
+{
+ double rate;
+
+ if (mode_info->hTotal && mode_info->vTotal)
+ rate = ((double) mode_info->dotClock /
+ ((double) mode_info->hTotal * (double) mode_info->vTotal));
+ else
+ rate = 0;
+ return rate;
+}
+
+/* h sync frequency in Hz */
+static double
+mode_hsync (XRRModeInfo *mode_info)
+{
+ double rate;
+
+ if (mode_info->hTotal)
+ rate = (double) mode_info->dotClock / (double) mode_info->hTotal;
+ else
+ rate = 0;
+ return rate;
+}
+
+static void
+init_name (name_t *name)
+{
+ name->kind = name_none;
+}
+
+static void
+set_name_string (name_t *name, char *string)
+{
+ name->kind |= name_string;
+ name->string = string;
+}
+
+static void
+set_name_xid (name_t *name, XID xid)
+{
+ name->kind |= name_xid;
+ name->xid = xid;
+}
+
+static void
+set_name_index (name_t *name, int index)
+{
+ name->kind |= name_index;
+ name->index = index;
+}
+
+static void
+set_name_preferred (name_t *name)
+{
+ name->kind |= name_preferred;
+}
+
+static void
+set_name_all (name_t *name, name_t *old)
+{
+ if (old->kind & name_xid)
+ name->xid = old->xid;
+ if (old->kind & name_string)
+ name->string = old->string;
+ if (old->kind & name_index)
+ name->index = old->index;
+ name->kind |= old->kind;
+}
+
+static void
+set_name (name_t *name, char *string, name_kind_t valid)
+{
+ unsigned int xid; /* don't make it XID (which is unsigned long):
+ scanf() takes unsigned int */
+ int index;
+
+ if ((valid & name_xid) && sscanf (string, "0x%x", &xid) == 1)
+ set_name_xid (name, xid);
+ else if ((valid & name_index) && sscanf (string, "%d", &index) == 1)
+ set_name_index (name, index);
+ else if (valid & name_string)
+ set_name_string (name, string);
+ else
+ usage ();
+}
+
+static void
+init_transform (transform_t *transform)
+{
+ int x;
+ memset (&transform->transform, '\0', sizeof (transform->transform));
+ for (x = 0; x < 3; x++)
+ transform->transform.matrix[x][x] = XDoubleToFixed (1.0);
+ transform->filter = "";
+ transform->nparams = 0;
+ transform->params = NULL;
+}
+
+static void
+set_transform (transform_t *dest,
+ XTransform *transform,
+ char *filter,
+ XFixed *params,
+ int nparams)
+{
+ dest->transform = *transform;
+ dest->filter = strdup (filter);
+ dest->nparams = nparams;
+ dest->params = malloc (nparams * sizeof (XFixed));
+ memcpy (dest->params, params, nparams * sizeof (XFixed));
+}
+
+static void
+copy_transform (transform_t *dest, transform_t *src)
+{
+ set_transform (dest, &src->transform,
+ src->filter, src->params, src->nparams);
+}
+
+static Bool
+equal_transform (transform_t *a, transform_t *b)
+{
+ if (memcmp (&a->transform, &b->transform, sizeof (XTransform)) != 0)
+ return False;
+ if (strcmp (a->filter, b->filter) != 0)
+ return False;
+ if (a->nparams != b->nparams)
+ return False;
+ if (memcmp (a->params, b->params, a->nparams * sizeof (XFixed)) != 0)
+ return False;
+ return True;
+}
+
+static output_t *
+add_output (void)
+{
+ output_t *output = calloc (1, sizeof (output_t));
+
+ if (!output)
+ fatal ("out of memory\n");
+ output->next = NULL;
+ output->found = False;
+ output->brightness = 1.0;
+ *outputs_tail = output;
+ outputs_tail = &output->next;
+ return output;
+}
+
+static output_t *
+find_output (name_t *name)
+{
+ output_t *output;
+
+ for (output = outputs; output; output = output->next)
+ {
+ name_kind_t common = name->kind & output->output.kind;
+
+ if ((common & name_xid) && name->xid == output->output.xid)
+ break;
+ if ((common & name_string) && !strcmp (name->string, output->output.string))
+ break;
+ if ((common & name_index) && name->index == output->output.index)
+ break;
+ }
+ return output;
+}
+
+static output_t *
+find_output_by_xid (RROutput output)
+{
+ name_t output_name;
+
+ init_name (&output_name);
+ set_name_xid (&output_name, output);
+ return find_output (&output_name);
+}
+
+static output_t *
+find_output_by_name (char *name)
+{
+ name_t output_name;
+
+ init_name (&output_name);
+ set_name_string (&output_name, name);
+ return find_output (&output_name);
+}
+
+static crtc_t *
+find_crtc (name_t *name)
+{
+ int c;
+ crtc_t *crtc = NULL;
+
+ for (c = 0; c < num_crtcs; c++)
+ {
+ name_kind_t common;
+
+ crtc = &crtcs[c];
+ common = name->kind & crtc->crtc.kind;
+
+ if ((common & name_xid) && name->xid == crtc->crtc.xid)
+ break;
+ if ((common & name_string) && !strcmp (name->string, crtc->crtc.string))
+ break;
+ if ((common & name_index) && name->index == crtc->crtc.index)
+ break;
+ crtc = NULL;
+ }
+ return crtc;
+}
+
+static crtc_t *
+find_crtc_by_xid (RRCrtc crtc)
+{
+ name_t crtc_name;
+
+ init_name (&crtc_name);
+ set_name_xid (&crtc_name, crtc);
+ return find_crtc (&crtc_name);
+}
+
+static XRRModeInfo *
+find_mode (name_t *name, double refresh)
+{
+ int m;
+ XRRModeInfo *best = NULL;
+ double bestDist = 0;
+
+ for (m = 0; m < res->nmode; m++)
+ {
+ XRRModeInfo *mode = &res->modes[m];
+ if ((name->kind & name_xid) && name->xid == mode->id)
+ {
+ best = mode;
+ break;
+ }
+ if ((name->kind & name_string) && !strcmp (name->string, mode->name))
+ {
+ double dist;
+
+ if (refresh)
+ dist = fabs (mode_refresh (mode) - refresh);
+ else
+ dist = 0;
+ if (!best || dist < bestDist)
+ {
+ bestDist = dist;
+ best = mode;
+ }
+ }
+ }
+ return best;
+}
+
+static XRRModeInfo *
+find_mode_by_xid (RRMode mode)
+{
+ name_t mode_name;
+
+ init_name (&mode_name);
+ set_name_xid (&mode_name, mode);
+ return find_mode (&mode_name, 0);
+}
+
+#if 0
+static XRRModeInfo *
+find_mode_by_name (char *name)
+{
+ name_t mode_name;
+ init_name (&mode_name);
+ set_name_string (&mode_name, name);
+ return find_mode (&mode_name, 0);
+}
+#endif
+
+static
+XRRModeInfo *
+find_mode_for_output (output_t *output, name_t *name)
+{
+ XRROutputInfo *output_info = output->output_info;
+ int m;
+ XRRModeInfo *best = NULL;
+ double bestDist = 0;
+
+ for (m = 0; m < output_info->nmode; m++)
+ {
+ XRRModeInfo *mode;
+
+ mode = find_mode_by_xid (output_info->modes[m]);
+ if (!mode) continue;
+ if ((name->kind & name_xid) && name->xid == mode->id)
+ {
+ best = mode;
+ break;
+ }
+ if ((name->kind & name_string) && !strcmp (name->string, mode->name))
+ {
+ double dist;
+
+ /* Stay away from doublescan modes unless refresh rate is specified. */
+ if (!output->refresh && (mode->modeFlags & RR_DoubleScan))
+ continue;
+
+ if (output->refresh)
+ dist = fabs (mode_refresh (mode) - output->refresh);
+ else
+ dist = 0;
+ if (!best || dist < bestDist)
+ {
+ bestDist = dist;
+ best = mode;
+ }
+ }
+ }
+ return best;
+}
+
+static XRRModeInfo *
+preferred_mode (output_t *output)
+{
+ XRROutputInfo *output_info = output->output_info;
+ int m;
+ XRRModeInfo *best;
+ int bestDist;
+
+ best = NULL;
+ bestDist = 0;
+ for (m = 0; m < output_info->nmode; m++)
+ {
+ XRRModeInfo *mode_info = find_mode_by_xid (output_info->modes[m]);
+ int dist;
+
+ if (m < output_info->npreferred)
+ dist = 0;
+ else if (output_info->mm_height)
+ dist = (1000 * DisplayHeight(dpy, screen) / DisplayHeightMM(dpy, screen) -
+ 1000 * mode_info->height / output_info->mm_height);
+ else
+ dist = DisplayHeight(dpy, screen) - mode_info->height;
+
+ if (dist < 0) dist = -dist;
+ if (!best || dist < bestDist)
+ {
+ best = mode_info;
+ bestDist = dist;
+ }
+ }
+ return best;
+}
+
+static Bool
+output_can_use_crtc (output_t *output, crtc_t *crtc)
+{
+ XRROutputInfo *output_info = output->output_info;
+ int c;
+
+ for (c = 0; c < output_info->ncrtc; c++)
+ if (output_info->crtcs[c] == crtc->crtc.xid)
+ return True;
+ return False;
+}
+
+static Bool
+output_can_use_mode (output_t *output, XRRModeInfo *mode)
+{
+ XRROutputInfo *output_info = output->output_info;
+ int m;
+
+ for (m = 0; m < output_info->nmode; m++)
+ if (output_info->modes[m] == mode->id)
+ return True;
+ return False;
+}
+
+static Bool
+crtc_can_use_rotation (crtc_t *crtc, Rotation rotation)
+{
+ Rotation rotations = crtc->crtc_info->rotations;
+ Rotation dir = rotation & (RR_Rotate_0|RR_Rotate_90|RR_Rotate_180|RR_Rotate_270);
+ Rotation reflect = rotation & (RR_Reflect_X|RR_Reflect_Y);
+ if (((rotations & dir) != 0) && ((rotations & reflect) == reflect))
+ return True;
+ return False;
+}
+
+#if 0
+static Bool
+crtc_can_use_transform (crtc_t *crtc, XTransform *transform)
+{
+ int major, minor;
+
+ XRRQueryVersion (dpy, &major, &minor);
+ if (major > 1 || (major == 1 && minor >= 3))
+ return True;
+ return False;
+}
+#endif
+
+/*
+ * Report only rotations that are supported by all crtcs
+ */
+static Rotation
+output_rotations (output_t *output)
+{
+ Bool found = False;
+ Rotation rotation = RR_Rotate_0;
+ XRROutputInfo *output_info = output->output_info;
+ int c;
+
+ for (c = 0; c < output_info->ncrtc; c++)
+ {
+ crtc_t *crtc = find_crtc_by_xid (output_info->crtcs[c]);
+ if (crtc)
+ {
+ if (!found) {
+ rotation = crtc->crtc_info->rotations;
+ found = True;
+ } else
+ rotation &= crtc->crtc_info->rotations;
+ }
+ }
+ return rotation;
+}
+
+static Bool
+output_can_use_rotation (output_t *output, Rotation rotation)
+{
+ XRROutputInfo *output_info = output->output_info;
+ int c;
+
+ /* make sure all of the crtcs can use this rotation.
+ * yes, this is not strictly necessary, but it is
+ * simpler,and we expect most drivers to either
+ * support rotation everywhere or nowhere
+ */
+ for (c = 0; c < output_info->ncrtc; c++)
+ {
+ crtc_t *crtc = find_crtc_by_xid (output_info->crtcs[c]);
+ if (crtc && !crtc_can_use_rotation (crtc, rotation))
+ return False;
+ }
+ return True;
+}
+
+static Bool
+output_is_primary(output_t *output)
+{
+ if (has_1_3)
+ return XRRGetOutputPrimary(dpy, root) == output->output.xid;
+ return False;
+}
+
+/* Returns the index of the last value in an array < 0xffff */
+static int
+find_last_non_clamped(CARD16 array[], int size) {
+ int i;
+ for (i = size - 1; i > 0; i--) {
+ if (array[i] < 0xffff)
+ return i;
+ }
+ return 0;
+}
+
+static void
+set_gamma_info(output_t *output)
+{
+ XRRCrtcGamma *gamma;
+ double i1, v1, i2, v2;
+ int size, middle, last_best, last_red, last_green, last_blue;
+ CARD16 *best_array;
+
+ if (!output->crtc_info)
+ return;
+
+ size = XRRGetCrtcGammaSize(dpy, output->crtc_info->crtc.xid);
+ if (!size) {
+ warning("Failed to get size of gamma for output %s\n", output->output.string);
+ return;
+ }
+
+ gamma = XRRGetCrtcGamma(dpy, output->crtc_info->crtc.xid);
+ if (!gamma) {
+ warning("Failed to get gamma for output %s\n", output->output.string);
+ return;
+ }
+
+ /*
+ * Here is a bit tricky because gamma is a whole curve for each
+ * color. So, typically, we need to represent 3 * 256 values as 3 + 1
+ * values. Therefore, we approximate the gamma curve (v) by supposing
+ * it always follows the way we set it: a power function (i^g)
+ * multiplied by a brightness (b).
+ * v = i^g * b
+ * so g = (ln(v) - ln(b))/ln(i)
+ * and b can be found using two points (v1,i1) and (v2, i2):
+ * b = e^((ln(v2)*ln(i1) - ln(v1)*ln(i2))/ln(i1/i2))
+ * For the best resolution, we select i2 at the highest place not
+ * clamped and i1 at i2/2. Note that if i2 = 1 (as in most normal
+ * cases), then b = v2.
+ */
+ last_red = find_last_non_clamped(gamma->red, size);
+ last_green = find_last_non_clamped(gamma->green, size);
+ last_blue = find_last_non_clamped(gamma->blue, size);
+ best_array = gamma->red;
+ last_best = last_red;
+ if (last_green > last_best) {
+ last_best = last_green;
+ best_array = gamma->green;
+ }
+ if (last_blue > last_best) {
+ last_best = last_blue;
+ best_array = gamma->blue;
+ }
+ if (last_best == 0)
+ last_best = 1;
+
+ middle = last_best / 2;
+ i1 = (double)(middle + 1) / size;
+ v1 = (double)(best_array[middle]) / 65535;
+ i2 = (double)(last_best + 1) / size;
+ v2 = (double)(best_array[last_best]) / 65535;
+ if (v2 < 0.0001) { /* The screen is black */
+ output->brightness = 0;
+ output->gamma.red = 1;
+ output->gamma.green = 1;
+ output->gamma.blue = 1;
+ } else {
+ if ((last_best + 1) == size)
+ output->brightness = v2;
+ else
+ output->brightness = exp((log(v2)*log(i1) - log(v1)*log(i2))/log(i1/i2));
+ output->gamma.red = log((double)(gamma->red[last_red / 2]) / output->brightness
+ / 65535) / log((double)((last_red / 2) + 1) / size);
+ output->gamma.green = log((double)(gamma->green[last_green / 2]) / output->brightness
+ / 65535) / log((double)((last_green / 2) + 1) / size);
+ output->gamma.blue = log((double)(gamma->blue[last_blue / 2]) / output->brightness
+ / 65535) / log((double)((last_blue / 2) + 1) / size);
+ }
+
+ XRRFreeGamma(gamma);
+}
+
+static void
+set_output_info (output_t *output, RROutput xid, XRROutputInfo *output_info)
+{
+ /* sanity check output info */
+ if (output_info->connection != RR_Disconnected && !output_info->nmode)
+ warning ("Output %s is not disconnected but has no modes\n",
+ output_info->name);
+
+ /* set output name and info */
+ if (!(output->output.kind & name_xid))
+ set_name_xid (&output->output, xid);
+ if (!(output->output.kind & name_string))
+ set_name_string (&output->output, output_info->name);
+ output->output_info = output_info;
+
+ /* set crtc name and info */
+ if (!(output->changes & changes_crtc))
+ set_name_xid (&output->crtc, output_info->crtc);
+
+ if (output->crtc.kind == name_xid && output->crtc.xid == None)
+ output->crtc_info = NULL;
+ else
+ {
+ output->crtc_info = find_crtc (&output->crtc);
+ if (!output->crtc_info)
+ {
+ if (output->crtc.kind & name_xid)
+ fatal ("cannot find crtc 0x%x\n", output->crtc.xid);
+ if (output->crtc.kind & name_index)
+ fatal ("cannot find crtc %d\n", output->crtc.index);
+ }
+ if (!output_can_use_crtc (output, output->crtc_info))
+ fatal ("output %s cannot use crtc 0x%x\n", output->output.string,
+ output->crtc_info->crtc.xid);
+ }
+
+ /* set mode name and info */
+ if (!(output->changes & changes_mode))
+ {
+ crtc_t *crtc = NULL;
+
+ if (output_info->crtc)
+ crtc = find_crtc_by_xid(output_info->crtc);
+ if (crtc && crtc->crtc_info)
+ set_name_xid (&output->mode, crtc->crtc_info->mode);
+ else if (output->crtc_info)
+ set_name_xid (&output->mode, output->crtc_info->crtc_info->mode);
+ else
+ set_name_xid (&output->mode, None);
+ if (output->mode.xid)
+ {
+ output->mode_info = find_mode_by_xid (output->mode.xid);
+ if (!output->mode_info)
+ fatal ("server did not report mode 0x%x for output %s\n",
+ output->mode.xid, output->output.string);
+ }
+ else
+ output->mode_info = NULL;
+ }
+ else if (output->mode.kind == name_xid && output->mode.xid == None)
+ output->mode_info = NULL;
+ else
+ {
+ if (output->mode.kind == name_preferred)
+ output->mode_info = preferred_mode (output);
+ else
+ output->mode_info = find_mode_for_output (output, &output->mode);
+ if (!output->mode_info)
+ {
+ if (output->mode.kind & name_preferred)
+ fatal ("cannot find preferred mode\n");
+ if (output->mode.kind & name_string)
+ fatal ("cannot find mode %s\n", output->mode.string);
+ if (output->mode.kind & name_xid)
+ fatal ("cannot find mode 0x%x\n", output->mode.xid);
+ }
+ if (!output_can_use_mode (output, output->mode_info))
+ fatal ("output %s cannot use mode %s\n", output->output.string,
+ output->mode_info->name);
+ }
+
+ /* set position */
+ if (!(output->changes & changes_position))
+ {
+ if (output->crtc_info)
+ {
+ output->x = output->crtc_info->crtc_info->x;
+ output->y = output->crtc_info->crtc_info->y;
+ }
+ else
+ {
+ output->x = 0;
+ output->y = 0;
+ }
+ }
+
+ /* set rotation */
+ if (!(output->changes & changes_rotation))
+ {
+ output->rotation &= ~0xf;
+ if (output->crtc_info)
+ output->rotation |= (output->crtc_info->crtc_info->rotation & 0xf);
+ else
+ output->rotation = RR_Rotate_0;
+ }
+ if (!(output->changes & changes_reflection))
+ {
+ output->rotation &= ~(RR_Reflect_X|RR_Reflect_Y);
+ if (output->crtc_info)
+ output->rotation |= (output->crtc_info->crtc_info->rotation &
+ (RR_Reflect_X|RR_Reflect_Y));
+ }
+ if (!output_can_use_rotation (output, output->rotation))
+ fatal ("output %s cannot use rotation \"%s\" reflection \"%s\"\n",
+ output->output.string,
+ rotation_name (output->rotation),
+ reflection_name (output->rotation));
+
+ /* set gamma */
+ if (!(output->changes & changes_gamma))
+ set_gamma_info(output);
+
+ /* set transformation */
+ if (!(output->changes & changes_transform))
+ {
+ if (output->crtc_info)
+ copy_transform (&output->transform, &output->crtc_info->current_transform);
+ else
+ init_transform (&output->transform);
+ }
+
+ /* set primary */
+ if (!(output->changes & changes_primary))
+ output->primary = output_is_primary(output);
+}
+
+static void
+get_screen (Bool current)
+{
+ if (!has_1_2)
+ fatal ("Server RandR version before 1.2\n");
+
+ XRRGetScreenSizeRange (dpy, root, &minWidth, &minHeight,
+ &maxWidth, &maxHeight);
+
+ if (current)
+ res = XRRGetScreenResourcesCurrent (dpy, root);
+ else
+ res = XRRGetScreenResources (dpy, root);
+ if (!res) fatal ("could not get screen resources");
+}
+
+static void
+get_crtcs (void)
+{
+ int c;
+
+ num_crtcs = res->ncrtc;
+ crtcs = calloc (num_crtcs, sizeof (crtc_t));
+ if (!crtcs) fatal ("out of memory\n");
+
+ for (c = 0; c < res->ncrtc; c++)
+ {
+ XRRCrtcInfo *crtc_info = XRRGetCrtcInfo (dpy, res, res->crtcs[c]);
+ XRRCrtcTransformAttributes *attr;
+ XRRPanning *panning_info = NULL;
+
+ if (has_1_3) {
+ XRRPanning zero;
+ memset(&zero, 0, sizeof(zero));
+ panning_info = XRRGetPanning (dpy, res, res->crtcs[c]);
+ zero.timestamp = panning_info->timestamp;
+ if (!memcmp(panning_info, &zero, sizeof(zero))) {
+ Xfree(panning_info);
+ panning_info = NULL;
+ }
+ }
+
+ set_name_xid (&crtcs[c].crtc, res->crtcs[c]);
+ set_name_index (&crtcs[c].crtc, c);
+ if (!crtc_info) fatal ("could not get crtc 0x%x information\n", res->crtcs[c]);
+ crtcs[c].crtc_info = crtc_info;
+ crtcs[c].panning_info = panning_info;
+ if (crtc_info->mode == None)
+ {
+ crtcs[c].mode_info = NULL;
+ crtcs[c].x = 0;
+ crtcs[c].y = 0;
+ crtcs[c].rotation = RR_Rotate_0;
+ }
+ if (XRRGetCrtcTransform (dpy, res->crtcs[c], &attr) && attr) {
+ set_transform (&crtcs[c].current_transform,
+ &attr->currentTransform,
+ attr->currentFilter,
+ attr->currentParams,
+ attr->currentNparams);
+ XFree (attr);
+ }
+ else
+ {
+ init_transform (&crtcs[c].current_transform);
+ }
+ copy_transform (&crtcs[c].pending_transform, &crtcs[c].current_transform);
+ }
+}
+
+static void
+crtc_add_output (crtc_t *crtc, output_t *output)
+{
+ if (crtc->outputs)
+ crtc->outputs = realloc (crtc->outputs, (crtc->noutput + 1) * sizeof (output_t *));
+ else
+ {
+ crtc->outputs = malloc (sizeof (output_t *));
+ crtc->x = output->x;
+ crtc->y = output->y;
+ crtc->rotation = output->rotation;
+ crtc->mode_info = output->mode_info;
+ copy_transform (&crtc->pending_transform, &output->transform);
+ }
+ if (!crtc->outputs) fatal ("out of memory\n");
+ crtc->outputs[crtc->noutput++] = output;
+}
+
+static void
+set_crtcs (void)
+{
+ output_t *output;
+
+ for (output = outputs; output; output = output->next)
+ {
+ if (!output->mode_info) continue;
+ crtc_add_output (output->crtc_info, output);
+ }
+}
+
+static void
+set_panning (void)
+{
+ output_t *output;
+
+ for (output = outputs; output; output = output->next)
+ {
+ if (! output->crtc_info)
+ continue;
+ if (! (output->changes & changes_panning))
+ continue;
+ if (! output->crtc_info->panning_info)
+ output->crtc_info->panning_info = malloc (sizeof(XRRPanning));
+ memcpy (output->crtc_info->panning_info, &output->panning, sizeof(XRRPanning));
+ output->crtc_info->changing = 1;
+ }
+}
+
+static void
+set_gamma(void)
+{
+ output_t *output;
+
+ for (output = outputs; output; output = output->next) {
+ int i, size;
+ crtc_t *crtc;
+ XRRCrtcGamma *gamma;
+
+ if (!(output->changes & changes_gamma))
+ continue;
+
+ if (!output->crtc_info) {
+ fatal("Need crtc to set gamma on.\n");
+ continue;
+ }
+
+ crtc = output->crtc_info;
+
+ size = XRRGetCrtcGammaSize(dpy, crtc->crtc.xid);
+
+ if (!size) {
+ fatal("Gamma size is 0.\n");
+ continue;
+ }
+
+ gamma = XRRAllocGamma(size);
+ if (!gamma) {
+ fatal("Gamma allocation failed.\n");
+ continue;
+ }
+
+ if(output->gamma.red == 0.0 && output->gamma.green == 0.0 && output->gamma.blue == 0.0)
+ output->gamma.red = output->gamma.green = output->gamma.blue = 1.0;
+
+ for (i = 0; i < size; i++) {
+ if (output->gamma.red == 1.0 && output->brightness == 1.0)
+ gamma->red[i] = (i << 8) + i;
+ else
+ gamma->red[i] = dmin(pow((double)i/(double)(size - 1),
+ output->gamma.red) * output->brightness,
+ 1.0) * 65535.0;
+
+ if (output->gamma.green == 1.0 && output->brightness == 1.0)
+ gamma->green[i] = (i << 8) + i;
+ else
+ gamma->green[i] = dmin(pow((double)i/(double)(size - 1),
+ output->gamma.green) * output->brightness,
+ 1.0) * 65535.0;
+
+ if (output->gamma.blue == 1.0 && output->brightness == 1.0)
+ gamma->blue[i] = (i << 8) + i;
+ else
+ gamma->blue[i] = dmin(pow((double)i/(double)(size - 1),
+ output->gamma.blue) * output->brightness,
+ 1.0) * 65535.0;
+ }
+
+ XRRSetCrtcGamma(dpy, crtc->crtc.xid, gamma);
+
+ free(gamma);
+ }
+}
+
+static void
+set_primary(void)
+{
+ output_t *output;
+
+ if (no_primary) {
+ XRRSetOutputPrimary(dpy, root, None);
+ } else {
+ for (output = outputs; output; output = output->next) {
+ if (!(output->changes & changes_primary))
+ continue;
+ if (output->primary)
+ XRRSetOutputPrimary(dpy, root, output->output.xid);
+ }
+ }
+}
+
+static Status
+crtc_disable (crtc_t *crtc)
+{
+ if (verbose)
+ printf ("crtc %d: disable\n", crtc->crtc.index);
+
+ if (dryrun)
+ return RRSetConfigSuccess;
+ return XRRSetCrtcConfig (dpy, res, crtc->crtc.xid, CurrentTime,
+ 0, 0, None, RR_Rotate_0, NULL, 0);
+}
+
+static void
+crtc_set_transform (crtc_t *crtc, transform_t *transform)
+{
+ int major, minor;
+
+ XRRQueryVersion (dpy, &major, &minor);
+ if (major > 1 || (major == 1 && minor >= 3))
+ XRRSetCrtcTransform (dpy, crtc->crtc.xid,
+ &transform->transform,
+ transform->filter,
+ transform->params,
+ transform->nparams);
+}
+
+static Status
+crtc_revert (crtc_t *crtc)
+{
+ XRRCrtcInfo *crtc_info = crtc->crtc_info;
+
+ if (verbose)
+ printf ("crtc %d: revert\n", crtc->crtc.index);
+
+ if (dryrun)
+ return RRSetConfigSuccess;
+
+ if (!equal_transform (&crtc->current_transform, &crtc->pending_transform))
+ crtc_set_transform (crtc, &crtc->current_transform);
+ return XRRSetCrtcConfig (dpy, res, crtc->crtc.xid, CurrentTime,
+ crtc_info->x, crtc_info->y,
+ crtc_info->mode, crtc_info->rotation,
+ crtc_info->outputs, crtc_info->noutput);
+}
+
+static Status
+crtc_apply (crtc_t *crtc)
+{
+ RROutput *rr_outputs;
+ int o;
+ Status s;
+ RRMode mode = None;
+
+ if (!crtc->changing || !crtc->mode_info)
+ return RRSetConfigSuccess;
+
+ rr_outputs = calloc (crtc->noutput, sizeof (RROutput));
+ if (!rr_outputs)
+ return BadAlloc;
+ for (o = 0; o < crtc->noutput; o++)
+ rr_outputs[o] = crtc->outputs[o]->output.xid;
+ mode = crtc->mode_info->id;
+ if (verbose) {
+ printf ("crtc %d: %12s %6.1f +%d+%d", crtc->crtc.index,
+ crtc->mode_info->name, mode_refresh (crtc->mode_info),
+ crtc->x, crtc->y);
+ for (o = 0; o < crtc->noutput; o++)
+ printf (" \"%s\"", crtc->outputs[o]->output.string);
+ printf ("\n");
+ }
+
+ if (dryrun)
+ s = RRSetConfigSuccess;
+ else
+ {
+ if (!equal_transform (&crtc->current_transform, &crtc->pending_transform))
+ crtc_set_transform (crtc, &crtc->pending_transform);
+ s = XRRSetCrtcConfig (dpy, res, crtc->crtc.xid, CurrentTime,
+ crtc->x, crtc->y, mode, crtc->rotation,
+ rr_outputs, crtc->noutput);
+ if (s == RRSetConfigSuccess && crtc->panning_info) {
+ if (has_1_3)
+ s = XRRSetPanning (dpy, res, crtc->crtc.xid, crtc->panning_info);
+ else
+ fatal ("panning needs RandR 1.3\n");
+ }
+ }
+ free (rr_outputs);
+ return s;
+}
+
+static void
+screen_revert (void)
+{
+ if (verbose)
+ printf ("screen %d: revert\n", screen);
+
+ if (dryrun)
+ return;
+ XRRSetScreenSize (dpy, root,
+ DisplayWidth (dpy, screen),
+ DisplayHeight (dpy, screen),
+ DisplayWidthMM (dpy, screen),
+ DisplayHeightMM (dpy, screen));
+}
+
+static void
+screen_apply (void)
+{
+ if (fb_width == DisplayWidth (dpy, screen) &&
+ fb_height == DisplayHeight (dpy, screen) &&
+ fb_width_mm == DisplayWidthMM (dpy, screen) &&
+ fb_height_mm == DisplayHeightMM (dpy, screen))
+ {
+ return;
+ }
+ if (verbose)
+ printf ("screen %d: %dx%d %dx%d mm %6.2fdpi\n", screen,
+ fb_width, fb_height, fb_width_mm, fb_height_mm, dpi);
+ if (dryrun)
+ return;
+ XRRSetScreenSize (dpy, root, fb_width, fb_height,
+ fb_width_mm, fb_height_mm);
+}
+
+static void
+revert (void)
+{
+ int c;
+
+ /* first disable all crtcs */
+ for (c = 0; c < res->ncrtc; c++)
+ crtc_disable (&crtcs[c]);
+ /* next reset screen size */
+ screen_revert ();
+ /* now restore all crtcs */
+ for (c = 0; c < res->ncrtc; c++)
+ crtc_revert (&crtcs[c]);
+}
+
+/*
+ * uh-oh, something bad happened in the middle of changing
+ * the configuration. Revert to the previous configuration
+ * and bail
+ */
+static void _X_NORETURN
+panic (Status s, crtc_t *crtc)
+{
+ int c = crtc->crtc.index;
+ char *message;
+
+ switch (s) {
+ case RRSetConfigSuccess: message = "succeeded"; break;
+ case BadAlloc: message = "out of memory"; break;
+ case RRSetConfigFailed: message = "failed"; break;
+ case RRSetConfigInvalidConfigTime: message = "invalid config time"; break;
+ case RRSetConfigInvalidTime: message = "invalid time"; break;
+ default: message = "unknown failure"; break;
+ }
+
+ fprintf (stderr, "%s: Configure crtc %d %s\n", program_name, c, message);
+ revert ();
+ exit (1);
+}
+
+static void
+apply (void)
+{
+ Status s;
+ int c;
+
+ /*
+ * Hold the server grabbed while messing with
+ * the screen so that apps which notice the resize
+ * event and ask for xinerama information from the server
+ * receive up-to-date information
+ */
+ if (grab_server)
+ XGrabServer (dpy);
+
+ /*
+ * Turn off any crtcs which are to be disabled or which are
+ * larger than the target size
+ */
+ for (c = 0; c < res->ncrtc; c++)
+ {
+ crtc_t *crtc = &crtcs[c];
+ XRRCrtcInfo *crtc_info = crtc->crtc_info;
+
+ /* if this crtc is already disabled, skip it */
+ if (crtc_info->mode == None)
+ continue;
+
+ /*
+ * If this crtc is to be left enabled, make
+ * sure the old size fits then new screen
+ */
+ if (crtc->mode_info)
+ {
+ XRRModeInfo *old_mode = find_mode_by_xid (crtc_info->mode);
+ int x, y, w, h;
+ box_t bounds;
+
+ if (!old_mode)
+ panic (RRSetConfigFailed, crtc);
+
+ /* old position and size information */
+ mode_geometry (old_mode, crtc_info->rotation,
+ &crtc->current_transform.transform,
+ &bounds);
+
+ x = crtc_info->x + bounds.x1;
+ y = crtc_info->y + bounds.y1;
+ w = bounds.x2 - bounds.x1;
+ h = bounds.y2 - bounds.y1;
+
+ /* if it fits, skip it */
+ if (x + w <= fb_width && y + h <= fb_height)
+ continue;
+ crtc->changing = True;
+ }
+ s = crtc_disable (crtc);
+ if (s != RRSetConfigSuccess)
+ panic (s, crtc);
+ }
+
+ /*
+ * Set the screen size
+ */
+ screen_apply ();
+
+ /*
+ * Set crtcs
+ */
+
+ for (c = 0; c < res->ncrtc; c++)
+ {
+ crtc_t *crtc = &crtcs[c];
+
+ s = crtc_apply (crtc);
+ if (s != RRSetConfigSuccess)
+ panic (s, crtc);
+ }
+
+ set_primary ();
+
+ /*
+ * Release the server grab and let all clients
+ * respond to the updated state
+ */
+ if (grab_server)
+ XUngrabServer (dpy);
+}
+
+/*
+ * Use current output state to complete the output list
+ */
+static void
+get_outputs (void)
+{
+ int o;
+ output_t *q;
+
+ for (o = 0; o < res->noutput; o++)
+ {
+ XRROutputInfo *output_info = XRRGetOutputInfo (dpy, res, res->outputs[o]);
+ output_t *output;
+ name_t output_name;
+ if (!output_info) fatal ("could not get output 0x%x information\n", res->outputs[o]);
+ set_name_xid (&output_name, res->outputs[o]);
+ set_name_index (&output_name, o);
+ set_name_string (&output_name, output_info->name);
+ output = find_output (&output_name);
+ if (!output)
+ {
+ output = add_output ();
+ set_name_all (&output->output, &output_name);
+ /*
+ * When global --automatic mode is set, turn on connected but off
+ * outputs, turn off disconnected but on outputs
+ */
+ if (automatic)
+ {
+ switch (output_info->connection) {
+ case RR_Connected:
+ if (!output_info->crtc) {
+ output->changes |= changes_automatic;
+ output->automatic = True;
+ }
+ break;
+ case RR_Disconnected:
+ if (output_info->crtc)
+ {
+ output->changes |= changes_automatic;
+ output->automatic = True;
+ }
+ break;
+ }
+ }
+ }
+ output->found = True;
+
+ /*
+ * Automatic mode -- track connection state and enable/disable outputs
+ * as necessary
+ */
+ if (output->automatic)
+ {
+ switch (output_info->connection) {
+ case RR_Connected:
+ case RR_UnknownConnection:
+ if ((!(output->changes & changes_mode)))
+ {
+ set_name_preferred (&output->mode);
+ output->changes |= changes_mode;
+ }
+ break;
+ case RR_Disconnected:
+ if ((!(output->changes & changes_mode)))
+ {
+ set_name_xid (&output->mode, None);
+ set_name_xid (&output->crtc, None);
+ output->changes |= changes_mode;
+ output->changes |= changes_crtc;
+ }
+ break;
+ }
+ }
+
+ set_output_info (output, res->outputs[o], output_info);
+ }
+ for (q = outputs; q; q = q->next)
+ {
+ if (!q->found)
+ {
+ fprintf(stderr, "warning: output %s not found; ignoring\n",
+ q->output.string);
+ }
+ }
+}
+
+static void
+mark_changing_crtcs (void)
+{
+ int c;
+
+ for (c = 0; c < num_crtcs; c++)
+ {
+ crtc_t *crtc = &crtcs[c];
+ int o;
+ output_t *output;
+
+ /* walk old output list (to catch disables) */
+ for (o = 0; o < crtc->crtc_info->noutput; o++)
+ {
+ output = find_output_by_xid (crtc->crtc_info->outputs[o]);
+ if (!output) fatal ("cannot find output 0x%x\n",
+ crtc->crtc_info->outputs[o]);
+ if (output->changes)
+ crtc->changing = True;
+ }
+ /* walk new output list */
+ for (o = 0; o < crtc->noutput; o++)
+ {
+ output = crtc->outputs[o];
+ if (output->changes)
+ crtc->changing = True;
+ }
+ }
+}
+
+/*
+ * Test whether 'crtc' can be used for 'output'
+ */
+static Bool
+check_crtc_for_output (crtc_t *crtc, output_t *output)
+{
+ int c;
+ int l;
+ output_t *other;
+
+ for (c = 0; c < output->output_info->ncrtc; c++)
+ if (output->output_info->crtcs[c] == crtc->crtc.xid)
+ break;
+ if (c == output->output_info->ncrtc)
+ return False;
+ for (other = outputs; other; other = other->next)
+ {
+ if (other == output)
+ continue;
+
+ if (other->mode_info == NULL)
+ continue;
+
+ if (other->crtc_info != crtc)
+ continue;
+
+ /* see if the output connected to the crtc can clone to this output */
+ for (l = 0; l < output->output_info->nclone; l++)
+ if (output->output_info->clones[l] == other->output.xid)
+ break;
+ /* not on the list, can't clone */
+ if (l == output->output_info->nclone)
+ return False;
+ }
+
+ if (crtc->noutput)
+ {
+ /* make sure the state matches */
+ if (crtc->mode_info != output->mode_info)
+ return False;
+ if (crtc->x != output->x)
+ return False;
+ if (crtc->y != output->y)
+ return False;
+ if (crtc->rotation != output->rotation)
+ return False;
+ if (!equal_transform (&crtc->current_transform, &output->transform))
+ return False;
+ }
+ else if (crtc->crtc_info->noutput)
+ {
+ /* make sure the state matches the already used state */
+ XRRModeInfo *mode = find_mode_by_xid (crtc->crtc_info->mode);
+
+ if (mode != output->mode_info)
+ return False;
+ if (crtc->crtc_info->x != output->x)
+ return False;
+ if (crtc->crtc_info->y != output->y)
+ return False;
+ if (crtc->crtc_info->rotation != output->rotation)
+ return False;
+ }
+ return True;
+}
+
+static crtc_t *
+find_crtc_for_output (output_t *output)
+{
+ int c;
+
+ for (c = 0; c < output->output_info->ncrtc; c++)
+ {
+ crtc_t *crtc;
+
+ crtc = find_crtc_by_xid (output->output_info->crtcs[c]);
+ if (!crtc) fatal ("cannot find crtc 0x%x\n", output->output_info->crtcs[c]);
+
+ if (check_crtc_for_output (crtc, output))
+ return crtc;
+ }
+ return NULL;
+}
+
+static void
+set_positions (void)
+{
+ output_t *output;
+ Bool keep_going;
+ Bool any_set;
+ int min_x, min_y;
+
+ for (;;)
+ {
+ any_set = False;
+ keep_going = False;
+ for (output = outputs; output; output = output->next)
+ {
+ output_t *relation;
+ name_t relation_name;
+
+ if (!(output->changes & changes_relation)) continue;
+
+ if (output->mode_info == NULL) continue;
+
+ init_name (&relation_name);
+ set_name_string (&relation_name, output->relative_to);
+ relation = find_output (&relation_name);
+ if (!relation) fatal ("cannot find output \"%s\"\n", output->relative_to);
+
+ if (relation->mode_info == NULL)
+ {
+ output->x = 0;
+ output->y = 0;
+ output->changes |= changes_position;
+ any_set = True;
+ continue;
+ }
+ /*
+ * Make sure the dependent object has been set in place
+ */
+ if ((relation->changes & changes_relation) &&
+ !(relation->changes & changes_position))
+ {
+ keep_going = True;
+ continue;
+ }
+
+ switch (output->relation) {
+ case relation_left_of:
+ output->y = relation->y;
+ output->x = relation->x - mode_width (output->mode_info, output->rotation);
+ break;
+ case relation_right_of:
+ output->y = relation->y;
+ output->x = relation->x + mode_width (relation->mode_info, relation->rotation);
+ break;
+ case relation_above:
+ output->x = relation->x;
+ output->y = relation->y - mode_height (output->mode_info, output->rotation);
+ break;
+ case relation_below:
+ output->x = relation->x;
+ output->y = relation->y + mode_height (relation->mode_info, relation->rotation);
+ break;
+ case relation_same_as:
+ output->x = relation->x;
+ output->y = relation->y;
+ }
+ output->changes |= changes_position;
+ any_set = True;
+ }
+ if (!keep_going)
+ break;
+ if (!any_set)
+ fatal ("loop in relative position specifications\n");
+ }
+
+ /*
+ * Now normalize positions so the upper left corner of all outputs is at 0,0
+ */
+ min_x = 32768;
+ min_y = 32768;
+ for (output = outputs; output; output = output->next)
+ {
+ if (output->mode_info == NULL) continue;
+
+ if (output->x < min_x) min_x = output->x;
+ if (output->y < min_y) min_y = output->y;
+ }
+ if (min_x || min_y)
+ {
+ /* move all outputs */
+ for (output = outputs; output; output = output->next)
+ {
+ if (output->mode_info == NULL) continue;
+
+ output->x -= min_x;
+ output->y -= min_y;
+ output->changes |= changes_position;
+ }
+ }
+}
+
+static void
+set_screen_size (void)
+{
+ output_t *output;
+ Bool fb_specified = fb_width != 0 && fb_height != 0;
+
+ for (output = outputs; output; output = output->next)
+ {
+ XRRModeInfo *mode_info = output->mode_info;
+ int x, y, w, h;
+ box_t bounds;
+
+ if (!mode_info) continue;
+
+ mode_geometry (mode_info, output->rotation,
+ &output->transform.transform,
+ &bounds);
+ x = output->x + bounds.x1;
+ y = output->y + bounds.y1;
+ w = bounds.x2 - bounds.x1;
+ h = bounds.y2 - bounds.y1;
+ /* make sure output fits in specified size */
+ if (fb_specified)
+ {
+ if (x + w > fb_width || y + h > fb_height)
+ warning ("specified screen %dx%d not large enough for output %s (%dx%d+%d+%d)\n",
+ fb_width, fb_height, output->output.string, w, h, x, y);
+ }
+ /* fit fb to output */
+ else
+ {
+ XRRPanning *pan;
+ if (x + w > fb_width)
+ fb_width = x + w;
+ if (y + h > fb_height)
+ fb_height = y + h;
+ if (output->changes & changes_panning)
+ pan = &output->panning;
+ else
+ pan = output->crtc_info ? output->crtc_info->panning_info : NULL;
+ if (pan && pan->left + pan->width > fb_width)
+ fb_width = pan->left + pan->width;
+ if (pan && pan->top + pan->height > fb_height)
+ fb_height = pan->top + pan->height;
+ }
+ }
+
+ if (fb_width > maxWidth || fb_height > maxHeight)
+ fatal ("screen cannot be larger than %dx%d (desired size %dx%d)\n",
+ maxWidth, maxHeight, fb_width, fb_height);
+ if (fb_specified)
+ {
+ if (fb_width < minWidth || fb_height < minHeight)
+ fatal ("screen must be at least %dx%d\n", minWidth, minHeight);
+ }
+ else
+ {
+ if (fb_width < minWidth) fb_width = minWidth;
+ if (fb_height < minHeight) fb_height = minHeight;
+ }
+}
+
+
+static void
+disable_outputs (output_t *outputs)
+{
+ while (outputs)
+ {
+ outputs->crtc_info = NULL;
+ outputs = outputs->next;
+ }
+}
+
+/*
+ * find the best mapping from output to crtc available
+ */
+static int
+pick_crtcs_score (output_t *outputs)
+{
+ output_t *output;
+ int best_score;
+ int my_score;
+ int score;
+ crtc_t *best_crtc;
+ int c;
+
+ if (!outputs)
+ return 0;
+
+ output = outputs;
+ outputs = outputs->next;
+ /*
+ * Score with this output disabled
+ */
+ output->crtc_info = NULL;
+ best_score = pick_crtcs_score (outputs);
+ if (output->mode_info == NULL)
+ return best_score;
+
+ best_crtc = NULL;
+ /*
+ * Now score with this output any valid crtc
+ */
+ for (c = 0; c < output->output_info->ncrtc; c++)
+ {
+ crtc_t *crtc;
+
+ crtc = find_crtc_by_xid (output->output_info->crtcs[c]);
+ if (!crtc)
+ fatal ("cannot find crtc 0x%x\n", output->output_info->crtcs[c]);
+
+ /* reset crtc allocation for following outputs */
+ disable_outputs (outputs);
+ if (!check_crtc_for_output (crtc, output))
+ continue;
+
+ my_score = 1000;
+ /* slight preference for existing connections */
+ if (crtc == output->current_crtc_info)
+ my_score++;
+
+ output->crtc_info = crtc;
+ score = my_score + pick_crtcs_score (outputs);
+ if (score > best_score)
+ {
+ best_crtc = crtc;
+ best_score = score;
+ }
+ }
+ if (output->crtc_info != best_crtc)
+ output->crtc_info = best_crtc;
+ /*
+ * Reset other outputs based on this one using the best crtc
+ */
+ (void) pick_crtcs_score (outputs);
+
+ return best_score;
+}
+
+/*
+ * Pick crtcs for any changing outputs that don't have one
+ */
+static void
+pick_crtcs (void)
+{
+ output_t *output;
+
+ /*
+ * First try to match up newly enabled outputs with spare crtcs
+ */
+ for (output = outputs; output; output = output->next)
+ {
+ if (output->changes && output->mode_info)
+ {
+ if (output->crtc_info) {
+ if (output->crtc_info->crtc_info->noutput > 0 &&
+ (output->crtc_info->crtc_info->noutput > 1 ||
+ output != find_output_by_xid (output->crtc_info->crtc_info->outputs[0])))
+ break;
+ } else {
+ output->crtc_info = find_crtc_for_output (output);
+ if (!output->crtc_info)
+ break;
+ }
+ }
+ }
+ /*
+ * Everyone is happy
+ */
+ if (!output)
+ return;
+ /*
+ * When the simple way fails, see if there is a way
+ * to swap crtcs around and make things work
+ */
+ for (output = outputs; output; output = output->next)
+ output->current_crtc_info = output->crtc_info;
+ pick_crtcs_score (outputs);
+ for (output = outputs; output; output = output->next)
+ {
+ if (output->mode_info && !output->crtc_info)
+ fatal ("cannot find crtc for output %s\n", output->output.string);
+ if (!output->changes && output->crtc_info != output->current_crtc_info)
+ output->changes |= changes_crtc;
+ }
+}
+
+static int
+check_strtol(char *s)
+{
+ char *endptr;
+ int result = strtol(s, &endptr, 10);
+ if (s == endptr)
+ usage();
+ return result;
+}
+
+static double
+check_strtod(char *s)
+{
+ char *endptr;
+ double result = strtod(s, &endptr);
+ if (s == endptr)
+ usage();
+ return result;
+}
+
+int
+main (int argc, char **argv)
+{
+ XRRScreenSize *sizes;
+ XRRScreenConfiguration *sc;
+ int nsize;
+ int nrate;
+ short *rates;
+ Status status = RRSetConfigFailed;
+ int rot = -1;
+ int query = False;
+ int action_requested = False;
+ Rotation rotation, current_rotation, rotations;
+ XEvent event;
+ XRRScreenChangeNotifyEvent *sce;
+ char *display_name = NULL;
+ int i, j;
+ SizeID current_size;
+ short current_rate;
+ double rate = -1;
+ int size = -1;
+ int dirind = 0;
+ Bool setit = False;
+ Bool version = False;
+ int event_base, error_base;
+ int reflection = 0;
+ int width = 0, height = 0;
+ Bool have_pixel_size = False;
+ int ret = 0;
+ output_t *output = NULL;
+ Bool setit_1_2 = False;
+ Bool query_1_2 = False;
+ Bool modeit = False;
+ Bool propit = False;
+ Bool query_1 = False;
+ int major, minor;
+ Bool current = False;
+
+ program_name = argv[0];
+ for (i = 1; i < argc; i++) {
+ if (!strcmp ("-display", argv[i]) || !strcmp ("-d", argv[i])) {
+ if (++i>=argc) usage ();
+ display_name = argv[i];
+ continue;
+ }
+ if (!strcmp("-help", argv[i])) {
+ usage();
+ action_requested = True;
+ continue;
+ }
+ if (!strcmp ("--verbose", argv[i])) {
+ verbose = True;
+ continue;
+ }
+ if (!strcmp ("--dryrun", argv[i])) {
+ dryrun = True;
+ verbose = True;
+ continue;
+ }
+ if (!strcmp ("--nograb", argv[i])) {
+ grab_server = False;
+ continue;
+ }
+ if (!strcmp("--current", argv[i])) {
+ current = True;
+ continue;
+ }
+
+ if (!strcmp ("-s", argv[i]) || !strcmp ("--size", argv[i])) {
+ if (++i>=argc) usage ();
+ if (sscanf (argv[i], "%dx%d", &width, &height) == 2) {
+ have_pixel_size = True;
+ } else {
+ size = check_strtol(argv[i]);
+ if (size < 0) usage();
+ }
+ setit = True;
+ action_requested = True;
+ continue;
+ }
+
+ if (!strcmp ("-r", argv[i]) ||
+ !strcmp ("--rate", argv[i]) ||
+ !strcmp ("--refresh", argv[i]))
+ {
+ if (++i>=argc) usage ();
+ rate = check_strtod(argv[i]);
+ setit = True;
+ if (output)
+ {
+ output->refresh = rate;
+ output->changes |= changes_refresh;
+ setit_1_2 = True;
+ }
+ action_requested = True;
+ continue;
+ }
+
+ if (!strcmp ("-v", argv[i]) || !strcmp ("--version", argv[i])) {
+ version = True;
+ action_requested = True;
+ continue;
+ }
+
+ if (!strcmp ("-x", argv[i])) {
+ reflection |= RR_Reflect_X;
+ setit = True;
+ action_requested = True;
+ continue;
+ }
+ if (!strcmp ("-y", argv[i])) {
+ reflection |= RR_Reflect_Y;
+ setit = True;
+ action_requested = True;
+ continue;
+ }
+ if (!strcmp ("--screen", argv[i])) {
+ if (++i>=argc) usage ();
+ screen = check_strtol(argv[i]);
+ if (screen < 0) usage();
+ continue;
+ }
+ if (!strcmp ("-q", argv[i]) || !strcmp ("--query", argv[i])) {
+ query = True;
+ continue;
+ }
+ if (!strcmp ("-o", argv[i]) || !strcmp ("--orientation", argv[i])) {
+ char *endptr;
+ if (++i>=argc) usage ();
+ dirind = strtol(argv[i], &endptr, 10);
+ if (argv[i] == endptr) {
+ for (dirind = 0; dirind < 4; dirind++) {
+ if (strcmp (direction[dirind], argv[i]) == 0) break;
+ }
+ if ((dirind < 0) || (dirind > 3)) usage();
+ }
+ rot = dirind;
+ setit = True;
+ action_requested = True;
+ continue;
+ }
+ if (!strcmp ("--prop", argv[i]) ||
+ !strcmp ("--props", argv[i]) ||
+ !strcmp ("--madprops", argv[i]) ||
+ !strcmp ("--properties", argv[i]))
+ {
+ query_1_2 = True;
+ properties = True;
+ action_requested = True;
+ continue;
+ }
+ if (!strcmp ("--output", argv[i])) {
+ if (++i >= argc) usage();
+
+ output = find_output_by_name (argv[i]);
+ if (!output) {
+ output = add_output ();
+ set_name (&output->output, argv[i], name_string|name_xid);
+ }
+
+ setit_1_2 = True;
+ action_requested = True;
+ continue;
+ }
+ if (!strcmp ("--crtc", argv[i])) {
+ if (++i >= argc) usage();
+ if (!output) usage();
+ set_name (&output->crtc, argv[i], name_xid|name_index);
+ output->changes |= changes_crtc;
+ continue;
+ }
+ if (!strcmp ("--mode", argv[i])) {
+ if (++i >= argc) usage();
+ if (!output) usage();
+ set_name (&output->mode, argv[i], name_string|name_xid);
+ output->changes |= changes_mode;
+ continue;
+ }
+ if (!strcmp ("--preferred", argv[i])) {
+ if (!output) usage();
+ set_name_preferred (&output->mode);
+ output->changes |= changes_mode;
+ continue;
+ }
+ if (!strcmp ("--pos", argv[i])) {
+ if (++i>=argc) usage ();
+ if (!output) usage();
+ if (sscanf (argv[i], "%dx%d",
+ &output->x, &output->y) != 2)
+ usage ();
+ output->changes |= changes_position;
+ continue;
+ }
+ if (!strcmp ("--rotation", argv[i]) || !strcmp ("--rotate", argv[i])) {
+ if (++i>=argc) usage ();
+ if (!output) usage();
+ for (dirind = 0; dirind < 4; dirind++) {
+ if (strcmp (direction[dirind], argv[i]) == 0) break;
+ }
+ if (dirind == 4)
+ usage ();
+ output->rotation &= ~0xf;
+ output->rotation |= 1 << dirind;
+ output->changes |= changes_rotation;
+ continue;
+ }
+ if (!strcmp ("--reflect", argv[i]) || !strcmp ("--reflection", argv[i])) {
+ if (++i>=argc) usage ();
+ if (!output) usage();
+ for (dirind = 0; dirind < 4; dirind++) {
+ if (strcmp (reflections[dirind], argv[i]) == 0) break;
+ }
+ if (dirind == 4)
+ usage ();
+ output->rotation &= ~(RR_Reflect_X|RR_Reflect_Y);
+ output->rotation |= dirind * RR_Reflect_X;
+ output->changes |= changes_reflection;
+ continue;
+ }
+ if (!strcmp ("--left-of", argv[i])) {
+ if (++i>=argc) usage ();
+ if (!output) usage();
+ output->relation = relation_left_of;
+ output->relative_to = argv[i];
+ output->changes |= changes_relation;
+ continue;
+ }
+ if (!strcmp ("--right-of", argv[i])) {
+ if (++i>=argc) usage ();
+ if (!output) usage();
+ output->relation = relation_right_of;
+ output->relative_to = argv[i];
+ output->changes |= changes_relation;
+ continue;
+ }
+ if (!strcmp ("--above", argv[i])) {
+ if (++i>=argc) usage ();
+ if (!output) usage();
+ output->relation = relation_above;
+ output->relative_to = argv[i];
+ output->changes |= changes_relation;
+ continue;
+ }
+ if (!strcmp ("--below", argv[i])) {
+ if (++i>=argc) usage ();
+ if (!output) usage();
+ output->relation = relation_below;
+ output->relative_to = argv[i];
+ output->changes |= changes_relation;
+ continue;
+ }
+ if (!strcmp ("--same-as", argv[i])) {
+ if (++i>=argc) usage ();
+ if (!output) usage();
+ output->relation = relation_same_as;
+ output->relative_to = argv[i];
+ output->changes |= changes_relation;
+ continue;
+ }
+ if (!strcmp ("--panning", argv[i])) {
+ XRRPanning *pan;
+ if (++i>=argc) usage ();
+ if (!output) usage();
+ pan = &output->panning;
+ switch (sscanf (argv[i], "%dx%d+%d+%d/%dx%d+%d+%d/%d/%d/%d/%d",
+ &pan->width, &pan->height, &pan->left, &pan->top,
+ &pan->track_width, &pan->track_height,
+ &pan->track_left, &pan->track_top,
+ &pan->border_left, &pan->border_top,
+ &pan->border_right, &pan->border_bottom)) {
+ case 2:
+ pan->left = pan->top = 0;
+ /* fall through */
+ case 4:
+ pan->track_left = pan->track_top =
+ pan->track_width = pan->track_height = 0;
+ /* fall through */
+ case 8:
+ pan->border_left = pan->border_top =
+ pan->border_right = pan->border_bottom = 0;
+ /* fall through */
+ case 12:
+ break;
+ default:
+ usage ();
+ }
+ output->changes |= changes_panning;
+ continue;
+ }
+ if (!strcmp ("--gamma", argv[i])) {
+ if (!output) usage();
+ if (++i>=argc) usage ();
+ if (sscanf(argv[i], "%f:%f:%f", &output->gamma.red,
+ &output->gamma.green, &output->gamma.blue) != 3)
+ usage ();
+ output->changes |= changes_gamma;
+ setit_1_2 = True;
+ continue;
+ }
+ if (!strcmp ("--brightness", argv[i])) {
+ if (!output) usage();
+ if (++i>=argc) usage();
+ if (sscanf(argv[i], "%f", &output->brightness) != 1)
+ usage ();
+ output->changes |= changes_gamma;
+ setit_1_2 = True;
+ continue;
+ }
+ if (!strcmp ("--primary", argv[i])) {
+ if (!output) usage();
+ output->changes |= changes_primary;
+ output->primary = True;
+ setit_1_2 = True;
+ continue;
+ }
+ if (!strcmp ("--noprimary", argv[i])) {
+ no_primary = True;
+ setit_1_2 = True;
+ continue;
+ }
+ if (!strcmp ("--set", argv[i])) {
+ output_prop_t *prop;
+ if (!output) usage();
+ prop = malloc (sizeof (output_prop_t));
+ prop->next = output->props;
+ output->props = prop;
+ if (++i>=argc) usage ();
+ prop->name = argv[i];
+ if (++i>=argc) usage ();
+ prop->value = argv[i];
+ propit = True;
+ output->changes |= changes_property;
+ setit_1_2 = True;
+ continue;
+ }
+ if (!strcmp ("--scale", argv[i]))
+ {
+ double sx, sy;
+ if (!output) usage();
+ if (++i>=argc) usage();
+ if (sscanf (argv[i], "%lfx%lf", &sx, &sy) != 2)
+ usage ();
+ init_transform (&output->transform);
+ output->transform.transform.matrix[0][0] = XDoubleToFixed (sx);
+ output->transform.transform.matrix[1][1] = XDoubleToFixed (sy);
+ output->transform.transform.matrix[2][2] = XDoubleToFixed (1.0);
+ if (sx != 1 || sy != 1)
+ output->transform.filter = "bilinear";
+ else
+ output->transform.filter = "nearest";
+ output->transform.nparams = 0;
+ output->transform.params = NULL;
+ output->changes |= changes_transform;
+ continue;
+ }
+ if (!strcmp ("--transform", argv[i])) {
+ double transform[3][3];
+ int k, l;
+ if (!output) usage();
+ if (++i>=argc) usage ();
+ init_transform (&output->transform);
+ if (strcmp (argv[i], "none") != 0)
+ {
+ if (sscanf(argv[i], "%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf",
+ &transform[0][0],&transform[0][1],&transform[0][2],
+ &transform[1][0],&transform[1][1],&transform[1][2],
+ &transform[2][0],&transform[2][1],&transform[2][2])
+ != 9)
+ usage ();
+ init_transform (&output->transform);
+ for (k = 0; k < 3; k++)
+ for (l = 0; l < 3; l++) {
+ output->transform.transform.matrix[k][l] = XDoubleToFixed (transform[k][l]);
+ }
+ output->transform.filter = "bilinear";
+ output->transform.nparams = 0;
+ output->transform.params = NULL;
+ }
+ output->changes |= changes_transform;
+ continue;
+ }
+ if (!strcmp ("--off", argv[i])) {
+ if (!output) usage();
+ set_name_xid (&output->mode, None);
+ set_name_xid (&output->crtc, None);
+ output->changes |= changes_mode;
+ continue;
+ }
+ if (!strcmp ("--fb", argv[i])) {
+ if (++i>=argc) usage ();
+ if (sscanf (argv[i], "%dx%d",
+ &fb_width, &fb_height) != 2)
+ usage ();
+ setit_1_2 = True;
+ action_requested = True;
+ continue;
+ }
+ if (!strcmp ("--fbmm", argv[i])) {
+ if (++i>=argc) usage ();
+ if (sscanf (argv[i], "%dx%d",
+ &fb_width_mm, &fb_height_mm) != 2)
+ usage ();
+ setit_1_2 = True;
+ action_requested = True;
+ continue;
+ }
+ if (!strcmp ("--dpi", argv[i])) {
+ char *strtod_error;
+ if (++i>=argc) usage ();
+ dpi = strtod(argv[i], &strtod_error);
+ if (argv[i] == strtod_error)
+ {
+ dpi = 0.0;
+ dpi_output = argv[i];
+ }
+ setit_1_2 = True;
+ action_requested = True;
+ continue;
+ }
+ if (!strcmp ("--auto", argv[i])) {
+ if (output)
+ {
+ output->automatic = True;
+ output->changes |= changes_automatic;
+ }
+ else
+ automatic = True;
+ setit_1_2 = True;
+ action_requested = True;
+ continue;
+ }
+ if (!strcmp ("--q12", argv[i]))
+ {
+ query_1_2 = True;
+ continue;
+ }
+ if (!strcmp ("--q1", argv[i]))
+ {
+ query_1 = True;
+ continue;
+ }
+ if (!strcmp ("--newmode", argv[i]))
+ {
+ umode_t *m = malloc (sizeof (umode_t));
+ double clock;
+
+ ++i;
+ if (i + 9 >= argc) usage ();
+ m->mode.name = argv[i];
+ m->mode.nameLength = strlen (argv[i]);
+ i++;
+ clock = check_strtod(argv[i++]);
+ m->mode.dotClock = clock * 1e6;
+
+ m->mode.width = check_strtol(argv[i++]);
+ m->mode.hSyncStart = check_strtol(argv[i++]);
+ m->mode.hSyncEnd = check_strtol(argv[i++]);
+ m->mode.hTotal = check_strtol(argv[i++]);
+ m->mode.height = check_strtol(argv[i++]);
+ m->mode.vSyncStart = check_strtol(argv[i++]);
+ m->mode.vSyncEnd = check_strtol(argv[i++]);
+ m->mode.vTotal = check_strtol(argv[i++]);
+ m->mode.modeFlags = 0;
+ while (i < argc) {
+ int f;
+
+ for (f = 0; mode_flags[f].string; f++)
+ if (!strcasecmp (mode_flags[f].string, argv[i]))
+ break;
+
+ if (!mode_flags[f].string)
+ break;
+ m->mode.modeFlags |= mode_flags[f].flag;
+ i++;
+ }
+ m->next = umodes;
+ m->action = umode_create;
+ umodes = m;
+ modeit = True;
+ action_requested = True;
+ continue;
+ }
+ if (!strcmp ("--rmmode", argv[i]))
+ {
+ umode_t *m = malloc (sizeof (umode_t));
+
+ if (++i>=argc) usage ();
+ set_name (&m->name, argv[i], name_string|name_xid);
+ m->action = umode_destroy;
+ m->next = umodes;
+ umodes = m;
+ modeit = True;
+ action_requested = True;
+ continue;
+ }
+ if (!strcmp ("--addmode", argv[i]))
+ {
+ umode_t *m = malloc (sizeof (umode_t));
+
+ if (++i>=argc) usage ();
+ set_name (&m->output, argv[i], name_string|name_xid);
+ if (++i>=argc) usage();
+ set_name (&m->name, argv[i], name_string|name_xid);
+ m->action = umode_add;
+ m->next = umodes;
+ umodes = m;
+ modeit = True;
+ action_requested = True;
+ continue;
+ }
+ if (!strcmp ("--delmode", argv[i]))
+ {
+ umode_t *m = malloc (sizeof (umode_t));
+
+ if (++i>=argc) usage ();
+ set_name (&m->output, argv[i], name_string|name_xid);
+ if (++i>=argc) usage();
+ set_name (&m->name, argv[i], name_string|name_xid);
+ m->action = umode_delete;
+ m->next = umodes;
+ umodes = m;
+ modeit = True;
+ action_requested = True;
+ continue;
+ }
+ usage();
+ }
+ if (!action_requested)
+ query = True;
+ if (verbose)
+ {
+ query = True;
+ if (setit && !setit_1_2)
+ query_1 = True;
+ }
+ if (version)
+ printf("xrandr program version " VERSION "\n");
+
+ dpy = XOpenDisplay (display_name);
+
+ if (dpy == NULL) {
+ fprintf (stderr, "Can't open display %s\n", XDisplayName(display_name));
+ exit (1);
+ }
+ if (screen < 0)
+ screen = DefaultScreen (dpy);
+ if (screen >= ScreenCount (dpy)) {
+ fprintf (stderr, "Invalid screen number %d (display has %d)\n",
+ screen, ScreenCount (dpy));
+ exit (1);
+ }
+
+ root = RootWindow (dpy, screen);
+
+ if (!XRRQueryExtension (dpy, &event_base, &error_base) ||
+ !XRRQueryVersion (dpy, &major, &minor))
+ {
+ fprintf (stderr, "RandR extension missing\n");
+ exit (1);
+ }
+ if (major > 1 || (major == 1 && minor >= 2))
+ has_1_2 = True;
+ if (major > 1 || (major == 1 && minor >= 3))
+ has_1_3 = True;
+
+ if (has_1_2 && modeit)
+ {
+ umode_t *m;
+
+ get_screen (current);
+ get_crtcs();
+ get_outputs();
+
+ for (m = umodes; m; m = m->next)
+ {
+ XRRModeInfo *e;
+ output_t *o;
+
+ switch (m->action) {
+ case umode_create:
+ XRRCreateMode (dpy, root, &m->mode);
+ break;
+ case umode_destroy:
+ e = find_mode (&m->name, 0);
+ if (!e)
+ fatal ("cannot find mode \"%s\"\n", m->name.string);
+ XRRDestroyMode (dpy, e->id);
+ break;
+ case umode_add:
+ o = find_output (&m->output);
+ if (!o)
+ fatal ("cannot find output \"%s\"\n", m->output.string);
+ e = find_mode (&m->name, 0);
+ if (!e)
+ fatal ("cannot find mode \"%s\"\n", m->name.string);
+ XRRAddOutputMode (dpy, o->output.xid, e->id);
+ break;
+ case umode_delete:
+ o = find_output (&m->output);
+ if (!o)
+ fatal ("cannot find output \"%s\"\n", m->output.string);
+ e = find_mode (&m->name, 0);
+ if (!e)
+ fatal ("cannot find mode \"%s\"\n", m->name.string);
+ XRRDeleteOutputMode (dpy, o->output.xid, e->id);
+ break;
+ }
+ }
+ if (!setit_1_2)
+ {
+ XSync (dpy, False);
+ exit (0);
+ }
+ }
+ if (has_1_2 && propit)
+ {
+
+ get_screen (current);
+ get_crtcs();
+ get_outputs();
+
+ for (output = outputs; output; output = output->next)
+ {
+ output_prop_t *prop;
+
+ for (prop = output->props; prop; prop = prop->next)
+ {
+ Atom name = XInternAtom (dpy, prop->name, False);
+ Atom type;
+ int format = 0;
+ unsigned char *data;
+ int nelements;
+ int int_value;
+ unsigned long ulong_value;
+ unsigned char *prop_data;
+ int actual_format;
+ unsigned long nitems, bytes_after;
+ Atom actual_type;
+ XRRPropertyInfo *propinfo;
+
+ type = AnyPropertyType;
+
+ if (XRRGetOutputProperty (dpy, output->output.xid, name,
+ 0, 100, False, False,
+ AnyPropertyType,
+ &actual_type, &actual_format,
+ &nitems, &bytes_after, &prop_data) == Success &&
+
+ (propinfo = XRRQueryOutputProperty(dpy, output->output.xid,
+ name)))
+ {
+ type = actual_type;
+ format = actual_format;
+ }
+
+ if ((type == XA_INTEGER || type == AnyPropertyType) &&
+ (sscanf (prop->value, "%d", &int_value) == 1 ||
+ sscanf (prop->value, "0x%x", &int_value) == 1))
+ {
+ type = XA_INTEGER;
+ ulong_value = int_value;
+ data = (unsigned char *) &ulong_value;
+ nelements = 1;
+ format = 32;
+ }
+ else if ((type == XA_ATOM))
+ {
+ ulong_value = XInternAtom (dpy, prop->value, False);
+ data = (unsigned char *) &ulong_value;
+ nelements = 1;
+ }
+ else if ((type == XA_STRING || type == AnyPropertyType))
+ {
+ type = XA_STRING;
+ data = (unsigned char *) prop->value;
+ nelements = strlen (prop->value);
+ format = 8;
+ }
+ else
+ continue;
+ XRRChangeOutputProperty (dpy, output->output.xid,
+ name, type, format, PropModeReplace,
+ data, nelements);
+ }
+ }
+ if (!setit_1_2)
+ {
+ XSync (dpy, False);
+ exit (0);
+ }
+ }
+ if (setit_1_2)
+ {
+ get_screen (current);
+ get_crtcs ();
+ get_outputs ();
+ set_positions ();
+ set_screen_size ();
+
+ pick_crtcs ();
+
+ /*
+ * Assign outputs to crtcs
+ */
+ set_crtcs ();
+
+ /*
+ * Mark changing crtcs
+ */
+ mark_changing_crtcs ();
+
+ /*
+ * If an output was specified to track dpi, use it
+ */
+ if (dpi_output)
+ {
+ output_t *output = find_output_by_name (dpi_output);
+ XRROutputInfo *output_info;
+ XRRModeInfo *mode_info;
+ if (!output)
+ fatal ("Cannot find output %s\n", dpi_output);
+ output_info = output->output_info;
+ mode_info = output->mode_info;
+ if (output_info && mode_info && output_info->mm_height)
+ {
+ /*
+ * When this output covers the whole screen, just use
+ * the known physical size
+ */
+ if (fb_width == mode_info->width &&
+ fb_height == mode_info->height)
+ {
+ fb_width_mm = output_info->mm_width;
+ fb_height_mm = output_info->mm_height;
+ }
+ else
+ {
+ dpi = (25.4 * mode_info->height) / output_info->mm_height;
+ }
+ }
+ }
+
+ /*
+ * Compute physical screen size
+ */
+ if (fb_width_mm == 0 || fb_height_mm == 0)
+ {
+ if (fb_width != DisplayWidth (dpy, screen) ||
+ fb_height != DisplayHeight (dpy, screen) || dpi != 0.0)
+ {
+ if (dpi <= 0)
+ dpi = (25.4 * DisplayHeight (dpy, screen)) / DisplayHeightMM(dpy, screen);
+
+ fb_width_mm = (25.4 * fb_width) / dpi;
+ fb_height_mm = (25.4 * fb_height) / dpi;
+ }
+ else
+ {
+ fb_width_mm = DisplayWidthMM (dpy, screen);
+ fb_height_mm = DisplayHeightMM (dpy, screen);
+ }
+ }
+
+ /*
+ * Set panning
+ */
+ set_panning ();
+
+ /*
+ * Set gamma on crtc's that belong to the outputs.
+ */
+ set_gamma ();
+
+ /*
+ * Now apply all of the changes
+ */
+ apply ();
+
+ XSync (dpy, False);
+ exit (0);
+ }
+ if (query_1_2 || (query && has_1_2 && !query_1))
+ {
+ output_t *output;
+ int m;
+
+#define ModeShown 0x80000000
+
+ get_screen (current);
+ get_crtcs ();
+ get_outputs ();
+
+ printf ("Screen %d: minimum %d x %d, current %d x %d, maximum %d x %d\n",
+ screen, minWidth, minHeight,
+ DisplayWidth (dpy, screen), DisplayHeight(dpy, screen),
+ maxWidth, maxHeight);
+
+ for (output = outputs; output; output = output->next)
+ {
+ XRROutputInfo *output_info = output->output_info;
+ crtc_t *crtc = output->crtc_info;
+ XRRCrtcInfo *crtc_info = crtc ? crtc->crtc_info : NULL;
+ XRRModeInfo *mode = output->mode_info;
+ Atom *props;
+ int j, k, nprop;
+ Bool *mode_shown;
+ Rotation rotations = output_rotations (output);
+
+ printf ("%s %s", output_info->name, connection[output_info->connection]);
+ if (mode)
+ {
+ if (crtc_info) {
+ printf (" %dx%d+%d+%d",
+ crtc_info->width, crtc_info->height,
+ crtc_info->x, crtc_info->y);
+ } else {
+ printf (" %dx%d+%d+%d",
+ mode->width, mode->height, output->x, output->y);
+ }
+ if (verbose)
+ printf (" (0x%x)", (int)mode->id);
+ if (output->rotation != RR_Rotate_0 || verbose)
+ {
+ printf (" %s",
+ rotation_name (output->rotation));
+ if (output->rotation & (RR_Reflect_X|RR_Reflect_Y))
+ printf (" %s", reflection_name (output->rotation));
+ }
+ }
+ if (rotations != RR_Rotate_0 || verbose)
+ {
+ Bool first = True;
+ printf (" (");
+ for (i = 0; i < 4; i ++) {
+ if ((rotations >> i) & 1) {
+ if (!first) printf (" "); first = False;
+ printf("%s", direction[i]);
+ }
+ }
+ if (rotations & RR_Reflect_X)
+ {
+ if (!first) printf (" "); first = False;
+ printf ("x axis");
+ }
+ if (rotations & RR_Reflect_Y)
+ {
+ if (!first) printf (" ");
+ printf ("y axis");
+ }
+ printf (")");
+ }
+
+ if (mode)
+ {
+ printf (" %dmm x %dmm",
+ (int)output_info->mm_width, (int)output_info->mm_height);
+ }
+
+ if (crtc && crtc->panning_info && crtc->panning_info->width > 0)
+ {
+ XRRPanning *pan = crtc->panning_info;
+ printf (" panning %dx%d+%d+%d",
+ pan->width, pan->height, pan->left, pan->top);
+ if ((pan->track_width != 0 &&
+ (pan->track_left != pan->left ||
+ pan->track_width != pan->width ||
+ pan->border_left != 0 ||
+ pan->border_right != 0)) ||
+ (pan->track_height != 0 &&
+ (pan->track_top != pan->top ||
+ pan->track_height != pan->height ||
+ pan->border_top != 0 ||
+ pan->border_bottom != 0)))
+ printf (" tracking %dx%d+%d+%d border %d/%d/%d/%d",
+ pan->track_width, pan->track_height,
+ pan->track_left, pan->track_top,
+ pan->border_left, pan->border_top,
+ pan->border_right, pan->border_bottom);
+ }
+ printf ("\n");
+
+ if (verbose)
+ {
+ printf ("\tIdentifier: 0x%x\n", (int)output->output.xid);
+ printf ("\tTimestamp: %d\n", (int)output_info->timestamp);
+ printf ("\tSubpixel: %s\n", order[output_info->subpixel_order]);
+ if (output->gamma.red != 0.0 && output->gamma.green != 0.0 && output->gamma.blue != 0.0) {
+ printf ("\tGamma: %#.2g:%#.2g:%#.2g\n",
+ output->gamma.red, output->gamma.green, output->gamma.blue);
+ printf ("\tBrightness: %#.2g\n", output->brightness);
+ }
+ printf ("\tClones: ");
+ for (j = 0; j < output_info->nclone; j++)
+ {
+ output_t *clone = find_output_by_xid (output_info->clones[j]);
+
+ if (clone) printf (" %s", clone->output.string);
+ }
+ printf ("\n");
+ if (output->crtc_info)
+ printf ("\tCRTC: %d\n", output->crtc_info->crtc.index);
+ printf ("\tCRTCs: ");
+ for (j = 0; j < output_info->ncrtc; j++)
+ {
+ crtc_t *crtc = find_crtc_by_xid (output_info->crtcs[j]);
+ if (crtc)
+ printf (" %d", crtc->crtc.index);
+ }
+ printf ("\n");
+ if (output->crtc_info && output->crtc_info->panning_info) {
+ XRRPanning *pan = output->crtc_info->panning_info;
+ printf ("\tPanning: %dx%d+%d+%d\n",
+ pan->width, pan->height, pan->left, pan->top);
+ printf ("\tTracking: %dx%d+%d+%d\n",
+ pan->track_width, pan->track_height,
+ pan->track_left, pan->track_top);
+ printf ("\tBorder: %d/%d/%d/%d\n",
+ pan->border_left, pan->border_top,
+ pan->border_right, pan->border_bottom);
+ }
+ }
+ if (verbose)
+ {
+ int x, y;
+
+ printf ("\tTransform: ");
+ for (y = 0; y < 3; y++)
+ {
+ for (x = 0; x < 3; x++)
+ printf (" %f", XFixedToDouble (output->transform.transform.matrix[y][x]));
+ if (y < 2)
+ printf ("\n\t ");
+ }
+ if (output->transform.filter)
+ printf ("\n\t filter: %s", output->transform.filter);
+ printf ("\n");
+ }
+ if (verbose || properties)
+ {
+ props = XRRListOutputProperties (dpy, output->output.xid,
+ &nprop);
+ for (j = 0; j < nprop; j++) {
+ unsigned char *prop;
+ int actual_format;
+ unsigned long nitems, bytes_after;
+ Atom actual_type;
+ XRRPropertyInfo *propinfo;
+
+ XRRGetOutputProperty (dpy, output->output.xid, props[j],
+ 0, 100, False, False,
+ AnyPropertyType,
+ &actual_type, &actual_format,
+ &nitems, &bytes_after, &prop);
+
+ propinfo = XRRQueryOutputProperty(dpy, output->output.xid,
+ props[j]);
+
+ if (actual_type == XA_INTEGER && actual_format == 8) {
+ int k;
+
+ printf("\t%s:\n", XGetAtomName (dpy, props[j]));
+ for (k = 0; k < nitems; k++) {
+ if (k % 16 == 0)
+ printf ("\t\t");
+ printf("%02x", (unsigned char)prop[k]);
+ if (k % 16 == 15)
+ printf("\n");
+ }
+ } else if (actual_type == XA_INTEGER &&
+ actual_format == 32)
+ {
+ printf("\t%s: ", XGetAtomName (dpy, props[j]));
+ for (k = 0; k < nitems; k++) {
+ if (k > 0)
+ printf ("\n\t\t\t");
+ printf("%d (0x%08x)",
+ (int)((INT32 *)prop)[k], (int)((INT32 *)prop)[k]);
+ }
+
+ if (propinfo->range && propinfo->num_values > 0) {
+ if (nitems > 1)
+ printf ("\n\t\t");
+ printf("\trange%s: ",
+ (propinfo->num_values == 2) ? "" : "s");
+
+ for (k = 0; k < propinfo->num_values / 2; k++)
+ printf(" (%d,%d)", (int)propinfo->values[k * 2],
+ (int)propinfo->values[k * 2 + 1]);
+ }
+
+ printf("\n");
+ } else if (actual_type == XA_ATOM &&
+ actual_format == 32)
+ {
+ printf("\t%s:", XGetAtomName (dpy, props[j]));
+ for (k = 0; k < nitems; k++) {
+ if (k > 0 && (k & 1) == 0)
+ printf ("\n\t\t");
+ printf("\t%s", XGetAtomName (dpy, ((Atom *)prop)[k]));
+ }
+
+ if (!propinfo->range && propinfo->num_values > 0) {
+ printf("\n\t\tsupported:");
+
+ for (k = 0; k < propinfo->num_values; k++)
+ {
+ printf(" %-12.12s", XGetAtomName (dpy,
+ propinfo->values[k]));
+ if (k % 4 == 3 && k < propinfo->num_values - 1)
+ printf ("\n\t\t ");
+ }
+ }
+ printf("\n");
+ } else if (actual_format == 8) {
+ printf ("\t%s: %s%s\n", XGetAtomName (dpy, props[j]),
+ prop, bytes_after ? "..." : "");
+ } else {
+ char *type = actual_type ? XGetAtomName (dpy, actual_type) : "none";
+ printf ("\t%s: %s(%d) (format %d items %d) ????\n",
+ XGetAtomName (dpy, props[j]),
+ type, (int)actual_type, actual_format, (int)nitems);
+ }
+
+ free(propinfo);
+ }
+ }
+
+ if (verbose)
+ {
+ for (j = 0; j < output_info->nmode; j++)
+ {
+ XRRModeInfo *mode = find_mode_by_xid (output_info->modes[j]);
+ int f;
+
+ printf (" %s (0x%x) %6.1fMHz",
+ mode->name, (int)mode->id,
+ (double)mode->dotClock / 1000000.0);
+ for (f = 0; mode_flags[f].flag; f++)
+ if (mode->modeFlags & mode_flags[f].flag)
+ printf (" %s", mode_flags[f].string);
+ if (mode == output->mode_info)
+ printf (" *current");
+ if (j < output_info->npreferred)
+ printf (" +preferred");
+ printf ("\n");
+ printf (" h: width %4d start %4d end %4d total %4d skew %4d clock %6.1fKHz\n",
+ mode->width, mode->hSyncStart, mode->hSyncEnd,
+ mode->hTotal, mode->hSkew, mode_hsync (mode) / 1000);
+ printf (" v: height %4d start %4d end %4d total %4d clock %6.1fHz\n",
+ mode->height, mode->vSyncStart, mode->vSyncEnd, mode->vTotal,
+ mode_refresh (mode));
+ mode->modeFlags |= ModeShown;
+ }
+ }
+ else
+ {
+ mode_shown = calloc (output_info->nmode, sizeof (Bool));
+ if (!mode_shown) fatal ("out of memory\n");
+ for (j = 0; j < output_info->nmode; j++)
+ {
+ XRRModeInfo *jmode, *kmode;
+
+ if (mode_shown[j]) continue;
+
+ jmode = find_mode_by_xid (output_info->modes[j]);
+ printf (" ");
+ printf (" %-12s", jmode->name);
+ for (k = j; k < output_info->nmode; k++)
+ {
+ if (mode_shown[k]) continue;
+ kmode = find_mode_by_xid (output_info->modes[k]);
+ if (strcmp (jmode->name, kmode->name) != 0) continue;
+ mode_shown[k] = True;
+ kmode->modeFlags |= ModeShown;
+ printf (" %6.1f", mode_refresh (kmode));
+ if (kmode == output->mode_info)
+ printf ("*");
+ else
+ printf (" ");
+ if (k < output_info->npreferred)
+ printf ("+");
+ else
+ printf (" ");
+ }
+ printf ("\n");
+ }
+ free (mode_shown);
+ }
+ }
+ for (m = 0; m < res->nmode; m++)
+ {
+ XRRModeInfo *mode = &res->modes[m];
+
+ if (!(mode->modeFlags & ModeShown))
+ {
+ printf (" %s (0x%x) %6.1fMHz\n",
+ mode->name, (int)mode->id,
+ (double)mode->dotClock / 1000000.0);
+ printf (" h: width %4d start %4d end %4d total %4d skew %4d clock %6.1fKHz\n",
+ mode->width, mode->hSyncStart, mode->hSyncEnd,
+ mode->hTotal, mode->hSkew, mode_hsync (mode) / 1000);
+ printf (" v: height %4d start %4d end %4d total %4d clock %6.1fHz\n",
+ mode->height, mode->vSyncStart, mode->vSyncEnd, mode->vTotal,
+ mode_refresh (mode));
+ }
+ }
+ exit (0);
+ }
+
+ sc = XRRGetScreenInfo (dpy, root);
+
+ if (sc == NULL)
+ exit (1);
+
+ current_size = XRRConfigCurrentConfiguration (sc, &current_rotation);
+
+ sizes = XRRConfigSizes(sc, &nsize);
+
+ if (have_pixel_size) {
+ for (size = 0; size < nsize; size++)
+ {
+ if (sizes[size].width == width && sizes[size].height == height)
+ break;
+ }
+ if (size >= nsize) {
+ fprintf (stderr,
+ "Size %dx%d not found in available modes\n", width, height);
+ exit (1);
+ }
+ }
+ else if (size < 0)
+ size = current_size;
+ else if (size >= nsize) {
+ fprintf (stderr,
+ "Size index %d is too large, there are only %d sizes\n",
+ size, nsize);
+ exit (1);
+ }
+
+ if (rot < 0)
+ {
+ for (rot = 0; rot < 4; rot++)
+ if (1 << rot == (current_rotation & 0xf))
+ break;
+ }
+
+ current_rate = XRRConfigCurrentRate (sc);
+
+ if (rate < 0)
+ {
+ if (size == current_size)
+ rate = current_rate;
+ else
+ rate = 0;
+ }
+ else
+ {
+ rates = XRRConfigRates (sc, size, &nrate);
+ for (i = 0; i < nrate; i++)
+ if (rate == rates[i])
+ break;
+ if (i == nrate) {
+ fprintf (stderr, "Rate %.1f Hz not available for this size\n", rate);
+ exit (1);
+ }
+ }
+
+ if (version) {
+ int major_version, minor_version;
+ XRRQueryVersion (dpy, &major_version, &minor_version);
+ printf("Server reports RandR version %d.%d\n",
+ major_version, minor_version);
+ }
+
+ if (query || query_1) {
+ printf(" SZ: Pixels Physical Refresh\n");
+ for (i = 0; i < nsize; i++) {
+ printf ("%c%-2d %5d x %-5d (%4dmm x%4dmm )",
+ i == current_size ? '*' : ' ',
+ i, sizes[i].width, sizes[i].height,
+ sizes[i].mwidth, sizes[i].mheight);
+ rates = XRRConfigRates (sc, i, &nrate);
+ if (nrate) printf (" ");
+ for (j = 0; j < nrate; j++)
+ printf ("%c%-4d",
+ i == current_size && rates[j] == current_rate ? '*' : ' ',
+ rates[j]);
+ printf ("\n");
+ }
+ }
+
+ rotations = XRRConfigRotations(sc, &current_rotation);
+
+ rotation = 1 << rot ;
+ if (query) {
+ printf("Current rotation - %s\n",
+ rotation_name (current_rotation));
+
+ printf("Current reflection - %s\n",
+ reflection_name (current_rotation));
+
+ printf ("Rotations possible - ");
+ for (i = 0; i < 4; i ++) {
+ if ((rotations >> i) & 1) printf("%s ", direction[i]);
+ }
+ printf ("\n");
+
+ printf ("Reflections possible - ");
+ if (rotations & (RR_Reflect_X|RR_Reflect_Y))
+ {
+ if (rotations & RR_Reflect_X) printf ("X Axis ");
+ if (rotations & RR_Reflect_Y) printf ("Y Axis");
+ }
+ else
+ printf ("none");
+ printf ("\n");
+ }
+
+ if (verbose) {
+ printf("Setting size to %d, rotation to %s\n", size, direction[rot]);
+
+ printf ("Setting reflection on ");
+ if (reflection)
+ {
+ if (reflection & RR_Reflect_X) printf ("X Axis ");
+ if (reflection & RR_Reflect_Y) printf ("Y Axis");
+ }
+ else
+ printf ("neither axis");
+ printf ("\n");
+
+ if (reflection & RR_Reflect_X) printf("Setting reflection on X axis\n");
+
+ if (reflection & RR_Reflect_Y) printf("Setting reflection on Y axis\n");
+ }
+
+ /* we should test configureNotify on the root window */
+ XSelectInput (dpy, root, StructureNotifyMask);
+
+ if (setit && !dryrun) XRRSelectInput (dpy, root,
+ RRScreenChangeNotifyMask);
+ if (setit && !dryrun) status = XRRSetScreenConfigAndRate (dpy, sc,
+ root,
+ (SizeID) size, (Rotation) (rotation | reflection), rate, CurrentTime);
+
+ if (setit && !dryrun && status == RRSetConfigFailed) {
+ printf ("Failed to change the screen configuration!\n");
+ ret = 1;
+ }
+
+ if (verbose && setit && !dryrun && size != current_size) {
+ if (status == RRSetConfigSuccess)
+ {
+ Bool seen_screen = False;
+ while (!seen_screen) {
+ int spo;
+ XNextEvent(dpy, (XEvent *) &event);
+
+ printf ("Event received, type = %d\n", event.type);
+ /* update Xlib's knowledge of the event */
+ XRRUpdateConfiguration (&event);
+ if (event.type == ConfigureNotify)
+ printf("Received ConfigureNotify Event!\n");
+
+ switch (event.type - event_base) {
+ case RRScreenChangeNotify:
+ sce = (XRRScreenChangeNotifyEvent *) &event;
+
+ printf("Got a screen change notify event!\n");
+ printf(" window = %d\n root = %d\n size_index = %d\n rotation %d\n",
+ (int) sce->window, (int) sce->root,
+ sce->size_index, sce->rotation);
+ printf(" timestamp = %ld, config_timestamp = %ld\n",
+ sce->timestamp, sce->config_timestamp);
+ printf(" Rotation = %x\n", sce->rotation);
+ printf(" %d X %d pixels, %d X %d mm\n",
+ sce->width, sce->height, sce->mwidth, sce->mheight);
+ printf("Display width %d, height %d\n",
+ DisplayWidth(dpy, screen), DisplayHeight(dpy, screen));
+ printf("Display widthmm %d, heightmm %d\n",
+ DisplayWidthMM(dpy, screen), DisplayHeightMM(dpy, screen));
+ spo = sce->subpixel_order;
+ if ((spo < 0) || (spo > 5))
+ printf ("Unknown subpixel order, value = %d\n", spo);
+ else printf ("new Subpixel rendering model is %s\n", order[spo]);
+ seen_screen = True;
+ break;
+ default:
+ if (event.type != ConfigureNotify)
+ printf("unknown event received, type = %d!\n", event.type);
+ }
+ }
+ }
+ }
+ XRRFreeScreenConfigInfo(sc);
+ return(ret);
+}