--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/open-src/app/xrandr/dispswitch.patch Tue Sep 09 18:28:39 2008 -0700
@@ -0,0 +1,1948 @@
+diff -urN old/dispswitch.c new/dispswitch.c
+--- dispswitch.c 1969-12-31 16:00:00.000000000 -0800
++++ dispswitch.c 2008-09-07 21:45:43.578226000 -0700
+@@ -0,0 +1,1847 @@
++/*
++ * Copyright © 2001 Keith Packard, member of The XFree86 Project, Inc.
++ * Copyright © 2002 Hewlett Packard Company, Inc.
++ * Copyright © 2006 Intel Corporation
++ * Copyright © 2008 Sun Microsystems, Inc.
++ *
++ * 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.
++ *
++ */
++
++#include <stdio.h>
++#include <X11/Xlib.h>
++#include <X11/Xlibint.h>
++#include <X11/Xproto.h>
++#include <X11/extensions/Xrandr.h>
++#include <X11/Xos.h>
++#include <string.h>
++#include <stdlib.h>
++#include <stdarg.h>
++#include <fcntl.h>
++#include <signal.h>
++#include <sys/proc.h>
++#include <unistd.h>
++
++
++static char *program_name;
++static Display *dpy = NULL;
++static Window root;
++static int screen;
++static Bool nosideview = False;
++static Bool verbose = False;
++static Bool testrun = False;
++static int haderror = 0;
++static struct timeval time_val;
++static Rotation init_rotation;
++static int init_x;
++static int init_y;
++static Bool use_init_pos = False;
++
++static void
++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, " -key <key> or -k <key>\n");
++ fprintf(stderr, " -mod <modifier> or -m <modifier>\n");
++ fprintf(stderr, " -help\n");
++ fprintf(stderr, " -nosideview\n");
++ fprintf(stderr, " -verbose or -v\n");
++ fprintf(stderr, " -testrun\n");
++ exit(1);
++ /*NOTREACHED*/
++}
++
++static void
++fatal (const char *format, ...)
++{
++ va_list ap;
++
++ va_start (ap, format);
++ fprintf (stderr, "%s exiting: ", program_name);
++ vfprintf (stderr, format, ap);
++ va_end (ap);
++ exit (1);
++ /*NOTREACHED*/
++}
++
++static int
++curhandler (
++ Display *const errdisplay,
++ XErrorEvent *const errevent)
++{
++ haderror = errevent -> error_code;
++ return (0);
++}
++
++typedef enum _relation {
++ left_of, right_of, above, below, same_as,
++} relation_t;
++
++typedef enum _changes {
++ changes_none = 0,
++ changes_crtc = (1 << 0),
++ changes_mode = (1 << 1),
++ changes_relation = (1 << 2),
++ changes_position = (1 << 3),
++} changes_t;
++
++typedef enum _name_kind {
++ name_none = 0,
++ name_string = (1 << 0),
++ name_xid = (1 << 1),
++ name_index = (1 << 2),
++} 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;
++
++struct _crtc {
++ name_t crtc;
++ XRRCrtcInfo *crtc_info;
++
++ XRRModeInfo *mode_info;
++ int x;
++ int y;
++ Rotation rotation;
++ output_t **outputs;
++ int noutput;
++};
++
++struct _output {
++ struct _output *next;
++
++ changes_t changes;
++
++ name_t output;
++ XRROutputInfo *output_info;
++
++ name_t crtc;
++ crtc_t *crtc_info;
++ crtc_t *current_crtc_info;
++
++ name_t mode;
++ float refresh;
++ XRRModeInfo *mode_info;
++
++ name_t addmode;
++
++ relation_t relation;
++ struct _output *relative_to;
++
++ int x, y;
++ Rotation rotation;
++};
++
++static output_t *outputs = NULL;
++static output_t **outputs_tail = &outputs;
++static crtc_t *crtcs = NULL;
++static int num_crtcs;
++static XRRScreenResources *res = NULL;
++static int fb_width = 0, fb_height = 0;
++static int fb_width_mm = 0, fb_height_mm = 0;
++static float dpi = 0.0;
++static Bool dryrun = False;
++static int minWidth, maxWidth, minHeight, maxHeight;
++
++#define MAX_OUTPUT 3
++#define MAX_MODIFIERS 10
++
++typedef struct _con_output con_output_t;
++
++struct _con_output {
++ output_t *output;
++ XRRModeInfo **smodes;
++ int nsmodes;
++ Bool on;
++};
++
++typedef struct _mod_key_table mod_key_table_t;
++
++struct _mod_key_table {
++ char *modname;
++ unsigned int mod;
++};
++
++static con_output_t con_outputs[MAX_OUTPUT];
++static con_output_t dis_con_outputs[MAX_OUTPUT];
++static XRRModeInfo *start_mode[MAX_OUTPUT];
++static XRRModeInfo *new_mode[MAX_OUTPUT];
++static int start = 0;
++static int ncon;
++static int dis_ncon;
++static Bool do_not_switch = False;
++static Bool did_init = False;
++
++static mod_key_table_t mod_key_table [MAX_MODIFIERS] = {
++ {"none", 0},
++ {"shift", ShiftMask},
++ {"lock", LockMask},
++ {"control", ControlMask},
++ {"mod1", Mod1Mask},
++ {"mod2", Mod2Mask},
++ {"mod3", Mod3Mask},
++ {"mod4", Mod4Mask},
++ {"mod5", Mod5Mask},
++ {"any", AnyModifier}
++};
++
++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;
++ }
++}
++
++/* v refresh frequency in Hz */
++static float
++mode_refresh (XRRModeInfo *mode_info)
++{
++ float rate;
++
++ if (mode_info->hTotal && mode_info->vTotal)
++ rate = ((float) mode_info->dotClock /
++ ((float) mode_info->hTotal * (float) mode_info->vTotal));
++ 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_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 output_t *
++add_output (void)
++{
++ output_t *output = calloc (1, sizeof (output_t));
++
++ if (!output)
++ fatal ("out of memory");
++ output->next = NULL;
++ *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 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)
++{
++ int m;
++ XRRModeInfo *best = NULL;
++
++ for (m = 0; m < res->nmode; m++)
++ {
++ XRRModeInfo *mode = &res->modes[m];
++ if ((name->kind & name_xid) && name->xid == mode->id)
++ {
++ best = mode;
++ break;
++ }
++ }
++ 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);
++}
++
++static void
++set_output_info (output_t *output, RROutput xid, XRROutputInfo *output_info)
++{
++ crtc_t *crtc;
++
++ /* sanity check output info */
++ if (output_info->connection != RR_Disconnected && !output_info->nmode)
++ fatal ("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;
++
++ crtc = find_crtc_by_xid (output->output_info->crtc);
++ /* set position */
++ if (crtc && crtc->crtc_info) {
++ output->x = crtc->crtc_info->x;
++ output->y = crtc->crtc_info->y;
++ }
++
++ /* set rotation */
++ output->rotation &= ~0xf;
++ if (crtc && crtc->crtc_info)
++ output->rotation |= (crtc->crtc_info->rotation & 0xf);
++ else
++ output->rotation = RR_Rotate_0;
++
++}
++
++static void
++get_crtcs (void)
++{
++ int c;
++
++ num_crtcs = res->ncrtc;
++ if (crtcs)
++ {
++ for (c = 0; c < res->ncrtc; c++)
++ {
++ if (crtcs[c].crtc_info)
++ XRRFreeCrtcInfo (crtcs[c].crtc_info);
++ }
++ free (crtcs);
++ crtcs = NULL;
++ }
++
++ crtcs = calloc (num_crtcs, sizeof (crtc_t));
++ if (!crtcs) fatal ("out of memory");
++
++ for (c = 0; c < res->ncrtc; c++)
++ {
++ XRRCrtcInfo *crtc_info = XRRGetCrtcInfo (dpy, res, res->crtcs[c]);
++ 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", res->crtcs[c]);
++ crtcs[c].crtc_info = crtc_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;
++ }
++ }
++}
++
++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 = calloc (1, sizeof (output_t *));
++ crtc->x = output->x;
++ crtc->y = output->y;
++ crtc->rotation = output->rotation;
++ crtc->mode_info = output->mode_info;
++ }
++ if (!crtc->outputs) fatal ("out of memory");
++ crtc->outputs[crtc->noutput++] = output;
++}
++
++static void
++set_crtcs (void)
++{
++ output_t *output;
++ int i;
++
++ for (i = 0; i < ncon; i++) {
++ output = con_outputs[i].output;
++ if (!output->mode_info) continue;
++ if (output->crtc_info)
++ crtc_add_output (output->crtc_info, output);
++ }
++}
++
++static void
++reset_crtcs_for_outputs (void)
++{
++ output_t *output;
++
++ for (output = outputs; output; output = output->next)
++ {
++ if ((output->crtc_info) && (output->crtc_info->outputs)) {
++ free (output->crtc_info->outputs);
++ output->crtc_info = NULL;
++ }
++ }
++}
++
++static Status
++crtc_disable (crtc_t *crtc)
++{
++ if (verbose)
++ fprintf (stderr, "crtc %d (%x) : disable\n", crtc->crtc.index, crtc->crtc.xid);
++
++ if (dryrun)
++ return RRSetConfigSuccess;
++
++ return XRRSetCrtcConfig (dpy, res, crtc->crtc.xid, CurrentTime,
++ 0, 0, None, RR_Rotate_0, NULL, 0);
++}
++
++static Status
++crtc_revert (crtc_t *crtc)
++{
++ XRRCrtcInfo *crtc_info = crtc->crtc_info;
++
++ if (verbose)
++ fprintf (stderr, "crtc %d: revert\n", crtc->crtc.index);
++
++ if (dryrun)
++ return RRSetConfigSuccess;
++ 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->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) {
++ fprintf (stderr, "crtc %d (%x) : %12s %6.1f +%d+%d", crtc->crtc.index,
++ crtc->crtc.xid,
++ crtc->mode_info->name, mode_refresh (crtc->mode_info),
++ crtc->x, crtc->y);
++ for (o = 0; o < crtc->noutput; o++)
++ fprintf (stderr, " \"%s\"", crtc->outputs[o]->output.string);
++ fprintf (stderr, "\n");
++ }
++ if (dryrun)
++ s = RRSetConfigSuccess;
++ else
++ s = XRRSetCrtcConfig (dpy, res, crtc->crtc.xid, CurrentTime,
++ crtc->x, crtc->y, mode, crtc->rotation,
++ rr_outputs, crtc->noutput);
++ free (rr_outputs);
++ return s;
++}
++
++static void
++screen_revert (void)
++{
++ if (verbose)
++ fprintf (stderr, "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)
++{
++ /* comment it out because DisplayWidth() does not reflect the
++ change of fb_width and fb_height previously set by
++ XRRSetScreenSize()
++ */
++ /*
++ 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)
++ fprintf (stderr, "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
++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;
++
++ /*
++ * 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
++ * Note server sets crtc_info->mode (before change)
++ */
++ if (crtc_info->mode == None)
++ continue;
++
++ /*
++ * If this crtc is to be left enabled, make
++ * sure the old size fits then new screen
++ * When crtc->mode_info is null, the crtc is to be
++ * disabled. Note set_crtcs () sets crtc->mode_info for
++ * new mode (to be changed to).
++ */
++ if (crtc->mode_info)
++ {
++ XRRModeInfo *old_mode = find_mode_by_xid (crtc_info->mode);
++ int x, y, w, h;
++
++ if (!old_mode)
++ panic (RRSetConfigFailed, crtc);
++
++ /* old position and size information */
++ x = crtc_info->x;
++ y = crtc_info->y;
++ w = mode_width (old_mode, crtc_info->rotation);
++ h = mode_height (old_mode, crtc_info->rotation);
++
++ /* if it fits, skip it */
++ if (x + w <= fb_width && y + h <= fb_height)
++ continue;
++ }
++ s = crtc_disable (crtc);
++ if (s != RRSetConfigSuccess)
++ panic (s, crtc);
++ }
++
++ /*
++ * 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
++ */
++ XGrabServer (dpy);
++
++ /*
++ * 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);
++ }
++ /*
++ * Release the server grab and let all clients
++ * respond to the updated state
++ */
++ XUngrabServer (dpy);
++}
++
++/*
++ * Use current output state to complete the output list
++ */
++static void
++get_outputs (void)
++{
++ int o;
++
++ 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", 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);
++ }
++
++ set_output_info (output, res->outputs[o], output_info);
++ }
++}
++
++
++/*
++ * Test whether 'crtc' can be used for 'output'
++ */
++static Bool
++check_crtc_for_output (crtc_t *crtc, output_t *output)
++{
++ int i;
++ int l;
++ output_t *other;
++
++ for (i = 0; i < ncon; i++)
++ {
++ other = con_outputs[i].output;
++
++ 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)
++ {
++ for (i = 0; i < crtc->noutput; i++)
++ /* Check if the output is to be turned on */
++ if (crtc->outputs[i]->mode_info) {
++ /* 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;
++ }
++ }
++ 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)
++{
++ Bool keep_going;
++ Bool any_set;
++ int min_x, min_y;
++ int i;
++
++ for (;;)
++ {
++ any_set = False;
++ keep_going = False;
++ for (i = 0; i < ncon; i++)
++ {
++ output_t *output = con_outputs[i].output;
++ output_t *relation;
++
++ if (!(output->changes & changes_relation)) continue;
++
++ if (output->mode_info == NULL) continue;
++
++ relation = 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 left_of:
++ output->y = relation->y;
++ output->x = relation->x - mode_width (output->mode_info, output->rotation);
++ break;
++ case right_of:
++ output->y = relation->y;
++ output->x = relation->x + mode_width (relation->mode_info, relation->rotation);
++ break;
++ case above:
++ output->x = relation->x;
++ output->y = relation->y - mode_height (output->mode_info, output->rotation);
++ break;
++ case below:
++ output->x = relation->x;
++ output->y = relation->y + mode_height (relation->mode_info, relation->rotation);
++ break;
++ case same_as:
++ output->x = relation->x;
++ output->y = relation->y;
++ }
++ output->changes |= changes_position;
++ relation->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 (i = 0; i < ncon; i++)
++ {
++ output_t *output = con_outputs[i].output;
++
++ 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 (i = 0; i < ncon; i++)
++ {
++ output_t *output = con_outputs[i].output;
++
++ if (output->mode_info == NULL) continue;
++
++ output->x -= min_x;
++ output->y -= min_y;
++ output->changes |= changes_position;
++ }
++ }
++}
++
++static Bool
++set_screen_size (void)
++{
++ int i;
++
++ fb_width = fb_height = 0;
++
++ for (i = 0; i < ncon; i++)
++ {
++ output_t *output = con_outputs[i].output;
++ XRRModeInfo *mode_info = output->mode_info;
++ int x, y, w, h;
++
++ if (!mode_info) continue;
++
++ x = output->x;
++ y = output->y;
++ w = mode_width (mode_info, output->rotation);
++ h = mode_height (mode_info, output->rotation);
++ if (x + w > fb_width) fb_width = x + w;
++ if (y + h > fb_height) fb_height = y + h;
++ }
++
++ if (fb_width > maxWidth || fb_height > maxHeight) {
++ if (verbose)
++ fprintf (stderr, "screen cannot be larger than %dx%d (desired size %dx%d)\n",
++ maxWidth, maxHeight, fb_width, fb_height);
++ return False;
++ }
++
++ if (fb_width < minWidth) fb_width = minWidth;
++ if (fb_height < minHeight) fb_height = minHeight;
++
++ return True;
++}
++
++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 Bool
++pick_crtcs (void)
++{
++ output_t *output;
++ int i;
++
++ /*
++ * First try to match up newly enabled outputs with spare crtcs
++ */
++ for (i = 0; i < ncon; i++)
++ {
++ output = con_outputs[i].output;
++
++ if (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;
++ else {
++ if (verbose)
++ fprintf(stderr, "picked crtc %x for output %d (%s)\n",
++ output->crtc_info->crtc.xid, i, output->output_info->name);
++ }
++ }
++ }
++ }
++
++ /*
++ * Everyone is happy
++ */
++ if (i == ncon)
++ return True;
++ /*
++ * 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) {
++ if (verbose)
++ fprintf (stderr, "cannot find crtc for output %s\n",
++ output->output.string);
++ return False;
++ }
++ }
++
++ return True;
++}
++
++static Bool
++probe_and_check_output_changes (void) {
++ XRRScreenResources *new_res = NULL;
++ int changed = False;
++ int i;
++
++ new_res = XRRGetScreenResources (dpy, root);
++ if (!new_res)
++ fatal ("could not get screen resources");
++
++ if ((new_res->noutput != res->noutput) || (new_res->nmode != res->nmode) ||
++ (new_res->ncrtc != res->ncrtc))
++ changed = True;
++ else {
++ for (i = 0; i < new_res->noutput; i++)
++ if (new_res->outputs[i] != res->outputs[i]) {
++ changed = True;
++ break;
++ }
++ for (i = 0; i < new_res->nmode; i++)
++ if (new_res->modes[i].id != res->modes[i].id) {
++ changed = True;
++ break;
++ }
++ for (i = 0; i < new_res->ncrtc; i++) {
++ crtc_t *crtc = NULL; /* old */
++ XRRCrtcInfo *crtc_info = NULL; /* new */
++
++ crtc = find_crtc_by_xid (res->crtcs[i]);
++ crtc_info = XRRGetCrtcInfo (dpy, new_res,
++ new_res->crtcs[i]);
++
++ if (!crtc || !crtc_info) {
++ changed = True;
++ break;
++ }
++ if (!crtc->mode_info && !find_mode_by_xid (crtc_info->mode))
++ continue;
++ if ((crtc_info->x != crtc->x) ||
++ (crtc_info->y != crtc->y) ||
++ (find_mode_by_xid (crtc_info->mode) != crtc->mode_info) ||
++ (crtc_info->rotation != crtc->rotation)) {
++ changed = True;
++ break;
++ }
++ }
++ }
++
++ if (changed) {
++ if (res)
++ XRRFreeScreenResources(res);
++ res = new_res;
++
++ if (verbose)
++ fprintf(stderr, "probed: output status changed\n");
++ return True;
++ }
++
++ if (verbose)
++ fprintf(stderr, "probed: no output status change\n");
++ return False;
++}
++
++static Bool
++need_probe (void) {
++ struct timeval cur_time_val;
++ long long cur, prev;
++
++ X_GETTIMEOFDAY(&cur_time_val);
++ cur = (long long) cur_time_val.tv_sec * 1000000 + cur_time_val.tv_usec;
++ prev =(long long) time_val.tv_sec * 1000000 + time_val.tv_usec;
++ if (((cur - prev) < 0) || ((cur - prev) > 5000000))
++ return True;
++ else
++ return False;
++}
++
++static int
++mode_sort (const void *p1, void *p2)
++{
++ XRRModeInfo *mi1 = * (XRRModeInfo **) p1;
++ XRRModeInfo *mi2 = * (XRRModeInfo **) p2;
++
++ if ((mi1->width == mi2->width) && (mi1->height == mi2->height)) {
++ if (mode_refresh(mi1) && mode_refresh(mi2)) {
++ if (mode_refresh(mi1) < mode_refresh(mi2))
++ return 1;
++ if (mode_refresh(mi1) > mode_refresh(mi2))
++ return -1;
++ } else
++ return 0;
++ }
++
++ if ((mi1->width == mi2->width) && (mi1->height < mi2->height))
++ return 1;
++ if ((mi1->width == mi2->width) && (mi1->height > mi2->height))
++ return -1;
++ if (mi1->width < mi2->width)
++ return 1;
++ if (mi1->width > mi2->width)
++ return -1;
++
++ return 0;
++}
++
++static int
++output_sort (const void *p1, const void *p2) {
++ con_output_t co1 = * (con_output_t *) p1;
++ con_output_t co2 = * (con_output_t *) p2;
++ int ncrtc1 = co1.output->output_info->ncrtc;
++ int ncrtc2 = co2.output->output_info->ncrtc;
++ char *name1 = co1.output->output_info->name;
++ char *name2 = co2.output->output_info->name;
++
++ if (ncrtc1 == ncrtc2)
++ return (strcmp(name1, name2));
++ if (ncrtc1 < ncrtc2)
++ return -1;
++
++ return 1;
++}
++
++static Bool
++get_common_mode(con_output_t *c0, con_output_t *c1, int *m0, int *m1) {
++ int i, j;
++ int i1 = -1, j1 = -1, i2 = -1, j2 = -1;
++ int x, y, w, h;
++ output_t *output = c0->output;
++
++ *m0 = -1;
++ *m0 = -1;
++ if (!c0 ||!c1 || !c0->smodes || !c1->smodes)
++ return False;
++
++ /* first try to find mode with common same size */
++ for (i = 0; i < c0->nsmodes; i ++) {
++ for (j = 0; j < c1->nsmodes; j ++)
++ if ((c0->smodes[i]->width == c1->smodes[j]->width) &&
++ (c0->smodes[i]->height == c1->smodes[j]->height)) {
++ x = output->x;
++ y = output->y;
++ w = mode_width (c0->smodes[i], output->rotation);
++ h = mode_height (c0->smodes[i], output->rotation);
++ if ((x + w <= maxWidth) && (y + h <= maxHeight)) {
++ i1 = i; j1 = j;
++ break;
++ }
++ }
++ if ((i1 != -1) && (j1 != -1))
++ break;
++ }
++
++ if ((i1 == -1) && (j1 == -1))
++ return False;
++
++ /* then try to find mode with common id for possible cloning */
++ for (i = 0; i < c0->nsmodes; i ++) {
++ for (j = 0; j < c1->nsmodes; j ++)
++ if (c0->smodes[i] == c1->smodes[j]) {
++ x = output->x;
++ y = output->y;
++ w = mode_width (c0->smodes[i], output->rotation);
++ h = mode_height (c0->smodes[i], output->rotation);
++ if ((x + w <= maxWidth) && (y + h <= maxHeight)) {
++ i2 = i; j2 = j;
++ break;
++ }
++ }
++ if ((i2 != -1) && (j2 != -1))
++ break;
++ }
++
++ if ((i2 == -1) && (j2 == -1)) {
++ *m0 = i1;
++ *m1 = j1;
++ } else {
++ /* use common id if it is not smaller */
++ if ((mode_width (c0->smodes[i1], output->rotation) >
++ mode_width (c0->smodes[i2], output->rotation)) &&
++ (mode_height (c0->smodes[i1], output->rotation) >
++ mode_height (c0->smodes[i2], output->rotation))) {
++ *m0 = i1;
++ *m1 = j1;
++ } else {
++ *m0 = i2;
++ *m1 = j2;
++ }
++ }
++
++ return True;
++}
++
++static XRRModeInfo *
++get_largest_mode (con_output_t *c, XRRModeInfo *start_mode) {
++ int i, found = False;
++ output_t *output = c->output;
++
++ for (i = 0; i < c->nsmodes; i++) {
++ XRRModeInfo *mode_info = c->smodes[i];
++ int x, y, w, h;
++
++ if (!found && (start_mode != mode_info))
++ continue;
++ else
++ found = True;
++
++ if (mode_info) {
++ x = output->x;
++ y = output->y;
++ w = mode_width (mode_info, output->rotation);
++ h = mode_height (mode_info, output->rotation);
++ if ((x + w <= maxWidth) && (y + h <= maxHeight))
++ break;
++ }
++ }
++
++ if (i < c->nsmodes)
++ return c->smodes[i];
++ else
++ fatal("cannot find mode");
++
++}
++
++static void
++do_init (void)
++{
++ int i, j;
++ output_t *output;
++
++ /* Initialize con_outputs array */
++ for (i = 0; i < MAX_OUTPUT; i++) {
++ con_outputs[i].output = NULL;
++ con_outputs[i].on = False;
++ start_mode[i] = NULL;
++ new_mode[i] = NULL;
++ }
++
++ ncon = 0;
++ dis_ncon = 0;
++ init_rotation = RR_Rotate_0;
++ init_x = 0;
++ init_y = 0;
++ get_crtcs ();
++ get_outputs ();
++
++ for (output = outputs; output; output = output->next) {
++ XRROutputInfo *output_info = output->output_info;
++
++ if (output_info->connection == RR_Connected) {
++ con_outputs[ncon].output = output;
++ con_outputs[ncon].nsmodes = 0;
++ for (j = 0; j < output_info->nmode; j++) {
++ XRRModeInfo *rmode = find_mode_by_xid (output_info->modes[j]);
++
++ con_outputs[ncon].smodes =
++ realloc(con_outputs[ncon].smodes,
++ (con_outputs[ncon].nsmodes + 1) * sizeof (XRRModeInfo *));
++ con_outputs[ncon].smodes[j] = rmode;
++ con_outputs[ncon].nsmodes ++;
++ }
++
++ /* Sort the modes */
++ qsort((void *) con_outputs[ncon].smodes,
++ con_outputs[ncon].nsmodes, sizeof(XRRModeInfo *),
++ (int (*) (const void *, const void *)) mode_sort);
++
++ if (output_info->crtc) {
++ crtc_t *crtc;
++
++ con_outputs[ncon].on = True;
++ for (j = 0; j < output_info->ncrtc; j++) {
++ if (output_info->crtcs[j] == output_info->crtc)
++ break;
++ if (j == output_info->ncrtc) {
++ if (verbose)
++ fatal ("crtc does not match for output\n");
++ }
++ }
++ /* set initial mode_info */
++ crtc = find_crtc_by_xid (output_info->crtc);
++ if (crtc)
++ con_outputs[ncon].output->mode_info =
++ find_mode_by_xid (crtc->crtc_info->mode);
++ }
++ else
++ con_outputs[ncon].on = False;
++ ncon ++;
++ } else if (output_info->connection == RR_Disconnected) {
++ dis_con_outputs[dis_ncon].output = output;
++ dis_ncon ++;
++ }
++ }
++
++ qsort((void **) con_outputs, ncon,
++ sizeof(con_output_t), (int (*) (const void *, const void *)) output_sort);
++
++ if (verbose) {
++ fprintf(stderr, "Total connected outputs = %d :\n", ncon);
++ for (j = 0; j < ncon; j++) {
++ fprintf(stderr, "%d (%s): top mode = %s, rotation = %d, crtcs =", j,
++ con_outputs[j].output->output_info->name,
++ con_outputs[j].smodes[0]->name,
++ con_outputs[j].output->rotation);
++ for (i = 0; i < con_outputs[j].output->output_info->ncrtc; i++)
++ fprintf(stderr, " %x", con_outputs[j].output->output_info->crtcs[i]);
++ fprintf(stderr, ", using %x", con_outputs[j].output->output_info->crtc);
++ fprintf(stderr, "\n");
++ }
++ fprintf(stderr, "Total disconnected outputs = %d :\n", dis_ncon);
++ for (j = 0; j < dis_ncon; j++) {
++ fprintf(stderr, "%d (%s) : number of crtcs %d =", j,
++ dis_con_outputs[j].output->output_info->name,
++ dis_con_outputs[j].output->output_info->ncrtc);
++ for (i = 0; i < dis_con_outputs[j].output->output_info->ncrtc; i++)
++ fprintf(stderr, " %x", dis_con_outputs[j].output->output_info->crtcs[i]);
++ fprintf(stderr, ", using %x", dis_con_outputs[j].output->output_info->crtc);
++ fprintf(stderr, "\n");
++ }
++ }
++
++ i = con_outputs[2].on * 4 + con_outputs[1].on * 2 + con_outputs[0].on;
++
++ if ((i == 1) || (i == 2) || (i == 4)) {
++ use_init_pos = True;
++ j = i >> 1;
++
++ /* remember position and mode info in single state */
++ start_mode[j] = con_outputs[j].output->mode_info;
++ init_rotation = con_outputs[j].output->rotation;
++ init_x = con_outputs[j].output->x;
++ init_y = con_outputs[j].output->y;
++ } else
++ use_init_pos = False;
++
++ if ((ncon != 2) || (start < 3))
++ start = i;
++
++ if ((ncon < 2) || (ncon > 3)) {
++ if (ncon < 2)
++ fprintf (stderr, "warn: too few (less than 2) connections: %d: can't switch\n", ncon);
++ else
++ fprintf (stderr, "warn: too many (more than 3) connections: %d: can't switch\n", ncon);
++ do_not_switch = True;
++ } else
++ do_not_switch = False;
++
++ did_init = True;
++
++ return;
++}
++
++static Bool
++do_switch (void)
++{
++ int i, j;
++ int single;
++
++ for (i = 0; i < ncon; i++)
++ new_mode[i] = NULL;
++
++ for (i = 0; i < ncon; i++) {
++ output_t *output = con_outputs[i].output;
++
++ output->relation = same_as;
++ output->relative_to = NULL;
++ if (use_init_pos) {
++ output->x = init_x;
++ output->y = init_y;
++ output->rotation = init_rotation;
++ } else {
++ output->x = 0;
++ output->y = 0;
++ }
++ }
++
++ if (ncon == 2) {
++ if (!nosideview) {
++ if (++start > 5) start = 1;
++ }
++ else {
++ if (++start > 3) start = 1;
++ }
++
++ if (verbose)
++ fprintf(stderr, "current state = %d\n", start);
++ if (start >= 3) {
++ int m0, m1;
++
++ if (get_common_mode(&con_outputs[0], &con_outputs[1], &m0, &m1)) {
++ new_mode[0] = con_outputs[0].smodes[m0];
++ new_mode[1] = con_outputs[1].smodes[m1];
++ } else {
++ new_mode[0] = get_largest_mode (&con_outputs[0],
++ con_outputs[0].smodes[0]);
++ new_mode[1] = get_largest_mode (&con_outputs[1],
++ con_outputs[1].smodes[0]);
++ }
++ } else {
++ if (start_mode[start -1])
++ new_mode[start -1] = start_mode[start -1];
++ else {
++ if (con_outputs[start -1].smodes[0])
++ new_mode[start -1] =
++ get_largest_mode (&con_outputs[start-1],
++ con_outputs[start -1].smodes[0]);
++ }
++ }
++ }
++
++ if (ncon == 3) {
++ if (++start > 6) start = 1;
++ if (verbose)
++ fprintf(stderr, "current state = %d\n", start);
++ if ((start == 1) || (start == 2) || (start == 4)) {
++ single = 1;
++ i = start >> 1;
++ j = 0;
++ }
++ else {
++ single = 0;
++ if (start > 4)
++ j = 2;
++ else
++ j = 1;
++ if (start > 5)
++ i = 1;
++ else
++ i = 0;
++ }
++
++ if (single) {
++ if (start_mode[i])
++ new_mode[i] = start_mode[i];
++ else {
++ if (con_outputs[i].smodes[0])
++ new_mode[i] = get_largest_mode (&con_outputs[i],
++ con_outputs[i].smodes[0]);
++ }
++ }
++ else {
++ int m0, m1;
++
++ if (get_common_mode(&con_outputs[i], &con_outputs[j], &m0, &m1)) {
++ new_mode[i] = con_outputs[i].smodes[m0];
++ new_mode[j] = con_outputs[j].smodes[m1];
++ } else {
++ new_mode[i] = get_largest_mode (&con_outputs[i],
++ con_outputs[i].smodes[0]);
++ new_mode[j] = get_largest_mode (&con_outputs[j],
++ con_outputs[j].smodes[0]);
++ }
++ }
++ }
++
++ /* Set mode */
++ for (i = 0; i < ncon; i++) {
++ output_t *output;
++
++ output = con_outputs[i].output;
++ if (new_mode[i]) {
++ if ((!output->mode_info) || (output->mode_info != new_mode[i])) {
++ output->mode_info = new_mode[i];
++ con_outputs[i].on = True;
++ if (verbose)
++ fprintf(stderr, "set output %d (%s) to mode %s rotation %d\n", i,
++ con_outputs[i].output->output_info->name,
++ con_outputs[i].output->mode_info->name,
++ con_outputs[i].output->rotation);
++ }
++ } else if (con_outputs[i].on ) {
++ output->mode_info = NULL;
++ con_outputs[i].on = False;
++ if (verbose)
++ fprintf(stderr, "turn off output %d (%s) \n",
++ i, con_outputs[i].output->output_info->name);
++ }
++ }
++
++ if ((ncon == 2) && (start >= 4)) {
++ if (start == 4) {
++ con_outputs[1].output->relative_to = con_outputs[0].output;
++ con_outputs[1].output->relation = right_of;
++ con_outputs[1].output->changes = changes_relation;
++ con_outputs[0].output->changes = 0;
++ }
++ else if (start == 5) {
++ con_outputs[0].output->relative_to = con_outputs[1].output;
++ con_outputs[0].output->relation = right_of;
++ con_outputs[0].output->changes = changes_relation;
++ con_outputs[1].output->changes = 0;
++ }
++
++ set_positions();
++ }
++
++ if (!set_screen_size ())
++ return False;
++
++
++ /* reset crtcs before allocation */
++ reset_crtcs_for_outputs();
++
++ if (!did_init)
++ get_crtcs();
++
++ if (!pick_crtcs()) {
++ if (verbose)
++ fprintf(stderr, "pick_crtcs failed\n");
++ return True;
++ }
++
++ set_crtcs ();
++ apply();
++ XSync (dpy, False);
++
++ did_init = False;
++
++ return True;
++
++}
++
++int
++main (int argc, char **argv)
++{
++ char *display_name = NULL;
++ int major, minor;
++ int i;
++ char msg[256];
++ XEvent ev;
++ unsigned int modifier = 0;
++ Bool keygiven = False;
++ Bool modgiven = False;
++ int keysym = 0;
++ XErrorHandler prevhandler;
++
++ 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 ("-key", argv[i]) || !strcmp ("-k", argv[i])) {
++ if (++i>=argc) usage ();
++ if ((keysym = XStringToKeysym(argv[i])) == NoSymbol) {
++ fprintf(stderr, "invalid keysym: -key %s\n", argv[i]);
++ usage();
++ }
++ keygiven = True;
++ continue;
++ }
++ if (!strcmp ("-mod", argv[i]) || !strcmp ("-m", argv[i])) {
++ int j;
++ char *s, *p, *q;
++ int end = 0;
++
++ if (++i>=argc) usage ();
++ s = strdup (argv[i]);
++ if (!s) {
++ if (verbose)
++ fprintf(stderr, "modifier failed, will use default modifier\n");
++ continue;
++ }
++ while (*s == ' ') s++;
++ p = s + strlen(s) - 1;
++ while (*p == ' ') *p-- = 0;
++ q = s;
++ for (; ;) {
++ if (p = strchr(s, '+')) {
++ *p = ' ';
++ while ((p > s) && (*(p-1) == ' ')) p--;
++ *p = 0;
++ }
++ else
++ end = 1;
++ for (j = 0; j < MAX_MODIFIERS; j++) {
++ if (!strcmp(mod_key_table[j].modname, s)) {
++ modifier |= mod_key_table[j].mod;
++ break;
++ }
++ }
++ if (j == MAX_MODIFIERS) {
++ fprintf(stderr, "invalid modifier: -mod %s\n", q);
++ usage();
++ }
++ if (end)
++ break;
++ else {
++ s = ++p;
++ while (*s == ' ') s++;
++ }
++ }
++ modgiven = True;
++ free (q);
++ continue;
++ }
++ if (!strcmp ("-nosideview", argv[i])) {
++ nosideview = True;
++ continue;
++ }
++ if (!strcmp ("-verbose", argv[i]) || !strcmp ("-v", argv[i])) {
++ verbose = True;
++ continue;
++ }
++ if (!strcmp ("-testrun", argv[i])) {
++ testrun = True;
++ verbose = True;
++ continue;
++ }
++ usage();
++ }
++
++ dpy = XOpenDisplay (display_name);
++
++ if (dpy == NULL)
++ fatal ("can't open display %s\n", XDisplayName(display_name));
++
++ screen = DefaultScreen (dpy);
++ root = RootWindow (dpy, screen);
++
++ if (!XRRQueryVersion (dpy, &major, &minor))
++ fatal ("randr extension missing\n");
++
++ if ((major <= 1) && (major != 1 || minor < 2))
++ fatal ("wrong randr version: %d.%d\n", major, minor);
++
++ haderror = 0;
++ prevhandler = XSetErrorHandler (curhandler);
++
++ /* set default key and modifier if not given in command */
++ if (!keygiven)
++ keysym = XStringToKeysym ("F5");
++ if (!modgiven)
++ modifier = ShiftMask;
++
++ if (!testrun) {
++ XGrabKey(dpy,
++ XKeysymToKeycode(dpy, keysym),
++ modifier,
++ root, True, GrabModeAsync, GrabModeAsync);
++ XSync (dpy, False);
++ }
++
++ XSetErrorHandler (prevhandler);
++ if (haderror) {
++ XGetErrorText (dpy, haderror, msg, sizeof (msg));
++ fatal ("XGrabKey: %s\n", msg);
++ }
++
++ XRRGetScreenSizeRange (dpy, root, &minWidth, &minHeight,
++ &maxWidth, &maxHeight);
++
++ fb_width_mm = DisplayWidthMM (dpy, screen);
++ fb_height_mm = DisplayHeightMM (dpy, screen);
++ dpi = (25.4 * DisplayHeight (dpy, screen)) / DisplayHeightMM(dpy, screen);
++
++ res = XRRGetScreenResources (dpy, root);
++ if (!res)
++ fatal ("could not get screen resources\n");
++ if (res->ncrtc != 2)
++ fatal ("too few (less than 2) or too many (more than 2) crtcs: %d\n", res->ncrtc);
++
++ do_init();
++
++ X_GETTIMEOFDAY(&time_val);
++
++ for(;;)
++ {
++ if (testrun) {
++ usleep(4000000);
++ fprintf(stderr, "\n");
++ } else
++ XNextEvent(dpy, &ev);
++
++ if (testrun || (ev.type == KeyPress)) {
++ if (verbose)
++ fprintf(stderr, "\na key press event was grabbed ...\n");
++
++ if (testrun || need_probe()) {
++ /* Too long since last switch, need to check output changes */
++ if (probe_and_check_output_changes ()) {
++ output_t *output, *next;
++
++ output = outputs;
++ while (output) {
++ if (output->output_info)
++ XRRFreeOutputInfo (output->output_info);
++ if (output->crtc_info && output->crtc_info->outputs) {
++ free(output->crtc_info->outputs);
++ output->crtc_info->outputs = NULL;
++ }
++ next = output->next;
++ free(output);
++ output = next;
++ }
++ outputs = NULL;
++ outputs_tail = &outputs;
++ for (i = 0; i < ncon; i++) {
++ con_outputs[i].output = NULL;
++ con_outputs[i].on = False;
++ if (con_outputs[i].smodes) {
++ free(con_outputs[i].smodes);
++ con_outputs[i].smodes = NULL;
++ }
++ con_outputs[i].nsmodes = 0;
++ }
++
++ do_init();
++ }
++ }
++
++ if (!do_not_switch)
++ if (!do_switch()) {
++ if ((ncon == 2) && (start == 4)) {
++ start = 5;
++ if (verbose)
++ fprintf(stderr, "too small screen, skipping side view\n");
++ (void) do_switch();
++ }
++ }
++
++ X_GETTIMEOFDAY(&time_val);
++ }
++ }
++}
++
+diff -urN old/dispswitch.man new/dispswitch.man
+--- dispswitch.man 1969-12-31 16:00:00.000000000 -0800
++++ dispswitch.man 2008-09-03 12:14:03.013107000 -0700
+@@ -0,0 +1,66 @@
++'\" t
++.\"
++.\" Copyright 2001 Keith Packard.\"
++.\" Copyright 2008 Sun Microsystems.\"
++.\" 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 Keith Packard not be used in
++.\" advertising or publicity pertaining to distribution of the software without
++.\" specific, written prior permission. Keith Packard makes no
++.\" representations about the suitability of this software for any purpose. It
++.\" is provided "as is" without express or implied warranty.
++.\"
++.\" KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
++.\" INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
++.\" EVENT SHALL KEITH PACKARD 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.
++.\"
++.\"
++.\" $XFree86: xc/programs/xrandr/dispswitch.man,v 1.1 2008/07/25 14:12:39 eich Exp $
++.\"
++.TH DISPSWITCH __appmansuffix__ __vendorversion__
++.SH NAME
++dispswitch \- Display Device Switch
++.SH SYNOPSIS
++.B "dispswitch"
++[\-display \fIdisplay\fP]
++[\-key \fIkeysym\fP]
++[\-mod \fImodifier\fP]
++[\-help]
++[\-verbose]
++[\-nosideview]
++[\-testrun]
++.SH DESCRIPTION
++.I Dispswitch
++uses hotkey to switch/rotate on/off of display devices up to 3 devices. Side-by-side views are included when two devices are connected. It works on systems where both server and driver support RandR version 1.2 (or above).
++User can define a hotkey with -key and -mod options (see below), the default is "Shift + F5".
++
++If a key stroke is hit 5 seconds or longer after the previous switch, display devices are re-probed so that any changes such as adding or removing devices, can be detected and adapted to.
++
++If a new display device is added whose mode size is larger than the one with which X server is started, or a side-by-side view is switched into that needs a larger mode to cover both screens, a config file specifying larger virtual screen size (Virtual field in Screen Section) will be needed during X server startup. Otherwise the larger mode will not be realized, or the side-by-side view will be skipped.
++
++.IP \-help
++Print out a summary of the usage and exit.
++.IP "\-verbose or -v"
++Print out debug messages when run.
++.IP \-nosideview
++Skip side-by-side views in two devices rotating.
++.IP \-testrun
++Switch/rotate display device states repeatedly without hotkey strokes.
++.IP "\-key <keysym> or -k <keysym>"
++Define the keysym of hotkey to be <keysym>, which can be any entry defined in
++defined in /usr/X11/include/X11/keysymdef.h, with prefix XK_ removed, such as F5, F8, Escape.
++.IP "\-mod <modifier> or -m <modifier>"
++Define the modifier of hotkey to be <modifier>. <modifier> is any or combination of modifiers on your system. Run /usr/X11/bin/xmodmap to find all available modifiers on your system. They can be none, shift, control, mod1, mod3, mod4, mod5, any.
++.SH
++Examples:
++
++dispswitch -key Escape -mod shift defines hotkey to be 'shift + Esc'
++.sp
++dispswitch -key F2 -mod "shift+control" defines hotkey to be 'shift + control + F2'
++
+diff -urN old/Makefile.am new/Makefile.am
+--- Makefile.am 2008-03-07 13:35:37.000000000 -0800
++++ Makefile.am 2008-08-07 18:31:32.391601000 -0700
+@@ -19,16 +19,21 @@
+ # TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ # PERFORMANCE OF THIS SOFTWARE.
+
+-bin_PROGRAMS = xrandr
++bin_PROGRAMS = xrandr dispswitch
+
+ AM_CFLAGS = $(XRANDR_CFLAGS)
+ xrandr_LDADD = $(XRANDR_LIBS)
++dispswitch_LDADD = $(XRANDR_LIBS)
+
+ xrandr_SOURCES = \
+ xrandr.c
+
++dispswitch_SOURCES = \
++ dispswitch.c
++
+ appman_PRE = \
+- xrandr.man
++ xrandr.man \
++ dispswitch.man
+
+ appmandir = $(APP_MAN_DIR)
+