diff --git a/compton.sample.conf b/compton.sample.conf index a964dc47..3f818675 100644 --- a/compton.sample.conf +++ b/compton.sample.conf @@ -28,8 +28,6 @@ inactive-opacity = 0.8; frame-opacity = 0.7; inactive-opacity-override = false; alpha-step = 0.06; -# inactive-dim = 0.2; -# inactive-dim-fixed = true; # blur-background = true; # blur-background-frame = true; blur-kern = "3x3box"; @@ -42,6 +40,11 @@ blur-background-exclude = [ ]; # opacity-rule = [ "80:class_g = 'URxvt'" ]; +# Dimming +# inactive-dim = 0.2; +# inactive-dim-fixed = true; +# dim-rule = [ "50:!(class_g ~= '^(URxvt|mpv|Sxiv)$' || _NET_WM_WINDOW_TYPE@:a *= 'DOCK')" ]; + # Fading fading = true; # fade-delta = 30; diff --git a/dbus-examples/ambient-dim.sh b/dbus-examples/ambient-dim.sh new file mode 100755 index 00000000..4a7e7a1d --- /dev/null +++ b/dbus-examples/ambient-dim.sh @@ -0,0 +1,167 @@ +#!/bin/bash + +# === Verify `compton --dbus` status === + +if [ -z "`dbus-send --session --dest=org.freedesktop.DBus --type=method_call --print-reply /org/freedesktop/DBus org.freedesktop.DBus.ListNames | grep compton`" ]; then + echo "compton DBus interface unavailable" + if [ -n "`pgrep compton`" ]; then + echo "compton running without dbus interface" + #killall compton & # Causes all windows to flicker away and come back ugly. + #compton --dbus & # Causes all windows to flicker away and come back beautiful + else + echo "compton not running" + fi + exit 1; +fi + +# === Get connection parameters === + +dpy=$(echo -n "$DISPLAY" | tr -c '[:alnum:]' _) + +if [ -z "$dpy" ]; then + echo "Cannot find display." + exit 1; +fi + +service="com.github.chjj.compton.${dpy}" +interface="com.github.chjj.compton" + +compton_dbus="dbus-send --print-reply=literal --dest="${service}" / "${interface}"." +compton_dbus_h="dbus-send --print-reply --dest="${service}" / "${interface}"." + +# === Try using flock === + +unbright="/dev/shm/unbright.$dpy" +if hash flock 2>/dev/null; then + flock="flock ${unbright}" +else + echo 'flock not found, running without it' +fi + +# === Get camera device === +if [[ -z "$1" ]] || [[ ! -c "$1" ]]; then + echo "usage: $0 " + exit 1 +else + cam="$1" +fi + +# === Sample parameters === + +max_dim=55 +frame_ms=30 +rule='!(class_g ~= "^(URxvt|mpv|Sxiv)$" || _NET_WM_WINDOW_TYPE@:32a *= "DOCK")' + +# === Trap === + +trap '_remove; _repaint; trap - SIGTERM && kill -- -$$' SIGINT SIGTERM EXIT + +_insert() { + ${compton_dbus}dim_rule_update string:insert string:"${1}:${rule}" +} + +_remove() { + if [ ! -z "$last_repr" ]; then + ${compton_dbus}dim_rule_update string:remove string:"$last_repr" >/dev/null 2>&1 + if [[ $? -ne 0 ]]; then + # Exiting during replace(). Fingers crossed we're removing the + # right thing + last_repr=$(${compton_dbus_h}opts_get string:dim-rule | \ + grep -m1 -Po '(?<=string ").*(?=")') + _remove + fi + fi +} + +_replace() { + ${compton_dbus}dim_rule_update string:replace string:"$last_repr" string:"${1}:${rule}" +} + +_repaint() { + ${compton_dbus}repaint >/dev/null +} + +replace() { + local new + if [[ -z "$last_repr" ]]; then + new=$(_insert $1) + else + new=$(_replace $1) + fi + last_repr=${new:3} + _repaint +} + +_normalize() { + old_min=72 + old_max=184 + new_min=0 + new_max=100 + old_val=$1 + new_val=$(( (((old_val - old_min) * (new_max - new_min)) / \ + (old_max - old_min)) + new_min )) + echo $new_val +} + +read_camera() { + # If your camera starts with odd brightness, have a go at these: + # if [[ $first_call -eq 1 ]]; then + # v4l2-ctl -d "$cam" -c exposure_auto=3 2>/dev/null + # v4l2-ctl -d "$cam" -c exposure_auto=1 2>/dev/null + # + # local ea=$(v4l2-ctl -d "$cam" -l | grep exposure_absolute | grep -Po '(?<=default=)\d+') + # v4l2-ctl -d "$cam" -c exposure_absolute=$ea 2>/dev/null + # + # local g=$(v4l2-ctl -d "$cam" -l | grep gain | grep -Po '(?<=default=)\d+') + # v4l2-ctl -d "$cam" -c gain=$g 2>/dev/null + # + # first=0 + # else + while read -r brightness; do + $flock echo $(_normalize "$brightness") > $unbright + done < <(ffmpeg \ + -hide_banner \ + -f video4linux2 \ + -s 640x480 \ + -i "$cam" \ + -filter:v "fps=fps=30, showinfo" \ + -f null - 2>&1 \ + | stdbuf -oL grep -Po '(?<=mean:\[)[0-9]*') +} + +rm -f "$unbright" + +read_camera & + +last_set=0 +last_brightness=0 +while :; do + # TODO Implement frame-dropping + start_date=$(date +%s%3N) + + brightness=$($flock cat "$unbright" 2>/dev/null) + if [[ ! -z $brightness ]]; then + + new_aim=$(( $max_dim - $brightness )) + [[ $new_aim -lt 0 ]] && new_aim=0 + [[ $new_aim -gt 100 ]] && new_aim=100 + + # +1 is poor man's way of avoiding some flicker. TODO become rich + if [[ $new_aim -gt $(( last_set + 1 )) ]]; then + last_set=$(( last_set + 1 )) + replace $last_set + elif [[ $new_aim -lt $(( $last_set - 1 )) ]]; then + last_set=$(( last_set - 1 )) + replace $last_set + fi + # echo $brightness $new_aim $last_set + fi + + last_brightness=$brightness + + end_date=$(date +%s%3N) + diff_date=$(( end_date - start_date )) + # -1 is time spent on invoking `sleep` and `printf`, kind of + to_sleep=$(( frame_ms - diff_date - 1 )) + [[ $to_sleep -gt 0 ]] && sleep $(printf "0.0%02d" $to_sleep) +done diff --git a/man/compton.1.asciidoc b/man/compton.1.asciidoc index 95729499..5c17ec1a 100644 --- a/man/compton.1.asciidoc +++ b/man/compton.1.asciidoc @@ -234,6 +234,9 @@ May also be one of the predefined kernels: `3x3box` (default), `5x5box`, `7x7box *--opacity-rule* 'OPACITY':'CONDITION':: Specify a list of opacity rules, in the format `PERCENT:PATTERN`, like `50:name *= "Firefox"`. compton-trans is recommended over this. Note we do not distinguish 100% and unset, and we don't make any guarantee about possible conflicts with other programs that set '_NET_WM_WINDOW_OPACITY' on frame or client windows. +*--dim-rule* 'VALUE':'CONDITION':: + Specify a list of dim rules, in the format `VALUE:PATTERN`, like `50:!class_g *= "URxvt"`, where `VALUE` is in the 0 - 100 range (0 means no dim). + *--shadow-exclude-reg* 'GEOMETRY':: Specify a X geometry that describes the region in which shadow should not be painted in, such as a dock window region. Use `--shadow-exclude-reg x10+0-0`, for example, if the 10 pixels on the bottom of the screen should not have shadows painted on. diff --git a/src/c2.c b/src/c2.c index 6baf1337..730e5c5c 100644 --- a/src/c2.c +++ b/src/c2.c @@ -57,6 +57,93 @@ c2_parsed(session_t *ps, c2_lptr_t **pcondlst, const char *pattern, } } +/** + * Remove an element from a condition list. + */ +bool +c2_remove(c2_lptr_t **pcondlst, const uint index) { + u_int16_t cur = 0; + c2_lptr_t *rule = NULL; + c2_lptr_t *rule_prev = NULL; + c2_lptr_t *rule_next = NULL; + + for (rule = *pcondlst; rule; rule = rule->next, cur++) { + if (cur == index) { + rule_next = c2_free_lptr(rule); + if (rule_prev == NULL) // rule -> rule_next + *pcondlst = rule_next; + else if (rule_next == NULL) // rule -> NULL + rule_prev->next = rule_next; + else { // rule_prev -> rule -> rule_next + rule_prev->next = rule_next; + *pcondlst = rule_prev; + } + return true; + } + rule_prev = rule; + } + + return false; +} + +/** + * Remove an element from a condition list based on its representation. + */ +bool +c2_remove_reprd(c2_lptr_t **pcondlst, const char *tgt_rule, \ + const char *data_fmt) { + u_int16_t cur = 0; + c2_lptr_t *rule = NULL; + char *rule_repr = NULL; + + for (rule = *pcondlst; rule; rule = rule->next, cur++) { + rule_repr = data_fmt ? c2_dump_strd(rule, data_fmt) : c2_dump_str(rule->ptr); + if (!strcmp(rule_repr, tgt_rule)) { + free(rule_repr); + return c2_remove(pcondlst, cur); + } + free(rule_repr); + } + return false; + +} + +/** + * Replace an element from a condition list based on its representation. + */ +bool +c2_replace_reprd(session_t *ps, c2_lptr_t **pcondlst, const char *tgt_rule, \ + c2_lptr_t *new_rule_l, const char *data_fmt) { + c2_lptr_t *rule = NULL; + c2_lptr_t *next_rule = NULL; + c2_lptr_t *rule_prev = NULL; + char *rule_repr = NULL; + + for (rule = *pcondlst; rule; rule = rule->next) { + rule_repr = data_fmt ? c2_dump_strd(rule, data_fmt) : c2_dump_str(rule->ptr); + if (!strcmp(rule_repr, tgt_rule)) { + free(rule_repr); + + // Remove the targeted rule + next_rule = c2_free_lptr(rule); + + // Insert the new rule into a list + // Inject into existing pcondlst + new_rule_l->next = next_rule; + if (rule_prev) + rule_prev->next = new_rule_l; + else + *pcondlst = new_rule_l; + + return true; + } + + free(rule_repr); + rule_prev = rule; + } + return false; +} + #undef c2_error #define c2_error(format, ...) do { \ printf_err("Pattern \"%s\" pos %d: " format, pattern, offset, \ @@ -895,98 +982,135 @@ c2h_dump_str_type(const c2_l_t *pleaf) { return NULL; } +char * +c2_dump_strd(c2_lptr_t *l, const char *data_fmt) { + char *rule_repr = mstrcpy(""); + mstrextendf(&rule_repr, data_fmt, l->data); + + char *tmp = c2_dump_str(l->ptr); + mstrextendf(&rule_repr, ":%s", tmp); + + free(tmp); + return rule_repr; +} + /** * Dump a condition tree. */ -static void -c2_dump_raw(c2_ptr_t p) { +char * +c2_dump_str(c2_ptr_t p) { + char *_buf = mstrcpy(""); + char **buf = &_buf; + char *inner_str = NULL; + // For a branch if (p.isbranch) { const c2_b_t * const pbranch = p.b; - if (!pbranch) - return; + if (!pbranch) { + printf_errf("(): Branch of condition not set."); + goto fail; + } if (pbranch->neg) - putchar('!'); + mstrextend(buf, "!"); - printf("("); - c2_dump_raw(pbranch->opr1); + mstrextend(buf, "("); + inner_str = c2_dump_str(pbranch->opr1); + if (!inner_str) { + printf_errf("(): Left operand missing."); + goto fail; + } + mstrextend(buf, inner_str); + free(inner_str); switch (pbranch->op) { - case C2_B_OAND: printf(" && "); break; - case C2_B_OOR: printf(" || "); break; - case C2_B_OXOR: printf(" XOR "); break; + case C2_B_OAND: mstrextend(buf, " && "); break; + case C2_B_OOR: mstrextend(buf, " || "); break; + case C2_B_OXOR: mstrextend(buf, " XOR "); break; default: assert(0); break; } - c2_dump_raw(pbranch->opr2); - printf(")"); + inner_str = c2_dump_str(pbranch->opr2); + if (!inner_str) { + printf_errf("(): Right operand missing."); + goto fail; + } + mstrextend(buf, inner_str); + free(inner_str); + mstrextend(buf, ")"); } // For a leaf else { const c2_l_t * const pleaf = p.l; if (!pleaf) - return; + goto fail; if (C2_L_OEXISTS == pleaf->op && pleaf->neg) - putchar('!'); + mstrextend(buf, "!"); // Print target name, type, and format { - printf("%s", c2h_dump_str_tgt(pleaf)); + mstrextendf(buf, "%s", c2h_dump_str_tgt(pleaf)); if (pleaf->tgt_onframe) - putchar('@'); + mstrextend(buf, "@"); if (pleaf->index >= 0) - printf("[%d]", pleaf->index); - printf(":%d%s", pleaf->format, c2h_dump_str_type(pleaf)); + mstrextendf(buf, "[%d]", pleaf->index); + mstrextendf(buf, ":%d%s", pleaf->format, c2h_dump_str_type(pleaf)); } // Print operator - putchar(' '); + mstrextend(buf, " "); if (C2_L_OEXISTS != pleaf->op && pleaf->neg) - putchar('!'); + mstrextend(buf, "!"); switch (pleaf->match) { case C2_L_MEXACT: break; - case C2_L_MCONTAINS: putchar('*'); break; - case C2_L_MSTART: putchar('^'); break; - case C2_L_MPCRE: putchar('~'); break; - case C2_L_MWILDCARD: putchar('%'); break; + case C2_L_MCONTAINS: mstrextend(buf, "*"); break; + case C2_L_MSTART: mstrextend(buf, "^"); break; + case C2_L_MPCRE: mstrextend(buf, "~"); break; + case C2_L_MWILDCARD: mstrextend(buf, "%"); break; } if (pleaf->match_ignorecase) - putchar('?'); + mstrextend(buf, "?"); switch (pleaf->op) { case C2_L_OEXISTS: break; - case C2_L_OEQ: fputs("=", stdout); break; - case C2_L_OGT: fputs(">", stdout); break; - case C2_L_OGTEQ: fputs(">=", stdout); break; - case C2_L_OLT: fputs("<", stdout); break; - case C2_L_OLTEQ: fputs("<=", stdout); break; + case C2_L_OEQ: mstrextendf(buf, "=", stdout); break; + case C2_L_OGT: mstrextendf(buf, ">", stdout); break; + case C2_L_OGTEQ: mstrextendf(buf, ">=", stdout); break; + case C2_L_OLT: mstrextendf(buf, "<", stdout); break; + case C2_L_OLTEQ: mstrextendf(buf, "<=", stdout); break; } if (C2_L_OEXISTS == pleaf->op) - return; + goto success; // Print pattern - putchar(' '); + mstrextend(buf, " "); switch (pleaf->ptntype) { case C2_L_PTINT: - printf("%ld", pleaf->ptnint); + mstrextendf(buf, "%ld", pleaf->ptnint); break; case C2_L_PTSTRING: // TODO: Escape string before printing out? - printf("\"%s\"", pleaf->ptnstr); + mstrextendf(buf, "\"%s\"", pleaf->ptnstr); break; default: - assert(0); break; } } + goto success; + +success: +return *buf; + +fail: +free(*buf); +return NULL; } /** diff --git a/src/c2.h b/src/c2.h index e9aaf8b5..1e0886b9 100644 --- a/src/c2.h +++ b/src/c2.h @@ -29,18 +29,6 @@ #define C2_MAX_LEVELS 10 -typedef struct _c2_b c2_b_t; -typedef struct _c2_l c2_l_t; - -/// Pointer to a condition tree. -typedef struct { - bool isbranch : 1; - union { - c2_b_t *b; - c2_l_t *l; - }; -} c2_ptr_t; - /// Initializer for c2_ptr_t. #define C2_PTR_INIT { \ .isbranch = false, \ @@ -165,13 +153,6 @@ struct _c2_l { const static c2_l_t leaf_def = C2_L_INIT; -/// Linked list type of conditions. -struct _c2_lptr { - c2_ptr_t ptr; - void *data; - struct _c2_lptr *next; -}; - /// Initializer for c2_lptr_t. #define C2_LPTR_INIT { \ .ptr = C2_PTR_INIT, \ @@ -295,6 +276,9 @@ c2h_b_opcmp(c2_b_op_t op1, c2_b_op_t op2) { return c2h_b_opp(op1) - c2h_b_opp(op2); } +bool +c2_removed(c2_lptr_t **pcondlst, const uint index); + static int c2_parse_grp(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult, int level); @@ -333,16 +317,14 @@ c2h_dump_str_tgt(const c2_l_t *pleaf); static const char * c2h_dump_str_type(const c2_l_t *pleaf); -static void -c2_dump_raw(c2_ptr_t p); - /** - * Wrapper of c2_dump_raw(). + * Wrapper of c2_dump_str(). */ static inline void c2_dump(c2_ptr_t p) { - c2_dump_raw(p); - printf("\n"); + char *c2_repr = c2_dump_str(p); + printf("%s\n", c2_repr); + free(c2_repr); fflush(stdout); } diff --git a/src/common.h b/src/common.h index 2b75ff7a..b1daec70 100644 --- a/src/common.h +++ b/src/common.h @@ -538,6 +538,24 @@ struct _timeout_t; struct _win; +typedef struct _c2_b c2_b_t; +typedef struct _c2_l c2_l_t; + +/// Pointer to a condition tree. +typedef struct { + bool isbranch : 1; + union { + c2_b_t *b; + c2_l_t *l; + }; +} c2_ptr_t; + +/// Linked list type of conditions. +struct _c2_lptr { + c2_ptr_t ptr; + void *data; + struct _c2_lptr *next; +}; typedef struct _c2_lptr c2_lptr_t; /// Structure representing all options. @@ -718,6 +736,8 @@ typedef struct _options_t { c2_lptr_t *invert_color_list; /// Rules to change window opacity. c2_lptr_t *opacity_rules; + /// Rules to change window dim. + c2_lptr_t *dim_rules; // === Focus related === /// Consider windows of specific types to be always focused. @@ -1154,6 +1174,7 @@ typedef struct _win { const c2_lptr_t *cache_ivclst; const c2_lptr_t *cache_bbblst; const c2_lptr_t *cache_oparule; + const c2_lptr_t *cache_dimrule; const c2_lptr_t *cache_pblst; const c2_lptr_t *cache_uipblst; @@ -1214,6 +1235,7 @@ typedef struct _win { // Dim-related members /// Whether the window is to be dimmed. bool dim; + double permanent_dim; /// Whether to invert window color. bool invert_color; @@ -1351,7 +1373,7 @@ allocchk_(const char *func_name, void *ptr) { /// @brief Wrapper of calloc(). #define ccalloc(nmemb, type) ((type *) allocchk(calloc((nmemb), sizeof(type)))) -/// @brief Wrapper of ealloc(). +/// @brief Wrapper of realloc(). #define crealloc(ptr, nmemb, type) ((type *) allocchk(realloc((ptr), (nmemb) * sizeof(type)))) /// @brief Zero out the given memory block. @@ -1361,6 +1383,17 @@ allocchk_(const char *func_name, void *ptr) { /// convenience. #define cmemzero_one(ptr) cmemzero((ptr), sizeof(*(ptr))) +/// @brief Wrapper of vasprintf(). +#define cvasprintf(strp, fmt, args) do { \ + if (vasprintf((strp), (fmt), (args)) == -1) \ + allocchk(NULL); \ +} while(0) + +/// @brief Wrapper of asprintf(). +#define casprintf(strp, fmt, ...) do { \ + if (asprintf((strp), (fmt), ## __VA_ARGS__) == -1) \ + allocchk(NULL); \ +} while(0) /** * Return whether a struct timeval value is empty. */ @@ -2474,11 +2507,24 @@ win_set_focused_force(session_t *ps, win *w, switch_t val); void win_set_invert_color_force(session_t *ps, win *w, switch_t val); +void +win_set_permanent_dim_force(session_t *ps, win *w, double val); + void opts_init_track_focus(session_t *ps); void opts_set_no_fading_openclose(session_t *ps, bool newval); + +bool +parse_rule_dim(session_t *ps, c2_lptr_t **dim_rules, const char *src); + +void +win_update_dim_rule(session_t *ps, win *w); + +void +win_recheck_client(session_t *ps, win *w); + //!@} #endif @@ -2493,6 +2539,20 @@ c2_parsed(session_t *ps, c2_lptr_t **pcondlst, const char *pattern, #define c2_parse(ps, pcondlst, pattern) c2_parsed((ps), (pcondlst), (pattern), NULL) +bool +c2_remove_reprd(c2_lptr_t **pcondlst, const char *tgt_rule, \ + const char *data_fmt); + +#define c2_remove_repr(pcondlst, tgt_rule, data_fmt) \ + c2_remove_reprd((pcondlst), (tgt_rule), NULL) + +bool +c2_replace_reprd(session_t *ps, c2_lptr_t **pcondlst, const char *tgt_rule, \ + c2_lptr_t *new_rule_l, const char *data_fmt); + +#define c2_replace_repr(ps, pcondlst, tgt_rule, new_rule, data_fmt) \ + c2_replace_reprd((ps), (pcondlst), (tgt_rule), (new_rule), NULL) + c2_lptr_t * c2_free_lptr(c2_lptr_t *lp); @@ -2502,6 +2562,16 @@ c2_matchd(session_t *ps, win *w, const c2_lptr_t *condlst, #define c2_match(ps, w, condlst, cache) c2_matchd((ps), (w), (condlst), \ (cache), NULL) + +char * +c2_dump_str(c2_ptr_t p) __attribute__ ((warn_unused_result)); + +char * +c2_dump_strd(c2_lptr_t *l, const char *data_fmt) __attribute__ ((warn_unused_result)); + +char * +c2_dump_str_data(c2_lptr_t *l, const char *data_fmt) __attribute__ ((warn_unused_result)); + #endif ///@} @@ -2563,4 +2633,25 @@ hexdump(const char *data, int len) { fflush(stdout); } +/** + * @brief Concatenate a formatted string to another. + * + * @param buf pointer to buffer + * @param fmt format string + */ +static inline void +mstrextendf(char **dest, const char *fmt, ...) { + char *str = NULL; + va_list args; + + // Format into str + va_start(args, fmt); + cvasprintf(&str, fmt, args); + va_end(args); + + // Concatenate + mstrextend(dest, str); + free(str); +} + #endif diff --git a/src/compton.c b/src/compton.c index 92935a3d..85bbd3aa 100644 --- a/src/compton.c +++ b/src/compton.c @@ -1689,11 +1689,15 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, free_picture(ps, &pict); // Dimming the window if needed + double dim_opacity = w->permanent_dim; if (w->dim) { - double dim_opacity = ps->o.inactive_dim; + double inactive_dim = ps->o.inactive_dim; if (!ps->o.inactive_dim_fixed) - dim_opacity *= get_opacity_percent(w); + inactive_dim *= get_opacity_percent(w); + dim_opacity += inactive_dim; + } + if (dim_opacity) { switch (ps->o.backend) { case BKEND_XRENDER: case BKEND_XR_GLX_HYBRID: @@ -2390,7 +2394,7 @@ calc_opacity(session_t *ps, win *w) { } /** - * Determine whether a window is to be dimmed. + * Determine whether a window is to be dimmed due to inactivity. */ static void calc_dim(session_t *ps, win *w) { @@ -2598,6 +2602,24 @@ win_determine_blur_background(session_t *ps, win *w) { win_set_blur_background(ps, w, blur_background_new); } +/** + * Update window dim according to dim rules. + */ +void +win_update_dim_rule(session_t *ps, win *w) { + if (IsViewable != w->a.map_state) + return; + +#ifdef CONFIG_C2 + void *val = NULL; + if (c2_matchd(ps, w, ps->o.dim_rules, &w->cache_dimrule, &val)) { + w->permanent_dim = ((double) (long) val) / 100.0; + } else { + w->permanent_dim = 0.0; + } +#endif +} + /** * Update window opacity according to opacity rules. */ @@ -2637,6 +2659,8 @@ win_on_wtype_change(session_t *ps, win *w) { win_determine_invert_color(ps, w); if (ps->o.opacity_rules) win_update_opacity_rule(ps, w); + if (ps->o.dim_rules) + win_update_dim_rule(ps, w); } /** @@ -2656,6 +2680,8 @@ win_on_factor_change(session_t *ps, win *w) { win_determine_blur_background(ps, w); if (ps->o.opacity_rules) win_update_opacity_rule(ps, w); + if (ps->o.dim_rules) + win_update_dim_rule(ps, w); if (IsViewable == w->a.map_state && ps->o.paint_blacklist) w->paint_excluded = win_match(ps, w, ps->o.paint_blacklist, &w->cache_pblst); @@ -2803,7 +2829,7 @@ win_unmark_client(session_t *ps, win *w) { * @param ps current session * @param w struct _win of the parent window */ -static void +void win_recheck_client(session_t *ps, win *w) { // Initialize wmwin to false w->wmwin = false; @@ -2911,6 +2937,7 @@ add_win(session_t *ps, Window id, Window prev) { .prop_shadow = -1, .dim = false, + .permanent_dim = 0.0, .invert_color = false, .invert_color_force = UNSET, @@ -4727,11 +4754,23 @@ usage(int ret) { " inverted color. Resource-hogging, and is not well tested.\n" "\n" "--opacity-rule opacity:condition\n" +#undef WARNING +#ifndef CONFIG_C2 +#define WARNING WARNING_DISABLED +#else +#define WARNING +#endif " Specify a list of opacity rules, in the format \"PERCENT:PATTERN\",\n" " like \'50:name *= \"Firefox\"'. compton-trans is recommended over\n" " this. Note we do not distinguish 100% and unset, and we don't make\n" " any guarantee about possible conflicts with other programs that set\n" " _NET_WM_WINDOW_OPACITY on frame or client windows.\n" + " " WARNING "\n" + "\n" + "--dim-rule value:condition\n" + " Specify a list of dim rules, in the format \"VALUE:PATTERN\",\n" + " like '50:!class_g *= \"URxvt\"', where `VALUE` is in the 0.0 - 1.0\n" + " range (0.0 means no dim)." WARNING "\n" "\n" "--shadow-exclude-reg geometry\n" " Specify a X geometry that describes the region in which shadow\n" @@ -5263,6 +5302,42 @@ parse_geometry(session_t *ps, const char *src, geometry_t *dest) { return true; } +/** + * Parse a list of dim rules. + */ +bool +parse_rule_dim(session_t *ps, c2_lptr_t **dim_rules, const char *src) { +#ifdef CONFIG_C2 + // Find dim value + char *endptr = NULL; + long val = strtol(src, &endptr, 0); + if (!endptr || endptr == src) { + printf_errf("(\"%s\"): No dim value specified?", src); + return false; + } + if(val > 100 || val < 0) { + printf_errf("(\"%s\"): Dim value %ld invalid.", src, val); + return false; + } + + // Skip over spaces + while (*endptr && isspace(*endptr)) + ++endptr; + if (':' != *endptr) { + printf_errf("(\"%s\"): Dim value terminator not found.", src); + return false; + } + ++endptr; + + // Parse pattern + // I hope 1-100 is acceptable for (void *) + return c2_parsed(ps, dim_rules, endptr, (void *) val); +#else + printf_errf("(\"%s\"): Condition support not compiled in.", src); + return false; +#endif +} + /** * Parse a list of opacity rules. */ @@ -5426,6 +5501,27 @@ parse_cfg_condlst_opct(session_t *ps, const config_t *pcfg, const char *name) { } } +/** + * Parse an dim rule list in configuration file. + */ +static inline void +parse_cfg_condlst_dim(session_t *ps, const config_t *pcfg, const char *name) { + config_setting_t *setting = config_lookup(pcfg, name); + if (setting) { + // Parse an array of options + if (config_setting_is_array(setting)) { + int i = config_setting_length(setting); + while (i--) + if (!parse_rule_dim(ps, &ps->o.dim_rules, config_setting_get_string_elem(setting, i))) + exit(1); + } + // Treat it as a single pattern if it's a string + else if (CONFIG_TYPE_STRING == config_setting_type(setting)) { + parse_rule_dim(ps, &ps->o.dim_rules, config_setting_get_string(setting)); + } + } +} + /** * Parse a configuration file from default location. */ @@ -5608,6 +5704,8 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { parse_cfg_condlst(ps, &cfg, &ps->o.blur_background_blacklist, "blur-background-exclude"); // --opacity-rule parse_cfg_condlst_opct(ps, &cfg, "opacity-rule"); + // --dim-rule + parse_cfg_condlst_dim(ps, &cfg, "dim-rule"); // --unredir-if-possible-exclude parse_cfg_condlst(ps, &cfg, &ps->o.unredir_if_possible_blacklist, "unredir-if-possible-exclude"); // --blur-background @@ -5756,6 +5854,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "version", no_argument, NULL, 318 }, { "no-x-selection", no_argument, NULL, 319 }, { "no-name-pixmap", no_argument, NULL, 320 }, + { "dim-rule", required_argument, NULL, 321 }, { "reredir-on-root-change", no_argument, NULL, 731 }, { "glx-reinit-on-root-change", no_argument, NULL, 732 }, // Must terminate with a NULL entry @@ -6026,6 +6125,11 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { case 317: ps->o.glx_fshader_win_str = mstrcpy(optarg); break; + case 321: + // --dim-rule + if (!parse_rule_dim(ps, &ps->o.dim_rules, optarg)) + exit(1); + break; P_CASEBOOL(319, no_x_selection); P_CASEBOOL(731, reredir_on_root_change); P_CASEBOOL(732, glx_reinit_on_root_change); @@ -7036,6 +7140,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .inactive_dim_fixed = false, .invert_color_list = NULL, .opacity_rules = NULL, + .dim_rules = NULL, .wintype_focus = { false }, .use_ewmh_active_win = false, @@ -7520,6 +7625,7 @@ session_destroy(session_t *ps) { free_wincondlst(&ps->o.invert_color_list); free_wincondlst(&ps->o.blur_background_blacklist); free_wincondlst(&ps->o.opacity_rules); + free_wincondlst(&ps->o.dim_rules); free_wincondlst(&ps->o.paint_blacklist); free_wincondlst(&ps->o.unredir_if_possible_blacklist); #endif diff --git a/src/compton.h b/src/compton.h index 0e27a759..1a6e7880 100644 --- a/src/compton.h +++ b/src/compton.h @@ -903,9 +903,6 @@ win_mark_client(session_t *ps, win *w, Window client); static void win_unmark_client(session_t *ps, win *w); -static void -win_recheck_client(session_t *ps, win *w); - static bool add_win(session_t *ps, Window id, Window prev); diff --git a/src/dbus.c b/src/dbus.c index 2ea1b863..48e24d09 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -383,6 +383,45 @@ cdbus_apdarg_wids(session_t *ps, DBusMessage *msg, const void *data) { } ///@} +/** + * Callback to append rules to a message. + */ +static bool +cdbus_apdarg_dim_rules(session_t *ps, DBusMessage *msg, const void *data) { + cdbus_string_t *arr = NULL; + size_t count = 0; + const char *data_fmt ="%ld"; + bool success = true; + + // Build the array + for (c2_lptr_t *rule = ps->o.dim_rules; rule; rule = rule->next) { + arr = crealloc(arr, ++count, cdbus_string_t); + arr[count - 1] = c2_dump_strd(rule, data_fmt); + } + + // Append arguments + if (!dbus_message_append_args(msg, DBUS_TYPE_ARRAY, CDBUS_TYPE_STRING, + &arr, count, DBUS_TYPE_INVALID)) { + printf_errf("(): Failed to append argument."); + goto error; + } + + goto end; + +error: + success = false; + goto end; + +end: + while(count) + free(arr[--count]); + free(arr); + return success; + +} +///@} + + /** * Send a D-Bus signal. * @@ -586,6 +625,9 @@ cdbus_process(session_t *ps, DBusMessage *msg) { else if (cdbus_m_ismethod("opts_set")) { success = cdbus_process_opts_set(ps, msg); } + else if (cdbus_m_ismethod("dim_rule_update")) { + success = cdbus_process_dim_rule_update(ps, msg); + } #undef cdbus_m_ismethod else if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Introspectable", "Introspect")) { @@ -638,6 +680,16 @@ cdbus_process(session_t *ps, DBusMessage *msg) { dbus_message_unref(msg); } +/** + * Process a list_win D-Bus request. + */ +static bool +cdbus_process_list_dim_rule(session_t *ps, DBusMessage *msg) { + cdbus_reply(ps, msg, cdbus_apdarg_dim_rules, NULL); + + return true; +} + /** * Process a list_win D-Bus request. */ @@ -722,6 +774,8 @@ cdbus_process_win_get(session_t *ps, DBusMessage *msg) { cdbus_m_win_get_do(opacity_prop_client, cdbus_reply_uint32); cdbus_m_win_get_do(opacity_set, cdbus_reply_uint32); + cdbus_m_win_get_do(permanent_dim, cdbus_reply_double); + cdbus_m_win_get_do(frame_opacity, cdbus_reply_double); if (!strcmp("left_width", target)) { cdbus_reply_uint32(ps, msg, w->frame_extents.left); @@ -819,6 +873,7 @@ cdbus_process_win_set(session_t *ps, DBusMessage *msg) { win_set_invert_color_force(ps, w, val); goto cdbus_process_win_set_success; } + #undef cdbus_m_win_set_do printf_errf("(): " CDBUS_ERROR_BADTGT_S, target); @@ -937,6 +992,9 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg) { cdbus_reply_string(ps, msg, BACKEND_STRS[ps->o.backend]); return true; } + if (!strcmp("dim-rule", target)) { + return cdbus_process_list_dim_rule(ps, msg); + } cdbus_m_opts_get_do(dbe, cdbus_reply_bool); cdbus_m_opts_get_do(vsync_aggressive, cdbus_reply_bool); @@ -985,6 +1043,109 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg) { return true; } +static bool +cdbus_process_dim_rule_update(session_t *ps, DBusMessage *msg) { + char *action = NULL; + char *tgt_rule = NULL; + char *new_rule = NULL; + const char *data_fmt = "%ld"; + bool track_wdata = ps->o.track_wdata; + + if (!cdbus_msg_get_arg(msg, 0, DBUS_TYPE_STRING, &action)) { + cdbus_reply_err(ps, msg, CDBUS_ERROR_BADARG, CDBUS_ERROR_BADARG_S, 0, \ + "You need to provide an action to perform."); + return false; + } + +#define cdbus_m_rule_get(arg_id, rule) ({ \ + bool ret = cdbus_msg_get_arg(msg, (arg_id), DBUS_TYPE_STRING, rule); \ + if (!ret) \ + cdbus_reply_err(ps, msg, CDBUS_ERROR_BADARG, CDBUS_ERROR_BADARG_S, \ + (arg_id), "Must be a single rule in string format."); \ + ret; }) + +#define cdbus_m_rule_parse(arg_id, rule, dest) ({ \ + bool ret = parse_rule_dim(ps, (dest), (rule)); \ + if (!ret) \ + cdbus_reply_err(ps, msg, CDBUS_ERROR_BADARG, CDBUS_ERROR_BADARG_S, \ + (arg_id), "Failed to parse rule. See compton output."); \ + ret; }) + +#define cdbus_m_rule_remove(arg_id, dest, tgt_rule) ({ \ + bool ret = c2_remove_reprd((dest), (tgt_rule), data_fmt); \ + if (!ret) \ + cdbus_reply_err(ps, msg, CDBUS_ERROR_MISRUL, CDBUS_ERROR_MISRUL_S, \ + (arg_id), tgt_rule); \ + ret; }) + +#define cdbus_m_rule_replace(arg_id, tgt_rule, new_rule_l) ({ \ + bool ret = c2_replace_reprd(ps, &ps->o.dim_rules, (tgt_rule), \ + (new_rule_l), data_fmt); \ + if (!ret) \ + cdbus_reply_err(ps, msg, CDBUS_ERROR_MISRUL, CDBUS_ERROR_MISRUL_S, \ + (arg_id), (tgt_rule)); \ + ret; }) + + if (!strcmp("insert", action)) { + if (!cdbus_m_rule_get(1, &new_rule) || \ + !cdbus_m_rule_parse(1, new_rule, &ps->o.dim_rules)) + return false; + } + else if (!strcmp("remove", action)) { + if (!cdbus_m_rule_get(1, &tgt_rule) || \ + !cdbus_m_rule_remove(1, &ps->o.dim_rules, tgt_rule)) + return false; + } + else if (!strcmp("replace", action)) { + bool success = true; + c2_lptr_t *new_rule_l = NULL; + if (!cdbus_m_rule_get(1, &tgt_rule) || \ + !cdbus_m_rule_get(2, &new_rule) || \ + !cdbus_m_rule_parse(2, new_rule, &new_rule_l) || \ + !cdbus_m_rule_replace(2, tgt_rule, new_rule_l)) + success = false; + if (new_rule_l) + c2_free_lptr(new_rule_l); + return success; + } + else { + cdbus_reply_err(ps, msg, CDBUS_ERROR_BADARG, CDBUS_ERROR_BADARG_S, 1, \ + "Unsupported action. Use one of [ insert | replace | remove ]."); + return false; + } + + // Flush cache and update dim rules on windows. + for (win *w = ps->list; w; w = w->next) { + // If wdata tracking was enabled by `parse_rule_dim` just now it means + // that the rules require wdata. Therefore we must recheck all the added + // clients so that this data is available to `win_update_dim_rule`. + if (!track_wdata && ps->o.track_wdata) { + win_recheck_client(ps, w); + } + w->cache_dimrule = NULL; + win_update_dim_rule(ps, w); + } + + // Reply with a newly inserted/updated rule so that user can reference it. + if (!dbus_message_get_no_reply(msg)) { + if (new_rule) { + char *rule_repr = c2_dump_strd(ps->o.dim_rules, data_fmt); + cdbus_reply_string(ps, msg, rule_repr); + free(rule_repr); + } + else { + cdbus_reply_bool(ps, msg, true); + } + } + + return true; + +#undef cdbus_m_rule_get +#undef cdbus_m_rule_parse +#undef cdbus_m_rule_remove +#undef cdbus_m_rule_replace +} + /** * Process a opts_set D-Bus request. */ diff --git a/src/dbus.h b/src/dbus.h index a806c2de..5c5e492f 100644 --- a/src/dbus.h +++ b/src/dbus.h @@ -28,6 +28,9 @@ #define CDBUS_ERROR_BADWIN_S "Requested window %#010lx not found." #define CDBUS_ERROR_BADTGT CDBUS_ERROR_PREFIX ".bad_target" #define CDBUS_ERROR_BADTGT_S "Target \"%s\" not found." +#define CDBUS_ERROR_MISRUL CDBUS_ERROR_PREFIX ".rule_missing" +#define CDBUS_ERROR_MISRUL_S "Rule of argument %d not found: \"%s\"." + #define CDBUS_ERROR_FORBIDDEN CDBUS_ERROR_PREFIX ".forbidden" #define CDBUS_ERROR_FORBIDDEN_S "Incorrect password, access denied." #define CDBUS_ERROR_CUSTOM CDBUS_ERROR_PREFIX ".custom" @@ -42,6 +45,12 @@ typedef uint16_t cdbus_enum_t; #define CDBUS_TYPE_ENUM DBUS_TYPE_UINT16 #define CDBUS_TYPE_ENUM_STR DBUS_TYPE_UINT16_AS_STRING +typedef double cdbus_double_t; +#define CDBUS_TYPE_DOUBLE DBUS_TYPE_DOUBLE + +typedef char *cdbus_string_t; +#define CDBUS_TYPE_STRING DBUS_TYPE_STRING + static dbus_bool_t cdbus_callback_add_timeout(DBusTimeout *timeout, void *data); @@ -246,6 +255,9 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg); static bool cdbus_process_opts_set(session_t *ps, DBusMessage *msg); +static bool +cdbus_process_dim_rule_update(session_t *ps, DBusMessage *msg); + static bool cdbus_process_introspect(session_t *ps, DBusMessage *msg);