From 1785a50b7564ab115b77018d513f88e61704b995 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sat, 2 Jan 2021 16:31:17 +0100 Subject: [PATCH] Implement ext-foreign-toplevel-management-v1 --- .../types/wlr_ext_foreign_toplevel_info_v1.h | 4 + .../wlr_ext_foreign_toplevel_management_v1.h | 82 ++++++ ...oreign-toplevel-management-unstable-v1.xml | 143 +++++++++ protocol/meson.build | 1 + types/meson.build | 1 + types/wlr_ext_foreign_toplevel_info_v1.c | 8 + .../wlr_ext_foreign_toplevel_management_v1.c | 278 ++++++++++++++++++ 7 files changed, 517 insertions(+) create mode 100644 include/wlr/types/wlr_ext_foreign_toplevel_management_v1.h create mode 100644 protocol/foreign-toplevel-management-unstable-v1.xml create mode 100644 types/wlr_ext_foreign_toplevel_management_v1.c diff --git a/include/wlr/types/wlr_ext_foreign_toplevel_info_v1.h b/include/wlr/types/wlr_ext_foreign_toplevel_info_v1.h index 2bf840158..a5cb85d01 100644 --- a/include/wlr/types/wlr_ext_foreign_toplevel_info_v1.h +++ b/include/wlr/types/wlr_ext_foreign_toplevel_info_v1.h @@ -67,6 +67,10 @@ struct wlr_ext_foreign_toplevel_info_v1 *wlr_ext_foreign_toplevel_info_v1_create struct wlr_ext_foreign_toplevel_handle_v1 *wlr_ext_foreign_toplevel_handle_v1_create( struct wlr_ext_foreign_toplevel_info_v1 *info); +// Returns NULL if the toplevel handle is inert +struct wlr_ext_foreign_toplevel_handle_v1 * +wlr_ext_foreign_toplevel_handle_v1_from_resource(struct wl_resource *resource); + /* Destroy the given toplevel handle, sending the closed event to any * client. Also, if the destroyed toplevel is set as a parent of any * other valid toplevel, clients still holding a handle to both are diff --git a/include/wlr/types/wlr_ext_foreign_toplevel_management_v1.h b/include/wlr/types/wlr_ext_foreign_toplevel_management_v1.h new file mode 100644 index 000000000..3824328ff --- /dev/null +++ b/include/wlr/types/wlr_ext_foreign_toplevel_management_v1.h @@ -0,0 +1,82 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_WLR_EXT_FOREIGN_TOPLEVEL_MANAGEMENT_V1_H +#define WLR_TYPES_WLR_EXT_FOREIGN_TOPLEVEL_MANAGEMENT_V1_H + +#include +#include +#include +#include + +struct wlr_ext_foreign_toplevel_manager_v1 { + struct wl_global *global; + + struct wl_listener display_destroy; + + struct { + // wlr_ext_foreign_toplevel_manager_v1_maximize_event + struct wl_signal request_maximize; + // wlr_ext_foreign_toplevel_manager_v1_minimize_event + struct wl_signal request_minimize; + // wlr_ext_foreign_toplevel_manager_v1_activate_event + struct wl_signal request_activate; + // wlr_ext_foreign_toplevel_manager_v1_fullscreen_event + struct wl_signal request_fullscreen; + // wlr_ext_foreign_toplevel_manager_v1_close_event + struct wl_signal request_close; + // wlr_ext_foreign_toplevel_manager_v1_set_rectangle_event + struct wl_signal set_rectangle; + + struct wl_signal destroy; + } events; + + void *data; +}; + +struct wlr_ext_foreign_toplevel_manager_v1_maximize_event { + struct wlr_ext_foreign_toplevel_manager_v1 *manager; + struct wlr_ext_foreign_toplevel_handle_v1 *toplevel; + bool maximize; +}; + +struct wlr_ext_foreign_toplevel_manager_v1_minimize_event { + struct wlr_ext_foreign_toplevel_manager_v1 *manager; + struct wlr_ext_foreign_toplevel_handle_v1 *toplevel; + bool minimize; +}; + +struct wlr_ext_foreign_toplevel_manager_v1_activate_event { + struct wlr_ext_foreign_toplevel_manager_v1 *manager; + struct wlr_ext_foreign_toplevel_handle_v1 *toplevel; + struct wlr_seat *seat; +}; + +struct wlr_ext_foreign_toplevel_manager_v1_fullscreen_event { + struct wlr_ext_foreign_toplevel_manager_v1 *manager; + struct wlr_ext_foreign_toplevel_handle_v1 *toplevel; + bool fullscreen; + struct wlr_output *output; +}; + +struct wlr_ext_foreign_toplevel_manager_v1_close_event { + struct wlr_ext_foreign_toplevel_manager_v1 *manager; + struct wlr_ext_foreign_toplevel_handle_v1 *toplevel; +}; + +struct wlr_ext_foreign_toplevel_manager_v1_set_rectangle_event { + struct wlr_ext_foreign_toplevel_manager_v1 *manager; + struct wlr_ext_foreign_toplevel_handle_v1 *toplevel; + struct wlr_surface *surface; + int32_t x, y, width, height; +}; + +struct wlr_ext_foreign_toplevel_manager_v1 *wlr_ext_foreign_toplevel_manager_v1_create( + struct wl_display *display); + +#endif diff --git a/protocol/foreign-toplevel-management-unstable-v1.xml b/protocol/foreign-toplevel-management-unstable-v1.xml new file mode 100644 index 000000000..42a4e03ed --- /dev/null +++ b/protocol/foreign-toplevel-management-unstable-v1.xml @@ -0,0 +1,143 @@ + + + + Copyright © 2018 Ilia Bozhinov + Copyright © 2020 Isaac Freund + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + This protocol allows clients such as a taskbar to request the compositor + to preform typical actions on open toplevels. The compositor is in all + cases free to ignore the request. + + + + + This request indicates that the client has finished using the + zext_foreign_toplevel_manager_v1 object and that it can be safely + destroyed. + + + + + + If the compositor honors this request, the + zext_foreign_toplevel_handle_v1.closed event will be sent. + + + + + + + If the compositor honors this request, the + zext_foreign_toplevel_handle_v1.state event will be sent. + + + + + + + If the compositor honors this request, the + zext_foreign_toplevel_handle_v1.state event will be sent. + + + + + + + If the compositor honors this request, the + zext_foreign_toplevel_handle_v1.state event will be sent. + + + + + + + If the compositor honors this request, the + zext_foreign_toplevel_handle_v1.state event will be sent. + + + + + + + If the compositor honors this request, the + zext_foreign_toplevel_handle_v1.state and potentially the + zext_foreign_toplevel_handle_v1.output_enter/output_leave events will + be sent. + + The output parameter a hint to the compositor and may be ignored. A + value of NULL indicates that the compositor should choose the target + output, if it honors the fullscreen request. + + + + + + + + If the compositor honors this request, the + zext_foreign_toplevel_handle_v1.state event will be sent. + + + + + + + If the compositor honors this request, the + zext_foreign_toplevel_handle_v1.state event will be sent. + + + + + + + + If a client using this protocol displays UI elements corresponding + to toplevels, it may use this request to inform the server about such + a relation. This information may be used by the server, for example as + the target for a minimize animation. + + If the client sets more than one rectangle, only the most recently + set rectangle is considered. + + The dimensions are given in surface-local coordinates. + + Setting width=height=0 removes the current rectangle if one was set. + + + + + + + + + + + + + + diff --git a/protocol/meson.build b/protocol/meson.build index 5799a469a..42ae1170d 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -45,6 +45,7 @@ protocols = { 'wlr-screencopy-unstable-v1': 'wlr-screencopy-unstable-v1.xml', 'wlr-virtual-pointer-unstable-v1': 'wlr-virtual-pointer-unstable-v1.xml', 'foreign-toplevel-info-unstable-v1': 'foreign-toplevel-info-unstable-v1.xml', + 'foreign-toplevel-management-unstable-v1': 'foreign-toplevel-management-unstable-v1.xml', } protocols_code = {} diff --git a/types/meson.build b/types/meson.build index 00870e1c0..461787823 100644 --- a/types/meson.build +++ b/types/meson.build @@ -23,6 +23,7 @@ wlr_files += files( 'wlr_data_control_v1.c', 'wlr_export_dmabuf_v1.c', 'wlr_ext_foreign_toplevel_info_v1.c', + 'wlr_ext_foreign_toplevel_management_v1.c', 'wlr_foreign_toplevel_management_v1.c', 'wlr_fullscreen_shell_v1.c', 'wlr_gamma_control_v1.c', diff --git a/types/wlr_ext_foreign_toplevel_info_v1.c b/types/wlr_ext_foreign_toplevel_info_v1.c index adc5b47ac..164df0a11 100644 --- a/types/wlr_ext_foreign_toplevel_info_v1.c +++ b/types/wlr_ext_foreign_toplevel_info_v1.c @@ -13,6 +13,14 @@ static const struct zext_foreign_toplevel_handle_v1_interface toplevel_handle_impl; +struct wlr_ext_foreign_toplevel_handle_v1 * +wlr_ext_foreign_toplevel_handle_v1_from_resource(struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &zext_foreign_toplevel_handle_v1_interface, + &toplevel_handle_impl)); + return wl_resource_get_user_data(resource); +} + static void foreign_toplevel_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); diff --git a/types/wlr_ext_foreign_toplevel_management_v1.c b/types/wlr_ext_foreign_toplevel_management_v1.c new file mode 100644 index 000000000..cfaf79c27 --- /dev/null +++ b/types/wlr_ext_foreign_toplevel_management_v1.c @@ -0,0 +1,278 @@ +#define _POSIX_C_SOURCE 200809L + +#include +#include +#include +#include +#include +#include +#include +#include +#include "util/signal.h" +#include "foreign-toplevel-management-unstable-v1-protocol.h" + +#define EXT_FOREIGN_TOPLEVEL_MANAGEMENT_V1_VERSION 1 + +static const struct zext_foreign_toplevel_manager_v1_interface manager_impl; + +static struct wlr_ext_foreign_toplevel_manager_v1 *manager_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &zext_foreign_toplevel_manager_v1_interface, + &manager_impl)); + return wl_resource_get_user_data(resource); +} + +static void manager_emit_maximized_signal(struct wl_resource *resource, + struct wl_resource *toplevel_resource, bool state) { + struct wlr_ext_foreign_toplevel_manager_v1 *manager = + manager_from_resource(resource); + if (!manager) { + return; + } + struct wlr_ext_foreign_toplevel_handle_v1 *toplevel = + wlr_ext_foreign_toplevel_handle_v1_from_resource(toplevel_resource); + if (!toplevel) { + return; + } + + struct wlr_ext_foreign_toplevel_manager_v1_maximize_event event = { + .manager = manager, + .toplevel = toplevel, + .maximize = state, + }; + wlr_signal_emit_safe(&manager->events.request_maximize, &event); +} + +static void manager_handle_set_maximized(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *toplevel_resource) { + manager_emit_maximized_signal(resource, toplevel_resource, true); +} + +static void manager_handle_unset_maximized(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *toplevel_resource) { + manager_emit_maximized_signal(resource, toplevel_resource, false); +} + +static void manager_emit_minimized_signal(struct wl_resource *resource, + struct wl_resource *toplevel_resource, bool state) { + struct wlr_ext_foreign_toplevel_manager_v1 *manager = + manager_from_resource(resource); + if (!manager) { + return; + } + struct wlr_ext_foreign_toplevel_handle_v1 *toplevel = + wlr_ext_foreign_toplevel_handle_v1_from_resource(toplevel_resource); + if (!toplevel) { + return; + } + + struct wlr_ext_foreign_toplevel_manager_v1_minimize_event event = { + .manager = manager, + .toplevel = toplevel, + .minimize = state, + }; + wlr_signal_emit_safe(&manager->events.request_minimize, &event); +} + +static void manager_handle_set_minimized(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *toplevel_resource) { + manager_emit_minimized_signal(resource, toplevel_resource, true); +} + +static void manager_handle_unset_minimized(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *toplevel_resource) { + manager_emit_minimized_signal(resource, toplevel_resource, false); +} + +static void manager_emit_fullscreen_signal(struct wl_resource *resource, + struct wl_resource *toplevel_resource, bool state, + struct wl_resource *output_resource) { + struct wlr_ext_foreign_toplevel_manager_v1 *manager = + manager_from_resource(resource); + if (!manager) { + return; + } + struct wlr_ext_foreign_toplevel_handle_v1 *toplevel = + wlr_ext_foreign_toplevel_handle_v1_from_resource(toplevel_resource); + if (!toplevel) { + return; + } + + struct wlr_output *output = NULL; + if (output_resource) { + output = wlr_output_from_resource(output_resource); + } + struct wlr_ext_foreign_toplevel_manager_v1_fullscreen_event event = { + .manager = manager, + .toplevel = toplevel, + .fullscreen = state, + .output = output, + }; + wlr_signal_emit_safe(&manager->events.request_fullscreen, &event); +} + +static void manager_handle_set_fullscreen(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *toplevel_resource, + struct wl_resource *output) { + manager_emit_fullscreen_signal(resource, toplevel_resource, true, output); +} + +static void manager_handle_unset_fullscreen(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *toplevel_resource) { + manager_emit_fullscreen_signal(resource, toplevel_resource, false, NULL); +} + +static void manager_handle_activate(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *toplevel_resource, + struct wl_resource *seat_resource) { + struct wlr_ext_foreign_toplevel_manager_v1 *manager = + manager_from_resource(resource); + if (!manager) { + return; + } + struct wlr_ext_foreign_toplevel_handle_v1 *toplevel = + wlr_ext_foreign_toplevel_handle_v1_from_resource(toplevel_resource); + if (!toplevel) { + return; + } + + struct wlr_seat_client *seat_client = + wlr_seat_client_from_resource(seat_resource); + if (!seat_client) { + return; + } + struct wlr_ext_foreign_toplevel_manager_v1_activate_event event = { + .manager = manager, + .toplevel = toplevel, + .seat = seat_client->seat, + }; + wlr_signal_emit_safe(&manager->events.request_activate, &event); +} + +static void manager_handle_close(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *toplevel_resource) { + struct wlr_ext_foreign_toplevel_manager_v1 *manager = + manager_from_resource(resource); + if (!manager) { + return; + } + struct wlr_ext_foreign_toplevel_handle_v1 *toplevel = + wlr_ext_foreign_toplevel_handle_v1_from_resource(toplevel_resource); + if (!toplevel) { + return; + } + + struct wlr_ext_foreign_toplevel_manager_v1_close_event event = { + .manager = manager, + .toplevel = toplevel, + }; + wlr_signal_emit_safe(&manager->events.request_close, &event); +} + +static void manager_handle_set_rectangle(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *toplevel_resource, + struct wl_resource *surface_resource, + int32_t x, int32_t y, int32_t width, int32_t height) { + struct wlr_ext_foreign_toplevel_manager_v1 *manager = + manager_from_resource(resource); + if (!manager) { + return; + } + struct wlr_ext_foreign_toplevel_handle_v1 *toplevel = + wlr_ext_foreign_toplevel_handle_v1_from_resource(toplevel_resource); + if (!toplevel) { + return; + } + + if (width < 0 || height < 0) { + wl_resource_post_error(resource, + ZEXT_FOREIGN_TOPLEVEL_MANAGER_V1_ERROR_INVALID_RECTANGLE, + "invalid rectangle passed to set_rectangle: width or height < 0"); + return; + } + + struct wlr_ext_foreign_toplevel_manager_v1_set_rectangle_event event = { + .manager = manager, + .toplevel = toplevel, + .surface = wlr_surface_from_resource(surface_resource), + .x = x, + .y = y, + .width = width, + .height = height, + }; + wlr_signal_emit_safe(&manager->events.set_rectangle, &event); +} + +static void manager_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static const struct zext_foreign_toplevel_manager_v1_interface manager_impl = { + .set_maximized = manager_handle_set_maximized, + .unset_maximized = manager_handle_unset_maximized, + .set_minimized = manager_handle_set_minimized, + .unset_minimized = manager_handle_unset_minimized, + .activate = manager_handle_activate, + .close = manager_handle_close, + .set_rectangle = manager_handle_set_rectangle, + .set_fullscreen = manager_handle_set_fullscreen, + .unset_fullscreen = manager_handle_unset_fullscreen, + .destroy = manager_handle_destroy, +}; + +static void manager_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) { + struct wlr_ext_foreign_toplevel_manager_v1 *manager = data; + struct wl_resource *resource = wl_resource_create(client, + &zext_foreign_toplevel_manager_v1_interface, version, id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &manager_impl, manager, NULL); +} + +static void handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_ext_foreign_toplevel_manager_v1 *manager = + wl_container_of(listener, manager, display_destroy); + wlr_signal_emit_safe(&manager->events.destroy, manager); + wl_list_remove(&manager->display_destroy.link); + wl_global_destroy(manager->global); + free(manager); +} + +struct wlr_ext_foreign_toplevel_manager_v1 *wlr_ext_foreign_toplevel_manager_v1_create( + struct wl_display *display) { + struct wlr_ext_foreign_toplevel_manager_v1 *manager = calloc(1, + sizeof(struct wlr_ext_foreign_toplevel_manager_v1)); + if (!manager) { + return NULL; + } + + manager->global = wl_global_create(display, + &zext_foreign_toplevel_manager_v1_interface, + EXT_FOREIGN_TOPLEVEL_MANAGEMENT_V1_VERSION, manager, + manager_bind); + if (!manager->global) { + free(manager); + return NULL; + } + + wl_signal_init(&manager->events.request_maximize); + wl_signal_init(&manager->events.request_minimize); + wl_signal_init(&manager->events.request_activate); + wl_signal_init(&manager->events.request_fullscreen); + wl_signal_init(&manager->events.request_close); + wl_signal_init(&manager->events.set_rectangle); + wl_signal_init(&manager->events.destroy); + + manager->display_destroy.notify = handle_display_destroy; + wl_display_add_destroy_listener(display, &manager->display_destroy); + + return manager; +}