diff --git a/README.md b/README.md index aa8560f..5564bf3 100644 --- a/README.md +++ b/README.md @@ -217,6 +217,8 @@ Colors are either [X color names](http://en.wikipedia.org/wiki/X11_color_names) - `gapless_monocle` — Whether to remove gaps for tiled windows in monocle mode. +- `focus_follows_pointer` — Wether to focus the window under the pointer. + - `adaptative_raise` — Prevent floating windows from being raised when they might cover other floating windows. - `apply_shadow_property` — Enable shadows for floating windows via the `_COMPTON_SHADOW` property. diff --git a/bspwm.1 b/bspwm.1 index 9f2c6ac..f3aa4a6 100644 --- a/bspwm.1 +++ b/bspwm.1 @@ -329,6 +329,9 @@ Whether to remove borders for tiled windows in monocle mode. .I gapless_monocle Whether to remove gaps for tiled windows in monocle mode. .TP +.I focus_follows_pointer +Wether to focus the window under the pointer. +.TP .I adaptative_raise Prevent floating windows from being raised when they might cover other floating windows. .TP diff --git a/bspwm.c b/bspwm.c index 83b1bd1..736ee25 100644 --- a/bspwm.c +++ b/bspwm.c @@ -62,6 +62,11 @@ void setup(void) screen_height = screen->height_in_pixels; root_depth = screen->root_depth; + uint32_t mask = XCB_CW_EVENT_MASK; + uint32_t values[] = {XCB_EVENT_MASK_POINTER_MOTION}; + motion_recorder = xcb_generate_id(dpy); + xcb_create_window(dpy, XCB_COPY_FROM_PARENT, motion_recorder, root, 0, 0, screen_width, screen_height, 0, XCB_WINDOW_CLASS_INPUT_ONLY, XCB_COPY_FROM_PARENT, mask, values); + xcb_atom_t net_atoms[] = {ewmh->_NET_SUPPORTED, ewmh->_NET_DESKTOP_NAMES, ewmh->_NET_NUMBER_OF_DESKTOPS, @@ -238,6 +243,7 @@ int main(int argc, char *argv[]) if (status_fifo != NULL) fclose(status_fifo); xcb_ewmh_connection_wipe(ewmh); + xcb_destroy_window(dpy, motion_recorder); free(ewmh); xcb_flush(dpy); xcb_disconnect(dpy); diff --git a/bspwm.h b/bspwm.h index a82c190..fd6c69c 100644 --- a/bspwm.h +++ b/bspwm.h @@ -5,6 +5,7 @@ #define ROOT_EVENT_MASK (XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY) #define CLIENT_EVENT_MASK (XCB_EVENT_MASK_PROPERTY_CHANGE) +#define CLIENT_EVENT_MASK_FFP (XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_ENTER_WINDOW) xcb_connection_t *dpy; int default_screen, screen_width, screen_height; @@ -30,6 +31,7 @@ rule_t *rule_head; rule_t *rule_tail; pointer_state_t *frozen_pointer; +xcb_window_t motion_recorder; xcb_atom_t compton_shadow; int exit_status; diff --git a/events.c b/events.c index 7244028..0c04ba3 100644 --- a/events.c +++ b/events.c @@ -35,6 +35,12 @@ void handle_event(xcb_generic_event_t *evt) case XCB_PROPERTY_NOTIFY: property_notify(evt); break; + case XCB_ENTER_NOTIFY: + enter_notify(evt); + break; + case XCB_MOTION_NOTIFY: + motion_notify(); + break; default: break; } @@ -220,6 +226,32 @@ void client_message(xcb_generic_event_t *evt) } } +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; + + PRINTF("enter notify %X %d %d\n", win, e->mode, e->detail); + + if (e->mode != XCB_NOTIFY_MODE_NORMAL + || (mon->desk->focus != NULL && mon->desk->focus->client->window == win)) + return; + + enable_motion_recorder(); +} + +void motion_notify(void) +{ + PUTS("motion notify"); + + disable_motion_recorder(); + + xcb_window_t win = XCB_NONE; + get_pointed_window(&win); + if (win != XCB_NONE) + window_focus(win); +} + 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) { diff --git a/events.h b/events.h index 83c6820..9bb3493 100644 --- a/events.h +++ b/events.h @@ -11,6 +11,8 @@ void unmap_notify(xcb_generic_event_t *); void configure_request(xcb_generic_event_t *); void client_message(xcb_generic_event_t *); void property_notify(xcb_generic_event_t *); +void enter_notify(xcb_generic_event_t *); +void motion_notify(void); void handle_state(monitor_t *, desktop_t *, node_t *, xcb_atom_t, unsigned int); void grab_pointer(pointer_action_t); void track_pointer(int, int); diff --git a/messages.c b/messages.c index 3d45489..afb4831 100644 --- a/messages.c +++ b/messages.c @@ -491,6 +491,20 @@ void set_setting(char *name, char *value, char *rsp) bool b; if (parse_bool(value, &b)) gapless_monocle = b; + } else if (strcmp(name, "focus_follows_pointer") == 0) { + bool b; + if (parse_bool(value, &b) && b != focus_follows_pointer) { + uint32_t values[] = {(focus_follows_pointer ? CLIENT_EVENT_MASK : CLIENT_EVENT_MASK_FFP)}; + 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)) + xcb_change_window_attributes(dpy, n->client->window, XCB_CW_EVENT_MASK, values); + if (focus_follows_pointer) + disable_motion_recorder(); + else + enable_motion_recorder(); + focus_follows_pointer = b; + } } else if (strcmp(name, "adaptative_raise") == 0) { bool b; if (parse_bool(value, &b)) @@ -548,6 +562,8 @@ void get_setting(char *name, char* rsp) snprintf(rsp, BUFSIZ, "%s", BOOLSTR(borderless_monocle)); else if (strcmp(name, "gapless_monocle") == 0) snprintf(rsp, BUFSIZ, "%s", BOOLSTR(gapless_monocle)); + else if (strcmp(name, "focus_follows_pointer") == 0) + snprintf(rsp, BUFSIZ, "%s", BOOLSTR(focus_follows_pointer)); else if (strcmp(name, "adaptative_raise") == 0) snprintf(rsp, BUFSIZ, "%s", BOOLSTR(adaptative_raise)); else if (strcmp(name, "apply_shadow_property") == 0) diff --git a/settings.c b/settings.c index 2a39f7e..d6dcdec 100644 --- a/settings.c +++ b/settings.c @@ -61,6 +61,7 @@ void load_settings(void) borderless_monocle = BORDERLESS_MONOCLE; gapless_monocle = GAPLESS_MONOCLE; + focus_follows_pointer = FOCUS_FOLLOWS_POINTER; adaptative_raise = ADAPTATIVE_RAISE; apply_shadow_property = APPLY_SHADOW_PROPERTY; } diff --git a/settings.h b/settings.h index 6423f3e..4ed1ec0 100644 --- a/settings.h +++ b/settings.h @@ -22,6 +22,7 @@ #define BORDERLESS_MONOCLE false #define GAPLESS_MONOCLE false +#define FOCUS_FOLLOWS_POINTER false #define ADAPTATIVE_RAISE false #define APPLY_SHADOW_PROPERTY false @@ -48,6 +49,7 @@ int window_gap; bool borderless_monocle; bool gapless_monocle; +bool focus_follows_pointer; bool adaptative_raise; bool apply_shadow_property; diff --git a/tree.c b/tree.c index 8b0b705..434c551 100644 --- a/tree.c +++ b/tree.c @@ -386,6 +386,15 @@ void focus_node(monitor_t *m, desktop_t *d, node_t *n, bool is_mapped) xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, n->client->window, XCB_CURRENT_TIME); } + if (focus_follows_pointer) { + xcb_window_t win = XCB_NONE; + get_pointed_window(&win); + if (win != n->client->window) + enable_motion_recorder(); + else + disable_motion_recorder(); + } + if (!is_tiled(n->client)) { if (!adaptative_raise || !might_cover(d, n)) window_raise(n->client->window); @@ -940,7 +949,7 @@ void restore(char *file_path) 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)) { - uint32_t values[] = {CLIENT_EVENT_MASK}; + uint32_t values[] = {(focus_follows_pointer ? CLIENT_EVENT_MASK_FFP : CLIENT_EVENT_MASK)}; xcb_change_window_attributes(dpy, n->client->window, XCB_CW_EVENT_MASK, values); if (n->client->floating) { n->vacant = true; diff --git a/window.c b/window.c index c52029f..13c3019 100644 --- a/window.c +++ b/window.c @@ -154,7 +154,7 @@ void manage_window(monitor_t *m, desktop_t *d, xcb_window_t win) if (takes_focus) xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_POINTER_ROOT, win, XCB_CURRENT_TIME); - uint32_t values[] = {CLIENT_EVENT_MASK}; + uint32_t values[] = {(focus_follows_pointer ? CLIENT_EVENT_MASK_FFP : CLIENT_EVENT_MASK)}; xcb_change_window_attributes(dpy, c->window, XCB_CW_EVENT_MASK, values); num_clients++; @@ -411,6 +411,27 @@ void update_floating_rectangle(client_t *c) } } + +void save_pointer_position(xcb_point_t *pos) +{ + xcb_query_pointer_reply_t *qpr = xcb_query_pointer_reply(dpy, xcb_query_pointer(dpy, root), NULL); + if (qpr != NULL) { + *pos = (xcb_point_t) {qpr->root_x, qpr->root_y}; + free(qpr); + } +} + +void get_pointed_window(xcb_window_t *win) +{ + window_lower(motion_recorder); + xcb_query_pointer_reply_t *qpr = xcb_query_pointer_reply(dpy, xcb_query_pointer(dpy, root), NULL); + if (qpr != NULL) { + *win = qpr->child; + free(qpr); + } + window_raise(motion_recorder); +} + void window_focus(xcb_window_t win) { window_location_t loc; @@ -490,3 +511,14 @@ void toggle_visibility(void) if (visible) update_current(); } + +void enable_motion_recorder(void) +{ + window_raise(motion_recorder); + window_show(motion_recorder); +} + +void disable_motion_recorder(void) +{ + window_hide(motion_recorder); +} diff --git a/window.h b/window.h index da1f38e..cb72b12 100644 --- a/window.h +++ b/window.h @@ -34,9 +34,12 @@ void window_lower(xcb_window_t); void window_set_visibility(xcb_window_t, bool); void window_hide(xcb_window_t); void window_show(xcb_window_t); +void enable_motion_recorder(void); +void disable_motion_recorder(void); void toggle_visibility(void); uint32_t get_border_color(client_t *, bool, bool); void update_floating_rectangle(client_t *); +void get_pointed_window(xcb_window_t *); void list_windows(char *); #endif