Feature #16: Advanced window matching
- Add advanced window matching system, capable of matching against
arbitrary window properties as well as a series of internal
properties, with 4 additional operators (>, <, >=, <=) useful for
integer targets, and support of logical operators. The old matching
system is removed, but compatibility with the format is retained.
- As the new matching system is pretty complicated, and I have no past
experience in writing a parser, it's pretty possible that bugs are
present. It also has inferior performance, but I hope it doesn't
matter on modern CPUs.
- It's possible to disable matching system at compile time with NO_C2=1
now.
- Add ps->o.config_file to track which config file we have actually
read. Queryable via D-Bus.
- Parse -d in first pass in get_cfg() as c2 needs to query X to get
atoms during condition parsing.
- Fix a bug in wid_get_prop_adv() that 0 == rformat is not handled
correctly.
- Fix incompatibility with FreeBSD sed in dbus-examples/cdbus-driver.sh
.
- Add recipe to generate .clang_complete in Makefile, used by Vim
clang_complete plugin.
- Add DEBUG_C2 for debugging condition string parsing. DEBUG_WINMATCH is
still used for match debugging.
- Rename win_on_wdata_change() to win_on_factor_change().
- Extra malloc() failure checks. Add const to matching cache members in
session_t. Code clean-up. Documentation update.
2013-01-28 21:39:38 +08:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
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,
|
2013-03-10 18:45:54 +08:00
|
|
|
C2_L_PARGB,
|
Feature #16: Advanced window matching
- Add advanced window matching system, capable of matching against
arbitrary window properties as well as a series of internal
properties, with 4 additional operators (>, <, >=, <=) useful for
integer targets, and support of logical operators. The old matching
system is removed, but compatibility with the format is retained.
- As the new matching system is pretty complicated, and I have no past
experience in writing a parser, it's pretty possible that bugs are
present. It also has inferior performance, but I hope it doesn't
matter on modern CPUs.
- It's possible to disable matching system at compile time with NO_C2=1
now.
- Add ps->o.config_file to track which config file we have actually
read. Queryable via D-Bus.
- Parse -d in first pass in get_cfg() as c2 needs to query X to get
atoms during condition parsing.
- Fix a bug in wid_get_prop_adv() that 0 == rformat is not handled
correctly.
- Fix incompatibility with FreeBSD sed in dbus-examples/cdbus-driver.sh
.
- Add recipe to generate .clang_complete in Makefile, used by Vim
clang_complete plugin.
- Add DEBUG_C2 for debugging condition string parsing. DEBUG_WINMATCH is
still used for match debugging.
- Rename win_on_wdata_change() to win_on_factor_change().
- Extra malloc() failure checks. Add const to matching cache members in
session_t. Code clean-up. Documentation update.
2013-01-28 21:39:38 +08:00
|
|
|
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, \
|
|
|
|
.match = C2_L_MEXACT, \
|
|
|
|
.match_ignorecase = false, \
|
|
|
|
.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 {
|
|
|
|
const 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 },
|
2013-03-10 18:45:54 +08:00
|
|
|
[C2_L_PARGB ] = { "argb" , C2_L_TCARDINAL , 0 },
|
Feature #16: Advanced window matching
- Add advanced window matching system, capable of matching against
arbitrary window properties as well as a series of internal
properties, with 4 additional operators (>, <, >=, <=) useful for
integer targets, and support of logical operators. The old matching
system is removed, but compatibility with the format is retained.
- As the new matching system is pretty complicated, and I have no past
experience in writing a parser, it's pretty possible that bugs are
present. It also has inferior performance, but I hope it doesn't
matter on modern CPUs.
- It's possible to disable matching system at compile time with NO_C2=1
now.
- Add ps->o.config_file to track which config file we have actually
read. Queryable via D-Bus.
- Parse -d in first pass in get_cfg() as c2 needs to query X to get
atoms during condition parsing.
- Fix a bug in wid_get_prop_adv() that 0 == rformat is not handled
correctly.
- Fix incompatibility with FreeBSD sed in dbus-examples/cdbus-driver.sh
.
- Add recipe to generate .clang_complete in Makefile, used by Vim
clang_complete plugin.
- Add DEBUG_C2 for debugging condition string parsing. DEBUG_WINMATCH is
still used for match debugging.
- Rename win_on_wdata_change() to win_on_factor_change().
- Extra malloc() failure checks. Add const to matching cache members in
session_t. Code clean-up. Documentation update.
2013-01-28 21:39:38 +08:00
|
|
|
[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 },
|
|
|
|
};
|
|
|
|
|
|
|
|
#define mstrncmp(s1, s2) strncmp((s1), (s2), strlen(s1))
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Compare next word in a string with another string.
|
|
|
|
*/
|
|
|
|
static inline int
|
|
|
|
strcmp_wd(const char *needle, const char *src) {
|
|
|
|
int ret = mstrncmp(needle, src);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
char c = src[strlen(needle)];
|
|
|
|
if (isalnum(c) || '_' == c)
|
|
|
|
return 1;
|
|
|
|
else
|
|
|
|
return 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 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(const c2_l_t *pleaf);
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
c2h_dump_str_type(const c2_l_t *pleaf);
|
|
|
|
|
|
|
|
static void
|
|
|
|
c2_dump_raw(c2_ptr_t p);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wrapper of c2_dump_raw().
|
|
|
|
*/
|
|
|
|
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);
|
|
|
|
|