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.
pull/2/head
Richard Grenville 11 years ago
parent 56a35506b1
commit 679bfe3cab

1296
c2.c

File diff suppressed because it is too large Load Diff

326
c2.h

@ -0,0 +1,326 @@
/*
* 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,
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 },
[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);

@ -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.
@ -1056,10 +1030,13 @@ print_timestamp(session_t *ps) {
/**
* Allocate the space and copy a string.
*/
static inline char * __attribute__((const))
static inline char *
mstrcpy(const char *src) {
char *str = malloc(sizeof(char) * (strlen(src) + 1));
if (!str)
printf_errfq(1, "(): Failed to allocate memory.");
strcpy(str, src);
return str;
@ -1068,10 +1045,13 @@ mstrcpy(const char *src) {
/**
* Allocate the space and copy a string.
*/
static inline char * __attribute__((const))
static inline char *
mstrncpy(const char *src, unsigned len) {
char *str = malloc(sizeof(char) * (len + 1));
if (!str)
printf_errfq(1, "(): Failed to allocate memory.");
strncpy(str, src, len);
str[len] = '\0';
@ -1479,7 +1459,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);

@ -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,
@ -629,244 +629,20 @@ win_rounded_corners(session_t *ps, win *w) {
XFree(rects);
}
/**
* 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);

@ -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);

@ -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);

Loading…
Cancel
Save