Feature: Issue #29: Alternative shadow blacklist implementation

- Add shadow blacklist feature, but a different implementation from
  nicklan's. 5 matching modes (exact, starts-with, contains, wildcard,
  PCRE) and 3 matching targets (window name, window class instance,
  window general class). Not extensively tested, bugs to be expected.
  It's slower for exact matching than nicklan's as it uses linear search
  instead of hash table. Also, PCRE's JIT optimization may cause issues
  on PaX kernels.

- Add dependency to libpcre. Could be made optional if we have a
  graceful way to handle that in Makefile.

- Some matching functions are GNU extensions of glibc. So this version
  may have troubles running on platforms not using glibc.

- Fix a bug that access freed memory blocks in set_fade_callcack() and
  check_fade_fin(). valgrind found it out.

- Use WM_CLASS to detect client windows instead of WM_STATE. Some client
  windows (like notification windows) have WM_CLASS but not WM_STATE.

- Mark the extents as damaged if shadow state changed in
  determine_shadow().

- Rewrite wid_get_name(). Code clean-up.

- Two debugging options: DEBUG_WINDATA and DEBUG_WINMATCH.

- As the matching system is ready, it should be rather easy to add other
  kinds of blacklists, like fading blacklist.
This commit is contained in:
Richard Grenville 2012-09-22 11:42:39 +08:00
parent 8724101c0a
commit 0d6724385e
4 changed files with 577 additions and 82 deletions

View File

@ -6,8 +6,10 @@ MANDIR ?= $(PREFIX)/share/man/man1
PACKAGES = x11 xcomposite xfixes xdamage xrender xext
LIBS = $(shell pkg-config --libs $(PACKAGES)) -lm
LIBS += $(shell pcre-config --libs)
INCS = $(shell pkg-config --cflags $(PACKAGES))
CFLAGS += -Wall
INCS += $(shell pcre-config --cflags)
CFLAGS += -Wall -std=c99
OBJS = compton.o
%.o: src/%.c src/%.h

View File

@ -50,6 +50,7 @@ __R__ for runtime
* xproto / x11proto (B)
* bash (R)
* xprop,xwininfo / x11-utils (R)
* libpcre (B,R) (Will probably be made optional soon)
To build, make sure you have the above dependencies:

View File

@ -35,7 +35,7 @@ Bool has_name_pixmap;
int root_height, root_width;
/* errors */
ignore *ignore_head, **ignore_tail = &ignore_head;
ignore *ignore_head = NULL, **ignore_tail = &ignore_head;
int xfixes_event, xfixes_error;
int damage_event, damage_error;
int composite_event, composite_error;
@ -71,8 +71,14 @@ Atom extents_atom;
Atom opacity_atom;
Atom frame_extents_atom;
Atom client_atom;
Atom name_atom;
Atom name_ewmh_atom;
Atom class_atom;
Atom win_type_atom;
Atom win_type[NUM_WINTYPES];
// Window type settings
double win_type_opacity[NUM_WINTYPES];
Bool win_type_shadow[NUM_WINTYPES];
Bool win_type_fade[NUM_WINTYPES];
@ -123,6 +129,13 @@ double mark_wmwin_focused = False;
/// Whether compton needs to track focus changes.
Bool track_focus = False;
/// Whether compton needs to track window name and class.
Bool track_wdata = False;
/// Shadow blacklist. A linked list of conditions.
wincond *shadow_blacklist = NULL;
/// Fading blacklist. A linked list of conditions.
wincond *fade_blacklist = NULL;
Bool synchronize = False;
@ -206,9 +219,12 @@ run_fade(Display *dpy, win *w, unsigned steps) {
static void
set_fade_callback(Display *dpy, win *w,
void (*callback) (Display *dpy, win *w), Bool exec_callback) {
if (exec_callback && w->fade_callback)
(w->fade_callback)(dpy, w);
void (*old_callback) (Display *dpy, win *w) = w->fade_callback;
w->fade_callback = callback;
// Must be the last line as the callback could destroy w!
if (exec_callback && old_callback)
old_callback(dpy, w);
}
/**
@ -615,6 +631,231 @@ should_ignore(Display *dpy, unsigned long sequence) {
* Windows
*/
/**
* Match a window against a single window condition.
*
* @return true if matched, false otherwise.
*/
static bool
win_match_once(win *w, const wincond *cond) {
const char *target;
bool matched = false;
#ifdef DEBUG_WINMATCH
printf("win_match_once(%#010lx \"%s\"): cond = %p", w->id, w->name,
cond);
#endif
// 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;
}
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 *condlst, wincond **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)) {
*cache = condlst;
return true;
}
}
return false;
}
/**
* Add a pattern to a condition linked list.
*/
static Bool
condlst_add(wincond **pcondlst, const char *pattern) {
unsigned plen = strlen(pattern);
wincond *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));
// 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;
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
#endif
}
else {
cond->pattern = mstrcpy(pos);
}
// Insert it into the linked list
cond->next = *pcondlst;
*pcondlst = cond;
return True;
}
static long
determine_evmask(Display *dpy, Window wid, win_evmode_t mode) {
long evmask = NoEventMask;
@ -625,7 +866,7 @@ determine_evmask(Display *dpy, Window wid, win_evmode_t mode) {
}
if (WIN_EVMODE_CLIENT == mode || find_toplevel(dpy, wid)) {
if (frame_opacity)
if (frame_opacity || track_wdata)
evmask |= PropertyChangeMask;
}
@ -1106,7 +1347,7 @@ paint_all(Display *dpy, XserverRegion region, win *t) {
XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, region);
// Painting shadow
if (win_type_shadow[w->window_type]) {
if (w->shadow) {
XRenderComposite(
dpy, PictOpOver, cshadow_picture, w->shadow_pict,
root_buffer, 0, 0, 0, 0,
@ -1363,13 +1604,6 @@ map_win(Display *dpy, Window id,
w->a.map_state = IsViewable;
w->window_type = determine_wintype(dpy, w->id, w->id);
// Window type change could affect shadow and fade
determine_shadow(dpy, w);
determine_fade(dpy, w);
// Determine mode here just in case the colormap changes
determine_mode(dpy, w);
#ifdef DEBUG_WINTYPE
printf("map_win(): window %#010lx type %s\n",
w->id, wintype_name(w->window_type));
@ -1401,6 +1635,12 @@ map_win(Display *dpy, Window id,
get_frame_extents(dpy, w, w->client_win);
}
// Get window name and class if we are tracking them
if (track_wdata) {
win_get_name(dpy, w);
win_get_class(dpy, w);
}
/*
* Occasionally compton does not seem able to get a FocusIn event from a
* window just mapped. I suspect it's a timing issue again when the
@ -1415,6 +1655,13 @@ map_win(Display *dpy, Window id,
w->focused = True;
}
// Window type change could affect shadow and fade
determine_shadow(dpy, w);
determine_fade(dpy, w);
// Determine mode here just in case the colormap changes
determine_mode(dpy, w);
// Fading in
calc_opacity(dpy, w, True);
set_fade_callback(dpy, w, NULL, True);
@ -1620,15 +1867,23 @@ static void
determine_shadow(Display *dpy, win *w) {
Bool shadow_old = w->shadow;
w->shadow = win_type_shadow[w->window_type];
w->shadow = (win_type_shadow[w->window_type]
&& !win_match(w, shadow_blacklist, &w->cache_sblst));
// Window extents need update on shadow state change
if (w->shadow != shadow_old) {
// Shadow geometry currently doesn't change on shadow state change
// calc_shadow_geometry(dpy, w);
if (w->extents) {
free_region(dpy, &w->extents);
// Mark the old extents as damaged if the shadow is removed
if (!w->shadow)
add_damage(dpy, w->extents);
else
free_region(dpy, &w->extents);
w->extents = win_extents(dpy, w);
// Mark the new extents as damaged if the shadow is added
if (w->shadow)
add_damage_win(dpy, w);
}
}
}
@ -1666,7 +1921,7 @@ calc_shadow_geometry(Display *dpy, win *w) {
static void
mark_client_win(Display *dpy, win *w, Window client) {
w->client_win = client;
// Get the frame width and monitor further frame width changes on client
// window if necessary
if (frame_opacity) {
@ -1721,6 +1976,12 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) {
new->damage = XDamageCreate(dpy, id, XDamageReportNonEmpty);
}
new->name = NULL;
new->class_instance = NULL;
new->class_general = NULL;
new->cache_sblst = NULL;
new->cache_fblst = NULL;
new->border_size = None;
new->extents = None;
new->shadow = False;
@ -1815,7 +2076,7 @@ restack_win(Display *dpy, win *w, Window new_above) {
if (root == c->id) {
window_name = "(Root window)";
} else {
to_free = window_get_name(c->id, &window_name);
to_free = wid_get_name(dpy, c->id, &window_name);
}
desc = "";
@ -2131,39 +2392,127 @@ expose_root(Display *dpy, Window root, XRectangle *rects, int nrects) {
add_damage(dpy, region);
}
#if defined(DEBUG_EVENTS) || defined(DEBUG_RESTACK)
static int
window_get_name(Window w, char **name) {
Atom prop = XInternAtom(dpy, "_NET_WM_NAME", False);
Atom utf8_type = XInternAtom(dpy, "UTF8_STRING", False);
Atom actual_type;
int actual_format;
unsigned long nitems;
unsigned long leftover;
char *data = NULL;
Status ret;
static Bool
wid_get_text_prop(Display *dpy, Window wid, Atom prop,
char ***pstrlst, int *pnstr) {
XTextProperty text_prop;
set_ignore(dpy, NextRequest(dpy));
if (!(XGetTextProperty(dpy, wid, &text_prop, prop) && text_prop.value))
return False;
if (Success != (ret = XGetWindowProperty(dpy, w, prop, 0L, (long) BUFSIZ,
False, utf8_type, &actual_type, &actual_format, &nitems,
&leftover, (unsigned char **) &data))) {
if (BadWindow == ret) return 0;
set_ignore(dpy, NextRequest(dpy));
printf("Window %#010lx: _NET_WM_NAME unset, falling back to WM_NAME.\n", w);
if (!XFetchName(dpy, w, &data)) {
return 0;
}
if (Success !=
XmbTextPropertyToTextList(dpy, &text_prop, pstrlst, pnstr)
|| !*pnstr) {
*pnstr = 0;
if (*pstrlst)
XFreeStringList(*pstrlst);
return False;
}
// if (actual_type == utf8_type && actual_format == 8)
*name = (char *) data;
return 1;
return True;
}
static Bool
wid_get_name(Display *dpy, Window wid, char **name) {
XTextProperty text_prop;
char **strlst = NULL;
int nstr = 0;
// set_ignore(dpy, NextRequest(dpy));
if (!(XGetTextProperty(dpy, wid, &text_prop, name_ewmh_atom)
&& text_prop.value)) {
// set_ignore(dpy, NextRequest(dpy));
#ifdef DEBUG_WINDATA
printf("wid_get_name(%#010lx): _NET_WM_NAME unset, falling back to WM_NAME.\n", wid);
#endif
if (!(XGetWMName(dpy, wid, &text_prop) && text_prop.value)) {
return False;
}
}
if (Success !=
XmbTextPropertyToTextList(dpy, &text_prop, &strlst, &nstr)
|| !nstr || !strlst) {
if (strlst)
XFreeStringList(strlst);
return False;
}
*name = mstrcpy(strlst[0]);
XFreeStringList(strlst);
return True;
}
static int
win_get_name(Display *dpy, win *w) {
Bool ret;
char *name_old = w->name;
// Can't do anything if there's no client window
if (!w->client_win)
return False;
// Get the name
ret = wid_get_name(dpy, w->client_win, &w->name);
// Return -1 if wid_get_name() failed, 0 if name didn't change, 1 if
// it changes
if (!ret)
ret = -1;
else if (name_old && !strcmp(w->name, name_old))
ret = 0;
else
ret = 1;
// Keep the old name if there's no new one
if (w->name != name_old)
free(name_old);
#ifdef DEBUG_WINDATA
printf("win_get_name(%#010lx): client = %#010lx, name = \"%s\", "
"ret = %d\n", w->id, w->client_win, w->name, ret);
#endif
return ret;
}
static Bool
win_get_class(Display *dpy, win *w) {
char **strlst = NULL;
int nstr = 0;
// Can't do anything if there's no client window
if (!w->client_win)
return False;
// Free and reset old strings
free(w->class_instance);
free(w->class_general);
w->class_instance = NULL;
w->class_general = NULL;
// Retrieve the property string list
if (!wid_get_text_prop(dpy, w->client_win, class_atom, &strlst, &nstr))
return False;
// Copy the strings if successful
w->class_instance = mstrcpy(strlst[0]);
if (nstr > 1)
w->class_general = mstrcpy(strlst[1]);
XFreeStringList(strlst);
#ifdef DEBUG_WINDATA
printf("win_get_class(%#010lx): client = %#010lx, "
"instance = \"%s\", general = \"%s\"\n",
w->id, w->client_win, w->class_instance, w->class_general);
#endif
return True;
}
#ifdef DEBUG_EVENTS
static int
ev_serial(XEvent *ev) {
@ -2394,6 +2743,7 @@ ev_property_notify(XPropertyEvent *ev) {
}
}
// If frame extents property changes
if (frame_opacity && ev->atom == extents_atom) {
win *w = find_toplevel(dpy, ev->window);
if (w) {
@ -2402,6 +2752,23 @@ ev_property_notify(XPropertyEvent *ev) {
add_damage_win(dpy, w);
}
}
// If name changes
if (track_wdata
&& (name_atom == ev->atom || name_ewmh_atom == ev->atom)) {
win *w = find_toplevel(dpy, ev->window);
if (w && 1 == win_get_name(dpy, w))
determine_shadow(dpy, w);
}
// If class changes
if (track_wdata && class_atom == ev->atom) {
win *w = find_toplevel(dpy, ev->window);
if (w) {
win_get_class(dpy, w);
determine_shadow(dpy, w);
}
}
}
inline static void
@ -2433,38 +2800,37 @@ ev_shape_notify(XShapeEvent *ev) {
inline static void
ev_handle(XEvent *ev) {
#ifdef DEBUG_EVENTS
Window w;
char *window_name;
Bool to_free = False;
#endif
if ((ev->type & 0x7f) != KeymapNotify) {
discard_ignore(dpy, ev->xany.serial);
}
#ifdef DEBUG_EVENTS
w = ev_window(ev);
window_name = "(Failed to get title)";
if (w) {
if (root == w) {
window_name = "(Root window)";
} else {
to_free = (Bool) window_get_name(w, &window_name);
}
}
if (ev->type != damage_event + XDamageNotify) {
Window w;
char *window_name;
Bool to_free = False;
w = ev_window(ev);
window_name = "(Failed to get title)";
if (w) {
if (root == w) {
window_name = "(Root window)";
} else {
to_free = (Bool) wid_get_name(dpy, w, &window_name);
}
}
print_timestamp();
printf("event %10.10s serial %#010x window %#010lx \"%s\"\n",
ev_name(ev), ev_serial(ev), w, window_name);
if (to_free) {
XFree(window_name);
window_name = NULL;
}
}
if (to_free) {
XFree(window_name);
window_name = NULL;
}
#endif
switch (ev->type) {
@ -2519,10 +2885,11 @@ ev_handle(XEvent *ev) {
static void
usage(void) {
fprintf(stderr, "compton v0.0.1\n");
fprintf(stderr, "compton (development version)\n");
fprintf(stderr, "usage: compton [options]\n");
fprintf(stderr,
"Options\n"
"Options:\n"
"\n"
"-d display\n"
" Which display should be managed.\n"
"-r radius\n"
@ -2570,7 +2937,28 @@ usage(void) {
"--inactive-opacity-override\n"
" Inactive opacity set by -i overrides value of _NET_WM_OPACITY.\n"
"--inactive-dim value\n"
" Dim inactive windows. (0.0 - 1.0, defaults to 0)\n");
" Dim inactive windows. (0.0 - 1.0, defaults to 0)\n"
"--mark-wmwin-focused\n"
" Try to detect WM windows and mark them as active.\n"
"--shadow-exclude condition\n"
" Exclude conditions for shadows.\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), and \"g\" (window general class)\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 a serious of flags. Currently the only defined\n"
" flag is \"i\" (ignore case).\n"
"\n"
" <pattern> is the actual pattern string.\n"
);
exit(1);
}
@ -2634,7 +3022,10 @@ get_atoms(void) {
extents_atom = XInternAtom(dpy, "_NET_FRAME_EXTENTS", False);
opacity_atom = XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False);
frame_extents_atom = XInternAtom(dpy, "_NET_FRAME_EXTENTS", False);
client_atom = XInternAtom(dpy, "WM_STATE", False);
client_atom = XInternAtom(dpy, "WM_CLASS", False);
name_atom = XInternAtom(dpy, "WM_NAME", False);
name_ewmh_atom = XInternAtom(dpy, "_NET_WM_NAME", False);
class_atom = XInternAtom(dpy, "WM_CLASS", False);
win_type_atom = XInternAtom(dpy,
"_NET_WM_WINDOW_TYPE", False);
@ -2678,6 +3069,7 @@ main(int argc, char **argv) {
{ "inactive-opacity-override", no_argument, NULL, 0 },
{ "inactive-dim", required_argument, NULL, 0 },
{ "mark-wmwin-focused", no_argument, NULL, 0 },
{ "shadow-exclude", required_argument, NULL, 0 },
// Must terminate with a NULL entry
{ NULL, 0, NULL, 0 },
};
@ -2703,6 +3095,10 @@ main(int argc, char **argv) {
gettimeofday(&time_start, NULL);
// Set locale so window names with special characters are interpreted
// correctly
setlocale (LC_ALL, "");
for (i = 0; i < NUM_WINTYPES; ++i) {
win_type_fade[i] = False;
win_type_shadow[i] = False;
@ -2734,6 +3130,9 @@ main(int argc, char **argv) {
case 5:
mark_wmwin_focused = True;
break;
case 6:
condlst_add(&shadow_blacklist, optarg);
break;
}
break;
// Short options
@ -2831,6 +3230,10 @@ main(int argc, char **argv) {
track_focus = True;
}
// Determine whether we need to track window name and class
if (shadow_blacklist || fade_blacklist)
track_wdata = True;
fade_time = get_time_in_milliseconds();
dpy = XOpenDisplay(display);

View File

@ -4,6 +4,32 @@
// Throw everything in here.
// === Options ===
#define CAN_DO_USABLE 0
// Debug options, enable them using -D in CFLAGS
// #define DEBUG_REPAINT 1
// #define DEBUG_EVENTS 1
// #define DEBUG_RESTACK 1
// #define DEBUG_WINTYPE 1
// #define DEBUG_CLIENTWIN 1
// #define DEBUG_WINDATA 1
// #define DEBUG_WINMATCH 1
// #define MONITOR_REPAINT 1
// Whether to enable PCRE regular expression support in blacklists, enabled
// by default
#define CONFIG_REGEX_PCRE 1
// Whether to enable JIT support of libpcre. This may cause problems on PaX
// kernels.
#define CONFIG_REGEX_PCRE_JIT 1
// === Includes ===
// For some special functions
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@ -14,6 +40,13 @@
#include <time.h>
#include <unistd.h>
#include <getopt.h>
#include <stdbool.h>
#include <locale.h>
#include <fnmatch.h>
#ifdef CONFIG_REGEX_PCRE
#include <pcre.h>
#endif
#include <X11/Xlib.h>
#include <X11/Xutil.h>
@ -23,20 +56,11 @@
#include <X11/extensions/Xrender.h>
#include <X11/extensions/shape.h>
// === Constants ===
#if COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR >= 2
#define HAS_NAME_WINDOW_PIXMAP 1
#endif
#define CAN_DO_USABLE 0
// Debug options, enable them using -D in CFLAGS
// #define DEBUG_REPAINT 1
// #define DEBUG_EVENTS 1
// #define DEBUG_RESTACK 1
// #define DEBUG_WINTYPE 1
// #define DEBUG_CLIENTWIN 1
// #define MONITOR_REPAINT 1
// For printing timestamps
#include <time.h>
extern struct timeval time_start;
@ -83,6 +107,34 @@ typedef struct _ignore {
unsigned long sequence;
} ignore;
enum wincond_target {
CONDTGT_NAME,
CONDTGT_CLASSI,
CONDTGT_CLASSG,
};
enum wincond_type {
CONDTP_EXACT,
CONDTP_ANYWHERE,
CONDTP_FROMSTART,
CONDTP_WILDCARD,
CONDTP_REGEX_PCRE,
};
#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;
typedef struct _win {
struct _win *next;
Window id;
@ -109,6 +161,13 @@ typedef struct _win {
/// Cached width/height of the window including border.
int widthb, heightb;
// Blacklist related members
char *name;
char *class_instance;
char *class_general;
wincond *cache_sblst;
wincond *cache_fblst;
// Opacity-related members
/// Current window opacity.
opacity_t opacity;
@ -206,6 +265,18 @@ set_ignore(Display *dpy, unsigned long sequence);
static int
should_ignore(Display *dpy, unsigned long sequence);
/**
* Allocate the space and copy a string.
*/
static inline char *
mstrcpy(const char *src) {
char *str = malloc(sizeof(char) * (strlen(src) + 1));
strcpy(str, src);
return str;
}
/**
* Normalize an int value to a specific range.
*
@ -425,8 +496,9 @@ set_fade_callback(Display *dpy, win *w,
static inline void
check_fade_fin(Display *dpy, win *w) {
if (w->fade_fin) {
set_fade_callback(dpy, w, NULL, True);
w->fade_fin = False;
// Must be the last line as the callback could destroy w!
set_fade_callback(dpy, w, NULL, True);
}
}
@ -458,6 +530,15 @@ static Picture
solid_picture(Display *dpy, Bool argb, double a,
double r, double g, double b);
static bool
win_match_once(win *w, const wincond *cond);
static bool
win_match(win *w, wincond *condlst, wincond * *cache);
static Bool
condlst_add(wincond **pcondlst, const char *pattern);
static long
determine_evmask(Display *dpy, Window wid, win_evmode_t mode);
@ -594,10 +675,18 @@ error(Display *dpy, XErrorEvent *ev);
static void
expose_root(Display *dpy, Window root, XRectangle *rects, int nrects);
#if defined(DEBUG_EVENTS) || defined(DEBUG_RESTACK)
static Bool
wid_get_text_prop(Display *dpy, Window wid, Atom prop,
char ***pstrlst, int *pnstr);
static Bool
wid_get_name(Display *dpy, Window w, char **name);
static int
window_get_name(Window w, char **name);
#endif
win_get_name(Display *dpy, win *w);
static Bool
win_get_class(Display *dpy, win *w);
#ifdef DEBUG_EVENTS
static int