tinyds: Refactor out 65/279465/1
authorSeunghun Lee <shiin.lee@samsung.com>
Thu, 4 Aug 2022 08:47:59 +0000 (17:47 +0900)
committerTizen Window System <tizen.windowsystem@gmail.com>
Wed, 10 Aug 2022 04:24:16 +0000 (13:24 +0900)
Change-Id: Ia4d51ce58790812d34ca64e013808455c66f5a45

examples/tinyds.c

index 1ba5834..aa5a586 100644 (file)
@@ -1,5 +1,3 @@
-#include "pixman-helper.h"
-
 #include <assert.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <libds/pointer.h>
 #include <libds/seat.h>
 
-// FIXME Instead of including interfaces, add api for getting modifiers
+// FIXME Instead of including interfaces here,
+// add api for getting modifiers in keyboard.h
 #include <libds/interfaces/input_device.h>
 #include <libds/interfaces/keyboard.h>
 
+#include "pixman-helper.h"
+
 #define OUTPUT_WIDTH   1280
 #define OUTPUT_HEIGHT  720
 
@@ -34,26 +35,24 @@ struct tinyds_server;
 
 struct tinyds_pointer
 {
-    struct ds_input_device *dev;
     struct tinyds_server *server;
-
     struct tinyds_view *focused_view;
-
-    double x, y;
+    struct ds_input_device *dev;
 
     struct wl_listener destroy;
     struct wl_listener motion;
     struct wl_listener motion_absolute;
     struct wl_listener button;
     struct wl_listener frame;
+
+    double x, y;
 };
 
 struct tinyds_keyboard
 {
-    struct ds_input_device *dev;
     struct tinyds_server *server;
-
     struct tinyds_view *focused_view;
+    struct ds_input_device *dev;
 
     struct wl_listener destroy;
     struct wl_listener key;
@@ -62,8 +61,8 @@ struct tinyds_keyboard
 
 struct tinyds_touch
 {
-    struct ds_input_device *dev;
     struct tinyds_server *server;
+    struct ds_input_device *dev;
 
     struct wl_listener destroy;
     struct wl_listener down;
@@ -78,6 +77,8 @@ struct tinyds_output
     struct ds_allocator *allocator;
     struct ds_swapchain *swapchain;
 
+    struct wl_event_source *idle_redraw;
+
     struct wl_listener output_destroy;
     struct wl_listener output_frame;
 
@@ -89,19 +90,18 @@ struct tinyds_output
 
 struct tinyds_server
 {
+    struct tinyds_output *output;
+    struct tinyds_keyboard *keyboard;
     struct wl_display *display;
-
     struct ds_backend *backend;
     struct ds_compositor *compositor;
     struct ds_xdg_shell *xdg_shell;
     struct ds_seat *seat;
     struct ds_data_device_manager *data_device;
 
-    struct tinyds_output output;
-    struct tinyds_keyboard *keyboard;
-
     struct wl_list views;
 
+    struct wl_listener display_destroy;
     struct wl_listener new_output;
     struct wl_listener new_input;
     struct wl_listener new_xdg_surface;
@@ -112,7 +112,6 @@ struct tinyds_server
 struct tinyds_view
 {
     struct tinyds_server *server;
-
     struct ds_xdg_surface *xdg_surface;
 
     struct wl_listener xdg_surface_map;
@@ -127,46 +126,104 @@ struct tinyds_view
 
 static bool server_init(struct tinyds_server *server,
         struct wl_display *display);
-static void server_fini(struct tinyds_server *server);
-static bool server_init_data_device(struct tinyds_server *server);
+static bool server_init_backend(struct tinyds_server *server);
+static bool server_init_protocols(struct tinyds_server *server);
+static void server_handle_display_destroy(struct wl_listener *listener,
+        void *data);
+static void server_handle_new_input(struct wl_listener *listener, void *data);
+static void server_handle_new_output(struct wl_listener *listener, void *data);
+static void server_handle_new_xdg_surface(struct wl_listener *listener,
+        void *data);
+static void server_handle_request_set_selection(struct wl_listener *listener,
+        void *data);
+static void server_handle_request_data_offer_receive(
+        struct wl_listener *listener, void *data);
+static bool server_handle_keybinding(struct tinyds_server *server,
+        xkb_keysym_t sym);
+static void server_add_output(struct tinyds_server *server,
+        struct ds_output *ds_output, int width, int height);
+static void server_add_keyboard(struct tinyds_server *server,
+        struct ds_input_device *dev);
+static void server_add_touch(struct tinyds_server *server,
+        struct ds_input_device *dev);
+static void server_add_pointer(struct tinyds_server *server,
+        struct ds_input_device *dev);
 static void server_add_view(struct tinyds_server *server,
         struct ds_xdg_surface *xdg_surface);
+static struct tinyds_view *server_view_at(struct tinyds_server *server,
+        double lx, double ly, double *sx, double *sy);
 static void server_next_focus(struct tinyds_server *server);
 static void server_focus_view(struct tinyds_server *server,
         struct tinyds_view *view);
-static struct tinyds_view *server_view_at(struct tinyds_server *server,
-        double lx, double ly, double *sx, double *sy);
-static bool output_init(struct tinyds_output *output,
-        struct tinyds_server *server, struct ds_output *ds_output,
-        int width, int height);
-static void output_fini(struct tinyds_output *output);
+static void output_destroy(struct tinyds_output *output);
+static void output_unlink_ds_output(struct tinyds_output *output);
+static void output_handle_destroy(struct wl_listener *listener, void *data);
+static void output_handle_frame(struct wl_listener *listener, void *data);
 static void output_damage(struct tinyds_output *output);
-static void output_redraw(struct tinyds_output *output);
+static void output_schedule_idle_redraw(struct tinyds_output *output);
+static void output_handle_idle_redraw(void *data);
+static void keyboard_handle_device_destroy(struct wl_listener *listener,
+        void *data);
+static void keyboard_handle_key(struct wl_listener *listener, void *data);
+static void keyboard_handle_modifiers(struct wl_listener *listener,
+        void *data);
+static void touch_handle_device_destroy(struct wl_listener *listener,
+        void *data);
+static void touch_handle_down(struct wl_listener *listener, void *data);
+static void touch_handle_up(struct wl_listener *listener, void *data);
+static void touch_handle_motion(struct wl_listener *listener, void *data);
+static void pointer_handle_device_destroy(struct wl_listener *listener,
+        void *data);
+static void pointer_handle_motion(struct wl_listener *listener, void *data);
+static void pointer_handle_motion_absolute(struct wl_listener *listener,
+        void *data);
+static void pointer_handle_button(struct wl_listener *listener, void *data);
+static void pointer_handle_frame(struct wl_listener *listener, void *data);
 static void view_destroy(struct tinyds_view *view);
+static void view_handle_xdg_surface_map(struct wl_listener *listener,
+        void *data);
+static void view_handle_xdg_surface_unmap(struct wl_listener *listener,
+        void *data);
+static void view_handle_xdg_surface_destroy(struct wl_listener *listener,
+        void *data);
+static void view_handle_surface_commit(struct wl_listener *listener,
+        void *data);
 static void view_composite(struct tinyds_view *view,
         pixman_image_t *dst_image);
+static void view_send_frame_done(struct tinyds_view *view);
 
 int
 main(void)
 {
-    struct tinyds_server server;
+    struct tinyds_server server = {0, };
     struct wl_display *display;
     const char *socket;
+    int ret = EXIT_FAILURE;
 
     ds_log_init(DS_DBG, NULL);
 
     display = wl_display_create();
-    assert(display);
-
-    assert(server_init(&server, display) == true);
+    if (!display) {
+        ds_err("Could not create wl_display");
+        return ret;
+    }
 
-    socket = wl_display_add_socket_auto(display);
-    assert(socket);
+    if (!server_init(&server, display)) {
+        ds_err("Could not initialize server");
+        goto end;
+    }
 
     ds_backend_start(server.backend);
+    if (!server.output) {
+        ds_err("No available output");
+        goto end;
+    }
 
-    output_damage(&server.output);
-    output_redraw(&server.output);
+    socket = wl_display_add_socket_auto(display);
+    if (!socket) {
+        ds_err("Could not add display socket");
+        goto end;
+    }
 
     setenv("WAYLAND_DISPLAY", socket, true);
 
@@ -174,10 +231,62 @@ main(void)
 
     wl_display_run(server.display);
 
-    server_fini(&server);
+    wl_display_destroy_clients(display);
+
+    ret = EXIT_SUCCESS;
+end:
     wl_display_destroy(display);
 
-    return 0;
+    return ret;
+}
+
+static bool
+server_init(struct tinyds_server *server, struct wl_display *display)
+{
+    server->display = display;
+
+    server->display_destroy.notify = server_handle_display_destroy;
+    wl_display_add_destroy_listener(display, &server->display_destroy);
+
+    wl_list_init(&server->views);
+
+    if (wl_display_init_shm(display) != 0)
+        return false;
+
+    if (!server_init_backend(server))
+        return false;
+
+    if (!server_init_protocols(server)) {
+        ds_backend_destroy(server->backend);
+        return false;
+    }
+
+    return true;
+}
+
+static void
+server_handle_display_destroy(struct wl_listener *listener, void *data)
+{
+    struct tinyds_server *server;
+    struct tinyds_view *view, *tmp;
+
+    server = wl_container_of(listener, server, display_destroy);
+
+    wl_list_for_each_safe(view, tmp, &server->views, link)
+        view_destroy(view);
+
+    if (server->output)
+        output_destroy(server->output);
+
+    /* It's safe to remove links of all listener here because
+     * server_handle_display_destroy must be the first to be called
+     * before other objects such as backend. */
+    wl_list_remove(&server->display_destroy.link);
+    wl_list_remove(&server->new_output.link);
+    wl_list_remove(&server->new_input.link);
+    wl_list_remove(&server->new_xdg_surface.link);
+    wl_list_remove(&server->request_set_selection.link);
+    wl_list_remove(&server->request_data_offer_receive.link);
 }
 
 static struct ds_backend *
@@ -189,6 +298,90 @@ create_wl_backend(struct wl_display *display)
     return ds_wl_backend_create(display, NULL);
 }
 
+static bool
+server_init_backend(struct tinyds_server *server)
+{
+    server->backend = create_wl_backend(server->display);
+    if (!server->backend)
+        return false;
+
+    ds_wl_backend_create_output(server->backend);
+
+    server->new_input.notify = server_handle_new_input;
+    ds_backend_add_new_input_listener(server->backend, &server->new_input);
+
+    server->new_output.notify = server_handle_new_output;
+    ds_backend_add_new_output_listener(server->backend, &server->new_output);
+
+    return true;
+}
+
+static bool
+server_init_protocols(struct tinyds_server *server)
+{
+    server->compositor = ds_compositor_create(server->display);
+    if (!server->compositor)
+        return false;
+
+    server->seat = ds_seat_create(server->display, "seat0" /* arbitrary name */);
+    if (!server->seat)
+        return false;
+
+    server->xdg_shell = ds_xdg_shell_create(server->display);
+    if (!server->xdg_shell)
+        return false;
+
+    server->new_xdg_surface.notify = server_handle_new_xdg_surface;
+    ds_xdg_shell_add_new_surface_listener(server->xdg_shell,
+            &server->new_xdg_surface);
+
+    server->data_device = ds_data_device_manager_create(server->display);
+    if (!server->data_device)
+        return false;
+
+    server->request_set_selection.notify =
+        server_handle_request_set_selection;
+    ds_data_device_manager_add_request_set_selection_listener(
+            server->data_device, &server->request_set_selection);
+
+    server->request_data_offer_receive.notify =
+        server_handle_request_data_offer_receive;
+    ds_data_device_manager_add_request_data_offer_receive_listener(
+            server->data_device, &server->request_data_offer_receive);
+
+    return true;
+}
+
+static void
+server_handle_new_input(struct wl_listener *listener, void *data)
+{
+    struct tinyds_server *server;
+    struct ds_input_device *dev = data;
+    enum ds_input_device_type dev_type;
+
+    server = wl_container_of(listener, server, new_input);
+
+    dev_type = ds_input_device_get_type(dev);
+    switch (dev_type) {
+        case DS_INPUT_DEVICE_KEYBOARD:
+            server_add_keyboard(server, dev);
+            ds_seat_set_capabilities(server->seat,
+                    WL_SEAT_CAPABILITY_KEYBOARD);
+            break;
+        case DS_INPUT_DEVICE_TOUCH:
+            server_add_touch(server, dev);
+            break;
+        case DS_INPUT_DEVICE_POINTER:
+            server_add_pointer(server, dev);
+            ds_seat_set_capabilities(server->seat,
+                    WL_SEAT_CAPABILITY_POINTER);
+            break;
+        default:
+            ds_err("Unknown type(%d) of ds_input_device", dev_type);
+            break;
+    }
+}
+
 static void
 server_handle_new_output(struct wl_listener *listener, void *data)
 {
@@ -196,24 +389,32 @@ server_handle_new_output(struct wl_listener *listener, void *data)
     struct ds_output *ds_output = data;
 
     server = wl_container_of(listener, server, new_output);
-
-    assert(output_init(&server->output, server, ds_output,
-            OUTPUT_WIDTH, OUTPUT_HEIGHT) == true);
+    server_add_output(server, ds_output, OUTPUT_WIDTH, OUTPUT_HEIGHT);
 }
 
 static void
-keyboard_handle_device_destroy(struct wl_listener *listener, void *data)
+server_handle_new_xdg_surface(struct wl_listener *listener, void *data)
 {
-    struct tinyds_keyboard *kbd;
+    struct tinyds_server *server;
 
-    kbd = wl_container_of(listener, kbd, destroy);
+    server = wl_container_of(listener, server, new_xdg_surface);
+    server_add_view(server, (struct ds_xdg_surface *)data);
+}
 
-    ds_inf("Keyboard(%p) destroyed", kbd);
+static void
+server_handle_request_set_selection(struct wl_listener *listener, void *data)
+{
+    struct ds_event_request_set_selection *event = data;
 
-    wl_list_remove(&kbd->destroy.link);
-    wl_list_remove(&kbd->key.link);
+    ds_seat_set_selection(event->seat, event->source, event->serial);
+}
 
-    free(kbd);
+static void
+server_handle_request_data_offer_receive(struct wl_listener *listener, void *data)
+{
+    struct ds_event_request_data_offer_receive *event = data;
+
+    ds_data_offer_send(event->offer, event->mime_type, event->fd);
 }
 
 static bool
@@ -234,62 +435,15 @@ server_handle_keybinding(struct tinyds_server *server, xkb_keysym_t sym)
 }
 
 static void
-keyboard_handle_key(struct wl_listener *listener, void *data)
-{
-    struct tinyds_keyboard *kbd;
-    struct ds_event_keyboard_key *event = data;
-    struct ds_keyboard *ds_keyboard;
-    struct xkb_state *xkb_state;
-    const xkb_keysym_t *syms;
-    uint32_t modifiers;
-    int nsyms;
-    bool handled = false;
-
-    kbd = wl_container_of(listener, kbd, key);
-
-    ds_keyboard = ds_input_device_get_keyboard(kbd->dev);
-
-    modifiers = ds_keyboard_get_modifiers(ds_keyboard);
-    if ((modifiers & DS_MODIFIER_CTRL) &&
-            (modifiers & DS_MODIFIER_ALT) &&
-            (modifiers & DS_MODIFIER_SHIFT) &&
-            event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
-        xkb_state = ds_keyboard_get_xkb_state(ds_keyboard);
-        if (xkb_state) {
-            nsyms = xkb_state_key_get_syms(xkb_state, event->keycode + 8,
-                    &syms);
-            for (int i = 0; i < nsyms; i++) {
-                handled = server_handle_keybinding(kbd->server, syms[i]);
-            }
-        }
-    }
-
-    if (!handled) {
-        ds_seat_set_keyboard(kbd->server->seat, kbd->dev);
-        ds_seat_keyboard_notify_key(kbd->server->seat, event->time_msec,
-                event->keycode, event->state);
-    }
-}
-
-static void
-keyboard_handle_modifiers(struct wl_listener *listener, void *data)
-{
-    struct tinyds_keyboard *kbd;
-
-    kbd = wl_container_of(listener, kbd, modifiers);
-
-    ds_seat_set_keyboard(kbd->server->seat, kbd->dev);
-    ds_seat_keyboard_notify_modifiers(kbd->server->seat,
-            &kbd->dev->keyboard->modifiers);
-}
-
-static void
 server_add_keyboard(struct tinyds_server *server, struct ds_input_device *dev)
 {
     struct tinyds_keyboard *kbd;
 
     kbd = calloc(1, sizeof *kbd);
-    assert(kbd);
+    if (!kbd) {
+        ds_err("Could not allocate memory");
+        return;
+    }
 
     kbd->dev = dev;
     kbd->server = server;
@@ -310,47 +464,15 @@ server_add_keyboard(struct tinyds_server *server, struct ds_input_device *dev)
 }
 
 static void
-touch_handle_device_destroy(struct wl_listener *listener, void *data)
-{
-    struct tinyds_touch *touch;
-
-    touch = wl_container_of(listener, touch, destroy);
-
-    ds_inf("Touch(%p) destroyed", touch);
-
-    wl_list_remove(&touch->destroy.link);
-    wl_list_remove(&touch->down.link);
-    wl_list_remove(&touch->up.link);
-    wl_list_remove(&touch->motion.link);
-
-    free(touch);
-}
-
-static void
-touch_handle_down(struct wl_listener *listener, void *data)
-{
-    ds_inf("Touch device(%p): down", data);
-}
-
-static void
-touch_handle_up(struct wl_listener *listener, void *data)
-{
-    ds_inf("Touch device(%p): up", data);
-}
-
-static void
-touch_handle_motion(struct wl_listener *listener, void *data)
-{
-    ds_inf("Touch device(%p): motion", data);
-}
-
-static void
 server_add_touch(struct tinyds_server *server, struct ds_input_device *dev)
 {
     struct tinyds_touch *touch;
 
     touch = calloc(1, sizeof *touch);
-    assert(touch);
+    if (!touch) {
+        ds_err("Could not allocate memory");
+        return;
+    }
 
     touch->dev = dev;
     touch->server = server;
@@ -371,115 +493,15 @@ server_add_touch(struct tinyds_server *server, struct ds_input_device *dev)
 }
 
 static void
-pointer_handle_device_destroy(struct wl_listener *listener, void *data)
-{
-    struct tinyds_pointer *pointer;
-
-    pointer = wl_container_of(listener, pointer, destroy);
-
-    ds_inf("Pointer(%p) destroyed", pointer);
-
-    wl_list_remove(&pointer->destroy.link);
-    wl_list_remove(&pointer->motion.link);
-    wl_list_remove(&pointer->motion_absolute.link);
-    wl_list_remove(&pointer->button.link);
-    wl_list_remove(&pointer->frame.link);
-
-    free(pointer);
-}
-
-static void
-pointer_handle_motion(struct wl_listener *listener, void *data)
-{
-    struct tinyds_pointer *pointer;
-
-    pointer = wl_container_of(listener, pointer, motion);
-
-    ds_inf("Pointer(%p) motion", pointer);
-}
-
-static void
-pointer_handle_motion_absolute(struct wl_listener *listener, void *data)
-{
-    struct tinyds_pointer *pointer;
-    struct ds_event_pointer_motion_absolute *event = data;
-    struct tinyds_view *view;
-    double sx, sy;
-
-    pointer = wl_container_of(listener, pointer, motion_absolute);
-
-    ds_inf("Pointer(%p) motion absolute: (x %f y %f) time(%d)",
-            pointer, event->x, event->y, event->time_msec);
-
-    pointer->x = event->x * OUTPUT_WIDTH;
-    pointer->y = event->y * OUTPUT_HEIGHT;
-    view = server_view_at(pointer->server, pointer->x, pointer->y, &sx, &sy);
-
-    if (pointer->focused_view != view) {
-        if (pointer->focused_view) {
-            ds_inf("Clear pointer focus from view(%p)", pointer->focused_view);
-            ds_seat_pointer_notify_clear_focus(pointer->server->seat);
-            pointer->focused_view = NULL;
-        }
-
-        if (view) {
-            ds_inf("Set pointer focus to view(%p)", view);
-            ds_seat_pointer_notify_enter(pointer->server->seat,
-                    ds_xdg_surface_get_surface(view->xdg_surface), sx, sy);
-            pointer->focused_view = view;
-        }
-    }
-
-    if (view) {
-        ds_seat_pointer_notify_motion(pointer->server->seat,
-                event->time_msec, sx, sy);
-    }
-}
-
-static void
-pointer_handle_button(struct wl_listener *listener, void *data)
-{
-    struct tinyds_pointer *pointer;
-    struct ds_event_pointer_button *event = data;
-    struct tinyds_view *view;
-    double sx, sy;
-
-    pointer = wl_container_of(listener, pointer, button);
-
-    ds_inf("Pointer(%p) button(%d): state(%s) time(%d)",
-            pointer, event->button,
-            (event->state == DS_BUTTON_PRESSED) ? "Pressed" : "Released",
-            event->time_msec);
-
-    ds_seat_pointer_notify_button(pointer->server->seat, event->time_msec,
-            event->button, event->state);
-
-    if (event->state == DS_BUTTON_PRESSED) {
-        view = server_view_at(pointer->server, pointer->x, pointer->y,
-                &sx, &sy);
-        if (view)
-            server_focus_view(pointer->server, view);
-    }
-}
-
-static void
-pointer_handle_frame(struct wl_listener *listener, void *data)
-{
-    struct tinyds_pointer *pointer;
-
-    pointer = wl_container_of(listener, pointer, frame);
-
-    ds_inf("Pointer(%p) frame", pointer);
-    ds_seat_pointer_notify_frame(pointer->server->seat);
-}
-
-static void
-server_add_pointer(struct tinyds_server *server, struct ds_input_device *dev)
+server_add_pointer(struct tinyds_server *server, struct ds_input_device *dev)
 {
     struct tinyds_pointer *pointer;
 
     pointer = calloc(1, sizeof *pointer);
-    assert(pointer);
+    if (!pointer) {
+        ds_err("Could not allocate memory");
+        return;
+    }
 
     pointer->dev = dev;
     pointer->server = server;
@@ -507,84 +529,69 @@ server_add_pointer(struct tinyds_server *server, struct ds_input_device *dev)
 }
 
 static void
-server_handle_new_input(struct wl_listener *listener, void *data)
+server_add_view(struct tinyds_server *server, struct ds_xdg_surface *xdg_surface)
 {
-    struct tinyds_server *server;
-    struct ds_input_device *dev = data;
-    enum ds_input_device_type dev_type;
-
-    server = wl_container_of(listener, server, new_input);
+    struct tinyds_view *view;
 
-    dev_type = ds_input_device_get_type(dev);
-    switch (dev_type) {
-        case DS_INPUT_DEVICE_KEYBOARD:
-            server_add_keyboard(server, dev);
-            ds_seat_set_capabilities(server->seat, WL_SEAT_CAPABILITY_KEYBOARD);
-            break;
-        case DS_INPUT_DEVICE_TOUCH:
-            server_add_touch(server, dev);
-            break;
-        case DS_INPUT_DEVICE_POINTER:
-            server_add_pointer(server, dev);
-            ds_seat_set_capabilities(server->seat, WL_SEAT_CAPABILITY_POINTER);
-            break;
-        default:
-            ds_err("Unknown type(%d) of ds_input_device", dev_type);
-            break;
+    view = calloc(1, sizeof *view);
+    if (!view) {
+        ds_err("Could not allocate memory");
+        return;
     }
-}
-
-static void
-view_handle_xdg_surface_map(struct wl_listener *listener, void *data)
-{
-    struct tinyds_view *view;
 
-    view = wl_container_of(listener, view, xdg_surface_map);
-    view->mapped = true;
-    server_focus_view(view->server, view);
-}
+    view->server = server;
+    view->xdg_surface = xdg_surface;
 
-static void
-view_handle_xdg_surface_unmap(struct wl_listener *listener, void *data)
-{
-    struct tinyds_view *view;
+    view->xdg_surface_map.notify = view_handle_xdg_surface_map;
+    ds_xdg_surface_add_map_listener(xdg_surface,
+            &view->xdg_surface_map);
 
-    view = wl_container_of(listener, view, xdg_surface_unmap);
-    view->mapped = false;
-}
+    view->xdg_surface_unmap.notify = view_handle_xdg_surface_unmap;
+    ds_xdg_surface_add_unmap_listener(xdg_surface,
+            &view->xdg_surface_unmap);
 
-static void
-view_handle_xdg_surface_destroy(struct wl_listener *listener, void *data) 
-{
-    struct tinyds_view *view;
+    view->xdg_surface_destroy.notify = view_handle_xdg_surface_destroy;
+    ds_xdg_surface_add_destroy_listener(xdg_surface,
+            &view->xdg_surface_destroy);
 
-    view = wl_container_of(listener, view, xdg_surface_destroy);
+    view->surface_commit.notify = view_handle_surface_commit;
+    ds_surface_add_commit_listener(
+            ds_xdg_surface_get_surface(xdg_surface),
+            &view->surface_commit);
 
-    output_damage(&view->server->output);
-    output_redraw(&view->server->output);
+    wl_list_insert(&server->views, &view->link);
 
-    view_destroy(view);
+    ds_inf("View(%p) added", view);
 }
 
-static void
-view_handle_surface_commit(struct wl_listener *listener, void *data)
+static struct tinyds_view *
+server_view_at(struct tinyds_server *server, double lx, double ly,
+        double *sx, double *sy)
 {
     struct tinyds_view *view;
+    struct ds_surface *surface;
+    struct ds_buffer *buffer;
+    int x, y, w = 0, h = 0;
 
-    view = wl_container_of(listener, view, surface_commit);
+    wl_list_for_each(view, &server->views, link) {
+        surface = ds_xdg_surface_get_surface(view->xdg_surface);
+        buffer = ds_surface_get_buffer(surface);
+        ds_buffer_get_size(buffer, &w, &h);
 
-    output_damage(&view->server->output);
-    output_redraw(&view->server->output);
-}
+        x = view->x;
+        y = view->y;
 
-static void
-server_new_xdg_surface(struct wl_listener *listener, void *data)
-{
-    struct tinyds_server *server;
+        if (lx >= x && lx <= w && ly >= y && ly <= h) {
+            if (sx)
+                *sx = lx - x;
+            if (sy)
+                *sy = ly - y;
 
-    server = wl_container_of(listener, server, new_xdg_surface);
+            return view;
+        }
+    }
 
-    server_add_view(server, (struct ds_xdg_surface *)data);
+    return NULL;
 }
 
 static void
@@ -595,16 +602,15 @@ server_next_focus(struct tinyds_server *server)
     if (wl_list_length(&server->views) < 2)
         return;
 
-    top = wl_container_of(server->views.prev, top, link);
-    next = wl_container_of(top->link.prev, next, link);
+    top = wl_container_of(server->views.next, top, link);
+    next = wl_container_of(top->link.next, next, link);
 
     server_focus_view(server, next);
 
     wl_list_remove(&top->link);
-    wl_list_insert(&server->views, &top->link);
+    wl_list_insert(server->views.prev, &top->link);
 
-    output_damage(&server->output);
-    output_redraw(&server->output);
+    output_damage(server->output);
 }
 
 static void
@@ -624,7 +630,7 @@ server_focus_view(struct tinyds_server *server, struct tinyds_view *view)
 
     if (view) {
         wl_list_remove(&view->link);
-        wl_list_insert(server->views.prev, &view->link);
+        wl_list_insert(&server->views, &view->link);
 
         ds_seat_keyboard_notify_enter(server->seat,
                 ds_xdg_surface_get_surface(view->xdg_surface),
@@ -633,147 +639,134 @@ server_focus_view(struct tinyds_server *server, struct tinyds_view *view)
     }
 }
 
-static bool
-server_init(struct tinyds_server *server, struct wl_display *display)
+static void
+server_add_output(struct tinyds_server *server, struct ds_output *ds_output,
+        int width, int height)
 {
-    server->display = display;
-
-    wl_list_init(&server->views);
+    struct tinyds_output *output;
 
-    if (wl_display_init_shm(display) != 0)
-        return false;
-
-    server->backend = create_wl_backend(display);
-    if (!server->backend)
-        return false;
+    if (server->output) {
+        ds_err("Output already exists. Not support multi output yet.");
+        return;
+    }
 
-    ds_wl_backend_create_output(server->backend);
+    output = calloc(1, sizeof *output);
+    if (!output) {
+        ds_err("Could not allocate memory for output");
+        return;
+    }
 
-    server->new_input.notify = server_handle_new_input;
-    ds_backend_add_new_input_listener(server->backend, &server->new_input);
+    output->server = server;
+    output->ds_output = ds_output;
+    output->width = width;
+    output->height = height;
+    output->drawable = true;
 
-    server->new_output.notify = server_handle_new_output;
-    ds_backend_add_new_output_listener(server->backend, &server->new_output);
+    ds_output_set_custom_mode(ds_output, width, height, 0);
 
-    server->compositor = ds_compositor_create(display);
-    if (!server->compositor)
-        goto err;
+    output->allocator = ds_shm_allocator_create();
+    if (!output->allocator) {
+        ds_err("Could not create shm_allocator");
+        goto err_alloc;
+    }
 
-    server->xdg_shell = ds_xdg_shell_create(display);
-    if (!server->xdg_shell)
-        goto err;
+    output->swapchain = ds_swapchain_create(output->allocator,
+            width, height, DRM_FORMAT_XRGB8888);
+    if (!output->swapchain) {
+        ds_err("Could not create swapchain");
+        goto err_swapchain;
+    }
 
-    server->new_xdg_surface.notify = server_new_xdg_surface;
-    ds_xdg_shell_add_new_surface_listener(server->xdg_shell,
-            &server->new_xdg_surface);
+    output->output_destroy.notify = output_handle_destroy;
+    ds_output_add_destroy_listener(output->ds_output, &output->output_destroy);
 
-    server->seat = ds_seat_create(display, "seat0" /* arbitrary name */);
-    if (!server->seat)
-        goto err;
+    output->output_frame.notify = output_handle_frame;
+    ds_output_add_frame_listener(output->ds_output, &output->output_frame);
 
-    if (!server_init_data_device(server))
-        goto err;
+    output_damage(output);
 
-    return true;
+    server->output = output;
 
-err:
-    ds_backend_destroy(server->backend);
+    return;
 
-    return false;
+err_swapchain:
+    ds_allocator_destroy(output->allocator);
+err_alloc:
+    free(output);
 }
 
 static void
-server_fini(struct tinyds_server *server)
+output_destroy(struct tinyds_output *output)
 {
-    struct tinyds_view *view, *tmp;
-
-    wl_list_for_each_safe(view, tmp, &server->views, link)
-        view_destroy(view);
-
-    output_fini(&server->output);
+    if (output->ds_output)
+        output_unlink_ds_output(output);
 
-    wl_list_remove(&server->new_xdg_surface.link);
+    ds_swapchain_destroy(output->swapchain);
+    ds_allocator_destroy(output->allocator);
+    free(output);
 }
 
 static void
-output_handle_destroy(struct wl_listener *listener, void *data)
+output_unlink_ds_output(struct tinyds_output *output)
 {
-    struct tinyds_output *output =
-        wl_container_of(listener, output, output_destroy);
-
     wl_list_remove(&output->output_destroy.link);
     wl_list_remove(&output->output_frame.link);
+
     output->ds_output = NULL;
+}
+
+static void
+output_handle_destroy(struct wl_listener *listener, void *data)
+{
+    struct tinyds_output *output;
 
+    output = wl_container_of(listener, output, output_destroy);
+    output_unlink_ds_output(output);
     wl_display_terminate(output->server->display);
 }
 
 static void
 output_handle_frame(struct wl_listener *listener, void *data)
 {
-    struct tinyds_output *output =
-        wl_container_of(listener, output, output_frame);
+    struct tinyds_output *output;
 
+    output = wl_container_of(listener, output, output_frame);
     output->drawable = true;
-    output_redraw(output);
+
+    if (output->damaged)
+        output_schedule_idle_redraw(output);
 }
 
-static bool
-output_init(struct tinyds_output *output, struct tinyds_server *server,
-        struct ds_output *ds_output, int width, int height)
+static void
+output_damage(struct tinyds_output *output)
 {
-    output->server = server;
-    output->ds_output = ds_output;
-    output->width = width;
-    output->height = height;
-    output->drawable = true;
-
-    ds_output_set_custom_mode(ds_output, OUTPUT_WIDTH, OUTPUT_HEIGHT, 0);
-
-    output->allocator = ds_shm_allocator_create();
-    if (!output->allocator)
-        return false;
-
-    output->swapchain = ds_swapchain_create(output->allocator,
-            width, height, DRM_FORMAT_XRGB8888);
-    if (!output->swapchain)
-        goto err_swapchain;
-
-    output->output_destroy.notify = output_handle_destroy;
-    ds_output_add_destroy_listener(output->ds_output, &output->output_destroy);
-
-    output->output_frame.notify = output_handle_frame;
-    ds_output_add_frame_listener(output->ds_output, &output->output_frame);
-
-    return true;
-
-err_swapchain:
-    ds_allocator_destroy(output->allocator);
-
-    return false;
+    output->damaged = true;
+    output_schedule_idle_redraw(output);
 }
 
 static void
-output_fini(struct tinyds_output *output)
+output_schedule_idle_redraw(struct tinyds_output *output)
 {
-    ds_output_destroy(output->ds_output);
-    ds_swapchain_destroy(output->swapchain);
-    ds_allocator_destroy(output->allocator);
-}
+    struct tinyds_server *server = output->server;
 
-static void
-output_damage(struct tinyds_output *output)
-{
-    output->damaged = true;
+    if (output->idle_redraw)
+        return;
+
+    output->idle_redraw =
+        wl_event_loop_add_idle(wl_display_get_event_loop(server->display),
+                output_handle_idle_redraw, output);
 }
 
 static void
-output_redraw(struct tinyds_output *output)
+output_handle_idle_redraw(void *data)
 {
+    struct tinyds_output *output = data;
     struct ds_buffer *output_buffer;
     pixman_image_t *output_image;
     struct tinyds_view *view;
 
+    output->idle_redraw = NULL;
+
     if (!output->drawable || !output->damaged)
         return;
 
@@ -788,7 +781,7 @@ output_redraw(struct tinyds_output *output)
 
     pixman_image_fill_color(output_image, 80, 80, 80);
 
-    wl_list_for_each(view, &output->server->views, link) {
+    wl_list_for_each_reverse(view, &output->server->views, link) {
         if (!view->mapped)
             continue;
         view_composite(view, output_image);
@@ -806,98 +799,205 @@ out:
 }
 
 static void
-server_handle_request_set_selection(struct wl_listener *listener, void *data)
+keyboard_handle_device_destroy(struct wl_listener *listener, void *data)
 {
-    struct ds_event_request_set_selection *event = data;
+    struct tinyds_keyboard *kbd;
 
-    ds_seat_set_selection(event->seat, event->source, event->serial);
+    kbd = wl_container_of(listener, kbd, destroy);
+
+    ds_inf("Keyboard(%p) destroyed", kbd);
+
+    wl_list_remove(&kbd->destroy.link);
+    wl_list_remove(&kbd->key.link);
+
+    free(kbd);
 }
 
 static void
-server_handle_request_data_offer_receive(struct wl_listener *listener, void *data)
+keyboard_handle_key(struct wl_listener *listener, void *data)
 {
-    struct ds_event_request_data_offer_receive *event = data;
+    struct tinyds_keyboard *kbd;
+    struct ds_event_keyboard_key *event = data;
+    struct ds_keyboard *ds_keyboard;
+    struct xkb_state *xkb_state;
+    const xkb_keysym_t *syms;
+    uint32_t modifiers;
+    int nsyms;
+    bool handled = false;
 
-    ds_data_offer_send(event->offer, event->mime_type, event->fd);
+    kbd = wl_container_of(listener, kbd, key);
+
+    ds_keyboard = ds_input_device_get_keyboard(kbd->dev);
+
+    modifiers = ds_keyboard_get_modifiers(ds_keyboard);
+    if ((modifiers & DS_MODIFIER_CTRL) &&
+            (modifiers & DS_MODIFIER_ALT) &&
+            (modifiers & DS_MODIFIER_SHIFT) &&
+            event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
+        xkb_state = ds_keyboard_get_xkb_state(ds_keyboard);
+        if (xkb_state) {
+            nsyms = xkb_state_key_get_syms(xkb_state, event->keycode + 8,
+                    &syms);
+            for (int i = 0; i < nsyms; i++) {
+                handled = server_handle_keybinding(kbd->server, syms[i]);
+            }
+        }
+    }
+
+    if (!handled) {
+        ds_seat_set_keyboard(kbd->server->seat, kbd->dev);
+        ds_seat_keyboard_notify_key(kbd->server->seat, event->time_msec,
+                event->keycode, event->state);
+    }
 }
 
-static bool
-server_init_data_device(struct tinyds_server *server)
+static void
+keyboard_handle_modifiers(struct wl_listener *listener, void *data)
 {
-    server->data_device = ds_data_device_manager_create(server->display);
-    if (!server->data_device)
-        return false;
+    struct tinyds_keyboard *kbd;
 
-    server->request_set_selection.notify =
-        server_handle_request_set_selection;
-    ds_data_device_manager_add_request_set_selection_listener(
-            server->data_device, &server->request_set_selection);
+    kbd = wl_container_of(listener, kbd, modifiers);
 
-    server->request_data_offer_receive.notify =
-        server_handle_request_data_offer_receive;
-    ds_data_device_manager_add_request_data_offer_receive_listener(
-            server->data_device, &server->request_data_offer_receive);
+    ds_seat_set_keyboard(kbd->server->seat, kbd->dev);
+    ds_seat_keyboard_notify_modifiers(kbd->server->seat,
+            &kbd->dev->keyboard->modifiers);
+}
 
-    return true;
+static void
+touch_handle_device_destroy(struct wl_listener *listener, void *data)
+{
+    struct tinyds_touch *touch;
+
+    touch = wl_container_of(listener, touch, destroy);
+
+    ds_inf("Touch(%p) destroyed", touch);
+
+    wl_list_remove(&touch->destroy.link);
+    wl_list_remove(&touch->down.link);
+    wl_list_remove(&touch->up.link);
+    wl_list_remove(&touch->motion.link);
+
+    free(touch);
 }
 
 static void
-server_add_view(struct tinyds_server *server, struct ds_xdg_surface *xdg_surface)
+touch_handle_down(struct wl_listener *listener, void *data)
 {
-    struct tinyds_view *view;
+    ds_inf("Touch device(%p): down", data);
+}
 
-    view = calloc(1, sizeof *view);
-    view->server = server;
-    view->xdg_surface = xdg_surface;
+static void
+touch_handle_up(struct wl_listener *listener, void *data)
+{
+    ds_inf("Touch device(%p): up", data);
+}
 
-    view->xdg_surface_map.notify = view_handle_xdg_surface_map;
-    ds_xdg_surface_add_map_listener(xdg_surface,
-            &view->xdg_surface_map);
+static void
+touch_handle_motion(struct wl_listener *listener, void *data)
+{
+    ds_inf("Touch device(%p): motion", data);
+}
 
-    view->xdg_surface_unmap.notify = view_handle_xdg_surface_unmap;
-    ds_xdg_surface_add_unmap_listener(xdg_surface,
-            &view->xdg_surface_unmap);
+static void
+pointer_handle_device_destroy(struct wl_listener *listener, void *data)
+{
+    struct tinyds_pointer *pointer;
 
-    view->xdg_surface_destroy.notify = view_handle_xdg_surface_destroy;
-    ds_xdg_surface_add_destroy_listener(xdg_surface,
-            &view->xdg_surface_destroy);
+    pointer = wl_container_of(listener, pointer, destroy);
 
-    view->surface_commit.notify = view_handle_surface_commit;
-    ds_surface_add_commit_listener(
-            ds_xdg_surface_get_surface(xdg_surface),
-            &view->surface_commit);
+    ds_inf("Pointer(%p) destroyed", pointer);
 
-    wl_list_insert(server->views.prev, &view->link);
+    wl_list_remove(&pointer->destroy.link);
+    wl_list_remove(&pointer->motion.link);
+    wl_list_remove(&pointer->motion_absolute.link);
+    wl_list_remove(&pointer->button.link);
+    wl_list_remove(&pointer->frame.link);
 
-    ds_inf("View(%p) added", view);
+    free(pointer);
 }
 
-static struct tinyds_view *
-server_view_at(struct tinyds_server *server, double lx, double ly,
-        double *sx, double *sy)
+static void
+pointer_handle_motion(struct wl_listener *listener, void *data)
+{
+    struct tinyds_pointer *pointer;
+
+    pointer = wl_container_of(listener, pointer, motion);
+
+    ds_inf("Pointer(%p) motion", pointer);
+}
+
+static void
+pointer_handle_motion_absolute(struct wl_listener *listener, void *data)
 {
+    struct tinyds_pointer *pointer;
+    struct ds_event_pointer_motion_absolute *event = data;
     struct tinyds_view *view;
-    struct ds_surface *surface;
-    struct ds_buffer *buffer;
-    int x, y, w = 0, h = 0;
+    double sx, sy;
 
-    wl_list_for_each(view, &server->views, link) {
-        surface = ds_xdg_surface_get_surface(view->xdg_surface);
-        buffer = ds_surface_get_buffer(surface);
-        ds_buffer_get_size(buffer, &w, &h);
+    pointer = wl_container_of(listener, pointer, motion_absolute);
 
-        x = view->x;
-        y = view->y;
+    ds_inf("Pointer(%p) motion absolute: (x %f y %f) time(%d)",
+            pointer, event->x, event->y, event->time_msec);
 
-        if (lx >= x && lx <= w && ly >= y && ly <= h) {
-            *sx = lx - x;
-            *sy = ly - y;
+    pointer->x = event->x * OUTPUT_WIDTH;
+    pointer->y = event->y * OUTPUT_HEIGHT;
 
-            return view;
+    view = server_view_at(pointer->server, pointer->x, pointer->y, &sx, &sy);
+    if (pointer->focused_view != view) {
+        if (pointer->focused_view) {
+            ds_inf("Clear pointer focus from view(%p)", pointer->focused_view);
+            ds_seat_pointer_notify_clear_focus(pointer->server->seat);
+            pointer->focused_view = NULL;
+        }
+
+        if (view) {
+            ds_inf("Set pointer focus to view(%p)", view);
+            ds_seat_pointer_notify_enter(pointer->server->seat,
+                    ds_xdg_surface_get_surface(view->xdg_surface), sx, sy);
+            pointer->focused_view = view;
         }
     }
 
-    return NULL;
+    if (view) {
+        ds_seat_pointer_notify_motion(pointer->server->seat,
+                event->time_msec, sx, sy);
+    }
+}
+
+static void
+pointer_handle_button(struct wl_listener *listener, void *data)
+{
+    struct tinyds_pointer *pointer;
+    struct ds_event_pointer_button *event = data;
+    struct tinyds_view *view;
+
+    pointer = wl_container_of(listener, pointer, button);
+
+    ds_inf("Pointer(%p) button(%d): state(%s) time(%d)",
+            pointer, event->button,
+            (event->state == DS_BUTTON_PRESSED) ? "Pressed" : "Released",
+            event->time_msec);
+
+    ds_seat_pointer_notify_button(pointer->server->seat, event->time_msec,
+            event->button, event->state);
+
+    if (event->state == DS_BUTTON_PRESSED) {
+        view = server_view_at(pointer->server, pointer->x, pointer->y,
+                NULL, NULL);
+        if (view)
+            server_focus_view(pointer->server, view);
+    }
+}
+
+static void
+pointer_handle_frame(struct wl_listener *listener, void *data)
+{
+    struct tinyds_pointer *pointer;
+
+    pointer = wl_container_of(listener, pointer, frame);
+
+    ds_inf("Pointer(%p) frame", pointer);
+    ds_seat_pointer_notify_frame(pointer->server->seat);
 }
 
 static void
@@ -914,13 +1014,42 @@ view_destroy(struct tinyds_view *view)
 }
 
 static void
-view_send_frame_done(struct tinyds_view *view)
+view_handle_xdg_surface_map(struct wl_listener *listener, void *data)
 {
-    struct timespec now;
+    struct tinyds_view *view;
 
-    clock_gettime(CLOCK_MONOTONIC, &now);
-    ds_surface_send_frame_done(ds_xdg_surface_get_surface(view->xdg_surface),
-            &now);
+    view = wl_container_of(listener, view, xdg_surface_map);
+    view->mapped = true;
+    server_focus_view(view->server, view);
+}
+
+static void
+view_handle_xdg_surface_unmap(struct wl_listener *listener, void *data)
+{
+    struct tinyds_view *view;
+
+    view = wl_container_of(listener, view, xdg_surface_unmap);
+    view->mapped = false;
+    output_damage(view->server->output);
+}
+
+static void
+view_handle_xdg_surface_destroy(struct wl_listener *listener, void *data)
+{
+    struct tinyds_view *view;
+
+    view = wl_container_of(listener, view, xdg_surface_destroy);
+    output_damage(view->server->output);
+    view_destroy(view);
+}
+
+static void
+view_handle_surface_commit(struct wl_listener *listener, void *data)
+{
+    struct tinyds_view *view;
+
+    view = wl_container_of(listener, view, surface_commit);
+    output_damage(view->server->output);
 }
 
 static void
@@ -947,3 +1076,13 @@ view_composite(struct tinyds_view *view, pixman_image_t *dst_image)
 
     view_send_frame_done(view);
 }
+
+static void
+view_send_frame_done(struct tinyds_view *view)
+{
+    struct timespec now;
+
+    clock_gettime(CLOCK_MONOTONIC, &now);
+    ds_surface_send_frame_done(ds_xdg_surface_get_surface(view->xdg_surface),
+            &now);
+}