Skip to content

Instantly share code, notes, and snippets.

@richardgv
Last active December 10, 2015 09:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save richardgv/4413196 to your computer and use it in GitHub Desktop.
Save richardgv/4413196 to your computer and use it in GitHub Desktop.
chjj/compton #56: My work in progress (NOT USABLE right now!)
diff --git a/.gitignore b/.gitignore
index 475cacb..a11d54d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,4 +38,6 @@ oprofile_data/
compton.plist
callgrind.out.*
man/*.html
+man/*.1
doxygen/
+.clang_complete
diff --git a/Makefile b/Makefile
index 8143a6a..cb06bcd 100644
--- a/Makefile
+++ b/Makefile
@@ -55,10 +55,10 @@ ifeq "$(NO_DBUS)" ""
endif
# ==== C2 ====
-# ifeq "$(NO_C2)" ""
-# CFG += -DCONFIG_C2
-# OBJS += c2.o
-# endif
+ifeq "$(NO_C2)" ""
+ CFG += -DCONFIG_C2
+ OBJS += c2.o
+endif
# === Version string ===
COMPTON_VERSION ?= git-$(shell git describe --always --dirty)-$(shell git log -1 --date=short --pretty=format:%cd)
@@ -78,6 +78,9 @@ MANPAGES_HTML = $(addsuffix .html,$(MANPAGES))
# === Recipes ===
.DEFAULT_GOAL := compton
+.clang_complete: Makefile
+ @(for i in $(filter-out -O% -DNDEBUG, $(CFLAGS) $(INCS)); do echo "$$i"; done) > $@
+
%.o: src/%.c src/%.h src/common.h
$(CC) $(CFLAGS) $(INCS) -c src/$*.c
@@ -105,7 +108,7 @@ uninstall:
@rm -f "$(DESTDIR)$(MANDIR)/compton-trans.1"
clean:
- @rm -f $(OBJS) compton $(MANPAGES) $(MANPAGES_HTML)
+ @rm -f $(OBJS) compton $(MANPAGES) $(MANPAGES_HTML) .clang_complete
version:
@echo "$(COMPTON_VERSION)"
diff --git a/compton.sample.conf b/compton.sample.conf
index 5f4fe4f..0098ece 100644
--- a/compton.sample.conf
+++ b/compton.sample.conf
@@ -10,7 +10,7 @@ shadow-offset-y = -7;
# shadow-red = 0.0;
# shadow-green = 0.0;
# shadow-blue = 0.0;
-shadow-exclude = [ "n:e:Notification", "g:e:Conky" ];
+shadow-exclude = [ "name = 'Notification'", "class_g = 'Conky'" ];
# shadow-exclude = "n:e:Notification";
shadow-ignore-shaped = false;
diff --git a/dbus-examples/cdbus-driver.sh b/dbus-examples/cdbus-driver.sh
index 9582fc7..bd01b3f 100755
--- a/dbus-examples/cdbus-driver.sh
+++ b/dbus-examples/cdbus-driver.sh
@@ -26,7 +26,7 @@ type_enum='uint16'
dbus-send --print-reply --dest="$service" "$object" "${interface}.list_win"
# Get window ID of currently focused window
-focused=$(dbus-send --print-reply --dest="$service" "$object" "${interface}.find_win" string:focused | $SED -n 's/^[[:space:]]*'${type_win}'\s*\([[:digit:]]*\).*/\1/p')
+focused=$(dbus-send --print-reply --dest="$service" "$object" "${interface}.find_win" string:focused | $SED -n 's/^[[:space:]]*'${type_win}'[[:space:]]*\([[:digit:]]*\).*/\1/p')
if [ -n "$focused" ]; then
# Get invert_color_force property of the window
diff --git a/man/compton.1.asciidoc b/man/compton.1.asciidoc
index 10d3901..f7f2002 100644
--- a/man/compton.1.asciidoc
+++ b/man/compton.1.asciidoc
@@ -180,8 +180,75 @@ OPTIONS
FORMAT OF CONDITIONS
--------------------
+Some options accept a condition string to match certain windows. A condition string is formed by one or more conditions, joined by logical operators.
-*--shadow-exclude* and *--focus-exclude* uses a condition string to blacklist certain windows. The format of the condition string is:
+A condition with "exists" operator looks like this:
+
+ <NEGATION> <TARGET> <CLIENT/FRAME> [<INDEX>] : <FORMAT> <TYPE>
+
+With equals operator it looks like:
+
+ <NEGATION> <TARGET> <CLIENT/FRAME> [<INDEX>] : <FORMAT> <TYPE> <NEGATION> <OP QUALIFIER> <MATCH TYPE> = <PATTERN>
+
+With greater-than/less-than operators it looks like:
+
+ <NEGATION> <TARGET> <CLIENT/FRAME> [<INDEX>] : <FORMAT> <TYPE> <NEGATION> <OPERATOR> <PATTERN>
+
+'NEGATION' (optional) is one or more exclamation marks;
+
+'TARGET' is either a predefined target name, or the name of a window property to match. Supported predefined targets are `id`, `override_redirect`, `focused`, `wmwin`, `client` (ID of client window), `window_type` (window type in string), `leader` (ID of window leader), `name`, `class_g` (= `WM_CLASS[1]`), `class_i` (= `WM_CLASS[0]`), and `role`.
+
+'CLIENT/FRAME' is a single `@` if the window attribute should be be looked up on client window, nothing if on frame window;
+
+'INDEX' (optional) is the index number of the property to look up. For example, `[2]` means look at the third value in the property. Do not specify it for predefined targets.
+
+'FORMAT' (optional) specifies the format of the property, 8, 16, or 32. On absence we use format X reports. Do not specify it for predefined or string targets.
+
+'TYPE' is a single character representing the type of the property to match for: `c` for 'CARDINAL', `a` for 'ATOM', `w` for 'WINDOW', `d` for 'DRAWABLE', `s` for 'STRING' (and any other string types, such as 'UTF8_STRING'). Do not specify it for predefined targets.
+
+'OP QUALIFIER' (optional), applicable only for equals operator, could be `?` (ignore-case).
+
+'MATCH TYPE' (optional), applicable only for equals operator, could be nothing (exact match), `*` (match anywhere), `^` (match from start), `%` (wildcard), or `~` (PCRE regular expression).
+
+'OPERATOR' is one of `=` (equals), `<`, `>`, `<=`, `=>`, or nothing (exists). Exists operator checks whether a property exists on a window (but for predefined targets, exists means != 0 then).
+
+'PATTERN' is either an integer or a string enclosed by single or double quotes. Python-3-style escape sequences and raw string are supported in the string format.
+
+Supported logical operators are `&&` (and) and `||` (or). `&&` has higher precedence than `||`, left-to-right associativity. Use parentheses to change precedence.
+
+Examples:
+
+ # If the window is focused
+ focused
+ focused = 1
+ # If the window is not override-redirected
+ !override_redirect
+ override_redirect = 1
+ # If the window is a menu
+ window_type *= "menu"
+ _NET_WM_WINDOW_TYPE@:a *= "MENU"
+ # If the window name contains "Firefox", ignore case
+ name *?= "Firefox"
+ _NET_WM_NAME@:s *?= "Firefox"
+ # If the window name ends with "Firefox"
+ name %= "*Firefox"
+ name ~= "Firefox$"
+ # If the window has a property _COMPTON_SHADOW with value 0, type CARDINAL,
+ # format 32, value 0, on its frame window
+ _COMPTON_SHADOW:32c = 0
+ # If the third value of _NET_FRAME_EXTENTS is less than 20, or there's no
+ # _NET_FRAME_EXTENTS property on client window
+ _NET_FRAME_EXTENTS@[2]:32c < 20 || !_NET_FRAME_EXTENTS@:32c
+ # The pattern here will be parsed as "dd4"
+ name = "\x64\x64\o64"
+ # The pattern here will be parsed as "\x64\x64\x64"
+ name = r"\x64\x64\o64"
+
+
+LEGACY FORMAT OF CONDITIONS
+---------------------------
+
+This is the old condition format we once used. Support of this format might be removed in the future.
condition = TARGET:TYPE[FLAGS]:PATTERN
@@ -239,7 +306,7 @@ $ compton -c --shadow-red 1 --shadow-green 1 --shadow-blue 1
* Avoid drawing shadows on wbar window:
+
------------
-$ compton -c --shadow-exclude 'g:e:wbar'
+$ compton -c --shadow-exclude 'class_g = "wbar"'
------------
* Enable OpenGL vsync:
diff --git a/src/c2.c b/src/c2.c
new file mode 100644
index 0000000..a63a274
--- /dev/null
+++ b/src/c2.c
@@ -0,0 +1,1334 @@
+/*
+ * Compton - a compositor for X11
+ *
+ * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
+ *
+ * Copyright (c) 2011-2013, Christopher Jeffrey
+ * See LICENSE for more information.
+ *
+ */
+
+#include "c2.h"
+
+#define c2_error(format, ...) { \
+ printf_err("Pattern \"%s\" pos %d: " format, pattern, offset, \
+ ## __VA_ARGS__); \
+ return NULL; }
+
+#define mstrncmp(s1, s2) strncmp(s1, s2, strlen(s1))
+
+/**
+ * Parse a condition string.
+ */
+c2_lptr_t *
+c2_parse(session_t *ps, c2_lptr_t **pcondlst, const char *pattern) {
+ if (!pattern)
+ return NULL;
+
+ // Parse the pattern
+ c2_ptr_t result = C2_PTR_INIT;
+ int offset = -1;
+
+ if (strlen(pattern) >= 2 && ':' == pattern[1])
+ offset = c2_parse_legacy(ps, pattern, 0, &result);
+ else
+ offset = c2_parse_grp(ps, pattern, 0, &result, 0);
+
+ if (offset < 0)
+ return NULL;
+
+ // Insert to pcondlst
+ {
+ const static c2_lptr_t lptr_def = C2_LPTR_INIT;
+ c2_lptr_t *plptr = malloc(sizeof(c2_lptr_t));
+ if (!plptr)
+ printf_errfq(2, "(): Failed to allocate memory for new condition linked"
+ " list element.");
+ memcpy(plptr, &lptr_def, sizeof(c2_lptr_t));
+ plptr->ptr = result;
+ if (pcondlst) {
+ plptr->next = *pcondlst;
+ *pcondlst = plptr;
+ }
+
+#ifdef DEBUG_C2
+ printf_dbgf("(\"%s\"): ", pattern);
+ c2_dump(plptr->ptr);
+#endif
+
+ return plptr;
+ }
+}
+
+#undef c2_error
+#define c2_error(format, ...) do { \
+ printf_err("Pattern \"%s\" pos %d: " format, pattern, offset, \
+ ## __VA_ARGS__); \
+ return -1; \
+} while(0)
+
+#define C2H_SKIP_SPACES() { while (isspace(pattern[offset])) ++offset; }
+
+/**
+ * Parse a group in condition string.
+ *
+ * @return offset of next character in string
+ */
+static int
+c2_parse_grp(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult, int level) {
+ // Check for recursion levels
+ if (level > C2_MAX_LEVELS)
+ c2_error("Exceeded maximum recursion levels.");
+
+ if (!pattern)
+ return -1;
+
+ // Expected end character
+ const char endchar = (offset ? ')': '\0');
+
+#undef c2_error
+#define c2_error(format, ...) do { \
+ printf_err("Pattern \"%s\" pos %d: " format, pattern, offset, \
+ ## __VA_ARGS__); \
+ goto c2_parse_grp_fail; \
+} while(0)
+
+ // We use a system that a maximum of 2 elements are kept. When we find
+ // the third element, we combine the elements according to operator
+ // precedence. This design limits operators to have at most two-levels
+ // of precedence and fixed left-to-right associativity.
+
+ // For storing branch operators. ops[0] is actually unused
+ c2_b_op_t ops[3] = { };
+ // For storing elements
+ c2_ptr_t eles[2] = { C2_PTR_INIT, C2_PTR_INIT };
+ // Index of next free element slot in eles
+ int elei = 0;
+ // Pointer to the position of next element
+ c2_ptr_t *pele = eles;
+ // Negation flag of next operator
+ bool neg = false;
+ // Whether we are expecting another element
+ bool next_expected = false;
+
+ // Parse the pattern character-by-character
+ for (; pattern[offset]; ++offset) {
+ // Jump over spaces
+ if (isspace(pattern[offset]))
+ continue;
+
+ // Handle end of group
+ if (')' == pattern[offset])
+ break;
+
+ // Handle "!"
+ if ('!' == pattern[offset]) {
+ if (elei && !ops[elei])
+ c2_error("Negation mark can't come right after an expression.");
+
+ next_expected = true;
+ neg = !neg;
+ continue;
+ }
+
+ // Handle AND and OR
+ if ('&' == pattern[offset] || '|' == pattern[offset]) {
+ if (!elei)
+ c2_error("Operator found before first expression in group.");
+
+ if (ops[elei])
+ c2_error("More than one operator found.");
+
+ next_expected = true;
+ if (!mstrncmp("&&", pattern + offset)) {
+ ops[elei] = C2_B_OAND;
+ offset += 2;
+ }
+ else if (!mstrncmp("||", pattern + offset)) {
+ ops[elei] = C2_B_OOR;
+ offset += 2;
+ }
+ else
+ c2_error("Illegal operator.");
+
+ C2H_SKIP_SPACES();
+ }
+
+ // Parsing an element
+ if (elei && !next_expected) {
+ c2_error("No operator found between two elements.");
+ assert(ops[elei]);
+ }
+
+ // If we are out of space
+ if (2 == elei) {
+ --elei;
+ // If the first operator has higher or equal precedence, combine
+ // the first two elements
+ if (c2h_b_opcmp(ops[1], ops[2]) >= 0) {
+ eles[0] = c2h_comb_tree(ops[1], eles[0], eles[1]);
+ c2_ptr_reset(&eles[1]);
+ pele = &eles[elei];
+ ops[1] = ops[2];
+ }
+ // Otherwise, combine the second and the incoming one
+ else {
+ eles[1] = c2h_comb_tree(ops[2], eles[1], C2_PTR_NULL);
+ assert(eles[1].isbranch);
+ pele = &eles[1].b->opr2;
+ }
+ // The last operator always needs to be reset
+ ops[2] = C2_B_OUNDEFINED;
+ }
+
+ // It's a subgroup if it starts with '('
+ if ('(' == pattern[offset]) {
+ if ((offset = c2_parse_grp(ps, pattern, offset + 1, pele, level + 1)) < 0)
+ goto c2_parse_grp_fail;
+ }
+ // Otherwise it's a leaf
+ else {
+ if ((offset = c2_parse_target(ps, pattern, offset, pele)) < 0)
+ goto c2_parse_grp_fail;
+
+ assert(pele->b || pele->l);
+
+ if ((offset = c2_parse_op(pattern, offset, pele)) < 0)
+ goto c2_parse_grp_fail;
+
+ if ((offset = c2_parse_pattern(ps, pattern, offset, pele)) < 0)
+ goto c2_parse_grp_fail;
+
+ assert(!pele->isbranch && pele->l);
+ if (!c2_l_postprocess(ps, pele->l)) {
+ offset = -1;
+ goto c2_parse_grp_fail;
+ }
+ }
+ --offset;
+
+ // Apply negation
+ if (neg) {
+ neg = false;
+ if (pele->isbranch)
+ pele->b->neg = !pele->b->neg;
+ else
+ pele->l->neg = !pele->l->neg;
+ }
+
+ next_expected = false;
+ ++elei;
+ pele = &eles[elei];
+ }
+
+ // Wrong end character?
+ if (')' == pattern[offset] && !endchar)
+ c2_error("Expected end of string but found ')'.");
+ if (!pattern[offset] && ')' == endchar)
+ c2_error("Expected ')' but found end of string.");
+
+ // Handle end of group
+ if (!elei) {
+ c2_error("Empty group.");
+ }
+ else if (next_expected) {
+ c2_error("Missing rule before end of group.");
+ }
+ else if (elei > 1) {
+ assert(2 == elei);
+ assert(ops[1]);
+ eles[0] = c2h_comb_tree(ops[1], eles[0], eles[1]);
+ c2_ptr_reset(&eles[1]);
+ }
+
+ *presult = eles[0];
+
+ if (')' == pattern[offset])
+ ++offset;
+
+ return offset;
+
+c2_parse_grp_fail:
+ c2_freep(&eles[0]);
+ c2_freep(&eles[1]);
+
+ return -1;
+}
+
+#undef c2_error
+#define c2_error(format, ...) do { \
+ printf_err("Pattern \"%s\" pos %d: " format, pattern, offset, \
+ ## __VA_ARGS__); \
+ return -1; \
+} while(0)
+
+/**
+ * Parse the target part of a rule.
+ */
+static int
+c2_parse_target(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult) {
+ // Initialize leaf
+ presult->isbranch = false;
+ presult->l = malloc(sizeof(c2_l_t));
+ if (!presult->l)
+ c2_error("Failed to allocate memory for new leaf.");
+
+ c2_l_t * const pleaf = presult->l;
+ memcpy(pleaf, &leaf_def, sizeof(c2_l_t));
+
+ // Parse negation marks
+ for (; pattern[offset]; ++offset) {
+ if (isspace(pattern[offset]))
+ continue;
+
+ if ('!' == pattern[offset]) {
+ pleaf->neg = !pleaf->neg;
+ continue;
+ }
+
+ break;
+ }
+
+ // Copy target name out
+ unsigned tgtlen = 0;
+ for (; pattern[offset]
+ && (isalnum(pattern[offset]) || '_' == pattern[offset]); ++offset) {
+ ++tgtlen;
+ }
+ if (!tgtlen)
+ c2_error("Empty target.");
+
+ char *tgt = malloc((tgtlen + 1) * sizeof(char));
+ if (!tgt)
+ c2_error("Failed to allocate memory for target string.");
+ strncpy(tgt, pattern + offset - tgtlen, tgtlen);
+ tgt[tgtlen] = '\0';
+ pleaf->tgt = tgt;
+
+ // Check for predefined targets
+ for (unsigned i = 1; i < sizeof(C2_PREDEFS) / sizeof(C2_PREDEFS[0]); ++i) {
+ if (!strcmp(C2_PREDEFS[i].name, tgt)) {
+ pleaf->predef = i;
+ pleaf->type = C2_PREDEFS[i].type;
+ pleaf->format = C2_PREDEFS[i].format;
+ break;
+ }
+ }
+
+ // Alias for predefined targets
+ if (!pleaf->predef) {
+#define TGTFILL(pdefid) \
+ (pleaf->predef = pdefid, \
+ pleaf->type = C2_PREDEFS[pdefid].type, \
+ pleaf->format = C2_PREDEFS[pdefid].format)
+
+ // if (!strcmp("WM_NAME", tgt) || !strcmp("_NET_WM_NAME", tgt))
+ // TGTFILL(C2_L_PNAME);
+#undef TGTFILL
+ }
+
+ // Alias for custom properties
+#define TGTFILL(target, type, format) \
+ (pleaf->target = mstrcpy(target), \
+ pleaf->type = type, \
+ pleaf->format = format)
+
+ // if (!strcmp("SOME_ALIAS"))
+ // TGTFILL("ALIAS_TEXT", C2_L_TSTRING, 32);
+#undef TGTFILL
+
+ C2H_SKIP_SPACES();
+
+ // Parse target-on-frame flag
+ if ('@' == pattern[offset]) {
+ pleaf->tgt_onframe = true;
+ ++offset;
+ C2H_SKIP_SPACES();
+ }
+
+ // Parse array index
+ if ('[' == pattern[offset]) {
+ offset++;
+
+ int index = -1;
+ char *endptr = NULL;
+
+ index = strtol(pattern + offset, &endptr, 0);
+
+ if (!endptr || pattern + offset == endptr)
+ c2_error("No index number found after bracket.");
+
+ if (index < 0)
+ c2_error("Index number invalid.");
+
+ if (pleaf->predef)
+ c2_error("Predefined targets can't have index.");
+
+ pleaf->index = index;
+ offset = endptr - pattern;
+
+ C2H_SKIP_SPACES();
+
+ if (']' != pattern[offset])
+ c2_error("Index end marker not found.");
+
+ ++offset;
+
+ C2H_SKIP_SPACES();
+ }
+
+ // Parse target type and format
+ if (':' == pattern[offset]) {
+ ++offset;
+
+ // Look for format
+ bool hasformat = false;
+ int format = 0;
+ {
+ char *endptr = NULL;
+ format = strtol(pattern + offset, &endptr, 0);
+ assert(endptr);
+ hasformat = (endptr != pattern + offset);
+ offset = endptr - pattern;
+ }
+
+ C2H_SKIP_SPACES();
+
+ // Look for pattern
+ char ctype = pattern[offset++];
+
+ enum c2_l_type type = C2_L_TUNDEFINED;
+ if (isalpha(ctype)) {
+ switch (ctype) {
+ case 'w': type = C2_L_TWINDOW; break;
+ case 'd': type = C2_L_TDRAWABLE; break;
+ case 'c': type = C2_L_TCARDINAL; break;
+ case 's': type = C2_L_TSTRING; break;
+ case 'a': type = C2_L_TATOM; break;
+ }
+ }
+ if (ctype && !type)
+ c2_error("Invalid type character.");
+
+ if (type) {
+ if (pleaf->predef) {
+ printf_errf("(): Warning: Type specified for a default target will be ignored.");
+ }
+ else {
+ if (pleaf->type && type != pleaf->type)
+ printf_errf("(): Warning: Default type overridden on target.");
+ pleaf->type = type;
+ }
+ }
+ if (!pleaf->type)
+ c2_error("Cannot determine target type.");
+
+ // Default format
+ if (!pleaf->format) {
+ switch (pleaf->type) {
+ case C2_L_TWINDOW:
+ case C2_L_TDRAWABLE:
+ case C2_L_TATOM:
+ pleaf->format = 32; break;
+ case C2_L_TSTRING:
+ pleaf->format = 8; break;
+ default:
+ break;
+ }
+ }
+
+ C2H_SKIP_SPACES();
+
+ if (hasformat) {
+ if (pleaf->predef)
+ printf_err("Warning: Format \"%d\" specified on a default target will be ignored.", format);
+ else if (C2_L_TSTRING == pleaf->type)
+ printf_err("Warning: Format \"%d\" specified on a string target will be ignored.", format);
+ else {
+ if (pleaf->format && pleaf->format != format)
+ printf_err("Warning: Default format %d overridden on target.",
+ pleaf->format);
+ pleaf->format = format;
+ }
+ }
+ }
+
+ if (!pleaf->type)
+ c2_error("Target type cannot be determined.");
+
+ // if (!pleaf->predef && !pleaf->format && C2_L_TSTRING != pleaf->type)
+ // c2_error("Target format cannot be determined.");
+
+ if (pleaf->format && 8 != pleaf->format
+ && 16 != pleaf->format && 32 != pleaf->format)
+ c2_error("Invalid format.");
+
+ return offset;
+}
+
+/**
+ * Parse the operator part of a leaf.
+ */
+static int
+c2_parse_op(const char *pattern, int offset, c2_ptr_t *presult) {
+ c2_l_t * const pleaf = presult->l;
+
+ // Parse negation mark
+ C2H_SKIP_SPACES();
+ while ('!' == pattern[offset]) {
+ pleaf->neg = !pleaf->neg;
+ ++offset;
+ C2H_SKIP_SPACES();
+ }
+
+ // strchr() finds NULL character, too, so we have to check end of string
+ // before a strchr() call.
+ if (!pattern[offset])
+ return offset;
+
+ // Parse qualifiers
+ if (strchr("*^%~", pattern[offset])) {
+ switch (pattern[offset]) {
+ case '*': pleaf->match = C2_L_MCONTAINS; break;
+ case '^': pleaf->match = C2_L_MSTART; break;
+ case '%': pleaf->match = C2_L_MWILDCARD; break;
+ case '~': pleaf->match = C2_L_MPCRE; break;
+ default: putchar(pattern[offset]); fflush(stdout); assert(0);
+ }
+ ++offset;
+ C2H_SKIP_SPACES();
+ }
+
+ // Parse flags
+ while ('?' == pattern[offset]) {
+ pleaf->match_ignorecase = true;
+ ++offset;
+ C2H_SKIP_SPACES();
+ }
+
+ if (!pattern[offset])
+ return offset;
+
+ // Parse operator
+ while (strchr("><=", pattern[offset])) {
+ if ('=' == pattern[offset] && C2_L_OGT == pleaf->op)
+ pleaf->op = C2_L_OGTEQ;
+ else if ('=' == pattern[offset] && C2_L_OLT == pleaf->op)
+ pleaf->op = C2_L_OLTEQ;
+ else if (pleaf->op) {
+ c2_error("Duplicate operator.");
+ }
+ else {
+ switch (pattern[offset]) {
+ case '=': pleaf->op = C2_L_OEQ; break;
+ case '>': pleaf->op = C2_L_OGT; break;
+ case '<': pleaf->op = C2_L_OLT; break;
+ default: assert(0);
+ }
+ }
+ ++offset;
+ C2H_SKIP_SPACES();
+ }
+
+ // Check for problems
+ if (C2_L_OEQ != pleaf->op && (pleaf->match || pleaf->match_ignorecase))
+ c2_error("Greater-than/less-than operators cannot have a qualifier.");
+
+ if (!pleaf->op && (pleaf->match || pleaf->match_ignorecase))
+ c2_error("Exists operator cannot have qualifiers.");
+
+ if (pleaf->predef && pleaf->index >= 0)
+ c2_error("Predefined targets cannot be matched with index.");
+
+ return offset;
+}
+
+/**
+ * Parse the pattern part of a leaf.
+ */
+static int
+c2_parse_pattern(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult) {
+ c2_l_t * const pleaf = presult->l;
+
+ // Exists operator cannot have pattern
+ if (!pleaf->op)
+ return offset;
+
+ C2H_SKIP_SPACES();
+
+ char *endptr = 0;
+ // Check for boolean patterns
+ if (!strcmp("true", &pattern[offset])) {
+ pleaf->ptntype = C2_L_PTINT;
+ pleaf->ptnint = true;
+ offset += strlen("true");
+ }
+ else if (!strcmp("false", &pattern[offset])) {
+ pleaf->ptntype = C2_L_PTINT;
+ pleaf->ptnint = false;
+ offset += strlen("false");
+ }
+ // Check for integer patterns
+ else if (pleaf->ptnint = strtol(pattern + offset, &endptr, 0),
+ pattern + offset != endptr) {
+ pleaf->ptntype = C2_L_PTINT;
+ offset = endptr - pattern;
+ // Make sure we are stopping at the end of a word
+ if (isalnum(pattern[offset]))
+ c2_error("Trailing characters after a numeric pattern.");
+ }
+ // Check for string patterns
+ else {
+ bool raw = false;
+ char delim = '\0';
+
+ // String flags
+ if ('r' == tolower(pattern[offset])) {
+ raw = true;
+ ++offset;
+ C2H_SKIP_SPACES();
+ }
+
+ // Check for delimiters
+ if ('\"' == pattern[offset] || '\'' == pattern[offset]) {
+ pleaf->ptntype = C2_L_PTSTRING;
+ delim = pattern[offset];
+ ++offset;
+ }
+
+ if (C2_L_PTSTRING != pleaf->ptntype)
+ c2_error("Invalid pattern type.");
+
+ // Parse the string now
+ // We can't determine the length of the pattern, so we use the length
+ // to the end of the pattern string -- currently escape sequences
+ // cannot be converted to a string longer than itself.
+ char *tptnstr = malloc((strlen(pattern + offset) + 1) * sizeof(char));
+ char *ptptnstr = tptnstr;
+ pleaf->ptnstr = tptnstr;
+ for (; pattern[offset] && delim != pattern[offset]; ++offset) {
+ // Handle escape sequences if it's not a raw string
+ if ('\\' == pattern[offset] && !raw) {
+ switch(pattern[++offset]) {
+ case '\\': *(ptptnstr++) = '\\'; break;
+ case '\'': *(ptptnstr++) = '\''; break;
+ case '\"': *(ptptnstr++) = '\"'; break;
+ case 'a': *(ptptnstr++) = '\a'; break;
+ case 'b': *(ptptnstr++) = '\b'; break;
+ case 'f': *(ptptnstr++) = '\f'; break;
+ case 'n': *(ptptnstr++) = '\n'; break;
+ case 'r': *(ptptnstr++) = '\r'; break;
+ case 't': *(ptptnstr++) = '\t'; break;
+ case 'v': *(ptptnstr++) = '\v'; break;
+ case 'o':
+ case 'x':
+ {
+ char *tstr = mstrncpy(pattern + offset + 1, 2);
+ char *pstr = NULL;
+ long val = strtol(tstr, &pstr,
+ ('o' == pattern[offset] ? 8: 16));
+ free(tstr);
+ if (pstr != &tstr[2])
+ c2_error("Invalid octal/hex escape sequence.");
+ assert(val < 256 && val >= 0);
+ *(ptptnstr++) = val;
+ offset += 2;
+ break;
+ }
+ default: c2_error("Invalid escape sequence.");
+ }
+ }
+ else {
+ *(ptptnstr++) = pattern[offset];
+ }
+ }
+ if (!pattern[offset])
+ c2_error("Premature end of pattern string.");
+ ++offset;
+ *ptptnstr = '\0';
+ pleaf->ptnstr = mstrcpy(tptnstr);
+ free(tptnstr);
+ }
+
+ C2H_SKIP_SPACES();
+
+ if (!pleaf->ptntype)
+ c2_error("Invalid pattern type.");
+
+ // Check if the type is correct
+ if (!(((C2_L_TSTRING == pleaf->type
+ || C2_L_TATOM == pleaf->type)
+ && C2_L_PTSTRING == pleaf->ptntype)
+ || ((C2_L_TCARDINAL == pleaf->type
+ || C2_L_TWINDOW == pleaf->type
+ || C2_L_TDRAWABLE == pleaf->type)
+ && C2_L_PTINT == pleaf->ptntype)))
+ c2_error("Pattern type incompatible with target type.");
+
+ if (C2_L_PTINT == pleaf->ptntype && pleaf->match)
+ c2_error("Integer/boolean pattern cannot have operator qualifiers.");
+
+ if (C2_L_PTINT == pleaf->ptntype && pleaf->match_ignorecase)
+ c2_error("Integer/boolean pattern cannot have flags.");
+
+ if (C2_L_PTSTRING == pleaf->ptntype
+ && (C2_L_OGT == pleaf->op || C2_L_OGTEQ == pleaf->op
+ || C2_L_OLT == pleaf->op || C2_L_OLTEQ == pleaf->op))
+ c2_error("String pattern cannot have an arithmetic operator.");
+
+ return offset;
+}
+
+/**
+ * Parse a condition with legacy syntax.
+ */
+static int
+c2_parse_legacy(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult) {
+ unsigned plen = strlen(pattern + offset);
+
+ if (plen < 4 || ':' != pattern[offset + 1]
+ || !strchr(pattern + offset + 2, ':'))
+ c2_error("Format invalid.");
+
+ // Allocate memory for the new pleafition
+ c2_l_t *pleaf = malloc(sizeof(c2_l_t));
+ presult->isbranch = false;
+ presult->l = pleaf;
+ memcpy(pleaf, &leaf_def, sizeof(c2_l_t));
+ pleaf->type = C2_L_TSTRING;
+ pleaf->op = C2_L_OEQ;
+ pleaf->ptntype = C2_L_PTSTRING;
+
+ // Determine the pattern target
+#define TGTFILL(pdefid) \
+ (pleaf->predef = pdefid, \
+ pleaf->type = C2_PREDEFS[pdefid].type, \
+ pleaf->format = C2_PREDEFS[pdefid].format)
+ switch (pattern[offset]) {
+ case 'n': TGTFILL(C2_L_PNAME); break;
+ case 'i': TGTFILL(C2_L_PCLASSI); break;
+ case 'g': TGTFILL(C2_L_PCLASSG); break;
+ case 'r': TGTFILL(C2_L_PROLE); break;
+ default:
+ c2_error("Target \"%c\" invalid.\n", pattern[offset]);
+ }
+#undef TGTFILL
+
+ offset += 2;
+
+ // Determine the match type
+ switch (pattern[offset]) {
+ case 'e': pleaf->match = C2_L_MEXACT; break;
+ case 'a': pleaf->match = C2_L_MCONTAINS; break;
+ case 's': pleaf->match = C2_L_MSTART; break;
+ case 'w': pleaf->match = C2_L_MWILDCARD; break;
+ case 'p': pleaf->match = C2_L_MPCRE; break;
+ default:
+ free(pleaf);
+ c2_error("Type \"%c\" invalid.\n", pattern[offset]);
+ }
+ ++offset;
+
+ // Determine the pattern flags
+ while (':' != pattern[offset]) {
+ switch (pattern[offset]) {
+ case 'i': pleaf->match_ignorecase = true; break;
+ default: free(pleaf);
+ c2_error("Flag \"%c\" invalid.", pattern[offset]);
+ }
+ ++offset;
+ }
+ ++offset;
+
+ // Copy the pattern
+ pleaf->ptnstr = mstrcpy(pattern + offset);
+
+ if (!c2_l_postprocess(ps, pleaf))
+ return -1;
+
+ return offset;
+}
+
+#undef c2_error
+#define c2_error(format, ...) { \
+ printf_err(format, ## __VA_ARGS__); \
+ return false; }
+
+/**
+ * Do postprocessing on a condition leaf.
+ */
+static bool
+c2_l_postprocess(session_t *ps, c2_l_t *pleaf) {
+ // Give a pattern type to a leaf with exists operator, if needed
+ if (C2_L_OEXISTS == pleaf->op && !pleaf->ptntype) {
+ pleaf->ptntype =
+ (C2_L_TSTRING == pleaf->type ? C2_L_PTSTRING: C2_L_PTINT);
+ }
+
+ // Get target atom if it's not a predefined one
+ if (!pleaf->predef) {
+ pleaf->tgtatom = get_atom(ps, pleaf->tgt);
+ if (!pleaf->tgtatom)
+ c2_error("Failed to get atom for target \"%s\".", pleaf->tgt);
+ }
+
+ // Insert target Atom into atom track list
+ if (pleaf->tgtatom) {
+ bool found = false;
+ for (latom_t *platom = ps->track_atom_lst; platom;
+ platom = platom->next) {
+ if (pleaf->tgtatom == platom->atom) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ latom_t *pnew = malloc(sizeof(latom_t));
+ if (!pnew)
+ printf_errfq(1, "(): Failed to allocate memory for new track atom.");
+ pnew->next = ps->track_atom_lst;
+ pnew->atom = pleaf->tgtatom;
+ ps->track_atom_lst = pnew;
+ }
+ }
+
+ // Enable specific tracking options in compton if needed by the condition
+ // TODO: Add track_leader
+ if (pleaf->predef) {
+ switch (pleaf->predef) {
+ case C2_L_PFOCUSED: ps->o.track_focus = true; break;
+ case C2_L_PNAME:
+ case C2_L_PCLASSG:
+ case C2_L_PCLASSI:
+ case C2_L_PROLE: ps->o.track_wdata = true; break;
+ default: break;
+ }
+ }
+
+ // Warn about lower case characters in target name
+ if (!pleaf->predef) {
+ for (const char *pc = pleaf->tgt; *pc; ++pc) {
+ if (islower(*pc)) {
+ printf_errf("(): Warning: Lowercase character in target name \"%s\".", pleaf->tgt);
+ break;
+ }
+ }
+ }
+
+ // PCRE patterns
+ if (C2_L_PTSTRING == pleaf->ptntype && C2_L_MPCRE == pleaf->match) {
+#ifdef CONFIG_REGEX_PCRE
+ const char *error = NULL;
+ int erroffset = 0;
+ int options = 0;
+
+ // Ignore case flag
+ if (pleaf->match_ignorecase)
+ options |= PCRE_CASELESS;
+
+ // Compile PCRE expression
+ pleaf->regex_pcre = pcre_compile(pleaf->ptnstr, options,
+ &error, &erroffset, NULL);
+ if (!pleaf->regex_pcre)
+ c2_error("Pattern \"%s\": PCRE regular expression parsing failed on "
+ "offset %d.", pleaf->ptnstr, erroffset);
+#ifdef CONFIG_REGEX_PCRE_JIT
+ pleaf->regex_pcre_extra = pcre_study(pleaf->regex_pcre,
+ PCRE_STUDY_JIT_COMPILE, &error);
+ if (!pleaf->regex_pcre_extra) {
+ printf("Pattern \"%s\": PCRE regular expression study failed: %s",
+ pleaf->ptnstr, error);
+ }
+#endif
+
+ // Free the target string
+ // free(pleaf->tgt);
+ // pleaf->tgt = NULL;
+#else
+ c2_error("PCRE regular expression support not compiled in.");
+#endif
+ }
+
+ return true;
+}
+/**
+ * Free a condition tree.
+ */
+static void
+c2_free(c2_ptr_t p) {
+ // For a branch element
+ if (p.isbranch) {
+ c2_b_t * const pbranch = p.b;
+
+ if (!pbranch)
+ return;
+
+ c2_free(pbranch->opr1);
+ c2_free(pbranch->opr2);
+ free(pbranch);
+ }
+ // For a leaf element
+ else {
+ c2_l_t * const pleaf = p.l;
+
+ if (!pleaf)
+ return;
+
+ free(pleaf->tgt);
+ free(pleaf->ptnstr);
+#ifdef CONFIG_REGEX_PCRE
+ pcre_free(pleaf->regex_pcre);
+ LPCRE_FREE_STUDY(pleaf->regex_pcre_extra);
+#endif
+ free(pleaf);
+ }
+}
+
+/**
+ * Free a condition tree in c2_lptr_t.
+ */
+c2_lptr_t *
+c2_free_lptr(c2_lptr_t *lp) {
+ if (!lp)
+ return NULL;
+
+ c2_lptr_t *pnext = lp->next;
+ c2_free(lp->ptr);
+ free(lp);
+
+ return pnext;
+}
+
+/**
+ * Get a string representation of a rule target.
+ */
+static const char *
+c2h_dump_str_tgt(c2_l_t * const pleaf) {
+ if (pleaf->predef)
+ return C2_PREDEFS[pleaf->predef].name;
+ else
+ return pleaf->tgt;
+}
+
+/**
+ * Get a string representation of a target.
+ */
+static const char *
+c2h_dump_str_type(c2_l_t * const pleaf) {
+ switch (pleaf->type) {
+ case C2_L_TWINDOW: return "w";
+ case C2_L_TDRAWABLE: return "d";
+ case C2_L_TCARDINAL: return "c";
+ case C2_L_TSTRING: return "s";
+ case C2_L_TATOM: return "a";
+ case C2_L_TUNDEFINED: break;
+ }
+
+ return NULL;
+}
+
+/**
+ * Dump a condition tree.
+ */
+static void
+c2_dump_raw(c2_ptr_t p) {
+ // For a branch
+ if (p.isbranch) {
+ c2_b_t * const pbranch = p.b;
+
+ if (!pbranch)
+ return;
+
+ if (pbranch->neg)
+ putchar('!');
+
+ printf("(");
+ c2_dump_raw(pbranch->opr1);
+
+ switch (pbranch->op) {
+ case C2_B_OAND: printf(" && "); break;
+ case C2_B_OOR: printf(" || "); break;
+ case C2_B_OXOR: printf(" XOR "); break;
+ default: assert(0); break;
+ }
+
+ c2_dump_raw(pbranch->opr2);
+ printf(")");
+ }
+ // For a leaf
+ else {
+ c2_l_t * const pleaf = p.l;
+
+ if (!pleaf)
+ return;
+
+ if (C2_L_OEXISTS == pleaf->op && pleaf->neg)
+ putchar('!');
+
+ // Print target name, type, and format
+ {
+ fputs(c2h_dump_str_tgt(pleaf), stdout);
+ if (pleaf->tgt_onframe)
+ fputs("@", stdout);
+ if (pleaf->index >= 0)
+ printf("[%d]", pleaf->index);
+ printf(":%d%s", pleaf->format, c2h_dump_str_type(pleaf));
+ }
+
+ // Print operator
+ putchar(' ');
+
+ if (C2_L_OEXISTS != pleaf->op && pleaf->neg)
+ putchar('!');
+
+ 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;
+ }
+
+ if (pleaf->match_ignorecase)
+ putchar('?');
+
+ 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;
+ }
+
+ if (C2_L_OEXISTS == pleaf->op)
+ return;
+
+ // Print pattern
+ putchar(' ');
+ switch (pleaf->ptntype) {
+ case C2_L_PTINT:
+ printf("%ld", pleaf->ptnint);
+ break;
+ case C2_L_PTSTRING:
+ // TODO: Escape string before printing out?
+ printf("\"%s\"", pleaf->ptnstr);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ }
+}
+
+/**
+ * Get the type atom of a condition.
+ */
+static Atom
+c2_get_atom_type(const c2_l_t *pleaf) {
+ switch (pleaf->type) {
+ case C2_L_TCARDINAL:
+ return XA_CARDINAL;
+ case C2_L_TWINDOW:
+ return XA_WINDOW;
+ case C2_L_TSTRING:
+ return XA_STRING;
+ case C2_L_TATOM:
+ return XA_ATOM;
+ case C2_L_TDRAWABLE:
+ return XA_DRAWABLE;
+ default:
+ assert(0);
+ break;
+ }
+
+ assert(0);
+ return AnyPropertyType;
+}
+
+/**
+ * Match a window against a single leaf window condition.
+ *
+ * For internal use.
+ */
+static inline void
+c2_match_once_leaf(session_t *ps, win *w, c2_l_t *pleaf,
+ bool *pres, bool *perr) {
+ assert(pleaf);
+
+ const Window wid = (pleaf->tgt_onframe ? w->client_win: w->id);
+
+ // Return if wid is missing
+ if (!pleaf->predef && !wid)
+ return;
+
+ const int idx = (pleaf->index < 0 ? 0: pleaf->index);
+
+ switch (pleaf->ptntype) {
+ // Deal with integer patterns
+ case C2_L_PTINT:
+ {
+ long tgt = 0;
+
+ // Get the value
+ // A predefined target
+ if (pleaf->predef) {
+ *perr = false;
+ switch (pleaf->predef) {
+ case C2_L_PID: tgt = wid; break;
+ case C2_L_POVREDIR: tgt = w->a.override_redirect; break;
+ case C2_L_PFOCUSED: tgt = w->focused_real; break;
+ case C2_L_PWMWIN: tgt = w->wmwin; break;
+ case C2_L_PCLIENT: tgt = w->client_win; break;
+ case C2_L_PLEADER: tgt = w->leader; break;
+ default: *perr = true; assert(0); break;
+ }
+ }
+ // A raw window property
+ else {
+ winprop_t prop = wid_get_prop_adv(ps, wid, pleaf->tgtatom,
+ idx, 1L, c2_get_atom_type(pleaf), pleaf->format);
+ if (prop.nitems) {
+ *perr = false;
+ tgt = winprop_get_int(prop);
+ }
+ free_winprop(&prop);
+ }
+
+ if (*perr)
+ return;
+
+ // Do comparison
+ switch (pleaf->op) {
+ case C2_L_OEXISTS:
+ *pres = (pleaf->predef ? tgt: true);
+ break;
+ case C2_L_OEQ: *pres = (tgt == pleaf->ptnint); break;
+ case C2_L_OGT: *pres = (tgt > pleaf->ptnint); break;
+ case C2_L_OGTEQ: *pres = (tgt >= pleaf->ptnint); break;
+ case C2_L_OLT: *pres = (tgt < pleaf->ptnint); break;
+ case C2_L_OLTEQ: *pres = (tgt <= pleaf->ptnint); break;
+ default: *perr = true; assert(0); break;
+ }
+ }
+ break;
+ // String patterns
+ case C2_L_PTSTRING:
+ {
+ const char *tgt = NULL;
+ char *tgt_free = NULL;
+
+ // A predefined target
+ if (pleaf->predef) {
+ switch (pleaf->predef) {
+ case C2_L_PWINDOWTYPE: tgt = WINTYPES[w->window_type];
+ break;
+ case C2_L_PNAME: tgt = w->name; break;
+ case C2_L_PCLASSG: tgt = w->class_general; break;
+ case C2_L_PCLASSI: tgt = w->class_instance; break;
+ case C2_L_PROLE: tgt = w->role; break;
+ default: assert(0); break;
+ }
+ }
+ // If it's an atom type property, convert atom to string
+ else if (C2_L_TATOM == pleaf->type) {
+ winprop_t prop = wid_get_prop_adv(ps, wid, pleaf->tgtatom,
+ idx, 1L, c2_get_atom_type(pleaf), pleaf->format);
+ Atom atom = winprop_get_int(prop);
+ if (atom) {
+ tgt_free = XGetAtomName(ps->dpy, atom);
+ }
+ if (tgt_free) {
+ tgt = tgt_free;
+ }
+ free_winprop(&prop);
+ }
+ // Otherwise, just fetch the string list
+ else {
+ char **strlst = NULL;
+ int nstr;
+ if (wid_get_text_prop(ps, wid, pleaf->tgtatom, &strlst,
+ &nstr) && nstr > idx) {
+ tgt_free = mstrcpy(strlst[idx]);
+ tgt = tgt_free;
+ }
+ if (strlst)
+ XFreeStringList(strlst);
+ }
+
+ if (tgt) {
+ *perr = false;
+ }
+ else {
+ return;
+ }
+
+ // Actual matching
+ switch (pleaf->op) {
+ case C2_L_OEXISTS:
+ *pres = true;
+ break;
+ case C2_L_OEQ:
+ switch (pleaf->match) {
+ case C2_L_MEXACT:
+ if (pleaf->match_ignorecase)
+ *pres = !strcasecmp(tgt, pleaf->ptnstr);
+ else
+ *pres = !strcmp(tgt, pleaf->ptnstr);
+ break;
+ case C2_L_MCONTAINS:
+ if (pleaf->match_ignorecase)
+ *pres = strcasestr(tgt, pleaf->ptnstr);
+ else
+ *pres = strstr(tgt, pleaf->ptnstr);
+ break;
+ case C2_L_MSTART:
+ if (pleaf->match_ignorecase)
+ *pres = !strncasecmp(tgt, pleaf->ptnstr,
+ strlen(pleaf->ptnstr));
+ else
+ *pres = !strncmp(tgt, pleaf->ptnstr,
+ strlen(pleaf->ptnstr));
+ break;
+ case C2_L_MWILDCARD:
+ {
+ int flags = 0;
+ if (pleaf->match_ignorecase)
+ flags |= FNM_CASEFOLD;
+ *pres = !fnmatch(pleaf->ptnstr, tgt, flags);
+ }
+ break;
+ case C2_L_MPCRE:
+#ifdef CONFIG_REGEX_PCRE
+ *pres = (pcre_exec(pleaf->regex_pcre,
+ pleaf->regex_pcre_extra,
+ tgt, strlen(tgt), 0, 0, NULL, 0) >= 0);
+#else
+ assert(0);
+#endif
+ break;
+ }
+ break;
+ default:
+ *perr = true;
+ assert(0);
+ }
+
+ // Free the string after usage, if necessary
+ if (tgt_free) {
+ if (C2_L_TATOM == pleaf->type)
+ XFree(tgt_free);
+ else
+ free(tgt_free);
+ }
+ }
+ break;
+ default:
+ assert(0);
+ break;
+ }
+}
+
+/**
+ * Match a window against a single window condition.
+ *
+ * @return true if matched, false otherwise.
+ */
+static bool
+c2_match_once(session_t *ps, win *w, const c2_ptr_t cond) {
+ if (c2_ptr_isempty(cond))
+ return false;
+
+ bool result = false;
+ bool error = true;
+
+ // Handle a branch
+ if (cond.isbranch) {
+ const c2_b_t *pb = cond.b;
+
+ if (!pb)
+ return false;
+
+ error = false;
+
+ switch (pb->op) {
+ case C2_B_OAND:
+ result = (c2_match_once(ps, w, pb->opr1)
+ && c2_match_once(ps, w, pb->opr2));
+ break;
+ case C2_B_OOR:
+ result = (c2_match_once(ps, w, pb->opr1)
+ || c2_match_once(ps, w, pb->opr2));
+ break;
+ case C2_B_OXOR:
+ result = (c2_match_once(ps, w, pb->opr1)
+ != c2_match_once(ps, w, pb->opr2));
+ break;
+ default:
+ error = true;
+ assert(0);
+ }
+
+#ifdef DEBUG_WINMATCH
+ printf_dbgf("(%#010lx): branch: result = %d, pattern = ", w->id, result);
+ c2_dump(cond);
+#endif
+ }
+ // Handle a leaf
+ else {
+ c2_l_t *pleaf = cond.l;
+
+ if (!pleaf)
+ return false;
+
+ c2_match_once_leaf(ps, w, pleaf, &result, &error);
+
+ // For EXISTS operator, no errors are fatal
+ if (C2_L_OEXISTS == pleaf->op && error) {
+ result = false;
+ error = false;
+ }
+
+#ifdef DEBUG_WINMATCH
+ printf_dbgf("(%#010lx): leaf: result = %d, error = %d, "
+ "client = %#010lx, pattern = ",
+ w->id, result, error, w->client_win);
+ c2_dump(cond);
+#endif
+ }
+
+ // Postprocess the result
+ if (error)
+ result = false;
+
+ if (cond.isbranch ? cond.b->neg: cond.l->neg)
+ result = !result;
+
+ return result;
+}
+
+/**
+ * Match a window against a condition linked list.
+ *
+ * @param cache a place to cache the last matched condition
+ * @return true if matched, false otherwise.
+ */
+bool
+c2_match(session_t *ps, win *w, const c2_lptr_t *condlst,
+ const c2_lptr_t **cache) {
+ // Check if the cached entry matches firstly
+ if (cache && *cache && c2_match_once(ps, w, (*cache)->ptr))
+ return true;
+
+ // Then go through the whole linked list
+ for (; condlst; condlst = condlst->next) {
+ if (c2_match_once(ps, w, condlst->ptr)) {
+ if (cache)
+ *cache = condlst;
+ return true;
+ }
+ }
+
+ return false;
+}
+
diff --git a/src/c2.h b/src/c2.h
new file mode 100644
index 0000000..4062388
--- /dev/null
+++ b/src/c2.h
@@ -0,0 +1,309 @@
+/*
+ * Compton - a compositor for X11
+ *
+ * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
+ *
+ * Copyright (c) 2011-2013, Christopher Jeffrey
+ * See LICENSE for more information.
+ *
+ */
+
+#include "common.h"
+
+#include <fnmatch.h>
+#include <ctype.h>
+
+// libpcre
+#ifdef CONFIG_REGEX_PCRE
+#include <pcre.h>
+
+// For compatiblity with <libpcre-8.20
+#ifndef PCRE_STUDY_JIT_COMPILE
+#define PCRE_STUDY_JIT_COMPILE 0
+#define LPCRE_FREE_STUDY(extra) pcre_free(extra)
+#else
+#define LPCRE_FREE_STUDY(extra) pcre_free_study(extra)
+#endif
+
+#endif
+
+#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, \
+ .l = NULL, \
+}
+
+const static c2_ptr_t C2_PTR_NULL = C2_PTR_INIT;
+
+/// Operator of a branch element.
+typedef enum {
+ C2_B_OUNDEFINED,
+ C2_B_OAND,
+ C2_B_OOR,
+ C2_B_OXOR,
+} c2_b_op_t;
+
+/// Structure for branch element in a window condition
+struct _c2_b {
+ bool neg : 1;
+ c2_b_op_t op : 2;
+ c2_ptr_t opr1;
+ c2_ptr_t opr2;
+};
+
+/// Initializer for c2_b_t.
+#define C2_B_INIT { \
+ .neg = false, \
+ .op = C2_B_OUNDEFINED, \
+ .opr1 = C2_PTR_INIT, \
+ .opr2 = C2_PTR_INIT, \
+}
+
+/// Structure for leaf element in a window condition
+struct _c2_l {
+ bool neg : 1;
+ enum {
+ C2_L_OEXISTS,
+ C2_L_OEQ,
+ C2_L_OGT,
+ C2_L_OGTEQ,
+ C2_L_OLT,
+ C2_L_OLTEQ,
+ } op : 3;
+ enum {
+ C2_L_MEXACT,
+ C2_L_MSTART,
+ C2_L_MCONTAINS,
+ C2_L_MWILDCARD,
+ C2_L_MPCRE,
+ } match : 3;
+ bool match_ignorecase : 1;
+ char *tgt;
+ Atom tgtatom;
+ bool tgt_onframe;
+ int index;
+ enum {
+ C2_L_PUNDEFINED,
+ C2_L_PID,
+ C2_L_POVREDIR,
+ C2_L_PFOCUSED,
+ C2_L_PWMWIN,
+ C2_L_PCLIENT,
+ C2_L_PWINDOWTYPE,
+ C2_L_PLEADER,
+ C2_L_PNAME,
+ C2_L_PCLASSG,
+ C2_L_PCLASSI,
+ C2_L_PROLE,
+ } predef;
+ enum c2_l_type {
+ C2_L_TUNDEFINED,
+ C2_L_TSTRING,
+ C2_L_TCARDINAL,
+ C2_L_TWINDOW,
+ C2_L_TATOM,
+ C2_L_TDRAWABLE,
+ } type;
+ int format;
+ enum {
+ C2_L_PTUNDEFINED,
+ C2_L_PTSTRING,
+ C2_L_PTINT,
+ } ptntype;
+ char *ptnstr;
+ long ptnint;
+#ifdef CONFIG_REGEX_PCRE
+ pcre *regex_pcre;
+ pcre_extra *regex_pcre_extra;
+#endif
+};
+
+/// Initializer for c2_l_t.
+#define C2_L_INIT { \
+ .neg = false, \
+ .op = C2_L_OEXISTS, \
+ .tgt = NULL, \
+ .tgtatom = 0, \
+ .tgt_onframe = false, \
+ .predef = C2_L_PUNDEFINED, \
+ .index = -1, \
+ .type = C2_L_TUNDEFINED, \
+ .format = 0, \
+ .ptntype = C2_L_PTUNDEFINED, \
+ .ptnstr = NULL, \
+ .ptnint = 0, \
+}
+
+const static c2_l_t leaf_def = C2_L_INIT;
+
+/// Linked list type of conditions.
+struct _c2_lptr {
+ c2_ptr_t ptr;
+ struct _c2_lptr *next;
+};
+
+/// Initializer for c2_lptr_t.
+#define C2_LPTR_INIT { \
+ .ptr = C2_PTR_INIT, \
+ .next = NULL, \
+}
+
+/// Structure representing a predefined target.
+typedef struct {
+ char *name;
+ enum c2_l_type type;
+ int format;
+} c2_predef_t;
+
+// Predefined targets.
+const static c2_predef_t C2_PREDEFS[] = {
+ [C2_L_PID ] = { "id" , C2_L_TCARDINAL , 0 },
+ [C2_L_POVREDIR ] = { "override_redirect" , C2_L_TCARDINAL , 0 },
+ [C2_L_PFOCUSED ] = { "focused" , C2_L_TCARDINAL , 0 },
+ [C2_L_PWMWIN ] = { "wmwin" , C2_L_TCARDINAL , 0 },
+ [C2_L_PCLIENT ] = { "client" , C2_L_TWINDOW , 0 },
+ [C2_L_PWINDOWTYPE ] = { "window_type" , C2_L_TSTRING , 0 },
+ [C2_L_PLEADER ] = { "leader" , C2_L_TWINDOW , 0 },
+ [C2_L_PNAME ] = { "name" , C2_L_TSTRING , 0 },
+ [C2_L_PCLASSG ] = { "class_g" , C2_L_TSTRING , 0 },
+ [C2_L_PCLASSI ] = { "class_i" , C2_L_TSTRING , 0 },
+ [C2_L_PROLE ] = { "role" , C2_L_TSTRING , 0 },
+};
+
+/**
+ * Return whether a c2_ptr_t is empty.
+ */
+static inline bool
+c2_ptr_isempty(const c2_ptr_t p) {
+ return !(p.isbranch ? (bool) p.b: (bool) p.l);
+}
+
+/**
+ * Reset a c2_ptr_t.
+ */
+static inline void
+c2_ptr_reset(c2_ptr_t *pp) {
+ if (pp)
+ memcpy(pp, &C2_PTR_NULL, sizeof(c2_ptr_t));
+}
+
+/**
+ * Combine two condition trees.
+ */
+static inline c2_ptr_t
+c2h_comb_tree(c2_b_op_t op, c2_ptr_t p1, c2_ptr_t p2) {
+ c2_ptr_t p = {
+ .isbranch = true,
+ .b = malloc(sizeof(c2_b_t))
+ };
+
+ p.b->opr1 = p1;
+ p.b->opr2 = p2;
+ p.b->op = op;
+
+ return p;
+}
+
+/**
+ * Get the precedence value of a condition branch operator.
+ */
+static inline int
+c2h_b_opp(c2_b_op_t op) {
+ switch (op) {
+ case C2_B_OAND: return 2;
+ case C2_B_OOR: return 1;
+ case C2_B_OXOR: return 1;
+ default: break;
+ }
+
+ assert(0);
+ return 0;
+}
+
+/**
+ * Compare precedence of two condition branch operators.
+ *
+ * Associativity is left-to-right, forever.
+ *
+ * @return positive number if op1 > op2, 0 if op1 == op2 in precedence,
+ * negative number otherwise
+ */
+static inline int
+c2h_b_opcmp(c2_b_op_t op1, c2_b_op_t op2) {
+ return c2h_b_opp(op1) - c2h_b_opp(op2);
+}
+
+static int
+c2_parse_grp(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult, int level);
+
+static int
+c2_parse_target(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult);
+
+static int
+c2_parse_op(const char *pattern, int offset, c2_ptr_t *presult);
+
+static int
+c2_parse_pattern(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult);
+
+static bool
+c2_l_postprocess(session_t *ps, c2_l_t *pleaf);
+
+static int
+c2_parse_legacy(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult);
+
+static bool
+c2_l_postprocess(session_t *ps, c2_l_t *pleaf);
+
+static void
+c2_free(c2_ptr_t p);
+
+/**
+ * Wrapper of c2_free().
+ */
+static inline void
+c2_freep(c2_ptr_t *pp) {
+ if (pp) {
+ c2_free(*pp);
+ c2_ptr_reset(pp);
+ }
+}
+
+static const char *
+c2h_dump_str_tgt(c2_l_t * const pleaf);
+
+static const char *
+c2h_dump_str_type(c2_l_t * const pleaf);
+
+static void
+c2_dump_raw(c2_ptr_t p);
+
+/**
+ * Wrapper of c2_dump_raw().
+ */
+static inline void
+c2_dump(c2_ptr_t p) {
+ c2_dump_raw(p);
+ printf("\n");
+ fflush(stdout);
+}
+
+static Atom
+c2_get_atom_type(const c2_l_t *pleaf);
+
+static bool
+c2_match_once(session_t *ps, win *w, const c2_ptr_t cond);
+
diff --git a/src/common.h b/src/common.h
index 004d955..41fc402 100644
--- a/src/common.h
+++ b/src/common.h
@@ -76,20 +76,6 @@
#include <X11/extensions/Xrandr.h>
#include <X11/extensions/Xdbe.h>
-// libpcre
-#ifdef CONFIG_REGEX_PCRE
-#include <pcre.h>
-
-// For compatiblity with <libpcre-8.20
-#ifndef PCRE_STUDY_JIT_COMPILE
-#define PCRE_STUDY_JIT_COMPILE 0
-#define LPCRE_FREE_STUDY(extra) pcre_free(extra)
-#else
-#define LPCRE_FREE_STUDY(extra) pcre_free_study(extra)
-#endif
-
-#endif
-
// libconfig
#ifdef CONFIG_LIBCONFIG
#include <libgen.h>
@@ -257,18 +243,6 @@ enum wincond_type {
#define CONDF_IGNORECASE 0x0001
-typedef struct _wincond {
- enum wincond_target target;
- enum wincond_type type;
- char *pattern;
-#ifdef CONFIG_REGEX_PCRE
- pcre *regex_pcre;
- pcre_extra *regex_pcre_extra;
-#endif
- int16_t flags;
- struct _wincond *next;
-} wincond_t;
-
/// VSync modes.
typedef enum {
VSYNC_NONE,
@@ -297,13 +271,13 @@ struct _timeout_t;
struct _win;
-#ifdef CONFIG_C2
typedef struct _c2_lptr c2_lptr_t;
-#endif
/// Structure representing all options.
typedef struct {
// === General ===
+ /// The configuration file we used.
+ char *config_file;
/// The display name we used. NULL means we are using the value of the
/// <code>DISPLAY</code> environment variable.
char *display;
@@ -350,7 +324,7 @@ typedef struct {
double shadow_opacity;
bool clear_shadow;
/// Shadow blacklist. A linked list of conditions.
- wincond_t *shadow_blacklist;
+ c2_lptr_t *shadow_blacklist;
/// Whether bounding-shaped window should be ignored.
bool shadow_ignore_shaped;
/// Whether to respect _COMPTON_SHADOW.
@@ -368,7 +342,7 @@ typedef struct {
/// Whether to disable fading on window open/close.
bool no_fading_openclose;
/// Fading blacklist. A linked list of conditions.
- wincond_t *fade_blacklist;
+ c2_lptr_t *fade_blacklist;
// === Opacity ===
/// Default opacity for specific window types
@@ -404,7 +378,7 @@ typedef struct {
/// based on window opacity.
bool inactive_dim_fixed;
/// Conditions of windows to have inverted colors.
- wincond_t *invert_color_list;
+ c2_lptr_t *invert_color_list;
// === Focus related ===
/// Consider windows of specific types to be always focused.
@@ -412,7 +386,7 @@ typedef struct {
/// Whether to use EWMH _NET_ACTIVE_WINDOW to find active window.
bool use_ewmh_active_win;
/// A list of windows always to be considered focused.
- wincond_t *focus_blacklist;
+ c2_lptr_t *focus_blacklist;
/// Whether to do window grouping with <code>WM_TRANSIENT_FOR</code>.
bool detect_transient;
/// Whether to do window grouping with <code>WM_CLIENT_LEADER</code>.
@@ -736,10 +710,10 @@ typedef struct _win {
char *class_general;
/// <code>WM_WINDOW_ROLE</code> value of the window.
char *role;
- wincond_t *cache_sblst;
- wincond_t *cache_fblst;
- wincond_t *cache_fcblst;
- wincond_t *cache_ivclst;
+ const c2_lptr_t *cache_sblst;
+ const c2_lptr_t *cache_fblst;
+ const c2_lptr_t *cache_fcblst;
+ const c2_lptr_t *cache_ivclst;
// Opacity-related members
/// Current window opacity.
@@ -1479,7 +1453,7 @@ win_set_invert_color_force(session_t *ps, win *w, switch_t val);
///@{
c2_lptr_t *
-c2_parse(session_t *ps, c2_lptr_t **pcondlst, char *pattern);
+c2_parse(session_t *ps, c2_lptr_t **pcondlst, const char *pattern);
c2_lptr_t *
c2_free_lptr(c2_lptr_t *lp);
diff --git a/src/compton.c b/src/compton.c
index 814bf4a..cbb5e7b 100644
--- a/src/compton.c
+++ b/src/compton.c
@@ -567,7 +567,7 @@ wid_get_prop_adv(const session_t *ps, Window w, Atom atom, long offset,
if (Success == XGetWindowProperty(ps->dpy, w, atom, offset, length,
False, rtype, &type, &format, &nitems, &after, &data)
&& nitems && (AnyPropertyType == type || type == rtype)
- && (!format || format == rformat)
+ && (!rformat || format == rformat)
&& (8 == format || 16 == format || 32 == format)) {
return (winprop_t) {
.data.p8 = data,
@@ -630,243 +630,19 @@ win_rounded_corners(session_t *ps, win *w) {
}
/**
- * Match a window against a single window condition.
- *
- * @return true if matched, false otherwise.
- */
-static bool
-win_match_once(win *w, const wincond_t *cond) {
- const char *target;
- bool matched = false;
-
-#ifdef DEBUG_WINMATCH
- printf("win_match_once(%#010lx \"%s\"): cond = %p", w->id, w->name,
- cond);
-#endif
-
- if (InputOnly == w->a.class) {
-#ifdef DEBUG_WINMATCH
- printf(": InputOnly\n");
-#endif
- return false;
- }
-
- // Determine the target
- target = NULL;
- switch (cond->target) {
- case CONDTGT_NAME:
- target = w->name;
- break;
- case CONDTGT_CLASSI:
- target = w->class_instance;
- break;
- case CONDTGT_CLASSG:
- target = w->class_general;
- break;
- case CONDTGT_ROLE:
- target = w->role;
- break;
- }
-
- if (!target) {
-#ifdef DEBUG_WINMATCH
- printf(": Target not found\n");
-#endif
- return false;
- }
-
- // Determine pattern type and match
- switch (cond->type) {
- case CONDTP_EXACT:
- if (cond->flags & CONDF_IGNORECASE)
- matched = !strcasecmp(target, cond->pattern);
- else
- matched = !strcmp(target, cond->pattern);
- break;
- case CONDTP_ANYWHERE:
- if (cond->flags & CONDF_IGNORECASE)
- matched = strcasestr(target, cond->pattern);
- else
- matched = strstr(target, cond->pattern);
- break;
- case CONDTP_FROMSTART:
- if (cond->flags & CONDF_IGNORECASE)
- matched = !strncasecmp(target, cond->pattern,
- strlen(cond->pattern));
- else
- matched = !strncmp(target, cond->pattern,
- strlen(cond->pattern));
- break;
- case CONDTP_WILDCARD:
- {
- int flags = 0;
- if (cond->flags & CONDF_IGNORECASE)
- flags = FNM_CASEFOLD;
- matched = !fnmatch(cond->pattern, target, flags);
- }
- break;
- case CONDTP_REGEX_PCRE:
-#ifdef CONFIG_REGEX_PCRE
- matched = (pcre_exec(cond->regex_pcre, cond->regex_pcre_extra,
- target, strlen(target), 0, 0, NULL, 0) >= 0);
-#endif
- break;
- }
-
-#ifdef DEBUG_WINMATCH
- printf(", matched = %d\n", matched);
-#endif
-
- return matched;
-}
-
-/**
- * Match a window against a condition linked list.
- *
- * @param cache a place to cache the last matched condition
- * @return true if matched, false otherwise.
- */
-static bool
-win_match(win *w, wincond_t *condlst, wincond_t **cache) {
- // Check if the cached entry matches firstly
- if (cache && *cache && win_match_once(w, *cache))
- return true;
-
- // Then go through the whole linked list
- for (; condlst; condlst = condlst->next) {
- if (win_match_once(w, condlst)) {
- if (cache)
- *cache = condlst;
- return true;
- }
- }
-
- return false;
-}
-
-/**
* Add a pattern to a condition linked list.
*/
static bool
-condlst_add(wincond_t **pcondlst, const char *pattern) {
+condlst_add(session_t *ps, c2_lptr_t **pcondlst, const char *pattern) {
if (!pattern)
return false;
- unsigned plen = strlen(pattern);
- wincond_t *cond;
- const char *pos;
-
- if (plen < 4 || ':' != pattern[1] || !strchr(pattern + 2, ':')) {
- printf("Pattern \"%s\": Format invalid.\n", pattern);
- return false;
- }
-
- // Allocate memory for the new condition
- cond = malloc(sizeof(wincond_t));
-
- // Determine the pattern target
- switch (pattern[0]) {
- case 'n':
- cond->target = CONDTGT_NAME;
- break;
- case 'i':
- cond->target = CONDTGT_CLASSI;
- break;
- case 'g':
- cond->target = CONDTGT_CLASSG;
- break;
- case 'r':
- cond->target = CONDTGT_ROLE;
- break;
- default:
- printf("Pattern \"%s\": Target \"%c\" invalid.\n",
- pattern, pattern[0]);
- free(cond);
- return false;
- }
-
- // Determine the pattern type
- switch (pattern[2]) {
- case 'e':
- cond->type = CONDTP_EXACT;
- break;
- case 'a':
- cond->type = CONDTP_ANYWHERE;
- break;
- case 's':
- cond->type = CONDTP_FROMSTART;
- break;
- case 'w':
- cond->type = CONDTP_WILDCARD;
- break;
-#ifdef CONFIG_REGEX_PCRE
- case 'p':
- cond->type = CONDTP_REGEX_PCRE;
- break;
-#endif
- default:
- printf("Pattern \"%s\": Type \"%c\" invalid.\n",
- pattern, pattern[2]);
- free(cond);
- return false;
- }
-
- // Determine the pattern flags
- pos = &pattern[3];
- cond->flags = 0;
- while (':' != *pos) {
- switch (*pos) {
- case 'i':
- cond->flags |= CONDF_IGNORECASE;
- break;
- default:
- printf("Pattern \"%s\": Flag \"%c\" invalid.\n",
- pattern, *pos);
- break;
- }
- ++pos;
- }
-
- // Copy the pattern
- ++pos;
- cond->pattern = NULL;
-#ifdef CONFIG_REGEX_PCRE
- cond->regex_pcre = NULL;
- cond->regex_pcre_extra = NULL;
-#endif
- if (CONDTP_REGEX_PCRE == cond->type) {
-#ifdef CONFIG_REGEX_PCRE
- const char *error = NULL;
- int erroffset = 0;
- int options = 0;
-
- if (cond->flags & CONDF_IGNORECASE)
- options |= PCRE_CASELESS;
-
- cond->regex_pcre = pcre_compile(pos, options, &error, &erroffset,
- NULL);
- if (!cond->regex_pcre) {
- printf("Pattern \"%s\": PCRE regular expression parsing failed on "
- "offset %d: %s\n", pattern, erroffset, error);
- free(cond);
- return false;
- }
-#ifdef CONFIG_REGEX_PCRE_JIT
- cond->regex_pcre_extra = pcre_study(cond->regex_pcre, PCRE_STUDY_JIT_COMPILE, &error);
- if (!cond->regex_pcre_extra) {
- printf("Pattern \"%s\": PCRE regular expression study failed: %s",
- pattern, error);
- }
-#endif
+#ifdef CONFIG_C2
+ if (!c2_parse(ps, pcondlst, pattern))
+ exit(1);
+#else
+ printf_errfq(1, "(): Condition support not compiled in.");
#endif
- }
- else {
- cond->pattern = mstrcpy(pos);
- }
-
- // Insert it into the linked list
- cond->next = *pcondlst;
- *pcondlst = cond;
return true;
}
@@ -2313,7 +2089,7 @@ win_determine_shadow(session_t *ps, win *w) {
w->shadow = (UNSET == w->shadow_force ?
(ps->o.wintype_shadow[w->window_type]
- && !win_match(w, ps->o.shadow_blacklist, &w->cache_sblst)
+ && !win_match(ps, w, ps->o.shadow_blacklist, &w->cache_sblst)
&& !(ps->o.shadow_ignore_shaped && w->bounding_shaped
&& !w->rounded_corners)
&& !(ps->o.respect_prop_shadow && 0 == w->prop_shadow))
@@ -2347,7 +2123,7 @@ win_determine_invert_color(session_t *ps, win *w) {
if (UNSET != w->invert_color_force)
w->invert_color = w->invert_color_force;
else
- w->invert_color = win_match(w, ps->o.invert_color_list, &w->cache_ivclst);
+ w->invert_color = win_match(ps, w, ps->o.invert_color_list, &w->cache_ivclst);
if (w->invert_color != invert_color_old)
add_damage_win(ps, w);
@@ -2361,13 +2137,15 @@ win_on_wtype_change(session_t *ps, win *w) {
win_determine_shadow(ps, w);
win_determine_fade(ps, w);
win_update_focused(ps, w);
+ if (ps->o.invert_color_list)
+ win_determine_invert_color(ps, w);
}
/**
* Function to be called on window data changes.
*/
static void
-win_on_wdata_change(session_t *ps, win *w) {
+win_on_factor_change(session_t *ps, win *w) {
if (ps->o.shadow_blacklist)
win_determine_shadow(ps, w);
if (ps->o.fade_blacklist)
@@ -2481,9 +2259,11 @@ win_mark_client(session_t *ps, win *w, Window client) {
win_get_name(ps, w);
win_get_class(ps, w);
win_get_role(ps, w);
- win_on_wdata_change(ps, w);
}
+ // Update everything related to conditions
+ win_on_factor_change(ps, w);
+
// Update window focus state
win_update_focused(ps, w);
}
@@ -3048,7 +2828,7 @@ win_update_focused(session_t *ps, win *w) {
|| (ps->o.mark_wmwin_focused && w->wmwin)
|| (ps->o.mark_ovredir_focused
&& w->id == w->client_win && !w->wmwin)
- || win_match(w, ps->o.focus_blacklist, &w->cache_fcblst))
+ || win_match(ps, w, ps->o.focus_blacklist, &w->cache_fcblst))
w->focused = true;
// If window grouping detection is enabled, mark the window active if
@@ -3102,6 +2882,9 @@ win_set_focused(session_t *ps, win *w, bool focused) {
else {
win_update_focused(ps, w);
}
+
+ // Update everything related to conditions
+ win_on_factor_change(ps, w);
}
}
/**
@@ -3153,6 +2936,9 @@ win_set_leader(session_t *ps, win *w, Window nleader) {
else {
win_update_focused(ps, w);
}
+
+ // Update everything related to conditions
+ win_on_factor_change(ps, w);
}
}
@@ -3746,7 +3532,7 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) {
&& (ps->atom_name == ev->atom || ps->atom_name_ewmh == ev->atom)) {
win *w = find_toplevel(ps, ev->window);
if (w && 1 == win_get_name(ps, w)) {
- win_on_wdata_change(ps, w);
+ win_on_factor_change(ps, w);
}
}
@@ -3755,7 +3541,7 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) {
win *w = find_toplevel(ps, ev->window);
if (w) {
win_get_class(ps, w);
- win_on_wdata_change(ps, w);
+ win_on_factor_change(ps, w);
}
}
@@ -3763,7 +3549,7 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) {
if (ps->o.track_wdata && ps->atom_role == ev->atom) {
win *w = find_toplevel(ps, ev->window);
if (w && 1 == win_get_role(ps, w)) {
- win_on_wdata_change(ps, w);
+ win_on_factor_change(ps, w);
}
}
@@ -3790,7 +3576,7 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) {
if (!w)
w = find_toplevel(ps, ev->window);
if (w)
- win_on_wdata_change(ps, w);
+ win_on_factor_change(ps, w);
break;
}
}
@@ -4095,24 +3881,7 @@ usage(void) {
" inverted color. Resource-hogging, and is not well tested.\n"
"--dbus\n"
" Enable remote control via D-Bus. See the D-BUS API section in the\n"
- " man page for more details.\n"
- "\n"
- "Format of a condition:\n"
- "\n"
- " condition = <target>:<type>[<flags>]:<pattern>\n"
- "\n"
- " <target> is one of \"n\" (window name), \"i\" (window class\n"
- " instance), \"g\" (window general class), and \"r\"\n"
- " (window role).\n"
- "\n"
- " <type> is one of \"e\" (exact match), \"a\" (match anywhere),\n"
- " \"s\" (match from start), \"w\" (wildcard), and \"p\" (PCRE\n"
- " regular expressions, if compiled with the support).\n"
- "\n"
- " <flags> could be a series of flags. Currently the only defined\n"
- " flag is \"i\" (ignore case).\n"
- "\n"
- " <pattern> is the actual pattern string.\n";
+ " man page for more details.\n";
fputs(usage_text , stderr);
exit(1);
@@ -4270,8 +4039,6 @@ open_config_file(char *cpath, char **ppath) {
f = fopen(path, "r");
if (f && ppath)
*ppath = path;
- else
- free(path);
return f;
}
@@ -4356,7 +4123,7 @@ parse_vsync(session_t *ps, const char *optarg) {
* Parse a condition list in configuration file.
*/
static void
-parse_cfg_condlst(const config_t *pcfg, wincond_t **pcondlst,
+parse_cfg_condlst(session_t *ps, const config_t *pcfg, c2_lptr_t **pcondlst,
const char *name) {
config_setting_t *setting = config_lookup(pcfg, name);
if (setting) {
@@ -4364,12 +4131,12 @@ parse_cfg_condlst(const config_t *pcfg, wincond_t **pcondlst,
if (config_setting_is_array(setting)) {
int i = config_setting_length(setting);
while (i--) {
- condlst_add(pcondlst, config_setting_get_string_elem(setting, i));
+ condlst_add(ps, pcondlst, config_setting_get_string_elem(setting, i));
}
}
// Treat it as a single pattern if it's a string
else if (CONFIG_TYPE_STRING == config_setting_type(setting)) {
- condlst_add(pcondlst, config_setting_get_string(setting));
+ condlst_add(ps, pcondlst, config_setting_get_string(setting));
}
}
}
@@ -4378,7 +4145,7 @@ parse_cfg_condlst(const config_t *pcfg, wincond_t **pcondlst,
* Parse a configuration file from default location.
*/
static void
-parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp) {
+parse_config(session_t *ps, struct options_tmp *pcfgtmp) {
char *path = NULL;
FILE *f;
config_t cfg;
@@ -4386,10 +4153,14 @@ parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp) {
double dval = 0.0;
const char *sval = NULL;
- f = open_config_file(cpath, &path);
+ f = open_config_file(ps->o.config_file, &path);
if (!f) {
- if (cpath)
- printf_errfq(1, "(): Failed to read the specified configuration file.");
+ if (ps->o.config_file) {
+ printf_errfq(1, "(): Failed to read configuration file \"%s\".",
+ ps->o.config_file);
+ free(ps->o.config_file);
+ ps->o.config_file = NULL;
+ }
return;
}
@@ -4417,7 +4188,10 @@ parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp) {
}
config_set_auto_convert(&cfg, 1);
- free(path);
+ if (path != ps->o.config_file) {
+ free(ps->o.config_file);
+ ps->o.config_file = path;
+ }
// Get options from the configuration file. We don't do range checking
// right now. It will be done later
@@ -4512,11 +4286,11 @@ parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp) {
lcfg_lookup_bool(&cfg, "detect-client-leader",
&ps->o.detect_client_leader);
// --shadow-exclude
- parse_cfg_condlst(&cfg, &ps->o.shadow_blacklist, "shadow-exclude");
+ parse_cfg_condlst(ps, &cfg, &ps->o.shadow_blacklist, "shadow-exclude");
// --focus-exclude
- parse_cfg_condlst(&cfg, &ps->o.focus_blacklist, "focus-exclude");
+ parse_cfg_condlst(ps, &cfg, &ps->o.focus_blacklist, "focus-exclude");
// --invert-color-include
- parse_cfg_condlst(&cfg, &ps->o.invert_color_list, "invert-color-include");
+ parse_cfg_condlst(ps, &cfg, &ps->o.invert_color_list, "invert-color-include");
// --blur-background
lcfg_lookup_bool(&cfg, "blur-background", &ps->o.blur_background);
// --blur-background-frame
@@ -4554,7 +4328,7 @@ parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp) {
* Process arguments and configuration files.
*/
static void
-get_cfg(session_t *ps, int argc, char *const *argv) {
+get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) {
const static char *shortopts = "D:I:O:d:r:o:m:l:t:i:e:hscnfFCaSzGb";
const static struct option longopts[] = {
{ "help", no_argument, NULL, 'h' },
@@ -4595,14 +4369,33 @@ get_cfg(session_t *ps, int argc, char *const *argv) {
{ NULL, 0, NULL, 0 },
};
+ int o = 0, longopt_idx = -1, i = 0;
+
+ if (first_pass) {
+ // Pre-parse the commandline arguments to check for --config and invalid
+ // switches
+ // Must reset optind to 0 here in case we reread the commandline
+ // arguments
+ optind = 1;
+ while (-1 !=
+ (o = getopt_long(argc, argv, shortopts, longopts, &longopt_idx))) {
+ if (256 == o)
+ ps->o.config_file = mstrcpy(optarg);
+ else if ('d' == o)
+ ps->o.display = mstrcpy(optarg);
+ else if ('?' == o || ':' == o)
+ usage();
+ }
+
+ return;
+ }
+
struct options_tmp cfgtmp = {
.no_dock_shadow = false,
.no_dnd_shadow = false,
.menu_opacity = 1.0,
};
bool shadow_enable = false, fading_enable = false;
- int o, longopt_idx, i;
- char *config_file = NULL;
char *lc_numeric_old = mstrcpy(setlocale(LC_NUMERIC, NULL));
for (i = 0; i < NUM_WINTYPES; ++i) {
@@ -4611,21 +4404,8 @@ get_cfg(session_t *ps, int argc, char *const *argv) {
ps->o.wintype_opacity[i] = 1.0;
}
- // Pre-parse the commandline arguments to check for --config and invalid
- // switches
- // Must reset optind to 0 here in case we reread the commandline
- // arguments
- optind = 1;
- while (-1 !=
- (o = getopt_long(argc, argv, shortopts, longopts, &longopt_idx))) {
- if (256 == o)
- config_file = mstrcpy(optarg);
- else if ('?' == o || ':' == o)
- usage();
- }
-
#ifdef CONFIG_LIBCONFIG
- parse_config(ps, config_file, &cfgtmp);
+ parse_config(ps, &cfgtmp);
#endif
// Parse commandline arguments. Range checking will be done later.
@@ -4643,7 +4423,6 @@ get_cfg(session_t *ps, int argc, char *const *argv) {
usage();
break;
case 'd':
- ps->o.display = mstrcpy(optarg);
break;
case 'D':
ps->o.fade_delta = atoi(optarg);
@@ -4732,7 +4511,7 @@ get_cfg(session_t *ps, int argc, char *const *argv) {
break;
case 263:
// --shadow-exclude
- condlst_add(&ps->o.shadow_blacklist, optarg);
+ condlst_add(ps, &ps->o.shadow_blacklist, optarg);
break;
case 264:
// --mark-ovredir-focused
@@ -4796,7 +4575,7 @@ get_cfg(session_t *ps, int argc, char *const *argv) {
break;
case 279:
// --focus-exclude
- condlst_add(&ps->o.focus_blacklist, optarg);
+ condlst_add(ps, &ps->o.focus_blacklist, optarg);
break;
case 280:
// --inactive-dim-fixed
@@ -4832,7 +4611,7 @@ get_cfg(session_t *ps, int argc, char *const *argv) {
break;
case 288:
// --invert-color-include
- condlst_add(&ps->o.invert_color_list, optarg);
+ condlst_add(ps, &ps->o.invert_color_list, optarg);
break;
default:
usage();
@@ -4884,11 +4663,6 @@ get_cfg(session_t *ps, int argc, char *const *argv) {
ps->o.track_focus = true;
}
- // Determine whether we need to track window name and class
- if (ps->o.shadow_blacklist || ps->o.fade_blacklist
- || ps->o.focus_blacklist || ps->o.invert_color_list)
- ps->o.track_wdata = true;
-
// Determine whether we track window grouping
if (ps->o.detect_transient || ps->o.detect_client_leader) {
ps->o.track_leader = true;
@@ -5702,7 +5476,8 @@ session_init(session_t *ps_old, int argc, char **argv) {
ps->o.wintype_focus[WINTYPE_NORMAL] = false;
ps->o.wintype_focus[WINTYPE_UTILITY] = false;
- get_cfg(ps, argc, argv);
+ // First pass
+ get_cfg(ps, argc, argv, true);
// Inherit old Display if possible, primarily for resource leak checking
if (ps_old && ps_old->dpy)
@@ -5712,11 +5487,13 @@ session_init(session_t *ps_old, int argc, char **argv) {
if (!ps->dpy) {
ps->dpy = XOpenDisplay(ps->o.display);
if (!ps->dpy) {
- fprintf(stderr, "Can't open display\n");
- exit(1);
+ printf_errfq(1, "(): Can't open display.");
}
}
+ // Second pass
+ get_cfg(ps, argc, argv, false);
+
XSetErrorHandler(error);
if (ps->o.synchronize) {
XSynchronize(ps->dpy, 1);
@@ -5973,11 +5750,24 @@ session_destroy(session_t *ps) {
ps->alpha_picts = NULL;
}
+#ifdef CONFIG_C2
// Free blacklists
free_wincondlst(&ps->o.shadow_blacklist);
free_wincondlst(&ps->o.fade_blacklist);
free_wincondlst(&ps->o.focus_blacklist);
free_wincondlst(&ps->o.invert_color_list);
+#endif
+
+ // Free tracked atom list
+ {
+ latom_t *next = NULL;
+ for (latom_t *this = ps->track_atom_lst; this; this = next) {
+ next = this->next;
+ free(this);
+ }
+
+ ps->track_atom_lst = NULL;
+ }
// Free ignore linked list
{
@@ -6025,6 +5815,7 @@ session_destroy(session_t *ps) {
free(ps->gaussian_map);
free(ps->o.display);
free(ps->o.logpath);
+ free(ps->o.config_file);
free(ps->pfds_read);
free(ps->pfds_write);
free(ps->pfds_except);
@@ -6164,11 +5955,6 @@ main(int argc, char **argv) {
printf_errf("Failed to create new session.");
return 1;
}
-#ifdef DEBUG_C2
- // c2_parse(ps_g, NULL, "name ~= \"master\"");
- // c2_parse(ps_g, NULL, "n:e:Notification");
- c2_parse(ps_g, NULL, "(WM_NAME:16s = 'Notification' || class_g = 'fox') && class_g = 'fox'");
-#endif
session_run(ps_g);
ps_old = ps_g;
session_destroy(ps_g);
diff --git a/src/compton.h b/src/compton.h
index 7670424..75d517e 100644
--- a/src/compton.h
+++ b/src/compton.h
@@ -15,7 +15,6 @@
#include <unistd.h>
#include <getopt.h>
#include <locale.h>
-#include <fnmatch.h>
#include <signal.h>
#ifdef CONFIG_VSYNC_DRM
@@ -165,37 +164,16 @@ free_damage(session_t *ps, Damage *p) {
}
}
+#ifdef CONFIG_C2
/**
- * Destroy a <code>wincond_t</code>.
+ * Destroy a condition list.
*/
-inline static void
-free_wincond(wincond_t *cond) {
- if (cond->pattern)
- free(cond->pattern);
-#ifdef CONFIG_REGEX_PCRE
- if (cond->regex_pcre_extra)
- LPCRE_FREE_STUDY(cond->regex_pcre_extra);
- if (cond->regex_pcre)
- pcre_free(cond->regex_pcre);
-#endif
- free(cond);
-}
-
-/**
- * Destroy a linked list of <code>wincond_t</code>.
- */
-inline static void
-free_wincondlst(wincond_t **cond_lst) {
- wincond_t *next = NULL;
-
- for (wincond_t *cond = *cond_lst; cond; cond = next) {
- next = cond->next;
-
- free_wincond(cond);
- }
-
- *cond_lst = NULL;
+static inline void
+free_wincondlst(c2_lptr_t **pcondlst) {
+ while ((*pcondlst = c2_free_lptr(*pcondlst)))
+ continue;
}
+#endif
/**
* Destroy all resources in a <code>struct _win</code>.
@@ -396,14 +374,20 @@ win_is_fullscreen(session_t *ps, const win *w) {
static void
win_rounded_corners(session_t *ps, win *w);
-static bool
-win_match_once(win *w, const wincond_t *cond);
-
-static bool
-win_match(win *w, wincond_t *condlst, wincond_t * *cache);
+/**
+ * Wrapper of c2_match().
+ */
+static inline bool
+win_match(session_t *ps, win *w, c2_lptr_t *condlst, const c2_lptr_t **cache) {
+#ifdef CONFIG_C2
+ return c2_match(ps, w, condlst, cache);
+#else
+ return false;
+#endif
+}
static bool
-condlst_add(wincond_t **pcondlst, const char *pattern);
+condlst_add(session_t *ps, c2_lptr_t **pcondlst, const char *pattern);
static long
determine_evmask(session_t *ps, Window wid, win_evmode_t mode);
@@ -594,7 +578,7 @@ static void
win_on_wtype_change(session_t *ps, win *w);
static void
-win_on_wdata_change(session_t *ps, win *w);
+win_on_factor_change(session_t *ps, win *w);
static void
win_upd_run(session_t *ps, win *w, win_upd_t *pupd);
@@ -858,15 +842,15 @@ static FILE *
open_config_file(char *cpath, char **path);
static void
-parse_cfg_condlst(const config_t *pcfg, wincond_t **pcondlst,
+parse_cfg_condlst(session_t *ps, const config_t *pcfg, c2_lptr_t **pcondlst,
const char *name);
static void
-parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp);
+parse_config(session_t *ps, struct options_tmp *pcfgtmp);
#endif
static void
-get_cfg(session_t *ps, int argc, char *const *argv);
+get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass);
static void
init_atoms(session_t *ps);
diff --git a/src/dbus.c b/src/dbus.c
index a86ed17..44f1369 100644
--- a/src/dbus.c
+++ b/src/dbus.c
@@ -859,6 +859,7 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg) {
return true;
}
+ cdbus_m_opts_get_do(config_file, cdbus_reply_string);
cdbus_m_opts_get_do(mark_wmwin_focused, cdbus_reply_bool);
cdbus_m_opts_get_do(mark_ovredir_focused, cdbus_reply_bool);
cdbus_m_opts_get_do(fork_after_register, cdbus_reply_bool);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment