Implement ext-foreign-toplevel-management-v1

This commit is contained in:
Isaac Freund 2021-01-02 16:31:17 +01:00
parent e9ae795c2c
commit 1785a50b75
No known key found for this signature in database
GPG key ID: 86DED400DDFD7A11
7 changed files with 517 additions and 0 deletions

View file

@ -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

View file

@ -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 <wayland-server-core.h>
#include <wlr/types/wlr_ext_foreign_toplevel_info_v1.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_seat.h>
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

View file

@ -0,0 +1,143 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="foreign_toplevel_management_unstable_v1">
<copyright>
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.
</copyright>
<interface name="zext_foreign_toplevel_manager_v1" version="1">
<description summary="control open apps">
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.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the zext_foreign_toplevel_manager_v1">
This request indicates that the client has finished using the
zext_foreign_toplevel_manager_v1 object and that it can be safely
destroyed.
</description>
</request>
<request name="close">
<description summary="request that a toplevel be closed">
If the compositor honors this request, the
zext_foreign_toplevel_handle_v1.closed event will be sent.
</description>
<arg name="toplevel" type="object" interface="zext_foreign_toplevel_handle_v1"/>
</request>
<request name="set_maximized">
<description summary="request that a toplevel be maximized">
If the compositor honors this request, the
zext_foreign_toplevel_handle_v1.state event will be sent.
</description>
<arg name="toplevel" type="object" interface="zext_foreign_toplevel_handle_v1"/>
</request>
<request name="unset_maximized">
<description summary="request that a toplevel be unmaximized">
If the compositor honors this request, the
zext_foreign_toplevel_handle_v1.state event will be sent.
</description>
<arg name="toplevel" type="object" interface="zext_foreign_toplevel_handle_v1"/>
</request>
<request name="set_minimized">
<description summary="request that a toplevel be minimized">
If the compositor honors this request, the
zext_foreign_toplevel_handle_v1.state event will be sent.
</description>
<arg name="toplevel" type="object" interface="zext_foreign_toplevel_handle_v1"/>
</request>
<request name="unset_minimized">
<description summary="request that a toplevel be unminimized">
If the compositor honors this request, the
zext_foreign_toplevel_handle_v1.state event will be sent.
</description>
<arg name="toplevel" type="object" interface="zext_foreign_toplevel_handle_v1"/>
</request>
<request name="set_fullscreen">
<description summary="request that a toplevel be fullscreened">
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.
</description>
<arg name="toplevel" type="object" interface="zext_foreign_toplevel_handle_v1"/>
<arg name="output" type="object" interface="wl_output" allow-null="true"/>
</request>
<request name="unset_fullscreen">
<description summary="request that a toplevel be unfullscreened">
If the compositor honors this request, the
zext_foreign_toplevel_handle_v1.state event will be sent.
</description>
<arg name="toplevel" type="object" interface="zext_foreign_toplevel_handle_v1"/>
</request>
<request name="activate">
<description summary="request that a toplevel be activated">
If the compositor honors this request, the
zext_foreign_toplevel_handle_v1.state event will be sent.
</description>
<arg name="toplevel" type="object" interface="zext_foreign_toplevel_handle_v1"/>
<arg name="seat" type="object" interface="wl_seat"/>
</request>
<request name="set_rectangle">
<description summary="designate a rectangle to represent a toplevel">
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.
</description>
<arg name="toplevel" type="object" interface="zext_foreign_toplevel_handle_v1"/>
<arg name="surface" type="object" interface="wl_surface"/>
<arg name="x" type="int"/>
<arg name="y" type="int"/>
<arg name="width" type="int"/>
<arg name="height" type="int"/>
</request>
<enum name="error">
<entry name="invalid_rectangle" value="0"
summary="the provided rectangle is invalid"/>
</enum>
</interface>
</protocol>

View file

@ -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 = {}

View file

@ -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',

View file

@ -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);

View file

@ -0,0 +1,278 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <wlr/types/wlr_ext_foreign_toplevel_info_v1.h>
#include <wlr/types/wlr_ext_foreign_toplevel_management_v1.h>
#include <wlr/types/wlr_seat.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/util/log.h>
#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;
}