6 /* Parsed TILE property contents */
16 /* Configuration for one output */
29 /* Configuration for one monitor (collection of outputs) */
39 (*output_t)[...] outputs;
42 /* Overall screen configuration */
48 /* List the existing manually configured monitors
51 get_existing_monitors() {
52 file input = popen(popen_direction.read, false,
53 "xrandr", "xrandr", "--listmonitors");
54 string[...] names = {};
55 while (!File::end(input)) {
56 string line = File::fgets(input);
57 string[] words = String::wordsplit(line, " \t");
59 if (words[0] != "Monitors:" && dim(words) >= 2) {
60 string name = words[1];
64 name = String::substr(name, 1, String::length(name) - 1);
65 names[dim(names)] = name;
72 const int priority_any = 0;
73 const int priority_preferred = 1;
74 const int priority_current = 2;
76 typedef enum { start, start_output, output, skip_output,
77 mode_get, mode_skip, done } state_t;
79 /* Parse xrandr results to compute the set of available monitors and
83 get_outputs(*screen_t screen) {
84 file input = popen(popen_direction.read, false,
85 "xrandr", "xrandr", "--verbose", "--prop");
86 output_t[...] outputs = {};
89 state_t state = state_t.start;
95 case state_t.skip_output:
97 case state_t.mode_skip:
98 case state_t.mode_get:
99 # printf ("add output %s\n", output.name);
100 outputs[dim(outputs)] = output;
105 if (File::end(input))
108 line = File::fgets(input);
110 words = String::wordsplit(line, " \t");
113 bool check_output() {
114 if (dim(words) >= 2) {
119 state = state_t.start_output;
128 while (state != state_t.done) {
129 # printf("\tstate %v words: %v\n", state, words);
130 if (words[0] == "DONE") {
132 state = state_t.done;
137 if (dim(words) >= 10) {
138 screen->width = string_to_integer(words[7]);
139 screen->height = string_to_integer(words[9]);
140 state = state_t.start_output;
144 case state_t.start_output:
145 /* Look for an output */
146 if (dim(words) >= 2) {
149 bool primary = false;
150 if (dim(words) >= 3 && words[2] == "primary")
152 output = (output_t) {
161 state = state_t.output;
165 output = (output_t) {
174 state = state_t.skip_output;
180 case state_t.skip_output:
189 /* Look for a mode */
190 if (String::index(line, "MHz") >= 0) {
191 int this_priority = priority_any;
192 if (String::index(line, "current") >= 0) {
193 this_priority = priority_current;
194 } else if (String::index(line, "preferred") >= 0) {
195 this_priority = priority_preferred;
197 if (this_priority > mode_priority) {
198 output.mode = words[0];
199 File::sscanf(words[1], "(0x%x)", &output.mode_id);
200 state = state_t.mode_get;
201 mode_priority = this_priority;
203 state = state_t.mode_skip;
205 } else if (words[0] == "TILE:" && dim(words) >= 7) {
206 int[6] vals = { [i] = string_to_integer(words[i+1]) };
207 output.is_tile = true;
208 output.tile = (tile_t) {
219 case state_t.mode_get:
220 case state_t.mode_skip:
221 if (words[0] == "h:") {
222 if (state == state_t.mode_get) {
223 for (int h = 0; h < dim(words) - 1; h++) {
224 if (words[h] == "width") {
225 output.width = string_to_integer(words[h+1]);
230 } else if (words[0] == "v:") {
231 if (state == state_t.mode_get) {
232 for (int v = 0; v < dim(words) - 1; v++) {
233 if (words[v] == "height") {
234 output.height = string_to_integer(words[v+1]);
240 state = state_t.output;
251 * Construct the set of monitors from the output information, building
252 * composite monitors from outputs with the TILE property
254 monitor_t[] get_monitors(&output_t[] outputs) {
255 monitor_t[...] monitors = {};
257 for (int i = 0; i < dim(outputs); i++) {
258 *output_t output = &outputs[i];
260 if (!output->connected)
263 if (output->is_tile) {
266 for (m = 0; m < dim(monitors); m++) {
267 if (monitors[m].group_id == output->tile.group_id)
270 if (m == dim(monitors)) {
271 (*output_t)[...] outputs;
273 monitors[m] = (monitor_t) {
274 .name = sprintf("DP-GROUP-%d", output->tile.group_id),
275 .group_id = output->tile.group_id,
279 .width = output->tile.number_h * output->width,
280 .height = output->tile.number_v * output->height,
285 &output_t leader = monitors[m].outputs[monitors[m].leader];
287 if (output->tile.vpos < leader.tile.vpos ||
288 output->tile.vpos == leader.tile.vpos &&
289 output->tile.hpos < leader.tile.hpos)
291 monitors[m].leader = dim(monitors[m].outputs);
293 monitors[m].outputs[dim(monitors[m].outputs)] = output;
296 monitors[dim(monitors)] = (monitor_t) {
297 .name = output->name,
299 .outputs = ((*output_t)[1]) { output },
302 .width = output->width,
303 .height = output->height,
314 is_internal_output(&output_t output) {
315 if (String::index(output.name, "eDP") >= 0)
317 if (String::index(output.name, "LVDS") >= 0)
323 is_internal_monitor(&monitor_t monitor) {
324 for (int o = 0; o < dim(monitor.outputs); o++)
325 if (is_internal_output(monitor.outputs[o]))
330 /* Return the current primary monitor */
332 get_primary(&monitor_t[] monitors)
334 for (int m = 0; m < dim(monitors); m++) {
335 &monitor_t monitor = &monitors[m];
340 monitors[0].primary = true;
344 /* Return the first internal monitor (LVDS or eDP) */
346 get_internal(&monitor_t[] monitors)
348 for (int m = 0; m < dim(monitors); m++) {
349 &monitor_t monitor = &monitors[m];
350 if (is_internal_monitor(&monitor))
356 /* Set output positions and primary status */
358 set_output(&monitor_t[] monitors)
360 /* Set output positions and primary value */
361 for (int m = 0; m < dim(monitors); m++) {
362 *monitor_t monitor = &monitors[m];
364 if (monitor->primary)
365 monitor->outputs[monitor->leader]->primary = true;
367 if (monitor->group_id >= 0) {
368 int tile_width = monitor->outputs[0]->width;
369 int tile_height = monitor->outputs[0]->height;
371 for (int o = 0; o < dim(monitor->outputs); o++) {
372 monitor->outputs[o]->x = monitor->x + monitor->outputs[o]->tile.hpos * tile_width;
373 monitor->outputs[o]->y = monitor->y + monitor->outputs[o]->tile.vpos * tile_height;
376 monitor->outputs[0]->x = monitor->x;
377 monitor->outputs[0]->y = monitor->y;
382 /* Set overall screen size */
384 set_screen(&output_t[] outputs, *screen_t screen)
389 for (int o = 0; o < dim(outputs); o++) {
390 if (outputs[o].connected) {
391 int w = outputs[o].x + outputs[o].width;
392 int h = outputs[o].y + outputs[o].height;
400 screen->width = width;
401 screen->height = height;
405 * Policy -- select the primary monitor
407 * Pick the largest external monitor if it's bigger than 1080p,
408 * otherwise pick the internal monitor
411 set_primary(&monitor_t[] monitors)
413 *monitor_t primary = &monitors[0];
415 for (int m = 1; m < dim(monitors); m++) {
416 *monitor_t monitor = &monitors[m];
418 if (is_internal_monitor(primary)) {
419 if (!is_internal_monitor(monitor)) {
420 if (monitor->height > 1080)
424 if (is_internal_monitor(monitor)) {
425 if (primary->height <= 1080)
428 if (monitor->height > primary->height)
433 primary->primary = true;
437 * Policy -- position the monitors
439 * Place the primary monitor at the upper left corner of the
442 * If the internal monitor is not primary, place it just below the
445 * Place all other monitors to the right of the primary monitor
449 set_monitor_pos(&monitor_t[] monitors)
453 /* Primary monitor goes upper left */
454 *monitor_t primary = get_primary(&monitors);
459 /* Set panel position, if not primary */
460 *monitor_t internal = get_internal(&monitors);
461 if (is_internal_monitor(internal) && !internal->primary) {
463 internal->y = primary->height;
466 int x = primary->width;
468 /* Set remaining positions, right of primary */
469 for (int m = 0; m < dim(monitors); m++) {
470 *monitor_t monitor = &monitors[m];
472 if (monitor->x < 0) {
483 static string[] t = { "", "\t", "\t\t", "\t\t\t", "t\t\t\t" };
489 print_output(*output_t output, int tab) {
490 printf ("%soutput %s\n", tabs(tab), output->name);
491 printf ("%sconnected %v\n", tabs(tab+1), output->connected);
492 if (output->connected) {
493 printf ("%smode %s (0x%x)\n", tabs(tab+1), output->mode, output->mode_id);
494 printf ("%sprimary %v\n", tabs(tab+1), output->primary);
495 printf ("%swidth, height %d,%d\n", tabs(tab+1), output->width, output->height);
496 printf ("%sx, y %d, %d\n", tabs(tab+1), output->x, output->y);
501 print_monitor(*monitor_t monitor, int tab) {
503 printf ("%smonitor %s\n", tabs(tab), monitor->name);
504 printf ("%sgroup_id %d\n", tabs(tab+1), monitor->group_id);
505 printf ("%sprimary %v\n", tabs(tab+1), monitor->primary);
506 printf ("%sleader %d\n", tabs(tab+1), monitor->leader);
507 printf ("%swidth, height %d,%d\n", tabs(tab+1), monitor->width, monitor->height);
508 printf ("%sx, y %d, %d\n", tabs(tab+1), monitor->x, monitor->y);
509 printf ("%sgroup_id %d\n", tabs(tab+1), monitor->group_id);
510 printf ("%soutputs", tabs(tab+1));
511 for (int o = 0; o < dim(monitor->outputs); o++)
512 printf(" %s", monitor->outputs[o]->name);
517 output_config(*output_t output) {
520 if (!output->connected) {
521 config = (string[...]) {
527 config = (string[...]) {
531 sprintf ("0x%x", output->mode_id),
533 sprintf("%dx%d", output->x, output->y)
536 config[dim(config)] = "--primary";
542 output_names(&(*output_t)[] outputs) {
543 string name = outputs[0]->name;
545 for (int o = 1; o < dim(outputs); o++)
546 name = name + "," + outputs[o]->name;
551 monitor_config(*monitor_t monitor) {
552 if (monitor->group_id < 0)
553 return (string[0]) {};
555 string[...] config = {
559 output_names(&monitor->outputs),
566 screen_config(*screen_t screen) {
567 return (string[...]) {
569 sprintf("%dx%d", screen->width, screen->height),
575 cat_args(string[][] args) {
576 string[...] ret = {};
578 for (int a = 0; a < dim(args); a++) {
579 for (int s = 0; s < dim(args[a]); s++)
580 ret[dim(ret)] = args[a][s];
585 bool verbose = false;
586 bool dry_run = false;
588 ParseArgs::argdesc argd = {
590 { .var = { .arg_flag = &verbose },
593 .desc = "verbose mode"
595 { .var = { .arg_flag = &dry_run },
598 .desc = "don't execute, just print"
601 .unknown = &(int user_argind),
607 ParseArgs::parseargs(&argd, &argv);
609 screen_t orig_screen;
610 output_t[] outputs = get_outputs(&orig_screen);
611 monitor_t[] monitors = get_monitors(&outputs);
612 string[...][...] args = { {"xrandr"} };
613 string[...] existing = get_existing_monitors();
615 screen_t temp_screen;
617 /* Implement our policy */
618 set_primary(&monitors);
619 set_monitor_pos(&monitors);
621 /* Using the specific configuration, place outputs
622 * and set the overall screen size
624 set_output(&monitors);
625 set_screen(&outputs, &screen);
627 temp_screen.width = max(orig_screen.width, screen.width);
628 temp_screen.height = max(orig_screen.height, screen.height);
630 if (temp_screen.width != orig_screen.width || temp_screen.height != orig_screen.height)
631 args[dim(args)] = screen_config(&temp_screen);
633 for (int e = 0; e < dim(existing); e++)
634 args[dim(args)] = (string[...]) {
639 for (int m = 0; m < dim(monitors); m++) {
640 args[dim(args)] = monitor_config(&monitors[m]);
643 for (int o = 0; o < dim(outputs); o++) {
644 args[dim(args)] = output_config(&outputs[o]);
647 if (temp_screen.height != screen.height || temp_screen.width != screen.width)
648 args[dim(args)] = screen_config(&screen);
651 for (int m = 0; m < dim(monitors); m++)
652 print_monitor(&monitors[m], 0);
653 for (int o = 0; o < dim(outputs); o++)
654 print_output(&outputs[o], 0);
657 string[] xrandr_args = cat_args(args);
659 if (dry_run || verbose) {
660 for (int a = 0; a < dim(xrandr_args); a++)
661 printf("%s ", xrandr_args[a]);
665 system("xrandr", xrandr_args ...);