Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Manuel Barrio Linares 2025-12-10 14:26:44 -03:00
commit fd6d62eaed
18 changed files with 248 additions and 75 deletions

View file

@ -414,6 +414,20 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
goto cleanup;
}
break;
case ACTION_TYPE_RESIZE:
if (!strcmp(argument, "direction")) {
enum lab_edge edge = lab_edge_parse(content,
/*tiled*/ true, /*any*/ false);
if (edge == LAB_EDGE_NONE || edge == LAB_EDGE_CENTER) {
wlr_log(WLR_ERROR,
"Invalid argument for action %s: '%s' (%s)",
action_names[action->type], argument, content);
} else {
action_arg_add_int(action, argument, edge);
}
goto cleanup;
}
break;
case ACTION_TYPE_RESIZE_RELATIVE:
if (!strcmp(argument, "left") || !strcmp(argument, "right") ||
!strcmp(argument, "top") || !strcmp(argument, "bottom")) {
@ -1223,8 +1237,17 @@ run_action(struct view *view, struct server *server, struct action *action,
break;
case ACTION_TYPE_RESIZE:
if (view) {
enum lab_edge resize_edges = cursor_get_resize_edges(
server->seat.cursor, ctx);
/*
* If a direction was specified in the config, honour it.
* Otherwise, fall back to determining the resize edges from
* the current cursor position (existing behaviour).
*/
enum lab_edge resize_edges =
action_get_int(action, "direction", LAB_EDGE_NONE);
if (resize_edges == LAB_EDGE_NONE) {
resize_edges = cursor_get_resize_edges(
server->seat.cursor, ctx);
}
interactive_begin(view, LAB_INPUT_STATE_RESIZE,
resize_edges);
}

View file

@ -714,6 +714,8 @@ fill_libinput_category(xmlNode *node)
char *key, *content;
LAB_XML_FOR_EACH(node, child, key, content) {
if (string_null_or_empty(content)) {
wlr_log(WLR_ERROR, "Empty string is not allowed for "
"<libinput><device><%s>. Ignoring.", key);
continue;
}
if (!strcmp(key, "category")) {
@ -1076,7 +1078,8 @@ entry(xmlNode *node, char *nodename, char *content)
return true;
} else if (str_space_only(content)) {
/* ignore empty leaf nodes other than above */
wlr_log(WLR_ERROR, "Empty string is not allowed for %s. "
"Ignoring.", nodename);
/* handle non-empty leaf nodes */
} else if (!strcmp(nodename, "decoration.core")) {
@ -1177,7 +1180,14 @@ entry(xmlNode *node, char *nodename, char *content)
} else if (!strcasecmp(nodename, "unMaximizeThreshold.resistance")) {
rc.unmaximize_threshold = atoi(content);
} else if (!strcasecmp(nodename, "range.snapping")) {
rc.snap_edge_range = atoi(content);
rc.snap_edge_range_inner = atoi(content);
rc.snap_edge_range_outer = atoi(content);
wlr_log(WLR_ERROR, "<snapping><range> is deprecated. "
"Use <snapping><range inner=\"\" outer=\"\"> instead.");
} else if (!strcasecmp(nodename, "inner.range.snapping")) {
rc.snap_edge_range_inner = atoi(content);
} else if (!strcasecmp(nodename, "outer.range.snapping")) {
rc.snap_edge_range_outer = atoi(content);
} else if (!strcasecmp(nodename, "cornerRange.snapping")) {
rc.snap_edge_corner_range = atoi(content);
} else if (!strcasecmp(nodename, "enabled.overlay.snapping")) {
@ -1230,6 +1240,15 @@ entry(xmlNode *node, char *nodename, char *content)
wlr_log(WLR_ERROR, "Invalid windowSwitcher output %s: "
"should be one of all|focused|cursor", content);
}
} else if (!strcasecmp(nodename, "order.windowSwitcher")) {
if (!strcasecmp(content, "focus")) {
rc.window_switcher.order = WINDOW_SWITCHER_ORDER_FOCUS;
} else if (!strcasecmp(content, "age")) {
rc.window_switcher.order = WINDOW_SWITCHER_ORDER_AGE;
} else {
wlr_log(WLR_ERROR, "Invalid windowSwitcher order %s: "
"should be one of focus|age", content);
}
/* The following two are for backward compatibility only. */
} else if (!strcasecmp(nodename, "show.windowSwitcher")) {
@ -1456,7 +1475,8 @@ rcxml_init(void)
rc.unsnap_threshold = 20;
rc.unmaximize_threshold = 150;
rc.snap_edge_range = 10;
rc.snap_edge_range_inner = 10;
rc.snap_edge_range_outer = 10;
rc.snap_edge_corner_range = 50;
rc.snap_overlay_enabled = true;
rc.snap_overlay_delay_inner = 500;
@ -1474,6 +1494,7 @@ rcxml_init(void)
rc.window_switcher.criteria = LAB_VIEW_CRITERIA_CURRENT_WORKSPACE
| LAB_VIEW_CRITERIA_ROOT_TOPLEVEL
| LAB_VIEW_CRITERIA_NO_SKIP_WINDOW_SWITCHER;
rc.window_switcher.order = WINDOW_SWITCHER_ORDER_FOCUS;
rc.resize_indicator = LAB_RESIZE_INDICATOR_NEVER;
rc.resize_draw_contents = true;
@ -1648,9 +1669,8 @@ load_default_window_switcher_fields(void)
#endif
};
struct cycle_osd_field *field;
for (size_t i = 0; i < ARRAY_SIZE(fields); i++) {
field = znew(*field);
struct cycle_osd_field *field = znew(*field);
field->content = fields[i].content;
field->width = fields[i].width;
wl_list_append(&rc.window_switcher.fields, &field->link);

View file

@ -276,13 +276,31 @@ create_osd_on_output(struct output *output)
assert(output->cycle_osd.tree);
}
static void
insert_view_ordered_by_age(struct wl_list *views, struct view *new_view)
{
struct wl_list *link = views;
struct view *view;
wl_list_for_each(view, views, cycle_link) {
if (view->creation_id >= new_view->creation_id) {
break;
}
link = &view->cycle_link;
}
wl_list_insert(link, &new_view->cycle_link);
}
/* Return false on failure */
static bool
init_cycle(struct server *server)
{
struct view *view;
for_each_view(view, &server->views, rc.window_switcher.criteria) {
wl_list_append(&server->cycle.views, &view->cycle_link);
if (rc.window_switcher.order == WINDOW_SWITCHER_ORDER_AGE) {
insert_view_ordered_by_age(&server->cycle.views, view);
} else {
wl_list_append(&server->cycle.views, &view->cycle_link);
}
}
if (wl_list_empty(&server->cycle.views)) {
wlr_log(WLR_DEBUG, "no views to switch between");

View file

@ -144,7 +144,6 @@ cycle_osd_classic_create(struct output *output)
y += switcher_theme->item_height;
}
struct buf buf = BUF_INIT;
int nr_fields = wl_list_length(&rc.window_switcher.fields);
/* This is the width of the area available for text fields */
@ -194,9 +193,9 @@ cycle_osd_classic_create(struct output *output)
/* Highlight around selected window's item */
struct lab_scene_rect_options highlight_opts = {
.border_colors = (float *[1]) {active_border_color},
.bg_color = active_bg_color,
.nr_borders = 1,
.border_width = switcher_theme->item_active_border_width,
.bg_color = active_bg_color,
.width = w - 2 * padding,
.height = switcher_theme->item_height,
};
@ -216,7 +215,6 @@ cycle_osd_classic_create(struct output *output)
y += switcher_theme->item_height;
}
buf_reset(&buf);
error:;
/* Center OSD */

View file

@ -251,6 +251,9 @@ err:
* (e.g. "thunderbird" matches "org.mozilla.Thunderbird.desktop"
* and "XTerm" matches "xterm.desktop"). This is not per any spec
* but is needed to find icons for existing applications.
*
* The second loop tries to match more partial strings, for
* example "gimp-2.0" would match "org.something.gimp.desktop".
*/
static struct sfdo_desktop_entry *
get_db_entry_by_id_fuzzy(struct sfdo_desktop_db *db, const char *app_id)
@ -258,6 +261,7 @@ get_db_entry_by_id_fuzzy(struct sfdo_desktop_db *db, const char *app_id)
size_t n_entries;
struct sfdo_desktop_entry **entries = sfdo_desktop_db_get_entries(db, &n_entries);
/* Would match "org.foobar.xterm" when given app-id "XTerm" */
for (size_t i = 0; i < n_entries; i++) {
struct sfdo_desktop_entry *entry = entries[i];
const char *desktop_id = sfdo_desktop_entry_get_id(entry, NULL);
@ -266,6 +270,8 @@ get_db_entry_by_id_fuzzy(struct sfdo_desktop_db *db, const char *app_id)
const char *desktop_id_base = dot ? (dot + 1) : desktop_id;
if (!strcasecmp(app_id, desktop_id_base)) {
wlr_log(WLR_DEBUG, "'%s' to '%s.desktop' via case-insensitive match",
app_id, desktop_id);
return entry;
}
@ -278,20 +284,31 @@ get_db_entry_by_id_fuzzy(struct sfdo_desktop_db *db, const char *app_id)
const char *wm_class =
sfdo_desktop_entry_get_startup_wm_class(entry, NULL);
if (wm_class && !strcasecmp(app_id, wm_class)) {
wlr_log(WLR_DEBUG, "'%s' to '%s.desktop' via StartupWMClass",
app_id, desktop_id);
return entry;
}
}
/* Try matching partial strings - catches GIMP, among others */
/* Would match "org.foobar.xterm-unicode" when given app-id "XTerm" */
const int app_id_len = strlen(app_id);
for (size_t i = 0; i < n_entries; i++) {
struct sfdo_desktop_entry *entry = entries[i];
const char *desktop_id = sfdo_desktop_entry_get_id(entry, NULL);
const char *dot = strrchr(desktop_id, '.');
const char *desktop_id_base = dot ? (dot + 1) : desktop_id;
int alen = strlen(app_id);
int dlen = strlen(desktop_id_base);
if (!strncasecmp(app_id, desktop_id_base, alen > dlen ? dlen : alen)) {
const int dlen = strlen(desktop_id_base);
const int cmp_len = MIN(app_id_len, dlen);
if (cmp_len < 3) {
/*
* Without this check, app-id "foot" would match
* "something.f" and any app-id would match "R.E.P.O."
*/
continue;
}
if (!strncasecmp(app_id, desktop_id_base, cmp_len)) {
wlr_log(WLR_DEBUG, "'%s' to '%s.desktop' via partial match",
app_id, desktop_id);
return entry;
}
}
@ -304,10 +321,14 @@ get_desktop_entry(struct sfdo *sfdo, const char *app_id)
{
struct sfdo_desktop_entry *entry = sfdo_desktop_db_get_entry_by_id(
sfdo->desktop_db, app_id, SFDO_NT);
if (!entry) {
entry = get_db_entry_by_id_fuzzy(sfdo->desktop_db, app_id);
if (entry) {
wlr_log(WLR_DEBUG, "matched '%s.desktop' via exact match", app_id);
return entry;
}
entry = get_db_entry_by_id_fuzzy(sfdo->desktop_db, app_id);
if (!entry) {
wlr_log(WLR_DEBUG, "failed to find .desktop file for '%s'", app_id);
}
return entry;
}

View file

@ -840,10 +840,12 @@ apply_constraint(struct seat *seat, struct wlr_pointer *pointer, double *x, doub
if (!seat->server->active_view) {
return;
}
if (!seat->current_constraint || pointer->base.type != WLR_INPUT_DEVICE_POINTER) {
if (!seat->current_constraint
|| pointer->base.type != WLR_INPUT_DEVICE_POINTER
|| seat->current_constraint->type
!= WLR_POINTER_CONSTRAINT_V1_CONFINED) {
return;
}
assert(seat->current_constraint->type == WLR_POINTER_CONSTRAINT_V1_CONFINED);
double sx = seat->cursor->x;
double sy = seat->cursor->y;
@ -866,7 +868,9 @@ cursor_locked(struct seat *seat, struct wlr_pointer *pointer)
{
return seat->current_constraint
&& pointer->base.type == WLR_INPUT_DEVICE_POINTER
&& seat->current_constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED;
&& seat->current_constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED
&& seat->current_constraint->surface
== seat->seat->pointer_state.focused_surface;
}
static void
@ -981,11 +985,6 @@ handle_motion_absolute(struct wl_listener *listener, void *data)
double dx = lx - seat->cursor->x;
double dy = ly - seat->cursor->y;
wlr_relative_pointer_manager_v1_send_relative_motion(
seat->server->relative_pointer_manager,
seat->seat, (uint64_t)event->time_msec * 1000,
dx, dy, dx, dy);
preprocess_cursor_motion(seat, event->pointer,
event->time_msec, dx, dy);
}

View file

@ -186,7 +186,7 @@ edge_from_cursor(struct seat *seat, struct output **dest_output,
return false;
}
if (rc.snap_edge_range == 0) {
if (rc.snap_edge_range_inner == 0 && rc.snap_edge_range_outer == 0) {
return false;
}
@ -197,9 +197,31 @@ edge_from_cursor(struct seat *seat, struct output **dest_output,
}
*dest_output = output;
/* Translate into output local coordinates */
double cursor_x = seat->cursor->x;
double cursor_y = seat->cursor->y;
int top_range = rc.snap_edge_range_outer;
int bottom_range = rc.snap_edge_range_outer;
int left_range = rc.snap_edge_range_outer;
int right_range = rc.snap_edge_range_outer;
if (wlr_output_layout_adjacent_output(seat->server->output_layout, WLR_DIRECTION_UP,
output->wlr_output, cursor_x, cursor_y)) {
top_range = rc.snap_edge_range_inner;
}
if (wlr_output_layout_adjacent_output(seat->server->output_layout, WLR_DIRECTION_DOWN,
output->wlr_output, cursor_x, cursor_y)) {
bottom_range = rc.snap_edge_range_inner;
}
if (wlr_output_layout_adjacent_output(seat->server->output_layout, WLR_DIRECTION_LEFT,
output->wlr_output, cursor_x, cursor_y)) {
left_range = rc.snap_edge_range_inner;
}
if (wlr_output_layout_adjacent_output(seat->server->output_layout, WLR_DIRECTION_RIGHT,
output->wlr_output, cursor_x, cursor_y)) {
right_range = rc.snap_edge_range_inner;
}
/* Translate into output local coordinates */
wlr_output_layout_output_coords(seat->server->output_layout,
output->wlr_output, &cursor_x, &cursor_y);
@ -210,13 +232,13 @@ edge_from_cursor(struct seat *seat, struct output **dest_output,
int left = cursor_x - area->x;
int right = area->x + area->width - cursor_x;
if (top < rc.snap_edge_range) {
if (top < top_range) {
*edge1 = LAB_EDGE_TOP;
} else if (bottom < rc.snap_edge_range) {
} else if (bottom < bottom_range) {
*edge1 = LAB_EDGE_BOTTOM;
} else if (left < rc.snap_edge_range) {
} else if (left < left_range) {
*edge1 = LAB_EDGE_LEFT;
} else if (right < rc.snap_edge_range) {
} else if (right < right_range) {
*edge1 = LAB_EDGE_RIGHT;
} else {
return false;

View file

@ -1085,6 +1085,7 @@ handle_new_xdg_toplevel(struct wl_listener *listener, void *data)
CONNECT_SIGNAL(xdg_surface, xdg_toplevel_view, new_popup);
wl_list_insert(&server->views, &view->link);
view->creation_id = server->next_view_creation_id++;
}
static void

View file

@ -1036,6 +1036,7 @@ xwayland_view_create(struct server *server,
CONNECT_SIGNAL(xsurface, xwayland_view, map_request);
wl_list_insert(&view->server->views, &view->link);
view->creation_id = view->server->next_view_creation_id++;
if (xsurface->surface) {
handle_associate(&xwayland_view->associate, NULL);