diff --git a/include/types/wlr_foreign_toplevel.h b/include/types/wlr_foreign_toplevel.h new file mode 100644 index 000000000..6b808e789 --- /dev/null +++ b/include/types/wlr_foreign_toplevel.h @@ -0,0 +1,12 @@ +#ifndef TYPES_WLR_FOREIGN_TOPLEVEL_H +#define TYPES_WLR_FOREIGN_TOPLEVEL_H + +#include +#include + +struct wl_resource *foreign_toplevel_create_resource_for_client( + struct wlr_ext_foreign_toplevel_handle_v1 *toplevel, struct wl_client *client); +void foreign_toplevel_send_details_to_resource( + struct wlr_ext_foreign_toplevel_handle_v1 *toplevel, struct wl_resource *resource); + +#endif diff --git a/include/wlr/types/wlr_ext_foreign_toplevel_list_v1.h b/include/wlr/types/wlr_ext_foreign_toplevel_list_v1.h index 902418eaf..e28000d4a 100644 --- a/include/wlr/types/wlr_ext_foreign_toplevel_list_v1.h +++ b/include/wlr/types/wlr_ext_foreign_toplevel_list_v1.h @@ -32,6 +32,7 @@ struct wlr_ext_foreign_toplevel_handle_v1 { struct wl_list resources; // wl_resource_get_link() struct wl_list link; // wlr_ext_foreign_toplevel_list_v1.toplevels + uint32_t version; char *title; char *app_id; char *identifier; @@ -62,9 +63,8 @@ struct wlr_ext_foreign_toplevel_handle_v1 *wlr_ext_foreign_toplevel_handle_v1_cr void wlr_ext_foreign_toplevel_handle_v1_destroy( struct wlr_ext_foreign_toplevel_handle_v1 *toplevel); -void wlr_ext_foreign_toplevel_handle_v1_update_state( - struct wlr_ext_foreign_toplevel_handle_v1 *toplevel, - const struct wlr_ext_foreign_toplevel_handle_v1_state *state); +void wlr_ext_foreign_toplevel_handle_v1_update_state(struct wlr_ext_foreign_toplevel_handle_v1 *toplevel, + const struct wlr_ext_foreign_toplevel_handle_v1_state *state, uint32_t version); struct wlr_ext_foreign_toplevel_handle_v1 *wlr_ext_foreign_toplevel_handle_v1_from_resource( struct wl_resource *resource); diff --git a/include/wlr/types/wlr_ext_foreign_toplevel_request_v1.h b/include/wlr/types/wlr_ext_foreign_toplevel_request_v1.h new file mode 100644 index 000000000..35d118e49 --- /dev/null +++ b/include/wlr/types/wlr_ext_foreign_toplevel_request_v1.h @@ -0,0 +1,73 @@ +/* + * 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_FOREIGN_TOPLEVEL_REQUEST_V1_H +#define WLR_TYPES_WLR_FOREIGN_TOPLEVEL_REQUEST_V1_H + +#include + +struct wlr_ext_foreign_toplevel_request_manager_v1 { + struct wl_global *global; + struct wl_list resources; // wl_resource_get_link() + + struct { + struct wl_signal request; + struct wl_signal destroy; + } events; + + void *data; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; +}; + +struct wlr_ext_foreign_toplevel_request_v1 { + struct wl_resource *manager; + struct wl_resource *resource; + + struct { + struct wl_signal destroy; + } events; +}; + +struct wlr_ext_foreign_toplevel_request_source_v1 { + struct wl_global *global; + struct wl_list resources; // wl_resource_get_link() + + struct { + struct wl_signal toplevel; + struct wl_signal cancel; + struct wl_signal destroy; + } events; + + struct { + struct wl_listener display_destroy; + } WLR_PRIVATE; +}; + +struct wlr_ext_foreign_toplevel_request_pending_v1 { + struct wlr_ext_foreign_toplevel_request_source_v1 *source; + struct wlr_ext_foreign_toplevel_request_v1 *request; + struct wlr_ext_foreign_toplevel_handle_v1 *handle; + struct wl_resource *resource; +}; + +struct wlr_ext_foreign_toplevel_request_manager_v1 * + wlr_ext_foreign_toplevel_request_manager_v1_create(struct wl_display *display, uint32_t version); + +void wlr_ext_foreign_toplevel_request_v1_send_toplevel( + struct wlr_ext_foreign_toplevel_request_v1 *request, struct wlr_ext_foreign_toplevel_handle_v1 *toplevel); +void wlr_ext_foreign_toplevel_request_v1_cancel(struct wlr_ext_foreign_toplevel_request_v1 *request); + +struct wlr_ext_foreign_toplevel_request_source_v1 * + wlr_ext_foreign_toplevel_request_source_v1_create(struct wl_display *display, uint32_t version); + +void wlr_ext_foreign_toplevel_request_source_v1_request( + struct wlr_ext_foreign_toplevel_request_source_v1 *source, struct wlr_ext_foreign_toplevel_request_v1 *request); +#endif diff --git a/protocol/meson.build b/protocol/meson.build index 5012753b5..f07e4954c 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -30,6 +30,7 @@ protocols = { 'cursor-shape-v1': wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml', 'drm-lease-v1': wl_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml', 'ext-foreign-toplevel-list-v1': wl_protocol_dir / 'staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml', + 'ext-foreign-toplevel-request-v1': wl_protocol_dir / 'staging/ext-foreign-toplevel-request/ext-foreign-toplevel-request-v1.xml', 'ext-idle-notify-v1': wl_protocol_dir / 'staging/ext-idle-notify/ext-idle-notify-v1.xml', 'ext-image-capture-source-v1': wl_protocol_dir / 'staging/ext-image-capture-source/ext-image-capture-source-v1.xml', 'ext-image-copy-capture-v1': wl_protocol_dir / 'staging/ext-image-copy-capture/ext-image-copy-capture-v1.xml', diff --git a/types/meson.build b/types/meson.build index 25a0d4434..84ff2b127 100644 --- a/types/meson.build +++ b/types/meson.build @@ -51,6 +51,7 @@ wlr_files += files( 'wlr_color_representation_v1.c', 'wlr_ext_image_copy_capture_v1.c', 'wlr_ext_foreign_toplevel_list_v1.c', + 'wlr_ext_foreign_toplevel_request_v1.c', 'wlr_ext_data_control_v1.c', 'wlr_fractional_scale_v1.c', 'wlr_gamma_control_v1.c', diff --git a/types/wlr_ext_foreign_toplevel_list_v1.c b/types/wlr_ext_foreign_toplevel_list_v1.c index f20ddc877..5a6ea71ed 100644 --- a/types/wlr_ext_foreign_toplevel_list_v1.c +++ b/types/wlr_ext_foreign_toplevel_list_v1.c @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include #include #include "ext-foreign-toplevel-list-v1-protocol.h" @@ -10,6 +10,7 @@ #include "util/token.h" #define FOREIGN_TOPLEVEL_LIST_V1_VERSION 1 +#define FOREIGN_TOPLEVEL_HANDLE_V1_VERSION 1 static const struct ext_foreign_toplevel_handle_v1_interface toplevel_handle_impl; @@ -112,13 +113,10 @@ static void foreign_toplevel_resource_destroy(struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(resource)); } -static struct wl_resource *create_toplevel_resource_for_resource( - struct wlr_ext_foreign_toplevel_handle_v1 *toplevel, - struct wl_resource *list_resource) { - struct wl_client *client = wl_resource_get_client(list_resource); +struct wl_resource *foreign_toplevel_create_resource_for_client( + struct wlr_ext_foreign_toplevel_handle_v1 *toplevel, struct wl_client *client) { struct wl_resource *resource = wl_resource_create(client, - &ext_foreign_toplevel_handle_v1_interface, - wl_resource_get_version(list_resource), 0); + &ext_foreign_toplevel_handle_v1_interface, toplevel->version, 0); if (!resource) { wl_client_post_no_memory(client); return NULL; @@ -128,11 +126,19 @@ static struct wl_resource *create_toplevel_resource_for_resource( foreign_toplevel_resource_destroy); wl_list_insert(&toplevel->resources, wl_resource_get_link(resource)); + return resource; +} + +static struct wl_resource *create_toplevel_resource_for_resource( + struct wlr_ext_foreign_toplevel_handle_v1 *toplevel, + struct wl_resource *list_resource) { + struct wl_resource *resource = foreign_toplevel_create_resource_for_client( + toplevel, wl_resource_get_client(list_resource)); ext_foreign_toplevel_list_v1_send_toplevel(list_resource, resource); return resource; } -static void toplevel_send_details_to_toplevel_resource( +void foreign_toplevel_send_details_to_resource( struct wlr_ext_foreign_toplevel_handle_v1 *toplevel, struct wl_resource *resource) { if (toplevel->title) { @@ -148,13 +154,15 @@ static void toplevel_send_details_to_toplevel_resource( struct wlr_ext_foreign_toplevel_handle_v1 * wlr_ext_foreign_toplevel_handle_v1_create(struct wlr_ext_foreign_toplevel_list_v1 *list, - const struct wlr_ext_foreign_toplevel_handle_v1_state *state) { + const struct wlr_ext_foreign_toplevel_handle_v1_state *state, uint32_t version) { + assert(version <= FOREIGN_TOPLEVEL_HANDLE_V1_VERSION); struct wlr_ext_foreign_toplevel_handle_v1 *toplevel = calloc(1, sizeof(*toplevel)); if (!toplevel) { wlr_log(WLR_ERROR, "failed to allocate memory for toplevel handle"); return NULL; } + toplevel->version = version; toplevel->identifier = calloc(TOKEN_SIZE, sizeof(char)); if (toplevel->identifier == NULL) { wlr_log(WLR_ERROR, "failed to allocate memory for toplevel identifier"); @@ -187,7 +195,7 @@ wlr_ext_foreign_toplevel_handle_v1_create(struct wlr_ext_foreign_toplevel_list_v if (!toplevel_resource) { continue; } - toplevel_send_details_to_toplevel_resource(toplevel, toplevel_resource); + foreign_toplevel_send_details_to_resource(toplevel, toplevel_resource); } return toplevel; @@ -245,7 +253,7 @@ static void foreign_toplevel_list_bind(struct wl_client *client, void *data, wl_list_for_each(toplevel, &list->toplevels, link) { struct wl_resource *toplevel_resource = create_toplevel_resource_for_resource(toplevel, resource); - toplevel_send_details_to_toplevel_resource(toplevel, + foreign_toplevel_send_details_to_resource(toplevel, toplevel_resource); } } diff --git a/types/wlr_ext_foreign_toplevel_request_v1.c b/types/wlr_ext_foreign_toplevel_request_v1.c new file mode 100644 index 000000000..7ba60d7a2 --- /dev/null +++ b/types/wlr_ext_foreign_toplevel_request_v1.c @@ -0,0 +1,298 @@ +#include +#include +#include +#include +#include +#include "ext-foreign-toplevel-request-v1-protocol.h" + +#define FOREIGN_TOPLEVEL_REQUEST_V1_VERSION 1 + +struct ext_foreign_toplevel_request_v1_interface foreign_toplevel_request_v1_impl; +static struct wlr_ext_foreign_toplevel_request_v1 * + foreign_toplevel_request_v1_from_resource(struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &ext_foreign_toplevel_request_v1_interface, &foreign_toplevel_request_v1_impl)); + return wl_resource_get_user_data(resource); +} + +static void foreign_toplevel_request_v1_destroy(struct wl_client *client, struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &ext_foreign_toplevel_request_v1_interface, &foreign_toplevel_request_v1_impl)); + wl_resource_destroy(resource); +} + +static void foreign_toplevel_request_v1_resource_destroy(struct wl_resource *resource) { + struct wlr_ext_foreign_toplevel_request_v1 *request = + foreign_toplevel_request_v1_from_resource(resource); + wl_signal_emit_mutable(&request->events.destroy, NULL); + free(request); +} + +struct ext_foreign_toplevel_request_v1_interface foreign_toplevel_request_v1_impl = { + .destroy = foreign_toplevel_request_v1_destroy, +}; + +static const struct ext_foreign_toplevel_request_manager_v1_interface foreign_toplevel_request_manager_v1_impl; +static struct wlr_ext_foreign_toplevel_request_manager_v1 * + foreign_toplevel_request_manager_v1_from_resource(struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &ext_foreign_toplevel_request_manager_v1_interface, + &foreign_toplevel_request_manager_v1_impl)); + return wl_resource_get_user_data(resource); +} + +static void foreign_toplevel_request_manager_request( + struct wl_client *client, struct wl_resource *resource, uint32_t request) { + struct wlr_ext_foreign_toplevel_request_manager_v1 *manager + = foreign_toplevel_request_manager_v1_from_resource(resource); + struct wlr_ext_foreign_toplevel_request_v1 *wlr_request = calloc(1, sizeof(*wlr_request)); + if (!wlr_request) { + wl_resource_post_no_memory(resource); + return; + } + + wlr_request->manager = resource; + wlr_request->resource = wl_resource_create(client, + &ext_foreign_toplevel_request_v1_interface, + ext_foreign_toplevel_request_v1_interface.version, request); + if (!wlr_request->resource) { + wl_resource_post_no_memory(resource); + free(wlr_request); + return; + } + + wl_signal_init(&wlr_request->events.destroy); + + wl_resource_set_implementation(wlr_request->resource, &foreign_toplevel_request_v1_impl, + wlr_request, foreign_toplevel_request_v1_resource_destroy); + + wl_signal_emit_mutable(&manager->events.request, wlr_request); +} + +static void foreign_toplevel_request_manager_destroy( + struct wl_client *client, struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &ext_foreign_toplevel_request_manager_v1_interface, + &foreign_toplevel_request_manager_v1_impl)); + + wl_resource_destroy(resource); +} + +static const struct ext_foreign_toplevel_request_manager_v1_interface foreign_toplevel_request_manager_v1_impl = { + .request = foreign_toplevel_request_manager_request, + .destroy = foreign_toplevel_request_manager_destroy +}; + +static void foreign_toplevel_request_manager_resource_destroy(struct wl_resource *resource) { + wl_list_remove(wl_resource_get_link(resource)); +} + +static void foreign_toplevel_request_manager_bind( + struct wl_client *client, void *data, uint32_t version, uint32_t id) { + struct wlr_ext_foreign_toplevel_request_manager_v1 *manager = data; + struct wl_resource *resource = wl_resource_create(client, + &ext_foreign_toplevel_request_manager_v1_interface, version, id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(resource, &foreign_toplevel_request_manager_v1_impl, + manager, foreign_toplevel_request_manager_resource_destroy); + wl_list_insert(&manager->resources, wl_resource_get_link(resource)); +} + +static void manager_handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_ext_foreign_toplevel_request_manager_v1 *manager = + wl_container_of(listener, manager, display_destroy); + wl_signal_emit_mutable(&manager->events.destroy, NULL); + + assert(wl_list_empty(&manager->events.destroy.listener_list)); + + wl_list_remove(&manager->display_destroy.link); + wl_global_destroy(manager->global); + free(manager); +} + +struct wlr_ext_foreign_toplevel_request_manager_v1 * + wlr_ext_foreign_toplevel_request_manager_v1_create(struct wl_display *display, uint32_t version) { + assert(version <= FOREIGN_TOPLEVEL_REQUEST_V1_VERSION); + + struct wlr_ext_foreign_toplevel_request_manager_v1 *manager = calloc(1, sizeof(*manager)); + if (!manager) { + return NULL; + } + + manager->global = wl_global_create(display, + &ext_foreign_toplevel_request_manager_v1_interface, + version, manager, foreign_toplevel_request_manager_bind); + if (!manager->global) { + free(manager); + return NULL; + } + + wl_list_init(&manager->resources); + + wl_signal_init(&manager->events.request); + wl_signal_init(&manager->events.destroy); + + manager->display_destroy.notify = manager_handle_display_destroy; + wl_display_add_destroy_listener(display, &manager->display_destroy); + + return manager; +} + +struct wl_resource *create_toplevel_resource_for_resource( + struct wlr_ext_foreign_toplevel_handle_v1 *toplevel, struct wl_resource *list_resource); + +void wlr_ext_foreign_toplevel_request_v1_send_toplevel( + struct wlr_ext_foreign_toplevel_request_v1 *request, struct wlr_ext_foreign_toplevel_handle_v1 *toplevel) { + struct wl_resource *resource = foreign_toplevel_create_resource_for_client( + toplevel, wl_resource_get_client(request->manager)); + ext_foreign_toplevel_request_v1_send_toplevel(request->resource, resource); + foreign_toplevel_send_details_to_resource(toplevel, resource); +} + +void wlr_ext_foreign_toplevel_request_v1_cancel(struct wlr_ext_foreign_toplevel_request_v1 *request) { + ext_foreign_toplevel_request_v1_send_cancelled(request->resource); +} + +struct ext_foreign_toplevel_request_source_v1_interface foreign_toplevel_request_source_v1_impl; +static void foreign_toplevel_request_source_destroy( + struct wl_client *client, struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &ext_foreign_toplevel_request_source_v1_interface, + &foreign_toplevel_request_source_v1_impl)); + + wl_resource_destroy(resource); +} + +struct ext_foreign_toplevel_request_source_v1_interface foreign_toplevel_request_source_v1_impl = { + .destroy = foreign_toplevel_request_source_destroy +}; + +static void source_handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_ext_foreign_toplevel_request_source_v1 *source = + wl_container_of(listener, source, display_destroy); + wl_signal_emit_mutable(&source->events.destroy, NULL); + + assert(wl_list_empty(&source->events.destroy.listener_list)); + + wl_list_remove(&source->display_destroy.link); + wl_global_destroy(source->global); + free(source); +} + +static void foreign_toplevel_request_source_resource_destroy(struct wl_resource *resource) { + wl_list_remove(wl_resource_get_link(resource)); +} + +static void foreign_toplevel_request_source_bind( + struct wl_client *client, void *data, uint32_t version, uint32_t id) { + struct wlr_ext_foreign_toplevel_request_source_v1 *source = data; + struct wl_resource *resource = wl_resource_create(client, + &ext_foreign_toplevel_request_source_v1_interface, version, id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(resource, &foreign_toplevel_request_source_v1_impl, + source, foreign_toplevel_request_source_resource_destroy); + wl_list_insert(&source->resources, wl_resource_get_link(resource)); +} + +struct wlr_ext_foreign_toplevel_request_source_v1 * + wlr_ext_foreign_toplevel_request_source_v1_create(struct wl_display *display, uint32_t version) { + assert(version <= FOREIGN_TOPLEVEL_REQUEST_V1_VERSION); + + struct wlr_ext_foreign_toplevel_request_source_v1 *source = calloc(1, sizeof(*source)); + if (!source) { + return NULL; + } + + source->global = wl_global_create(display, + &ext_foreign_toplevel_request_source_v1_interface, + version, source, foreign_toplevel_request_source_bind); + if (!source->global) { + free(source); + return NULL; + } + + wl_list_init(&source->resources); + + wl_signal_init(&source->events.toplevel); + wl_signal_init(&source->events.cancel); + wl_signal_init(&source->events.destroy); + + source->display_destroy.notify = source_handle_display_destroy; + wl_display_add_destroy_listener(display, &source->display_destroy); + + return source; +} + +struct ext_foreign_toplevel_request_pending_v1_interface foreign_toplevel_request_pending_v1_impl; +static struct wlr_ext_foreign_toplevel_request_pending_v1 * + foreign_toplevel_request_pending_v1_from_resource(struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &ext_foreign_toplevel_request_pending_v1_interface, &foreign_toplevel_request_pending_v1_impl)); + return wl_resource_get_user_data(resource); +} + +static void foreign_toplevel_request_pending_v1_resource_destroy(struct wl_resource *resource) { + free(foreign_toplevel_request_pending_v1_from_resource(resource)); +} + +static void foreign_toplevel_request_pending_toplevel( + struct wl_client *client, struct wl_resource *resource, struct wl_resource *toplevel) { + struct wlr_ext_foreign_toplevel_request_pending_v1 *response = + foreign_toplevel_request_pending_v1_from_resource(resource); + response->handle = wlr_ext_foreign_toplevel_handle_v1_from_resource(toplevel); + + wl_signal_emit_mutable(&response->source->events.toplevel, response); + wl_resource_destroy(resource); +} + +static void foreign_toplevel_request_pending_cancel( + struct wl_client *client, struct wl_resource *resource) { + struct wlr_ext_foreign_toplevel_request_pending_v1 *response = + foreign_toplevel_request_pending_v1_from_resource(resource); + + wl_signal_emit_mutable(&response->source->events.cancel, response); + wl_resource_destroy(resource); +} + +struct ext_foreign_toplevel_request_pending_v1_interface foreign_toplevel_request_pending_v1_impl = { + .toplevel = foreign_toplevel_request_pending_toplevel, + .cancel = foreign_toplevel_request_pending_cancel +}; + +#include + +void wlr_ext_foreign_toplevel_request_source_v1_request( + struct wlr_ext_foreign_toplevel_request_source_v1 *source, struct wlr_ext_foreign_toplevel_request_v1 *request) { + struct wlr_ext_foreign_toplevel_request_pending_v1 *response = calloc(1, sizeof(*response)); + struct wl_resource *source_resource = wl_container_of(source->resources.next, source_resource, link); + if (!response) { + wl_client_post_no_memory(wl_resource_get_client(request->resource)); + return; + } + + response->source = source; + response->request = request; + response->resource = wl_resource_create(wl_resource_get_client(source_resource), + &ext_foreign_toplevel_request_pending_v1_interface, + ext_foreign_toplevel_request_pending_v1_interface.version, 0); + if (!response->resource) { + wl_client_post_no_memory(wl_resource_get_client(request->resource)); + free(response); + return; + } + + wl_resource_set_implementation(response->resource, &foreign_toplevel_request_pending_v1_impl, + response, foreign_toplevel_request_pending_v1_resource_destroy); + + ext_foreign_toplevel_request_source_v1_send_request(source_resource, response->resource); + + return; +}