2014-10-22 11:53:03 +02:00
|
|
|
/* Copyright (c) 2012, Bastien Dejean
|
2013-10-08 21:05:56 +02:00
|
|
|
* All rights reserved.
|
2014-01-19 14:41:37 +01:00
|
|
|
*
|
2014-01-18 16:30:00 +01:00
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
2014-01-19 14:41:37 +01:00
|
|
|
*
|
2014-01-18 16:30:00 +01:00
|
|
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
|
|
* list of conditions and the following disclaimer.
|
|
|
|
* 2. 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.
|
2014-01-19 14:41:37 +01:00
|
|
|
*
|
2014-01-18 16:30:00 +01:00
|
|
|
* 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 OWNER OR CONTRIBUTORS BE LIABLE FOR
|
2013-10-08 21:05:56 +02:00
|
|
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
2014-01-18 16:30:00 +01:00
|
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
2013-10-08 21:05:56 +02:00
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
2013-10-01 10:48:03 +02:00
|
|
|
#include <limits.h>
|
2013-09-19 15:02:49 +02:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2015-12-22 19:25:45 +01:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdbool.h>
|
2013-09-19 15:02:49 +02:00
|
|
|
#include "bspwm.h"
|
2013-09-19 15:38:22 +02:00
|
|
|
#include "desktop.h"
|
2013-09-19 15:02:49 +02:00
|
|
|
#include "ewmh.h"
|
2013-10-01 10:48:03 +02:00
|
|
|
#include "history.h"
|
|
|
|
#include "query.h"
|
|
|
|
#include "settings.h"
|
2016-02-26 14:22:03 +01:00
|
|
|
#include "geometry.h"
|
2013-10-01 10:48:03 +02:00
|
|
|
#include "tree.h"
|
2015-05-09 21:12:19 +02:00
|
|
|
#include "subscribe.h"
|
2013-10-01 10:48:03 +02:00
|
|
|
#include "window.h"
|
2013-09-20 11:57:09 +02:00
|
|
|
#include "monitor.h"
|
2013-09-19 15:02:49 +02:00
|
|
|
|
2016-03-16 15:15:27 +01:00
|
|
|
monitor_t *make_monitor(xcb_rectangle_t *rect, uint32_t id)
|
2013-09-19 15:02:49 +02:00
|
|
|
{
|
2014-01-18 16:30:00 +01:00
|
|
|
monitor_t *m = malloc(sizeof(monitor_t));
|
2016-03-16 15:15:27 +01:00
|
|
|
if (id == XCB_NONE) {
|
|
|
|
m->id = xcb_generate_id(dpy);
|
|
|
|
}
|
|
|
|
m->randr_id = XCB_NONE;
|
|
|
|
snprintf(m->name, sizeof(m->name), "%s", DEFAULT_MON_NAME);
|
2016-03-21 14:28:04 +01:00
|
|
|
m->padding = padding;
|
|
|
|
m->border_width = border_width;
|
|
|
|
m->window_gap = window_gap;
|
2015-12-15 01:46:31 +11:00
|
|
|
m->root = XCB_NONE;
|
2014-01-18 16:30:00 +01:00
|
|
|
m->prev = m->next = NULL;
|
|
|
|
m->desk = m->desk_head = m->desk_tail = NULL;
|
|
|
|
m->wired = true;
|
2015-12-22 19:25:45 +01:00
|
|
|
m->sticky_count = 0;
|
2015-11-22 14:41:00 +01:00
|
|
|
if (rect != NULL) {
|
|
|
|
update_root(m, rect);
|
|
|
|
} else {
|
|
|
|
m->rectangle = (xcb_rectangle_t) {0, 0, screen_width, screen_height};
|
2015-06-12 22:05:22 +02:00
|
|
|
}
|
2014-01-18 16:30:00 +01:00
|
|
|
return m;
|
2013-09-19 15:02:49 +02:00
|
|
|
}
|
|
|
|
|
2015-12-30 11:36:14 +01:00
|
|
|
void update_root(monitor_t *m, xcb_rectangle_t *rect)
|
2015-11-22 14:41:00 +01:00
|
|
|
{
|
2015-12-30 11:36:14 +01:00
|
|
|
xcb_rectangle_t last_rect = m->rectangle;
|
|
|
|
m->rectangle = *rect;
|
2015-11-22 14:41:00 +01:00
|
|
|
if (m->root == XCB_NONE) {
|
|
|
|
uint32_t values[] = {XCB_EVENT_MASK_ENTER_WINDOW};
|
|
|
|
m->root = xcb_generate_id(dpy);
|
2015-12-30 11:36:14 +01:00
|
|
|
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, XCB_CW_EVENT_MASK, values);
|
2015-11-22 14:41:00 +01:00
|
|
|
xcb_icccm_set_wm_class(dpy, m->root, sizeof(ROOT_WINDOW_IC), ROOT_WINDOW_IC);
|
|
|
|
window_lower(m->root);
|
|
|
|
if (focus_follows_pointer) {
|
|
|
|
window_show(m->root);
|
|
|
|
}
|
|
|
|
} else {
|
2015-12-30 11:36:14 +01:00
|
|
|
window_move_resize(m->root, rect->x, rect->y, rect->width, rect->height);
|
2016-03-23 11:24:47 +01:00
|
|
|
put_status(SBSC_MASK_MONITOR_GEOMETRY, "monitor_geometry 0x%08X %ux%u+%i+%i\n",
|
2016-03-16 15:15:27 +01:00
|
|
|
m->id, rect->width, rect->height, rect->x, rect->y);
|
2015-12-30 11:36:14 +01:00
|
|
|
}
|
|
|
|
for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
|
|
|
|
for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
|
2016-03-26 11:47:17 +01:00
|
|
|
if (n->client == NULL) {
|
|
|
|
continue;
|
|
|
|
}
|
2015-12-30 11:36:14 +01:00
|
|
|
adapt_geometry(&last_rect, rect, n);
|
|
|
|
}
|
|
|
|
arrange(m, d);
|
2015-11-22 14:41:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-09 15:00:47 +01:00
|
|
|
void rename_monitor(monitor_t *m, const char *name)
|
|
|
|
{
|
2016-03-23 11:24:47 +01:00
|
|
|
put_status(SBSC_MASK_MONITOR_RENAME, "monitor_rename 0x%08X %s %s\n", m->id, m->name, name);
|
2015-11-09 15:00:47 +01:00
|
|
|
|
|
|
|
snprintf(m->name, sizeof(m->name), "%s", name);
|
|
|
|
|
|
|
|
put_status(SBSC_MASK_REPORT);
|
|
|
|
}
|
|
|
|
|
2013-09-19 15:02:49 +02:00
|
|
|
monitor_t *find_monitor(char *name)
|
|
|
|
{
|
2015-12-22 19:25:45 +01:00
|
|
|
for (monitor_t *m = mon_head; m != NULL; m = m->next) {
|
|
|
|
if (streq(m->name, name)) {
|
2014-01-18 16:30:00 +01:00
|
|
|
return m;
|
2015-12-22 19:25:45 +01:00
|
|
|
}
|
|
|
|
}
|
2014-01-18 16:30:00 +01:00
|
|
|
return NULL;
|
2013-09-19 15:02:49 +02:00
|
|
|
}
|
|
|
|
|
2016-03-16 15:15:27 +01:00
|
|
|
monitor_t *get_monitor_by_randr_id(xcb_randr_output_t id)
|
2013-09-19 15:02:49 +02:00
|
|
|
{
|
2015-12-22 19:25:45 +01:00
|
|
|
for (monitor_t *m = mon_head; m != NULL; m = m->next) {
|
2016-03-16 15:15:27 +01:00
|
|
|
if (m->randr_id == id) {
|
2014-01-18 16:30:00 +01:00
|
|
|
return m;
|
2015-12-22 19:25:45 +01:00
|
|
|
}
|
|
|
|
}
|
2014-01-18 16:30:00 +01:00
|
|
|
return NULL;
|
2013-09-19 15:02:49 +02:00
|
|
|
}
|
|
|
|
|
2013-11-28 11:32:20 +01:00
|
|
|
void embrace_client(monitor_t *m, client_t *c)
|
|
|
|
{
|
2015-12-22 19:25:45 +01:00
|
|
|
if ((c->floating_rectangle.x + c->floating_rectangle.width) <= m->rectangle.x) {
|
2014-01-18 16:30:00 +01:00
|
|
|
c->floating_rectangle.x = m->rectangle.x;
|
2015-12-22 19:25:45 +01:00
|
|
|
} else if (c->floating_rectangle.x >= (m->rectangle.x + m->rectangle.width)) {
|
2014-01-18 16:30:00 +01:00
|
|
|
c->floating_rectangle.x = (m->rectangle.x + m->rectangle.width) - c->floating_rectangle.width;
|
2015-12-22 19:25:45 +01:00
|
|
|
}
|
|
|
|
if ((c->floating_rectangle.y + c->floating_rectangle.height) <= m->rectangle.y) {
|
2014-01-18 16:30:00 +01:00
|
|
|
c->floating_rectangle.y = m->rectangle.y;
|
2015-12-22 19:25:45 +01:00
|
|
|
} else if (c->floating_rectangle.y >= (m->rectangle.y + m->rectangle.height)) {
|
2014-01-18 16:30:00 +01:00
|
|
|
c->floating_rectangle.y = (m->rectangle.y + m->rectangle.height) - c->floating_rectangle.height;
|
2015-12-22 19:25:45 +01:00
|
|
|
}
|
2013-11-28 11:32:20 +01:00
|
|
|
}
|
|
|
|
|
2015-12-22 19:25:45 +01:00
|
|
|
void adapt_geometry(xcb_rectangle_t *rs, xcb_rectangle_t *rd, node_t *n)
|
2013-09-20 11:57:09 +02:00
|
|
|
{
|
2015-12-22 19:25:45 +01:00
|
|
|
for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
|
2016-03-26 11:47:17 +01:00
|
|
|
if (f->client == NULL) {
|
|
|
|
continue;
|
|
|
|
}
|
2015-12-22 19:25:45 +01:00
|
|
|
client_t *c = f->client;
|
|
|
|
/* 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((rs->x - c->floating_rectangle.x), 0);
|
|
|
|
int top_adjust = MAX((rs->y - c->floating_rectangle.y), 0);
|
|
|
|
int right_adjust = MAX((c->floating_rectangle.x + c->floating_rectangle.width) - (rs->x + rs->width), 0);
|
|
|
|
int bottom_adjust = MAX((c->floating_rectangle.y + c->floating_rectangle.height) - (rs->y + rs->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 - rs->x;
|
|
|
|
int dy_s = c->floating_rectangle.y - rs->y;
|
|
|
|
|
|
|
|
int nume_x = dx_s * (rd->width - c->floating_rectangle.width);
|
|
|
|
int nume_y = dy_s * (rd->height - c->floating_rectangle.height);
|
|
|
|
|
|
|
|
int deno_x = rs->width - c->floating_rectangle.width;
|
|
|
|
int deno_y = rs->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 = rd->x + dx_d - left_adjust;
|
|
|
|
c->floating_rectangle.y = rd->y + dy_d - top_adjust;
|
|
|
|
}
|
2013-10-26 13:13:42 -04:00
|
|
|
}
|
|
|
|
|
2013-10-02 11:01:21 +02:00
|
|
|
void focus_monitor(monitor_t *m)
|
2013-09-20 11:57:09 +02:00
|
|
|
{
|
2015-12-22 19:25:45 +01:00
|
|
|
if (mon == m) {
|
2014-01-18 16:30:00 +01:00
|
|
|
return;
|
2015-12-22 19:25:45 +01:00
|
|
|
}
|
2013-09-20 11:57:09 +02:00
|
|
|
|
2014-01-18 16:30:00 +01:00
|
|
|
mon = m;
|
2013-09-20 11:57:09 +02:00
|
|
|
|
2015-11-09 15:00:47 +01:00
|
|
|
if (pointer_follows_monitor) {
|
2016-04-07 18:08:32 +02:00
|
|
|
xcb_point_t pt;
|
|
|
|
query_pointer(NULL, &pt);
|
|
|
|
monitor_t *mp = monitor_from_point(pt);
|
|
|
|
if (mp != m) {
|
|
|
|
center_pointer(m->rectangle);
|
|
|
|
}
|
2015-11-09 15:00:47 +01:00
|
|
|
}
|
2013-09-20 11:57:09 +02:00
|
|
|
|
2016-03-23 11:24:47 +01:00
|
|
|
put_status(SBSC_MASK_MONITOR_FOCUS, "monitor_focus 0x%08X\n", m->id);
|
2013-09-20 11:57:09 +02:00
|
|
|
}
|
|
|
|
|
2015-11-09 15:00:47 +01:00
|
|
|
void add_monitor(monitor_t *m)
|
2013-09-19 15:02:49 +02:00
|
|
|
{
|
2015-11-09 15:00:47 +01:00
|
|
|
xcb_rectangle_t r = m->rectangle;
|
|
|
|
|
2014-01-18 16:30:00 +01:00
|
|
|
if (mon == NULL) {
|
|
|
|
mon = m;
|
|
|
|
mon_head = m;
|
|
|
|
mon_tail = m;
|
|
|
|
} else {
|
2016-02-23 21:19:12 +01:00
|
|
|
monitor_t *a = mon_head;
|
2016-02-26 14:22:03 +01:00
|
|
|
while (a != NULL && rect_cmp(m->rectangle, a->rectangle) > 0) {
|
2016-02-23 21:19:12 +01:00
|
|
|
a = a->next;
|
|
|
|
}
|
|
|
|
if (a != NULL) {
|
|
|
|
monitor_t *b = a->prev;
|
|
|
|
if (b != NULL) {
|
|
|
|
b->next = m;
|
|
|
|
} else {
|
|
|
|
mon_head = m;
|
|
|
|
}
|
|
|
|
m->prev = b;
|
|
|
|
m->next = a;
|
|
|
|
a->prev = m;
|
|
|
|
} else {
|
|
|
|
mon_tail->next = m;
|
|
|
|
m->prev = mon_tail;
|
|
|
|
mon_tail = m;
|
|
|
|
}
|
2014-01-18 16:30:00 +01:00
|
|
|
}
|
2015-12-22 19:25:45 +01:00
|
|
|
|
2016-03-23 11:24:47 +01:00
|
|
|
put_status(SBSC_MASK_MONITOR_ADD, "monitor_add 0x%08X %s %ux%u+%i+%i\n", m->id, m->name, r.width, r.height, r.x, r.y);
|
2015-12-22 19:25:45 +01:00
|
|
|
|
|
|
|
put_status(SBSC_MASK_REPORT);
|
2013-09-19 15:02:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void remove_monitor(monitor_t *m)
|
|
|
|
{
|
2015-11-05 14:00:49 +01:00
|
|
|
while (m->desk_head != NULL) {
|
2014-01-18 16:30:00 +01:00
|
|
|
remove_desktop(m, m->desk_head);
|
2015-11-05 14:00:49 +01:00
|
|
|
}
|
|
|
|
|
2014-01-18 16:30:00 +01:00
|
|
|
monitor_t *prev = m->prev;
|
|
|
|
monitor_t *next = m->next;
|
2015-12-22 19:25:45 +01:00
|
|
|
monitor_t *last_mon = history_last_monitor(m);
|
2015-11-05 14:00:49 +01:00
|
|
|
|
|
|
|
if (prev != NULL) {
|
2014-01-18 16:30:00 +01:00
|
|
|
prev->next = next;
|
2015-11-05 14:00:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (next != NULL) {
|
2014-01-18 16:30:00 +01:00
|
|
|
next->prev = prev;
|
2015-11-05 14:00:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (mon_head == m) {
|
2014-01-18 16:30:00 +01:00
|
|
|
mon_head = next;
|
2015-11-05 14:00:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (mon_tail == m) {
|
2014-01-18 16:30:00 +01:00
|
|
|
mon_tail = prev;
|
2015-11-05 14:00:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (pri_mon == m) {
|
2014-01-18 16:30:00 +01:00
|
|
|
pri_mon = NULL;
|
2015-11-05 14:00:49 +01:00
|
|
|
}
|
|
|
|
|
2014-01-18 16:30:00 +01:00
|
|
|
if (mon == m) {
|
|
|
|
mon = (last_mon == NULL ? (prev == NULL ? next : prev) : last_mon);
|
2015-11-05 14:00:49 +01:00
|
|
|
if (mon != NULL && mon->desk != NULL) {
|
2015-12-22 19:25:45 +01:00
|
|
|
update_focused();
|
2015-11-05 14:00:49 +01:00
|
|
|
}
|
2014-01-18 16:30:00 +01:00
|
|
|
}
|
2015-11-05 14:00:49 +01:00
|
|
|
|
2016-03-23 11:24:47 +01:00
|
|
|
put_status(SBSC_MASK_MONITOR_REMOVE, "monitor_remove 0x%08X\n", m->id);
|
2015-12-22 19:25:45 +01:00
|
|
|
|
2015-06-12 22:05:22 +02:00
|
|
|
xcb_destroy_window(dpy, m->root);
|
2014-01-18 16:30:00 +01:00
|
|
|
free(m);
|
2015-12-22 19:25:45 +01:00
|
|
|
|
2015-05-09 21:12:19 +02:00
|
|
|
put_status(SBSC_MASK_REPORT);
|
2013-09-19 15:02:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void merge_monitors(monitor_t *ms, monitor_t *md)
|
|
|
|
{
|
2015-04-26 14:37:30 +02:00
|
|
|
if (ms == NULL || md == NULL || ms == md) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-01-18 16:30:00 +01:00
|
|
|
desktop_t *d = ms->desk_head;
|
|
|
|
while (d != NULL) {
|
|
|
|
desktop_t *next = d->next;
|
2015-12-22 19:25:45 +01:00
|
|
|
if (d->root != NULL || strstr(d->name, DEFAULT_DESK_NAME) == NULL) {
|
2014-01-18 16:30:00 +01:00
|
|
|
transfer_desktop(ms, md, d);
|
2015-12-22 19:25:45 +01:00
|
|
|
}
|
2014-01-18 16:30:00 +01:00
|
|
|
d = next;
|
|
|
|
}
|
2013-09-19 15:02:49 +02:00
|
|
|
}
|
|
|
|
|
2016-02-01 11:46:03 +01:00
|
|
|
bool swap_monitors(monitor_t *m1, monitor_t *m2)
|
2013-09-19 15:02:49 +02:00
|
|
|
{
|
2015-12-22 19:25:45 +01:00
|
|
|
if (m1 == NULL || m2 == NULL || m1 == m2) {
|
2016-02-01 11:46:03 +01:00
|
|
|
return false;
|
2015-12-22 19:25:45 +01:00
|
|
|
}
|
2014-01-18 16:30:00 +01:00
|
|
|
|
2016-03-23 11:24:47 +01:00
|
|
|
put_status(SBSC_MASK_MONITOR_SWAP, "monitor_swap 0x%08X 0x%08X\n", m1->id, m2->id);
|
2016-02-01 11:46:03 +01:00
|
|
|
|
2015-12-22 19:25:45 +01:00
|
|
|
if (mon_head == m1) {
|
2014-01-18 16:30:00 +01:00
|
|
|
mon_head = m2;
|
2015-12-22 19:25:45 +01:00
|
|
|
} else if (mon_head == m2) {
|
2014-01-18 16:30:00 +01:00
|
|
|
mon_head = m1;
|
2015-12-22 19:25:45 +01:00
|
|
|
}
|
|
|
|
if (mon_tail == m1) {
|
2014-01-18 16:30:00 +01:00
|
|
|
mon_tail = m2;
|
2015-12-22 19:25:45 +01:00
|
|
|
} else if (mon_tail == m2) {
|
2014-01-18 16:30:00 +01:00
|
|
|
mon_tail = m1;
|
2015-12-22 19:25:45 +01:00
|
|
|
}
|
2014-01-18 16:30:00 +01:00
|
|
|
|
|
|
|
monitor_t *p1 = m1->prev;
|
|
|
|
monitor_t *n1 = m1->next;
|
|
|
|
monitor_t *p2 = m2->prev;
|
|
|
|
monitor_t *n2 = m2->next;
|
|
|
|
|
2015-12-22 19:25:45 +01:00
|
|
|
if (p1 != NULL && p1 != m2) {
|
2014-01-18 16:30:00 +01:00
|
|
|
p1->next = m2;
|
2015-12-22 19:25:45 +01:00
|
|
|
}
|
|
|
|
if (n1 != NULL && n1 != m2) {
|
2014-01-18 16:30:00 +01:00
|
|
|
n1->prev = m2;
|
2015-12-22 19:25:45 +01:00
|
|
|
}
|
|
|
|
if (p2 != NULL && p2 != m1) {
|
2014-01-18 16:30:00 +01:00
|
|
|
p2->next = m1;
|
2015-12-22 19:25:45 +01:00
|
|
|
}
|
|
|
|
if (n2 != NULL && n2 != m1) {
|
2014-01-18 16:30:00 +01:00
|
|
|
n2->prev = m1;
|
2015-12-22 19:25:45 +01:00
|
|
|
}
|
2014-01-18 16:30:00 +01:00
|
|
|
|
|
|
|
m1->prev = p2 == m1 ? m2 : p2;
|
|
|
|
m1->next = n2 == m1 ? m2 : n2;
|
|
|
|
m2->prev = p1 == m2 ? m1 : p1;
|
|
|
|
m2->next = n1 == m2 ? m1 : n1;
|
|
|
|
|
|
|
|
ewmh_update_wm_desktops();
|
|
|
|
ewmh_update_desktop_names();
|
|
|
|
ewmh_update_current_desktop();
|
2015-12-22 19:25:45 +01:00
|
|
|
|
2015-05-09 21:12:19 +02:00
|
|
|
put_status(SBSC_MASK_REPORT);
|
2016-02-01 11:46:03 +01:00
|
|
|
return true;
|
2013-09-19 15:02:49 +02:00
|
|
|
}
|
|
|
|
|
2015-11-26 20:07:46 +01:00
|
|
|
monitor_t *closest_monitor(monitor_t *m, cycle_dir_t dir, monitor_select_t sel)
|
2013-09-20 11:57:09 +02:00
|
|
|
{
|
2014-01-18 16:30:00 +01:00
|
|
|
monitor_t *f = (dir == CYCLE_PREV ? m->prev : m->next);
|
2015-11-26 20:07:46 +01:00
|
|
|
|
|
|
|
if (f == NULL) {
|
2014-01-18 16:30:00 +01:00
|
|
|
f = (dir == CYCLE_PREV ? mon_tail : mon_head);
|
2015-11-26 20:07:46 +01:00
|
|
|
}
|
2014-01-18 16:30:00 +01:00
|
|
|
|
|
|
|
while (f != m) {
|
2016-04-15 17:53:21 +02:00
|
|
|
coordinates_t loc = {f, NULL, NULL};
|
2015-11-26 20:07:46 +01:00
|
|
|
if (monitor_matches(&loc, &loc, sel)) {
|
2014-01-18 16:30:00 +01:00
|
|
|
return f;
|
2015-11-26 20:07:46 +01:00
|
|
|
}
|
2016-04-15 17:53:21 +02:00
|
|
|
f = (dir == CYCLE_PREV ? f->prev : f->next);
|
2015-11-26 20:07:46 +01:00
|
|
|
if (f == NULL) {
|
2014-01-18 16:30:00 +01:00
|
|
|
f = (dir == CYCLE_PREV ? mon_tail : mon_head);
|
2015-11-26 20:07:46 +01:00
|
|
|
}
|
2014-01-18 16:30:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
2013-09-20 11:57:09 +02:00
|
|
|
}
|
|
|
|
|
2013-10-27 11:25:34 +01:00
|
|
|
bool is_inside_monitor(monitor_t *m, xcb_point_t pt)
|
|
|
|
{
|
2015-12-23 22:21:43 +01:00
|
|
|
return is_inside(pt, m->rectangle);
|
2013-10-27 11:25:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
monitor_t *monitor_from_point(xcb_point_t pt)
|
|
|
|
{
|
2015-11-09 15:00:47 +01:00
|
|
|
for (monitor_t *m = mon_head; m != NULL; m = m->next) {
|
|
|
|
if (is_inside_monitor(m, pt)) {
|
2014-01-18 16:30:00 +01:00
|
|
|
return m;
|
2015-11-09 15:00:47 +01:00
|
|
|
}
|
|
|
|
}
|
2014-01-18 16:30:00 +01:00
|
|
|
return NULL;
|
2013-10-27 11:25:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
monitor_t *monitor_from_client(client_t *c)
|
|
|
|
{
|
2016-03-26 14:25:40 +01:00
|
|
|
int16_t xc = c->floating_rectangle.x + c->floating_rectangle.width/2;
|
|
|
|
int16_t yc = c->floating_rectangle.y + c->floating_rectangle.height/2;
|
|
|
|
xcb_point_t pt = {xc, yc};
|
2014-01-18 16:30:00 +01:00
|
|
|
monitor_t *nearest = monitor_from_point(pt);
|
|
|
|
if (nearest == NULL) {
|
|
|
|
int dmin = INT_MAX;
|
|
|
|
for (monitor_t *m = mon_head; m != NULL; m = m->next) {
|
|
|
|
xcb_rectangle_t r = m->rectangle;
|
2016-03-26 14:25:40 +01:00
|
|
|
int d = abs((r.x + r.width / 2) - xc) + abs((r.y + r.height / 2) - yc);
|
2014-01-18 16:30:00 +01:00
|
|
|
if (d < dmin) {
|
|
|
|
dmin = d;
|
|
|
|
nearest = m;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nearest;
|
2013-10-27 11:25:34 +01:00
|
|
|
}
|
|
|
|
|
2015-11-26 20:07:46 +01:00
|
|
|
monitor_t *nearest_monitor(monitor_t *m, direction_t dir, monitor_select_t sel)
|
2013-09-20 11:57:09 +02:00
|
|
|
{
|
2014-01-18 16:30:00 +01:00
|
|
|
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) {
|
2015-11-26 20:07:46 +01:00
|
|
|
if (f == m) {
|
2014-01-18 16:30:00 +01:00
|
|
|
continue;
|
2015-11-26 20:07:46 +01:00
|
|
|
}
|
|
|
|
coordinates_t loc = {f, NULL, NULL};
|
|
|
|
if (!monitor_matches(&loc, &loc, sel)) {
|
2014-01-18 16:30:00 +01:00
|
|
|
continue;
|
2015-11-26 20:07:46 +01:00
|
|
|
}
|
2014-01-18 16:30:00 +01:00
|
|
|
xcb_rectangle_t r = f->rectangle;
|
2015-12-22 19:25:45 +01:00
|
|
|
if ((dir == DIR_WEST && r.x < rect.x) ||
|
|
|
|
(dir == DIR_EAST && r.x >= (rect.x + rect.width)) ||
|
|
|
|
(dir == DIR_NORTH && r.y < rect.y) ||
|
|
|
|
(dir == DIR_SOUTH && r.y >= (rect.y + rect.height))) {
|
2014-01-18 16:30:00 +01:00
|
|
|
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;
|
2013-09-20 11:57:09 +02:00
|
|
|
}
|
|
|
|
|
2014-05-05 11:35:06 +02:00
|
|
|
bool update_monitors(void)
|
2013-09-19 15:02:49 +02:00
|
|
|
{
|
2015-11-22 15:03:18 +01:00
|
|
|
xcb_randr_get_screen_resources_reply_t *sres = xcb_randr_get_screen_resources_reply(dpy, xcb_randr_get_screen_resources(dpy, root), NULL);
|
2015-12-22 19:25:45 +01:00
|
|
|
if (sres == NULL) {
|
2014-01-18 16:30:00 +01:00
|
|
|
return false;
|
2015-12-22 19:25:45 +01:00
|
|
|
}
|
2014-01-18 16:30:00 +01:00
|
|
|
|
|
|
|
monitor_t *m, *mm = NULL;
|
|
|
|
|
2015-11-22 15:03:18 +01:00
|
|
|
int len = xcb_randr_get_screen_resources_outputs_length(sres);
|
|
|
|
xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_outputs(sres);
|
2014-01-18 16:30:00 +01:00
|
|
|
|
|
|
|
xcb_randr_get_output_info_cookie_t cookies[len];
|
2015-12-22 19:25:45 +01:00
|
|
|
for (int i = 0; i < len; i++) {
|
2014-01-18 16:30:00 +01:00
|
|
|
cookies[i] = xcb_randr_get_output_info(dpy, outputs[i], XCB_CURRENT_TIME);
|
2015-12-22 19:25:45 +01:00
|
|
|
}
|
2014-01-18 16:30:00 +01:00
|
|
|
|
2015-12-22 19:25:45 +01:00
|
|
|
for (m = mon_head; m != NULL; m = m->next) {
|
2014-01-18 16:30:00 +01:00
|
|
|
m->wired = false;
|
2015-12-22 19:25:45 +01:00
|
|
|
}
|
2014-01-18 16:30:00 +01:00
|
|
|
|
|
|
|
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) {
|
|
|
|
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};
|
2016-03-16 15:15:27 +01:00
|
|
|
mm = get_monitor_by_randr_id(outputs[i]);
|
2014-01-18 16:30:00 +01:00
|
|
|
if (mm != NULL) {
|
2015-11-22 14:41:00 +01:00
|
|
|
update_root(mm, &rect);
|
2014-01-18 16:30:00 +01:00
|
|
|
mm->wired = true;
|
|
|
|
} else {
|
2016-03-16 15:15:27 +01:00
|
|
|
mm = make_monitor(&rect, XCB_NONE);
|
2014-01-18 16:30:00 +01:00
|
|
|
char *name = (char *)xcb_randr_get_output_info_name(info);
|
2016-03-21 14:28:04 +01:00
|
|
|
int len = xcb_randr_get_output_info_name_length(info);
|
|
|
|
size_t name_size = MIN(sizeof(mm->name), (size_t) len + 1);
|
|
|
|
snprintf(mm->name, name_size, "%s", name);
|
2016-03-16 15:15:27 +01:00
|
|
|
mm->randr_id = outputs[i];
|
2015-11-09 15:00:47 +01:00
|
|
|
add_monitor(mm);
|
2014-01-18 16:30:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
free(cir);
|
2014-05-05 11:58:12 +02:00
|
|
|
} else if (!remove_disabled_monitors && info->connection != XCB_RANDR_CONNECTION_DISCONNECTED) {
|
2016-03-16 15:15:27 +01:00
|
|
|
m = get_monitor_by_randr_id(outputs[i]);
|
2015-12-22 19:25:45 +01:00
|
|
|
if (m != NULL) {
|
2014-01-18 16:30:00 +01:00
|
|
|
m->wired = true;
|
2015-12-22 19:25:45 +01:00
|
|
|
}
|
2014-01-18 16:30:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
free(info);
|
|
|
|
}
|
|
|
|
|
|
|
|
xcb_randr_get_output_primary_reply_t *gpo = xcb_randr_get_output_primary_reply(dpy, xcb_randr_get_output_primary(dpy, root), NULL);
|
|
|
|
if (gpo != NULL) {
|
2016-03-16 15:15:27 +01:00
|
|
|
pri_mon = get_monitor_by_randr_id(gpo->output);
|
2014-01-18 16:30:00 +01:00
|
|
|
}
|
|
|
|
free(gpo);
|
|
|
|
|
|
|
|
/* handle overlapping monitors */
|
2014-05-05 11:54:49 +02:00
|
|
|
if (merge_overlapping_monitors) {
|
|
|
|
m = mon_head;
|
|
|
|
while (m != NULL) {
|
|
|
|
monitor_t *next = m->next;
|
|
|
|
if (m->wired) {
|
2015-04-27 11:03:08 +02:00
|
|
|
monitor_t *mb = mon_head;
|
|
|
|
while (mb != NULL) {
|
|
|
|
monitor_t *mb_next = mb->next;
|
|
|
|
if (m != mb && mb->wired && contains(m->rectangle, mb->rectangle)) {
|
|
|
|
if (mm == mb) {
|
2014-05-05 20:48:45 +02:00
|
|
|
mm = m;
|
|
|
|
}
|
2015-04-27 11:03:08 +02:00
|
|
|
if (next == mb) {
|
|
|
|
next = mb_next;
|
|
|
|
}
|
|
|
|
merge_monitors(mb, m);
|
|
|
|
remove_monitor(mb);
|
2014-05-05 11:54:49 +02:00
|
|
|
}
|
2015-04-27 11:03:08 +02:00
|
|
|
mb = mb_next;
|
|
|
|
}
|
2014-05-05 11:54:49 +02:00
|
|
|
}
|
|
|
|
m = next;
|
2014-01-18 16:30:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* merge and remove disconnected monitors */
|
2014-05-05 11:46:55 +02:00
|
|
|
if (remove_unplugged_monitors) {
|
2014-04-29 11:50:09 +02:00
|
|
|
m = mon_head;
|
|
|
|
while (m != NULL) {
|
|
|
|
monitor_t *next = m->next;
|
|
|
|
if (!m->wired) {
|
|
|
|
merge_monitors(m, mm);
|
|
|
|
remove_monitor(m);
|
|
|
|
}
|
|
|
|
m = next;
|
2014-01-18 16:30:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add one desktop to each new monitor */
|
2015-12-22 19:25:45 +01:00
|
|
|
for (m = mon_head; m != NULL; m = m->next) {
|
2016-02-26 15:28:42 +01:00
|
|
|
if (m->desk == NULL) {
|
2016-03-16 15:15:27 +01:00
|
|
|
add_desktop(m, make_desktop(NULL, XCB_NONE));
|
2015-12-22 19:25:45 +01:00
|
|
|
}
|
|
|
|
}
|
2014-01-18 16:30:00 +01:00
|
|
|
|
2016-02-26 15:28:42 +01:00
|
|
|
if (!running && mon != NULL) {
|
|
|
|
if (pri_mon != NULL) {
|
|
|
|
mon = pri_mon;
|
|
|
|
}
|
|
|
|
center_pointer(mon->rectangle);
|
|
|
|
ewmh_update_current_desktop();
|
|
|
|
}
|
|
|
|
|
2014-01-18 16:30:00 +01:00
|
|
|
free(sres);
|
2015-06-12 22:05:22 +02:00
|
|
|
update_motion_recorder();
|
2016-02-26 15:28:42 +01:00
|
|
|
|
|
|
|
return (mon != NULL);
|
2013-09-19 15:02:49 +02:00
|
|
|
}
|