From 141403f748496a04402682ed62fd63f63dc56c66 Mon Sep 17 00:00:00 2001 From: Reginald Kennedy Date: Sun, 18 Mar 2018 12:11:39 +0800 Subject: [PATCH] Add support for _NET_WM_WINDOW_OPACITY. Enables using compositing manager transparency to provide additional options for visual feedback. New options are available to enable/configure window opacity: opacity_hinting - utilize compositing manager window transparency. opacity_focus - opacity of focused window. opacity_unfocus - opacity of unfocused window. bar_opacity_focus - opacity of the status bar on the focused region. bar_opacity_unfocus - opacity of the status bar on unfocused regions. Opacity values range from 0.0 (fully transparent) to 1.0 (opaque). Default values: opacity_hinting = 0 # (disabled) opacity_focus = 1.0 opacity_unfocus = 0.75 bar_opacity_focus[1] = 1.0 bar_opacity_unfocus[1] = 0.75 Add ALWAYSOPAQUE quirk - disable opacity hinting on matching window(s). --- spectrwm.1 | 31 ++++++ spectrwm.c | 294 +++++++++++++++++++++++++++++++++++++++----------- spectrwm.conf | 7 ++ 3 files changed, 267 insertions(+), 65 deletions(-) diff --git a/spectrwm.1 b/spectrwm.1 index d01d950..6a6ca28 100644 --- a/spectrwm.1 +++ b/spectrwm.1 @@ -246,6 +246,20 @@ in some circumstances, due to the white-spaces in the default static format. See the .Ic bar_format option for more details. +.It Ic bar_opacity_focus +Bar transparency of the focused region on screen +.Ar x +when +.Ic opacity_hinting +is enabled. Values range from 0.0 (fully transparent) to 1.0 (opaque). +Default is 1.0. +.It Ic bar_opacity_unfocus Ns Bq Ar x +Bar transparency of unfocused region(s) on screen +.Ar x +when +.Ic opacity_hinting +is enabled. Values range from 0.0 (fully transparent) to 1.0 (opaque). +Default is 0.75. .It Ic bind Ns Bq Ar x Bind key or button combo to action .Ar x . @@ -389,6 +403,19 @@ Defined in the format .Li ws Ns Bo Ar idx Bc : Ns Ar name , e.g. ws[1]:Console sets the name of workspace 1 to .Dq Console . +.It Ic opacity_focus +The transparency of the focused window when +.Ic opacity_hinting +is enabled. Values range from 0.0 (fully transparent) to 1.0 (opaque). +Default is 1.0. +.It Ic opacity_hinting +Utilize compositing manager window opacity (_NET_WM_WINDOW_OPACITY) as +complementary visual feedback. Enable by setting to 1. +.It Ic opacity_unfocus +The transparency of unfocused windows when +.Ic opacity_hinting +is enabled. Values range from 0.0 (fully transparent) to 1.0 (opaque). +Default is 0.75. .It Ic program Ns Bq Ar p Define new action to spawn a program .Ar p . @@ -1157,6 +1184,10 @@ XTERM_FONTADJ The quirks themselves are described below: .Pp .Bl -tag -width "XTERM_FONTADJXXX" -offset indent -compact +.It ALWAYSOPAQUE +When +.Ic opacity_hinting +is enabled, disregard this window. .It ANYWHERE Allow window to position itself, uncentered. .It FLOAT diff --git a/spectrwm.c b/spectrwm.c index 8cfa8d4..1c0edda 100644 --- a/spectrwm.c +++ b/spectrwm.c @@ -205,6 +205,8 @@ uint32_t swm_debug = 0 #define EWMH_F_MAXIMIZED (EWMH_F_MAXIMIZED_VERT | EWMH_F_MAXIMIZED_HORZ) +#define OPACITY_OPAQUE (0xffffffff) + /* convert 8-bit to 16-bit */ #define RGB_8_TO_16(col) (((col) << 8) + (col)) @@ -403,6 +405,9 @@ bool iconic_enabled = false; bool maximize_hide_bar = false; bool urgent_enabled = false; bool urgent_collapse = false; +bool opacity_hinting = false; +uint32_t opacity_focus = OPACITY_OPAQUE; +uint32_t opacity_unfocus = (0.8 * OPACITY_OPAQUE); char *clock_format = NULL; bool window_class_enabled = false; bool window_instance_enabled = false; @@ -450,6 +455,7 @@ struct swm_bar { xcb_pixmap_t buffer; struct swm_geometry g; struct swm_region *r; /* Associated region. */ + uint32_t opacity; /* Current opacity. */ }; /* virtual "screens" */ @@ -486,6 +492,7 @@ struct ws_win { bool mapped; uint8_t state; bool bordered; + uint32_t opacity; uint32_t ewmh_flags; int font_size_boundary[SWM_MAX_FONT_STEPS]; int font_steps; @@ -587,9 +594,9 @@ enum { }; enum { - SWM_S_COLOR_BAR, + SWM_S_COLOR_BAR_FOCUS, SWM_S_COLOR_BAR_SELECTED, - SWM_S_COLOR_BAR_BORDER, + SWM_S_COLOR_BAR_BORDER_FOCUS, SWM_S_COLOR_BAR_BORDER_UNFOCUS, SWM_S_COLOR_BAR_FONT, SWM_S_COLOR_BAR_FONT_SELECTED, @@ -627,6 +634,8 @@ struct swm_screen { Visual *xvisual; /* Needed for Xft. */ xcb_colormap_t colormap; xcb_gcontext_t gc; + uint32_t bar_opacity_focus; + uint32_t bar_opacity_unfocus; }; struct swm_screen *screens; @@ -708,6 +717,7 @@ struct quirk { #define SWM_Q_IGNORESPAWNWS (1<<10) /* Ignore _SWM_WS when managing win. */ #define SWM_Q_NOFOCUSCYCLE (1<<11) /* Remove from normal focus cycle. */ #define SWM_Q_MINIMALBORDER (1<<12) /* No border when floating/unfocused. */ +#define SWM_Q_ALWAYSOPAQUE (1<<13) /* No opacity hinting. */ }; TAILQ_HEAD(quirk_list, quirk) quirks = TAILQ_HEAD_INITIALIZER(quirks); @@ -744,6 +754,7 @@ enum { _NET_WM_STATE_MAXIMIZED_HORZ, _NET_WM_STATE_SKIP_PAGER, _NET_WM_STATE_SKIP_TASKBAR, + _NET_WM_WINDOW_OPACITY, _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_DIALOG, _NET_WM_WINDOW_TYPE_DOCK, @@ -758,44 +769,46 @@ enum { struct ewmh_hint { char *name; xcb_atom_t atom; + bool enabled; } ewmh[SWM_EWMH_HINT_MAX] = { /* must be in same order as in the enum */ - {"_NET_ACTIVE_WINDOW", XCB_ATOM_NONE}, - {"_NET_CLIENT_LIST", XCB_ATOM_NONE}, - {"_NET_CLOSE_WINDOW", XCB_ATOM_NONE}, - {"_NET_CURRENT_DESKTOP", XCB_ATOM_NONE}, - {"_NET_DESKTOP_GEOMETRY", XCB_ATOM_NONE}, - {"_NET_DESKTOP_NAMES", XCB_ATOM_NONE}, - {"_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}, - {"_NET_WM_ACTION_FULLSCREEN", XCB_ATOM_NONE}, - {"_NET_WM_ACTION_MOVE", XCB_ATOM_NONE}, - {"_NET_WM_ACTION_RESIZE", XCB_ATOM_NONE}, - {"_NET_WM_ALLOWED_ACTIONS", XCB_ATOM_NONE}, - {"_NET_WM_DESKTOP", XCB_ATOM_NONE}, - {"_NET_WM_FULL_PLACEMENT", XCB_ATOM_NONE}, - {"_NET_WM_NAME", XCB_ATOM_NONE}, - {"_NET_WM_STATE", XCB_ATOM_NONE}, - {"_NET_WM_STATE_ABOVE", XCB_ATOM_NONE}, - {"_NET_WM_STATE_FULLSCREEN", XCB_ATOM_NONE}, - {"_NET_WM_STATE_HIDDEN", XCB_ATOM_NONE}, - {"_NET_WM_STATE_MAXIMIZED_VERT", XCB_ATOM_NONE}, - {"_NET_WM_STATE_MAXIMIZED_HORZ", XCB_ATOM_NONE}, - {"_NET_WM_STATE_SKIP_PAGER", XCB_ATOM_NONE}, - {"_NET_WM_STATE_SKIP_TASKBAR", XCB_ATOM_NONE}, - {"_NET_WM_WINDOW_TYPE", XCB_ATOM_NONE}, - {"_NET_WM_WINDOW_TYPE_DIALOG", XCB_ATOM_NONE}, - {"_NET_WM_WINDOW_TYPE_DOCK", XCB_ATOM_NONE}, - {"_NET_WM_WINDOW_TYPE_NORMAL", XCB_ATOM_NONE}, - {"_NET_WM_WINDOW_TYPE_SPLASH", XCB_ATOM_NONE}, - {"_NET_WM_WINDOW_TYPE_TOOLBAR", XCB_ATOM_NONE}, - {"_NET_WM_WINDOW_TYPE_UTILITY", XCB_ATOM_NONE}, - {"_SWM_WM_STATE_MANUAL", XCB_ATOM_NONE}, + {"_NET_ACTIVE_WINDOW", XCB_ATOM_NONE, true}, + {"_NET_CLIENT_LIST", XCB_ATOM_NONE, true}, + {"_NET_CLOSE_WINDOW", XCB_ATOM_NONE, true}, + {"_NET_CURRENT_DESKTOP", XCB_ATOM_NONE, true}, + {"_NET_DESKTOP_GEOMETRY", XCB_ATOM_NONE, true}, + {"_NET_DESKTOP_NAMES", XCB_ATOM_NONE, true}, + {"_NET_DESKTOP_VIEWPORT", XCB_ATOM_NONE, true}, + {"_NET_MOVERESIZE_WINDOW", XCB_ATOM_NONE, true}, + {"_NET_NUMBER_OF_DESKTOPS", XCB_ATOM_NONE, true}, + {"_NET_REQUEST_FRAME_EXTENTS", XCB_ATOM_NONE, true}, + {"_NET_RESTACK_WINDOW", XCB_ATOM_NONE, true}, + {"_NET_WM_ACTION_ABOVE", XCB_ATOM_NONE, true}, + {"_NET_WM_ACTION_CLOSE", XCB_ATOM_NONE, true}, + {"_NET_WM_ACTION_FULLSCREEN", XCB_ATOM_NONE, true}, + {"_NET_WM_ACTION_MOVE", XCB_ATOM_NONE, true}, + {"_NET_WM_ACTION_RESIZE", XCB_ATOM_NONE, true}, + {"_NET_WM_ALLOWED_ACTIONS", XCB_ATOM_NONE, true}, + {"_NET_WM_DESKTOP", XCB_ATOM_NONE, true}, + {"_NET_WM_FULL_PLACEMENT", XCB_ATOM_NONE, true}, + {"_NET_WM_NAME", XCB_ATOM_NONE, true}, + {"_NET_WM_STATE", XCB_ATOM_NONE, true}, + {"_NET_WM_STATE_ABOVE", XCB_ATOM_NONE, true}, + {"_NET_WM_STATE_FULLSCREEN", XCB_ATOM_NONE, true}, + {"_NET_WM_STATE_HIDDEN", XCB_ATOM_NONE, true}, + {"_NET_WM_STATE_MAXIMIZED_VERT", XCB_ATOM_NONE, true}, + {"_NET_WM_STATE_MAXIMIZED_HORZ", XCB_ATOM_NONE, true}, + {"_NET_WM_STATE_SKIP_PAGER", XCB_ATOM_NONE, true}, + {"_NET_WM_STATE_SKIP_TASKBAR", XCB_ATOM_NONE, true}, + {"_NET_WM_WINDOW_OPACITY", XCB_ATOM_NONE, false}, + {"_NET_WM_WINDOW_TYPE", XCB_ATOM_NONE, true}, + {"_NET_WM_WINDOW_TYPE_DIALOG", XCB_ATOM_NONE, true}, + {"_NET_WM_WINDOW_TYPE_DOCK", XCB_ATOM_NONE, true}, + {"_NET_WM_WINDOW_TYPE_NORMAL", XCB_ATOM_NONE, true}, + {"_NET_WM_WINDOW_TYPE_SPLASH", XCB_ATOM_NONE, true}, + {"_NET_WM_WINDOW_TYPE_TOOLBAR", XCB_ATOM_NONE, true}, + {"_NET_WM_WINDOW_TYPE_UTILITY", XCB_ATOM_NONE, true}, + {"_SWM_WM_STATE_MANUAL", XCB_ATOM_NONE, true}, }; /* EWMH source type */ @@ -1080,6 +1093,7 @@ void ewmh_update_client_list(void); void ewmh_update_current_desktop(void); void ewmh_update_desktop_names(void); void ewmh_update_desktops(void); +void ewmh_update_supported(void); void ewmh_change_wm_state(struct ws_win *, xcb_atom_t, long); void ewmh_update_wm_state(struct ws_win *); char *expand_tilde(const char *); @@ -1209,6 +1223,7 @@ 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_bar_opacity(struct swm_bar *, uint32_t); void set_region(struct swm_region *); int setautorun(const char *, const char *, int); void setbinding(uint16_t, enum binding_type, uint32_t, enum actionid, @@ -1216,6 +1231,7 @@ void setbinding(uint16_t, enum binding_type, uint32_t, enum actionid, int setconfbinding(const char *, const char *, int); int setconfcolor(const char *, const char *, int); int setconfmodkey(const char *, const char *, int); +int setconfopacity(const char *, const char *, int); int setconfquirk(const char *, const char *, int); int setconfregion(const char *, const char *, int); int setconfspawn(const char *, const char *, int); @@ -1234,6 +1250,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_opacity(struct ws_win *, uint32_t); void set_win_state(struct ws_win *, uint8_t); void shutdown_cleanup(void); void sighdlr(int); @@ -1513,7 +1530,7 @@ void setup_ewmh(void) { xcb_window_t root, win; - int i, j, num_screens; + int i, num_screens; for (i = 0; i < LENGTH(ewmh); i++) ewmh[i].atom = get_atom_from_string(ewmh[i].name); @@ -1536,18 +1553,34 @@ setup_ewmh(void) xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win, ewmh[_NET_WM_NAME].atom, a_utf8_string, 8, strlen("spectrwm"), "spectrwm"); + } + + ewmh_update_supported(); + ewmh_update_desktops(); + ewmh_get_desktop_names(); +} + +void +ewmh_update_supported(void) +{ + xcb_window_t root; + int i, j, num_screens; + + num_screens = get_screen_count(); + for (i = 0; i < num_screens; i++) { + root = screens[i].root; /* Report supported atoms */ xcb_delete_property(conn, root, a_net_supported); - for (j = 0; j < LENGTH(ewmh); j++) + for (j = 0; j < LENGTH(ewmh); j++) { + if (!ewmh[j].enabled) + continue; xcb_change_property(conn, XCB_PROP_MODE_APPEND, root, a_net_supported, XCB_ATOM_ATOM, 32, 1, &ewmh[j].atom); - + DNPRINTF(SWM_D_CONF, "adding %s\n", ewmh[j].name); + } } - - ewmh_update_desktops(); - ewmh_get_desktop_names(); } void @@ -1573,10 +1606,12 @@ teardown_ewmh(void) xcb_destroy_window(conn, id); xcb_delete_property(conn, screens[i].root, a_net_wm_check); - xcb_delete_property(conn, screens[i].root, - a_net_supported); } free(pr); + + /* Cleanup _NET_SUPPORTED */ + xcb_delete_property(conn, screens[i].root, + a_net_supported); } } @@ -1951,8 +1986,8 @@ debug_refresh(struct ws_win *win) 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[0] = win->s->c[SWM_S_COLOR_BAR_FOCUS].pixel; + wc[1] = win->s->c[SWM_S_COLOR_BAR_BORDER_FOCUS].pixel; wc[2] = win->s->colormap; xcb_create_window(conn, win->s->depth, win->debug, @@ -2018,7 +2053,7 @@ debug_refresh(struct ws_win *win) rect.width = width; rect.height = height; - gcv[0] = win->s->c[SWM_S_COLOR_BAR].pixel; + gcv[0] = win->s->c[SWM_S_COLOR_BAR_FOCUS].pixel; xcb_change_gc(conn, win->s->gc, XCB_GC_FOREGROUND, gcv); xcb_poly_fill_rectangle(conn, win->debug, win->s->gc, 1, &rect); @@ -2301,7 +2336,7 @@ bar_print_legacy(struct swm_region *r, const char *s) rect.width = WIDTH(r->bar); rect.height = HEIGHT(r->bar); - gcv[0] = r->s->c[SWM_S_COLOR_BAR].pixel; + gcv[0] = r->s->c[SWM_S_COLOR_BAR_FOCUS].pixel; xcb_change_gc(conn, r->s->gc, XCB_GC_FOREGROUND, gcv); xcb_poly_fill_rectangle(conn, r->bar->buffer, r->s->gc, 1, &rect); @@ -2354,7 +2389,7 @@ bar_print(struct swm_region *r, const char *s) rect.width = WIDTH(r->bar); rect.height = HEIGHT(r->bar); - gcv[0] = r->s->c[SWM_S_COLOR_BAR].pixel; + gcv[0] = r->s->c[SWM_S_COLOR_BAR_FOCUS].pixel; xcb_change_gc(conn, r->s->gc, XCB_GC_FOREGROUND, gcv); xcb_poly_fill_rectangle(conn, r->bar->buffer, r->s->gc, 1, &rect); @@ -3123,7 +3158,7 @@ xft_init(struct swm_region *r) &color, &bar_font_color)) warn("Xft error: unable to allocate color."); - PIXEL_TO_XRENDERCOLOR(r->s->c[SWM_S_COLOR_BAR].pixel, color); + PIXEL_TO_XRENDERCOLOR(r->s->c[SWM_S_COLOR_BAR_FOCUS].pixel, color); if (!XftColorAllocValue(display, r->s->xvisual, r->s->colormap, &color, &search_font_color)) @@ -3162,10 +3197,11 @@ bar_setup(struct swm_region *r) Y(r->bar) = bar_at_bottom ? (Y(r) + HEIGHT(r) - bar_height) : Y(r); WIDTH(r->bar) = WIDTH(r) - 2 * bar_border_width; HEIGHT(r->bar) = bar_height - 2 * bar_border_width; + r->bar->opacity = OPACITY_OPAQUE; /* Assume region is unfocused when we create the bar. */ r->bar->id = xcb_generate_id(conn); - wa[0] = r->s->c[SWM_S_COLOR_BAR].pixel; + wa[0] = r->s->c[SWM_S_COLOR_BAR_FOCUS].pixel; wa[1] = r->s->c[SWM_S_COLOR_BAR_BORDER_UNFOCUS].pixel; wa[2] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_POINTER_MOTION_HINT; @@ -4028,6 +4064,47 @@ validate_ws(struct workspace *testws) return (1); } +void +set_bar_opacity(struct swm_bar *bar, uint32_t opacity) +{ + if (opacity == bar->opacity) + return; + DNPRINTF(SWM_D_BAR, "id: %#x, cur_opacity: %#x, opacity: %#x\n", + bar->id, bar->opacity, opacity); + + if (opacity != OPACITY_OPAQUE) { + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, bar->id, + ewmh[_NET_WM_WINDOW_OPACITY].atom, XCB_ATOM_CARDINAL, 32, 1, + &opacity); + } else { + xcb_delete_property(conn, bar->id, + ewmh[_NET_WM_WINDOW_OPACITY].atom); + } + + bar->opacity = opacity; +} + +void +set_win_opacity(struct ws_win *win, uint32_t opacity) +{ + if (win->quirks & SWM_Q_ALWAYSOPAQUE) + return; + + if (opacity == win->opacity) + return; + + if (opacity != OPACITY_OPAQUE) { + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win->frame, + ewmh[_NET_WM_WINDOW_OPACITY].atom, XCB_ATOM_CARDINAL, 32, 1, + &opacity); + } else { + xcb_delete_property(conn, win->frame, + ewmh[_NET_WM_WINDOW_OPACITY].atom); + } + + win->opacity = opacity; +} + void unfocus_win(struct ws_win *win) { @@ -4142,6 +4219,8 @@ focus_win(struct ws_win *win) DNPRINTF(SWM_D_FOCUS, "skip refocus " "from override_redirect.\n"); goto out; + } else { + unfocus_win(cfw); } } } @@ -4312,6 +4391,9 @@ set_region(struct swm_region *r) xcb_change_window_attributes(conn, rf->bar->id, XCB_CW_BORDER_PIXEL, &r->s->c[SWM_S_COLOR_BAR_BORDER_UNFOCUS].pixel); + + if (opacity_hinting) + set_bar_opacity(rf->bar, r->s->bar_opacity_unfocus); } if (rf != NULL && rf != r && (X(rf) != X(r) || Y(rf) != Y(r) || @@ -4326,7 +4408,10 @@ set_region(struct swm_region *r) /* Set region bar border to focus_color. */ xcb_change_window_attributes(conn, r->bar->id, - XCB_CW_BORDER_PIXEL, &r->s->c[SWM_S_COLOR_BAR_BORDER].pixel); + XCB_CW_BORDER_PIXEL, &r->s->c[SWM_S_COLOR_BAR_BORDER_FOCUS].pixel); + + if (opacity_hinting) + set_bar_opacity(r->bar, r->s->bar_opacity_focus); r->s->r_focus = r; @@ -6326,7 +6411,7 @@ search_win(struct binding *bp, struct swm_region *r, union arg *args) l_draw = XCreateGC(display, w, 0, &l_gcv); XSetForeground(display, l_draw, - r->s->c[SWM_S_COLOR_BAR].pixel); + r->s->c[SWM_S_COLOR_BAR_FOCUS].pixel); DRAWSTRING(display, w, bar_fs, l_draw, 2, (bar_fs_extents->max_logical_extent.height - @@ -6929,15 +7014,22 @@ draw_frame(struct ws_win *win) DNPRINTF(SWM_D_EVENT, "win %#x frame disabled\n", win->id); } - if (WS_FOCUSED(win->ws) && win->ws->focus == win) + 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 + + if (opacity_hinting) + set_win_opacity(win, opacity_focus); + } else { pixel = MAXIMIZED(win) ? &win->s->c[SWM_S_COLOR_UNFOCUS_MAXIMIZED].pixel : &win->s->c[SWM_S_COLOR_UNFOCUS].pixel; + if (opacity_hinting) + set_win_opacity(win, opacity_unfocus); + } + /* Top (with corners) */ rect[n].x = 0; rect[n].y = 0; @@ -7784,12 +7876,12 @@ spawn_expand(struct swm_region *r, union arg *args, const char *spawn_name, DNPRINTF(SWM_D_SPAWN, "raw arg: %s\n", ap); if (strcasecmp(ap, "$bar_border") == 0) { if ((real_args[c] = - strdup(r->s->c[SWM_S_COLOR_BAR_BORDER].name)) + strdup(r->s->c[SWM_S_COLOR_BAR_BORDER_FOCUS].name)) == NULL) err(1, "spawn_custom border color"); } else if (strcasecmp(ap, "$bar_color") == 0) { if ((real_args[c] = - strdup(r->s->c[SWM_S_COLOR_BAR].name)) + strdup(r->s->c[SWM_S_COLOR_BAR_FOCUS].name)) == NULL) err(1, "spawn_custom bar color"); } else if (strcasecmp(ap, "$bar_color_selected") == 0) { @@ -8858,6 +8950,7 @@ const char *quirkname[] = { "IGNORESPAWNWS", "NOFOCUSCYCLE", "MINIMALBORDER", + "ALWAYSOPAQUE", }; /* SWM_Q_DELIM: retain '|' for back compat for now (2009-08-11) */ @@ -9180,6 +9273,11 @@ enum { SWM_S_FOCUS_MODE, SWM_S_ICONIC_ENABLED, SWM_S_MAXIMIZE_HIDE_BAR, + SWM_S_OPACITY_BAR_FOCUS, + SWM_S_OPACITY_BAR_UNFOCUS, + SWM_S_OPACITY_FOCUS, + SWM_S_OPACITY_HINTING, + SWM_S_OPACITY_UNFOCUS, SWM_S_REGION_PADDING, SWM_S_SPAWN_ORDER, SWM_S_SPAWN_TERM, @@ -9390,6 +9488,11 @@ setconfvalue(const char *selector, const char *value, int flags) case SWM_S_URGENT_ENABLED: urgent_enabled = (atoi(value) != 0); break; + case SWM_S_OPACITY_HINTING: + opacity_hinting = (atoi(value) != 0); + ewmh[_NET_WM_WINDOW_OPACITY].enabled = opacity_hinting; + ewmh_update_supported(); + break; case SWM_S_VERBOSE_LAYOUT: verbose_layout = (atoi(value) != 0); for (i = 0; layouts[i].l_stack != NULL; i++) { @@ -9484,6 +9587,57 @@ setconfmodkey(const char *selector, const char *value, int flags) return (0); } +int +setconfopacity(const char *selector, const char *value, int flags) +{ + int first, last, i = 0, num_screens; + double d; + uint32_t opacity; + + 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); + } + + d = atof(value); + if (d < 0.0) + d = 0.0; + else if (d > 1.0) + d = 1.0; + opacity = d * OPACITY_OPAQUE; + + for (i = first; i <= last; ++i) { + switch (flags) { + case SWM_S_OPACITY_FOCUS: + opacity_focus = opacity; + break; + case SWM_S_OPACITY_UNFOCUS: + opacity_unfocus = opacity; + break; + case SWM_S_OPACITY_BAR_FOCUS: + screens[i].bar_opacity_focus = opacity; + break; + case SWM_S_OPACITY_BAR_UNFOCUS: + screens[i].bar_opacity_unfocus = opacity; + break; + } + } + + return (0); +} + int setconfcolor(const char *selector, const char *value, int flags) { @@ -9524,12 +9678,12 @@ setconfcolor(const char *selector, const char *value, int flags) setscreencolor(value, i, SWM_S_COLOR_UNFOCUS_MAXIMIZED); break; - case SWM_S_COLOR_BAR: + case SWM_S_COLOR_BAR_FOCUS: if (!screens[i].c[SWM_S_COLOR_BAR_FONT_SELECTED].manual) setscreencolor(value, i, SWM_S_COLOR_BAR_FONT_SELECTED); break; - case SWM_S_COLOR_BAR_BORDER: + case SWM_S_COLOR_BAR_BORDER_FOCUS: if (!screens[i].c[SWM_S_COLOR_BAR_SELECTED].manual) setscreencolor(value, i, SWM_S_COLOR_BAR_SELECTED); @@ -9718,10 +9872,10 @@ struct config_option configopt[] = { { "autorun", setautorun, 0 }, { "bar_action", setconfvalue, SWM_S_BAR_ACTION }, { "bar_at_bottom", setconfvalue, SWM_S_BAR_AT_BOTTOM }, - { "bar_border", setconfcolor, SWM_S_COLOR_BAR_BORDER }, + { "bar_border", setconfcolor, SWM_S_COLOR_BAR_BORDER_FOCUS }, { "bar_border_unfocus", setconfcolor, SWM_S_COLOR_BAR_BORDER_UNFOCUS }, { "bar_border_width", setconfvalue, SWM_S_BAR_BORDER_WIDTH }, - { "bar_color", setconfcolor, SWM_S_COLOR_BAR }, + { "bar_color", setconfcolor, SWM_S_COLOR_BAR_FOCUS }, { "bar_color_selected", setconfcolor, SWM_S_COLOR_BAR_SELECTED }, { "bar_delay", NULL, 0 }, /* dummy */ { "bar_enabled", setconfvalue, SWM_S_BAR_ENABLED }, @@ -9731,6 +9885,8 @@ struct config_option configopt[] = { { "bar_font_color_selected", setconfcolor, SWM_S_COLOR_BAR_FONT_SELECTED }, { "bar_format", setconfvalue, SWM_S_BAR_FORMAT }, { "bar_justify", setconfvalue, SWM_S_BAR_JUSTIFY }, + { "bar_opacity", setconfopacity, SWM_S_OPACITY_BAR_FOCUS }, + { "bar_opacity_unfocus", setconfopacity, SWM_S_OPACITY_BAR_UNFOCUS }, { "bind", setconfbinding, 0 }, { "border_width", setconfvalue, SWM_S_BORDER_WIDTH }, { "boundary_width", setconfvalue, SWM_S_BOUNDARY_WIDTH }, @@ -9754,6 +9910,9 @@ struct config_option configopt[] = { { "layout", setlayout, 0 }, { "maximize_hide_bar", setconfvalue, SWM_S_MAXIMIZE_HIDE_BAR }, { "modkey", setconfmodkey, 0 }, + { "opacity_focus", setconfopacity, SWM_S_OPACITY_FOCUS }, + { "opacity_hinting", setconfvalue, SWM_S_OPACITY_HINTING }, + { "opacity_unfocus", setconfopacity, SWM_S_OPACITY_UNFOCUS }, { "program", setconfspawn, 0 }, { "quirk", setconfquirk, 0 }, { "region", setconfregion, 0 }, @@ -10118,6 +10277,9 @@ reparent_window(struct ws_win *win) win->s->visual, XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP, wa); + if (opacity_hinting) + set_win_opacity(win, opacity_unfocus); + 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))) { @@ -12116,6 +12278,8 @@ setup_screens(void) DNPRINTF(SWM_D_WS, "init screen: %d\n", i); screens[i].idx = i; screens[i].r_focus = NULL; + screens[i].bar_opacity_focus = OPACITY_OPAQUE; + screens[i].bar_opacity_unfocus = 0.8 * OPACITY_OPAQUE; TAILQ_INIT(&screens[i].rl); TAILQ_INIT(&screens[i].orl); @@ -12178,10 +12342,10 @@ setup_screens(void) setscreencolor("rgb:88/88/88", i, SWM_S_COLOR_UNFOCUS); setscreencolor("rgb:88/88/88", i, SWM_S_COLOR_UNFOCUS_MAXIMIZED); - setscreencolor("rgb:00/80/80", i, SWM_S_COLOR_BAR_BORDER); + setscreencolor("rgb:00/80/80", i, SWM_S_COLOR_BAR_BORDER_FOCUS); setscreencolor("rgb:00/40/40", i, SWM_S_COLOR_BAR_BORDER_UNFOCUS); - setscreencolor("black", i, SWM_S_COLOR_BAR); + setscreencolor("black", i, SWM_S_COLOR_BAR_FOCUS); setscreencolor("rgb:00/80/80", i, SWM_S_COLOR_BAR_SELECTED); setscreencolor("rgb:a0/a0/a0", i, SWM_S_COLOR_BAR_FONT); setscreencolor("black", i, SWM_S_COLOR_BAR_FONT_SELECTED); diff --git a/spectrwm.conf b/spectrwm.conf index a7e3e6b..dff5940 100644 --- a/spectrwm.conf +++ b/spectrwm.conf @@ -29,6 +29,13 @@ # Remove window border when bar is disabled and there is only one window in workspace # disable_border = 1 +# Utilize compositing manager window transparency as visual feedback. +# opacity_hinting = 1 +# opacity_bar_focus[1] = 1.0 +# opacity_bar_unfocus[1] = 0.6 +# opacity_focus = 1.0 +# opacity_unfocus = 0.6 + # Bar Settings # bar_enabled = 1 # bar_border_width = 1