-#include <assert.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <time.h>
-
-#include <drm_fourcc.h>
-#include <pixman.h>
-#include <wayland-server.h>
-#include <libds/log.h>
-#include <libds/backend.h>
-#include <libds/output.h>
-#include <libds/compositor.h>
-#include <libds/xdg_shell.h>
-#include <libds-tizen/allocator/tbm.h>
-#include <libds-tizen/backend/tdm.h>
-#include <libds-tizen/tbm_server.h>
-#include <libds-tizen/backend/tdm_output_hwc.h>
-
-#define USE_TDM_BUFFER_QUEUE
-
-#ifdef USE_TDM_BUFFER_QUEUE
-#include "pixman-tbm-helper.h"
-#include "tinyds-tdm-renderer.h"
-#else
-#include <libds/swapchain.h>
-#endif
-
-#include "pixman-helper.h"
-
-#define TINYDS_UNUSED __attribute__((unused))
-
-struct tinyds_output
-{
- struct tinyds_server *server;
- struct ds_output *ds_output;
- struct ds_allocator *allocator;
-#ifdef USE_TDM_BUFFER_QUEUE
- struct tinyds_renderer renderer;
- struct ds_tdm_buffer_queue *buffer_queue;
- struct wl_listener buffer_queue_acquirable;
-#else
- struct ds_swapchain *swapchain;
-#endif
- struct ds_buffer *front_buffer;
-
- struct wl_listener output_destroy;
- struct wl_listener output_frame;
-
- int width, height;
-
- struct wl_event_source *idle_commit;
- bool commitable;
- bool damaged;
-
- struct ds_tdm_output_hwc *hwc;
-};
-
-struct tinyds_server
-{
- struct ds_tbm_server *tbm_server;
-
- struct wl_display *display;
-
- struct ds_backend *backend;
- struct ds_compositor *compositor;
- struct ds_xdg_shell *xdg_shell;
-
- struct tinyds_output *output;
- struct wl_event_source *stdin_source;
-
- struct wl_list views;
-
- struct wl_listener new_output;
- struct wl_listener new_xdg_surface;
-};
-
-struct tinyds_view
-{
- struct tinyds_server *server;
-
- struct tinyds_texture *texture;
- struct ds_xdg_surface *xdg_surface;
-
- struct wl_listener xdg_surface_map;
- struct wl_listener xdg_surface_unmap;
- struct wl_listener xdg_surface_destroy;
- struct wl_listener surface_commit;
- struct wl_list link; // tinyds_server::views
-
- struct ds_tdm_output_hwc_window *hwc_window;
-
- int x, y;
- bool mapped;
-};
-
-struct tinyds_server tinyds;
-
-static bool init_server(struct tinyds_server *server, struct wl_display *display);
-static int server_dispatch_stdin(int fd, uint32_t mask, void *data);
-static void output_handle_destroy(struct wl_listener *listener, void *data);
-static void output_handle_frame(struct wl_listener *listener, void *data);
-static void draw_server_with_damage(struct tinyds_server *server);
-static void draw_output(struct tinyds_output *output);
-static void output_swap_buffer(struct tinyds_output *output,
- struct ds_buffer *buffer);
-static void view_send_frame_done(struct tinyds_view *view);
-static void output_hwc_init(struct tinyds_output *output);
-static void output_schedule_commit(struct tinyds_output *output);
-static void output_commit(struct tinyds_output *output);
-#ifdef USE_TDM_BUFFER_QUEUE
-static void output_buffer_queue_init(struct tinyds_output *output);
-static void output_renderer_init(struct tinyds_output *output);
-static void output_draw_with_renderer(struct tinyds_output *output);
-#else
-static void output_swapchain_init(struct tinyds_output *output,
- int width, int height, uint32_t format);
-static void output_draw_with_swapchain(struct tinyds_output *output);
-static void draw_view(struct tinyds_view *view, pixman_image_t *dst_image);
-#endif
-
-int
-main(void)
-{
- struct tinyds_server *server = &tinyds;
- struct wl_display *display;
- struct wl_event_loop *loop;
- const char *socket;
- bool res;
-
- ds_log_init(DS_INF, NULL);
-
- display = wl_display_create();
- assert(display);
-
- res = init_server(server, display);
- assert(res);
-
- socket = wl_display_add_socket_auto(display);
- assert(socket);
-
- ds_backend_start(server->backend);
-
- setenv("WAYLAND_DISPLAY", socket, true);
-
- ds_inf("Running Wayland compositor on WAYLAND_DISPLAY=%s", socket);
-
- loop = wl_display_get_event_loop(display);
- server->stdin_source = wl_event_loop_add_fd(loop, STDIN_FILENO,
- WL_EVENT_READABLE, server_dispatch_stdin, server);
-
- wl_display_run(display);
-
- wl_display_destroy_clients(display);
- wl_display_destroy(display);
-
- return 0;
-}
-
-static void
-view_handle_xdg_surface_map(struct wl_listener *listener,
- void *data TINYDS_UNUSED)
-{
- struct tinyds_view *view;
-
- view = wl_container_of(listener, view, xdg_surface_map);
- view->mapped = true;
-}
-
-static void
-view_handle_xdg_surface_unmap(struct wl_listener *listener,
- void *data TINYDS_UNUSED)
-{
- struct tinyds_view *view;
-
- view = wl_container_of(listener, view, xdg_surface_unmap);
- view->mapped = false;
-}
-
-static void
-view_handle_xdg_surface_destroy(struct wl_listener *listener,
- void *data TINYDS_UNUSED)
-{
- struct tinyds_view *view;
-
- view = wl_container_of(listener, view, xdg_surface_destroy);
-
- draw_server_with_damage(view->server);
-
- ds_tdm_output_hwc_window_destroy(view->hwc_window);
-
- wl_list_remove(&view->xdg_surface_destroy.link);
- wl_list_remove(&view->xdg_surface_map.link);
- wl_list_remove(&view->xdg_surface_unmap.link);
- wl_list_remove(&view->surface_commit.link);
- wl_list_remove(&view->link);
- free(view);
-}
-
-static void
-view_handle_surface_commit(struct wl_listener *listener,
- void *data TINYDS_UNUSED)
-{
- struct tinyds_view *view;
-
- view = wl_container_of(listener, view, surface_commit);
-
- draw_server_with_damage(view->server);
-}
-
-static void
-server_new_xdg_surface(struct wl_listener *listener, void *data)
-{
- struct tinyds_server *server;
- struct tinyds_view *view;
- struct ds_xdg_surface *xdg_surface;
-
- server = wl_container_of(listener, server, new_xdg_surface);
- xdg_surface = data;
-
- ds_inf("New xdg_surface(%p)", (void *)xdg_surface);
-
- view = calloc(1, sizeof *view);
- assert(view);
-
- view->server = server;
- view->xdg_surface = xdg_surface;
-
- view->xdg_surface_map.notify = view_handle_xdg_surface_map;
- ds_xdg_surface_add_map_listener(xdg_surface,
- &view->xdg_surface_map);
-
- view->xdg_surface_unmap.notify = view_handle_xdg_surface_unmap;
- ds_xdg_surface_add_unmap_listener(xdg_surface,
- &view->xdg_surface_unmap);
-
- view->xdg_surface_destroy.notify = view_handle_xdg_surface_destroy;
- ds_xdg_surface_add_destroy_listener(xdg_surface,
- &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);
-
- view->hwc_window = ds_tdm_output_hwc_window_create(server->output->hwc);
-
- wl_list_insert(server->views.prev, &view->link);
-}
-
-static void
-backend_handle_new_output(struct wl_listener *listener, void *data)
-{
- struct tinyds_server *server;
- struct tinyds_output *output;
- struct ds_output *ds_output;
- const struct ds_output_mode *mode;
-
- server = wl_container_of(listener, server, new_output);
- ds_output = data;
-
- ds_inf("New output(%p)", ds_output);
-
- if (server->output)
- return;
-
- mode = ds_output_get_preferred_mode(ds_output);
- ds_output_set_mode(ds_output, mode);
-
- output = calloc(1, sizeof *output);
- if (!output)
- return;
-
- output->server = server;
- output->ds_output = ds_output;
- output->width = mode->width;
- output->height = mode->height;
- output->damaged = true;
- output->commitable = true;
-
-#ifdef USE_TDM_BUFFER_QUEUE
- output_buffer_queue_init(output);
- output_renderer_init(output);
-#else
- output_swapchain_init(output, mode->width, mode->height,
- DRM_FORMAT_XRGB8888);
-#endif
-
- output_hwc_init(output);
-
- output->output_destroy.notify = output_handle_destroy;
- ds_output_add_destroy_listener(ds_output, &output->output_destroy);
-
- output->output_frame.notify = output_handle_frame;
- ds_output_add_frame_listener(ds_output, &output->output_frame);
-
- server->output = output;
-
- output_schedule_commit(output);
-}
-
-static bool
-init_server(struct tinyds_server *server, struct wl_display *display)
-{
- server->display = display;
-
- wl_list_init(&server->views);
-
- if (wl_display_init_shm(display) != 0)
- return false;
-
- server->backend = ds_tdm_backend_create(display);
- if (!server->backend)
- return false;
-
- server->new_output.notify = backend_handle_new_output;
- ds_backend_add_new_output_listener(server->backend,
- &server->new_output);
-
- server->compositor = ds_compositor_create(display);
- if (!server->compositor) {
- ds_backend_destroy(server->backend);
- return false;
- }
-
- server->tbm_server = ds_tbm_server_create(display);
- if (!server->tbm_server) {
- ds_backend_destroy(server->backend);
- return false;
- }
-
- server->xdg_shell = ds_xdg_shell_create(display);
- if (!server->xdg_shell) {
- ds_backend_destroy(server->backend);
- return false;
- }
-
- server->new_xdg_surface.notify = server_new_xdg_surface;
- ds_xdg_shell_add_new_surface_listener(server->xdg_shell,
- &server->new_xdg_surface);
-
- return true;
-}
-
-static void
-output_handle_destroy(struct wl_listener *listener, void *data TINYDS_UNUSED)
-{
- 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);
-
- if (output->front_buffer)
- ds_buffer_unlock(output->front_buffer);
-
-#ifdef USE_TDM_BUFFER_QUEUE
- fini_renderer(&output->renderer);
-#else
- if (output->swapchain)
- ds_swapchain_destroy(output->swapchain);
-
- if (output->allocator)
- ds_allocator_destroy(output->allocator);
-#endif
-
- wl_display_terminate(output->server->display);
-
- output->server->output = NULL;
-
- free(output);
-}
-static void
-output_commit(struct tinyds_output *output)
-{
- uint32_t num_changed = 0;
- uint32_t num_windows = 0, current_num_windows = 0;
- struct ds_tdm_output_hwc_window **composited_hwc_windows = NULL;
- struct ds_tdm_output_hwc_window **changed_hwc_windows = NULL;
- enum ds_tdm_output_hwc_window_composition composition;
- struct tinyds_view *view;
- int i;
- bool need_target = false;
-
- if (!output->commitable || !output->damaged)
- return;
-
- wl_list_for_each_reverse(view, &output->server->views, link) {
- if (!view->hwc_window)
- continue;
-
- if (view->mapped)
- num_windows++;
- }
-
- if (num_windows) {
- composited_hwc_windows = calloc(num_windows, sizeof *composited_hwc_windows);
- if (!composited_hwc_windows)
- return;
- }
-
- wl_list_for_each_reverse(view, &output->server->views, link) {
- struct ds_buffer *ds_buffer;
- struct ds_tdm_box src_box;
- int w = 0, h = 0;
-
- if (!view->hwc_window)
- continue;
-
- ds_buffer = ds_surface_get_buffer(
- ds_xdg_surface_get_surface(view->xdg_surface));
- if (!ds_buffer)
- continue;
-
- ds_tdm_output_hwc_window_set_buffer(view->hwc_window, ds_buffer);
-
- if (ds_buffer)
- ds_buffer_get_size(ds_buffer, &w, &h);
-
- src_box.x = 0;
- src_box.y = 0;
- src_box.width = w;
- src_box.height = h;
-
- ds_tdm_output_hwc_window_set_src_box(view->hwc_window, &src_box);
- ds_tdm_output_hwc_window_set_position(view->hwc_window, view->x, view->y);
- ds_tdm_output_hwc_window_set_dest_size(view->hwc_window, w, h);
- ds_tdm_output_hwc_window_set_transform(view->hwc_window, WL_OUTPUT_TRANSFORM_NORMAL);
-
- if (view->mapped) {
- ds_tdm_output_hwc_window_set_composition(view->hwc_window,
- DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_DEVICE);
-
- composited_hwc_windows[current_num_windows] = view->hwc_window;
- current_num_windows++;
- } else {
- ds_tdm_output_hwc_window_set_composition(view->hwc_window,
- DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_NONE);
- }
- }
-
- if (!ds_tdm_output_hwc_validate(output->hwc, composited_hwc_windows,
- num_windows, &num_changed)) {
- free(composited_hwc_windows);
- ds_err("Could not hwc validate");
- return;
- }
-
- if (composited_hwc_windows)
- free(composited_hwc_windows);
-
- if (num_changed > 0) {
- changed_hwc_windows = calloc(num_windows, sizeof *changed_hwc_windows);
- if (!changed_hwc_windows)
- return;
-
- if (!ds_tdm_output_hwc_get_changed_composition(output->hwc, &num_changed,
- changed_hwc_windows)) {
- ds_err("Could not get chaged composition");
- return;
- }
-
- for (i = 0; i < num_changed; i++) {
- composition = ds_tdm_output_hwc_window_get_composition(changed_hwc_windows[i]);
- if (composition == DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_CLIENT) {
- need_target = true;
- break;
- }
- }
- }
-
- if (changed_hwc_windows)
- free(changed_hwc_windows);
-
- if (!ds_tdm_output_hwc_accept_validation(output->hwc)) {
- ds_err("Could not hwc accept validateion");
- return;
- }
-
- if (need_target)
- draw_output(output);
-
-#ifdef USE_TDM_BUFFER_QUEUE
- struct ds_buffer *buffer;
-
- buffer = ds_tdm_buffer_queue_acquire(output->buffer_queue);
- if (buffer)
- output_swap_buffer(output, buffer);
-#endif
-
- ds_output_commit(output->ds_output);
-
- output->commitable = false;
- output->damaged = false;
-
- wl_list_for_each(view, &output->server->views, link) {
- enum ds_tdm_output_hwc_window_composition composition;
-
- if (!view->mapped)
- continue;
-
- composition = ds_tdm_output_hwc_window_get_composition(view->hwc_window);
- if ((composition == DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_DEVICE) ||
- (composition == DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_VIDEO) ||
- (composition == DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_CURSOR))
- view_send_frame_done(view);
- }
-
- ds_inf("output:%p commit", output);
-}
-
-static void
-output_handle_frame(struct wl_listener *listener, void *data TINYDS_UNUSED)
-{
- struct tinyds_output *output =
- wl_container_of(listener, output, output_frame);
-
- ds_inf("output:%p handle frame", output);
-
- output->commitable = true;
-
- output_commit(output);
-}
-
-static void
-draw_server_with_damage(struct tinyds_server *server)
-{
- server->output->damaged = true;
- output_schedule_commit(server->output);
-}
-
-static void
-output_hwc_init(struct tinyds_output *output)
-{
- struct ds_tdm_output *tdm_output;
-
- tdm_output = ds_tdm_output_from_output(output->ds_output);
- assert(tdm_output);
-
- output->hwc = ds_tdm_output_get_hwc(tdm_output);
- assert(output->hwc);
-
- ds_tdm_output_hwc_set_enabled(output->hwc, true);
-}
-
-#ifdef USE_TDM_BUFFER_QUEUE
-static void
-output_handle_buffer_queue_acquirable(struct wl_listener *listener,
- void *data TINYDS_UNUSED)
-{
- struct tinyds_output *output;
-
- output = wl_container_of(listener, output, buffer_queue_acquirable);
-
- draw_server_with_damage(output->server);
-}
-
-static void
-output_buffer_queue_init(struct tinyds_output *output)
-{
- struct ds_tdm_output *tdm_output;
-
- tdm_output = ds_tdm_output_from_output(output->ds_output);
- assert(tdm_output);
-
- output->buffer_queue = ds_tdm_output_get_buffer_queue(tdm_output);
- assert(output->buffer_queue);
-
- output->buffer_queue_acquirable.notify =
- output_handle_buffer_queue_acquirable;
- ds_tdm_buffer_queue_add_acquirable_listener(output->buffer_queue,
- &output->buffer_queue_acquirable);
-}
-
-static void
-output_renderer_init(struct tinyds_output *output)
-{
- init_renderer(&output->renderer);
-
- renderer_set_surface_queue(&output->renderer,
- ds_tdm_buffer_queue_get_native_queue(output->buffer_queue));
-
- renderer_set_bg_color(&output->renderer, 0, 0, 0);
-}
-
-static void
-output_draw_with_renderer(struct tinyds_output *output)
-{
- struct tinyds_view *view;
-
- ds_dbg(">> BEGIN UPDATE TEXTURES");
-
- wl_list_for_each(view, &output->server->views, link) {
- struct ds_buffer *ds_buffer;
- struct ds_tbm_client_buffer *tbm_buffer;
- tbm_surface_h surface;
- enum ds_tdm_output_hwc_window_composition composition;
-
- if (!view->mapped)
- continue;
-
- composition = ds_tdm_output_hwc_window_get_composition(view->hwc_window);
- if ((composition == DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_DEVICE) ||
- (composition == DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_VIDEO) ||
- (composition == DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_CURSOR))
- continue;
-
- ds_buffer = ds_surface_get_buffer(
- ds_xdg_surface_get_surface(view->xdg_surface));
- assert(ds_buffer);
-
- tbm_buffer = ds_tbm_client_buffer_from_buffer(ds_buffer);
- assert(tbm_buffer);
-
- surface = ds_tbm_client_buffer_get_tbm_surface(tbm_buffer);
-
- renderer_add_texture(&output->renderer, surface, view->x, view->y);
-
- view_send_frame_done(view);
- }
-
- ds_dbg("<< END UPDATE TEXTURES");
-
- renderer_draw(&output->renderer);
-}
-
-#else
-static void
-output_swapchain_init(struct tinyds_output *output,
- int width, int height, uint32_t format)
-
-{
- output->allocator = ds_tbm_allocator_create();
- assert(output->allocator);
-
- output->swapchain = ds_swapchain_create(output->allocator,
- width, height, format);
- assert(output->swapchain);
-}
-
-static void
-output_draw_with_swapchain(struct tinyds_output *output)
-{
- struct tinyds_view *view;
- struct ds_buffer *output_buffer;
- pixman_image_t *output_image;
- enum ds_tdm_output_hwc_window_composition composition;
-
- output_buffer = ds_swapchain_acquire(output->swapchain, NULL);
- if (!output_buffer)
- return;
-
- output_image = pixman_image_from_buffer(output_buffer,
- DS_BUFFER_DATA_PTR_ACCESS_WRITE);
- if (!output_image) {
- ds_buffer_unlock(output_buffer);
- return;
- }
-
- pixman_image_fill_color(output_image, 0, 0, 0);
-
- wl_list_for_each(view, &output->server->views, link) {
- if (!view->mapped)
- continue;
-
- composition = ds_tdm_output_hwc_window_get_composition(view->hwc_window);
- if ((composition == DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_DEVICE) ||
- (composition == DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_VIDEO) ||
- (composition == DS_TDM_OUTPUT_HWC_WINDOW_COMPOSITION_CURSOR))
- continue;
-
- draw_view(view, output_image);
- }
- pixman_image_unref(output_image);
-
- output_swap_buffer(output, output_buffer);
-}
-
-static void
-draw_view(struct tinyds_view *view, pixman_image_t *dst_image)
-{
- struct ds_buffer *buffer;
- pixman_image_t *src_image;
-
- buffer = ds_surface_get_buffer(
- ds_xdg_surface_get_surface(view->xdg_surface));
- if (!buffer)
- return;
-
- src_image = pixman_image_from_buffer(buffer,
- DS_BUFFER_DATA_PTR_ACCESS_READ);
- pixman_image_composite32(PIXMAN_OP_OVER,
- src_image,
- NULL,
- dst_image,
- 0, 0, 0, 0,
- view->x, view->y,
- pixman_image_get_width(src_image),
- pixman_image_get_height(src_image));
- pixman_image_unref(src_image);
-
- view_send_frame_done(view);
-}
-#endif
-
-static void
-draw_output(struct tinyds_output *output)
-{
-#ifdef USE_TDM_BUFFER_QUEUE
- output_draw_with_renderer(output);
-#else
- output_draw_with_swapchain(output);
-#endif
-
- ds_inf("output:%p draw", output);
-}
-
-static void
-output_swap_buffer(struct tinyds_output *output, struct ds_buffer *buffer)
-{
- ds_output_attach_buffer(output->ds_output, buffer);
-
- if (output->front_buffer)
- ds_buffer_unlock(output->front_buffer);
- output->front_buffer = buffer;
-}
-
-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);
-}
-
-static void
-output_schedule_commit_handle_idle_timer(void *data)
-{
- struct tinyds_output *output = data;
- output->idle_commit = NULL;
-
- output_commit(output);
-}
-
-static void
-output_schedule_commit(struct tinyds_output *output)
-{
- if (output->idle_commit)
- return;
-
- output->damaged = true;
-
- struct wl_event_loop *ev = wl_display_get_event_loop(output->server->display);
- output->idle_commit =
- wl_event_loop_add_idle(ev, output_schedule_commit_handle_idle_timer, output);
-}
-
-static int
-server_dispatch_stdin(int fd, uint32_t mask, void *data)
-{
- struct tinyds_server *server = data;
-
- wl_display_terminate(server->display);
-
- return 1;
-}