open-src/app/dispswitch/sun-src/dispswitch.c
author X gate hg captive account <xhg@xserver.us.oracle.com>
Wed, 19 Aug 2015 13:48:41 -0700
changeset 1484 bc8922b8e1d2
parent 1459 f624eee89e0b
permissions -rw-r--r--
Added tag s11u3_30 for changeset 221e9d9cc9cb

/* 
 * 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.
 *
 */

/*
 * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE 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, win;
static int	screen;
static Bool	nosideview = False;
static Bool	verbose = False;
static Bool	testrun = False;
static int	had_error = 0;
static XErrorHandler	prev_handler;
static int	cur_keycode;
static struct timeval	time_val;
static Rotation	init_rotation;
static int 	init_x;
static int 	init_y;
static Bool	use_init_pos = False;
static Bool	need_off_deferred = False;

static void
usage(void)
{
    fprintf(stderr, "usage: %s [options]\n", program_name);
    fprintf(stderr, "  where options are:\n");
    fprintf(stderr, "  -toggle or -t\n");
    fprintf(stderr, "  -listen or -l\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
cur_handler (
    Display *const err_display,
    XErrorEvent *const err_event)
{
    had_error = err_event -> 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 (0x%lx) : 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 (0x%lx) : %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;
	}

	if (need_off_deferred)
	    /* Defer taking off */
	    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 0x%lx 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 Bool
valid_mode(con_output_t *con, XRRModeInfo *mode) {
    int i;

    for (i = 0; i < con->nsmodes; i++)
	if (con->smodes[i] == mode)
	    return True;

    return False;
}

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, " 0x%lx", con_outputs[j].output->output_info->crtcs[i]);
	    fprintf(stderr, ", using 0x%lx", 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, " 0x%lx", dis_con_outputs[j].output->output_info->crtcs[i]);
	    fprintf(stderr, ", using 0x%lx", 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 < 1) || (ncon > 3)) {
	if ((ncon < 1) && verbose)
	    fprintf (stderr, "warn: no connection\n");
	else if ((ncon > 3) && verbose)
	    fprintf (stderr, "warn: too many (more than 3) connections: %d: can't switch\n", ncon);
	do_not_switch = True;
    }

    did_init = True;

    return;
}

static int 
grab_key (Display *dpy, int keysym, unsigned int modifier, 
    Window grab_window)
{
    char    msg[256];
    int keycode = XKeysymToKeycode(dpy, keysym);

    if (keycode ==  NoSymbol)
	fatal ("grab_key: keycode not defined for keysym 0x%x\n", keysym);

    had_error = 0;
    prev_handler = XSetErrorHandler (cur_handler);

    if (!testrun) {
    	XGrabKey(dpy, 
	    keycode,
	    modifier,
	    root, True, GrabModeAsync, GrabModeAsync);
    	XSync (dpy, False);
    }

    XSetErrorHandler (prev_handler);
    if (had_error) {
	XGetErrorText (dpy, had_error, msg, sizeof (msg));
	fatal ("XGrabKey: %s\n", msg);
    }

    if (verbose)
	fprintf(stderr, "keycode to grab: %d\n", keycode);

    return keycode;
}

static Bool
do_switch (void)
{
    int 	i, j;
    int		single;
    int		save = -1;

    if (ncon <= 0)
	return True; 

    for (i = 0; i < ncon; i++) {
	output_t	*output = con_outputs[i].output;

	new_mode[i] = NULL;
	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] && valid_mode(&con_outputs[start -1], 
		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] && valid_mode(&con_outputs[i], 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]);
	    }
	}
    }

    if (ncon == 1) {
	if (start_mode[0] && valid_mode(&con_outputs[0], start_mode[0]))
	    new_mode[0] = start_mode[0]; 
	else {
	    if (con_outputs[0].smodes[0])
		new_mode[0] = get_largest_mode (&con_outputs[0],
		    con_outputs[0].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 ) {
	    if (!need_off_deferred) {
		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);
	    } else
		save = i;
	}
    }
  
    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();

    if (need_off_deferred && (save != -1)) {
	/* Now, take the deferred output off */
	output_t    *output;
	crtc_t      *crtc;
	Status      s;
	
	output = con_outputs[save].output;
	output->mode_info = NULL;
	con_outputs[save].on = False;
	if (verbose)
	    fprintf(stderr, "turn off output %d (%s) \n",
		save, con_outputs[save].output->output_info->name);
	
	crtc = output->crtc_info;
	s = crtc_disable (crtc);
	if (s != RRSetConfigSuccess)
	    panic (s, crtc);
    }

    XSync (dpy, False);

    did_init = False;

    return True;

}

static Bool 
do_toggle (void)
{
    Atom		atom;
    XEvent		xev;
    int			ret;

    atom = XInternAtom (dpy, "DISPLAYSWITCH_DAEMON", True);
    if (!atom) {
	fprintf(stderr, "dispswitch daemon not running\n");
	return False;
    }
	
    win = XGetSelectionOwner (dpy, atom);
    if (!win) {
	fprintf(stderr, "dispswitch: No owner of dispswitch daemon is found\n");
	return False;
    }

    bzero (&xev, sizeof (XEvent));
    xev.xkey.type = KeyPress;
    xev.xkey.send_event = True;
    xev.xkey.display = dpy;
    /* Any keycode */
    xev.xkey.keycode = 71;


    /* 
     * Send another instance of dispswitch (a daemon) an event to
     * request a switch
     */
    ret = XSendEvent(dpy, win, False, KeyPressMask, &xev);
    XFlush(dpy);

    if (!ret)
	fprintf(stderr, "dispswitch: XSendEvent error\n");

    return (!ret);
}

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    key_given = False;
    Bool    mod_given = False;
    int	    keysym = 0, toggle = False, listen = False;

    Atom    atom;

    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();
	    }
	    key_given = 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++;
		}
	    }
	    mod_given = 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;
        }
	if (!strcmp ("-toggle", argv[i]) || !strcmp ("-t", argv[i])) {
	    toggle = True;
	    continue;
        }
	if (!strcmp ("-listen", argv[i]) || !strcmp ("-l", argv[i])) {
	    listen = 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);


    if (toggle)
	exit (do_toggle());

    /* 
     * Create an atom, a trivial window, and make it selection owner. 
     * Ready to accept a client event request for switch
     */
    atom = XInternAtom(dpy, "DISPLAYSWITCH_DAEMON", False);
    if (!atom) {
	if (verbose)
	    fprintf(stderr, "cannot create Atom\n");
    }
    else {
	if (XGetSelectionOwner (dpy, atom)) {
	    if (verbose)
		fprintf(stderr, "dispswitch daemon is already running, quit\n");
	    exit (1);
	}
	win = XCreateSimpleWindow(dpy, root, 0, 0, 10, 10, 0, 10, 0);
	if (!win) {
	    if (verbose)
		fprintf(stderr, "cannot create window\n");
	}
	else {
	    XSetSelectionOwner(dpy, atom, win, CurrentTime);
	    if (XGetSelectionOwner(dpy, atom) != win) {
		if (verbose)
		    fprintf(stderr, "set selection owner failed\n");
	    } else
		XSelectInput(dpy, win, KeyPressMask);
	}
    }

    /* set default key and modifier if not given in command */
    if (!key_given)
	keysym  = XStringToKeysym ("F5");
    if (!mod_given)
	modifier = ShiftMask;

    if (!listen)
	cur_keycode = grab_key (dpy, keysym, modifier, root);

    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 crtcs: %d\n", res->ncrtc);

    do_init();

    X_GETTIMEOFDAY(&time_val);

    for(;;)
    {
	need_off_deferred = False;

	if (testrun) {
	    usleep(4000000);
	    fprintf(stderr, "\n");
	} else
	    XNextEvent(dpy, &ev);

	if (!listen && !testrun && (ev.type == MappingNotify) && 
	    ((ev.xmapping.request == MappingKeyboard) || 
	    (ev.xmapping.request == MappingModifier))) {
	    /* keyboard/modifier mapping changed */
	    if (verbose)
		fprintf(stderr, "\nkeyboard/modifier mapping changed ...\n");

	    XUngrabKey(dpy, cur_keycode, modifier, root);
	    cur_keycode = grab_key (dpy, keysym, modifier, root);
	}

	if (testrun || (ev.type == KeyPress)) {
	    if (verbose)
		fprintf(stderr, "\na key press event was grabbed ...\n");

	    do_not_switch = False;

	    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();
		} else if (ncon == 1)
		    do_not_switch = True;
	    } else if (ncon == 1)
		do_not_switch = True;

    	    if (!do_not_switch) {
		if ((ncon == 2) && (start == 1))
		    /* 
		     * Workaround for intel based graphics: in switching from 
		     * LVDS to VGA, off on LVDS needs to be deferred.
		     */
		    need_off_deferred = True;
	    	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);
	}
    }
}