debug: CFLAGS += -O0 -g
debug: bspwm bspc
+VPATH=src
+
include Sourcedeps
$(WM_OBJ) $(CLI_OBJ): Makefile
bspc.o: bspc.c common.h helpers.h
-bspwm.o: bspwm.c bspwm.h common.h desktop.h events.h ewmh.h helpers.h history.h messages.h monitor.h rule.h settings.h subscribe.h types.h window.h
+bspwm.o: bspwm.c bspwm.h common.h desktop.h events.h ewmh.h helpers.h history.h messages.h monitor.h pointer.h rule.h settings.h subscribe.h types.h window.h
desktop.o: desktop.c bspwm.h desktop.h ewmh.h helpers.h history.h monitor.h query.h settings.h subscribe.h tree.h types.h window.h
-events.o: events.c bspwm.h events.h ewmh.h helpers.h monitor.h query.h settings.h subscribe.h tree.h types.h window.h
+events.o: events.c bspwm.h events.h ewmh.h helpers.h monitor.h pointer.h query.h settings.h subscribe.h tree.h types.h window.h
ewmh.o: ewmh.c bspwm.h ewmh.h helpers.h settings.h tree.h types.h
+geometry.o: geometry.c geometry.h helpers.h types.h
helpers.o: helpers.c bspwm.h helpers.h types.h
history.o: history.c bspwm.h helpers.h query.h tree.h types.h
jsmn.o: jsmn.c jsmn.h
messages.o: messages.c bspwm.h common.h desktop.h helpers.h jsmn.h messages.h monitor.h parse.h pointer.h query.h restore.h rule.h settings.h subscribe.h tree.h types.h window.h
-monitor.o: monitor.c bspwm.h desktop.h ewmh.h helpers.h history.h monitor.h query.h settings.h subscribe.h tree.h types.h window.h
+monitor.o: monitor.c bspwm.h desktop.h ewmh.h geometry.h helpers.h monitor.h pointer.h query.h settings.h subscribe.h tree.h types.h window.h
parse.o: parse.c helpers.h parse.h subscribe.h types.h
-pointer.o: pointer.c bspwm.h helpers.h monitor.h query.h settings.h stack.h subscribe.h tree.h types.h window.h
-query.o: query.c bspwm.h desktop.h helpers.h history.h monitor.h parse.h query.h subscribe.h tree.h types.h
-restore.o: restore.c bspwm.h desktop.h ewmh.h helpers.h history.h jsmn.h monitor.h parse.h query.h restore.h settings.h stack.h subscribe.h tree.h types.h
+pointer.o: pointer.c bspwm.h events.h helpers.h monitor.h pointer.h query.h settings.h stack.h subscribe.h tree.h types.h window.h
+query.o: query.c bspwm.h desktop.h helpers.h history.h monitor.h parse.h query.h subscribe.h tree.h types.h window.h
+restore.o: restore.c bspwm.h desktop.h ewmh.h helpers.h history.h jsmn.h monitor.h parse.h pointer.h query.h restore.h settings.h stack.h subscribe.h tree.h types.h window.h
rule.o: rule.c bspwm.h ewmh.h helpers.h parse.h rule.h settings.h subscribe.h types.h window.h
settings.o: settings.c bspwm.h helpers.h settings.h types.h
stack.o: stack.c bspwm.h ewmh.h helpers.h stack.h subscribe.h tree.h types.h window.h
-subscribe.o: subscribe.c bspwm.h helpers.h settings.h subscribe.h tree.h types.h
-tree.o: tree.c bspwm.h desktop.h ewmh.h helpers.h history.h monitor.h query.h settings.h stack.h subscribe.h tree.h types.h window.h
-window.o: window.c bspwm.h ewmh.h helpers.h monitor.h parse.h query.h rule.h settings.h stack.h subscribe.h tree.h types.h window.h
+subscribe.o: subscribe.c bspwm.h desktop.h helpers.h settings.h subscribe.h types.h
+tree.o: tree.c bspwm.h desktop.h ewmh.h geometry.h helpers.h history.h monitor.h pointer.h query.h settings.h stack.h subscribe.h tree.h types.h window.h
+window.o: window.c bspwm.h ewmh.h geometry.h helpers.h monitor.h parse.h pointer.h query.h rule.h settings.h stack.h subscribe.h tree.h types.h window.h
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <unistd.h>
-#include "helpers.h"
-#include "common.h"
-
-int main(int argc, char *argv[])
-{
- int fd;
- struct sockaddr_un sock_address;
- char msg[BUFSIZ], rsp[BUFSIZ];
-
- if (argc < 2) {
- err("No arguments given.\n");
- }
-
- sock_address.sun_family = AF_UNIX;
- char *sp;
-
- if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
- err("Failed to create the socket.\n");
- }
-
- sp = getenv(SOCKET_ENV_VAR);
- if (sp != NULL) {
- snprintf(sock_address.sun_path, sizeof(sock_address.sun_path), "%s", sp);
- } else {
- char *host = NULL;
- int dn = 0, sn = 0;
- if (xcb_parse_display(NULL, &host, &dn, &sn) != 0) {
- snprintf(sock_address.sun_path, sizeof(sock_address.sun_path), SOCKET_PATH_TPL, host, dn, sn);
- }
- free(host);
- }
-
- if (connect(fd, (struct sockaddr *) &sock_address, sizeof(sock_address)) == -1) {
- err("Failed to connect to the socket.\n");
- }
-
- argc--, argv++;
- int msg_len = 0;
-
- for (int offset = 0, rem = sizeof(msg), n = 0; argc > 0 && rem > 0; offset += n, rem -= n, argc--, argv++) {
- n = snprintf(msg + offset, rem, "%s%c", *argv, 0);
- msg_len += n;
- }
-
- if (send(fd, msg, msg_len, 0) == -1) {
- err("Failed to send the data.\n");
- }
-
- int ret = EXIT_SUCCESS, nb;
- while ((nb = recv(fd, rsp, sizeof(rsp)-1, 0)) > 0) {
- rsp[nb] = '\0';
- if (rsp[0] == FAILURE_MESSAGE[0]) {
- ret = EXIT_FAILURE;
- printf("%s", rsp + 1);
- } else {
- printf("%s", rsp);
- }
- fflush(stdout);
- }
-
- close(fd);
- return ret;
-}
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/time.h>
-#include <sys/wait.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <signal.h>
-#include <unistd.h>
-#include <stdbool.h>
-#include <xcb/xinerama.h>
-#include "types.h"
-#include "desktop.h"
-#include "monitor.h"
-#include "settings.h"
-#include "messages.h"
-#include "pointer.h"
-#include "events.h"
-#include "common.h"
-#include "window.h"
-#include "history.h"
-#include "ewmh.h"
-#include "rule.h"
-#include "bspwm.h"
-
-int main(int argc, char *argv[])
-{
- fd_set descriptors;
- char socket_path[MAXLEN];
- config_path[0] = '\0';
- int sock_fd, cli_fd, dpy_fd, max_fd, n;
- struct sockaddr_un sock_address;
- char msg[BUFSIZ] = {0};
- xcb_generic_event_t *event;
- int opt;
-
- while ((opt = getopt(argc, argv, "hvc:")) != -1) {
- switch (opt) {
- case 'h':
- printf(WM_NAME " [-h|-v|-c CONFIG_PATH]\n");
- exit(EXIT_SUCCESS);
- break;
- case 'v':
- printf("%s\n", VERSION);
- exit(EXIT_SUCCESS);
- break;
- case 'c':
- snprintf(config_path, sizeof(config_path), "%s", optarg);
- break;
- }
- }
-
- if (config_path[0] == '\0') {
- char *config_home = getenv(CONFIG_HOME_ENV);
- if (config_home != NULL) {
- snprintf(config_path, sizeof(config_path), "%s/%s/%s", config_home, WM_NAME, CONFIG_NAME);
- } else {
- snprintf(config_path, sizeof(config_path), "%s/%s/%s/%s", getenv("HOME"), ".config", WM_NAME, CONFIG_NAME);
- }
- }
-
- dpy = xcb_connect(NULL, &default_screen);
-
- if (!check_connection(dpy)) {
- exit(EXIT_FAILURE);
- }
-
- load_settings();
- setup();
-
- dpy_fd = xcb_get_file_descriptor(dpy);
-
- char *sp = getenv(SOCKET_ENV_VAR);
- if (sp != NULL) {
- snprintf(socket_path, sizeof(socket_path), "%s", sp);
- } else {
- char *host = NULL;
- int dn = 0, sn = 0;
- if (xcb_parse_display(NULL, &host, &dn, &sn) != 0) {
- snprintf(socket_path, sizeof(socket_path), SOCKET_PATH_TPL, host, dn, sn);
- }
- free(host);
- }
-
- sock_address.sun_family = AF_UNIX;
- snprintf(sock_address.sun_path, sizeof(sock_address.sun_path), "%s", socket_path);
-
- sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
-
- if (sock_fd == -1) {
- err("Couldn't create the socket.\n");
- }
-
- unlink(socket_path);
- if (bind(sock_fd, (struct sockaddr *) &sock_address, sizeof(sock_address)) == -1) {
- err("Couldn't bind a name to the socket.\n");
- }
-
- if (listen(sock_fd, SOMAXCONN) == -1) {
- err("Couldn't listen to the socket.\n");
- }
-
- signal(SIGINT, sig_handler);
- signal(SIGHUP, sig_handler);
- signal(SIGTERM, sig_handler);
- signal(SIGCHLD, sig_handler);
- signal(SIGPIPE, SIG_IGN);
- run_config();
- running = true;
-
- while (running) {
-
- xcb_flush(dpy);
-
- FD_ZERO(&descriptors);
- FD_SET(sock_fd, &descriptors);
- FD_SET(dpy_fd, &descriptors);
- max_fd = MAX(sock_fd, dpy_fd);
-
- for (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {
- FD_SET(pr->fd, &descriptors);
- if (pr->fd > max_fd) {
- max_fd = pr->fd;
- }
- }
-
- if (select(max_fd + 1, &descriptors, NULL, NULL, NULL) > 0) {
-
- pending_rule_t *pr = pending_rule_head;
- while (pr != NULL) {
- pending_rule_t *next = pr->next;
- if (FD_ISSET(pr->fd, &descriptors)) {
- manage_window(pr->win, pr->csq, pr->fd);
- remove_pending_rule(pr);
- }
- pr = next;
- }
-
- if (FD_ISSET(sock_fd, &descriptors)) {
- cli_fd = accept(sock_fd, NULL, 0);
- if (cli_fd > 0 && (n = recv(cli_fd, msg, sizeof(msg), 0)) > 0) {
- msg[n] = '\0';
- FILE *rsp = fdopen(cli_fd, "w");
- if (rsp != NULL) {
- handle_message(msg, n, rsp);
- } else {
- warn("Can't open the client socket as file.\n");
- close(cli_fd);
- }
- }
- }
-
- if (FD_ISSET(dpy_fd, &descriptors)) {
- while ((event = xcb_poll_for_event(dpy)) != NULL) {
- handle_event(event);
- free(event);
- }
- }
-
- }
-
- if (!check_connection(dpy)) {
- running = false;
- }
- }
-
- cleanup();
- close(sock_fd);
- unlink(socket_path);
- ungrab_buttons();
- xcb_ewmh_connection_wipe(ewmh);
- xcb_destroy_window(dpy, meta_window);
- free(ewmh);
- xcb_flush(dpy);
- xcb_disconnect(dpy);
- return exit_status;
-}
-
-void init(void)
-{
- clients_count = 0;
- mon = mon_head = mon_tail = pri_mon = NULL;
- history_head = history_tail = history_needle = NULL;
- rule_head = rule_tail = NULL;
- stack_head = stack_tail = NULL;
- subscribe_head = subscribe_tail = NULL;
- pending_rule_head = pending_rule_tail = NULL;
- auto_raise = sticky_still = record_history = true;
- randr_base = 0;
- exit_status = 0;
-}
-
-void setup(void)
-{
- init();
- ewmh_init();
- pointer_init();
-
- screen = xcb_setup_roots_iterator(xcb_get_setup(dpy)).data;
-
- if (screen == NULL) {
- err("Can't acquire the default screen.\n");
- }
-
- root = screen->root;
- register_events();
-
- screen_width = screen->width_in_pixels;
- screen_height = screen->height_in_pixels;
-
- meta_window = xcb_generate_id(dpy);
- xcb_create_window(dpy, XCB_COPY_FROM_PARENT, meta_window, root, -1, -1, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY, XCB_COPY_FROM_PARENT, XCB_NONE, NULL);
- xcb_icccm_set_wm_class(dpy, meta_window, sizeof(META_WINDOW_IC), META_WINDOW_IC);
-
- xcb_atom_t net_atoms[] = {ewmh->_NET_SUPPORTED,
- ewmh->_NET_SUPPORTING_WM_CHECK,
- ewmh->_NET_DESKTOP_NAMES,
- ewmh->_NET_DESKTOP_VIEWPORT,
- ewmh->_NET_NUMBER_OF_DESKTOPS,
- ewmh->_NET_CURRENT_DESKTOP,
- ewmh->_NET_CLIENT_LIST,
- ewmh->_NET_ACTIVE_WINDOW,
- ewmh->_NET_CLOSE_WINDOW,
- ewmh->_NET_WM_STRUT_PARTIAL,
- ewmh->_NET_WM_DESKTOP,
- ewmh->_NET_WM_STATE,
- ewmh->_NET_WM_STATE_HIDDEN,
- ewmh->_NET_WM_STATE_FULLSCREEN,
- ewmh->_NET_WM_STATE_BELOW,
- ewmh->_NET_WM_STATE_ABOVE,
- ewmh->_NET_WM_STATE_STICKY,
- ewmh->_NET_WM_STATE_DEMANDS_ATTENTION,
- ewmh->_NET_WM_WINDOW_TYPE,
- ewmh->_NET_WM_WINDOW_TYPE_DOCK,
- ewmh->_NET_WM_WINDOW_TYPE_DESKTOP,
- ewmh->_NET_WM_WINDOW_TYPE_NOTIFICATION,
- ewmh->_NET_WM_WINDOW_TYPE_DIALOG,
- ewmh->_NET_WM_WINDOW_TYPE_UTILITY,
- ewmh->_NET_WM_WINDOW_TYPE_TOOLBAR};
-
- xcb_ewmh_set_supported(ewmh, default_screen, LENGTH(net_atoms), net_atoms);
- ewmh_set_supporting(meta_window);
-
-#define GETATOM(a) \
- get_atom(#a, &a);
- GETATOM(WM_STATE)
- GETATOM(WM_DELETE_WINDOW)
- GETATOM(WM_TAKE_FOCUS)
-#undef GETATOM
-
- const xcb_query_extension_reply_t *qep = xcb_get_extension_data(dpy, &xcb_randr_id);
- if (qep->present && update_monitors()) {
- randr = true;
- randr_base = qep->first_event;
- xcb_randr_select_input(dpy, root, XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE);
- } else {
- randr = false;
- warn("Couldn't retrieve monitors via RandR.\n");
- bool xinerama_is_active = false;
-
- if (xcb_get_extension_data(dpy, &xcb_xinerama_id)->present) {
- xcb_xinerama_is_active_reply_t *xia = xcb_xinerama_is_active_reply(dpy, xcb_xinerama_is_active(dpy), NULL);
- if (xia != NULL) {
- xinerama_is_active = xia->state;
- free(xia);
- }
- }
-
- if (xinerama_is_active) {
- xcb_xinerama_query_screens_reply_t *xsq = xcb_xinerama_query_screens_reply(dpy, xcb_xinerama_query_screens(dpy), NULL);
- xcb_xinerama_screen_info_t *xsi = xcb_xinerama_query_screens_screen_info(xsq);
- int n = xcb_xinerama_query_screens_screen_info_length(xsq);
- for (int i = 0; i < n; i++) {
- xcb_xinerama_screen_info_t info = xsi[i];
- xcb_rectangle_t rect = (xcb_rectangle_t) {info.x_org, info.y_org, info.width, info.height};
- monitor_t *m = make_monitor(NULL, &rect, XCB_NONE);
- add_monitor(m);
- add_desktop(m, make_desktop(NULL, XCB_NONE));
- }
- free(xsq);
- } else {
- warn("Xinerama is inactive.\n");
- xcb_rectangle_t rect = (xcb_rectangle_t) {0, 0, screen_width, screen_height};
- monitor_t *m = make_monitor(NULL, &rect, XCB_NONE);
- add_monitor(m);
- add_desktop(m, make_desktop(NULL, XCB_NONE));
- }
- }
-
- ewmh_update_number_of_desktops();
- ewmh_update_desktop_names();
- ewmh_update_desktop_viewport();
- ewmh_update_current_desktop();
- xcb_get_input_focus_reply_t *ifo = xcb_get_input_focus_reply(dpy, xcb_get_input_focus(dpy), NULL);
- if (ifo != NULL && (ifo->focus == XCB_INPUT_FOCUS_POINTER_ROOT || ifo->focus == XCB_NONE)) {
- clear_input_focus();
- }
- free(ifo);
-}
-
-void register_events(void)
-{
- uint32_t values[] = {ROOT_EVENT_MASK};
- xcb_generic_error_t *e = xcb_request_check(dpy, xcb_change_window_attributes_checked(dpy, root, XCB_CW_EVENT_MASK, values));
- if (e != NULL) {
- xcb_disconnect(dpy);
- err("Another window manager is already running.\n");
- }
-}
-
-void cleanup(void)
-{
- mon = NULL;
-
- while (mon_head != NULL) {
- remove_monitor(mon_head);
- }
- while (rule_head != NULL) {
- remove_rule(rule_head);
- }
- while (subscribe_head != NULL) {
- remove_subscriber(subscribe_head);
- }
- while (pending_rule_head != NULL) {
- remove_pending_rule(pending_rule_head);
- }
-
- empty_history();
-}
-
-bool check_connection (xcb_connection_t *dpy)
-{
- int xerr;
- if ((xerr = xcb_connection_has_error(dpy)) != 0) {
- warn("The server closed the connection: ");
- switch (xerr) {
- case XCB_CONN_ERROR:
- warn("socket, pipe or stream error.\n");
- break;
- case XCB_CONN_CLOSED_EXT_NOTSUPPORTED:
- warn("unsupported extension.\n");
- break;
- case XCB_CONN_CLOSED_MEM_INSUFFICIENT:
- warn("not enough memory.\n");
- break;
- case XCB_CONN_CLOSED_REQ_LEN_EXCEED:
- warn("request length exceeded.\n");
- break;
- case XCB_CONN_CLOSED_PARSE_ERR:
- warn("can't parse display string.\n");
- break;
- case XCB_CONN_CLOSED_INVALID_SCREEN:
- warn("invalid screen.\n");
- break;
- case XCB_CONN_CLOSED_FDPASSING_FAILED:
- warn("failed to pass FD.\n");
- break;
- default:
- warn("unknown error.\n");
- break;
- }
- return false;
- } else {
- return true;
- }
-}
-
-void sig_handler(int sig)
-{
- if (sig == SIGCHLD) {
- signal(sig, sig_handler);
- while (waitpid(-1, 0, WNOHANG) > 0)
- ;
- } else if (sig == SIGINT || sig == SIGHUP || sig == SIGTERM) {
- running = false;
- }
-}
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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.
- */
-
-#ifndef BSPWM_BSPWM_H
-#define BSPWM_BSPWM_H
-
-#include "types.h"
-
-#define ROOT_EVENT_MASK (XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_BUTTON_PRESS)
-#define CLIENT_EVENT_MASK (XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_FOCUS_CHANGE)
-#define BSPWM_CLASS_NAME "Bspwm"
-#define META_WINDOW_IC "wm\0" BSPWM_CLASS_NAME
-#define ROOT_WINDOW_IC "root\0" BSPWM_CLASS_NAME
-#define PRESEL_FEEDBACK_I "presel_feedback"
-#define PRESEL_FEEDBACK_IC PRESEL_FEEDBACK_I "\0" BSPWM_CLASS_NAME
-
-xcb_connection_t *dpy;
-int default_screen, screen_width, screen_height;
-uint32_t clients_count;
-xcb_screen_t *screen;
-xcb_window_t root;
-char config_path[MAXLEN];
-
-monitor_t *mon;
-monitor_t *mon_head;
-monitor_t *mon_tail;
-monitor_t *pri_mon;
-history_t *history_head;
-history_t *history_tail;
-history_t *history_needle;
-rule_t *rule_head;
-rule_t *rule_tail;
-stacking_list_t *stack_head;
-stacking_list_t *stack_tail;
-subscriber_list_t *subscribe_head;
-subscriber_list_t *subscribe_tail;
-pending_rule_t *pending_rule_head;
-pending_rule_t *pending_rule_tail;
-
-xcb_window_t meta_window;
-xcb_atom_t WM_STATE;
-xcb_atom_t WM_TAKE_FOCUS;
-xcb_atom_t WM_DELETE_WINDOW;
-int exit_status;
-
-bool auto_raise;
-bool sticky_still;
-bool record_history;
-bool running;
-bool randr;
-
-void init(void);
-void setup(void);
-void register_events(void);
-void cleanup(void);
-bool check_connection (xcb_connection_t *dpy);
-void sig_handler(int sig);
-
-#endif
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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.
- */
-
-#ifndef BSPWM_COMMON_H
-#define BSPWM_COMMON_H
-
-#define SOCKET_PATH_TPL "/tmp/bspwm%s_%i_%i-socket"
-#define SOCKET_ENV_VAR "BSPWM_SOCKET"
-
-#define FAILURE_MESSAGE "\x07"
-
-#endif
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include "bspwm.h"
-#include "ewmh.h"
-#include "history.h"
-#include "monitor.h"
-#include "query.h"
-#include "tree.h"
-#include "window.h"
-#include "desktop.h"
-#include "subscribe.h"
-#include "settings.h"
-
-void focus_desktop(monitor_t *m, desktop_t *d)
-{
- bool changed = (m != mon || m->desk != d);
-
- focus_monitor(m);
-
- if (m->desk != d) {
- if (focus_follows_pointer) {
- listen_enter_notify(d->root, false);
- }
- show_desktop(d);
- hide_desktop(m->desk);
- if (focus_follows_pointer) {
- listen_enter_notify(d->root, true);
- }
- m->desk = d;
- }
-
- if (changed) {
- ewmh_update_current_desktop();
- put_status(SBSC_MASK_DESKTOP_FOCUS, "desktop_focus 0x%08X 0x%08X\n", m->id, d->id);
- }
-}
-
-bool activate_desktop(monitor_t *m, desktop_t *d)
-{
- if (m == mon || d == m->desk) {
- return false;
- }
-
- if (m->sticky_count > 0) {
- sticky_still = false;
- transfer_sticky_nodes(m, m->desk, d, m->desk->root);
- sticky_still = true;
- }
-
- show_desktop(d);
- hide_desktop(m->desk);
-
- m->desk = d;
-
- put_status(SBSC_MASK_DESKTOP_ACTIVATE, "desktop_activate 0x%08X 0x%08X\n", m->id, d->id);
- put_status(SBSC_MASK_REPORT);
-
- return true;
-}
-
-bool find_closest_desktop(coordinates_t *ref, coordinates_t *dst, cycle_dir_t dir, desktop_select_t sel)
-{
- monitor_t *m = ref->monitor;
- desktop_t *d = ref->desktop;
- desktop_t *f = (dir == CYCLE_PREV ? d->prev : d->next);
-
-#define HANDLE_BOUNDARIES(f) \
- if (f == NULL) { \
- m = (dir == CYCLE_PREV ? m->prev : m->next); \
- if (m == NULL) { \
- m = (dir == CYCLE_PREV ? mon_tail : mon_head); \
- } \
- f = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head); \
- }
- HANDLE_BOUNDARIES(f)
-
- while (f != d) {
- coordinates_t loc = {m, f, NULL};
- if (desktop_matches(&loc, ref, sel)) {
- *dst = loc;
- return true;
- }
- f = (dir == CYCLE_PREV ? f->prev : f->next);
- HANDLE_BOUNDARIES(f)
- }
-#undef HANDLE_BOUNDARIES
-
- return false;
-}
-
-bool set_layout(monitor_t *m, desktop_t *d, layout_t l)
-{
- if (d->layout == l) {
- return false;
- }
-
- d->layout = l;
-
- handle_presel_feedbacks(m, d);
-
- arrange(m, d);
-
- put_status(SBSC_MASK_DESKTOP_LAYOUT, "desktop_layout 0x%08X 0x%08X %s\n", m->id, d->id, LAYOUT_STR(l));
-
- if (d == m->desk) {
- put_status(SBSC_MASK_REPORT);
- }
-
- return true;
-}
-
-void handle_presel_feedbacks(monitor_t *m, desktop_t *d)
-{
- if (m->desk != d) {
- return;
- }
- if (focus_follows_pointer) {
- listen_enter_notify(d->root, false);
- }
- if (d->layout == LAYOUT_MONOCLE) {
- hide_presel_feedbacks(m, d, d->root);
- } else {
- show_presel_feedbacks(m, d, d->root);
- }
- if (focus_follows_pointer) {
- listen_enter_notify(d->root, true);
- }
-}
-
-bool transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d)
-{
- if (ms == NULL || md == NULL || d == NULL || ms == md) {
- return false;
- }
-
- bool was_active = (d == ms->desk);
-
- unlink_desktop(ms, d);
-
- if (md->desk != NULL) {
- hide_desktop(d);
- }
-
- insert_desktop(md, d);
- history_transfer_desktop(md, d);
-
- if (was_active) {
- if (mon == ms) {
- focus_node(ms, NULL, NULL);
- } else {
- activate_node(ms, NULL, NULL);
- }
- }
-
- if (ms->sticky_count > 0 && was_active) {
- sticky_still = false;
- transfer_sticky_nodes(ms, d, ms->desk, d->root);
- sticky_still = true;
- }
-
- adapt_geometry(&ms->rectangle, &md->rectangle, d->root);
- arrange(md, d);
-
- if (md->desk == d) {
- if (mon == md) {
- focus_node(md, d, d->focus);
- } else {
- activate_node(md, d, d->focus);
- }
- }
-
- ewmh_update_wm_desktops();
- ewmh_update_desktop_names();
- ewmh_update_desktop_viewport();
- ewmh_update_current_desktop();
-
- put_status(SBSC_MASK_DESKTOP_TRANSFER, "desktop_transfer 0x%08X 0x%08X 0x%08X\n", ms->id, d->id, md->id);
- put_status(SBSC_MASK_REPORT);
-
- return true;
-}
-
-desktop_t *make_desktop(const char *name, uint32_t id)
-{
- desktop_t *d = calloc(1, sizeof(desktop_t));
- snprintf(d->name, sizeof(d->name), "%s", name == NULL ? DEFAULT_DESK_NAME : name);
- if (id == XCB_NONE) {
- d->id = xcb_generate_id(dpy);
- }
- d->prev = d->next = NULL;
- d->root = d->focus = NULL;
- d->layout = LAYOUT_TILED;
- d->padding = (padding_t) PADDING;
- d->window_gap = window_gap;
- d->border_width = border_width;
- return d;
-}
-
-void rename_desktop(monitor_t *m, desktop_t *d, const char *name)
-{
-
- put_status(SBSC_MASK_DESKTOP_RENAME, "desktop_rename 0x%08X 0x%08X %s %s\n", m->id, d->id, d->name, name);
-
- snprintf(d->name, sizeof(d->name), "%s", name);
-
- put_status(SBSC_MASK_REPORT);
- ewmh_update_desktop_names();
-}
-
-void insert_desktop(monitor_t *m, desktop_t *d)
-{
- if (m->desk == NULL) {
- m->desk = d;
- m->desk_head = d;
- m->desk_tail = d;
- } else {
- m->desk_tail->next = d;
- d->prev = m->desk_tail;
- m->desk_tail = d;
- }
-}
-
-void add_desktop(monitor_t *m, desktop_t *d)
-{
- put_status(SBSC_MASK_DESKTOP_ADD, "desktop_add 0x%08X %s 0x%08X\n", d->id, d->name, m->id);
-
- d->border_width = m->border_width;
- d->window_gap = m->window_gap;
- insert_desktop(m, d);
- ewmh_update_number_of_desktops();
- ewmh_update_desktop_names();
- ewmh_update_desktop_viewport();
- ewmh_update_wm_desktops();
- put_status(SBSC_MASK_REPORT);
-}
-
-desktop_t *find_desktop_in(uint32_t id, monitor_t *m)
-{
- if (m == NULL) {
- return NULL;
- }
-
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
- if (d->id == id) {
- return d;
- }
- }
-
- return NULL;
-}
-
-void unlink_desktop(monitor_t *m, desktop_t *d)
-{
- desktop_t *prev = d->prev;
- desktop_t *next = d->next;
-
- if (prev != NULL) {
- prev->next = next;
- }
-
- if (next != NULL) {
- next->prev = prev;
- }
-
- if (m->desk_head == d) {
- m->desk_head = next;
- }
-
- if (m->desk_tail == d) {
- m->desk_tail = prev;
- }
-
- if (m->desk == d) {
- m->desk = NULL;
- }
-
- d->prev = d->next = NULL;
-}
-
-void remove_desktop(monitor_t *m, desktop_t *d)
-{
- put_status(SBSC_MASK_DESKTOP_REMOVE, "desktop_remove 0x%08X 0x%08X\n", m->id, d->id);
-
- remove_node(m, d, d->root);
- unlink_desktop(m, d);
- history_remove(d, NULL, false);
- free(d);
-
- ewmh_update_current_desktop();
- ewmh_update_number_of_desktops();
- ewmh_update_desktop_names();
- ewmh_update_desktop_viewport();
-
- if (mon != NULL && m->desk == NULL) {
- if (m == mon) {
- focus_node(m, NULL, NULL);
- } else {
- activate_node(m, NULL, NULL);
- }
- }
-
- put_status(SBSC_MASK_REPORT);
-}
-
-void merge_desktops(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd)
-{
- if (ds == NULL || dd == NULL || ds == dd) {
- return;
- }
- transfer_node(ms, ds, ds->root, md, dd, dd->focus);
-}
-
-bool swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2)
-{
- if (d1 == NULL || d2 == NULL || d1 == d2 ||
- (m1->desk == d1 && m1->sticky_count > 0) ||
- (m2->desk == d2 && m2->sticky_count > 0)) {
- return false;
- }
-
- put_status(SBSC_MASK_DESKTOP_SWAP, "desktop_swap 0x%08X 0x%08X 0x%08X 0x%08X\n", m1->id, d1->id, m2->id, d2->id);
-
- bool d1_focused = (m1->desk == d1);
- bool d2_focused = (m2->desk == d2);
-
- if (m1 != m2) {
- if (m1->desk == d1) {
- m1->desk = d2;
- }
- if (m1->desk_head == d1) {
- m1->desk_head = d2;
- }
- if (m1->desk_tail == d1) {
- m1->desk_tail = d2;
- }
- if (m2->desk == d2) {
- m2->desk = d1;
- }
- if (m2->desk_head == d2) {
- m2->desk_head = d1;
- }
- if (m2->desk_tail == d2) {
- m2->desk_tail = d1;
- }
- } else {
- if (m1->desk == d1) {
- m1->desk = d2;
- } else if (m1->desk == d2) {
- m1->desk = d1;
- }
- if (m1->desk_head == d1) {
- m1->desk_head = d2;
- } else if (m1->desk_head == d2) {
- m1->desk_head = d1;
- }
- if (m1->desk_tail == d1) {
- m1->desk_tail = d2;
- } else if (m1->desk_tail == d2) {
- m1->desk_tail = d1;
- }
- }
-
- desktop_t *p1 = d1->prev;
- desktop_t *n1 = d1->next;
- desktop_t *p2 = d2->prev;
- desktop_t *n2 = d2->next;
-
- if (p1 != NULL && p1 != d2) {
- p1->next = d2;
- }
- if (n1 != NULL && n1 != d2) {
- n1->prev = d2;
- }
- if (p2 != NULL && p2 != d1) {
- p2->next = d1;
- }
- if (n2 != NULL && n2 != d1) {
- n2->prev = d1;
- }
-
- d1->prev = p2 == d1 ? d2 : p2;
- d1->next = n2 == d1 ? d2 : n2;
- d2->prev = p1 == d2 ? d1 : p1;
- d2->next = n1 == d2 ? d1 : n1;
-
- if (m1 != m2) {
- adapt_geometry(&m1->rectangle, &m2->rectangle, d1->root);
- adapt_geometry(&m2->rectangle, &m1->rectangle, d2->root);
- history_swap_desktops(m1, d1, m2, d2);
- arrange(m1, d2);
- arrange(m2, d1);
- }
-
- if (d1_focused && !d2_focused) {
- hide_desktop(d1);
- show_desktop(d2);
- } else if (!d1_focused && d2_focused) {
- show_desktop(d1);
- hide_desktop(d2);
- }
-
- if (d1 == mon->desk) {
- focus_node(m2, d1, d1->focus);
- } else if (d1 == m2->desk) {
- activate_node(m2, d1, d1->focus);
- }
-
- if (d2 == mon->desk) {
- focus_node(m1, d2, d2->focus);
- } else if (d2 == m1->desk) {
- activate_node(m1, d2, d2->focus);
- }
-
- ewmh_update_wm_desktops();
- ewmh_update_desktop_names();
- ewmh_update_desktop_viewport();
- ewmh_update_current_desktop();
-
- put_status(SBSC_MASK_REPORT);
-
- return true;
-}
-
-void show_desktop(desktop_t *d)
-{
- if (d == NULL) {
- return;
- }
- show_node(d, d->root);
-}
-
-void hide_desktop(desktop_t *d)
-{
- if (d == NULL) {
- return;
- }
- hide_node(d, d->root);
-}
-
-bool is_urgent(desktop_t *d)
-{
- for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
- if (n->client == NULL) {
- continue;
- }
- if (n->client->urgent) {
- return true;
- }
- }
- return false;
-}
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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.
- */
-
-#ifndef BSPWM_DESKTOP_H
-#define BSPWM_DESKTOP_H
-
-#define DEFAULT_DESK_NAME "Desktop"
-
-void focus_desktop(monitor_t *m, desktop_t *d);
-bool activate_desktop(monitor_t *m, desktop_t *d);
-bool find_closest_desktop(coordinates_t *ref, coordinates_t *dst, cycle_dir_t dir, desktop_select_t sel);
-bool set_layout(monitor_t *m, desktop_t *d, layout_t l);
-void handle_presel_feedbacks(monitor_t *m, desktop_t *d);
-bool transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d);
-desktop_t *make_desktop(const char *name, uint32_t id);
-void rename_desktop(monitor_t *m, desktop_t *d, const char *name);
-void insert_desktop(monitor_t *m, desktop_t *d);
-void add_desktop(monitor_t *m, desktop_t *d);
-desktop_t *find_desktop_in(uint32_t id, monitor_t *m);
-void unlink_desktop(monitor_t *m, desktop_t *d);
-void remove_desktop(monitor_t *m, desktop_t *d);
-void merge_desktops(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd);
-bool swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2);
-void show_desktop(desktop_t *d);
-void hide_desktop(desktop_t *d);
-bool is_urgent(desktop_t *d);
-
-#endif
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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 <stdbool.h>
-#include "bspwm.h"
-#include "ewmh.h"
-#include "monitor.h"
-#include "query.h"
-#include "settings.h"
-#include "subscribe.h"
-#include "tree.h"
-#include "window.h"
-#include "pointer.h"
-#include "events.h"
-
-void handle_event(xcb_generic_event_t *evt)
-{
- uint8_t resp_type = XCB_EVENT_RESPONSE_TYPE(evt);
- switch (resp_type) {
- case XCB_MAP_REQUEST:
- map_request(evt);
- break;
- case XCB_DESTROY_NOTIFY:
- destroy_notify(evt);
- break;
- case XCB_UNMAP_NOTIFY:
- unmap_notify(evt);
- break;
- case XCB_CLIENT_MESSAGE:
- client_message(evt);
- break;
- case XCB_CONFIGURE_REQUEST:
- configure_request(evt);
- break;
- case XCB_CONFIGURE_NOTIFY:
- configure_notify(evt);
- break;
- case XCB_PROPERTY_NOTIFY:
- property_notify(evt);
- break;
- case XCB_ENTER_NOTIFY:
- enter_notify(evt);
- break;
- case XCB_BUTTON_PRESS:
- button_press(evt);
- break;
- case XCB_FOCUS_IN:
- focus_in(evt);
- break;
- case 0:
- process_error(evt);
- break;
- default:
- if (randr && resp_type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY) {
- update_monitors();
- }
- break;
- }
-}
-
-void map_request(xcb_generic_event_t *evt)
-{
- xcb_map_request_event_t *e = (xcb_map_request_event_t *) evt;
-
- schedule_window(e->window);
-}
-
-void configure_request(xcb_generic_event_t *evt)
-{
- xcb_configure_request_event_t *e = (xcb_configure_request_event_t *) evt;
-
- coordinates_t loc;
- bool is_managed = locate_window(e->window, &loc);
- client_t *c = (is_managed ? loc.node->client : NULL);
- uint16_t width, height;
-
- if (!is_managed) {
- uint16_t mask = 0;
- uint32_t values[7];
- unsigned short i = 0;
-
- if (e->value_mask & XCB_CONFIG_WINDOW_X) {
- mask |= XCB_CONFIG_WINDOW_X;
- values[i++] = e->x;
- }
-
- if (e->value_mask & XCB_CONFIG_WINDOW_Y) {
- mask |= XCB_CONFIG_WINDOW_Y;
- values[i++] = e->y;
- }
-
- if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
- mask |= XCB_CONFIG_WINDOW_WIDTH;
- values[i++] = e->width;
- }
-
- if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
- mask |= XCB_CONFIG_WINDOW_HEIGHT;
- values[i++] = e->height;
- }
-
- if (e->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH) {
- mask |= XCB_CONFIG_WINDOW_BORDER_WIDTH;
- values[i++] = e->border_width;
- }
-
- if (e->value_mask & XCB_CONFIG_WINDOW_SIBLING) {
- mask |= XCB_CONFIG_WINDOW_SIBLING;
- values[i++] = e->sibling;
- }
-
- if (e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) {
- mask |= XCB_CONFIG_WINDOW_STACK_MODE;
- values[i++] = e->stack_mode;
- }
-
- xcb_configure_window(dpy, e->window, mask, values);
-
- } else if (IS_FLOATING(c)) {
- width = c->floating_rectangle.width;
- height = c->floating_rectangle.height;
-
- if (e->value_mask & XCB_CONFIG_WINDOW_X) {
- c->floating_rectangle.x = e->x;
- }
-
- if (e->value_mask & XCB_CONFIG_WINDOW_Y) {
- c->floating_rectangle.y = e->y;
- }
-
- if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
- width = e->width;
- }
-
- if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
- height = e->height;
- }
-
- apply_size_hints(c, &width, &height);
- c->floating_rectangle.width = width;
- c->floating_rectangle.height = height;
- xcb_rectangle_t r = c->floating_rectangle;
-
- if (focus_follows_pointer) {
- listen_enter_notify(loc.desktop->root, false);
- }
-
- window_move_resize(e->window, r.x, r.y, r.width, r.height);
-
- if (focus_follows_pointer) {
- listen_enter_notify(loc.desktop->root, true);
- }
-
- put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", loc.monitor->id, loc.desktop->id, e->window, r.width, r.height, r.x, r.y);
-
- monitor_t *m = monitor_from_client(c);
- if (m != loc.monitor) {
- transfer_node(loc.monitor, loc.desktop, loc.node, m, m->desk, m->desk->focus);
- }
- } else {
- if (c->state == STATE_PSEUDO_TILED) {
- width = c->floating_rectangle.width;
- height = c->floating_rectangle.height;
- if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
- width = e->width;
- }
- if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
- height = e->height;
- }
- apply_size_hints(c, &width, &height);
- if (width != c->floating_rectangle.width || height != c->floating_rectangle.height) {
- c->floating_rectangle.width = width;
- c->floating_rectangle.height = height;
- arrange(loc.monitor, loc.desktop);
- }
- }
-
-
- xcb_configure_notify_event_t evt;
- unsigned int bw = c->border_width;
-
- xcb_rectangle_t r = IS_FULLSCREEN(c) ? loc.monitor->rectangle : c->tiled_rectangle;
-
- evt.response_type = XCB_CONFIGURE_NOTIFY;
- evt.event = e->window;
- evt.window = e->window;
- evt.above_sibling = XCB_NONE;
- evt.x = r.x;
- evt.y = r.y;
- evt.width = r.width;
- evt.height = r.height;
- evt.border_width = bw;
- evt.override_redirect = false;
-
- xcb_send_event(dpy, false, e->window, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (const char *) &evt);
- }
-}
-
-void configure_notify(xcb_generic_event_t *evt)
-{
- xcb_configure_notify_event_t *e = (xcb_configure_notify_event_t *) evt;
-
- if (e->window == root) {
- screen_width = e->width;
- screen_height = e->height;
- }
-}
-
-void destroy_notify(xcb_generic_event_t *evt)
-{
- xcb_destroy_notify_event_t *e = (xcb_destroy_notify_event_t *) evt;
-
- unmanage_window(e->window);
-}
-
-void unmap_notify(xcb_generic_event_t *evt)
-{
- xcb_unmap_notify_event_t *e = (xcb_unmap_notify_event_t *) evt;
-
- unmanage_window(e->window);
-}
-
-void property_notify(xcb_generic_event_t *evt)
-{
- xcb_property_notify_event_t *e = (xcb_property_notify_event_t *) evt;
-
- if (e->atom == ewmh->_NET_WM_STRUT_PARTIAL && ewmh_handle_struts(e->window)) {
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
- arrange(m, d);
- }
- }
- }
-
- if (e->atom != XCB_ATOM_WM_HINTS && e->atom != XCB_ATOM_WM_NORMAL_HINTS) {
- return;
- }
-
- coordinates_t loc;
- if (!locate_window(e->window, &loc)) {
- return;
- }
-
- if (e->atom == XCB_ATOM_WM_HINTS) {
- xcb_icccm_wm_hints_t hints;
- if (xcb_icccm_get_wm_hints_reply(dpy, xcb_icccm_get_wm_hints(dpy, e->window), &hints, NULL) == 1 &&
- (hints.flags & XCB_ICCCM_WM_HINT_X_URGENCY))
- set_urgent(loc.monitor, loc.desktop, loc.node, xcb_icccm_wm_hints_get_urgency(&hints));
- } else if (e->atom == XCB_ATOM_WM_NORMAL_HINTS) {
- client_t *c = loc.node->client;
- if (xcb_icccm_get_wm_normal_hints_reply(dpy, xcb_icccm_get_wm_normal_hints(dpy, e->window), &c->size_hints, NULL) == 1) {
- arrange(loc.monitor, loc.desktop);
- }
- }
-}
-
-void client_message(xcb_generic_event_t *evt)
-{
- xcb_client_message_event_t *e = (xcb_client_message_event_t *) evt;
-
- if (e->type == ewmh->_NET_CURRENT_DESKTOP) {
- coordinates_t loc;
- if (ewmh_locate_desktop(e->data.data32[0], &loc)) {
- focus_node(loc.monitor, loc.desktop, loc.desktop->focus);
- }
- return;
- }
-
- coordinates_t loc;
- if (!locate_window(e->window, &loc)) {
- return;
- }
-
- if (e->type == ewmh->_NET_WM_STATE) {
- handle_state(loc.monitor, loc.desktop, loc.node, e->data.data32[1], e->data.data32[0]);
- handle_state(loc.monitor, loc.desktop, loc.node, e->data.data32[2], e->data.data32[0]);
- } else if (e->type == ewmh->_NET_ACTIVE_WINDOW) {
- if ((ignore_ewmh_focus && e->data.data32[0] == XCB_EWMH_CLIENT_SOURCE_TYPE_NORMAL) ||
- loc.node == mon->desk->focus) {
- return;
- }
- focus_node(loc.monitor, loc.desktop, loc.node);
- } else if (e->type == ewmh->_NET_WM_DESKTOP) {
- coordinates_t dloc;
- if (ewmh_locate_desktop(e->data.data32[0], &dloc)) {
- transfer_node(loc.monitor, loc.desktop, loc.node, dloc.monitor, dloc.desktop, dloc.desktop->focus);
- }
- } else if (e->type == ewmh->_NET_CLOSE_WINDOW) {
- close_node(loc.node);
- }
-}
-
-void focus_in(xcb_generic_event_t *evt)
-{
- xcb_focus_in_event_t *e = (xcb_focus_in_event_t *) evt;
-
- if (e->mode == XCB_NOTIFY_MODE_GRAB || e->mode == XCB_NOTIFY_MODE_UNGRAB
- || e->detail == XCB_NOTIFY_DETAIL_POINTER || e->detail == XCB_NOTIFY_DETAIL_POINTER_ROOT
- || e->detail == XCB_NOTIFY_DETAIL_NONE) {
- return;
- }
-
- if (mon->desk->focus != NULL && e->event == mon->desk->focus->id) {
- return;
- }
-
- coordinates_t loc;
- if (locate_window(e->event, &loc)) {
- // prevent input focus stealing
- update_input_focus();
- }
-}
-
-void button_press(xcb_generic_event_t *evt)
-{
- xcb_button_press_event_t *e = (xcb_button_press_event_t *) evt;
- bool replay = false;
- switch (e->detail) {
- case XCB_BUTTON_INDEX_1:
- if (click_to_focus && cleaned_mask(e->state) == XCB_NONE) {
- bool pff = pointer_follows_focus;
- bool pfm = pointer_follows_monitor;
- pointer_follows_focus = false;
- pointer_follows_monitor = false;
- replay = !grab_pointer(ACTION_FOCUS) || !swallow_first_click;
- pointer_follows_focus = pff;
- pointer_follows_monitor = pfm;
- } else {
- grab_pointer(pointer_actions[0]);
- }
- break;
- case XCB_BUTTON_INDEX_2:
- grab_pointer(pointer_actions[1]);
- break;
- case XCB_BUTTON_INDEX_3:
- grab_pointer(pointer_actions[2]);
- break;
- }
- xcb_allow_events(dpy, replay ? XCB_ALLOW_REPLAY_POINTER : XCB_ALLOW_SYNC_POINTER, e->time);
- xcb_flush(dpy);
-}
-
-void enter_notify(xcb_generic_event_t *evt)
-{
- xcb_enter_notify_event_t *e = (xcb_enter_notify_event_t *) evt;
- xcb_window_t win = e->event;
-
- if (e->mode != XCB_NOTIFY_MODE_NORMAL || e->detail == XCB_NOTIFY_DETAIL_INFERIOR) {
- return;
- }
-
- xcb_point_t pt = {e->root_x, e->root_y};
- monitor_t *m = monitor_from_point(pt);
-
- if (m == NULL) {
- return;
- }
-
- bool pff = pointer_follows_focus;
- bool pfm = pointer_follows_monitor;
- pointer_follows_focus = false;
- pointer_follows_monitor = false;
- auto_raise = false;
-
- desktop_t *d = m->desk;
- node_t *n = NULL;
-
- for (n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
- if (n->id == win || (n->presel != NULL && n->presel->feedback == win)) {
- break;
- }
- }
-
- if (n != mon->desk->focus || mon->desk->focus == NULL) {
- focus_node(m, d, n == NULL ? d->focus : n);
- }
-
- pointer_follows_focus = pff;
- pointer_follows_monitor = pfm;
- auto_raise = true;
-}
-
-void handle_state(monitor_t *m, desktop_t *d, node_t *n, xcb_atom_t state, unsigned int action)
-{
- if (state == ewmh->_NET_WM_STATE_FULLSCREEN) {
- if (action == XCB_EWMH_WM_STATE_ADD) {
- set_state(m, d, n, STATE_FULLSCREEN);
- } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
- if (n->client->state == STATE_FULLSCREEN) {
- set_state(m, d, n, n->client->last_state);
- }
- } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
- set_state(m, d, n, IS_FULLSCREEN(n->client) ? n->client->last_state : STATE_FULLSCREEN);
- }
- arrange(m, d);
- } else if (state == ewmh->_NET_WM_STATE_BELOW) {
- if (action == XCB_EWMH_WM_STATE_ADD) {
- set_layer(m, d, n, LAYER_BELOW);
- } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
- if (n->client->layer == LAYER_BELOW) {
- set_layer(m, d, n, n->client->last_layer);
- }
- } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
- set_layer(m, d, n, n->client->layer == LAYER_BELOW ? n->client->last_layer : LAYER_BELOW);
- }
- } else if (state == ewmh->_NET_WM_STATE_ABOVE) {
- if (action == XCB_EWMH_WM_STATE_ADD) {
- set_layer(m, d, n, LAYER_ABOVE);
- } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
- if (n->client->layer == LAYER_ABOVE) {
- set_layer(m, d, n, n->client->last_layer);
- }
- } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
- set_layer(m, d, n, n->client->layer == LAYER_ABOVE ? n->client->last_layer : LAYER_ABOVE);
- }
- } else if (state == ewmh->_NET_WM_STATE_HIDDEN) {
- if (action == XCB_EWMH_WM_STATE_ADD) {
- set_hidden(m, d, n, true);
- } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
- set_hidden(m, d, n, false);
- } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
- set_hidden(m, d, n, !n->hidden);
- }
- } else if (state == ewmh->_NET_WM_STATE_STICKY) {
- if (action == XCB_EWMH_WM_STATE_ADD) {
- set_sticky(m, d, n, true);
- } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
- set_sticky(m, d, n, false);
- } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
- set_sticky(m, d, n, !n->sticky);
- }
- } else if (state == ewmh->_NET_WM_STATE_DEMANDS_ATTENTION) {
- if (action == XCB_EWMH_WM_STATE_ADD) {
- set_urgent(m, d, n, true);
- } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
- set_urgent(m, d, n, false);
- } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
- set_urgent(m, d, n, !n->client->urgent);
- }
-#define HANDLE_WM_STATE(s) \
- } else if (state == ewmh->_NET_WM_STATE_##s) { \
- if (action == XCB_EWMH_WM_STATE_ADD) { \
- n->client->wm_flags |= WM_FLAG_##s; \
- } else if (action == XCB_EWMH_WM_STATE_REMOVE) { \
- n->client->wm_flags &= ~WM_FLAG_##s; \
- } else if (action == XCB_EWMH_WM_STATE_TOGGLE) { \
- n->client->wm_flags ^= WM_FLAG_##s; \
- } \
- ewmh_wm_state_update(n);
- HANDLE_WM_STATE(MODAL)
- HANDLE_WM_STATE(MAXIMIZED_VERT)
- HANDLE_WM_STATE(MAXIMIZED_HORZ)
- HANDLE_WM_STATE(SHADED)
- HANDLE_WM_STATE(SKIP_TASKBAR)
- HANDLE_WM_STATE(SKIP_PAGER)
- }
-#undef HANDLE_WM_STATE
-}
-
-void process_error(xcb_generic_event_t *evt)
-{
- xcb_request_error_t *e = (xcb_request_error_t *) evt;
- warn("Failed request: %s, %s: 0x%08X.\n", xcb_event_get_request_label(e->major_opcode), xcb_event_get_error_label(e->error_code), e->bad_value);
-}
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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.
- */
-
-#ifndef BSPWM_EVENTS_H
-#define BSPWM_EVENTS_H
-
-#include <xcb/xcb.h>
-#include <xcb/xcb_event.h>
-
-uint8_t randr_base;
-
-void handle_event(xcb_generic_event_t *evt);
-void map_request(xcb_generic_event_t *evt);
-void configure_request(xcb_generic_event_t *evt);
-void configure_notify(xcb_generic_event_t *evt);
-void destroy_notify(xcb_generic_event_t *evt);
-void unmap_notify(xcb_generic_event_t *evt);
-void property_notify(xcb_generic_event_t *evt);
-void client_message(xcb_generic_event_t *evt);
-void focus_in(xcb_generic_event_t *evt);
-void button_press(xcb_generic_event_t *evt);
-void enter_notify(xcb_generic_event_t *evt);
-void handle_state(monitor_t *m, desktop_t *d, node_t *n, xcb_atom_t state, unsigned int action);
-void process_error(xcb_generic_event_t *evt);
-
-#endif
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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 <stdlib.h>
-#include <sys/types.h>
-#include <string.h>
-#include <unistd.h>
-#include "bspwm.h"
-#include "settings.h"
-#include "tree.h"
-#include "ewmh.h"
-
-void ewmh_init(void)
-{
- ewmh = calloc(1, sizeof(xcb_ewmh_connection_t));
- if (xcb_ewmh_init_atoms_replies(ewmh, xcb_ewmh_init_atoms(dpy, ewmh), NULL) == 0) {
- err("Can't initialize EWMH atoms.\n");
- }
-}
-
-void ewmh_update_active_window(void)
-{
- xcb_window_t win = ((mon->desk->focus == NULL || mon->desk->focus->client == NULL) ? XCB_NONE : mon->desk->focus->id);
- xcb_ewmh_set_active_window(ewmh, default_screen, win);
-}
-
-void ewmh_update_number_of_desktops(void)
-{
- uint32_t desktops_count = 0;
-
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
- desktops_count++;
- }
- }
-
- xcb_ewmh_set_number_of_desktops(ewmh, default_screen, desktops_count);
-}
-
-uint32_t ewmh_get_desktop_index(desktop_t *d)
-{
- uint32_t i = 0;
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- for (desktop_t *cd = m->desk_head; cd != NULL; cd = cd->next, i++) {
- if (d == cd) {
- return i;
- }
- }
- }
- return 0;
-}
-
-bool ewmh_locate_desktop(uint32_t i, coordinates_t *loc)
-{
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next, i--) {
- if (i == 0) {
- loc->monitor = m;
- loc->desktop = d;
- loc->node = NULL;
- return true;
- }
- }
- }
- return false;
-}
-
-void ewmh_update_current_desktop(void)
-{
- if (mon == NULL) {
- return;
- }
- uint32_t i = ewmh_get_desktop_index(mon->desk);
- xcb_ewmh_set_current_desktop(ewmh, default_screen, i);
-}
-
-void ewmh_set_wm_desktop(node_t *n, desktop_t *d)
-{
- uint32_t i = ewmh_get_desktop_index(d);
- for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
- if (f->client == NULL) {
- continue;
- }
- xcb_ewmh_set_wm_desktop(ewmh, f->id, i);
- }
-}
-
-void ewmh_update_wm_desktops(void)
-{
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
- uint32_t i = ewmh_get_desktop_index(d);
- for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
- if (n->client == NULL) {
- continue;
- }
- xcb_ewmh_set_wm_desktop(ewmh, n->id, i);
- }
- }
- }
-}
-
-void ewmh_update_desktop_names(void)
-{
- char names[MAXLEN];
- unsigned int i, j;
- uint32_t names_len;
- i = 0;
-
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
- for (j = 0; d->name[j] != '\0' && (i + j) < sizeof(names); j++) {
- names[i + j] = d->name[j];
- }
- i += j;
- if (i < sizeof(names)) {
- names[i++] = '\0';
- }
- }
- }
-
- if (i < 1) {
- xcb_ewmh_set_desktop_names(ewmh, default_screen, 0, NULL);
- return;
- }
-
- names_len = i - 1;
- xcb_ewmh_set_desktop_names(ewmh, default_screen, names_len, names);
-}
-
-void ewmh_update_desktop_viewport(void)
-{
- uint32_t desktops_count = 0;
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
- desktops_count++;
- }
- }
- xcb_ewmh_coordinates_t coords[desktops_count];
- uint16_t desktop = 0;
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
- coords[desktop++] = (xcb_ewmh_coordinates_t){m->rectangle.x, m->rectangle.y};
- }
- }
- xcb_ewmh_set_desktop_viewport(ewmh, default_screen, desktop, coords);
-}
-
-bool ewmh_handle_struts(xcb_window_t win)
-{
- xcb_ewmh_wm_strut_partial_t struts;
- bool changed = false;
- if (xcb_ewmh_get_wm_strut_partial_reply(ewmh, xcb_ewmh_get_wm_strut_partial(ewmh, win), &struts, NULL) == 1) {
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- xcb_rectangle_t rect = m->rectangle;
- if (rect.x < (int16_t) struts.left &&
- (int16_t) struts.left < (rect.x + rect.width - 1) &&
- (int16_t) struts.left_end_y >= rect.y &&
- (int16_t) struts.left_start_y < (rect.y + rect.height)) {
- int dx = struts.left - rect.x;
- if (m->padding.left < 0) {
- m->padding.left += dx;
- } else {
- m->padding.left = MAX(dx, m->padding.left);
- }
- changed = true;
- }
- if ((rect.x + rect.width) > (int16_t) (screen_width - struts.right) &&
- (int16_t) (screen_width - struts.right) > rect.x &&
- (int16_t) struts.right_end_y >= rect.y &&
- (int16_t) struts.right_start_y < (rect.y + rect.height)) {
- int dx = (rect.x + rect.width) - screen_width + struts.right;
- if (m->padding.right < 0) {
- m->padding.right += dx;
- } else {
- m->padding.right = MAX(dx, m->padding.right);
- }
- changed = true;
- }
- if (rect.y < (int16_t) struts.top &&
- (int16_t) struts.top < (rect.y + rect.height - 1) &&
- (int16_t) struts.top_end_x >= rect.x &&
- (int16_t) struts.top_start_x < (rect.x + rect.width)) {
- int dy = struts.top - rect.y;
- if (m->padding.top < 0) {
- m->padding.top += dy;
- } else {
- m->padding.top = MAX(dy, m->padding.top);
- }
- changed = true;
- }
- if ((rect.y + rect.height) > (int16_t) (screen_height - struts.bottom) &&
- (int16_t) (screen_height - struts.bottom) > rect.y &&
- (int16_t) struts.bottom_end_x >= rect.x &&
- (int16_t) struts.bottom_start_x < (rect.x + rect.width)) {
- int dy = (rect.y + rect.height) - screen_height + struts.bottom;
- if (m->padding.bottom < 0) {
- m->padding.bottom += dy;
- } else {
- m->padding.bottom = MAX(dy, m->padding.bottom);
- }
- changed = true;
- }
- }
- }
- return changed;
-}
-
-void ewmh_update_client_list(bool stacking)
-{
- if (clients_count == 0) {
- xcb_ewmh_set_client_list(ewmh, default_screen, 0, NULL);
- xcb_ewmh_set_client_list_stacking(ewmh, default_screen, 0, NULL);
- return;
- }
-
- xcb_window_t wins[clients_count];
- unsigned int i = 0;
-
- if (stacking) {
- for (stacking_list_t *s = stack_head; s != NULL; s = s->next) {
- wins[i++] = s->node->id;
- }
- xcb_ewmh_set_client_list_stacking(ewmh, default_screen, clients_count, wins);
- } else {
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- 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)) {
- if (n->client == NULL) {
- continue;
- }
- wins[i++] = n->id;
- }
- }
- }
- xcb_ewmh_set_client_list(ewmh, default_screen, clients_count, wins);
- }
-}
-
-void ewmh_wm_state_update(node_t *n)
-{
- client_t *c = n->client;
- size_t count = 0;
- uint32_t values[12];
-#define HANDLE_WM_STATE(s) \
- if (WM_FLAG_##s & c->wm_flags) { \
- values[count++] = ewmh->_NET_WM_STATE_##s; \
- }
- HANDLE_WM_STATE(MODAL)
- HANDLE_WM_STATE(STICKY)
- HANDLE_WM_STATE(MAXIMIZED_VERT)
- HANDLE_WM_STATE(MAXIMIZED_HORZ)
- HANDLE_WM_STATE(SHADED)
- HANDLE_WM_STATE(SKIP_TASKBAR)
- HANDLE_WM_STATE(SKIP_PAGER)
- HANDLE_WM_STATE(HIDDEN)
- HANDLE_WM_STATE(FULLSCREEN)
- HANDLE_WM_STATE(ABOVE)
- HANDLE_WM_STATE(BELOW)
- HANDLE_WM_STATE(DEMANDS_ATTENTION)
-#undef HANDLE_WM_STATE
- xcb_ewmh_set_wm_state(ewmh, n->id, count, values);
-}
-
-void ewmh_set_supporting(xcb_window_t win)
-{
- pid_t wm_pid = getpid();
- xcb_ewmh_set_supporting_wm_check(ewmh, root, win);
- xcb_ewmh_set_supporting_wm_check(ewmh, win, win);
- xcb_ewmh_set_wm_name(ewmh, win, strlen(WM_NAME), WM_NAME);
- xcb_ewmh_set_wm_pid(ewmh, win, wm_pid);
-}
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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.
- */
-
-#ifndef BSPWM_EWMH_H
-#define BSPWM_EWMH_H
-
-#include <xcb/xcb_ewmh.h>
-
-xcb_ewmh_connection_t *ewmh;
-
-void ewmh_init(void);
-void ewmh_update_active_window(void);
-void ewmh_update_number_of_desktops(void);
-uint32_t ewmh_get_desktop_index(desktop_t *d);
-bool ewmh_locate_desktop(uint32_t i, coordinates_t *loc);
-void ewmh_update_current_desktop(void);
-void ewmh_set_wm_desktop(node_t *n, desktop_t *d);
-void ewmh_update_wm_desktops(void);
-void ewmh_update_desktop_names(void);
-void ewmh_update_desktop_viewport(void);
-bool ewmh_handle_struts(xcb_window_t win);
-void ewmh_update_client_list(bool stacking);
-void ewmh_wm_state_update(node_t *n);
-void ewmh_set_supporting(xcb_window_t win);
-
-#endif
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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 <math.h>
-#include "types.h"
-#include "geometry.h"
-
-bool is_inside(xcb_point_t p, xcb_rectangle_t r)
-{
- return (p.x >= r.x && p.x < (r.x + r.width) &&
- p.y >= r.y && p.y < (r.y + r.height));
-}
-
-unsigned int area(xcb_rectangle_t r)
-{
- return r.width * r.height;
-}
-
-
-dpoint_t center(xcb_rectangle_t r)
-{
- return (dpoint_t) {(double)r.x + ((double)r.width / 2), (double)r.y + ((double)r.height / 2)};
-}
-
-double distance_center(xcb_rectangle_t r1, xcb_rectangle_t r2)
-{
- dpoint_t r1_center = center(r1);
- dpoint_t r2_center = center(r2);
- return hypot(r1_center.x - r2_center.x, r1_center.y - r2_center.y);
-}
-
-bool on_dir_side(xcb_rectangle_t r1, xcb_rectangle_t r2, direction_t dir)
-{
- dpoint_t r1_max = {r1.x + r1.width, r1.y + r1.height};
- dpoint_t r2_max = {r2.x + r2.width, r2.y + r2.height};
- dpoint_t r1_center = center(r1);
- dpoint_t r2_center = center(r2);
-
- switch (dir) {
- case DIR_NORTH:
- if (r2_center.y >= r1_center.y)
- return false;
- break;
- case DIR_WEST:
- if (r2_center.x >= r1_center.x)
- return false;
- break;
- case DIR_SOUTH:
- if (r1_center.y >= r2_center.y)
- return false;
- break;
- case DIR_EAST:
- if (r1_center.x >= r2_center.x)
- return false;
- break;
- default:
- return false;
- }
-
- switch (dir) {
- case DIR_NORTH:
- case DIR_SOUTH:
- return
- (r2.x >= r1.x && r2.x <= r1_max.x) ||
- (r2_max.x >= r1.x && r2_max.x <= r1_max.x) ||
- (r1.x >= r2.x && r1.x <= r2_max.x) ||
- (r1_max.x >= r2.x && r1_max.x <= r2_max.x);
- break;
- case DIR_WEST:
- case DIR_EAST:
- return
- (r2.y >= r1.y && r2.y <= r1_max.y) ||
- (r2_max.y >= r1.y && r2_max.y <= r1_max.y) ||
- (r1.y >= r2.y && r1.y <= r2_max.y) ||
- (r1_max.y >= r2.y && r1_max.y <= r2_max.y);
- break;
- default:
- return false;
- }
-}
-
-bool rect_eq(xcb_rectangle_t a, xcb_rectangle_t b)
-{
- return (a.x == b.x && a.y == b.y &&
- a.width == b.width && a.height == b.height);
-}
-
-int rect_cmp(xcb_rectangle_t r1, xcb_rectangle_t r2)
-{
- if (r1.y >= (r2.y + r2.height)) {
- return 1;
- } else if (r2.y >= (r1.y + r1.height)) {
- return -1;
- } else {
- if (r1.x >= (r2.x + r2.width)) {
- return 1;
- } else if (r2.x >= (r1.x + r1.width)) {
- return -1;
- } else {
- return area(r1) - area(r2);
- }
- }
-}
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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.
- */
-
-#ifndef BSPWM_GEOMETRY_H
-#define BSPWM_GEOMETRY_H
-
-#include <stdbool.h>
-#include <xcb/xcb.h>
-
-bool is_inside(xcb_point_t p, xcb_rectangle_t r);
-unsigned int area(xcb_rectangle_t r);
-dpoint_t center(xcb_rectangle_t r);
-double distance_center(xcb_rectangle_t r1, xcb_rectangle_t r2);
-bool on_dir_side(xcb_rectangle_t r1, xcb_rectangle_t r2, direction_t dir);
-bool rect_eq(xcb_rectangle_t a, xcb_rectangle_t b);
-int rect_cmp(xcb_rectangle_t r1, xcb_rectangle_t r2);
-
-#endif
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdarg.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include "bspwm.h"
-
-void warn(char *fmt, ...)
-{
- va_list ap;
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
-}
-
-__attribute__((noreturn))
-void err(char *fmt, ...)
-{
- va_list ap;
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- exit(EXIT_FAILURE);
-}
-
-char *read_string(const char *file_path, size_t *tlen)
-{
- if (file_path == NULL) {
- return NULL;
- }
-
- int fd = open(file_path, O_RDONLY);
-
- if (fd == -1) {
- perror("Read file: open");
- return NULL;
- }
-
- char buf[BUFSIZ], *content = NULL;
- size_t len = sizeof(buf);
-
- if ((content = calloc(len, sizeof(char))) == NULL) {
- perror("Read file: calloc");
- goto end;
- }
-
- int nb;
- *tlen = 0;
-
- while (true) {
- nb = read(fd, buf, sizeof(buf));
- if (nb < 0) {
- perror("Restore tree: read");
- free(content);
- content = NULL;
- goto end;
- } else if (nb == 0) {
- break;
- } else {
- *tlen += nb;
- if (*tlen > len) {
- len *= 2;
- char *rcontent = realloc(content, len * sizeof(char));
- if (rcontent == NULL) {
- perror("Read file: realloc");
- free(content);
- content = NULL;
- goto end;
- } else {
- content = rcontent;
- }
- }
- strncpy(content + (*tlen - nb), buf, nb);
- }
- }
-
-end:
- close(fd);
- return content;
-}
-
-char *copy_string(char *str, size_t len)
-{
- char *cpy = calloc(1, ((len+1) * sizeof(char)));
- if (cpy == NULL) {
- perror("Copy string: calloc");
- return NULL;
- }
- strncpy(cpy, str, len);
- cpy[len] = '\0';
- return cpy;
-}
-
-/* Adapted from i3wm */
-uint32_t get_color_pixel(const char *color)
-{
- unsigned int red, green, blue;
- if (sscanf(color + 1, "%02x%02x%02x", &red, &green, &blue) == 3) {
- /* We set the first 8 bits high to have 100% opacity in case of a 32 bit
- * color depth visual. */
- return (0xFF << 24) | (red << 16 | green << 8 | blue);
- } else {
- return screen->black_pixel;
- }
-}
-
-bool is_hex_color(const char *color)
-{
- if (color[0] != '#' || strlen(color) != 7) {
- return false;
- }
- for (int i = 1; i < 7; i++) {
- if (!isxdigit(color[i])) {
- return false;
- }
- }
- return true;
-}
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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.
- */
-
-#ifndef BSPWM_HELPERS_H
-#define BSPWM_HELPERS_H
-
-#include <xcb/xcb.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <float.h>
-
-#define LENGTH(x) (sizeof(x) / sizeof(*x))
-#define MAX(A, B) ((A) > (B) ? (A) : (B))
-#define MIN(A, B) ((A) < (B) ? (A) : (B))
-
-#define IS_TILED(c) (c->state == STATE_TILED || c->state == STATE_PSEUDO_TILED)
-#define IS_FLOATING(c) (c->state == STATE_FLOATING)
-#define IS_FULLSCREEN(c) (c->state == STATE_FULLSCREEN)
-#define IS_RECEPTACLE(n) (is_leaf(n) && n->client == NULL)
-#define IS_MONOCLE(d) (d->layout == LAYOUT_MONOCLE || (single_monocle && tiled_count(d->root) <= 1))
-
-#define BOOL_STR(A) ((A) ? "true" : "false")
-#define ON_OFF_STR(A) ((A) ? "on" : "off")
-#define LAYOUT_STR(A) ((A) == LAYOUT_TILED ? "tiled" : "monocle")
-#define LAYOUT_CHR(A) ((A) == LAYOUT_TILED ? 'T' : 'M')
-#define CHILD_POL_STR(A) ((A) == FIRST_CHILD ? "first_child" : "second_child")
-#define SPLIT_TYPE_STR(A) ((A) == TYPE_HORIZONTAL ? "horizontal" : "vertical")
-#define SPLIT_MODE_STR(A) ((A) == MODE_AUTOMATIC ? "automatic" : "manual")
-#define SPLIT_DIR_STR(A) ((A) == DIR_NORTH ? "north" : ((A) == DIR_WEST ? "west" : ((A) == DIR_SOUTH ? "south" : "east")))
-#define STATE_STR(A) ((A) == STATE_TILED ? "tiled" : ((A) == STATE_FLOATING ? "floating" : ((A) == STATE_FULLSCREEN ? "fullscreen" : "pseudo_tiled")))
-#define STATE_CHR(A) ((A) == STATE_TILED ? 'T' : ((A) == STATE_FLOATING ? 'F' : ((A) == STATE_FULLSCREEN ? '=' : 'P')))
-#define LAYER_STR(A) ((A) == LAYER_BELOW ? "below" : ((A) == LAYER_NORMAL ? "normal" : "above"))
-
-#define XCB_CONFIG_WINDOW_X_Y (XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y)
-#define XCB_CONFIG_WINDOW_WIDTH_HEIGHT (XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT)
-#define XCB_CONFIG_WINDOW_X_Y_WIDTH_HEIGHT (XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT)
-
-#define MAXLEN 256
-#define SMALEN 32
-#define INIT_CAP 8
-
-#define cleaned_mask(m) (m & ~(num_lock | scroll_lock | caps_lock))
-#define streq(s1, s2) (strcmp((s1), (s2)) == 0)
-#define unsigned_subtract(a, b) \
- do { \
- if (b > a) { \
- a = 0; \
- } else { \
- a -= b; \
- } \
- } while (false)
-
-
-void warn(char *fmt, ...);
-void err(char *fmt, ...);
-char *read_string(const char *file_path, size_t *tlen);
-char *copy_string(char *str, size_t len);
-uint32_t get_color_pixel(const char *color);
-bool is_hex_color(const char *color);
-
-#endif
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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 <stdlib.h>
-#include <stdbool.h>
-#include "bspwm.h"
-#include "tree.h"
-#include "query.h"
-
-history_t *make_history(monitor_t *m, desktop_t *d, node_t *n)
-{
- history_t *h = calloc(1, sizeof(history_t));
- h->loc = (coordinates_t) {m, d, n};
- h->prev = h->next = NULL;
- h->latest = true;
- return h;
-}
-
-void history_add(monitor_t *m, desktop_t *d, node_t *n)
-{
- if (!record_history) {
- return;
- }
- history_needle = NULL;
- history_t *h = make_history(m, d, n);
- if (history_head == NULL) {
- history_head = history_tail = h;
- } else if ((n != NULL && history_tail->loc.node != n) || (n == NULL && d != history_tail->loc.desktop)) {
- for (history_t *hh = history_tail; hh != NULL; hh = hh->prev) {
- if ((n != NULL && hh->loc.node == n) || (n == NULL && d == hh->loc.desktop)) {
- hh->latest = false;
- }
- }
- history_tail->next = h;
- h->prev = history_tail;
- history_tail = h;
- } else {
- free(h);
- }
-}
-
-void history_transfer_node(monitor_t *m, desktop_t *d, node_t *n)
-{
- for (history_t *h = history_head; h != NULL; h = h->next) {
- if (is_descendant(h->loc.node, n)) {
- h->loc.monitor = m;
- h->loc.desktop = d;
- }
- }
-}
-
-void history_transfer_desktop(monitor_t *m, desktop_t *d)
-{
- for (history_t *h = history_head; h != NULL; h = h->next) {
- if (h->loc.desktop == d) {
- h->loc.monitor = m;
- }
- }
-}
-
-void history_swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop_t *d2, node_t *n2)
-{
- for (history_t *h = history_head; h != NULL; h = h->next) {
- if (is_descendant(h->loc.node, n1)) {
- h->loc.monitor = m2;
- h->loc.desktop = d2;
- } else if (is_descendant(h->loc.node, n2)) {
- h->loc.monitor = m1;
- h->loc.desktop = d1;
- }
- }
-}
-
-void history_swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2)
-{
- for (history_t *h = history_head; h != NULL; h = h->next) {
- if (h->loc.desktop == d1) {
- h->loc.monitor = m2;
- } else if (h->loc.desktop == d2) {
- h->loc.monitor = m1;
- }
- }
-}
-
-void history_remove(desktop_t *d, node_t *n, bool deep)
-{
- /* removing from the newest to the oldest is required */
- /* for maintaining the *latest* attribute */
- history_t *b = history_tail;
- while (b != NULL) {
- if ((n != NULL && ((deep && is_descendant(b->loc.node, n)) || (!deep && b->loc.node == n))) ||
- (n == NULL && d == b->loc.desktop)) {
- history_t *a = b->next;
- history_t *c = b->prev;
- if (a != NULL) {
- /* remove duplicate entries */
- while (c != NULL && ((a->loc.node != NULL && a->loc.node == c->loc.node) ||
- (a->loc.node == NULL && a->loc.desktop == c->loc.desktop))) {
- history_t *p = c->prev;
- if (history_head == c) {
- history_head = history_tail;
- }
- if (history_needle == c) {
- history_needle = history_tail;
- }
- free(c);
- c = p;
- }
- a->prev = c;
- }
- if (c != NULL) {
- c->next = a;
- }
- if (history_tail == b) {
- history_tail = c;
- }
- if (history_head == b) {
- history_head = a;
- }
- if (history_needle == b) {
- history_needle = c;
- }
- free(b);
- b = c;
- } else {
- b = b->prev;
- }
- }
-}
-
-void empty_history(void)
-{
- history_t *h = history_head;
- while (h != NULL) {
- history_t *next = h->next;
- free(h);
- h = next;
- }
- history_head = history_tail = NULL;
-}
-
-node_t *history_last_node(desktop_t *d, node_t *n)
-{
- for (history_t *h = history_tail; h != NULL; h = h->prev) {
- if (h->latest && h->loc.node != NULL && !h->loc.node->hidden &&
- !is_descendant(h->loc.node, n) && h->loc.desktop == d) {
- return h->loc.node;
- }
- }
- return NULL;
-}
-
-desktop_t *history_last_desktop(monitor_t *m, desktop_t *d)
-{
- for (history_t *h = history_tail; h != NULL; h = h->prev) {
- if (h->latest && h->loc.desktop != d && h->loc.monitor == m) {
- return h->loc.desktop;
- }
- }
- return NULL;
-}
-
-monitor_t *history_last_monitor(monitor_t *m)
-{
- for (history_t *h = history_tail; h != NULL; h = h->prev) {
- if (h->latest && h->loc.monitor != m) {
- return h->loc.monitor;
- }
- }
- return NULL;
-}
-
-bool history_find_node(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, node_select_t sel)
-{
- if (history_needle == NULL || record_history) {
- history_needle = history_tail;
- }
-
- history_t *h;
- for (h = history_needle; h != NULL; h = (hdi == HISTORY_OLDER ? h->prev : h->next)) {
- if (!h->latest ||
- h->loc.node == NULL ||
- h->loc.node == ref->node ||
- h->loc.node->hidden ||
- !node_matches(&h->loc, ref, sel)) {
- continue;
- }
- if (!record_history) {
- history_needle = h;
- }
- *dst = h->loc;
- return true;
- }
- return false;
-}
-
-bool history_find_desktop(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, desktop_select_t sel)
-{
- if (history_needle == NULL || record_history) {
- history_needle = history_tail;
- }
-
- history_t *h;
- for (h = history_needle; h != NULL; h = (hdi == HISTORY_OLDER ? h->prev : h->next)) {
- if (!h->latest ||
- h->loc.desktop == ref->desktop ||
- !desktop_matches(&h->loc, ref, sel)) {
- continue;
- }
- if (!record_history) {
- history_needle = h;
- }
- *dst = h->loc;
- return true;
- }
- return false;
-}
-
-bool history_find_monitor(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, monitor_select_t sel)
-{
- if (history_needle == NULL || record_history) {
- history_needle = history_tail;
- }
-
- history_t *h;
-
- for (h = history_needle; h != NULL; h = (hdi == HISTORY_OLDER ? h->prev : h->next)) {
- if (!h->latest ||
- h->loc.monitor == ref->monitor ||
- !monitor_matches(&h->loc, ref, sel)) {
- continue;
- }
- if (!record_history) {
- history_needle = h;
- }
- *dst = h->loc;
- return true;
- }
-
- return false;
-}
-
-uint32_t history_rank(node_t *n)
-{
- uint32_t r = 0;
- history_t *h = history_tail;
- while (h != NULL && (!h->latest || h->loc.node != n)) {
- h = h->prev;
- r++;
- }
- if (h == NULL) {
- return UINT32_MAX;
- } else {
- return r;
- }
-}
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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.
- */
-
-#ifndef BSPWM_HISTORY_H
-#define BSPWM_HISTORY_H
-
-#include "types.h"
-
-history_t *make_history(monitor_t *m, desktop_t *d, node_t *n);
-void history_add(monitor_t *m, desktop_t *d, node_t *n);
-void history_transfer_node(monitor_t *m, desktop_t *d, node_t *n);
-void history_transfer_desktop(monitor_t *m, desktop_t *d);
-void history_swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop_t *d2, node_t *n2);
-void history_swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2);
-void history_remove(desktop_t *d, node_t *n, bool deep);
-void empty_history(void);
-node_t *history_last_node(desktop_t *d, node_t *n);
-desktop_t *history_last_desktop(monitor_t *m, desktop_t *d);
-monitor_t *history_last_monitor(monitor_t *m);
-bool history_find_node(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, node_select_t sel);
-bool history_find_desktop(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, desktop_select_t sel);
-bool history_find_monitor(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, monitor_select_t sel);
-uint32_t history_rank(node_t *n);
-
-#endif
+++ /dev/null
-#include "jsmn.h"
-
-/**
- * Allocates a fresh unused token from the token pull.
- */
-static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
- jsmntok_t *tokens, size_t num_tokens) {
- jsmntok_t *tok;
- if (parser->toknext >= num_tokens) {
- return NULL;
- }
- tok = &tokens[parser->toknext++];
- tok->start = tok->end = -1;
- tok->size = 0;
-#ifdef JSMN_PARENT_LINKS
- tok->parent = -1;
-#endif
- return tok;
-}
-
-/**
- * Fills token type and boundaries.
- */
-static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
- int start, int end) {
- token->type = type;
- token->start = start;
- token->end = end;
- token->size = 0;
-}
-
-/**
- * Fills next available token with JSON primitive.
- */
-static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
- size_t len, jsmntok_t *tokens, size_t num_tokens) {
- jsmntok_t *token;
- int start;
-
- start = parser->pos;
-
- for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
- switch (js[parser->pos]) {
-#ifndef JSMN_STRICT
- /* In strict mode primitive must be followed by "," or "}" or "]" */
- case ':':
-#endif
- case '\t' : case '\r' : case '\n' : case ' ' :
- case ',' : case ']' : case '}' :
- goto found;
- }
- if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
- parser->pos = start;
- return JSMN_ERROR_INVAL;
- }
- }
-#ifdef JSMN_STRICT
- /* In strict mode primitive must be followed by a comma/object/array */
- parser->pos = start;
- return JSMN_ERROR_PART;
-#endif
-
-found:
- if (tokens == NULL) {
- parser->pos--;
- return 0;
- }
- token = jsmn_alloc_token(parser, tokens, num_tokens);
- if (token == NULL) {
- parser->pos = start;
- return JSMN_ERROR_NOMEM;
- }
- jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
-#ifdef JSMN_PARENT_LINKS
- token->parent = parser->toksuper;
-#endif
- parser->pos--;
- return 0;
-}
-
-/**
- * Filsl next token with JSON string.
- */
-static int jsmn_parse_string(jsmn_parser *parser, const char *js,
- size_t len, jsmntok_t *tokens, size_t num_tokens) {
- jsmntok_t *token;
-
- int start = parser->pos;
-
- parser->pos++;
-
- /* Skip starting quote */
- for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
- char c = js[parser->pos];
-
- /* Quote: end of string */
- if (c == '\"') {
- if (tokens == NULL) {
- return 0;
- }
- token = jsmn_alloc_token(parser, tokens, num_tokens);
- if (token == NULL) {
- parser->pos = start;
- return JSMN_ERROR_NOMEM;
- }
- jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
-#ifdef JSMN_PARENT_LINKS
- token->parent = parser->toksuper;
-#endif
- return 0;
- }
-
- /* Backslash: Quoted symbol expected */
- if (c == '\\' && parser->pos + 1 < len) {
- int i;
- parser->pos++;
- switch (js[parser->pos]) {
- /* Allowed escaped symbols */
- case '\"': case '/' : case '\\' : case 'b' :
- case 'f' : case 'r' : case 'n' : case 't' :
- break;
- /* Allows escaped symbol \uXXXX */
- case 'u':
- parser->pos++;
- for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
- /* If it isn't a hex character we have an error */
- if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
- (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
- (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
- parser->pos = start;
- return JSMN_ERROR_INVAL;
- }
- parser->pos++;
- }
- parser->pos--;
- break;
- /* Unexpected symbol */
- default:
- parser->pos = start;
- return JSMN_ERROR_INVAL;
- }
- }
- }
- parser->pos = start;
- return JSMN_ERROR_PART;
-}
-
-/**
- * Parse JSON string and fill tokens.
- */
-int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
- jsmntok_t *tokens, unsigned int num_tokens) {
- int r;
- int i;
- jsmntok_t *token;
- int count = parser->toknext;
-
- for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
- char c;
- jsmntype_t type;
-
- c = js[parser->pos];
- switch (c) {
- case '{': case '[':
- count++;
- if (tokens == NULL) {
- break;
- }
- token = jsmn_alloc_token(parser, tokens, num_tokens);
- if (token == NULL)
- return JSMN_ERROR_NOMEM;
- if (parser->toksuper != -1) {
- tokens[parser->toksuper].size++;
-#ifdef JSMN_PARENT_LINKS
- token->parent = parser->toksuper;
-#endif
- }
- token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
- token->start = parser->pos;
- parser->toksuper = parser->toknext - 1;
- break;
- case '}': case ']':
- if (tokens == NULL)
- break;
- type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
-#ifdef JSMN_PARENT_LINKS
- if (parser->toknext < 1) {
- return JSMN_ERROR_INVAL;
- }
- token = &tokens[parser->toknext - 1];
- for (;;) {
- if (token->start != -1 && token->end == -1) {
- if (token->type != type) {
- return JSMN_ERROR_INVAL;
- }
- token->end = parser->pos + 1;
- parser->toksuper = token->parent;
- break;
- }
- if (token->parent == -1) {
- break;
- }
- token = &tokens[token->parent];
- }
-#else
- for (i = parser->toknext - 1; i >= 0; i--) {
- token = &tokens[i];
- if (token->start != -1 && token->end == -1) {
- if (token->type != type) {
- return JSMN_ERROR_INVAL;
- }
- parser->toksuper = -1;
- token->end = parser->pos + 1;
- break;
- }
- }
- /* Error if unmatched closing bracket */
- if (i == -1) return JSMN_ERROR_INVAL;
- for (; i >= 0; i--) {
- token = &tokens[i];
- if (token->start != -1 && token->end == -1) {
- parser->toksuper = i;
- break;
- }
- }
-#endif
- break;
- case '\"':
- r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
- if (r < 0) return r;
- count++;
- if (parser->toksuper != -1 && tokens != NULL)
- tokens[parser->toksuper].size++;
- break;
- case '\t' : case '\r' : case '\n' : case ' ':
- break;
- case ':':
- parser->toksuper = parser->toknext - 1;
- break;
- case ',':
- if (tokens != NULL && parser->toksuper != -1 &&
- tokens[parser->toksuper].type != JSMN_ARRAY &&
- tokens[parser->toksuper].type != JSMN_OBJECT) {
-#ifdef JSMN_PARENT_LINKS
- parser->toksuper = tokens[parser->toksuper].parent;
-#else
- for (i = parser->toknext - 1; i >= 0; i--) {
- if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
- if (tokens[i].start != -1 && tokens[i].end == -1) {
- parser->toksuper = i;
- break;
- }
- }
- }
-#endif
- }
- break;
-#ifdef JSMN_STRICT
- /* In strict mode primitives are: numbers and booleans */
- case '-': case '0': case '1' : case '2': case '3' : case '4':
- case '5': case '6': case '7' : case '8': case '9':
- case 't': case 'f': case 'n' :
- /* And they must not be keys of the object */
- if (tokens != NULL && parser->toksuper != -1) {
- jsmntok_t *t = &tokens[parser->toksuper];
- if (t->type == JSMN_OBJECT ||
- (t->type == JSMN_STRING && t->size != 0)) {
- return JSMN_ERROR_INVAL;
- }
- }
-#else
- /* In non-strict mode every unquoted value is a primitive */
- default:
-#endif
- r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
- if (r < 0) return r;
- count++;
- if (parser->toksuper != -1 && tokens != NULL)
- tokens[parser->toksuper].size++;
- break;
-
-#ifdef JSMN_STRICT
- /* Unexpected char in strict mode */
- default:
- return JSMN_ERROR_INVAL;
-#endif
- }
- }
-
- if (tokens != NULL) {
- for (i = parser->toknext - 1; i >= 0; i--) {
- /* Unmatched opened object or array */
- if (tokens[i].start != -1 && tokens[i].end == -1) {
- return JSMN_ERROR_PART;
- }
- }
- }
-
- return count;
-}
-
-/**
- * Creates a new parser based over a given buffer with an array of tokens
- * available.
- */
-void jsmn_init(jsmn_parser *parser) {
- parser->pos = 0;
- parser->toknext = 0;
- parser->toksuper = -1;
-}
-
+++ /dev/null
-#ifndef __JSMN_H_
-#define __JSMN_H_
-
-#include <stddef.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * JSON type identifier. Basic types are:
- * o Object
- * o Array
- * o String
- * o Other primitive: number, boolean (true/false) or null
- */
-typedef enum {
- JSMN_UNDEFINED = 0,
- JSMN_OBJECT = 1,
- JSMN_ARRAY = 2,
- JSMN_STRING = 3,
- JSMN_PRIMITIVE = 4
-} jsmntype_t;
-
-enum jsmnerr {
- /* Not enough tokens were provided */
- JSMN_ERROR_NOMEM = -1,
- /* Invalid character inside JSON string */
- JSMN_ERROR_INVAL = -2,
- /* The string is not a full JSON packet, more bytes expected */
- JSMN_ERROR_PART = -3
-};
-
-/**
- * JSON token description.
- * @param type type (object, array, string etc.)
- * @param start start position in JSON data string
- * @param end end position in JSON data string
- */
-typedef struct {
- jsmntype_t type;
- int start;
- int end;
- int size;
-#ifdef JSMN_PARENT_LINKS
- int parent;
-#endif
-} jsmntok_t;
-
-/**
- * JSON parser. Contains an array of token blocks available. Also stores
- * the string being parsed now and current position in that string
- */
-typedef struct {
- unsigned int pos; /* offset in the JSON string */
- unsigned int toknext; /* next token to allocate */
- int toksuper; /* superior token node, e.g parent object or array */
-} jsmn_parser;
-
-/**
- * Create JSON parser over an array of tokens
- */
-void jsmn_init(jsmn_parser *parser);
-
-/**
- * Run JSON parser. It parses a JSON data string into and array of tokens, each describing
- * a single JSON object.
- */
-int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
- jsmntok_t *tokens, unsigned int num_tokens);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __JSMN_H_ */
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdbool.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include "bspwm.h"
-#include "desktop.h"
-#include "monitor.h"
-#include "pointer.h"
-#include "query.h"
-#include "rule.h"
-#include "restore.h"
-#include "settings.h"
-#include "tree.h"
-#include "window.h"
-#include "common.h"
-#include "parse.h"
-#include "messages.h"
-
-void handle_message(char *msg, int msg_len, FILE *rsp)
-{
- int cap = INIT_CAP;
- int num = 0;
- char **args = calloc(cap, sizeof(char *));
-
- if (args == NULL) {
- perror("Handle message: calloc");
- return;
- }
-
- for (int i = 0, j = 0; i < msg_len; i++) {
- if (msg[i] == 0) {
- args[num++] = msg + j;
- j = i + 1;
- }
- if (num >= cap) {
- cap *= 2;
- char **new = realloc(args, cap * sizeof(char *));
- if (new == NULL) {
- free(args);
- perror("Handle message: realloc");
- return;
- } else {
- args = new;
- }
- }
- }
-
- if (num < 1) {
- free(args);
- fail(rsp, "No arguments given.\n");
- return;
- }
-
- char **args_orig = args;
- process_message(args, num, rsp);
- free(args_orig);
-}
-
-void process_message(char **args, int num, FILE *rsp)
-{
- int ret = SUBSCRIBE_FAILURE;
-
- if (streq("node", *args)) {
- cmd_node(++args, --num, rsp);
- } else if (streq("desktop", *args)) {
- cmd_desktop(++args, --num, rsp);
- } else if (streq("monitor", *args)) {
- cmd_monitor(++args, --num, rsp);
- } else if (streq("query", *args)) {
- cmd_query(++args, --num, rsp);
- } else if (streq("subscribe", *args)) {
- ret = cmd_subscribe(++args, --num, rsp);
- } else if (streq("wm", *args)) {
- cmd_wm(++args, --num, rsp);
- } else if (streq("rule", *args)) {
- cmd_rule(++args, --num, rsp);
- } else if (streq("config", *args)) {
- cmd_config(++args, --num, rsp);
- } else if (streq("quit", *args)) {
- cmd_quit(++args, --num, rsp);
- } else {
- fail(rsp, "Unknown domain or command: '%s'.\n", *args);
- }
-
- fflush(rsp);
-
- if (ret != SUBSCRIBE_SUCCESS) {
- fclose(rsp);
- }
-}
-
-void cmd_node(char **args, int num, FILE *rsp)
-{
- if (num < 1) {
- fail(rsp, "node: Missing commands.\n");
- return;
- }
-
- coordinates_t ref = {mon, mon->desk, mon->desk->focus};
- coordinates_t trg = ref;
-
- if ((*args)[0] != OPT_CHR) {
- int ret;
- if ((ret = node_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {
- num--, args++;
- } else {
- handle_failure(ret, "node", *args, rsp);
- return;
- }
- }
-
- bool changed = false;
-
- while (num > 0) {
- if (streq("-f", *args) || streq("--focus", *args)) {
- coordinates_t dst = trg;
- if (num > 1 && *(args + 1)[0] != OPT_CHR) {
- num--, args++;
- int ret;
- if ((ret = node_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
- handle_failure(ret, "node -f", *args, rsp);
- break;
- }
- }
- if (dst.node == NULL || !focus_node(dst.monitor, dst.desktop, dst.node)) {
- fail(rsp, "");
- break;
- }
- } else if (streq("-a", *args) || streq("--activate", *args)) {
- coordinates_t dst = trg;
- if (num > 1 && *(args + 1)[0] != OPT_CHR) {
- num--, args++;
- int ret;
- if ((ret = node_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
- handle_failure(ret, "node -a", *args, rsp);
- break;
- }
- }
- if (dst.node == NULL || !activate_node(dst.monitor, dst.desktop, dst.node)) {
- fail(rsp, "");
- break;
- }
- } else if (streq("-d", *args) || streq("--to-desktop", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
- break;
- }
- coordinates_t dst;
- int ret;
- if ((ret = desktop_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
- if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.desktop->focus)) {
- trg.monitor = dst.monitor;
- trg.desktop = dst.desktop;
- } else {
- fail(rsp, "");
- break;
- }
- } else {
- handle_failure(ret, "node -d", *args, rsp);
- break;
- }
- } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
- break;
- }
- coordinates_t dst;
- int ret;
- if ((ret = monitor_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
- if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.monitor->desk, dst.monitor->desk->focus)) {
- trg.monitor = dst.monitor;
- trg.desktop = dst.monitor->desk;
- } else {
- fail(rsp, "");
- break;
- }
- } else {
- handle_failure(ret, "node -m", *args, rsp);
- break;
- }
- } else if (streq("-n", *args) || streq("--to-node", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
- break;
- }
- coordinates_t dst;
- int ret;
- if ((ret = node_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
- if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.node)) {
- trg.monitor = dst.monitor;
- trg.desktop = dst.desktop;
- } else {
- fail(rsp, "");
- break;
- }
- } else {
- handle_failure(ret, "node -n", *args, rsp);
- break;
- }
- } else if (streq("-s", *args) || streq("--swap", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
- break;
- }
- coordinates_t dst;
- int ret;
- if ((ret = node_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
- if (swap_nodes(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.node)) {
- trg.monitor = dst.monitor;
- trg.desktop = dst.desktop;
- } else {
- fail(rsp, "");
- break;
- }
- } else {
- handle_failure(ret, "node -s", *args, rsp);
- break;
- }
- } else if (streq("-l", *args) || streq("--layer", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
- break;
- }
- stack_layer_t lyr;
- if (parse_stack_layer(*args, &lyr)) {
- if (!set_layer(trg.monitor, trg.desktop, trg.node, lyr)) {
- fail(rsp, "");
- break;
- }
- } else {
- fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
- break;
- }
- } else if (streq("-t", *args) || streq("--state", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
- break;
- }
- client_state_t cst;
- bool alternate = false;
- if ((*args)[0] == '~') {
- alternate = true;
- (*args)++;
- }
- if (parse_client_state(*args, &cst)) {
- if (alternate && trg.node != NULL && trg.node->client != NULL &&
- trg.node->client->state == cst) {
- cst = trg.node->client->last_state;
- }
- if (!set_state(trg.monitor, trg.desktop, trg.node, cst)) {
- fail(rsp, "");
- break;
- }
- changed = true;
- } else {
- fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
- break;
- }
- } else if (streq("-g", *args) || streq("--flag", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
- break;
- }
- if (trg.node == NULL) {
- fail(rsp, "");
- break;
- }
- char *key = strtok(*args, EQL_TOK);
- char *val = strtok(NULL, EQL_TOK);
- alter_state_t a;
- bool b;
- if (val == NULL) {
- a = ALTER_TOGGLE;
- } else {
- if (parse_bool(val, &b)) {
- a = ALTER_SET;
- } else {
- fail(rsp, "node %s: Invalid value for %s: '%s'.\n", *(args - 1), key, val);
- break;
- }
- }
- if (streq("hidden", key)) {
- set_hidden(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->hidden));
- changed = true;
- } else if (streq("sticky", key)) {
- set_sticky(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->sticky));
- } else if (streq("private", key)) {
- set_private(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->private));
- } else if (streq("locked", key)) {
- set_locked(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->locked));
- } else {
- fail(rsp, "node %s: Invalid key: '%s'.\n", *(args - 1), key);
- break;
- }
- } else if (streq("-p", *args) || streq("--presel-dir", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
- break;
- }
- if (trg.node == NULL || trg.node->vacant) {
- fail(rsp, "");
- break;
- }
- if (streq("cancel", *args)) {
- cancel_presel(trg.monitor, trg.desktop, trg.node);
- } else {
- bool alternate = false;
- if ((*args)[0] == '~') {
- alternate = true;
- (*args)++;
- }
- direction_t dir;
- if (parse_direction(*args, &dir)) {
- if (alternate && trg.node->presel != NULL && trg.node->presel->split_dir == dir) {
- cancel_presel(trg.monitor, trg.desktop, trg.node);
- } else {
- presel_dir(trg.monitor, trg.desktop, trg.node, dir);
- if (!IS_RECEPTACLE(trg.node)) {
- draw_presel_feedback(trg.monitor, trg.desktop, trg.node);
- }
- }
- } else {
- fail(rsp, "node %s: Invalid argument: '%s%s'.\n", *(args - 1), alternate?"~":"", *args);
- break;
- }
- }
- } else if (streq("-o", *args) || streq("--presel-ratio", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
- break;
- }
- if (trg.node == NULL || trg.node->vacant) {
- fail(rsp, "");
- break;
- }
- double rat;
- if (sscanf(*args, "%lf", &rat) != 1 || rat <= 0 || rat >= 1) {
- fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
- break;
- } else {
- presel_ratio(trg.monitor, trg.desktop, trg.node, rat);
- draw_presel_feedback(trg.monitor, trg.desktop, trg.node);
- }
- } else if (streq("-v", *args) || streq("--move", *args)) {
- num--, args++;
- if (num < 2) {
- fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
- break;
- }
- int dx = 0, dy = 0;
- if (sscanf(*args, "%i", &dx) == 1) {
- num--, args++;
- if (sscanf(*args, "%i", &dy) == 1) {
- if (!move_client(&trg, dx, dy)) {
- fail(rsp, "");
- break;
- }
- } else {
- fail(rsp, "node %s: Invalid dy argument: '%s'.\n", *(args - 3), *args);
- break;
- }
- } else {
- fail(rsp, "node %s: Invalid dx argument: '%s'.\n", *(args - 2), *args);
- break;
- }
- } else if (streq("-z", *args) || streq("--resize", *args)) {
- num--, args++;
- if (num < 3) {
- fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
- break;
- }
- resize_handle_t rh;
- if (parse_resize_handle(*args, &rh)) {
- num--, args++;
- int dx = 0, dy = 0;
- if (sscanf(*args, "%i", &dx) == 1) {
- num--, args++;
- if (sscanf(*args, "%i", &dy) == 1) {
- if (!resize_client(&trg, rh, dx, dy)) {
- fail(rsp, "");
- break;
- }
- } else {
- fail(rsp, "node %s: Invalid dy argument: '%s'.\n", *(args - 3), *args);
- break;
- }
- } else {
- fail(rsp, "node %s: Invalid dx argument: '%s'.\n", *(args - 2), *args);
- break;
- }
- } else {
- fail(rsp, "node %s: Invalid resize handle argument: '%s'.\n", *(args - 1), *args);
- break;
- }
- } else if (streq("-r", *args) || streq("--ratio", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
- break;
- }
- if (trg.node == NULL) {
- fail(rsp, "");
- break;
- }
- if ((*args)[0] == '+' || (*args)[0] == '-') {
- int pix;
- if (sscanf(*args, "%i", &pix) == 1) {
- int max = (trg.node->split_type == TYPE_HORIZONTAL ? trg.node->rectangle.height : trg.node->rectangle.width);
- double rat = ((max * trg.node->split_ratio) + pix) / max;
- if (rat > 0 && rat < 1) {
- set_ratio(trg.node, rat);
- } else {
- fail(rsp, "");
- break;
- }
- } else {
- fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
- break;
- }
- } else {
- double rat;
- if (sscanf(*args, "%lf", &rat) == 1 && rat > 0 && rat < 1) {
- set_ratio(trg.node, rat);
- } else {
- fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
- break;
- }
- }
- changed = true;
- } else if (streq("-F", *args) || streq("--flip", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
- break;
- }
- if (trg.node == NULL) {
- fail(rsp, "");
- break;
- }
- flip_t flp;
- if (parse_flip(*args, &flp)) {
- flip_tree(trg.node, flp);
- changed = true;
- } else {
- fail(rsp, "");
- break;
- }
- } else if (streq("-R", *args) || streq("--rotate", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
- break;
- }
- if (trg.node == NULL) {
- fail(rsp, "");
- break;
- }
- int deg;
- if (parse_degree(*args, °)) {
- rotate_tree(trg.node, deg);
- changed = true;
- } else {
- fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
- break;
- }
- } else if (streq("-E", *args) || streq("--equalize", *args)) {
- if (trg.node == NULL) {
- fail(rsp, "");
- break;
- }
- equalize_tree(trg.node);
- changed = true;
- } else if (streq("-B", *args) || streq("--balance", *args)) {
- if (trg.node == NULL) {
- fail(rsp, "");
- break;
- }
- balance_tree(trg.node);
- changed = true;
- } else if (streq("-C", *args) || streq("--circulate", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
- break;
- }
- if (trg.node == NULL) {
- fail(rsp, "");
- break;
- }
- circulate_dir_t cir;
- if (parse_circulate_direction(*args, &cir)) {
- circulate_leaves(trg.monitor, trg.desktop, trg.node, cir);
- changed = true;
- } else {
- fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
- break;
- }
- } else if (streq("-i", *args) || streq("--insert-receptacle", *args)) {
- insert_receptacle(trg.monitor, trg.desktop, trg.node);
- changed = true;
- } else if (streq("-c", *args) || streq("--close", *args)) {
- if (num > 1) {
- fail(rsp, "node %s: Trailing commands.\n", *args);
- break;
- }
- if (trg.node == NULL || locked_count(trg.node) > 0) {
- fail(rsp, "");
- break;
- }
- close_node(trg.node);
- break;
- } else if (streq("-k", *args) || streq("--kill", *args)) {
- if (num > 1) {
- fail(rsp, "node %s: Trailing commands.\n", *args);
- break;
- }
- if (trg.node == NULL) {
- fail(rsp, "");
- break;
- }
- kill_node(trg.monitor, trg.desktop, trg.node);
- changed = true;
- break;
- } else {
- fail(rsp, "node: Unknown command: '%s'.\n", *args);
- break;
- }
-
- num--, args++;
- }
-
- if (changed) {
- arrange(trg.monitor, trg.desktop);
- }
-}
-
-void cmd_desktop(char **args, int num, FILE *rsp)
-{
- if (num < 1) {
- fail(rsp, "desktop: Missing commands.\n");
- return;
- }
-
- coordinates_t ref = {mon, mon->desk, NULL};
- coordinates_t trg = ref;
-
- if ((*args)[0] != OPT_CHR) {
- int ret;
- if ((ret = desktop_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {
- num--, args++;
- } else {
- handle_failure(ret, "desktop", *args, rsp);
- return;
- }
- }
-
- bool changed = false;
-
- while (num > 0) {
- if (streq("-f", *args) || streq("--focus", *args)) {
- coordinates_t dst = trg;
- if (num > 1 && *(args + 1)[0] != OPT_CHR) {
- num--, args++;
- int ret;
- if ((ret = desktop_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
- handle_failure(ret, "desktop -f", *args, rsp);
- break;
- }
- }
- focus_node(dst.monitor, dst.desktop, dst.desktop->focus);
- } else if (streq("-a", *args) || streq("--activate", *args)) {
- coordinates_t dst = trg;
- if (num > 1 && *(args + 1)[0] != OPT_CHR) {
- num--, args++;
- int ret;
- if ((ret = desktop_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
- handle_failure(ret, "desktop -a", *args, rsp);
- break;
- }
- }
- if (!activate_desktop(dst.monitor, dst.desktop)) {
- fail(rsp, "");
- break;
- }
- } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
- break;
- }
- if (trg.monitor->desk_head == trg.monitor->desk_tail) {
- fail(rsp, "");
- break;
- }
- coordinates_t dst;
- int ret;
- if ((ret = monitor_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
- if (transfer_desktop(trg.monitor, dst.monitor, trg.desktop)) {
- trg.monitor = dst.monitor;
- } else {
- fail(rsp, "");
- break;
- }
- } else {
- handle_failure(ret, "desktop -m", *args, rsp);
- break;
- }
- } else if (streq("-s", *args) || streq("--swap", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
- break;
- }
- coordinates_t dst;
- int ret;
- if ((ret = desktop_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
- if (swap_desktops(trg.monitor, trg.desktop, dst.monitor, dst.desktop)) {
- trg.monitor = dst.monitor;
- } else {
- fail(rsp, "");
- break;
- }
- } else {
- handle_failure(ret, "desktop -s", *args, rsp);
- break;
- }
- } else if (streq("-b", *args) || streq("--bubble", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
- break;
- }
- cycle_dir_t cyc;
- if (parse_cycle_direction(*args, &cyc)) {
- desktop_t *d = trg.desktop;
- if (cyc == CYCLE_PREV) {
- if (d->prev == NULL) {
- while (d->next != NULL) {
- swap_desktops(trg.monitor, d, trg.monitor, d->next);
- }
- } else {
- swap_desktops(trg.monitor, d, trg.monitor, d->prev);
- }
- } else {
- if (d->next == NULL) {
- while (d->prev != NULL) {
- swap_desktops(trg.monitor, d, trg.monitor, d->prev);
- }
- } else {
- swap_desktops(trg.monitor, d, trg.monitor, d->next);
- }
- }
- } else {
- fail(rsp, "desktop %s: Invalid argument: '%s'.\n", *(args - 1), *args);
- break;
- }
- } else if (streq("-l", *args) || streq("--layout", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
- break;
- }
- bool ret;
- layout_t lyt;
- cycle_dir_t cyc;
- if (parse_cycle_direction(*args, &cyc)) {
- ret = set_layout(trg.monitor, trg.desktop, (trg.desktop->layout + 1) % 2);
- } else if (parse_layout(*args, &lyt)) {
- ret = set_layout(trg.monitor, trg.desktop, lyt);
- } else {
- fail(rsp, "desktop %s: Invalid argument: '%s'.\n", *(args - 1), *args);
- break;
- }
- if (!ret) {
- fail(rsp, "");
- break;
- }
- } else if (streq("-n", *args) || streq("--rename", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
- break;
- }
- rename_desktop(trg.monitor, trg.desktop, *args);
- } else if (streq("-r", *args) || streq("--remove", *args)) {
- if (num > 1) {
- fail(rsp, "desktop %s: Trailing commands.\n", *args);
- break;
- }
- if (trg.desktop->root == NULL &&
- trg.monitor->desk_head != trg.monitor->desk_tail) {
- remove_desktop(trg.monitor, trg.desktop);
- return;
- } else {
- fail(rsp, "");
- break;
- }
- } else {
- fail(rsp, "desktop: Unknown command: '%s'.\n", *args);
- break;
- }
- num--, args++;
- }
-
- if (changed) {
- arrange(trg.monitor, trg.desktop);
- }
-}
-
-void cmd_monitor(char **args, int num, FILE *rsp)
-{
- if (num < 1) {
- fail(rsp, "monitor: Missing commands.\n");
- return;
- }
-
- coordinates_t ref = {mon, NULL, NULL};
- coordinates_t trg = ref;
-
- if ((*args)[0] != OPT_CHR) {
- int ret;
- if ((ret = monitor_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {
- num--, args++;
- } else {
- handle_failure(ret, "monitor", *args, rsp);
- return;
- }
- }
-
- while (num > 0) {
- if (streq("-f", *args) || streq("--focus", *args)) {
- coordinates_t dst = trg;
- if (num > 1 && *(args + 1)[0] != OPT_CHR) {
- num--, args++;
- int ret;
- if ((ret = monitor_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
- handle_failure(ret, "monitor -f", *args, rsp);
- fail(rsp, "");
- return;
- }
- }
- focus_node(dst.monitor, dst.monitor->desk, dst.monitor->desk->focus);
- } else if (streq("-s", *args) || streq("--swap", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
- return;
- }
- coordinates_t dst;
- int ret;
- if ((ret = monitor_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
- if (!swap_monitors(trg.monitor, dst.monitor)) {
- fail(rsp, "");
- return;
- }
- } else {
- handle_failure(ret, "monitor -s", *args, rsp);
- return;
- }
- } else if (streq("-d", *args) || streq("--reset-desktops", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
- return;
- }
- desktop_t *d = trg.monitor->desk_head;
- while (num > 0 && d != NULL) {
- rename_desktop(trg.monitor, d, *args);
- d = d->next;
- num--, args++;
- }
- put_status(SBSC_MASK_REPORT);
- while (num > 0) {
- add_desktop(trg.monitor, make_desktop(*args, XCB_NONE));
- num--, args++;
- }
- while (d != NULL) {
- desktop_t *next = d->next;
- if (d == mon->desk) {
- focus_node(trg.monitor, d->prev, d->prev->focus);
- }
- merge_desktops(trg.monitor, d, mon, mon->desk);
- remove_desktop(trg.monitor, d);
- d = next;
- }
- } else if (streq("-a", *args) || streq("--add-desktops", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
- return;
- }
- while (num > 0) {
- add_desktop(trg.monitor, make_desktop(*args, XCB_NONE));
- num--, args++;
- }
- } else if (streq("-r", *args) || streq("--remove", *args)) {
- if (num > 1) {
- fail(rsp, "monitor %s: Trailing commands.\n", *args);
- return;
- }
- if (mon_head == mon_tail) {
- fail(rsp, "");
- return;
- }
- remove_monitor(trg.monitor);
- return;
- } else if (streq("-o", *args) || streq("--order-desktops", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
- return;
- }
- desktop_t *d = trg.monitor->desk_head;
- while (d != NULL && num > 0) {
- desktop_t *next = d->next;
- coordinates_t dst;
- if (locate_desktop(*args, &dst) && dst.monitor == trg.monitor) {
- swap_desktops(trg.monitor, d, dst.monitor, dst.desktop);
- if (next == dst.desktop) {
- next = d;
- }
- }
- d = next;
- num--, args++;
- }
- } else if (streq("-g", *args) || streq("--rectangle", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
- return;
- }
- xcb_rectangle_t r;
- if (parse_rectangle(*args, &r)) {
- update_root(trg.monitor, &r);
- } else {
- fail(rsp, "monitor %s: Invalid argument: '%s'.\n", *(args - 1), *args);
- return;
- }
- } else if (streq("-n", *args) || streq("--rename", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
- return;
- }
- rename_monitor(trg.monitor, *args);
- } else {
- fail(rsp, "monitor: Unknown command: '%s'.\n", *args);
- return;
- }
- num--, args++;
- }
-}
-
-void cmd_query(char **args, int num, FILE *rsp)
-{
- coordinates_t ref = {mon, mon->desk, mon->desk->focus};
- coordinates_t trg = {NULL, NULL, NULL};
- monitor_select_t *monitor_sel = NULL;
- desktop_select_t *desktop_sel = NULL;
- node_select_t *node_sel = NULL;
- domain_t dom = DOMAIN_TREE;
- bool print_ids = true;
- uint8_t d = 0;
-
- if (num < 1) {
- fail(rsp, "query: Not enough arguments.\n");
- return;
- }
-
- while (num > 0) {
- if (streq("-T", *args) || streq("--tree", *args)) {
- dom = DOMAIN_TREE, d++;
- } else if (streq("-M", *args) || streq("--monitors", *args)) {
- dom = DOMAIN_MONITOR, d++;
- if (num > 1 && *(args + 1)[0] != OPT_CHR) {
- num--, args++;
- int ret;
- coordinates_t tmp = ref;
- if ((ret = monitor_from_desc(*args, &tmp, &ref)) != SELECTOR_OK) {
- handle_failure(ret, "query -M", *args, rsp);
- goto end;
- }
- }
- } else if (streq("-D", *args) || streq("--desktops", *args)) {
- dom = DOMAIN_DESKTOP, d++;
- if (num > 1 && *(args + 1)[0] != OPT_CHR) {
- num--, args++;
- int ret;
- coordinates_t tmp = ref;
- if ((ret = desktop_from_desc(*args, &tmp, &ref)) != SELECTOR_OK) {
- handle_failure(ret, "query -D", *args, rsp);
- goto end;
- }
- }
- } else if (streq("-N", *args) || streq("--nodes", *args)) {
- dom = DOMAIN_NODE, d++;
- if (num > 1 && *(args + 1)[0] != OPT_CHR) {
- num--, args++;
- int ret;
- coordinates_t tmp = ref;
- if ((ret = node_from_desc(*args, &tmp, &ref)) != SELECTOR_OK) {
- handle_failure(ret, "query -N", *args, rsp);
- goto end;
- }
- }
- } else if (streq("-m", *args) || streq("--monitor", *args)) {
- if (num > 1 && *(args + 1)[0] != OPT_CHR) {
- num--, args++;
- int ret;
- if ((*args)[0] == '.') {
- free(monitor_sel);
- monitor_sel = malloc(sizeof(monitor_select_t));
- *monitor_sel = make_monitor_select();
- char *desc = copy_string(*args, strlen(*args));
- if (!parse_monitor_modifiers(desc, monitor_sel)) {
- handle_failure(SELECTOR_BAD_MODIFIERS, "query -m", *args, rsp);
- free(desc);
- goto end;
- }
- free(desc);
- } else if ((ret = monitor_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
- handle_failure(ret, "query -m", *args, rsp);
- goto end;
- }
- } else {
- trg.monitor = ref.monitor;
- }
- } else if (streq("-d", *args) || streq("--desktop", *args)) {
- if (num > 1 && *(args + 1)[0] != OPT_CHR) {
- num--, args++;
- int ret;
- if ((*args)[0] == '.') {
- free(desktop_sel);
- desktop_sel = malloc(sizeof(desktop_select_t));
- *desktop_sel = make_desktop_select();
- char *desc = copy_string(*args, strlen(*args));
- if (!parse_desktop_modifiers(desc, desktop_sel)) {
- handle_failure(SELECTOR_BAD_MODIFIERS, "query -d", *args, rsp);
- free(desc);
- goto end;
- }
- free(desc);
- } else if ((ret = desktop_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
- handle_failure(ret, "query -d", *args, rsp);
- goto end;
- }
- } else {
- trg.monitor = ref.monitor;
- trg.desktop = ref.desktop;
- }
- } else if (streq("-n", *args) || streq("--node", *args)) {
- if (num > 1 && *(args + 1)[0] != OPT_CHR) {
- num--, args++;
- int ret;
- if ((*args)[0] == '.') {
- free(node_sel);
- node_sel = malloc(sizeof(node_select_t));
- *node_sel = make_node_select();
- char *desc = copy_string(*args, strlen(*args));
- if (!parse_node_modifiers(desc, node_sel)) {
- handle_failure(SELECTOR_BAD_MODIFIERS, "query -n", *args, rsp);
- free(desc);
- goto end;
- }
- free(desc);
- } else if ((ret = node_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
- handle_failure(ret, "query -n", *args, rsp);
- goto end;
- }
- } else {
- trg = ref;
- if (ref.node == NULL) {
- fail(rsp, "");
- goto end;
- }
- }
- } else if (streq("--names", *args)) {
- print_ids = false;
- } else {
- fail(rsp, "query: Unknown option: '%s'.\n", *args);
- goto end;
- }
- num--, args++;
- }
-
- if (d < 1) {
- fail(rsp, "query: No commands given.\n");
- goto end;
- }
-
- if (d > 1) {
- fail(rsp, "query: Multiple commands given.\n");
- goto end;
- }
-
- if (dom == DOMAIN_TREE && trg.monitor == NULL) {
- fail(rsp, "query -T: No options given.\n");
- goto end;
- }
-
- if (dom == DOMAIN_NODE) {
- if (query_node_ids(&ref, &trg, node_sel, rsp) < 1) {
- fail(rsp, "");
- }
- } else if (dom == DOMAIN_DESKTOP) {
- if (query_desktop_ids(&ref, &trg, desktop_sel, print_ids ? fprint_desktop_id : fprint_desktop_name, rsp) < 1) {
- fail(rsp, "");
- }
- } else if (dom == DOMAIN_MONITOR) {
- if (query_monitor_ids(&ref, &trg, monitor_sel, print_ids ? fprint_monitor_id : fprint_monitor_name, rsp) < 1) {
- fail(rsp, "");
- }
- } else {
- if (trg.node != NULL) {
- query_node(trg.node, rsp);
- } else if (trg.desktop != NULL) {
- query_desktop(trg.desktop, rsp);
- } else {
- query_monitor(trg.monitor, rsp);
- }
- fprintf(rsp, "\n");
- }
-
-end:
- free(monitor_sel);
- free(desktop_sel);
- free(node_sel);
-}
-
-void cmd_rule(char **args, int num, FILE *rsp)
-{
- if (num < 1) {
- fail(rsp, "rule: Missing commands.\n");
- return;
- }
-
- while (num > 0) {
- if (streq("-a", *args) || streq("--add", *args)) {
- num--, args++;
- if (num < 2) {
- fail(rsp, "rule %s: Not enough arguments.\n", *(args - 1));
- return;
- }
- rule_t *rule = make_rule();
- char *class_name = strtok(*args, COL_TOK);
- char *instance_name = strtok(NULL, COL_TOK);
- snprintf(rule->class_name, sizeof(rule->class_name), "%s", class_name);
- snprintf(rule->instance_name, sizeof(rule->instance_name), "%s", instance_name==NULL?MATCH_ANY:instance_name);
- num--, args++;
- size_t i = 0;
- while (num > 0) {
- if (streq("-o", *args) || streq("--one-shot", *args)) {
- rule->one_shot = true;
- } else {
- for (size_t j = 0; i < sizeof(rule->effect) && j < strlen(*args); i++, j++) {
- rule->effect[i] = (*args)[j];
- }
- if (num > 1 && i < sizeof(rule->effect)) {
- rule->effect[i++] = ' ';
- }
- }
- num--, args++;
- }
- rule->effect[MIN(i, sizeof(rule->effect) - 1)] = '\0';
- add_rule(rule);
- } else if (streq("-r", *args) || streq("--remove", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "rule %s: Not enough arguments.\n", *(args - 1));
- return;
- }
- uint16_t idx;
- while (num > 0) {
- if (parse_index(*args, &idx)) {
- remove_rule_by_index(idx - 1);
- } else if (streq("tail", *args)) {
- remove_rule(rule_tail);
- } else if (streq("head", *args)) {
- remove_rule(rule_head);
- } else {
- remove_rule_by_cause(*args);
- }
- num--, args++;
- }
- } else if (streq("-l", *args) || streq("--list", *args)) {
- list_rules(rsp);
- } else {
- fail(rsp, "rule: Unknown command: '%s'.\n", *args);
- return;
- }
- num--, args++;
- }
-}
-
-void cmd_wm(char **args, int num, FILE *rsp)
-{
- if (num < 1) {
- fail(rsp, "wm: Missing commands.\n");
- return;
- }
-
- while (num > 0) {
- if (streq("-d", *args) || streq("--dump-state", *args)) {
- query_tree(rsp);
- fprintf(rsp, "\n");
- } else if (streq("-l", *args) || streq("--load-state", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "wm %s: Not enough arguments.\n", *(args - 1));
- break;
- }
- if (!restore_tree(*args)) {
- fail(rsp, "");
- break;
- }
- } else if (streq("-a", *args) || streq("--add-monitor", *args)) {
- num--, args++;
- if (num < 2) {
- fail(rsp, "wm %s: Not enough arguments.\n", *(args - 1));
- break;
- }
- char *name = *args;
- num--, args++;
- xcb_rectangle_t r;
- if (parse_rectangle(*args, &r)) {
- monitor_t *m = make_monitor(name, &r, XCB_NONE);
- add_monitor(m);
- add_desktop(m, make_desktop(NULL, XCB_NONE));
- } else {
- fail(rsp, "wm %s: Invalid argument: '%s'.\n", *(args - 1), *args);
- break;
- }
- } else if (streq("-o", *args) || streq("--adopt-orphans", *args)) {
- adopt_orphans();
- } else if (streq("-g", *args) || streq("--get-status", *args)) {
- print_report(rsp);
- } else if (streq("-h", *args) || streq("--record-history", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "wm %s: Not enough arguments.\n", *(args - 1));
- break;
- }
- bool b;
- if (parse_bool(*args, &b)) {
- record_history = b;
- } else {
- fail(rsp, "wm %s: Invalid argument: '%s'.\n", *(args - 1), *args);
- break;
- }
- } else {
- fail(rsp, "wm: Unkown command: '%s'.\n", *args);
- break;
- }
- num--, args++;
- }
-}
-
-int cmd_subscribe(char **args, int num, FILE *rsp)
-{
- int field = 0;
- if (num < 1) {
- field = SBSC_MASK_REPORT;
- } else {
- subscriber_mask_t mask;
- while (num > 0) {
- if (parse_subscriber_mask(*args, &mask)) {
- field |= mask;
- } else {
- fail(rsp, "subscribe: Invalid argument: '%s'.\n", *args);
- return SUBSCRIBE_FAILURE;
- }
- num--, args++;
- }
- }
-
- add_subscriber(rsp, field);
- return SUBSCRIBE_SUCCESS;
-}
-
-void cmd_quit(char **args, int num, FILE *rsp)
-{
- if (num > 0 && sscanf(*args, "%i", &exit_status) != 1) {
- fail(rsp, "%s: Invalid argument: '%s'.\n", *(args - 1), *args);
- return;
- }
- running = false;
-}
-
-void cmd_config(char **args, int num, FILE *rsp)
-{
- if (num < 1) {
- fail(rsp, "config: Missing arguments.\n");
- return;
- }
-
- coordinates_t ref = {mon, mon->desk, mon->desk->focus};
- coordinates_t trg = {NULL, NULL, NULL};
-
- while (num > 0 && (*args)[0] == OPT_CHR) {
- if (streq("-m", *args) || streq("--monitor", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "config %s: Not enough arguments.\n", *(args - 1));
- return;
- }
- int ret;
- if ((ret = monitor_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
- handle_failure(ret, "config -m", *args, rsp);
- return;
- }
- } else if (streq("-d", *args) || streq("--desktop", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "config %s: Not enough arguments.\n", *(args - 1));
- return;
- }
- int ret;
- if ((ret = desktop_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
- handle_failure(ret, "config -d", *args, rsp);
- return;
- }
- } else if (streq("-n", *args) || streq("--node", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "config %s: Not enough arguments.\n", *(args - 1));
- return;
- }
- int ret;
- if ((ret = node_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
- handle_failure(ret, "config -n", *args, rsp);
- return;
- }
- } else {
- fail(rsp, "config: Unknown option: '%s'.\n", *args);
- return;
- }
- num--, args++;
- }
- if (num == 2) {
- set_setting(trg, *args, *(args + 1), rsp);
- } else if (num == 1) {
- get_setting(trg, *args, rsp);
- } else {
- fail(rsp, "config: Was expecting 1 or 2 arguments, received %i.\n", num);
- }
-}
-
-void set_setting(coordinates_t loc, char *name, char *value, FILE *rsp)
-{
- bool colors_changed = false;
-#define SET_DEF_DEFMON_DEFDESK_WIN(k, v) \
- if (loc.node != NULL) { \
- for (node_t *n = first_extrema(loc.node); n != NULL; n = next_leaf(n, loc.node)) { \
- if (n->client != NULL) { \
- n->client->k = v; \
- } \
- } \
- } else if (loc.desktop != NULL) { \
- loc.desktop->k = v; \
- for (node_t *n = first_extrema(loc.desktop->root); n != NULL; n = next_leaf(n, loc.desktop->root)) { \
- if (n->client != NULL) { \
- n->client->k = v; \
- } \
- } \
- } else if (loc.monitor != NULL) { \
- loc.monitor->k = v; \
- for (desktop_t *d = loc.monitor->desk_head; d != NULL; d = d->next) { \
- d->k = v; \
- for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) { \
- if (n->client != NULL) { \
- n->client->k = v; \
- } \
- } \
- } \
- } else { \
- k = v; \
- for (monitor_t *m = mon_head; m != NULL; m = m->next) { \
- m->k = v; \
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { \
- d->k = v; \
- for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) { \
- if (n->client != NULL) { \
- n->client->k = v; \
- } \
- } \
- } \
- } \
- }
- if (streq("border_width", name)) {
- unsigned int bw;
- if (sscanf(value, "%u", &bw) != 1) {
- fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
- return;
- }
- SET_DEF_DEFMON_DEFDESK_WIN(border_width, bw)
-#undef SET_DEF_DEFMON_DEFDESK_WIN
-#define SET_DEF_DEFMON_DESK(k, v) \
- if (loc.desktop != NULL) { \
- loc.desktop->k = v; \
- } else if (loc.monitor != NULL) { \
- loc.monitor->k = v; \
- for (desktop_t *d = loc.monitor->desk_head; d != NULL; d = d->next) { \
- d->k = v; \
- } \
- } else { \
- k = v; \
- for (monitor_t *m = mon_head; m != NULL; m = m->next) { \
- m->k = v; \
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { \
- d->k = v; \
- } \
- } \
- }
- } else if (streq("window_gap", name)) {
- int wg;
- if (sscanf(value, "%i", &wg) != 1) {
- fail(rsp, "");
- return;
- }
- SET_DEF_DEFMON_DESK(window_gap, wg)
-#undef SET_DEF_DEFMON_DESK
-#define SET_DEF_MON_DESK(k, v) \
- if (loc.desktop != NULL) { \
- loc.desktop->k = v; \
- } else if (loc.monitor != NULL) { \
- loc.monitor->k = v; \
- } else { \
- k = v; \
- for (monitor_t *m = mon_head; m != NULL; m = m->next) { \
- m->k = v; \
- } \
- }
- } else if (streq("top_padding", name)) {
- int tp;
- if (sscanf(value, "%i", &tp) != 1) {
- fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
- return;
- }
- SET_DEF_MON_DESK(padding.top, tp)
- } else if (streq("right_padding", name)) {
- int rp;
- if (sscanf(value, "%i", &rp) != 1) {
- fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
- return;
- }
- SET_DEF_MON_DESK(padding.right, rp)
- } else if (streq("bottom_padding", name)) {
- int bp;
- if (sscanf(value, "%i", &bp) != 1) {
- fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
- return;
- }
- SET_DEF_MON_DESK(padding.bottom, bp)
- } else if (streq("left_padding", name)) {
- int lp;
- if (sscanf(value, "%i", &lp) != 1) {
- fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
- return;
- }
- SET_DEF_MON_DESK(padding.left, lp)
-#undef SET_DEF_MON_DESK
-#define SET_STR(s) \
- } else if (streq(#s, name)) { \
- if (snprintf(s, sizeof(s), "%s", value) < 0) { \
- fail(rsp, ""); \
- return; \
- }
- SET_STR(external_rules_command)
- SET_STR(status_prefix)
-#undef SET_STR
- } else if (streq("split_ratio", name)) {
- double r;
- if (sscanf(value, "%lf", &r) == 1 && r > 0 && r < 1) {
- split_ratio = r;
- } else {
- fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
- return;
- }
- return;
-#define SET_COLOR(s) \
- } else if (streq(#s, name)) { \
- if (!is_hex_color(value)) { \
- fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
- return; \
- } else { \
- snprintf(s, sizeof(s), "%s", value); \
- colors_changed = true; \
- }
- SET_COLOR(normal_border_color)
- SET_COLOR(active_border_color)
- SET_COLOR(focused_border_color)
- SET_COLOR(presel_feedback_color)
-#undef SET_COLOR
- } else if (streq("initial_polarity", name)) {
- child_polarity_t p;
- if (parse_child_polarity(value, &p)) {
- initial_polarity = p;
- } else {
- fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
- return;
- }
- } else if (streq("pointer_modifier", name)) {
- if (parse_modifier_mask(value, &pointer_modifier)) {
- ungrab_buttons();
- grab_buttons();
- } else {
- fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
- return;
- }
- } else if (streq("pointer_motion_interval", name)) {
- if (sscanf(value, "%u", &pointer_motion_interval) != 1) {
- fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
- return;
- }
- } else if (streq("pointer_action1", name) ||
- streq("pointer_action2", name) ||
- streq("pointer_action3", name)) {
- int index = name[14] - '1';
- if (parse_pointer_action(value, &pointer_actions[index])) {
- ungrab_buttons();
- grab_buttons();
- } else {
- fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
- return;
- }
- } else if (streq("click_to_focus", name)) {
- if (parse_bool(value, &click_to_focus)) {
- ungrab_buttons();
- grab_buttons();
- } else {
- fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
- return;
- }
- } else if (streq("focus_follows_pointer", name)) {
- bool b;
- if (parse_bool(value, &b)) {
- if (b == focus_follows_pointer) {
- fail(rsp, "");
- return;
- }
- focus_follows_pointer = b;
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- if (b) {
- window_show(m->root);
- } else {
- window_hide(m->root);
- }
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
- listen_enter_notify(d->root, b);
- }
- }
- return;
- } else {
- fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
- return;
- }
-#define SET_BOOL(s) \
- } else if (streq(#s, name)) { \
- if (!parse_bool(value, &s)) { \
- fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
- return; \
- }
- SET_BOOL(borderless_monocle)
- SET_BOOL(gapless_monocle)
- SET_BOOL(paddingless_monocle)
- SET_BOOL(single_monocle)
- SET_BOOL(swallow_first_click)
- SET_BOOL(pointer_follows_focus)
- SET_BOOL(pointer_follows_monitor)
- SET_BOOL(ignore_ewmh_focus)
- SET_BOOL(center_pseudo_tiled)
- SET_BOOL(honor_size_hints)
-#undef SET_BOOL
-#define SET_MON_BOOL(s) \
- } else if (streq(#s, name)) { \
- if (!parse_bool(value, &s)) { \
- fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
- return; \
- } \
- if (s) { \
- update_monitors(); \
- }
- SET_MON_BOOL(remove_disabled_monitors)
- SET_MON_BOOL(remove_unplugged_monitors)
- SET_MON_BOOL(merge_overlapping_monitors)
-#undef SET_MON_BOOL
- } else {
- fail(rsp, "config: Unknown setting: '%s'.\n", name);
- return;
- }
-
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
- arrange(m, d);
- if (colors_changed) {
- update_colors_in(d->root, d, m);
- }
- }
- }
-}
-
-void get_setting(coordinates_t loc, char *name, FILE* rsp)
-{
- if (streq("split_ratio", name)) {
- fprintf(rsp, "%lf", split_ratio);
- } else if (streq("border_width", name)) {
- if (loc.node != NULL) {
- for (node_t *n = first_extrema(loc.node); n != NULL; n = next_leaf(n, loc.node)) {
- if (n->client != NULL) {
- fprintf(rsp, "%u", n->client->border_width);
- break;
- }
- }
- } else if (loc.desktop != NULL) {
- fprintf(rsp, "%u", loc.desktop->border_width);
- } else if (loc.monitor != NULL) {
- fprintf(rsp, "%u", loc.monitor->border_width);
- } else {
- fprintf(rsp, "%u", border_width);
- }
- } else if (streq("window_gap", name)) {
- if (loc.desktop != NULL) {
- fprintf(rsp, "%i", loc.desktop->window_gap);
- } else if (loc.monitor != NULL) {
- fprintf(rsp, "%i", loc.monitor->window_gap);
- } else {
- fprintf(rsp, "%i", window_gap);
- }
-#define GET_DEF_MON_DESK(k) \
- if (loc.desktop != NULL) { \
- fprintf(rsp, "%i", loc.desktop->k); \
- } else if (loc.monitor != NULL) { \
- fprintf(rsp, "%i", loc.monitor->k); \
- } else { \
- fprintf(rsp, "%i", k); \
- }
- } else if (streq("top_padding", name)) {
- GET_DEF_MON_DESK(padding.top)
- } else if (streq("right_padding", name)) {
- GET_DEF_MON_DESK(padding.right)
- } else if (streq("bottom_padding", name)) {
- GET_DEF_MON_DESK(padding.bottom)
- } else if (streq("left_padding", name)) {
- GET_DEF_MON_DESK(padding.left)
-#undef GET_DEF_MON_DESK
- } else if (streq("external_rules_command", name)) {
- fprintf(rsp, "%s", external_rules_command);
- } else if (streq("status_prefix", name)) {
- fprintf(rsp, "%s", status_prefix);
- } else if (streq("initial_polarity", name)) {
- fprintf(rsp, "%s", CHILD_POL_STR(initial_polarity));
- } else if (streq("pointer_modifier", name)) {
- print_modifier_mask(pointer_modifier, rsp);
- } else if (streq("pointer_motion_interval", name)) {
- fprintf(rsp, "%u", pointer_motion_interval);
- } else if (streq("pointer_action1", name) ||
- streq("pointer_action2", name) ||
- streq("pointer_action3", name)) {
- int index = name[14] - '1';
- print_pointer_action(pointer_actions[index], rsp);
-#define GET_COLOR(s) \
- } else if (streq(#s, name)) { \
- fprintf(rsp, "%s", s);
- GET_COLOR(normal_border_color)
- GET_COLOR(active_border_color)
- GET_COLOR(focused_border_color)
- GET_COLOR(presel_feedback_color)
-#undef GET_COLOR
-#define GET_BOOL(s) \
- } else if (streq(#s, name)) { \
- fprintf(rsp, "%s", BOOL_STR(s));
- GET_BOOL(borderless_monocle)
- GET_BOOL(gapless_monocle)
- GET_BOOL(paddingless_monocle)
- GET_BOOL(single_monocle)
- GET_BOOL(click_to_focus)
- GET_BOOL(swallow_first_click)
- GET_BOOL(focus_follows_pointer)
- GET_BOOL(pointer_follows_focus)
- GET_BOOL(pointer_follows_monitor)
- GET_BOOL(ignore_ewmh_focus)
- GET_BOOL(center_pseudo_tiled)
- GET_BOOL(honor_size_hints)
- GET_BOOL(remove_disabled_monitors)
- GET_BOOL(remove_unplugged_monitors)
- GET_BOOL(merge_overlapping_monitors)
-#undef GET_BOOL
- } else {
- fail(rsp, "config: Unknown setting: '%s'.\n", name);
- return;
- }
- fprintf(rsp, "\n");
-}
-
-void handle_failure(int code, char *src, char *val, FILE *rsp)
-{
- switch (code) {
- case SELECTOR_BAD_DESCRIPTOR:
- fail(rsp, "%s: Invalid descriptor found in '%s'.\n", src, val);
- break;
- case SELECTOR_BAD_MODIFIERS:
- fail(rsp, "%s: Invalid modifier found in '%s'.\n", src, val);
- break;
- case SELECTOR_INVALID:
- fail(rsp, "");
- break;
- }
-}
-
-void fail(FILE *rsp, char *fmt, ...)
-{
- fprintf(rsp, FAILURE_MESSAGE);
- va_list ap;
- va_start(ap, fmt);
- vfprintf(rsp, fmt, ap);
- va_end(ap);
-}
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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.
- */
-
-#ifndef BSPWM_MESSAGES_H
-#define BSPWM_MESSAGES_H
-
-#include "types.h"
-#include "subscribe.h"
-
-enum {
- SUBSCRIBE_SUCCESS,
- SUBSCRIBE_FAILURE
-};
-
-void handle_message(char *msg, int msg_len, FILE *rsp);
-void process_message(char **args, int num, FILE *rsp);
-void cmd_node(char **args, int num, FILE *rsp);
-void cmd_desktop(char **args, int num, FILE *rsp);
-void cmd_monitor(char **args, int num, FILE *rsp);
-void cmd_query(char **args, int num, FILE *rsp);
-void cmd_rule(char **args, int num, FILE *rsp);
-void cmd_wm(char **args, int num, FILE *rsp);
-int cmd_subscribe(char **args, int num, FILE *rsp);
-void cmd_quit(char **args, int num, FILE *rsp);
-void cmd_config(char **args, int num, FILE *rsp);
-void set_setting(coordinates_t loc, char *name, char *value, FILE *rsp);
-void get_setting(coordinates_t loc, char *name, FILE* rsp);
-void handle_failure(int code, char *src, char *val, FILE *rsp);
-void fail(FILE *rsp, char *fmt, ...);
-
-#endif
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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 <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdbool.h>
-#include "bspwm.h"
-#include "desktop.h"
-#include "ewmh.h"
-#include "query.h"
-#include "pointer.h"
-#include "settings.h"
-#include "geometry.h"
-#include "tree.h"
-#include "subscribe.h"
-#include "window.h"
-#include "monitor.h"
-
-monitor_t *make_monitor(const char *name, xcb_rectangle_t *rect, uint32_t id)
-{
- monitor_t *m = calloc(1, sizeof(monitor_t));
- if (id == XCB_NONE) {
- m->id = xcb_generate_id(dpy);
- }
- m->randr_id = XCB_NONE;
- snprintf(m->name, sizeof(m->name), "%s", name == NULL ? DEFAULT_MON_NAME : name);
- m->padding = padding;
- m->border_width = border_width;
- m->window_gap = window_gap;
- m->root = XCB_NONE;
- m->prev = m->next = NULL;
- m->desk = m->desk_head = m->desk_tail = NULL;
- m->wired = true;
- m->sticky_count = 0;
- if (rect != NULL) {
- update_root(m, rect);
- } else {
- m->rectangle = (xcb_rectangle_t) {0, 0, screen_width, screen_height};
- }
- return m;
-}
-
-void update_root(monitor_t *m, xcb_rectangle_t *rect)
-{
- xcb_rectangle_t last_rect = m->rectangle;
- m->rectangle = *rect;
- if (m->root == XCB_NONE) {
- 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, XCB_CW_EVENT_MASK, values);
- xcb_icccm_set_wm_class(dpy, m->root, sizeof(ROOT_WINDOW_IC), ROOT_WINDOW_IC);
- xcb_icccm_set_wm_name(dpy, m->root, XCB_ATOM_STRING, 8, strlen(m->name), m->name);
- window_lower(m->root);
- if (focus_follows_pointer) {
- window_show(m->root);
- }
- } else {
- window_move_resize(m->root, rect->x, rect->y, rect->width, rect->height);
- put_status(SBSC_MASK_MONITOR_GEOMETRY, "monitor_geometry 0x%08X %ux%u+%i+%i\n",
- m->id, rect->width, rect->height, rect->x, rect->y);
- }
- 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)) {
- if (n->client == NULL) {
- continue;
- }
- adapt_geometry(&last_rect, rect, n);
- }
- arrange(m, d);
- }
-}
-
-void rename_monitor(monitor_t *m, const char *name)
-{
- put_status(SBSC_MASK_MONITOR_RENAME, "monitor_rename 0x%08X %s %s\n", m->id, m->name, name);
-
- snprintf(m->name, sizeof(m->name), "%s", name);
- xcb_icccm_set_wm_name(dpy, m->root, XCB_ATOM_STRING, 8, strlen(m->name), m->name);
-
- put_status(SBSC_MASK_REPORT);
-}
-
-monitor_t *find_monitor(uint32_t id)
-{
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- if (m->id == id) {
- return m;
- }
- }
- return NULL;
-}
-
-monitor_t *get_monitor_by_randr_id(xcb_randr_output_t id)
-{
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- if (m->randr_id == id) {
- return m;
- }
- }
- 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 adapt_geometry(xcb_rectangle_t *rs, xcb_rectangle_t *rd, node_t *n)
-{
- for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
- if (f->client == NULL) {
- continue;
- }
- 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;
- }
-}
-
-void focus_monitor(monitor_t *m)
-{
- if (mon == m) {
- return;
- }
-
- mon = m;
-
- if (pointer_follows_monitor) {
- center_pointer(m->rectangle);
- }
-
- put_status(SBSC_MASK_MONITOR_FOCUS, "monitor_focus 0x%08X\n", m->id);
-}
-
-void add_monitor(monitor_t *m)
-{
- xcb_rectangle_t r = m->rectangle;
-
- if (mon == NULL) {
- mon = m;
- mon_head = m;
- mon_tail = m;
- } else {
- monitor_t *a = mon_head;
- while (a != NULL && rect_cmp(m->rectangle, a->rectangle) > 0) {
- 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;
- }
- }
-
- 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);
-
- put_status(SBSC_MASK_REPORT);
-}
-
-void unlink_monitor(monitor_t *m)
-{
- monitor_t *prev = m->prev;
- monitor_t *next = m->next;
-
- if (prev != NULL) {
- prev->next = next;
- }
-
- if (next != NULL) {
- next->prev = prev;
- }
-
- if (mon_head == m) {
- mon_head = next;
- }
-
- if (mon_tail == m) {
- mon_tail = prev;
- }
-
- if (pri_mon == m) {
- pri_mon = NULL;
- }
-
- if (mon == m) {
- mon = NULL;
- }
-}
-
-void remove_monitor(monitor_t *m)
-{
- put_status(SBSC_MASK_MONITOR_REMOVE, "monitor_remove 0x%08X\n", m->id);
-
- while (m->desk_head != NULL) {
- remove_desktop(m, m->desk_head);
- }
-
- monitor_t *last_mon = mon;
-
- unlink_monitor(m);
- xcb_destroy_window(dpy, m->root);
- free(m);
-
- if (mon != last_mon) {
- focus_node(NULL, NULL, NULL);
- }
-
- put_status(SBSC_MASK_REPORT);
-}
-
-void merge_monitors(monitor_t *ms, monitor_t *md)
-{
- if (ms == NULL || md == NULL || ms == md) {
- return;
- }
-
- desktop_t *d = ms->desk_head;
- while (d != NULL) {
- desktop_t *next = d->next;
- transfer_desktop(ms, md, d);
- d = next;
- }
-}
-
-bool swap_monitors(monitor_t *m1, monitor_t *m2)
-{
- if (m1 == NULL || m2 == NULL || m1 == m2) {
- return false;
- }
-
- put_status(SBSC_MASK_MONITOR_SWAP, "monitor_swap 0x%08X 0x%08X\n", m1->id, m2->id);
-
- if (mon_head == m1) {
- mon_head = m2;
- } else if (mon_head == m2) {
- mon_head = m1;
- }
- if (mon_tail == m1) {
- mon_tail = m2;
- } else if (mon_tail == m2) {
- mon_tail = m1;
- }
-
- monitor_t *p1 = m1->prev;
- monitor_t *n1 = m1->next;
- monitor_t *p2 = m2->prev;
- monitor_t *n2 = m2->next;
-
- if (p1 != NULL && p1 != m2) {
- p1->next = m2;
- }
- if (n1 != NULL && n1 != m2) {
- n1->prev = m2;
- }
- if (p2 != NULL && p2 != m1) {
- p2->next = m1;
- }
- if (n2 != NULL && n2 != m1) {
- n2->prev = m1;
- }
-
- 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_desktop_viewport();
- ewmh_update_current_desktop();
-
- put_status(SBSC_MASK_REPORT);
- return true;
-}
-
-monitor_t *closest_monitor(monitor_t *m, cycle_dir_t dir, monitor_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 = {f, NULL, NULL};
- if (monitor_matches(&loc, &loc, sel)) {
- return f;
- }
- f = (dir == CYCLE_PREV ? f->prev : f->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)
-{
- return is_inside(pt, m->rectangle);
-}
-
-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)
-{
- 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};
- 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;
- int d = abs((r.x + r.width / 2) - xc) + abs((r.y + r.height / 2) - yc);
- if (d < dmin) {
- dmin = d;
- nearest = m;
- }
- }
- }
- return nearest;
-}
-
-monitor_t *nearest_monitor(monitor_t *m, direction_t dir, monitor_select_t sel)
-{
- double dmin = DBL_MAX;
- monitor_t *nearest = NULL;
- xcb_rectangle_t rect = m->rectangle;
- for (monitor_t *f = mon_head; f != NULL; f = f->next) {
- coordinates_t loc = {f, NULL, NULL};
- xcb_rectangle_t r = f->rectangle;
- if (f == m ||
- !monitor_matches(&loc, &loc, sel) ||
- !on_dir_side(rect, r, dir)) {
- continue;
- }
- double d = distance_center(rect, r);
- if (d < dmin) {
- dmin = d;
- nearest = f;
- }
- }
- return nearest;
-}
-
-bool update_monitors(void)
-{
- xcb_randr_get_screen_resources_reply_t *sres = xcb_randr_get_screen_resources_reply(dpy, xcb_randr_get_screen_resources(dpy, root), NULL);
- if (sres == NULL) {
- return false;
- }
-
- monitor_t *m, *mm = NULL;
-
- int len = xcb_randr_get_screen_resources_outputs_length(sres);
- xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_outputs(sres);
-
- xcb_randr_get_output_info_cookie_t cookies[len];
- for (int i = 0; i < len; i++) {
- cookies[i] = xcb_randr_get_output_info(dpy, outputs[i], XCB_CURRENT_TIME);
- }
-
- for (m = mon_head; m != NULL; m = m->next) {
- m->wired = false;
- }
-
- 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};
- mm = get_monitor_by_randr_id(outputs[i]);
- if (mm != NULL) {
- update_root(mm, &rect);
- mm->wired = true;
- } else {
- char *name = (char *) xcb_randr_get_output_info_name(info);
- size_t len = (size_t) xcb_randr_get_output_info_name_length(info);
- char *name_copy = copy_string(name, len);
- mm = make_monitor(name_copy, &rect, XCB_NONE);
- free(name_copy);
- mm->randr_id = outputs[i];
- add_monitor(mm);
- }
- }
- free(cir);
- } else if (!remove_disabled_monitors && info->connection != XCB_RANDR_CONNECTION_DISCONNECTED) {
- m = get_monitor_by_randr_id(outputs[i]);
- if (m != NULL) {
- m->wired = true;
- }
- }
- }
- 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) {
- pri_mon = get_monitor_by_randr_id(gpo->output);
- }
- free(gpo);
-
- /* handle overlapping monitors */
- if (merge_overlapping_monitors) {
- m = mon_head;
- while (m != NULL) {
- monitor_t *next = m->next;
- if (m->wired) {
- 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) {
- mm = m;
- }
- if (next == mb) {
- next = mb_next;
- }
- merge_monitors(mb, m);
- remove_monitor(mb);
- }
- mb = mb_next;
- }
- }
- m = next;
- }
- }
-
- /* merge and remove disconnected monitors */
- if (remove_unplugged_monitors) {
- m = mon_head;
- while (m != NULL) {
- monitor_t *next = m->next;
- if (!m->wired) {
- 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) {
- add_desktop(m, make_desktop(NULL, XCB_NONE));
- }
- }
-
- if (!running && mon != NULL) {
- if (pri_mon != NULL) {
- mon = pri_mon;
- }
- center_pointer(mon->rectangle);
- ewmh_update_current_desktop();
- }
-
- free(sres);
-
- return (mon != NULL);
-}
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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.
- */
-
-#ifndef BSPWM_MONITOR_H
-#define BSPWM_MONITOR_H
-
-#define DEFAULT_MON_NAME "MONITOR"
-
-monitor_t *make_monitor(const char *name, xcb_rectangle_t *rect, uint32_t id);
-void update_root(monitor_t *m, xcb_rectangle_t *rect);
-void rename_monitor(monitor_t *m, const char *name);
-monitor_t *find_monitor(uint32_t id);
-monitor_t *get_monitor_by_randr_id(xcb_randr_output_t id);
-void embrace_client(monitor_t *m, client_t *c);
-void adapt_geometry(xcb_rectangle_t *rs, xcb_rectangle_t *rd, node_t *n);
-void focus_monitor(monitor_t *m);
-void add_monitor(monitor_t *m);
-void unlink_monitor(monitor_t *m);
-void remove_monitor(monitor_t *m);
-void merge_monitors(monitor_t *ms, monitor_t *md);
-bool swap_monitors(monitor_t *m1, monitor_t *m2);
-monitor_t *closest_monitor(monitor_t *m, cycle_dir_t dir, monitor_select_t sel);
-bool is_inside_monitor(monitor_t *m, xcb_point_t pt);
-monitor_t *monitor_from_point(xcb_point_t pt);
-monitor_t *monitor_from_client(client_t *c);
-monitor_t *nearest_monitor(monitor_t *m, direction_t dir, monitor_select_t sel);
-bool update_monitors(void);
-
-#endif
+++ /dev/null
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdbool.h>
-#include <errno.h>
-#include "parse.h"
-
-bool parse_bool(char *value, bool *b)
-{
- if (streq("true", value) || streq("on", value)) {
- *b = true;
- return true;
- } else if (streq("false", value) || streq("off", value)) {
- *b = false;
- return true;
- }
- return false;
-}
-
-bool parse_split_type(char *s, split_type_t *t)
-{
- if (streq("horizontal", s)) {
- *t = TYPE_HORIZONTAL;
- return true;
- } else if (streq("vertical", s)) {
- *t = TYPE_VERTICAL;
- return true;
- }
- return false;
-}
-
-bool parse_split_mode(char *s, split_mode_t *m)
-{
- if (streq("automatic", s)) {
- *m = MODE_AUTOMATIC;
- return true;
- } else if (streq("vertical", s)) {
- *m = MODE_MANUAL;
- return true;
- }
- return false;
-}
-
-bool parse_layout(char *s, layout_t *l)
-{
- if (streq("monocle", s)) {
- *l = LAYOUT_MONOCLE;
- return true;
- } else if (streq("tiled", s)) {
- *l = LAYOUT_TILED;
- return true;
- }
- return false;
-}
-
-bool parse_client_state(char *s, client_state_t *t)
-{
- if (streq("tiled", s)) {
- *t = STATE_TILED;
- return true;
- } else if (streq("pseudo_tiled", s)) {
- *t = STATE_PSEUDO_TILED;
- return true;
- } else if (streq("floating", s)) {
- *t = STATE_FLOATING;
- return true;
- } else if (streq("fullscreen", s)) {
- *t = STATE_FULLSCREEN;
- return true;
- }
- return false;
-}
-
-bool parse_stack_layer(char *s, stack_layer_t *l)
-{
- if (streq("below", s)) {
- *l = LAYER_BELOW;
- return true;
- } else if (streq("normal", s)) {
- *l = LAYER_NORMAL;
- return true;
- } else if (streq("above", s)) {
- *l = LAYER_ABOVE;
- return true;
- }
- return false;
-}
-
-bool parse_direction(char *s, direction_t *d)
-{
- if (streq("north", s)) {
- *d = DIR_NORTH;
- return true;
- } else if (streq("west", s)) {
- *d = DIR_WEST;
- return true;
- } else if (streq("south", s)) {
- *d = DIR_SOUTH;
- return true;
- } else if (streq("east", s)) {
- *d = DIR_EAST;
- return true;
- }
- return false;
-}
-
-bool parse_cycle_direction(char *s, cycle_dir_t *d)
-{
- if (streq("next", s)) {
- *d = CYCLE_NEXT;
- return true;
- } else if (streq("prev", s)) {
- *d = CYCLE_PREV;
- return true;
- }
- return false;
-}
-
-bool parse_circulate_direction(char *s, circulate_dir_t *d)
-{
- if (streq("forward", s)) {
- *d = CIRCULATE_FORWARD;
- return true;
- } else if (streq("backward", s)) {
- *d = CIRCULATE_BACKWARD;
- return true;
- }
- return false;
-}
-
-bool parse_history_direction(char *s, history_dir_t *d)
-{
- if (streq("older", s)) {
- *d = HISTORY_OLDER;
- return true;
- } else if (streq("newer", s)) {
- *d = HISTORY_NEWER;
- return true;
- }
- return false;
-}
-
-
-bool parse_flip(char *s, flip_t *f)
-{
- if (streq("horizontal", s)) {
- *f = FLIP_HORIZONTAL;
- return true;
- } else if (streq("vertical", s)) {
- *f = FLIP_VERTICAL;
- return true;
- }
- return false;
-}
-
-bool parse_resize_handle(char *s, resize_handle_t *h)
-{
- if (streq("left", s)) {
- *h = HANDLE_LEFT;
- return true;
- } else if (streq("top", s)) {
- *h = HANDLE_TOP;
- return true;
- } else if (streq("right", s)) {
- *h = HANDLE_RIGHT;
- return true;
- } else if (streq("bottom", s)) {
- *h = HANDLE_BOTTOM;
- return true;
- } else if (streq("top_left", s)) {
- *h = HANDLE_TOP_LEFT;
- return true;
- } else if (streq("top_right", s)) {
- *h = HANDLE_TOP_RIGHT;
- return true;
- } else if (streq("bottom_right", s)) {
- *h = HANDLE_BOTTOM_RIGHT;
- return true;
- } else if (streq("bottom_left", s)) {
- *h = HANDLE_BOTTOM_LEFT;
- return true;
- }
- return false;
-}
-
-bool parse_modifier_mask(char *s, uint16_t *m)
-{
- if (strcmp(s, "shift") == 0) {
- *m = XCB_MOD_MASK_SHIFT;
- return true;
- } else if (strcmp(s, "control") == 0) {
- *m = XCB_MOD_MASK_CONTROL;
- return true;
- } else if (strcmp(s, "lock") == 0) {
- *m = XCB_MOD_MASK_LOCK;
- return true;
- } else if (strcmp(s, "mod1") == 0) {
- *m = XCB_MOD_MASK_1;
- return true;
- } else if (strcmp(s, "mod2") == 0) {
- *m = XCB_MOD_MASK_2;
- return true;
- } else if (strcmp(s, "mod3") == 0) {
- *m = XCB_MOD_MASK_3;
- return true;
- } else if (strcmp(s, "mod4") == 0) {
- *m = XCB_MOD_MASK_4;
- return true;
- } else if (strcmp(s, "mod5") == 0) {
- *m = XCB_MOD_MASK_5;
- return true;
- }
- return false;
-}
-
-bool parse_pointer_action(char *s, pointer_action_t *a)
-{
- if (streq("move", s)) {
- *a = ACTION_MOVE;
- return true;
- } else if (streq("resize_corner", s)) {
- *a = ACTION_RESIZE_CORNER;
- return true;
- } else if (streq("resize_side", s)) {
- *a = ACTION_RESIZE_SIDE;
- return true;
- } else if (streq("focus", s)) {
- *a = ACTION_FOCUS;
- return true;
- } else if (streq("none", s)) {
- *a = ACTION_NONE;
- return true;
- }
- return false;
-}
-
-bool parse_child_polarity(char *s, child_polarity_t *p)
-{
- if (streq("first_child", s)) {
- *p = FIRST_CHILD;
- return true;
- } else if (streq("second_child", s)) {
- *p = SECOND_CHILD;
- return true;
- }
- return false;
-}
-
-bool parse_degree(char *s, int *d)
-{
- int i = atoi(s);
- while (i < 0)
- i += 360;
- while (i > 359)
- i -= 360;
- if ((i % 90) != 0) {
- return false;
- } else {
- *d = i;
- return true;
- }
-}
-
-bool parse_id(char *s, uint32_t *id)
-{
- char *end;
- errno = 0;
- uint32_t v = strtol(s, &end, 0);
- if (errno != 0 || *end != '\0') {
- return false;
- }
- *id = v;
- return true;
-}
-
-bool parse_bool_declaration(char *s, char **key, bool *value, alter_state_t *state)
-{
- *key = strtok(s, EQL_TOK);
- char *v = strtok(NULL, EQL_TOK);
- if (v == NULL) {
- *state = ALTER_TOGGLE;
- return true;
- } else {
- if (parse_bool(v, value)) {
- *state = ALTER_SET;
- return true;
- } else {
- return false;
- }
- }
- return false;
-}
-
-bool parse_index(char *s, uint16_t *idx)
-{
- return (sscanf(s, "^%hu", idx) == 1);
-}
-
-bool parse_rectangle(char *s, xcb_rectangle_t *r)
-{
- uint16_t w, h;
- int16_t x, y;
- if (sscanf(s, "%hux%hu+%hi+%hi", &w, &h, &x, &y) != 4) {
- return false;
- }
- r->width = w;
- r->height = h;
- r->x = x;
- r->y = y;
- return true;
-}
-
-bool parse_subscriber_mask(char *s, subscriber_mask_t *mask)
-{
- if (streq("all", s)) {
- *mask = SBSC_MASK_ALL;
- } else if (streq("node", s)) {
- *mask = SBSC_MASK_NODE;
- } else if (streq("desktop", s)) {
- *mask = SBSC_MASK_DESKTOP;
- } else if (streq("monitor", s)) {
- *mask = SBSC_MASK_MONITOR;
- } else if (streq("pointer_action", s)) {
- *mask = SBSC_MASK_POINTER_ACTION;
- } else if (streq("node_manage", s)) {
- *mask = SBSC_MASK_NODE_MANAGE;
- } else if (streq("node_unmanage", s)) {
- *mask = SBSC_MASK_NODE_UNMANAGE;
- } else if (streq("node_swap", s)) {
- *mask = SBSC_MASK_NODE_SWAP;
- } else if (streq("node_transfer", s)) {
- *mask = SBSC_MASK_NODE_TRANSFER;
- } else if (streq("node_focus", s)) {
- *mask = SBSC_MASK_NODE_FOCUS;
- } else if (streq("node_presel", s)) {
- *mask = SBSC_MASK_NODE_PRESEL;
- } else if (streq("node_stack", s)) {
- *mask = SBSC_MASK_NODE_STACK;
- } else if (streq("node_activate", s)) {
- *mask = SBSC_MASK_NODE_ACTIVATE;
- } else if (streq("node_geometry", s)) {
- *mask = SBSC_MASK_NODE_GEOMETRY;
- } else if (streq("node_state", s)) {
- *mask = SBSC_MASK_NODE_STATE;
- } else if (streq("node_flag", s)) {
- *mask = SBSC_MASK_NODE_FLAG;
- } else if (streq("node_layer", s)) {
- *mask = SBSC_MASK_NODE_LAYER;
- } else if (streq("desktop_add", s)) {
- *mask = SBSC_MASK_DESKTOP_ADD;
- } else if (streq("desktop_rename", s)) {
- *mask = SBSC_MASK_DESKTOP_RENAME;
- } else if (streq("desktop_remove", s)) {
- *mask = SBSC_MASK_DESKTOP_REMOVE;
- } else if (streq("desktop_swap", s)) {
- *mask = SBSC_MASK_DESKTOP_SWAP;
- } else if (streq("desktop_transfer", s)) {
- *mask = SBSC_MASK_DESKTOP_TRANSFER;
- } else if (streq("desktop_focus", s)) {
- *mask = SBSC_MASK_DESKTOP_FOCUS;
- } else if (streq("desktop_activate", s)) {
- *mask = SBSC_MASK_DESKTOP_ACTIVATE;
- } else if (streq("desktop_layout", s)) {
- *mask = SBSC_MASK_DESKTOP_LAYOUT;
- } else if (streq("monitor_add", s)) {
- *mask = SBSC_MASK_MONITOR_ADD;
- } else if (streq("monitor_rename", s)) {
- *mask = SBSC_MASK_MONITOR_RENAME;
- } else if (streq("monitor_remove", s)) {
- *mask = SBSC_MASK_MONITOR_REMOVE;
- } else if (streq("monitor_swap", s)) {
- *mask = SBSC_MASK_MONITOR_SWAP;
- } else if (streq("monitor_focus", s)) {
- *mask = SBSC_MASK_MONITOR_FOCUS;
- } else if (streq("monitor_geometry", s)) {
- *mask = SBSC_MASK_MONITOR_GEOMETRY;
- } else if (streq("report", s)) {
- *mask = SBSC_MASK_REPORT;
- } else {
- return false;
- }
- return true;
-}
-
-
-#define GET_MOD(k) \
- } else if (streq(#k, tok)) { \
- sel->k = OPTION_TRUE; \
- } else if (streq("!" #k, tok)) { \
- sel->k = OPTION_FALSE;
-
-bool parse_monitor_modifiers(char *desc, monitor_select_t *sel)
-{
- char *tok;
- while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
- tok[0] = '\0';
- tok++;
- if (streq("occupied", tok)) {
- sel->occupied = OPTION_TRUE;
- } else if (streq("!occupied", tok)) {
- sel->occupied = OPTION_FALSE;
- GET_MOD(focused)
- } else {
- return false;
- }
- }
- return true;
-}
-
-bool parse_desktop_modifiers(char *desc, desktop_select_t *sel)
-{
- char *tok;
- while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
- tok[0] = '\0';
- tok++;
- if (streq("occupied", tok)) {
- sel->occupied = OPTION_TRUE;
- } else if (streq("!occupied", tok)) {
- sel->occupied = OPTION_FALSE;
- GET_MOD(focused)
- GET_MOD(urgent)
- GET_MOD(local)
- } else {
- return false;
- }
- }
- return true;
-
-}
-
-bool parse_node_modifiers(char *desc, node_select_t *sel)
-{
- char *tok;
- while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
- tok[0] = '\0';
- tok++;
- if (streq("tiled", tok)) {
- sel->tiled = OPTION_TRUE;
- } else if (streq("!tiled", tok)) {
- sel->tiled = OPTION_FALSE;
- GET_MOD(automatic)
- GET_MOD(focused)
- GET_MOD(local)
- GET_MOD(active)
- GET_MOD(leaf)
- GET_MOD(window)
- GET_MOD(pseudo_tiled)
- GET_MOD(floating)
- GET_MOD(fullscreen)
- GET_MOD(hidden)
- GET_MOD(sticky)
- GET_MOD(private)
- GET_MOD(locked)
- GET_MOD(urgent)
- GET_MOD(same_class)
- GET_MOD(descendant_of)
- GET_MOD(ancestor_of)
- GET_MOD(below)
- GET_MOD(normal)
- GET_MOD(above)
- } else {
- return false;
- }
- }
- return true;
-}
-
-#undef GET_MOD
+++ /dev/null
-#ifndef BSPWM_PARSE_H
-#define BSPWM_PARSE_H
-
-#include "types.h"
-#include "subscribe.h"
-
-#define OPT_CHR '-'
-#define CAT_CHR '.'
-#define EQL_TOK "="
-#define COL_TOK ":"
-
-bool parse_bool(char *value, bool *b);
-bool parse_split_type(char *s, split_type_t *t);
-bool parse_split_mode(char *s, split_mode_t *m);
-bool parse_layout(char *s, layout_t *l);
-bool parse_client_state(char *s, client_state_t *t);
-bool parse_stack_layer(char *s, stack_layer_t *l);
-bool parse_direction(char *s, direction_t *d);
-bool parse_cycle_direction(char *s, cycle_dir_t *d);
-bool parse_circulate_direction(char *s, circulate_dir_t *d);
-bool parse_history_direction(char *s, history_dir_t *d);
-bool parse_flip(char *s, flip_t *f);
-bool parse_resize_handle(char *s, resize_handle_t *h);
-bool parse_modifier_mask(char *s, uint16_t *m);
-bool parse_pointer_action(char *s, pointer_action_t *a);
-bool parse_child_polarity(char *s, child_polarity_t *p);
-bool parse_degree(char *s, int *d);
-bool parse_id(char *s, uint32_t *id);
-bool parse_bool_declaration(char *s, char **key, bool *value, alter_state_t *state);
-bool parse_index(char *s, uint16_t *idx);
-bool parse_rectangle(char *s, xcb_rectangle_t *r);
-bool parse_subscriber_mask(char *s, subscriber_mask_t *mask);
-bool parse_monitor_modifiers(char *desc, monitor_select_t *sel);
-bool parse_desktop_modifiers(char *desc, desktop_select_t *sel);
-bool parse_node_modifiers(char *desc, node_select_t *sel);
-
-#endif
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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 <xcb/xcb_keysyms.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include "bspwm.h"
-#include "query.h"
-#include "settings.h"
-#include "stack.h"
-#include "tree.h"
-#include "monitor.h"
-#include "subscribe.h"
-#include "events.h"
-#include "window.h"
-#include "pointer.h"
-
-void pointer_init(void)
-{
- num_lock = modfield_from_keysym(XK_Num_Lock);
- caps_lock = modfield_from_keysym(XK_Caps_Lock);
- scroll_lock = modfield_from_keysym(XK_Scroll_Lock);
- if (caps_lock == XCB_NO_SYMBOL) {
- caps_lock = XCB_MOD_MASK_LOCK;
- }
- grabbing = false;
- grabbed_node = NULL;
-}
-
-void window_grab_buttons(xcb_window_t win)
-{
- if (click_to_focus) {
- window_grab_button(win, XCB_BUTTON_INDEX_1, XCB_NONE);
- }
- uint8_t buttons[] = {XCB_BUTTON_INDEX_1, XCB_BUTTON_INDEX_2, XCB_BUTTON_INDEX_3};
- for (unsigned int i = 0; i < LENGTH(buttons); i++) {
- if (pointer_actions[i] != ACTION_NONE) {
- window_grab_button(win, buttons[i], pointer_modifier);
- }
- }
-}
-
-void window_grab_button(xcb_window_t win, uint8_t button, uint16_t modifier)
-{
-#define GRAB(b, m) \
- xcb_grab_button(dpy, false, win, XCB_EVENT_MASK_BUTTON_PRESS, \
- XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, b, m)
- GRAB(button, modifier);
- if (num_lock != XCB_NO_SYMBOL && caps_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
- GRAB(button, modifier | num_lock | caps_lock | scroll_lock);
- }
- if (num_lock != XCB_NO_SYMBOL && caps_lock != XCB_NO_SYMBOL) {
- GRAB(button, modifier | num_lock | caps_lock);
- }
- if (caps_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
- GRAB(button, modifier | caps_lock | scroll_lock);
- }
- if (num_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
- GRAB(button, modifier | num_lock | scroll_lock);
- }
- if (num_lock != XCB_NO_SYMBOL) {
- GRAB(button, modifier | num_lock);
- }
- if (caps_lock != XCB_NO_SYMBOL) {
- GRAB(button, modifier | caps_lock);
- }
- if (scroll_lock != XCB_NO_SYMBOL) {
- GRAB(button, modifier | scroll_lock);
- }
-#undef GRAB
-}
-
-void grab_buttons(void)
-{
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- 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)) {
- window_grab_buttons(n->id);
- if (n->presel != NULL) {
- window_grab_buttons(n->presel->feedback);
- }
- }
- }
- }
-}
-
-void ungrab_buttons(void)
-{
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- 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)) {
- xcb_ungrab_button(dpy, XCB_BUTTON_INDEX_ANY, n->id, XCB_MOD_MASK_ANY);
- }
- }
- }
-}
-
-int16_t modfield_from_keysym(xcb_keysym_t keysym)
-{
- uint16_t modfield = 0;
- xcb_keycode_t *keycodes = NULL, *mod_keycodes = NULL;
- xcb_get_modifier_mapping_reply_t *reply = NULL;
- xcb_key_symbols_t *symbols = xcb_key_symbols_alloc(dpy);
-
- if ((keycodes = xcb_key_symbols_get_keycode(symbols, keysym)) == NULL ||
- (reply = xcb_get_modifier_mapping_reply(dpy, xcb_get_modifier_mapping(dpy), NULL)) == NULL ||
- reply->keycodes_per_modifier < 1 ||
- (mod_keycodes = xcb_get_modifier_mapping_keycodes(reply)) == NULL) {
- goto end;
- }
-
- unsigned int num_mod = xcb_get_modifier_mapping_keycodes_length(reply) / reply->keycodes_per_modifier;
- for (unsigned int i = 0; i < num_mod; i++) {
- for (unsigned int j = 0; j < reply->keycodes_per_modifier; j++) {
- xcb_keycode_t mk = mod_keycodes[i * reply->keycodes_per_modifier + j];
- if (mk == XCB_NO_SYMBOL) {
- continue;
- }
- for (xcb_keycode_t *k = keycodes; *k != XCB_NO_SYMBOL; k++) {
- if (*k == mk) {
- modfield |= (1 << i);
- }
- }
- }
- }
-
-end:
- xcb_key_symbols_free(symbols);
- free(keycodes);
- free(reply);
- return modfield;
-}
-
-resize_handle_t get_handle(node_t *n, xcb_point_t pos, pointer_action_t pac)
-{
- resize_handle_t rh = HANDLE_BOTTOM_RIGHT;
- xcb_rectangle_t rect = get_rectangle(NULL, n);
- if (pac == ACTION_RESIZE_SIDE) {
- float W = rect.width;
- float H = rect.height;
- float ratio = W / H;
- float x = pos.x - rect.x;
- float y = pos.y - rect.y;
- float diag_a = ratio * y;
- float diag_b = W - diag_a;
- if (x < diag_a) {
- if (x < diag_b) {
- rh = HANDLE_LEFT;
- } else {
- rh = HANDLE_BOTTOM;
- }
- } else {
- if (x < diag_b) {
- rh = HANDLE_TOP;
- } else {
- rh = HANDLE_RIGHT;
- }
- }
- } else if (pac == ACTION_RESIZE_CORNER) {
- int16_t mid_x = rect.x + (rect.width / 2);
- int16_t mid_y = rect.y + (rect.height / 2);
- if (pos.x > mid_x) {
- if (pos.y > mid_y) {
- rh = HANDLE_BOTTOM_RIGHT;
- } else {
- rh = HANDLE_TOP_RIGHT;
- }
- } else {
- if (pos.y > mid_y) {
- rh = HANDLE_BOTTOM_LEFT;
- } else {
- rh = HANDLE_TOP_LEFT;
- }
- }
- }
- return rh;
-}
-
-bool grab_pointer(pointer_action_t pac)
-{
- xcb_window_t win = XCB_NONE;
- xcb_point_t pos;
-
- query_pointer(&win, &pos);
-
- coordinates_t loc;
-
- if (!locate_window(win, &loc)) {
- if (pac == ACTION_FOCUS) {
- monitor_t *m = monitor_from_point(pos);
- if (m != NULL && m != mon && (win == XCB_NONE || win == m->root)) {
- focus_node(m, m->desk, m->desk->focus);
- return true;
- }
- }
- return false;
- }
-
- if (pac == ACTION_FOCUS) {
- if (loc.node != mon->desk->focus) {
- focus_node(loc.monitor, loc.desktop, loc.node);
- return true;
- } else if (focus_follows_pointer) {
- stack(loc.desktop, loc.node, true);
- }
- return false;
- }
-
- if (loc.node->client->state == STATE_FULLSCREEN) {
- return true;
- }
-
- xcb_grab_pointer_reply_t *reply = xcb_grab_pointer_reply(dpy, xcb_grab_pointer(dpy, 0, root, XCB_EVENT_MASK_BUTTON_RELEASE|XCB_EVENT_MASK_BUTTON_MOTION, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, XCB_CURRENT_TIME), NULL);
-
- if (reply == NULL || reply->status != XCB_GRAB_STATUS_SUCCESS) {
- free(reply);
- return true;
- }
- free(reply);
-
- if (pac == ACTION_MOVE) {
- put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X move begin\n", loc.monitor->id, loc.desktop->id, loc.node->id);
- } else if (pac == ACTION_RESIZE_CORNER) {
- put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X resize_corner begin\n", loc.monitor->id, loc.desktop->id, loc.node->id);
- } else if (pac == ACTION_RESIZE_SIDE) {
- put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X resize_side begin\n", loc.monitor->id, loc.desktop->id, loc.node->id);
- }
-
- track_pointer(loc, pac, pos);
-
- return true;
-}
-
-void track_pointer(coordinates_t loc, pointer_action_t pac, xcb_point_t pos)
-{
- node_t *n = loc.node;
- resize_handle_t rh = get_handle(loc.node, pos, pac);
-
- uint16_t last_motion_x = pos.x, last_motion_y = pos.y;
- xcb_timestamp_t last_motion_time = 0;
-
- xcb_generic_event_t *evt = NULL;
-
- grabbing = true;
- grabbed_node = n;
-
- do {
- free(evt);
- while ((evt = xcb_wait_for_event(dpy)) == NULL) {
- xcb_flush(dpy);
- }
- uint8_t resp_type = XCB_EVENT_RESPONSE_TYPE(evt);
- if (resp_type == XCB_MOTION_NOTIFY) {
- xcb_motion_notify_event_t *e = (xcb_motion_notify_event_t*) evt;
- uint32_t dtime = e->time - last_motion_time;
- if (dtime < pointer_motion_interval) {
- continue;
- }
- last_motion_time = e->time;
- int16_t dx = e->root_x - last_motion_x;
- int16_t dy = e->root_y - last_motion_y;
- if (pac == ACTION_MOVE) {
- move_client(&loc, dx, dy);
- } else {
- resize_client(&loc, rh, dx, dy);
- }
- last_motion_x = e->root_x;
- last_motion_y = e->root_y;
- xcb_flush(dpy);
- } else if (resp_type == XCB_BUTTON_RELEASE) {
- grabbing = false;
- } else {
- handle_event(evt);
- }
- } while (grabbing && grabbed_node != NULL);
- free(evt);
-
- xcb_ungrab_pointer(dpy, XCB_CURRENT_TIME);
-
- if (grabbed_node == NULL) {
- grabbing = false;
- return;
- }
-
- if (pac == ACTION_MOVE) {
- put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X move end\n", loc.monitor->id, loc.desktop->id, n->id);
- } else if (pac == ACTION_RESIZE_CORNER) {
- put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X resize_corner end\n", loc.monitor->id, loc.desktop->id, n->id);
- } else if (pac == ACTION_RESIZE_SIDE) {
- put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X resize_side end\n", loc.monitor->id, loc.desktop->id, n->id);
- }
-
- xcb_rectangle_t r = get_rectangle(NULL, n);
-
- put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", loc.monitor->id, loc.desktop->id, loc.node->id, r.width, r.height, r.x, r.y);
-
- if ((pac == ACTION_MOVE && IS_TILED(n->client)) ||
- ((pac == ACTION_RESIZE_CORNER || pac == ACTION_RESIZE_SIDE) &&
- n->client->state == STATE_TILED)) {
- for (node_t *f = first_extrema(loc.desktop->root); f != NULL; f = next_leaf(f, loc.desktop->root)) {
- if (f == n || f->client == NULL || !IS_TILED(f->client)) {
- continue;
- }
- xcb_rectangle_t r = f->client->tiled_rectangle;
- put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", loc.monitor->id, loc.desktop->id, f->id, r.width, r.height, r.x, r.y);
- }
- }
-}
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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.
- */
-
-#ifndef BSPWM_POINTER_H
-#define BSPWM_POINTER_H
-
-#define XK_Num_Lock 0xff7f
-#define XK_Caps_Lock 0xffe5
-#define XK_Scroll_Lock 0xff14
-
-uint16_t num_lock;
-uint16_t caps_lock;
-uint16_t scroll_lock;
-
-bool grabbing;
-node_t *grabbed_node;
-
-void pointer_init(void);
-void window_grab_buttons(xcb_window_t win);
-void window_grab_button(xcb_window_t win, uint8_t button, uint16_t modifier);
-void grab_buttons(void);
-void ungrab_buttons(void);
-int16_t modfield_from_keysym(xcb_keysym_t keysym);
-resize_handle_t get_handle(node_t *n, xcb_point_t pos, pointer_action_t pac);
-bool grab_pointer(pointer_action_t pac);
-void track_pointer(coordinates_t loc, pointer_action_t pac, xcb_point_t pos);
-
-#endif
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "bspwm.h"
-#include "desktop.h"
-#include "history.h"
-#include "parse.h"
-#include "monitor.h"
-#include "window.h"
-#include "tree.h"
-#include "query.h"
-
-void query_tree(FILE *rsp)
-{
- fprintf(rsp, "{");
- fprintf(rsp, "\"focusedMonitorId\":%u,", mon->id);
- if (pri_mon != NULL) {
- fprintf(rsp, "\"primaryMonitorId\":%u,", pri_mon->id);
- }
- fprintf(rsp, "\"clientsCount\":%i,", clients_count);
- fprintf(rsp, "\"monitors\":");
- fprintf(rsp, "[");
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- query_monitor(m, rsp);
- if (m->next != NULL) {
- fprintf(rsp, ",");
- }
- }
- fprintf(rsp, "]");
- fprintf(rsp,",");
- fprintf(rsp, "\"focusHistory\":");
- query_history(rsp);
- fprintf(rsp,",");
- fprintf(rsp, "\"stackingList\":");
- query_stack(rsp);
- fprintf(rsp, "}");
-
-}
-
-void query_monitor(monitor_t *m, FILE *rsp)
-{
- fprintf(rsp, "{");
- fprintf(rsp, "\"name\":\"%s\",", m->name);
- fprintf(rsp, "\"id\":%u,", m->id);
- fprintf(rsp, "\"randrId\":%u,", m->randr_id);
- fprintf(rsp, "\"wired\":%s,", BOOL_STR(m->wired));
- fprintf(rsp, "\"stickyCount\":%i,", m->sticky_count);
- fprintf(rsp, "\"windowGap\":%i,", m->window_gap);
- fprintf(rsp, "\"borderWidth\":%u,", m->border_width);
- fprintf(rsp, "\"focusedDesktopId\":%u,", m->desk->id);
- fprintf(rsp, "\"padding\":");
- query_padding(m->padding, rsp);
- fprintf(rsp,",");
- fprintf(rsp, "\"rectangle\":");
- query_rectangle(m->rectangle, rsp);
- fprintf(rsp,",");
- fprintf(rsp, "\"desktops\":");
- fprintf(rsp, "[");
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
- query_desktop(d, rsp);
- if (d->next != NULL) {
- fprintf(rsp,",");
- }
- }
- fprintf(rsp, "]");
- fprintf(rsp, "}");
-}
-
-void query_desktop(desktop_t *d, FILE *rsp)
-{
- fprintf(rsp, "{");
- fprintf(rsp, "\"name\":\"%s\",", d->name);
- fprintf(rsp, "\"id\":%u,", d->id);
- fprintf(rsp, "\"layout\":\"%s\",", LAYOUT_STR(d->layout));
- fprintf(rsp, "\"windowGap\":%i,", d->window_gap);
- fprintf(rsp, "\"borderWidth\":%u,", d->border_width);
- fprintf(rsp, "\"focusedNodeId\":%u,", d->focus != NULL ? d->focus->id : 0);
- fprintf(rsp, "\"padding\":");
- query_padding(d->padding, rsp);
- fprintf(rsp,",");
- fprintf(rsp, "\"root\":");
- query_node(d->root, rsp);
- fprintf(rsp, "}");
-}
-
-void query_node(node_t *n, FILE *rsp)
-{
- if (n == NULL) {
- fprintf(rsp, "null");
- } else {
- fprintf(rsp, "{");
- fprintf(rsp, "\"id\":%u,", n->id);
- fprintf(rsp, "\"splitType\":\"%s\",", SPLIT_TYPE_STR(n->split_type));
- fprintf(rsp, "\"splitRatio\":%lf,", n->split_ratio);
- fprintf(rsp, "\"birthRotation\":%i,", n->birth_rotation);
- fprintf(rsp, "\"vacant\":%s,", BOOL_STR(n->vacant));
- fprintf(rsp, "\"hidden\":%s,", BOOL_STR(n->hidden));
- fprintf(rsp, "\"sticky\":%s,", BOOL_STR(n->sticky));
- fprintf(rsp, "\"private\":%s,", BOOL_STR(n->private));
- fprintf(rsp, "\"locked\":%s,", BOOL_STR(n->locked));
- fprintf(rsp, "\"presel\":");
- query_presel(n->presel, rsp);
- fprintf(rsp,",");
- fprintf(rsp, "\"rectangle\":");
- query_rectangle(n->rectangle, rsp);
- fprintf(rsp,",");
- fprintf(rsp, "\"firstChild\":");
- query_node(n->first_child, rsp);
- fprintf(rsp,",");
- fprintf(rsp, "\"secondChild\":");
- query_node(n->second_child, rsp);
- fprintf(rsp,",");
- fprintf(rsp, "\"client\":");
- query_client(n->client, rsp);
- fprintf(rsp, "}");
- }
-}
-
-void query_presel(presel_t *p, FILE *rsp)
-{
- if (p == NULL) {
- fprintf(rsp, "null");
- } else {
- fprintf(rsp, "{\"splitDir\":\"%s\",\"splitRatio\":%lf}", SPLIT_DIR_STR(p->split_dir), p->split_ratio);
- }
-}
-
-void query_client(client_t *c, FILE *rsp)
-{
- if (c == NULL) {
- fprintf(rsp, "null");
- } else {
- fprintf(rsp, "{");
- fprintf(rsp, "\"className\":\"%s\",", c->class_name);
- fprintf(rsp, "\"instanceName\":\"%s\",", c->instance_name);
- fprintf(rsp, "\"borderWidth\":%u,", c->border_width);
- fprintf(rsp, "\"state\":\"%s\",", STATE_STR(c->state));
- fprintf(rsp, "\"lastState\":\"%s\",", STATE_STR(c->last_state));
- fprintf(rsp, "\"layer\":\"%s\",", LAYER_STR(c->layer));
- fprintf(rsp, "\"lastLayer\":\"%s\",", LAYER_STR(c->last_layer));
- fprintf(rsp, "\"urgent\":%s,", BOOL_STR(c->urgent));
- fprintf(rsp, "\"shown\":%s,", BOOL_STR(c->shown));
- fprintf(rsp, "\"tiledRectangle\":");
- query_rectangle(c->tiled_rectangle, rsp);
- fprintf(rsp,",");
- fprintf(rsp, "\"floatingRectangle\":");
- query_rectangle(c->floating_rectangle, rsp);
- fprintf(rsp, "}");
- }
-}
-
-void query_rectangle(xcb_rectangle_t r, FILE *rsp)
-{
- fprintf(rsp, "{\"x\":%i,\"y\":%i,\"width\":%u,\"height\":%u}", r.x, r.y, r.width, r.height);
-}
-
-void query_padding(padding_t p, FILE *rsp)
-{
- fprintf(rsp, "{\"top\":%i,\"right\":%i,\"bottom\":%i,\"left\":%i}", p.top, p.right, p.bottom, p.left);
-}
-
-void query_history(FILE *rsp)
-{
- fprintf(rsp, "[");
- for (history_t *h = history_head; h != NULL; h = h->next) {
- query_coordinates(&h->loc, rsp);
- if (h->next != NULL) {
- fprintf(rsp, ",");
- }
- }
- fprintf(rsp, "]");
-}
-
-void query_coordinates(coordinates_t *loc, FILE *rsp)
-{
- fprintf(rsp, "{\"monitorId\":%u,\"desktopId\":%u,\"nodeId\":%u}", loc->monitor->id, loc->desktop->id, loc->node!=NULL?loc->node->id:0);
-}
-
-void query_stack(FILE *rsp)
-{
- fprintf(rsp, "[");
- for (stacking_list_t *s = stack_head; s != NULL; s = s->next) {
- fprintf(rsp, "%u", s->node->id);
- if (s->next != NULL) {
- fprintf(rsp, ",");
- }
- }
- fprintf(rsp, "]");
-}
-
-int query_node_ids(coordinates_t *ref, coordinates_t *trg, node_select_t *sel, FILE *rsp)
-{
- int count = 0;
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- if (trg->monitor != NULL && m != trg->monitor) {
- continue;
- }
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
- if (trg->desktop != NULL && d != trg->desktop) {
- continue;
- }
- count += query_node_ids_in(d->root, d, m, ref, trg, sel, rsp);
- }
- }
- return count;
-}
-
-int query_node_ids_in(node_t *n, desktop_t *d, monitor_t *m, coordinates_t *ref, coordinates_t *trg, node_select_t *sel, FILE *rsp)
-{
- int count = 0;
- if (n == NULL) {
- return 0;
- } else {
- coordinates_t loc = {m, d, n};
- if ((trg->node == NULL || n == trg->node) &&
- (sel == NULL || node_matches(&loc, ref, *sel))) {
- fprintf(rsp, "0x%08X\n", n->id);
- count++;
- }
- count += query_node_ids_in(n->first_child, d, m, ref, trg, sel, rsp);
- count += query_node_ids_in(n->second_child, d, m, ref, trg, sel, rsp);
- }
- return count;
-}
-
-int query_desktop_ids(coordinates_t *ref, coordinates_t *trg, desktop_select_t *sel, desktop_printer_t printer, FILE *rsp)
-{
- int count = 0;
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- if (trg->monitor != NULL && m != trg->monitor) {
- continue;
- }
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
- coordinates_t loc = {m, d, NULL};
- if ((trg->desktop != NULL && d != trg->desktop) ||
- (sel != NULL && !desktop_matches(&loc, ref, *sel))) {
- continue;
- }
- printer(d, rsp);
- count++;
- }
- }
- return count;
-}
-
-int query_monitor_ids(coordinates_t *ref, coordinates_t *trg, monitor_select_t *sel, monitor_printer_t printer, FILE *rsp)
-{
- int count = 0;
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- coordinates_t loc = {m, NULL, NULL};
- if ((trg->monitor != NULL && m != trg->monitor) ||
- (sel != NULL && !monitor_matches(&loc, ref, *sel))) {
- continue;
- }
- printer(m, rsp);
- count++;
- }
- return count;
-}
-
-void fprint_monitor_id(monitor_t *m, FILE *rsp)
-{
- fprintf(rsp, "0x%08X\n", m->id);
-}
-
-void fprint_monitor_name(monitor_t *m, FILE *rsp)
-{
- fprintf(rsp, "%s\n", m->name);
-}
-
-void fprint_desktop_id(desktop_t *d, FILE *rsp)
-{
- fprintf(rsp, "0x%08X\n", d->id);
-}
-
-void fprint_desktop_name(desktop_t *d, FILE *rsp)
-{
- fprintf(rsp, "%s\n", d->name);
-}
-
-void print_modifier_mask(uint16_t m, FILE *rsp)
-{
- switch (m) {
- case XCB_MOD_MASK_SHIFT:
- fprintf(rsp, "shift");
- break;
- case XCB_MOD_MASK_CONTROL:
- fprintf(rsp, "control");
- break;
- case XCB_MOD_MASK_LOCK:
- fprintf(rsp, "lock");
- break;
- case XCB_MOD_MASK_1:
- fprintf(rsp, "mod1");
- break;
- case XCB_MOD_MASK_2:
- fprintf(rsp, "mod2");
- break;
- case XCB_MOD_MASK_3:
- fprintf(rsp, "mod3");
- break;
- case XCB_MOD_MASK_4:
- fprintf(rsp, "mod4");
- break;
- case XCB_MOD_MASK_5:
- fprintf(rsp, "mod5");
- break;
- }
-}
-
-void print_pointer_action(pointer_action_t a, FILE *rsp)
-{
- switch (a) {
- case ACTION_MOVE:
- fprintf(rsp, "move");
- break;
- case ACTION_RESIZE_SIDE:
- fprintf(rsp, "resize_side");
- break;
- case ACTION_RESIZE_CORNER:
- fprintf(rsp, "resize_corner");
- break;
- case ACTION_FOCUS:
- fprintf(rsp, "focus");
- break;
- case ACTION_NONE:
- fprintf(rsp, "none");
- break;
- }
-}
-
-node_select_t make_node_select(void)
-{
- node_select_t sel = {
- .automatic = OPTION_NONE,
- .focused = OPTION_NONE,
- .local = OPTION_NONE,
- .active = OPTION_NONE,
- .leaf = OPTION_NONE,
- .window = OPTION_NONE,
- .tiled = OPTION_NONE,
- .pseudo_tiled = OPTION_NONE,
- .floating = OPTION_NONE,
- .fullscreen = OPTION_NONE,
- .hidden = OPTION_NONE,
- .sticky = OPTION_NONE,
- .private = OPTION_NONE,
- .locked = OPTION_NONE,
- .urgent = OPTION_NONE,
- .same_class = OPTION_NONE,
- .descendant_of = OPTION_NONE,
- .ancestor_of = OPTION_NONE,
- .below = OPTION_NONE,
- .normal = OPTION_NONE,
- .above = OPTION_NONE
- };
- return sel;
-}
-
-desktop_select_t make_desktop_select(void)
-{
- desktop_select_t sel = {
- .occupied = OPTION_NONE,
- .focused = OPTION_NONE,
- .urgent = OPTION_NONE,
- .local = OPTION_NONE
- };
- return sel;
-}
-
-monitor_select_t make_monitor_select(void)
-{
- monitor_select_t sel = {
- .occupied = OPTION_NONE,
- .focused = OPTION_NONE
- };
- return sel;
-}
-
-int node_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
-{
- coordinates_t ref_copy = *ref;
- ref = &ref_copy;
- char *desc_copy = copy_string(desc, strlen(desc));
- desc = desc_copy;
-
- char *hash = NULL;
- char *path = strrchr(desc, '@');
- if (path == NULL) {
- hash = strrchr(desc, '#');
- } else {
- if (path != desc && *(path - 1) == '#') {
- hash = path - 1;
- }
- }
-
- if (hash != NULL) {
- *hash = '\0';
- int ret;
- coordinates_t tmp = {mon, mon->desk, mon->desk->focus};
- if ((ret = node_from_desc(desc, &tmp, ref)) == SELECTOR_OK) {
- desc = hash + 1;
- } else {
- free(desc_copy);
- return ret;
- }
- }
-
- node_select_t sel = make_node_select();
- char *colon = strrchr(desc, ':');
-
- if (!parse_node_modifiers(colon != NULL ? colon : desc, &sel)) {
- free(desc_copy);
- return SELECTOR_BAD_MODIFIERS;
- }
-
- dst->node = NULL;
-
- direction_t dir;
- cycle_dir_t cyc;
- history_dir_t hdi;
- if (parse_direction(desc, &dir)) {
- find_nearest_neighbor(ref, dst, dir, sel);
- } else if (parse_cycle_direction(desc, &cyc)) {
- find_closest_node(ref, dst, cyc, sel);
- } else if (parse_history_direction(desc, &hdi)) {
- history_find_node(hdi, ref, dst, sel);
- } else if (streq("last", desc)) {
- history_find_node(HISTORY_OLDER, ref, dst, sel);
- } else if (streq("biggest", desc)) {
- find_biggest(ref, dst, sel);
- } else if (streq("pointed", desc)) {
- xcb_window_t win;
- query_pointer(&win, NULL);
- if (locate_window(win, dst) && node_matches(dst, ref, sel)) {
- return SELECTOR_OK;
- } else {
- return SELECTOR_INVALID;
- }
- } else if (streq("focused", desc)) {
- coordinates_t loc = {mon, mon->desk, mon->desk->focus};
- if (node_matches(&loc, ref, sel)) {
- *dst = loc;
- }
- } else if (*desc == '@') {
- desc++;
- *dst = *ref;
- if (colon != NULL) {
- *colon = '\0';
- int ret;
- if ((ret = desktop_from_desc(desc, ref, dst)) == SELECTOR_OK) {
- dst->node = dst->desktop->focus;
- desc = colon + 1;
- } else {
- free(desc_copy);
- return ret;
- }
- }
- if (*desc == '/') {
- dst->node = dst->desktop->root;
- }
- char *move = strtok(desc, PTH_TOK);
- while (move != NULL && dst->node != NULL) {
- if (streq("first", move) || streq("1", move)) {
- dst->node = dst->node->first_child;
- } else if (streq("second", move) || streq("2", move)) {
- dst->node = dst->node->second_child;
- } else if (streq("parent", move)) {
- dst->node = dst->node->parent;
- } else if (streq("brother", move)) {
- dst->node = brother_tree(dst->node);
- } else {
- direction_t dir;
- if (parse_direction(move, &dir)) {
- dst->node = find_fence(dst->node, dir);
- } else {
- free(desc_copy);
- return SELECTOR_BAD_DESCRIPTOR;
- }
- }
- move = strtok(NULL, PTH_TOK);
- }
- free(desc_copy);
- if (dst->node != NULL) {
- if (node_matches(dst, ref, sel)) {
- return SELECTOR_OK;
- } else {
- return SELECTOR_INVALID;
- }
- } else if (dst->desktop->root != NULL) {
- return SELECTOR_INVALID;
- }
- return SELECTOR_OK;
- } else {
- uint32_t id;
- if (parse_id(desc, &id)) {
- free(desc_copy);
- if (find_by_id(id, dst) && node_matches(dst, ref, sel)) {
- return SELECTOR_OK;
- } else {
- return SELECTOR_INVALID;
- }
- } else {
- free(desc_copy);
- return SELECTOR_BAD_DESCRIPTOR;
- }
- }
-
- free(desc_copy);
-
- if (dst->node == NULL) {
- return SELECTOR_INVALID;
- }
-
- return SELECTOR_OK;
-}
-
-int desktop_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
-{
- coordinates_t ref_copy = *ref;
- ref = &ref_copy;
- char *desc_copy = copy_string(desc, strlen(desc));
- desc = desc_copy;
-
- char *hash = strrchr(desc, '#');
-
- if (hash != NULL) {
- *hash = '\0';
- int ret;
- coordinates_t tmp = {mon, mon->desk, NULL};
- if ((ret = desktop_from_desc(desc, &tmp, ref)) == SELECTOR_OK) {
- desc = hash + 1;
- } else {
- free(desc_copy);
- return ret;
- }
- }
-
- desktop_select_t sel = make_desktop_select();
- char *colon = strrchr(desc, ':');
-
- if (!parse_desktop_modifiers(colon != NULL ? colon : desc, &sel)) {
- free(desc_copy);
- return SELECTOR_BAD_MODIFIERS;
- }
-
- dst->desktop = NULL;
-
- cycle_dir_t cyc;
- history_dir_t hdi;
- uint16_t idx;
- uint32_t id;
- if (parse_cycle_direction(desc, &cyc)) {
- find_closest_desktop(ref, dst, cyc, sel);
- } else if (parse_history_direction(desc, &hdi)) {
- history_find_desktop(hdi, ref, dst, sel);
- } else if (streq("last", desc)) {
- history_find_desktop(HISTORY_OLDER, ref, dst, sel);
- } else if (streq("focused", desc)) {
- coordinates_t loc = {mon, mon->desk, NULL};
- if (desktop_matches(&loc, ref, sel)) {
- *dst = loc;
- }
- } else if (colon != NULL) {
- *colon = '\0';
- int ret;
- if ((ret = monitor_from_desc(desc, ref, dst)) == SELECTOR_OK) {
- if (streq("focused", colon + 1)) {
- coordinates_t loc = {dst->monitor, dst->monitor->desk, NULL};
- if (desktop_matches(&loc, ref, sel)) {
- *dst = loc;
- }
- } else if (parse_index(colon + 1, &idx)) {
- free(desc_copy);
- if (desktop_from_index(idx, dst, dst->monitor) && desktop_matches(dst, ref, sel)) {
- return SELECTOR_OK;
- } else {
- return SELECTOR_INVALID;
- }
- } else {
- free(desc_copy);
- return SELECTOR_BAD_DESCRIPTOR;
- }
- } else {
- free(desc_copy);
- return ret;
- }
- } else if (parse_index(desc, &idx) && desktop_from_index(idx, dst, NULL)) {
- free(desc_copy);
- if (desktop_matches(dst, ref, sel)) {
- return SELECTOR_OK;
- } else {
- return SELECTOR_INVALID;
- }
- } else if (parse_id(desc, &id) && desktop_from_id(id, dst, NULL)) {
- free(desc_copy);
- if (desktop_matches(dst, ref, sel)) {
- return SELECTOR_OK;
- } else {
- return SELECTOR_INVALID;
- }
- } else {
- if (locate_desktop(desc, dst)) {
- free(desc_copy);
- if (desktop_matches(dst, ref, sel)) {
- return SELECTOR_OK;
- } else {
- return SELECTOR_INVALID;
- }
- } else {
- free(desc_copy);
- return SELECTOR_BAD_DESCRIPTOR;
- }
- }
-
- free(desc_copy);
-
- if (dst->desktop == NULL) {
- return SELECTOR_INVALID;
- }
-
- return SELECTOR_OK;
-}
-
-int monitor_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
-{
- coordinates_t ref_copy = *ref;
- ref = &ref_copy;
- char *desc_copy = copy_string(desc, strlen(desc));
- desc = desc_copy;
-
- char *hash = strrchr(desc, '#');
-
- if (hash != NULL) {
- *hash = '\0';
- int ret;
- coordinates_t tmp = {mon, NULL, NULL};
- if ((ret = monitor_from_desc(desc, &tmp, ref)) == SELECTOR_OK) {
- desc = hash + 1;
- } else {
- free(desc_copy);
- return ret;
- }
- }
-
- monitor_select_t sel = make_monitor_select();
-
- if (!parse_monitor_modifiers(desc, &sel)) {
- free(desc_copy);
- return SELECTOR_BAD_MODIFIERS;
- }
-
- dst->monitor = NULL;
-
- direction_t dir;
- cycle_dir_t cyc;
- history_dir_t hdi;
- uint16_t idx;
- uint32_t id;
- if (parse_direction(desc, &dir)) {
- dst->monitor = nearest_monitor(ref->monitor, dir, sel);
- } else if (parse_cycle_direction(desc, &cyc)) {
- dst->monitor = closest_monitor(ref->monitor, cyc, sel);
- } else if (parse_history_direction(desc, &hdi)) {
- history_find_monitor(hdi, ref, dst, sel);
- } else if (streq("last", desc)) {
- history_find_monitor(HISTORY_OLDER, ref, dst, sel);
- } else if (streq("primary", desc)) {
- if (pri_mon != NULL) {
- coordinates_t loc = {pri_mon, NULL, NULL};
- if (monitor_matches(&loc, ref, sel)) {
- dst->monitor = pri_mon;
- }
- }
- } else if (streq("focused", desc)) {
- coordinates_t loc = {mon, NULL, NULL};
- if (monitor_matches(&loc, ref, sel)) {
- dst->monitor = mon;
- }
- } else if (parse_index(desc, &idx) && monitor_from_index(idx, dst)) {
- free(desc_copy);
- if (monitor_matches(dst, ref, sel)) {
- return SELECTOR_OK;
- } else {
- return SELECTOR_INVALID;
- }
- } else if (parse_id(desc, &id) && monitor_from_id(id, dst)) {
- free(desc_copy);
- if (monitor_matches(dst, ref, sel)) {
- return SELECTOR_OK;
- } else {
- return SELECTOR_INVALID;
- }
- } else {
- if (locate_monitor(desc, dst)) {
- free(desc_copy);
- if (monitor_matches(dst, ref, sel)) {
- return SELECTOR_OK;
- } else {
- return SELECTOR_INVALID;
- }
- } else {
- free(desc_copy);
- return SELECTOR_BAD_DESCRIPTOR;
- }
- }
-
- free(desc_copy);
-
- if (dst->monitor == NULL) {
- return SELECTOR_INVALID;
- }
-
- return SELECTOR_OK;
-}
-
-bool locate_window(xcb_window_t win, coordinates_t *loc)
-{
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- 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)) {
- if (n->client == NULL) {
- continue;
- }
- if (n->id == win) {
- loc->monitor = m;
- loc->desktop = d;
- loc->node = n;
- return true;
- }
- }
- }
- }
- return false;
-}
-
-bool locate_desktop(char *name, coordinates_t *loc)
-{
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
- if (streq(d->name, name)) {
- loc->monitor = m;
- loc->desktop = d;
- return true;
- }
- }
- }
- return false;
-}
-
-bool locate_monitor(char *name, coordinates_t *loc)
-{
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- if (streq(m->name, name)) {
- loc->monitor = m;
- return true;
- }
- }
- return false;
-}
-
-bool desktop_from_id(uint32_t id, coordinates_t *loc, monitor_t *mm)
-{
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- if (mm != NULL && m != mm) {
- continue;
- }
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
- if (d->id == id) {
- loc->monitor = m;
- loc->desktop = d;
- loc->node = NULL;
- return true;
- }
- }
- }
- return false;
-}
-
-bool desktop_from_index(uint16_t idx, coordinates_t *loc, monitor_t *mm)
-{
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- if (mm != NULL && m != mm) {
- continue;
- }
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next, idx--) {
- if (idx == 1) {
- loc->monitor = m;
- loc->desktop = d;
- loc->node = NULL;
- return true;
- }
- }
- }
- return false;
-}
-
-bool monitor_from_id(uint32_t id, coordinates_t *loc)
-{
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- if (m->id == id) {
- loc->monitor = m;
- loc->desktop = NULL;
- loc->node = NULL;
- return true;
- }
- }
- return false;
-}
-
-bool monitor_from_index(int idx, coordinates_t *loc)
-{
- for (monitor_t *m = mon_head; m != NULL; m = m->next, idx--) {
- if (idx == 1) {
- loc->monitor = m;
- loc->desktop = NULL;
- loc->node = NULL;
- return true;
- }
- }
- return false;
-}
-
-bool node_matches(coordinates_t *loc, coordinates_t *ref, node_select_t sel)
-{
- if (loc->node == NULL) {
- return false;
- }
-
- if (sel.focused != OPTION_NONE &&
- loc->node != loc->desktop->focus
- ? sel.focused == OPTION_TRUE
- : sel.focused == OPTION_FALSE) {
- return false;
- }
-
- if (sel.automatic != OPTION_NONE &&
- loc->node->presel != NULL
- ? sel.automatic == OPTION_TRUE
- : sel.automatic == OPTION_FALSE) {
- return false;
- }
-
- if (sel.local != OPTION_NONE &&
- loc->desktop != ref->desktop
- ? sel.local == OPTION_TRUE
- : sel.local == OPTION_FALSE) {
- return false;
- }
-
- if (sel.active != OPTION_NONE &&
- loc->desktop != loc->monitor->desk
- ? sel.active == OPTION_TRUE
- : sel.active == OPTION_FALSE) {
- return false;
- }
-
- if (sel.leaf != OPTION_NONE &&
- !is_leaf(loc->node)
- ? sel.leaf == OPTION_TRUE
- : sel.leaf == OPTION_FALSE) {
- return false;
- }
-
- if (sel.window != OPTION_NONE &&
- loc->node->client == NULL
- ? sel.window == OPTION_TRUE
- : sel.window == OPTION_FALSE) {
- return false;
- }
-
-#define NFLAG(p) \
- if (sel.p != OPTION_NONE && \
- !loc->node->p \
- ? sel.p == OPTION_TRUE \
- : sel.p == OPTION_FALSE) { \
- return false; \
- }
- NFLAG(hidden)
- NFLAG(sticky)
- NFLAG(private)
- NFLAG(locked)
-#undef WFLAG
-
- if (loc->node->client == NULL &&
- (sel.same_class != OPTION_NONE ||
- sel.tiled != OPTION_NONE ||
- sel.pseudo_tiled != OPTION_NONE ||
- sel.floating != OPTION_NONE ||
- sel.fullscreen != OPTION_NONE ||
- sel.below != OPTION_NONE ||
- sel.normal != OPTION_NONE ||
- sel.above != OPTION_NONE ||
- sel.urgent != OPTION_NONE)) {
- return false;
- }
-
- if (ref->node != NULL && ref->node->client != NULL &&
- sel.same_class != OPTION_NONE &&
- streq(loc->node->client->class_name, ref->node->client->class_name)
- ? sel.same_class == OPTION_FALSE
- : sel.same_class == OPTION_TRUE) {
- return false;
- }
-
- if (sel.descendant_of != OPTION_NONE &&
- !is_descendant(loc->node, ref->node)
- ? sel.descendant_of == OPTION_TRUE
- : sel.descendant_of == OPTION_FALSE) {
- return false;
- }
-
- if (sel.ancestor_of != OPTION_NONE &&
- !is_descendant(ref->node, loc->node)
- ? sel.ancestor_of == OPTION_TRUE
- : sel.ancestor_of == OPTION_FALSE) {
- return false;
- }
-
-#define WSTATE(p, e) \
- if (sel.p != OPTION_NONE && \
- loc->node->client->state != e \
- ? sel.p == OPTION_TRUE \
- : sel.p == OPTION_FALSE) { \
- return false; \
- }
- WSTATE(tiled, STATE_TILED)
- WSTATE(pseudo_tiled, STATE_PSEUDO_TILED)
- WSTATE(floating, STATE_FLOATING)
- WSTATE(fullscreen, STATE_FULLSCREEN)
-#undef WSTATE
-
-#define WLAYER(p, e) \
- if (sel.p != OPTION_NONE && \
- loc->node->client->layer != e \
- ? sel.p == OPTION_TRUE \
- : sel.p == OPTION_FALSE) { \
- return false; \
- }
- WLAYER(below, LAYER_BELOW)
- WLAYER(normal, LAYER_NORMAL)
- WLAYER(above, LAYER_ABOVE)
-#undef WLAYER
-
-#define WFLAG(p) \
- if (sel.p != OPTION_NONE && \
- !loc->node->client->p \
- ? sel.p == OPTION_TRUE \
- : sel.p == OPTION_FALSE) { \
- return false; \
- }
- WFLAG(urgent)
-#undef WFLAG
-
- return true;
-}
-
-bool desktop_matches(coordinates_t *loc, coordinates_t *ref, desktop_select_t sel)
-{
- if (sel.occupied != OPTION_NONE &&
- loc->desktop->root == NULL
- ? sel.occupied == OPTION_TRUE
- : sel.occupied == OPTION_FALSE) {
- return false;
- }
-
- if (sel.focused != OPTION_NONE &&
- loc->desktop != loc->monitor->desk
- ? sel.focused == OPTION_TRUE
- : sel.focused == OPTION_FALSE) {
- return false;
- }
-
- if (sel.urgent != OPTION_NONE &&
- !is_urgent(loc->desktop)
- ? sel.urgent == OPTION_TRUE
- : sel.urgent == OPTION_FALSE) {
- return false;
- }
-
- if (sel.local != OPTION_NONE &&
- ref->monitor != loc->monitor
- ? sel.local == OPTION_TRUE
- : sel.local == OPTION_FALSE) {
- return false;
- }
-
- return true;
-}
-
-bool monitor_matches(coordinates_t *loc, __attribute__((unused)) coordinates_t *ref, monitor_select_t sel)
-{
- if (sel.occupied != OPTION_NONE &&
- loc->monitor->desk->root == NULL
- ? sel.occupied == OPTION_TRUE
- : sel.occupied == OPTION_FALSE) {
- return false;
- }
-
- if (sel.focused != OPTION_NONE &&
- mon != loc->monitor
- ? sel.focused == OPTION_TRUE
- : sel.focused == OPTION_FALSE) {
- return false;
- }
-
- return true;
-}
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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.
- */
-
-#ifndef BSPWM_QUERY_H
-#define BSPWM_QUERY_H
-
-#define PTH_TOK "/"
-
-typedef enum {
- DOMAIN_TREE,
- DOMAIN_MONITOR,
- DOMAIN_DESKTOP,
- DOMAIN_NODE
-} domain_t;
-
-enum {
- SELECTOR_OK,
- SELECTOR_INVALID,
- SELECTOR_BAD_MODIFIERS,
- SELECTOR_BAD_DESCRIPTOR
-};
-
-typedef void (*monitor_printer_t)(monitor_t *m, FILE *rsp);
-typedef void (*desktop_printer_t)(desktop_t *m, FILE *rsp);
-
-void query_tree(FILE *rsp);
-void query_monitor(monitor_t *m, FILE *rsp);
-void query_desktop(desktop_t *d, FILE *rsp);
-void query_node(node_t *n, FILE *rsp);
-void query_presel(presel_t *p, FILE *rsp);
-void query_client(client_t *c, FILE *rsp);
-void query_rectangle(xcb_rectangle_t r, FILE *rsp);
-void query_padding(padding_t p, FILE *rsp);
-void query_history(FILE *rsp);
-void query_coordinates(coordinates_t *loc, FILE *rsp);
-void query_stack(FILE *rsp);
-int query_node_ids(coordinates_t *ref, coordinates_t *trg, node_select_t *sel, FILE *rsp);
-int query_node_ids_in(node_t *n, desktop_t *d, monitor_t *m, coordinates_t *ref, coordinates_t *trg, node_select_t *sel, FILE *rsp);
-int query_desktop_ids(coordinates_t *ref, coordinates_t *trg, desktop_select_t *sel, desktop_printer_t printer, FILE *rsp);
-int query_monitor_ids(coordinates_t *ref, coordinates_t *trg, monitor_select_t *sel, monitor_printer_t printer, FILE *rsp);
-void fprint_monitor_id(monitor_t *m, FILE *rsp);
-void fprint_monitor_name(monitor_t *m, FILE *rsp);
-void fprint_desktop_id(desktop_t *d, FILE *rsp);
-void fprint_desktop_name(desktop_t *d, FILE *rsp);
-void print_modifier_mask(uint16_t m, FILE *rsp);
-void print_pointer_action(pointer_action_t a, FILE *rsp);
-node_select_t make_node_select(void);
-desktop_select_t make_desktop_select(void);
-monitor_select_t make_monitor_select(void);
-int node_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst);
-int desktop_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst);
-int monitor_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst);
-bool locate_window(xcb_window_t win, coordinates_t *loc);
-bool locate_desktop(char *name, coordinates_t *loc);
-bool locate_monitor(char *name, coordinates_t *loc);
-bool desktop_from_id(uint32_t id, coordinates_t *loc, monitor_t *mm);
-bool desktop_from_index(uint16_t idx, coordinates_t *loc, monitor_t *mm);
-bool monitor_from_id(uint32_t id, coordinates_t *loc);
-bool monitor_from_index(int idx, coordinates_t *loc);
-bool node_matches(coordinates_t *loc, coordinates_t *ref, node_select_t sel);
-bool desktop_matches(coordinates_t *loc, coordinates_t *ref, desktop_select_t sel);
-bool monitor_matches(coordinates_t *loc, __attribute__((unused)) coordinates_t *ref, monitor_select_t sel);
-
-#endif
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include "bspwm.h"
-#include "desktop.h"
-#include "ewmh.h"
-#include "history.h"
-#include "pointer.h"
-#include "monitor.h"
-#include "query.h"
-#include "stack.h"
-#include "tree.h"
-#include "settings.h"
-#include "restore.h"
-#include "window.h"
-#include "parse.h"
-
-bool restore_tree(const char *file_path)
-{
- size_t jslen;
- char *json = read_string(file_path, &jslen);
-
- if (json == NULL) {
- return false;
- }
-
- int nbtok = 256;
- jsmn_parser parser;
- jsmntok_t *tokens = malloc(nbtok * sizeof(jsmntok_t));
-
- if (tokens == NULL) {
- perror("Restore tree: malloc");
- free(json);
- return false;
- }
-
- jsmn_init(&parser);
- int ret;
-
- while ((ret = jsmn_parse(&parser, json, jslen, tokens, nbtok)) == JSMN_ERROR_NOMEM) {
- nbtok *= 2;
- jsmntok_t *rtokens = realloc(tokens, nbtok * sizeof(jsmntok_t));
- if (rtokens == NULL) {
- perror("Restore tree: realloc");
- free(tokens);
- free(json);
- return false;
- } else {
- tokens = rtokens;
- }
- }
-
- if (ret < 0) {
- warn("Restore tree: jsmn_parse: ");
- switch (ret) {
- case JSMN_ERROR_NOMEM:
- warn("not enough memory.\n");
- break;
- case JSMN_ERROR_INVAL:
- warn("found invalid character inside JSON string.\n");
- break;
- case JSMN_ERROR_PART:
- warn("not a full JSON packet.\n");
- break;
- default:
- warn("unknown error.\n");
- break;
- }
-
- free(tokens);
- free(json);
-
- return false;
- }
-
- int num = tokens[0].size;
-
- if (num < 1) {
- free(tokens);
- free(json);
-
- return false;
- }
-
- mon = NULL;
- while (mon_head != NULL) {
- remove_monitor(mon_head);
- }
-
- jsmntok_t *t = tokens + 1;
- uint32_t focused_monitor_id = 0, primary_monitor_id = 0;
-
- for (int i = 0; i < num; i++) {
- if (keyeq("focusedMonitorId", t, json)) {
- t++;
- sscanf(json + t->start, "%u", &focused_monitor_id);
- } else if (keyeq("primaryMonitorId", t, json)) {
- t++;
- sscanf(json + t->start, "%u", &primary_monitor_id);
- } else if (keyeq("clientsCount", t, json)) {
- t++;
- sscanf(json + t->start, "%u", &clients_count);
- } else if (keyeq("monitors", t, json)) {
- t++;
- int s = t->size;
- t++;
- for (int j = 0; j < s; j++) {
- monitor_t *m = restore_monitor(&t, json);
- if (m->desk == NULL) {
- add_desktop(m, make_desktop(NULL, XCB_NONE));
- }
- add_monitor(m);
- }
- continue;
- } else if (keyeq("focusHistory", t, json)) {
- t++;
- restore_history(&t, json);
- continue;
- } else if (keyeq("stackingList", t, json)) {
- t++;
- restore_stack(&t, json);
- continue;
- }
- t++;
- }
-
- if (focused_monitor_id != 0) {
- coordinates_t loc;
- if (monitor_from_id(focused_monitor_id, &loc)) {
- mon = loc.monitor;
- }
- }
-
- if (primary_monitor_id != 0) {
- coordinates_t loc;
- if (monitor_from_id(primary_monitor_id, &loc)) {
- pri_mon = loc.monitor;
- }
- }
-
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
- refresh_presel_feedbacks(m, d, d->root);
- restack_presel_feedbacks(d);
- for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
- if (n->client == NULL) {
- continue;
- }
- initialize_client(n);
- uint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)};
- xcb_change_window_attributes(dpy, n->id, XCB_CW_EVENT_MASK, values);
- window_grab_buttons(n->id);
- }
- }
- }
-
- ewmh_update_client_list(false);
- ewmh_update_client_list(true);
- ewmh_update_active_window();
- ewmh_update_number_of_desktops();
- ewmh_update_current_desktop();
- ewmh_update_desktop_names();
- ewmh_update_desktop_viewport();
-
- free(tokens);
- free(json);
-
- return true;
-}
-
-#define RESTORE_INT(k, p) \
- } else if (keyeq(#k, *t, json)) { \
- (*t)++; \
- sscanf(json + (*t)->start, "%i", p);
-
-#define RESTORE_UINT(k, p) \
- } else if (keyeq(#k, *t, json)) { \
- (*t)++; \
- sscanf(json + (*t)->start, "%u", p);
-
-#define RESTORE_USINT(k, p) \
- } else if (keyeq(#k, *t, json)) { \
- (*t)++; \
- sscanf(json + (*t)->start, "%hu", p);
-
-#define RESTORE_DOUBLE(k, p) \
- } else if (keyeq(#k, *t, json)) { \
- (*t)++; \
- sscanf(json + (*t)->start, "%lf", p);
-
-#define RESTORE_ANY(k, p, f) \
- } else if (keyeq(#k, *t, json)) { \
- (*t)++; \
- char *val = copy_string(json + (*t)->start, (*t)->end - (*t)->start); \
- f(val, p); \
- free(val);
-
-#define RESTORE_BOOL(k, p) RESTORE_ANY(k, p, parse_bool)
-
-monitor_t *restore_monitor(jsmntok_t **t, char *json)
-{
- int num = (*t)->size;
- (*t)++;
- monitor_t *m = make_monitor(NULL, NULL, UINT32_MAX);
- uint32_t focused_desktop_id = 0;
-
- for (int i = 0; i < num; i++) {
- if (keyeq("name", *t, json)) {
- (*t)++;
- snprintf(m->name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
- RESTORE_UINT(id, &m->id)
- RESTORE_UINT(randrId, &m->randr_id)
- RESTORE_BOOL(wired, &m->wired)
- RESTORE_UINT(stickyCount, &m->sticky_count)
- RESTORE_INT(windowGap, &m->window_gap)
- RESTORE_UINT(borderWidth, &m->border_width)
- RESTORE_UINT(focusedDesktopId, &focused_desktop_id)
- } else if (keyeq("padding", *t, json)) {
- (*t)++;
- restore_padding(&m->padding, t, json);
- continue;
- } else if (keyeq("rectangle", *t, json)) {
- (*t)++;
- restore_rectangle(&m->rectangle, t, json);
- update_root(m, &m->rectangle);
- continue;
- } else if (keyeq("desktops", *t, json)) {
- (*t)++;
- int s = (*t)->size;
- (*t)++;
- for (int j = 0; j < s; j++) {
- desktop_t *d = restore_desktop(t, json);
- add_desktop(m, d);
- }
- continue;
- } else {
- warn("Restore monitor: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
- (*t)++;
- }
- (*t)++;
- }
-
- if (focused_desktop_id != 0) {
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
- if (d->id == focused_desktop_id) {
- m->desk = d;
- break;
- }
- }
- }
-
- return m;
-}
-
-desktop_t *restore_desktop(jsmntok_t **t, char *json)
-{
- int s = (*t)->size;
- (*t)++;
- desktop_t *d = make_desktop(NULL, UINT32_MAX);
- xcb_window_t focusedNodeId = XCB_NONE;
-
- for (int i = 0; i < s; i++) {
- if (keyeq("name", *t, json)) {
- (*t)++;
- snprintf(d->name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
- RESTORE_UINT(id, &d->id)
- RESTORE_ANY(layout, &d->layout, parse_layout)
- RESTORE_INT(windowGap, &d->window_gap)
- RESTORE_UINT(borderWidth, &d->border_width)
- } else if (keyeq("focusedNodeId", *t, json)) {
- (*t)++;
- sscanf(json + (*t)->start, "%u", &focusedNodeId);
- } else if (keyeq("padding", *t, json)) {
- (*t)++;
- restore_padding(&d->padding, t, json);
- continue;
- } else if (keyeq("root", *t, json)) {
- (*t)++;
- d->root = restore_node(t, json);
- continue;
- } else {
- warn("Restore desktop: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
- (*t)++;
- }
- (*t)++;
- }
-
- if (focusedNodeId != XCB_NONE) {
- d->focus = find_by_id_in(d->root, focusedNodeId);
- }
-
- return d;
-}
-
-node_t *restore_node(jsmntok_t **t, char *json)
-{
- if ((*t)->type == JSMN_PRIMITIVE) {
- (*t)++;
- return NULL;
- } else {
- int s = (*t)->size;
- (*t)++;
- /* hack to prevent a new ID from being generated */
- node_t *n = make_node(UINT32_MAX);
-
- for (int i = 0; i < s; i++) {
- if (keyeq("id", *t, json)) {
- (*t)++;
- sscanf(json + (*t)->start, "%u", &n->id);
- RESTORE_ANY(splitType, &n->split_type, parse_split_type)
- RESTORE_DOUBLE(splitRatio, &n->split_ratio)
- RESTORE_INT(birthRotation, &n->birth_rotation)
- RESTORE_BOOL(vacant, &n->vacant)
- RESTORE_BOOL(hidden, &n->hidden)
- RESTORE_BOOL(sticky, &n->sticky)
- RESTORE_BOOL(private, &n->private)
- RESTORE_BOOL(locked, &n->locked)
- } else if (keyeq("presel", *t, json)) {
- (*t)++;
- n->presel = restore_presel(t, json);
- continue;
- } else if (keyeq("rectangle", *t, json)) {
- (*t)++;
- restore_rectangle(&n->rectangle, t, json);
- continue;
- } else if (keyeq("firstChild", *t, json)) {
- (*t)++;
- node_t *fc = restore_node(t, json);
- n->first_child = fc;
- if (fc != NULL) {
- fc->parent = n;
- }
- continue;
- } else if (keyeq("secondChild", *t, json)) {
- (*t)++;
- node_t *sc = restore_node(t, json);
- n->second_child = sc;
- if (sc != NULL) {
- sc->parent = n;
- }
- continue;
- } else if (keyeq("client", *t, json)) {
- (*t)++;
- n->client = restore_client(t, json);
- continue;
- } else {
- warn("Restore node: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
- (*t)++;
- }
- (*t)++;
- }
-
- return n;
- }
-}
-
-presel_t *restore_presel(jsmntok_t **t, char *json)
-{
- if ((*t)->type == JSMN_PRIMITIVE) {
- (*t)++;
- return NULL;
- } else {
- int s = (*t)->size;
- (*t)++;
- presel_t *p = make_presel();
-
- for (int i = 0; i < s; i++) {
- if (keyeq("splitRatio", *t, json)) {
- (*t)++;
- sscanf(json + (*t)->start, "%lf", &p->split_ratio);
- RESTORE_ANY(splitDir, &p->split_dir, parse_direction)
- }
-
- (*t)++;
- }
-
- return p;
- }
-}
-
-
-client_t *restore_client(jsmntok_t **t, char *json)
-{
- if ((*t)->type == JSMN_PRIMITIVE) {
- (*t)++;
- return NULL;
- } else {
- int s = (*t)->size;
- (*t)++;
- client_t *c = make_client();
-
- for (int i = 0; i < s; i++) {
- if (keyeq("className", *t, json)) {
- (*t)++;
- snprintf(c->class_name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
- } else if (keyeq("instanceName", *t, json)) {
- (*t)++;
- snprintf(c->instance_name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
- RESTORE_ANY(state, &c->state, parse_client_state)
- RESTORE_ANY(lastState, &c->last_state, parse_client_state)
- RESTORE_ANY(layer, &c->layer, parse_stack_layer)
- RESTORE_ANY(lastLayer, &c->last_layer, parse_stack_layer)
- RESTORE_UINT(borderWidth, &c->border_width)
- RESTORE_BOOL(urgent, &c->urgent)
- RESTORE_BOOL(shown, &c->shown)
- } else if (keyeq("tiledRectangle", *t, json)) {
- (*t)++;
- restore_rectangle(&c->tiled_rectangle, t, json);
- continue;
- } else if (keyeq("floatingRectangle", *t, json)) {
- (*t)++;
- restore_rectangle(&c->floating_rectangle, t, json);
- continue;
- } else {
- warn("Restore client: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
- (*t)++;
- }
-
- (*t)++;
- }
-
- return c;
- }
-}
-
-void restore_rectangle(xcb_rectangle_t *r, jsmntok_t **t, char *json)
-{
- int s = (*t)->size;
- (*t)++;
-
- for (int i = 0; i < s; i++) {
- if (keyeq("x", *t, json)) {
- (*t)++;
- sscanf(json + (*t)->start, "%hi", &r->x);
- } else if (keyeq("y", *t, json)) {
- (*t)++;
- sscanf(json + (*t)->start, "%hi", &r->y);
- } else if (keyeq("width", *t, json)) {
- (*t)++;
- sscanf(json + (*t)->start, "%hu", &r->width);
- } else if (keyeq("height", *t, json)) {
- (*t)++;
- sscanf(json + (*t)->start, "%hu", &r->height);
- }
- (*t)++;
- }
-}
-
-void restore_padding(padding_t *p, jsmntok_t **t, char *json)
-{
- int s = (*t)->size;
- (*t)++;
-
- for (int i = 0; i < s; i++) {
- if (keyeq("top", *t, json)) {
- (*t)++;
- sscanf(json + (*t)->start, "%i", &p->top);
- } else if (keyeq("right", *t, json)) {
- (*t)++;
- sscanf(json + (*t)->start, "%i", &p->right);
- } else if (keyeq("bottom", *t, json)) {
- (*t)++;
- sscanf(json + (*t)->start, "%i", &p->bottom);
- } else if (keyeq("left", *t, json)) {
- (*t)++;
- sscanf(json + (*t)->start, "%i", &p->left);
- }
- (*t)++;
- }
-}
-
-void restore_history(jsmntok_t **t, char *json)
-{
- int s = (*t)->size;
- (*t)++;
-
- for (int i = 0; i < s; i++) {
- coordinates_t loc = {NULL, NULL, NULL};
- restore_coordinates(&loc, t, json);
- if (loc.monitor != NULL && loc.desktop != NULL) {
- history_add(loc.monitor, loc.desktop, loc.node);
- }
- }
-}
-
-void restore_coordinates(coordinates_t *loc, jsmntok_t **t, char *json)
-{
- int s = (*t)->size;
- (*t)++;
- uint32_t id = 0;
-
- for (int i = 0; i < s; i++) {
- if (keyeq("monitorId", *t, json)) {
- (*t)++;
- sscanf(json + (*t)->start, "%u", &id);
- loc->monitor = find_monitor(id);
- } else if (keyeq("desktopId", *t, json)) {
- (*t)++;
- sscanf(json + (*t)->start, "%u", &id);
- loc->desktop = find_desktop_in(id, loc->monitor);
- } else if (keyeq("nodeId", *t, json)) {
- (*t)++;
- sscanf(json + (*t)->start, "%u", &id);
- loc->node = find_by_id_in(loc->desktop != NULL ? loc->desktop->root : NULL, id);
- }
- (*t)++;
- }
-}
-
-void restore_stack(jsmntok_t **t, char *json)
-{
- int s = (*t)->size;
- (*t)++;
-
- for (int i = 0; i < s; i++) {
- uint32_t id;
- sscanf(json + (*t)->start, "%u", &id);
- coordinates_t loc;
- if (locate_window(id, &loc)) {
- stack_insert_after(stack_tail, loc.node);
- }
- (*t)++;
- }
-}
-
-#undef RESTORE_INT
-#undef RESTORE_UINT
-#undef RESTORE_USINT
-#undef RESTORE_DOUBLE
-#undef RESTORE_ANY
-#undef RESTORE_BOOL
-
-bool keyeq(char *s, jsmntok_t *key, char *json)
-{
- size_t n = key->end - key->start;
- return (strlen(s) == n && strncmp(s, json + key->start, n) == 0);
-}
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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.
- */
-
-#ifndef BSPWM_RESTORE_H
-#define BSPWM_RESTORE_H
-
-#include "jsmn.h"
-
-bool restore_tree(const char *file_path);
-monitor_t *restore_monitor(jsmntok_t **t, char *json);
-desktop_t *restore_desktop(jsmntok_t **t, char *json);
-node_t *restore_node(jsmntok_t **t, char *json);
-presel_t *restore_presel(jsmntok_t **t, char *json);
-client_t *restore_client(jsmntok_t **t, char *json);
-void restore_rectangle(xcb_rectangle_t *r, jsmntok_t **t, char *json);
-void restore_padding(padding_t *p, jsmntok_t **t, char *json);
-void restore_history(jsmntok_t **t, char *json);
-void restore_coordinates(coordinates_t *loc, jsmntok_t **t, char *json);
-void restore_stack(jsmntok_t **t, char *json);
-void restore_wm_state(xcb_atom_t *w, jsmntok_t **t, char *json);
-bool keyeq(char *s, jsmntok_t *key, char *json);
-
-#endif
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <sys/types.h>
-#include <string.h>
-#include <unistd.h>
-#include "bspwm.h"
-#include "ewmh.h"
-#include "window.h"
-#include "parse.h"
-#include "settings.h"
-#include "rule.h"
-
-rule_t *make_rule(void)
-{
- rule_t *r = calloc(1, sizeof(rule_t));
- r->class_name[0] = r->instance_name[0] = r->effect[0] = '\0';
- r->next = r->prev = NULL;
- r->one_shot = false;
- return r;
-}
-
-void add_rule(rule_t *r)
- {
- if (rule_head == NULL) {
- rule_head = rule_tail = r;
- } else {
- rule_tail->next = r;
- r->prev = rule_tail;
- rule_tail = r;
- }
-}
-
-void remove_rule(rule_t *r)
-{
- if (r == NULL)
- return;
- rule_t *prev = r->prev;
- rule_t *next = r->next;
- if (prev != NULL)
- prev->next = next;
- if (next != NULL)
- next->prev = prev;
- if (r == rule_head)
- rule_head = next;
- if (r == rule_tail)
- rule_tail = prev;
- free(r);
-}
-
-void remove_rule_by_cause(char *cause)
-{
- rule_t *r = rule_head;
- char *class_name = strtok(cause, COL_TOK);
- char *instance_name = strtok(NULL, COL_TOK);
- while (r != NULL) {
- rule_t *next = r->next;
- if ((streq(class_name, MATCH_ANY) || streq(r->class_name, class_name)) &&
- (instance_name == NULL || streq(instance_name, MATCH_ANY) || streq(r->instance_name, instance_name))) {
- remove_rule(r);
- }
- r = next;
- }
-}
-
-bool remove_rule_by_index(int idx)
-{
- for (rule_t *r = rule_head; r != NULL; r = r->next, idx--) {
- if (idx == 0) {
- remove_rule(r);
- return true;
- }
- }
- return false;
-}
-
-rule_consequence_t *make_rule_conquence(void)
-{
- rule_consequence_t *rc = calloc(1, sizeof(rule_consequence_t));
- rc->manage = rc->focus = rc->border = true;
- rc->layer = NULL;
- rc->state = NULL;
- return rc;
-}
-
-pending_rule_t *make_pending_rule(int fd, xcb_window_t win, rule_consequence_t *csq)
-{
- pending_rule_t *pr = calloc(1, sizeof(pending_rule_t));
- pr->prev = pr->next = NULL;
- pr->fd = fd;
- pr->win = win;
- pr->csq = csq;
- return pr;
-}
-
-void add_pending_rule(pending_rule_t *pr)
-{
- if (pr == NULL) {
- return;
- }
- if (pending_rule_head == NULL) {
- pending_rule_head = pending_rule_tail = pr;
- } else {
- pending_rule_tail->next = pr;
- pr->prev = pending_rule_tail;
- pending_rule_tail = pr;
- }
-}
-
-void remove_pending_rule(pending_rule_t *pr)
-{
- if (pr == NULL) {
- return;
- }
- pending_rule_t *a = pr->prev;
- pending_rule_t *b = pr->next;
- if (a != NULL) {
- a->next = b;
- }
- if (b != NULL) {
- b->prev = a;
- }
- if (pr == pending_rule_head) {
- pending_rule_head = b;
- }
- if (pr == pending_rule_tail) {
- pending_rule_tail = a;
- }
- close(pr->fd);
- free(pr->csq);
- free(pr);
-}
-
-void apply_rules(xcb_window_t win, rule_consequence_t *csq)
-{
- xcb_ewmh_get_atoms_reply_t win_type;
-
- if (xcb_ewmh_get_wm_window_type_reply(ewmh, xcb_ewmh_get_wm_window_type(ewmh, win), &win_type, NULL) == 1) {
- for (unsigned int i = 0; i < win_type.atoms_len; i++) {
- xcb_atom_t a = win_type.atoms[i];
- if (a == ewmh->_NET_WM_WINDOW_TYPE_TOOLBAR ||
- a == ewmh->_NET_WM_WINDOW_TYPE_UTILITY) {
- csq->focus = false;
- } else if (a == ewmh->_NET_WM_WINDOW_TYPE_DIALOG) {
- if (csq->state == NULL) {
- csq->state = calloc(1, sizeof(client_state_t));
- }
- *(csq->state) = STATE_FLOATING;
- csq->center = true;
- } else if (a == ewmh->_NET_WM_WINDOW_TYPE_DOCK ||
- a == ewmh->_NET_WM_WINDOW_TYPE_DESKTOP ||
- a == ewmh->_NET_WM_WINDOW_TYPE_NOTIFICATION) {
- csq->manage = false;
- if (a == ewmh->_NET_WM_WINDOW_TYPE_DESKTOP) {
- window_lower(win);
- }
- }
- }
- xcb_ewmh_get_atoms_reply_wipe(&win_type);
- }
-
- xcb_ewmh_get_atoms_reply_t win_state;
-
- if (xcb_ewmh_get_wm_state_reply(ewmh, xcb_ewmh_get_wm_state(ewmh, win), &win_state, NULL) == 1) {
- for (unsigned int i = 0; i < win_state.atoms_len; i++) {
- xcb_atom_t a = win_state.atoms[i];
- if (a == ewmh->_NET_WM_STATE_FULLSCREEN) {
- if (csq->state == NULL) {
- csq->state = calloc(1, sizeof(client_state_t));
- }
- *(csq->state) = STATE_FULLSCREEN;
- } else if (a == ewmh->_NET_WM_STATE_BELOW) {
- if (csq->layer == NULL) {
- csq->layer = calloc(1, sizeof(stack_layer_t));
- }
- *(csq->layer) = LAYER_BELOW;
- } else if (a == ewmh->_NET_WM_STATE_ABOVE) {
- if (csq->layer == NULL) {
- csq->layer = calloc(1, sizeof(stack_layer_t));
- }
- *(csq->layer) = LAYER_ABOVE;
- } else if (a == ewmh->_NET_WM_STATE_STICKY) {
- csq->sticky = true;
- }
- }
- xcb_ewmh_get_atoms_reply_wipe(&win_state);
- }
-
- xcb_window_t transient_for = XCB_NONE;
- xcb_icccm_get_wm_transient_for_reply(dpy, xcb_icccm_get_wm_transient_for(dpy, win), &transient_for, NULL);
- if (transient_for != XCB_NONE) {
- if (csq->state == NULL) {
- csq->state = calloc(1, sizeof(client_state_t));
- }
- *(csq->state) = STATE_FLOATING;
- }
-
- xcb_size_hints_t size_hints;
- if (xcb_icccm_get_wm_normal_hints_reply(dpy, xcb_icccm_get_wm_normal_hints(dpy, win), &size_hints, NULL) == 1) {
- if ((size_hints.flags & (XCB_ICCCM_SIZE_HINT_P_MIN_SIZE|XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)) &&
- size_hints.min_width == size_hints.max_width && size_hints.min_height == size_hints.max_height) {
- if (csq->state == NULL) {
- csq->state = calloc(1, sizeof(client_state_t));
- }
- *(csq->state) = STATE_FLOATING;
- }
- }
-
- xcb_icccm_get_wm_class_reply_t reply;
- if (xcb_icccm_get_wm_class_reply(dpy, xcb_icccm_get_wm_class(dpy, win), &reply, NULL) == 1) {
- snprintf(csq->class_name, sizeof(csq->class_name), "%s", reply.class_name);
- snprintf(csq->instance_name, sizeof(csq->instance_name), "%s", reply.instance_name);
- xcb_icccm_get_wm_class_reply_wipe(&reply);
- }
-
- rule_t *rule = rule_head;
- while (rule != NULL) {
- rule_t *next = rule->next;
- if ((streq(rule->class_name, MATCH_ANY) || streq(rule->class_name, csq->class_name)) &&
- (streq(rule->instance_name, MATCH_ANY) || streq(rule->instance_name, csq->instance_name))) {
- char effect[MAXLEN];
- snprintf(effect, sizeof(effect), "%s", rule->effect);
- char *key = strtok(effect, CSQ_BLK);
- char *value = strtok(NULL, CSQ_BLK);
- while (key != NULL && value != NULL) {
- parse_key_value(key, value, csq);
- key = strtok(NULL, CSQ_BLK);
- value = strtok(NULL, CSQ_BLK);
- }
- if (rule->one_shot) {
- remove_rule(rule);
- break;
- }
- }
- rule = next;
- }
-}
-
-bool schedule_rules(xcb_window_t win, rule_consequence_t *csq)
-{
- if (external_rules_command[0] == '\0') {
- return false;
- }
- int fds[2];
- if (pipe(fds) == -1) {
- return false;
- }
- pid_t pid = fork();
- if (pid == 0) {
- if (dpy != NULL) {
- close(xcb_get_file_descriptor(dpy));
- }
- dup2(fds[1], 1);
- close(fds[0]);
- char wid[SMALEN];
- snprintf(wid, sizeof(wid), "%i", win);
- setsid();
- execl(external_rules_command, external_rules_command, wid, csq->class_name, csq->instance_name, csq->monitor_desc, csq->desktop_desc, csq->node_desc, NULL);
- err("Couldn't spawn rule command.\n");
- } else if (pid > 0) {
- close(fds[1]);
- pending_rule_t *pr = make_pending_rule(fds[0], win, csq);
- add_pending_rule(pr);
- }
- return (pid != -1);
-}
-
-void parse_rule_consequence(int fd, rule_consequence_t *csq)
-{
- if (fd == -1) {
- return;
- }
- char data[BUFSIZ];
- int nb;
- while ((nb = read(fd, data, sizeof(data))) > 0) {
- int end = MIN(nb, (int) sizeof(data) - 1);
- data[end] = '\0';
- char *key = strtok(data, CSQ_BLK);
- char *value = strtok(NULL, CSQ_BLK);
- while (key != NULL && value != NULL) {
- parse_key_value(key, value, csq);
- key = strtok(NULL, CSQ_BLK);
- value = strtok(NULL, CSQ_BLK);
- }
- }
-}
-
-void parse_key_value(char *key, char *value, rule_consequence_t *csq)
-{
- bool v;
- if (streq("monitor", key)) {
- snprintf(csq->monitor_desc, sizeof(csq->monitor_desc), "%s", value);
- } else if (streq("desktop", key)) {
- snprintf(csq->desktop_desc, sizeof(csq->desktop_desc), "%s", value);
- } else if (streq("node", key)) {
- snprintf(csq->node_desc, sizeof(csq->node_desc), "%s", value);
- } else if (streq("split_dir", key)) {
- snprintf(csq->split_dir, sizeof(csq->split_dir), "%s", value);
- } else if (streq("state", key)) {
- client_state_t cst;
- if (parse_client_state(value, &cst)) {
- if (csq->state == NULL) {
- csq->state = calloc(1, sizeof(client_state_t));
- }
- *(csq->state) = cst;
- }
- } else if (streq("layer", key)) {
- stack_layer_t lyr;
- if (parse_stack_layer(value, &lyr)) {
- if (csq->layer == NULL) {
- csq->layer = calloc(1, sizeof(stack_layer_t));
- }
- *(csq->layer) = lyr;
- }
- } else if (streq("split_ratio", key)) {
- double rat;
- if (sscanf(value, "%lf", &rat) == 1 && rat > 0 && rat < 1) {
- csq->split_ratio = rat;
- }
- } else if (parse_bool(value, &v)) {
- if (streq("hidden", key))
- csq->hidden = true;
-#define SETCSQ(name) \
- else if (streq(#name, key)) \
- csq->name = v;
- SETCSQ(sticky)
- SETCSQ(private)
- SETCSQ(locked)
- SETCSQ(center)
- SETCSQ(follow)
- SETCSQ(manage)
- SETCSQ(focus)
- SETCSQ(border)
-#undef SETCSQ
- }
-}
-
-void list_rules(FILE *rsp)
-{
- for (rule_t *r = rule_head; r != NULL; r = r->next) {
- fprintf(rsp, "%s:%s %c> %s\n", r->class_name, r->instance_name, r->one_shot?'-':'=', r->effect);
- }
-}
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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.
- */
-
-#ifndef BSPWM_RULE_H
-#define BSPWM_RULE_H
-
-#define MATCH_ANY "*"
-#define CSQ_BLK " =,\n"
-
-rule_t *make_rule(void);
-void add_rule(rule_t *r);
-void remove_rule(rule_t *r);
-void remove_rule_by_cause(char *cause);
-bool remove_rule_by_index(int idx);
-rule_consequence_t *make_rule_conquence(void);
-pending_rule_t *make_pending_rule(int fd, xcb_window_t win, rule_consequence_t *csq);
-void add_pending_rule(pending_rule_t *pr);
-void remove_pending_rule(pending_rule_t *pr);
-void apply_rules(xcb_window_t win, rule_consequence_t *csq);
-bool schedule_rules(xcb_window_t win, rule_consequence_t *csq);
-void parse_rule_consequence(int fd, rule_consequence_t *csq);
-void parse_key_value(char *key, char *value, rule_consequence_t *csq);
-void list_rules(FILE *rsp);
-
-#endif
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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 <stdio.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include "bspwm.h"
-#include "settings.h"
-
-void run_config(void)
-{
- if (fork() == 0) {
- if (dpy != NULL) {
- close(xcb_get_file_descriptor(dpy));
- }
- setsid();
- execl(config_path, config_path, NULL);
- err("Couldn't execute the configuration file.\n");
- }
-}
-
-void load_settings(void)
-{
- snprintf(external_rules_command, sizeof(external_rules_command), "%s", EXTERNAL_RULES_COMMAND);
- snprintf(status_prefix, sizeof(status_prefix), "%s", STATUS_PREFIX);
-
- snprintf(normal_border_color, sizeof(normal_border_color), "%s", NORMAL_BORDER_COLOR);
- snprintf(active_border_color, sizeof(active_border_color), "%s", ACTIVE_BORDER_COLOR);
- snprintf(focused_border_color, sizeof(focused_border_color), "%s", FOCUSED_BORDER_COLOR);
- snprintf(presel_feedback_color, sizeof(presel_feedback_color), "%s", PRESEL_FEEDBACK_COLOR);
-
- padding = (padding_t) PADDING;
- window_gap = WINDOW_GAP;
- border_width = BORDER_WIDTH;
- split_ratio = SPLIT_RATIO;
- initial_polarity = FIRST_CHILD;
- pointer_modifier = POINTER_MODIFIER;
- pointer_motion_interval = POINTER_MOTION_INTERVAL;
-
- pointer_actions[0] = ACTION_MOVE;
- pointer_actions[1] = ACTION_RESIZE_SIDE;
- pointer_actions[2] = ACTION_RESIZE_CORNER;
-
- borderless_monocle = BORDERLESS_MONOCLE;
- gapless_monocle = GAPLESS_MONOCLE;
- paddingless_monocle = PADDINGLESS_MONOCLE;
- single_monocle = SINGLE_MONOCLE;
- focus_follows_pointer = FOCUS_FOLLOWS_POINTER;
- pointer_follows_focus = POINTER_FOLLOWS_FOCUS;
- pointer_follows_monitor = POINTER_FOLLOWS_MONITOR;
- ignore_ewmh_focus = IGNORE_EWMH_FOCUS;
- center_pseudo_tiled = CENTER_PSEUDO_TILED;
- click_to_focus = CLICK_TO_FOCUS;
- swallow_first_click = SWALLOW_FIRST_CLICK;
- honor_size_hints = HONOR_SIZE_HINTS;
- remove_disabled_monitors = REMOVE_DISABLED_MONITORS;
- remove_unplugged_monitors = REMOVE_UNPLUGGED_MONITORS;
- merge_overlapping_monitors = MERGE_OVERLAPPING_MONITORS;
-}
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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.
- */
-
-#ifndef BSPWM_SETTINGS_H
-#define BSPWM_SETTINGS_H
-
-#include "types.h"
-
-#define WM_NAME "bspwm"
-#define CONFIG_NAME WM_NAME "rc"
-#define CONFIG_HOME_ENV "XDG_CONFIG_HOME"
-#define POINTER_MODIFIER XCB_MOD_MASK_4
-#define POINTER_MOTION_INTERVAL 17
-#define EXTERNAL_RULES_COMMAND ""
-#define STATUS_PREFIX "W"
-
-#define NORMAL_BORDER_COLOR "#30302f"
-#define ACTIVE_BORDER_COLOR "#474645"
-#define FOCUSED_BORDER_COLOR "#817f7f"
-#define PRESEL_FEEDBACK_COLOR "#f4d775"
-
-#define PADDING {0, 0, 0, 0}
-#define WINDOW_GAP 6
-#define BORDER_WIDTH 1
-#define SPLIT_RATIO 0.5
-
-#define BORDERLESS_MONOCLE false
-#define GAPLESS_MONOCLE false
-#define PADDINGLESS_MONOCLE false
-#define SINGLE_MONOCLE false
-#define FOCUS_FOLLOWS_POINTER false
-#define POINTER_FOLLOWS_FOCUS false
-#define POINTER_FOLLOWS_MONITOR false
-#define IGNORE_EWMH_FOCUS false
-#define CENTER_PSEUDO_TILED true
-#define CLICK_TO_FOCUS false
-#define SWALLOW_FIRST_CLICK false
-#define HONOR_SIZE_HINTS false
-#define REMOVE_DISABLED_MONITORS false
-#define REMOVE_UNPLUGGED_MONITORS false
-#define MERGE_OVERLAPPING_MONITORS false
-
-char external_rules_command[MAXLEN];
-char status_prefix[MAXLEN];
-
-char normal_border_color[MAXLEN];
-char active_border_color[MAXLEN];
-char focused_border_color[MAXLEN];
-char presel_feedback_color[MAXLEN];
-
-padding_t padding;
-int window_gap;
-unsigned int border_width;
-double split_ratio;
-
-child_polarity_t initial_polarity;
-uint16_t pointer_modifier;
-uint32_t pointer_motion_interval;
-pointer_action_t pointer_actions[3];
-
-bool borderless_monocle;
-bool gapless_monocle;
-bool paddingless_monocle;
-bool single_monocle;
-bool focus_follows_pointer;
-bool pointer_follows_focus;
-bool pointer_follows_monitor;
-bool ignore_ewmh_focus;
-bool center_pseudo_tiled;
-bool click_to_focus;
-bool swallow_first_click;
-bool honor_size_hints;
-bool remove_disabled_monitors;
-bool remove_unplugged_monitors;
-bool merge_overlapping_monitors;
-
-void run_config(void);
-void load_settings(void);
-
-#endif
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include "helpers.h"
+#include "common.h"
+
+int main(int argc, char *argv[])
+{
+ int fd;
+ struct sockaddr_un sock_address;
+ char msg[BUFSIZ], rsp[BUFSIZ];
+
+ if (argc < 2) {
+ err("No arguments given.\n");
+ }
+
+ sock_address.sun_family = AF_UNIX;
+ char *sp;
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+ err("Failed to create the socket.\n");
+ }
+
+ sp = getenv(SOCKET_ENV_VAR);
+ if (sp != NULL) {
+ snprintf(sock_address.sun_path, sizeof(sock_address.sun_path), "%s", sp);
+ } else {
+ char *host = NULL;
+ int dn = 0, sn = 0;
+ if (xcb_parse_display(NULL, &host, &dn, &sn) != 0) {
+ snprintf(sock_address.sun_path, sizeof(sock_address.sun_path), SOCKET_PATH_TPL, host, dn, sn);
+ }
+ free(host);
+ }
+
+ if (connect(fd, (struct sockaddr *) &sock_address, sizeof(sock_address)) == -1) {
+ err("Failed to connect to the socket.\n");
+ }
+
+ argc--, argv++;
+ int msg_len = 0;
+
+ for (int offset = 0, rem = sizeof(msg), n = 0; argc > 0 && rem > 0; offset += n, rem -= n, argc--, argv++) {
+ n = snprintf(msg + offset, rem, "%s%c", *argv, 0);
+ msg_len += n;
+ }
+
+ if (send(fd, msg, msg_len, 0) == -1) {
+ err("Failed to send the data.\n");
+ }
+
+ int ret = EXIT_SUCCESS, nb;
+ while ((nb = recv(fd, rsp, sizeof(rsp)-1, 0)) > 0) {
+ rsp[nb] = '\0';
+ if (rsp[0] == FAILURE_MESSAGE[0]) {
+ ret = EXIT_FAILURE;
+ printf("%s", rsp + 1);
+ } else {
+ printf("%s", rsp);
+ }
+ fflush(stdout);
+ }
+
+ close(fd);
+ return ret;
+}
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <xcb/xinerama.h>
+#include "types.h"
+#include "desktop.h"
+#include "monitor.h"
+#include "settings.h"
+#include "messages.h"
+#include "pointer.h"
+#include "events.h"
+#include "common.h"
+#include "window.h"
+#include "history.h"
+#include "ewmh.h"
+#include "rule.h"
+#include "bspwm.h"
+
+int main(int argc, char *argv[])
+{
+ fd_set descriptors;
+ char socket_path[MAXLEN];
+ config_path[0] = '\0';
+ int sock_fd, cli_fd, dpy_fd, max_fd, n;
+ struct sockaddr_un sock_address;
+ char msg[BUFSIZ] = {0};
+ xcb_generic_event_t *event;
+ int opt;
+
+ while ((opt = getopt(argc, argv, "hvc:")) != -1) {
+ switch (opt) {
+ case 'h':
+ printf(WM_NAME " [-h|-v|-c CONFIG_PATH]\n");
+ exit(EXIT_SUCCESS);
+ break;
+ case 'v':
+ printf("%s\n", VERSION);
+ exit(EXIT_SUCCESS);
+ break;
+ case 'c':
+ snprintf(config_path, sizeof(config_path), "%s", optarg);
+ break;
+ }
+ }
+
+ if (config_path[0] == '\0') {
+ char *config_home = getenv(CONFIG_HOME_ENV);
+ if (config_home != NULL) {
+ snprintf(config_path, sizeof(config_path), "%s/%s/%s", config_home, WM_NAME, CONFIG_NAME);
+ } else {
+ snprintf(config_path, sizeof(config_path), "%s/%s/%s/%s", getenv("HOME"), ".config", WM_NAME, CONFIG_NAME);
+ }
+ }
+
+ dpy = xcb_connect(NULL, &default_screen);
+
+ if (!check_connection(dpy)) {
+ exit(EXIT_FAILURE);
+ }
+
+ load_settings();
+ setup();
+
+ dpy_fd = xcb_get_file_descriptor(dpy);
+
+ char *sp = getenv(SOCKET_ENV_VAR);
+ if (sp != NULL) {
+ snprintf(socket_path, sizeof(socket_path), "%s", sp);
+ } else {
+ char *host = NULL;
+ int dn = 0, sn = 0;
+ if (xcb_parse_display(NULL, &host, &dn, &sn) != 0) {
+ snprintf(socket_path, sizeof(socket_path), SOCKET_PATH_TPL, host, dn, sn);
+ }
+ free(host);
+ }
+
+ sock_address.sun_family = AF_UNIX;
+ snprintf(sock_address.sun_path, sizeof(sock_address.sun_path), "%s", socket_path);
+
+ sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+
+ if (sock_fd == -1) {
+ err("Couldn't create the socket.\n");
+ }
+
+ unlink(socket_path);
+ if (bind(sock_fd, (struct sockaddr *) &sock_address, sizeof(sock_address)) == -1) {
+ err("Couldn't bind a name to the socket.\n");
+ }
+
+ if (listen(sock_fd, SOMAXCONN) == -1) {
+ err("Couldn't listen to the socket.\n");
+ }
+
+ signal(SIGINT, sig_handler);
+ signal(SIGHUP, sig_handler);
+ signal(SIGTERM, sig_handler);
+ signal(SIGCHLD, sig_handler);
+ signal(SIGPIPE, SIG_IGN);
+ run_config();
+ running = true;
+
+ while (running) {
+
+ xcb_flush(dpy);
+
+ FD_ZERO(&descriptors);
+ FD_SET(sock_fd, &descriptors);
+ FD_SET(dpy_fd, &descriptors);
+ max_fd = MAX(sock_fd, dpy_fd);
+
+ for (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {
+ FD_SET(pr->fd, &descriptors);
+ if (pr->fd > max_fd) {
+ max_fd = pr->fd;
+ }
+ }
+
+ if (select(max_fd + 1, &descriptors, NULL, NULL, NULL) > 0) {
+
+ pending_rule_t *pr = pending_rule_head;
+ while (pr != NULL) {
+ pending_rule_t *next = pr->next;
+ if (FD_ISSET(pr->fd, &descriptors)) {
+ manage_window(pr->win, pr->csq, pr->fd);
+ remove_pending_rule(pr);
+ }
+ pr = next;
+ }
+
+ if (FD_ISSET(sock_fd, &descriptors)) {
+ cli_fd = accept(sock_fd, NULL, 0);
+ if (cli_fd > 0 && (n = recv(cli_fd, msg, sizeof(msg), 0)) > 0) {
+ msg[n] = '\0';
+ FILE *rsp = fdopen(cli_fd, "w");
+ if (rsp != NULL) {
+ handle_message(msg, n, rsp);
+ } else {
+ warn("Can't open the client socket as file.\n");
+ close(cli_fd);
+ }
+ }
+ }
+
+ if (FD_ISSET(dpy_fd, &descriptors)) {
+ while ((event = xcb_poll_for_event(dpy)) != NULL) {
+ handle_event(event);
+ free(event);
+ }
+ }
+
+ }
+
+ if (!check_connection(dpy)) {
+ running = false;
+ }
+ }
+
+ cleanup();
+ close(sock_fd);
+ unlink(socket_path);
+ ungrab_buttons();
+ xcb_ewmh_connection_wipe(ewmh);
+ xcb_destroy_window(dpy, meta_window);
+ free(ewmh);
+ xcb_flush(dpy);
+ xcb_disconnect(dpy);
+ return exit_status;
+}
+
+void init(void)
+{
+ clients_count = 0;
+ mon = mon_head = mon_tail = pri_mon = NULL;
+ history_head = history_tail = history_needle = NULL;
+ rule_head = rule_tail = NULL;
+ stack_head = stack_tail = NULL;
+ subscribe_head = subscribe_tail = NULL;
+ pending_rule_head = pending_rule_tail = NULL;
+ auto_raise = sticky_still = record_history = true;
+ randr_base = 0;
+ exit_status = 0;
+}
+
+void setup(void)
+{
+ init();
+ ewmh_init();
+ pointer_init();
+
+ screen = xcb_setup_roots_iterator(xcb_get_setup(dpy)).data;
+
+ if (screen == NULL) {
+ err("Can't acquire the default screen.\n");
+ }
+
+ root = screen->root;
+ register_events();
+
+ screen_width = screen->width_in_pixels;
+ screen_height = screen->height_in_pixels;
+
+ meta_window = xcb_generate_id(dpy);
+ xcb_create_window(dpy, XCB_COPY_FROM_PARENT, meta_window, root, -1, -1, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY, XCB_COPY_FROM_PARENT, XCB_NONE, NULL);
+ xcb_icccm_set_wm_class(dpy, meta_window, sizeof(META_WINDOW_IC), META_WINDOW_IC);
+
+ xcb_atom_t net_atoms[] = {ewmh->_NET_SUPPORTED,
+ ewmh->_NET_SUPPORTING_WM_CHECK,
+ ewmh->_NET_DESKTOP_NAMES,
+ ewmh->_NET_DESKTOP_VIEWPORT,
+ ewmh->_NET_NUMBER_OF_DESKTOPS,
+ ewmh->_NET_CURRENT_DESKTOP,
+ ewmh->_NET_CLIENT_LIST,
+ ewmh->_NET_ACTIVE_WINDOW,
+ ewmh->_NET_CLOSE_WINDOW,
+ ewmh->_NET_WM_STRUT_PARTIAL,
+ ewmh->_NET_WM_DESKTOP,
+ ewmh->_NET_WM_STATE,
+ ewmh->_NET_WM_STATE_HIDDEN,
+ ewmh->_NET_WM_STATE_FULLSCREEN,
+ ewmh->_NET_WM_STATE_BELOW,
+ ewmh->_NET_WM_STATE_ABOVE,
+ ewmh->_NET_WM_STATE_STICKY,
+ ewmh->_NET_WM_STATE_DEMANDS_ATTENTION,
+ ewmh->_NET_WM_WINDOW_TYPE,
+ ewmh->_NET_WM_WINDOW_TYPE_DOCK,
+ ewmh->_NET_WM_WINDOW_TYPE_DESKTOP,
+ ewmh->_NET_WM_WINDOW_TYPE_NOTIFICATION,
+ ewmh->_NET_WM_WINDOW_TYPE_DIALOG,
+ ewmh->_NET_WM_WINDOW_TYPE_UTILITY,
+ ewmh->_NET_WM_WINDOW_TYPE_TOOLBAR};
+
+ xcb_ewmh_set_supported(ewmh, default_screen, LENGTH(net_atoms), net_atoms);
+ ewmh_set_supporting(meta_window);
+
+#define GETATOM(a) \
+ get_atom(#a, &a);
+ GETATOM(WM_STATE)
+ GETATOM(WM_DELETE_WINDOW)
+ GETATOM(WM_TAKE_FOCUS)
+#undef GETATOM
+
+ const xcb_query_extension_reply_t *qep = xcb_get_extension_data(dpy, &xcb_randr_id);
+ if (qep->present && update_monitors()) {
+ randr = true;
+ randr_base = qep->first_event;
+ xcb_randr_select_input(dpy, root, XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE);
+ } else {
+ randr = false;
+ warn("Couldn't retrieve monitors via RandR.\n");
+ bool xinerama_is_active = false;
+
+ if (xcb_get_extension_data(dpy, &xcb_xinerama_id)->present) {
+ xcb_xinerama_is_active_reply_t *xia = xcb_xinerama_is_active_reply(dpy, xcb_xinerama_is_active(dpy), NULL);
+ if (xia != NULL) {
+ xinerama_is_active = xia->state;
+ free(xia);
+ }
+ }
+
+ if (xinerama_is_active) {
+ xcb_xinerama_query_screens_reply_t *xsq = xcb_xinerama_query_screens_reply(dpy, xcb_xinerama_query_screens(dpy), NULL);
+ xcb_xinerama_screen_info_t *xsi = xcb_xinerama_query_screens_screen_info(xsq);
+ int n = xcb_xinerama_query_screens_screen_info_length(xsq);
+ for (int i = 0; i < n; i++) {
+ xcb_xinerama_screen_info_t info = xsi[i];
+ xcb_rectangle_t rect = (xcb_rectangle_t) {info.x_org, info.y_org, info.width, info.height};
+ monitor_t *m = make_monitor(NULL, &rect, XCB_NONE);
+ add_monitor(m);
+ add_desktop(m, make_desktop(NULL, XCB_NONE));
+ }
+ free(xsq);
+ } else {
+ warn("Xinerama is inactive.\n");
+ xcb_rectangle_t rect = (xcb_rectangle_t) {0, 0, screen_width, screen_height};
+ monitor_t *m = make_monitor(NULL, &rect, XCB_NONE);
+ add_monitor(m);
+ add_desktop(m, make_desktop(NULL, XCB_NONE));
+ }
+ }
+
+ ewmh_update_number_of_desktops();
+ ewmh_update_desktop_names();
+ ewmh_update_desktop_viewport();
+ ewmh_update_current_desktop();
+ xcb_get_input_focus_reply_t *ifo = xcb_get_input_focus_reply(dpy, xcb_get_input_focus(dpy), NULL);
+ if (ifo != NULL && (ifo->focus == XCB_INPUT_FOCUS_POINTER_ROOT || ifo->focus == XCB_NONE)) {
+ clear_input_focus();
+ }
+ free(ifo);
+}
+
+void register_events(void)
+{
+ uint32_t values[] = {ROOT_EVENT_MASK};
+ xcb_generic_error_t *e = xcb_request_check(dpy, xcb_change_window_attributes_checked(dpy, root, XCB_CW_EVENT_MASK, values));
+ if (e != NULL) {
+ xcb_disconnect(dpy);
+ err("Another window manager is already running.\n");
+ }
+}
+
+void cleanup(void)
+{
+ mon = NULL;
+
+ while (mon_head != NULL) {
+ remove_monitor(mon_head);
+ }
+ while (rule_head != NULL) {
+ remove_rule(rule_head);
+ }
+ while (subscribe_head != NULL) {
+ remove_subscriber(subscribe_head);
+ }
+ while (pending_rule_head != NULL) {
+ remove_pending_rule(pending_rule_head);
+ }
+
+ empty_history();
+}
+
+bool check_connection (xcb_connection_t *dpy)
+{
+ int xerr;
+ if ((xerr = xcb_connection_has_error(dpy)) != 0) {
+ warn("The server closed the connection: ");
+ switch (xerr) {
+ case XCB_CONN_ERROR:
+ warn("socket, pipe or stream error.\n");
+ break;
+ case XCB_CONN_CLOSED_EXT_NOTSUPPORTED:
+ warn("unsupported extension.\n");
+ break;
+ case XCB_CONN_CLOSED_MEM_INSUFFICIENT:
+ warn("not enough memory.\n");
+ break;
+ case XCB_CONN_CLOSED_REQ_LEN_EXCEED:
+ warn("request length exceeded.\n");
+ break;
+ case XCB_CONN_CLOSED_PARSE_ERR:
+ warn("can't parse display string.\n");
+ break;
+ case XCB_CONN_CLOSED_INVALID_SCREEN:
+ warn("invalid screen.\n");
+ break;
+ case XCB_CONN_CLOSED_FDPASSING_FAILED:
+ warn("failed to pass FD.\n");
+ break;
+ default:
+ warn("unknown error.\n");
+ break;
+ }
+ return false;
+ } else {
+ return true;
+ }
+}
+
+void sig_handler(int sig)
+{
+ if (sig == SIGCHLD) {
+ signal(sig, sig_handler);
+ while (waitpid(-1, 0, WNOHANG) > 0)
+ ;
+ } else if (sig == SIGINT || sig == SIGHUP || sig == SIGTERM) {
+ running = false;
+ }
+}
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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.
+ */
+
+#ifndef BSPWM_BSPWM_H
+#define BSPWM_BSPWM_H
+
+#include "types.h"
+
+#define ROOT_EVENT_MASK (XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_BUTTON_PRESS)
+#define CLIENT_EVENT_MASK (XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_FOCUS_CHANGE)
+#define BSPWM_CLASS_NAME "Bspwm"
+#define META_WINDOW_IC "wm\0" BSPWM_CLASS_NAME
+#define ROOT_WINDOW_IC "root\0" BSPWM_CLASS_NAME
+#define PRESEL_FEEDBACK_I "presel_feedback"
+#define PRESEL_FEEDBACK_IC PRESEL_FEEDBACK_I "\0" BSPWM_CLASS_NAME
+
+xcb_connection_t *dpy;
+int default_screen, screen_width, screen_height;
+uint32_t clients_count;
+xcb_screen_t *screen;
+xcb_window_t root;
+char config_path[MAXLEN];
+
+monitor_t *mon;
+monitor_t *mon_head;
+monitor_t *mon_tail;
+monitor_t *pri_mon;
+history_t *history_head;
+history_t *history_tail;
+history_t *history_needle;
+rule_t *rule_head;
+rule_t *rule_tail;
+stacking_list_t *stack_head;
+stacking_list_t *stack_tail;
+subscriber_list_t *subscribe_head;
+subscriber_list_t *subscribe_tail;
+pending_rule_t *pending_rule_head;
+pending_rule_t *pending_rule_tail;
+
+xcb_window_t meta_window;
+xcb_atom_t WM_STATE;
+xcb_atom_t WM_TAKE_FOCUS;
+xcb_atom_t WM_DELETE_WINDOW;
+int exit_status;
+
+bool auto_raise;
+bool sticky_still;
+bool record_history;
+bool running;
+bool randr;
+
+void init(void);
+void setup(void);
+void register_events(void);
+void cleanup(void);
+bool check_connection (xcb_connection_t *dpy);
+void sig_handler(int sig);
+
+#endif
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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.
+ */
+
+#ifndef BSPWM_COMMON_H
+#define BSPWM_COMMON_H
+
+#define SOCKET_PATH_TPL "/tmp/bspwm%s_%i_%i-socket"
+#define SOCKET_ENV_VAR "BSPWM_SOCKET"
+
+#define FAILURE_MESSAGE "\x07"
+
+#endif
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include "bspwm.h"
+#include "ewmh.h"
+#include "history.h"
+#include "monitor.h"
+#include "query.h"
+#include "tree.h"
+#include "window.h"
+#include "desktop.h"
+#include "subscribe.h"
+#include "settings.h"
+
+void focus_desktop(monitor_t *m, desktop_t *d)
+{
+ bool changed = (m != mon || m->desk != d);
+
+ focus_monitor(m);
+
+ if (m->desk != d) {
+ if (focus_follows_pointer) {
+ listen_enter_notify(d->root, false);
+ }
+ show_desktop(d);
+ hide_desktop(m->desk);
+ if (focus_follows_pointer) {
+ listen_enter_notify(d->root, true);
+ }
+ m->desk = d;
+ }
+
+ if (changed) {
+ ewmh_update_current_desktop();
+ put_status(SBSC_MASK_DESKTOP_FOCUS, "desktop_focus 0x%08X 0x%08X\n", m->id, d->id);
+ }
+}
+
+bool activate_desktop(monitor_t *m, desktop_t *d)
+{
+ if (m == mon || d == m->desk) {
+ return false;
+ }
+
+ if (m->sticky_count > 0) {
+ sticky_still = false;
+ transfer_sticky_nodes(m, m->desk, d, m->desk->root);
+ sticky_still = true;
+ }
+
+ show_desktop(d);
+ hide_desktop(m->desk);
+
+ m->desk = d;
+
+ put_status(SBSC_MASK_DESKTOP_ACTIVATE, "desktop_activate 0x%08X 0x%08X\n", m->id, d->id);
+ put_status(SBSC_MASK_REPORT);
+
+ return true;
+}
+
+bool find_closest_desktop(coordinates_t *ref, coordinates_t *dst, cycle_dir_t dir, desktop_select_t sel)
+{
+ monitor_t *m = ref->monitor;
+ desktop_t *d = ref->desktop;
+ desktop_t *f = (dir == CYCLE_PREV ? d->prev : d->next);
+
+#define HANDLE_BOUNDARIES(f) \
+ if (f == NULL) { \
+ m = (dir == CYCLE_PREV ? m->prev : m->next); \
+ if (m == NULL) { \
+ m = (dir == CYCLE_PREV ? mon_tail : mon_head); \
+ } \
+ f = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head); \
+ }
+ HANDLE_BOUNDARIES(f)
+
+ while (f != d) {
+ coordinates_t loc = {m, f, NULL};
+ if (desktop_matches(&loc, ref, sel)) {
+ *dst = loc;
+ return true;
+ }
+ f = (dir == CYCLE_PREV ? f->prev : f->next);
+ HANDLE_BOUNDARIES(f)
+ }
+#undef HANDLE_BOUNDARIES
+
+ return false;
+}
+
+bool set_layout(monitor_t *m, desktop_t *d, layout_t l)
+{
+ if (d->layout == l) {
+ return false;
+ }
+
+ d->layout = l;
+
+ handle_presel_feedbacks(m, d);
+
+ arrange(m, d);
+
+ put_status(SBSC_MASK_DESKTOP_LAYOUT, "desktop_layout 0x%08X 0x%08X %s\n", m->id, d->id, LAYOUT_STR(l));
+
+ if (d == m->desk) {
+ put_status(SBSC_MASK_REPORT);
+ }
+
+ return true;
+}
+
+void handle_presel_feedbacks(monitor_t *m, desktop_t *d)
+{
+ if (m->desk != d) {
+ return;
+ }
+ if (focus_follows_pointer) {
+ listen_enter_notify(d->root, false);
+ }
+ if (d->layout == LAYOUT_MONOCLE) {
+ hide_presel_feedbacks(m, d, d->root);
+ } else {
+ show_presel_feedbacks(m, d, d->root);
+ }
+ if (focus_follows_pointer) {
+ listen_enter_notify(d->root, true);
+ }
+}
+
+bool transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d)
+{
+ if (ms == NULL || md == NULL || d == NULL || ms == md) {
+ return false;
+ }
+
+ bool was_active = (d == ms->desk);
+
+ unlink_desktop(ms, d);
+
+ if (md->desk != NULL) {
+ hide_desktop(d);
+ }
+
+ insert_desktop(md, d);
+ history_transfer_desktop(md, d);
+
+ if (was_active) {
+ if (mon == ms) {
+ focus_node(ms, NULL, NULL);
+ } else {
+ activate_node(ms, NULL, NULL);
+ }
+ }
+
+ if (ms->sticky_count > 0 && was_active) {
+ sticky_still = false;
+ transfer_sticky_nodes(ms, d, ms->desk, d->root);
+ sticky_still = true;
+ }
+
+ adapt_geometry(&ms->rectangle, &md->rectangle, d->root);
+ arrange(md, d);
+
+ if (md->desk == d) {
+ if (mon == md) {
+ focus_node(md, d, d->focus);
+ } else {
+ activate_node(md, d, d->focus);
+ }
+ }
+
+ ewmh_update_wm_desktops();
+ ewmh_update_desktop_names();
+ ewmh_update_desktop_viewport();
+ ewmh_update_current_desktop();
+
+ put_status(SBSC_MASK_DESKTOP_TRANSFER, "desktop_transfer 0x%08X 0x%08X 0x%08X\n", ms->id, d->id, md->id);
+ put_status(SBSC_MASK_REPORT);
+
+ return true;
+}
+
+desktop_t *make_desktop(const char *name, uint32_t id)
+{
+ desktop_t *d = calloc(1, sizeof(desktop_t));
+ snprintf(d->name, sizeof(d->name), "%s", name == NULL ? DEFAULT_DESK_NAME : name);
+ if (id == XCB_NONE) {
+ d->id = xcb_generate_id(dpy);
+ }
+ d->prev = d->next = NULL;
+ d->root = d->focus = NULL;
+ d->layout = LAYOUT_TILED;
+ d->padding = (padding_t) PADDING;
+ d->window_gap = window_gap;
+ d->border_width = border_width;
+ return d;
+}
+
+void rename_desktop(monitor_t *m, desktop_t *d, const char *name)
+{
+
+ put_status(SBSC_MASK_DESKTOP_RENAME, "desktop_rename 0x%08X 0x%08X %s %s\n", m->id, d->id, d->name, name);
+
+ snprintf(d->name, sizeof(d->name), "%s", name);
+
+ put_status(SBSC_MASK_REPORT);
+ ewmh_update_desktop_names();
+}
+
+void insert_desktop(monitor_t *m, desktop_t *d)
+{
+ if (m->desk == NULL) {
+ m->desk = d;
+ m->desk_head = d;
+ m->desk_tail = d;
+ } else {
+ m->desk_tail->next = d;
+ d->prev = m->desk_tail;
+ m->desk_tail = d;
+ }
+}
+
+void add_desktop(monitor_t *m, desktop_t *d)
+{
+ put_status(SBSC_MASK_DESKTOP_ADD, "desktop_add 0x%08X %s 0x%08X\n", d->id, d->name, m->id);
+
+ d->border_width = m->border_width;
+ d->window_gap = m->window_gap;
+ insert_desktop(m, d);
+ ewmh_update_number_of_desktops();
+ ewmh_update_desktop_names();
+ ewmh_update_desktop_viewport();
+ ewmh_update_wm_desktops();
+ put_status(SBSC_MASK_REPORT);
+}
+
+desktop_t *find_desktop_in(uint32_t id, monitor_t *m)
+{
+ if (m == NULL) {
+ return NULL;
+ }
+
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+ if (d->id == id) {
+ return d;
+ }
+ }
+
+ return NULL;
+}
+
+void unlink_desktop(monitor_t *m, desktop_t *d)
+{
+ desktop_t *prev = d->prev;
+ desktop_t *next = d->next;
+
+ if (prev != NULL) {
+ prev->next = next;
+ }
+
+ if (next != NULL) {
+ next->prev = prev;
+ }
+
+ if (m->desk_head == d) {
+ m->desk_head = next;
+ }
+
+ if (m->desk_tail == d) {
+ m->desk_tail = prev;
+ }
+
+ if (m->desk == d) {
+ m->desk = NULL;
+ }
+
+ d->prev = d->next = NULL;
+}
+
+void remove_desktop(monitor_t *m, desktop_t *d)
+{
+ put_status(SBSC_MASK_DESKTOP_REMOVE, "desktop_remove 0x%08X 0x%08X\n", m->id, d->id);
+
+ remove_node(m, d, d->root);
+ unlink_desktop(m, d);
+ history_remove(d, NULL, false);
+ free(d);
+
+ ewmh_update_current_desktop();
+ ewmh_update_number_of_desktops();
+ ewmh_update_desktop_names();
+ ewmh_update_desktop_viewport();
+
+ if (mon != NULL && m->desk == NULL) {
+ if (m == mon) {
+ focus_node(m, NULL, NULL);
+ } else {
+ activate_node(m, NULL, NULL);
+ }
+ }
+
+ put_status(SBSC_MASK_REPORT);
+}
+
+void merge_desktops(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd)
+{
+ if (ds == NULL || dd == NULL || ds == dd) {
+ return;
+ }
+ transfer_node(ms, ds, ds->root, md, dd, dd->focus);
+}
+
+bool swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2)
+{
+ if (d1 == NULL || d2 == NULL || d1 == d2 ||
+ (m1->desk == d1 && m1->sticky_count > 0) ||
+ (m2->desk == d2 && m2->sticky_count > 0)) {
+ return false;
+ }
+
+ put_status(SBSC_MASK_DESKTOP_SWAP, "desktop_swap 0x%08X 0x%08X 0x%08X 0x%08X\n", m1->id, d1->id, m2->id, d2->id);
+
+ bool d1_focused = (m1->desk == d1);
+ bool d2_focused = (m2->desk == d2);
+
+ if (m1 != m2) {
+ if (m1->desk == d1) {
+ m1->desk = d2;
+ }
+ if (m1->desk_head == d1) {
+ m1->desk_head = d2;
+ }
+ if (m1->desk_tail == d1) {
+ m1->desk_tail = d2;
+ }
+ if (m2->desk == d2) {
+ m2->desk = d1;
+ }
+ if (m2->desk_head == d2) {
+ m2->desk_head = d1;
+ }
+ if (m2->desk_tail == d2) {
+ m2->desk_tail = d1;
+ }
+ } else {
+ if (m1->desk == d1) {
+ m1->desk = d2;
+ } else if (m1->desk == d2) {
+ m1->desk = d1;
+ }
+ if (m1->desk_head == d1) {
+ m1->desk_head = d2;
+ } else if (m1->desk_head == d2) {
+ m1->desk_head = d1;
+ }
+ if (m1->desk_tail == d1) {
+ m1->desk_tail = d2;
+ } else if (m1->desk_tail == d2) {
+ m1->desk_tail = d1;
+ }
+ }
+
+ desktop_t *p1 = d1->prev;
+ desktop_t *n1 = d1->next;
+ desktop_t *p2 = d2->prev;
+ desktop_t *n2 = d2->next;
+
+ if (p1 != NULL && p1 != d2) {
+ p1->next = d2;
+ }
+ if (n1 != NULL && n1 != d2) {
+ n1->prev = d2;
+ }
+ if (p2 != NULL && p2 != d1) {
+ p2->next = d1;
+ }
+ if (n2 != NULL && n2 != d1) {
+ n2->prev = d1;
+ }
+
+ d1->prev = p2 == d1 ? d2 : p2;
+ d1->next = n2 == d1 ? d2 : n2;
+ d2->prev = p1 == d2 ? d1 : p1;
+ d2->next = n1 == d2 ? d1 : n1;
+
+ if (m1 != m2) {
+ adapt_geometry(&m1->rectangle, &m2->rectangle, d1->root);
+ adapt_geometry(&m2->rectangle, &m1->rectangle, d2->root);
+ history_swap_desktops(m1, d1, m2, d2);
+ arrange(m1, d2);
+ arrange(m2, d1);
+ }
+
+ if (d1_focused && !d2_focused) {
+ hide_desktop(d1);
+ show_desktop(d2);
+ } else if (!d1_focused && d2_focused) {
+ show_desktop(d1);
+ hide_desktop(d2);
+ }
+
+ if (d1 == mon->desk) {
+ focus_node(m2, d1, d1->focus);
+ } else if (d1 == m2->desk) {
+ activate_node(m2, d1, d1->focus);
+ }
+
+ if (d2 == mon->desk) {
+ focus_node(m1, d2, d2->focus);
+ } else if (d2 == m1->desk) {
+ activate_node(m1, d2, d2->focus);
+ }
+
+ ewmh_update_wm_desktops();
+ ewmh_update_desktop_names();
+ ewmh_update_desktop_viewport();
+ ewmh_update_current_desktop();
+
+ put_status(SBSC_MASK_REPORT);
+
+ return true;
+}
+
+void show_desktop(desktop_t *d)
+{
+ if (d == NULL) {
+ return;
+ }
+ show_node(d, d->root);
+}
+
+void hide_desktop(desktop_t *d)
+{
+ if (d == NULL) {
+ return;
+ }
+ hide_node(d, d->root);
+}
+
+bool is_urgent(desktop_t *d)
+{
+ for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
+ if (n->client == NULL) {
+ continue;
+ }
+ if (n->client->urgent) {
+ return true;
+ }
+ }
+ return false;
+}
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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.
+ */
+
+#ifndef BSPWM_DESKTOP_H
+#define BSPWM_DESKTOP_H
+
+#define DEFAULT_DESK_NAME "Desktop"
+
+void focus_desktop(monitor_t *m, desktop_t *d);
+bool activate_desktop(monitor_t *m, desktop_t *d);
+bool find_closest_desktop(coordinates_t *ref, coordinates_t *dst, cycle_dir_t dir, desktop_select_t sel);
+bool set_layout(monitor_t *m, desktop_t *d, layout_t l);
+void handle_presel_feedbacks(monitor_t *m, desktop_t *d);
+bool transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d);
+desktop_t *make_desktop(const char *name, uint32_t id);
+void rename_desktop(monitor_t *m, desktop_t *d, const char *name);
+void insert_desktop(monitor_t *m, desktop_t *d);
+void add_desktop(monitor_t *m, desktop_t *d);
+desktop_t *find_desktop_in(uint32_t id, monitor_t *m);
+void unlink_desktop(monitor_t *m, desktop_t *d);
+void remove_desktop(monitor_t *m, desktop_t *d);
+void merge_desktops(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd);
+bool swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2);
+void show_desktop(desktop_t *d);
+void hide_desktop(desktop_t *d);
+bool is_urgent(desktop_t *d);
+
+#endif
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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 <stdbool.h>
+#include "bspwm.h"
+#include "ewmh.h"
+#include "monitor.h"
+#include "query.h"
+#include "settings.h"
+#include "subscribe.h"
+#include "tree.h"
+#include "window.h"
+#include "pointer.h"
+#include "events.h"
+
+void handle_event(xcb_generic_event_t *evt)
+{
+ uint8_t resp_type = XCB_EVENT_RESPONSE_TYPE(evt);
+ switch (resp_type) {
+ case XCB_MAP_REQUEST:
+ map_request(evt);
+ break;
+ case XCB_DESTROY_NOTIFY:
+ destroy_notify(evt);
+ break;
+ case XCB_UNMAP_NOTIFY:
+ unmap_notify(evt);
+ break;
+ case XCB_CLIENT_MESSAGE:
+ client_message(evt);
+ break;
+ case XCB_CONFIGURE_REQUEST:
+ configure_request(evt);
+ break;
+ case XCB_CONFIGURE_NOTIFY:
+ configure_notify(evt);
+ break;
+ case XCB_PROPERTY_NOTIFY:
+ property_notify(evt);
+ break;
+ case XCB_ENTER_NOTIFY:
+ enter_notify(evt);
+ break;
+ case XCB_BUTTON_PRESS:
+ button_press(evt);
+ break;
+ case XCB_FOCUS_IN:
+ focus_in(evt);
+ break;
+ case 0:
+ process_error(evt);
+ break;
+ default:
+ if (randr && resp_type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY) {
+ update_monitors();
+ }
+ break;
+ }
+}
+
+void map_request(xcb_generic_event_t *evt)
+{
+ xcb_map_request_event_t *e = (xcb_map_request_event_t *) evt;
+
+ schedule_window(e->window);
+}
+
+void configure_request(xcb_generic_event_t *evt)
+{
+ xcb_configure_request_event_t *e = (xcb_configure_request_event_t *) evt;
+
+ coordinates_t loc;
+ bool is_managed = locate_window(e->window, &loc);
+ client_t *c = (is_managed ? loc.node->client : NULL);
+ uint16_t width, height;
+
+ if (!is_managed) {
+ uint16_t mask = 0;
+ uint32_t values[7];
+ unsigned short i = 0;
+
+ if (e->value_mask & XCB_CONFIG_WINDOW_X) {
+ mask |= XCB_CONFIG_WINDOW_X;
+ values[i++] = e->x;
+ }
+
+ if (e->value_mask & XCB_CONFIG_WINDOW_Y) {
+ mask |= XCB_CONFIG_WINDOW_Y;
+ values[i++] = e->y;
+ }
+
+ if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
+ mask |= XCB_CONFIG_WINDOW_WIDTH;
+ values[i++] = e->width;
+ }
+
+ if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
+ mask |= XCB_CONFIG_WINDOW_HEIGHT;
+ values[i++] = e->height;
+ }
+
+ if (e->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH) {
+ mask |= XCB_CONFIG_WINDOW_BORDER_WIDTH;
+ values[i++] = e->border_width;
+ }
+
+ if (e->value_mask & XCB_CONFIG_WINDOW_SIBLING) {
+ mask |= XCB_CONFIG_WINDOW_SIBLING;
+ values[i++] = e->sibling;
+ }
+
+ if (e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) {
+ mask |= XCB_CONFIG_WINDOW_STACK_MODE;
+ values[i++] = e->stack_mode;
+ }
+
+ xcb_configure_window(dpy, e->window, mask, values);
+
+ } else if (IS_FLOATING(c)) {
+ width = c->floating_rectangle.width;
+ height = c->floating_rectangle.height;
+
+ if (e->value_mask & XCB_CONFIG_WINDOW_X) {
+ c->floating_rectangle.x = e->x;
+ }
+
+ if (e->value_mask & XCB_CONFIG_WINDOW_Y) {
+ c->floating_rectangle.y = e->y;
+ }
+
+ if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
+ width = e->width;
+ }
+
+ if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
+ height = e->height;
+ }
+
+ apply_size_hints(c, &width, &height);
+ c->floating_rectangle.width = width;
+ c->floating_rectangle.height = height;
+ xcb_rectangle_t r = c->floating_rectangle;
+
+ if (focus_follows_pointer) {
+ listen_enter_notify(loc.desktop->root, false);
+ }
+
+ window_move_resize(e->window, r.x, r.y, r.width, r.height);
+
+ if (focus_follows_pointer) {
+ listen_enter_notify(loc.desktop->root, true);
+ }
+
+ put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", loc.monitor->id, loc.desktop->id, e->window, r.width, r.height, r.x, r.y);
+
+ monitor_t *m = monitor_from_client(c);
+ if (m != loc.monitor) {
+ transfer_node(loc.monitor, loc.desktop, loc.node, m, m->desk, m->desk->focus);
+ }
+ } else {
+ if (c->state == STATE_PSEUDO_TILED) {
+ width = c->floating_rectangle.width;
+ height = c->floating_rectangle.height;
+ if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
+ width = e->width;
+ }
+ if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
+ height = e->height;
+ }
+ apply_size_hints(c, &width, &height);
+ if (width != c->floating_rectangle.width || height != c->floating_rectangle.height) {
+ c->floating_rectangle.width = width;
+ c->floating_rectangle.height = height;
+ arrange(loc.monitor, loc.desktop);
+ }
+ }
+
+
+ xcb_configure_notify_event_t evt;
+ unsigned int bw = c->border_width;
+
+ xcb_rectangle_t r = IS_FULLSCREEN(c) ? loc.monitor->rectangle : c->tiled_rectangle;
+
+ evt.response_type = XCB_CONFIGURE_NOTIFY;
+ evt.event = e->window;
+ evt.window = e->window;
+ evt.above_sibling = XCB_NONE;
+ evt.x = r.x;
+ evt.y = r.y;
+ evt.width = r.width;
+ evt.height = r.height;
+ evt.border_width = bw;
+ evt.override_redirect = false;
+
+ xcb_send_event(dpy, false, e->window, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (const char *) &evt);
+ }
+}
+
+void configure_notify(xcb_generic_event_t *evt)
+{
+ xcb_configure_notify_event_t *e = (xcb_configure_notify_event_t *) evt;
+
+ if (e->window == root) {
+ screen_width = e->width;
+ screen_height = e->height;
+ }
+}
+
+void destroy_notify(xcb_generic_event_t *evt)
+{
+ xcb_destroy_notify_event_t *e = (xcb_destroy_notify_event_t *) evt;
+
+ unmanage_window(e->window);
+}
+
+void unmap_notify(xcb_generic_event_t *evt)
+{
+ xcb_unmap_notify_event_t *e = (xcb_unmap_notify_event_t *) evt;
+
+ unmanage_window(e->window);
+}
+
+void property_notify(xcb_generic_event_t *evt)
+{
+ xcb_property_notify_event_t *e = (xcb_property_notify_event_t *) evt;
+
+ if (e->atom == ewmh->_NET_WM_STRUT_PARTIAL && ewmh_handle_struts(e->window)) {
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+ arrange(m, d);
+ }
+ }
+ }
+
+ if (e->atom != XCB_ATOM_WM_HINTS && e->atom != XCB_ATOM_WM_NORMAL_HINTS) {
+ return;
+ }
+
+ coordinates_t loc;
+ if (!locate_window(e->window, &loc)) {
+ return;
+ }
+
+ if (e->atom == XCB_ATOM_WM_HINTS) {
+ xcb_icccm_wm_hints_t hints;
+ if (xcb_icccm_get_wm_hints_reply(dpy, xcb_icccm_get_wm_hints(dpy, e->window), &hints, NULL) == 1 &&
+ (hints.flags & XCB_ICCCM_WM_HINT_X_URGENCY))
+ set_urgent(loc.monitor, loc.desktop, loc.node, xcb_icccm_wm_hints_get_urgency(&hints));
+ } else if (e->atom == XCB_ATOM_WM_NORMAL_HINTS) {
+ client_t *c = loc.node->client;
+ if (xcb_icccm_get_wm_normal_hints_reply(dpy, xcb_icccm_get_wm_normal_hints(dpy, e->window), &c->size_hints, NULL) == 1) {
+ arrange(loc.monitor, loc.desktop);
+ }
+ }
+}
+
+void client_message(xcb_generic_event_t *evt)
+{
+ xcb_client_message_event_t *e = (xcb_client_message_event_t *) evt;
+
+ if (e->type == ewmh->_NET_CURRENT_DESKTOP) {
+ coordinates_t loc;
+ if (ewmh_locate_desktop(e->data.data32[0], &loc)) {
+ focus_node(loc.monitor, loc.desktop, loc.desktop->focus);
+ }
+ return;
+ }
+
+ coordinates_t loc;
+ if (!locate_window(e->window, &loc)) {
+ return;
+ }
+
+ if (e->type == ewmh->_NET_WM_STATE) {
+ handle_state(loc.monitor, loc.desktop, loc.node, e->data.data32[1], e->data.data32[0]);
+ handle_state(loc.monitor, loc.desktop, loc.node, e->data.data32[2], e->data.data32[0]);
+ } else if (e->type == ewmh->_NET_ACTIVE_WINDOW) {
+ if ((ignore_ewmh_focus && e->data.data32[0] == XCB_EWMH_CLIENT_SOURCE_TYPE_NORMAL) ||
+ loc.node == mon->desk->focus) {
+ return;
+ }
+ focus_node(loc.monitor, loc.desktop, loc.node);
+ } else if (e->type == ewmh->_NET_WM_DESKTOP) {
+ coordinates_t dloc;
+ if (ewmh_locate_desktop(e->data.data32[0], &dloc)) {
+ transfer_node(loc.monitor, loc.desktop, loc.node, dloc.monitor, dloc.desktop, dloc.desktop->focus);
+ }
+ } else if (e->type == ewmh->_NET_CLOSE_WINDOW) {
+ close_node(loc.node);
+ }
+}
+
+void focus_in(xcb_generic_event_t *evt)
+{
+ xcb_focus_in_event_t *e = (xcb_focus_in_event_t *) evt;
+
+ if (e->mode == XCB_NOTIFY_MODE_GRAB || e->mode == XCB_NOTIFY_MODE_UNGRAB
+ || e->detail == XCB_NOTIFY_DETAIL_POINTER || e->detail == XCB_NOTIFY_DETAIL_POINTER_ROOT
+ || e->detail == XCB_NOTIFY_DETAIL_NONE) {
+ return;
+ }
+
+ if (mon->desk->focus != NULL && e->event == mon->desk->focus->id) {
+ return;
+ }
+
+ coordinates_t loc;
+ if (locate_window(e->event, &loc)) {
+ // prevent input focus stealing
+ update_input_focus();
+ }
+}
+
+void button_press(xcb_generic_event_t *evt)
+{
+ xcb_button_press_event_t *e = (xcb_button_press_event_t *) evt;
+ bool replay = false;
+ switch (e->detail) {
+ case XCB_BUTTON_INDEX_1:
+ if (click_to_focus && cleaned_mask(e->state) == XCB_NONE) {
+ bool pff = pointer_follows_focus;
+ bool pfm = pointer_follows_monitor;
+ pointer_follows_focus = false;
+ pointer_follows_monitor = false;
+ replay = !grab_pointer(ACTION_FOCUS) || !swallow_first_click;
+ pointer_follows_focus = pff;
+ pointer_follows_monitor = pfm;
+ } else {
+ grab_pointer(pointer_actions[0]);
+ }
+ break;
+ case XCB_BUTTON_INDEX_2:
+ grab_pointer(pointer_actions[1]);
+ break;
+ case XCB_BUTTON_INDEX_3:
+ grab_pointer(pointer_actions[2]);
+ break;
+ }
+ xcb_allow_events(dpy, replay ? XCB_ALLOW_REPLAY_POINTER : XCB_ALLOW_SYNC_POINTER, e->time);
+ xcb_flush(dpy);
+}
+
+void enter_notify(xcb_generic_event_t *evt)
+{
+ xcb_enter_notify_event_t *e = (xcb_enter_notify_event_t *) evt;
+ xcb_window_t win = e->event;
+
+ if (e->mode != XCB_NOTIFY_MODE_NORMAL || e->detail == XCB_NOTIFY_DETAIL_INFERIOR) {
+ return;
+ }
+
+ xcb_point_t pt = {e->root_x, e->root_y};
+ monitor_t *m = monitor_from_point(pt);
+
+ if (m == NULL) {
+ return;
+ }
+
+ bool pff = pointer_follows_focus;
+ bool pfm = pointer_follows_monitor;
+ pointer_follows_focus = false;
+ pointer_follows_monitor = false;
+ auto_raise = false;
+
+ desktop_t *d = m->desk;
+ node_t *n = NULL;
+
+ for (n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
+ if (n->id == win || (n->presel != NULL && n->presel->feedback == win)) {
+ break;
+ }
+ }
+
+ if (n != mon->desk->focus || mon->desk->focus == NULL) {
+ focus_node(m, d, n == NULL ? d->focus : n);
+ }
+
+ pointer_follows_focus = pff;
+ pointer_follows_monitor = pfm;
+ auto_raise = true;
+}
+
+void handle_state(monitor_t *m, desktop_t *d, node_t *n, xcb_atom_t state, unsigned int action)
+{
+ if (state == ewmh->_NET_WM_STATE_FULLSCREEN) {
+ if (action == XCB_EWMH_WM_STATE_ADD) {
+ set_state(m, d, n, STATE_FULLSCREEN);
+ } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
+ if (n->client->state == STATE_FULLSCREEN) {
+ set_state(m, d, n, n->client->last_state);
+ }
+ } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
+ set_state(m, d, n, IS_FULLSCREEN(n->client) ? n->client->last_state : STATE_FULLSCREEN);
+ }
+ arrange(m, d);
+ } else if (state == ewmh->_NET_WM_STATE_BELOW) {
+ if (action == XCB_EWMH_WM_STATE_ADD) {
+ set_layer(m, d, n, LAYER_BELOW);
+ } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
+ if (n->client->layer == LAYER_BELOW) {
+ set_layer(m, d, n, n->client->last_layer);
+ }
+ } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
+ set_layer(m, d, n, n->client->layer == LAYER_BELOW ? n->client->last_layer : LAYER_BELOW);
+ }
+ } else if (state == ewmh->_NET_WM_STATE_ABOVE) {
+ if (action == XCB_EWMH_WM_STATE_ADD) {
+ set_layer(m, d, n, LAYER_ABOVE);
+ } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
+ if (n->client->layer == LAYER_ABOVE) {
+ set_layer(m, d, n, n->client->last_layer);
+ }
+ } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
+ set_layer(m, d, n, n->client->layer == LAYER_ABOVE ? n->client->last_layer : LAYER_ABOVE);
+ }
+ } else if (state == ewmh->_NET_WM_STATE_HIDDEN) {
+ if (action == XCB_EWMH_WM_STATE_ADD) {
+ set_hidden(m, d, n, true);
+ } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
+ set_hidden(m, d, n, false);
+ } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
+ set_hidden(m, d, n, !n->hidden);
+ }
+ } else if (state == ewmh->_NET_WM_STATE_STICKY) {
+ if (action == XCB_EWMH_WM_STATE_ADD) {
+ set_sticky(m, d, n, true);
+ } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
+ set_sticky(m, d, n, false);
+ } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
+ set_sticky(m, d, n, !n->sticky);
+ }
+ } else if (state == ewmh->_NET_WM_STATE_DEMANDS_ATTENTION) {
+ if (action == XCB_EWMH_WM_STATE_ADD) {
+ set_urgent(m, d, n, true);
+ } else if (action == XCB_EWMH_WM_STATE_REMOVE) {
+ set_urgent(m, d, n, false);
+ } else if (action == XCB_EWMH_WM_STATE_TOGGLE) {
+ set_urgent(m, d, n, !n->client->urgent);
+ }
+#define HANDLE_WM_STATE(s) \
+ } else if (state == ewmh->_NET_WM_STATE_##s) { \
+ if (action == XCB_EWMH_WM_STATE_ADD) { \
+ n->client->wm_flags |= WM_FLAG_##s; \
+ } else if (action == XCB_EWMH_WM_STATE_REMOVE) { \
+ n->client->wm_flags &= ~WM_FLAG_##s; \
+ } else if (action == XCB_EWMH_WM_STATE_TOGGLE) { \
+ n->client->wm_flags ^= WM_FLAG_##s; \
+ } \
+ ewmh_wm_state_update(n);
+ HANDLE_WM_STATE(MODAL)
+ HANDLE_WM_STATE(MAXIMIZED_VERT)
+ HANDLE_WM_STATE(MAXIMIZED_HORZ)
+ HANDLE_WM_STATE(SHADED)
+ HANDLE_WM_STATE(SKIP_TASKBAR)
+ HANDLE_WM_STATE(SKIP_PAGER)
+ }
+#undef HANDLE_WM_STATE
+}
+
+void process_error(xcb_generic_event_t *evt)
+{
+ xcb_request_error_t *e = (xcb_request_error_t *) evt;
+ warn("Failed request: %s, %s: 0x%08X.\n", xcb_event_get_request_label(e->major_opcode), xcb_event_get_error_label(e->error_code), e->bad_value);
+}
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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.
+ */
+
+#ifndef BSPWM_EVENTS_H
+#define BSPWM_EVENTS_H
+
+#include <xcb/xcb.h>
+#include <xcb/xcb_event.h>
+
+uint8_t randr_base;
+
+void handle_event(xcb_generic_event_t *evt);
+void map_request(xcb_generic_event_t *evt);
+void configure_request(xcb_generic_event_t *evt);
+void configure_notify(xcb_generic_event_t *evt);
+void destroy_notify(xcb_generic_event_t *evt);
+void unmap_notify(xcb_generic_event_t *evt);
+void property_notify(xcb_generic_event_t *evt);
+void client_message(xcb_generic_event_t *evt);
+void focus_in(xcb_generic_event_t *evt);
+void button_press(xcb_generic_event_t *evt);
+void enter_notify(xcb_generic_event_t *evt);
+void handle_state(monitor_t *m, desktop_t *d, node_t *n, xcb_atom_t state, unsigned int action);
+void process_error(xcb_generic_event_t *evt);
+
+#endif
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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 <stdlib.h>
+#include <sys/types.h>
+#include <string.h>
+#include <unistd.h>
+#include "bspwm.h"
+#include "settings.h"
+#include "tree.h"
+#include "ewmh.h"
+
+void ewmh_init(void)
+{
+ ewmh = calloc(1, sizeof(xcb_ewmh_connection_t));
+ if (xcb_ewmh_init_atoms_replies(ewmh, xcb_ewmh_init_atoms(dpy, ewmh), NULL) == 0) {
+ err("Can't initialize EWMH atoms.\n");
+ }
+}
+
+void ewmh_update_active_window(void)
+{
+ xcb_window_t win = ((mon->desk->focus == NULL || mon->desk->focus->client == NULL) ? XCB_NONE : mon->desk->focus->id);
+ xcb_ewmh_set_active_window(ewmh, default_screen, win);
+}
+
+void ewmh_update_number_of_desktops(void)
+{
+ uint32_t desktops_count = 0;
+
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+ desktops_count++;
+ }
+ }
+
+ xcb_ewmh_set_number_of_desktops(ewmh, default_screen, desktops_count);
+}
+
+uint32_t ewmh_get_desktop_index(desktop_t *d)
+{
+ uint32_t i = 0;
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ for (desktop_t *cd = m->desk_head; cd != NULL; cd = cd->next, i++) {
+ if (d == cd) {
+ return i;
+ }
+ }
+ }
+ return 0;
+}
+
+bool ewmh_locate_desktop(uint32_t i, coordinates_t *loc)
+{
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next, i--) {
+ if (i == 0) {
+ loc->monitor = m;
+ loc->desktop = d;
+ loc->node = NULL;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void ewmh_update_current_desktop(void)
+{
+ if (mon == NULL) {
+ return;
+ }
+ uint32_t i = ewmh_get_desktop_index(mon->desk);
+ xcb_ewmh_set_current_desktop(ewmh, default_screen, i);
+}
+
+void ewmh_set_wm_desktop(node_t *n, desktop_t *d)
+{
+ uint32_t i = ewmh_get_desktop_index(d);
+ for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+ if (f->client == NULL) {
+ continue;
+ }
+ xcb_ewmh_set_wm_desktop(ewmh, f->id, i);
+ }
+}
+
+void ewmh_update_wm_desktops(void)
+{
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+ uint32_t i = ewmh_get_desktop_index(d);
+ for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
+ if (n->client == NULL) {
+ continue;
+ }
+ xcb_ewmh_set_wm_desktop(ewmh, n->id, i);
+ }
+ }
+ }
+}
+
+void ewmh_update_desktop_names(void)
+{
+ char names[MAXLEN];
+ unsigned int i, j;
+ uint32_t names_len;
+ i = 0;
+
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+ for (j = 0; d->name[j] != '\0' && (i + j) < sizeof(names); j++) {
+ names[i + j] = d->name[j];
+ }
+ i += j;
+ if (i < sizeof(names)) {
+ names[i++] = '\0';
+ }
+ }
+ }
+
+ if (i < 1) {
+ xcb_ewmh_set_desktop_names(ewmh, default_screen, 0, NULL);
+ return;
+ }
+
+ names_len = i - 1;
+ xcb_ewmh_set_desktop_names(ewmh, default_screen, names_len, names);
+}
+
+void ewmh_update_desktop_viewport(void)
+{
+ uint32_t desktops_count = 0;
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+ desktops_count++;
+ }
+ }
+ xcb_ewmh_coordinates_t coords[desktops_count];
+ uint16_t desktop = 0;
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+ coords[desktop++] = (xcb_ewmh_coordinates_t){m->rectangle.x, m->rectangle.y};
+ }
+ }
+ xcb_ewmh_set_desktop_viewport(ewmh, default_screen, desktop, coords);
+}
+
+bool ewmh_handle_struts(xcb_window_t win)
+{
+ xcb_ewmh_wm_strut_partial_t struts;
+ bool changed = false;
+ if (xcb_ewmh_get_wm_strut_partial_reply(ewmh, xcb_ewmh_get_wm_strut_partial(ewmh, win), &struts, NULL) == 1) {
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ xcb_rectangle_t rect = m->rectangle;
+ if (rect.x < (int16_t) struts.left &&
+ (int16_t) struts.left < (rect.x + rect.width - 1) &&
+ (int16_t) struts.left_end_y >= rect.y &&
+ (int16_t) struts.left_start_y < (rect.y + rect.height)) {
+ int dx = struts.left - rect.x;
+ if (m->padding.left < 0) {
+ m->padding.left += dx;
+ } else {
+ m->padding.left = MAX(dx, m->padding.left);
+ }
+ changed = true;
+ }
+ if ((rect.x + rect.width) > (int16_t) (screen_width - struts.right) &&
+ (int16_t) (screen_width - struts.right) > rect.x &&
+ (int16_t) struts.right_end_y >= rect.y &&
+ (int16_t) struts.right_start_y < (rect.y + rect.height)) {
+ int dx = (rect.x + rect.width) - screen_width + struts.right;
+ if (m->padding.right < 0) {
+ m->padding.right += dx;
+ } else {
+ m->padding.right = MAX(dx, m->padding.right);
+ }
+ changed = true;
+ }
+ if (rect.y < (int16_t) struts.top &&
+ (int16_t) struts.top < (rect.y + rect.height - 1) &&
+ (int16_t) struts.top_end_x >= rect.x &&
+ (int16_t) struts.top_start_x < (rect.x + rect.width)) {
+ int dy = struts.top - rect.y;
+ if (m->padding.top < 0) {
+ m->padding.top += dy;
+ } else {
+ m->padding.top = MAX(dy, m->padding.top);
+ }
+ changed = true;
+ }
+ if ((rect.y + rect.height) > (int16_t) (screen_height - struts.bottom) &&
+ (int16_t) (screen_height - struts.bottom) > rect.y &&
+ (int16_t) struts.bottom_end_x >= rect.x &&
+ (int16_t) struts.bottom_start_x < (rect.x + rect.width)) {
+ int dy = (rect.y + rect.height) - screen_height + struts.bottom;
+ if (m->padding.bottom < 0) {
+ m->padding.bottom += dy;
+ } else {
+ m->padding.bottom = MAX(dy, m->padding.bottom);
+ }
+ changed = true;
+ }
+ }
+ }
+ return changed;
+}
+
+void ewmh_update_client_list(bool stacking)
+{
+ if (clients_count == 0) {
+ xcb_ewmh_set_client_list(ewmh, default_screen, 0, NULL);
+ xcb_ewmh_set_client_list_stacking(ewmh, default_screen, 0, NULL);
+ return;
+ }
+
+ xcb_window_t wins[clients_count];
+ unsigned int i = 0;
+
+ if (stacking) {
+ for (stacking_list_t *s = stack_head; s != NULL; s = s->next) {
+ wins[i++] = s->node->id;
+ }
+ xcb_ewmh_set_client_list_stacking(ewmh, default_screen, clients_count, wins);
+ } else {
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ 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)) {
+ if (n->client == NULL) {
+ continue;
+ }
+ wins[i++] = n->id;
+ }
+ }
+ }
+ xcb_ewmh_set_client_list(ewmh, default_screen, clients_count, wins);
+ }
+}
+
+void ewmh_wm_state_update(node_t *n)
+{
+ client_t *c = n->client;
+ size_t count = 0;
+ uint32_t values[12];
+#define HANDLE_WM_STATE(s) \
+ if (WM_FLAG_##s & c->wm_flags) { \
+ values[count++] = ewmh->_NET_WM_STATE_##s; \
+ }
+ HANDLE_WM_STATE(MODAL)
+ HANDLE_WM_STATE(STICKY)
+ HANDLE_WM_STATE(MAXIMIZED_VERT)
+ HANDLE_WM_STATE(MAXIMIZED_HORZ)
+ HANDLE_WM_STATE(SHADED)
+ HANDLE_WM_STATE(SKIP_TASKBAR)
+ HANDLE_WM_STATE(SKIP_PAGER)
+ HANDLE_WM_STATE(HIDDEN)
+ HANDLE_WM_STATE(FULLSCREEN)
+ HANDLE_WM_STATE(ABOVE)
+ HANDLE_WM_STATE(BELOW)
+ HANDLE_WM_STATE(DEMANDS_ATTENTION)
+#undef HANDLE_WM_STATE
+ xcb_ewmh_set_wm_state(ewmh, n->id, count, values);
+}
+
+void ewmh_set_supporting(xcb_window_t win)
+{
+ pid_t wm_pid = getpid();
+ xcb_ewmh_set_supporting_wm_check(ewmh, root, win);
+ xcb_ewmh_set_supporting_wm_check(ewmh, win, win);
+ xcb_ewmh_set_wm_name(ewmh, win, strlen(WM_NAME), WM_NAME);
+ xcb_ewmh_set_wm_pid(ewmh, win, wm_pid);
+}
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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.
+ */
+
+#ifndef BSPWM_EWMH_H
+#define BSPWM_EWMH_H
+
+#include <xcb/xcb_ewmh.h>
+
+xcb_ewmh_connection_t *ewmh;
+
+void ewmh_init(void);
+void ewmh_update_active_window(void);
+void ewmh_update_number_of_desktops(void);
+uint32_t ewmh_get_desktop_index(desktop_t *d);
+bool ewmh_locate_desktop(uint32_t i, coordinates_t *loc);
+void ewmh_update_current_desktop(void);
+void ewmh_set_wm_desktop(node_t *n, desktop_t *d);
+void ewmh_update_wm_desktops(void);
+void ewmh_update_desktop_names(void);
+void ewmh_update_desktop_viewport(void);
+bool ewmh_handle_struts(xcb_window_t win);
+void ewmh_update_client_list(bool stacking);
+void ewmh_wm_state_update(node_t *n);
+void ewmh_set_supporting(xcb_window_t win);
+
+#endif
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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 <math.h>
+#include "types.h"
+#include "geometry.h"
+
+bool is_inside(xcb_point_t p, xcb_rectangle_t r)
+{
+ return (p.x >= r.x && p.x < (r.x + r.width) &&
+ p.y >= r.y && p.y < (r.y + r.height));
+}
+
+unsigned int area(xcb_rectangle_t r)
+{
+ return r.width * r.height;
+}
+
+
+dpoint_t center(xcb_rectangle_t r)
+{
+ return (dpoint_t) {(double)r.x + ((double)r.width / 2), (double)r.y + ((double)r.height / 2)};
+}
+
+double distance_center(xcb_rectangle_t r1, xcb_rectangle_t r2)
+{
+ dpoint_t r1_center = center(r1);
+ dpoint_t r2_center = center(r2);
+ return hypot(r1_center.x - r2_center.x, r1_center.y - r2_center.y);
+}
+
+bool on_dir_side(xcb_rectangle_t r1, xcb_rectangle_t r2, direction_t dir)
+{
+ dpoint_t r1_max = {r1.x + r1.width, r1.y + r1.height};
+ dpoint_t r2_max = {r2.x + r2.width, r2.y + r2.height};
+ dpoint_t r1_center = center(r1);
+ dpoint_t r2_center = center(r2);
+
+ switch (dir) {
+ case DIR_NORTH:
+ if (r2_center.y >= r1_center.y)
+ return false;
+ break;
+ case DIR_WEST:
+ if (r2_center.x >= r1_center.x)
+ return false;
+ break;
+ case DIR_SOUTH:
+ if (r1_center.y >= r2_center.y)
+ return false;
+ break;
+ case DIR_EAST:
+ if (r1_center.x >= r2_center.x)
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ switch (dir) {
+ case DIR_NORTH:
+ case DIR_SOUTH:
+ return
+ (r2.x >= r1.x && r2.x <= r1_max.x) ||
+ (r2_max.x >= r1.x && r2_max.x <= r1_max.x) ||
+ (r1.x >= r2.x && r1.x <= r2_max.x) ||
+ (r1_max.x >= r2.x && r1_max.x <= r2_max.x);
+ break;
+ case DIR_WEST:
+ case DIR_EAST:
+ return
+ (r2.y >= r1.y && r2.y <= r1_max.y) ||
+ (r2_max.y >= r1.y && r2_max.y <= r1_max.y) ||
+ (r1.y >= r2.y && r1.y <= r2_max.y) ||
+ (r1_max.y >= r2.y && r1_max.y <= r2_max.y);
+ break;
+ default:
+ return false;
+ }
+}
+
+bool rect_eq(xcb_rectangle_t a, xcb_rectangle_t b)
+{
+ return (a.x == b.x && a.y == b.y &&
+ a.width == b.width && a.height == b.height);
+}
+
+int rect_cmp(xcb_rectangle_t r1, xcb_rectangle_t r2)
+{
+ if (r1.y >= (r2.y + r2.height)) {
+ return 1;
+ } else if (r2.y >= (r1.y + r1.height)) {
+ return -1;
+ } else {
+ if (r1.x >= (r2.x + r2.width)) {
+ return 1;
+ } else if (r2.x >= (r1.x + r1.width)) {
+ return -1;
+ } else {
+ return area(r1) - area(r2);
+ }
+ }
+}
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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.
+ */
+
+#ifndef BSPWM_GEOMETRY_H
+#define BSPWM_GEOMETRY_H
+
+#include <stdbool.h>
+#include <xcb/xcb.h>
+
+bool is_inside(xcb_point_t p, xcb_rectangle_t r);
+unsigned int area(xcb_rectangle_t r);
+dpoint_t center(xcb_rectangle_t r);
+double distance_center(xcb_rectangle_t r1, xcb_rectangle_t r2);
+bool on_dir_side(xcb_rectangle_t r1, xcb_rectangle_t r2, direction_t dir);
+bool rect_eq(xcb_rectangle_t a, xcb_rectangle_t b);
+int rect_cmp(xcb_rectangle_t r1, xcb_rectangle_t r2);
+
+#endif
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include "bspwm.h"
+
+void warn(char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+__attribute__((noreturn))
+void err(char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ exit(EXIT_FAILURE);
+}
+
+char *read_string(const char *file_path, size_t *tlen)
+{
+ if (file_path == NULL) {
+ return NULL;
+ }
+
+ int fd = open(file_path, O_RDONLY);
+
+ if (fd == -1) {
+ perror("Read file: open");
+ return NULL;
+ }
+
+ char buf[BUFSIZ], *content = NULL;
+ size_t len = sizeof(buf);
+
+ if ((content = calloc(len, sizeof(char))) == NULL) {
+ perror("Read file: calloc");
+ goto end;
+ }
+
+ int nb;
+ *tlen = 0;
+
+ while (true) {
+ nb = read(fd, buf, sizeof(buf));
+ if (nb < 0) {
+ perror("Restore tree: read");
+ free(content);
+ content = NULL;
+ goto end;
+ } else if (nb == 0) {
+ break;
+ } else {
+ *tlen += nb;
+ if (*tlen > len) {
+ len *= 2;
+ char *rcontent = realloc(content, len * sizeof(char));
+ if (rcontent == NULL) {
+ perror("Read file: realloc");
+ free(content);
+ content = NULL;
+ goto end;
+ } else {
+ content = rcontent;
+ }
+ }
+ strncpy(content + (*tlen - nb), buf, nb);
+ }
+ }
+
+end:
+ close(fd);
+ return content;
+}
+
+char *copy_string(char *str, size_t len)
+{
+ char *cpy = calloc(1, ((len+1) * sizeof(char)));
+ if (cpy == NULL) {
+ perror("Copy string: calloc");
+ return NULL;
+ }
+ strncpy(cpy, str, len);
+ cpy[len] = '\0';
+ return cpy;
+}
+
+/* Adapted from i3wm */
+uint32_t get_color_pixel(const char *color)
+{
+ unsigned int red, green, blue;
+ if (sscanf(color + 1, "%02x%02x%02x", &red, &green, &blue) == 3) {
+ /* We set the first 8 bits high to have 100% opacity in case of a 32 bit
+ * color depth visual. */
+ return (0xFF << 24) | (red << 16 | green << 8 | blue);
+ } else {
+ return screen->black_pixel;
+ }
+}
+
+bool is_hex_color(const char *color)
+{
+ if (color[0] != '#' || strlen(color) != 7) {
+ return false;
+ }
+ for (int i = 1; i < 7; i++) {
+ if (!isxdigit(color[i])) {
+ return false;
+ }
+ }
+ return true;
+}
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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.
+ */
+
+#ifndef BSPWM_HELPERS_H
+#define BSPWM_HELPERS_H
+
+#include <xcb/xcb.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <float.h>
+
+#define LENGTH(x) (sizeof(x) / sizeof(*x))
+#define MAX(A, B) ((A) > (B) ? (A) : (B))
+#define MIN(A, B) ((A) < (B) ? (A) : (B))
+
+#define IS_TILED(c) (c->state == STATE_TILED || c->state == STATE_PSEUDO_TILED)
+#define IS_FLOATING(c) (c->state == STATE_FLOATING)
+#define IS_FULLSCREEN(c) (c->state == STATE_FULLSCREEN)
+#define IS_RECEPTACLE(n) (is_leaf(n) && n->client == NULL)
+#define IS_MONOCLE(d) (d->layout == LAYOUT_MONOCLE || (single_monocle && tiled_count(d->root) <= 1))
+
+#define BOOL_STR(A) ((A) ? "true" : "false")
+#define ON_OFF_STR(A) ((A) ? "on" : "off")
+#define LAYOUT_STR(A) ((A) == LAYOUT_TILED ? "tiled" : "monocle")
+#define LAYOUT_CHR(A) ((A) == LAYOUT_TILED ? 'T' : 'M')
+#define CHILD_POL_STR(A) ((A) == FIRST_CHILD ? "first_child" : "second_child")
+#define SPLIT_TYPE_STR(A) ((A) == TYPE_HORIZONTAL ? "horizontal" : "vertical")
+#define SPLIT_MODE_STR(A) ((A) == MODE_AUTOMATIC ? "automatic" : "manual")
+#define SPLIT_DIR_STR(A) ((A) == DIR_NORTH ? "north" : ((A) == DIR_WEST ? "west" : ((A) == DIR_SOUTH ? "south" : "east")))
+#define STATE_STR(A) ((A) == STATE_TILED ? "tiled" : ((A) == STATE_FLOATING ? "floating" : ((A) == STATE_FULLSCREEN ? "fullscreen" : "pseudo_tiled")))
+#define STATE_CHR(A) ((A) == STATE_TILED ? 'T' : ((A) == STATE_FLOATING ? 'F' : ((A) == STATE_FULLSCREEN ? '=' : 'P')))
+#define LAYER_STR(A) ((A) == LAYER_BELOW ? "below" : ((A) == LAYER_NORMAL ? "normal" : "above"))
+
+#define XCB_CONFIG_WINDOW_X_Y (XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y)
+#define XCB_CONFIG_WINDOW_WIDTH_HEIGHT (XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT)
+#define XCB_CONFIG_WINDOW_X_Y_WIDTH_HEIGHT (XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT)
+
+#define MAXLEN 256
+#define SMALEN 32
+#define INIT_CAP 8
+
+#define cleaned_mask(m) (m & ~(num_lock | scroll_lock | caps_lock))
+#define streq(s1, s2) (strcmp((s1), (s2)) == 0)
+#define unsigned_subtract(a, b) \
+ do { \
+ if (b > a) { \
+ a = 0; \
+ } else { \
+ a -= b; \
+ } \
+ } while (false)
+
+
+void warn(char *fmt, ...);
+void err(char *fmt, ...);
+char *read_string(const char *file_path, size_t *tlen);
+char *copy_string(char *str, size_t len);
+uint32_t get_color_pixel(const char *color);
+bool is_hex_color(const char *color);
+
+#endif
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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 <stdlib.h>
+#include <stdbool.h>
+#include "bspwm.h"
+#include "tree.h"
+#include "query.h"
+
+history_t *make_history(monitor_t *m, desktop_t *d, node_t *n)
+{
+ history_t *h = calloc(1, sizeof(history_t));
+ h->loc = (coordinates_t) {m, d, n};
+ h->prev = h->next = NULL;
+ h->latest = true;
+ return h;
+}
+
+void history_add(monitor_t *m, desktop_t *d, node_t *n)
+{
+ if (!record_history) {
+ return;
+ }
+ history_needle = NULL;
+ history_t *h = make_history(m, d, n);
+ if (history_head == NULL) {
+ history_head = history_tail = h;
+ } else if ((n != NULL && history_tail->loc.node != n) || (n == NULL && d != history_tail->loc.desktop)) {
+ for (history_t *hh = history_tail; hh != NULL; hh = hh->prev) {
+ if ((n != NULL && hh->loc.node == n) || (n == NULL && d == hh->loc.desktop)) {
+ hh->latest = false;
+ }
+ }
+ history_tail->next = h;
+ h->prev = history_tail;
+ history_tail = h;
+ } else {
+ free(h);
+ }
+}
+
+void history_transfer_node(monitor_t *m, desktop_t *d, node_t *n)
+{
+ for (history_t *h = history_head; h != NULL; h = h->next) {
+ if (is_descendant(h->loc.node, n)) {
+ h->loc.monitor = m;
+ h->loc.desktop = d;
+ }
+ }
+}
+
+void history_transfer_desktop(monitor_t *m, desktop_t *d)
+{
+ for (history_t *h = history_head; h != NULL; h = h->next) {
+ if (h->loc.desktop == d) {
+ h->loc.monitor = m;
+ }
+ }
+}
+
+void history_swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop_t *d2, node_t *n2)
+{
+ for (history_t *h = history_head; h != NULL; h = h->next) {
+ if (is_descendant(h->loc.node, n1)) {
+ h->loc.monitor = m2;
+ h->loc.desktop = d2;
+ } else if (is_descendant(h->loc.node, n2)) {
+ h->loc.monitor = m1;
+ h->loc.desktop = d1;
+ }
+ }
+}
+
+void history_swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2)
+{
+ for (history_t *h = history_head; h != NULL; h = h->next) {
+ if (h->loc.desktop == d1) {
+ h->loc.monitor = m2;
+ } else if (h->loc.desktop == d2) {
+ h->loc.monitor = m1;
+ }
+ }
+}
+
+void history_remove(desktop_t *d, node_t *n, bool deep)
+{
+ /* removing from the newest to the oldest is required */
+ /* for maintaining the *latest* attribute */
+ history_t *b = history_tail;
+ while (b != NULL) {
+ if ((n != NULL && ((deep && is_descendant(b->loc.node, n)) || (!deep && b->loc.node == n))) ||
+ (n == NULL && d == b->loc.desktop)) {
+ history_t *a = b->next;
+ history_t *c = b->prev;
+ if (a != NULL) {
+ /* remove duplicate entries */
+ while (c != NULL && ((a->loc.node != NULL && a->loc.node == c->loc.node) ||
+ (a->loc.node == NULL && a->loc.desktop == c->loc.desktop))) {
+ history_t *p = c->prev;
+ if (history_head == c) {
+ history_head = history_tail;
+ }
+ if (history_needle == c) {
+ history_needle = history_tail;
+ }
+ free(c);
+ c = p;
+ }
+ a->prev = c;
+ }
+ if (c != NULL) {
+ c->next = a;
+ }
+ if (history_tail == b) {
+ history_tail = c;
+ }
+ if (history_head == b) {
+ history_head = a;
+ }
+ if (history_needle == b) {
+ history_needle = c;
+ }
+ free(b);
+ b = c;
+ } else {
+ b = b->prev;
+ }
+ }
+}
+
+void empty_history(void)
+{
+ history_t *h = history_head;
+ while (h != NULL) {
+ history_t *next = h->next;
+ free(h);
+ h = next;
+ }
+ history_head = history_tail = NULL;
+}
+
+node_t *history_last_node(desktop_t *d, node_t *n)
+{
+ for (history_t *h = history_tail; h != NULL; h = h->prev) {
+ if (h->latest && h->loc.node != NULL && !h->loc.node->hidden &&
+ !is_descendant(h->loc.node, n) && h->loc.desktop == d) {
+ return h->loc.node;
+ }
+ }
+ return NULL;
+}
+
+desktop_t *history_last_desktop(monitor_t *m, desktop_t *d)
+{
+ for (history_t *h = history_tail; h != NULL; h = h->prev) {
+ if (h->latest && h->loc.desktop != d && h->loc.monitor == m) {
+ return h->loc.desktop;
+ }
+ }
+ return NULL;
+}
+
+monitor_t *history_last_monitor(monitor_t *m)
+{
+ for (history_t *h = history_tail; h != NULL; h = h->prev) {
+ if (h->latest && h->loc.monitor != m) {
+ return h->loc.monitor;
+ }
+ }
+ return NULL;
+}
+
+bool history_find_node(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, node_select_t sel)
+{
+ if (history_needle == NULL || record_history) {
+ history_needle = history_tail;
+ }
+
+ history_t *h;
+ for (h = history_needle; h != NULL; h = (hdi == HISTORY_OLDER ? h->prev : h->next)) {
+ if (!h->latest ||
+ h->loc.node == NULL ||
+ h->loc.node == ref->node ||
+ h->loc.node->hidden ||
+ !node_matches(&h->loc, ref, sel)) {
+ continue;
+ }
+ if (!record_history) {
+ history_needle = h;
+ }
+ *dst = h->loc;
+ return true;
+ }
+ return false;
+}
+
+bool history_find_desktop(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, desktop_select_t sel)
+{
+ if (history_needle == NULL || record_history) {
+ history_needle = history_tail;
+ }
+
+ history_t *h;
+ for (h = history_needle; h != NULL; h = (hdi == HISTORY_OLDER ? h->prev : h->next)) {
+ if (!h->latest ||
+ h->loc.desktop == ref->desktop ||
+ !desktop_matches(&h->loc, ref, sel)) {
+ continue;
+ }
+ if (!record_history) {
+ history_needle = h;
+ }
+ *dst = h->loc;
+ return true;
+ }
+ return false;
+}
+
+bool history_find_monitor(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, monitor_select_t sel)
+{
+ if (history_needle == NULL || record_history) {
+ history_needle = history_tail;
+ }
+
+ history_t *h;
+
+ for (h = history_needle; h != NULL; h = (hdi == HISTORY_OLDER ? h->prev : h->next)) {
+ if (!h->latest ||
+ h->loc.monitor == ref->monitor ||
+ !monitor_matches(&h->loc, ref, sel)) {
+ continue;
+ }
+ if (!record_history) {
+ history_needle = h;
+ }
+ *dst = h->loc;
+ return true;
+ }
+
+ return false;
+}
+
+uint32_t history_rank(node_t *n)
+{
+ uint32_t r = 0;
+ history_t *h = history_tail;
+ while (h != NULL && (!h->latest || h->loc.node != n)) {
+ h = h->prev;
+ r++;
+ }
+ if (h == NULL) {
+ return UINT32_MAX;
+ } else {
+ return r;
+ }
+}
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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.
+ */
+
+#ifndef BSPWM_HISTORY_H
+#define BSPWM_HISTORY_H
+
+#include "types.h"
+
+history_t *make_history(monitor_t *m, desktop_t *d, node_t *n);
+void history_add(monitor_t *m, desktop_t *d, node_t *n);
+void history_transfer_node(monitor_t *m, desktop_t *d, node_t *n);
+void history_transfer_desktop(monitor_t *m, desktop_t *d);
+void history_swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop_t *d2, node_t *n2);
+void history_swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2);
+void history_remove(desktop_t *d, node_t *n, bool deep);
+void empty_history(void);
+node_t *history_last_node(desktop_t *d, node_t *n);
+desktop_t *history_last_desktop(monitor_t *m, desktop_t *d);
+monitor_t *history_last_monitor(monitor_t *m);
+bool history_find_node(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, node_select_t sel);
+bool history_find_desktop(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, desktop_select_t sel);
+bool history_find_monitor(history_dir_t hdi, coordinates_t *ref, coordinates_t *dst, monitor_select_t sel);
+uint32_t history_rank(node_t *n);
+
+#endif
--- /dev/null
+#include "jsmn.h"
+
+/**
+ * Allocates a fresh unused token from the token pull.
+ */
+static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
+ jsmntok_t *tokens, size_t num_tokens) {
+ jsmntok_t *tok;
+ if (parser->toknext >= num_tokens) {
+ return NULL;
+ }
+ tok = &tokens[parser->toknext++];
+ tok->start = tok->end = -1;
+ tok->size = 0;
+#ifdef JSMN_PARENT_LINKS
+ tok->parent = -1;
+#endif
+ return tok;
+}
+
+/**
+ * Fills token type and boundaries.
+ */
+static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
+ int start, int end) {
+ token->type = type;
+ token->start = start;
+ token->end = end;
+ token->size = 0;
+}
+
+/**
+ * Fills next available token with JSON primitive.
+ */
+static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
+ size_t len, jsmntok_t *tokens, size_t num_tokens) {
+ jsmntok_t *token;
+ int start;
+
+ start = parser->pos;
+
+ for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
+ switch (js[parser->pos]) {
+#ifndef JSMN_STRICT
+ /* In strict mode primitive must be followed by "," or "}" or "]" */
+ case ':':
+#endif
+ case '\t' : case '\r' : case '\n' : case ' ' :
+ case ',' : case ']' : case '}' :
+ goto found;
+ }
+ if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
+ parser->pos = start;
+ return JSMN_ERROR_INVAL;
+ }
+ }
+#ifdef JSMN_STRICT
+ /* In strict mode primitive must be followed by a comma/object/array */
+ parser->pos = start;
+ return JSMN_ERROR_PART;
+#endif
+
+found:
+ if (tokens == NULL) {
+ parser->pos--;
+ return 0;
+ }
+ token = jsmn_alloc_token(parser, tokens, num_tokens);
+ if (token == NULL) {
+ parser->pos = start;
+ return JSMN_ERROR_NOMEM;
+ }
+ jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
+#ifdef JSMN_PARENT_LINKS
+ token->parent = parser->toksuper;
+#endif
+ parser->pos--;
+ return 0;
+}
+
+/**
+ * Filsl next token with JSON string.
+ */
+static int jsmn_parse_string(jsmn_parser *parser, const char *js,
+ size_t len, jsmntok_t *tokens, size_t num_tokens) {
+ jsmntok_t *token;
+
+ int start = parser->pos;
+
+ parser->pos++;
+
+ /* Skip starting quote */
+ for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
+ char c = js[parser->pos];
+
+ /* Quote: end of string */
+ if (c == '\"') {
+ if (tokens == NULL) {
+ return 0;
+ }
+ token = jsmn_alloc_token(parser, tokens, num_tokens);
+ if (token == NULL) {
+ parser->pos = start;
+ return JSMN_ERROR_NOMEM;
+ }
+ jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
+#ifdef JSMN_PARENT_LINKS
+ token->parent = parser->toksuper;
+#endif
+ return 0;
+ }
+
+ /* Backslash: Quoted symbol expected */
+ if (c == '\\' && parser->pos + 1 < len) {
+ int i;
+ parser->pos++;
+ switch (js[parser->pos]) {
+ /* Allowed escaped symbols */
+ case '\"': case '/' : case '\\' : case 'b' :
+ case 'f' : case 'r' : case 'n' : case 't' :
+ break;
+ /* Allows escaped symbol \uXXXX */
+ case 'u':
+ parser->pos++;
+ for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
+ /* If it isn't a hex character we have an error */
+ if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
+ (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
+ (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
+ parser->pos = start;
+ return JSMN_ERROR_INVAL;
+ }
+ parser->pos++;
+ }
+ parser->pos--;
+ break;
+ /* Unexpected symbol */
+ default:
+ parser->pos = start;
+ return JSMN_ERROR_INVAL;
+ }
+ }
+ }
+ parser->pos = start;
+ return JSMN_ERROR_PART;
+}
+
+/**
+ * Parse JSON string and fill tokens.
+ */
+int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
+ jsmntok_t *tokens, unsigned int num_tokens) {
+ int r;
+ int i;
+ jsmntok_t *token;
+ int count = parser->toknext;
+
+ for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
+ char c;
+ jsmntype_t type;
+
+ c = js[parser->pos];
+ switch (c) {
+ case '{': case '[':
+ count++;
+ if (tokens == NULL) {
+ break;
+ }
+ token = jsmn_alloc_token(parser, tokens, num_tokens);
+ if (token == NULL)
+ return JSMN_ERROR_NOMEM;
+ if (parser->toksuper != -1) {
+ tokens[parser->toksuper].size++;
+#ifdef JSMN_PARENT_LINKS
+ token->parent = parser->toksuper;
+#endif
+ }
+ token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
+ token->start = parser->pos;
+ parser->toksuper = parser->toknext - 1;
+ break;
+ case '}': case ']':
+ if (tokens == NULL)
+ break;
+ type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
+#ifdef JSMN_PARENT_LINKS
+ if (parser->toknext < 1) {
+ return JSMN_ERROR_INVAL;
+ }
+ token = &tokens[parser->toknext - 1];
+ for (;;) {
+ if (token->start != -1 && token->end == -1) {
+ if (token->type != type) {
+ return JSMN_ERROR_INVAL;
+ }
+ token->end = parser->pos + 1;
+ parser->toksuper = token->parent;
+ break;
+ }
+ if (token->parent == -1) {
+ break;
+ }
+ token = &tokens[token->parent];
+ }
+#else
+ for (i = parser->toknext - 1; i >= 0; i--) {
+ token = &tokens[i];
+ if (token->start != -1 && token->end == -1) {
+ if (token->type != type) {
+ return JSMN_ERROR_INVAL;
+ }
+ parser->toksuper = -1;
+ token->end = parser->pos + 1;
+ break;
+ }
+ }
+ /* Error if unmatched closing bracket */
+ if (i == -1) return JSMN_ERROR_INVAL;
+ for (; i >= 0; i--) {
+ token = &tokens[i];
+ if (token->start != -1 && token->end == -1) {
+ parser->toksuper = i;
+ break;
+ }
+ }
+#endif
+ break;
+ case '\"':
+ r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
+ if (r < 0) return r;
+ count++;
+ if (parser->toksuper != -1 && tokens != NULL)
+ tokens[parser->toksuper].size++;
+ break;
+ case '\t' : case '\r' : case '\n' : case ' ':
+ break;
+ case ':':
+ parser->toksuper = parser->toknext - 1;
+ break;
+ case ',':
+ if (tokens != NULL && parser->toksuper != -1 &&
+ tokens[parser->toksuper].type != JSMN_ARRAY &&
+ tokens[parser->toksuper].type != JSMN_OBJECT) {
+#ifdef JSMN_PARENT_LINKS
+ parser->toksuper = tokens[parser->toksuper].parent;
+#else
+ for (i = parser->toknext - 1; i >= 0; i--) {
+ if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
+ if (tokens[i].start != -1 && tokens[i].end == -1) {
+ parser->toksuper = i;
+ break;
+ }
+ }
+ }
+#endif
+ }
+ break;
+#ifdef JSMN_STRICT
+ /* In strict mode primitives are: numbers and booleans */
+ case '-': case '0': case '1' : case '2': case '3' : case '4':
+ case '5': case '6': case '7' : case '8': case '9':
+ case 't': case 'f': case 'n' :
+ /* And they must not be keys of the object */
+ if (tokens != NULL && parser->toksuper != -1) {
+ jsmntok_t *t = &tokens[parser->toksuper];
+ if (t->type == JSMN_OBJECT ||
+ (t->type == JSMN_STRING && t->size != 0)) {
+ return JSMN_ERROR_INVAL;
+ }
+ }
+#else
+ /* In non-strict mode every unquoted value is a primitive */
+ default:
+#endif
+ r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
+ if (r < 0) return r;
+ count++;
+ if (parser->toksuper != -1 && tokens != NULL)
+ tokens[parser->toksuper].size++;
+ break;
+
+#ifdef JSMN_STRICT
+ /* Unexpected char in strict mode */
+ default:
+ return JSMN_ERROR_INVAL;
+#endif
+ }
+ }
+
+ if (tokens != NULL) {
+ for (i = parser->toknext - 1; i >= 0; i--) {
+ /* Unmatched opened object or array */
+ if (tokens[i].start != -1 && tokens[i].end == -1) {
+ return JSMN_ERROR_PART;
+ }
+ }
+ }
+
+ return count;
+}
+
+/**
+ * Creates a new parser based over a given buffer with an array of tokens
+ * available.
+ */
+void jsmn_init(jsmn_parser *parser) {
+ parser->pos = 0;
+ parser->toknext = 0;
+ parser->toksuper = -1;
+}
+
--- /dev/null
+#ifndef __JSMN_H_
+#define __JSMN_H_
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * JSON type identifier. Basic types are:
+ * o Object
+ * o Array
+ * o String
+ * o Other primitive: number, boolean (true/false) or null
+ */
+typedef enum {
+ JSMN_UNDEFINED = 0,
+ JSMN_OBJECT = 1,
+ JSMN_ARRAY = 2,
+ JSMN_STRING = 3,
+ JSMN_PRIMITIVE = 4
+} jsmntype_t;
+
+enum jsmnerr {
+ /* Not enough tokens were provided */
+ JSMN_ERROR_NOMEM = -1,
+ /* Invalid character inside JSON string */
+ JSMN_ERROR_INVAL = -2,
+ /* The string is not a full JSON packet, more bytes expected */
+ JSMN_ERROR_PART = -3
+};
+
+/**
+ * JSON token description.
+ * @param type type (object, array, string etc.)
+ * @param start start position in JSON data string
+ * @param end end position in JSON data string
+ */
+typedef struct {
+ jsmntype_t type;
+ int start;
+ int end;
+ int size;
+#ifdef JSMN_PARENT_LINKS
+ int parent;
+#endif
+} jsmntok_t;
+
+/**
+ * JSON parser. Contains an array of token blocks available. Also stores
+ * the string being parsed now and current position in that string
+ */
+typedef struct {
+ unsigned int pos; /* offset in the JSON string */
+ unsigned int toknext; /* next token to allocate */
+ int toksuper; /* superior token node, e.g parent object or array */
+} jsmn_parser;
+
+/**
+ * Create JSON parser over an array of tokens
+ */
+void jsmn_init(jsmn_parser *parser);
+
+/**
+ * Run JSON parser. It parses a JSON data string into and array of tokens, each describing
+ * a single JSON object.
+ */
+int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
+ jsmntok_t *tokens, unsigned int num_tokens);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __JSMN_H_ */
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include "bspwm.h"
+#include "desktop.h"
+#include "monitor.h"
+#include "pointer.h"
+#include "query.h"
+#include "rule.h"
+#include "restore.h"
+#include "settings.h"
+#include "tree.h"
+#include "window.h"
+#include "common.h"
+#include "parse.h"
+#include "messages.h"
+
+void handle_message(char *msg, int msg_len, FILE *rsp)
+{
+ int cap = INIT_CAP;
+ int num = 0;
+ char **args = calloc(cap, sizeof(char *));
+
+ if (args == NULL) {
+ perror("Handle message: calloc");
+ return;
+ }
+
+ for (int i = 0, j = 0; i < msg_len; i++) {
+ if (msg[i] == 0) {
+ args[num++] = msg + j;
+ j = i + 1;
+ }
+ if (num >= cap) {
+ cap *= 2;
+ char **new = realloc(args, cap * sizeof(char *));
+ if (new == NULL) {
+ free(args);
+ perror("Handle message: realloc");
+ return;
+ } else {
+ args = new;
+ }
+ }
+ }
+
+ if (num < 1) {
+ free(args);
+ fail(rsp, "No arguments given.\n");
+ return;
+ }
+
+ char **args_orig = args;
+ process_message(args, num, rsp);
+ free(args_orig);
+}
+
+void process_message(char **args, int num, FILE *rsp)
+{
+ int ret = SUBSCRIBE_FAILURE;
+
+ if (streq("node", *args)) {
+ cmd_node(++args, --num, rsp);
+ } else if (streq("desktop", *args)) {
+ cmd_desktop(++args, --num, rsp);
+ } else if (streq("monitor", *args)) {
+ cmd_monitor(++args, --num, rsp);
+ } else if (streq("query", *args)) {
+ cmd_query(++args, --num, rsp);
+ } else if (streq("subscribe", *args)) {
+ ret = cmd_subscribe(++args, --num, rsp);
+ } else if (streq("wm", *args)) {
+ cmd_wm(++args, --num, rsp);
+ } else if (streq("rule", *args)) {
+ cmd_rule(++args, --num, rsp);
+ } else if (streq("config", *args)) {
+ cmd_config(++args, --num, rsp);
+ } else if (streq("quit", *args)) {
+ cmd_quit(++args, --num, rsp);
+ } else {
+ fail(rsp, "Unknown domain or command: '%s'.\n", *args);
+ }
+
+ fflush(rsp);
+
+ if (ret != SUBSCRIBE_SUCCESS) {
+ fclose(rsp);
+ }
+}
+
+void cmd_node(char **args, int num, FILE *rsp)
+{
+ if (num < 1) {
+ fail(rsp, "node: Missing commands.\n");
+ return;
+ }
+
+ coordinates_t ref = {mon, mon->desk, mon->desk->focus};
+ coordinates_t trg = ref;
+
+ if ((*args)[0] != OPT_CHR) {
+ int ret;
+ if ((ret = node_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {
+ num--, args++;
+ } else {
+ handle_failure(ret, "node", *args, rsp);
+ return;
+ }
+ }
+
+ bool changed = false;
+
+ while (num > 0) {
+ if (streq("-f", *args) || streq("--focus", *args)) {
+ coordinates_t dst = trg;
+ if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+ num--, args++;
+ int ret;
+ if ((ret = node_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
+ handle_failure(ret, "node -f", *args, rsp);
+ break;
+ }
+ }
+ if (dst.node == NULL || !focus_node(dst.monitor, dst.desktop, dst.node)) {
+ fail(rsp, "");
+ break;
+ }
+ } else if (streq("-a", *args) || streq("--activate", *args)) {
+ coordinates_t dst = trg;
+ if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+ num--, args++;
+ int ret;
+ if ((ret = node_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
+ handle_failure(ret, "node -a", *args, rsp);
+ break;
+ }
+ }
+ if (dst.node == NULL || !activate_node(dst.monitor, dst.desktop, dst.node)) {
+ fail(rsp, "");
+ break;
+ }
+ } else if (streq("-d", *args) || streq("--to-desktop", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+ break;
+ }
+ coordinates_t dst;
+ int ret;
+ if ((ret = desktop_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
+ if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.desktop->focus)) {
+ trg.monitor = dst.monitor;
+ trg.desktop = dst.desktop;
+ } else {
+ fail(rsp, "");
+ break;
+ }
+ } else {
+ handle_failure(ret, "node -d", *args, rsp);
+ break;
+ }
+ } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+ break;
+ }
+ coordinates_t dst;
+ int ret;
+ if ((ret = monitor_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
+ if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.monitor->desk, dst.monitor->desk->focus)) {
+ trg.monitor = dst.monitor;
+ trg.desktop = dst.monitor->desk;
+ } else {
+ fail(rsp, "");
+ break;
+ }
+ } else {
+ handle_failure(ret, "node -m", *args, rsp);
+ break;
+ }
+ } else if (streq("-n", *args) || streq("--to-node", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+ break;
+ }
+ coordinates_t dst;
+ int ret;
+ if ((ret = node_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
+ if (transfer_node(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.node)) {
+ trg.monitor = dst.monitor;
+ trg.desktop = dst.desktop;
+ } else {
+ fail(rsp, "");
+ break;
+ }
+ } else {
+ handle_failure(ret, "node -n", *args, rsp);
+ break;
+ }
+ } else if (streq("-s", *args) || streq("--swap", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+ break;
+ }
+ coordinates_t dst;
+ int ret;
+ if ((ret = node_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
+ if (swap_nodes(trg.monitor, trg.desktop, trg.node, dst.monitor, dst.desktop, dst.node)) {
+ trg.monitor = dst.monitor;
+ trg.desktop = dst.desktop;
+ } else {
+ fail(rsp, "");
+ break;
+ }
+ } else {
+ handle_failure(ret, "node -s", *args, rsp);
+ break;
+ }
+ } else if (streq("-l", *args) || streq("--layer", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+ break;
+ }
+ stack_layer_t lyr;
+ if (parse_stack_layer(*args, &lyr)) {
+ if (!set_layer(trg.monitor, trg.desktop, trg.node, lyr)) {
+ fail(rsp, "");
+ break;
+ }
+ } else {
+ fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
+ break;
+ }
+ } else if (streq("-t", *args) || streq("--state", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+ break;
+ }
+ client_state_t cst;
+ bool alternate = false;
+ if ((*args)[0] == '~') {
+ alternate = true;
+ (*args)++;
+ }
+ if (parse_client_state(*args, &cst)) {
+ if (alternate && trg.node != NULL && trg.node->client != NULL &&
+ trg.node->client->state == cst) {
+ cst = trg.node->client->last_state;
+ }
+ if (!set_state(trg.monitor, trg.desktop, trg.node, cst)) {
+ fail(rsp, "");
+ break;
+ }
+ changed = true;
+ } else {
+ fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
+ break;
+ }
+ } else if (streq("-g", *args) || streq("--flag", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+ break;
+ }
+ if (trg.node == NULL) {
+ fail(rsp, "");
+ break;
+ }
+ char *key = strtok(*args, EQL_TOK);
+ char *val = strtok(NULL, EQL_TOK);
+ alter_state_t a;
+ bool b;
+ if (val == NULL) {
+ a = ALTER_TOGGLE;
+ } else {
+ if (parse_bool(val, &b)) {
+ a = ALTER_SET;
+ } else {
+ fail(rsp, "node %s: Invalid value for %s: '%s'.\n", *(args - 1), key, val);
+ break;
+ }
+ }
+ if (streq("hidden", key)) {
+ set_hidden(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->hidden));
+ changed = true;
+ } else if (streq("sticky", key)) {
+ set_sticky(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->sticky));
+ } else if (streq("private", key)) {
+ set_private(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->private));
+ } else if (streq("locked", key)) {
+ set_locked(trg.monitor, trg.desktop, trg.node, (a == ALTER_SET ? b : !trg.node->locked));
+ } else {
+ fail(rsp, "node %s: Invalid key: '%s'.\n", *(args - 1), key);
+ break;
+ }
+ } else if (streq("-p", *args) || streq("--presel-dir", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+ break;
+ }
+ if (trg.node == NULL || trg.node->vacant) {
+ fail(rsp, "");
+ break;
+ }
+ if (streq("cancel", *args)) {
+ cancel_presel(trg.monitor, trg.desktop, trg.node);
+ } else {
+ bool alternate = false;
+ if ((*args)[0] == '~') {
+ alternate = true;
+ (*args)++;
+ }
+ direction_t dir;
+ if (parse_direction(*args, &dir)) {
+ if (alternate && trg.node->presel != NULL && trg.node->presel->split_dir == dir) {
+ cancel_presel(trg.monitor, trg.desktop, trg.node);
+ } else {
+ presel_dir(trg.monitor, trg.desktop, trg.node, dir);
+ if (!IS_RECEPTACLE(trg.node)) {
+ draw_presel_feedback(trg.monitor, trg.desktop, trg.node);
+ }
+ }
+ } else {
+ fail(rsp, "node %s: Invalid argument: '%s%s'.\n", *(args - 1), alternate?"~":"", *args);
+ break;
+ }
+ }
+ } else if (streq("-o", *args) || streq("--presel-ratio", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+ break;
+ }
+ if (trg.node == NULL || trg.node->vacant) {
+ fail(rsp, "");
+ break;
+ }
+ double rat;
+ if (sscanf(*args, "%lf", &rat) != 1 || rat <= 0 || rat >= 1) {
+ fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
+ break;
+ } else {
+ presel_ratio(trg.monitor, trg.desktop, trg.node, rat);
+ draw_presel_feedback(trg.monitor, trg.desktop, trg.node);
+ }
+ } else if (streq("-v", *args) || streq("--move", *args)) {
+ num--, args++;
+ if (num < 2) {
+ fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+ break;
+ }
+ int dx = 0, dy = 0;
+ if (sscanf(*args, "%i", &dx) == 1) {
+ num--, args++;
+ if (sscanf(*args, "%i", &dy) == 1) {
+ if (!move_client(&trg, dx, dy)) {
+ fail(rsp, "");
+ break;
+ }
+ } else {
+ fail(rsp, "node %s: Invalid dy argument: '%s'.\n", *(args - 3), *args);
+ break;
+ }
+ } else {
+ fail(rsp, "node %s: Invalid dx argument: '%s'.\n", *(args - 2), *args);
+ break;
+ }
+ } else if (streq("-z", *args) || streq("--resize", *args)) {
+ num--, args++;
+ if (num < 3) {
+ fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+ break;
+ }
+ resize_handle_t rh;
+ if (parse_resize_handle(*args, &rh)) {
+ num--, args++;
+ int dx = 0, dy = 0;
+ if (sscanf(*args, "%i", &dx) == 1) {
+ num--, args++;
+ if (sscanf(*args, "%i", &dy) == 1) {
+ if (!resize_client(&trg, rh, dx, dy)) {
+ fail(rsp, "");
+ break;
+ }
+ } else {
+ fail(rsp, "node %s: Invalid dy argument: '%s'.\n", *(args - 3), *args);
+ break;
+ }
+ } else {
+ fail(rsp, "node %s: Invalid dx argument: '%s'.\n", *(args - 2), *args);
+ break;
+ }
+ } else {
+ fail(rsp, "node %s: Invalid resize handle argument: '%s'.\n", *(args - 1), *args);
+ break;
+ }
+ } else if (streq("-r", *args) || streq("--ratio", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+ break;
+ }
+ if (trg.node == NULL) {
+ fail(rsp, "");
+ break;
+ }
+ if ((*args)[0] == '+' || (*args)[0] == '-') {
+ int pix;
+ if (sscanf(*args, "%i", &pix) == 1) {
+ int max = (trg.node->split_type == TYPE_HORIZONTAL ? trg.node->rectangle.height : trg.node->rectangle.width);
+ double rat = ((max * trg.node->split_ratio) + pix) / max;
+ if (rat > 0 && rat < 1) {
+ set_ratio(trg.node, rat);
+ } else {
+ fail(rsp, "");
+ break;
+ }
+ } else {
+ fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
+ break;
+ }
+ } else {
+ double rat;
+ if (sscanf(*args, "%lf", &rat) == 1 && rat > 0 && rat < 1) {
+ set_ratio(trg.node, rat);
+ } else {
+ fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
+ break;
+ }
+ }
+ changed = true;
+ } else if (streq("-F", *args) || streq("--flip", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+ break;
+ }
+ if (trg.node == NULL) {
+ fail(rsp, "");
+ break;
+ }
+ flip_t flp;
+ if (parse_flip(*args, &flp)) {
+ flip_tree(trg.node, flp);
+ changed = true;
+ } else {
+ fail(rsp, "");
+ break;
+ }
+ } else if (streq("-R", *args) || streq("--rotate", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+ break;
+ }
+ if (trg.node == NULL) {
+ fail(rsp, "");
+ break;
+ }
+ int deg;
+ if (parse_degree(*args, °)) {
+ rotate_tree(trg.node, deg);
+ changed = true;
+ } else {
+ fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
+ break;
+ }
+ } else if (streq("-E", *args) || streq("--equalize", *args)) {
+ if (trg.node == NULL) {
+ fail(rsp, "");
+ break;
+ }
+ equalize_tree(trg.node);
+ changed = true;
+ } else if (streq("-B", *args) || streq("--balance", *args)) {
+ if (trg.node == NULL) {
+ fail(rsp, "");
+ break;
+ }
+ balance_tree(trg.node);
+ changed = true;
+ } else if (streq("-C", *args) || streq("--circulate", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "node %s: Not enough arguments.\n", *(args - 1));
+ break;
+ }
+ if (trg.node == NULL) {
+ fail(rsp, "");
+ break;
+ }
+ circulate_dir_t cir;
+ if (parse_circulate_direction(*args, &cir)) {
+ circulate_leaves(trg.monitor, trg.desktop, trg.node, cir);
+ changed = true;
+ } else {
+ fail(rsp, "node %s: Invalid argument: '%s'.\n", *(args - 1), *args);
+ break;
+ }
+ } else if (streq("-i", *args) || streq("--insert-receptacle", *args)) {
+ insert_receptacle(trg.monitor, trg.desktop, trg.node);
+ changed = true;
+ } else if (streq("-c", *args) || streq("--close", *args)) {
+ if (num > 1) {
+ fail(rsp, "node %s: Trailing commands.\n", *args);
+ break;
+ }
+ if (trg.node == NULL || locked_count(trg.node) > 0) {
+ fail(rsp, "");
+ break;
+ }
+ close_node(trg.node);
+ break;
+ } else if (streq("-k", *args) || streq("--kill", *args)) {
+ if (num > 1) {
+ fail(rsp, "node %s: Trailing commands.\n", *args);
+ break;
+ }
+ if (trg.node == NULL) {
+ fail(rsp, "");
+ break;
+ }
+ kill_node(trg.monitor, trg.desktop, trg.node);
+ changed = true;
+ break;
+ } else {
+ fail(rsp, "node: Unknown command: '%s'.\n", *args);
+ break;
+ }
+
+ num--, args++;
+ }
+
+ if (changed) {
+ arrange(trg.monitor, trg.desktop);
+ }
+}
+
+void cmd_desktop(char **args, int num, FILE *rsp)
+{
+ if (num < 1) {
+ fail(rsp, "desktop: Missing commands.\n");
+ return;
+ }
+
+ coordinates_t ref = {mon, mon->desk, NULL};
+ coordinates_t trg = ref;
+
+ if ((*args)[0] != OPT_CHR) {
+ int ret;
+ if ((ret = desktop_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {
+ num--, args++;
+ } else {
+ handle_failure(ret, "desktop", *args, rsp);
+ return;
+ }
+ }
+
+ bool changed = false;
+
+ while (num > 0) {
+ if (streq("-f", *args) || streq("--focus", *args)) {
+ coordinates_t dst = trg;
+ if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+ num--, args++;
+ int ret;
+ if ((ret = desktop_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
+ handle_failure(ret, "desktop -f", *args, rsp);
+ break;
+ }
+ }
+ focus_node(dst.monitor, dst.desktop, dst.desktop->focus);
+ } else if (streq("-a", *args) || streq("--activate", *args)) {
+ coordinates_t dst = trg;
+ if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+ num--, args++;
+ int ret;
+ if ((ret = desktop_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
+ handle_failure(ret, "desktop -a", *args, rsp);
+ break;
+ }
+ }
+ if (!activate_desktop(dst.monitor, dst.desktop)) {
+ fail(rsp, "");
+ break;
+ }
+ } else if (streq("-m", *args) || streq("--to-monitor", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
+ break;
+ }
+ if (trg.monitor->desk_head == trg.monitor->desk_tail) {
+ fail(rsp, "");
+ break;
+ }
+ coordinates_t dst;
+ int ret;
+ if ((ret = monitor_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
+ if (transfer_desktop(trg.monitor, dst.monitor, trg.desktop)) {
+ trg.monitor = dst.monitor;
+ } else {
+ fail(rsp, "");
+ break;
+ }
+ } else {
+ handle_failure(ret, "desktop -m", *args, rsp);
+ break;
+ }
+ } else if (streq("-s", *args) || streq("--swap", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
+ break;
+ }
+ coordinates_t dst;
+ int ret;
+ if ((ret = desktop_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
+ if (swap_desktops(trg.monitor, trg.desktop, dst.monitor, dst.desktop)) {
+ trg.monitor = dst.monitor;
+ } else {
+ fail(rsp, "");
+ break;
+ }
+ } else {
+ handle_failure(ret, "desktop -s", *args, rsp);
+ break;
+ }
+ } else if (streq("-b", *args) || streq("--bubble", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
+ break;
+ }
+ cycle_dir_t cyc;
+ if (parse_cycle_direction(*args, &cyc)) {
+ desktop_t *d = trg.desktop;
+ if (cyc == CYCLE_PREV) {
+ if (d->prev == NULL) {
+ while (d->next != NULL) {
+ swap_desktops(trg.monitor, d, trg.monitor, d->next);
+ }
+ } else {
+ swap_desktops(trg.monitor, d, trg.monitor, d->prev);
+ }
+ } else {
+ if (d->next == NULL) {
+ while (d->prev != NULL) {
+ swap_desktops(trg.monitor, d, trg.monitor, d->prev);
+ }
+ } else {
+ swap_desktops(trg.monitor, d, trg.monitor, d->next);
+ }
+ }
+ } else {
+ fail(rsp, "desktop %s: Invalid argument: '%s'.\n", *(args - 1), *args);
+ break;
+ }
+ } else if (streq("-l", *args) || streq("--layout", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
+ break;
+ }
+ bool ret;
+ layout_t lyt;
+ cycle_dir_t cyc;
+ if (parse_cycle_direction(*args, &cyc)) {
+ ret = set_layout(trg.monitor, trg.desktop, (trg.desktop->layout + 1) % 2);
+ } else if (parse_layout(*args, &lyt)) {
+ ret = set_layout(trg.monitor, trg.desktop, lyt);
+ } else {
+ fail(rsp, "desktop %s: Invalid argument: '%s'.\n", *(args - 1), *args);
+ break;
+ }
+ if (!ret) {
+ fail(rsp, "");
+ break;
+ }
+ } else if (streq("-n", *args) || streq("--rename", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "desktop %s: Not enough arguments.\n", *(args - 1));
+ break;
+ }
+ rename_desktop(trg.monitor, trg.desktop, *args);
+ } else if (streq("-r", *args) || streq("--remove", *args)) {
+ if (num > 1) {
+ fail(rsp, "desktop %s: Trailing commands.\n", *args);
+ break;
+ }
+ if (trg.desktop->root == NULL &&
+ trg.monitor->desk_head != trg.monitor->desk_tail) {
+ remove_desktop(trg.monitor, trg.desktop);
+ return;
+ } else {
+ fail(rsp, "");
+ break;
+ }
+ } else {
+ fail(rsp, "desktop: Unknown command: '%s'.\n", *args);
+ break;
+ }
+ num--, args++;
+ }
+
+ if (changed) {
+ arrange(trg.monitor, trg.desktop);
+ }
+}
+
+void cmd_monitor(char **args, int num, FILE *rsp)
+{
+ if (num < 1) {
+ fail(rsp, "monitor: Missing commands.\n");
+ return;
+ }
+
+ coordinates_t ref = {mon, NULL, NULL};
+ coordinates_t trg = ref;
+
+ if ((*args)[0] != OPT_CHR) {
+ int ret;
+ if ((ret = monitor_from_desc(*args, &ref, &trg)) == SELECTOR_OK) {
+ num--, args++;
+ } else {
+ handle_failure(ret, "monitor", *args, rsp);
+ return;
+ }
+ }
+
+ while (num > 0) {
+ if (streq("-f", *args) || streq("--focus", *args)) {
+ coordinates_t dst = trg;
+ if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+ num--, args++;
+ int ret;
+ if ((ret = monitor_from_desc(*args, &ref, &dst)) != SELECTOR_OK) {
+ handle_failure(ret, "monitor -f", *args, rsp);
+ fail(rsp, "");
+ return;
+ }
+ }
+ focus_node(dst.monitor, dst.monitor->desk, dst.monitor->desk->focus);
+ } else if (streq("-s", *args) || streq("--swap", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
+ return;
+ }
+ coordinates_t dst;
+ int ret;
+ if ((ret = monitor_from_desc(*args, &ref, &dst)) == SELECTOR_OK) {
+ if (!swap_monitors(trg.monitor, dst.monitor)) {
+ fail(rsp, "");
+ return;
+ }
+ } else {
+ handle_failure(ret, "monitor -s", *args, rsp);
+ return;
+ }
+ } else if (streq("-d", *args) || streq("--reset-desktops", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
+ return;
+ }
+ desktop_t *d = trg.monitor->desk_head;
+ while (num > 0 && d != NULL) {
+ rename_desktop(trg.monitor, d, *args);
+ d = d->next;
+ num--, args++;
+ }
+ put_status(SBSC_MASK_REPORT);
+ while (num > 0) {
+ add_desktop(trg.monitor, make_desktop(*args, XCB_NONE));
+ num--, args++;
+ }
+ while (d != NULL) {
+ desktop_t *next = d->next;
+ if (d == mon->desk) {
+ focus_node(trg.monitor, d->prev, d->prev->focus);
+ }
+ merge_desktops(trg.monitor, d, mon, mon->desk);
+ remove_desktop(trg.monitor, d);
+ d = next;
+ }
+ } else if (streq("-a", *args) || streq("--add-desktops", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
+ return;
+ }
+ while (num > 0) {
+ add_desktop(trg.monitor, make_desktop(*args, XCB_NONE));
+ num--, args++;
+ }
+ } else if (streq("-r", *args) || streq("--remove", *args)) {
+ if (num > 1) {
+ fail(rsp, "monitor %s: Trailing commands.\n", *args);
+ return;
+ }
+ if (mon_head == mon_tail) {
+ fail(rsp, "");
+ return;
+ }
+ remove_monitor(trg.monitor);
+ return;
+ } else if (streq("-o", *args) || streq("--order-desktops", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
+ return;
+ }
+ desktop_t *d = trg.monitor->desk_head;
+ while (d != NULL && num > 0) {
+ desktop_t *next = d->next;
+ coordinates_t dst;
+ if (locate_desktop(*args, &dst) && dst.monitor == trg.monitor) {
+ swap_desktops(trg.monitor, d, dst.monitor, dst.desktop);
+ if (next == dst.desktop) {
+ next = d;
+ }
+ }
+ d = next;
+ num--, args++;
+ }
+ } else if (streq("-g", *args) || streq("--rectangle", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
+ return;
+ }
+ xcb_rectangle_t r;
+ if (parse_rectangle(*args, &r)) {
+ update_root(trg.monitor, &r);
+ } else {
+ fail(rsp, "monitor %s: Invalid argument: '%s'.\n", *(args - 1), *args);
+ return;
+ }
+ } else if (streq("-n", *args) || streq("--rename", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "monitor %s: Not enough arguments.\n", *(args - 1));
+ return;
+ }
+ rename_monitor(trg.monitor, *args);
+ } else {
+ fail(rsp, "monitor: Unknown command: '%s'.\n", *args);
+ return;
+ }
+ num--, args++;
+ }
+}
+
+void cmd_query(char **args, int num, FILE *rsp)
+{
+ coordinates_t ref = {mon, mon->desk, mon->desk->focus};
+ coordinates_t trg = {NULL, NULL, NULL};
+ monitor_select_t *monitor_sel = NULL;
+ desktop_select_t *desktop_sel = NULL;
+ node_select_t *node_sel = NULL;
+ domain_t dom = DOMAIN_TREE;
+ bool print_ids = true;
+ uint8_t d = 0;
+
+ if (num < 1) {
+ fail(rsp, "query: Not enough arguments.\n");
+ return;
+ }
+
+ while (num > 0) {
+ if (streq("-T", *args) || streq("--tree", *args)) {
+ dom = DOMAIN_TREE, d++;
+ } else if (streq("-M", *args) || streq("--monitors", *args)) {
+ dom = DOMAIN_MONITOR, d++;
+ if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+ num--, args++;
+ int ret;
+ coordinates_t tmp = ref;
+ if ((ret = monitor_from_desc(*args, &tmp, &ref)) != SELECTOR_OK) {
+ handle_failure(ret, "query -M", *args, rsp);
+ goto end;
+ }
+ }
+ } else if (streq("-D", *args) || streq("--desktops", *args)) {
+ dom = DOMAIN_DESKTOP, d++;
+ if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+ num--, args++;
+ int ret;
+ coordinates_t tmp = ref;
+ if ((ret = desktop_from_desc(*args, &tmp, &ref)) != SELECTOR_OK) {
+ handle_failure(ret, "query -D", *args, rsp);
+ goto end;
+ }
+ }
+ } else if (streq("-N", *args) || streq("--nodes", *args)) {
+ dom = DOMAIN_NODE, d++;
+ if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+ num--, args++;
+ int ret;
+ coordinates_t tmp = ref;
+ if ((ret = node_from_desc(*args, &tmp, &ref)) != SELECTOR_OK) {
+ handle_failure(ret, "query -N", *args, rsp);
+ goto end;
+ }
+ }
+ } else if (streq("-m", *args) || streq("--monitor", *args)) {
+ if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+ num--, args++;
+ int ret;
+ if ((*args)[0] == '.') {
+ free(monitor_sel);
+ monitor_sel = malloc(sizeof(monitor_select_t));
+ *monitor_sel = make_monitor_select();
+ char *desc = copy_string(*args, strlen(*args));
+ if (!parse_monitor_modifiers(desc, monitor_sel)) {
+ handle_failure(SELECTOR_BAD_MODIFIERS, "query -m", *args, rsp);
+ free(desc);
+ goto end;
+ }
+ free(desc);
+ } else if ((ret = monitor_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
+ handle_failure(ret, "query -m", *args, rsp);
+ goto end;
+ }
+ } else {
+ trg.monitor = ref.monitor;
+ }
+ } else if (streq("-d", *args) || streq("--desktop", *args)) {
+ if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+ num--, args++;
+ int ret;
+ if ((*args)[0] == '.') {
+ free(desktop_sel);
+ desktop_sel = malloc(sizeof(desktop_select_t));
+ *desktop_sel = make_desktop_select();
+ char *desc = copy_string(*args, strlen(*args));
+ if (!parse_desktop_modifiers(desc, desktop_sel)) {
+ handle_failure(SELECTOR_BAD_MODIFIERS, "query -d", *args, rsp);
+ free(desc);
+ goto end;
+ }
+ free(desc);
+ } else if ((ret = desktop_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
+ handle_failure(ret, "query -d", *args, rsp);
+ goto end;
+ }
+ } else {
+ trg.monitor = ref.monitor;
+ trg.desktop = ref.desktop;
+ }
+ } else if (streq("-n", *args) || streq("--node", *args)) {
+ if (num > 1 && *(args + 1)[0] != OPT_CHR) {
+ num--, args++;
+ int ret;
+ if ((*args)[0] == '.') {
+ free(node_sel);
+ node_sel = malloc(sizeof(node_select_t));
+ *node_sel = make_node_select();
+ char *desc = copy_string(*args, strlen(*args));
+ if (!parse_node_modifiers(desc, node_sel)) {
+ handle_failure(SELECTOR_BAD_MODIFIERS, "query -n", *args, rsp);
+ free(desc);
+ goto end;
+ }
+ free(desc);
+ } else if ((ret = node_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
+ handle_failure(ret, "query -n", *args, rsp);
+ goto end;
+ }
+ } else {
+ trg = ref;
+ if (ref.node == NULL) {
+ fail(rsp, "");
+ goto end;
+ }
+ }
+ } else if (streq("--names", *args)) {
+ print_ids = false;
+ } else {
+ fail(rsp, "query: Unknown option: '%s'.\n", *args);
+ goto end;
+ }
+ num--, args++;
+ }
+
+ if (d < 1) {
+ fail(rsp, "query: No commands given.\n");
+ goto end;
+ }
+
+ if (d > 1) {
+ fail(rsp, "query: Multiple commands given.\n");
+ goto end;
+ }
+
+ if (dom == DOMAIN_TREE && trg.monitor == NULL) {
+ fail(rsp, "query -T: No options given.\n");
+ goto end;
+ }
+
+ if (dom == DOMAIN_NODE) {
+ if (query_node_ids(&ref, &trg, node_sel, rsp) < 1) {
+ fail(rsp, "");
+ }
+ } else if (dom == DOMAIN_DESKTOP) {
+ if (query_desktop_ids(&ref, &trg, desktop_sel, print_ids ? fprint_desktop_id : fprint_desktop_name, rsp) < 1) {
+ fail(rsp, "");
+ }
+ } else if (dom == DOMAIN_MONITOR) {
+ if (query_monitor_ids(&ref, &trg, monitor_sel, print_ids ? fprint_monitor_id : fprint_monitor_name, rsp) < 1) {
+ fail(rsp, "");
+ }
+ } else {
+ if (trg.node != NULL) {
+ query_node(trg.node, rsp);
+ } else if (trg.desktop != NULL) {
+ query_desktop(trg.desktop, rsp);
+ } else {
+ query_monitor(trg.monitor, rsp);
+ }
+ fprintf(rsp, "\n");
+ }
+
+end:
+ free(monitor_sel);
+ free(desktop_sel);
+ free(node_sel);
+}
+
+void cmd_rule(char **args, int num, FILE *rsp)
+{
+ if (num < 1) {
+ fail(rsp, "rule: Missing commands.\n");
+ return;
+ }
+
+ while (num > 0) {
+ if (streq("-a", *args) || streq("--add", *args)) {
+ num--, args++;
+ if (num < 2) {
+ fail(rsp, "rule %s: Not enough arguments.\n", *(args - 1));
+ return;
+ }
+ rule_t *rule = make_rule();
+ char *class_name = strtok(*args, COL_TOK);
+ char *instance_name = strtok(NULL, COL_TOK);
+ snprintf(rule->class_name, sizeof(rule->class_name), "%s", class_name);
+ snprintf(rule->instance_name, sizeof(rule->instance_name), "%s", instance_name==NULL?MATCH_ANY:instance_name);
+ num--, args++;
+ size_t i = 0;
+ while (num > 0) {
+ if (streq("-o", *args) || streq("--one-shot", *args)) {
+ rule->one_shot = true;
+ } else {
+ for (size_t j = 0; i < sizeof(rule->effect) && j < strlen(*args); i++, j++) {
+ rule->effect[i] = (*args)[j];
+ }
+ if (num > 1 && i < sizeof(rule->effect)) {
+ rule->effect[i++] = ' ';
+ }
+ }
+ num--, args++;
+ }
+ rule->effect[MIN(i, sizeof(rule->effect) - 1)] = '\0';
+ add_rule(rule);
+ } else if (streq("-r", *args) || streq("--remove", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "rule %s: Not enough arguments.\n", *(args - 1));
+ return;
+ }
+ uint16_t idx;
+ while (num > 0) {
+ if (parse_index(*args, &idx)) {
+ remove_rule_by_index(idx - 1);
+ } else if (streq("tail", *args)) {
+ remove_rule(rule_tail);
+ } else if (streq("head", *args)) {
+ remove_rule(rule_head);
+ } else {
+ remove_rule_by_cause(*args);
+ }
+ num--, args++;
+ }
+ } else if (streq("-l", *args) || streq("--list", *args)) {
+ list_rules(rsp);
+ } else {
+ fail(rsp, "rule: Unknown command: '%s'.\n", *args);
+ return;
+ }
+ num--, args++;
+ }
+}
+
+void cmd_wm(char **args, int num, FILE *rsp)
+{
+ if (num < 1) {
+ fail(rsp, "wm: Missing commands.\n");
+ return;
+ }
+
+ while (num > 0) {
+ if (streq("-d", *args) || streq("--dump-state", *args)) {
+ query_tree(rsp);
+ fprintf(rsp, "\n");
+ } else if (streq("-l", *args) || streq("--load-state", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "wm %s: Not enough arguments.\n", *(args - 1));
+ break;
+ }
+ if (!restore_tree(*args)) {
+ fail(rsp, "");
+ break;
+ }
+ } else if (streq("-a", *args) || streq("--add-monitor", *args)) {
+ num--, args++;
+ if (num < 2) {
+ fail(rsp, "wm %s: Not enough arguments.\n", *(args - 1));
+ break;
+ }
+ char *name = *args;
+ num--, args++;
+ xcb_rectangle_t r;
+ if (parse_rectangle(*args, &r)) {
+ monitor_t *m = make_monitor(name, &r, XCB_NONE);
+ add_monitor(m);
+ add_desktop(m, make_desktop(NULL, XCB_NONE));
+ } else {
+ fail(rsp, "wm %s: Invalid argument: '%s'.\n", *(args - 1), *args);
+ break;
+ }
+ } else if (streq("-o", *args) || streq("--adopt-orphans", *args)) {
+ adopt_orphans();
+ } else if (streq("-g", *args) || streq("--get-status", *args)) {
+ print_report(rsp);
+ } else if (streq("-h", *args) || streq("--record-history", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "wm %s: Not enough arguments.\n", *(args - 1));
+ break;
+ }
+ bool b;
+ if (parse_bool(*args, &b)) {
+ record_history = b;
+ } else {
+ fail(rsp, "wm %s: Invalid argument: '%s'.\n", *(args - 1), *args);
+ break;
+ }
+ } else {
+ fail(rsp, "wm: Unkown command: '%s'.\n", *args);
+ break;
+ }
+ num--, args++;
+ }
+}
+
+int cmd_subscribe(char **args, int num, FILE *rsp)
+{
+ int field = 0;
+ if (num < 1) {
+ field = SBSC_MASK_REPORT;
+ } else {
+ subscriber_mask_t mask;
+ while (num > 0) {
+ if (parse_subscriber_mask(*args, &mask)) {
+ field |= mask;
+ } else {
+ fail(rsp, "subscribe: Invalid argument: '%s'.\n", *args);
+ return SUBSCRIBE_FAILURE;
+ }
+ num--, args++;
+ }
+ }
+
+ add_subscriber(rsp, field);
+ return SUBSCRIBE_SUCCESS;
+}
+
+void cmd_quit(char **args, int num, FILE *rsp)
+{
+ if (num > 0 && sscanf(*args, "%i", &exit_status) != 1) {
+ fail(rsp, "%s: Invalid argument: '%s'.\n", *(args - 1), *args);
+ return;
+ }
+ running = false;
+}
+
+void cmd_config(char **args, int num, FILE *rsp)
+{
+ if (num < 1) {
+ fail(rsp, "config: Missing arguments.\n");
+ return;
+ }
+
+ coordinates_t ref = {mon, mon->desk, mon->desk->focus};
+ coordinates_t trg = {NULL, NULL, NULL};
+
+ while (num > 0 && (*args)[0] == OPT_CHR) {
+ if (streq("-m", *args) || streq("--monitor", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "config %s: Not enough arguments.\n", *(args - 1));
+ return;
+ }
+ int ret;
+ if ((ret = monitor_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
+ handle_failure(ret, "config -m", *args, rsp);
+ return;
+ }
+ } else if (streq("-d", *args) || streq("--desktop", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "config %s: Not enough arguments.\n", *(args - 1));
+ return;
+ }
+ int ret;
+ if ((ret = desktop_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
+ handle_failure(ret, "config -d", *args, rsp);
+ return;
+ }
+ } else if (streq("-n", *args) || streq("--node", *args)) {
+ num--, args++;
+ if (num < 1) {
+ fail(rsp, "config %s: Not enough arguments.\n", *(args - 1));
+ return;
+ }
+ int ret;
+ if ((ret = node_from_desc(*args, &ref, &trg)) != SELECTOR_OK) {
+ handle_failure(ret, "config -n", *args, rsp);
+ return;
+ }
+ } else {
+ fail(rsp, "config: Unknown option: '%s'.\n", *args);
+ return;
+ }
+ num--, args++;
+ }
+ if (num == 2) {
+ set_setting(trg, *args, *(args + 1), rsp);
+ } else if (num == 1) {
+ get_setting(trg, *args, rsp);
+ } else {
+ fail(rsp, "config: Was expecting 1 or 2 arguments, received %i.\n", num);
+ }
+}
+
+void set_setting(coordinates_t loc, char *name, char *value, FILE *rsp)
+{
+ bool colors_changed = false;
+#define SET_DEF_DEFMON_DEFDESK_WIN(k, v) \
+ if (loc.node != NULL) { \
+ for (node_t *n = first_extrema(loc.node); n != NULL; n = next_leaf(n, loc.node)) { \
+ if (n->client != NULL) { \
+ n->client->k = v; \
+ } \
+ } \
+ } else if (loc.desktop != NULL) { \
+ loc.desktop->k = v; \
+ for (node_t *n = first_extrema(loc.desktop->root); n != NULL; n = next_leaf(n, loc.desktop->root)) { \
+ if (n->client != NULL) { \
+ n->client->k = v; \
+ } \
+ } \
+ } else if (loc.monitor != NULL) { \
+ loc.monitor->k = v; \
+ for (desktop_t *d = loc.monitor->desk_head; d != NULL; d = d->next) { \
+ d->k = v; \
+ for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) { \
+ if (n->client != NULL) { \
+ n->client->k = v; \
+ } \
+ } \
+ } \
+ } else { \
+ k = v; \
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) { \
+ m->k = v; \
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { \
+ d->k = v; \
+ for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) { \
+ if (n->client != NULL) { \
+ n->client->k = v; \
+ } \
+ } \
+ } \
+ } \
+ }
+ if (streq("border_width", name)) {
+ unsigned int bw;
+ if (sscanf(value, "%u", &bw) != 1) {
+ fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
+ return;
+ }
+ SET_DEF_DEFMON_DEFDESK_WIN(border_width, bw)
+#undef SET_DEF_DEFMON_DEFDESK_WIN
+#define SET_DEF_DEFMON_DESK(k, v) \
+ if (loc.desktop != NULL) { \
+ loc.desktop->k = v; \
+ } else if (loc.monitor != NULL) { \
+ loc.monitor->k = v; \
+ for (desktop_t *d = loc.monitor->desk_head; d != NULL; d = d->next) { \
+ d->k = v; \
+ } \
+ } else { \
+ k = v; \
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) { \
+ m->k = v; \
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next) { \
+ d->k = v; \
+ } \
+ } \
+ }
+ } else if (streq("window_gap", name)) {
+ int wg;
+ if (sscanf(value, "%i", &wg) != 1) {
+ fail(rsp, "");
+ return;
+ }
+ SET_DEF_DEFMON_DESK(window_gap, wg)
+#undef SET_DEF_DEFMON_DESK
+#define SET_DEF_MON_DESK(k, v) \
+ if (loc.desktop != NULL) { \
+ loc.desktop->k = v; \
+ } else if (loc.monitor != NULL) { \
+ loc.monitor->k = v; \
+ } else { \
+ k = v; \
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) { \
+ m->k = v; \
+ } \
+ }
+ } else if (streq("top_padding", name)) {
+ int tp;
+ if (sscanf(value, "%i", &tp) != 1) {
+ fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
+ return;
+ }
+ SET_DEF_MON_DESK(padding.top, tp)
+ } else if (streq("right_padding", name)) {
+ int rp;
+ if (sscanf(value, "%i", &rp) != 1) {
+ fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
+ return;
+ }
+ SET_DEF_MON_DESK(padding.right, rp)
+ } else if (streq("bottom_padding", name)) {
+ int bp;
+ if (sscanf(value, "%i", &bp) != 1) {
+ fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
+ return;
+ }
+ SET_DEF_MON_DESK(padding.bottom, bp)
+ } else if (streq("left_padding", name)) {
+ int lp;
+ if (sscanf(value, "%i", &lp) != 1) {
+ fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
+ return;
+ }
+ SET_DEF_MON_DESK(padding.left, lp)
+#undef SET_DEF_MON_DESK
+#define SET_STR(s) \
+ } else if (streq(#s, name)) { \
+ if (snprintf(s, sizeof(s), "%s", value) < 0) { \
+ fail(rsp, ""); \
+ return; \
+ }
+ SET_STR(external_rules_command)
+ SET_STR(status_prefix)
+#undef SET_STR
+ } else if (streq("split_ratio", name)) {
+ double r;
+ if (sscanf(value, "%lf", &r) == 1 && r > 0 && r < 1) {
+ split_ratio = r;
+ } else {
+ fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
+ return;
+ }
+ return;
+#define SET_COLOR(s) \
+ } else if (streq(#s, name)) { \
+ if (!is_hex_color(value)) { \
+ fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
+ return; \
+ } else { \
+ snprintf(s, sizeof(s), "%s", value); \
+ colors_changed = true; \
+ }
+ SET_COLOR(normal_border_color)
+ SET_COLOR(active_border_color)
+ SET_COLOR(focused_border_color)
+ SET_COLOR(presel_feedback_color)
+#undef SET_COLOR
+ } else if (streq("initial_polarity", name)) {
+ child_polarity_t p;
+ if (parse_child_polarity(value, &p)) {
+ initial_polarity = p;
+ } else {
+ fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
+ return;
+ }
+ } else if (streq("pointer_modifier", name)) {
+ if (parse_modifier_mask(value, &pointer_modifier)) {
+ ungrab_buttons();
+ grab_buttons();
+ } else {
+ fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
+ return;
+ }
+ } else if (streq("pointer_motion_interval", name)) {
+ if (sscanf(value, "%u", &pointer_motion_interval) != 1) {
+ fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
+ return;
+ }
+ } else if (streq("pointer_action1", name) ||
+ streq("pointer_action2", name) ||
+ streq("pointer_action3", name)) {
+ int index = name[14] - '1';
+ if (parse_pointer_action(value, &pointer_actions[index])) {
+ ungrab_buttons();
+ grab_buttons();
+ } else {
+ fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
+ return;
+ }
+ } else if (streq("click_to_focus", name)) {
+ if (parse_bool(value, &click_to_focus)) {
+ ungrab_buttons();
+ grab_buttons();
+ } else {
+ fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
+ return;
+ }
+ } else if (streq("focus_follows_pointer", name)) {
+ bool b;
+ if (parse_bool(value, &b)) {
+ if (b == focus_follows_pointer) {
+ fail(rsp, "");
+ return;
+ }
+ focus_follows_pointer = b;
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ if (b) {
+ window_show(m->root);
+ } else {
+ window_hide(m->root);
+ }
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+ listen_enter_notify(d->root, b);
+ }
+ }
+ return;
+ } else {
+ fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value);
+ return;
+ }
+#define SET_BOOL(s) \
+ } else if (streq(#s, name)) { \
+ if (!parse_bool(value, &s)) { \
+ fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
+ return; \
+ }
+ SET_BOOL(borderless_monocle)
+ SET_BOOL(gapless_monocle)
+ SET_BOOL(paddingless_monocle)
+ SET_BOOL(single_monocle)
+ SET_BOOL(swallow_first_click)
+ SET_BOOL(pointer_follows_focus)
+ SET_BOOL(pointer_follows_monitor)
+ SET_BOOL(ignore_ewmh_focus)
+ SET_BOOL(center_pseudo_tiled)
+ SET_BOOL(honor_size_hints)
+#undef SET_BOOL
+#define SET_MON_BOOL(s) \
+ } else if (streq(#s, name)) { \
+ if (!parse_bool(value, &s)) { \
+ fail(rsp, "config: %s: Invalid value: '%s'.\n", name, value); \
+ return; \
+ } \
+ if (s) { \
+ update_monitors(); \
+ }
+ SET_MON_BOOL(remove_disabled_monitors)
+ SET_MON_BOOL(remove_unplugged_monitors)
+ SET_MON_BOOL(merge_overlapping_monitors)
+#undef SET_MON_BOOL
+ } else {
+ fail(rsp, "config: Unknown setting: '%s'.\n", name);
+ return;
+ }
+
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+ arrange(m, d);
+ if (colors_changed) {
+ update_colors_in(d->root, d, m);
+ }
+ }
+ }
+}
+
+void get_setting(coordinates_t loc, char *name, FILE* rsp)
+{
+ if (streq("split_ratio", name)) {
+ fprintf(rsp, "%lf", split_ratio);
+ } else if (streq("border_width", name)) {
+ if (loc.node != NULL) {
+ for (node_t *n = first_extrema(loc.node); n != NULL; n = next_leaf(n, loc.node)) {
+ if (n->client != NULL) {
+ fprintf(rsp, "%u", n->client->border_width);
+ break;
+ }
+ }
+ } else if (loc.desktop != NULL) {
+ fprintf(rsp, "%u", loc.desktop->border_width);
+ } else if (loc.monitor != NULL) {
+ fprintf(rsp, "%u", loc.monitor->border_width);
+ } else {
+ fprintf(rsp, "%u", border_width);
+ }
+ } else if (streq("window_gap", name)) {
+ if (loc.desktop != NULL) {
+ fprintf(rsp, "%i", loc.desktop->window_gap);
+ } else if (loc.monitor != NULL) {
+ fprintf(rsp, "%i", loc.monitor->window_gap);
+ } else {
+ fprintf(rsp, "%i", window_gap);
+ }
+#define GET_DEF_MON_DESK(k) \
+ if (loc.desktop != NULL) { \
+ fprintf(rsp, "%i", loc.desktop->k); \
+ } else if (loc.monitor != NULL) { \
+ fprintf(rsp, "%i", loc.monitor->k); \
+ } else { \
+ fprintf(rsp, "%i", k); \
+ }
+ } else if (streq("top_padding", name)) {
+ GET_DEF_MON_DESK(padding.top)
+ } else if (streq("right_padding", name)) {
+ GET_DEF_MON_DESK(padding.right)
+ } else if (streq("bottom_padding", name)) {
+ GET_DEF_MON_DESK(padding.bottom)
+ } else if (streq("left_padding", name)) {
+ GET_DEF_MON_DESK(padding.left)
+#undef GET_DEF_MON_DESK
+ } else if (streq("external_rules_command", name)) {
+ fprintf(rsp, "%s", external_rules_command);
+ } else if (streq("status_prefix", name)) {
+ fprintf(rsp, "%s", status_prefix);
+ } else if (streq("initial_polarity", name)) {
+ fprintf(rsp, "%s", CHILD_POL_STR(initial_polarity));
+ } else if (streq("pointer_modifier", name)) {
+ print_modifier_mask(pointer_modifier, rsp);
+ } else if (streq("pointer_motion_interval", name)) {
+ fprintf(rsp, "%u", pointer_motion_interval);
+ } else if (streq("pointer_action1", name) ||
+ streq("pointer_action2", name) ||
+ streq("pointer_action3", name)) {
+ int index = name[14] - '1';
+ print_pointer_action(pointer_actions[index], rsp);
+#define GET_COLOR(s) \
+ } else if (streq(#s, name)) { \
+ fprintf(rsp, "%s", s);
+ GET_COLOR(normal_border_color)
+ GET_COLOR(active_border_color)
+ GET_COLOR(focused_border_color)
+ GET_COLOR(presel_feedback_color)
+#undef GET_COLOR
+#define GET_BOOL(s) \
+ } else if (streq(#s, name)) { \
+ fprintf(rsp, "%s", BOOL_STR(s));
+ GET_BOOL(borderless_monocle)
+ GET_BOOL(gapless_monocle)
+ GET_BOOL(paddingless_monocle)
+ GET_BOOL(single_monocle)
+ GET_BOOL(click_to_focus)
+ GET_BOOL(swallow_first_click)
+ GET_BOOL(focus_follows_pointer)
+ GET_BOOL(pointer_follows_focus)
+ GET_BOOL(pointer_follows_monitor)
+ GET_BOOL(ignore_ewmh_focus)
+ GET_BOOL(center_pseudo_tiled)
+ GET_BOOL(honor_size_hints)
+ GET_BOOL(remove_disabled_monitors)
+ GET_BOOL(remove_unplugged_monitors)
+ GET_BOOL(merge_overlapping_monitors)
+#undef GET_BOOL
+ } else {
+ fail(rsp, "config: Unknown setting: '%s'.\n", name);
+ return;
+ }
+ fprintf(rsp, "\n");
+}
+
+void handle_failure(int code, char *src, char *val, FILE *rsp)
+{
+ switch (code) {
+ case SELECTOR_BAD_DESCRIPTOR:
+ fail(rsp, "%s: Invalid descriptor found in '%s'.\n", src, val);
+ break;
+ case SELECTOR_BAD_MODIFIERS:
+ fail(rsp, "%s: Invalid modifier found in '%s'.\n", src, val);
+ break;
+ case SELECTOR_INVALID:
+ fail(rsp, "");
+ break;
+ }
+}
+
+void fail(FILE *rsp, char *fmt, ...)
+{
+ fprintf(rsp, FAILURE_MESSAGE);
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(rsp, fmt, ap);
+ va_end(ap);
+}
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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.
+ */
+
+#ifndef BSPWM_MESSAGES_H
+#define BSPWM_MESSAGES_H
+
+#include "types.h"
+#include "subscribe.h"
+
+enum {
+ SUBSCRIBE_SUCCESS,
+ SUBSCRIBE_FAILURE
+};
+
+void handle_message(char *msg, int msg_len, FILE *rsp);
+void process_message(char **args, int num, FILE *rsp);
+void cmd_node(char **args, int num, FILE *rsp);
+void cmd_desktop(char **args, int num, FILE *rsp);
+void cmd_monitor(char **args, int num, FILE *rsp);
+void cmd_query(char **args, int num, FILE *rsp);
+void cmd_rule(char **args, int num, FILE *rsp);
+void cmd_wm(char **args, int num, FILE *rsp);
+int cmd_subscribe(char **args, int num, FILE *rsp);
+void cmd_quit(char **args, int num, FILE *rsp);
+void cmd_config(char **args, int num, FILE *rsp);
+void set_setting(coordinates_t loc, char *name, char *value, FILE *rsp);
+void get_setting(coordinates_t loc, char *name, FILE* rsp);
+void handle_failure(int code, char *src, char *val, FILE *rsp);
+void fail(FILE *rsp, char *fmt, ...);
+
+#endif
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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 <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include "bspwm.h"
+#include "desktop.h"
+#include "ewmh.h"
+#include "query.h"
+#include "pointer.h"
+#include "settings.h"
+#include "geometry.h"
+#include "tree.h"
+#include "subscribe.h"
+#include "window.h"
+#include "monitor.h"
+
+monitor_t *make_monitor(const char *name, xcb_rectangle_t *rect, uint32_t id)
+{
+ monitor_t *m = calloc(1, sizeof(monitor_t));
+ if (id == XCB_NONE) {
+ m->id = xcb_generate_id(dpy);
+ }
+ m->randr_id = XCB_NONE;
+ snprintf(m->name, sizeof(m->name), "%s", name == NULL ? DEFAULT_MON_NAME : name);
+ m->padding = padding;
+ m->border_width = border_width;
+ m->window_gap = window_gap;
+ m->root = XCB_NONE;
+ m->prev = m->next = NULL;
+ m->desk = m->desk_head = m->desk_tail = NULL;
+ m->wired = true;
+ m->sticky_count = 0;
+ if (rect != NULL) {
+ update_root(m, rect);
+ } else {
+ m->rectangle = (xcb_rectangle_t) {0, 0, screen_width, screen_height};
+ }
+ return m;
+}
+
+void update_root(monitor_t *m, xcb_rectangle_t *rect)
+{
+ xcb_rectangle_t last_rect = m->rectangle;
+ m->rectangle = *rect;
+ if (m->root == XCB_NONE) {
+ 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, XCB_CW_EVENT_MASK, values);
+ xcb_icccm_set_wm_class(dpy, m->root, sizeof(ROOT_WINDOW_IC), ROOT_WINDOW_IC);
+ xcb_icccm_set_wm_name(dpy, m->root, XCB_ATOM_STRING, 8, strlen(m->name), m->name);
+ window_lower(m->root);
+ if (focus_follows_pointer) {
+ window_show(m->root);
+ }
+ } else {
+ window_move_resize(m->root, rect->x, rect->y, rect->width, rect->height);
+ put_status(SBSC_MASK_MONITOR_GEOMETRY, "monitor_geometry 0x%08X %ux%u+%i+%i\n",
+ m->id, rect->width, rect->height, rect->x, rect->y);
+ }
+ 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)) {
+ if (n->client == NULL) {
+ continue;
+ }
+ adapt_geometry(&last_rect, rect, n);
+ }
+ arrange(m, d);
+ }
+}
+
+void rename_monitor(monitor_t *m, const char *name)
+{
+ put_status(SBSC_MASK_MONITOR_RENAME, "monitor_rename 0x%08X %s %s\n", m->id, m->name, name);
+
+ snprintf(m->name, sizeof(m->name), "%s", name);
+ xcb_icccm_set_wm_name(dpy, m->root, XCB_ATOM_STRING, 8, strlen(m->name), m->name);
+
+ put_status(SBSC_MASK_REPORT);
+}
+
+monitor_t *find_monitor(uint32_t id)
+{
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ if (m->id == id) {
+ return m;
+ }
+ }
+ return NULL;
+}
+
+monitor_t *get_monitor_by_randr_id(xcb_randr_output_t id)
+{
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ if (m->randr_id == id) {
+ return m;
+ }
+ }
+ 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 adapt_geometry(xcb_rectangle_t *rs, xcb_rectangle_t *rd, node_t *n)
+{
+ for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+ if (f->client == NULL) {
+ continue;
+ }
+ 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;
+ }
+}
+
+void focus_monitor(monitor_t *m)
+{
+ if (mon == m) {
+ return;
+ }
+
+ mon = m;
+
+ if (pointer_follows_monitor) {
+ center_pointer(m->rectangle);
+ }
+
+ put_status(SBSC_MASK_MONITOR_FOCUS, "monitor_focus 0x%08X\n", m->id);
+}
+
+void add_monitor(monitor_t *m)
+{
+ xcb_rectangle_t r = m->rectangle;
+
+ if (mon == NULL) {
+ mon = m;
+ mon_head = m;
+ mon_tail = m;
+ } else {
+ monitor_t *a = mon_head;
+ while (a != NULL && rect_cmp(m->rectangle, a->rectangle) > 0) {
+ 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;
+ }
+ }
+
+ 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);
+
+ put_status(SBSC_MASK_REPORT);
+}
+
+void unlink_monitor(monitor_t *m)
+{
+ monitor_t *prev = m->prev;
+ monitor_t *next = m->next;
+
+ if (prev != NULL) {
+ prev->next = next;
+ }
+
+ if (next != NULL) {
+ next->prev = prev;
+ }
+
+ if (mon_head == m) {
+ mon_head = next;
+ }
+
+ if (mon_tail == m) {
+ mon_tail = prev;
+ }
+
+ if (pri_mon == m) {
+ pri_mon = NULL;
+ }
+
+ if (mon == m) {
+ mon = NULL;
+ }
+}
+
+void remove_monitor(monitor_t *m)
+{
+ put_status(SBSC_MASK_MONITOR_REMOVE, "monitor_remove 0x%08X\n", m->id);
+
+ while (m->desk_head != NULL) {
+ remove_desktop(m, m->desk_head);
+ }
+
+ monitor_t *last_mon = mon;
+
+ unlink_monitor(m);
+ xcb_destroy_window(dpy, m->root);
+ free(m);
+
+ if (mon != last_mon) {
+ focus_node(NULL, NULL, NULL);
+ }
+
+ put_status(SBSC_MASK_REPORT);
+}
+
+void merge_monitors(monitor_t *ms, monitor_t *md)
+{
+ if (ms == NULL || md == NULL || ms == md) {
+ return;
+ }
+
+ desktop_t *d = ms->desk_head;
+ while (d != NULL) {
+ desktop_t *next = d->next;
+ transfer_desktop(ms, md, d);
+ d = next;
+ }
+}
+
+bool swap_monitors(monitor_t *m1, monitor_t *m2)
+{
+ if (m1 == NULL || m2 == NULL || m1 == m2) {
+ return false;
+ }
+
+ put_status(SBSC_MASK_MONITOR_SWAP, "monitor_swap 0x%08X 0x%08X\n", m1->id, m2->id);
+
+ if (mon_head == m1) {
+ mon_head = m2;
+ } else if (mon_head == m2) {
+ mon_head = m1;
+ }
+ if (mon_tail == m1) {
+ mon_tail = m2;
+ } else if (mon_tail == m2) {
+ mon_tail = m1;
+ }
+
+ monitor_t *p1 = m1->prev;
+ monitor_t *n1 = m1->next;
+ monitor_t *p2 = m2->prev;
+ monitor_t *n2 = m2->next;
+
+ if (p1 != NULL && p1 != m2) {
+ p1->next = m2;
+ }
+ if (n1 != NULL && n1 != m2) {
+ n1->prev = m2;
+ }
+ if (p2 != NULL && p2 != m1) {
+ p2->next = m1;
+ }
+ if (n2 != NULL && n2 != m1) {
+ n2->prev = m1;
+ }
+
+ 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_desktop_viewport();
+ ewmh_update_current_desktop();
+
+ put_status(SBSC_MASK_REPORT);
+ return true;
+}
+
+monitor_t *closest_monitor(monitor_t *m, cycle_dir_t dir, monitor_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 = {f, NULL, NULL};
+ if (monitor_matches(&loc, &loc, sel)) {
+ return f;
+ }
+ f = (dir == CYCLE_PREV ? f->prev : f->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)
+{
+ return is_inside(pt, m->rectangle);
+}
+
+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)
+{
+ 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};
+ 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;
+ int d = abs((r.x + r.width / 2) - xc) + abs((r.y + r.height / 2) - yc);
+ if (d < dmin) {
+ dmin = d;
+ nearest = m;
+ }
+ }
+ }
+ return nearest;
+}
+
+monitor_t *nearest_monitor(monitor_t *m, direction_t dir, monitor_select_t sel)
+{
+ double dmin = DBL_MAX;
+ monitor_t *nearest = NULL;
+ xcb_rectangle_t rect = m->rectangle;
+ for (monitor_t *f = mon_head; f != NULL; f = f->next) {
+ coordinates_t loc = {f, NULL, NULL};
+ xcb_rectangle_t r = f->rectangle;
+ if (f == m ||
+ !monitor_matches(&loc, &loc, sel) ||
+ !on_dir_side(rect, r, dir)) {
+ continue;
+ }
+ double d = distance_center(rect, r);
+ if (d < dmin) {
+ dmin = d;
+ nearest = f;
+ }
+ }
+ return nearest;
+}
+
+bool update_monitors(void)
+{
+ xcb_randr_get_screen_resources_reply_t *sres = xcb_randr_get_screen_resources_reply(dpy, xcb_randr_get_screen_resources(dpy, root), NULL);
+ if (sres == NULL) {
+ return false;
+ }
+
+ monitor_t *m, *mm = NULL;
+
+ int len = xcb_randr_get_screen_resources_outputs_length(sres);
+ xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_outputs(sres);
+
+ xcb_randr_get_output_info_cookie_t cookies[len];
+ for (int i = 0; i < len; i++) {
+ cookies[i] = xcb_randr_get_output_info(dpy, outputs[i], XCB_CURRENT_TIME);
+ }
+
+ for (m = mon_head; m != NULL; m = m->next) {
+ m->wired = false;
+ }
+
+ 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};
+ mm = get_monitor_by_randr_id(outputs[i]);
+ if (mm != NULL) {
+ update_root(mm, &rect);
+ mm->wired = true;
+ } else {
+ char *name = (char *) xcb_randr_get_output_info_name(info);
+ size_t len = (size_t) xcb_randr_get_output_info_name_length(info);
+ char *name_copy = copy_string(name, len);
+ mm = make_monitor(name_copy, &rect, XCB_NONE);
+ free(name_copy);
+ mm->randr_id = outputs[i];
+ add_monitor(mm);
+ }
+ }
+ free(cir);
+ } else if (!remove_disabled_monitors && info->connection != XCB_RANDR_CONNECTION_DISCONNECTED) {
+ m = get_monitor_by_randr_id(outputs[i]);
+ if (m != NULL) {
+ m->wired = true;
+ }
+ }
+ }
+ 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) {
+ pri_mon = get_monitor_by_randr_id(gpo->output);
+ }
+ free(gpo);
+
+ /* handle overlapping monitors */
+ if (merge_overlapping_monitors) {
+ m = mon_head;
+ while (m != NULL) {
+ monitor_t *next = m->next;
+ if (m->wired) {
+ 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) {
+ mm = m;
+ }
+ if (next == mb) {
+ next = mb_next;
+ }
+ merge_monitors(mb, m);
+ remove_monitor(mb);
+ }
+ mb = mb_next;
+ }
+ }
+ m = next;
+ }
+ }
+
+ /* merge and remove disconnected monitors */
+ if (remove_unplugged_monitors) {
+ m = mon_head;
+ while (m != NULL) {
+ monitor_t *next = m->next;
+ if (!m->wired) {
+ 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) {
+ add_desktop(m, make_desktop(NULL, XCB_NONE));
+ }
+ }
+
+ if (!running && mon != NULL) {
+ if (pri_mon != NULL) {
+ mon = pri_mon;
+ }
+ center_pointer(mon->rectangle);
+ ewmh_update_current_desktop();
+ }
+
+ free(sres);
+
+ return (mon != NULL);
+}
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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.
+ */
+
+#ifndef BSPWM_MONITOR_H
+#define BSPWM_MONITOR_H
+
+#define DEFAULT_MON_NAME "MONITOR"
+
+monitor_t *make_monitor(const char *name, xcb_rectangle_t *rect, uint32_t id);
+void update_root(monitor_t *m, xcb_rectangle_t *rect);
+void rename_monitor(monitor_t *m, const char *name);
+monitor_t *find_monitor(uint32_t id);
+monitor_t *get_monitor_by_randr_id(xcb_randr_output_t id);
+void embrace_client(monitor_t *m, client_t *c);
+void adapt_geometry(xcb_rectangle_t *rs, xcb_rectangle_t *rd, node_t *n);
+void focus_monitor(monitor_t *m);
+void add_monitor(monitor_t *m);
+void unlink_monitor(monitor_t *m);
+void remove_monitor(monitor_t *m);
+void merge_monitors(monitor_t *ms, monitor_t *md);
+bool swap_monitors(monitor_t *m1, monitor_t *m2);
+monitor_t *closest_monitor(monitor_t *m, cycle_dir_t dir, monitor_select_t sel);
+bool is_inside_monitor(monitor_t *m, xcb_point_t pt);
+monitor_t *monitor_from_point(xcb_point_t pt);
+monitor_t *monitor_from_client(client_t *c);
+monitor_t *nearest_monitor(monitor_t *m, direction_t dir, monitor_select_t sel);
+bool update_monitors(void);
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <errno.h>
+#include "parse.h"
+
+bool parse_bool(char *value, bool *b)
+{
+ if (streq("true", value) || streq("on", value)) {
+ *b = true;
+ return true;
+ } else if (streq("false", value) || streq("off", value)) {
+ *b = false;
+ return true;
+ }
+ return false;
+}
+
+bool parse_split_type(char *s, split_type_t *t)
+{
+ if (streq("horizontal", s)) {
+ *t = TYPE_HORIZONTAL;
+ return true;
+ } else if (streq("vertical", s)) {
+ *t = TYPE_VERTICAL;
+ return true;
+ }
+ return false;
+}
+
+bool parse_split_mode(char *s, split_mode_t *m)
+{
+ if (streq("automatic", s)) {
+ *m = MODE_AUTOMATIC;
+ return true;
+ } else if (streq("vertical", s)) {
+ *m = MODE_MANUAL;
+ return true;
+ }
+ return false;
+}
+
+bool parse_layout(char *s, layout_t *l)
+{
+ if (streq("monocle", s)) {
+ *l = LAYOUT_MONOCLE;
+ return true;
+ } else if (streq("tiled", s)) {
+ *l = LAYOUT_TILED;
+ return true;
+ }
+ return false;
+}
+
+bool parse_client_state(char *s, client_state_t *t)
+{
+ if (streq("tiled", s)) {
+ *t = STATE_TILED;
+ return true;
+ } else if (streq("pseudo_tiled", s)) {
+ *t = STATE_PSEUDO_TILED;
+ return true;
+ } else if (streq("floating", s)) {
+ *t = STATE_FLOATING;
+ return true;
+ } else if (streq("fullscreen", s)) {
+ *t = STATE_FULLSCREEN;
+ return true;
+ }
+ return false;
+}
+
+bool parse_stack_layer(char *s, stack_layer_t *l)
+{
+ if (streq("below", s)) {
+ *l = LAYER_BELOW;
+ return true;
+ } else if (streq("normal", s)) {
+ *l = LAYER_NORMAL;
+ return true;
+ } else if (streq("above", s)) {
+ *l = LAYER_ABOVE;
+ return true;
+ }
+ return false;
+}
+
+bool parse_direction(char *s, direction_t *d)
+{
+ if (streq("north", s)) {
+ *d = DIR_NORTH;
+ return true;
+ } else if (streq("west", s)) {
+ *d = DIR_WEST;
+ return true;
+ } else if (streq("south", s)) {
+ *d = DIR_SOUTH;
+ return true;
+ } else if (streq("east", s)) {
+ *d = DIR_EAST;
+ return true;
+ }
+ return false;
+}
+
+bool parse_cycle_direction(char *s, cycle_dir_t *d)
+{
+ if (streq("next", s)) {
+ *d = CYCLE_NEXT;
+ return true;
+ } else if (streq("prev", s)) {
+ *d = CYCLE_PREV;
+ return true;
+ }
+ return false;
+}
+
+bool parse_circulate_direction(char *s, circulate_dir_t *d)
+{
+ if (streq("forward", s)) {
+ *d = CIRCULATE_FORWARD;
+ return true;
+ } else if (streq("backward", s)) {
+ *d = CIRCULATE_BACKWARD;
+ return true;
+ }
+ return false;
+}
+
+bool parse_history_direction(char *s, history_dir_t *d)
+{
+ if (streq("older", s)) {
+ *d = HISTORY_OLDER;
+ return true;
+ } else if (streq("newer", s)) {
+ *d = HISTORY_NEWER;
+ return true;
+ }
+ return false;
+}
+
+
+bool parse_flip(char *s, flip_t *f)
+{
+ if (streq("horizontal", s)) {
+ *f = FLIP_HORIZONTAL;
+ return true;
+ } else if (streq("vertical", s)) {
+ *f = FLIP_VERTICAL;
+ return true;
+ }
+ return false;
+}
+
+bool parse_resize_handle(char *s, resize_handle_t *h)
+{
+ if (streq("left", s)) {
+ *h = HANDLE_LEFT;
+ return true;
+ } else if (streq("top", s)) {
+ *h = HANDLE_TOP;
+ return true;
+ } else if (streq("right", s)) {
+ *h = HANDLE_RIGHT;
+ return true;
+ } else if (streq("bottom", s)) {
+ *h = HANDLE_BOTTOM;
+ return true;
+ } else if (streq("top_left", s)) {
+ *h = HANDLE_TOP_LEFT;
+ return true;
+ } else if (streq("top_right", s)) {
+ *h = HANDLE_TOP_RIGHT;
+ return true;
+ } else if (streq("bottom_right", s)) {
+ *h = HANDLE_BOTTOM_RIGHT;
+ return true;
+ } else if (streq("bottom_left", s)) {
+ *h = HANDLE_BOTTOM_LEFT;
+ return true;
+ }
+ return false;
+}
+
+bool parse_modifier_mask(char *s, uint16_t *m)
+{
+ if (strcmp(s, "shift") == 0) {
+ *m = XCB_MOD_MASK_SHIFT;
+ return true;
+ } else if (strcmp(s, "control") == 0) {
+ *m = XCB_MOD_MASK_CONTROL;
+ return true;
+ } else if (strcmp(s, "lock") == 0) {
+ *m = XCB_MOD_MASK_LOCK;
+ return true;
+ } else if (strcmp(s, "mod1") == 0) {
+ *m = XCB_MOD_MASK_1;
+ return true;
+ } else if (strcmp(s, "mod2") == 0) {
+ *m = XCB_MOD_MASK_2;
+ return true;
+ } else if (strcmp(s, "mod3") == 0) {
+ *m = XCB_MOD_MASK_3;
+ return true;
+ } else if (strcmp(s, "mod4") == 0) {
+ *m = XCB_MOD_MASK_4;
+ return true;
+ } else if (strcmp(s, "mod5") == 0) {
+ *m = XCB_MOD_MASK_5;
+ return true;
+ }
+ return false;
+}
+
+bool parse_pointer_action(char *s, pointer_action_t *a)
+{
+ if (streq("move", s)) {
+ *a = ACTION_MOVE;
+ return true;
+ } else if (streq("resize_corner", s)) {
+ *a = ACTION_RESIZE_CORNER;
+ return true;
+ } else if (streq("resize_side", s)) {
+ *a = ACTION_RESIZE_SIDE;
+ return true;
+ } else if (streq("focus", s)) {
+ *a = ACTION_FOCUS;
+ return true;
+ } else if (streq("none", s)) {
+ *a = ACTION_NONE;
+ return true;
+ }
+ return false;
+}
+
+bool parse_child_polarity(char *s, child_polarity_t *p)
+{
+ if (streq("first_child", s)) {
+ *p = FIRST_CHILD;
+ return true;
+ } else if (streq("second_child", s)) {
+ *p = SECOND_CHILD;
+ return true;
+ }
+ return false;
+}
+
+bool parse_degree(char *s, int *d)
+{
+ int i = atoi(s);
+ while (i < 0)
+ i += 360;
+ while (i > 359)
+ i -= 360;
+ if ((i % 90) != 0) {
+ return false;
+ } else {
+ *d = i;
+ return true;
+ }
+}
+
+bool parse_id(char *s, uint32_t *id)
+{
+ char *end;
+ errno = 0;
+ uint32_t v = strtol(s, &end, 0);
+ if (errno != 0 || *end != '\0') {
+ return false;
+ }
+ *id = v;
+ return true;
+}
+
+bool parse_bool_declaration(char *s, char **key, bool *value, alter_state_t *state)
+{
+ *key = strtok(s, EQL_TOK);
+ char *v = strtok(NULL, EQL_TOK);
+ if (v == NULL) {
+ *state = ALTER_TOGGLE;
+ return true;
+ } else {
+ if (parse_bool(v, value)) {
+ *state = ALTER_SET;
+ return true;
+ } else {
+ return false;
+ }
+ }
+ return false;
+}
+
+bool parse_index(char *s, uint16_t *idx)
+{
+ return (sscanf(s, "^%hu", idx) == 1);
+}
+
+bool parse_rectangle(char *s, xcb_rectangle_t *r)
+{
+ uint16_t w, h;
+ int16_t x, y;
+ if (sscanf(s, "%hux%hu+%hi+%hi", &w, &h, &x, &y) != 4) {
+ return false;
+ }
+ r->width = w;
+ r->height = h;
+ r->x = x;
+ r->y = y;
+ return true;
+}
+
+bool parse_subscriber_mask(char *s, subscriber_mask_t *mask)
+{
+ if (streq("all", s)) {
+ *mask = SBSC_MASK_ALL;
+ } else if (streq("node", s)) {
+ *mask = SBSC_MASK_NODE;
+ } else if (streq("desktop", s)) {
+ *mask = SBSC_MASK_DESKTOP;
+ } else if (streq("monitor", s)) {
+ *mask = SBSC_MASK_MONITOR;
+ } else if (streq("pointer_action", s)) {
+ *mask = SBSC_MASK_POINTER_ACTION;
+ } else if (streq("node_manage", s)) {
+ *mask = SBSC_MASK_NODE_MANAGE;
+ } else if (streq("node_unmanage", s)) {
+ *mask = SBSC_MASK_NODE_UNMANAGE;
+ } else if (streq("node_swap", s)) {
+ *mask = SBSC_MASK_NODE_SWAP;
+ } else if (streq("node_transfer", s)) {
+ *mask = SBSC_MASK_NODE_TRANSFER;
+ } else if (streq("node_focus", s)) {
+ *mask = SBSC_MASK_NODE_FOCUS;
+ } else if (streq("node_presel", s)) {
+ *mask = SBSC_MASK_NODE_PRESEL;
+ } else if (streq("node_stack", s)) {
+ *mask = SBSC_MASK_NODE_STACK;
+ } else if (streq("node_activate", s)) {
+ *mask = SBSC_MASK_NODE_ACTIVATE;
+ } else if (streq("node_geometry", s)) {
+ *mask = SBSC_MASK_NODE_GEOMETRY;
+ } else if (streq("node_state", s)) {
+ *mask = SBSC_MASK_NODE_STATE;
+ } else if (streq("node_flag", s)) {
+ *mask = SBSC_MASK_NODE_FLAG;
+ } else if (streq("node_layer", s)) {
+ *mask = SBSC_MASK_NODE_LAYER;
+ } else if (streq("desktop_add", s)) {
+ *mask = SBSC_MASK_DESKTOP_ADD;
+ } else if (streq("desktop_rename", s)) {
+ *mask = SBSC_MASK_DESKTOP_RENAME;
+ } else if (streq("desktop_remove", s)) {
+ *mask = SBSC_MASK_DESKTOP_REMOVE;
+ } else if (streq("desktop_swap", s)) {
+ *mask = SBSC_MASK_DESKTOP_SWAP;
+ } else if (streq("desktop_transfer", s)) {
+ *mask = SBSC_MASK_DESKTOP_TRANSFER;
+ } else if (streq("desktop_focus", s)) {
+ *mask = SBSC_MASK_DESKTOP_FOCUS;
+ } else if (streq("desktop_activate", s)) {
+ *mask = SBSC_MASK_DESKTOP_ACTIVATE;
+ } else if (streq("desktop_layout", s)) {
+ *mask = SBSC_MASK_DESKTOP_LAYOUT;
+ } else if (streq("monitor_add", s)) {
+ *mask = SBSC_MASK_MONITOR_ADD;
+ } else if (streq("monitor_rename", s)) {
+ *mask = SBSC_MASK_MONITOR_RENAME;
+ } else if (streq("monitor_remove", s)) {
+ *mask = SBSC_MASK_MONITOR_REMOVE;
+ } else if (streq("monitor_swap", s)) {
+ *mask = SBSC_MASK_MONITOR_SWAP;
+ } else if (streq("monitor_focus", s)) {
+ *mask = SBSC_MASK_MONITOR_FOCUS;
+ } else if (streq("monitor_geometry", s)) {
+ *mask = SBSC_MASK_MONITOR_GEOMETRY;
+ } else if (streq("report", s)) {
+ *mask = SBSC_MASK_REPORT;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+
+#define GET_MOD(k) \
+ } else if (streq(#k, tok)) { \
+ sel->k = OPTION_TRUE; \
+ } else if (streq("!" #k, tok)) { \
+ sel->k = OPTION_FALSE;
+
+bool parse_monitor_modifiers(char *desc, monitor_select_t *sel)
+{
+ char *tok;
+ while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
+ tok[0] = '\0';
+ tok++;
+ if (streq("occupied", tok)) {
+ sel->occupied = OPTION_TRUE;
+ } else if (streq("!occupied", tok)) {
+ sel->occupied = OPTION_FALSE;
+ GET_MOD(focused)
+ } else {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool parse_desktop_modifiers(char *desc, desktop_select_t *sel)
+{
+ char *tok;
+ while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
+ tok[0] = '\0';
+ tok++;
+ if (streq("occupied", tok)) {
+ sel->occupied = OPTION_TRUE;
+ } else if (streq("!occupied", tok)) {
+ sel->occupied = OPTION_FALSE;
+ GET_MOD(focused)
+ GET_MOD(urgent)
+ GET_MOD(local)
+ } else {
+ return false;
+ }
+ }
+ return true;
+
+}
+
+bool parse_node_modifiers(char *desc, node_select_t *sel)
+{
+ char *tok;
+ while ((tok = strrchr(desc, CAT_CHR)) != NULL) {
+ tok[0] = '\0';
+ tok++;
+ if (streq("tiled", tok)) {
+ sel->tiled = OPTION_TRUE;
+ } else if (streq("!tiled", tok)) {
+ sel->tiled = OPTION_FALSE;
+ GET_MOD(automatic)
+ GET_MOD(focused)
+ GET_MOD(local)
+ GET_MOD(active)
+ GET_MOD(leaf)
+ GET_MOD(window)
+ GET_MOD(pseudo_tiled)
+ GET_MOD(floating)
+ GET_MOD(fullscreen)
+ GET_MOD(hidden)
+ GET_MOD(sticky)
+ GET_MOD(private)
+ GET_MOD(locked)
+ GET_MOD(urgent)
+ GET_MOD(same_class)
+ GET_MOD(descendant_of)
+ GET_MOD(ancestor_of)
+ GET_MOD(below)
+ GET_MOD(normal)
+ GET_MOD(above)
+ } else {
+ return false;
+ }
+ }
+ return true;
+}
+
+#undef GET_MOD
--- /dev/null
+#ifndef BSPWM_PARSE_H
+#define BSPWM_PARSE_H
+
+#include "types.h"
+#include "subscribe.h"
+
+#define OPT_CHR '-'
+#define CAT_CHR '.'
+#define EQL_TOK "="
+#define COL_TOK ":"
+
+bool parse_bool(char *value, bool *b);
+bool parse_split_type(char *s, split_type_t *t);
+bool parse_split_mode(char *s, split_mode_t *m);
+bool parse_layout(char *s, layout_t *l);
+bool parse_client_state(char *s, client_state_t *t);
+bool parse_stack_layer(char *s, stack_layer_t *l);
+bool parse_direction(char *s, direction_t *d);
+bool parse_cycle_direction(char *s, cycle_dir_t *d);
+bool parse_circulate_direction(char *s, circulate_dir_t *d);
+bool parse_history_direction(char *s, history_dir_t *d);
+bool parse_flip(char *s, flip_t *f);
+bool parse_resize_handle(char *s, resize_handle_t *h);
+bool parse_modifier_mask(char *s, uint16_t *m);
+bool parse_pointer_action(char *s, pointer_action_t *a);
+bool parse_child_polarity(char *s, child_polarity_t *p);
+bool parse_degree(char *s, int *d);
+bool parse_id(char *s, uint32_t *id);
+bool parse_bool_declaration(char *s, char **key, bool *value, alter_state_t *state);
+bool parse_index(char *s, uint16_t *idx);
+bool parse_rectangle(char *s, xcb_rectangle_t *r);
+bool parse_subscriber_mask(char *s, subscriber_mask_t *mask);
+bool parse_monitor_modifiers(char *desc, monitor_select_t *sel);
+bool parse_desktop_modifiers(char *desc, desktop_select_t *sel);
+bool parse_node_modifiers(char *desc, node_select_t *sel);
+
+#endif
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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 <xcb/xcb_keysyms.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include "bspwm.h"
+#include "query.h"
+#include "settings.h"
+#include "stack.h"
+#include "tree.h"
+#include "monitor.h"
+#include "subscribe.h"
+#include "events.h"
+#include "window.h"
+#include "pointer.h"
+
+void pointer_init(void)
+{
+ num_lock = modfield_from_keysym(XK_Num_Lock);
+ caps_lock = modfield_from_keysym(XK_Caps_Lock);
+ scroll_lock = modfield_from_keysym(XK_Scroll_Lock);
+ if (caps_lock == XCB_NO_SYMBOL) {
+ caps_lock = XCB_MOD_MASK_LOCK;
+ }
+ grabbing = false;
+ grabbed_node = NULL;
+}
+
+void window_grab_buttons(xcb_window_t win)
+{
+ if (click_to_focus) {
+ window_grab_button(win, XCB_BUTTON_INDEX_1, XCB_NONE);
+ }
+ uint8_t buttons[] = {XCB_BUTTON_INDEX_1, XCB_BUTTON_INDEX_2, XCB_BUTTON_INDEX_3};
+ for (unsigned int i = 0; i < LENGTH(buttons); i++) {
+ if (pointer_actions[i] != ACTION_NONE) {
+ window_grab_button(win, buttons[i], pointer_modifier);
+ }
+ }
+}
+
+void window_grab_button(xcb_window_t win, uint8_t button, uint16_t modifier)
+{
+#define GRAB(b, m) \
+ xcb_grab_button(dpy, false, win, XCB_EVENT_MASK_BUTTON_PRESS, \
+ XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, b, m)
+ GRAB(button, modifier);
+ if (num_lock != XCB_NO_SYMBOL && caps_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
+ GRAB(button, modifier | num_lock | caps_lock | scroll_lock);
+ }
+ if (num_lock != XCB_NO_SYMBOL && caps_lock != XCB_NO_SYMBOL) {
+ GRAB(button, modifier | num_lock | caps_lock);
+ }
+ if (caps_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
+ GRAB(button, modifier | caps_lock | scroll_lock);
+ }
+ if (num_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
+ GRAB(button, modifier | num_lock | scroll_lock);
+ }
+ if (num_lock != XCB_NO_SYMBOL) {
+ GRAB(button, modifier | num_lock);
+ }
+ if (caps_lock != XCB_NO_SYMBOL) {
+ GRAB(button, modifier | caps_lock);
+ }
+ if (scroll_lock != XCB_NO_SYMBOL) {
+ GRAB(button, modifier | scroll_lock);
+ }
+#undef GRAB
+}
+
+void grab_buttons(void)
+{
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ 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)) {
+ window_grab_buttons(n->id);
+ if (n->presel != NULL) {
+ window_grab_buttons(n->presel->feedback);
+ }
+ }
+ }
+ }
+}
+
+void ungrab_buttons(void)
+{
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ 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)) {
+ xcb_ungrab_button(dpy, XCB_BUTTON_INDEX_ANY, n->id, XCB_MOD_MASK_ANY);
+ }
+ }
+ }
+}
+
+int16_t modfield_from_keysym(xcb_keysym_t keysym)
+{
+ uint16_t modfield = 0;
+ xcb_keycode_t *keycodes = NULL, *mod_keycodes = NULL;
+ xcb_get_modifier_mapping_reply_t *reply = NULL;
+ xcb_key_symbols_t *symbols = xcb_key_symbols_alloc(dpy);
+
+ if ((keycodes = xcb_key_symbols_get_keycode(symbols, keysym)) == NULL ||
+ (reply = xcb_get_modifier_mapping_reply(dpy, xcb_get_modifier_mapping(dpy), NULL)) == NULL ||
+ reply->keycodes_per_modifier < 1 ||
+ (mod_keycodes = xcb_get_modifier_mapping_keycodes(reply)) == NULL) {
+ goto end;
+ }
+
+ unsigned int num_mod = xcb_get_modifier_mapping_keycodes_length(reply) / reply->keycodes_per_modifier;
+ for (unsigned int i = 0; i < num_mod; i++) {
+ for (unsigned int j = 0; j < reply->keycodes_per_modifier; j++) {
+ xcb_keycode_t mk = mod_keycodes[i * reply->keycodes_per_modifier + j];
+ if (mk == XCB_NO_SYMBOL) {
+ continue;
+ }
+ for (xcb_keycode_t *k = keycodes; *k != XCB_NO_SYMBOL; k++) {
+ if (*k == mk) {
+ modfield |= (1 << i);
+ }
+ }
+ }
+ }
+
+end:
+ xcb_key_symbols_free(symbols);
+ free(keycodes);
+ free(reply);
+ return modfield;
+}
+
+resize_handle_t get_handle(node_t *n, xcb_point_t pos, pointer_action_t pac)
+{
+ resize_handle_t rh = HANDLE_BOTTOM_RIGHT;
+ xcb_rectangle_t rect = get_rectangle(NULL, n);
+ if (pac == ACTION_RESIZE_SIDE) {
+ float W = rect.width;
+ float H = rect.height;
+ float ratio = W / H;
+ float x = pos.x - rect.x;
+ float y = pos.y - rect.y;
+ float diag_a = ratio * y;
+ float diag_b = W - diag_a;
+ if (x < diag_a) {
+ if (x < diag_b) {
+ rh = HANDLE_LEFT;
+ } else {
+ rh = HANDLE_BOTTOM;
+ }
+ } else {
+ if (x < diag_b) {
+ rh = HANDLE_TOP;
+ } else {
+ rh = HANDLE_RIGHT;
+ }
+ }
+ } else if (pac == ACTION_RESIZE_CORNER) {
+ int16_t mid_x = rect.x + (rect.width / 2);
+ int16_t mid_y = rect.y + (rect.height / 2);
+ if (pos.x > mid_x) {
+ if (pos.y > mid_y) {
+ rh = HANDLE_BOTTOM_RIGHT;
+ } else {
+ rh = HANDLE_TOP_RIGHT;
+ }
+ } else {
+ if (pos.y > mid_y) {
+ rh = HANDLE_BOTTOM_LEFT;
+ } else {
+ rh = HANDLE_TOP_LEFT;
+ }
+ }
+ }
+ return rh;
+}
+
+bool grab_pointer(pointer_action_t pac)
+{
+ xcb_window_t win = XCB_NONE;
+ xcb_point_t pos;
+
+ query_pointer(&win, &pos);
+
+ coordinates_t loc;
+
+ if (!locate_window(win, &loc)) {
+ if (pac == ACTION_FOCUS) {
+ monitor_t *m = monitor_from_point(pos);
+ if (m != NULL && m != mon && (win == XCB_NONE || win == m->root)) {
+ focus_node(m, m->desk, m->desk->focus);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ if (pac == ACTION_FOCUS) {
+ if (loc.node != mon->desk->focus) {
+ focus_node(loc.monitor, loc.desktop, loc.node);
+ return true;
+ } else if (focus_follows_pointer) {
+ stack(loc.desktop, loc.node, true);
+ }
+ return false;
+ }
+
+ if (loc.node->client->state == STATE_FULLSCREEN) {
+ return true;
+ }
+
+ xcb_grab_pointer_reply_t *reply = xcb_grab_pointer_reply(dpy, xcb_grab_pointer(dpy, 0, root, XCB_EVENT_MASK_BUTTON_RELEASE|XCB_EVENT_MASK_BUTTON_MOTION, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, XCB_CURRENT_TIME), NULL);
+
+ if (reply == NULL || reply->status != XCB_GRAB_STATUS_SUCCESS) {
+ free(reply);
+ return true;
+ }
+ free(reply);
+
+ if (pac == ACTION_MOVE) {
+ put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X move begin\n", loc.monitor->id, loc.desktop->id, loc.node->id);
+ } else if (pac == ACTION_RESIZE_CORNER) {
+ put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X resize_corner begin\n", loc.monitor->id, loc.desktop->id, loc.node->id);
+ } else if (pac == ACTION_RESIZE_SIDE) {
+ put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X resize_side begin\n", loc.monitor->id, loc.desktop->id, loc.node->id);
+ }
+
+ track_pointer(loc, pac, pos);
+
+ return true;
+}
+
+void track_pointer(coordinates_t loc, pointer_action_t pac, xcb_point_t pos)
+{
+ node_t *n = loc.node;
+ resize_handle_t rh = get_handle(loc.node, pos, pac);
+
+ uint16_t last_motion_x = pos.x, last_motion_y = pos.y;
+ xcb_timestamp_t last_motion_time = 0;
+
+ xcb_generic_event_t *evt = NULL;
+
+ grabbing = true;
+ grabbed_node = n;
+
+ do {
+ free(evt);
+ while ((evt = xcb_wait_for_event(dpy)) == NULL) {
+ xcb_flush(dpy);
+ }
+ uint8_t resp_type = XCB_EVENT_RESPONSE_TYPE(evt);
+ if (resp_type == XCB_MOTION_NOTIFY) {
+ xcb_motion_notify_event_t *e = (xcb_motion_notify_event_t*) evt;
+ uint32_t dtime = e->time - last_motion_time;
+ if (dtime < pointer_motion_interval) {
+ continue;
+ }
+ last_motion_time = e->time;
+ int16_t dx = e->root_x - last_motion_x;
+ int16_t dy = e->root_y - last_motion_y;
+ if (pac == ACTION_MOVE) {
+ move_client(&loc, dx, dy);
+ } else {
+ resize_client(&loc, rh, dx, dy);
+ }
+ last_motion_x = e->root_x;
+ last_motion_y = e->root_y;
+ xcb_flush(dpy);
+ } else if (resp_type == XCB_BUTTON_RELEASE) {
+ grabbing = false;
+ } else {
+ handle_event(evt);
+ }
+ } while (grabbing && grabbed_node != NULL);
+ free(evt);
+
+ xcb_ungrab_pointer(dpy, XCB_CURRENT_TIME);
+
+ if (grabbed_node == NULL) {
+ grabbing = false;
+ return;
+ }
+
+ if (pac == ACTION_MOVE) {
+ put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X move end\n", loc.monitor->id, loc.desktop->id, n->id);
+ } else if (pac == ACTION_RESIZE_CORNER) {
+ put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X resize_corner end\n", loc.monitor->id, loc.desktop->id, n->id);
+ } else if (pac == ACTION_RESIZE_SIDE) {
+ put_status(SBSC_MASK_POINTER_ACTION, "pointer_action 0x%08X 0x%08X 0x%08X resize_side end\n", loc.monitor->id, loc.desktop->id, n->id);
+ }
+
+ xcb_rectangle_t r = get_rectangle(NULL, n);
+
+ put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", loc.monitor->id, loc.desktop->id, loc.node->id, r.width, r.height, r.x, r.y);
+
+ if ((pac == ACTION_MOVE && IS_TILED(n->client)) ||
+ ((pac == ACTION_RESIZE_CORNER || pac == ACTION_RESIZE_SIDE) &&
+ n->client->state == STATE_TILED)) {
+ for (node_t *f = first_extrema(loc.desktop->root); f != NULL; f = next_leaf(f, loc.desktop->root)) {
+ if (f == n || f->client == NULL || !IS_TILED(f->client)) {
+ continue;
+ }
+ xcb_rectangle_t r = f->client->tiled_rectangle;
+ put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", loc.monitor->id, loc.desktop->id, f->id, r.width, r.height, r.x, r.y);
+ }
+ }
+}
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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.
+ */
+
+#ifndef BSPWM_POINTER_H
+#define BSPWM_POINTER_H
+
+#define XK_Num_Lock 0xff7f
+#define XK_Caps_Lock 0xffe5
+#define XK_Scroll_Lock 0xff14
+
+uint16_t num_lock;
+uint16_t caps_lock;
+uint16_t scroll_lock;
+
+bool grabbing;
+node_t *grabbed_node;
+
+void pointer_init(void);
+void window_grab_buttons(xcb_window_t win);
+void window_grab_button(xcb_window_t win, uint8_t button, uint16_t modifier);
+void grab_buttons(void);
+void ungrab_buttons(void);
+int16_t modfield_from_keysym(xcb_keysym_t keysym);
+resize_handle_t get_handle(node_t *n, xcb_point_t pos, pointer_action_t pac);
+bool grab_pointer(pointer_action_t pac);
+void track_pointer(coordinates_t loc, pointer_action_t pac, xcb_point_t pos);
+
+#endif
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "bspwm.h"
+#include "desktop.h"
+#include "history.h"
+#include "parse.h"
+#include "monitor.h"
+#include "window.h"
+#include "tree.h"
+#include "query.h"
+
+void query_tree(FILE *rsp)
+{
+ fprintf(rsp, "{");
+ fprintf(rsp, "\"focusedMonitorId\":%u,", mon->id);
+ if (pri_mon != NULL) {
+ fprintf(rsp, "\"primaryMonitorId\":%u,", pri_mon->id);
+ }
+ fprintf(rsp, "\"clientsCount\":%i,", clients_count);
+ fprintf(rsp, "\"monitors\":");
+ fprintf(rsp, "[");
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ query_monitor(m, rsp);
+ if (m->next != NULL) {
+ fprintf(rsp, ",");
+ }
+ }
+ fprintf(rsp, "]");
+ fprintf(rsp,",");
+ fprintf(rsp, "\"focusHistory\":");
+ query_history(rsp);
+ fprintf(rsp,",");
+ fprintf(rsp, "\"stackingList\":");
+ query_stack(rsp);
+ fprintf(rsp, "}");
+
+}
+
+void query_monitor(monitor_t *m, FILE *rsp)
+{
+ fprintf(rsp, "{");
+ fprintf(rsp, "\"name\":\"%s\",", m->name);
+ fprintf(rsp, "\"id\":%u,", m->id);
+ fprintf(rsp, "\"randrId\":%u,", m->randr_id);
+ fprintf(rsp, "\"wired\":%s,", BOOL_STR(m->wired));
+ fprintf(rsp, "\"stickyCount\":%i,", m->sticky_count);
+ fprintf(rsp, "\"windowGap\":%i,", m->window_gap);
+ fprintf(rsp, "\"borderWidth\":%u,", m->border_width);
+ fprintf(rsp, "\"focusedDesktopId\":%u,", m->desk->id);
+ fprintf(rsp, "\"padding\":");
+ query_padding(m->padding, rsp);
+ fprintf(rsp,",");
+ fprintf(rsp, "\"rectangle\":");
+ query_rectangle(m->rectangle, rsp);
+ fprintf(rsp,",");
+ fprintf(rsp, "\"desktops\":");
+ fprintf(rsp, "[");
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+ query_desktop(d, rsp);
+ if (d->next != NULL) {
+ fprintf(rsp,",");
+ }
+ }
+ fprintf(rsp, "]");
+ fprintf(rsp, "}");
+}
+
+void query_desktop(desktop_t *d, FILE *rsp)
+{
+ fprintf(rsp, "{");
+ fprintf(rsp, "\"name\":\"%s\",", d->name);
+ fprintf(rsp, "\"id\":%u,", d->id);
+ fprintf(rsp, "\"layout\":\"%s\",", LAYOUT_STR(d->layout));
+ fprintf(rsp, "\"windowGap\":%i,", d->window_gap);
+ fprintf(rsp, "\"borderWidth\":%u,", d->border_width);
+ fprintf(rsp, "\"focusedNodeId\":%u,", d->focus != NULL ? d->focus->id : 0);
+ fprintf(rsp, "\"padding\":");
+ query_padding(d->padding, rsp);
+ fprintf(rsp,",");
+ fprintf(rsp, "\"root\":");
+ query_node(d->root, rsp);
+ fprintf(rsp, "}");
+}
+
+void query_node(node_t *n, FILE *rsp)
+{
+ if (n == NULL) {
+ fprintf(rsp, "null");
+ } else {
+ fprintf(rsp, "{");
+ fprintf(rsp, "\"id\":%u,", n->id);
+ fprintf(rsp, "\"splitType\":\"%s\",", SPLIT_TYPE_STR(n->split_type));
+ fprintf(rsp, "\"splitRatio\":%lf,", n->split_ratio);
+ fprintf(rsp, "\"birthRotation\":%i,", n->birth_rotation);
+ fprintf(rsp, "\"vacant\":%s,", BOOL_STR(n->vacant));
+ fprintf(rsp, "\"hidden\":%s,", BOOL_STR(n->hidden));
+ fprintf(rsp, "\"sticky\":%s,", BOOL_STR(n->sticky));
+ fprintf(rsp, "\"private\":%s,", BOOL_STR(n->private));
+ fprintf(rsp, "\"locked\":%s,", BOOL_STR(n->locked));
+ fprintf(rsp, "\"presel\":");
+ query_presel(n->presel, rsp);
+ fprintf(rsp,",");
+ fprintf(rsp, "\"rectangle\":");
+ query_rectangle(n->rectangle, rsp);
+ fprintf(rsp,",");
+ fprintf(rsp, "\"firstChild\":");
+ query_node(n->first_child, rsp);
+ fprintf(rsp,",");
+ fprintf(rsp, "\"secondChild\":");
+ query_node(n->second_child, rsp);
+ fprintf(rsp,",");
+ fprintf(rsp, "\"client\":");
+ query_client(n->client, rsp);
+ fprintf(rsp, "}");
+ }
+}
+
+void query_presel(presel_t *p, FILE *rsp)
+{
+ if (p == NULL) {
+ fprintf(rsp, "null");
+ } else {
+ fprintf(rsp, "{\"splitDir\":\"%s\",\"splitRatio\":%lf}", SPLIT_DIR_STR(p->split_dir), p->split_ratio);
+ }
+}
+
+void query_client(client_t *c, FILE *rsp)
+{
+ if (c == NULL) {
+ fprintf(rsp, "null");
+ } else {
+ fprintf(rsp, "{");
+ fprintf(rsp, "\"className\":\"%s\",", c->class_name);
+ fprintf(rsp, "\"instanceName\":\"%s\",", c->instance_name);
+ fprintf(rsp, "\"borderWidth\":%u,", c->border_width);
+ fprintf(rsp, "\"state\":\"%s\",", STATE_STR(c->state));
+ fprintf(rsp, "\"lastState\":\"%s\",", STATE_STR(c->last_state));
+ fprintf(rsp, "\"layer\":\"%s\",", LAYER_STR(c->layer));
+ fprintf(rsp, "\"lastLayer\":\"%s\",", LAYER_STR(c->last_layer));
+ fprintf(rsp, "\"urgent\":%s,", BOOL_STR(c->urgent));
+ fprintf(rsp, "\"shown\":%s,", BOOL_STR(c->shown));
+ fprintf(rsp, "\"tiledRectangle\":");
+ query_rectangle(c->tiled_rectangle, rsp);
+ fprintf(rsp,",");
+ fprintf(rsp, "\"floatingRectangle\":");
+ query_rectangle(c->floating_rectangle, rsp);
+ fprintf(rsp, "}");
+ }
+}
+
+void query_rectangle(xcb_rectangle_t r, FILE *rsp)
+{
+ fprintf(rsp, "{\"x\":%i,\"y\":%i,\"width\":%u,\"height\":%u}", r.x, r.y, r.width, r.height);
+}
+
+void query_padding(padding_t p, FILE *rsp)
+{
+ fprintf(rsp, "{\"top\":%i,\"right\":%i,\"bottom\":%i,\"left\":%i}", p.top, p.right, p.bottom, p.left);
+}
+
+void query_history(FILE *rsp)
+{
+ fprintf(rsp, "[");
+ for (history_t *h = history_head; h != NULL; h = h->next) {
+ query_coordinates(&h->loc, rsp);
+ if (h->next != NULL) {
+ fprintf(rsp, ",");
+ }
+ }
+ fprintf(rsp, "]");
+}
+
+void query_coordinates(coordinates_t *loc, FILE *rsp)
+{
+ fprintf(rsp, "{\"monitorId\":%u,\"desktopId\":%u,\"nodeId\":%u}", loc->monitor->id, loc->desktop->id, loc->node!=NULL?loc->node->id:0);
+}
+
+void query_stack(FILE *rsp)
+{
+ fprintf(rsp, "[");
+ for (stacking_list_t *s = stack_head; s != NULL; s = s->next) {
+ fprintf(rsp, "%u", s->node->id);
+ if (s->next != NULL) {
+ fprintf(rsp, ",");
+ }
+ }
+ fprintf(rsp, "]");
+}
+
+int query_node_ids(coordinates_t *ref, coordinates_t *trg, node_select_t *sel, FILE *rsp)
+{
+ int count = 0;
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ if (trg->monitor != NULL && m != trg->monitor) {
+ continue;
+ }
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+ if (trg->desktop != NULL && d != trg->desktop) {
+ continue;
+ }
+ count += query_node_ids_in(d->root, d, m, ref, trg, sel, rsp);
+ }
+ }
+ return count;
+}
+
+int query_node_ids_in(node_t *n, desktop_t *d, monitor_t *m, coordinates_t *ref, coordinates_t *trg, node_select_t *sel, FILE *rsp)
+{
+ int count = 0;
+ if (n == NULL) {
+ return 0;
+ } else {
+ coordinates_t loc = {m, d, n};
+ if ((trg->node == NULL || n == trg->node) &&
+ (sel == NULL || node_matches(&loc, ref, *sel))) {
+ fprintf(rsp, "0x%08X\n", n->id);
+ count++;
+ }
+ count += query_node_ids_in(n->first_child, d, m, ref, trg, sel, rsp);
+ count += query_node_ids_in(n->second_child, d, m, ref, trg, sel, rsp);
+ }
+ return count;
+}
+
+int query_desktop_ids(coordinates_t *ref, coordinates_t *trg, desktop_select_t *sel, desktop_printer_t printer, FILE *rsp)
+{
+ int count = 0;
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ if (trg->monitor != NULL && m != trg->monitor) {
+ continue;
+ }
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+ coordinates_t loc = {m, d, NULL};
+ if ((trg->desktop != NULL && d != trg->desktop) ||
+ (sel != NULL && !desktop_matches(&loc, ref, *sel))) {
+ continue;
+ }
+ printer(d, rsp);
+ count++;
+ }
+ }
+ return count;
+}
+
+int query_monitor_ids(coordinates_t *ref, coordinates_t *trg, monitor_select_t *sel, monitor_printer_t printer, FILE *rsp)
+{
+ int count = 0;
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ coordinates_t loc = {m, NULL, NULL};
+ if ((trg->monitor != NULL && m != trg->monitor) ||
+ (sel != NULL && !monitor_matches(&loc, ref, *sel))) {
+ continue;
+ }
+ printer(m, rsp);
+ count++;
+ }
+ return count;
+}
+
+void fprint_monitor_id(monitor_t *m, FILE *rsp)
+{
+ fprintf(rsp, "0x%08X\n", m->id);
+}
+
+void fprint_monitor_name(monitor_t *m, FILE *rsp)
+{
+ fprintf(rsp, "%s\n", m->name);
+}
+
+void fprint_desktop_id(desktop_t *d, FILE *rsp)
+{
+ fprintf(rsp, "0x%08X\n", d->id);
+}
+
+void fprint_desktop_name(desktop_t *d, FILE *rsp)
+{
+ fprintf(rsp, "%s\n", d->name);
+}
+
+void print_modifier_mask(uint16_t m, FILE *rsp)
+{
+ switch (m) {
+ case XCB_MOD_MASK_SHIFT:
+ fprintf(rsp, "shift");
+ break;
+ case XCB_MOD_MASK_CONTROL:
+ fprintf(rsp, "control");
+ break;
+ case XCB_MOD_MASK_LOCK:
+ fprintf(rsp, "lock");
+ break;
+ case XCB_MOD_MASK_1:
+ fprintf(rsp, "mod1");
+ break;
+ case XCB_MOD_MASK_2:
+ fprintf(rsp, "mod2");
+ break;
+ case XCB_MOD_MASK_3:
+ fprintf(rsp, "mod3");
+ break;
+ case XCB_MOD_MASK_4:
+ fprintf(rsp, "mod4");
+ break;
+ case XCB_MOD_MASK_5:
+ fprintf(rsp, "mod5");
+ break;
+ }
+}
+
+void print_pointer_action(pointer_action_t a, FILE *rsp)
+{
+ switch (a) {
+ case ACTION_MOVE:
+ fprintf(rsp, "move");
+ break;
+ case ACTION_RESIZE_SIDE:
+ fprintf(rsp, "resize_side");
+ break;
+ case ACTION_RESIZE_CORNER:
+ fprintf(rsp, "resize_corner");
+ break;
+ case ACTION_FOCUS:
+ fprintf(rsp, "focus");
+ break;
+ case ACTION_NONE:
+ fprintf(rsp, "none");
+ break;
+ }
+}
+
+node_select_t make_node_select(void)
+{
+ node_select_t sel = {
+ .automatic = OPTION_NONE,
+ .focused = OPTION_NONE,
+ .local = OPTION_NONE,
+ .active = OPTION_NONE,
+ .leaf = OPTION_NONE,
+ .window = OPTION_NONE,
+ .tiled = OPTION_NONE,
+ .pseudo_tiled = OPTION_NONE,
+ .floating = OPTION_NONE,
+ .fullscreen = OPTION_NONE,
+ .hidden = OPTION_NONE,
+ .sticky = OPTION_NONE,
+ .private = OPTION_NONE,
+ .locked = OPTION_NONE,
+ .urgent = OPTION_NONE,
+ .same_class = OPTION_NONE,
+ .descendant_of = OPTION_NONE,
+ .ancestor_of = OPTION_NONE,
+ .below = OPTION_NONE,
+ .normal = OPTION_NONE,
+ .above = OPTION_NONE
+ };
+ return sel;
+}
+
+desktop_select_t make_desktop_select(void)
+{
+ desktop_select_t sel = {
+ .occupied = OPTION_NONE,
+ .focused = OPTION_NONE,
+ .urgent = OPTION_NONE,
+ .local = OPTION_NONE
+ };
+ return sel;
+}
+
+monitor_select_t make_monitor_select(void)
+{
+ monitor_select_t sel = {
+ .occupied = OPTION_NONE,
+ .focused = OPTION_NONE
+ };
+ return sel;
+}
+
+int node_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
+{
+ coordinates_t ref_copy = *ref;
+ ref = &ref_copy;
+ char *desc_copy = copy_string(desc, strlen(desc));
+ desc = desc_copy;
+
+ char *hash = NULL;
+ char *path = strrchr(desc, '@');
+ if (path == NULL) {
+ hash = strrchr(desc, '#');
+ } else {
+ if (path != desc && *(path - 1) == '#') {
+ hash = path - 1;
+ }
+ }
+
+ if (hash != NULL) {
+ *hash = '\0';
+ int ret;
+ coordinates_t tmp = {mon, mon->desk, mon->desk->focus};
+ if ((ret = node_from_desc(desc, &tmp, ref)) == SELECTOR_OK) {
+ desc = hash + 1;
+ } else {
+ free(desc_copy);
+ return ret;
+ }
+ }
+
+ node_select_t sel = make_node_select();
+ char *colon = strrchr(desc, ':');
+
+ if (!parse_node_modifiers(colon != NULL ? colon : desc, &sel)) {
+ free(desc_copy);
+ return SELECTOR_BAD_MODIFIERS;
+ }
+
+ dst->node = NULL;
+
+ direction_t dir;
+ cycle_dir_t cyc;
+ history_dir_t hdi;
+ if (parse_direction(desc, &dir)) {
+ find_nearest_neighbor(ref, dst, dir, sel);
+ } else if (parse_cycle_direction(desc, &cyc)) {
+ find_closest_node(ref, dst, cyc, sel);
+ } else if (parse_history_direction(desc, &hdi)) {
+ history_find_node(hdi, ref, dst, sel);
+ } else if (streq("last", desc)) {
+ history_find_node(HISTORY_OLDER, ref, dst, sel);
+ } else if (streq("biggest", desc)) {
+ find_biggest(ref, dst, sel);
+ } else if (streq("pointed", desc)) {
+ xcb_window_t win;
+ query_pointer(&win, NULL);
+ if (locate_window(win, dst) && node_matches(dst, ref, sel)) {
+ return SELECTOR_OK;
+ } else {
+ return SELECTOR_INVALID;
+ }
+ } else if (streq("focused", desc)) {
+ coordinates_t loc = {mon, mon->desk, mon->desk->focus};
+ if (node_matches(&loc, ref, sel)) {
+ *dst = loc;
+ }
+ } else if (*desc == '@') {
+ desc++;
+ *dst = *ref;
+ if (colon != NULL) {
+ *colon = '\0';
+ int ret;
+ if ((ret = desktop_from_desc(desc, ref, dst)) == SELECTOR_OK) {
+ dst->node = dst->desktop->focus;
+ desc = colon + 1;
+ } else {
+ free(desc_copy);
+ return ret;
+ }
+ }
+ if (*desc == '/') {
+ dst->node = dst->desktop->root;
+ }
+ char *move = strtok(desc, PTH_TOK);
+ while (move != NULL && dst->node != NULL) {
+ if (streq("first", move) || streq("1", move)) {
+ dst->node = dst->node->first_child;
+ } else if (streq("second", move) || streq("2", move)) {
+ dst->node = dst->node->second_child;
+ } else if (streq("parent", move)) {
+ dst->node = dst->node->parent;
+ } else if (streq("brother", move)) {
+ dst->node = brother_tree(dst->node);
+ } else {
+ direction_t dir;
+ if (parse_direction(move, &dir)) {
+ dst->node = find_fence(dst->node, dir);
+ } else {
+ free(desc_copy);
+ return SELECTOR_BAD_DESCRIPTOR;
+ }
+ }
+ move = strtok(NULL, PTH_TOK);
+ }
+ free(desc_copy);
+ if (dst->node != NULL) {
+ if (node_matches(dst, ref, sel)) {
+ return SELECTOR_OK;
+ } else {
+ return SELECTOR_INVALID;
+ }
+ } else if (dst->desktop->root != NULL) {
+ return SELECTOR_INVALID;
+ }
+ return SELECTOR_OK;
+ } else {
+ uint32_t id;
+ if (parse_id(desc, &id)) {
+ free(desc_copy);
+ if (find_by_id(id, dst) && node_matches(dst, ref, sel)) {
+ return SELECTOR_OK;
+ } else {
+ return SELECTOR_INVALID;
+ }
+ } else {
+ free(desc_copy);
+ return SELECTOR_BAD_DESCRIPTOR;
+ }
+ }
+
+ free(desc_copy);
+
+ if (dst->node == NULL) {
+ return SELECTOR_INVALID;
+ }
+
+ return SELECTOR_OK;
+}
+
+int desktop_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
+{
+ coordinates_t ref_copy = *ref;
+ ref = &ref_copy;
+ char *desc_copy = copy_string(desc, strlen(desc));
+ desc = desc_copy;
+
+ char *hash = strrchr(desc, '#');
+
+ if (hash != NULL) {
+ *hash = '\0';
+ int ret;
+ coordinates_t tmp = {mon, mon->desk, NULL};
+ if ((ret = desktop_from_desc(desc, &tmp, ref)) == SELECTOR_OK) {
+ desc = hash + 1;
+ } else {
+ free(desc_copy);
+ return ret;
+ }
+ }
+
+ desktop_select_t sel = make_desktop_select();
+ char *colon = strrchr(desc, ':');
+
+ if (!parse_desktop_modifiers(colon != NULL ? colon : desc, &sel)) {
+ free(desc_copy);
+ return SELECTOR_BAD_MODIFIERS;
+ }
+
+ dst->desktop = NULL;
+
+ cycle_dir_t cyc;
+ history_dir_t hdi;
+ uint16_t idx;
+ uint32_t id;
+ if (parse_cycle_direction(desc, &cyc)) {
+ find_closest_desktop(ref, dst, cyc, sel);
+ } else if (parse_history_direction(desc, &hdi)) {
+ history_find_desktop(hdi, ref, dst, sel);
+ } else if (streq("last", desc)) {
+ history_find_desktop(HISTORY_OLDER, ref, dst, sel);
+ } else if (streq("focused", desc)) {
+ coordinates_t loc = {mon, mon->desk, NULL};
+ if (desktop_matches(&loc, ref, sel)) {
+ *dst = loc;
+ }
+ } else if (colon != NULL) {
+ *colon = '\0';
+ int ret;
+ if ((ret = monitor_from_desc(desc, ref, dst)) == SELECTOR_OK) {
+ if (streq("focused", colon + 1)) {
+ coordinates_t loc = {dst->monitor, dst->monitor->desk, NULL};
+ if (desktop_matches(&loc, ref, sel)) {
+ *dst = loc;
+ }
+ } else if (parse_index(colon + 1, &idx)) {
+ free(desc_copy);
+ if (desktop_from_index(idx, dst, dst->monitor) && desktop_matches(dst, ref, sel)) {
+ return SELECTOR_OK;
+ } else {
+ return SELECTOR_INVALID;
+ }
+ } else {
+ free(desc_copy);
+ return SELECTOR_BAD_DESCRIPTOR;
+ }
+ } else {
+ free(desc_copy);
+ return ret;
+ }
+ } else if (parse_index(desc, &idx) && desktop_from_index(idx, dst, NULL)) {
+ free(desc_copy);
+ if (desktop_matches(dst, ref, sel)) {
+ return SELECTOR_OK;
+ } else {
+ return SELECTOR_INVALID;
+ }
+ } else if (parse_id(desc, &id) && desktop_from_id(id, dst, NULL)) {
+ free(desc_copy);
+ if (desktop_matches(dst, ref, sel)) {
+ return SELECTOR_OK;
+ } else {
+ return SELECTOR_INVALID;
+ }
+ } else {
+ if (locate_desktop(desc, dst)) {
+ free(desc_copy);
+ if (desktop_matches(dst, ref, sel)) {
+ return SELECTOR_OK;
+ } else {
+ return SELECTOR_INVALID;
+ }
+ } else {
+ free(desc_copy);
+ return SELECTOR_BAD_DESCRIPTOR;
+ }
+ }
+
+ free(desc_copy);
+
+ if (dst->desktop == NULL) {
+ return SELECTOR_INVALID;
+ }
+
+ return SELECTOR_OK;
+}
+
+int monitor_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst)
+{
+ coordinates_t ref_copy = *ref;
+ ref = &ref_copy;
+ char *desc_copy = copy_string(desc, strlen(desc));
+ desc = desc_copy;
+
+ char *hash = strrchr(desc, '#');
+
+ if (hash != NULL) {
+ *hash = '\0';
+ int ret;
+ coordinates_t tmp = {mon, NULL, NULL};
+ if ((ret = monitor_from_desc(desc, &tmp, ref)) == SELECTOR_OK) {
+ desc = hash + 1;
+ } else {
+ free(desc_copy);
+ return ret;
+ }
+ }
+
+ monitor_select_t sel = make_monitor_select();
+
+ if (!parse_monitor_modifiers(desc, &sel)) {
+ free(desc_copy);
+ return SELECTOR_BAD_MODIFIERS;
+ }
+
+ dst->monitor = NULL;
+
+ direction_t dir;
+ cycle_dir_t cyc;
+ history_dir_t hdi;
+ uint16_t idx;
+ uint32_t id;
+ if (parse_direction(desc, &dir)) {
+ dst->monitor = nearest_monitor(ref->monitor, dir, sel);
+ } else if (parse_cycle_direction(desc, &cyc)) {
+ dst->monitor = closest_monitor(ref->monitor, cyc, sel);
+ } else if (parse_history_direction(desc, &hdi)) {
+ history_find_monitor(hdi, ref, dst, sel);
+ } else if (streq("last", desc)) {
+ history_find_monitor(HISTORY_OLDER, ref, dst, sel);
+ } else if (streq("primary", desc)) {
+ if (pri_mon != NULL) {
+ coordinates_t loc = {pri_mon, NULL, NULL};
+ if (monitor_matches(&loc, ref, sel)) {
+ dst->monitor = pri_mon;
+ }
+ }
+ } else if (streq("focused", desc)) {
+ coordinates_t loc = {mon, NULL, NULL};
+ if (monitor_matches(&loc, ref, sel)) {
+ dst->monitor = mon;
+ }
+ } else if (parse_index(desc, &idx) && monitor_from_index(idx, dst)) {
+ free(desc_copy);
+ if (monitor_matches(dst, ref, sel)) {
+ return SELECTOR_OK;
+ } else {
+ return SELECTOR_INVALID;
+ }
+ } else if (parse_id(desc, &id) && monitor_from_id(id, dst)) {
+ free(desc_copy);
+ if (monitor_matches(dst, ref, sel)) {
+ return SELECTOR_OK;
+ } else {
+ return SELECTOR_INVALID;
+ }
+ } else {
+ if (locate_monitor(desc, dst)) {
+ free(desc_copy);
+ if (monitor_matches(dst, ref, sel)) {
+ return SELECTOR_OK;
+ } else {
+ return SELECTOR_INVALID;
+ }
+ } else {
+ free(desc_copy);
+ return SELECTOR_BAD_DESCRIPTOR;
+ }
+ }
+
+ free(desc_copy);
+
+ if (dst->monitor == NULL) {
+ return SELECTOR_INVALID;
+ }
+
+ return SELECTOR_OK;
+}
+
+bool locate_window(xcb_window_t win, coordinates_t *loc)
+{
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ 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)) {
+ if (n->client == NULL) {
+ continue;
+ }
+ if (n->id == win) {
+ loc->monitor = m;
+ loc->desktop = d;
+ loc->node = n;
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool locate_desktop(char *name, coordinates_t *loc)
+{
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+ if (streq(d->name, name)) {
+ loc->monitor = m;
+ loc->desktop = d;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool locate_monitor(char *name, coordinates_t *loc)
+{
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ if (streq(m->name, name)) {
+ loc->monitor = m;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool desktop_from_id(uint32_t id, coordinates_t *loc, monitor_t *mm)
+{
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ if (mm != NULL && m != mm) {
+ continue;
+ }
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+ if (d->id == id) {
+ loc->monitor = m;
+ loc->desktop = d;
+ loc->node = NULL;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool desktop_from_index(uint16_t idx, coordinates_t *loc, monitor_t *mm)
+{
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ if (mm != NULL && m != mm) {
+ continue;
+ }
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next, idx--) {
+ if (idx == 1) {
+ loc->monitor = m;
+ loc->desktop = d;
+ loc->node = NULL;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool monitor_from_id(uint32_t id, coordinates_t *loc)
+{
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ if (m->id == id) {
+ loc->monitor = m;
+ loc->desktop = NULL;
+ loc->node = NULL;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool monitor_from_index(int idx, coordinates_t *loc)
+{
+ for (monitor_t *m = mon_head; m != NULL; m = m->next, idx--) {
+ if (idx == 1) {
+ loc->monitor = m;
+ loc->desktop = NULL;
+ loc->node = NULL;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool node_matches(coordinates_t *loc, coordinates_t *ref, node_select_t sel)
+{
+ if (loc->node == NULL) {
+ return false;
+ }
+
+ if (sel.focused != OPTION_NONE &&
+ loc->node != loc->desktop->focus
+ ? sel.focused == OPTION_TRUE
+ : sel.focused == OPTION_FALSE) {
+ return false;
+ }
+
+ if (sel.automatic != OPTION_NONE &&
+ loc->node->presel != NULL
+ ? sel.automatic == OPTION_TRUE
+ : sel.automatic == OPTION_FALSE) {
+ return false;
+ }
+
+ if (sel.local != OPTION_NONE &&
+ loc->desktop != ref->desktop
+ ? sel.local == OPTION_TRUE
+ : sel.local == OPTION_FALSE) {
+ return false;
+ }
+
+ if (sel.active != OPTION_NONE &&
+ loc->desktop != loc->monitor->desk
+ ? sel.active == OPTION_TRUE
+ : sel.active == OPTION_FALSE) {
+ return false;
+ }
+
+ if (sel.leaf != OPTION_NONE &&
+ !is_leaf(loc->node)
+ ? sel.leaf == OPTION_TRUE
+ : sel.leaf == OPTION_FALSE) {
+ return false;
+ }
+
+ if (sel.window != OPTION_NONE &&
+ loc->node->client == NULL
+ ? sel.window == OPTION_TRUE
+ : sel.window == OPTION_FALSE) {
+ return false;
+ }
+
+#define NFLAG(p) \
+ if (sel.p != OPTION_NONE && \
+ !loc->node->p \
+ ? sel.p == OPTION_TRUE \
+ : sel.p == OPTION_FALSE) { \
+ return false; \
+ }
+ NFLAG(hidden)
+ NFLAG(sticky)
+ NFLAG(private)
+ NFLAG(locked)
+#undef WFLAG
+
+ if (loc->node->client == NULL &&
+ (sel.same_class != OPTION_NONE ||
+ sel.tiled != OPTION_NONE ||
+ sel.pseudo_tiled != OPTION_NONE ||
+ sel.floating != OPTION_NONE ||
+ sel.fullscreen != OPTION_NONE ||
+ sel.below != OPTION_NONE ||
+ sel.normal != OPTION_NONE ||
+ sel.above != OPTION_NONE ||
+ sel.urgent != OPTION_NONE)) {
+ return false;
+ }
+
+ if (ref->node != NULL && ref->node->client != NULL &&
+ sel.same_class != OPTION_NONE &&
+ streq(loc->node->client->class_name, ref->node->client->class_name)
+ ? sel.same_class == OPTION_FALSE
+ : sel.same_class == OPTION_TRUE) {
+ return false;
+ }
+
+ if (sel.descendant_of != OPTION_NONE &&
+ !is_descendant(loc->node, ref->node)
+ ? sel.descendant_of == OPTION_TRUE
+ : sel.descendant_of == OPTION_FALSE) {
+ return false;
+ }
+
+ if (sel.ancestor_of != OPTION_NONE &&
+ !is_descendant(ref->node, loc->node)
+ ? sel.ancestor_of == OPTION_TRUE
+ : sel.ancestor_of == OPTION_FALSE) {
+ return false;
+ }
+
+#define WSTATE(p, e) \
+ if (sel.p != OPTION_NONE && \
+ loc->node->client->state != e \
+ ? sel.p == OPTION_TRUE \
+ : sel.p == OPTION_FALSE) { \
+ return false; \
+ }
+ WSTATE(tiled, STATE_TILED)
+ WSTATE(pseudo_tiled, STATE_PSEUDO_TILED)
+ WSTATE(floating, STATE_FLOATING)
+ WSTATE(fullscreen, STATE_FULLSCREEN)
+#undef WSTATE
+
+#define WLAYER(p, e) \
+ if (sel.p != OPTION_NONE && \
+ loc->node->client->layer != e \
+ ? sel.p == OPTION_TRUE \
+ : sel.p == OPTION_FALSE) { \
+ return false; \
+ }
+ WLAYER(below, LAYER_BELOW)
+ WLAYER(normal, LAYER_NORMAL)
+ WLAYER(above, LAYER_ABOVE)
+#undef WLAYER
+
+#define WFLAG(p) \
+ if (sel.p != OPTION_NONE && \
+ !loc->node->client->p \
+ ? sel.p == OPTION_TRUE \
+ : sel.p == OPTION_FALSE) { \
+ return false; \
+ }
+ WFLAG(urgent)
+#undef WFLAG
+
+ return true;
+}
+
+bool desktop_matches(coordinates_t *loc, coordinates_t *ref, desktop_select_t sel)
+{
+ if (sel.occupied != OPTION_NONE &&
+ loc->desktop->root == NULL
+ ? sel.occupied == OPTION_TRUE
+ : sel.occupied == OPTION_FALSE) {
+ return false;
+ }
+
+ if (sel.focused != OPTION_NONE &&
+ loc->desktop != loc->monitor->desk
+ ? sel.focused == OPTION_TRUE
+ : sel.focused == OPTION_FALSE) {
+ return false;
+ }
+
+ if (sel.urgent != OPTION_NONE &&
+ !is_urgent(loc->desktop)
+ ? sel.urgent == OPTION_TRUE
+ : sel.urgent == OPTION_FALSE) {
+ return false;
+ }
+
+ if (sel.local != OPTION_NONE &&
+ ref->monitor != loc->monitor
+ ? sel.local == OPTION_TRUE
+ : sel.local == OPTION_FALSE) {
+ return false;
+ }
+
+ return true;
+}
+
+bool monitor_matches(coordinates_t *loc, __attribute__((unused)) coordinates_t *ref, monitor_select_t sel)
+{
+ if (sel.occupied != OPTION_NONE &&
+ loc->monitor->desk->root == NULL
+ ? sel.occupied == OPTION_TRUE
+ : sel.occupied == OPTION_FALSE) {
+ return false;
+ }
+
+ if (sel.focused != OPTION_NONE &&
+ mon != loc->monitor
+ ? sel.focused == OPTION_TRUE
+ : sel.focused == OPTION_FALSE) {
+ return false;
+ }
+
+ return true;
+}
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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.
+ */
+
+#ifndef BSPWM_QUERY_H
+#define BSPWM_QUERY_H
+
+#define PTH_TOK "/"
+
+typedef enum {
+ DOMAIN_TREE,
+ DOMAIN_MONITOR,
+ DOMAIN_DESKTOP,
+ DOMAIN_NODE
+} domain_t;
+
+enum {
+ SELECTOR_OK,
+ SELECTOR_INVALID,
+ SELECTOR_BAD_MODIFIERS,
+ SELECTOR_BAD_DESCRIPTOR
+};
+
+typedef void (*monitor_printer_t)(monitor_t *m, FILE *rsp);
+typedef void (*desktop_printer_t)(desktop_t *m, FILE *rsp);
+
+void query_tree(FILE *rsp);
+void query_monitor(monitor_t *m, FILE *rsp);
+void query_desktop(desktop_t *d, FILE *rsp);
+void query_node(node_t *n, FILE *rsp);
+void query_presel(presel_t *p, FILE *rsp);
+void query_client(client_t *c, FILE *rsp);
+void query_rectangle(xcb_rectangle_t r, FILE *rsp);
+void query_padding(padding_t p, FILE *rsp);
+void query_history(FILE *rsp);
+void query_coordinates(coordinates_t *loc, FILE *rsp);
+void query_stack(FILE *rsp);
+int query_node_ids(coordinates_t *ref, coordinates_t *trg, node_select_t *sel, FILE *rsp);
+int query_node_ids_in(node_t *n, desktop_t *d, monitor_t *m, coordinates_t *ref, coordinates_t *trg, node_select_t *sel, FILE *rsp);
+int query_desktop_ids(coordinates_t *ref, coordinates_t *trg, desktop_select_t *sel, desktop_printer_t printer, FILE *rsp);
+int query_monitor_ids(coordinates_t *ref, coordinates_t *trg, monitor_select_t *sel, monitor_printer_t printer, FILE *rsp);
+void fprint_monitor_id(monitor_t *m, FILE *rsp);
+void fprint_monitor_name(monitor_t *m, FILE *rsp);
+void fprint_desktop_id(desktop_t *d, FILE *rsp);
+void fprint_desktop_name(desktop_t *d, FILE *rsp);
+void print_modifier_mask(uint16_t m, FILE *rsp);
+void print_pointer_action(pointer_action_t a, FILE *rsp);
+node_select_t make_node_select(void);
+desktop_select_t make_desktop_select(void);
+monitor_select_t make_monitor_select(void);
+int node_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst);
+int desktop_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst);
+int monitor_from_desc(char *desc, coordinates_t *ref, coordinates_t *dst);
+bool locate_window(xcb_window_t win, coordinates_t *loc);
+bool locate_desktop(char *name, coordinates_t *loc);
+bool locate_monitor(char *name, coordinates_t *loc);
+bool desktop_from_id(uint32_t id, coordinates_t *loc, monitor_t *mm);
+bool desktop_from_index(uint16_t idx, coordinates_t *loc, monitor_t *mm);
+bool monitor_from_id(uint32_t id, coordinates_t *loc);
+bool monitor_from_index(int idx, coordinates_t *loc);
+bool node_matches(coordinates_t *loc, coordinates_t *ref, node_select_t sel);
+bool desktop_matches(coordinates_t *loc, coordinates_t *ref, desktop_select_t sel);
+bool monitor_matches(coordinates_t *loc, __attribute__((unused)) coordinates_t *ref, monitor_select_t sel);
+
+#endif
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "bspwm.h"
+#include "desktop.h"
+#include "ewmh.h"
+#include "history.h"
+#include "pointer.h"
+#include "monitor.h"
+#include "query.h"
+#include "stack.h"
+#include "tree.h"
+#include "settings.h"
+#include "restore.h"
+#include "window.h"
+#include "parse.h"
+
+bool restore_tree(const char *file_path)
+{
+ size_t jslen;
+ char *json = read_string(file_path, &jslen);
+
+ if (json == NULL) {
+ return false;
+ }
+
+ int nbtok = 256;
+ jsmn_parser parser;
+ jsmntok_t *tokens = malloc(nbtok * sizeof(jsmntok_t));
+
+ if (tokens == NULL) {
+ perror("Restore tree: malloc");
+ free(json);
+ return false;
+ }
+
+ jsmn_init(&parser);
+ int ret;
+
+ while ((ret = jsmn_parse(&parser, json, jslen, tokens, nbtok)) == JSMN_ERROR_NOMEM) {
+ nbtok *= 2;
+ jsmntok_t *rtokens = realloc(tokens, nbtok * sizeof(jsmntok_t));
+ if (rtokens == NULL) {
+ perror("Restore tree: realloc");
+ free(tokens);
+ free(json);
+ return false;
+ } else {
+ tokens = rtokens;
+ }
+ }
+
+ if (ret < 0) {
+ warn("Restore tree: jsmn_parse: ");
+ switch (ret) {
+ case JSMN_ERROR_NOMEM:
+ warn("not enough memory.\n");
+ break;
+ case JSMN_ERROR_INVAL:
+ warn("found invalid character inside JSON string.\n");
+ break;
+ case JSMN_ERROR_PART:
+ warn("not a full JSON packet.\n");
+ break;
+ default:
+ warn("unknown error.\n");
+ break;
+ }
+
+ free(tokens);
+ free(json);
+
+ return false;
+ }
+
+ int num = tokens[0].size;
+
+ if (num < 1) {
+ free(tokens);
+ free(json);
+
+ return false;
+ }
+
+ mon = NULL;
+ while (mon_head != NULL) {
+ remove_monitor(mon_head);
+ }
+
+ jsmntok_t *t = tokens + 1;
+ uint32_t focused_monitor_id = 0, primary_monitor_id = 0;
+
+ for (int i = 0; i < num; i++) {
+ if (keyeq("focusedMonitorId", t, json)) {
+ t++;
+ sscanf(json + t->start, "%u", &focused_monitor_id);
+ } else if (keyeq("primaryMonitorId", t, json)) {
+ t++;
+ sscanf(json + t->start, "%u", &primary_monitor_id);
+ } else if (keyeq("clientsCount", t, json)) {
+ t++;
+ sscanf(json + t->start, "%u", &clients_count);
+ } else if (keyeq("monitors", t, json)) {
+ t++;
+ int s = t->size;
+ t++;
+ for (int j = 0; j < s; j++) {
+ monitor_t *m = restore_monitor(&t, json);
+ if (m->desk == NULL) {
+ add_desktop(m, make_desktop(NULL, XCB_NONE));
+ }
+ add_monitor(m);
+ }
+ continue;
+ } else if (keyeq("focusHistory", t, json)) {
+ t++;
+ restore_history(&t, json);
+ continue;
+ } else if (keyeq("stackingList", t, json)) {
+ t++;
+ restore_stack(&t, json);
+ continue;
+ }
+ t++;
+ }
+
+ if (focused_monitor_id != 0) {
+ coordinates_t loc;
+ if (monitor_from_id(focused_monitor_id, &loc)) {
+ mon = loc.monitor;
+ }
+ }
+
+ if (primary_monitor_id != 0) {
+ coordinates_t loc;
+ if (monitor_from_id(primary_monitor_id, &loc)) {
+ pri_mon = loc.monitor;
+ }
+ }
+
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+ refresh_presel_feedbacks(m, d, d->root);
+ restack_presel_feedbacks(d);
+ for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
+ if (n->client == NULL) {
+ continue;
+ }
+ initialize_client(n);
+ uint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)};
+ xcb_change_window_attributes(dpy, n->id, XCB_CW_EVENT_MASK, values);
+ window_grab_buttons(n->id);
+ }
+ }
+ }
+
+ ewmh_update_client_list(false);
+ ewmh_update_client_list(true);
+ ewmh_update_active_window();
+ ewmh_update_number_of_desktops();
+ ewmh_update_current_desktop();
+ ewmh_update_desktop_names();
+ ewmh_update_desktop_viewport();
+
+ free(tokens);
+ free(json);
+
+ return true;
+}
+
+#define RESTORE_INT(k, p) \
+ } else if (keyeq(#k, *t, json)) { \
+ (*t)++; \
+ sscanf(json + (*t)->start, "%i", p);
+
+#define RESTORE_UINT(k, p) \
+ } else if (keyeq(#k, *t, json)) { \
+ (*t)++; \
+ sscanf(json + (*t)->start, "%u", p);
+
+#define RESTORE_USINT(k, p) \
+ } else if (keyeq(#k, *t, json)) { \
+ (*t)++; \
+ sscanf(json + (*t)->start, "%hu", p);
+
+#define RESTORE_DOUBLE(k, p) \
+ } else if (keyeq(#k, *t, json)) { \
+ (*t)++; \
+ sscanf(json + (*t)->start, "%lf", p);
+
+#define RESTORE_ANY(k, p, f) \
+ } else if (keyeq(#k, *t, json)) { \
+ (*t)++; \
+ char *val = copy_string(json + (*t)->start, (*t)->end - (*t)->start); \
+ f(val, p); \
+ free(val);
+
+#define RESTORE_BOOL(k, p) RESTORE_ANY(k, p, parse_bool)
+
+monitor_t *restore_monitor(jsmntok_t **t, char *json)
+{
+ int num = (*t)->size;
+ (*t)++;
+ monitor_t *m = make_monitor(NULL, NULL, UINT32_MAX);
+ uint32_t focused_desktop_id = 0;
+
+ for (int i = 0; i < num; i++) {
+ if (keyeq("name", *t, json)) {
+ (*t)++;
+ snprintf(m->name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
+ RESTORE_UINT(id, &m->id)
+ RESTORE_UINT(randrId, &m->randr_id)
+ RESTORE_BOOL(wired, &m->wired)
+ RESTORE_UINT(stickyCount, &m->sticky_count)
+ RESTORE_INT(windowGap, &m->window_gap)
+ RESTORE_UINT(borderWidth, &m->border_width)
+ RESTORE_UINT(focusedDesktopId, &focused_desktop_id)
+ } else if (keyeq("padding", *t, json)) {
+ (*t)++;
+ restore_padding(&m->padding, t, json);
+ continue;
+ } else if (keyeq("rectangle", *t, json)) {
+ (*t)++;
+ restore_rectangle(&m->rectangle, t, json);
+ update_root(m, &m->rectangle);
+ continue;
+ } else if (keyeq("desktops", *t, json)) {
+ (*t)++;
+ int s = (*t)->size;
+ (*t)++;
+ for (int j = 0; j < s; j++) {
+ desktop_t *d = restore_desktop(t, json);
+ add_desktop(m, d);
+ }
+ continue;
+ } else {
+ warn("Restore monitor: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
+ (*t)++;
+ }
+ (*t)++;
+ }
+
+ if (focused_desktop_id != 0) {
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+ if (d->id == focused_desktop_id) {
+ m->desk = d;
+ break;
+ }
+ }
+ }
+
+ return m;
+}
+
+desktop_t *restore_desktop(jsmntok_t **t, char *json)
+{
+ int s = (*t)->size;
+ (*t)++;
+ desktop_t *d = make_desktop(NULL, UINT32_MAX);
+ xcb_window_t focusedNodeId = XCB_NONE;
+
+ for (int i = 0; i < s; i++) {
+ if (keyeq("name", *t, json)) {
+ (*t)++;
+ snprintf(d->name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
+ RESTORE_UINT(id, &d->id)
+ RESTORE_ANY(layout, &d->layout, parse_layout)
+ RESTORE_INT(windowGap, &d->window_gap)
+ RESTORE_UINT(borderWidth, &d->border_width)
+ } else if (keyeq("focusedNodeId", *t, json)) {
+ (*t)++;
+ sscanf(json + (*t)->start, "%u", &focusedNodeId);
+ } else if (keyeq("padding", *t, json)) {
+ (*t)++;
+ restore_padding(&d->padding, t, json);
+ continue;
+ } else if (keyeq("root", *t, json)) {
+ (*t)++;
+ d->root = restore_node(t, json);
+ continue;
+ } else {
+ warn("Restore desktop: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
+ (*t)++;
+ }
+ (*t)++;
+ }
+
+ if (focusedNodeId != XCB_NONE) {
+ d->focus = find_by_id_in(d->root, focusedNodeId);
+ }
+
+ return d;
+}
+
+node_t *restore_node(jsmntok_t **t, char *json)
+{
+ if ((*t)->type == JSMN_PRIMITIVE) {
+ (*t)++;
+ return NULL;
+ } else {
+ int s = (*t)->size;
+ (*t)++;
+ /* hack to prevent a new ID from being generated */
+ node_t *n = make_node(UINT32_MAX);
+
+ for (int i = 0; i < s; i++) {
+ if (keyeq("id", *t, json)) {
+ (*t)++;
+ sscanf(json + (*t)->start, "%u", &n->id);
+ RESTORE_ANY(splitType, &n->split_type, parse_split_type)
+ RESTORE_DOUBLE(splitRatio, &n->split_ratio)
+ RESTORE_INT(birthRotation, &n->birth_rotation)
+ RESTORE_BOOL(vacant, &n->vacant)
+ RESTORE_BOOL(hidden, &n->hidden)
+ RESTORE_BOOL(sticky, &n->sticky)
+ RESTORE_BOOL(private, &n->private)
+ RESTORE_BOOL(locked, &n->locked)
+ } else if (keyeq("presel", *t, json)) {
+ (*t)++;
+ n->presel = restore_presel(t, json);
+ continue;
+ } else if (keyeq("rectangle", *t, json)) {
+ (*t)++;
+ restore_rectangle(&n->rectangle, t, json);
+ continue;
+ } else if (keyeq("firstChild", *t, json)) {
+ (*t)++;
+ node_t *fc = restore_node(t, json);
+ n->first_child = fc;
+ if (fc != NULL) {
+ fc->parent = n;
+ }
+ continue;
+ } else if (keyeq("secondChild", *t, json)) {
+ (*t)++;
+ node_t *sc = restore_node(t, json);
+ n->second_child = sc;
+ if (sc != NULL) {
+ sc->parent = n;
+ }
+ continue;
+ } else if (keyeq("client", *t, json)) {
+ (*t)++;
+ n->client = restore_client(t, json);
+ continue;
+ } else {
+ warn("Restore node: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
+ (*t)++;
+ }
+ (*t)++;
+ }
+
+ return n;
+ }
+}
+
+presel_t *restore_presel(jsmntok_t **t, char *json)
+{
+ if ((*t)->type == JSMN_PRIMITIVE) {
+ (*t)++;
+ return NULL;
+ } else {
+ int s = (*t)->size;
+ (*t)++;
+ presel_t *p = make_presel();
+
+ for (int i = 0; i < s; i++) {
+ if (keyeq("splitRatio", *t, json)) {
+ (*t)++;
+ sscanf(json + (*t)->start, "%lf", &p->split_ratio);
+ RESTORE_ANY(splitDir, &p->split_dir, parse_direction)
+ }
+
+ (*t)++;
+ }
+
+ return p;
+ }
+}
+
+
+client_t *restore_client(jsmntok_t **t, char *json)
+{
+ if ((*t)->type == JSMN_PRIMITIVE) {
+ (*t)++;
+ return NULL;
+ } else {
+ int s = (*t)->size;
+ (*t)++;
+ client_t *c = make_client();
+
+ for (int i = 0; i < s; i++) {
+ if (keyeq("className", *t, json)) {
+ (*t)++;
+ snprintf(c->class_name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
+ } else if (keyeq("instanceName", *t, json)) {
+ (*t)++;
+ snprintf(c->instance_name, (*t)->end - (*t)->start + 1, "%s", json + (*t)->start);
+ RESTORE_ANY(state, &c->state, parse_client_state)
+ RESTORE_ANY(lastState, &c->last_state, parse_client_state)
+ RESTORE_ANY(layer, &c->layer, parse_stack_layer)
+ RESTORE_ANY(lastLayer, &c->last_layer, parse_stack_layer)
+ RESTORE_UINT(borderWidth, &c->border_width)
+ RESTORE_BOOL(urgent, &c->urgent)
+ RESTORE_BOOL(shown, &c->shown)
+ } else if (keyeq("tiledRectangle", *t, json)) {
+ (*t)++;
+ restore_rectangle(&c->tiled_rectangle, t, json);
+ continue;
+ } else if (keyeq("floatingRectangle", *t, json)) {
+ (*t)++;
+ restore_rectangle(&c->floating_rectangle, t, json);
+ continue;
+ } else {
+ warn("Restore client: unknown key: '%.*s'.\n", (*t)->end - (*t)->start, json + (*t)->start);
+ (*t)++;
+ }
+
+ (*t)++;
+ }
+
+ return c;
+ }
+}
+
+void restore_rectangle(xcb_rectangle_t *r, jsmntok_t **t, char *json)
+{
+ int s = (*t)->size;
+ (*t)++;
+
+ for (int i = 0; i < s; i++) {
+ if (keyeq("x", *t, json)) {
+ (*t)++;
+ sscanf(json + (*t)->start, "%hi", &r->x);
+ } else if (keyeq("y", *t, json)) {
+ (*t)++;
+ sscanf(json + (*t)->start, "%hi", &r->y);
+ } else if (keyeq("width", *t, json)) {
+ (*t)++;
+ sscanf(json + (*t)->start, "%hu", &r->width);
+ } else if (keyeq("height", *t, json)) {
+ (*t)++;
+ sscanf(json + (*t)->start, "%hu", &r->height);
+ }
+ (*t)++;
+ }
+}
+
+void restore_padding(padding_t *p, jsmntok_t **t, char *json)
+{
+ int s = (*t)->size;
+ (*t)++;
+
+ for (int i = 0; i < s; i++) {
+ if (keyeq("top", *t, json)) {
+ (*t)++;
+ sscanf(json + (*t)->start, "%i", &p->top);
+ } else if (keyeq("right", *t, json)) {
+ (*t)++;
+ sscanf(json + (*t)->start, "%i", &p->right);
+ } else if (keyeq("bottom", *t, json)) {
+ (*t)++;
+ sscanf(json + (*t)->start, "%i", &p->bottom);
+ } else if (keyeq("left", *t, json)) {
+ (*t)++;
+ sscanf(json + (*t)->start, "%i", &p->left);
+ }
+ (*t)++;
+ }
+}
+
+void restore_history(jsmntok_t **t, char *json)
+{
+ int s = (*t)->size;
+ (*t)++;
+
+ for (int i = 0; i < s; i++) {
+ coordinates_t loc = {NULL, NULL, NULL};
+ restore_coordinates(&loc, t, json);
+ if (loc.monitor != NULL && loc.desktop != NULL) {
+ history_add(loc.monitor, loc.desktop, loc.node);
+ }
+ }
+}
+
+void restore_coordinates(coordinates_t *loc, jsmntok_t **t, char *json)
+{
+ int s = (*t)->size;
+ (*t)++;
+ uint32_t id = 0;
+
+ for (int i = 0; i < s; i++) {
+ if (keyeq("monitorId", *t, json)) {
+ (*t)++;
+ sscanf(json + (*t)->start, "%u", &id);
+ loc->monitor = find_monitor(id);
+ } else if (keyeq("desktopId", *t, json)) {
+ (*t)++;
+ sscanf(json + (*t)->start, "%u", &id);
+ loc->desktop = find_desktop_in(id, loc->monitor);
+ } else if (keyeq("nodeId", *t, json)) {
+ (*t)++;
+ sscanf(json + (*t)->start, "%u", &id);
+ loc->node = find_by_id_in(loc->desktop != NULL ? loc->desktop->root : NULL, id);
+ }
+ (*t)++;
+ }
+}
+
+void restore_stack(jsmntok_t **t, char *json)
+{
+ int s = (*t)->size;
+ (*t)++;
+
+ for (int i = 0; i < s; i++) {
+ uint32_t id;
+ sscanf(json + (*t)->start, "%u", &id);
+ coordinates_t loc;
+ if (locate_window(id, &loc)) {
+ stack_insert_after(stack_tail, loc.node);
+ }
+ (*t)++;
+ }
+}
+
+#undef RESTORE_INT
+#undef RESTORE_UINT
+#undef RESTORE_USINT
+#undef RESTORE_DOUBLE
+#undef RESTORE_ANY
+#undef RESTORE_BOOL
+
+bool keyeq(char *s, jsmntok_t *key, char *json)
+{
+ size_t n = key->end - key->start;
+ return (strlen(s) == n && strncmp(s, json + key->start, n) == 0);
+}
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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.
+ */
+
+#ifndef BSPWM_RESTORE_H
+#define BSPWM_RESTORE_H
+
+#include "jsmn.h"
+
+bool restore_tree(const char *file_path);
+monitor_t *restore_monitor(jsmntok_t **t, char *json);
+desktop_t *restore_desktop(jsmntok_t **t, char *json);
+node_t *restore_node(jsmntok_t **t, char *json);
+presel_t *restore_presel(jsmntok_t **t, char *json);
+client_t *restore_client(jsmntok_t **t, char *json);
+void restore_rectangle(xcb_rectangle_t *r, jsmntok_t **t, char *json);
+void restore_padding(padding_t *p, jsmntok_t **t, char *json);
+void restore_history(jsmntok_t **t, char *json);
+void restore_coordinates(coordinates_t *loc, jsmntok_t **t, char *json);
+void restore_stack(jsmntok_t **t, char *json);
+void restore_wm_state(xcb_atom_t *w, jsmntok_t **t, char *json);
+bool keyeq(char *s, jsmntok_t *key, char *json);
+
+#endif
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <string.h>
+#include <unistd.h>
+#include "bspwm.h"
+#include "ewmh.h"
+#include "window.h"
+#include "parse.h"
+#include "settings.h"
+#include "rule.h"
+
+rule_t *make_rule(void)
+{
+ rule_t *r = calloc(1, sizeof(rule_t));
+ r->class_name[0] = r->instance_name[0] = r->effect[0] = '\0';
+ r->next = r->prev = NULL;
+ r->one_shot = false;
+ return r;
+}
+
+void add_rule(rule_t *r)
+ {
+ if (rule_head == NULL) {
+ rule_head = rule_tail = r;
+ } else {
+ rule_tail->next = r;
+ r->prev = rule_tail;
+ rule_tail = r;
+ }
+}
+
+void remove_rule(rule_t *r)
+{
+ if (r == NULL)
+ return;
+ rule_t *prev = r->prev;
+ rule_t *next = r->next;
+ if (prev != NULL)
+ prev->next = next;
+ if (next != NULL)
+ next->prev = prev;
+ if (r == rule_head)
+ rule_head = next;
+ if (r == rule_tail)
+ rule_tail = prev;
+ free(r);
+}
+
+void remove_rule_by_cause(char *cause)
+{
+ rule_t *r = rule_head;
+ char *class_name = strtok(cause, COL_TOK);
+ char *instance_name = strtok(NULL, COL_TOK);
+ while (r != NULL) {
+ rule_t *next = r->next;
+ if ((streq(class_name, MATCH_ANY) || streq(r->class_name, class_name)) &&
+ (instance_name == NULL || streq(instance_name, MATCH_ANY) || streq(r->instance_name, instance_name))) {
+ remove_rule(r);
+ }
+ r = next;
+ }
+}
+
+bool remove_rule_by_index(int idx)
+{
+ for (rule_t *r = rule_head; r != NULL; r = r->next, idx--) {
+ if (idx == 0) {
+ remove_rule(r);
+ return true;
+ }
+ }
+ return false;
+}
+
+rule_consequence_t *make_rule_conquence(void)
+{
+ rule_consequence_t *rc = calloc(1, sizeof(rule_consequence_t));
+ rc->manage = rc->focus = rc->border = true;
+ rc->layer = NULL;
+ rc->state = NULL;
+ return rc;
+}
+
+pending_rule_t *make_pending_rule(int fd, xcb_window_t win, rule_consequence_t *csq)
+{
+ pending_rule_t *pr = calloc(1, sizeof(pending_rule_t));
+ pr->prev = pr->next = NULL;
+ pr->fd = fd;
+ pr->win = win;
+ pr->csq = csq;
+ return pr;
+}
+
+void add_pending_rule(pending_rule_t *pr)
+{
+ if (pr == NULL) {
+ return;
+ }
+ if (pending_rule_head == NULL) {
+ pending_rule_head = pending_rule_tail = pr;
+ } else {
+ pending_rule_tail->next = pr;
+ pr->prev = pending_rule_tail;
+ pending_rule_tail = pr;
+ }
+}
+
+void remove_pending_rule(pending_rule_t *pr)
+{
+ if (pr == NULL) {
+ return;
+ }
+ pending_rule_t *a = pr->prev;
+ pending_rule_t *b = pr->next;
+ if (a != NULL) {
+ a->next = b;
+ }
+ if (b != NULL) {
+ b->prev = a;
+ }
+ if (pr == pending_rule_head) {
+ pending_rule_head = b;
+ }
+ if (pr == pending_rule_tail) {
+ pending_rule_tail = a;
+ }
+ close(pr->fd);
+ free(pr->csq);
+ free(pr);
+}
+
+void apply_rules(xcb_window_t win, rule_consequence_t *csq)
+{
+ xcb_ewmh_get_atoms_reply_t win_type;
+
+ if (xcb_ewmh_get_wm_window_type_reply(ewmh, xcb_ewmh_get_wm_window_type(ewmh, win), &win_type, NULL) == 1) {
+ for (unsigned int i = 0; i < win_type.atoms_len; i++) {
+ xcb_atom_t a = win_type.atoms[i];
+ if (a == ewmh->_NET_WM_WINDOW_TYPE_TOOLBAR ||
+ a == ewmh->_NET_WM_WINDOW_TYPE_UTILITY) {
+ csq->focus = false;
+ } else if (a == ewmh->_NET_WM_WINDOW_TYPE_DIALOG) {
+ if (csq->state == NULL) {
+ csq->state = calloc(1, sizeof(client_state_t));
+ }
+ *(csq->state) = STATE_FLOATING;
+ csq->center = true;
+ } else if (a == ewmh->_NET_WM_WINDOW_TYPE_DOCK ||
+ a == ewmh->_NET_WM_WINDOW_TYPE_DESKTOP ||
+ a == ewmh->_NET_WM_WINDOW_TYPE_NOTIFICATION) {
+ csq->manage = false;
+ if (a == ewmh->_NET_WM_WINDOW_TYPE_DESKTOP) {
+ window_lower(win);
+ }
+ }
+ }
+ xcb_ewmh_get_atoms_reply_wipe(&win_type);
+ }
+
+ xcb_ewmh_get_atoms_reply_t win_state;
+
+ if (xcb_ewmh_get_wm_state_reply(ewmh, xcb_ewmh_get_wm_state(ewmh, win), &win_state, NULL) == 1) {
+ for (unsigned int i = 0; i < win_state.atoms_len; i++) {
+ xcb_atom_t a = win_state.atoms[i];
+ if (a == ewmh->_NET_WM_STATE_FULLSCREEN) {
+ if (csq->state == NULL) {
+ csq->state = calloc(1, sizeof(client_state_t));
+ }
+ *(csq->state) = STATE_FULLSCREEN;
+ } else if (a == ewmh->_NET_WM_STATE_BELOW) {
+ if (csq->layer == NULL) {
+ csq->layer = calloc(1, sizeof(stack_layer_t));
+ }
+ *(csq->layer) = LAYER_BELOW;
+ } else if (a == ewmh->_NET_WM_STATE_ABOVE) {
+ if (csq->layer == NULL) {
+ csq->layer = calloc(1, sizeof(stack_layer_t));
+ }
+ *(csq->layer) = LAYER_ABOVE;
+ } else if (a == ewmh->_NET_WM_STATE_STICKY) {
+ csq->sticky = true;
+ }
+ }
+ xcb_ewmh_get_atoms_reply_wipe(&win_state);
+ }
+
+ xcb_window_t transient_for = XCB_NONE;
+ xcb_icccm_get_wm_transient_for_reply(dpy, xcb_icccm_get_wm_transient_for(dpy, win), &transient_for, NULL);
+ if (transient_for != XCB_NONE) {
+ if (csq->state == NULL) {
+ csq->state = calloc(1, sizeof(client_state_t));
+ }
+ *(csq->state) = STATE_FLOATING;
+ }
+
+ xcb_size_hints_t size_hints;
+ if (xcb_icccm_get_wm_normal_hints_reply(dpy, xcb_icccm_get_wm_normal_hints(dpy, win), &size_hints, NULL) == 1) {
+ if ((size_hints.flags & (XCB_ICCCM_SIZE_HINT_P_MIN_SIZE|XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)) &&
+ size_hints.min_width == size_hints.max_width && size_hints.min_height == size_hints.max_height) {
+ if (csq->state == NULL) {
+ csq->state = calloc(1, sizeof(client_state_t));
+ }
+ *(csq->state) = STATE_FLOATING;
+ }
+ }
+
+ xcb_icccm_get_wm_class_reply_t reply;
+ if (xcb_icccm_get_wm_class_reply(dpy, xcb_icccm_get_wm_class(dpy, win), &reply, NULL) == 1) {
+ snprintf(csq->class_name, sizeof(csq->class_name), "%s", reply.class_name);
+ snprintf(csq->instance_name, sizeof(csq->instance_name), "%s", reply.instance_name);
+ xcb_icccm_get_wm_class_reply_wipe(&reply);
+ }
+
+ rule_t *rule = rule_head;
+ while (rule != NULL) {
+ rule_t *next = rule->next;
+ if ((streq(rule->class_name, MATCH_ANY) || streq(rule->class_name, csq->class_name)) &&
+ (streq(rule->instance_name, MATCH_ANY) || streq(rule->instance_name, csq->instance_name))) {
+ char effect[MAXLEN];
+ snprintf(effect, sizeof(effect), "%s", rule->effect);
+ char *key = strtok(effect, CSQ_BLK);
+ char *value = strtok(NULL, CSQ_BLK);
+ while (key != NULL && value != NULL) {
+ parse_key_value(key, value, csq);
+ key = strtok(NULL, CSQ_BLK);
+ value = strtok(NULL, CSQ_BLK);
+ }
+ if (rule->one_shot) {
+ remove_rule(rule);
+ break;
+ }
+ }
+ rule = next;
+ }
+}
+
+bool schedule_rules(xcb_window_t win, rule_consequence_t *csq)
+{
+ if (external_rules_command[0] == '\0') {
+ return false;
+ }
+ int fds[2];
+ if (pipe(fds) == -1) {
+ return false;
+ }
+ pid_t pid = fork();
+ if (pid == 0) {
+ if (dpy != NULL) {
+ close(xcb_get_file_descriptor(dpy));
+ }
+ dup2(fds[1], 1);
+ close(fds[0]);
+ char wid[SMALEN];
+ snprintf(wid, sizeof(wid), "%i", win);
+ setsid();
+ execl(external_rules_command, external_rules_command, wid, csq->class_name, csq->instance_name, csq->monitor_desc, csq->desktop_desc, csq->node_desc, NULL);
+ err("Couldn't spawn rule command.\n");
+ } else if (pid > 0) {
+ close(fds[1]);
+ pending_rule_t *pr = make_pending_rule(fds[0], win, csq);
+ add_pending_rule(pr);
+ }
+ return (pid != -1);
+}
+
+void parse_rule_consequence(int fd, rule_consequence_t *csq)
+{
+ if (fd == -1) {
+ return;
+ }
+ char data[BUFSIZ];
+ int nb;
+ while ((nb = read(fd, data, sizeof(data))) > 0) {
+ int end = MIN(nb, (int) sizeof(data) - 1);
+ data[end] = '\0';
+ char *key = strtok(data, CSQ_BLK);
+ char *value = strtok(NULL, CSQ_BLK);
+ while (key != NULL && value != NULL) {
+ parse_key_value(key, value, csq);
+ key = strtok(NULL, CSQ_BLK);
+ value = strtok(NULL, CSQ_BLK);
+ }
+ }
+}
+
+void parse_key_value(char *key, char *value, rule_consequence_t *csq)
+{
+ bool v;
+ if (streq("monitor", key)) {
+ snprintf(csq->monitor_desc, sizeof(csq->monitor_desc), "%s", value);
+ } else if (streq("desktop", key)) {
+ snprintf(csq->desktop_desc, sizeof(csq->desktop_desc), "%s", value);
+ } else if (streq("node", key)) {
+ snprintf(csq->node_desc, sizeof(csq->node_desc), "%s", value);
+ } else if (streq("split_dir", key)) {
+ snprintf(csq->split_dir, sizeof(csq->split_dir), "%s", value);
+ } else if (streq("state", key)) {
+ client_state_t cst;
+ if (parse_client_state(value, &cst)) {
+ if (csq->state == NULL) {
+ csq->state = calloc(1, sizeof(client_state_t));
+ }
+ *(csq->state) = cst;
+ }
+ } else if (streq("layer", key)) {
+ stack_layer_t lyr;
+ if (parse_stack_layer(value, &lyr)) {
+ if (csq->layer == NULL) {
+ csq->layer = calloc(1, sizeof(stack_layer_t));
+ }
+ *(csq->layer) = lyr;
+ }
+ } else if (streq("split_ratio", key)) {
+ double rat;
+ if (sscanf(value, "%lf", &rat) == 1 && rat > 0 && rat < 1) {
+ csq->split_ratio = rat;
+ }
+ } else if (parse_bool(value, &v)) {
+ if (streq("hidden", key))
+ csq->hidden = true;
+#define SETCSQ(name) \
+ else if (streq(#name, key)) \
+ csq->name = v;
+ SETCSQ(sticky)
+ SETCSQ(private)
+ SETCSQ(locked)
+ SETCSQ(center)
+ SETCSQ(follow)
+ SETCSQ(manage)
+ SETCSQ(focus)
+ SETCSQ(border)
+#undef SETCSQ
+ }
+}
+
+void list_rules(FILE *rsp)
+{
+ for (rule_t *r = rule_head; r != NULL; r = r->next) {
+ fprintf(rsp, "%s:%s %c> %s\n", r->class_name, r->instance_name, r->one_shot?'-':'=', r->effect);
+ }
+}
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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.
+ */
+
+#ifndef BSPWM_RULE_H
+#define BSPWM_RULE_H
+
+#define MATCH_ANY "*"
+#define CSQ_BLK " =,\n"
+
+rule_t *make_rule(void);
+void add_rule(rule_t *r);
+void remove_rule(rule_t *r);
+void remove_rule_by_cause(char *cause);
+bool remove_rule_by_index(int idx);
+rule_consequence_t *make_rule_conquence(void);
+pending_rule_t *make_pending_rule(int fd, xcb_window_t win, rule_consequence_t *csq);
+void add_pending_rule(pending_rule_t *pr);
+void remove_pending_rule(pending_rule_t *pr);
+void apply_rules(xcb_window_t win, rule_consequence_t *csq);
+bool schedule_rules(xcb_window_t win, rule_consequence_t *csq);
+void parse_rule_consequence(int fd, rule_consequence_t *csq);
+void parse_key_value(char *key, char *value, rule_consequence_t *csq);
+void list_rules(FILE *rsp);
+
+#endif
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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 <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include "bspwm.h"
+#include "settings.h"
+
+void run_config(void)
+{
+ if (fork() == 0) {
+ if (dpy != NULL) {
+ close(xcb_get_file_descriptor(dpy));
+ }
+ setsid();
+ execl(config_path, config_path, NULL);
+ err("Couldn't execute the configuration file.\n");
+ }
+}
+
+void load_settings(void)
+{
+ snprintf(external_rules_command, sizeof(external_rules_command), "%s", EXTERNAL_RULES_COMMAND);
+ snprintf(status_prefix, sizeof(status_prefix), "%s", STATUS_PREFIX);
+
+ snprintf(normal_border_color, sizeof(normal_border_color), "%s", NORMAL_BORDER_COLOR);
+ snprintf(active_border_color, sizeof(active_border_color), "%s", ACTIVE_BORDER_COLOR);
+ snprintf(focused_border_color, sizeof(focused_border_color), "%s", FOCUSED_BORDER_COLOR);
+ snprintf(presel_feedback_color, sizeof(presel_feedback_color), "%s", PRESEL_FEEDBACK_COLOR);
+
+ padding = (padding_t) PADDING;
+ window_gap = WINDOW_GAP;
+ border_width = BORDER_WIDTH;
+ split_ratio = SPLIT_RATIO;
+ initial_polarity = FIRST_CHILD;
+ pointer_modifier = POINTER_MODIFIER;
+ pointer_motion_interval = POINTER_MOTION_INTERVAL;
+
+ pointer_actions[0] = ACTION_MOVE;
+ pointer_actions[1] = ACTION_RESIZE_SIDE;
+ pointer_actions[2] = ACTION_RESIZE_CORNER;
+
+ borderless_monocle = BORDERLESS_MONOCLE;
+ gapless_monocle = GAPLESS_MONOCLE;
+ paddingless_monocle = PADDINGLESS_MONOCLE;
+ single_monocle = SINGLE_MONOCLE;
+ focus_follows_pointer = FOCUS_FOLLOWS_POINTER;
+ pointer_follows_focus = POINTER_FOLLOWS_FOCUS;
+ pointer_follows_monitor = POINTER_FOLLOWS_MONITOR;
+ ignore_ewmh_focus = IGNORE_EWMH_FOCUS;
+ center_pseudo_tiled = CENTER_PSEUDO_TILED;
+ click_to_focus = CLICK_TO_FOCUS;
+ swallow_first_click = SWALLOW_FIRST_CLICK;
+ honor_size_hints = HONOR_SIZE_HINTS;
+ remove_disabled_monitors = REMOVE_DISABLED_MONITORS;
+ remove_unplugged_monitors = REMOVE_UNPLUGGED_MONITORS;
+ merge_overlapping_monitors = MERGE_OVERLAPPING_MONITORS;
+}
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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.
+ */
+
+#ifndef BSPWM_SETTINGS_H
+#define BSPWM_SETTINGS_H
+
+#include "types.h"
+
+#define WM_NAME "bspwm"
+#define CONFIG_NAME WM_NAME "rc"
+#define CONFIG_HOME_ENV "XDG_CONFIG_HOME"
+#define POINTER_MODIFIER XCB_MOD_MASK_4
+#define POINTER_MOTION_INTERVAL 17
+#define EXTERNAL_RULES_COMMAND ""
+#define STATUS_PREFIX "W"
+
+#define NORMAL_BORDER_COLOR "#30302f"
+#define ACTIVE_BORDER_COLOR "#474645"
+#define FOCUSED_BORDER_COLOR "#817f7f"
+#define PRESEL_FEEDBACK_COLOR "#f4d775"
+
+#define PADDING {0, 0, 0, 0}
+#define WINDOW_GAP 6
+#define BORDER_WIDTH 1
+#define SPLIT_RATIO 0.5
+
+#define BORDERLESS_MONOCLE false
+#define GAPLESS_MONOCLE false
+#define PADDINGLESS_MONOCLE false
+#define SINGLE_MONOCLE false
+#define FOCUS_FOLLOWS_POINTER false
+#define POINTER_FOLLOWS_FOCUS false
+#define POINTER_FOLLOWS_MONITOR false
+#define IGNORE_EWMH_FOCUS false
+#define CENTER_PSEUDO_TILED true
+#define CLICK_TO_FOCUS false
+#define SWALLOW_FIRST_CLICK false
+#define HONOR_SIZE_HINTS false
+#define REMOVE_DISABLED_MONITORS false
+#define REMOVE_UNPLUGGED_MONITORS false
+#define MERGE_OVERLAPPING_MONITORS false
+
+char external_rules_command[MAXLEN];
+char status_prefix[MAXLEN];
+
+char normal_border_color[MAXLEN];
+char active_border_color[MAXLEN];
+char focused_border_color[MAXLEN];
+char presel_feedback_color[MAXLEN];
+
+padding_t padding;
+int window_gap;
+unsigned int border_width;
+double split_ratio;
+
+child_polarity_t initial_polarity;
+uint16_t pointer_modifier;
+uint32_t pointer_motion_interval;
+pointer_action_t pointer_actions[3];
+
+bool borderless_monocle;
+bool gapless_monocle;
+bool paddingless_monocle;
+bool single_monocle;
+bool focus_follows_pointer;
+bool pointer_follows_focus;
+bool pointer_follows_monitor;
+bool ignore_ewmh_focus;
+bool center_pseudo_tiled;
+bool click_to_focus;
+bool swallow_first_click;
+bool honor_size_hints;
+bool remove_disabled_monitors;
+bool remove_unplugged_monitors;
+bool merge_overlapping_monitors;
+
+void run_config(void);
+void load_settings(void);
+
+#endif
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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 <stdlib.h>
+#include "bspwm.h"
+#include "window.h"
+#include "subscribe.h"
+#include "ewmh.h"
+#include "tree.h"
+#include "stack.h"
+
+stacking_list_t *make_stack(node_t *n)
+{
+ stacking_list_t *s = calloc(1, sizeof(stacking_list_t));
+ s->node = n;
+ s->prev = s->next = NULL;
+ return s;
+}
+
+void stack_insert_after(stacking_list_t *a, node_t *n)
+{
+ stacking_list_t *s = make_stack(n);
+ if (a == NULL) {
+ stack_head = stack_tail = s;
+ } else {
+ if (a->node == n) {
+ free(s);
+ return;
+ }
+ remove_stack_node(n);
+ stacking_list_t *b = a->next;
+ if (b != NULL) {
+ b->prev = s;
+ }
+ s->next = b;
+ s->prev = a;
+ a->next = s;
+ if (stack_tail == a) {
+ stack_tail = s;
+ }
+ }
+}
+
+void stack_insert_before(stacking_list_t *a, node_t *n)
+{
+ stacking_list_t *s = make_stack(n);
+ if (a == NULL) {
+ stack_head = stack_tail = s;
+ } else {
+ if (a->node == n) {
+ free(s);
+ return;
+ }
+ remove_stack_node(n);
+ stacking_list_t *b = a->prev;
+ if (b != NULL) {
+ b->next = s;
+ }
+ s->prev = b;
+ s->next = a;
+ a->prev = s;
+ if (stack_head == a) {
+ stack_head = s;
+ }
+ }
+}
+
+void remove_stack(stacking_list_t *s)
+{
+ if (s == NULL) {
+ return;
+ }
+ stacking_list_t *a = s->prev;
+ stacking_list_t *b = s->next;
+ if (a != NULL) {
+ a->next = b;
+ }
+ if (b != NULL) {
+ b->prev = a;
+ }
+ if (s == stack_head) {
+ stack_head = b;
+ }
+ if (s == stack_tail) {
+ stack_tail = a;
+ }
+ free(s);
+}
+
+void remove_stack_node(node_t *n)
+{
+ for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+ for (stacking_list_t *s = stack_head; s != NULL; s = s->next) {
+ if (s->node == f) {
+ remove_stack(s);
+ break;
+ }
+ }
+ }
+}
+
+int stack_level(client_t *c)
+{
+ int layer_level = (c->layer == LAYER_NORMAL ? 1 : (c->layer == LAYER_BELOW ? 0 : 2));
+ int state_level = (IS_TILED(c) ? 0 : (IS_FLOATING(c) ? 2 : 1));
+ return 3 * layer_level + state_level;
+}
+
+int stack_cmp(client_t *c1, client_t *c2)
+{
+ return stack_level(c1) - stack_level(c2);
+}
+
+stacking_list_t *limit_above(node_t *n)
+{
+ stacking_list_t *s = stack_head;
+ while (s != NULL && stack_cmp(n->client, s->node->client) >= 0) {
+ s = s->next;
+ }
+ if (s == NULL) {
+ s = stack_tail;
+ }
+ if (s->node == n) {
+ s = s->prev;
+ }
+ return s;
+}
+
+stacking_list_t *limit_below(node_t *n)
+{
+ stacking_list_t *s = stack_tail;
+ while (s != NULL && stack_cmp(n->client, s->node->client) <= 0) {
+ s = s->prev;
+ }
+ if (s == NULL) {
+ s = stack_head;
+ }
+ if (s->node == n) {
+ s = s->next;
+ }
+ return s;
+}
+
+void stack(desktop_t *d, node_t *n, bool focused)
+{
+ for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+ if (f->client == NULL || (IS_FLOATING(f->client) && !auto_raise)) {
+ continue;
+ }
+
+ if (stack_head == NULL) {
+ stack_insert_after(NULL, f);
+ } else {
+ stacking_list_t *s = (focused ? limit_above(f) : limit_below(f));
+ if (s == NULL) {
+ continue;
+ }
+ int i = stack_cmp(f->client, s->node->client);
+ if (i < 0 || (i == 0 && !focused)) {
+ stack_insert_before(s, f);
+ window_below(f->id, s->node->id);
+ put_status(SBSC_MASK_NODE_STACK, "node_stack 0x%08X below 0x%08X\n", f->id, s->node->id);
+ } else {
+ stack_insert_after(s, f);
+ window_above(f->id, s->node->id);
+ put_status(SBSC_MASK_NODE_STACK, "node_stack 0x%08X above 0x%08X\n", f->id, s->node->id);
+ }
+ }
+ }
+
+ ewmh_update_client_list(true);
+ restack_presel_feedbacks(d);
+}
+
+void restack_presel_feedbacks(desktop_t *d)
+{
+ stacking_list_t *s = stack_tail;
+ while (s != NULL && !IS_TILED(s->node->client)) {
+ s = s->prev;
+ }
+ if (s != NULL) {
+ restack_presel_feedbacks_in(d->root, s->node);
+ }
+}
+
+void restack_presel_feedbacks_in(node_t *r, node_t *n)
+{
+ if (r == NULL) {
+ return;
+ } else {
+ if (r->presel != NULL) {
+ window_above(r->presel->feedback, n->id);
+ }
+ restack_presel_feedbacks_in(r->first_child, n);
+ restack_presel_feedbacks_in(r->second_child, n);
+ }
+}
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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.
+ */
+
+#ifndef BSPWM_STACK_H
+#define BSPWM_STACK_H
+
+stacking_list_t *make_stack(node_t *n);
+void stack_insert_after(stacking_list_t *a, node_t *n);
+void stack_insert_before(stacking_list_t *a, node_t *n);
+void remove_stack(stacking_list_t *s);
+void remove_stack_node(node_t *n);
+int stack_level(client_t *c);
+int stack_cmp(client_t *c1, client_t *c2);
+stacking_list_t *limit_above(node_t *n);
+stacking_list_t *limit_below(node_t *n);
+void stack(desktop_t *d, node_t *n, bool focused);
+void restack_presel_feedbacks(desktop_t *d);
+void restack_presel_feedbacks_in(node_t *r, node_t *n);
+
+#endif
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include "bspwm.h"
+#include "desktop.h"
+#include "settings.h"
+#include "subscribe.h"
+
+subscriber_list_t *make_subscriber_list(FILE *stream, int field)
+{
+ subscriber_list_t *sb = calloc(1, sizeof(subscriber_list_t));
+ sb->prev = sb->next = NULL;
+ sb->stream = stream;
+ sb->field = field;
+ return sb;
+}
+
+void remove_subscriber(subscriber_list_t *sb)
+{
+ if (sb == NULL) {
+ return;
+ }
+ subscriber_list_t *a = sb->prev;
+ subscriber_list_t *b = sb->next;
+ if (a != NULL) {
+ a->next = b;
+ }
+ if (b != NULL) {
+ b->prev = a;
+ }
+ if (sb == subscribe_head) {
+ subscribe_head = b;
+ }
+ if (sb == subscribe_tail) {
+ subscribe_tail = a;
+ }
+ fclose(sb->stream);
+ free(sb);
+}
+
+void add_subscriber(FILE *stream, int field)
+{
+ subscriber_list_t *sb = make_subscriber_list(stream, field);
+ if (subscribe_head == NULL) {
+ subscribe_head = subscribe_tail = sb;
+ } else {
+ subscribe_tail->next = sb;
+ sb->prev = subscribe_tail;
+ subscribe_tail = sb;
+ }
+ if (sb->field & SBSC_MASK_REPORT) {
+ print_report(sb->stream);
+ }
+}
+
+int print_report(FILE *stream)
+{
+ fprintf(stream, "%s", status_prefix);
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ fprintf(stream, "%c%s", (mon == m ? 'M' : 'm'), m->name);
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+ char c = (is_urgent(d) ? 'u' : (d->root == NULL ? 'f' : 'o'));
+ if (m->desk == d) {
+ c = toupper(c);
+ }
+ fprintf(stream, ":%c%s", c, d->name);
+ }
+ if (m->desk != NULL) {
+ fprintf(stream, ":L%c", LAYOUT_CHR(m->desk->layout));
+ if (m->desk->focus != NULL) {
+ node_t *n = m->desk->focus;
+ if (n->client != NULL) {
+ fprintf(stream, ":T%c", STATE_CHR(n->client->state));
+ } else {
+ fprintf(stream, ":T@");
+ }
+ int i = 0;
+ char flags[4];
+ if (n->sticky) {
+ flags[i++] = 'S';
+ }
+ if (n->private) {
+ flags[i++] = 'P';
+ }
+ if (n->locked) {
+ flags[i++] = 'L';
+ }
+ flags[i] = '\0';
+ fprintf(stream, ":G%s", flags);
+ }
+ }
+ if (m != mon_tail) {
+ fprintf(stream, "%s", ":");
+ }
+ }
+ fprintf(stream, "%s", "\n");
+ return fflush(stream);
+}
+
+void put_status(subscriber_mask_t mask, ...)
+{
+ subscriber_list_t *sb = subscribe_head;
+ int ret;
+ while (sb != NULL) {
+ subscriber_list_t *next = sb->next;
+ if (sb->field & mask) {
+ if (mask == SBSC_MASK_REPORT) {
+ ret = print_report(sb->stream);
+ } else {
+ char *fmt;
+ va_list args;
+ va_start(args, mask);
+ fmt = va_arg(args, char *);
+ vfprintf(sb->stream, fmt, args);
+ va_end(args);
+ ret = fflush(sb->stream);
+ }
+ if (ret != 0) {
+ remove_subscriber(sb);
+ }
+ }
+ sb = next;
+ }
+}
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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.
+ */
+
+#ifndef BSPWM_SUBSCRIBE_H
+#define BSPWM_SUBSCRIBE_H
+
+typedef enum {
+ SBSC_MASK_REPORT = 1 << 0,
+ SBSC_MASK_MONITOR_ADD = 1 << 1,
+ SBSC_MASK_MONITOR_RENAME = 1 << 2,
+ SBSC_MASK_MONITOR_REMOVE = 1 << 3,
+ SBSC_MASK_MONITOR_SWAP = 1 << 4,
+ SBSC_MASK_MONITOR_FOCUS = 1 << 5,
+ SBSC_MASK_MONITOR_GEOMETRY = 1 << 6,
+ SBSC_MASK_DESKTOP_ADD = 1 << 7,
+ SBSC_MASK_DESKTOP_RENAME = 1 << 8,
+ SBSC_MASK_DESKTOP_REMOVE = 1 << 9,
+ SBSC_MASK_DESKTOP_SWAP = 1 << 10,
+ SBSC_MASK_DESKTOP_TRANSFER = 1 << 11,
+ SBSC_MASK_DESKTOP_FOCUS = 1 << 12,
+ SBSC_MASK_DESKTOP_ACTIVATE = 1 << 13,
+ SBSC_MASK_DESKTOP_LAYOUT = 1 << 14,
+ SBSC_MASK_NODE_MANAGE = 1 << 15,
+ SBSC_MASK_NODE_UNMANAGE = 1 << 16,
+ SBSC_MASK_NODE_SWAP = 1 << 17,
+ SBSC_MASK_NODE_TRANSFER = 1 << 18,
+ SBSC_MASK_NODE_FOCUS = 1 << 19,
+ SBSC_MASK_NODE_PRESEL = 1 << 20,
+ SBSC_MASK_NODE_STACK = 1 << 21,
+ SBSC_MASK_NODE_ACTIVATE = 1 << 22,
+ SBSC_MASK_NODE_GEOMETRY = 1 << 23,
+ SBSC_MASK_NODE_STATE = 1 << 24,
+ SBSC_MASK_NODE_FLAG = 1 << 25,
+ SBSC_MASK_NODE_LAYER = 1 << 26,
+ SBSC_MASK_POINTER_ACTION = 1 << 27,
+ SBSC_MASK_MONITOR = (1 << 7) - (1 << 1),
+ SBSC_MASK_DESKTOP = (1 << 15) - (1 << 7),
+ SBSC_MASK_NODE = (1 << 28) - (1 << 15),
+ SBSC_MASK_ALL = (1 << 28) - 1
+} subscriber_mask_t;
+
+subscriber_list_t *make_subscriber_list(FILE *stream, int field);
+void remove_subscriber(subscriber_list_t *sb);
+void add_subscriber(FILE *stream, int field);
+int print_report(FILE *stream);
+void put_status(subscriber_mask_t mask, ...);
+
+#endif
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include "bspwm.h"
+#include "desktop.h"
+#include "ewmh.h"
+#include "history.h"
+#include "monitor.h"
+#include "query.h"
+#include "geometry.h"
+#include "subscribe.h"
+#include "settings.h"
+#include "pointer.h"
+#include "stack.h"
+#include "window.h"
+#include "tree.h"
+
+void arrange(monitor_t *m, desktop_t *d)
+{
+ if (d->root == NULL) {
+ return;
+ }
+
+ layout_t l = d->layout;
+
+ if (single_monocle && tiled_count(d->root) <= 1) {
+ l = LAYOUT_MONOCLE;
+ }
+
+ xcb_rectangle_t rect = m->rectangle;
+
+ if (!paddingless_monocle || l != LAYOUT_MONOCLE) {
+ rect.x += m->padding.left + d->padding.left;
+ rect.y += m->padding.top + d->padding.top;
+ rect.width -= m->padding.left + d->padding.left + d->padding.right + m->padding.right;
+ rect.height -= m->padding.top + d->padding.top + d->padding.bottom + m->padding.bottom;
+ }
+
+ if (!gapless_monocle || l != LAYOUT_MONOCLE) {
+ rect.x += d->window_gap;
+ rect.y += d->window_gap;
+ rect.width -= d->window_gap;
+ rect.height -= d->window_gap;
+ }
+
+ if (focus_follows_pointer) {
+ listen_enter_notify(d->root, false);
+ }
+
+ apply_layout(m, d, d->root, l, rect, rect);
+
+ if (focus_follows_pointer) {
+ listen_enter_notify(d->root, true);
+ }
+}
+
+void apply_layout(monitor_t *m, desktop_t *d, node_t *n, layout_t l, xcb_rectangle_t rect, xcb_rectangle_t root_rect)
+{
+ if (n == NULL) {
+ return;
+ }
+
+ n->rectangle = rect;
+
+ if (pointer_follows_focus && mon->desk->focus == n) {
+ xcb_rectangle_t r = rect;
+ r.width -= d->window_gap;
+ r.height -= d->window_gap;
+ center_pointer(r);
+ }
+
+ if (n->presel != NULL) {
+ draw_presel_feedback(m, d, n);
+ }
+
+ if (is_leaf(n)) {
+
+ if (n->client == NULL) {
+ return;
+ }
+
+ unsigned int bw;
+ if ((borderless_monocle && n->client->state == STATE_TILED && l == LAYOUT_MONOCLE)
+ || n->client->state == STATE_FULLSCREEN) {
+ bw = 0;
+ } else {
+ bw = n->client->border_width;
+ }
+
+ xcb_rectangle_t r;
+ xcb_rectangle_t cr = get_window_rectangle(n);
+ client_state_t s = n->client->state;
+ if (s == STATE_TILED || s == STATE_PSEUDO_TILED) {
+ int wg = (gapless_monocle && l == LAYOUT_MONOCLE ? 0 : d->window_gap);
+ /* tiled clients */
+ if (s == STATE_TILED) {
+ r = rect;
+ int bleed = wg + 2 * bw;
+ r.width = (bleed < r.width ? r.width - bleed : 1);
+ r.height = (bleed < r.height ? r.height - bleed : 1);
+ /* pseudo-tiled clients */
+ } else {
+ r = n->client->floating_rectangle;
+ if (center_pseudo_tiled) {
+ r.x = rect.x - bw + (rect.width - wg - r.width) / 2;
+ r.y = rect.y - bw + (rect.height - wg - r.height) / 2;
+ } else {
+ r.x = rect.x;
+ r.y = rect.y;
+ }
+ }
+ n->client->tiled_rectangle = r;
+ /* floating clients */
+ } else if (s == STATE_FLOATING) {
+ r = n->client->floating_rectangle;
+ /* fullscreen clients */
+ } else {
+ r = m->rectangle;
+ n->client->tiled_rectangle = r;
+ }
+
+ apply_size_hints(n->client, &r.width, &r.height);
+
+ if (!rect_eq(r, cr)) {
+ window_move_resize(n->id, r.x, r.y, r.width, r.height);
+ if (!grabbing) {
+ put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", m->id, d->id, n->id, r.width, r.height, r.x, r.y);
+ }
+ }
+
+ window_border_width(n->id, bw);
+
+ } else {
+ xcb_rectangle_t first_rect;
+ xcb_rectangle_t second_rect;
+
+ if (l == LAYOUT_MONOCLE || n->first_child->vacant || n->second_child->vacant) {
+ first_rect = second_rect = rect;
+ } else {
+ unsigned int fence;
+ if (n->split_type == TYPE_VERTICAL) {
+ fence = rect.width * n->split_ratio;
+ first_rect = (xcb_rectangle_t) {rect.x, rect.y, fence, rect.height};
+ second_rect = (xcb_rectangle_t) {rect.x + fence, rect.y, rect.width - fence, rect.height};
+ } else {
+ fence = rect.height * n->split_ratio;
+ first_rect = (xcb_rectangle_t) {rect.x, rect.y, rect.width, fence};
+ second_rect = (xcb_rectangle_t) {rect.x, rect.y + fence, rect.width, rect.height - fence};
+ }
+ }
+
+ apply_layout(m, d, n->first_child, l, first_rect, root_rect);
+ apply_layout(m, d, n->second_child, l, second_rect, root_rect);
+ }
+}
+
+presel_t *make_presel(void)
+{
+ presel_t *p = calloc(1, sizeof(presel_t));
+ p->split_dir = DIR_EAST;
+ p->split_ratio = split_ratio;
+ p->feedback = XCB_NONE;
+ return p;
+}
+
+void set_ratio(node_t *n, double rat)
+{
+ if (n == NULL) {
+ return;
+ }
+
+ n->split_ratio = rat;
+}
+
+void presel_dir(monitor_t *m, desktop_t *d, node_t *n, direction_t dir)
+{
+ if (n->presel == NULL) {
+ n->presel = make_presel();
+ }
+
+ n->presel->split_dir = dir;
+
+ put_status(SBSC_MASK_NODE_PRESEL, "node_presel 0x%08X 0x%08X 0x%08X dir %s\n", m->id, d->id, n->id, SPLIT_DIR_STR(dir));
+}
+
+void presel_ratio(monitor_t *m, desktop_t *d, node_t *n, double ratio)
+{
+ if (n->presel == NULL) {
+ n->presel = make_presel();
+ }
+
+ n->presel->split_ratio = ratio;
+
+ put_status(SBSC_MASK_NODE_PRESEL, "node_presel 0x%08X 0x%08X 0x%08X ratio %lf\n", m->id, d->id, n->id, ratio);
+}
+
+void cancel_presel(monitor_t *m, desktop_t *d, node_t *n)
+{
+ if (n->presel == NULL) {
+ return;
+ }
+
+ if (focus_follows_pointer) {
+ listen_enter_notify(n, false);
+ }
+
+ if (n->presel->feedback != XCB_NONE) {
+ xcb_destroy_window(dpy, n->presel->feedback);
+ }
+
+ if (focus_follows_pointer) {
+ listen_enter_notify(n, true);
+ }
+
+ free(n->presel);
+ n->presel = NULL;
+
+ put_status(SBSC_MASK_NODE_PRESEL, "node_presel 0x%08X 0x%08X 0x%08X cancel\n", m->id, d->id, n->id);
+}
+
+void cancel_presel_in(monitor_t *m, desktop_t *d, node_t *n)
+{
+ if (n == NULL) {
+ return;
+ }
+ cancel_presel(m, d, n);
+ cancel_presel_in(m, d, n->first_child);
+ cancel_presel_in(m, d, n->second_child);
+}
+
+node_t *find_public(desktop_t *d)
+{
+ unsigned int b_manual_area = 0;
+ unsigned int b_automatic_area = 0;
+ node_t *b_manual = NULL;
+ node_t *b_automatic = NULL;
+ for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
+ if (n->vacant) {
+ continue;
+ }
+ unsigned int n_area = node_area(d, n);
+ if (n_area > b_manual_area && (n->presel != NULL || !n->private)) {
+ b_manual = n;
+ b_manual_area = n_area;
+ }
+ if (n_area > b_automatic_area &&
+ n->presel == NULL && !n->private && private_count(n->parent) == 0) {
+ b_automatic = n;
+ b_automatic_area = n_area;
+ }
+ }
+ if (b_automatic != NULL) {
+ return b_automatic;
+ } else {
+ return b_manual;
+ }
+}
+
+node_t *insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f)
+{
+ if (d == NULL || n == NULL) {
+ return NULL;
+ }
+
+ /* n: inserted node */
+ /* c: new internal node */
+ /* f: focus or insertion anchor */
+ /* p: parent of focus */
+ /* g: grand parent of focus */
+
+ if (f == NULL) {
+ f = d->root;
+ }
+
+ if (f == NULL) {
+ d->root = n;
+ } else if (IS_RECEPTACLE(f) && f->presel == NULL) {
+ node_t *p = f->parent;
+ if (p != NULL) {
+ if (is_first_child(f)) {
+ p->first_child = n;
+ } else {
+ p->second_child = n;
+ }
+ } else {
+ d->root = n;
+ }
+ n->parent = p;
+ free(f);
+ f = NULL;
+ } else {
+ node_t *c = make_node(XCB_NONE);
+ node_t *p = f->parent;
+ if (f->presel == NULL && (f->private || private_count(f->parent) > 0)) {
+ node_t *k = find_public(d);
+ if (k != NULL) {
+ f = k;
+ p = f->parent;
+ }
+ if (f->presel == NULL && (f->private || private_count(f->parent) > 0)) {
+ xcb_rectangle_t rect = get_rectangle(d, f);
+ presel_dir(m, d, f, (rect.width >= rect.height ? DIR_EAST : DIR_SOUTH));
+ }
+ }
+ if (f->presel == NULL && p != NULL && p->vacant) {
+ f = p;
+ p = f->parent;
+ }
+ n->parent = c;
+ c->birth_rotation = f->birth_rotation;
+ if (f->presel == NULL) {
+ if (p == NULL) {
+ if (initial_polarity == FIRST_CHILD) {
+ c->first_child = n;
+ c->second_child = f;
+ } else {
+ c->first_child = f;
+ c->second_child = n;
+ }
+ if (m->rectangle.width > m->rectangle.height) {
+ c->split_type = TYPE_VERTICAL;
+ } else {
+ c->split_type = TYPE_HORIZONTAL;
+ }
+ f->parent = c;
+ d->root = c;
+ } else {
+ node_t *g = p->parent;
+ c->parent = g;
+ if (g != NULL) {
+ if (is_first_child(p)) {
+ g->first_child = c;
+ } else {
+ g->second_child = c;
+ }
+ } else {
+ d->root = c;
+ }
+ c->split_type = p->split_type;
+ c->split_ratio = p->split_ratio;
+ p->parent = c;
+ int rot;
+ if (is_first_child(f)) {
+ c->first_child = n;
+ c->second_child = p;
+ rot = 90;
+ } else {
+ c->first_child = p;
+ c->second_child = n;
+ rot = 270;
+ }
+ n->birth_rotation = rot;
+ if (!n->vacant) {
+ rotate_tree(p, rot);
+ }
+ }
+ } else {
+ if (p != NULL) {
+ if (is_first_child(f)) {
+ p->first_child = c;
+ } else {
+ p->second_child = c;
+ }
+ }
+ c->split_ratio = f->presel->split_ratio;
+ c->parent = p;
+ f->parent = c;
+ f->birth_rotation = 0;
+ switch (f->presel->split_dir) {
+ case DIR_WEST:
+ c->split_type = TYPE_VERTICAL;
+ c->first_child = n;
+ c->second_child = f;
+ break;
+ case DIR_EAST:
+ c->split_type = TYPE_VERTICAL;
+ c->first_child = f;
+ c->second_child = n;
+ break;
+ case DIR_NORTH:
+ c->split_type = TYPE_HORIZONTAL;
+ c->first_child = n;
+ c->second_child = f;
+ break;
+ case DIR_SOUTH:
+ c->split_type = TYPE_HORIZONTAL;
+ c->first_child = f;
+ c->second_child = n;
+ break;
+ }
+ if (d->root == f) {
+ d->root = c;
+ }
+ cancel_presel(m, d, f);
+ }
+ }
+
+ propagate_flags_upward(m, d, n);
+
+ if (d->focus == NULL && is_focusable(n)) {
+ d->focus = n;
+ }
+
+ return f;
+}
+
+void insert_receptacle(monitor_t *m, desktop_t *d, node_t *n)
+{
+ node_t *r = make_node(XCB_NONE);
+ insert_node(m, d, r, n);
+}
+
+bool activate_node(monitor_t *m, desktop_t *d, node_t *n)
+{
+ if (d == NULL) {
+ d = history_last_desktop(m, NULL);
+ if (d == NULL) {
+ d = m->desk_head;
+ }
+ }
+
+ if (d == NULL) {
+ return false;
+ }
+
+ if (n == NULL && d->root != NULL) {
+ n = history_last_node(d, NULL);
+ if (n == NULL) {
+ n = first_focusable_leaf(d->root);
+ }
+ }
+
+ if (d == mon->desk || (n != NULL && !is_focusable(n))) {
+ return false;
+ }
+
+ if (n != NULL) {
+ if (d->focus != NULL && n != d->focus) {
+ neutralize_occluding_windows(m, d, n);
+ }
+ stack(d, n, true);
+ if (d->focus != n) {
+ for (node_t *f = first_extrema(d->focus); f != NULL; f = next_leaf(f, d->focus)) {
+ if (f->client != NULL && !is_descendant(f, n)) {
+ window_draw_border(f->id, get_border_color(false, (m == mon)));
+ }
+ }
+ }
+ draw_border(n, true, (m == mon));
+ }
+
+ activate_desktop(m, d);
+
+ d->focus = n;
+ history_add(m, d, n);
+
+ put_status(SBSC_MASK_REPORT);
+
+ if (n == NULL) {
+ return true;
+ }
+
+ put_status(SBSC_MASK_NODE_ACTIVATE, "node_activate 0x%08X 0x%08X 0x%08X\n", m->id, d->id, n->id);
+
+ return true;
+}
+
+void transfer_sticky_nodes(monitor_t *m, desktop_t *ds, desktop_t *dd, node_t *n)
+{
+ if (n == NULL) {
+ return;
+ } else if (n->sticky) {
+ transfer_node(m, ds, n, m, dd, dd->focus);
+ } else {
+ /* we need references to the children because n might be freed after
+ * the first recursive call */
+ node_t *first_child = n->first_child;
+ node_t *second_child = n->second_child;
+ transfer_sticky_nodes(m, ds, dd, first_child);
+ transfer_sticky_nodes(m, ds, dd, second_child);
+ }
+}
+
+bool focus_node(monitor_t *m, desktop_t *d, node_t *n)
+{
+ if (m == NULL) {
+ m = history_last_monitor(NULL);
+ if (m == NULL) {
+ m = mon_head;
+ }
+ }
+
+ if (m == NULL) {
+ return false;
+ }
+
+ if (d == NULL) {
+ d = history_last_desktop(m, NULL);
+ if (d == NULL) {
+ d = m->desk_head;
+ }
+ }
+
+ if (d == NULL) {
+ return false;
+ }
+
+ if (n == NULL && d->root != NULL) {
+ n = history_last_node(d, NULL);
+ if (n == NULL) {
+ n = first_focusable_leaf(d->root);
+ }
+ }
+
+ if (n != NULL && !is_focusable(n)) {
+ return false;
+ }
+
+ if ((mon != NULL && mon->desk != d) || n == NULL || n->client == NULL) {
+ clear_input_focus();
+ }
+
+ if (m->sticky_count > 0 && d != m->desk) {
+ sticky_still = false;
+ transfer_sticky_nodes(m, m->desk, d, m->desk->root);
+ sticky_still = true;
+ if (n == NULL && d->focus != NULL) {
+ n = d->focus;
+ }
+ }
+
+ if (d->focus != NULL && n != d->focus) {
+ neutralize_occluding_windows(m, d, n);
+ }
+
+ if (n != NULL && n->client != NULL && n->client->urgent) {
+ set_urgent(m, d, n, false);
+ }
+
+ if (mon != m) {
+ if (mon != NULL) {
+ for (desktop_t *e = mon->desk_head; e != NULL; e = e->next) {
+ draw_border(e->focus, true, false);
+ }
+ }
+ for (desktop_t *e = m->desk_head; e != NULL; e = e->next) {
+ if (e == d) {
+ continue;
+ }
+ draw_border(e->focus, true, true);
+ }
+ }
+
+ if (d->focus != n) {
+ for (node_t *f = first_extrema(d->focus); f != NULL; f = next_leaf(f, d->focus)) {
+ if (f->client != NULL && !is_descendant(f, n)) {
+ window_draw_border(f->id, get_border_color(false, true));
+ }
+ }
+ }
+
+ draw_border(n, true, true);
+
+ focus_desktop(m, d);
+
+ d->focus = n;
+ ewmh_update_active_window();
+ history_add(m, d, n);
+
+ put_status(SBSC_MASK_REPORT);
+
+ if (n == NULL) {
+ return true;
+ }
+
+ put_status(SBSC_MASK_NODE_FOCUS, "node_focus 0x%08X 0x%08X 0x%08X\n", m->id, d->id, n->id);
+
+ stack(d, n, true);
+ set_input_focus(n);
+
+ if (pointer_follows_focus) {
+ center_pointer(get_rectangle(d, n));
+ }
+
+ return true;
+}
+
+void hide_node(desktop_t *d, node_t *n)
+{
+ if (n == NULL) {
+ return;
+ } else {
+ if (!n->hidden) {
+ if (n->presel != NULL && d->layout != LAYOUT_MONOCLE) {
+ window_hide(n->presel->feedback);
+ }
+ if (n->client != NULL) {
+ window_hide(n->id);
+ }
+ }
+ if (n->client != NULL) {
+ n->client->shown = false;
+ }
+ hide_node(d, n->first_child);
+ hide_node(d, n->second_child);
+ }
+}
+
+void show_node(desktop_t *d, node_t *n)
+{
+ if (n == NULL) {
+ return;
+ } else {
+ if (!n->hidden) {
+ if (n->client != NULL) {
+ window_show(n->id);
+ }
+ if (n->presel != NULL && d->layout != LAYOUT_MONOCLE) {
+ window_show(n->presel->feedback);
+ }
+ }
+ if (n->client != NULL) {
+ n->client->shown = true;
+ }
+ show_node(d, n->first_child);
+ show_node(d, n->second_child);
+ }
+}
+
+node_t *make_node(uint32_t id)
+{
+ if (id == XCB_NONE) {
+ id = xcb_generate_id(dpy);
+ }
+ node_t *n = calloc(1, sizeof(node_t));
+ n->id = id;
+ n->parent = n->first_child = n->second_child = NULL;
+ n->vacant = n->hidden = n->sticky = n->private = n->locked = false;
+ n->split_ratio = split_ratio;
+ n->split_type = TYPE_VERTICAL;
+ n->birth_rotation = 0;
+ n->presel = NULL;
+ n->client = NULL;
+ return n;
+}
+
+client_t *make_client(void)
+{
+ client_t *c = calloc(1, sizeof(client_t));
+ c->state = c->last_state = STATE_TILED;
+ c->layer = c->last_layer = LAYER_NORMAL;
+ snprintf(c->class_name, sizeof(c->class_name), "%s", MISSING_VALUE);
+ snprintf(c->instance_name, sizeof(c->instance_name), "%s", MISSING_VALUE);
+ c->border_width = border_width;
+ c->urgent = false;
+ c->shown = false;
+ c->wm_flags = 0;
+ c->icccm_props.input_hint = true;
+ c->icccm_props.take_focus = false;
+ c->icccm_props.delete_window = false;
+ c->size_hints.flags = 0;
+ return c;
+}
+
+void initialize_client(node_t *n)
+{
+ xcb_window_t win = n->id;
+ client_t *c = n->client;
+ xcb_icccm_get_wm_protocols_reply_t protos;
+ if (xcb_icccm_get_wm_protocols_reply(dpy, xcb_icccm_get_wm_protocols(dpy, win, ewmh->WM_PROTOCOLS), &protos, NULL) == 1) {
+ for (uint32_t i = 0; i < protos.atoms_len; i++) {
+ if (protos.atoms[i] == WM_TAKE_FOCUS) {
+ c->icccm_props.take_focus = true;
+ } else if (protos.atoms[i] == WM_DELETE_WINDOW) {
+ c->icccm_props.delete_window = true;
+ }
+ }
+ xcb_icccm_get_wm_protocols_reply_wipe(&protos);
+ }
+ xcb_ewmh_get_atoms_reply_t wm_state;
+ if (xcb_ewmh_get_wm_state_reply(ewmh, xcb_ewmh_get_wm_state(ewmh, win), &wm_state, NULL) == 1) {
+ for (unsigned int i = 0; i < wm_state.atoms_len && i < MAX_WM_STATES; i++) {
+#define HANDLE_WM_STATE(s) \
+ if (wm_state.atoms[i] == ewmh->_NET_WM_STATE_##s) { \
+ c->wm_flags |= WM_FLAG_##s; continue; \
+ }
+ HANDLE_WM_STATE(MODAL)
+ HANDLE_WM_STATE(STICKY)
+ HANDLE_WM_STATE(MAXIMIZED_VERT)
+ HANDLE_WM_STATE(MAXIMIZED_HORZ)
+ HANDLE_WM_STATE(SHADED)
+ HANDLE_WM_STATE(SKIP_TASKBAR)
+ HANDLE_WM_STATE(SKIP_PAGER)
+ HANDLE_WM_STATE(HIDDEN)
+ HANDLE_WM_STATE(FULLSCREEN)
+ HANDLE_WM_STATE(ABOVE)
+ HANDLE_WM_STATE(BELOW)
+ HANDLE_WM_STATE(DEMANDS_ATTENTION)
+#undef HANDLE_WM_STATE
+ }
+ xcb_ewmh_get_atoms_reply_wipe(&wm_state);
+ }
+ xcb_icccm_wm_hints_t hints;
+ if (xcb_icccm_get_wm_hints_reply(dpy, xcb_icccm_get_wm_hints(dpy, win), &hints, NULL) == 1
+ && (hints.flags & XCB_ICCCM_WM_HINT_INPUT)) {
+ c->icccm_props.input_hint = hints.input;
+ }
+ xcb_icccm_get_wm_normal_hints_reply(dpy, xcb_icccm_get_wm_normal_hints(dpy, win), &c->size_hints, NULL);
+}
+
+bool is_focusable(node_t *n)
+{
+ for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+ if (f->client != NULL && !f->hidden) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool is_leaf(node_t *n)
+{
+ return (n != NULL && n->first_child == NULL && n->second_child == NULL);
+}
+
+bool is_first_child(node_t *n)
+{
+ return (n != NULL && n->parent != NULL && n->parent->first_child == n);
+}
+
+bool is_second_child(node_t *n)
+{
+ return (n != NULL && n->parent != NULL && n->parent->second_child == n);
+}
+
+unsigned int clients_count_in(node_t *n)
+{
+ if (n == NULL) {
+ return 0;
+ } else {
+ return (n->client != NULL ? 1 : 0) +
+ clients_count_in(n->first_child) +
+ clients_count_in(n->second_child);
+ }
+}
+
+node_t *brother_tree(node_t *n)
+{
+ if (n == NULL || n->parent == NULL) {
+ return NULL;
+ }
+ if (is_first_child(n)) {
+ return n->parent->second_child;
+ } else {
+ return n->parent->first_child;
+ }
+}
+
+node_t *first_extrema(node_t *n)
+{
+ if (n == NULL) {
+ return NULL;
+ } else if (n->first_child == NULL) {
+ return n;
+ } else {
+ return first_extrema(n->first_child);
+ }
+}
+
+node_t *second_extrema(node_t *n)
+{
+ if (n == NULL) {
+ return NULL;
+ } else if (n->second_child == NULL) {
+ return n;
+ } else {
+ return second_extrema(n->second_child);
+ }
+}
+
+node_t *first_focusable_leaf(node_t *n)
+{
+ for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+ if (f->client != NULL && !f->hidden) {
+ return f;
+ }
+ }
+ return NULL;
+}
+
+node_t *next_leaf(node_t *n, node_t *r)
+{
+ if (n == NULL) {
+ return NULL;
+ }
+ node_t *p = n;
+ while (is_second_child(p) && p != r) {
+ p = p->parent;
+ }
+ if (p == r) {
+ return NULL;
+ }
+ return first_extrema(p->parent->second_child);
+}
+
+node_t *prev_leaf(node_t *n, node_t *r)
+{
+ if (n == NULL) {
+ return NULL;
+ }
+ node_t *p = n;
+ while (is_first_child(p) && p != r) {
+ p = p->parent;
+ }
+ if (p == r) {
+ return NULL;
+ }
+ return second_extrema(p->parent->first_child);
+}
+
+node_t *next_tiled_leaf(node_t *n, node_t *r)
+{
+ node_t *next = next_leaf(n, r);
+ if (next == NULL || (next->client != NULL && !next->vacant)) {
+ return next;
+ } else {
+ return next_tiled_leaf(next, r);
+ }
+}
+
+node_t *prev_tiled_leaf(node_t *n, node_t *r)
+{
+ node_t *prev = prev_leaf(n, r);
+ if (prev == NULL || (prev->client != NULL && !prev->vacant)) {
+ return prev;
+ } else {
+ return prev_tiled_leaf(prev, r);
+ }
+}
+
+/* Returns true if *b* is adjacent to *a* in the direction *dir* */
+bool is_adjacent(node_t *a, node_t *b, direction_t dir)
+{
+ switch (dir) {
+ case DIR_EAST:
+ return (a->rectangle.x + a->rectangle.width) == b->rectangle.x;
+ break;
+ case DIR_SOUTH:
+ return (a->rectangle.y + a->rectangle.height) == b->rectangle.y;
+ break;
+ case DIR_WEST:
+ return (b->rectangle.x + b->rectangle.width) == a->rectangle.x;
+ break;
+ case DIR_NORTH:
+ return (b->rectangle.y + b->rectangle.height) == a->rectangle.y;
+ break;
+ }
+ return false;
+}
+
+node_t *find_fence(node_t *n, direction_t dir)
+{
+ node_t *p;
+
+ if (n == NULL) {
+ return NULL;
+ }
+
+ p = n->parent;
+
+ while (p != NULL) {
+ if ((dir == DIR_NORTH && p->split_type == TYPE_HORIZONTAL && p->rectangle.y < n->rectangle.y) ||
+ (dir == DIR_WEST && p->split_type == TYPE_VERTICAL && p->rectangle.x < n->rectangle.x) ||
+ (dir == DIR_SOUTH && p->split_type == TYPE_HORIZONTAL && (p->rectangle.y + p->rectangle.height) > (n->rectangle.y + n->rectangle.height)) ||
+ (dir == DIR_EAST && p->split_type == TYPE_VERTICAL && (p->rectangle.x + p->rectangle.width) > (n->rectangle.x + n->rectangle.width)))
+ return p;
+ p = p->parent;
+ }
+
+ return NULL;
+}
+
+/* returns *true* if *a* is a child of *b* */
+bool is_child(node_t *a, node_t *b)
+{
+ if (a == NULL || b == NULL) {
+ return false;
+ }
+ return (a->parent != NULL && a->parent == b);
+}
+
+/* returns *true* if *a* is a descendant of *b* */
+bool is_descendant(node_t *a, node_t *b)
+{
+ if (a == NULL || b == NULL) {
+ return false;
+ }
+ while (a != b && a != NULL) {
+ a = a->parent;
+ }
+ return a == b;
+}
+
+bool find_by_id(uint32_t id, coordinates_t *loc)
+{
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+ node_t *n = find_by_id_in(d->root, id);
+ if (n != NULL) {
+ loc->monitor = m;
+ loc->desktop = d;
+ loc->node = n;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+node_t *find_by_id_in(node_t *r, uint32_t id)
+{
+ if (r == NULL) {
+ return NULL;
+ } else if (r->id == id) {
+ return r;
+ } else {
+ node_t *f = find_by_id_in(r->first_child, id);
+ if (f != NULL) {
+ return f;
+ } else {
+ return find_by_id_in(r->second_child, id);
+ }
+ }
+}
+
+void find_nearest_neighbor(coordinates_t *ref, coordinates_t *dst, direction_t dir, node_select_t sel)
+{
+ if (ref->node == NULL) {
+ return;
+ }
+
+ xcb_rectangle_t rect = get_rectangle(ref->desktop, ref->node);
+ double md = DBL_MAX, mr = UINT32_MAX;
+
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ desktop_t *d = m->desk;
+ for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f, d->root)) {
+ coordinates_t loc = {m, d, f};
+ xcb_rectangle_t r = get_rectangle(d, f);
+ if (f == ref->node ||
+ f->client == NULL ||
+ f->hidden ||
+ is_descendant(f, ref->node) ||
+ !node_matches(&loc, ref, sel) ||
+ !on_dir_side(rect, r, dir)) {
+ continue;
+ }
+ double fd = distance_center(rect, r);
+ uint32_t fr = history_rank(f);
+ if (fd < md || (fd == md && fr < mr)) {
+ md = fd;
+ mr = fr;
+ *dst = loc;
+ }
+ }
+ }
+}
+
+unsigned int node_area(desktop_t *d, node_t *n)
+{
+ if (n == NULL) {
+ return 0;
+ }
+ return area(get_rectangle(d, n));
+}
+
+int tiled_count(node_t *n)
+{
+ if (n == NULL) {
+ return 0;
+ }
+ int cnt = 0;
+ for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+ if (!f->hidden && f->client != NULL && IS_TILED(f->client)) {
+ cnt++;
+ }
+ }
+ return cnt;
+}
+
+void find_biggest(coordinates_t *ref, coordinates_t *dst, node_select_t sel)
+{
+ unsigned int b_area = 0;
+
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+ for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f, d->root)) {
+ coordinates_t loc = {m, d, f};
+ if (f->client == NULL || f->vacant || !node_matches(&loc, ref, sel)) {
+ continue;
+ }
+ unsigned int f_area = node_area(d, f);
+ if (f_area > b_area) {
+ *dst = loc;
+ b_area = f_area;
+ }
+ }
+ }
+ }
+}
+
+void rotate_tree(node_t *n, int deg)
+{
+ if (n == NULL || is_leaf(n) || deg == 0) {
+ return;
+ }
+
+ node_t *tmp;
+
+ if ((deg == 90 && n->split_type == TYPE_HORIZONTAL) ||
+ (deg == 270 && n->split_type == TYPE_VERTICAL) ||
+ deg == 180) {
+ tmp = n->first_child;
+ n->first_child = n->second_child;
+ n->second_child = tmp;
+ n->split_ratio = 1.0 - n->split_ratio;
+ }
+
+ if (deg != 180) {
+ if (n->split_type == TYPE_HORIZONTAL) {
+ n->split_type = TYPE_VERTICAL;
+ } else if (n->split_type == TYPE_VERTICAL) {
+ n->split_type = TYPE_HORIZONTAL;
+ }
+ }
+
+ rotate_tree(n->first_child, deg);
+ rotate_tree(n->second_child, deg);
+}
+
+void rotate_brother(node_t *n)
+{
+ rotate_tree(brother_tree(n), n->birth_rotation);
+}
+
+void unrotate_tree(node_t *n, int rot)
+{
+ if (rot == 0) {
+ return;
+ }
+ rotate_tree(n, 360 - rot);
+}
+
+void unrotate_brother(node_t *n)
+{
+ unrotate_tree(brother_tree(n), n->birth_rotation);
+}
+
+void flip_tree(node_t *n, flip_t flp)
+{
+ if (n == NULL || is_leaf(n)) {
+ return;
+ }
+
+ node_t *tmp;
+
+ if ((flp == FLIP_HORIZONTAL && n->split_type == TYPE_HORIZONTAL) ||
+ (flp == FLIP_VERTICAL && n->split_type == TYPE_VERTICAL)) {
+ tmp = n->first_child;
+ n->first_child = n->second_child;
+ n->second_child = tmp;
+ n->split_ratio = 1.0 - n->split_ratio;
+ }
+
+ flip_tree(n->first_child, flp);
+ flip_tree(n->second_child, flp);
+}
+
+void equalize_tree(node_t *n)
+{
+ if (n == NULL || n->vacant) {
+ return;
+ } else {
+ n->split_ratio = split_ratio;
+ equalize_tree(n->first_child);
+ equalize_tree(n->second_child);
+ }
+}
+
+int balance_tree(node_t *n)
+{
+ if (n == NULL || n->vacant) {
+ return 0;
+ } else if (is_leaf(n)) {
+ return 1;
+ } else {
+ int b1 = balance_tree(n->first_child);
+ int b2 = balance_tree(n->second_child);
+ int b = b1 + b2;
+ if (b1 > 0 && b2 > 0) {
+ n->split_ratio = (double) b1 / b;
+ }
+ return b;
+ }
+}
+
+void unlink_node(monitor_t *m, desktop_t *d, node_t *n)
+{
+ if (d == NULL || n == NULL) {
+ return;
+ }
+
+ node_t *p = n->parent;
+
+ if (p == NULL) {
+ d->root = NULL;
+ d->focus = NULL;
+ } else {
+ if (d->focus == p || is_descendant(d->focus, n)) {
+ d->focus = NULL;
+ }
+
+ history_remove(d, p, false);
+ cancel_presel(m, d, p);
+ if (p->sticky) {
+ m->sticky_count--;
+ }
+
+ node_t *b = brother_tree(n);
+ node_t *g = p->parent;
+
+ if (!n->vacant) {
+ unrotate_tree(b, n->birth_rotation);
+ }
+
+ b->parent = g;
+
+ if (g != NULL) {
+ if (is_first_child(p)) {
+ g->first_child = b;
+ } else {
+ g->second_child = b;
+ }
+ } else {
+ d->root = b;
+ }
+
+ b->birth_rotation = p->birth_rotation;
+
+ free(p);
+ n->parent = NULL;
+
+ propagate_flags_upward(m, d, b);
+ }
+}
+
+void close_node(node_t *n)
+{
+ if (n == NULL) {
+ return;
+ } else if (n->client != NULL) {
+ if (n->client->icccm_props.delete_window) {
+ send_client_message(n->id, ewmh->WM_PROTOCOLS, WM_DELETE_WINDOW);
+ } else {
+ xcb_kill_client(dpy, n->id);
+ }
+ } else {
+ close_node(n->first_child);
+ close_node(n->second_child);
+ }
+}
+
+void kill_node(monitor_t *m, desktop_t *d, node_t *n)
+{
+ if (n == NULL) {
+ return;
+ }
+
+ for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+ if (f->client != NULL) {
+ xcb_kill_client(dpy, f->id);
+ }
+ }
+
+ remove_node(m, d, n);
+}
+
+void remove_node(monitor_t *m, desktop_t *d, node_t *n)
+{
+ if (n == NULL) {
+ return;
+ }
+
+ unlink_node(m, d, n);
+ history_remove(d, n, true);
+ remove_stack_node(n);
+ cancel_presel_in(m, d, n);
+ if (m->sticky_count > 0) {
+ m->sticky_count -= sticky_count(n);
+ }
+ clients_count -= clients_count_in(n);
+ if (is_descendant(grabbed_node, n)) {
+ grabbed_node = NULL;
+ }
+ free_node(n);
+
+ ewmh_update_client_list(false);
+ ewmh_update_client_list(true);
+
+ if (mon != NULL && d->focus == NULL) {
+ if (d == mon->desk) {
+ focus_node(m, d, NULL);
+ } else {
+ activate_node(m, d, NULL);
+ }
+ }
+}
+
+void free_node(node_t *n)
+{
+ if (n == NULL) {
+ return;
+ }
+ node_t *first_child = n->first_child;
+ node_t *second_child = n->second_child;
+ free(n->client);
+ free(n);
+ free_node(first_child);
+ free_node(second_child);
+}
+
+bool swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop_t *d2, node_t *n2)
+{
+ if (n1 == NULL || n2 == NULL || n1 == n2 || is_descendant(n1, n2) || is_descendant(n2, n1) ||
+ (d1 != d2 && ((m1->sticky_count > 0 && sticky_count(n1) > 0) ||
+ (m2->sticky_count > 0 && sticky_count(n2) > 0)))) {
+ return false;
+ }
+
+ put_status(SBSC_MASK_NODE_SWAP, "node_swap 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X\n", m1->id, d1->id, n1->id, m2->id, d2->id, n2->id);
+
+ node_t *pn1 = n1->parent;
+ node_t *pn2 = n2->parent;
+ bool n1_first_child = is_first_child(n1);
+ bool n2_first_child = is_first_child(n2);
+ int br1 = n1->birth_rotation;
+ int br2 = n2->birth_rotation;
+ bool n1_held_focus = is_descendant(d1->focus, n1);
+ bool n2_held_focus = is_descendant(d2->focus, n2);
+ node_t *last_d1_focus = d1->focus;
+ node_t *last_d2_focus = d2->focus;
+
+ if (pn1 != NULL) {
+ if (n1_first_child) {
+ pn1->first_child = n2;
+ } else {
+ pn1->second_child = n2;
+ }
+ }
+
+ if (pn2 != NULL) {
+ if (n2_first_child) {
+ pn2->first_child = n1;
+ } else {
+ pn2->second_child = n1;
+ }
+ }
+
+ n1->parent = pn2;
+ n2->parent = pn1;
+ n1->birth_rotation = br2;
+ n2->birth_rotation = br1;
+
+ propagate_flags_upward(m2, d2, n1);
+ propagate_flags_upward(m1, d1, n2);
+
+ if (d1 != d2) {
+ if (d1->root == n1) {
+ d1->root = n2;
+ }
+
+ if (d2->root == n2) {
+ d2->root = n1;
+ }
+
+ if (n1_held_focus) {
+ d1->focus = n2_held_focus ? last_d2_focus : n2;
+ }
+
+ if (n2_held_focus) {
+ d2->focus = n1_held_focus ? last_d1_focus : n1;
+ }
+
+ if (m1 != m2) {
+ adapt_geometry(&m2->rectangle, &m1->rectangle, n2);
+ adapt_geometry(&m1->rectangle, &m2->rectangle, n1);
+ }
+
+ ewmh_set_wm_desktop(n1, d2);
+ ewmh_set_wm_desktop(n2, d1);
+
+ history_swap_nodes(m1, d1, n1, m2, d2, n2);
+
+ if (m1->desk != d1 && m2->desk == d2) {
+ show_node(d2, n1);
+ hide_node(d2, n2);
+ } else if (m1->desk == d1 && m2->desk != d2) {
+ hide_node(d1, n1);
+ show_node(d1, n2);
+ }
+
+ if (n1_held_focus) {
+ if (d1 == mon->desk) {
+ focus_node(m1, d1, d1->focus);
+ } else {
+ activate_node(m1, d1, d1->focus);
+ }
+ } else {
+ draw_border(n2, is_descendant(n2, d1->focus), (m1 == mon));
+ }
+
+ if (n2_held_focus) {
+ if (d2 == mon->desk) {
+ focus_node(m2, d2, d2->focus);
+ } else {
+ activate_node(m2, d2, d2->focus);
+ }
+ } else {
+ draw_border(n1, is_descendant(n1, d2->focus), (m2 == mon));
+ }
+ }
+
+ arrange(m1, d1);
+
+ if (d1 != d2) {
+ arrange(m2, d2);
+ }
+
+ return true;
+}
+
+bool transfer_node(monitor_t *ms, desktop_t *ds, node_t *ns, monitor_t *md, desktop_t *dd, node_t *nd)
+{
+ if (ns == NULL || ns == nd || is_child(ns, nd) || is_descendant(nd, ns)) {
+ return false;
+ }
+
+ if (sticky_still && ds != dd && ms->sticky_count > 0 && sticky_count(ns) > 0) {
+ return false;
+ }
+
+ put_status(SBSC_MASK_NODE_TRANSFER, "node_transfer 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X\n", ms->id, ds->id, ns->id, md->id, dd->id, nd!=NULL?nd->id:0);
+
+ bool held_focus = is_descendant(ds->focus, ns);
+ /* avoid ending up with a dangling pointer (because of unlink_node) */
+ node_t *last_ds_focus = is_child(ns, ds->focus) ? NULL : ds->focus;
+
+ if (held_focus && ds == mon->desk) {
+ clear_input_focus();
+ }
+
+ unlink_node(ms, ds, ns);
+ insert_node(md, dd, ns, nd);
+
+ if (md != ms && (ns->client == NULL || monitor_from_client(ns->client) != md)) {
+ adapt_geometry(&ms->rectangle, &md->rectangle, ns);
+ }
+
+ if (ds != dd) {
+ ewmh_set_wm_desktop(ns, dd);
+ if (sticky_still) {
+ if (ds == ms->desk && dd != md->desk) {
+ hide_node(ds, ns);
+ } else if (ds != ms->desk && dd == md->desk) {
+ show_node(dd, ns);
+ }
+ }
+ }
+
+ history_transfer_node(md, dd, ns);
+ stack(dd, ns, false);
+
+ if (ds == dd) {
+ if (held_focus) {
+ if (ds == mon->desk) {
+ focus_node(ms, ds, last_ds_focus);
+ } else {
+ activate_node(ms, ds, last_ds_focus);
+ }
+ } else {
+ draw_border(ns, is_descendant(ns, ds->focus), (ms == mon));
+ }
+ } else {
+ if (held_focus) {
+ if (ds == mon->desk) {
+ focus_node(ms, ds, ds->focus);
+ } else {
+ activate_node(ms, ds, ds->focus);
+ }
+ }
+ if (dd->focus == ns) {
+ if (dd == mon->desk) {
+ focus_node(md, dd, held_focus ? last_ds_focus : dd->focus);
+ } else {
+ activate_node(md, dd, held_focus ? last_ds_focus : dd->focus);
+ }
+ } else {
+ draw_border(ns, is_descendant(ns, dd->focus), (md == mon));
+ }
+ }
+
+ arrange(ms, ds);
+
+ if (ds != dd) {
+ arrange(md, dd);
+ }
+
+ return true;
+}
+
+bool find_closest_node(coordinates_t *ref, coordinates_t *dst, cycle_dir_t dir, node_select_t sel)
+{
+ if (ref->node == NULL) {
+ return false;
+ }
+
+ monitor_t *m = ref->monitor;
+ desktop_t *d = ref->desktop;
+ node_t *n = ref->node;
+
+ node_t *f = (dir == CYCLE_PREV ? prev_leaf(n, d->root) : next_leaf(n, d->root));
+
+#define HANDLE_BOUNDARIES(f) \
+ while (f == NULL) { \
+ d = (dir == CYCLE_PREV ? d->prev : d->next); \
+ if (d == NULL) { \
+ m = (dir == CYCLE_PREV ? m->prev : m->next); \
+ if (m == NULL) { \
+ m = (dir == CYCLE_PREV ? mon_tail : mon_head); \
+ } \
+ d = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head); \
+ } \
+ f = (dir == CYCLE_PREV ? second_extrema(d->root) : first_extrema(d->root)); \
+ }
+ HANDLE_BOUNDARIES(f);
+
+ while (f != n) {
+ coordinates_t loc = {m, d, f};
+ if (f->client != NULL && !f->hidden && node_matches(&loc, ref, sel)) {
+ *dst = loc;
+ return true;
+ }
+ f = (dir == CYCLE_PREV ? prev_leaf(f, d->root) : next_leaf(f, d->root));
+ HANDLE_BOUNDARIES(f);
+ }
+#undef HANDLE_EXTREMUM
+ return false;
+}
+
+void circulate_leaves(monitor_t *m, desktop_t *d, node_t *n, circulate_dir_t dir)
+{
+ if (n == NULL || d->focus == NULL || is_leaf(n)) {
+ return;
+ }
+ node_t *p = d->focus->parent;
+ bool focus_first_child = is_first_child(d->focus);
+ if (dir == CIRCULATE_FORWARD) {
+ node_t *e = second_extrema(n);
+ while (e != NULL && (e->client == NULL || !IS_TILED(e->client))) {
+ e = prev_leaf(e, n);
+ }
+ for (node_t *s = e, *f = prev_tiled_leaf(s, n); f != NULL; s = prev_tiled_leaf(f, n), f = prev_tiled_leaf(s, n)) {
+ swap_nodes(m, d, f, m, d, s);
+ }
+ } else {
+ node_t *e = first_extrema(n);
+ while (e != NULL && (e->client == NULL || !IS_TILED(e->client))) {
+ e = next_leaf(e, n);
+ }
+ for (node_t *f = e, *s = next_tiled_leaf(f, n); s != NULL; f = next_tiled_leaf(s, n), s = next_tiled_leaf(f, n)) {
+ swap_nodes(m, d, f, m, d, s);
+ }
+ }
+ if (focus_first_child) {
+ focus_node(m, d, p->first_child);
+ } else {
+ focus_node(m, d, p->second_child);
+ }
+}
+
+void set_vacant(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+ if (n->vacant == value) {
+ return;
+ }
+
+ propagate_vacant_downward(m, d, n, value);
+ propagate_vacant_upward(m, d, n);
+}
+
+void set_vacant_local(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+ if (n->vacant == value) {
+ return;
+ }
+
+ n->vacant = value;
+
+ if (value) {
+ unrotate_brother(n);
+ cancel_presel(m, d, n);
+ } else {
+ rotate_brother(n);
+ }
+}
+
+void propagate_vacant_downward(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+ if (n == NULL) {
+ return;
+ }
+
+ set_vacant_local(m, d, n, value);
+
+ propagate_vacant_downward(m, d, n->first_child, value);
+ propagate_vacant_downward(m, d, n->second_child, value);
+}
+
+void propagate_vacant_upward(monitor_t *m, desktop_t *d, node_t *n)
+{
+ if (n == NULL) {
+ return;
+ }
+
+ node_t *p = n->parent;
+
+ if (p != NULL) {
+ set_vacant_local(m, d, p, (p->first_child->vacant && p->second_child->vacant));
+ }
+
+ propagate_vacant_upward(m, d, p);
+}
+
+bool set_layer(monitor_t *m, desktop_t *d, node_t *n, stack_layer_t l)
+{
+ if (n == NULL || n->client->layer == l) {
+ return false;
+ }
+
+ n->client->last_layer = n->client->layer;
+ n->client->layer = l;
+
+ if (l == LAYER_ABOVE) {
+ n->client->wm_flags |= WM_FLAG_ABOVE;
+ n->client->wm_flags &= ~WM_FLAG_BELOW;
+ } else if (l == LAYER_BELOW) {
+ n->client->wm_flags |= WM_FLAG_BELOW;
+ n->client->wm_flags &= ~WM_FLAG_ABOVE;
+ } else {
+ n->client->wm_flags &= ~(WM_FLAG_ABOVE | WM_FLAG_BELOW);
+ }
+
+ ewmh_wm_state_update(n);
+
+ put_status(SBSC_MASK_NODE_LAYER, "node_layer 0x%08X 0x%08X 0x%08X %s\n", m->id, d->id, n->id, LAYER_STR(l));
+
+ if (d->focus == n) {
+ neutralize_occluding_windows(m, d, n);
+ }
+
+ stack(d, n, (d->focus == n));
+
+ return true;
+}
+
+bool set_state(monitor_t *m, desktop_t *d, node_t *n, client_state_t s)
+{
+ if (n == NULL || n->client == NULL || n->client->state == s) {
+ return false;
+ }
+
+ client_t *c = n->client;
+
+ c->last_state = c->state;
+ c->state = s;
+
+ switch (c->last_state) {
+ case STATE_TILED:
+ case STATE_PSEUDO_TILED:
+ break;
+ case STATE_FLOATING:
+ set_floating(m, d, n, false);
+ break;
+ case STATE_FULLSCREEN:
+ set_fullscreen(m, d, n, false);
+ break;
+ }
+
+ put_status(SBSC_MASK_NODE_STATE, "node_state 0x%08X 0x%08X 0x%08X %s off\n", m->id, d->id, n->id, STATE_STR(c->last_state));
+
+ switch (c->state) {
+ case STATE_TILED:
+ case STATE_PSEUDO_TILED:
+ break;
+ case STATE_FLOATING:
+ set_floating(m, d, n, true);
+ break;
+ case STATE_FULLSCREEN:
+ set_fullscreen(m, d, n, true);
+ break;
+ }
+
+ put_status(SBSC_MASK_NODE_STATE, "node_state 0x%08X 0x%08X 0x%08X %s on\n", m->id, d->id, n->id, STATE_STR(c->state));
+
+ if (n == m->desk->focus) {
+ put_status(SBSC_MASK_REPORT);
+ }
+
+ return true;
+}
+
+void set_floating(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+ if (n == NULL) {
+ return;
+ }
+
+ cancel_presel(m, d, n);
+ set_vacant(m, d, n, value);
+
+ if (!value && d->focus == n) {
+ neutralize_occluding_windows(m, d, n);
+ }
+
+ stack(d, n, (d->focus == n));
+}
+
+void set_fullscreen(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+ if (n == NULL) {
+ return;
+ }
+
+ client_t *c = n->client;
+
+ cancel_presel(m, d, n);
+ set_vacant(m, d, n, value);
+
+ if (value) {
+ c->wm_flags |= WM_FLAG_FULLSCREEN;
+ c->last_layer = c->layer;
+ c->layer = LAYER_ABOVE;
+ } else {
+ c->wm_flags &= ~WM_FLAG_FULLSCREEN;
+ c->layer = c->last_layer;
+ if (d->focus == n) {
+ neutralize_occluding_windows(m, d, n);
+ }
+ }
+
+ ewmh_wm_state_update(n);
+ stack(d, n, (d->focus == n));
+}
+
+void neutralize_occluding_windows(monitor_t *m, desktop_t *d, node_t *n)
+{
+ bool changed = false;
+ for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+ for (node_t *a = first_extrema(d->root); a != NULL; a = next_leaf(a, d->root)) {
+ if (a != f && a->client != NULL && f->client != NULL &&
+ IS_FULLSCREEN(a->client) && stack_cmp(f->client, a->client) < 0) {
+ set_state(m, d, a, a->client->last_state);
+ changed = true;
+ }
+ }
+ }
+ if (changed) {
+ arrange(m, d);
+ }
+}
+
+void propagate_flags_upward(monitor_t *m, desktop_t *d, node_t *n)
+{
+ if (n == NULL) {
+ return;
+ }
+
+ node_t *p = n->parent;
+
+ if (p != NULL) {
+ set_vacant_local(m, d, p, (p->first_child->vacant && p->second_child->vacant));
+ set_hidden_local(m, d, p, (p->first_child->hidden && p->second_child->hidden));
+ }
+
+ propagate_flags_upward(m, d, p);
+}
+
+void set_hidden(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+ if (n == NULL || n->hidden == value) {
+ return;
+ }
+
+
+ if (focus_follows_pointer) {
+ listen_enter_notify(d->root, false);
+ }
+
+ bool held_focus = is_descendant(d->focus, n);
+
+ propagate_hidden_downward(m, d, n, value);
+ propagate_hidden_upward(m, d, n);
+
+ put_status(SBSC_MASK_NODE_FLAG, "node_flag 0x%08X 0x%08X 0x%08X hidden %s\n", m->id, d->id, n->id, ON_OFF_STR(value));
+
+ if (held_focus || d->focus == NULL) {
+ if (d->focus != NULL) {
+ d->focus = NULL;
+ draw_border(n, false, (mon == m));
+ }
+ if (d == mon->desk) {
+ focus_node(m, d, d->focus);
+ } else {
+ activate_node(m, d, d->focus);
+ }
+ }
+
+ if (focus_follows_pointer) {
+ listen_enter_notify(d->root, true);
+ }
+}
+
+void set_hidden_local(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+ if (n->hidden == value) {
+ return;
+ }
+
+ n->hidden = value;
+
+ if (n->client != NULL) {
+ if (n->client->shown) {
+ window_set_visibility(n->id, !value);
+ }
+
+ if (IS_TILED(n->client)) {
+ set_vacant(m, d, n, value);
+ }
+
+ if (value) {
+ n->client->wm_flags |= WM_FLAG_HIDDEN;
+ } else {
+ n->client->wm_flags &= ~WM_FLAG_HIDDEN;
+ }
+
+ ewmh_wm_state_update(n);
+ }
+}
+
+void propagate_hidden_downward(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+ if (n == NULL) {
+ return;
+ }
+
+ set_hidden_local(m, d, n, value);
+
+ propagate_hidden_downward(m, d, n->first_child, value);
+ propagate_hidden_downward(m, d, n->second_child, value);
+}
+
+void propagate_hidden_upward(monitor_t *m, desktop_t *d, node_t *n)
+{
+ if (n == NULL) {
+ return;
+ }
+
+ node_t *p = n->parent;
+
+ if (p != NULL) {
+ set_hidden_local(m, d, p, p->first_child->hidden && p->second_child->hidden);
+ }
+
+ propagate_hidden_upward(m, d, p);
+}
+
+void set_sticky(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+ if (n == NULL || n->sticky == value) {
+ return;
+ }
+
+ if (d != m->desk) {
+ transfer_node(m, d, n, m, m->desk, m->desk->focus);
+ }
+
+ n->sticky = value;
+
+ if (value) {
+ m->sticky_count++;
+ } else {
+ m->sticky_count--;
+ }
+
+ if (n->client != NULL) {
+ if (value) {
+ n->client->wm_flags |= WM_FLAG_STICKY;
+ } else {
+ n->client->wm_flags &= ~WM_FLAG_STICKY;
+ }
+ ewmh_wm_state_update(n);
+ }
+
+ put_status(SBSC_MASK_NODE_FLAG, "node_flag 0x%08X 0x%08X 0x%08X sticky %s\n", m->id, d->id, n->id, ON_OFF_STR(value));
+
+ if (n == m->desk->focus) {
+ put_status(SBSC_MASK_REPORT);
+ }
+}
+
+void set_private(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+ if (n == NULL || n->private == value) {
+ return;
+ }
+
+ n->private = value;
+
+ put_status(SBSC_MASK_NODE_FLAG, "node_flag 0x%08X 0x%08X 0x%08X private %s\n", m->id, d->id, n->id, ON_OFF_STR(value));
+
+ if (n == m->desk->focus) {
+ put_status(SBSC_MASK_REPORT);
+ }
+}
+
+void set_locked(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+ if (n == NULL || n->locked == value) {
+ return;
+ }
+
+ n->locked = value;
+
+ put_status(SBSC_MASK_NODE_FLAG, "node_flag 0x%08X 0x%08X 0x%08X locked %s\n", m->id, d->id, n->id, ON_OFF_STR(value));
+
+ if (n == m->desk->focus) {
+ put_status(SBSC_MASK_REPORT);
+ }
+}
+
+void set_urgent(monitor_t *m, desktop_t *d, node_t *n, bool value)
+{
+ if (value && mon->desk->focus == n) {
+ return;
+ }
+
+ n->client->urgent = value;
+
+ if (value) {
+ n->client->wm_flags |= WM_FLAG_DEMANDS_ATTENTION;
+ } else {
+ n->client->wm_flags &= ~WM_FLAG_DEMANDS_ATTENTION;
+ }
+
+ ewmh_wm_state_update(n);
+
+ put_status(SBSC_MASK_NODE_FLAG, "node_flag 0x%08X 0x%08X 0x%08X urgent %s\n", m->id, d->id, n->id, ON_OFF_STR(value));
+ put_status(SBSC_MASK_REPORT);
+}
+
+/* Returns true if a contains b */
+bool contains(xcb_rectangle_t a, xcb_rectangle_t b)
+{
+ return (a.x <= b.x && (a.x + a.width) >= (b.x + b.width) &&
+ a.y <= b.y && (a.y + a.height) >= (b.y + b.height));
+}
+
+xcb_rectangle_t get_rectangle(desktop_t *d, node_t *n)
+{
+ client_t *c = n->client;
+ if (c != NULL) {
+ if (IS_FLOATING(c)) {
+ return c->floating_rectangle;
+ } else {
+ return c->tiled_rectangle;
+ }
+ } else {
+ int wg = (d == NULL ? 0 : (gapless_monocle && IS_MONOCLE(d) ? 0 : d->window_gap));
+ xcb_rectangle_t rect = n->rectangle;
+ rect.width -= wg;
+ rect.height -= wg;
+ return rect;
+ }
+}
+
+void listen_enter_notify(node_t *n, bool enable)
+{
+ uint32_t mask = CLIENT_EVENT_MASK | (enable ? XCB_EVENT_MASK_ENTER_WINDOW : 0);
+ for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+ if (f->client == NULL) {
+ continue;
+ }
+ xcb_change_window_attributes(dpy, f->id, XCB_CW_EVENT_MASK, &mask);
+ if (f->presel != NULL) {
+ xcb_change_window_attributes(dpy, f->presel->feedback, XCB_CW_EVENT_MASK, &mask);
+ }
+ }
+}
+
+#define DEF_FLAG_COUNT(flag) \
+ unsigned int flag##_count(node_t *n) \
+ { \
+ if (n == NULL) { \
+ return 0; \
+ } else { \
+ return ((n->flag ? 1 : 0) + \
+ flag##_count(n->first_child) + \
+ flag##_count(n->second_child)); \
+ } \
+ }
+ DEF_FLAG_COUNT(sticky)
+ DEF_FLAG_COUNT(private)
+ DEF_FLAG_COUNT(locked)
+#undef DEF_FLAG_COUNT
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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.
+ */
+
+#ifndef BSPWM_TREE_H
+#define BSPWM_TREE_H
+
+void arrange(monitor_t *m, desktop_t *d);
+void apply_layout(monitor_t *m, desktop_t *d, node_t *n, layout_t l, xcb_rectangle_t rect, xcb_rectangle_t root_rect);
+presel_t *make_presel(void);
+void set_ratio(node_t *n, double rat);
+void presel_dir(monitor_t *m, desktop_t *d, node_t *n, direction_t dir);
+void presel_ratio(monitor_t *m, desktop_t *d, node_t *n, double ratio);
+void cancel_presel(monitor_t *m, desktop_t *d, node_t *n);
+void cancel_presel_in(monitor_t *m, desktop_t *d, node_t *n);
+node_t *find_public(desktop_t *d);
+node_t *insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f);
+void insert_receptacle(monitor_t *m, desktop_t *d, node_t *n);
+bool activate_node(monitor_t *m, desktop_t *d, node_t *n);
+void transfer_sticky_nodes(monitor_t *m, desktop_t *ds, desktop_t *dd, node_t *n);
+bool focus_node(monitor_t *m, desktop_t *d, node_t *n);
+void hide_node(desktop_t *d, node_t *n);
+void show_node(desktop_t *d, node_t *n);
+node_t *make_node(uint32_t id);
+client_t *make_client(void);
+void initialize_client(node_t *n);
+bool is_focusable(node_t *n);
+bool is_leaf(node_t *n);
+bool is_first_child(node_t *n);
+bool is_second_child(node_t *n);
+unsigned int clients_count_in(node_t *n);
+node_t *brother_tree(node_t *n);
+node_t *first_extrema(node_t *n);
+node_t *second_extrema(node_t *n);
+node_t *first_focusable_leaf(node_t *n);
+node_t *next_leaf(node_t *n, node_t *r);
+node_t *prev_leaf(node_t *n, node_t *r);
+node_t *next_tiled_leaf(node_t *n, node_t *r);
+node_t *prev_tiled_leaf(node_t *n, node_t *r);
+bool is_adjacent(node_t *a, node_t *b, direction_t dir);
+node_t *find_fence(node_t *n, direction_t dir);
+bool is_child(node_t *a, node_t *b);
+bool is_descendant(node_t *a, node_t *b);
+bool find_by_id(uint32_t id, coordinates_t *loc);
+node_t *find_by_id_in(node_t *r, uint32_t id);
+void find_nearest_neighbor(coordinates_t *ref, coordinates_t *dst, direction_t dir, node_select_t sel);
+unsigned int node_area(desktop_t *d, node_t *n);
+int tiled_count(node_t *n);
+void find_biggest(coordinates_t *ref, coordinates_t *dst, node_select_t sel);
+void rotate_tree(node_t *n, int deg);
+void rotate_brother(node_t *n);
+void unrotate_tree(node_t *n, int rot);
+void unrotate_brother(node_t *n);
+void flip_tree(node_t *n, flip_t flp);
+void equalize_tree(node_t *n);
+int balance_tree(node_t *n);
+void unlink_node(monitor_t *m, desktop_t *d, node_t *n);
+void close_node(node_t *n);
+void kill_node(monitor_t *m, desktop_t *d, node_t *n);
+void remove_node(monitor_t *m, desktop_t *d, node_t *n);
+void free_node(node_t *n);
+bool swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop_t *d2, node_t *n2);
+bool transfer_node(monitor_t *ms, desktop_t *ds, node_t *ns, monitor_t *md, desktop_t *dd, node_t *nd);
+bool find_closest_node(coordinates_t *ref, coordinates_t *dst, cycle_dir_t dir, node_select_t sel);
+void circulate_leaves(monitor_t *m, desktop_t *d, node_t *n, circulate_dir_t dir);
+void set_vacant(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void set_vacant_local(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void propagate_vacant_downward(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void propagate_vacant_upward(monitor_t *m, desktop_t *d, node_t *n);
+bool set_layer(monitor_t *m, desktop_t *d, node_t *n, stack_layer_t l);
+bool set_state(monitor_t *m, desktop_t *d, node_t *n, client_state_t s);
+void set_floating(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void set_fullscreen(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void neutralize_occluding_windows(monitor_t *m, desktop_t *d, node_t *n);
+void propagate_flags_upward(monitor_t *m, desktop_t *d, node_t *n);
+void set_hidden(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void set_hidden_local(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void propagate_hidden_downward(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void propagate_hidden_upward(monitor_t *m, desktop_t *d, node_t *n);
+void set_sticky(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void set_private(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void set_locked(monitor_t *m, desktop_t *d, node_t *n, bool value);
+void set_urgent(monitor_t *m, desktop_t *d, node_t *n, bool value);
+bool contains(xcb_rectangle_t a, xcb_rectangle_t b);
+xcb_rectangle_t get_rectangle(desktop_t *d, node_t *n);
+void listen_enter_notify(node_t *n, bool enable);
+
+unsigned int sticky_count(node_t *n);
+unsigned int private_count(node_t *n);
+unsigned int locked_count(node_t *n);
+
+#endif
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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.
+ */
+
+#ifndef BSPWM_TYPES_H
+#define BSPWM_TYPES_H
+#include <stdbool.h>
+#include <xcb/xcb.h>
+#include <xcb/xcb_icccm.h>
+#include <xcb/randr.h>
+#include <xcb/xcb_event.h>
+#include "helpers.h"
+
+#define MISSING_VALUE "N/A"
+#define MAX_WM_STATES 4
+
+typedef enum {
+ TYPE_HORIZONTAL,
+ TYPE_VERTICAL
+} split_type_t;
+
+typedef enum {
+ MODE_AUTOMATIC,
+ MODE_MANUAL
+} split_mode_t;
+
+typedef enum {
+ STATE_TILED,
+ STATE_PSEUDO_TILED,
+ STATE_FLOATING,
+ STATE_FULLSCREEN
+} client_state_t;
+
+typedef enum {
+ WM_FLAG_MODAL = 1 << 0,
+ WM_FLAG_STICKY = 1 << 1,
+ WM_FLAG_MAXIMIZED_VERT = 1 << 2,
+ WM_FLAG_MAXIMIZED_HORZ = 1 << 3,
+ WM_FLAG_SHADED = 1 << 4,
+ WM_FLAG_SKIP_TASKBAR = 1 << 5,
+ WM_FLAG_SKIP_PAGER = 1 << 6,
+ WM_FLAG_HIDDEN = 1 << 7,
+ WM_FLAG_FULLSCREEN = 1 << 8,
+ WM_FLAG_ABOVE = 1 << 9,
+ WM_FLAG_BELOW = 1 << 10,
+ WM_FLAG_DEMANDS_ATTENTION = 1 << 11,
+} wm_flags_t;
+
+typedef enum {
+ LAYER_BELOW,
+ LAYER_NORMAL,
+ LAYER_ABOVE
+} stack_layer_t;
+
+typedef enum {
+ OPTION_NONE,
+ OPTION_TRUE,
+ OPTION_FALSE
+} option_bool_t;
+
+typedef enum {
+ ALTER_TOGGLE,
+ ALTER_SET
+} alter_state_t;
+
+typedef enum {
+ CYCLE_NEXT,
+ CYCLE_PREV
+} cycle_dir_t;
+
+typedef enum {
+ CIRCULATE_FORWARD,
+ CIRCULATE_BACKWARD
+} circulate_dir_t;
+
+typedef enum {
+ HISTORY_OLDER,
+ HISTORY_NEWER
+} history_dir_t;
+
+typedef enum {
+ DIR_NORTH,
+ DIR_WEST,
+ DIR_SOUTH,
+ DIR_EAST
+} direction_t;
+
+typedef enum {
+ HANDLE_LEFT = 1 << 0,
+ HANDLE_TOP = 1 << 1,
+ HANDLE_RIGHT = 1 << 2,
+ HANDLE_BOTTOM = 1 << 3,
+ HANDLE_TOP_LEFT = HANDLE_TOP | HANDLE_LEFT,
+ HANDLE_TOP_RIGHT = HANDLE_TOP | HANDLE_RIGHT,
+ HANDLE_BOTTOM_RIGHT = HANDLE_BOTTOM | HANDLE_RIGHT,
+ HANDLE_BOTTOM_LEFT = HANDLE_BOTTOM | HANDLE_LEFT
+} resize_handle_t;
+
+typedef enum {
+ ACTION_NONE,
+ ACTION_FOCUS,
+ ACTION_MOVE,
+ ACTION_RESIZE_SIDE,
+ ACTION_RESIZE_CORNER
+} pointer_action_t;
+
+typedef enum {
+ LAYOUT_TILED,
+ LAYOUT_MONOCLE
+} layout_t;
+
+typedef enum {
+ FLIP_HORIZONTAL,
+ FLIP_VERTICAL
+} flip_t;
+
+typedef enum {
+ FIRST_CHILD,
+ SECOND_CHILD
+} child_polarity_t;
+
+typedef struct {
+ option_bool_t automatic;
+ option_bool_t focused;
+ option_bool_t local;
+ option_bool_t active;
+ option_bool_t leaf;
+ option_bool_t window;
+ option_bool_t tiled;
+ option_bool_t pseudo_tiled;
+ option_bool_t floating;
+ option_bool_t fullscreen;
+ option_bool_t hidden;
+ option_bool_t sticky;
+ option_bool_t private;
+ option_bool_t locked;
+ option_bool_t urgent;
+ option_bool_t same_class;
+ option_bool_t descendant_of;
+ option_bool_t ancestor_of;
+ option_bool_t below;
+ option_bool_t normal;
+ option_bool_t above;
+} node_select_t;
+
+typedef struct {
+ option_bool_t occupied;
+ option_bool_t focused;
+ option_bool_t urgent;
+ option_bool_t local;
+} desktop_select_t;
+
+typedef struct {
+ option_bool_t occupied;
+ option_bool_t focused;
+} monitor_select_t;
+
+typedef struct icccm_props_t icccm_props_t;
+struct icccm_props_t {
+ bool take_focus;
+ bool input_hint;
+ bool delete_window;
+};
+
+typedef struct {
+ char class_name[3 * SMALEN / 2];
+ char instance_name[3 * SMALEN / 2];
+ unsigned int border_width;
+ bool urgent;
+ bool shown;
+ client_state_t state;
+ client_state_t last_state;
+ stack_layer_t layer;
+ stack_layer_t last_layer;
+ xcb_rectangle_t floating_rectangle;
+ xcb_rectangle_t tiled_rectangle;
+ xcb_size_hints_t size_hints;
+ icccm_props_t icccm_props;
+ wm_flags_t wm_flags;
+} client_t;
+
+typedef struct presel_t presel_t;
+struct presel_t {
+ double split_ratio;
+ direction_t split_dir;
+ xcb_window_t feedback;
+};
+
+typedef struct node_t node_t;
+struct node_t {
+ uint32_t id;
+ split_type_t split_type;
+ double split_ratio;
+ int birth_rotation;
+ presel_t *presel;
+ xcb_rectangle_t rectangle;
+ bool vacant;
+ bool hidden;
+ bool sticky;
+ bool private;
+ bool locked;
+ node_t *first_child;
+ node_t *second_child;
+ node_t *parent;
+ client_t *client;
+};
+
+typedef struct padding_t padding_t;
+struct padding_t {
+ int top;
+ int right;
+ int bottom;
+ int left;
+};
+
+typedef struct desktop_t desktop_t;
+struct desktop_t {
+ char name[SMALEN];
+ uint32_t id;
+ layout_t layout;
+ node_t *root;
+ node_t *focus;
+ desktop_t *prev;
+ desktop_t *next;
+ padding_t padding;
+ int window_gap;
+ unsigned int border_width;
+};
+
+typedef struct monitor_t monitor_t;
+struct monitor_t {
+ char name[SMALEN];
+ uint32_t id;
+ xcb_randr_output_t randr_id;
+ xcb_window_t root;
+ bool wired;
+ padding_t padding;
+ unsigned int sticky_count;
+ int window_gap;
+ unsigned int border_width;
+ xcb_rectangle_t rectangle;
+ desktop_t *desk;
+ desktop_t *desk_head;
+ desktop_t *desk_tail;
+ monitor_t *prev;
+ monitor_t *next;
+};
+
+typedef struct {
+ monitor_t *monitor;
+ desktop_t *desktop;
+ node_t *node;
+} coordinates_t;
+
+typedef struct history_t history_t;
+struct history_t {
+ coordinates_t loc;
+ bool latest;
+ history_t *prev;
+ history_t *next;
+};
+
+typedef struct stacking_list_t stacking_list_t;
+struct stacking_list_t {
+ node_t *node;
+ stacking_list_t *prev;
+ stacking_list_t *next;
+};
+
+typedef struct subscriber_list_t subscriber_list_t;
+struct subscriber_list_t {
+ int fd;
+ FILE *stream;
+ int field;
+ subscriber_list_t *prev;
+ subscriber_list_t *next;
+};
+
+typedef struct rule_t rule_t;
+struct rule_t {
+ char class_name[MAXLEN];
+ char instance_name[MAXLEN];
+ char effect[MAXLEN];
+ bool one_shot;
+ rule_t *prev;
+ rule_t *next;
+};
+
+typedef struct {
+ char class_name[3 * SMALEN / 2];
+ char instance_name[3 * SMALEN / 2];
+ char monitor_desc[MAXLEN];
+ char desktop_desc[MAXLEN];
+ char node_desc[MAXLEN];
+ char split_dir[SMALEN];
+ double split_ratio;
+ stack_layer_t *layer;
+ client_state_t *state;
+ bool hidden;
+ bool sticky;
+ bool private;
+ bool locked;
+ bool center;
+ bool follow;
+ bool manage;
+ bool focus;
+ bool border;
+} rule_consequence_t;
+
+typedef struct pending_rule_t pending_rule_t;
+struct pending_rule_t {
+ int fd;
+ xcb_window_t win;
+ rule_consequence_t *csq;
+ pending_rule_t *prev;
+ pending_rule_t *next;
+};
+
+typedef struct {
+ double x;
+ double y;
+} dpoint_t;
+
+#endif
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include "bspwm.h"
+#include "ewmh.h"
+#include "monitor.h"
+#include "query.h"
+#include "rule.h"
+#include "settings.h"
+#include "geometry.h"
+#include "pointer.h"
+#include "stack.h"
+#include "tree.h"
+#include "parse.h"
+#include "window.h"
+
+void schedule_window(xcb_window_t win)
+{
+ coordinates_t loc;
+ uint8_t override_redirect = 0;
+ xcb_get_window_attributes_reply_t *wa = xcb_get_window_attributes_reply(dpy, xcb_get_window_attributes(dpy, win), NULL);
+
+ if (wa != NULL) {
+ override_redirect = wa->override_redirect;
+ free(wa);
+ }
+
+ if (override_redirect || locate_window(win, &loc)) {
+ return;
+ }
+
+ /* ignore pending windows */
+ for (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {
+ if (pr->win == win) {
+ return;
+ }
+ }
+
+ rule_consequence_t *csq = make_rule_conquence();
+ apply_rules(win, csq);
+ if (!schedule_rules(win, csq)) {
+ manage_window(win, csq, -1);
+ free(csq);
+ }
+}
+
+void manage_window(xcb_window_t win, rule_consequence_t *csq, int fd)
+{
+ monitor_t *m = mon;
+ desktop_t *d = mon->desk;
+ node_t *f = mon->desk->focus;
+
+ parse_rule_consequence(fd, csq);
+
+ if (ewmh_handle_struts(win)) {
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ arrange(m, m->desk);
+ }
+ }
+
+ if (!csq->manage) {
+ free(csq->layer);
+ free(csq->state);
+ window_show(win);
+ return;
+ }
+
+ if (csq->node_desc[0] != '\0') {
+ coordinates_t ref = {m, d, f};
+ coordinates_t trg = {NULL, NULL, NULL};
+ if (node_from_desc(csq->node_desc, &ref, &trg) == SELECTOR_OK) {
+ m = trg.monitor;
+ d = trg.desktop;
+ f = trg.node;
+ }
+ } else if (csq->desktop_desc[0] != '\0') {
+ coordinates_t ref = {m, d, NULL};
+ coordinates_t trg = {NULL, NULL, NULL};
+ if (desktop_from_desc(csq->desktop_desc, &ref, &trg) == SELECTOR_OK) {
+ m = trg.monitor;
+ d = trg.desktop;
+ f = trg.desktop->focus;
+ }
+ } else if (csq->monitor_desc[0] != '\0') {
+ coordinates_t ref = {m, NULL, NULL};
+ coordinates_t trg = {NULL, NULL, NULL};
+ if (monitor_from_desc(csq->monitor_desc, &ref, &trg) == SELECTOR_OK) {
+ m = trg.monitor;
+ d = trg.monitor->desk;
+ f = trg.monitor->desk->focus;
+ }
+ }
+
+ if (csq->sticky) {
+ m = mon;
+ d = mon->desk;
+ f = mon->desk->focus;
+ }
+
+ if (csq->split_dir[0] != '\0' && f != NULL) {
+ direction_t dir;
+ if (parse_direction(csq->split_dir, &dir)) {
+ presel_dir(m, d, f, dir);
+ }
+ }
+
+ if (csq->split_ratio != 0 && f != NULL) {
+ presel_ratio(m, d, f, csq->split_ratio);
+ }
+
+ node_t *n = make_node(win);
+ client_t *c = make_client();
+ c->border_width = csq->border ? d->border_width : 0;
+ n->client = c;
+ initialize_client(n);
+ initialize_floating_rectangle(n);
+
+ if (c->floating_rectangle.x == 0 && c->floating_rectangle.y == 0) {
+ csq->center = true;
+ }
+
+ monitor_t *mm = monitor_from_client(c);
+ embrace_client(mm, c);
+ adapt_geometry(&mm->rectangle, &m->rectangle, n);
+
+ if (csq->center) {
+ window_center(m, c);
+ }
+
+ snprintf(c->class_name, sizeof(c->class_name), "%s", csq->class_name);
+ snprintf(c->instance_name, sizeof(c->instance_name), "%s", csq->instance_name);
+
+ f = insert_node(m, d, n, f);
+ clients_count++;
+
+ put_status(SBSC_MASK_NODE_MANAGE, "node_manage 0x%08X 0x%08X 0x%08X 0x%08X\n", m->id, d->id, win, f!=NULL?f->id:0);
+
+ if (f != NULL && f->client != NULL && csq->state != NULL && *(csq->state) == STATE_FLOATING) {
+ c->layer = f->client->layer;
+ }
+
+ if (csq->layer != NULL) {
+ c->layer = *(csq->layer);
+ }
+
+ if (csq->state != NULL) {
+ set_state(m, d, n, *(csq->state));
+ }
+
+ set_hidden(m, d, n, csq->hidden);
+ set_sticky(m, d, n, csq->sticky);
+ set_private(m, d, n, csq->private);
+ set_locked(m, d, n, csq->locked);
+
+ arrange(m, d);
+
+ uint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)};
+ xcb_change_window_attributes(dpy, win, XCB_CW_EVENT_MASK, values);
+ set_window_state(win, XCB_ICCCM_WM_STATE_NORMAL);
+ window_grab_buttons(win);
+
+ if (d == m->desk) {
+ show_node(d, n);
+ } else {
+ hide_node(d, n);
+ }
+
+ if (!csq->hidden && csq->focus) {
+ if (d == mon->desk || csq->follow) {
+ focus_node(m, d, n);
+ } else {
+ activate_node(m, d, n);
+ }
+ } else {
+ stack(d, n, false);
+ draw_border(n, false, (m == mon));
+ }
+
+ ewmh_set_wm_desktop(n, d);
+ ewmh_update_client_list(false);
+ free(csq->layer);
+ free(csq->state);
+}
+
+void set_window_state(xcb_window_t win, xcb_icccm_wm_state_t state)
+{
+ long data[] = {state, XCB_NONE};
+ xcb_change_property(dpy, XCB_PROP_MODE_REPLACE, win, WM_STATE, WM_STATE, 32, 2, data);
+}
+
+void unmanage_window(xcb_window_t win)
+{
+ coordinates_t loc;
+ if (locate_window(win, &loc)) {
+ put_status(SBSC_MASK_NODE_UNMANAGE, "node_unmanage 0x%08X 0x%08X 0x%08X\n", loc.monitor->id, loc.desktop->id, win);
+ remove_node(loc.monitor, loc.desktop, loc.node);
+ set_window_state(win, XCB_ICCCM_WM_STATE_WITHDRAWN);
+ arrange(loc.monitor, loc.desktop);
+ } else {
+ for (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {
+ if (pr->win == win) {
+ remove_pending_rule(pr);
+ return;
+ }
+ }
+ }
+}
+
+bool is_presel_window(xcb_window_t win)
+{
+ xcb_icccm_get_wm_class_reply_t reply;
+ bool ret = false;
+ if (xcb_icccm_get_wm_class_reply(dpy, xcb_icccm_get_wm_class(dpy, win), &reply, NULL) == 1) {
+ if (streq(BSPWM_CLASS_NAME, reply.class_name) && streq(PRESEL_FEEDBACK_I, reply.instance_name)) {
+ ret = true;
+ }
+ xcb_icccm_get_wm_class_reply_wipe(&reply);
+ }
+ return ret;
+}
+
+void initialize_presel_feedback(node_t *n)
+{
+ if (n == NULL || n->presel == NULL || n->presel->feedback != XCB_NONE) {
+ return;
+ }
+
+ xcb_window_t win = xcb_generate_id(dpy);
+ uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_SAVE_UNDER | XCB_CW_EVENT_MASK;
+ uint32_t values[] = {get_color_pixel(presel_feedback_color), 1, focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0};
+ xcb_create_window(dpy, XCB_COPY_FROM_PARENT, win, root, 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
+ XCB_COPY_FROM_PARENT, mask, values);
+
+ xcb_icccm_set_wm_class(dpy, win, sizeof(PRESEL_FEEDBACK_IC), PRESEL_FEEDBACK_IC);
+ window_grab_buttons(win);
+ stacking_list_t *s = stack_tail;
+ while (s != NULL && !IS_TILED(s->node->client)) {
+ s = s->prev;
+ }
+ if (s != NULL) {
+ window_above(win, s->node->id);
+ }
+ n->presel->feedback = win;
+}
+
+void draw_presel_feedback(monitor_t *m, desktop_t *d, node_t *n)
+{
+ if (n == NULL || n->presel == NULL || d->layout == LAYOUT_MONOCLE) {
+ return;
+ }
+
+ if (focus_follows_pointer) {
+ listen_enter_notify(d->root, false);
+ }
+
+ bool exists = (n->presel->feedback != XCB_NONE);
+ if (!exists) {
+ initialize_presel_feedback(n);
+ }
+
+ int gap = gapless_monocle && IS_MONOCLE(d) ? 0 : d->window_gap;
+ presel_t *p = n->presel;
+ xcb_rectangle_t rect = n->rectangle;
+ rect.x = rect.y = 0;
+ rect.width -= gap;
+ rect.height -= gap;
+ xcb_rectangle_t presel_rect = rect;
+
+ switch (p->split_dir) {
+ case DIR_NORTH:
+ presel_rect.height = p->split_ratio * rect.height;
+ break;
+ case DIR_EAST:
+ presel_rect.width = (1 - p->split_ratio) * rect.width;
+ presel_rect.x = rect.width - presel_rect.width;
+ break;
+ case DIR_SOUTH:
+ presel_rect.height = (1 - p->split_ratio) * rect.height;
+ presel_rect.y = rect.height - presel_rect.height;
+ break;
+ case DIR_WEST:
+ presel_rect.width = p->split_ratio * rect.width;
+ break;
+ }
+
+ window_move_resize(p->feedback, n->rectangle.x + presel_rect.x, n->rectangle.y + presel_rect.y,
+ presel_rect.width, presel_rect.height);
+
+ if (!exists && m->desk == d) {
+ window_show(p->feedback);
+ }
+
+ if (focus_follows_pointer) {
+ listen_enter_notify(d->root, true);
+ }
+}
+
+void refresh_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n)
+{
+ if (n == NULL) {
+ return;
+ } else {
+ if (n->presel != NULL) {
+ draw_presel_feedback(m, d, n);
+ }
+ refresh_presel_feedbacks(m, d, n->first_child);
+ refresh_presel_feedbacks(m, d, n->second_child);
+ }
+}
+
+void show_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n)
+{
+ if (n == NULL) {
+ return;
+ } else {
+ if (n->presel != NULL) {
+ window_show(n->presel->feedback);
+ }
+ show_presel_feedbacks(m, d, n->first_child);
+ show_presel_feedbacks(m, d, n->second_child);
+ }
+}
+
+void hide_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n)
+{
+ if (n == NULL) {
+ return;
+ } else {
+ if (n->presel != NULL) {
+ window_hide(n->presel->feedback);
+ }
+ hide_presel_feedbacks(m, d, n->first_child);
+ hide_presel_feedbacks(m, d, n->second_child);
+ }
+}
+
+void update_colors(void)
+{
+ for (monitor_t *m = mon_head; m != NULL; m = m->next) {
+ for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
+ update_colors_in(d->root, d, m);
+ }
+ }
+}
+
+void update_colors_in(node_t *n, desktop_t *d, monitor_t *m)
+{
+ if (n == NULL) {
+ return;
+ } else {
+ if (n->presel != NULL) {
+ uint32_t pxl = get_color_pixel(presel_feedback_color);
+ xcb_change_window_attributes(dpy, n->presel->feedback, XCB_CW_BACK_PIXEL, &pxl);
+ if (d == m->desk) {
+ /* hack to induce back pixel refresh */
+ window_hide(n->presel->feedback);
+ window_show(n->presel->feedback);
+ }
+ }
+ if (n == d->focus) {
+ draw_border(n, true, (m == mon));
+ } else if (n->client != NULL) {
+ draw_border(n, false, (m == mon));
+ } else {
+ update_colors_in(n->first_child, d, m);
+ update_colors_in(n->second_child, d, m);
+ }
+ }
+}
+
+void draw_border(node_t *n, bool focused_node, bool focused_monitor)
+{
+ if (n == NULL) {
+ return;
+ }
+
+ uint32_t border_color_pxl = get_border_color(focused_node, focused_monitor);
+ for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
+ if (f->client != NULL && f->client->border_width > 0) {
+ window_draw_border(f->id, border_color_pxl);
+ }
+ }
+}
+
+void window_draw_border(xcb_window_t win, uint32_t border_color_pxl)
+{
+ xcb_change_window_attributes(dpy, win, XCB_CW_BORDER_PIXEL, &border_color_pxl);
+}
+
+void adopt_orphans(void)
+{
+ xcb_query_tree_reply_t *qtr = xcb_query_tree_reply(dpy, xcb_query_tree(dpy, root), NULL);
+ if (qtr == NULL) {
+ return;
+ }
+
+ int len = xcb_query_tree_children_length(qtr);
+ xcb_window_t *wins = xcb_query_tree_children(qtr);
+
+ for (int i = 0; i < len; i++) {
+ uint32_t idx;
+ xcb_window_t win = wins[i];
+ if (xcb_ewmh_get_wm_desktop_reply(ewmh, xcb_ewmh_get_wm_desktop(ewmh, win), &idx, NULL) == 1) {
+ schedule_window(win);
+ }
+ }
+
+ free(qtr);
+}
+
+uint32_t get_border_color(bool focused_node, bool focused_monitor)
+{
+ if (focused_monitor && focused_node) {
+ return get_color_pixel(focused_border_color);
+ } else if (focused_node) {
+ return get_color_pixel(active_border_color);
+ } else {
+ return get_color_pixel(normal_border_color);
+ }
+}
+
+void initialize_floating_rectangle(node_t *n)
+{
+ client_t *c = n->client;
+
+ xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, n->id), NULL);
+
+ if (geo != NULL) {
+ c->floating_rectangle = (xcb_rectangle_t) {geo->x, geo->y, geo->width, geo->height};
+ }
+
+ free(geo);
+}
+
+xcb_rectangle_t get_window_rectangle(node_t *n)
+{
+ client_t *c = n->client;
+ if (c != NULL) {
+ xcb_get_geometry_reply_t *g = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, n->id), NULL);
+ if (g != NULL) {
+ xcb_rectangle_t rect = (xcb_rectangle_t) {g->x, g->y, g->width, g->height};
+ free(g);
+ return rect;
+ }
+ }
+ return (xcb_rectangle_t) {0, 0, screen_width, screen_height};
+}
+
+bool move_client(coordinates_t *loc, int dx, int dy)
+{
+ node_t *n = loc->node;
+
+ if (n == NULL || n->client == NULL) {
+ return false;
+ }
+
+ monitor_t *pm = NULL;
+
+ if (IS_TILED(n->client)) {
+ if (!grabbing) {
+ return false;
+ }
+ xcb_window_t pwin = XCB_NONE;
+ query_pointer(&pwin, NULL);
+ if (pwin == n->id) {
+ return false;
+ }
+ coordinates_t dst;
+ bool is_managed = (pwin != XCB_NONE && locate_window(pwin, &dst));
+ if (is_managed && dst.monitor == loc->monitor && IS_TILED(dst.node->client)) {
+ swap_nodes(loc->monitor, loc->desktop, n, loc->monitor, loc->desktop, dst.node);
+ return true;
+ } else {
+ if (is_managed && dst.monitor == loc->monitor) {
+ return false;
+ } else {
+ xcb_point_t pt = {0, 0};
+ query_pointer(NULL, &pt);
+ pm = monitor_from_point(pt);
+ }
+ }
+ } else {
+ client_t *c = n->client;
+ xcb_rectangle_t rect = c->floating_rectangle;
+ int16_t x = rect.x + dx;
+ int16_t y = rect.y + dy;
+
+ if (focus_follows_pointer) {
+ listen_enter_notify(loc->desktop->root, false);
+ }
+
+ window_move(n->id, x, y);
+
+ if (focus_follows_pointer) {
+ listen_enter_notify(loc->desktop->root, true);
+ }
+
+ c->floating_rectangle.x = x;
+ c->floating_rectangle.y = y;
+ if (!grabbing) {
+ put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", loc->monitor->id, loc->desktop->id, loc->node->id, rect.width, rect.height, x, y);
+ }
+ pm = monitor_from_client(c);
+ }
+
+ if (pm == NULL || pm == loc->monitor) {
+ return true;
+ }
+
+ bool focused = (n == mon->desk->focus);
+ transfer_node(loc->monitor, loc->desktop, n, pm, pm->desk, pm->desk->focus);
+ loc->monitor = pm;
+ loc->desktop = pm->desk;
+ if (focused) {
+ focus_node(pm, pm->desk, n);
+ }
+
+ return true;
+}
+
+bool resize_client(coordinates_t *loc, resize_handle_t rh, int dx, int dy)
+{
+ node_t *n = loc->node;
+ if (n == NULL || n->client == NULL || n->client->state == STATE_FULLSCREEN) {
+ return false;
+ }
+ node_t *horizontal_fence = NULL, *vertical_fence = NULL;
+ xcb_rectangle_t rect = get_rectangle(NULL, n);
+ uint16_t width = rect.width, height = rect.height;
+ int16_t x = rect.x, y = rect.y;
+ if (n->client->state == STATE_TILED) {
+ if (rh & HANDLE_LEFT) {
+ vertical_fence = find_fence(n, DIR_WEST);
+ } else if (rh & HANDLE_RIGHT) {
+ vertical_fence = find_fence(n, DIR_EAST);
+ }
+ if (rh & HANDLE_TOP) {
+ horizontal_fence = find_fence(n, DIR_NORTH);
+ } else if (rh & HANDLE_BOTTOM) {
+ horizontal_fence = find_fence(n, DIR_SOUTH);
+ }
+ if (vertical_fence == NULL && horizontal_fence == NULL) {
+ return false;
+ }
+ if (vertical_fence != NULL) {
+ double sr = vertical_fence->split_ratio + (double) dx / vertical_fence->rectangle.width;
+ sr = MAX(0, sr);
+ sr = MIN(1, sr);
+ vertical_fence->split_ratio = sr;
+ }
+ if (horizontal_fence != NULL) {
+ double sr = horizontal_fence->split_ratio + (double) dy / horizontal_fence->rectangle.height;
+ sr = MAX(0, sr);
+ sr = MIN(1, sr);
+ horizontal_fence->split_ratio = sr;
+ }
+ arrange(loc->monitor, loc->desktop);
+ } else {
+ int w = width + dx * (rh & HANDLE_LEFT ? -1 : (rh & HANDLE_RIGHT ? 1 : 0));
+ int h = height + dy * (rh & HANDLE_TOP ? -1 : (rh & HANDLE_BOTTOM ? 1 : 0));
+ width = MAX(1, w);
+ height = MAX(1, h);
+ apply_size_hints(n->client, &width, &height);
+ if (rh & HANDLE_LEFT) {
+ x += rect.width - width;
+ }
+ if (rh & HANDLE_TOP) {
+ y += rect.height - height;
+ }
+ n->client->floating_rectangle = (xcb_rectangle_t) {x, y, width, height};
+ if (n->client->state == STATE_FLOATING) {
+ if (focus_follows_pointer) {
+ listen_enter_notify(loc->desktop->root, false);
+ }
+
+ window_move_resize(n->id, x, y, width, height);
+
+ if (focus_follows_pointer) {
+ listen_enter_notify(loc->desktop->root, true);
+ }
+
+ if (!grabbing) {
+ put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", loc->monitor->id, loc->desktop->id, loc->node->id, width, height, x, y);
+ }
+ } else {
+ arrange(loc->monitor, loc->desktop);
+ }
+ }
+ return true;
+}
+
+/* taken from awesomeWM */
+void apply_size_hints(client_t *c, uint16_t *width, uint16_t *height)
+{
+ if (!honor_size_hints) {
+ return;
+ }
+
+ int32_t minw = 0, minh = 0;
+ int32_t basew = 0, baseh = 0, real_basew = 0, real_baseh = 0;
+
+ if (c->state == STATE_FULLSCREEN) {
+ return;
+ }
+
+ if (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_BASE_SIZE) {
+ basew = c->size_hints.base_width;
+ baseh = c->size_hints.base_height;
+ real_basew = basew;
+ real_baseh = baseh;
+ } else if (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE) {
+ /* base size is substituted with min size if not specified */
+ basew = c->size_hints.min_width;
+ baseh = c->size_hints.min_height;
+ }
+
+ if (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE) {
+ minw = c->size_hints.min_width;
+ minh = c->size_hints.min_height;
+ } else if (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_BASE_SIZE) {
+ /* min size is substituted with base size if not specified */
+ minw = c->size_hints.base_width;
+ minh = c->size_hints.base_height;
+ }
+
+ /* Handle the size aspect ratio */
+ if (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_ASPECT &&
+ c->size_hints.min_aspect_den > 0 &&
+ c->size_hints.max_aspect_den > 0 &&
+ *height > real_baseh &&
+ *width > real_basew) {
+ /* ICCCM mandates:
+ * If a base size is provided along with the aspect ratio fields, the base size should be subtracted from the
+ * window size prior to checking that the aspect ratio falls in range. If a base size is not provided, nothing
+ * should be subtracted from the window size. (The minimum size is not to be used in place of the base size for
+ * this purpose.)
+ */
+ double dx = *width - real_basew;
+ double dy = *height - real_baseh;
+ double ratio = dx / dy;
+ double min = c->size_hints.min_aspect_num / (double) c->size_hints.min_aspect_den;
+ double max = c->size_hints.max_aspect_num / (double) c->size_hints.max_aspect_den;
+
+ if (max > 0 && min > 0 && ratio > 0) {
+ if (ratio < min) {
+ /* dx is lower than allowed, make dy lower to compensate this (+ 0.5 to force proper rounding). */
+ dy = dx / min + 0.5;
+ *width = dx + real_basew;
+ *height = dy + real_baseh;
+ } else if (ratio > max) {
+ /* dx is too high, lower it (+0.5 for proper rounding) */
+ dx = dy * max + 0.5;
+ *width = dx + real_basew;
+ *height = dy + real_baseh;
+ }
+ }
+ }
+
+ /* Handle the minimum size */
+ *width = MAX(*width, minw);
+ *height = MAX(*height, minh);
+
+ /* Handle the maximum size */
+ if (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)
+ {
+ if (c->size_hints.max_width > 0) {
+ *width = MIN(*width, c->size_hints.max_width);
+ }
+ if (c->size_hints.max_height > 0) {
+ *height = MIN(*height, c->size_hints.max_height);
+ }
+ }
+
+ /* Handle the size increment */
+ if (c->size_hints.flags & (XCB_ICCCM_SIZE_HINT_P_RESIZE_INC | XCB_ICCCM_SIZE_HINT_BASE_SIZE) &&
+ c->size_hints.width_inc > 0 && c->size_hints.height_inc > 0) {
+ uint16_t t1 = *width, t2 = *height;
+ unsigned_subtract(t1, basew);
+ unsigned_subtract(t2, baseh);
+ *width -= t1 % c->size_hints.width_inc;
+ *height -= t2 % c->size_hints.height_inc;
+ }
+}
+
+void query_pointer(xcb_window_t *win, xcb_point_t *pt)
+{
+ xcb_query_pointer_reply_t *qpr = xcb_query_pointer_reply(dpy, xcb_query_pointer(dpy, root), NULL);
+
+ if (qpr != NULL) {
+ if (win != NULL) {
+ *win = qpr->child;
+ xcb_point_t pt = {qpr->root_x, qpr->root_y};
+ for (stacking_list_t *s = stack_tail; s != NULL; s = s->prev) {
+ if (!s->node->client->shown || s->node->hidden) {
+ continue;
+ }
+ xcb_rectangle_t rect = get_rectangle(NULL, s->node);
+ if (is_inside(pt, rect)) {
+ if (s->node->id == qpr->child || is_presel_window(qpr->child)) {
+ *win = s->node->id;
+ }
+ break;
+ }
+ }
+ }
+ if (pt != NULL) {
+ *pt = (xcb_point_t) {qpr->root_x, qpr->root_y};
+ }
+ }
+
+ free(qpr);
+}
+
+void window_border_width(xcb_window_t win, uint32_t bw)
+{
+ uint32_t values[] = {bw};
+ xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_BORDER_WIDTH, values);
+}
+
+void window_move(xcb_window_t win, int16_t x, int16_t y)
+{
+ uint32_t values[] = {x, y};
+ xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_X_Y, values);
+}
+
+void window_resize(xcb_window_t win, uint16_t w, uint16_t h)
+{
+ uint32_t values[] = {w, h};
+ xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_WIDTH_HEIGHT, values);
+}
+
+void window_move_resize(xcb_window_t win, int16_t x, int16_t y, uint16_t w, uint16_t h)
+{
+ uint32_t values[] = {x, y, w, h};
+ xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_X_Y_WIDTH_HEIGHT, values);
+}
+
+void window_center(monitor_t *m, client_t *c)
+{
+ xcb_rectangle_t *r = &c->floating_rectangle;
+ xcb_rectangle_t a = m->rectangle;
+ if (r->width >= a.width) {
+ r->x = a.x;
+ } else {
+ r->x = a.x + (a.width - r->width) / 2;
+ }
+ if (r->height >= a.height) {
+ r->y = a.y;
+ } else {
+ r->y = a.y + (a.height - r->height) / 2;
+ }
+ r->x -= c->border_width;
+ r->y -= c->border_width;
+}
+
+void window_stack(xcb_window_t w1, xcb_window_t w2, uint32_t mode)
+{
+ if (w2 == XCB_NONE) {
+ return;
+ }
+ uint16_t mask = XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE;
+ uint32_t values[] = {w2, mode};
+ xcb_configure_window(dpy, w1, mask, values);
+}
+
+void window_above(xcb_window_t w1, xcb_window_t w2)
+{
+ window_stack(w1, w2, XCB_STACK_MODE_ABOVE);
+}
+
+void window_below(xcb_window_t w1, xcb_window_t w2)
+{
+ window_stack(w1, w2, XCB_STACK_MODE_BELOW);
+}
+
+void window_lower(xcb_window_t win)
+{
+ uint32_t values[] = {XCB_STACK_MODE_BELOW};
+ xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_STACK_MODE, values);
+}
+
+void window_set_visibility(xcb_window_t win, bool visible)
+{
+ uint32_t values_off[] = {ROOT_EVENT_MASK & ~XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY};
+ uint32_t values_on[] = {ROOT_EVENT_MASK};
+ xcb_change_window_attributes(dpy, root, XCB_CW_EVENT_MASK, values_off);
+ if (visible) {
+ xcb_map_window(dpy, win);
+ } else {
+ xcb_unmap_window(dpy, win);
+ }
+ xcb_change_window_attributes(dpy, root, XCB_CW_EVENT_MASK, values_on);
+}
+
+void window_hide(xcb_window_t win)
+{
+ window_set_visibility(win, false);
+}
+
+void window_show(xcb_window_t win)
+{
+ window_set_visibility(win, true);
+}
+
+void update_input_focus(void)
+{
+ set_input_focus(mon->desk->focus);
+}
+
+void set_input_focus(node_t *n)
+{
+ if (n == NULL || n->client == NULL) {
+ clear_input_focus();
+ } else {
+ if (n->client->icccm_props.input_hint) {
+ xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_PARENT, n->id, XCB_CURRENT_TIME);
+ } else if (n->client->icccm_props.take_focus) {
+ send_client_message(n->id, ewmh->WM_PROTOCOLS, WM_TAKE_FOCUS);
+ }
+ }
+}
+
+void clear_input_focus(void)
+{
+ xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, root, XCB_CURRENT_TIME);
+}
+
+void center_pointer(xcb_rectangle_t r)
+{
+ if (grabbing) {
+ return;
+ }
+ int16_t cx = r.x + r.width / 2;
+ int16_t cy = r.y + r.height / 2;
+ xcb_warp_pointer(dpy, XCB_NONE, root, 0, 0, 0, 0, cx, cy);
+}
+
+void get_atom(char *name, xcb_atom_t *atom)
+{
+ xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(dpy, xcb_intern_atom(dpy, 0, strlen(name), name), NULL);
+ if (reply != NULL) {
+ *atom = reply->atom;
+ } else {
+ *atom = XCB_NONE;
+ }
+ free(reply);
+}
+
+void set_atom(xcb_window_t win, xcb_atom_t atom, uint32_t value)
+{
+ xcb_change_property(dpy, XCB_PROP_MODE_REPLACE, win, atom, XCB_ATOM_CARDINAL, 32, 1, &value);
+}
+
+void send_client_message(xcb_window_t win, xcb_atom_t property, xcb_atom_t value)
+{
+ xcb_client_message_event_t *e = calloc(32, 1);
+
+ e->response_type = XCB_CLIENT_MESSAGE;
+ e->window = win;
+ e->type = property;
+ e->format = 32;
+ e->data.data32[0] = value;
+ e->data.data32[1] = XCB_CURRENT_TIME;
+
+ xcb_send_event(dpy, false, win, XCB_EVENT_MASK_NO_EVENT, (char *) e);
+ xcb_flush(dpy);
+ free(e);
+}
--- /dev/null
+/* Copyright (c) 2012, 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:
+ *
+ * 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.
+ *
+ * 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
+ * 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.
+ */
+
+#ifndef BSPWM_WINDOW_H
+#define BSPWM_WINDOW_H
+
+#include <stdarg.h>
+#include <xcb/xcb.h>
+#include <xcb/xcb_event.h>
+#include <xcb/xcb_icccm.h>
+#include "types.h"
+
+void schedule_window(xcb_window_t win);
+void manage_window(xcb_window_t win, rule_consequence_t *csq, int fd);
+void set_window_state(xcb_window_t win, xcb_icccm_wm_state_t state);
+void unmanage_window(xcb_window_t win);
+bool is_presel_window(xcb_window_t win);
+void initialize_presel_feedback(node_t *n);
+void draw_presel_feedback(monitor_t *m, desktop_t *d, node_t *n);
+void refresh_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n);
+void show_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n);
+void hide_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n);
+void update_colors(void);
+void update_colors_in(node_t *n, desktop_t *d, monitor_t *m);
+void draw_border(node_t *n, bool focused_node, bool focused_monitor);
+void window_draw_border(xcb_window_t win, uint32_t border_color_pxl);
+void adopt_orphans(void);
+uint32_t get_border_color(bool focused_node, bool focused_monitor);
+void initialize_floating_rectangle(node_t *n);
+xcb_rectangle_t get_window_rectangle(node_t *n);
+bool move_client(coordinates_t *loc, int dx, int dy);
+bool resize_client(coordinates_t *loc, resize_handle_t rh, int dx, int dy);
+void apply_size_hints(client_t *c, uint16_t *width, uint16_t *height);
+void query_pointer(xcb_window_t *win, xcb_point_t *pt);
+void window_border_width(xcb_window_t win, uint32_t bw);
+void window_move(xcb_window_t win, int16_t x, int16_t y);
+void window_resize(xcb_window_t win, uint16_t w, uint16_t h);
+void window_move_resize(xcb_window_t win, int16_t x, int16_t y, uint16_t w, uint16_t h);
+void window_center(monitor_t *m, client_t *c);
+void window_stack(xcb_window_t w1, xcb_window_t w2, uint32_t mode);
+void window_above(xcb_window_t w1, xcb_window_t w2);
+void window_below(xcb_window_t w1, xcb_window_t w2);
+void window_lower(xcb_window_t win);
+void window_set_visibility(xcb_window_t win, bool visible);
+void window_hide(xcb_window_t win);
+void window_show(xcb_window_t win);
+void update_input_focus(void);
+void set_input_focus(node_t *n);
+void clear_input_focus(void);
+void center_pointer(xcb_rectangle_t r);
+void get_atom(char *name, xcb_atom_t *atom);
+void set_atom(xcb_window_t win, xcb_atom_t atom, uint32_t value);
+void send_client_message(xcb_window_t win, xcb_atom_t property, xcb_atom_t value);
+
+#endif
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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 <stdlib.h>
-#include "bspwm.h"
-#include "window.h"
-#include "subscribe.h"
-#include "ewmh.h"
-#include "tree.h"
-#include "stack.h"
-
-stacking_list_t *make_stack(node_t *n)
-{
- stacking_list_t *s = calloc(1, sizeof(stacking_list_t));
- s->node = n;
- s->prev = s->next = NULL;
- return s;
-}
-
-void stack_insert_after(stacking_list_t *a, node_t *n)
-{
- stacking_list_t *s = make_stack(n);
- if (a == NULL) {
- stack_head = stack_tail = s;
- } else {
- if (a->node == n) {
- free(s);
- return;
- }
- remove_stack_node(n);
- stacking_list_t *b = a->next;
- if (b != NULL) {
- b->prev = s;
- }
- s->next = b;
- s->prev = a;
- a->next = s;
- if (stack_tail == a) {
- stack_tail = s;
- }
- }
-}
-
-void stack_insert_before(stacking_list_t *a, node_t *n)
-{
- stacking_list_t *s = make_stack(n);
- if (a == NULL) {
- stack_head = stack_tail = s;
- } else {
- if (a->node == n) {
- free(s);
- return;
- }
- remove_stack_node(n);
- stacking_list_t *b = a->prev;
- if (b != NULL) {
- b->next = s;
- }
- s->prev = b;
- s->next = a;
- a->prev = s;
- if (stack_head == a) {
- stack_head = s;
- }
- }
-}
-
-void remove_stack(stacking_list_t *s)
-{
- if (s == NULL) {
- return;
- }
- stacking_list_t *a = s->prev;
- stacking_list_t *b = s->next;
- if (a != NULL) {
- a->next = b;
- }
- if (b != NULL) {
- b->prev = a;
- }
- if (s == stack_head) {
- stack_head = b;
- }
- if (s == stack_tail) {
- stack_tail = a;
- }
- free(s);
-}
-
-void remove_stack_node(node_t *n)
-{
- for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
- for (stacking_list_t *s = stack_head; s != NULL; s = s->next) {
- if (s->node == f) {
- remove_stack(s);
- break;
- }
- }
- }
-}
-
-int stack_level(client_t *c)
-{
- int layer_level = (c->layer == LAYER_NORMAL ? 1 : (c->layer == LAYER_BELOW ? 0 : 2));
- int state_level = (IS_TILED(c) ? 0 : (IS_FLOATING(c) ? 2 : 1));
- return 3 * layer_level + state_level;
-}
-
-int stack_cmp(client_t *c1, client_t *c2)
-{
- return stack_level(c1) - stack_level(c2);
-}
-
-stacking_list_t *limit_above(node_t *n)
-{
- stacking_list_t *s = stack_head;
- while (s != NULL && stack_cmp(n->client, s->node->client) >= 0) {
- s = s->next;
- }
- if (s == NULL) {
- s = stack_tail;
- }
- if (s->node == n) {
- s = s->prev;
- }
- return s;
-}
-
-stacking_list_t *limit_below(node_t *n)
-{
- stacking_list_t *s = stack_tail;
- while (s != NULL && stack_cmp(n->client, s->node->client) <= 0) {
- s = s->prev;
- }
- if (s == NULL) {
- s = stack_head;
- }
- if (s->node == n) {
- s = s->next;
- }
- return s;
-}
-
-void stack(desktop_t *d, node_t *n, bool focused)
-{
- for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
- if (f->client == NULL || (IS_FLOATING(f->client) && !auto_raise)) {
- continue;
- }
-
- if (stack_head == NULL) {
- stack_insert_after(NULL, f);
- } else {
- stacking_list_t *s = (focused ? limit_above(f) : limit_below(f));
- if (s == NULL) {
- continue;
- }
- int i = stack_cmp(f->client, s->node->client);
- if (i < 0 || (i == 0 && !focused)) {
- stack_insert_before(s, f);
- window_below(f->id, s->node->id);
- put_status(SBSC_MASK_NODE_STACK, "node_stack 0x%08X below 0x%08X\n", f->id, s->node->id);
- } else {
- stack_insert_after(s, f);
- window_above(f->id, s->node->id);
- put_status(SBSC_MASK_NODE_STACK, "node_stack 0x%08X above 0x%08X\n", f->id, s->node->id);
- }
- }
- }
-
- ewmh_update_client_list(true);
- restack_presel_feedbacks(d);
-}
-
-void restack_presel_feedbacks(desktop_t *d)
-{
- stacking_list_t *s = stack_tail;
- while (s != NULL && !IS_TILED(s->node->client)) {
- s = s->prev;
- }
- if (s != NULL) {
- restack_presel_feedbacks_in(d->root, s->node);
- }
-}
-
-void restack_presel_feedbacks_in(node_t *r, node_t *n)
-{
- if (r == NULL) {
- return;
- } else {
- if (r->presel != NULL) {
- window_above(r->presel->feedback, n->id);
- }
- restack_presel_feedbacks_in(r->first_child, n);
- restack_presel_feedbacks_in(r->second_child, n);
- }
-}
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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.
- */
-
-#ifndef BSPWM_STACK_H
-#define BSPWM_STACK_H
-
-stacking_list_t *make_stack(node_t *n);
-void stack_insert_after(stacking_list_t *a, node_t *n);
-void stack_insert_before(stacking_list_t *a, node_t *n);
-void remove_stack(stacking_list_t *s);
-void remove_stack_node(node_t *n);
-int stack_level(client_t *c);
-int stack_cmp(client_t *c1, client_t *c2);
-stacking_list_t *limit_above(node_t *n);
-stacking_list_t *limit_below(node_t *n);
-void stack(desktop_t *d, node_t *n, bool focused);
-void restack_presel_feedbacks(desktop_t *d);
-void restack_presel_feedbacks_in(node_t *r, node_t *n);
-
-#endif
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <stdarg.h>
-#include "bspwm.h"
-#include "desktop.h"
-#include "settings.h"
-#include "subscribe.h"
-
-subscriber_list_t *make_subscriber_list(FILE *stream, int field)
-{
- subscriber_list_t *sb = calloc(1, sizeof(subscriber_list_t));
- sb->prev = sb->next = NULL;
- sb->stream = stream;
- sb->field = field;
- return sb;
-}
-
-void remove_subscriber(subscriber_list_t *sb)
-{
- if (sb == NULL) {
- return;
- }
- subscriber_list_t *a = sb->prev;
- subscriber_list_t *b = sb->next;
- if (a != NULL) {
- a->next = b;
- }
- if (b != NULL) {
- b->prev = a;
- }
- if (sb == subscribe_head) {
- subscribe_head = b;
- }
- if (sb == subscribe_tail) {
- subscribe_tail = a;
- }
- fclose(sb->stream);
- free(sb);
-}
-
-void add_subscriber(FILE *stream, int field)
-{
- subscriber_list_t *sb = make_subscriber_list(stream, field);
- if (subscribe_head == NULL) {
- subscribe_head = subscribe_tail = sb;
- } else {
- subscribe_tail->next = sb;
- sb->prev = subscribe_tail;
- subscribe_tail = sb;
- }
- if (sb->field & SBSC_MASK_REPORT) {
- print_report(sb->stream);
- }
-}
-
-int print_report(FILE *stream)
-{
- fprintf(stream, "%s", status_prefix);
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- fprintf(stream, "%c%s", (mon == m ? 'M' : 'm'), m->name);
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
- char c = (is_urgent(d) ? 'u' : (d->root == NULL ? 'f' : 'o'));
- if (m->desk == d) {
- c = toupper(c);
- }
- fprintf(stream, ":%c%s", c, d->name);
- }
- if (m->desk != NULL) {
- fprintf(stream, ":L%c", LAYOUT_CHR(m->desk->layout));
- if (m->desk->focus != NULL) {
- node_t *n = m->desk->focus;
- if (n->client != NULL) {
- fprintf(stream, ":T%c", STATE_CHR(n->client->state));
- } else {
- fprintf(stream, ":T@");
- }
- int i = 0;
- char flags[4];
- if (n->sticky) {
- flags[i++] = 'S';
- }
- if (n->private) {
- flags[i++] = 'P';
- }
- if (n->locked) {
- flags[i++] = 'L';
- }
- flags[i] = '\0';
- fprintf(stream, ":G%s", flags);
- }
- }
- if (m != mon_tail) {
- fprintf(stream, "%s", ":");
- }
- }
- fprintf(stream, "%s", "\n");
- return fflush(stream);
-}
-
-void put_status(subscriber_mask_t mask, ...)
-{
- subscriber_list_t *sb = subscribe_head;
- int ret;
- while (sb != NULL) {
- subscriber_list_t *next = sb->next;
- if (sb->field & mask) {
- if (mask == SBSC_MASK_REPORT) {
- ret = print_report(sb->stream);
- } else {
- char *fmt;
- va_list args;
- va_start(args, mask);
- fmt = va_arg(args, char *);
- vfprintf(sb->stream, fmt, args);
- va_end(args);
- ret = fflush(sb->stream);
- }
- if (ret != 0) {
- remove_subscriber(sb);
- }
- }
- sb = next;
- }
-}
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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.
- */
-
-#ifndef BSPWM_SUBSCRIBE_H
-#define BSPWM_SUBSCRIBE_H
-
-typedef enum {
- SBSC_MASK_REPORT = 1 << 0,
- SBSC_MASK_MONITOR_ADD = 1 << 1,
- SBSC_MASK_MONITOR_RENAME = 1 << 2,
- SBSC_MASK_MONITOR_REMOVE = 1 << 3,
- SBSC_MASK_MONITOR_SWAP = 1 << 4,
- SBSC_MASK_MONITOR_FOCUS = 1 << 5,
- SBSC_MASK_MONITOR_GEOMETRY = 1 << 6,
- SBSC_MASK_DESKTOP_ADD = 1 << 7,
- SBSC_MASK_DESKTOP_RENAME = 1 << 8,
- SBSC_MASK_DESKTOP_REMOVE = 1 << 9,
- SBSC_MASK_DESKTOP_SWAP = 1 << 10,
- SBSC_MASK_DESKTOP_TRANSFER = 1 << 11,
- SBSC_MASK_DESKTOP_FOCUS = 1 << 12,
- SBSC_MASK_DESKTOP_ACTIVATE = 1 << 13,
- SBSC_MASK_DESKTOP_LAYOUT = 1 << 14,
- SBSC_MASK_NODE_MANAGE = 1 << 15,
- SBSC_MASK_NODE_UNMANAGE = 1 << 16,
- SBSC_MASK_NODE_SWAP = 1 << 17,
- SBSC_MASK_NODE_TRANSFER = 1 << 18,
- SBSC_MASK_NODE_FOCUS = 1 << 19,
- SBSC_MASK_NODE_PRESEL = 1 << 20,
- SBSC_MASK_NODE_STACK = 1 << 21,
- SBSC_MASK_NODE_ACTIVATE = 1 << 22,
- SBSC_MASK_NODE_GEOMETRY = 1 << 23,
- SBSC_MASK_NODE_STATE = 1 << 24,
- SBSC_MASK_NODE_FLAG = 1 << 25,
- SBSC_MASK_NODE_LAYER = 1 << 26,
- SBSC_MASK_POINTER_ACTION = 1 << 27,
- SBSC_MASK_MONITOR = (1 << 7) - (1 << 1),
- SBSC_MASK_DESKTOP = (1 << 15) - (1 << 7),
- SBSC_MASK_NODE = (1 << 28) - (1 << 15),
- SBSC_MASK_ALL = (1 << 28) - 1
-} subscriber_mask_t;
-
-subscriber_list_t *make_subscriber_list(FILE *stream, int field);
-void remove_subscriber(subscriber_list_t *sb);
-void add_subscriber(FILE *stream, int field);
-int print_report(FILE *stream);
-void put_status(subscriber_mask_t mask, ...);
-
-#endif
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include "bspwm.h"
-#include "desktop.h"
-#include "ewmh.h"
-#include "history.h"
-#include "monitor.h"
-#include "query.h"
-#include "geometry.h"
-#include "subscribe.h"
-#include "settings.h"
-#include "pointer.h"
-#include "stack.h"
-#include "window.h"
-#include "tree.h"
-
-void arrange(monitor_t *m, desktop_t *d)
-{
- if (d->root == NULL) {
- return;
- }
-
- layout_t l = d->layout;
-
- if (single_monocle && tiled_count(d->root) <= 1) {
- l = LAYOUT_MONOCLE;
- }
-
- xcb_rectangle_t rect = m->rectangle;
-
- if (!paddingless_monocle || l != LAYOUT_MONOCLE) {
- rect.x += m->padding.left + d->padding.left;
- rect.y += m->padding.top + d->padding.top;
- rect.width -= m->padding.left + d->padding.left + d->padding.right + m->padding.right;
- rect.height -= m->padding.top + d->padding.top + d->padding.bottom + m->padding.bottom;
- }
-
- if (!gapless_monocle || l != LAYOUT_MONOCLE) {
- rect.x += d->window_gap;
- rect.y += d->window_gap;
- rect.width -= d->window_gap;
- rect.height -= d->window_gap;
- }
-
- if (focus_follows_pointer) {
- listen_enter_notify(d->root, false);
- }
-
- apply_layout(m, d, d->root, l, rect, rect);
-
- if (focus_follows_pointer) {
- listen_enter_notify(d->root, true);
- }
-}
-
-void apply_layout(monitor_t *m, desktop_t *d, node_t *n, layout_t l, xcb_rectangle_t rect, xcb_rectangle_t root_rect)
-{
- if (n == NULL) {
- return;
- }
-
- n->rectangle = rect;
-
- if (pointer_follows_focus && mon->desk->focus == n) {
- xcb_rectangle_t r = rect;
- r.width -= d->window_gap;
- r.height -= d->window_gap;
- center_pointer(r);
- }
-
- if (n->presel != NULL) {
- draw_presel_feedback(m, d, n);
- }
-
- if (is_leaf(n)) {
-
- if (n->client == NULL) {
- return;
- }
-
- unsigned int bw;
- if ((borderless_monocle && n->client->state == STATE_TILED && l == LAYOUT_MONOCLE)
- || n->client->state == STATE_FULLSCREEN) {
- bw = 0;
- } else {
- bw = n->client->border_width;
- }
-
- xcb_rectangle_t r;
- xcb_rectangle_t cr = get_window_rectangle(n);
- client_state_t s = n->client->state;
- if (s == STATE_TILED || s == STATE_PSEUDO_TILED) {
- int wg = (gapless_monocle && l == LAYOUT_MONOCLE ? 0 : d->window_gap);
- /* tiled clients */
- if (s == STATE_TILED) {
- r = rect;
- int bleed = wg + 2 * bw;
- r.width = (bleed < r.width ? r.width - bleed : 1);
- r.height = (bleed < r.height ? r.height - bleed : 1);
- /* pseudo-tiled clients */
- } else {
- r = n->client->floating_rectangle;
- if (center_pseudo_tiled) {
- r.x = rect.x - bw + (rect.width - wg - r.width) / 2;
- r.y = rect.y - bw + (rect.height - wg - r.height) / 2;
- } else {
- r.x = rect.x;
- r.y = rect.y;
- }
- }
- n->client->tiled_rectangle = r;
- /* floating clients */
- } else if (s == STATE_FLOATING) {
- r = n->client->floating_rectangle;
- /* fullscreen clients */
- } else {
- r = m->rectangle;
- n->client->tiled_rectangle = r;
- }
-
- apply_size_hints(n->client, &r.width, &r.height);
-
- if (!rect_eq(r, cr)) {
- window_move_resize(n->id, r.x, r.y, r.width, r.height);
- if (!grabbing) {
- put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", m->id, d->id, n->id, r.width, r.height, r.x, r.y);
- }
- }
-
- window_border_width(n->id, bw);
-
- } else {
- xcb_rectangle_t first_rect;
- xcb_rectangle_t second_rect;
-
- if (l == LAYOUT_MONOCLE || n->first_child->vacant || n->second_child->vacant) {
- first_rect = second_rect = rect;
- } else {
- unsigned int fence;
- if (n->split_type == TYPE_VERTICAL) {
- fence = rect.width * n->split_ratio;
- first_rect = (xcb_rectangle_t) {rect.x, rect.y, fence, rect.height};
- second_rect = (xcb_rectangle_t) {rect.x + fence, rect.y, rect.width - fence, rect.height};
- } else {
- fence = rect.height * n->split_ratio;
- first_rect = (xcb_rectangle_t) {rect.x, rect.y, rect.width, fence};
- second_rect = (xcb_rectangle_t) {rect.x, rect.y + fence, rect.width, rect.height - fence};
- }
- }
-
- apply_layout(m, d, n->first_child, l, first_rect, root_rect);
- apply_layout(m, d, n->second_child, l, second_rect, root_rect);
- }
-}
-
-presel_t *make_presel(void)
-{
- presel_t *p = calloc(1, sizeof(presel_t));
- p->split_dir = DIR_EAST;
- p->split_ratio = split_ratio;
- p->feedback = XCB_NONE;
- return p;
-}
-
-void set_ratio(node_t *n, double rat)
-{
- if (n == NULL) {
- return;
- }
-
- n->split_ratio = rat;
-}
-
-void presel_dir(monitor_t *m, desktop_t *d, node_t *n, direction_t dir)
-{
- if (n->presel == NULL) {
- n->presel = make_presel();
- }
-
- n->presel->split_dir = dir;
-
- put_status(SBSC_MASK_NODE_PRESEL, "node_presel 0x%08X 0x%08X 0x%08X dir %s\n", m->id, d->id, n->id, SPLIT_DIR_STR(dir));
-}
-
-void presel_ratio(monitor_t *m, desktop_t *d, node_t *n, double ratio)
-{
- if (n->presel == NULL) {
- n->presel = make_presel();
- }
-
- n->presel->split_ratio = ratio;
-
- put_status(SBSC_MASK_NODE_PRESEL, "node_presel 0x%08X 0x%08X 0x%08X ratio %lf\n", m->id, d->id, n->id, ratio);
-}
-
-void cancel_presel(monitor_t *m, desktop_t *d, node_t *n)
-{
- if (n->presel == NULL) {
- return;
- }
-
- if (focus_follows_pointer) {
- listen_enter_notify(n, false);
- }
-
- if (n->presel->feedback != XCB_NONE) {
- xcb_destroy_window(dpy, n->presel->feedback);
- }
-
- if (focus_follows_pointer) {
- listen_enter_notify(n, true);
- }
-
- free(n->presel);
- n->presel = NULL;
-
- put_status(SBSC_MASK_NODE_PRESEL, "node_presel 0x%08X 0x%08X 0x%08X cancel\n", m->id, d->id, n->id);
-}
-
-void cancel_presel_in(monitor_t *m, desktop_t *d, node_t *n)
-{
- if (n == NULL) {
- return;
- }
- cancel_presel(m, d, n);
- cancel_presel_in(m, d, n->first_child);
- cancel_presel_in(m, d, n->second_child);
-}
-
-node_t *find_public(desktop_t *d)
-{
- unsigned int b_manual_area = 0;
- unsigned int b_automatic_area = 0;
- node_t *b_manual = NULL;
- node_t *b_automatic = NULL;
- for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
- if (n->vacant) {
- continue;
- }
- unsigned int n_area = node_area(d, n);
- if (n_area > b_manual_area && (n->presel != NULL || !n->private)) {
- b_manual = n;
- b_manual_area = n_area;
- }
- if (n_area > b_automatic_area &&
- n->presel == NULL && !n->private && private_count(n->parent) == 0) {
- b_automatic = n;
- b_automatic_area = n_area;
- }
- }
- if (b_automatic != NULL) {
- return b_automatic;
- } else {
- return b_manual;
- }
-}
-
-node_t *insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f)
-{
- if (d == NULL || n == NULL) {
- return NULL;
- }
-
- /* n: inserted node */
- /* c: new internal node */
- /* f: focus or insertion anchor */
- /* p: parent of focus */
- /* g: grand parent of focus */
-
- if (f == NULL) {
- f = d->root;
- }
-
- if (f == NULL) {
- d->root = n;
- } else if (IS_RECEPTACLE(f) && f->presel == NULL) {
- node_t *p = f->parent;
- if (p != NULL) {
- if (is_first_child(f)) {
- p->first_child = n;
- } else {
- p->second_child = n;
- }
- } else {
- d->root = n;
- }
- n->parent = p;
- free(f);
- f = NULL;
- } else {
- node_t *c = make_node(XCB_NONE);
- node_t *p = f->parent;
- if (f->presel == NULL && (f->private || private_count(f->parent) > 0)) {
- node_t *k = find_public(d);
- if (k != NULL) {
- f = k;
- p = f->parent;
- }
- if (f->presel == NULL && (f->private || private_count(f->parent) > 0)) {
- xcb_rectangle_t rect = get_rectangle(d, f);
- presel_dir(m, d, f, (rect.width >= rect.height ? DIR_EAST : DIR_SOUTH));
- }
- }
- if (f->presel == NULL && p != NULL && p->vacant) {
- f = p;
- p = f->parent;
- }
- n->parent = c;
- c->birth_rotation = f->birth_rotation;
- if (f->presel == NULL) {
- if (p == NULL) {
- if (initial_polarity == FIRST_CHILD) {
- c->first_child = n;
- c->second_child = f;
- } else {
- c->first_child = f;
- c->second_child = n;
- }
- if (m->rectangle.width > m->rectangle.height) {
- c->split_type = TYPE_VERTICAL;
- } else {
- c->split_type = TYPE_HORIZONTAL;
- }
- f->parent = c;
- d->root = c;
- } else {
- node_t *g = p->parent;
- c->parent = g;
- if (g != NULL) {
- if (is_first_child(p)) {
- g->first_child = c;
- } else {
- g->second_child = c;
- }
- } else {
- d->root = c;
- }
- c->split_type = p->split_type;
- c->split_ratio = p->split_ratio;
- p->parent = c;
- int rot;
- if (is_first_child(f)) {
- c->first_child = n;
- c->second_child = p;
- rot = 90;
- } else {
- c->first_child = p;
- c->second_child = n;
- rot = 270;
- }
- n->birth_rotation = rot;
- if (!n->vacant) {
- rotate_tree(p, rot);
- }
- }
- } else {
- if (p != NULL) {
- if (is_first_child(f)) {
- p->first_child = c;
- } else {
- p->second_child = c;
- }
- }
- c->split_ratio = f->presel->split_ratio;
- c->parent = p;
- f->parent = c;
- f->birth_rotation = 0;
- switch (f->presel->split_dir) {
- case DIR_WEST:
- c->split_type = TYPE_VERTICAL;
- c->first_child = n;
- c->second_child = f;
- break;
- case DIR_EAST:
- c->split_type = TYPE_VERTICAL;
- c->first_child = f;
- c->second_child = n;
- break;
- case DIR_NORTH:
- c->split_type = TYPE_HORIZONTAL;
- c->first_child = n;
- c->second_child = f;
- break;
- case DIR_SOUTH:
- c->split_type = TYPE_HORIZONTAL;
- c->first_child = f;
- c->second_child = n;
- break;
- }
- if (d->root == f) {
- d->root = c;
- }
- cancel_presel(m, d, f);
- }
- }
-
- propagate_flags_upward(m, d, n);
-
- if (d->focus == NULL && is_focusable(n)) {
- d->focus = n;
- }
-
- return f;
-}
-
-void insert_receptacle(monitor_t *m, desktop_t *d, node_t *n)
-{
- node_t *r = make_node(XCB_NONE);
- insert_node(m, d, r, n);
-}
-
-bool activate_node(monitor_t *m, desktop_t *d, node_t *n)
-{
- if (d == NULL) {
- d = history_last_desktop(m, NULL);
- if (d == NULL) {
- d = m->desk_head;
- }
- }
-
- if (d == NULL) {
- return false;
- }
-
- if (n == NULL && d->root != NULL) {
- n = history_last_node(d, NULL);
- if (n == NULL) {
- n = first_focusable_leaf(d->root);
- }
- }
-
- if (d == mon->desk || (n != NULL && !is_focusable(n))) {
- return false;
- }
-
- if (n != NULL) {
- if (d->focus != NULL && n != d->focus) {
- neutralize_occluding_windows(m, d, n);
- }
- stack(d, n, true);
- if (d->focus != n) {
- for (node_t *f = first_extrema(d->focus); f != NULL; f = next_leaf(f, d->focus)) {
- if (f->client != NULL && !is_descendant(f, n)) {
- window_draw_border(f->id, get_border_color(false, (m == mon)));
- }
- }
- }
- draw_border(n, true, (m == mon));
- }
-
- activate_desktop(m, d);
-
- d->focus = n;
- history_add(m, d, n);
-
- put_status(SBSC_MASK_REPORT);
-
- if (n == NULL) {
- return true;
- }
-
- put_status(SBSC_MASK_NODE_ACTIVATE, "node_activate 0x%08X 0x%08X 0x%08X\n", m->id, d->id, n->id);
-
- return true;
-}
-
-void transfer_sticky_nodes(monitor_t *m, desktop_t *ds, desktop_t *dd, node_t *n)
-{
- if (n == NULL) {
- return;
- } else if (n->sticky) {
- transfer_node(m, ds, n, m, dd, dd->focus);
- } else {
- /* we need references to the children because n might be freed after
- * the first recursive call */
- node_t *first_child = n->first_child;
- node_t *second_child = n->second_child;
- transfer_sticky_nodes(m, ds, dd, first_child);
- transfer_sticky_nodes(m, ds, dd, second_child);
- }
-}
-
-bool focus_node(monitor_t *m, desktop_t *d, node_t *n)
-{
- if (m == NULL) {
- m = history_last_monitor(NULL);
- if (m == NULL) {
- m = mon_head;
- }
- }
-
- if (m == NULL) {
- return false;
- }
-
- if (d == NULL) {
- d = history_last_desktop(m, NULL);
- if (d == NULL) {
- d = m->desk_head;
- }
- }
-
- if (d == NULL) {
- return false;
- }
-
- if (n == NULL && d->root != NULL) {
- n = history_last_node(d, NULL);
- if (n == NULL) {
- n = first_focusable_leaf(d->root);
- }
- }
-
- if (n != NULL && !is_focusable(n)) {
- return false;
- }
-
- if ((mon != NULL && mon->desk != d) || n == NULL || n->client == NULL) {
- clear_input_focus();
- }
-
- if (m->sticky_count > 0 && d != m->desk) {
- sticky_still = false;
- transfer_sticky_nodes(m, m->desk, d, m->desk->root);
- sticky_still = true;
- if (n == NULL && d->focus != NULL) {
- n = d->focus;
- }
- }
-
- if (d->focus != NULL && n != d->focus) {
- neutralize_occluding_windows(m, d, n);
- }
-
- if (n != NULL && n->client != NULL && n->client->urgent) {
- set_urgent(m, d, n, false);
- }
-
- if (mon != m) {
- if (mon != NULL) {
- for (desktop_t *e = mon->desk_head; e != NULL; e = e->next) {
- draw_border(e->focus, true, false);
- }
- }
- for (desktop_t *e = m->desk_head; e != NULL; e = e->next) {
- if (e == d) {
- continue;
- }
- draw_border(e->focus, true, true);
- }
- }
-
- if (d->focus != n) {
- for (node_t *f = first_extrema(d->focus); f != NULL; f = next_leaf(f, d->focus)) {
- if (f->client != NULL && !is_descendant(f, n)) {
- window_draw_border(f->id, get_border_color(false, true));
- }
- }
- }
-
- draw_border(n, true, true);
-
- focus_desktop(m, d);
-
- d->focus = n;
- ewmh_update_active_window();
- history_add(m, d, n);
-
- put_status(SBSC_MASK_REPORT);
-
- if (n == NULL) {
- return true;
- }
-
- put_status(SBSC_MASK_NODE_FOCUS, "node_focus 0x%08X 0x%08X 0x%08X\n", m->id, d->id, n->id);
-
- stack(d, n, true);
- set_input_focus(n);
-
- if (pointer_follows_focus) {
- center_pointer(get_rectangle(d, n));
- }
-
- return true;
-}
-
-void hide_node(desktop_t *d, node_t *n)
-{
- if (n == NULL) {
- return;
- } else {
- if (!n->hidden) {
- if (n->presel != NULL && d->layout != LAYOUT_MONOCLE) {
- window_hide(n->presel->feedback);
- }
- if (n->client != NULL) {
- window_hide(n->id);
- }
- }
- if (n->client != NULL) {
- n->client->shown = false;
- }
- hide_node(d, n->first_child);
- hide_node(d, n->second_child);
- }
-}
-
-void show_node(desktop_t *d, node_t *n)
-{
- if (n == NULL) {
- return;
- } else {
- if (!n->hidden) {
- if (n->client != NULL) {
- window_show(n->id);
- }
- if (n->presel != NULL && d->layout != LAYOUT_MONOCLE) {
- window_show(n->presel->feedback);
- }
- }
- if (n->client != NULL) {
- n->client->shown = true;
- }
- show_node(d, n->first_child);
- show_node(d, n->second_child);
- }
-}
-
-node_t *make_node(uint32_t id)
-{
- if (id == XCB_NONE) {
- id = xcb_generate_id(dpy);
- }
- node_t *n = calloc(1, sizeof(node_t));
- n->id = id;
- n->parent = n->first_child = n->second_child = NULL;
- n->vacant = n->hidden = n->sticky = n->private = n->locked = false;
- n->split_ratio = split_ratio;
- n->split_type = TYPE_VERTICAL;
- n->birth_rotation = 0;
- n->presel = NULL;
- n->client = NULL;
- return n;
-}
-
-client_t *make_client(void)
-{
- client_t *c = calloc(1, sizeof(client_t));
- c->state = c->last_state = STATE_TILED;
- c->layer = c->last_layer = LAYER_NORMAL;
- snprintf(c->class_name, sizeof(c->class_name), "%s", MISSING_VALUE);
- snprintf(c->instance_name, sizeof(c->instance_name), "%s", MISSING_VALUE);
- c->border_width = border_width;
- c->urgent = false;
- c->shown = false;
- c->wm_flags = 0;
- c->icccm_props.input_hint = true;
- c->icccm_props.take_focus = false;
- c->icccm_props.delete_window = false;
- c->size_hints.flags = 0;
- return c;
-}
-
-void initialize_client(node_t *n)
-{
- xcb_window_t win = n->id;
- client_t *c = n->client;
- xcb_icccm_get_wm_protocols_reply_t protos;
- if (xcb_icccm_get_wm_protocols_reply(dpy, xcb_icccm_get_wm_protocols(dpy, win, ewmh->WM_PROTOCOLS), &protos, NULL) == 1) {
- for (uint32_t i = 0; i < protos.atoms_len; i++) {
- if (protos.atoms[i] == WM_TAKE_FOCUS) {
- c->icccm_props.take_focus = true;
- } else if (protos.atoms[i] == WM_DELETE_WINDOW) {
- c->icccm_props.delete_window = true;
- }
- }
- xcb_icccm_get_wm_protocols_reply_wipe(&protos);
- }
- xcb_ewmh_get_atoms_reply_t wm_state;
- if (xcb_ewmh_get_wm_state_reply(ewmh, xcb_ewmh_get_wm_state(ewmh, win), &wm_state, NULL) == 1) {
- for (unsigned int i = 0; i < wm_state.atoms_len && i < MAX_WM_STATES; i++) {
-#define HANDLE_WM_STATE(s) \
- if (wm_state.atoms[i] == ewmh->_NET_WM_STATE_##s) { \
- c->wm_flags |= WM_FLAG_##s; continue; \
- }
- HANDLE_WM_STATE(MODAL)
- HANDLE_WM_STATE(STICKY)
- HANDLE_WM_STATE(MAXIMIZED_VERT)
- HANDLE_WM_STATE(MAXIMIZED_HORZ)
- HANDLE_WM_STATE(SHADED)
- HANDLE_WM_STATE(SKIP_TASKBAR)
- HANDLE_WM_STATE(SKIP_PAGER)
- HANDLE_WM_STATE(HIDDEN)
- HANDLE_WM_STATE(FULLSCREEN)
- HANDLE_WM_STATE(ABOVE)
- HANDLE_WM_STATE(BELOW)
- HANDLE_WM_STATE(DEMANDS_ATTENTION)
-#undef HANDLE_WM_STATE
- }
- xcb_ewmh_get_atoms_reply_wipe(&wm_state);
- }
- xcb_icccm_wm_hints_t hints;
- if (xcb_icccm_get_wm_hints_reply(dpy, xcb_icccm_get_wm_hints(dpy, win), &hints, NULL) == 1
- && (hints.flags & XCB_ICCCM_WM_HINT_INPUT)) {
- c->icccm_props.input_hint = hints.input;
- }
- xcb_icccm_get_wm_normal_hints_reply(dpy, xcb_icccm_get_wm_normal_hints(dpy, win), &c->size_hints, NULL);
-}
-
-bool is_focusable(node_t *n)
-{
- for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
- if (f->client != NULL && !f->hidden) {
- return true;
- }
- }
- return false;
-}
-
-bool is_leaf(node_t *n)
-{
- return (n != NULL && n->first_child == NULL && n->second_child == NULL);
-}
-
-bool is_first_child(node_t *n)
-{
- return (n != NULL && n->parent != NULL && n->parent->first_child == n);
-}
-
-bool is_second_child(node_t *n)
-{
- return (n != NULL && n->parent != NULL && n->parent->second_child == n);
-}
-
-unsigned int clients_count_in(node_t *n)
-{
- if (n == NULL) {
- return 0;
- } else {
- return (n->client != NULL ? 1 : 0) +
- clients_count_in(n->first_child) +
- clients_count_in(n->second_child);
- }
-}
-
-node_t *brother_tree(node_t *n)
-{
- if (n == NULL || n->parent == NULL) {
- return NULL;
- }
- if (is_first_child(n)) {
- return n->parent->second_child;
- } else {
- return n->parent->first_child;
- }
-}
-
-node_t *first_extrema(node_t *n)
-{
- if (n == NULL) {
- return NULL;
- } else if (n->first_child == NULL) {
- return n;
- } else {
- return first_extrema(n->first_child);
- }
-}
-
-node_t *second_extrema(node_t *n)
-{
- if (n == NULL) {
- return NULL;
- } else if (n->second_child == NULL) {
- return n;
- } else {
- return second_extrema(n->second_child);
- }
-}
-
-node_t *first_focusable_leaf(node_t *n)
-{
- for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
- if (f->client != NULL && !f->hidden) {
- return f;
- }
- }
- return NULL;
-}
-
-node_t *next_leaf(node_t *n, node_t *r)
-{
- if (n == NULL) {
- return NULL;
- }
- node_t *p = n;
- while (is_second_child(p) && p != r) {
- p = p->parent;
- }
- if (p == r) {
- return NULL;
- }
- return first_extrema(p->parent->second_child);
-}
-
-node_t *prev_leaf(node_t *n, node_t *r)
-{
- if (n == NULL) {
- return NULL;
- }
- node_t *p = n;
- while (is_first_child(p) && p != r) {
- p = p->parent;
- }
- if (p == r) {
- return NULL;
- }
- return second_extrema(p->parent->first_child);
-}
-
-node_t *next_tiled_leaf(node_t *n, node_t *r)
-{
- node_t *next = next_leaf(n, r);
- if (next == NULL || (next->client != NULL && !next->vacant)) {
- return next;
- } else {
- return next_tiled_leaf(next, r);
- }
-}
-
-node_t *prev_tiled_leaf(node_t *n, node_t *r)
-{
- node_t *prev = prev_leaf(n, r);
- if (prev == NULL || (prev->client != NULL && !prev->vacant)) {
- return prev;
- } else {
- return prev_tiled_leaf(prev, r);
- }
-}
-
-/* Returns true if *b* is adjacent to *a* in the direction *dir* */
-bool is_adjacent(node_t *a, node_t *b, direction_t dir)
-{
- switch (dir) {
- case DIR_EAST:
- return (a->rectangle.x + a->rectangle.width) == b->rectangle.x;
- break;
- case DIR_SOUTH:
- return (a->rectangle.y + a->rectangle.height) == b->rectangle.y;
- break;
- case DIR_WEST:
- return (b->rectangle.x + b->rectangle.width) == a->rectangle.x;
- break;
- case DIR_NORTH:
- return (b->rectangle.y + b->rectangle.height) == a->rectangle.y;
- break;
- }
- return false;
-}
-
-node_t *find_fence(node_t *n, direction_t dir)
-{
- node_t *p;
-
- if (n == NULL) {
- return NULL;
- }
-
- p = n->parent;
-
- while (p != NULL) {
- if ((dir == DIR_NORTH && p->split_type == TYPE_HORIZONTAL && p->rectangle.y < n->rectangle.y) ||
- (dir == DIR_WEST && p->split_type == TYPE_VERTICAL && p->rectangle.x < n->rectangle.x) ||
- (dir == DIR_SOUTH && p->split_type == TYPE_HORIZONTAL && (p->rectangle.y + p->rectangle.height) > (n->rectangle.y + n->rectangle.height)) ||
- (dir == DIR_EAST && p->split_type == TYPE_VERTICAL && (p->rectangle.x + p->rectangle.width) > (n->rectangle.x + n->rectangle.width)))
- return p;
- p = p->parent;
- }
-
- return NULL;
-}
-
-/* returns *true* if *a* is a child of *b* */
-bool is_child(node_t *a, node_t *b)
-{
- if (a == NULL || b == NULL) {
- return false;
- }
- return (a->parent != NULL && a->parent == b);
-}
-
-/* returns *true* if *a* is a descendant of *b* */
-bool is_descendant(node_t *a, node_t *b)
-{
- if (a == NULL || b == NULL) {
- return false;
- }
- while (a != b && a != NULL) {
- a = a->parent;
- }
- return a == b;
-}
-
-bool find_by_id(uint32_t id, coordinates_t *loc)
-{
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
- node_t *n = find_by_id_in(d->root, id);
- if (n != NULL) {
- loc->monitor = m;
- loc->desktop = d;
- loc->node = n;
- return true;
- }
- }
- }
- return false;
-}
-
-node_t *find_by_id_in(node_t *r, uint32_t id)
-{
- if (r == NULL) {
- return NULL;
- } else if (r->id == id) {
- return r;
- } else {
- node_t *f = find_by_id_in(r->first_child, id);
- if (f != NULL) {
- return f;
- } else {
- return find_by_id_in(r->second_child, id);
- }
- }
-}
-
-void find_nearest_neighbor(coordinates_t *ref, coordinates_t *dst, direction_t dir, node_select_t sel)
-{
- if (ref->node == NULL) {
- return;
- }
-
- xcb_rectangle_t rect = get_rectangle(ref->desktop, ref->node);
- double md = DBL_MAX, mr = UINT32_MAX;
-
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- desktop_t *d = m->desk;
- for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f, d->root)) {
- coordinates_t loc = {m, d, f};
- xcb_rectangle_t r = get_rectangle(d, f);
- if (f == ref->node ||
- f->client == NULL ||
- f->hidden ||
- is_descendant(f, ref->node) ||
- !node_matches(&loc, ref, sel) ||
- !on_dir_side(rect, r, dir)) {
- continue;
- }
- double fd = distance_center(rect, r);
- uint32_t fr = history_rank(f);
- if (fd < md || (fd == md && fr < mr)) {
- md = fd;
- mr = fr;
- *dst = loc;
- }
- }
- }
-}
-
-unsigned int node_area(desktop_t *d, node_t *n)
-{
- if (n == NULL) {
- return 0;
- }
- return area(get_rectangle(d, n));
-}
-
-int tiled_count(node_t *n)
-{
- if (n == NULL) {
- return 0;
- }
- int cnt = 0;
- for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
- if (!f->hidden && f->client != NULL && IS_TILED(f->client)) {
- cnt++;
- }
- }
- return cnt;
-}
-
-void find_biggest(coordinates_t *ref, coordinates_t *dst, node_select_t sel)
-{
- unsigned int b_area = 0;
-
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
- for (node_t *f = first_extrema(d->root); f != NULL; f = next_leaf(f, d->root)) {
- coordinates_t loc = {m, d, f};
- if (f->client == NULL || f->vacant || !node_matches(&loc, ref, sel)) {
- continue;
- }
- unsigned int f_area = node_area(d, f);
- if (f_area > b_area) {
- *dst = loc;
- b_area = f_area;
- }
- }
- }
- }
-}
-
-void rotate_tree(node_t *n, int deg)
-{
- if (n == NULL || is_leaf(n) || deg == 0) {
- return;
- }
-
- node_t *tmp;
-
- if ((deg == 90 && n->split_type == TYPE_HORIZONTAL) ||
- (deg == 270 && n->split_type == TYPE_VERTICAL) ||
- deg == 180) {
- tmp = n->first_child;
- n->first_child = n->second_child;
- n->second_child = tmp;
- n->split_ratio = 1.0 - n->split_ratio;
- }
-
- if (deg != 180) {
- if (n->split_type == TYPE_HORIZONTAL) {
- n->split_type = TYPE_VERTICAL;
- } else if (n->split_type == TYPE_VERTICAL) {
- n->split_type = TYPE_HORIZONTAL;
- }
- }
-
- rotate_tree(n->first_child, deg);
- rotate_tree(n->second_child, deg);
-}
-
-void rotate_brother(node_t *n)
-{
- rotate_tree(brother_tree(n), n->birth_rotation);
-}
-
-void unrotate_tree(node_t *n, int rot)
-{
- if (rot == 0) {
- return;
- }
- rotate_tree(n, 360 - rot);
-}
-
-void unrotate_brother(node_t *n)
-{
- unrotate_tree(brother_tree(n), n->birth_rotation);
-}
-
-void flip_tree(node_t *n, flip_t flp)
-{
- if (n == NULL || is_leaf(n)) {
- return;
- }
-
- node_t *tmp;
-
- if ((flp == FLIP_HORIZONTAL && n->split_type == TYPE_HORIZONTAL) ||
- (flp == FLIP_VERTICAL && n->split_type == TYPE_VERTICAL)) {
- tmp = n->first_child;
- n->first_child = n->second_child;
- n->second_child = tmp;
- n->split_ratio = 1.0 - n->split_ratio;
- }
-
- flip_tree(n->first_child, flp);
- flip_tree(n->second_child, flp);
-}
-
-void equalize_tree(node_t *n)
-{
- if (n == NULL || n->vacant) {
- return;
- } else {
- n->split_ratio = split_ratio;
- equalize_tree(n->first_child);
- equalize_tree(n->second_child);
- }
-}
-
-int balance_tree(node_t *n)
-{
- if (n == NULL || n->vacant) {
- return 0;
- } else if (is_leaf(n)) {
- return 1;
- } else {
- int b1 = balance_tree(n->first_child);
- int b2 = balance_tree(n->second_child);
- int b = b1 + b2;
- if (b1 > 0 && b2 > 0) {
- n->split_ratio = (double) b1 / b;
- }
- return b;
- }
-}
-
-void unlink_node(monitor_t *m, desktop_t *d, node_t *n)
-{
- if (d == NULL || n == NULL) {
- return;
- }
-
- node_t *p = n->parent;
-
- if (p == NULL) {
- d->root = NULL;
- d->focus = NULL;
- } else {
- if (d->focus == p || is_descendant(d->focus, n)) {
- d->focus = NULL;
- }
-
- history_remove(d, p, false);
- cancel_presel(m, d, p);
- if (p->sticky) {
- m->sticky_count--;
- }
-
- node_t *b = brother_tree(n);
- node_t *g = p->parent;
-
- if (!n->vacant) {
- unrotate_tree(b, n->birth_rotation);
- }
-
- b->parent = g;
-
- if (g != NULL) {
- if (is_first_child(p)) {
- g->first_child = b;
- } else {
- g->second_child = b;
- }
- } else {
- d->root = b;
- }
-
- b->birth_rotation = p->birth_rotation;
-
- free(p);
- n->parent = NULL;
-
- propagate_flags_upward(m, d, b);
- }
-}
-
-void close_node(node_t *n)
-{
- if (n == NULL) {
- return;
- } else if (n->client != NULL) {
- if (n->client->icccm_props.delete_window) {
- send_client_message(n->id, ewmh->WM_PROTOCOLS, WM_DELETE_WINDOW);
- } else {
- xcb_kill_client(dpy, n->id);
- }
- } else {
- close_node(n->first_child);
- close_node(n->second_child);
- }
-}
-
-void kill_node(monitor_t *m, desktop_t *d, node_t *n)
-{
- if (n == NULL) {
- return;
- }
-
- for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
- if (f->client != NULL) {
- xcb_kill_client(dpy, f->id);
- }
- }
-
- remove_node(m, d, n);
-}
-
-void remove_node(monitor_t *m, desktop_t *d, node_t *n)
-{
- if (n == NULL) {
- return;
- }
-
- unlink_node(m, d, n);
- history_remove(d, n, true);
- remove_stack_node(n);
- cancel_presel_in(m, d, n);
- if (m->sticky_count > 0) {
- m->sticky_count -= sticky_count(n);
- }
- clients_count -= clients_count_in(n);
- if (is_descendant(grabbed_node, n)) {
- grabbed_node = NULL;
- }
- free_node(n);
-
- ewmh_update_client_list(false);
- ewmh_update_client_list(true);
-
- if (mon != NULL && d->focus == NULL) {
- if (d == mon->desk) {
- focus_node(m, d, NULL);
- } else {
- activate_node(m, d, NULL);
- }
- }
-}
-
-void free_node(node_t *n)
-{
- if (n == NULL) {
- return;
- }
- node_t *first_child = n->first_child;
- node_t *second_child = n->second_child;
- free(n->client);
- free(n);
- free_node(first_child);
- free_node(second_child);
-}
-
-bool swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop_t *d2, node_t *n2)
-{
- if (n1 == NULL || n2 == NULL || n1 == n2 || is_descendant(n1, n2) || is_descendant(n2, n1) ||
- (d1 != d2 && ((m1->sticky_count > 0 && sticky_count(n1) > 0) ||
- (m2->sticky_count > 0 && sticky_count(n2) > 0)))) {
- return false;
- }
-
- put_status(SBSC_MASK_NODE_SWAP, "node_swap 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X\n", m1->id, d1->id, n1->id, m2->id, d2->id, n2->id);
-
- node_t *pn1 = n1->parent;
- node_t *pn2 = n2->parent;
- bool n1_first_child = is_first_child(n1);
- bool n2_first_child = is_first_child(n2);
- int br1 = n1->birth_rotation;
- int br2 = n2->birth_rotation;
- bool n1_held_focus = is_descendant(d1->focus, n1);
- bool n2_held_focus = is_descendant(d2->focus, n2);
- node_t *last_d1_focus = d1->focus;
- node_t *last_d2_focus = d2->focus;
-
- if (pn1 != NULL) {
- if (n1_first_child) {
- pn1->first_child = n2;
- } else {
- pn1->second_child = n2;
- }
- }
-
- if (pn2 != NULL) {
- if (n2_first_child) {
- pn2->first_child = n1;
- } else {
- pn2->second_child = n1;
- }
- }
-
- n1->parent = pn2;
- n2->parent = pn1;
- n1->birth_rotation = br2;
- n2->birth_rotation = br1;
-
- propagate_flags_upward(m2, d2, n1);
- propagate_flags_upward(m1, d1, n2);
-
- if (d1 != d2) {
- if (d1->root == n1) {
- d1->root = n2;
- }
-
- if (d2->root == n2) {
- d2->root = n1;
- }
-
- if (n1_held_focus) {
- d1->focus = n2_held_focus ? last_d2_focus : n2;
- }
-
- if (n2_held_focus) {
- d2->focus = n1_held_focus ? last_d1_focus : n1;
- }
-
- if (m1 != m2) {
- adapt_geometry(&m2->rectangle, &m1->rectangle, n2);
- adapt_geometry(&m1->rectangle, &m2->rectangle, n1);
- }
-
- ewmh_set_wm_desktop(n1, d2);
- ewmh_set_wm_desktop(n2, d1);
-
- history_swap_nodes(m1, d1, n1, m2, d2, n2);
-
- if (m1->desk != d1 && m2->desk == d2) {
- show_node(d2, n1);
- hide_node(d2, n2);
- } else if (m1->desk == d1 && m2->desk != d2) {
- hide_node(d1, n1);
- show_node(d1, n2);
- }
-
- if (n1_held_focus) {
- if (d1 == mon->desk) {
- focus_node(m1, d1, d1->focus);
- } else {
- activate_node(m1, d1, d1->focus);
- }
- } else {
- draw_border(n2, is_descendant(n2, d1->focus), (m1 == mon));
- }
-
- if (n2_held_focus) {
- if (d2 == mon->desk) {
- focus_node(m2, d2, d2->focus);
- } else {
- activate_node(m2, d2, d2->focus);
- }
- } else {
- draw_border(n1, is_descendant(n1, d2->focus), (m2 == mon));
- }
- }
-
- arrange(m1, d1);
-
- if (d1 != d2) {
- arrange(m2, d2);
- }
-
- return true;
-}
-
-bool transfer_node(monitor_t *ms, desktop_t *ds, node_t *ns, monitor_t *md, desktop_t *dd, node_t *nd)
-{
- if (ns == NULL || ns == nd || is_child(ns, nd) || is_descendant(nd, ns)) {
- return false;
- }
-
- if (sticky_still && ds != dd && ms->sticky_count > 0 && sticky_count(ns) > 0) {
- return false;
- }
-
- put_status(SBSC_MASK_NODE_TRANSFER, "node_transfer 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X 0x%08X\n", ms->id, ds->id, ns->id, md->id, dd->id, nd!=NULL?nd->id:0);
-
- bool held_focus = is_descendant(ds->focus, ns);
- /* avoid ending up with a dangling pointer (because of unlink_node) */
- node_t *last_ds_focus = is_child(ns, ds->focus) ? NULL : ds->focus;
-
- if (held_focus && ds == mon->desk) {
- clear_input_focus();
- }
-
- unlink_node(ms, ds, ns);
- insert_node(md, dd, ns, nd);
-
- if (md != ms && (ns->client == NULL || monitor_from_client(ns->client) != md)) {
- adapt_geometry(&ms->rectangle, &md->rectangle, ns);
- }
-
- if (ds != dd) {
- ewmh_set_wm_desktop(ns, dd);
- if (sticky_still) {
- if (ds == ms->desk && dd != md->desk) {
- hide_node(ds, ns);
- } else if (ds != ms->desk && dd == md->desk) {
- show_node(dd, ns);
- }
- }
- }
-
- history_transfer_node(md, dd, ns);
- stack(dd, ns, false);
-
- if (ds == dd) {
- if (held_focus) {
- if (ds == mon->desk) {
- focus_node(ms, ds, last_ds_focus);
- } else {
- activate_node(ms, ds, last_ds_focus);
- }
- } else {
- draw_border(ns, is_descendant(ns, ds->focus), (ms == mon));
- }
- } else {
- if (held_focus) {
- if (ds == mon->desk) {
- focus_node(ms, ds, ds->focus);
- } else {
- activate_node(ms, ds, ds->focus);
- }
- }
- if (dd->focus == ns) {
- if (dd == mon->desk) {
- focus_node(md, dd, held_focus ? last_ds_focus : dd->focus);
- } else {
- activate_node(md, dd, held_focus ? last_ds_focus : dd->focus);
- }
- } else {
- draw_border(ns, is_descendant(ns, dd->focus), (md == mon));
- }
- }
-
- arrange(ms, ds);
-
- if (ds != dd) {
- arrange(md, dd);
- }
-
- return true;
-}
-
-bool find_closest_node(coordinates_t *ref, coordinates_t *dst, cycle_dir_t dir, node_select_t sel)
-{
- if (ref->node == NULL) {
- return false;
- }
-
- monitor_t *m = ref->monitor;
- desktop_t *d = ref->desktop;
- node_t *n = ref->node;
-
- node_t *f = (dir == CYCLE_PREV ? prev_leaf(n, d->root) : next_leaf(n, d->root));
-
-#define HANDLE_BOUNDARIES(f) \
- while (f == NULL) { \
- d = (dir == CYCLE_PREV ? d->prev : d->next); \
- if (d == NULL) { \
- m = (dir == CYCLE_PREV ? m->prev : m->next); \
- if (m == NULL) { \
- m = (dir == CYCLE_PREV ? mon_tail : mon_head); \
- } \
- d = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head); \
- } \
- f = (dir == CYCLE_PREV ? second_extrema(d->root) : first_extrema(d->root)); \
- }
- HANDLE_BOUNDARIES(f);
-
- while (f != n) {
- coordinates_t loc = {m, d, f};
- if (f->client != NULL && !f->hidden && node_matches(&loc, ref, sel)) {
- *dst = loc;
- return true;
- }
- f = (dir == CYCLE_PREV ? prev_leaf(f, d->root) : next_leaf(f, d->root));
- HANDLE_BOUNDARIES(f);
- }
-#undef HANDLE_EXTREMUM
- return false;
-}
-
-void circulate_leaves(monitor_t *m, desktop_t *d, node_t *n, circulate_dir_t dir)
-{
- if (n == NULL || d->focus == NULL || is_leaf(n)) {
- return;
- }
- node_t *p = d->focus->parent;
- bool focus_first_child = is_first_child(d->focus);
- if (dir == CIRCULATE_FORWARD) {
- node_t *e = second_extrema(n);
- while (e != NULL && (e->client == NULL || !IS_TILED(e->client))) {
- e = prev_leaf(e, n);
- }
- for (node_t *s = e, *f = prev_tiled_leaf(s, n); f != NULL; s = prev_tiled_leaf(f, n), f = prev_tiled_leaf(s, n)) {
- swap_nodes(m, d, f, m, d, s);
- }
- } else {
- node_t *e = first_extrema(n);
- while (e != NULL && (e->client == NULL || !IS_TILED(e->client))) {
- e = next_leaf(e, n);
- }
- for (node_t *f = e, *s = next_tiled_leaf(f, n); s != NULL; f = next_tiled_leaf(s, n), s = next_tiled_leaf(f, n)) {
- swap_nodes(m, d, f, m, d, s);
- }
- }
- if (focus_first_child) {
- focus_node(m, d, p->first_child);
- } else {
- focus_node(m, d, p->second_child);
- }
-}
-
-void set_vacant(monitor_t *m, desktop_t *d, node_t *n, bool value)
-{
- if (n->vacant == value) {
- return;
- }
-
- propagate_vacant_downward(m, d, n, value);
- propagate_vacant_upward(m, d, n);
-}
-
-void set_vacant_local(monitor_t *m, desktop_t *d, node_t *n, bool value)
-{
- if (n->vacant == value) {
- return;
- }
-
- n->vacant = value;
-
- if (value) {
- unrotate_brother(n);
- cancel_presel(m, d, n);
- } else {
- rotate_brother(n);
- }
-}
-
-void propagate_vacant_downward(monitor_t *m, desktop_t *d, node_t *n, bool value)
-{
- if (n == NULL) {
- return;
- }
-
- set_vacant_local(m, d, n, value);
-
- propagate_vacant_downward(m, d, n->first_child, value);
- propagate_vacant_downward(m, d, n->second_child, value);
-}
-
-void propagate_vacant_upward(monitor_t *m, desktop_t *d, node_t *n)
-{
- if (n == NULL) {
- return;
- }
-
- node_t *p = n->parent;
-
- if (p != NULL) {
- set_vacant_local(m, d, p, (p->first_child->vacant && p->second_child->vacant));
- }
-
- propagate_vacant_upward(m, d, p);
-}
-
-bool set_layer(monitor_t *m, desktop_t *d, node_t *n, stack_layer_t l)
-{
- if (n == NULL || n->client->layer == l) {
- return false;
- }
-
- n->client->last_layer = n->client->layer;
- n->client->layer = l;
-
- if (l == LAYER_ABOVE) {
- n->client->wm_flags |= WM_FLAG_ABOVE;
- n->client->wm_flags &= ~WM_FLAG_BELOW;
- } else if (l == LAYER_BELOW) {
- n->client->wm_flags |= WM_FLAG_BELOW;
- n->client->wm_flags &= ~WM_FLAG_ABOVE;
- } else {
- n->client->wm_flags &= ~(WM_FLAG_ABOVE | WM_FLAG_BELOW);
- }
-
- ewmh_wm_state_update(n);
-
- put_status(SBSC_MASK_NODE_LAYER, "node_layer 0x%08X 0x%08X 0x%08X %s\n", m->id, d->id, n->id, LAYER_STR(l));
-
- if (d->focus == n) {
- neutralize_occluding_windows(m, d, n);
- }
-
- stack(d, n, (d->focus == n));
-
- return true;
-}
-
-bool set_state(monitor_t *m, desktop_t *d, node_t *n, client_state_t s)
-{
- if (n == NULL || n->client == NULL || n->client->state == s) {
- return false;
- }
-
- client_t *c = n->client;
-
- c->last_state = c->state;
- c->state = s;
-
- switch (c->last_state) {
- case STATE_TILED:
- case STATE_PSEUDO_TILED:
- break;
- case STATE_FLOATING:
- set_floating(m, d, n, false);
- break;
- case STATE_FULLSCREEN:
- set_fullscreen(m, d, n, false);
- break;
- }
-
- put_status(SBSC_MASK_NODE_STATE, "node_state 0x%08X 0x%08X 0x%08X %s off\n", m->id, d->id, n->id, STATE_STR(c->last_state));
-
- switch (c->state) {
- case STATE_TILED:
- case STATE_PSEUDO_TILED:
- break;
- case STATE_FLOATING:
- set_floating(m, d, n, true);
- break;
- case STATE_FULLSCREEN:
- set_fullscreen(m, d, n, true);
- break;
- }
-
- put_status(SBSC_MASK_NODE_STATE, "node_state 0x%08X 0x%08X 0x%08X %s on\n", m->id, d->id, n->id, STATE_STR(c->state));
-
- if (n == m->desk->focus) {
- put_status(SBSC_MASK_REPORT);
- }
-
- return true;
-}
-
-void set_floating(monitor_t *m, desktop_t *d, node_t *n, bool value)
-{
- if (n == NULL) {
- return;
- }
-
- cancel_presel(m, d, n);
- set_vacant(m, d, n, value);
-
- if (!value && d->focus == n) {
- neutralize_occluding_windows(m, d, n);
- }
-
- stack(d, n, (d->focus == n));
-}
-
-void set_fullscreen(monitor_t *m, desktop_t *d, node_t *n, bool value)
-{
- if (n == NULL) {
- return;
- }
-
- client_t *c = n->client;
-
- cancel_presel(m, d, n);
- set_vacant(m, d, n, value);
-
- if (value) {
- c->wm_flags |= WM_FLAG_FULLSCREEN;
- c->last_layer = c->layer;
- c->layer = LAYER_ABOVE;
- } else {
- c->wm_flags &= ~WM_FLAG_FULLSCREEN;
- c->layer = c->last_layer;
- if (d->focus == n) {
- neutralize_occluding_windows(m, d, n);
- }
- }
-
- ewmh_wm_state_update(n);
- stack(d, n, (d->focus == n));
-}
-
-void neutralize_occluding_windows(monitor_t *m, desktop_t *d, node_t *n)
-{
- bool changed = false;
- for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
- for (node_t *a = first_extrema(d->root); a != NULL; a = next_leaf(a, d->root)) {
- if (a != f && a->client != NULL && f->client != NULL &&
- IS_FULLSCREEN(a->client) && stack_cmp(f->client, a->client) < 0) {
- set_state(m, d, a, a->client->last_state);
- changed = true;
- }
- }
- }
- if (changed) {
- arrange(m, d);
- }
-}
-
-void propagate_flags_upward(monitor_t *m, desktop_t *d, node_t *n)
-{
- if (n == NULL) {
- return;
- }
-
- node_t *p = n->parent;
-
- if (p != NULL) {
- set_vacant_local(m, d, p, (p->first_child->vacant && p->second_child->vacant));
- set_hidden_local(m, d, p, (p->first_child->hidden && p->second_child->hidden));
- }
-
- propagate_flags_upward(m, d, p);
-}
-
-void set_hidden(monitor_t *m, desktop_t *d, node_t *n, bool value)
-{
- if (n == NULL || n->hidden == value) {
- return;
- }
-
-
- if (focus_follows_pointer) {
- listen_enter_notify(d->root, false);
- }
-
- bool held_focus = is_descendant(d->focus, n);
-
- propagate_hidden_downward(m, d, n, value);
- propagate_hidden_upward(m, d, n);
-
- put_status(SBSC_MASK_NODE_FLAG, "node_flag 0x%08X 0x%08X 0x%08X hidden %s\n", m->id, d->id, n->id, ON_OFF_STR(value));
-
- if (held_focus || d->focus == NULL) {
- if (d->focus != NULL) {
- d->focus = NULL;
- draw_border(n, false, (mon == m));
- }
- if (d == mon->desk) {
- focus_node(m, d, d->focus);
- } else {
- activate_node(m, d, d->focus);
- }
- }
-
- if (focus_follows_pointer) {
- listen_enter_notify(d->root, true);
- }
-}
-
-void set_hidden_local(monitor_t *m, desktop_t *d, node_t *n, bool value)
-{
- if (n->hidden == value) {
- return;
- }
-
- n->hidden = value;
-
- if (n->client != NULL) {
- if (n->client->shown) {
- window_set_visibility(n->id, !value);
- }
-
- if (IS_TILED(n->client)) {
- set_vacant(m, d, n, value);
- }
-
- if (value) {
- n->client->wm_flags |= WM_FLAG_HIDDEN;
- } else {
- n->client->wm_flags &= ~WM_FLAG_HIDDEN;
- }
-
- ewmh_wm_state_update(n);
- }
-}
-
-void propagate_hidden_downward(monitor_t *m, desktop_t *d, node_t *n, bool value)
-{
- if (n == NULL) {
- return;
- }
-
- set_hidden_local(m, d, n, value);
-
- propagate_hidden_downward(m, d, n->first_child, value);
- propagate_hidden_downward(m, d, n->second_child, value);
-}
-
-void propagate_hidden_upward(monitor_t *m, desktop_t *d, node_t *n)
-{
- if (n == NULL) {
- return;
- }
-
- node_t *p = n->parent;
-
- if (p != NULL) {
- set_hidden_local(m, d, p, p->first_child->hidden && p->second_child->hidden);
- }
-
- propagate_hidden_upward(m, d, p);
-}
-
-void set_sticky(monitor_t *m, desktop_t *d, node_t *n, bool value)
-{
- if (n == NULL || n->sticky == value) {
- return;
- }
-
- if (d != m->desk) {
- transfer_node(m, d, n, m, m->desk, m->desk->focus);
- }
-
- n->sticky = value;
-
- if (value) {
- m->sticky_count++;
- } else {
- m->sticky_count--;
- }
-
- if (n->client != NULL) {
- if (value) {
- n->client->wm_flags |= WM_FLAG_STICKY;
- } else {
- n->client->wm_flags &= ~WM_FLAG_STICKY;
- }
- ewmh_wm_state_update(n);
- }
-
- put_status(SBSC_MASK_NODE_FLAG, "node_flag 0x%08X 0x%08X 0x%08X sticky %s\n", m->id, d->id, n->id, ON_OFF_STR(value));
-
- if (n == m->desk->focus) {
- put_status(SBSC_MASK_REPORT);
- }
-}
-
-void set_private(monitor_t *m, desktop_t *d, node_t *n, bool value)
-{
- if (n == NULL || n->private == value) {
- return;
- }
-
- n->private = value;
-
- put_status(SBSC_MASK_NODE_FLAG, "node_flag 0x%08X 0x%08X 0x%08X private %s\n", m->id, d->id, n->id, ON_OFF_STR(value));
-
- if (n == m->desk->focus) {
- put_status(SBSC_MASK_REPORT);
- }
-}
-
-void set_locked(monitor_t *m, desktop_t *d, node_t *n, bool value)
-{
- if (n == NULL || n->locked == value) {
- return;
- }
-
- n->locked = value;
-
- put_status(SBSC_MASK_NODE_FLAG, "node_flag 0x%08X 0x%08X 0x%08X locked %s\n", m->id, d->id, n->id, ON_OFF_STR(value));
-
- if (n == m->desk->focus) {
- put_status(SBSC_MASK_REPORT);
- }
-}
-
-void set_urgent(monitor_t *m, desktop_t *d, node_t *n, bool value)
-{
- if (value && mon->desk->focus == n) {
- return;
- }
-
- n->client->urgent = value;
-
- if (value) {
- n->client->wm_flags |= WM_FLAG_DEMANDS_ATTENTION;
- } else {
- n->client->wm_flags &= ~WM_FLAG_DEMANDS_ATTENTION;
- }
-
- ewmh_wm_state_update(n);
-
- put_status(SBSC_MASK_NODE_FLAG, "node_flag 0x%08X 0x%08X 0x%08X urgent %s\n", m->id, d->id, n->id, ON_OFF_STR(value));
- put_status(SBSC_MASK_REPORT);
-}
-
-/* Returns true if a contains b */
-bool contains(xcb_rectangle_t a, xcb_rectangle_t b)
-{
- return (a.x <= b.x && (a.x + a.width) >= (b.x + b.width) &&
- a.y <= b.y && (a.y + a.height) >= (b.y + b.height));
-}
-
-xcb_rectangle_t get_rectangle(desktop_t *d, node_t *n)
-{
- client_t *c = n->client;
- if (c != NULL) {
- if (IS_FLOATING(c)) {
- return c->floating_rectangle;
- } else {
- return c->tiled_rectangle;
- }
- } else {
- int wg = (d == NULL ? 0 : (gapless_monocle && IS_MONOCLE(d) ? 0 : d->window_gap));
- xcb_rectangle_t rect = n->rectangle;
- rect.width -= wg;
- rect.height -= wg;
- return rect;
- }
-}
-
-void listen_enter_notify(node_t *n, bool enable)
-{
- uint32_t mask = CLIENT_EVENT_MASK | (enable ? XCB_EVENT_MASK_ENTER_WINDOW : 0);
- for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
- if (f->client == NULL) {
- continue;
- }
- xcb_change_window_attributes(dpy, f->id, XCB_CW_EVENT_MASK, &mask);
- if (f->presel != NULL) {
- xcb_change_window_attributes(dpy, f->presel->feedback, XCB_CW_EVENT_MASK, &mask);
- }
- }
-}
-
-#define DEF_FLAG_COUNT(flag) \
- unsigned int flag##_count(node_t *n) \
- { \
- if (n == NULL) { \
- return 0; \
- } else { \
- return ((n->flag ? 1 : 0) + \
- flag##_count(n->first_child) + \
- flag##_count(n->second_child)); \
- } \
- }
- DEF_FLAG_COUNT(sticky)
- DEF_FLAG_COUNT(private)
- DEF_FLAG_COUNT(locked)
-#undef DEF_FLAG_COUNT
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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.
- */
-
-#ifndef BSPWM_TREE_H
-#define BSPWM_TREE_H
-
-void arrange(monitor_t *m, desktop_t *d);
-void apply_layout(monitor_t *m, desktop_t *d, node_t *n, layout_t l, xcb_rectangle_t rect, xcb_rectangle_t root_rect);
-presel_t *make_presel(void);
-void set_ratio(node_t *n, double rat);
-void presel_dir(monitor_t *m, desktop_t *d, node_t *n, direction_t dir);
-void presel_ratio(monitor_t *m, desktop_t *d, node_t *n, double ratio);
-void cancel_presel(monitor_t *m, desktop_t *d, node_t *n);
-void cancel_presel_in(monitor_t *m, desktop_t *d, node_t *n);
-node_t *find_public(desktop_t *d);
-node_t *insert_node(monitor_t *m, desktop_t *d, node_t *n, node_t *f);
-void insert_receptacle(monitor_t *m, desktop_t *d, node_t *n);
-bool activate_node(monitor_t *m, desktop_t *d, node_t *n);
-void transfer_sticky_nodes(monitor_t *m, desktop_t *ds, desktop_t *dd, node_t *n);
-bool focus_node(monitor_t *m, desktop_t *d, node_t *n);
-void hide_node(desktop_t *d, node_t *n);
-void show_node(desktop_t *d, node_t *n);
-node_t *make_node(uint32_t id);
-client_t *make_client(void);
-void initialize_client(node_t *n);
-bool is_focusable(node_t *n);
-bool is_leaf(node_t *n);
-bool is_first_child(node_t *n);
-bool is_second_child(node_t *n);
-unsigned int clients_count_in(node_t *n);
-node_t *brother_tree(node_t *n);
-node_t *first_extrema(node_t *n);
-node_t *second_extrema(node_t *n);
-node_t *first_focusable_leaf(node_t *n);
-node_t *next_leaf(node_t *n, node_t *r);
-node_t *prev_leaf(node_t *n, node_t *r);
-node_t *next_tiled_leaf(node_t *n, node_t *r);
-node_t *prev_tiled_leaf(node_t *n, node_t *r);
-bool is_adjacent(node_t *a, node_t *b, direction_t dir);
-node_t *find_fence(node_t *n, direction_t dir);
-bool is_child(node_t *a, node_t *b);
-bool is_descendant(node_t *a, node_t *b);
-bool find_by_id(uint32_t id, coordinates_t *loc);
-node_t *find_by_id_in(node_t *r, uint32_t id);
-void find_nearest_neighbor(coordinates_t *ref, coordinates_t *dst, direction_t dir, node_select_t sel);
-unsigned int node_area(desktop_t *d, node_t *n);
-int tiled_count(node_t *n);
-void find_biggest(coordinates_t *ref, coordinates_t *dst, node_select_t sel);
-void rotate_tree(node_t *n, int deg);
-void rotate_brother(node_t *n);
-void unrotate_tree(node_t *n, int rot);
-void unrotate_brother(node_t *n);
-void flip_tree(node_t *n, flip_t flp);
-void equalize_tree(node_t *n);
-int balance_tree(node_t *n);
-void unlink_node(monitor_t *m, desktop_t *d, node_t *n);
-void close_node(node_t *n);
-void kill_node(monitor_t *m, desktop_t *d, node_t *n);
-void remove_node(monitor_t *m, desktop_t *d, node_t *n);
-void free_node(node_t *n);
-bool swap_nodes(monitor_t *m1, desktop_t *d1, node_t *n1, monitor_t *m2, desktop_t *d2, node_t *n2);
-bool transfer_node(monitor_t *ms, desktop_t *ds, node_t *ns, monitor_t *md, desktop_t *dd, node_t *nd);
-bool find_closest_node(coordinates_t *ref, coordinates_t *dst, cycle_dir_t dir, node_select_t sel);
-void circulate_leaves(monitor_t *m, desktop_t *d, node_t *n, circulate_dir_t dir);
-void set_vacant(monitor_t *m, desktop_t *d, node_t *n, bool value);
-void set_vacant_local(monitor_t *m, desktop_t *d, node_t *n, bool value);
-void propagate_vacant_downward(monitor_t *m, desktop_t *d, node_t *n, bool value);
-void propagate_vacant_upward(monitor_t *m, desktop_t *d, node_t *n);
-bool set_layer(monitor_t *m, desktop_t *d, node_t *n, stack_layer_t l);
-bool set_state(monitor_t *m, desktop_t *d, node_t *n, client_state_t s);
-void set_floating(monitor_t *m, desktop_t *d, node_t *n, bool value);
-void set_fullscreen(monitor_t *m, desktop_t *d, node_t *n, bool value);
-void neutralize_occluding_windows(monitor_t *m, desktop_t *d, node_t *n);
-void propagate_flags_upward(monitor_t *m, desktop_t *d, node_t *n);
-void set_hidden(monitor_t *m, desktop_t *d, node_t *n, bool value);
-void set_hidden_local(monitor_t *m, desktop_t *d, node_t *n, bool value);
-void propagate_hidden_downward(monitor_t *m, desktop_t *d, node_t *n, bool value);
-void propagate_hidden_upward(monitor_t *m, desktop_t *d, node_t *n);
-void set_sticky(monitor_t *m, desktop_t *d, node_t *n, bool value);
-void set_private(monitor_t *m, desktop_t *d, node_t *n, bool value);
-void set_locked(monitor_t *m, desktop_t *d, node_t *n, bool value);
-void set_urgent(monitor_t *m, desktop_t *d, node_t *n, bool value);
-bool contains(xcb_rectangle_t a, xcb_rectangle_t b);
-xcb_rectangle_t get_rectangle(desktop_t *d, node_t *n);
-void listen_enter_notify(node_t *n, bool enable);
-
-unsigned int sticky_count(node_t *n);
-unsigned int private_count(node_t *n);
-unsigned int locked_count(node_t *n);
-
-#endif
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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.
- */
-
-#ifndef BSPWM_TYPES_H
-#define BSPWM_TYPES_H
-#include <stdbool.h>
-#include <xcb/xcb.h>
-#include <xcb/xcb_icccm.h>
-#include <xcb/randr.h>
-#include <xcb/xcb_event.h>
-#include "helpers.h"
-
-#define MISSING_VALUE "N/A"
-#define MAX_WM_STATES 4
-
-typedef enum {
- TYPE_HORIZONTAL,
- TYPE_VERTICAL
-} split_type_t;
-
-typedef enum {
- MODE_AUTOMATIC,
- MODE_MANUAL
-} split_mode_t;
-
-typedef enum {
- STATE_TILED,
- STATE_PSEUDO_TILED,
- STATE_FLOATING,
- STATE_FULLSCREEN
-} client_state_t;
-
-typedef enum {
- WM_FLAG_MODAL = 1 << 0,
- WM_FLAG_STICKY = 1 << 1,
- WM_FLAG_MAXIMIZED_VERT = 1 << 2,
- WM_FLAG_MAXIMIZED_HORZ = 1 << 3,
- WM_FLAG_SHADED = 1 << 4,
- WM_FLAG_SKIP_TASKBAR = 1 << 5,
- WM_FLAG_SKIP_PAGER = 1 << 6,
- WM_FLAG_HIDDEN = 1 << 7,
- WM_FLAG_FULLSCREEN = 1 << 8,
- WM_FLAG_ABOVE = 1 << 9,
- WM_FLAG_BELOW = 1 << 10,
- WM_FLAG_DEMANDS_ATTENTION = 1 << 11,
-} wm_flags_t;
-
-typedef enum {
- LAYER_BELOW,
- LAYER_NORMAL,
- LAYER_ABOVE
-} stack_layer_t;
-
-typedef enum {
- OPTION_NONE,
- OPTION_TRUE,
- OPTION_FALSE
-} option_bool_t;
-
-typedef enum {
- ALTER_TOGGLE,
- ALTER_SET
-} alter_state_t;
-
-typedef enum {
- CYCLE_NEXT,
- CYCLE_PREV
-} cycle_dir_t;
-
-typedef enum {
- CIRCULATE_FORWARD,
- CIRCULATE_BACKWARD
-} circulate_dir_t;
-
-typedef enum {
- HISTORY_OLDER,
- HISTORY_NEWER
-} history_dir_t;
-
-typedef enum {
- DIR_NORTH,
- DIR_WEST,
- DIR_SOUTH,
- DIR_EAST
-} direction_t;
-
-typedef enum {
- HANDLE_LEFT = 1 << 0,
- HANDLE_TOP = 1 << 1,
- HANDLE_RIGHT = 1 << 2,
- HANDLE_BOTTOM = 1 << 3,
- HANDLE_TOP_LEFT = HANDLE_TOP | HANDLE_LEFT,
- HANDLE_TOP_RIGHT = HANDLE_TOP | HANDLE_RIGHT,
- HANDLE_BOTTOM_RIGHT = HANDLE_BOTTOM | HANDLE_RIGHT,
- HANDLE_BOTTOM_LEFT = HANDLE_BOTTOM | HANDLE_LEFT
-} resize_handle_t;
-
-typedef enum {
- ACTION_NONE,
- ACTION_FOCUS,
- ACTION_MOVE,
- ACTION_RESIZE_SIDE,
- ACTION_RESIZE_CORNER
-} pointer_action_t;
-
-typedef enum {
- LAYOUT_TILED,
- LAYOUT_MONOCLE
-} layout_t;
-
-typedef enum {
- FLIP_HORIZONTAL,
- FLIP_VERTICAL
-} flip_t;
-
-typedef enum {
- FIRST_CHILD,
- SECOND_CHILD
-} child_polarity_t;
-
-typedef struct {
- option_bool_t automatic;
- option_bool_t focused;
- option_bool_t local;
- option_bool_t active;
- option_bool_t leaf;
- option_bool_t window;
- option_bool_t tiled;
- option_bool_t pseudo_tiled;
- option_bool_t floating;
- option_bool_t fullscreen;
- option_bool_t hidden;
- option_bool_t sticky;
- option_bool_t private;
- option_bool_t locked;
- option_bool_t urgent;
- option_bool_t same_class;
- option_bool_t descendant_of;
- option_bool_t ancestor_of;
- option_bool_t below;
- option_bool_t normal;
- option_bool_t above;
-} node_select_t;
-
-typedef struct {
- option_bool_t occupied;
- option_bool_t focused;
- option_bool_t urgent;
- option_bool_t local;
-} desktop_select_t;
-
-typedef struct {
- option_bool_t occupied;
- option_bool_t focused;
-} monitor_select_t;
-
-typedef struct icccm_props_t icccm_props_t;
-struct icccm_props_t {
- bool take_focus;
- bool input_hint;
- bool delete_window;
-};
-
-typedef struct {
- char class_name[3 * SMALEN / 2];
- char instance_name[3 * SMALEN / 2];
- unsigned int border_width;
- bool urgent;
- bool shown;
- client_state_t state;
- client_state_t last_state;
- stack_layer_t layer;
- stack_layer_t last_layer;
- xcb_rectangle_t floating_rectangle;
- xcb_rectangle_t tiled_rectangle;
- xcb_size_hints_t size_hints;
- icccm_props_t icccm_props;
- wm_flags_t wm_flags;
-} client_t;
-
-typedef struct presel_t presel_t;
-struct presel_t {
- double split_ratio;
- direction_t split_dir;
- xcb_window_t feedback;
-};
-
-typedef struct node_t node_t;
-struct node_t {
- uint32_t id;
- split_type_t split_type;
- double split_ratio;
- int birth_rotation;
- presel_t *presel;
- xcb_rectangle_t rectangle;
- bool vacant;
- bool hidden;
- bool sticky;
- bool private;
- bool locked;
- node_t *first_child;
- node_t *second_child;
- node_t *parent;
- client_t *client;
-};
-
-typedef struct padding_t padding_t;
-struct padding_t {
- int top;
- int right;
- int bottom;
- int left;
-};
-
-typedef struct desktop_t desktop_t;
-struct desktop_t {
- char name[SMALEN];
- uint32_t id;
- layout_t layout;
- node_t *root;
- node_t *focus;
- desktop_t *prev;
- desktop_t *next;
- padding_t padding;
- int window_gap;
- unsigned int border_width;
-};
-
-typedef struct monitor_t monitor_t;
-struct monitor_t {
- char name[SMALEN];
- uint32_t id;
- xcb_randr_output_t randr_id;
- xcb_window_t root;
- bool wired;
- padding_t padding;
- unsigned int sticky_count;
- int window_gap;
- unsigned int border_width;
- xcb_rectangle_t rectangle;
- desktop_t *desk;
- desktop_t *desk_head;
- desktop_t *desk_tail;
- monitor_t *prev;
- monitor_t *next;
-};
-
-typedef struct {
- monitor_t *monitor;
- desktop_t *desktop;
- node_t *node;
-} coordinates_t;
-
-typedef struct history_t history_t;
-struct history_t {
- coordinates_t loc;
- bool latest;
- history_t *prev;
- history_t *next;
-};
-
-typedef struct stacking_list_t stacking_list_t;
-struct stacking_list_t {
- node_t *node;
- stacking_list_t *prev;
- stacking_list_t *next;
-};
-
-typedef struct subscriber_list_t subscriber_list_t;
-struct subscriber_list_t {
- int fd;
- FILE *stream;
- int field;
- subscriber_list_t *prev;
- subscriber_list_t *next;
-};
-
-typedef struct rule_t rule_t;
-struct rule_t {
- char class_name[MAXLEN];
- char instance_name[MAXLEN];
- char effect[MAXLEN];
- bool one_shot;
- rule_t *prev;
- rule_t *next;
-};
-
-typedef struct {
- char class_name[3 * SMALEN / 2];
- char instance_name[3 * SMALEN / 2];
- char monitor_desc[MAXLEN];
- char desktop_desc[MAXLEN];
- char node_desc[MAXLEN];
- char split_dir[SMALEN];
- double split_ratio;
- stack_layer_t *layer;
- client_state_t *state;
- bool hidden;
- bool sticky;
- bool private;
- bool locked;
- bool center;
- bool follow;
- bool manage;
- bool focus;
- bool border;
-} rule_consequence_t;
-
-typedef struct pending_rule_t pending_rule_t;
-struct pending_rule_t {
- int fd;
- xcb_window_t win;
- rule_consequence_t *csq;
- pending_rule_t *prev;
- pending_rule_t *next;
-};
-
-typedef struct {
- double x;
- double y;
-} dpoint_t;
-
-#endif
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <string.h>
-#include "bspwm.h"
-#include "ewmh.h"
-#include "monitor.h"
-#include "query.h"
-#include "rule.h"
-#include "settings.h"
-#include "geometry.h"
-#include "pointer.h"
-#include "stack.h"
-#include "tree.h"
-#include "parse.h"
-#include "window.h"
-
-void schedule_window(xcb_window_t win)
-{
- coordinates_t loc;
- uint8_t override_redirect = 0;
- xcb_get_window_attributes_reply_t *wa = xcb_get_window_attributes_reply(dpy, xcb_get_window_attributes(dpy, win), NULL);
-
- if (wa != NULL) {
- override_redirect = wa->override_redirect;
- free(wa);
- }
-
- if (override_redirect || locate_window(win, &loc)) {
- return;
- }
-
- /* ignore pending windows */
- for (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {
- if (pr->win == win) {
- return;
- }
- }
-
- rule_consequence_t *csq = make_rule_conquence();
- apply_rules(win, csq);
- if (!schedule_rules(win, csq)) {
- manage_window(win, csq, -1);
- free(csq);
- }
-}
-
-void manage_window(xcb_window_t win, rule_consequence_t *csq, int fd)
-{
- monitor_t *m = mon;
- desktop_t *d = mon->desk;
- node_t *f = mon->desk->focus;
-
- parse_rule_consequence(fd, csq);
-
- if (ewmh_handle_struts(win)) {
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- arrange(m, m->desk);
- }
- }
-
- if (!csq->manage) {
- free(csq->layer);
- free(csq->state);
- window_show(win);
- return;
- }
-
- if (csq->node_desc[0] != '\0') {
- coordinates_t ref = {m, d, f};
- coordinates_t trg = {NULL, NULL, NULL};
- if (node_from_desc(csq->node_desc, &ref, &trg) == SELECTOR_OK) {
- m = trg.monitor;
- d = trg.desktop;
- f = trg.node;
- }
- } else if (csq->desktop_desc[0] != '\0') {
- coordinates_t ref = {m, d, NULL};
- coordinates_t trg = {NULL, NULL, NULL};
- if (desktop_from_desc(csq->desktop_desc, &ref, &trg) == SELECTOR_OK) {
- m = trg.monitor;
- d = trg.desktop;
- f = trg.desktop->focus;
- }
- } else if (csq->monitor_desc[0] != '\0') {
- coordinates_t ref = {m, NULL, NULL};
- coordinates_t trg = {NULL, NULL, NULL};
- if (monitor_from_desc(csq->monitor_desc, &ref, &trg) == SELECTOR_OK) {
- m = trg.monitor;
- d = trg.monitor->desk;
- f = trg.monitor->desk->focus;
- }
- }
-
- if (csq->sticky) {
- m = mon;
- d = mon->desk;
- f = mon->desk->focus;
- }
-
- if (csq->split_dir[0] != '\0' && f != NULL) {
- direction_t dir;
- if (parse_direction(csq->split_dir, &dir)) {
- presel_dir(m, d, f, dir);
- }
- }
-
- if (csq->split_ratio != 0 && f != NULL) {
- presel_ratio(m, d, f, csq->split_ratio);
- }
-
- node_t *n = make_node(win);
- client_t *c = make_client();
- c->border_width = csq->border ? d->border_width : 0;
- n->client = c;
- initialize_client(n);
- initialize_floating_rectangle(n);
-
- if (c->floating_rectangle.x == 0 && c->floating_rectangle.y == 0) {
- csq->center = true;
- }
-
- monitor_t *mm = monitor_from_client(c);
- embrace_client(mm, c);
- adapt_geometry(&mm->rectangle, &m->rectangle, n);
-
- if (csq->center) {
- window_center(m, c);
- }
-
- snprintf(c->class_name, sizeof(c->class_name), "%s", csq->class_name);
- snprintf(c->instance_name, sizeof(c->instance_name), "%s", csq->instance_name);
-
- f = insert_node(m, d, n, f);
- clients_count++;
-
- put_status(SBSC_MASK_NODE_MANAGE, "node_manage 0x%08X 0x%08X 0x%08X 0x%08X\n", m->id, d->id, win, f!=NULL?f->id:0);
-
- if (f != NULL && f->client != NULL && csq->state != NULL && *(csq->state) == STATE_FLOATING) {
- c->layer = f->client->layer;
- }
-
- if (csq->layer != NULL) {
- c->layer = *(csq->layer);
- }
-
- if (csq->state != NULL) {
- set_state(m, d, n, *(csq->state));
- }
-
- set_hidden(m, d, n, csq->hidden);
- set_sticky(m, d, n, csq->sticky);
- set_private(m, d, n, csq->private);
- set_locked(m, d, n, csq->locked);
-
- arrange(m, d);
-
- uint32_t values[] = {CLIENT_EVENT_MASK | (focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0)};
- xcb_change_window_attributes(dpy, win, XCB_CW_EVENT_MASK, values);
- set_window_state(win, XCB_ICCCM_WM_STATE_NORMAL);
- window_grab_buttons(win);
-
- if (d == m->desk) {
- show_node(d, n);
- } else {
- hide_node(d, n);
- }
-
- if (!csq->hidden && csq->focus) {
- if (d == mon->desk || csq->follow) {
- focus_node(m, d, n);
- } else {
- activate_node(m, d, n);
- }
- } else {
- stack(d, n, false);
- draw_border(n, false, (m == mon));
- }
-
- ewmh_set_wm_desktop(n, d);
- ewmh_update_client_list(false);
- free(csq->layer);
- free(csq->state);
-}
-
-void set_window_state(xcb_window_t win, xcb_icccm_wm_state_t state)
-{
- long data[] = {state, XCB_NONE};
- xcb_change_property(dpy, XCB_PROP_MODE_REPLACE, win, WM_STATE, WM_STATE, 32, 2, data);
-}
-
-void unmanage_window(xcb_window_t win)
-{
- coordinates_t loc;
- if (locate_window(win, &loc)) {
- put_status(SBSC_MASK_NODE_UNMANAGE, "node_unmanage 0x%08X 0x%08X 0x%08X\n", loc.monitor->id, loc.desktop->id, win);
- remove_node(loc.monitor, loc.desktop, loc.node);
- set_window_state(win, XCB_ICCCM_WM_STATE_WITHDRAWN);
- arrange(loc.monitor, loc.desktop);
- } else {
- for (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {
- if (pr->win == win) {
- remove_pending_rule(pr);
- return;
- }
- }
- }
-}
-
-bool is_presel_window(xcb_window_t win)
-{
- xcb_icccm_get_wm_class_reply_t reply;
- bool ret = false;
- if (xcb_icccm_get_wm_class_reply(dpy, xcb_icccm_get_wm_class(dpy, win), &reply, NULL) == 1) {
- if (streq(BSPWM_CLASS_NAME, reply.class_name) && streq(PRESEL_FEEDBACK_I, reply.instance_name)) {
- ret = true;
- }
- xcb_icccm_get_wm_class_reply_wipe(&reply);
- }
- return ret;
-}
-
-void initialize_presel_feedback(node_t *n)
-{
- if (n == NULL || n->presel == NULL || n->presel->feedback != XCB_NONE) {
- return;
- }
-
- xcb_window_t win = xcb_generate_id(dpy);
- uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_SAVE_UNDER | XCB_CW_EVENT_MASK;
- uint32_t values[] = {get_color_pixel(presel_feedback_color), 1, focus_follows_pointer ? XCB_EVENT_MASK_ENTER_WINDOW : 0};
- xcb_create_window(dpy, XCB_COPY_FROM_PARENT, win, root, 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
- XCB_COPY_FROM_PARENT, mask, values);
-
- xcb_icccm_set_wm_class(dpy, win, sizeof(PRESEL_FEEDBACK_IC), PRESEL_FEEDBACK_IC);
- window_grab_buttons(win);
- stacking_list_t *s = stack_tail;
- while (s != NULL && !IS_TILED(s->node->client)) {
- s = s->prev;
- }
- if (s != NULL) {
- window_above(win, s->node->id);
- }
- n->presel->feedback = win;
-}
-
-void draw_presel_feedback(monitor_t *m, desktop_t *d, node_t *n)
-{
- if (n == NULL || n->presel == NULL || d->layout == LAYOUT_MONOCLE) {
- return;
- }
-
- if (focus_follows_pointer) {
- listen_enter_notify(d->root, false);
- }
-
- bool exists = (n->presel->feedback != XCB_NONE);
- if (!exists) {
- initialize_presel_feedback(n);
- }
-
- int gap = gapless_monocle && IS_MONOCLE(d) ? 0 : d->window_gap;
- presel_t *p = n->presel;
- xcb_rectangle_t rect = n->rectangle;
- rect.x = rect.y = 0;
- rect.width -= gap;
- rect.height -= gap;
- xcb_rectangle_t presel_rect = rect;
-
- switch (p->split_dir) {
- case DIR_NORTH:
- presel_rect.height = p->split_ratio * rect.height;
- break;
- case DIR_EAST:
- presel_rect.width = (1 - p->split_ratio) * rect.width;
- presel_rect.x = rect.width - presel_rect.width;
- break;
- case DIR_SOUTH:
- presel_rect.height = (1 - p->split_ratio) * rect.height;
- presel_rect.y = rect.height - presel_rect.height;
- break;
- case DIR_WEST:
- presel_rect.width = p->split_ratio * rect.width;
- break;
- }
-
- window_move_resize(p->feedback, n->rectangle.x + presel_rect.x, n->rectangle.y + presel_rect.y,
- presel_rect.width, presel_rect.height);
-
- if (!exists && m->desk == d) {
- window_show(p->feedback);
- }
-
- if (focus_follows_pointer) {
- listen_enter_notify(d->root, true);
- }
-}
-
-void refresh_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n)
-{
- if (n == NULL) {
- return;
- } else {
- if (n->presel != NULL) {
- draw_presel_feedback(m, d, n);
- }
- refresh_presel_feedbacks(m, d, n->first_child);
- refresh_presel_feedbacks(m, d, n->second_child);
- }
-}
-
-void show_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n)
-{
- if (n == NULL) {
- return;
- } else {
- if (n->presel != NULL) {
- window_show(n->presel->feedback);
- }
- show_presel_feedbacks(m, d, n->first_child);
- show_presel_feedbacks(m, d, n->second_child);
- }
-}
-
-void hide_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n)
-{
- if (n == NULL) {
- return;
- } else {
- if (n->presel != NULL) {
- window_hide(n->presel->feedback);
- }
- hide_presel_feedbacks(m, d, n->first_child);
- hide_presel_feedbacks(m, d, n->second_child);
- }
-}
-
-void update_colors(void)
-{
- for (monitor_t *m = mon_head; m != NULL; m = m->next) {
- for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
- update_colors_in(d->root, d, m);
- }
- }
-}
-
-void update_colors_in(node_t *n, desktop_t *d, monitor_t *m)
-{
- if (n == NULL) {
- return;
- } else {
- if (n->presel != NULL) {
- uint32_t pxl = get_color_pixel(presel_feedback_color);
- xcb_change_window_attributes(dpy, n->presel->feedback, XCB_CW_BACK_PIXEL, &pxl);
- if (d == m->desk) {
- /* hack to induce back pixel refresh */
- window_hide(n->presel->feedback);
- window_show(n->presel->feedback);
- }
- }
- if (n == d->focus) {
- draw_border(n, true, (m == mon));
- } else if (n->client != NULL) {
- draw_border(n, false, (m == mon));
- } else {
- update_colors_in(n->first_child, d, m);
- update_colors_in(n->second_child, d, m);
- }
- }
-}
-
-void draw_border(node_t *n, bool focused_node, bool focused_monitor)
-{
- if (n == NULL) {
- return;
- }
-
- uint32_t border_color_pxl = get_border_color(focused_node, focused_monitor);
- for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
- if (f->client != NULL && f->client->border_width > 0) {
- window_draw_border(f->id, border_color_pxl);
- }
- }
-}
-
-void window_draw_border(xcb_window_t win, uint32_t border_color_pxl)
-{
- xcb_change_window_attributes(dpy, win, XCB_CW_BORDER_PIXEL, &border_color_pxl);
-}
-
-void adopt_orphans(void)
-{
- xcb_query_tree_reply_t *qtr = xcb_query_tree_reply(dpy, xcb_query_tree(dpy, root), NULL);
- if (qtr == NULL) {
- return;
- }
-
- int len = xcb_query_tree_children_length(qtr);
- xcb_window_t *wins = xcb_query_tree_children(qtr);
-
- for (int i = 0; i < len; i++) {
- uint32_t idx;
- xcb_window_t win = wins[i];
- if (xcb_ewmh_get_wm_desktop_reply(ewmh, xcb_ewmh_get_wm_desktop(ewmh, win), &idx, NULL) == 1) {
- schedule_window(win);
- }
- }
-
- free(qtr);
-}
-
-uint32_t get_border_color(bool focused_node, bool focused_monitor)
-{
- if (focused_monitor && focused_node) {
- return get_color_pixel(focused_border_color);
- } else if (focused_node) {
- return get_color_pixel(active_border_color);
- } else {
- return get_color_pixel(normal_border_color);
- }
-}
-
-void initialize_floating_rectangle(node_t *n)
-{
- client_t *c = n->client;
-
- xcb_get_geometry_reply_t *geo = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, n->id), NULL);
-
- if (geo != NULL) {
- c->floating_rectangle = (xcb_rectangle_t) {geo->x, geo->y, geo->width, geo->height};
- }
-
- free(geo);
-}
-
-xcb_rectangle_t get_window_rectangle(node_t *n)
-{
- client_t *c = n->client;
- if (c != NULL) {
- xcb_get_geometry_reply_t *g = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, n->id), NULL);
- if (g != NULL) {
- xcb_rectangle_t rect = (xcb_rectangle_t) {g->x, g->y, g->width, g->height};
- free(g);
- return rect;
- }
- }
- return (xcb_rectangle_t) {0, 0, screen_width, screen_height};
-}
-
-bool move_client(coordinates_t *loc, int dx, int dy)
-{
- node_t *n = loc->node;
-
- if (n == NULL || n->client == NULL) {
- return false;
- }
-
- monitor_t *pm = NULL;
-
- if (IS_TILED(n->client)) {
- if (!grabbing) {
- return false;
- }
- xcb_window_t pwin = XCB_NONE;
- query_pointer(&pwin, NULL);
- if (pwin == n->id) {
- return false;
- }
- coordinates_t dst;
- bool is_managed = (pwin != XCB_NONE && locate_window(pwin, &dst));
- if (is_managed && dst.monitor == loc->monitor && IS_TILED(dst.node->client)) {
- swap_nodes(loc->monitor, loc->desktop, n, loc->monitor, loc->desktop, dst.node);
- return true;
- } else {
- if (is_managed && dst.monitor == loc->monitor) {
- return false;
- } else {
- xcb_point_t pt = {0, 0};
- query_pointer(NULL, &pt);
- pm = monitor_from_point(pt);
- }
- }
- } else {
- client_t *c = n->client;
- xcb_rectangle_t rect = c->floating_rectangle;
- int16_t x = rect.x + dx;
- int16_t y = rect.y + dy;
-
- if (focus_follows_pointer) {
- listen_enter_notify(loc->desktop->root, false);
- }
-
- window_move(n->id, x, y);
-
- if (focus_follows_pointer) {
- listen_enter_notify(loc->desktop->root, true);
- }
-
- c->floating_rectangle.x = x;
- c->floating_rectangle.y = y;
- if (!grabbing) {
- put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", loc->monitor->id, loc->desktop->id, loc->node->id, rect.width, rect.height, x, y);
- }
- pm = monitor_from_client(c);
- }
-
- if (pm == NULL || pm == loc->monitor) {
- return true;
- }
-
- bool focused = (n == mon->desk->focus);
- transfer_node(loc->monitor, loc->desktop, n, pm, pm->desk, pm->desk->focus);
- loc->monitor = pm;
- loc->desktop = pm->desk;
- if (focused) {
- focus_node(pm, pm->desk, n);
- }
-
- return true;
-}
-
-bool resize_client(coordinates_t *loc, resize_handle_t rh, int dx, int dy)
-{
- node_t *n = loc->node;
- if (n == NULL || n->client == NULL || n->client->state == STATE_FULLSCREEN) {
- return false;
- }
- node_t *horizontal_fence = NULL, *vertical_fence = NULL;
- xcb_rectangle_t rect = get_rectangle(NULL, n);
- uint16_t width = rect.width, height = rect.height;
- int16_t x = rect.x, y = rect.y;
- if (n->client->state == STATE_TILED) {
- if (rh & HANDLE_LEFT) {
- vertical_fence = find_fence(n, DIR_WEST);
- } else if (rh & HANDLE_RIGHT) {
- vertical_fence = find_fence(n, DIR_EAST);
- }
- if (rh & HANDLE_TOP) {
- horizontal_fence = find_fence(n, DIR_NORTH);
- } else if (rh & HANDLE_BOTTOM) {
- horizontal_fence = find_fence(n, DIR_SOUTH);
- }
- if (vertical_fence == NULL && horizontal_fence == NULL) {
- return false;
- }
- if (vertical_fence != NULL) {
- double sr = vertical_fence->split_ratio + (double) dx / vertical_fence->rectangle.width;
- sr = MAX(0, sr);
- sr = MIN(1, sr);
- vertical_fence->split_ratio = sr;
- }
- if (horizontal_fence != NULL) {
- double sr = horizontal_fence->split_ratio + (double) dy / horizontal_fence->rectangle.height;
- sr = MAX(0, sr);
- sr = MIN(1, sr);
- horizontal_fence->split_ratio = sr;
- }
- arrange(loc->monitor, loc->desktop);
- } else {
- int w = width + dx * (rh & HANDLE_LEFT ? -1 : (rh & HANDLE_RIGHT ? 1 : 0));
- int h = height + dy * (rh & HANDLE_TOP ? -1 : (rh & HANDLE_BOTTOM ? 1 : 0));
- width = MAX(1, w);
- height = MAX(1, h);
- apply_size_hints(n->client, &width, &height);
- if (rh & HANDLE_LEFT) {
- x += rect.width - width;
- }
- if (rh & HANDLE_TOP) {
- y += rect.height - height;
- }
- n->client->floating_rectangle = (xcb_rectangle_t) {x, y, width, height};
- if (n->client->state == STATE_FLOATING) {
- if (focus_follows_pointer) {
- listen_enter_notify(loc->desktop->root, false);
- }
-
- window_move_resize(n->id, x, y, width, height);
-
- if (focus_follows_pointer) {
- listen_enter_notify(loc->desktop->root, true);
- }
-
- if (!grabbing) {
- put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", loc->monitor->id, loc->desktop->id, loc->node->id, width, height, x, y);
- }
- } else {
- arrange(loc->monitor, loc->desktop);
- }
- }
- return true;
-}
-
-/* taken from awesomeWM */
-void apply_size_hints(client_t *c, uint16_t *width, uint16_t *height)
-{
- if (!honor_size_hints) {
- return;
- }
-
- int32_t minw = 0, minh = 0;
- int32_t basew = 0, baseh = 0, real_basew = 0, real_baseh = 0;
-
- if (c->state == STATE_FULLSCREEN) {
- return;
- }
-
- if (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_BASE_SIZE) {
- basew = c->size_hints.base_width;
- baseh = c->size_hints.base_height;
- real_basew = basew;
- real_baseh = baseh;
- } else if (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE) {
- /* base size is substituted with min size if not specified */
- basew = c->size_hints.min_width;
- baseh = c->size_hints.min_height;
- }
-
- if (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE) {
- minw = c->size_hints.min_width;
- minh = c->size_hints.min_height;
- } else if (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_BASE_SIZE) {
- /* min size is substituted with base size if not specified */
- minw = c->size_hints.base_width;
- minh = c->size_hints.base_height;
- }
-
- /* Handle the size aspect ratio */
- if (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_ASPECT &&
- c->size_hints.min_aspect_den > 0 &&
- c->size_hints.max_aspect_den > 0 &&
- *height > real_baseh &&
- *width > real_basew) {
- /* ICCCM mandates:
- * If a base size is provided along with the aspect ratio fields, the base size should be subtracted from the
- * window size prior to checking that the aspect ratio falls in range. If a base size is not provided, nothing
- * should be subtracted from the window size. (The minimum size is not to be used in place of the base size for
- * this purpose.)
- */
- double dx = *width - real_basew;
- double dy = *height - real_baseh;
- double ratio = dx / dy;
- double min = c->size_hints.min_aspect_num / (double) c->size_hints.min_aspect_den;
- double max = c->size_hints.max_aspect_num / (double) c->size_hints.max_aspect_den;
-
- if (max > 0 && min > 0 && ratio > 0) {
- if (ratio < min) {
- /* dx is lower than allowed, make dy lower to compensate this (+ 0.5 to force proper rounding). */
- dy = dx / min + 0.5;
- *width = dx + real_basew;
- *height = dy + real_baseh;
- } else if (ratio > max) {
- /* dx is too high, lower it (+0.5 for proper rounding) */
- dx = dy * max + 0.5;
- *width = dx + real_basew;
- *height = dy + real_baseh;
- }
- }
- }
-
- /* Handle the minimum size */
- *width = MAX(*width, minw);
- *height = MAX(*height, minh);
-
- /* Handle the maximum size */
- if (c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)
- {
- if (c->size_hints.max_width > 0) {
- *width = MIN(*width, c->size_hints.max_width);
- }
- if (c->size_hints.max_height > 0) {
- *height = MIN(*height, c->size_hints.max_height);
- }
- }
-
- /* Handle the size increment */
- if (c->size_hints.flags & (XCB_ICCCM_SIZE_HINT_P_RESIZE_INC | XCB_ICCCM_SIZE_HINT_BASE_SIZE) &&
- c->size_hints.width_inc > 0 && c->size_hints.height_inc > 0) {
- uint16_t t1 = *width, t2 = *height;
- unsigned_subtract(t1, basew);
- unsigned_subtract(t2, baseh);
- *width -= t1 % c->size_hints.width_inc;
- *height -= t2 % c->size_hints.height_inc;
- }
-}
-
-void query_pointer(xcb_window_t *win, xcb_point_t *pt)
-{
- xcb_query_pointer_reply_t *qpr = xcb_query_pointer_reply(dpy, xcb_query_pointer(dpy, root), NULL);
-
- if (qpr != NULL) {
- if (win != NULL) {
- *win = qpr->child;
- xcb_point_t pt = {qpr->root_x, qpr->root_y};
- for (stacking_list_t *s = stack_tail; s != NULL; s = s->prev) {
- if (!s->node->client->shown || s->node->hidden) {
- continue;
- }
- xcb_rectangle_t rect = get_rectangle(NULL, s->node);
- if (is_inside(pt, rect)) {
- if (s->node->id == qpr->child || is_presel_window(qpr->child)) {
- *win = s->node->id;
- }
- break;
- }
- }
- }
- if (pt != NULL) {
- *pt = (xcb_point_t) {qpr->root_x, qpr->root_y};
- }
- }
-
- free(qpr);
-}
-
-void window_border_width(xcb_window_t win, uint32_t bw)
-{
- uint32_t values[] = {bw};
- xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_BORDER_WIDTH, values);
-}
-
-void window_move(xcb_window_t win, int16_t x, int16_t y)
-{
- uint32_t values[] = {x, y};
- xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_X_Y, values);
-}
-
-void window_resize(xcb_window_t win, uint16_t w, uint16_t h)
-{
- uint32_t values[] = {w, h};
- xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_WIDTH_HEIGHT, values);
-}
-
-void window_move_resize(xcb_window_t win, int16_t x, int16_t y, uint16_t w, uint16_t h)
-{
- uint32_t values[] = {x, y, w, h};
- xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_X_Y_WIDTH_HEIGHT, values);
-}
-
-void window_center(monitor_t *m, client_t *c)
-{
- xcb_rectangle_t *r = &c->floating_rectangle;
- xcb_rectangle_t a = m->rectangle;
- if (r->width >= a.width) {
- r->x = a.x;
- } else {
- r->x = a.x + (a.width - r->width) / 2;
- }
- if (r->height >= a.height) {
- r->y = a.y;
- } else {
- r->y = a.y + (a.height - r->height) / 2;
- }
- r->x -= c->border_width;
- r->y -= c->border_width;
-}
-
-void window_stack(xcb_window_t w1, xcb_window_t w2, uint32_t mode)
-{
- if (w2 == XCB_NONE) {
- return;
- }
- uint16_t mask = XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE;
- uint32_t values[] = {w2, mode};
- xcb_configure_window(dpy, w1, mask, values);
-}
-
-void window_above(xcb_window_t w1, xcb_window_t w2)
-{
- window_stack(w1, w2, XCB_STACK_MODE_ABOVE);
-}
-
-void window_below(xcb_window_t w1, xcb_window_t w2)
-{
- window_stack(w1, w2, XCB_STACK_MODE_BELOW);
-}
-
-void window_lower(xcb_window_t win)
-{
- uint32_t values[] = {XCB_STACK_MODE_BELOW};
- xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_STACK_MODE, values);
-}
-
-void window_set_visibility(xcb_window_t win, bool visible)
-{
- uint32_t values_off[] = {ROOT_EVENT_MASK & ~XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY};
- uint32_t values_on[] = {ROOT_EVENT_MASK};
- xcb_change_window_attributes(dpy, root, XCB_CW_EVENT_MASK, values_off);
- if (visible) {
- xcb_map_window(dpy, win);
- } else {
- xcb_unmap_window(dpy, win);
- }
- xcb_change_window_attributes(dpy, root, XCB_CW_EVENT_MASK, values_on);
-}
-
-void window_hide(xcb_window_t win)
-{
- window_set_visibility(win, false);
-}
-
-void window_show(xcb_window_t win)
-{
- window_set_visibility(win, true);
-}
-
-void update_input_focus(void)
-{
- set_input_focus(mon->desk->focus);
-}
-
-void set_input_focus(node_t *n)
-{
- if (n == NULL || n->client == NULL) {
- clear_input_focus();
- } else {
- if (n->client->icccm_props.input_hint) {
- xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_PARENT, n->id, XCB_CURRENT_TIME);
- } else if (n->client->icccm_props.take_focus) {
- send_client_message(n->id, ewmh->WM_PROTOCOLS, WM_TAKE_FOCUS);
- }
- }
-}
-
-void clear_input_focus(void)
-{
- xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, root, XCB_CURRENT_TIME);
-}
-
-void center_pointer(xcb_rectangle_t r)
-{
- if (grabbing) {
- return;
- }
- int16_t cx = r.x + r.width / 2;
- int16_t cy = r.y + r.height / 2;
- xcb_warp_pointer(dpy, XCB_NONE, root, 0, 0, 0, 0, cx, cy);
-}
-
-void get_atom(char *name, xcb_atom_t *atom)
-{
- xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(dpy, xcb_intern_atom(dpy, 0, strlen(name), name), NULL);
- if (reply != NULL) {
- *atom = reply->atom;
- } else {
- *atom = XCB_NONE;
- }
- free(reply);
-}
-
-void set_atom(xcb_window_t win, xcb_atom_t atom, uint32_t value)
-{
- xcb_change_property(dpy, XCB_PROP_MODE_REPLACE, win, atom, XCB_ATOM_CARDINAL, 32, 1, &value);
-}
-
-void send_client_message(xcb_window_t win, xcb_atom_t property, xcb_atom_t value)
-{
- xcb_client_message_event_t *e = calloc(32, 1);
-
- e->response_type = XCB_CLIENT_MESSAGE;
- e->window = win;
- e->type = property;
- e->format = 32;
- e->data.data32[0] = value;
- e->data.data32[1] = XCB_CURRENT_TIME;
-
- xcb_send_event(dpy, false, win, XCB_EVENT_MASK_NO_EVENT, (char *) e);
- xcb_flush(dpy);
- free(e);
-}
+++ /dev/null
-/* Copyright (c) 2012, 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:
- *
- * 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.
- *
- * 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
- * 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.
- */
-
-#ifndef BSPWM_WINDOW_H
-#define BSPWM_WINDOW_H
-
-#include <stdarg.h>
-#include <xcb/xcb.h>
-#include <xcb/xcb_event.h>
-#include <xcb/xcb_icccm.h>
-#include "types.h"
-
-void schedule_window(xcb_window_t win);
-void manage_window(xcb_window_t win, rule_consequence_t *csq, int fd);
-void set_window_state(xcb_window_t win, xcb_icccm_wm_state_t state);
-void unmanage_window(xcb_window_t win);
-bool is_presel_window(xcb_window_t win);
-void initialize_presel_feedback(node_t *n);
-void draw_presel_feedback(monitor_t *m, desktop_t *d, node_t *n);
-void refresh_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n);
-void show_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n);
-void hide_presel_feedbacks(monitor_t *m, desktop_t *d, node_t *n);
-void update_colors(void);
-void update_colors_in(node_t *n, desktop_t *d, monitor_t *m);
-void draw_border(node_t *n, bool focused_node, bool focused_monitor);
-void window_draw_border(xcb_window_t win, uint32_t border_color_pxl);
-void adopt_orphans(void);
-uint32_t get_border_color(bool focused_node, bool focused_monitor);
-void initialize_floating_rectangle(node_t *n);
-xcb_rectangle_t get_window_rectangle(node_t *n);
-bool move_client(coordinates_t *loc, int dx, int dy);
-bool resize_client(coordinates_t *loc, resize_handle_t rh, int dx, int dy);
-void apply_size_hints(client_t *c, uint16_t *width, uint16_t *height);
-void query_pointer(xcb_window_t *win, xcb_point_t *pt);
-void window_border_width(xcb_window_t win, uint32_t bw);
-void window_move(xcb_window_t win, int16_t x, int16_t y);
-void window_resize(xcb_window_t win, uint16_t w, uint16_t h);
-void window_move_resize(xcb_window_t win, int16_t x, int16_t y, uint16_t w, uint16_t h);
-void window_center(monitor_t *m, client_t *c);
-void window_stack(xcb_window_t w1, xcb_window_t w2, uint32_t mode);
-void window_above(xcb_window_t w1, xcb_window_t w2);
-void window_below(xcb_window_t w1, xcb_window_t w2);
-void window_lower(xcb_window_t win);
-void window_set_visibility(xcb_window_t win, bool visible);
-void window_hide(xcb_window_t win);
-void window_show(xcb_window_t win);
-void update_input_focus(void);
-void set_input_focus(node_t *n);
-void clear_input_focus(void);
-void center_pointer(xcb_rectangle_t r);
-void get_atom(char *name, xcb_atom_t *atom);
-void set_atom(xcb_window_t win, xcb_atom_t atom, uint32_t value);
-void send_client_message(xcb_window_t win, xcb_atom_t property, xcb_atom_t value);
-
-#endif