X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=spectrwm.c;h=950c708c242aacfba7a30d5f6be446504f31ef57;hb=8b3d816a6450cec0fc166f5243d10ad8149cef52;hp=786deae95a627aeaedc3821e0ee0de63304b5e51;hpb=bedad617e4f77af0f54d47ca9dcafddb32b3cc35;p=spectrwm.git diff --git a/spectrwm.c b/spectrwm.c index 786deae..950c708 100644 --- a/spectrwm.c +++ b/spectrwm.c @@ -703,6 +703,7 @@ char *get_atom_name(xcb_atom_t); char *get_notify_detail_label(uint8_t); char *get_notify_mode_label(uint8_t); #endif +struct ws_win *get_pointer_win(xcb_window_t); struct ws_win *get_region_focus(struct swm_region *); xcb_screen_t *get_screen(int); char *get_win_name(xcb_window_t); @@ -2449,6 +2450,28 @@ restart(struct swm_region *r, union arg *args) quit(NULL, NULL); } +struct ws_win * +get_pointer_win(xcb_window_t root) +{ + struct ws_win *win = NULL; + xcb_query_pointer_reply_t *r; + + DNPRINTF(SWM_D_EVENT, "get_pointer_win: root: 0x%x.\n", root); + + r = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, root), NULL); + if (r) { + win = find_window(r->child); + if (win) { + DNPRINTF(SWM_D_EVENT, "get_pointer_win: 0x%x.\n", + win->id); + } else { + DNPRINTF(SWM_D_EVENT, "get_pointer_win: none.\n"); + } + } + + return win; +} + struct swm_region * root_to_region(xcb_window_t root, int check) { @@ -2731,7 +2754,8 @@ unfocus_win(struct ws_win *win) void focus_win(struct ws_win *win) { - struct ws_win *cfw = NULL; + struct ws_win *cfw = NULL, *parent = NULL, *w; + struct workspace *ws; xcb_get_input_focus_reply_t *r; DNPRINTF(SWM_D_FOCUS, "focus_win: window: 0x%x\n", WINID(win)); @@ -2742,7 +2766,9 @@ focus_win(struct ws_win *win) if (win->ws == NULL) goto out; - if (validate_ws(win->ws)) + ws = win->ws; + + if (validate_ws(ws)) goto out; if (validate_win(win)) { @@ -2758,13 +2784,24 @@ focus_win(struct ws_win *win) free(r); } - if (win->ws->focus != win) { - if (win->ws->focus && win->ws->focus != cfw) - unfocus_win(win->ws->focus); - win->ws->focus = win; + if (ws->focus != win) { + if (ws->focus && ws->focus != cfw) + unfocus_win(ws->focus); + ws->focus = win; } - if (cfw != win && win->ws->r != NULL) { + /* If this window directs focus to a child window, then clear. */ + if (win->focus_child) + win->focus_child = NULL; + + /* If transient, adjust parent's focus child for focus_magic. */ + if (win->transient) { + parent = find_window(win->transient); + if (parent && parent->focus_child != win) + parent->focus_child = win; + } + + if (cfw != win && ws->r != NULL) { /* Set input focus if no input hint, or indicated by hint. */ if (!(win->hints.flags & XCB_ICCCM_WM_HINT_INPUT) || (win->hints.flags & XCB_ICCCM_WM_HINT_INPUT && @@ -2776,19 +2813,38 @@ focus_win(struct ws_win *win) if (win->take_focus) { /* java is special; always tell parent */ if (win->transient && win->java) - client_msg(find_window(win->transient), - a_takefocus, last_event_time); + client_msg(parent, a_takefocus, + last_event_time); else client_msg(win, a_takefocus, last_event_time); } xcb_change_window_attributes(conn, win->id, XCB_CW_BORDER_PIXEL, - &win->ws->r->s->c[SWM_S_COLOR_FOCUS].pixel); + &ws->r->s->c[SWM_S_COLOR_FOCUS].pixel); - if (win->ws->cur_layout->flags & SWM_L_MAPONFOCUS || - win->ws->always_raise) + if (ws->cur_layout->flags & SWM_L_MAPONFOCUS || + ws->always_raise) { + /* If a parent exists, map it first. */ + if (parent) { + map_window(parent); + + /* Map siblings next. */ + TAILQ_FOREACH(w, &ws->winlist, entry) + if (w != win && !w->iconic && + w->transient == parent->id) + map_window(w); + } + + /* Map focused window. */ map_window(win); + /* Finally, map children of focus window. */ + TAILQ_FOREACH(w, &ws->winlist, entry) + if (w->transient == win->id && !w->iconic) + map_window(w); + + } + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->s->root, ewmh[_NET_ACTIVE_WINDOW].atom, XCB_ATOM_WINDOW, 32, 1, &win->id); @@ -3321,7 +3377,7 @@ focus(struct swm_region *r, union arg *args) focus_win(get_focus_magic(winfocus)); - xcb_flush(conn); + focus_flush(); } void @@ -3741,6 +3797,7 @@ notiles: map_window(win); } + /* Make sure fs_win is stacked last so it's on top. */ if (fs_win) { stack_floater(fs_win, ws->r); map_window(fs_win); @@ -3853,7 +3910,7 @@ void max_stack(struct workspace *ws, struct swm_geometry *g) { struct swm_geometry gg = *g; - struct ws_win *win, *wintrans = NULL, *parent = NULL; + struct ws_win *w, *win = NULL, *parent = NULL; int winno, num_screens; DNPRINTF(SWM_D_STACK, "max_stack: workspace: %d\n", ws->idx); @@ -3865,50 +3922,77 @@ max_stack(struct workspace *ws, struct swm_geometry *g) if (winno == 0 && count_win(ws, 1) == 0) return; + /* Figure out which top level window should be visible. */ + if (ws->focus_pending) + win = ws->focus_pending; + else if (ws->focus) + win = ws->focus; + else + win = TAILQ_FIRST(&ws->winlist); + + if (win->transient) + parent = find_window(win->transient); + + DNPRINTF(SWM_D_STACK, "max_stack: win: 0x%x\n", win->id); + + /* maximize all top level windows */ num_screens = xcb_setup_roots_length(xcb_get_setup(conn)); - TAILQ_FOREACH(win, &ws->winlist, entry) { - if (win->transient) { - wintrans = win; - parent = find_window(win->transient); + TAILQ_FOREACH(w, &ws->winlist, entry) { + if (w->transient || w->iconic) continue; - } - if (win->floating && !win->floatmaxed ) { + if (w->floating && !w->floatmaxed) { /* * retain geometry for retrieval on exit from * max_stack mode */ - store_float_geom(win, ws->r); - win->floatmaxed = 1; + store_float_geom(w, ws->r); + w->floatmaxed = 1; } /* only reconfigure if necessary */ - if (X(win) != gg.x || Y(win) != gg.y || WIDTH(win) != gg.w || - HEIGHT(win) != gg.h) { - win->g = gg; + if (X(w) != gg.x || Y(w) != gg.y || WIDTH(w) != gg.w || + HEIGHT(w) != gg.h) { + w->g = gg; if (bar_enabled){ - win->bordered = 1; + w->bordered = 1; } else { - win->bordered = 0; - WIDTH(win) += 2 * border_width; - HEIGHT(win) += 2 * border_width; + w->bordered = 0; + WIDTH(w) += 2 * border_width; + HEIGHT(w) += 2 * border_width; } - update_window(win); + update_window(w); } - /* unmap only if we don't have multi screen */ - if (win != ws->focus) - if (!(num_screens > 1 || outputs > 1)) - unmap_window(win); + + /* Unmap unwanted windows if not multi-screen. */ + if (!(num_screens > 1 || outputs > 1) && (w != win || + w != parent || w->transient != win->id)) + unmap_window(w); } - /* put the last transient on top */ - if (wintrans) { - if (parent) - map_window(parent); - stack_floater(wintrans, ws->r); - ws->focus = get_focus_magic(wintrans); + /* If a parent exists, map it first. */ + if (parent) { + map_window(parent); + + /* Map siblings next. */ + TAILQ_FOREACH(w, &ws->winlist, entry) + if (w != win && !w->iconic && + w->transient == parent->id) { + stack_floater(w, ws->r); + map_window(w); + } } + + /* Map focused window. */ + map_window(win); + + /* Finally, map children of focus window. */ + TAILQ_FOREACH(w, &ws->winlist, entry) + if (w->transient == win->id && !w->iconic) { + stack_floater(w, ws->r); + map_window(w); + } } void @@ -4334,7 +4418,6 @@ search_resp_uniconify(char *resp, unsigned long len) if (strncmp(s, resp, len) == 0) { /* XXX this should be a callback to generalize */ set_swm_iconic(win, 0); - xcb_flush(conn); free(s); break; } @@ -4823,6 +4906,7 @@ resize_step(struct swm_region *r, union arg *args) return; resize(win, args); + focus_flush(); } #define SWM_MOVE_STEPS (50) @@ -4969,6 +5053,7 @@ move_step(struct swm_region *r, union arg *args) return; move(win, args); + focus_flush(); } /* user/key callable function IDs */ @@ -5937,11 +6022,6 @@ grabbuttons(struct ws_win *win) XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, XCB_WINDOW_NONE, XCB_CURSOR_NONE, buttons[i].button, buttons[i].mask); - - /* click to focus */ - xcb_grab_button(conn, 0, win->id, BUTTONMASK, XCB_GRAB_MODE_SYNC, - XCB_GRAB_MODE_ASYNC, XCB_WINDOW_NONE, XCB_CURSOR_NONE, - XCB_BUTTON_INDEX_1, XCB_BUTTON_MASK_ANY); } const char *quirkname[] = { @@ -7139,14 +7219,47 @@ keypress(xcb_key_press_event_t *e) void buttonpress(xcb_button_press_event_t *e) { - struct ws_win *win; + struct ws_win *win = NULL; + struct swm_region *r, *old_r; int i; int handled = 0; - DNPRINTF(SWM_D_EVENT, "buttonpress: window 0x%x, detail: %u\n", - e->event, e->detail); + DNPRINTF(SWM_D_EVENT, "buttonpress: win (x,y): 0x%x (%d,%d), " + "detail: %u, time: %u, root (x,y): 0x%x (%d,%d), child: 0x%x, " + "state: %u, same_screen: %s\n", e->event, e->event_x, e->event_y, + e->detail, e->time, e->root, e->root_x, e->root_y, e->child, + e->state, YESNO(e->same_screen)); + + if (e->event == e->root) { + if (e->child != 0) { + win = find_window(e->child); + /* Pass ButtonPress to window if it isn't managed. */ + if (win == NULL) + goto out; + } else { + /* Focus on empty region */ + /* If no windows on region if its empty. */ + r = root_to_region(e->root, SWM_CK_POINTER); + if (r && TAILQ_EMPTY(&r->ws->winlist)) { + old_r = root_to_region(e->root, SWM_CK_FOCUS); + if (old_r && old_r != r) + unfocus_win(old_r->ws->focus); + + xcb_set_input_focus(conn, + XCB_INPUT_FOCUS_PARENT, e->root, e->time); - if ((win = find_window(e->event)) == NULL) + /* Clear bar since empty. */ + bar_update(); + + handled = 1; + goto out; + } + } + } else { + win = find_window(e->event); + } + + if (win == NULL) return; last_event_time = e->time; @@ -7161,11 +7274,15 @@ buttonpress(xcb_button_press_event_t *e) handled = 1; } +out: if (!handled) { DNPRINTF(SWM_D_EVENT, "buttonpress: passing to window.\n"); + /* Replay event to event window */ xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, e->time); } else { DNPRINTF(SWM_D_EVENT, "buttonpress: handled.\n"); + /* Unfreeze grab events. */ + xcb_allow_events(conn, XCB_ALLOW_SYNC_POINTER, e->time); } xcb_flush(conn); @@ -7293,8 +7410,10 @@ configurerequest(xcb_configure_request_event_t *e) wc[i++] = e->stack_mode; } - if (mask != 0) + if (mask != 0) { xcb_configure_window(conn, e->window, mask, wc); + xcb_flush(conn); + } } else if ((!win->manual || win->quirks & SWM_Q_ANYWHERE) && !(win->ewmh_flags & EWMH_F_FULLSCREEN)) { if (win->ws->r) @@ -7325,7 +7444,8 @@ configurerequest(xcb_configure_request_event_t *e) win->g_floatvalid = 1; - if (win->floating && r) { + if (win->floating && r && (win->transient || + win->ws->cur_layout != &layouts[SWM_MAX_STACK])) { WIDTH(win) = win->g_float.w; HEIGHT(win) = win->g_float.h; @@ -7340,6 +7460,8 @@ configurerequest(xcb_configure_request_event_t *e) config_win(win, e); xcb_flush(conn); } + + DNPRINTF(SWM_D_EVENT, "configurerequest: done.\n"); } void @@ -7552,6 +7674,7 @@ mapnotify(xcb_map_notify_event_t *e) if (win->ws->focus_pending == win) { focus_win(win); win->ws->focus_pending = NULL; + focus_flush(); } } @@ -7593,14 +7716,14 @@ maprequest(xcb_map_request_event_t *e) win = manage_window(e->window, (war->map_state == XCB_MAP_STATE_VIEWABLE)); + /* The new window should get focus; prepare. */ + if (focus_mode != SWM_FOCUS_FOLLOW) + win->ws->focus_pending = get_focus_magic(win); + /* All windows need to be mapped if they are in the current workspace.*/ if (win->ws->r) stack(); - /* The new window should get focus. */ - if (focus_mode != SWM_FOCUS_FOLLOW) - win->ws->focus_pending = get_focus_magic(win); - /* Ignore EnterNotify to handle the mapnotify without interference. */ if (focus_mode == SWM_FOCUS_DEFAULT) event_drain(XCB_ENTER_NOTIFY); @@ -7652,7 +7775,8 @@ propertynotify(xcb_property_notify_event_t *e) name = get_atom_name(e->atom); DNPRINTF(SWM_D_EVENT, "propertynotify: window: 0x%x, atom: %s(%u), " - "time: %#x\n", e->window, name, e->atom, e->time); + "time: %#x, state: %u\n", e->window, name, e->atom, e->time, + e->state); free(name); #endif win = find_window(e->window); @@ -7714,38 +7838,53 @@ unmapnotify(xcb_unmap_notify_event_t *e) DNPRINTF(SWM_D_EVENT, "unmapnotify: window: 0x%x\n", e->window); - /* determine if we need to help unmanage this window */ + /* If we aren't managing the window, then ignore. */ win = find_window(e->window); if (win == NULL) return; ws = win->ws; - if (getstate(e->window) == XCB_ICCCM_WM_STATE_NORMAL) { - win->mapped = 0; + if (getstate(e->window) != XCB_ICCCM_WM_STATE_ICONIC) set_win_state(win, XCB_ICCCM_WM_STATE_ICONIC); - /* If we were focused, make sure we focus on something else. */ - if (win == ws->focus) - if (focus_mode != SWM_FOCUS_FOLLOW) + if (win->mapped) { + /* window unmapped itself */ + /* do unmap/unfocus/restack and unmanage */ + win->mapped = 0; + + /* If win was focused, make sure to focus on something else. */ + if (win == ws->focus) { + if (focus_mode != SWM_FOCUS_FOLLOW) { ws->focus_pending = get_focus_prev(win); + DNPRINTF(SWM_D_EVENT, "unmapnotify: " + "focus_pending: 0x%x\n", + WINID(ws->focus_pending)); + } + + unfocus_win(win); + } - unfocus_win(win); unmanage_window(win); - stack(); - DNPRINTF(SWM_D_EVENT, "unmapnotify: focus_pending: 0x%x\n", - ws->focus_pending->id); + if (ws->r) + stack(); - if (focus_mode != SWM_FOCUS_FOLLOW) { + if (focus_mode == SWM_FOCUS_FOLLOW) { + if (ws->r) + focus_win(get_pointer_win(ws->r->s->root)); + } else { if (ws->focus_pending) { focus_win(ws->focus_pending); ws->focus_pending = NULL; } } - - focus_flush(); } + + if (getstate(e->window) == XCB_ICCCM_WM_STATE_NORMAL) + set_win_state(win, XCB_ICCCM_WM_STATE_ICONIC); + + focus_flush(); } #if 0 @@ -7774,6 +7913,7 @@ clientmessage(xcb_client_message_event_t *e) if (win == NULL) { if (e->type == ewmh[_NET_ACTIVE_WINDOW].atom) { + /* Manage the window with maprequest. */ DNPRINTF(SWM_D_EVENT, "clientmessage: request focus on " "unmanaged window.\n"); mre.window = e->window; @@ -7824,7 +7964,7 @@ clientmessage(xcb_client_message_event_t *e) stack(); } - xcb_flush(conn); + focus_flush(); } void @@ -7885,6 +8025,11 @@ enable_wm(void) free(error); return 1; } + + /* click to focus on empty region */ + xcb_grab_button(conn, 1, sc->root, BUTTONMASK, + XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, XCB_WINDOW_NONE, + XCB_CURSOR_NONE, XCB_BUTTON_INDEX_1, XCB_BUTTON_MASK_ANY); } return 0; @@ -8074,7 +8219,7 @@ screenchange(xcb_randr_screen_change_notify_event_t *e) void grab_windows(void) { - xcb_window_t *wins = NULL; + xcb_window_t *wins = NULL, trans; int no; int i, j, num_screens; uint16_t state, manage, mapped; @@ -8096,48 +8241,62 @@ grab_windows(void) no = xcb_query_tree_children_length(qtr); /* attach windows to a region */ /* normal windows */ + DNPRINTF(SWM_D_INIT, "grab_windows: grab top level windows.\n"); for (j = 0; j < no; j++) { c = xcb_get_window_attributes(conn, wins[j]); r = xcb_get_window_attributes_reply(conn, c, NULL); - if (!r) + if (!r) { + DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; " + "doesn't exist.\n", wins[j]); continue; + } if (r->override_redirect) { + DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; " + "override_redirect set.\n", wins[j]); free(r); continue; } pc = xcb_icccm_get_wm_transient_for(conn, wins[j]); if (xcb_icccm_get_wm_transient_for_reply(conn, pc, - &wins[j], NULL)) { + &trans, NULL)) { + DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; " + "is transient for %#x.\n", wins[j], trans); free(r); continue; } state = getstate(wins[j]); - manage = state == XCB_ICCCM_WM_STATE_ICONIC; + manage = state != XCB_ICCCM_WM_STATE_WITHDRAWN; mapped = r->map_state != XCB_MAP_STATE_UNMAPPED; if (mapped || manage) manage_window(wins[j], mapped); free(r); } /* transient windows */ + DNPRINTF(SWM_D_INIT, "grab_windows: grab transient windows.\n"); for (j = 0; j < no; j++) { c = xcb_get_window_attributes(conn, wins[j]); r = xcb_get_window_attributes_reply(conn, c, NULL); - if (!r) + if (!r) { + DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; " + "doesn't exist.\n", wins[j]); continue; + } if (r->override_redirect) { + DNPRINTF(SWM_D_INIT, "grab_windows: skip %#x; " + "override_redirect set.\n", wins[j]); free(r); continue; } free(r); state = getstate(wins[j]); - manage = state == XCB_ICCCM_WM_STATE_ICONIC; + manage = state != XCB_ICCCM_WM_STATE_WITHDRAWN; mapped = r->map_state != XCB_MAP_STATE_UNMAPPED; pc = xcb_icccm_get_wm_transient_for(conn, wins[j]); if (xcb_icccm_get_wm_transient_for_reply(conn, pc, - &wins[j], NULL) && manage) + &trans, NULL) && manage) manage_window(wins[j], mapped); } free(qtr); @@ -8556,11 +8715,6 @@ noconfig: winfocus = NULL; continue; } - /* move pointer to first screen if multi screen */ - if (num_screens > 1 || outputs > 1) - xcb_warp_pointer(conn, XCB_WINDOW_NONE, - rr->s[0].root, 0, 0, 0, 0, X(rr), - Y(rr) + (bar_enabled ? bar_height : 0)); focus_win(get_region_focus(rr)); focus_flush();