From a002a9cf36b0c0e691d4f9aa5494a119a4d485ff Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Mon, 16 May 2022 17:39:29 +0900 Subject: [PATCH] shell: implement wl_shell_interface ds_shell and ds_shell_surface are resources which implement the wl_shell_interface and wl_shell_surface_interface. Change-Id: I1064d0b431bdf1fd734353b58bc7994d202e9c57 --- include/libds/shell.h | 50 +++++ src/libds/meson.build | 2 + src/libds/shell.c | 175 +++++++++++++++ src/libds/shell.h | 157 ++++++++++++++ src/libds/shell_surface.c | 540 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 924 insertions(+) create mode 100644 include/libds/shell.h create mode 100644 src/libds/shell.c create mode 100644 src/libds/shell.h create mode 100644 src/libds/shell_surface.c diff --git a/include/libds/shell.h b/include/libds/shell.h new file mode 100644 index 0000000..b4fa688 --- /dev/null +++ b/include/libds/shell.h @@ -0,0 +1,50 @@ +#ifndef LIBDS_SHELL_H +#define LIBDS_SHELL_H + +#include +#include + +#include "surface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct ds_shell; + +struct ds_shell_surface; + +struct ds_shell * +ds_shell_create(struct wl_display *display); + +void +ds_shell_add_destroy_listener(struct ds_shell *shell, + struct wl_listener *listener); + +void +ds_shell_add_new_surface_listener(struct ds_shell *shell, + struct wl_listener *listener); + +void +ds_shell_surface_add_destroy_listener(struct ds_shell_surface *surface, + struct wl_listener *listener); + +void +ds_shell_surface_add_map_listener(struct ds_shell_surface *surface, + struct wl_listener *listener); + +void +ds_shell_surface_add_unmap_listener(struct ds_shell_surface *surface, + struct wl_listener *listener); + +void +ds_shell_surface_ping(struct ds_shell_surface *surface); + +struct ds_surface * +ds_shell_surface_get_surface(struct ds_shell_surface *surface); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libds/meson.build b/src/libds/meson.build index 43bdeae..b59fda2 100644 --- a/src/libds/meson.build +++ b/src/libds/meson.build @@ -26,6 +26,8 @@ libds_files = [ 'seat/seat_pointer.c', 'seat/seat_keyboard.c', 'seat/seat_touch.c', + 'shell.c', + 'shell_surface.c', ] protocols = { diff --git a/src/libds/shell.c b/src/libds/shell.c new file mode 100644 index 0000000..3adb519 --- /dev/null +++ b/src/libds/shell.c @@ -0,0 +1,175 @@ +#include +#include +#include + +#include "libds/log.h" +#include "libds/shell.h" + +#include "shell.h" + +#define WL_SHELL_VERSION 1 +#define SHELL_PING_TIMEOUT 10000 + +static void shell_handle_display_destroy(struct wl_listener *listener, + void *data); +static void shell_bind(struct wl_client *wl_client, void *data, + uint32_t verison, uint32_t id); + +WL_EXPORT struct ds_shell * +ds_shell_create(struct wl_display *display) +{ + struct ds_shell *shell; + + shell = calloc(1, sizeof *shell); + if (!shell) { + return NULL; + } + + shell->global = wl_global_create(display, &wl_shell_interface, + WL_SHELL_VERSION, shell, shell_bind); + if (!shell->global) { + free(shell); + return NULL; + } + + wl_list_init(&shell->clients); + + shell->display_destroy.notify = shell_handle_display_destroy; + wl_display_add_destroy_listener(display, &shell->display_destroy); + + wl_signal_init(&shell->events.destroy); + wl_signal_init(&shell->events.new_surface); + + shell->ping_timeout = SHELL_PING_TIMEOUT; + + ds_inf("Global created: xdg_wm_base shell(%p)", shell); + + return shell; +} + +WL_EXPORT void +ds_shell_add_destroy_listener(struct ds_shell *shell, + struct wl_listener *listener) +{ + wl_signal_add(&shell->events.destroy, listener); +} + +WL_EXPORT void +ds_shell_add_new_surface_listener(struct ds_shell *shell, + struct wl_listener *listener) +{ + wl_signal_add(&shell->events.new_surface, listener); +} + +static void +shell_handle_display_destroy(struct wl_listener *listener, void *data) +{ + struct ds_shell *shell; + + shell = wl_container_of(listener, shell, display_destroy); + + ds_inf("Global destroy: xdg_wm_base shell(%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 +shell_handle_get_shell_surface(struct wl_client *wl_client, + struct wl_resource *resource, uint32_t id, + struct wl_resource *surface_resource) +{ + struct ds_shell_client *client; + struct ds_surface *surface; + + client = wl_resource_get_user_data(resource); + surface = ds_surface_from_resource(surface_resource); + create_shell_surface(client, surface, id); +} + +static const struct wl_shell_interface shell_impl = +{ + .get_shell_surface = shell_handle_get_shell_surface, +}; + +static void +shell_client_handle_resource_destroy(struct wl_resource *resource) +{ + struct ds_shell_client *client; + struct ds_shell_surface *shell_surface, *tmp; + + client = wl_resource_get_user_data(resource); + + wl_list_for_each_safe(shell_surface, tmp, &client->shell_surfaces, link) + destroy_shell_surface(shell_surface); + + if (client->ping_timer != NULL) + wl_event_source_remove(client->ping_timer); + + wl_list_remove(&client->link); + free(client); +} + +static int +shell_client_handle_ping_timeout(void *user_data) +{ + struct ds_shell_client *client = user_data; + struct ds_shell_surface *surface; + + wl_list_for_each(surface, &client->shell_surfaces, link) + wl_signal_emit(&surface->events.ping_timeout, surface); + + client->ping_serial = 0; + + return 1; +} + +static void +shell_client_init_ping_timer(struct ds_shell_client *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, + shell_client_handle_ping_timeout, client); + if (client->ping_timer == NULL) + wl_client_post_no_memory(client->wl_client); +} + +static void +shell_bind(struct wl_client *wl_client, void *data, uint32_t version, + uint32_t id) +{ + struct ds_shell *shell = data; + struct ds_shell_client *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->shell_surfaces); + + client->resource = + wl_resource_create(wl_client, &wl_shell_interface, version, id); + if (client->resource == NULL) { + free(client); + wl_client_post_no_memory(wl_client); + return; + } + + wl_resource_set_implementation(client->resource, &shell_impl, client, + shell_client_handle_resource_destroy); + + wl_list_insert(&shell->clients, &client->link); + + shell_client_init_ping_timer(client); +} diff --git a/src/libds/shell.h b/src/libds/shell.h new file mode 100644 index 0000000..c2306af --- /dev/null +++ b/src/libds/shell.h @@ -0,0 +1,157 @@ +#ifndef DS_SHELL_H +#define DS_SHELL_H + +#include + +#include "libds/output.h" + +#include "surface.h" + +enum ds_shell_surface_role +{ + DS_SHELL_SURFACE_ROLE_NONE, + DS_SHELL_SURFACE_ROLE_TOPLEVEL, + DS_SHELL_SURFACE_ROLE_POPUP, +}; + +struct ds_shell +{ + 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_shell_client +{ + struct ds_shell *shell; + + struct wl_resource *resource; + struct wl_client *wl_client; + struct wl_event_source *ping_timer; + + struct wl_list shell_surfaces; + + struct wl_list link; // ds_shell::clients + + uint32_t ping_serial; +}; + +struct ds_shell_toplevel_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_shell_toplevel_requested +{ + bool maximized, minimized, fullscreen; + struct ds_output *fullscreen_output; + struct wl_listener fullscreen_output_destroy; +}; + +struct ds_shell_toplevel +{ + struct ds_shell_surface *base; + bool added; + + struct ds_shell_surface *parent; + struct wl_listener parent_unmap; + + struct ds_shell_toplevel_state current, pending; + struct ds_shell_toplevel_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_shell_surface_state +{ + uint32_t configure_serial; + struct { + int x, y; + int width, height; + } geometry; +}; + +struct ds_shell_surface +{ + struct ds_shell_client *client; + struct ds_surface *surface; + + enum ds_shell_surface_role role; + + union { + struct ds_shell_toplevel *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_shell_surface_state current, pending; + + struct wl_list link; // ds_shell_client::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; + } events; + + bool added, configured, mapped; +}; + +struct ds_shell_surface_configure +{ + struct ds_shell_surface *shell_surface; + struct wl_list link; + uint32_t serial; +}; + +struct ds_shell_surface * +create_shell_surface(struct ds_shell_client *client, struct ds_surface *surface, + uint32_t id); + +void destroy_shell_surface(struct ds_shell_surface *surface); + +#endif diff --git a/src/libds/shell_surface.c b/src/libds/shell_surface.c new file mode 100644 index 0000000..73c498b --- /dev/null +++ b/src/libds/shell_surface.c @@ -0,0 +1,540 @@ +#include +#include +#include + +#include "libds/log.h" + +#include "shell.h" + +static const struct wl_shell_surface_interface shell_surface_impl; + +static void reset_shell_surface(struct ds_shell_surface *shell_surface); +static void shell_surface_handle_surface_destroy(struct wl_listener *listener, + void *data); +static void shell_surface_handle_surface_commit(struct wl_listener *listener, + void *data); +static void shell_surface_handle_resource_destroy(struct wl_resource *resource); +static void shell_surface_configure_destroy(struct ds_shell_surface_configure *configure); +static void surface_send_configure(void *user_data); +static void handle_shell_surface_commit(struct ds_surface *surface); + +WL_EXPORT void +ds_shell_surface_add_destroy_listener(struct ds_shell_surface *shell_surface, + struct wl_listener *listener) +{ + wl_signal_add(&shell_surface->events.destroy, listener); +} + +WL_EXPORT void +ds_shell_surface_add_map_listener(struct ds_shell_surface *shell_surface, + struct wl_listener *listener) +{ + wl_signal_add(&shell_surface->events.map, listener); +} + +WL_EXPORT void +ds_shell_surface_add_unmap_listener(struct ds_shell_surface *shell_surface, + struct wl_listener *listener) +{ + wl_signal_add(&shell_surface->events.unmap, listener); +} + +WL_EXPORT struct ds_surface * +ds_shell_surface_get_surface(struct ds_shell_surface *shell_surface) +{ + return shell_surface->surface; +} + +static const struct ds_surface_role shell_surface_role = +{ + .name = "shell_surface", + .commit = handle_shell_surface_commit, +}; + +struct ds_shell_surface * +create_shell_surface(struct ds_shell_client *client, struct ds_surface *surface, + uint32_t id) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = calloc(1, sizeof *shell_surface); + if (!shell_surface) { + wl_client_post_no_memory(client->wl_client); + return NULL; + } + + shell_surface->client = client; + shell_surface->role = DS_SHELL_SURFACE_ROLE_NONE; + shell_surface->surface = surface; + shell_surface->resource = wl_resource_create(client->wl_client, + &wl_shell_surface_interface, wl_resource_get_version(client->resource), + id); + if (!shell_surface->resource) { + free(shell_surface); + wl_client_post_no_memory(client->wl_client); + return NULL; + } + + if (!ds_surface_set_role(shell_surface->surface, &shell_surface_role, + shell_surface, shell_surface->resource, -1)) { + free(shell_surface); + return NULL; + } + + wl_list_init(&shell_surface->configure_list); + + wl_signal_init(&shell_surface->events.destroy); + wl_signal_init(&shell_surface->events.ping_timeout); + wl_signal_init(&shell_surface->events.new_popup); + wl_signal_init(&shell_surface->events.map); + wl_signal_init(&shell_surface->events.unmap); + wl_signal_init(&shell_surface->events.configure); + + shell_surface->listener.surface_destroy.notify = + shell_surface_handle_surface_destroy; + ds_surface_add_destroy_listener(surface, + &shell_surface->listener.surface_destroy); + + shell_surface->listener.surface_commit.notify = + shell_surface_handle_surface_commit; + ds_surface_add_commit_listener(surface, + &shell_surface->listener.surface_commit); + + wl_resource_set_implementation(shell_surface->resource, &shell_surface_impl, + shell_surface, shell_surface_handle_resource_destroy); + + wl_list_insert(&client->shell_surfaces, &shell_surface->link); + + ds_inf("New shell_surface %p (res %p)", shell_surface, shell_surface->resource); + + return shell_surface; +} + +void +destroy_shell_surface(struct ds_shell_surface *shell_surface) +{ + reset_shell_surface(shell_surface); + + wl_resource_set_user_data(shell_surface->resource, NULL); + + ds_surface_reset_role_data(shell_surface->surface); + + wl_list_remove(&shell_surface->link); + wl_list_remove(&shell_surface->listener.surface_destroy.link); + wl_list_remove(&shell_surface->listener.surface_commit.link); + + free(shell_surface); +} + +static void +unmap_shell_surface(struct ds_shell_surface *shell_surface) +{ + struct ds_shell_surface_configure *configure, *tmp; + + // TODO handle popup + + if (shell_surface->mapped) + wl_signal_emit(&shell_surface->events.unmap, shell_surface); + + switch (shell_surface->role) { + case DS_SHELL_SURFACE_ROLE_TOPLEVEL: + if (shell_surface->toplevel->parent) { + wl_list_remove(&shell_surface->toplevel->parent_unmap.link); + shell_surface->toplevel->parent = NULL; + } + free(shell_surface->toplevel->title); + shell_surface->toplevel->title = NULL; + free(shell_surface->toplevel->app_id); + shell_surface->toplevel->app_id = NULL; + break; + case DS_SHELL_SURFACE_ROLE_POPUP: + // TODO + break; + case DS_SHELL_SURFACE_ROLE_NONE: + assert(false && "not reached"); + } + + wl_list_for_each_safe(configure, tmp, &shell_surface->configure_list, link) + shell_surface_configure_destroy(configure); + + if (shell_surface->configure_idle) { + wl_event_source_remove(shell_surface->configure_idle); + shell_surface->configure_idle = NULL; + } + + shell_surface->configured = false; + shell_surface->mapped = false; +} + +static void +reset_shell_surface(struct ds_shell_surface *shell_surface) +{ + struct ds_shell_toplevel_requested *req; + + if (shell_surface->role != DS_SHELL_SURFACE_ROLE_NONE) + unmap_shell_surface(shell_surface); + + if (shell_surface->added) { + wl_signal_emit(&shell_surface->events.destroy, shell_surface); + shell_surface->added = false; + } + + switch (shell_surface->role) { + case DS_SHELL_SURFACE_ROLE_TOPLEVEL: + req = &shell_surface->toplevel->requested; + if (req->fullscreen_output) + wl_list_remove(&req->fullscreen_output_destroy.link); + free(shell_surface->toplevel); + shell_surface->toplevel = NULL; + break; + case DS_SHELL_SURFACE_ROLE_POPUP: + // TODO + break; + case DS_SHELL_SURFACE_ROLE_NONE: + // This space is intentionally left blank + break; + } + + shell_surface->role = DS_SHELL_SURFACE_ROLE_NONE; +} + +static uint32_t +ds_shell_surface_schedule_configure(struct ds_shell_surface *shell_surface) +{ + struct wl_display *display; + struct wl_event_loop *loop; + + display = wl_client_get_display(shell_surface->client->wl_client); + loop = wl_display_get_event_loop(display); + + if (!shell_surface->configure_idle) { + shell_surface->scheduled_serial = wl_display_next_serial(display); + shell_surface->configure_idle = wl_event_loop_add_idle(loop, + surface_send_configure, shell_surface); + if (!shell_surface->configure_idle) + wl_client_post_no_memory(shell_surface->client->wl_client); + } + + return shell_surface->scheduled_serial; +} + +static void +handle_shell_surface_commit(struct ds_surface *surface) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = ds_surface_get_role_data(surface); + shell_surface->current = shell_surface->pending; + + switch (shell_surface->role) { + case DS_SHELL_SURFACE_ROLE_NONE: + // inert toplevel or popup + break; + case DS_SHELL_SURFACE_ROLE_TOPLEVEL: + if (!shell_surface->toplevel->added) { + ds_shell_surface_schedule_configure(shell_surface); + shell_surface->toplevel->added = true; + } + // TODO + break; + case DS_SHELL_SURFACE_ROLE_POPUP: + // TODO + break; + } + + if (!shell_surface->added) { + shell_surface->added = true; + wl_signal_emit(&shell_surface->client->shell->events.new_surface, shell_surface); + } + + if (shell_surface->configured && + ds_surface_has_buffer(shell_surface->surface) && + !shell_surface->mapped) { + shell_surface->mapped = true; + wl_signal_emit(&shell_surface->events.map, shell_surface); + } +} + +void handle_shell_surface_precommit(struct ds_surface *surface) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = ds_surface_get_role_data(surface); + + // TODO + (void)shell_surface; +} + +void +ds_shell_surface_ping(struct ds_shell_surface *shell_surface) +{ +} + +static void +shell_surface_handle_surface_destroy(struct wl_listener *listener, void *data) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = wl_container_of(listener, shell_surface, listener.surface_destroy); + destroy_shell_surface(shell_surface); +} + +static void +shell_surface_handle_surface_commit(struct wl_listener *listener, void *data) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = wl_container_of(listener, shell_surface, listener.surface_commit); + + if (ds_surface_has_buffer(shell_surface->surface) && + !shell_surface->configured) { + wl_resource_post_error(shell_surface->resource, + -1, + "shell_surface has never been configured"); + return; + } + + if (!ds_surface_get_role(shell_surface->surface)) { + wl_resource_post_error(shell_surface->resource, + -1, + "shell_surface must have a role"); + return; + } +} + +static void +shell_surface_handle_resource_destroy(struct wl_resource *resource) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = wl_resource_get_user_data(resource); + if (!shell_surface) + return; + + destroy_shell_surface(shell_surface); +} + +static void +shell_surface_configure_destroy(struct ds_shell_surface_configure *configure) +{ + wl_list_remove(&configure->link); + free(configure); +} + +static void +create_shell_surface_toplevel(struct ds_shell_surface *shell_surface) +{ + assert(shell_surface->toplevel == NULL); + + shell_surface->toplevel = calloc(1, sizeof *shell_surface->toplevel); + if (!shell_surface->toplevel) { + wl_resource_post_no_memory(shell_surface->resource); + return; + } + + shell_surface->toplevel->base = shell_surface; + + wl_signal_init(&shell_surface->toplevel->events.request_maximize); + wl_signal_init(&shell_surface->toplevel->events.request_fullscreen); + wl_signal_init(&shell_surface->toplevel->events.request_minimize); + wl_signal_init(&shell_surface->toplevel->events.request_move); + wl_signal_init(&shell_surface->toplevel->events.request_resize); + wl_signal_init(&shell_surface->toplevel->events.request_show_window_menu); + wl_signal_init(&shell_surface->toplevel->events.set_parent); + wl_signal_init(&shell_surface->toplevel->events.set_title); + wl_signal_init(&shell_surface->toplevel->events.set_app_id); + + shell_surface->role = DS_SHELL_SURFACE_ROLE_TOPLEVEL; +} + +static void +shell_surface_handle_pong(struct wl_client *wl_client, + struct wl_resource *resource, uint32_t serial) +{ + struct ds_shell_surface *shell_surface; + struct ds_shell_client *client; + + shell_surface = wl_resource_get_user_data(resource); + + client = shell_surface->client; + if (client->ping_serial != serial) + return; + + wl_event_source_timer_update(client->ping_timer, 0); + client->ping_serial = 0; +} + +static void +shell_surface_handle_move(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *seat_resource, + uint32_t serial) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = wl_resource_get_user_data(resource); + + // TODO + (void)shell_surface; +} + +static void +shell_surface_handle_resize(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *seat_resource, + uint32_t serial, uint32_t edges) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = wl_resource_get_user_data(resource); + + // TODO + (void)shell_surface; +} + +static void +shell_surface_handle_set_toplevel(struct wl_client *client, struct wl_resource *resource) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = wl_resource_get_user_data(resource); + create_shell_surface_toplevel(shell_surface); +} + +static void +shell_surface_handle_set_transient(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *parent_resource, + int32_t x, int32_t y, uint32_t flags) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = wl_resource_get_user_data(resource); + + // TODO + (void)shell_surface; + ds_err("Not implemented yet."); +} + +static void +shell_surface_handle_set_fullscreen(struct wl_client *client, + struct wl_resource *resource, uint32_t method, uint32_t framerate, + struct wl_resource *output_resource) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = wl_resource_get_user_data(resource); + + // TODO + (void)shell_surface; + ds_err("Not implemented yet."); +} + +static void +shell_surface_handle_set_popup(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *seat_resource, + uint32_t serial, struct wl_resource *parent_resource, + int32_t x, int32_t y, uint32_t flags) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = wl_resource_get_user_data(resource); + + // TODO + (void)shell_surface; + ds_err("Not implemented yet."); +} + +static void +shell_surface_handle_set_maximized(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *output_resource) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = wl_resource_get_user_data(resource); + + shell_surface->toplevel->requested.maximized = true; + wl_signal_emit(&shell_surface->toplevel->events.request_maximize, shell_surface); + ds_shell_surface_schedule_configure(shell_surface); +} + +static void +shell_surface_handle_set_title(struct wl_client *client, + struct wl_resource *resource, const char *title) +{ + struct ds_shell_surface *shell_surface; + char *tmp; + + shell_surface = wl_resource_get_user_data(resource); + tmp = strdup(title); + if (!tmp) { + wl_resource_post_no_memory(resource); + return; + } + + if (shell_surface->toplevel->title) + free(shell_surface->toplevel->title); + + shell_surface->toplevel->title = tmp; + wl_signal_emit(&shell_surface->toplevel->events.set_title, shell_surface); +} + +static void +shell_surface_handle_set_class(struct wl_client *client, + struct wl_resource *resource, const char *clas) +{ + struct ds_shell_surface *shell_surface; + + shell_surface = wl_resource_get_user_data(resource); + + // TODO + (void)shell_surface; + ds_err("Not implemented yet."); +} + +static const struct wl_shell_surface_interface shell_surface_impl = +{ + .pong = shell_surface_handle_pong, + .move = shell_surface_handle_move, + .resize = shell_surface_handle_resize, + .set_toplevel = shell_surface_handle_set_toplevel, + .set_transient = shell_surface_handle_set_transient, + .set_fullscreen = shell_surface_handle_set_fullscreen, + .set_popup = shell_surface_handle_set_popup, + .set_maximized = shell_surface_handle_set_maximized, + .set_title = shell_surface_handle_set_title, + .set_class = shell_surface_handle_set_class, +}; + +static void +surface_send_configure(void *user_data) +{ + struct ds_shell_surface *shell_surface; + struct ds_shell_surface_configure *configure; + uint32_t width, height; + uint32_t edges = 0; + + shell_surface = user_data; + shell_surface->configure_idle = NULL; + + // TDOO: Not sure if shell needs the struct ds_shell_surface_configure. + configure = calloc(1, sizeof *configure); + if (!configure) { + wl_client_post_no_memory(shell_surface->client->wl_client); + return; + } + + wl_list_insert(shell_surface->configure_list.prev, &configure->link); + configure->serial = shell_surface->scheduled_serial; + configure->shell_surface = shell_surface; + + wl_signal_emit(&shell_surface->events.configure, configure); + + edges = (WL_SHELL_SURFACE_RESIZE_TOP | WL_SHELL_SURFACE_RESIZE_LEFT); // fixed default value + width = shell_surface->current.geometry.width; + height = shell_surface->current.geometry.height; + + wl_shell_surface_send_configure(shell_surface->resource, edges, width, height); + + shell_surface->configured = true; + + // TDOO: Not sure if shell needs the struct ds_shell_surface_configure. + shell_surface_configure_destroy(configure); +} -- 2.7.4