From: Junkyeong Kim Date: Tue, 16 Oct 2018 12:28:25 +0000 (+0900) Subject: virtual: add virtual backend module X-Git-Tag: accepted/tizen/5.0/unified/20181108.074357~6 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Flibtdm.git;a=commitdiff_plain;h=29faed4a576ecf17702612f7326272d1540af189 virtual: add virtual backend module support virtual output create, destroy, connect, disconnect, mode set, size set, buffer commit default load virtual module when tdm init for support virtual output. Change-Id: Ib970dd3ade87873e1b9e3be96c410f5e355a7ff8 Signed-off-by: Junkyeong Kim --- diff --git a/backends/Makefile.am b/backends/Makefile.am index 4a66860..f841dd0 100644 --- a/backends/Makefile.am +++ b/backends/Makefile.am @@ -1 +1 @@ -SUBDIRS = dummy +SUBDIRS = dummy virtual diff --git a/backends/virtual/Makefile.am b/backends/virtual/Makefile.am new file mode 100644 index 0000000..099b71e --- /dev/null +++ b/backends/virtual/Makefile.am @@ -0,0 +1,14 @@ +AM_CFLAGS = \ + $(CFLAGS) \ + $(TDM_CFLAGS) \ + -I$(top_srcdir)/include + +libtdm_virtual_la_LTLIBRARIES = libtdm-virtual.la +libtdm_virtual_ladir = $(TDM_MODULE_PATH) +libtdm_virtual_la_LDFLAGS = -module -avoid-version +libtdm_virtual_la_LIBADD = $(TDM_LIBS) $(top_builddir)/src/libtdm.la + +libtdm_virtual_la_SOURCES = \ + tdm_virtual_display.c \ + tdm_virtual.c + diff --git a/backends/virtual/tdm_virtual.c b/backends/virtual/tdm_virtual.c new file mode 100644 index 0000000..f4ad7fe --- /dev/null +++ b/backends/virtual/tdm_virtual.c @@ -0,0 +1,148 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "tdm_virtual.h" + +static tdm_virtual_data *virtual_data; + +void +tdm_virtual_deinit(tdm_backend_data *bdata) +{ + if (virtual_data != bdata) + return; + + TDM_INFO("deinit"); + + tdm_virtual_display_destroy_output_list(virtual_data); + + if (virtual_data->pipe[0] >= 0) + close(virtual_data->pipe[0]); + if (virtual_data->pipe[1] >= 0) + close(virtual_data->pipe[1]); + + free(virtual_data); + virtual_data = NULL; +} + +tdm_backend_data * +tdm_virtual_init(tdm_display *dpy, tdm_error *error) +{ + tdm_func_display virtual_func_display; + tdm_func_output virtual_func_output; + tdm_func_voutput virtual_func_voutput; + tdm_func_layer virtual_func_layer; + tdm_error ret; + + if (!dpy) { + TDM_ERR("display is null"); + if (error) + *error = TDM_ERROR_INVALID_PARAMETER; + return NULL; + } + + if (virtual_data) { + TDM_ERR("failed: init twice"); + if (error) + *error = TDM_ERROR_BAD_REQUEST; + return NULL; + } + + virtual_data = calloc(1, sizeof(tdm_virtual_data)); + if (!virtual_data) { + TDM_ERR("alloc failed"); + if (error) + *error = TDM_ERROR_OUT_OF_MEMORY; + return NULL; + } + + LIST_INITHEAD(&virtual_data->voutput_list); + LIST_INITHEAD(&virtual_data->output_list); + LIST_INITHEAD(&virtual_data->buffer_list); + + memset(&virtual_func_display, 0, sizeof(virtual_func_display)); + virtual_func_display.display_get_capability = virtual_display_get_capability; + virtual_func_display.display_get_outputs = virtual_display_get_outputs; + virtual_func_display.display_get_fd = virtual_display_get_fd; + virtual_func_display.display_handle_events = virtual_display_handle_events; + virtual_func_display.voutput_create = virtual_output_create; + + memset(&virtual_func_output, 0, sizeof(virtual_func_output)); + virtual_func_output.output_get_capability = virtual_output_get_capability; + virtual_func_output.output_get_layers = virtual_output_get_layers; + virtual_func_output.output_wait_vblank = virtual_output_wait_vblank; + virtual_func_output.output_set_vblank_handler = virtual_output_set_vblank_handler; + virtual_func_output.output_commit = virtual_output_commit; + virtual_func_output.output_set_commit_handler = virtual_output_set_commit_handler; + + virtual_func_output.output_set_dpms = virtual_output_set_dpms; + virtual_func_output.output_get_dpms = virtual_output_get_dpms; + + virtual_func_output.output_set_mode = virtual_output_set_mode; + virtual_func_output.output_get_mode = virtual_output_get_mode; + virtual_func_output.output_set_status_handler = virtual_output_set_status_handler; + + memset(&virtual_func_voutput, 0, sizeof(virtual_func_voutput)); + virtual_func_voutput.voutput_destroy = virtual_output_destroy; + virtual_func_voutput.voutput_set_available_mode = virtual_output_set_available_mode; + virtual_func_voutput.voutput_set_physical_size = virtual_output_set_physical_size; + virtual_func_voutput.voutput_connect = virtual_output_connect; + virtual_func_voutput.voutput_disconnect = virtual_output_disconnect; + virtual_func_voutput.voutput_get_output = virtual_output_get_output; + virtual_func_voutput.voutput_set_commit_func = virtual_output_set_commit_func; + virtual_func_voutput.voutput_commit_done = virtual_output_commit_done; + + memset(&virtual_func_layer, 0, sizeof(virtual_func_layer)); + virtual_func_layer.layer_get_capability = virtual_layer_get_capability; + virtual_func_layer.layer_set_info = virtual_layer_set_info; + virtual_func_layer.layer_get_info = virtual_layer_get_info; + virtual_func_layer.layer_set_buffer = virtual_layer_set_buffer; + virtual_func_layer.layer_unset_buffer = virtual_layer_unset_buffer; + + ret = tdm_backend_register_func_display(dpy, &virtual_func_display); + if (ret != TDM_ERROR_NONE) + goto failed; + + ret = tdm_backend_register_func_output(dpy, &virtual_func_output); + if (ret != TDM_ERROR_NONE) + goto failed; + + ret = tdm_backend_register_func_voutput(dpy, &virtual_func_voutput); + if (ret != TDM_ERROR_NONE) + goto failed; + + ret = tdm_backend_register_func_layer(dpy, &virtual_func_layer); + if (ret != TDM_ERROR_NONE) + goto failed; + + virtual_data->dpy = dpy; + + if (pipe(virtual_data->pipe) < 0) { + TDM_ERR("failed get pipe: %m"); + ret = TDM_ERROR_OPERATION_FAILED; + goto failed; + } + + if (error) + *error = TDM_ERROR_NONE; + + TDM_INFO("init success!"); + + return (tdm_backend_data *)virtual_data; +failed: + if (error) + *error = ret; + + tdm_virtual_deinit(virtual_data); + + TDM_ERR("init failed!"); + return NULL; +} + +tdm_backend_module tdm_backend_module_data = { + "Virtual", + "Samsung", + TDM_BACKEND_SET_ABI_VERSION(1, 1), + tdm_virtual_init, + tdm_virtual_deinit +}; diff --git a/backends/virtual/tdm_virtual.h b/backends/virtual/tdm_virtual.h new file mode 100644 index 0000000..c85dff6 --- /dev/null +++ b/backends/virtual/tdm_virtual.h @@ -0,0 +1,89 @@ +#ifndef _TDM_VIRTUAL_H_ +#define _TDM_VIRTUAL_H_ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* virtual backend functions (display) */ +tdm_error virtual_display_get_capability(tdm_backend_data *bdata, tdm_caps_display *caps); +tdm_output **virtual_display_get_outputs(tdm_backend_data *bdata, int *count, tdm_error *error); +tdm_error virtual_display_get_fd(tdm_backend_data *bdata, int *fd); +tdm_error virtual_display_handle_events(tdm_backend_data *bdata); +tdm_output *virtual_display_output_create(tdm_backend_data *bdata, const char* name, tdm_error *error); +tdm_error virtual_display_output_destroy(tdm_backend_data *bdata, tdm_output *output); + +tdm_error virtual_output_get_capability(tdm_output *output, tdm_caps_output *caps); +tdm_layer **virtual_output_get_layers(tdm_output *output, int *count, tdm_error *error); +tdm_error virtual_output_wait_vblank(tdm_output *output, int interval, int sync, void *user_data); +tdm_error virtual_output_set_vblank_handler(tdm_output *output, tdm_output_vblank_handler func); +tdm_error virtual_output_commit(tdm_output *output, int sync, void *user_data); +tdm_error virtual_output_set_commit_handler(tdm_output *output, tdm_output_commit_handler func); +tdm_error virtual_output_set_dpms(tdm_output *output, tdm_output_dpms dpms_value); +tdm_error virtual_output_get_dpms(tdm_output *output, tdm_output_dpms *dpms_value); +tdm_error virtual_output_set_mode(tdm_output *output, const tdm_output_mode *mode); +tdm_error virtual_output_get_mode(tdm_output *output, const tdm_output_mode **mode); +tdm_error virtual_output_set_available_mode(tdm_output *output, const tdm_output_mode *modes, int count); +tdm_error virtual_output_set_physical_size(tdm_output *output, unsigned int mmwidth, unsigned int mmheight); +tdm_error virtual_output_connect(tdm_output *output); +tdm_error virtual_output_disconnect(tdm_output *output); +tdm_error virtual_output_set_status_handler(tdm_output *output, tdm_output_status_handler func, void *user_data); + +tdm_voutput *virtual_output_create(tdm_backend_data *bdata, const char *name, tdm_error *error); +tdm_error virtual_output_destroy(tdm_voutput *voutput); +tdm_output *virtual_output_get_output(tdm_voutput *voutput, tdm_error *error); +tdm_error virtual_output_set_commit_func(tdm_voutput *voutput, tdm_voutput_commit_handler commit_func); +tdm_error virtual_output_commit_done(tdm_voutput *voutput); + +tdm_error virtual_layer_get_capability(tdm_layer *layer, tdm_caps_layer *caps); +tdm_error virtual_layer_set_info(tdm_layer *layer, tdm_info_layer *info); +tdm_error virtual_layer_get_info(tdm_layer *layer, tdm_info_layer *info); +tdm_error virtual_layer_set_buffer(tdm_layer *layer, tbm_surface_h buffer); +tdm_error virtual_layer_unset_buffer(tdm_layer *layer); + +#define RETURN_VAL_IF_FAIL(cond, val) {\ + if (!(cond)) {\ + TDM_ERR("'%s' failed", #cond);\ + return val;\ + }\ +} + +#define GOTO_IF_FAIL(cond, val) {\ + if (!(cond)) {\ + TDM_ERR("'%s' failed", #cond);\ + goto val;\ + }\ +} + +typedef struct _tdm_virtual_data { + tdm_display *dpy; + + int pipe[2]; + + struct list_head voutput_list; + struct list_head output_list; + struct list_head buffer_list; +} tdm_virtual_data; + +void tdm_virtual_display_destroy_output_list(tdm_virtual_data *virtual_data); + +#endif /* _TDM_VIRTUAL_H_ */ diff --git a/backends/virtual/tdm_virtual_display.c b/backends/virtual/tdm_virtual_display.c new file mode 100644 index 0000000..fe4a5c9 --- /dev/null +++ b/backends/virtual/tdm_virtual_display.c @@ -0,0 +1,997 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "tdm_virtual.h" + +typedef struct _tdm_virtual_output_data tdm_virtual_output_data; +typedef struct _tdm_virtual_voutput_data tdm_virtual_voutput_data; +typedef struct _tdm_virtual_layer_data tdm_virtual_layer_data; +typedef struct _tdm_virtual_event_data tdm_virtual_event_data; + +typedef enum { + TDM_VIRTUAL_EVENT_TYPE_WAIT, + TDM_VIRTUAL_EVENT_TYPE_COMMIT, + TDM_VIRTUAL_EVENT_TYPE_VCOMMIT, +} tdm_virtual_event_type; + +struct _tdm_virtual_event_data { + struct list_head link; + + tdm_virtual_event_type type; + tdm_virtual_output_data *output_data; + void *user_data; +}; + +struct _tdm_virtual_output_data { + struct list_head link; + + /* data which are fixed at initializing */ + tdm_virtual_data *virtual_data; + tdm_virtual_voutput_data *voutput_data; + + char name[TDM_NAME_LEN]; /* output name */ + uint32_t pipe; + tdm_output_type connector_type; + struct list_head layer_list; + tdm_virtual_layer_data *primary_layer; + + tdm_output_dpms dpms; + + /* not fixed data below */ + tdm_output_vblank_handler vblank_func; + tdm_output_commit_handler commit_func; + void *commit_user_data; + + tdm_output_conn_status status; + tdm_output_status_handler status_func; + void *status_user_data; + + int mode_changed; + const tdm_output_mode *current_mode; + + tdm_event_loop_source *timer; + unsigned int timer_waiting; + struct list_head timer_event_list; +}; + +struct _tdm_virtual_voutput_data { + struct list_head link; + + /* data which are fixed at initializing */ + tdm_virtual_data *virtual_data; + tdm_virtual_output_data *output_data; + + char name[TDM_NAME_LEN]; /* output name */ + + tdm_voutput_commit_handler vcommit_func; + + tdm_output_mode *output_modes; + int mode_count; + + unsigned int mmwidth; + unsigned int mmheight; +}; + + +struct _tdm_virtual_layer_data { + struct list_head link; + + /* data which are fixed at initializing */ + tdm_virtual_data *virtual_data; + tdm_virtual_output_data *output_data; + tdm_layer_capability capabilities; + int zpos; + + /* not fixed data below */ + tdm_info_layer info; + int info_changed; + + tbm_surface_h display_buffer; + int display_buffer_changed; +}; + +static void +_tdm_virtual_display_cb_event(tdm_virtual_output_data *output_data, tdm_virtual_event_data *event_data, + unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec) +{ + tdm_virtual_voutput_data *voutput_data = NULL; + + switch (event_data->type) { + case TDM_VIRTUAL_EVENT_TYPE_WAIT: + if (output_data->vblank_func) + output_data->vblank_func(output_data, sequence, tv_sec, tv_usec, event_data->user_data); + break; + case TDM_VIRTUAL_EVENT_TYPE_COMMIT: + if (output_data->commit_func) + output_data->commit_func(output_data, sequence, tv_sec, tv_usec, event_data->user_data); + break; + case TDM_VIRTUAL_EVENT_TYPE_VCOMMIT: + voutput_data = output_data->voutput_data; + voutput_data->vcommit_func(voutput_data, sequence, tv_sec, tv_usec, NULL); + default: + break; + } +} + +static void _tdm_virtual_get_current_time(unsigned int *tv_sec, unsigned int *tv_usec) +{ + struct timespec tp; + + if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) { + *tv_sec = tp.tv_sec; + *tv_usec = tp.tv_nsec / 1000; + } else { + *tv_sec = *tv_usec = 0; + } +} + +static tdm_error +_tdm_virtual_display_cb_timeout(void *user_data) +{ + tdm_virtual_output_data *output_data = user_data; + tdm_virtual_event_data *e = NULL, *ee = NULL; + unsigned int tv_sec, tv_usec; + static unsigned int sequence = 0; + + sequence++; + + _tdm_virtual_get_current_time(&tv_sec, &tv_usec); + + LIST_FOR_EACH_ENTRY_SAFE(e, ee, &output_data->timer_event_list, link) { + LIST_DEL(&e->link); + _tdm_virtual_display_cb_event(output_data, e, sequence, tv_sec, tv_usec); + free(e); + } + + return TDM_ERROR_NONE; +} + +static tdm_error +_tdm_virtual_display_wait_vblank(tdm_virtual_output_data *output_data, int interval, tdm_virtual_event_data *event_data) +{ + tdm_error ret; + unsigned int ms; + + RETURN_VAL_IF_FAIL(output_data->timer != NULL, TDM_ERROR_OPERATION_FAILED); + RETURN_VAL_IF_FAIL(output_data->current_mode->vrefresh > 0, TDM_ERROR_OPERATION_FAILED); + + if (output_data->timer_waiting) { + LIST_ADDTAIL(&event_data->link, &output_data->timer_event_list); + return TDM_ERROR_NONE; + } + + if (interval == -1) + ms = 1; + else + ms = ((double)1000.0 / output_data->current_mode->vrefresh) * interval; + + ret = tdm_event_loop_source_timer_update(output_data->timer, ms); + if (ret != TDM_ERROR_NONE) + return ret; + + LIST_ADDTAIL(&event_data->link, &output_data->timer_event_list); + + return TDM_ERROR_NONE; +} + +static void +_tdm_virtual_display_destroy_layer_list(tdm_virtual_data *virtual_data) +{ + tdm_virtual_output_data *o = NULL; + + LIST_FOR_EACH_ENTRY(o, &virtual_data->output_list, link) { + tdm_virtual_layer_data *l = NULL, *ll = NULL; + LIST_FOR_EACH_ENTRY_SAFE(l, ll, &o->layer_list, link) { + LIST_DEL(&l->link); + free(l); + } + } +} + +void +tdm_virtual_display_destroy_output_list(tdm_virtual_data *virtual_data) +{ + tdm_virtual_output_data *o = NULL, *oo = NULL; + + if (LIST_IS_EMPTY(&virtual_data->output_list)) + return; + + _tdm_virtual_display_destroy_layer_list(virtual_data); + + LIST_FOR_EACH_ENTRY_SAFE(o, oo, &virtual_data->output_list, link) { + LIST_DEL(&o->link); + + if (!LIST_IS_EMPTY(&o->timer_event_list)) { + tdm_virtual_event_data *e = NULL, *ee = NULL; + LIST_FOR_EACH_ENTRY_SAFE(e, ee, &o->timer_event_list, link) { + LIST_DEL(&e->link); + free(e); + } + } + + if (o->timer) + tdm_event_loop_source_remove(o->timer); + + free(o); + } +} + +tdm_error +virtual_display_get_capability(tdm_backend_data *bdata, tdm_caps_display *caps) +{ + RETURN_VAL_IF_FAIL(caps, TDM_ERROR_INVALID_PARAMETER); + + caps->max_layer_count = -1; /* not defined */ + + return TDM_ERROR_NONE; +} + +tdm_output ** +virtual_display_get_outputs(tdm_backend_data *bdata, int *count, tdm_error *error) +{ + tdm_virtual_data *virtual_data = bdata; + tdm_virtual_output_data *output_data = NULL; + tdm_output **outputs; + tdm_error ret; + int i; + + RETURN_VAL_IF_FAIL(virtual_data, NULL); + RETURN_VAL_IF_FAIL(count, NULL); + + *count = 0; + LIST_FOR_EACH_ENTRY(output_data, &virtual_data->output_list, link) + (*count)++; + + if (*count == 0) { + ret = TDM_ERROR_NONE; + goto failed_get; + } + + /* will be freed in frontend */ + outputs = calloc(*count, sizeof(tdm_virtual_output_data *)); + if (!outputs) { + TDM_ERR("failed: alloc memory"); + *count = 0; + ret = TDM_ERROR_OUT_OF_MEMORY; + goto failed_get; + } + + i = 0; + LIST_FOR_EACH_ENTRY(output_data, &virtual_data->output_list, link) + outputs[i++] = output_data; + + if (error) + *error = TDM_ERROR_NONE; + + return outputs; +failed_get: + if (error) + *error = ret; + return NULL; +} + +tdm_error +virtual_display_get_fd(tdm_backend_data *bdata, int *fd) +{ + tdm_virtual_data *virtual_data = bdata; + + RETURN_VAL_IF_FAIL(virtual_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(fd, TDM_ERROR_INVALID_PARAMETER); + + *fd = virtual_data->pipe[0]; + + return TDM_ERROR_NONE; +} + +tdm_error +virtual_display_handle_events(tdm_backend_data *bdata) +{ + return TDM_ERROR_NONE; +} + +tdm_error +virtual_output_get_capability(tdm_output *output, tdm_caps_output *caps) +{ + tdm_virtual_output_data *output_data = output; + tdm_virtual_voutput_data *voutput_data = NULL; + tdm_error ret; + + RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(caps, TDM_ERROR_INVALID_PARAMETER); + + voutput_data = output_data->voutput_data; + + memset(caps, 0, sizeof(tdm_caps_output)); + + snprintf(caps->maker, TDM_NAME_LEN, "virtual"); + snprintf(caps->model, TDM_NAME_LEN, "virtual"); + snprintf(caps->name, TDM_NAME_LEN, "%s", output_data->name); + + caps->status = output_data->status; + caps->type = output_data->connector_type; + caps->type_id = 0; + + if (output_data->status == TDM_OUTPUT_CONN_STATUS_CONNECTED || + output_data->status == TDM_OUTPUT_CONN_STATUS_MODE_SETTED) { + caps->mode_count = voutput_data->mode_count; + if (voutput_data->mode_count != 0) { + caps->modes = calloc(voutput_data->mode_count, sizeof(tdm_output_mode)); + if (!caps->modes) { + ret = TDM_ERROR_OUT_OF_MEMORY; + TDM_ERR("alloc failed\n"); + goto failed_get; + } + memcpy(caps->modes, voutput_data->output_modes, voutput_data->mode_count * sizeof(tdm_output_mode)); + } + + caps->mmWidth = voutput_data->mmwidth; + caps->mmHeight = voutput_data->mmheight; + } else { + caps->modes = NULL; + caps->mode_count = 0; + caps->mmWidth = 0; + caps->mmHeight = 0; + } + caps->subpixel = 1; + + caps->min_w = -1; + caps->min_h = -1; + caps->max_w = -1; + caps->max_h = -1; + caps->preferred_align = -1; + + caps->prop_count = 0; + + return TDM_ERROR_NONE; +failed_get: + memset(caps, 0, sizeof(tdm_caps_output)); + return ret; +} + +tdm_layer ** +virtual_output_get_layers(tdm_output *output, int *count, tdm_error *error) +{ + tdm_virtual_output_data *output_data = output; + tdm_virtual_layer_data *layer_data = NULL; + tdm_layer **layers; + tdm_error ret; + int i; + + RETURN_VAL_IF_FAIL(output_data, NULL); + RETURN_VAL_IF_FAIL(count, NULL); + + *count = 0; + LIST_FOR_EACH_ENTRY(layer_data, &output_data->layer_list, link) + (*count)++; + + if (*count == 0) { + ret = TDM_ERROR_NONE; + goto failed_get; + } + + /* will be freed in frontend */ + layers = calloc(*count, sizeof(tdm_virtual_layer_data *)); + if (!layers) { + TDM_ERR("failed: alloc memory"); + *count = 0; + ret = TDM_ERROR_OUT_OF_MEMORY; + goto failed_get; + } + + i = 0; + LIST_FOR_EACH_ENTRY(layer_data, &output_data->layer_list, link) + layers[i++] = layer_data; + + if (error) + *error = TDM_ERROR_NONE; + + return layers; +failed_get: + if (error) + *error = ret; + return NULL; +} + +tdm_error +virtual_output_wait_vblank(tdm_output *output, int interval, int sync, void *user_data) +{ + tdm_virtual_output_data *output_data = output; + tdm_virtual_event_data *event_data; + tdm_error ret; + + RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER); + + event_data = calloc(1, sizeof(tdm_virtual_event_data)); + if (!event_data) { + TDM_ERR("alloc failed"); + return TDM_ERROR_OUT_OF_MEMORY; + } + + event_data->type = TDM_VIRTUAL_EVENT_TYPE_WAIT; + event_data->output_data = output_data; + event_data->user_data = user_data; + + ret = _tdm_virtual_display_wait_vblank(output_data, interval, event_data); + if (ret != TDM_ERROR_NONE) { + free(event_data); + return ret; + } + + return TDM_ERROR_NONE; +} + +tdm_error +virtual_output_set_vblank_handler(tdm_output *output, tdm_output_vblank_handler func) +{ + tdm_virtual_output_data *output_data = output; + + RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(func, TDM_ERROR_INVALID_PARAMETER); + + output_data->vblank_func = func; + + return TDM_ERROR_NONE; +} + +tdm_error +virtual_output_commit(tdm_output *output, int sync, void *user_data) +{ + tdm_virtual_output_data *output_data = output; + tdm_virtual_voutput_data *voutput_data = NULL; + tdm_virtual_layer_data *layer_data = NULL; + tdm_virtual_event_data *event_data; + tdm_error ret; + + RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER); + + voutput_data = output_data->voutput_data; + RETURN_VAL_IF_FAIL(voutput_data, TDM_ERROR_INVALID_PARAMETER); + + if (output_data->mode_changed) { + output_data->mode_changed = 0; + output_data->status = TDM_OUTPUT_CONN_STATUS_MODE_SETTED; + + if (output_data->status_func) + output_data->status_func(output_data, TDM_OUTPUT_CONN_STATUS_MODE_SETTED, + output_data->status_user_data); + } + + if (voutput_data->vcommit_func) { + layer_data = output_data->primary_layer; + if (layer_data->display_buffer_changed) { + output_data->commit_user_data = user_data; + event_data = calloc(1, sizeof(tdm_virtual_event_data)); + if (!event_data) { + TDM_ERR("alloc failed"); + return TDM_ERROR_OUT_OF_MEMORY; + } + + event_data->type = TDM_VIRTUAL_EVENT_TYPE_VCOMMIT; + event_data->output_data = output_data; + event_data->user_data = user_data; + + ret = _tdm_virtual_display_wait_vblank(output_data, 1, event_data); + if (ret != TDM_ERROR_NONE) { + free(event_data); + return ret; + } + } + } else { + event_data = calloc(1, sizeof(tdm_virtual_event_data)); + if (!event_data) { + TDM_ERR("alloc failed"); + return TDM_ERROR_OUT_OF_MEMORY; + } + + event_data->type = TDM_VIRTUAL_EVENT_TYPE_COMMIT; + event_data->output_data = output_data; + event_data->user_data = user_data; + + ret = _tdm_virtual_display_wait_vblank(output_data, 1, event_data); + if (ret != TDM_ERROR_NONE) { + free(event_data); + return ret; + } + } + + return TDM_ERROR_NONE; +} + +tdm_error +virtual_output_set_commit_handler(tdm_output *output, tdm_output_commit_handler func) +{ + tdm_virtual_output_data *output_data = output; + + RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(func, TDM_ERROR_INVALID_PARAMETER); + + output_data->commit_func = func; + + return TDM_ERROR_NONE; +} + +tdm_error +virtual_output_set_dpms(tdm_output *output, tdm_output_dpms dpms_value) +{ + tdm_virtual_output_data *output_data = output; + + RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(dpms_value <= TDM_OUTPUT_DPMS_OFF, TDM_ERROR_INVALID_PARAMETER); + + TDM_DBG("dpms change [%d] -> [%d]", output_data->dpms, dpms_value); + + output_data->dpms = dpms_value; + + return TDM_ERROR_NONE; +} + +tdm_error +virtual_output_get_dpms(tdm_output *output, tdm_output_dpms *dpms_value) +{ + tdm_virtual_output_data *output_data = output; + + RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(dpms_value, TDM_ERROR_INVALID_PARAMETER); + + *dpms_value = output_data->dpms; + + return TDM_ERROR_NONE; +} + +tdm_error +virtual_output_set_mode(tdm_output *output, const tdm_output_mode *mode) +{ + tdm_virtual_output_data *output_data = output; + + RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(mode, TDM_ERROR_INVALID_PARAMETER); + + output_data->current_mode = mode; + output_data->mode_changed = 1; + + return TDM_ERROR_NONE; +} + +tdm_error +virtual_output_get_mode(tdm_output *output, const tdm_output_mode **mode) +{ + tdm_virtual_output_data *output_data = output; + + RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(mode, TDM_ERROR_INVALID_PARAMETER); + + *mode = output_data->current_mode; + + return TDM_ERROR_NONE; +} + +tdm_error +virtual_output_set_status_handler(tdm_output *output, + tdm_output_status_handler func, void *user_data) +{ + tdm_virtual_output_data *output_data = output; + + RETURN_VAL_IF_FAIL(output_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(func, TDM_ERROR_INVALID_PARAMETER); + + output_data->status_func = func; + output_data->status_user_data = user_data; + + return TDM_ERROR_NONE; +} + +tdm_error +virtual_output_set_available_mode(tdm_voutput *voutput, const tdm_output_mode *modes, int count) +{ + tdm_virtual_voutput_data *voutput_data = voutput; + tdm_virtual_output_data *output_data = NULL; + + RETURN_VAL_IF_FAIL(voutput_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(modes, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(count > 0, TDM_ERROR_INVALID_PARAMETER); + + output_data = voutput_data->output_data; + + /* set available mode only permittied disconnect status */ + RETURN_VAL_IF_FAIL(output_data->status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED, TDM_ERROR_BUSY); + + if (voutput_data->output_modes) + free(voutput_data->output_modes); + voutput_data->output_modes = NULL; + + voutput_data->output_modes = calloc(1, count * sizeof(tdm_output_mode)); + RETURN_VAL_IF_FAIL(voutput_data->output_modes != NULL, TDM_ERROR_OUT_OF_MEMORY); + + memcpy(voutput_data->output_modes, modes, count * sizeof(tdm_output_mode)); + voutput_data->mode_count = count; + + return TDM_ERROR_NONE; +} + +tdm_error +virtual_output_set_physical_size(tdm_voutput *voutput, unsigned int mmwidth, unsigned int mmheight) +{ + tdm_virtual_voutput_data *voutput_data = voutput; + + RETURN_VAL_IF_FAIL(voutput_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(mmwidth != 0, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(mmheight != 0, TDM_ERROR_INVALID_PARAMETER); + + voutput_data->mmwidth = mmwidth; + voutput_data->mmheight = mmheight; + + return TDM_ERROR_NONE; +} + +tdm_error +virtual_output_connect(tdm_voutput *voutput) +{ + tdm_virtual_voutput_data *voutput_data = voutput; + tdm_virtual_output_data *output_data = NULL; + + RETURN_VAL_IF_FAIL(voutput_data, TDM_ERROR_INVALID_PARAMETER); + + output_data = voutput_data->output_data; + + if (output_data->status == TDM_OUTPUT_CONN_STATUS_CONNECTED || + output_data->status == TDM_OUTPUT_CONN_STATUS_MODE_SETTED) + return TDM_ERROR_NONE; + + output_data->status = TDM_OUTPUT_CONN_STATUS_CONNECTED; + + if (output_data->status_func) + output_data->status_func(output_data, output_data->status, + output_data->status_user_data); + + return TDM_ERROR_NONE; +} + +tdm_error +virtual_output_disconnect(tdm_voutput *voutput) +{ + + tdm_virtual_voutput_data *voutput_data = voutput; + tdm_virtual_output_data *output_data = NULL; + + RETURN_VAL_IF_FAIL(voutput_data, TDM_ERROR_INVALID_PARAMETER); + + output_data = voutput_data->output_data; + + if (output_data->status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) + return TDM_ERROR_NONE; + + output_data->status = TDM_OUTPUT_CONN_STATUS_DISCONNECTED; + + if (output_data->status_func) + output_data->status_func(output_data, output_data->status, + output_data->status_user_data); + + return TDM_ERROR_NONE; +} + +tdm_error +virtual_layer_get_capability(tdm_layer *layer, tdm_caps_layer *caps) +{ + tdm_virtual_layer_data *layer_data = layer; + + RETURN_VAL_IF_FAIL(layer_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(caps, TDM_ERROR_INVALID_PARAMETER); + + memset(caps, 0, sizeof(tdm_caps_layer)); + + caps->capabilities = layer_data->capabilities; + caps->zpos = layer_data->zpos; + + caps->format_count = 2; + caps->formats = calloc(caps->format_count, sizeof(tbm_format)); + if (!caps->formats) { + TDM_ERR("alloc failed\n"); + free(caps->formats); + memset(caps, 0, sizeof(tdm_caps_layer)); + return TDM_ERROR_OUT_OF_MEMORY; + } + + caps->formats[0] = TBM_FORMAT_ARGB8888; + caps->formats[1] = TBM_FORMAT_XRGB8888; + + caps->prop_count = 0; + + return TDM_ERROR_NONE; +} + +tdm_error +virtual_layer_set_info(tdm_layer *layer, tdm_info_layer *info) +{ + tdm_virtual_layer_data *layer_data = layer; + + RETURN_VAL_IF_FAIL(layer_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(info, TDM_ERROR_INVALID_PARAMETER); + + layer_data->info = *info; + layer_data->info_changed = 1; + + return TDM_ERROR_NONE; +} + +tdm_error +virtual_layer_get_info(tdm_layer *layer, tdm_info_layer *info) +{ + tdm_virtual_layer_data *layer_data = layer; + + RETURN_VAL_IF_FAIL(layer_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(info, TDM_ERROR_INVALID_PARAMETER); + + *info = layer_data->info; + + return TDM_ERROR_NONE; +} + +tdm_error +virtual_layer_set_buffer(tdm_layer *layer, tbm_surface_h buffer) +{ + tdm_virtual_layer_data *layer_data = layer; + + RETURN_VAL_IF_FAIL(layer_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(buffer, TDM_ERROR_INVALID_PARAMETER); + + layer_data->display_buffer = buffer; + layer_data->display_buffer_changed = 1; + + return TDM_ERROR_NONE; +} + +tdm_error +virtual_layer_unset_buffer(tdm_layer *layer) +{ + tdm_virtual_layer_data *layer_data = layer; + + RETURN_VAL_IF_FAIL(layer_data, TDM_ERROR_INVALID_PARAMETER); + + layer_data->display_buffer = NULL; + layer_data->display_buffer_changed = 1; + + return TDM_ERROR_NONE; +} + +tdm_voutput * +virtual_output_create(tdm_backend_data *bdata, const char *name, tdm_error *error) +{ + tdm_virtual_data *virtual_data = bdata; + tdm_virtual_voutput_data *voutput_data = NULL; + tdm_virtual_output_data *output_data = NULL; + tdm_virtual_layer_data *layer_data = NULL; + tdm_error ret; + + if (!virtual_data || !name) { + TDM_ERR("invalid parameter"); + *error = TDM_ERROR_INVALID_PARAMETER; + return NULL; + } + + voutput_data = calloc(1, sizeof(tdm_virtual_voutput_data)); + if (!voutput_data) { + TDM_ERR("alloc failed"); + *error = TDM_ERROR_OUT_OF_MEMORY; + return NULL; + } + + voutput_data->output_modes = calloc(1, sizeof(tdm_output_mode)); + if (!voutput_data->output_modes) { + TDM_ERR("alloc failed"); + ret = TDM_ERROR_OUT_OF_MEMORY; + goto create_fail; + } + + /* default mode */ + snprintf(voutput_data->output_modes->name, TDM_NAME_LEN, "640x480"); + voutput_data->output_modes->vrefresh = 30; + voutput_data->output_modes->clock = 25200; + voutput_data->output_modes->hdisplay = 640; + voutput_data->output_modes->hsync_start = 656; + voutput_data->output_modes->hsync_end = 752; + voutput_data->output_modes->htotal = 800; + voutput_data->output_modes->hskew = 0; + voutput_data->output_modes->vdisplay = 480; + voutput_data->output_modes->vsync_start = 490; + voutput_data->output_modes->vsync_end = 492; + voutput_data->output_modes->vtotal = 525; + voutput_data->output_modes->vscan = 0; + voutput_data->output_modes->flags = 0; + voutput_data->output_modes->type = 0; + voutput_data->mode_count = 1; + + voutput_data->mmwidth = 10; + voutput_data->mmheight = 10; + + voutput_data->virtual_data = virtual_data; + + output_data = calloc(1, sizeof(tdm_virtual_output_data)); + if (!output_data) { + TDM_ERR("alloc failed"); + *error = TDM_ERROR_OUT_OF_MEMORY; + goto create_fail; + } + voutput_data->output_data = output_data; + + LIST_INITHEAD(&output_data->layer_list); + + output_data->virtual_data = virtual_data; + output_data->voutput_data = voutput_data; + output_data->pipe = 0; + output_data->connector_type = TDM_OUTPUT_TYPE_Unknown; + output_data->status = TDM_OUTPUT_CONN_STATUS_DISCONNECTED; + + if (name) { + snprintf(voutput_data->name, TDM_NAME_LEN, "%s", name); + snprintf(output_data->name, TDM_NAME_LEN, "%s", name); + } else { + snprintf(voutput_data->name, TDM_NAME_LEN, "unknown"); + snprintf(output_data->name, TDM_NAME_LEN, "%s", name); + } + + output_data->timer = tdm_event_loop_add_timer_handler(virtual_data->dpy, + _tdm_virtual_display_cb_timeout, + output_data, + &ret); + if (!output_data->timer) goto create_fail; + + LIST_INITHEAD(&output_data->timer_event_list); + + /* The TDM virtual backend output support only one layer. */ + layer_data = calloc(1, sizeof(tdm_virtual_layer_data)); + if (!layer_data) { + TDM_ERR("alloc failed"); + ret = TDM_ERROR_OUT_OF_MEMORY; + goto create_fail; + } + + layer_data->virtual_data = virtual_data; + layer_data->output_data = output_data; + layer_data->zpos = 0; + + layer_data->capabilities = TDM_LAYER_CAPABILITY_PRIMARY | TDM_LAYER_CAPABILITY_GRAPHIC; + output_data->primary_layer = layer_data; + + LIST_ADDTAIL(&voutput_data->link, &virtual_data->voutput_list); + LIST_ADDTAIL(&output_data->link, &virtual_data->output_list); + LIST_ADDTAIL(&layer_data->link, &output_data->layer_list); + + *error = TDM_ERROR_NONE; + + TDM_DBG("virtual output create(%s)(%p)(%p)", output_data->name, voutput_data, output_data); + + return voutput_data; + +create_fail: + if (layer_data) free(layer_data); + if (output_data) free(output_data); + if (voutput_data->output_modes) free(voutput_data->output_modes); + if (voutput_data) free(voutput_data); + + *error = ret; + + return NULL; +} + +tdm_error +virtual_output_destroy(tdm_voutput *voutput) +{ + tdm_virtual_data *virtual_data = NULL; + tdm_virtual_voutput_data *vo, *voutput_data = voutput; + int find = 0; + + RETURN_VAL_IF_FAIL(voutput_data, TDM_ERROR_INVALID_PARAMETER); + + virtual_data = voutput_data->virtual_data; + + LIST_FOR_EACH_ENTRY(vo, &virtual_data->voutput_list, link) { + if (vo == voutput_data) { + find = 1; + break; + } + } + + if (find) { + tdm_virtual_layer_data *l = NULL, *ll = NULL; + tdm_virtual_output_data *output_data = NULL; + + TDM_DBG("virtual output destroy(%s)", voutput_data->name); + + output_data = voutput_data->output_data; + + if (!LIST_IS_EMPTY(&output_data->timer_event_list)) { + tdm_virtual_event_data *e = NULL, *ee = NULL; + LIST_FOR_EACH_ENTRY_SAFE(e, ee, &output_data->timer_event_list, link) { + LIST_DEL(&e->link); + free(e); + } + } + + if (output_data->timer) + tdm_event_loop_source_remove(output_data->timer); + + LIST_FOR_EACH_ENTRY_SAFE(l, ll, &output_data->layer_list, link) { + LIST_DEL(&l->link); + free(l); + } + + LIST_DEL(&output_data->link); + free(output_data); + + if (voutput_data->output_modes) + free(voutput_data->output_modes); + LIST_DEL(&voutput_data->link); + free(voutput_data); + } else + return TDM_ERROR_INVALID_PARAMETER; + + return TDM_ERROR_NONE; +} + +tdm_output * +virtual_output_get_output(tdm_voutput *voutput, tdm_error *error) +{ + tdm_virtual_voutput_data *voutput_data = voutput; + + RETURN_VAL_IF_FAIL(voutput_data, NULL); + + if (!voutput_data) { + *error = TDM_ERROR_INVALID_PARAMETER; + return NULL; + } + + *error = TDM_ERROR_NONE; + + return voutput_data->output_data; +} + +tdm_error +virtual_output_set_commit_func(tdm_voutput *voutput, tdm_voutput_commit_handler commit_func) +{ + tdm_virtual_voutput_data *voutput_data = voutput; + + RETURN_VAL_IF_FAIL(voutput_data, TDM_ERROR_INVALID_PARAMETER); + RETURN_VAL_IF_FAIL(commit_func, TDM_ERROR_INVALID_PARAMETER); + + voutput_data->vcommit_func = commit_func; + + return TDM_ERROR_NONE; +} + +tdm_error +virtual_output_commit_done(tdm_voutput *voutput) +{ + tdm_virtual_voutput_data *voutput_data = voutput; + tdm_virtual_output_data *output_data = NULL; + unsigned int tv_sec, tv_usec; + static unsigned int sequence = 0; + tdm_virtual_event_data *event_data; + tdm_error ret = TDM_ERROR_NONE; + + RETURN_VAL_IF_FAIL(voutput_data, TDM_ERROR_INVALID_PARAMETER); + output_data = voutput_data->output_data; + + sequence++; + + _tdm_virtual_get_current_time(&tv_sec, &tv_usec); + + event_data = calloc(1, sizeof(tdm_virtual_event_data)); + if (!event_data) { + TDM_ERR("alloc failed"); + return TDM_ERROR_OUT_OF_MEMORY; + } + + event_data->type = TDM_VIRTUAL_EVENT_TYPE_COMMIT; + event_data->output_data = output_data; + event_data->user_data = output_data->commit_user_data; + + ret = _tdm_virtual_display_wait_vblank(output_data, 1, event_data); + if (ret != TDM_ERROR_NONE) { + free(event_data); + return ret; + } + + return TDM_ERROR_NONE; +} + diff --git a/configure.ac b/configure.ac index 8944c9e..51ceb3c 100644 --- a/configure.ac +++ b/configure.ac @@ -100,6 +100,7 @@ AC_OUTPUT([ src/Makefile backends/Makefile backends/dummy/Makefile + backends/virtual/Makefile client/libtdm-client.pc client/Makefile tools/Makefile diff --git a/packaging/libtdm.spec b/packaging/libtdm.spec index 7479fce..e9a5468 100644 --- a/packaging/libtdm.spec +++ b/packaging/libtdm.spec @@ -115,6 +115,7 @@ rm -f %{_unitdir_user}/basic.target.wants/tdm-socket-user.path %license COPYING %{_libdir}/libtdm.so.* %{_libdir}/tdm/libtdm-dummy.so +%{_libdir}/tdm/libtdm-virtual.so %attr(750,root,root) %{_bindir}/tdm-monitor %{_unitdir_user}/tdm-socket-user.path %{_unitdir_user}/tdm-socket-user.service diff --git a/src/tdm.c b/src/tdm.c index 886e847..e6866cc 100644 --- a/src/tdm.c +++ b/src/tdm.c @@ -818,6 +818,11 @@ _tdm_display_setup(tdm_private_display *private_display) } } + TDM_INFO("loading a %s backend", TDM_VIRTUAL_MODULE); + ret = _tdm_display_load_module_with_file(private_display, TDM_VIRTUAL_MODULE); + TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, failed_update); + TDM_GOTO_IF_FAIL(private_display->virtual_module != NULL, failed_update); + return TDM_ERROR_NONE; failed_update: @@ -1031,6 +1036,9 @@ _tdm_display_load_module_with_file(tdm_private_display *private_display, if (!strncmp(file, TDM_DUMMY_MODULE, TDM_NAME_LEN)) private_display->dummy_module = private_module; + if (!strncmp(file, TDM_VIRTUAL_MODULE, TDM_NAME_LEN)) + private_display->virtual_module = private_module; + private_module->bdata = bdata; if (ret != TDM_ERROR_NONE) { diff --git a/src/tdm_backend.c b/src/tdm_backend.c index c979129..12f6a2d 100644 --- a/src/tdm_backend.c +++ b/src/tdm_backend.c @@ -109,6 +109,30 @@ tdm_backend_register_func_output(tdm_display *dpy, tdm_func_output *func_output) } EXTERN tdm_error +tdm_backend_register_func_voutput(tdm_display *dpy, tdm_func_voutput *func_voutput) +{ + tdm_backend_module *module; + + TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), TDM_ERROR_OPERATION_FAILED); + + BACKEND_FUNC_ENTRY(); + + TDM_RETURN_VAL_IF_FAIL(func_voutput != NULL, TDM_ERROR_INVALID_PARAMETER); + + assert(private_display->current_module); + + /* the ABI version of backend module should be more than 1.1 */ + module = private_display->current_module->module_data; + if (_check_abi_version(module, 1, 1) < 0) + return TDM_ERROR_BAD_MODULE; + + private_display->current_module->func_voutput = *func_voutput; + + return TDM_ERROR_NONE; +} + + +EXTERN tdm_error tdm_backend_register_func_layer(tdm_display *dpy, tdm_func_layer *func_layer) { tdm_backend_module *module; diff --git a/src/tdm_display.c b/src/tdm_display.c index 4ee693e..7d2f82b 100644 --- a/src/tdm_display.c +++ b/src/tdm_display.c @@ -805,6 +805,15 @@ tdm_display_find_output(tdm_display *dpy, const char *name, tdm_error *error) } } + if (private_display->virtual_module) { + private_module = private_display->virtual_module; + LIST_FOR_EACH_ENTRY(private_output, &private_module->output_list, link) { + if (strncmp(private_output->name, name, TDM_NAME_LEN)) + continue; + _pthread_mutex_unlock(&private_display->lock); + return private_output; + } + } _pthread_mutex_unlock(&private_display->lock); return NULL;