From 85be38aed104e41311763361052364f02702d8b4 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 20 Mar 2022 22:58:36 +0100 Subject: ilmControl: Extend screenshot API with callback support Extend the screenshot API such that it permits user to add their own hooks into screenshot_done and screenshot_err callbacks. This way, the user can obtain the FD with the screenshot and process it instead of having the screenshot written into a file in /tmp directory. Make the filename optional, so the user can provide only the callbacks and skip writing the file into /tmp altogether. The library ABI is unaffected, the new functionality is added via two library functions, ilm_takeScreenshot5() and ilm_takeSurfaceScreenshot5(), which are now internally called by their original counterparts with callback hooks set to NULL. Signed-off-by: Marek Vasut [khangtb: rename the name of functions, ilm_takeAsyncScreenshot() and ilm_takeAsyncSurfaceScreenshot(). Define typedef for notification callbacks. Correct the minor things. remove the filename input from the function, make the non-blocking] Signed-off-by: Tran Ba Khang(MS/EMC31-XC) --- .../ilmCommon/include/ilm_types.h | 29 +++++ .../ilmControl/include/ilm_control.h | 35 +++++ .../ilmControl/src/ilm_control_wayland_platform.c | 145 ++++++++++++++++----- 3 files changed, 180 insertions(+), 29 deletions(-) diff --git a/ivi-layermanagement-api/ilmCommon/include/ilm_types.h b/ivi-layermanagement-api/ilmCommon/include/ilm_types.h index 98a8e62..64e30a7 100644 --- a/ivi-layermanagement-api/ilmCommon/include/ilm_types.h +++ b/ivi-layermanagement-api/ilmCommon/include/ilm_types.h @@ -287,4 +287,33 @@ typedef void(*notificationFunc)(ilmObjectType object, typedef void(*shutdownNotificationFunc)(t_ilm_shutdown_error_type error_type, int errornum, void* user_data); + +/** + * Typedef for notification callback on screenshot send done event + * @param user_data the use data, be passed when call the screenshot api + * @param fd fd for file containing image data, don't close it in callback, + * it will be closed and shouldn't be accessed any longer after the callback execution. + * @param width image width in pixels + * @param height image height in pixels + * @param stride number of bytes per pixel row + * @param format image format of type wl_shm.format + * @param timestamp timestamp in milliseconds + */ +typedef ilmErrorTypes(*screenshotDoneNotificationFunc)(void *user_data, + t_ilm_int fd, + t_ilm_uint width, + t_ilm_uint height, + t_ilm_uint stride, + t_ilm_uint format, + t_ilm_uint timestamp); + +/** + * Typedef for notification callback on screenshot send error event + * @param user_data the use data, be passed when call the screenshot api + * @param error error code + * @param message error description + */ +typedef void(*screenshotErrorNotificationFunc)(void *user_data, + t_ilm_uint error, + const char *message); #endif /* _ILM_TYPES_H_*/ diff --git a/ivi-layermanagement-api/ilmControl/include/ilm_control.h b/ivi-layermanagement-api/ilmControl/include/ilm_control.h index 71e4968..c67152a 100644 --- a/ivi-layermanagement-api/ilmControl/include/ilm_control.h +++ b/ivi-layermanagement-api/ilmControl/include/ilm_control.h @@ -365,6 +365,23 @@ ilmErrorTypes ilm_displaySetRenderOrder(t_ilm_display display, t_ilm_layer *pLay */ ilmErrorTypes ilm_takeScreenshot(t_ilm_uint screen, t_ilm_const_string filename); +/** + * \brief Take a screenshot from the current displayed layer scene with non-blocking. + * The function allows to setup callbacks when capturing the display, + * It helps to avoid a blocking, user can handle screenshot data or error in + * the callbacks. + * \param[in] screen Id of screen where screenshot should be taken + * \param[in] callback_done callback called when screenshot is acquired + * \param[in] callback_error callback called when screenshot acqusition failed + * \param[in] user_data callback user data passed in by called + * \return ILM_SUCCESS if the method call was successful + * \return ILM_FAILED if the client can not call the method on the service. + */ +ilmErrorTypes ilm_takeAsyncScreenshot(t_ilm_uint screen, + screenshotDoneNotificationFunc callback_done, + screenshotErrorNotificationFunc callback_error, + void *user_data); + /** * \brief Take a screenshot of a certain surface * The screenshot is saved as bmp file with the corresponding filename. @@ -376,6 +393,24 @@ ilmErrorTypes ilm_takeScreenshot(t_ilm_uint screen, t_ilm_const_string filename) */ ilmErrorTypes ilm_takeSurfaceScreenshot(t_ilm_const_string filename, t_ilm_surface surfaceid); +/** + * \brief Take a screenshot of a certain surface with non-blocking. + * The function allows to setup callbacks when capturing a surface, + * It helps to avoid a blocking, user can handle screenshot data or error in + * the callbacks. + * \ingroup ilmControl + * \param[in] surfaceid Identifier of the surface to take the screenshot of + * \param[in] callback_done callback called when screenshot is acquired + * \param[in] callback_error callback called when screenshot acqusition failed + * \param[in] user_data callback user data passed in by called + * \return ILM_SUCCESS if the method call was successful + * \return ILM_FAILED if the client can not call the method on the service. + */ +ilmErrorTypes ilm_takeAsyncSurfaceScreenshot(t_ilm_surface surfaceid, + screenshotDoneNotificationFunc callback_done, + screenshotErrorNotificationFunc callback_error, + void *user_data); + /** * \brief register for notification on property changes of layer * \ingroup ilmControl diff --git a/ivi-layermanagement-api/ilmControl/src/ilm_control_wayland_platform.c b/ivi-layermanagement-api/ilmControl/src/ilm_control_wayland_platform.c index b4fdfbf..97e4e6d 100644 --- a/ivi-layermanagement-api/ilmControl/src/ilm_control_wayland_platform.c +++ b/ivi-layermanagement-api/ilmControl/src/ilm_control_wayland_platform.c @@ -70,6 +70,9 @@ struct screen_context { struct screenshot_context { const char *filename; ilmErrorTypes result; + screenshotDoneNotificationFunc callback_done; + screenshotErrorNotificationFunc callback_error; + void *callback_priv; }; static inline void lock_context(struct ilm_control_context *ctx) @@ -2126,9 +2129,17 @@ static void screenshot_done(void *data, struct ivi_screenshot *ivi_screenshot, ctx_scrshot->filename = NULL; ivi_screenshot_destroy(ivi_screenshot); - if (filename == NULL) { - ctx_scrshot->result = ILM_FAILED; - fprintf(stderr, "screenshot file name not provided: %m\n"); + if (ctx_scrshot->callback_done) + if (ctx_scrshot->callback_done(ctx_scrshot->callback_priv, + fd, width, height, stride, format, timestamp) == ILM_FAILED) { + ctx_scrshot->result = ILM_FAILED; + close(fd); + return; + } + // if filename is null, free resource and return + if (!filename) { + close(fd); + free(ctx_scrshot); return; } @@ -2170,9 +2181,15 @@ static void screenshot_error(void *data, struct ivi_screenshot *ivi_screenshot, uint32_t error, const char *message) { struct screenshot_context *ctx_scrshot = data; + const char *filename = ctx_scrshot->filename; ctx_scrshot->filename = NULL; ivi_screenshot_destroy(ivi_screenshot); + if (ctx_scrshot->callback_error) + ctx_scrshot->callback_error(ctx_scrshot->callback_priv, error, message); fprintf(stderr, "screenshot failed, error 0x%x: %s\n", error, message); + // free resource + if (!filename) + free(ctx_scrshot); } static struct ivi_screenshot_listener screenshot_listener = { @@ -2180,75 +2197,145 @@ static struct ivi_screenshot_listener screenshot_listener = { screenshot_error, }; -ILM_EXPORT ilmErrorTypes -ilm_takeScreenshot(t_ilm_uint screen, t_ilm_const_string filename) +static ilmErrorTypes +ilm_takeShoot(t_ilm_uint screen, t_ilm_const_string filename, + screenshotDoneNotificationFunc callback_done, + screenshotErrorNotificationFunc callback_error, + void *user_data) { ilmErrorTypes returnValue = ILM_FAILED; struct ilm_control_context *const ctx = &ilm_context; struct screen_context *ctx_scrn = NULL; + // if filename, callback_done and callback_error are null, don't do anything, then return success + if (!filename && !callback_done && !callback_error) { + return ILM_SUCCESS; + } + lock_context(ctx); ctx_scrn = get_screen_context_by_id(&ctx->wl, (uint32_t)screen); if (ctx_scrn != NULL) { - struct screenshot_context ctx_scrshot = { - .filename = filename, - .result = ILM_FAILED, - }; + struct screenshot_context *ctx_scrshot = calloc(1, sizeof(struct screenshot_context)); - struct ivi_screenshot *scrshot = - ivi_wm_screen_screenshot(ctx_scrn->controller); + if (!ctx_scrshot) { + fprintf(stderr, "Failed to allocate memory for screenshot_context\n"); + goto exit; + } + ctx_scrshot->filename = filename; + ctx_scrshot->result = ILM_FAILED; + ctx_scrshot->callback_done = callback_done; + ctx_scrshot->callback_error = callback_error; + ctx_scrshot->callback_priv = user_data; + + struct ivi_screenshot *scrshot = ivi_wm_screen_screenshot(ctx_scrn->controller); if (scrshot) { - ivi_screenshot_add_listener(scrshot, &screenshot_listener, - &ctx_scrshot); + ivi_screenshot_add_listener(scrshot, &screenshot_listener, ctx_scrshot); + // don't need to wait if file name is empty + if (!filename) { + wl_display_flush(ctx->wl.display); + returnValue = ILM_SUCCESS; + goto exit; + } // dispatch until filename has been reset in done or error callback int ret; do { ret = wl_display_dispatch_queue(ctx->wl.display, ctx->wl.queue); - } while ((ret != -1) && ctx_scrshot.filename); + } while ((ret != -1) && ctx_scrshot->filename); - returnValue = ctx_scrshot.result; + returnValue = ctx_scrshot->result; } + free(ctx_scrshot); } +exit: unlock_context(ctx); - return returnValue; } ILM_EXPORT ilmErrorTypes -ilm_takeSurfaceScreenshot(t_ilm_const_string filename, - t_ilm_surface surfaceid) +ilm_takeAsyncScreenshot(t_ilm_uint screen, + screenshotDoneNotificationFunc callback_done, + screenshotErrorNotificationFunc callback_error, + void *user_data) +{ + return ilm_takeShoot(screen, NULL, callback_done, callback_error, user_data); +} + +ILM_EXPORT ilmErrorTypes +ilm_takeScreenshot(t_ilm_uint screen, t_ilm_const_string filename) +{ + return ilm_takeShoot(screen, filename, NULL, NULL, NULL); +} + +static ilmErrorTypes +ilm_takeSurfaceShoot(t_ilm_surface surfaceid, t_ilm_const_string filename, + screenshotDoneNotificationFunc callback_done, + screenshotErrorNotificationFunc callback_error, + void *user_data) { ilmErrorTypes returnValue = ILM_FAILED; struct ilm_control_context *const ctx = &ilm_context; + // if filename, callback_done and callback_error are null, don't do anything, then return success + if (!filename && !callback_done && !callback_error) { + return ILM_SUCCESS; + } + lock_context(ctx); if (ctx->wl.controller) { - struct screenshot_context ctx_scrshot = { - .filename = filename, - .result = ILM_FAILED, - }; + struct screenshot_context *ctx_scrshot = calloc(1, sizeof(struct screenshot_context)); - struct ivi_screenshot *scrshot = - ivi_wm_surface_screenshot(ctx->wl.controller, surfaceid); + if (!ctx_scrshot) { + fprintf(stderr, "Failed to allocate memory for screenshot_context\n"); + goto exit; + } + ctx_scrshot->filename = filename; + ctx_scrshot->result = ILM_FAILED; + ctx_scrshot->callback_done = callback_done; + ctx_scrshot->callback_error = callback_error; + ctx_scrshot->callback_priv = user_data; + + struct ivi_screenshot *scrshot = ivi_wm_surface_screenshot(ctx->wl.controller, surfaceid); if (scrshot) { - ivi_screenshot_add_listener(scrshot, &screenshot_listener, - &ctx_scrshot); + ivi_screenshot_add_listener(scrshot, &screenshot_listener, ctx_scrshot); + // don't need to wait if file name is empty + if (!filename) { + wl_display_flush(ctx->wl.display); + returnValue = ILM_SUCCESS; + goto exit; + } // dispatch until filename has been reset in done or error callback int ret; do { ret = wl_display_dispatch_queue(ctx->wl.display, ctx->wl.queue); - } while ((ret != -1) && ctx_scrshot.filename); + } while ((ret != -1) && ctx_scrshot->filename); - returnValue = ctx_scrshot.result; + returnValue = ctx_scrshot->result; } + free(ctx_scrshot); } +exit: unlock_context(ctx); - return returnValue; } +ILM_EXPORT ilmErrorTypes +ilm_takeAsyncSurfaceScreenshot(t_ilm_surface surfaceid, + screenshotDoneNotificationFunc callback_done, + screenshotErrorNotificationFunc callback_error, + void *user_data) +{ + return ilm_takeSurfaceShoot(surfaceid, NULL, callback_done, callback_error, user_data); +} + +ILM_EXPORT ilmErrorTypes +ilm_takeSurfaceScreenshot(t_ilm_const_string filename, + t_ilm_surface surfaceid) +{ + return ilm_takeSurfaceShoot(surfaceid, filename, NULL, NULL, NULL); +} + ILM_EXPORT ilmErrorTypes ilm_layerAddNotification(t_ilm_layer layer, layerNotificationFunc callback) -- cgit v1.2.1 From d65adaebc70cd36da3878c44980fcd99d24c9b6c Mon Sep 17 00:00:00 2001 From: "Tran Ba Khang(MS/EMC31-XC)" Date: Fri, 24 Feb 2023 14:08:06 +0700 Subject: tests: create tests for new screenshot functions with callbacks Two new screenshot async functions, it help to capture a screen or a surface without blocking. Write the notification test for them, make sure the done and error callback is trigged Signed-off-by: Tran Ba Khang(MS/EMC31-XC) --- .../test/ilm_control_notification_test.cpp | 60 ++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/ivi-layermanagement-api/test/ilm_control_notification_test.cpp b/ivi-layermanagement-api/test/ilm_control_notification_test.cpp index 2638a03..b918e42 100644 --- a/ivi-layermanagement-api/test/ilm_control_notification_test.cpp +++ b/ivi-layermanagement-api/test/ilm_control_notification_test.cpp @@ -33,6 +33,12 @@ extern "C" { #include "ilm_control.h" } +struct screenshot_data_t { + std::atomic fd; + std::atomic error; + ilmErrorTypes result = ILM_SUCCESS; +}; + void add_nsecs(struct timespec *tv, long nsec) { assert(nsec < 1000000000); @@ -230,6 +236,25 @@ public: pthread_cond_signal( &waiterVariable ); } + + static ilmErrorTypes ScreenshotDoneCallbackFunc(void *user_data, t_ilm_int fd, t_ilm_uint width, t_ilm_uint height, t_ilm_uint stride, t_ilm_uint format, t_ilm_uint timestamp) + { + PthreadMutexLock lock(notificationMutex); + screenshot_data_t *screenshotData = static_cast(user_data); + screenshotData->fd.store(fd); + timesCalled++; + pthread_cond_signal( &waiterVariable ); + return screenshotData->result; + } + + static void ScreenshotErrorCallbackFunc(void *user_data, t_ilm_uint error, const char *message) + { + PthreadMutexLock lock(notificationMutex); + screenshot_data_t *screenshotData = static_cast(user_data); + screenshotData->error.store(error); + timesCalled++; + pthread_cond_signal( &waiterVariable ); + } }; // Pointers where to put received values for current Test @@ -726,3 +751,38 @@ TEST_F(NotificationTest, DefaultIsNotToReceiveNotificationsSurface) // assert that we have not been notified assertNoCallbackIsCalled(); } + +TEST_F(NotificationTest, getNotificationWhenScreenshotDone) +{ + /* Call ilm_takeAsyncScreenshot with right screen id + * The ilm_takeAsyncScreenshot should return ILM_SUCCESS + * Screenshot done callback should trigged + */ + screenshot_data_t screenshotData; + screenshotData.fd.store(-1); + ASSERT_EQ(ILM_SUCCESS, ilm_takeAsyncScreenshot(0, ScreenshotDoneCallbackFunc, ScreenshotErrorCallbackFunc, &screenshotData)); + assertCallbackcalled(); + ASSERT_NE(screenshotData.fd.load(), -1); + + /* Call ilm_takeAsyncSurfaceScreenshot with right surface id + * The ilm_takeAsyncSurfaceScreenshot should return ILM_SUCCESS + * Screenshot done callback should trigged + */ + screenshotData.fd.store(-1); + ASSERT_EQ(ILM_SUCCESS, ilm_takeAsyncSurfaceScreenshot(surface, ScreenshotDoneCallbackFunc, ScreenshotErrorCallbackFunc, &screenshotData)); + assertCallbackcalled(); + ASSERT_NE(screenshotData.fd.load(), -1); +} + +TEST_F(NotificationTest, getNotificationWhenScreenshotError) +{ + /* Call ilm_takeAsyncSurfaceScreenshot with wrong surface id + * The ilm_takeAsyncSurfaceScreenshot should return ILM_SUCCESS + * Screenshot error callback should trigged + */ + screenshot_data_t screenshotData; + screenshotData.error.store(0); + ASSERT_EQ(ILM_SUCCESS, ilm_takeAsyncSurfaceScreenshot(surface + 1, ScreenshotDoneCallbackFunc, ScreenshotErrorCallbackFunc, &screenshotData)); + assertCallbackcalled(); + ASSERT_EQ(screenshotData.error.load(), 3); //IVI_SCREENSHOT_ERROR_NO_SURFACE is 3 +} -- cgit v1.2.1