mirror of
https://github.com/labwc/labwc.git
synced 2025-10-29 05:40:24 -04:00
window-rules: implement type filter
Co-Authored-By: Grigory Kirillov <txgk@bk.ru>
This commit is contained in:
parent
9be18f3009
commit
858e1c65cf
9 changed files with 157 additions and 79 deletions
|
|
@ -820,7 +820,7 @@ defined as shown below.
|
|||
<windowRules>
|
||||
|
||||
<!-- Action -->
|
||||
<windowRule identifier="" title="">
|
||||
<windowRule identifier="" title="" type="">
|
||||
<action name=""/>
|
||||
</windowRule>
|
||||
|
||||
|
|
@ -832,10 +832,10 @@ defined as shown below.
|
|||
|
||||
*Criteria*
|
||||
|
||||
*<windowRules><windowRule identifier="" title="" matchOnce="">*
|
||||
*<windowRules><windowRule identifier="" title="" type="" matchOnce="">*
|
||||
Define a window rule for any window which matches the criteria defined
|
||||
by the attributes *identifier* or *title*. If both are defined, AND
|
||||
logic is used, so both have to match.
|
||||
by the attributes *identifier*, *title*, or *type*. If more than one
|
||||
is defined, AND logic is used, so all have to match.
|
||||
Matching against patterns with '\*' (wildcard) and '?' (joker) is
|
||||
supported. Pattern matching is case-insensitive.
|
||||
|
||||
|
|
@ -844,6 +844,12 @@ defined as shown below.
|
|||
|
||||
*title* is the title of the window.
|
||||
|
||||
*type* [desktop|dock|toolbar|menu|utility|splash|dialog|dropdown_menu|
|
||||
popup_menu|tooltip|notification|combo|dnd|normal] relates to
|
||||
NET_WM_WINDOW_TYPE for XWayland clients. Native wayland clients have
|
||||
type "dialog" when they have a parent or a fixed size, or "normal"
|
||||
otherwise.
|
||||
|
||||
*matchOnce* can be true|false. If true, the rule will only apply to the
|
||||
first instance of the window with the specified identifier or title.
|
||||
|
||||
|
|
|
|||
|
|
@ -67,6 +67,26 @@ enum view_wants_focus {
|
|||
VIEW_WANTS_FOCUS_OFFER,
|
||||
};
|
||||
|
||||
enum window_type {
|
||||
/* https://specifications.freedesktop.org/wm-spec/wm-spec-1.4.html#idm45649101374512 */
|
||||
NET_WM_WINDOW_TYPE_DESKTOP = 0,
|
||||
NET_WM_WINDOW_TYPE_DOCK,
|
||||
NET_WM_WINDOW_TYPE_TOOLBAR,
|
||||
NET_WM_WINDOW_TYPE_MENU,
|
||||
NET_WM_WINDOW_TYPE_UTILITY,
|
||||
NET_WM_WINDOW_TYPE_SPLASH,
|
||||
NET_WM_WINDOW_TYPE_DIALOG,
|
||||
NET_WM_WINDOW_TYPE_DROPDOWN_MENU,
|
||||
NET_WM_WINDOW_TYPE_POPUP_MENU,
|
||||
NET_WM_WINDOW_TYPE_TOOLTIP,
|
||||
NET_WM_WINDOW_TYPE_NOTIFICATION,
|
||||
NET_WM_WINDOW_TYPE_COMBO,
|
||||
NET_WM_WINDOW_TYPE_DND,
|
||||
NET_WM_WINDOW_TYPE_NORMAL,
|
||||
|
||||
WINDOW_TYPE_LEN
|
||||
};
|
||||
|
||||
struct view;
|
||||
struct wlr_surface;
|
||||
|
||||
|
|
@ -113,6 +133,8 @@ struct view_impl {
|
|||
enum view_wants_focus (*wants_focus)(struct view *self);
|
||||
/* returns true if view reserves space at screen edge */
|
||||
bool (*has_strut_partial)(struct view *self);
|
||||
/* returns true if view declared itself a window type */
|
||||
bool (*contains_window_type)(struct view *view, int32_t window_type);
|
||||
};
|
||||
|
||||
struct view {
|
||||
|
|
@ -358,6 +380,7 @@ void view_array_append(struct server *server, struct wl_array *views,
|
|||
enum lab_view_criteria criteria);
|
||||
|
||||
enum view_wants_focus view_wants_focus(struct view *view);
|
||||
bool view_contains_window_type(struct view *view, enum window_type window_type);
|
||||
|
||||
/**
|
||||
* view_edge_invert() - select the opposite of a provided edge
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ enum property {
|
|||
struct window_rule {
|
||||
char *identifier;
|
||||
char *title;
|
||||
int window_type;
|
||||
bool match_once;
|
||||
|
||||
enum window_rule_event event;
|
||||
|
|
|
|||
|
|
@ -14,26 +14,6 @@ struct wlr_compositor;
|
|||
struct wlr_output;
|
||||
struct wlr_output_layout;
|
||||
|
||||
enum atom {
|
||||
/* https://specifications.freedesktop.org/wm-spec/wm-spec-1.4.html#idm45649101374512 */
|
||||
NET_WM_WINDOW_TYPE_DESKTOP = 0,
|
||||
NET_WM_WINDOW_TYPE_DOCK,
|
||||
NET_WM_WINDOW_TYPE_TOOLBAR,
|
||||
NET_WM_WINDOW_TYPE_MENU,
|
||||
NET_WM_WINDOW_TYPE_UTILITY,
|
||||
NET_WM_WINDOW_TYPE_SPLASH,
|
||||
NET_WM_WINDOW_TYPE_DIALOG,
|
||||
NET_WM_WINDOW_TYPE_DROPDOWN_MENU,
|
||||
NET_WM_WINDOW_TYPE_POPUP_MENU,
|
||||
NET_WM_WINDOW_TYPE_TOOLTIP,
|
||||
NET_WM_WINDOW_TYPE_NOTIFICATION,
|
||||
NET_WM_WINDOW_TYPE_COMBO,
|
||||
NET_WM_WINDOW_TYPE_DND,
|
||||
NET_WM_WINDOW_TYPE_NORMAL,
|
||||
|
||||
ATOM_LEN
|
||||
};
|
||||
|
||||
static const char * const atom_names[] = {
|
||||
"_NET_WM_WINDOW_TYPE_DESKTOP",
|
||||
"_NET_WM_WINDOW_TYPE_DOCK",
|
||||
|
|
@ -52,10 +32,10 @@ static const char * const atom_names[] = {
|
|||
};
|
||||
|
||||
static_assert(
|
||||
ARRAY_SIZE(atom_names) == ATOM_LEN,
|
||||
ARRAY_SIZE(atom_names) == WINDOW_TYPE_LEN,
|
||||
"Xwayland atoms out of sync");
|
||||
|
||||
extern xcb_atom_t atoms[ATOM_LEN];
|
||||
extern xcb_atom_t atoms[WINDOW_TYPE_LEN];
|
||||
|
||||
struct xwayland_unmanaged {
|
||||
struct server *server;
|
||||
|
|
@ -105,9 +85,6 @@ void xwayland_adjust_stacking_order(struct server *server);
|
|||
|
||||
struct wlr_xwayland_surface *xwayland_surface_from_view(struct view *view);
|
||||
|
||||
bool xwayland_surface_contains_window_type(
|
||||
struct wlr_xwayland_surface *surface, enum atom window_type);
|
||||
|
||||
void xwayland_server_init(struct server *server,
|
||||
struct wlr_compositor *compositor);
|
||||
void xwayland_server_finish(struct server *server);
|
||||
|
|
|
|||
|
|
@ -77,6 +77,45 @@ enum font_place {
|
|||
static void load_default_key_bindings(void);
|
||||
static void load_default_mouse_bindings(void);
|
||||
|
||||
static int
|
||||
parse_window_type(const char *type)
|
||||
{
|
||||
if (!type) {
|
||||
return -1;
|
||||
}
|
||||
if (!strcasecmp(type, "desktop")) {
|
||||
return NET_WM_WINDOW_TYPE_DESKTOP;
|
||||
} else if (!strcasecmp(type, "dock")) {
|
||||
return NET_WM_WINDOW_TYPE_DOCK;
|
||||
} else if (!strcasecmp(type, "toolbar")) {
|
||||
return NET_WM_WINDOW_TYPE_TOOLBAR;
|
||||
} else if (!strcasecmp(type, "menu")) {
|
||||
return NET_WM_WINDOW_TYPE_MENU;
|
||||
} else if (!strcasecmp(type, "utility")) {
|
||||
return NET_WM_WINDOW_TYPE_UTILITY;
|
||||
} else if (!strcasecmp(type, "splash")) {
|
||||
return NET_WM_WINDOW_TYPE_SPLASH;
|
||||
} else if (!strcasecmp(type, "dialog")) {
|
||||
return NET_WM_WINDOW_TYPE_DIALOG;
|
||||
} else if (!strcasecmp(type, "dropdown_menu")) {
|
||||
return NET_WM_WINDOW_TYPE_DROPDOWN_MENU;
|
||||
} else if (!strcasecmp(type, "popup_menu")) {
|
||||
return NET_WM_WINDOW_TYPE_POPUP_MENU;
|
||||
} else if (!strcasecmp(type, "tooltip")) {
|
||||
return NET_WM_WINDOW_TYPE_TOOLTIP;
|
||||
} else if (!strcasecmp(type, "notification")) {
|
||||
return NET_WM_WINDOW_TYPE_NOTIFICATION;
|
||||
} else if (!strcasecmp(type, "combo")) {
|
||||
return NET_WM_WINDOW_TYPE_COMBO;
|
||||
} else if (!strcasecmp(type, "dnd")) {
|
||||
return NET_WM_WINDOW_TYPE_DND;
|
||||
} else if (!strcasecmp(type, "normal")) {
|
||||
return NET_WM_WINDOW_TYPE_NORMAL;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fill_usable_area_override(char *nodename, char *content)
|
||||
{
|
||||
|
|
@ -127,6 +166,7 @@ fill_window_rule(char *nodename, char *content)
|
|||
{
|
||||
if (!strcasecmp(nodename, "windowRule.windowRules")) {
|
||||
current_window_rule = znew(*current_window_rule);
|
||||
current_window_rule->window_type = -1; // Window types are >= 0
|
||||
wl_list_append(&rc.window_rules, ¤t_window_rule->link);
|
||||
wl_list_init(¤t_window_rule->actions);
|
||||
return;
|
||||
|
|
@ -145,6 +185,8 @@ fill_window_rule(char *nodename, char *content)
|
|||
} else if (!strcmp(nodename, "title")) {
|
||||
free(current_window_rule->title);
|
||||
current_window_rule->title = xstrdup(content);
|
||||
} else if (!strcmp(nodename, "type")) {
|
||||
current_window_rule->window_type = parse_window_type(content);
|
||||
} else if (!strcasecmp(nodename, "matchOnce")) {
|
||||
set_bool(content, ¤t_window_rule->match_once);
|
||||
|
||||
|
|
@ -1493,7 +1535,7 @@ validate(void)
|
|||
/* Window-rule criteria */
|
||||
struct window_rule *rule, *rule_tmp;
|
||||
wl_list_for_each_safe(rule, rule_tmp, &rc.window_rules, link) {
|
||||
if (!rule->identifier && !rule->title) {
|
||||
if (!rule->identifier && !rule->title && rule->window_type < 0) {
|
||||
wlr_log(WLR_ERROR, "Deleting rule %p as it has no criteria", rule);
|
||||
rule_destroy(rule);
|
||||
}
|
||||
|
|
|
|||
10
src/view.c
10
src/view.c
|
|
@ -211,6 +211,16 @@ view_wants_focus(struct view *view)
|
|||
return VIEW_WANTS_FOCUS_ALWAYS;
|
||||
}
|
||||
|
||||
bool
|
||||
view_contains_window_type(struct view *view, enum window_type window_type)
|
||||
{
|
||||
assert(view);
|
||||
if (view->impl->contains_window_type) {
|
||||
return view->impl->contains_window_type(view, window_type);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
view_is_focusable(struct view *view)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -14,63 +14,50 @@
|
|||
#include "window-rules.h"
|
||||
|
||||
static bool
|
||||
other_instances_exist(struct view *self, const char *id, const char *title)
|
||||
matches_criteria(struct window_rule *rule, struct view *view)
|
||||
{
|
||||
const char *id = view_get_string_prop(view, "app_id");
|
||||
const char *title = view_get_string_prop(view, "title");
|
||||
|
||||
if (rule->identifier) {
|
||||
if (!id || !match_glob(rule->identifier, id)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (rule->title) {
|
||||
if (!title || !match_glob(rule->title, title)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (rule->window_type >= 0) {
|
||||
if (!view_contains_window_type(view, rule->window_type)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
other_instances_exist(struct window_rule *rule, struct view *self)
|
||||
{
|
||||
struct wl_list *views = &self->server->views;
|
||||
const char *prop = NULL;
|
||||
struct view *view;
|
||||
|
||||
wl_list_for_each(view, views, link) {
|
||||
if (view == self) {
|
||||
continue;
|
||||
}
|
||||
if (id) {
|
||||
prop = view_get_string_prop(view, "app_id");
|
||||
if (prop && !strcmp(prop, id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (title) {
|
||||
prop = view_get_string_prop(view, "title");
|
||||
if (prop && !strcmp(prop, title)) {
|
||||
return true;
|
||||
}
|
||||
if (view != self && matches_criteria(rule, view)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Try to match against identifier AND title (if set) */
|
||||
static bool
|
||||
view_matches_criteria(struct window_rule *rule, struct view *view)
|
||||
{
|
||||
const char *id = view_get_string_prop(view, "app_id");
|
||||
const char *title = view_get_string_prop(view, "title");
|
||||
|
||||
if (rule->match_once && other_instances_exist(view, id, title)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rule->identifier && rule->title) {
|
||||
if (!id || !title) {
|
||||
return false;
|
||||
}
|
||||
return match_glob(rule->identifier, id)
|
||||
&& match_glob(rule->title, title);
|
||||
} else if (rule->identifier) {
|
||||
if (!id) {
|
||||
return false;
|
||||
}
|
||||
return match_glob(rule->identifier, id);
|
||||
} else if (rule->title) {
|
||||
if (!title) {
|
||||
return false;
|
||||
}
|
||||
return match_glob(rule->title, title);
|
||||
} else {
|
||||
wlr_log(WLR_ERROR, "rule has no identifier or title\n");
|
||||
if (rule->match_once && other_instances_exist(rule, view)) {
|
||||
return false;
|
||||
}
|
||||
return matches_criteria(rule, view);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
23
src/xdg.c
23
src/xdg.c
|
|
@ -43,6 +43,28 @@ xdg_toplevel_from_view(struct view *view)
|
|||
return xdg_surface->toplevel;
|
||||
}
|
||||
|
||||
static bool
|
||||
xdg_toplevel_view_contains_window_type(struct view *view, int32_t window_type)
|
||||
{
|
||||
assert(view);
|
||||
|
||||
struct wlr_xdg_toplevel *toplevel = xdg_toplevel_from_view(view);
|
||||
struct wlr_xdg_toplevel_state *state = &toplevel->current;
|
||||
bool is_dialog = (state->min_width != 0 && state->min_height != 0
|
||||
&& (state->min_width == state->max_width
|
||||
|| state->min_height == state->max_height))
|
||||
|| toplevel->parent;
|
||||
|
||||
switch (window_type) {
|
||||
case NET_WM_WINDOW_TYPE_NORMAL:
|
||||
return !is_dialog;
|
||||
case NET_WM_WINDOW_TYPE_DIALOG:
|
||||
return is_dialog;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_new_popup(struct wl_listener *listener, void *data)
|
||||
{
|
||||
|
|
@ -652,6 +674,7 @@ static const struct view_impl xdg_toplevel_view_impl = {
|
|||
.move_to_back = view_impl_move_to_back,
|
||||
.get_root = xdg_toplevel_view_get_root,
|
||||
.append_children = xdg_toplevel_view_append_children,
|
||||
.contains_window_type = xdg_toplevel_view_contains_window_type,
|
||||
};
|
||||
|
||||
static void
|
||||
|
|
|
|||
|
|
@ -15,13 +15,13 @@
|
|||
#include "workspaces.h"
|
||||
#include "xwayland.h"
|
||||
|
||||
xcb_atom_t atoms[ATOM_LEN] = {0};
|
||||
xcb_atom_t atoms[WINDOW_TYPE_LEN] = {0};
|
||||
|
||||
static void xwayland_view_unmap(struct view *view, bool client_request);
|
||||
|
||||
bool
|
||||
static bool
|
||||
xwayland_surface_contains_window_type(
|
||||
struct wlr_xwayland_surface *surface, enum atom window_type)
|
||||
struct wlr_xwayland_surface *surface, enum window_type window_type)
|
||||
{
|
||||
assert(surface);
|
||||
for (size_t i = 0; i < surface->window_type_len; i++) {
|
||||
|
|
@ -32,6 +32,14 @@ xwayland_surface_contains_window_type(
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
xwayland_view_contains_window_type(struct view *view, int32_t window_type)
|
||||
{
|
||||
assert(view);
|
||||
struct wlr_xwayland_surface *surface = xwayland_surface_from_view(view);
|
||||
return xwayland_surface_contains_window_type(surface, window_type);
|
||||
}
|
||||
|
||||
static struct view_size_hints
|
||||
xwayland_view_get_size_hints(struct view *view)
|
||||
{
|
||||
|
|
@ -845,6 +853,7 @@ static const struct view_impl xwayland_view_impl = {
|
|||
.get_size_hints = xwayland_view_get_size_hints,
|
||||
.wants_focus = xwayland_view_wants_focus,
|
||||
.has_strut_partial = xwayland_view_has_strut_partial,
|
||||
.contains_window_type = xwayland_view_contains_window_type,
|
||||
};
|
||||
|
||||
void
|
||||
|
|
@ -926,15 +935,15 @@ sync_atoms(xcb_connection_t *xcb_conn)
|
|||
assert(xcb_conn);
|
||||
|
||||
wlr_log(WLR_DEBUG, "Syncing X11 atoms");
|
||||
xcb_intern_atom_cookie_t cookies[ATOM_LEN];
|
||||
xcb_intern_atom_cookie_t cookies[WINDOW_TYPE_LEN];
|
||||
|
||||
/* First request everything and then loop over the results to reduce latency */
|
||||
for (size_t i = 0; i < ATOM_LEN; i++) {
|
||||
for (size_t i = 0; i < WINDOW_TYPE_LEN; i++) {
|
||||
cookies[i] = xcb_intern_atom(xcb_conn, 0,
|
||||
strlen(atom_names[i]), atom_names[i]);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ATOM_LEN; i++) {
|
||||
for (size_t i = 0; i < WINDOW_TYPE_LEN; i++) {
|
||||
xcb_generic_error_t *err = NULL;
|
||||
xcb_intern_atom_reply_t *reply =
|
||||
xcb_intern_atom_reply(xcb_conn, cookies[i], &err);
|
||||
|
|
@ -960,7 +969,7 @@ handle_server_ready(struct wl_listener *listener, void *data)
|
|||
wlr_log(WLR_ERROR, "Failed to create xcb connection");
|
||||
|
||||
/* Just clear all existing atoms */
|
||||
for (size_t i = 0; i < ATOM_LEN; i++) {
|
||||
for (size_t i = 0; i < WINDOW_TYPE_LEN; i++) {
|
||||
atoms[i] = XCB_ATOM_NONE;
|
||||
}
|
||||
return;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue