]> git.lizzy.rs Git - bspwm.git/blobdiff - monitor.c
Use default sxhkd config path in DM session
[bspwm.git] / monitor.c
index 35d1e24324902253880b0d5860e22e2c901ac081..ea8e8c156811fcdbf7c39ae0c9f99333ea86e585 100644 (file)
--- a/monitor.c
+++ b/monitor.c
@@ -1,22 +1,57 @@
+/* * Copyright (c) 2012-2013 Bastien Dejean
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <limits.h>
 #include <stdlib.h>
 #include <string.h>
 #include "bspwm.h"
-#include "types.h"
 #include "desktop.h"
+#include "ewmh.h"
+#include "history.h"
+#include "query.h"
+#include "settings.h"
 #include "tree.h"
-#include "monitor.h"
 #include "window.h"
-#include "ewmh.h"
+#include "monitor.h"
 
 monitor_t *make_monitor(xcb_rectangle_t rect)
 {
     monitor_t *m = malloc(sizeof(monitor_t));
     snprintf(m->name, sizeof(m->name), "%s%02d", DEFAULT_MON_NAME, ++monitor_uid);
     m->prev = m->next = NULL;
-    m->desk = m->last_desk = NULL;
+    m->desk = m->desk_head = m->desk_tail = NULL;
     m->rectangle = rect;
     m->top_padding = m->right_padding = m->bottom_padding = m->left_padding = 0;
     m->wired = true;
+    m->num_sticky = 0;
+    uint32_t mask = XCB_CW_EVENT_MASK;
+    uint32_t values[] = {XCB_EVENT_MASK_ENTER_WINDOW};
+    m->root = xcb_generate_id(dpy);
+    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);
+    window_lower(m->root);
+    if (focus_follows_pointer)
+        window_show(m->root);
     return m;
 }
 
@@ -36,6 +71,76 @@ monitor_t *get_monitor_by_id(xcb_randr_output_t id)
     return NULL;
 }
 
+void embrace_client(monitor_t *m, client_t *c)
+{
+    if ((c->floating_rectangle.x + c->floating_rectangle.width) <= m->rectangle.x)
+        c->floating_rectangle.x = m->rectangle.x;
+    else if (c->floating_rectangle.x >= (m->rectangle.x + m->rectangle.width))
+        c->floating_rectangle.x = (m->rectangle.x + m->rectangle.width) - c->floating_rectangle.width;
+    if ((c->floating_rectangle.y + c->floating_rectangle.height) <= m->rectangle.y)
+        c->floating_rectangle.y = m->rectangle.y;
+    else if (c->floating_rectangle.y >= (m->rectangle.y + m->rectangle.height))
+        c->floating_rectangle.y = (m->rectangle.y + m->rectangle.height) - c->floating_rectangle.height;
+}
+
+void translate_client(monitor_t *ms, monitor_t *md, client_t *c)
+{
+    if (frozen_pointer->action != ACTION_NONE || ms == md)
+        return;
+
+    /* Clip the rectangle to fit into the monitor.  Without this, the fitting
+     * algorithm doesn't work as expected. This also conserves the
+     * out-of-bounds regions */
+    int left_adjust = MAX((ms->rectangle.x - c->floating_rectangle.x), 0);
+    int top_adjust = MAX((ms->rectangle.y - c->floating_rectangle.y), 0);
+    int right_adjust = MAX((c->floating_rectangle.x + c->floating_rectangle.width) - (ms->rectangle.x + ms->rectangle.width), 0);
+    int bottom_adjust = MAX((c->floating_rectangle.y + c->floating_rectangle.height) - (ms->rectangle.y + ms->rectangle.height), 0);
+    c->floating_rectangle.x += left_adjust;
+    c->floating_rectangle.y += top_adjust;
+    c->floating_rectangle.width -= (left_adjust + right_adjust);
+    c->floating_rectangle.height -= (top_adjust + bottom_adjust);
+
+    int dx_s = c->floating_rectangle.x - ms->rectangle.x;
+    int dy_s = c->floating_rectangle.y - ms->rectangle.y;
+
+    int nume_x = dx_s * (md->rectangle.width - c->floating_rectangle.width);
+    int nume_y = dy_s * (md->rectangle.height - c->floating_rectangle.height);
+
+    int deno_x = ms->rectangle.width - c->floating_rectangle.width;
+    int deno_y = ms->rectangle.height - c->floating_rectangle.height;
+
+    int dx_d = (deno_x == 0 ? 0 : nume_x / deno_x);
+    int dy_d = (deno_y == 0 ? 0 : nume_y / deno_y);
+
+    /* Translate and undo clipping */
+    c->floating_rectangle.width += left_adjust + right_adjust;
+    c->floating_rectangle.height += top_adjust + bottom_adjust;
+    c->floating_rectangle.x = md->rectangle.x + dx_d - left_adjust;
+    c->floating_rectangle.y = md->rectangle.y + dy_d - top_adjust;
+}
+
+void update_root(monitor_t *m)
+{
+    xcb_rectangle_t rect = m->rectangle;
+    window_move_resize(m->root, rect.x, rect.y, rect.width, rect.height);
+}
+
+void focus_monitor(monitor_t *m)
+{
+    if (mon == m)
+        return;
+
+    PRINTF("focus monitor %s\n", m->name);
+
+    mon = m;
+
+    if (pointer_follows_monitor)
+        center_pointer(m);
+
+    ewmh_update_current_desktop();
+    put_status();
+}
+
 monitor_t *add_monitor(xcb_rectangle_t rect)
 {
     monitor_t *m = make_monitor(rect);
@@ -54,10 +159,13 @@ monitor_t *add_monitor(xcb_rectangle_t rect)
 
 void remove_monitor(monitor_t *m)
 {
+    PRINTF("remove monitor %s (0x%X)\n", m->name, m->id);
+
     while (m->desk_head != NULL)
         remove_desktop(m, m->desk_head);
     monitor_t *prev = m->prev;
     monitor_t *next = m->next;
+    monitor_t *last_mon = history_get_monitor(m);
     if (prev != NULL)
         prev->next = next;
     if (next != NULL)
@@ -66,19 +174,14 @@ void remove_monitor(monitor_t *m)
         mon_head = next;
     if (mon_tail == m)
         mon_tail = prev;
-    if (last_mon == m)
-        last_mon = NULL;
     if (pri_mon == m)
         pri_mon = NULL;
     if (mon == m) {
-        monitor_t *mm = (last_mon == NULL ? (prev == NULL ? next : prev) : last_mon);
-        if (mm != NULL) {
-            focus_node(mm, mm->desk, mm->desk->focus);
-            last_mon = NULL;
-        } else {
-            mon = NULL;
-        }
+        mon = (last_mon == NULL ? (prev == NULL ? next : prev) : last_mon);
+        if (mon != NULL && mon->desk != NULL)
+            update_current();
     }
+    xcb_destroy_window(dpy, m->root);
     free(m);
     num_monitors--;
     put_status();
@@ -91,7 +194,8 @@ void merge_monitors(monitor_t *ms, monitor_t *md)
     desktop_t *d = ms->desk_head;
     while (d != NULL) {
         desktop_t *next = d->next;
-        transfer_desktop(ms, md, d);
+        if (d->root != NULL || strstr(d->name, DEFAULT_DESK_NAME) == NULL)
+            transfer_desktop(ms, md, d);
         d = next;
     }
 }
@@ -135,6 +239,86 @@ void swap_monitors(monitor_t *m1, monitor_t *m2)
     put_status();
 }
 
+monitor_t *closest_monitor(monitor_t *m, cycle_dir_t dir, desktop_select_t sel)
+{
+    monitor_t *f = (dir == CYCLE_PREV ? m->prev : m->next);
+    if (f == NULL)
+        f = (dir == CYCLE_PREV ? mon_tail : mon_head);
+
+    while (f != m) {
+        coordinates_t loc = {m, m->desk, NULL};
+        if (desktop_matches(&loc, &loc, sel))
+            return f;
+        f = (dir == CYCLE_PREV ? m->prev : m->next);
+        if (f == NULL)
+            f = (dir == CYCLE_PREV ? mon_tail : mon_head);
+    }
+
+    return NULL;
+}
+
+bool is_inside_monitor(monitor_t *m, xcb_point_t pt)
+{
+    xcb_rectangle_t r = m->rectangle;
+    return (r.x <= pt.x && pt.x < (r.x + r.width)
+            && r.y <= pt.y && pt.y < (r.y + r.height));
+}
+
+monitor_t *monitor_from_point(xcb_point_t pt)
+{
+    for (monitor_t *m = mon_head; m != NULL; m = m->next)
+        if (is_inside_monitor(m, pt))
+            return m;
+    return NULL;
+}
+
+monitor_t *monitor_from_client(client_t *c)
+{
+    xcb_point_t pt = {c->floating_rectangle.x, c->floating_rectangle.y};
+    monitor_t *nearest = monitor_from_point(pt);
+    if (nearest == NULL) {
+        int x = (c->floating_rectangle.x + c->floating_rectangle.width) / 2;
+        int y = (c->floating_rectangle.y + c->floating_rectangle.height) / 2;
+        int dmin = INT_MAX;
+        for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+            xcb_rectangle_t r = m->rectangle;
+            int d = abs((r.x + r.width / 2) - x) + abs((r.y + r.height / 2) - y);
+            if (d < dmin) {
+                dmin = d;
+                nearest = m;
+            }
+        }
+    }
+    return nearest;
+}
+
+monitor_t *nearest_monitor(monitor_t *m, direction_t dir, desktop_select_t sel)
+{
+    int dmin = INT_MAX;
+    monitor_t *nearest = NULL;
+    xcb_rectangle_t rect = m->rectangle;
+    for (monitor_t *f = mon_head; f != NULL; f = f->next) {
+        if (f == m)
+            continue;
+        coordinates_t loc = {f, f->desk, NULL};
+        if (!desktop_matches(&loc, &loc, sel))
+            continue;
+        xcb_rectangle_t r = f->rectangle;
+        if ((dir == DIR_LEFT && r.x < rect.x) ||
+                (dir == DIR_RIGHT && r.x >= (rect.x + rect.width)) ||
+                (dir == DIR_UP && r.y < rect.y) ||
+                (dir == DIR_DOWN && r.y >= (rect.y + rect.height))) {
+            int d = abs((r.x + r.width / 2) - (rect.x + rect.width / 2)) +
+                abs((r.y + r.height / 2) - (rect.y + rect.height / 2));
+            if (d < dmin) {
+                dmin = d;
+                nearest = f;
+            }
+        }
+    }
+    return nearest;
+}
+
 bool import_monitors(void)
 {
     PUTS("import monitors");
@@ -142,6 +326,8 @@ bool import_monitors(void)
     if (sres == NULL)
         return false;
 
+    monitor_t *m, *mm = NULL;
+
     int len = xcb_randr_get_screen_resources_current_outputs_length(sres);
     xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_current_outputs(sres);
 
@@ -149,40 +335,41 @@ bool import_monitors(void)
     for (int i = 0; i < len; i++)
         cookies[i] = xcb_randr_get_output_info(dpy, outputs[i], XCB_CURRENT_TIME);
 
-    for (monitor_t *m = mon_head; m != NULL; m = m->next)
+    for (m = mon_head; m != NULL; m = m->next)
         m->wired = false;
 
-    monitor_t *mm = NULL;
-    unsigned int num = 0;
-
     for (int i = 0; i < len; i++) {
         xcb_randr_get_output_info_reply_t *info = xcb_randr_get_output_info_reply(dpy, cookies[i], NULL);
-        if (info != NULL && info->crtc != XCB_NONE) {
-
-            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);
-            if (cir != NULL) {
-                xcb_rectangle_t rect = (xcb_rectangle_t) {cir->x, cir->y, cir->width, cir->height};
-                mm = get_monitor_by_id(outputs[i]);
-                if (mm != NULL) {
-                    mm->rectangle = rect;
-                    for (desktop_t *d = mm->desk_head; d != NULL; d = d->next)
-                        for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
-                            fit_monitor(mm, n->client);
-                    arrange(mm, mm->desk);
-                    mm->wired = true;
-                    PRINTF("update monitor %s (0x%X)\n", mm->name, mm->id);
-                } else {
-                    mm = add_monitor(rect);
-                    char *name = (char *)xcb_randr_get_output_info_name(info);
-                    size_t name_len = MIN(sizeof(mm->name), (size_t)xcb_randr_get_output_info_name_length(info));
-                    strncpy(mm->name, name, name_len);
-                    mm->name[name_len] = '\0';
-                    mm->id = outputs[i];
-                    PRINTF("add monitor %s (0x%X)\n", mm->name, mm->id);
+        if (info != NULL) {
+            if (info->crtc != XCB_NONE) {
+                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);
+                if (cir != NULL) {
+                    xcb_rectangle_t rect = (xcb_rectangle_t) {cir->x, cir->y, cir->width, cir->height};
+                    mm = get_monitor_by_id(outputs[i]);
+                    if (mm != NULL) {
+                        mm->rectangle = rect;
+                        update_root(mm);
+                        for (desktop_t *d = mm->desk_head; d != NULL; d = d->next)
+                            for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
+                                translate_client(mm, mm, n->client);
+                        arrange(mm, mm->desk);
+                        mm->wired = true;
+                        PRINTF("update monitor %s (0x%X)\n", mm->name, mm->id);
+                    } else {
+                        mm = add_monitor(rect);
+                        char *name = (char *)xcb_randr_get_output_info_name(info);
+                        size_t name_len = MIN(sizeof(mm->name), (size_t)xcb_randr_get_output_info_name_length(info) + 1);
+                        snprintf(mm->name, name_len, "%s", name);
+                        mm->id = outputs[i];
+                        PRINTF("add monitor %s (0x%X)\n", mm->name, mm->id);
+                    }
                 }
-                num++;
+                free(cir);
+            } else if (!remove_disabled_monitor && info->connection != XCB_RANDR_CONNECTION_DISCONNECTED) {
+                m = get_monitor_by_id(outputs[i]);
+                if (m != NULL)
+                    m->wired = true;
             }
-            free(cir);
         }
         free(info);
     }
@@ -200,23 +387,40 @@ bool import_monitors(void)
     }
     free(gpo);
 
-    /* add one desktop to each new monitor */
-    for (monitor_t *m = mon_head; m != NULL; m = m->next)
-        if (m->desk == NULL && (running || pri_mon == NULL || m != pri_mon))
-            add_desktop(m, make_desktop(NULL));
+    /* handle overlapping monitors */
+    m = mon_head;
+    while (m != NULL) {
+        monitor_t *next = m->next;
+        if (m->wired) {
+            for (monitor_t *mb = mon_head; mb != NULL; mb = mb->next)
+                if (mb != m && mb->wired && (m->desk == NULL || mb->desk == NULL)
+                        && contains(mb->rectangle, m->rectangle)) {
+                    if (mm == m)
+                        mm = mb;
+                    merge_monitors(m, mb);
+                    remove_monitor(m);
+                    break;
+                }
+        }
+        m = next;
+    }
 
     /* merge and remove disconnected monitors */
-    monitor_t *m = mon_head;
+    m = mon_head;
     while (m != NULL) {
         monitor_t *next = m->next;
         if (!m->wired) {
-            PRINTF("remove monitor %s (0x%X)\n", m->name, m->id);
             merge_monitors(m, mm);
             remove_monitor(m);
         }
         m = next;
     }
 
+    /* add one desktop to each new monitor */
+    for (m = mon_head; m != NULL; m = m->next)
+        if (m->desk == NULL && (running || pri_mon == NULL || m != pri_mon))
+            add_desktop(m, make_desktop(NULL));
+
     free(sres);
     update_motion_recorder();
     return (num_monitors > 0);