From 205dcfe62b9efa6d34364312bd7ce6cac45ef6f4 Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Mon, 4 Jul 2022 19:12:59 +0900 Subject: [PATCH] implement the xdg_shell_v6 It provides the wayland server implementation for zxdg_shell_v6_unstable protocol. Change-Id: I154bba10ba3cd9b7a35f93ac309808a172ee48df --- include/libds/xdg_shell_v6.h | 58 +++++ packaging/libds.spec | 30 +++ src/meson.build | 2 + src/xdg_shell_v6/meson.build | 31 +++ src/xdg_shell_v6/xdg_shell_v6.c | 217 ++++++++++++++++ src/xdg_shell_v6/xdg_shell_v6.h | 189 ++++++++++++++ src/xdg_shell_v6/xdg_surface_v6.c | 493 +++++++++++++++++++++++++++++++++++++ src/xdg_shell_v6/xdg_toplevel_v6.c | 329 +++++++++++++++++++++++++ 8 files changed, 1349 insertions(+) create mode 100644 include/libds/xdg_shell_v6.h create mode 100644 src/xdg_shell_v6/meson.build create mode 100644 src/xdg_shell_v6/xdg_shell_v6.c create mode 100644 src/xdg_shell_v6/xdg_shell_v6.h create mode 100644 src/xdg_shell_v6/xdg_surface_v6.c create mode 100644 src/xdg_shell_v6/xdg_toplevel_v6.c diff --git a/include/libds/xdg_shell_v6.h b/include/libds/xdg_shell_v6.h new file mode 100644 index 0000000..40c9c5a --- /dev/null +++ b/include/libds/xdg_shell_v6.h @@ -0,0 +1,58 @@ +#ifndef LIBDS_XDG_SHELL_V6_H +#define LIBDS_XDG_SHELL_V6_H + +#include +#include + +#include "surface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct ds_xdg_shell_v6; + +struct ds_xdg_surface_v6; + +struct ds_xdg_shell_v6 * +ds_xdg_shell_v6_create(struct wl_display *display); + +void +ds_xdg_shell_v6_add_destroy_listener(struct ds_xdg_shell_v6 *shell, + struct wl_listener *listener); + +void +ds_xdg_shell_v6_add_new_surface_listener(struct ds_xdg_shell_v6 *shell, + struct wl_listener *listener); + +void +ds_xdg_surface_v6_add_destroy_listener(struct ds_xdg_surface_v6 *surface, + struct wl_listener *listener); + +void +ds_xdg_surface_v6_add_map_listener(struct ds_xdg_surface_v6 *surface, + struct wl_listener *listener); + +void +ds_xdg_surface_v6_add_unmap_listener(struct ds_xdg_surface_v6 *surface, + struct wl_listener *listener); + +void +ds_xdg_surface_v6_ping(struct ds_xdg_surface_v6 *surface); + +struct ds_surface * +ds_xdg_surface_v6_get_surface(struct ds_xdg_surface_v6 *surface); + +uint32_t +ds_xdg_toplevel_set_size(struct ds_xdg_surface_v6 *surface, + uint32_t width, uint32_t height); + +uint32_t +ds_xdg_toplevel_set_activated(struct ds_xdg_surface_v6 *surface, + bool activated); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/packaging/libds.spec b/packaging/libds.spec index 1a7951e..13d59cf 100644 --- a/packaging/libds.spec +++ b/packaging/libds.spec @@ -11,6 +11,7 @@ BuildRequires: meson BuildRequires: pkgconfig(wayland-server) BuildRequires: pkgconfig(wayland-client) BuildRequires: pkgconfig(wayland-protocols) +BuildRequires: pkgconfig(xdg-shell-unstable-v6-server) BuildRequires: pkgconfig(pixman-1) BuildRequires: pkgconfig(libdrm) BuildRequires: pkgconfig(xkbcommon) @@ -28,6 +29,21 @@ Requires: %{name} = %{version}-%{release} %description devel Development package of Wayland Compositor Library +## libds-xdg-shell-v6 +%package xdg-shell-v6 +Summary: Wayland Compositor Library for xdg-shell-v6 +Group: Development/Libraries + +%description xdg-shell-v6 +Wayland Compositor Library for xdg-shell-v6 + +%package xdg-shell-v6-devel +Summary: Wayland Compositor Development package for xdg-shell-v6 +Group: Development/Libraries + +%description xdg-shell-v6-devel +Wayland Compositor Development package for xdg-shell-v6 + %prep %setup -q cp %{SOURCE1001} . @@ -62,3 +78,17 @@ ninja -C builddir install %{_bindir}/input-device-test %{_bindir}/libinput-backend %{_bindir}/ds-simple-shm-shell + +%files xdg-shell-v6 +%manifest %{name}.manifest +%defattr(-,root,root,-) +%license LICENSE +%{_libdir}/libds-xdg-shell-v6.so.* + +%files xdg-shell-v6-devel +%manifest %{name}.manifest +%defattr(-,root,root,-) +%license LICENSE +%{_includedir}/libds/xdg_shell_v6.h +%{_libdir}/pkgconfig/libds-xdg-shell-v6.pc +%{_libdir}/libds-xdg-shell-v6.so diff --git a/src/meson.build b/src/meson.build index b59fda2..7cde232 100644 --- a/src/meson.build +++ b/src/meson.build @@ -99,3 +99,5 @@ pkgconfig.generate(lib_libds, name: meson.project_name(), description: 'Wayland compositor library', ) + +subdir('xdg_shell_v6') diff --git a/src/xdg_shell_v6/meson.build b/src/xdg_shell_v6/meson.build new file mode 100644 index 0000000..55c9b85 --- /dev/null +++ b/src/xdg_shell_v6/meson.build @@ -0,0 +1,31 @@ +libds_xdg_shell_v6_files = [ + 'xdg_shell_v6.c', + 'xdg_surface_v6.c', + 'xdg_toplevel_v6.c', +] + +libds_xdg_shell_v6_deps = [ + dep_libds, + dependency('xdg-shell-unstable-v6-server', required: true), +] + +lib_libds_xdg_shell_v6 = shared_library('ds-xdg-shell-v6', libds_xdg_shell_v6_files, + dependencies: libds_xdg_shell_v6_deps, + include_directories: [ common_inc, include_directories('.'), include_directories('..') ], + version: meson.project_version(), + install: true +) + +deps_libds_xdg_shell_v6 = declare_dependency( + link_with: lib_libds_xdg_shell_v6, + dependencies: libds_xdg_shell_v6_deps, + include_directories: [ common_inc, include_directories('.') ], +) + +pkgconfig = import('pkgconfig') +pkgconfig.generate(lib_libds_xdg_shell_v6, + version: meson.project_version(), + filebase: 'libds-xdg-shell-v6', + name: 'libds-xdg-shell-v6', + description: 'wayland server implementation of xdg-shel-v6 unstable protocol', +) diff --git a/src/xdg_shell_v6/xdg_shell_v6.c b/src/xdg_shell_v6/xdg_shell_v6.c new file mode 100644 index 0000000..7702d73 --- /dev/null +++ b/src/xdg_shell_v6/xdg_shell_v6.c @@ -0,0 +1,217 @@ +#include +#include +#include + +#include "libds/log.h" +#include "libds/xdg_shell_v6.h" + +#include "xdg_shell_v6.h" + +#define XDG_SHELL_V6_BASE_VERSION 1 +#define XDG_SHELL_V6_PING_TIMEOUT 10000 + +static void xdg_shell_v6_handle_display_destroy(struct wl_listener *listener, + void *data); +static void xdg_shell_v6_bind(struct wl_client *wl_client, void *data, + uint32_t verison, uint32_t id); + +WL_EXPORT struct ds_xdg_shell_v6 * +ds_xdg_shell_v6_create(struct wl_display *display) +{ + struct ds_xdg_shell_v6 *shell; + + shell = calloc(1, sizeof *shell); + if (!shell) { + return NULL; + } + + shell->ping_timeout = XDG_SHELL_V6_PING_TIMEOUT; + + wl_list_init(&shell->clients); + + shell->global = wl_global_create(display, &zxdg_shell_v6_interface, + XDG_SHELL_V6_BASE_VERSION, shell, xdg_shell_v6_bind); + if (!shell->global) { + free(shell); + return NULL; + } + + wl_signal_init(&shell->events.destroy); + wl_signal_init(&shell->events.new_surface); + + shell->display_destroy.notify = xdg_shell_v6_handle_display_destroy; + wl_display_add_destroy_listener(display, &shell->display_destroy); + + ds_inf("Global created: xdg_shell_v6(%p)", shell); + + return shell; +} + +WL_EXPORT void +ds_xdg_shell_v6_add_destroy_listener(struct ds_xdg_shell_v6 *shell, + struct wl_listener *listener) +{ + wl_signal_add(&shell->events.destroy, listener); +} + +void +ds_xdg_shell_v6_add_new_surface_listener(struct ds_xdg_shell_v6 *shell, + struct wl_listener *listener) +{ + wl_signal_add(&shell->events.new_surface, listener); +} + +static void +xdg_shell_v6_handle_display_destroy(struct wl_listener *listener, void *data) +{ + struct ds_xdg_shell_v6 *shell; + + shell = wl_container_of(listener, shell, display_destroy); + + ds_inf("Global destroy: xdg_shell_v6(%p)", shell); + + wl_signal_emit(&shell->events.destroy, shell); + wl_list_remove(&shell->display_destroy.link); + wl_global_destroy(shell->global); + free(shell); +} + +static void +xdg_shell_v6_handle_destroy(struct wl_client *wl_client, + struct wl_resource *resource) +{ + struct ds_xdg_client_v6 *client; + + client = wl_resource_get_user_data(resource); + + if (!wl_list_empty(&client->surfaces)) { + wl_resource_post_error(client->resource, + ZXDG_SHELL_V6_ERROR_DEFUNCT_SURFACES, + "xdg_shell_v6 was destroyed before children"); + return; + } + + wl_resource_destroy(resource); +} + +static void +xdg_shell_v6_handle_create_positioner(struct wl_client *wl_client, + struct wl_resource *resource, uint32_t id) +{ + // TODO +} + +static void +xdg_shell_v6_handle_get_xdg_surface_v6(struct wl_client *wl_client, + struct wl_resource *resource, uint32_t id, + struct wl_resource *surface_resource) +{ + struct ds_xdg_client_v6 *client; + struct ds_surface *surface; + + client = wl_resource_get_user_data(resource); + surface = ds_surface_from_resource(surface_resource); + create_xdg_surface_v6(client, surface, id); +} + +static void +xdg_shell_v6_handle_pong(struct wl_client *wl_client, + struct wl_resource *resource, uint32_t serial) +{ + struct ds_xdg_client_v6 *client; + + client = wl_resource_get_user_data(resource); + if (client->ping_serial != serial) + return; + + wl_event_source_timer_update(client->ping_timer, 0); + client->ping_serial = 0; +} + +static const struct zxdg_shell_v6_interface xdg_shell_v6_impl = +{ + .destroy = xdg_shell_v6_handle_destroy, + .create_positioner = xdg_shell_v6_handle_create_positioner, + .get_xdg_surface = xdg_shell_v6_handle_get_xdg_surface_v6, + .pong = xdg_shell_v6_handle_pong, +}; + +static void +xdg_client_v6_handle_resource_destroy(struct wl_resource *resource) +{ + struct ds_xdg_client_v6 *client; + struct ds_xdg_surface_v6 *surface, *tmp; + + client = wl_resource_get_user_data(resource); + + wl_list_for_each_safe(surface, tmp, &client->surfaces, link) + destroy_xdg_surface_v6(surface); + + if (client->ping_timer != NULL) + wl_event_source_remove(client->ping_timer); + + wl_list_remove(&client->link); + free(client); +} + +static int +xdg_client_v6_handle_ping_timeout(void *user_data) +{ + struct ds_xdg_client_v6 *client = user_data; + struct ds_xdg_surface_v6 *surface; + + wl_list_for_each(surface, &client->surfaces, link) + wl_signal_emit(&surface->events.ping_timeout, surface); + + client->ping_serial = 0; + + return 1; +} + +static void +xdg_client_v6_init_ping_timer(struct ds_xdg_client_v6 *client) +{ + struct wl_display *display; + struct wl_event_loop *loop; + + display = wl_client_get_display(client->wl_client); + loop = wl_display_get_event_loop(display); + client->ping_timer = wl_event_loop_add_timer(loop, + xdg_client_v6_handle_ping_timeout, client); + if (client->ping_timer == NULL) + wl_client_post_no_memory(client->wl_client); +} + +static void +xdg_shell_v6_bind(struct wl_client *wl_client, void *data, uint32_t version, + uint32_t id) +{ + struct ds_xdg_shell_v6 *shell = data; + struct ds_xdg_client_v6 *client; + + client = calloc(1, sizeof *client); + if (client == NULL) { + wl_client_post_no_memory(wl_client); + return; + } + + client->wl_client = wl_client; + client->shell = shell; + + wl_list_init(&client->surfaces); + + client->resource = + wl_resource_create(wl_client, &zxdg_shell_v6_interface, version, id); + if (client->resource == NULL) { + free(client); + wl_client_post_no_memory(wl_client); + return; + } + + wl_resource_set_implementation(client->resource, &xdg_shell_v6_impl, client, + xdg_client_v6_handle_resource_destroy); + + wl_list_insert(&shell->clients, &client->link); + + xdg_client_v6_init_ping_timer(client); +} diff --git a/src/xdg_shell_v6/xdg_shell_v6.h b/src/xdg_shell_v6/xdg_shell_v6.h new file mode 100644 index 0000000..ee8de5e --- /dev/null +++ b/src/xdg_shell_v6/xdg_shell_v6.h @@ -0,0 +1,189 @@ +#ifndef DS_XDG_SHELL_V6_H +#define DS_XDG_SHELL_V6_H + +#include +#include + +#include "libds/output.h" + +#include "surface.h" + +enum ds_xdg_surface_v6_role +{ + DS_XDG_SURFACE_V6_ROLE_NONE, + DS_XDG_SURFACE_V6_ROLE_TOPLEVEL, + DS_XDG_SURFACE_V6_ROLE_POPUP, +}; + +struct ds_xdg_shell_v6 +{ + struct wl_global *global; + + struct wl_list clients; + + struct wl_listener display_destroy; + + struct { + struct wl_signal destroy; + struct wl_signal new_surface; + } events; + + uint32_t ping_timeout; +}; + +struct ds_xdg_client_v6 +{ + struct ds_xdg_shell_v6 *shell; + + struct wl_resource *resource; + struct wl_client *wl_client; + struct wl_event_source *ping_timer; + + struct wl_list surfaces; + + struct wl_list link; // ds_xdg_shell_v6::clients + + uint32_t ping_serial; +}; + +struct ds_xdg_toplevle_v6_state +{ + bool maximized, fullscreen, resizing, activated; + uint32_t tiled; + uint32_t width, height; + uint32_t max_width, max_height; + uint32_t min_width, min_height; +}; + +struct ds_xdg_toplevle_v6_configure +{ + bool maximized, fullscreen, resizing, activated; + uint32_t tiled; + uint32_t width, height; +}; + +struct ds_xdg_toplevel_v6_requested +{ + bool maximized, minimized, fullscreen; + struct ds_output *fullscreen_output; + struct wl_listener fullscreen_output_destroy; +}; + +struct ds_xdg_toplevle_v6 +{ + struct wl_resource *resource; + struct ds_xdg_surface_v6 *base; + bool added; + + struct ds_xdg_surface_v6 *parent; + struct wl_listener parent_unmap; + + struct ds_xdg_toplevle_v6_state current, pending; + struct ds_xdg_toplevle_v6_configure scheduled; + struct ds_xdg_toplevel_v6_requested requested; + + char *title; + char *app_id; + + struct { + struct wl_signal request_maximize; + struct wl_signal request_fullscreen; + struct wl_signal request_minimize; + struct wl_signal request_move; + struct wl_signal request_resize; + struct wl_signal request_show_window_menu; + struct wl_signal set_parent; + struct wl_signal set_title; + struct wl_signal set_app_id; + } events; +}; + +struct ds_xdg_popup +{ + +}; + +struct ds_xdg_surface_v6_state +{ + uint32_t configure_serial; + struct { + int x, y; + int width, height; + } geometry; +}; + +struct ds_xdg_surface_v6 +{ + struct ds_xdg_client_v6 *client; + struct ds_surface *ds_surface; + + enum ds_xdg_surface_v6_role role; + + union { + struct ds_xdg_toplevle_v6 *toplevel; + struct ds_xdg_popup *popup; + }; + + struct wl_resource *resource; + + struct wl_event_source *configure_idle; + uint32_t scheduled_serial; + struct wl_list configure_list; + + struct ds_xdg_surface_v6_state current, pending; + + struct wl_list link; // ds_xdg_client_v6::surfaces + + struct { + struct wl_listener surface_destroy; + struct wl_listener surface_commit; + } listener; + + struct { + struct wl_signal destroy; + struct wl_signal ping_timeout; + struct wl_signal new_popup; + struct wl_signal map; + struct wl_signal unmap; + struct wl_signal configure; + struct wl_signal ack_configure; + } events; + + bool added, configured, mapped; +}; + +struct ds_xdg_surface_v6_configure +{ + struct ds_xdg_surface_v6 *surface; + struct wl_list link; + uint32_t serial; + + struct ds_xdg_toplevle_v6_configure *toplevel_configure; +}; + +uint32_t +ds_xdg_surface_v6_schedule_configure(struct ds_xdg_surface_v6 *surface); + +struct ds_xdg_surface_v6 * +create_xdg_surface_v6(struct ds_xdg_client_v6 *client, struct ds_surface *surface, + uint32_t id); + +void destroy_xdg_surface_v6(struct ds_xdg_surface_v6 *surface); + +void +reset_xdg_surface_v6(struct ds_xdg_surface_v6 *surface); + +void +create_xdg_toplevel_v6(struct ds_xdg_surface_v6 *surface, uint32_t id); + +void +handle_xdg_surface_v6_commit(struct ds_surface *ds_surface); + +void +handle_xdg_surface_v6_toplevel_committed(struct ds_xdg_surface_v6 *surface); + +void +send_xdg_toplevel_v6_configure(struct ds_xdg_surface_v6 *surface, + struct ds_xdg_surface_v6_configure *configure); + +#endif diff --git a/src/xdg_shell_v6/xdg_surface_v6.c b/src/xdg_shell_v6/xdg_surface_v6.c new file mode 100644 index 0000000..69ab595 --- /dev/null +++ b/src/xdg_shell_v6/xdg_surface_v6.c @@ -0,0 +1,493 @@ +#include +#include + +#include "libds/log.h" + +#include "xdg_shell_v6.h" + +static const struct zxdg_surface_v6_interface xdg_surface_v6_impl; + +static void xdg_surface_v6_handle_surface_destroy(struct wl_listener *listener, + void *data); +static void xdg_surface_v6_handle_surface_commit(struct wl_listener *listener, + void *data); +static void xdg_surface_v6_handle_resource_destroy(struct wl_resource *resource); +static void xdg_surface_v6_configure_destroy(struct ds_xdg_surface_v6_configure *configure); +static void surface_send_configure(void *user_data); + +void +ds_xdg_surface_v6_add_destroy_listener(struct ds_xdg_surface_v6 *surface, + struct wl_listener *listener) +{ + wl_signal_add(&surface->events.destroy, listener); +} + +WL_EXPORT void +ds_xdg_surface_v6_add_map_listener(struct ds_xdg_surface_v6 *surface, + struct wl_listener *listener) +{ + wl_signal_add(&surface->events.map, listener); +} + +WL_EXPORT void +ds_xdg_surface_v6_add_unmap_listener(struct ds_xdg_surface_v6 *surface, + struct wl_listener *listener) +{ + wl_signal_add(&surface->events.unmap, listener); +} + +struct ds_surface * +ds_xdg_surface_v6_get_surface(struct ds_xdg_surface_v6 *surface) +{ + return surface->ds_surface; +} + +struct ds_xdg_surface_v6 * +create_xdg_surface_v6(struct ds_xdg_client_v6 *client, struct ds_surface *ds_surface, + uint32_t id) +{ + struct ds_xdg_surface_v6 *surface; + + surface = calloc(1, sizeof *surface); + if (!surface) { + wl_client_post_no_memory(client->wl_client); + return NULL; + } + + surface->client = client; + surface->role = DS_XDG_SURFACE_V6_ROLE_NONE; + surface->ds_surface = ds_surface; + surface->resource = wl_resource_create(client->wl_client, + &zxdg_surface_v6_interface, wl_resource_get_version(client->resource), + id); + if (!surface->resource) { + free(surface); + wl_client_post_no_memory(client->wl_client); + return NULL; + } + + if (ds_surface_has_buffer(surface->ds_surface)) { + wl_resource_destroy(surface->resource); + free(surface); + wl_resource_post_error(client->resource, + ZXDG_SURFACE_V6_ERROR_UNCONFIGURED_BUFFER, + "xdg_surface_v6 must not have a buffer at creation"); + return NULL; + } + + wl_list_init(&surface->configure_list); + + wl_signal_init(&surface->events.destroy); + wl_signal_init(&surface->events.ping_timeout); + wl_signal_init(&surface->events.new_popup); + wl_signal_init(&surface->events.map); + wl_signal_init(&surface->events.unmap); + wl_signal_init(&surface->events.configure); + wl_signal_init(&surface->events.ack_configure); + + surface->listener.surface_destroy.notify = + xdg_surface_v6_handle_surface_destroy; + ds_surface_add_destroy_listener(ds_surface, + &surface->listener.surface_destroy); + + surface->listener.surface_commit.notify = + xdg_surface_v6_handle_surface_commit; + ds_surface_add_commit_listener(ds_surface, + &surface->listener.surface_commit); + + wl_resource_set_implementation(surface->resource, &xdg_surface_v6_impl, + surface, xdg_surface_v6_handle_resource_destroy); + + wl_list_insert(&client->surfaces, &surface->link); + + ds_inf("New xdg_surface_v6 %p (res %p)", surface, surface->resource); + + return surface; +} + +void +unmap_xdg_surface_v6(struct ds_xdg_surface_v6 *surface) +{ + struct ds_xdg_surface_v6_configure *configure, *tmp; + + // TODO handle popup + + if (surface->mapped) + wl_signal_emit(&surface->events.unmap, surface); + + switch (surface->role) { + case DS_XDG_SURFACE_V6_ROLE_TOPLEVEL: + if (surface->toplevel->parent) { + wl_list_remove(&surface->toplevel->parent_unmap.link); + surface->toplevel->parent = NULL; + } + free(surface->toplevel->title); + surface->toplevel->title = NULL; + free(surface->toplevel->app_id); + surface->toplevel->app_id = NULL; + break; + case DS_XDG_SURFACE_V6_ROLE_POPUP: + // TODO + break; + case DS_XDG_SURFACE_V6_ROLE_NONE: + assert(false && "not reached"); + } + + wl_list_for_each_safe(configure, tmp, &surface->configure_list, link) + xdg_surface_v6_configure_destroy(configure); + + if (surface->configure_idle) { + wl_event_source_remove(surface->configure_idle); + surface->configure_idle = NULL; + } + + surface->configured = false; + surface->mapped = false; +} + +void +reset_xdg_surface_v6(struct ds_xdg_surface_v6 *surface) +{ + struct ds_xdg_toplevel_v6_requested *req; + + if (surface->role != DS_XDG_SURFACE_V6_ROLE_NONE) + unmap_xdg_surface_v6(surface); + + if (surface->added) { + wl_signal_emit(&surface->events.destroy, surface); + surface->added = false; + } + + switch (surface->role) { + case DS_XDG_SURFACE_V6_ROLE_TOPLEVEL: + wl_resource_set_user_data(surface->toplevel->resource, NULL); + surface->toplevel->resource = NULL; + req = &surface->toplevel->requested; + if (req->fullscreen_output) + wl_list_remove(&req->fullscreen_output_destroy.link); + free(surface->toplevel); + surface->toplevel = NULL; + break; + case DS_XDG_SURFACE_V6_ROLE_POPUP: + // TODO + break; + case DS_XDG_SURFACE_V6_ROLE_NONE: + // This space is intentionally left blank + break; + } + + surface->role = DS_XDG_SURFACE_V6_ROLE_NONE; +} + +void +destroy_xdg_surface_v6(struct ds_xdg_surface_v6 *surface) +{ + reset_xdg_surface_v6(surface); + + wl_resource_set_user_data(surface->resource, NULL); + + ds_surface_reset_role_data(surface->ds_surface); + + wl_list_remove(&surface->link); + wl_list_remove(&surface->listener.surface_destroy.link); + wl_list_remove(&surface->listener.surface_commit.link); + + free(surface); +} + +void +handle_xdg_surface_v6_commit(struct ds_surface *ds_surface) +{ + struct ds_xdg_surface_v6 *surface; + + surface = ds_surface_get_role_data(ds_surface); + surface->current = surface->pending; + + switch (surface->role) { + case DS_XDG_SURFACE_V6_ROLE_NONE: + // inert toplevel or popup + break; + case DS_XDG_SURFACE_V6_ROLE_TOPLEVEL: + handle_xdg_surface_v6_toplevel_committed(surface); + // TODO + break; + case DS_XDG_SURFACE_V6_ROLE_POPUP: + // TODO + break; + } + + if (!surface->added) { + surface->added = true; + wl_signal_emit(&surface->client->shell->events.new_surface, surface); + } + + if (surface->configured && + ds_surface_has_buffer(surface->ds_surface) && + !surface->mapped) { + surface->mapped = true; + wl_signal_emit(&surface->events.map, surface); + } +} + +void handle_xdg_surface_v6_precommit(struct ds_surface *ds_surface) +{ + struct ds_xdg_surface_v6 *surface; + + surface = ds_surface_get_role_data(ds_surface); + + // TODO + (void)surface; +} + +uint32_t +ds_xdg_surface_v6_schedule_configure(struct ds_xdg_surface_v6 *surface) +{ + struct wl_display *display; + struct wl_event_loop *loop; + + display = wl_client_get_display(surface->client->wl_client); + loop = wl_display_get_event_loop(display); + + if (!surface->configure_idle) { + surface->scheduled_serial = wl_display_next_serial(display); + surface->configure_idle = wl_event_loop_add_idle(loop, + surface_send_configure, surface); + if (!surface->configure_idle) + wl_client_post_no_memory(surface->client->wl_client); + } + + return surface->scheduled_serial; +} + +void +ds_xdg_surface_v6_ping(struct ds_xdg_surface_v6 *surface) +{ + struct wl_display *display; + + display = wl_client_get_display(surface->client->wl_client); + + surface->client->ping_serial = wl_display_next_serial(display); + zxdg_shell_v6_send_ping(surface->resource, surface->client->ping_serial); +} + +static void +xdg_surface_v6_handle_surface_destroy(struct wl_listener *listener, void *data) +{ + struct ds_xdg_surface_v6 *surface; + + surface = wl_container_of(listener, surface, listener.surface_destroy); + destroy_xdg_surface_v6(surface); +} + +static void +xdg_surface_v6_handle_surface_commit(struct wl_listener *listener, void *data) +{ + struct ds_xdg_surface_v6 *surface; + + surface = wl_container_of(listener, surface, listener.surface_commit); + + if (ds_surface_has_buffer(surface->ds_surface) && + !surface->configured) { + wl_resource_post_error(surface->resource, + ZXDG_SURFACE_V6_ERROR_UNCONFIGURED_BUFFER, + "xdg_surface_v6 has never been configured"); + return; + } + + if (!ds_surface_get_role(surface->ds_surface)) { + wl_resource_post_error(surface->resource, + ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED, + "xdg_surface_v6 must have a role"); + return; + } +} + +static void +xdg_surface_v6_handle_resource_destroy(struct wl_resource *resource) +{ + struct ds_xdg_surface_v6 *surface; + + surface = wl_resource_get_user_data(resource); + if (!surface) + return; + + destroy_xdg_surface_v6(surface); +} + +static void +xdg_surface_v6_configure_destroy(struct ds_xdg_surface_v6_configure *configure) +{ + wl_list_remove(&configure->link); + free(configure->toplevel_configure); + free(configure); +} + +static void +xdg_surface_v6_handle_destroy(struct wl_client *client, + struct wl_resource *resource) +{ + struct ds_xdg_surface_v6 *surface; + + surface = wl_resource_get_user_data(resource); + + if (surface->role != DS_XDG_SURFACE_V6_ROLE_NONE) { + ds_err("Tried to destroy an xdg_surface_v6 before its role object"); + return; + } + + wl_resource_destroy(resource); +} + +static void +xdg_surface_v6_handle_get_toplevel(struct wl_client *client, + struct wl_resource *resource, uint32_t id) +{ + struct ds_xdg_surface_v6 *surface; + + surface = wl_resource_get_user_data(resource); + create_xdg_toplevel_v6(surface, id); +} + +static void +xdg_surface_v6_handle_get_popup(struct wl_client *client, + struct wl_resource *resource, uint32_t id, + struct wl_resource *parent_resource, + struct wl_resource *positioner_resource) +{ + struct ds_xdg_surface_v6 *surface; + + surface = wl_resource_get_user_data(resource); + + // TODO + (void)surface; +} + +static void +xdg_surface_v6_handle_ack_configure(struct wl_client *client, + struct wl_resource *resource, uint32_t serial) +{ + struct ds_xdg_surface_v6 *surface; + + surface = wl_resource_get_user_data(resource); + + if (surface->role == DS_XDG_SURFACE_V6_ROLE_NONE) { + wl_resource_post_error(surface->resource, + ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED, + "xdg_surface_v6 must have a role"); + return; + } + + bool found = false; + struct ds_xdg_surface_v6_configure *configure, *tmp; + wl_list_for_each(configure, &surface->configure_list, link) { + if (configure->serial == serial) { + found = true; + break; + } + } + + if (!found) { + wl_resource_post_error(surface->client->resource, + ZXDG_SHELL_V6_ERROR_INVALID_SURFACE_STATE, + "wrong configure serial: %u", serial); + return; + } + + wl_list_for_each_safe(configure, tmp, &surface->configure_list, link) { + if (configure->serial == serial) + break; + + wl_signal_emit(&surface->events.ack_configure, configure); + xdg_surface_v6_configure_destroy(configure); + } + + switch (surface->role) { + case DS_XDG_SURFACE_V6_ROLE_NONE: + assert(0 && "not reached"); + break; + case DS_XDG_SURFACE_V6_ROLE_TOPLEVEL: + // TODO + break; + case DS_XDG_SURFACE_V6_ROLE_POPUP: + break; + } + + surface->configured = true; + surface->pending.configure_serial = serial; + + wl_signal_emit(&surface->events.ack_configure, configure); + xdg_surface_v6_configure_destroy(configure); +} + +static void +xdg_surface_v6_handle_set_window_geometry(struct wl_client *client, + struct wl_resource *resource, + int32_t x, int32_t y, int32_t width, int32_t height) +{ + struct ds_xdg_surface_v6 *surface; + + surface = wl_resource_get_user_data(resource); + + if (surface->role == DS_XDG_SURFACE_V6_ROLE_NONE) { + wl_resource_post_error(surface->resource, + ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED, + "xdg_surface_v6 must have a role"); + return; + } + + if (width <= 0 || height <= 0) { + ds_err("Client tried to set invalid geometry"); + wl_resource_post_error(resource, -1, + "Tried to set invalid xdg_surface_v6 geometry"); + return; + } + + surface->pending.geometry.x = x; + surface->pending.geometry.y = y; + surface->pending.geometry.width = width; + surface->pending.geometry.height = height; +} + +static const struct zxdg_surface_v6_interface xdg_surface_v6_impl = +{ + .destroy = xdg_surface_v6_handle_destroy, + .get_toplevel = xdg_surface_v6_handle_get_toplevel, + .get_popup = xdg_surface_v6_handle_get_popup, + .ack_configure = xdg_surface_v6_handle_ack_configure, + .set_window_geometry = xdg_surface_v6_handle_set_window_geometry, +}; + +static void +surface_send_configure(void *user_data) +{ + struct ds_xdg_surface_v6 *surface; + struct ds_xdg_surface_v6_configure *configure; + + surface = user_data; + surface->configure_idle = NULL; + + configure = calloc(1, sizeof *configure); + if (!configure) { + wl_client_post_no_memory(surface->client->wl_client); + return; + } + + wl_list_insert(surface->configure_list.prev, &configure->link); + configure->serial = surface->scheduled_serial; + configure->surface = surface; + + switch (surface->role) { + case DS_XDG_SURFACE_V6_ROLE_NONE: + assert(0 && "not reached"); + break; + case DS_XDG_SURFACE_V6_ROLE_TOPLEVEL: + send_xdg_toplevel_v6_configure(surface, configure); + break; + case DS_XDG_SURFACE_V6_ROLE_POPUP: + break; + } + + wl_signal_emit(&surface->events.configure, configure); + + zxdg_surface_v6_send_configure(surface->resource, configure->serial); +} diff --git a/src/xdg_shell_v6/xdg_toplevel_v6.c b/src/xdg_shell_v6/xdg_toplevel_v6.c new file mode 100644 index 0000000..463b65a --- /dev/null +++ b/src/xdg_shell_v6/xdg_toplevel_v6.c @@ -0,0 +1,329 @@ +#include +#include +#include + +#include "xdg_shell_v6.h" + +static const struct ds_surface_role xdg_toplevel_v6_surface_role = +{ + .name = "xdg_toplevel_v6", + .commit = handle_xdg_surface_v6_commit, +}; + +static const struct zxdg_toplevel_v6_interface xdg_toplevel_v6_impl; + +static void xdg_toplevel_v6_handle_resource_destroy(struct wl_resource *resource); + +void +create_xdg_toplevel_v6(struct ds_xdg_surface_v6 *surface, uint32_t id) +{ + if (!ds_surface_set_role(surface->ds_surface, &xdg_toplevel_v6_surface_role, + surface, surface->resource, ZXDG_SHELL_V6_ERROR_ROLE)) + return; + + if (surface->role != DS_XDG_SURFACE_V6_ROLE_NONE) { + wl_resource_post_error(surface->resource, + ZXDG_SURFACE_V6_ERROR_ALREADY_CONSTRUCTED, + "xdg_surface_v6 has already been constructed"); + return; + } + + assert(surface->toplevel == NULL); + + surface->toplevel = calloc(1, sizeof *surface->toplevel); + if (!surface->toplevel) { + wl_resource_post_no_memory(surface->resource); + return; + } + + surface->toplevel->base = surface; + + wl_signal_init(&surface->toplevel->events.request_maximize); + wl_signal_init(&surface->toplevel->events.request_fullscreen); + wl_signal_init(&surface->toplevel->events.request_minimize); + wl_signal_init(&surface->toplevel->events.request_move); + wl_signal_init(&surface->toplevel->events.request_resize); + wl_signal_init(&surface->toplevel->events.request_show_window_menu); + wl_signal_init(&surface->toplevel->events.set_parent); + wl_signal_init(&surface->toplevel->events.set_title); + wl_signal_init(&surface->toplevel->events.set_app_id); + + surface->toplevel->resource = wl_resource_create( + surface->client->wl_client, &zxdg_toplevel_v6_interface, + wl_resource_get_version(surface->resource), id); + if (!surface->toplevel->resource) { + free(surface->toplevel); + wl_resource_post_no_memory(surface->resource); + return; + } + + wl_resource_set_implementation(surface->toplevel->resource, + &xdg_toplevel_v6_impl, surface, + xdg_toplevel_v6_handle_resource_destroy); + + surface->role = DS_XDG_SURFACE_V6_ROLE_TOPLEVEL; +} + +void +handle_xdg_surface_v6_toplevel_committed(struct ds_xdg_surface_v6 *surface) +{ + if (!surface->toplevel->added) { + ds_xdg_surface_v6_schedule_configure(surface); + surface->toplevel->added = true; + return; + } + + surface->toplevel->current = surface->toplevel->pending; +} + +void +send_xdg_toplevel_v6_configure(struct ds_xdg_surface_v6 *surface, + struct ds_xdg_surface_v6_configure *configure) +{ + struct wl_array states; + uint32_t width, height; + + configure->toplevel_configure = + malloc(sizeof *configure->toplevel_configure); + if (!configure->toplevel_configure) { + wl_resource_post_no_memory(surface->toplevel->resource); + return; + } + + *configure->toplevel_configure = surface->toplevel->scheduled; + + wl_array_init(&states); + if (surface->toplevel->scheduled.maximized) { + uint32_t *s = wl_array_add(&states, sizeof(uint32_t)); + if (!s) + goto error_out; + *s = ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED; + } + + if (surface->toplevel->scheduled.fullscreen) { + uint32_t *s = wl_array_add(&states, sizeof(uint32_t)); + if (!s) + goto error_out; + *s = ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN; + } + + if (surface->toplevel->scheduled.resizing) { + uint32_t *s = wl_array_add(&states, sizeof(uint32_t)); + if (!s) + goto error_out; + *s = ZXDG_TOPLEVEL_V6_STATE_RESIZING; + } + + if (surface->toplevel->scheduled.activated) { + uint32_t *s = wl_array_add(&states, sizeof(uint32_t)); + if (!s) + goto error_out; + *s = ZXDG_TOPLEVEL_V6_STATE_ACTIVATED; + } + + if (surface->toplevel->scheduled.tiled) { + ;;; + // TODO + } + + width = surface->toplevel->scheduled.width; + height = surface->toplevel->scheduled.height; + + zxdg_toplevel_v6_send_configure(surface->toplevel->resource, width, height, + &states); + wl_array_release(&states); + + return; + +error_out: + wl_array_release(&states); + wl_resource_post_no_memory(surface->toplevel->resource); +} + +void +destroy_xdg_toplevel_v6(struct ds_xdg_surface_v6 *xdg_surface_v6) +{ + reset_xdg_surface_v6(xdg_surface_v6); +} + +static void +xdg_toplevel_v6_handle_destroy(struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +xdg_toplevel_v6_handle_set_parent(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *parent_resource) +{ + // TODO +} + +static void +xdg_toplevel_v6_handle_set_title(struct wl_client *client, + struct wl_resource *resource, const char *title) +{ + struct ds_xdg_surface_v6 *surface; + char *tmp; + + surface = wl_resource_get_user_data(resource); + tmp = strdup(title); + if (!tmp) { + wl_resource_post_no_memory(resource); + return; + } + + if (surface->toplevel->title) + free(surface->toplevel->title); + + surface->toplevel->title = tmp; + wl_signal_emit(&surface->toplevel->events.set_title, surface); +} + +static void +xdg_toplevel_v6_handle_set_app_id(struct wl_client *client, + struct wl_resource *resource, const char *app_id) +{ + struct ds_xdg_surface_v6 *surface; + char *tmp; + + surface = wl_resource_get_user_data(resource); + tmp = strdup(app_id); + if (!tmp) { + wl_resource_post_no_memory(resource); + return; + } + + if (surface->toplevel->app_id) + free(surface->toplevel->app_id); + + surface->toplevel->app_id= tmp; + wl_signal_emit(&surface->toplevel->events.set_app_id, surface); + +} + +static void +xdg_toplevel_v6_handle_show_window_menu(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *seat_resource, + uint32_t serial, int32_t x, int32_t y) +{ + // TODO +} + +static void +xdg_toplevel_v6_handle_move(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *seat_resource, + uint32_t serial) +{ + // TODO +} + +static void +xdg_toplevel_v6_handle_resize(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *seat_resource, + uint32_t serial, uint32_t edges) +{ + // TODO +} + +static void +xdg_toplevel_v6_handle_set_max_size(struct wl_client *client, + struct wl_resource *resource, int32_t width, int32_t height) +{ + struct ds_xdg_surface_v6 *surface; + + surface = wl_resource_get_user_data(resource); + surface->toplevel->pending.max_width = width; + surface->toplevel->pending.max_height = height; +} + +static void +xdg_toplevel_v6_handle_set_min_size(struct wl_client *client, + struct wl_resource *resource, int32_t width, int32_t height) +{ + struct ds_xdg_surface_v6 *surface; + + surface = wl_resource_get_user_data(resource); + surface->toplevel->pending.min_width = width; + surface->toplevel->pending.min_height = height; +} + +static void +xdg_toplevel_v6_handle_set_maximized(struct wl_client *client, + struct wl_resource *resource) +{ + struct ds_xdg_surface_v6 *surface; + + surface = wl_resource_get_user_data(resource); + surface->toplevel->requested.maximized = true; + wl_signal_emit(&surface->toplevel->events.request_maximize, surface); + ds_xdg_surface_v6_schedule_configure(surface); +} + +static void +xdg_toplevel_v6_handle_unset_maximized(struct wl_client *client, + struct wl_resource *resource) +{ + struct ds_xdg_surface_v6 *surface; + + surface = wl_resource_get_user_data(resource); + surface->toplevel->requested.maximized = false; + wl_signal_emit(&surface->toplevel->events.request_maximize, surface); + ds_xdg_surface_v6_schedule_configure(surface); +} + +static void +xdg_toplevel_v6_handle_set_fullscreen(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *output_resource) +{ + // TODO +} + +static void +xdg_toplevel_v6_handle_unset_fullscreen(struct wl_client *client, + struct wl_resource *resource) +{ + // TODO +} + +static void +xdg_toplevel_v6_handle_set_minimized(struct wl_client *client, + struct wl_resource *resource) +{ + struct ds_xdg_surface_v6 *surface; + + surface = wl_resource_get_user_data(resource); + surface->toplevel->requested.minimized = true; + wl_signal_emit(&surface->toplevel->events.request_maximize, surface); +} + +static const struct zxdg_toplevel_v6_interface xdg_toplevel_v6_impl = +{ + xdg_toplevel_v6_handle_destroy, + xdg_toplevel_v6_handle_set_parent, + xdg_toplevel_v6_handle_set_title, + xdg_toplevel_v6_handle_set_app_id, + xdg_toplevel_v6_handle_show_window_menu, + xdg_toplevel_v6_handle_move, + xdg_toplevel_v6_handle_resize, + xdg_toplevel_v6_handle_set_max_size, + xdg_toplevel_v6_handle_set_min_size, + xdg_toplevel_v6_handle_set_maximized, + xdg_toplevel_v6_handle_unset_maximized, + xdg_toplevel_v6_handle_set_fullscreen, + xdg_toplevel_v6_handle_unset_fullscreen, + xdg_toplevel_v6_handle_set_minimized, +}; + +static void +xdg_toplevel_v6_handle_resource_destroy(struct wl_resource *resource) +{ + struct ds_xdg_surface_v6 *surface; + + surface = wl_resource_get_user_data(resource); + if (!surface) + return; + + destroy_xdg_toplevel_v6(surface); +} -- 2.7.4