X-Git-Url: https://code.delx.au/spectrwm/blobdiff_plain/da92f07e6b07d2288083460f539d4bf3ed280f1a..refs/heads/hacking:/spectrwm.c diff --git a/spectrwm.c b/spectrwm.c index 63fa75e..a835d00 100644 --- a/spectrwm.c +++ b/spectrwm.c @@ -23,38 +23,6 @@ * 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 @@ -101,6 +69,7 @@ #include #include #include +#include #include #include #include @@ -249,7 +218,6 @@ uint32_t swm_debug = 0 #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) @@ -268,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) @@ -278,7 +248,8 @@ uint32_t swm_debug = 0 #define MAXIMIZED(w) (MAXIMIZED_VERT(w) || MAXIMIZED_HORZ(w)) #define MANUAL(w) ((w)->ewmh_flags & SWM_F_MANUAL) #define TRANS(w) ((w)->transient != XCB_WINDOW_NONE) -#define FLOATING(w) (ABOVE(w) || TRANS(w) || FULLSCREEN(w) || \ +#define FLOATING(w) (ABOVE(w) || TRANS(w)) +#define FLOATINGFULLMAX(w) (ABOVE(w) || TRANS(w) || FULLSCREEN(w) || \ MAXIMIZED(w)) /* Constrain Window flags */ @@ -295,17 +266,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) @@ -431,6 +397,7 @@ int region_padding = 0; int tile_gap = 0; bool java_workaround = true; bool verbose_layout = false; +bool track_pid_ws = true; #ifdef SWM_DEBUG bool debug_enabled; time_t time_started; @@ -491,6 +458,7 @@ struct ws_win { 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 */ bool g_floatvalid; /* g_float geometry validity */ bool mapped; @@ -532,6 +500,7 @@ void vertical_stack(struct workspace *, struct swm_geometry *); void horizontal_config(struct workspace *, int); void horizontal_stack(struct workspace *, struct swm_geometry *); void max_stack(struct workspace *, struct swm_geometry *); +void max_config(struct workspace *, int); void plain_stacker(struct workspace *); void fancy_stacker(struct workspace *); @@ -546,7 +515,7 @@ struct layout { /* stack, configure */ { vertical_stack, vertical_config, 0, plain_stacker }, { horizontal_stack, horizontal_config, 0, plain_stacker }, - { max_stack, NULL, + { max_stack, max_config, SWM_L_MAPONFOCUS | SWM_L_FOCUSPREV, plain_stacker }, { NULL, NULL, 0, NULL }, }; @@ -569,6 +538,7 @@ struct workspace { 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 */ @@ -632,6 +602,16 @@ struct swm_screen { }; struct swm_screen *screens; +struct layout_config { + bool configured; + int layout; + int master_grow; + int master_add; + int stack_inc; + int always_raise; + bool apply_flip; +} initial_layouts[SWM_WS_MAX]; + /* args to functions */ union arg { int id; @@ -648,7 +628,6 @@ union arg { #define SWM_ARG_ID_MASTERADD (22) #define SWM_ARG_ID_MASTERDEL (23) #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) @@ -661,8 +640,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) @@ -675,8 +652,6 @@ 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) @@ -746,6 +721,7 @@ enum { _NET_WM_STATE_SKIP_PAGER, _NET_WM_STATE_SKIP_TASKBAR, _NET_WM_WINDOW_TYPE, + _NET_WM_WINDOW_TYPE_DESKTOP, _NET_WM_WINDOW_TYPE_DIALOG, _NET_WM_WINDOW_TYPE_DOCK, _NET_WM_WINDOW_TYPE_NORMAL, @@ -790,6 +766,7 @@ struct ewmh_hint { {"_NET_WM_STATE_SKIP_PAGER", XCB_ATOM_NONE}, {"_NET_WM_STATE_SKIP_TASKBAR", XCB_ATOM_NONE}, {"_NET_WM_WINDOW_TYPE", XCB_ATOM_NONE}, + {"_NET_WM_WINDOW_TYPE_DESKTOP", XCB_ATOM_NONE}, {"_NET_WM_WINDOW_TYPE_DIALOG", XCB_ATOM_NONE}, {"_NET_WM_WINDOW_TYPE_DOCK", XCB_ATOM_NONE}, {"_NET_WM_WINDOW_TYPE_NORMAL", XCB_ATOM_NONE}, @@ -862,6 +839,7 @@ enum actionid { FN_FOCUS_NEXT, FN_FOCUS_PREV, FN_FOCUS_URGENT, + FN_FULLSCREEN_TOGGLE, FN_MAXIMIZE_TOGGLE, FN_HEIGHT_GROW, FN_HEIGHT_SHRINK, @@ -884,6 +862,8 @@ enum actionid { FN_MVRG_7, FN_MVRG_8, FN_MVRG_9, + KF_MVRG_NEXT, + KF_MVRG_PREV, FN_MVWS_1, FN_MVWS_2, FN_MVWS_3, @@ -908,6 +888,7 @@ enum actionid { FN_MVWS_22, FN_NAME_WORKSPACE, FN_QUIT, + FN_RAISE, FN_RAISE_TOGGLE, FN_RESIZE, FN_RESIZE_CENTERED, @@ -933,7 +914,7 @@ enum actionid { FN_STACK_BALANCE, FN_STACK_INC, FN_STACK_DEC, - FN_STACK_RESET, + FN_STACK_INIT, FN_SWAP_MAIN, FN_SWAP_NEXT, FN_SWAP_PREV, @@ -1019,11 +1000,12 @@ 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 *); -void bar_window_float(char *, size_t, struct swm_region *); +void bar_window_index_count(char *, size_t, struct swm_region *); 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 *); +void bar_workspace_state(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 *); @@ -1067,6 +1049,7 @@ void ewmh_apply_flags(struct ws_win *, uint32_t); void ewmh_autoquirk(struct ws_win *); void ewmh_get_desktop_names(void); void ewmh_get_wm_state(struct ws_win *); +int ewmh_handle_special_types(xcb_window_t, struct swm_region *); void ewmh_update_actions(struct ws_win *); void ewmh_update_client_list(void); void ewmh_update_current_desktop(void); @@ -1089,13 +1072,14 @@ void focus_flush(void); void focus_pointer(struct binding *, struct swm_region *, union arg *); void focus_region(struct swm_region *); void focus_win(struct ws_win *); -#ifdef SWM_DEBUG void focusin(xcb_focus_in_event_t *); +#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); @@ -1119,6 +1103,9 @@ 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 *); @@ -1127,6 +1114,7 @@ void grab_windows(void); void grabbuttons(void); void grabkeys(void); void iconify(struct binding *, struct swm_region *, union arg *); +void initlayout(struct workspace *); bool isxlfd(char *); bool keybindreleased(struct binding *, xcb_key_release_event_t *); void keypress(xcb_key_press_event_t *); @@ -1159,7 +1147,6 @@ void pressbutton(struct binding *, struct swm_region *, union arg *); void priorws(struct binding *, struct swm_region *, union arg *); #ifdef SWM_DEBUG void print_win_geom(xcb_window_t); -void print_win_input_model(struct ws_win *); #endif void propertynotify(xcb_property_notify_event_t *); void put_back_event(xcb_generic_event_t *); @@ -1169,6 +1156,7 @@ void quirk_remove(struct quirk *); void quirk_replace(struct quirk *, const char *, const char *, const char *, 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); @@ -1191,6 +1179,7 @@ void search_win(struct binding *, struct swm_region *, union arg *); void search_win_cleanup(void); 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); @@ -1537,6 +1526,51 @@ teardown_ewmh(void) } } +int +ewmh_handle_special_types(xcb_window_t id, struct swm_region *region) +{ + xcb_get_property_reply_t *r; + xcb_get_property_cookie_t c; + xcb_atom_t *type; + int i, n; + uint16_t configure_mask = 0; + uint32_t wa[2]; + + c = xcb_get_property(conn, 0, id, + ewmh[_NET_WM_WINDOW_TYPE].atom, XCB_ATOM_ATOM, 0, UINT32_MAX); + r = xcb_get_property_reply(conn, c, NULL); + if (r == NULL) + return 0; + + type = xcb_get_property_value(r); + n = xcb_get_property_value_length(r) / sizeof(xcb_atom_t); + + for (i = 0; i < n; i++) { + if (type[i] == ewmh[_NET_WM_WINDOW_TYPE_DESKTOP].atom) { + configure_mask = XCB_CONFIG_WINDOW_STACK_MODE | + XCB_CONFIG_WINDOW_SIBLING; + wa[0] = region->id; + wa[1] = XCB_STACK_MODE_ABOVE; + break; + } + + if (type[i] == ewmh[_NET_WM_WINDOW_TYPE_DOCK].atom) { + configure_mask = XCB_CONFIG_WINDOW_STACK_MODE; + wa[0] = XCB_STACK_MODE_ABOVE; + break; + } + } + free(r); + + if (configure_mask != 0) { + xcb_map_window(conn, id); + xcb_configure_window (conn, id, configure_mask, wa); + return 1; + } + + return 0; +} + void ewmh_autoquirk(struct ws_win *win) { @@ -1557,8 +1591,7 @@ ewmh_autoquirk(struct ws_win *win) for (i = 0; i < n; i++) { if (type[i] == ewmh[_NET_WM_WINDOW_TYPE_NORMAL].atom) break; - if (type[i] == ewmh[_NET_WM_WINDOW_TYPE_DOCK].atom || - type[i] == ewmh[_NET_WM_WINDOW_TYPE_TOOLBAR].atom || + if (type[i] == ewmh[_NET_WM_WINDOW_TYPE_TOOLBAR].atom || type[i] == ewmh[_NET_WM_WINDOW_TYPE_UTILITY].atom) { win->quirks = SWM_Q_FLOAT | SWM_Q_ANYWHERE; break; @@ -1570,6 +1603,25 @@ ewmh_autoquirk(struct ws_win *win) } } free(r); + + + c = xcb_get_property(conn, 0, win->id, + ewmh[_NET_WM_STATE].atom, XCB_ATOM_ATOM, 0, UINT32_MAX); + r = xcb_get_property_reply(conn, c, NULL); + if (r == NULL) + return; + + type = xcb_get_property_value(r); + n = xcb_get_property_value_length(r) / sizeof(xcb_atom_t); + + for (i = 0; i < n; i++) { + if (type[i] == ewmh[_NET_WM_STATE_SKIP_PAGER].atom || + type[i] == ewmh[_NET_WM_STATE_SKIP_TASKBAR].atom) { + win->quirks = SWM_Q_FLOAT | SWM_Q_ANYWHERE; + break; + } + } + free(r); } void @@ -1688,19 +1740,14 @@ ewmh_apply_flags(struct ws_win *win, uint32_t pending) } if (changed & EWMH_F_ABOVE) { - if (ws->cur_layout != &layouts[SWM_MAX_STACK]) { - if (ABOVE(win)) - load_float_geom(win); - else if (!MAXIMIZED(win)) - store_float_geom(win); + if (ABOVE(win)) + load_float_geom(win); + else if (!MAXIMIZED(win)) + store_float_geom(win); - win->ewmh_flags &= ~EWMH_F_MAXIMIZED; - changed &= ~EWMH_F_MAXIMIZED; - raise_window(win); - } else { - /* Revert. */ - win->ewmh_flags ^= EWMH_F_ABOVE & pending; - } + win->ewmh_flags &= ~EWMH_F_MAXIMIZED; + changed &= ~EWMH_F_MAXIMIZED; + raise_window(win); } if (changed & EWMH_F_MAXIMIZED) { @@ -1938,7 +1985,8 @@ debug_refresh(struct ws_win *win) break; } - if (asprintf(&s, "%#x wl:%d s:%d", win->id, widx, sidx) == -1) + 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); @@ -2380,6 +2428,30 @@ bar_window_class_instance(char *s, size_t sz, struct swm_region *r) bar_window_instance(s, sz, r); } +void +bar_window_index_count(char *s, size_t sz, struct swm_region *r) +{ + struct ws_win *w; + int count, index; + + if (r == NULL || r->ws == NULL || r->ws->focus == NULL) { + strlcat(s, "0/0", sz); + return; + } + + count = 0; + index = 0; + + TAILQ_FOREACH(w, &r->ws->winlist, entry) { + ++count; + if (w->id == r->ws->focus->id) { + index = count; + } + } + + snprintf(s, sz, "%d/%d", index, count); +} + void bar_window_state(char *s, size_t sz, struct swm_region *r) { @@ -2446,7 +2518,7 @@ bar_urgent(char *s, size_t sz) strlcat(s, "- ", sz); } } - if(urgent_collapse && s[0]) + if (urgent_collapse && s[0]) s[strlen(s) - 1] = 0; } @@ -2459,6 +2531,44 @@ bar_workspace_name(char *s, size_t sz, struct swm_region *r) strlcat(s, r->ws->name, sz); } +void +bar_workspace_state(char *s, size_t sz, struct swm_region *r) +{ + struct ws_win *win; + int i, j, num_screens; + bool used_workspaces[SWM_WS_MAX]; + char tmp[8]; + int first = 1; + char *fmt; + + if (r == NULL || r->ws == NULL) + return; + + for (i = 0; i < SWM_WS_MAX; ++i) + used_workspaces[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) + ++used_workspaces[win->ws->idx]; + + for (i = 0; i < SWM_WS_MAX; ++i) { + fmt = NULL; + if (i == r->ws->idx) { + fmt = " [%d]"; + } else if (used_workspaces[i]) { + fmt = " %d"; + } + if (fmt) { + fmt = fmt + first; + first = 0; + snprintf(tmp, sizeof tmp, fmt, i + 1); + strlcat(s, tmp, sz); + } + } +} + /* build the default bar format according to the defined enabled options */ void bar_fmt(const char *fmtexp, char *fmtnew, struct swm_region *r, size_t sz) @@ -2581,6 +2691,9 @@ bar_replace_seq(char *fmt, char *fmtrep, struct swm_region *r, size_t *offrep, case 'I': snprintf(tmp, sizeof tmp, "%d", r->ws->idx + 1); break; + case 'i': + bar_workspace_state(tmp, sizeof tmp, r); + break; case 'M': count = 0; TAILQ_FOREACH(w, &r->ws->winlist, entry) @@ -2592,6 +2705,9 @@ bar_replace_seq(char *fmt, char *fmtrep, struct swm_region *r, size_t *offrep, case 'N': snprintf(tmp, sizeof tmp, "%d", r->s->idx + 1); break; + case 'p': + bar_window_index_count(tmp, sizeof tmp, r); + break; case 'P': bar_window_class_instance(tmp, sizeof tmp, r); break; @@ -3237,13 +3353,13 @@ config_win(struct ws_win *win, xcb_configure_request_event_t *ev) } int -count_win(struct workspace *ws, bool count_transient) +count_win(struct workspace *ws, bool count_floating) { struct ws_win *win; int count = 0; TAILQ_FOREACH(win, &ws->winlist, entry) { - if (!count_transient && FLOATING(win)) + if (!count_floating && FLOATING(win)) continue; if (ICONIC(win)) continue; @@ -3388,11 +3504,11 @@ raise_window(struct ws_win *win) void update_win_stacking(struct ws_win *win) { - struct ws_win *sibling; #ifdef SWM_DEBUG struct ws_win *w; #endif struct swm_region *r; + uint16_t configure_mask; uint32_t val[2]; if (win == NULL || (r = win->ws->r) == NULL) @@ -3404,23 +3520,25 @@ update_win_stacking(struct ws_win *win) return; } - sibling = TAILQ_NEXT(win, stack_entry); - 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] = r->id; + if (FLOATINGFULLMAX(win) || (win->ws->always_raise && + win->ws->focus == win)) { + configure_mask = XCB_CONFIG_WINDOW_STACK_MODE; + val[0] = XCB_STACK_MODE_ABOVE; + val[1] = 0; - DNPRINTF(SWM_D_EVENT, "update_win_stacking: win %#x (%#x), " - "sibling %#x\n", win->frame, win->id, val[0]); + DNPRINTF(SWM_D_EVENT, "update_win_stacking: to very top " + "win %#x (%#x), ", win->frame, win->id); + } else { + configure_mask = XCB_CONFIG_WINDOW_STACK_MODE | + XCB_CONFIG_WINDOW_SIBLING; + val[0] = r->bar->id; + val[1] = XCB_STACK_MODE_BELOW; - val[1] = XCB_STACK_MODE_ABOVE; + DNPRINTF(SWM_D_EVENT, "update_win_stacking: to tile top " + "win %#x (%#x), ", win->frame, win->id); + } - xcb_configure_window(conn, win->frame, XCB_CONFIG_WINDOW_SIBLING | - XCB_CONFIG_WINDOW_STACK_MODE, val); + xcb_configure_window(conn, win->frame, configure_mask, val); #ifdef SWM_DEBUG TAILQ_FOREACH(w, &win->ws->winlist, entry) @@ -3682,10 +3800,12 @@ find_frame_window(xcb_window_t id) { 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 *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++) @@ -3693,7 +3813,6 @@ find_window(xcb_window_t id) if (id == win->id || id == win->frame) return (win); - /* win NULL */ /* If window isn't top-level, try to find managed ancestor. */ qtr = xcb_query_tree_reply(conn, xcb_query_tree(conn, id), NULL); @@ -3739,23 +3858,34 @@ spawn(int ws_idx, union arg *args, bool close_fd) close(xcb_get_file_descriptor(conn)); - setenv("LD_PRELOAD", SWM_LIB, 1); + if (track_pid_ws) { + 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"); - _exit(1); - } - setenv("_SWM_WS", ret, 1); - free(ret); - ret = NULL; + if (asprintf(&ret, "%d", ws_idx) == -1) { + warn("spawn: asprintf SWM_WS"); + _exit(1); + } + setenv("_SWM_WS", ret, 1); + free(ret); + ret = NULL; - if (asprintf(&ret, "%d", getpid()) == -1) { - warn("spawn: asprintf _SWM_PID"); - _exit(1); + if (asprintf(&ret, "%d", getpid()) == -1) { + warn("spawn: asprintf _SWM_PID"); + _exit(1); + } + setenv("_SWM_PID", ret, 1); + free(ret); + ret = NULL; } - setenv("_SWM_PID", ret, 1); - free(ret); - ret = NULL; if (setsid() == -1) { warn("spawn: setsid"); @@ -3805,6 +3935,8 @@ kill_refs(struct ws_win *win) 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) @@ -3890,6 +4022,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)) { @@ -3928,7 +4063,8 @@ focus_win(struct ws_win *win) { 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)); @@ -3951,15 +4087,24 @@ focus_win(struct ws_win *win) gifr->focus); cfw = find_window(gifr->focus); - if (cfw != NULL && cfw != win) { - if (cfw->ws != ws && cfw->ws->r != NULL && - cfw->frame != XCB_WINDOW_NONE) { - draw_frame(cfw); - } 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) { @@ -3988,8 +4133,7 @@ focus_win(struct ws_win *win) 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.input) { + 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); @@ -4058,6 +4202,8 @@ focus_win(struct ws_win *win) } out: + free(gifr); + free(war); DNPRINTF(SWM_D_FOCUS, "focus_win: done.\n"); } @@ -4146,6 +4292,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(); } @@ -4190,8 +4340,6 @@ switchws(struct binding *bp, struct swm_region *r, union arg *args) int wsid = args->id; bool unmap_old = false; - (void)bp; - if (!(r && r->s)) return; @@ -4211,7 +4359,8 @@ switchws(struct binding *bp, struct swm_region *r, union arg *args) return; other_r = new_ws->r; - if (other_r && workspace_clamp) { + 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) { @@ -4512,9 +4661,6 @@ swapwin(struct binding *bp, struct swm_region *r, union arg *args) goto out; } - if (r->ws->cur_layout == &layouts[SWM_MAX_STACK]) - return; - clear_maximized(r->ws); source = cur_focus; @@ -4889,14 +5035,15 @@ stack_config(struct binding *bp, struct swm_region *r, union arg *args) DNPRINTF(SWM_D_STACK, "stack_config: id: %d workspace: %d\n", args->id, ws->idx); - if (clear_maximized(ws) > 0) - stack(r); - - if (ws->cur_layout->l_config != NULL) + if (args->id == SWM_ARG_ID_STACKINIT) { + initlayout(ws); + } else { ws->cur_layout->l_config(ws, args->id); + } + + clear_maximized(ws); + stack(r); - if (args->id != SWM_ARG_ID_STACKINIT) - stack(r); bar_draw(r->bar); center_pointer(r); @@ -5139,6 +5286,8 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, bool flip) 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, false)) > 0) { /* Find first tiled window. */ @@ -5206,7 +5355,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, bool flip) if (ICONIC(win)) continue; - if (FLOATING(win)) { + if (FLOATINGFULLMAX(win)) { update_floater(win); continue; } @@ -5340,7 +5489,6 @@ vertical_config(struct workspace *ws, int id) id, ws->idx); switch (id) { - case SWM_ARG_ID_STACKRESET: case SWM_ARG_ID_STACKINIT: ws->l_state.vertical_msize = SWM_V_SLICE / 2; ws->l_state.vertical_mwin = 1; @@ -5393,7 +5541,6 @@ horizontal_config(struct workspace *ws, int id) DNPRINTF(SWM_D_STACK, "horizontal_config: workspace: %d\n", ws->idx); switch (id) { - case SWM_ARG_ID_STACKRESET: case SWM_ARG_ID_STACKINIT: ws->l_state.horizontal_mwin = 1; ws->l_state.horizontal_msize = SWM_H_SLICE / 2; @@ -5446,15 +5593,13 @@ max_stack(struct workspace *ws, struct swm_geometry *g) { struct swm_geometry gg = *g; struct ws_win *w, *win = NULL, *parent = NULL, *tmpw; - int winno; DNPRINTF(SWM_D_STACK, "max_stack: workspace: %d\n", ws->idx); if (ws == NULL) return; - winno = count_win(ws, false); - if (winno == 0 && count_win(ws, true) == 0) + if (count_win(ws, true) == 0) return; /* Figure out which top level window should be visible. */ @@ -5477,17 +5622,13 @@ max_stack(struct workspace *ws, struct swm_geometry *g) if (ICONIC(w)) continue; - if (TRANS(w)) { + if (TRANS(w) || ABOVE(w)) { update_floater(w); continue; } /* Set maximized flag for all maxed windows. */ if (!MAXIMIZED(w)) { - /* Preserve floating geometry. */ - if (ABOVE(w)) - store_float_geom(w); - ewmh_apply_flags(w, w->ewmh_flags | EWMH_F_MAXIMIZED); ewmh_update_wm_state(w); } @@ -5534,6 +5675,16 @@ max_stack(struct workspace *ws, struct swm_geometry *g) map_window(w); } +void +max_config(struct workspace *ws, int id) +{ + /* suppress unused warning since vars are needed */ + (void)ws; + (void)id; + + DNPRINTF(SWM_D_STACK, "max_config: workspace: %d (noop)\n", ws->idx); +} + void send_to_rg(struct binding *bp, struct swm_region *r, union arg *args) { @@ -5648,6 +5799,30 @@ send_to_ws(struct binding *bp, struct swm_region *r, union arg *args) 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, bool unfocus) { @@ -5755,6 +5930,31 @@ pressbutton(struct binding *bp, struct swm_region *r, union arg *args) XCB_CURRENT_TIME, XCB_WINDOW_NONE, 0, 0, 0); } +void +raise_focus(struct binding *bp, struct swm_region *r, union arg *args) +{ + struct ws_win *win; + uint32_t val; + + /* Suppress warning. */ + (void)bp; + (void)args; + + if (r == NULL || r->ws == NULL || r->ws->focus == NULL) + return; + + win = r->ws->focus; + r->ws->focus_raise = win; + raise_window(win); + + /* 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 raise_toggle(struct binding *bp, struct swm_region *r, union arg *args) { @@ -6137,7 +6337,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."); @@ -6515,6 +6715,32 @@ floating_toggle(struct binding *bp, struct swm_region *r, union arg *args) 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) { @@ -6700,7 +6926,16 @@ update_window(struct ws_win *win) "(%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); - config_win(win, NULL); + + /* 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 { @@ -6710,7 +6945,7 @@ struct event { SIMPLEQ_HEAD(event_queue, event) events = SIMPLEQ_HEAD_INITIALIZER(events); xcb_generic_event_t * -get_next_event(bool wait) +get_next_event(bool dowait) { struct event *ep; xcb_generic_event_t *evt; @@ -6720,7 +6955,7 @@ get_next_event(bool wait) evt = ep->ev; SIMPLEQ_REMOVE_HEAD(&events, entry); free(ep); - } else if (wait) + } else if (dowait) evt = xcb_wait_for_event(conn); else evt = xcb_poll_for_event(conn); @@ -6792,8 +7027,8 @@ resize_win(struct ws_win *win, struct binding *bp, int opt) if (FULLSCREEN(win)) return; - /* In max_stack mode, should only resize transients. */ - if (win->ws->cur_layout == &layouts[SWM_MAX_STACK] && !TRANS(win)) + /* In max_stack mode, should only resize transients/floating. */ + if (win->ws->cur_layout == &layouts[SWM_MAX_STACK] && !TRANS(win) && !ABOVE(win)) return; DNPRINTF(SWM_D_EVENT, "resize: win %#x, floating: %s, " @@ -7081,10 +7316,6 @@ move_win(struct ws_win *win, struct binding *bp, int opt) DNPRINTF(SWM_D_EVENT, "move: win %#x, floating: %s, transient: " "%#x\n", win->id, YESNO(ABOVE(win)), win->transient); - /* in max_stack mode should only move transients */ - if (win->ws->cur_layout == &layouts[SWM_MAX_STACK] && !TRANS(win)) - return; - if (!(ABOVE(win) || TRANS(win)) || MAXIMIZED(win)) { store_float_geom(win); restack = true; @@ -7292,6 +7523,7 @@ struct action { { "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} }, @@ -7314,6 +7546,8 @@ struct action { { "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} }, @@ -7338,6 +7572,7 @@ struct action { { "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} }, @@ -7363,7 +7598,7 @@ struct action { { "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} }, + { "stack_reset", stack_config, 0, {.id = SWM_ARG_ID_STACKINIT} }, { "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} }, @@ -8075,6 +8310,7 @@ setup_keybindings(void) 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); @@ -8120,6 +8356,7 @@ setup_keybindings(void) 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); @@ -8143,7 +8380,7 @@ setup_keybindings(void) 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(MODSHIFT, XK_space, FN_STACK_INIT); BINDKEY(MODKEY, XK_Return, FN_SWAP_MAIN); BINDKEY(MODSHIFT, XK_j, FN_SWAP_NEXT); BINDKEY(MODSHIFT, XK_k, FN_SWAP_PREV); @@ -8768,6 +9005,7 @@ enum { SWM_S_STACK_ENABLED, SWM_S_TERM_WIDTH, SWM_S_TILE_GAP, + SWM_S_TRACK_PID_WS, SWM_S_URGENT_COLLAPSE, SWM_S_URGENT_ENABLED, SWM_S_VERBOSE_LAYOUT, @@ -8976,6 +9214,9 @@ setconfvalue(const char *selector, const char *value, int flags) case SWM_S_TILE_GAP: tile_gap = atoi(value); break; + case SWM_S_TRACK_PID_WS: + track_pid_ws = atoi(value); + break; case SWM_S_URGENT_COLLAPSE: urgent_collapse = (atoi(value) != 0); break; @@ -9201,7 +9442,7 @@ int setlayout(const char *selector, const char *value, int flags) { struct workspace *ws; - int ws_id, i, x, mg, ma, si, ar; + int ws_id, i, mg, ma, si, ar; int st = SWM_V_STACK, num_screens; char s[1024]; bool f = false; @@ -9210,9 +9451,6 @@ setlayout(const char *selector, const char *value, int flags) (void)selector; (void)flags; - if (getenv("SWM_STARTED")) - return (0); - bzero(s, sizeof s); if (sscanf(value, "ws[%d]:%d:%d:%d:%d:%1023c", &ws_id, &mg, &ma, &si, &ar, s) != 6) @@ -9235,46 +9473,69 @@ setlayout(const char *selector, const char *value, int flags) f = true; } else if (strcasecmp(s, "fullscreen") == 0) st = SWM_MAX_STACK; - else + else { errx(1, "invalid layout entry, should be 'ws[]:" "::::" "'"); + return 0; + } + + initial_layouts[ws_id].configured = true; + initial_layouts[ws_id].layout = st; + initial_layouts[ws_id].master_grow = mg; + initial_layouts[ws_id].master_add = ma; + initial_layouts[ws_id].stack_inc = si; + initial_layouts[ws_id].always_raise = ar; + initial_layouts[ws_id].apply_flip = f; num_screens = get_screen_count(); - for (i = 0; i < num_screens; i++) { - ws = (struct workspace *)&screens[i].ws; - ws[ws_id].cur_layout = &layouts[st]; + for (i = 0; i < num_screens; ++i) { + ws = &screens[i].ws[ws_id]; + initlayout(ws); + } - ws[ws_id].always_raise = (ar != 0); - if (st == SWM_MAX_STACK) - continue; + return 0; +} - /* master grow */ - for (x = 0; x < abs(mg); x++) { - ws[ws_id].cur_layout->l_config(&ws[ws_id], - mg >= 0 ? SWM_ARG_ID_MASTERGROW : - SWM_ARG_ID_MASTERSHRINK); - } - /* 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 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); - } - /* Apply flip */ - if (f) { - ws[ws_id].cur_layout->l_config(&ws[ws_id], - SWM_ARG_ID_FLIPLAYOUT); - } - } +void +initlayout(struct workspace *ws) +{ + int i, ws_id; - return (0); + ws_id = ws->idx; + + if (!initial_layouts[ws_id].configured) + return; + + ws->cur_layout = &layouts[initial_layouts[ws_id].layout]; + + ws->cur_layout->l_config(ws, SWM_ARG_ID_STACKINIT); + + ws->always_raise = (initial_layouts[ws_id].always_raise != 0); + + /* master grow */ + for (i = 0; i < abs(initial_layouts[ws_id].master_grow); i++) { + ws->cur_layout->l_config(ws, + initial_layouts[ws_id].master_grow >= 0 ? SWM_ARG_ID_MASTERGROW : + SWM_ARG_ID_MASTERSHRINK); + } + /* master add */ + for (i = 0; i < abs(initial_layouts[ws_id].master_add); i++) { + ws->cur_layout->l_config(ws, + initial_layouts[ws_id].master_add >= 0 ? SWM_ARG_ID_MASTERADD : + SWM_ARG_ID_MASTERDEL); + } + /* stack inc */ + for (i = 0; i < abs(initial_layouts[ws_id].stack_inc); i++) { + ws->cur_layout->l_config(ws, + initial_layouts[ws_id].stack_inc >= 0 ? SWM_ARG_ID_STACKINC : + SWM_ARG_ID_STACKDEC); + } + /* Apply flip */ + if (initial_layouts[ws_id].apply_flip) { + ws->cur_layout->l_config(ws, + SWM_ARG_ID_FLIPLAYOUT); + } } /* config options */ @@ -9334,6 +9595,7 @@ 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. */ + { "track_pid_ws", setconfvalue, SWM_S_TRACK_PID_WS }, { "urgent_collapse", setconfvalue, SWM_S_URGENT_COLLAPSE }, { "urgent_enabled", setconfvalue, SWM_S_URGENT_ENABLED }, { "verbose_layout", setconfvalue, SWM_S_VERBOSE_LAYOUT }, @@ -9476,6 +9738,10 @@ conf_load(const char *filename, int keymapping) configopt[optidx].optname); continue; } + /* trim trailing spaces */ + ce = optval + strlen(optval) - 1; + while (ce > optval && isspace(*ce)) --ce; + *(ce + 1) = '\0'; /* call function to deal with it all */ if (configopt[optidx].func(optsub, optval, configopt[optidx].funcflags) != 0) { @@ -9708,7 +9974,7 @@ unparent_window(struct ws_win *win) struct ws_win * manage_window(xcb_window_t id, int spawn_pos, bool mapping) { - struct ws_win *win, *ww; + struct ws_win *win = NULL, *ww; struct swm_region *r; struct pid_e *p; struct quirk *qp; @@ -9742,11 +10008,6 @@ manage_window(xcb_window_t id, int spawn_pos, bool mapping) DNPRINTF(SWM_D_MISC, "manage_window: win %#x found on " "unmanaged list.\n", id); TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry); - - if (TRANS(win)) - set_child_transient(win, &trans); - - goto remanage; } else { DNPRINTF(SWM_D_MISC, "manage_window: win %#x is new.\n", id); } @@ -9778,15 +10039,24 @@ manage_window(xcb_window_t id, int spawn_pos, bool mapping) goto out; } - /* Create and initialize ws_win object. */ - if ((win = calloc(1, sizeof(struct ws_win))) == NULL) - err(1, "manage_window: calloc: failed to allocate memory for " - "new window"); + /* Figure out which region the window belongs to. */ + r = root_to_region(gr->root, SWM_CK_ALL); - win->id = id; + /* Handle special windows with special _NET_WM_WINDOW_TYPE */ + if (ewmh_handle_special_types(id, r)) { + DNPRINTF(SWM_D_EVENT, "manage_window: " + "unmanaged ewmh window type\n"); + goto out; + } - /* Figureout which region the window belongs to. */ - r = root_to_region(gr->root, SWM_CK_ALL); + if (!win) { + /* Create and initialize ws_win object. */ + if ((win = calloc(1, sizeof(struct ws_win))) == NULL) + err(1, "manage_window: calloc: failed to allocate " + "memory for new window"); + + win->id = id; + } /* Ignore window border if there is one. */ WIDTH(win) = gr->width; @@ -9829,7 +10099,8 @@ manage_window(xcb_window_t id, int spawn_pos, bool mapping) #ifdef SWM_DEBUG /* Must be after getting WM_HINTS and WM_PROTOCOLS. */ - print_win_input_model(win); + DNPRINTF(SWM_D_FOCUS, "manage_window: input_model: %s\n", + get_win_input_model(win)); #endif /* Set initial quirks based on EWMH. */ @@ -9916,7 +10187,6 @@ manage_window(xcb_window_t id, int spawn_pos, bool mapping) update_window(win); } -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); @@ -10036,15 +10306,29 @@ expose(xcb_expose_event_t *e) 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) { @@ -10146,7 +10430,7 @@ keyrelease(xcb_key_release_event_t *e) 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; struct action *ap; struct binding *bp; @@ -10190,8 +10474,15 @@ buttonpress(xcb_button_press_event_t *e) win = find_window(e->event); } - if (win) - focus_win(get_focus_magic(win)); + 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); + } /* Handle any bound action. */ bp = binding_lookup(CLEANMASK(e->state), BTNBIND, e->detail); @@ -10259,8 +10550,8 @@ buttonrelease(xcb_button_release_event_t *e) } #ifdef SWM_DEBUG -void -print_win_input_model(struct ws_win *win) +char * +get_win_input_model(struct ws_win *win) { char *inputmodel; /* @@ -10271,13 +10562,12 @@ print_win_input_model(struct ws_win *win) * Globally Active False Present */ - if (!(win->hints.flags & XCB_ICCCM_WM_HINT_INPUT) || win->hints.input) + if (ACCEPTS_FOCUS(win)) inputmodel = (win->take_focus) ? "Locally Active" : "Passive"; else inputmodel = (win->take_focus) ? "Globally Active" : "No Input"; - DNPRINTF(SWM_D_FOCUS, "print_win_input_model: win %#x, model: %s\n", - win->id, inputmodel); + return inputmodel; } void @@ -10307,7 +10597,7 @@ get_stack_mode_name(uint8_t mode) { char *name; - switch(mode) { + switch (mode) { case XCB_STACK_MODE_ABOVE: name = "Above"; break; @@ -10810,35 +11100,17 @@ 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, 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) { @@ -10868,7 +11140,6 @@ maprequest(xcb_map_request_event_t *e) if (focus_mode == SWM_FOCUS_DEFAULT) event_drain(XCB_ENTER_NOTIFY); out: - free(war); DNPRINTF(SWM_D_EVENT, "maprequest: done.\n"); } @@ -11001,7 +11272,7 @@ reparentnotify(xcb_reparent_notify_event_t *e) if (win->state == SWM_WIN_STATE_REPARENTING) { win->state = SWM_WIN_STATE_REPARENTED; - if (win->ws->r) + if (win->ws->r && !ICONIC(win)) map_window(win); else unmap_window(win); @@ -11427,7 +11698,6 @@ scan_randr(int idx) 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; @@ -11502,13 +11772,8 @@ scan_randr(int idx) screen->height_in_pixels); out: - /* Cleanup unused previously visible workspaces. */ + /* The screen shouldn't focus on unused regions. */ TAILQ_FOREACH(r, &screens[idx].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[idx].r_focus == r) screens[idx].r_focus = NULL; } @@ -11520,7 +11785,9 @@ 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); @@ -11539,32 +11806,37 @@ screenchange(xcb_randr_screen_change_notify_event_t *e) 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 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; } } focus_flush(); /* Update workspace state and bar on all regions. */ - for (i = 0; i < num_screens; i++) - TAILQ_FOREACH(r, &screens[i].rl, entry) { - r->ws->state = SWM_WS_STATE_MAPPED; - bar_draw(r->bar); - } + TAILQ_FOREACH(r, &screens[i].rl, entry) { + r->ws->state = SWM_WS_STATE_MAPPED; + bar_draw(r->bar); + } } void @@ -11731,6 +12003,7 @@ setup_screens(void) 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; @@ -11739,9 +12012,7 @@ setup_screens(void) TAILQ_INIT(&ws->unmanagedlist); for (k = 0; layouts[k].l_stack != NULL; k++) - if (layouts[k].l_config != NULL) - layouts[k].l_config(ws, - SWM_ARG_ID_STACKINIT); + layouts[k].l_config(ws, SWM_ARG_ID_STACKINIT); ws->cur_layout = &layouts[0]; ws->cur_layout->l_string(ws); } @@ -11906,8 +12177,8 @@ 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, );*/