1 #include "pixman-helper.h"
10 #include <drm_fourcc.h>
12 #include <wayland-server.h>
13 #include <libds/log.h>
14 #include <libds/backend.h>
15 #include <libds/allocator/shm.h>
16 #include <libds/backend/wayland.h>
17 #include <libds/swapchain.h>
18 #include <libds/compositor.h>
19 #include <libds/xdg_shell.h>
20 #include <libds/input_device.h>
21 #include <libds/keyboard.h>
22 #include <libds/touch.h>
23 #include <libds/pointer.h>
24 #include <libds/seat.h>
26 #define TINYDS_UNUSED __attribute__((unused))
28 #define OUTPUT_WIDTH 1280
29 #define OUTPUT_HEIGHT 720
35 struct ds_input_device *dev;
36 struct tinyds_server *server;
38 struct tinyds_view *focused_view;
40 struct wl_listener destroy;
41 struct wl_listener motion;
42 struct wl_listener motion_absolute;
43 struct wl_listener button;
44 struct wl_listener frame;
47 struct tinyds_keyboard
49 struct ds_input_device *dev;
50 struct tinyds_server *server;
52 struct wl_listener destroy;
53 struct wl_listener key;
58 struct ds_input_device *dev;
59 struct tinyds_server *server;
61 struct wl_listener destroy;
62 struct wl_listener down;
63 struct wl_listener up;
64 struct wl_listener motion;
69 struct tinyds_server *server;
70 struct ds_output *ds_output;
71 struct ds_allocator *allocator;
72 struct ds_swapchain *swapchain;
74 struct wl_listener output_destroy;
75 struct wl_listener output_frame;
85 struct wl_display *display;
87 struct ds_backend *backend;
88 struct ds_compositor *compositor;
89 struct ds_xdg_shell *xdg_shell;
92 struct tinyds_output output;
96 struct wl_listener new_output;
97 struct wl_listener new_input;
98 struct wl_listener new_xdg_surface;
103 struct tinyds_server *server;
105 struct ds_xdg_surface *xdg_surface;
107 struct wl_listener xdg_surface_map;
108 struct wl_listener xdg_surface_unmap;
109 struct wl_listener xdg_surface_destroy;
110 struct wl_listener surface_commit;
111 struct wl_list link; // tinyds_server::views
117 static bool server_init(struct tinyds_server *server,
118 struct wl_display *display);
119 static void server_fini(struct tinyds_server *server);
120 static void server_add_view(struct tinyds_server *server,
121 struct ds_xdg_surface *xdg_surface);
122 static struct tinyds_view *server_view_at(struct tinyds_server *server,
123 double lx, double ly, double *sx, double *sy);
124 static bool output_init(struct tinyds_output *output,
125 struct tinyds_server *server, struct ds_output *ds_output,
126 int width, int height);
127 static void output_fini(struct tinyds_output *output);
128 static void output_damage(struct tinyds_output *output);
129 static void output_redraw(struct tinyds_output *output);
130 static void view_destroy(struct tinyds_view *view);
131 static void view_composite(struct tinyds_view *view,
132 pixman_image_t *dst_image);
137 struct tinyds_server server;
138 struct wl_display *display;
141 ds_log_init(DS_DBG, NULL);
143 display = wl_display_create();
146 assert(server_init(&server, display) == true);
148 socket = wl_display_add_socket_auto(display);
151 ds_backend_start(server.backend);
153 output_damage(&server.output);
154 output_redraw(&server.output);
156 setenv("WAYLAND_DISPLAY", socket, true);
158 ds_inf("Running Wayland compositor on WAYLAND_DISPLAY=%s", socket);
160 wl_display_run(server.display);
162 server_fini(&server);
163 wl_display_destroy(display);
168 static struct ds_backend *
169 create_wl_backend(struct wl_display *display)
171 if (!getenv("WAYLAND_DISPLAY") && !getenv("WAYLAND_SOCKET"))
174 return ds_wl_backend_create(display, NULL);
178 server_handle_new_output(struct wl_listener *listener, void *data)
180 struct tinyds_server *server;
181 struct ds_output *ds_output = data;
183 server = wl_container_of(listener, server, new_output);
185 assert(output_init(&server->output, server, ds_output,
186 OUTPUT_WIDTH, OUTPUT_HEIGHT) == true);
190 keyboard_handle_device_destroy(struct wl_listener *listener, void *data)
192 struct tinyds_keyboard *kbd;
194 kbd = wl_container_of(listener, kbd, destroy);
196 ds_inf("Keyboard(%p) destroyed", kbd);
198 wl_list_remove(&kbd->destroy.link);
199 wl_list_remove(&kbd->key.link);
205 server_handle_keybinding(struct tinyds_server *server, xkb_keysym_t sym)
208 case XKB_KEY_BackSpace:
209 wl_display_terminate(server->display);
219 keyboard_handle_key(struct wl_listener *listener, void *data)
221 struct tinyds_keyboard *kbd;
222 struct ds_event_keyboard_key *event = data;
223 struct ds_keyboard *ds_keyboard;
224 struct xkb_state *xkb_state;
225 const xkb_keysym_t *syms;
229 kbd = wl_container_of(listener, kbd, key);
231 ds_keyboard = ds_input_device_get_keyboard(kbd->dev);
233 modifiers = ds_keyboard_get_modifiers(ds_keyboard);
234 if ((modifiers & DS_MODIFIER_CTRL) &&
235 (modifiers & DS_MODIFIER_ALT) &&
236 (modifiers & DS_MODIFIER_SHIFT) &&
237 event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
238 xkb_state = ds_keyboard_get_xkb_state(ds_keyboard);
240 nsyms = xkb_state_key_get_syms(xkb_state, event->keycode + 8,
242 for (int i = 0; i < nsyms; i++) {
243 server_handle_keybinding(kbd->server, syms[i]);
250 server_add_keyboard(struct tinyds_server *server, struct ds_input_device *dev)
252 struct tinyds_keyboard *kbd;
254 kbd = calloc(1, sizeof *kbd);
258 kbd->server = server;
260 kbd->destroy.notify = keyboard_handle_device_destroy;
261 ds_input_device_add_destroy_listener(dev, &kbd->destroy);
263 kbd->key.notify = keyboard_handle_key;
264 ds_keyboard_add_key_listener(ds_input_device_get_keyboard(dev), &kbd->key);
266 ds_inf("Keyboard(%p) added", kbd);
270 touch_handle_device_destroy(struct wl_listener *listener, void *data)
272 struct tinyds_touch *touch;
274 touch = wl_container_of(listener, touch, destroy);
276 ds_inf("Touch(%p) destroyed", touch);
278 wl_list_remove(&touch->destroy.link);
279 wl_list_remove(&touch->down.link);
280 wl_list_remove(&touch->up.link);
281 wl_list_remove(&touch->motion.link);
287 touch_handle_down(struct wl_listener *listener, void *data)
289 ds_inf("Touch device(%p): down", data);
293 touch_handle_up(struct wl_listener *listener, void *data)
295 ds_inf("Touch device(%p): up", data);
299 touch_handle_motion(struct wl_listener *listener, void *data)
301 ds_inf("Touch device(%p): motion", data);
305 server_add_touch(struct tinyds_server *server, struct ds_input_device *dev)
307 struct tinyds_touch *touch;
309 touch = calloc(1, sizeof *touch);
313 touch->server = server;
315 touch->destroy.notify = touch_handle_device_destroy;
316 ds_input_device_add_destroy_listener(dev, &touch->destroy);
318 touch->down.notify = touch_handle_down;
319 ds_touch_add_down_listener(ds_input_device_get_touch(dev), &touch->down);
321 touch->up.notify = touch_handle_up;
322 ds_touch_add_up_listener(ds_input_device_get_touch(dev), &touch->up);
324 touch->motion.notify = touch_handle_motion;
325 ds_touch_add_motion_listener(ds_input_device_get_touch(dev), &touch->motion);
327 ds_inf("Touch(%p) added", touch);
331 pointer_handle_device_destroy(struct wl_listener *listener, void *data)
333 struct tinyds_pointer *pointer;
335 pointer = wl_container_of(listener, pointer, destroy);
337 ds_inf("Pointer(%p) destroyed", pointer);
339 wl_list_remove(&pointer->destroy.link);
340 wl_list_remove(&pointer->motion.link);
341 wl_list_remove(&pointer->motion_absolute.link);
342 wl_list_remove(&pointer->button.link);
343 wl_list_remove(&pointer->frame.link);
349 pointer_handle_motion(struct wl_listener *listener, void *data)
351 struct tinyds_pointer *pointer;
353 pointer = wl_container_of(listener, pointer, motion);
355 ds_inf("Pointer(%p) motion", pointer);
359 pointer_handle_motion_absolute(struct wl_listener *listener, void *data)
361 struct tinyds_pointer *pointer;
362 struct ds_event_pointer_motion_absolute *event = data;
363 struct tinyds_view *view;
364 double ox, oy, sx, sy;
366 pointer = wl_container_of(listener, pointer, motion_absolute);
368 ds_inf("Pointer(%p) motion absolute: (x %f y %f) time(%d)",
369 pointer, event->x, event->y, event->time_msec);
371 ox = event->x * OUTPUT_WIDTH;
372 oy = event->y * OUTPUT_HEIGHT;
373 view = server_view_at(pointer->server, ox, oy, &sx, &sy);
375 if (pointer->focused_view != view) {
376 if (pointer->focused_view) {
377 ds_inf("Clear pointer focus from view(%p)", pointer->focused_view);
378 ds_seat_pointer_notify_clear_focus(pointer->server->seat);
379 pointer->focused_view = NULL;
383 ds_inf("Set pointer focus to view(%p)", view);
384 ds_seat_pointer_notify_enter(pointer->server->seat,
385 ds_xdg_surface_get_surface(view->xdg_surface), sx, sy);
386 pointer->focused_view = view;
391 ds_seat_pointer_notify_motion(pointer->server->seat,
392 event->time_msec, sx, sy);
397 pointer_handle_button(struct wl_listener *listener, void *data)
399 struct tinyds_pointer *pointer;
400 struct ds_event_pointer_button *event = data;
402 pointer = wl_container_of(listener, pointer, button);
404 ds_inf("Pointer(%p) button(%d): state(%s) time(%d)",
405 pointer, event->button,
406 (event->state == DS_BUTTON_PRESSED) ? "Pressed" : "Released",
411 pointer_handle_frame(struct wl_listener *listener, void *data)
413 struct tinyds_pointer *pointer;
415 pointer = wl_container_of(listener, pointer, frame);
417 ds_inf("Pointer(%p) frame", pointer);
418 ds_seat_pointer_notify_frame(pointer->server->seat);
422 server_add_pointer(struct tinyds_server *server, struct ds_input_device *dev)
424 struct tinyds_pointer *pointer;
426 pointer = calloc(1, sizeof *pointer);
430 pointer->server = server;
432 pointer->destroy.notify = pointer_handle_device_destroy;
433 ds_input_device_add_destroy_listener(dev, &pointer->destroy);
435 pointer->motion.notify = pointer_handle_motion;
436 ds_pointer_add_motion_listener(ds_input_device_get_pointer(dev),
439 pointer->motion_absolute.notify = pointer_handle_motion_absolute;
440 ds_pointer_add_motion_absolute_listener(ds_input_device_get_pointer(dev),
441 &pointer->motion_absolute);
443 pointer->button.notify = pointer_handle_button;
444 ds_pointer_add_button_listener(ds_input_device_get_pointer(dev),
447 pointer->frame.notify = pointer_handle_frame;
448 ds_pointer_add_frame_listener(ds_input_device_get_pointer(dev),
451 ds_inf("Pointer(%p) added", pointer);
455 server_handle_new_input(struct wl_listener *listener, void *data)
457 struct tinyds_server *server;
458 struct ds_input_device *dev = data;
459 enum ds_input_device_type dev_type;
461 server = wl_container_of(listener, server, new_input);
463 dev_type = ds_input_device_get_type(dev);
465 case DS_INPUT_DEVICE_KEYBOARD:
466 server_add_keyboard(server, dev);
468 case DS_INPUT_DEVICE_TOUCH:
469 server_add_touch(server, dev);
471 case DS_INPUT_DEVICE_POINTER:
472 server_add_pointer(server, dev);
473 ds_seat_set_capabilities(server->seat, WL_SEAT_CAPABILITY_POINTER);
476 ds_err("Unknown type(%d) of ds_input_device", dev_type);
482 view_handle_xdg_surface_map(struct wl_listener *listener,
483 void *data TINYDS_UNUSED)
485 struct tinyds_view *view;
487 view = wl_container_of(listener, view, xdg_surface_map);
492 view_handle_xdg_surface_unmap(struct wl_listener *listener,
493 void *data TINYDS_UNUSED)
495 struct tinyds_view *view;
497 view = wl_container_of(listener, view, xdg_surface_unmap);
498 view->mapped = false;
502 view_handle_xdg_surface_destroy(struct wl_listener *listener,
503 void *data TINYDS_UNUSED)
505 struct tinyds_view *view;
507 view = wl_container_of(listener, view, xdg_surface_destroy);
509 output_damage(&view->server->output);
510 output_redraw(&view->server->output);
516 view_handle_surface_commit(struct wl_listener *listener,
517 void *data TINYDS_UNUSED)
519 struct tinyds_view *view;
521 view = wl_container_of(listener, view, surface_commit);
523 output_damage(&view->server->output);
524 output_redraw(&view->server->output);
528 server_new_xdg_surface(struct wl_listener *listener, void *data)
530 struct tinyds_server *server;
532 server = wl_container_of(listener, server, new_xdg_surface);
534 server_add_view(server, (struct ds_xdg_surface *)data);
538 server_init(struct tinyds_server *server, struct wl_display *display)
540 server->display = display;
542 wl_list_init(&server->views);
544 if (wl_display_init_shm(display) != 0)
547 server->backend = create_wl_backend(display);
548 if (!server->backend)
551 ds_wl_backend_create_output(server->backend);
553 server->new_input.notify = server_handle_new_input;
554 ds_backend_add_new_input_listener(server->backend, &server->new_input);
556 server->new_output.notify = server_handle_new_output;
557 ds_backend_add_new_output_listener(server->backend, &server->new_output);
559 server->compositor = ds_compositor_create(display);
560 if (!server->compositor)
563 server->xdg_shell = ds_xdg_shell_create(display);
564 if (!server->xdg_shell)
567 server->new_xdg_surface.notify = server_new_xdg_surface;
568 ds_xdg_shell_add_new_surface_listener(server->xdg_shell,
569 &server->new_xdg_surface);
571 server->seat = ds_seat_create(display, "seat0" /* arbitrary name */);
578 ds_backend_destroy(server->backend);
584 server_fini(struct tinyds_server *server)
586 struct tinyds_view *view, *tmp;
588 wl_list_for_each_safe(view, tmp, &server->views, link)
591 output_fini(&server->output);
593 wl_list_remove(&server->new_xdg_surface.link);
597 output_handle_destroy(struct wl_listener *listener, void *data TINYDS_UNUSED)
599 struct tinyds_output *output =
600 wl_container_of(listener, output, output_destroy);
602 wl_list_remove(&output->output_destroy.link);
603 wl_list_remove(&output->output_frame.link);
604 output->ds_output = NULL;
606 wl_display_terminate(output->server->display);
610 output_handle_frame(struct wl_listener *listener, void *data TINYDS_UNUSED)
612 struct tinyds_output *output =
613 wl_container_of(listener, output, output_frame);
615 output->drawable = true;
616 output_redraw(output);
620 output_init(struct tinyds_output *output, struct tinyds_server *server,
621 struct ds_output *ds_output, int width, int height)
623 output->server = server;
624 output->ds_output = ds_output;
625 output->width = width;
626 output->height = height;
627 output->drawable = true;
629 ds_output_set_custom_mode(ds_output, OUTPUT_WIDTH, OUTPUT_HEIGHT, 0);
631 output->allocator = ds_shm_allocator_create();
632 if (!output->allocator)
635 output->swapchain = ds_swapchain_create(output->allocator,
636 width, height, DRM_FORMAT_XRGB8888);
637 if (!output->swapchain)
640 output->output_destroy.notify = output_handle_destroy;
641 ds_output_add_destroy_listener(output->ds_output, &output->output_destroy);
643 output->output_frame.notify = output_handle_frame;
644 ds_output_add_frame_listener(output->ds_output, &output->output_frame);
649 ds_allocator_destroy(output->allocator);
655 output_fini(struct tinyds_output *output)
657 ds_output_destroy(output->ds_output);
658 ds_swapchain_destroy(output->swapchain);
659 ds_allocator_destroy(output->allocator);
663 output_damage(struct tinyds_output *output)
665 output->damaged = true;
669 output_redraw(struct tinyds_output *output)
671 struct ds_buffer *output_buffer;
672 pixman_image_t *output_image;
673 struct tinyds_view *view;
675 if (!output->drawable || !output->damaged)
678 output_buffer = ds_swapchain_acquire(output->swapchain, NULL);
682 output_image = pixman_image_from_buffer(output_buffer,
683 DS_BUFFER_DATA_PTR_ACCESS_WRITE);
687 pixman_image_fill_color(output_image, 80, 80, 80);
689 wl_list_for_each(view, &output->server->views, link) {
692 view_composite(view, output_image);
694 pixman_image_unref(output_image);
696 ds_output_attach_buffer(output->ds_output, output_buffer);
697 ds_output_commit(output->ds_output);
699 output->drawable = false;
700 output->damaged = false;
703 ds_buffer_unlock(output_buffer);
707 server_add_view(struct tinyds_server *server, struct ds_xdg_surface *xdg_surface)
709 struct tinyds_view *view;
711 view = calloc(1, sizeof *view);
712 view->server = server;
713 view->xdg_surface = xdg_surface;
715 view->xdg_surface_map.notify = view_handle_xdg_surface_map;
716 ds_xdg_surface_add_map_listener(xdg_surface,
717 &view->xdg_surface_map);
719 view->xdg_surface_unmap.notify = view_handle_xdg_surface_unmap;
720 ds_xdg_surface_add_unmap_listener(xdg_surface,
721 &view->xdg_surface_unmap);
723 view->xdg_surface_destroy.notify = view_handle_xdg_surface_destroy;
724 ds_xdg_surface_add_destroy_listener(xdg_surface,
725 &view->xdg_surface_destroy);
727 view->surface_commit.notify = view_handle_surface_commit;
728 ds_surface_add_commit_listener(
729 ds_xdg_surface_get_surface(xdg_surface),
730 &view->surface_commit);
732 wl_list_insert(server->views.prev, &view->link);
734 ds_inf("View(%p) added", view);
737 static struct tinyds_view *
738 server_view_at(struct tinyds_server *server, double lx, double ly,
739 double *sx, double *sy)
741 struct tinyds_view *view;
742 struct ds_surface *surface;
743 struct ds_buffer *buffer;
744 int x, y, w = 0, h = 0;
746 wl_list_for_each(view, &server->views, link) {
747 surface = ds_xdg_surface_get_surface(view->xdg_surface);
748 buffer = ds_surface_get_buffer(surface);
749 ds_buffer_get_size(buffer, &w, &h);
754 if (lx >= x && lx <= w && ly >= y && ly <= h) {
766 view_destroy(struct tinyds_view *view)
768 ds_inf("View(%p) destroyed", view);
770 wl_list_remove(&view->xdg_surface_destroy.link);
771 wl_list_remove(&view->xdg_surface_map.link);
772 wl_list_remove(&view->xdg_surface_unmap.link);
773 wl_list_remove(&view->surface_commit.link);
774 wl_list_remove(&view->link);
779 view_send_frame_done(struct tinyds_view *view)
783 clock_gettime(CLOCK_MONOTONIC, &now);
784 ds_surface_send_frame_done(ds_xdg_surface_get_surface(view->xdg_surface),
789 view_composite(struct tinyds_view *view, pixman_image_t *dst_image)
791 struct ds_buffer *buffer;
792 pixman_image_t *src_image;
794 buffer = ds_surface_get_buffer(
795 ds_xdg_surface_get_surface(view->xdg_surface));
799 src_image = pixman_image_from_buffer(buffer,
800 DS_BUFFER_DATA_PTR_ACCESS_READ);
801 pixman_image_composite32(PIXMAN_OP_OVER,
806 pixman_image_get_width(src_image),
807 pixman_image_get_height(src_image));
808 pixman_image_unref(src_image);
810 view_send_frame_done(view);