Unpublish scene trees from surfaces outliving them

bd14fad1446df5403ab00341e646dd7ce8cd821d
Alexis Sellier committed ago 1 parent a09d7a06
swm.c +58 -21
102 102
#define MAX_LAYER_SURFACES      256
103 103
#define MAX_KEYBOARD_GROUPS     32
104 104
#define MAX_POINTER_CONSTRAINTS 256
105 105
#define MAX_TEXT_INPUTS         128
106 106
#define MAX_INPUT_POPUPS        64
107 +
#define MAX_POPUPS              256
107 108
#define MAX_SESSION_LOCKS       4
108 109
#define MAX_PENDING_SPAWNS      256
109 110
#define MAX_WINDOW_STATES       512
110 111
#define MAX_WINDOW_STATE_FIELD  256
111 112
#define MAX_WINDOW_STATE_LINE   1024
376 377
    struct wl_listener        commit;
377 378
    struct wl_listener        disable;
378 379
    struct wl_listener        destroy;
379 380
} text_input_t;
380 381
382 +
/* XDG popup state. */
383 +
typedef struct {
384 +
    struct wlr_xdg_popup *popup;
385 +
    struct wl_listener    commit;
386 +
    struct wl_listener    destroy;
387 +
} popup_t;
388 +
381 389
/* Input-method popup state. */
382 390
typedef struct {
383 391
    struct wlr_input_popup_surface_v2 *popup;
384 392
    struct wlr_scene_tree             *scene;
385 393
    struct wl_list                     link;
450 458
static void              cleanup_listeners(void);
451 459
static void              close_monitor(monitor_t *m);
452 460
static void              layer_surface_commit_notify(struct wl_listener *listener, void *data);
453 461
static void              commit_notify(struct wl_listener *listener, void *data);
454 462
static void              popup_commit(struct wl_listener *listener, void *data);
463 +
static void              popup_destroy(struct wl_listener *listener, void *data);
455 464
static void              create_decoration(struct wl_listener *listener, void *data);
456 465
static void              create_idle_inhibitor(struct wl_listener *listener, void *data);
457 466
static void              create_keyboard(struct wlr_keyboard *keyboard);
458 467
static keyboard_group_t *create_keyboard_group(bool is_virtual);
459 468
static void              create_layer_surface(struct wl_listener *listener, void *data);
825 834
static layer_surface_t      layer_surface_items[MAX_LAYER_SURFACES];
826 835
static keyboard_group_t     keyboard_group_items[MAX_KEYBOARD_GROUPS];
827 836
static pointer_constraint_t pointer_constraint_items[MAX_POINTER_CONSTRAINTS];
828 837
static text_input_t         text_input_items[MAX_TEXT_INPUTS];
829 838
static input_popup_t        input_popup_items[MAX_INPUT_POPUPS];
839 +
static popup_t              popup_items[MAX_POPUPS];
830 840
static session_lock_t       session_lock_items[MAX_SESSION_LOCKS];
831 841
static pending_spawn_t      pending_spawn_items[MAX_PENDING_SPAWNS];
832 842
static window_state_t       window_state_items[MAX_WINDOW_STATES];
833 843
static static_listener_t    static_listener_items[MAX_STATIC_LISTENERS];
834 844
static workspace_manager_t  workspace_manager_items[MAX_WS_MANAGERS];
837 847
838 848
static bool client_used[MAX_CLIENTS], monitor_used[MAX_MONITORS];
839 849
static bool layer_surface_used[MAX_LAYER_SURFACES], keyboard_group_used[MAX_KEYBOARD_GROUPS];
840 850
static bool pointer_constraint_used[MAX_POINTER_CONSTRAINTS], text_input_used[MAX_TEXT_INPUTS];
841 851
static bool input_popup_used[MAX_INPUT_POPUPS], session_lock_used[MAX_SESSION_LOCKS];
852 +
static bool popup_used[MAX_POPUPS];
842 853
static bool pending_spawn_used[MAX_PENDING_SPAWNS], window_state_used[MAX_WINDOW_STATES];
843 854
static bool static_listener_used[MAX_STATIC_LISTENERS], workspace_manager_used[MAX_WS_MANAGERS];
844 855
static bool workspace_handle_used[MAX_WS_HANDLES], metadata_manager_used[MAX_WS_MANAGERS];
845 856
846 857
static pool_t client_pool = {
907 918
static pool_t metadata_manager_pool   = { metadata_manager_items,
908 919
                                          metadata_manager_used,
909 920
                                          LENGTH(metadata_manager_items),
910 921
                                          sizeof *metadata_manager_items,
911 922
                                          "metadata_manager" };
923 +
static pool_t popup_pool              = {
924 +
    popup_items, popup_used, LENGTH(popup_items), sizeof *popup_items, "popup"
925 +
};
912 926
913 927
/* Backend-specific client operations. */
914 928
/* Client helpers shared by the XDG shell and Xwayland implementations. These
915 929
 * are inline so unused helpers are omitted for builds that disable a backend. */
916 930
2321 2335
        c->resize_edges = WLR_EDGE_NONE;
2322 2336
}
2323 2337
2324 2338
/* Create and constrain a popup after its initial state is committed. */
2325 2339
void popup_commit(struct wl_listener *listener, void *data) {
2326 -
    struct wlr_surface   *surface = data;
2327 -
    struct wlr_xdg_popup *popup   = wlr_xdg_popup_try_from_wlr_surface(surface);
2328 -
    layer_surface_t      *l       = nullptr;
2329 -
    client_t             *c       = nullptr;
2340 +
    popup_t              *p     = wl_container_of(listener, p, commit);
2341 +
    struct wlr_xdg_popup *popup = p->popup;
2342 +
    layer_surface_t      *l     = nullptr;
2343 +
    client_t             *c     = nullptr;
2330 2344
    struct wlr_box        box;
2331 2345
    int                   type = -1;
2332 2346
2333 -
    if (!popup) {
2334 -
        wl_list_remove(&listener->link);
2335 -
        listener_release(listener);
2336 -
        return;
2337 -
    }
2338 2347
    if (!popup->base->initial_commit)
2339 2348
        return;
2340 2349
2341 2350
    type = toplevel_from_wlr_surface(popup->base->surface, &c, &l);
2342 2351
2343 2352
    if ((type == LAYER_SHELL && (!l || !l->mon)) || (type != LAYER_SHELL && (!c || !c->mon)) ||
2344 -
        !popup->parent || !popup->parent->data)
2345 -
        goto destroy_popup;
2353 +
        !popup->parent || !popup->parent->data) {
2354 +
        wlr_xdg_popup_destroy(popup);
2355 +
        return;
2356 +
    }
2346 2357
    popup->base->surface->data = wlr_scene_xdg_surface_create(popup->parent->data, popup->base);
2347 2358
2348 -
    if (!popup->base->surface->data)
2349 -
        goto destroy_popup;
2359 +
    if (!popup->base->surface->data) {
2360 +
        wlr_xdg_popup_destroy(popup);
2361 +
        return;
2362 +
    }
2350 2363
    box    = type == LAYER_SHELL ? l->mon->m : c->mon->w;
2351 2364
    box.x -= (type == LAYER_SHELL ? l->scene->node.x : c->geom.x);
2352 2365
    box.y -= (type == LAYER_SHELL ? l->scene->node.y : c->geom.y);
2353 2366
    wlr_xdg_popup_unconstrain_from_box(popup, &box);
2354 -
    wl_list_remove(&listener->link);
2355 -
    listener_release(listener);
2356 -
    return;
2367 +
}
2357 2368
2358 -
destroy_popup:
2359 -
    wl_list_remove(&listener->link);
2360 -
    listener_release(listener);
2361 -
    wlr_xdg_popup_destroy(popup);
2369 +
/* Unpublish a destroyed popup's scene tree and release its state. */
2370 +
void popup_destroy(struct wl_listener *listener, void *data) {
2371 +
    popup_t *p = wl_container_of(listener, p, destroy);
2372 +
2373 +
    /* The scene tree dies with the popup, but its wl_surface can outlive both. */
2374 +
    p->popup->base->surface->data = nullptr;
2375 +
    wl_list_remove(&p->commit.link);
2376 +
    wl_list_remove(&p->destroy.link);
2377 +
    pool_release(&popup_pool, p);
2362 2378
}
2363 2379
2364 2380
/* Attach an event listener from the fixed-capacity listener pool. */
2365 2381
void listen_static(struct wl_signal *signal, wl_notify_func_t notify) {
2366 2382
    static_listener_t *slot = pool_take(&static_listener_pool);
2758 2774
/* Wait for a new popup's initial commit before adding it to the scene. */
2759 2775
void create_popup(struct wl_listener *listener, void *data) {
2760 2776
    /* This event is raised when a client (either xdg-shell or layer-shell)
2761 2777
     * creates a new popup. */
2762 2778
    struct wlr_xdg_popup *popup = data;
2779 +
    popup_t              *p     = pool_take(&popup_pool);
2780 +
2781 +
    if (!p)
2782 +
        return;
2783 +
    p->popup = popup;
2763 2784
2764 -
    LISTEN_STATIC(&popup->base->surface->events.commit, popup_commit);
2785 +
    LISTEN(&popup->base->surface->events.commit, &p->commit, popup_commit);
2786 +
    LISTEN(&popup->events.destroy, &p->destroy, popup_destroy);
2765 2787
}
2766 2788
2767 2789
/* Activate the pointer constraint belonging to the focused surface. */
2768 2790
void cursor_constrain(struct wlr_pointer_constraint_v1 *constraint) {
2769 2791
    if (active_constraint == constraint)
2840 2862
2841 2863
    wl_list_remove(&l->link);
2842 2864
    wl_list_remove(&l->destroy.link);
2843 2865
    wl_list_remove(&l->unmap.link);
2844 2866
    wl_list_remove(&l->surface_commit.link);
2867 +
    /* A wl_surface outlives the role object built on it. Drop the scene tree
2868 +
     * published in its user data before destroying that tree. */
2869 +
    l->layer_surface->surface->data = nullptr;
2845 2870
    wlr_scene_node_destroy(&l->popups->node);
2846 2871
2847 2872
    if (l->dim)
2848 2873
        wlr_scene_node_destroy(&l->dim->node);
2849 2874
    pool_release(&layer_surface_pool, l);
2850 2875
}
2851 2876
2852 2877
/* Handle a session locker that exits without unlocking cleanly. */
2853 2878
void destroy_lock(session_lock_t *lock, int unlock) {
2879 +
    monitor_t *m;
2880 +
2854 2881
    wlr_seat_keyboard_notify_clear_focus(seat);
2855 2882
    input_method_set_focus(nullptr);
2856 2883
    locked = !unlock;
2857 2884
2858 2885
    if (unlock) {
2862 2889
    }
2863 2890
    wl_list_remove(&lock->new_surface.link);
2864 2891
    wl_list_remove(&lock->unlock.link);
2865 2892
    wl_list_remove(&lock->destroy.link);
2866 2893
2894 +
    /* Detach the lock surfaces still attached to a display, since the scene
2895 +
     * destroyed below owns their trees. Keyboard focus was cleared above, so
2896 +
     * this only unpublishes those trees and drops their destroy listeners. */
2897 +
    wl_list_for_each(m, &mons, link) {
2898 +
        if (m->lock_surface)
2899 +
            destroy_lock_surface(&m->destroy_lock_surface, nullptr);
2900 +
    }
2867 2901
    wlr_scene_node_destroy(&lock->scene->node);
2868 2902
    cur_lock = nullptr;
2869 2903
    pool_release(&session_lock_pool, lock);
2870 2904
}
2871 2905
2874 2908
    monitor_t                          *m = wl_container_of(listener, m, destroy_lock_surface);
2875 2909
    struct wlr_session_lock_surface_v1 *surface, *lock_surface = m->lock_surface;
2876 2910
2877 2911
    m->lock_surface = nullptr;
2878 2912
    wl_list_remove(&m->destroy_lock_surface.link);
2913 +
    /* The wl_surface can outlive the lock surface built on it, while its scene
2914 +
     * tree cannot. */
2915 +
    lock_surface->surface->data = nullptr;
2879 2916
2880 2917
    if (lock_surface->surface != seat->keyboard_state.focused_surface)
2881 2918
        return;
2882 2919
2883 2920
    if (locked && cur_lock && !wl_list_empty(&cur_lock->surfaces)) {