component(developer/golang-123): Add Component
[oi-userland.git] / components / x11 / dispswitch / src / dispswitch.c
blob15e6efe4bf37bad935814dfc6478f99f1d37cbce
1 /*
2 * Copyright © 2001 Keith Packard, member of The XFree86 Project, Inc.
3 * Copyright © 2002 Hewlett Packard Company, Inc.
4 * Copyright © 2006 Intel Corporation
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that copyright
9 * notice and this permission notice appear in supporting documentation, and
10 * that the name of the copyright holders not be used in advertising or
11 * publicity pertaining to distribution of the software without specific,
12 * written prior permission. The copyright holders make no representations
13 * about the suitability of this software for any purpose. It is provided "as
14 * is" without express or implied warranty.
16 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
22 * OF THIS SOFTWARE.
27 * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
29 * Permission is hereby granted, free of charge, to any person obtaining a
30 * copy of this software and associated documentation files (the "Software"),
31 * to deal in the Software without restriction, including without limitation
32 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
33 * and/or sell copies of the Software, and to permit persons to whom the
34 * Software is furnished to do so, subject to the following conditions:
36 * The above copyright notice and this permission notice (including the next
37 * paragraph) shall be included in all copies or substantial portions of the
38 * Software.
40 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
41 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
42 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
43 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
44 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
45 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
46 * DEALINGS IN THE SOFTWARE.
50 #include <stdio.h>
51 #include <X11/Xlib.h>
52 #include <X11/Xlibint.h>
53 #include <X11/Xproto.h>
54 #include <X11/extensions/Xrandr.h>
55 #include <X11/Xos.h>
56 #include <string.h>
57 #include <stdlib.h>
58 #include <stdarg.h>
59 #include <fcntl.h>
60 #include <signal.h>
61 #include <sys/proc.h>
62 #include <unistd.h>
65 static char *program_name;
66 static Display *dpy = NULL;
67 static Window root, win;
68 static int screen;
69 static Bool nosideview = False;
70 static Bool verbose = False;
71 static Bool testrun = False;
72 static int had_error = 0;
73 static XErrorHandler prev_handler;
74 static int cur_keycode;
75 static struct timeval time_val;
76 static Rotation init_rotation;
77 static int init_x;
78 static int init_y;
79 static Bool use_init_pos = False;
80 static Bool need_off_deferred = False;
82 static void
83 usage(void)
85 fprintf(stderr, "usage: %s [options]\n", program_name);
86 fprintf(stderr, " where options are:\n");
87 fprintf(stderr, " -toggle or -t\n");
88 fprintf(stderr, " -listen or -l\n");
89 fprintf(stderr, " -display <display> or -d <display>\n");
90 fprintf(stderr, " -key <key> or -k <key>\n");
91 fprintf(stderr, " -mod <modifier> or -m <modifier>\n");
92 fprintf(stderr, " -help\n");
93 fprintf(stderr, " -nosideview\n");
94 fprintf(stderr, " -verbose or -v\n");
95 fprintf(stderr, " -testrun\n");
96 exit(1);
97 /*NOTREACHED*/
100 static void
101 fatal (const char *format, ...)
103 va_list ap;
105 va_start (ap, format);
106 fprintf (stderr, "%s exiting: ", program_name);
107 vfprintf (stderr, format, ap);
108 va_end (ap);
109 exit (1);
110 /*NOTREACHED*/
113 static int
114 cur_handler (
115 Display *const err_display,
116 XErrorEvent *const err_event)
118 had_error = err_event -> error_code;
119 return (0);
122 typedef enum _relation {
123 left_of, right_of, above, below, same_as,
124 } relation_t;
126 typedef enum _changes {
127 changes_none = 0,
128 changes_crtc = (1 << 0),
129 changes_mode = (1 << 1),
130 changes_relation = (1 << 2),
131 changes_position = (1 << 3),
132 } changes_t;
134 typedef enum _name_kind {
135 name_none = 0,
136 name_string = (1 << 0),
137 name_xid = (1 << 1),
138 name_index = (1 << 2),
139 } name_kind_t;
141 typedef struct {
142 name_kind_t kind;
143 char *string;
144 XID xid;
145 int index;
146 } name_t;
148 typedef struct _crtc crtc_t;
149 typedef struct _output output_t;
151 struct _crtc {
152 name_t crtc;
153 XRRCrtcInfo *crtc_info;
155 XRRModeInfo *mode_info;
156 int x;
157 int y;
158 Rotation rotation;
159 output_t **outputs;
160 int noutput;
163 struct _output {
164 struct _output *next;
166 changes_t changes;
168 name_t output;
169 XRROutputInfo *output_info;
171 name_t crtc;
172 crtc_t *crtc_info;
173 crtc_t *current_crtc_info;
175 name_t mode;
176 float refresh;
177 XRRModeInfo *mode_info;
179 name_t addmode;
181 relation_t relation;
182 struct _output *relative_to;
184 int x, y;
185 Rotation rotation;
188 static output_t *outputs = NULL;
189 static output_t **outputs_tail = &outputs;
190 static crtc_t *crtcs = NULL;
191 static int num_crtcs;
192 static XRRScreenResources *res = NULL;
193 static int fb_width = 0, fb_height = 0;
194 static int fb_width_mm = 0, fb_height_mm = 0;
195 static float dpi = 0.0;
196 static Bool dryrun = False;
197 static int minWidth, maxWidth, minHeight, maxHeight;
199 #define MAX_OUTPUT 3
200 #define MAX_MODIFIERS 10
202 typedef struct _con_output con_output_t;
204 struct _con_output {
205 output_t *output;
206 XRRModeInfo **smodes;
207 int nsmodes;
208 Bool on;
211 typedef struct _mod_key_table mod_key_table_t;
213 struct _mod_key_table {
214 char *modname;
215 unsigned int mod;
218 static con_output_t con_outputs[MAX_OUTPUT];
219 static con_output_t dis_con_outputs[MAX_OUTPUT];
220 static XRRModeInfo *start_mode[MAX_OUTPUT];
221 static XRRModeInfo *new_mode[MAX_OUTPUT];
222 static int start = 0;
223 static int ncon;
224 static int dis_ncon;
225 static Bool do_not_switch = False;
226 static Bool did_init = False;
228 static mod_key_table_t mod_key_table [MAX_MODIFIERS] = {
229 {"none", 0},
230 {"shift", ShiftMask},
231 {"lock", LockMask},
232 {"control", ControlMask},
233 {"mod1", Mod1Mask},
234 {"mod2", Mod2Mask},
235 {"mod3", Mod3Mask},
236 {"mod4", Mod4Mask},
237 {"mod5", Mod5Mask},
238 {"any", AnyModifier}
241 static int
242 mode_height (XRRModeInfo *mode_info, Rotation rotation)
244 switch (rotation & 0xf) {
245 case RR_Rotate_0:
246 case RR_Rotate_180:
247 return mode_info->height;
248 case RR_Rotate_90:
249 case RR_Rotate_270:
250 return mode_info->width;
251 default:
252 return 0;
256 static int
257 mode_width (XRRModeInfo *mode_info, Rotation rotation)
259 switch (rotation & 0xf) {
260 case RR_Rotate_0:
261 case RR_Rotate_180:
262 return mode_info->width;
263 case RR_Rotate_90:
264 case RR_Rotate_270:
265 return mode_info->height;
266 default:
267 return 0;
271 /* v refresh frequency in Hz */
272 static float
273 mode_refresh (XRRModeInfo *mode_info)
275 float rate;
277 if (mode_info->hTotal && mode_info->vTotal)
278 rate = ((float) mode_info->dotClock /
279 ((float) mode_info->hTotal * (float) mode_info->vTotal));
280 else
281 rate = 0;
282 return rate;
286 static void
287 init_name (name_t *name)
289 name->kind = name_none;
292 static void
293 set_name_string (name_t *name, char *string)
295 name->kind |= name_string;
296 name->string = string;
299 static void
300 set_name_xid (name_t *name, XID xid)
302 name->kind |= name_xid;
303 name->xid = xid;
306 static void
307 set_name_index (name_t *name, int index)
309 name->kind |= name_index;
310 name->index = index;
313 static void
314 set_name_all (name_t *name, name_t *old)
316 if (old->kind & name_xid)
317 name->xid = old->xid;
318 if (old->kind & name_string)
319 name->string = old->string;
320 if (old->kind & name_index)
321 name->index = old->index;
322 name->kind |= old->kind;
325 static output_t *
326 add_output (void)
328 output_t *output = calloc (1, sizeof (output_t));
330 if (!output)
331 fatal ("out of memory");
332 output->next = NULL;
333 *outputs_tail = output;
334 outputs_tail = &output->next;
335 return output;
338 static output_t *
339 find_output (name_t *name)
341 output_t *output;
343 for (output = outputs; output; output = output->next)
345 name_kind_t common = name->kind & output->output.kind;
347 if ((common & name_xid) && name->xid == output->output.xid)
348 break;
349 if ((common & name_string) && !strcmp (name->string, output->output.string))
350 break;
351 if ((common & name_index) && name->index == output->output.index)
352 break;
354 return output;
357 static output_t *
358 find_output_by_xid (RROutput output)
360 name_t output_name;
362 init_name (&output_name);
363 set_name_xid (&output_name, output);
364 return find_output (&output_name);
367 static crtc_t *
368 find_crtc (name_t *name)
370 int c;
371 crtc_t *crtc = NULL;
373 for (c = 0; c < num_crtcs; c++)
375 name_kind_t common;
377 crtc = &crtcs[c];
378 common = name->kind & crtc->crtc.kind;
380 if ((common & name_xid) && name->xid == crtc->crtc.xid)
381 break;
382 if ((common & name_string) && !strcmp (name->string, crtc->crtc.string))
383 break;
384 if ((common & name_index) && name->index == crtc->crtc.index)
385 break;
386 crtc = NULL;
388 return crtc;
391 static crtc_t *
392 find_crtc_by_xid (RRCrtc crtc)
394 name_t crtc_name;
396 init_name (&crtc_name);
397 set_name_xid (&crtc_name, crtc);
398 return find_crtc (&crtc_name);
401 static XRRModeInfo *
402 find_mode (name_t *name)
404 int m;
405 XRRModeInfo *best = NULL;
407 for (m = 0; m < res->nmode; m++)
409 XRRModeInfo *mode = &res->modes[m];
410 if ((name->kind & name_xid) && name->xid == mode->id)
412 best = mode;
413 break;
416 return best;
419 static XRRModeInfo *
420 find_mode_by_xid (RRMode mode)
422 name_t mode_name;
424 init_name (&mode_name);
425 set_name_xid (&mode_name, mode);
426 return find_mode (&mode_name);
429 static void
430 set_output_info (output_t *output, RROutput xid, XRROutputInfo *output_info)
432 crtc_t *crtc;
434 /* sanity check output info */
435 if (output_info->connection != RR_Disconnected && !output_info->nmode)
436 fatal ("Output %s is not disconnected but has no modes\n",
437 output_info->name);
439 /* set output name and info */
440 if (!(output->output.kind & name_xid))
441 set_name_xid (&output->output, xid);
442 if (!(output->output.kind & name_string))
443 set_name_string (&output->output, output_info->name);
444 output->output_info = output_info;
446 crtc = find_crtc_by_xid (output->output_info->crtc);
447 /* set position */
448 if (crtc && crtc->crtc_info) {
449 output->x = crtc->crtc_info->x;
450 output->y = crtc->crtc_info->y;
453 /* set rotation */
454 output->rotation &= ~0xf;
455 if (crtc && crtc->crtc_info)
456 output->rotation |= (crtc->crtc_info->rotation & 0xf);
457 else
458 output->rotation = RR_Rotate_0;
462 static void
463 get_crtcs (void)
465 int c;
467 num_crtcs = res->ncrtc;
468 if (crtcs)
470 for (c = 0; c < res->ncrtc; c++)
472 if (crtcs[c].crtc_info)
473 XRRFreeCrtcInfo (crtcs[c].crtc_info);
475 free (crtcs);
476 crtcs = NULL;
479 crtcs = calloc (num_crtcs, sizeof (crtc_t));
480 if (!crtcs) fatal ("out of memory");
482 for (c = 0; c < res->ncrtc; c++)
484 XRRCrtcInfo *crtc_info = XRRGetCrtcInfo (dpy, res, res->crtcs[c]);
485 set_name_xid (&crtcs[c].crtc, res->crtcs[c]);
486 set_name_index (&crtcs[c].crtc, c);
487 if (!crtc_info) fatal ("could not get crtc 0x%x information", res->crtcs[c]);
488 crtcs[c].crtc_info = crtc_info;
489 if (crtc_info->mode == None)
491 crtcs[c].mode_info = NULL;
492 crtcs[c].x = 0;
493 crtcs[c].y = 0;
494 crtcs[c].rotation = RR_Rotate_0;
499 static void
500 crtc_add_output (crtc_t *crtc, output_t *output)
502 if (crtc->outputs)
503 crtc->outputs = realloc (crtc->outputs, (crtc->noutput + 1) * sizeof (output_t *));
504 else
506 crtc->outputs = calloc (1, sizeof (output_t *));
507 crtc->x = output->x;
508 crtc->y = output->y;
509 crtc->rotation = output->rotation;
510 crtc->mode_info = output->mode_info;
512 if (!crtc->outputs) fatal ("out of memory");
513 crtc->outputs[crtc->noutput++] = output;
516 static void
517 set_crtcs (void)
519 output_t *output;
520 int i;
522 for (i = 0; i < ncon; i++) {
523 output = con_outputs[i].output;
524 if (!output->mode_info) continue;
525 if (output->crtc_info)
526 crtc_add_output (output->crtc_info, output);
530 static void
531 reset_crtcs_for_outputs (void)
533 output_t *output;
535 for (output = outputs; output; output = output->next)
537 if ((output->crtc_info) && (output->crtc_info->outputs)) {
538 free (output->crtc_info->outputs);
539 output->crtc_info = NULL;
544 static Status
545 crtc_disable (crtc_t *crtc)
547 if (verbose)
548 fprintf (stderr, "crtc %d (0x%lx) : disable\n", crtc->crtc.index, crtc->crtc.xid);
550 if (dryrun)
551 return RRSetConfigSuccess;
553 return XRRSetCrtcConfig (dpy, res, crtc->crtc.xid, CurrentTime,
554 0, 0, None, RR_Rotate_0, NULL, 0);
557 static Status
558 crtc_revert (crtc_t *crtc)
560 XRRCrtcInfo *crtc_info = crtc->crtc_info;
562 if (verbose)
563 fprintf (stderr, "crtc %d: revert\n", crtc->crtc.index);
565 if (dryrun)
566 return RRSetConfigSuccess;
567 return XRRSetCrtcConfig (dpy, res, crtc->crtc.xid, CurrentTime,
568 crtc_info->x, crtc_info->y,
569 crtc_info->mode, crtc_info->rotation,
570 crtc_info->outputs, crtc_info->noutput);
573 static Status
574 crtc_apply (crtc_t *crtc)
576 RROutput *rr_outputs;
577 int o;
578 Status s;
579 RRMode mode = None;
581 if (!crtc->mode_info)
582 return RRSetConfigSuccess;
584 rr_outputs = calloc (crtc->noutput, sizeof (RROutput));
585 if (!rr_outputs)
586 return BadAlloc;
587 for (o = 0; o < crtc->noutput; o++)
588 rr_outputs[o] = crtc->outputs[o]->output.xid;
589 mode = crtc->mode_info->id;
590 if (verbose) {
591 fprintf (stderr, "crtc %d (0x%lx) : %12s %6.1f +%d+%d", crtc->crtc.index,
592 crtc->crtc.xid,
593 crtc->mode_info->name, mode_refresh (crtc->mode_info),
594 crtc->x, crtc->y);
595 for (o = 0; o < crtc->noutput; o++)
596 fprintf (stderr, " \"%s\"", crtc->outputs[o]->output.string);
597 fprintf (stderr, "\n");
599 if (dryrun)
600 s = RRSetConfigSuccess;
601 else
602 s = XRRSetCrtcConfig (dpy, res, crtc->crtc.xid, CurrentTime,
603 crtc->x, crtc->y, mode, crtc->rotation,
604 rr_outputs, crtc->noutput);
605 free (rr_outputs);
606 return s;
609 static void
610 screen_revert (void)
612 if (verbose)
613 fprintf (stderr, "screen %d: revert\n", screen);
615 if (dryrun)
616 return;
617 XRRSetScreenSize (dpy, root,
618 DisplayWidth (dpy, screen),
619 DisplayHeight (dpy, screen),
620 DisplayWidthMM (dpy, screen),
621 DisplayHeightMM (dpy, screen));
624 static void
625 screen_apply (void)
627 /* comment it out because DisplayWidth() does not reflect the
628 change of fb_width and fb_height previously set by
629 XRRSetScreenSize()
632 if (fb_width == DisplayWidth (dpy, screen) &&
633 fb_height == DisplayHeight (dpy, screen) &&
634 fb_width_mm == DisplayWidthMM (dpy, screen) &&
635 fb_height_mm == DisplayHeightMM (dpy, screen))
637 return;
641 if (verbose)
642 fprintf (stderr, "screen %d: %dx%d %dx%d mm %6.2fdpi\n", screen,
643 fb_width, fb_height, fb_width_mm, fb_height_mm, dpi);
644 if (dryrun)
645 return;
646 XRRSetScreenSize (dpy, root, fb_width, fb_height,
647 fb_width_mm, fb_height_mm);
651 static void
652 revert (void)
654 int c;
656 /* first disable all crtcs */
657 for (c = 0; c < res->ncrtc; c++)
658 crtc_disable (&crtcs[c]);
659 /* next reset screen size */
660 screen_revert ();
661 /* now restore all crtcs */
662 for (c = 0; c < res->ncrtc; c++)
663 crtc_revert (&crtcs[c]);
667 * uh-oh, something bad happened in the middle of changing
668 * the configuration. Revert to the previous configuration
669 * and bail
671 static void
672 panic (Status s, crtc_t *crtc)
674 int c = crtc->crtc.index;
675 char *message;
677 switch (s) {
678 case RRSetConfigSuccess: message = "succeeded"; break;
679 case BadAlloc: message = "out of memory"; break;
680 case RRSetConfigFailed: message = "failed"; break;
681 case RRSetConfigInvalidConfigTime: message = "invalid config time"; break;
682 case RRSetConfigInvalidTime: message = "invalid time"; break;
683 default: message = "unknown failure"; break;
686 fprintf (stderr, "%s: Configure crtc %d %s\n", program_name, c, message);
687 revert ();
688 exit (1);
691 static void
692 apply (void)
694 Status s;
695 int c;
698 * Turn off any crtcs which are to be disabled or which are
699 * larger than the target size
701 for (c = 0; c < res->ncrtc; c++)
703 crtc_t *crtc = &crtcs[c];
704 XRRCrtcInfo *crtc_info = crtc->crtc_info;
707 * if this crtc is already disabled, skip it
708 * Note server sets crtc_info->mode (before change)
710 if (crtc_info->mode == None)
711 continue;
714 * If this crtc is to be left enabled, make
715 * sure the old size fits then new screen
716 * When crtc->mode_info is null, the crtc is to be
717 * disabled. Note set_crtcs () sets crtc->mode_info for
718 * new mode (to be changed to).
720 if (crtc->mode_info)
722 XRRModeInfo *old_mode = find_mode_by_xid (crtc_info->mode);
723 int x, y, w, h;
725 if (!old_mode)
726 panic (RRSetConfigFailed, crtc);
728 /* old position and size information */
729 x = crtc_info->x;
730 y = crtc_info->y;
731 w = mode_width (old_mode, crtc_info->rotation);
732 h = mode_height (old_mode, crtc_info->rotation);
734 /* if it fits, skip it */
735 if (x + w <= fb_width && y + h <= fb_height)
736 continue;
739 if (need_off_deferred)
740 /* Defer taking off */
741 continue;
743 s = crtc_disable (crtc);
744 if (s != RRSetConfigSuccess)
745 panic (s, crtc);
749 * Hold the server grabbed while messing with
750 * the screen so that apps which notice the resize
751 * event and ask for xinerama information from the server
752 * receive up-to-date information
754 XGrabServer (dpy);
757 * Set the screen size
759 screen_apply ();
762 * Set crtcs
765 for (c = 0; c < res->ncrtc; c++)
767 crtc_t *crtc = &crtcs[c];
769 s = crtc_apply (crtc);
770 if (s != RRSetConfigSuccess)
771 panic (s, crtc);
774 * Release the server grab and let all clients
775 * respond to the updated state
777 XUngrabServer (dpy);
781 * Use current output state to complete the output list
783 static void
784 get_outputs (void)
786 int o;
788 for (o = 0; o < res->noutput; o++)
790 XRROutputInfo *output_info = XRRGetOutputInfo (dpy, res, res->outputs[o]);
791 output_t *output;
792 name_t output_name;
794 if (!output_info) fatal ("could not get output 0x%x information", res->outputs[o]);
795 set_name_xid (&output_name, res->outputs[o]);
796 set_name_index (&output_name, o);
797 set_name_string (&output_name, output_info->name);
798 output = find_output (&output_name);
799 if (!output)
801 output = add_output ();
802 set_name_all (&output->output, &output_name);
805 set_output_info (output, res->outputs[o], output_info);
811 * Test whether 'crtc' can be used for 'output'
813 static Bool
814 check_crtc_for_output (crtc_t *crtc, output_t *output)
816 int i;
817 int l;
818 output_t *other;
820 for (i = 0; i < ncon; i++)
822 other = con_outputs[i].output;
824 if (other == output)
825 continue;
827 if (other->mode_info == NULL)
828 continue;
830 if (other->crtc_info != crtc)
831 continue;
833 /* see if the output connected to the crtc can clone to this output */
834 for (l = 0; l < output->output_info->nclone; l++)
835 if (output->output_info->clones[l] == other->output.xid)
836 break;
837 /* not on the list, can't clone */
838 if (l == output->output_info->nclone)
839 return False;
842 if (crtc->noutput)
844 for (i = 0; i < crtc->noutput; i++)
845 /* Check if the output is to be turned on */
846 if (crtc->outputs[i]->mode_info) {
847 /* make sure the state matches */
848 if (crtc->mode_info != output->mode_info)
849 return False;
850 if (crtc->x != output->x)
851 return False;
852 if (crtc->y != output->y)
853 return False;
854 if (crtc->rotation != output->rotation)
855 return False;
858 return True;
861 static crtc_t *
862 find_crtc_for_output (output_t *output)
864 int c;
866 for (c = 0; c < output->output_info->ncrtc; c++)
868 crtc_t *crtc;
870 crtc = find_crtc_by_xid (output->output_info->crtcs[c]);
871 if (!crtc) fatal ("cannot find crtc 0x%x\n", output->output_info->crtcs[c]);
873 if (check_crtc_for_output (crtc, output))
874 return crtc;
876 return NULL;
879 static void
880 set_positions (void)
882 Bool keep_going;
883 Bool any_set;
884 int min_x, min_y;
885 int i;
887 for (;;)
889 any_set = False;
890 keep_going = False;
891 for (i = 0; i < ncon; i++)
893 output_t *output = con_outputs[i].output;
894 output_t *relation;
896 if (!(output->changes & changes_relation)) continue;
898 if (output->mode_info == NULL) continue;
900 relation = output->relative_to;
902 if (relation->mode_info == NULL)
904 output->x = 0;
905 output->y = 0;
906 output->changes |= changes_position;
907 any_set = True;
908 continue;
911 * Make sure the dependent object has been set in place
913 if ((relation->changes & changes_relation) &&
914 !(relation->changes & changes_position))
916 keep_going = True;
917 continue;
920 switch (output->relation) {
921 case left_of:
922 output->y = relation->y;
923 output->x = relation->x - mode_width (output->mode_info, output->rotation);
924 break;
925 case right_of:
926 output->y = relation->y;
927 output->x = relation->x + mode_width (relation->mode_info, relation->rotation);
928 break;
929 case above:
930 output->x = relation->x;
931 output->y = relation->y - mode_height (output->mode_info, output->rotation);
932 break;
933 case below:
934 output->x = relation->x;
935 output->y = relation->y + mode_height (relation->mode_info, relation->rotation);
936 break;
937 case same_as:
938 output->x = relation->x;
939 output->y = relation->y;
941 output->changes |= changes_position;
942 relation->changes |= changes_position;
943 any_set = True;
945 if (!keep_going)
946 break;
947 if (!any_set)
948 fatal ("loop in relative position specifications\n");
952 * Now normalize positions so the upper left corner of all outputs is at 0,0
954 min_x = 32768;
955 min_y = 32768;
956 for (i = 0; i < ncon; i++)
958 output_t *output = con_outputs[i].output;
960 if (output->mode_info == NULL) continue;
962 if (output->x < min_x) min_x = output->x;
963 if (output->y < min_y) min_y = output->y;
965 if (min_x || min_y)
967 /* move all outputs */
968 for (i = 0; i < ncon; i++)
970 output_t *output = con_outputs[i].output;
972 if (output->mode_info == NULL) continue;
974 output->x -= min_x;
975 output->y -= min_y;
976 output->changes |= changes_position;
981 static Bool
982 set_screen_size (void)
984 int i;
986 fb_width = fb_height = 0;
988 for (i = 0; i < ncon; i++)
990 output_t *output = con_outputs[i].output;
991 XRRModeInfo *mode_info = output->mode_info;
992 int x, y, w, h;
994 if (!mode_info) continue;
996 x = output->x;
997 y = output->y;
998 w = mode_width (mode_info, output->rotation);
999 h = mode_height (mode_info, output->rotation);
1000 if (x + w > fb_width) fb_width = x + w;
1001 if (y + h > fb_height) fb_height = y + h;
1004 if (fb_width > maxWidth || fb_height > maxHeight) {
1005 if (verbose)
1006 fprintf (stderr, "screen cannot be larger than %dx%d (desired size %dx%d)\n",
1007 maxWidth, maxHeight, fb_width, fb_height);
1008 return False;
1011 if (fb_width < minWidth) fb_width = minWidth;
1012 if (fb_height < minHeight) fb_height = minHeight;
1014 return True;
1017 static void
1018 disable_outputs (output_t *outputs)
1020 while (outputs)
1022 outputs->crtc_info = NULL;
1023 outputs = outputs->next;
1028 * find the best mapping from output to crtc available
1030 static int
1031 pick_crtcs_score (output_t *outputs)
1033 output_t *output;
1034 int best_score;
1035 int my_score;
1036 int score;
1037 crtc_t *best_crtc;
1038 int c;
1040 if (!outputs)
1041 return 0;
1043 output = outputs;
1044 outputs = outputs->next;
1046 * Score with this output disabled
1048 output->crtc_info = NULL;
1049 best_score = pick_crtcs_score (outputs);
1050 if (output->mode_info == NULL)
1051 return best_score;
1053 best_crtc = NULL;
1055 * Now score with this output any valid crtc
1057 for (c = 0; c < output->output_info->ncrtc; c++)
1059 crtc_t *crtc;
1061 crtc = find_crtc_by_xid (output->output_info->crtcs[c]);
1062 if (!crtc)
1063 fatal ("cannot find crtc 0x%x\n", output->output_info->crtcs[c]);
1065 /* reset crtc allocation for following outputs */
1066 disable_outputs (outputs);
1067 if (!check_crtc_for_output (crtc, output))
1068 continue;
1070 my_score = 1000;
1071 /* slight preference for existing connections */
1072 if (crtc == output->current_crtc_info)
1073 my_score++;
1075 output->crtc_info = crtc;
1076 score = my_score + pick_crtcs_score (outputs);
1077 if (score > best_score)
1079 best_crtc = crtc;
1080 best_score = score;
1083 if (output->crtc_info != best_crtc)
1084 output->crtc_info = best_crtc;
1086 * Reset other outputs based on this one using the best crtc
1088 (void) pick_crtcs_score (outputs);
1090 return best_score;
1094 * Pick crtcs for any changing outputs that don't have one
1096 static Bool
1097 pick_crtcs (void)
1099 output_t *output;
1100 int i;
1103 * First try to match up newly enabled outputs with spare crtcs
1105 for (i = 0; i < ncon; i++)
1107 output = con_outputs[i].output;
1109 if (output->mode_info)
1111 if (output->crtc_info) {
1112 if (output->crtc_info->crtc_info->noutput > 0 &&
1113 (output->crtc_info->crtc_info->noutput > 1 ||
1114 output != find_output_by_xid (output->crtc_info->crtc_info->outputs[0])))
1115 break;
1116 } else {
1117 output->crtc_info = find_crtc_for_output (output);
1118 if (!output->crtc_info)
1119 break;
1120 else {
1121 if (verbose)
1122 fprintf(stderr, "picked crtc 0x%lx for output %d (%s)\n",
1123 output->crtc_info->crtc.xid, i, output->output_info->name);
1130 * Everyone is happy
1132 if (i == ncon)
1133 return True;
1135 * When the simple way fails, see if there is a way
1136 * to swap crtcs around and make things work
1138 for (output = outputs; output; output = output->next)
1139 output->current_crtc_info = output->crtc_info;
1140 pick_crtcs_score (outputs);
1141 for (output = outputs; output; output = output->next)
1143 if (output->mode_info && !output->crtc_info) {
1144 if (verbose)
1145 fprintf (stderr, "cannot find crtc for output %s\n",
1146 output->output.string);
1147 return False;
1151 return True;
1154 static Bool
1155 probe_and_check_output_changes (void) {
1156 XRRScreenResources *new_res = NULL;
1157 int changed = False;
1158 int i;
1160 new_res = XRRGetScreenResources (dpy, root);
1161 if (!new_res)
1162 fatal ("could not get screen resources");
1164 if ((new_res->noutput != res->noutput) || (new_res->nmode != res->nmode) ||
1165 (new_res->ncrtc != res->ncrtc))
1166 changed = True;
1167 else {
1168 for (i = 0; i < new_res->noutput; i++)
1169 if (new_res->outputs[i] != res->outputs[i]) {
1170 changed = True;
1171 break;
1173 for (i = 0; i < new_res->nmode; i++)
1174 if (new_res->modes[i].id != res->modes[i].id) {
1175 changed = True;
1176 break;
1178 for (i = 0; i < new_res->ncrtc; i++) {
1179 crtc_t *crtc = NULL; /* old */
1180 XRRCrtcInfo *crtc_info = NULL; /* new */
1182 crtc = find_crtc_by_xid (res->crtcs[i]);
1183 crtc_info = XRRGetCrtcInfo (dpy, new_res,
1184 new_res->crtcs[i]);
1186 if (!crtc || !crtc_info) {
1187 changed = True;
1188 break;
1190 if (!crtc->mode_info && !find_mode_by_xid (crtc_info->mode))
1191 continue;
1192 if ((crtc_info->x != crtc->x) ||
1193 (crtc_info->y != crtc->y) ||
1194 (find_mode_by_xid (crtc_info->mode) != crtc->mode_info) ||
1195 (crtc_info->rotation != crtc->rotation)) {
1196 changed = True;
1197 break;
1202 if (changed) {
1203 if (res)
1204 XRRFreeScreenResources(res);
1205 res = new_res;
1207 if (verbose)
1208 fprintf(stderr, "probed: output status changed\n");
1209 return True;
1212 if (verbose)
1213 fprintf(stderr, "probed: no output status change\n");
1214 return False;
1217 static Bool
1218 need_probe (void) {
1219 struct timeval cur_time_val;
1220 long long cur, prev;
1222 X_GETTIMEOFDAY(&cur_time_val);
1223 cur = (long long) cur_time_val.tv_sec * 1000000 + cur_time_val.tv_usec;
1224 prev =(long long) time_val.tv_sec * 1000000 + time_val.tv_usec;
1225 if (((cur - prev) < 0) || ((cur - prev) > 5000000))
1226 return True;
1227 else
1228 return False;
1231 static int
1232 mode_sort (const void *p1, void *p2)
1234 XRRModeInfo *mi1 = * (XRRModeInfo **) p1;
1235 XRRModeInfo *mi2 = * (XRRModeInfo **) p2;
1237 if ((mi1->width == mi2->width) && (mi1->height == mi2->height)) {
1238 if (mode_refresh(mi1) && mode_refresh(mi2)) {
1239 if (mode_refresh(mi1) < mode_refresh(mi2))
1240 return 1;
1241 if (mode_refresh(mi1) > mode_refresh(mi2))
1242 return -1;
1243 } else
1244 return 0;
1247 if ((mi1->width == mi2->width) && (mi1->height < mi2->height))
1248 return 1;
1249 if ((mi1->width == mi2->width) && (mi1->height > mi2->height))
1250 return -1;
1251 if (mi1->width < mi2->width)
1252 return 1;
1253 if (mi1->width > mi2->width)
1254 return -1;
1256 return 0;
1259 static int
1260 output_sort (const void *p1, const void *p2) {
1261 con_output_t co1 = * (con_output_t *) p1;
1262 con_output_t co2 = * (con_output_t *) p2;
1263 int ncrtc1 = co1.output->output_info->ncrtc;
1264 int ncrtc2 = co2.output->output_info->ncrtc;
1265 char *name1 = co1.output->output_info->name;
1266 char *name2 = co2.output->output_info->name;
1268 if (ncrtc1 == ncrtc2)
1269 return (strcmp(name1, name2));
1270 if (ncrtc1 < ncrtc2)
1271 return -1;
1273 return 1;
1276 static Bool
1277 get_common_mode(con_output_t *c0, con_output_t *c1, int *m0, int *m1) {
1278 int i, j;
1279 int i1 = -1, j1 = -1, i2 = -1, j2 = -1;
1280 int x, y, w, h;
1281 output_t *output = c0->output;
1283 *m0 = -1;
1284 *m0 = -1;
1285 if (!c0 ||!c1 || !c0->smodes || !c1->smodes)
1286 return False;
1288 /* first try to find mode with common same size */
1289 for (i = 0; i < c0->nsmodes; i ++) {
1290 for (j = 0; j < c1->nsmodes; j ++)
1291 if ((c0->smodes[i]->width == c1->smodes[j]->width) &&
1292 (c0->smodes[i]->height == c1->smodes[j]->height)) {
1293 x = output->x;
1294 y = output->y;
1295 w = mode_width (c0->smodes[i], output->rotation);
1296 h = mode_height (c0->smodes[i], output->rotation);
1297 if ((x + w <= maxWidth) && (y + h <= maxHeight)) {
1298 i1 = i; j1 = j;
1299 break;
1302 if ((i1 != -1) && (j1 != -1))
1303 break;
1306 if ((i1 == -1) && (j1 == -1))
1307 return False;
1309 /* then try to find mode with common id for possible cloning */
1310 for (i = 0; i < c0->nsmodes; i ++) {
1311 for (j = 0; j < c1->nsmodes; j ++)
1312 if (c0->smodes[i] == c1->smodes[j]) {
1313 x = output->x;
1314 y = output->y;
1315 w = mode_width (c0->smodes[i], output->rotation);
1316 h = mode_height (c0->smodes[i], output->rotation);
1317 if ((x + w <= maxWidth) && (y + h <= maxHeight)) {
1318 i2 = i; j2 = j;
1319 break;
1322 if ((i2 != -1) && (j2 != -1))
1323 break;
1326 if ((i2 == -1) && (j2 == -1)) {
1327 *m0 = i1;
1328 *m1 = j1;
1329 } else {
1330 /* use common id if it is not smaller */
1331 if ((mode_width (c0->smodes[i1], output->rotation) >
1332 mode_width (c0->smodes[i2], output->rotation)) &&
1333 (mode_height (c0->smodes[i1], output->rotation) >
1334 mode_height (c0->smodes[i2], output->rotation))) {
1335 *m0 = i1;
1336 *m1 = j1;
1337 } else {
1338 *m0 = i2;
1339 *m1 = j2;
1343 return True;
1346 static XRRModeInfo *
1347 get_largest_mode (con_output_t *c, XRRModeInfo *start_mode) {
1348 int i, found = False;
1349 output_t *output = c->output;
1351 for (i = 0; i < c->nsmodes; i++) {
1352 XRRModeInfo *mode_info = c->smodes[i];
1353 int x, y, w, h;
1355 if (!found && (start_mode != mode_info))
1356 continue;
1357 else
1358 found = True;
1360 if (mode_info) {
1361 x = output->x;
1362 y = output->y;
1363 w = mode_width (mode_info, output->rotation);
1364 h = mode_height (mode_info, output->rotation);
1365 if ((x + w <= maxWidth) && (y + h <= maxHeight))
1366 break;
1370 if (i < c->nsmodes)
1371 return c->smodes[i];
1372 else
1373 fatal("cannot find mode");
1377 static Bool
1378 valid_mode(con_output_t *con, XRRModeInfo *mode) {
1379 int i;
1381 for (i = 0; i < con->nsmodes; i++)
1382 if (con->smodes[i] == mode)
1383 return True;
1385 return False;
1388 static void
1389 do_init (void)
1391 int i, j;
1392 output_t *output;
1394 /* Initialize con_outputs array */
1395 for (i = 0; i < MAX_OUTPUT; i++) {
1396 con_outputs[i].output = NULL;
1397 con_outputs[i].on = False;
1398 start_mode[i] = NULL;
1399 new_mode[i] = NULL;
1402 ncon = 0;
1403 dis_ncon = 0;
1404 init_rotation = RR_Rotate_0;
1405 init_x = 0;
1406 init_y = 0;
1407 get_crtcs ();
1408 get_outputs ();
1410 for (output = outputs; output; output = output->next) {
1411 XRROutputInfo *output_info = output->output_info;
1413 if (output_info->connection == RR_Connected) {
1414 con_outputs[ncon].output = output;
1415 con_outputs[ncon].nsmodes = 0;
1416 for (j = 0; j < output_info->nmode; j++) {
1417 XRRModeInfo *rmode = find_mode_by_xid (output_info->modes[j]);
1419 con_outputs[ncon].smodes =
1420 realloc(con_outputs[ncon].smodes,
1421 (con_outputs[ncon].nsmodes + 1) * sizeof (XRRModeInfo *));
1422 con_outputs[ncon].smodes[j] = rmode;
1423 con_outputs[ncon].nsmodes ++;
1426 /* Sort the modes */
1427 qsort((void *) con_outputs[ncon].smodes,
1428 con_outputs[ncon].nsmodes, sizeof(XRRModeInfo *),
1429 (int (*) (const void *, const void *)) mode_sort);
1431 if (output_info->crtc) {
1432 crtc_t *crtc;
1434 con_outputs[ncon].on = True;
1435 for (j = 0; j < output_info->ncrtc; j++) {
1436 if (output_info->crtcs[j] == output_info->crtc)
1437 break;
1438 if (j == output_info->ncrtc) {
1439 if (verbose)
1440 fatal ("crtc does not match for output\n");
1443 /* set initial mode_info */
1444 crtc = find_crtc_by_xid (output_info->crtc);
1445 if (crtc)
1446 con_outputs[ncon].output->mode_info =
1447 find_mode_by_xid (crtc->crtc_info->mode);
1449 else
1450 con_outputs[ncon].on = False;
1451 ncon ++;
1452 } else if (output_info->connection == RR_Disconnected) {
1453 dis_con_outputs[dis_ncon].output = output;
1454 dis_ncon ++;
1458 qsort((void **) con_outputs, ncon,
1459 sizeof(con_output_t), (int (*) (const void *, const void *)) output_sort);
1461 if (verbose) {
1462 fprintf(stderr, "Total connected outputs = %d :\n", ncon);
1463 for (j = 0; j < ncon; j++) {
1464 fprintf(stderr, "%d (%s): top mode = %s, rotation = %d, crtcs =", j,
1465 con_outputs[j].output->output_info->name,
1466 con_outputs[j].smodes[0]->name,
1467 con_outputs[j].output->rotation);
1468 for (i = 0; i < con_outputs[j].output->output_info->ncrtc; i++)
1469 fprintf(stderr, " 0x%lx", con_outputs[j].output->output_info->crtcs[i]);
1470 fprintf(stderr, ", using 0x%lx", con_outputs[j].output->output_info->crtc);
1471 fprintf(stderr, "\n");
1473 fprintf(stderr, "Total disconnected outputs = %d :\n", dis_ncon);
1474 for (j = 0; j < dis_ncon; j++) {
1475 fprintf(stderr, "%d (%s) : number of crtcs %d =", j,
1476 dis_con_outputs[j].output->output_info->name,
1477 dis_con_outputs[j].output->output_info->ncrtc);
1478 for (i = 0; i < dis_con_outputs[j].output->output_info->ncrtc; i++)
1479 fprintf(stderr, " 0x%lx", dis_con_outputs[j].output->output_info->crtcs[i]);
1480 fprintf(stderr, ", using 0x%lx", dis_con_outputs[j].output->output_info->crtc);
1481 fprintf(stderr, "\n");
1485 i = con_outputs[2].on * 4 + con_outputs[1].on * 2 + con_outputs[0].on;
1487 if ((i == 1) || (i == 2) || (i == 4)) {
1488 use_init_pos = True;
1489 j = i >> 1;
1491 /* remember position and mode info in single state */
1492 start_mode[j] = con_outputs[j].output->mode_info;
1493 init_rotation = con_outputs[j].output->rotation;
1494 init_x = con_outputs[j].output->x;
1495 init_y = con_outputs[j].output->y;
1496 } else
1497 use_init_pos = False;
1499 if ((ncon != 2) || (start < 3))
1500 start = i;
1502 if ((ncon < 1) || (ncon > 3)) {
1503 if ((ncon < 1) && verbose)
1504 fprintf (stderr, "warn: no connection\n");
1505 else if ((ncon > 3) && verbose)
1506 fprintf (stderr, "warn: too many (more than 3) connections: %d: can't switch\n", ncon);
1507 do_not_switch = True;
1510 did_init = True;
1512 return;
1515 static int
1516 grab_key (Display *dpy, int keysym, unsigned int modifier,
1517 Window grab_window)
1519 char msg[256];
1520 int keycode = XKeysymToKeycode(dpy, keysym);
1522 if (keycode == NoSymbol)
1523 fatal ("grab_key: keycode not defined for keysym 0x%x\n", keysym);
1525 had_error = 0;
1526 prev_handler = XSetErrorHandler (cur_handler);
1528 if (!testrun) {
1529 XGrabKey(dpy,
1530 keycode,
1531 modifier,
1532 root, True, GrabModeAsync, GrabModeAsync);
1533 XSync (dpy, False);
1536 XSetErrorHandler (prev_handler);
1537 if (had_error) {
1538 XGetErrorText (dpy, had_error, msg, sizeof (msg));
1539 fatal ("XGrabKey: %s\n", msg);
1542 if (verbose)
1543 fprintf(stderr, "keycode to grab: %d\n", keycode);
1545 return keycode;
1548 static Bool
1549 do_switch (void)
1551 int i, j;
1552 int single;
1553 int save = -1;
1555 if (ncon <= 0)
1556 return True;
1558 for (i = 0; i < ncon; i++) {
1559 output_t *output = con_outputs[i].output;
1561 new_mode[i] = NULL;
1562 output->relation = same_as;
1563 output->relative_to = NULL;
1564 if (use_init_pos) {
1565 output->x = init_x;
1566 output->y = init_y;
1567 output->rotation = init_rotation;
1568 } else {
1569 output->x = 0;
1570 output->y = 0;
1574 if (ncon == 2) {
1575 if (!nosideview) {
1576 if (++start > 5) start = 1;
1578 else {
1579 if (++start > 3) start = 1;
1582 if (verbose)
1583 fprintf(stderr, "current state = %d\n", start);
1584 if (start >= 3) {
1585 int m0, m1;
1587 if (get_common_mode(&con_outputs[0], &con_outputs[1], &m0, &m1)) {
1588 new_mode[0] = con_outputs[0].smodes[m0];
1589 new_mode[1] = con_outputs[1].smodes[m1];
1590 } else {
1591 new_mode[0] = get_largest_mode (&con_outputs[0],
1592 con_outputs[0].smodes[0]);
1593 new_mode[1] = get_largest_mode (&con_outputs[1],
1594 con_outputs[1].smodes[0]);
1596 } else {
1597 if (start_mode[start -1] && valid_mode(&con_outputs[start -1],
1598 start_mode[start -1]))
1599 new_mode[start -1] = start_mode[start -1];
1600 else {
1601 if (con_outputs[start -1].smodes[0])
1602 new_mode[start -1] =
1603 get_largest_mode (&con_outputs[start-1],
1604 con_outputs[start -1].smodes[0]);
1609 if (ncon == 3) {
1610 if (++start > 6) start = 1;
1611 if (verbose)
1612 fprintf(stderr, "current state = %d\n", start);
1613 if ((start == 1) || (start == 2) || (start == 4)) {
1614 single = 1;
1615 i = start >> 1;
1616 j = 0;
1618 else {
1619 single = 0;
1620 if (start > 4)
1621 j = 2;
1622 else
1623 j = 1;
1624 if (start > 5)
1625 i = 1;
1626 else
1627 i = 0;
1630 if (single) {
1631 if (start_mode[i] && valid_mode(&con_outputs[i], start_mode[i]))
1632 new_mode[i] = start_mode[i];
1633 else {
1634 if (con_outputs[i].smodes[0])
1635 new_mode[i] = get_largest_mode (&con_outputs[i],
1636 con_outputs[i].smodes[0]);
1639 else {
1640 int m0, m1;
1642 if (get_common_mode(&con_outputs[i], &con_outputs[j], &m0, &m1)) {
1643 new_mode[i] = con_outputs[i].smodes[m0];
1644 new_mode[j] = con_outputs[j].smodes[m1];
1645 } else {
1646 new_mode[i] = get_largest_mode (&con_outputs[i],
1647 con_outputs[i].smodes[0]);
1648 new_mode[j] = get_largest_mode (&con_outputs[j],
1649 con_outputs[j].smodes[0]);
1654 if (ncon == 1) {
1655 if (start_mode[0] && valid_mode(&con_outputs[0], start_mode[0]))
1656 new_mode[0] = start_mode[0];
1657 else {
1658 if (con_outputs[0].smodes[0])
1659 new_mode[0] = get_largest_mode (&con_outputs[0],
1660 con_outputs[0].smodes[0]);
1664 /* Set mode */
1665 for (i = 0; i < ncon; i++) {
1666 output_t *output;
1668 output = con_outputs[i].output;
1669 if (new_mode[i]) {
1670 if ((!output->mode_info) || (output->mode_info != new_mode[i])) {
1671 output->mode_info = new_mode[i];
1672 con_outputs[i].on = True;
1673 if (verbose)
1674 fprintf(stderr, "set output %d (%s) to mode %s rotation %d\n", i,
1675 con_outputs[i].output->output_info->name,
1676 con_outputs[i].output->mode_info->name,
1677 con_outputs[i].output->rotation);
1679 } else if (con_outputs[i].on ) {
1680 if (!need_off_deferred) {
1681 output->mode_info = NULL;
1682 con_outputs[i].on = False;
1683 if (verbose)
1684 fprintf(stderr, "turn off output %d (%s) \n",
1685 i, con_outputs[i].output->output_info->name);
1686 } else
1687 save = i;
1691 if ((ncon == 2) && (start >= 4)) {
1692 if (start == 4) {
1693 con_outputs[1].output->relative_to = con_outputs[0].output;
1694 con_outputs[1].output->relation = right_of;
1695 con_outputs[1].output->changes = changes_relation;
1696 con_outputs[0].output->changes = 0;
1698 else if (start == 5) {
1699 con_outputs[0].output->relative_to = con_outputs[1].output;
1700 con_outputs[0].output->relation = right_of;
1701 con_outputs[0].output->changes = changes_relation;
1702 con_outputs[1].output->changes = 0;
1705 set_positions();
1708 if (!set_screen_size ())
1709 return False;
1712 /* reset crtcs before allocation */
1713 reset_crtcs_for_outputs();
1715 if (!did_init)
1716 get_crtcs();
1718 if (!pick_crtcs()) {
1719 if (verbose)
1720 fprintf(stderr, "pick_crtcs failed\n");
1721 return True;
1724 set_crtcs ();
1725 apply();
1727 if (need_off_deferred && (save != -1)) {
1728 /* Now, take the deferred output off */
1729 output_t *output;
1730 crtc_t *crtc;
1731 Status s;
1733 output = con_outputs[save].output;
1734 output->mode_info = NULL;
1735 con_outputs[save].on = False;
1736 if (verbose)
1737 fprintf(stderr, "turn off output %d (%s) \n",
1738 save, con_outputs[save].output->output_info->name);
1740 crtc = output->crtc_info;
1741 s = crtc_disable (crtc);
1742 if (s != RRSetConfigSuccess)
1743 panic (s, crtc);
1746 XSync (dpy, False);
1748 did_init = False;
1750 return True;
1754 static Bool
1755 do_toggle (void)
1757 Atom atom;
1758 XEvent xev;
1759 int ret;
1761 atom = XInternAtom (dpy, "DISPLAYSWITCH_DAEMON", True);
1762 if (!atom) {
1763 fprintf(stderr, "dispswitch daemon not running\n");
1764 return False;
1767 win = XGetSelectionOwner (dpy, atom);
1768 if (!win) {
1769 fprintf(stderr, "dispswitch: No owner of dispswitch daemon is found\n");
1770 return False;
1773 bzero (&xev, sizeof (XEvent));
1774 xev.xkey.type = KeyPress;
1775 xev.xkey.send_event = True;
1776 xev.xkey.display = dpy;
1777 /* Any keycode */
1778 xev.xkey.keycode = 71;
1782 * Send another instance of dispswitch (a daemon) an event to
1783 * request a switch
1785 ret = XSendEvent(dpy, win, False, KeyPressMask, &xev);
1786 XFlush(dpy);
1788 if (!ret)
1789 fprintf(stderr, "dispswitch: XSendEvent error\n");
1791 return (!ret);
1795 main (int argc, char **argv)
1797 char *display_name = NULL;
1798 int major, minor;
1799 int i;
1800 char msg[256];
1801 XEvent ev;
1802 unsigned int modifier = 0;
1803 Bool key_given = False;
1804 Bool mod_given = False;
1805 int keysym = 0, toggle = False, listen = False;
1807 Atom atom;
1809 program_name = argv[0];
1811 for (i = 1; i < argc; i++) {
1812 if (!strcmp ("-display", argv[i]) || !strcmp ("-d", argv[i])) {
1813 if (++i>=argc) usage ();
1814 display_name = argv[i];
1815 continue;
1817 if (!strcmp ("-key", argv[i]) || !strcmp ("-k", argv[i])) {
1818 if (++i>=argc) usage ();
1819 if ((keysym = XStringToKeysym(argv[i])) == NoSymbol) {
1820 fprintf(stderr, "invalid keysym: -key %s\n", argv[i]);
1821 usage();
1823 key_given = True;
1824 continue;
1826 if (!strcmp ("-mod", argv[i]) || !strcmp ("-m", argv[i])) {
1827 int j;
1828 char *s, *p, *q;
1829 int end = 0;
1831 if (++i>=argc) usage ();
1832 s = strdup (argv[i]);
1833 if (!s) {
1834 if (verbose)
1835 fprintf(stderr, "modifier failed, will use default modifier\n");
1836 continue;
1838 while (*s == ' ') s++;
1839 p = s + strlen(s) - 1;
1840 while (*p == ' ') *p-- = 0;
1841 q = s;
1842 for (; ;) {
1843 if (p = strchr(s, '+')) {
1844 *p = ' ';
1845 while ((p > s) && (*(p-1) == ' ')) p--;
1846 *p = 0;
1848 else
1849 end = 1;
1850 for (j = 0; j < MAX_MODIFIERS; j++) {
1851 if (!strcmp(mod_key_table[j].modname, s)) {
1852 modifier |= mod_key_table[j].mod;
1853 break;
1856 if (j == MAX_MODIFIERS) {
1857 fprintf(stderr, "invalid modifier: -mod %s\n", q);
1858 usage();
1860 if (end)
1861 break;
1862 else {
1863 s = ++p;
1864 while (*s == ' ') s++;
1867 mod_given = True;
1868 free (q);
1869 continue;
1871 if (!strcmp ("-nosideview", argv[i])) {
1872 nosideview = True;
1873 continue;
1875 if (!strcmp ("-verbose", argv[i]) || !strcmp ("-v", argv[i])) {
1876 verbose = True;
1877 continue;
1879 if (!strcmp ("-testrun", argv[i])) {
1880 testrun = True;
1881 verbose = True;
1882 continue;
1884 if (!strcmp ("-toggle", argv[i]) || !strcmp ("-t", argv[i])) {
1885 toggle = True;
1886 continue;
1888 if (!strcmp ("-listen", argv[i]) || !strcmp ("-l", argv[i])) {
1889 listen = True;
1890 continue;
1892 usage();
1895 dpy = XOpenDisplay (display_name);
1897 if (dpy == NULL)
1898 fatal ("can't open display %s\n", XDisplayName(display_name));
1900 screen = DefaultScreen (dpy);
1901 root = RootWindow (dpy, screen);
1903 if (!XRRQueryVersion (dpy, &major, &minor))
1904 fatal ("randr extension missing\n");
1906 if ((major <= 1) && (major != 1 || minor < 2))
1907 fatal ("wrong randr version: %d.%d\n", major, minor);
1910 if (toggle)
1911 exit (do_toggle());
1914 * Create an atom, a trivial window, and make it selection owner.
1915 * Ready to accept a client event request for switch
1917 atom = XInternAtom(dpy, "DISPLAYSWITCH_DAEMON", False);
1918 if (!atom) {
1919 if (verbose)
1920 fprintf(stderr, "cannot create Atom\n");
1922 else {
1923 if (XGetSelectionOwner (dpy, atom)) {
1924 if (verbose)
1925 fprintf(stderr, "dispswitch daemon is already running, quit\n");
1926 exit (1);
1928 win = XCreateSimpleWindow(dpy, root, 0, 0, 10, 10, 0, 10, 0);
1929 if (!win) {
1930 if (verbose)
1931 fprintf(stderr, "cannot create window\n");
1933 else {
1934 XSetSelectionOwner(dpy, atom, win, CurrentTime);
1935 if (XGetSelectionOwner(dpy, atom) != win) {
1936 if (verbose)
1937 fprintf(stderr, "set selection owner failed\n");
1938 } else
1939 XSelectInput(dpy, win, KeyPressMask);
1943 /* set default key and modifier if not given in command */
1944 if (!key_given)
1945 keysym = XStringToKeysym ("F5");
1946 if (!mod_given)
1947 modifier = ShiftMask;
1949 if (!listen)
1950 cur_keycode = grab_key (dpy, keysym, modifier, root);
1952 XRRGetScreenSizeRange (dpy, root, &minWidth, &minHeight,
1953 &maxWidth, &maxHeight);
1955 fb_width_mm = DisplayWidthMM (dpy, screen);
1956 fb_height_mm = DisplayHeightMM (dpy, screen);
1957 dpi = (25.4 * DisplayHeight (dpy, screen)) / DisplayHeightMM(dpy, screen);
1959 res = XRRGetScreenResources (dpy, root);
1960 if (!res)
1961 fatal ("could not get screen resources\n");
1962 if (res->ncrtc < 2)
1963 fatal ("too few crtcs: %d\n", res->ncrtc);
1965 do_init();
1967 X_GETTIMEOFDAY(&time_val);
1969 for(;;)
1971 need_off_deferred = False;
1973 if (testrun) {
1974 usleep(4000000);
1975 fprintf(stderr, "\n");
1976 } else
1977 XNextEvent(dpy, &ev);
1979 if (!listen && !testrun && (ev.type == MappingNotify) &&
1980 ((ev.xmapping.request == MappingKeyboard) ||
1981 (ev.xmapping.request == MappingModifier))) {
1982 /* keyboard/modifier mapping changed */
1983 if (verbose)
1984 fprintf(stderr, "\nkeyboard/modifier mapping changed ...\n");
1986 XUngrabKey(dpy, cur_keycode, modifier, root);
1987 cur_keycode = grab_key (dpy, keysym, modifier, root);
1990 if (testrun || (ev.type == KeyPress)) {
1991 if (verbose)
1992 fprintf(stderr, "\na key press event was grabbed ...\n");
1994 do_not_switch = False;
1996 if (testrun || need_probe()) {
1997 /* Too long since last switch, need to check output changes */
1998 if (probe_and_check_output_changes ()) {
1999 output_t *output, *next;
2001 output = outputs;
2002 while (output) {
2003 if (output->output_info)
2004 XRRFreeOutputInfo (output->output_info);
2005 if (output->crtc_info && output->crtc_info->outputs) {
2006 free(output->crtc_info->outputs);
2007 output->crtc_info->outputs = NULL;
2009 next = output->next;
2010 free(output);
2011 output = next;
2013 outputs = NULL;
2014 outputs_tail = &outputs;
2015 for (i = 0; i < ncon; i++) {
2016 con_outputs[i].output = NULL;
2017 con_outputs[i].on = False;
2018 if (con_outputs[i].smodes) {
2019 free(con_outputs[i].smodes);
2020 con_outputs[i].smodes = NULL;
2022 con_outputs[i].nsmodes = 0;
2025 do_init();
2026 } else if (ncon == 1)
2027 do_not_switch = True;
2028 } else if (ncon == 1)
2029 do_not_switch = True;
2031 if (!do_not_switch) {
2032 if ((ncon == 2) && (start == 1))
2034 * Workaround for intel based graphics: in switching from
2035 * LVDS to VGA, off on LVDS needs to be deferred.
2037 need_off_deferred = True;
2038 if (!do_switch()) {
2039 if ((ncon == 2) && (start == 4)) {
2040 start = 5;
2041 if (verbose)
2042 fprintf(stderr, "too small screen, skipping side view\n");
2043 (void) do_switch();
2048 X_GETTIMEOFDAY(&time_val);