virtual: add virtual backend module 98/191398/3
authorJunkyeong Kim <jk0430.kim@samsung.com>
Tue, 16 Oct 2018 12:28:25 +0000 (21:28 +0900)
committerJunkyeong Kim <jk0430.kim@samsung.com>
Thu, 1 Nov 2018 02:32:26 +0000 (02:32 +0000)
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 <jk0430.kim@samsung.com>
backends/Makefile.am
backends/virtual/Makefile.am [new file with mode: 0644]
backends/virtual/tdm_virtual.c [new file with mode: 0644]
backends/virtual/tdm_virtual.h [new file with mode: 0644]
backends/virtual/tdm_virtual_display.c [new file with mode: 0644]
configure.ac
packaging/libtdm.spec
src/tdm.c
src/tdm_backend.c
src/tdm_display.c

index 4a66860..f841dd0 100644 (file)
@@ -1 +1 @@
-SUBDIRS = dummy
+SUBDIRS = dummy virtual
diff --git a/backends/virtual/Makefile.am b/backends/virtual/Makefile.am
new file mode 100644 (file)
index 0000000..099b71e
--- /dev/null
@@ -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 (file)
index 0000000..f4ad7fe
--- /dev/null
@@ -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 (file)
index 0000000..c85dff6
--- /dev/null
@@ -0,0 +1,89 @@
+#ifndef _TDM_VIRTUAL_H_
+#define _TDM_VIRTUAL_H_
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <errno.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+
+#include <tbm_surface.h>
+#include <tbm_surface_internal.h>
+#include <tdm_backend.h>
+#include <tdm_log.h>
+#include <tdm_list.h>
+#include <tdm_helper.h>
+
+/* 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 (file)
index 0000000..fe4a5c9
--- /dev/null
@@ -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;
+}
+
index 8944c9e..51ceb3c 100644 (file)
@@ -100,6 +100,7 @@ AC_OUTPUT([
        src/Makefile
        backends/Makefile
        backends/dummy/Makefile
+       backends/virtual/Makefile
        client/libtdm-client.pc
        client/Makefile
        tools/Makefile
index 7479fce..e9a5468 100644 (file)
@@ -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
index 886e847..e6866cc 100644 (file)
--- 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) {
index c979129..12f6a2d 100644 (file)
@@ -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;
index 4ee693e..7d2f82b 100644 (file)
@@ -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;