From b200a7e58bbb86acf6af5e25031bb47210b985b5 Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Wed, 10 Oct 2018 14:47:01 +0900 Subject: [PATCH 01/16] hwc: remove the unused enum type Change-Id: I8ccb01dd67b0b75c96274974007231290d2ee118 --- include/tdm_types.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/include/tdm_types.h b/include/tdm_types.h index 072f466..86ad6e7 100644 --- a/include/tdm_types.h +++ b/include/tdm_types.h @@ -218,14 +218,6 @@ typedef enum { } tdm_hwc_window_constraint; /** - * @brief The hwc window flag enumeration - * @since 2.0.0 - */ -typedef enum { - TDM_HWC_WINDOW_FLAG_NONE = 0, -} tdm_hwc_window_flag; - -/** * @brief The tdm display object */ typedef void tdm_display; -- 2.7.4 From 0e9134c961756bf62b6e77039a5bca36506c254e Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Wed, 10 Oct 2018 15:10:45 +0900 Subject: [PATCH 02/16] hwc: modify the doxygen of tdm_hwc_window_composition Change-Id: Ie27771f551f9885ed2168d7a05ad717acba20c18 --- include/tdm_types.h | 55 +++++++++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/include/tdm_types.h b/include/tdm_types.h index 86ad6e7..b87fc18 100644 --- a/include/tdm_types.h +++ b/include/tdm_types.h @@ -161,47 +161,52 @@ typedef struct _tdm_info_capture { */ typedef enum { - /** Set by the client for an invisible window. The value by default. + /** The composition type for an invisible window. The value by default. * - * The device ignores windows of this type. + * The backend ignores windows of this type. */ TDM_COMPOSITION_NONE = 0, - /** The client will composite this window into the client target window + /** The compostion type for an window to be compsoited by the client. * - * User can choose this type for window to avoid a hardware composition for - * this window. + * When the client sets this composition type, + * the backend MUST NOT modify this composition type into other types. * - * The device must not request any composition type changes for windows of - * this type. + * When the backend changes TDM_COMPOSITION_DEVICE or TDM_COMPOSITION_CURSOR + * to this composition type at the time of tdm_hwc_validate and + * tdm_hwc_get_changed_composition_types, + * the client has to composite this window with gl or other drawing operations. */ TDM_COMPOSITION_CLIENT = 1, - /** Set by the HWC after tdm_hwc_validate(). + /** The compostion type for an window to be set to the hw overlay. * - * The device will handle the composition of this window through a hardware - * overlay or other similar means. - * - * Upon tdm_hwc_validate(), the device may request a change from this type to - * TDM_COMPOSITION_CLIENT or TDM_COMPOSITION_DEVICE_CANDIDATE. */ + * The client sets this composition type to the visible windows before requesting + * the tdm_hwc_validate. + * Nomally, the backend leave it if the backend sets the window to the hw overlay. + * If the backend does not set the window to the hw overlay at the time of + * tdm_hwc_validate, the backend changes the composition type of the window into + * TDM_COMPOSITION_CLIENT. + */ TDM_COMPOSITION_DEVICE = 3, - /** Similar to DEVICE, but the position of this layer may also be set - * asynchronously through layer_set_cursor_position. If this functionality is not - * supported on a layer that the client sets to TDM_COMPOSITION_CURSOR, the - * device must request that the composition type of that layer is changed to - * TDM_COMPOSITION_CLIENT upon the next call to tdm_hwc_validate(). + /** The compostion type for an window to be set to the cursor hw overlay. * - * Upon tdm_hwc_validate(), the device may request a change from this type to - * either TDM_COMPOSITION_DEVICE or TDM_COMPOSITION_CLIENT. Changing to - * TDM_COMPOSITION_DEVICE will prevent the use of layer_set_cursor_position but - * still permit the device to composite the layer. */ + * The client sets this composition type to the cursor window before requesting + * the tdm_hwc_validate. + * If the backend does not support the cursor hw overlay, the backend can change + * the comopsition type into the TDM_COMPOSITION_CLIENT. + */ TDM_COMPOSITION_CURSOR = 4, - /** This type is for the VIDEO window which can be set to the reserved hw overlay - * which is assigned by the device. + /** The compostion type for an window to be set to the video hw overlay. * - * Normally, this VIDEO window displays under the primary hw overlayer of the output. */ + * The client sets this composition type to the video window before requesting + * the tdm_hwc_validate. + * If the backend does not support the video hw overlay, the backend can change + * the comopsition type into the TDM_COMPOSITION_CLIENT. + * Normally, this VIDEO window displays under the primary hw overlayer of the output. + */ TDM_COMPOSITION_VIDEO = 5, } tdm_hwc_window_composition; -- 2.7.4 From ed387c18be48e1de769bde246ad9bf7b4b4183ec Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Thu, 11 Oct 2018 10:28:16 +0900 Subject: [PATCH 03/16] Package version up to 2.3.1 Change-Id: I7397ead44069ada32db5591e15b3c5a56e596976 --- packaging/libtdm.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/libtdm.spec b/packaging/libtdm.spec index 71b69cc..5bd715e 100644 --- a/packaging/libtdm.spec +++ b/packaging/libtdm.spec @@ -2,7 +2,7 @@ %define HALTESTS_GCOV 0 Name: libtdm -Version: 2.3.0 +Version: 2.3.1 Release: 0 Summary: User Library of Tizen Display Manager Group: Development/Libraries -- 2.7.4 From 4b871459cd1dc0beb11ada9d0f8acace511503e7 Mon Sep 17 00:00:00 2001 From: Seunghun Lee Date: Wed, 27 Jun 2018 19:23:33 +0900 Subject: [PATCH 04/16] tdm_client: Introduce 'tdm_virtual_output' on the client side. NOTE: This needs extra implementation of server side to be operated properly. APIs of 'tdm_virtual_output' will provide services to applications like Wi-Fi display manager. Any application which has proper privilege can ask tdm_server for considering itself as a sort of virtual output. Once an application acquires the instance of 'tdm_virtual_output' successfully, server considers it as a normal output. Change-Id: Ib6085db09521facd90d28637056b48e166f8a90e Signed-off-by: Junkyeong Kim --- client/tdm_client.c | 902 ++++++++++++++++++++++++++++++++++++++++- client/tdm_client.h | 39 ++ client/tdm_client_types.h | 20 + configure.ac | 2 +- haltests/src/tc_tdm_client.cpp | 384 +++++++++++++++++- libtdm.pc.in | 2 +- packaging/libtdm.spec | 1 + protocol/tdm.xml | 89 ++++ src/tdm_server.c | 86 ++++ 9 files changed, 1516 insertions(+), 9 deletions(-) diff --git a/client/tdm_client.c b/client/tdm_client.c index 4023208..fbb875b 100644 --- a/client/tdm_client.c +++ b/client/tdm_client.c @@ -33,6 +33,8 @@ * **************************************************************************/ +#define WL_HIDE_DEPRECATED + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -52,8 +54,12 @@ #include "tdm_list.h" #include "tdm.h" #include "tdm_private.h" +#include + +#define TDM_ARRAY_NTH_DATA(array, type, n) (((type*)((array)->data)) + n) typedef struct _tdm_private_client_vblank tdm_private_client_vblank; +typedef struct _tdm_private_client_voutput tdm_private_client_voutput; typedef struct _tdm_private_client { pthread_mutex_t lock; @@ -63,6 +69,7 @@ typedef struct _tdm_private_client { struct wl_registry *registry; struct wl_tdm *tdm; struct list_head output_list; + struct list_head voutput_list; unsigned int enable_ttrace; unsigned int stamp; @@ -71,6 +78,8 @@ typedef struct _tdm_private_client { } tdm_private_client; typedef struct _tdm_private_client_output { + struct list_head link; + tdm_private_client *private_client; char name[TDM_NAME_LEN]; @@ -86,9 +95,39 @@ typedef struct _tdm_private_client_output { unsigned int req_id; unsigned int watch_output_changes; - struct list_head link; + tdm_private_client_voutput *voutput; } tdm_private_client_output; +typedef struct _tdm_private_client_buffer { + struct list_head link; + struct wl_buffer *wl_buffer; +} tdm_private_client_buffer; + +struct _tdm_private_client_voutput { + struct list_head link; + struct wl_tdm_voutput *wl_voutput; + struct list_head commit_handler_list; + + struct { + int count; + tdm_client_output_mode *modes; + } available_modes; + + unsigned int mmwidth; + unsigned int mmheight; + + uint32_t msg; + + struct list_head buffer_list; + tbm_bufmgr bufmgr; + tdm_private_client_buffer *attach_buffer; + + tdm_private_client *private_client; + tdm_private_client_output *private_output; + char name[TDM_NAME_LEN]; + int get_output; +}; + struct _tdm_private_client_vblank { tdm_private_client_output *private_output; @@ -135,6 +174,16 @@ typedef struct _tdm_client_wait_info { struct list_head call_link; } tdm_client_wait_info; +typedef struct _tdm_client_voutput_commit_handler_info { + tdm_private_client_voutput *private_voutput; + + tdm_client_voutput_commit_handler func; + void *user_data; + + struct list_head link; + struct list_head call_link; +} tdm_client_voutput_commit_handler_info; + static unsigned int _tdm_client_check_wl_error(tdm_private_client *private_client, const char *func, int line) { @@ -446,6 +495,7 @@ tdm_client_create(tdm_error *error) } LIST_INITHEAD(&private_client->output_list); + LIST_INITHEAD(&private_client->voutput_list); private_client->display = wl_display_connect("tdm-socket"); TDM_GOTO_IF_FAIL(private_client->display != NULL, create_failed); @@ -482,6 +532,7 @@ tdm_client_destroy(tdm_client *client) { tdm_private_client *private_client = (tdm_private_client*)client; tdm_private_client_output *o = NULL, *oo = NULL; + tdm_private_client_voutput *vo = NULL, *voo = NULL; if (!private_client) return; @@ -498,6 +549,10 @@ tdm_client_destroy(tdm_client *client) _tdm_client_output_destroy(o); } + LIST_FOR_EACH_ENTRY_SAFE(vo, voo, &private_client->voutput_list, link) { + tdm_client_voutput_destroy(vo); + } + if (private_client->tdm) wl_tdm_destroy(private_client->tdm); if (private_client->registry) @@ -772,11 +827,24 @@ tdm_client_get_output(tdm_client *client, char *name, tdm_error *error) if (!name) { name = "primary"; - } else if (strncmp(name, "primary", 7) && strncmp(name, "default", 7)) { - if (error) - *error = TDM_ERROR_INVALID_PARAMETER; - pthread_mutex_unlock(&private_client->lock); - return NULL; + } else if (!strncmp(name, "primary", 7) || !strncmp(name, "default", 7)) { + TDM_DBG("get primary or default output"); + } else { + tdm_private_client_voutput *private_voutput = NULL; + int find = 0; + + LIST_FOR_EACH_ENTRY(private_voutput, &private_client->voutput_list, link) { + if (!strncmp(private_voutput->name, name, TDM_NAME_LEN)) { + find = 1; + break; + } + } + if (!find) { + if (error) + *error = TDM_ERROR_INVALID_PARAMETER; + pthread_mutex_unlock(&private_client->lock); + return NULL; + } } LIST_FOR_EACH_ENTRY(private_output, &private_client->output_list, link) { @@ -1591,3 +1659,825 @@ tdm_client_vblank_is_waiting(tdm_client_vblank *vblank) return (LIST_LENGTH(&private_vblank->wait_list) > 0) ? 1 : 0; } + +static tbm_surface_h +_tdm_client_voutput_create_surface_from_param(tbm_bufmgr bufmgr, + int is_fd, + int32_t width, + int32_t height, + uint32_t format, + int32_t bpp, + int32_t size, + int32_t num_plane, + struct wl_array *plane_buf_idx, + struct wl_array *plane_offset, + struct wl_array *plane_stride, + struct wl_array *plane_size, + uint32_t flags, + int32_t num_buf, + uint32_t buf0, + uint32_t buf1, + uint32_t buf2) +{ + int32_t names[TBM_SURF_PLANE_MAX] = { -1, -1, -1, -1}; + tbm_surface_info_s info = { 0, }; + tbm_bo bos[TBM_SURF_PLANE_MAX]; + int i, numPlane, numName; + tbm_surface_h tbm_surface; + + numPlane = tbm_surface_internal_get_num_planes(format); + TDM_RETURN_VAL_IF_FAIL(numPlane == num_plane, NULL); + + info.width = width; + info.height = height; + info.format = format; + info.bpp = bpp; + info.size = size; + info.num_planes = numPlane; + + /*Fill plane info*/ + for (i = 0; i < numPlane; i++) { + info.planes[i].offset = *TDM_ARRAY_NTH_DATA(plane_offset, int32_t, i); + info.planes[i].stride = *TDM_ARRAY_NTH_DATA(plane_stride, int32_t, i); + info.planes[i].size = *TDM_ARRAY_NTH_DATA(plane_size, int32_t, i); + } + + /*Fill buffer*/ + numName = num_buf; + names[0] = buf0; + names[1] = buf1; + names[2] = buf2; + + for (i = 0; i < numName; i++) { + if (is_fd) + bos[i] = tbm_bo_import_fd(bufmgr, names[i]); + else + bos[i] = tbm_bo_import(bufmgr, names[i]); + } + + tbm_surface = tbm_surface_internal_create_with_bos(&info, bos, numName); + if (tbm_surface == NULL) { + if (is_fd) { + close(buf0); + close(buf1); + close(buf2); + } + return NULL; + } + + if (is_fd) { + close(buf0); + close(buf1); + close(buf2); + } + + for (i = 0; i < numName; i++) + tbm_bo_unref(bos[i]); + + return tbm_surface; +} +static void +tdm_client_voutput_cb_buffer_import_with_id(void *data, + struct wl_tdm_voutput *wl_voutput, + struct wl_buffer *wl_buffer, + int32_t width, + int32_t height, + uint32_t format, + int32_t bpp, + int32_t size, + int32_t num_plane, + struct wl_array *plane_buf_idx, + struct wl_array *plane_offset, + struct wl_array *plane_stride, + struct wl_array *plane_size, + uint32_t flags, + int32_t num_buf, + uint32_t buf0, + uint32_t buf1, + uint32_t buf2) +{ + tdm_private_client_voutput *private_voutput = (tdm_private_client_voutput *)data; + tdm_private_client_buffer *buffer = NULL; + tbm_surface_h tbm_surface; + + TDM_RETURN_IF_FAIL(private_voutput != NULL); + + buffer = calloc(1, sizeof *buffer); + TDM_RETURN_IF_FAIL(buffer != NULL); + + tbm_surface = _tdm_client_voutput_create_surface_from_param(private_voutput->bufmgr, 0, + width, height, format, bpp, size, + num_plane, + plane_buf_idx, plane_offset, plane_stride, plane_size, + 0, + num_buf, + buf0, buf1, buf2); + TDM_GOTO_IF_FAIL(tbm_surface != NULL, fail); + + tbm_surface_internal_ref(tbm_surface); + wl_buffer_set_user_data(wl_buffer, tbm_surface); + + buffer->wl_buffer = wl_buffer; + + LIST_ADDTAIL(&buffer->link, &private_voutput->buffer_list); + + return; + +fail: + if (buffer) + free(buffer); + + if (wl_buffer) + wl_buffer_destroy(wl_buffer); +} + +static void +tdm_client_voutput_cb_buffer_import_with_fd(void *data, + struct wl_tdm_voutput *wl_voutput, + struct wl_buffer *wl_buffer, + int32_t width, + int32_t height, + uint32_t format, + int32_t bpp, + int32_t size, + int32_t num_plane, + struct wl_array *plane_buf_idx, + struct wl_array *plane_offset, + struct wl_array *plane_stride, + struct wl_array *plane_size, + uint32_t flags, + int32_t num_buf, + int32_t buf0, + int32_t buf1, + int32_t buf2) +{ + tdm_private_client_voutput *private_voutput = (tdm_private_client_voutput *)data; + tdm_private_client_buffer *buffer = NULL; + tbm_surface_h tbm_surface; + + TDM_RETURN_IF_FAIL(private_voutput != NULL); + + buffer = calloc(1, sizeof *buffer); + TDM_RETURN_IF_FAIL(buffer != NULL); + + tbm_surface = _tdm_client_voutput_create_surface_from_param(private_voutput->bufmgr, 1, + width, height, format, bpp, size, + num_plane, + plane_buf_idx, plane_offset, plane_stride, plane_size, + 0, + num_buf, + buf0, buf1, buf2); + TDM_GOTO_IF_FAIL(tbm_surface != NULL, fail); + + tbm_surface_internal_ref(tbm_surface); + wl_buffer_set_user_data(wl_buffer, tbm_surface); + + buffer->wl_buffer = wl_buffer; + + LIST_ADDTAIL(&buffer->link, &private_voutput->buffer_list); + + return; + +fail: + if (buffer) + free(buffer); + + if (wl_buffer) + wl_buffer_destroy(wl_buffer); +} + +static void +tdm_client_voutput_cb_buffer_destroy(void *data, + struct wl_tdm_voutput *wl_voutput, + struct wl_buffer *wl_buffer) +{ + tdm_private_client_voutput *private_voutput = (tdm_private_client_voutput *)data; + tdm_private_client_buffer *cb = NULL, *cbb = NULL; + tbm_surface_h tbm_surface = NULL; + + TDM_RETURN_IF_FAIL(private_voutput != NULL); + + LIST_FOR_EACH_ENTRY_SAFE(cb, cbb, &private_voutput->buffer_list, link) { + if (wl_buffer == cb->wl_buffer) { + LIST_DEL(&cb->link); + + tbm_surface = (tbm_surface_h)wl_buffer_get_user_data(wl_buffer); + if (tbm_surface) + tbm_surface_internal_unref(tbm_surface); + + wl_buffer_set_user_data(wl_buffer, NULL); + wl_buffer_destroy(wl_buffer); + + free(cb); + + break; + } + } + + return; +} + +void +tdm_client_voutput_cb_attach_buffer(void *data, + struct wl_tdm_voutput *wl_voutput, + struct wl_buffer *wl_buffer) +{ + tdm_private_client_voutput *private_voutput = (tdm_private_client_voutput *)data; + tdm_private_client_buffer *cb = NULL, *cbb = NULL; + + TDM_RETURN_IF_FAIL(private_voutput != NULL); + + LIST_FOR_EACH_ENTRY_SAFE(cb, cbb, &private_voutput->buffer_list, link) { + if (wl_buffer == cb->wl_buffer) { + private_voutput->attach_buffer = cb; + break; + } + } + + return; +} + +void +tdm_client_voutput_cb_commit(void *data, struct wl_tdm_voutput *wl_voutput) +{ + tdm_private_client_voutput *private_voutput; + tdm_private_client *private_client; + tbm_surface_h buffer = NULL; + tdm_client_voutput_commit_handler_info *h = NULL, *hh = NULL; + struct list_head call_list; + + private_voutput = (tdm_private_client_voutput *)data; + TDM_RETURN_IF_FAIL(private_voutput != NULL); + TDM_RETURN_IF_FAIL(private_voutput->attach_buffer != NULL); + + buffer = (tbm_surface_h)wl_buffer_get_user_data(private_voutput->attach_buffer->wl_buffer); + TDM_RETURN_IF_FAIL(buffer != NULL); + + tbm_surface_internal_ref(buffer); + + private_client = private_voutput->private_client; + + LIST_INITHEAD(&call_list); + + LIST_FOR_EACH_ENTRY(h, &private_voutput->commit_handler_list, link) { + LIST_ADDTAIL(&h->call_link, &call_list); + } + + pthread_mutex_unlock(&private_client->lock); + LIST_FOR_EACH_ENTRY_SAFE(h, hh, &call_list, call_link) { + if (h->func) + h->func(private_voutput, buffer, h->user_data); + } + + /* if no added commit_handler call commit done immediately */ + if (LIST_IS_EMPTY(&private_voutput->commit_handler_list)) + tdm_client_voutput_commit_done(private_voutput); + + pthread_mutex_lock(&private_client->lock); +} + +void +tdm_client_voutput_cb_ack_message(void *data, struct wl_tdm_voutput *wl_voutput, uint32_t msg) +{ + tdm_private_client_voutput *private_voutput = data; + + private_voutput->msg = msg; +} + +static const struct wl_tdm_voutput_listener tdm_client_voutput_lisntener = { + tdm_client_voutput_cb_buffer_import_with_id, + tdm_client_voutput_cb_buffer_import_with_fd, + tdm_client_voutput_cb_buffer_destroy, + tdm_client_voutput_cb_attach_buffer, + tdm_client_voutput_cb_commit, + tdm_client_voutput_cb_ack_message +}; + +tdm_client_voutput * +tdm_client_create_voutput(tdm_client *client, const char *name, tdm_error *error) +{ + tdm_private_client *private_client; + tdm_private_client_output *private_output; + tdm_private_client_voutput *private_voutput; + struct wl_proxy *wrapper; + + if (error) + *error = TDM_ERROR_NONE; + + if (!client) { + TDM_ERR("'!client' failed"); + if (error) + *error = TDM_ERROR_INVALID_PARAMETER; + return NULL; + } + + if (!name) { + TDM_ERR("'!name' failed"); + if (error) + *error = TDM_ERROR_INVALID_PARAMETER; + return NULL; + } + + private_client = (tdm_private_client *)client; + + pthread_mutex_lock(&private_client->lock); + + if (CHECK_WL_PROTOCOL_ERROR(private_client)) { + if (error) + *error = TDM_ERROR_PROTOCOL_ERROR; + pthread_mutex_unlock(&private_client->lock); + return NULL; + } + + LIST_FOR_EACH_ENTRY(private_output, &private_client->output_list, link) { + if (!strncmp(private_output->name, name, TDM_NAME_LEN)) { + if (error) + *error = TDM_ERROR_INVALID_PARAMETER; // FIXME define new error type. + pthread_mutex_unlock(&private_client->lock); + return NULL; + } + } + + wrapper = wl_proxy_create_wrapper(private_client->tdm); + if (!wrapper) { + TDM_ERR("create virtual output wrapper failed"); + if (error) + *error = TDM_ERROR_OUT_OF_MEMORY; + pthread_mutex_unlock(&private_client->lock); + return NULL; + } + + wl_proxy_set_queue(wrapper, private_client->queue); + + private_voutput = calloc(1, sizeof *private_voutput); + if (!private_voutput) { + /* LOCV_EXCL_START */ + wl_proxy_wrapper_destroy(wrapper); + TDM_ERR("alloc failed"); + if (error) + *error = TDM_ERROR_OUT_OF_MEMORY; + pthread_mutex_unlock(&private_client->lock); + return NULL; + /* LOCV_EXCL_STOP */ + } + + private_voutput->bufmgr = tbm_bufmgr_init(-1); + if (private_voutput->bufmgr == NULL) { + /* LCOV_EXCL_START */ + TDM_ERR("fail tbm_bufmgr_init"); + free(private_voutput); + if (error) + *error = TDM_ERROR_OUT_OF_MEMORY; + pthread_mutex_unlock(&private_client->lock); + return NULL; + /* LCOV_EXCL_STOP */ + } + + LIST_INITHEAD(&private_voutput->commit_handler_list); + LIST_INITHEAD(&private_voutput->buffer_list); + + private_voutput->private_client = private_client; + strncpy(private_voutput->name, name, TDM_NAME_LEN); + + private_voutput->wl_voutput = wl_tdm_create_voutput((struct wl_tdm *)wrapper, name); + wl_proxy_wrapper_destroy(wrapper); + if (!private_voutput->wl_voutput) { + /* LCOV_EXCL_START */ + TDM_ERR("couldn't create voutput resource"); + free(private_voutput); + if (error) + *error = TDM_ERROR_OUT_OF_MEMORY; + pthread_mutex_unlock(&private_client->lock); + return NULL; + /* LCOV_EXCL_STOP */ + } + + wl_tdm_voutput_add_listener(private_voutput->wl_voutput, + &tdm_client_voutput_lisntener, private_voutput); + wl_display_roundtrip_queue(private_client->display, private_client->queue); + + wl_proxy_set_queue((struct wl_proxy *)private_voutput->wl_voutput, NULL); + + if (CHECK_WL_PROTOCOL_ERROR(private_client)) { + wl_tdm_voutput_destroy(private_voutput->wl_voutput); + free(private_voutput); + if (error) + *error = TDM_ERROR_PROTOCOL_ERROR; + pthread_mutex_unlock(&private_client->lock); + return NULL; + } + + if (private_voutput->msg != WL_TDM_VOUTPUT_MESSAGE_ADDED) { + wl_tdm_voutput_destroy(private_voutput->wl_voutput); + free(private_voutput); + if (error) + *error = TDM_ERROR_PROTOCOL_ERROR; // FIXME add new error type. + pthread_mutex_unlock(&private_client->lock); + return NULL; + } + + LIST_ADDTAIL(&private_voutput->link, &private_client->voutput_list); + + pthread_mutex_unlock(&private_client->lock); + + return (tdm_client_voutput *)private_voutput; +} + +void +tdm_client_voutput_destroy(tdm_client_voutput *voutput) +{ + tdm_private_client_voutput *private_voutput = (tdm_private_client_voutput *)voutput; + tdm_private_client *private_client; + tdm_client_voutput_commit_handler_info *h = NULL, *hh = NULL; + + if (!private_voutput) + return; + private_client = private_voutput->private_client; + + pthread_mutex_lock(&private_client->lock); + + if (!(LIST_IS_EMPTY(&private_voutput->buffer_list))) { + tdm_private_client_buffer *cb = NULL, *cbb = NULL; + + LIST_FOR_EACH_ENTRY_SAFE(cb, cbb, &private_voutput->buffer_list, link) { + tbm_surface_h tbm_surface = NULL; + + if (!cb) continue; + + LIST_DEL(&cb->link); + + tbm_surface = (tbm_surface_h)wl_buffer_get_user_data(cb->wl_buffer); + if (tbm_surface) + tbm_surface_internal_unref(tbm_surface); + + wl_buffer_set_user_data(cb->wl_buffer, NULL); + wl_buffer_destroy(cb->wl_buffer); + + free(cb); + } + } + + if (private_voutput->bufmgr) + tbm_bufmgr_deinit(private_voutput->bufmgr); + + if (private_voutput->available_modes.modes) + free(private_voutput->available_modes.modes); + + LIST_FOR_EACH_ENTRY_SAFE(h, hh, &private_voutput->commit_handler_list, link) { + LIST_DEL(&h->link); + free(h); + } + + if (private_voutput->get_output) + _tdm_client_output_destroy(private_voutput->private_output); + + wl_tdm_voutput_destroy(private_voutput->wl_voutput); + + LIST_DEL(&private_voutput->link); + + free(private_voutput); + + pthread_mutex_unlock(&private_client->lock); +} + +tdm_error +tdm_client_voutput_set_available_modes(tdm_client_voutput *voutput, const tdm_client_output_mode *modes, int count) +{ + tdm_private_client_voutput *private_voutput; + tdm_private_client *private_client; + tdm_error ret = TDM_ERROR_NONE; + + TDM_RETURN_VAL_IF_FAIL(voutput != NULL, TDM_ERROR_INVALID_PARAMETER); + + if ((count > 0) && (modes == NULL)) + return TDM_ERROR_INVALID_PARAMETER; + + private_voutput = (tdm_private_client_voutput *)voutput; + private_client = private_voutput->private_client; + + if (!private_voutput->private_output) { + private_voutput->private_output = tdm_client_voutput_get_client_output(private_voutput, &ret); + TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, TDM_ERROR_OPERATION_FAILED); + } + + pthread_mutex_lock(&private_client->lock); + + if (private_voutput->private_output->connection != TDM_OUTPUT_CONN_STATUS_DISCONNECTED) { + pthread_mutex_unlock(&private_client->lock); + return TDM_ERROR_BAD_REQUEST; + } + + if (private_voutput->available_modes.modes) + free(private_voutput->available_modes.modes); + + private_voutput->available_modes.count = count; + + if (count != 0) { + private_voutput->available_modes.modes = calloc(count, sizeof(tdm_client_output_mode)); + memcpy(private_voutput->available_modes.modes, modes, sizeof(tdm_client_output_mode) * count); + } + + pthread_mutex_unlock(&private_client->lock); + + return TDM_ERROR_NONE; +} + +tdm_error +tdm_client_voutput_set_physical_size(tdm_client_voutput *voutput, unsigned int mmWidth, unsigned int mmHeight) +{ + tdm_private_client_voutput *private_voutput; + tdm_private_client *private_client; + tdm_error ret = TDM_ERROR_NONE; + + TDM_RETURN_VAL_IF_FAIL(voutput != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(mmWidth != 0, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(mmHeight != 0, TDM_ERROR_INVALID_PARAMETER); + + private_voutput = (tdm_private_client_voutput *)voutput; + private_client = private_voutput->private_client; + + if (!private_voutput->private_output) { + private_voutput->private_output = tdm_client_voutput_get_client_output(private_voutput, &ret); + TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, TDM_ERROR_OPERATION_FAILED); + } + + pthread_mutex_lock(&private_client->lock); + + if (private_voutput->private_output->connection != TDM_OUTPUT_CONN_STATUS_DISCONNECTED) { + pthread_mutex_unlock(&private_client->lock); + return TDM_ERROR_BAD_REQUEST; + } + + private_voutput->mmwidth = mmWidth; + private_voutput->mmheight = mmHeight; + + pthread_mutex_unlock(&private_client->lock); + + return TDM_ERROR_NONE; +} + +tdm_error +tdm_client_voutput_add_commit_handler(tdm_client_voutput *voutput, + tdm_client_voutput_commit_handler func, + void *user_data) +{ + tdm_private_client_voutput *private_voutput; + tdm_private_client *private_client; + tdm_client_voutput_commit_handler_info *h = NULL; + + TDM_RETURN_VAL_IF_FAIL(voutput != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER); + + private_voutput = (tdm_private_client_voutput *)voutput; + private_client = private_voutput->private_client; + + LIST_FOR_EACH_ENTRY(h, &private_voutput->commit_handler_list, link) { + if (h->func == func && h->user_data == user_data) { + TDM_ERR("can't add twice"); + return TDM_ERROR_BAD_REQUEST; + } + } + + h = calloc(1, sizeof *h); + TDM_RETURN_VAL_IF_FAIL(h != NULL, TDM_ERROR_OUT_OF_MEMORY); + + pthread_mutex_lock(&private_client->lock); + + if (CHECK_WL_PROTOCOL_ERROR(private_client)) { + free(h); + pthread_mutex_unlock(&private_client->lock); + return TDM_ERROR_PROTOCOL_ERROR; + } + + h->private_voutput = private_voutput; + h->func = func; + h->user_data = user_data; + LIST_ADDTAIL(&h->link, &private_voutput->commit_handler_list); + LIST_INITHEAD(&h->call_link); + + pthread_mutex_unlock(&private_client->lock); + + return TDM_ERROR_NONE; +} + +void +tdm_client_voutput_remove_commit_handler(tdm_client_voutput *voutput, + tdm_client_voutput_commit_handler func, + void *user_data) +{ + tdm_private_client_voutput *private_voutput; + tdm_private_client *private_client; + tdm_client_voutput_commit_handler_info *h = NULL; + + TDM_RETURN_IF_FAIL(voutput != NULL); + TDM_RETURN_IF_FAIL(func != NULL); + + private_voutput = (tdm_private_client_voutput *)voutput; + private_client = private_voutput->private_client; + + pthread_mutex_lock(&private_client->lock); + + LIST_FOR_EACH_ENTRY(h, &private_voutput->commit_handler_list, link) { + if (h->func != func || h->user_data != user_data) + continue; + + LIST_DEL(&h->link); + free(h); + + pthread_mutex_unlock(&private_client->lock); + } + + pthread_mutex_unlock(&private_client->lock); +} + +tdm_error +tdm_client_voutput_commit_done(tdm_client_voutput *voutput) +{ + tdm_private_client_voutput *private_voutput; + tdm_private_client *private_client; + tbm_surface_h buffer = NULL; + + TDM_RETURN_VAL_IF_FAIL(voutput != NULL, TDM_ERROR_INVALID_PARAMETER); + + private_voutput = (tdm_private_client_voutput *)voutput; + TDM_RETURN_VAL_IF_FAIL(private_voutput->attach_buffer != NULL, TDM_ERROR_NONE); + + private_client = private_voutput->private_client; + + pthread_mutex_lock(&private_client->lock); + + buffer = (tbm_surface_h)wl_buffer_get_user_data(private_voutput->attach_buffer->wl_buffer); + tbm_surface_internal_unref(buffer); + private_voutput->attach_buffer = NULL; + wl_tdm_voutput_commit_done(private_voutput->wl_voutput); + + pthread_mutex_unlock(&private_client->lock); + + return TDM_ERROR_NONE; +} + +tdm_client_output * +tdm_client_voutput_get_client_output(tdm_client_voutput *voutput, tdm_error *error) +{ + tdm_private_client_voutput *private_voutput; + tdm_private_client_output *private_output = NULL; + tdm_private_client *private_client; + tdm_error ret = TDM_ERROR_NONE; + + if (error) + *error = TDM_ERROR_NONE; + + if (!voutput) { + TDM_ERR("'!voutput' failed"); + if (error) + *error = TDM_ERROR_INVALID_PARAMETER; + return NULL; + } + + private_voutput = (tdm_private_client_voutput *)voutput; + private_client = private_voutput->private_client; + + pthread_mutex_lock(&private_client->lock); + + if (private_voutput->get_output) { + pthread_mutex_unlock(&private_client->lock); + return private_voutput->private_output; + } + + pthread_mutex_unlock(&private_client->lock); + private_output = (tdm_private_client_output *)tdm_client_get_output(private_voutput->private_client, private_voutput->name, &ret); + if (!private_output) { + TDM_ERR("tdm_client_voutput_get_client_output get private_output fail"); + if (error) + *error = ret; + return NULL; + } + pthread_mutex_lock(&private_client->lock); + private_output->voutput = private_voutput; + private_voutput->private_output = private_output; + private_voutput->get_output = 1; + + pthread_mutex_unlock(&private_client->lock); + + return private_output; +} + +void +_tdm_client_voutput_send_available_modes(tdm_private_client_voutput *private_voutput) +{ + tdm_client_output_mode *modes, *mode; + struct wl_array array; + int i, size; + + modes = private_voutput->available_modes.modes; + size = sizeof(tdm_client_output_mode); + + wl_array_init(&array); + for (i = 0; i < private_voutput->available_modes.count; i++) { + mode = wl_array_add(&array, size); + memcpy(mode, &modes[i], size); + } + wl_tdm_voutput_set_available_modes(private_voutput->wl_voutput, &array); + wl_array_release(&array); +} + +tdm_error +tdm_client_voutput_connect(tdm_client_voutput *voutput) +{ + tdm_private_client_output *private_output = NULL;; + tdm_private_client_voutput *private_voutput = NULL; + tdm_private_client *private_client; + + TDM_RETURN_VAL_IF_FAIL(voutput != NULL, TDM_ERROR_INVALID_PARAMETER); + + private_voutput = (tdm_private_client_voutput *)voutput; + private_client = private_voutput->private_client; + private_output = private_voutput->private_output; + + pthread_mutex_lock(&private_client->lock); + + if (private_output->connection != TDM_OUTPUT_CONN_STATUS_DISCONNECTED) { + pthread_mutex_unlock(&private_client->lock); + return TDM_ERROR_NONE; + } + + if (!private_output->watch_output_changes) + private_output->connection = TDM_OUTPUT_CONN_STATUS_CONNECTED; + + _tdm_client_voutput_send_available_modes(private_voutput); + + wl_tdm_voutput_set_physical_size(private_voutput->wl_voutput, private_voutput->mmwidth, private_voutput->mmheight); + + wl_tdm_voutput_connect(private_voutput->wl_voutput); + + pthread_mutex_unlock(&private_client->lock); + + return TDM_ERROR_NONE; +} + +tdm_error +tdm_client_voutput_disconnect(tdm_client_voutput *voutput) +{ + tdm_private_client_output *private_output = NULL; + tdm_private_client_voutput *private_voutput = NULL; + tdm_private_client *private_client; + + TDM_RETURN_VAL_IF_FAIL(voutput != NULL, TDM_ERROR_INVALID_PARAMETER); + + private_voutput = (tdm_private_client_voutput *)voutput; + private_client = private_voutput->private_client; + private_output = private_voutput->private_output; + + pthread_mutex_lock(&private_client->lock); + + if (private_output->connection == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) { + pthread_mutex_unlock(&private_client->lock); + return TDM_ERROR_NONE; + } + + if (!private_output->watch_output_changes) + private_output->connection = TDM_OUTPUT_CONN_STATUS_DISCONNECTED; + + wl_tdm_voutput_disconnect(private_voutput->wl_voutput); + + pthread_mutex_unlock(&private_client->lock); + + return TDM_ERROR_NONE; +} + + +tdm_error +tdm_client_voutput_set_mode(tdm_client_voutput *voutput, int index) +{ + tdm_private_client_output *private_output; + tdm_private_client_voutput *private_voutput = NULL; + tdm_private_client *private_client; + + TDM_RETURN_VAL_IF_FAIL(voutput != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(index >= 0, TDM_ERROR_INVALID_PARAMETER); + + private_voutput = (tdm_private_client_voutput *)voutput; + private_client = private_voutput->private_client; + private_output = private_voutput->private_output; + + pthread_mutex_lock(&private_client->lock); + + if (private_voutput->available_modes.count - 1 < index) { + pthread_mutex_unlock(&private_client->lock); + return TDM_ERROR_INVALID_PARAMETER; + } + + if ((private_output->connection == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) || + (private_voutput->available_modes.count == 0)) { + pthread_mutex_unlock(&private_client->lock); + return TDM_ERROR_BAD_REQUEST; + } + + TDM_DBG("mode_set request : %d", index); + + wl_tdm_voutput_set_mode(private_voutput->wl_voutput, index); + + pthread_mutex_unlock(&private_client->lock); + + return TDM_ERROR_NONE; +} diff --git a/client/tdm_client.h b/client/tdm_client.h index a13bf2a..17d63cf 100644 --- a/client/tdm_client.h +++ b/client/tdm_client.h @@ -429,6 +429,45 @@ tdm_client_vblank_wait_seq(tdm_client_vblank *vblank, unsigned int sequence, tdm unsigned int tdm_client_vblank_is_waiting(tdm_client_vblank *vblank); + +/* Virtual Output */ +tdm_client_voutput * +tdm_client_create_voutput(tdm_client *client, const char *name, tdm_error *error); + +void +tdm_client_voutput_destroy(tdm_client_voutput *voutput); + +tdm_error +tdm_client_voutput_set_available_modes(tdm_client_voutput *voutput, const tdm_client_output_mode *modes, int count); + +tdm_error +tdm_client_voutput_set_physical_size(tdm_client_voutput *voutput, unsigned int mmWidth, unsigned int mmHeight); + +tdm_error +tdm_client_voutput_add_commit_handler(tdm_client_voutput *voutput, tdm_client_voutput_commit_handler func, void *user_data); + +void +tdm_client_voutput_remove_commit_handler(tdm_client_voutput *voutput, tdm_client_voutput_commit_handler func, void *user_data); + +tdm_error +tdm_client_voutput_get_committed_tbm_surface(tdm_client_voutput *voutput, tbm_surface_h surface); + +tdm_error +tdm_client_voutput_commit_done(tdm_client_voutput *voutput); + +tdm_client_output * +tdm_client_voutput_get_client_output(tdm_client_voutput *voutput, tdm_error *error); + +tdm_error +tdm_client_voutput_set_mode(tdm_client_voutput *voutput, int index); + +tdm_error +tdm_client_voutput_connect(tdm_client_voutput *voutput); + +tdm_error +tdm_client_voutput_disconnect(tdm_client_voutput *voutput); +/* End of Virtual Output */ + #ifdef __cplusplus } #endif diff --git a/client/tdm_client_types.h b/client/tdm_client_types.h index bd7e8a5..569cd1d 100644 --- a/client/tdm_client_types.h +++ b/client/tdm_client_types.h @@ -38,6 +38,8 @@ #include "tdm_common.h" +#include + #ifdef __cplusplus extern "C" { #endif @@ -109,6 +111,24 @@ typedef void unsigned int tv_usec, void *user_data); +/* Virtual Output */ +/* this is a copy of server side's tdm_output_mode */ +typedef struct _tdm_client_output_mode { + unsigned int clock; + unsigned int hdisplay, hsync_start, hsync_end, htotal, hskew; + unsigned int vdisplay, vsync_start, vsync_end, vtotal, vscan; + unsigned int vrefresh; + unsigned int flags; + unsigned int type; + char name[TDM_NAME_LEN]; +} tdm_client_output_mode; + +typedef void tdm_client_voutput; + +typedef void (*tdm_client_voutput_commit_handler)(tdm_client_voutput *voutput, + tbm_surface_h buffer, + void *user_data); +/* End of Virtual Output */ #ifdef __cplusplus } #endif diff --git a/configure.ac b/configure.ac index 3d13ec7..8944c9e 100644 --- a/configure.ac +++ b/configure.ac @@ -45,7 +45,7 @@ fi PKG_CHECK_MODULES(WAYLAND_SCANNER, wayland-scanner >= 1.7.0) PKG_CHECK_MODULES(TDM, dlog libtbm libpng pixman-1 wayland-server iniparser) -PKG_CHECK_MODULES(TDM_CLIENT, dlog wayland-client) +PKG_CHECK_MODULES(TDM_CLIENT, dlog libtbm wayland-client) PKG_CHECK_MODULES(TTRACE, [ttrace], diff --git a/haltests/src/tc_tdm_client.cpp b/haltests/src/tc_tdm_client.cpp index eb537ca..0d54855 100644 --- a/haltests/src/tc_tdm_client.cpp +++ b/haltests/src/tc_tdm_client.cpp @@ -50,6 +50,15 @@ enum { TDM_UT_PIPE_MSG_TERMINATE_SERVER, }; +#define TDM_UT_WAIT(fmt, ...) \ + do { \ + char ch; \ + do { \ + printf(fmt" [n]):next ", ##__VA_ARGS__); \ + ch = tc_tdm_getchar(); \ + } while (ch != 'n'); \ + } while (0) + static int _tc_tdm_pipe_read_msg(int fd); static bool _tc_tdm_pipe_write_msg(int fd, int reply_fd, int msg); static pid_t _tc_tdm_client_server_fork(int *pipe_to_parent, int *pipe_to_child); @@ -66,6 +75,7 @@ public: tdm_client *client; tdm_client_output *output; tdm_client_vblank *vblank; + tdm_client_voutput *voutput; double vrefresh_interval, start, end; @@ -199,6 +209,23 @@ bool TDMClient::PrepareVblank(void) return true; } +char +tc_tdm_getchar(void) +{ + int c = getchar(); + int ch = c; + + if (ch == '\n' || ch == '\r') + ch = 'y'; + else if (ch < 'a') + ch += ('a' - 'A'); + + while (c != '\n' && c != EOF) + c = getchar(); + + return ch; +} + static int _tc_tdm_pipe_read_msg(int fd) { @@ -719,7 +746,7 @@ TEST_P(TDMClient, ClientOutputGetRefreshRateNullOther) ASSERT_EQ(tdm_client_output_get_refresh_rate(output, NULL), TDM_ERROR_INVALID_PARAMETER); } -/* tdm_client_output_get_refresh_rate */ +/* tdm_client_output_get_conn_status */ TEST_P(TDMClient, ClientOutputGetConnStatus) { tdm_output_conn_status status = (tdm_output_conn_status)TDM_UT_INVALID_VALUE; @@ -1358,6 +1385,361 @@ TEST_P(TDMClient, ClientVblankIsWaitingNullObject) ASSERT_EQ(waiting, 0); } +TEST_P(TDMClient, ClientCreateVOutput) +{ + tdm_error ret; + const char name[TDM_NAME_LEN] = "Virtual Output"; + + ASSERT_EQ(PrepareClient(), true); + + voutput = tdm_client_create_voutput(client, name, &ret); + ASSERT_EQ(ret, TDM_ERROR_NONE); + ASSERT_NE(voutput, NULL); + + tdm_client_voutput_destroy(voutput); +} + +class TDMVirtualOutput : public ::testing::Test +{ +public: + TDMVirtualOutput() {}; + ~TDMVirtualOutput() {}; + + static void SetUpTestCase(); + static void TearDownTestCase(); + static bool PrepareVOutput(void); + +protected: + static tdm_client *client; + static tdm_client_voutput *voutput; + const int MODE_COUNT = 2; + +private: + static pid_t server_pid; + + /* 0: read, 1: write */ + static int pipe_parent[2]; + static int pipe_child[2]; + + static void ServerFork(void); + static void ServerKill(void); +}; + +pid_t TDMVirtualOutput::server_pid = -1; +int TDMVirtualOutput::pipe_parent[2] = {-1, -1}; +int TDMVirtualOutput::pipe_child[2] = {-1, -1}; +tdm_client* TDMVirtualOutput::client = nullptr; +tdm_client_voutput* TDMVirtualOutput::voutput = nullptr; + +void TDMVirtualOutput::ServerKill(void) +{ + if (pipe_child[0] >= 0) + close(pipe_child[0]); + if (pipe_child[1] >= 0) { + if (server_pid > 0) { + bool ret = _tc_tdm_pipe_write_msg(pipe_child[1], pipe_parent[0], TDM_UT_PIPE_MSG_TERMINATE_SERVER); + if (ret) { + if (waitpid(server_pid, NULL, 0) == server_pid) + TDM_INFO("*** server terminated ***"); + else + TDM_ERR("*** failed to terminate server ***"); + } else { + if (kill(server_pid, 9) < 0) + TDM_ERR("*** failed to kill server ***"); + } + } + close(pipe_child[1]); + } + + if (pipe_parent[0] >= 0) + close(pipe_parent[0]); + if (pipe_parent[1] >= 0) + close(pipe_parent[1]); + + server_pid = -1; + pipe_parent[0] = pipe_parent[1] = -1; + pipe_child[0] = pipe_child[1] = -1; +} + +void TDMVirtualOutput::ServerFork(void) +{ + if (server_pid > 0) + return; + + server_pid = _tc_tdm_client_server_fork(pipe_parent, pipe_child); + ASSERT_GT(server_pid, 0); +} + +void TDMVirtualOutput::SetUpTestCase(void) +{ + setenv("XDG_RUNTIME_DIR", "/run", 1); + setenv("TBM_DISPLAY_SERVER", "1", 1); + + if (server_pid == -1) + ServerFork(); + + ASSERT_EQ(PrepareVOutput(), true); +} + +void TDMVirtualOutput::TearDownTestCase(void) +{ +// TDM_UT_WAIT("check & press"); + + if (voutput) + tdm_client_voutput_destroy(voutput); + + if (client) + tdm_client_destroy(client); + + ServerKill(); + + unsetenv("XDG_RUNTIME_DIR"); + unsetenv("TBM_DISPLAY_SERVER"); +} + +bool TDMVirtualOutput::PrepareVOutput(void) +{ + tdm_error ret; + const char name[TDM_NAME_LEN] = "Virtual Output"; + + client = tdm_client_create(&ret); + TDM_UT_RETURN_FALSE_IF_FAIL(ret == TDM_ERROR_NONE); + TDM_UT_RETURN_FALSE_IF_FAIL(client != NULL); + + voutput = tdm_client_create_voutput(client, name, &ret); + TDM_UT_RETURN_FALSE_IF_FAIL(ret == TDM_ERROR_NONE); + TDM_UT_RETURN_FALSE_IF_FAIL(voutput != NULL); + +// TDM_UT_WAIT("check & press"); + + return true; +} + +static void +_tc_tdm_client_virutual_make_available_mode(tdm_client_output_mode *modes, int count) +{ + int i; + + for (i = 0; i < count; i++) { + modes[i].clock = 25200; + modes[i].hdisplay = 640; + modes[i].hsync_start = 656; + modes[i].hsync_end = 752; + modes[i].htotal = 800; + modes[i].hskew = 0; + modes[i].vdisplay = 480; + modes[i].vsync_start = 490; + modes[i].vsync_end = 492; + modes[i].vtotal = 525; + modes[i].vscan = 0; + modes[i].vrefresh = 30; + modes[i].flags = 0; + modes[i].type = 0; + snprintf(modes[i].name, TDM_NAME_LEN, "%dx%d_%d", modes[i].hdisplay, modes[i].vdisplay, i); + } +} + +TEST_F(TDMVirtualOutput, SetAvailableModes) +{ + tdm_error ret; + tdm_client_output_mode modes[this->MODE_COUNT]; + int count = this->MODE_COUNT; + + _tc_tdm_client_virutual_make_available_mode(modes, count); + + ret = tdm_client_voutput_set_available_modes(this->voutput, modes, count); + ASSERT_EQ(ret, TDM_ERROR_NONE); +} + +TEST_F(TDMVirtualOutput, FailTestSetAvailableModes) +{ + tdm_error ret; + tdm_client_output_mode modes[this->MODE_COUNT]; + int count = this->MODE_COUNT; + + ret = tdm_client_voutput_set_available_modes(NULL, modes, count); + ASSERT_EQ(ret, TDM_ERROR_INVALID_PARAMETER); + + ret = tdm_client_voutput_set_available_modes(this->voutput, NULL, count); + ASSERT_EQ(ret, TDM_ERROR_INVALID_PARAMETER); +} + +TEST_F(TDMVirtualOutput, SetPhysicalSize) +{ + tdm_error ret; + unsigned int mmWidth = 1234, mmHeight = 1234; + + ret = tdm_client_voutput_set_physical_size(this->voutput, mmWidth, mmHeight); + ASSERT_EQ(ret, TDM_ERROR_NONE); +} + +TEST_F(TDMVirtualOutput, FailTestSetPhysicalSize) +{ + tdm_error ret; + unsigned int invalid_mmWidth = 0, invalid_mmHeight = 0; + + ret = tdm_client_voutput_set_physical_size(this->voutput, invalid_mmWidth, invalid_mmHeight); + ASSERT_EQ(ret, TDM_ERROR_INVALID_PARAMETER); +} + +static void +_tc_tdm_client_voutput_commit_handler(tdm_client_voutput *voutput, tbm_surface_h buffer, void *user_data) +{ + int *flag; + flag = (int *)user_data; + *flag = 1; +} + +TEST_F(TDMVirtualOutput, AddCommitHandler) +{ + tdm_error ret; + int flag_callback_called = 0; + + ret = tdm_client_voutput_add_commit_handler(this->voutput, + _tc_tdm_client_voutput_commit_handler, + &flag_callback_called); + ASSERT_EQ(ret, TDM_ERROR_NONE); +// ASSERT_EQ(flag_callback_called, 1); + + tdm_client_voutput_remove_commit_handler(this->voutput, + _tc_tdm_client_voutput_commit_handler, + &flag_callback_called); +} + +TEST_F(TDMVirtualOutput, CommitDone) +{ + tdm_error ret; + + ret = tdm_client_voutput_commit_done(this->voutput); + ASSERT_EQ(ret, TDM_ERROR_NONE); +} + +TEST_F(TDMVirtualOutput, GetClientOutput) +{ + tdm_error ret; + tdm_client_output *output; + + output = tdm_client_voutput_get_client_output(this->voutput, &ret); + ASSERT_EQ(ret, TDM_ERROR_NONE); + ASSERT_NE(output, NULL); +} + +TEST_F(TDMVirtualOutput, Connect) +{ + tdm_error ret; + unsigned int mmWidth = 300, mmHeight = 150; + tdm_client_output_mode modes[this->MODE_COUNT]; + int count = this->MODE_COUNT; + + ret = tdm_client_voutput_set_physical_size(this->voutput, mmWidth, mmHeight); + ASSERT_EQ(ret, TDM_ERROR_NONE); + + _tc_tdm_client_virutual_make_available_mode(modes, count); + ret = tdm_client_voutput_set_available_modes(this->voutput, modes, count); + ASSERT_EQ(ret, TDM_ERROR_NONE); + + ret = tdm_client_voutput_connect(this->voutput); + ASSERT_EQ(ret, TDM_ERROR_NONE); + + tdm_client_handle_events_timeout(this->client, 0); +} + +TEST_F(TDMVirtualOutput, Disconnect) +{ + tdm_error ret; + +// TDM_UT_WAIT("check & press"); + + ret = tdm_client_voutput_disconnect(this->voutput); + ASSERT_EQ(ret, TDM_ERROR_NONE); + + tdm_client_handle_events_timeout(this->client, 0); +} + + +TEST_F(TDMVirtualOutput, SetMode) +{ + tdm_error ret; + unsigned int mmWidth = 300, mmHeight = 150; + tdm_client_output_mode modes[this->MODE_COUNT]; + int count = this->MODE_COUNT; + + ret = tdm_client_voutput_set_physical_size(this->voutput, mmWidth, mmHeight); + ASSERT_EQ(ret, TDM_ERROR_NONE); + + _tc_tdm_client_virutual_make_available_mode(modes, count); + ret = tdm_client_voutput_set_available_modes(this->voutput, modes, count); + ASSERT_EQ(ret, TDM_ERROR_NONE); + + ret = tdm_client_voutput_connect(this->voutput); + ASSERT_EQ(ret, TDM_ERROR_NONE); + + tdm_client_handle_events_timeout(this->client, 100); + + ASSERT_EQ(tdm_client_voutput_set_mode(this->voutput, count - 1), TDM_ERROR_NONE); + + tdm_client_handle_events_timeout(this->client, 100); + + ret = tdm_client_voutput_disconnect(this->voutput); + ASSERT_EQ(ret, TDM_ERROR_NONE); + + tdm_client_handle_events_timeout(this->client, 0); +} + +TEST_F(TDMVirtualOutput, SetModeNullObject) +{ + tdm_error ret; + unsigned int mmWidth = 300, mmHeight = 150; + tdm_client_output_mode modes[this->MODE_COUNT]; + int count = this->MODE_COUNT; + + ret = tdm_client_voutput_set_physical_size(this->voutput, mmWidth, mmHeight); + ASSERT_EQ(ret, TDM_ERROR_NONE); + + _tc_tdm_client_virutual_make_available_mode(modes, count); + ret = tdm_client_voutput_set_available_modes(this->voutput, modes, count); + ASSERT_EQ(ret, TDM_ERROR_NONE); + + ret = tdm_client_voutput_connect(this->voutput); + ASSERT_EQ(ret, TDM_ERROR_NONE); + + tdm_client_handle_events_timeout(this->client, 100); + + ASSERT_EQ(tdm_client_voutput_set_mode(NULL, 0), TDM_ERROR_INVALID_PARAMETER); + + ret = tdm_client_voutput_disconnect(this->voutput); + ASSERT_EQ(ret, TDM_ERROR_NONE); + + tdm_client_handle_events_timeout(this->client, 0); +} + +TEST_F(TDMVirtualOutput, SetModeInvalidIndex) +{ + tdm_error ret; + unsigned int mmWidth = 300, mmHeight = 150; + tdm_client_output_mode modes[this->MODE_COUNT]; + int count = this->MODE_COUNT; + + ret = tdm_client_voutput_set_physical_size(this->voutput, mmWidth, mmHeight); + ASSERT_EQ(ret, TDM_ERROR_NONE); + + _tc_tdm_client_virutual_make_available_mode(modes, count); + ret = tdm_client_voutput_set_available_modes(this->voutput, modes, count); + ASSERT_EQ(ret, TDM_ERROR_NONE); + + ret = tdm_client_voutput_connect(this->voutput); + ASSERT_EQ(ret, TDM_ERROR_NONE); + + tdm_client_handle_events_timeout(this->client, 100); + + ASSERT_EQ(tdm_client_voutput_set_mode(this->voutput, -1), TDM_ERROR_INVALID_PARAMETER); + + ret = tdm_client_voutput_disconnect(this->voutput); + ASSERT_EQ(ret, TDM_ERROR_NONE); + + tdm_client_handle_events_timeout(this->client, 0); +} + #ifdef TDM_UT_TEST_WITH_PARAMS INSTANTIATE_TEST_CASE_P(TDMClientParams, TDMClient, diff --git a/libtdm.pc.in b/libtdm.pc.in index 8c2adf4..7065867 100644 --- a/libtdm.pc.in +++ b/libtdm.pc.in @@ -7,5 +7,5 @@ Name: libtdm Description: Tizen Display Manager Library Version: @TDM_VERSION@ Requires: libtbm -Libs: -L${libdir} -ltdm +Libs: -L${libdir} -ltdm -ltbm Cflags: -I${includedir} diff --git a/packaging/libtdm.spec b/packaging/libtdm.spec index 5bd715e..7479fce 100644 --- a/packaging/libtdm.spec +++ b/packaging/libtdm.spec @@ -13,6 +13,7 @@ BuildRequires: pkgconfig(libtbm) BuildRequires: pkgconfig(libpng) BuildRequires: pkgconfig(ttrace) BuildRequires: pkgconfig(wayland-server) +BuildRequires: pkgconfig(wayland-client) BuildRequires: pkgconfig(iniparser) BuildRequires: pkgconfig(pixman-1) BuildRequires: gtest-devel diff --git a/protocol/tdm.xml b/protocol/tdm.xml index 6434e98..dbba607 100644 --- a/protocol/tdm.xml +++ b/protocol/tdm.xml @@ -23,6 +23,11 @@ + + + + + @@ -66,6 +71,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/tdm_server.c b/src/tdm_server.c index 30bb475..93d6be9 100644 --- a/src/tdm_server.c +++ b/src/tdm_server.c @@ -682,6 +682,91 @@ _tdm_server_cb_create_output(struct wl_client *client, struct wl_resource *resou } } +static void _tdm_voutput_cb_destroy(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +_tdm_voutput_cb_set_available_modes(struct wl_client *client, + struct wl_resource *resource, + struct wl_array *modes) +{ + /* TODO */ +} + +static void +_tdm_voutput_cb_set_physical_size(struct wl_client *client, struct wl_resource *resource, + unsigned int mmwidth, unsigned int mmheight) +{ + /* TODO */ +} + +static void +_tdm_voutput_cb_set_mode(struct wl_client *client, struct wl_resource *resource, unsigned int index) +{ + /* TODO */ +} + +static void +_tdm_voutput_cb_connect(struct wl_client *client, struct wl_resource *resource) +{ + /* TODO */ +} + +static void +_tdm_voutput_cb_disconnect(struct wl_client *client, struct wl_resource *resource) +{ + /* TODO */ +} + +static void +_tdm_voutput_cb_commit_done(struct wl_client *client, struct wl_resource *resource) +{ + /* TODO */ +} + +static const struct wl_tdm_voutput_interface tdm_voutput_implementation = { + _tdm_voutput_cb_destroy, + _tdm_voutput_cb_set_available_modes, + _tdm_voutput_cb_set_physical_size, + _tdm_voutput_cb_set_mode, + _tdm_voutput_cb_connect, + _tdm_voutput_cb_disconnect, + _tdm_voutput_cb_commit_done +}; + +void +tdm_voutput_cb_resource_destroy(struct wl_resource *resource) +{ + /* TODO */ +} + +static void +_tdm_server_cb_create_virtual_output(struct wl_client *client, struct wl_resource *resource, const char *name, uint32_t id) +{ + struct wl_resource *voutput_resource = NULL; + + voutput_resource = + wl_resource_create(client, &wl_tdm_voutput_interface, + wl_resource_get_version(resource), id); + if (!voutput_resource) { + /* LCOV_EXCL_START */ + + wl_resource_post_no_memory(resource); + TDM_ERR("wl_resource_create failed"); + return; + + /* LCOV_EXCL_STOP */ + } + wl_resource_set_implementation(voutput_resource, + &tdm_voutput_implementation, + NULL, + tdm_voutput_cb_resource_destroy); + + wl_tdm_voutput_send_ack_message(voutput_resource, WL_TDM_VOUTPUT_MESSAGE_ADDED); +} + /* LCOV_EXCL_START */ static void _tdm_server_cb_debug(struct wl_client *client, struct wl_resource *resource, const char *options) @@ -727,6 +812,7 @@ _tdm_server_cb_debug(struct wl_client *client, struct wl_resource *resource, con static const struct wl_tdm_interface tdm_implementation = { _tdm_server_cb_debug, _tdm_server_cb_create_output, + _tdm_server_cb_create_virtual_output }; static void -- 2.7.4 From a8855a952419b40a6779b1c2716bee1873d7731a Mon Sep 17 00:00:00 2001 From: Junkyeong Kim Date: Wed, 5 Sep 2018 20:58:54 +0900 Subject: [PATCH 05/16] virtual: define functions and structure for supporting virtual output Change-Id: I71b298a2917645261c838dce51b9ae1854b341c3 Signed-off-by: Junkyeong Kim --- include/tdm.h | 72 ++++++++++++++++++++++++++ include/tdm_backend.h | 135 +++++++++++++++++++++++++++++++++++++++++++++++- include/tdm_types.h | 30 +++++++++++ src/tdm_macro.h | 4 ++ src/tdm_private.h | 38 ++++++++++++++ src/tdm_private_types.h | 97 ++++++++++++++++++++++++++++++++++ 6 files changed, 375 insertions(+), 1 deletion(-) diff --git a/include/tdm.h b/include/tdm.h index 11e6173..169fd7c 100644 --- a/include/tdm.h +++ b/include/tdm.h @@ -207,6 +207,30 @@ tdm_output * tdm_display_get_output(tdm_display *dpy, int index, tdm_error *error); /** + * @brief Add a output create handler + * @param[in] output A output object + * @param[in] func A output create handler + * @param[in] user_data The user data + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ +tdm_error +tdm_display_add_output_create_handler(tdm_display *dpy, + tdm_output_create_handler func, + void *user_data); + +/** + * @brief Remove a output create handler + * @param[in] output A output object + * @param[in] func A output create handler + * @param[in] user_data The user data + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ +void +tdm_display_remove_output_create_handler(tdm_display *dpy, + tdm_output_create_handler func, + void *user_data); + +/** * @brief Find a output object which has the given name. * @param[in] dpy A display object * @param[in] name The name of a output object @@ -239,6 +263,30 @@ tdm_module_get_info(tdm_module *module, const char **name, const char **vendor, int *major, int *minor); /** + * @brief Add a output destroy handler + * @param[in] output A output object + * @param[in] func A output destroy handler + * @param[in] user_data The user data + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ +tdm_error +tdm_output_add_destroy_handler(tdm_output *output, + tdm_output_destroy_handler func, + void *user_data); + +/** + * @brief Remove a output destroy handler + * @param[in] output A output object + * @param[in] func A output destroy handler + * @param[in] user_data The user data + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ +void +tdm_output_remove_destroy_handler(tdm_output *output, + tdm_output_destroy_handler func, + void *user_data); + +/** * @brief Get a backend module object of the given output. * @param[in] output A output object * @param[out] error #TDM_ERROR_NONE if success. Otherwise, error value. @@ -305,6 +353,30 @@ tdm_output_remove_change_handler(tdm_output *output, void *user_data); /** + * @brief Add a output mode change handler + * @param[in] output A output object + * @param[in] func A output mode change handler + * @param[in] user_data The user data + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ +tdm_error +tdm_output_add_mode_change_request_handler(tdm_output *output, + tdm_output_mode_change_request_handler func, + void *user_data); + +/** + * @brief Remove a output mode change handler + * @param[in] output A output object + * @param[in] func A output mode change handler + * @param[in] user_data The user data + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ +tdm_error +tdm_output_remove_mode_change_request_handler(tdm_output *output, + tdm_output_mode_change_request_handler func, + void *user_data); + +/** * @brief Get the connection type of a output object. * @param[in] output A output object * @param[out] type The connection type. diff --git a/include/tdm_backend.h b/include/tdm_backend.h index 033f803..675d9c4 100644 --- a/include/tdm_backend.h +++ b/include/tdm_backend.h @@ -335,7 +335,18 @@ typedef struct _tdm_func_display { */ tdm_pp *(*display_create_pp)(tdm_backend_data *bdata, tdm_error *error); - void (*reserved1)(void); + /** + * @brief Create a virtual output object of a backend module + * @param[in] bdata The backend module data + * @param[in] name The output name + * @param[out] error #TDM_ERROR_NONE if success. Otherwise, error value. + * @return A tdm_voutput object + * @see voutput_destroy() function + * @remark + * A backend module doesn't need to implement this function if doesn't support virtual output. + */ + tdm_voutput *(*voutput_create)(tdm_backend_data *bdata, const char *name, tdm_error *error); + void (*reserved2)(void); void (*reserved3)(void); void (*reserved4)(void); @@ -548,12 +559,102 @@ typedef struct _tdm_func_output { */ tdm_hwc *(*output_get_hwc)(tdm_output *output, tdm_error *error); + void (*reserved3)(void); + void (*reserved4)(void); void (*reserved5)(void); void (*reserved6)(void); void (*reserved7)(void); void (*reserved8)(void); } tdm_func_output; +typedef struct _tdm_func_voutput { + /** + * @brief Destroy a virtual output object of a backend module + * @param[in] voutput The voutput object + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @see voutput_create() function + * @remark + * A backend module doesn't need to implement this function if doesn't support virtual output. + */ + tdm_error (*voutput_destroy)(tdm_voutput *voutput); + + /** + * @brief Set available modes of a virtual output object + * @param[in] voutput A voutput object + * @param[in] modes Modes of voutput + * @param[in] count A count of modes + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @remark + * A backend module doesn't need to implement this function if doesn't support virtual output. + */ + tdm_error (*voutput_set_available_mode)(tdm_voutput *voutput, const tdm_output_mode *modes, int count); + + /** + * @brief Set physical size(mm) of a virtual output object + * @param[in] voutput A voutput object + * @param[in] mmwidth Width of voutput + * @param[in] mmheight Height of voutput + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @remark + * A backend module doesn't need to implement this function if doesn't support virtual output. + */ + tdm_error (*voutput_set_physical_size)(tdm_voutput *voutput, unsigned int mmwidth, unsigned int mmheight); + + /** + * @brief Set connect status of a virtual output object + * @param[in] voutput A voutput object + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @remark + * A backend module doesn't need to implement this function if doesn't support virtual output. + */ + tdm_error (*voutput_connect)(tdm_voutput *voutput); + + /** + * @brief Set disconnect status of a virtual output object + * @param[in] voutput A voutput object + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @remark + * A backend module doesn't need to implement this function if doesn't support virtual output. + */ + tdm_error (*voutput_disconnect)(tdm_voutput *voutput); + + /** + * @brief Get output object from virtual output object + * @param[in] voutput A voutput object + * @param[out] error #TDM_ERROR_NONE if success. Otherwise, error value. + * @return A tdm_output object + * @remark + * A backend module doesn't need to implement this function if doesn't support virtual output. + */ + tdm_output *(*voutput_get_output)(tdm_voutput *voutput, tdm_error *error); + + /** + * @brief Set a user commit function + * @param[in] voutput A voutput object + * @param[in] func A user voutput commit function + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @remark + * A backend module doesn't need to implement this function if doesn't support virtual output. + * If virtual output's output_commit is executed, call this voutput commit func. + */ + tdm_error (*voutput_set_commit_func)(tdm_voutput *voutput, tdm_voutput_commit_handler commit_func); + + /** + * @brief Notify commit done to backend + * @param[in] voutput A voutput object + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @remark + * A backend module doesn't need to implement this function if doesn't support virtual output. + */ + tdm_error (*voutput_commit_done)(tdm_voutput *voutput); + + void (*reserved1)(void); + void (*reserved2)(void); + void (*reserved3)(void); + void (*reserved4)(void); + void (*reserved5)(void); + void (*reserved6)(void); +} tdm_func_voutput; /** * @brief The layer functions for a backend module. */ @@ -1187,6 +1288,18 @@ tdm_backend_register_func_output(tdm_display *dpy, tdm_func_output *func_output); /** + * @brief Register the backend voutput functions to a display + * @param[in] dpy A display object + * @param[in] func_voutput voutput functions + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @see tdm_backend_register_func_display, tdm_backend_register_func_output + * @remarks + * A backend module doesn't need to implement this function if doesn't support virtual output. + */ +tdm_error +tdm_backend_register_func_voutput(tdm_display *dpy, tdm_func_voutput *func_voutput); + +/** * @brief Register the backend layer functions to a display * @param[in] dpy A display object * @param[in] func_layer layer functions @@ -1252,6 +1365,26 @@ tdm_backend_register_func_capture(tdm_display *dpy, tdm_func_capture *func_capture); /** + * @brief Register the backend output to a display + * @param[in] dpy A display object + * @param[in] output A backend output object + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @see tdm_backend_unregister_output + */ +tdm_error +tdm_backend_register_output(tdm_display *dpy, tdm_output *output); + +/** + * @brief Unregister the backend output to a display + * @param[in] dpy A display object + * @param[in] output A backend output object + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @see tdm_backend_register_output + */ +void +tdm_backend_unregister_output(tdm_display *dpy, tdm_output *output); + +/** * @brief Increase the ref_count of a TDM buffer * @details * TDM has its own buffer release mechanism to let an frontend user know when a TDM buffer diff --git a/include/tdm_types.h b/include/tdm_types.h index b87fc18..24423d3 100644 --- a/include/tdm_types.h +++ b/include/tdm_types.h @@ -238,6 +238,11 @@ typedef void tdm_module; typedef void tdm_output; /** + * @brief The tdm voutput object + */ +typedef void tdm_voutput; + +/** * @brief The tdm layer object */ typedef void tdm_layer; @@ -270,6 +275,20 @@ typedef void tdm_pp; typedef void tdm_vblank; /** + * @brief The output create handler + * @details This handler will be called when the output object is + * createed in runtime. + */ +typedef void (*tdm_output_create_handler)(tdm_display *dpy, tdm_output *output, void *user_data); + +/** + * @brief The output destroy handler + * @details This handler will be called when the output object is + * destroied in runtime. + */ +typedef void (*tdm_output_destroy_handler)(tdm_output *output, void *user_data); + +/** * @brief The output change handler * @details This handler will be called when the status of a output object is * changed in runtime. @@ -296,6 +315,12 @@ typedef void (*tdm_output_commit_handler)(tdm_output *output, unsigned int seque void *user_data); /** + * @brief The output mode change request handler + */ +typedef void (*tdm_output_mode_change_request_handler)(tdm_output *output, + unsigned int index, void *user_data); + +/** * @brief The layer commit handler */ typedef void (*tdm_layer_commit_handler)(tdm_layer *layer, unsigned int sequence, @@ -327,6 +352,11 @@ typedef void (*tdm_hwc_commit_handler)(tdm_hwc *hwc, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, void *user_data); +typedef void (*tdm_voutput_commit_handler)(tdm_voutput *voutput, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec, + void *user_data); + +typedef void (*tdm_voutput_commit_func)(tdm_voutput *voutput, tbm_surface_h buffer); #ifdef __cplusplus } #endif diff --git a/src/tdm_macro.h b/src/tdm_macro.h index 5b8f861..bd7e257 100644 --- a/src/tdm_macro.h +++ b/src/tdm_macro.h @@ -86,6 +86,7 @@ extern "C" { /* common backend names *****************************************************/ #define TDM_DEFAULT_MODULE "libtdm-default.so" #define TDM_DUMMY_MODULE "libtdm-dummy.so" +#define TDM_VIRTUAL_MODULE "libtdm-virtual.so" /* dump directory ***********************************************************/ #define TDM_DUMP_DIR "/tmp" @@ -245,6 +246,8 @@ TDM_TYPE_NAME_FN(value_type) static struct tdm_type_name tdm_cb_type_names[] = { { TDM_THREAD_CB_NONE, "none" }, { TDM_THREAD_CB_EXIT, "exit" }, + { TDM_THREAD_CB_DISPLAY_OUTPUT_CREATE, "output-create" }, + { TDM_THREAD_CB_OUTPUT_DESTROY, "output-destroy" }, { TDM_THREAD_CB_OUTPUT_COMMIT, "output-commit" }, { TDM_THREAD_CB_OUTPUT_VBLANK, "output-vblank" }, { TDM_THREAD_CB_OUTPUT_STATUS, "output-status" }, @@ -254,6 +257,7 @@ static struct tdm_type_name tdm_cb_type_names[] = { { TDM_THREAD_CB_VBLANK_SW, "vblank-sw" }, { TDM_THREAD_CB_VBLANK_CREATE, "vblank-create" }, { TDM_THREAD_CB_HWC_COMMIT, "hwc-commit" }, + { TDM_THREAD_CB_VOUTPUT_COMMIT, "voutput-commit" }, }; TDM_TYPE_NAME_FN(cb_type) diff --git a/src/tdm_private.h b/src/tdm_private.h index 5e1ed84..fd1fdae 100644 --- a/src/tdm_private.h +++ b/src/tdm_private.h @@ -88,10 +88,17 @@ tdm_display_get(void); int tdm_module_check_abi(tdm_private_module *private_module, int abimaj, int abimin); +tdm_error +tdm_display_call_thread_cb_output_create(tdm_private_display *private_display, tdm_output *output); +void +tdm_display_thread_cb_output_create(tdm_private_display *private_display, void *object, tdm_thread_cb_base *cb_base, void *user_data); void * tdm_display_find_output_stamp(tdm_private_display *private_display, double stamp); tdm_private_output * tdm_display_find_private_output(tdm_private_display *private_display, tdm_output *output_backend); +unsigned int +tdm_display_find_empty_output_pipe(tdm_private_display *private_display); + void * tdm_display_find_hwc_stamp(tdm_private_display *private_display, double stamp); @@ -101,6 +108,10 @@ tdm_display_find_private_hwc(tdm_private_display *private_display, tdm_hwc *hwc_ tdm_error tdm_output_init(tdm_private_display *private_display); +tdm_error +tdm_output_call_thread_cb_destroy(tdm_private_output *private_output); +void +tdm_output_thread_cb_destroy(tdm_private_display *private_display, void *object, tdm_thread_cb_base *cb_base, void *user_data); void tdm_output_thread_cb_change(tdm_private_display *private_display, void *object, tdm_thread_cb_base *cb_base, void *user_data); void @@ -127,6 +138,10 @@ void tdm_output_remove_vblank_handler_internal(tdm_output *output, tdm_output_vblank_handler func, void *user_data); void tdm_output_remove_commit_handler_internal(tdm_output *output, tdm_output_commit_handler func, void *user_data); + +void +tdm_output_request_mode_set(tdm_output *output, unsigned int index); + void tdm_layer_remove_commit_handler_internal(tdm_layer *layer, tdm_layer_commit_handler func, void *user_data); @@ -242,6 +257,29 @@ tdm_config_deinit(void); void tdm_monitor_server_command(tdm_display *dpy, const char *options, char *reply, int *len); +/* virtual */ +tdm_voutput * +tdm_voutput_create(tdm_display *dpy, const char *name, tdm_error *error); +tdm_error +tdm_voutput_destroy(tdm_voutput *voutput); +tdm_error +tdm_voutput_set_available_mode(tdm_voutput *voutput, const tdm_output_mode *modes, int count); +tdm_error +tdm_voutput_set_physical_size(tdm_voutput *voutput, unsigned int mmwidth, unsigned int mmheight); +tdm_error +tdm_voutput_connect(tdm_voutput *voutput); +tdm_error +tdm_voutput_disconnect(tdm_voutput *voutput); +tdm_error +tdm_voutput_set_commit_func(tdm_voutput *voutput, tdm_voutput_commit_handler func); +tdm_error +tdm_voutput_attach_buffer(tdm_voutput *voutput, tbm_surface_h buffer); +tdm_error +tdm_voutput_commit_buffer(tdm_voutput *voutput); +tdm_error +tdm_voutput_commit_done(tdm_voutput *voutput); +void * +tdm_display_find_private_voutput(tdm_private_display *private_display, double stamp); #ifdef __cplusplus } #endif diff --git a/src/tdm_private_types.h b/src/tdm_private_types.h index 37adf72..993be0e 100644 --- a/src/tdm_private_types.h +++ b/src/tdm_private_types.h @@ -99,6 +99,7 @@ typedef enum { typedef struct _tdm_private_module tdm_private_module; typedef struct _tdm_private_display tdm_private_display; typedef struct _tdm_private_output tdm_private_output; +typedef struct _tdm_private_voutput tdm_private_voutput; typedef struct _tdm_private_layer tdm_private_layer; typedef struct _tdm_private_hwc tdm_private_hwc; typedef struct _tdm_private_hwc_window tdm_private_hwc_window; @@ -107,11 +108,15 @@ typedef struct _tdm_private_capture tdm_private_capture; typedef struct _tdm_private_loop tdm_private_loop; typedef struct _tdm_private_server tdm_private_server; typedef struct _tdm_private_thread tdm_private_thread; +typedef struct _tdm_private_output_create_handler tdm_private_output_create_handler; +typedef struct _tdm_private_output_destroy_handler tdm_private_output_destroy_handler; typedef struct _tdm_private_output_change_handler tdm_private_output_change_handler; typedef struct _tdm_private_output_commit_handler tdm_private_output_commit_handler; typedef struct _tdm_private_output_vblank_handler tdm_private_output_vblank_handler; +typedef struct _tdm_private_output_mode_change_handler tdm_private_output_mode_change_handler; typedef struct _tdm_private_layer_commit_handler tdm_private_layer_commit_handler; typedef struct _tdm_private_hwc_commit_handler tdm_private_hwc_commit_handler; +typedef struct _tdm_private_voutput_commit_handler tdm_private_voutput_commit_handler; typedef struct _tdm_private_layer_buffer tdm_private_layer_buffer; @@ -129,6 +134,7 @@ struct _tdm_private_module { tdm_display_capability capabilities; tdm_func_display func_display; tdm_func_output func_output; + tdm_func_voutput func_voutput; tdm_func_layer func_layer; tdm_func_hwc func_hwc; tdm_func_hwc_window func_hwc_window; @@ -142,6 +148,7 @@ struct _tdm_private_module { /* output, pp list */ struct list_head output_list; + struct list_head voutput_list; struct list_head pp_list; struct list_head capture_list; @@ -163,6 +170,8 @@ struct _tdm_private_display { #endif struct list_head module_list; + tdm_private_module *dummy_module; + tdm_private_module *virtual_module; tdm_private_module *current_module; //setted only when loading tdm_private_module *pp_module; //pp-support backend tdm_private_module *capture_module; //TODO: remove later @@ -170,6 +179,8 @@ struct _tdm_private_display { /* for event handling */ tdm_private_loop *private_loop; + struct list_head output_create_handler_list; + int print_fps; }; @@ -177,6 +188,7 @@ struct _tdm_private_output { struct list_head link; tdm_private_module *private_module; + tdm_private_voutput *private_voutput; int index; double stamp; @@ -210,6 +222,7 @@ struct _tdm_private_output { tdm_event_loop_source *vblank_timeout_timer; unsigned int vblank_timeout_timer_expired; + struct list_head destroy_handler_list; struct list_head change_handler_list; void **layers_ptr; @@ -226,6 +239,34 @@ struct _tdm_private_output { /* hwc */ int need_set_target_info; tdm_private_hwc *private_hwc; + + /* virtual */ + char name[TDM_NAME_LEN]; + struct list_head mode_change_request_handler_list; +}; + +struct _tdm_private_voutput { + struct list_head link; + + tdm_private_module *private_module; + + int regist_commit_cb; + + struct list_head voutput_commit_handler_list; + + int index; + + tdm_private_display *private_display; + tdm_private_output *private_output; + + tdm_voutput *voutput_backend; + + char name[TDM_NAME_LEN]; + tdm_output_mode *modes; + int mode_count; + unsigned int mmwidth; + unsigned int mmheight; + int connect_status; }; struct _tdm_private_layer { @@ -263,6 +304,9 @@ struct _tdm_private_layer { double fps_stamp; unsigned int fps_count; + + /* virtual */ + tbm_surface_h commiting_buffer; }; struct _tdm_private_hwc { @@ -384,6 +428,26 @@ struct _tdm_private_output_vblank_handler { pid_t owner_tid; }; +struct _tdm_private_output_create_handler { + struct list_head link; + + tdm_private_display *private_display; + tdm_output_create_handler func; + void *user_data; + + pid_t owner_tid; +}; + +struct _tdm_private_output_destroy_handler { + struct list_head link; + + tdm_private_output *private_output; + tdm_output_destroy_handler func; + void *user_data; + + pid_t owner_tid; +}; + struct _tdm_private_output_change_handler { struct list_head link; @@ -404,6 +468,24 @@ struct _tdm_private_output_commit_handler { pid_t owner_tid; }; +struct _tdm_private_voutput_commit_handler { + struct list_head link; + + tdm_private_voutput *private_voutput; + tdm_voutput_commit_func func; + void *user_data; + + pid_t owner_tid; +}; + +struct _tdm_private_output_mode_change_handler { + struct list_head link; + + tdm_private_output *private_output; + tdm_output_mode_change_request_handler func; + void *user_data; +}; + struct _tdm_private_hwc_commit_handler { struct list_head link; @@ -458,6 +540,8 @@ typedef struct _tdm_capture_private_buffer { typedef enum { TDM_THREAD_CB_NONE, TDM_THREAD_CB_EXIT, /* special type to exit the tdm-thread */ + TDM_THREAD_CB_DISPLAY_OUTPUT_CREATE, + TDM_THREAD_CB_OUTPUT_DESTROY, TDM_THREAD_CB_OUTPUT_COMMIT, TDM_THREAD_CB_OUTPUT_VBLANK, TDM_THREAD_CB_OUTPUT_STATUS, @@ -467,10 +551,13 @@ typedef enum { TDM_THREAD_CB_VBLANK_SW, TDM_THREAD_CB_VBLANK_CREATE, TDM_THREAD_CB_HWC_COMMIT, + TDM_THREAD_CB_VOUTPUT_COMMIT, TDM_THREAD_CB_MAX, } tdm_thread_cb_type; typedef struct _tdm_thread_cb_base tdm_thread_cb_base; +typedef struct _tdm_thread_cb_display_output_create tdm_thread_cb_display_output_create; +typedef struct _tdm_thread_cb_output_destroy tdm_thread_cb_output_destroy; typedef struct _tdm_thread_cb_output_vblank tdm_thread_cb_output_commit; typedef struct _tdm_thread_cb_output_vblank tdm_thread_cb_output_vblank; typedef struct _tdm_thread_cb_output_dpms tdm_thread_cb_output_dpms; @@ -480,6 +567,7 @@ typedef struct _tdm_thread_cb_capture_done tdm_thread_cb_capture_done; typedef struct _tdm_thread_cb_vblank_sw tdm_thread_cb_vblank_sw; typedef struct _tdm_thread_cb_vblank_create tdm_thread_cb_vblank_create; typedef struct _tdm_thread_cb_output_vblank tdm_thread_cb_hwc_commit; +typedef struct _tdm_thread_cb_output_vblank tdm_thread_cb_voutput_commit; struct _tdm_thread_cb_base { tdm_thread_cb_type type; @@ -489,6 +577,15 @@ struct _tdm_thread_cb_base { unsigned int sync; }; +struct _tdm_thread_cb_display_output_create { + tdm_thread_cb_base base; + tdm_output *output; +}; + +struct _tdm_thread_cb_output_destroy { + tdm_thread_cb_base base; +}; + struct _tdm_thread_cb_output_vblank { tdm_thread_cb_base base; unsigned int sequence; -- 2.7.4 From f9c6b2e12aaad5b265609d4ab3a901b37ceb2f11 Mon Sep 17 00:00:00 2001 From: Junkyeong Kim Date: Thu, 11 Oct 2018 14:47:03 +0900 Subject: [PATCH 06/16] virtual: support virtual output create & destroy Change-Id: I863304cefa6358950ab354b824d7acf2e81dcea6 Signed-off-by: Junkyeong Kim --- src/tdm.c | 177 +++++++++++++++++++++++++--------- src/tdm_display.c | 118 +++++++++++++++++++++++ src/tdm_event_loop.c | 4 + src/tdm_helper.c | 2 + src/tdm_output.c | 267 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/tdm_private.h | 6 +- src/tdm_server.c | 100 ++++++++++++++++++- src/tdm_thread.c | 2 + 8 files changed, 623 insertions(+), 53 deletions(-) diff --git a/src/tdm.c b/src/tdm.c index 6886527..30f3c07 100644 --- a/src/tdm.c +++ b/src/tdm.c @@ -50,6 +50,8 @@ pthread_mutex_t tdm_debug_mutex_check_lock = PTHREAD_MUTEX_INITIALIZER; const char *tdm_debug_mutex_lock_func; int tdm_debug_mutex_lock_line; +static tdm_error _tdm_display_load_module_with_file(tdm_private_display *private_display, const char *file); + /* LCOV_EXCL_START */ static tdm_private_layer * _tdm_display_find_private_layer(tdm_private_output *private_output, @@ -81,6 +83,34 @@ tdm_display_find_private_output(tdm_private_display *private_display, tdm_output return NULL; } +INTERN unsigned int +tdm_display_find_empty_output_pipe(tdm_private_display *private_display) +{ + tdm_private_module *private_module = NULL; + tdm_private_output *private_output = NULL; + unsigned int pipe = 0; + while (1) { + int found = 0; + LIST_FOR_EACH_ENTRY(private_module, &private_display->module_list, link) { + LIST_FOR_EACH_ENTRY(private_output, &private_module->output_list, link) { + if (private_output->pipe == pipe) { + found = 1; + break; + } + } + if (found) + break; + } + + if (!found) + break; + else + pipe++; + } + + return pipe; +} + INTERN void * tdm_display_find_output_stamp(tdm_private_display *private_display, double stamp) { @@ -197,8 +227,8 @@ _tdm_display_destroy_private_layer(tdm_private_layer *private_layer) free(private_layer); } -static void -_tdm_display_destroy_private_output(tdm_private_output *private_output) +INTERN void +tdm_display_destroy_private_output(tdm_private_output *private_output) { tdm_private_display *private_display = private_output->private_display; tdm_private_layer *l = NULL, *ll = NULL; @@ -210,6 +240,11 @@ _tdm_display_destroy_private_output(tdm_private_output *private_output) tdm_private_hwc_commit_handler *hm = NULL, *hmm = NULL; tdm_private_layer_commit_handler *lm = NULL, *lmm = NULL; tdm_private_output_change_handler *h = NULL, *hh = NULL; + tdm_private_output_destroy_handler *dh = NULL, *dhh = NULL; + tdm_error ret; + + ret = tdm_output_call_thread_cb_destroy(private_output); + TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE); free(private_output->layers_ptr); @@ -251,6 +286,12 @@ _tdm_display_destroy_private_output(tdm_private_output *private_output) free(h); } + LIST_FOR_EACH_ENTRY_SAFE(dh, dhh, &private_output->destroy_handler_list, link) { + LIST_DEL(&dh->link); + tdm_thread_cb_remove(dh->private_output, TDM_THREAD_CB_OUTPUT_DESTROY, NULL, tdm_output_thread_cb_destroy, dh); + free(dh); + } + if (private_output->vblank) { /* tdm_vblank APIs is for server. it should be called in unlock status*/ _pthread_mutex_unlock(&private_display->lock); @@ -295,6 +336,7 @@ _tdm_display_destroy_private_display(tdm_private_display *private_display) tdm_private_module *private_module = NULL, *bb = NULL; tdm_private_output *o = NULL, *oo = NULL; tdm_private_pp *p = NULL, *pp = NULL; + tdm_private_output_create_handler *ch = NULL, *chh = NULL; LIST_FOR_EACH_ENTRY_SAFE(private_module, bb, &private_display->module_list, link) { LIST_FOR_EACH_ENTRY_SAFE(p, pp, &private_module->pp_list, link) { @@ -302,7 +344,7 @@ _tdm_display_destroy_private_display(tdm_private_display *private_display) } LIST_FOR_EACH_ENTRY_SAFE(o, oo, &private_module->output_list, link) { - _tdm_display_destroy_private_output(o); + tdm_display_destroy_private_output(o); } _tdm_display_destroy_caps_pp(&private_module->caps_pp); @@ -316,6 +358,13 @@ _tdm_display_destroy_private_display(tdm_private_display *private_display) private_module->outputs = NULL; } } + + LIST_FOR_EACH_ENTRY_SAFE(ch, chh, &private_display->output_create_handler_list, link) { + LIST_DEL(&ch->link); + tdm_thread_cb_remove(ch->private_display, TDM_THREAD_CB_DISPLAY_OUTPUT_CREATE, NULL, + tdm_display_thread_cb_output_create, ch); + free(ch); + } } static tdm_error @@ -392,11 +441,6 @@ _tdm_display_update_caps_output(tdm_private_module *private_module, int pipe, tdm_error ret; double stamp; - if (!func_output->output_get_capability) { - TDM_ERR("backend(%s) no output_get_capability()", private_module->module_data->name); - return TDM_ERROR_BAD_MODULE; - } - stamp = tdm_helper_get_time(); ret = func_output->output_get_capability(output_backend, caps); TDM_DBG("backend(%s) backend output_get_capability() time: %.3f ms", @@ -446,30 +490,35 @@ _tdm_display_update_layer(tdm_private_output *private_output, } INTERN tdm_error -tdm_display_update_output(tdm_private_module *private_module, - tdm_output *output_backend, int pipe, unsigned int need_new_caps) +tdm_display_update_output(tdm_private_module *private_module, tdm_output *output_backend) { tdm_func_output *func_output = &private_module->func_output; + tdm_private_display *private_display = NULL; tdm_private_output *private_output = NULL; tdm_layer **layers = NULL; tdm_private_hwc *private_hwc = NULL; tdm_hwc *hwc; int layer_count = 0, i; tdm_error ret; + unsigned int output_created = 0; + + private_display = private_module->private_display; - private_output = tdm_display_find_private_output(private_module->private_display, output_backend); + private_output = tdm_display_find_private_output(private_display, output_backend); if (!private_output) { + unsigned int pipe = tdm_display_find_empty_output_pipe(private_display); + private_output = calloc(1, sizeof(tdm_private_output)); TDM_RETURN_VAL_IF_FAIL(private_output != NULL, TDM_ERROR_OUT_OF_MEMORY); private_output->stamp = tdm_helper_get_time(); - while (tdm_display_find_output_stamp(private_module->private_display, private_output->stamp)) + while (tdm_display_find_output_stamp(private_display, private_output->stamp)) private_output->stamp++; LIST_ADDTAIL(&private_output->link, &private_module->output_list); private_output->private_module = private_module; - private_output->private_display = private_module->private_display; + private_output->private_display = private_display; private_output->current_dpms_value = TDM_OUTPUT_DPMS_OFF; private_output->output_backend = output_backend; private_output->pipe = pipe; @@ -479,6 +528,7 @@ tdm_display_update_output(tdm_private_module *private_module, LIST_INITHEAD(&private_output->vblank_handler_list); LIST_INITHEAD(&private_output->output_commit_handler_list); LIST_INITHEAD(&private_output->pending_commit_handler_list); + LIST_INITHEAD(&private_output->destroy_handler_list); LIST_INITHEAD(&private_output->change_handler_list); if (func_output->output_set_status_handler) { @@ -488,7 +538,11 @@ tdm_display_update_output(tdm_private_module *private_module, private_output->regist_change_cb = 1; } - ret = _tdm_display_update_caps_output(private_module, pipe, output_backend, &private_output->caps); + output_created = 1; + + /* NOTE that output modes will be allocated newly after here */ + _tdm_display_destroy_caps_output(&private_output->caps); + ret = _tdm_display_update_caps_output(private_module, private_output->pipe, output_backend, &private_output->caps); TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, ret); if (private_output->caps.status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) @@ -519,18 +573,12 @@ tdm_display_update_output(tdm_private_module *private_module, LIST_INITHEAD(&private_output->layer_commit_handler_list); } } else { - /* need_new_caps will be true only in case of "disconnected -> connected" and "connected -> disconnected" - * because we have to get new modes. - */ - if (need_new_caps) { - _tdm_display_destroy_caps_output(&private_output->caps); - - ret = _tdm_display_update_caps_output(private_module, pipe, output_backend, &private_output->caps); - TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, ret); + _tdm_display_destroy_caps_output(&private_output->caps); + ret = _tdm_display_update_caps_output(private_module, private_output->pipe, output_backend, &private_output->caps); + TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, ret); - if (private_output->caps.status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) - private_output->current_mode = NULL; - } + if (private_output->caps.status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) + private_output->current_mode = NULL; } /* do not use the layer object when the tdm_output has the hwc capability */ @@ -552,6 +600,11 @@ tdm_display_update_output(tdm_private_module *private_module, free(layers); } + if (output_created) { + ret = tdm_display_call_thread_cb_output_create(private_display, private_output); + TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE); + } + return TDM_ERROR_NONE; } @@ -589,12 +642,12 @@ _tdm_display_get_ordered_outputs(tdm_private_module *private_module, int *count) outputs = func_display->display_get_outputs(private_module->bdata, &output_count, &ret); if (ret != TDM_ERROR_NONE) - goto failed_get_outputs; + goto no_output; *count = output_count; if (output_count == 0) - goto failed_get_outputs; + goto no_output; else if (output_count == 1) { private_module->outputs = outputs; return outputs; @@ -609,7 +662,7 @@ _tdm_display_get_ordered_outputs(tdm_private_module *private_module, int *count) ret = func_output->output_get_capability(outputs[i], &caps); if (ret != TDM_ERROR_NONE) { TDM_ERR("output_get_capability() failed"); - goto failed_get_outputs; + goto no_output; } if (caps.status == TDM_OUTPUT_CONN_STATUS_CONNECTED) { @@ -673,7 +726,7 @@ _tdm_display_get_ordered_outputs(tdm_private_module *private_module, int *count) return outputs; -failed_get_outputs: +no_output: free(outputs); *count = 0; return NULL; @@ -684,7 +737,7 @@ _tdm_display_setup(tdm_private_display *private_display) { tdm_private_module *private_module = NULL; tdm_error ret = TDM_ERROR_NONE; - int index = 0; + int output_count = 0; if (private_display->pp_module) { ret = _tdm_display_update_caps_pp(private_display->pp_module, @@ -702,14 +755,43 @@ _tdm_display_setup(tdm_private_display *private_display) LIST_FOR_EACH_ENTRY(private_module, &private_display->module_list, link) { tdm_output **outputs; - int output_count = 0, i; + int i, count = 0; - outputs = _tdm_display_get_ordered_outputs(private_module, &output_count); - if (!outputs) - goto failed_update; + outputs = _tdm_display_get_ordered_outputs(private_module, &count); + + if (count > 0) + TDM_GOTO_IF_FAIL(outputs != NULL, failed_update); + + for (i = 0; i < count; i++) { + ret = tdm_display_update_output(private_module, outputs[i]); + if (ret != TDM_ERROR_NONE) + goto failed_update; + output_count++; + } + } + + /* At least, the output count should be greater than 0 to ensure tdm_vblank works. + * So we will create a dummy output when backends don't have any output. + * Without destroying a tdm_output object, this dummy output will be replaced with + * a virtual output which is created in runtime. + */ + if (output_count == 0) { + tdm_output **outputs; + int i, count = 0; + + TDM_INFO("loading a %s backend", TDM_DUMMY_MODULE); + ret = _tdm_display_load_module_with_file(private_display, TDM_DUMMY_MODULE); + TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, failed_update); + TDM_GOTO_IF_FAIL(private_display->dummy_module != NULL, failed_update); + + private_module = private_display->dummy_module; - for (i = 0; i < output_count; i++) { - ret = tdm_display_update_output(private_module, outputs[i], index++, 1); + outputs = _tdm_display_get_ordered_outputs(private_module, &count); + TDM_GOTO_IF_FAIL(count > 0, failed_update); + TDM_GOTO_IF_FAIL(outputs != NULL, failed_update); + + for (i = 0; i < count; i++) { + ret = tdm_display_update_output(private_module, outputs[i]); if (ret != TDM_ERROR_NONE) goto failed_update; } @@ -925,6 +1007,9 @@ _tdm_display_load_module_with_file(tdm_private_display *private_display, private_display->current_module = NULL; + if (!strncmp(file, TDM_DUMMY_MODULE, TDM_NAME_LEN)) + private_display->dummy_module = private_module; + private_module->bdata = bdata; if (ret != TDM_ERROR_NONE) { @@ -983,16 +1068,6 @@ _tdm_display_load_modules(tdm_private_display *private_display) arg = strtok_r(NULL, TDM_CONFIG_DELIM, &end); } - /* load bufmgr priv from dummy lib */ - if (LIST_IS_EMPTY(&private_display->module_list)) { - TDM_WRN("No backend. loading a %s backend", TDM_DUMMY_MODULE); - ret = _tdm_display_load_module_with_file(private_display, TDM_DUMMY_MODULE); - if (ret == TDM_ERROR_NONE) - TDM_INFO("%s backend loading success", TDM_DUMMY_MODULE); - else - TDM_INFO("%s backend loading failed", TDM_DUMMY_MODULE); - } - return ret; } @@ -1014,6 +1089,12 @@ _tdm_display_unload_modules(tdm_private_display *private_display) } /* LCOV_EXCL_STOP */ +INTERN void * +tdm_display_find_stamp(tdm_private_display *private_display, double stamp) +{ + return (void*)g_private_display; +} + EXTERN tdm_display * tdm_display_init(tdm_error *error) { @@ -1046,6 +1127,8 @@ tdm_display_init(tdm_error *error) private_display->stamp = tdm_helper_get_time(); + LIST_INITHEAD(&private_display->output_create_handler_list); + str = tdm_config_get_string(TDM_CONFIG_KEY_DEBUG_MODULE, NULL); if (str) tdm_display_enable_debug_module(str); @@ -1158,6 +1241,8 @@ tdm_display_init(tdm_error *error) if (error) *error = TDM_ERROR_NONE; + tdm_thread_cb_set_find_func(TDM_THREAD_CB_DISPLAY_OUTPUT_CREATE, tdm_display_find_stamp); + _pthread_mutex_unlock(&private_display->lock); pthread_mutex_unlock(&gLock); diff --git a/src/tdm_display.c b/src/tdm_display.c index 297dd70..4ee693e 100644 --- a/src/tdm_display.c +++ b/src/tdm_display.c @@ -349,6 +349,124 @@ tdm_display_enable_fps(tdm_private_display *private_display, int enable) } /* LCOV_EXCL_STOP */ +INTERN tdm_error +tdm_display_call_thread_cb_output_create(tdm_private_display *private_display, tdm_output *output) +{ + tdm_thread_cb_display_output_create output_create; + tdm_error ret; + + if (LIST_IS_EMPTY(&private_display->output_create_handler_list)) return TDM_ERROR_NONE; + + memset(&output_create, 0, sizeof output_create); + output_create.base.type = TDM_THREAD_CB_DISPLAY_OUTPUT_CREATE; + output_create.base.length = sizeof output_create; + output_create.base.object_stamp = tdm_helper_get_time(); + output_create.base.data = NULL; + output_create.base.sync = 1; + output_create.output = output; + + ret = tdm_thread_cb_call(private_display, &output_create.base, 1); + TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE); + + return TDM_ERROR_NONE; +} + +INTERN void +tdm_display_thread_cb_output_create(tdm_private_display *private_display, void *object, tdm_thread_cb_base *cb_base, void *user_data) +{ + tdm_thread_cb_display_output_create *output_create = (tdm_thread_cb_display_output_create*)cb_base; + tdm_private_output_create_handler *create_handler = user_data; + TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED()); + + assert(create_handler->owner_tid == syscall(SYS_gettid)); + + _pthread_mutex_unlock(&private_display->lock); + create_handler->func(private_display, output_create->output, create_handler->user_data); + _pthread_mutex_lock(&private_display->lock); +} + +EXTERN tdm_error +tdm_display_add_output_create_handler(tdm_display *dpy, + tdm_output_create_handler func, + void *user_data) +{ + tdm_private_output_create_handler *create_handler = NULL; + + DISPLAY_FUNC_ENTRY(); + + TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER); + + _pthread_mutex_lock(&private_display->lock); + + LIST_FOR_EACH_ENTRY(create_handler, &private_display->output_create_handler_list, link) { + if (create_handler->func == func && create_handler->user_data == user_data) { + TDM_ERR("can't add twice"); + _pthread_mutex_unlock(&private_display->lock); + return TDM_ERROR_BAD_REQUEST; + } + } + + create_handler = calloc(1, sizeof(tdm_private_output_create_handler)); + if (!create_handler) { + TDM_ERR("failed: alloc memory"); + _pthread_mutex_unlock(&private_display->lock); + return TDM_ERROR_OUT_OF_MEMORY; + } + + ret = tdm_thread_cb_add(private_display, TDM_THREAD_CB_DISPLAY_OUTPUT_CREATE, NULL, + tdm_display_thread_cb_output_create, create_handler); + if (ret != TDM_ERROR_NONE) { + TDM_ERR("tdm_thread_cb_add failed"); + free(create_handler); + _pthread_mutex_unlock(&private_display->lock); + return TDM_ERROR_OPERATION_FAILED; + } + + create_handler->private_display = private_display; + create_handler->func = func; + create_handler->user_data = user_data; + create_handler->owner_tid = syscall(SYS_gettid); + + LIST_ADDTAIL(&create_handler->link, &private_display->output_create_handler_list); + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + +EXTERN void +tdm_display_remove_output_create_handler(tdm_display *dpy, + tdm_output_create_handler func, + void *user_data) +{ + tdm_private_display *private_display; + tdm_private_output_create_handler *create_handler = NULL, *hh = NULL; + + TDM_RETURN_IF_FAIL(dpy != NULL); + TDM_RETURN_IF_FAIL(func != NULL); + + private_display = (tdm_private_display*)dpy; + + _pthread_mutex_lock(&private_display->lock); + + LIST_FOR_EACH_ENTRY_SAFE(create_handler, hh, &private_display->output_create_handler_list, link) { + if (create_handler->func != func || create_handler->user_data != user_data) + continue; + + tdm_thread_cb_remove(private_display, TDM_THREAD_CB_DISPLAY_OUTPUT_CREATE, NULL, + tdm_display_thread_cb_output_create, create_handler); + + LIST_DEL(&create_handler->link); + free(create_handler); + + _pthread_mutex_unlock(&private_display->lock); + + return; + } + + _pthread_mutex_unlock(&private_display->lock); +} + EXTERN tdm_error tdm_display_get_capabilities(tdm_display *dpy, tdm_display_capability *capabilities) diff --git a/src/tdm_event_loop.c b/src/tdm_event_loop.c index b7de4ab..5117c27 100644 --- a/src/tdm_event_loop.c +++ b/src/tdm_event_loop.c @@ -63,6 +63,7 @@ static tdm_error _tdm_event_loop_main_fd_handler(int fd, tdm_event_loop_mask mask, void *user_data) { tdm_private_module *private_module = (tdm_private_module*)user_data; + tdm_private_display *private_display; tdm_func_display *func_display; tdm_error ret; @@ -76,7 +77,10 @@ _tdm_event_loop_main_fd_handler(int fd, tdm_event_loop_mask mask, void *user_dat if (!func_display->display_handle_events) return TDM_ERROR_NONE; + private_display = private_module->private_display; + private_display->current_module = private_module; ret = func_display->display_handle_events(private_module->bdata); + private_display->current_module = NULL; return ret; } diff --git a/src/tdm_helper.c b/src/tdm_helper.c index 3607f13..afedccd 100644 --- a/src/tdm_helper.c +++ b/src/tdm_helper.c @@ -846,6 +846,8 @@ _tdm_helper_get_backend_information(tdm_private_module *private_module, char *re } } } + if (LIST_IS_EMPTY(&private_module->output_list)) + TDM_SNPRINTF(reply, len, "(no output)\n"); TDM_SNPRINTF(reply, len, "\n"); /* layer information */ diff --git a/src/tdm_output.c b/src/tdm_output.c index ee760c1..4a96e16 100644 --- a/src/tdm_output.c +++ b/src/tdm_output.c @@ -130,15 +130,130 @@ _tdm_output_vblank_timeout_update(tdm_private_output *private_output, int ms_del INTERN tdm_error tdm_output_init(tdm_private_display *private_display) { + tdm_thread_cb_set_find_func(TDM_THREAD_CB_OUTPUT_DESTROY, tdm_display_find_output_stamp); tdm_thread_cb_set_find_func(TDM_THREAD_CB_OUTPUT_COMMIT, tdm_display_find_output_stamp); tdm_thread_cb_set_find_func(TDM_THREAD_CB_OUTPUT_VBLANK, tdm_display_find_output_stamp); tdm_thread_cb_set_find_func(TDM_THREAD_CB_OUTPUT_STATUS, tdm_display_find_output_stamp); tdm_thread_cb_set_find_func(TDM_THREAD_CB_OUTPUT_DPMS, tdm_display_find_output_stamp); + return TDM_ERROR_NONE; +} + +INTERN tdm_error +tdm_output_call_thread_cb_destroy(tdm_private_output *private_output) +{ + tdm_thread_cb_output_destroy output_destroy; + tdm_error ret; + + memset(&output_destroy, 0, sizeof output_destroy); + output_destroy.base.type = TDM_THREAD_CB_OUTPUT_DESTROY; + output_destroy.base.length = sizeof output_destroy; + output_destroy.base.object_stamp = private_output->stamp; + output_destroy.base.data = NULL; + output_destroy.base.sync = 1; + + ret = tdm_thread_cb_call(private_output, &output_destroy.base, 1); + TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE); return TDM_ERROR_NONE; } +INTERN void +tdm_output_thread_cb_destroy(tdm_private_display *private_display, void *object, tdm_thread_cb_base *cb_base, void *user_data) +{ + tdm_private_output *private_output = object; + tdm_private_output_destroy_handler *destroy_handler = user_data; + + TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED()); + + assert(destroy_handler->owner_tid == syscall(SYS_gettid)); + + _pthread_mutex_unlock(&private_display->lock); + destroy_handler->func(private_output, destroy_handler->user_data); + _pthread_mutex_lock(&private_display->lock); +} + +EXTERN tdm_error +tdm_output_add_destroy_handler(tdm_output *output, + tdm_output_destroy_handler func, + void *user_data) +{ + tdm_private_output_destroy_handler *destroy_handler = NULL; + OUTPUT_FUNC_ENTRY(); + + TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER); + + _pthread_mutex_lock(&private_display->lock); + + LIST_FOR_EACH_ENTRY(destroy_handler, &private_output->destroy_handler_list, link) { + if (destroy_handler->func == func && destroy_handler->user_data == user_data) { + TDM_ERR("can't add twice"); + _pthread_mutex_unlock(&private_display->lock); + return TDM_ERROR_BAD_REQUEST; + } + } + + destroy_handler = calloc(1, sizeof(tdm_private_output_destroy_handler)); + if (!destroy_handler) { + TDM_ERR("failed: alloc memory"); + _pthread_mutex_unlock(&private_display->lock); + return TDM_ERROR_OUT_OF_MEMORY; + } + + ret = tdm_thread_cb_add(private_output, TDM_THREAD_CB_OUTPUT_DESTROY, NULL, tdm_output_thread_cb_destroy, destroy_handler); + if (ret != TDM_ERROR_NONE) { + TDM_ERR("tdm_thread_cb_add failed"); + free(destroy_handler); + _pthread_mutex_unlock(&private_display->lock); + return TDM_ERROR_OPERATION_FAILED; + } + + destroy_handler->private_output = private_output; + destroy_handler->func = func; + destroy_handler->user_data = user_data; + destroy_handler->owner_tid = syscall(SYS_gettid); + + LIST_ADDTAIL(&destroy_handler->link, &private_output->destroy_handler_list); + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + +EXTERN void +tdm_output_remove_destroy_handler(tdm_output *output, + tdm_output_destroy_handler func, + void *user_data) +{ + tdm_private_display *private_display; + tdm_private_output *private_output; + tdm_private_output_destroy_handler *destroy_handler = NULL, *hh = NULL; + + TDM_RETURN_IF_FAIL(output != NULL); + TDM_RETURN_IF_FAIL(func != NULL); + + private_output = (tdm_private_output*)output; + private_display = private_output->private_display; + + _pthread_mutex_lock(&private_display->lock); + + LIST_FOR_EACH_ENTRY_SAFE(destroy_handler, hh, &private_output->destroy_handler_list, link) { + if (destroy_handler->func != func || destroy_handler->user_data != user_data) + continue; + + tdm_thread_cb_remove(private_output, TDM_THREAD_CB_OUTPUT_DESTROY, NULL, tdm_output_thread_cb_destroy, destroy_handler); + + LIST_DEL(&destroy_handler->link); + free(destroy_handler); + + _pthread_mutex_unlock(&private_display->lock); + + return; + } + + _pthread_mutex_unlock(&private_display->lock); +} + EXTERN tdm_module * tdm_output_get_backend_module(tdm_output *output, tdm_error *error) { @@ -297,7 +412,7 @@ tdm_output_cb_status(tdm_output *output_backend, tdm_output_conn_status status, if ((private_output->caps.status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED && status != TDM_OUTPUT_CONN_STATUS_DISCONNECTED) || (private_output->caps.status != TDM_OUTPUT_CONN_STATUS_DISCONNECTED && status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED)) { - ret = tdm_display_update_output(private_output->private_module, output_backend, private_output->pipe, 1); + ret = tdm_display_update_output(private_output->private_module, output_backend); TDM_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); } else { private_output->caps.status = status; @@ -456,7 +571,7 @@ tdm_output_get_layer_count(tdm_output *output, int *count) *count = 0; LIST_FOR_EACH_ENTRY(private_layer, &private_output->layer_list, link) - (*count)++; + (*count)++; if (*count == 0) { _pthread_mutex_unlock(&private_display->lock); return TDM_ERROR_NONE; @@ -1594,4 +1709,150 @@ tdm_output_choose_commit_per_vblank_mode(tdm_private_output *private_output, int return TDM_ERROR_NONE; } -/* LCOV_EXCL_STOP */ + +INTERN tdm_voutput * +tdm_voutput_create(tdm_display *dpy, const char *name, tdm_error *error) +{ + tdm_private_module *private_module = NULL; + tdm_private_module *current_module = NULL; + tdm_private_voutput *private_voutput = NULL; + tdm_private_output *private_output = NULL; + tdm_func_display *func_display = NULL; + tdm_func_voutput *func_voutput = NULL; + tdm_voutput *voutput_backend = NULL; + tdm_output *output_backend = NULL; + int output_find = 0; + tdm_private_display *private_display; + tdm_error ret = TDM_ERROR_NONE; + + TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(dpy != NULL, TDM_ERROR_INVALID_PARAMETER, NULL); + private_display = (tdm_private_display*)dpy; + private_module = private_display->virtual_module; + TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(private_module != NULL, TDM_ERROR_BAD_MODULE, NULL); + + _pthread_mutex_lock(&private_display->lock); + + if (error) + *error = TDM_ERROR_NONE; + + private_voutput = calloc(1, sizeof(tdm_private_voutput)); + if (!private_voutput) { + *error = TDM_ERROR_OUT_OF_MEMORY; + _pthread_mutex_unlock(&private_display->lock); + return NULL; + } + + func_display = &private_module->func_display; + func_voutput = &private_module->func_voutput; + current_module = private_display->current_module; + private_display->current_module = private_module; + + voutput_backend = func_display->voutput_create(private_module->bdata, name, &ret); + + if (voutput_backend == NULL || ret != TDM_ERROR_NONE) { + TDM_ERR("voutput_create fail"); + free(private_voutput); + *error = ret; + private_display->current_module = current_module; + _pthread_mutex_unlock(&private_display->lock); + return NULL; + } + private_voutput->voutput_backend = voutput_backend; + private_voutput->private_display = private_display; + private_voutput->private_module = private_module; + LIST_INITHEAD(&private_voutput->voutput_commit_handler_list); + + output_backend = func_voutput->voutput_get_output(voutput_backend, &ret); + if (output_backend == NULL || ret != TDM_ERROR_NONE) { + TDM_ERR("voutput_get_output fail"); + free(private_voutput); + *error = ret; + if (func_voutput->voutput_destroy) + func_voutput->voutput_destroy(voutput_backend); + else + TDM_ERR("no destroy function"); + private_display->current_module = current_module; + _pthread_mutex_unlock(&private_display->lock); + return NULL; + } + + ret = tdm_display_update_output(private_display->current_module, output_backend); + if (ret != TDM_ERROR_NONE) { + TDM_ERR("tdm_display_update_output fail"); + free(private_voutput); + if (func_voutput->voutput_destroy) + func_voutput->voutput_destroy(voutput_backend); + else + TDM_ERR("no destroy function"); + *error = ret; + private_display->current_module = current_module; + _pthread_mutex_unlock(&private_display->lock); + return NULL; + } + + LIST_FOR_EACH_ENTRY(private_output, &private_module->output_list, link) { + if (private_output->output_backend == output_backend) { + output_find = 1; + break; + } + } + + if (output_find != 1) { + private_output = NULL; + free(private_voutput); + if (func_voutput->voutput_destroy) + func_voutput->voutput_destroy(voutput_backend); + else + TDM_ERR("no destroy function"); + } else { + strncpy(private_voutput->name, name, TDM_NAME_LEN); + strncpy(private_output->name, name, TDM_NAME_LEN); + + private_voutput->private_output = private_output; + private_output->private_voutput = private_voutput; + + /* do not use vblank */ + tdm_output_choose_commit_per_vblank_mode(private_output, 0); + + LIST_ADDTAIL(&private_voutput->link, &private_module->voutput_list); + } + + private_display->current_module = current_module; + + _pthread_mutex_unlock(&private_display->lock); + + return private_voutput; +} + +INTERN tdm_error +tdm_voutput_destroy(tdm_voutput *voutput) +{ + tdm_private_module *private_module = NULL; + tdm_voutput *voutput_backend = NULL; + tdm_func_voutput *func_voutput = NULL; + + tdm_private_display *private_display; + tdm_private_voutput *private_voutput; + tdm_private_output *private_output; + tdm_error ret = TDM_ERROR_NONE; + TDM_RETURN_VAL_IF_FAIL(voutput != NULL, TDM_ERROR_INVALID_PARAMETER); + private_voutput = (tdm_private_voutput*)voutput; + private_display = private_voutput->private_display; + private_module = private_voutput->private_module; + TDM_RETURN_VAL_IF_FAIL(private_module == private_display->virtual_module, TDM_ERROR_BAD_MODULE); + + _pthread_mutex_lock(&private_display->lock); + + func_voutput = &private_module->func_voutput; + voutput_backend = private_voutput->voutput_backend; + private_output = private_voutput->private_output; + tdm_display_destroy_private_output(private_output); + LIST_DEL(&private_voutput->link); + free(private_voutput); + if (func_voutput->voutput_destroy) + ret = func_voutput->voutput_destroy(voutput_backend); + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} diff --git a/src/tdm_private.h b/src/tdm_private.h index fd1fdae..9e10713 100644 --- a/src/tdm_private.h +++ b/src/tdm_private.h @@ -240,8 +240,10 @@ extern int tdm_dump_enable; extern char *tdm_debug_dump_dir; tdm_error -tdm_display_update_output(tdm_private_module *private_module, - tdm_output *output_backend, int pipe, unsigned int need_new_caps); +tdm_display_update_output(tdm_private_module *private_module, tdm_output *output_backend); +void +tdm_display_destroy_private_output(tdm_private_output *private_output); + tdm_error tdm_display_enable_debug_module(const char*modules); tdm_error diff --git a/src/tdm_server.c b/src/tdm_server.c index 93d6be9..12896d5 100644 --- a/src/tdm_server.c +++ b/src/tdm_server.c @@ -37,6 +37,8 @@ #include "config.h" #endif +#define WL_HIDE_DEPRECATED + #include #include "tdm_private.h" @@ -53,6 +55,7 @@ struct _tdm_private_server { tdm_private_loop *private_loop; struct list_head output_list; + struct list_head voutput_list; struct list_head wait_list; }; @@ -65,6 +68,29 @@ typedef struct _tdm_server_output_info { unsigned int watch_output_changes; } tdm_server_output_info; +typedef struct _tdm_server_voutput_buffer { + struct list_head link; + struct wl_resource *wl_buffer; + tbm_surface_h buffer; +} tdm_server_voutput_buffer; + +typedef struct _tdm_server_voutput_info { + struct list_head link; + tdm_private_server *private_server; + struct wl_resource *resource; + tdm_voutput *voutput; + tdm_output *output; + + tdm_output_conn_status status; + struct { + int count; + tdm_output_mode *modes; + } available_modes; + + unsigned int mmwidth; + unsigned int mmheight; +} tdm_server_voutput_info; + typedef struct _tdm_server_vblank_info { struct list_head link; tdm_server_output_info *output_info; @@ -739,13 +765,58 @@ static const struct wl_tdm_voutput_interface tdm_voutput_implementation = { void tdm_voutput_cb_resource_destroy(struct wl_resource *resource) { - /* TODO */ + tdm_server_voutput_info *voutput_info = wl_resource_get_user_data(resource); + tdm_voutput *voutput; + tdm_error ret = TDM_ERROR_NONE; + + TDM_RETURN_IF_FAIL(voutput_info != NULL); + + voutput = voutput_info->voutput; + + if (voutput) + ret = tdm_voutput_destroy(voutput); + if (ret != TDM_ERROR_NONE) + TDM_ERR("_tdm_voutput_cb_destroy fail"); + + LIST_DEL(&voutput_info->link); + + /* Do free your own resource */ + free(voutput_info); } static void _tdm_server_cb_create_virtual_output(struct wl_client *client, struct wl_resource *resource, const char *name, uint32_t id) { struct wl_resource *voutput_resource = NULL; + tdm_private_server *private_server = wl_resource_get_user_data(resource); + tdm_server_voutput_info *voutput_info; + tdm_voutput *voutput; + tdm_output *output; + tdm_error ret; + + output = tdm_display_find_output(private_server->private_loop->dpy, name, NULL); + if (output) { + TDM_ERR("There is '%s' output, cannot create.", name); + wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, + "There is '%s' output", name); + return; + } + + voutput = tdm_voutput_create(private_server->private_loop->dpy, name, &ret); + if (!voutput) { + TDM_ERR("voutput creation fail(%s)(%d).", name, ret); + wl_resource_post_error(resource, WL_DISPLAY_ERROR_NO_MEMORY, + "%s output creation fail", name); + return; + } + + output = tdm_display_find_output(private_server->private_loop->dpy, name, NULL); + if (!output) { + TDM_ERR("There is no '%s' output.", name); + wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT, + "There is '%s' output", name); + return; + } voutput_resource = wl_resource_create(client, &wl_tdm_voutput_interface, @@ -759,9 +830,28 @@ _tdm_server_cb_create_virtual_output(struct wl_client *client, struct wl_resourc /* LCOV_EXCL_STOP */ } + + voutput_info = calloc(1, sizeof * voutput_info); + if (!voutput_info) { + /* LCOV_EXCL_START */ + + wl_resource_post_no_memory(resource); + wl_resource_destroy(voutput_resource); + TDM_ERR("alloc failed"); + return; + + /* LCOV_EXCL_STOP */ + } + + LIST_ADDTAIL(&voutput_info->link, &private_server->voutput_list); + voutput_info->private_server = private_server; + voutput_info->resource = voutput_resource; + voutput_info->voutput = voutput; + voutput_info->output = output; + wl_resource_set_implementation(voutput_resource, &tdm_voutput_implementation, - NULL, + voutput_info, tdm_voutput_cb_resource_destroy); wl_tdm_voutput_send_ack_message(voutput_resource, WL_TDM_VOUTPUT_MESSAGE_ADDED); @@ -897,6 +987,7 @@ tdm_server_init(tdm_private_loop *private_loop) } LIST_INITHEAD(&private_server->output_list); + LIST_INITHEAD(&private_server->voutput_list); LIST_INITHEAD(&private_server->wait_list); if (!wl_global_create(private_loop->wl_display, &wl_tdm_interface, 1, @@ -923,6 +1014,7 @@ INTERN void tdm_server_deinit(tdm_private_loop *private_loop) { tdm_server_output_info *o = NULL, *oo = NULL; + tdm_server_voutput_info *vo = NULL, *voo = NULL; tdm_server_wait_info *w = NULL, *ww = NULL; tdm_server_client_info *c = NULL, *cc = NULL; tdm_private_server *private_server; @@ -940,6 +1032,10 @@ tdm_server_deinit(tdm_private_loop *private_loop) wl_resource_destroy(o->resource); } + LIST_FOR_EACH_ENTRY_SAFE(vo, voo, &private_server->voutput_list, link) { + wl_resource_destroy(vo->resource); + } + LIST_FOR_EACH_ENTRY_SAFE(c, cc, &client_list, link) { wl_resource_destroy(c->resource); } diff --git a/src/tdm_thread.c b/src/tdm_thread.c index 2cc2f13..d367d4e 100644 --- a/src/tdm_thread.c +++ b/src/tdm_thread.c @@ -451,6 +451,8 @@ tdm_thread_handle_cb(tdm_private_loop *private_loop) if (tdm_debug_module & TDM_DEBUG_THREAD) TDM_INFO("type(%s), length(%d)", tdm_cb_type_str(base->type), base->length); switch (base->type) { + case TDM_THREAD_CB_DISPLAY_OUTPUT_CREATE: + case TDM_THREAD_CB_OUTPUT_DESTROY: case TDM_THREAD_CB_OUTPUT_COMMIT: case TDM_THREAD_CB_OUTPUT_VBLANK: case TDM_THREAD_CB_OUTPUT_STATUS: -- 2.7.4 From b718e753150a166cdc4589188700bcc3878ae3ab Mon Sep 17 00:00:00 2001 From: Junkyeong Kim Date: Thu, 11 Oct 2018 16:36:08 +0900 Subject: [PATCH 07/16] virtual: implementations for virtual output - voutput connect & disconnect - set voutput available modes - set voutput mode - set voutput physical size Change-Id: I7aae9f5b425dfbdcf0b50843ee5b31e93e7ca353 Signed-off-by: Junkyeong Kim --- client/tdm_client.c | 2 +- src/tdm.c | 22 ++++++ src/tdm_helper.c | 2 + src/tdm_output.c | 215 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tdm_server.c | 97 ++++++++++++++++++++++-- 5 files changed, 331 insertions(+), 7 deletions(-) diff --git a/client/tdm_client.c b/client/tdm_client.c index fbb875b..972ae00 100644 --- a/client/tdm_client.c +++ b/client/tdm_client.c @@ -359,7 +359,7 @@ _tdm_client_output_cb_connection(void *data, struct wl_tdm_output *wl_tdm_output private_client = private_output->private_client; - if (private_output->connection == value) + if (private_output->connection == value && value != TDM_OUTPUT_CONN_STATUS_MODE_SETTED) return; private_output->connection = value; diff --git a/src/tdm.c b/src/tdm.c index 30f3c07..886e847 100644 --- a/src/tdm.c +++ b/src/tdm.c @@ -127,6 +127,26 @@ tdm_display_find_output_stamp(tdm_private_display *private_display, double stamp return NULL; } +INTERN void * +tdm_display_find_private_voutput(tdm_private_display *private_display, double stamp) +{ + tdm_private_module *private_module = NULL; + tdm_private_voutput *private_voutput = NULL; + tdm_private_output *private_output = NULL; + + private_module = private_display->virtual_module; + if (!private_module) return NULL; + + LIST_FOR_EACH_ENTRY(private_voutput, &private_module->voutput_list, link) { + if (!private_voutput->private_output) continue; + private_output = private_voutput->private_output; + if (private_output->stamp == stamp) + return private_voutput; + } + + return NULL; +} + tdm_private_hwc * tdm_display_find_private_hwc(tdm_private_display *private_display, tdm_hwc *hwc_backend) { @@ -530,6 +550,7 @@ tdm_display_update_output(tdm_private_module *private_module, tdm_output *output LIST_INITHEAD(&private_output->pending_commit_handler_list); LIST_INITHEAD(&private_output->destroy_handler_list); LIST_INITHEAD(&private_output->change_handler_list); + LIST_INITHEAD(&private_output->mode_change_request_handler_list); if (func_output->output_set_status_handler) { func_output->output_set_status_handler(private_output->output_backend, @@ -1024,6 +1045,7 @@ _tdm_display_load_module_with_file(tdm_private_display *private_display, } LIST_INITHEAD(&private_module->output_list); + LIST_INITHEAD(&private_module->voutput_list); LIST_INITHEAD(&private_module->pp_list); LIST_INITHEAD(&private_module->capture_list); diff --git a/src/tdm_helper.c b/src/tdm_helper.c index afedccd..e9db919 100644 --- a/src/tdm_helper.c +++ b/src/tdm_helper.c @@ -947,6 +947,8 @@ _tdm_helper_get_backend_information(tdm_private_module *private_module, char *re } } } + if (LIST_IS_EMPTY(&private_module->output_list)) + TDM_SNPRINTF(reply, len, "(no layer)\n"); TDM_SNPRINTF(reply, len, "\n"); if (private_module->capabilities & TDM_DISPLAY_CAPABILITY_PP) { diff --git a/src/tdm_output.c b/src/tdm_output.c index 4a96e16..9454647 100644 --- a/src/tdm_output.c +++ b/src/tdm_output.c @@ -500,6 +500,85 @@ tdm_output_add_change_handler(tdm_output *output, return ret; } +EXTERN tdm_error +tdm_output_add_mode_change_request_handler(tdm_output *output, + tdm_output_mode_change_request_handler func, + void *user_data) +{ + tdm_private_display *private_display; + tdm_private_output *private_output; + tdm_private_output_mode_change_handler *mode_change_handler = NULL; + + TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER); + + private_output = (tdm_private_output*)output; + private_display = private_output->private_display; + + _pthread_mutex_lock(&private_display->lock); + + LIST_FOR_EACH_ENTRY(mode_change_handler, &private_output->mode_change_request_handler_list, link) { + if (mode_change_handler->func == func && mode_change_handler->user_data == user_data) { + TDM_ERR("can't add twice"); + _pthread_mutex_unlock(&private_display->lock); + return TDM_ERROR_BAD_REQUEST; + } + } + + mode_change_handler = calloc(1, sizeof(tdm_private_output_change_handler)); + if (!mode_change_handler) { + /* LCOV_EXCL_START */ + TDM_ERR("failed: alloc memory"); + _pthread_mutex_unlock(&private_display->lock); + return TDM_ERROR_OUT_OF_MEMORY; + /* LCOV_EXCL_STOP */ + } + + mode_change_handler->private_output = private_output; + mode_change_handler->func = func; + mode_change_handler->user_data = user_data; + + LIST_ADDTAIL(&mode_change_handler->link, &private_output->mode_change_request_handler_list); + + _pthread_mutex_unlock(&private_display->lock); + + return TDM_ERROR_NONE; +} + +EXTERN tdm_error +tdm_output_remove_mode_change_request_handler(tdm_output *output, + tdm_output_mode_change_request_handler func, + void *user_data) +{ + tdm_private_display *private_display; + tdm_private_output *private_output; + tdm_private_output_mode_change_handler *mode_change_handler = NULL, *hh = NULL; + + TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER); + + private_output = (tdm_private_output*)output; + private_display = private_output->private_display; + + _pthread_mutex_lock(&private_display->lock); + + LIST_FOR_EACH_ENTRY_SAFE(mode_change_handler, hh, &private_output->mode_change_request_handler_list, link) { + if (mode_change_handler->func != func || mode_change_handler->user_data != user_data) + continue; + + LIST_DEL(&mode_change_handler->link); + free(mode_change_handler); + + _pthread_mutex_unlock(&private_display->lock); + + return TDM_ERROR_NONE; + } + + _pthread_mutex_unlock(&private_display->lock); + + return TDM_ERROR_INVALID_PARAMETER; +} + EXTERN void tdm_output_remove_change_handler(tdm_output *output, tdm_output_change_handler func, @@ -1206,6 +1285,22 @@ tdm_output_remove_commit_handler(tdm_output *output, tdm_output_commit_handler f return ret; } +INTERN void +tdm_output_request_mode_set(tdm_output *output, unsigned int index) +{ + tdm_private_output *private_output = (tdm_private_output*)output; + tdm_private_output_mode_change_handler *mode_change_handler = NULL; + + TDM_RETURN_IF_FAIL(private_output != NULL); + + if (LIST_IS_EMPTY(&private_output->mode_change_request_handler_list)) + return; + + LIST_FOR_EACH_ENTRY(mode_change_handler, &private_output->mode_change_request_handler_list, link) { + mode_change_handler->func(mode_change_handler->private_output, index, mode_change_handler->user_data); + } +} + INTERN tdm_error tdm_output_commit_internal(tdm_output *output, int sync, tdm_output_commit_handler func, void *user_data) { @@ -1856,3 +1951,123 @@ tdm_voutput_destroy(tdm_voutput *voutput) return ret; } + +INTERN tdm_error +tdm_voutput_set_available_mode(tdm_voutput *voutput, const tdm_output_mode *modes, int count) +{ + tdm_private_display *private_display; + tdm_private_module *private_module = NULL; + tdm_private_voutput *private_voutput; + tdm_func_voutput *func_voutput = NULL; + tdm_error ret = TDM_ERROR_NONE; + + TDM_RETURN_VAL_IF_FAIL(voutput != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(modes != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(count != 0, TDM_ERROR_INVALID_PARAMETER); + + private_voutput = (tdm_private_voutput*)voutput; + private_display = private_voutput->private_display; + private_module = private_voutput->private_module; + TDM_RETURN_VAL_IF_FAIL(private_module == private_display->virtual_module, TDM_ERROR_BAD_MODULE); + + _pthread_mutex_lock(&private_display->lock); + + func_voutput = &private_module->func_voutput; + if (func_voutput->voutput_set_available_mode) + ret = func_voutput->voutput_set_available_mode(private_voutput->voutput_backend, modes, count); + else + ret = TDM_ERROR_NOT_IMPLEMENTED; + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + +INTERN tdm_error +tdm_voutput_set_physical_size(tdm_voutput *voutput, unsigned int mmwidth, unsigned int mmheight) +{ + tdm_private_display *private_display; + tdm_private_module *private_module = NULL; + tdm_private_voutput *private_voutput; + tdm_func_voutput *func_voutput = NULL; + tdm_error ret = TDM_ERROR_NONE; + + TDM_RETURN_VAL_IF_FAIL(voutput != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(mmwidth != 0, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(mmheight != 0, TDM_ERROR_INVALID_PARAMETER); + + private_voutput = (tdm_private_voutput*)voutput; + private_display = private_voutput->private_display; + private_module = private_voutput->private_module; + TDM_RETURN_VAL_IF_FAIL(private_module == private_display->virtual_module, TDM_ERROR_BAD_MODULE); + + _pthread_mutex_lock(&private_display->lock); + + func_voutput = &private_module->func_voutput; + if (func_voutput->voutput_set_physical_size) + ret = func_voutput->voutput_set_physical_size(private_voutput->voutput_backend, mmwidth, mmheight); + else + ret = TDM_ERROR_NOT_IMPLEMENTED; + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + +INTERN tdm_error +tdm_voutput_connect(tdm_voutput *voutput) +{ + tdm_private_display *private_display; + tdm_private_module *private_module = NULL; + tdm_private_voutput *private_voutput; + tdm_func_voutput *func_voutput = NULL; + tdm_error ret = TDM_ERROR_NONE; + + TDM_RETURN_VAL_IF_FAIL(voutput != NULL, TDM_ERROR_INVALID_PARAMETER); + + private_voutput = (tdm_private_voutput*)voutput; + private_display = private_voutput->private_display; + private_module = private_voutput->private_module; + TDM_RETURN_VAL_IF_FAIL(private_module == private_display->virtual_module, TDM_ERROR_BAD_MODULE); + + _pthread_mutex_lock(&private_display->lock); + + func_voutput = &private_module->func_voutput; + if (func_voutput->voutput_connect) + ret = func_voutput->voutput_connect(private_voutput->voutput_backend); + else + ret = TDM_ERROR_NOT_IMPLEMENTED; + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} + +INTERN tdm_error +tdm_voutput_disconnect(tdm_voutput *voutput) +{ + tdm_private_display *private_display; + tdm_private_module *private_module = NULL; + tdm_private_voutput *private_voutput; + tdm_func_voutput *func_voutput = NULL; + tdm_error ret = TDM_ERROR_NONE; + + TDM_RETURN_VAL_IF_FAIL(voutput != NULL, TDM_ERROR_INVALID_PARAMETER); + + private_voutput = (tdm_private_voutput*)voutput; + private_display = private_voutput->private_display; + private_module = private_voutput->private_module; + TDM_RETURN_VAL_IF_FAIL(private_module == private_display->virtual_module, TDM_ERROR_BAD_MODULE); + + _pthread_mutex_lock(&private_display->lock); + + func_voutput = &private_module->func_voutput; + if (func_voutput->voutput_disconnect) + ret = func_voutput->voutput_disconnect(private_voutput->voutput_backend); + else + ret = TDM_ERROR_NOT_IMPLEMENTED; + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} diff --git a/src/tdm_server.c b/src/tdm_server.c index 12896d5..d035a1d 100644 --- a/src/tdm_server.c +++ b/src/tdm_server.c @@ -196,6 +196,8 @@ _tdm_server_cb_output_change(tdm_output *output, tdm_output_change_type type, tdm_server_output_info *output_info = user_data; struct wl_client *client; pid_t pid = 0; + tdm_output_conn_status status; + tdm_error ret = TDM_ERROR_NONE; TDM_RETURN_IF_FAIL(output_info != NULL); @@ -209,13 +211,29 @@ _tdm_server_cb_output_change(tdm_output *output, tdm_output_change_type type, return; } - TDM_DBG("send the output changes: %d", (unsigned int)pid); + TDM_DBG("send the output changes: %d, type:%d, value:%d", (unsigned int)pid, type, value.u32); switch (type) { case TDM_OUTPUT_CHANGE_DPMS: wl_tdm_output_send_dpms(output_info->resource, value.u32, TDM_ERROR_NONE); break; case TDM_OUTPUT_CHANGE_CONNECTION: + status = value.u32; + if (status == TDM_OUTPUT_CONN_STATUS_MODE_SETTED) { + const tdm_output_mode *mode = NULL; + unsigned int hdisplay, vdisplay, vrefresh; + + ret = tdm_output_get_mode(output_info->output, &mode); + if (ret == TDM_ERROR_NONE) { + hdisplay = (mode) ? mode->hdisplay : 0; + vdisplay = (mode) ? mode->vdisplay : 0; + vrefresh = (mode) ? mode->vrefresh : 0; + + wl_tdm_output_send_mode(output_info->resource, hdisplay, vdisplay, vrefresh, ret); + } else { + wl_tdm_output_send_mode(output_info->resource, 0, 0, 0, ret); + } + } wl_tdm_output_send_connection(output_info->resource, value.u32, TDM_ERROR_NONE); break; default: @@ -718,32 +736,99 @@ _tdm_voutput_cb_set_available_modes(struct wl_client *client, struct wl_resource *resource, struct wl_array *modes) { - /* TODO */ + tdm_server_voutput_info *voutput_info; + tdm_output_mode *mode; + int size, count = 0, i = 0; + + voutput_info = wl_resource_get_user_data(resource); + + voutput_info->available_modes.count = 0; + if (voutput_info->available_modes.modes) + free(voutput_info->available_modes.modes); + + wl_array_for_each(mode, modes) + count++; + size = sizeof(tdm_output_mode); + + voutput_info->available_modes.modes = malloc(count * size); + voutput_info->available_modes.count = count; + + wl_array_for_each(mode, modes) + memcpy(&voutput_info->available_modes.modes[i++], mode, size); } static void _tdm_voutput_cb_set_physical_size(struct wl_client *client, struct wl_resource *resource, unsigned int mmwidth, unsigned int mmheight) { - /* TODO */ + tdm_server_voutput_info *voutput_info; + + voutput_info = wl_resource_get_user_data(resource); + + voutput_info->mmwidth = mmwidth; + voutput_info->mmheight = mmheight; } static void _tdm_voutput_cb_set_mode(struct wl_client *client, struct wl_resource *resource, unsigned int index) { - /* TODO */ + tdm_server_voutput_info *voutput_info = NULL; + tdm_output *output = NULL; + tdm_output_conn_status status = TDM_OUTPUT_CONN_STATUS_DISCONNECTED; + const tdm_output_mode *modes, *mode; + + int count = 0; + tdm_error ret; + + voutput_info = wl_resource_get_user_data(resource); + TDM_RETURN_IF_FAIL(voutput_info != NULL); + output = voutput_info->output; + + ret = tdm_output_get_conn_status(output, &status); + TDM_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + TDM_RETURN_IF_FAIL(status != TDM_OUTPUT_CONN_STATUS_DISCONNECTED); + + ret = tdm_output_get_available_modes(output, &modes, &count); + TDM_RETURN_IF_FAIL(index < count); + + mode = &modes[index]; + TDM_DBG("mode set request to index:%d (%dx%d, %d)", index, mode->hdisplay, mode->vdisplay, mode->vrefresh); + + ret = tdm_output_set_mode(output, mode); + TDM_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + + tdm_output_request_mode_set(voutput_info->output, index); } static void _tdm_voutput_cb_connect(struct wl_client *client, struct wl_resource *resource) { - /* TODO */ + tdm_server_voutput_info *voutput_info; + + voutput_info = wl_resource_get_user_data(resource); + voutput_info->status = TDM_OUTPUT_CONN_STATUS_CONNECTED; + + tdm_voutput_set_physical_size(voutput_info->voutput, voutput_info->mmwidth, voutput_info->mmheight); + tdm_voutput_set_available_mode(voutput_info->voutput, voutput_info->available_modes.modes, voutput_info->available_modes.count); + tdm_voutput_connect(voutput_info->voutput); } static void _tdm_voutput_cb_disconnect(struct wl_client *client, struct wl_resource *resource) { - /* TODO */ + tdm_server_voutput_info *voutput_info; + + voutput_info = wl_resource_get_user_data(resource); + voutput_info->status = TDM_OUTPUT_CONN_STATUS_DISCONNECTED; + + /* Do free resources when it's being disconnected */ + free(voutput_info->available_modes.modes); + voutput_info->available_modes.modes = NULL; + voutput_info->available_modes.count = 0; + voutput_info->mmwidth = 0; + voutput_info->mmheight = 0; + + tdm_voutput_disconnect(voutput_info->voutput); } static void -- 2.7.4 From 242fc68711eb15837735c52a8b81ec1b41236ea6 Mon Sep 17 00:00:00 2001 From: Junkyeong Kim Date: Tue, 16 Oct 2018 21:16:31 +0900 Subject: [PATCH 08/16] virtual: share buffer with client - attach buffer - commit buffer - commit done sequence : output commit -> backend call voutput commit func(not output commit handler this time) -> execute voutput attach buffer and commit buffer from voutput commit func(send buffer to client) -> commit done by client -> backend receive commit done so execute output commit handler Change-Id: Ie0dde85f016ead14153c12e71d0d0ae6c2a65459 Signed-off-by: Junkyeong Kim --- src/tdm_layer.c | 7 +- src/tdm_output.c | 212 ++++++++++++++++++++++++++++++++++++++ src/tdm_server.c | 306 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- src/tdm_thread.c | 1 + 4 files changed, 523 insertions(+), 3 deletions(-) diff --git a/src/tdm_layer.c b/src/tdm_layer.c index d8503a5..0885abb 100644 --- a/src/tdm_layer.c +++ b/src/tdm_layer.c @@ -804,7 +804,7 @@ _tdm_layer_cb_wait_vblank(tdm_vblank *vblank, tdm_error error, unsigned int sequ } static unsigned int -_tdm_lauer_get_output_used_layer_count(tdm_private_output *private_output) +_tdm_layer_get_output_used_layer_count(tdm_private_output *private_output) { tdm_private_layer *private_layer = NULL; unsigned int count = 0; @@ -840,7 +840,7 @@ _tdm_layer_commit_possible(tdm_private_layer *private_layer) return 0; } - if (private_output->commit_per_vblank == 1 && _tdm_lauer_get_output_used_layer_count(private_output) > 1) { + if (private_output->commit_per_vblank == 1 && _tdm_layer_get_output_used_layer_count(private_output) > 1) { if (tdm_debug_module & TDM_DEBUG_COMMIT) TDM_INFO("layer(%p,%d) commit: not possible(more than 2 layers)", private_layer, private_layer->index); @@ -944,6 +944,9 @@ _tdm_layer_commit(tdm_layer *layer, tdm_layer_commit_handler func, void *user_da layer_commit_handler->committed_buffer = private_layer->waiting_buffer; private_layer->waiting_buffer = NULL; + if (!private_layer->committing && layer_commit_handler->committed_buffer) + private_layer->commiting_buffer = layer_commit_handler->committed_buffer->buffer; + if (private_layer->committing) TDM_WRN("layer(%p,%d) too many commit", private_layer, private_layer->index); else diff --git a/src/tdm_output.c b/src/tdm_output.c index 9454647..3d6686b 100644 --- a/src/tdm_output.c +++ b/src/tdm_output.c @@ -135,6 +135,7 @@ tdm_output_init(tdm_private_display *private_display) tdm_thread_cb_set_find_func(TDM_THREAD_CB_OUTPUT_VBLANK, tdm_display_find_output_stamp); tdm_thread_cb_set_find_func(TDM_THREAD_CB_OUTPUT_STATUS, tdm_display_find_output_stamp); tdm_thread_cb_set_find_func(TDM_THREAD_CB_OUTPUT_DPMS, tdm_display_find_output_stamp); + tdm_thread_cb_set_find_func(TDM_THREAD_CB_VOUTPUT_COMMIT, tdm_display_find_private_voutput); return TDM_ERROR_NONE; } @@ -1075,6 +1076,87 @@ _tdm_output_cb_commit(tdm_output *output_backend, unsigned int sequence, TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE); } +static void +_tdm_voutput_thread_cb_commit(tdm_private_display *private_display, void *object, tdm_thread_cb_base *cb_base, void *user_data) +{ + tdm_thread_cb_voutput_commit *voutput_commit = (tdm_thread_cb_voutput_commit *)cb_base; + tdm_private_voutput_commit_handler *voutput_commit_handler = voutput_commit->base.data; + tdm_private_voutput *private_voutput = object; + tdm_private_output *private_output = NULL; + + TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED()); + + if (!voutput_commit_handler) + return; + + assert(voutput_commit_handler->owner_tid == syscall(SYS_gettid)); + + tdm_thread_cb_remove(private_voutput, TDM_THREAD_CB_VOUTPUT_COMMIT, voutput_commit_handler, _tdm_voutput_thread_cb_commit, NULL); + + LIST_DEL(&voutput_commit_handler->link); + + private_output = private_voutput->private_output; + if (tdm_debug_module & TDM_DEBUG_COMMIT) { + TDM_INFO("----------------------------------------- voutput(%d) committed", private_output->pipe); + TDM_INFO("handler(%p)", voutput_commit_handler); + } + + if (voutput_commit_handler->func) { + _pthread_mutex_unlock(&private_display->lock); + voutput_commit_handler->func(private_voutput, voutput_commit_handler->user_data); + _pthread_mutex_lock(&private_display->lock); + } + + free(voutput_commit_handler); + + if (tdm_debug_module & TDM_DEBUG_COMMIT) + TDM_INFO("-----------------------------------------..."); +} + +static void +_tdm_voutput_cb_commit(tdm_voutput *voutput_backend, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec, void *user_data) +{ + tdm_private_voutput_commit_handler *voutput_commit_handler = NULL; + tdm_private_module *private_module; + tdm_private_voutput *private_voutput = NULL, *v; + tdm_private_output *private_output; + tdm_thread_cb_voutput_commit voutput_commit; + tdm_error ret; + + private_module = tdm_display_get()->virtual_module; + + LIST_FOR_EACH_ENTRY(v, &private_module->voutput_list, link) { + if (v->voutput_backend == voutput_backend) { + private_voutput = v; + break; + } + } + if (!private_voutput) { + TDM_ERR("cannot find voutput"); + return; + } + + LIST_FOR_EACH_ENTRY(voutput_commit_handler, &private_voutput->voutput_commit_handler_list, link) { + if (voutput_commit_handler) break; + } + + private_output = private_voutput->private_output; + + memset(&voutput_commit, 0, sizeof voutput_commit); + voutput_commit.base.type = TDM_THREAD_CB_VOUTPUT_COMMIT; + voutput_commit.base.length = sizeof voutput_commit; + voutput_commit.base.object_stamp = private_output->stamp; + voutput_commit.base.data = voutput_commit_handler; + voutput_commit.base.sync = 0; + voutput_commit.sequence = sequence; + voutput_commit.tv_sec = tv_sec; + voutput_commit.tv_usec = tv_usec; + + ret = tdm_thread_cb_call(private_voutput, &voutput_commit.base, 1); + TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE); +} + /* add_front: To distinguish between the user vblank handlers and the layer * commit vblank handlers. The layer commit handlers will be called * before calling the user vblank handlers. @@ -1301,13 +1383,33 @@ tdm_output_request_mode_set(tdm_output *output, unsigned int index) } } +static void +_voutput_commit_func(tdm_voutput *voutput, tbm_surface_h buffer) +{ + tdm_private_voutput *private_voutput = (tdm_private_voutput *)voutput; + tdm_error ret; + + TDM_RETURN_IF_FAIL(voutput != NULL); + TDM_RETURN_IF_FAIL(buffer != NULL); + + ret = tdm_voutput_attach_buffer(private_voutput, buffer); + TDM_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + ret = tdm_voutput_commit_buffer(private_voutput); + TDM_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + + return; +} + INTERN tdm_error tdm_output_commit_internal(tdm_output *output, int sync, tdm_output_commit_handler func, void *user_data) { + tdm_private_display *private_display; tdm_private_output *private_output; + tdm_private_voutput *private_voutput; tdm_private_module *private_module; tdm_func_output *func_output; tdm_private_output_commit_handler *output_commit_handler = NULL; + tdm_private_voutput_commit_handler *voutput_commit_handler = NULL; tdm_private_layer *private_layer = NULL; tdm_output_dpms dpms_value = TDM_OUTPUT_DPMS_ON; tdm_error ret = TDM_ERROR_NONE; @@ -1316,6 +1418,7 @@ tdm_output_commit_internal(tdm_output *output, int sync, tdm_output_commit_handl private_output = (tdm_private_output*)output; private_module = private_output->private_module; + private_display = private_module->private_display; func_output = &private_module->func_output; if (!func_output->output_commit) { @@ -1343,10 +1446,44 @@ tdm_output_commit_internal(tdm_output *output, int sync, tdm_output_commit_handl /* LCOV_EXCL_STOP */ } + if (private_module == private_display->virtual_module) { + if (!private_output->private_voutput) { + TDM_ERR("virtual module but don't have voutput"); + free(output_commit_handler); + return TDM_ERROR_BAD_MODULE; + } + private_voutput = private_output->private_voutput; + + if (!private_voutput->regist_commit_cb) { + private_voutput->regist_commit_cb = 1; + ret = tdm_voutput_set_commit_func(private_voutput, _tdm_voutput_cb_commit); + TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, commit_failed); + } + + voutput_commit_handler = calloc(1, sizeof(tdm_private_voutput_commit_handler)); + if (!voutput_commit_handler) { + /* LCOV_EXCL_START */ + TDM_ERR("failed: alloc memory"); + free(output_commit_handler); + return TDM_ERROR_OUT_OF_MEMORY; + /* LCOV_EXCL_STOP */ + } + + ret = tdm_thread_cb_add(private_output->private_voutput, TDM_THREAD_CB_VOUTPUT_COMMIT, voutput_commit_handler, _tdm_voutput_thread_cb_commit, NULL); + if (ret != TDM_ERROR_NONE) { + TDM_ERR("tdm_thread_cb_add failed"); + free(voutput_commit_handler); + free(output_commit_handler); + return ret; + } + } + ret = tdm_thread_cb_add(private_output, TDM_THREAD_CB_OUTPUT_COMMIT, output_commit_handler, _tdm_output_thread_cb_commit, NULL); if (ret != TDM_ERROR_NONE) { TDM_ERR("tdm_thread_cb_add failed"); free(output_commit_handler); + if (voutput_commit_handler) + free(voutput_commit_handler); return ret; } @@ -1356,6 +1493,19 @@ tdm_output_commit_internal(tdm_output *output, int sync, tdm_output_commit_handl output_commit_handler->user_data = user_data; output_commit_handler->owner_tid = syscall(SYS_gettid); + if (voutput_commit_handler) { + /* voutput use only 1 layer */ + LIST_FOR_EACH_ENTRY(private_layer, &private_output->layer_list, link) { + if (private_layer) break; + } + + LIST_ADDTAIL(&voutput_commit_handler->link, &private_voutput->voutput_commit_handler_list); + voutput_commit_handler->private_voutput = private_voutput; + voutput_commit_handler->func = _voutput_commit_func; + voutput_commit_handler->user_data = private_layer->commiting_buffer; + voutput_commit_handler->owner_tid = syscall(SYS_gettid); + } + ret = func_output->output_commit(private_output->output_backend, sync, output_commit_handler); TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, commit_failed); @@ -1383,6 +1533,7 @@ tdm_output_commit_internal(tdm_output *output, int sync, tdm_output_commit_handl private_layer->committed_buffer = private_layer->waiting_buffer; private_layer->waiting_buffer = NULL; + private_layer->commiting_buffer = NULL; if (tdm_debug_module & TDM_DEBUG_BUFFER) TDM_INFO("layer(%p) waiting_buffer(%p) committed_buffer(%p)", private_layer, private_layer->waiting_buffer, @@ -1404,6 +1555,11 @@ commit_failed: LIST_DEL(&output_commit_handler->link); free(output_commit_handler); } + if (voutput_commit_handler) { + tdm_thread_cb_remove(private_output->private_voutput, TDM_THREAD_CB_VOUTPUT_COMMIT, voutput_commit_handler, _tdm_voutput_thread_cb_commit, NULL); + LIST_DEL(&voutput_commit_handler->link); + free(voutput_commit_handler); + } return ret; /* LCOV_EXCL_STOP */ } @@ -2071,3 +2227,59 @@ tdm_voutput_disconnect(tdm_voutput *voutput) return ret; } + +/* LCOV_EXCL_START */ +INTERN tdm_error +tdm_voutput_set_commit_func(tdm_voutput *voutput, tdm_voutput_commit_handler func) +{ + tdm_private_display *private_display; + tdm_private_module *private_module = NULL; + tdm_private_voutput *private_voutput; + tdm_func_voutput *func_voutput = NULL; + tdm_error ret = TDM_ERROR_NONE; + + TDM_RETURN_VAL_IF_FAIL(voutput != NULL, TDM_ERROR_INVALID_PARAMETER); + + private_voutput = (tdm_private_voutput*)voutput; + private_display = private_voutput->private_display; + private_module = private_voutput->private_module; + TDM_RETURN_VAL_IF_FAIL(private_module == private_display->virtual_module, TDM_ERROR_BAD_MODULE); + + func_voutput = &private_module->func_voutput; + if (func_voutput->voutput_set_commit_func) + ret = func_voutput->voutput_set_commit_func(private_voutput->voutput_backend, func); + else + ret = TDM_ERROR_NOT_IMPLEMENTED; + + return ret; +} + +INTERN tdm_error +tdm_voutput_commit_done(tdm_voutput *voutput) +{ + tdm_private_display *private_display; + tdm_private_module *private_module = NULL; + tdm_private_voutput *private_voutput; + tdm_func_voutput *func_voutput = NULL; + tdm_error ret = TDM_ERROR_NONE; + + TDM_RETURN_VAL_IF_FAIL(voutput != NULL, TDM_ERROR_INVALID_PARAMETER); + + private_voutput = (tdm_private_voutput*)voutput; + private_display = private_voutput->private_display; + private_module = private_voutput->private_module; + TDM_RETURN_VAL_IF_FAIL(private_module == private_display->virtual_module, TDM_ERROR_BAD_MODULE); + + _pthread_mutex_lock(&private_display->lock); + + func_voutput = &private_module->func_voutput; + if (func_voutput->voutput_commit_done) + ret = func_voutput->voutput_commit_done(private_voutput->voutput_backend); + else + ret = TDM_ERROR_NOT_IMPLEMENTED; + + _pthread_mutex_unlock(&private_display->lock); + + return ret; +} +/* LCOV_EXCL_STOP */ diff --git a/src/tdm_server.c b/src/tdm_server.c index d035a1d..d1619d9 100644 --- a/src/tdm_server.c +++ b/src/tdm_server.c @@ -89,6 +89,10 @@ typedef struct _tdm_server_voutput_info { unsigned int mmwidth; unsigned int mmheight; + + struct list_head buffer_list; + tdm_server_voutput_buffer *attach_buffer; + int committing; } tdm_server_voutput_info; typedef struct _tdm_server_vblank_info { @@ -828,13 +832,34 @@ _tdm_voutput_cb_disconnect(struct wl_client *client, struct wl_resource *resourc voutput_info->mmwidth = 0; voutput_info->mmheight = 0; + if (voutput_info->attach_buffer) { + tbm_surface_h buffer = voutput_info->attach_buffer->buffer; + tbm_surface_internal_unref(buffer); + voutput_info->committing = 0; + voutput_info->attach_buffer = NULL; + } + tdm_voutput_disconnect(voutput_info->voutput); } static void _tdm_voutput_cb_commit_done(struct wl_client *client, struct wl_resource *resource) { - /* TODO */ + tdm_server_voutput_info *voutput_info; + tbm_surface_h buffer; + + voutput_info = wl_resource_get_user_data(resource); + if (voutput_info->status != TDM_OUTPUT_CONN_STATUS_CONNECTED) { + TDM_DBG("not connected."); + return; + } + + buffer = voutput_info->attach_buffer->buffer; + tbm_surface_internal_unref(buffer); + voutput_info->committing = 0; + voutput_info->attach_buffer = NULL; + + tdm_voutput_commit_done(voutput_info->voutput); } static const struct wl_tdm_voutput_interface tdm_voutput_implementation = { @@ -847,10 +872,274 @@ static const struct wl_tdm_voutput_interface tdm_voutput_implementation = { _tdm_voutput_cb_commit_done }; +static void +_tdm_voutput_wl_buffer_destroy(struct wl_client *client, struct wl_resource *wl_buffer) +{ + wl_resource_destroy(wl_buffer); +} + +static const struct wl_buffer_interface _tdm_voutput_buffer_impementation = { + _tdm_voutput_wl_buffer_destroy +}; + +static void +_tdm_voutput_buffer_destory(struct wl_resource *wl_buffer) +{ + tdm_server_voutput_info *voutput_info = NULL; + tdm_server_voutput_buffer *vb = NULL, *vbb = NULL; + + voutput_info = (tdm_server_voutput_info *)wl_resource_get_user_data(wl_buffer); + TDM_RETURN_IF_FAIL(voutput_info != NULL); + + LIST_FOR_EACH_ENTRY_SAFE(vb, vbb, &voutput_info->buffer_list, link) { + if (vb->wl_buffer == wl_buffer) { + tbm_surface_internal_unref(vb->buffer); + wl_resource_set_user_data(wl_buffer, NULL); + LIST_DEL(&vb->link); + free(vb); + } + } +} + +struct wl_resource * +_tdm_voutput_create_wl_buffer(tdm_server_voutput_info *voutput_info) +{ + struct wl_client *wl_client; + struct wl_resource *wl_buffer = NULL; + + wl_client = wl_resource_get_client(voutput_info->resource); + + /* create a wl_buffer resource */ + wl_buffer = wl_resource_create(wl_client, &wl_buffer_interface, 1, 0); + TDM_RETURN_VAL_IF_FAIL(wl_buffer != NULL, NULL); + + wl_resource_set_implementation(wl_buffer, + (void (**)(void)) &_tdm_voutput_buffer_impementation, + voutput_info, _tdm_voutput_buffer_destory); + + return wl_buffer; +} + +struct wl_resource * +_tdm_voutput_export_buffer(tdm_server_voutput_info *voutput_info, + tbm_surface_h buffer) +{ + int bufs[TBM_SURF_PLANE_MAX] = { -1, -1, -1, -1}; + struct wl_resource *wl_buffer = NULL; + int num_buf, is_fd = -1, i; + tbm_surface_info_s info; + uint32_t flags = 0; + struct wl_array plane_buf_idx, plane_offset, plane_stride, plane_size; + int *p; + + TDM_RETURN_VAL_IF_FAIL(voutput_info != NULL, NULL); + TDM_RETURN_VAL_IF_FAIL(buffer != NULL, NULL); + + if (tbm_surface_get_info(buffer, &info) != TBM_SURFACE_ERROR_NONE) { + TDM_ERR("Failed to create buffer from surface"); + return NULL; + } + + if (info.num_planes > 3) { + TDM_ERR("invalid num_planes(%d)", info.num_planes); + return NULL; + } + + num_buf = tbm_surface_internal_get_num_bos(buffer); + if (num_buf == 0) { + TDM_ERR("surface doesn't have any bo."); + return NULL; + } + + for (i = 0; i < num_buf; i++) { + tbm_bo bo = tbm_surface_internal_get_bo(buffer, i); + if (bo == NULL) { + TDM_ERR("Failed to get bo from surface"); + goto err; + } + + /* try to get fd first */ + if (is_fd == -1 || is_fd == 1) { + bufs[i] = tbm_bo_export_fd(bo); + if (is_fd == -1 && bufs[i] >= 0) + is_fd = 1; + } + + /* if fail to get fd, try to get name second */ + if (is_fd == -1 || is_fd == 0) { + bufs[i] = tbm_bo_export(bo); + if (is_fd == -1 && bufs[i] > 0) + is_fd = 0; + } + + if (is_fd == -1 || + (is_fd == 1 && bufs[i] < 0) || + (is_fd == 0 && bufs[i] <= 0)) { + TDM_ERR("Failed to export(is_fd:%d, bufs:%d)", is_fd, bufs[i]); + goto err; + } + } + + wl_buffer = _tdm_voutput_create_wl_buffer(voutput_info); + if (!wl_buffer) { + TDM_ERR("Failed to create wl_buffer"); + goto err; + } + + wl_array_init(&plane_buf_idx); + wl_array_init(&plane_offset); + wl_array_init(&plane_stride); + wl_array_init(&plane_size); + + for (i = 0; i < 3; i++) { + p = wl_array_add(&plane_buf_idx, sizeof(int)); + *p = tbm_surface_internal_get_plane_bo_idx(buffer, i); + p = wl_array_add(&plane_offset, sizeof(int)); + *p = info.planes[i].offset; + p = wl_array_add(&plane_stride, sizeof(int)); + *p = info.planes[i].stride; + p = wl_array_add(&plane_size, sizeof(int)); + *p = info.planes[i].size; + } + + if (is_fd == 1) + wl_tdm_voutput_send_buffer_set_with_fd(voutput_info->resource, + wl_buffer, + info.width, info.height, info.format, info.bpp, info.size, info.num_planes, + &plane_buf_idx, &plane_offset, &plane_stride, &plane_size, + flags, num_buf, bufs[0], + (bufs[1] == -1) ? bufs[0] : bufs[1], + (bufs[2] == -1) ? bufs[0] : bufs[2]); + else + wl_tdm_voutput_send_buffer_set_with_id(voutput_info->resource, + wl_buffer, + info.width, info.height, info.format, info.bpp, info.size, info.num_planes, + &plane_buf_idx, &plane_offset, &plane_stride, &plane_size, + flags, + num_buf, bufs[0], bufs[1], bufs[2]); + + wl_array_release(&plane_buf_idx); + wl_array_release(&plane_offset); + wl_array_release(&plane_stride); + wl_array_release(&plane_size); + + for (i = 0; i < TBM_SURF_PLANE_MAX; i++) { + if (is_fd == 1 && (bufs[i] >= 0)) + close(bufs[i]); + } + + return wl_buffer; + +err: + for (i = 0; i < TBM_SURF_PLANE_MAX; i++) { + if (is_fd == 1 && (bufs[i] >= 0)) + close(bufs[i]); + } + + return NULL; +} + +tdm_server_voutput_buffer * +_tdm_output_get_voutput_buffer(tdm_server_voutput_info *voutput_info, tbm_surface_h buffer) +{ + tdm_server_voutput_buffer *voutput_buffer = NULL, *vb = NULL; + + LIST_FOR_EACH_ENTRY(vb, &voutput_info->buffer_list, link) { + if (vb && vb->buffer == buffer) { + tbm_surface_internal_ref(vb->buffer); + return vb; + } + } + + tbm_surface_internal_ref(buffer); + voutput_buffer = calloc(1, sizeof *voutput_buffer); + if (!voutput_buffer) { + /* LCOV_EXCL_START */ + + TDM_ERR("fail calloc"); + tbm_surface_internal_unref(buffer); + return NULL; + + /* LCOV_EXCL_STOP */ + } + + voutput_buffer->wl_buffer = _tdm_voutput_export_buffer(voutput_info, buffer); + if (!voutput_buffer->wl_buffer) { + /* LCOV_EXCL_START */ + + TDM_ERR("fail export buffer"); + free(voutput_buffer); + tbm_surface_internal_unref(buffer); + return NULL; + + /* LCOV_EXCL_STOP */ + } + + voutput_buffer->buffer = buffer; + LIST_ADDTAIL(&voutput_buffer->link, &voutput_info->buffer_list); + + return voutput_buffer; +} + +tdm_error +tdm_voutput_attach_buffer(tdm_voutput *voutput, tbm_surface_h buffer) +{ + tdm_private_server *private_server = keep_private_server; + tdm_server_voutput_info *voutput_info = NULL, *vo = NULL; + tdm_server_voutput_buffer *voutput_buffer = NULL; + + TDM_RETURN_VAL_IF_FAIL(keep_private_server != NULL, TDM_ERROR_OPERATION_FAILED); + + LIST_FOR_EACH_ENTRY(vo, &private_server->voutput_list, link) { + if (vo && vo->voutput == voutput) { + voutput_info = vo; + break; + } + } + TDM_RETURN_VAL_IF_FAIL(voutput_info != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(voutput_info->attach_buffer == NULL, TDM_ERROR_OPERATION_FAILED); + + voutput_buffer = _tdm_output_get_voutput_buffer(voutput_info, buffer); + TDM_RETURN_VAL_IF_FAIL(voutput_buffer != NULL, TDM_ERROR_OUT_OF_MEMORY); + + voutput_info->attach_buffer = voutput_buffer; + + wl_tdm_voutput_send_attach_buffer(voutput_info->resource, voutput_buffer->wl_buffer); + + return TDM_ERROR_NONE; +} + +tdm_error +tdm_voutput_commit_buffer(tdm_voutput *voutput) +{ + tdm_private_server *private_server = keep_private_server; + tdm_server_voutput_info *voutput_info = NULL, *vo = NULL; + + TDM_RETURN_VAL_IF_FAIL(keep_private_server != NULL, TDM_ERROR_OPERATION_FAILED); + + LIST_FOR_EACH_ENTRY(vo, &private_server->voutput_list, link) { + if (vo && vo->voutput == voutput) { + voutput_info = vo; + break; + } + } + TDM_RETURN_VAL_IF_FAIL(voutput_info != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(voutput_info->attach_buffer != NULL, TDM_ERROR_OPERATION_FAILED); + TDM_RETURN_VAL_IF_FAIL(voutput_info->committing == 0, TDM_ERROR_OPERATION_FAILED); + + tbm_surface_internal_ref(voutput_info->attach_buffer->buffer); + voutput_info->committing = 1; + + wl_tdm_voutput_send_commit(voutput_info->resource); + + return TDM_ERROR_NONE; +} + void tdm_voutput_cb_resource_destroy(struct wl_resource *resource) { tdm_server_voutput_info *voutput_info = wl_resource_get_user_data(resource); + tdm_server_voutput_buffer *vb; tdm_voutput *voutput; tdm_error ret = TDM_ERROR_NONE; @@ -863,6 +1152,20 @@ tdm_voutput_cb_resource_destroy(struct wl_resource *resource) if (ret != TDM_ERROR_NONE) TDM_ERR("_tdm_voutput_cb_destroy fail"); + if (voutput_info->attach_buffer) { + tbm_surface_h buffer = voutput_info->attach_buffer->buffer; + tbm_surface_internal_unref(buffer); + voutput_info->committing = 0; + voutput_info->attach_buffer = NULL; + } + + LIST_FOR_EACH_ENTRY(vb, &voutput_info->buffer_list, link) { + if (!vb) continue; + + if (vb->wl_buffer) + wl_resource_destroy(vb->wl_buffer); + } + LIST_DEL(&voutput_info->link); /* Do free your own resource */ @@ -933,6 +1236,7 @@ _tdm_server_cb_create_virtual_output(struct wl_client *client, struct wl_resourc voutput_info->resource = voutput_resource; voutput_info->voutput = voutput; voutput_info->output = output; + LIST_INITHEAD(&voutput_info->buffer_list); wl_resource_set_implementation(voutput_resource, &tdm_voutput_implementation, diff --git a/src/tdm_thread.c b/src/tdm_thread.c index d367d4e..40d7859 100644 --- a/src/tdm_thread.c +++ b/src/tdm_thread.c @@ -462,6 +462,7 @@ tdm_thread_handle_cb(tdm_private_loop *private_loop) case TDM_THREAD_CB_VBLANK_SW: case TDM_THREAD_CB_VBLANK_CREATE: case TDM_THREAD_CB_HWC_COMMIT: + case TDM_THREAD_CB_VOUTPUT_COMMIT: /* this event comes from other thread. so we don't need to propagate this to other thread */ ret = tdm_thread_cb_call(NULL, base, 0); TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE); -- 2.7.4 From 29faed4a576ecf17702612f7326272d1540af189 Mon Sep 17 00:00:00 2001 From: Junkyeong Kim Date: Tue, 16 Oct 2018 21:28:25 +0900 Subject: [PATCH 09/16] 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 --- backends/Makefile.am | 2 +- backends/virtual/Makefile.am | 14 + backends/virtual/tdm_virtual.c | 148 +++++ backends/virtual/tdm_virtual.h | 89 +++ backends/virtual/tdm_virtual_display.c | 997 +++++++++++++++++++++++++++++++++ configure.ac | 1 + packaging/libtdm.spec | 1 + src/tdm.c | 8 + src/tdm_backend.c | 24 + src/tdm_display.c | 9 + 10 files changed, 1292 insertions(+), 1 deletion(-) create mode 100644 backends/virtual/Makefile.am create mode 100644 backends/virtual/tdm_virtual.c create mode 100644 backends/virtual/tdm_virtual.h create mode 100644 backends/virtual/tdm_virtual_display.c 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; -- 2.7.4 From 6c80f26553de39cde3962f18c418e665a48740e5 Mon Sep 17 00:00:00 2001 From: Junkyeong Kim Date: Tue, 16 Oct 2018 21:30:48 +0900 Subject: [PATCH 10/16] client: add tdm_client_output_get_mode function Change-Id: Ie21b4f8f3179624339bf97e5dce7727b36c5d6bb Signed-off-by: Junkyeong Kim --- client/tdm_client.c | 45 ++++++++++++++++++++++++++++++++++++++++++ client/tdm_client.h | 10 ++++++++++ haltests/src/tc_tdm_client.cpp | 31 +++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+) diff --git a/client/tdm_client.c b/client/tdm_client.c index 972ae00..57f8ce3 100644 --- a/client/tdm_client.c +++ b/client/tdm_client.c @@ -1061,6 +1061,51 @@ tdm_client_output_get_refresh_rate(tdm_client_output *output, unsigned int *refr } tdm_error +tdm_client_output_get_mode(tdm_client_output *output, unsigned int *width, unsigned int *height) +{ + tdm_private_client_output *private_output; + tdm_private_client *private_client; + + TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(width != NULL, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(height != NULL, TDM_ERROR_INVALID_PARAMETER); + + private_output = (tdm_private_client_output*)output; + private_client = private_output->private_client; + + pthread_mutex_lock(&private_client->lock); + + if (private_output->watch_output_changes) { + *width = private_output->width; + *height = private_output->height; + pthread_mutex_unlock(&private_client->lock); + return TDM_ERROR_NONE; + } + + if (CHECK_WL_PROTOCOL_ERROR(private_client)) { + pthread_mutex_unlock(&private_client->lock); + return TDM_ERROR_PROTOCOL_ERROR; + } + + wl_proxy_set_queue((struct wl_proxy *)private_output->output, private_client->queue); + wl_tdm_output_get_mode(private_output->output); + wl_display_roundtrip_queue(private_client->display, private_client->queue); + wl_proxy_set_queue((struct wl_proxy *)private_output->output, NULL); + + if (CHECK_WL_PROTOCOL_ERROR(private_client)) { + pthread_mutex_unlock(&private_client->lock); + return TDM_ERROR_PROTOCOL_ERROR; + } + + *width = private_output->width; + *height = private_output->height; + + pthread_mutex_unlock(&private_client->lock); + + return TDM_ERROR_NONE; +} + +tdm_error tdm_client_output_get_conn_status(tdm_client_output *output, tdm_output_conn_status *status) { tdm_private_client_output *private_output; diff --git a/client/tdm_client.h b/client/tdm_client.h index 17d63cf..f8b2dec 100644 --- a/client/tdm_client.h +++ b/client/tdm_client.h @@ -219,6 +219,16 @@ tdm_error tdm_client_output_get_refresh_rate(tdm_client_output *output, unsigned int *refresh); /** + * @brief Get the width and height of the given client output mode + * @param[in] output The client output object + * @param[out] width The width of output mode + * @param[out] height The height of output mode + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ +tdm_error +tdm_client_output_get_mode(tdm_client_output *output, unsigned int *width, unsigned int *height); + +/** * @brief Get the connection status of the given client output * @param[in] output The client output object * @param[out] status The connection status diff --git a/haltests/src/tc_tdm_client.cpp b/haltests/src/tc_tdm_client.cpp index 0d54855..504aaee 100644 --- a/haltests/src/tc_tdm_client.cpp +++ b/haltests/src/tc_tdm_client.cpp @@ -746,6 +746,37 @@ TEST_P(TDMClient, ClientOutputGetRefreshRateNullOther) ASSERT_EQ(tdm_client_output_get_refresh_rate(output, NULL), TDM_ERROR_INVALID_PARAMETER); } +/* tdm_client_output_get_mode */ +TEST_P(TDMClient, ClientOutputGetMode) +{ + unsigned int width = 0, height = 0; + + ASSERT_EQ(PrepareClient(), true); + ASSERT_EQ(PrepareOutput(), true); + + ASSERT_EQ(tdm_client_output_get_mode(output, &width, &height), TDM_ERROR_NONE); + ASSERT_GT(width, 0); + ASSERT_GT(height, 0); +} + +TEST_P(TDMClient, ClientOutputGetModeNullObject) +{ + unsigned int width = (unsigned int)TDM_UT_INVALID_VALUE; + unsigned int height = (unsigned int)TDM_UT_INVALID_VALUE; + + ASSERT_EQ(tdm_client_output_get_mode(NULL, &width, &height), TDM_ERROR_INVALID_PARAMETER); + ASSERT_EQ(width, (unsigned int)TDM_UT_INVALID_VALUE); + ASSERT_EQ(height, (unsigned int)TDM_UT_INVALID_VALUE); +} + +TEST_P(TDMClient, ClientOutputGetModeNullOther) +{ + ASSERT_EQ(PrepareClient(), true); + ASSERT_EQ(PrepareOutput(), true); + + ASSERT_EQ(tdm_client_output_get_mode(output, NULL, NULL), TDM_ERROR_INVALID_PARAMETER); +} + /* tdm_client_output_get_conn_status */ TEST_P(TDMClient, ClientOutputGetConnStatus) { -- 2.7.4 From 82dff0f96f23656af78388fd2955690dd31b7ccc Mon Sep 17 00:00:00 2001 From: Junkyeong Kim Date: Tue, 16 Oct 2018 21:34:07 +0900 Subject: [PATCH 11/16] tests: add virtual output test use -V option. sample sequence: test client create virtual output and execute voutput_connect. test server send buffers to client. test client dump received buffers (0~10, 31~40). test client execute mode_change_request when received 30th commit. test client execute voutput_disconnect and destroy when received 50th commit. Change-Id: I8864e8a5bc974b3189ff4e3a8fb66ceccc22dd2c Signed-off-by: Junkyeong Kim --- tools/tdm_test_client.c | 339 ++++++++++++++++++++++++++++++++++++++++- tools/tdm_test_server.c | 390 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 726 insertions(+), 3 deletions(-) diff --git a/tools/tdm_test_client.c b/tools/tdm_test_client.c index 1031685..ab12626 100644 --- a/tools/tdm_test_client.c +++ b/tools/tdm_test_client.c @@ -40,9 +40,13 @@ #include #include #include +#include #include "tdm_client.h" #include "tdm_macro.h" +#include "buffers.h" + +#define CHECK_V_STEP 0 typedef struct _tdm_test_client_arg { char *output_name; @@ -60,9 +64,12 @@ typedef struct _tdm_test_client { int do_query; int do_vblank; + int do_voutput; int waiting; tdm_client *client; + tdm_client_voutput *voutput; + tdm_client_output *output; } tdm_test_client; struct typestrings { @@ -93,6 +100,7 @@ static struct typestrings typestrs[] = { static struct optstrings optstrs[] = { {OPT_QRY, "qo", "output objects info", "", "primary"}, {OPT_TST, "v", "vblank test", "[,][@][~][+][*fake][^vblank_name]", "primary,0@60~1+0*1^test"}, + {OPT_TST, "V", "virtual output test", NULL, NULL}, }; static void @@ -181,7 +189,7 @@ parse_args(tdm_test_client *data, int argc, char *argv[]) { int i; - if (argc < 3) { + if (argc < 2) { usage(argv[0]); exit(0); } @@ -196,6 +204,8 @@ parse_args(tdm_test_client *data, int argc, char *argv[]) } else if (!strncmp(argv[i] + 1, "v", 1)) { data->do_vblank = 1; parse_arg_v(data, argv[++i]); + } else if (!strncmp(argv[i] + 1, "V", 1)) { + data->do_voutput = 1; } else { usage(argv[0]); exit(0); @@ -374,6 +384,318 @@ done: tdm_client_vblank_destroy(vblank); } +#define PNG_DEPTH 8 + +void +_tdm_client_get_buffer_full_size(tbm_surface_h buffer, int *buffer_w, int *buffer_h) +{ + tbm_surface_info_s info; + int ret; + + TDM_RETURN_IF_FAIL(buffer != NULL); + + ret = tbm_surface_get_info(buffer, &info); + TDM_RETURN_IF_FAIL(ret == TBM_SURFACE_ERROR_NONE); + + if (buffer_w) { + if (IS_RGB(info.format)) + *buffer_w = info.planes[0].stride >> 2; + else + *buffer_w = info.planes[0].stride; + } + + if (buffer_h) + *buffer_h = info.planes[0].size / info.planes[0].stride; +} + +static void +_tdm_client_dump_png(const char *file, const void *data, int width, + int height) +{ + FILE *fp; + + fp = fopen(file, "wb"); + TDM_RETURN_IF_FAIL(fp != NULL); + + png_structp pPngStruct = + png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!pPngStruct) { + fclose(fp); + return; + } + + png_infop pPngInfo = png_create_info_struct(pPngStruct); + if (!pPngInfo) { + png_destroy_write_struct(&pPngStruct, NULL); + fclose(fp); + return; + } + + png_init_io(pPngStruct, fp); + png_set_IHDR(pPngStruct, + pPngInfo, + width, + height, + PNG_DEPTH, + PNG_COLOR_TYPE_RGBA, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + png_set_bgr(pPngStruct); + png_write_info(pPngStruct, pPngInfo); + + const int pixel_size = 4; // RGBA + png_bytep *row_pointers = + png_malloc(pPngStruct, height * sizeof(png_byte *)); + if (!row_pointers) { + png_destroy_write_struct(&pPngStruct, &pPngInfo); + fclose(fp); + return; + } + + unsigned int *blocks = (unsigned int *)data; + int y = 0; + int x = 0; + + for (; y < height; ++y) { + png_bytep row = + png_malloc(pPngStruct, sizeof(png_byte) * width * pixel_size); + if (!row) { + for (x = 0; x < y; x++) + png_free(pPngStruct, row_pointers[x]); + png_free(pPngStruct, row_pointers); + png_destroy_write_struct(&pPngStruct, &pPngInfo); + fclose(fp); + return; + } + + row_pointers[y] = (png_bytep)row; + for (x = 0; x < width; ++x) { + unsigned int curBlock = blocks[y * width + x]; + row[x * pixel_size] = (curBlock & 0xFF); + row[1 + x * pixel_size] = (curBlock >> 8) & 0xFF; + row[2 + x * pixel_size] = (curBlock >> 16) & 0xFF; + row[3 + x * pixel_size] = (curBlock >> 24) & 0xFF; + } + } + + png_write_image(pPngStruct, row_pointers); + png_write_end(pPngStruct, pPngInfo); + + for (y = 0; y < height; y++) + png_free(pPngStruct, row_pointers[y]); + png_free(pPngStruct, row_pointers); + + png_destroy_write_struct(&pPngStruct, &pPngInfo); + + fclose(fp); +} + +void +_tdm_client_dump_buffer(tbm_surface_h buffer, const char *file) +{ + char temp[TDM_PATH_LEN] = {0,}; + tbm_surface_info_s info; + int len, ret; + const char *ext; + int bo_cnt; + int bw, bh; + char *dot, *p = temp; + const char *file_exts[2] = {"png", "raw"}; + + TDM_RETURN_IF_FAIL(buffer != NULL); + TDM_RETURN_IF_FAIL(file != NULL); + + ret = tbm_surface_map(buffer, TBM_OPTION_READ, &info); + TDM_RETURN_IF_FAIL(ret == TBM_SURFACE_ERROR_NONE); + + if (IS_RGB(info.format)) + ext = file_exts[0]; + else + ext = file_exts[1]; + + dot = strrchr(file, '.'); + if (!dot || strlen(dot + 1) != 3 || strncmp(dot + 1, ext, 3)) { + len = strnlen(file, TDM_PATH_LEN - 5); + strncat(p, file, len); + p += len; + *(p++) = '.'; + strncat(p, ext, 3); + p += 3; + *p = '\0'; + } else { + len = strnlen(file, TDM_PATH_LEN - 1); + strncat(p, file, len); + p += len; + *p = '\0'; + } + + _tdm_client_get_buffer_full_size(buffer, &bw, &bh); + + bo_cnt = tbm_surface_internal_get_num_bos(buffer); + TDM_DBG("buffer: bo_cnt(%d) %dx%d(%dx%d) %c%c%c%c, plane: (%p+%d, %d,%d) (%p+%d, %d,%d) (%p+%d, %d,%d)", + bo_cnt, bw, bh, info.width, info.height, FOURCC_STR(info.format), + info.planes[0].ptr, info.planes[0].offset, info.planes[0].stride, info.planes[0].size, + info.planes[1].ptr, info.planes[1].offset, info.planes[1].stride, info.planes[1].size, + info.planes[2].ptr, info.planes[2].offset, info.planes[2].stride, info.planes[2].size); + + _tdm_client_dump_png(temp, info.planes[0].ptr, bw, bh); + + tbm_surface_unmap(buffer); + + printf("dump %s\n", temp); +} + +static void +_dump_buffer(tbm_surface_h buffer, int count) +{ + char temp[TDM_PATH_LEN] = {0,}; + + snprintf(temp, TDM_PATH_LEN, "/tmp/%c%c%c%c_%dx%d_%d", + FOURCC_STR(tbm_surface_get_format(buffer)), + tbm_surface_get_width(buffer), + tbm_surface_get_height(buffer), + count); + _tdm_client_dump_buffer(buffer, temp); +} + +static void +_voutput_commit(tdm_client_voutput *voutput, tbm_surface_h buffer, void *user_data) +{ + tdm_test_client *data = (tdm_test_client *)user_data; + static int count = 0; + + TDM_EXIT_IF_FAIL(data != NULL); + TDM_EXIT_IF_FAIL(buffer != NULL); + + if ((count < 10) || (count >= 31 && count <= 40)) + _dump_buffer(buffer, count); + count++; + + if (count == 30) { + printf("client: %d commited(%p), mode change request to index 1\n", count, buffer); + tdm_client_voutput_set_mode(data->voutput, 1); + } else if (count == 50) { + printf("client: %d commited(%p), disconnect\n", count, buffer); + tdm_client_voutput_disconnect(data->voutput); + } else { + printf("client: %d commited(%p)\n", count, buffer); + } + + tdm_client_voutput_commit_done(voutput); +} + +static void +_voutput_output_handler(tdm_client_output *output, tdm_output_change_type type, + tdm_value value, void *user_data) +{ + tdm_client_voutput *voutput = NULL; + tdm_output_conn_status status; + tdm_test_client *data; + unsigned int width, height; + + data = (tdm_test_client *) user_data; + TDM_RETURN_IF_FAIL(data != NULL); + voutput = data->voutput; + TDM_RETURN_IF_FAIL(voutput != NULL); + + if (type == TDM_OUTPUT_CHANGE_CONNECTION) { + status = (tdm_output_conn_status)value.u32; + printf("output %s.\n", conn_str[value.u32]); + + if (status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) { + printf("client: disconnected, destroy voutput\n"); + tdm_client_output_remove_change_handler(output, _voutput_output_handler, data); +#if CHECK_V_STEP + printf("press enter to continuet\n"); + getchar(); +#endif + tdm_client_voutput_destroy(voutput); + } else if (status == TDM_OUTPUT_CONN_STATUS_CONNECTED) { + printf("client: connected\n"); + } else if (status == TDM_OUTPUT_CONN_STATUS_MODE_SETTED) { + tdm_client_output_get_mode(output, &width, &height); + printf("client: mode setted(%dx%d)\n", width, height); +#if CHECK_V_STEP + printf("press enter to continuet\n"); + getchar(); +#endif + } + } else if (type == TDM_OUTPUT_CHANGE_DPMS) { + printf("output %s.\n", dpms_str[value.u32]); + } +} + +static void +_voutput_make_available_mode(tdm_client_output_mode *modes, int count) +{ + int i; + for (i = 0 ; i < count; i++) { + modes[i].clock = 25200; + modes[i].hdisplay = 640 * (count - i); + modes[i].hsync_start = 656; + modes[i].hsync_end = 752; + modes[i].htotal = 800; + modes[i].hskew = 0; + modes[i].vdisplay = 480 * (count - i); + modes[i].vsync_start = 490; + modes[i].vsync_end = 492; + modes[i].vtotal = 525; + modes[i].vscan = 0; + modes[i].vrefresh = 30; + modes[i].flags = 0; + modes[i].type = 0; + snprintf(modes[i].name, TDM_NAME_LEN, "%dx%d_%d", modes[i].hdisplay, modes[i].vdisplay, i); + } +} + +static void +do_voutput(tdm_test_client *data) +{ + tdm_client_voutput *voutput = NULL; + tdm_client_output *output = NULL; + tdm_client_output_mode modes[2]; + tdm_error ret = TDM_ERROR_NONE; + + printf("virtual output test - client\n"); + + voutput = tdm_client_create_voutput(data->client, "virtual-test", &ret); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + ret = tdm_client_voutput_add_commit_handler(voutput, _voutput_commit, data); + TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done); + + output = tdm_client_voutput_get_client_output(voutput, &ret); + TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done); + + ret = tdm_client_output_add_change_handler(output, _voutput_output_handler, data); + TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done); + + ret = tdm_client_voutput_set_physical_size(voutput, 300, 200); + TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done); + + _voutput_make_available_mode(modes, 2); + ret = tdm_client_voutput_set_available_modes(voutput, modes, 2); + TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done); +#if CHECK_V_STEP + printf("virtual output test - press enter to connect\n"); + getchar(); +#endif + ret = tdm_client_voutput_connect(voutput); + TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done); + + data->voutput = voutput; + data->output = output; + + while (1) { + tdm_client_handle_events_timeout(data->client, 1000); + } + +done: + if (voutput) + tdm_client_voutput_destroy(voutput); +} + static tdm_test_client ttc_data; int @@ -382,7 +704,7 @@ main(int argc, char *argv[]) tdm_test_client *data = &ttc_data; tdm_error error; -#if 1 /* for testing */ + /* for testing */ const char *xdg = (const char*)getenv("XDG_RUNTIME_DIR"); if (!xdg) { char buf[32]; @@ -391,7 +713,16 @@ main(int argc, char *argv[]) if (ret != 0) exit(0); } -#endif + + /* for tbm_bufmgr_init */ + const char *s = (const char*)getenv("TBM_DISPLAY_SERVER"); + if (!s) { + char buf[32]; + snprintf(buf, sizeof(buf), "1"); + int ret = setenv("TBM_DISPLAY_SERVER", (const char*)buf, 1); + if (ret != 0) + exit(0); + } parse_args(data, argc, argv); @@ -409,6 +740,8 @@ main(int argc, char *argv[]) do_query(data); if (data->do_vblank) do_vblank(data); + if (data->do_voutput) + do_voutput(data); done: if (data->args.output_name) diff --git a/tools/tdm_test_server.c b/tools/tdm_test_server.c index 535462f..45671ea 100644 --- a/tools/tdm_test_server.c +++ b/tools/tdm_test_server.c @@ -108,6 +108,10 @@ static struct optstrings optstrs[] = { "[,]~x[++][,x][@][*][^stream]", NULL }, { + OPT_TST, "V", "virtual output test.\n\t\t'-l' is used to show the result on screen.", + NULL, NULL + }, + { OPT_GEN, "w", "set the property of a object", ":", NULL }, @@ -217,6 +221,7 @@ TDM_BIT_NAME_FB(buf_flag) typedef struct _tdm_test_server tdm_test_server; typedef struct _tdm_test_server_layer tdm_test_server_layer; typedef struct _tdm_test_server_capture tdm_test_server_capture; +typedef struct _tdm_test_server_voutput tdm_test_server_voutput; typedef struct _tdm_test_server_prop { /* args */ @@ -300,11 +305,25 @@ struct _tdm_test_server_layer { int buf_idx; }; +struct _tdm_test_server_voutput { + struct list_head link; + tdm_test_server *data; + tdm_output *output; + tdm_layer *layer; + tbm_surface_h bufs[3]; + int buf_idx; + int need_mode_change; + int mode_index; + tdm_event_loop_source *timer; + const tdm_output_mode *current_mode; +}; + struct _tdm_test_server { /* args */ int do_query; int do_all; int do_vblank; + int do_voutput; int bflags; int b_fill; @@ -312,6 +331,7 @@ struct _tdm_test_server { struct list_head output_list; struct list_head pp_list; struct list_head capture_list; + struct list_head voutput_list; tdm_display *display; }; @@ -320,6 +340,8 @@ static void run_test(tdm_test_server *data); static void output_setup(tdm_test_server_output *o); static void layer_show_buffer(tdm_test_server_layer *l, tbm_surface_h b); static void capture_attach(tdm_test_server_capture *c, tbm_surface_h b); +static void _vlayer_show_buffer(tdm_test_server_voutput *voutput); +static void _voutput_layer_init(tdm_test_server_voutput *voutput); static char* parse_size(tdm_size *size, char *arg) @@ -596,6 +618,8 @@ parse_args(tdm_test_server *data, int argc, char *argv[]) parse_arg_b(data, argv[++i]); } else if (!strncmp(argv[i] + 1, "v", 1)) { data->do_vblank = 1; + } else if (!strncmp(argv[i] + 1, "V", 1)) { + data->do_voutput = 1; } else { usage(argv[0]); destroy(data); @@ -790,6 +814,355 @@ get_tts_buffer(tbm_surface_h b) } static void +_voutput_buff_deinit(tdm_test_server_voutput *voutput) +{ + int i; + + for (i = 0; i < 3; i++) { + if (!voutput->bufs[i]) continue; + + tbm_surface_destroy(voutput->bufs[i]); + voutput->bufs[i] = NULL; + } +} + +static void +_voutput_buff_init(tdm_test_server_voutput *voutput) +{ + tdm_output *output = voutput->output; + const tdm_output_mode *mode; + tdm_error ret = TDM_ERROR_NONE; + int i; + + ret = tdm_output_get_mode(output, &mode); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + for (i = 0; i < 3; i++) { + tbm_surface_h b = tbm_surface_internal_create_with_flags(mode->hdisplay, mode->vdisplay, DEFAULT_FORMAT, 0); + TDM_EXIT_IF_FAIL(b != NULL); + tdm_test_buffer_fill(b, i); + voutput->bufs[i] = b; + } +} +static void +_vlayer_cb_commit(tdm_layer *layer, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec, void *user_data) +{ + tdm_test_server_voutput *voutput = (tdm_test_server_voutput *)user_data; + TDM_EXIT_IF_FAIL(voutput != NULL); + tdm_test_server *data; + tdm_output_conn_status status; + tdm_error ret; + + printf("voutput cb:\t %d: l(%p) b(%p)\n", voutput->buf_idx, voutput->layer, voutput->bufs[voutput->buf_idx]); + + ret = tdm_output_get_conn_status(voutput->output, &status); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + if (status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) return; + + if (voutput->need_mode_change) { + const tdm_output_mode *modes; + int count = 0; + + tdm_output_get_available_modes(voutput->output, &modes, &count); + if (count > voutput->mode_index) { + const tdm_output_mode *mode; + + mode = &modes[voutput->mode_index]; + data = voutput->data; + + printf("mode change to %d (%dx%d, %d)\n", + voutput->mode_index, mode->hdisplay, mode->vdisplay, mode->vrefresh); + _voutput_buff_deinit(voutput); + _voutput_buff_init(voutput); + + ret = tdm_output_set_mode(voutput->output, mode); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + voutput->current_mode = mode; + + _voutput_layer_init(voutput); + voutput->buf_idx = 0; + + tdm_display_lock(data->display); + ret = tdm_event_loop_source_timer_update(voutput->timer, 10); + tdm_display_unlock(data->display); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + } + + voutput->need_mode_change = 0; + return; + } + + voutput->buf_idx++; + _vlayer_show_buffer(voutput); +} + +static void +_vlayer_show_buffer(tdm_test_server_voutput *voutput) +{ + tdm_error ret; + int index; + if (voutput->buf_idx == 3) + voutput->buf_idx = 0; + + index = voutput->buf_idx; + + ret = tdm_layer_set_buffer(voutput->layer, voutput->bufs[index]); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + ret = tdm_layer_commit(voutput->layer, _vlayer_cb_commit, voutput); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + printf("voutput show:\t %d: l(%p) b(%p)\n", index, voutput->layer, voutput->bufs[index]); +} + +static void +_voutput_layer_init(tdm_test_server_voutput *voutput) +{ + tdm_output *output = voutput->output; + const tdm_output_mode *mode; + tdm_info_layer layer_info; + tbm_surface_info_s info; + tdm_error ret = TDM_ERROR_NONE; + + ret = tdm_output_get_mode(output, &mode); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + TDM_ERR("modeinfo : %dx%d %d", mode->hdisplay, mode->vdisplay, mode->vrefresh); + + voutput->current_mode = mode; + + voutput->layer = tdm_output_get_layer(output, 0, &ret); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + _voutput_buff_init(voutput); + + tbm_surface_get_info(voutput->bufs[0], &info); + memset(&layer_info, 0x0, sizeof(tdm_info_layer)); + if (IS_RGB(info.format)) { + layer_info.src_config.size.h = info.planes[0].stride >> 2; + layer_info.src_config.size.v = info.height; + } else { + layer_info.src_config.size.h = info.planes[0].stride; + layer_info.src_config.size.v = info.height; + } + layer_info.src_config.format = info.format; + + layer_info.src_config.size.h = mode->hdisplay; + layer_info.src_config.size.v = mode->vdisplay; + layer_info.src_config.pos.x = 0; + layer_info.src_config.pos.y = 0; + layer_info.src_config.pos.w = mode->hdisplay; + layer_info.src_config.pos.h = mode->vdisplay; + layer_info.dst_pos.x = 0; + layer_info.dst_pos.y = 0; + layer_info.dst_pos.w = mode->hdisplay; + layer_info.dst_pos.h = mode->vdisplay; + layer_info.transform = TDM_TRANSFORM_NORMAL; + + ret = tdm_layer_set_info(voutput->layer, &layer_info); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); +} + +static void +_voutput_connect(tdm_test_server_voutput *voutput) +{ + tdm_output *output; + const tdm_output_mode *modes, *found = NULL, *best = NULL, *prefer = NULL; + int i, count; + tdm_error ret; + + output = voutput->output; + + ret = tdm_output_get_available_modes(output, &modes, &count); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + for (i = 0; i < count; i++) { + if (!best) + best = &modes[i]; + if (modes[i].type & TDM_OUTPUT_MODE_TYPE_PREFERRED) + prefer = &modes[i]; + } + if (prefer) { + found = prefer; + printf("found prefer mode: %dx%d %d\n", found->hdisplay, found->vdisplay, found->vrefresh); + } + if (!found && best) { + found = best; + printf("found best mode: %dx%d %d\n", found->hdisplay, found->vdisplay, found->vrefresh); + } + if (!found) { + printf("couldn't find any mode\n"); + exit(0); + } + + ret = tdm_output_set_mode(output, found); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + printf("output: %s %d\n", found->name, found->vrefresh); + + ret = tdm_output_set_dpms(output, TDM_OUTPUT_DPMS_ON); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); +} + +static void +_voutput_disconnect(tdm_test_server_voutput *voutput) +{ + tdm_output *output; + tdm_error ret; + + output = voutput->output; + + printf("output: %p disconnect\n", voutput); + + ret = tdm_output_set_dpms(output, TDM_OUTPUT_DPMS_OFF); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); +} + +static tdm_error +_voutput_buffer_send(void *user_data) +{ + tdm_test_server_voutput *voutput = user_data; + tdm_test_server *data = voutput->data; + + tdm_display_unlock(data->display); + + _vlayer_show_buffer(voutput); + + tdm_display_lock(data->display); + + return TDM_ERROR_NONE; +} + +static void +_tdm_test_server_cb_output_change(tdm_output *output, tdm_output_change_type type, tdm_value value, void *user_data) +{ + tdm_test_server_voutput *voutput = NULL; + tdm_test_server *data = NULL; + const tdm_output_mode *mode; + tdm_output_conn_status status; + tdm_error ret = TDM_ERROR_NONE; + + voutput = (tdm_test_server_voutput *)user_data; + TDM_EXIT_IF_FAIL(voutput != NULL); + + data = voutput->data; + + switch (type) { + case TDM_OUTPUT_CHANGE_CONNECTION: + status = (tdm_output_conn_status)value.u32; + if (status == TDM_OUTPUT_CONN_STATUS_CONNECTED) { + _voutput_connect(voutput); + _voutput_layer_init(voutput); + + tdm_display_lock(data->display); + ret = tdm_event_loop_source_timer_update(voutput->timer, 10); + tdm_display_unlock(data->display); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + } else if (status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) { + _voutput_disconnect(voutput); + } else if (status == TDM_OUTPUT_CONN_STATUS_MODE_SETTED) { + ret = tdm_output_get_mode(output, &mode); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + printf("mode setted (%dx%d, %d)\n", mode->hdisplay, mode->vdisplay, mode->vrefresh); + } + + break; + default: + break; + } +} + +static void +_tdm_test_server_cb_output_mode_change(tdm_output *output, unsigned int index, void *user_data) +{ + tdm_test_server_voutput *voutput = NULL; + const tdm_output_mode *modes, *mode; + int count = 0; + tdm_error ret; + + voutput = (tdm_test_server_voutput *)user_data; + TDM_EXIT_IF_FAIL(voutput != NULL); + + ret = tdm_output_get_available_modes(output, &modes, &count); + TDM_RETURN_IF_FAIL(ret == TDM_ERROR_NONE); + TDM_RETURN_IF_FAIL(index < count); + + + mode = &modes[index]; + + if (mode == voutput->current_mode) return; + + voutput->need_mode_change = 1; + voutput->mode_index = index; + + printf("request mode change to %d (%dx%d, %d) from (%dx%d, %d)\n", + index, mode->hdisplay, mode->vdisplay, mode->vrefresh, + voutput->current_mode->hdisplay, voutput->current_mode->vdisplay, voutput->current_mode->vrefresh); +} + +static void +_tdm_output_cb_destroy_handler(tdm_output *output, void *user_data) +{ + tdm_test_server_voutput *voutput = NULL; + + voutput = (tdm_test_server_voutput *)user_data; + TDM_EXIT_IF_FAIL(voutput != NULL); + + tdm_output_remove_mode_change_request_handler(output, _tdm_test_server_cb_output_mode_change, voutput); + tdm_output_remove_change_handler(output, _tdm_test_server_cb_output_change, voutput); + tdm_output_remove_destroy_handler(output, _tdm_output_cb_destroy_handler, voutput); + + LIST_DEL(&voutput->link); + + _voutput_buff_deinit(voutput); + + printf("voutput: %p destroy\n", voutput); + + free(voutput); +} + +static void +_tdm_output_cb_create_handler(tdm_display *dpy, tdm_output *output, void *user_data) +{ + tdm_test_server *data; + tdm_test_server_voutput *voutput = NULL; + tdm_error ret = TDM_ERROR_NONE; + + printf("voutput create call\n"); + + data = (tdm_test_server *)user_data; + TDM_EXIT_IF_FAIL(data != NULL); + + voutput = calloc(1, sizeof *voutput); + TDM_EXIT_IF_FAIL(voutput != NULL); + + ret = tdm_output_add_change_handler(output, _tdm_test_server_cb_output_change, voutput); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + ret = tdm_output_add_mode_change_request_handler(output, _tdm_test_server_cb_output_mode_change, voutput); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + ret = tdm_output_add_destroy_handler(output, _tdm_output_cb_destroy_handler, voutput); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + voutput->output = output; + voutput->data = data; + + tdm_display_lock(data->display); + voutput->timer = tdm_event_loop_add_timer_handler(data->display, _voutput_buffer_send, voutput, &ret); + tdm_display_unlock(data->display); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + + printf("voutput %p create done\n", voutput); + + LIST_ADDTAIL(&voutput->link, &data->voutput_list); +} + +static void destroy(tdm_test_server *data) { tdm_test_server_output *o = NULL, *oo = NULL; @@ -797,6 +1170,7 @@ destroy(tdm_test_server *data) tdm_test_server_pp *p = NULL, *pp = NULL; tdm_test_server_capture *c = NULL, *cc = NULL; tdm_test_server_prop *w = NULL, *ww = NULL; + tdm_test_server_voutput *v = NULL, *vv = NULL; tdm_error ret; int i; @@ -850,6 +1224,15 @@ destroy(tdm_test_server *data) free(o); } + LIST_FOR_EACH_ENTRY_SAFE(v, vv, &data->voutput_list, link) { + _voutput_buff_deinit(v); + LIST_DEL(&v->link); + free(v); + } + + if (data->do_voutput) + tdm_display_remove_output_create_handler(data->display, _tdm_output_cb_create_handler, data); + if (data->display) tdm_display_deinit(data->display); @@ -888,6 +1271,7 @@ main(int argc, char *argv[]) LIST_INITHEAD(&data->output_list); LIST_INITHEAD(&data->pp_list); LIST_INITHEAD(&data->capture_list); + LIST_INITHEAD(&data->voutput_list); /* init value */ data->bflags = TBM_BO_SCANOUT; @@ -905,6 +1289,12 @@ main(int argc, char *argv[]) goto done; } + if (data->do_voutput) { + printf("support virtual output - server\n"); + ret = tdm_display_add_output_create_handler(data->display, _tdm_output_cb_create_handler, data); + TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE); + } + run_test(data); done: -- 2.7.4 From 0941cfe10233193d1ba97b321fadc7badd93eaff Mon Sep 17 00:00:00 2001 From: Junkyeong Kim Date: Fri, 2 Nov 2018 18:17:50 +0900 Subject: [PATCH 12/16] vblank: fix list add error Change-Id: I1e0545f47760235bb90e8ff4428747891d63c2cc Signed-off-by: Junkyeong Kim --- src/tdm_vblank.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tdm_vblank.c b/src/tdm_vblank.c index 568605a..ff7da3e 100644 --- a/src/tdm_vblank.c +++ b/src/tdm_vblank.c @@ -321,7 +321,7 @@ _tdm_vblank_insert_wait(tdm_vblank_wait_info *wait_info, struct list_head *list) if (found) LIST_ADD(&wait_info->link, &found->link); else - LIST_ADDTAIL(&wait_info->link, list->next); + LIST_ADDTAIL(&wait_info->link, list); } static void -- 2.7.4 From 1b9b01754da48fb64f9c59d43ac6bdc2beb3b371 Mon Sep 17 00:00:00 2001 From: Zhaowei Yuan Date: Tue, 6 Nov 2018 11:11:08 +0800 Subject: [PATCH 13/16] Fix a typo Signed-off-by: Zhaowei Yuan Change-Id: If08b47b04df9e0b56c69039e38e535c0caa03ebf --- src/tdm_vblank.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tdm_vblank.c b/src/tdm_vblank.c index ff7da3e..977e402 100644 --- a/src/tdm_vblank.c +++ b/src/tdm_vblank.c @@ -1575,8 +1575,8 @@ tdm_vblank_wait(tdm_vblank *vblank, unsigned int req_sec, unsigned int req_usec, /* 1) if fps != factor of vrefresh, SW timer * 2) if fps == factor of vrefresh && dpms == off, SW timer (Fake HW vblank) - * 2) if fps == factor of vrefresh && dpms == on && offset == 0, HW vblank - * 3) if fps == factor of vrefresh && dpms == on && offset != 0, HW vblank + SW timer + * 3) if fps == factor of vrefresh && dpms == on && offset == 0, HW vblank + * 4) if fps == factor of vrefresh && dpms == on && offset != 0, HW vblank + SW timer * In case of 1), we really don't need to align with HW vblank. */ if (private_vblank->vrefresh % fps) -- 2.7.4 From 790d334f04dd00d23309140580cf82add7ce4f5a Mon Sep 17 00:00:00 2001 From: Junkyeong Kim Date: Wed, 7 Nov 2018 14:26:40 +0900 Subject: [PATCH 14/16] virtual: default unload virtual module Change-Id: Icffbeda1b9f004b4df3f1d58ced94dc2a3d2d38b Signed-off-by: Junkyeong Kim --- haltests/src/tc_tdm_client.cpp | 94 ++++++++++++++++++++++++++++++++++++++++++ src/tdm.c | 12 ++++-- src/tdm_config.h | 6 +++ 3 files changed, 108 insertions(+), 4 deletions(-) diff --git a/haltests/src/tc_tdm_client.cpp b/haltests/src/tc_tdm_client.cpp index 504aaee..282d8ab 100644 --- a/haltests/src/tc_tdm_client.cpp +++ b/haltests/src/tc_tdm_client.cpp @@ -1419,8 +1419,15 @@ TEST_P(TDMClient, ClientVblankIsWaitingNullObject) TEST_P(TDMClient, ClientCreateVOutput) { tdm_error ret; + int virtual_conf = 0; const char name[TDM_NAME_LEN] = "Virtual Output"; + virtual_conf = tdm_config_get_int(TDM_CONFIG_KEY_GENERAL_VIRTUAL_OUTPUT, 0); + if (virtual_conf == 0) { + ASSERT_EQ(virtual_conf, 0); + return; + } + ASSERT_EQ(PrepareClient(), true); voutput = tdm_client_create_voutput(client, name, &ret); @@ -1531,8 +1538,12 @@ void TDMVirtualOutput::TearDownTestCase(void) bool TDMVirtualOutput::PrepareVOutput(void) { tdm_error ret; + int virtual_conf = 0; const char name[TDM_NAME_LEN] = "Virtual Output"; + virtual_conf = tdm_config_get_int(TDM_CONFIG_KEY_GENERAL_VIRTUAL_OUTPUT, 0); + if (virtual_conf == 0) return true; + client = tdm_client_create(&ret); TDM_UT_RETURN_FALSE_IF_FAIL(ret == TDM_ERROR_NONE); TDM_UT_RETURN_FALSE_IF_FAIL(client != NULL); @@ -1575,7 +1586,13 @@ TEST_F(TDMVirtualOutput, SetAvailableModes) tdm_error ret; tdm_client_output_mode modes[this->MODE_COUNT]; int count = this->MODE_COUNT; + int virtual_conf; + if (this->voutput == NULL) { + virtual_conf = tdm_config_get_int(TDM_CONFIG_KEY_GENERAL_VIRTUAL_OUTPUT, 0); + ASSERT_EQ(virtual_conf, 0); + return; + } _tc_tdm_client_virutual_make_available_mode(modes, count); ret = tdm_client_voutput_set_available_modes(this->voutput, modes, count); @@ -1587,6 +1604,13 @@ TEST_F(TDMVirtualOutput, FailTestSetAvailableModes) tdm_error ret; tdm_client_output_mode modes[this->MODE_COUNT]; int count = this->MODE_COUNT; + int virtual_conf; + + if (this->voutput == NULL) { + virtual_conf = tdm_config_get_int(TDM_CONFIG_KEY_GENERAL_VIRTUAL_OUTPUT, 0); + ASSERT_EQ(virtual_conf, 0); + return; + } ret = tdm_client_voutput_set_available_modes(NULL, modes, count); ASSERT_EQ(ret, TDM_ERROR_INVALID_PARAMETER); @@ -1599,6 +1623,13 @@ TEST_F(TDMVirtualOutput, SetPhysicalSize) { tdm_error ret; unsigned int mmWidth = 1234, mmHeight = 1234; + int virtual_conf; + + if (this->voutput == NULL) { + virtual_conf = tdm_config_get_int(TDM_CONFIG_KEY_GENERAL_VIRTUAL_OUTPUT, 0); + ASSERT_EQ(virtual_conf, 0); + return; + } ret = tdm_client_voutput_set_physical_size(this->voutput, mmWidth, mmHeight); ASSERT_EQ(ret, TDM_ERROR_NONE); @@ -1608,6 +1639,13 @@ TEST_F(TDMVirtualOutput, FailTestSetPhysicalSize) { tdm_error ret; unsigned int invalid_mmWidth = 0, invalid_mmHeight = 0; + int virtual_conf; + + if (this->voutput == NULL) { + virtual_conf = tdm_config_get_int(TDM_CONFIG_KEY_GENERAL_VIRTUAL_OUTPUT, 0); + ASSERT_EQ(virtual_conf, 0); + return; + } ret = tdm_client_voutput_set_physical_size(this->voutput, invalid_mmWidth, invalid_mmHeight); ASSERT_EQ(ret, TDM_ERROR_INVALID_PARAMETER); @@ -1625,6 +1663,13 @@ TEST_F(TDMVirtualOutput, AddCommitHandler) { tdm_error ret; int flag_callback_called = 0; + int virtual_conf; + + if (this->voutput == NULL) { + virtual_conf = tdm_config_get_int(TDM_CONFIG_KEY_GENERAL_VIRTUAL_OUTPUT, 0); + ASSERT_EQ(virtual_conf, 0); + return; + } ret = tdm_client_voutput_add_commit_handler(this->voutput, _tc_tdm_client_voutput_commit_handler, @@ -1640,6 +1685,13 @@ TEST_F(TDMVirtualOutput, AddCommitHandler) TEST_F(TDMVirtualOutput, CommitDone) { tdm_error ret; + int virtual_conf; + + if (this->voutput == NULL) { + virtual_conf = tdm_config_get_int(TDM_CONFIG_KEY_GENERAL_VIRTUAL_OUTPUT, 0); + ASSERT_EQ(virtual_conf, 0); + return; + } ret = tdm_client_voutput_commit_done(this->voutput); ASSERT_EQ(ret, TDM_ERROR_NONE); @@ -1649,6 +1701,13 @@ TEST_F(TDMVirtualOutput, GetClientOutput) { tdm_error ret; tdm_client_output *output; + int virtual_conf; + + if (this->voutput == NULL) { + virtual_conf = tdm_config_get_int(TDM_CONFIG_KEY_GENERAL_VIRTUAL_OUTPUT, 0); + ASSERT_EQ(virtual_conf, 0); + return; + } output = tdm_client_voutput_get_client_output(this->voutput, &ret); ASSERT_EQ(ret, TDM_ERROR_NONE); @@ -1661,6 +1720,13 @@ TEST_F(TDMVirtualOutput, Connect) unsigned int mmWidth = 300, mmHeight = 150; tdm_client_output_mode modes[this->MODE_COUNT]; int count = this->MODE_COUNT; + int virtual_conf; + + if (this->voutput == NULL) { + virtual_conf = tdm_config_get_int(TDM_CONFIG_KEY_GENERAL_VIRTUAL_OUTPUT, 0); + ASSERT_EQ(virtual_conf, 0); + return; + } ret = tdm_client_voutput_set_physical_size(this->voutput, mmWidth, mmHeight); ASSERT_EQ(ret, TDM_ERROR_NONE); @@ -1678,6 +1744,13 @@ TEST_F(TDMVirtualOutput, Connect) TEST_F(TDMVirtualOutput, Disconnect) { tdm_error ret; + int virtual_conf; + + if (this->voutput == NULL) { + virtual_conf = tdm_config_get_int(TDM_CONFIG_KEY_GENERAL_VIRTUAL_OUTPUT, 0); + ASSERT_EQ(virtual_conf, 0); + return; + } // TDM_UT_WAIT("check & press"); @@ -1694,6 +1767,13 @@ TEST_F(TDMVirtualOutput, SetMode) unsigned int mmWidth = 300, mmHeight = 150; tdm_client_output_mode modes[this->MODE_COUNT]; int count = this->MODE_COUNT; + int virtual_conf; + + if (this->voutput == NULL) { + virtual_conf = tdm_config_get_int(TDM_CONFIG_KEY_GENERAL_VIRTUAL_OUTPUT, 0); + ASSERT_EQ(virtual_conf, 0); + return; + } ret = tdm_client_voutput_set_physical_size(this->voutput, mmWidth, mmHeight); ASSERT_EQ(ret, TDM_ERROR_NONE); @@ -1723,6 +1803,13 @@ TEST_F(TDMVirtualOutput, SetModeNullObject) unsigned int mmWidth = 300, mmHeight = 150; tdm_client_output_mode modes[this->MODE_COUNT]; int count = this->MODE_COUNT; + int virtual_conf; + + if (this->voutput == NULL) { + virtual_conf = tdm_config_get_int(TDM_CONFIG_KEY_GENERAL_VIRTUAL_OUTPUT, 0); + ASSERT_EQ(virtual_conf, 0); + return; + } ret = tdm_client_voutput_set_physical_size(this->voutput, mmWidth, mmHeight); ASSERT_EQ(ret, TDM_ERROR_NONE); @@ -1750,6 +1837,13 @@ TEST_F(TDMVirtualOutput, SetModeInvalidIndex) unsigned int mmWidth = 300, mmHeight = 150; tdm_client_output_mode modes[this->MODE_COUNT]; int count = this->MODE_COUNT; + int virtual_conf; + + if (this->voutput == NULL) { + virtual_conf = tdm_config_get_int(TDM_CONFIG_KEY_GENERAL_VIRTUAL_OUTPUT, 0); + ASSERT_EQ(virtual_conf, 0); + return; + } ret = tdm_client_voutput_set_physical_size(this->voutput, mmWidth, mmHeight); ASSERT_EQ(ret, TDM_ERROR_NONE); diff --git a/src/tdm.c b/src/tdm.c index e6866cc..6208859 100644 --- a/src/tdm.c +++ b/src/tdm.c @@ -759,6 +759,7 @@ _tdm_display_setup(tdm_private_display *private_display) tdm_private_module *private_module = NULL; tdm_error ret = TDM_ERROR_NONE; int output_count = 0; + int virtual = 0; if (private_display->pp_module) { ret = _tdm_display_update_caps_pp(private_display->pp_module, @@ -818,10 +819,13 @@ _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); + virtual = tdm_config_get_int(TDM_CONFIG_KEY_GENERAL_VIRTUAL_OUTPUT, 0); + if (virtual) { + 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; diff --git a/src/tdm_config.h b/src/tdm_config.h index 73eeb9f..a9ed0fd 100644 --- a/src/tdm_config.h +++ b/src/tdm_config.h @@ -114,6 +114,12 @@ tdm_config_set_string(const char *key, const char *value); */ #define TDM_CONFIG_KEY_GENERAL_COMMIT_PER_VBLANK "general:commit_per_vblank" +/* enable the tdm virtual output module. [0(disable), 1(enable)] + * default: 0 + * ex) 1 + */ +#define TDM_CONFIG_KEY_GENERAL_VIRTUAL_OUTPUT "general:virtual_output" + /*** debug keys **************************************************************/ -- 2.7.4 From 88a633c7b0681b0e66e9bbce50f0ca040f5da709 Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Wed, 7 Nov 2018 14:17:53 +0900 Subject: [PATCH 15/16] Package version up to 2.4.0 Change-Id: If507be05cfc0f18d9f820e10704b0d7c30e0d14e --- packaging/libtdm.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/libtdm.spec b/packaging/libtdm.spec index e9a5468..5bc0659 100644 --- a/packaging/libtdm.spec +++ b/packaging/libtdm.spec @@ -2,7 +2,7 @@ %define HALTESTS_GCOV 0 Name: libtdm -Version: 2.3.1 +Version: 2.4.0 Release: 0 Summary: User Library of Tizen Display Manager Group: Development/Libraries -- 2.7.4 From c1f7ee3070a2cf11d5eaaede1312ef13a0f036cf Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Fri, 9 Nov 2018 19:21:35 +0900 Subject: [PATCH 16/16] hwc: modify the some symbol change tdm_hwc_accept_changes to tdm_hwc_accept_validation add the prefix TDM_HWC_ instead TDM_ Change-Id: I909695e485e1bfab32eb290ce6a8450b301862c4 --- haltests/src/tc_tdm_hwc.cpp | 16 ++++++++-------- haltests/src/tc_tdm_hwc_window.cpp | 10 +++++----- include/tdm.h | 10 +++++----- include/tdm_backend.h | 10 +++++----- include/tdm_types.h | 28 ++++++++++++++-------------- src/tdm_hwc.c | 4 ++-- src/tdm_hwc_window.c | 4 ++-- 7 files changed, 41 insertions(+), 41 deletions(-) diff --git a/haltests/src/tc_tdm_hwc.cpp b/haltests/src/tc_tdm_hwc.cpp index 4df9e54..541414f 100644 --- a/haltests/src/tc_tdm_hwc.cpp +++ b/haltests/src/tc_tdm_hwc.cpp @@ -378,12 +378,12 @@ TEST_P(TDMHwc, GetChangedCompositionTypesFailNull) } } -/* tdm_error tdm_hwc_accept_changes() */ +/* tdm_error tdm_hwc_accept_validation() */ TEST_P(TDMHwc, AcceptChangesFailNull) { TDM_UT_SKIP_FLAG(has_outputs); - error = tdm_hwc_accept_changes(NULL); + error = tdm_hwc_accept_validation(NULL); ASSERT_NE(TDM_ERROR_NONE, error); } @@ -395,10 +395,10 @@ TEST_P(TDMHwc, AcceptChangesFailNoHwc) for (int o = 0; o < output_count; o++) { hwc = tdm_output_get_hwc(outputs[o], &error); if (hwc) { - error = tdm_hwc_accept_changes(hwc); + error = tdm_hwc_accept_validation(hwc); ASSERT_NE(TDM_ERROR_NONE, error); } else { - error = tdm_hwc_accept_changes(hwc); + error = tdm_hwc_accept_validation(hwc); ASSERT_NE(TDM_ERROR_NONE, error); } } @@ -422,7 +422,7 @@ TEST_P(TDMHwc, AcceptChangesSuccessful) for (int w = 0; w < HWC_WIN_NUM; w++) { hwc_wnds[w] = tdm_hwc_create_window(hwc, &error); ASSERT_EQ(TDM_ERROR_NONE, error); - error = tdm_hwc_window_set_composition_type(hwc_wnds[w], TDM_COMPOSITION_DEVICE); + error = tdm_hwc_window_set_composition_type(hwc_wnds[w], TDM_HWC_WIN_COMPOSITION_DEVICE); ASSERT_EQ(TDM_ERROR_NONE, error); } @@ -437,7 +437,7 @@ TEST_P(TDMHwc, AcceptChangesSuccessful) ASSERT_EQ(TDM_ERROR_NONE, error); ASSERT_EQ(get_num, num_types); - error = tdm_hwc_accept_changes(hwc); + error = tdm_hwc_accept_validation(hwc); ASSERT_EQ(TDM_ERROR_NONE, error); free(composition_types); @@ -479,7 +479,7 @@ TEST_P(TDMHwc, CommitSuccessful) for (int w = 0; w < HWC_WIN_NUM; w++) { hwc_wnds[w] = tdm_hwc_create_window(hwc, &error); ASSERT_EQ(TDM_ERROR_NONE, error); - error = tdm_hwc_window_set_composition_type(hwc_wnds[w], TDM_COMPOSITION_DEVICE); + error = tdm_hwc_window_set_composition_type(hwc_wnds[w], TDM_HWC_WIN_COMPOSITION_DEVICE); ASSERT_EQ(TDM_ERROR_NONE, error); } @@ -494,7 +494,7 @@ TEST_P(TDMHwc, CommitSuccessful) ASSERT_EQ(TDM_ERROR_NONE, error); ASSERT_EQ(get_num, num_types); - error = tdm_hwc_accept_changes(hwc); + error = tdm_hwc_accept_validation(hwc); ASSERT_EQ(TDM_ERROR_NONE, error); free(composition_types); diff --git a/haltests/src/tc_tdm_hwc_window.cpp b/haltests/src/tc_tdm_hwc_window.cpp index 1e704a7..3f91ace 100644 --- a/haltests/src/tc_tdm_hwc_window.cpp +++ b/haltests/src/tc_tdm_hwc_window.cpp @@ -150,7 +150,7 @@ TEST_P(TDMHwcWindow, SetCompositionTypeFailNull) tdm_error error = TDM_ERROR_NONE; - error = tdm_hwc_window_set_composition_type(NULL, TDM_COMPOSITION_DEVICE); + error = tdm_hwc_window_set_composition_type(NULL, TDM_HWC_WIN_COMPOSITION_DEVICE); ASSERT_NE(TDM_ERROR_NONE, error); } @@ -167,7 +167,7 @@ TEST_P(TDMHwcWindow, SetCompositionTypeFailInvalieCompositionType) if (hwc) { hwc_win = tdm_hwc_create_window(hwc, &error); ASSERT_NE(NULL, hwc_win); - error = tdm_hwc_window_set_composition_type(hwc_win, tdm_hwc_window_composition(TDM_COMPOSITION_NONE - 1)); + error = tdm_hwc_window_set_composition_type(hwc_win, tdm_hwc_window_composition(TDM_HWC_WIN_COMPOSITION_NONE - 1)); ASSERT_NE(TDM_ERROR_NONE, error); tdm_hwc_window_destroy(hwc_win); } @@ -192,11 +192,11 @@ TEST_P(TDMHwcWindow, SetCompositionTypeSuccessful) } for (w = 0; w < HWC_WIN_NUM; w++) { - error = tdm_hwc_window_set_composition_type(hwc_wins[w], TDM_COMPOSITION_DEVICE); + error = tdm_hwc_window_set_composition_type(hwc_wins[w], TDM_HWC_WIN_COMPOSITION_DEVICE); ASSERT_EQ(TDM_ERROR_NONE, error); - error = tdm_hwc_window_set_composition_type(hwc_wins[w], TDM_COMPOSITION_CLIENT); + error = tdm_hwc_window_set_composition_type(hwc_wins[w], TDM_HWC_WIN_COMPOSITION_CLIENT); ASSERT_EQ(TDM_ERROR_NONE, error); - error = tdm_hwc_window_set_composition_type(hwc_wins[w], TDM_COMPOSITION_CURSOR); + error = tdm_hwc_window_set_composition_type(hwc_wins[w], TDM_HWC_WIN_COMPOSITION_CURSOR); ASSERT_EQ(TDM_ERROR_NONE, error); } diff --git a/include/tdm.h b/include/tdm.h index 169fd7c..538592b 100644 --- a/include/tdm.h +++ b/include/tdm.h @@ -943,7 +943,7 @@ tdm_hwc_get_client_target_buffer_queue(tdm_hwc *hwc, tdm_error *error); * @brief Set the client(relative to the TDM) target buffer * @details This function lets the backend know the target buffer. * The target buffer contains the result of the gl composition with the - * tdm_hwc_windows which marked as TDM_COMPOSITION_CLIENT. + * tdm_hwc_windows which marked as TDM_HWC_WIN_COMPOSITION_CLIENT. * @param[in] hwc A output hwc * @param[in] target_buffer The new target buffer * @param[in] damage The buffer damage region @@ -979,7 +979,7 @@ tdm_hwc_validate(tdm_hwc *hwc, tdm_hwc_window **composited_wnds, uint32_t num_wn * @details Retrieves the windows for which the backend requires a different * composition types that had been set prior to the last call to tdm_hwc_validate(). * The client will either update its state with these types and call - * tdm_hwc_accept_changes, or will set new types and attempt to validate the + * tdm_hwc_accept_validation, or will set new types and attempt to validate the * display again. The number of elements returned must be the same as the * value returned in num_types from the last call to tdm_hwc_validate(). * @param[in] hwc A hwc object @@ -997,15 +997,15 @@ tdm_hwc_get_changed_composition_types(tdm_hwc *hwc, uint32_t *num_elements, tdm_hwc_window_composition *composition_types); /** - * @brief Accepts the changes required by the backend - * @details Accepts the changes required by the backend from the previous + * @brief Accepts the validation required by the backend + * @details Accepts the validation required by the backend from the previous * tdm_hwc_validate() and tdm_hwc_get_chaged_composition_types(). * @param[in] hwc A hwc object * @return #TDM_ERROR_NONE if success. Otherwise, error value. * @since 2.0.0 */ tdm_error -tdm_hwc_accept_changes(tdm_hwc *hwc); +tdm_hwc_accept_validation(tdm_hwc *hwc); /** * @brief Commit changes for a hwc diff --git a/include/tdm_backend.h b/include/tdm_backend.h index 675d9c4..2de78c0 100644 --- a/include/tdm_backend.h +++ b/include/tdm_backend.h @@ -836,7 +836,7 @@ typedef struct _tdm_func_hwc { * @brief Set the client(relative to the TDM) target buffer * @details This function lets the backend know the target buffer. * The target buffer contains the result of the gl composition with the - * tdm_hwc_windows which marked as TDM_COMPOSITION_CLIENT. + * tdm_hwc_windows which marked as TDM_HWC_WIN_COMPOSITION_CLIENT. * @param[in] hwc A hwc object * @param[in] target_buffer The new target buffer * @param[in] damage The buffer damage region @@ -872,7 +872,7 @@ typedef struct _tdm_func_hwc { * @details Retrieves the windows for which the backend requires a different * composition types that had been set prior to the last call to tdm_hwc_validate(). * The client will either update its state with these types and call - * tdm_hwc_accept_changes, or will set new types and attempt to validate the + * tdm_hwc_accept_validation, or will set new types and attempt to validate the * display again. The number of elements returned must be the same as the * value returned in num_types from the last call to tdm_hwc_validate(). * @param[in] hwc A hwc object @@ -887,14 +887,14 @@ typedef struct _tdm_func_hwc { tdm_hwc_window **hwc_window, tdm_hwc_window_composition *composition_types); /** - * @brief Accepts the changes required by the backend - * @details Accepts the changes required by the backend from the previous + * @brief Accepts the validation required by the backend + * @details Accepts the validation required by the backend from the previous * tdm_hwc_validate() and tdm_hwc_get_chaged_composition_types(). * @param[in] hwc A hwc object * @return #TDM_ERROR_NONE if success. Otherwise, error value. * @since 2.0.0 */ - tdm_error (*hwc_accept_changes)(tdm_hwc *hwc); + tdm_error (*hwc_accept_validation)(tdm_hwc *hwc); /** * @brief Commit changes for a hwc object diff --git a/include/tdm_types.h b/include/tdm_types.h index 24423d3..d9ab984 100644 --- a/include/tdm_types.h +++ b/include/tdm_types.h @@ -165,19 +165,19 @@ typedef enum { * * The backend ignores windows of this type. */ - TDM_COMPOSITION_NONE = 0, + TDM_HWC_WIN_COMPOSITION_NONE = 0, /** The compostion type for an window to be compsoited by the client. * * When the client sets this composition type, * the backend MUST NOT modify this composition type into other types. * - * When the backend changes TDM_COMPOSITION_DEVICE or TDM_COMPOSITION_CURSOR + * When the backend changes TDM_HWC_WIN_COMPOSITION_DEVICE or TDM_HWC_WIN_COMPOSITION_CURSOR * to this composition type at the time of tdm_hwc_validate and * tdm_hwc_get_changed_composition_types, * the client has to composite this window with gl or other drawing operations. */ - TDM_COMPOSITION_CLIENT = 1, + TDM_HWC_WIN_COMPOSITION_CLIENT = 1, /** The compostion type for an window to be set to the hw overlay. * @@ -186,40 +186,40 @@ typedef enum { * Nomally, the backend leave it if the backend sets the window to the hw overlay. * If the backend does not set the window to the hw overlay at the time of * tdm_hwc_validate, the backend changes the composition type of the window into - * TDM_COMPOSITION_CLIENT. + * TDM_HWC_WIN_COMPOSITION_CLIENT. */ - TDM_COMPOSITION_DEVICE = 3, + TDM_HWC_WIN_COMPOSITION_DEVICE = 3, /** The compostion type for an window to be set to the cursor hw overlay. * * The client sets this composition type to the cursor window before requesting * the tdm_hwc_validate. * If the backend does not support the cursor hw overlay, the backend can change - * the comopsition type into the TDM_COMPOSITION_CLIENT. + * the comopsition type into the TDM_HWC_WIN_COMPOSITION_CLIENT. */ - TDM_COMPOSITION_CURSOR = 4, + TDM_HWC_WIN_COMPOSITION_CURSOR = 4, /** The compostion type for an window to be set to the video hw overlay. * * The client sets this composition type to the video window before requesting * the tdm_hwc_validate. * If the backend does not support the video hw overlay, the backend can change - * the comopsition type into the TDM_COMPOSITION_CLIENT. + * the comopsition type into the TDM_HWC_WIN_COMPOSITION_CLIENT. * Normally, this VIDEO window displays under the primary hw overlayer of the output. */ - TDM_COMPOSITION_VIDEO = 5, + TDM_HWC_WIN_COMPOSITION_VIDEO = 5, } tdm_hwc_window_composition; typedef enum { - TDM_CONSTRAINT_NONE = 0, + TDM_HWC_WIN_CONSTRAINT_NONE = 0, /** If the client needs to render to a specific buffer for compositing - * with TDM_COMPOSITION_DEVICE, the backend needs to set - * TDM_CONSTRAINT_BUFFER_QUEUE to hwc_window until the hwc_window is not - * TDM_COMPOSITION_DEVICE. The client gets the tbm_surface_queue_h through + * with TDM_HWC_WIN_COMPOSITION_DEVICE, the backend needs to set + * TDM_HWC_WIN_CONSTRAINT_BUFFER_QUEUE to hwc_window until the hwc_window is not + * TDM_HWC_WIN_COMPOSITION_DEVICE. The client gets the tbm_surface_queue_h through * the tdm_hwc_window_aquire_buffer_queue. It will render the frames on * the buffers which gets from the tbm_surface_queue_h. */ - TDM_CONSTRAINT_BUFFER_QUEUE = (1 << 0), + TDM_HWC_WIN_CONSTRAINT_BUFFER_QUEUE = (1 << 0), } tdm_hwc_window_constraint; /** diff --git a/src/tdm_hwc.c b/src/tdm_hwc.c index 49d4ca9..013eb34 100644 --- a/src/tdm_hwc.c +++ b/src/tdm_hwc.c @@ -452,7 +452,7 @@ tdm_hwc_get_changed_composition_types(tdm_hwc *hwc, uint32_t *num_elements, } EXTERN tdm_error -tdm_hwc_accept_changes(tdm_hwc *hwc) +tdm_hwc_accept_validation(tdm_hwc *hwc) { tdm_private_module *private_module; tdm_func_hwc *func_hwc = NULL; @@ -472,7 +472,7 @@ tdm_hwc_accept_changes(tdm_hwc *hwc) /* LCOV_EXCL_STOP */ } - ret = func_hwc->hwc_accept_changes(private_hwc->hwc_backend); + ret = func_hwc->hwc_accept_validation(private_hwc->hwc_backend); _pthread_mutex_unlock(&private_display->lock); diff --git a/src/tdm_hwc_window.c b/src/tdm_hwc_window.c index ce04c51..9248ff5 100644 --- a/src/tdm_hwc_window.c +++ b/src/tdm_hwc_window.c @@ -260,8 +260,8 @@ tdm_hwc_window_set_composition_type(tdm_hwc_window *hwc_window, tdm_func_hwc_window *func_hwc_window = NULL; HWC_WINDOW_FUNC_ENTRY(); - TDM_RETURN_VAL_IF_FAIL(composition_type >= TDM_COMPOSITION_NONE, TDM_ERROR_INVALID_PARAMETER); - TDM_RETURN_VAL_IF_FAIL(composition_type <= TDM_COMPOSITION_VIDEO, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(composition_type >= TDM_HWC_WIN_COMPOSITION_NONE, TDM_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(composition_type <= TDM_HWC_WIN_COMPOSITION_VIDEO, TDM_ERROR_INVALID_PARAMETER); _pthread_mutex_lock(&private_display->lock); -- 2.7.4