]> git.lizzy.rs Git - bspwm.git/blob - monitor.c
Remove unused variable
[bspwm.git] / monitor.c
1 #include <stdlib.h>
2 #include <string.h>
3 #include <limits.h>
4 #include "settings.h"
5 #include "bspwm.h"
6 #include "tree.h"
7 #include "desktop.h"
8 #include "window.h"
9 #include "query.h"
10 #include "ewmh.h"
11 #include "monitor.h"
12
13 monitor_t *make_monitor(xcb_rectangle_t rect)
14 {
15     monitor_t *m = malloc(sizeof(monitor_t));
16     snprintf(m->name, sizeof(m->name), "%s%02d", DEFAULT_MON_NAME, ++monitor_uid);
17     m->prev = m->next = NULL;
18     m->desk = m->last_desk = m->desk_head = m->desk_tail = NULL;
19     m->rectangle = rect;
20     m->top_padding = m->right_padding = m->bottom_padding = m->left_padding = 0;
21     m->wired = true;
22     uint32_t mask = XCB_CW_EVENT_MASK;
23     uint32_t values[] = {XCB_EVENT_MASK_ENTER_WINDOW};
24     m->root = xcb_generate_id(dpy);
25     xcb_create_window(dpy, XCB_COPY_FROM_PARENT, m->root, root, rect.x, rect.y, rect.width, rect.height, 0, XCB_WINDOW_CLASS_INPUT_ONLY, XCB_COPY_FROM_PARENT, mask, values);
26     window_lower(m->root);
27     if (focus_follows_pointer)
28         window_show(m->root);
29     return m;
30 }
31
32 monitor_t *find_monitor(char *name)
33 {
34     for (monitor_t *m = mon_head; m != NULL; m = m->next)
35         if (streq(m->name, name))
36             return m;
37     return NULL;
38 }
39
40 monitor_t *get_monitor_by_id(xcb_randr_output_t id)
41 {
42     for (monitor_t *m = mon_head; m != NULL; m = m->next)
43         if (m->id == id)
44             return m;
45     return NULL;
46 }
47
48 void fit_monitor(monitor_t *m, client_t *c)
49 {
50     xcb_rectangle_t crect = c->floating_rectangle;
51     xcb_rectangle_t mrect = m->rectangle;
52     while (crect.x < mrect.x)
53         crect.x += mrect.width;
54     while (crect.x > (mrect.x + mrect.width - 1))
55         crect.x -= mrect.width;
56     while (crect.y < mrect.y)
57         crect.y += mrect.height;
58     while (crect.y > (mrect.y + mrect.height - 1))
59         crect.y -= mrect.height;
60     c->floating_rectangle = crect;
61 }
62
63 void update_root(monitor_t *m)
64 {
65     xcb_rectangle_t rect = m->rectangle;
66     window_move_resize(m->root, rect.x, rect.y, rect.width, rect.height);
67 }
68
69 void select_monitor(monitor_t *m)
70 {
71     if (mon == m)
72         return;
73
74     PRINTF("select monitor %s\n", m->name);
75
76     last_mon = mon;
77     mon = m;
78
79     if (pointer_follows_monitor)
80         center_pointer(m);
81
82     ewmh_update_current_desktop();
83     put_status();
84 }
85
86 monitor_t *add_monitor(xcb_rectangle_t rect)
87 {
88     monitor_t *m = make_monitor(rect);
89     if (mon == NULL) {
90         mon = m;
91         mon_head = m;
92         mon_tail = m;
93     } else {
94         mon_tail->next = m;
95         m->prev = mon_tail;
96         mon_tail = m;
97     }
98     num_monitors++;
99     return m;
100 }
101
102 void remove_monitor(monitor_t *m)
103 {
104     PRINTF("remove monitor %s (0x%X)\n", m->name, m->id);
105
106     while (m->desk_head != NULL)
107         remove_desktop(m, m->desk_head);
108     monitor_t *prev = m->prev;
109     monitor_t *next = m->next;
110     if (prev != NULL)
111         prev->next = next;
112     if (next != NULL)
113         next->prev = prev;
114     if (mon_head == m)
115         mon_head = next;
116     if (mon_tail == m)
117         mon_tail = prev;
118     if (last_mon == m)
119         last_mon = NULL;
120     if (pri_mon == m)
121         pri_mon = NULL;
122     if (mon == m) {
123         monitor_t *mm = (last_mon == NULL ? (prev == NULL ? next : prev) : last_mon);
124         if (mm != NULL) {
125             focus_node(mm, mm->desk, mm->desk->focus);
126             last_mon = NULL;
127         } else {
128             mon = NULL;
129         }
130     }
131     xcb_destroy_window(dpy, m->root);
132     free(m);
133     num_monitors--;
134     put_status();
135 }
136
137 void merge_monitors(monitor_t *ms, monitor_t *md)
138 {
139     PRINTF("merge %s into %s\n", ms->name, md->name);
140
141     desktop_t *d = ms->desk_head;
142     while (d != NULL) {
143         desktop_t *next = d->next;
144         if (d->root != NULL || strstr(d->name, DEFAULT_DESK_NAME) == NULL)
145             transfer_desktop(ms, md, d);
146         d = next;
147     }
148 }
149
150 void swap_monitors(monitor_t *m1, monitor_t *m2)
151 {
152     if (m1 == NULL || m2 == NULL || m1 == m2)
153         return;
154
155     if (mon_head == m1)
156         mon_head = m2;
157     else if (mon_head == m2)
158         mon_head = m1;
159     if (mon_tail == m1)
160         mon_tail = m2;
161     else if (mon_tail == m2)
162         mon_tail = m1;
163
164     monitor_t *p1 = m1->prev;
165     monitor_t *n1 = m1->next;
166     monitor_t *p2 = m2->prev;
167     monitor_t *n2 = m2->next;
168
169     if (p1 != NULL && p1 != m2)
170         p1->next = m2;
171     if (n1 != NULL && n1 != m2)
172         n1->prev = m2;
173     if (p2 != NULL && p2 != m1)
174         p2->next = m1;
175     if (n2 != NULL && n2 != m1)
176         n2->prev = m1;
177
178     m1->prev = p2 == m1 ? m2 : p2;
179     m1->next = n2 == m1 ? m2 : n2;
180     m2->prev = p1 == m2 ? m1 : p1;
181     m2->next = n1 == m2 ? m1 : n1;
182
183     ewmh_update_wm_desktops();
184     ewmh_update_desktop_names();
185     ewmh_update_current_desktop();
186     put_status();
187 }
188
189 monitor_t *closest_monitor(monitor_t *m, cycle_dir_t dir, desktop_select_t sel)
190 {
191     monitor_t *f = (dir == CYCLE_PREV ? m->prev : m->next);
192     if (f == NULL)
193         f = (dir == CYCLE_PREV ? mon_tail : mon_head);
194
195     while (f != m) {
196         if (desktop_matches(f->desk, sel))
197             return f;
198         f = (dir == CYCLE_PREV ? m->prev : m->next);
199         if (f == NULL)
200             f = (dir == CYCLE_PREV ? mon_tail : mon_head);
201     }
202
203     return NULL;
204 }
205
206 monitor_t *nearest_monitor(monitor_t *m, direction_t dir, desktop_select_t sel)
207 {
208     int dmin = INT_MAX;
209     monitor_t *nearest = NULL;
210     xcb_rectangle_t rect = m->rectangle;
211     for (monitor_t *f = mon_head; f != NULL; f = f->next) {
212         if (f == m)
213             continue;
214         if (!desktop_matches(f->desk, sel))
215             continue;
216         xcb_rectangle_t r = f->rectangle;
217         if ((dir == DIR_LEFT && r.x < rect.x) ||
218                 (dir == DIR_RIGHT && r.x >= (rect.x + rect.width)) ||
219                 (dir == DIR_UP && r.y < rect.y) ||
220                 (dir == DIR_DOWN && r.y >= (rect.y + rect.height))) {
221             int d = abs((r.x + r.width / 2) - (rect.x + rect.width / 2)) +
222                 abs((r.y + r.height / 2) - (rect.y + rect.height / 2));
223             if (d < dmin) {
224                 dmin = d;
225                 nearest = f;
226             }
227         }
228     }
229     return nearest;
230 }
231
232 bool import_monitors(void)
233 {
234     PUTS("import monitors");
235     xcb_randr_get_screen_resources_current_reply_t *sres = xcb_randr_get_screen_resources_current_reply(dpy, xcb_randr_get_screen_resources_current(dpy, root), NULL);
236     if (sres == NULL)
237         return false;
238
239     monitor_t *m, *mm = NULL;
240
241     int len = xcb_randr_get_screen_resources_current_outputs_length(sres);
242     xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_current_outputs(sres);
243
244     xcb_randr_get_output_info_cookie_t cookies[len];
245     for (int i = 0; i < len; i++)
246         cookies[i] = xcb_randr_get_output_info(dpy, outputs[i], XCB_CURRENT_TIME);
247
248     for (m = mon_head; m != NULL; m = m->next)
249         m->wired = false;
250
251     for (int i = 0; i < len; i++) {
252         xcb_randr_get_output_info_reply_t *info = xcb_randr_get_output_info_reply(dpy, cookies[i], NULL);
253         if (info != NULL && info->crtc != XCB_NONE) {
254
255             xcb_randr_get_crtc_info_reply_t *cir = xcb_randr_get_crtc_info_reply(dpy, xcb_randr_get_crtc_info(dpy, info->crtc, XCB_CURRENT_TIME), NULL);
256             if (cir != NULL) {
257                 xcb_rectangle_t rect = (xcb_rectangle_t) {cir->x, cir->y, cir->width, cir->height};
258                 mm = get_monitor_by_id(outputs[i]);
259                 if (mm != NULL) {
260                     mm->rectangle = rect;
261                     update_root(mm);
262                     for (desktop_t *d = mm->desk_head; d != NULL; d = d->next)
263                         for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
264                             fit_monitor(mm, n->client);
265                     arrange(mm, mm->desk);
266                     mm->wired = true;
267                     PRINTF("update monitor %s (0x%X)\n", mm->name, mm->id);
268                 } else {
269                     mm = add_monitor(rect);
270                     char *name = (char *)xcb_randr_get_output_info_name(info);
271                     size_t name_len = MIN(sizeof(mm->name), (size_t)xcb_randr_get_output_info_name_length(info) + 1);
272                     snprintf(mm->name, name_len, "%s", name);
273                     mm->id = outputs[i];
274                     PRINTF("add monitor %s (0x%X)\n", mm->name, mm->id);
275                 }
276             }
277             free(cir);
278         }
279         free(info);
280     }
281
282     /* initially focus the primary monitor and add the first desktop to it */
283     xcb_randr_get_output_primary_reply_t *gpo = xcb_randr_get_output_primary_reply(dpy, xcb_randr_get_output_primary(dpy, root), NULL);
284     if (gpo != NULL) {
285         pri_mon = get_monitor_by_id(gpo->output);
286         if (!running && pri_mon != NULL) {
287             if (mon != pri_mon)
288                 mon = pri_mon;
289             add_desktop(pri_mon, make_desktop(NULL));
290             ewmh_update_current_desktop();
291         }
292     }
293     free(gpo);
294
295     /* handle overlapping monitors */
296     m = mon_head;
297     while (m != NULL) {
298         monitor_t *next = m->next;
299         if (m->wired) {
300             for (monitor_t *mb = mon_head; mb != NULL; mb = mb->next)
301                 if (mb != m && mb->wired && (m->desk == NULL || mb->desk == NULL)
302                         && contains(mb->rectangle, m->rectangle)) {
303                     if (mm == m)
304                         mm = mb;
305                     merge_monitors(m, mb);
306                     remove_monitor(m);
307                     break;
308                 }
309         }
310         m = next;
311     }
312
313     /* merge and remove disconnected monitors */
314     m = mon_head;
315     while (m != NULL) {
316         monitor_t *next = m->next;
317         if (!m->wired) {
318             merge_monitors(m, mm);
319             remove_monitor(m);
320         }
321         m = next;
322     }
323
324     /* add one desktop to each new monitor */
325     for (m = mon_head; m != NULL; m = m->next)
326         if (m->desk == NULL && (running || pri_mon == NULL || m != pri_mon))
327             add_desktop(m, make_desktop(NULL));
328
329     free(sres);
330     update_motion_recorder();
331     return (num_monitors > 0);
332 }