wlroots/types/wlr_xdg_foreign_v2.c

430 lines
13 KiB
C
Raw Normal View History

#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <wlr/types/wlr_xdg_foreign_v2.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/log.h>
#include "util/signal.h"
#include "xdg-foreign-unstable-v2-protocol.h"
#define FOREIGN_V2_VERSION 1
static const struct zxdg_exported_v2_interface xdg_exported_impl;
static const struct zxdg_imported_v2_interface xdg_imported_impl;
static const struct zxdg_exporter_v2_interface xdg_exporter_impl;
static const struct zxdg_importer_v2_interface xdg_importer_impl;
static struct wlr_xdg_imported_v2 *xdg_imported_from_resource(
struct wl_resource *resource) {
assert(wl_resource_instance_of(resource, &zxdg_imported_v2_interface,
&xdg_imported_impl));
return wl_resource_get_user_data(resource);
}
static void xdg_imported_handle_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static bool verify_is_toplevel(struct wl_resource *client_resource,
struct wlr_surface *surface) {
// Note: the error codes are the same for zxdg_exporter_v2 and
// zxdg_importer_v2
if (!wlr_surface_is_xdg_surface(surface)) {
wl_resource_post_error(client_resource,
ZXDG_EXPORTER_V2_ERROR_INVALID_SURFACE,
"surface must be an xdg_surface");
return false;
}
struct wlr_xdg_surface *xdg_surface =
wlr_xdg_surface_from_wlr_surface(surface);
xdg-foreign: Fix crash on destroy of degenerate surface I am running a custom compiled version of chromium with a patch to get it up and running on sway git at the moment, and in that development build I compiled there is a bug where the browser will crash if you try to open a file select dialog. When this crash happens, chromium will not close, but instead will remain open and impossible to close unless you send a SIGKILL signal to the process. However, sway will crash to tty when you send the SIGKILL. I have a hunch that when chromium is opening the file select dialog it is creating some sort of a xdg toplevel surface. But it freezes before it fully initializes the surface. When the SIGKILL signal is given, sway/wlroots will try to free the xdg_toplevel surface but because it hasn't fully initialized due to the frozen window, it segfaults. Don't be fooled by the assert, the assert is not firing, the surface pointer is indeed NULL here. * thread #1, name = 'sway', stop reason = signal SIGSEGV: invalid address (fault address: 0x28) frame #0: 0x00007ffff78b9041 libwlroots.so.11`wlr_xdg_toplevel_set_parent(surface=0x0000000000000000, parent=0x0000000000000000) at wlr_xdg_toplevel.c:159:37 156 157 void wlr_xdg_toplevel_set_parent(struct wlr_xdg_surface *surface, 158 struct wlr_xdg_surface *parent) { -> 159 assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); 160 assert(!parent || parent->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); 161 162 if (surface->toplevel->parent) { (lldb) up error: sway {0x0003442a}: DIE has DW_AT_ranges(DW_FORM_sec_offset 0x67) attribute, but range extraction failed (invalid range list offset 0x67), please file a bug and attach the file at the start of this error message error: sway {0x0003442a}: DIE has DW_AT_ranges(DW_FORM_sec_offset 0x67) attribute, but range extraction failed (invalid range list offset 0x67), please file a bug and attach the file at the start of this error message frame #1: 0x00007ffff78e176e libwlroots.so.11`destroy_imported(imported=0x000055555626d570) at wlr_xdg_foreign_v1.c:154:3 151 wl_list_for_each_safe(child, child_tmp, &imported->children, link) { 152 struct wlr_xdg_surface *xdg_child = 153 wlr_xdg_surface_from_wlr_surface(child->surface); -> 154 wlr_xdg_toplevel_set_parent(xdg_child, NULL); 155 } 156 157 wl_list_remove(&imported->exported_destroyed.link); (lldb) up frame #2: 0x00007ffff78e1b9d libwlroots.so.11`xdg_imported_handle_resource_destroy(resource=0x00005555562555a0) at wlr_xdg_foreign_v1.c:280:2 277 struct wl_resource *resource) { 278 struct wlr_xdg_imported_v1 *imported = xdg_imported_from_resource(resource); 279 if (!imported) { -> 280 return; 281 } 282 283 destroy_imported(imported); (lldb) up frame #3: 0x00007ffff794989a libwayland-server.so.0`___lldb_unnamed_symbol211 + 154 libwayland-server.so.0`___lldb_unnamed_symbol211: -> 0x7ffff794989a <+154>: andl $0x1, %r13d 0x7ffff794989e <+158>: je 0x7ffff79498b0 ; <+176> 0x7ffff79498a0 <+160>: addq $0x8, %rsp 0x7ffff79498a4 <+164>: movl $0x1, %eax (lldb) up frame #4: 0x00007ffff794fec0 libwayland-server.so.0`___lldb_unnamed_symbol290 + 64 libwayland-server.so.0`___lldb_unnamed_symbol290: -> 0x7ffff794fec0 <+64>: cmpl $0x1, %eax 0x7ffff794fec3 <+67>: jne 0x7ffff794fed3 ; <+83> 0x7ffff794fec5 <+69>: addq $0x8, %rbx 0x7ffff794fec9 <+73>: cmpq %rbx, %r13 (lldb) up frame #5: 0x00007ffff79503e0 libwayland-server.so.0`___lldb_unnamed_symbol300 + 32 libwayland-server.so.0`___lldb_unnamed_symbol300: -> 0x7ffff79503e0 <+32>: cmpl $0x1, %eax 0x7ffff79503e3 <+35>: je 0x7ffff79503f0 ; <+48> 0x7ffff79503e5 <+37>: popq %rbx 0x7ffff79503e6 <+38>: popq %r12 (lldb) up frame #6: 0x00007ffff794a30e libwayland-server.so.0`wl_client_destroy + 126 libwayland-server.so.0`wl_client_destroy: -> 0x7ffff794a30e <+126>: movq %r12, %rdi 0x7ffff794a311 <+129>: callq 0x7ffff7950150 ; ___lldb_unnamed_symbol293 0x7ffff794a317 <+135>: movq 0x8(%rbp), %rdi 0x7ffff794a31b <+139>: callq *0xdc77(%rip) (lldb) up frame #7: 0x00007ffff794a3f7 libwayland-server.so.0`___lldb_unnamed_symbol214 + 119 libwayland-server.so.0`___lldb_unnamed_symbol214: -> 0x7ffff794a3f7 <+119>: movq 0x28(%rsp), %rax 0x7ffff794a3fc <+124>: subq %fs:0x28, %rax 0x7ffff794a405 <+133>: jne 0x7ffff794a727 ; <+935> 0x7ffff794a40b <+139>: addq $0x38, %rsp (lldb) up frame #8: 0x00007ffff794d1ca libwayland-server.so.0`wl_event_loop_dispatch + 202 libwayland-server.so.0`wl_event_loop_dispatch: -> 0x7ffff794d1ca <+202>: addq $0xc, %r15 0x7ffff794d1ce <+206>: cmpq %r15, %rbp 0x7ffff794d1d1 <+209>: jne 0x7ffff794d1b8 ; <+184> 0x7ffff794d1d3 <+211>: movq 0x8(%rsp), %rcx (lldb) up frame #9: 0x00007ffff794ad37 libwayland-server.so.0`wl_display_run + 39 libwayland-server.so.0`wl_display_run: -> 0x7ffff794ad37 <+39>: movl 0x8(%rbx), %eax 0x7ffff794ad3a <+42>: testl %eax, %eax 0x7ffff794ad3c <+44>: jne 0x7ffff794ad20 ; <+16> 0x7ffff794ad3e <+46>: popq %rbx (lldb) up frame #10: 0x000055555557689a sway`server_run(server=0x00005555555f26c0) at server.c:307:2 304 wlr_backend_destroy(server->backend); 305 return false; 306 } -> 307 308 return true; 309 } 310 (lldb) up frame #11: 0x0000555555575a93 sway`main(argc=3, argv=0x00007fffffffe978) at main.c:431:2 428 swaynag_show(&config->swaynag_config_errors); 429 } 430 -> 431 server_run(&server); 432 433 shutdown: 434 sway_log(SWAY_INFO, "Shutting down sway"); (cherry picked from commit cddc1c1bd9f796709c50f4bbb300788edd42fd4f)
2022-01-20 14:39:13 +00:00
if (xdg_surface == NULL || xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
wl_resource_post_error(client_resource,
ZXDG_EXPORTER_V2_ERROR_INVALID_SURFACE,
"surface must be an xdg_toplevel");
return false;
}
return true;
}
static void destroy_imported_child(struct wlr_xdg_imported_child_v2 *child) {
wl_list_remove(&child->xdg_toplevel_set_parent.link);
wl_list_remove(&child->xdg_surface_unmap.link);
wl_list_remove(&child->link);
free(child);
}
static void handle_child_xdg_surface_unmap(
struct wl_listener *listener, void *data) {
struct wlr_xdg_imported_child_v2 *child =
wl_container_of(listener, child, xdg_surface_unmap);
destroy_imported_child(child);
}
static void handle_xdg_toplevel_set_parent(
struct wl_listener *listener, void *data) {
struct wlr_xdg_imported_child_v2 *child =
wl_container_of(listener, child, xdg_toplevel_set_parent);
destroy_imported_child(child);
}
static void xdg_imported_handle_set_parent_of(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *child_resource) {
struct wlr_xdg_imported_v2 *imported =
xdg_imported_from_resource(resource);
if (imported == NULL) {
return;
}
struct wlr_surface *wlr_surface = imported->exported->surface;
struct wlr_surface *wlr_surface_child =
wlr_surface_from_resource(child_resource);
if (!verify_is_toplevel(resource, wlr_surface_child)) {
return;
}
struct wlr_xdg_imported_child_v2 *child;
wl_list_for_each(child, &imported->children, link) {
if (child->surface == wlr_surface_child) {
return;
}
}
child = calloc(1, sizeof(struct wlr_xdg_imported_child_v2));
if (child == NULL) {
wl_client_post_no_memory(client);
return;
}
child->surface = wlr_surface_child;
child->xdg_surface_unmap.notify = handle_child_xdg_surface_unmap;
child->xdg_toplevel_set_parent.notify = handle_xdg_toplevel_set_parent;
struct wlr_xdg_surface *surface =
wlr_xdg_surface_from_wlr_surface(wlr_surface);
struct wlr_xdg_surface *surface_child =
wlr_xdg_surface_from_wlr_surface(wlr_surface_child);
wlr_xdg_toplevel_set_parent(surface_child, surface);
wl_signal_add(&surface_child->events.unmap,
&child->xdg_surface_unmap);
wl_signal_add(&surface_child->toplevel->events.set_parent,
&child->xdg_toplevel_set_parent);
wl_list_insert(&imported->children, &child->link);
}
static const struct zxdg_imported_v2_interface xdg_imported_impl = {
.destroy = xdg_imported_handle_destroy,
.set_parent_of = xdg_imported_handle_set_parent_of
};
static struct wlr_xdg_exported_v2 *xdg_exported_from_resource(
struct wl_resource *resource) {
assert(wl_resource_instance_of(resource, &zxdg_exported_v2_interface,
&xdg_exported_impl));
return wl_resource_get_user_data(resource);
}
static void xdg_exported_handle_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static const struct zxdg_exported_v2_interface xdg_exported_impl = {
.destroy = xdg_exported_handle_destroy
};
static void xdg_exporter_handle_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static struct wlr_xdg_foreign_v2 *xdg_foreign_from_exporter_resource(
struct wl_resource *resource) {
assert(wl_resource_instance_of(resource, &zxdg_exporter_v2_interface,
&xdg_exporter_impl));
return wl_resource_get_user_data(resource);
}
static void destroy_imported(struct wlr_xdg_imported_v2 *imported) {
imported->exported = NULL;
struct wlr_xdg_imported_child_v2 *child, *child_tmp;
wl_list_for_each_safe(child, child_tmp, &imported->children, link) {
struct wlr_xdg_surface *xdg_child =
wlr_xdg_surface_from_wlr_surface(child->surface);
xdg-foreign: Fix crash on destroy of degenerate surface I am running a custom compiled version of chromium with a patch to get it up and running on sway git at the moment, and in that development build I compiled there is a bug where the browser will crash if you try to open a file select dialog. When this crash happens, chromium will not close, but instead will remain open and impossible to close unless you send a SIGKILL signal to the process. However, sway will crash to tty when you send the SIGKILL. I have a hunch that when chromium is opening the file select dialog it is creating some sort of a xdg toplevel surface. But it freezes before it fully initializes the surface. When the SIGKILL signal is given, sway/wlroots will try to free the xdg_toplevel surface but because it hasn't fully initialized due to the frozen window, it segfaults. Don't be fooled by the assert, the assert is not firing, the surface pointer is indeed NULL here. * thread #1, name = 'sway', stop reason = signal SIGSEGV: invalid address (fault address: 0x28) frame #0: 0x00007ffff78b9041 libwlroots.so.11`wlr_xdg_toplevel_set_parent(surface=0x0000000000000000, parent=0x0000000000000000) at wlr_xdg_toplevel.c:159:37 156 157 void wlr_xdg_toplevel_set_parent(struct wlr_xdg_surface *surface, 158 struct wlr_xdg_surface *parent) { -> 159 assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); 160 assert(!parent || parent->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); 161 162 if (surface->toplevel->parent) { (lldb) up error: sway {0x0003442a}: DIE has DW_AT_ranges(DW_FORM_sec_offset 0x67) attribute, but range extraction failed (invalid range list offset 0x67), please file a bug and attach the file at the start of this error message error: sway {0x0003442a}: DIE has DW_AT_ranges(DW_FORM_sec_offset 0x67) attribute, but range extraction failed (invalid range list offset 0x67), please file a bug and attach the file at the start of this error message frame #1: 0x00007ffff78e176e libwlroots.so.11`destroy_imported(imported=0x000055555626d570) at wlr_xdg_foreign_v1.c:154:3 151 wl_list_for_each_safe(child, child_tmp, &imported->children, link) { 152 struct wlr_xdg_surface *xdg_child = 153 wlr_xdg_surface_from_wlr_surface(child->surface); -> 154 wlr_xdg_toplevel_set_parent(xdg_child, NULL); 155 } 156 157 wl_list_remove(&imported->exported_destroyed.link); (lldb) up frame #2: 0x00007ffff78e1b9d libwlroots.so.11`xdg_imported_handle_resource_destroy(resource=0x00005555562555a0) at wlr_xdg_foreign_v1.c:280:2 277 struct wl_resource *resource) { 278 struct wlr_xdg_imported_v1 *imported = xdg_imported_from_resource(resource); 279 if (!imported) { -> 280 return; 281 } 282 283 destroy_imported(imported); (lldb) up frame #3: 0x00007ffff794989a libwayland-server.so.0`___lldb_unnamed_symbol211 + 154 libwayland-server.so.0`___lldb_unnamed_symbol211: -> 0x7ffff794989a <+154>: andl $0x1, %r13d 0x7ffff794989e <+158>: je 0x7ffff79498b0 ; <+176> 0x7ffff79498a0 <+160>: addq $0x8, %rsp 0x7ffff79498a4 <+164>: movl $0x1, %eax (lldb) up frame #4: 0x00007ffff794fec0 libwayland-server.so.0`___lldb_unnamed_symbol290 + 64 libwayland-server.so.0`___lldb_unnamed_symbol290: -> 0x7ffff794fec0 <+64>: cmpl $0x1, %eax 0x7ffff794fec3 <+67>: jne 0x7ffff794fed3 ; <+83> 0x7ffff794fec5 <+69>: addq $0x8, %rbx 0x7ffff794fec9 <+73>: cmpq %rbx, %r13 (lldb) up frame #5: 0x00007ffff79503e0 libwayland-server.so.0`___lldb_unnamed_symbol300 + 32 libwayland-server.so.0`___lldb_unnamed_symbol300: -> 0x7ffff79503e0 <+32>: cmpl $0x1, %eax 0x7ffff79503e3 <+35>: je 0x7ffff79503f0 ; <+48> 0x7ffff79503e5 <+37>: popq %rbx 0x7ffff79503e6 <+38>: popq %r12 (lldb) up frame #6: 0x00007ffff794a30e libwayland-server.so.0`wl_client_destroy + 126 libwayland-server.so.0`wl_client_destroy: -> 0x7ffff794a30e <+126>: movq %r12, %rdi 0x7ffff794a311 <+129>: callq 0x7ffff7950150 ; ___lldb_unnamed_symbol293 0x7ffff794a317 <+135>: movq 0x8(%rbp), %rdi 0x7ffff794a31b <+139>: callq *0xdc77(%rip) (lldb) up frame #7: 0x00007ffff794a3f7 libwayland-server.so.0`___lldb_unnamed_symbol214 + 119 libwayland-server.so.0`___lldb_unnamed_symbol214: -> 0x7ffff794a3f7 <+119>: movq 0x28(%rsp), %rax 0x7ffff794a3fc <+124>: subq %fs:0x28, %rax 0x7ffff794a405 <+133>: jne 0x7ffff794a727 ; <+935> 0x7ffff794a40b <+139>: addq $0x38, %rsp (lldb) up frame #8: 0x00007ffff794d1ca libwayland-server.so.0`wl_event_loop_dispatch + 202 libwayland-server.so.0`wl_event_loop_dispatch: -> 0x7ffff794d1ca <+202>: addq $0xc, %r15 0x7ffff794d1ce <+206>: cmpq %r15, %rbp 0x7ffff794d1d1 <+209>: jne 0x7ffff794d1b8 ; <+184> 0x7ffff794d1d3 <+211>: movq 0x8(%rsp), %rcx (lldb) up frame #9: 0x00007ffff794ad37 libwayland-server.so.0`wl_display_run + 39 libwayland-server.so.0`wl_display_run: -> 0x7ffff794ad37 <+39>: movl 0x8(%rbx), %eax 0x7ffff794ad3a <+42>: testl %eax, %eax 0x7ffff794ad3c <+44>: jne 0x7ffff794ad20 ; <+16> 0x7ffff794ad3e <+46>: popq %rbx (lldb) up frame #10: 0x000055555557689a sway`server_run(server=0x00005555555f26c0) at server.c:307:2 304 wlr_backend_destroy(server->backend); 305 return false; 306 } -> 307 308 return true; 309 } 310 (lldb) up frame #11: 0x0000555555575a93 sway`main(argc=3, argv=0x00007fffffffe978) at main.c:431:2 428 swaynag_show(&config->swaynag_config_errors); 429 } 430 -> 431 server_run(&server); 432 433 shutdown: 434 sway_log(SWAY_INFO, "Shutting down sway"); (cherry picked from commit cddc1c1bd9f796709c50f4bbb300788edd42fd4f)
2022-01-20 14:39:13 +00:00
if (xdg_child != NULL) {
wlr_xdg_toplevel_set_parent(xdg_child, NULL);
}
}
wl_list_remove(&imported->exported_destroyed.link);
wl_list_init(&imported->exported_destroyed.link);
wl_list_remove(&imported->link);
wl_list_init(&imported->link);
wl_resource_set_user_data(imported->resource, NULL);
free(imported);
}
static void destroy_exported(struct wlr_xdg_exported_v2 *exported) {
wlr_xdg_foreign_exported_finish(&exported->base);
wl_list_remove(&exported->xdg_surface_destroy.link);
wl_list_remove(&exported->link);
wl_resource_set_user_data(exported->resource, NULL);
free(exported);
}
static void xdg_exported_handle_resource_destroy(
struct wl_resource *resource) {
struct wlr_xdg_exported_v2 *exported =
xdg_exported_from_resource(resource);
if (exported) {
destroy_exported(exported);
}
}
static void handle_xdg_surface_destroy(
struct wl_listener *listener, void *data) {
struct wlr_xdg_exported_v2 *exported =
wl_container_of(listener, exported, xdg_surface_destroy);
destroy_exported(exported);
}
static void xdg_exporter_handle_export(struct wl_client *wl_client,
struct wl_resource *client_resource,
uint32_t id,
struct wl_resource *surface_resource) {
struct wlr_xdg_foreign_v2 *foreign =
xdg_foreign_from_exporter_resource(client_resource);
struct wlr_surface *surface = wlr_surface_from_resource(surface_resource);
if (!verify_is_toplevel(client_resource, surface)) {
return;
}
struct wlr_xdg_exported_v2 *exported =
calloc(1, sizeof(struct wlr_xdg_exported_v2));
if (exported == NULL) {
wl_client_post_no_memory(wl_client);
return;
}
if (!wlr_xdg_foreign_exported_init(&exported->base, foreign->registry)) {
wl_client_post_no_memory(wl_client);
free(exported);
return;
}
exported->base.surface = surface;
exported->resource = wl_resource_create(wl_client, &zxdg_exported_v2_interface,
wl_resource_get_version(client_resource), id);
if (exported->resource == NULL) {
wlr_xdg_foreign_exported_finish(&exported->base);
wl_client_post_no_memory(wl_client);
free(exported);
return;
}
wl_resource_set_implementation(exported->resource, &xdg_exported_impl,
exported, xdg_exported_handle_resource_destroy);
wl_list_insert(&foreign->exporter.objects, &exported->link);
zxdg_exported_v2_send_handle(exported->resource, exported->base.handle);
exported->xdg_surface_destroy.notify = handle_xdg_surface_destroy;
struct wlr_xdg_surface *xdg_surface =
wlr_xdg_surface_from_wlr_surface(surface);
wl_signal_add(&xdg_surface->events.unmap, &exported->xdg_surface_destroy);
}
static const struct zxdg_exporter_v2_interface xdg_exporter_impl = {
.destroy = xdg_exporter_handle_destroy,
.export_toplevel = xdg_exporter_handle_export
};
static void xdg_exporter_bind(struct wl_client *wl_client, void *data,
uint32_t version, uint32_t id) {
struct wlr_xdg_foreign_v2 *foreign = data;
struct wl_resource *exporter_resource =
wl_resource_create(wl_client, &zxdg_exporter_v2_interface, version, id);
if (exporter_resource == NULL) {
wl_client_post_no_memory(wl_client);
return;
}
wl_resource_set_implementation(exporter_resource, &xdg_exporter_impl,
foreign, NULL);
}
static struct wlr_xdg_foreign_v2 *xdg_foreign_from_importer_resource(
struct wl_resource *resource) {
assert(wl_resource_instance_of(resource, &zxdg_importer_v2_interface,
&xdg_importer_impl));
return wl_resource_get_user_data(resource);
}
static void xdg_importer_handle_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static void xdg_imported_handle_resource_destroy(
struct wl_resource *resource) {
struct wlr_xdg_imported_v2 *imported = xdg_imported_from_resource(resource);
if (!imported) {
return;
}
destroy_imported(imported);
}
static void xdg_imported_handle_exported_destroy(struct wl_listener *listener,
void *data) {
struct wlr_xdg_imported_v2 *imported =
wl_container_of(listener, imported, exported_destroyed);
zxdg_imported_v2_send_destroyed(imported->resource);
destroy_imported(imported);
}
static void xdg_importer_handle_import(struct wl_client *wl_client,
struct wl_resource *client_resource,
uint32_t id,
const char *handle) {
struct wlr_xdg_foreign_v2 *foreign =
xdg_foreign_from_importer_resource(client_resource);
struct wlr_xdg_imported_v2 *imported =
calloc(1, sizeof(struct wlr_xdg_imported_v2));
if (imported == NULL) {
wl_client_post_no_memory(wl_client);
return;
}
imported->exported = wlr_xdg_foreign_registry_find_by_handle(
foreign->registry, handle);
imported->resource = wl_resource_create(wl_client, &zxdg_imported_v2_interface,
wl_resource_get_version(client_resource), id);
if (imported->resource == NULL) {
wl_client_post_no_memory(wl_client);
free(imported);
return;
}
wl_resource_set_implementation(imported->resource, &xdg_imported_impl,
imported, xdg_imported_handle_resource_destroy);
if (imported->exported == NULL) {
wl_resource_set_user_data(imported->resource, NULL);
zxdg_imported_v2_send_destroyed(imported->resource);
free(imported);
return;
}
wl_list_init(&imported->children);
wl_list_insert(&foreign->importer.objects, &imported->link);
imported->exported_destroyed.notify = xdg_imported_handle_exported_destroy;
wl_signal_add(&imported->exported->events.destroy, &imported->exported_destroyed);
}
static const struct zxdg_importer_v2_interface xdg_importer_impl = {
.destroy = xdg_importer_handle_destroy,
.import_toplevel = xdg_importer_handle_import
};
static void xdg_importer_bind(struct wl_client *wl_client, void *data,
uint32_t version, uint32_t id) {
struct wlr_xdg_foreign_v2 *foreign = data;
struct wl_resource *importer_resource =
wl_resource_create(wl_client, &zxdg_importer_v2_interface, version, id);
if (importer_resource == NULL) {
wl_client_post_no_memory(wl_client);
return;
}
wl_resource_set_implementation(importer_resource, &xdg_importer_impl,
foreign, NULL);
}
static void xdg_foreign_destroy(struct wlr_xdg_foreign_v2 *foreign) {
if (!foreign) {
return;
}
wlr_signal_emit_safe(&foreign->events.destroy, NULL);
wl_list_remove(&foreign->foreign_registry_destroy.link);
wl_list_remove(&foreign->display_destroy.link);
wl_global_destroy(foreign->exporter.global);
wl_global_destroy(foreign->importer.global);
free(foreign);
}
static void handle_display_destroy(struct wl_listener *listener, void *data) {
struct wlr_xdg_foreign_v2 *foreign =
wl_container_of(listener, foreign, display_destroy);
xdg_foreign_destroy(foreign);
}
static void handle_foreign_registry_destroy(struct wl_listener *listener,
void *data) {
struct wlr_xdg_foreign_v2 *foreign =
wl_container_of(listener, foreign, foreign_registry_destroy);
xdg_foreign_destroy(foreign);
}
struct wlr_xdg_foreign_v2 *wlr_xdg_foreign_v2_create(
struct wl_display *display, struct wlr_xdg_foreign_registry *registry) {
struct wlr_xdg_foreign_v2 *foreign = calloc(1,
sizeof(struct wlr_xdg_foreign_v2));
if (!foreign) {
return NULL;
}
foreign->exporter.global = wl_global_create(display,
&zxdg_exporter_v2_interface,
FOREIGN_V2_VERSION, foreign,
xdg_exporter_bind);
if (!foreign->exporter.global) {
free(foreign);
return NULL;
}
foreign->importer.global = wl_global_create(display,
&zxdg_importer_v2_interface,
FOREIGN_V2_VERSION, foreign,
xdg_importer_bind);
if (!foreign->importer.global) {
wl_global_destroy(foreign->exporter.global);
free(foreign);
return NULL;
}
foreign->registry = registry;
wl_signal_init(&foreign->events.destroy);
wl_list_init(&foreign->exporter.objects);
wl_list_init(&foreign->importer.objects);
foreign->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(display, &foreign->display_destroy);
foreign->foreign_registry_destroy.notify = handle_foreign_registry_destroy;
wl_signal_add(&registry->events.destroy, &foreign->foreign_registry_destroy);
return foreign;
}