diff --git a/include/foreign-toplevel-internal.h b/include/foreign-toplevel-internal.h new file mode 100644 index 00000000..7179117f --- /dev/null +++ b/include/foreign-toplevel-internal.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_FOREIGN_TOPLEVEL_INTERNAL_H +#define LABWC_FOREIGN_TOPLEVEL_INTERNAL_H + +#include +#include +#include "foreign-toplevel.h" + +struct foreign_toplevel { + struct view *view; + + /* *-toplevel implementations */ + struct wlr_foreign_toplevel { + struct wlr_foreign_toplevel_handle_v1 *handle; + + /* Client side events */ + struct { + struct wl_listener request_maximize; + struct wl_listener request_minimize; + struct wl_listener request_fullscreen; + struct wl_listener request_activate; + struct wl_listener request_close; + struct wl_listener handle_destroy; + } on; + + /* Compositor side state updates */ + struct { + struct wl_listener new_app_id; + struct wl_listener new_title; + struct wl_listener new_outputs; + struct wl_listener maximized; + struct wl_listener minimized; + struct wl_listener fullscreened; + struct wl_listener activated; + } on_view; + + /* Internal signals */ + struct { + struct wl_listener toplevel_parent; + struct wl_listener toplevel_destroy; + } on_foreign_toplevel; + + } wlr_toplevel; + + /* TODO: add struct xdg_x11_mapped_toplevel at some point */ + + struct { + struct wl_signal toplevel_parent; /* struct view *parent */ + struct wl_signal toplevel_destroy; + } events; +}; + +void wlr_foreign_toplevel_init(struct foreign_toplevel *toplevel); + +void foreign_request_minimize(struct foreign_toplevel *toplevel, bool minimized); +void foreign_request_maximize(struct foreign_toplevel *toplevel, enum view_axis axis); +void foreign_request_fullscreen(struct foreign_toplevel *toplevel, bool fullscreen); +void foreign_request_activate(struct foreign_toplevel *toplevel); +void foreign_request_close(struct foreign_toplevel *toplevel); + +#endif /* LABWC_FOREIGN_TOPLEVEL_INTERNAL_H */ diff --git a/include/foreign-toplevel.h b/include/foreign-toplevel.h new file mode 100644 index 00000000..bc226776 --- /dev/null +++ b/include/foreign-toplevel.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_FOREIGN_TOPLEVEL_H +#define LABWC_FOREIGN_TOPLEVEL_H + +struct view; +struct foreign_toplevel; + +struct foreign_toplevel *foreign_toplevel_create(struct view *view); +void foreign_toplevel_set_parent(struct foreign_toplevel *toplevel, + struct foreign_toplevel *parent); +void foreign_toplevel_destroy(struct foreign_toplevel *toplevel); + +#endif /* LABWC_FOREIGN_TOPLEVEL_H */ diff --git a/include/labwc.h b/include/labwc.h index 29f72c39..30e04f69 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -419,9 +418,6 @@ struct constraint { void xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup); void xdg_shell_init(struct server *server); -void foreign_toplevel_handle_create(struct view *view); -void foreign_toplevel_update_outputs(struct view *view); - /* * desktop.c routines deal with a collection of views * diff --git a/include/view.h b/include/view.h index a1c00fef..bffdea07 100644 --- a/include/view.h +++ b/include/view.h @@ -111,6 +111,7 @@ enum window_type { struct view; struct wlr_surface; +struct foreign_toplevel; /* Common to struct view and struct xwayland_unmanaged */ struct mappable { @@ -265,16 +266,6 @@ struct view { struct multi_rect *rect; } resize_outlines; - struct foreign_toplevel { - struct wlr_foreign_toplevel_handle_v1 *handle; - struct wl_listener maximize; - struct wl_listener minimize; - struct wl_listener fullscreen; - struct wl_listener activate; - struct wl_listener close; - struct wl_listener destroy; - } toplevel; - struct mappable mappable; struct wl_listener destroy; @@ -287,6 +278,8 @@ struct view { struct wl_listener request_fullscreen; struct wl_listener set_title; + struct foreign_toplevel *foreign_toplevel; + struct { struct wl_signal new_app_id; struct wl_signal new_title; diff --git a/src/foreign-toplevel/foreign.c b/src/foreign-toplevel/foreign.c new file mode 100644 index 00000000..d1e263ab --- /dev/null +++ b/src/foreign-toplevel/foreign.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include "common/macros.h" +#include "common/mem.h" +#include "labwc.h" +#include "view.h" +#include "foreign-toplevel-internal.h" + +/* Internal API */ +void +foreign_request_minimize(struct foreign_toplevel *toplevel, bool minimized) +{ + view_minimize(toplevel->view, minimized); +} + +void +foreign_request_maximize(struct foreign_toplevel *toplevel, enum view_axis axis) +{ + view_maximize(toplevel->view, axis, /*store_natural_geometry*/ true); +} + +void +foreign_request_fullscreen(struct foreign_toplevel *toplevel, bool fullscreen) +{ + view_set_fullscreen(toplevel->view, fullscreen); +} + +void +foreign_request_activate(struct foreign_toplevel *toplevel) +{ + if (toplevel->view->server->osd_state.cycle_view) { + wlr_log(WLR_INFO, "Preventing focus request while in window switcher"); + return; + } + + desktop_focus_view(toplevel->view, /*raise*/ true); +} + +void +foreign_request_close(struct foreign_toplevel *toplevel) +{ + view_close(toplevel->view); +} + +/* Public API */ +struct foreign_toplevel * +foreign_toplevel_create(struct view *view) +{ + assert(view); + assert(view->mapped); + + struct foreign_toplevel *toplevel = znew(*toplevel); + toplevel->view = view; + + wl_signal_init(&toplevel->events.toplevel_parent); + wl_signal_init(&toplevel->events.toplevel_destroy); + + wlr_foreign_toplevel_init(toplevel); + + return toplevel; +} + +void +foreign_toplevel_set_parent(struct foreign_toplevel *toplevel, struct foreign_toplevel *parent) +{ + assert(toplevel); + wl_signal_emit_mutable(&toplevel->events.toplevel_parent, parent); +} + +void +foreign_toplevel_destroy(struct foreign_toplevel *toplevel) +{ + assert(toplevel); + wl_signal_emit_mutable(&toplevel->events.toplevel_destroy, NULL); + assert(!toplevel->wlr_toplevel.handle); + free(toplevel); +} diff --git a/src/foreign-toplevel/meson.build b/src/foreign-toplevel/meson.build new file mode 100644 index 00000000..3a617f00 --- /dev/null +++ b/src/foreign-toplevel/meson.build @@ -0,0 +1,4 @@ +labwc_sources += files( + 'foreign.c', + 'wlr-foreign.c', +) diff --git a/src/foreign-toplevel/wlr-foreign.c b/src/foreign-toplevel/wlr-foreign.c new file mode 100644 index 00000000..21a8d61c --- /dev/null +++ b/src/foreign-toplevel/wlr-foreign.c @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include "common/macros.h" +#include "labwc.h" +#include "view.h" +#include "foreign-toplevel-internal.h" + +/* wlr signals */ +static void +handle_request_minimize(struct wl_listener *listener, void *data) +{ + struct foreign_toplevel *toplevel = wl_container_of( + listener, toplevel, wlr_toplevel.on.request_minimize); + struct wlr_foreign_toplevel_handle_v1_minimized_event *event = data; + + foreign_request_minimize(toplevel, event->minimized); +} + +static void +handle_request_maximize(struct wl_listener *listener, void *data) +{ + struct foreign_toplevel *toplevel = wl_container_of( + listener, toplevel, wlr_toplevel.on.request_maximize); + struct wlr_foreign_toplevel_handle_v1_maximized_event *event = data; + + foreign_request_maximize(toplevel, + event->maximized ? VIEW_AXIS_BOTH : VIEW_AXIS_NONE); +} + +static void +handle_request_fullscreen(struct wl_listener *listener, void *data) +{ + struct foreign_toplevel *toplevel = wl_container_of( + listener, toplevel, wlr_toplevel.on.request_fullscreen); + struct wlr_foreign_toplevel_handle_v1_fullscreen_event *event = data; + + /* TODO: This ignores event->output */ + foreign_request_fullscreen(toplevel, event->fullscreen); +} + +static void +handle_request_activate(struct wl_listener *listener, void *data) +{ + struct foreign_toplevel *toplevel = wl_container_of( + listener, toplevel, wlr_toplevel.on.request_activate); + + /* In a multi-seat world we would select seat based on event->seat here. */ + foreign_request_activate(toplevel); +} + +static void +handle_request_close(struct wl_listener *listener, void *data) +{ + struct foreign_toplevel *toplevel = wl_container_of( + listener, toplevel, wlr_toplevel.on.request_close); + + foreign_request_close(toplevel); +} + +static void +handle_handle_destroy(struct wl_listener *listener, void *data) +{ + struct wlr_foreign_toplevel *wlr_toplevel = + wl_container_of(listener, wlr_toplevel, on.handle_destroy); + + /* Client side requests */ + wl_list_remove(&wlr_toplevel->on.request_maximize.link); + wl_list_remove(&wlr_toplevel->on.request_minimize.link); + wl_list_remove(&wlr_toplevel->on.request_fullscreen.link); + wl_list_remove(&wlr_toplevel->on.request_activate.link); + wl_list_remove(&wlr_toplevel->on.request_close.link); + wl_list_remove(&wlr_toplevel->on.handle_destroy.link); + wlr_toplevel->handle = NULL; +} + +/* Compositor signals */ +static void +handle_new_app_id(struct wl_listener *listener, void *data) +{ + struct foreign_toplevel *toplevel = + wl_container_of(listener, toplevel, wlr_toplevel.on_view.new_app_id); + assert(toplevel->wlr_toplevel.handle); + + const char *app_id = view_get_string_prop(toplevel->view, "app_id"); + wlr_foreign_toplevel_handle_v1_set_app_id(toplevel->wlr_toplevel.handle, app_id); +} + +static void +handle_new_title(struct wl_listener *listener, void *data) +{ + struct foreign_toplevel *toplevel = + wl_container_of(listener, toplevel, wlr_toplevel.on_view.new_title); + assert(toplevel->wlr_toplevel.handle); + + const char *title = view_get_string_prop(toplevel->view, "title"); + wlr_foreign_toplevel_handle_v1_set_title(toplevel->wlr_toplevel.handle, title); +} + +static void +handle_new_outputs(struct wl_listener *listener, void *data) +{ + struct foreign_toplevel *toplevel = + wl_container_of(listener, toplevel, wlr_toplevel.on_view.new_outputs); + assert(toplevel->wlr_toplevel.handle); + + /* + * Loop over all outputs and notify foreign_toplevel clients about changes. + * wlr_foreign_toplevel_handle_v1_output_xxx() keeps track of the active + * outputs internally and merges the events. It also listens to output + * destroy events so its fine to just relay the current state and let + * wlr_foreign_toplevel handle the rest. + */ + struct output *output; + wl_list_for_each(output, &toplevel->view->server->outputs, link) { + if (view_on_output(toplevel->view, output)) { + wlr_foreign_toplevel_handle_v1_output_enter( + toplevel->wlr_toplevel.handle, output->wlr_output); + } else { + wlr_foreign_toplevel_handle_v1_output_leave( + toplevel->wlr_toplevel.handle, output->wlr_output); + } + } +} + +static void +handle_maximized(struct wl_listener *listener, void *data) +{ + struct foreign_toplevel *toplevel = + wl_container_of(listener, toplevel, wlr_toplevel.on_view.maximized); + assert(toplevel->wlr_toplevel.handle); + + wlr_foreign_toplevel_handle_v1_set_maximized( + toplevel->wlr_toplevel.handle, + toplevel->view->maximized == VIEW_AXIS_BOTH); +} + +static void +handle_minimized(struct wl_listener *listener, void *data) +{ + struct foreign_toplevel *toplevel = + wl_container_of(listener, toplevel, wlr_toplevel.on_view.minimized); + assert(toplevel->wlr_toplevel.handle); + + wlr_foreign_toplevel_handle_v1_set_minimized( + toplevel->wlr_toplevel.handle, toplevel->view->minimized); +} + +static void +handle_fullscreened(struct wl_listener *listener, void *data) +{ + struct foreign_toplevel *toplevel = + wl_container_of(listener, toplevel, wlr_toplevel.on_view.fullscreened); + assert(toplevel->wlr_toplevel.handle); + + wlr_foreign_toplevel_handle_v1_set_fullscreen( + toplevel->wlr_toplevel.handle, toplevel->view->fullscreen); +} + +static void +handle_activated(struct wl_listener *listener, void *data) +{ + struct foreign_toplevel *toplevel = + wl_container_of(listener, toplevel, wlr_toplevel.on_view.activated); + assert(toplevel->wlr_toplevel.handle); + + bool *activated = data; + wlr_foreign_toplevel_handle_v1_set_activated( + toplevel->wlr_toplevel.handle, *activated); +} + +/* Internal signals */ +static void +handle_toplevel_parent(struct wl_listener *listener, void *data) +{ + struct wlr_foreign_toplevel *wlr_toplevel = wl_container_of( + listener, wlr_toplevel, on_foreign_toplevel.toplevel_parent); + struct foreign_toplevel *parent = data; + + assert(wlr_toplevel->handle); + + /* The wlroots wlr-foreign-toplevel impl ensures parent is reset to NULL on destroy */ + wlr_foreign_toplevel_handle_v1_set_parent(wlr_toplevel->handle, parent + ? parent->wlr_toplevel.handle + : NULL); +} + +static void +handle_toplevel_destroy(struct wl_listener *listener, void *data) +{ + struct wlr_foreign_toplevel *wlr_toplevel = wl_container_of( + listener, wlr_toplevel, on_foreign_toplevel.toplevel_destroy); + + assert(wlr_toplevel->handle); + wlr_foreign_toplevel_handle_v1_destroy(wlr_toplevel->handle); + + /* Compositor side state changes */ + wl_list_remove(&wlr_toplevel->on_view.new_app_id.link); + wl_list_remove(&wlr_toplevel->on_view.new_title.link); + wl_list_remove(&wlr_toplevel->on_view.new_outputs.link); + wl_list_remove(&wlr_toplevel->on_view.maximized.link); + wl_list_remove(&wlr_toplevel->on_view.minimized.link); + wl_list_remove(&wlr_toplevel->on_view.fullscreened.link); + wl_list_remove(&wlr_toplevel->on_view.activated.link); + + /* Internal signals */ + wl_list_remove(&wlr_toplevel->on_foreign_toplevel.toplevel_parent.link); + wl_list_remove(&wlr_toplevel->on_foreign_toplevel.toplevel_destroy.link); +} + +/* Internal API */ +void +wlr_foreign_toplevel_init(struct foreign_toplevel *toplevel) +{ + struct wlr_foreign_toplevel *wlr_toplevel = &toplevel->wlr_toplevel; + struct view *view = toplevel->view; + + assert(view->server->foreign_toplevel_manager); + + wlr_toplevel->handle = wlr_foreign_toplevel_handle_v1_create( + view->server->foreign_toplevel_manager); + if (!wlr_toplevel->handle) { + wlr_log(WLR_ERROR, "cannot create wlr foreign toplevel handle for (%s)", + view_get_string_prop(view, "title")); + return; + } + + /* Client side requests */ + CONNECT_SIGNAL(wlr_toplevel->handle, &wlr_toplevel->on, request_maximize); + CONNECT_SIGNAL(wlr_toplevel->handle, &wlr_toplevel->on, request_minimize); + CONNECT_SIGNAL(wlr_toplevel->handle, &wlr_toplevel->on, request_fullscreen); + CONNECT_SIGNAL(wlr_toplevel->handle, &wlr_toplevel->on, request_activate); + CONNECT_SIGNAL(wlr_toplevel->handle, &wlr_toplevel->on, request_close); + wlr_toplevel->on.handle_destroy.notify = handle_handle_destroy; + wl_signal_add(&wlr_toplevel->handle->events.destroy, &wlr_toplevel->on.handle_destroy); + + /* Compositor side state changes */ + CONNECT_SIGNAL(view, &wlr_toplevel->on_view, new_app_id); + CONNECT_SIGNAL(view, &wlr_toplevel->on_view, new_title); + CONNECT_SIGNAL(view, &wlr_toplevel->on_view, new_outputs); + CONNECT_SIGNAL(view, &wlr_toplevel->on_view, maximized); + CONNECT_SIGNAL(view, &wlr_toplevel->on_view, minimized); + CONNECT_SIGNAL(view, &wlr_toplevel->on_view, fullscreened); + CONNECT_SIGNAL(view, &wlr_toplevel->on_view, activated); + + /* Internal signals */ + CONNECT_SIGNAL(toplevel, &wlr_toplevel->on_foreign_toplevel, toplevel_parent); + CONNECT_SIGNAL(toplevel, &wlr_toplevel->on_foreign_toplevel, toplevel_destroy); +} diff --git a/src/foreign.c b/src/foreign.c deleted file mode 100644 index 982e7840..00000000 --- a/src/foreign.c +++ /dev/null @@ -1,127 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#include -#include "labwc.h" -#include "view.h" -#include "workspaces.h" - -static void -handle_request_minimize(struct wl_listener *listener, void *data) -{ - struct view *view = wl_container_of(listener, view, toplevel.minimize); - struct wlr_foreign_toplevel_handle_v1_minimized_event *event = data; - view_minimize(view, event->minimized); -} - -static void -handle_request_maximize(struct wl_listener *listener, void *data) -{ - struct view *view = wl_container_of(listener, view, toplevel.maximize); - struct wlr_foreign_toplevel_handle_v1_maximized_event *event = data; - view_maximize(view, event->maximized ? VIEW_AXIS_BOTH : VIEW_AXIS_NONE, - /*store_natural_geometry*/ true); -} - -static void -handle_request_fullscreen(struct wl_listener *listener, void *data) -{ - struct view *view = wl_container_of(listener, view, toplevel.fullscreen); - struct wlr_foreign_toplevel_handle_v1_fullscreen_event *event = data; - view_set_fullscreen(view, event->fullscreen); -} - -static void -handle_request_activate(struct wl_listener *listener, void *data) -{ - struct view *view = wl_container_of(listener, view, toplevel.activate); - // struct wlr_foreign_toplevel_handle_v1_activated_event *event = data; - - if (view->server->osd_state.cycle_view) { - wlr_log(WLR_INFO, "Preventing focus request while in window switcher"); - return; - } - - /* In a multi-seat world we would select seat based on event->seat here. */ - desktop_focus_view(view, /*raise*/ true); -} - -static void -handle_request_close(struct wl_listener *listener, void *data) -{ - struct view *view = wl_container_of(listener, view, toplevel.close); - view_close(view); -} - -static void -handle_destroy(struct wl_listener *listener, void *data) -{ - struct view *view = wl_container_of(listener, view, toplevel.destroy); - struct foreign_toplevel *toplevel = &view->toplevel; - wl_list_remove(&toplevel->maximize.link); - wl_list_remove(&toplevel->minimize.link); - wl_list_remove(&toplevel->fullscreen.link); - wl_list_remove(&toplevel->activate.link); - wl_list_remove(&toplevel->close.link); - wl_list_remove(&toplevel->destroy.link); - toplevel->handle = NULL; -} - -void -foreign_toplevel_handle_create(struct view *view) -{ - struct foreign_toplevel *toplevel = &view->toplevel; - - toplevel->handle = wlr_foreign_toplevel_handle_v1_create( - view->server->foreign_toplevel_manager); - if (!toplevel->handle) { - wlr_log(WLR_ERROR, "cannot create foreign toplevel handle for (%s)", - view_get_string_prop(view, "title")); - return; - } - - toplevel->maximize.notify = handle_request_maximize; - wl_signal_add(&toplevel->handle->events.request_maximize, - &toplevel->maximize); - - toplevel->minimize.notify = handle_request_minimize; - wl_signal_add(&toplevel->handle->events.request_minimize, - &toplevel->minimize); - - toplevel->fullscreen.notify = handle_request_fullscreen; - wl_signal_add(&toplevel->handle->events.request_fullscreen, - &toplevel->fullscreen); - - toplevel->activate.notify = handle_request_activate; - wl_signal_add(&toplevel->handle->events.request_activate, - &toplevel->activate); - - toplevel->close.notify = handle_request_close; - wl_signal_add(&toplevel->handle->events.request_close, - &toplevel->close); - - toplevel->destroy.notify = handle_destroy; - wl_signal_add(&toplevel->handle->events.destroy, &toplevel->destroy); -} - -/* - * Loop over all outputs and notify foreign_toplevel clients about changes. - * wlr_foreign_toplevel_handle_v1_output_xxx() keeps track of the active - * outputs internally and merges the events. It also listens to output - * destroy events so its fine to just relay the current state and let - * wlr_foreign_toplevel handle the rest. - */ -void -foreign_toplevel_update_outputs(struct view *view) -{ - assert(view->toplevel.handle); - - struct output *output; - wl_list_for_each(output, &view->server->outputs, link) { - if (view_on_output(view, output)) { - wlr_foreign_toplevel_handle_v1_output_enter( - view->toplevel.handle, output->wlr_output); - } else { - wlr_foreign_toplevel_handle_v1_output_leave( - view->toplevel.handle, output->wlr_output); - } - } -} diff --git a/src/menu/menu.c b/src/menu/menu.c index 7d08aa41..bbd89964 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -1001,7 +1001,7 @@ update_client_list_combined_menu(struct server *server) wl_list_for_each(view, &server->views, link) { if (view->workspace == workspace) { const char *title = view_get_string_prop(view, "title"); - if (!view->toplevel.handle || string_null_or_empty(title)) { + if (!view->foreign_toplevel || string_null_or_empty(title)) { continue; } diff --git a/src/meson.build b/src/meson.build index c6ec720e..497bc33f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -5,7 +5,6 @@ labwc_sources = files( 'desktop.c', 'dnd.c', 'edges.c', - 'foreign.c', 'idle.c', 'interactive.c', 'layers.c', @@ -54,6 +53,7 @@ subdir('img') subdir('common') subdir('config') subdir('decorations') +subdir('foreign-toplevel') subdir('input') subdir('menu') subdir('ssd') diff --git a/src/server.c b/src/server.c index b731fb4a..65c9b09b 100644 --- a/src/server.c +++ b/src/server.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -18,6 +19,7 @@ #include #include #include + #if HAVE_XWAYLAND #include #include "xwayland-shell-v1-protocol.h" diff --git a/src/view-impl-common.c b/src/view-impl-common.c index 2440ba36..24d47c73 100644 --- a/src/view-impl-common.c +++ b/src/view-impl-common.c @@ -3,6 +3,7 @@ #include #include #include "common/list.h" +#include "foreign-toplevel.h" #include "labwc.h" #include "view.h" #include "view-impl-common.h" @@ -41,8 +42,9 @@ view_impl_map(struct view *view) */ enum property ret = window_rules_get_property(view, "skipTaskbar"); if (ret == LAB_PROP_TRUE) { - if (view->toplevel.handle) { - wlr_foreign_toplevel_handle_v1_destroy(view->toplevel.handle); + if (view->foreign_toplevel) { + foreign_toplevel_destroy(view->foreign_toplevel); + view->foreign_toplevel = NULL; } } diff --git a/src/view.c b/src/view.c index 84c34c09..51b335a7 100644 --- a/src/view.c +++ b/src/view.c @@ -10,6 +10,7 @@ #include "common/mem.h" #include "common/parse-bool.h" #include "common/scene-helpers.h" +#include "foreign-toplevel.h" #include "input/keyboard.h" #include "labwc.h" #include "menu/menu.h" @@ -469,10 +470,6 @@ view_set_activated(struct view *view, bool activated) if (view->impl->set_activated) { view->impl->set_activated(view, activated); } - if (view->toplevel.handle) { - wlr_foreign_toplevel_handle_v1_set_activated( - view->toplevel.handle, activated); - } wl_signal_emit_mutable(&view->events.activated, &activated); @@ -529,9 +526,6 @@ view_update_outputs(struct view *view) if (new_outputs != view->outputs) { view->outputs = new_outputs; - if (view->toplevel.handle) { - foreign_toplevel_update_outputs(view); - } wl_signal_emit_mutable(&view->events.new_outputs, NULL); desktop_update_top_layer_visiblity(view->server); } @@ -738,10 +732,7 @@ _minimize(struct view *view, bool minimized) if (view->minimized == minimized) { return; } - if (view->toplevel.handle) { - wlr_foreign_toplevel_handle_v1_set_minimized( - view->toplevel.handle, minimized); - } + if (view->impl->minimize) { view->impl->minimize(view, minimized); } @@ -1312,10 +1303,6 @@ set_maximized(struct view *view, enum view_axis maximized) if (view->impl->maximize) { view->impl->maximize(view, (maximized == VIEW_AXIS_BOTH)); } - if (view->toplevel.handle) { - wlr_foreign_toplevel_handle_v1_set_maximized( - view->toplevel.handle, (maximized == VIEW_AXIS_BOTH)); - } view->maximized = maximized; wl_signal_emit_mutable(&view->events.maximized, NULL); @@ -1676,10 +1663,6 @@ set_fullscreen(struct view *view, bool fullscreen) if (view->impl->set_fullscreen) { view->impl->set_fullscreen(view, fullscreen); } - if (view->toplevel.handle) { - wlr_foreign_toplevel_handle_v1_set_fullscreen( - view->toplevel.handle, fullscreen); - } view->fullscreen = fullscreen; wl_signal_emit_mutable(&view->events.fullscreened, NULL); @@ -2337,11 +2320,11 @@ view_update_title(struct view *view) { assert(view); const char *title = view_get_string_prop(view, "title"); - if (!view->toplevel.handle || !title) { + if (!title) { return; } ssd_update_title(view->ssd); - wlr_foreign_toplevel_handle_v1_set_title(view->toplevel.handle, title); + wl_signal_emit_mutable(&view->events.new_title, NULL); } @@ -2350,11 +2333,9 @@ view_update_app_id(struct view *view) { assert(view); const char *app_id = view_get_string_prop(view, "app_id"); - if (!view->toplevel.handle || !app_id) { + if (!app_id) { return; } - wlr_foreign_toplevel_handle_v1_set_app_id( - view->toplevel.handle, app_id); if (view->ssd_enabled) { ssd_update_window_icon(view->ssd); @@ -2512,8 +2493,9 @@ view_destroy(struct view *view) wl_list_remove(&view->set_title.link); wl_list_remove(&view->destroy.link); - if (view->toplevel.handle) { - wlr_foreign_toplevel_handle_v1_destroy(view->toplevel.handle); + if (view->foreign_toplevel) { + foreign_toplevel_destroy(view->foreign_toplevel); + view->foreign_toplevel = NULL; } if (server->grabbed_view == view) { diff --git a/src/xdg.c b/src/xdg.c index a09d1ae4..daa5eba2 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -6,6 +6,7 @@ #include "common/macros.h" #include "common/mem.h" #include "decorations.h" +#include "foreign-toplevel.h" #include "labwc.h" #include "menu/menu.h" #include "node.h" @@ -670,17 +671,19 @@ xdg_toplevel_view_get_string_prop(struct view *view, const char *prop) static void init_foreign_toplevel(struct view *view) { - foreign_toplevel_handle_create(view); + assert(!view->foreign_toplevel); + view->foreign_toplevel = foreign_toplevel_create(view); + struct wlr_xdg_toplevel *toplevel = xdg_toplevel_from_view(view); if (!toplevel->parent) { return; } struct wlr_xdg_surface *surface = toplevel->parent->base; struct view *parent = surface->data; - if (!parent->toplevel.handle) { + if (!parent->foreign_toplevel) { return; } - wlr_foreign_toplevel_handle_v1_set_parent(view->toplevel.handle, parent->toplevel.handle); + foreign_toplevel_set_parent(view->foreign_toplevel, parent->foreign_toplevel); } static void @@ -701,6 +704,15 @@ xdg_toplevel_view_map(struct view *view) } struct wlr_xdg_surface *xdg_surface = xdg_surface_from_view(view); wlr_scene_node_set_enabled(&view->scene_tree->node, true); + + if (!view->foreign_toplevel) { + init_foreign_toplevel(view); + /* + * Initial outputs will be synced via + * view->events.new_outputs on view_moved() + */ + } + if (!view->been_mapped) { if (view_wants_decorations(view)) { view_set_ssd_mode(view, LAB_SSD_MODE_FULL); @@ -736,11 +748,6 @@ xdg_toplevel_view_map(struct view *view) view_moved(view); } - if (!view->toplevel.handle) { - init_foreign_toplevel(view); - foreign_toplevel_update_outputs(view); - } - view_impl_map(view); view->been_mapped = true; } @@ -759,8 +766,9 @@ xdg_toplevel_view_unmap(struct view *view, bool client_request) * than just minimized), destroy the foreign toplevel handle so * the unmapped view doesn't show up in panels and the like. */ - if (client_request && view->toplevel.handle) { - wlr_foreign_toplevel_handle_v1_destroy(view->toplevel.handle); + if (client_request && view->foreign_toplevel) { + foreign_toplevel_destroy(view->foreign_toplevel); + view->foreign_toplevel = NULL; } } diff --git a/src/xwayland.c b/src/xwayland.c index c1c5a8ad..9d05d79c 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -8,6 +8,7 @@ #include "common/mem.h" #include "config/rcxml.h" #include "config/session.h" +#include "foreign-toplevel.h" #include "labwc.h" #include "node.h" #include "ssd.h" @@ -669,17 +670,18 @@ set_initial_position(struct view *view, static void init_foreign_toplevel(struct view *view) { - foreign_toplevel_handle_create(view); + assert(!view->foreign_toplevel); + view->foreign_toplevel = foreign_toplevel_create(view); struct wlr_xwayland_surface *surface = xwayland_surface_from_view(view); if (!surface->parent) { return; } struct view *parent = (struct view *)surface->parent->data; - if (!parent || !parent->toplevel.handle) { + if (!parent || !parent->foreign_toplevel) { return; } - wlr_foreign_toplevel_handle_v1_set_parent(view->toplevel.handle, parent->toplevel.handle); + foreign_toplevel_set_parent(view->foreign_toplevel, parent->foreign_toplevel); } static void @@ -736,6 +738,19 @@ xwayland_view_map(struct view *view) view->scene_node = &tree->node; } + /* + * Exclude unfocusable views from wlr-foreign-toplevel. These + * views (notifications, floating toolbars, etc.) should not be + * shown in taskbars/docks/etc. + */ + if (!view->foreign_toplevel && view_is_focusable(view)) { + init_foreign_toplevel(view); + /* + * Initial outputs will be synced via + * view->events.new_outputs on view_moved() + */ + } + if (!view->been_mapped) { check_natural_geometry(view); set_initial_position(view, xwayland_surface); @@ -749,16 +764,6 @@ xwayland_view_map(struct view *view) view_moved(view); } - /* - * Exclude unfocusable views from wlr-foreign-toplevel. These - * views (notifications, floating toolbars, etc.) should not be - * shown in taskbars/docks/etc. - */ - if (!view->toplevel.handle && view_is_focusable(view)) { - init_foreign_toplevel(view); - foreign_toplevel_update_outputs(view); - } - /* Add commit here, as xwayland map/unmap can change the wlr_surface */ wl_signal_add(&xwayland_surface->surface->events.commit, &view->commit); view->commit.notify = handle_commit; @@ -794,8 +799,9 @@ xwayland_view_unmap(struct view *view, bool client_request) * the unmapped view doesn't show up in panels and the like. */ out: - if (client_request && view->toplevel.handle) { - wlr_foreign_toplevel_handle_v1_destroy(view->toplevel.handle); + if (client_request && view->foreign_toplevel) { + foreign_toplevel_destroy(view->foreign_toplevel); + view->foreign_toplevel = NULL; } }