c2: remove type specifiers

Same logic as the format specifiers, we just use what the X server
reports, asking the user for the type is just going to be confusing.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2024-02-17 23:01:29 +00:00
parent a7d599e72d
commit 198da194e8
No known key found for this signature in database
GPG Key ID: D3A4405BE6CC17F4
5 changed files with 109 additions and 195 deletions

View File

@ -7,7 +7,7 @@
## Notable changes ## Notable changes
* Marginally improve performance when resizing/opening/closing windows. (#1190) * Marginally improve performance when resizing/opening/closing windows. (#1190)
* Format specifiers are no longer used in rules. Format specifier is the number you put after the colon (':') in rules, e.g. the `32` in `"_GTK_FRAME_EXTENTS@:32c"`. Now this information is ignored and the property is matched regardless of format. * Type and format specifiers are no longer used in rules. These specifiers are what you put after the colon (':') in rules, e.g. the `:32c` in `"_GTK_FRAME_EXTENTS@:32c"`. Now this information is ignored and the property is matched regardless of format or type.
## Dependency changes ## Dependency changes

View File

@ -286,15 +286,15 @@ Some options accept a condition string to match certain windows. A condition str
A condition with "exists" operator looks like this: A condition with "exists" operator looks like this:
<NEGATION> <TARGET> <CLIENT/FRAME> [<INDEX>] : <TYPE> <NEGATION> <TARGET> <CLIENT/FRAME> [<INDEX>]
With equals operator it looks like: With equals operator it looks like:
<NEGATION> <TARGET> <CLIENT/FRAME> [<INDEX>] : <TYPE> <NEGATION> <OP QUALIFIER> <MATCH TYPE> = <PATTERN> <NEGATION> <TARGET> <CLIENT/FRAME> [<INDEX>] <NEGATION> <OP QUALIFIER> <MATCH TYPE> = <PATTERN>
With greater-than/less-than operators it looks like: With greater-than/less-than operators it looks like:
<NEGATION> <TARGET> <CLIENT/FRAME> [<INDEX>] : <TYPE> <NEGATION> <OPERATOR> <PATTERN> <NEGATION> <TARGET> <CLIENT/FRAME> [<INDEX>] <NEGATION> <OPERATOR> <PATTERN>
'NEGATION' (optional) is one or more exclamation marks; 'NEGATION' (optional) is one or more exclamation marks;
@ -304,8 +304,6 @@ With greater-than/less-than operators it looks like:
'INDEX' (optional) is the index number of the property to look up. For example, `[2]` means look at the third value in the property. If not specified, the first value (index `[0]`) is used implicitly. Use the special value `[*]` to perform matching against all available property values using logical OR. Do not specify it for predefined targets. 'INDEX' (optional) is the index number of the property to look up. For example, `[2]` means look at the third value in the property. If not specified, the first value (index `[0]`) is used implicitly. Use the special value `[*]` to perform matching against all available property values using logical OR. Do not specify it for predefined 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). '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). 'MATCH TYPE' (optional), applicable only for equals operator, could be nothing (exact match), `*` (match anywhere), `^` (match from start), `%` (wildcard), or `~` (PCRE regular expression).
@ -328,24 +326,24 @@ Examples:
override_redirect != 1 override_redirect != 1
# If the window is a menu # If the window is a menu
window_type *= "menu" window_type *= "menu"
_NET_WM_WINDOW_TYPE@:a *= "MENU" _NET_WM_WINDOW_TYPE@ *= "MENU"
# If the window is marked hidden: _NET_WM_STATE contains _NET_WM_STATE_HIDDEN # If the window is marked hidden: _NET_WM_STATE contains _NET_WM_STATE_HIDDEN
_NET_WM_STATE@[*]:a = "_NET_WM_STATE_HIDDEN" _NET_WM_STATE@[*] = "_NET_WM_STATE_HIDDEN"
# If the window is marked sticky: _NET_WM_STATE contains an atom that contains # If the window is marked sticky: _NET_WM_STATE contains an atom that contains
# "sticky", ignore case # "sticky", ignore case
_NET_WM_STATE@[*]:a *?= "sticky" _NET_WM_STATE@[*] *?= "sticky"
# If the window name contains "Firefox", ignore case # If the window name contains "Firefox", ignore case
name *?= "Firefox" name *?= "Firefox"
_NET_WM_NAME@:s *?= "Firefox" _NET_WM_NAME@ *?= "Firefox"
# If the window name ends with "Firefox" # If the window name ends with "Firefox"
name %= "*Firefox" name %= "*Firefox"
name ~= "Firefox$" name ~= "Firefox$"
# If the window has a property _COMPTON_SHADOW with value 0, type CARDINAL, # If the window has a property _COMPTON_SHADOW with value 0, type CARDINAL,
# format 32, value 0, on its frame window # format 32, value 0, on its frame window
_COMPTON_SHADOW:32c = 0 _COMPTON_SHADOW = 0
# If the third value of _NET_FRAME_EXTENTS is less than 20, or there's no # 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 property on client window
_NET_FRAME_EXTENTS@[2]:32c < 20 || !_NET_FRAME_EXTENTS@:32c _NET_FRAME_EXTENTS@[2] < 20 || !_NET_FRAME_EXTENTS@
# The pattern here will be parsed as "dd4" # The pattern here will be parsed as "dd4"
name = "\x64\x64\o64" name = "\x64\x64\o64"

274
src/c2.c
View File

@ -66,7 +66,6 @@ static_assert(sizeof(struct c2_tracked_property_key) == 8, "Padding bytes in "
struct c2_tracked_property { struct c2_tracked_property {
UT_hash_handle hh; UT_hash_handle hh;
struct c2_tracked_property_key key; struct c2_tracked_property_key key;
bool is_string;
}; };
struct c2_state { struct c2_state {
@ -150,14 +149,6 @@ struct _c2_l {
C2_L_PCLASSI, C2_L_PCLASSI,
C2_L_PROLE, C2_L_PROLE,
} predef; } predef;
enum c2_l_type {
C2_L_TUNDEFINED,
C2_L_TSTRING,
C2_L_TCARDINAL,
C2_L_TWINDOW,
C2_L_TATOM,
C2_L_TDRAWABLE,
} type;
enum { enum {
C2_L_PTUNDEFINED, C2_L_PTUNDEFINED,
C2_L_PTSTRING, C2_L_PTSTRING,
@ -172,12 +163,12 @@ struct _c2_l {
}; };
/// Initializer for c2_l_t. /// Initializer for c2_l_t.
#define C2_L_INIT \ #define C2_L_INIT \
{ \ { \
.neg = false, .op = C2_L_OEXISTS, .match = C2_L_MEXACT, \ .neg = false, .op = C2_L_OEXISTS, .match = C2_L_MEXACT, \
.match_ignorecase = false, .tgt = NULL, .tgtatom = 0, .tgt_onframe = false, \ .match_ignorecase = false, .tgt = NULL, .tgtatom = 0, \
.predef = C2_L_PUNDEFINED, .index = 0, .type = C2_L_TUNDEFINED, \ .tgt_onframe = false, .predef = C2_L_PUNDEFINED, .index = 0, \
.ptntype = C2_L_PTUNDEFINED, .ptnstr = NULL, .ptnint = 0, \ .ptntype = C2_L_PTUNDEFINED, .ptnstr = NULL, .ptnint = 0, \
} }
static const c2_l_t leaf_def = C2_L_INIT; static const c2_l_t leaf_def = C2_L_INIT;
@ -196,35 +187,34 @@ struct _c2_lptr {
/// Structure representing a predefined target. /// Structure representing a predefined target.
typedef struct { typedef struct {
const char *name; const char *name;
enum c2_l_type type;
} c2_predef_t; } c2_predef_t;
// Predefined targets. // Predefined targets.
static const c2_predef_t C2_PREDEFS[] = { static const char *C2_PREDEFS[] = {
[C2_L_PID] = {"id", C2_L_TCARDINAL}, [C2_L_PID] = "id",
[C2_L_PX] = {"x", C2_L_TCARDINAL}, [C2_L_PX] = "x",
[C2_L_PY] = {"y", C2_L_TCARDINAL}, [C2_L_PY] = "y",
[C2_L_PX2] = {"x2", C2_L_TCARDINAL}, [C2_L_PX2] = "x2",
[C2_L_PY2] = {"y2", C2_L_TCARDINAL}, [C2_L_PY2] = "y2",
[C2_L_PWIDTH] = {"width", C2_L_TCARDINAL}, [C2_L_PWIDTH] = "width",
[C2_L_PHEIGHT] = {"height", C2_L_TCARDINAL}, [C2_L_PHEIGHT] = "height",
[C2_L_PWIDTHB] = {"widthb", C2_L_TCARDINAL}, [C2_L_PWIDTHB] = "widthb",
[C2_L_PHEIGHTB] = {"heightb", C2_L_TCARDINAL}, [C2_L_PHEIGHTB] = "heightb",
[C2_L_PBDW] = {"border_width", C2_L_TCARDINAL}, [C2_L_PBDW] = "border_width",
[C2_L_PFULLSCREEN] = {"fullscreen", C2_L_TCARDINAL}, [C2_L_PFULLSCREEN] = "fullscreen",
[C2_L_POVREDIR] = {"override_redirect", C2_L_TCARDINAL}, [C2_L_POVREDIR] = "override_redirect",
[C2_L_PARGB] = {"argb", C2_L_TCARDINAL}, [C2_L_PARGB] = "argb",
[C2_L_PFOCUSED] = {"focused", C2_L_TCARDINAL}, [C2_L_PFOCUSED] = "focused",
[C2_L_PWMWIN] = {"wmwin", C2_L_TCARDINAL}, [C2_L_PWMWIN] = "wmwin",
[C2_L_PBSHAPED] = {"bounding_shaped", C2_L_TCARDINAL}, [C2_L_PBSHAPED] = "bounding_shaped",
[C2_L_PROUNDED] = {"rounded_corners", C2_L_TCARDINAL}, [C2_L_PROUNDED] = "rounded_corners",
[C2_L_PCLIENT] = {"client", C2_L_TWINDOW}, [C2_L_PCLIENT] = "client",
[C2_L_PWINDOWTYPE] = {"window_type", C2_L_TSTRING}, [C2_L_PWINDOWTYPE] = "window_type",
[C2_L_PLEADER] = {"leader", C2_L_TWINDOW}, [C2_L_PLEADER] = "leader",
[C2_L_PNAME] = {"name", C2_L_TSTRING}, [C2_L_PNAME] = "name",
[C2_L_PCLASSG] = {"class_g", C2_L_TSTRING}, [C2_L_PCLASSG] = "class_g",
[C2_L_PCLASSI] = {"class_i", C2_L_TSTRING}, [C2_L_PCLASSI] = "class_i",
[C2_L_PROLE] = {"role", C2_L_TSTRING}, [C2_L_PROLE] = "role",
}; };
/** /**
@ -349,11 +339,7 @@ static inline void c2_freep(c2_ptr_t *pp) {
static const char *c2h_dump_str_tgt(const c2_l_t *pleaf); static const char *c2h_dump_str_tgt(const c2_l_t *pleaf);
static const char *c2h_dump_str_type(const c2_l_t *pleaf); static bool c2_match_once(session_t *ps, const struct managed_win *w, c2_ptr_t cond);
static xcb_atom_t c2_get_atom_type(const c2_l_t *pleaf);
static bool c2_match_once(session_t *ps, const struct managed_win *w, const c2_ptr_t cond);
/** /**
* Parse a condition string. * Parse a condition string.
@ -413,7 +399,7 @@ TEST_CASE(c2_parse) {
TEST_STREQUAL(cond->ptr.l->ptnstr, "xterm"); TEST_STREQUAL(cond->ptr.l->ptnstr, "xterm");
size_t len = c2_condition_to_str(cond->ptr, str, sizeof(str)); size_t len = c2_condition_to_str(cond->ptr, str, sizeof(str));
TEST_STREQUAL3(str, "name:s = \"xterm\"", len); TEST_STREQUAL3(str, "name = \"xterm\"", len);
c2_list_free(&cond, NULL); c2_list_free(&cond, NULL);
cond = c2_parse(NULL, "_GTK_FRAME_EXTENTS@:c", NULL); cond = c2_parse(NULL, "_GTK_FRAME_EXTENTS@:c", NULL);
@ -423,13 +409,12 @@ TEST_CASE(c2_parse) {
TEST_EQUAL(cond->ptr.l->op, C2_L_OEXISTS); TEST_EQUAL(cond->ptr.l->op, C2_L_OEXISTS);
TEST_EQUAL(cond->ptr.l->match, C2_L_MEXACT); TEST_EQUAL(cond->ptr.l->match, C2_L_MEXACT);
TEST_EQUAL(cond->ptr.l->predef, C2_L_PUNDEFINED); TEST_EQUAL(cond->ptr.l->predef, C2_L_PUNDEFINED);
TEST_EQUAL(cond->ptr.l->type, C2_L_TCARDINAL);
TEST_TRUE(cond->ptr.l->tgt_onframe); TEST_TRUE(cond->ptr.l->tgt_onframe);
TEST_NOTEQUAL(cond->ptr.l->tgt, NULL); TEST_NOTEQUAL(cond->ptr.l->tgt, NULL);
TEST_STREQUAL(cond->ptr.l->tgt, "_GTK_FRAME_EXTENTS"); TEST_STREQUAL(cond->ptr.l->tgt, "_GTK_FRAME_EXTENTS");
len = c2_condition_to_str(cond->ptr, str, sizeof(str)); len = c2_condition_to_str(cond->ptr, str, sizeof(str));
TEST_STREQUAL3(str, "_GTK_FRAME_EXTENTS@[0]:c", len); TEST_STREQUAL3(str, "_GTK_FRAME_EXTENTS@[0]", len);
c2_list_free(&cond, NULL); c2_list_free(&cond, NULL);
cond = c2_parse(NULL, "name = \"xterm\" && class_g *= \"XTerm\"", NULL); cond = c2_parse(NULL, "name = \"xterm\" && class_g *= \"XTerm\"", NULL);
@ -451,34 +436,33 @@ TEST_CASE(c2_parse) {
TEST_EQUAL(cond->ptr.b->opr2.l->predef, C2_L_PCLASSG); TEST_EQUAL(cond->ptr.b->opr2.l->predef, C2_L_PCLASSG);
len = c2_condition_to_str(cond->ptr, str, sizeof(str)); len = c2_condition_to_str(cond->ptr, str, sizeof(str));
TEST_STREQUAL3(str, "(name:s = \"xterm\" && class_g:s *= \"XTerm\")", len); TEST_STREQUAL3(str, "(name = \"xterm\" && class_g *= \"XTerm\")", len);
c2_list_free(&cond, NULL); c2_list_free(&cond, NULL);
cond = c2_parse(NULL, "_NET_WM_STATE[1]:32a *='_NET_WM_STATE_HIDDEN'", NULL); cond = c2_parse(NULL, "_NET_WM_STATE[1]:32a *='_NET_WM_STATE_HIDDEN'", NULL);
TEST_EQUAL(cond->ptr.l->index, 1); TEST_EQUAL(cond->ptr.l->index, 1);
TEST_EQUAL(cond->ptr.l->type, C2_L_TATOM);
TEST_STREQUAL(cond->ptr.l->tgt, "_NET_WM_STATE"); TEST_STREQUAL(cond->ptr.l->tgt, "_NET_WM_STATE");
TEST_STREQUAL(cond->ptr.l->ptnstr, "_NET_WM_STATE_HIDDEN"); TEST_STREQUAL(cond->ptr.l->ptnstr, "_NET_WM_STATE_HIDDEN");
len = c2_condition_to_str(cond->ptr, str, sizeof(str)); len = c2_condition_to_str(cond->ptr, str, sizeof(str));
TEST_STREQUAL3(str, "_NET_WM_STATE[1]:a *= \"_NET_WM_STATE_HIDDEN\"", len); TEST_STREQUAL3(str, "_NET_WM_STATE[1] *= \"_NET_WM_STATE_HIDDEN\"", len);
c2_list_free(&cond, NULL); c2_list_free(&cond, NULL);
cond = c2_parse(NULL, "_NET_WM_STATE[*]:32a*='_NET_WM_STATE_HIDDEN'", NULL); cond = c2_parse(NULL, "_NET_WM_STATE[*]:32a*='_NET_WM_STATE_HIDDEN'", NULL);
TEST_EQUAL(cond->ptr.l->index, -1); TEST_EQUAL(cond->ptr.l->index, -1);
len = c2_condition_to_str(cond->ptr, str, sizeof(str)); len = c2_condition_to_str(cond->ptr, str, sizeof(str));
TEST_STREQUAL3(str, "_NET_WM_STATE[*]:a *= \"_NET_WM_STATE_HIDDEN\"", len); TEST_STREQUAL3(str, "_NET_WM_STATE[*] *= \"_NET_WM_STATE_HIDDEN\"", len);
c2_list_free(&cond, NULL); c2_list_free(&cond, NULL);
cond = c2_parse(NULL, "!class_i:0s", NULL); cond = c2_parse(NULL, "!class_i:0s", NULL);
TEST_NOTEQUAL(cond, NULL); TEST_NOTEQUAL(cond, NULL);
len = c2_condition_to_str(cond->ptr, str, sizeof(str)); len = c2_condition_to_str(cond->ptr, str, sizeof(str));
TEST_STREQUAL3(str, "!class_i:s", len); TEST_STREQUAL3(str, "!class_i", len);
c2_list_free(&cond, NULL); c2_list_free(&cond, NULL);
cond = c2_parse(NULL, "_NET_WM_STATE = '_NET_WM_STATE_HIDDEN'", NULL); cond = c2_parse(NULL, "_NET_WM_STATE = '_NET_WM_STATE_HIDDEN'", NULL);
TEST_EQUAL(cond, NULL); TEST_NOTEQUAL(cond, NULL);
cond = c2_parse(NULL, "1A:\n1111111111111ar1", NULL); cond = c2_parse(NULL, "1A:\n1111111111111ar1", NULL);
TEST_EQUAL(cond, NULL); TEST_EQUAL(cond, NULL);
@ -492,8 +476,8 @@ TEST_CASE(c2_parse) {
cond = c2_parse(NULL, "!!!!!!!((((((!(((((,", NULL); cond = c2_parse(NULL, "!!!!!!!((((((!(((((,", NULL);
TEST_EQUAL(cond, NULL); TEST_EQUAL(cond, NULL);
const char *rule = "(((role:s = \"\\\\tg^\\n\\n[\\t\" && role:s ~?= \"\") && " const char *rule = "(((role = \"\\\\tg^\\n\\n[\\t\" && role ~?= \"\") && "
"role:s ~?= \"\\n\\n\\n\\b\\n^\\n*0bon\") && role:s ~?= " "role ~?= \"\\n\\n\\n\\b\\n^\\n*0bon\") && role ~?= "
"\"\\n\\n\\x8a\\b\\n^\\n*0\\n[\\n[\\n\\n\\b\\n\")"; "\"\\n\\n\\x8a\\b\\n^\\n*0\\n[\\n[\\n\\n\\b\\n\")";
cond = c2_parse(NULL, rule, NULL); cond = c2_parse(NULL, rule, NULL);
TEST_NOTEQUAL(cond, NULL); TEST_NOTEQUAL(cond, NULL);
@ -732,9 +716,8 @@ static int c2_parse_target(const char *pattern, int offset, c2_ptr_t *presult) {
// Check for predefined targets // Check for predefined targets
static const int npredefs = (int)(sizeof(C2_PREDEFS) / sizeof(C2_PREDEFS[0])); static const int npredefs = (int)(sizeof(C2_PREDEFS) / sizeof(C2_PREDEFS[0]));
for (int i = 0; i < npredefs; ++i) { for (int i = 0; i < npredefs; ++i) {
if (!strcmp(C2_PREDEFS[i].name, pleaf->tgt)) { if (!strcmp(C2_PREDEFS[i], pleaf->tgt)) {
pleaf->predef = i; pleaf->predef = i;
pleaf->type = C2_PREDEFS[i].type;
break; break;
} }
} }
@ -800,39 +783,27 @@ static int c2_parse_target(const char *pattern, int offset, c2_ptr_t *presult) {
// Look for format // Look for format
bool hasformat = false; bool hasformat = false;
long format = 0; long format = 0;
{ char *endptr = NULL;
char *endptr = NULL; format = strtol(pattern + offset, &endptr, 0);
format = strtol(pattern + offset, &endptr, 0); assert(endptr);
assert(endptr); hasformat = endptr && endptr != pattern + offset;
if ((hasformat = (endptr && endptr != pattern + offset))) { if (hasformat) {
offset = to_int_checked(endptr - pattern); offset = to_int_checked(endptr - pattern);
}
C2H_SKIP_SPACES();
} }
C2H_SKIP_SPACES();
// Look for type // Look for type
enum c2_l_type type = C2_L_TUNDEFINED;
switch (pattern[offset]) { switch (pattern[offset]) {
case 'w': type = C2_L_TWINDOW; break; case 'w':
case 'd': type = C2_L_TDRAWABLE; break; case 'd':
case 'c': type = C2_L_TCARDINAL; break; case 'c':
case 's': type = C2_L_TSTRING; break; case 's':
case 'a': type = C2_L_TATOM; break; case 'a': break;
default: c2_error("Invalid type character."); default: c2_error("Invalid type character.");
} }
if (type) { log_warn("Type \"%c\" specified on target \"%s\" will be ignored.",
if (pleaf->predef != C2_L_PUNDEFINED) { pattern[offset], pleaf->tgt);
log_warn("Type specified for a default target "
"will be ignored.");
} else {
if (pleaf->type && type != pleaf->type) {
log_warn("Default type overridden on "
"target.");
}
pleaf->type = type;
}
}
offset++; offset++;
C2H_SKIP_SPACES(); C2H_SKIP_SPACES();
@ -847,11 +818,6 @@ static int c2_parse_target(const char *pattern, int offset, c2_ptr_t *presult) {
} }
} }
} }
if (!pleaf->type) {
c2_error("Target type cannot be determined.");
}
return offset; return offset;
fail: fail:
@ -1046,21 +1012,14 @@ static int c2_parse_pattern(const char *pattern, int offset, c2_ptr_t *presult)
c2_error("Invalid pattern type."); c2_error("Invalid pattern type.");
} }
// Check if the type is correct if (pleaf->ptntype == C2_L_PTINT) {
if (!(((C2_L_TSTRING == pleaf->type || C2_L_TATOM == pleaf->type) && if (pleaf->match) {
C2_L_PTSTRING == pleaf->ptntype) || c2_error("Integer/boolean pattern cannot have operator "
((C2_L_TCARDINAL == pleaf->type || C2_L_TWINDOW == pleaf->type || "qualifiers.");
C2_L_TDRAWABLE == pleaf->type) && }
C2_L_PTINT == pleaf->ptntype))) { if (pleaf->match_ignorecase) {
c2_error("Pattern type incompatible with target type."); c2_error("Integer/boolean pattern cannot have flags.");
} }
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 && if (C2_L_PTSTRING == pleaf->ptntype &&
@ -1089,22 +1048,17 @@ static int c2_parse_legacy(const char *pattern, int offset, c2_ptr_t *presult) {
presult->isbranch = false; presult->isbranch = false;
presult->l = pleaf; presult->l = pleaf;
memcpy(pleaf, &leaf_def, sizeof(c2_l_t)); memcpy(pleaf, &leaf_def, sizeof(c2_l_t));
pleaf->type = C2_L_TSTRING;
pleaf->op = C2_L_OEQ; pleaf->op = C2_L_OEQ;
pleaf->ptntype = C2_L_PTSTRING; pleaf->ptntype = C2_L_PTSTRING;
// Determine the pattern target // Determine the pattern target
#define TGTFILL(pdefid) \
pleaf->predef = (pdefid); \
pleaf->type = C2_PREDEFS[pdefid].type;
switch (pattern[offset]) { switch (pattern[offset]) {
case 'n': TGTFILL(C2_L_PNAME); break; case 'n': pleaf->predef = C2_L_PNAME; break;
case 'i': TGTFILL(C2_L_PCLASSI); break; case 'i': pleaf->predef = C2_L_PCLASSI; break;
case 'g': TGTFILL(C2_L_PCLASSG); break; case 'g': pleaf->predef = C2_L_PCLASSG; break;
case 'r': TGTFILL(C2_L_PROLE); break; case 'r': pleaf->predef = C2_L_PROLE; break;
default: c2_error("Target \"%c\" invalid.\n", pattern[offset]); default: c2_error("Target \"%c\" invalid.\n", pattern[offset]);
} }
#undef TGTFILL
offset += 2; offset += 2;
@ -1144,11 +1098,6 @@ fail:
* Do postprocessing on a condition leaf. * Do postprocessing on a condition leaf.
*/ */
static bool c2_l_postprocess(struct c2_state *state, xcb_connection_t *c, c2_l_t *pleaf) { static bool c2_l_postprocess(struct c2_state *state, xcb_connection_t *c, 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 // Get target atom if it's not a predefined one
if (pleaf->predef == C2_L_PUNDEFINED) { if (pleaf->predef == C2_L_PUNDEFINED) {
pleaf->tgtatom = get_atom_with_nul(state->atoms, pleaf->tgt, c); pleaf->tgtatom = get_atom_with_nul(state->atoms, pleaf->tgt, c);
@ -1171,18 +1120,6 @@ static bool c2_l_postprocess(struct c2_state *state, xcb_connection_t *c, c2_l_t
property->key = key; property->key = key;
HASH_ADD_KEYPTR(hh, state->tracked_properties, &property->key, HASH_ADD_KEYPTR(hh, state->tracked_properties, &property->key,
sizeof(property->key), property); sizeof(property->key), property);
property->is_string = pleaf->type == C2_L_TSTRING;
} else {
if (property->is_string != (pleaf->type == C2_L_TSTRING)) {
log_error("Type mismatch for property \"%s\", %s a "
"string, now %s. Offending rule is: %s, it "
"will be disabled.",
pleaf->tgt, property->is_string ? "was" : "wasn't",
pleaf->type == C2_L_TSTRING ? "is" : "isn't",
c2_condition_to_str2(
(c2_ptr_t){.isbranch = false, .l = pleaf}));
return false;
}
} }
} }
@ -1311,27 +1248,11 @@ c2_lptr_t *c2_free_lptr(c2_lptr_t *lp, c2_userdata_free f) {
*/ */
static const char *c2h_dump_str_tgt(const c2_l_t *pleaf) { static const char *c2h_dump_str_tgt(const c2_l_t *pleaf) {
if (pleaf->predef != C2_L_PUNDEFINED) { if (pleaf->predef != C2_L_PUNDEFINED) {
return C2_PREDEFS[pleaf->predef].name; return C2_PREDEFS[pleaf->predef];
} }
return pleaf->tgt; return pleaf->tgt;
} }
/**
* Get a string representation of a target.
*/
static const char *c2h_dump_str_type(const c2_l_t *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 to string. Return the number of characters that * Dump a condition tree to string. Return the number of characters that
* would have been written if the buffer had been large enough, excluding * would have been written if the buffer had been large enough, excluding
@ -1416,10 +1337,6 @@ static size_t c2_condition_to_str(c2_ptr_t p, char *output, size_t len) {
} }
} }
const char *type_str = c2h_dump_str_type(pleaf);
push_char(':');
push_str(type_str);
if (C2_L_OEXISTS == pleaf->op) { if (C2_L_OEXISTS == pleaf->op) {
return offset; return offset;
} }
@ -1509,21 +1426,6 @@ static const char *c2_condition_to_str2(c2_ptr_t ptr) {
return buf; return buf;
} }
/**
* Get the type atom of a condition.
*/
static xcb_atom_t c2_get_atom_type(const c2_l_t *pleaf) {
switch (pleaf->type) {
case C2_L_TCARDINAL: return XCB_ATOM_CARDINAL;
case C2_L_TWINDOW: return XCB_ATOM_WINDOW;
case C2_L_TSTRING: return XCB_ATOM_STRING;
case C2_L_TATOM: return XCB_ATOM_ATOM;
case C2_L_TDRAWABLE: return XCB_ATOM_DRAWABLE;
default: assert(0); break;
}
unreachable();
}
/** /**
* Match a window against a single leaf window condition. * Match a window against a single leaf window condition.
* *
@ -1541,8 +1443,17 @@ static inline void c2_match_once_leaf(session_t *ps, const struct managed_win *w
} }
const int idx = (pleaf->index < 0 ? 0 : pleaf->index); const int idx = (pleaf->index < 0 ? 0 : pleaf->index);
auto type = pleaf->ptntype;
if (type == C2_L_PTUNDEFINED) {
auto prop_info = x_get_prop_info(&ps->c, wid, pleaf->tgtatom);
if (x_is_type_string(ps->atoms, prop_info.type)) {
type = C2_L_PTSTRING;
} else {
type = C2_L_PTINT;
}
}
switch (pleaf->ptntype) { switch (type) {
// Deal with integer patterns // Deal with integer patterns
case C2_L_PTINT: { case C2_L_PTINT: {
long long *targets = NULL; long long *targets = NULL;
@ -1590,9 +1501,8 @@ static inline void c2_match_once_leaf(session_t *ps, const struct managed_win *w
auto prop_info = x_get_prop_info(&ps->c, wid, pleaf->tgtatom); auto prop_info = x_get_prop_info(&ps->c, wid, pleaf->tgtatom);
word_count = to_int_checked((prop_info.length + 4 - 1) / 4); word_count = to_int_checked((prop_info.length + 4 - 1) / 4);
} }
winprop_t prop = winprop_t prop = x_get_prop_with_offset(
x_get_prop_with_offset(&ps->c, wid, pleaf->tgtatom, idx, &ps->c, wid, pleaf->tgtatom, idx, word_count, 0, 0);
word_count, c2_get_atom_type(pleaf), 0);
ntargets = (pleaf->index < 0 ? prop.nitems : min2(prop.nitems, 1)); ntargets = (pleaf->index < 0 ? prop.nitems : min2(prop.nitems, 1));
if (ntargets > 0) { if (ntargets > 0) {
@ -1642,6 +1552,10 @@ static inline void c2_match_once_leaf(session_t *ps, const struct managed_win *w
const char **targets_free = NULL; const char **targets_free = NULL;
const char **targets_free_inner = NULL; const char **targets_free_inner = NULL;
size_t ntargets = 0; size_t ntargets = 0;
winprop_info_t prop_info = {0};
if (pleaf->predef == C2_L_PUNDEFINED) {
prop_info = x_get_prop_info(&ps->c, wid, pleaf->tgtatom);
}
// A predefined target // A predefined target
const char *predef_target = NULL; const char *predef_target = NULL;
@ -1658,18 +1572,15 @@ static inline void c2_match_once_leaf(session_t *ps, const struct managed_win *w
} }
ntargets = 1; ntargets = 1;
targets = &predef_target; targets = &predef_target;
} } else if (prop_info.type == XCB_ATOM_ATOM) {
// An atom type property, convert it to string // An atom type property, convert it to string
else if (pleaf->type == C2_L_TATOM) {
int word_count = 1; int word_count = 1;
if (pleaf->index < 0) { if (pleaf->index < 0) {
// Get length of property in 32-bit multiples // Get length of property in 32-bit multiples
auto prop_info = x_get_prop_info(&ps->c, wid, pleaf->tgtatom);
word_count = to_int_checked((prop_info.length + 4 - 1) / 4); word_count = to_int_checked((prop_info.length + 4 - 1) / 4);
} }
winprop_t prop = winprop_t prop = x_get_prop_with_offset(
x_get_prop_with_offset(&ps->c, wid, pleaf->tgtatom, idx, &ps->c, wid, pleaf->tgtatom, idx, word_count, XCB_ATOM_ATOM, 0);
word_count, c2_get_atom_type(pleaf), 0);
ntargets = (pleaf->index < 0 ? prop.nitems : min2(prop.nitems, 1)); ntargets = (pleaf->index < 0 ? prop.nitems : min2(prop.nitems, 1));
targets = targets_free = (const char **)ccalloc(2 * ntargets, char *); targets = targets_free = (const char **)ccalloc(2 * ntargets, char *);
@ -1689,9 +1600,8 @@ static inline void c2_match_once_leaf(session_t *ps, const struct managed_win *w
} }
} }
free_winprop(&prop); free_winprop(&prop);
} } else {
// Not an atom type, just fetch the string list // Not an atom type, just fetch the string list
else {
char **strlst = NULL; char **strlst = NULL;
int nstr = 0; int nstr = 0;
if (wid_get_text_prop(&ps->c, ps->atoms, wid, pleaf->tgtatom, if (wid_get_text_prop(&ps->c, ps->atoms, wid, pleaf->tgtatom,
@ -1792,7 +1702,7 @@ static inline void c2_match_once_leaf(session_t *ps, const struct managed_win *w
free(targets_free); free(targets_free);
} }
} break; } break;
default: assert(0); break; default: unreachable();
} }
} }

View File

@ -193,7 +193,7 @@ bool wid_get_text_prop(struct x_connection *c, struct atom *atoms, xcb_window_t
return false; return false;
} }
if (type != XCB_ATOM_STRING && type != atoms->aUTF8_STRING && type != atoms->aC_STRING) { if (!x_is_type_string(atoms, type)) {
log_warn("Text property %d of window %#010x has unsupported type: %d", log_warn("Text property %d of window %#010x has unsupported type: %d",
prop, wid, type); prop, wid, type);
return false; return false;

View File

@ -11,6 +11,7 @@
#include <xcb/xcb_renderutil.h> #include <xcb/xcb_renderutil.h>
#include <xcb/xfixes.h> #include <xcb/xfixes.h>
#include "atom.h"
#include "compiler.h" #include "compiler.h"
#include "kernel.h" #include "kernel.h"
#include "log.h" #include "log.h"
@ -264,6 +265,11 @@ xcb_window_t wid_get_prop_window(struct x_connection *c, xcb_window_t wid, xcb_a
bool wid_get_text_prop(struct x_connection *c, struct atom *atoms, xcb_window_t wid, bool wid_get_text_prop(struct x_connection *c, struct atom *atoms, xcb_window_t wid,
xcb_atom_t prop, char ***pstrlst, int *pnstr); xcb_atom_t prop, char ***pstrlst, int *pnstr);
static inline bool x_is_type_string(struct atom *atoms, xcb_atom_t type) {
return type == XCB_ATOM_STRING || type == atoms->aUTF8_STRING ||
type == atoms->aC_STRING;
}
const xcb_render_pictforminfo_t * const xcb_render_pictforminfo_t *
x_get_pictform_for_visual(struct x_connection *, xcb_visualid_t); x_get_pictform_for_visual(struct x_connection *, xcb_visualid_t);