X-Git-Url: https://code.delx.au/spectrwm/blobdiff_plain/37f7ee73cc676ce1c964c8d0455ef75f669e60f8..HEAD:/spectrwm.c diff --git a/spectrwm.c b/spectrwm.c index f5b4eac..285bac9 100644 --- a/spectrwm.c +++ b/spectrwm.c @@ -5,7 +5,7 @@ * Copyright (c) 2009 Pierre-Yves Ritschard * Copyright (c) 2010 Tuukka Kataja * Copyright (c) 2011 Jason L. Wright - * Copyright (c) 2011-2015 Reginald Kennedy + * Copyright (c) 2011-2016 Reginald Kennedy * Copyright (c) 2011-2012 Lawrence Teo * Copyright (c) 2011-2012 Tiago Cunha * Copyright (c) 2012-2015 David Hill @@ -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,13 +218,11 @@ 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) #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) @@ -269,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) @@ -296,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) @@ -318,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; @@ -432,6 +397,7 @@ int tile_gap = 0; bool java_workaround = true; bool verbose_layout = false; #ifdef SWM_DEBUG +bool debug_enabled; time_t time_started; #endif pid_t bar_pid; @@ -460,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" */ @@ -474,16 +441,26 @@ 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 */ 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]; @@ -498,6 +475,9 @@ struct ws_win { 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); @@ -512,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); @@ -555,6 +535,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 */ @@ -647,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) @@ -661,8 +640,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) @@ -712,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, @@ -755,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}, @@ -846,6 +825,7 @@ enum actionid { FN_FOCUS_NEXT, FN_FOCUS_PREV, FN_FOCUS_URGENT, + FN_FULLSCREEN_TOGGLE, FN_MAXIMIZE_TOGGLE, FN_HEIGHT_GROW, FN_HEIGHT_SHRINK, @@ -868,6 +848,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, @@ -892,6 +874,7 @@ enum actionid { FN_MVWS_22, FN_NAME_WORKSPACE, FN_QUIT, + FN_RAISE, FN_RAISE_TOGGLE, FN_RESIZE, FN_RESIZE_CENTERED, @@ -956,7 +939,10 @@ enum actionid { FN_WS_PREV_ALL, FN_WS_PREV_MOVE, FN_WS_PRIOR, - FN_DUMPWINS, /* MUST BE LAST */ + /* SWM_DEBUG actions MUST be here: */ + FN_DEBUG_TOGGLE, + FN_DUMPWINS, + /* ALWAYS last: */ FN_INVALID }; @@ -989,7 +975,7 @@ 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); @@ -1033,6 +1019,10 @@ void custom_region(const char *); 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 binding *, struct swm_region *, union arg *); int enable_wm(void); @@ -1054,7 +1044,10 @@ 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 binding *, struct swm_region *, union arg *); @@ -1063,13 +1056,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); @@ -1093,6 +1087,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 *); @@ -1133,7 +1130,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 *); @@ -1143,11 +1139,14 @@ 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); struct swm_region *region_under(struct swm_screen *, int, int); void regionize(struct ws_win *, int, int); +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 *); @@ -1163,6 +1162,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); @@ -1215,11 +1215,12 @@ void unmanage_window(struct ws_win *); void unmap_all(void); void unmap_window(struct ws_win *); void unmapnotify(xcb_unmap_notify_event_t *); +void unparent_window(struct ws_win *); void update_floater(struct ws_win *); 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); @@ -1461,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); @@ -1702,7 +1694,7 @@ ewmh_apply_flags(struct ws_win *win, uint32_t pending) } } - update_window_color(win); + draw_frame(win); raise_window(win); } @@ -1807,8 +1799,8 @@ dumpwins(struct binding *bp, 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 @@ -1818,8 +1810,8 @@ dumpwins(struct binding *bp, 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))); } @@ -1840,6 +1832,156 @@ dumpwins(struct binding *bp, 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 binding *b, struct swm_region *r, union arg *s) @@ -1848,6 +1990,14 @@ dumpwins(struct binding *b, struct swm_region *r, union arg *s) (void)r; (void)s; } + +void +debug_toggle(struct binding *b, struct swm_region *r, union arg *s) +{ + (void)b; + (void)r; + (void)s; +} #endif /* SWM_DEBUG */ void @@ -2269,7 +2419,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; } @@ -2510,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); } /* @@ -2637,10 +2784,13 @@ bar_toggle(struct binding *bp, 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(); } @@ -2718,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); @@ -2830,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; @@ -2922,6 +3074,9 @@ get_win_state(xcb_window_t w) void 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; @@ -2934,8 +3089,13 @@ version(struct binding *bp, 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 @@ -2984,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 */ @@ -3035,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; } @@ -3202,29 +3362,43 @@ void update_win_stacking(struct ws_win *win) { struct ws_win *sibling; +#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) || (win->ws->always_raise && win->ws->focus == win))) - val[0] = sibling->id; + 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; - 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 @@ -3239,6 +3413,7 @@ 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 = true; @@ -3257,6 +3432,7 @@ 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 = false; } @@ -3365,7 +3541,7 @@ center_pointer(struct swm_region *r) DNPRINTF(SWM_D_EVENT, "center_pointer: win %#x.\n", WINID(win)); if (win && win->mapped) - xcb_warp_pointer(conn, XCB_NONE, win->id, 0, 0, 0, 0, + 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, @@ -3430,56 +3606,100 @@ 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); } @@ -3493,7 +3713,16 @@ spawn(int ws_idx, union arg *args, bool 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"); @@ -3557,6 +3786,10 @@ kill_refs(struct ws_win *win) 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) @@ -3642,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)) { @@ -3654,7 +3890,7 @@ 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) @@ -3680,7 +3916,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)); @@ -3699,30 +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); - - /* Update border width */ - if (cfw->bordered && - (cfw->quirks & SWM_Q_MINIMALBORDER) && - FLOATING(cfw)) { - cfw->bordered = 0; - X(cfw) += border_width; - Y(cfw) += border_width; - update_window(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 { - 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) { @@ -3741,15 +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.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) { @@ -3802,25 +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); - - /* Update border width */ - if (!win->bordered && WS_FOCUSED(win->ws) && - (win->quirks & SWM_Q_MINIMALBORDER) && FLOATING(win)) { - win->bordered = 1; - X(win) -= border_width; - Y(win) -= border_width; - update_window(win); - } + bar_draw(ws->r->bar); } out: - bar_draw(); - + free(gifr); + free(war); DNPRINTF(SWM_D_FOCUS, "focus_win: done.\n"); } @@ -3909,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(); } @@ -3929,14 +4169,17 @@ 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(); } } @@ -3950,8 +4193,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; @@ -3971,7 +4212,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) { @@ -3985,7 +4227,7 @@ switchws(struct binding *bp, struct swm_region *r, union arg *args) } 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, @@ -4016,7 +4258,9 @@ switchws(struct binding *bp, 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) { @@ -4035,9 +4279,11 @@ switchws(struct binding *bp, 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(); @@ -4329,7 +4575,7 @@ swapwin(struct binding *bp, struct swm_region *r, union arg *args) sort_windows(wl); ewmh_update_client_list(); - stack(); + stack(r); center_pointer(r); focus_flush(); out: @@ -4587,7 +4833,7 @@ focus(struct binding *bp, struct swm_region *r, union arg *args) } if (clear_maximized(ws) > 0) - stack(); + stack(r); focus_win(get_focus_magic(winfocus)); center_pointer(r); @@ -4626,8 +4872,8 @@ cycle_layout(struct binding *bp, 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)); @@ -4646,79 +4892,71 @@ stack_config(struct binding *bp, struct swm_region *r, union arg *args) 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--; @@ -4802,6 +5040,9 @@ update_floater(struct ws_win *win) } 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; } @@ -4830,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); } } @@ -4882,7 +5121,7 @@ adjust_font(struct ws_win *win) void 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 = 0, j = 0, s = 0, stacks = 0; int w_inc = 1, h_inc, w_base = 1, h_base; @@ -4892,9 +5131,18 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, bool flip) 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, false)) > 0) { /* Find first tiled window. */ @@ -4917,7 +5165,10 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, bool 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; @@ -4930,26 +5181,27 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, bool 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; } @@ -4973,24 +5225,24 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, bool 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; @@ -5001,55 +5253,62 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, bool 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; - if (disable_border && !(bar_enabled && ws->bar_enabled) && - winno == 1) { - bordered = false; - win_g.w += 2 * border_width; - win_g.h += 2 * border_width; - } else { + /* Window coordinates exclude frame. */ + + if (winno > 1 || !disable_border || + (bar_enabled && ws->bar_enabled)) { bordered = true; + } else { + 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) { + if (X(win) != cell.y || Y(win) != cell.x || + WIDTH(win) != cell.h || HEIGHT(win) != cell.w) { reconfigure = true; - X(win) = win_g.y; - Y(win) = win_g.x; - WIDTH(win) = win_g.h; - HEIGHT(win) = win_g.w; + 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) { + if (X(win) != cell.x || Y(win) != cell.y || + WIDTH(win) != cell.w || HEIGHT(win) != cell.h) { reconfigure = true; - X(win) = win_g.x; - Y(win) = win_g.y; - WIDTH(win) = win_g.w; - HEIGHT(win) = win_g.h; + 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 = true; win->bordered = bordered; @@ -5060,7 +5319,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, bool flip) update_window(win); } - last_h = win_g.h; + last_h = cell.h; i++; j++; } @@ -5248,6 +5507,8 @@ max_stack(struct workspace *ws, struct swm_geometry *g) HEIGHT(w) += 2 * border_width; } else { w->bordered = true; + X(w) += border_width; + Y(w) += border_width; } update_window(w); @@ -5351,7 +5612,7 @@ send_to_ws(struct binding *bp, 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, " @@ -5363,19 +5624,27 @@ send_to_ws(struct binding *bp, 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); - stack(); + /* If destination ws has a region, restack. */ + if (win->ws->r) { + if (FLOATING(win)) + load_float_geom(win); + 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); } } @@ -5383,6 +5652,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) { @@ -5490,6 +5783,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) { @@ -5526,7 +5844,7 @@ iconify(struct binding *bp, 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(); } @@ -5753,7 +6071,7 @@ search_win(struct binding *bp, struct swm_region *r, union arg *args) height = bar_font->height + 4; } - xcb_create_window(conn, screen->root_depth, 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, screen->root_visual, XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_COLORMAP, wa); @@ -5821,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; } @@ -5872,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."); @@ -6207,7 +6525,7 @@ maximize_toggle(struct binding *bp, 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); @@ -6240,7 +6558,7 @@ floating_toggle(struct binding *bp, 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); @@ -6250,6 +6568,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) { @@ -6263,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, @@ -6301,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) { @@ -6338,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) ? @@ -6351,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 @@ -6361,20 +6739,56 @@ 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 { @@ -6384,7 +6798,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; @@ -6394,7 +6808,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); @@ -6483,7 +6897,7 @@ resize_win(struct ws_win *win, struct binding *bp, int opt) ~EWMH_F_MAXIMIZED); ewmh_update_wm_state(win); - stack(); + stack(r); focus_flush(); @@ -6713,12 +7127,15 @@ 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, false); + /* Stack old and new region. */ + stack(r); + stack(win->ws->r); + /* Set focus on new ws. */ unfocus_win(r->ws->focus); r->ws->focus = win; @@ -6766,7 +7183,7 @@ move_win(struct ws_win *win, struct binding *bp, int opt) ewmh_update_wm_state(win); if (restack) - stack(); + stack(r); focus_flush(); @@ -6896,8 +7313,8 @@ move_win(struct ws_win *win, struct binding *bp, int opt) 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(); } @@ -6920,6 +7337,10 @@ move(struct binding *bp, struct swm_region *r, union arg *args) /* move_* uses focus window. */ if (r->ws) win = r->ws->focus; + + /* Disallow move_ on tiled. */ + if (win && !(TRANS(win) || ABOVE(win))) + return; } else { /* move uses window under pointer. */ qpr = xcb_query_pointer_reply(conn, @@ -6946,7 +7367,7 @@ struct action { union arg *); uint32_t flags; union arg args; -} actions[FN_INVALID + 1] = { +} 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} }, @@ -6959,6 +7380,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} }, @@ -6981,6 +7403,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} }, @@ -7005,6 +7429,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} }, @@ -7069,7 +7494,10 @@ struct action { { "ws_prev_all", cyclews, 0, {.id = SWM_ARG_ID_CYCLEWS_DOWN_ALL} }, { "ws_prev_move", cyclews, 0, {.id = SWM_ARG_ID_CYCLEWS_MOVE_DOWN} }, { "ws_prior", priorws, 0, {0} }, - { "dumpwins", dumpwins, 0, {0} }, /* MUST BE LAST */ + /* SWM_DEBUG actions MUST be here: */ + { "debug_toggle", debug_toggle, 0, {0} }, + { "dumpwins", dumpwins, 0, {0} }, + /* ALWAYS last: */ { "invalid action", NULL, 0, {0} }, }; @@ -7739,6 +8167,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); @@ -7784,6 +8213,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); @@ -7847,6 +8277,7 @@ setup_keybindings(void) BINDKEY(MODSHIFT, XK_Down, FN_WS_PREV_MOVE); BINDKEY(MODKEY, XK_a, FN_WS_PRIOR); #ifdef SWM_DEBUG + BINDKEY(MODKEY, XK_d, FN_DEBUG_TOGGLE); BINDKEY(MODSHIFT, XK_d, FN_DUMPWINS); #endif #undef BINDKEY @@ -9315,18 +9746,84 @@ get_ws_idx(struct ws_win *win) return ws_idx; } -struct ws_win * -manage_window(xcb_window_t id, int spawn_pos, bool 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, force_ws = -1; - 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 " @@ -9343,16 +9840,36 @@ manage_window(xcb_window_t id, int spawn_pos, bool 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. */ @@ -9368,24 +9885,19 @@ manage_window(xcb_window_t id, int spawn_pos, bool 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 = true; - 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, @@ -9411,7 +9923,8 @@ manage_window(xcb_window_t id, int spawn_pos, bool mapped) #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. */ @@ -9498,7 +10011,7 @@ manage_window(xcb_window_t id, int spawn_pos, bool mapped) update_window(win); } -out: +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); @@ -9541,11 +10054,14 @@ out: /* Set initial _NET_WM_ALLOWED_ACTIONS */ ewmh_update_actions(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); } @@ -9569,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; - 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); @@ -9592,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) { @@ -9686,14 +10225,14 @@ keyrelease(xcb_key_release_event_t *e) 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)); - keysym = xcb_key_release_lookup_keysym(syms, e, 0); - bp = binding_lookup(CLEANMASK(e->state), KEYBIND, keysym); if (bp == NULL) /* Look for catch-all. */ @@ -9705,6 +10244,7 @@ keyrelease(xcb_key_release_event_t *e) 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); @@ -9715,7 +10255,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; @@ -9741,11 +10281,15 @@ buttonpress(xcb_button_press_event_t *e) 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); /* No need to replay event. */ replay = false; @@ -9755,8 +10299,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); @@ -9790,7 +10341,7 @@ out: xcb_allow_events(conn, XCB_ALLOW_SYNC_POINTER, e->time); } - xcb_flush(conn); + focus_flush(); } void @@ -9824,8 +10375,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; /* @@ -9836,13 +10387,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 @@ -9872,7 +10422,7 @@ get_stack_mode_name(uint8_t mode) { char *name; - switch(mode) { + switch (mode) { case XCB_STACK_MODE_ABOVE: name = "Above"; break; @@ -10038,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); } } @@ -10053,43 +10600,57 @@ 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))) { - /* Managed window cleanup. */ - if (focus_mode != SWM_FOCUS_FOLLOW) { - /* If focused, focus on something else. */ - if (win == win->ws->focus) - win->ws->focus_pending = get_focus_prev(win); + if ((win = find_window(e->window)) == NULL) { + if ((win = find_unmanaged_window(e->window)) == NULL) + goto out; + /* Window is on unmanaged list. */ + TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry); + free_window(win); + 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 == ws->focus) { + ws->focus_pending = get_focus_prev(win); + if (ws->focus_pending == win) + ws->focus_pending = NULL; } + } - unmanage_window(win); - stack(); + unmanage_window(win); + 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); } - kill_refs(win); focus_flush(); - } else { - win = find_unmanaged_window(e->window); - } - - if (win) { - /* unmanage_window() puts win into unmanaged list. */ - TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry); - free_window(win); } +out: DNPRINTF(SWM_D_EVENT, "destroynotify: done.\n"); } @@ -10230,6 +10791,13 @@ enternotify(xcb_enter_notify_event_t *e) 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) { DNPRINTF(SWM_D_EVENT, "enternotify: manual focus; ignoring.\n"); @@ -10270,6 +10838,8 @@ enternotify(xcb_enter_notify_event_t *e) focus_win(get_focus_magic(win)); } + DNPRINTF(SWM_D_EVENT, "enternotify: done\n"); + xcb_flush(conn); } @@ -10299,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, spawn_position, true)) == 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)) @@ -10311,7 +10889,7 @@ 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 = true; @@ -10321,11 +10899,14 @@ mapnotify(xcb_map_notify_event_t *e) if (ws->focus_pending == win) { focus_win(win); ws->focus_pending = NULL; - center_pointer(win->ws->r); + center_pointer(ws->r); focus_flush(); } } +out: + DNPRINTF(SWM_D_EVENT, "mapnotify: done\n"); + xcb_flush(conn); } @@ -10344,36 +10925,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, - (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) { @@ -10397,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"); } @@ -10501,6 +11061,7 @@ propertynotify(xcb_property_notify_event_t *e) 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; @@ -10509,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) { @@ -10527,16 +11120,26 @@ 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 = 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) { if (focus_mode != SWM_FOCUS_FOLLOW) { @@ -10551,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)) { @@ -10569,13 +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 @@ -10608,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 @@ -10634,6 +11242,14 @@ clientmessage(xcb_client_message_event_t *e) 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; } @@ -10693,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]); @@ -10706,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], 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); + } } } @@ -10763,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; @@ -10811,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); } @@ -10858,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; @@ -10889,7 +11523,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; @@ -10912,6 +11545,7 @@ scan_randr(int idx) r->ws->old_r = r->ws->r = NULL; bar_cleanup(r); xcb_destroy_window(conn, r->id); + r->id = XCB_WINDOW_NONE; TAILQ_REMOVE(&screens[idx].rl, r, entry); TAILQ_INSERT_TAIL(&screens[idx].orl, r, entry); } @@ -10963,13 +11597,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; } @@ -10981,7 +11610,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); @@ -11000,46 +11631,49 @@ 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(); + /* 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; - uint8_t state; - bool manage, mapped; - - 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(); @@ -11073,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]) { @@ -11094,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], SWM_STACK_TOP, 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], SWM_STACK_TOP, mapped); - free(gar); + &trans, NULL)) + manage_window(wins[j], SWM_STACK_TOP, false); } free(qtr); } @@ -11234,6 +11828,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; @@ -11272,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"); @@ -11307,6 +11903,8 @@ shutdown_cleanup(void) 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); @@ -11366,6 +11964,7 @@ shutdown_cleanup(void) xcb_key_symbols_free(syms); xcb_flush(conn); + xcb_aux_sync(conn); xcb_disconnect(conn); } @@ -11405,8 +12004,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, );*/ @@ -11423,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, );*/ @@ -11574,16 +12173,21 @@ noconfig: grabkeys(); grabbuttons(); - stack(); - bar_draw(); + + /* 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); + } memset(&pfd, 0, sizeof(pfd)); pfd[0].fd = xfd; @@ -11636,7 +12240,11 @@ noconfig: 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: