X-Git-Url: https://code.delx.au/spectrwm/blobdiff_plain/b015891a950f9dd40711d0847f46892855967151..HEAD:/spectrwm.c diff --git a/spectrwm.c b/spectrwm.c index 0589ebd..285bac9 100644 --- a/spectrwm.c +++ b/spectrwm.c @@ -1,14 +1,15 @@ /* - * Copyright (c) 2009-2012 Marco Peereboom + * Copyright (c) 2009-2015 Marco Peereboom * Copyright (c) 2009-2011 Ryan McBride * Copyright (c) 2009 Darrin Chandler * Copyright (c) 2009 Pierre-Yves Ritschard * Copyright (c) 2010 Tuukka Kataja * Copyright (c) 2011 Jason L. Wright - * Copyright (c) 2011-2014 Reginald Kennedy + * Copyright (c) 2011-2016 Reginald Kennedy * Copyright (c) 2011-2012 Lawrence Teo * Copyright (c) 2011-2012 Tiago Cunha - * Copyright (c) 2012-2013 David Hill + * Copyright (c) 2012-2015 David Hill + * Copyright (c) 2014-2015 Yuri D'Elia * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -22,45 +23,17 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* - * Much code and ideas taken from dwm under the following license: - * MIT/X Consortium License - * - * 2006-2008 Anselm R Garbe - * 2006-2007 Sander van Dijk - * 2006-2007 Jukka Salmi - * 2007 Premysl Hruby - * 2007 Szabolcs Nagy - * 2007 Christof Musik - * 2007-2008 Enno Gottox Boland - * 2007-2008 Peter Hartlich - * 2008 Martin Hurton - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ /* kernel includes */ #include #include #include #include +#ifdef __OSX__ +#include "queue.h" +#else #include +#endif #include #include #if defined(__linux__) @@ -77,12 +50,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -94,6 +69,7 @@ #include #include #include +#include #include #include #include @@ -114,9 +90,9 @@ static const char *buildstr = SPECTRWM_BUILDSTR; static const char *buildstr = SPECTRWM_VERSION; #endif -#if !defined(__CYGWIN__) /* cygwin chokes on xrandr stuff */ +#if !defined(__CYGWIN__) /* cygwin chokes on randr stuff */ # if RANDR_MAJOR < 1 -# error XRandR versions less than 1.0 are not supported +# error RandR versions less than 1.0 are not supported #endif # if RANDR_MAJOR >= 1 @@ -232,19 +208,21 @@ 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) & ~(numlockmask)) #define BUTTONMASK (XCB_EVENT_MASK_BUTTON_PRESS | \ XCB_EVENT_MASK_BUTTON_RELEASE) #define MOUSEMASK (BUTTONMASK|XCB_EVENT_MASK_POINTER_MOTION) #define SWM_PROPLEN (16) #define SWM_FUNCNAME_LEN (32) -#define SWM_KEYS_LEN (255) #define SWM_QUIRK_LEN (64) #define X(r) ((r)->g.x) #define Y(r) ((r)->g.y) #define WIDTH(r) ((r)->g.w) #define HEIGHT(r) ((r)->g.h) -#define BORDER(w) ((w)->bordered ? border_width : 0) #define MAX_X(r) ((r)->g.x + (r)->g.w) #define MAX_Y(r) ((r)->g.y + (r)->g.h) #define SH_MIN(w) ((w)->sh.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE) @@ -258,6 +236,8 @@ uint32_t swm_debug = 0 #define SH_INC_H(w) ((w)->sh.height_inc) #define SWM_MAX_FONT_STEPS (3) #define WINID(w) ((w) ? (w)->id : XCB_WINDOW_NONE) +#define ACCEPTS_FOCUS(w) (!((w)->hints.flags & XCB_ICCCM_WM_HINT_INPUT) \ + || ((w)->hints.input)) #define WS_FOCUSED(ws) ((ws)->r && (ws)->r->s->r_focus == (ws)->r) #define YESNO(x) ((x) ? "yes" : "no") #define ICONIC(w) ((w)->ewmh_flags & EWMH_F_HIDDEN) @@ -285,17 +265,12 @@ uint32_t swm_debug = 0 #define SWM_FOCUS_FOLLOW (1) #define SWM_FOCUS_MANUAL (2) -#define SWM_CK_NONE (0) #define SWM_CK_ALL (0xf) #define SWM_CK_FOCUS (0x1) #define SWM_CK_POINTER (0x2) #define SWM_CK_FALLBACK (0x4) #define SWM_CK_REGION (0x8) -#define SWM_G_ALL (0xf) -#define SWM_G_SIZE (0x1) -#define SWM_G_POS (0x2) - #define SWM_CONF_DEFAULT (0) #define SWM_CONF_KEYMAPPING (1) @@ -307,6 +282,7 @@ char **start_argv; xcb_atom_t a_state; xcb_atom_t a_prot; xcb_atom_t a_delete; +xcb_atom_t a_net_frame_extents; xcb_atom_t a_net_wm_check; xcb_atom_t a_net_supported; xcb_atom_t a_takefocus; @@ -316,9 +292,8 @@ volatile sig_atomic_t running = 1; volatile sig_atomic_t restart_wm = 0; xcb_timestamp_t last_event_time = 0; int outputs = 0; -int other_wm; -int xrandr_support; -int xrandr_eventbase; +bool randr_support; +int randr_eventbase; unsigned int numlockmask = 0; Display *display; @@ -326,11 +301,14 @@ xcb_connection_t *conn; xcb_key_symbols_t *syms; int boundary_width = 50; -int cycle_empty = 0; -int cycle_visible = 0; +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; /* dmenu search */ struct swm_region *search_r; @@ -389,44 +367,48 @@ int bar_pipe[2]; char bar_ext[SWM_BAR_MAX]; char bar_ext_buf[SWM_BAR_MAX]; char bar_vertext[SWM_BAR_MAX]; -int bar_version = 0; -int bar_enabled = 1; +bool bar_version = false; +bool bar_enabled = true; int bar_border_width = 1; -int bar_at_bottom = 0; -int bar_extra = 0; -int bar_verbose = 1; +bool bar_at_bottom = false; +bool bar_extra = false; int bar_height = 0; int bar_justify = SWM_BAR_JUSTIFY_LEFT; -char *bar_format = NULL; -int stack_enabled = 1; -int clock_enabled = 1; -int iconic_enabled = 0; -int urgent_enabled = 0; +char *bar_format = NULL; +bool stack_enabled = true; +bool clock_enabled = true; +bool iconic_enabled = false; +bool maximize_hide_bar = false; +bool urgent_enabled = false; +bool urgent_collapse = false; char *clock_format = NULL; -int window_class_enabled = 0; -int window_instance_enabled = 0; -int window_name_enabled = 0; +bool window_class_enabled = false; +bool window_instance_enabled = false; +bool window_name_enabled = false; int focus_mode = SWM_FOCUS_DEFAULT; int focus_close = SWM_STACK_BELOW; -int focus_close_wrap = 1; +bool focus_close_wrap = true; int focus_default = SWM_STACK_TOP; int spawn_position = SWM_STACK_TOP; -int disable_border = 0; +bool disable_border = false; int border_width = 1; int region_padding = 0; int tile_gap = 0; -int java_workaround = 1; -int verbose_layout = 0; +bool java_workaround = true; +bool verbose_layout = false; +#ifdef SWM_DEBUG +bool debug_enabled; time_t time_started; +#endif pid_t bar_pid; -XFontSet bar_fs; +XFontSet bar_fs = NULL; XFontSetExtents *bar_fs_extents; -XftFont *bar_font; -int bar_font_legacy = 1; -char *bar_fonts; +XftFont *bar_font = NULL; +bool bar_font_legacy = true; +char *bar_fonts = NULL; XftColor bar_font_color; -struct passwd *pwd; -char *startup_exception; +XftColor search_font_color; +char *startup_exception = NULL; unsigned int nr_exceptions = 0; /* layout manager data */ @@ -444,6 +426,7 @@ struct swm_bar { xcb_window_t id; xcb_pixmap_t buffer; struct swm_geometry g; + struct swm_region *r; /* Associated region. */ }; /* virtual "screens" */ @@ -458,30 +441,43 @@ struct swm_region { }; TAILQ_HEAD(swm_region_list, swm_region); +enum { + SWM_WIN_STATE_REPARENTING, + SWM_WIN_STATE_REPARENTED, + SWM_WIN_STATE_UNPARENTING, + SWM_WIN_STATE_UNPARENTED, +}; + struct ws_win { TAILQ_ENTRY(ws_win) entry; TAILQ_ENTRY(ws_win) stack_entry; xcb_window_t id; + xcb_window_t frame; xcb_window_t transient; struct ws_win *focus_child; /* focus on child transient */ struct swm_geometry g; /* current geometry */ + struct swm_geometry g_prev; /* prev configured geometry */ struct swm_geometry g_float; /* region coordinates */ - int g_floatvalid; /* g_float geometry validity */ - int mapped; - int bordered; + bool g_floatvalid; /* g_float geometry validity */ + bool mapped; + uint8_t state; + bool bordered; uint32_t ewmh_flags; int font_size_boundary[SWM_MAX_FONT_STEPS]; int font_steps; int last_inc; - int can_delete; - int take_focus; - int java; - unsigned long quirks; + bool can_delete; + bool take_focus; + bool java; + uint32_t quirks; struct workspace *ws; /* always valid */ struct swm_screen *s; /* always valid, never changes */ xcb_size_hints_t sh; xcb_icccm_get_wm_class_reply_t ch; xcb_icccm_wm_hints_t hints; +#ifdef SWM_DEBUG + xcb_window_t debug; +#endif }; TAILQ_HEAD(ws_win_list, ws_win); TAILQ_HEAD(ws_win_stack, ws_win); @@ -496,7 +492,7 @@ TAILQ_HEAD(pid_list, pid_e); struct pid_list pidlist = TAILQ_HEAD_INITIALIZER(pidlist); /* layout handlers */ -void stack(void); +void stack(struct swm_region *); void vertical_config(struct workspace *, int); void vertical_stack(struct workspace *, struct swm_geometry *); void horizontal_config(struct workspace *, int); @@ -533,12 +529,13 @@ struct layout { struct workspace { int idx; /* workspace index */ char *name; /* workspace name */ - int always_raise; /* raise windows on focus */ - int bar_enabled; /* bar visibility */ + bool always_raise; /* raise windows on focus */ + bool bar_enabled; /* bar visibility */ struct layout *cur_layout; /* current layout handlers */ struct ws_win *focus; /* may be NULL */ struct ws_win *focus_prev; /* may be NULL */ struct ws_win *focus_pending; /* may be NULL */ + struct ws_win *focus_raise; /* may be NULL */ struct swm_region *r; /* may be NULL */ struct swm_region *old_r; /* may be NULL */ struct ws_win_list winlist; /* list of windows in ws */ @@ -552,11 +549,11 @@ struct workspace { int horizontal_msize; int horizontal_mwin; int horizontal_stacks; - int horizontal_flip; + bool horizontal_flip; int vertical_msize; int vertical_mwin; int vertical_stacks; - int vertical_flip; + bool vertical_flip; } l_state; }; @@ -620,6 +617,7 @@ union arg { #define SWM_ARG_ID_FLIPLAYOUT (24) #define SWM_ARG_ID_STACKRESET (30) #define SWM_ARG_ID_STACKINIT (31) +#define SWM_ARG_ID_STACKBALANCE (32) #define SWM_ARG_ID_CYCLEWS_UP (40) #define SWM_ARG_ID_CYCLEWS_DOWN (41) #define SWM_ARG_ID_CYCLERG_UP (42) @@ -630,8 +628,6 @@ union arg { #define SWM_ARG_ID_CYCLEWS_MOVE_DOWN (47) #define SWM_ARG_ID_STACKINC (50) #define SWM_ARG_ID_STACKDEC (51) -#define SWM_ARG_ID_SS_ALL (60) -#define SWM_ARG_ID_SS_WINDOW (61) #define SWM_ARG_ID_DONTCENTER (70) #define SWM_ARG_ID_CENTER (71) #define SWM_ARG_ID_KILLWINDOW (80) @@ -644,10 +640,10 @@ union arg { #define SWM_ARG_ID_MOVEDOWN (101) #define SWM_ARG_ID_MOVELEFT (102) #define SWM_ARG_ID_MOVERIGHT (103) -#define SWM_ARG_ID_RAISE (105) -#define SWM_ARG_ID_LOWER (106) #define SWM_ARG_ID_BAR_TOGGLE (110) #define SWM_ARG_ID_BAR_TOGGLE_WS (111) +#define SWM_ARG_ID_CYCLERG_MOVE_UP (112) +#define SWM_ARG_ID_CYCLERG_MOVE_DOWN (113) char **argv; }; @@ -660,16 +656,21 @@ struct quirk { regex_t regex_class; regex_t regex_instance; regex_t regex_name; - unsigned long quirk; -#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 */ -#define SWM_Q_FOCUSPREV (1<<5) /* focus on caller */ + uint32_t quirk; + int ws; /* Initial workspace. */ +#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) /* No border when floating/unfocused. */ }; TAILQ_HEAD(quirk_list, quirk); struct quirk_list quirks = TAILQ_HEAD_INITIALIZER(quirks); @@ -688,6 +689,7 @@ enum { _NET_DESKTOP_VIEWPORT, _NET_MOVERESIZE_WINDOW, _NET_NUMBER_OF_DESKTOPS, + _NET_REQUEST_FRAME_EXTENTS, _NET_RESTACK_WINDOW, _NET_WM_ACTION_ABOVE, _NET_WM_ACTION_CLOSE, @@ -731,6 +733,7 @@ struct ewmh_hint { {"_NET_DESKTOP_VIEWPORT", XCB_ATOM_NONE}, {"_NET_MOVERESIZE_WINDOW", XCB_ATOM_NONE}, {"_NET_NUMBER_OF_DESKTOPS", XCB_ATOM_NONE}, + {"_NET_REQUEST_FRAME_EXTENTS", XCB_ATOM_NONE}, {"_NET_RESTACK_WINDOW", XCB_ATOM_NONE}, {"_NET_WM_ACTION_ABOVE", XCB_ATOM_NONE}, {"_NET_WM_ACTION_CLOSE", XCB_ATOM_NONE}, @@ -805,134 +808,163 @@ 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_NEXT, - KF_RG_PREV, - KF_SCREEN_NEXT, - KF_SCREEN_PREV, - KF_SEARCH_WIN, - KF_SEARCH_WORKSPACE, - KF_SPAWN_CUSTOM, - 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, +}; + +/* 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_FULLSCREEN_TOGGLE, + 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, + KF_MVRG_NEXT, + KF_MVRG_PREV, + 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, + 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, + /* SWM_DEBUG actions MUST be here: */ + FN_DEBUG_TOGGLE, + FN_DUMPWINS, + /* ALWAYS last: */ + FN_INVALID +}; + +enum binding_type { + KEYBIND, + BTNBIND +}; + +enum { + BINDING_F_REPLAY = 0x1, }; -struct key { - RB_ENTRY(key) entry; - unsigned int mod; - KeySym keysym; - enum keyfuncid funcid; - char *spawn_name; +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(key_tree, key); +RB_HEAD(binding_tree, binding); /* function prototypes */ void adjust_font(struct ws_win *); @@ -943,14 +975,14 @@ void bar_extra_stop(void); int bar_extra_update(void); void bar_fmt(const char *, char *, struct swm_region *, size_t); void bar_fmt_expand(char *, size_t); -void bar_draw(void); +void bar_draw(struct swm_bar *); void bar_print(struct swm_region *, const char *); void bar_print_legacy(struct swm_region *, const char *); 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 *); @@ -959,10 +991,20 @@ 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); void clientmessage(xcb_client_message_event_t *); void client_msg(struct ws_win *, xcb_atom_t, xcb_timestamp_t); int conf_load(const char *, int); @@ -970,15 +1012,19 @@ void configurenotify(xcb_configure_notify_event_t *); void configurerequest(xcb_configure_request_event_t *); void config_win(struct ws_win *, xcb_configure_request_event_t *); void constrain_window(struct ws_win *, struct swm_geometry *, int *); -int count_win(struct workspace *, int); +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 *); +#ifdef SWM_DEBUG +void debug_refresh(struct ws_win *); +#endif +void debug_toggle(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); @@ -998,27 +1044,33 @@ void ewmh_update_wm_state(struct ws_win *); char *expand_tilde(const char *); void expose(xcb_expose_event_t *); void fake_keypress(struct ws_win *, xcb_keysym_t, uint16_t); +struct swm_bar *find_bar(xcb_window_t); +struct ws_win *find_frame_window(xcb_window_t); struct pid_e *find_pid(pid_t); +struct swm_region *find_region(xcb_window_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 *); -#ifdef SWM_DEBUG -void focusin(xcb_focus_in_event_t *); -void focusout(xcb_focus_out_event_t *); -#endif +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 focusrg(struct swm_region *, union arg *); void focus_win(struct ws_win *); +void focusin(xcb_focus_in_event_t *); +#ifdef SWM_DEBUG +void focusout(xcb_focus_out_event_t *); +#endif +void focusrg(struct binding *, struct swm_region *, union arg *); void fontset_init(void); void free_window(struct ws_win *); +void fullscreen_toggle(struct binding *, struct swm_region *, union arg *); xcb_atom_t get_atom_from_string(const char *); #ifdef SWM_DEBUG 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); @@ -1031,80 +1083,91 @@ 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 *); +#ifdef SWM_DEBUG +char *get_win_input_model(struct ws_win *); +#endif 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(xcb_window_t); -void grabbuttons(struct ws_win *); -void grabkeys(void); +int get_ws_idx(struct ws_win *); void grab_windows(void); -void iconify(struct swm_region *, union arg *); -int isxlfd(char *); +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 void leavenotify(xcb_leave_notify_event_t *); #endif void load_float_geom(struct ws_win *); -struct ws_win *manage_window(xcb_window_t, int); +void lower_window(struct ws_win *); +struct ws_win *manage_window(xcb_window_t, int, bool); 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 *, unsigned long *); 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); #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 *,unsigned long); +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 *, - unsigned long); -void quit(struct swm_region *, union arg *); -void raise_toggle(struct swm_region *, union arg *); + uint32_t, int); +void quit(struct binding *, struct swm_region *, union arg *); +void raise_focus(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 reparent_window(struct ws_win *); +void reparentnotify(xcb_reparent_notify_event_t *); +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_xrandr(int); +void scan_randr(int); void search_do_resp(void); 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_rg_relative(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); @@ -1112,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 *,unsigned long); +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); @@ -1130,7 +1193,7 @@ void shutdown_cleanup(void); void sighdlr(int); void socket_setnonblock(int); void sort_windows(struct ws_win_list *); -void spawn(int, union arg *, int); +void spawn(int, union arg *, bool); void spawn_custom(struct swm_region *, union arg *, const char *); int spawn_expand(struct swm_region *, union arg *, const char *, char ***); void spawn_insert(const char *, const char *, int); @@ -1138,42 +1201,43 @@ 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_master(struct workspace *, struct swm_geometry *, int, int); +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 unparent_window(struct ws_win *); 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 draw_frame(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 win_to_ws(struct ws_win *, int, int); +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) @@ -1366,9 +1430,9 @@ get_wm_protocols(struct ws_win *win) { &wpr, NULL)) { for (i = 0; i < (int)wpr.atoms_len; i++) { if (wpr.atoms[i] == a_takefocus) - win->take_focus = 1; + win->take_focus = true; if (wpr.atoms[i] == a_delete) - win->can_delete = 1; + win->can_delete = true; } xcb_icccm_get_wm_protocols_reply_wipe(&wpr); } @@ -1398,18 +1462,9 @@ setup_ewmh(void) xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win, a_net_wm_check, XCB_ATOM_WINDOW, 32, 1, &win); - /* - * Impersonate LG3D non-reparenting WM, written by Sun, to - * workaround a Java GUI rendering issue. - */ - if (java_workaround) - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win, - ewmh[_NET_WM_NAME].atom, a_utf8_string, - 8, strlen("LG3D"), "LG3D"); - else - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win, - ewmh[_NET_WM_NAME].atom, a_utf8_string, - 8, strlen("spectrwm"), "spectrwm"); + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win, + ewmh[_NET_WM_NAME].atom, a_utf8_string, + 8, strlen("spectrwm"), "spectrwm"); /* Report supported atoms */ xcb_delete_property(conn, root, a_net_supported); @@ -1639,7 +1694,7 @@ ewmh_apply_flags(struct ws_win *win, uint32_t pending) } } - update_window_color(win); + draw_frame(win); raise_window(win); } @@ -1722,7 +1777,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; @@ -1730,6 +1785,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) { @@ -1743,8 +1799,8 @@ dumpwins(struct swm_region *r, union arg *args) c = xcb_get_window_attributes(conn, w->id); wa = xcb_get_window_attributes_reply(conn, c, NULL); if (wa) { - DPRINTF("win %#x, map_state: %d, state: %u, " - "transient: %#x\n", w->id, wa->map_state, + DPRINTF("win %#x (%#x), map_state: %d, state: %u, " + "transient: %#x\n", w->frame, w->id, wa->map_state, state, w->transient); free(wa); } else @@ -1754,8 +1810,8 @@ dumpwins(struct swm_region *r, union arg *args) DPRINTF("=== stacking order (top down) === \n"); TAILQ_FOREACH(w, &r->ws->stack, stack_entry) { - DPRINTF("win %#x, fs: %s, maximized: %s, above: %s, " - "iconic: %s\n", w->id, YESNO(FULLSCREEN(w)), + DPRINTF("win %#x (%#x), fs: %s, maximized: %s, above: %s, " + "iconic: %s\n", w->frame, w->id, YESNO(FULLSCREEN(w)), YESNO(MAXIMIZED(w)), YESNO(ABOVE(w)), YESNO(ICONIC(w))); } @@ -1776,10 +1832,169 @@ dumpwins(struct swm_region *r, union arg *args) DPRINTF("=================================\n"); } + +void +debug_toggle(struct binding *b, struct swm_region *r, union arg *s) +{ + struct ws_win *win; + int num_screens, i, j; + + /* Suppress warnings. */ + (void)b; + (void)r; + (void)s; + + DNPRINTF(SWM_D_MISC, "debug_toggle\n"); + + debug_enabled = !debug_enabled; + + num_screens = get_screen_count(); + for (i = 0; i < num_screens; i++) + for (j = 0; j < workspace_limit; j++) + TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) + debug_refresh(win); + + xcb_flush(conn); +} + +void +debug_refresh(struct ws_win *win) +{ + struct ws_win *w; + XftDraw *draw; + XGlyphInfo info; + GC l_draw; + XGCValues l_gcv; + XRectangle l_ibox, l_lbox; + xcb_rectangle_t rect; + size_t len; + uint32_t wc[4], mask, width, height, gcv[1]; + int widx, sidx; + char *s; + xcb_screen_t *screen; + + if (debug_enabled) { + /* Create debug window if it doesn't exist. */ + if (win->debug == XCB_WINDOW_NONE) { + if ((screen = get_screen(win->s->idx)) == NULL) + errx(1, "ERROR: can't get screen %d.", + win->s->idx); + + win->debug = xcb_generate_id(conn); + wc[0] = win->s->c[SWM_S_COLOR_BAR].pixel; + wc[1] = win->s->c[SWM_S_COLOR_BAR_BORDER].pixel; + wc[2] = screen->default_colormap; + + xcb_create_window(conn, screen->root_depth, win->debug, + win->frame, 0, 0, 10, 10, 1, + XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, + XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | + XCB_CW_COLORMAP, wc); + + xcb_map_window(conn, win->debug); + } + + /* Determine workspace window list index. */ + widx = 0; + TAILQ_FOREACH(w, &win->ws->winlist, entry) { + ++widx; + if (w == win) + break; + } + + /* Determine stacking index (top down). */ + sidx = 0; + TAILQ_FOREACH(w, &win->ws->stack, stack_entry) { + ++sidx; + if (w == win) + break; + } + + if (asprintf(&s, "%#x f:%#x wl:%d s:%d im:%s", win->id, + win->frame, widx, sidx, get_win_input_model(win)) == -1) + return; + + len = strlen(s); + + /* Update window to an appropriate dimension. */ + if (bar_font_legacy) { + XmbTextExtents(bar_fs, s, len, &l_ibox, &l_lbox); + width = l_lbox.width + 4; + height = bar_fs_extents->max_logical_extent.height + 4; + } else { + XftTextExtentsUtf8(display, bar_font, (FcChar8 *)s, len, + &info); + width = info.width + 4; + height = bar_font->height + 4; + } + + mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | + XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; + if (win->bordered) + wc[0] = wc[1] = border_width; + else + wc[0] = wc[1] = 0; + + wc[2] = width; + wc[3] = height; + + xcb_configure_window(conn, win->debug, mask, wc); + + /* Draw a filled rectangle to 'clear' window. */ + rect.x = 0; + rect.y = 0; + rect.width = width; + rect.height = height; + + gcv[0] = win->s->c[SWM_S_COLOR_BAR].pixel; + xcb_change_gc(conn, win->s->bar_gc, XCB_GC_FOREGROUND, gcv); + xcb_poly_fill_rectangle(conn, win->debug, win->s->bar_gc, 1, + &rect); + + /* Draw text. */ + if (bar_font_legacy) { + l_gcv.graphics_exposures = 0; + l_draw = XCreateGC(display, win->debug, 0, &l_gcv); + + XSetForeground(display, l_draw, + win->s->c[SWM_S_COLOR_BAR_FONT].pixel); + + DRAWSTRING(display, win->debug, bar_fs, l_draw, 2, + (bar_fs_extents->max_logical_extent.height - + l_lbox.height) / 2 - l_lbox.y, s, len); + + XFreeGC(display, l_draw); + } else { + draw = XftDrawCreate(display, win->debug, + DefaultVisual(display, win->s->idx), + DefaultColormap(display, win->s->idx)); + + XftDrawStringUtf8(draw, &bar_font_color, bar_font, 2, + (bar_height + bar_font->height) / 2 - + bar_font->descent, (FcChar8 *)s, len); + + XftDrawDestroy(draw); + } + + free(s); + } else if (win->debug != XCB_WINDOW_NONE) { + xcb_destroy_window(conn, win->debug); + win->debug = XCB_WINDOW_NONE; + } +} #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; +} + +void +debug_toggle(struct binding *b, struct swm_region *r, union arg *s) { + (void)b; (void)r; (void)s; } @@ -1944,6 +2159,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, " @@ -2104,7 +2321,7 @@ bar_extra_stop(void) bar_pid = 0; } strlcpy(bar_ext, "", sizeof bar_ext); - bar_extra = 0; + bar_extra = false; } void @@ -2160,38 +2377,50 @@ bar_window_name(char *s, size_t sz, struct swm_region *r) free(title); } +bool +get_urgent(struct ws_win *win) +{ + xcb_icccm_wm_hints_t hints; + xcb_get_property_cookie_t c; + bool urgent = false; + + if (win) { + c = xcb_icccm_get_wm_hints(conn, win->id); + if (xcb_icccm_get_wm_hints_reply(conn, c, &hints, NULL)) + urgent = xcb_icccm_wm_hints_get_urgency(&hints); + } + + return urgent; +} + void bar_urgent(char *s, size_t sz) { struct ws_win *win; int i, j, num_screens; - int urgent[SWM_WS_MAX]; + bool urgent[SWM_WS_MAX]; char b[8]; - xcb_get_property_cookie_t c; - xcb_icccm_wm_hints_t hints; for (i = 0; i < workspace_limit; i++) - urgent[i] = 0; + urgent[i] = false; num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) for (j = 0; j < workspace_limit; j++) - TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) { - c = xcb_icccm_get_wm_hints(conn, win->id); - if (xcb_icccm_get_wm_hints_reply(conn, c, - &hints, NULL) == 0) - continue; - if (hints.flags & XCB_ICCCM_WM_HINT_X_URGENCY) - urgent[j] = 1; - } + TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) + if (get_urgent(win)) + urgent[j] = true; for (i = 0; i < workspace_limit; i++) { - if (urgent[i]) + if (urgent[i]) { snprintf(b, sizeof b, "%d ", i + 1); - else - snprintf(b, sizeof b, "- "); - strlcat(s, b, sz); + strlcat(s, b, sz); + } else if (!urgent_collapse) { + strlcat(s, "- ", sz); + } } + if (urgent_collapse && s[0]) + s[strlen(s) - 1] = 0; } void @@ -2244,7 +2473,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); @@ -2431,46 +2660,43 @@ bar_fmt_expand(char *fmtexp, size_t sz) #endif } -/* Redraws the bar; need to follow with xcb_flush() or focus_flush(). */ +/* Redraws a region bar; need to follow with xcb_flush() or focus_flush(). */ void -bar_draw(void) +bar_draw(struct swm_bar *bar) { + struct swm_region *r; char fmtexp[SWM_BAR_MAX], fmtnew[SWM_BAR_MAX]; char fmtrep[SWM_BAR_MAX]; - int i, num_screens; - struct swm_region *r; /* expand the format by first passing it through strftime(3) */ bar_fmt_expand(fmtexp, sizeof fmtexp); - num_screens = get_screen_count(); - for (i = 0; i < num_screens; i++) { - TAILQ_FOREACH(r, &screens[i].rl, entry) { - if (r->bar == NULL) - continue; + if (bar == NULL) + return; - if (bar_enabled && r->ws->bar_enabled) - xcb_map_window(conn, r->bar->id); - else { - xcb_unmap_window(conn, r->bar->id); - continue; - } + r = bar->r; - if (startup_exception) - snprintf(fmtrep, sizeof fmtrep, "total " - "exceptions: %d, first exception: %s", - nr_exceptions, - startup_exception); - else { - bar_fmt(fmtexp, fmtnew, r, sizeof fmtnew); - bar_replace(fmtnew, fmtrep, r, sizeof fmtrep); - } - if (bar_font_legacy) - bar_print_legacy(r, fmtrep); - else - bar_print(r, fmtrep); - } + if (bar_enabled && r->ws->bar_enabled) + xcb_map_window(conn, bar->id); + else { + xcb_unmap_window(conn, bar->id); + return; + } + + if (startup_exception) + snprintf(fmtrep, sizeof fmtrep, "total " + "exceptions: %d, first exception: %s", + nr_exceptions, + startup_exception); + else { + bar_fmt(fmtexp, fmtnew, r, sizeof fmtnew); + bar_replace(fmtnew, fmtrep, r, sizeof fmtrep); } + + if (bar_font_legacy) + bar_print_legacy(r, fmtrep); + else + bar_print(r, fmtrep); } /* @@ -2482,7 +2708,7 @@ bar_extra_update(void) { size_t len; char b[SWM_BAR_MAX]; - int changed = 0; + bool changed = false; if (!bar_extra) return changed; @@ -2504,7 +2730,7 @@ bar_extra_update(void) /* Append new output to bar. */ strlcat(bar_ext, b, sizeof(bar_ext)); - changed = 1; + changed = true; } else { /* Buffer output. */ strlcat(bar_ext_buf, b, sizeof(bar_ext_buf)); @@ -2515,19 +2741,20 @@ bar_extra_update(void) if (errno != EAGAIN) { warn("bar_action failed"); bar_extra_stop(); - changed = 1; + changed = true; } return changed; } 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; @@ -2539,7 +2766,7 @@ bar_toggle(struct swm_region *r, union arg *args) if (bar_enabled) r->ws->bar_enabled = !r->ws->bar_enabled; else - bar_enabled = r->ws->bar_enabled = 1; + bar_enabled = r->ws->bar_enabled = true; break; case SWM_ARG_ID_BAR_TOGGLE: bar_enabled = !bar_enabled; @@ -2557,10 +2784,13 @@ bar_toggle(struct swm_region *r, union arg *args) xcb_unmap_window(conn, tmpr->bar->id); } - stack(); - - /* must be after stack */ - bar_draw(); + /* Restack all regions and redraw bar. */ + num_screens = get_screen_count(); + for (i = 0; i < num_screens; i++) + TAILQ_FOREACH(tmpr, &screens[i].rl, entry) { + stack(tmpr); + bar_draw(tmpr->bar); + } focus_flush(); } @@ -2571,7 +2801,7 @@ bar_extra_setup(void) /* do this here because the conf file is in memory */ if (!bar_extra && bar_argv[0]) { /* launch external status app */ - bar_extra = 1; + bar_extra = true; if (pipe(bar_pipe) == -1) err(1, "pipe error"); socket_setnonblock(bar_pipe[0]); @@ -2612,7 +2842,7 @@ kill_bar_extra_atexit(void) kill(bar_pid, SIGTERM); } -int +bool isxlfd(char *s) { int count = 0; @@ -2638,7 +2868,8 @@ fontset_init(void) bar_fs = NULL; } - DNPRINTF(SWM_D_INIT, "fontset_init: loading bar_fonts: %s\n", bar_fonts); + DNPRINTF(SWM_D_INIT, "fontset_init: loading bar_fonts: %s\n", + bar_fonts); bar_fs = XCreateFontSet(display, bar_fonts, &missing_charsets, &num_missing_charsets, &default_string); @@ -2673,13 +2904,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; @@ -2703,7 +2934,7 @@ xft_init(struct swm_region *r) break; } } - free(d); + free(str); } if (bar_font == NULL) @@ -2715,6 +2946,12 @@ xft_init(struct swm_region *r) DefaultColormap(display, r->s->idx), &color, &bar_font_color)) warn("Xft error: unable to allocate color."); + PIXEL_TO_XRENDERCOLOR(r->s->c[SWM_S_COLOR_BAR].pixel, color); + + if (!XftColorAllocValue(display, DefaultVisual(display, r->s->idx), + DefaultColormap(display, r->s->idx), &color, &search_font_color)) + warn("Xft error: unable to allocate color."); + bar_height = bar_font->height + 2 * bar_border_width; if (bar_height < 1) @@ -2744,6 +2981,7 @@ bar_setup(struct swm_region *r) else xft_init(r); + r->bar->r = r; X(r->bar) = X(r); Y(r->bar) = bar_at_bottom ? (Y(r) + HEIGHT(r) - bar_height) : Y(r); WIDTH(r->bar) = WIDTH(r) - 2 * bar_border_width; @@ -2762,11 +3000,18 @@ bar_setup(struct swm_region *r) XCB_COPY_FROM_PARENT, XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK, wa); + /* Stack bar window above region window to start. */ + wa[0] = r->id; + wa[1] = XCB_STACK_MODE_ABOVE; + + xcb_configure_window(conn, r->bar->id, XCB_CONFIG_WINDOW_SIBLING | + XCB_CONFIG_WINDOW_STACK_MODE, wa); + r->bar->buffer = xcb_generate_id(conn); xcb_create_pixmap(conn, screen->root_depth, r->bar->buffer, r->bar->id, WIDTH(r->bar), HEIGHT(r->bar)); - if (xrandr_support) + if (randr_support) xcb_randr_select_input(conn, r->bar->id, XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE); @@ -2797,7 +3042,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; @@ -2827,9 +3072,13 @@ 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) { + struct swm_region *tmpr; + int i, num_screens; + /* suppress unused warnings since vars are needed */ + (void)bp; (void)r; (void)args; @@ -2840,8 +3089,13 @@ version(struct swm_region *r, union arg *args) else strlcpy(bar_vertext, "", sizeof bar_vertext); - bar_draw(); - xcb_flush(conn); + num_screens = get_screen_count(); + for (i = 0; i < num_screens; i++) { + TAILQ_FOREACH(tmpr, &screens[i].rl, entry) { + bar_draw(tmpr->bar); + xcb_flush(conn); + } + } } void @@ -2890,13 +3144,13 @@ config_win(struct ws_win *win, xcb_configure_request_event_t *ev) ce.y = Y(win); ce.width = WIDTH(win); ce.height = HEIGHT(win); + ce.border_width = 0; ce.override_redirect = 0; if (ev == NULL) { /* EWMH */ ce.event = win->id; ce.window = win->id; - ce.border_width = BORDER(win); ce.above_sibling = XCB_WINDOW_NONE; } else { /* normal */ @@ -2941,9 +3195,9 @@ config_win(struct ws_win *win, xcb_configure_request_event_t *ev) } /* adjust x and y for requested border_width. */ - ce.x += BORDER(win) - ev->border_width; - ce.y += BORDER(win) - ev->border_width; - ce.border_width = ev->border_width; + ce.x += ev->border_width; + ce.y += ev->border_width; + ce.above_sibling = ev->sibling; } @@ -2956,7 +3210,7 @@ config_win(struct ws_win *win, xcb_configure_request_event_t *ev) } int -count_win(struct workspace *ws, int count_transient) +count_win(struct workspace *ws, bool count_transient) { struct ws_win *win; int count = 0; @@ -2974,9 +3228,10 @@ count_win(struct workspace *ws, int 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; @@ -2985,17 +3240,78 @@ quit(struct swm_region *r, union arg *args) } void -raise_window(struct ws_win *win) +lower_window(struct ws_win *win) { struct ws_win *target = NULL; - struct swm_region *r; struct workspace *ws; - if (win == NULL || (r = win->ws->r) == NULL) + DNPRINTF(SWM_D_EVENT, "lower_window: win %#x\n", WINID(win)); + + if (win == NULL) return; + ws = win->ws; - DNPRINTF(SWM_D_EVENT, "raise_window: win %#x\n", win->id); + TAILQ_FOREACH(target, &ws->stack, stack_entry) { + if (target == win || ICONIC(target)) + continue; + if (ws->cur_layout == &layouts[SWM_MAX_STACK]) + break; + if (TRANS(win)) { + if (win->transient == target->transient) + continue; + if (win->transient == target->id) + break; + } + if (FULLSCREEN(target)) + continue; + if (FULLSCREEN(win)) + break; + if (MAXIMIZED(target)) + continue; + if (MAXIMIZED(win)) + break; + if (ABOVE(target) || TRANS(target)) + continue; + if (ABOVE(win) || TRANS(win)) + break; + } + + /* Change stack position. */ + TAILQ_REMOVE(&ws->stack, win, stack_entry); + if (target) + TAILQ_INSERT_BEFORE(target, win, stack_entry); + else + TAILQ_INSERT_TAIL(&ws->stack, win, stack_entry); + + update_win_stacking(win); + +#ifdef SWM_DEBUG + if (swm_debug & SWM_D_STACK) { + DPRINTF("=== stacking order (top down) === \n"); + TAILQ_FOREACH(target, &ws->stack, stack_entry) { + DPRINTF("win %#x, fs: %s, maximized: %s, above: %s, " + "iconic: %s\n", target->id, YESNO(FULLSCREEN(target)), + YESNO(MAXIMIZED(target)), YESNO(ABOVE(target)), + YESNO(ICONIC(target))); + } + } +#endif + DNPRINTF(SWM_D_EVENT, "lower_window: done\n"); +} + +void +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; TAILQ_FOREACH(target, &ws->stack, stack_entry) { if (target == win || ICONIC(target)) @@ -3013,23 +3329,25 @@ raise_window(struct ws_win *win) break; if (MAXIMIZED(target)) continue; - if (ABOVE(win) || TRANS(win)) + if (ABOVE(win) || TRANS(win) || + (win->ws->focus == win && ws->always_raise)) break; if (!ABOVE(target) && !TRANS(target)) break; } - if (target != NULL) { - /* Change stack position. */ - TAILQ_REMOVE(&ws->stack, win, stack_entry); + TAILQ_REMOVE(&ws->stack, win, stack_entry); + if (target) TAILQ_INSERT_BEFORE(target, win, stack_entry); - update_win_stacking(win); - } + else + TAILQ_INSERT_TAIL(&ws->stack, win, stack_entry); + + update_win_stacking(win); #ifdef SWM_DEBUG if (swm_debug & SWM_D_STACK) { DPRINTF("=== stacking order (top down) === \n"); - TAILQ_FOREACH(target, &r->ws->stack, stack_entry) { + TAILQ_FOREACH(target, &ws->stack, stack_entry) { DPRINTF("win %#x, fs: %s, maximized: %s, above: %s, " "iconic: %s\n", target->id, YESNO(FULLSCREEN(target)), YESNO(MAXIMIZED(target)), YESNO(ABOVE(target)), @@ -3044,25 +3362,43 @@ void update_win_stacking(struct ws_win *win) { struct ws_win *sibling; - struct swm_region *r; +#ifdef SWM_DEBUG + struct ws_win *w; +#endif + struct swm_region *r; uint32_t val[2]; if (win == NULL || (r = win->ws->r) == NULL) return; + if (win->frame == XCB_WINDOW_NONE) { + DNPRINTF(SWM_D_EVENT, "update_window_stacking: win %#x not " + "reparented.\n", win->id); + return; + } + sibling = TAILQ_NEXT(win, stack_entry); - if (sibling != NULL && FLOATING(win) == FLOATING(sibling)) - val[0] = sibling->id; + if (sibling != NULL && (FLOATING(win) == FLOATING(sibling) || + (win->ws->always_raise && win->ws->focus == win))) + val[0] = sibling->frame; + else if (FLOATING(win) || (win->ws->always_raise && + win->ws->focus == win)) + val[0] = r->bar->id; else - val[0] = FLOATING(win) ? r->bar->id : r->id; + val[0] = r->id; - DNPRINTF(SWM_D_EVENT, "update_win_stacking: %#x, sibling %#x\n", - win->id, val[0]); + DNPRINTF(SWM_D_EVENT, "update_win_stacking: win %#x (%#x), " + "sibling %#x\n", win->frame, win->id, val[0]); val[1] = XCB_STACK_MODE_ABOVE; - xcb_configure_window(conn, win->id, XCB_CONFIG_WINDOW_SIBLING | + xcb_configure_window(conn, win->frame, XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE, val); + +#ifdef SWM_DEBUG + TAILQ_FOREACH(w, &win->ws->winlist, entry) + debug_refresh(w); +#endif } void @@ -3077,9 +3413,10 @@ map_window(struct ws_win *win) if (win->mapped) return; + xcb_map_window(conn, win->frame); xcb_map_window(conn, win->id); set_win_state(win, XCB_ICCCM_WM_STATE_NORMAL); - win->mapped = 1; + win->mapped = true; } void @@ -3095,8 +3432,9 @@ unmap_window(struct ws_win *win) return; xcb_unmap_window(conn, win->id); + xcb_unmap_window(conn, win->frame); set_win_state(win, XCB_ICCCM_WM_STATE_ICONIC); - win->mapped = 0; + win->mapped = false; } void @@ -3151,9 +3489,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; @@ -3163,7 +3502,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 * @@ -3189,6 +3528,26 @@ get_pointer_win(xcb_window_t root) return win; } +void +center_pointer(struct swm_region *r) +{ + struct ws_win *win; + + if (!warp_pointer || r == NULL) + return; + + win = r->ws->focus; + + DNPRINTF(SWM_D_EVENT, "center_pointer: win %#x.\n", WINID(win)); + + if (win && win->mapped) + xcb_warp_pointer(conn, XCB_NONE, win->frame, 0, 0, 0, 0, + WIDTH(win) / 2, HEIGHT(win) / 2); + else + xcb_warp_pointer(conn, XCB_NONE, r->id, 0, 0, 0, 0, + WIDTH(r) / 2, HEIGHT(r) / 2); +} + struct swm_region * root_to_region(xcb_window_t root, int check) { @@ -3247,61 +3606,105 @@ root_to_region(xcb_window_t root, int check) return (r); } -struct ws_win * -find_unmanaged_window(xcb_window_t id) +struct swm_region * +find_region(xcb_window_t id) { - struct ws_win *win; - int i, j, num_screens; + struct swm_region *r; + int i, num_screens; num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) - for (j = 0; j < workspace_limit; j++) - TAILQ_FOREACH(win, &screens[i].ws[j].unmanagedlist, - entry) - if (id == win->id) - return (win); - return (NULL); + TAILQ_FOREACH(r, &screens[i].rl, entry) + if (r->id == id) + return r; + + return NULL; +} + +struct swm_bar * +find_bar(xcb_window_t id) +{ + struct swm_region *r; + int i, num_screens; + + num_screens = get_screen_count(); + for (i = 0; i < num_screens; i++) + TAILQ_FOREACH(r, &screens[i].rl, entry) + if (r->bar && r->bar->id == id) + return r->bar; + + return NULL; +} + +struct ws_win * +find_frame_window(xcb_window_t id) { + struct swm_region *r; + struct ws_win *w; + int i, num_screens; + + num_screens = get_screen_count(); + for (i = 0; i < num_screens; i++) + TAILQ_FOREACH(r, &screens[i].rl, entry) + TAILQ_FOREACH(w, &r->ws->winlist, entry) + if (w->frame == id) + return w; + + return NULL; } struct ws_win * find_window(xcb_window_t id) { - struct ws_win *win; + struct ws_win *win = NULL; int i, j, num_screens; - xcb_query_tree_reply_t *r; + xcb_query_tree_reply_t *qtr; + + DNPRINTF(SWM_D_MISC, "find_window: id: %#x\n", id); num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) for (j = 0; j < workspace_limit; j++) TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) - if (id == win->id) + if (id == win->id || id == win->frame) return (win); - r = xcb_query_tree_reply(conn, xcb_query_tree(conn, id), NULL); - if (r == NULL) - return (NULL); - /* if we were looking for the parent return that window instead */ - if (r->parent == 0 || r->root == r->parent) { - free(r); - return (NULL); + /* If window isn't top-level, try to find managed ancestor. */ + qtr = xcb_query_tree_reply(conn, xcb_query_tree(conn, id), NULL); + if (qtr) { + if (qtr->parent != XCB_WINDOW_NONE && qtr->parent != qtr->root) + win = find_window(qtr->parent); + +#ifdef SWM_DEBUG + if (win) + DNPRINTF(SWM_D_MISC, "find_window: found child %#x " + "of %#x.\n", win->id, qtr->parent); +#endif + + free(qtr); } - /* look for parent */ + return (win); +} + +struct ws_win * +find_unmanaged_window(xcb_window_t id) +{ + struct ws_win *win; + int i, j, num_screens; + + num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) for (j = 0; j < workspace_limit; j++) - TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) - if (r->parent == win->id) { - free(r); + TAILQ_FOREACH(win, &screens[i].ws[j].unmanagedlist, + entry) + if (id == win->id) return (win); - } - - free(r); return (NULL); } void -spawn(int ws_idx, union arg *args, int close_fd) +spawn(int ws_idx, union arg *args, bool close_fd) { int fd; char *ret = NULL; @@ -3310,7 +3713,16 @@ spawn(int ws_idx, union arg *args, int close_fd) close(xcb_get_file_descriptor(conn)); - setenv("LD_PRELOAD", SWM_LIB, 1); + if ((ret = getenv("LD_PRELOAD"))) { + if (asprintf(&ret, "%s:%s", SWM_LIB, ret) == -1) { + warn("spawn: asprintf LD_PRELOAD"); + _exit(1); + } + setenv("LD_PRELOAD", ret, 1); + free(ret); + } else { + setenv("LD_PRELOAD", SWM_LIB, 1); + } if (asprintf(&ret, "%d", ws_idx) == -1) { warn("spawn: asprintf SWM_WS"); @@ -3358,23 +3770,33 @@ spawn(int ws_idx, union arg *args, int 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 (win == ws->focus_pending) + ws->focus_pending = NULL; + if (win == ws->focus_raise) + ws->focus_raise = NULL; + + if (TRANS(win)) + TAILQ_FOREACH(w, &ws->winlist, entry) + if (win == w->focus_child) + w->focus_child = NULL; + } + } } int @@ -3453,6 +3875,9 @@ unfocus_win(struct ws_win *win) if (win->ws->focus == win) { win->ws->focus = NULL; win->ws->focus_prev = win; + if (win->ws->focus_raise == win && !FLOATING(win)) { + update_win_stacking(win); + } } if (validate_win(win->ws->focus)) { @@ -3465,7 +3890,20 @@ unfocus_win(struct ws_win *win) win->ws->focus_prev = NULL; } - update_window_color(win); + draw_frame(win); + + /* Raise window to "top unfocused position." */ + if (win->ws->always_raise) + raise_window(win); + + /* Update border width */ + if (win->bordered && (win->quirks & SWM_Q_MINIMALBORDER) && + FLOATING(win)) { + win->bordered = 0; + X(win) += border_width; + Y(win) += border_width; + update_window(win); + } xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->s->root, ewmh[_NET_ACTIVE_WINDOW].atom, XCB_ATOM_WINDOW, 32, 1, &none); @@ -3476,9 +3914,10 @@ unfocus_win(struct ws_win *win) void focus_win(struct ws_win *win) { - struct ws_win *cfw = NULL, *parent = NULL, *w; + struct ws_win *cfw = NULL, *parent = NULL, *w, *tmpw; struct workspace *ws; - xcb_get_input_focus_reply_t *gifr; + xcb_get_input_focus_reply_t *gifr = NULL; + xcb_get_window_attributes_reply_t *war = NULL; DNPRINTF(SWM_D_FOCUS, "focus_win: win %#x\n", WINID(win)); @@ -3497,20 +3936,28 @@ focus_win(struct ws_win *win) gifr = xcb_get_input_focus_reply(conn, xcb_get_input_focus(conn), NULL); if (gifr) { + DNPRINTF(SWM_D_FOCUS, "focus_win: cur focus: %#x\n", + gifr->focus); + cfw = find_window(gifr->focus); - if (cfw != NULL && cfw != win) { - if (cfw->ws != ws && cfw->ws->r != NULL) { - /* Change border to unfocused color. */ - xcb_change_window_attributes(conn, cfw->id, - XCB_CW_BORDER_PIXEL, - &cfw->s->c[(MAXIMIZED(cfw) ? - SWM_S_COLOR_UNFOCUS_MAXIMIZED : - SWM_S_COLOR_UNFOCUS)].pixel); - } else { - unfocus_win(cfw); + if (cfw) { + if (cfw != win) { + if (cfw->ws != ws && cfw->ws->r != NULL && + cfw->frame != XCB_WINDOW_NONE) { + draw_frame(cfw); + } else { + unfocus_win(cfw); + } + } + } else { + war = xcb_get_window_attributes_reply(conn, + xcb_get_window_attributes(conn, gifr->focus), NULL); + if (war && war->override_redirect && ws->focus == win) { + DNPRINTF(SWM_D_FOCUS, "focus_win: skip refocus " + "from override_redirect.\n"); + goto out; } } - free(gifr); } if (ws->focus != win) { @@ -3519,9 +3966,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)) { @@ -3530,16 +3976,28 @@ focus_win(struct ws_win *win) parent->focus_child = win; } - if (cfw != win && ws->r != NULL) { + /* Update window border even if workspace is hidden. */ + draw_frame(win); + + if (cfw == win) { + DNPRINTF(SWM_D_FOCUS, "focus_win: already focused.\n"); + goto out; + } + + if (ws->r) { /* 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)) - xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, - win->id, last_event_time); - else - xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, + if (ACCEPTS_FOCUS(win)) { + DNPRINTF(SWM_D_FOCUS, "focus_win: set_input_focus: %#x," + " revert-to: parent, time: %#x\n", win->id, + last_event_time); + xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, + win->id, last_event_time); + } else if (!win->take_focus) { + DNPRINTF(SWM_D_FOCUS, "focus_win: set_input_focus: %#x," + " revert-to: parent, time: 0\n", ws->r->id); + xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, ws->r->id, XCB_CURRENT_TIME); + } /* Tell app it can adjust focus to a specific window. */ if (win->take_focus) { @@ -3559,20 +4017,25 @@ focus_win(struct ws_win *win) map_window(parent); /* Map siblings next. */ - TAILQ_FOREACH(w, &ws->winlist, entry) + TAILQ_FOREACH_SAFE(w, &ws->stack, stack_entry, + tmpw) if (w != win && !ICONIC(w) && - win->transient == parent->id) + w->transient == parent->id) { + raise_window(w); map_window(w); + } } /* Map focused window. */ raise_window(win); map_window(win); - /* Finally, map children of focus window. */ - TAILQ_FOREACH(w, &ws->winlist, entry) - if (w->transient == win->id && !ICONIC(w)) + /* Stack any children of focus window. */ + TAILQ_FOREACH_SAFE(w, &ws->stack, stack_entry, tmpw) + if (w->transient == win->id && !ICONIC(w)) { + raise_window(w); map_window(w); + } } else if (tile_gap < 0 && !ABOVE(win)) { /* * Windows overlap in the layout. @@ -3587,15 +4050,13 @@ focus_win(struct ws_win *win) xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->s->root, ewmh[_NET_ACTIVE_WINDOW].atom, XCB_ATOM_WINDOW, 32, 1, &win->id); - } - if (cfw != win) - /* Update window border even if workspace is hidden. */ - update_window_color(win); + bar_draw(ws->r->bar); + } out: - bar_draw(); - + free(gifr); + free(war); DNPRINTF(SWM_D_FOCUS, "focus_win: done.\n"); } @@ -3640,7 +4101,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); @@ -3684,6 +4145,10 @@ set_region(struct swm_region *r) r->s->r_focus = r; + /* Update the focus window frame on the now unfocused region. */ + if (rf && rf->ws->focus) + draw_frame(rf->ws->focus); + ewmh_update_current_desktop(); } @@ -3704,25 +4169,29 @@ focus_region(struct swm_region *r) focus_win(nfw); } else { /* New region is empty; need to manually unfocus win. */ - if (old_r) + if (old_r) { unfocus_win(old_r->ws->focus); + /* Clear bar since empty. */ + bar_draw(old_r->bar); + } - xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, r->id, + DNPRINTF(SWM_D_FOCUS, "focus_region: set_input_focus: %#x, " + "revert-to: parent, time: 0\n", r->id); + xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, r->id, XCB_CURRENT_TIME); - /* Clear bar since empty. */ - bar_draw(); } } 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; struct workspace *new_ws, *old_ws; xcb_window_t none = XCB_WINDOW_NONE; - int wsid = args->id, unmap_old = 0; + int wsid = args->id; + bool unmap_old = false; if (!(r && r->s)) return; @@ -3742,25 +4211,40 @@ switchws(struct swm_region *r, union arg *args) if (new_ws == old_ws) return; + other_r = new_ws->r; + if (other_r && workspace_clamp && + bp->action != FN_RG_MOVE_NEXT && bp->action != FN_RG_MOVE_PREV) { + DNPRINTF(SWM_D_WS, "switchws: ws clamped.\n"); + + if (warp_focus) { + DNPRINTF(SWM_D_WS, "switchws: warping focus to region " + "with ws %d.\n", wsid); + focus_region(other_r); + center_pointer(other_r); + focus_flush(); + } + return; + } + if ((win = old_ws->focus) != NULL) { - update_window_color(win); + draw_frame(win); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->s->root, ewmh[_NET_ACTIVE_WINDOW].atom, XCB_ATOM_WINDOW, 32, 1, &none); } - other_r = new_ws->r; - if (other_r == NULL) { - /* the other workspace is hidden, hide this one */ - old_ws->r = NULL; - unmap_old = 1; - } else { + if (other_r) { /* the other ws is visible in another region, exchange them */ other_r->ws_prior = new_ws; other_r->ws = old_ws; old_ws->r = other_r; + } else { + /* the other workspace is hidden, hide this one */ + old_ws->r = NULL; + unmap_old = true; } + this_r->ws_prior = old_ws; this_r->ws = new_ws; new_ws->r = this_r; @@ -3774,7 +4258,9 @@ switchws(struct swm_region *r, union arg *args) } new_ws->state = SWM_WS_STATE_MAPPING; - stack(); + + stack(other_r); + stack(this_r); /* unmap old windows */ if (unmap_old) { @@ -3793,13 +4279,16 @@ switchws(struct swm_region *r, union arg *args) /* Clear bar and set focus on region input win if new ws is empty. */ if (new_ws->focus_pending == NULL && new_ws->focus == NULL) { - xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, r->id, + DNPRINTF(SWM_D_FOCUS, "switchws: set_input_focus: %#x, " + "revert-to: parent, time: 0\n", r->id); + xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, r->id, XCB_CURRENT_TIME); - bar_draw(); + bar_draw(r->bar); } ewmh_update_current_desktop(); + center_pointer(r); focus_flush(); new_ws->state = SWM_WS_STATE_MAPPED; @@ -3807,12 +4296,11 @@ 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; - int cycle_all = 0; - int mv = 0; + bool cycle_all = false, mv = false; DNPRINTF(SWM_D_WS, "cyclews: 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); @@ -3822,19 +4310,19 @@ cyclews(struct swm_region *r, union arg *args) do { switch (args->id) { case SWM_ARG_ID_CYCLEWS_MOVE_UP: - mv = 1; + mv = true; /* FALLTHROUGH */ case SWM_ARG_ID_CYCLEWS_UP_ALL: - cycle_all = 1; + cycle_all = true; /* FALLTHROUGH */ case SWM_ARG_ID_CYCLEWS_UP: a.id = (a.id < workspace_limit - 1) ? a.id + 1 : 0; break; case SWM_ARG_ID_CYCLEWS_MOVE_DOWN: - mv = 1; + mv = true; /* FALLTHROUGH */ case SWM_ARG_ID_CYCLEWS_DOWN_ALL: - cycle_all = 1; + cycle_all = true; /* FALLTHROUGH */ case SWM_ARG_ID_CYCLEWS_DOWN: a.id = (a.id > 0) ? a.id - 1 : workspace_limit - 1; @@ -3850,16 +4338,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; @@ -3872,16 +4360,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)) @@ -3897,13 +4387,15 @@ focusrg(struct swm_region *r, union arg *args) return; focus_region(rr); + center_pointer(rr); focus_flush(); DNPRINTF(SWM_D_FOCUS, "focusrg: done\n"); } 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; int i, num_screens; @@ -3917,11 +4409,13 @@ cyclerg(struct swm_region *r, union arg *args) switch (args->id) { case SWM_ARG_ID_CYCLERG_UP: + case SWM_ARG_ID_CYCLERG_MOVE_UP: rr = TAILQ_NEXT(r, entry); if (rr == NULL) rr = TAILQ_FIRST(&screens[i].rl); break; case SWM_ARG_ID_CYCLERG_DOWN: + case SWM_ARG_ID_CYCLERG_MOVE_DOWN: rr = TAILQ_PREV(r, swm_region_list, entry); if (rr == NULL) rr = TAILQ_LAST(&screens[i].rl, swm_region_list); @@ -3932,8 +4426,22 @@ cyclerg(struct swm_region *r, union arg *args) if (rr == NULL) return; - focus_region(rr); - focus_flush(); + switch (args->id) { + case SWM_ARG_ID_CYCLERG_UP: + case SWM_ARG_ID_CYCLERG_DOWN: + focus_region(rr); + center_pointer(rr); + focus_flush(); + break; + case SWM_ARG_ID_CYCLERG_MOVE_UP: + case SWM_ARG_ID_CYCLERG_MOVE_DOWN: + a.id = rr->ws->idx; + switchws(bp, r, &a); + break; + default: + return; + }; + DNPRINTF(SWM_D_FOCUS, "cyclerg: done\n"); } @@ -3961,12 +4469,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); @@ -4065,7 +4575,8 @@ swapwin(struct swm_region *r, union arg *args) sort_windows(wl); ewmh_update_client_list(); - stack(); + stack(r); + center_pointer(r); focus_flush(); out: DNPRINTF(SWM_D_MOVE, "swapwin: done\n"); @@ -4212,29 +4723,32 @@ 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; struct workspace *ws = NULL; union arg a; int i; - xcb_icccm_wm_hints_t hints; if (!(r && r->ws)) goto out; - DNPRINTF(SWM_D_FOCUS, "focus: id: %d\n", args->id); - cur_focus = r->ws->focus; ws = r->ws; wl = &ws->winlist; + DNPRINTF(SWM_D_FOCUS, "focus: id: %d, cur_focus: %#x\n", args->id, + WINID(cur_focus)); + /* Make sure an uniconified window has focus, if one exists. */ if (cur_focus == NULL) { cur_focus = TAILQ_FIRST(wl); while (cur_focus != NULL && ICONIC(cur_focus)) cur_focus = TAILQ_NEXT(cur_focus, entry); + + DNPRINTF(SWM_D_FOCUS, "focus: new cur_focus: %#x\n", + WINID(cur_focus)); } switch (args->id) { @@ -4249,8 +4763,11 @@ focus(struct swm_region *r, union arg *args) winfocus = TAILQ_LAST(wl, ws_win_list); if (winfocus == cur_focus) break; - } while (winfocus != NULL && - (ICONIC(winfocus) || winfocus->id == cur_focus->transient)); + } while (winfocus && (ICONIC(winfocus) || + winfocus->id == cur_focus->transient || + (cur_focus->transient != XCB_WINDOW_NONE && + winfocus->transient == cur_focus->transient) || + (winfocus->quirks & SWM_Q_NOFOCUSCYCLE))); break; case SWM_ARG_ID_FOCUSNEXT: if (cur_focus == NULL) @@ -4263,8 +4780,11 @@ focus(struct swm_region *r, union arg *args) winfocus = TAILQ_FIRST(wl); if (winfocus == cur_focus) break; - } while (winfocus != NULL && - (ICONIC(winfocus) || winfocus->id == cur_focus->transient)); + } while (winfocus && (ICONIC(winfocus) || + winfocus->id == cur_focus->transient || + (cur_focus->transient != XCB_WINDOW_NONE && + winfocus->transient == cur_focus->transient) || + (winfocus->quirks & SWM_Q_NOFOCUSCYCLE))); break; case SWM_ARG_ID_FOCUSMAIN: if (cur_focus == NULL) @@ -4284,29 +4804,28 @@ focus(struct swm_region *r, union arg *args) head = TAILQ_FIRST(&r->s->ws[(ws->idx + i) % workspace_limit].winlist); - while (head != NULL && - (head = TAILQ_NEXT(head, entry)) != NULL) { + while (head) { if (head == cur_focus) { - winfocus = cur_focus; - break; - } - if (xcb_icccm_get_wm_hints_reply(conn, - xcb_icccm_get_wm_hints(conn, head->id), - &hints, NULL) != 0 && - xcb_icccm_wm_hints_get_urgency(&hints)) { + if (i > 0) { + winfocus = cur_focus; + break; + } + } else if (get_urgent(head)) { winfocus = head; break; } + + head = TAILQ_NEXT(head, entry); } - if (winfocus != NULL) + if (winfocus) break; } /* Switch ws if new focus is on a different ws. */ - if (winfocus != NULL && winfocus->ws != ws) { + if (winfocus && winfocus->ws != ws) { a.id = winfocus->ws->idx; - switchws(r, &a); + switchws(bp, r, &a); } break; default: @@ -4314,9 +4833,10 @@ focus(struct swm_region *r, union arg *args) } if (clear_maximized(ws) > 0) - stack(); + stack(r); focus_win(get_focus_magic(winfocus)); + center_pointer(r); focus_flush(); out: @@ -4324,11 +4844,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); @@ -4339,95 +4872,91 @@ cycle_layout(struct swm_region *r, union arg *args) clear_maximized(ws); - stack(); - bar_draw(); + stack(r); + bar_draw(r->bar); focus_win(get_region_focus(r)); + center_pointer(r); focus_flush(); } 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); if (clear_maximized(ws) > 0) - stack(); + stack(r); if (ws->cur_layout->l_config != NULL) ws->cur_layout->l_config(ws, args->id); if (args->id != SWM_ARG_ID_STACKINIT) - stack(); - bar_draw(); + stack(r); + bar_draw(r->bar); + center_pointer(r); focus_flush(); } void -stack(void) { +stack(struct swm_region *r) { struct swm_geometry g; - struct swm_region *r, *r_prev = NULL; - int i, num_screens; + struct swm_region *r_prev; uint32_t val[2]; -#ifdef SWM_DEBUG - int j; -#endif - DNPRINTF(SWM_D_STACK, "stack: begin\n"); + if (r == NULL) + return; - num_screens = get_screen_count(); - for (i = 0; i < num_screens; i++) { -#ifdef SWM_DEBUG - j = 0; -#endif - TAILQ_FOREACH(r, &screens[i].rl, entry) { - /* Adjust stack area for region bar and padding. */ - g = r->g; - g.x += region_padding; - g.y += region_padding; - g.w -= 2 * border_width + 2 * region_padding; - g.h -= 2 * border_width + 2 * region_padding; - if (bar_enabled && r->ws->bar_enabled) { - if (!bar_at_bottom) - g.y += bar_height; - g.h -= bar_height; - } + DNPRINTF(SWM_D_STACK, "stack: begin\n"); - DNPRINTF(SWM_D_STACK, "stack: workspace: %d (screen: " - "%d, region: %d), (x,y) WxH: (%d,%d) %d x %d\n", - r->ws->idx, i, j++, g.x, g.y, g.w, g.h); - - if (r_prev) { - /* Stack bar/input relative to prev. region. */ - val[1] = XCB_STACK_MODE_ABOVE; - - val[0] = r_prev->id; - DNPRINTF(SWM_D_STACK, "stack: region input %#x " - "relative to %#x.\n", r->id, val[0]); - xcb_configure_window(conn, r->id, - XCB_CONFIG_WINDOW_SIBLING | - XCB_CONFIG_WINDOW_STACK_MODE, val); - - val[0] = r_prev->bar->id; - DNPRINTF(SWM_D_STACK, "stack: region bar %#x " - "relative to %#x.\n", r->bar->id, val[0]); - xcb_configure_window(conn, r->bar->id, - XCB_CONFIG_WINDOW_SIBLING | - XCB_CONFIG_WINDOW_STACK_MODE, val); - } + /* Adjust stack area for region bar and padding. */ + g = r->g; + g.x += region_padding; + g.y += region_padding; + g.w -= 2 * border_width + 2 * region_padding; + g.h -= 2 * border_width + 2 * region_padding; + if (bar_enabled && r->ws->bar_enabled) { + if (!bar_at_bottom) + g.y += bar_height; + g.h -= bar_height; + } + + DNPRINTF(SWM_D_STACK, "stack: workspace: %d (screen: " + "%d, region: %d), (x,y) WxH: (%d,%d) %d x %d\n", + r->ws->idx, r->s->idx, get_region_index(r), g.x, g.y, g.w, g.h); + + r_prev = TAILQ_PREV(r, swm_region_list, entry); + if (r_prev) { + /* Stack bar/input relative to prev. region. */ + val[1] = XCB_STACK_MODE_ABOVE; + + val[0] = r_prev->id; + DNPRINTF(SWM_D_STACK, "stack: region input %#x " + "relative to %#x.\n", r->id, val[0]); + xcb_configure_window(conn, r->id, + XCB_CONFIG_WINDOW_SIBLING | + XCB_CONFIG_WINDOW_STACK_MODE, val); - r->ws->cur_layout->l_stack(r->ws, &g); - r->ws->cur_layout->l_string(r->ws); - /* save r so we can track region changes */ - r->ws->old_r = r; - r_prev = r; - } + val[0] = r_prev->bar->id; + DNPRINTF(SWM_D_STACK, "stack: region bar %#x " + "relative to %#x.\n", r->bar->id, val[0]); + xcb_configure_window(conn, r->bar->id, + XCB_CONFIG_WINDOW_SIBLING | + XCB_CONFIG_WINDOW_STACK_MODE, val); } + + r->ws->cur_layout->l_stack(r->ws, &g); + r->ws->cur_layout->l_string(r->ws); + /* save r so we can track region changes */ + r->ws->old_r = r; + if (font_adjusted) font_adjusted--; @@ -4444,7 +4973,7 @@ store_float_geom(struct ws_win *win) win->g_float = win->g; win->g_float.x -= X(win->ws->r); win->g_float.y -= Y(win->ws->r); - win->g_floatvalid = 1; + win->g_floatvalid = true; DNPRINTF(SWM_D_MISC, "store_float_geom: win %#x, g: (%d,%d)" " %d x %d, g_float: (%d,%d) %d x %d\n", win->id, X(win), Y(win), WIDTH(win), HEIGHT(win), win->g_float.x, win->g_float.y, @@ -4476,6 +5005,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; @@ -4484,7 +5015,7 @@ 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)) { /* _NET_WM_FULLSCREEN: fullscreen without border. */ @@ -4492,7 +5023,7 @@ update_floater(struct ws_win *win) store_float_geom(win); win->g = r->g; - win->bordered = 0; + win->bordered = false; } else if (MAXIMIZED(win)) { /* Maximize: like a single stacked window. */ if (!win->g_floatvalid) @@ -4500,18 +5031,18 @@ update_floater(struct ws_win *win) win->g = r->g; - if (bar_enabled && ws->bar_enabled) { - win->bordered = 1; + if (bar_enabled && ws->bar_enabled && !maximize_hide_bar) { if (!bar_at_bottom) Y(win) += bar_height; HEIGHT(win) -= bar_height; } else if (disable_border) { - win->bordered = 0; - } else { - win->bordered = 1; + win->bordered = false; } if (win->bordered) { + /* Window geometry excludes frame. */ + X(win) += border_width; + Y(win) += border_width; HEIGHT(win) -= 2 * border_width; WIDTH(win) -= 2 * border_width; } @@ -4521,10 +5052,12 @@ update_floater(struct ws_win *win) if (r != ws->old_r) load_float_geom(win); - if ((win->quirks & SWM_Q_FULLSCREEN) && - WIDTH(win) >= WIDTH(r) && HEIGHT(win) >= HEIGHT(r)) { - /* Remove border for FULLSCREEN quirk. */ - win->bordered = 0; + if (((win->quirks & SWM_Q_FULLSCREEN) && + WIDTH(win) >= WIDTH(r) && HEIGHT(win) >= HEIGHT(r)) || + ((!WS_FOCUSED(win->ws) || win->ws->focus != win) && + (win->quirks & SWM_Q_MINIMALBORDER))) { + /* Remove border */ + win->bordered = false; } else if (!MANUAL(win)) { if (TRANS(win) && (win->quirks & SWM_Q_TRANSSZ)) { /* Adjust size on TRANSSZ quirk. */ @@ -4538,10 +5071,8 @@ update_floater(struct ws_win *win) * unless manually moved, resized or ANYWHERE * quirk is set. */ - X(win) = X(r) + (WIDTH(r) - WIDTH(win)) / 2 - - BORDER(win); - Y(win) = Y(r) + (HEIGHT(r) - HEIGHT(win)) / 2 - - BORDER(win); + X(win) = X(r) + (WIDTH(r) - WIDTH(win)) / 2; + Y(win) = Y(r) + (HEIGHT(r) - HEIGHT(win)) / 2; store_float_geom(win); } } @@ -4588,22 +5119,32 @@ adjust_font(struct ws_win *win) tmp = (g)->h; (g)->h = (g)->w; (g)->w = tmp; \ } while (0) void -stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) +stack_master(struct workspace *ws, struct swm_geometry *g, int rot, bool flip) { - struct swm_geometry win_g, r_g = *g; + struct swm_geometry cell, 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, reconfigure = 0; - int bordered = 1; + 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; + + /* + * cell: geometry for window, including frame. + * mwin: # of windows in master area. + * mscale: size increment of master area. + * stacks: # of stack columns + */ DNPRINTF(SWM_D_STACK, "stack_master: workspace: %d, rot: %s, " "flip: %s\n", ws->idx, YESNO(rot), YESNO(flip)); + memset(&cell, 0, sizeof(cell)); + /* Prepare tiling variables, if needed. */ - if ((winno = count_win(ws, 0)) > 0) { + if ((winno = count_win(ws, false)) > 0) { /* Find first tiled window. */ TAILQ_FOREACH(win, &ws->winlist, entry) if (!FLOATING(win) && !ICONIC(win)) @@ -4624,7 +5165,10 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) mscale = ws->l_state.vertical_msize; stacks = ws->l_state.vertical_stacks; } - win_g = r_g; + + cell = r_g; + cell.x += border_width; + cell.y += border_width; if (stacks > winno - mwin) stacks = winno - mwin; @@ -4637,26 +5181,27 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) split = mwin; colno = split; - win_g.w = v_slice * mscale; + cell.w = v_slice * mscale; if (w_inc > 1 && w_inc < v_slice) { /* Adjust for requested size increment. */ - remain = (win_g.w - w_base) % w_inc; - win_g.w -= remain; + remain = (cell.w - w_base) % w_inc; + cell.w -= remain; } - msize = win_g.w; + msize = cell.w; if (flip) - win_g.x += r_g.w - msize; + cell.x += r_g.w - msize; } else { msize = -2; colno = split = winno / stacks; - win_g.w = ((r_g.w - (stacks * 2 * border_width) + + cell.w = ((r_g.w - (stacks * 2 * border_width) + 2 * border_width) / stacks); } + hrh = r_g.h / colno; extra = r_g.h - (colno * hrh); - win_g.h = hrh - 2 * border_width; + cell.h = hrh - 2 * border_width; i = j = 0, s = stacks; } @@ -4680,24 +5225,24 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) extra = r_g.h - (colno * hrh); if (!flip) - win_g.x += win_g.w + 2 * border_width + + cell.x += cell.w + 2 * border_width + tile_gap; - win_g.w = (r_g.w - msize - + cell.w = (r_g.w - msize - (stacks * (2 * border_width + tile_gap))) / stacks; if (s == 1) - win_g.w += (r_g.w - msize - + cell.w += (r_g.w - msize - (stacks * (2 * border_width + tile_gap))) % stacks; if (flip) - win_g.x -= win_g.w + 2 * border_width + + cell.x -= cell.w + 2 * border_width + tile_gap; s--; j = 0; } - win_g.h = hrh - 2 * border_width - tile_gap; + cell.h = hrh - 2 * border_width - tile_gap; if (rot) { h_inc = win->sh.width_inc; @@ -4708,57 +5253,64 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) } if (j == colno - 1) { - win_g.h = hrh + extra; + cell.h = hrh + extra; } else if (h_inc > 1 && h_inc < h_slice) { /* adjust for window's requested size increment */ - remain = (win_g.h - h_base) % h_inc; + remain = (cell.h - h_base) % h_inc; missing = h_inc - remain; if (missing <= extra || j == 0) { extra -= missing; - win_g.h += missing; + cell.h += missing; } else { - win_g.h -= remain; + cell.h -= remain; extra += remain; } } if (j == 0) - win_g.y = r_g.y; + cell.y = r_g.y + border_width; else - win_g.y += last_h + 2 * border_width + tile_gap; + cell.y += last_h + 2 * border_width + tile_gap; + + /* Window coordinates exclude frame. */ - if (disable_border && !(bar_enabled && ws->bar_enabled) && - winno == 1){ - bordered = 0; - win_g.w += 2 * border_width; - win_g.h += 2 * border_width; + if (winno > 1 || !disable_border || + (bar_enabled && ws->bar_enabled)) { + bordered = true; } else { - bordered = 1; + bordered = false; } if (rot) { - if (X(win) != win_g.y || Y(win) != win_g.x || - WIDTH(win) != win_g.h || HEIGHT(win) != win_g.w) { - reconfigure = 1; - X(win) = win_g.y; - Y(win) = win_g.x; - WIDTH(win) = win_g.h; - HEIGHT(win) = win_g.w; + if (X(win) != cell.y || Y(win) != cell.x || + WIDTH(win) != cell.h || HEIGHT(win) != cell.w) { + reconfigure = true; + X(win) = cell.y; + Y(win) = cell.x; + WIDTH(win) = cell.h; + HEIGHT(win) = cell.w; } } else { - if (X(win) != win_g.x || Y(win) != win_g.y || - WIDTH(win) != win_g.w || HEIGHT(win) != win_g.h) { - reconfigure = 1; - X(win) = win_g.x; - Y(win) = win_g.y; - WIDTH(win) = win_g.w; - HEIGHT(win) = win_g.h; + if (X(win) != cell.x || Y(win) != cell.y || + WIDTH(win) != cell.w || HEIGHT(win) != cell.h) { + reconfigure = true; + X(win) = cell.x; + Y(win) = cell.y; + WIDTH(win) = cell.w; + HEIGHT(win) = cell.h; } } + if (!bordered) { + X(win) -= border_width; + Y(win) -= border_width; + WIDTH(win) += 2 * border_width; + HEIGHT(win) += 2 * border_width; + } + if (bordered != win->bordered) { - reconfigure = 1; + reconfigure = true; win->bordered = bordered; } @@ -4767,7 +5319,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) update_window(win); } - last_h = win_g.h; + last_h = cell.h; i++; j++; } @@ -4813,6 +5365,9 @@ vertical_config(struct workspace *ws, int id) if (ws->l_state.vertical_mwin > 0) ws->l_state.vertical_mwin--; break; + case SWM_ARG_ID_STACKBALANCE: + ws->l_state.vertical_msize = SWM_V_SLICE / (ws->l_state.vertical_stacks + 1); + break; case SWM_ARG_ID_STACKINC: ws->l_state.vertical_stacks++; break; @@ -4863,6 +5418,9 @@ horizontal_config(struct workspace *ws, int id) if (ws->l_state.horizontal_mwin > 0) ws->l_state.horizontal_mwin--; break; + case SWM_ARG_ID_STACKBALANCE: + ws->l_state.horizontal_msize = SWM_H_SLICE / (ws->l_state.horizontal_stacks + 1); + break; case SWM_ARG_ID_STACKINC: ws->l_state.horizontal_stacks++; break; @@ -4891,7 +5449,7 @@ void max_stack(struct workspace *ws, struct swm_geometry *g) { struct swm_geometry gg = *g; - struct ws_win *w, *win = NULL, *parent = NULL; + struct ws_win *w, *win = NULL, *parent = NULL, *tmpw; int winno; DNPRINTF(SWM_D_STACK, "max_stack: workspace: %d\n", ws->idx); @@ -4899,8 +5457,8 @@ max_stack(struct workspace *ws, struct swm_geometry *g) if (ws == NULL) return; - winno = count_win(ws, 0); - if (winno == 0 && count_win(ws, 1) == 0) + winno = count_win(ws, false); + if (winno == 0 && count_win(ws, true) == 0) return; /* Figure out which top level window should be visible. */ @@ -4942,30 +5500,35 @@ max_stack(struct workspace *ws, struct swm_geometry *g) if (X(w) != gg.x || Y(w) != gg.y || WIDTH(w) != gg.w || HEIGHT(w) != gg.h) { w->g = gg; - if (bar_enabled && ws->bar_enabled){ - w->bordered = 1; - } else { - w->bordered = 0; + + if (disable_border && !(bar_enabled && ws->bar_enabled)) { + w->bordered = false; WIDTH(w) += 2 * border_width; HEIGHT(w) += 2 * border_width; + } else { + w->bordered = true; + X(w) += border_width; + Y(w) += border_width; } update_window(w); } } - if (TRANS(win)) { - parent = find_window(win->transient); + /* If transient, stack parent and its children. */ + if (TRANS(win) && (parent = find_window(win->transient))) { raise_window(parent); - TAILQ_FOREACH(w, &ws->stack, stack_entry) + TAILQ_FOREACH_SAFE(w, &ws->stack, stack_entry, tmpw) if (w->transient == parent->id) raise_window(w); } + /* Make sure focus window is on top. */ raise_window(win); - TAILQ_FOREACH(w, &ws->stack, stack_entry) + /* Stack any children of focus window. */ + TAILQ_FOREACH_SAFE(w, &ws->stack, stack_entry, tmpw) if (w->transient == win->id) raise_window(w); @@ -4976,7 +5539,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; @@ -4998,7 +5561,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 * @@ -5020,11 +5583,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 @@ -5038,7 +5603,7 @@ send_to_ws(struct swm_region *r, union arg *args) if (win->ws->idx == wsid) return; - win_to_ws(win, wsid, 1); + win_to_ws(win, wsid, true); /* Set new focus on target ws. */ if (focus_mode != SWM_FOCUS_FOLLOW) { @@ -5047,7 +5612,7 @@ send_to_ws(struct swm_region *r, union arg *args) win->ws->focus_pending = NULL; if (win->ws->focus_prev) - update_window_color(win->ws->focus_prev); + draw_frame(win->ws->focus_prev); } DNPRINTF(SWM_D_STACK, "send_to_ws: focus_pending: %#x, focus: %#x, " @@ -5059,27 +5624,60 @@ send_to_ws(struct swm_region *r, union arg *args) ewmh_apply_flags(win, win->ewmh_flags & ~EWMH_F_MAXIMIZED); ewmh_update_wm_state(win); - /* Restack and set new focus on current ws. */ - if (FLOATING(win)) - load_float_geom(win); + /* Restack focused region. */ + stack(r); + + /* If destination ws has a region, restack. */ + if (win->ws->r) { + if (FLOATING(win)) + load_float_geom(win); - stack(); + stack(win->ws->r); + } + /* Set new focus on current ws. */ if (focus_mode != SWM_FOCUS_FOLLOW) { if (r->ws->focus != NULL) { focus_win(r->ws->focus); } else { - xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, r->id, - XCB_CURRENT_TIME); - bar_draw(); + DNPRINTF(SWM_D_FOCUS, "send_to_ws: set_input_focus: " + "%#x, revert-to: parent, time: 0\n", r->id); + xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, + r->id, XCB_CURRENT_TIME); + bar_draw(r->bar); } } + center_pointer(r); focus_flush(); } +/* Transfer focused window to region-relative workspace and focus. */ +void +send_to_rg_relative(struct binding *bp, struct swm_region *r, union arg *args) +{ + union arg args_abs; + struct swm_region *r_other; + + if (args->id == 1) { + r_other = TAILQ_NEXT(r, entry); + if (r_other == NULL) + r_other = TAILQ_FIRST(&r->s->rl); + } else { + r_other = TAILQ_PREV(r, swm_region_list, entry); + if (r_other == NULL) + r_other = TAILQ_LAST(&r->s->rl, swm_region_list); + } + + /* Map relative to absolute */ + args_abs = *args; + args_abs.id = r_other->ws->idx; + + send_to_ws(bp, r, &args_abs); +} + void -win_to_ws(struct ws_win *win, int wsid, int unfocus) +win_to_ws(struct ws_win *win, int wsid, bool unfocus) { struct ws_win *parent; struct workspace *ws, *nws, *pws; @@ -5173,9 +5771,10 @@ win_to_ws(struct ws_win *win, int wsid, int 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, @@ -5185,31 +5784,58 @@ pressbutton(struct swm_region *r, union arg *args) } void -raise_toggle(struct swm_region *r, union arg *args) +raise_focus(struct binding *bp, struct swm_region *r, union arg *args) { - /* suppress unused warning since var is needed */ - (void)args; + struct ws_win *win; + uint32_t val; - if (r == NULL || r->ws == NULL) - return; + /* Suppress warning. */ + (void)bp; + (void)args; - if (r->ws->focus && MAXIMIZED(r->ws->focus)) + if (r == NULL || r->ws == NULL || r->ws->focus == NULL) return; - r->ws->always_raise = !r->ws->always_raise; - - /* bring floaters back to top */ - if (!r->ws->always_raise) - stack(); + win = r->ws->focus; + r->ws->focus_raise = win; + raise_window(win); - focus_flush(); + /* Temporarily override stacking order also in the stack */ + if (!FLOATING(win)) { + val = XCB_STACK_MODE_ABOVE; + xcb_configure_window(conn, win->frame, + XCB_CONFIG_WINDOW_STACK_MODE, &val); + } } void -iconify(struct swm_region *r, union arg *args) +raise_toggle(struct binding *bp, struct swm_region *r, union arg *args) { - struct ws_win *w; - /* suppress unused warning since var is needed */ + /* Suppress warning. */ + (void)bp; + (void)args; + + if (r == NULL || r->ws == NULL) + return; + + if (r->ws->focus && MAXIMIZED(r->ws->focus)) + return; + + r->ws->always_raise = !r->ws->always_raise; + + /* Update focused win stacking order based on new always_raise value. */ + raise_window(r->ws->focus); + + focus_flush(); +} + +void +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) @@ -5218,7 +5844,7 @@ iconify(struct swm_region *r, union arg *args) ewmh_apply_flags(w, w->ewmh_flags | EWMH_F_HIDDEN); ewmh_update_wm_state(w); - stack(); + stack(r); focus_flush(); } @@ -5257,13 +5883,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) @@ -5306,10 +5934,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) @@ -5328,12 +5958,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) @@ -5371,12 +6003,13 @@ 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; xcb_window_t w; - uint32_t wa[2]; + uint32_t wa[3]; + xcb_screen_t *screen; int i, width, height; char s[8]; FILE *lfile; @@ -5387,6 +6020,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; @@ -5397,6 +6032,9 @@ search_win(struct swm_region *r, union arg *args) if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL) return; + if ((screen = get_screen(r->s->idx)) == NULL) + errx(1, "ERROR: can't get screen %d.", r->s->idx); + TAILQ_INIT(&search_wl); i = 1; @@ -5420,6 +6058,7 @@ search_win(struct swm_region *r, union arg *args) w = xcb_generate_id(conn); wa[0] = r->s->c[SWM_S_COLOR_FOCUS].pixel; wa[1] = r->s->c[SWM_S_COLOR_UNFOCUS].pixel; + wa[2] = screen->default_colormap; if (bar_font_legacy) { XmbTextExtents(bar_fs, s, len, &l_ibox, &l_lbox); @@ -5432,10 +6071,10 @@ search_win(struct swm_region *r, union arg *args) height = bar_font->height + 4; } - xcb_create_window(conn, XCB_COPY_FROM_PARENT, w, win->id, 0, 0, + xcb_create_window(conn, screen->root_depth, w, win->frame, 0, 0, width, height, 1, XCB_WINDOW_CLASS_INPUT_OUTPUT, - XCB_COPY_FROM_PARENT, XCB_CW_BACK_PIXEL | - XCB_CW_BORDER_PIXEL, wa); + screen->root_visual, XCB_CW_BACK_PIXEL | + XCB_CW_BORDER_PIXEL | XCB_CW_COLORMAP, wa); xcb_map_window(conn, w); @@ -5460,7 +6099,7 @@ search_win(struct swm_region *r, union arg *args) DefaultVisual(display, r->s->idx), DefaultColormap(display, r->s->idx)); - XftDrawStringUtf8(draw, &bar_font_color, bar_font, 2, + XftDrawStringUtf8(draw, &search_font_color, bar_font, 2, (HEIGHT(r->bar) + bar_font->height) / 2 - bar_font->descent, (FcChar8 *)s, len); @@ -5500,7 +6139,7 @@ search_resp_uniconify(const char *resp, size_t len) /* XXX this should be a callback to generalize */ ewmh_apply_flags(win, win->ewmh_flags & ~EWMH_F_HIDDEN); ewmh_update_wm_state(win); - stack(); + stack(search_r); free(s); break; } @@ -5551,7 +6190,7 @@ ewmh_update_desktop_names(void) ++len; } - if((name_list = calloc(len, sizeof(char))) == NULL) + if ((name_list = calloc(len, sizeof(char))) == NULL) err(1, "update_desktop_names: calloc: failed to " "allocate memory."); @@ -5657,10 +6296,13 @@ ewmh_update_current_desktop(void) int num_screens, i; num_screens = get_screen_count(); - for (i = 0; i < num_screens; ++i) - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, - screens[i].root, ewmh[_NET_CURRENT_DESKTOP].atom, - XCB_ATOM_CARDINAL, 32, 1, &screens[i].r_focus->ws->idx); + for (i = 0; i < num_screens; ++i) { + if (screens[i].r_focus) + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, + screens[i].root, ewmh[_NET_CURRENT_DESKTOP].atom, + XCB_ATOM_CARDINAL, 32, 1, + &screens[i].r_focus->ws->idx); + } } void @@ -5728,7 +6370,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 @@ -5824,8 +6466,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); @@ -5859,11 +6503,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) @@ -5880,21 +6525,23 @@ maximize_toggle(struct swm_region *r, union arg *args) ewmh_apply_flags(w, w->ewmh_flags ^ EWMH_F_MAXIMIZED); ewmh_update_wm_state(w); - stack(); + stack(r); if (w == w->ws->focus) focus_win(w); + center_pointer(r); focus_flush(); DNPRINTF(SWM_D_MISC, "maximize_toggle: done\n"); } 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) @@ -5911,15 +6558,42 @@ floating_toggle(struct swm_region *r, union arg *args) ewmh_apply_flags(w, w->ewmh_flags ^ EWMH_F_ABOVE); ewmh_update_wm_state(w); - stack(); + stack(r); if (w == w->ws->focus) focus_win(w); + center_pointer(r); focus_flush(); DNPRINTF(SWM_D_MISC, "floating_toggle: done\n"); } +void +fullscreen_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) + return; + + DNPRINTF(SWM_D_MISC, "fullscreen_toggle: win %#x\n", w->id); + + ewmh_apply_flags(w, w->ewmh_flags ^ EWMH_F_FULLSCREEN); + ewmh_update_wm_state(w); + + stack(r); + + if (w == w->ws->focus) + focus_win(w); + + center_pointer(r); + focus_flush(); + DNPRINTF(SWM_D_MISC, "fullscreen_toggle: done\n"); +} void region_containment(struct ws_win *win, struct swm_region *r, int opts) { @@ -5933,10 +6607,10 @@ region_containment(struct ws_win *win, struct swm_region *r, int opts) * side of the region boundary. Positive values indicate the side of * the window has passed beyond the region boundary. */ - rt = opts & SWM_CW_RIGHT ? MAX_X(win) + BORDER(win) - MAX_X(r) : bw; - lt = opts & SWM_CW_LEFT ? X(r) - X(win) + BORDER(win) : bw; - bm = opts & SWM_CW_BOTTOM ? MAX_Y(win) + BORDER(win) - MAX_Y(r) : bw; - tp = opts & SWM_CW_TOP ? Y(r) - Y(win) + BORDER(win) : bw; + rt = opts & SWM_CW_RIGHT ? MAX_X(win) - MAX_X(r) : bw; + lt = opts & SWM_CW_LEFT ? X(r) - X(win) : bw; + bm = opts & SWM_CW_BOTTOM ? MAX_Y(win) - MAX_Y(r) : bw; + tp = opts & SWM_CW_TOP ? Y(r) - Y(win) : bw; DNPRINTF(SWM_D_MISC, "region_containment: win %#x, rt: %d, lt: %d, " "bm: %d, tp: %d, SOFTBOUNDARY: %s, HARDBOUNDARY: %s\n", win->id, rt, @@ -5971,32 +6645,32 @@ constrain_window(struct ws_win *win, struct swm_geometry *b, int *opts) YESNO(*opts & SWM_CW_BOTTOM), YESNO(*opts & SWM_CW_TOP), YESNO(*opts & SWM_CW_RESIZABLE)); - if ((*opts & SWM_CW_RIGHT) && MAX_X(win) + BORDER(win) > b->x + b->w) { + if ((*opts & SWM_CW_RIGHT) && MAX_X(win) > b->x + b->w) { if (*opts & SWM_CW_RESIZABLE) - WIDTH(win) = b->x + b->w - X(win) - BORDER(win); + WIDTH(win) = b->x + b->w - X(win); else - X(win) = b->x + b->w - WIDTH(win) - BORDER(win); + X(win) = b->x + b->w - WIDTH(win); } - if ((*opts & SWM_CW_LEFT) && X(win) + BORDER(win) < b->x) { + if ((*opts & SWM_CW_LEFT) && X(win) < b->x) { if (*opts & SWM_CW_RESIZABLE) - WIDTH(win) -= b->x - X(win) - BORDER(win); + WIDTH(win) -= b->x - X(win); - X(win) = b->x - BORDER(win); + X(win) = b->x; } - if ((*opts & SWM_CW_BOTTOM) && MAX_Y(win) + BORDER(win) > b->y + b->h) { + if ((*opts & SWM_CW_BOTTOM) && MAX_Y(win) > b->y + b->h) { if (*opts & SWM_CW_RESIZABLE) - HEIGHT(win) = b->y + b->h - Y(win) - BORDER(win); + HEIGHT(win) = b->y + b->h - Y(win); else - Y(win) = b->y + b->h - HEIGHT(win) - BORDER(win); + Y(win) = b->y + b->h - HEIGHT(win); } - if ((*opts & SWM_CW_TOP) && Y(win) + BORDER(win) < b->y) { + if ((*opts & SWM_CW_TOP) && Y(win) < b->y) { if (*opts & SWM_CW_RESIZABLE) - HEIGHT(win) -= b->y - Y(win) - BORDER(win); + HEIGHT(win) -= b->y - Y(win); - Y(win) = b->y - BORDER(win); + Y(win) = b->y; } if (*opts & SWM_CW_RESIZABLE) { @@ -6008,9 +6682,22 @@ constrain_window(struct ws_win *win, struct swm_geometry *b, int *opts) } void -update_window_color(struct ws_win *win) +draw_frame(struct ws_win *win) { - uint32_t *pixel; + xcb_rectangle_t rect[4]; + uint32_t *pixel; + int n = 0; + + if (win->frame == XCB_WINDOW_NONE) { + DNPRINTF(SWM_D_EVENT, "draw_frame: win %#x not " + "reparented.\n", win->id); + return; + } + + if (!win->bordered) { + DNPRINTF(SWM_D_EVENT, "draw_frame: win %#x frame " + "disabled\n", win->id); + } if (WS_FOCUSED(win->ws) && win->ws->focus == win) pixel = MAXIMIZED(win) ? @@ -6021,8 +6708,29 @@ update_window_color(struct ws_win *win) &win->s->c[SWM_S_COLOR_UNFOCUS_MAXIMIZED].pixel : &win->s->c[SWM_S_COLOR_UNFOCUS].pixel; - xcb_change_window_attributes(conn, win->id, - XCB_CW_BORDER_PIXEL, pixel); + /* Top (with corners) */ + rect[n].x = 0; + rect[n].y = 0; + rect[n].width = WIDTH(win) + 2 * border_width; + rect[n].height = border_width; + /* Left (without corners) */ + rect[++n].x = 0; + rect[n].y = border_width; + rect[n].width = border_width; + rect[n].height = HEIGHT(win); + /* Right (without corners) */ + rect[++n].x = border_width + WIDTH(win); + rect[n].y = border_width; + rect[n].width = border_width; + rect[n].height = HEIGHT(win); + /* Bottom (with corners)*/ + rect[++n].x = 0; + rect[n].y = border_width + HEIGHT(win); + rect[n].width = WIDTH(win) + 2 * border_width; + rect[n].height = border_width; + + xcb_change_gc(conn, win->s->bar_gc, XCB_GC_FOREGROUND, pixel); + xcb_poly_fill_rectangle(conn, win->frame, win->s->bar_gc, 4, rect); } void @@ -6031,37 +6739,139 @@ update_window(struct ws_win *win) uint16_t mask; uint32_t wc[5]; + if (win->frame == XCB_WINDOW_NONE) { + DNPRINTF(SWM_D_EVENT, "update_window: skip win %#x; " + "not reparented.\n", win->id); + return; + } + mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT | XCB_CONFIG_WINDOW_BORDER_WIDTH; - wc[0] = X(win); - wc[1] = Y(win); + + /* Reconfigure frame. */ + if (win->bordered) { + wc[0] = X(win) - border_width; + wc[1] = Y(win) - border_width; + wc[2] = WIDTH(win) + 2 * border_width; + wc[3] = HEIGHT(win) + 2 * border_width; + } else { + wc[0] = X(win); + wc[1] = Y(win); + wc[2] = WIDTH(win); + wc[3] = HEIGHT(win); + } + + wc[4] = 0; + + DNPRINTF(SWM_D_EVENT, "update_window: win %#x frame %#x, (x,y) w x h: " + "(%d,%d) %d x %d, bordered: %s\n", win->id, win->frame, wc[0], + wc[1], wc[2], wc[3], YESNO(win->bordered)); + + xcb_configure_window(conn, win->frame, mask, wc); + + /* Reconfigure client window. */ + wc[0] = wc[1] = win->bordered ? border_width : 0; wc[2] = WIDTH(win); wc[3] = HEIGHT(win); - wc[4] = BORDER(win); DNPRINTF(SWM_D_EVENT, "update_window: win %#x, (x,y) w x h: " "(%d,%d) %d x %d, bordered: %s\n", win->id, wc[0], wc[1], wc[2], wc[3], YESNO(win->bordered)); - xcb_configure_window(conn, win->id, mask, wc); + + /* ICCCM 4.2.3 Synthetic ConfigureNotify when moved and not resized. */ + if ((X(win) != win->g_prev.x || Y(win) != win->g_prev.y) && + (win->java || (WIDTH(win) == win->g_prev.w && + HEIGHT(win) == win->g_prev.h))) { + /* Java has special needs when moved together with a resize. */ + config_win(win, NULL); + } + + win->g_prev = win->g; +} + +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 dowait) +{ + 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 (dowait) + 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, resizing; + 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, step = false; if (win == NULL) return; @@ -6087,7 +6897,7 @@ resize(struct ws_win *win, union arg *args) ~EWMH_F_MAXIMIZED); ewmh_update_wm_state(win); - stack(); + stack(r); focus_flush(); @@ -6097,27 +6907,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); @@ -6143,7 +6953,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 : @@ -6154,18 +6964,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 = 1; - while (resizing && (evt = xcb_wait_for_event(conn))) { + resizing = true; + 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 = 0; + 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; @@ -6174,7 +6997,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; @@ -6194,7 +7017,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; @@ -6221,8 +7044,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); @@ -6253,16 +7084,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); + 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(); } @@ -6277,11 +7127,14 @@ regionize(struct ws_win *win, int x, int y) r = region_under(win->s, X(win) + WIDTH(win) / 2, Y(win) + HEIGHT(win) / 2); - if (r != NULL && r != win->ws->r) { - if (clear_maximized(r->ws) > 0) - stack(); + if (r && r != win->ws->r) { + clear_maximized(r->ws); - win_to_ws(win, r->ws->idx, 0); + win_to_ws(win, r->ws->idx, false); + + /* Stack old and new region. */ + stack(r); + stack(win->ws->r); /* Set focus on new ws. */ unfocus_win(r->ws->focus); @@ -6295,14 +7148,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, moving, restack = 0; xcb_query_pointer_reply_t *qpr = NULL; xcb_generic_event_t *evt; xcb_motion_notify_event_t *mne; + bool moving, restack = false, step = false; if (win == NULL) return; @@ -6322,7 +7175,7 @@ move(struct ws_win *win, union arg *args) if (!(ABOVE(win) || TRANS(win)) || MAXIMIZED(win)) { store_float_geom(win); - restack = 1; + restack = true; } ewmh_apply_flags(win, (win->ewmh_flags | SWM_F_MANUAL | EWMH_F_ABOVE) & @@ -6330,7 +7183,7 @@ move(struct ws_win *win, union arg *args) ewmh_update_wm_state(win); if (restack) - stack(); + stack(r); focus_flush(); @@ -6340,28 +7193,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); @@ -6381,21 +7233,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 = 1; - while (moving && (evt = xcb_wait_for_event(conn))) { + moving = true; + 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 = 0; + 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; @@ -6409,6 +7280,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, @@ -6418,7 +7295,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; @@ -6436,8 +7313,8 @@ move(struct ws_win *win, union arg *args) store_float_geom(win); /* New region set to fullscreen layout. */ - if (win->ws->cur_layout == &layouts[SWM_MAX_STACK]) { - stack(); + if (win->ws->r && win->ws->cur_layout == &layouts[SWM_MAX_STACK]) { + stack(win->ws->r); focus_flush(); } @@ -6448,198 +7325,192 @@ 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)) - return; + if (args->id) { + /* move_* uses focus window. */ + if (r->ws) + win = r->ws->focus; - move(win, args); - focus_flush(); -} + /* Disallow move_ on tiled. */ + if (win && !(TRANS(win) || ABOVE(win))) + return; + } 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); + } -/* key definitions */ -struct keyfunc { - char name[SWM_FUNCNAME_LEN]; - void (*func)(struct swm_region *r, union arg *); - union arg args; -} keyfuncs[KF_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_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_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} }, -}; + if (win == NULL) + return; -int -key_cmp(struct key *kp1, struct key *kp2) -{ - if (kp1->keysym < kp2->keysym) - return (-1); - if (kp1->keysym > kp2->keysym) - return (1); + move_win(win, bp, args->id); - if (kp1->mod < kp2->mod) - return (-1); - if (kp1->mod > kp2->mod) - return (1); + if (args->id && bp->type == KEYBIND) + center_pointer(r); - return (0); + focus_flush(); } -/* mouse */ -enum { client_click, root_click }; -struct button { - unsigned int action; - unsigned int mask; - unsigned int button; - void (*func)(struct ws_win *, union arg *); +/* action definitions */ +struct action { + char name[SWM_FUNCNAME_LEN]; + void (*func)(struct binding *, struct swm_region *, + union arg *); + uint32_t flags; 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 +} actions[FN_INVALID + 2] = { + /* name function argument */ + { "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} }, + { "fullscreen_toggle", fullscreen_toggle, 0, {0} }, + { "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} }, + { "mvrg_next", send_to_rg_relative, 0, {.id = 1} }, + { "mvrg_prev", send_to_rg_relative, 0, {.id = -1} }, + { "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", raise_focus, 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} }, + /* SWM_DEBUG actions MUST be here: */ + { "debug_toggle", debug_toggle, 0, {0} }, + { "dumpwins", dumpwins, 0, {0} }, + /* ALWAYS 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 @@ -6755,7 +7626,7 @@ spawn_custom(struct swm_region *r, union arg *args, const char *spawn_name) return; a.argv = real_args; if (fork() == 0) - spawn(r->ws->idx, &a, 1); + spawn(r->ws->idx, &a, true); for (i = 0; i < spawn_argc; i++) free(real_args[i]); @@ -6792,7 +7663,7 @@ spawn_select(struct swm_region *r, union arg *args, const char *spawn_name, err(1, "dup2"); close(select_list_pipe[1]); close(select_resp_pipe[0]); - spawn(r->ws->idx, &a, 0); + spawn(r->ws->idx, &a, false); break; default: /* parent */ close(select_list_pipe[0]); @@ -6808,8 +7679,8 @@ spawn_select(struct swm_region *r, union arg *args, const char *spawn_name, /* Argument tokenizer. */ char * argsep(char **sp) { - int single_quoted = 0, double_quoted = 0; char *arg, *cp, *next; + bool single_quoted = false, double_quoted = false; if (*sp == NULL) return NULL; @@ -6908,6 +7779,16 @@ spawn_remove(struct spawn_prog *sp) DNPRINTF(SWM_D_SPAWN, "spawn_remove: leave\n"); } +void +clear_spawns(void) +{ + struct spawn_prog *sp; + + while ((sp = TAILQ_FIRST(&spawns)) != NULL) { + spawn_remove(sp); + } +} + struct spawn_prog* spawn_find(const char *name) { @@ -6964,18 +7845,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; @@ -7034,63 +7914,86 @@ 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) *mod |= XCB_MOD_MASK_4; + else if (strncmp(name, "Mod5", SWM_MODNAME_SIZE) == 0) + *mod |= XCB_MOD_MASK_5; else if (strncasecmp(name, "SHIFT", SWM_MODNAME_SIZE) == 0) *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); } @@ -7103,97 +8006,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); - key_insert(mod, ks, kfid, spawn_name); - DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n"); + if (aid != FN_INVALID) + binding_insert(mod, type, val, aid, flags, spawn_name); + + 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; @@ -7202,21 +8112,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); @@ -7226,8 +8136,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 @@ -7237,143 +8147,173 @@ 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(MODSHIFT, XK_e, FN_FULLSCREEN_TOGGLE); + 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(MODKEY, XK_r, FN_RAISE); + 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(MODKEY, XK_d, FN_DEBUG_TOGGLE); + BINDKEY(MODSHIFT, XK_d, FN_DUMPWINS); #endif -#undef MODKEY_SHIFT +#undef BINDKEY +#undef BINDKEYSPAWN +} + +void +setup_btnbindings(void) +{ + 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 + +void +clear_bindings(void) +{ + struct binding *bp; + + while ((bp = RB_ROOT(&bindings))) + binding_remove(bp); } void -clear_keys(void) +clear_keybindings(void) { - struct key *kp; + struct binding *bp, *bptmp; - while (RB_EMPTY(&keys) == 0) { - kp = RB_ROOT(&keys); - key_remove(kp); + RB_FOREACH_SAFE(bp, binding_tree, &bindings, bptmp) { + if (bp->type != KEYBIND) + continue; + binding_remove(bp); } } @@ -7390,11 +8330,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); @@ -7438,9 +8378,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"); @@ -7457,38 +8397,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 | modifiers[j]); 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; @@ -7496,14 +8461,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[] = { @@ -7517,12 +8525,16 @@ const char *quirkname[] = { "NOFOCUSONMAP", "FOCUSONMAP_SINGLE", "OBEYAPPFOCUSREQ", + "IGNOREPID", + "IGNORESPAWNWS", + "NOFOCUSCYCLE", + "MINIMALBORDER", }; -/* SWM_Q_WS: retain '|' for back compat for now (2009-08-11) */ -#define SWM_Q_WS "\n|+ \t" +/* SWM_Q_DELIM: retain '|' for back compat for now (2009-08-11) */ +#define SWM_Q_DELIM "\n|+ \t" int -parsequirks(const char *qstr, unsigned long *quirk) +parsequirks(const char *qstr, uint32_t *quirk, int *ws) { char *str, *cp, *name; int i; @@ -7530,14 +8542,20 @@ parsequirks(const char *qstr, unsigned long *quirk) 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_WS)) != NULL) { + while ((name = strsep(&cp, SWM_Q_DELIM)) != NULL) { if (cp) - cp += (long)strspn(cp, SWM_Q_WS); + cp += (long)strspn(cp, SWM_Q_DELIM); + + if (sscanf(name, "WS[%d]", ws) == 1) { + if (*ws > 0) + *ws -= 1; + continue; + } + for (i = 0; i < LENGTH(quirkname); i++) { if (strncasecmp(name, quirkname[i], SWM_QUIRK_LEN) == 0) { @@ -7566,14 +8584,14 @@ parsequirks(const char *qstr, unsigned long *quirk) void quirk_insert(const char *class, const char *instance, const char *name, - unsigned long quirk) + uint32_t quirk, int ws) { struct quirk *qp; - int failed = 0; char *str; + bool failed = false; DNPRINTF(SWM_D_QUIRK, "quirk_insert: class: %s, instance: %s, name: %s," - " value: %lu\n", class, instance, name, quirk); + " value: %u, ws: %d\n", class, instance, name, quirk, ws); if ((qp = malloc(sizeof *qp)) == NULL) err(1, "quirk_insert: malloc"); @@ -7590,7 +8608,7 @@ quirk_insert(const char *class, const char *instance, const char *name, if (regcomp(&qp->regex_class, str, REG_EXTENDED | REG_NOSUB)) { add_startup_exception("regex failed to compile quirk 'class' " "field: %s", class); - failed = 1; + failed = true; } DNPRINTF(SWM_D_QUIRK, "quirk_insert: compiled: %s\n", str); free(str); @@ -7600,7 +8618,7 @@ quirk_insert(const char *class, const char *instance, const char *name, if (regcomp(&qp->regex_instance, str, REG_EXTENDED | REG_NOSUB)) { add_startup_exception("regex failed to compile quirk 'instance'" " field: %s", instance); - failed = 1; + failed = true; } DNPRINTF(SWM_D_QUIRK, "quirk_insert: compiled: %s\n", str); free(str); @@ -7610,7 +8628,7 @@ quirk_insert(const char *class, const char *instance, const char *name, if (regcomp(&qp->regex_name, str, REG_EXTENDED | REG_NOSUB)) { add_startup_exception("regex failed to compile quirk 'name' " "field: %s", name); - failed = 1; + failed = true; } DNPRINTF(SWM_D_QUIRK, "quirk_insert: compiled: %s\n", str); free(str); @@ -7620,6 +8638,7 @@ quirk_insert(const char *class, const char *instance, const char *name, quirk_free(qp); } else { qp->quirk = quirk; + qp->ws = ws; TAILQ_INSERT_TAIL(&quirks, qp, entry); } DNPRINTF(SWM_D_QUIRK, "quirk_insert: leave\n"); @@ -7628,7 +8647,7 @@ quirk_insert(const char *class, const char *instance, const char *name, void quirk_remove(struct quirk *qp) { - DNPRINTF(SWM_D_QUIRK, "quirk_remove: %s:%s [%lu]\n", qp->class, + DNPRINTF(SWM_D_QUIRK, "quirk_remove: %s:%s [%u]\n", qp->class, qp->name, qp->quirk); TAILQ_REMOVE(&quirks, qp, entry); @@ -7649,45 +8668,56 @@ quirk_free(struct quirk *qp) free(qp); } +void +clear_quirks(void) +{ + struct quirk *qp; + + while ((qp = TAILQ_FIRST(&quirks)) != NULL) { + quirk_remove(qp); + } +} + void quirk_replace(struct quirk *qp, const char *class, const char *instance, - const char *name, unsigned long quirk) + const char *name, uint32_t quirk, int ws) { - DNPRINTF(SWM_D_QUIRK, "quirk_replace: %s:%s:%s [%lu]\n", qp->class, - qp->instance, qp->name, qp->quirk); + DNPRINTF(SWM_D_QUIRK, "quirk_replace: %s:%s:%s [%u], ws: %d\n", qp->class, + qp->instance, qp->name, qp->quirk, qp->ws); quirk_remove(qp); - quirk_insert(class, instance, name, quirk); + quirk_insert(class, instance, name, quirk, ws); DNPRINTF(SWM_D_QUIRK, "quirk_replace: leave\n"); } void setquirk(const char *class, const char *instance, const char *name, - unsigned long quirk) + uint32_t quirk, int ws) { struct quirk *qp; - DNPRINTF(SWM_D_QUIRK, "setquirk: enter %s:%s:%s [%lu]\n", class, - instance, name, quirk); + DNPRINTF(SWM_D_QUIRK, "setquirk: enter %s:%s:%s [%u], ws: %d\n", class, + instance, name, quirk, ws); /* Remove/replace existing quirk. */ TAILQ_FOREACH(qp, &quirks, entry) { if (strcmp(qp->class, class) == 0 && strcmp(qp->instance, instance) == 0 && strcmp(qp->name, name) == 0) { - if (quirk == 0) + if (quirk == 0 && ws == -1) quirk_remove(qp); else - quirk_replace(qp, class, instance, name, quirk); + quirk_replace(qp, class, instance, name, quirk, + ws); DNPRINTF(SWM_D_QUIRK, "setquirk: leave\n"); return; } } - /* Only insert if quirk is not NONE. */ - if (quirk) - quirk_insert(class, instance, name, quirk); + /* Only insert if quirk is not NONE or forced ws is set. */ + if (quirk || ws != -1) + quirk_insert(class, instance, name, quirk, ws); DNPRINTF(SWM_D_QUIRK, "setquirk: leave\n"); } @@ -7713,8 +8743,8 @@ setconfquirk(const char *selector, const char *value, int flags) { char *str, *cp, *class; char *instance = NULL, *name = NULL; - int retval, count = 0; - unsigned long qrks; + int retval, count = 0, ws = -1; + uint32_t qrks; /* suppress unused warning since var is needed */ (void)flags; @@ -7757,8 +8787,8 @@ setconfquirk(const char *selector, const char *value, int flags) DNPRINTF(SWM_D_CONF, "setconfquirk: class: %s, instance: %s, " "name: %s\n", class, instance, name); - if ((retval = parsequirks(value, &qrks)) == 0) - setquirk(class, instance, name, qrks); + if ((retval = parsequirks(value, &qrks, &ws)) == 0) + setquirk(class, instance, name, qrks, ws); free(str); return (retval); @@ -7767,19 +8797,32 @@ setconfquirk(const char *selector, const char *value, int flags) void setup_quirks(void) { - setquirk("MPlayer", "xv", ".*", SWM_Q_FLOAT | SWM_Q_FULLSCREEN | SWM_Q_FOCUSPREV); - setquirk("OpenOffice.org 3.2", "VCLSalFrame", ".*", SWM_Q_FLOAT); - setquirk("Firefox-bin", "firefox-bin", ".*", SWM_Q_TRANSSZ); - setquirk("Firefox", "Dialog", ".*", SWM_Q_FLOAT); - setquirk("Gimp", "gimp", ".*", SWM_Q_FLOAT | SWM_Q_ANYWHERE); - setquirk("XTerm", "xterm", ".*", SWM_Q_XTERM_FONTADJ); - setquirk("xine", "Xine Window", ".*", SWM_Q_FLOAT | SWM_Q_ANYWHERE); - setquirk("Xitk", "Xitk Combo", ".*", SWM_Q_FLOAT | SWM_Q_ANYWHERE); - setquirk("xine", "xine Panel", ".*", SWM_Q_FLOAT | SWM_Q_ANYWHERE); - setquirk("Xitk", "Xine Window", ".*", SWM_Q_FLOAT | SWM_Q_ANYWHERE); - setquirk("xine", "xine Video Fullscreen Window", ".*", SWM_Q_FULLSCREEN | SWM_Q_FLOAT); - setquirk("pcb", "pcb", ".*", SWM_Q_FLOAT); - setquirk("SDL_App", "SDL_App", ".*", SWM_Q_FLOAT | SWM_Q_FULLSCREEN); + setquirk("MPlayer", "xv", ".*", + SWM_Q_FLOAT | SWM_Q_FULLSCREEN | SWM_Q_FOCUSPREV, -1); + setquirk("OpenOffice.org 3.2", "VCLSalFrame", ".*", + SWM_Q_FLOAT, -1); + setquirk("Firefox-bin", "firefox-bin", ".*", + SWM_Q_TRANSSZ, -1); + setquirk("Firefox", "Dialog", ".*", + SWM_Q_FLOAT, -1); + setquirk("Gimp", "gimp", ".*", + SWM_Q_FLOAT | SWM_Q_ANYWHERE, -1); + setquirk("XTerm", "xterm", ".*", + SWM_Q_XTERM_FONTADJ, -1); + setquirk("xine", "Xine Window", ".*", + SWM_Q_FLOAT | SWM_Q_ANYWHERE, -1); + setquirk("Xitk", "Xitk Combo", ".*", + SWM_Q_FLOAT | SWM_Q_ANYWHERE, -1); + setquirk("xine", "xine Panel", ".*", + SWM_Q_FLOAT | SWM_Q_ANYWHERE, -1); + setquirk("Xitk", "Xine Window", ".*", + SWM_Q_FLOAT | SWM_Q_ANYWHERE, -1); + setquirk("xine", "xine Video Fullscreen Window", ".*", + SWM_Q_FULLSCREEN | SWM_Q_FLOAT, -1); + setquirk("pcb", "pcb", ".*", + SWM_Q_FLOAT, -1); + setquirk("SDL_App", "SDL_App", ".*", + SWM_Q_FLOAT | SWM_Q_FULLSCREEN, -1); } /* conf file stuff */ @@ -7809,6 +8852,8 @@ enum { SWM_S_FOCUS_DEFAULT, SWM_S_FOCUS_MODE, SWM_S_ICONIC_ENABLED, + SWM_S_JAVA_WORKAROUND, + SWM_S_MAXIMIZE_HIDE_BAR, SWM_S_REGION_PADDING, SWM_S_SPAWN_ORDER, SWM_S_SPAWN_TERM, @@ -7817,11 +8862,15 @@ enum { SWM_S_STACK_ENABLED, SWM_S_TERM_WIDTH, SWM_S_TILE_GAP, + SWM_S_URGENT_COLLAPSE, SWM_S_URGENT_ENABLED, SWM_S_VERBOSE_LAYOUT, + SWM_S_WARP_FOCUS, + SWM_S_WARP_POINTER, SWM_S_WINDOW_CLASS_ENABLED, SWM_S_WINDOW_INSTANCE_ENABLED, SWM_S_WINDOW_NAME_ENABLED, + SWM_S_WORKSPACE_CLAMP, SWM_S_WORKSPACE_LIMIT, SWM_S_WORKSPACE_NAME, }; @@ -7831,7 +8880,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: @@ -7840,7 +8889,7 @@ setconfvalue(const char *selector, const char *value, int flags) err(1, "setconfvalue: bar_action"); break; case SWM_S_BAR_AT_BOTTOM: - bar_at_bottom = atoi(value); + bar_at_bottom = (atoi(value) != 0); break; case SWM_S_BAR_BORDER_WIDTH: bar_border_width = atoi(value); @@ -7851,7 +8900,7 @@ setconfvalue(const char *selector, const char *value, int flags) /* No longer needed; leave to not break old conf files. */ break; case SWM_S_BAR_ENABLED: - bar_enabled = atoi(value); + bar_enabled = (atoi(value) != 0); break; case SWM_S_BAR_ENABLED_WS: ws_id = atoi(selector) - 1; @@ -7862,7 +8911,7 @@ setconfvalue(const char *selector, const char *value, int flags) num_screens = get_screen_count(); for (i = 0; i < num_screens; i++) { ws = (struct workspace *)&screens[i].ws; - ws[ws_id].bar_enabled = atoi(value); + ws[ws_id].bar_enabled = (atoi(value) != 0); } break; case SWM_S_BAR_FONT: @@ -7876,15 +8925,15 @@ 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)) { - bar_font_legacy = 0; + bar_font_legacy = false; break; } } @@ -7917,7 +8966,7 @@ setconfvalue(const char *selector, const char *value, int flags) boundary_width = 0; break; case SWM_S_CLOCK_ENABLED: - clock_enabled = atoi(value); + clock_enabled = (atoi(value) != 0); break; case SWM_S_CLOCK_FORMAT: #ifndef SWM_DENY_CLOCK_FORMAT @@ -7927,10 +8976,10 @@ setconfvalue(const char *selector, const char *value, int flags) #endif break; case SWM_S_CYCLE_EMPTY: - cycle_empty = atoi(value); + cycle_empty = (atoi(value) != 0); break; case SWM_S_CYCLE_VISIBLE: - cycle_visible = atoi(value); + cycle_visible = (atoi(value) != 0); break; case SWM_S_DIALOG_RATIO: dialog_ratio = atof(value); @@ -7938,7 +8987,7 @@ setconfvalue(const char *selector, const char *value, int flags) dialog_ratio = .6; break; case SWM_S_DISABLE_BORDER: - disable_border = atoi(value); + disable_border = (atoi(value) != 0); break; case SWM_S_FOCUS_CLOSE: if (strcmp(value, "first") == 0) @@ -7953,7 +9002,7 @@ setconfvalue(const char *selector, const char *value, int flags) errx(1, "focus_close"); break; case SWM_S_FOCUS_CLOSE_WRAP: - focus_close_wrap = atoi(value); + focus_close_wrap = (atoi(value) != 0); break; case SWM_S_FOCUS_DEFAULT: if (strcmp(value, "last") == 0) @@ -7975,7 +9024,13 @@ setconfvalue(const char *selector, const char *value, int flags) errx(1, "focus_mode"); break; case SWM_S_ICONIC_ENABLED: - iconic_enabled = atoi(value); + iconic_enabled = (atoi(value) != 0); + break; + case SWM_S_JAVA_WORKAROUND: + java_workaround = (atoi(value) != 0); + break; + case SWM_S_MAXIMIZE_HIDE_BAR: + maximize_hide_bar = atoi(value); break; case SWM_S_REGION_PADDING: region_padding = atoi(value); @@ -8005,7 +9060,7 @@ setconfvalue(const char *selector, const char *value, int flags) /* No longer needed; leave to not break old conf files. */ break; case SWM_S_STACK_ENABLED: - stack_enabled = atoi(value); + stack_enabled = (atoi(value) != 0); break; case SWM_S_TERM_WIDTH: term_width = atoi(value); @@ -8015,11 +9070,14 @@ setconfvalue(const char *selector, const char *value, int flags) case SWM_S_TILE_GAP: tile_gap = atoi(value); break; + case SWM_S_URGENT_COLLAPSE: + urgent_collapse = (atoi(value) != 0); + break; case SWM_S_URGENT_ENABLED: - urgent_enabled = atoi(value); + urgent_enabled = (atoi(value) != 0); break; case SWM_S_VERBOSE_LAYOUT: - verbose_layout = atoi(value); + verbose_layout = (atoi(value) != 0); for (i = 0; layouts[i].l_stack != NULL; i++) { if (verbose_layout) layouts[i].l_string = fancy_stacker; @@ -8027,14 +9085,23 @@ setconfvalue(const char *selector, const char *value, int flags) layouts[i].l_string = plain_stacker; } break; - case SWM_S_WINDOW_CLASS_ENABLED: - window_class_enabled = atoi(value); + case SWM_S_WARP_FOCUS: + warp_focus = (atoi(value) != 0); + break; + case SWM_S_WARP_POINTER: + warp_pointer = (atoi(value) != 0); + break; + case SWM_S_WINDOW_CLASS_ENABLED: + window_class_enabled = (atoi(value) != 0); break; case SWM_S_WINDOW_INSTANCE_ENABLED: - window_instance_enabled = atoi(value); + window_instance_enabled = (atoi(value) != 0); break; case SWM_S_WINDOW_NAME_ENABLED: - window_name_enabled = atoi(value); + window_name_enabled = (atoi(value) != 0); + break; + case SWM_S_WORKSPACE_CLAMP: + workspace_clamp = (atoi(value) != 0); break; case SWM_S_WORKSPACE_LIMIT: workspace_limit = atoi(value); @@ -8092,6 +9159,8 @@ setconfmodkey(const char *selector, const char *value, int flags) update_modkey(XCB_MOD_MASK_3); else if (strncasecmp(value, "Mod4", strlen("Mod4")) == 0) update_modkey(XCB_MOD_MASK_4); + else if (strncasecmp(value, "Mod5", strlen("Mod5")) == 0) + update_modkey(XCB_MOD_MASK_5); else return (1); return (0); @@ -8155,7 +9224,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; @@ -8175,7 +9244,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 @@ -8194,18 +9263,18 @@ setautorun(const char *selector, const char *value, int flags) err(1, "setautorun: realloc"); a.argv[argc - 1] = ap; } - free(sp); if ((a.argv = realloc(a.argv, (argc + 1) * sizeof(char *))) == NULL) err(1, "setautorun: realloc"); a.argv[argc] = NULL; if ((pid = fork()) == 0) { - spawn(ws_id, &a, 1); + spawn(ws_id, &a, true); /* NOTREACHED */ _exit(1); } free(a.argv); + free(str); /* parent */ p = find_pid(pid); @@ -8225,10 +9294,11 @@ setautorun(const char *selector, const char *value, int flags) int setlayout(const char *selector, const char *value, int flags) { - int ws_id, i, x, mg, ma, si, ar, f = 0; + struct workspace *ws; + int ws_id, i, x, mg, ma, si, ar; int st = SWM_V_STACK, num_screens; char s[1024]; - struct workspace *ws; + bool f = false; /* suppress unused warnings since vars are needed */ (void)selector; @@ -8251,12 +9321,12 @@ setlayout(const char *selector, const char *value, int flags) st = SWM_V_STACK; else if (strcasecmp(s, "vertical_flip") == 0) { st = SWM_V_STACK; - f = 1; + f = true; } else if (strcasecmp(s, "horizontal") == 0) st = SWM_H_STACK; else if (strcasecmp(s, "horizontal_flip") == 0) { st = SWM_H_STACK; - f = 1; + f = true; } else if (strcasecmp(s, "fullscreen") == 0) st = SWM_MAX_STACK; else @@ -8269,7 +9339,7 @@ setlayout(const char *selector, const char *value, int flags) ws = (struct workspace *)&screens[i].ws; ws[ws_id].cur_layout = &layouts[st]; - ws[ws_id].always_raise = ar; + ws[ws_id].always_raise = (ar != 0); if (st == SWM_MAX_STACK) continue; @@ -8278,32 +9348,26 @@ setlayout(const char *selector, const char *value, int flags) ws[ws_id].cur_layout->l_config(&ws[ws_id], mg >= 0 ? SWM_ARG_ID_MASTERGROW : SWM_ARG_ID_MASTERSHRINK); - stack(); } /* master add */ for (x = 0; x < abs(ma); x++) { ws[ws_id].cur_layout->l_config(&ws[ws_id], ma >= 0 ? SWM_ARG_ID_MASTERADD : SWM_ARG_ID_MASTERDEL); - stack(); } /* stack inc */ for (x = 0; x < abs(si); x++) { ws[ws_id].cur_layout->l_config(&ws[ws_id], si >= 0 ? SWM_ARG_ID_STACKINC : SWM_ARG_ID_STACKDEC); - stack(); } /* Apply flip */ if (f) { ws[ws_id].cur_layout->l_config(&ws[ws_id], SWM_ARG_ID_FLIPLAYOUT); - stack(); } } - focus_flush(); - return (0); } @@ -8346,8 +9410,10 @@ struct config_option configopt[] = { { "focus_default", setconfvalue, SWM_S_FOCUS_DEFAULT }, { "focus_mode", setconfvalue, SWM_S_FOCUS_MODE }, { "iconic_enabled", setconfvalue, SWM_S_ICONIC_ENABLED }, + { "java_workaround", setconfvalue, SWM_S_JAVA_WORKAROUND }, { "keyboard_mapping", setkeymapping, 0 }, { "layout", setlayout, 0 }, + { "maximize_hide_bar", setconfvalue, SWM_S_MAXIMIZE_HIDE_BAR }, { "modkey", setconfmodkey, 0 }, { "program", setconfspawn, 0 }, { "quirk", setconfquirk, 0 }, @@ -8362,11 +9428,15 @@ struct config_option configopt[] = { { "tile_gap", setconfvalue, SWM_S_TILE_GAP }, { "title_class_enabled", setconfvalue, SWM_S_WINDOW_CLASS_ENABLED }, /* For backwards compat. */ { "title_name_enabled", setconfvalue, SWM_S_WINDOW_INSTANCE_ENABLED }, /* For backwards compat. */ + { "urgent_collapse", setconfvalue, SWM_S_URGENT_COLLAPSE }, { "urgent_enabled", setconfvalue, SWM_S_URGENT_ENABLED }, { "verbose_layout", setconfvalue, SWM_S_VERBOSE_LAYOUT }, + { "warp_focus", setconfvalue, SWM_S_WARP_FOCUS }, + { "warp_pointer", setconfvalue, SWM_S_WARP_POINTER }, { "window_class_enabled", setconfvalue, SWM_S_WINDOW_CLASS_ENABLED }, { "window_instance_enabled", setconfvalue, SWM_S_WINDOW_INSTANCE_ENABLED }, { "window_name_enabled", setconfvalue, SWM_S_WINDOW_NAME_ENABLED }, + { "workspace_clamp", setconfvalue, SWM_S_WORKSPACE_CLAMP }, { "workspace_limit", setconfvalue, SWM_S_WORKSPACE_LIMIT }, { "name", setconfvalue, SWM_S_WORKSPACE_NAME }, }; @@ -8389,7 +9459,7 @@ add_startup_exception(const char *fmt, ...) return; /* force bar to be enabled due to exception */ - bar_enabled = 1; + bar_enabled = true; va_start(ap, fmt); _add_startup_exception(fmt, ap); @@ -8512,6 +9582,7 @@ conf_load(const char *filename, int keymapping) if (line) free(line); fclose(config); + DNPRINTF(SWM_D_CONF, "conf_load: end\n"); return (0); @@ -8645,13 +9716,16 @@ get_swm_ws(xcb_window_t id) } int -get_ws_idx(xcb_window_t id) +get_ws_idx(struct ws_win *win) { xcb_get_property_reply_t *gpr; int ws_idx = -1; + if (win == NULL) + return -1; + gpr = xcb_get_property_reply(conn, - xcb_get_property(conn, 0, id, ewmh[_NET_WM_DESKTOP].atom, + xcb_get_property(conn, 0, win->id, ewmh[_NET_WM_DESKTOP].atom, XCB_ATOM_CARDINAL, 0, 1), NULL); if (gpr) { @@ -8660,30 +9734,96 @@ get_ws_idx(xcb_window_t id) free(gpr); } - if (ws_idx == -1) - if ((ws_idx = get_swm_ws(id)) != -1) - xcb_delete_property(conn, id, a_swm_ws); + if (ws_idx == -1 && !(win->quirks & SWM_Q_IGNORESPAWNWS)) + ws_idx = get_swm_ws(win->id); if (ws_idx > workspace_limit - 1 || ws_idx < -1) ws_idx = -1; - DNPRINTF(SWM_D_PROP, "get_ws_idx: win %#x, ws_idx: %d\n", id, ws_idx); + DNPRINTF(SWM_D_PROP, "get_ws_idx: win %#x, ws_idx: %d\n", win->id, + ws_idx); return ws_idx; } -struct ws_win * -manage_window(xcb_window_t id, int mapped) +void +reparent_window(struct ws_win *win) { - struct ws_win *win, *ww; - struct swm_region *r; - struct pid_e *p; - struct quirk *qp; - xcb_get_geometry_reply_t *gr; - xcb_window_t trans = XCB_WINDOW_NONE; - uint32_t i, wa[2], new_flags; - int ws_idx; - char *class, *instance, *name; + xcb_void_cookie_t c; + xcb_generic_error_t *error; + uint32_t wa[2]; + + win->frame = xcb_generate_id(conn); + + DNPRINTF(SWM_D_MISC, "reparent_window: win %#x, frame: %#x\n", + win->id, win->frame); + + wa[0] = + XCB_EVENT_MASK_ENTER_WINDOW | + XCB_EVENT_MASK_STRUCTURE_NOTIFY | + XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | + XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | + XCB_EVENT_MASK_EXPOSURE; +#ifdef SWM_DEBUG + wa[0] |= XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_FOCUS_CHANGE; +#endif + + xcb_create_window(conn, XCB_COPY_FROM_PARENT, win->frame, win->s->root, + X(win), Y(win), WIDTH(win), HEIGHT(win), 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, + XCB_CW_EVENT_MASK, wa); + + win->state = SWM_WIN_STATE_REPARENTING; + c = xcb_reparent_window_checked(conn, win->id, win->frame, 0, 0); + if ((error = xcb_request_check(conn, c))) { + DNPRINTF(SWM_D_MISC, "reparent_window: error:\n"); + event_error(error); + free(error); + + /* Abort. */ + xcb_destroy_window(conn, win->frame); + win->frame = XCB_WINDOW_NONE; + } else { + xcb_change_save_set(conn, XCB_SET_MODE_INSERT, win->id); + } + DNPRINTF(SWM_D_MISC, "reparent_window: done.\n"); +} + +void +unparent_window(struct ws_win *win) +{ + xcb_change_save_set(conn, XCB_SET_MODE_DELETE, win->id); + xcb_reparent_window(conn, win->id, win->s->root, X(win), Y(win)); + xcb_destroy_window(conn, win->frame); + win->frame = XCB_WINDOW_NONE; + win->state = SWM_WIN_STATE_UNPARENTING; +} + +struct ws_win * +manage_window(xcb_window_t id, int spawn_pos, bool mapping) +{ + struct ws_win *win = NULL, *ww; + struct swm_region *r; + struct pid_e *p; + struct quirk *qp; + xcb_get_geometry_reply_t *gr; + xcb_get_window_attributes_reply_t *war = NULL; + xcb_window_t trans = XCB_WINDOW_NONE; + uint32_t i, wa[1], new_flags; + int ws_idx, force_ws = -1; + char *class, *instance, *name; + + if (find_bar(id)) { + DNPRINTF(SWM_D_MISC, "manage_window: win %#x is region bar; " + "skipping.\n", id); + goto out; + } + + if (find_region(id)) { + DNPRINTF(SWM_D_MISC, "manage_window: win %#x is region window; " + "skipping.\n", id); + goto out; + } if ((win = find_window(id)) != NULL) { DNPRINTF(SWM_D_MISC, "manage_window: win %#x already " @@ -8700,16 +9840,36 @@ manage_window(xcb_window_t id, int mapped) if (TRANS(win)) set_child_transient(win, &trans); - goto out; + goto remanage; } else { DNPRINTF(SWM_D_MISC, "manage_window: win %#x is new.\n", id); } + war = xcb_get_window_attributes_reply(conn, + xcb_get_window_attributes(conn, id), NULL); + if (war == NULL) { + DNPRINTF(SWM_D_EVENT, "manage_window: window lost.\n"); + goto out; + } + + if (war->override_redirect) { + DNPRINTF(SWM_D_EVENT, "manage_window: override_redirect; " + "skipping.\n"); + goto out; + } + + if (!mapping && war->map_state == XCB_MAP_STATE_UNMAPPED && + get_win_state(id) == XCB_ICCCM_WM_STATE_WITHDRAWN) { + DNPRINTF(SWM_D_EVENT, "manage_window: window withdrawn; " + "skipping.\n"); + goto out; + } + /* Try to get initial window geometry. */ gr = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, id), NULL); if (gr == NULL) { DNPRINTF(SWM_D_MISC, "manage_window: get geometry failed.\n"); - return (NULL); + goto out; } /* Create and initialize ws_win object. */ @@ -8725,24 +9885,19 @@ manage_window(xcb_window_t id, int mapped) /* Ignore window border if there is one. */ WIDTH(win) = gr->width; HEIGHT(win) = gr->height; - X(win) = gr->x + gr->border_width - border_width; - Y(win) = gr->y + gr->border_width - border_width; - win->bordered = 1; - win->mapped = mapped; + X(win) = gr->x + gr->border_width; + Y(win) = gr->y + gr->border_width; + win->bordered = false; + win->mapped = (war->map_state != XCB_MAP_STATE_UNMAPPED); win->s = r->s; /* this never changes */ free(gr); /* Select which X events to monitor and set border pixel color. */ - wa[0] = win->s->c[SWM_S_COLOR_UNFOCUS].pixel; - wa[1] = XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_PROPERTY_CHANGE | + wa[0] = XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY; -#ifdef SWM_DEBUG - wa[1] |= XCB_EVENT_MASK_LEAVE_WINDOW | XCB_EVENT_MASK_FOCUS_CHANGE; -#endif - xcb_change_window_attributes(conn, win->id, XCB_CW_BORDER_PIXEL | - XCB_CW_EVENT_MASK, wa); + xcb_change_window_attributes(conn, win->id, XCB_CW_EVENT_MASK, wa); /* Get WM_SIZE_HINTS. */ xcb_icccm_get_wm_normal_hints_reply(conn, @@ -8766,31 +9921,11 @@ manage_window(xcb_window_t id, int mapped) /* Get WM_PROTOCOLS. */ get_wm_protocols(win); - /* Figure out which workspace the window belongs to. */ - if ((p = find_pid(window_get_pid(win->id))) != NULL) { - win->ws = &r->s->ws[p->ws]; - TAILQ_REMOVE(&pidlist, p, entry); - free(p); - p = NULL; - } else if ((ws_idx = get_ws_idx(win->id)) != -1 && - !TRANS(win)) { - /* _SWM_WS is set; use that. */ - win->ws = &r->s->ws[ws_idx]; - } else if (trans && (ww = find_window(trans)) != NULL) { - /* Launch transients in the same ws as parent. */ - win->ws = ww->ws; - } else { - win->ws = r->ws; - } - - /* Set the _NET_WM_DESKTOP atom. */ - DNPRINTF(SWM_D_PROP, "manage_window: set _NET_WM_DESKTOP: %d\n", - win->ws->idx); - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id, - ewmh[_NET_WM_DESKTOP].atom, XCB_ATOM_CARDINAL, 32, 1, &win->ws->idx); - - /* WS must already be set for this to work. */ - store_float_geom(win); +#ifdef SWM_DEBUG + /* Must be after getting WM_HINTS and WM_PROTOCOLS. */ + DNPRINTF(SWM_D_FOCUS, "manage_window: input_model: %s\n", + get_win_input_model(win)); +#endif /* Set initial quirks based on EWMH. */ ewmh_autoquirk(win); @@ -8810,7 +9945,7 @@ manage_window(xcb_window_t id, int mapped) /* java is retarded so treat it special */ if (strstr(instance, "sun-awt")) { DNPRINTF(SWM_D_CLASS, "manage_window: java window detected.\n"); - win->java = 1; + win->java = true; } TAILQ_FOREACH(qp, &quirks, entry) { @@ -8818,9 +9953,11 @@ manage_window(xcb_window_t id, int mapped) regexec(&qp->regex_instance, instance, 0, NULL, 0) == 0 && regexec(&qp->regex_name, name, 0, NULL, 0) == 0) { DNPRINTF(SWM_D_CLASS, "manage_window: matched " - "quirk: %s:%s:%s mask: %#lx\n", qp->class, - qp->instance, qp->name, qp->quirk); + "quirk: %s:%s:%s mask: %#x, ws: %d\n", qp->class, + qp->instance, qp->name, qp->quirk, qp->ws); win->quirks = qp->quirk; + if (qp->ws >= 0 && qp->ws < workspace_limit) + force_ws = qp->ws; } } @@ -8834,6 +9971,39 @@ manage_window(xcb_window_t id, int mapped) fake_keypress(win, XK_KP_Add, XCB_MOD_MASK_SHIFT); } + /* Figure out which workspace the window belongs to. */ + if (!(win->quirks & SWM_Q_IGNOREPID) && + (p = find_pid(window_get_pid(win->id))) != NULL) { + win->ws = &r->s->ws[p->ws]; + TAILQ_REMOVE(&pidlist, p, entry); + free(p); + p = NULL; + } else if ((ws_idx = get_ws_idx(win)) != -1 && + !TRANS(win)) { + /* _SWM_WS is set; use that. */ + win->ws = &r->s->ws[ws_idx]; + } else if (trans && (ww = find_window(trans)) != NULL) { + /* Launch transients in the same ws as parent. */ + win->ws = ww->ws; + } else { + win->ws = r->ws; + } + + if (force_ws != -1) + win->ws = &r->s->ws[force_ws]; + + /* Set the _NET_WM_DESKTOP atom. */ + DNPRINTF(SWM_D_PROP, "manage_window: set _NET_WM_DESKTOP: %d\n", + win->ws->idx); + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->id, + ewmh[_NET_WM_DESKTOP].atom, XCB_ATOM_CARDINAL, 32, 1, &win->ws->idx); + + /* Remove any _SWM_WS now that we set _NET_WM_DESKTOP. */ + xcb_delete_property(conn, win->id, a_swm_ws); + + /* WS must already be set for this to work. */ + store_float_geom(win); + /* Make sure window is positioned inside its region, if its active. */ if (win->ws->r) { region_containment(win, r, SWM_CW_ALLSIDES | @@ -8841,16 +10011,16 @@ manage_window(xcb_window_t id, int mapped) update_window(win); } -out: - /* Figure out where to stack the window in the workspace. */ +remanage: + /* Figure out where to insert the window in the workspace list. */ if (trans && (ww = find_window(trans))) TAILQ_INSERT_AFTER(&win->ws->winlist, ww, win, entry); - else if (win->ws->focus && spawn_position == SWM_STACK_ABOVE) + else if (win->ws->focus && spawn_pos == SWM_STACK_ABOVE) TAILQ_INSERT_AFTER(&win->ws->winlist, win->ws->focus, win, entry); - else if (win->ws->focus && spawn_position == SWM_STACK_BELOW) + else if (win->ws->focus && spawn_pos == SWM_STACK_BELOW) TAILQ_INSERT_BEFORE(win->ws->focus, win, entry); - else switch (spawn_position) { + else switch (spawn_pos) { default: case SWM_STACK_TOP: case SWM_STACK_ABOVE: @@ -8863,7 +10033,8 @@ out: ewmh_update_client_list(); - TAILQ_INSERT_TAIL(&win->ws->stack, win, stack_entry); + TAILQ_INSERT_HEAD(&win->ws->stack, win, stack_entry); + lower_window(win); /* Get/apply initial _NET_WM_STATE */ ewmh_get_wm_state(win); @@ -8883,30 +10054,27 @@ out: /* Set initial _NET_WM_ALLOWED_ACTIONS */ ewmh_update_actions(win); - grabbuttons(win); + reparent_window(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, YESNO(ICONIC(win)), win->transient); - +out: + free(war); return (win); } 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 */ @@ -8917,18 +10085,13 @@ free_window(struct ws_win *win) void 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) - parent->focus_child = NULL; - } + kill_refs(win); + unparent_window(win); TAILQ_REMOVE(&win->ws->stack, win, stack_entry); TAILQ_REMOVE(&win->ws->winlist, win, entry); @@ -8940,29 +10103,57 @@ unmanage_window(struct ws_win *win) void expose(xcb_expose_event_t *e) { - int i, num_screens; - struct swm_region *r; + struct ws_win *w; + struct swm_bar *b; +#ifdef SWM_DEBUG + struct workspace *ws; +#endif - DNPRINTF(SWM_D_EVENT, "expose: win %#x\n", e->window); + DNPRINTF(SWM_D_EVENT, "expose: win %#x, count: %d\n", e->window, + e->count); - num_screens = get_screen_count(); - for (i = 0; i < num_screens; i++) - TAILQ_FOREACH(r, &screens[i].rl, entry) - if (e->window == WINID(r->bar)) - bar_draw(); + if (e->count > 0) + return; - xcb_flush(conn); + if ((b = find_bar(e->window))) { + bar_draw(b); + xcb_flush(conn); + } else if ((w = find_window(e->window)) && w->frame == e->window) { + draw_frame(w); +#ifdef SWM_DEBUG + ws = w->ws; + TAILQ_FOREACH(w, &ws->winlist, entry) + debug_refresh(w); +#endif + xcb_flush(conn); + } + + DNPRINTF(SWM_D_EVENT, "expose: done\n"); } -#ifdef SWM_DEBUG void focusin(xcb_focus_in_event_t *e) { + struct ws_win *win; + DNPRINTF(SWM_D_EVENT, "focusin: win %#x, mode: %s(%u), " "detail: %s(%u)\n", e->event, get_notify_mode_label(e->mode), e->mode, get_notify_detail_label(e->detail), e->detail); + if ((win = find_window(e->event)) && win != win->ws->focus && + win != win->ws->focus_pending && + e->mode == XCB_NOTIFY_MODE_NORMAL) { + win->ws->focus_prev = win->ws->focus; + win->ws->focus = win; + win->ws->focus_pending = NULL; + + if (win->ws->focus_prev) + draw_frame(win->ws->focus_prev); + draw_frame(win); + raise_window(win); + } } +#ifdef SWM_DEBUG void focusout(xcb_focus_out_event_t *e) { @@ -8975,44 +10166,102 @@ 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); DNPRINTF(SWM_D_EVENT, "keypress: 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)); + "state: %u, cleaned: %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, CLEANMASK(e->state), + YESNO(e->same_screen)); + + 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; + } - if ((kp = key_lookup(CLEANMASK(e->state), keysym)) == NULL) + 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; + + keysym = xcb_key_release_lookup_keysym(syms, e, 0); + + 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)); + + 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); + DNPRINTF(SWM_D_EVENT, "keyrelease: unfreezing.\n"); + } + + xcb_flush(conn); + + DNPRINTF(SWM_D_EVENT, "keyrelease: done.\n"); +} + void buttonpress(xcb_button_press_event_t *e) { - struct ws_win *win = NULL; + struct ws_win *win = NULL, *newf; struct swm_region *r, *old_r; - int i; - int handled = 0; + 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, " @@ -9021,70 +10270,131 @@ 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); + DNPRINTF(SWM_D_FOCUS, "buttonpress: " + "set_input_focus: %#x, revert-to: parent, " + "time: %#x\n", e->root, e->time); xcb_set_input_focus(conn, - XCB_INPUT_FOCUS_PARENT, e->root, e->time); + XCB_INPUT_FOCUS_POINTER_ROOT, + e->root, e->time); /* Clear bar since empty. */ - bar_draw(); + bar_draw(r->bar); - handled = 1; - goto out; + /* No need to replay event. */ + replay = false; } } } else { win = find_window(e->event); } - if (win == NULL) - goto out; + if (win) { + newf = get_focus_magic(win); + if (win->ws->focus == newf && newf != win) { + if (win->focus_child == win) + win->focus_child = NULL; + newf = win; + } + focus_win(newf); + } - 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 = 1; - } + 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); } + focus_flush(); +} + +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 +char * +get_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 (ACCEPTS_FOCUS(win)) + inputmodel = (win->take_focus) ? "Locally Active" : "Passive"; + else + inputmodel = (win->take_focus) ? "Globally Active" : "No Input"; + + return inputmodel; +} + void print_win_geom(xcb_window_t w) { @@ -9112,7 +10422,7 @@ get_stack_mode_name(uint8_t mode) { char *name; - switch(mode) { + switch (mode) { case XCB_STACK_MODE_ABOVE: name = "Above"; break; @@ -9141,13 +10451,14 @@ configurerequest(xcb_configure_request_event_t *e) { struct ws_win *win; struct swm_region *r = NULL; - int new = 0, i = 0; - uint16_t mask = 0; + int i = 0; uint32_t wc[7] = {0}; + uint16_t mask = 0; + bool new = false; if ((win = find_window(e->window)) == NULL) if ((win = find_unmanaged_window(e->window)) == NULL) - new = 1; + new = true; #ifdef SWM_DEBUG if (swm_debug & SWM_D_EVENT) { @@ -9237,7 +10548,7 @@ configurerequest(xcb_configure_request_event_t *e) if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) win->g_float.h = e->height; - win->g_floatvalid = 1; + win->g_floatvalid = true; if (!MAXIMIZED(win) && !FULLSCREEN(win) && (TRANS(win) || (ABOVE(win) && @@ -9277,12 +10588,9 @@ configurenotify(xcb_configure_notify_event_t *e) win = find_window(e->window); if (win) { - xcb_icccm_get_wm_normal_hints_reply(conn, - xcb_icccm_get_wm_normal_hints(conn, win->id), - &win->sh, NULL); adjust_font(win); - if (font_adjusted) { - stack(); + if (font_adjusted && win->ws->r) { + stack(win->ws->r); xcb_flush(conn); } } @@ -9292,38 +10600,58 @@ void destroynotify(xcb_destroy_notify_event_t *e) { struct ws_win *win; + struct workspace *ws; 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; + goto out; + /* Window is on unmanaged list. */ + TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry); free_window(win); - return; + goto out; + } + + ws = win->ws; + + if (win->frame == e->window) { + DNPRINTF(SWM_D_EVENT, "destroynotify: frame for win %#x\n", + win->id); + win->frame = XCB_WINDOW_NONE; + goto out; } 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 == ws->focus) { + ws->focus_pending = get_focus_prev(win); + if (ws->focus_pending == win) + ws->focus_pending = NULL; + } } unmanage_window(win); - stack(); + TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry); + free_window(win); + stack(ws->r); - 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(ws)) { + if (ws->focus_pending) { + focus_win(ws->focus_pending); + ws->focus_pending = NULL; + } else if (ws->focus == NULL) { + DNPRINTF(SWM_D_FOCUS, "destroynotify: set_input_focus: " + "%#x, revert-to: parent, time: 0\n", ws->r->id); + xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, + ws->r->id, XCB_CURRENT_TIME); } - } - free_window(win); + focus_flush(); + } - focus_flush(); +out: + DNPRINTF(SWM_D_EVENT, "destroynotify: done.\n"); } #ifdef SWM_DEBUG @@ -9388,6 +10716,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 @@ -9396,14 +10779,24 @@ 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 (e->event == e->root && e->child == XCB_WINDOW_NONE && + e->mode == XCB_NOTIFY_MODE_GRAB && + e->detail == XCB_NOTIFY_DETAIL_INFERIOR) { + DNPRINTF(SWM_D_EVENT, "enternotify: grab inferior; ignoring.\n"); + return; + } if (focus_mode == SWM_FOCUS_MANUAL && e->mode == XCB_NOTIFY_MODE_NORMAL) { @@ -9411,7 +10804,12 @@ enternotify(xcb_enter_notify_event_t *e) return; } - last_event_time = e->time; + if (focus_mode != SWM_FOCUS_FOLLOW && + e->mode == XCB_NOTIFY_MODE_UNGRAB && + e->detail != XCB_NOTIFY_DETAIL_ANCESTOR) { + DNPRINTF(SWM_D_EVENT, "enternotify: ungrab; ignoring.\n"); + return; + } if ((win = find_window(e->event)) == NULL) { if (e->event == e->root) { @@ -9430,9 +10828,18 @@ enternotify(xcb_enter_notify_event_t *e) return; } } else { + if (e->mode == XCB_NOTIFY_MODE_NORMAL && + e->detail == XCB_NOTIFY_DETAIL_INFERIOR) { + DNPRINTF(SWM_D_EVENT, "enternotify: entering from " + "inferior; ignoring\n"); + return; + } + focus_win(get_focus_magic(win)); } + DNPRINTF(SWM_D_EVENT, "enternotify: done\n"); + xcb_flush(conn); } @@ -9440,14 +10847,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 @@ -9459,10 +10869,18 @@ mapnotify(xcb_map_notify_event_t *e) DNPRINTF(SWM_D_EVENT, "mapnotify: win %#x\n", e->window); - if ((win = manage_window(e->window, 1)) == NULL) + if ((win = manage_window(e->window, spawn_position, false)) == NULL) return; ws = win->ws; + if (win->state == SWM_WIN_STATE_REPARENTING) + return; + + if (ws->r == NULL) { + unmap_window(win); + goto out; + } + /* Need to know if win was mapped due to ws switch. */ if (ws->state == SWM_WS_STATE_MAPPED) { if (ws->focus_pending && TRANS(ws->focus_pending)) @@ -9471,78 +10889,53 @@ mapnotify(xcb_map_notify_event_t *e) /* If window's parent is maximized, don't clear it. */ if ((parent == NULL) || !MAXIMIZED(parent)) if (clear_maximized(ws) > 0) - stack(); + stack(ws->r); } - win->mapped = 1; + win->mapped = true; set_win_state(win, XCB_ICCCM_WM_STATE_NORMAL); if (focus_mode != SWM_FOCUS_FOLLOW && WS_FOCUSED(win->ws)) { if (ws->focus_pending == win) { focus_win(win); ws->focus_pending = NULL; + center_pointer(ws->r); focus_flush(); } } +out: + DNPRINTF(SWM_D_EVENT, "mapnotify: done\n"); + xcb_flush(conn); } 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 maprequest(xcb_map_request_event_t *e) { struct ws_win *win, *w = NULL; - xcb_get_window_attributes_reply_t *war; DNPRINTF(SWM_D_EVENT, "maprequest: win %#x\n", e->window); - war = xcb_get_window_attributes_reply(conn, - xcb_get_window_attributes(conn, e->window), - NULL); - if (war == NULL) { - DNPRINTF(SWM_D_EVENT, "maprequest: window lost.\n"); - goto out; - } - - if (war->override_redirect) { - DNPRINTF(SWM_D_EVENT, "maprequest: override_redirect; " - "skipping.\n"); - goto out; - } - - win = manage_window(e->window, - (war->map_state == XCB_MAP_STATE_VIEWABLE)); + win = manage_window(e->window, spawn_position, true); if (win == NULL) goto out; /* The new window should get focus; prepare. */ if (focus_mode != SWM_FOCUS_FOLLOW && - !(win->quirks & SWM_Q_NOFOCUSONMAP) && - (!(win->hints.flags & XCB_ICCCM_WM_HINT_INPUT) || - (win->hints.flags & XCB_ICCCM_WM_HINT_INPUT && - win->hints.input))) { + !(win->quirks & SWM_Q_NOFOCUSONMAP) && ACCEPTS_FOCUS(win)) { if (win->quirks & SWM_Q_FOCUSONMAP_SINGLE) { /* See if other wins of same type are already mapped. */ TAILQ_FOREACH(w, &win->ws->winlist, entry) { @@ -9566,14 +10959,12 @@ maprequest(xcb_map_request_event_t *e) } /* All windows need to be mapped if they are in the current workspace.*/ - if (win->ws->r) - stack(); + stack(win->ws->r); /* Ignore EnterNotify to handle the mapnotify without interference. */ if (focus_mode == SWM_FOCUS_DEFAULT) event_drain(XCB_ENTER_NOTIFY); out: - free(war); DNPRINTF(SWM_D_EVENT, "maprequest: done.\n"); } @@ -9583,6 +10974,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", @@ -9591,8 +10984,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; @@ -9657,19 +11048,20 @@ 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) { if (focus_mode != SWM_FOCUS_FOLLOW && WS_FOCUSED(ws)) { if (win->mapped && + win->state == SWM_WIN_STATE_REPARENTED && ws->focus_pending == win) { focus_win(ws->focus_pending); ws->focus_pending = NULL; @@ -9678,14 +11070,46 @@ propertynotify(xcb_property_notify_event_t *e) } } else if (e->atom == XCB_ATOM_WM_CLASS || e->atom == XCB_ATOM_WM_NAME) { - bar_draw(); + if (ws->r) + bar_draw(ws->r->bar); } else if (e->atom == a_prot) { get_wm_protocols(win); + } else if (e->atom == XCB_ATOM_WM_NORMAL_HINTS) { + xcb_icccm_get_wm_normal_hints_reply(conn, + xcb_icccm_get_wm_normal_hints(conn, win->id), + &win->sh, NULL); } xcb_flush(conn); } +void +reparentnotify(xcb_reparent_notify_event_t *e) +{ + struct ws_win *win; + + DNPRINTF(SWM_D_EVENT, "reparentnotify: event: %#x, win %#x, " + "parent: %#x, (x,y): (%u,%u), override_redirect: %u\n", + e->event, e->window, e->parent, e->x, e->y, e->override_redirect); + + win = find_window(e->window); + if (win) { + if (win->state == SWM_WIN_STATE_REPARENTING) { + win->state = SWM_WIN_STATE_REPARENTED; + + if (win->ws->r && !ICONIC(win)) + map_window(win); + else + unmap_window(win); + + update_window(win); + update_win_stacking(win); + } else if (win->state == SWM_WIN_STATE_UNPARENTING) { + win->state = SWM_WIN_STATE_UNPARENTED; + } + } +} + void unmapnotify(xcb_unmap_notify_event_t *e) { @@ -9696,15 +11120,25 @@ unmapnotify(xcb_unmap_notify_event_t *e) /* If we aren't managing the window, then ignore. */ win = find_window(e->window); - if (win == NULL || win->id != e->window) + if (win == NULL || win->id != e->window) { + DNPRINTF(SWM_D_EVENT, "unmapnotify: ignore unmanaged.\n"); return; + } /* Do nothing if already withdrawn. */ - if (!win->mapped && !ICONIC(win)) + if (!win->mapped && !ICONIC(win)) { + DNPRINTF(SWM_D_EVENT, "unmapnotify: ignore withdrawn.\n"); return; + } ws = win->ws; - win->mapped = 0; + win->mapped = false; + + /* Ignore if reparenting-related. */ + if (win->state != SWM_WIN_STATE_REPARENTED) { + DNPRINTF(SWM_D_EVENT, "unmapnotify: ignore not reparented.\n"); + return; + } /* If win was focused, make sure to focus on something else. */ if (win == ws->focus) { @@ -9720,15 +11154,16 @@ unmapnotify(xcb_unmap_notify_event_t *e) if (ICONIC(win)) { /* Iconify. */ + DNPRINTF(SWM_D_EVENT, "unmapnotify: iconify.\n"); set_win_state(win, XCB_ICCCM_WM_STATE_ICONIC); } else { /* Withdraw. */ + DNPRINTF(SWM_D_EVENT, "unmapnotify: withdraw.\n"); set_win_state(win, XCB_ICCCM_WM_STATE_WITHDRAWN); unmanage_window(win); } - if (ws->r) - stack(); + stack(ws->r); /* Update focus if ws is active. */ if (WS_FOCUSED(ws)) { @@ -9738,12 +11173,17 @@ unmapnotify(xcb_unmap_notify_event_t *e) focus_win(ws->focus_pending); ws->focus_pending = NULL; } else if (ws->focus == NULL) { - xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, + DNPRINTF(SWM_D_FOCUS, "unmapnotify: set_input_focus: " + "%#x, revert-to: parent, time: 0\n", + ws->r->id); + xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, ws->r->id, XCB_CURRENT_TIME); } } + center_pointer(ws->r); focus_flush(); + DNPRINTF(SWM_D_EVENT, "unmapnotify: done.\n"); } #ifdef SWM_DEBUG @@ -9776,7 +11216,7 @@ clientmessage(xcb_client_message_event_t *e) struct ws_win *win; struct swm_region *r = NULL; union arg a; - uint32_t val[2]; + uint32_t vals[4]; int num_screens, i; xcb_map_request_event_t mre; #ifdef SWM_DEBUG @@ -9798,10 +11238,18 @@ 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(); } + return; + } else if (e->type == ewmh[_NET_REQUEST_FRAME_EXTENTS].atom) { + DNPRINTF(SWM_D_EVENT, + "clientmessage: set _NET_FRAME_EXTENTS on window.\n"); + vals[0] = vals[1] = vals[2] = vals[3] = border_width; + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, e->window, + a_net_frame_extents, XCB_ATOM_CARDINAL, 32, 4, vals); + xcb_flush(conn); return; } @@ -9861,11 +11309,13 @@ clientmessage(xcb_client_message_event_t *e) } } else if (e->type == ewmh[_NET_RESTACK_WINDOW].atom) { DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_RESTACK_WINDOW\n"); - val[0] = e->data.data32[1]; /* Sibling window. */ - val[1] = e->data.data32[2]; /* Stack mode detail. */ + vals[0] = e->data.data32[1]; /* Sibling window. */ + vals[1] = e->data.data32[2]; /* Stack mode detail. */ - xcb_configure_window(conn, win->id, XCB_CONFIG_WINDOW_SIBLING | - XCB_CONFIG_WINDOW_STACK_MODE, val); + if (win->frame != XCB_WINDOW_NONE) + xcb_configure_window(conn, win->frame, + XCB_CONFIG_WINDOW_SIBLING | + XCB_CONFIG_WINDOW_STACK_MODE, vals); } else if (e->type == ewmh[_NET_WM_STATE].atom) { DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_WM_STATE\n"); ewmh_change_wm_state(win, e->data.data32[1], e->data.data32[0]); @@ -9874,19 +11324,24 @@ clientmessage(xcb_client_message_event_t *e) e->data.data32[0]); ewmh_update_wm_state(win); - stack(); + stack(win->ws->r); } else if (e->type == ewmh[_NET_WM_DESKTOP].atom) { DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_WM_DESKTOP\n"); r = win->ws->r; - win_to_ws(win, e->data.data32[0], 1); + win_to_ws(win, e->data.data32[0], true); - /* Restack if either the source or destination ws is mapped. */ - if (r != NULL || win->ws->r != NULL) { - if (FLOATING(win)) - load_float_geom(win); + /* Stack source and destination ws, if mapped. */ + if (r != win->ws->r) { + if (r) + stack(r); - stack(); + if (win->ws->r) { + if (FLOATING(win)) + load_float_geom(win); + + stack(win->ws->r); + } } } @@ -9931,7 +11386,16 @@ enable_wm(void) { int num_screens, i; const uint32_t val = XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | - XCB_EVENT_MASK_ENTER_WINDOW; + XCB_EVENT_MASK_ENTER_WINDOW | + XCB_EVENT_MASK_OWNER_GRAB_BUTTON | + XCB_EVENT_MASK_STRUCTURE_NOTIFY | + XCB_EVENT_MASK_ENTER_WINDOW | + XCB_EVENT_MASK_LEAVE_WINDOW | + XCB_EVENT_MASK_BUTTON_PRESS | + XCB_EVENT_MASK_BUTTON_RELEASE | + XCB_EVENT_MASK_KEY_PRESS | + XCB_EVENT_MASK_KEY_RELEASE | + XCB_EVENT_MASK_PROPERTY_CHANGE; xcb_screen_t *sc; xcb_void_cookie_t wac; xcb_generic_error_t *error; @@ -9951,11 +11415,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; @@ -9984,6 +11443,7 @@ new_region(struct swm_screen *s, int x, int y, int w, int h) r->ws->r = NULL; bar_cleanup(r); xcb_destroy_window(conn, r->id); + r->id = XCB_WINDOW_NONE; TAILQ_REMOVE(&s->rl, r, entry); TAILQ_INSERT_TAIL(&s->orl, r, entry); } @@ -10031,6 +11491,7 @@ new_region(struct swm_screen *s, int x, int y, int w, int h) Y(r) = y; WIDTH(r) = w; HEIGHT(r) = h; + r->bar = NULL; r->s = s; r->ws = ws; r->ws_prior = NULL; @@ -10055,14 +11516,13 @@ new_region(struct swm_screen *s, int x, int y, int w, int h) } void -scan_xrandr(int i) +scan_randr(int idx) { #ifdef SWM_XRR_HAS_CRTC int c; int ncrtc = 0; #endif /* SWM_XRR_HAS_CRTC */ struct swm_region *r; - struct ws_win *win; int num_screens; xcb_randr_get_screen_resources_current_cookie_t src; xcb_randr_get_screen_resources_current_reply_t *srr; @@ -10071,34 +11531,35 @@ scan_xrandr(int i) xcb_randr_crtc_t *crtc; xcb_screen_t *screen; - DNPRINTF(SWM_D_MISC, "scan_xrandr: screen: %d\n", i); + DNPRINTF(SWM_D_MISC, "scan_randr: screen: %d\n", idx); - if ((screen = get_screen(i)) == NULL) - errx(1, "ERROR: can't get screen %d.", i); + if ((screen = get_screen(idx)) == NULL) + errx(1, "ERROR: can't get screen %d.", idx); num_screens = get_screen_count(); - if (i >= num_screens) - errx(1, "scan_xrandr: invalid screen"); + if (idx >= num_screens) + errx(1, "scan_randr: invalid screen"); /* remove any old regions */ - while ((r = TAILQ_FIRST(&screens[i].rl)) != NULL) { + while ((r = TAILQ_FIRST(&screens[idx].rl)) != NULL) { r->ws->old_r = r->ws->r = NULL; bar_cleanup(r); xcb_destroy_window(conn, r->id); - TAILQ_REMOVE(&screens[i].rl, r, entry); - TAILQ_INSERT_TAIL(&screens[i].orl, r, entry); + r->id = XCB_WINDOW_NONE; + TAILQ_REMOVE(&screens[idx].rl, r, entry); + TAILQ_INSERT_TAIL(&screens[idx].orl, r, entry); } outputs = 0; /* map virtual screens onto physical screens */ #ifdef SWM_XRR_HAS_CRTC - if (xrandr_support) { + if (randr_support) { src = xcb_randr_get_screen_resources_current(conn, - screens[i].root); + screens[idx].root); srr = xcb_randr_get_screen_resources_current_reply(conn, src, NULL); if (srr == NULL) { - new_region(&screens[i], 0, 0, + new_region(&screens[idx], 0, 0, screen->width_in_pixels, screen->height_in_pixels); goto out; @@ -10118,11 +11579,11 @@ scan_xrandr(int i) } if (cir->mode == 0) - new_region(&screens[i], 0, 0, + new_region(&screens[idx], 0, 0, screen->width_in_pixels, screen->height_in_pixels); else - new_region(&screens[i], + new_region(&screens[idx], cir->x, cir->y, cir->width, cir->height); free(cir); } @@ -10131,30 +11592,27 @@ scan_xrandr(int i) #endif /* SWM_XRR_HAS_CRTC */ /* If detection failed, create a single region that spans the screen. */ - if (TAILQ_EMPTY(&screens[i].rl)) - new_region(&screens[i], 0, 0, screen->width_in_pixels, + if (TAILQ_EMPTY(&screens[idx].rl)) + new_region(&screens[idx], 0, 0, screen->width_in_pixels, screen->height_in_pixels); out: - /* Cleanup unused previously visible workspaces. */ - TAILQ_FOREACH(r, &screens[i].orl, entry) { - TAILQ_FOREACH(win, &r->ws->winlist, entry) - unmap_window(win); - r->ws->state = SWM_WS_STATE_HIDDEN; - - /* The screen shouldn't focus on an unused region. */ - if (screens[i].r_focus == r) - screens[i].r_focus = NULL; + /* The screen shouldn't focus on unused regions. */ + TAILQ_FOREACH(r, &screens[idx].orl, entry) { + if (screens[idx].r_focus == r) + screens[idx].r_focus = NULL; } - DNPRINTF(SWM_D_MISC, "scan_xrandr: done.\n"); + DNPRINTF(SWM_D_MISC, "scan_randr: done.\n"); } void screenchange(xcb_randr_screen_change_notify_event_t *e) { struct swm_region *r; - int i, num_screens; + struct workspace *ws; + struct ws_win *win; + int i, j, num_screens; DNPRINTF(SWM_D_EVENT, "screenchange: root: %#x\n", e->root); @@ -10167,51 +11625,55 @@ screenchange(xcb_randr_screen_change_notify_event_t *e) errx(1, "screenchange: screen not found"); /* brute force for now, just re-enumerate the regions */ - scan_xrandr(i); + scan_randr(i); #ifdef SWM_DEBUG print_win_geom(e->root); #endif /* add bars to all regions */ - for (i = 0; i < num_screens; i++) { - TAILQ_FOREACH(r, &screens[i].rl, entry) - bar_setup(r); - } + TAILQ_FOREACH(r, &screens[i].rl, entry) + bar_setup(r); - stack(); + /* Stack all regions. */ + TAILQ_FOREACH(r, &screens[i].rl, entry) + stack(r); - /* Make sure a region has focus on each screen. */ - for (i = 0; i < num_screens; i++) { - if (screens[i].r_focus == NULL) { - r = TAILQ_FIRST(&screens[i].rl); - if (r != NULL) - focus_region(r); + /* Make sure a region has focus. */ + if (screens[i].r_focus == NULL) { + r = TAILQ_FIRST(&screens[i].rl); + if (r != NULL) + focus_region(r); + } + + /* Cleanup unused previously visible workspaces. */ + for (j = 0; j < workspace_limit; j++) { + ws = &screens[i].ws[j]; + if (ws->r == NULL && ws->state != SWM_WS_STATE_HIDDEN) { + TAILQ_FOREACH(win, &ws->winlist, entry) + unmap_window(win); + ws->state = SWM_WS_STATE_HIDDEN; } } - bar_draw(); focus_flush(); - /* Update workspace state on all regions. */ - for (i = 0; i < num_screens; i++) - TAILQ_FOREACH(r, &screens[i].rl, entry) - r->ws->state = SWM_WS_STATE_MAPPED; + /* Update workspace state and bar on all regions. */ + TAILQ_FOREACH(r, &screens[i].rl, entry) { + r->ws->state = SWM_WS_STATE_MAPPED; + bar_draw(r->bar); + } } void grab_windows(void) { - struct swm_region *r = NULL; - xcb_window_t *wins = NULL, trans, *cwins = NULL; - int i, j, k, n, no, num_screens, manage, mapped; - uint8_t state; - - xcb_query_tree_cookie_t qtc; - xcb_query_tree_reply_t *qtr; - xcb_get_window_attributes_cookie_t gac; - xcb_get_window_attributes_reply_t *gar; - xcb_get_property_cookie_t pc; - xcb_get_property_reply_t *pr; + struct swm_region *r = NULL; + xcb_query_tree_cookie_t qtc; + xcb_query_tree_reply_t *qtr; + xcb_get_property_cookie_t pc; + xcb_get_property_reply_t *pr; + xcb_window_t *wins = NULL, trans, *cwins = NULL; + int i, j, k, n, no, num_screens; DNPRINTF(SWM_D_INIT, "grab_windows: begin\n"); num_screens = get_screen_count(); @@ -10245,9 +11707,9 @@ grab_windows(void) free(pr); } - /* attach windows to a region */ - /* normal windows */ - DNPRINTF(SWM_D_INIT, "grab_windows: grab top level windows.\n"); + /* Manage top-level windows first, then transients. */ + /* TODO: allow transients to be managed before leader. */ + DNPRINTF(SWM_D_INIT, "grab_windows: grab top-level windows.\n"); for (j = 0; j < no; j++) { TAILQ_FOREACH(r, &screens[i].rl, entry) { if (r->id == wins[j]) { @@ -10266,63 +11728,23 @@ grab_windows(void) if (r) continue; - gac = xcb_get_window_attributes(conn, wins[j]); - gar = xcb_get_window_attributes_reply(conn, gac, NULL); - if (gar == NULL) { - DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; " - "doesn't exist.\n", wins[j]); - continue; - } - - if (gar->override_redirect) { - DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; " - "override_redirect set.\n", wins[j]); - free(gar); - continue; - } - pc = xcb_icccm_get_wm_transient_for(conn, wins[j]); if (xcb_icccm_get_wm_transient_for_reply(conn, pc, &trans, NULL)) { DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; " "is transient for %#x.\n", wins[j], trans); - free(gar); continue; } - state = get_win_state(wins[j]); - manage = state != XCB_ICCCM_WM_STATE_WITHDRAWN; - mapped = gar->map_state == XCB_MAP_STATE_VIEWABLE; - if (mapped || manage) - manage_window(wins[j], mapped); - free(gar); + manage_window(wins[j], SWM_STACK_TOP, false); } - /* transient windows */ + DNPRINTF(SWM_D_INIT, "grab_windows: grab transient windows.\n"); for (j = 0; j < no; j++) { - gac = xcb_get_window_attributes(conn, wins[j]); - gar = xcb_get_window_attributes_reply(conn, gac, NULL); - if (gar == NULL) { - DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; " - "doesn't exist.\n", wins[j]); - continue; - } - - if (gar->override_redirect) { - DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; " - "override_redirect set.\n", wins[j]); - free(gar); - continue; - } - - state = get_win_state(wins[j]); - manage = state != XCB_ICCCM_WM_STATE_WITHDRAWN; - mapped = gar->map_state == XCB_MAP_STATE_VIEWABLE; pc = xcb_icccm_get_wm_transient_for(conn, wins[j]); if (xcb_icccm_get_wm_transient_for_reply(conn, pc, - &trans, NULL) && manage) - manage_window(wins[j], mapped); - free(gar); + &trans, NULL)) + manage_window(wins[j], SWM_STACK_TOP, false); } free(qtr); } @@ -10341,21 +11763,20 @@ 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"); - /* initial Xrandr setup */ - xrandr_support = 0; + /* Initial RandR setup. */ + randr_support = false; qep = xcb_get_extension_data(conn, &xcb_randr_id); if (qep->present) { c = xcb_randr_query_version(conn, 1, 1); r = xcb_randr_query_version_reply(conn, c, NULL); if (r) { if (r->major_version >= 1) { - xrandr_support = 1; - xrandr_eventbase = qep->first_event; + randr_support = true; + randr_eventbase = qep->first_event; } free(r); } @@ -10403,10 +11824,11 @@ setup_screens(void) ws = &screens[i].ws[j]; ws->idx = j; ws->name = NULL; - ws->bar_enabled = 1; + ws->bar_enabled = true; ws->focus = NULL; ws->focus_prev = NULL; ws->focus_pending = NULL; + ws->focus_raise = NULL; ws->r = NULL; ws->old_r = NULL; ws->state = SWM_WS_STATE_HIDDEN; @@ -10422,9 +11844,9 @@ setup_screens(void) ws->cur_layout->l_string(ws); } - scan_xrandr(i); + scan_randr(i); - if (xrandr_support) + if (randr_support) xcb_randr_select_input(conn, screens[i].root, XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE); } @@ -10445,6 +11867,7 @@ setup_globals(void) a_state = get_atom_from_string("WM_STATE"); a_prot = get_atom_from_string("WM_PROTOCOLS"); a_delete = get_atom_from_string("WM_DELETE_WINDOW"); + a_net_frame_extents = get_atom_from_string("_NET_FRAME_EXTENTS"); a_net_supported = get_atom_from_string("_NET_SUPPORTED"); a_net_wm_check = get_atom_from_string("_NET_SUPPORTING_WM_CHECK"); a_takefocus = get_atom_from_string("WM_TAKE_FOCUS"); @@ -10455,7 +11878,10 @@ setup_globals(void) void shutdown_cleanup(void) { - int i, num_screens; + 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 */ alarm(0); @@ -10467,28 +11893,78 @@ shutdown_cleanup(void) cursors_cleanup(); + clear_quirks(); + clear_spawns(); + clear_bindings(); + teardown_ewmh(); num_screens = get_screen_count(); for (i = 0; i < num_screens; ++i) { + int j; + + DNPRINTF(SWM_D_FOCUS, "shutdown_cleanup: set_input_focus: " + "%#x, revert-to: root, time: 0\n", screens[i].root); xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, screens[i].root, XCB_CURRENT_TIME); if (screens[i].bar_gc != XCB_NONE) xcb_free_gc(conn, screens[i].bar_gc); - if (!bar_font_legacy) + if (!bar_font_legacy) { XftColorFree(display, DefaultVisual(display, i), DefaultColormap(display, i), &bar_font_color); + XftColorFree(display, DefaultVisual(display, i), + DefaultColormap(display, i), &search_font_color); + } + + for (j = 0; j < SWM_S_COLOR_MAX; ++j) { + free(screens[i].c[j].name); + } + + /* Free window memory. */ + for (j = 0; j < SWM_WS_MAX; ++j) { + ws = &screens[i].ws[j]; + free(ws->name); + + while ((w = TAILQ_FIRST(&ws->winlist)) != NULL) { + TAILQ_REMOVE(&ws->winlist, w, entry); + free_window(w); + } + + 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); + free(r); + } + + while ((r = TAILQ_FIRST(&screens[i].orl)) != NULL) { + TAILQ_REMOVE(&screens[i].rl, r, entry); + free(r->bar); + free(r); + } } + free(screens); - if (bar_font_legacy) + free(bar_format); + free(bar_fonts); + free(clock_format); + free(startup_exception); + + if (bar_fs) XFreeFontSet(display, bar_fs); - else { + if (bar_font) XftFontClose(display, bar_font); - } xcb_key_symbols_free(syms); xcb_flush(conn); + xcb_aux_sync(conn); xcb_disconnect(conn); } @@ -10507,7 +11983,7 @@ event_error(xcb_generic_error_t *e) void event_handle(xcb_generic_event_t *evt) { - uint8_t type = XCB_EVENT_RESPONSE_TYPE(evt); + uint8_t type = XCB_EVENT_RESPONSE_TYPE(evt); DNPRINTF(SWM_D_EVENT, "XCB Event: %s(%d), seq %u\n", xcb_event_get_label(XCB_EVENT_RESPONSE_TYPE(evt)), @@ -10517,7 +11993,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); @@ -10528,14 +12004,14 @@ event_handle(xcb_generic_event_t *evt) EVENT(XCB_DESTROY_NOTIFY, destroynotify); EVENT(XCB_ENTER_NOTIFY, enternotify); EVENT(XCB_EXPOSE, expose); -#ifdef SWM_DEBUG EVENT(XCB_FOCUS_IN, focusin); +#ifdef SWM_DEBUG EVENT(XCB_FOCUS_OUT, focusout); #endif /*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); @@ -10546,7 +12022,7 @@ event_handle(xcb_generic_event_t *evt) EVENT(XCB_MOTION_NOTIFY, motionnotify); /*EVENT(XCB_NO_EXPOSURE, );*/ EVENT(XCB_PROPERTY_NOTIFY, propertynotify); - /*EVENT(XCB_REPARENT_NOTIFY, );*/ + EVENT(XCB_REPARENT_NOTIFY, reparentnotify); /*EVENT(XCB_RESIZE_REQUEST, );*/ /*EVENT(XCB_SELECTION_CLEAR, );*/ /*EVENT(XCB_SELECTION_NOTIFY, );*/ @@ -10555,29 +12031,29 @@ event_handle(xcb_generic_event_t *evt) /*EVENT(XCB_VISIBILITY_NOTIFY, );*/ #undef EVENT } - if (type - xrandr_eventbase == XCB_RANDR_SCREEN_CHANGE_NOTIFY) + if (type - randr_eventbase == XCB_RANDR_SCREEN_CHANGE_NOTIFY) screenchange((void *)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, startup = 1; + struct pollfd pfd[2]; struct sigaction sact; + struct stat sb; + struct passwd *pwd; + struct swm_region *r; xcb_generic_event_t *evt; - struct timeval tv; - fd_set rd; - int rd_max; - int stdin_ready = 0; - int num_readable; + 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 */ (void)argc; +#ifdef SWM_DEBUG time_started = time(NULL); +#endif start_argv = argv; warnx("Welcome to spectrwm V%s Build: %s", SPECTRWM_VERSION, buildstr); @@ -10619,7 +12095,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); @@ -10636,7 +12112,8 @@ main(int argc, char *argv[]) setup_globals(); setup_screens(); setup_ewmh(); - setup_keys(); + setup_keybindings(); + setup_btnbindings(); setup_quirks(); setup_spawn(); @@ -10695,21 +12172,31 @@ noconfig: grab_windows(); grabkeys(); - stack(); - bar_draw(); + grabbuttons(); + + /* Stack all regions to trigger mapping. */ + for (i = 0; i < num_screens; i++) + TAILQ_FOREACH(r, &screens[i].rl, entry) + stack(r); xcb_ungrab_server(conn); xcb_flush(conn); - /* Update state of each newly mapped workspace. */ + /* Update state and bar of each newly mapped workspace. */ for (i = 0; i < num_screens; i++) - TAILQ_FOREACH(r, &screens[i].rl, entry) + TAILQ_FOREACH(r, &screens[i].rl, entry) { r->ws->state = SWM_WS_STATE_MAPPED; + bar_draw(r->bar); + } - rd_max = xfd > STDIN_FILENO ? xfd : STDIN_FILENO; + memset(&pfd, 0, sizeof(pfd)); + pfd[0].fd = xfd; + pfd[0].events = POLLIN; + pfd[1].fd = STDIN_FILENO; + 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); @@ -10718,7 +12205,7 @@ noconfig: /* If just (re)started, set default focus if needed. */ if (startup) { - startup = 0; + startup = false; if (focus_mode != SWM_FOCUS_FOLLOW) { r = TAILQ_FIRST(&screens[0].rl); @@ -10730,36 +12217,34 @@ noconfig: } } - FD_ZERO(&rd); - - if (bar_extra) - FD_SET(STDIN_FILENO, &rd); + if (search_resp) + search_do_resp(); - FD_SET(xfd, &rd); - tv.tv_sec = 1; - tv.tv_usec = 0; - num_readable = select(rd_max + 1, &rd, NULL, NULL, &tv); - if (num_readable == -1 && errno != EINTR) { - DNPRINTF(SWM_D_MISC, "select failed"); - } else if (num_readable > 0 && FD_ISSET(STDIN_FILENO, &rd)) { - stdin_ready = 1; + 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) { + stdin_ready = true; } if (restart_wm) - restart(NULL, NULL); - - if (search_resp) - search_do_resp(); + restart(NULL, NULL, NULL); if (!running) goto done; if (stdin_ready) { - stdin_ready = 0; + stdin_ready = false; bar_extra_update(); } - bar_draw(); + /* Need to ensure the bar(s) are always updated. */ + for (i = 0; i < num_screens; i++) + TAILQ_FOREACH(r, &screens[i].rl, entry) + bar_draw(r->bar); + xcb_flush(conn); } done: