Last active
December 10, 2015 09:18
-
-
Save richardgv/4413196 to your computer and use it in GitHub Desktop.
chjj/compton #56: My work in progress (NOT USABLE right now!)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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