shell: implement wl_shell_interface 96/278196/1
authorSooChan Lim <sc1.lim@samsung.com>
Mon, 16 May 2022 08:39:29 +0000 (17:39 +0900)
committerSooChan Lim <sc1.lim@samsung.com>
Mon, 18 Jul 2022 05:58:43 +0000 (14:58 +0900)
ds_shell and ds_shell_surface are resources which implement the
wl_shell_interface and wl_shell_surface_interface.

Change-Id: I551324b8f14a3aa77c920cece19e15a12e8d9981

include/libds/shell.h [new file with mode: 0644]
src/libds/meson.build
src/libds/shell.c [new file with mode: 0644]
src/libds/shell.h [new file with mode: 0644]
src/libds/shell_surface.c [new file with mode: 0644]

diff --git a/include/libds/shell.h b/include/libds/shell.h
new file mode 100644 (file)
index 0000000..b4fa688
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef LIBDS_SHELL_H
+#define LIBDS_SHELL_H
+
+#include <stdint.h>
+#include <wayland-server.h>
+
+#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
index 43bdeae..b59fda2 100644 (file)
@@ -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 (file)
index 0000000..3adb519
--- /dev/null
@@ -0,0 +1,175 @@
+#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);
+}
diff --git a/src/libds/shell.h b/src/libds/shell.h
new file mode 100644 (file)
index 0000000..c2306af
--- /dev/null
@@ -0,0 +1,157 @@
+#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
diff --git a/src/libds/shell_surface.c b/src/libds/shell_surface.c
new file mode 100644 (file)
index 0000000..73c498b
--- /dev/null
@@ -0,0 +1,540 @@
+#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);
+}