+/* * 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 <limits.h>
-#include "settings.h"
#include "bspwm.h"
-#include "tree.h"
#include "desktop.h"
-#include "window.h"
-#include "query.h"
#include "ewmh.h"
+#include "history.h"
+#include "query.h"
+#include "settings.h"
+#include "tree.h"
+#include "window.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 = m->desk_head = m->desk_tail = 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);
return NULL;
}
-void fit_monitor(monitor_t *m, client_t *c)
+void embrace_client(monitor_t *m, client_t *c)
{
- xcb_rectangle_t crect = c->floating_rectangle;
- xcb_rectangle_t mrect = m->rectangle;
- while (crect.x < mrect.x)
- crect.x += mrect.width;
- while (crect.x > (mrect.x + mrect.width - 1))
- crect.x -= mrect.width;
- while (crect.y < mrect.y)
- crect.y += mrect.height;
- while (crect.y > (mrect.y + mrect.height - 1))
- crect.y -= mrect.height;
- c->floating_rectangle = crect;
+ 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)
window_move_resize(m->root, rect.x, rect.y, rect.width, rect.height);
}
-void select_monitor(monitor_t *m)
+void focus_monitor(monitor_t *m)
{
if (mon == m)
return;
- PRINTF("select monitor %s\n", m->name);
+ PRINTF("focus monitor %s\n", m->name);
- last_mon = mon;
mon = m;
if (pointer_follows_monitor)
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)
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);
f = (dir == CYCLE_PREV ? mon_tail : mon_head);
while (f != m) {
- if (desktop_matches(f->desk, sel))
+ 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)
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;
for (monitor_t *f = mon_head; f != NULL; f = f->next) {
if (f == m)
continue;
- if (!desktop_matches(f->desk, sel))
+ 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) ||
return false;
monitor_t *m, *mm = NULL;
- unsigned int num = 0;
int len = xcb_randr_get_screen_resources_current_outputs_length(sres);
xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_current_outputs(sres);
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;
- 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))
- 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) + 1);
- snprintf(mm->name, name_len, "%s", name);
- 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);
}