mirror of https://github.com/yshui/picom.git
Merge pull request #1201 from yshui/cached-c2
This commit is contained in:
commit
bef295f4c4
119
src/atom.c
119
src/atom.c
|
@ -1,4 +1,5 @@
|
|||
#include <string.h>
|
||||
#include <uthash.h>
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
#include "atom.h"
|
||||
|
@ -10,12 +11,21 @@
|
|||
|
||||
struct atom_entry {
|
||||
struct cache_handle entry;
|
||||
UT_hash_handle hh;
|
||||
xcb_atom_t atom;
|
||||
};
|
||||
|
||||
static inline int atom_getter(struct cache *cache attr_unused, const char *atom_name,
|
||||
size_t keylen, struct cache_handle **value, void *user_data) {
|
||||
struct atom_impl {
|
||||
struct atom base;
|
||||
struct cache c;
|
||||
struct atom_entry *atom_to_name;
|
||||
cache_getter_t getter;
|
||||
};
|
||||
|
||||
static inline int atom_getter(struct cache *cache, const char *atom_name, size_t keylen,
|
||||
struct cache_handle **value, void *user_data) {
|
||||
xcb_connection_t *c = user_data;
|
||||
auto atoms = container_of(cache, struct atom_impl, c);
|
||||
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(
|
||||
c, xcb_intern_atom(c, 0, to_u16_checked(keylen), atom_name), NULL);
|
||||
|
||||
|
@ -31,19 +41,32 @@ static inline int atom_getter(struct cache *cache attr_unused, const char *atom_
|
|||
|
||||
struct atom_entry *entry = ccalloc(1, struct atom_entry);
|
||||
entry->atom = atom;
|
||||
HASH_ADD_INT(atoms->atom_to_name, atom, entry);
|
||||
*value = &entry->entry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
atom_entry_free(struct cache *cache attr_unused, struct cache_handle *handle) {
|
||||
struct atom_entry *entry = cache_entry(handle, struct atom_entry, entry);
|
||||
static inline int
|
||||
known_atom_getter(struct cache *cache attr_unused, const char *atom_name attr_unused,
|
||||
size_t keylen attr_unused, struct cache_handle **value, void *user_data) {
|
||||
auto atom = *(xcb_atom_t *)user_data;
|
||||
struct atom_entry *entry = ccalloc(1, struct atom_entry);
|
||||
entry->atom = atom;
|
||||
*value = &entry->entry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void atom_entry_free(struct cache *cache, struct cache_handle *handle) {
|
||||
auto entry = cache_entry(handle, struct atom_entry, entry);
|
||||
auto atoms = container_of(cache, struct atom_impl, c);
|
||||
HASH_DEL(atoms->atom_to_name, entry);
|
||||
free(entry);
|
||||
}
|
||||
|
||||
xcb_atom_t get_atom(struct atom *a, const char *key, size_t keylen, xcb_connection_t *c) {
|
||||
struct cache_handle *entry = NULL;
|
||||
if (cache_get_or_fetch(&a->c, key, keylen, &entry, c, atom_getter) < 0) {
|
||||
auto atoms = container_of(a, struct atom_impl, base);
|
||||
if (cache_get_or_fetch(&atoms->c, key, keylen, &entry, c, atoms->getter) < 0) {
|
||||
log_error("Failed to get atom %s", key);
|
||||
return XCB_NONE;
|
||||
}
|
||||
|
@ -51,23 +74,89 @@ xcb_atom_t get_atom(struct atom *a, const char *key, size_t keylen, xcb_connecti
|
|||
}
|
||||
|
||||
xcb_atom_t get_atom_cached(struct atom *a, const char *key, size_t keylen) {
|
||||
return cache_entry(cache_get(&a->c, key, keylen), struct atom_entry, entry)->atom;
|
||||
auto atoms = container_of(a, struct atom_impl, base);
|
||||
return cache_entry(cache_get(&atoms->c, key, keylen), struct atom_entry, entry)->atom;
|
||||
}
|
||||
|
||||
const char *get_atom_name(struct atom *a, xcb_atom_t atom, xcb_connection_t *c) {
|
||||
struct atom_entry *entry = NULL;
|
||||
auto atoms = container_of(a, struct atom_impl, base);
|
||||
HASH_FIND(hh, atoms->atom_to_name, &atom, sizeof(xcb_atom_t), entry);
|
||||
if (!entry) {
|
||||
BUG_ON(c == NULL);
|
||||
auto r = xcb_get_atom_name_reply(c, xcb_get_atom_name(c, atom), NULL);
|
||||
if (!r) {
|
||||
log_error("Failed to get atom name");
|
||||
return NULL;
|
||||
}
|
||||
char *atom_name = xcb_get_atom_name_name(r);
|
||||
auto len = (size_t)xcb_get_atom_name_name_length(r);
|
||||
struct cache_handle *handle = NULL;
|
||||
cache_get_or_fetch(&atoms->c, atom_name, len, &handle, &atom, known_atom_getter);
|
||||
entry = cache_entry(handle, struct atom_entry, entry);
|
||||
HASH_ADD_INT(atoms->atom_to_name, atom, entry);
|
||||
free(r);
|
||||
}
|
||||
return entry->entry.key;
|
||||
}
|
||||
|
||||
const char *get_atom_name_cached(struct atom *a, xcb_atom_t atom) {
|
||||
struct atom_entry *entry = NULL;
|
||||
auto atoms = container_of(a, struct atom_impl, base);
|
||||
HASH_FIND(hh, atoms->atom_to_name, &atom, sizeof(xcb_atom_t), entry);
|
||||
if (!entry) {
|
||||
return NULL;
|
||||
}
|
||||
return entry->entry.key;
|
||||
}
|
||||
|
||||
static inline struct atom *init_atoms_impl(xcb_connection_t *c, cache_getter_t getter) {
|
||||
auto atoms = ccalloc(1, struct atom_impl);
|
||||
atoms->c = CACHE_INIT;
|
||||
atoms->getter = getter;
|
||||
#define ATOM_GET(x) atoms->base.a##x = get_atom(&atoms->base, #x, sizeof(#x) - 1, c)
|
||||
LIST_APPLY(ATOM_GET, SEP_COLON, ATOM_LIST1);
|
||||
LIST_APPLY(ATOM_GET, SEP_COLON, ATOM_LIST2);
|
||||
#undef ATOM_GET
|
||||
return &atoms->base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new atom structure and fetch all predefined atoms
|
||||
*/
|
||||
struct atom *init_atoms(xcb_connection_t *c) {
|
||||
auto atoms = ccalloc(1, struct atom);
|
||||
atoms->c = CACHE_INIT;
|
||||
#define ATOM_GET(x) atoms->a##x = get_atom(atoms, #x, sizeof(#x) - 1, c)
|
||||
LIST_APPLY(ATOM_GET, SEP_COLON, ATOM_LIST1);
|
||||
LIST_APPLY(ATOM_GET, SEP_COLON, ATOM_LIST2);
|
||||
#undef ATOM_GET
|
||||
return atoms;
|
||||
return init_atoms_impl(c, atom_getter);
|
||||
}
|
||||
|
||||
void destroy_atoms(struct atom *a) {
|
||||
cache_invalidate_all(&a->c, atom_entry_free);
|
||||
auto atoms = container_of(a, struct atom_impl, base);
|
||||
cache_invalidate_all(&atoms->c, atom_entry_free);
|
||||
assert(atoms->atom_to_name == NULL);
|
||||
free(a);
|
||||
}
|
||||
|
||||
#if defined(UNIT_TEST) || defined(CONFIG_FUZZER)
|
||||
|
||||
static inline int mock_atom_getter(struct cache *cache, const char *atom_name attr_unused,
|
||||
size_t atom_len attr_unused, struct cache_handle **value,
|
||||
void *user_data attr_unused) {
|
||||
auto atoms = container_of(cache, struct atom_impl, c);
|
||||
xcb_atom_t atom = (xcb_atom_t)HASH_COUNT(atoms->atom_to_name) + 1;
|
||||
struct atom_entry *entry = ccalloc(1, struct atom_entry);
|
||||
entry->atom = atom;
|
||||
HASH_ADD_INT(atoms->atom_to_name, atom, entry);
|
||||
*value = &entry->entry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct atom *init_mock_atoms(void) {
|
||||
return init_atoms_impl(NULL, mock_atom_getter);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
struct atom *init_mock_atoms(void) {
|
||||
abort();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -53,7 +53,6 @@
|
|||
|
||||
struct atom_entry;
|
||||
struct atom {
|
||||
struct cache c;
|
||||
LIST_APPLY(ATOM_DEF, SEP_COLON, ATOM_LIST1);
|
||||
LIST_APPLY(ATOM_DEF, SEP_COLON, ATOM_LIST2);
|
||||
};
|
||||
|
@ -71,5 +70,13 @@ xcb_atom_t get_atom_cached(struct atom *a, const char *key, size_t keylen);
|
|||
static inline xcb_atom_t get_atom_cached_with_nul(struct atom *a, const char *key) {
|
||||
return get_atom_cached(a, key, strlen(key));
|
||||
}
|
||||
const char *get_atom_name(struct atom *a, xcb_atom_t, xcb_connection_t *c);
|
||||
const char *get_atom_name_cached(struct atom *a, xcb_atom_t atom);
|
||||
|
||||
void destroy_atoms(struct atom *a);
|
||||
|
||||
/// A mock atom object for unit testing. Successive calls to get_atom will return
|
||||
/// secutive integers as atoms, starting from 1. Calling get_atom_name with atoms
|
||||
/// previously seen will result in the string that was used to create the atom; if
|
||||
/// the atom was never returned by get_atom, it will abort.
|
||||
struct atom *init_mock_atoms(void);
|
18
src/c2.h
18
src/c2.h
|
@ -18,6 +18,12 @@
|
|||
typedef struct _c2_lptr c2_lptr_t;
|
||||
typedef struct session session_t;
|
||||
struct c2_state;
|
||||
/// Per-window state used for c2 condition matching.
|
||||
struct c2_window_state {
|
||||
/// An array of window properties. Exact how many
|
||||
/// properties there are is stored inside `struct c2_state`.
|
||||
struct c2_property_value *values;
|
||||
};
|
||||
struct atom;
|
||||
struct managed_win;
|
||||
|
||||
|
@ -34,9 +40,17 @@ struct c2_state *c2_state_new(struct atom *atoms);
|
|||
void c2_state_free(struct c2_state *state);
|
||||
/// Returns true if value of the property is used in any condition.
|
||||
bool c2_state_is_property_tracked(struct c2_state *state, xcb_atom_t property);
|
||||
void c2_window_state_init(const struct c2_state *state, struct c2_window_state *window_state);
|
||||
void c2_window_state_destroy(const struct c2_state *state, struct c2_window_state *window_state);
|
||||
void c2_window_state_mark_dirty(const struct c2_state *state,
|
||||
struct c2_window_state *window_state, xcb_atom_t property,
|
||||
bool is_on_frame);
|
||||
void c2_window_state_update(struct c2_state *state, struct c2_window_state *window_state,
|
||||
xcb_connection_t *c, xcb_window_t client_win,
|
||||
xcb_window_t frame_win);
|
||||
|
||||
bool c2_match(session_t *ps, const struct managed_win *w, const c2_lptr_t *condlst,
|
||||
void **pdata);
|
||||
bool c2_match(struct c2_state *state, const struct managed_win *w,
|
||||
const c2_lptr_t *condlst, void **pdata);
|
||||
|
||||
bool c2_list_postprocess(struct c2_state *state, xcb_connection_t *c, c2_lptr_t *list);
|
||||
typedef bool (*c2_list_foreach_cb_t)(const c2_lptr_t *cond, void *data);
|
||||
|
|
|
@ -575,11 +575,15 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
|
|||
|
||||
// Check for other atoms we are tracking
|
||||
if (c2_state_is_property_tracked(ps->c2_state, ev->atom)) {
|
||||
bool change_is_on_frame = true;
|
||||
auto w = find_managed_win(ps, ev->window);
|
||||
if (!w) {
|
||||
w = find_toplevel(ps, ev->window);
|
||||
change_is_on_frame = false;
|
||||
}
|
||||
if (w) {
|
||||
c2_window_state_mark_dirty(ps->c2_state, &w->c2_state, ev->atom,
|
||||
change_is_on_frame);
|
||||
// Set FACTOR_CHANGED so rules based on properties will be
|
||||
// re-evaluated.
|
||||
// Don't need to set property stale here, since that only
|
||||
|
|
|
@ -98,7 +98,7 @@ endif
|
|||
|
||||
if cc.has_argument('-fsanitize=fuzzer')
|
||||
c2_fuzz = executable('c2_fuzz', srcs + ['fuzzer/c2.c'],
|
||||
c_args: cflags + ['-fsanitize=fuzzer', '-Dmain=__main__'],
|
||||
c_args: cflags + ['-fsanitize=fuzzer', '-DCONFIG_FUZZER'],
|
||||
link_args: ['-fsanitize=fuzzer'],
|
||||
dependencies: [ base_deps, deps, test_h_dep ],
|
||||
build_by_default: false,
|
||||
|
|
|
@ -2791,10 +2791,16 @@ static void session_run(session_t *ps) {
|
|||
ev_run(ps->loop, 0);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FUZZER
|
||||
#define PICOM_MAIN(...) no_main(__VA_ARGS__)
|
||||
#else
|
||||
#define PICOM_MAIN(...) main(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The function that everybody knows.
|
||||
*/
|
||||
int main(int argc, char **argv) {
|
||||
int PICOM_MAIN(int argc, char **argv) {
|
||||
// Set locale so window names with special characters are interpreted
|
||||
// correctly
|
||||
setlocale(LC_ALL, "");
|
||||
|
|
32
src/win.c
32
src/win.c
|
@ -150,7 +150,7 @@ static void win_update_focused(session_t *ps, struct managed_win *w) {
|
|||
(ps->o.mark_wmwin_focused && w->wmwin) ||
|
||||
(ps->o.mark_ovredir_focused && w->base.id == w->client_win && !w->wmwin) ||
|
||||
(w->a.map_state == XCB_MAP_STATE_VIEWABLE &&
|
||||
c2_match(ps, w, ps->o.focus_blacklist, NULL))) {
|
||||
c2_match(ps->c2_state, w, ps->o.focus_blacklist, NULL))) {
|
||||
w->focused = true;
|
||||
}
|
||||
|
||||
|
@ -959,7 +959,7 @@ static void win_determine_shadow(session_t *ps, struct managed_win *w) {
|
|||
if (!ps->o.wintype_option[w->window_type].shadow) {
|
||||
log_debug("Shadow disabled by wintypes");
|
||||
shadow_new = false;
|
||||
} else if (c2_match(ps, w, ps->o.shadow_blacklist, NULL)) {
|
||||
} else if (c2_match(ps->c2_state, w, ps->o.shadow_blacklist, NULL)) {
|
||||
log_debug("Shadow disabled by shadow-exclude");
|
||||
shadow_new = false;
|
||||
} else if (ps->o.shadow_ignore_shaped && w->bounding_shaped &&
|
||||
|
@ -1015,7 +1015,7 @@ bool win_update_prop_fullscreen(struct x_connection *c, const struct atom *atoms
|
|||
|
||||
static void win_determine_clip_shadow_above(session_t *ps, struct managed_win *w) {
|
||||
bool should_crop = (ps->o.wintype_option[w->window_type].clip_shadow_above ||
|
||||
c2_match(ps, w, ps->o.shadow_clip_list, NULL));
|
||||
c2_match(ps->c2_state, w, ps->o.shadow_clip_list, NULL));
|
||||
w->clip_shadow_above = should_crop;
|
||||
}
|
||||
|
||||
|
@ -1038,7 +1038,7 @@ static void win_determine_invert_color(session_t *ps, struct managed_win *w) {
|
|||
if (UNSET != w->invert_color_force) {
|
||||
invert_color_new = w->invert_color_force;
|
||||
} else if (w->a.map_state == XCB_MAP_STATE_VIEWABLE) {
|
||||
invert_color_new = c2_match(ps, w, ps->o.invert_color_list, NULL);
|
||||
invert_color_new = c2_match(ps->c2_state, w, ps->o.invert_color_list, NULL);
|
||||
}
|
||||
|
||||
win_set_invert_color(ps, w, invert_color_new);
|
||||
|
@ -1127,7 +1127,7 @@ static void win_determine_blur_background(session_t *ps, struct managed_win *w)
|
|||
if (!ps->o.wintype_option[w->window_type].blur_background) {
|
||||
log_debug("Blur background disabled by wintypes");
|
||||
blur_background_new = false;
|
||||
} else if (c2_match(ps, w, ps->o.blur_background_blacklist, NULL)) {
|
||||
} else if (c2_match(ps->c2_state, w, ps->o.blur_background_blacklist, NULL)) {
|
||||
log_debug("Blur background disabled by "
|
||||
"blur-background-exclude");
|
||||
blur_background_new = false;
|
||||
|
@ -1142,7 +1142,7 @@ static void win_determine_blur_background(session_t *ps, struct managed_win *w)
|
|||
*/
|
||||
static void win_determine_rounded_corners(session_t *ps, struct managed_win *w) {
|
||||
void *radius_override = NULL;
|
||||
if (c2_match(ps, w, ps->o.corner_radius_rules, &radius_override)) {
|
||||
if (c2_match(ps->c2_state, w, ps->o.corner_radius_rules, &radius_override)) {
|
||||
log_debug("Matched corner rule! %d", w->corner_radius);
|
||||
}
|
||||
|
||||
|
@ -1153,8 +1153,9 @@ static void win_determine_rounded_corners(session_t *ps, struct managed_win *w)
|
|||
|
||||
// Don't round full screen windows & excluded windows,
|
||||
// unless we find a corner override in corner_radius_rules
|
||||
if (!radius_override && ((w && w->is_fullscreen) ||
|
||||
c2_match(ps, w, ps->o.rounded_corners_blacklist, NULL))) {
|
||||
if (!radius_override &&
|
||||
((w && w->is_fullscreen) ||
|
||||
c2_match(ps->c2_state, w, ps->o.rounded_corners_blacklist, NULL))) {
|
||||
w->corner_radius = 0;
|
||||
log_debug("Not rounding corners for window %#010x", w->base.id);
|
||||
} else {
|
||||
|
@ -1181,7 +1182,7 @@ static void win_determine_fg_shader(session_t *ps, struct managed_win *w) {
|
|||
|
||||
auto shader_new = ps->o.window_shader_fg;
|
||||
void *val = NULL;
|
||||
if (c2_match(ps, w, ps->o.window_shader_fg_rules, &val)) {
|
||||
if (c2_match(ps->c2_state, w, ps->o.window_shader_fg_rules, &val)) {
|
||||
shader_new = val;
|
||||
}
|
||||
|
||||
|
@ -1204,7 +1205,7 @@ void win_update_opacity_rule(session_t *ps, struct managed_win *w) {
|
|||
double opacity = 1.0;
|
||||
bool is_set = false;
|
||||
void *val = NULL;
|
||||
if (c2_match(ps, w, ps->o.opacity_rules, &val)) {
|
||||
if (c2_match(ps->c2_state, w, ps->o.opacity_rules, &val)) {
|
||||
opacity = ((double)(long)val) / 100.0;
|
||||
is_set = true;
|
||||
}
|
||||
|
@ -1220,6 +1221,7 @@ void win_update_opacity_rule(session_t *ps, struct managed_win *w) {
|
|||
*/
|
||||
void win_on_factor_change(session_t *ps, struct managed_win *w) {
|
||||
log_debug("Window %#010x (%s) factor change", w->base.id, w->name);
|
||||
c2_window_state_update(ps->c2_state, &w->c2_state, ps->c.c, w->client_win, w->base.id);
|
||||
// Focus and is_fullscreen needs to be updated first, as other rules might depend
|
||||
// on the focused state of the window
|
||||
win_update_focused(ps, w);
|
||||
|
@ -1235,17 +1237,17 @@ void win_on_factor_change(session_t *ps, struct managed_win *w) {
|
|||
log_debug("Window mode changed to %d", w->mode);
|
||||
win_update_opacity_rule(ps, w);
|
||||
if (w->a.map_state == XCB_MAP_STATE_VIEWABLE) {
|
||||
w->paint_excluded = c2_match(ps, w, ps->o.paint_blacklist, NULL);
|
||||
w->paint_excluded = c2_match(ps->c2_state, w, ps->o.paint_blacklist, NULL);
|
||||
}
|
||||
if (w->a.map_state == XCB_MAP_STATE_VIEWABLE) {
|
||||
w->unredir_if_possible_excluded =
|
||||
c2_match(ps, w, ps->o.unredir_if_possible_blacklist, NULL);
|
||||
c2_match(ps->c2_state, w, ps->o.unredir_if_possible_blacklist, NULL);
|
||||
}
|
||||
|
||||
w->fade_excluded = c2_match(ps, w, ps->o.fade_blacklist, NULL);
|
||||
w->fade_excluded = c2_match(ps->c2_state, w, ps->o.fade_blacklist, NULL);
|
||||
|
||||
w->transparent_clipping_excluded =
|
||||
c2_match(ps, w, ps->o.transparent_clipping_blacklist, NULL);
|
||||
c2_match(ps->c2_state, w, ps->o.transparent_clipping_blacklist, NULL);
|
||||
|
||||
win_update_opacity_target(ps, w);
|
||||
|
||||
|
@ -1472,6 +1474,7 @@ void free_win_res(session_t *ps, struct managed_win *w) {
|
|||
free(w->stale_props);
|
||||
w->stale_props = NULL;
|
||||
w->stale_props_capacity = 0;
|
||||
c2_window_state_destroy(ps->c2_state, &w->c2_state);
|
||||
}
|
||||
|
||||
/// Insert a new window after list_node `prev`
|
||||
|
@ -1729,6 +1732,7 @@ struct win *fill_win(session_t *ps, struct win *w) {
|
|||
ps->atoms->a_NET_WM_STATE,
|
||||
};
|
||||
win_set_properties_stale(new, init_stale_props, ARR_SIZE(init_stale_props));
|
||||
c2_window_state_init(ps->c2_state, &new->c2_state);
|
||||
|
||||
#ifdef CONFIG_DBUS
|
||||
// Send D-Bus signal
|
||||
|
|
Loading…
Reference in New Issue