CPPFLAGS += -D_POSIX_C_SOURCE=200112L -DVERSION=\"$(VERSION)\"
CFLAGS += -std=c99 -pedantic -Wall -Wextra
-LDLIBS = -lm -lxcb -lxcb-util -lxcb-icccm -lxcb-ewmh -lxcb-randr -lxcb-xinerama
+LDLIBS = -lm -lxcb -lxcb-util -lxcb-keysyms -lxcb-icccm -lxcb-ewmh -lxcb-randr -lxcb-xinerama
PREFIX ?= /usr/local
BINPREFIX ?= $(PREFIX)/bin
#include "monitor.h"
#include "settings.h"
#include "messages.h"
+#include "pointer.h"
#include "events.h"
#include "common.h"
#include "window.h"
cleanup();
close(sock_fd);
unlink(socket_path);
+ ungrab_buttons();
xcb_ewmh_connection_wipe(ewmh);
xcb_destroy_window(dpy, meta_window);
xcb_destroy_window(dpy, motion_recorder);
stack_head = stack_tail = NULL;
subscribe_head = subscribe_tail = NULL;
pending_rule_head = pending_rule_tail = NULL;
- last_motion_time = last_motion_x = last_motion_y = 0;
auto_raise = sticky_still = record_history = true;
randr_base = 0;
exit_status = 0;
{
init();
ewmh_init();
+ pointer_init();
+
screen = xcb_setup_roots_iterator(xcb_get_setup(dpy)).data;
if (screen == NULL) {
root = screen->root;
register_events();
+ grab_buttons();
screen_width = screen->width_in_pixels;
screen_height = screen->height_in_pixels;
ewmh_update_number_of_desktops();
ewmh_update_desktop_names();
ewmh_update_current_desktop();
- frozen_pointer = make_pointer_state();
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();
}
empty_history();
- free(frozen_pointer);
}
bool check_connection (xcb_connection_t *dpy)
pending_rule_t *pending_rule_head;
pending_rule_t *pending_rule_tail;
-pointer_state_t *frozen_pointer;
xcb_window_t meta_window;
xcb_window_t motion_recorder;
xcb_atom_t WM_TAKE_FOCUS;
_bspc() {
- local commands='node desktop monitor query pointer rule restore wm subscribe config quit'
+ local commands='node desktop monitor query rule restore wm subscribe config quit'
- local settings='external_rules_command status_prefix normal_border_color active_border_color focused_border_color presel_feedback_color border_width window_gap top_padding right_padding bottom_padding left_padding split_ratio initial_polarity borderless_monocle gapless_monocle leaf_monocle focus_follows_pointer pointer_follows_focus pointer_follows_monitor history_aware_focus focus_by_distance ignore_ewmh_focus center_pseudo_tiled remove_disabled_monitors remove_unplugged_monitors merge_overlapping_monitors'
+ local settings='external_rules_command status_prefix normal_border_color active_border_color focused_border_color presel_feedback_color border_width window_gap top_padding right_padding bottom_padding left_padding split_ratio initial_polarity borderless_monocle gapless_monocle leaf_monocle pointer_modifier click_to_focus focus_follows_pointer pointer_follows_focus pointer_follows_monitor history_aware_focus focus_by_distance ignore_ewmh_focus center_pseudo_tiled honor_size_hints remove_disabled_monitors remove_unplugged_monitors merge_overlapping_monitors'
COMPREPLY=()
_bspc() {
local -a commands settings
- commands=('node' 'desktop' 'monitor' 'query' 'pointer' 'rule' 'restore' 'wm' 'subscribe' 'config' 'quit')
- settings=('external_rules_command' 'status_prefix' 'normal_border_color' 'active_border_color' 'focused_border_color' 'presel_feedback_color' 'border_width' 'window_gap' 'top_padding' 'right_padding' 'bottom_padding' 'left_padding' 'split_ratio' 'initial_polarity' 'borderless_monocle' 'gapless_monocle' 'leaf_monocle' 'focus_follows_pointer' 'pointer_follows_focus' 'pointer_follows_monitor' 'history_aware_focus' 'focus_by_distance' 'ignore_ewmh_focus' 'center_pseudo_tiled' 'remove_disabled_monitors' 'remove_unplugged_monitors' 'merge_overlapping_monitors')
+ commands=('node' 'desktop' 'monitor' 'query' 'rule' 'restore' 'wm' 'subscribe' 'config' 'quit')
+ settings=('external_rules_command' 'status_prefix' 'normal_border_color' 'active_border_color' 'focused_border_color' 'presel_feedback_color' 'border_width' 'window_gap' 'top_padding' 'right_padding' 'bottom_padding' 'left_padding' 'split_ratio' 'initial_polarity' 'borderless_monocle' 'gapless_monocle' 'leaf_monocle' 'pointer_modifier' 'click_to_focus' 'focus_follows_pointer' 'pointer_follows_focus' 'pointer_follows_monitor' 'history_aware_focus' 'focus_by_distance' 'ignore_ewmh_focus' 'center_pseudo_tiled' 'honor_size_hints' 'remove_disabled_monitors' 'remove_unplugged_monitors' 'merge_overlapping_monitors')
if (( CURRENT == 2 )) ; then
_values 'command' "$commands[@]"
elif (( CURRENT == 3 )) ; then
-- Add zoom feature (view point distinct from root).
-- Add receptacles (leaves with NULL client pointer).
-- Handle window size constraints specified by size hints.
- Add support for showing/hiding nodes.
-- Implement all the MUSTs in the EWMH specification.
-- Set more attributes in `make_client` (instead of doing it in `apply_rules`) and don't pass `XCB_NONE` as argument.
+- Add zoom feature (view point distinct from root).
- Use BSD `sys/{queue/tree}.h` for {list,tree} structures?
-- Handle malloc failure everywhere.
.\" Title: bspwm
.\" Author: [see the "Author" section]
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
-.\" Date: 03/26/2016
+.\" Date: 04/07/2016
.\" Manual: Bspwm Manual
-.\" Source: Bspwm 0.9.1-7-geb209d8
+.\" Source: Bspwm 0.9.1-11-ge1b5f77
.\" Language: English
.\"
-.TH "BSPWM" "1" "03/26/2016" "Bspwm 0\&.9\&.1\-7\-geb209d8" "Bspwm Manual"
+.TH "BSPWM" "1" "04/07/2016" "Bspwm 0\&.9\&.1\-11\-ge1b5f77" "Bspwm Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
Set the splitting ratio of the preselection area\&.
.RE
.PP
+\fB\-v\fR, \fB\-\-move\fR \fIdx\fR \fIdy\fR
+.RS 4
+Move the selected window by
+\fIdx\fR
+pixels horizontally and
+\fIdy\fR
+pixels vertically\&.
+.RE
+.PP
+\fB\-z\fR, \fB\-\-resize\fR top|left|bottom|right|top_left|top_right|bottom_right|bottom_left \fIdx\fR \fIdy\fR
+.RS 4
+Resize the selected window by moving the given handle by
+\fIdx\fR
+pixels horizontally and
+\fIdy\fR
+pixels vertically\&.
+.RE
+.PP
\fB\-r\fR, \fB\-\-ratio\fR \fIRATIO\fR|(+|\-)\fIPIXELS\fR
.RS 4
Set the splitting ratio of the selected node (0 <
Print the current status information\&.
.RE
.RE
-.SS "Pointer"
-.sp
-.it 1 an-trap
-.nr an-no-space-flag 1
-.nr an-break-flag 1
-.br
-.ps +1
-\fBGeneral Syntax\fR
-.RS 4
-.sp
-pointer \fICOMMANDS\fR
-.RE
-.sp
-.it 1 an-trap
-.nr an-no-space-flag 1
-.nr an-break-flag 1
-.br
-.ps +1
-\fBCommands\fR
-.RS 4
-.PP
-\fB\-g\fR, \fB\-\-grab\fR focus|move|resize_side|resize_corner
-.RS 4
-Initiate the given pointer action\&.
-.RE
-.PP
-\fB\-t\fR, \fB\-\-track\fR <x> <y>
-.RS 4
-Pass the pointer root coordinates for the current pointer action\&.
-.RE
-.PP
-\fB\-u\fR, \fB\-\-ungrab\fR
-.RS 4
-Terminate the current pointer action\&.
-.RE
-.RE
.SS "Rule"
.sp
.it 1 an-trap
if there\(cqs only one tiled window in the tree\&.
.RE
.PP
+\fIpointer_modifier\fR
+.RS 4
+Keyboard modifier used for moving or resizing windows\&. Accept the following values:
+\fBshift\fR,
+\fBcontrol\fR,
+\fBlock\fR,
+\fBmod1\fR,
+\fBmod2\fR,
+\fBmod3\fR,
+\fBmod4\fR,
+\fBmod5\fR\&.
+.RE
+.PP
+\fIclick_to_focus\fR
+.RS 4
+Focus a window (or a monitor) by clicking it\&.
+.RE
+.PP
\fIfocus_follows_pointer\fR
.RS 4
Focus the window under the pointer\&.
\fItrue\fR\&.
.RE
.PP
+\fIhonor_size_hints\fR
+.RS 4
+Apply ICCCM window size hints\&.
+.RE
+.PP
\fIremove_disabled_monitors\fR
.RS 4
Consider disabled monitors as disconnected\&.
.RS 4
Window border width\&.
.RE
+.SH "POINTER BINDINGS"
+.PP
+\fIbutton1\fR
+.RS 4
+Focus the window under the pointer if
+\fIclick_to_focus\fR
+is set\&.
+.RE
+.PP
+\fIpointer_modifier\fR + \fIbutton1\fR
+.RS 4
+Move the window under the pointer\&.
+.RE
+.PP
+\fIpointer_modifier\fR + \fIbutton2\fR
+.RS 4
+Resize the window under the pointer by dragging the nearest side\&.
+.RE
+.PP
+\fIpointer_modifier\fR + \fIbutton3\fR
+.RS 4
+Resize the window under the pointer by dragging the nearest corner\&.
+.RE
.SH "EVENTS"
.PP
\fIreport\fR
*-o*, *--presel-ratio* 'RATIO'::
Set the splitting ratio of the preselection area.
+*-v*, *--move* 'dx' 'dy'::
+ Move the selected window by 'dx' pixels horizontally and 'dy' pixels vertically.
+
+*-z*, *--resize* top|left|bottom|right|top_left|top_right|bottom_right|bottom_left 'dx' 'dy'::
+ Resize the selected window by moving the given handle by 'dx' pixels horizontally and 'dy' pixels vertically.
+
*-r*, *--ratio* 'RATIO'|(+|-)'PIXELS'::
Set the splitting ratio of the selected node (0 < 'RATIO' < 1).
*-g*, *--get-status*::
Print the current status information.
-Pointer
-~~~~~~~
-
-General Syntax
-^^^^^^^^^^^^^^
-
-pointer 'COMMANDS'
-
-Commands
-^^^^^^^^
-
-*-g*, *--grab* focus|move|resize_side|resize_corner::
- Initiate the given pointer action.
-
-*-t*, *--track* <x> <y>::
- Pass the pointer root coordinates for the current pointer action.
-
-*-u*, *--ungrab*::
- Terminate the current pointer action.
-
Rule
~~~~
'single_monocle'::
Set the desktop layout to *monocle* if there's only one tiled window in the tree.
+'pointer_modifier'::
+ Keyboard modifier used for moving or resizing windows. Accept the following values: *shift*, *control*, *lock*, *mod1*, *mod2*, *mod3*, *mod4*, *mod5*.
+
+'click_to_focus'::
+ Focus a window (or a monitor) by clicking it.
+
'focus_follows_pointer'::
Focus the window under the pointer.
'center_pseudo_tiled'::
Center pseudo tiled windows into their tiling rectangles. Defaults to 'true'.
+'honor_size_hints'::
+ Apply ICCCM window size hints.
+
'remove_disabled_monitors'::
Consider disabled monitors as disconnected.
'border_width'::
Window border width.
+Pointer Bindings
+----------------
+
+'button1'::
+ Focus the window under the pointer if 'click_to_focus' is set.
+
+'pointer_modifier' + 'button1'::
+ Move the window under the pointer.
+
+'pointer_modifier' + 'button2'::
+ Resize the window under the pointer by dragging the nearest side.
+
+'pointer_modifier' + 'button3'::
+ Resize the window under the pointer by dragging the nearest corner.
Events
------
#include "subscribe.h"
#include "tree.h"
#include "window.h"
+#include "pointer.h"
#include "events.h"
void handle_event(xcb_generic_event_t *evt)
case XCB_ENTER_NOTIFY:
enter_notify(evt);
break;
+ case XCB_BUTTON_PRESS:
+ button_press(evt);
+ break;
case XCB_MOTION_NOTIFY:
motion_notify(evt);
break;
coordinates_t loc;
bool is_managed = locate_window(e->window, &loc);
client_t *c = (is_managed ? loc.node->client : NULL);
- int w = 0, h = 0;
-
- if (is_managed && !IS_FLOATING(c)) {
- 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) {
- w = e->width;
- }
- if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
- h = e->height;
- }
-
- if (w != 0) {
- restrain_floating_width(c, &w);
- c->floating_rectangle.width = w;
- }
-
- if (h != 0) {
- restrain_floating_height(c, &h);
- c->floating_rectangle.height = h;
- }
-
- xcb_configure_notify_event_t evt;
- unsigned int bw = c->border_width;
-
- xcb_rectangle_t rect = get_rectangle(loc.desktop, loc.node);
-
- evt.response_type = XCB_CONFIGURE_NOTIFY;
- evt.event = e->window;
- evt.window = e->window;
- evt.above_sibling = XCB_NONE;
- evt.x = rect.x;
- evt.y = rect.y;
- evt.width = rect.width;
- evt.height = rect.height;
- evt.border_width = bw;
- evt.override_redirect = false;
+ uint16_t width, height;
- xcb_send_event(dpy, false, e->window, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (const char *) &evt);
-
- if (c->state == STATE_PSEUDO_TILED) {
- arrange(loc.monitor, loc.desktop);
- }
- } else {
+ 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 (is_managed) {
- c->floating_rectangle.x = e->x;
- }
}
if (e->value_mask & XCB_CONFIG_WINDOW_Y) {
mask |= XCB_CONFIG_WINDOW_Y;
values[i++] = e->y;
- if (is_managed) {
- c->floating_rectangle.y = e->y;
- }
}
if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
mask |= XCB_CONFIG_WINDOW_WIDTH;
- w = e->width;
- if (is_managed) {
- restrain_floating_width(c, &w);
- c->floating_rectangle.width = w;
- }
- values[i++] = w;
+ values[i++] = e->width;
}
if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
mask |= XCB_CONFIG_WINDOW_HEIGHT;
- h = e->height;
- if (is_managed) {
- restrain_floating_height(c, &h);
- c->floating_rectangle.height = h;
- }
- values[i++] = h;
+ values[i++] = e->height;
}
- if (!is_managed && e->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH) {
+ if (e->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH) {
mask |= XCB_CONFIG_WINDOW_BORDER_WIDTH;
values[i++] = e->border_width;
}
- if (!is_managed && e->value_mask & XCB_CONFIG_WINDOW_SIBLING) {
+ if (e->value_mask & XCB_CONFIG_WINDOW_SIBLING) {
mask |= XCB_CONFIG_WINDOW_SIBLING;
values[i++] = e->sibling;
}
- if (!is_managed && e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) {
+ 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);
- if (is_managed && mask & XCB_CONFIG_WINDOW_X_Y_WIDTH_HEIGHT) {
- xcb_rectangle_t r = c->floating_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, e->window, r.width, r.height, r.x, r.y);
+ } 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 (is_managed) {
+ 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;
+
+ window_move_resize(e->window, r.x, r.y, r.width, r.height);
+
+ 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);
}
}
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;
- xcb_size_hints_t size_hints;
- if (xcb_icccm_get_wm_normal_hints_reply(dpy, xcb_icccm_get_wm_normal_hints(dpy, e->window), &size_hints, NULL) == 1 &&
- (size_hints.flags & (XCB_ICCCM_SIZE_HINT_P_MIN_SIZE | XCB_ICCCM_SIZE_HINT_P_MAX_SIZE))) {
- c->min_width = size_hints.min_width;
- c->max_width = size_hints.max_width;
- c->min_height = size_hints.min_height;
- c->max_height = size_hints.max_height;
- int w = c->floating_rectangle.width;
- int h = c->floating_rectangle.height;
- restrain_floating_size(c, &w, &h);
- c->floating_rectangle.width = w;
- c->floating_rectangle.height = h;
+ 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 button_press(xcb_generic_event_t *evt)
+{
+ xcb_button_press_event_t *e = (xcb_button_press_event_t *) evt;
+ switch (e->detail) {
+ case XCB_BUTTON_INDEX_1:
+ if (click_to_focus && cleaned_mask(e->state) == XCB_NONE) {
+ xcb_allow_events(dpy, XCB_ALLOW_REPLAY_POINTER, e->time);
+ xcb_flush(dpy);
+ grab_pointer(ACTION_FOCUS);
+ } else {
+ grab_pointer(ACTION_MOVE);
+ }
+ break;
+ case XCB_BUTTON_INDEX_2:
+ grab_pointer(ACTION_RESIZE_SIDE);
+ break;
+ case XCB_BUTTON_INDEX_3:
+ grab_pointer(ACTION_RESIZE_CORNER);
+ break;
+ }
+}
+
void enter_notify(xcb_generic_event_t *evt)
{
xcb_enter_notify_event_t *e = (xcb_enter_notify_event_t *) evt;
{
xcb_motion_notify_event_t *e = (xcb_motion_notify_event_t *) evt;
- int dtime = e->time - last_motion_time;
+ static uint16_t last_motion_x = 0, last_motion_y = 0;
+ static xcb_timestamp_t last_motion_time = 0;
+
+ int64_t dtime = e->time - last_motion_time;
+
if (dtime > 1000) {
last_motion_time = e->time;
last_motion_x = e->event_x;
} 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)
+ HANDLE_WM_STATE(HIDDEN)
}
+#undef HANDLE_WM_STATE
}
void process_error(xcb_generic_event_t *evt)
#include <xcb/xcb_event.h>
uint8_t randr_base;
-uint16_t last_motion_x, last_motion_y;
-xcb_timestamp_t last_motion_time;
void handle_event(xcb_generic_event_t *evt);
void map_request(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 motion_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);
}
}
-bool ewmh_wm_state_add(node_t *n, xcb_atom_t state)
+void ewmh_wm_state_update(node_t *n)
{
client_t *c = n->client;
-
- if (c == NULL || c->wm_states_count >= MAX_WM_STATES) {
- return false;
- }
-
- for (int i = 0; i < c->wm_states_count; i++) {
- if (c->wm_state[i] == state) {
- return false;
- }
- }
-
- c->wm_state[c->wm_states_count] = state;
- c->wm_states_count++;
- xcb_ewmh_set_wm_state(ewmh, n->id, c->wm_states_count, c->wm_state);
- return true;
-}
-
-bool ewmh_wm_state_remove(node_t *n, xcb_atom_t state)
-{
- client_t *c = n->client;
- if (c == NULL) {
- return false;
+ 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; \
}
- for (int i = 0; i < c->wm_states_count; i++) {
- if (c->wm_state[i] == state) {
- for (int j = i; j < (c->wm_states_count - 1); j++) {
- c->wm_state[j] = c->wm_state[j + 1];
- }
- c->wm_states_count--;
- xcb_ewmh_set_wm_state(ewmh, n->id, c->wm_states_count, c->wm_state);
- return true;
- }
- }
- return false;
+ 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)
void ewmh_update_wm_desktops(void);
void ewmh_update_desktop_names(void);
void ewmh_update_client_list(bool stacking);
-bool ewmh_wm_state_add(node_t *n, xcb_atom_t state);
-bool ewmh_wm_state_remove(node_t *n, xcb_atom_t state);
+void ewmh_wm_state_update(node_t *n);
void ewmh_set_supporting(xcb_window_t win);
#endif
bspc query -N -d | xargs -I id -n 1 bspc node id -p cancel
#
-# resize tiled/floating
+# move/resize
#
-# expand the tiled space in the given direction
+# expand a window by moving one of its side outward
super + alt + {h,j,k,l}
- bspc node {@west -r -10,@south -r +10,@north -r -10,@east -r +10}
+ bspc node -z {left -20 0,bottom 0 20,top 0 -20,right 20 0}
-# contract the tiled space in the given direction
-super + alt + shift + {h,j,k,l}
- bspc node {@east -r -10,@north -r +10,@south -r -10,@west -r +10}
+# contract a window by moving one of its side inward
+super + alt + shit + {h,j,k,l}
+ bspc node -z {right -20 0,top 0 20,bottom 0 -20,left 20 0}
# move a floating window
super + {Left,Down,Up,Right}
- xdo move {-x -20,-y +20,-y -20,-x +20}
-
-#
-# pointer focus/move/resize
-#
-
-# focus
-~button1
- bspc pointer -g focus
-
-# start move/resize
-super + button{1-3}
- ; bspc pointer -g {move,resize_side,resize_corner}
-
-# end move/resize
-super + @button{1-3}
- bspc pointer -u
+ bspc node -v {-20 0,0 20,0 -20,20 0}
#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, ...);
cmd_wm(++args, --num, rsp);
} else if (streq("rule", *args)) {
cmd_rule(++args, --num, rsp);
- } else if (streq("pointer", *args)) {
- cmd_pointer(++args, --num, rsp);
} else if (streq("config", *args)) {
cmd_config(++args, --num, rsp);
} else if (streq("quit", *args)) {
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) {
}
}
-void cmd_pointer(char **args, int num, FILE *rsp)
-{
- if (num < 1) {
- fail(rsp, "pointer: Missing commands.\n");
- return;
- }
-
- while (num > 0) {
- if (streq("-t", *args) || streq("--track", *args)) {
- num--, args++;
- if (num < 2) {
- fail(rsp, "pointer %s: Not enough arguments.\n", *(args - 1));
- return;
- }
- int x, y;
- if (sscanf(*args, "%i", &x) == 1 && sscanf(*(args + 1), "%i", &y) == 1) {
- track_pointer(x, y);
- } else {
- fail(rsp, "");
- return;
- }
- num--, args++;
- } else if (streq("-g", *args) || streq("--grab", *args)) {
- num--, args++;
- if (num < 1) {
- fail(rsp, "pointer %s: Not enough arguments.\n", *(args - 1));
- return;
- }
- pointer_action_t pac;
- if (parse_pointer_action(*args, &pac)) {
- grab_pointer(pac);
- } else {
- fail(rsp, "pointer %s: Invalid argument: '%s'.\n", *(args - 1), *args);
- return;
- }
- } else if (streq("-u", *args) || streq("--ungrab", *args)) {
- ungrab_pointer();
- } else {
- fail(rsp, "pointer: Unknown command: '%s'.\n", *args);
- return;
- }
- num--, args++;
- }
-}
-
void cmd_wm(char **args, int num, FILE *rsp)
{
if (num < 1) {
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("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)) {
SET_BOOL(focus_by_distance)
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)) { \
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);
#define GET_COLOR(s) \
} else if (streq(#s, name)) { \
fprintf(rsp, "%s", s);
GET_BOOL(gapless_monocle)
GET_BOOL(paddingless_monocle)
GET_BOOL(single_monocle)
+ GET_BOOL(click_to_focus)
GET_BOOL(focus_follows_pointer)
GET_BOOL(pointer_follows_focus)
GET_BOOL(pointer_follows_monitor)
GET_BOOL(focus_by_distance)
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)
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_pointer(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 adapt_geometry(xcb_rectangle_t *rs, xcb_rectangle_t *rd, node_t *n)
{
- if (frozen_pointer->action != ACTION_NONE) {
- return;
- }
-
for (node_t *f = first_extrema(n); f != NULL; f = next_leaf(f, n)) {
if (f->client == NULL) {
continue;
mon = m;
if (pointer_follows_monitor) {
- center_pointer(m->rectangle);
+ xcb_point_t pt;
+ query_pointer(NULL, &pt);
+ monitor_t *mp = monitor_from_point(pt);
+ if (mp != m) {
+ center_pointer(m->rectangle);
+ }
}
put_status(SBSC_MASK_MONITOR_FOCUS, "monitor_focus 0x%08X\n", m->id);
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)) {
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);
* 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 "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 grab_buttons(void)
+{
+#define GRAB(b, m) \
+ xcb_grab_button(dpy, false, root, XCB_EVENT_MASK_BUTTON_PRESS, \
+ XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, XCB_NONE, XCB_NONE, b, m)
+ uint8_t buttons[] = {XCB_BUTTON_INDEX_1, XCB_BUTTON_INDEX_2, XCB_BUTTON_INDEX_3};
+ for (unsigned int i = 0; i < LENGTH(buttons); i++) {
+ uint8_t button = buttons[i];
+ if (click_to_focus && button == XCB_BUTTON_INDEX_1) {
+ GRAB(button, XCB_MOD_MASK_ANY);
+ continue;
+ }
+ GRAB(button, pointer_modifier);
+ if (num_lock != XCB_NO_SYMBOL && caps_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
+ GRAB(button, pointer_modifier | num_lock | caps_lock | scroll_lock);
+ }
+ if (num_lock != XCB_NO_SYMBOL && caps_lock != XCB_NO_SYMBOL) {
+ GRAB(button, pointer_modifier | num_lock | caps_lock);
+ }
+ if (caps_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
+ GRAB(button, pointer_modifier | caps_lock | scroll_lock);
+ }
+ if (num_lock != XCB_NO_SYMBOL && scroll_lock != XCB_NO_SYMBOL) {
+ GRAB(button, pointer_modifier | num_lock | scroll_lock);
+ }
+ if (num_lock != XCB_NO_SYMBOL) {
+ GRAB(button, pointer_modifier | num_lock);
+ }
+ if (caps_lock != XCB_NO_SYMBOL) {
+ GRAB(button, pointer_modifier | caps_lock);
+ }
+ if (scroll_lock != XCB_NO_SYMBOL) {
+ GRAB(button, pointer_modifier | scroll_lock);
+ }
+ }
+#undef GRAB
+}
+
+void ungrab_buttons(void)
+{
+ xcb_ungrab_button(dpy, XCB_BUTTON_INDEX_ANY, root, 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;
+}
void grab_pointer(pointer_action_t pac)
{
query_pointer(&win, &pos);
coordinates_t loc;
- if (locate_window(win, &loc)) {
- client_t *c = loc.node->client;
- frozen_pointer->position = pos;
- frozen_pointer->action = pac;
- frozen_pointer->monitor = loc.monitor;
- frozen_pointer->desktop = loc.desktop;
- frozen_pointer->node = loc.node;
- frozen_pointer->client = c;
- frozen_pointer->window = loc.node->id;
- frozen_pointer->horizontal_fence = NULL;
- frozen_pointer->vertical_fence = NULL;
-
- switch (pac) {
- case ACTION_FOCUS:
- if (loc.node != mon->desk->focus) {
- bool backup = pointer_follows_monitor;
- pointer_follows_monitor = false;
- focus_node(loc.monitor, loc.desktop, loc.node);
- pointer_follows_monitor = backup;
- } else if (focus_follows_pointer) {
- stack(loc.desktop, loc.node, true);
- }
- frozen_pointer->action = ACTION_NONE;
- break;
- case ACTION_MOVE:
- case ACTION_RESIZE_SIDE:
- case ACTION_RESIZE_CORNER:
- if (IS_FLOATING(c)) {
- frozen_pointer->rectangle = c->floating_rectangle;
- frozen_pointer->is_tiled = false;
- } else if (IS_TILED(c)) {
- frozen_pointer->rectangle = c->tiled_rectangle;
- frozen_pointer->is_tiled = (pac == ACTION_MOVE || c->state != STATE_PSEUDO_TILED);
- } else {
- frozen_pointer->action = ACTION_NONE;
- return;
- }
- if (pac == ACTION_RESIZE_SIDE) {
- float W = frozen_pointer->rectangle.width;
- float H = frozen_pointer->rectangle.height;
- float ratio = W / H;
- float x = pos.x - frozen_pointer->rectangle.x;
- float y = pos.y - frozen_pointer->rectangle.y;
- float diag_a = ratio * y;
- float diag_b = W - diag_a;
- if (x < diag_a) {
- if (x < diag_b) {
- frozen_pointer->side = SIDE_LEFT;
- } else {
- frozen_pointer->side = SIDE_BOTTOM;
- }
- } else {
- if (x < diag_b) {
- frozen_pointer->side = SIDE_TOP;
- } else {
- frozen_pointer->side = SIDE_RIGHT;
- }
- }
- } else if (pac == ACTION_RESIZE_CORNER) {
- int16_t mid_x = frozen_pointer->rectangle.x + (frozen_pointer->rectangle.width / 2);
- int16_t mid_y = frozen_pointer->rectangle.y + (frozen_pointer->rectangle.height / 2);
- if (pos.x > mid_x) {
- if (pos.y > mid_y) {
- frozen_pointer->corner = CORNER_BOTTOM_RIGHT;
- } else {
- frozen_pointer->corner = CORNER_TOP_RIGHT;
- }
- } else {
- if (pos.y > mid_y) {
- frozen_pointer->corner = CORNER_BOTTOM_LEFT;
- } else {
- frozen_pointer->corner = CORNER_TOP_LEFT;
- }
- }
- }
- if (frozen_pointer->is_tiled) {
- if (pac == ACTION_RESIZE_SIDE) {
- switch (frozen_pointer->side) {
- case SIDE_TOP:
- frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_NORTH);
- break;
- case SIDE_RIGHT:
- frozen_pointer->vertical_fence = find_fence(loc.node, DIR_EAST);
- break;
- case SIDE_BOTTOM:
- frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_SOUTH);
- break;
- case SIDE_LEFT:
- frozen_pointer->vertical_fence = find_fence(loc.node, DIR_WEST);
- break;
- }
- } else if (pac == ACTION_RESIZE_CORNER) {
- switch (frozen_pointer->corner) {
- case CORNER_TOP_LEFT:
- frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_NORTH);
- frozen_pointer->vertical_fence = find_fence(loc.node, DIR_WEST);
- break;
- case CORNER_TOP_RIGHT:
- frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_NORTH);
- frozen_pointer->vertical_fence = find_fence(loc.node, DIR_EAST);
- break;
- case CORNER_BOTTOM_RIGHT:
- frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_SOUTH);
- frozen_pointer->vertical_fence = find_fence(loc.node, DIR_EAST);
- break;
- case CORNER_BOTTOM_LEFT:
- frozen_pointer->horizontal_fence = find_fence(loc.node, DIR_SOUTH);
- frozen_pointer->vertical_fence = find_fence(loc.node, DIR_WEST);
- break;
- }
- }
- if (frozen_pointer->horizontal_fence != NULL) {
- frozen_pointer->horizontal_ratio = frozen_pointer->horizontal_fence->split_ratio;
- }
- if (frozen_pointer->vertical_fence != NULL) {
- frozen_pointer->vertical_ratio = frozen_pointer->vertical_fence->split_ratio;
- }
- }
- break;
- case ACTION_NONE:
- break;
- }
- } else {
+ 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);
}
}
- frozen_pointer->action = ACTION_NONE;
+ return;
}
-}
-void track_pointer(int root_x, int root_y)
-{
- if (frozen_pointer->action == ACTION_NONE) {
+ if (pac == ACTION_FOCUS) {
+ if (loc.node != mon->desk->focus) {
+ focus_node(loc.monitor, loc.desktop, loc.node);
+ } else if (focus_follows_pointer) {
+ stack(loc.desktop, loc.node, true);
+ }
return;
}
- int delta_x, delta_y, x = 0, y = 0, w = 1, h = 1;
+ if (loc.node->client->state == STATE_FULLSCREEN) {
+ return;
+ }
- pointer_action_t pac = frozen_pointer->action;
- monitor_t *m = frozen_pointer->monitor;
- desktop_t *d = frozen_pointer->desktop;
- node_t *n = frozen_pointer->node;
- client_t *c = frozen_pointer->client;
- xcb_window_t win = frozen_pointer->window;
- xcb_rectangle_t rect = frozen_pointer->rectangle;
- node_t *vertical_fence = frozen_pointer->vertical_fence;
- node_t *horizontal_fence = frozen_pointer->horizontal_fence;
+ 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);
- delta_x = root_x - frozen_pointer->position.x;
- delta_y = root_y - frozen_pointer->position.y;
+ if (reply == NULL || reply->status != XCB_GRAB_STATUS_SUCCESS) {
+ return;
+ }
- switch (pac) {
- case ACTION_MOVE:
- if (frozen_pointer->is_tiled) {
- xcb_window_t pwin = XCB_NONE;
- query_pointer(&pwin, NULL);
- if (pwin == win) {
- return;
- }
- coordinates_t loc;
- bool is_managed = (pwin == XCB_NONE ? false : locate_window(pwin, &loc));
- if (is_managed && !IS_FLOATING(loc.node->client) && loc.monitor == m) {
- swap_nodes(m, d, n, m, d, loc.node);
- } else {
- if (is_managed && loc.monitor == m) {
- return;
- } else if (!is_managed) {
- xcb_point_t pt = (xcb_point_t) {root_x, root_y};
- monitor_t *pmon = monitor_from_point(pt);
- if (pmon == NULL || pmon == m) {
- return;
- } else {
- loc.monitor = pmon;
- loc.desktop = pmon->desk;
- }
- }
- bool focused = (n == mon->desk->focus);
- transfer_node(m, d, n, loc.monitor, loc.desktop, loc.desktop->focus);
- if (focused) {
- focus_node(loc.monitor, loc.desktop, n);
- }
- frozen_pointer->monitor = loc.monitor;
- frozen_pointer->desktop = loc.desktop;
- }
- } else {
- x = rect.x + delta_x;
- y = rect.y + delta_y;
- window_move(win, x, y);
- c->floating_rectangle.x = x;
- c->floating_rectangle.y = y;
- xcb_point_t pt = (xcb_point_t) {root_x, root_y};
- monitor_t *pmon = monitor_from_point(pt);
- if (pmon == NULL || pmon == m) {
- return;
- }
- bool focused = (n == mon->desk->focus);
- transfer_node(m, d, n, pmon, pmon->desk, pmon->desk->focus);
- if (focused) {
- focus_node(pmon, pmon->desk, n);
- }
- frozen_pointer->monitor = pmon;
- frozen_pointer->desktop = pmon->desk;
+ track_pointer(loc, pac, pos);
+}
+
+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;
+ int64_t dtime = e->time - last_motion_time;
+ if (dtime < 20) {
+ continue;
}
- break;
- case ACTION_RESIZE_SIDE:
- case ACTION_RESIZE_CORNER:
- if (frozen_pointer->is_tiled) {
- if (vertical_fence != NULL) {
- double sr = frozen_pointer->vertical_ratio + (double) delta_x / vertical_fence->rectangle.width;
- sr = MAX(0, sr);
- sr = MIN(1, sr);
- vertical_fence->split_ratio = sr;
- }
- if (horizontal_fence != NULL) {
- double sr = frozen_pointer->horizontal_ratio + (double) delta_y / horizontal_fence->rectangle.height;
- sr = MAX(0, sr);
- sr = MIN(1, sr);
- horizontal_fence->split_ratio = sr;
- }
- arrange(m, d);
+ 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 {
- if (pac == ACTION_RESIZE_SIDE) {
- switch (frozen_pointer->side) {
- case SIDE_TOP:
- x = rect.x;
- y = rect.y + delta_y;
- w = rect.width;
- h = rect.height - delta_y;
- break;
- case SIDE_RIGHT:
- x = rect.x;
- y = rect.y;
- w = rect.width + delta_x;
- h = rect.height;
- break;
- case SIDE_BOTTOM:
- x = rect.x;
- y = rect.y;
- w = rect.width;
- h = rect.height + delta_y;
- break;
- case SIDE_LEFT:
- x = rect.x + delta_x;
- y = rect.y;
- w = rect.width - delta_x;
- h = rect.height;
- break;
- }
- } else if (pac == ACTION_RESIZE_CORNER) {
- switch (frozen_pointer->corner) {
- case CORNER_TOP_LEFT:
- x = rect.x + delta_x;
- y = rect.y + delta_y;
- w = rect.width - delta_x;
- h = rect.height - delta_y;
- break;
- case CORNER_TOP_RIGHT:
- x = rect.x;
- y = rect.y + delta_y;
- w = rect.width + delta_x;
- h = rect.height - delta_y;
- break;
- case CORNER_BOTTOM_LEFT:
- x = rect.x + delta_x;
- y = rect.y;
- w = rect.width - delta_x;
- h = rect.height + delta_y;
- break;
- case CORNER_BOTTOM_RIGHT:
- x = rect.x;
- y = rect.y;
- w = rect.width + delta_x;
- h = rect.height + delta_y;
- break;
- }
- }
+ 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);
- int oldw = w, oldh = h;
- restrain_floating_size(c, &w, &h);
+ xcb_ungrab_pointer(dpy, XCB_CURRENT_TIME);
- if (c->state == STATE_FLOATING) {
- if (oldw == w) {
- c->floating_rectangle.x = x;
- c->floating_rectangle.width = w;
- }
- if (oldh == h) {
- c->floating_rectangle.y = y;
- c->floating_rectangle.height = h;
- }
- window_move_resize(win, c->floating_rectangle.x,
- c->floating_rectangle.y,
- c->floating_rectangle.width,
- c->floating_rectangle.height);
- } else {
- c->floating_rectangle.width = w;
- c->floating_rectangle.height = h;
- arrange(m, d);
- }
- }
- break;
- case ACTION_FOCUS:
- case ACTION_NONE:
- break;
+ if (grabbed_node == NULL) {
+ grabbing = false;
+ return;
}
-}
-void ungrab_pointer(void)
-{
- if (frozen_pointer->action != ACTION_NONE) {
- xcb_rectangle_t r = get_rectangle(frozen_pointer->desktop, frozen_pointer->node);
- put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", frozen_pointer->monitor->id, frozen_pointer->desktop->id, frozen_pointer->window, r.width, r.height, r.x, r.y);
- }
- frozen_pointer->action = ACTION_NONE;
+ 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);
}
#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 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);
void grab_pointer(pointer_action_t pac);
-void track_pointer(int root_x, int root_y);
+void track_pointer(coordinates_t loc, pointer_action_t pac, xcb_point_t pos);
void ungrab_pointer(void);
#endif
#include "history.h"
#include "parse.h"
#include "monitor.h"
+#include "window.h"
#include "tree.h"
#include "query.h"
fprintf(rsp, "\"lastLayer\":\"%s\",", LAYER_STR(c->last_layer));
fprintf(rsp, "\"urgent\":%s,", BOOL_STR(c->urgent));
fprintf(rsp, "\"visible\":%s,", BOOL_STR(c->visible));
- fprintf(rsp, "\"icccmFocus\":%s,", BOOL_STR(c->icccm_focus));
- fprintf(rsp, "\"icccmInput\":%s,", BOOL_STR(c->icccm_input));
- fprintf(rsp, "\"minWidth\":%u,", c->min_width);
- fprintf(rsp, "\"maxWidth\":%u,", c->max_width);
- fprintf(rsp, "\"minHeight\":%u,", c->min_height);
- fprintf(rsp, "\"maxHeight\":%u,", c->max_height);
- fprintf(rsp, "\"wmStatesCount\":%i,", c->wm_states_count);
- fprintf(rsp, "\"wmState\":");
- query_wm_state(c->wm_state, c->wm_states_count, rsp);
- fprintf(rsp,",");
fprintf(rsp, "\"tiledRectangle\":");
query_rectangle(c->tiled_rectangle, rsp);
fprintf(rsp,",");
fprintf(rsp, "{\"top\":%i,\"right\":%i,\"bottom\":%i,\"left\":%i}", p.top, p.right, p.bottom, p.left);
}
-void query_wm_state(xcb_atom_t *wm_state, int wm_states_count, FILE *rsp)
-{
- fprintf(rsp, "[");
- for (int i = 0; i < wm_states_count; i++) {
- fprintf(rsp, "%u", wm_state[i]);
- if (i < wm_states_count - 1) {
- fprintf(rsp, ",");
- }
- }
- fprintf(rsp, "]");
-}
-
void query_history(FILE *rsp)
{
fprintf(rsp, "[");
return count;
}
+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;
+ }
+}
+
node_select_t make_node_select(void)
{
node_select_t sel = {
dst->desktop = mon->desk;
dst->node = mon->desk->focus;
}
+ } 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 (*desc == '@') {
desc++;
if (colon != NULL) {
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_wm_state(xcb_atom_t *wm_state, int wm_states_count, FILE *rsp);
void query_history(FILE *rsp);
void query_coordinates(coordinates_t *loc, FILE *rsp);
void query_stack(FILE *rsp);
int query_node_ids_in(node_t *n, desktop_t *d, monitor_t *m, coordinates_t loc, node_select_t *sel, FILE *rsp);
int query_desktop_ids(coordinates_t loc, desktop_select_t *sel, FILE *rsp);
int query_monitor_ids(coordinates_t loc, monitor_select_t *sel, FILE *rsp);
+void print_modifier_mask(uint16_t m, FILE *rsp);
node_select_t make_node_select(void);
desktop_select_t make_desktop_select(void);
monitor_select_t make_monitor_select(void);
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);
}
RESTORE_UINT(borderWidth, &c->border_width)
RESTORE_BOOL(urgent, &c->urgent)
RESTORE_BOOL(visible, &c->visible)
- RESTORE_BOOL(icccmFocus, &c->icccm_focus)
- RESTORE_BOOL(icccmInput, &c->icccm_input)
- RESTORE_USINT(minWidth, &c->min_width)
- RESTORE_USINT(maxWidth, &c->max_width)
- RESTORE_USINT(minHeight, &c->min_height)
- RESTORE_USINT(maxHeight, &c->max_height)
- RESTORE_INT(wmStatesCount, &c->wm_states_count)
- } else if (keyeq("wmState", *t, json)) {
- (*t)++;
- restore_wm_state(c->wm_state, t, json);
- continue;
} else if (keyeq("tiledRectangle", *t, json)) {
(*t)++;
restore_rectangle(&c->tiled_rectangle, t, json);
}
}
-void restore_wm_state(xcb_atom_t *w, jsmntok_t **t, char *json)
-{
- int s = (*t)->size;
- (*t)++;
-
- for (int i = 0; i < s; i++) {
- sscanf(json + (*t)->start, "%u", &w[i]);
- (*t)++;
- }
-}
-
#undef RESTORE_INT
#undef RESTORE_UINT
#undef RESTORE_USINT
xcb_ewmh_get_atoms_reply_wipe(&win_state);
}
- 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.min_width > 0 && size_hints.min_height > 0 &&
- size_hints.min_width == size_hints.max_width &&
- size_hints.min_height == size_hints.max_height) {
- if (csq->state == NULL) {
- csq->state = malloc(sizeof(client_state_t));
- }
- *(csq->state) = STATE_FLOATING;
- }
- csq->min_width = size_hints.min_width;
- csq->max_width = size_hints.max_width;
- csq->min_height = size_hints.min_height;
- csq->max_height = size_hints.max_height;
- }
-
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) {
*(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 = malloc(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);
border_width = BORDER_WIDTH;
split_ratio = SPLIT_RATIO;
initial_polarity = FIRST_CHILD;
+ pointer_modifier = POINTER_MODIFIER;
borderless_monocle = BORDERLESS_MONOCLE;
gapless_monocle = GAPLESS_MONOCLE;
history_aware_focus = HISTORY_AWARE_FOCUS;
ignore_ewmh_focus = IGNORE_EWMH_FOCUS;
center_pseudo_tiled = CENTER_PSEUDO_TILED;
+ click_to_focus = CLICK_TO_FOCUS;
+ honor_size_hints = HONOR_SIZE_HINTS;
remove_disabled_monitors = REMOVE_DISABLED_MONITORS;
remove_unplugged_monitors = REMOVE_UNPLUGGED_MONITORS;
merge_overlapping_monitors = MERGE_OVERLAPPING_MONITORS;
#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 EXTERNAL_RULES_COMMAND ""
#define STATUS_PREFIX "W"
+
#define NORMAL_BORDER_COLOR "#30302f"
#define ACTIVE_BORDER_COLOR "#474645"
#define FOCUSED_BORDER_COLOR "#817f7f"
#define POINTER_FOLLOWS_MONITOR false
#define IGNORE_EWMH_FOCUS false
#define CENTER_PSEUDO_TILED true
+#define CLICK_TO_FOCUS false
+#define HONOR_SIZE_HINTS false
#define REMOVE_DISABLED_MONITORS false
#define REMOVE_UNPLUGGED_MONITORS false
#define MERGE_OVERLAPPING_MONITORS false
double split_ratio;
child_polarity_t initial_polarity;
+uint16_t pointer_modifier;
bool borderless_monocle;
bool gapless_monocle;
bool focus_by_distance;
bool ignore_ewmh_focus;
bool center_pseudo_tiled;
+bool click_to_focus;
+bool honor_size_hints;
bool remove_disabled_monitors;
bool remove_unplugged_monitors;
bool merge_overlapping_monitors;
#include <stdio.h>
#include <stdlib.h>
-#include <stdbool.h>
#include <ctype.h>
#include <stdarg.h>
#include "bspwm.h"
-#include "tree.h"
#include "desktop.h"
#include "settings.h"
#include "subscribe.h"
#include "geometry.h"
#include "subscribe.h"
#include "settings.h"
+#include "pointer.h"
#include "stack.h"
#include "window.h"
#include "tree.h"
n->rectangle = rect;
- if (pointer_follows_focus && mon->desk->focus == n && frozen_pointer->action == ACTION_NONE) {
+ if (pointer_follows_focus && mon->desk->focus == n) {
xcb_rectangle_t r = rect;
r.width -= d->window_gap;
r.height -= d->window_gap;
r = m->rectangle;
}
+ 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 (frozen_pointer->action == ACTION_NONE) {
+ 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);
}
}
client_t *make_client(void)
{
- client_t *c = malloc(sizeof(client_t));
+ 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 = c->visible = c->icccm_focus = false;
- c->icccm_input = true;
- c->wm_states_count = 0;
return c;
}
void initialize_client(node_t *n)
{
xcb_window_t win = n->id;
- if (win != XCB_NONE) {
- client_t *c = n->client;
- xcb_icccm_get_wm_protocols_reply_t protocols;
- if (xcb_icccm_get_wm_protocols_reply(dpy, xcb_icccm_get_wm_protocols(dpy, win, ewmh->WM_PROTOCOLS), &protocols, NULL) == 1) {
- if (has_proto(WM_TAKE_FOCUS, &protocols)) {
- c->icccm_focus = true;
- }
- xcb_icccm_get_wm_protocols_reply_wipe(&protocols);
- }
- 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++) {
- ewmh_wm_state_add(n, wm_state.atoms[i]);
+ 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) {
+ if (has_proto(WM_TAKE_FOCUS, &protos)) {
+ c->icccm_props.take_focus = 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; \
}
- 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_input = hints.input;
- }
- }
+ 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_leaf(node_t *n)
m->sticky_count -= sticky_count(n);
}
clients_count -= clients_count_in(n);
+ if (grabbed_node == n) {
+ grabbed_node = NULL;
+ }
free(n->client);
free(n);
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) {
set_vacant_state(m, d, n, value);
if (value) {
- ewmh_wm_state_add(n, ewmh->_NET_WM_STATE_FULLSCREEN);
+ c->wm_flags |= WM_FLAG_FULLSCREEN;
c->last_layer = c->layer;
c->layer = LAYER_ABOVE;
} else {
- ewmh_wm_state_remove(n, ewmh->_NET_WM_STATE_FULLSCREEN);
+ 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));
}
n->sticky = value;
if (value) {
- ewmh_wm_state_add(n, ewmh->_NET_WM_STATE_STICKY);
m->sticky_count++;
} else {
- ewmh_wm_state_remove(n, ewmh->_NET_WM_STATE_STICKY);
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) {
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);
}
#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"
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,
} direction_t;
typedef enum {
- CORNER_TOP_LEFT,
- CORNER_TOP_RIGHT,
- CORNER_BOTTOM_RIGHT,
- CORNER_BOTTOM_LEFT
-} corner_t;
-
-typedef enum {
- SIDE_LEFT,
- SIDE_TOP,
- SIDE_RIGHT,
- SIDE_BOTTOM
-} side_t;
+ 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,
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;
+};
+
typedef struct {
char class_name[3 * SMALEN / 2];
char instance_name[3 * SMALEN / 2];
stack_layer_t last_layer;
xcb_rectangle_t floating_rectangle;
xcb_rectangle_t tiled_rectangle;
- bool icccm_focus;
- bool icccm_input;
- uint16_t min_width;
- uint16_t max_width;
- uint16_t min_height;
- uint16_t max_height;
- xcb_atom_t wm_state[MAX_WM_STATES];
- int wm_states_count;
+ xcb_size_hints_t size_hints;
+ icccm_props_t icccm_props;
+ wm_flags_t wm_flags;
} client_t;
typedef struct presel_t presel_t;
double split_ratio;
stack_layer_t *layer;
client_state_t *state;
- uint16_t min_width;
- uint16_t max_width;
- uint16_t min_height;
- uint16_t max_height;
bool locked;
bool sticky;
bool private;
pending_rule_t *next;
};
-typedef struct {
- xcb_point_t position;
- pointer_action_t action;
- xcb_rectangle_t rectangle;
- node_t *vertical_fence;
- node_t *horizontal_fence;
- monitor_t *monitor;
- desktop_t *desktop;
- node_t *node;
- client_t *client;
- xcb_window_t window;
- bool is_tiled;
- double vertical_ratio;
- double horizontal_ratio;
- corner_t corner;
- side_t side;
-} pointer_state_t;
-
-typedef struct {
- node_t *fence;
- unsigned int distance;
-} fence_distance_t;
-
#endif
#include "rule.h"
#include "settings.h"
#include "geometry.h"
+#include "pointer.h"
#include "stack.h"
#include "tree.h"
#include "parse.h"
c->border_width = csq->border ? d->border_width : 0;
n->client = c;
initialize_client(n);
- update_floating_rectangle(n);
+ initialize_floating_rectangle(n);
if (c->floating_rectangle.x == 0 && c->floating_rectangle.y == 0) {
csq->center = true;
}
- c->min_width = csq->min_width;
- c->max_width = csq->max_width;
- c->min_height = csq->min_height;
- c->max_height = csq->max_height;
-
monitor_t *mm = monitor_from_client(c);
embrace_client(mm, c);
adapt_geometry(&mm->rectangle, &m->rectangle, n);
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);
- if (frozen_pointer->window == win) {
- frozen_pointer->action = ACTION_NONE;
- }
arrange(loc.monitor, loc.desktop);
} else {
for (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) {
xcb_change_window_attributes(dpy, win, XCB_CW_BORDER_PIXEL, &border_color_pxl);
}
-pointer_state_t *make_pointer_state(void)
-{
- pointer_state_t *p = malloc(sizeof(pointer_state_t));
- p->monitor = NULL;
- p->desktop = NULL;
- p->node = p->vertical_fence = p->horizontal_fence = NULL;
- p->client = NULL;
- p->window = XCB_NONE;
- p->action = ACTION_NONE;
- return p;
-}
-
void adopt_orphans(void)
{
xcb_query_tree_reply_t *qtr = xcb_query_tree_reply(dpy, xcb_query_tree(dpy, root), NULL);
}
}
-void update_floating_rectangle(node_t *n)
+void initialize_floating_rectangle(node_t *n)
{
client_t *c = n->client;
free(geo);
}
-void restrain_floating_width(client_t *c, int *width)
+bool move_client(coordinates_t *loc, int dx, int dy)
{
- if (*width < 1) {
- *width = 1;
+ 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;
+ window_move(n->id, x, y);
+ 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 (c->min_width > 0 && *width < c->min_width) {
- *width = c->min_width;
- } else if (c->max_width > 0 && *width > c->max_width) {
- *width = c->max_width;
+
+ 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;
}
-void restrain_floating_height(client_t *c, int *height)
+bool resize_client(coordinates_t *loc, resize_handle_t rh, int dx, int dy)
{
- if (*height < 1) {
- *height = 1;
+ node_t *n = loc->node;
+ if (n == NULL || n->client == NULL || n->client->state == STATE_FULLSCREEN) {
+ return false;
}
- if (c->min_height > 0 && *height < c->min_height) {
- *height = c->min_height;
- } else if (c->max_height > 0 && *height > c->max_height) {
- *height = c->max_height;
+ 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) {
+ 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) {
+ window_move_resize(n->id, x, y, width, height);
+ 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;
}
-void restrain_floating_size(client_t *c, int *width, int *height)
+/* taken from awesomeWM */
+void apply_size_hints(client_t *c, uint16_t *width, uint16_t *height)
{
- restrain_floating_width(c, width);
- restrain_floating_height(c, 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)
if (n == NULL || n->client == NULL) {
clear_input_focus();
} else {
- if (n->client->icccm_input) {
+ 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_focus) {
+ } else if (n->client->icccm_props.take_focus) {
send_client_message(n->id, ewmh->WM_PROTOCOLS, WM_TAKE_FOCUS);
}
}
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;
window_lower(motion_recorder);
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);
-pointer_state_t *make_pointer_state(void);
void adopt_orphans(void);
uint32_t get_border_color(bool focused_node, bool focused_monitor);
-void update_floating_rectangle(node_t *n);
-void restrain_floating_width(client_t *c, int *width);
-void restrain_floating_height(client_t *c, int *height);
-void restrain_floating_size(client_t *c, int *width, int *height);
+void initialize_floating_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);