1From 478e93eed23500351fa606fb7706a0297548a049 Mon Sep 17 00:00:00 2001
2From: =?UTF-8?q?S=C3=B6ren=20Tempel?= <soeren@soeren-tempel.net>
3Date: Sat, 16 Mar 2024 13:29:14 +0100
4Subject: [PATCH wlr-randr] Implement relative positioning options from xrandr
5
6This implements the --above, --below, --right-of, and --left-of
7positioning options from xrandr(1). The code is heavily inspired by
8xrandr's implementation [1]. The functionality is intended to be
9equivalent. The --same-as option has not been implemented but can
10easily be added as well, if deemed useful.
11
12Absolute positioning via --pos overwrites a previously established
13relative position and vice versa. The last positioning option passed
14on the command-line takes precedence.
15
16Contrary to xrandr, this implementation does currently not detect
17loops in relative specifications. This could be implemented in future
18work.
19
20[1]: https://gitlab.freedesktop.org/xorg/app/xrandr/-/blob/xrandr-1.5.2/xrandr.c?ref_type=tags#L2004-L2024
21---
22 main.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
23 1 file changed, 74 insertions(+), 2 deletions(-)
24
25diff --git a/main.c b/main.c
26index 527379f..ef90991 100644
27--- a/main.c
28+++ b/main.c
29@@ -75,6 +75,40 @@ static const char *output_transform_map[] = {
30 [WL_OUTPUT_TRANSFORM_FLIPPED_270] = "flipped-270",
31 };
32
33+static int32_t head_height(struct randr_head *head) {
34+ switch (head->transform) {
35+ case WL_OUTPUT_TRANSFORM_NORMAL:
36+ case WL_OUTPUT_TRANSFORM_FLIPPED:
37+ case WL_OUTPUT_TRANSFORM_180:
38+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
39+ return head->mode->height / head->scale;
40+ case WL_OUTPUT_TRANSFORM_90:
41+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
42+ case WL_OUTPUT_TRANSFORM_270:
43+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
44+ return head->mode->width / head->scale;
45+ }
46+
47+ assert(0 && "unreachable");
48+}
49+
50+static int32_t head_width(struct randr_head *head) {
51+ switch (head->transform) {
52+ case WL_OUTPUT_TRANSFORM_NORMAL:
53+ case WL_OUTPUT_TRANSFORM_FLIPPED:
54+ case WL_OUTPUT_TRANSFORM_180:
55+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
56+ return head->mode->width / head->scale;
57+ case WL_OUTPUT_TRANSFORM_90:
58+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
59+ case WL_OUTPUT_TRANSFORM_270:
60+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
61+ return head->mode->height / head->scale;
62+ }
63+
64+ assert(0 && "unreachable");
65+}
66+
67 static void print_state(struct randr_state *state) {
68 struct randr_head *head;
69 wl_list_for_each(head, &state->heads, link) {
70@@ -615,6 +649,10 @@ static const struct option long_options[] = {
71 {"preferred", no_argument, 0, 0},
72 {"custom-mode", required_argument, 0, 0},
73 {"pos", required_argument, 0, 0},
74+ {"left-of", required_argument, 0, 0},
75+ {"right-of", required_argument, 0, 0},
76+ {"above", required_argument, 0, 0},
77+ {"below", required_argument, 0, 0},
78 {"transform", required_argument, 0, 0},
79 {"scale", required_argument, 0, 0},
80 {"adaptive-sync", required_argument, 0, 0},
81@@ -696,7 +734,7 @@ static void fixup_disabled_head(struct randr_head *head) {
82 }
83 }
84
85-static bool parse_output_arg(struct randr_head *head,
86+static bool parse_output_arg(struct randr_state *state, struct randr_head *head,
87 const char *name, const char *value) {
88 if (strcmp(name, "on") == 0) {
89 if (!head->enabled) {
90@@ -788,6 +826,36 @@ static bool parse_output_arg(struct randr_head *head,
91 head->changed |= RANDR_HEAD_POSITION;
92 head->x = x;
93 head->y = y;
94+ } else if (strcmp(name, "left-of") == 0 ||
95+ strcmp(name, "right-of") == 0 ||
96+ strcmp(name, "above") == 0 ||
97+ strcmp(name, "below") == 0) {
98+ struct randr_head *relation, *tmp_head;
99+ wl_list_for_each_safe(relation, tmp_head, &state->heads, link) {
100+ if (strcmp(relation->name, value) != 0) {
101+ continue;
102+ }
103+
104+ if (strcmp(name, "left-of") == 0) {
105+ head->y = relation->y;
106+ head->x = relation->x - head_width(head);
107+ } else if (strcmp(name, "right-of") == 0) {
108+ head->y = relation->y;
109+ head->x = relation->x + head_width(relation);
110+ } else if (strcmp(name, "above") == 0) {
111+ head->x = relation->x;
112+ head->y = relation->y - head_height(head);
113+ } else if (strcmp(name, "below") == 0) {
114+ head->x = relation->x;
115+ head->y = relation->y + head_height(relation);
116+ }
117+
118+ head->changed |= RANDR_HEAD_POSITION;
119+ return true;
120+ }
121+
122+ fprintf(stderr, "invalid output: %s\n", value);
123+ return false;
124 } else if (strcmp(name, "transform") == 0) {
125 bool found = false;
126 size_t len =
127@@ -851,6 +919,10 @@ static const char usage[] =
128 " --mode|--custom-mode <width>x<height>[@<refresh>Hz]\n"
129 " --preferred\n"
130 " --pos <x>,<y>\n"
131+ " --left-of <name>\n"
132+ " --right-of <name>\n"
133+ " --above <name>\n"
134+ " --below <name>\n"
135 " --transform normal|90|180|270|flipped|flipped-90|flipped-180|flipped-270\n"
136 " --scale <factor>\n"
137 " --adaptive-sync enabled|disabled\n";
138@@ -924,7 +996,7 @@ int main(int argc, char *argv[]) {
139 return EXIT_FAILURE;
140 }
141
142- if (!parse_output_arg(current_head, name, value)) {
143+ if (!parse_output_arg(&state, current_head, name, value)) {
144 return EXIT_FAILURE;
145 }
146