X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=spectrwm.c;h=3719d0d965a27ca9364eafe7074e4997f94e43d2;hb=332906d454ef06919ddcdb82e8b67e11050897c4;hp=342dc69c0438a6d61695e1bea98d1ea6d65878fb;hpb=09e555187ca667c59401415f492d0482056026e1;p=spectrwm.git diff --git a/spectrwm.c b/spectrwm.c index 342dc69..3719d0d 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-2013 Reginald Kennedy + * Copyright (c) 2011-2014 Reginald Kennedy * Copyright (c) 2011-2012 Lawrence Teo * Copyright (c) 2011-2012 Tiago Cunha * Copyright (c) 2012-2013 David Hill @@ -186,7 +186,7 @@ static const char *buildstr = SPECTRWM_VERSION; #define SWM_D_BAR 0x4000 #define SWM_D_INIT 0x8000 -u_int32_t swm_debug = 0 +uint32_t swm_debug = 0 | SWM_D_MISC | SWM_D_EVENT | SWM_D_WS @@ -425,6 +425,7 @@ XftFont *bar_font; int bar_font_legacy = 1; char *bar_fonts; XftColor bar_font_color; +XftColor search_font_color; struct passwd *pwd; char *startup_exception; unsigned int nr_exceptions = 0; @@ -467,7 +468,7 @@ struct ws_win { struct swm_geometry g; /* current geometry */ struct swm_geometry g_float; /* region coordinates */ int g_floatvalid; /* g_float geometry validity */ - int32_t mapped; + int mapped; int bordered; uint32_t ewmh_flags; int font_size_boundary[SWM_MAX_FONT_STEPS]; @@ -508,7 +509,7 @@ void fancy_stacker(struct workspace *); struct layout { void (*l_stack)(struct workspace *, struct swm_geometry *); void (*l_config)(struct workspace *, int); - u_int32_t flags; + uint32_t flags; #define SWM_L_FOCUSPREV (1<<0) #define SWM_L_MAPONFOCUS (1<<1) void (*l_string)(struct workspace *); @@ -544,7 +545,7 @@ struct workspace { struct ws_win_list winlist; /* list of windows in ws */ struct ws_win_list unmanagedlist; /* list of dead windows in ws */ struct ws_win_stack stack; /* stacking order */ - int32_t state; /* mapping state */ + int state; /* mapping state */ char stacker[10]; /* display stacker and layout */ /* stacker state */ @@ -572,7 +573,9 @@ enum { SWM_S_COLOR_BAR_BORDER_UNFOCUS, SWM_S_COLOR_BAR_FONT, SWM_S_COLOR_FOCUS, + SWM_S_COLOR_FOCUS_MAXIMIZED, SWM_S_COLOR_UNFOCUS, + SWM_S_COLOR_UNFOCUS_MAXIMIZED, SWM_S_COLOR_MAX }; @@ -592,6 +595,7 @@ struct swm_screen { struct { uint32_t pixel; char *name; + int manual; } c[SWM_S_COLOR_MAX]; xcb_gcontext_t bar_gc; @@ -641,6 +645,8 @@ 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) char **argv; @@ -664,6 +670,7 @@ struct quirk { #define SWM_Q_FOCUSPREV (1<<5) /* focus on caller */ #define SWM_Q_NOFOCUSONMAP (1<<6) /* Don't focus on window when mapped. */ #define SWM_Q_FOCUSONMAP_SINGLE (1<<7) /* Only focus if single win of type. */ +#define SWM_Q_OBEYAPPFOCUSREQ (1<<8) /* Focus when applications ask. */ }; TAILQ_HEAD(quirk_list, quirk); struct quirk_list quirks = TAILQ_HEAD_INITIALIZER(quirks); @@ -1023,13 +1030,14 @@ int get_region_index(struct swm_region *); xcb_screen_t *get_screen(int); int get_screen_count(void); #ifdef SWM_DEBUG +char *get_source_type_label(uint32_t); char *get_stack_mode_name(uint8_t); #endif int32_t get_swm_ws(xcb_window_t); char *get_win_name(xcb_window_t); +uint8_t get_win_state(xcb_window_t); void get_wm_protocols(struct ws_win *); int get_ws_idx(xcb_window_t); -uint32_t getstate(xcb_window_t); void grabbuttons(struct ws_win *); void grabkeys(void); void grab_windows(void); @@ -1048,7 +1056,7 @@ void kill_refs(struct ws_win *); void leavenotify(xcb_leave_notify_event_t *); #endif void load_float_geom(struct ws_win *); -struct ws_win *manage_window(xcb_window_t, uint16_t); +struct ws_win *manage_window(xcb_window_t, int); void map_window(struct ws_win *); void mapnotify(xcb_map_notify_event_t *); void mappingnotify(xcb_mapping_notify_event_t *); @@ -1118,7 +1126,7 @@ void setup_quirks(void); void setup_screens(void); void setup_spawn(void); void set_child_transient(struct ws_win *, xcb_window_t *); -void set_win_state(struct ws_win *, uint16_t); +void set_win_state(struct ws_win *, uint8_t); void shutdown_cleanup(void); void sighdlr(int); void socket_setnonblock(int); @@ -1150,6 +1158,7 @@ void update_floater(struct ws_win *); void update_modkey(unsigned int); void update_win_stacking(struct ws_win *); void update_window(struct ws_win *); +void update_window_color(struct ws_win *); void update_wm_state(struct ws_win *win); void validate_spawns(void); int validate_win(struct ws_win *); @@ -1449,10 +1458,10 @@ teardown_ewmh(void) void ewmh_autoquirk(struct ws_win *win) { - uint32_t i, n; - xcb_atom_t *type; - xcb_get_property_cookie_t c; xcb_get_property_reply_t *r; + xcb_get_property_cookie_t c; + xcb_atom_t *type; + int i, n; c = xcb_get_property(conn, 0, win->id, ewmh[_NET_WM_WINDOW_TYPE].atom, XCB_ATOM_ATOM, 0, UINT32_MAX); @@ -1630,6 +1639,8 @@ ewmh_apply_flags(struct ws_win *win, uint32_t pending) ws->focus_pending = win; } } + + update_window_color(win); raise_window(win); } @@ -1714,7 +1725,7 @@ ewmh_get_wm_state(struct ws_win *win) void dumpwins(struct swm_region *r, union arg *args) { - struct ws_win *win; + struct ws_win *w; uint32_t state; xcb_get_window_attributes_cookie_t c; xcb_get_window_attributes_reply_t *wa; @@ -1728,33 +1739,40 @@ dumpwins(struct swm_region *r, union arg *args) } DPRINTF("=== managed window list ws %02d ===\n", r->ws->idx); - TAILQ_FOREACH(win, &r->ws->winlist, entry) { - state = getstate(win->id); - c = xcb_get_window_attributes(conn, win->id); + TAILQ_FOREACH(w, &r->ws->winlist, entry) { + state = get_win_state(w->id); + 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", win->id, wa->map_state, - state, win->transient); + "transient: %#x\n", w->id, wa->map_state, + state, w->transient); free(wa); } else DPRINTF("win %#x, failed xcb_get_window_attributes\n", - win->id); + w->id); + } + + 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)), + YESNO(MAXIMIZED(w)), YESNO(ABOVE(w)), YESNO(ICONIC(w))); } DPRINTF("===== unmanaged window list =====\n"); - TAILQ_FOREACH(win, &r->ws->unmanagedlist, entry) { - state = getstate(win->id); - c = xcb_get_window_attributes(conn, win->id); + TAILQ_FOREACH(w, &r->ws->unmanagedlist, entry) { + state = get_win_state(w->id); + 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", win->id, wa->map_state, - state, win->transient); + "transient: %#x\n", w->id, wa->map_state, + state, w->transient); free(wa); } else DPRINTF("win %#x, failed xcb_get_window_attributes\n", - win->id); + w->id); } DPRINTF("=================================\n"); @@ -1885,24 +1903,13 @@ name_to_pixel(int sidx, const char *colorname) void setscreencolor(const char *val, int i, int c) { - int num_screens; + if (i < 0 || i >= get_screen_count()) + return; - num_screens = get_screen_count(); - if (i > 0 && i <= num_screens) { - screens[i - 1].c[c].pixel = name_to_pixel(i - 1, val); - free(screens[i - 1].c[c].name); - if ((screens[i - 1].c[c].name = strdup(val)) == NULL) - err(1, "strdup"); - } else if (i == -1) { - for (i = 0; i < num_screens; i++) { - screens[i].c[c].pixel = name_to_pixel(0, val); - free(screens[i].c[c].name); - if ((screens[i].c[c].name = strdup(val)) == NULL) - err(1, "strdup"); - } - } else - errx(1, "invalid screen index: %d out of bounds (maximum %d)", - i, num_screens); + screens[i].c[c].pixel = name_to_pixel(i, val); + free(screens[i].c[c].name); + if ((screens[i].c[c].name = strdup(val)) == NULL) + err(1, "strdup"); } void @@ -2709,6 +2716,12 @@ xft_init(struct swm_region *r) DefaultColormap(display, r->s->idx), &color, &bar_font_color)) warn("Xft error: unable to allocate color."); + PIXEL_TO_XRENDERCOLOR(r->s->c[SWM_S_COLOR_BAR].pixel, color); + + if (!XftColorAllocValue(display, DefaultVisual(display, r->s->idx), + DefaultColormap(display, r->s->idx), &color, &search_font_color)) + warn("Xft error: unable to allocate color."); + bar_height = bar_font->height + 2 * bar_border_width; if (bar_height < 1) @@ -2756,6 +2769,13 @@ bar_setup(struct swm_region *r) XCB_COPY_FROM_PARENT, XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK, wa); + /* Stack bar window above region window to start. */ + wa[0] = r->id; + wa[1] = XCB_STACK_MODE_ABOVE; + + xcb_configure_window(conn, r->bar->id, XCB_CONFIG_WINDOW_SIBLING | + XCB_CONFIG_WINDOW_STACK_MODE, wa); + r->bar->buffer = xcb_generate_id(conn); xcb_create_pixmap(conn, screen->root_depth, r->bar->buffer, r->bar->id, WIDTH(r->bar), HEIGHT(r->bar)); @@ -2786,7 +2806,7 @@ bar_cleanup(struct swm_region *r) } void -set_win_state(struct ws_win *win, uint16_t state) +set_win_state(struct ws_win *win, uint8_t state) { uint16_t data[2] = { state, XCB_ATOM_NONE }; @@ -2800,12 +2820,12 @@ set_win_state(struct ws_win *win, uint16_t state) a_state, 32, 2, data); } -uint32_t -getstate(xcb_window_t w) +uint8_t +get_win_state(xcb_window_t w) { - uint32_t result = 0; - xcb_get_property_cookie_t c; xcb_get_property_reply_t *r; + xcb_get_property_cookie_t c; + uint32_t result = 0; c = xcb_get_property(conn, 0, w, a_state, a_state, 0L, 2L); r = xcb_get_property_reply(conn, c, NULL); @@ -2815,7 +2835,7 @@ getstate(xcb_window_t w) free(r); } - DNPRINTF(SWM_D_MISC, "getstate property: win %#x state %u\n", w, + DNPRINTF(SWM_D_MISC, "get_win_state property: win %#x state %u\n", w, result); return (result); } @@ -2996,6 +3016,9 @@ raise_window(struct ws_win *win) continue; if (ws->cur_layout == &layouts[SWM_MAX_STACK]) break; + if (TRANS(win) && (win->transient == target->transient || + win->transient == target->id)) + break; if (FULLSCREEN(win)) break; if (FULLSCREEN(target)) @@ -3017,6 +3040,17 @@ raise_window(struct ws_win *win) update_win_stacking(win); } +#ifdef SWM_DEBUG + if (swm_debug & SWM_D_STACK) { + DPRINTF("=== stacking order (top down) === \n"); + TAILQ_FOREACH(target, &r->ws->stack, stack_entry) { + DPRINTF("win %#x, fs: %s, maximized: %s, above: %s, " + "iconic: %s\n", target->id, YESNO(FULLSCREEN(target)), + YESNO(MAXIMIZED(target)), YESNO(ABOVE(target)), + YESNO(ICONIC(target))); + } + } +#endif DNPRINTF(SWM_D_EVENT, "raise_window: done\n"); } @@ -3445,8 +3479,7 @@ unfocus_win(struct ws_win *win) win->ws->focus_prev = NULL; } - xcb_change_window_attributes(conn, win->id, XCB_CW_BORDER_PIXEL, - &win->ws->r->s->c[SWM_S_COLOR_UNFOCUS].pixel); + update_window_color(win); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->s->root, ewmh[_NET_ACTIVE_WINDOW].atom, XCB_ATOM_WINDOW, 32, 1, &none); @@ -3463,13 +3496,7 @@ focus_win(struct ws_win *win) DNPRINTF(SWM_D_FOCUS, "focus_win: win %#x\n", WINID(win)); - if (win == NULL) - goto out; - - if (win->ws == NULL) - goto out; - - if (!win->mapped) + if (win == NULL || win->ws == NULL || !win->mapped) goto out; ws = win->ws; @@ -3490,7 +3517,9 @@ focus_win(struct ws_win *win) /* Change border to unfocused color. */ xcb_change_window_attributes(conn, cfw->id, XCB_CW_BORDER_PIXEL, - &cfw->ws->r->s->c[SWM_S_COLOR_UNFOCUS].pixel); + &cfw->s->c[(MAXIMIZED(cfw) ? + SWM_S_COLOR_UNFOCUS_MAXIMIZED : + SWM_S_COLOR_UNFOCUS)].pixel); } else { unfocus_win(cfw); } @@ -3536,9 +3565,6 @@ focus_win(struct ws_win *win) client_msg(win, a_takefocus, last_event_time); } - xcb_change_window_attributes(conn, win->id, XCB_CW_BORDER_PIXEL, - &ws->r->s->c[SWM_S_COLOR_FOCUS].pixel); - if (ws->cur_layout->flags & SWM_L_MAPONFOCUS || ws->always_raise) { /* If a parent exists, map it first. */ @@ -3577,6 +3603,10 @@ focus_win(struct ws_win *win) &win->id); } + if (cfw != win) + /* Update window border even if workspace is hidden. */ + update_window_color(win); + out: bar_draw(); @@ -3727,8 +3757,7 @@ switchws(struct swm_region *r, union arg *args) return; if ((win = old_ws->focus) != NULL) { - xcb_change_window_attributes(conn, win->id, XCB_CW_BORDER_PIXEL, - &win->ws->r->s->c[SWM_S_COLOR_UNFOCUS].pixel); + update_window_color(win); xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->s->root, ewmh[_NET_ACTIVE_WINDOW].atom, XCB_ATOM_WINDOW, 32, 1, @@ -3797,7 +3826,7 @@ cyclews(struct swm_region *r, union arg *args) union arg a; struct swm_screen *s = r->s; int cycle_all = 0; - int move = 0; + int mv = 0; DNPRINTF(SWM_D_WS, "cyclews: id: %d, screen[%d]:%dx%d+%d+%d, ws: %d\n", args->id, r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx); @@ -3807,7 +3836,7 @@ cyclews(struct swm_region *r, union arg *args) do { switch (args->id) { case SWM_ARG_ID_CYCLEWS_MOVE_UP: - move = 1; + mv = 1; /* FALLTHROUGH */ case SWM_ARG_ID_CYCLEWS_UP_ALL: cycle_all = 1; @@ -3816,7 +3845,7 @@ cyclews(struct swm_region *r, union arg *args) a.id = (a.id < workspace_limit - 1) ? a.id + 1 : 0; break; case SWM_ARG_ID_CYCLEWS_MOVE_DOWN: - move = 1; + mv = 1; /* FALLTHROUGH */ case SWM_ARG_ID_CYCLEWS_DOWN_ALL: cycle_all = 1; @@ -3834,7 +3863,7 @@ cyclews(struct swm_region *r, union arg *args) if (!cycle_visible && s->ws[a.id].r != NULL) continue; - if (move) + if (mv) send_to_ws(r, &a); switchws(r, &a); @@ -3956,9 +3985,39 @@ swapwin(struct swm_region *r, union arg *args) args->id, r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx); cur_focus = r->ws->focus; - if (cur_focus == NULL || ABOVE(cur_focus) || FULLSCREEN(cur_focus)) + if (cur_focus == NULL || FULLSCREEN(cur_focus)) return; + /* Adjust stacking in floating layer. */ + if (ABOVE(cur_focus)) { + switch (args->id) { + case SWM_ARG_ID_SWAPPREV: + target = TAILQ_PREV(cur_focus, ws_win_stack, + stack_entry); + if (target != NULL && FLOATING(target)) { + TAILQ_REMOVE(&cur_focus->ws->stack, cur_focus, + stack_entry); + TAILQ_INSERT_BEFORE(target, cur_focus, + stack_entry); + update_win_stacking(cur_focus); + focus_flush(); + } + break; + case SWM_ARG_ID_SWAPNEXT: + target = TAILQ_NEXT(cur_focus, stack_entry); + if (target != NULL && FLOATING(target)) { + TAILQ_REMOVE(&cur_focus->ws->stack, cur_focus, + stack_entry); + TAILQ_INSERT_AFTER(&cur_focus->ws->stack, + target, cur_focus, stack_entry); + update_win_stacking(cur_focus); + focus_flush(); + } + break; + } + goto out; + } + if (r->ws->cur_layout == &layouts[SWM_MAX_STACK]) return; @@ -4018,12 +4077,12 @@ swapwin(struct swm_region *r, union arg *args) } sort_windows(wl); - ewmh_update_client_list(); stack(); - focus_flush(); +out: + DNPRINTF(SWM_D_MOVE, "swapwin: done\n"); } struct ws_win * @@ -4441,6 +4500,8 @@ update_floater(struct ws_win *win) DNPRINTF(SWM_D_MISC, "update_floater: win %#x\n", win->id); + win->bordered = 1; + if (FULLSCREEN(win)) { /* _NET_WM_FULLSCREEN: fullscreen without border. */ if (!win->g_floatvalid) @@ -4456,14 +4517,11 @@ update_floater(struct ws_win *win) win->g = r->g; if (bar_enabled && ws->bar_enabled) { - win->bordered = 1; if (!bar_at_bottom) Y(win) += bar_height; HEIGHT(win) -= bar_height; } else if (disable_border) { win->bordered = 0; - } else { - win->bordered = 1; } if (win->bordered) { @@ -4868,7 +4926,10 @@ max_stack(struct workspace *ws, struct swm_geometry *g) else win = TAILQ_FIRST(&ws->winlist); - DNPRINTF(SWM_D_STACK, "max_stack: win: %#x\n", win->id); + DNPRINTF(SWM_D_STACK, "max_stack: focus_pending: %#x, focus: %#x, " + "focus_prev: %#x, first: %#x, win: %#x\n", WINID(ws->focus_pending), + WINID(ws->focus), WINID(ws->focus_prev), + WINID(TAILQ_FIRST(&ws->winlist)), win->id); /* Update window geometry. */ TAILQ_FOREACH(w, &ws->winlist, entry) { @@ -4906,8 +4967,7 @@ max_stack(struct workspace *ws, struct swm_geometry *g) } } - if (TRANS(win)) { - parent = find_window(win->transient); + if (TRANS(win) && (parent = find_window(win->transient))) { raise_window(parent); TAILQ_FOREACH(w, &ws->stack, stack_entry) @@ -4970,6 +5030,7 @@ region_under(struct swm_screen *s, int x, int y) return (NULL); } +/* Transfer focused window to target workspace and focus. */ void send_to_ws(struct swm_region *r, union arg *args) { @@ -4983,7 +5044,7 @@ send_to_ws(struct swm_region *r, union arg *args) DNPRINTF(SWM_D_MOVE, "send_to_ws: win %#x, ws %d\n", win->id, wsid); - if (wsid >= workspace_limit) + if (wsid < 0 || wsid >= workspace_limit) return; if (win->ws->idx == wsid) @@ -4991,20 +5052,38 @@ send_to_ws(struct swm_region *r, union arg *args) win_to_ws(win, wsid, 1); + /* Set new focus on target ws. */ + if (focus_mode != SWM_FOCUS_FOLLOW) { + win->ws->focus_prev = win->ws->focus; + win->ws->focus = win; + win->ws->focus_pending = NULL; + + if (win->ws->focus_prev) + update_window_color(win->ws->focus_prev); + } + + DNPRINTF(SWM_D_STACK, "send_to_ws: focus_pending: %#x, focus: %#x, " + "focus_prev: %#x, first: %#x, win: %#x\n", + WINID(r->ws->focus_pending), WINID(r->ws->focus), + WINID(r->ws->focus_prev), WINID(TAILQ_FIRST(&r->ws->winlist)), + win->id); + ewmh_apply_flags(win, win->ewmh_flags & ~EWMH_F_MAXIMIZED); ewmh_update_wm_state(win); - /* Restack and set new focus. */ + /* Restack and set new focus on current ws. */ + if (FLOATING(win)) + load_float_geom(win); + stack(); if (focus_mode != SWM_FOCUS_FOLLOW) { - if (r->ws->focus_pending) { - focus_win(r->ws->focus_pending); - r->ws->focus_pending = NULL; + if (r->ws->focus != NULL) { + focus_win(r->ws->focus); } else { - xcb_set_input_focus(conn, - XCB_INPUT_FOCUS_PARENT, r->id, + xcb_set_input_focus(conn, XCB_INPUT_FOCUS_PARENT, r->id, XCB_CURRENT_TIME); + bar_draw(); } } @@ -5017,7 +5096,7 @@ win_to_ws(struct ws_win *win, int wsid, int unfocus) struct ws_win *parent; struct workspace *ws, *nws, *pws; - if (wsid >= workspace_limit) + if (wsid < 0 || wsid >= workspace_limit) return; if (win->ws->idx == wsid) @@ -5029,7 +5108,9 @@ win_to_ws(struct ws_win *win, int wsid, int unfocus) DNPRINTF(SWM_D_MOVE, "win_to_ws: win %#x, ws %d -> %d\n", win->id, ws->idx, wsid); - if (focus_mode != SWM_FOCUS_FOLLOW) + /* Cleanup focus on source ws. */ + if (focus_mode != SWM_FOCUS_FOLLOW && + (ws->focus == win || ws->focus_pending == win)) ws->focus_pending = get_focus_prev(win); /* Move the parent if this is a transient window. */ @@ -5073,6 +5154,14 @@ win_to_ws(struct ws_win *win, int wsid, int unfocus) if (unfocus) unfocus_win(win); + if (ws->focus_prev == win) + ws->focus_prev = NULL; + + if (focus_mode != SWM_FOCUS_FOLLOW && ws->focus_pending != NULL) { + ws->focus = ws->focus_pending; + ws->focus_pending = NULL; + } + /* Don't unmap if new ws is visible */ if (nws->r == NULL) unmap_window(win); @@ -5084,10 +5173,6 @@ win_to_ws(struct ws_win *win, int wsid, int unfocus) TAILQ_INSERT_TAIL(&nws->stack, win, stack_entry); win->ws = nws; - /* Set focus on new ws. */ - unfocus_win(nws->focus); - nws->focus = win; - /* Update the window's workspace property: _NET_WM_DESKTOP */ DNPRINTF(SWM_D_PROP, "win_to_ws: set property: " "_NET_WM_DESKTOP: %d\n", wsid); @@ -5303,7 +5388,8 @@ search_win(struct swm_region *r, union arg *args) struct ws_win *win = NULL; struct search_window *sw = NULL; xcb_window_t w; - uint32_t wa[2]; + uint32_t wa[3]; + xcb_screen_t *screen; int i, width, height; char s[8]; FILE *lfile; @@ -5324,6 +5410,9 @@ search_win(struct swm_region *r, union arg *args) if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL) return; + if ((screen = get_screen(r->s->idx)) == NULL) + errx(1, "ERROR: can't get screen %d.", r->s->idx); + TAILQ_INIT(&search_wl); i = 1; @@ -5347,6 +5436,7 @@ search_win(struct swm_region *r, union arg *args) w = xcb_generate_id(conn); wa[0] = r->s->c[SWM_S_COLOR_FOCUS].pixel; wa[1] = r->s->c[SWM_S_COLOR_UNFOCUS].pixel; + wa[2] = screen->default_colormap; if (bar_font_legacy) { XmbTextExtents(bar_fs, s, len, &l_ibox, &l_lbox); @@ -5359,10 +5449,10 @@ search_win(struct swm_region *r, union arg *args) height = bar_font->height + 4; } - xcb_create_window(conn, XCB_COPY_FROM_PARENT, w, win->id, 0, 0, + xcb_create_window(conn, screen->root_depth, w, win->id, 0, 0, width, height, 1, XCB_WINDOW_CLASS_INPUT_OUTPUT, - XCB_COPY_FROM_PARENT, XCB_CW_BACK_PIXEL | - XCB_CW_BORDER_PIXEL, wa); + screen->root_visual, XCB_CW_BACK_PIXEL | + XCB_CW_BORDER_PIXEL | XCB_CW_COLORMAP, wa); xcb_map_window(conn, w); @@ -5387,7 +5477,7 @@ search_win(struct swm_region *r, union arg *args) DefaultVisual(display, r->s->idx), DefaultColormap(display, r->s->idx)); - XftDrawStringUtf8(draw, &bar_font_color, bar_font, 2, + XftDrawStringUtf8(draw, &search_font_color, bar_font, 2, (HEIGHT(r->bar) + bar_font->height) / 2 - bar_font->descent, (FcChar8 *)s, len); @@ -5478,7 +5568,7 @@ ewmh_update_desktop_names(void) ++len; } - if((name_list = calloc(sizeof(char *), len)) == NULL) + if((name_list = calloc(len, sizeof(char))) == NULL) err(1, "update_desktop_names: calloc: failed to " "allocate memory."); @@ -5561,7 +5651,7 @@ ewmh_update_client_list(void) if (count == 0) continue; - wins = calloc(sizeof(xcb_window_t), count); + wins = calloc(count, sizeof(xcb_window_t)); if (wins == NULL) err(1, "ewmh_update_client_list: calloc: failed to " "allocate memory."); @@ -5596,7 +5686,7 @@ ewmh_update_desktops(void) int num_screens, i, j; uint32_t *vals; - vals = calloc(sizeof(uint32_t), workspace_limit * 2); + vals = calloc(workspace_limit * 2, sizeof(uint32_t)); if (vals == NULL) err(1, "ewmh_update_desktops: calloc: failed to allocate " "memory."); @@ -5819,26 +5909,32 @@ maximize_toggle(struct swm_region *r, union arg *args) void floating_toggle(struct swm_region *r, union arg *args) { - struct ws_win *win = r->ws->focus; + struct ws_win *w = r->ws->focus; /* suppress unused warning since var is needed */ (void)args; - if (win == NULL) + if (w == NULL) return; - if (FULLSCREEN(win) || TRANS(win)) + DNPRINTF(SWM_D_MISC, "floating_toggle: win %#x\n", w->id); + + if (FULLSCREEN(w) || TRANS(w)) return; - ewmh_apply_flags(win, win->ewmh_flags ^ EWMH_F_ABOVE); - ewmh_update_wm_state(win); + if (w->ws->cur_layout == &layouts[SWM_MAX_STACK]) + return; + + ewmh_apply_flags(w, w->ewmh_flags ^ EWMH_F_ABOVE); + ewmh_update_wm_state(w); stack(); - if (win == win->ws->focus) - focus_win(win); + if (w == w->ws->focus) + focus_win(w); focus_flush(); + DNPRINTF(SWM_D_MISC, "floating_toggle: done\n"); } void @@ -5929,6 +6025,24 @@ constrain_window(struct ws_win *win, struct swm_geometry *b, int *opts) } void +update_window_color(struct ws_win *win) +{ + uint32_t *pixel; + + if (WS_FOCUSED(win->ws) && win->ws->focus == win) + pixel = MAXIMIZED(win) ? + &win->s->c[SWM_S_COLOR_FOCUS_MAXIMIZED].pixel : + &win->s->c[SWM_S_COLOR_FOCUS].pixel; + else + pixel = MAXIMIZED(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); +} + +void update_window(struct ws_win *win) { uint16_t mask; @@ -6124,6 +6238,12 @@ resize(struct ws_win *win, union arg *args) xcb_flush(conn); } break; + case XCB_KEY_PRESS: + /* Ignore. */ + xcb_allow_events(conn, XCB_ALLOW_ASYNC_KEYBOARD, + ((xcb_key_press_event_t *)evt)->time); + xcb_flush(conn); + break; default: event_handle(evt); @@ -6174,9 +6294,18 @@ 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 && r != win->ws->r) { + if (r != NULL && r != win->ws->r) { + if (clear_maximized(r->ws) > 0) + stack(); + win_to_ws(win, r->ws->idx, 0); + + /* Set focus on new ws. */ + unfocus_win(r->ws->focus); + r->ws->focus = win; + set_region(r); + raise_window(win); } } @@ -6297,6 +6426,12 @@ move(struct ws_win *win, union arg *args) xcb_flush(conn); } break; + case XCB_KEY_PRESS: + /* Ignore. */ + xcb_allow_events(conn, XCB_ALLOW_ASYNC_KEYBOARD, + ((xcb_key_press_event_t *)evt)->time); + xcb_flush(conn); + break; default: event_handle(evt); @@ -6316,6 +6451,13 @@ move(struct ws_win *win, union arg *args) xcb_flush(conn); } store_float_geom(win); + + /* New region set to fullscreen layout. */ + if (win->ws->cur_layout == &layouts[SWM_MAX_STACK]) { + stack(); + focus_flush(); + } + out: free(qpr); xcb_ungrab_pointer(conn, XCB_CURRENT_TIME); @@ -6572,11 +6714,21 @@ spawn_expand(struct swm_region *r, union arg *args, const char *spawn_name, strdup(r->s->c[SWM_S_COLOR_FOCUS].name)) == NULL) err(1, "spawn_custom color focus"); + } else if (strcasecmp(ap, "$color_focus_maximized") == 0) { + if ((real_args[c] = + strdup(r->s->c[SWM_S_COLOR_FOCUS_MAXIMIZED].name)) + == NULL) + err(1, "spawn_custom color focus maximized"); } else if (strcasecmp(ap, "$color_unfocus") == 0) { if ((real_args[c] = strdup(r->s->c[SWM_S_COLOR_UNFOCUS].name)) == NULL) err(1, "spawn_custom color unfocus"); + } else if (strcasecmp(ap, "$color_unfocus_maximized") == 0) { + if ((real_args[c] = + strdup(r->s->c[SWM_S_COLOR_UNFOCUS_MAXIMIZED].name)) + == NULL) + err(1, "spawn_custom color unfocus maximized"); } else if (strcasecmp(ap, "$region_index") == 0) { if (asprintf(&real_args[c], "%d", get_region_index(r) + 1) < 1) @@ -6720,7 +6872,7 @@ void spawn_insert(const char *name, const char *args, int flags) { struct spawn_prog *sp; - char *arg, *dup, *ptr; + char *arg, *cp, *ptr; DNPRINTF(SWM_D_SPAWN, "spawn_insert: %s[%s]\n", name, args); @@ -6733,7 +6885,7 @@ spawn_insert(const char *name, const char *args, int flags) err(1, "spawn_insert: strdup"); /* Convert the arguments to an argument list. */ - if ((ptr = dup = strdup(args)) == NULL) + if ((ptr = cp = strdup(args)) == NULL) err(1, "spawn_insert: strdup"); while ((arg = argsep(&ptr)) != NULL) { /* Null argument; skip it. */ @@ -6747,7 +6899,7 @@ spawn_insert(const char *name, const char *args, int flags) if ((sp->argv[sp->argc - 1] = strdup(arg)) == NULL) err(1, "spawn_insert: strdup"); } - free(dup); + free(cp); sp->flags = flags; @@ -7381,6 +7533,7 @@ const char *quirkname[] = { "FOCUSPREV", "NOFOCUSONMAP", "FOCUSONMAP_SINGLE", + "OBEYAPPFOCUSREQ", }; /* SWM_Q_WS: retain '|' for back compat for now (2009-08-11) */ @@ -7686,7 +7839,8 @@ enum { SWM_S_WINDOW_CLASS_ENABLED, SWM_S_WINDOW_INSTANCE_ENABLED, SWM_S_WINDOW_NAME_ENABLED, - SWM_S_WORKSPACE_LIMIT + SWM_S_WORKSPACE_LIMIT, + SWM_S_WORKSPACE_NAME, }; int @@ -7694,7 +7848,7 @@ setconfvalue(const char *selector, const char *value, int flags) { struct workspace *ws; int i, ws_id, num_screens; - char *b, *str; + char *b, *str, s[1024]; switch (flags) { case SWM_S_BAR_ACTION: @@ -7908,6 +8062,32 @@ setconfvalue(const char *selector, const char *value, int flags) ewmh_update_desktops(); break; + case SWM_S_WORKSPACE_NAME: + if (getenv("SWM_STARTED") != NULL) + return (0); + + bzero(s, sizeof s); + if (sscanf(value, "ws[%d]:%1023c", &ws_id, s) != 2) + errx(1, "invalid entry, should be 'ws[]:name'"); + ws_id--; + if (ws_id < 0 || ws_id >= workspace_limit) + errx(1, "setconfvalue: workspace_name: invalid " + "workspace %d.", ws_id + 1); + + num_screens = get_screen_count(); + for (i = 0; i < num_screens; ++i) { + ws = (struct workspace *)&screens[i].ws; + + if (strlen(s) > 0) { + free(ws[ws_id].name); + if ((ws[ws_id].name = strdup(s)) == NULL) + err(1, "setconfvalue: workspace_name."); + + ewmh_update_desktop_names(); + ewmh_get_desktop_names(); + } + } + break; default: return (1); } @@ -7937,9 +8117,42 @@ setconfmodkey(const char *selector, const char *value, int flags) int setconfcolor(const char *selector, const char *value, int flags) { - setscreencolor(value, - (selector == NULL || strlen(selector) == 0) ? -1 : atoi(selector), - flags); + int first, last, i = 0, num_screens; + + num_screens = get_screen_count(); + + /* conf screen indices begin at 1; treat vals <= 0 as 'all screens.' */ + if (selector == NULL || strlen(selector) == 0 || + (last = atoi(selector) - 1) < 0) { + first = 0; + last = num_screens - 1; + } else { + first = last; + } + + if (last >= num_screens) { + add_startup_exception("invalid screen index: %d out of bounds " + "(maximum %d)", last + 1, num_screens); + return (1); + } + + for (i = first; i <= last; ++i) { + setscreencolor(value, i, flags); + + /* + * When setting focus/unfocus colors, we need to also + * set maximize colors to match if they haven't been customized. + */ + if (flags == SWM_S_COLOR_FOCUS && + !screens[i].c[SWM_S_COLOR_FOCUS_MAXIMIZED].manual) + setscreencolor(value, i, SWM_S_COLOR_FOCUS_MAXIMIZED); + else if (flags == SWM_S_COLOR_UNFOCUS && + !screens[i].c[SWM_S_COLOR_UNFOCUS_MAXIMIZED].manual) + setscreencolor(value, i, SWM_S_COLOR_UNFOCUS_MAXIMIZED); + + screens[i].c[flags].manual = 1; + } + return (0); } @@ -8138,7 +8351,9 @@ struct config_option configopt[] = { { "clock_enabled", setconfvalue, SWM_S_CLOCK_ENABLED }, { "clock_format", setconfvalue, SWM_S_CLOCK_FORMAT }, { "color_focus", setconfcolor, SWM_S_COLOR_FOCUS }, + { "color_focus_maximized", setconfcolor, SWM_S_COLOR_FOCUS_MAXIMIZED }, { "color_unfocus", setconfcolor, SWM_S_COLOR_UNFOCUS }, + { "color_unfocus_maximized", setconfcolor, SWM_S_COLOR_UNFOCUS_MAXIMIZED }, { "cycle_empty", setconfvalue, SWM_S_CYCLE_EMPTY }, { "cycle_visible", setconfvalue, SWM_S_CYCLE_VISIBLE }, { "dialog_ratio", setconfvalue, SWM_S_DIALOG_RATIO }, @@ -8170,6 +8385,7 @@ struct config_option configopt[] = { { "window_instance_enabled", setconfvalue, SWM_S_WINDOW_INSTANCE_ENABLED }, { "window_name_enabled", setconfvalue, SWM_S_WINDOW_NAME_ENABLED }, { "workspace_limit", setconfvalue, SWM_S_WORKSPACE_LIMIT }, + { "name", setconfvalue, SWM_S_WORKSPACE_NAME }, }; void @@ -8474,17 +8690,17 @@ get_ws_idx(xcb_window_t id) } struct ws_win * -manage_window(xcb_window_t id, uint16_t mapped) +manage_window(xcb_window_t id, int mapped) { - xcb_window_t trans = XCB_WINDOW_NONE; struct ws_win *win, *ww; - int ws_idx; - char *class, *instance, *name; struct swm_region *r; struct pid_e *p; struct quirk *qp; - uint32_t i, wa[2], new_flags; xcb_get_geometry_reply_t *gr; + xcb_window_t trans = XCB_WINDOW_NONE; + uint32_t i, wa[2], new_flags; + int ws_idx; + char *class, *instance, *name; if ((win = find_window(id)) != NULL) { DNPRINTF(SWM_D_MISC, "manage_window: win %#x already " @@ -9040,14 +9256,18 @@ configurerequest(xcb_configure_request_event_t *e) win->g_floatvalid = 1; - if (ABOVE(win) && r && !MAXIMIZED(win) && (TRANS(win) || - win->ws->cur_layout != &layouts[SWM_MAX_STACK])) { + if (!MAXIMIZED(win) && !FULLSCREEN(win) && + (TRANS(win) || (ABOVE(win) && + win->ws->cur_layout != &layouts[SWM_MAX_STACK]))) { WIDTH(win) = win->g_float.w; HEIGHT(win) = win->g_float.h; - if (r) { + if (r != NULL) { update_floater(win); focus_flush(); + } else { + config_win(win, e); + xcb_flush(conn); } } else { config_win(win, e); @@ -9227,6 +9447,13 @@ enternotify(xcb_enter_notify_event_t *e) return; } } else { + if (e->mode == XCB_NOTIFY_MODE_NORMAL && + e->detail == XCB_NOTIFY_DETAIL_INFERIOR) { + DNPRINTF(SWM_D_EVENT, "enternotify: entering from " + "inferior; ignoring\n"); + return; + } + focus_win(get_focus_magic(win)); } @@ -9543,6 +9770,30 @@ unmapnotify(xcb_unmap_notify_event_t *e) focus_flush(); } +#ifdef SWM_DEBUG +char * +get_source_type_label(uint32_t type) +{ + char *label; + + switch (type) { + case EWMH_SOURCE_TYPE_NONE: + label = "None"; + break; + case EWMH_SOURCE_TYPE_NORMAL: + label = "Normal"; + break; + case EWMH_SOURCE_TYPE_OTHER: + label = "Other"; + break; + default: + label = "Invalid"; + } + + return label; +} +#endif + void clientmessage(xcb_client_message_event_t *e) { @@ -9569,8 +9820,7 @@ clientmessage(xcb_client_message_event_t *e) break; } - if (r && e->data.data32[0] < - (uint32_t)workspace_limit) { + if (r && e->data.data32[0] < (uint32_t)workspace_limit) { a.id = e->data.data32[0]; switchws(r, &a); focus_flush(); @@ -9592,11 +9842,22 @@ clientmessage(xcb_client_message_event_t *e) } if (e->type == ewmh[_NET_ACTIVE_WINDOW].atom) { - DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_ACTIVE_WINDOW\n"); - if (WS_FOCUSED(win->ws)) - focus_win(win); - else - win->ws->focus_pending = win; + DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_ACTIVE_WINDOW, " + "source_type: %s(%d)\n", + get_source_type_label(e->data.data32[0]), + e->data.data32[0]); + + /* + * Allow focus changes that are a result of direct user + * action and from applications that use the old EWMH spec. + */ + if (e->data.data32[0] != EWMH_SOURCE_TYPE_NORMAL || + win->quirks & SWM_Q_OBEYAPPFOCUSREQ) { + if (WS_FOCUSED(win->ws)) + focus_win(win); + else + win->ws->focus_pending = win; + } } else if (e->type == ewmh[_NET_CLOSE_WINDOW].atom) { DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_CLOSE_WINDOW\n"); if (win->can_delete) @@ -9618,9 +9879,9 @@ clientmessage(xcb_client_message_event_t *e) update_window(win); } else { - /* TODO: Change stack sizes */ - /* notify no change was made. */ + /* Notify no change was made. */ config_win(win, NULL); + /* TODO: Change stack sizes */ } } else if (e->type == ewmh[_NET_RESTACK_WINDOW].atom) { DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_RESTACK_WINDOW\n"); @@ -9638,6 +9899,19 @@ clientmessage(xcb_client_message_event_t *e) ewmh_update_wm_state(win); stack(); + } else if (e->type == ewmh[_NET_WM_DESKTOP].atom) { + DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_WM_DESKTOP\n"); + r = win->ws->r; + + win_to_ws(win, e->data.data32[0], 1); + + /* 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(); + } } focus_flush(); @@ -10005,6 +10279,11 @@ grab_windows(void) "skip %#x; region input window.\n", wins[j]); break; + } else if (r->bar->id == wins[j]) { + DNPRINTF(SWM_D_INIT, "grab_windows: " + "skip %#x; region bar.\n", + wins[j]); + break; } } @@ -10035,7 +10314,7 @@ grab_windows(void) continue; } - state = getstate(wins[j]); + state = get_win_state(wins[j]); manage = state != XCB_ICCCM_WM_STATE_WITHDRAWN; mapped = gar->map_state == XCB_MAP_STATE_VIEWABLE; if (mapped || manage) @@ -10060,7 +10339,7 @@ grab_windows(void) continue; } - state = getstate(wins[j]); + 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]); @@ -10121,13 +10400,16 @@ setup_screens(void) screens[i].root = screen->root; /* set default colors */ - setscreencolor("red", i + 1, SWM_S_COLOR_FOCUS); - setscreencolor("rgb:88/88/88", i + 1, SWM_S_COLOR_UNFOCUS); - setscreencolor("rgb:00/80/80", i + 1, SWM_S_COLOR_BAR_BORDER); - setscreencolor("rgb:00/40/40", i + 1, + setscreencolor("red", i, SWM_S_COLOR_FOCUS); + setscreencolor("rgb:88/88/88", i, SWM_S_COLOR_UNFOCUS); + setscreencolor("rgb:00/80/80", i, SWM_S_COLOR_BAR_BORDER); + setscreencolor("rgb:00/40/40", i, SWM_S_COLOR_BAR_BORDER_UNFOCUS); - setscreencolor("black", i + 1, SWM_S_COLOR_BAR); - setscreencolor("rgb:a0/a0/a0", i + 1, SWM_S_COLOR_BAR_FONT); + setscreencolor("black", i, SWM_S_COLOR_BAR); + setscreencolor("rgb:a0/a0/a0", i, SWM_S_COLOR_BAR_FONT); + setscreencolor("red", i, SWM_S_COLOR_FOCUS_MAXIMIZED); + setscreencolor("rgb:88/88/88", i, + SWM_S_COLOR_UNFOCUS_MAXIMIZED); /* create graphics context on screen */ screens[i].bar_gc = xcb_generate_id(conn); @@ -10218,9 +10500,12 @@ shutdown_cleanup(void) if (screens[i].bar_gc != XCB_NONE) xcb_free_gc(conn, screens[i].bar_gc); - if (!bar_font_legacy) + if (!bar_font_legacy) { XftColorFree(display, DefaultVisual(display, i), DefaultColormap(display, i), &bar_font_color); + XftColorFree(display, DefaultVisual(display, i), + DefaultColormap(display, i), &search_font_color); + } } if (bar_font_legacy) @@ -10424,9 +10709,6 @@ noconfig: validate_spawns(); - /* grab existing windows (before we build the bars) */ - grab_windows(); - if (getenv("SWM_STARTED") == NULL) setenv("SWM_STARTED", "YES", 1); @@ -10436,6 +10718,9 @@ noconfig: TAILQ_FOREACH(r, &screens[i].rl, entry) bar_setup(r); + /* Manage existing windows. */ + grab_windows(); + grabkeys(); stack(); bar_draw();