]> git.lizzy.rs Git - bspwm.git/blob - monitor.c
Set proper socket option to allow fast restart
[bspwm.git] / monitor.c
1 /* * Copyright (c) 2012-2013 Bastien Dejean
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without modification,
5  * are permitted provided that the following conditions are met:
6  *
7  *  * Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9  *  * Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation and/or
11  * other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
17  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include <limits.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include "bspwm.h"
29 #include "desktop.h"
30 #include "ewmh.h"
31 #include "history.h"
32 #include "query.h"
33 #include "settings.h"
34 #include "tree.h"
35 #include "window.h"
36 #include "monitor.h"
37
38 monitor_t *make_monitor(xcb_rectangle_t rect)
39 {
40     monitor_t *m = malloc(sizeof(monitor_t));
41     snprintf(m->name, sizeof(m->name), "%s%02d", DEFAULT_MON_NAME, ++monitor_uid);
42     m->prev = m->next = NULL;
43     m->desk = m->desk_head = m->desk_tail = NULL;
44     m->rectangle = rect;
45     m->top_padding = m->right_padding = m->bottom_padding = m->left_padding = 0;
46     m->wired = true;
47     m->num_sticky = 0;
48     uint32_t mask = XCB_CW_EVENT_MASK;
49     uint32_t values[] = {XCB_EVENT_MASK_ENTER_WINDOW};
50     m->root = xcb_generate_id(dpy);
51     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);
52     window_lower(m->root);
53     if (focus_follows_pointer)
54         window_show(m->root);
55     return m;
56 }
57
58 monitor_t *find_monitor(char *name)
59 {
60     for (monitor_t *m = mon_head; m != NULL; m = m->next)
61         if (streq(m->name, name))
62             return m;
63     return NULL;
64 }
65
66 monitor_t *get_monitor_by_id(xcb_randr_output_t id)
67 {
68     for (monitor_t *m = mon_head; m != NULL; m = m->next)
69         if (m->id == id)
70             return m;
71     return NULL;
72 }
73
74 void translate_client(monitor_t *ms, monitor_t *md, client_t *c)
75 {
76     if (frozen_pointer->action != ACTION_NONE || ms == md)
77         return;
78
79     /* Clip the rectangle to fit into the monitor.  Without this, the fitting
80      * algorithm doesn't work as expected. This also conserves the
81      * out-of-bounds regions */
82     int left_adjust = MAX((ms->rectangle.x - c->floating_rectangle.x), 0);
83     int top_adjust = MAX((ms->rectangle.y - c->floating_rectangle.y), 0);
84     int right_adjust = MAX((c->floating_rectangle.x + c->floating_rectangle.width) - (ms->rectangle.x + ms->rectangle.width), 0);
85     int bottom_adjust = MAX((c->floating_rectangle.y + c->floating_rectangle.height) - (ms->rectangle.y + ms->rectangle.height), 0);
86     c->floating_rectangle.x += left_adjust;
87     c->floating_rectangle.y += top_adjust;
88     c->floating_rectangle.width -= (left_adjust + right_adjust);
89     c->floating_rectangle.height -= (top_adjust + bottom_adjust);
90
91     int dx_s = c->floating_rectangle.x - ms->rectangle.x;
92     int dy_s = c->floating_rectangle.y - ms->rectangle.y;
93
94     int nume_x = dx_s * (md->rectangle.width - c->floating_rectangle.width);
95     int nume_y = dy_s * (md->rectangle.height - c->floating_rectangle.height);
96
97     int deno_x = ms->rectangle.width - c->floating_rectangle.width;
98     int deno_y = ms->rectangle.height - c->floating_rectangle.height;
99
100     int dx_d = (deno_x == 0 ? 0 : nume_x / deno_x);
101     int dy_d = (deno_y == 0 ? 0 : nume_y / deno_y);
102
103     /* Translate and undo clipping */
104     c->floating_rectangle.width += left_adjust + right_adjust;
105     c->floating_rectangle.height += top_adjust + bottom_adjust;
106     c->floating_rectangle.x = md->rectangle.x + dx_d - left_adjust;
107     c->floating_rectangle.y = md->rectangle.y + dy_d - top_adjust;
108 }
109
110 void update_root(monitor_t *m)
111 {
112     xcb_rectangle_t rect = m->rectangle;
113     window_move_resize(m->root, rect.x, rect.y, rect.width, rect.height);
114 }
115
116 void focus_monitor(monitor_t *m)
117 {
118     if (mon == m)
119         return;
120
121     PRINTF("focus monitor %s\n", m->name);
122
123     mon = m;
124
125     if (pointer_follows_monitor)
126         center_pointer(m);
127
128     ewmh_update_current_desktop();
129     put_status();
130 }
131
132 monitor_t *add_monitor(xcb_rectangle_t rect)
133 {
134     monitor_t *m = make_monitor(rect);
135     if (mon == NULL) {
136         mon = m;
137         mon_head = m;
138         mon_tail = m;
139     } else {
140         mon_tail->next = m;
141         m->prev = mon_tail;
142         mon_tail = m;
143     }
144     num_monitors++;
145     return m;
146 }
147
148 void remove_monitor(monitor_t *m)
149 {
150     PRINTF("remove monitor %s (0x%X)\n", m->name, m->id);
151
152     while (m->desk_head != NULL)
153         remove_desktop(m, m->desk_head);
154     monitor_t *prev = m->prev;
155     monitor_t *next = m->next;
156     monitor_t *last_mon = history_get_monitor(m);
157     if (prev != NULL)
158         prev->next = next;
159     if (next != NULL)
160         next->prev = prev;
161     if (mon_head == m)
162         mon_head = next;
163     if (mon_tail == m)
164         mon_tail = prev;
165     if (pri_mon == m)
166         pri_mon = NULL;
167     if (mon == m) {
168         mon = (last_mon == NULL ? (prev == NULL ? next : prev) : last_mon);
169         if (mon != NULL && mon->desk != NULL)
170             update_current();
171     }
172     xcb_destroy_window(dpy, m->root);
173     free(m);
174     num_monitors--;
175     put_status();
176 }
177
178 void merge_monitors(monitor_t *ms, monitor_t *md)
179 {
180     PRINTF("merge %s into %s\n", ms->name, md->name);
181
182     desktop_t *d = ms->desk_head;
183     while (d != NULL) {
184         desktop_t *next = d->next;
185         if (d->root != NULL || strstr(d->name, DEFAULT_DESK_NAME) == NULL)
186             transfer_desktop(ms, md, d);
187         d = next;
188     }
189 }
190
191 void swap_monitors(monitor_t *m1, monitor_t *m2)
192 {
193     if (m1 == NULL || m2 == NULL || m1 == m2)
194         return;
195
196     if (mon_head == m1)
197         mon_head = m2;
198     else if (mon_head == m2)
199         mon_head = m1;
200     if (mon_tail == m1)
201         mon_tail = m2;
202     else if (mon_tail == m2)
203         mon_tail = m1;
204
205     monitor_t *p1 = m1->prev;
206     monitor_t *n1 = m1->next;
207     monitor_t *p2 = m2->prev;
208     monitor_t *n2 = m2->next;
209
210     if (p1 != NULL && p1 != m2)
211         p1->next = m2;
212     if (n1 != NULL && n1 != m2)
213         n1->prev = m2;
214     if (p2 != NULL && p2 != m1)
215         p2->next = m1;
216     if (n2 != NULL && n2 != m1)
217         n2->prev = m1;
218
219     m1->prev = p2 == m1 ? m2 : p2;
220     m1->next = n2 == m1 ? m2 : n2;
221     m2->prev = p1 == m2 ? m1 : p1;
222     m2->next = n1 == m2 ? m1 : n1;
223
224     ewmh_update_wm_desktops();
225     ewmh_update_desktop_names();
226     ewmh_update_current_desktop();
227     put_status();
228 }
229
230 monitor_t *closest_monitor(monitor_t *m, cycle_dir_t dir, desktop_select_t sel)
231 {
232     monitor_t *f = (dir == CYCLE_PREV ? m->prev : m->next);
233     if (f == NULL)
234         f = (dir == CYCLE_PREV ? mon_tail : mon_head);
235
236     while (f != m) {
237         coordinates_t loc = {m, m->desk, NULL};
238         if (desktop_matches(&loc, &loc, sel))
239             return f;
240         f = (dir == CYCLE_PREV ? m->prev : m->next);
241         if (f == NULL)
242             f = (dir == CYCLE_PREV ? mon_tail : mon_head);
243     }
244
245     return NULL;
246 }
247
248 bool is_inside_monitor(monitor_t *m, xcb_point_t pt)
249 {
250     xcb_rectangle_t r = m->rectangle;
251     return (r.x <= pt.x && pt.x < (r.x + r.width)
252             && r.y <= pt.y && pt.y < (r.y + r.height));
253 }
254
255 monitor_t *monitor_from_point(xcb_point_t pt)
256 {
257     for (monitor_t *m = mon_head; m != NULL; m = m->next)
258         if (is_inside_monitor(m, pt))
259             return m;
260     return NULL;
261 }
262
263 monitor_t *monitor_from_client(client_t *c)
264 {
265     xcb_point_t pt = {c->floating_rectangle.x, c->floating_rectangle.y};
266     monitor_t *nearest = monitor_from_point(pt);
267     if (nearest == NULL) {
268         int x = (c->floating_rectangle.x + c->floating_rectangle.width) / 2;
269         int y = (c->floating_rectangle.y + c->floating_rectangle.height) / 2;
270         int dmin = INT_MAX;
271         for (monitor_t *m = mon_head; m != NULL; m = m->next) {
272             xcb_rectangle_t r = m->rectangle;
273             int d = abs((r.x + r.width / 2) - x) + abs((r.y + r.height / 2) - y);
274             if (d < dmin) {
275                 dmin = d;
276                 nearest = m;
277             }
278         }
279     }
280     return nearest;
281 }
282
283 monitor_t *nearest_monitor(monitor_t *m, direction_t dir, desktop_select_t sel)
284 {
285     int dmin = INT_MAX;
286     monitor_t *nearest = NULL;
287     xcb_rectangle_t rect = m->rectangle;
288     for (monitor_t *f = mon_head; f != NULL; f = f->next) {
289         if (f == m)
290             continue;
291         coordinates_t loc = {f, f->desk, NULL};
292         if (!desktop_matches(&loc, &loc, sel))
293             continue;
294         xcb_rectangle_t r = f->rectangle;
295         if ((dir == DIR_LEFT && r.x < rect.x) ||
296                 (dir == DIR_RIGHT && r.x >= (rect.x + rect.width)) ||
297                 (dir == DIR_UP && r.y < rect.y) ||
298                 (dir == DIR_DOWN && r.y >= (rect.y + rect.height))) {
299             int d = abs((r.x + r.width / 2) - (rect.x + rect.width / 2)) +
300                 abs((r.y + r.height / 2) - (rect.y + rect.height / 2));
301             if (d < dmin) {
302                 dmin = d;
303                 nearest = f;
304             }
305         }
306     }
307     return nearest;
308 }
309
310 bool import_monitors(void)
311 {
312     PUTS("import monitors");
313     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);
314     if (sres == NULL)
315         return false;
316
317     monitor_t *m, *mm = NULL;
318
319     int len = xcb_randr_get_screen_resources_current_outputs_length(sres);
320     xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_current_outputs(sres);
321
322     xcb_randr_get_output_info_cookie_t cookies[len];
323     for (int i = 0; i < len; i++)
324         cookies[i] = xcb_randr_get_output_info(dpy, outputs[i], XCB_CURRENT_TIME);
325
326     for (m = mon_head; m != NULL; m = m->next)
327         m->wired = false;
328
329     for (int i = 0; i < len; i++) {
330         xcb_randr_get_output_info_reply_t *info = xcb_randr_get_output_info_reply(dpy, cookies[i], NULL);
331         if (info != NULL) {
332             if (info->crtc != XCB_NONE) {
333                 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);
334                 if (cir != NULL) {
335                     xcb_rectangle_t rect = (xcb_rectangle_t) {cir->x, cir->y, cir->width, cir->height};
336                     mm = get_monitor_by_id(outputs[i]);
337                     if (mm != NULL) {
338                         mm->rectangle = rect;
339                         update_root(mm);
340                         for (desktop_t *d = mm->desk_head; d != NULL; d = d->next)
341                             for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
342                                 translate_client(mm, mm, n->client);
343                         arrange(mm, mm->desk);
344                         mm->wired = true;
345                         PRINTF("update monitor %s (0x%X)\n", mm->name, mm->id);
346                     } else {
347                         mm = add_monitor(rect);
348                         char *name = (char *)xcb_randr_get_output_info_name(info);
349                         size_t name_len = MIN(sizeof(mm->name), (size_t)xcb_randr_get_output_info_name_length(info) + 1);
350                         snprintf(mm->name, name_len, "%s", name);
351                         mm->id = outputs[i];
352                         PRINTF("add monitor %s (0x%X)\n", mm->name, mm->id);
353                     }
354                 }
355                 free(cir);
356             } else if (info->connection != XCB_RANDR_CONNECTION_DISCONNECTED) {
357                 m = get_monitor_by_id(outputs[i]);
358                 if (m != NULL)
359                     m->wired = true;
360             }
361         }
362         free(info);
363     }
364
365     /* initially focus the primary monitor and add the first desktop to it */
366     xcb_randr_get_output_primary_reply_t *gpo = xcb_randr_get_output_primary_reply(dpy, xcb_randr_get_output_primary(dpy, root), NULL);
367     if (gpo != NULL) {
368         pri_mon = get_monitor_by_id(gpo->output);
369         if (!running && pri_mon != NULL) {
370             if (mon != pri_mon)
371                 mon = pri_mon;
372             add_desktop(pri_mon, make_desktop(NULL));
373             ewmh_update_current_desktop();
374         }
375     }
376     free(gpo);
377
378     /* handle overlapping monitors */
379     m = mon_head;
380     while (m != NULL) {
381         monitor_t *next = m->next;
382         if (m->wired) {
383             for (monitor_t *mb = mon_head; mb != NULL; mb = mb->next)
384                 if (mb != m && mb->wired && (m->desk == NULL || mb->desk == NULL)
385                         && contains(mb->rectangle, m->rectangle)) {
386                     if (mm == m)
387                         mm = mb;
388                     merge_monitors(m, mb);
389                     remove_monitor(m);
390                     break;
391                 }
392         }
393         m = next;
394     }
395
396     /* merge and remove disconnected monitors */
397     m = mon_head;
398     while (m != NULL) {
399         monitor_t *next = m->next;
400         if (!m->wired) {
401             merge_monitors(m, mm);
402             remove_monitor(m);
403         }
404         m = next;
405     }
406
407     /* add one desktop to each new monitor */
408     for (m = mon_head; m != NULL; m = m->next)
409         if (m->desk == NULL && (running || pri_mon == NULL || m != pri_mon))
410             add_desktop(m, make_desktop(NULL));
411
412     free(sres);
413     update_motion_recorder();
414     return (num_monitors > 0);
415 }