#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,
{
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
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
}
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)
{
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)
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
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);
+}