From e8d7b6bc5a897a04cfadfc121e1251787632d1f0 Mon Sep 17 00:00:00 2001 From: Junkyeong Kim Date: Thu, 13 Sep 2018 19:22:44 +0900 Subject: [PATCH 01/16] haltest: change ClientVblankWaitSetFps test error condition tw2 cannot success before condition. reduce error checking condition. Change-Id: I291207363b42ea0dc2e51fca60c4dae4b609a63e Signed-off-by: Junkyeong Kim --- haltests/src/tc_tdm_client.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/haltests/src/tc_tdm_client.cpp b/haltests/src/tc_tdm_client.cpp index 10e122d..eb537ca 100644 --- a/haltests/src/tc_tdm_client.cpp +++ b/haltests/src/tc_tdm_client.cpp @@ -1150,7 +1150,7 @@ TEST_P(TDMClient, ClientVblankWaitSetFps) end = tdm_helper_get_time(); /* "+ vrefresh_interval" consider the delay of socket communication between kernel and platform */ - ASSERT_GT((end - start), (interval - vrefresh_interval)); + ASSERT_GT((end - start), (interval - vrefresh_interval * 2)); ASSERT_LT((end - start), (interval + vrefresh_interval)); } @@ -1368,4 +1368,4 @@ INSTANTIATE_TEST_CASE_P(TDMClientParams, Values(TDM_DEFAULT_MODULE)); #endif -/* LCOV_EXCL_END */ \ No newline at end of file +/* LCOV_EXCL_END */ -- 2.7.4 From c5d9687d6b76a50d13ea7229a182d193489bb802 Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Fri, 14 Sep 2018 16:46:50 +0900 Subject: [PATCH 02/16] Package version up to 2.2.1 Change-Id: If8e3ace70b17d8a76763ef4e1d1d63f6a89d4cab --- packaging/libtdm.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/libtdm.spec b/packaging/libtdm.spec index f25bd23..f956e55 100644 --- a/packaging/libtdm.spec +++ b/packaging/libtdm.spec @@ -2,7 +2,7 @@ %define HALTESTS_GCOV 0 Name: libtdm -Version: 2.2.0 +Version: 2.2.1 Release: 0 Summary: User Library of Tizen Display Manager Group: Development/Libraries -- 2.7.4 From feeaa2e26e9fc8c3e3672b58f0e3c0d9c2f83709 Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Wed, 19 Sep 2018 19:21:02 +0900 Subject: [PATCH 03/16] tdm_hwc: change the symbol names of preperation types into constraints Change-Id: Ia02711a6d927edf6785048db171929e446398020 --- include/tdm.h | 7 +++---- include/tdm_backend.h | 8 ++++---- include/tdm_types.h | 14 ++++++++------ src/tdm_hwc_window.c | 7 +++---- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/include/tdm.h b/include/tdm.h index 0b40cd5..42837ab 100644 --- a/include/tdm.h +++ b/include/tdm.h @@ -1099,14 +1099,13 @@ tdm_error tdm_hwc_window_set_property(tdm_hwc_window *hwc_window, uint32_t id, tdm_value value); /** - * @brief Get the preperation type of hwc_window + * @brief Get the constraints of hwc_window * @param[in] hwc window A hwc window object - * @param[out] preperation_types The tdm_hwc_window_preparation types + * @param[out] constraints The tdm_hwc_window_constraint types * @return #TDM_ERROR_NONE if success. Otherwise, error value. */ tdm_error -tdm_hwc_window_get_preparation_types(tdm_hwc_window *hwc_window, - int *preparation_types); +tdm_hwc_window_get_constraints(tdm_hwc_window *hwc_window, int *constraints); /** * @brief Destroy a pp object diff --git a/include/tdm_backend.h b/include/tdm_backend.h index f695562..e7d08ee 100644 --- a/include/tdm_backend.h +++ b/include/tdm_backend.h @@ -971,13 +971,13 @@ typedef struct _tdm_func_hwc_window { uint32_t id, tdm_value *value); /** - * @brief Get the preperation type of hwc_window + * @brief Get the constraints of hwc_window * @param[in] hwc window A hwc window object - * @param[out] preperation_types The tdm_hwc_window_preparation types + * @param[out] constraints The tdm_hwc_window_constraint types * @return #TDM_ERROR_NONE if success. Otherwise, error value. */ - tdm_error (*hwc_window_get_preparation_types)(tdm_hwc_window *hwc_window, - int *preperation_types); + tdm_error (*hwc_window_get_constraints)(tdm_hwc_window *hwc_window, + int *constraints); } tdm_func_hwc_window; /** diff --git a/include/tdm_types.h b/include/tdm_types.h index 225325f..072f466 100644 --- a/include/tdm_types.h +++ b/include/tdm_types.h @@ -206,14 +206,16 @@ typedef enum { } tdm_hwc_window_composition; typedef enum { - TDM_PREPARATION_NONE = 0, + TDM_CONSTRAINT_NONE = 0, /** If the client needs to render to a specific buffer for compositing - * with TDM_COMPOSITION_DEVICE, Set TDM_PREPARATION_BUFFER_QUEUE type to hwc_window. - * The client will render next frame on buffers of queue which got by - * tdm_hwc_window_acquire_buffer_queue. + * 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 + * the tdm_hwc_window_aquire_buffer_queue. It will render the frames on + * the buffers which gets from the tbm_surface_queue_h. */ - TDM_PREPARATION_BUFFER_QUEUE = (1 << 0), -} tdm_hwc_window_preparation; + TDM_CONSTRAINT_BUFFER_QUEUE = (1 << 0), +} tdm_hwc_window_constraint; /** * @brief The hwc window flag enumeration diff --git a/src/tdm_hwc_window.c b/src/tdm_hwc_window.c index 0066ef3..ce04c51 100644 --- a/src/tdm_hwc_window.c +++ b/src/tdm_hwc_window.c @@ -443,8 +443,7 @@ tdm_hwc_window_set_property(tdm_hwc_window *hwc_window, unsigned int id, tdm_val } EXTERN tdm_error -tdm_hwc_window_get_preparation_types(tdm_hwc_window *hwc_window, - int *preparation_types) +tdm_hwc_window_get_constraints(tdm_hwc_window *hwc_window, int *constraints) { tdm_private_module *private_module; tdm_func_hwc_window *func_hwc_window = NULL; @@ -456,13 +455,13 @@ tdm_hwc_window_get_preparation_types(tdm_hwc_window *hwc_window, private_module = private_output->private_module; func_hwc_window = &private_module->func_hwc_window; - if (!func_hwc_window->hwc_window_get_preparation_types) { + if (!func_hwc_window->hwc_window_get_constraints) { _pthread_mutex_unlock(&private_display->lock); TDM_WRN("not implemented!!"); return TDM_ERROR_NOT_IMPLEMENTED; } - ret = func_hwc_window->hwc_window_get_preparation_types(private_hwc_window->hwc_window_backend, preparation_types); + ret = func_hwc_window->hwc_window_get_constraints(private_hwc_window->hwc_window_backend, constraints); _pthread_mutex_unlock(&private_display->lock); -- 2.7.4 From 477497ef5c7d0812398b33de2bc5824638ba4a48 Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Wed, 19 Sep 2018 19:51:40 +0900 Subject: [PATCH 04/16] add temporary code to prevent the build break this code will be reverted later... Change-Id: I4b0f88519d705c64fe368d6429a4765cf0115541 --- include/tdm.h | 8 ++++++++ src/tdm_hwc_window.c | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/include/tdm.h b/include/tdm.h index 42837ab..5f14a4c 100644 --- a/include/tdm.h +++ b/include/tdm.h @@ -1107,6 +1107,14 @@ tdm_hwc_window_set_property(tdm_hwc_window *hwc_window, uint32_t id, tdm_value v tdm_error tdm_hwc_window_get_constraints(tdm_hwc_window *hwc_window, int *constraints); +// TODO : this will be removed... +#define TDM_PREPARATION_BUFFER_QUEUE 1000 +#define TDM_PREPARATION_NONE 1001 + +tdm_error +tdm_hwc_window_get_preparation_types(tdm_hwc_window *hwc_window, int *preparation_types); +// TODO + /** * @brief Destroy a pp object * @param[in] pp A pp object diff --git a/src/tdm_hwc_window.c b/src/tdm_hwc_window.c index ce04c51..71e0f01 100644 --- a/src/tdm_hwc_window.c +++ b/src/tdm_hwc_window.c @@ -467,4 +467,11 @@ tdm_hwc_window_get_constraints(tdm_hwc_window *hwc_window, int *constraints) return ret; } + +// TODO : this will be removed... +tdm_error +tdm_hwc_window_get_preparation_types(tdm_hwc_window *hwc_window, int *preparation_types) +{ + return TDM_ERROR_NONE; +} /* LCOV_EXCL_STOP */ \ No newline at end of file -- 2.7.4 From b988f674a26105301f39e9a528e47ea61956a895 Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Fri, 28 Sep 2018 14:58:04 +0900 Subject: [PATCH 05/16] hwc: fix the wrong parameter Change-Id: I087e2c0e3f5154453c60cd07c8947c74f0fd7db5 --- include/tdm_backend.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/tdm_backend.h b/include/tdm_backend.h index e7d08ee..6c5fd92 100644 --- a/include/tdm_backend.h +++ b/include/tdm_backend.h @@ -847,7 +847,7 @@ typedef struct _tdm_func_hwc { * @param[in] func A user commit handler * @return #TDM_ERROR_NONE if success. Otherwise, error value. */ - tdm_error (*hwc_set_commit_handler)(tdm_output *hwc, tdm_hwc_commit_handler func); + tdm_error (*hwc_set_commit_handler)(tdm_hwc *hwc, tdm_hwc_commit_handler func); } tdm_func_hwc; /** -- 2.7.4 From 94c9c19e4249c723efe343575d609df6a7d445dd Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Tue, 2 Oct 2018 20:12:18 +0900 Subject: [PATCH 06/16] hwc: modify the doxygen of the hwc apis Change-Id: I275d882f5ac466fd9755ccc9d4f4a1e2b89ec6d2 --- include/tdm.h | 111 ++++++++++++++++--------------------------------- include/tdm_backend.h | 112 ++++++++++++++++++-------------------------------- 2 files changed, 76 insertions(+), 147 deletions(-) diff --git a/include/tdm.h b/include/tdm.h index 5f14a4c..3f0b89d 100644 --- a/include/tdm.h +++ b/include/tdm.h @@ -856,10 +856,9 @@ tdm_hwc_get_available_properties(tdm_hwc *hwc, const tdm_prop **props, int *coun /** * @brief Get a target buffer queue - * @details Buffers from target buffer queue will receive the output of - * client composition. Window marked as TDM_COMPOSITION_CLIENT or - * TDM_COMPOSITION_DEVICE_CANDIDATE will be composited into this buffers - * prior to the call to tdm_output_commit(). + * @details The client composites the tdm_hwd_windows which have + * TDM_COMPOSITE_CLIENT types on the buffer from this target buffer queue. + * Then, the client set the buffer by calling tdm_hwc_set_client_target_buffer. * @param[in] output A output object * @param[out] error #TDM_ERROR_NONE if success. Otherwise, error value. * @return A buffer queue @@ -870,33 +869,12 @@ tdm_hwc_get_client_target_buffer_queue(tdm_hwc *hwc, tdm_error *error); /** * @brief Set the client(relative to the TDM) target buffer - * @details Sets the buffer which will receive the output of client composition. - * Window marked as TDM_COMPOSITION_CLIENT or TDM_COMPOSITION_DEVICE_CANDIDATE - * will be composited into this buffer prior to the call to tdm_output_commit(), - * and windows not marked as TDM_COMPOSITION_CLIENT and - * TDM_COMPOSITION_DEVICE_CANDIDATE should be composited with this buffer by the - * device. - * - * The buffer handle provided may be null if no windows are being composited by - * the client. This must not result in an error (unless an invalid display - * handle is also provided). - * - * The damage parameter describes a buffer damage region as defined in the - * description of tdm_hwc_window_set_buffer_damage(). - * - * List of composited hwc_windows (hwc_windows which buffers are presented on #target_buffer) - * should be passed along with #target_buffer to allow tdm to make the smooth transition - * from a DEVICE type to a CLIENT type. - * - * Should be called before tdm_output_commit() if any of the layers are marked as - * TDM_COMPOSITION_CLIENT or TDM_COMPOSITION_DEVICE_CANDIDATE. If no layers are - * so marked, then it is not necessary to call this function. It is not necessary - * to call tdm_hwc_validate() after changing the target through this function. + * @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. * @param[in] hwc A output hwc * @param[in] target_buffer The new target buffer * @param[in] damage The buffer damage region - * @param[in] composited_wnds The array of composited hwc_wnds - * @param[in] num_wnds The size of #composited_wnds array * @return #TDM_ERROR_NONE if success. Otherwise, error value. * @since 2.0.0 */ @@ -905,18 +883,19 @@ tdm_hwc_set_client_target_buffer(tdm_hwc *hwc, tbm_surface_h target_buffer, tdm_ /** * @brief Validate the output - * @details Instructs the device to inspect all of the layer state and + * @details Instructs the backend to inspect all of the hw layer state and * determine if there are any composition type changes necessary before - * presenting the output. Permitted changes are described in the definition - * of tdm_hwc_window_composition_t above. - * @param[in] output A output object - * @param[out] num_types The number of composition type changes required by - * the device; if greater than 0, the client must either set and validate new - * types, or call tdm_hwc_accept_changes() to accept the changes returned by - * tdm_hwc_get_changed_composition_types(); must be the same as the number of - * changes returned by tdm_hwc_get_changed_composition_types (see the - * declaration of that function for more information); pointer will be non-NULL - * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * presenting the hwc. + * @param[in] hwc A hwc object + * @param[in] composited_wnds the hwc window list which is visible. + * @param[in] num_wnds the number of the visible windows in the composited_wnds + * @param[out] num_types The number of composition type changes + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @remark + * The backend has to return the num_types when the assgined comopsite types of + * the tdm_hwc_windows in the composited_wnds. If the num_types is greater than + * 0, the cleint must get the changed composite types of the tdm_hwc_windows + * and change the comopsite types * @since 2.0.0 */ tdm_error @@ -925,25 +904,19 @@ tdm_hwc_validate(tdm_hwc *hwc, tdm_hwc_window **composited_wnds, uint32_t num_wn /** * @brief Get changed composition types - * @details Retrieves the windows for which the device requires a different - * composition type than had been set prior to the last call to tdm_hwc_validate(). + * @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 - * display again. - * windows and types may be NULL to retrieve the number of elements which - * will be returned. The number of elements returned must be the same as 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] output A output object - * @param[out] num_elements If windows or types were NULL, the number of layers - * and types which would have been returned; if both were non-NULL, the - * number of elements returned in layers and types, which must not exceed - * the value stored in num_elements prior to the call; pointer will be - * non-NULL - * @param[in] output A output object + * @param[in] hwc A hwc object + * @param[out] num_elements the number of hwc_windows * @param[out] windows An array of windows * @param[out] composition_types An array of composition types, each corresponding * to an element of windows * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @remark * @since 2.0.0 */ tdm_error @@ -952,17 +925,10 @@ 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 device - * @details Accepts the changes required by the device from the previous - * tdm_hwc_validate() call (which may be queried using - * tdm_hwc_get_chaged_composition_types()) and revalidates the display. This - * function is equivalent to requesting the changed types from - * tdm_hwc_get_chaged_composition_types(), setting those types on the - * corresponding windows, and then calling tdm_hwc_validate again. - * After this call it must be valid to present this display. Calling this after - * tdm_hwc_validate() returns 0 changes must succeed with TDM_ERROR_NONE, but - * should have no other effect. - * @param[in] output A output object + * @brief Accepts the changes required by the backend + * @details Accepts the changes 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 */ @@ -984,7 +950,6 @@ tdm_hwc_commit(tdm_hwc *hwc, int sync, tdm_hwc_commit_handler func, void *user_d /** * @brief Destroys the given window. - * @param[in] output A output object * @param[in] window the pointer of the window to destroy * @since 2.0.0 */ @@ -1015,7 +980,7 @@ tdm_hwc_window_release_buffer_queue(tdm_hwc_window *hwc_window, tbm_surface_queu /** * @brief Sets the desired composition type of the given window. - * @details During tdm_hwc_validate(), the device may request changes to + * @details During tdm_hwc_validate(), the backend may request changes to * the composition types of any of the layers as described in the definition * of tdm_hwc_window_composition_t above. * @param[in] hwc_window A window object @@ -1053,8 +1018,7 @@ tdm_hwc_window_set_buffer_damage(tdm_hwc_window *hwc_window, tdm_region damage); /** * @brief Set the information to a window object - * @details The information will be applied when the output object of a window - * object is committed. + * @details The information will be applied when the hwc object is committed. * @param[in] hwc_window A window object * @param[in] info The information * @return #TDM_ERROR_NONE if success. Otherwise, error value. @@ -1068,11 +1032,8 @@ tdm_hwc_window_set_info(tdm_hwc_window *hwc_window, tdm_hwc_window_info *info); * @details A TBM buffer will be applied when the output object of a layer * object is committed. * @param[in] hwc_window A window object - * @param[in] buffer A TDM buffer + * @param[in] buffer A TBM buffer * @return #TDM_ERROR_NONE if success. Otherwise, error value. - * @return #TDM_ERROR_BUSY if #hwc_window can't be updated right now, this - * can happen if #hwc_window is involved in the smooth transition from - * DEVICE to CLIENT, this shouldn't be interpreted like some critical error. * @since 2.0.0 */ tdm_error @@ -1080,9 +1041,9 @@ tdm_hwc_window_set_buffer(tdm_hwc_window *hwc_window, tbm_surface_h buffer); /** * @brief Get the property which has a given id. - * @param[in] hwc window A hwc window object + * @param[in] hwc_window A hwc window object * @param[in] id The property id - * @param[out] value The value + * @param[out] value The value of the propery id * @return #TDM_ERROR_NONE if success. Otherwise, error value. */ tdm_error @@ -1090,9 +1051,9 @@ tdm_hwc_window_get_property(tdm_hwc_window *hwc_window, uint32_t id, tdm_value * /** * @brief Set the property which has a given id. - * @param[in] hwc window A hwc window object + * @param[in] hwc_window A hwc window object * @param[in] id The property id - * @param[in] value The value + * @param[in] value The value of the propery id * @return #TDM_ERROR_NONE if success. Otherwise, error value. */ tdm_error @@ -1100,7 +1061,7 @@ tdm_hwc_window_set_property(tdm_hwc_window *hwc_window, uint32_t id, tdm_value v /** * @brief Get the constraints of hwc_window - * @param[in] hwc window A hwc window object + * @param[in] hwc_window A hwc window object * @param[out] constraints The tdm_hwc_window_constraint types * @return #TDM_ERROR_NONE if success. Otherwise, error value. */ diff --git a/include/tdm_backend.h b/include/tdm_backend.h index 6c5fd92..1ee231a 100644 --- a/include/tdm_backend.h +++ b/include/tdm_backend.h @@ -733,29 +733,12 @@ typedef struct _tdm_func_hwc { /** * @brief Set the client(relative to the TDM) target buffer - * @details Sets the buffer which will receive the hwc of client composition. - * Window marked as TDM_COMPOSITION_CLIENT or TDM_COMPOSITION_DEVICE_CANDIDATE - * will be composited into this buffer prior to the call to hwc_commit(), - * and windows not marked as TDM_COMPOSITION_CLIENT and - * TDM_COMPOSITION_DEVICE_CANDIDATE should be composited with this buffer by the - * device. - * - * The buffer handle provided may be null if no windows are being composited by - * the client. This must not result in an error (unless an invalid display - * handle is also provided). - * - * The damage parameter describes a buffer damage region as defined in the - * description of hwc_window_set_buffer_damage(). - * - * Will be called before hwc_commit() if any of the layers are marked as - * TDM_COMPOSITION_CLIENT or TDM_COMPOSITION_DEVICE_CANDIDATE. If no layers are - * so marked, then it is not necessary to call this function. It is not necessary - * to call hwc_validate() after changing the target through this function. + * @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. * @param[in] hwc A hwc object - * @param[in] target The new target buffer + * @param[in] target_buffer The new target buffer * @param[in] damage The buffer damage region - * @param[in] composited_wnds The array of composited hwc_wnds - * @param[in] num_wnds The size of #composited_wnds array * @return #TDM_ERROR_NONE if success. Otherwise, error value. * @since 2.0.0 */ @@ -765,18 +748,19 @@ typedef struct _tdm_func_hwc { /** * @brief Validate the hwc - * @details Instructs the device to inspect all of the layer state and + * @details Instructs the backend to inspect all of the hw layer state and * determine if there are any composition type changes necessary before - * presenting the hwc. Permitted changes are described in the definition - * of tdm_composition_t above. + * presenting the hwc. * @param[in] hwc A hwc object - * @param[out] num_types The number of composition type changes required by - * the device; if greater than 0, the client must either set and validate new - * types, or call hwc_accept_changes() to accept the changes returned by - * hwc_get_changed_composition_types(); must be the same as the number of - * changes returned by hwc_get_changed_composition_types (see the - * declaration of that function for more information); pointer will be non-NULL + * @param[in] composited_wnds the hwc window list which is visible. + * @param[in] num_wnds the number of the visible windows in the composited_wnds + * @param[out] num_types The number of composition type changes * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @remark + * The backend has to return the num_types when the assgined comopsite types of + * the tdm_hwc_windows in the composited_wnds. If the num_types is greater than + * 0, the cleint must get the changed composite types of the tdm_hwc_windows + * and change the comopsite types * @since 2.0.0 */ tdm_error (*hwc_validate)(tdm_hwc *hwc, tdm_hwc_window **composited_wnds, @@ -784,23 +768,17 @@ typedef struct _tdm_func_hwc { /** * @brief Get changed composition types - * @details Retrieves the windows for which the device requires a different - * composition type than had been set prior to the last call to hwc_validate(). + * @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 - * hwc_accept_changes, or will set new types and attempt to validate the - * display again. - * layers and types may be NULL to retrieve the number of elements which - * will be returned. The number of elements returned must be the same as the - * value returned in num_types from the last call to hwc_validate(). + * tdm_hwc_accept_changes, 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 - * @param[out] num_elements If windows or types were NULL, the number of layers - * and types which would have been returned; if both were non-NULL, the - * number of elements returned in layers and types, which must not exceed - * the value stored in num_elements prior to the call; pointer will be - * non-NULL + * @param[out] num_elements the number of hwc_windows * @param[out] windows An array of windows - * @param[out] composition_types An array of composition types, each - * corresponding to an element of windows + * @param[out] composition_types An array of composition types, each corresponding + * to an element of windows * @return #TDM_ERROR_NONE if success. Otherwise, error value. * @since 2.0.0 */ @@ -808,16 +786,9 @@ typedef struct _tdm_func_hwc { tdm_hwc_window **hwc_window, tdm_hwc_window_composition *composition_types); /** - * @brief Accepts the changes required by the device - * @details Accepts the changes required by the device from the previous - * hwc_validate() call (which may be queried using - * hwc_get_chaged_composition_types()) and revalidates the display. This - * function is equivalent to requesting the changed types from - * hwc_get_chaged_composition_types(), setting those types on the - * corresponding windows, and then calling hwc_validate again. - * After this call it must be valid to present this display. Calling this after - * hwc_validate() returns 0 changes must succeed with TDM_ERROR_NONE, but - * should have no other effect. + * @brief Accepts the changes required by the backend + * @details Accepts the changes 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 @@ -825,7 +796,7 @@ typedef struct _tdm_func_hwc { tdm_error (*hwc_accept_changes)(tdm_hwc *hwc); /** - * @brief Commit changes for a output object + * @brief Commit changes for a hwc object * @param[in] hwc A hwc object * @param[in] sync 0: asynchronous, 1:synchronous * @param[in] user_data The user data @@ -833,11 +804,11 @@ typedef struct _tdm_func_hwc { * @see hwc_set_commit_handler, tdm_hwc_commit_handler * @remark * When this function is called, a backend module @b SHOULD apply the all - * changes of the given output object to screen as well as the layer changes - * of this output. + * changes of the given hwc object to screen as well as the layer changes + * of this hwc. * If this function returns TDM_ERROR_NONE, a backend module @b SHOULD call * a user commit handler with the user data of this function after all - * changes of the given output object are applied. + * changes of the given hwc object are applied. */ tdm_error (*hwc_commit)(tdm_hwc *hwc, int sync, void *user_data); @@ -863,7 +834,9 @@ typedef struct _tdm_func_hwc_window { void (*hwc_window_destroy)(tdm_hwc_window *hwc_window); /** - * @brief Get a buffer queue for the window object + * @brief Acquire a buffer queue for the window object + * @details These buffers are used to composite by hardware a client content in + * the nocomp mode. * @param[in] hwc_window A window object * @param[out] error #TDM_ERROR_NONE if success. Otherwise, error value. * @return A buffer queue @@ -882,7 +855,7 @@ typedef struct _tdm_func_hwc_window { /** * @brief Sets the desired composition type of the given window. - * @details During hwc_validate(), the device may request changes to + * @details During hwc_validate(), the backend may request changes to * the composition types of any of the layers as described in the definition * of tdm_hwc_window_composition_t above. * @param[in] hwc_window A window object @@ -917,8 +890,7 @@ typedef struct _tdm_func_hwc_window { /** * @brief Set the information to a window object - * @details The information will be applied when the hwc object - * of a layer object is committed. + * @details The information will be applied when the hwc object is committed. * @param[in] hwc_window A window object * @param[in] info The geometry information * @return #TDM_ERROR_NONE if success. Otherwise, error value. @@ -940,21 +912,17 @@ typedef struct _tdm_func_hwc_window { * @details A TDM buffer will be applied when the hwc object * of a layer object is committed. * @param[in] hwc_window A window object - * @param[in] buffer A TDM buffer + * @param[in] buffer A TBM buffer * @return #TDM_ERROR_NONE if success. Otherwise, error value. - * - * Implementation should return #TDM_ERROR_BUSY if #hwc_window can't - * be updated right now, this won't be interpreted like some critical - * error. */ tdm_error (*hwc_window_set_buffer)(tdm_hwc_window *hwc_window, tbm_surface_h buffer); /** * @brief Set the property which has a given id. - * @param[in] hwc window A hwc window object + * @param[in] hwc_window A hwc window object * @param[in] id The property id - * @param[in] value The value + * @param[in] value The value of the propery id * @return #TDM_ERROR_NONE if success. Otherwise, error value. */ tdm_error (*hwc_window_set_property)(tdm_hwc_window *hwc_window, @@ -962,9 +930,9 @@ typedef struct _tdm_func_hwc_window { /** * @brief Get the property which has a given id. - * @param[in] hwc window A hwc window object + * @param[in] hwc_window A hwc window object * @param[in] id The property id - * @param[out] value The value + * @param[out] value The value of the propery id * @return #TDM_ERROR_NONE if success. Otherwise, error value. */ tdm_error (*hwc_window_get_property)(tdm_hwc_window *hwc_window, @@ -972,7 +940,7 @@ typedef struct _tdm_func_hwc_window { /** * @brief Get the constraints of hwc_window - * @param[in] hwc window A hwc window object + * @param[in] hwc_window A hwc window object * @param[out] constraints The tdm_hwc_window_constraint types * @return #TDM_ERROR_NONE if success. Otherwise, error value. */ -- 2.7.4 From b79cd65ec653675bf8fb5b398cdc4d4a2e432856 Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Thu, 4 Oct 2018 13:18:17 +0900 Subject: [PATCH 07/16] Revert "add temporary code to prevent the build break" This reverts commit 477497ef5c7d0812398b33de2bc5824638ba4a48. Change-Id: I9987d470e5f2fa51be7c6f634655057904f7dee2 --- include/tdm.h | 8 -------- src/tdm_hwc_window.c | 7 ------- 2 files changed, 15 deletions(-) diff --git a/include/tdm.h b/include/tdm.h index 3f0b89d..dc81bf8 100644 --- a/include/tdm.h +++ b/include/tdm.h @@ -1068,14 +1068,6 @@ tdm_hwc_window_set_property(tdm_hwc_window *hwc_window, uint32_t id, tdm_value v tdm_error tdm_hwc_window_get_constraints(tdm_hwc_window *hwc_window, int *constraints); -// TODO : this will be removed... -#define TDM_PREPARATION_BUFFER_QUEUE 1000 -#define TDM_PREPARATION_NONE 1001 - -tdm_error -tdm_hwc_window_get_preparation_types(tdm_hwc_window *hwc_window, int *preparation_types); -// TODO - /** * @brief Destroy a pp object * @param[in] pp A pp object diff --git a/src/tdm_hwc_window.c b/src/tdm_hwc_window.c index 71e0f01..ce04c51 100644 --- a/src/tdm_hwc_window.c +++ b/src/tdm_hwc_window.c @@ -467,11 +467,4 @@ tdm_hwc_window_get_constraints(tdm_hwc_window *hwc_window, int *constraints) return ret; } - -// TODO : this will be removed... -tdm_error -tdm_hwc_window_get_preparation_types(tdm_hwc_window *hwc_window, int *preparation_types) -{ - return TDM_ERROR_NONE; -} /* LCOV_EXCL_STOP */ \ No newline at end of file -- 2.7.4 From 3481e1535f0c47f89a742c5056bfee511b4e5be0 Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Thu, 4 Oct 2018 13:19:21 +0900 Subject: [PATCH 08/16] hwc: rearrange the api position Change-Id: I9dc2ed38b18031d09629f9322bc4883cbdf7efdd --- include/tdm.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/tdm.h b/include/tdm.h index dc81bf8..11e6173 100644 --- a/include/tdm.h +++ b/include/tdm.h @@ -1040,24 +1040,24 @@ tdm_error tdm_hwc_window_set_buffer(tdm_hwc_window *hwc_window, tbm_surface_h buffer); /** - * @brief Get the property which has a given id. - * @param[in] hwc_window A hwc window object + * @brief Set the property which has a given id. + * @param[in] hwc_window A hwc window object * @param[in] id The property id - * @param[out] value The value of the propery id + * @param[in] value The value of the propery id * @return #TDM_ERROR_NONE if success. Otherwise, error value. */ tdm_error -tdm_hwc_window_get_property(tdm_hwc_window *hwc_window, uint32_t id, tdm_value *value); +tdm_hwc_window_set_property(tdm_hwc_window *hwc_window, uint32_t id, tdm_value value); /** - * @brief Set the property which has a given id. - * @param[in] hwc_window A hwc window object + * @brief Get the property which has a given id. + * @param[in] hwc_window A hwc window object * @param[in] id The property id - * @param[in] value The value of the propery id + * @param[out] value The value of the propery id * @return #TDM_ERROR_NONE if success. Otherwise, error value. */ tdm_error -tdm_hwc_window_set_property(tdm_hwc_window *hwc_window, uint32_t id, tdm_value value); +tdm_hwc_window_get_property(tdm_hwc_window *hwc_window, uint32_t id, tdm_value *value); /** * @brief Get the constraints of hwc_window -- 2.7.4 From 21721343ed476dd14de68b65e71644ff44f4cba4 Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Thu, 4 Oct 2018 13:20:16 +0900 Subject: [PATCH 09/16] backend: remove hwc_window_get_info Change-Id: I78dcb2ec122f8b00ae8355df78187eb5dafe20a2 --- include/tdm_backend.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/include/tdm_backend.h b/include/tdm_backend.h index 1ee231a..033f803 100644 --- a/include/tdm_backend.h +++ b/include/tdm_backend.h @@ -899,15 +899,6 @@ typedef struct _tdm_func_hwc_window { tdm_hwc_window_info *info); /** - * @brief Get the geometry information to a layer object - * @param[in] layer A layer object - * @param[out] info The geometry information - * @return #TDM_ERROR_NONE if success. Otherwise, error value. - */ - tdm_error (*hwc_window_get_info)(tdm_hwc_window *hwc_window, - tdm_hwc_window_info *info); - - /** * @brief Set a TDM buffer to a window object * @details A TDM buffer will be applied when the hwc object * of a layer object is committed. -- 2.7.4 From a1058afd6c7414f47b36bdb06de6f1b00a1403ea Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Thu, 4 Oct 2018 13:28:57 +0900 Subject: [PATCH 10/16] Package version up to 2.3.0 Change-Id: I1cf990285993fde51116809aab1654d3c69ff7b3 --- packaging/libtdm.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/libtdm.spec b/packaging/libtdm.spec index f956e55..71b69cc 100644 --- a/packaging/libtdm.spec +++ b/packaging/libtdm.spec @@ -2,7 +2,7 @@ %define HALTESTS_GCOV 0 Name: libtdm -Version: 2.2.1 +Version: 2.3.0 Release: 0 Summary: User Library of Tizen Display Manager Group: Development/Libraries -- 2.7.4 From b200a7e58bbb86acf6af5e25031bb47210b985b5 Mon Sep 17 00:00:00 2001 From: SooChan Lim Date: Wed, 10 Oct 2018 14:47:01 +0900 Subject: [PATCH 11/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 12/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 13/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 14/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 15/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 16/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