diff options
Diffstat (limited to 'compton.c')
-rw-r--r-- | compton.c | 527 |
1 files changed, 465 insertions, 62 deletions
@@ -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; + if (Success != + XmbTextPropertyToTextList(dpy, &text_prop, pstrlst, pnstr) + || !*pnstr) { + *pnstr = 0; + if (*pstrlst) + XFreeStringList(*pstrlst); + return False; + } - set_ignore(dpy, NextRequest(dpy)); - printf("Window %#010lx: _NET_WM_NAME unset, falling back to WM_NAME.\n", w); + return True; +} - if (!XFetchName(dpy, w, &data)) { - return 0; +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 - // if (actual_type == utf8_type && actual_format == 8) - *name = (char *) data; - return 1; + 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 (ev->type != damage_event + XDamageNotify) { + Window w; + char *window_name; + Bool to_free = False; - if (w) { - if (root == w) { - window_name = "(Root window)"; - } else { - to_free = (Bool) window_get_name(w, &window_name); + 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); + } } - } - if (ev->type != damage_event + XDamageNotify) { 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); |