1 /* * Copyright (c) 2012-2013 Bastien Dejean
4 * Redistribution and use in source and binary forms, with or without modification,
5 * are permitted provided that the following conditions are met:
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.
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.
38 monitor_t *make_monitor(xcb_rectangle_t rect)
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;
45 m->top_padding = m->right_padding = m->bottom_padding = m->left_padding = 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)
58 monitor_t *find_monitor(char *name)
60 for (monitor_t *m = mon_head; m != NULL; m = m->next)
61 if (streq(m->name, name))
66 monitor_t *get_monitor_by_id(xcb_randr_output_t id)
68 for (monitor_t *m = mon_head; m != NULL; m = m->next)
74 void fit_monitor(monitor_t *m, client_t *c)
76 xcb_rectangle_t crect = c->floating_rectangle;
77 xcb_rectangle_t mrect = m->rectangle;
78 while (crect.x < mrect.x)
79 crect.x += mrect.width;
80 while (crect.x > (mrect.x + mrect.width - 1))
81 crect.x -= mrect.width;
82 while (crect.y < mrect.y)
83 crect.y += mrect.height;
84 while (crect.y > (mrect.y + mrect.height - 1))
85 crect.y -= mrect.height;
86 c->floating_rectangle = crect;
89 void update_root(monitor_t *m)
91 xcb_rectangle_t rect = m->rectangle;
92 window_move_resize(m->root, rect.x, rect.y, rect.width, rect.height);
95 void focus_monitor(monitor_t *m)
100 PRINTF("focus monitor %s\n", m->name);
104 if (pointer_follows_monitor)
107 ewmh_update_current_desktop();
111 monitor_t *add_monitor(xcb_rectangle_t rect)
113 monitor_t *m = make_monitor(rect);
127 void remove_monitor(monitor_t *m)
129 PRINTF("remove monitor %s (0x%X)\n", m->name, m->id);
131 while (m->desk_head != NULL)
132 remove_desktop(m, m->desk_head);
133 monitor_t *prev = m->prev;
134 monitor_t *next = m->next;
135 monitor_t *last_mon = history_get_monitor(m);
147 mon = (last_mon == NULL ? (prev == NULL ? next : prev) : last_mon);
148 if (mon != NULL && mon->desk != NULL)
151 xcb_destroy_window(dpy, m->root);
157 void merge_monitors(monitor_t *ms, monitor_t *md)
159 PRINTF("merge %s into %s\n", ms->name, md->name);
161 desktop_t *d = ms->desk_head;
163 desktop_t *next = d->next;
164 if (d->root != NULL || strstr(d->name, DEFAULT_DESK_NAME) == NULL)
165 transfer_desktop(ms, md, d);
170 void swap_monitors(monitor_t *m1, monitor_t *m2)
172 if (m1 == NULL || m2 == NULL || m1 == m2)
177 else if (mon_head == m2)
181 else if (mon_tail == m2)
184 monitor_t *p1 = m1->prev;
185 monitor_t *n1 = m1->next;
186 monitor_t *p2 = m2->prev;
187 monitor_t *n2 = m2->next;
189 if (p1 != NULL && p1 != m2)
191 if (n1 != NULL && n1 != m2)
193 if (p2 != NULL && p2 != m1)
195 if (n2 != NULL && n2 != m1)
198 m1->prev = p2 == m1 ? m2 : p2;
199 m1->next = n2 == m1 ? m2 : n2;
200 m2->prev = p1 == m2 ? m1 : p1;
201 m2->next = n1 == m2 ? m1 : n1;
203 ewmh_update_wm_desktops();
204 ewmh_update_desktop_names();
205 ewmh_update_current_desktop();
209 monitor_t *closest_monitor(monitor_t *m, cycle_dir_t dir, desktop_select_t sel)
211 monitor_t *f = (dir == CYCLE_PREV ? m->prev : m->next);
213 f = (dir == CYCLE_PREV ? mon_tail : mon_head);
216 coordinates_t loc = {m, m->desk, NULL};
217 if (desktop_matches(&loc, &loc, sel))
219 f = (dir == CYCLE_PREV ? m->prev : m->next);
221 f = (dir == CYCLE_PREV ? mon_tail : mon_head);
227 monitor_t *nearest_monitor(monitor_t *m, direction_t dir, desktop_select_t sel)
230 monitor_t *nearest = NULL;
231 xcb_rectangle_t rect = m->rectangle;
232 for (monitor_t *f = mon_head; f != NULL; f = f->next) {
235 coordinates_t loc = {f, f->desk, NULL};
236 if (!desktop_matches(&loc, &loc, sel))
238 xcb_rectangle_t r = f->rectangle;
239 if ((dir == DIR_LEFT && r.x < rect.x) ||
240 (dir == DIR_RIGHT && r.x >= (rect.x + rect.width)) ||
241 (dir == DIR_UP && r.y < rect.y) ||
242 (dir == DIR_DOWN && r.y >= (rect.y + rect.height))) {
243 int d = abs((r.x + r.width / 2) - (rect.x + rect.width / 2)) +
244 abs((r.y + r.height / 2) - (rect.y + rect.height / 2));
254 bool import_monitors(void)
256 PUTS("import monitors");
257 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);
261 monitor_t *m, *mm = NULL;
263 int len = xcb_randr_get_screen_resources_current_outputs_length(sres);
264 xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_current_outputs(sres);
266 xcb_randr_get_output_info_cookie_t cookies[len];
267 for (int i = 0; i < len; i++)
268 cookies[i] = xcb_randr_get_output_info(dpy, outputs[i], XCB_CURRENT_TIME);
270 for (m = mon_head; m != NULL; m = m->next)
273 for (int i = 0; i < len; i++) {
274 xcb_randr_get_output_info_reply_t *info = xcb_randr_get_output_info_reply(dpy, cookies[i], NULL);
276 if (info->crtc != XCB_NONE) {
277 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);
279 xcb_rectangle_t rect = (xcb_rectangle_t) {cir->x, cir->y, cir->width, cir->height};
280 mm = get_monitor_by_id(outputs[i]);
282 mm->rectangle = rect;
284 for (desktop_t *d = mm->desk_head; d != NULL; d = d->next)
285 for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
286 fit_monitor(mm, n->client);
287 arrange(mm, mm->desk);
289 PRINTF("update monitor %s (0x%X)\n", mm->name, mm->id);
291 mm = add_monitor(rect);
292 char *name = (char *)xcb_randr_get_output_info_name(info);
293 size_t name_len = MIN(sizeof(mm->name), (size_t)xcb_randr_get_output_info_name_length(info) + 1);
294 snprintf(mm->name, name_len, "%s", name);
296 PRINTF("add monitor %s (0x%X)\n", mm->name, mm->id);
300 } else if (info->connection != XCB_RANDR_CONNECTION_DISCONNECTED) {
301 m = get_monitor_by_id(outputs[i]);
309 /* initially focus the primary monitor and add the first desktop to it */
310 xcb_randr_get_output_primary_reply_t *gpo = xcb_randr_get_output_primary_reply(dpy, xcb_randr_get_output_primary(dpy, root), NULL);
312 pri_mon = get_monitor_by_id(gpo->output);
313 if (!running && pri_mon != NULL) {
316 add_desktop(pri_mon, make_desktop(NULL));
317 ewmh_update_current_desktop();
322 /* handle overlapping monitors */
325 monitor_t *next = m->next;
327 for (monitor_t *mb = mon_head; mb != NULL; mb = mb->next)
328 if (mb != m && mb->wired && (m->desk == NULL || mb->desk == NULL)
329 && contains(mb->rectangle, m->rectangle)) {
332 merge_monitors(m, mb);
340 /* merge and remove disconnected monitors */
343 monitor_t *next = m->next;
345 merge_monitors(m, mm);
351 /* add one desktop to each new monitor */
352 for (m = mon_head; m != NULL; m = m->next)
353 if (m->desk == NULL && (running || pri_mon == NULL || m != pri_mon))
354 add_desktop(m, make_desktop(NULL));
357 update_motion_recorder();
358 return (num_monitors > 0);