output: Add ds_output_{create,destroy}_global() 95/279195/1
authorSeunghun Lee <shiin.lee@samsung.com>
Mon, 18 Jul 2022 09:25:12 +0000 (18:25 +0900)
committerJunseok Kim <juns.kim@samsung.com>
Wed, 3 Aug 2022 08:52:35 +0000 (17:52 +0900)
This adds wl_output globals for ds_outputs.

Change-Id: I946206f33eeb00f484731767ce1b836b46b62947

include/libds/interfaces/output.h
src/output.c

index 53a4e0c..2c99b82 100644 (file)
@@ -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;
index d5af56e..31cb068 100644 (file)
@@ -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);
+}