From 564df864bf3b81c1bcd576a310e5ba297d8880a5 Mon Sep 17 00:00:00 2001 From: DreamMaoMao <2523610504@qq.com> Date: Wed, 25 Feb 2026 10:06:58 +0800 Subject: [PATCH] fix: popup position constrain not work for some app --- src/config/parse_config.h | 8 +- src/ext-protocol/text-input.h | 11 +-- src/fetch/monitor.h | 9 +++ src/mango.c | 137 ++++++++++++++++++++++++++-------- 4 files changed, 118 insertions(+), 47 deletions(-) diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 1c33383..95e54c0 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -3647,13 +3647,13 @@ void reapply_cursor_style(void) { cursor_mgr = wlr_xcursor_manager_create(config.cursor_theme, cursor_size); - if(cursor_size > 0){ - char size_str[16]; - snprintf(size_str, sizeof(size_str), "%d", cursor_size); + if (cursor_size > 0) { + char size_str[16]; + snprintf(size_str, sizeof(size_str), "%d", cursor_size); setenv("XCURSOR_SIZE", size_str, 1); } - if(config.cursor_theme){ + if (config.cursor_theme) { setenv("XCURSOR_THEME", config.cursor_theme, 1); } diff --git a/src/ext-protocol/text-input.h b/src/ext-protocol/text-input.h index dbd97e1..e9f221a 100644 --- a/src/ext-protocol/text-input.h +++ b/src/ext-protocol/text-input.h @@ -77,15 +77,6 @@ Monitor *output_from_wlr_output(struct wlr_output *wlr_output) { return NULL; } -Monitor *output_nearest_to(int32_t lx, int32_t ly) { - double closest_x, closest_y; - wlr_output_layout_closest_point(output_layout, NULL, lx, ly, &closest_x, - &closest_y); - - return output_from_wlr_output( - wlr_output_layout_output_at(output_layout, closest_x, closest_y)); -} - bool output_is_usable(Monitor *m) { return m && m->wlr_output->enabled; } static bool @@ -255,7 +246,7 @@ static void update_popup_position(struct dwl_input_method_popup *popup) { cursor_rect = (struct wlr_box){0}; } - output = output_nearest_to(cursor_rect.x, cursor_rect.y); + output = get_monitor_nearest_to(cursor_rect.x, cursor_rect.y); if (!output_is_usable(output)) { return; } diff --git a/src/fetch/monitor.h b/src/fetch/monitor.h index 7a1ca4d..fd18498 100644 --- a/src/fetch/monitor.h +++ b/src/fetch/monitor.h @@ -96,3 +96,12 @@ Monitor *xytomon(double x, double y) { struct wlr_output *o = wlr_output_layout_output_at(output_layout, x, y); return o ? o->data : NULL; } + +Monitor *get_monitor_nearest_to(int32_t lx, int32_t ly) { + double closest_x, closest_y; + wlr_output_layout_closest_point(output_layout, NULL, lx, ly, &closest_x, + &closest_y); + + return output_from_wlr_output( + wlr_output_layout_output_at(output_layout, closest_x, closest_y)); +} \ No newline at end of file diff --git a/src/mango.c b/src/mango.c index 886a8a2..c8288d0 100644 --- a/src/mango.c +++ b/src/mango.c @@ -486,6 +486,16 @@ typedef struct { bool being_unmapped; } LayerSurface; +typedef struct { + struct wlr_xdg_popup *wlr_popup; + Client *client; + LayerSurface *layer; + uint32_t type; + struct wl_listener destroy; + struct wl_listener commit; + struct wl_listener reposition; +} Popup; + typedef struct { const char *symbol; void (*arrange)(Monitor *); @@ -790,6 +800,7 @@ static Client *get_focused_stack_client(Client *sc); static bool client_is_in_same_stack(Client *sc, Client *tc, Client *fc); static void monitor_stop_skip_frame_timer(Monitor *m); static int monitor_skip_frame_timeout_callback(void *data); +static Monitor *get_monitor_nearest_to(int32_t lx, int32_t ly); #include "data/static_keymap.h" #include "dispatch/bind_declare.h" @@ -895,11 +906,8 @@ static KeyMode keymode = { .isdefault = true, }; -static char *env_vars[] = {"DISPLAY", - "WAYLAND_DISPLAY", - "XDG_CURRENT_DESKTOP", - "XDG_SESSION_TYPE", - NULL}; +static char *env_vars[] = {"DISPLAY", "WAYLAND_DISPLAY", "XDG_CURRENT_DESKTOP", + "XDG_SESSION_TYPE", NULL}; static struct { enum wp_cursor_shape_device_v1_shape shape; struct wlr_surface *surface; @@ -2545,37 +2553,107 @@ void destroydecoration(struct wl_listener *listener, void *data) { wl_list_remove(&c->set_decoration_mode.link); } -void commitpopup(struct wl_listener *listener, void *data) { +static void popup_unconstrain(Popup *popup) { + struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; + + if (!wlr_popup || !wlr_popup->parent) { + return; + } + + struct wlr_scene_node *parent_node = wlr_popup->parent->data; + if (!parent_node) { + wlr_log(WLR_ERROR, "Popup parent has no scene node"); + return; + } + int parent_lx, parent_ly; + wlr_scene_node_coords(parent_node, &parent_lx, &parent_ly); + + struct wlr_box *scheduled = &wlr_popup->scheduled.geometry; + int popup_lx = parent_lx + scheduled->x; + int popup_ly = parent_ly + scheduled->y; + + Monitor *mon = get_monitor_nearest_to(popup_lx, popup_ly); + + struct wlr_box usable = popup->type == LayerShell ? mon->m : mon->w; + + struct wlr_box constraint_box = { + .x = usable.x - parent_lx, + .y = usable.y - parent_ly, + .width = usable.width, + .height = usable.height, + }; + + wlr_xdg_popup_unconstrain_from_box(wlr_popup, &constraint_box); +} + +static void destroypopup(struct wl_listener *listener, void *data) { + Popup *popup = wl_container_of(listener, popup, destroy); + wl_list_remove(&popup->destroy.link); + wl_list_remove(&popup->reposition.link); + free(popup); +} + +static void commitpopup(struct wl_listener *listener, void *data) { + Popup *popup = wl_container_of(listener, popup, commit); + struct wlr_surface *surface = data; - struct wlr_xdg_popup *popup = wlr_xdg_popup_try_from_wlr_surface(surface); - LayerSurface *l = NULL; + struct wlr_xdg_popup *wkr_popup = + wlr_xdg_popup_try_from_wlr_surface(surface); + Client *c = NULL; - struct wlr_box box; + LayerSurface *l = NULL; int32_t type = -1; - if (!popup || !popup->base->initial_commit) + if (!wkr_popup || !wkr_popup->base->initial_commit) goto commitpopup_listen_free; - type = toplevel_from_wlr_surface(popup->base->surface, &c, &l); - if (!popup->parent || !popup->parent->data || type < 0) - goto commitpopup_listen_free; - - wlr_scene_node_raise_to_top(popup->parent->data); - - popup->base->surface->data = - wlr_scene_xdg_surface_create(popup->parent->data, popup->base); - if ((l && !l->mon) || (c && !c->mon)) { - wlr_xdg_popup_destroy(popup); + type = toplevel_from_wlr_surface(wkr_popup->base->surface, &c, &l); + if (!wkr_popup->parent || !wkr_popup->parent->data || type < 0) { + wlr_xdg_popup_destroy(wkr_popup); goto commitpopup_listen_free; } - box = type == LayerShell ? l->mon->m : c->mon->w; - box.x -= (type == LayerShell ? l->scene->node.x : c->geom.x); - box.y -= (type == LayerShell ? l->scene->node.y : c->geom.y); - wlr_xdg_popup_unconstrain_from_box(popup, &box); + + wlr_scene_node_raise_to_top(wkr_popup->parent->data); + + wkr_popup->base->surface->data = + wlr_scene_xdg_surface_create(wkr_popup->parent->data, wkr_popup->base); + if ((l && !l->mon) || (c && !c->mon)) { + wlr_xdg_popup_destroy(wkr_popup); + goto commitpopup_listen_free; + } + + popup->client = c; + popup->layer = l; + popup->type = type; + popup->wlr_popup = wkr_popup; + + popup_unconstrain(popup); commitpopup_listen_free: - wl_list_remove(&listener->link); - free(listener); + wl_list_remove(&popup->commit.link); + popup->commit.notify = NULL; +} + +static void repositionpopup(struct wl_listener *listener, void *data) { + Popup *popup = wl_container_of(listener, popup, reposition); + popup_unconstrain(popup); +} + +static void createpopup(struct wl_listener *listener, void *data) { + struct wlr_xdg_popup *wlr_popup = data; + + Popup *popup = calloc(1, sizeof(Popup)); + if (!popup) + return; + + popup->destroy.notify = destroypopup; + wl_signal_add(&wlr_popup->events.destroy, &popup->destroy); + + popup->commit.notify = commitpopup; + wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); + + popup->reposition.notify = repositionpopup; + wl_signal_add(&wlr_popup->events.reposition, &popup->reposition); } void createdecoration(struct wl_listener *listener, void *data) { @@ -3165,13 +3243,6 @@ void createpointerconstraint(struct wl_listener *listener, void *data) { &pointer_constraint->destroy, destroypointerconstraint); } -void createpopup(struct wl_listener *listener, void *data) { - /* This event is raised when a client (either xdg-shell or layer-shell) - * creates a new popup. */ - struct wlr_xdg_popup *popup = data; - LISTEN_STATIC(&popup->base->surface->events.commit, commitpopup); -} - void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint) { if (active_constraint == constraint) return;