--- /dev/null
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#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);
+}
--- /dev/null
+#ifndef DS_SHELL_H
+#define DS_SHELL_H
+
+#include <wayland-server.h>
+
+#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
--- /dev/null
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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);
+}