#include <stdint.h>
#include <wayland-server.h>
+#include <libds/output.h>
#include <libds/backend.h>
#include <libds/buffer.h>
bool (*commit)(struct ds_output *output);
};
-struct ds_output_mode {
- struct wl_list link;
- int32_t width, height;
- int32_t refresh;
- bool preferred;
-};
-
struct ds_output_state
{
enum ds_output_state_field committed;
struct ds_buffer *buffer;
- struct ds_output_mode *mode;
+ const struct ds_output_mode *mode;
bool enabled;
};
struct wl_global *global;
struct ds_buffer *back_buffer, *front_buffer;
+ const struct ds_output_mode *current_mode;
struct ds_output_state pending;
+ struct wl_list modes;
+
struct wl_listener display_destroy;
struct {
struct ds_output;
+struct ds_output_mode {
+ int32_t width, height;
+ int32_t refresh;
+ bool preferred;
+ struct wl_list link;
+};
+
void
ds_output_destroy(struct ds_output *output);
void
ds_output_attach_buffer(struct ds_output *output, struct ds_buffer *buffer);
+const struct ds_output_mode *
+ds_output_preferred_mode(struct ds_output *output);
+
+void
+ds_output_set_mode(struct ds_output *output,
+ const struct ds_output_mode *mode);
+
void
ds_output_add_destroy_listener(struct ds_output *output,
struct wl_listener *listener);
BuildRequires: pkgconfig(pixman-1)
BuildRequires: pkgconfig(libdrm)
+BuildRequires: pkgconfig(libtdm)
+
%description
Wayland Compositor Library
)
subdir('wayland')
+subdir('tdm')
--- /dev/null
+#include <assert.h>
+#include <stdlib.h>
+
+#include "libds/log.h"
+#include "tdm.h"
+
+static const struct ds_backend_interface tdm_backend_iface;
+static void tdm_backend_handle_display_destroy(struct wl_listener *listener,
+ void *data);
+static void tdm_backend_destroy(struct ds_tdm_backend *tdm);
+static int tdm_backend_handle_tdm_event(int fd, uint32_t mask, void *data);
+
+WL_EXPORT struct ds_backend *
+ds_tdm_backend_create(struct wl_display *display)
+{
+ struct ds_tdm_backend *tdm;
+ struct wl_event_loop *loop;
+ tdm_error err;
+
+ ds_inf("Initializing TDM backend");
+
+ tdm = calloc(1, sizeof *tdm);
+ if (!tdm)
+ return NULL;
+
+ ds_backend_init(&tdm->base, &tdm_backend_iface);
+
+ tdm->wl_display = display;
+ tdm->clock = CLOCK_MONOTONIC; // FIXME
+ wl_list_init(&tdm->outputs);
+
+ tdm->tdm_display = tdm_display_init(&err);
+ if (err != TDM_ERROR_NONE) {
+ ds_err("Could not initialize tdm_display");
+ goto err_display;
+ }
+
+ err = tdm_display_get_fd(tdm->tdm_display, &tdm->fd);
+ if (err != TDM_ERROR_NONE || tdm->fd < 0) {
+ ds_err("Could not get fd from tdm_display: err(%d)", err);
+ goto err_fd;
+ }
+
+ loop = wl_display_get_event_loop(display);
+ tdm->tdm_event = wl_event_loop_add_fd(loop, tdm->fd, WL_EVENT_READABLE,
+ tdm_backend_handle_tdm_event, tdm);
+ if (!tdm->tdm_event) {
+ ds_err("could not add fd event handler for tdm event");
+ goto err_event;
+ }
+
+ tdm->display_destroy.notify = tdm_backend_handle_display_destroy;
+ wl_display_add_destroy_listener(display, &tdm->display_destroy);
+
+ return &tdm->base;
+
+err_event:
+ close(tdm->fd);
+err_fd:
+ tdm_display_deinit(tdm->tdm_display);
+err_display:
+ free(tdm);
+
+ return NULL;
+}
+
+struct ds_tdm_backend *
+tdm_backend_from_backend(struct ds_backend *backend)
+{
+ assert(backend->iface == &tdm_backend_iface);
+ return (struct ds_tdm_backend *)backend;
+}
+
+static void
+tdm_backend_destroy(struct ds_tdm_backend *tdm)
+{
+ wl_list_remove(&tdm->display_destroy.link);
+ wl_event_source_remove(tdm->tdm_event);
+ close(tdm->fd);
+ tdm_display_deinit(tdm->tdm_display);
+ free(tdm);
+}
+
+static bool
+tdm_backend_scan_outputs(struct ds_tdm_backend *tdm)
+{
+ struct ds_tdm_output *output;
+ tdm_output *tdm_output;
+ tdm_error err;
+ int num_outputs, i;
+
+ err = tdm_display_get_output_count(tdm->tdm_display, &num_outputs);
+ if (err != TDM_ERROR_NONE) {
+ ds_err("Could not get number of outputs: err(%d)", err);
+ return false;
+ }
+
+ for (i = 0; i < num_outputs; i++) {
+ tdm_output = tdm_display_get_output(tdm->tdm_display, i, &err);
+ if (err != TDM_ERROR_NONE || !tdm_output) {
+ ds_err("Could not get output from tdm_display: index(%d) err(%d)",
+ i, err);
+ continue;
+ }
+
+ output = create_tdm_output(tdm, tdm_output);
+ if (!output) {
+ ds_err("Could not create output: index(%d)", i);
+ continue;
+ }
+
+ wl_list_insert(&tdm->outputs, &output->link);
+
+ wl_signal_emit(&tdm->base.events.new_output, &output->base);
+ }
+
+ return true;
+}
+
+static bool
+tdm_backend_iface_start(struct ds_backend *backend)
+{
+ struct ds_tdm_backend *tdm;
+
+ tdm = tdm_backend_from_backend(backend);
+ return tdm_backend_scan_outputs(tdm);
+}
+
+static void
+tdm_backend_iface_destroy(struct ds_backend *backend)
+{
+ struct ds_tdm_backend *tdm;
+
+ tdm = tdm_backend_from_backend(backend);
+ tdm_backend_destroy(tdm);
+}
+
+static const struct ds_backend_interface tdm_backend_iface = {
+ .start = tdm_backend_iface_start,
+ .destroy = tdm_backend_iface_destroy,
+ .get_drm_fd = NULL,
+};
+
+static void
+tdm_backend_handle_display_destroy(struct wl_listener *listener, void *data)
+{
+ struct ds_tdm_backend *tdm;
+
+ tdm = wl_container_of(listener, tdm, display_destroy);
+ tdm_backend_destroy(tdm);
+}
+
+static int
+tdm_backend_handle_tdm_event(int fd, uint32_t mask, void *data)
+{
+ struct ds_tdm_backend *tdm;
+
+ tdm = data;
+ tdm_display_handle_events(tdm->tdm_display);
+
+ return 0;
+}
--- /dev/null
+libds_files += files(
+ 'backend.c',
+ 'output.c',
+)
+
+libds_deps += [
+ dependency('libtdm', required: true),
+]
--- /dev/null
+#include <assert.h>
+#include <stdlib.h>
+
+#include "libds/log.h"
+#include "tdm.h"
+
+static const struct ds_output_interface tdm_output_iface;
+static bool tdm_output_init_modes(struct ds_tdm_output *output);
+static void tdm_output_destroy(struct ds_tdm_output *output);
+
+struct ds_tdm_output *
+create_tdm_output(struct ds_tdm_backend *tdm, tdm_output *tdm_output)
+{
+ struct ds_tdm_output *output;
+ tdm_output_conn_status conn;
+ tdm_error err;
+
+ output = calloc(1, sizeof *output);
+ if (!output)
+ return NULL;
+
+ ds_output_init(&output->base, &tdm->base, &tdm_output_iface,
+ tdm->wl_display);
+
+ output->tdm = tdm;
+ output->tdm_output = tdm_output;
+
+ err = tdm_output_get_conn_status(tdm_output, &conn);
+ if (err != TDM_ERROR_NONE) {
+ ds_err("Could not get connection status of tdm output(%p): err(%d)",
+ tdm_output, err);
+ goto err_status;
+ }
+
+ if (conn == TDM_OUTPUT_CONN_STATUS_CONNECTED) {
+ output->status = DS_TDM_OUTPUT_CONNECTED;
+
+ if (!tdm_output_init_modes(output)) {
+ ds_err("Could not initialize modes of tdm output(%p)",
+ tdm_output);
+ goto err_status;
+ }
+ }
+ else if (conn == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) {
+ output->status = DS_TDM_OUTPUT_DISCONNECTED;
+ }
+
+ ds_inf("TDM output(%p) created.", output);
+
+ return output;
+
+err_status:
+ free(output);
+
+ return NULL;
+}
+
+static struct ds_tdm_output *
+tdm_output_from_output(struct ds_output *ds_output)
+{
+ assert(ds_output->iface == &tdm_output_iface);
+ return (struct ds_tdm_output *)ds_output;
+}
+
+static void
+tdm_output_iface_destroy(struct ds_output *ds_output)
+{
+ struct ds_tdm_output *output;
+
+ output = tdm_output_from_output(ds_output);
+ tdm_output_destroy(output);
+}
+
+static bool
+tdm_output_iface_commit(struct ds_output *ds_output)
+{
+ // TODO
+ return true;
+}
+
+static const struct ds_output_interface tdm_output_iface =
+{
+ .destroy = tdm_output_iface_destroy,
+ .commit = tdm_output_iface_commit,
+};
+
+static void
+tdm_output_destroy(struct ds_tdm_output *output)
+{
+ struct ds_tdm_output_mode *mode, *mode_tmp;
+
+ wl_list_for_each_safe(mode, mode_tmp, &output->base.modes, base.link) {
+ wl_list_remove(&mode->base.link);
+ free(mode);
+ }
+
+ free(output);
+}
+
+static bool
+tdm_output_init_modes(struct ds_tdm_output *output)
+{
+ struct ds_tdm_output_mode *mode;
+ const tdm_output_mode *tdm_modes, *tdm_mode;
+ tdm_error err;
+ int num_modes, i;
+
+ err = tdm_output_get_available_modes(output->tdm_output, &tdm_modes,
+ &num_modes);
+ if (err != TDM_ERROR_NONE) {
+ ds_err("Could not get available modes: output(%p)", output);
+ return false;
+ }
+
+ ds_inf("Detected modes:");
+
+ for (i = 0; i < num_modes; i++) {
+ tdm_mode = &tdm_modes[i];
+
+ mode = calloc(1, sizeof *mode);
+ if (!mode) {
+ ds_err("Could not allocate memory");
+ continue;
+ }
+
+ mode->tdm_mode = tdm_mode;
+ mode->base.width = tdm_mode->hdisplay;
+ mode->base.height = tdm_mode->vdisplay;
+ mode->base.refresh = (int32_t)tdm_mode->vrefresh; // FIXME
+
+ if (tdm_mode->type & TDM_OUTPUT_MODE_TYPE_PREFERRED)
+ mode->base.preferred = true;
+
+ ds_inf(" %dx%d@%d %s", mode->base.width, mode->base.height,
+ mode->base.refresh,
+ mode->base.preferred ? "(preferred)" : "");
+
+ if (tdm_mode->type & TDM_OUTPUT_MODE_TYPE_DEFAULT)
+ wl_list_insert(&output->base.modes, &mode->base.link);
+ else
+ wl_list_insert(output->base.modes.prev, &mode->base.link);
+ }
+
+ return true;
+}
--- /dev/null
+#ifndef DS_BACKEND_TDM_H
+#define DS_BACKEND_TDM_H
+
+#include <time.h>
+#include <tdm.h>
+
+#include "libds/interfaces/backend.h"
+#include "libds/interfaces/output.h"
+
+enum ds_tdm_output_status {
+ DS_TDM_OUTPUT_DISCONNECTED,
+ DS_TDM_OUTPUT_CONNECTED,
+};
+
+struct ds_tdm_output_mode {
+ struct ds_output_mode base;
+ const tdm_output_mode *tdm_mode;
+};
+
+struct ds_tdm_backend
+{
+ struct ds_backend base;
+
+ tdm_display *tdm_display;
+ struct wl_display *wl_display;
+ struct wl_event_source *tdm_event;
+
+ struct wl_list outputs; // ds_tdm_output.link
+
+ struct wl_listener display_destroy;
+
+ clockid_t clock;
+ int fd;
+};
+
+struct ds_tdm_output
+{
+ struct ds_output base;
+
+ struct ds_tdm_backend *tdm;
+ tdm_output *tdm_output;
+
+ struct wl_list link;
+
+ enum ds_tdm_output_status status;
+};
+
+struct ds_tdm_backend *tdm_backend_from_backend(struct ds_backend *backend);
+
+struct ds_tdm_output *create_tdm_output(struct ds_tdm_backend *tdm,
+ tdm_output *tdm_output);
+
+#endif
output->iface = iface;
output->display = display;
+ wl_list_init(&output->modes);
+
wl_signal_init(&output->events.destroy);
wl_signal_init(&output->events.frame);
wl_signal_init(&output->events.commit);
output->pending.buffer = ds_buffer_lock(buffer);
}
+WL_EXPORT const struct ds_output_mode *
+ds_output_preferred_mode(struct ds_output *output)
+{
+ struct ds_output_mode *mode;
+
+ if (wl_list_empty(&output->modes))
+ return NULL;
+
+ wl_list_for_each(mode, &output->modes, link) {
+ if (mode->preferred)
+ return mode;
+ }
+
+ // No preferred mode, choose the first one
+ return wl_container_of(output->modes.next, mode, link);
+}
+
+WL_EXPORT void
+ds_output_set_mode(struct ds_output *output, const struct ds_output_mode *mode)
+{
+ output->pending.mode = NULL;
+ output->pending.committed &= ~DS_OUTPUT_STATE_MODE;
+
+ if (output->current_mode == mode) {
+ return;
+ }
+
+ output->pending.committed |= DS_OUTPUT_STATE_MODE;
+ output->pending.mode = mode;
+}
+
WL_EXPORT void
ds_output_add_destroy_listener(struct ds_output *output,
struct wl_listener *listener)