From 171bcf35e2a5f30f02dc2babb04d6e3450e1bdd6 Mon Sep 17 00:00:00 2001 From: Seunghun Lee Date: Mon, 18 Jul 2022 18:25:12 +0900 Subject: [PATCH] output: Add ds_output_{create,destroy}_global() This adds wl_output globals for ds_outputs. Change-Id: I946206f33eeb00f484731767ce1b836b46b62947 --- include/libds/interfaces/output.h | 18 +++ src/output.c | 242 +++++++++++++++++++++++++++++++++++++- 2 files changed, 258 insertions(+), 2 deletions(-) diff --git a/include/libds/interfaces/output.h b/include/libds/interfaces/output.h index 53a4e0c..2c99b82 100644 --- a/include/libds/interfaces/output.h +++ b/include/libds/interfaces/output.h @@ -40,8 +40,12 @@ struct ds_output_state enum ds_output_state_field committed; struct ds_buffer *buffer; + enum wl_output_transform transform; enum ds_output_state_mode_type mode_type; const struct ds_output_mode *mode; + + float scale; + struct { int32_t width, height; int32_t refresh; // mHz, may be zero @@ -58,6 +62,18 @@ struct ds_output struct wl_display *display; struct wl_global *global; + struct wl_list resources; + + char name[24]; + char *description; + char make[56]; + char model[16]; + char serial[16]; + int32_t phys_width, phys_height; // mm + + float scale; + enum wl_output_transform transform; + enum wl_output_subpixel subpixel; struct ds_buffer *back_buffer, *front_buffer; const struct ds_output_mode *current_mode; @@ -65,6 +81,8 @@ struct ds_output int32_t refresh; // mHz, may be zero struct ds_output_state pending; + struct wl_event_source *idle_done; + struct wl_list modes; struct wl_listener display_destroy; diff --git a/src/output.c b/src/output.c index d5af56e..31cb068 100644 --- a/src/output.c +++ b/src/output.c @@ -5,12 +5,24 @@ #include "libds/output.h" #include "libds/interfaces/output.h" +#define OUTPUT_VERSION 3 + static void output_handle_display_destroy(struct wl_listener *listener, void *data); static void output_enable(struct ds_output *output, bool enable); static void output_state_clear(struct ds_output_state *state); static void output_state_clear_buffer(struct ds_output_state *state); static void output_state_clear_mode(struct ds_output_state *state); +static void output_destroy_global(struct ds_output *output); +static void output_bind(struct wl_client *client, void *data, uint32_t version, + uint32_t id); +static void output_handle_resource_destroy(struct wl_resource *resource); +static void output_schedule_done(struct ds_output *output); +static void output_handle_idle_done(void *data); +static void send_geometry(struct wl_resource *resource); +static void send_current_mode(struct wl_resource *resource); +static void send_scale(struct wl_resource *resource); +static void send_done(struct wl_resource *resource); WL_EXPORT void ds_output_init(struct ds_output *output, struct ds_backend *backend, @@ -37,8 +49,13 @@ ds_output_destroy(struct ds_output *output) { wl_list_remove(&output->display_destroy.link); + output_destroy_global(output); + wl_signal_emit(&output->events.destroy, output); + if (output->idle_done) + wl_event_source_remove(output->idle_done); + if (output->iface && output->iface->destroy) output->iface->destroy(output); else @@ -60,12 +77,40 @@ ds_output_disable(struct ds_output *output) WL_EXPORT bool ds_output_commit(struct ds_output *output) { + struct wl_resource *resource; + bool scale_updated; + bool geometry_updated; + // TODO signal precommit if (!output->iface->commit(output)) { return false; } + scale_updated = output->pending.committed & DS_OUTPUT_STATE_SCALE; + if (scale_updated) { + output->scale = output->pending.scale; + } + + if (output->pending.committed & DS_OUTPUT_STATE_TRANSFORM) { + output->transform = output->pending.transform; + // TODO update matrix + } + + geometry_updated = output->pending.committed & + (DS_OUTPUT_STATE_MODE | DS_OUTPUT_STATE_TRANSFORM); + if (geometry_updated || scale_updated) { + wl_resource_for_each(resource, &output->resources) { + if (geometry_updated) { + send_geometry(resource); + } + if (scale_updated) { + send_scale(resource); + } + } + output_schedule_done(output); + } + output_state_clear(&output->pending); // TODO signal commit @@ -130,6 +175,24 @@ ds_output_set_custom_mode(struct ds_output *output, } WL_EXPORT void +ds_output_create_global(struct ds_output *output) +{ + if (output->global) + return; + + output->global = wl_global_create(output->display, + &wl_output_interface, OUTPUT_VERSION, output, output_bind); + if (!output->global) + ds_err("Failed to allocate wl_output global"); +} + +WL_EXPORT void +ds_output_destroy_global(struct ds_output *output) +{ + output_destroy_global(output); +} + +WL_EXPORT void ds_output_add_destroy_listener(struct ds_output *output, struct wl_listener *listener) { @@ -150,6 +213,31 @@ ds_output_add_commit_listener(struct ds_output *output, wl_signal_add(&output->events.commit, listener); } +WL_EXPORT void +ds_output_set_transform(struct ds_output *output, + enum wl_output_transform transform) +{ + if (output->transform == transform) { + output->pending.committed &= ~DS_OUTPUT_STATE_TRANSFORM; + return; + } + + output->pending.committed |= DS_OUTPUT_STATE_TRANSFORM; + output->pending.transform = transform; +} + +WL_EXPORT void +ds_output_set_scale(struct ds_output *output, float scale) +{ + if (output->scale == scale) { + output->pending.committed &= ~DS_OUTPUT_STATE_SCALE; + return; + } + + output->pending.committed |= DS_OUTPUT_STATE_SCALE; + output->pending.scale = scale; +} + void ds_output_update_custom_mode(struct ds_output *output, int32_t width, int32_t height, int32_t refresh) @@ -163,14 +251,30 @@ ds_output_update_custom_mode(struct ds_output *output, output->refresh = refresh; } +void +ds_output_set_subpixel(struct ds_output *output, + enum wl_output_subpixel subpixel) +{ + struct wl_resource *resource; + + if (output->subpixel == subpixel) + return; + + output->subpixel = subpixel; + + wl_resource_for_each(resource, &output->resources) + send_geometry(resource); + + output_schedule_done(output); +} + static void output_handle_display_destroy(struct wl_listener *listener, void *data) { struct ds_output *output; output = wl_container_of(listener, output, display_destroy); - // TODO - // destroy wl_global + output_destroy_global(output); } static void @@ -213,3 +317,137 @@ output_state_clear_mode(struct ds_output_state *state) state->mode = NULL; state->committed &= ~DS_OUTPUT_STATE_MODE; } + +static void +output_destroy_global(struct ds_output *output) +{ + struct wl_resource *resource, *tmp; + + if (!output->global) + return; + + wl_resource_for_each_safe(resource, tmp, &output->resources) { + wl_resource_set_user_data(resource, NULL); + wl_list_remove(wl_resource_get_link(resource)); + } + + wl_global_destroy(output->global); + output->global = NULL; +} + +static const struct wl_output_interface output_impl; + +static void +output_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) +{ + struct ds_output *output = data; + struct wl_resource *resource; + + resource = wl_resource_create(client, &wl_output_interface, version, id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(resource, &output_impl, output, + output_handle_resource_destroy); + + wl_list_insert(&output->resources, wl_resource_get_link(resource)); + + send_geometry(resource); + send_current_mode(resource); + send_scale(resource); + send_done(resource); +} + +static void +output_handle_release(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static const struct wl_output_interface output_impl = { + .release = output_handle_release, +}; + +static void +output_handle_resource_destroy(struct wl_resource *resource) +{ + wl_list_remove(wl_resource_get_link(resource)); +} + +static void +output_schedule_done(struct ds_output *output) +{ + struct wl_event_loop *loop; + + if (output->idle_done) + return; + + loop = wl_display_get_event_loop(output->display); + output->idle_done = + wl_event_loop_add_idle(loop, output_handle_idle_done, output); +} + +static void +output_handle_idle_done(void *data) +{ + struct ds_output *output = data; + struct wl_resource *resource; + + wl_resource_for_each(resource, &output->resources) + send_done(resource); + + output->idle_done = NULL; +} + +static void +send_geometry(struct wl_resource *resource) +{ + struct ds_output *output; + + output = wl_resource_get_user_data(resource); + wl_output_send_geometry(resource, 0, 0, + output->phys_width, output->phys_height, output->subpixel, + output->make, output->model, output->transform); +} + +static void +send_current_mode(struct wl_resource *resource) +{ + struct ds_output *output; + const struct ds_output_mode *mode; + + output = wl_resource_get_user_data(resource); + mode = output->current_mode; + if (mode) { + wl_output_send_mode(resource, WL_OUTPUT_MODE_CURRENT, + mode->width, mode->height, mode->refresh); + } + else { + wl_output_send_mode(resource, WL_OUTPUT_MODE_CURRENT, + output->width, output->height, output->refresh); + } +} + +static void +send_scale(struct wl_resource *resource) +{ + struct ds_output *output; + uint32_t version; + + output = wl_resource_get_user_data(resource); + version = wl_resource_get_version(resource); + if (version >= WL_OUTPUT_SCALE_SINCE_VERSION) + wl_output_send_scale(resource, (uint32_t)ceil(output->scale)); +} + +static void +send_done(struct wl_resource *resource) +{ + uint32_t version; + + version = wl_resource_get_version(resource); + if (version >= WL_OUTPUT_DONE_SINCE_VERSION) + wl_output_send_done(resource); +} -- 2.7.4