From a04fd9921276e435487436f1f535fbdc8b5b9c82 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Fri, 12 Aug 2022 10:55:01 -0500 Subject: [PATCH] xwm: Fix selection if no seat present at startup It's possible that there are no seats present at startup (especially with the RDP backend, which creates and removes seats for connections), and previously we'd just fail to set up XWayland cut and paste properly. We should set up a listener and find a seat when one becomes available - but we also need to switch seats if ours is removed. Signed-off-by: Derek Foreman Co-authored-by: Hideyuki Nagase --- xwayland/selection.c | 71 +++++++++++++++++++++++++++++++++++---- xwayland/window-manager.c | 2 ++ xwayland/xwayland.h | 2 ++ 3 files changed, 68 insertions(+), 7 deletions(-) diff --git a/xwayland/selection.c b/xwayland/selection.c index 0e4120ac6..463a738c9 100644 --- a/xwayland/selection.c +++ b/xwayland/selection.c @@ -716,13 +716,61 @@ weston_wm_set_selection(struct wl_listener *listener, void *data) XCB_TIME_CURRENT_TIME); } +static void +maybe_reassign_selection_seat(struct weston_wm *wm) +{ + struct weston_seat *seat; + + /* If we already have a seat, keep it */ + if (!wl_list_empty(&wm->selection_listener.link)) + return; + + seat = weston_wm_pick_seat(wm); + if (!seat) + return; + + wl_list_remove(&wm->selection_listener.link); + wl_list_remove(&wm->seat_destroy_listener.link); + + wl_signal_add(&seat->selection_signal, &wm->selection_listener); + wl_signal_add(&seat->destroy_signal, &wm->seat_destroy_listener); + + weston_wm_set_selection(&wm->selection_listener, seat); +} + +static void +weston_wm_seat_created(struct wl_listener *listener, void *data) +{ + struct weston_wm *wm = + container_of(listener, struct weston_wm, seat_create_listener); + + maybe_reassign_selection_seat(wm); +} + +static void +weston_wm_seat_destroyed(struct wl_listener *listener, void *data) +{ + struct weston_wm *wm = + container_of(listener, struct weston_wm, seat_destroy_listener); + + wl_list_remove(&wm->selection_listener.link); + wl_list_init(&wm->selection_listener.link); + + wl_list_remove(&wm->seat_destroy_listener.link); + wl_list_init(&wm->seat_destroy_listener.link); + + /* Try to pick another available seat to fall back to */ + maybe_reassign_selection_seat(wm); +} + void weston_wm_selection_init(struct weston_wm *wm) { - struct weston_seat *seat; uint32_t values[1], mask; wl_list_init(&wm->selection_listener.link); + wl_list_init(&wm->seat_create_listener.link); + wl_list_init(&wm->seat_destroy_listener.link); wm->selection_request.requestor = XCB_NONE; @@ -751,11 +799,20 @@ weston_wm_selection_init(struct weston_wm *wm) xcb_xfixes_select_selection_input(wm->conn, wm->selection_window, wm->atom.clipboard, mask); - seat = weston_wm_pick_seat(wm); - if (seat == NULL) - return; + /* Try to set up a selection listener for any existing seat - we + * have a clipboard manager that can copy a subset of available + * selections so they don't disappear when the client owning + * them quits, but to make this work we need to have a seat + * to hang the selection off. + * + * If we have no seat or lose our seat we need to make sure we + * eventually assign a new one, so we listen for seat creation + * and destruction. + */ wm->selection_listener.notify = weston_wm_set_selection; - wl_signal_add(&seat->selection_signal, &wm->selection_listener); - - weston_wm_set_selection(&wm->selection_listener, seat); + wm->seat_destroy_listener.notify = weston_wm_seat_destroyed; + wm->seat_create_listener.notify = weston_wm_seat_created; + wl_signal_add(&wm->server->compositor->seat_created_signal, + &wm->seat_create_listener); + maybe_reassign_selection_seat(wm); } diff --git a/xwayland/window-manager.c b/xwayland/window-manager.c index 9dc30e1c7..d90e84b7d 100644 --- a/xwayland/window-manager.c +++ b/xwayland/window-manager.c @@ -2728,6 +2728,8 @@ weston_wm_destroy(struct weston_wm *wm) theme_destroy(wm->theme); xcb_disconnect(wm->conn); wl_event_source_remove(wm->source); + wl_list_remove(&wm->seat_create_listener.link); + wl_list_remove(&wm->seat_destroy_listener.link); wl_list_remove(&wm->selection_listener.link); wl_list_remove(&wm->activate_listener.link); wl_list_remove(&wm->kill_listener.link); diff --git a/xwayland/xwayland.h b/xwayland/xwayland.h index df9cab83b..5b75ff8ec 100644 --- a/xwayland/xwayland.h +++ b/xwayland/xwayland.h @@ -88,6 +88,8 @@ struct weston_wm { int selection_property_set; int flush_property_on_delete; struct wl_listener selection_listener; + struct wl_listener seat_create_listener; + struct wl_listener seat_destroy_listener; xcb_window_t dnd_window; xcb_window_t dnd_owner;