diff --git a/include/types/wlr_xdg_shell.h b/include/types/wlr_xdg_shell.h index f3f12ec9..1b4c89c2 100644 --- a/include/types/wlr_xdg_shell.h +++ b/include/types/wlr_xdg_shell.h @@ -21,7 +21,6 @@ void create_xdg_popup(struct wlr_xdg_surface *surface, void reset_xdg_popup(struct wlr_xdg_popup *popup); void destroy_xdg_popup(struct wlr_xdg_popup *popup); void handle_xdg_popup_client_commit(struct wlr_xdg_popup *popup); -void handle_xdg_popup_committed(struct wlr_xdg_popup *popup); struct wlr_xdg_popup_configure *send_xdg_popup_configure( struct wlr_xdg_popup *popup); void handle_xdg_popup_ack_configure(struct wlr_xdg_popup *popup, @@ -32,7 +31,6 @@ void create_xdg_toplevel(struct wlr_xdg_surface *surface, void reset_xdg_toplevel(struct wlr_xdg_toplevel *toplevel); void destroy_xdg_toplevel(struct wlr_xdg_toplevel *toplevel); void handle_xdg_toplevel_client_commit(struct wlr_xdg_toplevel *toplevel); -void handle_xdg_toplevel_committed(struct wlr_xdg_toplevel *toplevel); struct wlr_xdg_toplevel_configure *send_xdg_toplevel_configure( struct wlr_xdg_toplevel *toplevel); void handle_xdg_toplevel_ack_configure(struct wlr_xdg_toplevel *toplevel, diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h index 5c490455..e5b75c57 100644 --- a/include/wlr/types/wlr_xdg_shell.h +++ b/include/wlr/types/wlr_xdg_shell.h @@ -95,7 +95,6 @@ struct wlr_xdg_popup { struct wl_list link; struct wl_resource *resource; - bool sent_initial_configure; struct wlr_surface *parent; struct wlr_seat *seat; diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index bd21dd91..0342ec54 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -86,6 +86,7 @@ struct tinywl_toplevel { struct wlr_scene_tree *scene_tree; struct wl_listener map; struct wl_listener unmap; + struct wl_listener commit; struct wl_listener destroy; struct wl_listener request_move; struct wl_listener request_resize; @@ -93,6 +94,12 @@ struct tinywl_toplevel { struct wl_listener request_fullscreen; }; +struct tinywl_popup { + struct wlr_xdg_popup *xdg_popup; + struct wl_listener commit; + struct wl_listener destroy; +}; + struct tinywl_keyboard { struct wl_list link; struct tinywl_server *server; @@ -672,12 +679,26 @@ static void xdg_toplevel_unmap(struct wl_listener *listener, void *data) { wl_list_remove(&toplevel->link); } +static void xdg_toplevel_commit(struct wl_listener *listener, void *data) { + /* Called when a new surface state is committed. */ + struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, commit); + + if (toplevel->xdg_toplevel->base->initial_commit) { + /* When an xdg_surface performs an initial commit, the compositor must + * reply with a configure so the client can map the surface. tinywl + * configures the xdg_toplevel with 0,0 size to let the client pick the + * dimensions itself. */ + wlr_xdg_toplevel_set_size(toplevel->xdg_toplevel, 0, 0); + } +} + static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) { /* Called when the xdg_toplevel is destroyed. */ struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, destroy); wl_list_remove(&toplevel->map.link); wl_list_remove(&toplevel->unmap.link); + wl_list_remove(&toplevel->commit.link); wl_list_remove(&toplevel->destroy.link); wl_list_remove(&toplevel->request_move.link); wl_list_remove(&toplevel->request_resize.link); @@ -793,6 +814,8 @@ static void server_new_xdg_toplevel(struct wl_listener *listener, void *data) { wl_signal_add(&xdg_toplevel->base->surface->events.map, &toplevel->map); toplevel->unmap.notify = xdg_toplevel_unmap; wl_signal_add(&xdg_toplevel->base->surface->events.unmap, &toplevel->unmap); + toplevel->commit.notify = xdg_toplevel_commit; + wl_signal_add(&xdg_toplevel->base->surface->events.commit, &toplevel->commit); toplevel->destroy.notify = xdg_toplevel_destroy; wl_signal_add(&xdg_toplevel->events.destroy, &toplevel->destroy); @@ -808,10 +831,37 @@ static void server_new_xdg_toplevel(struct wl_listener *listener, void *data) { wl_signal_add(&xdg_toplevel->events.request_fullscreen, &toplevel->request_fullscreen); } +static void xdg_popup_commit(struct wl_listener *listener, void *data) { + /* Called when a new surface state is committed. */ + struct tinywl_popup *popup = wl_container_of(listener, popup, commit); + + if (popup->xdg_popup->base->initial_commit) { + /* When an xdg_surface performs an initial commit, the compositor must + * reply with a configure so the client can map the surface. + * tinywl sends an empty configure. A more sophisticated compositor + * might change an xdg_popup's geometry to ensure it's not positioned + * off-screen, for example. */ + wlr_xdg_surface_schedule_configure(popup->xdg_popup->base); + } +} + +static void xdg_popup_destroy(struct wl_listener *listener, void *data) { + /* Called when the xdg_popup is destroyed. */ + struct tinywl_popup *popup = wl_container_of(listener, popup, destroy); + + wl_list_remove(&popup->commit.link); + wl_list_remove(&popup->destroy.link); + + free(popup); +} + static void server_new_xdg_popup(struct wl_listener *listener, void *data) { /* This event is raised when a client creates a new popup. */ struct wlr_xdg_popup *xdg_popup = data; + struct tinywl_popup *popup = calloc(1, sizeof(*popup)); + popup->xdg_popup = xdg_popup; + /* We must add xdg popups to the scene graph so they get rendered. The * wlroots scene graph provides a helper for this, but to use it we must * provide the proper parent scene node of the xdg popup. To enable this, @@ -821,6 +871,12 @@ static void server_new_xdg_popup(struct wl_listener *listener, void *data) { assert(parent != NULL); struct wlr_scene_tree *parent_tree = parent->data; xdg_popup->base->data = wlr_scene_xdg_surface_create(parent_tree, xdg_popup->base); + + popup->commit.notify = xdg_popup_commit; + wl_signal_add(&xdg_popup->base->surface->events.commit, &popup->commit); + + popup->destroy.notify = xdg_popup_destroy; + wl_signal_add(&xdg_popup->events.destroy, &popup->destroy); } int main(int argc, char *argv[]) { diff --git a/types/xdg_shell/wlr_xdg_popup.c b/types/xdg_shell/wlr_xdg_popup.c index b6134f49..97e6c8e2 100644 --- a/types/xdg_shell/wlr_xdg_popup.c +++ b/types/xdg_shell/wlr_xdg_popup.c @@ -246,13 +246,6 @@ void handle_xdg_popup_client_commit(struct wlr_xdg_popup *popup) { } } -void handle_xdg_popup_committed(struct wlr_xdg_popup *popup) { - if (popup->base->initial_commit && !popup->sent_initial_configure) { - wlr_xdg_surface_schedule_configure(popup->base); - popup->sent_initial_configure = true; - } -} - static const struct xdg_popup_interface xdg_popup_implementation; struct wlr_xdg_popup *wlr_xdg_popup_from_resource( @@ -285,7 +278,7 @@ static void xdg_popup_handle_grab(struct wl_client *client, wlr_xdg_popup_destroy(popup); return; } - if (popup->sent_initial_configure) { + if (popup->base->surface->mapped) { wl_resource_post_error(popup->resource, XDG_POPUP_ERROR_INVALID_GRAB, "xdg_popup is already mapped"); @@ -472,8 +465,6 @@ void reset_xdg_popup(struct wlr_xdg_popup *popup) { popup->seat = NULL; } - - popup->sent_initial_configure = false; } void destroy_xdg_popup(struct wlr_xdg_popup *popup) { diff --git a/types/xdg_shell/wlr_xdg_surface.c b/types/xdg_shell/wlr_xdg_surface.c index 0d7a461c..029ab81a 100644 --- a/types/xdg_shell/wlr_xdg_surface.c +++ b/types/xdg_shell/wlr_xdg_surface.c @@ -309,16 +309,12 @@ static void xdg_surface_role_commit(struct wlr_surface *wlr_surface) { assert(0 && "not reached"); return; case WLR_XDG_SURFACE_ROLE_TOPLEVEL: - if (surface->toplevel != NULL) { - handle_xdg_toplevel_committed(surface->toplevel); - } else { + if (surface->toplevel == NULL) { return; } break; case WLR_XDG_SURFACE_ROLE_POPUP: - if (surface->popup != NULL) { - handle_xdg_popup_committed(surface->popup); - } else { + if (surface->popup == NULL) { return; } break; diff --git a/types/xdg_shell/wlr_xdg_toplevel.c b/types/xdg_shell/wlr_xdg_toplevel.c index aac04ea4..7f67aeaf 100644 --- a/types/xdg_shell/wlr_xdg_toplevel.c +++ b/types/xdg_shell/wlr_xdg_toplevel.c @@ -131,24 +131,6 @@ void handle_xdg_toplevel_client_commit(struct wlr_xdg_toplevel *toplevel) { } } -void handle_xdg_toplevel_committed(struct wlr_xdg_toplevel *toplevel) { - if (toplevel->base->initial_commit) { - // On the initial commit, send a configure request to tell the client it - // is added - wlr_xdg_surface_schedule_configure(toplevel->base); - - if (toplevel->base->client->shell->version >= - XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) { - // The first configure event must carry WM capabilities - wlr_xdg_toplevel_set_wm_capabilities(toplevel, - WLR_XDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU | - WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE | - WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN | - WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE); - } - } -} - static const struct xdg_toplevel_interface xdg_toplevel_implementation; struct wlr_xdg_toplevel *wlr_xdg_toplevel_from_resource( @@ -506,6 +488,16 @@ void create_xdg_toplevel(struct wlr_xdg_surface *surface, set_xdg_surface_role_object(surface, surface->toplevel->resource); + if (surface->client->shell->version >= XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) { + // The first configure event must carry WM capabilities + surface->toplevel->scheduled.wm_capabilities = + WLR_XDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU | + WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE | + WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN | + WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE; + surface->toplevel->scheduled.fields |= WLR_XDG_TOPLEVEL_CONFIGURE_WM_CAPABILITIES; + } + wl_signal_emit_mutable(&surface->client->shell->events.new_toplevel, surface->toplevel); return;