649N/A/*
649N/A * Copyright © 2001 Keith Packard, member of The XFree86 Project, Inc.
649N/A * Copyright © 2002 Hewlett Packard Company, Inc.
649N/A * Copyright © 2006 Intel Corporation
649N/A *
649N/A * Permission to use, copy, modify, distribute, and sell this software and its
649N/A * documentation for any purpose is hereby granted without fee, provided that
649N/A * the above copyright notice appear in all copies and that both that copyright
649N/A * notice and this permission notice appear in supporting documentation, and
649N/A * that the name of the copyright holders not be used in advertising or
649N/A * publicity pertaining to distribution of the software without specific,
649N/A * written prior permission. The copyright holders make no representations
649N/A * about the suitability of this software for any purpose. It is provided "as
649N/A * is" without express or implied warranty.
649N/A *
649N/A * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
649N/A * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
649N/A * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
649N/A * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
649N/A * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
649N/A * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
649N/A * OF THIS SOFTWARE.
649N/A *
649N/A */
649N/A
919N/A/*
1459N/A * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
919N/A *
919N/A * Permission is hereby granted, free of charge, to any person obtaining a
919N/A * copy of this software and associated documentation files (the "Software"),
919N/A * to deal in the Software without restriction, including without limitation
919N/A * the rights to use, copy, modify, merge, publish, distribute, sublicense,
919N/A * and/or sell copies of the Software, and to permit persons to whom the
919N/A * Software is furnished to do so, subject to the following conditions:
919N/A *
919N/A * The above copyright notice and this permission notice (including the next
919N/A * paragraph) shall be included in all copies or substantial portions of the
919N/A * Software.
919N/A *
919N/A * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
919N/A * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
919N/A * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
919N/A * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
919N/A * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
919N/A * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
919N/A * DEALINGS IN THE SOFTWARE.
919N/A */
919N/A
919N/A
649N/A#include <stdio.h>
649N/A#include <X11/Xlib.h>
649N/A#include <X11/Xlibint.h>
649N/A#include <X11/Xproto.h>
649N/A#include <X11/extensions/Xrandr.h>
649N/A#include <X11/Xos.h>
649N/A#include <string.h>
649N/A#include <stdlib.h>
649N/A#include <stdarg.h>
649N/A#include <fcntl.h>
649N/A#include <signal.h>
649N/A#include <sys/proc.h>
649N/A#include <unistd.h>
649N/A
649N/A
649N/Astatic char *program_name;
649N/Astatic Display *dpy = NULL;
765N/Astatic Window root, win;
649N/Astatic int screen;
649N/Astatic Bool nosideview = False;
649N/Astatic Bool verbose = False;
649N/Astatic Bool testrun = False;
649N/Astatic int had_error = 0;
649N/Astatic XErrorHandler prev_handler;
649N/Astatic int cur_keycode;
649N/Astatic struct timeval time_val;
649N/Astatic Rotation init_rotation;
649N/Astatic int init_x;
649N/Astatic int init_y;
649N/Astatic Bool use_init_pos = False;
1323N/Astatic Bool need_off_deferred = False;
649N/A
649N/Astatic void
649N/Ausage(void)
649N/A{
649N/A fprintf(stderr, "usage: %s [options]\n", program_name);
649N/A fprintf(stderr, " where options are:\n");
765N/A fprintf(stderr, " -toggle or -t\n");
765N/A fprintf(stderr, " -listen or -l\n");
649N/A fprintf(stderr, " -display <display> or -d <display>\n");
649N/A fprintf(stderr, " -key <key> or -k <key>\n");
649N/A fprintf(stderr, " -mod <modifier> or -m <modifier>\n");
649N/A fprintf(stderr, " -help\n");
649N/A fprintf(stderr, " -nosideview\n");
649N/A fprintf(stderr, " -verbose or -v\n");
649N/A fprintf(stderr, " -testrun\n");
649N/A exit(1);
649N/A /*NOTREACHED*/
649N/A}
649N/A
649N/Astatic void
649N/Afatal (const char *format, ...)
649N/A{
649N/A va_list ap;
649N/A
649N/A va_start (ap, format);
649N/A fprintf (stderr, "%s exiting: ", program_name);
649N/A vfprintf (stderr, format, ap);
649N/A va_end (ap);
649N/A exit (1);
649N/A /*NOTREACHED*/
649N/A}
649N/A
649N/Astatic int
649N/Acur_handler (
649N/A Display *const err_display,
649N/A XErrorEvent *const err_event)
649N/A{
649N/A had_error = err_event -> error_code;
649N/A return (0);
649N/A}
649N/A
649N/Atypedef enum _relation {
649N/A left_of, right_of, above, below, same_as,
649N/A} relation_t;
649N/A
649N/Atypedef enum _changes {
649N/A changes_none = 0,
649N/A changes_crtc = (1 << 0),
649N/A changes_mode = (1 << 1),
649N/A changes_relation = (1 << 2),
649N/A changes_position = (1 << 3),
649N/A} changes_t;
649N/A
649N/Atypedef enum _name_kind {
649N/A name_none = 0,
649N/A name_string = (1 << 0),
649N/A name_xid = (1 << 1),
649N/A name_index = (1 << 2),
649N/A} name_kind_t;
649N/A
649N/Atypedef struct {
649N/A name_kind_t kind;
649N/A char *string;
649N/A XID xid;
649N/A int index;
649N/A} name_t;
649N/A
649N/Atypedef struct _crtc crtc_t;
649N/Atypedef struct _output output_t;
649N/A
649N/Astruct _crtc {
649N/A name_t crtc;
649N/A XRRCrtcInfo *crtc_info;
649N/A
649N/A XRRModeInfo *mode_info;
649N/A int x;
649N/A int y;
649N/A Rotation rotation;
649N/A output_t **outputs;
649N/A int noutput;
649N/A};
649N/A
649N/Astruct _output {
649N/A struct _output *next;
649N/A
649N/A changes_t changes;
649N/A
649N/A name_t output;
649N/A XRROutputInfo *output_info;
649N/A
649N/A name_t crtc;
649N/A crtc_t *crtc_info;
649N/A crtc_t *current_crtc_info;
649N/A
649N/A name_t mode;
649N/A float refresh;
649N/A XRRModeInfo *mode_info;
649N/A
649N/A name_t addmode;
649N/A
649N/A relation_t relation;
649N/A struct _output *relative_to;
649N/A
649N/A int x, y;
649N/A Rotation rotation;
649N/A};
649N/A
649N/Astatic output_t *outputs = NULL;
649N/Astatic output_t **outputs_tail = &outputs;
649N/Astatic crtc_t *crtcs = NULL;
649N/Astatic int num_crtcs;
649N/Astatic XRRScreenResources *res = NULL;
649N/Astatic int fb_width = 0, fb_height = 0;
649N/Astatic int fb_width_mm = 0, fb_height_mm = 0;
649N/Astatic float dpi = 0.0;
649N/Astatic Bool dryrun = False;
649N/Astatic int minWidth, maxWidth, minHeight, maxHeight;
649N/A
649N/A#define MAX_OUTPUT 3
649N/A#define MAX_MODIFIERS 10
649N/A
649N/Atypedef struct _con_output con_output_t;
649N/A
649N/Astruct _con_output {
649N/A output_t *output;
649N/A XRRModeInfo **smodes;
649N/A int nsmodes;
649N/A Bool on;
649N/A};
649N/A
649N/Atypedef struct _mod_key_table mod_key_table_t;
649N/A
649N/Astruct _mod_key_table {
649N/A char *modname;
649N/A unsigned int mod;
649N/A};
649N/A
649N/Astatic con_output_t con_outputs[MAX_OUTPUT];
649N/Astatic con_output_t dis_con_outputs[MAX_OUTPUT];
649N/Astatic XRRModeInfo *start_mode[MAX_OUTPUT];
649N/Astatic XRRModeInfo *new_mode[MAX_OUTPUT];
649N/Astatic int start = 0;
649N/Astatic int ncon;
649N/Astatic int dis_ncon;
649N/Astatic Bool do_not_switch = False;
649N/Astatic Bool did_init = False;
649N/A
649N/Astatic mod_key_table_t mod_key_table [MAX_MODIFIERS] = {
649N/A {"none", 0},
649N/A {"shift", ShiftMask},
649N/A {"lock", LockMask},
649N/A {"control", ControlMask},
649N/A {"mod1", Mod1Mask},
649N/A {"mod2", Mod2Mask},
649N/A {"mod3", Mod3Mask},
649N/A {"mod4", Mod4Mask},
649N/A {"mod5", Mod5Mask},
649N/A {"any", AnyModifier}
649N/A};
649N/A
649N/Astatic int
649N/Amode_height (XRRModeInfo *mode_info, Rotation rotation)
649N/A{
649N/A switch (rotation & 0xf) {
649N/A case RR_Rotate_0:
649N/A case RR_Rotate_180:
649N/A return mode_info->height;
649N/A case RR_Rotate_90:
649N/A case RR_Rotate_270:
649N/A return mode_info->width;
649N/A default:
649N/A return 0;
649N/A }
649N/A}
649N/A
649N/Astatic int
649N/Amode_width (XRRModeInfo *mode_info, Rotation rotation)
649N/A{
649N/A switch (rotation & 0xf) {
649N/A case RR_Rotate_0:
649N/A case RR_Rotate_180:
649N/A return mode_info->width;
649N/A case RR_Rotate_90:
649N/A case RR_Rotate_270:
649N/A return mode_info->height;
649N/A default:
649N/A return 0;
649N/A }
649N/A}
649N/A
649N/A/* v refresh frequency in Hz */
649N/Astatic float
649N/Amode_refresh (XRRModeInfo *mode_info)
649N/A{
649N/A float rate;
649N/A
649N/A if (mode_info->hTotal && mode_info->vTotal)
649N/A rate = ((float) mode_info->dotClock /
649N/A ((float) mode_info->hTotal * (float) mode_info->vTotal));
649N/A else
649N/A rate = 0;
649N/A return rate;
649N/A}
649N/A
649N/A
649N/Astatic void
649N/Ainit_name (name_t *name)
649N/A{
649N/A name->kind = name_none;
649N/A}
649N/A
649N/Astatic void
649N/Aset_name_string (name_t *name, char *string)
649N/A{
649N/A name->kind |= name_string;
649N/A name->string = string;
649N/A}
649N/A
649N/Astatic void
649N/Aset_name_xid (name_t *name, XID xid)
649N/A{
649N/A name->kind |= name_xid;
649N/A name->xid = xid;
649N/A}
649N/A
649N/Astatic void
649N/Aset_name_index (name_t *name, int index)
649N/A{
649N/A name->kind |= name_index;
649N/A name->index = index;
649N/A}
649N/A
649N/Astatic void
649N/Aset_name_all (name_t *name, name_t *old)
649N/A{
649N/A if (old->kind & name_xid)
649N/A name->xid = old->xid;
649N/A if (old->kind & name_string)
649N/A name->string = old->string;
649N/A if (old->kind & name_index)
649N/A name->index = old->index;
649N/A name->kind |= old->kind;
649N/A}
649N/A
649N/Astatic output_t *
649N/Aadd_output (void)
649N/A{
649N/A output_t *output = calloc (1, sizeof (output_t));
649N/A
649N/A if (!output)
649N/A fatal ("out of memory");
649N/A output->next = NULL;
649N/A *outputs_tail = output;
649N/A outputs_tail = &output->next;
649N/A return output;
649N/A}
649N/A
649N/Astatic output_t *
649N/Afind_output (name_t *name)
649N/A{
649N/A output_t *output;
649N/A
649N/A for (output = outputs; output; output = output->next)
649N/A {
649N/A name_kind_t common = name->kind & output->output.kind;
649N/A
649N/A if ((common & name_xid) && name->xid == output->output.xid)
649N/A break;
649N/A if ((common & name_string) && !strcmp (name->string, output->output.string))
649N/A break;
649N/A if ((common & name_index) && name->index == output->output.index)
649N/A break;
649N/A }
649N/A return output;
649N/A}
649N/A
649N/Astatic output_t *
649N/Afind_output_by_xid (RROutput output)
649N/A{
649N/A name_t output_name;
649N/A
649N/A init_name (&output_name);
649N/A set_name_xid (&output_name, output);
649N/A return find_output (&output_name);
649N/A}
649N/A
649N/Astatic crtc_t *
649N/Afind_crtc (name_t *name)
649N/A{
649N/A int c;
649N/A crtc_t *crtc = NULL;
649N/A
649N/A for (c = 0; c < num_crtcs; c++)
649N/A {
649N/A name_kind_t common;
649N/A
649N/A crtc = &crtcs[c];
649N/A common = name->kind & crtc->crtc.kind;
649N/A
649N/A if ((common & name_xid) && name->xid == crtc->crtc.xid)
649N/A break;
649N/A if ((common & name_string) && !strcmp (name->string, crtc->crtc.string))
649N/A break;
649N/A if ((common & name_index) && name->index == crtc->crtc.index)
649N/A break;
649N/A crtc = NULL;
649N/A }
649N/A return crtc;
649N/A}
649N/A
649N/Astatic crtc_t *
649N/Afind_crtc_by_xid (RRCrtc crtc)
649N/A{
649N/A name_t crtc_name;
649N/A
649N/A init_name (&crtc_name);
649N/A set_name_xid (&crtc_name, crtc);
649N/A return find_crtc (&crtc_name);
649N/A}
649N/A
649N/Astatic XRRModeInfo *
649N/Afind_mode (name_t *name)
649N/A{
649N/A int m;
649N/A XRRModeInfo *best = NULL;
649N/A
649N/A for (m = 0; m < res->nmode; m++)
649N/A {
649N/A XRRModeInfo *mode = &res->modes[m];
649N/A if ((name->kind & name_xid) && name->xid == mode->id)
649N/A {
649N/A best = mode;
649N/A break;
649N/A }
649N/A }
649N/A return best;
649N/A}
649N/A
649N/Astatic XRRModeInfo *
649N/Afind_mode_by_xid (RRMode mode)
649N/A{
649N/A name_t mode_name;
649N/A
649N/A init_name (&mode_name);
649N/A set_name_xid (&mode_name, mode);
649N/A return find_mode (&mode_name);
649N/A}
649N/A
649N/Astatic void
649N/Aset_output_info (output_t *output, RROutput xid, XRROutputInfo *output_info)
649N/A{
649N/A crtc_t *crtc;
649N/A
649N/A /* sanity check output info */
649N/A if (output_info->connection != RR_Disconnected && !output_info->nmode)
649N/A fatal ("Output %s is not disconnected but has no modes\n",
649N/A output_info->name);
649N/A
649N/A /* set output name and info */
649N/A if (!(output->output.kind & name_xid))
649N/A set_name_xid (&output->output, xid);
649N/A if (!(output->output.kind & name_string))
649N/A set_name_string (&output->output, output_info->name);
649N/A output->output_info = output_info;
649N/A
649N/A crtc = find_crtc_by_xid (output->output_info->crtc);
649N/A /* set position */
649N/A if (crtc && crtc->crtc_info) {
649N/A output->x = crtc->crtc_info->x;
649N/A output->y = crtc->crtc_info->y;
649N/A }
649N/A
649N/A /* set rotation */
649N/A output->rotation &= ~0xf;
649N/A if (crtc && crtc->crtc_info)
649N/A output->rotation |= (crtc->crtc_info->rotation & 0xf);
649N/A else
649N/A output->rotation = RR_Rotate_0;
649N/A
649N/A}
649N/A
649N/Astatic void
649N/Aget_crtcs (void)
649N/A{
649N/A int c;
649N/A
649N/A num_crtcs = res->ncrtc;
649N/A if (crtcs)
649N/A {
649N/A for (c = 0; c < res->ncrtc; c++)
649N/A {
649N/A if (crtcs[c].crtc_info)
649N/A XRRFreeCrtcInfo (crtcs[c].crtc_info);
649N/A }
649N/A free (crtcs);
649N/A crtcs = NULL;
649N/A }
649N/A
649N/A crtcs = calloc (num_crtcs, sizeof (crtc_t));
649N/A if (!crtcs) fatal ("out of memory");
649N/A
649N/A for (c = 0; c < res->ncrtc; c++)
649N/A {
649N/A XRRCrtcInfo *crtc_info = XRRGetCrtcInfo (dpy, res, res->crtcs[c]);
649N/A set_name_xid (&crtcs[c].crtc, res->crtcs[c]);
649N/A set_name_index (&crtcs[c].crtc, c);
649N/A if (!crtc_info) fatal ("could not get crtc 0x%x information", res->crtcs[c]);
649N/A crtcs[c].crtc_info = crtc_info;
649N/A if (crtc_info->mode == None)
649N/A {
649N/A crtcs[c].mode_info = NULL;
649N/A crtcs[c].x = 0;
649N/A crtcs[c].y = 0;
649N/A crtcs[c].rotation = RR_Rotate_0;
649N/A }
649N/A }
649N/A}
649N/A
649N/Astatic void
649N/Acrtc_add_output (crtc_t *crtc, output_t *output)
649N/A{
649N/A if (crtc->outputs)
649N/A crtc->outputs = realloc (crtc->outputs, (crtc->noutput + 1) * sizeof (output_t *));
649N/A else
649N/A {
649N/A crtc->outputs = calloc (1, sizeof (output_t *));
649N/A crtc->x = output->x;
649N/A crtc->y = output->y;
649N/A crtc->rotation = output->rotation;
649N/A crtc->mode_info = output->mode_info;
649N/A }
649N/A if (!crtc->outputs) fatal ("out of memory");
649N/A crtc->outputs[crtc->noutput++] = output;
649N/A}
649N/A
649N/Astatic void
649N/Aset_crtcs (void)
649N/A{
649N/A output_t *output;
649N/A int i;
649N/A
649N/A for (i = 0; i < ncon; i++) {
649N/A output = con_outputs[i].output;
649N/A if (!output->mode_info) continue;
649N/A if (output->crtc_info)
649N/A crtc_add_output (output->crtc_info, output);
649N/A }
649N/A}
649N/A
649N/Astatic void
649N/Areset_crtcs_for_outputs (void)
649N/A{
649N/A output_t *output;
649N/A
649N/A for (output = outputs; output; output = output->next)
649N/A {
649N/A if ((output->crtc_info) && (output->crtc_info->outputs)) {
649N/A free (output->crtc_info->outputs);
649N/A output->crtc_info = NULL;
649N/A }
649N/A }
649N/A}
649N/A
649N/Astatic Status
649N/Acrtc_disable (crtc_t *crtc)
649N/A{
649N/A if (verbose)
1459N/A fprintf (stderr, "crtc %d (0x%lx) : disable\n", crtc->crtc.index, crtc->crtc.xid);
649N/A
649N/A if (dryrun)
649N/A return RRSetConfigSuccess;
649N/A
649N/A return XRRSetCrtcConfig (dpy, res, crtc->crtc.xid, CurrentTime,
649N/A 0, 0, None, RR_Rotate_0, NULL, 0);
649N/A}
649N/A
649N/Astatic Status
649N/Acrtc_revert (crtc_t *crtc)
649N/A{
649N/A XRRCrtcInfo *crtc_info = crtc->crtc_info;
649N/A
649N/A if (verbose)
649N/A fprintf (stderr, "crtc %d: revert\n", crtc->crtc.index);
649N/A
649N/A if (dryrun)
649N/A return RRSetConfigSuccess;
649N/A return XRRSetCrtcConfig (dpy, res, crtc->crtc.xid, CurrentTime,
649N/A crtc_info->x, crtc_info->y,
649N/A crtc_info->mode, crtc_info->rotation,
649N/A crtc_info->outputs, crtc_info->noutput);
649N/A}
649N/A
649N/Astatic Status
649N/Acrtc_apply (crtc_t *crtc)
649N/A{
649N/A RROutput *rr_outputs;
649N/A int o;
649N/A Status s;
649N/A RRMode mode = None;
649N/A
649N/A if (!crtc->mode_info)
649N/A return RRSetConfigSuccess;
649N/A
649N/A rr_outputs = calloc (crtc->noutput, sizeof (RROutput));
649N/A if (!rr_outputs)
649N/A return BadAlloc;
649N/A for (o = 0; o < crtc->noutput; o++)
649N/A rr_outputs[o] = crtc->outputs[o]->output.xid;
649N/A mode = crtc->mode_info->id;
649N/A if (verbose) {
1459N/A fprintf (stderr, "crtc %d (0x%lx) : %12s %6.1f +%d+%d", crtc->crtc.index,
649N/A crtc->crtc.xid,
649N/A crtc->mode_info->name, mode_refresh (crtc->mode_info),
649N/A crtc->x, crtc->y);
649N/A for (o = 0; o < crtc->noutput; o++)
649N/A fprintf (stderr, " \"%s\"", crtc->outputs[o]->output.string);
649N/A fprintf (stderr, "\n");
649N/A }
649N/A if (dryrun)
649N/A s = RRSetConfigSuccess;
649N/A else
649N/A s = XRRSetCrtcConfig (dpy, res, crtc->crtc.xid, CurrentTime,
649N/A crtc->x, crtc->y, mode, crtc->rotation,
649N/A rr_outputs, crtc->noutput);
649N/A free (rr_outputs);
649N/A return s;
649N/A}
649N/A
649N/Astatic void
649N/Ascreen_revert (void)
649N/A{
649N/A if (verbose)
649N/A fprintf (stderr, "screen %d: revert\n", screen);
649N/A
649N/A if (dryrun)
649N/A return;
649N/A XRRSetScreenSize (dpy, root,
649N/A DisplayWidth (dpy, screen),
649N/A DisplayHeight (dpy, screen),
649N/A DisplayWidthMM (dpy, screen),
649N/A DisplayHeightMM (dpy, screen));
649N/A}
649N/A
649N/Astatic void
649N/Ascreen_apply (void)
649N/A{
649N/A /* comment it out because DisplayWidth() does not reflect the
649N/A change of fb_width and fb_height previously set by
649N/A XRRSetScreenSize()
649N/A */
649N/A /*
649N/A if (fb_width == DisplayWidth (dpy, screen) &&
649N/A fb_height == DisplayHeight (dpy, screen) &&
649N/A fb_width_mm == DisplayWidthMM (dpy, screen) &&
649N/A fb_height_mm == DisplayHeightMM (dpy, screen))
649N/A {
649N/A return;
649N/A }
649N/A */
649N/A
649N/A if (verbose)
649N/A fprintf (stderr, "screen %d: %dx%d %dx%d mm %6.2fdpi\n", screen,
649N/A fb_width, fb_height, fb_width_mm, fb_height_mm, dpi);
649N/A if (dryrun)
649N/A return;
649N/A XRRSetScreenSize (dpy, root, fb_width, fb_height,
649N/A fb_width_mm, fb_height_mm);
649N/A
649N/A}
649N/A
649N/Astatic void
649N/Arevert (void)
649N/A{
649N/A int c;
649N/A
649N/A /* first disable all crtcs */
649N/A for (c = 0; c < res->ncrtc; c++)
649N/A crtc_disable (&crtcs[c]);
649N/A /* next reset screen size */
649N/A screen_revert ();
649N/A /* now restore all crtcs */
649N/A for (c = 0; c < res->ncrtc; c++)
649N/A crtc_revert (&crtcs[c]);
649N/A}
649N/A
649N/A/*
649N/A * uh-oh, something bad happened in the middle of changing
649N/A * the configuration. Revert to the previous configuration
649N/A * and bail
649N/A */
649N/Astatic void
649N/Apanic (Status s, crtc_t *crtc)
649N/A{
649N/A int c = crtc->crtc.index;
649N/A char *message;
649N/A
649N/A switch (s) {
649N/A case RRSetConfigSuccess: message = "succeeded"; break;
649N/A case BadAlloc: message = "out of memory"; break;
649N/A case RRSetConfigFailed: message = "failed"; break;
649N/A case RRSetConfigInvalidConfigTime: message = "invalid config time"; break;
649N/A case RRSetConfigInvalidTime: message = "invalid time"; break;
649N/A default: message = "unknown failure"; break;
649N/A }
649N/A
649N/A fprintf (stderr, "%s: Configure crtc %d %s\n", program_name, c, message);
649N/A revert ();
649N/A exit (1);
649N/A}
649N/A
649N/Astatic void
649N/Aapply (void)
649N/A{
649N/A Status s;
649N/A int c;
649N/A
649N/A /*
649N/A * Turn off any crtcs which are to be disabled or which are
649N/A * larger than the target size
649N/A */
649N/A for (c = 0; c < res->ncrtc; c++)
649N/A {
649N/A crtc_t *crtc = &crtcs[c];
649N/A XRRCrtcInfo *crtc_info = crtc->crtc_info;
649N/A
649N/A /*
649N/A * if this crtc is already disabled, skip it
649N/A * Note server sets crtc_info->mode (before change)
649N/A */
649N/A if (crtc_info->mode == None)
649N/A continue;
649N/A
649N/A /*
649N/A * If this crtc is to be left enabled, make
649N/A * sure the old size fits then new screen
649N/A * When crtc->mode_info is null, the crtc is to be
649N/A * disabled. Note set_crtcs () sets crtc->mode_info for
649N/A * new mode (to be changed to).
649N/A */
649N/A if (crtc->mode_info)
649N/A {
649N/A XRRModeInfo *old_mode = find_mode_by_xid (crtc_info->mode);
649N/A int x, y, w, h;
649N/A
649N/A if (!old_mode)
649N/A panic (RRSetConfigFailed, crtc);
649N/A
649N/A /* old position and size information */
649N/A x = crtc_info->x;
649N/A y = crtc_info->y;
649N/A w = mode_width (old_mode, crtc_info->rotation);
649N/A h = mode_height (old_mode, crtc_info->rotation);
649N/A
649N/A /* if it fits, skip it */
649N/A if (x + w <= fb_width && y + h <= fb_height)
649N/A continue;
649N/A }
1323N/A
1323N/A if (need_off_deferred)
1323N/A /* Defer taking off */
1323N/A continue;
1323N/A
649N/A s = crtc_disable (crtc);
649N/A if (s != RRSetConfigSuccess)
649N/A panic (s, crtc);
649N/A }
649N/A
649N/A /*
649N/A * Hold the server grabbed while messing with
649N/A * the screen so that apps which notice the resize
649N/A * event and ask for xinerama information from the server
649N/A * receive up-to-date information
649N/A */
649N/A XGrabServer (dpy);
649N/A
649N/A /*
649N/A * Set the screen size
649N/A */
649N/A screen_apply ();
649N/A
649N/A /*
649N/A * Set crtcs
649N/A */
649N/A
649N/A for (c = 0; c < res->ncrtc; c++)
649N/A {
649N/A crtc_t *crtc = &crtcs[c];
649N/A
649N/A s = crtc_apply (crtc);
649N/A if (s != RRSetConfigSuccess)
649N/A panic (s, crtc);
649N/A }
649N/A /*
649N/A * Release the server grab and let all clients
649N/A * respond to the updated state
649N/A */
649N/A XUngrabServer (dpy);
649N/A}
649N/A
649N/A/*
649N/A * Use current output state to complete the output list
649N/A */
649N/Astatic void
649N/Aget_outputs (void)
649N/A{
649N/A int o;
649N/A
649N/A for (o = 0; o < res->noutput; o++)
649N/A {
649N/A XRROutputInfo *output_info = XRRGetOutputInfo (dpy, res, res->outputs[o]);
649N/A output_t *output;
649N/A name_t output_name;
649N/A
649N/A if (!output_info) fatal ("could not get output 0x%x information", res->outputs[o]);
649N/A set_name_xid (&output_name, res->outputs[o]);
649N/A set_name_index (&output_name, o);
649N/A set_name_string (&output_name, output_info->name);
649N/A output = find_output (&output_name);
649N/A if (!output)
649N/A {
649N/A output = add_output ();
649N/A set_name_all (&output->output, &output_name);
649N/A }
649N/A
649N/A set_output_info (output, res->outputs[o], output_info);
649N/A }
649N/A}
649N/A
649N/A
649N/A/*
649N/A * Test whether 'crtc' can be used for 'output'
649N/A */
649N/Astatic Bool
649N/Acheck_crtc_for_output (crtc_t *crtc, output_t *output)
649N/A{
649N/A int i;
649N/A int l;
649N/A output_t *other;
649N/A
649N/A for (i = 0; i < ncon; i++)
649N/A {
649N/A other = con_outputs[i].output;
649N/A
649N/A if (other == output)
649N/A continue;
649N/A
649N/A if (other->mode_info == NULL)
649N/A continue;
649N/A
649N/A if (other->crtc_info != crtc)
649N/A continue;
649N/A
649N/A /* see if the output connected to the crtc can clone to this output */
649N/A for (l = 0; l < output->output_info->nclone; l++)
649N/A if (output->output_info->clones[l] == other->output.xid)
649N/A break;
649N/A /* not on the list, can't clone */
649N/A if (l == output->output_info->nclone)
649N/A return False;
649N/A }
649N/A
649N/A if (crtc->noutput)
649N/A {
649N/A for (i = 0; i < crtc->noutput; i++)
649N/A /* Check if the output is to be turned on */
649N/A if (crtc->outputs[i]->mode_info) {
649N/A /* make sure the state matches */
649N/A if (crtc->mode_info != output->mode_info)
649N/A return False;
649N/A if (crtc->x != output->x)
649N/A return False;
649N/A if (crtc->y != output->y)
649N/A return False;
649N/A if (crtc->rotation != output->rotation)
649N/A return False;
649N/A }
649N/A }
649N/A return True;
649N/A}
649N/A
649N/Astatic crtc_t *
649N/Afind_crtc_for_output (output_t *output)
649N/A{
649N/A int c;
649N/A
649N/A for (c = 0; c < output->output_info->ncrtc; c++)
649N/A {
649N/A crtc_t *crtc;
649N/A
649N/A crtc = find_crtc_by_xid (output->output_info->crtcs[c]);
649N/A if (!crtc) fatal ("cannot find crtc 0x%x\n", output->output_info->crtcs[c]);
649N/A
649N/A if (check_crtc_for_output (crtc, output))
649N/A return crtc;
649N/A }
649N/A return NULL;
649N/A}
649N/A
649N/Astatic void
649N/Aset_positions (void)
649N/A{
649N/A Bool keep_going;
649N/A Bool any_set;
649N/A int min_x, min_y;
649N/A int i;
649N/A
649N/A for (;;)
649N/A {
649N/A any_set = False;
649N/A keep_going = False;
649N/A for (i = 0; i < ncon; i++)
649N/A {
649N/A output_t *output = con_outputs[i].output;
649N/A output_t *relation;
649N/A
649N/A if (!(output->changes & changes_relation)) continue;
649N/A
649N/A if (output->mode_info == NULL) continue;
649N/A
649N/A relation = output->relative_to;
649N/A
649N/A if (relation->mode_info == NULL)
649N/A {
649N/A output->x = 0;
649N/A output->y = 0;
649N/A output->changes |= changes_position;
649N/A any_set = True;
649N/A continue;
649N/A }
649N/A /*
649N/A * Make sure the dependent object has been set in place
649N/A */
649N/A if ((relation->changes & changes_relation) &&
649N/A !(relation->changes & changes_position))
649N/A {
649N/A keep_going = True;
649N/A continue;
649N/A }
649N/A
649N/A switch (output->relation) {
649N/A case left_of:
649N/A output->y = relation->y;
649N/A output->x = relation->x - mode_width (output->mode_info, output->rotation);
649N/A break;
649N/A case right_of:
649N/A output->y = relation->y;
649N/A output->x = relation->x + mode_width (relation->mode_info, relation->rotation);
649N/A break;
649N/A case above:
649N/A output->x = relation->x;
649N/A output->y = relation->y - mode_height (output->mode_info, output->rotation);
649N/A break;
649N/A case below:
649N/A output->x = relation->x;
649N/A output->y = relation->y + mode_height (relation->mode_info, relation->rotation);
649N/A break;
649N/A case same_as:
649N/A output->x = relation->x;
649N/A output->y = relation->y;
649N/A }
649N/A output->changes |= changes_position;
649N/A relation->changes |= changes_position;
649N/A any_set = True;
649N/A }
649N/A if (!keep_going)
649N/A break;
649N/A if (!any_set)
649N/A fatal ("loop in relative position specifications\n");
649N/A }
649N/A
649N/A /*
649N/A * Now normalize positions so the upper left corner of all outputs is at 0,0
649N/A */
649N/A min_x = 32768;
649N/A min_y = 32768;
649N/A for (i = 0; i < ncon; i++)
649N/A {
649N/A output_t *output = con_outputs[i].output;
649N/A
649N/A if (output->mode_info == NULL) continue;
649N/A
649N/A if (output->x < min_x) min_x = output->x;
649N/A if (output->y < min_y) min_y = output->y;
649N/A }
649N/A if (min_x || min_y)
649N/A {
649N/A /* move all outputs */
649N/A for (i = 0; i < ncon; i++)
649N/A {
649N/A output_t *output = con_outputs[i].output;
649N/A
649N/A if (output->mode_info == NULL) continue;
649N/A
649N/A output->x -= min_x;
649N/A output->y -= min_y;
649N/A output->changes |= changes_position;
649N/A }
649N/A }
649N/A}
649N/A
649N/Astatic Bool
649N/Aset_screen_size (void)
649N/A{
649N/A int i;
649N/A
649N/A fb_width = fb_height = 0;
649N/A
649N/A for (i = 0; i < ncon; i++)
649N/A {
649N/A output_t *output = con_outputs[i].output;
649N/A XRRModeInfo *mode_info = output->mode_info;
649N/A int x, y, w, h;
649N/A
649N/A if (!mode_info) continue;
649N/A
649N/A x = output->x;
649N/A y = output->y;
649N/A w = mode_width (mode_info, output->rotation);
649N/A h = mode_height (mode_info, output->rotation);
649N/A if (x + w > fb_width) fb_width = x + w;
649N/A if (y + h > fb_height) fb_height = y + h;
649N/A }
649N/A
649N/A if (fb_width > maxWidth || fb_height > maxHeight) {
649N/A if (verbose)
649N/A fprintf (stderr, "screen cannot be larger than %dx%d (desired size %dx%d)\n",
649N/A maxWidth, maxHeight, fb_width, fb_height);
649N/A return False;
649N/A }
649N/A
649N/A if (fb_width < minWidth) fb_width = minWidth;
649N/A if (fb_height < minHeight) fb_height = minHeight;
649N/A
649N/A return True;
649N/A}
649N/A
649N/Astatic void
649N/Adisable_outputs (output_t *outputs)
649N/A{
649N/A while (outputs)
649N/A {
649N/A outputs->crtc_info = NULL;
649N/A outputs = outputs->next;
649N/A }
649N/A}
649N/A
649N/A/*
649N/A * find the best mapping from output to crtc available
649N/A */
649N/Astatic int
649N/Apick_crtcs_score (output_t *outputs)
649N/A{
649N/A output_t *output;
649N/A int best_score;
649N/A int my_score;
649N/A int score;
649N/A crtc_t *best_crtc;
649N/A int c;
649N/A
649N/A if (!outputs)
649N/A return 0;
649N/A
649N/A output = outputs;
649N/A outputs = outputs->next;
649N/A /*
649N/A * Score with this output disabled
649N/A */
649N/A output->crtc_info = NULL;
649N/A best_score = pick_crtcs_score (outputs);
649N/A if (output->mode_info == NULL)
649N/A return best_score;
649N/A
649N/A best_crtc = NULL;
649N/A /*
649N/A * Now score with this output any valid crtc
649N/A */
649N/A for (c = 0; c < output->output_info->ncrtc; c++)
649N/A {
649N/A crtc_t *crtc;
649N/A
649N/A crtc = find_crtc_by_xid (output->output_info->crtcs[c]);
649N/A if (!crtc)
649N/A fatal ("cannot find crtc 0x%x\n", output->output_info->crtcs[c]);
649N/A
649N/A /* reset crtc allocation for following outputs */
649N/A disable_outputs (outputs);
649N/A if (!check_crtc_for_output (crtc, output))
649N/A continue;
649N/A
649N/A my_score = 1000;
649N/A /* slight preference for existing connections */
649N/A if (crtc == output->current_crtc_info)
649N/A my_score++;
649N/A
649N/A output->crtc_info = crtc;
649N/A score = my_score + pick_crtcs_score (outputs);
649N/A if (score > best_score)
649N/A {
649N/A best_crtc = crtc;
649N/A best_score = score;
649N/A }
649N/A }
649N/A if (output->crtc_info != best_crtc)
649N/A output->crtc_info = best_crtc;
649N/A /*
649N/A * Reset other outputs based on this one using the best crtc
649N/A */
649N/A (void) pick_crtcs_score (outputs);
649N/A
649N/A return best_score;
649N/A}
649N/A
649N/A/*
649N/A * Pick crtcs for any changing outputs that don't have one
649N/A */
649N/Astatic Bool
649N/Apick_crtcs (void)
649N/A{
649N/A output_t *output;
649N/A int i;
649N/A
649N/A /*
649N/A * First try to match up newly enabled outputs with spare crtcs
649N/A */
649N/A for (i = 0; i < ncon; i++)
649N/A {
649N/A output = con_outputs[i].output;
649N/A
649N/A if (output->mode_info)
649N/A {
649N/A if (output->crtc_info) {
649N/A if (output->crtc_info->crtc_info->noutput > 0 &&
649N/A (output->crtc_info->crtc_info->noutput > 1 ||
649N/A output != find_output_by_xid (output->crtc_info->crtc_info->outputs[0])))
649N/A break;
649N/A } else {
649N/A output->crtc_info = find_crtc_for_output (output);
649N/A if (!output->crtc_info)
649N/A break;
649N/A else {
649N/A if (verbose)
1459N/A fprintf(stderr, "picked crtc 0x%lx for output %d (%s)\n",
649N/A output->crtc_info->crtc.xid, i, output->output_info->name);
649N/A }
649N/A }
649N/A }
649N/A }
649N/A
649N/A /*
649N/A * Everyone is happy
649N/A */
649N/A if (i == ncon)
649N/A return True;
649N/A /*
649N/A * When the simple way fails, see if there is a way
649N/A * to swap crtcs around and make things work
649N/A */
649N/A for (output = outputs; output; output = output->next)
649N/A output->current_crtc_info = output->crtc_info;
649N/A pick_crtcs_score (outputs);
649N/A for (output = outputs; output; output = output->next)
649N/A {
649N/A if (output->mode_info && !output->crtc_info) {
649N/A if (verbose)
649N/A fprintf (stderr, "cannot find crtc for output %s\n",
649N/A output->output.string);
649N/A return False;
649N/A }
649N/A }
649N/A
649N/A return True;
649N/A}
649N/A
649N/Astatic Bool
649N/Aprobe_and_check_output_changes (void) {
649N/A XRRScreenResources *new_res = NULL;
649N/A int changed = False;
649N/A int i;
649N/A
649N/A new_res = XRRGetScreenResources (dpy, root);
649N/A if (!new_res)
649N/A fatal ("could not get screen resources");
649N/A
649N/A if ((new_res->noutput != res->noutput) || (new_res->nmode != res->nmode) ||
649N/A (new_res->ncrtc != res->ncrtc))
649N/A changed = True;
649N/A else {
649N/A for (i = 0; i < new_res->noutput; i++)
649N/A if (new_res->outputs[i] != res->outputs[i]) {
649N/A changed = True;
649N/A break;
649N/A }
649N/A for (i = 0; i < new_res->nmode; i++)
649N/A if (new_res->modes[i].id != res->modes[i].id) {
649N/A changed = True;
649N/A break;
649N/A }
649N/A for (i = 0; i < new_res->ncrtc; i++) {
649N/A crtc_t *crtc = NULL; /* old */
649N/A XRRCrtcInfo *crtc_info = NULL; /* new */
649N/A
649N/A crtc = find_crtc_by_xid (res->crtcs[i]);
649N/A crtc_info = XRRGetCrtcInfo (dpy, new_res,
649N/A new_res->crtcs[i]);
649N/A
649N/A if (!crtc || !crtc_info) {
649N/A changed = True;
649N/A break;
649N/A }
649N/A if (!crtc->mode_info && !find_mode_by_xid (crtc_info->mode))
649N/A continue;
649N/A if ((crtc_info->x != crtc->x) ||
649N/A (crtc_info->y != crtc->y) ||
649N/A (find_mode_by_xid (crtc_info->mode) != crtc->mode_info) ||
649N/A (crtc_info->rotation != crtc->rotation)) {
649N/A changed = True;
649N/A break;
649N/A }
649N/A }
649N/A }
649N/A
649N/A if (changed) {
649N/A if (res)
649N/A XRRFreeScreenResources(res);
649N/A res = new_res;
649N/A
649N/A if (verbose)
649N/A fprintf(stderr, "probed: output status changed\n");
649N/A return True;
649N/A }
649N/A
649N/A if (verbose)
649N/A fprintf(stderr, "probed: no output status change\n");
649N/A return False;
649N/A}
649N/A
649N/Astatic Bool
649N/Aneed_probe (void) {
649N/A struct timeval cur_time_val;
649N/A long long cur, prev;
649N/A
649N/A X_GETTIMEOFDAY(&cur_time_val);
649N/A cur = (long long) cur_time_val.tv_sec * 1000000 + cur_time_val.tv_usec;
649N/A prev =(long long) time_val.tv_sec * 1000000 + time_val.tv_usec;
649N/A if (((cur - prev) < 0) || ((cur - prev) > 5000000))
649N/A return True;
649N/A else
649N/A return False;
649N/A}
649N/A
649N/Astatic int
649N/Amode_sort (const void *p1, void *p2)
649N/A{
649N/A XRRModeInfo *mi1 = * (XRRModeInfo **) p1;
649N/A XRRModeInfo *mi2 = * (XRRModeInfo **) p2;
649N/A
649N/A if ((mi1->width == mi2->width) && (mi1->height == mi2->height)) {
649N/A if (mode_refresh(mi1) && mode_refresh(mi2)) {
649N/A if (mode_refresh(mi1) < mode_refresh(mi2))
649N/A return 1;
649N/A if (mode_refresh(mi1) > mode_refresh(mi2))
649N/A return -1;
649N/A } else
649N/A return 0;
649N/A }
649N/A
649N/A if ((mi1->width == mi2->width) && (mi1->height < mi2->height))
649N/A return 1;
649N/A if ((mi1->width == mi2->width) && (mi1->height > mi2->height))
649N/A return -1;
649N/A if (mi1->width < mi2->width)
649N/A return 1;
649N/A if (mi1->width > mi2->width)
649N/A return -1;
649N/A
649N/A return 0;
649N/A}
649N/A
649N/Astatic int
649N/Aoutput_sort (const void *p1, const void *p2) {
649N/A con_output_t co1 = * (con_output_t *) p1;
649N/A con_output_t co2 = * (con_output_t *) p2;
649N/A int ncrtc1 = co1.output->output_info->ncrtc;
649N/A int ncrtc2 = co2.output->output_info->ncrtc;
649N/A char *name1 = co1.output->output_info->name;
649N/A char *name2 = co2.output->output_info->name;
649N/A
649N/A if (ncrtc1 == ncrtc2)
649N/A return (strcmp(name1, name2));
649N/A if (ncrtc1 < ncrtc2)
649N/A return -1;
649N/A
649N/A return 1;
649N/A}
649N/A
649N/Astatic Bool
649N/Aget_common_mode(con_output_t *c0, con_output_t *c1, int *m0, int *m1) {
649N/A int i, j;
649N/A int i1 = -1, j1 = -1, i2 = -1, j2 = -1;
649N/A int x, y, w, h;
649N/A output_t *output = c0->output;
649N/A
649N/A *m0 = -1;
649N/A *m0 = -1;
649N/A if (!c0 ||!c1 || !c0->smodes || !c1->smodes)
649N/A return False;
649N/A
649N/A /* first try to find mode with common same size */
649N/A for (i = 0; i < c0->nsmodes; i ++) {
649N/A for (j = 0; j < c1->nsmodes; j ++)
649N/A if ((c0->smodes[i]->width == c1->smodes[j]->width) &&
649N/A (c0->smodes[i]->height == c1->smodes[j]->height)) {
649N/A x = output->x;
649N/A y = output->y;
649N/A w = mode_width (c0->smodes[i], output->rotation);
649N/A h = mode_height (c0->smodes[i], output->rotation);
649N/A if ((x + w <= maxWidth) && (y + h <= maxHeight)) {
649N/A i1 = i; j1 = j;
649N/A break;
649N/A }
649N/A }
649N/A if ((i1 != -1) && (j1 != -1))
649N/A break;
649N/A }
649N/A
649N/A if ((i1 == -1) && (j1 == -1))
649N/A return False;
649N/A
649N/A /* then try to find mode with common id for possible cloning */
649N/A for (i = 0; i < c0->nsmodes; i ++) {
649N/A for (j = 0; j < c1->nsmodes; j ++)
649N/A if (c0->smodes[i] == c1->smodes[j]) {
649N/A x = output->x;
649N/A y = output->y;
649N/A w = mode_width (c0->smodes[i], output->rotation);
649N/A h = mode_height (c0->smodes[i], output->rotation);
649N/A if ((x + w <= maxWidth) && (y + h <= maxHeight)) {
649N/A i2 = i; j2 = j;
649N/A break;
649N/A }
649N/A }
649N/A if ((i2 != -1) && (j2 != -1))
649N/A break;
649N/A }
649N/A
649N/A if ((i2 == -1) && (j2 == -1)) {
649N/A *m0 = i1;
649N/A *m1 = j1;
649N/A } else {
649N/A /* use common id if it is not smaller */
649N/A if ((mode_width (c0->smodes[i1], output->rotation) >
649N/A mode_width (c0->smodes[i2], output->rotation)) &&
649N/A (mode_height (c0->smodes[i1], output->rotation) >
649N/A mode_height (c0->smodes[i2], output->rotation))) {
649N/A *m0 = i1;
649N/A *m1 = j1;
649N/A } else {
649N/A *m0 = i2;
649N/A *m1 = j2;
649N/A }
649N/A }
649N/A
649N/A return True;
649N/A}
649N/A
649N/Astatic XRRModeInfo *
649N/Aget_largest_mode (con_output_t *c, XRRModeInfo *start_mode) {
649N/A int i, found = False;
649N/A output_t *output = c->output;
649N/A
649N/A for (i = 0; i < c->nsmodes; i++) {
649N/A XRRModeInfo *mode_info = c->smodes[i];
649N/A int x, y, w, h;
649N/A
649N/A if (!found && (start_mode != mode_info))
649N/A continue;
649N/A else
649N/A found = True;
649N/A
649N/A if (mode_info) {
649N/A x = output->x;
649N/A y = output->y;
649N/A w = mode_width (mode_info, output->rotation);
649N/A h = mode_height (mode_info, output->rotation);
649N/A if ((x + w <= maxWidth) && (y + h <= maxHeight))
649N/A break;
649N/A }
649N/A }
649N/A
649N/A if (i < c->nsmodes)
649N/A return c->smodes[i];
649N/A else
649N/A fatal("cannot find mode");
649N/A
649N/A}
649N/A
649N/Astatic Bool
649N/Avalid_mode(con_output_t *con, XRRModeInfo *mode) {
649N/A int i;
649N/A
649N/A for (i = 0; i < con->nsmodes; i++)
649N/A if (con->smodes[i] == mode)
649N/A return True;
649N/A
649N/A return False;
649N/A}
649N/A
649N/Astatic void
649N/Ado_init (void)
649N/A{
649N/A int i, j;
649N/A output_t *output;
649N/A
649N/A /* Initialize con_outputs array */
649N/A for (i = 0; i < MAX_OUTPUT; i++) {
649N/A con_outputs[i].output = NULL;
649N/A con_outputs[i].on = False;
649N/A start_mode[i] = NULL;
649N/A new_mode[i] = NULL;
649N/A }
649N/A
649N/A ncon = 0;
649N/A dis_ncon = 0;
649N/A init_rotation = RR_Rotate_0;
649N/A init_x = 0;
649N/A init_y = 0;
649N/A get_crtcs ();
649N/A get_outputs ();
649N/A
649N/A for (output = outputs; output; output = output->next) {
649N/A XRROutputInfo *output_info = output->output_info;
649N/A
649N/A if (output_info->connection == RR_Connected) {
649N/A con_outputs[ncon].output = output;
649N/A con_outputs[ncon].nsmodes = 0;
649N/A for (j = 0; j < output_info->nmode; j++) {
649N/A XRRModeInfo *rmode = find_mode_by_xid (output_info->modes[j]);
649N/A
649N/A con_outputs[ncon].smodes =
649N/A realloc(con_outputs[ncon].smodes,
649N/A (con_outputs[ncon].nsmodes + 1) * sizeof (XRRModeInfo *));
649N/A con_outputs[ncon].smodes[j] = rmode;
649N/A con_outputs[ncon].nsmodes ++;
649N/A }
649N/A
649N/A /* Sort the modes */
649N/A qsort((void *) con_outputs[ncon].smodes,
649N/A con_outputs[ncon].nsmodes, sizeof(XRRModeInfo *),
649N/A (int (*) (const void *, const void *)) mode_sort);
649N/A
649N/A if (output_info->crtc) {
649N/A crtc_t *crtc;
649N/A
649N/A con_outputs[ncon].on = True;
649N/A for (j = 0; j < output_info->ncrtc; j++) {
649N/A if (output_info->crtcs[j] == output_info->crtc)
649N/A break;
649N/A if (j == output_info->ncrtc) {
649N/A if (verbose)
649N/A fatal ("crtc does not match for output\n");
649N/A }
649N/A }
649N/A /* set initial mode_info */
649N/A crtc = find_crtc_by_xid (output_info->crtc);
649N/A if (crtc)
649N/A con_outputs[ncon].output->mode_info =
649N/A find_mode_by_xid (crtc->crtc_info->mode);
649N/A }
649N/A else
649N/A con_outputs[ncon].on = False;
649N/A ncon ++;
649N/A } else if (output_info->connection == RR_Disconnected) {
649N/A dis_con_outputs[dis_ncon].output = output;
649N/A dis_ncon ++;
649N/A }
649N/A }
649N/A
649N/A qsort((void **) con_outputs, ncon,
649N/A sizeof(con_output_t), (int (*) (const void *, const void *)) output_sort);
649N/A
649N/A if (verbose) {
649N/A fprintf(stderr, "Total connected outputs = %d :\n", ncon);
649N/A for (j = 0; j < ncon; j++) {
649N/A fprintf(stderr, "%d (%s): top mode = %s, rotation = %d, crtcs =", j,
649N/A con_outputs[j].output->output_info->name,
649N/A con_outputs[j].smodes[0]->name,
649N/A con_outputs[j].output->rotation);
649N/A for (i = 0; i < con_outputs[j].output->output_info->ncrtc; i++)
1459N/A fprintf(stderr, " 0x%lx", con_outputs[j].output->output_info->crtcs[i]);
1459N/A fprintf(stderr, ", using 0x%lx", con_outputs[j].output->output_info->crtc);
649N/A fprintf(stderr, "\n");
649N/A }
649N/A fprintf(stderr, "Total disconnected outputs = %d :\n", dis_ncon);
649N/A for (j = 0; j < dis_ncon; j++) {
649N/A fprintf(stderr, "%d (%s) : number of crtcs %d =", j,
649N/A dis_con_outputs[j].output->output_info->name,
649N/A dis_con_outputs[j].output->output_info->ncrtc);
649N/A for (i = 0; i < dis_con_outputs[j].output->output_info->ncrtc; i++)
1459N/A fprintf(stderr, " 0x%lx", dis_con_outputs[j].output->output_info->crtcs[i]);
1459N/A fprintf(stderr, ", using 0x%lx", dis_con_outputs[j].output->output_info->crtc);
649N/A fprintf(stderr, "\n");
649N/A }
649N/A }
649N/A
649N/A i = con_outputs[2].on * 4 + con_outputs[1].on * 2 + con_outputs[0].on;
649N/A
649N/A if ((i == 1) || (i == 2) || (i == 4)) {
649N/A use_init_pos = True;
649N/A j = i >> 1;
649N/A
649N/A /* remember position and mode info in single state */
649N/A start_mode[j] = con_outputs[j].output->mode_info;
649N/A init_rotation = con_outputs[j].output->rotation;
649N/A init_x = con_outputs[j].output->x;
649N/A init_y = con_outputs[j].output->y;
649N/A } else
649N/A use_init_pos = False;
649N/A
649N/A if ((ncon != 2) || (start < 3))
649N/A start = i;
649N/A
649N/A if ((ncon < 1) || (ncon > 3)) {
649N/A if ((ncon < 1) && verbose)
1459N/A fprintf (stderr, "warn: no connection\n");
649N/A else if ((ncon > 3) && verbose)
649N/A fprintf (stderr, "warn: too many (more than 3) connections: %d: can't switch\n", ncon);
649N/A do_not_switch = True;
649N/A }
649N/A
649N/A did_init = True;
649N/A
649N/A return;
649N/A}
649N/A
649N/Astatic int
649N/Agrab_key (Display *dpy, int keysym, unsigned int modifier,
649N/A Window grab_window)
649N/A{
649N/A char msg[256];
649N/A int keycode = XKeysymToKeycode(dpy, keysym);
649N/A
649N/A if (keycode == NoSymbol)
1459N/A fatal ("grab_key: keycode not defined for keysym 0x%x\n", keysym);
649N/A
649N/A had_error = 0;
649N/A prev_handler = XSetErrorHandler (cur_handler);
649N/A
649N/A if (!testrun) {
649N/A XGrabKey(dpy,
649N/A keycode,
649N/A modifier,
649N/A root, True, GrabModeAsync, GrabModeAsync);
649N/A XSync (dpy, False);
649N/A }
649N/A
649N/A XSetErrorHandler (prev_handler);
649N/A if (had_error) {
649N/A XGetErrorText (dpy, had_error, msg, sizeof (msg));
649N/A fatal ("XGrabKey: %s\n", msg);
649N/A }
649N/A
649N/A if (verbose)
649N/A fprintf(stderr, "keycode to grab: %d\n", keycode);
649N/A
649N/A return keycode;
649N/A}
649N/A
649N/Astatic Bool
649N/Ado_switch (void)
649N/A{
649N/A int i, j;
649N/A int single;
1459N/A int save = -1;
649N/A
1459N/A if (ncon <= 0)
1459N/A return True;
649N/A
649N/A for (i = 0; i < ncon; i++) {
649N/A output_t *output = con_outputs[i].output;
649N/A
1459N/A new_mode[i] = NULL;
649N/A output->relation = same_as;
649N/A output->relative_to = NULL;
649N/A if (use_init_pos) {
649N/A output->x = init_x;
649N/A output->y = init_y;
649N/A output->rotation = init_rotation;
649N/A } else {
649N/A output->x = 0;
649N/A output->y = 0;
649N/A }
649N/A }
649N/A
649N/A if (ncon == 2) {
649N/A if (!nosideview) {
649N/A if (++start > 5) start = 1;
649N/A }
649N/A else {
649N/A if (++start > 3) start = 1;
649N/A }
649N/A
649N/A if (verbose)
649N/A fprintf(stderr, "current state = %d\n", start);
649N/A if (start >= 3) {
649N/A int m0, m1;
649N/A
649N/A if (get_common_mode(&con_outputs[0], &con_outputs[1], &m0, &m1)) {
649N/A new_mode[0] = con_outputs[0].smodes[m0];
649N/A new_mode[1] = con_outputs[1].smodes[m1];
649N/A } else {
649N/A new_mode[0] = get_largest_mode (&con_outputs[0],
649N/A con_outputs[0].smodes[0]);
649N/A new_mode[1] = get_largest_mode (&con_outputs[1],
649N/A con_outputs[1].smodes[0]);
649N/A }
649N/A } else {
649N/A if (start_mode[start -1] && valid_mode(&con_outputs[start -1],
649N/A start_mode[start -1]))
649N/A new_mode[start -1] = start_mode[start -1];
649N/A else {
649N/A if (con_outputs[start -1].smodes[0])
649N/A new_mode[start -1] =
649N/A get_largest_mode (&con_outputs[start-1],
649N/A con_outputs[start -1].smodes[0]);
649N/A }
649N/A }
649N/A }
649N/A
649N/A if (ncon == 3) {
649N/A if (++start > 6) start = 1;
649N/A if (verbose)
649N/A fprintf(stderr, "current state = %d\n", start);
649N/A if ((start == 1) || (start == 2) || (start == 4)) {
649N/A single = 1;
649N/A i = start >> 1;
649N/A j = 0;
649N/A }
649N/A else {
649N/A single = 0;
649N/A if (start > 4)
649N/A j = 2;
649N/A else
649N/A j = 1;
649N/A if (start > 5)
649N/A i = 1;
649N/A else
649N/A i = 0;
649N/A }
649N/A
649N/A if (single) {
649N/A if (start_mode[i] && valid_mode(&con_outputs[i], start_mode[i]))
649N/A new_mode[i] = start_mode[i];
649N/A else {
649N/A if (con_outputs[i].smodes[0])
649N/A new_mode[i] = get_largest_mode (&con_outputs[i],
649N/A con_outputs[i].smodes[0]);
649N/A }
649N/A }
649N/A else {
649N/A int m0, m1;
649N/A
649N/A if (get_common_mode(&con_outputs[i], &con_outputs[j], &m0, &m1)) {
649N/A new_mode[i] = con_outputs[i].smodes[m0];
649N/A new_mode[j] = con_outputs[j].smodes[m1];
649N/A } else {
649N/A new_mode[i] = get_largest_mode (&con_outputs[i],
649N/A con_outputs[i].smodes[0]);
649N/A new_mode[j] = get_largest_mode (&con_outputs[j],
649N/A con_outputs[j].smodes[0]);
649N/A }
649N/A }
649N/A }
649N/A
649N/A if (ncon == 1) {
649N/A if (start_mode[0] && valid_mode(&con_outputs[0], start_mode[0]))
649N/A new_mode[0] = start_mode[0];
649N/A else {
649N/A if (con_outputs[0].smodes[0])
649N/A new_mode[0] = get_largest_mode (&con_outputs[0],
649N/A con_outputs[0].smodes[0]);
649N/A }
649N/A }
649N/A
649N/A /* Set mode */
649N/A for (i = 0; i < ncon; i++) {
649N/A output_t *output;
649N/A
649N/A output = con_outputs[i].output;
649N/A if (new_mode[i]) {
649N/A if ((!output->mode_info) || (output->mode_info != new_mode[i])) {
649N/A output->mode_info = new_mode[i];
649N/A con_outputs[i].on = True;
649N/A if (verbose)
649N/A fprintf(stderr, "set output %d (%s) to mode %s rotation %d\n", i,
649N/A con_outputs[i].output->output_info->name,
649N/A con_outputs[i].output->mode_info->name,
649N/A con_outputs[i].output->rotation);
649N/A }
649N/A } else if (con_outputs[i].on ) {
1323N/A if (!need_off_deferred) {
1323N/A output->mode_info = NULL;
1323N/A con_outputs[i].on = False;
1323N/A if (verbose)
1323N/A fprintf(stderr, "turn off output %d (%s) \n",
1323N/A i, con_outputs[i].output->output_info->name);
1323N/A } else
1323N/A save = i;
649N/A }
649N/A }
649N/A
649N/A if ((ncon == 2) && (start >= 4)) {
649N/A if (start == 4) {
649N/A con_outputs[1].output->relative_to = con_outputs[0].output;
649N/A con_outputs[1].output->relation = right_of;
649N/A con_outputs[1].output->changes = changes_relation;
649N/A con_outputs[0].output->changes = 0;
649N/A }
649N/A else if (start == 5) {
649N/A con_outputs[0].output->relative_to = con_outputs[1].output;
649N/A con_outputs[0].output->relation = right_of;
649N/A con_outputs[0].output->changes = changes_relation;
649N/A con_outputs[1].output->changes = 0;
649N/A }
649N/A
649N/A set_positions();
649N/A }
649N/A
649N/A if (!set_screen_size ())
649N/A return False;
649N/A
649N/A
649N/A /* reset crtcs before allocation */
649N/A reset_crtcs_for_outputs();
649N/A
649N/A if (!did_init)
649N/A get_crtcs();
649N/A
649N/A if (!pick_crtcs()) {
649N/A if (verbose)
649N/A fprintf(stderr, "pick_crtcs failed\n");
649N/A return True;
649N/A }
649N/A
649N/A set_crtcs ();
649N/A apply();
1323N/A
1459N/A if (need_off_deferred && (save != -1)) {
1323N/A /* Now, take the deferred output off */
1323N/A output_t *output;
1323N/A crtc_t *crtc;
1323N/A Status s;
1323N/A
1323N/A output = con_outputs[save].output;
1323N/A output->mode_info = NULL;
1323N/A con_outputs[save].on = False;
1323N/A if (verbose)
1323N/A fprintf(stderr, "turn off output %d (%s) \n",
1323N/A save, con_outputs[save].output->output_info->name);
1323N/A
1323N/A crtc = output->crtc_info;
1323N/A s = crtc_disable (crtc);
1323N/A if (s != RRSetConfigSuccess)
1323N/A panic (s, crtc);
1323N/A }
1323N/A
649N/A XSync (dpy, False);
649N/A
649N/A did_init = False;
649N/A
649N/A return True;
649N/A
649N/A}
649N/A
765N/Astatic Bool
1459N/Ado_toggle (void)
765N/A{
765N/A Atom atom;
765N/A XEvent xev;
765N/A int ret;
765N/A
765N/A atom = XInternAtom (dpy, "DISPLAYSWITCH_DAEMON", True);
765N/A if (!atom) {
765N/A fprintf(stderr, "dispswitch daemon not running\n");
765N/A return False;
765N/A }
765N/A
765N/A win = XGetSelectionOwner (dpy, atom);
765N/A if (!win) {
765N/A fprintf(stderr, "dispswitch: No owner of dispswitch daemon is found\n");
765N/A return False;
765N/A }
765N/A
765N/A bzero (&xev, sizeof (XEvent));
765N/A xev.xkey.type = KeyPress;
765N/A xev.xkey.send_event = True;
765N/A xev.xkey.display = dpy;
765N/A /* Any keycode */
765N/A xev.xkey.keycode = 71;
765N/A
765N/A
765N/A /*
765N/A * Send another instance of dispswitch (a daemon) an event to
765N/A * request a switch
765N/A */
765N/A ret = XSendEvent(dpy, win, False, KeyPressMask, &xev);
765N/A XFlush(dpy);
765N/A
765N/A if (!ret)
765N/A fprintf(stderr, "dispswitch: XSendEvent error\n");
765N/A
765N/A return (!ret);
765N/A}
765N/A
649N/Aint
649N/Amain (int argc, char **argv)
649N/A{
649N/A char *display_name = NULL;
649N/A int major, minor;
649N/A int i;
649N/A char msg[256];
649N/A XEvent ev;
649N/A unsigned int modifier = 0;
649N/A Bool key_given = False;
649N/A Bool mod_given = False;
765N/A int keysym = 0, toggle = False, listen = False;
765N/A
765N/A Atom atom;
649N/A
649N/A program_name = argv[0];
649N/A
649N/A for (i = 1; i < argc; i++) {
649N/A if (!strcmp ("-display", argv[i]) || !strcmp ("-d", argv[i])) {
649N/A if (++i>=argc) usage ();
649N/A display_name = argv[i];
649N/A continue;
649N/A }
649N/A if (!strcmp ("-key", argv[i]) || !strcmp ("-k", argv[i])) {
649N/A if (++i>=argc) usage ();
649N/A if ((keysym = XStringToKeysym(argv[i])) == NoSymbol) {
649N/A fprintf(stderr, "invalid keysym: -key %s\n", argv[i]);
649N/A usage();
649N/A }
649N/A key_given = True;
649N/A continue;
649N/A }
649N/A if (!strcmp ("-mod", argv[i]) || !strcmp ("-m", argv[i])) {
649N/A int j;
649N/A char *s, *p, *q;
649N/A int end = 0;
649N/A
649N/A if (++i>=argc) usage ();
649N/A s = strdup (argv[i]);
649N/A if (!s) {
649N/A if (verbose)
649N/A fprintf(stderr, "modifier failed, will use default modifier\n");
649N/A continue;
649N/A }
649N/A while (*s == ' ') s++;
649N/A p = s + strlen(s) - 1;
649N/A while (*p == ' ') *p-- = 0;
649N/A q = s;
649N/A for (; ;) {
649N/A if (p = strchr(s, '+')) {
649N/A *p = ' ';
649N/A while ((p > s) && (*(p-1) == ' ')) p--;
649N/A *p = 0;
649N/A }
649N/A else
649N/A end = 1;
649N/A for (j = 0; j < MAX_MODIFIERS; j++) {
649N/A if (!strcmp(mod_key_table[j].modname, s)) {
649N/A modifier |= mod_key_table[j].mod;
649N/A break;
649N/A }
649N/A }
649N/A if (j == MAX_MODIFIERS) {
649N/A fprintf(stderr, "invalid modifier: -mod %s\n", q);
649N/A usage();
649N/A }
649N/A if (end)
649N/A break;
649N/A else {
649N/A s = ++p;
649N/A while (*s == ' ') s++;
649N/A }
649N/A }
649N/A mod_given = True;
649N/A free (q);
649N/A continue;
649N/A }
649N/A if (!strcmp ("-nosideview", argv[i])) {
649N/A nosideview = True;
649N/A continue;
649N/A }
649N/A if (!strcmp ("-verbose", argv[i]) || !strcmp ("-v", argv[i])) {
649N/A verbose = True;
649N/A continue;
649N/A }
649N/A if (!strcmp ("-testrun", argv[i])) {
649N/A testrun = True;
649N/A verbose = True;
649N/A continue;
649N/A }
765N/A if (!strcmp ("-toggle", argv[i]) || !strcmp ("-t", argv[i])) {
765N/A toggle = True;
765N/A continue;
765N/A }
765N/A if (!strcmp ("-listen", argv[i]) || !strcmp ("-l", argv[i])) {
765N/A listen = True;
765N/A continue;
765N/A }
649N/A usage();
649N/A }
649N/A
649N/A dpy = XOpenDisplay (display_name);
649N/A
649N/A if (dpy == NULL)
649N/A fatal ("can't open display %s\n", XDisplayName(display_name));
649N/A
649N/A screen = DefaultScreen (dpy);
649N/A root = RootWindow (dpy, screen);
649N/A
649N/A if (!XRRQueryVersion (dpy, &major, &minor))
649N/A fatal ("randr extension missing\n");
649N/A
649N/A if ((major <= 1) && (major != 1 || minor < 2))
649N/A fatal ("wrong randr version: %d.%d\n", major, minor);
649N/A
765N/A
765N/A if (toggle)
765N/A exit (do_toggle());
765N/A
765N/A /*
765N/A * Create an atom, a trivial window, and make it selection owner.
765N/A * Ready to accept a client event request for switch
765N/A */
765N/A atom = XInternAtom(dpy, "DISPLAYSWITCH_DAEMON", False);
765N/A if (!atom) {
765N/A if (verbose)
765N/A fprintf(stderr, "cannot create Atom\n");
765N/A }
765N/A else {
765N/A if (XGetSelectionOwner (dpy, atom)) {
765N/A if (verbose)
765N/A fprintf(stderr, "dispswitch daemon is already running, quit\n");
765N/A exit (1);
765N/A }
765N/A win = XCreateSimpleWindow(dpy, root, 0, 0, 10, 10, 0, 10, 0);
765N/A if (!win) {
765N/A if (verbose)
765N/A fprintf(stderr, "cannot create window\n");
765N/A }
765N/A else {
765N/A XSetSelectionOwner(dpy, atom, win, CurrentTime);
765N/A if (XGetSelectionOwner(dpy, atom) != win) {
765N/A if (verbose)
765N/A fprintf(stderr, "set selection owner failed\n");
765N/A } else
765N/A XSelectInput(dpy, win, KeyPressMask);
765N/A }
765N/A }
765N/A
649N/A /* set default key and modifier if not given in command */
649N/A if (!key_given)
649N/A keysym = XStringToKeysym ("F5");
649N/A if (!mod_given)
649N/A modifier = ShiftMask;
649N/A
765N/A if (!listen)
765N/A cur_keycode = grab_key (dpy, keysym, modifier, root);
649N/A
649N/A XRRGetScreenSizeRange (dpy, root, &minWidth, &minHeight,
649N/A &maxWidth, &maxHeight);
649N/A
649N/A fb_width_mm = DisplayWidthMM (dpy, screen);
649N/A fb_height_mm = DisplayHeightMM (dpy, screen);
649N/A dpi = (25.4 * DisplayHeight (dpy, screen)) / DisplayHeightMM(dpy, screen);
649N/A
649N/A res = XRRGetScreenResources (dpy, root);
649N/A if (!res)
649N/A fatal ("could not get screen resources\n");
1459N/A if (res->ncrtc < 2)
1459N/A fatal ("too few crtcs: %d\n", res->ncrtc);
649N/A
649N/A do_init();
649N/A
649N/A X_GETTIMEOFDAY(&time_val);
649N/A
649N/A for(;;)
649N/A {
1323N/A need_off_deferred = False;
1323N/A
649N/A if (testrun) {
649N/A usleep(4000000);
649N/A fprintf(stderr, "\n");
649N/A } else
649N/A XNextEvent(dpy, &ev);
649N/A
765N/A if (!listen && !testrun && (ev.type == MappingNotify) &&
649N/A ((ev.xmapping.request == MappingKeyboard) ||
649N/A (ev.xmapping.request == MappingModifier))) {
649N/A /* keyboard/modifier mapping changed */
649N/A if (verbose)
649N/A fprintf(stderr, "\nkeyboard/modifier mapping changed ...\n");
649N/A
649N/A XUngrabKey(dpy, cur_keycode, modifier, root);
649N/A cur_keycode = grab_key (dpy, keysym, modifier, root);
649N/A }
649N/A
649N/A if (testrun || (ev.type == KeyPress)) {
649N/A if (verbose)
649N/A fprintf(stderr, "\na key press event was grabbed ...\n");
649N/A
649N/A do_not_switch = False;
649N/A
649N/A if (testrun || need_probe()) {
649N/A /* Too long since last switch, need to check output changes */
649N/A if (probe_and_check_output_changes ()) {
649N/A output_t *output, *next;
649N/A
649N/A output = outputs;
649N/A while (output) {
649N/A if (output->output_info)
649N/A XRRFreeOutputInfo (output->output_info);
649N/A if (output->crtc_info && output->crtc_info->outputs) {
649N/A free(output->crtc_info->outputs);
649N/A output->crtc_info->outputs = NULL;
649N/A }
649N/A next = output->next;
649N/A free(output);
649N/A output = next;
649N/A }
649N/A outputs = NULL;
649N/A outputs_tail = &outputs;
649N/A for (i = 0; i < ncon; i++) {
649N/A con_outputs[i].output = NULL;
649N/A con_outputs[i].on = False;
649N/A if (con_outputs[i].smodes) {
649N/A free(con_outputs[i].smodes);
649N/A con_outputs[i].smodes = NULL;
649N/A }
649N/A con_outputs[i].nsmodes = 0;
649N/A }
649N/A
649N/A do_init();
649N/A } else if (ncon == 1)
649N/A do_not_switch = True;
649N/A } else if (ncon == 1)
649N/A do_not_switch = True;
649N/A
1323N/A if (!do_not_switch) {
1323N/A if ((ncon == 2) && (start == 1))
1323N/A /*
1323N/A * Workaround for intel based graphics: in switching from
1323N/A * LVDS to VGA, off on LVDS needs to be deferred.
1323N/A */
1323N/A need_off_deferred = True;
649N/A if (!do_switch()) {
649N/A if ((ncon == 2) && (start == 4)) {
649N/A start = 5;
649N/A if (verbose)
649N/A fprintf(stderr, "too small screen, skipping side view\n");
649N/A (void) do_switch();
649N/A }
649N/A }
1323N/A }
649N/A
649N/A X_GETTIMEOFDAY(&time_val);
649N/A }
649N/A }
649N/A}
649N/A