X-Git-Url: https://code.delx.au/spectrwm/blobdiff_plain/d5362eb9f74aafae8330ae3806f450fdd6ffe17b..cea3baef918b0ddbdd58f91d69fc9b065373894e:/spectrwm.c diff --git a/spectrwm.c b/spectrwm.c index 2cc0d24..d82c0eb 100644 --- a/spectrwm.c +++ b/spectrwm.c @@ -61,7 +61,11 @@ #include #include #include +#ifdef __OSX__ +#include "queue.h" +#else #include +#endif #include #include #if defined(__linux__) @@ -235,7 +239,11 @@ uint32_t swm_debug = 0 #define LENGTH(x) (int)(sizeof (x) / sizeof (x)[0]) #define MODKEY XCB_MOD_MASK_1 -#define CLEANMASK(mask) ((mask) & ~(numlockmask | XCB_MOD_MASK_LOCK)) +#define ANYMOD XCB_MOD_MASK_ANY +#define CLEANMASK(mask) ((mask) & (XCB_KEY_BUT_MASK_SHIFT | \ + XCB_KEY_BUT_MASK_CONTROL | XCB_KEY_BUT_MASK_MOD_1 | \ + XCB_KEY_BUT_MASK_MOD_2 | XCB_KEY_BUT_MASK_MOD_3 | \ + XCB_KEY_BUT_MASK_MOD_4 | XCB_KEY_BUT_MASK_MOD_5)) #define BUTTONMASK (XCB_EVENT_MASK_BUTTON_PRESS | \ XCB_EVENT_MASK_BUTTON_RELEASE) #define MOUSEMASK (BUTTONMASK|XCB_EVENT_MASK_POINTER_MOTION) @@ -332,7 +340,7 @@ bool cycle_empty = false; bool cycle_visible = false; int term_width = 0; int font_adjusted = 0; -unsigned int mod_key = MODKEY; +uint16_t mod_key = MODKEY; bool warp_focus = false; bool warp_pointer = false; bool workspace_clamp = false; @@ -401,7 +409,7 @@ bool bar_at_bottom = false; bool bar_extra = false; int bar_height = 0; int bar_justify = SWM_BAR_JUSTIFY_LEFT; -char *bar_format = NULL; +char *bar_format = NULL; bool stack_enabled = true; bool clock_enabled = true; bool iconic_enabled = false; @@ -427,15 +435,14 @@ bool verbose_layout = false; time_t time_started; #endif pid_t bar_pid; -XFontSet bar_fs; +XFontSet bar_fs = NULL; XFontSetExtents *bar_fs_extents; -XftFont *bar_font; +XftFont *bar_font = NULL; bool bar_font_legacy = true; -char *bar_fonts; +char *bar_fonts = NULL; XftColor bar_font_color; XftColor search_font_color; -struct passwd *pwd; -char *startup_exception; +char *startup_exception = NULL; unsigned int nr_exceptions = 0; /* layout manager data */ @@ -674,19 +681,19 @@ struct quirk { regex_t regex_name; uint32_t quirk; int ws; /* Initial workspace. */ -#define SWM_Q_FLOAT (1<<0) /* float this window */ -#define SWM_Q_TRANSSZ (1<<1) /* transiend window size too small */ -#define SWM_Q_ANYWHERE (1<<2) /* don't position this window */ -#define SWM_Q_XTERM_FONTADJ (1<<3) /* adjust xterm fonts when resizing */ -#define SWM_Q_FULLSCREEN (1<<4) /* remove border when fullscreen */ -#define SWM_Q_FOCUSPREV (1<<5) /* focus on caller */ +#define SWM_Q_FLOAT (1<<0) /* Float this window. */ +#define SWM_Q_TRANSSZ (1<<1) /* Transient window size too small. */ +#define SWM_Q_ANYWHERE (1<<2) /* Don't position this window */ +#define SWM_Q_XTERM_FONTADJ (1<<3) /* Adjust xterm fonts when resizing. */ +#define SWM_Q_FULLSCREEN (1<<4) /* Remove border when fullscreen. */ +#define SWM_Q_FOCUSPREV (1<<5) /* Focus on caller. */ #define SWM_Q_NOFOCUSONMAP (1<<6) /* Don't focus on window when mapped. */ #define SWM_Q_FOCUSONMAP_SINGLE (1<<7) /* Only focus if single win of type. */ #define SWM_Q_OBEYAPPFOCUSREQ (1<<8) /* Focus when applications ask. */ #define SWM_Q_IGNOREPID (1<<9) /* Ignore PID when determining ws. */ #define SWM_Q_IGNORESPAWNWS (1<<10) /* Ignore _SWM_WS when managing win. */ #define SWM_Q_NOFOCUSCYCLE (1<<11) /* Remove from normal focus cycle. */ -#define SWM_Q_MINIMALBORDER (1<<12) /* Remove border when floating/unfocused */ +#define SWM_Q_MINIMALBORDER (1<<12) /* No border when floating/unfocused. */ }; TAILQ_HEAD(quirk_list, quirk); struct quirk_list quirks = TAILQ_HEAD_INITIALIZER(quirks); @@ -822,137 +829,156 @@ struct spawn_prog { TAILQ_HEAD(spawn_list, spawn_prog); struct spawn_list spawns = TAILQ_HEAD_INITIALIZER(spawns); -/* user/key callable function IDs */ -enum keyfuncid { - KF_BAR_TOGGLE, - KF_BAR_TOGGLE_WS, - KF_BUTTON2, - KF_CYCLE_LAYOUT, - KF_FLIP_LAYOUT, - KF_FLOAT_TOGGLE, - KF_FOCUS_MAIN, - KF_FOCUS_NEXT, - KF_FOCUS_PREV, - KF_FOCUS_URGENT, - KF_MAXIMIZE_TOGGLE, - KF_HEIGHT_GROW, - KF_HEIGHT_SHRINK, - KF_ICONIFY, - KF_MASTER_SHRINK, - KF_MASTER_GROW, - KF_MASTER_ADD, - KF_MASTER_DEL, - KF_MOVE_DOWN, - KF_MOVE_LEFT, - KF_MOVE_RIGHT, - KF_MOVE_UP, - KF_MVRG_1, - KF_MVRG_2, - KF_MVRG_3, - KF_MVRG_4, - KF_MVRG_5, - KF_MVRG_6, - KF_MVRG_7, - KF_MVRG_8, - KF_MVRG_9, - KF_MVWS_1, - KF_MVWS_2, - KF_MVWS_3, - KF_MVWS_4, - KF_MVWS_5, - KF_MVWS_6, - KF_MVWS_7, - KF_MVWS_8, - KF_MVWS_9, - KF_MVWS_10, - KF_MVWS_11, - KF_MVWS_12, - KF_MVWS_13, - KF_MVWS_14, - KF_MVWS_15, - KF_MVWS_16, - KF_MVWS_17, - KF_MVWS_18, - KF_MVWS_19, - KF_MVWS_20, - KF_MVWS_21, - KF_MVWS_22, - KF_NAME_WORKSPACE, - KF_QUIT, - KF_RAISE_TOGGLE, - KF_RESTART, - KF_RG_1, - KF_RG_2, - KF_RG_3, - KF_RG_4, - KF_RG_5, - KF_RG_6, - KF_RG_7, - KF_RG_8, - KF_RG_9, - KF_RG_MOVE_NEXT, - KF_RG_MOVE_PREV, - KF_RG_NEXT, - KF_RG_PREV, - KF_SCREEN_NEXT, - KF_SCREEN_PREV, - KF_SEARCH_WIN, - KF_SEARCH_WORKSPACE, - KF_SPAWN_CUSTOM, - KF_STACK_BALANCE, - KF_STACK_INC, - KF_STACK_DEC, - KF_STACK_RESET, - KF_SWAP_MAIN, - KF_SWAP_NEXT, - KF_SWAP_PREV, - KF_UNICONIFY, - KF_VERSION, - KF_WIDTH_GROW, - KF_WIDTH_SHRINK, - KF_WIND_DEL, - KF_WIND_KILL, - KF_WS_1, - KF_WS_2, - KF_WS_3, - KF_WS_4, - KF_WS_5, - KF_WS_6, - KF_WS_7, - KF_WS_8, - KF_WS_9, - KF_WS_10, - KF_WS_11, - KF_WS_12, - KF_WS_13, - KF_WS_14, - KF_WS_15, - KF_WS_16, - KF_WS_17, - KF_WS_18, - KF_WS_19, - KF_WS_20, - KF_WS_21, - KF_WS_22, - KF_WS_NEXT, - KF_WS_NEXT_ALL, - KF_WS_NEXT_MOVE, - KF_WS_PREV, - KF_WS_PREV_ALL, - KF_WS_PREV_MOVE, - KF_WS_PRIOR, - KF_DUMPWINS, /* MUST BE LAST */ - KF_INVALID +enum { + FN_F_NOREPLAY = 0x1, }; -struct key { - RB_ENTRY(key) entry; - unsigned int mod; - KeySym keysym; - enum keyfuncid funcid; - char *spawn_name; +/* User callable function IDs. */ +enum actionid { + FN_BAR_TOGGLE, + FN_BAR_TOGGLE_WS, + FN_BUTTON2, + FN_CYCLE_LAYOUT, + FN_FLIP_LAYOUT, + FN_FLOAT_TOGGLE, + FN_FOCUS, + FN_FOCUS_MAIN, + FN_FOCUS_NEXT, + FN_FOCUS_PREV, + FN_FOCUS_URGENT, + FN_MAXIMIZE_TOGGLE, + FN_HEIGHT_GROW, + FN_HEIGHT_SHRINK, + FN_ICONIFY, + FN_MASTER_SHRINK, + FN_MASTER_GROW, + FN_MASTER_ADD, + FN_MASTER_DEL, + FN_MOVE, + FN_MOVE_DOWN, + FN_MOVE_LEFT, + FN_MOVE_RIGHT, + FN_MOVE_UP, + FN_MVRG_1, + FN_MVRG_2, + FN_MVRG_3, + FN_MVRG_4, + FN_MVRG_5, + FN_MVRG_6, + FN_MVRG_7, + FN_MVRG_8, + FN_MVRG_9, + FN_MVWS_1, + FN_MVWS_2, + FN_MVWS_3, + FN_MVWS_4, + FN_MVWS_5, + FN_MVWS_6, + FN_MVWS_7, + FN_MVWS_8, + FN_MVWS_9, + FN_MVWS_10, + FN_MVWS_11, + FN_MVWS_12, + FN_MVWS_13, + FN_MVWS_14, + FN_MVWS_15, + FN_MVWS_16, + FN_MVWS_17, + FN_MVWS_18, + FN_MVWS_19, + FN_MVWS_20, + FN_MVWS_21, + FN_MVWS_22, + FN_NAME_WORKSPACE, + FN_QUIT, + FN_RAISE_TOGGLE, + FN_RESIZE, + FN_RESIZE_CENTERED, + FN_RESTART, + FN_RG_1, + FN_RG_2, + FN_RG_3, + FN_RG_4, + FN_RG_5, + FN_RG_6, + FN_RG_7, + FN_RG_8, + FN_RG_9, + FN_RG_MOVE_NEXT, + FN_RG_MOVE_PREV, + FN_RG_NEXT, + FN_RG_PREV, + FN_SCREEN_NEXT, + FN_SCREEN_PREV, + FN_SEARCH_WIN, + FN_SEARCH_WORKSPACE, + FN_SPAWN_CUSTOM, + FN_STACK_BALANCE, + FN_STACK_INC, + FN_STACK_DEC, + FN_STACK_RESET, + FN_SWAP_MAIN, + FN_SWAP_NEXT, + FN_SWAP_PREV, + FN_UNICONIFY, + FN_VERSION, + FN_WIDTH_GROW, + FN_WIDTH_SHRINK, + FN_WIND_DEL, + FN_WIND_KILL, + FN_WS_1, + FN_WS_2, + FN_WS_3, + FN_WS_4, + FN_WS_5, + FN_WS_6, + FN_WS_7, + FN_WS_8, + FN_WS_9, + FN_WS_10, + FN_WS_11, + FN_WS_12, + FN_WS_13, + FN_WS_14, + FN_WS_15, + FN_WS_16, + FN_WS_17, + FN_WS_18, + FN_WS_19, + FN_WS_20, + FN_WS_21, + FN_WS_22, + FN_WS_NEXT, + FN_WS_NEXT_ALL, + FN_WS_NEXT_MOVE, + FN_WS_PREV, + FN_WS_PREV_ALL, + FN_WS_PREV_MOVE, + FN_WS_PRIOR, + FN_DUMPWINS, /* MUST BE LAST */ + FN_INVALID }; -RB_HEAD(key_tree, key); + +enum binding_type { + KEYBIND, + BTNBIND +}; + +enum { + BINDING_F_REPLAY = 0x1, +}; + +struct binding { + RB_ENTRY(binding) entry; + uint16_t mod; /* Modifier Mask. */ + enum binding_type type; /* Key or Button. */ + uint32_t value; /* KeySym or Button Index. */ + enum actionid action; /* Action Identifier. */ + uint32_t flags; + char *spawn_name; +}; +RB_HEAD(binding_tree, binding); /* function prototypes */ void adjust_font(struct ws_win *); @@ -970,7 +996,7 @@ void bar_replace(char *, char *, struct swm_region *, size_t); void bar_replace_pad(char *, int *, size_t); char *bar_replace_seq(char *, char *, struct swm_region *, size_t *, size_t); void bar_setup(struct swm_region *); -void bar_toggle(struct swm_region *, union arg *); +void bar_toggle(struct binding *, struct swm_region *, union arg *); void bar_urgent(char *, size_t); void bar_window_class(char *, size_t, struct swm_region *); void bar_window_class_instance(char *, size_t, struct swm_region *); @@ -979,10 +1005,17 @@ void bar_window_instance(char *, size_t, struct swm_region *); void bar_window_name(char *, size_t, struct swm_region *); void bar_window_state(char *, size_t, struct swm_region *); void bar_workspace_name(char *, size_t, struct swm_region *); +int binding_cmp(struct binding *, struct binding *); +void binding_insert(uint16_t, enum binding_type, uint32_t, enum actionid, + uint32_t, const char *); +struct binding *binding_lookup(uint16_t, enum binding_type, uint32_t); +void binding_remove(struct binding *); void buttonpress(xcb_button_press_event_t *); +void buttonrelease(xcb_button_release_event_t *); void center_pointer(struct swm_region *); void check_conn(void); -void clear_keys(void); +void clear_bindings(void); +void clear_keybindings(void); int clear_maximized(struct workspace *); void clear_quirks(void); void clear_spawns(void); @@ -997,11 +1030,11 @@ int count_win(struct workspace *, bool); void cursors_cleanup(void); void cursors_load(void); void custom_region(const char *); -void cyclerg(struct swm_region *, union arg *); -void cyclews(struct swm_region *, union arg *); -void cycle_layout(struct swm_region *, union arg *); +void cycle_layout(struct binding *, struct swm_region *, union arg *); +void cyclerg(struct binding *, struct swm_region *, union arg *); +void cyclews(struct binding *, struct swm_region *, union arg *); void destroynotify(xcb_destroy_notify_event_t *); -void dumpwins(struct swm_region *, union arg *); +void dumpwins(struct binding *, struct swm_region *, union arg *); int enable_wm(void); void enternotify(xcb_enter_notify_event_t *); void event_drain(uint8_t); @@ -1024,16 +1057,17 @@ void fake_keypress(struct ws_win *, xcb_keysym_t, uint16_t); struct pid_e *find_pid(pid_t); struct ws_win *find_unmanaged_window(xcb_window_t); struct ws_win *find_window(xcb_window_t); -void floating_toggle(struct swm_region *, union arg *); -void focus(struct swm_region *, union arg *); +void floating_toggle(struct binding *, struct swm_region *, union arg *); +void focus(struct binding *, struct swm_region *, union arg *); +void focus_flush(void); +void focus_pointer(struct binding *, struct swm_region *, union arg *); +void focus_region(struct swm_region *); +void focus_win(struct ws_win *); #ifdef SWM_DEBUG void focusin(xcb_focus_in_event_t *); void focusout(xcb_focus_out_event_t *); #endif -void focus_flush(void); -void focus_region(struct swm_region *); -void focusrg(struct swm_region *, union arg *); -void focus_win(struct ws_win *); +void focusrg(struct binding *, struct swm_region *, union arg *); void fontset_init(void); void free_window(struct ws_win *); xcb_atom_t get_atom_from_string(const char *); @@ -1042,6 +1076,7 @@ char *get_atom_name(xcb_atom_t); #endif struct ws_win *get_focus_magic(struct ws_win *); struct ws_win *get_focus_prev(struct ws_win *); +xcb_generic_event_t *get_next_event(bool); #ifdef SWM_DEBUG char *get_notify_detail_label(uint8_t); char *get_notify_mode_label(uint8_t); @@ -1054,6 +1089,7 @@ int get_screen_count(void); #ifdef SWM_DEBUG char *get_source_type_label(uint32_t); char *get_stack_mode_name(uint8_t); +char *get_state_mask_label(uint16_t); #endif int32_t get_swm_ws(xcb_window_t); bool get_urgent(struct ws_win *); @@ -1061,18 +1097,15 @@ char *get_win_name(xcb_window_t); uint8_t get_win_state(xcb_window_t); void get_wm_protocols(struct ws_win *); int get_ws_idx(struct ws_win *); -void grabbuttons(struct ws_win *); -void grabkeys(void); void grab_windows(void); -void iconify(struct swm_region *, union arg *); +void grabbuttons(void); +void grabkeys(void); +void iconify(struct binding *, struct swm_region *, union arg *); bool isxlfd(char *); +bool keybindreleased(struct binding *, xcb_key_release_event_t *); void keypress(xcb_key_press_event_t *); -int key_cmp(struct key *, struct key *); -void key_insert(unsigned int, KeySym, enum keyfuncid, const char *); -struct key *key_lookup(unsigned int, KeySym); -void key_remove(struct key *); -void key_replace(struct key *, unsigned int, KeySym, enum keyfuncid, - const char *); +void keyrelease(xcb_key_release_event_t *); +bool keyrepeating(xcb_key_release_event_t *); void kill_bar_extra_atexit(void); void kill_refs(struct ws_win *); #ifdef SWM_DEBUG @@ -1085,36 +1118,39 @@ void map_window(struct ws_win *); void mapnotify(xcb_map_notify_event_t *); void mappingnotify(xcb_mapping_notify_event_t *); void maprequest(xcb_map_request_event_t *); -void maximize_toggle(struct swm_region *, union arg *); +void maximize_toggle(struct binding *, struct swm_region *, union arg *); void motionnotify(xcb_motion_notify_event_t *); -void move(struct ws_win *, union arg *); -void move_step(struct swm_region *, union arg *); +void move(struct binding *, struct swm_region *, union arg *); +void move_win(struct ws_win *, struct binding *, int); uint32_t name_to_pixel(int, const char *); -void name_workspace(struct swm_region *, union arg *); +void name_workspace(struct binding *, struct swm_region *, union arg *); void new_region(struct swm_screen *, int, int, int, int); -int parsekeys(const char *, unsigned int, unsigned int *, KeySym *); -int parsequirks(const char *, uint32_t *, int *); int parse_rgb(const char *, uint16_t *, uint16_t *, uint16_t *); -void pressbutton(struct swm_region *, union arg *); -void priorws(struct swm_region *, union arg *); +int parsebinding(const char *, uint16_t *, enum binding_type *, uint32_t *, + uint32_t *); +int parsequirks(const char *, uint32_t *, int *); +void pressbutton(struct binding *, struct swm_region *, union arg *); +void priorws(struct binding *, struct swm_region *, union arg *); #ifdef SWM_DEBUG void print_win_geom(xcb_window_t); +void print_win_input_model(struct ws_win *); #endif void propertynotify(xcb_property_notify_event_t *); +void put_back_event(xcb_generic_event_t *); void quirk_free(struct quirk *); void quirk_insert(const char *, const char *, const char *, uint32_t, int); void quirk_remove(struct quirk *); void quirk_replace(struct quirk *, const char *, const char *, const char *, uint32_t, int); -void quit(struct swm_region *, union arg *); -void raise_toggle(struct swm_region *, union arg *); +void quit(struct binding *, struct swm_region *, union arg *); +void raise_toggle(struct binding *, struct swm_region *, union arg *); void raise_window(struct ws_win *); void region_containment(struct ws_win *, struct swm_region *, int); struct swm_region *region_under(struct swm_screen *, int, int); void regionize(struct ws_win *, int, int); -void resize(struct ws_win *, union arg *); -void resize_step(struct swm_region *, union arg *); -void restart(struct swm_region *, union arg *); +void resize(struct binding *, struct swm_region *, union arg *); +void resize_win(struct ws_win *, struct binding *, int); +void restart(struct binding *, struct swm_region *, union arg *); struct swm_region *root_to_region(xcb_window_t, int); void screenchange(xcb_randr_screen_change_notify_event_t *); void scan_randr(int); @@ -1123,13 +1159,15 @@ void search_resp_name_workspace(const char *, size_t); void search_resp_search_window(const char *); void search_resp_search_workspace(const char *); void search_resp_uniconify(const char *, size_t); -void search_win(struct swm_region *, union arg *); +void search_win(struct binding *, struct swm_region *, union arg *); void search_win_cleanup(void); -void search_workspace(struct swm_region *, union arg *); -void send_to_rg(struct swm_region *, union arg *); -void send_to_ws(struct swm_region *, union arg *); +void search_workspace(struct binding *, struct swm_region *, union arg *); +void send_to_rg(struct binding *, struct swm_region *, union arg *); +void send_to_ws(struct binding *, struct swm_region *, union arg *); void set_region(struct swm_region *); int setautorun(const char *, const char *, int); +void setbinding(uint16_t, enum binding_type, uint32_t, enum actionid, + uint32_t, const char *); int setconfbinding(const char *, const char *, int); int setconfcolor(const char *, const char *, int); int setconfmodkey(const char *, const char *, int); @@ -1137,15 +1175,15 @@ int setconfquirk(const char *, const char *, int); int setconfregion(const char *, const char *, int); int setconfspawn(const char *, const char *, int); int setconfvalue(const char *, const char *, int); -void setkeybinding(unsigned int, KeySym, enum keyfuncid, const char *); int setkeymapping(const char *, const char *, int); int setlayout(const char *, const char *, int); void setquirk(const char *, const char *, const char *, uint32_t, int); void setscreencolor(const char *, int, int); void setspawn(const char *, const char *, int); +void setup_btnbindings(void); void setup_ewmh(void); void setup_globals(void); -void setup_keys(void); +void setup_keybindings(void); void setup_quirks(void); void setup_screens(void); void setup_spawn(void); @@ -1163,42 +1201,42 @@ struct spawn_prog *spawn_find(const char *); void spawn_remove(struct spawn_prog *); void spawn_replace(struct spawn_prog *, const char *, const char *, int); void spawn_select(struct swm_region *, union arg *, const char *, int *); -void stack_config(struct swm_region *, union arg *); +void stack_config(struct binding *, struct swm_region *, union arg *); void stack_master(struct workspace *, struct swm_geometry *, int, bool); void store_float_geom(struct ws_win *); char *strdupsafe(const char *); -void swapwin(struct swm_region *, union arg *); -void switchws(struct swm_region *, union arg *); +void swapwin(struct binding *, struct swm_region *, union arg *); +void switchws(struct binding *, struct swm_region *, union arg *); void teardown_ewmh(void); void unescape_selector(char *); void unfocus_win(struct ws_win *); -void uniconify(struct swm_region *, union arg *); +void uniconify(struct binding *, struct swm_region *, union arg *); void unmanage_window(struct ws_win *); -void unmapnotify(xcb_unmap_notify_event_t *); void unmap_all(void); void unmap_window(struct ws_win *); -void updatenumlockmask(void); +void unmapnotify(xcb_unmap_notify_event_t *); void update_floater(struct ws_win *); -void update_modkey(unsigned int); +void update_modkey(uint16_t); void update_win_stacking(struct ws_win *); void update_window(struct ws_win *); void update_window_color(struct ws_win *); void update_wm_state(struct ws_win *win); +void updatenumlockmask(void); void validate_spawns(void); int validate_win(struct ws_win *); int validate_ws(struct workspace *); -void version(struct swm_region *, union arg *); +void version(struct binding *, struct swm_region *, union arg *); void win_to_ws(struct ws_win *, int, bool); pid_t window_get_pid(xcb_window_t); -void wkill(struct swm_region *, union arg *); +void wkill(struct binding *, struct swm_region *, union arg *); void update_ws_stack(struct workspace *); void xft_init(struct swm_region *); void _add_startup_exception(const char *, va_list); void add_startup_exception(const char *, ...); -RB_PROTOTYPE(key_tree, key, entry, key_cmp); -RB_GENERATE(key_tree, key, entry, key_cmp); -struct key_tree keys; +RB_PROTOTYPE(binding_tree, binding, entry, binding_cmp); +RB_GENERATE(binding_tree, binding, entry, binding_cmp); +struct binding_tree bindings; void cursors_load(void) @@ -1747,7 +1785,7 @@ ewmh_get_wm_state(struct ws_win *win) /* events */ #ifdef SWM_DEBUG void -dumpwins(struct swm_region *r, union arg *args) +dumpwins(struct binding *bp, struct swm_region *r, union arg *args) { struct ws_win *w; uint32_t state; @@ -1755,6 +1793,7 @@ dumpwins(struct swm_region *r, union arg *args) xcb_get_window_attributes_reply_t *wa; /* suppress unused warning since var is needed */ + (void)bp; (void)args; if (r->ws == NULL) { @@ -1803,8 +1842,9 @@ dumpwins(struct swm_region *r, union arg *args) } #else void -dumpwins(struct swm_region *r, union arg *s) +dumpwins(struct binding *b, struct swm_region *r, union arg *s) { + (void)b; (void)r; (void)s; } @@ -1969,6 +2009,8 @@ custom_region(const char *val) int sidx, num_screens; xcb_screen_t *screen; + DNPRINTF(SWM_D_CONF, "custom_region: %s\n", val); + num_screens = get_screen_count(); if (sscanf(val, "screen[%d]:%ux%u+%u+%u", &sidx, &w, &h, &x, &y) != 5) errx(1, "invalid custom region, " @@ -2281,7 +2323,7 @@ bar_fmt(const char *fmtexp, char *fmtnew, struct swm_region *r, size_t sz) /* bar_urgent already adds the space before the last asterisk */ if (urgent_enabled) - strlcat(fmtnew, "* +U*+4<", sz); + strlcat(fmtnew, (urgent_collapse ? "*+U*+4<" : "* +U*+4<"), sz); if (window_class_enabled) { strlcat(fmtnew, "+C", sz); @@ -2559,12 +2601,13 @@ bar_extra_update(void) } void -bar_toggle(struct swm_region *r, union arg *args) +bar_toggle(struct binding *bp, struct swm_region *r, union arg *args) { struct swm_region *tmpr; int i, num_screens; /* suppress unused warnings since vars are needed */ + (void)bp; (void)r; (void)args; @@ -2710,13 +2753,13 @@ fontset_init(void) void xft_init(struct swm_region *r) { - char *font, *d, *search; + char *font, *str, *search; XRenderColor color; if (bar_font == NULL) { - if ((d = strdup(bar_fonts)) == NULL) + if ((search = str = strdup(bar_fonts)) == NULL) errx(1, "insufficient memory."); - search = d; + while ((font = strsep(&search, ",")) != NULL) { if (*font == '\0') continue; @@ -2740,7 +2783,7 @@ xft_init(struct swm_region *r) break; } } - free(d); + free(str); } if (bar_font == NULL) @@ -2847,7 +2890,7 @@ set_win_state(struct ws_win *win, uint8_t state) uint16_t data[2] = { state, XCB_ATOM_NONE }; DNPRINTF(SWM_D_EVENT, "set_win_state: win %#x, state: %u\n", - win->id, state); + WINID(win), state); if (win == NULL) return; @@ -2877,9 +2920,10 @@ get_win_state(xcb_window_t w) } void -version(struct swm_region *r, union arg *args) +version(struct binding *bp, struct swm_region *r, union arg *args) { /* suppress unused warnings since vars are needed */ + (void)bp; (void)r; (void)args; @@ -3024,9 +3068,10 @@ count_win(struct workspace *ws, bool count_transient) } void -quit(struct swm_region *r, union arg *args) +quit(struct binding *bp, struct swm_region *r, union arg *args) { /* suppress unused warnings since vars are needed */ + (void)bp; (void)r; (void)args; @@ -3040,13 +3085,13 @@ lower_window(struct ws_win *win) struct ws_win *target = NULL; struct workspace *ws; + DNPRINTF(SWM_D_EVENT, "lower_window: win %#x\n", WINID(win)); + if (win == NULL) return; ws = win->ws; - DNPRINTF(SWM_D_EVENT, "lower_window: win %#x\n", win->id); - TAILQ_FOREACH(target, &ws->stack, stack_entry) { if (target == win || ICONIC(target)) continue; @@ -3101,11 +3146,12 @@ raise_window(struct ws_win *win) struct ws_win *target = NULL; struct workspace *ws; + DNPRINTF(SWM_D_EVENT, "raise_window: win %#x\n", WINID(win)); + if (win == NULL) return; - ws = win->ws; - DNPRINTF(SWM_D_EVENT, "raise_window: win %#x\n", win->id); + ws = win->ws; TAILQ_FOREACH(target, &ws->stack, stack_entry) { if (target == win || ICONIC(target)) @@ -3267,9 +3313,10 @@ fake_keypress(struct ws_win *win, xcb_keysym_t keysym, uint16_t modifiers) } void -restart(struct swm_region *r, union arg *args) +restart(struct binding *bp, struct swm_region *r, union arg *args) { /* suppress unused warning since var is needed */ + (void)bp; (void)r; (void)args; @@ -3279,7 +3326,7 @@ restart(struct swm_region *r, union arg *args) execvp(start_argv[0], start_argv); warn("execvp failed"); - quit(NULL, NULL); + quit(NULL, NULL, NULL); } struct ws_win * @@ -3494,23 +3541,29 @@ spawn(int ws_idx, union arg *args, bool close_fd) void kill_refs(struct ws_win *win) { - int i, x, num_screens; - struct swm_region *r; struct workspace *ws; + struct ws_win *w; + int i, j, num_screens; if (win == NULL) return; num_screens = get_screen_count(); - for (i = 0; i < num_screens; i++) - TAILQ_FOREACH(r, &screens[i].rl, entry) - for (x = 0; x < workspace_limit; x++) { - ws = &r->s->ws[x]; - if (win == ws->focus) - ws->focus = NULL; - if (win == ws->focus_prev) - ws->focus_prev = NULL; - } + for (i = 0; i < num_screens; i++) { + for (j = 0; j < workspace_limit; j++) { + ws = &screens[i].ws[j]; + + if (win == ws->focus) + ws->focus = NULL; + if (win == ws->focus_prev) + ws->focus_prev = NULL; + + if (TRANS(win)) + TAILQ_FOREACH(w, &ws->winlist, entry) + if (win == w->focus_child) + w->focus_child = NULL; + } + } } int @@ -3678,9 +3731,8 @@ focus_win(struct ws_win *win) ws->focus = win; } - /* If this window directs focus to a child window, then clear. */ - if (win->focus_child) - win->focus_child = NULL; + /* Clear focus child redirect. */ + win->focus_child = NULL; /* If transient, adjust parent's focus child for focus_magic. */ if (TRANS(win)) { @@ -3692,8 +3744,7 @@ focus_win(struct ws_win *win) if (cfw != win && ws->r != NULL) { /* Set input focus if no input hint, or indicated by hint. */ if (!(win->hints.flags & XCB_ICCCM_WM_HINT_INPUT) || - (win->hints.flags & XCB_ICCCM_WM_HINT_INPUT && - win->hints.input)) + win->hints.input) xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, win->id, last_event_time); else @@ -3814,7 +3865,7 @@ event_drain(uint8_t rt) /* ensure all pending requests have been processed before filtering. */ xcb_aux_sync(conn); - while ((evt = xcb_poll_for_event(conn))) { + while ((evt = get_next_event(false))) { if (XCB_EVENT_RESPONSE_TYPE(evt) != rt) event_handle(evt); @@ -3890,7 +3941,7 @@ focus_region(struct swm_region *r) } void -switchws(struct swm_region *r, union arg *args) +switchws(struct binding *bp, struct swm_region *r, union arg *args) { struct swm_region *this_r, *other_r; struct ws_win *win; @@ -3899,6 +3950,8 @@ switchws(struct swm_region *r, union arg *args) int wsid = args->id; bool unmap_old = false; + (void)bp; + if (!(r && r->s)) return; @@ -3997,7 +4050,7 @@ switchws(struct swm_region *r, union arg *args) } void -cyclews(struct swm_region *r, union arg *args) +cyclews(struct binding *bp, struct swm_region *r, union arg *args) { union arg a; struct swm_screen *s = r->s; @@ -4039,16 +4092,16 @@ cyclews(struct swm_region *r, union arg *args) continue; if (mv) - send_to_ws(r, &a); + send_to_ws(bp, r, &a); - switchws(r, &a); + switchws(bp, r, &a); } while (a.id != r->ws->idx); DNPRINTF(SWM_D_FOCUS, "cyclews: done\n"); } void -priorws(struct swm_region *r, union arg *args) +priorws(struct binding *bp, struct swm_region *r, union arg *args) { union arg a; @@ -4061,16 +4114,18 @@ priorws(struct swm_region *r, union arg *args) return; a.id = r->ws_prior->idx; - switchws(r, &a); + switchws(bp, r, &a); DNPRINTF(SWM_D_FOCUS, "priorws: done\n"); } void -focusrg(struct swm_region *r, union arg *args) +focusrg(struct binding *bp, struct swm_region *r, union arg *args) { int ridx = args->id, i, num_screens; struct swm_region *rr = NULL; + (void)bp; + num_screens = get_screen_count(); /* do nothing if we don't have more than one screen */ if (!(num_screens > 1 || outputs > 1)) @@ -4092,7 +4147,7 @@ focusrg(struct swm_region *r, union arg *args) } void -cyclerg(struct swm_region *r, union arg *args) +cyclerg(struct binding *bp, struct swm_region *r, union arg *args) { union arg a; struct swm_region *rr = NULL; @@ -4135,7 +4190,7 @@ cyclerg(struct swm_region *r, union arg *args) case SWM_ARG_ID_CYCLERG_MOVE_UP: case SWM_ARG_ID_CYCLERG_MOVE_DOWN: a.id = rr->ws->idx; - switchws(r, &a); + switchws(bp, r, &a); break; default: return; @@ -4168,12 +4223,14 @@ sort_windows(struct ws_win_list *wl) } void -swapwin(struct swm_region *r, union arg *args) +swapwin(struct binding *bp, struct swm_region *r, union arg *args) { struct ws_win *target, *source; struct ws_win *cur_focus; struct ws_win_list *wl; + (void)bp; + DNPRINTF(SWM_D_WS, "swapwin: id: %d, screen[%d]:%dx%d+%d+%d, ws: %d\n", args->id, r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx); @@ -4420,7 +4477,7 @@ get_region_focus(struct swm_region *r) } void -focus(struct swm_region *r, union arg *args) +focus(struct binding *bp, struct swm_region *r, union arg *args) { struct ws_win *head, *cur_focus = NULL, *winfocus = NULL; struct ws_win_list *wl = NULL; @@ -4522,7 +4579,7 @@ focus(struct swm_region *r, union arg *args) /* Switch ws if new focus is on a different ws. */ if (winfocus && winfocus->ws != ws) { a.id = winfocus->ws->idx; - switchws(r, &a); + switchws(bp, r, &a); } break; default: @@ -4541,11 +4598,24 @@ out: } void -cycle_layout(struct swm_region *r, union arg *args) +focus_pointer(struct binding *bp, struct swm_region *r, union arg *args) +{ + (void)args; + + /* Not needed for buttons since this is already done in buttonpress. */ + if (bp->type == KEYBIND) { + focus_win(get_pointer_win(r->s->root)); + focus_flush(); + } +} + +void +cycle_layout(struct binding *bp, struct swm_region *r, union arg *args) { struct workspace *ws = r->ws; /* suppress unused warning since var is needed */ + (void)bp; (void)args; DNPRINTF(SWM_D_EVENT, "cycle_layout: workspace: %d\n", ws->idx); @@ -4566,10 +4636,12 @@ cycle_layout(struct swm_region *r, union arg *args) } void -stack_config(struct swm_region *r, union arg *args) +stack_config(struct binding *bp, struct swm_region *r, union arg *args) { struct workspace *ws = r->ws; + (void)bp; + DNPRINTF(SWM_D_STACK, "stack_config: id: %d workspace: %d\n", args->id, ws->idx); @@ -4695,6 +4767,8 @@ update_floater(struct ws_win *win) struct workspace *ws; struct swm_region *r; + DNPRINTF(SWM_D_MISC, "update_floater: win %#x\n", WINID(win)); + if (win == NULL) return; @@ -4703,8 +4777,6 @@ update_floater(struct ws_win *win) if ((r = ws->r) == NULL) return; - DNPRINTF(SWM_D_MISC, "update_floater: win %#x\n", win->id); - win->bordered = true; if (FULLSCREEN(win)) { @@ -4812,11 +4884,12 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, bool flip) { struct swm_geometry win_g, r_g = *g; struct ws_win *win; - int i, j, s, stacks; + int i = 0, j = 0, s = 0, stacks = 0; int w_inc = 1, h_inc, w_base = 1, h_base; - int hrh, extra = 0, h_slice, last_h = 0; - int split, colno, winno, mwin, msize, mscale; - int remain, missing, v_slice; + int hrh = 0, extra = 0, h_slice = 0, last_h = 0; + int split = 0, colno = 0; + int winno, mwin = 0, msize = 0; + int remain, missing, v_slice, mscale; bool bordered = true, reconfigure = false; DNPRINTF(SWM_D_STACK, "stack_master: workspace: %d, rot: %s, " @@ -5205,7 +5278,7 @@ max_stack(struct workspace *ws, struct swm_geometry *g) } void -send_to_rg(struct swm_region *r, union arg *args) +send_to_rg(struct binding *bp, struct swm_region *r, union arg *args) { int ridx = args->id, i, num_screens; struct swm_region *rr = NULL; @@ -5227,7 +5300,7 @@ send_to_rg(struct swm_region *r, union arg *args) a.id = rr->ws->idx; - send_to_ws(r, &a); + send_to_ws(bp, r, &a); } struct swm_region * @@ -5249,11 +5322,13 @@ region_under(struct swm_screen *s, int x, int y) /* Transfer focused window to target workspace and focus. */ void -send_to_ws(struct swm_region *r, union arg *args) +send_to_ws(struct binding *bp, struct swm_region *r, union arg *args) { int wsid = args->id; struct ws_win *win = NULL; + (void)bp; + if (r && r->ws && r->ws->focus) win = r->ws->focus; else @@ -5403,9 +5478,10 @@ win_to_ws(struct ws_win *win, int wsid, bool unfocus) } void -pressbutton(struct swm_region *r, union arg *args) +pressbutton(struct binding *bp, struct swm_region *r, union arg *args) { /* suppress unused warning since var is needed */ + (void)bp; (void)r; xcb_test_fake_input(conn, XCB_BUTTON_PRESS, args->id, @@ -5415,9 +5491,10 @@ pressbutton(struct swm_region *r, union arg *args) } void -raise_toggle(struct swm_region *r, union arg *args) +raise_toggle(struct binding *bp, struct swm_region *r, union arg *args) { /* Suppress warning. */ + (void)bp; (void)args; if (r == NULL || r->ws == NULL) @@ -5435,11 +5512,12 @@ raise_toggle(struct swm_region *r, union arg *args) } void -iconify(struct swm_region *r, union arg *args) +iconify(struct binding *bp, struct swm_region *r, union arg *args) { struct ws_win *w; /* Suppress warning. */ + (void)bp; (void)args; if ((w = r->ws->focus) == NULL) @@ -5487,13 +5565,15 @@ get_win_name(xcb_window_t win) } void -uniconify(struct swm_region *r, union arg *args) +uniconify(struct binding *bp, struct swm_region *r, union arg *args) { struct ws_win *win; FILE *lfile; char *name; int count = 0; + (void)bp; + DNPRINTF(SWM_D_MISC, "uniconify\n"); if (r == NULL || r->ws == NULL) @@ -5536,10 +5616,12 @@ uniconify(struct swm_region *r, union arg *args) } void -name_workspace(struct swm_region *r, union arg *args) +name_workspace(struct binding *bp, struct swm_region *r, union arg *args) { FILE *lfile; + (void)bp; + DNPRINTF(SWM_D_MISC, "name_workspace\n"); if (r == NULL) @@ -5558,12 +5640,14 @@ name_workspace(struct swm_region *r, union arg *args) } void -search_workspace(struct swm_region *r, union arg *args) +search_workspace(struct binding *bp, struct swm_region *r, union arg *args) { int i; struct workspace *ws; FILE *lfile; + (void)bp; + DNPRINTF(SWM_D_MISC, "search_workspace\n"); if (r == NULL) @@ -5601,7 +5685,7 @@ search_win_cleanup(void) } void -search_win(struct swm_region *r, union arg *args) +search_win(struct binding *bp, struct swm_region *r, union arg *args) { struct ws_win *win = NULL; struct search_window *sw = NULL; @@ -5618,6 +5702,8 @@ search_win(struct swm_region *r, union arg *args) XGCValues l_gcv; XRectangle l_ibox, l_lbox; + (void)bp; + DNPRINTF(SWM_D_MISC, "search_win\n"); search_r = r; @@ -5966,7 +6052,7 @@ search_resp_search_workspace(const char *resp) } free(q); a.id = ws_idx - 1; - switchws(search_r, &a); + switchws(NULL, search_r, &a); } void @@ -6062,8 +6148,10 @@ done: } void -wkill(struct swm_region *r, union arg *args) +wkill(struct binding *bp, struct swm_region *r, union arg *args) { + (void)bp; + DNPRINTF(SWM_D_MISC, "wkill: win %#x, id: %d\n", WINID(r->ws->focus), args->id); @@ -6097,11 +6185,12 @@ clear_maximized(struct workspace *ws) } void -maximize_toggle(struct swm_region *r, union arg *args) +maximize_toggle(struct binding *bp, struct swm_region *r, union arg *args) { struct ws_win *w = r->ws->focus; /* suppress unused warning since var is needed */ + (void)bp; (void)args; if (w == NULL) @@ -6129,11 +6218,12 @@ maximize_toggle(struct swm_region *r, union arg *args) } void -floating_toggle(struct swm_region *r, union arg *args) +floating_toggle(struct binding *bp, struct swm_region *r, union arg *args) { struct ws_win *w = r->ws->focus; /* suppress unused warning since var is needed */ + (void)bp; (void)args; if (w == NULL) @@ -6287,22 +6377,87 @@ update_window(struct ws_win *win) xcb_configure_window(conn, win->id, mask, wc); } +struct event { + SIMPLEQ_ENTRY(event) entry; + xcb_generic_event_t *ev; +}; +SIMPLEQ_HEAD(event_queue, event) events = SIMPLEQ_HEAD_INITIALIZER(events); + +xcb_generic_event_t * +get_next_event(bool wait) +{ + struct event *ep; + xcb_generic_event_t *evt; + + /* Try queue first. */ + if ((ep = SIMPLEQ_FIRST(&events))) { + evt = ep->ev; + SIMPLEQ_REMOVE_HEAD(&events, entry); + free(ep); + } else if (wait) + evt = xcb_wait_for_event(conn); + else + evt = xcb_poll_for_event(conn); + + return evt; +} + +void +put_back_event(xcb_generic_event_t *evt) +{ + struct event *ep; + if ((ep = malloc(sizeof (struct event))) == NULL) + err(1, "put_back_event: failed to allocate memory."); + ep->ev = evt; + SIMPLEQ_INSERT_HEAD(&events, ep, entry); +} + +/* Peeks at next event to detect auto-repeat. */ +bool +keyrepeating(xcb_key_release_event_t *kre) +{ + xcb_generic_event_t *evt; + + /* Ensure repeating keypress is finished processing. */ + xcb_aux_sync(conn); + + if ((evt = get_next_event(false))) { + put_back_event(evt); + + if (XCB_EVENT_RESPONSE_TYPE(evt) == XCB_KEY_PRESS && + kre->sequence == evt->sequence && + kre->detail == ((xcb_key_press_event_t *)evt)->detail) + return true; + } + + return false; +} + +bool +keybindreleased(struct binding *bp, xcb_key_release_event_t *kre) +{ + if (bp->type == KEYBIND && !keyrepeating(kre) && + bp->value == xcb_key_press_lookup_keysym(syms, kre, 0)) + return true; + + return false; +} + #define SWM_RESIZE_STEPS (50) void -resize(struct ws_win *win, union arg *args) +resize_win(struct ws_win *win, struct binding *bp, int opt) { xcb_timestamp_t timestamp = 0; struct swm_region *r = NULL; struct swm_geometry g; - int resize_stp = 0; int top = 0, left = 0; int dx, dy; xcb_cursor_t cursor; xcb_query_pointer_reply_t *xpr = NULL; xcb_generic_event_t *evt; xcb_motion_notify_event_t *mne; - bool resizing; + bool resizing, step = false; if (win == NULL) return; @@ -6338,27 +6493,27 @@ resize(struct ws_win *win, union arg *args) goto out; } - switch (args->id) { + switch (opt) { case SWM_ARG_ID_WIDTHSHRINK: WIDTH(win) -= SWM_RESIZE_STEPS; - resize_stp = 1; + step = true; break; case SWM_ARG_ID_WIDTHGROW: WIDTH(win) += SWM_RESIZE_STEPS; - resize_stp = 1; + step = true; break; case SWM_ARG_ID_HEIGHTSHRINK: HEIGHT(win) -= SWM_RESIZE_STEPS; - resize_stp = 1; + step = true; break; case SWM_ARG_ID_HEIGHTGROW: HEIGHT(win) += SWM_RESIZE_STEPS; - resize_stp = 1; + step = true; break; default: break; } - if (resize_stp) { + if (step) { region_containment(win, r, SWM_CW_ALLSIDES | SWM_CW_RESIZABLE | SWM_CW_HARDBOUNDARY); update_window(win); @@ -6384,7 +6539,7 @@ resize(struct ws_win *win, union arg *args) if (xpr->win_y < HEIGHT(win) / 2) top = 1; - if (args->id == SWM_ARG_ID_CENTER) + if (opt == SWM_ARG_ID_CENTER) cursor = cursors[XC_SIZING].cid; else if (top) cursor = cursors[left ? XC_TOP_LEFT_CORNER : @@ -6395,18 +6550,31 @@ resize(struct ws_win *win, union arg *args) xcb_grab_pointer(conn, 0, win->id, MOUSEMASK, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_WINDOW_NONE, cursor, - XCB_CURRENT_TIME), + XCB_CURRENT_TIME); + + /* Release keyboard freeze if called via keybind. */ + if (bp->type == KEYBIND) + xcb_allow_events(conn, XCB_ALLOW_ASYNC_KEYBOARD, + XCB_CURRENT_TIME); xcb_flush(conn); resizing = true; - while (resizing && (evt = xcb_wait_for_event(conn))) { + while (resizing && (evt = get_next_event(true))) { switch (XCB_EVENT_RESPONSE_TYPE(evt)) { case XCB_BUTTON_RELEASE: - DNPRINTF(SWM_D_EVENT, "resize: BUTTON_RELEASE\n"); - resizing = false; + if (bp->type == BTNBIND && bp->value == + ((xcb_button_release_event_t *)evt)->detail) + resizing = false; + break; + case XCB_KEY_RELEASE: + if (keybindreleased(bp, (xcb_key_release_event_t *)evt)) + resizing = false; break; case XCB_MOTION_NOTIFY: mne = (xcb_motion_notify_event_t *)evt; + DNPRINTF(SWM_D_EVENT, "resize: MOTION_NOTIFY: " + "root: %#x\n", mne->root); + /* cursor offset/delta from start of the operation */ dx = mne->root_x - xpr->root_x; dy = mne->root_y - xpr->root_y; @@ -6415,7 +6583,7 @@ resize(struct ws_win *win, union arg *args) if (top) dy = -dy; - if (args->id == SWM_ARG_ID_CENTER) { + if (opt == SWM_ARG_ID_CENTER) { if (g.h / 2 + dy < 1) dy = 1 - g.h / 2; @@ -6435,7 +6603,7 @@ resize(struct ws_win *win, union arg *args) if (left) dx = -dx; - if (args->id == SWM_ARG_ID_CENTER) { + if (opt == SWM_ARG_ID_CENTER) { if (g.w / 2 + dx < 1) dx = 1 - g.w / 2; @@ -6462,8 +6630,16 @@ resize(struct ws_win *win, union arg *args) xcb_flush(conn); } break; + case XCB_BUTTON_PRESS: + /* Ignore. */ + DNPRINTF(SWM_D_EVENT, "resize: BUTTON_PRESS ignored\n"); + xcb_allow_events(conn, XCB_ALLOW_ASYNC_POINTER, + ((xcb_button_press_event_t *)evt)->time); + xcb_flush(conn); + break; case XCB_KEY_PRESS: /* Ignore. */ + DNPRINTF(SWM_D_EVENT, "resize: KEY_PRESS ignored\n"); xcb_allow_events(conn, XCB_ALLOW_ASYNC_KEYBOARD, ((xcb_key_press_event_t *)evt)->time); xcb_flush(conn); @@ -6494,17 +6670,35 @@ out: } void -resize_step(struct swm_region *r, union arg *args) +resize(struct binding *bp, struct swm_region *r, union arg *args) { struct ws_win *win = NULL; + xcb_query_pointer_reply_t *qpr = NULL; - if (r && r->ws && r->ws->focus) - win = r->ws->focus; - else + if (r == NULL) return; - resize(win, args); - center_pointer(r); + if (args->id != SWM_ARG_ID_DONTCENTER && + args->id != SWM_ARG_ID_CENTER) { + /* {height,width}_{grow,shrink} use the focus window. */ + if (r->ws) + win = r->ws->focus; + } else { + /* resize uses window under pointer. */ + qpr = xcb_query_pointer_reply(conn, + xcb_query_pointer(conn, r->s->root), NULL); + if (qpr) + win = find_window(qpr->child); + } + + if (win == NULL) + return; + + resize_win(win, bp, args->id); + + if (args->id && bp->type == KEYBIND) + center_pointer(r); + focus_flush(); } @@ -6537,15 +6731,14 @@ regionize(struct ws_win *win, int x, int y) #define SWM_MOVE_STEPS (50) void -move(struct ws_win *win, union arg *args) +move_win(struct ws_win *win, struct binding *bp, int opt) { struct swm_region *r; xcb_timestamp_t timestamp = 0; - int move_stp = 0; xcb_query_pointer_reply_t *qpr = NULL; xcb_generic_event_t *evt; xcb_motion_notify_event_t *mne; - bool moving, restack = false; + bool moving, restack = false, step = false; if (win == NULL) return; @@ -6583,28 +6776,27 @@ move(struct ws_win *win, union arg *args) goto out; } - move_stp = 0; - switch (args->id) { + switch (opt) { case SWM_ARG_ID_MOVELEFT: X(win) -= (SWM_MOVE_STEPS - border_width); - move_stp = 1; + step = true; break; case SWM_ARG_ID_MOVERIGHT: X(win) += (SWM_MOVE_STEPS - border_width); - move_stp = 1; + step = true; break; case SWM_ARG_ID_MOVEUP: Y(win) -= (SWM_MOVE_STEPS - border_width); - move_stp = 1; + step = true; break; case SWM_ARG_ID_MOVEDOWN: Y(win) += (SWM_MOVE_STEPS - border_width); - move_stp = 1; + step = true; break; default: break; } - if (move_stp) { + if (step) { regionize(win, -1, -1); region_containment(win, win->ws->r, SWM_CW_ALLSIDES); update_window(win); @@ -6624,21 +6816,40 @@ move(struct ws_win *win, union arg *args) return; } + /* Release keyboard freeze if called via keybind. */ + if (bp->type == KEYBIND) + xcb_allow_events(conn, XCB_ALLOW_ASYNC_KEYBOARD, + XCB_CURRENT_TIME); + regionize(win, qpr->root_x, qpr->root_y); region_containment(win, win->ws->r, SWM_CW_ALLSIDES | SWM_CW_SOFTBOUNDARY); update_window(win); xcb_flush(conn); moving = true; - while (moving && (evt = xcb_wait_for_event(conn))) { + while (moving && (evt = get_next_event(true))) { switch (XCB_EVENT_RESPONSE_TYPE(evt)) { case XCB_BUTTON_RELEASE: - DNPRINTF(SWM_D_EVENT, "move: BUTTON_RELEASE\n"); - moving = false; + if (bp->type == BTNBIND && bp->value == + ((xcb_button_release_event_t *)evt)->detail) + moving = false; + + xcb_allow_events(conn, XCB_ALLOW_ASYNC_POINTER, + ((xcb_button_release_event_t *)evt)->time); + xcb_flush(conn); + break; + case XCB_KEY_RELEASE: + if (keybindreleased(bp, (xcb_key_release_event_t *)evt)) + moving = false; + + xcb_allow_events(conn, XCB_ALLOW_ASYNC_KEYBOARD, + ((xcb_key_release_event_t *)evt)->time); + xcb_flush(conn); break; case XCB_MOTION_NOTIFY: mne = (xcb_motion_notify_event_t *)evt; - DNPRINTF(SWM_D_EVENT, "motion: root: %#x\n", mne->root); + DNPRINTF(SWM_D_EVENT, "move: MOTION_NOTIFY: " + "root: %#x\n", mne->root); X(win) = mne->root_x - qpr->win_x - border_width; Y(win) = mne->root_y - qpr->win_y - border_width; @@ -6652,6 +6863,12 @@ move(struct ws_win *win, union arg *args) xcb_flush(conn); } break; + case XCB_BUTTON_PRESS: + /* Thaw and ignore. */ + xcb_allow_events(conn, XCB_ALLOW_ASYNC_POINTER, + ((xcb_button_press_event_t *)evt)->time); + xcb_flush(conn); + break; case XCB_KEY_PRESS: /* Ignore. */ xcb_allow_events(conn, XCB_ALLOW_ASYNC_KEYBOARD, @@ -6661,7 +6878,7 @@ move(struct ws_win *win, union arg *args) default: event_handle(evt); - /* It's possible for win to have been freed above. */ + /* It's possible for win to have been freed. */ if (validate_win(win)) { DNPRINTF(SWM_D_EVENT, "move: invalid win.\n"); goto out; @@ -6691,202 +6908,181 @@ out: } void -move_step(struct swm_region *r, union arg *args) +move(struct binding *bp, struct swm_region *r, union arg *args) { - struct ws_win *win = NULL; + struct ws_win *win = NULL; + xcb_query_pointer_reply_t *qpr = NULL; - if (r && r->ws && r->ws->focus) - win = r->ws->focus; - else + if (r == NULL) return; - if (!TRANS(win) && !ABOVE(win)) + if (args->id) { + /* move_* uses focus window. */ + if (r->ws) + win = r->ws->focus; + } else { + /* move uses window under pointer. */ + qpr = xcb_query_pointer_reply(conn, + xcb_query_pointer(conn, r->s->root), NULL); + if (qpr) + win = find_window(qpr->child); + } + + if (win == NULL) return; - move(win, args); - center_pointer(r); + move_win(win, bp, args->id); + + if (args->id && bp->type == KEYBIND) + center_pointer(r); + focus_flush(); } -/* key definitions */ -struct keyfunc { +/* action definitions */ +struct action { char name[SWM_FUNCNAME_LEN]; - void (*func)(struct swm_region *r, union arg *); + void (*func)(struct binding *, struct swm_region *, + union arg *); + uint32_t flags; union arg args; -} keyfuncs[KF_INVALID + 1] = { +} actions[FN_INVALID + 1] = { /* name function argument */ - { "bar_toggle", bar_toggle, {.id = SWM_ARG_ID_BAR_TOGGLE} }, - { "bar_toggle_ws", bar_toggle, {.id = SWM_ARG_ID_BAR_TOGGLE_WS} }, - { "button2", pressbutton, {2} }, - { "cycle_layout", cycle_layout, {0} }, - { "flip_layout", stack_config, {.id = SWM_ARG_ID_FLIPLAYOUT} }, - { "float_toggle", floating_toggle,{0} }, - { "focus_main", focus, {.id = SWM_ARG_ID_FOCUSMAIN} }, - { "focus_next", focus, {.id = SWM_ARG_ID_FOCUSNEXT} }, - { "focus_prev", focus, {.id = SWM_ARG_ID_FOCUSPREV} }, - { "focus_urgent", focus, {.id = SWM_ARG_ID_FOCUSURGENT} }, - { "maximize_toggle", maximize_toggle,{0} }, - { "height_grow", resize_step, {.id = SWM_ARG_ID_HEIGHTGROW} }, - { "height_shrink", resize_step, {.id = SWM_ARG_ID_HEIGHTSHRINK} }, - { "iconify", iconify, {0} }, - { "master_shrink", stack_config, {.id = SWM_ARG_ID_MASTERSHRINK} }, - { "master_grow", stack_config, {.id = SWM_ARG_ID_MASTERGROW} }, - { "master_add", stack_config, {.id = SWM_ARG_ID_MASTERADD} }, - { "master_del", stack_config, {.id = SWM_ARG_ID_MASTERDEL} }, - { "move_down", move_step, {.id = SWM_ARG_ID_MOVEDOWN} }, - { "move_left", move_step, {.id = SWM_ARG_ID_MOVELEFT} }, - { "move_right", move_step, {.id = SWM_ARG_ID_MOVERIGHT} }, - { "move_up", move_step, {.id = SWM_ARG_ID_MOVEUP} }, - { "mvrg_1", send_to_rg, {.id = 0} }, - { "mvrg_2", send_to_rg, {.id = 1} }, - { "mvrg_3", send_to_rg, {.id = 2} }, - { "mvrg_4", send_to_rg, {.id = 3} }, - { "mvrg_5", send_to_rg, {.id = 4} }, - { "mvrg_6", send_to_rg, {.id = 5} }, - { "mvrg_7", send_to_rg, {.id = 6} }, - { "mvrg_8", send_to_rg, {.id = 7} }, - { "mvrg_9", send_to_rg, {.id = 8} }, - { "mvws_1", send_to_ws, {.id = 0} }, - { "mvws_2", send_to_ws, {.id = 1} }, - { "mvws_3", send_to_ws, {.id = 2} }, - { "mvws_4", send_to_ws, {.id = 3} }, - { "mvws_5", send_to_ws, {.id = 4} }, - { "mvws_6", send_to_ws, {.id = 5} }, - { "mvws_7", send_to_ws, {.id = 6} }, - { "mvws_8", send_to_ws, {.id = 7} }, - { "mvws_9", send_to_ws, {.id = 8} }, - { "mvws_10", send_to_ws, {.id = 9} }, - { "mvws_11", send_to_ws, {.id = 10} }, - { "mvws_12", send_to_ws, {.id = 11} }, - { "mvws_13", send_to_ws, {.id = 12} }, - { "mvws_14", send_to_ws, {.id = 13} }, - { "mvws_15", send_to_ws, {.id = 14} }, - { "mvws_16", send_to_ws, {.id = 15} }, - { "mvws_17", send_to_ws, {.id = 16} }, - { "mvws_18", send_to_ws, {.id = 17} }, - { "mvws_19", send_to_ws, {.id = 18} }, - { "mvws_20", send_to_ws, {.id = 19} }, - { "mvws_21", send_to_ws, {.id = 20} }, - { "mvws_22", send_to_ws, {.id = 21} }, - { "name_workspace", name_workspace, {0} }, - { "quit", quit, {0} }, - { "raise_toggle", raise_toggle, {0} }, - { "restart", restart, {0} }, - { "rg_1", focusrg, {.id = 0} }, - { "rg_2", focusrg, {.id = 1} }, - { "rg_3", focusrg, {.id = 2} }, - { "rg_4", focusrg, {.id = 3} }, - { "rg_5", focusrg, {.id = 4} }, - { "rg_6", focusrg, {.id = 5} }, - { "rg_7", focusrg, {.id = 6} }, - { "rg_8", focusrg, {.id = 7} }, - { "rg_9", focusrg, {.id = 8} }, - { "rg_move_next", cyclerg, {.id = SWM_ARG_ID_CYCLERG_MOVE_UP} }, - { "rg_move_prev", cyclerg, {.id = SWM_ARG_ID_CYCLERG_MOVE_DOWN} }, - { "rg_next", cyclerg, {.id = SWM_ARG_ID_CYCLERG_UP} }, - { "rg_prev", cyclerg, {.id = SWM_ARG_ID_CYCLERG_DOWN} }, - { "screen_next", cyclerg, {.id = SWM_ARG_ID_CYCLERG_UP} }, - { "screen_prev", cyclerg, {.id = SWM_ARG_ID_CYCLERG_DOWN} }, - { "search_win", search_win, {0} }, - { "search_workspace", search_workspace, {0} }, - { "spawn_custom", NULL, {0} }, - { "stack_balance", stack_config, {.id = SWM_ARG_ID_STACKBALANCE} }, - { "stack_inc", stack_config, {.id = SWM_ARG_ID_STACKINC} }, - { "stack_dec", stack_config, {.id = SWM_ARG_ID_STACKDEC} }, - { "stack_reset", stack_config, {.id = SWM_ARG_ID_STACKRESET} }, - { "swap_main", swapwin, {.id = SWM_ARG_ID_SWAPMAIN} }, - { "swap_next", swapwin, {.id = SWM_ARG_ID_SWAPNEXT} }, - { "swap_prev", swapwin, {.id = SWM_ARG_ID_SWAPPREV} }, - { "uniconify", uniconify, {0} }, - { "version", version, {0} }, - { "width_grow", resize_step, {.id = SWM_ARG_ID_WIDTHGROW} }, - { "width_shrink", resize_step, {.id = SWM_ARG_ID_WIDTHSHRINK} }, - { "wind_del", wkill, {.id = SWM_ARG_ID_DELETEWINDOW} }, - { "wind_kill", wkill, {.id = SWM_ARG_ID_KILLWINDOW} }, - { "ws_1", switchws, {.id = 0} }, - { "ws_2", switchws, {.id = 1} }, - { "ws_3", switchws, {.id = 2} }, - { "ws_4", switchws, {.id = 3} }, - { "ws_5", switchws, {.id = 4} }, - { "ws_6", switchws, {.id = 5} }, - { "ws_7", switchws, {.id = 6} }, - { "ws_8", switchws, {.id = 7} }, - { "ws_9", switchws, {.id = 8} }, - { "ws_10", switchws, {.id = 9} }, - { "ws_11", switchws, {.id = 10} }, - { "ws_12", switchws, {.id = 11} }, - { "ws_13", switchws, {.id = 12} }, - { "ws_14", switchws, {.id = 13} }, - { "ws_15", switchws, {.id = 14} }, - { "ws_16", switchws, {.id = 15} }, - { "ws_17", switchws, {.id = 16} }, - { "ws_18", switchws, {.id = 17} }, - { "ws_19", switchws, {.id = 18} }, - { "ws_20", switchws, {.id = 19} }, - { "ws_21", switchws, {.id = 20} }, - { "ws_22", switchws, {.id = 21} }, - { "ws_next", cyclews, {.id = SWM_ARG_ID_CYCLEWS_UP} }, - { "ws_next_all", cyclews, {.id = SWM_ARG_ID_CYCLEWS_UP_ALL} }, - { "ws_next_move", cyclews, {.id = SWM_ARG_ID_CYCLEWS_MOVE_UP} }, - { "ws_prev", cyclews, {.id = SWM_ARG_ID_CYCLEWS_DOWN} }, - { "ws_prev_all", cyclews, {.id = SWM_ARG_ID_CYCLEWS_DOWN_ALL} }, - { "ws_prev_move", cyclews, {.id = SWM_ARG_ID_CYCLEWS_MOVE_DOWN} }, - { "ws_prior", priorws, {0} }, - { "dumpwins", dumpwins, {0} }, /* MUST BE LAST */ - { "invalid key func", NULL, {0} }, -}; - -int -key_cmp(struct key *kp1, struct key *kp2) -{ - if (kp1->keysym < kp2->keysym) - return (-1); - if (kp1->keysym > kp2->keysym) - return (1); - - if (kp1->mod < kp2->mod) - return (-1); - if (kp1->mod > kp2->mod) - return (1); - - return (0); -} - -/* mouse */ -enum { client_click, root_click }; -struct button { - unsigned int action; - unsigned int mask; - unsigned int button; - void (*func)(struct ws_win *, union arg *); - union arg args; -} buttons[] = { -#define MODKEY_SHIFT MODKEY | XCB_MOD_MASK_SHIFT - /* action key mouse button func args */ - { client_click, MODKEY, XCB_BUTTON_INDEX_3, resize, {.id = SWM_ARG_ID_DONTCENTER} }, - { client_click, MODKEY_SHIFT, XCB_BUTTON_INDEX_3, resize, {.id = SWM_ARG_ID_CENTER} }, - { client_click, MODKEY, XCB_BUTTON_INDEX_1, move, {0} }, -#undef MODKEY_SHIFT + { "bar_toggle", bar_toggle, 0, {.id = SWM_ARG_ID_BAR_TOGGLE} }, + { "bar_toggle_ws", bar_toggle, 0, {.id = SWM_ARG_ID_BAR_TOGGLE_WS} }, + { "button2", pressbutton, 0, {.id = 2} }, + { "cycle_layout", cycle_layout, 0, {0} }, + { "flip_layout", stack_config, 0, {.id = SWM_ARG_ID_FLIPLAYOUT} }, + { "float_toggle", floating_toggle,0, {0} }, + { "focus", focus_pointer, 0, {0} }, + { "focus_main", focus, 0, {.id = SWM_ARG_ID_FOCUSMAIN} }, + { "focus_next", focus, 0, {.id = SWM_ARG_ID_FOCUSNEXT} }, + { "focus_prev", focus, 0, {.id = SWM_ARG_ID_FOCUSPREV} }, + { "focus_urgent", focus, 0, {.id = SWM_ARG_ID_FOCUSURGENT} }, + { "maximize_toggle", maximize_toggle,0, {0} }, + { "height_grow", resize, 0, {.id = SWM_ARG_ID_HEIGHTGROW} }, + { "height_shrink", resize, 0, {.id = SWM_ARG_ID_HEIGHTSHRINK} }, + { "iconify", iconify, 0, {0} }, + { "master_shrink", stack_config, 0, {.id = SWM_ARG_ID_MASTERSHRINK} }, + { "master_grow", stack_config, 0, {.id = SWM_ARG_ID_MASTERGROW} }, + { "master_add", stack_config, 0, {.id = SWM_ARG_ID_MASTERADD} }, + { "master_del", stack_config, 0, {.id = SWM_ARG_ID_MASTERDEL} }, + { "move", move, FN_F_NOREPLAY, {0} }, + { "move_down", move, 0, {.id = SWM_ARG_ID_MOVEDOWN} }, + { "move_left", move, 0, {.id = SWM_ARG_ID_MOVELEFT} }, + { "move_right", move, 0, {.id = SWM_ARG_ID_MOVERIGHT} }, + { "move_up", move, 0, {.id = SWM_ARG_ID_MOVEUP} }, + { "mvrg_1", send_to_rg, 0, {.id = 0} }, + { "mvrg_2", send_to_rg, 0, {.id = 1} }, + { "mvrg_3", send_to_rg, 0, {.id = 2} }, + { "mvrg_4", send_to_rg, 0, {.id = 3} }, + { "mvrg_5", send_to_rg, 0, {.id = 4} }, + { "mvrg_6", send_to_rg, 0, {.id = 5} }, + { "mvrg_7", send_to_rg, 0, {.id = 6} }, + { "mvrg_8", send_to_rg, 0, {.id = 7} }, + { "mvrg_9", send_to_rg, 0, {.id = 8} }, + { "mvws_1", send_to_ws, 0, {.id = 0} }, + { "mvws_2", send_to_ws, 0, {.id = 1} }, + { "mvws_3", send_to_ws, 0, {.id = 2} }, + { "mvws_4", send_to_ws, 0, {.id = 3} }, + { "mvws_5", send_to_ws, 0, {.id = 4} }, + { "mvws_6", send_to_ws, 0, {.id = 5} }, + { "mvws_7", send_to_ws, 0, {.id = 6} }, + { "mvws_8", send_to_ws, 0, {.id = 7} }, + { "mvws_9", send_to_ws, 0, {.id = 8} }, + { "mvws_10", send_to_ws, 0, {.id = 9} }, + { "mvws_11", send_to_ws, 0, {.id = 10} }, + { "mvws_12", send_to_ws, 0, {.id = 11} }, + { "mvws_13", send_to_ws, 0, {.id = 12} }, + { "mvws_14", send_to_ws, 0, {.id = 13} }, + { "mvws_15", send_to_ws, 0, {.id = 14} }, + { "mvws_16", send_to_ws, 0, {.id = 15} }, + { "mvws_17", send_to_ws, 0, {.id = 16} }, + { "mvws_18", send_to_ws, 0, {.id = 17} }, + { "mvws_19", send_to_ws, 0, {.id = 18} }, + { "mvws_20", send_to_ws, 0, {.id = 19} }, + { "mvws_21", send_to_ws, 0, {.id = 20} }, + { "mvws_22", send_to_ws, 0, {.id = 21} }, + { "name_workspace", name_workspace, 0, {0} }, + { "quit", quit, 0, {0} }, + { "raise_toggle", raise_toggle, 0, {0} }, + { "resize", resize, FN_F_NOREPLAY, {.id = SWM_ARG_ID_DONTCENTER} }, + { "resize_centered", resize, FN_F_NOREPLAY, {.id = SWM_ARG_ID_CENTER} }, + { "restart", restart, 0, {0} }, + { "rg_1", focusrg, 0, {.id = 0} }, + { "rg_2", focusrg, 0, {.id = 1} }, + { "rg_3", focusrg, 0, {.id = 2} }, + { "rg_4", focusrg, 0, {.id = 3} }, + { "rg_5", focusrg, 0, {.id = 4} }, + { "rg_6", focusrg, 0, {.id = 5} }, + { "rg_7", focusrg, 0, {.id = 6} }, + { "rg_8", focusrg, 0, {.id = 7} }, + { "rg_9", focusrg, 0, {.id = 8} }, + { "rg_move_next", cyclerg, 0, {.id = SWM_ARG_ID_CYCLERG_MOVE_UP} }, + { "rg_move_prev", cyclerg, 0, {.id = SWM_ARG_ID_CYCLERG_MOVE_DOWN} }, + { "rg_next", cyclerg, 0, {.id = SWM_ARG_ID_CYCLERG_UP} }, + { "rg_prev", cyclerg, 0, {.id = SWM_ARG_ID_CYCLERG_DOWN} }, + { "screen_next", cyclerg, 0, {.id = SWM_ARG_ID_CYCLERG_UP} }, + { "screen_prev", cyclerg, 0, {.id = SWM_ARG_ID_CYCLERG_DOWN} }, + { "search_win", search_win, 0, {0} }, + { "search_workspace", search_workspace, 0, {0} }, + { "spawn_custom", NULL, 0, {0} }, + { "stack_balance", stack_config, 0, {.id = SWM_ARG_ID_STACKBALANCE} }, + { "stack_inc", stack_config, 0, {.id = SWM_ARG_ID_STACKINC} }, + { "stack_dec", stack_config, 0, {.id = SWM_ARG_ID_STACKDEC} }, + { "stack_reset", stack_config, 0, {.id = SWM_ARG_ID_STACKRESET} }, + { "swap_main", swapwin, 0, {.id = SWM_ARG_ID_SWAPMAIN} }, + { "swap_next", swapwin, 0, {.id = SWM_ARG_ID_SWAPNEXT} }, + { "swap_prev", swapwin, 0, {.id = SWM_ARG_ID_SWAPPREV} }, + { "uniconify", uniconify, 0, {0} }, + { "version", version, 0, {0} }, + { "width_grow", resize, 0, {.id = SWM_ARG_ID_WIDTHGROW} }, + { "width_shrink", resize, 0, {.id = SWM_ARG_ID_WIDTHSHRINK} }, + { "wind_del", wkill, 0, {.id = SWM_ARG_ID_DELETEWINDOW} }, + { "wind_kill", wkill, 0, {.id = SWM_ARG_ID_KILLWINDOW} }, + { "ws_1", switchws, 0, {.id = 0} }, + { "ws_2", switchws, 0, {.id = 1} }, + { "ws_3", switchws, 0, {.id = 2} }, + { "ws_4", switchws, 0, {.id = 3} }, + { "ws_5", switchws, 0, {.id = 4} }, + { "ws_6", switchws, 0, {.id = 5} }, + { "ws_7", switchws, 0, {.id = 6} }, + { "ws_8", switchws, 0, {.id = 7} }, + { "ws_9", switchws, 0, {.id = 8} }, + { "ws_10", switchws, 0, {.id = 9} }, + { "ws_11", switchws, 0, {.id = 10} }, + { "ws_12", switchws, 0, {.id = 11} }, + { "ws_13", switchws, 0, {.id = 12} }, + { "ws_14", switchws, 0, {.id = 13} }, + { "ws_15", switchws, 0, {.id = 14} }, + { "ws_16", switchws, 0, {.id = 15} }, + { "ws_17", switchws, 0, {.id = 16} }, + { "ws_18", switchws, 0, {.id = 17} }, + { "ws_19", switchws, 0, {.id = 18} }, + { "ws_20", switchws, 0, {.id = 19} }, + { "ws_21", switchws, 0, {.id = 20} }, + { "ws_22", switchws, 0, {.id = 21} }, + { "ws_next", cyclews, 0, {.id = SWM_ARG_ID_CYCLEWS_UP} }, + { "ws_next_all", cyclews, 0, {.id = SWM_ARG_ID_CYCLEWS_UP_ALL} }, + { "ws_next_move", cyclews, 0, {.id = SWM_ARG_ID_CYCLEWS_MOVE_UP} }, + { "ws_prev", cyclews, 0, {.id = SWM_ARG_ID_CYCLEWS_DOWN} }, + { "ws_prev_all", cyclews, 0, {.id = SWM_ARG_ID_CYCLEWS_DOWN_ALL} }, + { "ws_prev_move", cyclews, 0, {.id = SWM_ARG_ID_CYCLEWS_MOVE_DOWN} }, + { "ws_prior", priorws, 0, {0} }, + { "dumpwins", dumpwins, 0, {0} }, /* MUST BE LAST */ + { "invalid action", NULL, 0, {0} }, }; void -update_modkey(unsigned int mod) +update_modkey(uint16_t mod) { - int i; - struct key *kp; + struct binding *bp; + /* Replace all instances of the old mod key. */ + RB_FOREACH(bp, binding_tree, &bindings) + if (bp->mod & mod_key) + bp->mod = (bp->mod & ~mod_key) | mod; mod_key = mod; - RB_FOREACH(kp, key_tree, &keys) - if (kp->mod & XCB_MOD_MASK_SHIFT) - kp->mod = mod | XCB_MOD_MASK_SHIFT; - else - kp->mod = mod; - - for (i = 0; i < LENGTH(buttons); i++) - if (buttons[i].mask & XCB_MOD_MASK_SHIFT) - buttons[i].mask = mod | XCB_MOD_MASK_SHIFT; - else - buttons[i].mask = mod; } int @@ -7221,18 +7417,17 @@ setconfspawn(const char *selector, const char *value, int flags) void validate_spawns(void) { + struct binding *bp; struct spawn_prog *sp; char which[PATH_MAX]; size_t i; - struct key *kp; - - RB_FOREACH(kp, key_tree, &keys) { - if (kp->funcid != KF_SPAWN_CUSTOM) + RB_FOREACH(bp, binding_tree, &bindings) { + if (bp->action != FN_SPAWN_CUSTOM) continue; /* find program */ - sp = spawn_find(kp->spawn_name); + sp = spawn_find(bp->spawn_name); if (sp == NULL || sp->flags & SWM_SPAWN_OPTIONAL) continue; @@ -7291,40 +7486,43 @@ setup_spawn(void) setconfspawn("initscr", "initscreen.sh", SWM_SPAWN_OPTIONAL); } -/* key bindings */ +/* bindings */ #define SWM_MODNAME_SIZE 32 #define SWM_KEY_WS "\n+ \t" int -parsekeys(const char *keystr, unsigned int currmod, unsigned int *mod, KeySym *ks) +parsebinding(const char *bindstr, uint16_t *mod, enum binding_type *type, + uint32_t *val, uint32_t *flags) { char *str, *cp, *name; - KeySym uks; + KeySym ks, lks, uks; - DNPRINTF(SWM_D_KEY, "parsekeys: enter [%s]\n", keystr); - if (mod == NULL || ks == NULL) { - DNPRINTF(SWM_D_KEY, "parsekeys: no mod or key vars\n"); + DNPRINTF(SWM_D_KEY, "parsebinding: enter [%s]\n", bindstr); + if (mod == NULL || val == NULL) { + DNPRINTF(SWM_D_KEY, "parsebinding: no mod or key vars\n"); return (1); } - if (keystr == NULL || strlen(keystr) == 0) { - DNPRINTF(SWM_D_KEY, "parsekeys: no keystr\n"); + if (bindstr == NULL || strlen(bindstr) == 0) { + DNPRINTF(SWM_D_KEY, "parsebinding: no bindstr\n"); return (1); } - if ((cp = str = strdup(keystr)) == NULL) - err(1, "parsekeys: strdup"); + if ((cp = str = strdup(bindstr)) == NULL) + err(1, "parsebinding: strdup"); - *ks = XCB_NO_SYMBOL; + *val = XCB_NO_SYMBOL; *mod = 0; + *flags = 0; + *type = KEYBIND; while ((name = strsep(&cp, SWM_KEY_WS)) != NULL) { - DNPRINTF(SWM_D_KEY, "parsekeys: key [%s]\n", name); + DNPRINTF(SWM_D_KEY, "parsebinding: entry [%s]\n", name); if (cp) cp += (long)strspn(cp, SWM_KEY_WS); if (strncasecmp(name, "MOD", SWM_MODNAME_SIZE) == 0) - *mod |= currmod; + *mod |= mod_key; else if (strncasecmp(name, "Mod1", SWM_MODNAME_SIZE) == 0) *mod |= XCB_MOD_MASK_1; else if (strncasecmp(name, "Mod2", SWM_MODNAME_SIZE) == 0) - *mod += XCB_MOD_MASK_2; + *mod |= XCB_MOD_MASK_2; else if (strncmp(name, "Mod3", SWM_MODNAME_SIZE) == 0) *mod |= XCB_MOD_MASK_3; else if (strncmp(name, "Mod4", SWM_MODNAME_SIZE) == 0) @@ -7335,21 +7533,39 @@ parsekeys(const char *keystr, unsigned int currmod, unsigned int *mod, KeySym *k *mod |= XCB_MOD_MASK_SHIFT; else if (strncasecmp(name, "CONTROL", SWM_MODNAME_SIZE) == 0) *mod |= XCB_MOD_MASK_CONTROL; - else { - *ks = XStringToKeysym(name); - XConvertCase(*ks, ks, &uks); - if (ks == XCB_NO_SYMBOL) { + else if (strncasecmp(name, "ANYMOD", SWM_MODNAME_SIZE) == 0) + *mod |= XCB_MOD_MASK_ANY; + else if (strncasecmp(name, "REPLAY", SWM_MODNAME_SIZE) == 0) + *flags |= BINDING_F_REPLAY; + else if (sscanf(name, "Button%u", val) == 1) { + DNPRINTF(SWM_D_KEY, "parsebinding: button %u\n", *val); + *type = BTNBIND; + if (*val > 255 || *val == 0) { + DNPRINTF(SWM_D_KEY, + "parsebinding: invalid button %u\n", *val); + return (1); + } + } else { + /* TODO: do this without Xlib. */ + ks = XStringToKeysym(name); + if (ks == NoSymbol) { DNPRINTF(SWM_D_KEY, - "parsekeys: invalid key %s\n", - name); + "parsebinding: invalid key %s\n", name); free(str); return (1); } + + XConvertCase(ks, &lks, &uks); + *val = (uint32_t)lks; } } + /* If ANYMOD was specified, ignore the rest. */ + if (*mod & XCB_MOD_MASK_ANY) + *mod = XCB_MOD_MASK_ANY; + free(str); - DNPRINTF(SWM_D_KEY, "parsekeys: leave ok\n"); + DNPRINTF(SWM_D_KEY, "parsebinding: leave ok\n"); return (0); } @@ -7362,97 +7578,104 @@ strdupsafe(const char *str) return (strdup(str)); } -void -key_insert(unsigned int mod, KeySym ks, enum keyfuncid kfid, - const char *spawn_name) +int +binding_cmp(struct binding *bp1, struct binding *bp2) { - struct key *kp; - - DNPRINTF(SWM_D_KEY, "key_insert: enter %s [%s]\n", - keyfuncs[kfid].name, spawn_name); + if (bp1->type < bp2->type) + return (-1); + if (bp1->type > bp2->type) + return (1); - if ((kp = malloc(sizeof *kp)) == NULL) - err(1, "key_insert: malloc"); + if (bp1->value < bp2->value) + return (-1); + if (bp1->value > bp2->value) + return (1); - kp->mod = mod; - kp->keysym = ks; - kp->funcid = kfid; - kp->spawn_name = strdupsafe(spawn_name); - RB_INSERT(key_tree, &keys, kp); + if (bp1->mod < bp2->mod) + return (-1); + if (bp1->mod > bp2->mod) + return (1); - DNPRINTF(SWM_D_KEY, "key_insert: leave\n"); + return (0); } -struct key * -key_lookup(unsigned int mod, KeySym ks) +void +binding_insert(uint16_t mod, enum binding_type type, uint32_t val, + enum actionid aid, uint32_t flags, const char *spawn_name) { - struct key kp; + struct binding *bp; - kp.keysym = ks; - kp.mod = mod; + DNPRINTF(SWM_D_KEY, "binding_insert: mod: %u, type: %d, val: %u, " + "action: %s(%d), spawn_name: %s\n", mod, type, val, + actions[aid].name, aid, spawn_name); - return (RB_FIND(key_tree, &keys, &kp)); + if ((bp = malloc(sizeof *bp)) == NULL) + err(1, "binding_insert: malloc"); + + bp->mod = mod; + bp->type = type; + bp->value = val; + bp->action = aid; + bp->flags = flags; + bp->spawn_name = strdupsafe(spawn_name); + RB_INSERT(binding_tree, &bindings, bp); + + DNPRINTF(SWM_D_KEY, "binding_insert: leave\n"); } -void -key_remove(struct key *kp) +struct binding * +binding_lookup(uint16_t mod, enum binding_type type, uint32_t val) { - DNPRINTF(SWM_D_KEY, "key_remove: %s\n", keyfuncs[kp->funcid].name); + struct binding bp; - RB_REMOVE(key_tree, &keys, kp); - free(kp->spawn_name); - free(kp); + bp.mod = mod; + bp.type = type; + bp.value = val; - DNPRINTF(SWM_D_KEY, "key_remove: leave\n"); + return (RB_FIND(binding_tree, &bindings, &bp)); } void -key_replace(struct key *kp, unsigned int mod, KeySym ks, enum keyfuncid kfid, - const char *spawn_name) +binding_remove(struct binding *bp) { - DNPRINTF(SWM_D_KEY, "key_replace: %s [%s]\n", keyfuncs[kp->funcid].name, - spawn_name); + DNPRINTF(SWM_D_KEY, "binding_remove: mod: %u, type: %d, val: %u, " + "action: %s(%d), spawn_name: %s\n", bp->mod, bp->type, bp->value, + actions[bp->action].name, bp->action, bp->spawn_name); - key_remove(kp); - key_insert(mod, ks, kfid, spawn_name); + RB_REMOVE(binding_tree, &bindings, bp); + free(bp->spawn_name); + free(bp); - DNPRINTF(SWM_D_KEY, "key_replace: leave\n"); + DNPRINTF(SWM_D_KEY, "binding_remove: leave\n"); } void -setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid, - const char *spawn_name) +setbinding(uint16_t mod, enum binding_type type, uint32_t val, + enum actionid aid, uint32_t flags, const char *spawn_name) { - struct key *kp; + struct binding *bp; - DNPRINTF(SWM_D_KEY, "setkeybinding: enter %s [%s]\n", - keyfuncs[kfid].name, spawn_name); + DNPRINTF(SWM_D_KEY, "setbinding: enter %s [%s]\n", + actions[aid].name, spawn_name); - if ((kp = key_lookup(mod, ks)) != NULL) { - if (kfid == KF_INVALID) - key_remove(kp); - else - key_replace(kp, mod, ks, kfid, spawn_name); - DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n"); - return; - } - if (kfid == KF_INVALID) { - warnx("bind: Key combination already unbound."); - DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n"); - return; - } + /* Unbind any existing. Loop is to handle MOD_MASK_ANY. */ + while ((bp = binding_lookup(mod, type, val))) + binding_remove(bp); + + if (aid != FN_INVALID) + binding_insert(mod, type, val, aid, flags, spawn_name); - key_insert(mod, ks, kfid, spawn_name); - DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n"); + DNPRINTF(SWM_D_KEY, "setbinding: leave\n"); } int setconfbinding(const char *selector, const char *value, int flags) { - enum keyfuncid kfid; - unsigned int mod; - KeySym ks; struct spawn_prog *sp; + uint32_t keybtn, opts; + uint16_t mod; + enum actionid aid; + enum binding_type type; /* suppress unused warning since var is needed */ (void)flags; @@ -7461,21 +7684,21 @@ setconfbinding(const char *selector, const char *value, int flags) "value: [%s]\n", selector, value); if (selector == NULL || strlen(selector) == 0) { DNPRINTF(SWM_D_KEY, "setconfbinding: unbind %s\n", value); - if (parsekeys(value, mod_key, &mod, &ks) == 0) { - kfid = KF_INVALID; - setkeybinding(mod, ks, kfid, NULL); + if (parsebinding(value, &mod, &type, &keybtn, &opts) == 0) { + setbinding(mod, type, keybtn, FN_INVALID, opts, NULL); return (0); } else return (1); } /* search by key function name */ - for (kfid = 0; kfid < KF_INVALID; (kfid)++) { - if (strncasecmp(selector, keyfuncs[kfid].name, + for (aid = 0; aid < FN_INVALID; aid++) { + if (strncasecmp(selector, actions[aid].name, SWM_FUNCNAME_LEN) == 0) { DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match " - "keyfunc\n", selector); - if (parsekeys(value, mod_key, &mod, &ks) == 0) { - setkeybinding(mod, ks, kfid, NULL); + "action\n", selector); + if (parsebinding(value, &mod, &type, &keybtn, + &opts) == 0) { + setbinding(mod, type, keybtn, aid, opts, NULL); return (0); } else return (1); @@ -7485,8 +7708,8 @@ setconfbinding(const char *selector, const char *value, int flags) if ((sp = spawn_find(selector)) != NULL) { DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match " "spawn\n", selector); - if (parsekeys(value, mod_key, &mod, &ks) == 0) { - setkeybinding(mod, ks, KF_SPAWN_CUSTOM, + if (parsebinding(value, &mod, &type, &keybtn, &opts) == 0) { + setbinding(mod, type, keybtn, FN_SPAWN_CUSTOM, opts, sp->name); return (0); } else @@ -7496,143 +7719,170 @@ setconfbinding(const char *selector, const char *value, int flags) return (1); } -void -setup_keys(void) -{ -#define MODKEY_SHIFT MODKEY | XCB_MOD_MASK_SHIFT - setkeybinding(MODKEY, XK_b, KF_BAR_TOGGLE, NULL); - setkeybinding(MODKEY_SHIFT, XK_b, KF_BAR_TOGGLE_WS,NULL); - setkeybinding(MODKEY, XK_v, KF_BUTTON2, NULL); - setkeybinding(MODKEY, XK_space, KF_CYCLE_LAYOUT,NULL); - setkeybinding(MODKEY_SHIFT, XK_backslash, KF_FLIP_LAYOUT, NULL); - setkeybinding(MODKEY, XK_t, KF_FLOAT_TOGGLE,NULL); - setkeybinding(MODKEY, XK_m, KF_FOCUS_MAIN, NULL); - setkeybinding(MODKEY, XK_j, KF_FOCUS_NEXT, NULL); - setkeybinding(MODKEY, XK_Tab, KF_FOCUS_NEXT, NULL); - setkeybinding(MODKEY, XK_k, KF_FOCUS_PREV, NULL); - setkeybinding(MODKEY_SHIFT, XK_Tab, KF_FOCUS_PREV, NULL); - setkeybinding(MODKEY, XK_u, KF_FOCUS_URGENT,NULL); - setkeybinding(MODKEY, XK_e, KF_MAXIMIZE_TOGGLE,NULL); - setkeybinding(MODKEY_SHIFT, XK_equal, KF_HEIGHT_GROW,NULL); - setkeybinding(MODKEY_SHIFT, XK_minus, KF_HEIGHT_SHRINK,NULL); - setkeybinding(MODKEY, XK_w, KF_ICONIFY, NULL); - setkeybinding(MODKEY, XK_h, KF_MASTER_SHRINK, NULL); - setkeybinding(MODKEY, XK_l, KF_MASTER_GROW, NULL); - setkeybinding(MODKEY, XK_comma, KF_MASTER_ADD, NULL); - setkeybinding(MODKEY, XK_period, KF_MASTER_DEL, NULL); - setkeybinding(MODKEY_SHIFT, XK_bracketright,KF_MOVE_DOWN,NULL); - setkeybinding(MODKEY, XK_bracketleft, KF_MOVE_LEFT,NULL); - setkeybinding(MODKEY, XK_bracketright,KF_MOVE_RIGHT,NULL); - setkeybinding(MODKEY_SHIFT, XK_bracketleft, KF_MOVE_UP, NULL); - setkeybinding(MODKEY_SHIFT, XK_KP_End, KF_MVRG_1, NULL); - setkeybinding(MODKEY_SHIFT, XK_KP_Down, KF_MVRG_2, NULL); - setkeybinding(MODKEY_SHIFT, XK_KP_Next, KF_MVRG_3, NULL); - setkeybinding(MODKEY_SHIFT, XK_KP_Left, KF_MVRG_4, NULL); - setkeybinding(MODKEY_SHIFT, XK_KP_Begin, KF_MVRG_5, NULL); - setkeybinding(MODKEY_SHIFT, XK_KP_Right, KF_MVRG_6, NULL); - setkeybinding(MODKEY_SHIFT, XK_KP_Home, KF_MVRG_7, NULL); - setkeybinding(MODKEY_SHIFT, XK_KP_Up, KF_MVRG_8, NULL); - setkeybinding(MODKEY_SHIFT, XK_KP_Prior, KF_MVRG_9, NULL); - setkeybinding(MODKEY_SHIFT, XK_1, KF_MVWS_1, NULL); - setkeybinding(MODKEY_SHIFT, XK_2, KF_MVWS_2, NULL); - setkeybinding(MODKEY_SHIFT, XK_3, KF_MVWS_3, NULL); - setkeybinding(MODKEY_SHIFT, XK_4, KF_MVWS_4, NULL); - setkeybinding(MODKEY_SHIFT, XK_5, KF_MVWS_5, NULL); - setkeybinding(MODKEY_SHIFT, XK_6, KF_MVWS_6, NULL); - setkeybinding(MODKEY_SHIFT, XK_7, KF_MVWS_7, NULL); - setkeybinding(MODKEY_SHIFT, XK_8, KF_MVWS_8, NULL); - setkeybinding(MODKEY_SHIFT, XK_9, KF_MVWS_9, NULL); - setkeybinding(MODKEY_SHIFT, XK_0, KF_MVWS_10, NULL); - setkeybinding(MODKEY_SHIFT, XK_F1, KF_MVWS_11, NULL); - setkeybinding(MODKEY_SHIFT, XK_F2, KF_MVWS_12, NULL); - setkeybinding(MODKEY_SHIFT, XK_F3, KF_MVWS_13, NULL); - setkeybinding(MODKEY_SHIFT, XK_F4, KF_MVWS_14, NULL); - setkeybinding(MODKEY_SHIFT, XK_F5, KF_MVWS_15, NULL); - setkeybinding(MODKEY_SHIFT, XK_F6, KF_MVWS_16, NULL); - setkeybinding(MODKEY_SHIFT, XK_F7, KF_MVWS_17, NULL); - setkeybinding(MODKEY_SHIFT, XK_F8, KF_MVWS_18, NULL); - setkeybinding(MODKEY_SHIFT, XK_F9, KF_MVWS_19, NULL); - setkeybinding(MODKEY_SHIFT, XK_F10, KF_MVWS_20, NULL); - setkeybinding(MODKEY_SHIFT, XK_F11, KF_MVWS_21, NULL); - setkeybinding(MODKEY_SHIFT, XK_F12, KF_MVWS_22, NULL); - setkeybinding(MODKEY_SHIFT, XK_slash, KF_NAME_WORKSPACE,NULL); - setkeybinding(MODKEY_SHIFT, XK_q, KF_QUIT, NULL); - setkeybinding(MODKEY_SHIFT, XK_r, KF_RAISE_TOGGLE,NULL); - setkeybinding(MODKEY, XK_q, KF_RESTART, NULL); - setkeybinding(MODKEY, XK_KP_End, KF_RG_1, NULL); - setkeybinding(MODKEY, XK_KP_Down, KF_RG_2, NULL); - setkeybinding(MODKEY, XK_KP_Next, KF_RG_3, NULL); - setkeybinding(MODKEY, XK_KP_Left, KF_RG_4, NULL); - setkeybinding(MODKEY, XK_KP_Begin, KF_RG_5, NULL); - setkeybinding(MODKEY, XK_KP_Right, KF_RG_6, NULL); - setkeybinding(MODKEY, XK_KP_Home, KF_RG_7, NULL); - setkeybinding(MODKEY, XK_KP_Up, KF_RG_8, NULL); - setkeybinding(MODKEY, XK_KP_Prior, KF_RG_9, NULL); - setkeybinding(MODKEY_SHIFT, XK_Right, KF_RG_NEXT, NULL); - setkeybinding(MODKEY_SHIFT, XK_Left, KF_RG_PREV, NULL); - setkeybinding(MODKEY, XK_f, KF_SEARCH_WIN, NULL); - setkeybinding(MODKEY, XK_slash, KF_SEARCH_WORKSPACE,NULL); - setkeybinding(MODKEY_SHIFT, XK_i, KF_SPAWN_CUSTOM,"initscr"); - setkeybinding(MODKEY_SHIFT, XK_Delete, KF_SPAWN_CUSTOM,"lock"); - setkeybinding(MODKEY, XK_p, KF_SPAWN_CUSTOM,"menu"); - setkeybinding(MODKEY, XK_s, KF_SPAWN_CUSTOM,"screenshot_all"); - setkeybinding(MODKEY_SHIFT, XK_s, KF_SPAWN_CUSTOM,"screenshot_wind"); - setkeybinding(MODKEY_SHIFT, XK_Return, KF_SPAWN_CUSTOM,"term"); - setkeybinding(MODKEY_SHIFT, XK_comma, KF_STACK_INC, NULL); - setkeybinding(MODKEY_SHIFT, XK_period, KF_STACK_DEC, NULL); - setkeybinding(MODKEY_SHIFT, XK_space, KF_STACK_RESET, NULL); - setkeybinding(MODKEY, XK_Return, KF_SWAP_MAIN, NULL); - setkeybinding(MODKEY_SHIFT, XK_j, KF_SWAP_NEXT, NULL); - setkeybinding(MODKEY_SHIFT, XK_k, KF_SWAP_PREV, NULL); - setkeybinding(MODKEY_SHIFT, XK_w, KF_UNICONIFY, NULL); - setkeybinding(MODKEY_SHIFT, XK_v, KF_VERSION, NULL); - setkeybinding(MODKEY, XK_equal, KF_WIDTH_GROW, NULL); - setkeybinding(MODKEY, XK_minus, KF_WIDTH_SHRINK,NULL); - setkeybinding(MODKEY, XK_x, KF_WIND_DEL, NULL); - setkeybinding(MODKEY_SHIFT, XK_x, KF_WIND_KILL, NULL); - setkeybinding(MODKEY, XK_1, KF_WS_1, NULL); - setkeybinding(MODKEY, XK_2, KF_WS_2, NULL); - setkeybinding(MODKEY, XK_3, KF_WS_3, NULL); - setkeybinding(MODKEY, XK_4, KF_WS_4, NULL); - setkeybinding(MODKEY, XK_5, KF_WS_5, NULL); - setkeybinding(MODKEY, XK_6, KF_WS_6, NULL); - setkeybinding(MODKEY, XK_7, KF_WS_7, NULL); - setkeybinding(MODKEY, XK_8, KF_WS_8, NULL); - setkeybinding(MODKEY, XK_9, KF_WS_9, NULL); - setkeybinding(MODKEY, XK_0, KF_WS_10, NULL); - setkeybinding(MODKEY, XK_F1, KF_WS_11, NULL); - setkeybinding(MODKEY, XK_F2, KF_WS_12, NULL); - setkeybinding(MODKEY, XK_F3, KF_WS_13, NULL); - setkeybinding(MODKEY, XK_F4, KF_WS_14, NULL); - setkeybinding(MODKEY, XK_F5, KF_WS_15, NULL); - setkeybinding(MODKEY, XK_F6, KF_WS_16, NULL); - setkeybinding(MODKEY, XK_F7, KF_WS_17, NULL); - setkeybinding(MODKEY, XK_F8, KF_WS_18, NULL); - setkeybinding(MODKEY, XK_F9, KF_WS_19, NULL); - setkeybinding(MODKEY, XK_F10, KF_WS_20, NULL); - setkeybinding(MODKEY, XK_F11, KF_WS_21, NULL); - setkeybinding(MODKEY, XK_F12, KF_WS_22, NULL); - setkeybinding(MODKEY, XK_Right, KF_WS_NEXT, NULL); - setkeybinding(MODKEY, XK_Left, KF_WS_PREV, NULL); - setkeybinding(MODKEY, XK_Up, KF_WS_NEXT_ALL, NULL); - setkeybinding(MODKEY, XK_Down, KF_WS_PREV_ALL, NULL); - setkeybinding(MODKEY_SHIFT, XK_Up, KF_WS_NEXT_MOVE,NULL); - setkeybinding(MODKEY_SHIFT, XK_Down, KF_WS_PREV_MOVE,NULL); - setkeybinding(MODKEY, XK_a, KF_WS_PRIOR, NULL); +#define MODSHIFT MODKEY | XCB_MOD_MASK_SHIFT +void +setup_keybindings(void) +{ +#define BINDKEY(m, k, a) setbinding(m, KEYBIND, k, a, 0, NULL) +#define BINDKEYSPAWN(m, k, s) setbinding(m, KEYBIND, k, FN_SPAWN_CUSTOM, 0, s) + BINDKEY(MODKEY, XK_b, FN_BAR_TOGGLE); + BINDKEY(MODSHIFT, XK_b, FN_BAR_TOGGLE_WS); + BINDKEY(MODKEY, XK_b, FN_BAR_TOGGLE); + BINDKEY(MODSHIFT, XK_b, FN_BAR_TOGGLE_WS); + BINDKEY(MODKEY, XK_v, FN_BUTTON2); + BINDKEY(MODKEY, XK_space, FN_CYCLE_LAYOUT); + BINDKEY(MODSHIFT, XK_backslash, FN_FLIP_LAYOUT); + BINDKEY(MODKEY, XK_t, FN_FLOAT_TOGGLE); + BINDKEY(MODKEY, XK_m, FN_FOCUS_MAIN); + BINDKEY(MODKEY, XK_j, FN_FOCUS_NEXT); + BINDKEY(MODKEY, XK_Tab, FN_FOCUS_NEXT); + BINDKEY(MODKEY, XK_k, FN_FOCUS_PREV); + BINDKEY(MODSHIFT, XK_Tab, FN_FOCUS_PREV); + BINDKEY(MODKEY, XK_u, FN_FOCUS_URGENT); + BINDKEY(MODKEY, XK_e, FN_MAXIMIZE_TOGGLE); + BINDKEY(MODSHIFT, XK_equal, FN_HEIGHT_GROW); + BINDKEY(MODSHIFT, XK_minus, FN_HEIGHT_SHRINK); + BINDKEY(MODKEY, XK_w, FN_ICONIFY); + BINDKEY(MODKEY, XK_h, FN_MASTER_SHRINK); + BINDKEY(MODKEY, XK_l, FN_MASTER_GROW); + BINDKEY(MODKEY, XK_comma, FN_MASTER_ADD); + BINDKEY(MODKEY, XK_period, FN_MASTER_DEL); + BINDKEY(MODSHIFT, XK_bracketright, FN_MOVE_DOWN); + BINDKEY(MODKEY, XK_bracketleft, FN_MOVE_LEFT); + BINDKEY(MODKEY, XK_bracketright, FN_MOVE_RIGHT); + BINDKEY(MODSHIFT, XK_bracketleft, FN_MOVE_UP); + BINDKEY(MODSHIFT, XK_KP_End, FN_MVRG_1); + BINDKEY(MODSHIFT, XK_KP_Down, FN_MVRG_2); + BINDKEY(MODSHIFT, XK_KP_Next, FN_MVRG_3); + BINDKEY(MODSHIFT, XK_KP_Left, FN_MVRG_4); + BINDKEY(MODSHIFT, XK_KP_Begin, FN_MVRG_5); + BINDKEY(MODSHIFT, XK_KP_Right, FN_MVRG_6); + BINDKEY(MODSHIFT, XK_KP_Home, FN_MVRG_7); + BINDKEY(MODSHIFT, XK_KP_Up, FN_MVRG_8); + BINDKEY(MODSHIFT, XK_KP_Prior, FN_MVRG_9); + BINDKEY(MODSHIFT, XK_1, FN_MVWS_1); + BINDKEY(MODSHIFT, XK_2, FN_MVWS_2); + BINDKEY(MODSHIFT, XK_3, FN_MVWS_3); + BINDKEY(MODSHIFT, XK_4, FN_MVWS_4); + BINDKEY(MODSHIFT, XK_5, FN_MVWS_5); + BINDKEY(MODSHIFT, XK_6, FN_MVWS_6); + BINDKEY(MODSHIFT, XK_7, FN_MVWS_7); + BINDKEY(MODSHIFT, XK_8, FN_MVWS_8); + BINDKEY(MODSHIFT, XK_9, FN_MVWS_9); + BINDKEY(MODSHIFT, XK_0, FN_MVWS_10); + BINDKEY(MODSHIFT, XK_F1, FN_MVWS_11); + BINDKEY(MODSHIFT, XK_F2, FN_MVWS_12); + BINDKEY(MODSHIFT, XK_F3, FN_MVWS_13); + BINDKEY(MODSHIFT, XK_F4, FN_MVWS_14); + BINDKEY(MODSHIFT, XK_F5, FN_MVWS_15); + BINDKEY(MODSHIFT, XK_F6, FN_MVWS_16); + BINDKEY(MODSHIFT, XK_F7, FN_MVWS_17); + BINDKEY(MODSHIFT, XK_F8, FN_MVWS_18); + BINDKEY(MODSHIFT, XK_F9, FN_MVWS_19); + BINDKEY(MODSHIFT, XK_F10, FN_MVWS_20); + BINDKEY(MODSHIFT, XK_F11, FN_MVWS_21); + BINDKEY(MODSHIFT, XK_F12, FN_MVWS_22); + BINDKEY(MODSHIFT, XK_slash, FN_NAME_WORKSPACE); + BINDKEY(MODSHIFT, XK_q, FN_QUIT); + BINDKEY(MODSHIFT, XK_r, FN_RAISE_TOGGLE); + BINDKEY(MODKEY, XK_q, FN_RESTART); + BINDKEY(MODKEY, XK_KP_End, FN_RG_1); + BINDKEY(MODKEY, XK_KP_Down, FN_RG_2); + BINDKEY(MODKEY, XK_KP_Next, FN_RG_3); + BINDKEY(MODKEY, XK_KP_Left, FN_RG_4); + BINDKEY(MODKEY, XK_KP_Begin, FN_RG_5); + BINDKEY(MODKEY, XK_KP_Right, FN_RG_6); + BINDKEY(MODKEY, XK_KP_Home, FN_RG_7); + BINDKEY(MODKEY, XK_KP_Up, FN_RG_8); + BINDKEY(MODKEY, XK_KP_Prior, FN_RG_9); + BINDKEY(MODSHIFT, XK_Right, FN_RG_NEXT); + BINDKEY(MODSHIFT, XK_Left, FN_RG_PREV); + BINDKEY(MODKEY, XK_f, FN_SEARCH_WIN); + BINDKEY(MODKEY, XK_slash, FN_SEARCH_WORKSPACE); + BINDKEYSPAWN(MODSHIFT, XK_i, "initscr"); + BINDKEYSPAWN(MODSHIFT, XK_Delete, "lock"); + BINDKEYSPAWN(MODKEY, XK_p, "menu"); + BINDKEYSPAWN(MODKEY, XK_s, "screenshot_all"); + BINDKEYSPAWN(MODSHIFT, XK_s, "screenshot_wind"); + BINDKEYSPAWN(MODSHIFT, XK_Return, "term"); + BINDKEY(MODSHIFT, XK_comma, FN_STACK_INC); + BINDKEY(MODSHIFT, XK_period, FN_STACK_DEC); + BINDKEY(MODSHIFT, XK_space, FN_STACK_RESET); + BINDKEY(MODKEY, XK_Return, FN_SWAP_MAIN); + BINDKEY(MODSHIFT, XK_j, FN_SWAP_NEXT); + BINDKEY(MODSHIFT, XK_k, FN_SWAP_PREV); + BINDKEY(MODSHIFT, XK_w, FN_UNICONIFY); + BINDKEY(MODSHIFT, XK_v, FN_VERSION); + BINDKEY(MODKEY, XK_equal, FN_WIDTH_GROW); + BINDKEY(MODKEY, XK_minus, FN_WIDTH_SHRINK); + BINDKEY(MODKEY, XK_x, FN_WIND_DEL); + BINDKEY(MODSHIFT, XK_x, FN_WIND_KILL); + BINDKEY(MODKEY, XK_1, FN_WS_1); + BINDKEY(MODKEY, XK_2, FN_WS_2); + BINDKEY(MODKEY, XK_3, FN_WS_3); + BINDKEY(MODKEY, XK_4, FN_WS_4); + BINDKEY(MODKEY, XK_5, FN_WS_5); + BINDKEY(MODKEY, XK_6, FN_WS_6); + BINDKEY(MODKEY, XK_7, FN_WS_7); + BINDKEY(MODKEY, XK_8, FN_WS_8); + BINDKEY(MODKEY, XK_9, FN_WS_9); + BINDKEY(MODKEY, XK_0, FN_WS_10); + BINDKEY(MODKEY, XK_F1, FN_WS_11); + BINDKEY(MODKEY, XK_F2, FN_WS_12); + BINDKEY(MODKEY, XK_F3, FN_WS_13); + BINDKEY(MODKEY, XK_F4, FN_WS_14); + BINDKEY(MODKEY, XK_F5, FN_WS_15); + BINDKEY(MODKEY, XK_F6, FN_WS_16); + BINDKEY(MODKEY, XK_F7, FN_WS_17); + BINDKEY(MODKEY, XK_F8, FN_WS_18); + BINDKEY(MODKEY, XK_F9, FN_WS_19); + BINDKEY(MODKEY, XK_F10, FN_WS_20); + BINDKEY(MODKEY, XK_F11, FN_WS_21); + BINDKEY(MODKEY, XK_F12, FN_WS_22); + BINDKEY(MODKEY, XK_Right, FN_WS_NEXT); + BINDKEY(MODKEY, XK_Left, FN_WS_PREV); + BINDKEY(MODKEY, XK_Up, FN_WS_NEXT_ALL); + BINDKEY(MODKEY, XK_Down, FN_WS_PREV_ALL); + BINDKEY(MODSHIFT, XK_Up, FN_WS_NEXT_MOVE); + BINDKEY(MODSHIFT, XK_Down, FN_WS_PREV_MOVE); + BINDKEY(MODKEY, XK_a, FN_WS_PRIOR); #ifdef SWM_DEBUG - setkeybinding(MODKEY_SHIFT, XK_d, KF_DUMPWINS, NULL); + BINDKEY(MODSHIFT, XK_d, FN_DUMPWINS); #endif -#undef MODKEY_SHIFT +#undef BINDKEY +#undef BINDKEYSPAWN } void -clear_keys(void) +setup_btnbindings(void) { - struct key *kp; + setbinding(ANYMOD, BTNBIND, XCB_BUTTON_INDEX_1, FN_FOCUS, + BINDING_F_REPLAY, NULL); + setbinding(MODKEY, BTNBIND, XCB_BUTTON_INDEX_3, FN_RESIZE, 0, NULL); + setbinding(MODSHIFT, BTNBIND, XCB_BUTTON_INDEX_3, FN_RESIZE_CENTERED, 0, + NULL); + setbinding(MODKEY, BTNBIND, XCB_BUTTON_INDEX_1, FN_MOVE, 0, NULL); +} +#undef MODSHIFT - while (RB_EMPTY(&keys) == 0) { - kp = RB_ROOT(&keys); - key_remove(kp); +void +clear_bindings(void) +{ + struct binding *bp; + + while ((bp = RB_ROOT(&bindings))) + binding_remove(bp); +} + +void +clear_keybindings(void) +{ + struct binding *bp, *bptmp; + + RB_FOREACH_SAFE(bp, binding_tree, &bindings, bptmp) { + if (bp->type != KEYBIND) + continue; + binding_remove(bp); } } @@ -7649,11 +7899,11 @@ setkeymapping(const char *selector, const char *value, int flags) keymapping_file = expand_tilde(value); - clear_keys(); + clear_keybindings(); /* load new key bindings; if it fails, revert to default bindings */ if (conf_load(keymapping_file, SWM_CONF_KEYMAPPING)) { - clear_keys(); - setup_keys(); + clear_keybindings(); + setup_keybindings(); } free(keymapping_file); @@ -7697,9 +7947,9 @@ updatenumlockmask(void) void grabkeys(void) { - struct key *kp; + struct binding *bp; int num_screens, k, j; - unsigned int modifiers[4]; + uint16_t modifiers[4]; xcb_keycode_t *code; DNPRINTF(SWM_D_MISC, "grabkeys\n"); @@ -7716,38 +7966,63 @@ grabkeys(void) continue; xcb_ungrab_key(conn, XCB_GRAB_ANY, screens[k].root, XCB_MOD_MASK_ANY); - RB_FOREACH(kp, key_tree, &keys) { + RB_FOREACH(bp, binding_tree, &bindings) { + if (bp->type != KEYBIND) + continue; + + /* If there is a catch-all, only bind that. */ + if ((binding_lookup(ANYMOD, KEYBIND, bp->value)) && + bp->mod != ANYMOD) + continue; + /* Skip unused ws binds. */ - if ((int)kp->funcid > KF_WS_1 + workspace_limit - 1 && - kp->funcid <= KF_WS_22) + if ((int)bp->action > FN_WS_1 + workspace_limit - 1 && + bp->action <= FN_WS_22) continue; /* Skip unused mvws binds. */ - if ((int)kp->funcid > KF_MVWS_1 + workspace_limit - 1 && - kp->funcid <= KF_MVWS_22) + if ((int)bp->action > FN_MVWS_1 + workspace_limit - 1 && + bp->action <= FN_MVWS_22) continue; if ((code = xcb_key_symbols_get_keycode(syms, - kp->keysym))) { - for (j = 0; j < LENGTH(modifiers); j++) + bp->value)) == NULL) + continue; + + if (bp->mod == XCB_MOD_MASK_ANY) { + /* All modifiers are grabbed in one pass. */ + DNPRINTF(SWM_D_MOUSE, "grabkeys: grab, " + "key: %u, modifiers: %d\n", bp->value, + bp->mod); + xcb_grab_key(conn, 1, screens[k].root, + bp->mod, *code, XCB_GRAB_MODE_ASYNC, + XCB_GRAB_MODE_SYNC); + } else { + /* Need to grab each modifier permutation. */ + for (j = 0; j < LENGTH(modifiers); j++) { + DNPRINTF(SWM_D_MOUSE, "grabkeys: grab, " + "key: %u, modifiers: %d\n", + bp->value, bp->mod); xcb_grab_key(conn, 1, screens[k].root, - kp->mod | modifiers[j], - *code, XCB_GRAB_MODE_SYNC, + bp->mod | modifiers[j], + *code, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_SYNC); - free(code); + } } + free(code); } } } void -grabbuttons(struct ws_win *win) +grabbuttons(void) { - unsigned int modifiers[4]; - int i, j; + struct binding *bp; + int num_screens, i, k; + uint16_t modifiers[4]; - DNPRINTF(SWM_D_MOUSE, "grabbuttons: win %#x\n", win->id); + DNPRINTF(SWM_D_MOUSE, "grabbuttons\n"); updatenumlockmask(); modifiers[0] = 0; @@ -7755,14 +8030,57 @@ grabbuttons(struct ws_win *win) modifiers[2] = XCB_MOD_MASK_LOCK; modifiers[3] = numlockmask | XCB_MOD_MASK_LOCK; - for (i = 0; i < LENGTH(buttons); i++) - if (buttons[i].action == client_click) - for (j = 0; j < LENGTH(modifiers); ++j) - xcb_grab_button(conn, 0, win->id, BUTTONMASK, - XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, - XCB_WINDOW_NONE, XCB_CURSOR_NONE, - buttons[i].button, buttons[i].mask | - modifiers[j]); + num_screens = get_screen_count(); + for (k = 0; k < num_screens; k++) { + if (TAILQ_EMPTY(&screens[k].rl)) + continue; + xcb_ungrab_button(conn, XCB_BUTTON_INDEX_ANY, screens[k].root, + XCB_MOD_MASK_ANY); + RB_FOREACH(bp, binding_tree, &bindings) { + if (bp->type != BTNBIND) + continue; + + /* If there is a catch-all, only bind that. */ + if ((binding_lookup(ANYMOD, BTNBIND, bp->value)) && + bp->mod != ANYMOD) + continue; + + /* Skip unused ws binds. */ + if ((int)bp->action > FN_WS_1 + workspace_limit - 1 && + bp->action <= FN_WS_22) + continue; + + /* Skip unused mvws binds. */ + if ((int)bp->action > FN_MVWS_1 + workspace_limit - 1 && + bp->action <= FN_MVWS_22) + continue; + + if (bp->mod == XCB_MOD_MASK_ANY) { + /* All modifiers are grabbed in one pass. */ + DNPRINTF(SWM_D_MOUSE, "grabbuttons: grab, " + "button: %u, modifiers: %d\n", bp->value, + bp->mod); + xcb_grab_button(conn, 0, screens[k].root, + BUTTONMASK, XCB_GRAB_MODE_SYNC, + XCB_GRAB_MODE_ASYNC, XCB_WINDOW_NONE, + XCB_CURSOR_NONE, bp->value, bp->mod); + } else { + /* Need to grab each modifier permutation. */ + for (i = 0; i < LENGTH(modifiers); ++i) { + DNPRINTF(SWM_D_MOUSE, "grabbuttons: " + "grab, button: %u, modifiers: %u\n", + bp->value, bp->mod | modifiers[i]); + xcb_grab_button(conn, 0, + screens[k].root, BUTTONMASK, + XCB_GRAB_MODE_SYNC, + XCB_GRAB_MODE_ASYNC, + XCB_WINDOW_NONE, + XCB_CURSOR_NONE, bp->value, + bp->mod | modifiers[i]); + } + } + } + } } const char *quirkname[] = { @@ -7793,10 +8111,9 @@ parsequirks(const char *qstr, uint32_t *quirk, int *ws) if (quirk == NULL || qstr == NULL) return (1); - if ((str = strdup(qstr)) == NULL) + if ((cp = str = strdup(qstr)) == NULL) err(1, "parsequirks: strdup"); - cp = str; *quirk = 0; while ((name = strsep(&cp, SWM_Q_DELIM)) != NULL) { if (cp) @@ -8132,7 +8449,7 @@ setconfvalue(const char *selector, const char *value, int flags) { struct workspace *ws; int i, ws_id, num_screens; - char *b, *str, s[1024]; + char *b, *str, *sp, s[1024]; switch (flags) { case SWM_S_BAR_ACTION: @@ -8177,11 +8494,11 @@ setconfvalue(const char *selector, const char *value, int flags) if (!bar_font_legacy) break; - if ((str = strdup(value)) == NULL) + if ((sp = str = strdup(value)) == NULL) err(1, "setconfvalue: strdup"); /* If there are any non-XLFD entries, switch to Xft mode. */ - while ((b = strsep(&str, ",")) != NULL) { + while ((b = strsep(&sp, ",")) != NULL) { if (*b == '\0') continue; if (!isxlfd(b)) { @@ -8476,7 +8793,7 @@ setautorun(const char *selector, const char *value, int flags) { int ws_id; char s[1024]; - char *ap, *sp; + char *ap, *sp, *str; union arg a; int argc = 0; pid_t pid; @@ -8496,7 +8813,7 @@ setautorun(const char *selector, const char *value, int flags) if (ws_id < 0 || ws_id >= workspace_limit) errx(1, "autorun: invalid workspace %d", ws_id + 1); - sp = expand_tilde((char *)&s); + sp = str = expand_tilde((char *)&s); /* * This is a little intricate @@ -8515,7 +8832,7 @@ setautorun(const char *selector, const char *value, int flags) err(1, "setautorun: realloc"); a.argv[argc - 1] = ap; } - free(sp); + free(str); if ((a.argv = realloc(a.argv, (argc + 1) * sizeof(char *))) == NULL) err(1, "setautorun: realloc"); @@ -8834,6 +9151,7 @@ conf_load(const char *filename, int keymapping) if (line) free(line); fclose(config); + DNPRINTF(SWM_D_CONF, "conf_load: end\n"); return (0); @@ -9091,6 +9409,11 @@ manage_window(xcb_window_t id, int spawn_pos, bool mapped) /* Get WM_PROTOCOLS. */ get_wm_protocols(win); +#ifdef SWM_DEBUG + /* Must be after getting WM_HINTS and WM_PROTOCOLS. */ + print_win_input_model(win); +#endif + /* Set initial quirks based on EWMH. */ ewmh_autoquirk(win); @@ -9218,8 +9541,6 @@ out: /* Set initial _NET_WM_ALLOWED_ACTIONS */ ewmh_update_actions(win); - grabbuttons(win); - DNPRINTF(SWM_D_MISC, "manage_window: done. win %#x, (x,y) w x h: " "(%d,%d) %d x %d, ws: %d, iconic: %s, transient: %#x\n", win->id, X(win), Y(win), WIDTH(win), HEIGHT(win), win->ws->idx, @@ -9231,17 +9552,13 @@ out: void free_window(struct ws_win *win) { - DNPRINTF(SWM_D_MISC, "free_window: win %#x\n", win->id); + DNPRINTF(SWM_D_MISC, "free_window: win %#x\n", WINID(win)); if (win == NULL) return; - TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry); - xcb_icccm_get_wm_class_reply_wipe(&win->ch); - kill_refs(win); - /* paint memory */ memset(win, 0xff, sizeof *win); /* XXX kill later */ @@ -9254,11 +9571,11 @@ unmanage_window(struct ws_win *win) { struct ws_win *parent; + DNPRINTF(SWM_D_MISC, "unmanage_window: win %#x\n", WINID(win)); + if (win == NULL) return; - DNPRINTF(SWM_D_MISC, "unmanage_window: win %#x\n", win->id); - if (TRANS(win)) { parent = find_window(win->transient); if (parent) @@ -9310,8 +9627,12 @@ focusout(xcb_focus_out_event_t *e) void keypress(xcb_key_press_event_t *e) { + struct action *ap; + struct binding *bp; xcb_keysym_t keysym; - struct key *kp; + bool replay = true; + + last_event_time = e->time; keysym = xcb_key_press_lookup_keysym(syms, e, 0); @@ -9321,33 +9642,85 @@ keypress(xcb_key_press_event_t *e) e->event_y, e->detail, e->time, e->root, e->root_x, e->root_y, e->child, e->state, YESNO(e->same_screen)); - if ((kp = key_lookup(CLEANMASK(e->state), keysym)) == NULL) + bp = binding_lookup(CLEANMASK(e->state), KEYBIND, keysym); + if (bp == NULL) { + /* Look for catch-all. */ + if ((bp = binding_lookup(ANYMOD, KEYBIND, keysym)) == NULL) + goto out; + } + + replay = bp->flags & BINDING_F_REPLAY; + + if ((ap = &actions[bp->action]) == NULL) goto out; - last_event_time = e->time; + if (bp->action == FN_SPAWN_CUSTOM) + spawn_custom(root_to_region(e->root, SWM_CK_ALL), &ap->args, + bp->spawn_name); + else if (ap->func) + ap->func(bp, root_to_region(e->root, SWM_CK_ALL), &ap->args); - if (kp->funcid == KF_SPAWN_CUSTOM) - spawn_custom(root_to_region(e->root, SWM_CK_ALL), - &(keyfuncs[kp->funcid].args), kp->spawn_name); - else if (keyfuncs[kp->funcid].func) - keyfuncs[kp->funcid].func(root_to_region(e->root, SWM_CK_ALL), - &(keyfuncs[kp->funcid].args)); + replay = replay && !(ap->flags & FN_F_NOREPLAY); out: - /* Unfreeze grab events. */ - xcb_allow_events(conn, XCB_ALLOW_ASYNC_KEYBOARD, e->time); + if (replay) { + DNPRINTF(SWM_D_EVENT, "keypress: replaying.\n"); + /* Pass keypress to event window and unfreeze keyboard queue. */ + xcb_allow_events(conn, XCB_ALLOW_REPLAY_KEYBOARD, e->time); + } else { + /* Release freeze. */ + xcb_allow_events(conn, XCB_ALLOW_SYNC_KEYBOARD, e->time); + } xcb_flush(conn); DNPRINTF(SWM_D_EVENT, "keypress: done.\n"); } +void +keyrelease(xcb_key_release_event_t *e) +{ + struct action *ap; + struct binding *bp; + xcb_keysym_t keysym; + + last_event_time = e->time; + + DNPRINTF(SWM_D_EVENT, "keyrelease: keysym: %u, win (x,y): %#x (%d,%d), " + "detail: %u, time: %u, root (x,y): %#x (%d,%d), child: %#x, " + "state: %u, same_screen: %s\n", keysym, e->event, e->event_x, + e->event_y, e->detail, e->time, e->root, e->root_x, e->root_y, + e->child, e->state, YESNO(e->same_screen)); + + keysym = xcb_key_release_lookup_keysym(syms, e, 0); + + bp = binding_lookup(CLEANMASK(e->state), KEYBIND, keysym); + if (bp == NULL) + /* Look for catch-all. */ + bp = binding_lookup(ANYMOD, KEYBIND, keysym); + + if (bp && (ap = &actions[bp->action]) && !(ap->flags & FN_F_NOREPLAY) && + bp->flags & BINDING_F_REPLAY) { + xcb_allow_events(conn, XCB_ALLOW_REPLAY_KEYBOARD, e->time); + DNPRINTF(SWM_D_EVENT, "keyrelease: replaying.\n"); + } else { + xcb_allow_events(conn, XCB_ALLOW_SYNC_KEYBOARD, e->time); + } + + xcb_flush(conn); + + DNPRINTF(SWM_D_EVENT, "keyrelease: done.\n"); +} + void buttonpress(xcb_button_press_event_t *e) { struct ws_win *win = NULL; struct swm_region *r, *old_r; - int i; - bool handled = false; + struct action *ap; + struct binding *bp; + bool replay = true; + + last_event_time = e->time; DNPRINTF(SWM_D_EVENT, "buttonpress: win (x,y): %#x (%d,%d), " "detail: %u, time: %u, root (x,y): %#x (%d,%d), child: %#x, " @@ -9356,22 +9729,13 @@ buttonpress(xcb_button_press_event_t *e) e->state, YESNO(e->same_screen)); if (e->event == e->root) { - if (e->child != 0) { + if (e->child) { win = find_window(e->child); - /* Pass ButtonPress to window if it isn't managed. */ - if (win == NULL) - goto out; } else { /* Focus on empty region */ /* If no windows on region if its empty. */ r = root_to_region(e->root, SWM_CK_POINTER); - if (r == NULL) { - DNPRINTF(SWM_D_EVENT, "buttonpress: " - "NULL region; ignoring.\n"); - goto out; - } - - if (TAILQ_EMPTY(&r->ws->winlist)) { + if (r && TAILQ_EMPTY(&r->ws->winlist)) { old_r = root_to_region(e->root, SWM_CK_FOCUS); if (old_r && old_r != r) unfocus_win(old_r->ws->focus); @@ -9382,36 +9746,45 @@ buttonpress(xcb_button_press_event_t *e) /* Clear bar since empty. */ bar_draw(); - handled = true; - goto out; + /* No need to replay event. */ + replay = false; } } } else { win = find_window(e->event); } - if (win == NULL) - goto out; + if (win) + focus_win(get_focus_magic(win)); - last_event_time = e->time; + /* Handle any bound action. */ + bp = binding_lookup(CLEANMASK(e->state), BTNBIND, e->detail); + if (bp == NULL) { + /* Look for catch-all. */ + if ((bp = binding_lookup(ANYMOD, BTNBIND, e->detail)) == NULL) + goto out; - focus_win(get_focus_magic(win)); + } - for (i = 0; i < LENGTH(buttons); i++) - if (client_click == buttons[i].action && buttons[i].func && - buttons[i].button == e->detail && - CLEANMASK(buttons[i].mask) == CLEANMASK(e->state)) { - buttons[i].func(win, &buttons[i].args); - handled = true; - } + replay = bp->flags & BINDING_F_REPLAY; + + if ((ap = &actions[bp->action]) == NULL) + goto out; + + if (bp->action == FN_SPAWN_CUSTOM) + spawn_custom(root_to_region(e->root, SWM_CK_ALL), &ap->args, + bp->spawn_name); + else if (ap->func) + ap->func(bp, root_to_region(e->root, SWM_CK_ALL), &ap->args); + + replay = replay && !(ap->flags & FN_F_NOREPLAY); out: - if (!handled) { - DNPRINTF(SWM_D_EVENT, "buttonpress: passing to window.\n"); + if (replay) { + DNPRINTF(SWM_D_EVENT, "buttonpress: replaying.\n"); /* Replay event to event window */ xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, e->time); } else { - DNPRINTF(SWM_D_EVENT, "buttonpress: handled.\n"); /* Unfreeze grab events. */ xcb_allow_events(conn, XCB_ALLOW_SYNC_POINTER, e->time); } @@ -9419,7 +9792,58 @@ out: xcb_flush(conn); } +void +buttonrelease(xcb_button_release_event_t *e) +{ + struct action *ap; + struct binding *bp; + + last_event_time = e->time; + + DNPRINTF(SWM_D_EVENT, "buttonrelease: win (x,y): %#x (%d,%d), " + "detail: %u, time: %u, root (x,y): %#x (%d,%d), child: %#x, " + "state: %u, same_screen: %s\n", e->event, e->event_x, e->event_y, + e->detail, e->time, e->root, e->root_x, e->root_y, e->child, + e->state, YESNO(e->same_screen)); + + bp = binding_lookup(CLEANMASK(e->state), BTNBIND, e->detail); + if (bp == NULL) + /* Look for catch-all. */ + bp = binding_lookup(ANYMOD, BTNBIND, e->detail); + + if (bp && (ap = &actions[bp->action]) && !(ap->flags & FN_F_NOREPLAY) && + bp->flags & BINDING_F_REPLAY) { + DNPRINTF(SWM_D_EVENT, "buttonrelease: replaying.\n"); + xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, e->time); + } else { + xcb_allow_events(conn, XCB_ALLOW_SYNC_POINTER, e->time); + } + + xcb_flush(conn); +} + #ifdef SWM_DEBUG +void +print_win_input_model(struct ws_win *win) +{ + char *inputmodel; + /* + * Input Model Input Field WM_TAKE_FOCUS + * No Input False Absent + * Passive True Absent + * Locally Active True Present + * Globally Active False Present + */ + + if (!(win->hints.flags & XCB_ICCCM_WM_HINT_INPUT) || win->hints.input) + inputmodel = (win->take_focus) ? "Locally Active" : "Passive"; + else + inputmodel = (win->take_focus) ? "Globally Active" : "No Input"; + + DNPRINTF(SWM_D_FOCUS, "print_win_input_model: win %#x, model: %s\n", + win->id, inputmodel); +} + void print_win_geom(xcb_window_t w) { @@ -9631,35 +10055,41 @@ destroynotify(xcb_destroy_notify_event_t *e) DNPRINTF(SWM_D_EVENT, "destroynotify: win %#x\n", e->window); - if ((win = find_window(e->window)) == NULL) { - if ((win = find_unmanaged_window(e->window)) == NULL) - return; - free_window(win); - return; - } - - if (focus_mode != SWM_FOCUS_FOLLOW) { - /* If we were focused, make sure we focus on something else. */ - if (win == win->ws->focus) - win->ws->focus_pending = get_focus_prev(win); - } + if ((win = find_window(e->window))) { + /* Managed window cleanup. */ + if (focus_mode != SWM_FOCUS_FOLLOW) { + /* If focused, focus on something else. */ + if (win == win->ws->focus) + win->ws->focus_pending = get_focus_prev(win); + } - unmanage_window(win); - stack(); + unmanage_window(win); + stack(); - if (focus_mode != SWM_FOCUS_FOLLOW && WS_FOCUSED(win->ws)) { - if (win->ws->focus_pending) { - focus_win(win->ws->focus_pending); - win->ws->focus_pending = NULL; - } else if (win == win->ws->focus) { - xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, - win->ws->r->id, XCB_CURRENT_TIME); + if (focus_mode != SWM_FOCUS_FOLLOW && WS_FOCUSED(win->ws)) { + if (win->ws->focus_pending) { + focus_win(win->ws->focus_pending); + win->ws->focus_pending = NULL; + } else if (win == win->ws->focus) { + xcb_set_input_focus(conn, + XCB_INPUT_FOCUS_PARENT, win->ws->r->id, + XCB_CURRENT_TIME); + } } + + kill_refs(win); + focus_flush(); + } else { + win = find_unmanaged_window(e->window); } - free_window(win); + if (win) { + /* unmanage_window() puts win into unmanaged list. */ + TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry); + free_window(win); + } - focus_flush(); + DNPRINTF(SWM_D_EVENT, "destroynotify: done.\n"); } #ifdef SWM_DEBUG @@ -9724,6 +10154,61 @@ get_notify_mode_label(uint8_t mode) return label; } + +char * +get_state_mask_label(uint16_t state) +{ + char *label; + + switch (state) { + case XCB_KEY_BUT_MASK_SHIFT: + label = "ShiftMask"; + break; + case XCB_KEY_BUT_MASK_LOCK: + label = "LockMask"; + break; + case XCB_KEY_BUT_MASK_CONTROL: + label = "ControlMask"; + break; + case XCB_KEY_BUT_MASK_MOD_1: + label = "Mod1Mask"; + break; + case XCB_KEY_BUT_MASK_MOD_2: + label = "Mod2Mask"; + break; + case XCB_KEY_BUT_MASK_MOD_3: + label = "Mod3Mask"; + break; + case XCB_KEY_BUT_MASK_MOD_4: + label = "Mod4Mask"; + break; + case XCB_KEY_BUT_MASK_MOD_5: + label = "Mod5Mask"; + break; + case XCB_KEY_BUT_MASK_BUTTON_1: + label = "Button1Mask"; + break; + case XCB_KEY_BUT_MASK_BUTTON_2: + label = "Button2Mask"; + break; + case XCB_KEY_BUT_MASK_BUTTON_3: + label = "Button3Mask"; + break; + case XCB_KEY_BUT_MASK_BUTTON_4: + label = "Button4Mask"; + break; + case XCB_KEY_BUT_MASK_BUTTON_5: + label = "Button5Mask"; + break; + case 0: + label = "None"; + break; + default: + label = "Unknown"; + } + + return label; +} #endif void @@ -9732,14 +10217,17 @@ enternotify(xcb_enter_notify_event_t *e) struct ws_win *win; struct swm_region *r; + last_event_time = e->time; + DNPRINTF(SWM_D_FOCUS, "enternotify: time: %u, win (x,y): %#x " "(%d,%d), mode: %s(%d), detail: %s(%d), root (x,y): %#x (%d,%d), " - "child: %#x, same_screen_focus: %s, state: %d\n", + "child: %#x, same_screen_focus: %s, state: %s(%d)\n", e->time, e->event, e->event_x, e->event_y, get_notify_mode_label(e->mode), e->mode, get_notify_detail_label(e->detail), e->detail, e->root, e->root_x, e->root_y, e->child, - YESNO(e->same_screen_focus), e->state); + YESNO(e->same_screen_focus), get_state_mask_label(e->state), + e->state); if (focus_mode == SWM_FOCUS_MANUAL && e->mode == XCB_NOTIFY_MODE_NORMAL) { @@ -9748,13 +10236,12 @@ enternotify(xcb_enter_notify_event_t *e) } if (focus_mode != SWM_FOCUS_FOLLOW && - e->mode == XCB_NOTIFY_MODE_UNGRAB) { + e->mode == XCB_NOTIFY_MODE_UNGRAB && + e->detail != XCB_NOTIFY_DETAIL_ANCESTOR) { DNPRINTF(SWM_D_EVENT, "enternotify: ungrab; ignoring.\n"); return; } - last_event_time = e->time; - if ((win = find_window(e->event)) == NULL) { if (e->event == e->root) { /* If no windows on pointer region, then focus root. */ @@ -9789,14 +10276,17 @@ enternotify(xcb_enter_notify_event_t *e) void leavenotify(xcb_leave_notify_event_t *e) { + last_event_time = e->time; + DNPRINTF(SWM_D_FOCUS, "leavenotify: time: %u, win (x,y): %#x " "(%d,%d), mode: %s(%d), detail: %s(%d), root (x,y): %#x (%d,%d), " - "child: %#x, same_screen_focus: %s, state: %d\n", + "child: %#x, same_screen_focus: %s, state: %s(%d)\n", e->time, e->event, e->event_x, e->event_y, get_notify_mode_label(e->mode), e->mode, get_notify_detail_label(e->detail), e->detail, e->root, e->root_x, e->root_y, e->child, - YESNO(e->same_screen_focus), e->state); + YESNO(e->same_screen_focus), get_state_mask_label(e->state), + e->state); } #endif @@ -9841,22 +10331,12 @@ mapnotify(xcb_map_notify_event_t *e) void mappingnotify(xcb_mapping_notify_event_t *e) { - struct ws_win *w; - int i, j, num_screens; - - xcb_refresh_keyboard_mapping(syms, e); - - if (e->request == XCB_MAPPING_KEYBOARD) { + if (e->request != XCB_MAPPING_POINTER) { + xcb_refresh_keyboard_mapping(syms, e); grabkeys(); - - /* Regrab buttons on all managed windows. */ - num_screens = get_screen_count(); - for (i = 0; i < num_screens; i++) - for (j = 0; j < workspace_limit; j++) - TAILQ_FOREACH(w, &screens[i].ws[j].winlist, - entry) - grabbuttons(w); } + + grabbuttons(); } void @@ -9933,6 +10413,8 @@ motionnotify(xcb_motion_notify_event_t *e) struct swm_region *r = NULL; int i, num_screens; + last_event_time = e->time; + DNPRINTF(SWM_D_FOCUS, "motionnotify: time: %u, win (x,y): %#x " "(%d,%d), detail: %s(%d), root (x,y): %#x (%d,%d), " "child: %#x, same_screen_focus: %s, state: %d\n", @@ -9941,8 +10423,6 @@ motionnotify(xcb_motion_notify_event_t *e) e->root, e->root_x, e->root_y, e->child, YESNO(e->same_screen), e->state); - last_event_time = e->time; - if (focus_mode == SWM_FOCUS_MANUAL) return; @@ -10007,14 +10487,14 @@ propertynotify(xcb_property_notify_event_t *e) e->state); free(name); #endif + last_event_time = e->time; + win = find_window(e->window); if (win == NULL) return; ws = win->ws; - last_event_time = e->time; - if (e->atom == a_state) { /* State just changed, make sure it gets focused if mapped. */ if (e->state == XCB_PROPERTY_NEW_VALUE) { @@ -10149,7 +10629,7 @@ clientmessage(xcb_client_message_event_t *e) if (r && e->data.data32[0] < (uint32_t)workspace_limit) { a.id = e->data.data32[0]; - switchws(r, &a); + switchws(NULL, r, &a); focus_flush(); } @@ -10302,11 +10782,6 @@ enable_wm(void) free(error); return 1; } - - /* click to focus on empty region */ - xcb_grab_button(conn, 1, sc->root, BUTTONMASK, - XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, XCB_WINDOW_NONE, - XCB_CURSOR_NONE, XCB_BUTTON_INDEX_1, XCB_BUTTON_MASK_ANY); } return 0; @@ -10693,8 +11168,7 @@ setup_screens(void) xcb_randr_query_version_reply_t *r; num_screens = get_screen_count(); - if ((screens = calloc(num_screens, - sizeof(struct swm_screen))) == NULL) + if ((screens = calloc(num_screens, sizeof(struct swm_screen))) == NULL) err(1, "setup_screens: calloc: failed to allocate memory for " "screens"); @@ -10807,6 +11281,9 @@ setup_globals(void) void shutdown_cleanup(void) { + struct swm_region *r; + struct ws_win *w; + struct workspace *ws; int i, num_screens; /* disable alarm because the following code may not be interrupted */ @@ -10821,13 +11298,12 @@ shutdown_cleanup(void) clear_quirks(); clear_spawns(); - clear_keys(); + clear_bindings(); teardown_ewmh(); num_screens = get_screen_count(); for (i = 0; i < num_screens; ++i) { - struct swm_region *r; int j; xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, @@ -10846,17 +11322,23 @@ shutdown_cleanup(void) free(screens[i].c[j].name); } + /* Free window memory. */ for (j = 0; j < SWM_WS_MAX; ++j) { - struct ws_win *win; + ws = &screens[i].ws[j]; + free(ws->name); - free(screens[i].ws[j].name); + while ((w = TAILQ_FIRST(&ws->winlist)) != NULL) { + TAILQ_REMOVE(&ws->winlist, w, entry); + free_window(w); + } - while ((win = TAILQ_FIRST(&screens[i].ws[j].winlist)) != NULL) { - TAILQ_REMOVE(&screens[i].ws[j].winlist, win, entry); - free(win); + while ((w = TAILQ_FIRST(&ws->unmanagedlist)) != NULL) { + TAILQ_REMOVE(&ws->unmanagedlist, w, entry); + free_window(w); } } + /* Free region memory. */ while ((r = TAILQ_FIRST(&screens[i].rl)) != NULL) { TAILQ_REMOVE(&screens[i].rl, r, entry); free(r->bar); @@ -10878,7 +11360,7 @@ shutdown_cleanup(void) if (bar_fs) XFreeFontSet(display, bar_fs); - if (bar_font_legacy == false) + if (bar_font) XftFontClose(display, bar_font); xcb_key_symbols_free(syms); @@ -10911,7 +11393,7 @@ event_handle(xcb_generic_event_t *evt) #define EVENT(type, callback) case type: callback((void *)evt); return EVENT(0, event_error); EVENT(XCB_BUTTON_PRESS, buttonpress); - /*EVENT(XCB_BUTTON_RELEASE, buttonpress);*/ + EVENT(XCB_BUTTON_RELEASE, buttonrelease); /*EVENT(XCB_CIRCULATE_NOTIFY, );*/ /*EVENT(XCB_CIRCULATE_REQUEST, );*/ EVENT(XCB_CLIENT_MESSAGE, clientmessage); @@ -10929,7 +11411,7 @@ event_handle(xcb_generic_event_t *evt) /*EVENT(XCB_GRAPHICS_EXPOSURE, );*/ /*EVENT(XCB_GRAVITY_NOTIFY, );*/ EVENT(XCB_KEY_PRESS, keypress); - /*EVENT(XCB_KEY_RELEASE, keypress);*/ + EVENT(XCB_KEY_RELEASE, keyrelease); /*EVENT(XCB_KEYMAP_NOTIFY, );*/ #ifdef SWM_DEBUG EVENT(XCB_LEAVE_NOTIFY, leavenotify); @@ -10956,14 +11438,14 @@ event_handle(xcb_generic_event_t *evt) int main(int argc, char *argv[]) { - struct swm_region *r; - char conf[PATH_MAX], *cfile = NULL; - struct stat sb; - int xfd, i, num_screens; + struct pollfd pfd[2]; struct sigaction sact; + struct stat sb; + struct passwd *pwd; + struct swm_region *r; xcb_generic_event_t *evt; - int num_readable; - struct pollfd pfd[2]; + int xfd, i, num_screens, num_readable; + char conf[PATH_MAX], *cfile = NULL; bool stdin_ready = false, startup = true; /* suppress unused warning since var is needed */ @@ -11013,7 +11495,7 @@ main(int argc, char *argv[]) xcb_aux_sync(conn); /* flush all events */ - while ((evt = xcb_poll_for_event(conn))) { + while ((evt = get_next_event(false))) { if (XCB_EVENT_RESPONSE_TYPE(evt) == 0) event_handle(evt); free(evt); @@ -11030,7 +11512,8 @@ main(int argc, char *argv[]) setup_globals(); setup_screens(); setup_ewmh(); - setup_keys(); + setup_keybindings(); + setup_btnbindings(); setup_quirks(); setup_spawn(); @@ -11089,6 +11572,7 @@ noconfig: grab_windows(); grabkeys(); + grabbuttons(); stack(); bar_draw(); @@ -11107,7 +11591,7 @@ noconfig: pfd[1].events = POLLIN; while (running) { - while ((evt = xcb_poll_for_event(conn))) { + while ((evt = get_next_event(false))) { if (!running) goto done; event_handle(evt); @@ -11130,13 +11614,15 @@ noconfig: num_readable = poll(pfd, bar_extra ? 2 : 1, 1000); if (num_readable == -1) { - DNPRINTF(SWM_D_MISC, "poll failed: %s", strerror(errno)); - } else if (num_readable > 0 && bar_extra && pfd[1].revents & POLLIN) { + DNPRINTF(SWM_D_MISC, "poll failed: %s", + strerror(errno)); + } else if (num_readable > 0 && bar_extra && + pfd[1].revents & POLLIN) { stdin_ready = true; } if (restart_wm) - restart(NULL, NULL); + restart(NULL, NULL, NULL); if (search_resp) search_do_resp();