pax_global_header00006660000000000000000000000064150674112150014514gustar00rootroot0000000000000052 comment=dfc4db40af455370abae7d228de46a0bc5bdeb9b x-on-resize-0.3/000077500000000000000000000000001506741121500135365ustar00rootroot00000000000000x-on-resize-0.3/.gitignore000066400000000000000000000017631506741121500155350ustar00rootroot00000000000000# # X.Org module default exclusion patterns # The next section if for module specific patterns # # Do not edit the following section # GNU Build System (Autotools) aclocal.m4 autom4te.cache/ autoscan.log ChangeLog compile config.guess config.h config.h.in config.log config-ml.in config.py config.status config.status.lineno config.sub configure configure.scan depcomp .deps/ INSTALL install-sh .libs/ libtool libtool.m4 ltmain.sh lt~obsolete.m4 ltoptions.m4 ltsugar.m4 ltversion.m4 Makefile Makefile.in mdate-sh missing mkinstalldirs *.pc py-compile stamp-h? symlink-tree texinfo.tex ylwrap # Do not edit the following section # Edit Compile Debug Document Distribute *~ *.[0-9] *.[0-9]x *.bak *.bin core *.dll *.exe *-ISO*.bdf *-JIS*.bdf *-KOI8*.bdf *.kld *.ko *.ko.cmd *.lai *.l[oa] *.[oa] *.obj *.patch *.so *.pcf.gz *.pdb *.tar.bz2 *.tar.gz # # Add & Override patterns for xrandr # # Edit the following section as needed # For example, !report.pc overrides *.pc. See 'man gitignore' # x-on-resize x-on-resize-0.3/meson.build000066400000000000000000000025331506741121500157030ustar00rootroot00000000000000# # Copyright © 2011 Keith Packard # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. # project('x-on-resize', 'c', default_options: [ 'buildtype=release', 'warning_level=2', ], license : 'GPL-2', version: '0.3' ) libxrandr = dependency('xrandr', required: true) libx11 = dependency('x11', required: true) conf_data = configuration_data() conf_data.set('PACKAGE_STRING', '"@0@"'.format(meson.project_version())) configure_file(output: 'config.h', configuration: conf_data) configuration_inc = include_directories('.') x_on_resize = executable('x-on-resize', ['x-on-resize.c'], dependencies: [libxrandr, libx11], include_directories : configuration_inc, install: true ) install_man('x-on-resize.1') x-on-resize-0.3/x-on-resize.1000066400000000000000000000051501506741121500160010ustar00rootroot00000000000000.\" .\" Copyright 2013 Keith Packard .\" .\" Permission to use, copy, modify, distribute, and sell this software and its .\" documentation for any purpose is hereby granted without fee, provided that .\" the above copyright notice appear in all copies and that both that .\" copyright notice and this permission notice appear in supporting .\" documentation, and that the name of Keith Packard not be used in .\" advertising or publicity pertaining to distribution of the software without .\" specific, written prior permission. Keith Packard makes no .\" representations about the suitability of this software for any purpose. It .\" is provided "as is" without express or implied warranty. .\" .\" KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, .\" INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO .\" EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR .\" CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, .\" DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER .\" TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR .\" PERFORMANCE OF THIS SOFTWARE. .\" .TH X-ON-RESIZE 1 .SH NAME x-on-resize \- monitor for hotplug or screen reconfigure events .SH SYNOPSIS .B "x-on-resize" [\-\-help] [\-\-display \fIdisplay\fP] [\-\-config \fIconfig-script\fP] [\-\-resize \fIresize-script\fP] [\-\-start] .SH DESCRIPTION .I X-on-resize is used to monitor the X display for output configuration changes and/or screen resize events. When these occur, it can call external programs to respond to the new display environment. .SH OPTIONS .IP "\-h, \-\-help" Print out a summary of the usage and exit. .IP "\-d, \-\-display \fIname\fP" This option selects the X display to use. Note this refers to the X screen abstraction, not the monitor (or output). .IP "\-c, \-\-config \fIconfig-script\fP" This option selects the external script to run when the set of available outputs has changed. If not provided, x-on-resize prints "config" on stdout. .IP "\-r, \-\-resize \fIresize-script\fP" This option selects the external script to run when the size of the root window has changed. If not provided, x-on-resize prints "resize" on stdout. .IP "\-a, \-\-auto" This is the same as \-\-config "xrandr --auto", which sets the system to a reasonable configuration each time the connected set of outputs changes. .IP "\-s, \-\-start" This directs x-on-resize to run any defined scripts at startup time in addition to when the specified conditions occur. .SH "SEE ALSO" Xrandr(3), xrandr(1) .SH AUTHORS Keith Packard, Open Source Technology Center, Intel Corporation. x-on-resize-0.3/x-on-resize.c000066400000000000000000000117521506741121500160700ustar00rootroot00000000000000/* * Copyright © 2011 Keith Packard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include #include #include #include #include #include #include "config.h" struct output_info { struct output_info *next; RROutput output; XRROutputInfo *info; }; static struct output_info *output_info; static struct output_info * find_output_info (RROutput output) { struct output_info *oi; for (oi = output_info; oi; oi = oi->next) if (oi->output == output) return oi; return NULL; } static void clear_output_info (RROutput output) { struct output_info *oi, **prev; for (prev = &output_info; (oi = *prev); prev = &(oi->next)) if (oi->output == output) { XRRFreeOutputInfo (oi->info); *prev = oi->next; free (oi); break; } } /* * Check to see if the monitor attached to an output * is the same */ static int same_monitor(XRROutputInfo *a, XRROutputInfo *b) { int m; if (a->connection != b->connection) return 0; if (a->nmode != b->nmode) return 0; if (a->npreferred != b->npreferred) return 0; for (m = 0; m < a->nmode; m++) if (a->modes[m] != b->modes[m]) return 0; return 1; } static int check_output (Display *dpy, XRRScreenResources *resources, RROutput output) { XRROutputInfo *info; struct output_info *oi; info = XRRGetOutputInfo (dpy, resources, output); if (!info) { clear_output_info(output); return 0; } oi = find_output_info(output); if (oi) { int same = same_monitor(oi->info, info); XRRFreeOutputInfo(oi->info); oi->info = info; return same; } oi = calloc(1, sizeof (struct output_info)); oi->output = output; oi->info = info; oi->next = output_info; output_info = oi; return 0; } int main (int argc, char **argv) { Display *dpy; int event_base, error_base; int major, minor; XEvent ev; XRRNotifyEvent *nev; const char *config = NULL; const char *resize = NULL; const char *display = NULL; int c, o; int start = 0; XRRScreenResources *resources; static struct option opts[] = { { "config", 1, NULL, 'c' }, { "resize", 1, NULL, 'r' }, { "start", 0, NULL, 's' }, { "auto", 0, NULL, 'a' }, { "display", 1, NULL, 'd' }, { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'v' }, { 0, 0, NULL, 0 } }; while ((c = getopt_long(argc, argv, "c:r:d:hsav", opts, NULL)) != -1) { switch (c) { case 'c': config = optarg; break; case 'r': resize = optarg; break; case 'd': display = optarg; break; case 'a': config = "xrandr --auto"; break; case 's': start = 1; break; case 'v': printf("%s\n", PACKAGE_STRING); exit(0); case 'h': default: fprintf(stderr, "Usage: %s --display --config --resize --auto --start --version\n", argv[0]); exit(1); break; } } dpy = XOpenDisplay(display); if (!dpy) { fprintf(stderr, "XOpenDisplay %s failed\n", XDisplayName(display)); exit(1); } if (!XRRQueryExtension (dpy, &event_base, &error_base) || !XRRQueryVersion (dpy, &major, &minor)) { fprintf (stderr, "RandR extension missing on %s\n", XDisplayName(display)); exit (1); } XRRSelectInput(dpy, RootWindow(dpy, 0), RROutputChangeNotifyMask); XSelectInput(dpy, RootWindow(dpy, 0), StructureNotifyMask); /* Get current configuration */ resources = XRRGetScreenResourcesCurrent(dpy, RootWindow(dpy, 0)); for (o = 0; o < resources->noutput; o++) (void) check_output(dpy, resources, resources->outputs[o]); XRRFreeScreenResources (resources); if (start) { if (config) system(config); if (resize) system(resize); } for (;;) { int configed = 0; int resized = 0; do { XNextEvent(dpy, &ev); switch (ev.type - event_base) { case RRNotify: nev = (XRRNotifyEvent *) &ev; if (nev->subtype == RRNotify_OutputChange) { XRROutputChangeNotifyEvent *noev = (XRROutputChangeNotifyEvent *) nev; resources = XRRGetScreenResources(dpy, RootWindow(dpy, 0)); if (!check_output(dpy, resources, noev->output)) configed = 1; XRRFreeScreenResources (resources); } break; } switch (ev.type) { case ConfigureNotify: resized = 1; break; } usleep(100000); } while (XEventsQueued(dpy, QueuedAfterFlush)); if (configed) { if (config) system(config); else printf ("config\n"); } if (resized) { if (resize) system(resize); else printf ("resize\n"); } } } x-on-resize-0.3/xrandr-auto000077500000000000000000000351271506741121500157400ustar00rootroot00000000000000#!/usr/bin/env nickle autoimport Process; autoload ParseArgs; /* Parsed TILE property contents */ typedef struct { int group_id; int flags; int number_h; int number_v; int hpos; int vpos; } tile_t; /* Configuration for one output */ typedef struct { string name; string mode; int mode_id; bool connected; bool primary; int width, height; int x, y; bool is_tile; tile_t tile; } output_t; /* Configuration for one monitor (collection of outputs) */ typedef struct { string name; int group_id; bool primary; int leader; int width; int height; int x; int y; (*output_t)[...] outputs; } monitor_t; /* Overall screen configuration */ typedef struct { int width; int height; } screen_t; /* List the existing manually configured monitors */ string[] get_existing_monitors() { file input = popen(popen_direction.read, false, "xrandr", "xrandr", "--listmonitors"); string[...] names = {}; while (!File::end(input)) { string line = File::fgets(input); string[] words = String::wordsplit(line, " \t"); if (words[0] != "Monitors:" && dim(words) >= 2) { string name = words[1]; if (name[0] != '+') { if (name[0] == '*') name = String::substr(name, 1, String::length(name) - 1); names[dim(names)] = name; } } } return names; } const int priority_any = 0; const int priority_preferred = 1; const int priority_current = 2; typedef enum { start, start_output, output, skip_output, mode_get, mode_skip, done } state_t; /* Parse xrandr results to compute the set of available monitors and * current screen size */ output_t[] get_outputs(*screen_t screen) { file input = popen(popen_direction.read, false, "xrandr", "xrandr", "--verbose", "--prop"); output_t[...] outputs = {}; output_t output; int mode_priority; state_t state = state_t.start; string line; string[] words; void add_output() { switch (state) { case state_t.skip_output: case state_t.output: case state_t.mode_skip: case state_t.mode_get: # printf ("add output %s\n", output.name); outputs[dim(outputs)] = output; } } void getline() { if (File::end(input)) line = "DONE"; else line = File::fgets(input); words = String::wordsplit(line, " \t"); } bool check_output() { if (dim(words) >= 2) { switch (words[1]) { case "connected": case "disconnected": add_output(); state = state_t.start_output; return true; } } return false; } getline(); while (state != state_t.done) { # printf("\tstate %v words: %v\n", state, words); if (words[0] == "DONE") { add_output(); state = state_t.done; continue; } switch (state) { case state_t.start: if (dim(words) >= 10) { screen->width = string_to_integer(words[7]); screen->height = string_to_integer(words[9]); state = state_t.start_output; } getline(); break; case state_t.start_output: /* Look for an output */ if (dim(words) >= 2) { switch (words[1]) { case "connected": bool primary = false; if (dim(words) >= 3 && words[2] == "primary") primary = true; output = (output_t) { .name = words[0], .mode = "", .connected = true, .primary = primary, .width = 0, .height = 0, .is_tile = false, }; state = state_t.output; mode_priority = -1; break; case "disconnected": output = (output_t) { .name = words[0], .mode = "", .connected = false, .primary = false, .width = 0, .height = 0, .is_tile = false, }; state = state_t.skip_output; break; } } getline(); break; case state_t.skip_output: if (check_output()) break; getline(); break; case state_t.output: if (check_output()) break; /* Look for a mode */ if (String::index(line, "MHz") >= 0) { int this_priority = priority_any; if (String::index(line, "current") >= 0) { this_priority = priority_current; } else if (String::index(line, "preferred") >= 0) { this_priority = priority_preferred; } if (this_priority > mode_priority) { output.mode = words[0]; File::sscanf(words[1], "(0x%x)", &output.mode_id); state = state_t.mode_get; mode_priority = this_priority; } else { state = state_t.mode_skip; } } else if (words[0] == "TILE:" && dim(words) >= 7) { int[6] vals = { [i] = string_to_integer(words[i+1]) }; output.is_tile = true; output.tile = (tile_t) { .group_id = vals[0], .flags = vals[1], .number_h = vals[2], .number_v = vals[3], .hpos = vals[4], .vpos = vals[5], }; } getline(); break; case state_t.mode_get: case state_t.mode_skip: if (words[0] == "h:") { if (state == state_t.mode_get) { for (int h = 0; h < dim(words) - 1; h++) { if (words[h] == "width") { output.width = string_to_integer(words[h+1]); break; } } } } else if (words[0] == "v:") { if (state == state_t.mode_get) { for (int v = 0; v < dim(words) - 1; v++) { if (words[v] == "height") { output.height = string_to_integer(words[v+1]); break; } } } } else { state = state_t.output; break; } getline(); break; } } return outputs; } /* * Construct the set of monitors from the output information, building * composite monitors from outputs with the TILE property */ monitor_t[] get_monitors(&output_t[] outputs) { monitor_t[...] monitors = {}; for (int i = 0; i < dim(outputs); i++) { *output_t output = &outputs[i]; if (!output->connected) continue; if (output->is_tile) { int m; for (m = 0; m < dim(monitors); m++) { if (monitors[m].group_id == output->tile.group_id) break; } if (m == dim(monitors)) { (*output_t)[...] outputs; outputs[0] = output; monitors[m] = (monitor_t) { .name = sprintf("DP-GROUP-%d", output->tile.group_id), .group_id = output->tile.group_id, .outputs = outputs, .primary = false, .leader = 0, .width = output->tile.number_h * output->width, .height = output->tile.number_v * output->height, .x = -1, .y = -1, }; } else { &output_t leader = monitors[m].outputs[monitors[m].leader]; if (output->tile.vpos < leader.tile.vpos || output->tile.vpos == leader.tile.vpos && output->tile.hpos < leader.tile.hpos) monitors[m].leader = dim(monitors[m].outputs); monitors[m].outputs[dim(monitors[m].outputs)] = output; } } else { monitors[dim(monitors)] = (monitor_t) { .name = output->name, .group_id = -1, .outputs = ((*output_t)[1]) { output }, .leader = 0, .primary = false, .width = output->width, .height = output->height, .x = -1, .y = -1, }; } } return monitors; } bool is_internal_output(&output_t output) { if (String::index(output.name, "eDP") >= 0) return true; if (String::index(output.name, "LVDS") >= 0) return true; return false; } bool is_internal_monitor(&monitor_t monitor) { for (int o = 0; o < dim(monitor.outputs); o++) if (is_internal_output(monitor.outputs[o])) return true; return false; } /* Return the current primary monitor */ *monitor_t get_primary(&monitor_t[] monitors) { for (int m = 0; m < dim(monitors); m++) { &monitor_t monitor = &monitors[m]; if (monitor.primary) return &monitor; } monitors[0].primary = true; return &monitors[0]; } /* Return the first internal monitor (LVDS or eDP) */ *monitor_t get_internal(&monitor_t[] monitors) { for (int m = 0; m < dim(monitors); m++) { &monitor_t monitor = &monitors[m]; if (is_internal_monitor(&monitor)) return &monitor; } return &monitors[0]; } /* Set output positions and primary status */ void set_output(&monitor_t[] monitors) { /* Set output positions and primary value */ for (int m = 0; m < dim(monitors); m++) { *monitor_t monitor = &monitors[m]; if (monitor->primary) monitor->outputs[monitor->leader]->primary = true; if (monitor->group_id >= 0) { int tile_width = monitor->outputs[0]->width; int tile_height = monitor->outputs[0]->height; for (int o = 0; o < dim(monitor->outputs); o++) { monitor->outputs[o]->x = monitor->x + monitor->outputs[o]->tile.hpos * tile_width; monitor->outputs[o]->y = monitor->y + monitor->outputs[o]->tile.vpos * tile_height; } } else { monitor->outputs[0]->x = monitor->x; monitor->outputs[0]->y = monitor->y; } } } /* Set overall screen size */ void set_screen(&output_t[] outputs, *screen_t screen) { int width = 0; int height = 0; for (int o = 0; o < dim(outputs); o++) { if (outputs[o].connected) { int w = outputs[o].x + outputs[o].width; int h = outputs[o].y + outputs[o].height; if (w > width) width = w; if (h > height) height = h; } } screen->width = width; screen->height = height; } /* * Policy -- select the primary monitor * * Pick the largest external monitor if it's bigger than 1080p, * otherwise pick the internal monitor */ void set_primary(&monitor_t[] monitors) { *monitor_t primary = &monitors[0]; for (int m = 1; m < dim(monitors); m++) { *monitor_t monitor = &monitors[m]; if (is_internal_monitor(primary)) { if (!is_internal_monitor(monitor)) { if (monitor->height > 1080) primary = monitor; } } else { if (is_internal_monitor(monitor)) { if (primary->height <= 1080) primary = monitor; } else { if (monitor->height > primary->height) primary = monitor; } } } primary->primary = true; } /* * Policy -- position the monitors * * Place the primary monitor at the upper left corner of the * screen. * * If the internal monitor is not primary, place it just below the * primary monitor. * * Place all other monitors to the right of the primary monitor */ void set_monitor_pos(&monitor_t[] monitors) { int nset = 0; /* Primary monitor goes upper left */ *monitor_t primary = get_primary(&monitors); primary->x = 0; primary->y = 0; /* Set panel position, if not primary */ *monitor_t internal = get_internal(&monitors); if (is_internal_monitor(internal) && !internal->primary) { internal->x = 0; internal->y = primary->height; } int x = primary->width; /* Set remaining positions, right of primary */ for (int m = 0; m < dim(monitors); m++) { *monitor_t monitor = &monitors[m]; if (monitor->x < 0) { monitor->x = x; monitor->y = 0; x += monitor->width; } } } string tabs(int tab) { static string[] t = { "", "\t", "\t\t", "\t\t\t", "t\t\t\t" }; return t[tab]; } void print_output(*output_t output, int tab) { printf ("%soutput %s\n", tabs(tab), output->name); printf ("%sconnected %v\n", tabs(tab+1), output->connected); if (output->connected) { printf ("%smode %s (0x%x)\n", tabs(tab+1), output->mode, output->mode_id); printf ("%sprimary %v\n", tabs(tab+1), output->primary); printf ("%swidth, height %d,%d\n", tabs(tab+1), output->width, output->height); printf ("%sx, y %d, %d\n", tabs(tab+1), output->x, output->y); } } void print_monitor(*monitor_t monitor, int tab) { printf ("%smonitor %s\n", tabs(tab), monitor->name); printf ("%sgroup_id %d\n", tabs(tab+1), monitor->group_id); printf ("%sprimary %v\n", tabs(tab+1), monitor->primary); printf ("%sleader %d\n", tabs(tab+1), monitor->leader); printf ("%swidth, height %d,%d\n", tabs(tab+1), monitor->width, monitor->height); printf ("%sx, y %d, %d\n", tabs(tab+1), monitor->x, monitor->y); printf ("%sgroup_id %d\n", tabs(tab+1), monitor->group_id); printf ("%soutputs", tabs(tab+1)); for (int o = 0; o < dim(monitor->outputs); o++) printf(" %s", monitor->outputs[o]->name); printf ("\n"); } string[] output_config(*output_t output) { string[...] config; if (!output->connected) { config = (string[...]) { "--output", output->name, "--off" }; } else { config = (string[...]) { "--output", output->name, "--mode", sprintf ("0x%x", output->mode_id), "--pos", sprintf("%dx%d", output->x, output->y) }; if (output->primary) config[dim(config)] = "--primary"; } return config; } string output_names(&(*output_t)[] outputs) { string name = outputs[0]->name; for (int o = 1; o < dim(outputs); o++) name = name + "," + outputs[o]->name; return name; } string[] monitor_config(*monitor_t monitor) { if (monitor->group_id < 0) return (string[0]) {}; string[...] config = { "--setmonitor", monitor->name, "auto", output_names(&monitor->outputs), }; return config; } string[] screen_config(*screen_t screen) { return (string[...]) { "--fb", sprintf("%dx%d", screen->width, screen->height), "--dpi", "115" }; } string[] cat_args(string[][] args) { string[...] ret = {}; for (int a = 0; a < dim(args); a++) { for (int s = 0; s < dim(args[a]); s++) ret[dim(ret)] = args[a][s]; } return ret; } bool verbose = false; bool dry_run = false; ParseArgs::argdesc argd = { .args = { { .var = { .arg_flag = &verbose }, .abbr = 'v', .name = "verbose", .desc = "verbose mode" }, { .var = { .arg_flag = &dry_run }, .abbr = 'n', .name = "dry-run", .desc = "don't execute, just print" }, }, .unknown = &(int user_argind), }; void xrandr_auto() { ParseArgs::parseargs(&argd, &argv); screen_t orig_screen; output_t[] outputs = get_outputs(&orig_screen); monitor_t[] monitors = get_monitors(&outputs); string[...][...] args = { {"xrandr"} }; string[...] existing = get_existing_monitors(); screen_t screen; screen_t temp_screen; /* Implement our policy */ set_primary(&monitors); set_monitor_pos(&monitors); /* Using the specific configuration, place outputs * and set the overall screen size */ set_output(&monitors); set_screen(&outputs, &screen); temp_screen.width = max(orig_screen.width, screen.width); temp_screen.height = max(orig_screen.height, screen.height); if (temp_screen.width != orig_screen.width || temp_screen.height != orig_screen.height) args[dim(args)] = screen_config(&temp_screen); for (int e = 0; e < dim(existing); e++) args[dim(args)] = (string[...]) { "--delmonitor", existing[e] }; for (int m = 0; m < dim(monitors); m++) { args[dim(args)] = monitor_config(&monitors[m]); } for (int o = 0; o < dim(outputs); o++) { args[dim(args)] = output_config(&outputs[o]); } if (temp_screen.height != screen.height || temp_screen.width != screen.width) args[dim(args)] = screen_config(&screen); if (verbose) { for (int m = 0; m < dim(monitors); m++) print_monitor(&monitors[m], 0); for (int o = 0; o < dim(outputs); o++) print_output(&outputs[o], 0); } string[] xrandr_args = cat_args(args); if (dry_run || verbose) { for (int a = 0; a < dim(xrandr_args); a++) printf("%s ", xrandr_args[a]); printf ("\n"); } if (!dry_run) { system("xrandr", xrandr_args ...); } } xrandr_auto();