mirror of
https://github.com/yshui/picom.git
synced 2024-11-18 13:55:36 -05:00
wm/win: fix handling of windows with multiple window types
ICCCM states a window can have multiple types, which we didn't handle. Fixing this also includes a slight change to the dbus interface, I hope that doesn't break too many people. Also this is pretty unfixable for `wintypes`, and we are moving to universal rules anyway, so I didn't bother. Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
parent
1cfd2a0d98
commit
dff14c538a
8 changed files with 100 additions and 37 deletions
|
@ -36,6 +36,7 @@
|
|||
* Type and format specifiers are no longer used in rules. These specifiers are what you put after the colon (':') in rules, e.g. the `:32c` in `"_GTK_FRAME_EXTENTS@:32c"`. Now this information is ignored and the property is matched regardless of format or type.
|
||||
* `backend` is now a required option. picom will not start if one is not specified explicitly.
|
||||
* New predefined target for conditions: `group_focused`. This target indicate whether the focused window is in the same window group as the window being matched.
|
||||
* Meaning of `window_type` in conditions changed slightly, now it supports windows with multiple types. (However the behavior of `wintypes` remains unchanged.)
|
||||
|
||||
## Deprecated features
|
||||
|
||||
|
|
|
@ -414,7 +414,7 @@ Supported predefined targets are: :::
|
|||
Whether the window bounding shape only has rounded corners, and is otherwise rectangular. This implies <<c2-bounding-shaped>>. Requires <<detect-rounded-corners>>. This has no relation to <<corner-radius>>.
|
||||
|
||||
`window_type`::::
|
||||
Window type, as defined by _pass:[_]NET_WM_WINDOW_TYPE_. Name only, e.g. _normal_ means _pass:[_]NET_WM_WINDOW_TYPE_NORMAL_.
|
||||
Window type, as defined by _pass:[_]NET_WM_WINDOW_TYPE_. Name only, e.g. _normal_ means _pass:[_]NET_WM_WINDOW_TYPE_NORMAL_. Because a window can have multiple types, testing for equality succeeds if any of the window's types match.
|
||||
|
||||
`name`::::
|
||||
Name of the window. This is either _pass:[_]NET_WM_NAME_ or _pass:[_]WM_NAME_.
|
||||
|
@ -538,7 +538,7 @@ When `@include` directive is used in the config file, picom will first search fo
|
|||
|
||||
picom uses general libconfig configuration file format. A sample configuration file is available as `picom.sample.conf` in the source tree. Most of command line switches can be used as options in configuration file as well. For example, *--vsync* option documented above can be set in the configuration file using `vsync = `. Command line options will always overwrite the settings in the configuration file.
|
||||
|
||||
Window-type-specific settings allow you to set window-specific options based on the window type. These settings are exposed only in configuration file. Using this is discouraged, see the xref:_window_rules[*WINDOW RULES*] section for the recommended way to set window-specific options. The format of this option is as follows:
|
||||
Window-type-specific settings allow you to set window-specific options based on the window type. These settings are exposed only in configuration file. The format of this option is as follows:
|
||||
|
||||
[#wintypes]
|
||||
------------
|
||||
|
@ -548,6 +548,10 @@ wintypes:
|
|||
};
|
||||
------------
|
||||
|
||||
WARNING: Using this is highly discouraged, see the xref:_window_rules[*WINDOW RULES*] section for the recommended way to set window-specific options.
|
||||
|
||||
IMPORTANT: According to the window manager specification, a window can have multiple types. But due to the limitation of how _wintypes_ was implemented, if a window has multiple types, then for the purpose of applying `wintypes` options, one of the window types will be chosen at random. Again, you are recommended to use xref:_window_rules[*WINDOW RULES*] instead.
|
||||
|
||||
_WINDOW_TYPE_ is one of the 15 window types defined in EWMH standard: "unknown", "desktop", "dock", "toolbar", "menu", "utility", "splash", "dialog", "normal", "dropdown_menu", "popup_menu", "tooltip", "notification", "combo", and "dnd".
|
||||
|
||||
Following per window-type options are available: ::
|
||||
|
|
14
src/c2.c
14
src/c2.c
|
@ -1712,14 +1712,22 @@ c2_match_once_leaf_string(struct atom *atoms, const struct win *w, const c2_l_t
|
|||
// A predefined target
|
||||
const char *predef_target = NULL;
|
||||
if (leaf->predef != C2_L_PUNDEFINED) {
|
||||
if (leaf->predef == C2_L_PWINDOWTYPE) {
|
||||
for (unsigned i = 0; i < NUM_WINTYPES; i++) {
|
||||
if (w->window_types & (1 << i) &&
|
||||
c2_string_op(leaf, WINTYPES[i].name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (leaf->predef) {
|
||||
case C2_L_PWINDOWTYPE:
|
||||
predef_target = WINTYPES[w->window_type].name;
|
||||
break;
|
||||
case C2_L_PNAME: predef_target = w->name; break;
|
||||
case C2_L_PCLASSG: predef_target = w->class_general; break;
|
||||
case C2_L_PCLASSI: predef_target = w->class_instance; break;
|
||||
case C2_L_PROLE: predef_target = w->role; break;
|
||||
case C2_L_PWINDOWTYPE:
|
||||
default: unreachable();
|
||||
}
|
||||
if (!predef_target) {
|
||||
|
|
|
@ -118,6 +118,10 @@
|
|||
# define __has_include(x) 0
|
||||
#endif
|
||||
|
||||
#ifndef __has_builtin
|
||||
# define __has_builtin(x) 0
|
||||
#endif
|
||||
|
||||
#if !defined(__STDC_NO_THREADS__) && __has_include(<threads.h>)
|
||||
# include <threads.h>
|
||||
#elif __STDC_VERSION__ >= 201112L
|
||||
|
@ -137,3 +141,19 @@ typedef unsigned int uint;
|
|||
static inline int attr_const popcntul(unsigned long a) {
|
||||
return __builtin_popcountl(a);
|
||||
}
|
||||
|
||||
/// Get the index of the lowest bit set in a number. The result is undefined if
|
||||
/// `a` is 0.
|
||||
static inline int attr_const index_of_lowest_one(unsigned a) {
|
||||
#if __has_builtin(__builtin_ctz)
|
||||
return __builtin_ctz(a);
|
||||
#else
|
||||
auto lowbit = (a & -a);
|
||||
int r = (lowbit & 0xAAAAAAAA) != 0;
|
||||
r |= ((lowbit & 0xCCCCCCCC) != 0) << 1;
|
||||
r |= ((lowbit & 0xF0F0F0F0) != 0) << 2;
|
||||
r |= ((lowbit & 0xFF00FF00) != 0) << 3;
|
||||
r |= ((lowbit & 0xFFFF0000) != 0) << 4;
|
||||
return r;
|
||||
#endif
|
||||
}
|
||||
|
|
26
src/dbus.c
26
src/dbus.c
|
@ -605,13 +605,33 @@ cdbus_process_window_property_get(session_t *ps, DBusMessage *msg, cdbus_window_
|
|||
|
||||
append(Mapped, bool_variant, w->state == WSTATE_MAPPED);
|
||||
append(Id, wid_variant, win_id(w));
|
||||
append(Type, string_variant, WINTYPES[w->window_type].name);
|
||||
append(RawFocused, bool_variant,
|
||||
w->a.map_state == XCB_MAP_STATE_VIEWABLE && w->is_focused);
|
||||
append(ClientWin, wid_variant, win_client_id(w, /*fallback_to_self=*/true));
|
||||
append(Leader, wid_variant, wm_ref_win_id(wm_ref_leader(w->tree_ref)));
|
||||
append_win_property(Name, name, string_variant);
|
||||
|
||||
if (!strcmp("Type", target)) {
|
||||
DBusMessageIter iter, sub;
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
}
|
||||
for (int i = 0; i < NUM_WINTYPES; i++) {
|
||||
if ((w->window_types & (1 << i)) == 0) {
|
||||
continue;
|
||||
}
|
||||
if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING,
|
||||
&WINTYPES[i].name)) {
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
}
|
||||
}
|
||||
if (!dbus_message_iter_close_container(&iter, &sub)) {
|
||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||
}
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
if (!strcmp("Next", target)) {
|
||||
cdbus_window_t next_id = 0;
|
||||
auto below = wm_ref_below(cursor);
|
||||
|
@ -736,7 +756,7 @@ cdbus_process_win_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBusE
|
|||
append_win_property(mode, enum);
|
||||
append_win_property(opacity, double);
|
||||
append_win_property(ever_damaged, boolean);
|
||||
append_win_property(window_type, enum);
|
||||
append_win_property(window_types, enum);
|
||||
append_win_property(name, string);
|
||||
append_win_property(class_instance, string);
|
||||
append_win_property(class_general, string);
|
||||
|
@ -1188,7 +1208,7 @@ static bool cdbus_process_window_introspect(DBusMessage *reply) {
|
|||
" <property type='b' name='RawFocused' access='read'/>\n"
|
||||
" <property type='b' name='Mapped' access='read'/>\n"
|
||||
" <property type='s' name='Name' access='read'/>\n"
|
||||
" <property type='s' name='Type' access='read'/>\n"
|
||||
" <property type='as' name='Type' access='read'/>\n"
|
||||
" </interface>\n"
|
||||
"</node>\n";
|
||||
// clang-format on
|
||||
|
|
|
@ -280,8 +280,12 @@ int inspect_main(int argc, char **argv, const char *config_file) {
|
|||
if (w->role != NULL) {
|
||||
printf(" role = '%s'\n", w->role);
|
||||
}
|
||||
if (w->window_type != WINTYPE_UNKNOWN) {
|
||||
printf(" window_type = '%s'\n", WINTYPES[w->window_type].name);
|
||||
if (w->window_types != 0) {
|
||||
for (int i = 0; i < NUM_WINTYPES; i++) {
|
||||
if (w->window_types & (1 << i)) {
|
||||
printf(" window_type = '%s'\n", WINTYPES[i].name);
|
||||
}
|
||||
}
|
||||
}
|
||||
printf(" %sfullscreen\n", w->is_fullscreen ? "" : "! ");
|
||||
if (w->bounding_shaped) {
|
||||
|
|
55
src/wm/win.c
55
src/wm/win.c
|
@ -105,7 +105,7 @@ static bool win_is_focused(session_t *ps, struct win *w) {
|
|||
}
|
||||
// Use wintype_focus, and treat WM windows and override-redirected
|
||||
// windows specially
|
||||
if (ps->o.wintype_option[w->window_type].focus ||
|
||||
if (ps->o.wintype_option[index_of_lowest_one(w->window_types)].focus ||
|
||||
(ps->o.mark_wmwin_focused && is_wmwin) ||
|
||||
(ps->o.mark_ovredir_focused && wm_ref_client_of(w->tree_ref) == NULL && !is_wmwin) ||
|
||||
(w->a.map_state == XCB_MAP_STATE_VIEWABLE &&
|
||||
|
@ -655,24 +655,26 @@ static inline bool win_bounding_shaped(struct x_connection *c, xcb_window_t wid)
|
|||
return bounding_shaped;
|
||||
}
|
||||
|
||||
static wintype_t
|
||||
wid_get_prop_wintype(struct x_connection *c, struct atom *atoms, xcb_window_t wid) {
|
||||
static uint32_t
|
||||
wid_get_prop_window_types(struct x_connection *c, struct atom *atoms, xcb_window_t wid) {
|
||||
winprop_t prop =
|
||||
x_get_prop(c, wid, atoms->a_NET_WM_WINDOW_TYPE, 32L, XCB_ATOM_ATOM, 32);
|
||||
|
||||
static_assert(NUM_WINTYPES <= 32, "too many window types");
|
||||
|
||||
uint32_t ret = 0;
|
||||
for (unsigned i = 0; i < prop.nitems; ++i) {
|
||||
for (wintype_t j = 1; j < NUM_WINTYPES; ++j) {
|
||||
if (get_atom_with_nul(atoms, WINTYPES[j].atom, c->c) ==
|
||||
(xcb_atom_t)prop.p32[i]) {
|
||||
free_winprop(&prop);
|
||||
return j;
|
||||
if (get_atom_with_nul(atoms, WINTYPES[j].atom, c->c) == prop.atom[i]) {
|
||||
ret |= (1 << j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free_winprop(&prop);
|
||||
|
||||
return WINTYPE_UNKNOWN;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// XXX should distinguish between frame has alpha and window body has alpha
|
||||
|
@ -745,12 +747,13 @@ static double win_calc_opacity_target(session_t *ps, const struct win *w, bool f
|
|||
return 0;
|
||||
}
|
||||
// Try obeying opacity property and window type opacity firstly
|
||||
auto window_type = index_of_lowest_one(w->window_types);
|
||||
if (w->has_opacity_prop) {
|
||||
opacity = ((double)w->opacity_prop) / OPAQUE;
|
||||
} else if (!safe_isnan(w->options.opacity)) {
|
||||
opacity = w->options.opacity;
|
||||
} else if (!safe_isnan(ps->o.wintype_option[w->window_type].opacity)) {
|
||||
opacity = ps->o.wintype_option[w->window_type].opacity;
|
||||
} else if (!safe_isnan(ps->o.wintype_option[window_type].opacity)) {
|
||||
opacity = ps->o.wintype_option[window_type].opacity;
|
||||
} else {
|
||||
// Respect active_opacity only when the window is physically
|
||||
// focused
|
||||
|
@ -840,7 +843,7 @@ static void win_determine_shadow(session_t *ps, struct win *w) {
|
|||
if (w->a.map_state != XCB_MAP_STATE_VIEWABLE) {
|
||||
return;
|
||||
}
|
||||
if (!ps->o.wintype_option[w->window_type].shadow) {
|
||||
if (!ps->o.wintype_option[index_of_lowest_one(w->window_types)].shadow) {
|
||||
log_debug("Shadow disabled by wintypes");
|
||||
w->options.shadow = TRI_FALSE;
|
||||
} else if (c2_match(ps->c2_state, w, ps->o.shadow_blacklist, NULL)) {
|
||||
|
@ -887,8 +890,9 @@ bool win_update_prop_fullscreen(struct x_connection *c, const struct atom *atoms
|
|||
}
|
||||
|
||||
static void win_determine_clip_shadow_above(session_t *ps, struct win *w) {
|
||||
bool should_crop = (ps->o.wintype_option[w->window_type].clip_shadow_above ||
|
||||
c2_match(ps->c2_state, w, ps->o.shadow_clip_list, NULL));
|
||||
bool should_crop =
|
||||
(ps->o.wintype_option[index_of_lowest_one(w->window_types)].clip_shadow_above ||
|
||||
c2_match(ps->c2_state, w, ps->o.shadow_clip_list, NULL));
|
||||
w->options.clip_shadow_above = should_crop ? TRI_TRUE : TRI_UNKNOWN;
|
||||
}
|
||||
|
||||
|
@ -918,7 +922,7 @@ static void win_determine_blur_background(session_t *ps, struct win *w) {
|
|||
|
||||
bool blur_background_new = ps->o.blur_method != BLUR_METHOD_NONE;
|
||||
if (blur_background_new) {
|
||||
if (!ps->o.wintype_option[w->window_type].blur_background) {
|
||||
if (!ps->o.wintype_option[index_of_lowest_one(w->window_types)].blur_background) {
|
||||
log_debug("Blur background disabled by wintypes");
|
||||
w->options.blur_background = TRI_FALSE;
|
||||
} else if (c2_match(ps->c2_state, w, ps->o.blur_background_blacklist, NULL)) {
|
||||
|
@ -1029,8 +1033,10 @@ void win_on_factor_change(session_t *ps, struct win *w) {
|
|||
// on the focused state of the window
|
||||
win_update_is_fullscreen(ps, w);
|
||||
|
||||
assert(w->window_types != 0);
|
||||
if (ps->o.rules == NULL) {
|
||||
bool focused = win_is_focused(ps, w);
|
||||
auto window_type = index_of_lowest_one(w->window_types);
|
||||
// Universal rules take precedence over wintype_option and
|
||||
// other exclusion/inclusion lists. And it also supersedes
|
||||
// some of the "override" options.
|
||||
|
@ -1056,7 +1062,7 @@ void win_on_factor_change(session_t *ps, struct win *w) {
|
|||
}
|
||||
if (w->a.map_state == XCB_MAP_STATE_VIEWABLE &&
|
||||
c2_match(ps->c2_state, w, ps->o.unredir_if_possible_blacklist, NULL)) {
|
||||
if (ps->o.wintype_option[w->window_type].redir_ignore) {
|
||||
if (ps->o.wintype_option[window_type].redir_ignore) {
|
||||
w->options.unredir = WINDOW_UNREDIR_PASSIVE;
|
||||
} else {
|
||||
w->options.unredir = WINDOW_UNREDIR_TERMINATE;
|
||||
|
@ -1067,7 +1073,7 @@ void win_on_factor_change(session_t *ps, struct win *w) {
|
|||
// look different after unredirecting. Instead we always follow
|
||||
// the request.
|
||||
w->options.unredir = WINDOW_UNREDIR_FORCED;
|
||||
} else if (ps->o.wintype_option[w->window_type].redir_ignore) {
|
||||
} else if (ps->o.wintype_option[window_type].redir_ignore) {
|
||||
w->options.unredir = WINDOW_UNREDIR_WHEN_POSSIBLE;
|
||||
}
|
||||
|
||||
|
@ -1078,7 +1084,7 @@ void win_on_factor_change(session_t *ps, struct win *w) {
|
|||
w->options.transparent_clipping = TRI_FALSE;
|
||||
}
|
||||
w->options.full_shadow =
|
||||
tri_from_bool(ps->o.wintype_option[w->window_type].full_shadow);
|
||||
tri_from_bool(ps->o.wintype_option[window_type].full_shadow);
|
||||
} else {
|
||||
struct win_update_rule_params params = {
|
||||
.w = w,
|
||||
|
@ -1136,27 +1142,27 @@ void win_on_win_size_change(struct win *w, int shadow_offset_x, int shadow_offse
|
|||
* Update window type.
|
||||
*/
|
||||
bool win_update_wintype(struct x_connection *c, struct atom *atoms, struct win *w) {
|
||||
const wintype_t wtype_old = w->window_type;
|
||||
const uint32_t wtypes_old = w->window_types;
|
||||
auto wid = win_client_id(w, /*fallback_to_self=*/true);
|
||||
|
||||
// Detect window type here
|
||||
w->window_type = wid_get_prop_wintype(c, atoms, wid);
|
||||
w->window_types = wid_get_prop_window_types(c, atoms, wid);
|
||||
|
||||
// Conform to EWMH standard, if _NET_WM_WINDOW_TYPE is not present, take
|
||||
// override-redirect windows or windows without WM_TRANSIENT_FOR as
|
||||
// _NET_WM_WINDOW_TYPE_NORMAL, otherwise as _NET_WM_WINDOW_TYPE_DIALOG.
|
||||
if (WINTYPE_UNKNOWN == w->window_type) {
|
||||
if (w->window_types == 0) {
|
||||
if (w->a.override_redirect ||
|
||||
!wid_has_prop(c->c, wid, atoms->aWM_TRANSIENT_FOR)) {
|
||||
w->window_type = WINTYPE_NORMAL;
|
||||
w->window_types = (1 << WINTYPE_NORMAL);
|
||||
} else {
|
||||
w->window_type = WINTYPE_DIALOG;
|
||||
w->window_types = (1 << WINTYPE_DIALOG);
|
||||
}
|
||||
}
|
||||
|
||||
log_debug("Window (%#010x) has type %s", win_id(w), WINTYPES[w->window_type].name);
|
||||
log_debug("Window (%#010x) has type %#x", win_id(w), w->window_types);
|
||||
|
||||
return w->window_type != wtype_old;
|
||||
return w->window_types != wtypes_old;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1253,7 +1259,6 @@ struct win *win_maybe_allocate(session_t *ps, struct wm_ref *cursor,
|
|||
// change
|
||||
|
||||
.mode = WMODE_TRANS,
|
||||
.window_type = WINTYPE_UNKNOWN,
|
||||
.opacity_prop = OPAQUE,
|
||||
.opacity_set = 1,
|
||||
.frame_extents = MARGIN_INIT,
|
||||
|
|
|
@ -165,8 +165,9 @@ struct win {
|
|||
bool in_openclose;
|
||||
|
||||
// Client window related members
|
||||
/// Type of the window.
|
||||
wintype_t window_type;
|
||||
/// A bitflag of window types. According to ICCCM, a window can have more than one
|
||||
/// type.
|
||||
uint32_t window_types;
|
||||
|
||||
// Blacklist related members
|
||||
/// Name of the window.
|
||||
|
|
Loading…
Reference in a new issue