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