diff options
author | Sehong Na <sehong.na@samsung.com> | 2014-05-31 12:47:30 +0900 |
---|---|---|
committer | Sehong Na <sehong.na@samsung.com> | 2014-05-31 12:47:30 +0900 |
commit | 04f10f139af88753993d4ee89d53d7a8ed04d648 (patch) | |
tree | 10b09a038be578c98e7497561289493218210b02 | |
download | livebox-viewer-submit/tizen_2.3/20140531.085921.tar.gz livebox-viewer-submit/tizen_2.3/20140531.085921.tar.bz2 livebox-viewer-submit/tizen_2.3/20140531.085921.zip |
Initialize Tizen 2.3submit/tizen_2.3/20140531.0859212.3a_release
61 files changed, 19082 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..e12dbc0 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,95 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +PROJECT(livebox-viewer C) + +SET(PREFIX ${CMAKE_INSTALL_PREFIX}) +SET(EXEC_PREFIX "\${prefix}") +SET(PROJECT_NAME "${PROJECT_NAME}") +SET(LIBDIR "\${exec_prefix}/lib") +SET(INCLUDEDIR "\${prefix}/include/${PROJECT_NAME}") +SET(VERSION_MAJOR 0) +SET(VERSION "${VERSION_MAJOR}.0.1") + +SET(CMAKE_SKIP_BUILD_RPATH true) + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) + +INCLUDE(FindPkgConfig) +pkg_check_modules(pkgs REQUIRED + dlog + aul + glib-2.0 + gio-2.0 + com-core + sqlite3 + db-util + livebox-service + vconf +) + +SET(BUILD_SOURCE + src/dlist.c + src/livebox.c + src/util.c + src/desc_parser.c + src/master_rpc.c + src/client.c + src/file_service.c + src/conf.c +) + +IF (X11_SUPPORT) +pkg_check_modules(pkgs_extra REQUIRED + x11 + xext +) + +SET(BUILD_SOURCE + ${BUILD_SOURCE} + src/fb.c +) +ADD_DEFINITIONS("-DHAVE_X11") +ENDIF (X11_SUPPORT) + +IF (WAYLAND_SUPPORT) +SET(BUILD_SOURCE + ${BUILD_SOURCE} + src/fb_wayland.c +) +ADD_DEFINITIONS("-DHAVE_WAYLAND") +ENDIF (WAYLAND_SUPPORT) + +FOREACH(flag ${pkgs_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +FOREACH(flag ${pkgs_extra_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fvisibility=hidden -Wall -Werror -Winline -g") + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}") + +ADD_DEFINITIONS("-DPREFIX=\"${PREFIX}\"") +ADD_DEFINITIONS("-DLOG_TAG=\"LIVEBOX_VIEWER\"") +ADD_DEFINITIONS("-DNDEBUG") +ADD_DEFINITIONS("-D_USE_ECORE_TIME_GET") +#ADD_DEFINITIONS("-DFLOG") +ADD_DEFINITIONS("-DMASTER_PKGNAME=\"data-provider-master\"") +ADD_DEFINITIONS("-DINFO_SOCKET=\"/opt/usr/share/live_magazine/.live.socket\"") +ADD_DEFINITIONS("-DCLIENT_SOCKET=\"/opt/usr/share/live_magazine/.client.socket\"") +ADD_DEFINITIONS("-DSLAVE_SOCKET=\"/opt/usr/share/live_magazine/.slave.socket\"") +ADD_DEFINITIONS("-DSERVICE_SOCKET=\"/opt/usr/share/live_magazine/.service.socket\"") +ADD_LIBRARY(${PROJECT_NAME} SHARED ${BUILD_SOURCE}) + +SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES SOVERSION ${VERSION_MAJOR}) +SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES VERSION ${VERSION}) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS} ${pkgs_extra_LDFLAGS} "-lpthread") + +CONFIGURE_FILE(${PROJECT_NAME}.pc.in ${PROJECT_NAME}.pc @ONLY) +SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${PROJECT_NAME}.pc") + +INSTALL(TARGETS ${PROJECT_NAME} DESTINATION lib) +INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc DESTINATION lib/pkgconfig) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/include/livebox.h DESTINATION include/${PROJECT_NAME}) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/LICENSE DESTINATION /usr/share/license RENAME "lib${PROJECT_NAME}") @@ -0,0 +1,206 @@ +Flora License + +Version 1.1, April, 2013 + +http://floralicense.org/license/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and +all other entities that control, are controlled by, or are +under common control with that entity. For the purposes of +this definition, "control" means (i) the power, direct or indirect, +to cause the direction or management of such entity, +whether by contract or otherwise, or (ii) ownership of fifty percent (50%) +or more of the outstanding shares, or (iii) beneficial ownership of +such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation source, +and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, +made available under the License, as indicated by a copyright notice +that is included in or attached to the work (an example is provided +in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, +that is based on (or derived from) the Work and for which the editorial +revisions, annotations, elaborations, or other modifications represent, +as a whole, an original work of authorship. For the purposes of this License, +Derivative Works shall not include works that remain separable from, +or merely link (or bind by name) to the interfaces of, the Work and +Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original +version of the Work and any modifications or additions to that Work or +Derivative Works thereof, that is intentionally submitted to Licensor +for inclusion in the Work by the copyright owner or by an individual or +Legal Entity authorized to submit on behalf of the copyright owner. +For the purposes of this definition, "submitted" means any form of +electronic, verbal, or written communication sent to the Licensor or +its representatives, including but not limited to communication on +electronic mailing lists, source code control systems, and issue +tracking systems that are managed by, or on behalf of, the Licensor +for the purpose of discussing and improving the Work, but excluding +communication that is conspicuously marked or otherwise designated +in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +"Tizen Certified Platform" shall mean a software platform that complies +with the standards set forth in the Tizen Compliance Specification +and passes the Tizen Compliance Tests as defined from time to time +by the Tizen Technical Steering Group and certified by the Tizen +Association or its designated agent. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work +solely as incorporated into a Tizen Certified Platform, where such +license applies only to those patent claims licensable by such +Contributor that are necessarily infringed by their Contribution(s) +alone or by combination of their Contribution(s) with the Work solely +as incorporated into a Tizen Certified Platform to which such +Contribution(s) was submitted. If You institute patent litigation +against any entity (including a cross-claim or counterclaim +in a lawsuit) alleging that the Work or a Contribution incorporated +within the Work constitutes direct or contributory patent infringement, +then any patent licenses granted to You under this License for that +Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof pursuant to the copyright license +above, in any medium, with or without modifications, and in Source or +Object form, provided that You meet the following conditions: + + 1. You must give any other recipients of the Work or Derivative Works + a copy of this License; and + 2. You must cause any modified files to carry prominent notices stating + that You changed the files; and + 3. You must retain, in the Source form of any Derivative Works that + You distribute, all copyright, patent, trademark, and attribution + notices from the Source form of the Work, excluding those notices + that do not pertain to any part of the Derivative Works; and + 4. If the Work includes a "NOTICE" text file as part of its distribution, + then any Derivative Works that You distribute must include a readable + copy of the attribution notices contained within such NOTICE file, + excluding those notices that do not pertain to any part of + the Derivative Works, in at least one of the following places: + within a NOTICE text file distributed as part of the Derivative Works; + within the Source form or documentation, if provided along with the + Derivative Works; or, within a display generated by the Derivative Works, + if and wherever such third-party notices normally appear. + The contents of the NOTICE file are for informational purposes only + and do not modify the License. You may add Your own attribution notices + within Derivative Works that You distribute, alongside or as an addendum + to the NOTICE text from the Work, provided that such additional attribution + notices cannot be construed as modifying the License. You may add Your own + copyright statement to Your modifications and may provide additional or + different license terms and conditions for use, reproduction, or + distribution of Your modifications, or for any such Derivative Works + as a whole, provided Your use, reproduction, and distribution of + the Work otherwise complies with the conditions stated in this License + and your own copyright statement or terms and conditions do not conflict + the conditions stated in the License including section 3. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Flora License to your work + +To apply the Flora License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets "[]" +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same "printed page" as the copyright notice for easier +identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Flora License, Version 1.1 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://floralicense.org/license/ + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/TC/testcase/Makefile b/TC/testcase/Makefile new file mode 100644 index 0000000..79b2c02 --- /dev/null +++ b/TC/testcase/Makefile @@ -0,0 +1,27 @@ +include ../config + +CC ?= gcc + +C_FILES = $(shell ls *.c) + +PKGS = $(PKG_NAME) +PKGS += glib-2.0 +PKGS += livebox-service +LDFLAGS = `pkg-config --libs $(PKGS)` +LDFLAGS += $(TET_ROOT)/lib/tet3/tcm_s.o +LDFLAGS += -L$(TET_ROOT)/lib/tet3 -ltcm_s +LDFLAGS += -L$(TET_ROOT)/lib/tet3 -lapi_s + +CFLAGS = -I. `pkg-config --cflags $(PKGS)` +CFLAGS += -I$(TET_ROOT)/inc/tet3 +CFLAGS += -Wall + +TCS := $(shell ls -1 *.c | cut -d. -f1) + +all: $(TCS) + +%: %.c + $(CC) -o $@ $< $(CFLAGS) $(LDFLAGS) + +clean: + rm -f $(TCS) diff --git a/TC/testcase/tslist b/TC/testcase/tslist new file mode 100644 index 0000000..51eca81 --- /dev/null +++ b/TC/testcase/tslist @@ -0,0 +1,2 @@ +/testcase/utc_livebox_viewer + diff --git a/TC/testcase/utc_livebox_viewer b/TC/testcase/utc_livebox_viewer Binary files differnew file mode 100755 index 0000000..1f24379 --- /dev/null +++ b/TC/testcase/utc_livebox_viewer diff --git a/TC/testcase/utc_livebox_viewer.c b/TC/testcase/utc_livebox_viewer.c new file mode 100644 index 0000000..160cb38 --- /dev/null +++ b/TC/testcase/utc_livebox_viewer.c @@ -0,0 +1,2456 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include <tet_api.h> +#include <stdlib.h> + +#include <livebox.h> +#include <livebox-service.h> +#include <livebox-errno.h> + +#define MUSIC_APP "org.tizen.music-player" +#define EMAIL_APP "org.tizen.email" +#define EMAIL_LIVEBOX EMAIL_APP ".livebox" +#define MUSIC_LIVEBOX MUSIC_APP ".livebox" +#define MUSIC_EASYBOX "org.tizen.music-player.easymode.livebox" + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +static void startup(void) +{ + int ret; + /* start of TC */ + ret = livebox_init_with_options(NULL, 1, 0.01, 1); + tet_printf("\n TC start: ret = %d", ret); +} + + +static void cleanup(void) +{ + /* end of TC */ + tet_printf("\n TC end"); + livebox_fini(); +} + +void (*tet_startup)(void) = startup; +void (*tet_cleanup)(void) = cleanup; + +static void utc_livebox_client_paused_n(void) +{ + /*! + * \note + * Unable to test negative case + */ + dts_pass("livebox_client_paused", "skip negative test"); +} + +static void utc_livebox_client_paused_p(void) +{ + int ret; + + ret = livebox_client_paused(); + dts_check_eq("livebox_client_paused", ret, LB_STATUS_SUCCESS, "Success"); +} + +static void utc_livebox_client_resumed_n(void) +{ + /*! + * \note + * Unable to test negative case + */ + dts_pass("livebox_client_resumed", "skip negative test"); +} + +static void utc_livebox_client_resumed_p(void) +{ + int ret; + + ret = livebox_client_resumed(); + dts_check_eq("livebox_client_resumed", ret, LB_STATUS_SUCCESS, "Success"); +} + +static void create_ret_cb(struct livebox *handle, int ret, void *data) +{ + dts_check_eq("livebox_add", ret, LB_STATUS_SUCCESS, "Request to add a new box"); +} + +static void utc_livebox_add_p(void) +{ + struct livebox *handle; + + handle = livebox_add(MUSIC_LIVEBOX, NULL, NULL, NULL, -1.0f, create_ret_cb, NULL); + if (!handle) { + dts_check_ne("livebox_add", handle, NULL, "handle must not be NULL"); + } +} + +static void utc_livebox_add_n(void) +{ + struct livebox *handle; + + handle = livebox_add(NULL, NULL, NULL, NULL, -1.0f, create_ret_cb, NULL); + if (handle == NULL) { + dts_check_eq("livebox_add", handle, NULL, "Handle is NULL"); + } +} + +static void create_ret_with_size_cb(struct livebox *handle, int ret, void *data) +{ + dts_check_eq("livebox_add_with_size", ret, LB_STATUS_SUCCESS, "Request to add a new box with size"); +} + +static void utc_livebox_add_with_size_n(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(NULL, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, create_ret_with_size_cb, NULL); + dts_check_eq("livebox_add_with_size", handle, NULL, "Error"); +} + +static void utc_livebox_add_with_size_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(MUSIC_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, create_ret_with_size_cb, NULL); + if (!handle) { + dts_check_ne("livebox_add_with_size", handle, NULL, "Error"); + } +} + +static void utc_livebox_del_n(void) +{ + int ret; + + ret = livebox_del(NULL, NULL, NULL); + dts_check_ne("livebox_del", ret, LB_STATUS_SUCCESS, "Failed"); +} + +static void del_ret_cb(struct livebox *handle, int ret, void *data) +{ + dts_check_eq("livebox_del", ret, LB_STATUS_SUCCESS, "Success"); +} + +static void create_cb_for_testing_del(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_del", ret, LB_STATUS_SUCCESS, "create failed"); + return; + } + + ret = livebox_del(handle, del_ret_cb, NULL); + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_del", ret, LB_STATUS_SUCCESS, "Success"); + } +} + +static void utc_livebox_del_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(MUSIC_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, create_cb_for_testing_del, NULL); + if (!handle) { + dts_check_ne("livebox_del", handle, NULL, "Failed to create a box"); + } +} + +static void utc_livebox_del_NEW_n(void) +{ + int ret; + + ret = livebox_del_NEW(NULL, LB_DELETE_PERMANENTLY, NULL, NULL); + dts_check_eq("livebox_del_NEW", ret, LB_STATUS_ERROR_INVALID, "Invalid"); +} + +static void del_NEW_ret_cb(struct livebox *handle, int ret, void *data) +{ + dts_check_eq("livebox_del_NEW", ret, LB_STATUS_SUCCESS, "Success"); +} + +static void create_cb_for_testing_del_NEW(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_del_NEW", ret, LB_STATUS_SUCCESS, "Create failed"); + return; + } + + ret = livebox_del_NEW(handle, LB_DELETE_PERMANENTLY, del_NEW_ret_cb, NULL); + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_del_NEW", ret, LB_STATUS_SUCCESS, "Success"); + } +} + +static void utc_livebox_del_NEW_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(MUSIC_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, create_cb_for_testing_del_NEW, NULL); + if (!handle) { + dts_check_ne("livebox_del_NEW", handle, NULL, "Failed to add a new box\n"); + } +} + +static int event_handler(struct livebox *handler, enum livebox_event_type event, void *data) +{ + return 0; +} + +static void utc_livebox_set_event_handler_n(void) +{ + int ret; + + ret = livebox_set_event_handler(NULL, NULL); + dts_check_eq("livebox_set_event_handler", ret, LB_STATUS_ERROR_INVALID, "Invalid"); +} + +static void utc_livebox_set_event_handler_p(void) +{ + int ret; + + ret = livebox_set_event_handler(event_handler, (void *)123); + dts_check_eq("livebox_set_event_handler", ret, LB_STATUS_ERROR_INVALID, "Invalid"); +} + +static void utc_livebox_unset_event_handler_n(void) +{ + /*! + * \note + * Unable to unset event handler + */ + dts_pass("livebox_unset_event_handler", "skip negative test"); +} + +static void utc_livebox_unset_event_handler_p(void) +{ + void *data; + + data = livebox_unset_event_handler(event_handler); + dts_check_eq("livebox_unset_event_handler", data, (void *)123, "Unset"); +} + +static int fault_handler(enum livebox_fault_type type, const char *pkgname, const char *id, const char *func_name, void *data) +{ + return 0; +} + +static void utc_livebox_set_fault_handler_n(void) +{ + int ret; + ret = livebox_set_fault_handler(NULL, NULL); + dts_check_eq("livebox_set_fault_handler", ret, LB_STATUS_ERROR_INVALID, "Invalid"); +} + +static void utc_livebox_set_fault_handler_p(void) +{ + int ret; + ret = livebox_set_fault_handler(fault_handler, (void *)123); + dts_check_eq("livebox_set_fault_handler", ret, LB_STATUS_SUCCESS, "Success"); +} + +static void utc_livebox_unset_fault_handler_n(void) +{ + /*! + * Unable to test negative case + */ + dts_pass("livebox_unset_fault_handler", "skip negative test"); +} + +static void utc_livebox_unset_fault_handler_p(void) +{ + void *data; + + data = livebox_unset_fault_handler(fault_handler); + dts_check_eq("livebox_unset_fault_handler", data, (void *)123, "Unset success"); +} + +static void utc_livebox_activate_n(void) +{ + int ret; + + ret = livebox_activate(NULL, NULL, NULL); + dts_check_eq("livebox_activate", ret, LB_STATUS_ERROR_INVALID, "Invalid"); +} + +static void activate_ret_cb(struct livebox *handle, int ret, void *data) +{ + dts_check_eq("livebox_activate", ret, LB_STATUS_SUCCESS, "Success"); +} + +static void utc_livebox_activate_p(void) +{ + int ret; + + ret = livebox_activate(MUSIC_LIVEBOX, activate_ret_cb, NULL); + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_activate", ret, LB_STATUS_SUCCESS, "Success"); + } +} + +static void resize_cb(struct livebox *handle, int ret, void *data) +{ + dts_check_eq("livebox_resize", ret, LB_STATUS_SUCCESS, "Success"); +} + +static void resize_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret == LB_STATUS_SUCCESS) { + ret = livebox_resize(handle, LB_SIZE_TYPE_2x2, resize_cb, NULL); + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_resize", ret, LB_STATUS_SUCCESS, "Success"); + } + } else { + dts_check_eq("livebox_resize", ret, LB_STATUS_SUCCESS, "resize,create,callback"); + } +} + +static void utc_livebox_resize_n(void) +{ + int ret; + ret = livebox_resize(NULL, LB_SIZE_TYPE_2x2, NULL, NULL); + dts_check_eq("livebox_resize", ret, LB_STATUS_ERROR_INVALID, "resize"); +} + +static void utc_livebox_resize_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(MUSIC_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, resize_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_resize", handle, NULL, "Resize"); + } +} + +static void utc_livebox_click_n(void) +{ + int ret; + + ret = livebox_click(NULL, 0.5f, 0.5f); + dts_check_eq("livebox_click", ret, LB_STATUS_ERROR_INVALID, "Success"); +} + +static void click_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_click", ret, LB_STATUS_SUCCESS, "click,create"); + } else { + ret = livebox_click(handle, 0.5f, 0.5f); + dts_check_eq("livebox_click", ret, LB_STATUS_SUCCESS, "click"); + } +} + +static void utc_livebox_click_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(MUSIC_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, click_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_click", handle, NULL, "click,create"); + } +} + +static void utc_livebox_set_group_n(void) +{ + int ret; + + ret = livebox_set_group(NULL, NULL, NULL, NULL, NULL); + dts_check_eq("livebox_set_group", ret, LB_STATUS_ERROR_INVALID, "Invalid"); +} + +static void set_group_cb(struct livebox *handle, int ret, void *data) +{ + dts_check_eq("livebox_set_group", ret, LB_STATUS_SUCCESS, "Success"); +} + +static void set_group_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_set_group", ret, LB_STATUS_SUCCESS, "set_group,create_cb"); + } else { + ret = livebox_set_group(handle, "my,cluster", "my,category", set_group_cb, NULL); + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_set_group", ret, LB_STATUS_SUCCESS, "set_group,request"); + } + } +} + +static void utc_livebox_set_group_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(MUSIC_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, set_group_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_set_group", handle, NULL, "create,set,group"); + } +} + +static void utc_livebox_get_group_n(void) +{ + int ret; + + ret = livebox_get_group(NULL, NULL, NULL); + dts_check_eq("livebox_get_group", ret, LB_STATUS_ERROR_INVALID, "Success"); +} + +static void get_group_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_get_group", ret, LB_STATUS_SUCCESS, "get_group,create"); + } else { + const char *cluster; + const char *category; + + ret = livebox_get_group(handle, &cluster, &category); + dts_check_eq("livebox_get_group", ret, LB_STATUS_SUCCESS, "Success"); + } +} + +static void utc_livebox_get_group_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(MUSIC_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, get_group_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_get_group", handle, NULL, "get_group,create"); + } +} + +static void utc_livebox_period_n(void) +{ + double period; + + period = livebox_period(NULL); + dts_check_eq("livebox_period", period, 0.0f, "Success"); +} + +static void period_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_period", ret, LB_STATUS_SUCCESS, "period,create"); + } else { + double period; + + period = livebox_period(handle); + dts_check_ge("livebox_period", period, 0.0f, "Success"); + } +} + +static void utc_livebox_period_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(MUSIC_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, period_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_add_with_size", handle, NULL, "livebox_add_with_size"); + } +} + +static void utc_livebox_set_period_n(void) +{ + int ret; + + ret = livebox_set_period(NULL, 20.0f, NULL, NULL); + dts_check_gt("livebox_period", ret, LB_STATUS_ERROR_INVALID, "Invalid"); +} + +static void set_period_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_period", ret, LB_STATUS_SUCCESS, "Success"); + } else { + ret = livebox_set_period(handle, 20.0f, NULL, NULL); + dts_check_eq("livebox_period", ret, LB_STATUS_SUCCESS, "Success"); + } +} + +static void utc_livebox_set_period_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(MUSIC_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, set_period_create_cb, NULL); + if (!handle) { + dts_check_eq("livebox_set_period", handle, NULL, "set_period,create"); + } +} + +static void utc_livebox_lb_type_n(void) +{ + int ret; + + ret = livebox_lb_type(NULL); + dts_check_eq("livebox_lb_type", ret, LB_TYPE_INVALID, "Success"); +} + +static void lb_type_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_lb_type", ret, LB_STATUS_SUCCESS, "lb_type,create"); + } else { + ret = livebox_lb_type(handle); + dts_check_ne("livebox_lb_type", ret, LB_TYPE_INVALID, "Success"); + } +} + +static void utc_livebox_lb_type_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(MUSIC_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, lb_type_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_lb_type", handle, NULL, "lb,type,create"); + } +} + +static void utc_livebox_is_user_n(void) +{ + int ret; + + ret = livebox_is_user(NULL); + dts_check_eq("livebox_is_user", ret, LB_STATUS_ERROR_INVALID, "Success"); +} + +static void livebox_is_user_create_cb(struct livebox *handle, int ret, void *data) +{ + return; +} + +static void utc_livebox_is_user_p(void) +{ + struct livebox *handle; + int ret; + + handle = livebox_add_with_size(MUSIC_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, livebox_is_user_create_cb, NULL); + if (handle == NULL) { + dts_check_ne("livebox_is_user", handle, NULL, "handle is NULL"); + return; + } + + ret = livebox_is_user(handle); + dts_check_eq("livebox_is_user", ret, 1, "Success"); +} + +static void utc_livebox_content_n(void) +{ + const char *content; + + content = livebox_content(NULL); + dts_check_eq("livebox_content", content, NULL, "Success"); +} + +static void content_create_cb(struct livebox *handle, int ret, void *data) +{ + const char *content; + + content = livebox_content(handle); + dts_check_ne("livebox_content", content, NULL, "Success"); +} + +static void utc_livebox_content_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(MUSIC_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, content_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_content", handle, NULL, "Create for content\n"); + } +} + +static void utc_livebox_category_title_n(void) +{ + const char *title; + title = livebox_category_title(NULL); + dts_check_eq("livebox_category_title", title, NULL, "Success"); +} + +static void category_create_cb(struct livebox *handle, int ret, void *data) +{ + const char *title; + title = livebox_category_title(handle); + dts_check_ne("livebox_category_title", title, NULL, "Success"); +} + +static void utc_livebox_category_title_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(MUSIC_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, category_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_category_title", handle, NULL, "Create for category_title"); + } +} + +static void utc_livebox_filename_n(void) +{ + const char *filename; + filename = livebox_filename(NULL); + dts_check_eq("livebox_filename", filename, NULL, "Success"); +} + +static void filename_create_cb(struct livebox *handle, int ret, void *data) +{ + const char *filename; + filename = livebox_filename(handle); + dts_check_ne("livebox_filename", filename, NULL, "Success"); +} + +static void utc_livebox_filename_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(MUSIC_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, filename_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_category_title", handle, NULL, "Create for category_title"); + } +} + +static void utc_livebox_pkgname_n(void) +{ + const char *pkgname; + pkgname = livebox_pkgname(NULL); + dts_check_eq("livebox_pkgname", pkgname, NULL, "Success"); +} + +static void utc_livebox_pkgname_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(MUSIC_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, NULL, NULL); + if (!handle) { + dts_check_ne("livebox_pkgname", handle, NULL, "Create for pkgname"); + } else { + const char *pkgname; + + pkgname = livebox_pkgname(handle); + dts_check_ne("livebox_pkgname", pkgname, NULL, "pkgname"); + } +} + +static void utc_livebox_priority_n(void) +{ + double priority; + + priority = livebox_priority(NULL); + dts_check_eq("livebox_priority", priority, 0.0f, "Success"); +} + +static void priority_create_cb(struct livebox *handle, int ret, void *data) +{ + double priority; + + priority = livebox_priority(handle); + dts_check_ge("livebox_priority", priority, 0.0f, "Success"); +} + +static void utc_livebox_priority_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(MUSIC_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, priority_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_priority", handle, NULL, "Create for priority"); + } +} + +static void utc_livebox_acquire_fb_n(void) +{ + void *data; + + data = livebox_acquire_fb(NULL); + dts_check_eq("livebox_acquire_fb", data, NULL, "Success"); +} + +static void acquire_fb_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_acquire_fb", ret, LB_STATUS_SUCCESS, "acquire_fb"); + } else { + void *data; + data = livebox_acquire_fb(handle); + dts_check_ne("livebox_acquire_fb", data, NULL, "acquire_fb"); + livebox_release_fb(data); + } +} + +static void utc_livebox_acquire_fb_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, acquire_fb_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_priority", handle, NULL, "Create for priority"); + } +} + +static void utc_livebox_release_fb_n(void) +{ + int ret; + + ret = livebox_release_fb(NULL); + dts_check_eq("livebox_release_fb", ret, LB_STATUS_ERROR_INVALID, "Invalid"); +} + +static void release_fb_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_release_fb", ret, LB_STATUS_SUCCESS, "release_fb"); + } else { + void *data; + + data = livebox_acquire_fb(handle); + + ret = livebox_release_fb(data); + dts_check_eq("livebox_release_fb", ret, LB_STATUS_SUCCESS, "release_fb"); + } +} + +static void utc_livebox_release_fb_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, release_fb_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_priority", handle, NULL, "Create for priority"); + } +} + +static void refcnt_fb_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_fb_refcnt", ret, LB_STATUS_SUCCESS, "refcnt"); + } else { + void *data; + + data = livebox_acquire_fb(handle); + + ret = livebox_fb_refcnt(data); + dts_check_ge("livebox_fb_refcnt", ret, 0, "refcnt"); + + (void)livebox_release_fb(data); + } +} + +static void utc_livebox_fb_refcnt_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, refcnt_fb_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_priority", handle, NULL, "Create for priority"); + } +} + +static void utc_livebox_fb_refcnt_n(void) +{ + int ret; + + ret = livebox_fb_refcnt(NULL); + dts_check_eq("livebox_fb_refcnt", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void acquire_pdfb_pd_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_acquire_pdfb", ret, LB_STATUS_SUCCESS, "create pd failed"); + return; + } + + data = livebox_acquire_pdfb(handle); + dts_check_ne("livebox_acquire_pdfb", data, NULL, "acquire_pdfb"); + (void)livebox_release_pdfb(data); +} + +static void acquire_pdfb_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_acquire_pdfb", ret, LB_STATUS_SUCCESS, "refcnt"); + } else { + ret = livebox_create_pd(handle, acquire_pdfb_pd_cb, NULL); + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_acquire_pdfb", ret, LB_STATUS_SUCCESS, "refcnt"); + } + } +} + +static void utc_livebox_acquire_pdfb_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, acquire_pdfb_cb, NULL); + if (!handle) { + dts_check_ne("livebox_priority", handle, NULL, "Create for priority"); + } +} + +static void utc_livebox_acquire_pdfb_n(void) +{ + void *data; + + data = livebox_acquire_pdfb(NULL); + dts_check_eq("livebox_acquire_pdfb", data, NULL, "invalid"); +} + +static void release_pdfb_pd_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_release_pdfb", ret, LB_STATUS_SUCCESS, "create pd"); + return; + } + + data = livebox_acquire_pdfb(handle); + ret = livebox_release_pdfb(data); + dts_check_eq("livebox_release_pdfb", ret, LB_STATUS_SUCCESS, "release_pdfb"); +} + +static void release_pdfb_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_acquire_pdfb", ret, LB_STATUS_SUCCESS, "refcnt"); + } else { + ret = livebox_create_pd(handle, release_pdfb_pd_cb, NULL); + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_acquire_pdfb", ret, LB_STATUS_SUCCESS, "refcnt"); + } + } +} + +static void utc_livebox_release_pdfb_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, release_pdfb_cb, NULL); + if (!handle) { + dts_check_ne("livebox_priority", handle, NULL, "Create for priority"); + } +} + +static void utc_livebox_release_pdfb_n(void) +{ + int ret; + ret = livebox_release_pdfb(NULL); + dts_check_eq("livebox_release_pdfb", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void utc_livebox_pdfb_refcnt_n(void) +{ + int ret; + ret = livebox_pdfb_refcnt(NULL); + dts_check_eq("livebox_pdfb_refcnt", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void refcnt_pdfb_pd_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_pdfb_refcnt", ret, LB_STATUS_SUCCESS, "create pd"); + return; + } + + data = livebox_acquire_pdfb(handle); + ret = livebox_pdfb_refcnt(data); + dts_check_ge("livebox_pdfb_refcnt", ret, 0, "refcnt_pdfb"); + (void)livebox_release_pdfb(data); +} + +static void refcnt_pdfb_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_pdfb_refcnt", ret, LB_STATUS_SUCCESS, "refcnt"); + } else { + ret = livebox_create_pd(handle, refcnt_pdfb_pd_cb, NULL); + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_pdfb_refcnt", ret, LB_STATUS_SUCCESS, "refcnt"); + } + } +} + +static void utc_livebox_pdfb_refcnt_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, refcnt_pdfb_cb, NULL); + if (!handle) { + dts_check_ne("livebox_pdfb_refcnt", handle, NULL, "Create for pdfb refcnt"); + } +} + +static void size_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_size", ret, LB_STATUS_SUCCESS, "size"); + } else { + int size; + size = livebox_size(handle); + dts_check_eq("livebox_size", size, LB_SIZE_TYPE_1x1, "size"); + } +} + +static void utc_livebox_size_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, size_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_size", handle, NULL, "Create for size"); + } +} + +static void utc_livebox_size_n(void) +{ + int ret; + ret = livebox_size(NULL); + dts_check_eq("livebox_size", ret, LB_STATUS_ERROR_INVALID, "Invalid"); +} + +static void utc_livebox_get_pdsize_n(void) +{ + int ret; + ret = livebox_get_pdsize(NULL, NULL, NULL); + dts_check_eq("livebox_get_pdsize", ret, LB_STATUS_ERROR_INVALID, "Invalid"); +} + +static void pdsize_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_get_pdsize", ret, LB_STATUS_SUCCESS, "pdsize"); + } else { + int w; + int h; + + ret = livebox_get_pdsize(handle, &w, &h); + dts_check_eq("livebox_get_pdsize", ret, LB_STATUS_SUCCESS, "pdsize"); + } +} + +static void utc_livebox_get_pdsize_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, pdsize_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_get_pdsize", handle, NULL, "Create for priority"); + } +} + +static void utc_livebox_get_supported_sizes_n(void) +{ + int ret; + + ret = livebox_get_supported_sizes(NULL, NULL, NULL); + dts_check_eq("livebox_get_supported_sizes", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void size_list_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_get_supported_sizes", ret, LB_STATUS_SUCCESS, "size_list"); + } else { + int cnt = 20; + int size_list[20]; + + ret = livebox_get_supported_sizes(handle, &cnt, size_list); + dts_check_eq("livebox_get_supported_sizes", ret, LB_STATUS_SUCCESS, "size_list"); + } +} + +static void utc_livebox_get_supported_sizes_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, size_list_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_get_supported_sizes", handle, NULL, "Create for supported_sizes"); + } +} + +static void utc_livebox_lbfb_bufsz_n(void) +{ + int ret; + ret = livebox_lbfb_bufsz(NULL); + dts_check_eq("livebox_lbfb_bufsz", ret, LB_STATUS_ERROR_INVALID, "Invalid"); +} + +static void lbfb_bufsz_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_lbfb_bufsz", ret, LB_STATUS_SUCCESS, "size_list"); + } else { + int bufsz; + bufsz = livebox_lbfb_bufsz(handle); + dts_check_gt("livebox_lbfb_bufsz", bufsz, 0, "lbfb_bufsz"); + } +} + +static void utc_livebox_lbfb_bufsz_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, lbfb_bufsz_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_lbfb_bufsz", handle, NULL, "Create for supported_sizes"); + } +} + +static void utc_livebox_pdfb_bufsz_n(void) +{ + int ret; + ret = livebox_pdfb_bufsz(NULL); + dts_check_eq("livebox_pdfb_bufsz", ret, LB_STATUS_ERROR_INVALID, "Invalid"); +} + +static void pdfb_bufsz_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_pdfb_bufsz", ret, LB_STATUS_SUCCESS, "size_list"); + } else { + int bufsz; + bufsz = livebox_pdfb_bufsz(handle); + dts_check_gt("livebox_pdfb_bufsz", bufsz, 0, "pdfb_bufsz"); + } +} + +static void utc_livebox_pdfb_bufsz_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, pdfb_bufsz_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_pdfb_bufsz", handle, NULL, "Create for supported_sizes"); + } +} + +static void utc_livebox_content_event_n(void) +{ + int ret; + + ret = livebox_content_event(NULL, PD_MOUSE_DOWN, 0.0f, 0.0f); + dts_check_eq("livebox_content_event", ret, LB_STATUS_ERROR_INVALID, "Invalid"); +} + +static void content_event_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_content_event", ret, LB_STATUS_SUCCESS, "content_event"); + } else { + ret = livebox_content_event(handle, PD_MOUSE_DOWN, 0.0f, 0.0f); + dts_check_eq("livebox_content_event", ret, LB_STATUS_SUCCESS, "content_event"); + } +} + +static void utc_livebox_content_event_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, content_event_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_content_event", handle, NULL, "Create for content_event"); + } +} + +static void utc_livebox_mouse_event_n(void) +{ + int ret; + ret = livebox_mouse_event(NULL, PD_MOUSE_DOWN, 0.0f, 0.0f); + dts_check_eq("livebox_mouse_event", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void mouse_event_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_content_event", ret, LB_STATUS_SUCCESS, "content_event"); + } else { + ret = livebox_content_event(handle, PD_MOUSE_DOWN, 0.0f, 0.0f); + dts_check_eq("livebox_content_event", ret, LB_STATUS_SUCCESS, "content_event"); + } +} + +static void utc_livebox_mouse_event_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, mouse_event_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_content_event", handle, NULL, "Create for content_event"); + } +} + +static void utc_livebox_access_event_n(void) +{ + int ret; + ret = livebox_access_event(NULL, ACCESS_EVENT_ACTION_DOWN, 0.0f, 0.0f, NULL, NULL); + dts_check_eq("livebox_mouse_event", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void access_event_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_access_event", ret, LB_STATUS_SUCCESS, "access_event"); + } else { + ret = livebox_access_event(handle, ACCESS_EVENT_ACTION_DOWN, 0.0f, 0.0f, NULL, NULL); + dts_check_eq("livebox_access_event", ret, LB_STATUS_SUCCESS, "access_event"); + } +} + +static void utc_livebox_access_event_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, access_event_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_access_event", handle, NULL, "Create for access_event"); + } +} + +static void utc_livebox_key_event_n(void) +{ + int ret; + ret = livebox_key_event(NULL, PD_KEY_DOWN, 13, NULL, NULL); + dts_check_eq("livebox_key_event", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void key_event_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_key_event", ret, LB_STATUS_SUCCESS, "key_event"); + } else { + ret = livebox_key_event(handle, LB_KEY_DOWN, 13, NULL, NULL); + dts_check_eq("livebox_key_event", ret, LB_STATUS_SUCCESS, "key_event"); + } +} + +static void utc_livebox_key_event_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, key_event_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_key_event", handle, NULL, "Create for key_event"); + } +} + +static void utc_livebox_set_pinup_n(void) +{ + int ret; + ret = livebox_set_pinup(NULL, 0, NULL, NULL); + dts_check_eq("livebox_set_pinup", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void pinup_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_set_pinup", ret, LB_STATUS_SUCCESS, "key_event"); + } else { + ret = livebox_set_pinup(handle, 0, NULL, NULL); + dts_check_eq("livebox_set_pinup", ret, LB_STATUS_ERROR_ALREADY, "already"); + } +} + +static void utc_livebox_set_pinup_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, pinup_cb, NULL); + if (!handle) { + dts_check_ne("livebox_set_pinup", handle, NULL, "Create for pinup"); + } +} + +static void utc_livebox_is_pinned_up_n(void) +{ + int ret; + ret = livebox_is_pinned_up(NULL); + dts_check_eq("livebox_is_pinned_up", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void is_pinup_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_is_pinned_up", ret, LB_STATUS_SUCCESS, "key_event"); + } else { + ret = livebox_is_pinned_up(handle); + dts_check_eq("livebox_is_pinned_up", ret, LB_STATUS_ERROR_ALREADY, "already"); + } +} + +static void utc_livebox_is_pinned_up_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, is_pinup_cb, NULL); + if (!handle) { + dts_check_ne("livebox_is_pinned_up", handle, NULL, "Create for is_pinned_up"); + } +} + +static void utc_livebox_has_pinup_n(void) +{ + int ret; + ret = livebox_has_pinup(NULL); + dts_check_eq("livebox_has_pinup", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void has_pinup_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_has_pinup", ret, LB_STATUS_SUCCESS, "has_pinup"); + } else { + ret = livebox_has_pinup(handle); + dts_check_eq("livebox_has_pinup", ret, 0, "not supported"); + } +} + +static void utc_livebox_has_pinup_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, has_pinup_cb, NULL); + if (!handle) { + dts_check_ne("livebox_has_pinup", handle, NULL, "Create for has_pinup"); + } +} + +static void has_pd_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_has_pd", ret, LB_STATUS_SUCCESS, "has_pd"); + } else { + ret = livebox_has_pd(handle); + dts_check_eq("livebox_has_pd", ret, 1, "PD supported"); + } +} + +static void utc_livebox_has_pd_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, has_pd_cb, NULL); + if (!handle) { + dts_check_ne("livebox_has_pinup", handle, NULL, "Create for has_pinup"); + } +} + +static void utc_livebox_has_pd_n(void) +{ + int ret; + ret = livebox_has_pd(NULL); + dts_check_eq("livebox_has_pd", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void utc_livebox_create_pd_n(void) +{ + int ret; + ret = livebox_create_pd(NULL, NULL, NULL); + dts_check_eq("livebox_create_pd", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void create_pd_cb(struct livebox *handle, int ret, void *data) +{ + dts_check_eq("livebox_create_pd", ret, LB_STATUS_SUCCESS, "create_pd"); + (void)livebox_destroy_pd(handle, NULL, NULL); +} + +static void create_pd_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_create_pd", ret, LB_STATUS_SUCCESS, "create_pd"); + } else { + ret = livebox_create_pd(handle, create_pd_cb, NULL); + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_create_pd", ret, LB_STATUS_SUCCESS, "create_pd"); + } + } +} + +static void utc_livebox_create_pd_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, create_pd_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_has_pinup", handle, NULL, "Create for has_pinup"); + } +} + +static void utc_livebox_create_pd_with_position_n(void) +{ + int ret; + ret = livebox_create_pd_with_position(NULL, 0.0f, 0.0f, NULL, NULL); + dts_check_eq("livebox_create_pd_with_position", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void create_pd_pos_cb(struct livebox *handle, int ret, void *data) +{ + dts_check_eq("livebox_create_pd_with_position", ret, LB_STATUS_SUCCESS, "PD,create"); + (void)livebox_destroy_pd(handle, NULL, NULL); +} + +static void create_pd_pos_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_create_pd_with_position", ret, LB_STATUS_SUCCESS, "create_pd_w/h_position"); + } else { + ret = livebox_create_pd_with_position(handle, 0.0f, 0.0f, create_pd_pos_cb, NULL); + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_create_pd_with_position", ret, LB_STATUS_SUCCESS, "create_pd_w/h_position"); + } + } +} + +static void utc_livebox_create_pd_with_position_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, create_pd_pos_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_create_pd_with_position", handle, NULL, "Create for create_pd_with_position"); + } +} + +static void utc_livebox_move_pd_n(void) +{ + int ret; + ret = livebox_move_pd(NULL, 0.0f, 0.0f); + dts_check_eq("livebox_move_pd", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void create_move_pd_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_move_pd", ret, LB_STATUS_SUCCESS, "invalid"); + } else { + ret = livebox_move_pd(handle, 0.5f, 0.5f); + dts_check_eq("livebox_move_pd", ret, LB_STATUS_SUCCESS, "invalid"); + } +} + +static void move_pd_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_move_pd", ret, LB_STATUS_SUCCESS, "invalid"); + } else { + ret = livebox_create_pd_with_position(handle, 0.0f, 0.0f, create_move_pd_cb, NULL); + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_move_pd", ret, LB_STATUS_SUCCESS, "invalid"); + } + } +} + +static void utc_livebox_move_pd_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, move_pd_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_move_pd", handle, NULL, "Create for move_pd"); + } +} + +static void utc_livebox_destroy_pd_n(void) +{ + int ret; + ret = livebox_destroy_pd(NULL, NULL, NULL); + dts_check_eq("livebox_destroy_pd", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void destroy_pd_cb(struct livebox *handle, int ret, void *data) +{ + dts_check_eq("livebox_destroy_pd", ret, LB_STATUS_SUCCESS, "destroy_pd"); +} + +static void destroy_pd_create_pd_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_destroy_pd", ret, LB_STATUS_SUCCESS, "destroy_pd"); + } else { + ret = livebox_destroy_pd(handle, destroy_pd_cb, NULL); + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_destroy_pd", ret, LB_STATUS_SUCCESS, "destroy_pd"); + } + } +} + +static void destroy_pd_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_destroy_pd", ret, LB_STATUS_SUCCESS, "destroy_pd"); + } else { + ret = livebox_create_pd_with_position(handle, 0.0f, 0.0f, destroy_pd_create_pd_cb, NULL); + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_destroy_pd", ret, LB_STATUS_SUCCESS, "invalid"); + } + } +} + +static void utc_livebox_destroy_pd_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, destroy_pd_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_destroy_pd", handle, NULL, "Create for destroy_pd"); + } +} + +static void utc_livebox_pd_is_created_n(void) +{ + int ret; + ret = livebox_pd_is_created(NULL); + dts_check_eq("livebox_pd_is_created", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void pd_is_created_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_pd_is_created", ret, LB_STATUS_SUCCESS, "pd_is_created"); + } else { + ret = livebox_pd_is_created(handle); + dts_check_eq("livebox_pd_is_created", ret, 0, "pd_is_created"); + (void)livebox_del(handle, NULL, NULL); + } +} + +static void utc_livebox_pd_is_created_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, pd_is_created_cb, NULL); + if (!handle) { + dts_check_ne("livebox_pd_is_created", handle, NULL, "create for pd_is_created"); + } +} + +static void utc_livebox_pd_type_n(void) +{ + int type; + type = livebox_pd_type(NULL); + dts_check_eq("livebox_pd_type", type, PD_TYPE_INVALID, "invalid"); +} + +static void pd_type_created_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_pd_type", ret, LB_STATUS_SUCCESS, "pd_type"); + } else { + ret = livebox_pd_type(handle); + dts_check_ne("livebox_pd_type", ret, PD_TYPE_INVALID, "pd_type"); + } +} + +static void utc_livebox_pd_type_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, pd_type_created_cb, NULL); + if (!handle) { + dts_check_ne("livebox_pd_type", handle, NULL, "create for pd_is_created"); + } +} + +static void utc_livebox_is_exists_n(void) +{ + int ret; + ret = livebox_is_exists(NULL); + dts_check_eq("livebox_is_exists", ret, 0, "not exists"); +} + +static void utc_livebox_is_exists_p(void) +{ + int ret; + ret = livebox_is_exists(MUSIC_APP); + dts_check_eq("livebox_is_exists", ret, 1, "exists"); +} + +static void utc_livebox_set_text_handler_n(void) +{ + int ret; + ret = livebox_set_text_handler(NULL, NULL); + dts_check_eq("livebox_set_text_handler", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void set_text_handler_created_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_set_text_handler", ret, LB_STATUS_SUCCESS, "set_text_handler"); + } else { + struct livebox_script_operators ops = { + .update_begin = NULL, + .update_end = NULL, + .update_text = NULL, + .update_image = NULL, + .update_script = NULL, + .update_signal = NULL, + .update_drag = NULL, + .update_info_size = NULL, + .update_info_category = NULL, + .update_access = NULL, + .operate_access = NULL, + }; + + ret = livebox_set_text_handler(handle, &ops); + dts_check_eq("livebox_set_text_handler", ret, LB_STATUS_SUCCESS, "set_text_handler"); + } +} + +static void utc_livebox_set_text_handler_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, set_text_handler_created_cb, NULL); + if (!handle) { + dts_check_ne("livebox_set_text_handler", handle, NULL, "create for pd_is_created"); + } +} + +static void utc_livebox_set_pd_text_handler_n(void) +{ + int ret; + ret = livebox_set_pd_text_handler(NULL, NULL); + dts_check_eq("livebox_set_pd_text_handler", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void set_pd_text_handler_created_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_set_pd_text_handler", ret, LB_STATUS_SUCCESS, "set_text_handler"); + } else { + struct livebox_script_operators ops = { + .update_begin = NULL, + .update_end = NULL, + .update_text = NULL, + .update_image = NULL, + .update_script = NULL, + .update_signal = NULL, + .update_drag = NULL, + .update_info_size = NULL, + .update_info_category = NULL, + .update_access = NULL, + .operate_access = NULL, + }; + + ret = livebox_set_pd_text_handler(handle, &ops); + dts_check_eq("livebox_set_pd_text_handler", ret, LB_STATUS_SUCCESS, "set_text_handler"); + } +} + +static void utc_livebox_set_pd_text_handler_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, set_pd_text_handler_created_cb, NULL); + if (!handle) { + dts_check_ne("livebox_set_pd_text_handler", handle, NULL, "create for pd_is_created"); + } +} + +static void utc_livebox_emit_text_signal_n(void) +{ + int ret; + ret = livebox_emit_text_signal(NULL, NULL, NULL, 0.0f, 0.0f, 0.0f, 0.0f, NULL, NULL); + dts_check_eq("livebox_emit_text_signal", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void emit_signal_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_emit_text_signal", ret, LB_STATUS_SUCCESS, "set_text_handler"); + } else { + ret = livebox_emit_text_signal(handle, NULL, NULL, 0.0f, 0.0f, 0.0f, 0.0f, NULL, NULL); + dts_check_eq("livebox_emit_text_signal", ret, LB_STATUS_ERROR_INVALID, "text_handler"); + } +} + +static void utc_livebox_emit_text_signal_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, emit_signal_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_emit_text_signal", handle, NULL, "create for pd_is_created"); + } +} + +static void utc_livebox_set_data_n(void) +{ + int ret; + ret = livebox_set_data(NULL, NULL); + dts_check_eq("livebox_set_data", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void utc_livebox_set_data_p(void) +{ + struct livebox *handle; + int ret; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, emit_signal_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_set_data", handle, NULL, "create for pd_is_created"); + return; + } + + ret = livebox_set_data(handle, (void *)123); + dts_check_eq("livebox_set_data", ret, LB_STATUS_SUCCESS, "set_data"); +} + +static void utc_livebox_get_data_p(void) +{ + struct livebox *handle; + void *data; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, emit_signal_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_set_data", handle, NULL, "create for pd_is_created"); + return; + } + + data = livebox_get_data(handle); + dts_check_eq("livebox_get_data", data, NULL, "get_data"); +} + +static void utc_livebox_get_data_n(void) +{ + void *data; + data = livebox_get_data(NULL); + dts_check_eq("livebox_get_data", data, NULL, "data"); +} + +static void utc_livebox_subscribe_group_n(void) +{ + /*! + * \note + * Unable to test negative case + */ + dts_pass("livebox_subscribe_group", "pass negative test"); +} + +static void utc_livebox_subscribe_group_p(void) +{ + int ret; + ret = livebox_subscribe_group("my,cluster", "my,category"); + dts_check_eq("livebox_subscribe_group", ret, LB_STATUS_SUCCESS, "subscribe"); +} + +static void utc_livebox_unsubscribe_group_n(void) +{ + /*! + * \note + * Unable to test negative case + */ + dts_pass("livebox_unsubscribe_group", "pass negative test"); +} + +static void utc_livebox_unsubscribe_group_p(void) +{ + int ret; + ret = livebox_unsubscribe_group("my,cluster", "my,category"); + dts_check_eq("livebox_unsubscribe_group", ret, LB_STATUS_SUCCESS, "unsubscribe"); +} + +static void utc_livebox_refresh_group_n(void) +{ + int ret; + ret = livebox_refresh_group(NULL, NULL, 0); + dts_check_eq("livebox_refresh_group", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void utc_livebox_refresh_group_p(void) +{ + int ret; + ret = livebox_refresh_group("my,cluster", "my,category", 0); + dts_check_eq("livebox_refresh_group", ret, LB_STATUS_SUCCESS, "refresh_group"); +} + +static void refresh_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_refresh", ret, LB_STATUS_SUCCESS, "refresh"); + } else { + ret = livebox_refresh(handle, 0); + dts_check_eq("livebox_refresh", ret, LB_STATUS_SUCCESS, "refresh"); + } +} + +static void utc_livebox_refresh_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, refresh_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_set_data", handle, NULL, "create for pd_is_created"); + return; + } +} + +static void utc_livebox_refresh_n(void) +{ + int ret; + ret = livebox_refresh(NULL, 0); + dts_check_eq("livebox_refresh", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + + +static void utc_livebox_lb_pixmap_n(void) +{ + int ret; + ret = livebox_lb_pixmap(NULL); + dts_check_eq("livebox_lb_pixmap", ret, 0, "invalid"); +} + +static void lb_pixmap_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_lb_pixmap", ret, LB_STATUS_SUCCESS, "lb_pixmap"); + } else { + ret = livebox_lb_pixmap(handle); + dts_check_ne("livebox_lb_pixmap", ret, 0, "lb_pixmap"); + } +} + +static void utc_livebox_lb_pixmap_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, lb_pixmap_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_lb_pixmap", handle, NULL, "create for lb_pixmap"); + return; + } +} + +static void pd_pixmap_create_pd_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_pd_pixmap", ret, LB_STATUS_SUCCESS, "invalid"); + } else { + ret = livebox_pd_pixmap(handle); + dts_check_ne("livebox_pd_pixmap", ret, 0, "pd_pixmap"); + } +} + +static void pd_pixmap_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_pd_pixmap", ret, LB_STATUS_SUCCESS, "invalid"); + } else { + ret = livebox_create_pd(handle, pd_pixmap_create_pd_cb, NULL); + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_pd_pixmap", ret, LB_STATUS_SUCCESS, "invalid"); + } + } +} + +static void utc_livebox_pd_pixmap_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, pd_pixmap_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_pd_pixmap", handle, NULL, "create for pd_pixmap"); + } +} + +static void utc_livebox_pd_pixmap_n(void) +{ + int ret; + ret = livebox_pd_pixmap(NULL); + dts_check_eq("livebox_pd_pixmap", ret, 0, "invalid"); +} + +static void utc_livebox_acquire_pd_pixmap_n(void) +{ + int ret; + ret = livebox_acquire_pd_pixmap(NULL, NULL, NULL); + dts_check_eq("livebox_acquire_pd_pixmap", ret, 0, "invalid"); +} + +static void acquire_pd_pixmap_cb(struct livebox *handle, int pixmap, void *data) +{ + dts_check_ne("livebox_acquire_pd_pixmap", pixmap, 0, "acquire_pd_pixmap"); + (void)livebox_release_pd_pixmap(handle, pixmap); +} + +static void pd_acquire_pixmap_pd_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_acquire_pd_pixmap", ret, LB_STATUS_SUCCESS, "invalid"); + } else { + ret = livebox_acquire_pd_pixmap(handle, acquire_pd_pixmap_cb, NULL); + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_acquire_pd_pixmap", ret, LB_STATUS_SUCCESS, "invalid"); + } + } +} + +static void pd_acquire_pixmap_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_acquire_pd_pixmap", ret, LB_STATUS_SUCCESS, "invalid"); + } else { + ret = livebox_create_pd(handle, pd_acquire_pixmap_pd_create_cb, NULL); + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_acquire_pd_pixmap", ret, LB_STATUS_SUCCESS, "invalid"); + } + } +} + +static void utc_livebox_acquire_pd_pixmap_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, pd_acquire_pixmap_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_pd_pixmap", handle, NULL, "create for pd_pixmap"); + } +} + +static void utc_livebox_release_pd_pixmap_n(void) +{ + int ret; + ret = livebox_release_pd_pixmap(NULL, 0); + dts_check_eq("livebox_release_pd_pixmap", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void release_pd_pixmap_cb(struct livebox *handle, int pixmap, void *data) +{ + int ret; + + if (pixmap == 0) { + dts_check_ne("livebox_release_pd_pixmap", pixmap, 0, "release_pd_pixmap"); + return; + } + + ret = livebox_release_pd_pixmap(handle, pixmap); + dts_check_eq("livebox_release_pd_pixmap", ret, LB_STATUS_SUCCESS, "release_pd_pixmap"); +} + +static void pd_release_pixmap_pd_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_release_pd_pixmap", ret, LB_STATUS_SUCCESS, "release_pd_pixmap"); + } else { + ret = livebox_acquire_pd_pixmap(handle, release_pd_pixmap_cb, NULL); + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_release_pd_pixmap", ret, LB_STATUS_SUCCESS, "release_pd_pixmap"); + } + } +} + +static void pd_release_pixmap_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_release_pd_pixmap", ret, LB_STATUS_SUCCESS, "release_pd_pixmap"); + } else { + ret = livebox_create_pd(handle, pd_release_pixmap_pd_create_cb, NULL); + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_release_pd_pixmap", ret, LB_STATUS_SUCCESS, "release_pd_pixmap"); + } + } +} + +static void utc_livebox_release_pd_pixmap_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, pd_release_pixmap_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_release_pd_pixmap", handle, NULL, "create for release_pd_pixmap"); + } +} + +static void acquire_lb_pixmap_cb(struct livebox *handle, int pixmap, void *data) +{ + dts_check_ne("livebox_acquire_lb_pixmap", pixmap, 0, "acquire_lb_pixmap"); + (void)livebox_release_lb_pixmap(handle, pixmap); +} + +static void lb_acquire_pixmap_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_acquire_lb_pixmap", ret, LB_STATUS_SUCCESS, "invalid"); + } else { + ret = livebox_acquire_lb_pixmap(handle, acquire_lb_pixmap_cb, NULL); + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_acquire_lb_pixmap", ret, LB_STATUS_SUCCESS, "invalid"); + } + } +} + +static void utc_livebox_acquire_lb_pixmap_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, lb_acquire_pixmap_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_acquire_lb_pixmap", handle, NULL, "create for lb_pixmap"); + } +} + +static void utc_livebox_acquire_lb_pixmap_n(void) +{ + int ret; + ret = livebox_acquire_lb_pixmap(NULL, NULL, NULL); + dts_check_eq("livebox_acquire_lb_pixmap", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void release_lb_pixmap_cb(struct livebox *handle, int pixmap, void *data) +{ + if (pixmap == 0) { + dts_check_ne("livebox_release_lb_pixmap", pixmap, 0, "release_lb_pixmap"); + } else { + int ret; + ret = livebox_release_lb_pixmap(handle, pixmap); + dts_check_eq("livebox_release_lb_pixmap", ret, LB_STATUS_SUCCESS, "release_lb_pixmap"); + } +} + +static void lb_release_pixmap_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_release_lb_pixmap", ret, LB_STATUS_SUCCESS, "invalid"); + } else { + ret = livebox_acquire_lb_pixmap(handle, release_lb_pixmap_cb, NULL); + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_release_lb_pixmap", ret, LB_STATUS_SUCCESS, "invalid"); + } + } +} + +static void utc_livebox_release_lb_pixmap_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, lb_release_pixmap_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_release_lb_pixmap", handle, NULL, "create for lb_pixmap"); + } +} + +static void utc_livebox_release_lb_pixmap_n(void) +{ + int ret; + ret = livebox_release_lb_pixmap(NULL, 0); + dts_check_eq("livebox_release_lb_pixmap", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void set_visibility_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_ne("livebox_set_visibility", ret, LB_STATUS_SUCCESS, "visibility_set"); + } else { + ret = livebox_set_visibility(handle, LB_SHOW); + dts_check_eq("livebox_set_visibility", ret, LB_STATUS_SUCCESS, "visibility_set"); + } +} + +static void utc_livebox_set_visibility_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, set_visibility_cb, NULL); + if (!handle) { + dts_check_ne("livebox_set_visibility", handle, NULL, "create for set_visibility"); + } +} + +static void utc_livebox_set_visibility_n(void) +{ + int ret; + ret = livebox_set_visibility(NULL, LB_SHOW); + dts_check_eq("livebox_set_visibility", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void visibility_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_ne("livebox_visibility", ret, LB_STATUS_SUCCESS, "visibility"); + } else { + ret = livebox_visibility(handle); + dts_check_eq("livebox_visibility", ret, LB_HIDE_WITH_PAUSE, "visibility_set"); + } +} + +static void utc_livebox_visibility_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, visibility_cb, NULL); + if (!handle) { + dts_check_ne("livebox_visibility", handle, NULL, "create for visibility"); + } +} + +static void utc_livebox_visibility_n(void) +{ + int ret; + + ret = livebox_visibility(NULL); + dts_check_eq("livebox_visibility", ret, LB_VISIBLE_ERROR, "invalid"); +} + +static void utc_livebox_set_update_mode_n(void) +{ + int ret; + + ret = livebox_set_update_mode(NULL, 0, NULL, NULL); + dts_check_eq("livebox_set_update_mode", ret,LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void set_update_mode_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_set_update_mode", ret, LB_STATUS_SUCCESS, "set_update_mode"); + } else { + ret = livebox_set_update_mode(handle, 0, NULL, NULL); + dts_check_eq("livebox_set_update_mode", ret, LB_STATUS_ERROR_ALREADY, "set_update_mode"); + } +} + +static void utc_livebox_set_update_mode_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, set_update_mode_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_set_update_mode", handle, NULL, "create for set_update_mode"); + } +} + +static void utc_livebox_is_active_update_n(void) +{ + int ret; + ret = livebox_is_active_update(NULL); + dts_check_eq("livebox_is_active_update", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void is_active_update_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_is_active_update", ret, LB_STATUS_SUCCESS, "is_activate_update"); + } else { + ret = livebox_is_active_update(handle); + dts_check_eq("livebox_is_active_update", ret, 0, "is_activate_update"); + } +} + +static void utc_livebox_is_active_update_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, is_active_update_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_is_active_update", handle, NULL, "create for is_active_update"); + } +} + +static void utc_livebox_set_manual_sync_n(void) +{ + /*! + * \note + * Unable to test negative case + */ + dts_pass("livebox_set_manual_sync", "negative pass"); +} + +static void utc_livebox_set_manual_sync_p(void) +{ + livebox_set_option(LB_OPTION_MANUAL_SYNC, 0); + dts_pass("livebox_set_manual_sync", "Pass!"); +} + +static void utc_livebox_manual_sync_p(void) +{ + int ret; + + ret = livebox_option(LB_OPTION_MANUAL_SYNC); + dts_check_eq("livebox_manual_sync", ret, 0, "ok"); +} + +static void utc_livebox_manual_sync_n(void) +{ + /*! + * \note + * Unable to test negative case + */ + dts_pass("livebox_manual_sync", "negative Pass"); +} + +static void utc_livebox_set_frame_drop_for_resizing_n(void) +{ + /*! + * \note + * Unable to test negative case + */ + dts_pass("livebox_set_frame_drop_for_resizing", "pass"); +} + +static void utc_livebox_set_frame_drop_for_resizing_p(void) +{ + livebox_set_option(LB_OPTION_FRAME_DROP_FOR_RESIZE, 1); + dts_pass("livebox_set_frame_drop_for_resizing", "pass"); +} + +static void utc_livebox_frame_drop_for_resizing_n(void) +{ + /*! + * \note + * Unable to test negative case + */ + dts_pass("livebox_frame_drop_for_resizing", "pass"); +} + +static void utc_livebox_frame_drop_for_resizing_p(void) +{ + int ret; + + ret = livebox_option(LB_OPTION_FRAME_DROP_FOR_RESIZE); + dts_check_eq("livebox_frame_drop_for_resizing", ret, 1, "drop_for_resizing"); +} + +static void utc_livebox_sync_pd_fb_n(void) +{ + int ret; + ret = livebox_sync_pd_fb(NULL); + dts_check_eq("livebox_sync_pd_fb", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void sync_pd_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_sync_pd_fb", ret, LB_STATUS_SUCCESS, "sync_pd_fb"); + } else { + ret = livebox_sync_pd_fb(handle); + dts_check_eq("livebox_sync_pd_fb", ret, LB_STATUS_SUCCESS, "sync_pd_fb"); + } +} + +static void utc_livebox_sync_pd_fb_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, sync_pd_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_sync_pd_fb", handle, NULL, "create for sync_pd_fb"); + } +} + +static void sync_lb_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_sync_lb_fb", ret, LB_STATUS_SUCCESS, "sync_lb_fb"); + } else { + ret = livebox_sync_lb_fb(handle); + dts_check_eq("livebox_sync_lb_fb", ret, LB_STATUS_SUCCESS, "sync_lb_fb"); + } +} + +static void utc_livebox_sync_lb_fb_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, sync_lb_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_sync_pd_fb", handle, NULL, "create for sync_pd_fb"); + } +} + +static void utc_livebox_sync_lb_fb_n(void) +{ + int ret; + ret = livebox_sync_lb_fb(NULL); + dts_check_eq("livebox_sync_lb_fb", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void alt_icon_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_alt_name", ret, LB_STATUS_SUCCESS, "alt_name"); + } else { + const char *alt_icon; + + alt_icon = livebox_alt_icon(handle); + dts_check_ne("livebox_alt_icon", alt_icon, NULL, "alt_icon"); + } +} + +static void utc_livebox_alt_icon_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(MUSIC_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, alt_icon_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_alt_icon", handle, NULL, "alt_icon"); + } +} + +static void utc_livebox_alt_icon_n(void) +{ + const char *alt_icon; + + alt_icon = livebox_alt_icon(NULL); + dts_check_eq("livebox_alt_icon", alt_icon, NULL, "invalid"); +} + +static void alt_name_create_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_alt_name", ret, LB_STATUS_SUCCESS, "alt_name"); + } else { + const char *alt_name; + + alt_name = livebox_alt_name(handle); + dts_check_ne("livebox_alt_name", alt_name, NULL, "alt_name"); + } +} + +static void utc_livebox_alt_name_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(MUSIC_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, alt_name_create_cb, NULL); + if (!handle) { + dts_check_ne("livebox_alt_name", handle, NULL, "alt_name"); + } +} + +static void utc_livebox_alt_name_n(void) +{ + const char *alt_name; + alt_name = livebox_alt_name(NULL); + dts_check_eq("livebox_alt_icon", alt_name, NULL, "invalid"); +} + +static void utc_livebox_acquire_fb_lock_n(void) +{ + int ret; + ret = livebox_acquire_fb_lock(NULL, 0); + dts_check_eq("livebox_acquire_fb_lock", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void acquire_fb_lock_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_acquire_fb_lock", ret, LB_STATUS_SUCCESS, "acquire_fb_lock"); + } else { + ret = livebox_acquire_fb_lock(handle, 0); + dts_check_eq("livebox_acquire_fb_lock", ret, LB_STATUS_SUCCESS, "acquire_fb_lock"); + (void)livebox_release_fb_lock(handle, 0); + } +} + +static void utc_livebox_acquire_fb_lock_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, acquire_fb_lock_cb, NULL); + if (!handle) { + dts_check_ne("livebox_acquire_fb_lock", handle, NULL, "create for acquire_fb_lock"); + } +} + +static void utc_livebox_release_fb_lock_n(void) +{ + int ret; + ret = livebox_release_fb_lock(NULL, 0); + dts_check_eq("livebox_release_fb_lock", ret, LB_STATUS_ERROR_INVALID, "invalid"); +} + +static void release_fb_lock_cb(struct livebox *handle, int ret, void *data) +{ + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_release_fb_lock", ret, LB_STATUS_SUCCESS, "release_fb_lock"); + } else { + ret = livebox_acquire_fb_lock(handle, 0); + if (ret != LB_STATUS_SUCCESS) { + dts_check_eq("livebox_release_fb_lock", ret, LB_STATUS_SUCCESS, "release_fb_lock"); + return; + } + + ret = livebox_release_fb_lock(handle, 0); + dts_check_eq("livebox_acquire_fb_lock", ret, LB_STATUS_SUCCESS, "release_fb_lock"); + } +} + +static void utc_livebox_release_fb_lock_p(void) +{ + struct livebox *handle; + + handle = livebox_add_with_size(EMAIL_LIVEBOX, NULL, NULL, NULL, -1.0f, LB_SIZE_TYPE_1x1, release_fb_lock_cb, NULL); + if (!handle) { + dts_check_ne("livebox_release_fb_lock", handle, NULL, "create for release_fb_lock"); + } +} + +struct tet_testlist tet_testlist[] = { + { utc_livebox_client_paused_n, NEGATIVE_TC_IDX }, + { utc_livebox_client_paused_p, POSITIVE_TC_IDX }, + { utc_livebox_client_resumed_n, NEGATIVE_TC_IDX }, + { utc_livebox_client_resumed_p, POSITIVE_TC_IDX }, + { utc_livebox_add_p, POSITIVE_TC_IDX }, + { utc_livebox_add_n, NEGATIVE_TC_IDX }, + { utc_livebox_add_with_size_n, NEGATIVE_TC_IDX }, + { utc_livebox_add_with_size_p, POSITIVE_TC_IDX }, + { utc_livebox_del_n, NEGATIVE_TC_IDX }, + { utc_livebox_del_p, POSITIVE_TC_IDX }, + { utc_livebox_del_NEW_n, NEGATIVE_TC_IDX }, + { utc_livebox_del_NEW_p, POSITIVE_TC_IDX }, + { utc_livebox_set_event_handler_n, NEGATIVE_TC_IDX }, + { utc_livebox_set_event_handler_p, POSITIVE_TC_IDX }, + { utc_livebox_unset_event_handler_n, NEGATIVE_TC_IDX }, + { utc_livebox_unset_event_handler_p, POSITIVE_TC_IDX }, + { utc_livebox_set_fault_handler_n, NEGATIVE_TC_IDX }, + { utc_livebox_set_fault_handler_p, POSITIVE_TC_IDX }, + { utc_livebox_unset_fault_handler_n, NEGATIVE_TC_IDX }, + { utc_livebox_unset_fault_handler_p, POSITIVE_TC_IDX }, + { utc_livebox_activate_n, NEGATIVE_TC_IDX }, + { utc_livebox_activate_p, POSITIVE_TC_IDX }, + { utc_livebox_resize_n, NEGATIVE_TC_IDX }, + { utc_livebox_resize_p, POSITIVE_TC_IDX }, + { utc_livebox_click_n, NEGATIVE_TC_IDX }, + { utc_livebox_click_p, POSITIVE_TC_IDX }, + { utc_livebox_set_group_n, NEGATIVE_TC_IDX }, + { utc_livebox_set_group_p, POSITIVE_TC_IDX }, + { utc_livebox_get_group_n, NEGATIVE_TC_IDX }, + { utc_livebox_get_group_p, POSITIVE_TC_IDX }, + { utc_livebox_period_n, NEGATIVE_TC_IDX }, + { utc_livebox_period_p, POSITIVE_TC_IDX }, + { utc_livebox_set_period_n, NEGATIVE_TC_IDX }, + { utc_livebox_set_period_p, POSITIVE_TC_IDX }, + { utc_livebox_lb_type_n, NEGATIVE_TC_IDX }, + { utc_livebox_lb_type_p, POSITIVE_TC_IDX }, + { utc_livebox_is_user_n, NEGATIVE_TC_IDX }, + { utc_livebox_is_user_p, POSITIVE_TC_IDX }, + { utc_livebox_content_n, NEGATIVE_TC_IDX }, + { utc_livebox_content_p, POSITIVE_TC_IDX }, + { utc_livebox_category_title_n, NEGATIVE_TC_IDX }, + { utc_livebox_category_title_p, POSITIVE_TC_IDX }, + { utc_livebox_filename_n, NEGATIVE_TC_IDX }, + { utc_livebox_filename_p, POSITIVE_TC_IDX }, + { utc_livebox_pkgname_n, NEGATIVE_TC_IDX }, + { utc_livebox_pkgname_p, POSITIVE_TC_IDX }, + { utc_livebox_priority_n, NEGATIVE_TC_IDX }, + { utc_livebox_priority_p, POSITIVE_TC_IDX }, + { utc_livebox_acquire_fb_n, NEGATIVE_TC_IDX }, + { utc_livebox_acquire_fb_p, POSITIVE_TC_IDX }, + { utc_livebox_release_fb_n, NEGATIVE_TC_IDX }, + { utc_livebox_release_fb_p, POSITIVE_TC_IDX }, + { utc_livebox_fb_refcnt_p, POSITIVE_TC_IDX }, + { utc_livebox_fb_refcnt_n, NEGATIVE_TC_IDX }, + { utc_livebox_acquire_pdfb_p, POSITIVE_TC_IDX }, + { utc_livebox_acquire_pdfb_n, NEGATIVE_TC_IDX }, + { utc_livebox_release_pdfb_p, POSITIVE_TC_IDX }, + { utc_livebox_release_pdfb_n, NEGATIVE_TC_IDX }, + { utc_livebox_pdfb_refcnt_n, NEGATIVE_TC_IDX }, + { utc_livebox_pdfb_refcnt_p, POSITIVE_TC_IDX }, + { utc_livebox_size_p, POSITIVE_TC_IDX }, + { utc_livebox_size_n, NEGATIVE_TC_IDX }, + { utc_livebox_get_pdsize_n, NEGATIVE_TC_IDX }, + { utc_livebox_get_pdsize_p, POSITIVE_TC_IDX }, + { utc_livebox_get_supported_sizes_n, NEGATIVE_TC_IDX }, + { utc_livebox_get_supported_sizes_p, POSITIVE_TC_IDX }, + { utc_livebox_lbfb_bufsz_n, NEGATIVE_TC_IDX }, + { utc_livebox_lbfb_bufsz_p, POSITIVE_TC_IDX }, + { utc_livebox_pdfb_bufsz_n, NEGATIVE_TC_IDX }, + { utc_livebox_pdfb_bufsz_p, POSITIVE_TC_IDX }, + { utc_livebox_content_event_n, NEGATIVE_TC_IDX }, + { utc_livebox_content_event_p, POSITIVE_TC_IDX }, + { utc_livebox_mouse_event_n, NEGATIVE_TC_IDX }, + { utc_livebox_mouse_event_p, POSITIVE_TC_IDX }, + { utc_livebox_access_event_n, NEGATIVE_TC_IDX }, + { utc_livebox_access_event_p, POSITIVE_TC_IDX }, + { utc_livebox_key_event_n, NEGATIVE_TC_IDX }, + { utc_livebox_key_event_p, POSITIVE_TC_IDX }, + { utc_livebox_set_pinup_n, NEGATIVE_TC_IDX }, + { utc_livebox_set_pinup_p, POSITIVE_TC_IDX }, + { utc_livebox_is_pinned_up_n, NEGATIVE_TC_IDX }, + { utc_livebox_is_pinned_up_p, POSITIVE_TC_IDX }, + { utc_livebox_has_pinup_n, NEGATIVE_TC_IDX }, + { utc_livebox_has_pinup_p, POSITIVE_TC_IDX }, + { utc_livebox_has_pd_p, POSITIVE_TC_IDX }, + { utc_livebox_has_pd_n, NEGATIVE_TC_IDX }, + { utc_livebox_create_pd_n, NEGATIVE_TC_IDX }, + { utc_livebox_create_pd_p, POSITIVE_TC_IDX }, + { utc_livebox_create_pd_with_position_n, NEGATIVE_TC_IDX }, + { utc_livebox_create_pd_with_position_p, POSITIVE_TC_IDX }, + { utc_livebox_move_pd_n, NEGATIVE_TC_IDX }, + { utc_livebox_move_pd_p, POSITIVE_TC_IDX }, + { utc_livebox_destroy_pd_n, NEGATIVE_TC_IDX }, + { utc_livebox_destroy_pd_p, POSITIVE_TC_IDX }, + { utc_livebox_pd_is_created_n, NEGATIVE_TC_IDX }, + { utc_livebox_pd_is_created_p, POSITIVE_TC_IDX }, + { utc_livebox_pd_type_n, NEGATIVE_TC_IDX }, + { utc_livebox_pd_type_p, POSITIVE_TC_IDX }, + { utc_livebox_is_exists_n, NEGATIVE_TC_IDX }, + { utc_livebox_is_exists_p, POSITIVE_TC_IDX }, + { utc_livebox_set_text_handler_n, NEGATIVE_TC_IDX }, + { utc_livebox_set_text_handler_p, POSITIVE_TC_IDX }, + { utc_livebox_set_pd_text_handler_n, NEGATIVE_TC_IDX }, + { utc_livebox_set_pd_text_handler_p, POSITIVE_TC_IDX }, + { utc_livebox_emit_text_signal_n, NEGATIVE_TC_IDX }, + { utc_livebox_emit_text_signal_p, POSITIVE_TC_IDX }, + { utc_livebox_set_data_n, NEGATIVE_TC_IDX }, + { utc_livebox_set_data_p, POSITIVE_TC_IDX }, + { utc_livebox_get_data_p, POSITIVE_TC_IDX }, + { utc_livebox_get_data_n, NEGATIVE_TC_IDX }, + { utc_livebox_subscribe_group_n, NEGATIVE_TC_IDX }, + { utc_livebox_subscribe_group_p, POSITIVE_TC_IDX }, + { utc_livebox_unsubscribe_group_n, NEGATIVE_TC_IDX }, + { utc_livebox_unsubscribe_group_p, POSITIVE_TC_IDX }, + { utc_livebox_refresh_group_n, NEGATIVE_TC_IDX }, + { utc_livebox_refresh_group_p, POSITIVE_TC_IDX }, + { utc_livebox_refresh_p, POSITIVE_TC_IDX }, + { utc_livebox_refresh_n, NEGATIVE_TC_IDX }, + { utc_livebox_lb_pixmap_n, NEGATIVE_TC_IDX }, + { utc_livebox_lb_pixmap_p, POSITIVE_TC_IDX }, + { utc_livebox_pd_pixmap_p, POSITIVE_TC_IDX }, + { utc_livebox_pd_pixmap_n, NEGATIVE_TC_IDX }, + { utc_livebox_acquire_pd_pixmap_n, NEGATIVE_TC_IDX }, + { utc_livebox_acquire_pd_pixmap_p, POSITIVE_TC_IDX }, + { utc_livebox_release_pd_pixmap_n, NEGATIVE_TC_IDX }, + { utc_livebox_release_pd_pixmap_p, POSITIVE_TC_IDX }, + { utc_livebox_acquire_lb_pixmap_p, POSITIVE_TC_IDX }, + { utc_livebox_acquire_lb_pixmap_n, NEGATIVE_TC_IDX }, + { utc_livebox_release_lb_pixmap_p, POSITIVE_TC_IDX }, + { utc_livebox_release_lb_pixmap_n, NEGATIVE_TC_IDX }, + { utc_livebox_set_visibility_p, POSITIVE_TC_IDX }, + { utc_livebox_set_visibility_n, NEGATIVE_TC_IDX }, + { utc_livebox_visibility_p, POSITIVE_TC_IDX }, + { utc_livebox_visibility_n, NEGATIVE_TC_IDX }, + { utc_livebox_set_update_mode_n, NEGATIVE_TC_IDX }, + { utc_livebox_set_update_mode_p, POSITIVE_TC_IDX }, + { utc_livebox_is_active_update_n, NEGATIVE_TC_IDX }, + { utc_livebox_is_active_update_p, POSITIVE_TC_IDX }, + { utc_livebox_set_manual_sync_n, NEGATIVE_TC_IDX }, + { utc_livebox_set_manual_sync_p, POSITIVE_TC_IDX }, + { utc_livebox_manual_sync_p, POSITIVE_TC_IDX }, + { utc_livebox_manual_sync_n, NEGATIVE_TC_IDX }, + { utc_livebox_set_frame_drop_for_resizing_n, NEGATIVE_TC_IDX }, + { utc_livebox_set_frame_drop_for_resizing_p, POSITIVE_TC_IDX }, + { utc_livebox_frame_drop_for_resizing_n, NEGATIVE_TC_IDX }, + { utc_livebox_frame_drop_for_resizing_p, POSITIVE_TC_IDX }, + { utc_livebox_sync_pd_fb_n, NEGATIVE_TC_IDX }, + { utc_livebox_sync_pd_fb_p, POSITIVE_TC_IDX }, + { utc_livebox_sync_lb_fb_p, POSITIVE_TC_IDX }, + { utc_livebox_sync_lb_fb_n, NEGATIVE_TC_IDX }, + { utc_livebox_alt_icon_p, POSITIVE_TC_IDX }, + { utc_livebox_alt_icon_n, NEGATIVE_TC_IDX }, + { utc_livebox_alt_name_p, POSITIVE_TC_IDX }, + { utc_livebox_alt_name_n, NEGATIVE_TC_IDX }, + { utc_livebox_acquire_fb_lock_n, NEGATIVE_TC_IDX }, + { utc_livebox_acquire_fb_lock_p, POSITIVE_TC_IDX }, + { utc_livebox_release_fb_lock_n, NEGATIVE_TC_IDX }, + { utc_livebox_release_fb_lock_p, POSITIVE_TC_IDX }, + { NULL, 0 }, +}; + diff --git a/doc/image/PD.png b/doc/image/PD.png Binary files differnew file mode 100644 index 0000000..ebb7367 --- /dev/null +++ b/doc/image/PD.png diff --git a/doc/image/download_folder.png b/doc/image/download_folder.png Binary files differnew file mode 100644 index 0000000..1e5e01b --- /dev/null +++ b/doc/image/download_folder.png diff --git a/doc/image/front.jpg b/doc/image/front.jpg Binary files differnew file mode 100644 index 0000000..9d46441 --- /dev/null +++ b/doc/image/front.jpg diff --git a/doc/image/image_format.png b/doc/image/image_format.png Binary files differnew file mode 100644 index 0000000..8f79a0c --- /dev/null +++ b/doc/image/image_format.png diff --git a/doc/image/message.png b/doc/image/message.png Binary files differnew file mode 100644 index 0000000..4cb7760 --- /dev/null +++ b/doc/image/message.png diff --git a/doc/image/preload_folder.png b/doc/image/preload_folder.png Binary files differnew file mode 100644 index 0000000..3dbbf64 --- /dev/null +++ b/doc/image/preload_folder.png diff --git a/doc/image/script_format.png b/doc/image/script_format.png Binary files differnew file mode 100644 index 0000000..1fec88a --- /dev/null +++ b/doc/image/script_format.png diff --git a/doc/image/stock.png b/doc/image/stock.png Binary files differnew file mode 100644 index 0000000..fd274fe --- /dev/null +++ b/doc/image/stock.png diff --git a/doc/image/text_format.png b/doc/image/text_format.png Binary files differnew file mode 100644 index 0000000..b492883 --- /dev/null +++ b/doc/image/text_format.png diff --git a/doc/image/twitter.png b/doc/image/twitter.png Binary files differnew file mode 100644 index 0000000..36bce25 --- /dev/null +++ b/doc/image/twitter.png diff --git a/doc/image/weather.png b/doc/image/weather.png Binary files differnew file mode 100644 index 0000000..2f41af1 --- /dev/null +++ b/doc/image/weather.png diff --git a/doc/livebox-viewer_doc.h b/doc/livebox-viewer_doc.h new file mode 100644 index 0000000..850d08d --- /dev/null +++ b/doc/livebox-viewer_doc.h @@ -0,0 +1,125 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*! + * \ingroup CAPI_LIVEBOX_FRAMEWORK + * \defgroup CAPI_LIVEBOX_VIEWER_MODULE Livebox Viewer + * \brief API for livebox viewer (widget screen, home screen, ...) + * \section CAPI_LIVEBOX_VIEWER_MODULE_HEADER Required Header + * \#include <livebox.h> + * \section CAPI_LIVEBOX_VIEWER_MODULE_OVERVIEW Overview + * Tizen(SLP) homescreen S/W framework is supporing the live box. (aka widget which is similiar with the android widget) + * + * \image html front.jpg + * + * \subsection WhatIsTheLivebox 1. What is the Livebox + * The live box is the widget of the TIZEN. + * + * It works as a small application displayed on other applications' (such as homescreen, lockscreen, etc ...) view. + * Each live box can have (not a mandatory option) a PD (progressive disclosure) in which more detailed information can be found. + * The content of PD can be exposed when a certain gesture (e.g., flick-down) has been applied to the live box. + * If you are interested in developing a livebox, there are things you should know prior to making any source code for the box. + * To make your live box added to any live box viewer application (e.g., live panel in our case), then you need to create and prepare + * controller(SO file), layout script (EDJE for a PD if necessary), configuration files. + * + * A livebox is managed by data provider, since each SO file of a livebox is loaded on and controlled by data provider using predefined ABI. + * A viewer will receive any livebox's content in forms of "image file", "buffer" or "text" and display the content in various formats on its window. + * A livebox developer needs to make sure that your live box generates desirable content in-time on a explicit update-request or periodic update. + * + * After a data provider loads a livebox's SO file, it then assigns a specific "file name" for the livebox via an argument of a livebox function. + * Since then the livebox just generates content using then given file name. + * Passing an image file (whose name is the previously given name) is the basic method for providing contents to the viewer. + * But if you want play animation or handles user event in real-time, you can use the buffer type. + * + * And you should prepare the content of the Progressive Disclosure. + * The Progressive Dislcosure is only updated by the "buffer" type. so you should prepare the layout script for it. + * If you didn't install any script file for progressive disclosure, the viewer will ignore the "flick down" event from your livebox. + * + * \subsubsection Livebox 1.1 Livebox + * Live box is a default content of your widget. It always displays on the screen and updated periodically. + * It looks like below captured images. + * \image html weather.png Weather Livebox + * \image html stock.png Stock Livebox + * \image html twitter.png Twitter Livebox + * + * \subsubsection ProgressiveDisclosure 1.2 Progressive Disclosure + * \image html PD.png Progressive Disclosure + * Progressive disclosure will be displayed when a user flicks down a livebox. (basically it depends on the implementation of the view applications) + * To supports this, a developer should prepare the layout script (EDJE only for the moment) of the livebox's PD. (or you can use the buffer directly) + * Data provider supports EDJE script but the developer can use various scripts if (which is BIG IF) their interpreters can be implemented based on evas & ecore. + * + * When a layout script has been installed, data provider can load and rendering the given layout on the buffer. + * The content on the buffer can be shared between applications that need to display the content on their window. + * Description data file is necessary to place proper content components in rendered layout. + * Check this page Description Data. + * + * \subsubsection ClusterCategory 1.3 What is the "cluster" and "category" + * The cluster and the sub-cluster is just like the grouping concept. + * It is used for creating/destorying your livebox instance when the data provider receives any context event from the context engine. + * You will only get "user,created" cluster and "default" category(sub cluster) info. + * + * \subsection LiveboxContent 2. How the livebox can draw contents for viewer? + * There are several ways to update the content of a livebox. + * + * \li Image file based content updating + * \li Description file based content updating (with the layout script file) + * \li Buffer based content updating + * + * Each method has specific benefit for implementing the livebox. + * + * \subsubsection ImageFormat 2.1 Via image file + * This is the basic method for providing content of a livebox to the viewer application. + * But this can be used only for the livebox. (Unavailable for the progressive disclosure). + * When your livebox is created, the provider will assign an unique ID for your livebox(it would be a filename). + * You should keep that ID until your livebox is running. The ID will be passed to you via livebox_create function. + * \image html image_format.png + * + * When you need to update the output of your livebox, you should generate the image file using given ID(filename). + * Then the data provider will recognize the event of updated output of a livebox and it will send that event to the viewer to reload it on the screen. + * + * \subsubsection ScriptFormat 2.2 Via layout script + * \image html script_format.png + * This method is supported for static layout & various contents (text & image) + * When you develop your livebox, first design the layout of box content using script (edje is default) + * Then the provider will load it to the content buffer and start rendering. + * After the sciprt is loaded, you can fill it using description data format. + * liblivebox defines description data handling functions. + * + * \subsubsection TextFormat 2.3 Via text data + * \image html text_format.png + * This is the simplified method to update the content of livebox. + * So your box only need to update the text data using description data format. + * Then the viewer will parse it to fill its screen. + * So there is no buffer area, just viewer decide how handles it. + * + * \subsubsection BufferFormat 2.4 Via buffer + * This method is very complex to implement. + * The provider will give a content buffer to you, then your box should render its contents on this buffer. + * This type is only supported for 3rd party livebox such as OSP and WEB. + * Inhouse(EFL) livebox is not able to use this buffer type for the box content. + * + * \subsection PackageNTools 3. How can I get the development packages or tools? + * + * \subsection DevelopLivebox 4. How can I write a new livebox + * + * \subsection TestLivebox 5. How can I test my livebox + * + * \subsection LiveboxDirectory 6. Livebox directory hierachy + * \image html preload_folder.png + * \image html download_folder.png + * + */ + diff --git a/include/client.h b/include/client.h new file mode 100644 index 0000000..db8a931 --- /dev/null +++ b/include/client.h @@ -0,0 +1,22 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +extern int client_init(int use_thread); +extern int client_fd(void); +extern const char *client_addr(void); +extern int client_fini(void); + +/* End of a file */ diff --git a/include/conf.h b/include/conf.h new file mode 100644 index 0000000..83f2ff4 --- /dev/null +++ b/include/conf.h @@ -0,0 +1,42 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*! + * \note + * milli seconds + */ +#define MAX_LOG_FILE 3 +#define MAX_LOG_LINE 1000 +#define SLAVE_LOG_PATH "/tmp/.dbox.service/log/" + +#if !defined(VCONFKEY_MASTER_STARTED) +#define VCONFKEY_MASTER_STARTED "memory/data-provider-master/started" +#endif + +#if !defined(VCONFKEY_MASTER_CLIENT_ADDR) +#define VCONFKEY_MASTER_CLIENT_ADDR "db/data-provider-master/serveraddr" +#endif + +extern void conf_set_manual_sync(int flag); +extern int conf_manual_sync(void); +extern void conf_set_frame_drop_for_resizing(int flag); +extern int conf_frame_drop_for_resizing(void); +extern void conf_set_shared_content(int flag); +extern int conf_shared_content(void); +extern double conf_event_filter(void); +extern void conf_set_event_filter(double filter); + +/* End of a file */ diff --git a/include/debug.h b/include/debug.h new file mode 100644 index 0000000..d06862c --- /dev/null +++ b/include/debug.h @@ -0,0 +1,28 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !defined(FLOG) +#define DbgPrint(format, arg...) SECURE_LOGD(format, ##arg) +#define ErrPrint(format, arg...) SECURE_LOGE(format, ##arg) +#else +extern FILE *__file_log_fp; +#define DbgPrint(format, arg...) do { fprintf(__file_log_fp, "[LOG] [[32m%s/%s[0m:%d] " format, util_basename(__FILE__), __func__, __LINE__, ##arg); fflush(__file_log_fp); } while (0) + +#define ErrPrint(format, arg...) do { fprintf(__file_log_fp, "[ERR] [[32m%s/%s[0m:%d] " format, util_basename(__FILE__), __func__, __LINE__, ##arg); fflush(__file_log_fp); } while (0) +#endif + + +/* End of a file */ diff --git a/include/desc_parser.h b/include/desc_parser.h new file mode 100644 index 0000000..a7e0092 --- /dev/null +++ b/include/desc_parser.h @@ -0,0 +1,19 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +extern int parse_desc(struct livebox_common *common, const char *filename, int is_pd); + +/* End of a file */ diff --git a/include/dlist.h b/include/dlist.h new file mode 100644 index 0000000..cd1a421 --- /dev/null +++ b/include/dlist.h @@ -0,0 +1,43 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define dlist_remove_data(list, data) do { \ + struct dlist *l; \ + l = dlist_find_data(list, data); \ + list = dlist_remove(list, l); \ +} while (0) + +#define dlist_foreach(list, l, data) \ + for ((l) = (list); (l) && ((data) = dlist_data(l)); (l) = dlist_next(l)) + +#define dlist_foreach_safe(list, l, n, data) \ + for ((l) = (list), (n) = dlist_next(l); \ + (l) && ((data) = dlist_data(l)); \ + (l) = (n), (n) = dlist_next(l)) + +struct dlist; + +extern struct dlist *dlist_append(struct dlist *list, void *data); +extern struct dlist *dlist_prepend(struct dlist *list, void *data); +extern struct dlist *dlist_remove(struct dlist *list, struct dlist *l); +extern struct dlist *dlist_find_data(struct dlist *list, void *data); +extern void *dlist_data(struct dlist *l); +extern struct dlist *dlist_next(struct dlist *l); +extern struct dlist *dlist_prev(struct dlist *l); +extern int dlist_count(struct dlist *l); +extern struct dlist *dlist_nth(struct dlist *l, int nth); + +/* End of a file */ diff --git a/include/fb.h b/include/fb.h new file mode 100644 index 0000000..9e567dd --- /dev/null +++ b/include/fb.h @@ -0,0 +1,42 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +struct fb_info; + +enum buffer_type { /*!< Must have to be sync with libprovider, liblivebox-viewer */ + BUFFER_TYPE_FILE, + BUFFER_TYPE_SHM, + BUFFER_TYPE_PIXMAP, + BUFFER_TYPE_ERROR +}; + +extern int fb_init(void *disp); +extern int fb_fini(void); +extern const char *fb_id(struct fb_info *info); +extern int fb_get_size(struct fb_info *info, int *w, int *h); +extern int fb_sync(struct fb_info *info); +extern int fb_size(struct fb_info *info); +extern int fb_refcnt(void *data); +extern int fb_is_created(struct fb_info *info); +extern int fb_type(struct fb_info *info); + +extern struct fb_info *fb_create(const char *filename, int w, int h); +extern int fb_destroy(struct fb_info *info); + +extern void *fb_acquire_buffer(struct fb_info *info); +extern int fb_release_buffer(void *data); + +/* End of a file */ diff --git a/include/file_service.h b/include/file_service.h new file mode 100644 index 0000000..9212d03 --- /dev/null +++ b/include/file_service.h @@ -0,0 +1,21 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +extern int file_service_send_request(const char *filename, const char *save_to, void (*result_cb)(const char *filename, const char *save_to, int ret, void *data), void *data); +extern int file_service_fini(void); +extern int file_service_init(void); + +/* End of a file */ diff --git a/include/livebox.h b/include/livebox.h new file mode 100644 index 0000000..2e00248 --- /dev/null +++ b/include/livebox.h @@ -0,0 +1,1753 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LIVEBOX_H +#define __LIVEBOX_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \addtogroup CAPI_LIVEBOX_VIEWER_MODULE + * \{ + */ + +/*! + * \brief + * Structure for a Livebox instance + */ +struct livebox; + +/*! + * \brief + * Use the default update period which is defined in the package manifest file of a livebox. + */ +#define DEFAULT_PERIOD -1.0f + +/*! + * \brief + * Mouse & Key event for buffer type Livebox or PD + * Viewer should send these events to livebox. + */ +enum content_event_type { + CONTENT_EVENT_MOUSE_DOWN = 0x00000001, /*!< LB mouse down event for livebox */ + CONTENT_EVENT_MOUSE_UP = 0x00000002, /*!< LB mouse up event for livebox */ + CONTENT_EVENT_MOUSE_MOVE = 0x00000004, /*!< LB mouse move event for livebox */ + CONTENT_EVENT_MOUSE_ENTER = 0x00000008, /*!< LB mouse enter event for livebox */ + CONTENT_EVENT_MOUSE_LEAVE = 0x00000010, /*!< LB mouse leave event for livebox */ + CONTENT_EVENT_MOUSE_SET = 0x00000020, /*!< LB mouse set auto event for livebox */ + CONTENT_EVENT_MOUSE_UNSET = 0x00000040, /*!< LB mouse unset auto event for livebox */ + + CONTENT_EVENT_KEY_DOWN = 0x00000001, /*!< LB key press */ + CONTENT_EVENT_KEY_UP = 0x00000002, /*!< LB key release */ + CONTENT_EVENT_KEY_FOCUS_IN = 0x00000008, /*!< LB key focused in */ + CONTENT_EVENT_KEY_FOCUS_OUT = 0x00000010, /*!< LB key focused out */ + CONTENT_EVENT_KEY_SET = 0x00000020, /*!< LB Key, start feeding event by master */ + CONTENT_EVENT_KEY_UNSET = 0x00000040, /*!< LB key, stop feeding event by master */ + + CONTENT_EVENT_ON_SCROLL = 0x00000080, /*!< LB On scrolling */ + CONTENT_EVENT_ON_HOLD = 0x00000100, /*!< LB On holding */ + CONTENT_EVENT_OFF_SCROLL = 0x00000200, /*!< LB Stop scrolling */ + CONTENT_EVENT_OFF_HOLD = 0x00000400, /*!< LB Stop holding */ + + CONTENT_EVENT_KEY_MASK = 0x80000000, + CONTENT_EVENT_MOUSE_MASK = 0x20000000, + CONTENT_EVENT_PD_MASK = 0x10000000, + CONTENT_EVENT_LB_MASK = 0x40000000, + + LB_MOUSE_ON_SCROLL = CONTENT_EVENT_LB_MASK | CONTENT_EVENT_MOUSE_MASK | CONTENT_EVENT_ON_SCROLL, /*!< Mouse event occurs while scrolling */ + LB_MOUSE_ON_HOLD = CONTENT_EVENT_LB_MASK | CONTENT_EVENT_MOUSE_MASK | CONTENT_EVENT_ON_HOLD, /*!< Mouse event occurs on holding */ + LB_MOUSE_OFF_SCROLL = CONTENT_EVENT_LB_MASK | CONTENT_EVENT_MOUSE_MASK | CONTENT_EVENT_OFF_SCROLL, /*!< Scrolling stopped */ + LB_MOUSE_OFF_HOLD = CONTENT_EVENT_LB_MASK | CONTENT_EVENT_MOUSE_MASK | CONTENT_EVENT_OFF_HOLD, /*!< Holding stopped */ + + LB_MOUSE_DOWN = CONTENT_EVENT_LB_MASK | CONTENT_EVENT_MOUSE_MASK | CONTENT_EVENT_MOUSE_DOWN, /*!< Mouse down on the livebox */ + LB_MOUSE_UP = CONTENT_EVENT_LB_MASK | CONTENT_EVENT_MOUSE_MASK | CONTENT_EVENT_MOUSE_UP, /*!< Mouse up on the livebox */ + LB_MOUSE_MOVE = CONTENT_EVENT_LB_MASK | CONTENT_EVENT_MOUSE_MASK | CONTENT_EVENT_MOUSE_MOVE, /*!< Move move on the livebox */ + LB_MOUSE_ENTER = CONTENT_EVENT_LB_MASK | CONTENT_EVENT_MOUSE_MASK | CONTENT_EVENT_MOUSE_ENTER, /*!< Mouse enter to the livebox */ + LB_MOUSE_LEAVE = CONTENT_EVENT_LB_MASK | CONTENT_EVENT_MOUSE_MASK | CONTENT_EVENT_MOUSE_LEAVE, /*!< Mouse leave from the livebox */ + LB_MOUSE_SET = CONTENT_EVENT_LB_MASK | CONTENT_EVENT_MOUSE_MASK | CONTENT_EVENT_MOUSE_SET, /*!< Mouse event, start feeding event by master */ + LB_MOUSE_UNSET = CONTENT_EVENT_LB_MASK | CONTENT_EVENT_MOUSE_MASK | CONTENT_EVENT_MOUSE_UNSET, /*!< Mouse event, stop feeding event by master */ + + PD_MOUSE_ON_SCROLL = CONTENT_EVENT_PD_MASK | CONTENT_EVENT_MOUSE_MASK | CONTENT_EVENT_ON_SCROLL, /*!< Mouse event occurs while scrolling */ + PD_MOUSE_ON_HOLD = CONTENT_EVENT_PD_MASK | CONTENT_EVENT_MOUSE_MASK | CONTENT_EVENT_ON_HOLD, /*!< Mouse event occurs on holding */ + PD_MOUSE_OFF_SCROLL = CONTENT_EVENT_PD_MASK | CONTENT_EVENT_MOUSE_MASK | CONTENT_EVENT_OFF_SCROLL, /*!< Scrolling stopped */ + PD_MOUSE_OFF_HOLD = CONTENT_EVENT_PD_MASK | CONTENT_EVENT_MOUSE_MASK | CONTENT_EVENT_OFF_HOLD, /*!< Holding stopped */ + + PD_MOUSE_DOWN = CONTENT_EVENT_PD_MASK | CONTENT_EVENT_MOUSE_MASK | CONTENT_EVENT_MOUSE_DOWN, /*!< Mouse down on the PD */ + PD_MOUSE_UP = CONTENT_EVENT_PD_MASK | CONTENT_EVENT_MOUSE_MASK | CONTENT_EVENT_MOUSE_UP, /*!< Mouse up on the PD */ + PD_MOUSE_MOVE = CONTENT_EVENT_PD_MASK | CONTENT_EVENT_MOUSE_MASK | CONTENT_EVENT_MOUSE_MOVE, /*!< Mouse move on the PD */ + PD_MOUSE_ENTER = CONTENT_EVENT_PD_MASK | CONTENT_EVENT_MOUSE_MASK | CONTENT_EVENT_MOUSE_ENTER, /*!< Mouse enter to the PD */ + PD_MOUSE_LEAVE = CONTENT_EVENT_PD_MASK | CONTENT_EVENT_MOUSE_MASK | CONTENT_EVENT_MOUSE_LEAVE, /*!< Mouse leave from the PD */ + PD_MOUSE_SET = CONTENT_EVENT_PD_MASK | CONTENT_EVENT_MOUSE_MASK | CONTENT_EVENT_MOUSE_SET, /*!< Mouse event, start feeding event by master */ + PD_MOUSE_UNSET = CONTENT_EVENT_PD_MASK | CONTENT_EVENT_MOUSE_MASK | CONTENT_EVENT_MOUSE_UNSET, /*!< Mouse event, stop feeding event by master */ + + LB_KEY_DOWN = CONTENT_EVENT_LB_MASK | CONTENT_EVENT_KEY_MASK | CONTENT_EVENT_KEY_DOWN, /*!< Key down on the livebox */ + LB_KEY_UP = CONTENT_EVENT_LB_MASK | CONTENT_EVENT_KEY_MASK | CONTENT_EVENT_KEY_UP, /*!< Key up on the livebox */ + LB_KEY_SET = CONTENT_EVENT_LB_MASK | CONTENT_EVENT_KEY_MASK | CONTENT_EVENT_KEY_SET, /*!< Key event, start feeding event by master */ + LB_KEY_UNSET = CONTENT_EVENT_LB_MASK | CONTENT_EVENT_KEY_MASK | CONTENT_EVENT_KEY_UNSET, /*!< Key event, stop feeding event by master */ + LB_KEY_FOCUS_IN = CONTENT_EVENT_LB_MASK | CONTENT_EVENT_KEY_MASK | CONTENT_EVENT_KEY_FOCUS_IN, /*!< Key event, focus in */ + LB_KEY_FOCUS_OUT = CONTENT_EVENT_LB_MASK | CONTENT_EVENT_KEY_MASK | CONTENT_EVENT_KEY_FOCUS_OUT, /*!< Key event, foucs out */ + + PD_KEY_DOWN = CONTENT_EVENT_PD_MASK | CONTENT_EVENT_KEY_MASK | CONTENT_EVENT_KEY_DOWN, /*!< Key down on the livebox */ + PD_KEY_UP = CONTENT_EVENT_PD_MASK | CONTENT_EVENT_KEY_MASK | CONTENT_EVENT_KEY_UP, /*!< Key up on the livebox */ + PD_KEY_SET = CONTENT_EVENT_PD_MASK | CONTENT_EVENT_KEY_MASK | CONTENT_EVENT_KEY_SET, /*!< Key event, start feeding event by master */ + PD_KEY_UNSET = CONTENT_EVENT_PD_MASK | CONTENT_EVENT_KEY_MASK | CONTENT_EVENT_KEY_UNSET, /*!< Key event, stop feeding event by master */ + PD_KEY_FOCUS_IN = CONTENT_EVENT_PD_MASK | CONTENT_EVENT_KEY_MASK | CONTENT_EVENT_KEY_FOCUS_IN, /*!< Key event, focus in */ + PD_KEY_FOCUS_OUT = CONTENT_EVENT_PD_MASK | CONTENT_EVENT_KEY_MASK | CONTENT_EVENT_KEY_FOCUS_OUT, /*!< Key event, focus out */ + + CONTENT_EVENT_MAX = 0xFFFFFFFF +}; + +/*! + * \brief + * Accessibility event for buffer type Livebox or PD. + * These event set are sync'd with Tizen accessibility event set. + */ +enum access_event_type { + ACCESS_EVENT_PD_MASK = 0x10000000, + ACCESS_EVENT_LB_MASK = 0x20000000, + + ACCESS_EVENT_HIGHLIGHT = 0x00000100, /*!< LB accessibility: Hightlight a object */ + ACCESS_EVENT_HIGHLIGHT_NEXT = 0x00000200, /*!< LB accessibility: Set highlight to next object */ + ACCESS_EVENT_HIGHLIGHT_PREV = 0x00000400, /*!< LB accessibility: Set highlight to prev object */ + ACCESS_EVENT_UNHIGHLIGHT = 0x00000800, /*!< LB accessibility unhighlight */ + ACCESS_EVENT_ACTIVATE = 0x00001000, /*!< LB accessibility activate */ + ACCESS_EVENT_ACTION_DOWN = 0x00010000, /*!< LB accessibility value changed */ + ACCESS_EVENT_ACTION_UP = 0x00020000, /*!< LB accessibility value changed */ + ACCESS_EVENT_SCROLL_DOWN = 0x00100000, /*!< LB accessibility scroll down */ + ACCESS_EVENT_SCROLL_MOVE = 0x00200000, /*!< LB accessibility scroll move */ + ACCESS_EVENT_SCROLL_UP = 0x00400000, /*!< LB accessibility scroll up */ + + LB_ACCESS_HIGHLIGHT = ACCESS_EVENT_LB_MASK | ACCESS_EVENT_HIGHLIGHT, /*!< Access event - Highlight an object in the livebox */ + LB_ACCESS_HIGHLIGHT_NEXT = ACCESS_EVENT_LB_MASK | ACCESS_EVENT_HIGHLIGHT_NEXT, /*!< Access event - Move highlight to the next object in a livebox */ + LB_ACCESS_HIGHLIGHT_PREV = ACCESS_EVENT_LB_MASK | ACCESS_EVENT_HIGHLIGHT_PREV, /*!< Access event - Move highlight to the prev object in a livebox */ + LB_ACCESS_UNHIGHLIGHT = ACCESS_EVENT_LB_MASK | ACCESS_EVENT_UNHIGHLIGHT, /*!< Access event - Delete highlight from the livebox */ + LB_ACCESS_ACTIVATE = ACCESS_EVENT_LB_MASK | ACCESS_EVENT_ACTIVATE, /*!< Access event - Launch or activate the highlighted object */ + LB_ACCESS_ACTION_DOWN = ACCESS_EVENT_LB_MASK | ACCESS_EVENT_ACTION_DOWN, /*!< Access event - down */ + LB_ACCESS_ACTION_UP = ACCESS_EVENT_LB_MASK | ACCESS_EVENT_ACTION_UP, /*!< Access event - up */ + LB_ACCESS_SCROLL_DOWN = ACCESS_EVENT_LB_MASK | ACCESS_EVENT_SCROLL_DOWN, /*!< Access event - scroll down */ + LB_ACCESS_SCROLL_MOVE = ACCESS_EVENT_LB_MASK | ACCESS_EVENT_SCROLL_MOVE, /*!< Access event - scroll move */ + LB_ACCESS_SCROLL_UP = ACCESS_EVENT_LB_MASK | ACCESS_EVENT_SCROLL_UP, /*!< Access event - scroll up */ + + PD_ACCESS_HIGHLIGHT = ACCESS_EVENT_PD_MASK | ACCESS_EVENT_HIGHLIGHT, /*!< Access event - Highlight an object in the PD */ + PD_ACCESS_HIGHLIGHT_NEXT = ACCESS_EVENT_PD_MASK | ACCESS_EVENT_HIGHLIGHT_NEXT, /*!< Access event - Move highlight to the next object in a PD */ + PD_ACCESS_HIGHLIGHT_PREV = ACCESS_EVENT_PD_MASK | ACCESS_EVENT_HIGHLIGHT_PREV, /*!< Access event - Move highlight to the prev object in a PD */ + PD_ACCESS_UNHIGHLIGHT = ACCESS_EVENT_PD_MASK | ACCESS_EVENT_UNHIGHLIGHT, /*!< Access event - Delet highlight from the PD */ + PD_ACCESS_ACTIVATE = ACCESS_EVENT_PD_MASK | ACCESS_EVENT_ACTIVATE, /*!< Access event - Launch or activate the highlighted object */ + PD_ACCESS_ACTION_DOWN = ACCESS_EVENT_PD_MASK | ACCESS_EVENT_ACTION_DOWN, /*!< Access event - down */ + PD_ACCESS_ACTION_UP = ACCESS_EVENT_PD_MASK | ACCESS_EVENT_ACTION_UP, /*!< Access event - up */ + PD_ACCESS_SCROLL_DOWN = ACCESS_EVENT_PD_MASK | ACCESS_EVENT_SCROLL_DOWN, /*!< Access event - scroll down */ + PD_ACCESS_SCROLL_MOVE = ACCESS_EVENT_PD_MASK | ACCESS_EVENT_SCROLL_MOVE, /*!< Access event - scroll move */ + PD_ACCESS_SCROLL_UP = ACCESS_EVENT_PD_MASK | ACCESS_EVENT_SCROLL_UP /*!< Access event - scroll up */ +}; + +/*! + * \brief + * Livebox LB content type + */ +enum livebox_lb_type { + LB_TYPE_IMAGE = 0x01, /*!< Contents of a livebox is based on the image file */ + LB_TYPE_BUFFER = 0x02, /*!< Contents of a livebox is based on canvas buffer(shared) */ + LB_TYPE_TEXT = 0x04, /*!< Contents of a livebox is based on formatted text file */ + LB_TYPE_PIXMAP = 0x08, /*!< Contens of a livebox is shared by the pixmap(depends on X) */ + + LB_TYPE_INVALID = 0xFF +}; + +/*! + * \brief + * Livebox PD content type + */ +enum livebox_pd_type { + PD_TYPE_BUFFER = 0x01, /*!< Contents of a PD is based on canvas buffer(shared) */ + PD_TYPE_TEXT = 0x02, /*!< Contents of a PD is based on formatted text file */ + PD_TYPE_PIXMAP = 0x04, /*!< Contents of a livebox is shared by the pixmap(depends on X) */ + + PD_TYPE_INVALID = 0xFF +}; + +/*! + * \brief + * Livebox event type. + * These event will be sent from the provider. + */ +enum livebox_event_type { /*!< livebox_event_handler_set Event list */ + LB_EVENT_LB_UPDATED, /*!< Contents of the given livebox is updated */ + LB_EVENT_PD_UPDATED, /*!< Contents of the given pd is updated */ + + LB_EVENT_CREATED, /*!< A new livebox is created */ + LB_EVENT_DELETED, /*!< A livebox is deleted */ + + LB_EVENT_GROUP_CHANGED, /*!< Group (Cluster/Sub-cluster) information is changed */ + LB_EVENT_PINUP_CHANGED, /*!< PINUP status is changed */ + LB_EVENT_PERIOD_CHANGED, /*!< Update period is changed */ + + LB_EVENT_LB_SIZE_CHANGED, /*!< Livebox size is changed */ + LB_EVENT_PD_SIZE_CHANGED, /*!< PD size is changed */ + + LB_EVENT_PD_CREATED, /*!< If a PD is created even if you didn't call the livebox_create_pd API */ + LB_EVENT_PD_DESTROYED, /*!< If a PD is destroyed even if you didn't call the livebox_destroy_pd API */ + + LB_EVENT_HOLD_SCROLL, /*!< If the screen should be freezed */ + LB_EVENT_RELEASE_SCROLL, /*!< If the screen can be scrolled */ + + LB_EVENT_LB_UPDATE_BEGIN, /*!< Livebox LB content update is started */ + LB_EVENT_LB_UPDATE_END, /*!< Livebox LB content update is finished */ + + LB_EVENT_PD_UPDATE_BEGIN, /*!< Livebox PD content update is started */ + LB_EVENT_PD_UPDATE_END, /*!< Livebox PD content update is finished */ + + LB_EVENT_UPDATE_MODE_CHANGED, /*!< Livebox Update mode is changed */ + + LB_EVENT_REQUEST_CLOSE_PD, /*!< Livebox requests to close the PD */ + + LB_EVENT_IGNORED /*!< Request is ignored */ +}; + +enum livebox_option_type { + LB_OPTION_MANUAL_SYNC, /*!< Sync manually */ + LB_OPTION_FRAME_DROP_FOR_RESIZE, /*!< Drop frames while resizing */ + LB_OPTION_SHARED_CONTENT, /*!< Use only one real instance for multiple fake instances if user creates it using same content */ + + LB_OPTION_ERROR = 0xFFFFFFFF /*!< To specify the size of this enumeration type */ +}; + +enum livebox_fault_type { + LB_FAULT_DEACTIVATED, /*!< Livebox is deactivated by its fault operation */ + LB_FAULT_PROVIDER_DISCONNECTED /*!< Provider is disconnected */ +}; + +/*! + * \brief + * Must be sync'd with the provider + */ +enum livebox_visible_state { + LB_SHOW = 0x00, /*!< Livebox is shown. Default state */ + LB_HIDE = 0x01, /*!< Livebox is hidden, Update timer will not be freezed. but you cannot receive any updates events. */ + + LB_HIDE_WITH_PAUSE = 0x02, /*!< Livebix is hidden, it will pause the update timer, but if a livebox updates its contents, update event will be triggered */ + + LB_VISIBLE_ERROR = 0xFFFFFFFF /*!< To specify the size of this enumeration type */ +}; + +/*! + * \brief + * TEXT type livebox contents handling opertators. + */ +struct livebox_script_operators { + int (*update_begin)(struct livebox *handle); /*!< Content parser is started */ + int (*update_end)(struct livebox *handle); /*!< Content parser is finished */ + + /*! + * \brief + * Listed functions will be called when parser meets each typed content + */ + int (*update_text)(struct livebox *handle, const char *id, const char *part, const char *data); /*!< Update text content */ + int (*update_image)(struct livebox *handle, const char *id, const char *part, const char *data, const char *option); /*!< Update image content */ + int (*update_script)(struct livebox *handle, const char *id, const char *new_id, const char *part, const char *file, const char *group); /*!< Update script content */ + int (*update_signal)(struct livebox *handle, const char *id, const char *emission, const char *signal); /*!< Update signal */ + int (*update_drag)(struct livebox *handle, const char *id, const char *part, double dx, double dy); /*!< Update drag info */ + int (*update_info_size)(struct livebox *handle, const char *id, int w, int h); /*!< Update content size */ + int (*update_info_category)(struct livebox *handle, const char *id, const char *category); /*!< Update content category info */ + int (*update_access)(struct livebox *handle, const char *id, const char *part, const char *text, const char *option); /*!< Update access information */ + int (*operate_access)(struct livebox *handle, const char *id, const char *part, const char *operation, const char *option); /*!< Update access operation */ + int (*update_color)(struct livebox *handle, const char *id, const char *part, const char *data); /*!< Update color */ +}; + +/*! + * \brief Prototype of the return callback of every async functions + * \details N/A + * \remarks N/A + * \param[in] handle Handle of the livebox instance + * \param[in] ret Result status of operation. LB_STATUS_XXX defined from liblivebox-service + * \param[in] data data for result callback + * \return void + * \pre N/A + * \post N/A + * \see livebox_add + * \see livebox_add_with_size + * \see livebox_del + * \see livebox_activate + * \see livebox_resize + * \see livebox_set_group + * \see livebox_set_period + * \see livebox_access_event + * \see livebox_set_pinup + * \see livebox_create_pd + * \see livebox_create_pd_with_position + * \see livebox_destroy_pd + * \see livebox_emit_text_signal + * \see livebox_acquire_pd_pixmap + * \see livebox_acquire_lb_pixmap + * \see livebox_set_update_mode + */ +typedef void (*ret_cb_t)(struct livebox *handle, int ret, void *data); + +/*! + * \brief Initialize the livebox system + * \details N/A + * \remarks + * This API uses get/setenv APIs. + * Those APIs are not thread-safe. + * So you have to consider to use the livebox_init_with_options instead of this if you are developing multi-threaded viewer. + * \param[in] disp If you have X Display connection object already, you can re-use it. but you should care its life cycle. + * It must be alive before call the livebox_fini + * \return int + * \retval LB_STATUS_SUCCESS if success + * \pre N/A + * \post N/A + * \see livebox_fini + * \see livebox_init_with_options + */ +extern int livebox_init(void *disp); + +/*! + * \brief Initialize the livebox system with some options + * \details livebox_init function uses environment value to initiate some configurable values + * But some application doesn't want to use the env value. + * For them, this API will give a chance to set default options using given arguments + * \remarks N/A + * \param[in] disp if this display is NULL, the library will try to acquire a new connection with X + * \param[in] prevent_overwrite If the content of image type livebox is updated, don't overwrite it(1) or overwrite old file(0) + * \param[in] event_filter If the next event comes in this period, ignore it. It is too fast to processing it in time. + * \param[in] use_thread Use the receive thread. + * \return int Integer, Livebox status code + * \retval LB_STATUS_SUCCESS if success + * \pre N/A + * \post N/A + * \see livebox_init + * \see livebox_fini + */ +extern int livebox_init_with_options(void *disp, int prevent_overwrite, double event_filter, int use_thread); + +/*! + * \brief Finalize the livebox system + * \details N/A + * \remarks N/A + * \return int + * \retval LB_STATUS_SUCCES if success + * \retval LB_STATUS_ERROR_INVALID if livebox_init is not called. + * \pre N/A + * \post N/A + * \see livebox_init + * \see livebox_init_with_options + */ +extern int livebox_fini(void); + +/*! + * \brief Notify the status of client to the provider. "it is paused". + * \details N/A + * \remarks N/A + * \return int + * \retval LB_STATUS_SUCCESS if success + * \retval LB_STATUS_ERROR_FAULT if it failed to send state(paused) info + * \pre N/A + * \post N/A + * \see livebox_client_resumed + */ +extern int livebox_client_paused(void); + +/*! + * \brief Notify the status of client to the provider. "it is resumed". + * \details N/A + * \remarks N/A + * \return int + * \retval LB_STATUS_SUCCESS if success + * \retval LB_STATUS_ERROR_FAULT if it failed to send state(resumed) info + * \pre N/A + * \post N/A + * \see livebox_client_paused + */ +extern int livebox_client_resumed(void); + +/*! + * \brief Add a new livebox + * \details N/A + * \remarks + * Even though you get the livebox handle from return value of this function, + * it is not matured before return callback called. + * You have to use the handle after get the return callback with "ret == LB_STATUS_SUCCESS" + * \param[in] pkgname Livebox Id + * \param[in] content Will be passed to the livebox instance. + * \param[in] cluster Main group + * \param[in] category Sub group + * \param[in] period Update period. if you set DEFAULT_PERIOD, the provider will use the default period which is described in the manifest. + * \param[in] cb After send the request to the provider, its result will be passed + * \param[in] data + * \return handle + * \retval NULL if it fails to add a new instance + * \retval handle livebox handle + * \pre N/A + * \post + * \see ret_cb_t + * \see livebox_add_with_size + */ +extern struct livebox *livebox_add(const char *pkgname, const char *content, const char *cluster, const char *category, double period, ret_cb_t cb, void *data); + +/*! + * \brief Add a new livebox + * \details + * If the screen size if "1280x720" + * Below size lists are used for default. + * Or you can find the default sizes in pixel from /usr/share/data-provider-master/resolution.ini + * Size types are defined from the liblivebox-service package. (livebox-service.h) + * + * Normal mode livebox + * 1x1=175x175, LB_SIZE_TYPE_1x1 + * 2x1=354x175, LB_SIZE_TYPE_2x1 + * 2x2=354x354, LB_SIZE_TYPE_2x2 + * 4x1=712x175, LB_SIZE_TYPE_4x1 + * 4x2=712x354, LB_SIZE_TYPE_4x2 + * 4x4=712x712, LB_SIZE_TYPE_4x4 + * + * Extended sizes + * 4x3=712x533, LB_SIZE_TYPE_4x3 + * 4x5=712x891, LB_SIZE_TYPE_4x5 + * 4x6=712x1070, LB_SIZE_TYPE_4x6 + * + * Easy mode livebox + * 21x21=224x215, LB_SIZE_TYPE_EASY_1x1 + * 23x21=680x215, LB_SIZE_TYPE_EASY_3x1 + * 23x23=680x653, LB_SIZE_TYPE_EASY_3x3 + * + * Special livebox + * 0x0=720x1280, LB_SIZE_TYPE_0x0 + * + * \remarks + * Even if you get the handle by return value of this function, it is not created instance. + * So you have to deal it as not initialized handle. + * Only it can be initialized after get the return callback with "ret == LB_STATUS_SUCCESS". + * \param[in] pkgname Livebox Id + * \param[in] content Will be passed to the livebox instance. + * \param[in] cluster Main group + * \param[in] category Sub group + * \param[in] period DEFAULT_PERIOD can be used for this. this argument will be used to specify the period of update content of livebox. + * \param[in] type Size type - which are defined from liblivebox-service package. + * \param[in] cb After the request is sent to the master provider, this callback will be called. + * \param[in] data This data will be passed to the callback. + * \return handle + * \retval handle Livebox handle but not yet initialized + * \retval NULL if it fails to create a handle + * \see ret_cb_t + * \see livebox_add + */ +extern struct livebox *livebox_add_with_size(const char *pkgname, const char *content, const char *cluster, const char *category, double period, int type, ret_cb_t cb, void *data); + +/*! + * \brief Delete a livebox (deprecated) + * \details N/A + * \remarks + * If you call this with uninitialized handle, the return callback will be called synchronously. + * So before return from this function, the return callback will be called first. + * \param[in] handler Handler of a livebox instance + * \param[in] cb return callback + * \param[in] data user data for return callback + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_ERROR_BUSY already in process + * \retval LB_STATUS_ERROR_FAULT failed to create a request packet + * \retval LB_STATUS_SUCCESS successfully sent, return callack will be called + * \pre N/A + * \post N/A + * \see ret_cb_t + */ +extern int livebox_del(struct livebox *handler, ret_cb_t cb, void *data); + +/*! + * \brief Delete a livebox (will be replaced with livebox_del) + * \details N/A + * \remarks + * If you call this with uninitialized handle, the return callback will be called synchronously. + * So before return from this function, the return callback will be called first. + * \param[in] handler Handler of a livebox instance + * \param[in] type Deletion type, LB_DELETE_PERMANENTLY or LB_DELETE_TEMPORARY + * \param[in] cb return callback + * \param[in] data user data for return callback + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_ERROR_BUSY already in process + * \retval LB_STATUS_ERROR_FAULT failed to create a request packet + * \retval LB_STATUS_SUCCESS successfully sent, return callack will be called + * \pre N/A + * \post N/A + * \see ret_cb_t + */ +extern int livebox_del_NEW(struct livebox *handler, int type, ret_cb_t cb, void *data); + +/*! + * \brief Set a livebox events callback + * \details + * To get the events push from the provider, register the event callback using this function + * The callback will be called if there is any events from the provider. + * \remarks N/A + * \param[in] cb Event handler + * \param[in] data User data for the event handler + * \return int + * \retval LB_STATUS_SUCCESS if succeed to set event handler + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_ERROR_MEMORY Not enough memory + * \pre NULL + * \post NULL + * \see livebox_unset_event_handler + */ +extern int livebox_set_event_handler(int (*cb)(struct livebox *handler, enum livebox_event_type event, void *data), void *data); + +/*! + * \brief Unset the livebox event handler + * \details N/A + * \remarks N/A + * \param[in] cb Event handler + * \return void * Event handler data + * \retval pointer of 'data' which is used with the livebox_set_event_handler + * \pre N/A + * \post N/A + * \see livebox_set_event_handler + */ +extern void *livebox_unset_event_handler(int (*cb)(struct livebox *handler, enum livebox_event_type event, void *data)); + +/*! + * \brief Live box fault event handler registeration function + * argument list + * event, pkgname, filename, funcname + * \details N/A + * \remarks N/A + * \param[in] cb Event handler + * \param[in] data Event handler data + * \return int + * \retval LB_STATUS_SUCCESS if succeed to set fault event handler + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_ERROR_MEMORY Not enough memory + * \pre N/A + * \post N/A + * \see livebox_unset_fault_handler + */ +extern int livebox_set_fault_handler(int (*cb)(enum livebox_fault_type, const char *, const char *, const char *, void *), void *data); + +/*! + * \brief Unset the live box fault event handler + * \details N/A + * \remarks N/A + * \param[in] cb Event handler + * \return void * Callback data which is set via livebox_set_fault_handler + * \retval pointer of 'data' which is used with the livebox_set_fault_handler + * \pre N/A + * \post N/A + * \see livebox_set_fault_handler + */ +extern void *livebox_unset_fault_handler(int (*cb)(enum livebox_fault_type, const char *, const char *, const char *, void *)); + +/*! + * \brief Activate the faulted livebox. + * \details + * Request result will be back via return callback. + * \remarks + * Even though this function returns SUCCESS, it means just successfully sent a request to provider. + * So you have to check the return callback. and its "ret" argument. + * \param[in] pkgname Package name which should be activated + * \param[in] cb Result callback + * \param[in] data Callback data + * \return int + * \retval LB_STATUS_SUCCESS Successfully sent a request + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_ERROR_FAULT Failed to make a request + * \pre N/A + * \post N/A + * \see ret_cb_t + */ +extern int livebox_activate(const char *pkgname, ret_cb_t cb, void *data); + +/*! + * \brief Resize the livebox + * \details + * Normal mode livebox size + * 1x1=175x175, LB_SIZE_TYPE_1x1 + * 2x1=354x175, LB_SIZE_TYPE_2x1 + * 2x2=354x354, LB_SIZE_TYPE_2x2 + * 4x1=712x175, LB_SIZE_TYPE_4x1 + * 4x2=712x354, LB_SIZE_TYPE_4x2 + * 4x4=712x712, LB_SIZE_TYPE_4x4 + * + * Extended livebox size + * 4x3=712x533, LB_SIZE_TYPE_4x3 + * 4x5=712x891, LB_SIZE_TYPE_4x5 + * 4x6=712x1070, LB_SIZE_TYPE_4x6 + * + * Easy mode livebox size + * 21x21=224x215, LB_SIZE_TYPE_EASY_1x1 + * 23x21=680x215, LB_SIZE_TYPE_EASY_3x1 + * 23x23=680x653, LB_SIZE_TYPE_EASY_3x3 + * + * Special mode livebox size + * 0x0=720x1280, LB_SIZE_TYPE_0x0 + * \remarks + * You have to check the return callback. + * \param[in] handler Handler of a livebox instance + * \param[in] type Type of a livebox size, LB_SIZE_TYPE_1x1, ... + * \param[in] cb Result callback of the resize operation. + * \param[in] data User data for return callback + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_ERROR_BUSY Previous request of resize is in progress. + * \retval LB_STATUS_ERROR_ALREADY Already resized, there is no differences between current size and requested size. + * \retval LB_STATUS_ERROR_PERMISSION Permission denied, you only have view the content of this box. + * \retval LB_STATUS_ERROR_FAULT Failed to make a request + * \pre N/A + * \post N/A + * \see ret_cb_t + */ +extern int livebox_resize(struct livebox *handler, int type, ret_cb_t cb, void *data); + +/*! + * \brief Send the click event for a livebox. + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \param[in] x Rational X of the content width. + * \param[in] y Rational Y of the content height. + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_ERROR_FAULT Unrecoverable error occurred + * \retval LB_STATUS_SUCCESS Successfully done + * \pre N/A + * \post N/A + * \see N/A + */ +extern int livebox_click(struct livebox *handler, double x, double y); + +/*! + * \brief Change the cluster/sub-cluster name of given livebox handler + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \param[in] cluster New cluster of a livebox + * \param[in] category New category of a livebox + * \param[in] cb Result callback for changing the cluster/category of a livebox + * \param[in] data User data for the result callback + * \return int + * \retval LB_STATUS_SUCCESS Request is successfully sent. the return callback will be called. + * \retval LB_STATUS_ERROR_BUSY previous request is not finished yet. + * \retval LB_STATUS_ERROR_ALREADY group name is same with current one. + * \retval LB_STATUS_ERROR_PERMISSION you have no permission to change property of this livebox instance. + * \retval LB_STATUS_ERROR_FAULT Failed to make a request. + * \pre N/A + * \post N/A + * \see ret_cb_t + */ +extern int livebox_set_group(struct livebox *handler, const char *cluster, const char *category, ret_cb_t cb, void *data); + +/*! + * \brief Get the cluster and category(sub-cluster) name of given livebox (It is not I18N format, only english) + * \details N/A + * \remarks + * You have to do not release the cluster & category. + * It is allocated inside of given livebox instance, so you can only read it. + * \param[in] handler Handler of a livebox instance + * \param[out] cluster Storage(memory) for containing the cluster name + * \param[out] category Storage(memory) for containing the category name + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_SUCCESS Successfully done + * \pre N/A + * \post N/A + * \see N/A + */ +extern int livebox_get_group(struct livebox *handler, const char **cluster, const char **category); + +/*! + * \brief Get the period of this livebox handler + * \details N/A + * \remarks + * if this function returns 0.0f, it means the livebox has no update period. + * or the handle is not valid. + * This function only can be works after the return callback of livebox_create fucntion is called. + * \param[in] handler Handler of a livebox instance + * \return double + * \retval Current update period of a livebox + * \retval 0.0f it means the box has no update period, or it can returns 0.0 if the handles is not valid. + * \pre N/A + * \post N/A + * \see N/A + */ +extern double livebox_period(struct livebox *handler); + +/*! + * \brief Change the update period + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \param[in] period New update period of a livebox + * \param[in] cb Result callback of changing the update period of this livebox + * \param[in] data User data for the result callback + * \return int + * \retval LB_STATUS_SUCCESS Successfully done + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_ERROR_BUSY + * \retval LB_STATUS_ERROR_ALREADY + * \retval LB_STATUS_ERROR_FAULT Unrecoverable error occurred + * \pre N/A + * \post N/A + * \see ret_cb_t + */ +extern int livebox_set_period(struct livebox *handler, double period, ret_cb_t cb, void *data); + +/*! + * \brief Is this an text type livebox? + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \return livebox_lb_type + * \retval LB_TYPE_IMAGE Contents of a livebox is based on the image file + * \retval LB_TYPE_BUFFER Contents of a livebox is based on canvas buffer(shared) + * \retval LB_TYPE_TEXT Contents of a livebox is based on formatted text file + * \retval LB_TYPE_PIXMAP Contens of a livebox is shared by the pixmap(depends on X) + * \retval LB_TYPE_INVALID + * \pre N/A + * \post N/A + * \see livebox_lb_type + */ +extern enum livebox_lb_type livebox_lb_type(struct livebox *handler); + +/*! + * \brief Is this livebox is created by a user? + * \details + * If the livebox instance is created by system this will returns 0. + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval 0 automatically created livebox by the provider + * \retval 1 created by user via livebox_add or livebox_add_with_size + * \pre N/A + * \post N/A + * \see livebox_add + * \see livebox_add_with_size + * \see livebox_set_event_handler + */ +extern int livebox_is_user(struct livebox *handler); + +/*! + * \brief Get the content information string of given livebox + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \return const char * + * \retval content_info Livebox content info that can be used again via content_info argument of livebox_add or livebox_add_with_size. + * \pre N/A + * \post N/A + * \see livebox_add + * \see livebox_add_with_size + */ +extern const char *livebox_content(struct livebox *handler); + +/*! + * \brief Get the sub cluster title string of given livebox + * \details + * This API is now used for accessibility. + * Each box should set their content as a string to read by TTS. + * So if the box has focus on the homescreen, the homescreen will read a text using this API. + * \remarks + * The title which is returned by this, the TTS should read it. + * But it is just recomended to a homescreen. + * So read it or not is depends on implementation of the homescreen. + * \param[in] handler Handler of a livebox instance + * \return const char * + * \retval sub cluster name + * \retval NULL + * \pre N/A + * \post N/A + * \see N/A + */ +extern const char *livebox_category_title(struct livebox *handler); + +/*! + * \brief Get the filename of given livebox, if it is an IMAGE type livebox + * \details + * If the box is developed as image format to represent its contents, + * The homescreen should know its image file name. + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \return const char * + * \retval filename if the livebox type is image this function will give you a abspath of an image file (content is rendered) + * \retval NULL if this has no image file or type is not image file. + * \pre N/A + * \post N/A + * \see N/A + */ +extern const char *livebox_filename(struct livebox *handler); + +/*! + * \brief Get the package name of given livebox handler + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \return const char * + * \retval pkgname package name + * \retval NULL if the handler is not valid + * \pre N/A + * \post N/A + * \see N/A + */ +extern const char *livebox_pkgname(struct livebox *handler); + +/*! + * \brief Get the priority of a current content. + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \return double + * \retval 0.0f handler is NULL + * \retval -1.0f Handler is not valid (not yet initialized) + * \retval real number between 0.0 and 1.0 + * \pre N/A + * \post N/A + * \see N/A + */ +extern double livebox_priority(struct livebox *handler); + +/*! + * \brief Acquire the buffer of given livebox (Only for the buffer type) + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \return void * + * \retval address of a FB + * \retval NULL if it fails to get fb address + * \pre N/A + * \post N/A + * \see N/A + */ +extern void *livebox_acquire_fb(struct livebox *handler); + +/*! + * \brief Release the buffer of a livebox (Only for the buffer type) + * \details N/A + * \remarks N/A + * \param[in] buffer + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_SUCCESS Successfully done + * \pre N/A + * \post N/A + * \see livebox_acquire_fb + */ +extern int livebox_release_fb(void *buffer); + +/*! + * \brief Get the reference count of Livebox buffer (Only for the buffer type) + * \details N/A + * \remarks N/A + * \param[in] buffer + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_ERROR_FAULT Unrecoverable error occurred + * \retval refcnt positive integer including ZERO + * \pre N/A + * \post N/A + * \see livebox_pdfb_refcnt + */ +extern int livebox_fb_refcnt(void *buffer); + +/*! + * \brief Acquire the buffer of a PD frame (Only for the buffer type) + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \return int + * \retval NULL + * \retval adress of buffer of PD + * \pre N/A + * \post N/A + * \see livebox_release_pdfb + */ +extern void *livebox_acquire_pdfb(struct livebox *handler); + +/*! + * \brief Release the acquired buffer of the PD Frame (Only for the buffer type) + * \details N/A + * \remarks N/A + * \param[in] buffer + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_SUCCESS Successfully done + * \pre N/A + * \post N/A + * \see livebox_acquire_pdfb + */ +extern int livebox_release_pdfb(void *buffer); + +/*! + * \brief Reference count of given PD buffer (Only for the buffer type) + * \details N/A + * \remarks N/A + * \param[in] buffer + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_ERROR_FAULT Unrecoverable error occurred + * \retval reference count + * \pre N/A + * \post N/A + * \see livebox_fb_refcnt + */ +extern int livebox_pdfb_refcnt(void *buffer); + +/*! + * \brief Get the size of the Livebox + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \return int + * \retval LB_SIZE_TYPE_NxM + * \retval LB_SIZE_TYPE_INVALID + * \pre N/A + * \post N/A + * \see N/A + */ +extern int livebox_size(struct livebox *handler); + +/*! + * \brief Get the size of the Progressive Disclosure + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \param[out] w + * \param[out] h + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_SUCCESS Successfully done + * \pre N/A + * \post N/A + * \see N/A + */ +extern int livebox_get_pdsize(struct livebox *handler, int *w, int *h); + +/*! + * \brief List of supported sizes of given handler + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \param[out] cnt + * \param[out] size_list + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_SUCCESS Successfully done + * \pre N/A + * \post N/A + * \see N/A + */ +extern int livebox_get_supported_sizes(struct livebox *handler, int *cnt, int *size_list); + +/*! + * \brief BUFFER SIZE of the livebox if it is a buffer type + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval size of livebox buffer + * \pre N/A + * \post N/A + * \see N/A + */ +extern int livebox_lbfb_bufsz(struct livebox *handler); + +/*! + * \brief BUFFER SIZE of the progiressive disclosure if it is a buffer type + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval size of PD buffer + * \pre N/A + * \post N/A + * \see N/A + */ +extern int livebox_pdfb_bufsz(struct livebox *handler); + +/*! + * \brief Send the content event (for buffer type) to provider(livebox) + * \details N/A + * \remarks DEPRECATED + * Use the livebox_mouse_event function instead of this. + * \param[in] handler Handler of a livebox instance + * \param[in] type Event type + * \param[in] x coordinates of X axis + * \param[in] y coordinates of Y axis + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_ERROR_BUSY Previous operaion is not finished yet + * \retval LB_STATUS_ERROR_FAULT Unrecoverable error occurred + * \retval LB_STATUS_SUCCESS Successfully sent + * \pre N/A + * \post N/A + * \see livebox_mouse_event + * \see livebox_access_event + * \see livebox_key_event + */ +extern int livebox_content_event(struct livebox *handler, enum content_event_type type, double x, double y); + +/*! + * \brief Send the content event (for buffer type) to provider(livebox) + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \param[in] type Event type + * \param[in] x coordinates of X axis + * \param[in] y coordinates of Y axis + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_ERROR_BUSY Previous operation is not finished yet + * \retval LB_STATUS_ERROR_FAULT Unrecoverable error occurred + * \retval LB_STATUS_SUCCESS Successfully sent + * \pre N/A + * \post N/A + * \see livebox_content_event + * \see livebox_access_event + * \see livebox_key_event + */ +extern int livebox_mouse_event(struct livebox *handler, enum content_event_type type, double x, double y); + +/*! + * \brief Send the access event(for buffer type) to provider(livebox). + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \param[in] type Event type + * \param[in] x coordinates of X axsis + * \param[in] y coordinates of Y axsis + * \param[in] cb Result callback function + * \param[in] data Callback data + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_ERROR_BUSY Previous operation is not finished yet + * \retval LB_STATUS_ERROR_FAULT Unrecoverable error occurred + * \retval LB_STATUS_SUCCESS Successfully sent + * \pre N/A + * \post N/A + * \see livebox_mouse_event + * \see livebox_key_event + */ +extern int livebox_access_event(struct livebox *handler, enum access_event_type type, double x, double y, ret_cb_t cb, void *data); + +/*! + * \brief Send the key event(for buffer type) to provider(livebox). + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \param[in] type Key event type + * \param[in] keycode Code of key + * \param[in] cb Result callback + * \param[in] data Callback data + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_ERROR_BUSY Previous operation is not finished yet + * \retval LB_STATUS_ERROR_FAULT Unrecoverable error occurred + * \retval LB_STATUS_SUCCESS Successfully sent + * \pre N/A + * \post N/A + * \see livebox_mouse_event + * \see livebox_access_event + */ +extern int livebox_key_event(struct livebox *handler, enum content_event_type type, unsigned int keycode, ret_cb_t cb, void *data); + +/*! + * \brief Do pin up or not. + * \details + * If the livebox supports the pinup feature, + * you can freeze the update of given livebox. + * But it is different with pause. + * The box will be updated and it will decide wheter update its content or not when the pinup is on. + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \param[in] flag Pinup value + * \param[in] cb Result callback + * \param[in] data Callback data + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid parameters + * \see ret_cb_t + * \see livebox_set_visibility + * \see livebox_is_pinned_up + */ +extern int livebox_set_pinup(struct livebox *handler, int flag, ret_cb_t cb, void *data); + +/*! + * \brief Check the PIN-UP status of given handler + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid parameters + * \retval 1 box is pinned up + * \retval 0 box is not pinned up + * \see livebox_set_pinup + */ +extern int livebox_is_pinned_up(struct livebox *handler); + +/*! + * \brief Check the PINUP feature availability of the given handler + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval 1 if the box support Pinup feature + * \retval 0 if the box does not support the Pinup feature + * \pre N/A + * \post N/A + * \see livebox_is_pinned_up + * \see livebox_set_pinup + */ +extern int livebox_has_pinup(struct livebox *handler); + +/*! + * \brief Check the PD existence of given handler + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval 1 if the box support the PD + * \retval 0 if the box has no PD + * \pre N/A + * \post N/A + * \see N/A + */ +extern int livebox_has_pd(struct livebox *handler); + +/*! + * \brief Create the PD of given handler + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \param[in] cb Result callback + * \param[in] data Callback data + * \return int + * \retval LB_STATUS_SUCCESS Successfully done + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_ERROR_BUSY Previous operation is not finished yet + * \retval LB_STATUS_ERROR_FAULT Unrecoverable error occurred + * \pre N/A + * \post N/A + * \see ret_cb_t + * \see livebox_create_pd_with_position + * \see livebox_move_pd + * \see livebox_destroy_pd + */ +extern int livebox_create_pd(struct livebox *handler, ret_cb_t cb, void *data); + +/*! + * \brief Create the PD of given handler with the relative position from livebox + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \param[in] x 0.0 ~ 1.0 + * \param[in] y 0.0 ~ 1.0 + * \param[in] cb Result callback + * \param[in] data Callback data + * \return int + * \retval LB_STATUS_SUCCESS Successfully done + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_ERROR_BUSY Previous operation is not finished yet + * \retval LB_STATUS_ERROR_FAULT Unrecoverable error occurred + * \pre N/A + * \post N/A + * \see livebox_create_pd + * \see livebox_destroy_pd + * \see livebox_move_pd + */ +extern int livebox_create_pd_with_position(struct livebox *handler, double x, double y, ret_cb_t cb, void *data); + +/*! + * \brief PD position is updated. + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \param[in] x 0.0 ~ 1.0 + * \param[in] y 0.0 ~ 1.0 + * \return int + * \retval LB_STATUS_SUCCESS if succeed to send request for updating position of the PD. + * \retval LB_STATUS_ERROR_FAULT Unrecoverable error occurred + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \pre N/A + * \post N/A + * \see N/A + */ +extern int livebox_move_pd(struct livebox *handler, double x, double y); + +/*! + * \brief Destroy the PD of given handler if it is created. + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \param[in] cb + * \param[in] data + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_ERROR_FAULT Unrecoverable error occurred + * \retval LB_STATUS_SUCCESS Successfully done + * \pre N/A + * \post N/A + * \see ret_cb_t + */ +extern int livebox_destroy_pd(struct livebox *handler, ret_cb_t cb, void *data); + +/*! + * \brief Check the create status of given livebox handler + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval 0 PD is not created + * \retval 1 PD is created + */ +extern int livebox_pd_is_created(struct livebox *handler); + +/*! + * \brief Check the content type of the progressive disclosure of given handler + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \return int + * \retval PD_TYPE_BUFFER Contents of a PD is based on canvas buffer(shared) + * \retval PD_TYPE_TEXT Contents of a PD is based on formatted text file + * \retval PD_TYPE_PIXMAP Contents of a livebox is shared by the pixmap(depends on X) + * \retval PD_TYPE_INVALID + * \pre N/A + * \post N/A + * \see livebox_pd_type + */ +extern enum livebox_pd_type livebox_pd_type(struct livebox *handler); + +/*! + * \brief Check the existence of a livebox about given package name + * \details N/A + * \remarks N/A + * \param[in] pkgname + * \return int + * \retval 1 if the box is exists + * \retval 0 if the box is not exists + * \pre N/A + * \post N/A + * \see N/A + */ +extern int livebox_is_exists(const char *pkgname); + +/*! + * \brief Set function table for parsing the text content of a livebox + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \param[in] ops + * \return int + * \retval LB_STATUS_SUCCESS Successfully done + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \see livebox_set_pd_text_handler + */ +extern int livebox_set_text_handler(struct livebox *handler, struct livebox_script_operators *ops); + +/*! + * \brief Set function table for parsing the text content of a Progressive Disclosure + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \param[in] ops + * \return int + * \retval LB_STATUS_SUCCESS Successfully done + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \see livebox_set_text_handler + */ +extern int livebox_set_pd_text_handler(struct livebox *handler, struct livebox_script_operators *ops); + +/*! + * \brief Emit a text signal to given livebox only if it is a text type. + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \param[in] emission Emission string + * \param[in] source Source string + * \param[in] sx start X + * \param[in] sy start Y + * \param[in] ex end X + * \param[in] ey end Y + * \param[in] cb Result callback + * \param[in] data Callback data + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid parameters + * \retval LB_STATUS_ERROR_FAULT Unrecoverable error occurred + * \retval LB_STATUS_SUCCESS Successfully emitted + * \see ret_cb_t + */ +extern int livebox_emit_text_signal(struct livebox *handler, const char *emission, const char *source, double sx, double sy, double ex, double ey, ret_cb_t cb, void *data); + +/*! + * \brief Set a private data pointer to carry it using given handler + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \param[in] data data pointer + * \return int + * \retval LB_STATUS_SUCCESS Successfully registered + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \pre N/A + * \post N/A + * \see livebox_get_data + */ +extern int livebox_set_data(struct livebox *handler, void *data); + +/*! + * \brief Get private data pointer which is carried by given handler + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \return void * + * \retval data pointer + * \retval NULL if there is not data + * \pre N/A + * \post N/A + * \see livebox_set_data + */ +extern void *livebox_get_data(struct livebox *handler); + +/*! + * \brief Subscribe the event for liveboxes only in given cluster and sub-cluster + * \details + * If you wrote a view-only client. + * you can receive the event of specific liveboxes which are grouped in given cluster/category + * But you cannot modify their attributes (such as size, ...). + * \remarks N/A + * \param[in] cluster "*" can be used for subscribe all cluster's liveboxes event. + * If you use the "*", value in the category will be ignored. + * \param[in] category "*" can be used for subscribe liveboxes events of all category(sub-cluster) in given "cluster" + * \return int + * \retval LB_STATUS_ERROR_FAULT Unrecoverable error occurred + * \retval LB_STATUS_SUCCESS Successfully requested + * \pre N/A + * \post N/A + * \see livebox_unsubscribe_group + */ +extern int livebox_subscribe_group(const char *cluster, const char *category); + +/*! + * \brief Unsubscribe the event for the liveboxes, but you will receive already added liveboxes event. + * \details N/A + * \remarks N/A + * \param[in] cluster "*" can be used for subscribe all cluster's liveboxes event. + * If you use the "*", value in the category will be ignored. + * \param[in] category "*" can be used for subscribe all sub-cluster's liveboxes event in given "cluster" + * \return int + * \retval LB_STATUS_ERROR_FAULT Unrecoverable error occurred + * \retval LB_STATUS_SUCCESS Successfully requested + * \pre N/A + * \post N/A + * \see livebox_subscribe_group + */ +extern int livebox_unsubscribe_group(const char *cluster, const char *category); + +/*! + * \brief Refresh the group(cluster/sub-cluser(aka. category)) + * \details + * This function will trigger the update of all liveboxes in given cluster/category group + * \remarks + * Basically default livebox system doesn't use the cluster/category concept. + * But you can use it. so if you decide to use it then you can trigger the update of all liveboxes in given group. + * \param[in] cluster Cluster ID + * \param[in] category Sub-cluster ID + * \param[in] force 1 if the boxes should be updated even if they are paused + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_ERROR_FAULT Unrecoverable error occurred + * \retval LB_STATUS_SUCCESS Successfully requested + * \pre N/A + * \post N/A + * \see livebox_refresh + */ +extern int livebox_refresh_group(const char *cluster, const char *category, int force); + +/*! + * \brief Refresh a livebox + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \param[in] force 1 if the box should be updated even if it is paused + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_ERROR_FAULT Unrecoverable error occurred + * \retval LB_STATUS_SUCCESS Successfully requested + * \pre N/A + * \post N/A + * \see livebox_refresh_group + */ +extern int livebox_refresh(struct livebox *handler, int force); + +/*! + * \brief Pixmap Id of a livebox content + * \details + * This function doesn't guarantees the life-cycle of the pixmap. + * If the service provider destroy the pixmap, you will not know about it. + * So you should validate it before access it. + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \return int + * \retval 0 if the pixmap is not created + * \retval pixmap Pixmap Id need to be casted to (unsigned int) type + * \pre N/A + * \post N/A + * \see livebox_pd_pixmap + */ +extern int livebox_lb_pixmap(const struct livebox *handler); + +/*! + * \brief Pixmap Id of a PD content + * \details + * This function doesn't guarantees the life-cycle of the pixmap. + * If the service provider destroy the pixmap, you will not know about it. + * So you should validate it before access it. + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \return int + * \retval 0 if the pixmap is not created + * \retval pixmap Pixmap Id need to be casted to (unsigned int) type + * \pre N/A + * \post N/A + * \see livebox_lb_pixmap + */ +extern int livebox_pd_pixmap(const struct livebox *handler); + +/*! + * \brief Acquire the pixmap of PD + * \details + * After acquire the pixmap of PD, it will not be destroyed + * So if the new update is comming with new pixmap Id, you should release old pixmap manually + * \remarks N/A + * \param[in] handler Handler of a livebox instance. + * \param[in] cb Result callback for acquiring request + * \param[in] data Callback Data + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_ERROR_FAULT Failed to send a request to the service provider or there is critical error that is unrecoverable + * \retval LB_STATUS_SUCCESS Successfully requested to acquire the pixmap of PD + * \pre N/A + * \post N/A + * \see livebox_release_pd_pixmap + * \see livebox_acquire_lb_pixmap + * \see ret_cb_t + */ +extern int livebox_acquire_pd_pixmap(struct livebox *handler, ret_cb_t cb, void *data); + +/*! + * \brief Release the acquired pixmap ID + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance, This can be NULL, only if the handler is deleted. + * \param[in] pixmap Pixmap Id to release it + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_ERROR_FAULT Unrecoverable error occurred + * \retval LB_STATUS_SUCCESS Successfully released (request is sent) + * \pre N/A + * \post N/A + * \see livebox_acquire_pd_pixmap + * \see livebox_release_lb_pixmap + */ +extern int livebox_release_pd_pixmap(struct livebox *handler, int pixmap); + +/*! + * \brief Getting the PIXMAP of a livebox + * \details + * Even if the render process release the pixmap, the pixmap will be kept before released by livebox_release_lb_pixmap + * You should release the pixmap manually. + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \param[in] cb Callback function which will be called with result of acquiring lb pixmap + * \param[in] data Callback data + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_ERROR_FAULT Unrecoverable error occurred + * \retval LB_STATUS_SUCCESS Successfully requested + * \pre + * Livebox service system should support the PIXMAP type buffer. + * The livebox should be designed to use the buffer (script type) + * \post N/A + * \see livebox_release_lb_pixmap + * \see livebox_acquire_pd_pixmap + * \see ret_cb_t + */ +extern int livebox_acquire_lb_pixmap(struct livebox *handler, ret_cb_t cb, void *data); + +/*! + * \brief Release the pixmap of a livebox + * \details + * After the client gets new pixmap or no more need to keep current pixmap, use this to release it. + * If there is no user for given pixmap, the pixmap will be destroyed. + * \remarks N/A + * \param[in] handler Handler of a livebox instance, This can be NULL, only if the handler is deleted. + * \param[in] pixmap Pixmap Id of given livebox handler + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_ERROR_FAULT Unrecoverable error occurred + * \retval LB_STATUS_SUCCESS Successfully done + * \pre + * The pixmap should be acquired by livebox_acquire_lb_pixmap + * \post N/A + * \see livebox_acquire_lb_pixmap + * \see livebox_release_pd_pixmap + */ +extern int livebox_release_lb_pixmap(struct livebox *handler, int pixmap); + +/*! + * \brief Update the visible state of a livebox + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \param[in] state Configure the current visible state of a livebox + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_ERROR_BUSY + * \retval LB_STATUS_ERROR_PERMISSION + * \retval LB_STATUS_ERROR_ALREADY + * \retval LB_STATUS_ERROR_FAULT Unrecoverable error occurred + * \retval LB_STATUS_SUCCESS Successfully done + * \pre N/A + * \post N/A + * \see N/A + */ +extern int livebox_set_visibility(struct livebox *handler, enum livebox_visible_state state); + +/*! + * \brief Current visible state of a livebox + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \return livebox_visible_state + * \retval LB_SHOW Livebox is showed. Default state + * \retval LB_HIDE Livebox is hide, Update timer is not be freezed. but you cannot receive any updates events. you should refresh(reload) the content of a livebox when you make this show again + * \retval LB_HIDE_WITH_PAUSE Livebix is hide, it will paused the update timer, but if a livebox update its contents, update event will come to you + * \retval LB_VISIBLE_ERROR To enlarge the size of this enumeration type + * \pre N/A + * \post N/A + * \see N/A + */ +extern enum livebox_visible_state livebox_visibility(struct livebox *handler); + +/*! + * \brief Set the update mode of current livebox + * \details N/A + * \remarks N/A + * if you set 1 for active update mode, you should get buffer without updated event from provider. + * But is passive mode, you have to update content of a box when you get updated event. + * Default is Passive mode. + * \param[in] handler Handler of a livebox instance + * \param[in] active_update 1 means active update, 0 means passive update (default) + * \param[in] cb Result callback function + * \param[in] data Callback data + * \return int + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_ERROR_BUSY + * \retval LB_STATUS_ERROR_PERMISSION + * \retval LB_STATUS_ERROR_ALREADY + * \retval LB_STATUS_ERROR_FAULT Unrecoverable error occurred + * \retval LB_STATUS_SUCCESS Successfully done + * \pre N/A + * \post N/A + * \see ret_cb_t + */ +extern int livebox_set_update_mode(struct livebox *handler, int active_update, ret_cb_t cb, void *data); + +/*! + * \brief Is this box in the active update mode? + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \return int + * \retval 0 if passive mode + * \retval 1 if active mode or error code + * \pre N/A + * \post N/A + * \see N/A + */ +extern int livebox_is_active_update(struct livebox *handler); + +/*! + * \brief Sync manually + * \details N/A + * \remarks N/A + * param[in] handler Handler of a livebox instance + * \return void + * \retval LB_STATUS_SUCCESS If success + * \retval LB_STATUS_ERROR_INVALID Invalid handle + * \pre N/A + * \post N/A + * \see livebox_set_manual_sync + * \see livebox_manual_sync + * \see livebox_sync_lb_fb + */ +extern int livebox_sync_pd_fb(struct livebox *handler); + +/*! + * \brief Sync manually + * \details N/A + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \return void + * \retval LB_STATUS_SUCCESS If success + * \retval LB_STATUS_ERROR_INVALID Invalid handle + * \pre N/A + * \post N/A + * \see livebox_set_manual_sync + * \see livebox_manual_sync + * \see livebox_sync_pd_fb + */ +extern int livebox_sync_lb_fb(struct livebox *handler); + +/*! + * \brief Getting the alternative icon of given livebox instance. + * \details If the box should be represented as a shortcut icon, this function will get the alternative icon. + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \return const char * + * \retval address Absolute path of an alternative icon file + * \retval NULL Livebox has no alternative icon file + * \pre N/A + * \post N/A + * \see livebox_alt_name + */ +extern const char *livebox_alt_icon(struct livebox *handler); + +/*! + * \brief Getting the alternative name of given livebox instance. + * \details If the box should be represented as a shortcut name, this function will get the alternative name. + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \return const char * + * \retval name Alternative name of a livebox + * \retval NULL Livebox has no alternative name + * \pre N/A + * \post N/A + * \see livebox_alt_icon + */ +extern const char *livebox_alt_name(struct livebox *handler); + +/*! + * \brief Get the lock for frame buffer. + * \details + * This function should be used to prevent from rendering to the frame buffer while reading it. + * And the locking area should be short and must be released ASAP + * Or the render thread will be hang'd. + * \remarks + * \param[in] handler Handler of a livebox instance + * \param[in] is_pd 1 for PD or 0 + * \return int + * \retval LB_STATUS_ERROR_FAULT Unrecoverable error occurred + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_SUCCESS Successfully done + * \pre N/A + * \post N/A + * \see livebox_release_fb_lock + */ +extern int livebox_acquire_fb_lock(struct livebox *handler, int is_pd); + +/*! + * \brief Release the lock of frame buffer + * \details + * This function should be called ASAP after acquire a lock of FB. + * Or the render process will be blocked. + * \remarks N/A + * \param[in] handler Handler of a livebox instance + * \param[in] is_pd 1 for PD or 0 + * \return int + * \retval LB_STATUS_ERROR_FAULT Unrecoverable error occurred + * \retval LB_STATUS_ERROR_INVALID Invalid argument + * \retval LB_STATUS_SUCCESS Successfully done + * \pre N/A + * \post N/A + * \see livebox_acquire_fb_lock + */ +extern int livebox_release_fb_lock(struct livebox *handler, int is_pd); + +/*! + * \brief Set options for controlling livebox sub-system. + * \details + * LB_OPTION_FRAME_DROP_FOR_RESIZE + * While resizing the box, viewer doesn't want to know the updated frames of old size content anymore, + * In that case, turn this on, the provider will not send the updated event to the viewer about old content. + * So the viewer can reduce its burden to update unnecessary frames + * LB_OPTION_MANUAL_SYNC + * If you don't want updates frame automatically, + * Only you want reload the frames by your hands,(manually) + * Turn it on. + * After turnned it on, you should sync it using + * livebox_sync_pd_fb + * livebox_sync_lb_pfb + * LB_OPTION_SHARED_CONTENT + * If this option is turnned on, even though you create a new livebox, + * If there are already added same instance has same content, the instance will not be created again + * Instead of creating a new instance, viewer will provides old instance with new handle. + * \remarks N/A + * \param[in] option option which will be affected by this call + * \param[in] state new value for given option + * \return int + * \retval LB_STATUS_ERROR_INVALID Unknown option + * \retval LB_STATUS_ERROR_FAULT Failed to change the state of option + * \retval LB_STATUS_SUCCESS Successfully changed + * \pre N/A + * \post N/A + * \see livebox_get_option + * \see livebox_sync_pd_fb + * \see livebox_sync_lb_fb + */ +extern int livebox_set_option(enum livebox_option_type option, int state); + +/*! + * \brief Get options livebox sub-system + * \details N/A + * \remarks N/A + * \param[in] option type of option + * \return int + * \retval LB_STATUS_ERROR_INVALID invalid option + * \retval LB_STATUS_ERROR_FAULT Failed to get option + * \retval >=0 Value of given option. must has to be >=0 + * \pre N/A + * \post N/A + * \see livebox_set_option + */ +extern int livebox_option(enum livebox_option_type option); + + +/*! + * \brief Set a handler for launching an app for auto-launch feature + * \details If a user clicks a box, and the box uses auto-launch option, the launcher_handler will be called. + * \remarks N/A + * \param[in] launch_handler Handler for launching an app manually + * \param[in] data Callback data which will be given a data for launch_handler + * \return int + * \retval + * \pre N/A + * \post N/A + * \see N/A + */ +extern int livebox_set_auto_launch_handler(int (*launch_handler)(struct livebox *handler, const char *appid, void *data), void *data); + +/*! + * \} + */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/livebox_internal.h b/include/livebox_internal.h new file mode 100644 index 0000000..a6f92f9 --- /dev/null +++ b/include/livebox_internal.h @@ -0,0 +1,255 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +extern void lb_invoke_event_handler(struct livebox *handler, enum livebox_event_type event); +extern void lb_invoke_fault_handler(enum livebox_fault_type type, const char *pkgname, const char *filename, const char *function); + +extern struct livebox_common *lb_find_common_handle(const char *pkgname, const char *filename); +extern struct livebox *lb_new_livebox(const char *pkgname, const char *id, double timestamp, const char *cluster, const char *category); +extern struct livebox_common *lb_find_common_handle_by_timestamp(double timestamp); + +extern int lb_set_group(struct livebox_common *common, const char *cluster, const char *category); +extern void lb_set_size(struct livebox_common *common, int w, int h); +extern void lb_set_pdsize(struct livebox_common *common, int w, int h); +extern void lb_set_default_pdsize(struct livebox_common *common, int w, int h); +extern int lb_set_content(struct livebox_common *common, const char *content); +extern int lb_set_title(struct livebox_common *handler, const char *title); +extern void lb_set_auto_launch(struct livebox_common *handler, const char *auto_launch); +extern void lb_set_id(struct livebox_common *handler, const char *id); +extern void lb_set_size_list(struct livebox_common *handler, int size_list); +extern void lb_set_priority(struct livebox_common *handler, double priority); +extern int lb_set_lb_fb(struct livebox_common *handler, const char *filename); +extern int lb_set_pd_fb(struct livebox_common *handler, const char *filename); +extern struct fb_info *lb_get_pd_fb(struct livebox_common *handler); +extern struct fb_info *lb_get_lb_fb(struct livebox_common *handler); +extern void lb_set_user(struct livebox_common *handler, int user); +extern void lb_set_pinup(struct livebox_common *handler, int pinup); +extern void lb_set_text_lb(struct livebox_common *handler); +extern void lb_set_text_pd(struct livebox_common *handler); +extern int lb_text_lb(struct livebox_common *handler); +extern int lb_text_pd(struct livebox_common *handler); +extern void lb_set_period(struct livebox_common *handler, double period); +extern void lb_set_update_mode(struct livebox_common *handler, int active_mode); +extern void lb_set_filename(struct livebox_common *handler, const char *filename); +extern void lb_set_alt_info(struct livebox_common *handler, const char *icon, const char *name); +extern int lb_destroy_lock_file(struct livebox_common *common, int is_pd); +extern int lb_create_lock_file(struct livebox_common *common, int is_pd); +extern int lb_destroy_common_handle(struct livebox_common *common); +extern struct livebox_common *lb_create_common_handle(struct livebox *handle, const char *pkgname, const char *cluster, const char *category); +extern int lb_sync_pd_fb(struct livebox_common *common); +extern int lb_sync_lb_fb(struct livebox_common *common); +extern int lb_common_unref(struct livebox_common *common, struct livebox *handle); +extern int lb_common_ref(struct livebox_common *common, struct livebox *handle); + +extern struct livebox *lb_ref(struct livebox *handler); +extern struct livebox *lb_unref(struct livebox *handler, int destroy_common); +extern int lb_send_delete(struct livebox *handler, int type, ret_cb_t cb, void *data); +extern int lb_delete_all(void); + +enum lb_type { /*!< Must have to be sync with data-provider-master */ + _LB_TYPE_NONE = 0x0, + _LB_TYPE_SCRIPT, + _LB_TYPE_FILE, + _LB_TYPE_TEXT, + _LB_TYPE_BUFFER +}; + +enum pd_type { /*!< Must have to be sync with data-provider-master */ + _PD_TYPE_NONE = 0x0, + _PD_TYPE_SCRIPT, + _PD_TYPE_TEXT, + _PD_TYPE_BUFFER +}; + +enum livebox_state { + CREATE = 0xBEEFbeef, + DELETE = 0xDEADdead, /* Delete only for this client */ + DESTROYED = 0x00DEAD00 +}; + +struct livebox_common { + enum livebox_state state; + + struct dlist *livebox_list; + int refcnt; + + char *cluster; + char *category; + + char *pkgname; + char *id; + + char *content; + char *title; + char *filename; + + double timestamp; + + struct alt_info { + char *icon; + char *name; + } alt; + + enum livebox_delete_type delete_type; + + int is_user; + int is_pd_created; + int is_pinned_up; + int is_active_update; + enum livebox_visible_state visible; + + struct { + enum lb_type type; + struct fb_info *fb; + + int size_list; + + int width; + int height; + double priority; + + char *auto_launch; + double period; + int pinup_supported; + int mouse_event; + + /* For the filtering event */ + double x; + double y; + char *lock; + int lock_fd; + } lb; + + struct { + enum pd_type type; + struct fb_info *fb; + + int width; + int height; + + int default_width; + int default_height; + + /* For the filtering event */ + double x; + double y; + char *lock; + int lock_fd; + } pd; + + int nr_of_sizes; + + struct requested_flag { + unsigned int created:1; + unsigned int deleted:1; + unsigned int pinup:1; + unsigned int group_changed:1; + unsigned int period_changed:1; + unsigned int size_changed:1; + unsigned int pd_created:1; + unsigned int pd_destroyed:1; + unsigned int update_mode:1; + unsigned int access_event:1; + unsigned int key_event:1; + + /*! + * \note + * Reserved + */ + unsigned int reserved:21; + } request; +}; + +struct job_item { + struct livebox *handle; + ret_cb_t cb; + int ret; + void *data; +}; + +struct livebox { + enum livebox_state state; + + int refcnt; + int paused_updating; + + enum livebox_visible_state visible; + struct livebox_common *common; + + void *data; + + struct callback_table { + struct livebox_script_operators lb_ops; + struct livebox_script_operators pd_ops; + + struct created { + ret_cb_t cb; + void *data; + } created; + + struct deleted { + ret_cb_t cb; + void *data; + } deleted; + + struct pinup { + ret_cb_t cb; + void *data; + } pinup; + + struct group_changed { + ret_cb_t cb; + void *data; + } group_changed; + + struct period_changed { + ret_cb_t cb; + void *data; + } period_changed; + + struct size_changed { + ret_cb_t cb; + void *data; + } size_changed; + + struct pd_created { + ret_cb_t cb; + void *data; + } pd_created; + + struct pd_destroyed { + ret_cb_t cb; + void *data; + } pd_destroyed; + + struct update_mode { + ret_cb_t cb; + void *data; + } update_mode; + + struct access_event { + ret_cb_t cb; + void *data; + } access_event; + + struct key_event { + ret_cb_t cb; + void *data; + } key_event; + } cbs; +}; + +/* End of a file */ diff --git a/include/master_rpc.h b/include/master_rpc.h new file mode 100644 index 0000000..d850bfd --- /dev/null +++ b/include/master_rpc.h @@ -0,0 +1,24 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +extern int master_rpc_async_request(struct livebox *handler, struct packet *packet, int urgent, void (*ret_cb)(struct livebox *handler, const struct packet *result, void *data), void *data); +extern int master_rpc_sync_request(struct packet *packet); +extern void master_rpc_check_and_fire_consumer(void); +extern int master_rpc_request_only(struct livebox *handler, struct packet *packet); +extern int master_rpc_clear_fault_package(const char *pkgname); +extern int master_rpc_clear_all_request(void); + +/* End of a file */ diff --git a/include/util.h b/include/util.h new file mode 100644 index 0000000..b66b542 --- /dev/null +++ b/include/util.h @@ -0,0 +1,32 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +extern int util_check_extension(const char *filename, const char *check_ptr); +extern double util_timestamp(void); +extern const char *util_basename(const char *name); +extern int util_validate_livebox_package(const char *pkgname); +extern const char *util_uri_to_path(const char *uri); +extern int util_unlink(const char *filename); + +#define SCHEMA_FILE "file://" +#define SCHEMA_PIXMAP "pixmap://" +#define SCHEMA_SHM "shm://" + +#define container_of(ptr, type, member) \ + ({ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +/* End of a file */ diff --git a/live.viewer/CMakeLists.txt b/live.viewer/CMakeLists.txt new file mode 100644 index 0000000..9be5a14 --- /dev/null +++ b/live.viewer/CMakeLists.txt @@ -0,0 +1,48 @@ +PROJECT(live-viewer C) +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + +INCLUDE(FindPkgConfig) +pkg_check_modules(pkgs REQUIRED + capi-appfw-application + capi-appfw-app-manager + ail + bundle + dlog + elementary + ecore-x + appcore-efl + livebox-viewer + livebox-service +) + +FOREACH(flag ${pkgs_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Wall -Werror -Winline -g") +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}") + +SET(PKGROOT "/opt/usr/apps/live.viewer") +ADD_DEFINITIONS("-DNDEBUG") +ADD_DEFINITIONS("-DPKGROOT=\"${PKGROOT}\"") +#ADD_DEFINITIONS("-DFLOG") +ADD_DEFINITIONS("-DLOG_TAG=\"${PROJECT_NAME}\"") +ADD_DEFINITIONS(${pkgs_CFLAGS}) +ADD_DEFINITIONS(${pkgs_LDFLAGS}) + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) + +ADD_EXECUTABLE(${PROJECT_NAME} + src/main.c + src/dlist.c + src/live_scroller.c + src/util.c + src/scroller.c + src/lb.c +) + +TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS}) +INSTALL(TARGETS ${PROJECT_NAME} DESTINATION ${PKGROOT}/bin) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/live.viewer.xml DESTINATION /opt/share/packages) + +ADD_SUBDIRECTORY(res) diff --git a/live.viewer/image/folder b/live.viewer/image/folder new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/live.viewer/image/folder diff --git a/live.viewer/include/debug.h b/live.viewer/include/debug.h new file mode 100644 index 0000000..2efc6dc --- /dev/null +++ b/live.viewer/include/debug.h @@ -0,0 +1,27 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !defined(FLOG) +#define DbgPrint(format, arg...) SECURE_LOGD("[[32m%s/%s[0m:%d] " format, util_basename(__FILE__), __func__, __LINE__, ##arg) +#define ErrPrint(format, arg...) SECURE_LOGE("[[32m%s/%s[0m:%d] " format, util_basename(__FILE__), __func__, __LINE__, ##arg) +#else +extern FILE *__file_log_fp; +#define DbgPrint(format, arg...) do { fprintf(__file_log_fp, "[LOG] [[32m%s/%s[0m:%d] " format, util_basename(__FILE__), __func__, __LINE__, ##arg); fflush(__file_log_fp); } while (0) + +#define ErrPrint(format, arg...) do { fprintf(__file_log_fp, "[ERR] [[32m%s/%s[0m:%d] " format, util_basename(__FILE__), __func__, __LINE__, ##arg); fflush(__file_log_fp); } while (0) +#endif + +/* End of a file */ diff --git a/live.viewer/include/dlist.h b/live.viewer/include/dlist.h new file mode 100644 index 0000000..cd1a421 --- /dev/null +++ b/live.viewer/include/dlist.h @@ -0,0 +1,43 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define dlist_remove_data(list, data) do { \ + struct dlist *l; \ + l = dlist_find_data(list, data); \ + list = dlist_remove(list, l); \ +} while (0) + +#define dlist_foreach(list, l, data) \ + for ((l) = (list); (l) && ((data) = dlist_data(l)); (l) = dlist_next(l)) + +#define dlist_foreach_safe(list, l, n, data) \ + for ((l) = (list), (n) = dlist_next(l); \ + (l) && ((data) = dlist_data(l)); \ + (l) = (n), (n) = dlist_next(l)) + +struct dlist; + +extern struct dlist *dlist_append(struct dlist *list, void *data); +extern struct dlist *dlist_prepend(struct dlist *list, void *data); +extern struct dlist *dlist_remove(struct dlist *list, struct dlist *l); +extern struct dlist *dlist_find_data(struct dlist *list, void *data); +extern void *dlist_data(struct dlist *l); +extern struct dlist *dlist_next(struct dlist *l); +extern struct dlist *dlist_prev(struct dlist *l); +extern int dlist_count(struct dlist *l); +extern struct dlist *dlist_nth(struct dlist *l, int nth); + +/* End of a file */ diff --git a/live.viewer/include/lb.h b/live.viewer/include/lb.h new file mode 100644 index 0000000..37c48ac --- /dev/null +++ b/live.viewer/include/lb.h @@ -0,0 +1,21 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +extern int lb_init(void); +extern int lb_fini(void); +extern int lb_add(Evas_Object *sc, const char *pkgname); + +/* End of a file */ diff --git a/live.viewer/include/live_scroller.h b/live.viewer/include/live_scroller.h new file mode 100644 index 0000000..02b2450 --- /dev/null +++ b/live.viewer/include/live_scroller.h @@ -0,0 +1,57 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +struct live_sc_event_info { + int curidx; + int toidx; +}; + +struct live_sc_drag_info { + int dx; + int dy; +}; + +struct live_sc_move_info { + Evas_Object *item; + Evas_Coord x; + Evas_Coord y; + Evas_Coord w; + Evas_Coord h; + + double relx; + double rely; +}; + +extern Evas_Object *live_scroller_add(Evas_Object *parent); +extern int live_scroller_append(Evas_Object *scroller, Evas_Object *item); +extern Evas_Object *live_scroller_remove(Evas_Object *scroller, int idx); +extern Evas_Object *live_scroller_get_item(Evas_Object *scroller, int idx); +extern int live_scroller_get_current(Evas_Object *scroller); +extern int live_scroller_loop_set(Evas_Object *scroller, int is_loop); + +extern int live_scroller_freeze(Evas_Object *scroller); +extern int live_scroller_thaw(Evas_Object *scroller); + +extern int live_scroller_anim_to(Evas_Object *scroller, double fps, int offset); +extern int live_scroller_go_to(Evas_Object *scroller, int idx); + +extern int live_scroller_update(Evas_Object *scroller); + +extern int live_scroller_remove_by_obj(Evas_Object *scroller, Evas_Object *obj); +extern int live_scroller_get_item_index(Evas_Object *scroller, Evas_Object *item); +extern int live_scroller_get_item_count(Evas_Object *scroller); + +/* End of a file */ diff --git a/live.viewer/include/main.h b/live.viewer/include/main.h new file mode 100644 index 0000000..3c142be --- /dev/null +++ b/live.viewer/include/main.h @@ -0,0 +1,17 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +extern Evas_Object *main_get_window(void); diff --git a/live.viewer/include/scroller.h b/live.viewer/include/scroller.h new file mode 100644 index 0000000..f69575f --- /dev/null +++ b/live.viewer/include/scroller.h @@ -0,0 +1,44 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +extern Evas_Object *scroller_create(Evas_Object *parent); +extern Evas_Object *scroller_peek_by_idx(Evas_Object *sc, int idx); +extern int scroller_peek_by_obj(Evas_Object *sc, Evas_Object *obj); +extern int scroller_append(Evas_Object *sc, Evas_Object *child); +extern int scroller_get_current_idx(Evas_Object *sc); +extern int scroller_peek_by_obj(Evas_Object *sc, Evas_Object *obj); +extern Evas_Object *scroller_get_page(Evas_Object *sc, int idx); +extern int scroller_is_scrolling(Evas_Object *sc); + +extern int scroller_add_stop_cb(Evas_Object *scroller, int (*cb)(Evas_Object *sc, void *data), void *data); +extern void scroller_del_stop_cb(Evas_Object *scroller, int (*cb)(Evas_Object *sc, void *data), void *data); + +extern int scroller_get_page_index(Evas_Object *sc, Evas_Object *page); + +extern void scroller_unlock(Evas_Object *sc); +extern void scroller_lock(Evas_Object *sc); + +extern int scroller_get_page_count(Evas_Object *sc); +extern int scroller_scroll_to(Evas_Object *sc, int idx); +extern int scroller_jump_to(Evas_Object *sc, int idx); + +extern int scroller_destroy(Evas_Object *sc); +extern int scroller_update(Evas_Object *sc, void *data); +extern int scroller_fast_scroll(Evas_Object *sc, int idx); +extern void scroller_loop_set(Evas_Object *sc, Eina_Bool val); +extern void scroller_quick_navi(Evas_Object *sc, Eina_Bool val); + +/* End of a file */ diff --git a/live.viewer/include/util.h b/live.viewer/include/util.h new file mode 100644 index 0000000..79ab46b --- /dev/null +++ b/live.viewer/include/util.h @@ -0,0 +1,19 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +extern const char *util_basename(const char *name); + +/* End of a file */ diff --git a/live.viewer/live.viewer.xml b/live.viewer/live.viewer.xml new file mode 100644 index 0000000..7a55872 --- /dev/null +++ b/live.viewer/live.viewer.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8" ?> +<manifest xmlns="http://tizen.org/ns/packages" package="live.viewer" version="0.0.1" install-location="auto"> + <label>Live viewer (native)</label> + <author email="nicesj.park@samsung.com" href="www.samsung.com">Sung-jae Park</author> + <description>Live box simple viewer (native)</description> + + <ui-application appid="live.viewer" exec="/opt/usr/apps/live.viewer/bin/live-viewer" nodisplay="false" multiple="false" type="capp" taskmanage="true"> + <icon>live-viewer.png</icon> + <label>Live box simple viewer (native)</label> + <label xml:lang="en-us">Live box simple viewer (native)</label> + </ui-application> +</manifest> diff --git a/live.viewer/packaging/live.viewer.spec b/live.viewer/packaging/live.viewer.spec new file mode 100644 index 0000000..f817c32 --- /dev/null +++ b/live.viewer/packaging/live.viewer.spec @@ -0,0 +1,41 @@ +Name: live.viewer +Summary: viewer +Version: 0.0.1 +Release: 1 +Group: main/app +License: Flora License +Source0: %{name}-%{version}.tar.gz +BuildRequires: cmake, gettext-tools +BuildRequires: pkgconfig(dlog) +BuildRequires: pkgconfig(aul) +BuildRequires: pkgconfig(ail) +BuildRequires: pkgconfig(elementary) +BuildRequires: pkgconfig(appcore-efl) +BuildRequires: pkgconfig(livebox-viewer) +BuildRequires: pkgconfig(ecore-x) +BuildRequires: pkgconfig(livebox-service) +BuildRequires: pkgconfig(bundle) +BuildRequires: pkgconfig(capi-appfw-application) +BuildRequires: pkgconfig(capi-appfw-app-manager) +BuildRequires: edje-bin + +%description +Livebox viewer development library + +%prep +%setup -q + +%build +cmake . -DCMAKE_INSTALL_PREFIX=%{_prefix} +make %{?jobs:-j%jobs} + +%install +rm -rf %{buildroot} +%make_install + +%post + +%files +%defattr(-,root,root,-) +/opt/usr/apps/live.viewer/* +/opt/share/* diff --git a/live.viewer/res/CMakeLists.txt b/live.viewer/res/CMakeLists.txt new file mode 100644 index 0000000..5f6447c --- /dev/null +++ b/live.viewer/res/CMakeLists.txt @@ -0,0 +1,7 @@ +ADD_CUSTOM_TARGET(live-viewer.edj ALL + COMMAND edje_cc -id ${CMAKE_SOURCE_DIR}/res/image + ${CMAKE_CURRENT_SOURCE_DIR}/live-viewer.edc live-viewer.edj + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/live-viewer.edc +) +ADD_DEPENDENCIES(${PROJECT_NAME} live-viewer.edj) +INSTALL(FILES live-viewer.edj DESTINATION ${PKGROOT}/res/edje) diff --git a/live.viewer/res/live-viewer.edc b/live.viewer/res/live-viewer.edc new file mode 100644 index 0000000..fac023d --- /dev/null +++ b/live.viewer/res/live-viewer.edc @@ -0,0 +1,175 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +images { + /* image: "a" COMP; */ +} + +collections { + group { + name: "layout"; + parts { + part { + name: "indicator"; + type: RECT; + mouse_events: 0; + description { + state: "default" 0.0; + rel1 { relative: 0.0 0.0; } + rel2 { relative: 1.0 100/1280; } + color: 0 0 0 0; + } + } + + part { + name: "delete,btn"; + type: SWALLOW; + mouse_events: 1; + description { + state: "default" 0.0; + rel1 { relative: 4/720 1.0; to_y, "viewer"; } + rel2 { relative: 716/720 900/1280; } + } + } + + part { + name: "controller"; /* size list */ + type: SWALLOW; + mouse_events: 1; + description { + state: "default" 0.0; + rel1 { relative: 0.0 1.0; to_y, "delete,btn"; } + rel2 { relative: 0.3 1.0; } + } + } + + part { + name: "logger"; + type: SWALLOW; + mouse_events: 1; + description { + state: "default" 0.0; + rel1 { relative: 1.0 1.0; to_x: "controller"; to_y: "delete,btn"; } + rel2 { relative: 1.0 1.0; } + } + } + + part { + name: "viewer"; + type: RECT; + mouse_events: 1; + description { + state: "default" 0.0; + rel1 { relative: 4/720 1.0; to, "indicator"; } + rel2 { relative: 716/720 800/1280; } + color: 255 255 255 255; + } + } + + part { + name: "event,blocker"; + type: RECT; + mouse_events: 1; + description { + state: "default" 0.0; + rel1 { relative: 0.0 0.0; } + rel2 { relative: 1.0 1.0; } + color: 0 0 0 0; + visible: 0; + } + description { + state: "show" 0.0; + inherit: "default" 0.0; + color: 50 50 50 50; + visible: 1; + } + } + + part { + name: "livebox"; + type: SWALLOW; + mouse_events: 1; + description { + state: "default" 0.0; + rel1 { relative: 0.4 0.4; to, "viewer"; } + rel2 { relative: 0.6 0.6; to, "viewer"; } + } + } + + part { + name: "pd"; + type: SWALLOW; + mouse_events: 1; + description { + state: "default" 0.0; + rel1 { relative: 0.0 0.95; to_y, "livebox"; } + rel2 { relative: 1.0 1.0; to_y, "livebox"; } + visible: 0; + align: 0.0 0.0; + } + + description { + state: "show" 0.0; + rel1 { relative: 0.0 1.0; to_y, "livebox"; } + rel2 { relative: 1.0 1.0; } + visible: 1; + align: 0.0 0.0; + } + } + } + + programs { + program { + name: "open,pd"; + source: "pd"; + signal: "open"; + action: STATE_SET "show" 0.0; + target: "pd"; + target: "event,blocker"; + transition: LINEAR 0.2; + } + program { + name: "hide,pd"; + source: "pd"; + signal: "close"; + action: STATE_SET "default" 0.0; + target: "pd"; + target: "event,blocker"; + transition: LINEAR 0.1; + after: "hide,pd,done"; + } + + program { + name: "hide,pd,done"; + action: SIGNAL_EMIT "hide,done" "pd"; + } + + program { + name: "event,blocker"; + source: "event,blocker"; + signal: "mouse,clicked,1"; + action: SIGNAL_EMIT "close" "pd"; + } + + program { + name: "pd,close,pd"; + source: "viewer"; + signal: "mouse,clicked,1"; + action: SIGNAL_EMIT "close" "pd"; + } + } + } +} diff --git a/live.viewer/src/dlist.c b/live.viewer/src/dlist.c new file mode 100644 index 0000000..fa3082a --- /dev/null +++ b/live.viewer/src/dlist.c @@ -0,0 +1,180 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +#include "dlist.h" + +/*! + * \brief + * This dlist is called Modified Doubly Linked List. + * + * Noramlly, The dobule linked list contains address of previous and next element. + * This dlist also contains them, but the tail element only contains prev address. + * + * The head element's prev pointer indicates the last element. + * But the last element's next pointer indicates NIL. + * + * So we can find the last element while crawling this DList + * But we have to remember the address of the head element. + */ + +struct dlist { + struct dlist *next; + struct dlist *prev; + void *data; +}; + +struct dlist *dlist_append(struct dlist *list, void *data) +{ + struct dlist *item; + + item = malloc(sizeof(*item)); + if (!item) + return NULL; + + item->next = NULL; + item->data = data; + + if (!list) { + item->prev = item; + + list = item; + } else { + item->prev = list->prev; + item->prev->next = item; + list->prev = item; + } + + assert(!list->prev->next && "item NEXT"); + + return list; +} + +struct dlist *dlist_prepend(struct dlist *list, void *data) +{ + struct dlist *item; + + item = malloc(sizeof(*item)); + if (!item) + return NULL; + + item->data = data; + + if (!list) { + item->prev = item; + item->next = NULL; + } else { + if (list->prev->next) + list->prev->next = item; + + item->prev = list->prev; + item->next = list; + + list->prev = item; + + } + + return item; +} + +struct dlist *dlist_remove(struct dlist *list, struct dlist *l) +{ + if (!list || !l) + return NULL; + + if (l == list) + list = l->next; + else + l->prev->next = l->next; + + if (l->next) + l->next->prev = l->prev; + /*! + * \note + * If the removed entry 'l' has no next element, it is the last element. + * In this case, check the existence of the list first, + * and if the list is not empty, update the 'prev' of the list (which is a head element of the list) + * + * If we didn't care about this, the head element(list) can indicates the invalid element. + */ + else if (list) + list->prev = l->prev; + + free(l); + return list; +} + +struct dlist *dlist_find_data(struct dlist *list, void *data) +{ + struct dlist *l; + void *_data; + + dlist_foreach(list, l, _data) { + if (data == _data) + return l; + } + + return NULL; +} + +void *dlist_data(struct dlist *l) +{ + return l ? l->data : NULL; +} + +struct dlist *dlist_next(struct dlist *l) +{ + return l ? l->next : NULL; +} + +struct dlist *dlist_prev(struct dlist *l) +{ + return l ? l->prev : NULL; +} + +int dlist_count(struct dlist *l) +{ + register int i; + struct dlist *n; + void *data; + + i = 0; + dlist_foreach(l, n, data) { + i++; + } + + return i; +} + +struct dlist *dlist_nth(struct dlist *l, int nth) +{ + register int i; + struct dlist *n; + + i = 0; + for (n = l; n; n = n->next) { + if (i == nth) + return n; + i++; + } + + return NULL; +} + +/* End of a file */ diff --git a/live.viewer/src/lb.c b/live.viewer/src/lb.c new file mode 100644 index 0000000..9870b4a --- /dev/null +++ b/live.viewer/src/lb.c @@ -0,0 +1,966 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <Elementary.h> +#include <Ecore_X.h> + +#include <dlog.h> + +#include <livebox.h> +#include <livebox-service.h> + +#include "main.h" +#include "util.h" +#include "debug.h" +#include "lb.h" +#include "scroller.h" + +#define FLICK_COND 100 + +static Evas_Object *create_canvas(Evas_Object *parent) +{ + Evas_Object *canvas; + + canvas = evas_object_image_add(evas_object_evas_get(parent)); + if (!canvas) + return NULL; + + evas_object_image_content_hint_set(canvas, EVAS_IMAGE_CONTENT_HINT_DYNAMIC); + evas_object_image_colorspace_set(canvas, EVAS_COLORSPACE_ARGB8888); + evas_object_image_alpha_set(canvas, EINA_TRUE); + evas_object_move(canvas, 0, 0); + return canvas; +} + +static int update_pd_canvas(struct livebox *handle, Evas_Object *image) +{ + Evas_Native_Surface surface; + int w; + int h; + + DbgPrint("Updated\n"); + + switch (livebox_pd_type(handle)) { + case PD_TYPE_PIXMAP: + h = w = 0; + livebox_get_pdsize(handle, &w, &h); + if (w <= 0 || h <= 0) + break; + + //evas_object_image_size_set(image, w, h); + + DbgPrint("Update: %dx%d\n", w, h); + surface.version = EVAS_NATIVE_SURFACE_VERSION; + surface.type = EVAS_NATIVE_SURFACE_X11; + surface.data.x11.pixmap = livebox_pd_pixmap(handle); + surface.data.x11.visual = ecore_x_default_visual_get(ecore_x_display_get(), ecore_x_default_screen_get()); + evas_object_image_native_surface_set(image, &surface); + + evas_object_image_data_update_add(image, 0, 0, w, h); + evas_object_size_hint_max_set(image, w, h); + evas_object_resize(image, w, h); + evas_object_show(image); + break; + case PD_TYPE_BUFFER: + livebox_get_pdsize(handle, &w, &h); + if (w > 0 && h > 0) { + void *data; + + data = livebox_acquire_pdfb(handle); + if (data) { + evas_object_image_data_set(image, NULL); + evas_object_image_colorspace_set(image, EVAS_COLORSPACE_ARGB8888); + evas_object_image_alpha_set(image, EINA_TRUE); + evas_object_image_size_set(image, w, h); + evas_object_image_smooth_scale_set(image, EINA_TRUE); + evas_object_image_data_copy_set(image, data); + evas_object_image_data_update_add(image, 0, 0, w, h); + livebox_release_pdfb(data); + } + evas_object_resize(image, w, h); + //evas_object_size_hint_min_set(image, w, h); + evas_object_size_hint_max_set(image, w, h); + } + break; + case PD_TYPE_TEXT: + default: + break; + } + + return 0; +} + +static int update_canvas(struct livebox *handle, Evas_Object *image) +{ + Evas_Native_Surface surface; + const char *filename; + int w; + int h; + int type; + + DbgPrint("Updated\n"); + + switch (livebox_lb_type(handle)) { + case LB_TYPE_PIXMAP: + w = h = 0; + type = livebox_size(handle); + livebox_service_get_size(type, &w, &h); + if (w <= 0 || h <= 0) + break; + + DbgPrint("Update: %dx%d\n", w, h); + + //evas_object_image_size_set(image, w, h); + + surface.version = EVAS_NATIVE_SURFACE_VERSION; + surface.type = EVAS_NATIVE_SURFACE_X11; + surface.data.x11.pixmap = livebox_lb_pixmap(handle); + surface.data.x11.visual = ecore_x_default_visual_get(ecore_x_display_get(), ecore_x_default_screen_get()); + evas_object_image_native_surface_set(image, &surface); + + evas_object_image_data_update_add(image, 0, 0, w, h); + + evas_object_resize(image, w, h); + evas_object_size_hint_min_set(image, w, h); + evas_object_size_hint_max_set(image, w, h); + evas_object_show(image); + break; + case LB_TYPE_BUFFER: + w = h = 0; + type = livebox_size(handle); + livebox_service_get_size(type, &w, &h); + if (w > 0 && h > 0) { + void *data; + data = livebox_acquire_fb(handle); + if (data) { + evas_object_image_data_set(image, NULL); + evas_object_image_colorspace_set(image, EVAS_COLORSPACE_ARGB8888); + evas_object_image_alpha_set(image, EINA_TRUE); + evas_object_image_size_set(image, w, h); + evas_object_image_smooth_scale_set(image, EINA_TRUE); + evas_object_image_data_copy_set(image, data); + evas_object_image_data_update_add(image, 0, 0, w, h); + livebox_release_fb(data); + } + evas_object_resize(image, w, h); + evas_object_size_hint_min_set(image, w, h); + evas_object_size_hint_max_set(image, w, h); + } + break; + case LB_TYPE_IMAGE: + filename = livebox_filename(handle); + if (filename) { + const char *old; + evas_object_image_file_get(image, &old, NULL); + if (old && !strcmp(filename, old)) { + evas_object_image_reload(image); + } else { + evas_object_image_file_set(image, filename, NULL); + } + + w = h = 0; + type = livebox_size(handle); + livebox_service_get_size(type, &w, &h); + if (w > 0 && h > 0) { + evas_object_resize(image, w, h); + evas_object_size_hint_min_set(image, w, h); + evas_object_size_hint_max_set(image, w, h); + } + } + break; + case LB_TYPE_TEXT: + default: + break; + } + + return 0; +} + +static inline void prepend_log(struct livebox *handle, const char *buffer) +{ + Evas_Object *layout; + Evas_Object *logger; + + layout = livebox_get_data(handle); + if (!layout) { + ErrPrint("Failed to get layout\n"); + return; + } + + logger = elm_object_part_content_get(layout, "logger"); + if (logger) + elm_list_item_prepend(logger, buffer, NULL, NULL, NULL, NULL); +} + +static int lb_event_cb(struct livebox *handle, enum livebox_event_type event, void *data) +{ + Evas_Object *layout; + Evas_Object *sc; + Evas_Object *pd; + Evas_Object *image; + + layout = (Evas_Object *)livebox_get_data(handle); + if (!layout) + return -EFAULT; + + switch (event) { + case LB_EVENT_LB_UPDATED: + DbgPrint("Contents: [%s]\n", livebox_content(handle)); + image = elm_object_part_content_get(layout, "livebox"); + if (image) + update_canvas(handle, image); + break; + case LB_EVENT_PD_UPDATED: + image = elm_object_part_content_get(layout, "pd"); + if (image) + update_pd_canvas(handle, image); + break; + case LB_EVENT_DELETED: + sc = evas_object_data_del(layout, "sc"); + if (sc) + scroller_peek_by_obj(sc, layout); + + evas_object_del(layout); + break; + case LB_EVENT_PD_DESTROYED: + pd = elm_object_part_content_unset(layout, "pd"); + if (pd) + evas_object_del(pd); + + sc = evas_object_data_del(layout, "sc"); + if (sc) + scroller_unlock(sc); + break; + default: + break; + } + + return 0; +} + +static int lb_fault_cb(enum livebox_fault_type event, + const char *pkgname, const char *filename, + const char *funcname, void *data) +{ + DbgPrint("pkgname: %s\nfilename: %s\n:funcname: %s\n", pkgname, filename, funcname); + return 0; +} + +static void del_cb(struct livebox *handle, int ret, void *data) +{ + Evas_Object *layout = data; + Evas_Object *sc; + + sc = evas_object_data_del(layout, "sc"); + if (sc) { + DbgPrint("Scroller: %p\n", sc); + scroller_peek_by_obj(sc, layout); + } + + DbgPrint("Delete a layout object\n"); + evas_object_del(layout); +} + +static void delete_btn_cb(void *handle, Evas_Object *obj, void *event_info) +{ + int ret; + Evas_Object *layout; + layout = livebox_get_data(handle); + DbgPrint("Livebox Get Data %p - %p\n", handle, layout); + ret = livebox_del(handle, del_cb, layout); + if (ret < 0) { + char buffer[256]; + snprintf(buffer, sizeof(buffer), "Delete returns: %d\n", ret); + prepend_log(handle, buffer); + } +} + +static void error_popup(Evas_Object *parent, struct livebox *handle, int ret) +{ + ErrPrint("Failed to add a box: %d\n", ret); + return; +} + +static void resize_cb(struct livebox *handle, int ret, void *data) +{ + Evas_Object *layout; + Evas_Object *log_list; + char buffer[256]; + + layout = livebox_get_data(handle); + if (!layout) + return; + + log_list = elm_object_part_content_get(layout, "logger"); + if (!log_list) + return; + + snprintf(buffer, sizeof(buffer) - 1, "Resize: %d", ret); + elm_list_item_prepend(log_list, buffer, NULL, NULL, NULL, NULL); +} + +static void resize_click_cb(void *handle, Evas_Object *obj, void *event_info) +{ + Elm_Object_Item *item; + const char *label; + int w; + int h; + int size_type; + + item = elm_list_selected_item_get(obj); + if (!item) + return; + + label = elm_object_item_part_text_get(item, NULL); + if (!label) + return; + + sscanf(label, "%dx%d", &w, &h); + size_type = livebox_service_size_type(w, h); + + livebox_resize(handle, size_type, resize_cb, NULL); +} + +static void create_resize_controller(struct livebox *handle, Evas_Object *layout) +{ + Evas_Object *size_list; + char buffer[256]; + int sizes[NR_OF_SIZE_LIST]; + int cnt; + int i; + int w; + int h; + + size_list = elm_list_add(layout); + cnt = sizeof(sizes) / sizeof(sizes[0]); + livebox_get_supported_sizes(handle, &cnt, sizes); + for (i = 0; i < cnt; i++) { + livebox_service_get_size(sizes[i], &w, &h); + snprintf(buffer, sizeof(buffer) - 1, "%dx%d", w, h); + + elm_list_item_append(size_list, buffer, NULL, NULL, resize_click_cb, handle); + } + evas_object_show(size_list); + elm_list_go(size_list); + evas_object_size_hint_weight_set(size_list, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(size_list, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_object_part_content_set(layout, "controller", size_list); +} + +static void create_logger(struct livebox *handle, Evas_Object *layout) +{ + Evas_Object *log_list; + + log_list = elm_list_add(layout); + if (!log_list) + return; + + elm_list_item_prepend(log_list, "Created", NULL, NULL, NULL, NULL); + evas_object_show(log_list); + elm_list_go(log_list); + + evas_object_size_hint_weight_set(log_list, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(log_list, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_object_part_content_set(layout, "logger", log_list); +} + +struct event_data { + Evas_Coord x; + Evas_Coord y; + + enum flick { + FLICK_UNKNOWN = 0x00, + FLICK_DOWN = 0x01, + FLICK_UP = 0x02, + } flick; +}; + +static void pd_closed_cb(struct livebox *handle, int ret, void *data) +{ + evas_object_del(data); +} + +static void pd_hide_done_cb(void *data, Evas_Object *obj, const char *emission, const char *source) +{ + Evas_Object *pd; + Evas_Object *sc; + + sc = evas_object_data_get(obj, "sc"); + scroller_unlock(sc); + + elm_object_signal_callback_del(obj, emission, source, pd_hide_done_cb); + pd = elm_object_part_content_unset(obj, "pd"); + livebox_destroy_pd(data, pd_closed_cb, pd); +} + +static void pd_mouse_up_cb(void *handle, Evas *e, Evas_Object *obj, void *event_info) +{ + Evas_Event_Mouse_Up *up = event_info; + Evas_Coord x, y, w, h; + double rx, ry; + + evas_object_geometry_get(obj, &x, &y, &w, &h); + + rx = (double)(up->canvas.x - x) / (double)w; + ry = (double)(up->canvas.y - y) / (double)h; + DbgPrint("%dx%d - %dx%d, %lfx%lf\n", x, y, w, h, rx, ry); + livebox_content_event(handle, PD_MOUSE_UP, rx, ry); +} + +static void pd_mouse_down_cb(void *handle, Evas *e, Evas_Object *obj, void *event_info) +{ + Evas_Event_Mouse_Down *down = event_info; + Evas_Coord x, y, w, h; + double rx, ry; + + evas_object_geometry_get(obj, &x, &y, &w, &h); + rx = (double)(down->canvas.x - x) / (double)w; + ry = (double)(down->canvas.y - y) / (double)h; + DbgPrint("%dx%d - %dx%d, %lfx%lf\n", x, y, w, h, rx, ry); + livebox_content_event(handle, PD_MOUSE_DOWN, rx, ry); +} + +static void pd_mouse_move_cb(void *handle, Evas *e, Evas_Object *obj, void *event_info) +{ + Evas_Event_Mouse_Move *move = event_info; + Evas_Coord x, y, w, h; + double rx, ry; + + evas_object_geometry_get(obj, &x, &y, &w, &h); + rx = (double)(move->cur.canvas.x - x) / (double)w; + ry = (double)(move->cur.canvas.y - y) / (double)h; + DbgPrint("%dx%d - %dx%d, %lfx%lf\n", x, y, w, h, rx, ry); + livebox_content_event(handle, PD_MOUSE_MOVE, rx, ry); +} + +static void pd_created_cb(struct livebox *handle, int ret, void *data) +{ + Evas_Object *layout; + Evas_Object *pd_image; + Evas_Object *sc; + + layout = (Evas_Object *)livebox_get_data(handle); + if (!layout) + return; + + sc = evas_object_data_get(layout, "sc"); + + pd_image = create_canvas(layout); + if (!pd_image) + return; + + evas_object_event_callback_add(pd_image, EVAS_CALLBACK_MOUSE_UP, pd_mouse_up_cb, handle); + evas_object_event_callback_add(pd_image, EVAS_CALLBACK_MOUSE_DOWN, pd_mouse_down_cb, handle); + evas_object_event_callback_add(pd_image, EVAS_CALLBACK_MOUSE_MOVE, pd_mouse_move_cb, handle); + + update_pd_canvas(handle, pd_image); + + elm_object_signal_callback_add(layout, "hide,done", "pd", pd_hide_done_cb, handle); + elm_object_part_content_set(layout, "pd", pd_image); + elm_object_signal_emit(layout, "open", "pd"); + scroller_lock(sc); +} + +static void lb_mouse_up_cb(void *handle, Evas *e, Evas_Object *obj, void *event_info) +{ + Evas_Event_Mouse_Up *up = event_info; + Evas_Coord x, y, w, h; + struct event_data *evt; + + evt = evas_object_data_del(obj, "evt"); + if (!evt) + return; + + evas_object_geometry_get(obj, &x, &y, &w, &h); + + if (livebox_lb_type(handle) == LB_TYPE_PIXMAP || livebox_lb_type(handle) == LB_TYPE_BUFFER) { + double rx, ry; + rx = (double)(up->canvas.x - x) / (double)w; + ry = (double)(up->canvas.y - y) / (double)h; + livebox_content_event(handle, LB_MOUSE_UP, rx, ry); + } + + if (x < up->canvas.x && up->canvas.x < x + w) { + if (y < up->canvas.y && up->canvas.y < y + h) { + livebox_click(handle, (double)x / (double)w, (double)y / (double)h); + } + } + + if (evt->flick == FLICK_DOWN && (up->canvas.y - evt->y) > (FLICK_COND>>1)) { + int ret; + /* Open PD */ + ret = livebox_create_pd_with_position(handle, 0.5, 0.0, pd_created_cb, NULL); + } + + free(evt); +} + +static void lb_mouse_down_cb(void *handle, Evas *e, Evas_Object *obj, void *event_info) +{ + struct event_data *evt; + Evas_Event_Mouse_Down *down = event_info; + Evas_Object *layout; + Evas_Object *sc; + + layout = livebox_get_data(handle); + if (!layout) + return; + + sc = evas_object_data_get(layout, "sc"); + if (!sc) + return; + + if (scroller_is_scrolling(sc)) + return; + + if (livebox_lb_type(handle) == LB_TYPE_PIXMAP || livebox_lb_type(handle) == LB_TYPE_BUFFER) { + Evas_Coord x, y, w, h; + double rx, ry; + + evas_object_geometry_get(obj, &x, &y, &w, &h); + rx = (double)(down->canvas.x - x) / (double)w; + ry = (double)(down->canvas.y - y) / (double)h; + livebox_content_event(handle, LB_MOUSE_DOWN, rx, ry); + } + + evt = evas_object_data_get(obj, "evt"); + if (evt) { + ErrPrint("Huh?"); + } else { + evt = malloc(sizeof(*evt)); + if (!evt) { + ErrPrint("Heap: %s\n", strerror(errno)); + return; + } + } + + evas_object_data_set(obj, "evt", evt); + + evt->x = down->canvas.x; + evt->y = down->canvas.y; + evt->flick = FLICK_UNKNOWN; +} + +static void lb_mouse_move_cb(void *handle, Evas *e, Evas_Object *obj, void *event_info) +{ + Evas_Event_Mouse_Move *move = event_info; + struct event_data *evt; + + evt = evas_object_data_get(obj, "evt"); + if (!evt) + return; + + if (livebox_lb_type(handle) == LB_TYPE_PIXMAP || livebox_lb_type(handle) == LB_TYPE_BUFFER) { + Evas_Coord x, y, w, h; + double rx, ry; + + evas_object_geometry_get(obj, &x, &y, &w, &h); + rx = (double)(move->cur.canvas.x - x) / (double)w; + ry = (double)(move->cur.canvas.y - y) / (double)h; + livebox_content_event(handle, LB_MOUSE_MOVE, rx, ry); + } + + if ((move->cur.canvas.x - move->prev.canvas.x) > FLICK_COND) { + evt->flick = FLICK_UNKNOWN; + return; + } else if ((move->cur.canvas.x - move->prev.canvas.x) < -FLICK_COND) { + evt->flick = FLICK_UNKNOWN; + return; + } + + if ((move->cur.canvas.y - move->prev.canvas.y) > 0) { + if (evt->flick != FLICK_DOWN) + evt->flick = FLICK_DOWN; + } else if ((move->cur.canvas.y - move->prev.canvas.y) < 0) { + if (evt->flick != FLICK_UP) + evt->flick = FLICK_UP; + } +} + +static int lb_update_begin(struct livebox *handle) +{ + DbgPrint("%p\n", handle); + + Evas_Object *layout; + Evas_Object *list; + char buffer[80]; + + layout = (Evas_Object *)livebox_get_data(handle); + if (!layout) { + ErrPrint("Failed to get layout object\n"); + return 0; + } + + list = elm_object_part_content_get(layout, "livebox"); + if (!list) { + ErrPrint("Failed to get list\n"); + return 0; + } + + snprintf(buffer, sizeof(buffer), "begin"); + elm_list_item_prepend(list, buffer, NULL, NULL, NULL, NULL); + + return 0; +} + +static int lb_update_end(struct livebox *handle) +{ + DbgPrint("%p\n", handle); + + Evas_Object *layout; + Evas_Object *list; + char buffer[80]; + + layout = (Evas_Object *)livebox_get_data(handle); + if (!layout) { + ErrPrint("Failed to get layout object\n"); + return 0; + } + + list = elm_object_part_content_get(layout, "livebox"); + if (!list) { + ErrPrint("Failed to get list\n"); + return 0; + } + + snprintf(buffer, sizeof(buffer), "end"); + elm_list_item_prepend(list, buffer, NULL, NULL, NULL, NULL); + + return 0; +} + +static int lb_update_text(struct livebox *handle, const char *id, const char *part, const char *data) +{ + DbgPrint("%p\n", handle); + DbgPrint("id: [%s], part[%s], data[%s]\n", id, part, data); + + Evas_Object *layout; + Evas_Object *list; + char buffer[80]; + + layout = (Evas_Object *)livebox_get_data(handle); + if (!layout) { + ErrPrint("Failed to get layout object\n"); + return 0; + } + + list = elm_object_part_content_get(layout, "livebox"); + if (!list) { + ErrPrint("Failed to get list\n"); + return 0; + } + + snprintf(buffer, sizeof(buffer), "%s=%s", part, data); + elm_list_item_prepend(list, buffer, NULL, NULL, NULL, NULL); + + return 0; +} + +static int lb_update_image(struct livebox *handle, const char *id, const char *part, const char *data) +{ + DbgPrint("%p\n", handle); + DbgPrint("id: [%s], part[%s], data[%s]\n", id, part, data); + + Evas_Object *layout; + Evas_Object *list; + char buffer[80]; + + layout = (Evas_Object *)livebox_get_data(handle); + if (!layout) { + ErrPrint("Failed to get layout object\n"); + return 0; + } + + list = elm_object_part_content_get(layout, "livebox"); + if (!list) { + ErrPrint("Failed to get list\n"); + return 0; + } + + snprintf(buffer, sizeof(buffer), "%s=%s", part, data); + elm_list_item_prepend(list, buffer, NULL, NULL, NULL, NULL); + + return 0; +} + +static int lb_update_script(struct livebox *handle, const char *id, const char *part, const char *file, const char *group) +{ + DbgPrint("%p\n", handle); + DbgPrint("id: [%s], part[%s], file[%s], group[%s]\n", id, part, file, group); + + Evas_Object *layout; + Evas_Object *list; + char buffer[80]; + + layout = (Evas_Object *)livebox_get_data(handle); + if (!layout) { + ErrPrint("Failed to get layout object\n"); + return 0; + } + + list = elm_object_part_content_get(layout, "livebox"); + if (!list) { + ErrPrint("Failed to get list\n"); + return 0; + } + + snprintf(buffer, sizeof(buffer), "%s=%s, %s", part, file, group); + elm_list_item_prepend(list, buffer, NULL, NULL, NULL, NULL); + + return 0; +} + +static int lb_update_signal(struct livebox *handle, const char *id, const char *emission, const char *signal) +{ + DbgPrint("%p\n", handle); + DbgPrint("id: [%s], emission[%s], signal[%s]\n", id, emission, signal); + + Evas_Object *layout; + Evas_Object *list; + char buffer[80]; + + layout = (Evas_Object *)livebox_get_data(handle); + if (!layout) { + ErrPrint("Failed to get layout object\n"); + return 0; + } + + list = elm_object_part_content_get(layout, "livebox"); + if (!list) { + ErrPrint("Failed to get list\n"); + return 0; + } + + snprintf(buffer, sizeof(buffer), "%s,%s", emission, signal); + elm_list_item_prepend(list, buffer, NULL, NULL, NULL, NULL); + return 0; +} + +static int lb_update_drag(struct livebox *handle, const char *id, const char *part, double dx, double dy) +{ + DbgPrint("%p\n", handle); + DbgPrint("id: [%s], part[%s], pos[%lfx%lf]\n", id, part, dx, dy); + + Evas_Object *layout; + Evas_Object *list; + char buffer[80]; + + layout = (Evas_Object *)livebox_get_data(handle); + if (!layout) { + ErrPrint("Failed to get layout object\n"); + return 0; + } + + list = elm_object_part_content_get(layout, "livebox"); + if (!list) { + ErrPrint("Failed to get list\n"); + return 0; + } + + snprintf(buffer, sizeof(buffer), "%s=%lfx%lf", part, dx, dy); + elm_list_item_prepend(list, buffer, NULL, NULL, NULL, NULL); + return 0; +} + +static int lb_update_info_size(struct livebox *handle, const char *id, int w, int h) +{ + DbgPrint("%p\n", handle); + DbgPrint("id: [%s], size[%dx%d]\n", id, w, h); + + Evas_Object *layout; + Evas_Object *list; + char buffer[80]; + + layout = (Evas_Object *)livebox_get_data(handle); + if (!layout) { + ErrPrint("Failed to get layout object\n"); + return 0; + } + + list = elm_object_part_content_get(layout, "livebox"); + if (!list) { + ErrPrint("Failed to get list\n"); + return 0; + } + + snprintf(buffer, sizeof(buffer), "%dx%d", w, h); + elm_list_item_prepend(list, buffer, NULL, NULL, NULL, NULL); + return 0; +} + +static int lb_update_info_category(struct livebox *handle, const char *id, const char *category) +{ + DbgPrint("%p\n", handle); + DbgPrint("id: [%s], category: [%s]\n", id, category); + + Evas_Object *layout; + Evas_Object *list; + char buffer[80]; + + layout = (Evas_Object *)livebox_get_data(handle); + if (!layout) { + ErrPrint("Failed to get layout object\n"); + return 0; + } + + list = elm_object_part_content_get(layout, "livebox"); + if (!list) { + ErrPrint("Failed to get list\n"); + return 0; + } + + snprintf(buffer, sizeof(buffer), "%s=%s", id, category); + elm_list_item_prepend(list, buffer, NULL, NULL, NULL, NULL); + return 0; +} + +static void livebox_added_cb(struct livebox *handle, int ret, void *data) +{ + Evas_Object *layout; + Evas_Object *btn; + int w; + int h; + int type; + int idx; + + DbgPrint("%s - %d\n", livebox_pkgname(handle), ret); + + if (ret != 0) { + error_popup(main_get_window(), handle, ret); + return; + } + + layout = elm_layout_add(main_get_window()); + if (!layout) { + ErrPrint("Failed to add a layout\n"); + return; + } + + if (elm_layout_file_set(layout, PKGROOT "/res/edje/live-viewer.edj", "layout") == EINA_FALSE) { + DbgPrint("Failed to add a layout\n"); + evas_object_del(layout); + return; + } + + if (livebox_lb_type(handle) == LB_TYPE_TEXT) { + Evas_Object *list; + static struct livebox_script_operators ops = { + .update_begin = lb_update_begin, + .update_end = lb_update_end, + .update_text = lb_update_text, + .update_image = lb_update_image, + .update_script = lb_update_script, + .update_signal = lb_update_signal, + .update_drag = lb_update_drag, + .update_info_size = lb_update_info_size, + .update_info_category = lb_update_info_category, + }; + + list = elm_list_add(layout); + if (!list) { + evas_object_del(layout); + return; + } + + (void)livebox_set_text_handler(handle, &ops); + elm_object_part_content_set(layout, "livebox", list); + elm_list_go(list); + } else { + Evas_Object *lb_image; + lb_image = create_canvas(layout); + if (!lb_image) { + ErrPrint("Failed to create a canvas\n"); + evas_object_del(layout); + return; + } + + evas_object_event_callback_add(lb_image, EVAS_CALLBACK_MOUSE_UP, lb_mouse_up_cb, handle); + evas_object_event_callback_add(lb_image, EVAS_CALLBACK_MOUSE_DOWN, lb_mouse_down_cb, handle); + evas_object_event_callback_add(lb_image, EVAS_CALLBACK_MOUSE_MOVE, lb_mouse_move_cb, handle); + + w = 0; + h = 0; + type = livebox_size(handle); + if (type != LB_SIZE_TYPE_UNKNOWN) { + livebox_service_get_size(type, &w, &h); + DbgPrint("%dx%d\n", w, h); + } + evas_object_resize(lb_image, w, h); + evas_object_show(lb_image); + + update_canvas(handle, lb_image); + + btn = elm_button_add(main_get_window()); + if (btn) { + elm_object_text_set(btn, "Delete"); + evas_object_smart_callback_add(btn, "clicked", delete_btn_cb, handle); + elm_object_part_content_set(layout, "delete,btn", btn); + } + + elm_object_part_content_set(layout, "livebox", lb_image); + } + evas_object_resize(layout, 720, 1280); + evas_object_show(layout); + + create_resize_controller(handle, layout); + create_logger(handle, layout); + + livebox_set_data(handle, layout); + DbgPrint("Livebox Set Data: %p - %p\n", handle, layout); + evas_object_data_set(layout, "sc", data); + + scroller_append(data, layout); + + idx = scroller_get_page_index(data, layout); + DbgPrint("Scroll to %d\n", idx); + scroller_scroll_to(data, idx); +} + +int lb_add(Evas_Object *sc, const char *pkgname) +{ + int w, h; + struct livebox *handle; + + evas_object_geometry_get(sc, NULL, NULL, &w, &h); + + DbgPrint("sc: %dx%d, package: %s\n", w, h, pkgname); + livebox_activate(pkgname, NULL, NULL); + + handle = livebox_add(pkgname, "default", "user,created", "default", + DEFAULT_PERIOD, livebox_added_cb, sc); + if (!handle) { + ErrPrint("Failed to add a new livebox\n"); + return -EFAULT; + } + + return 0; +} + +int lb_init(void) +{ + livebox_init(ecore_x_display_get()); + livebox_set_event_handler(lb_event_cb, NULL); + livebox_set_fault_handler(lb_fault_cb, NULL); + return 0; +} + +int lb_fini(void) +{ + livebox_fini(); + return 0; +} + +/* End of a file */ diff --git a/live.viewer/src/live_scroller.c b/live.viewer/src/live_scroller.c new file mode 100644 index 0000000..2b88939 --- /dev/null +++ b/live.viewer/src/live_scroller.c @@ -0,0 +1,1457 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <Elementary.h> + +#include <dlog.h> + +#include "live_scroller.h" +#include "util.h" +#include "debug.h" + +#define SAMPLE_MAX 10 +#define EVT_PERIOD 0.016 /* 60 fps */ +#define EVT_SAMPLE_PERIOD 9 +#define DRAG_SENS 100 +#define ANIM_MIN 40 +#define ANIM_UNIT 15 + +struct item_list_entry { + struct item_list_entry *prev; + struct item_list_entry *next; + + Evas_Object *data; + Evas_Coord x; + Evas_Coord y; + Evas_Coord w; + Evas_Coord h; +}; + +struct evt_info { + Evas_Coord x; + unsigned int timestamp; +}; + +struct evt_queue { + struct evt_info ei[SAMPLE_MAX]; + int front; + int rear; + int cnt; + unsigned int old_timestamp; +}; + +struct widget_data { + Eina_Bool is_loop; + Eina_Bool is_freezed; + + struct item_list_entry *item_list; + + int item_cnt; + struct item_list_entry *curlist; + struct item_list_entry *tolist; + + Eina_Bool drag_started; + Eina_Bool is_pressed; + Evas_Coord press_x; + Evas_Coord press_y; + + Ecore_Timer *sc_anim_timer; + int sc_anim_dx; + + Evas_Object *clip; + Evas_Object *evt_layer; + Evas_Object *scroller; + + Evas_Coord clip_bx; + Evas_Coord clip_bw; + + struct evt_queue evtq; + Ecore_Timer *evt_emulator; + Evas_Coord old_x; + unsigned int prev_timestamp; +}; + +#ifdef PROFILE +#define PROFILE_START() \ +static int _exec_cnt = 0; \ +struct timeval _stv, _etv; \ +long _elapsed; \ +gettimeofday(&_stv, NULL); + +#define PROFILE_END() \ +do { \ + _exec_cnt++; \ + gettimeofday(&_etv, NULL); \ + _elapsed = (_etv.tv_sec - _stv.tv_sec) * 1000000l + (_etv.tv_usec - _stv.tv_usec); \ + DbgPrint("[%d] Elapsed time: %lu\n", _exec_cnt, _elapsed); \ +} while (0) +#else +#define PROFILE_START() +#define PROFILE_END() +#endif + +#define LIST_NEXT(list) ((list)->next) +#define LIST_PREV(list) ((list)->prev) +#define LIST_DATA(list) ((list) ? (list)->data : NULL) + +static inline void LIST_ITEM_GEO_SET(struct item_list_entry *list, int x, int y, int w, int h) +{ + list->x = x; + list->y = y; + list->w = w; + list->h = h; +} + +static inline void LIST_ITEM_GEO_GET(struct item_list_entry *list, int *x, int *y, int *w, int *h) +{ + if (x) + *x = list->x; + if (y) + *y = list->y; + if (w) + *w = list->w; + if (h) + *h = list->h; +} + +static inline struct item_list_entry *list_item_append(struct item_list_entry *list, void *obj) +{ + struct item_list_entry *item; + + item = malloc(sizeof(*item)); + if (!item) + return NULL; + + item->data = obj; + + if (list) { + list->prev->next = item; + item->prev = list->prev; + + item->next = list; + list->prev = item; + } else{ + item->prev = item; + item->next = item; + list = item; + } + + return list; +} + +static inline void *list_item_nth(struct item_list_entry *list, int idx) +{ + if (!list) + return NULL; + + while (--idx >= 0) + list = list->next; + + return list->data; +} + +static inline struct item_list_entry *list_item_nth_list(struct item_list_entry *list, int idx) +{ + if (!list) + return NULL; + + while (--idx >= 0) + list = list->next; + + return list; +} + +static inline struct item_list_entry *list_item_find(struct item_list_entry *list, void *data) +{ + struct item_list_entry *item; + + if (!list) + return NULL; + + item = list; + do { + if (LIST_DATA(item) == data) + return item; + + item = LIST_NEXT(item); + } while (item != list); + + return NULL; +} + +static inline struct item_list_entry *list_item_remove(struct item_list_entry *list, void *data) +{ + struct item_list_entry *item; + + if (!list) { + ErrPrint("List is not valid\n"); + return NULL; + } + + DbgPrint("Start\n"); + item = list; + do { + if (LIST_DATA(item) == data) { + DbgPrint("ITEM is removed\n"); + if (item == list) { + if (list == LIST_NEXT(list)) + list = NULL; + else + list = LIST_NEXT(list); + } + + item->prev->next = item->next; + item->next->prev = item->prev; + free(item); + break; + } + + item = LIST_NEXT(item); + } while (item != list); + DbgPrint("End\n"); + + return list; +} + +static inline void *list_item_last(struct item_list_entry *list) +{ + if (!list) + return NULL; + + return list->prev->data; +} + +static inline struct item_list_entry *list_item_last_list(struct item_list_entry *list) +{ + if (!list) + return NULL; + + return list->prev; +} + +static inline int list_item_count(struct item_list_entry *list) +{ + struct item_list_entry *n; + int cnt; + + if (!list) + return 0; + + cnt = 0; + n = list; + do { + cnt++; + n = LIST_NEXT(n); + } while (n != list); + + return cnt; +} + +static inline int list_item_idx(struct widget_data *sc_data, struct item_list_entry *ilist) +{ + int idx; + idx = 0; + + while (ilist != sc_data->item_list) { + idx++; + ilist = LIST_PREV(ilist); + } + + return idx; +} + +static inline void init_evtq(struct evt_queue *evtq) +{ + evtq->front = 0; + evtq->rear = 0; + evtq->cnt = 0; + evtq->old_timestamp = 0; +} + +static inline void dq_evt(struct evt_queue *evtq) +{ + if (evtq->cnt <= 0) + return; + evtq->front++; + if (evtq->front >= SAMPLE_MAX) + evtq->front -= SAMPLE_MAX; + evtq->cnt--; +} + +static inline void enq_evt(struct evt_queue *evtq, Evas_Coord x, unsigned int timestamp) +{ + unsigned int t_diff; + int replace; + + replace = 0; + t_diff = timestamp - evtq->old_timestamp; + + if (evtq->cnt <= 0) + evtq->old_timestamp = timestamp; + else if (t_diff > EVT_SAMPLE_PERIOD) + evtq->old_timestamp += EVT_SAMPLE_PERIOD * (t_diff / EVT_SAMPLE_PERIOD); + else + replace = 1; + + if (!replace) { + if (evtq->cnt >= SAMPLE_MAX) + dq_evt(evtq); + evtq->rear++; + if (evtq->rear >= SAMPLE_MAX) + evtq->rear -= SAMPLE_MAX; + evtq->cnt++; + } + + evtq->ei[evtq->rear].x = x; + evtq->ei[evtq->rear].timestamp = evtq->old_timestamp; +} + +static inline Evas_Coord get_evt_avg(struct evt_queue *evtq) +{ + int i; + int rear; + Evas_Coord x; + int weight; + int t; + + t = (int)(ecore_time_get() * 1000); + rear = evtq->rear; + + x = 0; + for (i = 0; i < evtq->cnt; i += weight) { + weight = (t - evtq->ei[rear].timestamp) / EVT_SAMPLE_PERIOD; + if (weight > (evtq->cnt - i)) + weight = evtq->cnt - i; + else + weight = 1; + + x += evtq->ei[rear].x * weight; + t = evtq->ei[rear].timestamp; + rear--; + if (rear < 0) + rear += SAMPLE_MAX; + } + + x /= evtq->cnt; + return x; +} + +/* Move the item to given direction to fit its coordinates to border */ +static inline int calc_anim_dx_with_dir(struct widget_data *sc_data, int *dir) +{ + Evas_Coord x, w; + + LIST_ITEM_GEO_GET(sc_data->curlist, &x, NULL, &w, NULL); + sc_data->sc_anim_dx = 0; + + if (*dir < 0) { + DbgPrint("MOVE to LEFT\n"); + if (x < sc_data->clip_bx) { + (*dir)++; + + if (sc_data->tolist == sc_data->item_list) { + if (!sc_data->is_loop) { + *dir = 0; + DbgPrint("Looping is disabled\n"); + return -EINVAL; + } + } + + sc_data->tolist = LIST_PREV(sc_data->tolist); + sc_data->sc_anim_dx = sc_data->clip_bx - x /*- w*/; + } else { + sc_data->sc_anim_dx = sc_data->clip_bx - x; + } + } else if (*dir > 0) { + DbgPrint("MOVE to RIGHT\n"); + if (x < sc_data->clip_bx) { + sc_data->sc_anim_dx = sc_data->clip_bx - x; + } else if (x > sc_data->clip_bx) { + struct item_list_entry *newlist; + + (*dir)--; + newlist = LIST_NEXT(sc_data->tolist); + if (newlist == sc_data->item_list) { + if (!sc_data->is_loop) { + *dir = 0; + DbgPrint("Looping is disabled\n"); + return -EINVAL; + } + } + sc_data->tolist = newlist; + sc_data->sc_anim_dx = sc_data->clip_bx - x; /*(sc_data->clip_bx + sc_data->clip_bw) - x;*/ + } + } + + return 0; +} + +static inline void move_item(struct widget_data *sc_data, struct item_list_entry *ilist, int x, int y, int w, int h) +{ + struct live_sc_move_info info; + + info.item = LIST_DATA(ilist); + + info.relx = ((double)x - (double)sc_data->clip_bx) / (double)sc_data->clip_bw; + + LIST_ITEM_GEO_SET(ilist, x, y, w, h); + info.x = x; + info.y = y; + info.w = w; + info.h = h; + + evas_object_smart_callback_call(sc_data->scroller, "item,moved", &info); +} + +static struct item_list_entry *update_items_geo(struct widget_data *sc_data, int dx) +{ + Evas_Coord sx, sw; + Evas_Coord y, w, h; + struct item_list_entry *ilist; + struct item_list_entry *newlist; + struct item_list_entry *boundary; + int bx_bw; + register int x; + + LIST_ITEM_GEO_GET(sc_data->curlist, &sx, &y, &sw, &h); + + bx_bw = sc_data->clip_bx + sc_data->clip_bw; + + sx += dx; + move_item(sc_data, sc_data->curlist, sx, y, sw, h); + + newlist = NULL; + + if (sc_data->item_cnt < 3) { + ilist = LIST_NEXT(sc_data->curlist); + LIST_ITEM_GEO_GET(ilist, NULL, &y, &w, &h); + + if (sx + sw < bx_bw) { + x = sx + sw; + move_item(sc_data, ilist, x, y, w, h); + if (x == sc_data->clip_bx || (x < sc_data->clip_bx && (x + w) > sc_data->clip_bx)) + newlist = ilist; + } else if (sx > 0) { + x = sx - w; + move_item(sc_data, ilist, x, y, w, h); + if (x == sc_data->clip_bx || (x > sc_data->clip_bx && x < bx_bw)) { + newlist = ilist; + } + } + + goto out; + } + + x = sx; + boundary = NULL; + ilist = sc_data->curlist; + do { + if (!sc_data->is_loop && ilist == sc_data->item_list) + break; + + ilist = LIST_PREV(ilist); + if (!ilist) + break; + + LIST_ITEM_GEO_GET(ilist, NULL, &y, &w, &h); + x -= w; + move_item(sc_data, ilist, x, y, w, h); + + if (dx > 0 && !newlist) { + if ((x == sc_data->clip_bx) || (x > sc_data->clip_bx && x < bx_bw)) + newlist = ilist; + } + + boundary = ilist; + } while (x > sc_data->clip_bx); + + x = sx; + w = sw; + ilist = sc_data->curlist; + do { + ilist = LIST_NEXT(ilist); + if (!ilist || (!sc_data->is_loop && ilist == sc_data->item_list) || ilist == boundary) + break; + + x += w; + LIST_ITEM_GEO_GET(ilist, NULL, &y, &w, &h); + move_item(sc_data, ilist, x, y, w, h); + + if (dx < 0 && !newlist) { + if ((x == sc_data->clip_bx) || (x < sc_data->clip_bx && (x + w) > sc_data->clip_bx)) + newlist = ilist; + } + } while (x < bx_bw); + +out: + if (newlist) + sc_data->curlist = newlist; + + return newlist; +} + +static Eina_Bool emulate_evt(void *data) +{ + PROFILE_START(); + struct widget_data *sc_data; + Evas_Coord x; + Evas_Coord dx; + struct item_list_entry *newlist; + + sc_data = data; + + x = get_evt_avg(&sc_data->evtq); + if (x == sc_data->old_x) { + PROFILE_END(); + return ECORE_CALLBACK_RENEW; + } + + dx = x - sc_data->old_x; + sc_data->old_x = x; + + newlist = update_items_geo(sc_data, dx); + if (newlist) { + int idx; + + idx = list_item_idx(sc_data, newlist); + evas_object_smart_callback_call(sc_data->scroller, "page,changed", (void *)idx); + } + PROFILE_END(); + return ECORE_CALLBACK_RENEW; +} + +static void evt_mouse_down_cb(void *data, Evas *e, Evas_Object *evt_layer, void *event_info) +{ + Evas_Event_Mouse_Down *down; + struct widget_data *sc_data; + + sc_data = data; + down = event_info; + + sc_data->is_pressed = EINA_TRUE; + sc_data->press_x = down->canvas.x; + sc_data->press_y = down->canvas.y; + sc_data->old_x = down->canvas.x; + + if (!sc_data->is_freezed) { + init_evtq(&sc_data->evtq); + enq_evt(&sc_data->evtq, down->canvas.x, down->timestamp); + } +} + +static void evt_mouse_up_cb(void *data, Evas *e, Evas_Object *evt_layer, void *event_info) +{ + Evas_Event_Mouse_Up *up; + struct widget_data *sc_data; + struct live_sc_drag_info info; + + sc_data = data; + + if (!sc_data->is_pressed) + return; + + sc_data->is_pressed = EINA_FALSE; + + if (sc_data->evt_emulator) { + ecore_timer_del(sc_data->evt_emulator); + sc_data->evt_emulator = NULL; + } + + if (sc_data->drag_started == EINA_FALSE) { + DbgPrint("drag is not started\n"); + return; + } + + up = event_info; + + info.dx = up->canvas.x - sc_data->press_x; + info.dy = up->canvas.y - sc_data->press_y; + + sc_data->drag_started = EINA_FALSE; + + evas_object_smart_callback_call(sc_data->scroller, "drag,stop", &info); +} + +static void evt_mouse_move_cb(void *data, Evas *e, Evas_Object *evt_layer, void *event_info) +{ + struct widget_data *sc_data; + Evas_Event_Mouse_Move *move; + + sc_data = data; + + if (sc_data->is_pressed == EINA_FALSE) + return; + + if (sc_data->item_cnt <= 1) + return; + + if (sc_data->is_freezed) + return; + + move = event_info; + + if (sc_data->drag_started == EINA_FALSE) { + if (abs(move->cur.canvas.x - sc_data->press_x) < DRAG_SENS) + return; + + if (sc_data->sc_anim_timer) { + ecore_timer_del(sc_data->sc_anim_timer); + sc_data->sc_anim_timer = NULL; + } + + evas_object_smart_callback_call(sc_data->scroller, "drag,start", NULL); + sc_data->drag_started = EINA_TRUE; + } + + sc_data->prev_timestamp = move->timestamp; + enq_evt(&sc_data->evtq, move->cur.canvas.x, move->timestamp); + if (!sc_data->evt_emulator) { + if (!sc_data->curlist) + sc_data->curlist = sc_data->item_list; + + sc_data->evt_emulator = ecore_timer_add(EVT_PERIOD, emulate_evt, sc_data); + } +} + +static inline int prepare_evt_layer(struct widget_data *sc_data) +{ + Evas *e; + + e = evas_object_evas_get(sc_data->scroller); + if (!e) + return -EFAULT; + + sc_data->evt_layer = evas_object_rectangle_add(e); + if (!sc_data->evt_layer) + return -EFAULT; + + evas_object_smart_member_add(sc_data->evt_layer, sc_data->scroller); + + evas_object_color_set(sc_data->evt_layer, 255, 255, 255, 0); + evas_object_show(sc_data->evt_layer); + evas_object_repeat_events_set(sc_data->evt_layer, EINA_TRUE); + + evas_object_event_callback_add(sc_data->evt_layer, + EVAS_CALLBACK_MOUSE_DOWN, evt_mouse_down_cb, sc_data); + + evas_object_event_callback_add(sc_data->evt_layer, + EVAS_CALLBACK_MOUSE_UP, evt_mouse_up_cb, sc_data); + + evas_object_event_callback_add(sc_data->evt_layer, + EVAS_CALLBACK_MOUSE_MOVE, evt_mouse_move_cb, sc_data); + + evas_object_clip_set(sc_data->evt_layer, sc_data->clip); + return 0; +} + +static void live_add(Evas_Object *scroller) +{ + struct widget_data *sc_data; + Evas *e; + int ret; + + sc_data = calloc(1, sizeof(*sc_data)); + if (!sc_data) + return; + + e = evas_object_evas_get(scroller); + if (!e) { + free(sc_data); + return; + } + + evas_object_smart_data_set(scroller, sc_data); + + sc_data->clip = evas_object_rectangle_add(e); + if (!sc_data->clip) { + free(sc_data); + return; + } + + sc_data->is_pressed = EINA_FALSE; + sc_data->drag_started = EINA_FALSE; + sc_data->tolist = NULL; + sc_data->curlist = NULL; + sc_data->item_list = NULL; + sc_data->scroller = scroller; + + evas_object_smart_member_add(sc_data->clip, sc_data->scroller); + + ret = prepare_evt_layer(sc_data); + if (ret < 0) { + evas_object_del(sc_data->clip); + free(sc_data); + } + + return; +} + +static void live_del(Evas_Object *scroller) +{ + struct widget_data *sc_data; + Evas_Object *item; + struct item_list_entry *ilist; + struct item_list_entry *next; + + sc_data = evas_object_smart_data_get(scroller); + if (!sc_data) { + ErrPrint("sc_data is not valid\n"); + return; + } + + ilist = sc_data->item_list; + if (ilist) { + do { + next = LIST_NEXT(ilist); + item = LIST_DATA(ilist); + evas_object_clip_unset(item); + evas_object_smart_member_del(item); + free(ilist); + ilist = next; + } while (ilist != sc_data->item_list); + } + + evas_object_del(sc_data->evt_layer); + evas_object_del(sc_data->clip); + free(sc_data); +} + +static void live_move(Evas_Object *scroller, Evas_Coord bx, Evas_Coord by) +{ + struct widget_data *sc_data; + Evas_Coord x, y, w, h; + Evas_Coord bw; + + Evas_Coord dx; + Evas_Coord dy; + + struct item_list_entry *n; + + sc_data = evas_object_smart_data_get(scroller); + if (!sc_data) { + ErrPrint("sc_data is not valid\n"); + return; + } + + evas_object_geometry_get(sc_data->clip, &x, &y, &bw, NULL); + + evas_object_move(sc_data->evt_layer, bx, by); + evas_object_move(sc_data->clip, bx, by); + sc_data->clip_bx = bx; + sc_data->clip_bw = bw; + + dx = bx - x; + dy = by - y; + + if (sc_data->item_list) { + n = sc_data->item_list; + do { + evas_object_move(LIST_DATA(n), bx, by); + + LIST_ITEM_GEO_GET(n, &x, &y, &w, &h); + x += dx; + y += dy; + move_item(sc_data, n, x, y, w, h); + n = LIST_NEXT(n); + } while (n != sc_data->item_list); + } +} + +static void live_resize(Evas_Object *scroller, Evas_Coord w, Evas_Coord h) +{ + struct widget_data *sc_data; + + sc_data = evas_object_smart_data_get(scroller); + if (!sc_data) { + ErrPrint("sc_data is not valid\n"); + return; + } + + evas_object_resize(sc_data->clip, w, h); + evas_object_resize(sc_data->evt_layer, w, h); + + sc_data->clip_bw = w; +} + +static void live_show(Evas_Object *scroller) +{ + struct widget_data *sc_data; + + sc_data = evas_object_smart_data_get(scroller); + if (!sc_data) { + ErrPrint("sc_data is not valid\n"); + return; + } + + evas_object_show(sc_data->clip); +} + +static void live_hide(Evas_Object *scroller) +{ + struct widget_data *sc_data; + + sc_data = evas_object_smart_data_get(scroller); + if (!sc_data) { + ErrPrint("sc_data is not valid\n"); + return; + } + + evas_object_hide(sc_data->clip); +} + +static void live_set_color(Evas_Object *scroller, int r, int g, int b, int a) +{ + struct widget_data *sc_data; + + sc_data = evas_object_smart_data_get(scroller); + if (!sc_data) { + ErrPrint("sc_data is not valid\n"); + return; + } + + evas_object_color_set(sc_data->clip, r, g, b, a); +} + +static void live_set_clip(Evas_Object *scroller, Evas_Object *clip) +{ + struct widget_data *sc_data; + + sc_data = evas_object_smart_data_get(scroller); + if (!sc_data) { + ErrPrint("sc_data is not valid\n"); + return; + } + + evas_object_clip_set(sc_data->clip, clip); +} + +static void live_unset_clip(Evas_Object *scroller) +{ + struct widget_data *sc_data; + + sc_data = evas_object_smart_data_get(scroller); + if (!sc_data) { + ErrPrint("sc_data is not valid\n"); + return; + } + + evas_object_clip_unset(sc_data->clip); +} + +static inline void rearrange_items(struct widget_data *sc_data) +{ + struct item_list_entry *ilist; + Evas_Coord x, y, w, h; + Evas_Coord sw; + + LIST_ITEM_GEO_GET(sc_data->curlist, NULL, &y, &sw, &h); + move_item(sc_data, sc_data->curlist, sc_data->clip_bx, y, sw, h); + + x = sc_data->clip_bx; + ilist = sc_data->curlist; + while (ilist != sc_data->item_list) { + ilist = LIST_PREV(ilist); + LIST_ITEM_GEO_GET(ilist, NULL, &y, &w, &h); + x -= w; + move_item(sc_data, ilist, x, y, w, h); + } + + w = sw; + x = sc_data->clip_bx; + ilist = LIST_NEXT(sc_data->curlist); + while (ilist != sc_data->item_list) { + x += w; + LIST_ITEM_GEO_GET(ilist, NULL, &y, &w, &h); + move_item(sc_data, ilist, x, y, w, h); + ilist = LIST_NEXT(ilist); + } +} + +static Eina_Bool sc_anim_cb(void *data) +{ + PROFILE_START(); + struct widget_data *sc_data; + Evas_Coord sx, sw; + Evas_Coord y; + Evas_Coord dx; + struct item_list_entry *ilist; + + sc_data = data; + + if (!sc_data->curlist || !sc_data->tolist) { + DbgPrint("cur_list: %p, tolist: %p\n", sc_data->curlist, sc_data->tolist); + goto clean_out; + } + + ilist = sc_data->curlist; + if (sc_data->curlist != sc_data->tolist) { + if (sc_data->sc_anim_dx > 0) + ilist = LIST_PREV(ilist); + else + ilist = LIST_NEXT(ilist); + } + + LIST_ITEM_GEO_GET(ilist, &sx, &y, &sw, NULL); + if (ilist == sc_data->tolist) { + DbgPrint("next list == tolist\n"); + dx = abs(sx - sc_data->clip_bx); + DbgPrint("sx: %d, clip_bx: %d --> %d\n", sx, sc_data->clip_bx, dx); + if (dx < abs(sc_data->sc_anim_dx)) { + if (sc_data->sc_anim_dx < 0) + dx = -dx; + } else { + dx = sc_data->sc_anim_dx; + } + } else { + dx = sc_data->sc_anim_dx; + DbgPrint("dx: %d\n", dx); + } + + if (!dx) { + DbgPrint("dx is 0\n"); + goto clean_out; + } + + ilist = update_items_geo(sc_data, dx); + if (ilist) { + int idx; + + idx = list_item_idx(sc_data, ilist); + evas_object_smart_callback_call(sc_data->scroller,"page,changed", (void *)idx); + } + PROFILE_END(); + return ECORE_CALLBACK_RENEW; + +clean_out: + PROFILE_END(); + evas_object_smart_callback_call(sc_data->scroller, "anim,stop", NULL); + sc_data->sc_anim_timer = NULL; + return ECORE_CALLBACK_CANCEL; +} + +Evas_Object *live_scroller_add(Evas_Object *parent) +{ + static Evas_Smart_Class sc = EVAS_SMART_CLASS_INIT_NAME_VERSION("live,scroller"); + static Evas_Smart *smart = NULL; + Evas_Object *scroller; + Evas *e; + + if (!parent) + return NULL; + + e = evas_object_evas_get(parent); + if (!e) + return NULL; + + if (!smart) { + sc.add = live_add; + sc.del = live_del; + sc.move = live_move; + sc.resize = live_resize; + sc.show = live_show; + sc.hide = live_hide; + sc.color_set = live_set_color; + sc.clip_set = live_set_clip; + sc.clip_unset = live_unset_clip; + + smart = evas_smart_class_new(&sc); + } + + scroller = evas_object_smart_add(e, smart); + + return scroller; +} + +int live_scroller_append(Evas_Object *scroller, Evas_Object *item) +{ + Evas_Coord x, y, w, h; + Evas_Coord bx, by, bw, bh; + struct widget_data *sc_data; + struct item_list_entry *tmplist; + Evas_Coord start_x = 0; + Evas_Coord start_w = 0; + + sc_data = evas_object_smart_data_get(scroller); + if (!sc_data) { + ErrPrint("sc_data is not valid\n"); + return -EINVAL; + } + + evas_object_geometry_get(sc_data->clip, &bx, &by, &bw, &bh); + + if (sc_data->item_list) + LIST_ITEM_GEO_GET(sc_data->item_list, &start_x, NULL, &start_w, NULL); + + tmplist = list_item_last_list(sc_data->item_list); + if (tmplist) { + LIST_ITEM_GEO_GET(tmplist, &x, NULL, &w, NULL); + if (x + w == start_x) { + x = start_x - w; + } else { + x += w; + } + } else { + x = bx; + } + + evas_object_geometry_get(item, NULL, NULL, &w, &h); + evas_object_smart_member_add(item, sc_data->scroller); + + y = by + ((bh - h) >> 1); + + tmplist = list_item_append(sc_data->item_list, item); + if (sc_data->item_list == sc_data->curlist) + sc_data->curlist = tmplist; + if (sc_data->item_list == sc_data->tolist) + sc_data->tolist = tmplist; + sc_data->item_list = tmplist; + + sc_data->item_cnt++; + evas_object_clip_set(item, sc_data->clip); + evas_object_stack_below(item, sc_data->clip); + + evas_object_move(item, bx, by); + move_item(sc_data, list_item_find(sc_data->item_list, item), x, y, w, h); + + return 0; +} + +int live_scroller_remove_by_obj(Evas_Object *scroller, Evas_Object *obj) +{ + struct widget_data *sc_data; + struct item_list_entry *tmplist; + Evas_Object *item; + + sc_data = evas_object_smart_data_get(scroller); + if (!sc_data) { + ErrPrint("sc_data is not valid\n"); + return -EINVAL; + } + + if (sc_data->curlist) { + if (LIST_DATA(sc_data->curlist) == obj) { + sc_data->curlist = sc_data->item_list; + DbgPrint("Reset curlist\n"); + } + } + + if (sc_data->tolist) { + if (LIST_DATA(sc_data->tolist) == obj) { + sc_data->tolist = sc_data->item_list; + DbgPrint("Reset tolist\n"); + } + } + + tmplist = list_item_remove(sc_data->item_list, obj); + if (sc_data->item_list == sc_data->curlist) { + sc_data->curlist = tmplist; + DbgPrint("Update curlist\n"); + } + if (sc_data->item_list == sc_data->tolist) { + sc_data->tolist = tmplist; + DbgPrint("Update tolist\n"); + } + sc_data->item_list = tmplist; + + sc_data->item_cnt--; + evas_object_clip_unset(obj); + evas_object_smart_member_del(obj); + DbgPrint("Count of items: %d\n", sc_data->item_cnt); + + item = LIST_DATA(sc_data->curlist); + if (item) { + Evas_Coord y, w, h; + int idx; + + LIST_ITEM_GEO_GET(sc_data->curlist, NULL, &y, &w, &h); + DbgPrint("Current GEO: %d, %dx%d\n", y, w, h); + LIST_ITEM_GEO_SET(sc_data->curlist, sc_data->clip_bx, y, w, h); + DbgPrint("sc_data->curlist : %p\n", sc_data->curlist); + update_items_geo(sc_data, 0); + DbgPrint("sc_data->curlist : %p\n", sc_data->curlist); + + idx = list_item_idx(sc_data, sc_data->curlist); + evas_object_smart_callback_call(sc_data->scroller, "page,changed", (void *)idx); + } + + return 0; +} + +Evas_Object *live_scroller_remove(Evas_Object *scroller, int idx) +{ + struct widget_data *sc_data; + struct item_list_entry *tmplist; + Evas_Object *ret; + Evas_Object *item; + + sc_data = evas_object_smart_data_get(scroller); + if (!sc_data) { + ErrPrint("sc_data is not valid\n"); + return NULL; + } + + if (idx < 0 || idx >= sc_data->item_cnt) + return NULL; + + ret = list_item_nth(sc_data->item_list, idx); + if (!ret) + return NULL; + + if (list_item_nth_list(sc_data->item_list, idx) == sc_data->curlist) { + DbgPrint("Reset curlist\n"); + sc_data->curlist = sc_data->item_list; + } + + if (list_item_nth_list(sc_data->item_list, idx) == sc_data->tolist) { + DbgPrint("Reset tolist\n"); + sc_data->tolist = sc_data->item_list; + } + + tmplist = list_item_remove(sc_data->item_list, ret); + if (sc_data->item_list == sc_data->curlist) + sc_data->curlist = tmplist; + if (sc_data->item_list == sc_data->tolist) + sc_data->tolist = tmplist; + sc_data->item_list = tmplist; + + sc_data->item_cnt--; + evas_object_clip_unset(ret); + evas_object_smart_member_del(ret); + + item = LIST_DATA(sc_data->curlist); + if (item) { + Evas_Coord y, w, h; + int idx; + LIST_ITEM_GEO_GET(sc_data->curlist, NULL, &y, &w, &h); + LIST_ITEM_GEO_SET(sc_data->curlist, sc_data->clip_bx, y, w, h); + update_items_geo(sc_data, 0); + idx = list_item_idx(sc_data, sc_data->curlist); + evas_object_smart_callback_call(sc_data->scroller, "page,changed", (void *)idx); + } + return ret; +} + +Evas_Object *live_scroller_get_item(Evas_Object *scroller, int idx) +{ + struct widget_data *sc_data; + + sc_data = evas_object_smart_data_get(scroller); + if (!sc_data) { + ErrPrint("sc_data is not valid\n"); + return NULL; + } + + if (idx < 0 || idx >= sc_data->item_cnt) + return NULL; + + return list_item_nth(sc_data->item_list, idx); +} + +int live_scroller_get_current(Evas_Object *scroller) +{ + struct widget_data *sc_data; + struct item_list_entry *ilist; + int idx; + + sc_data = evas_object_smart_data_get(scroller); + if (!sc_data) { + ErrPrint("sc_data is not valid\n"); + return -EINVAL; + } + + ilist = sc_data->curlist; + idx = 0; + if (ilist) { + while (ilist != sc_data->item_list) { + idx++; + ilist = LIST_PREV(ilist); + } + } + + return idx; +} + +int live_scroller_loop_set(Evas_Object *scroller, int is_loop) +{ + struct widget_data *sc_data; + + sc_data = evas_object_smart_data_get(scroller); + if (!sc_data) { + ErrPrint("sc_data is not valid\n"); + return -EINVAL; + } + + if (is_loop == EINA_FALSE && sc_data->is_loop == EINA_TRUE) + rearrange_items(sc_data); + + sc_data->is_loop = is_loop; + return 0; +} + +int live_scroller_freeze(Evas_Object *scroller) +{ + struct widget_data *sc_data; + + sc_data = evas_object_smart_data_get(scroller); + if (!sc_data) { + ErrPrint("sc_data is not valid\n"); + return -EINVAL; + } + + sc_data->is_freezed = EINA_TRUE; + return 0; +} + +int live_scroller_thaw(Evas_Object *scroller) +{ + struct widget_data *sc_data; + + sc_data = evas_object_smart_data_get(scroller); + if (!sc_data) { + ErrPrint("sc_data is not valid\n"); + return -EINVAL; + } + + sc_data->is_freezed = EINA_FALSE; + return 0; +} + +int live_scroller_anim_to(Evas_Object *scroller, double sec, int offset) +{ + PROFILE_START(); + struct widget_data *sc_data; + Evas_Coord sx, sw; + Evas_Coord y; + struct live_sc_event_info info; + struct item_list_entry *ilist; + double ftmp; + int ret; + + sc_data = evas_object_smart_data_get(scroller); + if (!sc_data) { + ErrPrint("sc_data is not valid\n"); + ret = -EINVAL; + goto out; + } + + if (sc_data->is_freezed) { + ErrPrint("Scroller is freezed\n"); + ret = -EBUSY; + goto out; + } + + if (!sc_data->curlist) + sc_data->curlist = sc_data->item_list; + + if (!sc_data->curlist) { + ErrPrint("List is empty\n"); + ret = -ENOENT; + goto out; + } + + if (sc_data->sc_anim_timer) { + ecore_timer_del(sc_data->sc_anim_timer); + sc_data->sc_anim_timer = NULL; + evas_object_smart_callback_call(sc_data->scroller, "anim,stop", NULL); + } else { + sc_data->tolist = sc_data->curlist; + } + + LIST_ITEM_GEO_GET(sc_data->curlist, &sx, &y, &sw, NULL); + + if (!offset) { + sc_data->sc_anim_dx = sc_data->clip_bx - sx; + DbgPrint("offset==0, dx: %d\n", sc_data->sc_anim_dx); + } else { + Evas_Coord tw; + struct item_list_entry *tmplist; + + calc_anim_dx_with_dir(sc_data, &offset); + DbgPrint("Offset: %d\n", offset); + + ilist = sc_data->curlist; + while (offset < 0) { + if (!sc_data->is_loop && ilist == sc_data->item_list) { + DbgPrint("Loop is disabled\n"); + break; + } + + LIST_ITEM_GEO_GET(ilist, NULL, NULL, &tw, NULL); + ilist = LIST_PREV(ilist); + + sc_data->sc_anim_dx += tw; + DbgPrint("tw: %d (%d)\n", tw, sc_data->sc_anim_dx); + + offset++; + if (sc_data->tolist == sc_data->item_list) { + if (!sc_data->is_loop) { + DbgPrint("Looping disabled\n"); + break; + } + } + sc_data->tolist = LIST_PREV(sc_data->tolist); + } + + while (offset > 0) { + LIST_ITEM_GEO_GET(ilist, NULL, NULL, &tw, NULL); + ilist = LIST_NEXT(ilist); + + sc_data->sc_anim_dx -= tw; + DbgPrint("tw: %d (%d)\n", tw, sc_data->sc_anim_dx); + + offset--; + tmplist = LIST_NEXT(sc_data->tolist); + if (tmplist == sc_data->item_list) { + if (!sc_data->is_loop) { + DbgPrint("Looping disabled\n"); + break; + } + } + sc_data->tolist = tmplist; + + if (!sc_data->is_loop && ilist == sc_data->item_list) { + DbgPrint("Destination arrived or loop is disabled"); + break; + } + } + } + + if (abs(sc_data->sc_anim_dx) > ANIM_MIN) { + ftmp = (double)sc_data->sc_anim_dx / ANIM_UNIT; + DbgPrint("ftmp: %lf\n", ftmp); + if (fabs(ftmp) < ANIM_MIN || fabs(ftmp) > abs(sc_data->sc_anim_dx)) + sc_data->sc_anim_dx = ftmp < 0 ? -ANIM_MIN : ANIM_MIN; + else + sc_data->sc_anim_dx = (int)ftmp; + DbgPrint("Result: %d\n", sc_data->sc_anim_dx); + } + + sc_data->sc_anim_timer = ecore_timer_add(sec, sc_anim_cb, sc_data); + if (!sc_data->sc_anim_timer) { + ErrPrint("Failed to add a animator\n"); + ret = -EFAULT; + goto out; + } + + info.curidx = list_item_idx(sc_data, sc_data->curlist); + info.toidx = list_item_idx(sc_data, sc_data->tolist); + DbgPrint("Current index: %d, To index: %d\n", info.curidx, info.toidx); + + evas_object_smart_callback_call(sc_data->scroller, "anim,start", &info); + ret = 0; + +out: + PROFILE_END(); + return ret; +} + +int live_scroller_go_to(Evas_Object *scroller, int idx) +{ + struct widget_data *sc_data; + + sc_data = evas_object_smart_data_get(scroller); + if (!sc_data) { + ErrPrint("sc_data is not valid\n"); + return -EINVAL; + } + + if (sc_data->is_freezed) + return -EBUSY; + + if (idx < 0 || idx >= sc_data->item_cnt) + return -EINVAL; + + sc_data->curlist = list_item_nth_list(sc_data->item_list, idx); + if (!sc_data->curlist) + return -EFAULT; + + rearrange_items(sc_data); + evas_object_smart_callback_call(sc_data->scroller, "page,changed", (void *)idx); + + return 0; +} + +int live_scroller_update(Evas_Object *scroller) +{ + struct widget_data *sc_data; + struct item_list_entry *n; + Evas_Object *item; + Evas_Coord x, y, w, h; + + sc_data = evas_object_smart_data_get(scroller); + if (!sc_data) { + ErrPrint("sc_data is not valid\n"); + return -EINVAL; + } + + if (sc_data->item_list) { + n = sc_data->item_list; + do { + item = LIST_DATA(n); + LIST_ITEM_GEO_GET(n, &x, &y, &w, &h); + move_item(sc_data, n, x, y, w, h); + n = LIST_NEXT(n); + } while (n != sc_data->item_list); + } + + return 0; +} + +int live_scroller_get_item_count(Evas_Object *scroller) +{ + struct widget_data *sc_data; + + sc_data = evas_object_smart_data_get(scroller); + if (!sc_data) { + ErrPrint("sc_data is not valid\n"); + return 0; + } + + return list_item_count(sc_data->item_list); +} + +int live_scroller_get_item_index(Evas_Object *scroller, Evas_Object *item) +{ + struct widget_data *sc_data; + struct item_list_entry *n; + Evas_Object *tmp; + int idx; + + sc_data = evas_object_smart_data_get(scroller); + if (!sc_data) { + ErrPrint("sc_data is not valid\n"); + return -EINVAL; + } + + if (!sc_data->item_list) + return -ENOENT; + + idx = 0; + n = sc_data->item_list; + do { + tmp = LIST_DATA(n); + n = LIST_NEXT(n); + + if (tmp == item) + return idx; + + idx++; + } while (n != sc_data->item_list); + + return -ENOENT; +} + +/* End of a file */ diff --git a/live.viewer/src/main.c b/live.viewer/src/main.c new file mode 100644 index 0000000..9f84418 --- /dev/null +++ b/live.viewer/src/main.c @@ -0,0 +1,189 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <Elementary.h> + +#include <dlog.h> +#include <ail.h> +#include <app.h> +#include <bundle.h> + +#include <livebox.h> +#include <livebox-service.h> + +#include "main.h" +#include "util.h" +#include "debug.h" +#include "scroller.h" +#include "lb.h" + +static struct info { + Evas_Object *window; + Evas_Object *scroller; +} s_info = { + .window = NULL, + .scroller = NULL, +}; + +Evas_Object *main_get_window(void) +{ + return s_info.window; +} + +static void click_cb(void *data, Evas_Object *obj, void *event_info) +{ + Elm_Object_Item *item; + const char *label; + + item = elm_list_selected_item_get(obj); + if (!item) + return; + + label = elm_object_item_part_text_get(item, NULL); + if (!label) + return; + + DbgPrint("Label: %s (%s)\n", label, data); + if (lb_add(s_info.scroller, data) < 0) + ErrPrint("Failed to add a new livebox\n"); +} + +static int append_livebox_cb(const char *appid, const char *lbid, int is_prime, void *data) +{ + char *name; + + DbgPrint("%s - %s\n", appid, lbid); + + name = livebox_service_i18n_name(lbid, NULL); + if (!name) { + name = strdup(lbid); + if (!name) { + ErrPrint("Heap: %s\n", strerror(errno)); + return 0; + } + } + + DbgPrint("Name: %s\n", name); + elm_list_item_append(data, name, NULL, NULL, click_cb, strdup(lbid)); + free(name); + return 0; +} + +static inline void livebox_list_create(void) +{ + Evas_Object *list; + + list = elm_list_add(s_info.window); + if (!list) { + ErrPrint("Failed to create a list\n"); + return; + } + + evas_object_resize(list, 720, 1280); + evas_object_show(list); + + DbgPrint("Get Package list\n"); + livebox_service_get_pkglist(append_livebox_cb, list); + elm_list_go(list); + + scroller_append(s_info.scroller, list); +} + +static bool app_create(void *data) +{ + Evas_Object *bg; + DbgPrint("create"); + lb_init(); + + s_info.window = elm_win_add(NULL, "Box viewer", ELM_WIN_BASIC); + if (!s_info.window) { + ErrPrint("Failed to create a window\n"); + return false; + } + elm_win_autodel_set(s_info.window, EINA_TRUE); + + evas_object_resize(s_info.window, 720, 1280); + evas_object_show(s_info.window); + + bg = elm_bg_add(s_info.window); + elm_win_resize_object_add(s_info.window, bg); + elm_bg_color_set(bg, 0, 0, 255); + evas_object_show(bg); + + s_info.scroller = scroller_create(s_info.window); + if (!s_info.scroller) { + evas_object_del(s_info.window); + s_info.window = NULL; + ErrPrint("Failed to create a scroller\n"); + return false; + } + + evas_object_resize(s_info.scroller, 720, 1280); + evas_object_show(s_info.scroller); + + livebox_list_create(); + + return true; +} + +static void app_terminate(void *data) +{ + DbgPrint("terminate"); + lb_fini(); + /*! + * \TODO + * Delete all objects from the scroller. + */ + + scroller_destroy(s_info.scroller); + evas_object_del(s_info.window); + s_info.window = NULL; +} + +static void app_pause(void *data) +{ + DbgPrint("pause"); +} + +static void app_resume(void *data) +{ + DbgPrint("resume"); +} + +static void app_reset(service_h service, void *data) +{ + DbgPrint("reset"); +} + +int main(int argc, char *argv[]) +{ + app_event_callback_s event_callback; + + setenv("ELM_ENGINE", "gl", 0); + event_callback.create = app_create; + event_callback.terminate = app_terminate; + event_callback.pause = app_pause; + event_callback.resume = app_resume; + event_callback.service = app_reset; + event_callback.low_memory = NULL; + event_callback.low_battery = NULL; + event_callback.device_orientation = NULL; + event_callback.language_changed = NULL; + + return app_efl_main(&argc, &argv, &event_callback, NULL); +} + +/* End of a file */ diff --git a/live.viewer/src/scroller.c b/live.viewer/src/scroller.c new file mode 100644 index 0000000..7a181fc --- /dev/null +++ b/live.viewer/src/scroller.c @@ -0,0 +1,524 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <Elementary.h> + +#include <dlog.h> + +#include "util.h" +#include "live_scroller.h" +#include "scroller.h" +#include "debug.h" + +#define FOCAL_DIST 800 +#define FLICK_COND 100 + +struct cb_item { + int (*cb)(Evas_Object *sc, void *data); + void *data; +}; + +struct scroll_info { + int locked; + Eina_Bool scrolling; + int focal; + Eina_Bool quick; + + Evas_Map *map; + + Ecore_Idler *bg_changer; + Eina_List *cb_list; +}; + +void scroller_lock(Evas_Object *sc) +{ + struct scroll_info *scinfo; + + scinfo = evas_object_data_get(sc, "scinfo"); + if (!scinfo) { + ErrPrint("scinfo is not valid\n"); + return; + } + + if (!scinfo->locked) + live_scroller_freeze(sc); + + scinfo->locked++; +} + +void scroller_unlock(Evas_Object *sc) +{ + struct scroll_info *scinfo; + + scinfo = evas_object_data_get(sc, "scinfo"); + if (!scinfo) { + ErrPrint("scinfo is not valid\n"); + return; + } + + if (scinfo->locked == 0) + return; + + scinfo->locked--; + + if (scinfo->locked == 0) + live_scroller_thaw(sc); +} + +static void sc_anim_stop(void *data, Evas_Object *obj, void *event_info) +{ + Eina_List *l; + Eina_List *tmp; + struct cb_item *item; + struct scroll_info *scinfo; + + scinfo = evas_object_data_get(obj, "scinfo"); + if (!scinfo) { + ErrPrint("scinfo is not valid\n"); + return; + } + /*! + * \TODO + * Do what you want at here when the scroller is stopped + */ + + scinfo->scrolling = EINA_FALSE; + EINA_LIST_FOREACH_SAFE(scinfo->cb_list, l, tmp, item) { + if (item->cb(obj, item->data) == ECORE_CALLBACK_CANCEL) { + if (eina_list_data_find(scinfo->cb_list, item)) { + scinfo->cb_list = eina_list_remove(scinfo->cb_list, item); + free(item); + } + } + } +} + +static inline void sc_drag_start(void *data, Evas_Object *obj, void *event_info) +{ + struct scroll_info *scinfo; + + scinfo = evas_object_data_get(obj, "scinfo"); + if (!scinfo) { + ErrPrint("scinfo is not valid\n"); + return; + } + + scinfo->scrolling = EINA_TRUE; +} + +static inline void sc_drag_stop(void *data, Evas_Object *scroller, void *event_info) +{ + struct live_sc_drag_info *info; + int offset = 0; + int ret; + + info = event_info; + + if (info->dx > FLICK_COND) + offset = -1; + else if (info->dx < -FLICK_COND) + offset = 1; + + ret = live_scroller_anim_to(scroller, 0.016f, offset); + if (ret < 0) { + struct scroll_info *scinfo; + scinfo = evas_object_data_get(scroller, "scinfo"); + if (scinfo) + scinfo->scrolling = EINA_FALSE; + } +} + +static Eina_Bool bg_change_cb(void *data) +{ + Evas_Object *sc = data; + struct scroll_info *scinfo; + + scinfo = evas_object_data_get(sc, "scinfo"); + if (scinfo) + scinfo->bg_changer = NULL; + + /*! + * \note: + * Here, + * Filename of background image handling code is only + * used to demonstrates UX concept and estimates its perfomance. + * So, I'll change this if it should be appled to + * main branch. + */ + DbgPrint("Change the background image (%p)\n", sc); + return ECORE_CALLBACK_CANCEL; +} + +static void sc_anim_start(void *data, Evas_Object *obj, void *event_info) +{ + struct scroll_info *scinfo; + + scinfo = evas_object_data_get(obj, "scinfo"); + if (!scinfo) { + ErrPrint("scinfo is not valid\n"); + return; + } + + /*! + * \note + * without drag,start + * anim start can be invoked by the scroller_anim_to + */ + scinfo->scrolling = EINA_TRUE; + + if (scinfo->bg_changer) + ecore_idler_del(scinfo->bg_changer); + + scinfo->bg_changer = ecore_idler_add(bg_change_cb, obj); + if (!scinfo->bg_changer) + DbgPrint("Failed to add an idler\n"); +} + +static void sc_item_moved(void *data, Evas_Object *obj, void *event_info) +{ + struct live_sc_move_info *evt = event_info; + int color; + int focal; + Evas_Coord y, sx, sw; + double ftmp; + struct scroll_info *scinfo; + + scinfo = evas_object_data_get(obj, "scinfo"); + if (!scinfo) { + ErrPrint("Has no scinfo\n"); + return; + } + + ftmp = fabsl(evt->relx); + if (ftmp >= 1.0f) { + evas_object_map_enable_set(evt->item, EINA_FALSE); + evas_object_hide(evt->item); + return; + } + + color = 255 * (1.0f - ftmp); + if (scinfo->quick) { + if (color < 100) + color = 100; + + focal = scinfo->focal; + } else { + if (color == 0) { + evas_object_map_enable_set(evt->item, EINA_FALSE); + evas_object_hide(evt->item); + return; + } + + focal = -ftmp * 200.0f + scinfo->focal; + } + + evas_object_geometry_get(data, &sx, NULL, &sw, NULL); + + /* LEFT */ + evas_map_point_coord_set(scinfo->map, 0, evt->x, evt->y, 0); + evas_map_point_image_uv_set(scinfo->map, 0, 0, 0); + evas_map_point_color_set(scinfo->map, 0, color, color, color, color); + + /* RIGHT */ + evas_map_point_coord_set(scinfo->map, 1, evt->x + evt->w, evt->y, 0); + evas_map_point_image_uv_set(scinfo->map, 1, evt->w, 0); + evas_map_point_color_set(scinfo->map, 1, color, color, color, color); + + /* BOTTOM-RIGHT */ + evas_map_point_coord_set(scinfo->map, 2, evt->x + evt->w, evt->y + evt->h, 0); + evas_map_point_image_uv_set(scinfo->map, 2, evt->w, evt->h); + evas_map_point_color_set(scinfo->map, 2, color, color, color, color); + + /* BOTTOM-LEFT */ + evas_map_point_coord_set(scinfo->map, 3, evt->x, evt->y + evt->h, 0); + evas_map_point_image_uv_set(scinfo->map, 3, 0, evt->h); + evas_map_point_color_set(scinfo->map, 3, color, color, color, color); + + y = evt->y + (evt->h >> 1); + evas_map_util_3d_rotate(scinfo->map, 0.0f, -30.0f * evt->relx, 0.0f, evt->x + (evt->w >> 1), y, 0); + evas_map_util_3d_perspective(scinfo->map, sx + (sw >> 1), y, focal, FOCAL_DIST); + evas_object_map_set(evt->item, scinfo->map); + evas_object_map_enable_set(evt->item, EINA_TRUE); + evas_object_show(evt->item); + return; +} + +static void sc_page_changed(void *data, Evas_Object *obj, void *event_info) +{ + DbgPrint("Page is changed %d\n", (int)event_info); +} + +int scroller_add_stop_cb(Evas_Object *scroller, + int (*cb)(Evas_Object *sc, void *data), void *data) +{ + struct cb_item *item; + struct scroll_info *scinfo; + + scinfo = evas_object_data_get(scroller, "scinfo"); + if (!scinfo) { + ErrPrint("scinfo is not valid\n"); + return -EINVAL; + } + + item = calloc(1, sizeof(*item)); + if (!item) { + ErrPrint("Error: %s\n", strerror(errno)); + return EXIT_FAILURE; + } + + item->cb = cb; + item->data = data; + + scinfo->cb_list = eina_list_append(scinfo->cb_list, item); + return EXIT_SUCCESS; +} + +void scroller_del_stop_cb(Evas_Object *scroller, + int (*cb)(Evas_Object *sc, void *data), void *data) +{ + struct cb_item *item; + Eina_List *l; + Eina_List *tmp; + struct scroll_info *scinfo; + + scinfo = evas_object_data_get(scroller, "scinfo"); + if (!scinfo) { + ErrPrint("Failed to get scinfo\n"); + return; + } + + EINA_LIST_FOREACH_SAFE(scinfo->cb_list, l, tmp, item) { + if (item->cb == cb && item->data == data) { + scinfo->cb_list = eina_list_remove(scinfo->cb_list, item); + free(item); + break; + } + } +} + +Evas_Object *scroller_create(Evas_Object *ctrl) +{ + Evas_Object *sc; + struct scroll_info *scinfo; + + scinfo = calloc(1, sizeof(*scinfo)); + if (!scinfo) { + ErrPrint("Heap: %s\n", strerror(errno)); + return NULL; + } + + sc = live_scroller_add(ctrl); + if (!sc) { + DbgPrint("Failed to create flip object\n"); + free(scinfo); + return NULL; + } + + evas_object_data_set(sc, "scinfo", scinfo); + + scinfo->map = evas_map_new(4); + if (!scinfo->map) { + ErrPrint("Failed to create a map object\n"); + evas_object_del(sc); + free(scinfo); + return NULL; + } + + evas_map_smooth_set(scinfo->map, EINA_TRUE); + evas_map_alpha_set(scinfo->map, EINA_TRUE); + + evas_object_size_hint_weight_set(sc, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_smart_callback_add(sc, "drag,start", sc_drag_start, NULL); + evas_object_smart_callback_add(sc, "drag,stop", sc_drag_stop, NULL); + evas_object_smart_callback_add(sc, "anim,stop", sc_anim_stop, NULL); + evas_object_smart_callback_add(sc, "anim,start", sc_anim_start, NULL); + evas_object_smart_callback_add(sc, "page,changed", sc_page_changed, NULL); + evas_object_smart_callback_add(sc, "item,moved", sc_item_moved, NULL); + live_scroller_loop_set(sc, EINA_TRUE); + evas_object_show(sc); + + return sc; +} + +int scroller_append(Evas_Object *sc, Evas_Object *child) +{ + return live_scroller_append(sc, child); +} + +int scroller_get_page_index(Evas_Object *sc, Evas_Object *page) +{ + return live_scroller_get_item_index(sc, page); +} + +Evas_Object *scroller_get_page(Evas_Object *sc, int idx) +{ + return live_scroller_get_item(sc, idx); +} + +Evas_Object *scroller_peek_by_idx(Evas_Object *sc, int idx) +{ + return live_scroller_remove(sc, idx); +} + +int scroller_peek_by_obj(Evas_Object *sc, Evas_Object *page) +{ + return live_scroller_remove_by_obj(sc, page); +} + +int scroller_get_current_idx(Evas_Object *sc) +{ + return live_scroller_get_current(sc); +} + +int scroller_is_scrolling(Evas_Object *sc) +{ + struct scroll_info *scinfo; + + scinfo = evas_object_data_get(sc, "scinfo"); + if (!scinfo) { + ErrPrint("scinfo is not valid\n"); + return -EINVAL; + } + + return scinfo->scrolling; +} + +int scroller_get_page_count(Evas_Object *sc) +{ + return live_scroller_get_item_count(sc); +} + +int scroller_scroll_to(Evas_Object *sc, int idx) +{ + int curidx; + int cnt; + register int i; + int next_offset; + int prev_offset; + struct scroll_info *scinfo; + + scinfo = evas_object_data_get(sc, "scinfo"); + if (!scinfo) { + ErrPrint("scinfo is not valid\n"); + return -EINVAL; + } + + if (scinfo->scrolling) { + DbgPrint("Scroller is scrolling\n"); + return -EINVAL; + } + + curidx = live_scroller_get_current(sc); + cnt = live_scroller_get_item_count(sc); + + i = curidx; + next_offset = 0; + while (i != idx && i >= 0 && i < cnt) { + i++; + if (i >= cnt) + i = 0; + + next_offset++; + } + + i = curidx; + prev_offset = 0; + while (i != idx && i >= 0 && i < cnt) { + i--; + if (i < 0) + i = cnt - 1; + + prev_offset--; + } + + idx = next_offset < -prev_offset ? next_offset : prev_offset; + live_scroller_anim_to(sc, 0.016f, idx); + return 0; +} + +int scroller_jump_to(Evas_Object *sc, int idx) +{ + live_scroller_go_to(sc, idx); + return 0; +} + +int scroller_destroy(Evas_Object *sc) +{ + int cnt; + struct scroll_info *scinfo; + struct cb_item *item; + + scinfo = evas_object_data_del(sc, "scinfo"); + if (!scinfo) + return -EFAULT; + + if (scinfo->bg_changer) + ecore_idler_del(scinfo->bg_changer); + + EINA_LIST_FREE(scinfo->cb_list, item) { + free(item); + } + + cnt = live_scroller_get_item_count(sc); + if (cnt) + DbgPrint("Children is not cleared (%d)\n", cnt); + + evas_object_del(sc); + evas_map_free(scinfo->map); + free(scinfo); + return 0; +} + +int scroller_update(Evas_Object *sc, void *data) +{ + struct scroll_info *scinfo; + + scinfo = evas_object_data_get(sc, "scinfo"); + if (!scinfo) { + ErrPrint("scinfo is not valid\n"); + return -EFAULT; + } + + scinfo->focal = (int)data; + live_scroller_update(sc); + return EXIT_SUCCESS; +} + +int scroller_fast_scroll(Evas_Object *sc, int idx) +{ + idx -= scroller_get_current_idx(sc); + live_scroller_anim_to(sc, 0.016f, idx); + return 0; +} + +void scroller_loop_set(Evas_Object *sc, Eina_Bool val) +{ + live_scroller_loop_set(sc, val); +} + +void scroller_quick_navi(Evas_Object *sc, Eina_Bool val) +{ + struct scroll_info *scinfo; + scinfo = evas_object_data_get(sc, "scinfo"); + if (!scinfo) { + ErrPrint("scinfo is not valid\n"); + return; + } + + scinfo->quick = val; +} + +/* End of a file */ diff --git a/live.viewer/src/util.c b/live.viewer/src/util.c new file mode 100644 index 0000000..8a1c5cb --- /dev/null +++ b/live.viewer/src/util.c @@ -0,0 +1,43 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <sys/time.h> +#include <unistd.h> +#include <stdlib.h> + +#include <dlog.h> + +#include "debug.h" +#include "util.h" + +int errno; + +const char *util_basename(const char *name) +{ + int length; + length = name ? strlen(name) : 0; + if (!length) + return "."; + + while (--length > 0 && name[length] != '/'); + + return length <= 0 ? name : name + length + (name[length] == '/'); +} + +/* End of a file */ diff --git a/livebox-viewer.pc.in b/livebox-viewer.pc.in new file mode 100644 index 0000000..06f4891 --- /dev/null +++ b/livebox-viewer.pc.in @@ -0,0 +1,11 @@ +prefix=@PREFIX@ +exec_prefix=@EXEC_PREFIX@ +libdir=@LIBDIR@ +includedir=@INCLUDEDIR@ + +Name: livebox-viewer +Description: livebox supporting library +Version: @VERSION@ +Libs: -L${libdir} -llivebox-viewer +Cflags: -I${includedir} +cppflags: -I${includedir} diff --git a/packaging/liblivebox-viewer.manifest b/packaging/liblivebox-viewer.manifest new file mode 100644 index 0000000..a76fdba --- /dev/null +++ b/packaging/liblivebox-viewer.manifest @@ -0,0 +1,5 @@ +<manifest> + <request> + <domain name="_" /> + </request> +</manifest> diff --git a/packaging/liblivebox-viewer.spec b/packaging/liblivebox-viewer.spec new file mode 100644 index 0000000..e92c499 --- /dev/null +++ b/packaging/liblivebox-viewer.spec @@ -0,0 +1,91 @@ +%bcond_with wayland + +Name: liblivebox-viewer +Summary: Library for developing the application +Version: 0.31.0 +Release: 1 +Group: HomeTF/Livebox +License: Flora +Source0: %{name}-%{version}.tar.gz +Source1001: %{name}.manifest +BuildRequires: cmake, gettext-tools, coreutils +BuildRequires: pkgconfig(dlog) +BuildRequires: pkgconfig(aul) +BuildRequires: pkgconfig(glib-2.0) +BuildRequires: pkgconfig(gio-2.0) +BuildRequires: pkgconfig(com-core) +BuildRequires: pkgconfig(sqlite3) +BuildRequires: pkgconfig(db-util) +BuildRequires: pkgconfig(livebox-service) +BuildRequires: pkgconfig(vconf) + +%if %{with wayland} +%else +BuildRequires: pkgconfig(x11) +BuildRequires: pkgconfig(xext) +%endif + +%if "%{sec_product_feature_livebox}" == "0" +ExclusiveArch: +%endif + +%description +API for creating a new instance of the livebox and managing its life-cycle. + +%package devel +Summary: Livebox viewer development library (dev) +Group: Development/Libraries +Requires: %{name} = %{version}-%{release} + +%description devel +Header and package configuration files for the livebox viewer development + +%prep +%setup -q +cp %{SOURCE1001} . + +%build +%if 0%{?sec_build_binary_debug_enable} +export CFLAGS="$CFLAGS -DTIZEN_DEBUG_ENABLE" +export CXXFLAGS="$CXXFLAGS -DTIZEN_DEBUG_ENABLE" +export FFLAGS="$FFLAGS -DTIZEN_DEBUG_ENABLE" +%endif + +%if 0%{?tizen_build_binary_release_type_eng} +export CFLAGS="${CFLAGS} -DTIZEN_ENGINEER_MODE" +export CXXFLAGS="${CXXFLAGS} -DTIZEN_ENGINEER_MODE" +export FFLAGS="${FFLAGS} -DTIZEN_ENGINEER_MODE" +%endif + +%if %{with wayland} +export WAYLAND_SUPPORT=On +export X11_SUPPORT=Off +%else +export WAYLAND_SUPPORT=Off +export X11_SUPPORT=On +%endif + +%cmake . -DWAYLAND_SUPPORT=${WAYLAND_SUPPORT} -DX11_SUPPORT=${X11_SUPPORT} +make %{?jobs:-j%jobs} + +%install +rm -rf %{buildroot} +%make_install +mkdir -p %{buildroot}/%{_datarootdir}/license + +%post -n liblivebox-viewer -p /sbin/ldconfig +%postun -n liblivebox-viewer -p /sbin/ldconfig + +%files -n liblivebox-viewer +%manifest %{name}.manifest +%defattr(-,root,root,-) +%{_libdir}/*.so* +%{_datarootdir}/license/* + +%files devel +%manifest %{name}.manifest +%defattr(-,root,root,-) +%{_includedir}/livebox-viewer/livebox.h +%{_libdir}/pkgconfig/*.pc + +# End of a file diff --git a/src/client.c b/src/client.c new file mode 100644 index 0000000..b3088fa --- /dev/null +++ b/src/client.c @@ -0,0 +1,1770 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <pthread.h> + +#include <dlog.h> +#include <glib.h> + +#include <vconf.h> +#include <vconf-keys.h> + +#include <packet.h> +#include <com-core.h> +#include <com-core_packet.h> +#include <livebox-errno.h> +#include <livebox-service.h> +#include <secure_socket.h> + +#include "debug.h" +#include "client.h" +#include "livebox.h" +#include "livebox_internal.h" +#include "desc_parser.h" +#include "fb.h" +#include "util.h" +#include "master_rpc.h" +#include "conf.h" +#include "file_service.h" +#include "dlist.h" + +int errno; + +static struct info { + int fd; + guint timer_id; + char *client_addr; +} s_info = { + .fd = -1, + .timer_id = 0, + .client_addr = NULL, +}; + +static struct packet *master_fault_package(pid_t pid, int handle, const struct packet *packet) +{ + const char *pkgname; + const char *id; + const char *function; + + if (packet_get(packet, "sss", &pkgname, &id, &function) != 3) { + ErrPrint("Invalid arguments\n"); + return NULL; + } + + DbgPrint("[%s]\n", pkgname); + master_rpc_clear_fault_package(pkgname); + lb_invoke_fault_handler(LB_FAULT_DEACTIVATED, pkgname, id, function); + return NULL; +} + +static struct packet *master_hold_scroll(pid_t pid, int handle, const struct packet *packet) +{ + struct livebox_common *common; + struct livebox *livebox; + const char *pkgname; + const char *id; + int seize; + int ret; + struct dlist *l; + + ret = packet_get(packet, "ssi", &pkgname, &id, &seize); + if (ret != 3) { + ErrPrint("Invalid argument\n"); + goto out; + } + + common = lb_find_common_handle(pkgname, id); + if (!common) { + ErrPrint("Instance(%s) is not exists\n", id); + goto out; + } + + DbgPrint("HOLD: [%s] seize(%d)\n", id, seize); + seize = seize ? LB_EVENT_HOLD_SCROLL : LB_EVENT_RELEASE_SCROLL; + dlist_foreach(common->livebox_list, l, livebox) { + lb_invoke_event_handler(livebox, seize); + } + +out: + return NULL; +} + +static struct packet *master_pinup(pid_t pid, int handle, const struct packet *packet) +{ + const char *pkgname; + const char *id; + const char *content; + struct livebox *handler; + struct dlist *l; + struct dlist *n; + struct livebox_common *common; + char *new_content; + int ret; + int status; + int pinup; + + ret = packet_get(packet, "iisss", &status, &pinup, &pkgname, &id, &content); + if (ret != 5) { + ErrPrint("Invalid argument\n"); + goto out; + } + + common = lb_find_common_handle(pkgname, id); + if (!common) { + ErrPrint("Instance (%s) is not exists\n", id); + goto out; + } + + if (status == (int)LB_STATUS_SUCCESS) { + new_content = strdup(content); + if (new_content) { + free(common->content); + common->content = new_content; + common->is_pinned_up = pinup; + } else { + ErrPrint("Heap: %s\n", strerror(errno)); + status = LB_STATUS_ERROR_MEMORY; + } + } + + common->request.pinup = 0; + + dlist_foreach_safe(common->livebox_list, l, n, handler) { + if (handler->cbs.pinup.cb) { + ret_cb_t cb; + void *cbdata; + + /* Make sure that user can call pinup API in its result callback */ + cb = handler->cbs.pinup.cb; + cbdata = handler->cbs.pinup.data; + + handler->cbs.pinup.cb = NULL; + handler->cbs.pinup.data = NULL; + + cb(handler, status, cbdata); + } else if (status == (int)LB_STATUS_SUCCESS) { + lb_invoke_event_handler(handler, LB_EVENT_PINUP_CHANGED); + } + } + +out: + return NULL; +} + +static struct packet *master_deleted(pid_t pid, int handle, const struct packet *packet) +{ + const char *pkgname; + const char *id; + double timestamp; + struct livebox *handler; + struct livebox_common *common; + struct dlist *l; + struct dlist *n; + int reason; + + if (packet_get(packet, "ssdi", &pkgname, &id, ×tamp, &reason) != 4) { + ErrPrint("Invalid arguemnt\n"); + goto out; + } + + DbgPrint("[%s]\n", pkgname); + common = lb_find_common_handle_by_timestamp(timestamp); + if (!common) { + /*! + * \note + * This can be happens only if the user delete a livebox + * right after create it before receive created event. + */ + goto out; + } + + /*!< Check validity of this "handler" */ + if (common->state != CREATE) { + if (common->state != DELETE) { + /*! + * \note + * This is not possible + */ + ErrPrint("Already deleted handler (%s - %s)\n", pkgname, id); + return NULL; + } + } + + common->request.deleted = 0; + /*! + * We should change the state of "common handler' before handling the callbacks. + * Because if user tries to create a new handle in the callbacks, + * find_sharable_common_handle will returns destroying object. + * Then we will get panic. + * To prevent it, we should change its state first. + */ + common->state = DELETE; + + dlist_foreach_safe(common->livebox_list, l, n, handler) { + if (handler->cbs.created.cb) { + ret_cb_t cb; + void *cbdata; + /*! + * \note + * + * "if (handler->id == NULL) {" + * + * The instance is not created yet. + * But the master forcely destroy it and send destroyed event to this + * without the created event. + * + * It could be destroyed when a slave has critical error(fault) + * before creating an instance successfully. + */ + if (handler->cbs.created.cb == handler->cbs.deleted.cb) { + if (handler->cbs.created.data != handler->cbs.deleted.data) { + DbgPrint("cb is same but cbdata is different (%s - %s)\n", pkgname, id); + } + + handler->cbs.deleted.cb = NULL; + handler->cbs.deleted.data = NULL; + } + + cb = handler->cbs.created.cb; + cbdata = handler->cbs.created.data; + + handler->cbs.created.cb = NULL; + handler->cbs.created.data = NULL; + + if (reason == (int)LB_STATUS_SUCCESS) { + reason = LB_STATUS_ERROR_CANCEL; + } + + cb(handler, reason, cbdata); + } else if (common->id) { + if (handler->cbs.deleted.cb) { + ret_cb_t cb; + void *cbdata; + + cb = handler->cbs.deleted.cb; + cbdata = handler->cbs.deleted.data; + + handler->cbs.deleted.cb = NULL; + handler->cbs.deleted.data = NULL; + + cb(handler, reason, cbdata); + } else { + lb_invoke_event_handler(handler, LB_EVENT_DELETED); + } + } + + /* Just try to delete it, if a user didn't remove it from the live box list */ + lb_unref(handler, 1); + } + +out: + return NULL; +} + +static struct packet *master_lb_update_begin(pid_t pid, int handle, const struct packet *packet) +{ + struct livebox *handler; + struct livebox_common *common; + const char *pkgname; + const char *id; + const char *content; + const char *title; + const char *fbfile; + double priority; + int ret; + + ret = packet_get(packet, "ssdsss", &pkgname, &id, &priority, &content, &title, &fbfile); + if (ret != 6) { + ErrPrint("Invalid argument\n"); + goto out; + } + + common = lb_find_common_handle(pkgname, id); + if (!common) { + ErrPrint("Instance[%s] is not exists\n", id); + goto out; + } + + if (common->state != CREATE) { + ErrPrint("(%s) is not created\n", id); + goto out; + } + + lb_set_priority(common, priority); + lb_set_content(common, content); + lb_set_title(common, title); + + /*! + * \NOTE + * Width & Height is not changed in this case. + * If the active update is began, the size should not be changed, + * And if the size is changed, the provider should finish the updating first. + * And then begin updating again after change its size. + */ + if (lb_get_lb_fb(common)) { + (void)lb_set_lb_fb(common, fbfile); + + ret = lb_sync_lb_fb(common); + + if (ret != (int)LB_STATUS_SUCCESS) { + ErrPrint("Failed to do sync FB (%s - %s) (%d)\n", pkgname, fbfile, ret); + } else { + struct dlist *l; + dlist_foreach(common->livebox_list, l, handler) { + lb_invoke_event_handler(handler, LB_EVENT_LB_UPDATE_BEGIN); + } + } + } else { + ErrPrint("Invalid request[%s], %s\n", id, fbfile); + } + +out: + return NULL; +} + +static struct packet *master_pd_update_begin(pid_t pid, int handle, const struct packet *packet) +{ + struct livebox *handler; + struct livebox_common *common; + const char *pkgname; + const char *id; + const char *fbfile; + int ret; + + ret = packet_get(packet, "sss", &pkgname, &id, &fbfile); + if (ret != 2) { + ErrPrint("Invalid argument\n"); + goto out; + } + + common = lb_find_common_handle(pkgname, id); + if (!common) { + ErrPrint("Instance[%s] is not exists\n", id); + goto out; + } + + if (common->state != CREATE) { + ErrPrint("[%s] is not created\n", id); + goto out; + } + + if (lb_get_pd_fb(common)) { + (void)lb_set_pd_fb(common, fbfile); + + ret = lb_sync_pd_fb(common); + if (ret != (int)LB_STATUS_SUCCESS) { + ErrPrint("Failed to do sync FB (%s - %s) (%d)\n", pkgname, fbfile, ret); + } else { + struct dlist *l; + dlist_foreach(common->livebox_list, l, handler) { + lb_invoke_event_handler(handler, LB_EVENT_PD_UPDATE_BEGIN); + } + } + } else { + ErrPrint("Invalid request[%s], %s\n", id, fbfile); + } + +out: + return NULL; +} + +static struct packet *master_lb_update_end(pid_t pid, int handle, const struct packet *packet) +{ + struct livebox *handler; + struct livebox_common *common; + const char *pkgname; + const char *id; + int ret; + + ret = packet_get(packet, "ss", &pkgname, &id); + if (ret != 2) { + ErrPrint("Invalid argument\n"); + goto out; + } + + common = lb_find_common_handle(pkgname, id); + if (!common) { + ErrPrint("Instance[%s] is not exists\n", id); + goto out; + } + + if (common->state != CREATE) { + ErrPrint("[%s] is not created\n", id); + goto out; + } + + if (lb_get_lb_fb(common)) { + struct dlist *l; + dlist_foreach(common->livebox_list, l, handler) { + lb_invoke_event_handler(handler, LB_EVENT_LB_UPDATE_END); + } + } else { + ErrPrint("Invalid request[%s]\n", id); + } + +out: + return NULL; +} + +static struct packet *master_key_status(pid_t pid, int handle, const struct packet *packet) +{ + struct livebox *handler; + struct livebox_common *common; + struct dlist *l; + const char *pkgname; + const char *id; + int ret; + int status; + + ret = packet_get(packet, "ssi", &pkgname, &id, &status); + if (ret != 3) { + ErrPrint("Invalid argument\n"); + goto out; + } + + common = lb_find_common_handle(pkgname, id); + if (!common) { + ErrPrint("Instance[%s] is not exists\n", id); + goto out; + } + + if (common->state != CREATE) { + ErrPrint("[%s] is not created\n", id); + goto out; + } + + common->request.key_event = 0; + dlist_foreach(common->livebox_list, l, handler) { + if (handler->cbs.key_event.cb) { + ret_cb_t cb; + void *cbdata; + + cb = handler->cbs.key_event.cb; + cbdata = handler->cbs.key_event.data; + + handler->cbs.key_event.cb = NULL; + handler->cbs.key_event.data = NULL; + + cb(handler, status, cbdata); + } else { + ErrPrint("Invalid event[%s]\n", id); + } + } + +out: + return NULL; +} + +static struct packet *master_request_close_pd(pid_t pid, int handle, const struct packet *packet) +{ + struct livebox *handler; + struct livebox_common *common; + struct dlist *l; + const char *pkgname; + const char *id; + int ret; + int reason; + + ret = packet_get(packet, "ssi", &pkgname, &id, &reason); + if (ret != 3) { + ErrPrint("Invalid argument\n"); + goto out; + } + + common = lb_find_common_handle(pkgname, id); + if (!common) { + ErrPrint("Instance[%s] is not exists\n", id); + goto out; + } + + if (common->state != CREATE) { + ErrPrint("[%s] is not created\n", id); + goto out; + } + + if (!common->is_pd_created) { + DbgPrint("PD is not created, closing what?(%s)\n", id); + goto out; + } + + DbgPrint("Reason: %d\n", reason); + + dlist_foreach(common->livebox_list, l, handler) { + lb_invoke_event_handler(handler, LB_EVENT_REQUEST_CLOSE_PD); + } +out: + return NULL; +} + +static struct packet *master_access_status(pid_t pid, int handle, const struct packet *packet) +{ + struct livebox *handler; + struct livebox_common *common; + struct dlist *l; + const char *pkgname; + const char *id; + int ret; + int status; + + ret = packet_get(packet, "ssi", &pkgname, &id, &status); + if (ret != 3) { + ErrPrint("Invalid argument\n"); + goto out; + } + + common = lb_find_common_handle(pkgname, id); + if (!common) { + ErrPrint("Instance[%s] is not exists\n", id); + goto out; + } + + if (common->state != CREATE) { + ErrPrint("[%s] is not created\n", id); + goto out; + } + + common->request.access_event = 0; + dlist_foreach(common->livebox_list, l, handler) { + if (handler->cbs.access_event.cb) { + ret_cb_t cb; + void *cbdata; + + cb = handler->cbs.access_event.cb; + cbdata = handler->cbs.access_event.data; + + handler->cbs.access_event.cb = NULL; + handler->cbs.access_event.data = NULL; + + cb(handler, status, cbdata); + } + } +out: + return NULL; +} + +static struct packet *master_pd_update_end(pid_t pid, int handle, const struct packet *packet) +{ + struct livebox *handler; + struct livebox_common *common; + const char *pkgname; + const char *id; + int ret; + + ret = packet_get(packet, "ss", &pkgname, &id); + if (ret != 2) { + ErrPrint("Invalid argument\n"); + goto out; + } + + common = lb_find_common_handle(pkgname, id); + if (!common) { + ErrPrint("Instance[%s] is not exists\n", id); + goto out; + } + + if (common->state != CREATE) { + ErrPrint("[%s] is not created\n", id); + goto out; + } + + if (lb_get_lb_fb(common)) { + struct dlist *l; + + dlist_foreach(common->livebox_list, l, handler) { + lb_invoke_event_handler(handler, LB_EVENT_PD_UPDATE_END); + } + } else { + ErrPrint("Invalid request[%s]", id); + } + +out: + return NULL; +} + +static struct packet *master_lb_updated(pid_t pid, int handle, const struct packet *packet) +{ + const char *pkgname; + const char *id; + const char *fbfile; + const char *content; + const char *title; + const char *safe_file; + const char *icon; + const char *name; + struct livebox *handler; + struct livebox_common *common; + int lb_w; + int lb_h; + double priority; + int ret; + + ret = packet_get(packet, "sssiidsssss", + &pkgname, &id, + &fbfile, &lb_w, &lb_h, + &priority, &content, &title, + &safe_file, &icon, &name); + if (ret != 11) { + ErrPrint("Invalid argument\n"); + goto out; + } + + common = lb_find_common_handle(pkgname, id); + if (!common) { + ErrPrint("instance(%s) is not exists\n", id); + goto out; + } + + if (common->state != CREATE) { + /*! + * \note + * Already deleted by the user. + * Don't try to notice anything with this, Just ignore all events + * Beacuse the user doesn't wants know about this anymore + */ + ErrPrint("(%s) is not exists, but updated\n", id); + goto out; + } + + lb_set_priority(common, priority); + lb_set_content(common, content); + lb_set_title(common, title); + lb_set_size(common, lb_w, lb_h); + lb_set_filename(common, safe_file); + + if (lb_text_lb(common)) { + const char *common_filename; + + common_filename = common->filename ? common->filename : util_uri_to_path(common->id); + + (void)parse_desc(common, common_filename, 0); + /*! + * \note + * DESC parser will call the "text event callback". + * Don't need to call global event callback in this case. + */ + goto out; + } else if (lb_get_lb_fb(common)) { + /*! + * \todo + * replace this with "flag" instead of "callback address" + */ + if (conf_frame_drop_for_resizing() && common->request.size_changed) { + /* Just for skipping the update event callback call, After request to resize buffer, update event will be discarded */ + DbgPrint("Discards obsoloted update event\n"); + ret = LB_STATUS_ERROR_BUSY; + } else { + (void)lb_set_lb_fb(common, fbfile); + + if (!conf_manual_sync()) { + ret = lb_sync_lb_fb(common); + if (ret != (int)LB_STATUS_SUCCESS) { + ErrPrint("Failed to do sync FB (%s - %s) (%d)\n", pkgname, util_basename(util_uri_to_path(id)), ret); + } + } else { + ret = LB_STATUS_SUCCESS; + } + } + } else { + ret = LB_STATUS_SUCCESS; + } + + if (ret == (int)LB_STATUS_SUCCESS) { + struct dlist *l; + struct dlist *n; + + dlist_foreach_safe(common->livebox_list, l, n, handler) { + lb_invoke_event_handler(handler, LB_EVENT_LB_UPDATED); + } + } + +out: + return NULL; +} + +static struct packet *master_pd_created(pid_t pid, int handle, const struct packet *packet) +{ + struct livebox *handler; + struct livebox_common *common; + const char *pkgname; + const char *id; + const char *buf_id; + struct dlist *l; + struct dlist *n; + int width; + int height; + int ret; + int status; + + ret = packet_get(packet, "sssiii", &pkgname, &id, &buf_id, &width, &height, &status); + if (ret != 6) { + ErrPrint("Invalid argument\n"); + goto out; + } + + DbgPrint("[%s]\n", pkgname); + common = lb_find_common_handle(pkgname, id); + if (!common) { + ErrPrint("Instance(%s) is not exists\n", id); + goto out; + } + + if (common->state != CREATE) { + ErrPrint("Instance(%s) is not created\n", id); + goto out; + } + + if (!common->request.pd_created) { + ErrPrint("PD create request is canceled\n"); + goto out; + } + + common->is_pd_created = (status == (int)LB_STATUS_SUCCESS); + common->request.pd_created = 0; + + if (common->is_pd_created) { + lb_set_pdsize(common, width, height); + if (lb_text_pd(common)) { + DbgPrint("Text TYPE does not need to handle this\n"); + } else { + (void)lb_set_pd_fb(common, buf_id); + + switch (common->pd.type) { + case _PD_TYPE_SCRIPT: + case _PD_TYPE_BUFFER: + switch (fb_type(lb_get_pd_fb(common))) { + case BUFFER_TYPE_FILE: + case BUFFER_TYPE_SHM: + lb_create_lock_file(common, 1); + break; + case BUFFER_TYPE_PIXMAP: + case BUFFER_TYPE_ERROR: + default: + break; + } + break; + case _PD_TYPE_TEXT: + default: + break; + } + + ret = lb_sync_pd_fb(common); + if (ret < 0) { + ErrPrint("Failed to do sync FB (%s - %s)\n", pkgname, util_basename(util_uri_to_path(id))); + } + } + } + + DbgPrint("PERF_DBOX\n"); + dlist_foreach_safe(common->livebox_list, l, n, handler) { + if (handler->cbs.pd_created.cb) { + ret_cb_t cb; + void *cbdata; + + cb = handler->cbs.pd_created.cb; + cbdata = handler->cbs.pd_created.data; + + handler->cbs.pd_created.cb = NULL; + handler->cbs.pd_created.data = NULL; + + /*! + * Before call the Callback function, + * pd_create_cb must be reset. + * Because, in the create callback, user can call create_pd function again. + */ + cb(handler, status, cbdata); + } else if (status == (int)LB_STATUS_SUCCESS) { + lb_invoke_event_handler(handler, LB_EVENT_PD_CREATED); + } + } + +out: + return NULL; +} + +static struct packet *master_pd_destroyed(pid_t pid, int handle, const struct packet *packet) +{ + struct livebox *handler; + struct dlist *l; + struct livebox_common *common; + const char *pkgname; + const char *id; + int ret; + int status; + + ret = packet_get(packet, "ssi", &pkgname, &id, &status); + if (ret != 3) { + ErrPrint("Invalid argument\n"); + goto out; + } + + DbgPrint("[%s]\n", pkgname); + common = lb_find_common_handle(pkgname, id); + if (!common) { + ErrPrint("Instance(%s) is not exists\n", id); + goto out; + } + + if (common->state != CREATE) { + ErrPrint("Instance(%s) is not created\n", id); + goto out; + } + + if (common->is_pd_created == 0) { + ErrPrint("PD is not created, event is ignored\n"); + goto out; + } + + common->is_pd_created = 0; + common->request.pd_destroyed = 0; + + dlist_foreach(common->livebox_list, l, handler) { + if (handler->cbs.pd_destroyed.cb) { + ret_cb_t cb; + void *cbdata; + + cb = handler->cbs.pd_destroyed.cb; + cbdata = handler->cbs.pd_destroyed.data; + + handler->cbs.pd_destroyed.cb = NULL; + handler->cbs.pd_destroyed.data = NULL; + + /*! + * Before call the Callback function, + * pd_destroyed_cb must be reset. + * Because, in the create callback, user can call destroy_pd function again. + */ + cb(handler, status, cbdata); + } else if (status == (int)LB_STATUS_SUCCESS) { + lb_invoke_event_handler(handler, LB_EVENT_PD_DESTROYED); + } + } + + /*! + * \note + * Lock file should be deleted after all callbacks are processed. + */ + switch (common->pd.type) { + case _PD_TYPE_SCRIPT: + case _PD_TYPE_BUFFER: + switch (fb_type(lb_get_pd_fb(common))) { + case BUFFER_TYPE_FILE: + case BUFFER_TYPE_SHM: + lb_destroy_lock_file(common, 1); + break; + case BUFFER_TYPE_PIXMAP: + case BUFFER_TYPE_ERROR: + default: + break; + } + break; + case _PD_TYPE_TEXT: + default: + break; + } + +out: + return NULL; +} + +static struct packet *master_pd_updated(pid_t pid, int handle, const struct packet *packet) +{ + const char *pkgname; + const char *id; + const char *descfile; + const char *fbfile; + int ret; + struct livebox *handler; + struct livebox_common *common; + struct dlist *l; + int pd_w; + int pd_h; + + ret = packet_get(packet, "ssssii", + &pkgname, &id, + &descfile, &fbfile, + &pd_w, &pd_h); + if (ret != 6) { + ErrPrint("Invalid argument\n"); + goto out; + } + + DbgPrint("[%s]\n", pkgname); + common = lb_find_common_handle(pkgname, id); + if (!common) { + ErrPrint("Instance(%s) is not exists\n", id); + goto out; + } + + if (common->state != CREATE) { + /*! + * \note + * This handler is already deleted by the user. + * So don't try to notice anything about this anymore. + * Just ignore all events. + */ + ErrPrint("Instance(%s) is not created\n", id); + goto out; + } + + lb_set_pdsize(common, pd_w, pd_h); + + if (lb_text_pd(common)) { + (void)parse_desc(common, descfile, 1); + } else { + if (conf_frame_drop_for_resizing() && common->request.size_changed) { + /* Just for skipping the update event callback call, After request to resize buffer, update event will be discarded */ + DbgPrint("Discards obsoloted update event\n"); + } else { + (void)lb_set_pd_fb(common, fbfile); + + if (!conf_manual_sync()) { + ret = lb_sync_pd_fb(common); + if (ret < 0) { + ErrPrint("Failed to do sync FB (%s - %s), %d\n", pkgname, util_basename(util_uri_to_path(id)), ret); + } else { + dlist_foreach(common->livebox_list, l, handler) { + lb_invoke_event_handler(handler, LB_EVENT_PD_UPDATED); + } + } + } else { + dlist_foreach(common->livebox_list, l, handler) { + lb_invoke_event_handler(handler, LB_EVENT_PD_UPDATED); + } + } + } + } + +out: + return NULL; +} + +static struct packet *master_update_mode(pid_t pid, int handle, const struct packet *packet) +{ + struct livebox *handler; + struct livebox_common *common; + struct dlist *l; + struct dlist *n; + const char *pkgname; + const char *id; + int active_mode; + int status; + int ret; + + if (!packet) { + ErrPrint("Invalid packet\n"); + goto out; + } + + ret = packet_get(packet, "ssii", &pkgname, &id, &status, &active_mode); + if (ret != 4) { + ErrPrint("Invalid argument\n"); + goto out; + } + + common = lb_find_common_handle(pkgname, id); + if (!common) { + ErrPrint("Livebox(%s) is not found\n", id); + goto out; + } + + if (common->state != CREATE) { + ErrPrint("Livebox(%s) is not created yet\n", id); + goto out; + } + + if (status == (int)LB_STATUS_SUCCESS) { + lb_set_update_mode(common, active_mode); + } + + common->request.update_mode = 0; + dlist_foreach_safe(common->livebox_list, l, n, handler) { + if (handler->cbs.update_mode.cb) { + ret_cb_t cb; + void *cbdata; + + cb = handler->cbs.update_mode.cb; + cbdata = handler->cbs.update_mode.data; + + handler->cbs.update_mode.cb = NULL; + handler->cbs.update_mode.data = NULL; + + cb(handler, status, cbdata); + } else if (status == (int)LB_STATUS_SUCCESS) { + lb_invoke_event_handler(handler, LB_EVENT_UPDATE_MODE_CHANGED); + } + } + +out: + return NULL; +} + +static struct packet *master_size_changed(pid_t pid, int handle, const struct packet *packet) +{ + struct livebox *handler; + struct livebox_common *common; + const char *pkgname; + const char *id; + const char *fbfile; + int status; + int ret; + int w; + int h; + int is_pd; + + if (!packet) { + ErrPrint("Invalid packet\n"); + goto out; + } + + ret = packet_get(packet, "sssiiii", &pkgname, &id, &fbfile, &is_pd, &w, &h, &status); + if (ret != 7) { + ErrPrint("Invalid argument\n"); + goto out; + } + + common = lb_find_common_handle(pkgname, id); + if (!common) { + ErrPrint("Livebox(%s) is not found\n", id); + goto out; + } + + if (common->state != CREATE) { + ErrPrint("Livebox(%s) is not created yet\n", id); + goto out; + } + + common->request.size_changed = 0; + if (is_pd) { + /*! + * \NOTE + * PD is not able to resized by the client. + * PD is only can be managed by the provider. + * So the PD has no private resized event handler. + * Notify it via global event handler only. + */ + if (status == (int)LB_STATUS_SUCCESS) { + struct dlist *l; + + lb_set_pdsize(common, w, h); + dlist_foreach(common->livebox_list, l, handler) { + lb_invoke_event_handler(handler, LB_EVENT_PD_SIZE_CHANGED); + } + } else { + ErrPrint("This is not possible. PD Size is changed but the return value is not ZERO (%d)\n", status); + } + } else { + struct dlist *l; + struct dlist *n; + + if (status == (int)LB_STATUS_SUCCESS) { + lb_set_size(common, w, h); + + /*! + * \NOTE + * If there is a created LB FB, + * Update it too. + */ + if (lb_get_lb_fb(common)) { + (void)lb_set_lb_fb(common, fbfile); + + ret = lb_sync_lb_fb(common); + if (ret < 0) { + ErrPrint("Failed to do sync FB (%s - %s)\n", pkgname, util_basename(util_uri_to_path(id))); + } + + /* Just update the size info only. */ + } + } + + /*! + * \NOTE + * I cannot believe client. + * So I added some log before & after call the user callback. + */ + dlist_foreach_safe(common->livebox_list, l, n, handler) { + if (handler->cbs.size_changed.cb) { + ret_cb_t cb; + void *cbdata; + + cb = handler->cbs.size_changed.cb; + cbdata = handler->cbs.size_changed.data; + + handler->cbs.size_changed.cb = NULL; + handler->cbs.size_changed.data = NULL; + + cb(handler, status, cbdata); + } else if (status == (int)LB_STATUS_SUCCESS) { + lb_invoke_event_handler(handler, LB_EVENT_LB_SIZE_CHANGED); + } + } + } + +out: + return NULL; +} + +static struct packet *master_period_changed(pid_t pid, int handle, const struct packet *packet) +{ + struct livebox *handler; + struct livebox_common *common; + struct dlist *l; + struct dlist *n; + const char *pkgname; + const char *id; + int ret; + double period; + int status; + + ret = packet_get(packet, "idss", &status, &period, &pkgname, &id); + if (ret != 4) { + ErrPrint("Invalid argument\n"); + goto out; + } + + common = lb_find_common_handle(pkgname, id); + if (!common) { + ErrPrint("Livebox(%s) is not found\n", id); + goto out; + } + + if (common->state != CREATE) { + ErrPrint("Livebox(%s) is not created\n", id); + goto out; + } + + if (status == (int)LB_STATUS_SUCCESS) { + lb_set_period(common, period); + } + + common->request.period_changed = 0; + + dlist_foreach_safe(common->livebox_list, l, n, handler) { + if (handler->cbs.period_changed.cb) { + ret_cb_t cb; + void *cbdata; + + cb = handler->cbs.period_changed.cb; + cbdata = handler->cbs.period_changed.data; + + handler->cbs.period_changed.cb = NULL; + handler->cbs.period_changed.data = NULL; + + cb(handler, status, cbdata); + } else if (status == (int)LB_STATUS_SUCCESS) { + lb_invoke_event_handler(handler, LB_EVENT_PERIOD_CHANGED); + } + } + +out: + return NULL; +} + +static struct packet *master_group_changed(pid_t pid, int handle, const struct packet *packet) +{ + struct livebox *handler; + struct livebox_common *common; + struct dlist *l; + struct dlist *n; + const char *pkgname; + const char *id; + int ret; + const char *cluster; + const char *category; + int status; + + ret = packet_get(packet, "ssiss", &pkgname, &id, &status, &cluster, &category); + if (ret != 5) { + ErrPrint("Invalid argument\n"); + goto out; + } + + common = lb_find_common_handle(pkgname, id); + if (!common) { + ErrPrint("Livebox(%s) is not exists\n", id); + goto out; + } + + if (common->state != CREATE) { + /*! + * \note + * Do no access this handler, + * You cannot believe this handler anymore. + */ + ErrPrint("Livebox(%s) is not created\n", id); + goto out; + } + + if (status == (int)LB_STATUS_SUCCESS) { + (void)lb_set_group(common, cluster, category); + } + + common->request.group_changed = 0; + + dlist_foreach_safe(common->livebox_list, l, n, handler) { + if (handler->cbs.group_changed.cb) { + ret_cb_t cb; + void *cbdata; + + cb = handler->cbs.group_changed.cb; + cbdata = handler->cbs.group_changed.data; + + handler->cbs.group_changed.cb = NULL; + handler->cbs.group_changed.data = NULL; + + cb(handler, status, cbdata); + } else if (status == (int)LB_STATUS_SUCCESS) { + lb_invoke_event_handler(handler, LB_EVENT_GROUP_CHANGED); + } + } + +out: + return NULL; +} + +static struct packet *master_created(pid_t pid, int handle, const struct packet *packet) +{ + struct livebox *handler; + struct livebox_common *common; + struct dlist *l; + + int lb_w; + int lb_h; + int pd_w; + int pd_h; + const char *pkgname; + const char *id; + + const char *content; + const char *cluster; + const char *category; + const char *lb_fname; + const char *pd_fname; + const char *title; + + double timestamp; + const char *auto_launch; + double priority; + int size_list; + int user; + int pinup_supported; + enum lb_type lb_type; + enum pd_type pd_type; + double period; + int is_pinned_up; + + int old_state = DESTROYED; + + int ret; + + ret = packet_get(packet, "dsssiiiisssssdiiiiidsi", + ×tamp, + &pkgname, &id, &content, + &lb_w, &lb_h, &pd_w, &pd_h, + &cluster, &category, &lb_fname, &pd_fname, + &auto_launch, &priority, &size_list, &user, &pinup_supported, + &lb_type, &pd_type, &period, &title, &is_pinned_up); + if (ret != 22) { + ErrPrint("Invalid argument\n"); + ret = LB_STATUS_ERROR_INVALID; + goto out; + } + + ErrPrint("[%lf] pkgname: %s, id: %s, content: %s, " + "pd_w: %d, pd_h: %d, lb_w: %d, lb_h: %d, " + "cluster: %s, category: %s, lb_fname: \"%s\", pd_fname: \"%s\", " + "auto_launch: %s, priority: %lf, size_list: %d, user: %d, pinup: %d, " + "lb_type: %d, pd_type: %d, period: %lf, title: [%s], is_pinned_up: %d\n", + timestamp, pkgname, id, content, + pd_w, pd_h, lb_w, lb_h, + cluster, category, lb_fname, pd_fname, + auto_launch, priority, size_list, user, pinup_supported, + lb_type, pd_type, period, title, is_pinned_up); + + common = lb_find_common_handle_by_timestamp(timestamp); + if (!common) { + handler = lb_new_livebox(pkgname, id, timestamp, cluster, category); + if (!handler) { + ErrPrint("Failed to create a new livebox\n"); + ret = LB_STATUS_ERROR_FAULT; + goto out; + } + common = handler->common; + old_state = common->state; + } else { + if (common->state != CREATE) { + if (common->state != DELETE) { + /*! + * \note + * This is not possible!!! + */ + ErrPrint("Invalid handler\n"); + ret = LB_STATUS_ERROR_INVALID; + goto out; + } + + /*! + * \note + * After get the delete states, + * call the create callback with deleted result. + */ + } + + old_state = common->state; + + if (common->id) { + ErrPrint("Already created: timestamp[%lf] " + "pkgname[%s], id[%s] content[%s] " + "cluster[%s] category[%s] lb_fname[%s] pd_fname[%s]\n", + timestamp, pkgname, id, + content, cluster, category, + lb_fname, pd_fname); + + ret = LB_STATUS_ERROR_ALREADY; + goto out; + } + + lb_set_id(common, id); + } + + common->request.created = 0; + lb_set_size(common, lb_w, lb_h); + common->lb.type = lb_type; + common->is_pinned_up = is_pinned_up; + + switch (lb_type) { + case _LB_TYPE_FILE: + break; + case _LB_TYPE_SCRIPT: + case _LB_TYPE_BUFFER: + if (!strlen(lb_fname)) { + break; + } + (void)lb_set_lb_fb(common, lb_fname); + + /*! + * \note + * Livebox should create the lock file from here. + * Even if the old_state == DELETE, + * the lock file will be deleted from deleted event callback. + */ + switch (fb_type(lb_get_lb_fb(common))) { + case BUFFER_TYPE_FILE: + case BUFFER_TYPE_SHM: + lb_create_lock_file(common, 0); + break; + case BUFFER_TYPE_PIXMAP: + case BUFFER_TYPE_ERROR: + default: + break; + } + + ret = lb_sync_lb_fb(common); + if (ret < 0) { + ErrPrint("Failed to do sync FB (%s - %s)\n", pkgname, util_basename(util_uri_to_path(id))); + } + break; + case _LB_TYPE_TEXT: + lb_set_text_lb(common); + break; + default: + break; + } + + common->pd.type = pd_type; + lb_set_pdsize(common, pd_w, pd_h); + lb_set_default_pdsize(common, pd_w, pd_h); + switch (pd_type) { + case _PD_TYPE_SCRIPT: + case _PD_TYPE_BUFFER: + if (!strlen(pd_fname)) { + break; + } + + lb_set_pd_fb(common, pd_fname); + + ret = lb_sync_pd_fb(common); + if (ret < 0) { + ErrPrint("Failed to do sync FB (%s - %s)\n", pkgname, util_basename(util_uri_to_path(id))); + } + + /*! + * \brief + * PD doesn't need to create the lock file from here. + * Just create it from PD_CREATED event. + */ + + break; + case _PD_TYPE_TEXT: + lb_set_text_pd(common); + break; + default: + break; + } + + lb_set_priority(common, priority); + + lb_set_size_list(common, size_list); + lb_set_group(common, cluster, category); + + lb_set_content(common, content); + lb_set_title(common, title); + + lb_set_user(common, user); + + lb_set_auto_launch(common, auto_launch); + lb_set_pinup(common, pinup_supported); + + lb_set_period(common, period); + + ret = 0; + + if (common->state == CREATE) { + dlist_foreach(common->livebox_list, l, handler) { + /*! + * \note + * These callback can change the handler->state. + * So we have to use the "old_state" which stored state before call these callbacks + */ + + if (handler->cbs.created.cb) { + ret_cb_t cb; + void *cbdata; + + cb = handler->cbs.created.cb; + cbdata = handler->cbs.created.data; + + handler->cbs.created.cb = NULL; + handler->cbs.created.data = NULL; + + cb(handler, ret, cbdata); + } else { + lb_invoke_event_handler(handler, LB_EVENT_CREATED); + } + } + } + +out: + if (ret == 0 && old_state == DELETE) { + struct dlist *n; + + DbgPrint("Take place an unexpected case [%d]\n", common->refcnt); + dlist_foreach_safe(common->livebox_list, l, n, handler) { + if (handler->cbs.created.cb) { + if (!handler->common->request.deleted) { + if (lb_send_delete(handler, common->delete_type, handler->cbs.created.cb, handler->cbs.created.data) < 0) { + /*! + * \note + * Already sent or something else happens. + * Callback will be called in any cases + */ + } + } else if (handler->state != DELETE) { + handler->cbs.created.cb(handler, LB_STATUS_ERROR_CANCEL, handler->cbs.created.data); + lb_unref(handler, 1); + } + } else { + lb_invoke_event_handler(handler, LB_EVENT_DELETED); + lb_unref(handler, 1); + } + } + + /*! + * \note + * handler->cbs.created.cb = NULL; + * handler->cbs.created.data = NULL; + * + * Do not clear this to use this from the deleted event callback. + * if this value is not cleared when the deleted event callback check it, + * it means that the created function is not called yet. + * Then the call the deleted event callback with LB_STATUS_ERROR_CANCEL errno. + */ + } + + return NULL; +} + +static struct method s_table[] = { + { + .cmd = "lb_updated", /* pkgname, id, lb_w, lb_h, priority, ret */ + .handler = master_lb_updated, + }, + { + .cmd = "pd_updated", /* pkgname, id, descfile, pd_w, pd_h, ret */ + .handler = master_pd_updated, + }, + { + .cmd = "pd_created", + .handler = master_pd_created, + }, + { + .cmd = "pd_destroyed", + .handler = master_pd_destroyed, + }, + { + .cmd = "fault_package", /* pkgname, id, function, ret */ + .handler = master_fault_package, + }, + { + .cmd = "deleted", /* pkgname, id, timestamp, ret */ + .handler = master_deleted, + }, + { + .cmd = "created", /* timestamp, pkgname, id, content, lb_w, lb_h, pd_w, pd_h, cluster, category, lb_file, pd_file, auto_launch, priority, size_list, is_user, pinup_supported, text_lb, text_pd, period, ret */ + .handler = master_created, + }, + { + .cmd = "group_changed", + .handler = master_group_changed, + }, + { + .cmd = "period_changed", + .handler = master_period_changed, + }, + { + .cmd = "size_changed", + .handler = master_size_changed, + }, + { + .cmd = "pinup", + .handler = master_pinup, + }, + { + .cmd = "scroll", + .handler = master_hold_scroll, + }, + + { + .cmd = "update_mode", + .handler = master_update_mode, + }, + + { + .cmd = "lb_update_begin", + .handler = master_lb_update_begin, + }, + { + .cmd = "lb_update_end", + .handler = master_lb_update_end, + }, + + { + .cmd = "pd_update_begin", + .handler = master_pd_update_begin, + }, + { + .cmd = "pd_update_end", + .handler = master_pd_update_end, + }, + + { + .cmd = "access_status", + .handler = master_access_status, + }, + { + .cmd = "key_status", + .handler = master_key_status, + }, + { + .cmd = "close_pd", + .handler = master_request_close_pd, + }, + + { + .cmd = NULL, + .handler = NULL, + }, +}; + +static void acquire_cb(struct livebox *handler, const struct packet *result, void *data) +{ + if (!result) { + DbgPrint("Result packet is not valid\n"); + } else { + int ret; + + if (packet_get(result, "i", &ret) != 1) { + ErrPrint("Invalid argument\n"); + } else { + DbgPrint("Acquire returns: %d\n", ret); + } + } + + return; +} + +static inline int make_connection(void) +{ + struct packet *packet; + int ret; + + DbgPrint("Let's making connection!\n"); + + s_info.fd = com_core_packet_client_init(client_addr(), 0, s_table); + if (s_info.fd < 0) { + ErrPrint("Try this again later\n"); + return LB_STATUS_ERROR_IO; + } + + packet = packet_create("acquire", "d", util_timestamp()); + if (!packet) { + com_core_packet_client_fini(s_info.fd); + s_info.fd = -1; + return LB_STATUS_ERROR_FAULT; + } + + ret = master_rpc_async_request(NULL, packet, 1, acquire_cb, NULL); + if (ret < 0) { + ErrPrint("Master RPC returns %d\n", ret); + com_core_packet_client_fini(s_info.fd); + s_info.fd = -1; + return LB_STATUS_ERROR_IO; + } + + return LB_STATUS_SUCCESS; +} + +static int connected_cb(int handle, void *data) +{ + master_rpc_check_and_fire_consumer(); + return 0; +} + +static void master_started_cb(keynode_t *node, void *data) +{ + int state = 0; + + if (vconf_get_bool(VCONFKEY_MASTER_STARTED, &state) < 0) { + ErrPrint("Unable to get [%s]\n", VCONFKEY_MASTER_STARTED); + } + + DbgPrint("Master state: %d\n", state); + if (state == 1 && make_connection() == (int)LB_STATUS_SUCCESS) { + int ret; + ret = vconf_ignore_key_changed(VCONFKEY_MASTER_STARTED, master_started_cb); + if (ret < 0) { + DbgPrint("master_started vconf key de-registered [%d]\n", ret); + } + } +} + +static gboolean timeout_cb(gpointer data) +{ + if (vconf_notify_key_changed(VCONFKEY_MASTER_STARTED, master_started_cb, NULL) < 0) { + ErrPrint("Failed to add vconf for monitoring service state\n"); + } else { + DbgPrint("vconf event callback is registered\n"); + } + + master_started_cb(NULL, NULL); + + s_info.timer_id = 0; + return FALSE; +} + +static int disconnected_cb(int handle, void *data) +{ + if (s_info.fd != handle) { + /*!< This handle is not my favor */ + return 0; + } + + s_info.fd = -1; /*!< Disconnected */ + + master_rpc_clear_all_request(); + lb_invoke_fault_handler(LB_FAULT_PROVIDER_DISCONNECTED, MASTER_PKGNAME, "default", "disconnected"); + + lb_delete_all(); + + /* Try to reconnect after 1 sec later */ + if (!s_info.timer_id) { + DbgPrint("Reconnecting timer is added\n"); + s_info.timer_id = g_timeout_add(1000, timeout_cb, NULL); + if (s_info.timer_id == 0) { + ErrPrint("Unable to add reconnecting timer\n"); + return 0; + } + } else { + ErrPrint("Reconnecting timer is already exists\n"); + } + + return 0; +} + +int client_init(int use_thread) +{ + com_core_packet_use_thread(use_thread); + + s_info.client_addr = vconf_get_str(VCONFKEY_MASTER_CLIENT_ADDR); + if (!s_info.client_addr) { + s_info.client_addr = strdup(CLIENT_SOCKET); + if (!s_info.client_addr) { + ErrPrint("Heap: %s\n", strerror(errno)); + return -ENOMEM; + } + } + + (void)file_service_init(); + + DbgPrint("Server Address: %s\n", s_info.client_addr); + + com_core_add_event_callback(CONNECTOR_DISCONNECTED, disconnected_cb, NULL); + com_core_add_event_callback(CONNECTOR_CONNECTED, connected_cb, NULL); + if (vconf_notify_key_changed(VCONFKEY_MASTER_STARTED, master_started_cb, NULL) < 0) { + ErrPrint("Failed to add vconf for service state\n"); + } else { + DbgPrint("vconf event callback is registered\n"); + } + + master_started_cb(NULL, NULL); + return 0; +} + +int client_fd(void) +{ + return s_info.fd; +} + +const char *client_addr(void) +{ + return s_info.client_addr; +} + +int client_fini(void) +{ + int ret; + + (void)file_service_fini(); + + ret = vconf_ignore_key_changed(VCONFKEY_MASTER_STARTED, master_started_cb); + if (ret < 0) { + DbgPrint("Ignore vconf key: %d\n", ret); + } + + com_core_del_event_callback(CONNECTOR_DISCONNECTED, disconnected_cb, NULL); + com_core_del_event_callback(CONNECTOR_CONNECTED, connected_cb, NULL); + com_core_packet_client_fini(s_info.fd); + s_info.fd = -1; + free(s_info.client_addr); + s_info.client_addr = NULL; + return LB_STATUS_SUCCESS; +} + +/* End of a file */ diff --git a/src/conf.c b/src/conf.c new file mode 100644 index 0000000..1c1f898 --- /dev/null +++ b/src/conf.c @@ -0,0 +1,57 @@ +#include <stdio.h> + +static struct info { + int manual_sync; + int frame_drop_for_resizing; + int shared_content; + + double event_filter; +} s_info = { + .manual_sync = 0, + .frame_drop_for_resizing = 1, + .shared_content = 0, + + .event_filter = 0.01f, +}; + +void conf_set_manual_sync(int flag) +{ + s_info.manual_sync = flag; +} + +int conf_manual_sync(void) +{ + return s_info.manual_sync; +} + +void conf_set_frame_drop_for_resizing(int flag) +{ + s_info.frame_drop_for_resizing = flag; +} + +int conf_frame_drop_for_resizing(void) +{ + return s_info.frame_drop_for_resizing; +} + +void conf_set_shared_content(int flag) +{ + s_info.shared_content = flag; +} + +int conf_shared_content(void) +{ + return s_info.shared_content; +} + +double conf_event_filter(void) +{ + return s_info.event_filter; +} + +void conf_set_event_filter(double filter) +{ + s_info.event_filter = filter; +} + +/* End of a file */ diff --git a/src/desc_parser.c b/src/desc_parser.c new file mode 100644 index 0000000..ac056ac --- /dev/null +++ b/src/desc_parser.c @@ -0,0 +1,697 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> /* malloc */ +#include <string.h> /* strdup */ +#include <ctype.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <gio/gio.h> +#include <dlog.h> +#include <livebox-errno.h> +#include <livebox-service.h> + +#include "debug.h" +#include "livebox.h" +#include "livebox_internal.h" +#include "desc_parser.h" +#include "dlist.h" +#include "util.h" + +#define INFO_SIZE "size" +#define INFO_CATEGORY "category" + +static const char *type_list[] = { + "access", + "access,operation", + "color", + "drag", + "image", + "info", + "script", + "signal", + "text", + NULL +}; + +static const char *field_list[] = { + "type", + "part", + "data", + "option", + "id", + "target", + "file", + NULL +}; + +enum block_type { + TYPE_ACCESS, + TYPE_ACCESS_OP, + TYPE_COLOR, + TYPE_DRAG, + TYPE_IMAGE, + TYPE_INFO, + TYPE_SCRIPT, + TYPE_SIGNAL, + TYPE_TEXT, + TYPE_MAX +}; + +enum field_type { + FIELD_TYPE, + FIELD_PART, + FIELD_DATA, + FIELD_OPTION, + FIELD_ID, + FIELD_TARGET, + FIELD_FILE +}; + +struct block { + enum block_type type; + char *part; + char *data; + char *option; + char *id; + char *target; + char *file; + + /* Should be released */ + char *filebuf; + const char *filename; +}; + +static int update_text(struct livebox *handle, struct block *block, int is_pd) +{ + struct livebox_script_operators *ops; + + if (!block || !block->part || !block->data) { + ErrPrint("Invalid argument\n"); + return LB_STATUS_ERROR_INVALID; + } + + ops = is_pd ? &handle->cbs.pd_ops : &handle->cbs.lb_ops; + if (ops->update_text) { + ops->update_text(handle, (const char *)block->id, (const char *)block->part, (const char *)block->data); + } + + return 0; +} + +static int update_image(struct livebox *handle, struct block *block, int is_pd) +{ + struct livebox_script_operators *ops; + + if (!block || !block->part) { + ErrPrint("Invalid argument\n"); + return LB_STATUS_ERROR_INVALID; + } + + ops = is_pd ? &handle->cbs.pd_ops : &handle->cbs.lb_ops; + if (ops->update_image) { + ops->update_image(handle, block->id, block->part, block->data, block->option); + } + + return 0; +} + +static int update_script(struct livebox *handle, struct block *block, int is_pd) +{ + struct livebox_script_operators *ops; + + if (!block || !block->part) { + ErrPrint("Invalid argument\n"); + return LB_STATUS_ERROR_INVALID; + } + + ops = is_pd ? &handle->cbs.pd_ops : &handle->cbs.lb_ops; + if (ops->update_script) { + ops->update_script(handle, block->id, block->target, block->part, block->data, block->option); + } + + return 0; +} + +static int update_signal(struct livebox *handle, struct block *block, int is_pd) +{ + struct livebox_script_operators *ops; + + if (!block) { + ErrPrint("Invalid argument\n"); + return LB_STATUS_ERROR_INVALID; + } + + ops = is_pd ? &handle->cbs.pd_ops : &handle->cbs.lb_ops; + if (ops->update_signal) { + ops->update_signal(handle, block->id, block->data, block->part); + } + + return 0; +} + +static int update_drag(struct livebox *handle, struct block *block, int is_pd) +{ + double dx, dy; + struct livebox_script_operators *ops; + + if (!block || !block->data || !block->part) { + ErrPrint("Invalid argument\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (sscanf(block->data, "%lfx%lf", &dx, &dy) != 2) { + ErrPrint("Invalid format of data\n"); + return LB_STATUS_ERROR_INVALID; + } + + ops = is_pd ? &handle->cbs.pd_ops : &handle->cbs.lb_ops; + if (ops->update_drag) { + ops->update_drag(handle, block->id, block->part, dx, dy); + } + + return 0; +} + +static int update_info(struct livebox *handle, struct block *block, int is_pd) +{ + struct livebox_script_operators *ops; + + if (!block || !block->part || !block->data) { + ErrPrint("Invalid argument\n"); + return LB_STATUS_ERROR_INVALID; + } + + ops = is_pd ? &handle->cbs.pd_ops : &handle->cbs.lb_ops; + if (!strcasecmp(block->part, INFO_SIZE)) { + int w, h; + + if (sscanf(block->data, "%dx%d", &w, &h) != 2) { + ErrPrint("Invalid format (%s)\n", block->data); + return LB_STATUS_ERROR_INVALID; + } + + if (ops->update_info_size) { + ops->update_info_size(handle, block->id, w, h); + } + } else if (!strcasecmp(block->part, INFO_CATEGORY)) { + if (ops->update_info_category) { + ops->update_info_category(handle, block->id, block->data); + } + } + + return 0; +} + +static int update_access(struct livebox *handle, struct block *block, int is_pd) +{ + struct livebox_script_operators *ops; + + if (!block) { + ErrPrint("Invalid argument\n"); + return LB_STATUS_ERROR_INVALID; + } + + ops = is_pd ? &handle->cbs.pd_ops : &handle->cbs.lb_ops; + if (ops->update_access) { + ops->update_access(handle, block->id, block->part, block->data, block->option); + } + + return 0; +} + +static int operate_access(struct livebox *handle, struct block *block, int is_pd) +{ + struct livebox_script_operators *ops; + + if (!block) { + ErrPrint("Invalid argument\n"); + return LB_STATUS_ERROR_INVALID; + } + + ops = is_pd ? &handle->cbs.pd_ops : &handle->cbs.lb_ops; + if (ops->operate_access) { + ops->operate_access(handle, block->id, block->part, block->data, block->option); + } + + return 0; +} + +static int update_color(struct livebox *handle, struct block *block, int is_pd) +{ + struct livebox_script_operators *ops; + + if (!block) { + ErrPrint("Invalid argument\n"); + return LB_STATUS_ERROR_INVALID; + } + + ops = is_pd ? &handle->cbs.pd_ops : &handle->cbs.lb_ops; + if (ops->update_color) { + ops->update_color(handle, block->id, block->part, block->data); + } + + return 0; +} + +static inline int update_begin(struct livebox *handle, int is_pd) +{ + struct livebox_script_operators *ops; + + ops = is_pd ? &handle->cbs.pd_ops : &handle->cbs.lb_ops; + if (ops->update_begin) { + ops->update_begin(handle); + } + + return 0; +} + +static inline int update_end(struct livebox *handle, int is_pd) +{ + struct livebox_script_operators *ops; + + ops = is_pd ? &handle->cbs.pd_ops : &handle->cbs.lb_ops; + if (ops->update_end) { + ops->update_end(handle); + } + + return 0; +} + +static inline void delete_block(struct block *block) +{ + free(block->filebuf); + free(block); +} + +static inline void consuming_parsed_block(struct livebox *handle, int is_pd, struct block *block) +{ + typedef int (*update_function_t)(struct livebox *handle, struct block *block, int is_pd); + static update_function_t updators[] = { + update_access, + operate_access, + update_color, + update_drag, + update_image, + update_info, + update_script, + update_signal, + update_text, + NULL + }; + + if (block->type >= 0 || block->type < TYPE_MAX) { + (void)updators[block->type](handle, block, is_pd); + } else { + ErrPrint("Block type[%d] is not valid\n", block->type); + } +} + +static inline char *load_file(const char *filename) +{ + char *filebuf = NULL; + int fd; + off_t filesize; + int ret; + size_t readsize = 0; + + fd = open(filename, O_RDONLY); + if (fd < 0) { + ErrPrint("open: %s\n", strerror(errno)); + return NULL; + } + + filesize = lseek(fd, 0L, SEEK_END); + if (filesize == (off_t)-1) { + ErrPrint("lseek: %s\n", strerror(errno)); + goto errout; + } + + if (lseek(fd, 0L, SEEK_SET) < 0) { + ErrPrint("lseek: %s\n", strerror(errno)); + goto errout; + } + + filebuf = malloc(filesize + 1); + if (!filebuf) { + ErrPrint("malloc: %s\n", strerror(errno)); + goto errout; + } + + while (readsize < filesize) { + ret = read(fd, filebuf + readsize, (size_t)filesize - readsize); + if (ret < 0) { + if (errno == EINTR) { + DbgPrint("Read is interrupted\n"); + continue; + } + + ErrPrint("read: %s\n", strerror(errno)); + free(filebuf); + filebuf = NULL; + break; + } + + readsize += ret; + } + + if (filebuf) { + filebuf[readsize] = '\0'; + } + + /*! + * \note + * Now, we are ready to parse the filebuf. + */ + +errout: + if (close(fd) < 0) { + ErrPrint("close: %s\n", strerror(errno)); + } + + return filebuf; +} + +int parse_desc(struct livebox_common *common, const char *filename, int is_pd) +{ + int type_idx = 0; + int type_len = 0; + int field_idx = 0; + int field_len = 0; + char *filebuf; + char *fileptr; + char *ptr = NULL; + struct block *block = NULL; + struct dlist *block_list = NULL; + struct dlist *l; + struct dlist *n; + struct dlist *handle_iterator; + struct livebox *handler; + enum state { + BEGIN, + FIELD, + DATA, + END, + DONE, + ERROR, + } state; + + filebuf = load_file(filename); + if (!filebuf) { + return LB_STATUS_ERROR_IO; + } + + fileptr = filebuf; + + state = BEGIN; + while (*fileptr && state != ERROR) { + switch (state) { + case BEGIN: + if (*fileptr == '{') { + block = calloc(1, sizeof(*block)); + if (!block) { + ErrPrint("calloc: %s\n", strerror(errno)); + state = ERROR; + continue; + } + state = FIELD; + ptr = NULL; + } + break; + case FIELD: + if (isspace(*fileptr)) { + if (ptr != NULL) { + *fileptr = '\0'; + } + } else if (*fileptr == '=') { + *fileptr = '\0'; + ptr = NULL; + state = DATA; + } else if (ptr == NULL) { + ptr = fileptr; + field_idx = 0; + field_len = 0; + + while (field_list[field_idx]) { + if (field_list[field_idx][field_len] == *fileptr) { + break; + } + field_idx++; + } + + if (!field_list[field_idx]) { + ErrPrint("Invalid field\n"); + state = ERROR; + continue; + } + + field_len++; + } else { + if (field_list[field_idx][field_len] != *fileptr) { + field_idx++; + while (field_list[field_idx]) { + if (!strncmp(field_list[field_idx], fileptr - field_len, field_len)) { + break; + } else { + field_idx++; + } + } + + if (!field_list[field_idx]) { + state = ERROR; + ErrPrint("field is not valid\n"); + continue; + } + } + + field_len++; + } + break; + case DATA: + switch (field_idx) { + case FIELD_TYPE: + if (ptr == NULL) { + if (isspace(*fileptr)) { + break; + } + + if (*fileptr == '\0') { + state = ERROR; + ErrPrint("Type is not valid\n"); + continue; + } + + ptr = fileptr; + type_idx = 0; + type_len = 0; + } + + if (*fileptr && (*fileptr == '\n' || *fileptr == '\r' || *fileptr == '\f')) { + *fileptr = '\0'; + } + + if (type_list[type_idx][type_len] != *fileptr) { + type_idx++; + while (type_list[type_idx]) { + if (!strncmp(type_list[type_idx], fileptr - type_len, type_len)) { + break; + } else { + type_idx++; + } + } + + if (!type_list[type_idx]) { + state = ERROR; + ErrPrint("type is not valid (%s)\n", fileptr - type_len); + continue; + } + } + + if (!*fileptr) { + block->type = type_idx; + state = DONE; + ptr = NULL; + } + + type_len++; + break; + case FIELD_PART: + if (ptr == NULL) { + ptr = fileptr; + } + + if (*fileptr && (*fileptr == '\n' || *fileptr == '\r' || *fileptr == '\f')) { + *fileptr = '\0'; + } + + if (!*fileptr) { + block->part = ptr; + state = DONE; + ptr = NULL; + } + break; + case FIELD_DATA: + if (ptr == NULL) { + ptr = fileptr; + } + + if (*fileptr && (*fileptr == '\n' || *fileptr == '\r' || *fileptr == '\f')) { + *fileptr = '\0'; + } + + if (!*fileptr) { + block->data = ptr; + state = DONE; + ptr = NULL; + } + break; + case FIELD_OPTION: + if (ptr == NULL) { + ptr = fileptr; + } + + if (*fileptr && (*fileptr == '\n' || *fileptr == '\r' || *fileptr == '\f')) { + *fileptr = '\0'; + } + + if (!*fileptr) { + block->option = ptr; + state = DONE; + ptr = NULL; + } + break; + case FIELD_ID: + if (ptr == NULL) { + ptr = fileptr; + } + + if (*fileptr && (*fileptr == '\n' || *fileptr == '\r' || *fileptr == '\f')) { + *fileptr = '\0'; + } + + if (!*fileptr) { + block->id = ptr; + state = DONE; + ptr = NULL; + } + break; + case FIELD_TARGET: + if (ptr == NULL) { + ptr = fileptr; + } + + if (*fileptr && (*fileptr == '\n' || *fileptr == '\r' || *fileptr == '\f')) { + *fileptr = '\0'; + } + + if (!*fileptr) { + block->target = ptr; + state = DONE; + ptr = NULL; + } + break; + case FIELD_FILE: + if (ptr == NULL) { + ptr = fileptr; + } + + if (*fileptr && (*fileptr == '\n' || *fileptr == '\r' || *fileptr == '\f')) { + *fileptr = '\0'; + } + + if (!*fileptr) { + block->target = ptr; + state = DONE; + ptr = NULL; + } + default: + break; + } + + break; + case DONE: + if (isspace(*fileptr)) { + } else if (*fileptr == '}') { + state = BEGIN; + block->filename = filename; + block_list = dlist_append(block_list, block); + block = NULL; + } else { + state = FIELD; + continue; + } + break; + case END: + default: + break; + } + + fileptr++; + } + + if (state != BEGIN) { + struct dlist *l; + struct dlist *n; + ErrPrint("State %d\n", state); + + free(filebuf); + free(block); + + dlist_foreach_safe(block_list, l, n, block) { + free(block); + block_list = dlist_remove(block_list, l); + } + + return LB_STATUS_ERROR_FAULT; + } + + + block = dlist_data(dlist_prev(block_list)); + if (block) { + block->filebuf = filebuf; + } else { + ErrPrint("Last block is not exists (There is no parsed block)\n"); + free(filebuf); + } + + ErrPrint("Begin: Set content for object\n"); + dlist_foreach(common->livebox_list, l, handler) { + update_begin(handler, is_pd); + } + + dlist_foreach_safe(block_list, l, n, block) { + dlist_foreach(common->livebox_list, handle_iterator, handler) { + consuming_parsed_block(handler, is_pd, block); + } + + block_list = dlist_remove(block_list, l); + delete_block(block); + } + + dlist_foreach(common->livebox_list, l, handler) { + update_end(handler, is_pd); + } + ErrPrint("End: Set content for object\n"); + + return LB_STATUS_SUCCESS; +} + +/* End of a file */ diff --git a/src/dlist.c b/src/dlist.c new file mode 100644 index 0000000..3ae571b --- /dev/null +++ b/src/dlist.c @@ -0,0 +1,189 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +#include "dlist.h" + +/*! + * \brief + * This dlist is called Modified Doubly Linked List. + * + * Noramlly, The dobule linked list contains address of previous and next element. + * This dlist also contains them, but the tail element only contains prev address. + * + * The head element's prev pointer indicates the last element. + * But the last element's next pointer indicates NIL. + * + * So we can find the last element while crawling this DList + * But we have to remember the address of the head element. + */ + +struct dlist { + struct dlist *next; + struct dlist *prev; + void *data; +}; + +struct dlist *dlist_append(struct dlist *list, void *data) +{ + struct dlist *item; + + item = malloc(sizeof(*item)); + if (!item) { + return NULL; + } + + item->next = NULL; + item->data = data; + + if (!list) { + item->prev = item; + + list = item; + } else { + item->prev = list->prev; + item->prev->next = item; + list->prev = item; + } + + assert(!list->prev->next && "item NEXT"); + + return list; +} + +struct dlist *dlist_prepend(struct dlist *list, void *data) +{ + struct dlist *item; + + item = malloc(sizeof(*item)); + if (!item) { + return NULL; + } + + item->data = data; + + if (!list) { + item->prev = item; + item->next = NULL; + } else { + if (list->prev->next) { + list->prev->next = item; + } + + item->prev = list->prev; + item->next = list; + + list->prev = item; + + } + + return item; +} + +struct dlist *dlist_remove(struct dlist *list, struct dlist *l) +{ + if (!list || !l) { + return NULL; + } + + if (l == list) { + list = l->next; + } else { + l->prev->next = l->next; + } + + if (l->next) { + l->next->prev = l->prev; + } + /*! + * \note + * If the removed entry 'l' has no next element, it is the last element. + * In this case, check the existence of the list first, + * and if the list is not empty, update the 'prev' of the list (which is a head element of the list) + * + * If we didn't care about this, the head element(list) can indicates the invalid element. + */ + else if (list) { + list->prev = l->prev; + } + + free(l); + return list; +} + +struct dlist *dlist_find_data(struct dlist *list, void *data) +{ + struct dlist *l; + void *_data; + + dlist_foreach(list, l, _data) { + if (data == _data) { + return l; + } + } + + return NULL; +} + +void *dlist_data(struct dlist *l) +{ + return l ? l->data : NULL; +} + +struct dlist *dlist_next(struct dlist *l) +{ + return l ? l->next : NULL; +} + +struct dlist *dlist_prev(struct dlist *l) +{ + return l ? l->prev : NULL; +} + +int dlist_count(struct dlist *l) +{ + register int i; + struct dlist *n; + void *data; + + i = 0; + dlist_foreach(l, n, data) { + i++; + } + + return i; +} + +struct dlist *dlist_nth(struct dlist *l, int nth) +{ + register int i; + struct dlist *n; + + i = 0; + for (n = l; n; n = n->next) { + if (i == nth) { + return n; + } + i++; + } + + return NULL; +} + +/* End of a file */ diff --git a/src/fb.c b/src/fb.c new file mode 100644 index 0000000..0e90ad1 --- /dev/null +++ b/src/fb.c @@ -0,0 +1,622 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <errno.h> +#include <sys/mman.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/types.h> +#include <unistd.h> +#include <stddef.h> +#include <sys/shm.h> +#include <sys/ipc.h> + +#include <X11/Xlib.h> +#include <X11/extensions/XShm.h> +#include <X11/Xutil.h> + +#include <dlog.h> +#include <livebox-errno.h> /* For error code */ + +#include "debug.h" +#include "util.h" +#include "fb.h" + +int errno; + +struct fb_info { + char *id; + int w; + int h; + int bufsz; + void *buffer; + + int pixels; + int handle; +}; + +struct buffer { /*!< Must has to be sync with slave & provider */ + enum { + CREATED = 0x00beef00, + DESTROYED = 0x00dead00 + } state; + enum buffer_type type; + int refcnt; + void *info; + char data[]; +}; + +static struct { + Display *disp; + int screen; + Visual *visual; + int disp_is_opened; +} s_info = { + .disp = NULL, + .disp_is_opened = 0, + .screen = -1, + .visual = NULL, +}; + +int fb_init(void *disp) +{ + s_info.disp = disp; + if (s_info.disp) { + Screen *screen; + + screen = DefaultScreenOfDisplay(s_info.disp); + + s_info.screen = DefaultScreen(s_info.disp); + s_info.visual = DefaultVisualOfScreen(screen); + } + + return 0; +} + +int fb_fini(void) +{ + if (s_info.disp_is_opened && s_info.disp) { + XCloseDisplay(s_info.disp); + } + + s_info.disp = NULL; + s_info.disp_is_opened = 0; + s_info.visual = NULL; + s_info.screen = -1; + return 0; +} + +static inline void update_fb_size(struct fb_info *info) +{ + info->bufsz = info->w * info->h * info->pixels; +} + +static inline int sync_for_file(struct fb_info *info) +{ + int fd; + struct buffer *buffer; + + buffer = info->buffer; + + if (!buffer) { /* Ignore this sync request */ + return LB_STATUS_SUCCESS; + } + + if (buffer->state != CREATED) { + ErrPrint("Invalid state of a FB\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (buffer->type != BUFFER_TYPE_FILE) { + ErrPrint("Invalid buffer\n"); + return LB_STATUS_SUCCESS; + } + + fd = open(util_uri_to_path(info->id), O_RDONLY); + if (fd < 0) { + ErrPrint("Failed to open a file (%s) because of (%s)\n", + util_uri_to_path(info->id), strerror(errno)); + + /*! + * \note + * But return ZERO, even if we couldn't get a buffer file, + * the viewer can draw empty screen. + * + * and then update it after it gots update events + */ + return LB_STATUS_SUCCESS; + } + + if (read(fd, buffer->data, info->bufsz) != info->bufsz) { + ErrPrint("read: %s\n", strerror(errno)); + if (close(fd) < 0) { + ErrPrint("close: %s\n", strerror(errno)); + } + + /*! + * \note + * But return ZERO, even if we couldn't get a buffer file, + * the viewer can draw empty screen. + * + * and then update it after it gots update events + */ + return LB_STATUS_SUCCESS; + } + + if (close(fd) < 0) { + ErrPrint("close: %s\n", strerror(errno)); + } + return LB_STATUS_SUCCESS; +} + +static inline __attribute__((always_inline)) int sync_for_pixmap(struct fb_info *info) +{ + struct buffer *buffer; + XShmSegmentInfo si; + XImage *xim; + + buffer = info->buffer; + if (!buffer) { /*!< Ignore this sync request */ + return LB_STATUS_SUCCESS; + } + + if (buffer->state != CREATED) { + ErrPrint("Invalid state of a FB\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (buffer->type != BUFFER_TYPE_PIXMAP) { + ErrPrint("Invalid buffer\n"); + return LB_STATUS_SUCCESS; + } + + if (!s_info.disp) { + s_info.disp = XOpenDisplay(NULL); + if (s_info.disp) { + Screen *screen; + + s_info.disp_is_opened = 1; + + screen = DefaultScreenOfDisplay(s_info.disp); + + s_info.screen = DefaultScreen(s_info.disp); + s_info.visual = DefaultVisualOfScreen(screen); + } else { + ErrPrint("Failed to open a display\n"); + return LB_STATUS_ERROR_FAULT; + } + } + + if (info->handle == 0) { + ErrPrint("Pixmap ID is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (info->bufsz == 0) { + /*! + * If the client does not acquire the buffer, + * This function will do nothing. + * It will work only if the buffer is acquired. + * To sync its contents. + */ + DbgPrint("Nothing can be sync\n"); + return LB_STATUS_SUCCESS; + } + + si.shmid = shmget(IPC_PRIVATE, info->bufsz, IPC_CREAT | 0666); + if (si.shmid < 0) { + ErrPrint("shmget: %s\n", strerror(errno)); + return LB_STATUS_ERROR_FAULT; + } + + si.readOnly = False; + si.shmaddr = shmat(si.shmid, NULL, 0); + if (si.shmaddr == (void *)-1) { + if (shmctl(si.shmid, IPC_RMID, 0) < 0) { + ErrPrint("shmctl: %s\n", strerror(errno)); + } + + return LB_STATUS_ERROR_FAULT; + } + + /*! + * \NOTE + * Use the 24 bits Pixmap for Video player + */ + xim = XShmCreateImage(s_info.disp, s_info.visual, + (info->pixels << 3), ZPixmap, NULL, + &si, + info->w, info->h); + if (xim == NULL) { + if (shmdt(si.shmaddr) < 0) { + ErrPrint("shmdt: %s\n", strerror(errno)); + } + + if (shmctl(si.shmid, IPC_RMID, 0) < 0) { + ErrPrint("shmctl: %s\n", strerror(errno)); + } + + return LB_STATUS_ERROR_FAULT; + } + + xim->data = si.shmaddr; + XShmAttach(s_info.disp, &si); + + XShmGetImage(s_info.disp, info->handle, xim, 0, 0, 0xFFFFFFFF); + XSync(s_info.disp, False); + + memcpy(buffer->data, xim->data, info->bufsz); + + XShmDetach(s_info.disp, &si); + XDestroyImage(xim); + + if (shmdt(si.shmaddr) < 0) { + ErrPrint("shmdt: %s\n", strerror(errno)); + } + + if (shmctl(si.shmid, IPC_RMID, 0) < 0) { + ErrPrint("shmctl: %s\n", strerror(errno)); + } + + return LB_STATUS_SUCCESS; +} + +int fb_sync(struct fb_info *info) +{ + if (!info) { + ErrPrint("FB Handle is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!info->id || info->id[0] == '\0') { + DbgPrint("Ingore sync\n"); + return LB_STATUS_SUCCESS; + } + + if (!strncasecmp(info->id, SCHEMA_FILE, strlen(SCHEMA_FILE))) { + return sync_for_file(info); + } else if (!strncasecmp(info->id, SCHEMA_PIXMAP, strlen(SCHEMA_PIXMAP))) { + return sync_for_pixmap(info); + } else if (!strncasecmp(info->id, SCHEMA_SHM, strlen(SCHEMA_SHM))) { + /* No need to do sync */ + return LB_STATUS_SUCCESS; + } + + return LB_STATUS_ERROR_INVALID; +} + +struct fb_info *fb_create(const char *id, int w, int h) +{ + struct fb_info *info; + + if (!id || id[0] == '\0') { + ErrPrint("Invalid ID\n"); + return NULL; + } + + info = calloc(1, sizeof(*info)); + if (!info) { + ErrPrint("Heap: %s\n", strerror(errno)); + return NULL; + } + + info->id = strdup(id); + if (!info->id) { + ErrPrint("Heap: %s\n", strerror(errno)); + free(info); + return NULL; + } + + info->pixels = sizeof(int); /* Use the default pixels(depth) */ + + if (sscanf(info->id, SCHEMA_SHM "%d", &info->handle) == 1) { + DbgPrint("SHMID: %d is gotten\n", info->handle); + } else if (sscanf(info->id, SCHEMA_PIXMAP "%d:%d", &info->handle, &info->pixels) == 2) { + DbgPrint("PIXMAP-SHMID: %d is gotten (%d)\n", info->handle, info->pixels); + } else { + info->handle = LB_STATUS_ERROR_INVALID; + } + + info->bufsz = 0; + info->buffer = NULL; + info->w = w; + info->h = h; + + return info; +} + +int fb_destroy(struct fb_info *info) +{ + if (!info) { + ErrPrint("Handle is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (info->buffer) { + struct buffer *buffer; + buffer = info->buffer; + + buffer->info = NULL; + } + + free(info->id); + free(info); + return LB_STATUS_SUCCESS; +} + +int fb_is_created(struct fb_info *info) +{ + if (!info) { + ErrPrint("Handle is not valid\n"); + return 0; + } + + if (!strncasecmp(info->id, SCHEMA_PIXMAP, strlen(SCHEMA_PIXMAP)) && info->handle != 0) { + return 1; + } else if (!strncasecmp(info->id, SCHEMA_SHM, strlen(SCHEMA_SHM)) && info->handle > 0) { + return 1; + } else { + const char *path; + path = util_uri_to_path(info->id); + if (path && access(path, F_OK | R_OK) == 0) { + return 1; + } else { + ErrPrint("access: %s (%s)\n", strerror(errno), path); + } + } + + return 0; +} + +void *fb_acquire_buffer(struct fb_info *info) +{ + struct buffer *buffer; + + if (!info) { + ErrPrint("info == NIL\n"); + return NULL; + } + + if (!info->buffer) { + if (!strncasecmp(info->id, SCHEMA_PIXMAP, strlen(SCHEMA_PIXMAP))) { + update_fb_size(info); + + buffer = calloc(1, sizeof(*buffer) + info->bufsz); + if (!buffer) { + ErrPrint("Heap: %s\n", strerror(errno)); + info->bufsz = 0; + return NULL; + } + + buffer->type = BUFFER_TYPE_PIXMAP; + buffer->refcnt = 0; + buffer->state = CREATED; + buffer->info = info; + info->buffer = buffer; + + /*! + * \note + * Just update from here. + */ + sync_for_pixmap(info); + } else if (!strncasecmp(info->id, SCHEMA_FILE, strlen(SCHEMA_FILE))) { + update_fb_size(info); + + buffer = calloc(1, sizeof(*buffer) + info->bufsz); + if (!buffer) { + ErrPrint("Heap: %s\n", strerror(errno)); + info->bufsz = 0; + return NULL; + } + + buffer->type = BUFFER_TYPE_FILE; + buffer->refcnt = 0; + buffer->state = CREATED; + buffer->info = info; + info->buffer = buffer; + + sync_for_file(info); + } else if (!strncasecmp(info->id, SCHEMA_SHM, strlen(SCHEMA_SHM))) { + buffer = shmat(info->handle, NULL, 0); + if (buffer == (void *)-1) { + ErrPrint("shmat: %s (%d)\n", strerror(errno), info->handle); + return NULL; + } + + return buffer->data; + } else { + ErrPrint("Buffer is not created (%s)\n", info->id); + return NULL; + } + } + + buffer = info->buffer; + + switch (buffer->type) { + case BUFFER_TYPE_PIXMAP: + buffer->refcnt++; + break; + case BUFFER_TYPE_FILE: + buffer->refcnt++; + break; + default: + DbgPrint("Unknwon FP: %d\n", buffer->type); + break; + } + + return buffer->data; +} + +int fb_release_buffer(void *data) +{ + struct buffer *buffer; + + if (!data) { + ErrPrint("buffer data == NIL\n"); + return LB_STATUS_ERROR_INVALID; + } + + buffer = container_of(data, struct buffer, data); + + if (buffer->state != CREATED) { + ErrPrint("Invalid handle\n"); + return LB_STATUS_ERROR_INVALID; + } + + switch (buffer->type) { + case BUFFER_TYPE_SHM: + if (shmdt(buffer) < 0) { + ErrPrint("shmdt: %s\n", strerror(errno)); + } + break; + case BUFFER_TYPE_PIXMAP: + buffer->refcnt--; + if (buffer->refcnt == 0) { + struct fb_info *info; + info = buffer->info; + + buffer->state = DESTROYED; + free(buffer); + + if (info && info->buffer == buffer) { + info->buffer = NULL; + } + } + break; + case BUFFER_TYPE_FILE: + buffer->refcnt--; + if (buffer->refcnt == 0) { + struct fb_info *info; + info = buffer->info; + + buffer->state = DESTROYED; + free(buffer); + + if (info && info->buffer == buffer) { + info->buffer = NULL; + } + } + break; + default: + ErrPrint("Unknwon buffer type\n"); + break; + } + + return LB_STATUS_SUCCESS; +} + +int fb_refcnt(void *data) +{ + struct buffer *buffer; + struct shmid_ds buf; + int ret; + + if (!data) { + return LB_STATUS_ERROR_INVALID; + } + + buffer = container_of(data, struct buffer, data); + + if (buffer->state != CREATED) { + ErrPrint("Invalid handle\n"); + return LB_STATUS_ERROR_INVALID; + } + + switch (buffer->type) { + case BUFFER_TYPE_SHM: + if (shmctl(buffer->refcnt, IPC_STAT, &buf) < 0) { + ErrPrint("Error: %s\n", strerror(errno)); + return LB_STATUS_ERROR_FAULT; + } + + ret = buf.shm_nattch; + break; + case BUFFER_TYPE_PIXMAP: + ret = buffer->refcnt; + break; + case BUFFER_TYPE_FILE: + ret = buffer->refcnt; + break; + default: + ret = LB_STATUS_ERROR_INVALID; + break; + } + + return ret; +} + +const char *fb_id(struct fb_info *info) +{ + return info ? info->id : NULL; +} + +int fb_get_size(struct fb_info *info, int *w, int *h) +{ + if (!info) { + ErrPrint("Handle is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + *w = info->w; + *h = info->h; + return LB_STATUS_SUCCESS; +} + +int fb_size(struct fb_info *info) +{ + if (!info) { + return 0; + } + + update_fb_size(info); + + return info->bufsz; +} + +int fb_type(struct fb_info *info) +{ + struct buffer *buffer; + + if (!info) { + return BUFFER_TYPE_ERROR; + } + + buffer = info->buffer; + if (!buffer) { + int type = BUFFER_TYPE_ERROR; + /*! + * \note + * Try to get this from SCHEMA + */ + if (info->id) { + if (!strncasecmp(info->id, SCHEMA_FILE, strlen(SCHEMA_FILE))) { + type = BUFFER_TYPE_FILE; + } else if (!strncasecmp(info->id, SCHEMA_PIXMAP, strlen(SCHEMA_PIXMAP))) { + type = BUFFER_TYPE_PIXMAP; + } else if (!strncasecmp(info->id, SCHEMA_SHM, strlen(SCHEMA_SHM))) { + type = BUFFER_TYPE_SHM; + } + } + + return type; + } + + return buffer->type; +} +/* End of a file */ diff --git a/src/fb_wayland.c b/src/fb_wayland.c new file mode 100644 index 0000000..a38eb5f --- /dev/null +++ b/src/fb_wayland.c @@ -0,0 +1,447 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <errno.h> +#include <sys/mman.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/types.h> +#include <unistd.h> +#include <stddef.h> +#include <sys/shm.h> +#include <sys/ipc.h> + +#include <dlog.h> +#include <livebox-errno.h> /* For error code */ + +#include "debug.h" +#include "util.h" +#include "fb.h" + +int errno; + +struct fb_info { + char *id; + int w; + int h; + int bufsz; + void *buffer; + + int pixels; + int handle; +}; + +struct buffer { /*!< Must has to be sync with slave & provider */ + enum { + CREATED = 0x00beef00, + DESTROYED = 0x00dead00 + } state; + enum buffer_type type; + int refcnt; + void *info; + char data[]; +}; + +static struct { +} s_info = { +}; + +int fb_init(void *disp) +{ + return 0; +} + +int fb_fini(void) +{ + return 0; +} + +static inline void update_fb_size(struct fb_info *info) +{ + info->bufsz = info->w * info->h * info->pixels; +} + +static inline int sync_for_file(struct fb_info *info) +{ + int fd; + struct buffer *buffer; + + buffer = info->buffer; + + if (!buffer) { /* Ignore this sync request */ + return LB_STATUS_SUCCESS; + } + + if (buffer->state != CREATED) { + ErrPrint("Invalid state of a FB\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (buffer->type != BUFFER_TYPE_FILE) { + ErrPrint("Invalid buffer\n"); + return LB_STATUS_SUCCESS; + } + + fd = open(util_uri_to_path(info->id), O_RDONLY); + if (fd < 0) { + ErrPrint("Failed to open a file (%s) because of (%s)\n", + util_uri_to_path(info->id), strerror(errno)); + + /*! + * \note + * But return ZERO, even if we couldn't get a buffer file, + * the viewer can draw empty screen. + * + * and then update it after it gots update events + */ + return LB_STATUS_SUCCESS; + } + + if (read(fd, buffer->data, info->bufsz) != info->bufsz) { + ErrPrint("read: %s\n", strerror(errno)); + if (close(fd) < 0) { + ErrPrint("close: %s\n", strerror(errno)); + } + + /*! + * \note + * But return ZERO, even if we couldn't get a buffer file, + * the viewer can draw empty screen. + * + * and then update it after it gots update events + */ + return LB_STATUS_SUCCESS; + } + + if (close(fd) < 0) { + ErrPrint("close: %s\n", strerror(errno)); + } + return LB_STATUS_SUCCESS; +} + +int fb_sync(struct fb_info *info) +{ + if (!info) { + ErrPrint("FB Handle is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!info->id || info->id[0] == '\0') { + DbgPrint("Ingore sync\n"); + return LB_STATUS_SUCCESS; + } + + if (!strncasecmp(info->id, SCHEMA_FILE, strlen(SCHEMA_FILE))) { + return sync_for_file(info); + } else if (!strncasecmp(info->id, SCHEMA_PIXMAP, strlen(SCHEMA_PIXMAP))) { + } else if (!strncasecmp(info->id, SCHEMA_SHM, strlen(SCHEMA_SHM))) { + /* No need to do sync */ + return LB_STATUS_SUCCESS; + } + + return LB_STATUS_ERROR_INVALID; +} + +struct fb_info *fb_create(const char *id, int w, int h) +{ + struct fb_info *info; + + if (!id || id[0] == '\0') { + ErrPrint("Invalid ID\n"); + return NULL; + } + + info = calloc(1, sizeof(*info)); + if (!info) { + ErrPrint("Heap: %s\n", strerror(errno)); + return NULL; + } + + info->id = strdup(id); + if (!info->id) { + ErrPrint("Heap: %s\n", strerror(errno)); + free(info); + return NULL; + } + + info->pixels = sizeof(int); /* Use the default pixels(depth) */ + + if (sscanf(info->id, SCHEMA_SHM "%d", &info->handle) == 1) { + DbgPrint("SHMID: %d is gotten\n", info->handle); + } else if (sscanf(info->id, SCHEMA_PIXMAP "%d:%d", &info->handle, &info->pixels) == 2) { + DbgPrint("PIXMAP-SHMID: %d is gotten (%d)\n", info->handle, info->pixels); + ErrPrint("Unsupported\n"); + free(info); + return NULL; + } else { + info->handle = LB_STATUS_ERROR_INVALID; + } + + info->bufsz = 0; + info->buffer = NULL; + info->w = w; + info->h = h; + + return info; +} + +int fb_destroy(struct fb_info *info) +{ + if (!info) { + ErrPrint("Handle is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (info->buffer) { + struct buffer *buffer; + buffer = info->buffer; + + buffer->info = NULL; + } + + free(info->id); + free(info); + return LB_STATUS_SUCCESS; +} + +int fb_is_created(struct fb_info *info) +{ + if (!info) { + ErrPrint("Handle is not valid\n"); + return 0; + } + + if (!strncasecmp(info->id, SCHEMA_PIXMAP, strlen(SCHEMA_PIXMAP)) && info->handle != 0) { + return 1; + } else if (!strncasecmp(info->id, SCHEMA_SHM, strlen(SCHEMA_SHM)) && info->handle > 0) { + return 1; + } else { + const char *path; + path = util_uri_to_path(info->id); + if (path && access(path, F_OK | R_OK) == 0) { + return 1; + } else { + ErrPrint("access: %s (%s)\n", strerror(errno), path); + } + } + + return 0; +} + +void *fb_acquire_buffer(struct fb_info *info) +{ + struct buffer *buffer; + + if (!info) { + ErrPrint("info == NIL\n"); + return NULL; + } + + if (!info->buffer) { + if (!strncasecmp(info->id, SCHEMA_PIXMAP, strlen(SCHEMA_PIXMAP))) { + ErrPrint("Unsupported Type\n"); + return NULL; + } else if (!strncasecmp(info->id, SCHEMA_FILE, strlen(SCHEMA_FILE))) { + update_fb_size(info); + + buffer = calloc(1, sizeof(*buffer) + info->bufsz); + if (!buffer) { + ErrPrint("Heap: %s\n", strerror(errno)); + info->bufsz = 0; + return NULL; + } + + buffer->type = BUFFER_TYPE_FILE; + buffer->refcnt = 0; + buffer->state = CREATED; + buffer->info = info; + info->buffer = buffer; + + sync_for_file(info); + } else if (!strncasecmp(info->id, SCHEMA_SHM, strlen(SCHEMA_SHM))) { + buffer = shmat(info->handle, NULL, 0); + if (buffer == (void *)-1) { + ErrPrint("shmat: %s (%d)\n", strerror(errno), info->handle); + return NULL; + } + + return buffer->data; + } else { + ErrPrint("Buffer is not created (%s)\n", info->id); + return NULL; + } + } + + buffer = info->buffer; + + switch (buffer->type) { + case BUFFER_TYPE_FILE: + buffer->refcnt++; + break; + case BUFFER_TYPE_PIXMAP: + default: + DbgPrint("Unknwon FP: %d\n", buffer->type); + break; + } + + return buffer->data; +} + +int fb_release_buffer(void *data) +{ + struct buffer *buffer; + + if (!data) { + ErrPrint("buffer data == NIL\n"); + return LB_STATUS_ERROR_INVALID; + } + + buffer = container_of(data, struct buffer, data); + + if (buffer->state != CREATED) { + ErrPrint("Invalid handle\n"); + return LB_STATUS_ERROR_INVALID; + } + + switch (buffer->type) { + case BUFFER_TYPE_SHM: + if (shmdt(buffer) < 0) { + ErrPrint("shmdt: %s\n", strerror(errno)); + } + break; + case BUFFER_TYPE_FILE: + buffer->refcnt--; + if (buffer->refcnt == 0) { + struct fb_info *info; + info = buffer->info; + + buffer->state = DESTROYED; + free(buffer); + + if (info && info->buffer == buffer) { + info->buffer = NULL; + } + } + break; + case BUFFER_TYPE_PIXMAP: + default: + ErrPrint("Unknwon buffer type\n"); + break; + } + + return LB_STATUS_SUCCESS; +} + +int fb_refcnt(void *data) +{ + struct buffer *buffer; + struct shmid_ds buf; + int ret; + + if (!data) { + return LB_STATUS_ERROR_INVALID; + } + + buffer = container_of(data, struct buffer, data); + + if (buffer->state != CREATED) { + ErrPrint("Invalid handle\n"); + return LB_STATUS_ERROR_INVALID; + } + + switch (buffer->type) { + case BUFFER_TYPE_SHM: + if (shmctl(buffer->refcnt, IPC_STAT, &buf) < 0) { + ErrPrint("Error: %s\n", strerror(errno)); + return LB_STATUS_ERROR_FAULT; + } + + ret = buf.shm_nattch; + break; + case BUFFER_TYPE_FILE: + ret = buffer->refcnt; + break; + case BUFFER_TYPE_PIXMAP: + default: + ret = LB_STATUS_ERROR_INVALID; + break; + } + + return ret; +} + +const char *fb_id(struct fb_info *info) +{ + return info ? info->id : NULL; +} + +int fb_get_size(struct fb_info *info, int *w, int *h) +{ + if (!info) { + ErrPrint("Handle is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + *w = info->w; + *h = info->h; + return LB_STATUS_SUCCESS; +} + +int fb_size(struct fb_info *info) +{ + if (!info) { + return 0; + } + + update_fb_size(info); + + return info->bufsz; +} + +int fb_type(struct fb_info *info) +{ + struct buffer *buffer; + + if (!info) { + return BUFFER_TYPE_ERROR; + } + + buffer = info->buffer; + if (!buffer) { + int type = BUFFER_TYPE_ERROR; + /*! + * \note + * Try to get this from SCHEMA + */ + if (info->id) { + if (!strncasecmp(info->id, SCHEMA_FILE, strlen(SCHEMA_FILE))) { + type = BUFFER_TYPE_FILE; + } else if (!strncasecmp(info->id, SCHEMA_PIXMAP, strlen(SCHEMA_PIXMAP))) { + /* Unsupported type */ + } else if (!strncasecmp(info->id, SCHEMA_SHM, strlen(SCHEMA_SHM))) { + type = BUFFER_TYPE_SHM; + } + } + + return type; + } + + return buffer->type; +} +/* End of a file */ diff --git a/src/file_service.c b/src/file_service.c new file mode 100644 index 0000000..e890d0b --- /dev/null +++ b/src/file_service.c @@ -0,0 +1,715 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _GNU_SOURCE + +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <pthread.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include <glib.h> + +#include <dlog.h> +#include <secure_socket.h> +#include <livebox-errno.h> + +#include "client.h" +#include "debug.h" +#include "dlist.h" + +#define FILE_SERVICE_PORT 8209 + +#define CRITICAL_SECTION_BEGIN(handle) \ +do { \ + int ret; \ + ret = pthread_mutex_lock(handle); \ + if (ret != 0) { \ + ErrPrint("Failed to lock: %s\n", strerror(ret)); \ + } \ +} while (0) + +#define CRITICAL_SECTION_END(handle) \ +do { \ + int ret; \ + ret = pthread_mutex_unlock(handle); \ + if (ret != 0) { \ + ErrPrint("Failed to unlock: %s\n", strerror(ret)); \ + } \ +} while (0) + +#define CANCEL_SECTION_BEGIN() do { \ + int ret; \ + ret = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); \ + if (ret != 0) { \ + ErrPrint("Unable to set cancelate state: %s\n", strerror(ret)); \ + } \ +} while (0) + +#define CANCEL_SECTION_END() do { \ + int ret; \ + ret = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); \ + if (ret != 0) { \ + ErrPrint("Unable to set cancelate state: %s\n", strerror(ret)); \ + } \ +} while (0) + +#define CLOSE_PIPE(p) do { \ + int status; \ + status = close(p[PIPE_READ]); \ + if (status < 0) { \ + ErrPrint("close: %s\n", strerror(errno)); \ + } \ + status = close(p[PIPE_WRITE]); \ + if (status < 0) { \ + ErrPrint("close: %s\n", strerror(errno)); \ + } \ +} while (0) + +#define PIPE_READ 0 +#define PIPE_WRITE 1 +#define PIPE_MAX 2 + +#define EVT_END_CH 'c' +#define EVT_CH 'e' + +static struct { + pthread_t file_svc_thid; + pthread_mutex_t file_svc_lock; + int ctrl_pipe[PIPE_MAX]; + int evt_pipe[PIPE_MAX]; + struct dlist *request_list; + int file_service_fd; +} s_info = { + .ctrl_pipe = { -1, -1 }, + .evt_pipe = { -1, -1 }, + .request_list = NULL, + .file_service_fd = -1, +}; + +struct request_item { + char *filename; + char *save_to; + void (*result_cb)(const char *filename, const char *save_to, int ret, void *data); + void *data; + int ret; +}; + +/*! + * File transfer header. + * This must should be shared with client. + */ +struct burst_head { + off_t size; + int flen; + char fname[]; +}; + +struct burst_data { + int size; + char data[]; +}; + +static inline int put_event_ch(int fd, char ch) +{ + int ret; + + ret = write(fd, &ch, sizeof(ch)); + if (ret != sizeof(ch)) { + ErrPrint("write: %s\n", strerror(errno)); + return ret; + } + + return 0; +} + +static inline int get_event_ch(int fd) +{ + int ret; + char ch; + + ret = read(fd, &ch, sizeof(ch)); + if (ret != sizeof(ch)) { + ErrPrint("read: %s\n", strerror(errno)); + return ret; + } + + ret = (int)((unsigned int)ch); + return ret; +} + +static inline int file_service_close(int fd) +{ + return secure_socket_destroy_handle(fd); +} + +static inline int file_service_open(void) +{ + char *addr; + int port; + char *file_addr; + int len; + int fd; + + addr = malloc(strlen(client_addr()) + 1); + if (!addr) { + ErrPrint("Heap: %s\n", strerror(errno)); + return -ENOMEM; + } + + if (sscanf(client_addr(), COM_CORE_REMOTE_SCHEME"%[^:]:%d", addr, &port) != 2) { + ErrPrint("Invalid URL\n"); + free(addr); + return -EINVAL; + } + + len = strlen(COM_CORE_REMOTE_SCHEME); + len+= strlen(addr); + len+= 6; /* Port length? */ + + file_addr = malloc(len); + if (!file_addr) { + ErrPrint("Heap: %s\n", strerror(errno)); + free(addr); + return -ENOMEM; + } + + snprintf(file_addr, len, COM_CORE_REMOTE_SCHEME"%s:%d", addr, FILE_SERVICE_PORT); + DbgPrint("File service: %s\n", file_addr); + fd = secure_socket_create_client(file_addr); + free(file_addr); + free(addr); + + return fd; +} + +/*! + * Service Thread + */ +static void write_item_to_pipe(struct request_item *item, int ret) +{ + item->ret = LB_STATUS_ERROR_FAULT; + if (write(s_info.evt_pipe[PIPE_WRITE], &item, sizeof(item)) != sizeof(item)) { + ErrPrint("write: %s\n", strerror(errno)); + free(item->filename); + free(item->save_to); + free(item); + item = NULL; + } +} + +/*! + * Service Thread + */ +static void *file_service_main(void *data) +{ + int ret = 0; + int select_fd; + struct timeval tv; + fd_set set; + int offset; + enum { + RECV_INIT, + RECV_HEADER, + RECV_DATA, + } recv_state; + struct burst_head *head; + struct burst_data *body; + int recvsz; + struct request_item *item; + int file_offset; + int file_fd; + + head = NULL; + item = NULL; + recv_state = RECV_INIT; + select_fd = (s_info.file_service_fd > s_info.ctrl_pipe[PIPE_READ] ? s_info.file_service_fd : s_info.ctrl_pipe[PIPE_READ]) + 1; + while (ret == 0) { + FD_ZERO(&set); + FD_SET(s_info.file_service_fd, &set); + FD_SET(s_info.ctrl_pipe[PIPE_READ], &set); + + tv.tv_sec = 3; + tv.tv_usec = 0; + ret = select(select_fd , &set, NULL, NULL, &tv); + if (ret < 0) { + ret = -errno; + if (errno == EINTR) { + ErrPrint("INTERRUPTED\n"); + ret = 0; + continue; + } + ErrPrint("Error: %s\n", strerror(errno)); + break; + } else if (ret == 0) { + ErrPrint("Timeout\n"); + ret = -ETIMEDOUT; + break; + } + + if (item && FD_ISSET(s_info.file_service_fd, &set)) { + switch (recv_state) { + case RECV_INIT: + if (head == NULL) { + recvsz = sizeof(*head); + + head = malloc(recvsz); + if (!head) { + ErrPrint("Heap: %s\n", strerror(errno)); + ret = LB_STATUS_ERROR_MEMORY; + write_item_to_pipe(item, ret); + item = NULL; + break; + } + + offset = 0; + recv_state = RECV_HEADER; + } + case RECV_HEADER: + if (offset < recvsz) { + ret = secure_socket_recv(s_info.file_service_fd, (char *)head + offset, recvsz - offset, NULL); + if (ret > 0) { + offset += ret; + } else { + free(head); + head = NULL; + recv_state = RECV_INIT; + ret = LB_STATUS_ERROR_FAULT; + write_item_to_pipe(item, ret); + item = NULL; + break; + } + } + + if (offset == sizeof(*head)) { + void *tmp; + + recvsz += head->flen; + + tmp = realloc(head, recvsz); + if (!tmp) { + ErrPrint("Heap: %s\n", strerror(errno)); + + free(head); + head = NULL; + recv_state = RECV_INIT; + + ret = LB_STATUS_ERROR_MEMORY; + write_item_to_pipe(item, ret); + item = NULL; + break; + } + + head = tmp; + } else if (offset == recvsz) { + DbgPrint("Filesize: %d, name[%s]\n", head->size, head->fname); + if (strcmp(item->filename, head->fname)) { + ErrPrint("Invalid data sequence (%s <> %s)\n", item->filename, head->fname); + + free(head); + head = NULL; + recv_state = RECV_INIT; + ret = LB_STATUS_ERROR_FAULT; + write_item_to_pipe(item, ret); + item = NULL; + break; + } + + file_fd = open(item->save_to, O_WRONLY|O_CREAT, 0644); + if (file_fd < 0) { + ErrPrint("open: %s\n", strerror(errno)); + free(head); + head = NULL; + recv_state = RECV_INIT; + + ret = LB_STATUS_ERROR_IO; + write_item_to_pipe(item, ret); + item = NULL; + break; + } + + recv_state = RECV_DATA; + body = NULL; + + } else { + ErrPrint("Invalid state\n"); + free(head); + head = NULL; + recv_state = RECV_INIT; + ret = LB_STATUS_ERROR_INVALID; + write_item_to_pipe(item, ret); + item = NULL; + } + break; + case RECV_DATA: + if (!body) { + body = malloc(sizeof(*body)); + if (!body) { + free(head); + head = NULL; + recv_state = RECV_INIT; + ret = LB_STATUS_ERROR_MEMORY; + write_item_to_pipe(item, ret); + item = NULL; + break; + } + + recvsz = sizeof(*body); + offset = 0; + } + + ret = secure_socket_recv(s_info.file_service_fd, (char *)body + offset, recvsz - offset, NULL); + if (ret > 0) { + offset += ret; + } else { + free(head); + head = NULL; + free(body); + body = NULL; + recv_state = RECV_INIT; + ret = LB_STATUS_ERROR_FAULT; + write_item_to_pipe(item, ret); + item = NULL; + break; + } + + if (offset == sizeof(*body)) { + void *tmp; + + if (body->size < 0) { + ErrPrint("body->size: %d\n", body->size); + free(head); + head = NULL; + free(body); + body = NULL; + recv_state = RECV_INIT; + ret = LB_STATUS_ERROR_FAULT; + write_item_to_pipe(item, ret); + item = NULL; + break; + } + + recvsz += body->size; + + tmp = realloc(body, recvsz); + if (!tmp) { + ErrPrint("Heap: %s\n", strerror(errno)); + free(head); + head = NULL; + + free(body); + body = NULL; + recv_state = RECV_INIT; + + ret = LB_STATUS_ERROR_MEMORY; + write_item_to_pipe(item, ret); + item = NULL; + break; + } + } else if (offset == recvsz) { + /* Flush this to the file */ + ret = write(file_fd, body->data, body->size); + if (ret < 0) { + ErrPrint("write: %s\n", strerror(errno)); + free(head); + head = NULL; + + free(body); + body = NULL; + recv_state = RECV_INIT; + + ret = LB_STATUS_ERROR_IO; + write_item_to_pipe(item, ret); + item = NULL; + break; + } else { + if (body->size != ret) { + DbgPrint("Body is not flushed correctly: %d, %d\n", ret, body->size); + ret = body->size; + } + + file_offset += ret; + if (file_offset == head->size) { + if (close(file_fd) < 0) { + ErrPrint("close: %s\n", strerror(errno)); + } + ret = LB_STATUS_SUCCESS; + write_item_to_pipe(item, ret); + item = NULL; + } + } + + free(body); + body = NULL; + + free(head); + head = NULL; + + recv_state = RECV_INIT; + } else { + ErrPrint("Invalid state\n"); + + ret = -EFAULT; + free(body); + body = NULL; + free(head); + head = NULL; + recv_state = RECV_INIT; + + ret = LB_STATUS_ERROR_FAULT; + write_item_to_pipe(item, ret); + item = NULL; + } + break; + default: + ErrPrint("Unknown event: %d\n", recv_state); + ret = LB_STATUS_ERROR_FAULT; + write_item_to_pipe(item, ret); + item = NULL; + break; + } + } else if (item == NULL && recv_state == RECV_INIT && FD_ISSET(s_info.ctrl_pipe[PIPE_READ], &set)) { + int ch; + struct dlist *l; + + /* Only if the recv state is not changed, we can get next request item */ + ch = get_event_ch(s_info.ctrl_pipe[PIPE_READ]); + if (ch == EVT_END_CH) { + DbgPrint("Service thread is canceled\n"); + break; + } + + CRITICAL_SECTION_BEGIN(&s_info.file_svc_lock); + l = dlist_nth(s_info.request_list, 0); + item = dlist_data(l); + s_info.request_list = dlist_remove(s_info.request_list, l); + CRITICAL_SECTION_END(&s_info.file_svc_lock); + } + } + + return (void *)ret; +} + +/* Master */ +static gboolean evt_cb(GIOChannel *src, GIOCondition cond, gpointer data) +{ + int fd; + struct request_item *item; + + fd = g_io_channel_unix_get_fd(src); + + if (!(cond & G_IO_IN)) { + DbgPrint("Client is disconencted\n"); + return FALSE; + } + + if ((cond & G_IO_ERR) || (cond & G_IO_HUP) || (cond & G_IO_NVAL)) { + DbgPrint("Client connection is lost\n"); + return FALSE; + } + + if (read(fd, &item, sizeof(item)) != sizeof(item)) { + ErrPrint("read: %s\n", strerror(errno)); + } else { + if (item->result_cb) { + item->result_cb(item->filename, item->save_to, item->ret, item->data); + } + + free(item->filename); + free(item->save_to); + free(item); + } + + return TRUE; +} + +int file_service_send_request(const char *filename, const char *save_to, void (*result_cb)(const char *filename, const char *save_to, int ret, void *data), void *data) +{ + struct request_item *item; + + item = malloc(sizeof(*item)); + if (!item) { + ErrPrint("Heap: %s\n", strerror(errno)); + return -ENOMEM; + } + + item->filename = strdup(filename); + if (!item->filename) { + ErrPrint("Heap: %s\n", strerror(errno)); + free(item); + return -ENOMEM; + } + + item->save_to = strdup(save_to); + if (!item->save_to) { + ErrPrint("Heap: %s\n", strerror(errno)); + free(item->filename); + free(item); + return -ENOMEM; + } + + item->result_cb = result_cb; + item->data = data; + + CRITICAL_SECTION_BEGIN(&s_info.file_svc_lock); + s_info.request_list = dlist_append(s_info.request_list, item); + CRITICAL_SECTION_END(&s_info.file_svc_lock); + return 0; +} + +int file_service_init(void) +{ + int status; + GIOChannel *gio; + guint id; + + if (strncmp(client_addr(), COM_CORE_REMOTE_SCHEME, strlen(COM_CORE_REMOTE_SCHEME))) { + return 0; + } + + s_info.file_service_fd = file_service_open(); + if (s_info.file_service_fd < 0) { + return -EFAULT; + } + + if (pipe2(s_info.ctrl_pipe, O_NONBLOCK | O_CLOEXEC) < 0) { + ErrPrint("file service: %s\n", strerror(errno)); + file_service_close(s_info.file_service_fd); + s_info.file_service_fd = -1; + return -EFAULT; + } + + if (pipe2(s_info.evt_pipe, O_NONBLOCK | O_CLOEXEC) < 0) { + ErrPrint("file service: %s\n", strerror(errno)); + CLOSE_PIPE(s_info.ctrl_pipe); + file_service_close(s_info.file_service_fd); + s_info.file_service_fd = -1; + return -EFAULT; + } + + status = pthread_mutex_init(&s_info.file_svc_lock, NULL); + if (status != 0) { + ErrPrint("Mutex: %s\n", strerror(status)); + CLOSE_PIPE(s_info.ctrl_pipe); + CLOSE_PIPE(s_info.evt_pipe); + file_service_close(s_info.file_service_fd); + s_info.file_service_fd = -1; + return -EFAULT; + } + + gio = g_io_channel_unix_new(s_info.evt_pipe[PIPE_READ]); + if (!gio) { + ErrPrint("io channel new\n"); + status = pthread_mutex_destroy(&s_info.file_svc_lock); + if (status != 0) { + ErrPrint("destroy: %s\n", strerror(status)); + } + CLOSE_PIPE(s_info.ctrl_pipe); + CLOSE_PIPE(s_info.evt_pipe); + file_service_close(s_info.file_service_fd); + s_info.file_service_fd = -1; + return -EFAULT; + } + + g_io_channel_set_close_on_unref(gio, FALSE); + + id = g_io_add_watch(gio, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, (GIOFunc)evt_cb, NULL); + if (id <= 0) { + GError *err = NULL; + ErrPrint("Failed to add IO watch\n"); + g_io_channel_shutdown(gio, TRUE, &err); + if (err) { + ErrPrint("Shutdown: %s\n", err->message); + g_error_free(err); + } + g_io_channel_unref(gio); + + status = pthread_mutex_destroy(&s_info.file_svc_lock); + if (status != 0) { + ErrPrint("destroy: %s\n", strerror(status)); + } + CLOSE_PIPE(s_info.ctrl_pipe); + CLOSE_PIPE(s_info.evt_pipe); + file_service_close(s_info.file_service_fd); + s_info.file_service_fd = -1; + return -EIO; + } + + status = pthread_create(&s_info.file_svc_thid, NULL, file_service_main, NULL); + if (status != 0) { + GError *err = NULL; + ErrPrint("Failed to add IO watch\n"); + g_io_channel_shutdown(gio, TRUE, &err); + if (err) { + ErrPrint("Shutdown: %s\n", err->message); + g_error_free(err); + } + g_io_channel_unref(gio); + + ErrPrint("file service: %s\n", strerror(status)); + CLOSE_PIPE(s_info.ctrl_pipe); + CLOSE_PIPE(s_info.evt_pipe); + file_service_close(s_info.file_service_fd); + s_info.file_service_fd = -1; + + status = pthread_mutex_destroy(&s_info.file_svc_lock); + if (status != 0) { + ErrPrint("destroy: %s\n", strerror(status)); + } + + return -EFAULT; + } + + g_io_channel_unref(gio); + return 0; +} + +int file_service_fini(void) +{ + void *svc_ret; + int ret; + + if (strncmp(client_addr(), COM_CORE_REMOTE_SCHEME, strlen(COM_CORE_REMOTE_SCHEME))) { + return 0; + } + + (void)put_event_ch(s_info.ctrl_pipe[PIPE_WRITE], EVT_END_CH); + + ret = pthread_join(s_info.file_svc_thid, &svc_ret); + if (ret != 0) { + ErrPrint("join: %s\n", strerror(ret)); + } else { + DbgPrint("file svc returns: %d\n", (int)svc_ret); + } + + ret = pthread_mutex_destroy(&s_info.file_svc_lock); + if (ret != 0) { + ErrPrint("destroy: %s\n", strerror(ret)); + } + + CLOSE_PIPE(s_info.evt_pipe); + CLOSE_PIPE(s_info.ctrl_pipe); + + return 0; +} + +/* End of a file */ diff --git a/src/livebox.c b/src/livebox.c new file mode 100644 index 0000000..1eed536 --- /dev/null +++ b/src/livebox.c @@ -0,0 +1,4885 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> /* malloc */ +#include <string.h> /* strdup */ +#include <math.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <gio/gio.h> +#include <aul.h> +#include <dlog.h> + +#include <com-core_packet.h> +#include <packet.h> +#include <livebox-service.h> +#include <livebox-errno.h> + +#include "debug.h" +#include "fb.h" +#include "livebox.h" +#include "livebox_internal.h" +#include "dlist.h" +#include "util.h" +#include "master_rpc.h" +#include "client.h" +#include "conf.h" + +#define EAPI __attribute__((visibility("default"))) + +#if defined(FLOG) +FILE *__file_log_fp; +#endif + +static int default_launch_handler(struct livebox *handler, const char *appid, void *data); + +enum event_state { + INFO_STATE_CALLBACK_IN_IDLE = 0x00, + INFO_STATE_CALLBACK_IN_PROCESSING = 0x01, +}; + +static struct info { + struct dlist *livebox_list; + struct dlist *livebox_common_list; + + struct dlist *event_list; + struct dlist *fault_list; + + int init_count; + int prevent_overwrite; + enum event_state event_state; + enum event_state fault_state; + guint job_timer; + struct dlist *job_list; + + struct launch { + int (*handler)(struct livebox *handler, const char *appid, void *data); + void *data; + } launch; +} s_info = { + .livebox_list = NULL, + .event_list = NULL, + .fault_list = NULL, + .init_count = 0, + .prevent_overwrite = 0, + .event_state = INFO_STATE_CALLBACK_IN_IDLE, + .fault_state = INFO_STATE_CALLBACK_IN_IDLE, + .job_timer = 0, + .job_list = NULL, + .launch = { + .handler = default_launch_handler, + .data = NULL, + }, +}; + +struct cb_info { + ret_cb_t cb; + void *data; +}; + +struct event_info { + int is_deleted; + int (*handler)(struct livebox *handler, enum livebox_event_type event, void *data); + void *user_data; +}; + +struct fault_info { + int is_deleted; + int (*handler)(enum livebox_fault_type event, const char *pkgname, const char *filename, const char *func, void *data); + void *user_data; +}; + +static void lb_pixmap_acquired_cb(struct livebox *handler, const struct packet *result, void *data); +static void pd_pixmap_acquired_cb(struct livebox *handler, const struct packet *result, void *data); + +static int default_launch_handler(struct livebox *handler, const char *appid, void *data) +{ + int ret; + + ret = aul_launch_app(appid, NULL); + if (ret <= 0) { + ErrPrint("Failed to launch an app %s (%d)\n", appid, ret); + } + +/* + service_h service; + + DbgPrint("AUTO_LAUNCH [%s]\n", handler->common->lb.auto_launch); + + ret = service_create(&service); + if (ret == SERVICE_ERROR_NONE) { + service_set_package(service, handler->common->lb.auto_launch); + service_send_launch_request(service, NULL, NULL); + service_destroy(service); + } else { + ErrPrint("Failed to launch an app %s (%d)\n", handler->common->lb.auto_launch, ret); + } +*/ + + return ret > 0 ? LB_STATUS_SUCCESS : LB_STATUS_ERROR_FAULT; +} + +static inline void default_create_cb(struct livebox *handler, int ret, void *data) +{ + DbgPrint("Default created event handler: %d\n", ret); +} + +static inline void default_delete_cb(struct livebox *handler, int ret, void *data) +{ + DbgPrint("Default deleted event handler: %d\n", ret); +} + +static inline void default_pinup_cb(struct livebox *handler, int ret, void *data) +{ + DbgPrint("Default pinup event handler: %d\n", ret); +} + +static inline void default_group_changed_cb(struct livebox *handler, int ret, void *data) +{ + DbgPrint("Default group changed event handler: %d\n", ret); +} + +static inline void default_period_changed_cb(struct livebox *handler, int ret, void *data) +{ + DbgPrint("Default period changed event handler: %d\n", ret); +} + +static inline void default_pd_created_cb(struct livebox *handler, int ret, void *data) +{ + DbgPrint("Default PD created event handler: %d\n", ret); +} + +static inline void default_pd_destroyed_cb(struct livebox *handler, int ret, void *data) +{ + DbgPrint("Default PD destroyed event handler: %d\n", ret); +} + +static inline void default_lb_size_changed_cb(struct livebox *handler, int ret, void *data) +{ + DbgPrint("Default LB size changed event handler: %d\n", ret); +} + +static inline void default_update_mode_cb(struct livebox *handler, int ret, void *data) +{ + DbgPrint("Default update mode set event handler: %d\n", ret); +} + +static inline void default_access_event_cb(struct livebox *handler, int ret, void *data) +{ + DbgPrint("Default access event handler: %d\n", ret); +} + +static inline void default_key_event_cb(struct livebox *handler, int ret, void *data) +{ + DbgPrint("Default key event handler: %d\n", ret); +} + +static inline __attribute__((always_inline)) struct cb_info *create_cb_info(ret_cb_t cb, void *data) +{ + struct cb_info *info; + + info = malloc(sizeof(*info)); + if (!info) { + ErrPrint("Heap: %s\n", strerror(errno)); + return NULL; + } + + info->cb = cb; + info->data = data; + return info; +} + +static inline void destroy_cb_info(struct cb_info *info) +{ + free(info); +} + +static int do_fb_lock(int fd) +{ + struct flock flock; + int ret; + + flock.l_type = F_RDLCK; + flock.l_whence = SEEK_SET; + flock.l_start = 0; + flock.l_len = 0; + flock.l_pid = getpid(); + + do { + ret = fcntl(fd, F_SETLKW, &flock); + if (ret < 0) { + ret = errno; + ErrPrint("fcntl: %s\n", strerror(errno)); + } + } while (ret == EINTR); + + return ret; +} + +static int do_fb_unlock(int fd) +{ + struct flock flock; + int ret; + + flock.l_type = F_UNLCK; + flock.l_whence = SEEK_SET; + flock.l_start = 0; + flock.l_len = 0; + flock.l_pid = getpid(); + + do { + ret = fcntl(fd, F_SETLKW, &flock); + if (ret < 0) { + ret = errno; + ErrPrint("fcntl: %s\n", strerror(errno)); + } + } while (ret == EINTR); + + return ret; +} + +int lb_destroy_lock_file(struct livebox_common *common, int is_pd) +{ + if (is_pd) { + if (!common->pd.lock) { + return LB_STATUS_ERROR_INVALID; + } + + if (close(common->pd.lock_fd) < 0) { + ErrPrint("close: %s\n", strerror(errno)); + } + common->pd.lock_fd = -1; + + if (unlink(common->pd.lock) < 0) { + ErrPrint("unlink: %s\n", strerror(errno)); + } + + free(common->pd.lock); + common->pd.lock = NULL; + } else { + if (!common->lb.lock) { + return LB_STATUS_ERROR_INVALID; + } + + if (close(common->lb.lock_fd) < 0) { + ErrPrint("close: %s\n", strerror(errno)); + } + common->lb.lock_fd = -1; + + if (unlink(common->lb.lock) < 0) { + ErrPrint("unlink: %s\n", strerror(errno)); + } + + free(common->lb.lock); + common->lb.lock = NULL; + } + + return LB_STATUS_SUCCESS; +} + +int lb_create_lock_file(struct livebox_common *common, int is_pd) +{ + int len; + char *file; + + len = strlen(common->id); + file = malloc(len + 20); + if (!file) { + ErrPrint("Heap: %s\n", strerror(errno)); + return LB_STATUS_ERROR_MEMORY; + } + + snprintf(file, len + 20, "%s.%s.lck", util_uri_to_path(common->id), is_pd ? "pd" : "lb"); + + if (is_pd) { + common->pd.lock_fd = open(file, O_RDONLY); + if (common->pd.lock_fd < 0) { + ErrPrint("open: %s\n", strerror(errno)); + free(file); + return LB_STATUS_ERROR_IO; + } + + common->pd.lock = file; + } else { + common->lb.lock_fd = open(file, O_RDONLY); + if (common->lb.lock_fd < 0) { + ErrPrint("open: %s\n", strerror(errno)); + free(file); + return LB_STATUS_ERROR_IO; + } + + common->lb.lock = file; + } + + return LB_STATUS_SUCCESS; +} + +static void update_mode_cb(struct livebox *handler, const struct packet *result, void *data) +{ + int ret; + + if (!result) { + ret = LB_STATUS_ERROR_FAULT; + goto errout; + } else if (packet_get(result, "i", &ret) != 1) { + ErrPrint("Invalid argument\n"); + ret = LB_STATUS_ERROR_INVALID; + goto errout; + } + + if (ret < 0) { + ErrPrint("Resize request is failed: %d\n", ret); + goto errout; + } + + return; + +errout: + handler->cbs.update_mode.cb(handler, ret, handler->cbs.update_mode.data); + handler->cbs.update_mode.cb = NULL; + handler->cbs.update_mode.data = NULL; + handler->common->request.update_mode = 0; + + if (ret == (int)LB_STATUS_ERROR_NOT_EXIST && handler->refcnt == 2) { + lb_invoke_event_handler(handler, LB_EVENT_DELETED); + lb_unref(handler, 1); + } +} + +static void resize_cb(struct livebox *handler, const struct packet *result, void *data) +{ + int ret; + + if (!result) { + ret = LB_STATUS_ERROR_FAULT; + goto errout; + } else if (packet_get(result, "i", &ret) != 1) { + ErrPrint("Invalid argument\n"); + ret = LB_STATUS_ERROR_INVALID; + goto errout; + } + + /*! + * \note + * In case of resize request, + * The livebox handler will not have resized value right after this callback, + * It can only get the new size when it makes updates. + * + * So the user can only get the resized value(result) from the first update event + * after this request. + */ + if (ret < 0) { + ErrPrint("Resize request is failed: %d\n", ret); + goto errout; + } + + return; + +errout: + handler->cbs.size_changed.cb(handler, ret, handler->cbs.size_changed.data); + handler->cbs.size_changed.cb = NULL; + handler->cbs.size_changed.data = NULL; + handler->common->request.size_changed = 0; + + if (ret == (int)LB_STATUS_ERROR_NOT_EXIST && handler->refcnt == 2) { + lb_invoke_event_handler(handler, LB_EVENT_DELETED); + lb_unref(handler, 1); + } +} + +static void text_signal_cb(struct livebox *handler, const struct packet *result, void *data) +{ + int ret; + void *cbdata; + struct cb_info *info = data; + ret_cb_t cb; + + cbdata = info->data; + cb = info->cb; + destroy_cb_info(info); + + if (!result) { + ret = LB_STATUS_ERROR_FAULT; + } else if (packet_get(result, "i", &ret) != 1) { + ErrPrint("Invalid argument\n"); + ret = LB_STATUS_ERROR_INVALID; + } + + if (cb) { + cb(handler, ret, cbdata); + } + return; +} + +static void set_group_ret_cb(struct livebox *handler, const struct packet *result, void *data) +{ + int ret; + + if (!result) { + ret = LB_STATUS_ERROR_FAULT; + goto errout; + } else if (packet_get(result, "i", &ret) != 1) { + ErrPrint("Invalid argument\n"); + ret = LB_STATUS_ERROR_INVALID; + goto errout; + } + + if (ret < 0) { + goto errout; + } + + return; + +errout: + handler->cbs.group_changed.cb(handler, ret, handler->cbs.group_changed.data); + handler->cbs.group_changed.cb = NULL; + handler->cbs.group_changed.data = NULL; + handler->common->request.group_changed = 0; + + if (ret == (int)LB_STATUS_ERROR_NOT_EXIST && handler->refcnt == 2) { + lb_invoke_event_handler(handler, LB_EVENT_DELETED); + lb_unref(handler, 1); + } +} + +static void period_ret_cb(struct livebox *handler, const struct packet *result, void *data) +{ + int ret; + + if (!result) { + ret = LB_STATUS_ERROR_FAULT; + goto errout; + } else if (packet_get(result, "i", &ret) != 1) { + ErrPrint("Invalid argument\n"); + ret = LB_STATUS_ERROR_INVALID; + goto errout; + } + + if (ret < 0) { + goto errout; + } + + return; + +errout: + handler->cbs.period_changed.cb(handler, ret, handler->cbs.period_changed.data); + handler->cbs.period_changed.cb = NULL; + handler->cbs.period_changed.data = NULL; + handler->common->request.period_changed = 0; + + if (ret == (int)LB_STATUS_ERROR_NOT_EXIST && handler->refcnt == 2) { + lb_invoke_event_handler(handler, LB_EVENT_DELETED); + lb_unref(handler, 1); + } +} + +static void del_ret_cb(struct livebox *handler, const struct packet *result, void *data) +{ + struct cb_info *info = data; + int ret; + ret_cb_t cb; + void *cbdata; + + cb = info->cb; + cbdata = info->data; + destroy_cb_info(info); + + if (!result) { + ErrPrint("Connection lost?\n"); + ret = LB_STATUS_ERROR_FAULT; + } else if (packet_get(result, "i", &ret) != 1) { + ErrPrint("Invalid argument\n"); + ret = LB_STATUS_ERROR_INVALID; + } + + if (ret == 0) { + handler->cbs.deleted.cb = cb; + handler->cbs.deleted.data = cbdata; + } else if (cb) { + cb(handler, ret, cbdata); + } + + /*! + * \note + * Do not call the deleted callback from here. + * master will send the "deleted" event. + * Then invoke this callback. + * + * if (handler->cbs.deleted.cb) + * handler->cbs.deleted.cb(handler, ret, handler->cbs.deleted.data); + */ +} + +static void new_ret_cb(struct livebox *handler, const struct packet *result, void *data) +{ + int ret; + struct cb_info *info = data; + ret_cb_t cb; + void *cbdata; + + cb = info->cb; + cbdata = info->data; + destroy_cb_info(info); + + if (!result) { + ret = LB_STATUS_ERROR_FAULT; + } else if (packet_get(result, "i", &ret) != 1) { + ret = LB_STATUS_ERROR_INVALID; + } + + if (ret >= 0) { + handler->cbs.created.cb = cb; + handler->cbs.created.data = cbdata; + + /*! + * \note + * Don't go anymore ;) + */ + return; + } else if (cb) { + /*! + * \note + * It means the current instance is not created, + * so user has to know about this. + * notice it to user using "deleted" event. + */ + cb(handler, ret, cbdata); + } + + lb_unref(handler, 1); +} + +static void pd_create_cb(struct livebox *handler, const struct packet *result, void *data) +{ + int ret; + + if (!result) { + ret = LB_STATUS_ERROR_FAULT; + goto errout; + } else if (packet_get(result, "i", &ret) != 1) { + ret = LB_STATUS_ERROR_INVALID; + goto errout; + } + + if (ret < 0) { + ErrPrint("Failed to create a PD[%d]\n", ret); + goto errout; + } + + return; + +errout: + handler->cbs.pd_created.cb(handler, ret, handler->cbs.pd_created.data); + handler->cbs.pd_created.cb = NULL; + handler->cbs.pd_created.data = NULL; + handler->common->request.pd_created = 0; + + if (ret == (int)LB_STATUS_ERROR_NOT_EXIST && handler->refcnt == 2) { + lb_invoke_event_handler(handler, LB_EVENT_DELETED); + lb_unref(handler, 1); + } +} + +static void activated_cb(struct livebox *handler, const struct packet *result, void *data) +{ + int ret; + struct cb_info *info = data; + void *cbdata; + ret_cb_t cb; + const char *pkgname = ""; + + cbdata = info->data; + cb = info->cb; + destroy_cb_info(info); + + if (!result) { + ret = LB_STATUS_ERROR_FAULT; + } else if (packet_get(result, "is", &ret, &pkgname) != 2) { + ret = LB_STATUS_ERROR_INVALID; + } + + if (cb) { + cb(handler, ret, cbdata); + } +} + +static void pd_destroy_cb(struct livebox *handler, const struct packet *result, void *data) +{ + int ret; + ret_cb_t cb; + void *cbdata; + struct cb_info *info = data; + + cbdata = info->data; + cb = info->cb; + destroy_cb_info(info); + + if (!result) { + ErrPrint("Result is NIL (may connection lost)\n"); + ret = LB_STATUS_ERROR_FAULT; + } else if (packet_get(result, "i", &ret) != 1) { + ErrPrint("Invalid parameter\n"); + ret = LB_STATUS_ERROR_INVALID; + } + + if (ret == (int)LB_STATUS_SUCCESS) { + handler->cbs.pd_destroyed.cb = cb; + handler->cbs.pd_destroyed.data = cbdata; + } else { + handler->common->is_pd_created = 0; + handler->common->request.pd_destroyed = 0; + + if (cb) { + cb(handler, ret, cbdata); + } + } +} + +static void delete_cluster_cb(struct livebox *handler, const struct packet *result, void *data) +{ + struct cb_info *info = data; + int ret; + ret_cb_t cb; + void *cbdata; + + cb = info->cb; + cbdata = info->data; + destroy_cb_info(info); + + if (!result) { + ret = LB_STATUS_ERROR_FAULT; + } else if (packet_get(result, "i", &ret) != 1) { + ret = LB_STATUS_ERROR_INVALID; + } + + if (cb) { + cb(handler, ret, cbdata); + } +} + +static void delete_category_cb(struct livebox *handler, const struct packet *result, void *data) +{ + struct cb_info *info = data; + int ret; + ret_cb_t cb; + void *cbdata; + + cb = info->cb; + cbdata = info->data; + destroy_cb_info(info); + + if (!result) { + ret = LB_STATUS_ERROR_FAULT; + } else if (packet_get(result, "i", &ret) != 1) { + ret = LB_STATUS_ERROR_INVALID; + } + + if (cb) { + cb(handler, ret, cbdata); + } +} + +static int lb_acquire_lb_pixmap(struct livebox *handler, ret_cb_t cb, void *data) +{ + struct packet *packet; + struct cb_info *cbinfo; + const char *id; + int ret; + + id = fb_id(handler->common->lb.fb); + if (!id || strncasecmp(id, SCHEMA_PIXMAP, strlen(SCHEMA_PIXMAP))) { + return LB_STATUS_ERROR_INVALID; + } + + packet = packet_create("lb_acquire_pixmap", "ss", handler->common->pkgname, handler->common->id); + if (!packet) { + ErrPrint("Failed to build a param\n"); + return LB_STATUS_ERROR_FAULT; + } + + cbinfo = create_cb_info(cb, data); + if (!cbinfo) { + packet_destroy(packet); + return LB_STATUS_ERROR_FAULT; + } + + ret = master_rpc_async_request(handler, packet, 0, lb_pixmap_acquired_cb, cbinfo); + if (ret < 0) { + destroy_cb_info(cbinfo); + } + + return ret; +} + +static void lb_pixmap_acquired_cb(struct livebox *handler, const struct packet *result, void *data) +{ + int pixmap; + int ret = LB_STATUS_ERROR_INVALID; + ret_cb_t cb; + void *cbdata; + struct cb_info *info = data; + + cb = info->cb; + cbdata = info->data; + destroy_cb_info(info); + + if (!result) { + pixmap = 0; /* PIXMAP 0 means error */ + } else if (packet_get(result, "ii", &pixmap, &ret) != 2) { + pixmap = 0; + } + + if (ret == (int)LB_STATUS_ERROR_BUSY) { + ret = lb_acquire_lb_pixmap(handler, cb, cbdata); + DbgPrint("Busy, Try again: %d\n", ret); + /* Try again */ + } else if (ret == (int)LB_STATUS_ERROR_NOT_EXIST && handler->refcnt == 2) { + if (cb) { + cb(handler, pixmap, cbdata); + } + + lb_invoke_event_handler(handler, LB_EVENT_DELETED); + lb_unref(handler, 1); + } else { + if (cb) { + cb(handler, pixmap, cbdata); + } + } +} + +static int lb_acquire_pd_pixmap(struct livebox *handler, ret_cb_t cb, void *data) +{ + struct packet *packet; + struct cb_info *cbinfo; + const char *id; + int ret; + + id = fb_id(handler->common->pd.fb); + if (!id || strncasecmp(id, SCHEMA_PIXMAP, strlen(SCHEMA_PIXMAP))) { + return LB_STATUS_ERROR_INVALID; + } + + packet = packet_create("pd_acquire_pixmap", "ss", handler->common->pkgname, handler->common->id); + if (!packet) { + ErrPrint("Failed to build a param\n"); + return LB_STATUS_ERROR_FAULT; + } + + cbinfo = create_cb_info(cb, data); + if (!cbinfo) { + packet_destroy(packet); + return LB_STATUS_ERROR_FAULT; + } + + ret = master_rpc_async_request(handler, packet, 0, pd_pixmap_acquired_cb, cbinfo); + if (ret < 0) { + /*! + * \note + * Packet will be destroyed by master_rpc_async_request + */ + destroy_cb_info(cbinfo); + } + + return ret; +} + +static void pd_pixmap_acquired_cb(struct livebox *handler, const struct packet *result, void *data) +{ + int pixmap; + int ret; + ret_cb_t cb; + void *cbdata; + struct cb_info *info = data; + + cb = info->cb; + cbdata = info->data; + destroy_cb_info(info); + + if (!result) { + pixmap = 0; /* PIXMAP 0 means error */ + ret = LB_STATUS_ERROR_FAULT; + } else if (packet_get(result, "ii", &pixmap, &ret) != 2) { + pixmap = 0; + ret = LB_STATUS_ERROR_INVALID; + } + + if (ret == (int)LB_STATUS_ERROR_BUSY) { + ret = lb_acquire_pd_pixmap(handler, cb, cbdata); + DbgPrint("Busy, Try again: %d\n", ret); + /* Try again */ + } else if (ret == (int)LB_STATUS_ERROR_NOT_EXIST && handler->refcnt == 2) { + if (cb) { + cb(handler, pixmap, cbdata); + } + lb_invoke_event_handler(handler, LB_EVENT_DELETED); + lb_unref(handler, 1); + } else { + if (cb) { + DbgPrint("ret: %d, pixmap: %d\n", ret, pixmap); + cb(handler, pixmap, cbdata); + } + } +} + +static void pinup_done_cb(struct livebox *handler, const struct packet *result, void *data) +{ + int ret; + + if (!result) { + ret = LB_STATUS_ERROR_FAULT; + goto errout; + } else if (packet_get(result, "i", &ret) != 1) { + goto errout; + } + + if (ret < 0) { + goto errout; + } + + return; + +errout: + handler->cbs.pinup.cb(handler, ret, handler->cbs.pinup.data); + handler->cbs.pinup.cb = NULL; + handler->cbs.pinup.data = NULL; + handler->common->request.pinup = 0; + + if (ret == (int)LB_STATUS_ERROR_NOT_EXIST && handler->refcnt == 2) { + lb_invoke_event_handler(handler, LB_EVENT_DELETED); + lb_unref(handler, 1); + } +} + +static void key_ret_cb(struct livebox *handler, const struct packet *result, void *data) +{ + int ret; + + if (!result) { + ret = LB_STATUS_ERROR_FAULT; + return; + } + + if (packet_get(result, "i", &ret) != 1) { + ret = LB_STATUS_ERROR_INVALID; + return; + } + + if (ret != LB_STATUS_SUCCESS) { + goto errout; + } + + return; +errout: + handler->cbs.key_event.cb(handler, ret, handler->cbs.key_event.data); + handler->cbs.key_event.cb = NULL; + handler->cbs.key_event.data = NULL; + handler->common->request.key_event = 0; + + if (ret == (int)LB_STATUS_ERROR_NOT_EXIST && handler->refcnt == 2) { + lb_invoke_event_handler(handler, LB_EVENT_DELETED); + lb_unref(handler, 1); + } +} + +static void access_ret_cb(struct livebox *handler, const struct packet *result, void *data) +{ + int ret; + + if (!result) { + ret = LB_STATUS_ERROR_FAULT; + return; + } + + if (packet_get(result, "i", &ret) != 1) { + ret = LB_STATUS_ERROR_INVALID; + return; + } + + if (ret != LB_STATUS_SUCCESS) { + goto errout; + } + + return; + +errout: + handler->cbs.access_event.cb(handler, ret, handler->cbs.access_event.data); + handler->cbs.access_event.cb = NULL; + handler->cbs.access_event.data = NULL; + handler->common->request.access_event = 0; + + if (ret == (int)LB_STATUS_ERROR_NOT_EXIST && handler->refcnt == 2) { + lb_invoke_event_handler(handler, LB_EVENT_DELETED); + lb_unref(handler, 1); + } +} + +static int send_access_event(struct livebox *handler, const char *event, int x, int y) +{ + struct packet *packet; + double timestamp; + + timestamp = util_timestamp(); + + packet = packet_create(event, "ssdii", handler->common->pkgname, handler->common->id, timestamp, x, y); + if (!packet) { + ErrPrint("Failed to build packet\n"); + return LB_STATUS_ERROR_FAULT; + } + + return master_rpc_async_request(handler, packet, 0, access_ret_cb, NULL); +} + +static int send_key_event(struct livebox *handler, const char *event, unsigned int keycode) +{ + struct packet *packet; + double timestamp; + + timestamp = util_timestamp(); + packet = packet_create(event, "ssdi", handler->common->pkgname, handler->common->id, timestamp, keycode); + if (!packet) { + ErrPrint("Failed to build packet\n"); + return LB_STATUS_ERROR_FAULT; + } + + return master_rpc_async_request(handler, packet, 0, key_ret_cb, NULL); +} + +static int send_mouse_event(struct livebox *handler, const char *event, int x, int y) +{ + struct packet *packet; + double timestamp; + + timestamp = util_timestamp(); + packet = packet_create_noack(event, "ssdii", handler->common->pkgname, handler->common->id, timestamp, x, y); + if (!packet) { + ErrPrint("Failed to build param\n"); + return LB_STATUS_ERROR_FAULT; + } + + return master_rpc_request_only(handler, packet); +} + +static void initialize_livebox(void *disp, int use_thread) +{ +#if defined(FLOG) + char filename[BUFSIZ]; + snprintf(filename, sizeof(filename), "/tmp/%d.box.log", getpid()); + __file_log_fp = fopen(filename, "w+t"); + if (!__file_log_fp) { + __file_log_fp = fdopen(1, "w+t"); + } +#endif + livebox_service_init(); + fb_init(disp); + + client_init(use_thread); + + s_info.init_count++; +} + +EAPI int livebox_init_with_options(void *disp, int prevent_overwrite, double event_filter, int use_thread) +{ + if (s_info.init_count > 0) { + s_info.init_count++; + return LB_STATUS_SUCCESS; + } + + /*! + * \note + * Some application doesn't want to use the environment value. + * So set them using arguments. + */ + s_info.prevent_overwrite = prevent_overwrite; + conf_set_event_filter(event_filter); + + initialize_livebox(disp, use_thread); + return LB_STATUS_SUCCESS; +} + +EAPI int livebox_init(void *disp) +{ + const char *env; + + if (s_info.init_count > 0) { + s_info.init_count++; + return LB_STATUS_SUCCESS; + } + + env = getenv("PROVIDER_DISABLE_PREVENT_OVERWRITE"); + if (env && !strcasecmp(env, "true")) { + s_info.prevent_overwrite = 1; + } + + env = getenv("PROVIDER_EVENT_FILTER"); + if (env) { + double event_filter; + if (sscanf(env, "%lf", &event_filter) == 1) { + conf_set_event_filter(event_filter); + } + } + + initialize_livebox(disp, 0); + return LB_STATUS_SUCCESS; +} + +EAPI int livebox_fini(void) +{ + if (s_info.init_count <= 0) { + ErrPrint("Doesn't initialized\n"); + return LB_STATUS_ERROR_INVALID; + } + + s_info.init_count--; + if (s_info.init_count > 0) { + ErrPrint("init count : %d\n", s_info.init_count); + return LB_STATUS_SUCCESS; + } + + client_fini(); + fb_fini(); + livebox_service_fini(); + return LB_STATUS_SUCCESS; +} + +static inline char *lb_pkgname(const char *pkgname) +{ + char *lb; + + lb = livebox_service_pkgname(pkgname); + if (!lb) { + if (util_validate_livebox_package(pkgname) == 0) { + return strdup(pkgname); + } + } + + return lb; +} + +static struct livebox_common *find_sharable_common_handle(const char *pkgname, const char *content, int w, int h, const char *cluster, const char *category) +{ + struct dlist *l; + struct livebox_common *common; + + if (!conf_shared_content()) { + /*! + * Shared content option is turnned off. + */ + return NULL; + } + + dlist_foreach(s_info.livebox_common_list, l, common) { + if (common->state != CREATE) { + continue; + } + + if (strcmp(common->pkgname, pkgname)) { + continue; + } + + if (strcmp(common->cluster, cluster)) { + DbgPrint("Cluster mismatched\n"); + continue; + } + + if (strcmp(common->category, category)) { + DbgPrint("Category mismatched\n"); + continue; + } + + if (common->content && content) { + if (strcmp(common->content, content)) { + DbgPrint("%s Content ([%s] <> [%s])\n", common->pkgname, common->content, content); + continue; + } + } else { + int c1_len; + int c2_len; + + /*! + * \note + * We assumes "" (ZERO length string) to NULL + */ + c1_len = common->content ? strlen(common->content) : 0; + c2_len = content ? strlen(content) : 0; + if (c1_len != c2_len) { + DbgPrint("%s Content %p <> %p\n", common->pkgname, common->content, content); + continue; + } + } + + if (common->request.size_changed) { + DbgPrint("Changing size\n"); + /*! + * \note + * Do not re-use resizing instance. + * We will not use predicted size. + */ + continue; + } + + if (common->request.created) { + DbgPrint("Creating now but re-use it (%s)\n", common->pkgname); + } + + if (common->lb.width != w || common->lb.height != h) { + DbgPrint("Size mismatched\n"); + continue; + } + + DbgPrint("common handle is found: %p\n", common); + return common; + } + + return NULL; +} + +/*! + * Just wrapping the livebox_add_with_size function. + */ +EAPI struct livebox *livebox_add(const char *pkgname, const char *content, const char *cluster, const char *category, double period, ret_cb_t cb, void *data) +{ + return livebox_add_with_size(pkgname, content, cluster, category, period, LB_SIZE_TYPE_UNKNOWN, cb, data); +} + +static gboolean job_execute_cb(void *data) +{ + struct job_item *item; + struct dlist *l; + + l = dlist_nth(s_info.job_list, 0); + if (!l) { + s_info.job_timer = 0; + return FALSE; + } + + item = dlist_data(l); + s_info.job_list = dlist_remove(s_info.job_list, l); + + if (item) { + item->cb(item->handle, item->ret, item->data); + lb_unref(item->handle, 1); + free(item); + } + + return TRUE; +} + +static int job_add(struct livebox *handle, ret_cb_t job_cb, int ret, void *data) +{ + struct job_item *item; + + if (!job_cb) { + ErrPrint("Invalid argument\n"); + return LB_STATUS_ERROR_INVALID; + } + + item = malloc(sizeof(*item)); + if (!item) { + ErrPrint("Heap: %s\n", strerror(errno)); + return LB_STATUS_ERROR_MEMORY; + } + + item->handle = lb_ref(handle); + item->cb = job_cb; + item->data = data; + item->ret = ret; + + s_info.job_list = dlist_append(s_info.job_list, item); + + if (!s_info.job_timer) { + s_info.job_timer = g_timeout_add(1, job_execute_cb, NULL); + if (!s_info.job_timer) { + ErrPrint("Failed to create a job timer\n"); + } + } + + return LB_STATUS_SUCCESS; +} + +static int create_real_instance(struct livebox *handler, ret_cb_t cb, void *data) +{ + struct cb_info *cbinfo; + struct packet *packet; + struct livebox_common *common; + int ret; + + common = handler->common; + + packet = packet_create("new", "dssssdii", + common->timestamp, common->pkgname, common->content, + common->cluster, common->category, + common->lb.period, common->lb.width, common->lb.height); + if (!packet) { + ErrPrint("Failed to create a new packet\n"); + return LB_STATUS_ERROR_FAULT; + } + + cbinfo = create_cb_info(cb, data); + if (!cbinfo) { + ErrPrint("Failed to create a cbinfo\n"); + packet_destroy(packet); + return LB_STATUS_ERROR_MEMORY; + } + + /*! + * \note + * master_rpc_async_request will destroy the packet (decrease the refcnt) + * So be aware the packet object after return from master_rpc_async_request. + */ + ret = master_rpc_async_request(handler, packet, 0, new_ret_cb, cbinfo); + if (ret < 0) { + ErrPrint("Failed to send a new packet\n"); + destroy_cb_info(cbinfo); + return LB_STATUS_ERROR_FAULT; + } + handler->common->request.created = 1; + return LB_STATUS_SUCCESS; +} + +static void create_cb(struct livebox *handle, int ret, void *data) +{ + struct cb_info *cbinfo = data; + + if (cbinfo->cb) { + cbinfo->cb(handle, ret, cbinfo->data); + } + + destroy_cb_info(cbinfo); + + /*! + * \note + * Forcely generate "updated" event + */ + lb_invoke_event_handler(handle, LB_EVENT_LB_UPDATED); +} + +static int create_fake_instance(struct livebox *handler, ret_cb_t cb, void *data) +{ + struct cb_info *cbinfo; + + cbinfo = create_cb_info(cb, data); + if (!cbinfo) { + ErrPrint("Failed to create a cbinfo\n"); + return LB_STATUS_ERROR_MEMORY; + } + + if (job_add(handler, create_cb, LB_STATUS_SUCCESS, cbinfo) != LB_STATUS_SUCCESS) { + destroy_cb_info(cbinfo); + } + + return LB_STATUS_SUCCESS; +} + +struct livebox_common *lb_create_common_handle(struct livebox *handle, const char *pkgname, const char *cluster, const char *category) +{ + struct livebox_common *common; + + common = calloc(1, sizeof(*common)); + if (!common) { + ErrPrint("Heap: %s\n", strerror(errno)); + return NULL; + } + + common->pkgname = strdup(pkgname); + if (!common->pkgname) { + free(common); + return NULL; + } + + common->cluster = strdup(cluster); + if (!common->cluster) { + ErrPrint("Error: %s\n", strerror(errno)); + free(common->pkgname); + free(common); + return NULL; + } + + common->category = strdup(category); + if (!common->category) { + ErrPrint("Error: %s\n", strerror(errno)); + free(common->cluster); + free(common->pkgname); + free(common); + return NULL; + } + + /* Data provider will set this */ + common->lb.type = _LB_TYPE_FILE; + common->pd.type = _PD_TYPE_SCRIPT; + + /* Used for handling the mouse event on a box */ + common->lb.mouse_event = 0; + + /* Cluster infomration is not determined yet */ + common->nr_of_sizes = 0x01; + + common->timestamp = util_timestamp(); + common->is_user = 1; + common->delete_type = LB_DELETE_PERMANENTLY; + common->pd.lock = NULL; + common->pd.lock_fd = -1; + common->lb.lock = NULL; + common->lb.lock_fd = -1; + + common->state = CREATE; + common->visible = LB_SHOW; + + s_info.livebox_common_list = dlist_append(s_info.livebox_common_list, common); + return common; +} + +int lb_destroy_common_handle(struct livebox_common *common) +{ + dlist_remove_data(s_info.livebox_common_list, common); + + common->state = DESTROYED; + + if (common->filename) { + (void)util_unlink(common->filename); + } + + free(common->cluster); + free(common->category); + free(common->id); + free(common->pkgname); + free(common->filename); + free(common->lb.auto_launch); + free(common->alt.icon); + free(common->alt.name); + + if (common->lb.fb) { + fb_destroy(common->lb.fb); + common->lb.fb = NULL; + } + + if (common->pd.fb) { + fb_destroy(common->pd.fb); + common->pd.fb = NULL; + } + + return 0; +} + +int lb_common_ref(struct livebox_common *common, struct livebox *handle) +{ + common->livebox_list = dlist_append(common->livebox_list, handle); + common->refcnt++; + + return common->refcnt; +} + +int lb_common_unref(struct livebox_common *common, struct livebox *handle) +{ + int refcnt; + dlist_remove_data(common->livebox_list, handle); + refcnt = --common->refcnt; + + return refcnt; +} + +static void refresh_for_paused_updating_cb(struct livebox *handle, int ret, void *data) +{ + if (handle->paused_updating == 0) { + DbgPrint("Paused updates are cleared\n"); + return; + } + + DbgPrint("Pending updates are found\n"); + lb_invoke_event_handler(handle, LB_EVENT_LB_UPDATED); +} + +static int lb_set_visibility(struct livebox *handler, enum livebox_visible_state state) +{ + struct packet *packet; + int need_to_add_job = 0; + int ret; + + if (handler->common->visible != LB_SHOW && state == LB_SHOW) { + need_to_add_job = !!handler->paused_updating; + } else if (handler->common->visible == LB_SHOW && state != LB_SHOW) { + struct dlist *l; + struct livebox *item; + + dlist_foreach(handler->common->livebox_list, l, item) { + if (item->visible == LB_SHOW) { + DbgPrint("%s visibility is not changed\n", handler->common->pkgname); + return LB_STATUS_SUCCESS; + } + } + } else if (handler->common->visible == LB_SHOW && state == LB_SHOW && handler->paused_updating) { + if (job_add(handler, refresh_for_paused_updating_cb, LB_STATUS_SUCCESS, NULL) < 0) { + ErrPrint("Unable to add a new job for refreshing box\n"); + } + + return LB_STATUS_SUCCESS; + } else { + /*! + * \brief + * No need to send this to the master + */ + return LB_STATUS_SUCCESS; + } + + packet = packet_create_noack("change,visibility", "ssi", handler->common->pkgname, handler->common->id, (int)state); + if (!packet) { + ErrPrint("Failed to create a packet\n"); + return LB_STATUS_ERROR_FAULT; + } + + ret = master_rpc_request_only(handler, packet); + if (ret == (int)LB_STATUS_SUCCESS) { + DbgPrint("[%s] visibility is changed 0x[%x]\n", handler->common->pkgname, state); + handler->common->visible = state; + + if (need_to_add_job) { + if (job_add(handler, refresh_for_paused_updating_cb, LB_STATUS_SUCCESS, NULL) < 0) { + ErrPrint("Unable to add a new job for refreshing box\n"); + } + } + } + + return ret; +} + +EAPI struct livebox *livebox_add_with_size(const char *pkgname, const char *content, const char *cluster, const char *category, double period, int type, ret_cb_t cb, void *data) +{ + char *lbid; + struct livebox *handler; + int w = 0; + int h = 0; + + if (!pkgname || !cluster || !category) { + ErrPrint("Invalid arguments: pkgname[%p], cluster[%p], category[%p]\n", + pkgname, cluster, category); + return NULL; + } + + lbid = lb_pkgname(pkgname); + if (!lbid) { + ErrPrint("Invalid package: %s\n", pkgname); + return NULL; + } + + if (livebox_service_is_enabled(lbid) == 0) { + DbgPrint("Livebox [%s](%s) is disabled package\n", lbid, pkgname); + free(lbid); + return NULL; + } + + if (type != LB_SIZE_TYPE_UNKNOWN) { + (void)livebox_service_get_size(type, &w, &h); + } + + handler = calloc(1, sizeof(*handler)); + if (!handler) { + ErrPrint("Error: %s\n", strerror(errno)); + free(lbid); + return NULL; + } + + if (!cb) { + cb = default_create_cb; + } + + handler->common = find_sharable_common_handle(lbid, content, w, h, cluster, category); + if (!handler->common) { + handler->common = lb_create_common_handle(handler, lbid, cluster, category); + free(lbid); + if (!handler->common) { + ErrPrint("Failed to find common handle\n"); + free(handler); + return NULL; + } + + if (!content || !strlen(content)) { + char *pc; + /*! + * \note + * I know the content should not be modified. use it temporarly without "const" + */ + pc = livebox_service_content(handler->common->pkgname); + lb_set_content(handler->common, pc); + free(pc); + } else { + lb_set_content(handler->common, content); + } + + lb_set_period(handler->common, period); + lb_set_size(handler->common, w, h); + lb_common_ref(handler->common, handler); + + if (create_real_instance(handler, cb, data) < 0) { + if (lb_common_unref(handler->common, handler) == 0) { + /*! + * Delete common + */ + lb_destroy_common_handle(handler->common); + handler->common = NULL; + } + free(handler); + return NULL; + } + } else { + free(lbid); + + lb_common_ref(handler->common, handler); + + if (handler->common->request.created) { + /*! + * If a box is in creating, wait its result too + */ + handler->cbs.created.cb = cb; + handler->cbs.created.data = data; + } else { + /*! + * or fire the fake created_event + */ + if (create_fake_instance(handler, cb, data) < 0) { + if (lb_common_unref(handler->common, handler) == 0) { + /*! + * Delete common + */ + lb_destroy_common_handle(handler->common); + } + free(handler); + return NULL; + } + } + } + + handler->visible = LB_SHOW; + handler->state = CREATE; + handler = lb_ref(handler); + + if (handler->common->visible != LB_SHOW) { + lb_set_visibility(handler, LB_SHOW); + } + + return handler; +} + +EAPI double livebox_period(struct livebox *handler) +{ + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is not valid\n"); + return 0.0f; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Invalid handle\n"); + return 0.0f; + } + + if (!handler->common->id) { + ErrPrint("Hnalder is not valid\n"); + return 0.0f; + } + + return handler->common->lb.period; +} + +EAPI int livebox_set_period(struct livebox *handler, double period, ret_cb_t cb, void *data) +{ + struct packet *packet; + int ret; + + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Invalid handle\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (handler->common->request.period_changed) { + ErrPrint("Previous request for changing period is not finished\n"); + return LB_STATUS_ERROR_BUSY; + } + + if (!handler->common->is_user) { + ErrPrint("CA Livebox is not able to change the period\n"); + return LB_STATUS_ERROR_PERMISSION; + } + + if (handler->common->lb.period == period) { + DbgPrint("No changes\n"); + return LB_STATUS_ERROR_ALREADY; + } + + packet = packet_create("set_period", "ssd", handler->common->pkgname, handler->common->id, period); + if (!packet) { + ErrPrint("Failed to build a packet %s\n", handler->common->pkgname); + return LB_STATUS_ERROR_FAULT; + } + + if (!cb) { + cb = default_period_changed_cb; + } + + ret = master_rpc_async_request(handler, packet, 0, period_ret_cb, NULL); + if (ret == (int)LB_STATUS_SUCCESS) { + handler->cbs.period_changed.cb = cb; + handler->cbs.period_changed.data = data; + handler->common->request.period_changed = 1; + } + + return ret; +} + +static void lb_update_visibility(struct livebox_common *old_common) +{ + struct dlist *l; + struct livebox *item; + + item = NULL; + dlist_foreach(old_common->livebox_list, l, item) { + if (item->visible == LB_SHOW) { + break; + } + + item = NULL; + } + + if (!item) { + l = dlist_nth(old_common->livebox_list, 0); + item = dlist_data(l); + + if (item) { + lb_set_visibility(item, LB_HIDE_WITH_PAUSE); + } else { + ErrPrint("Unable to get the valid handle from common handler\n"); + } + } else { + lb_set_visibility(item, LB_SHOW); + } +} + +/*! + * \note + * The second parameter should be the "return value", + * But in this case, we will use it for "type of deleting instance". + */ +static void job_del_cb(struct livebox *handle, int type, void *data) +{ + struct cb_info *cbinfo = data; + ret_cb_t cb; + + if (handle->visible == LB_SHOW) { + lb_update_visibility(handle->common); + } + + cb = cbinfo->cb; + data = cbinfo->data; + destroy_cb_info(cbinfo); + + if (handle->common->state != CREATE) { + DbgPrint("[%s] %d\n", handle->common->pkgname, handle->refcnt); + if (cb) { + cb(handle, LB_STATUS_SUCCESS, data); + } + + return; + } + + if (handle->common->refcnt == 1) { + handle->common->delete_type = type; + handle->common->state = DELETE; + + if (!handle->common->id) { + /*! + * \note + * The id is not determined yet. + * It means a user didn't receive created event yet. + * Then just stop to delete procedure from here. + * Because the "created" event handle will release this. + * By the way, if the user adds any callback for getting return status of this, + * call it at here. + */ + if (cb) { + cb(handle, LB_STATUS_SUCCESS, data); + } + } + + DbgPrint("Send delete request\n"); + lb_send_delete(handle, type, cb, data); + } else { + if (cb) { + cb(handle, LB_STATUS_SUCCESS, data); + } + + DbgPrint("Before unref: %d\n", handle->common->refcnt); + lb_unref(handle, 1); + } +} + +EAPI int livebox_del_NEW(struct livebox *handler, int type, ret_cb_t cb, void *data) +{ + struct cb_info *cbinfo; + + if (!handler) { + ErrPrint("Handler is NIL\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (handler->state != CREATE) { + ErrPrint("Handler is already deleted\n"); + return LB_STATUS_ERROR_INVALID; + } + + handler->state = DELETE; + + cbinfo = create_cb_info(cb, data); + if (!cbinfo) { + ErrPrint("Failed to create a cbinfo\n"); + return LB_STATUS_ERROR_MEMORY; + } + + if (job_add(handler, job_del_cb, type, cbinfo) != LB_STATUS_SUCCESS) { + ErrPrint("Failed to add a new job\n"); + destroy_cb_info(cbinfo); + return LB_STATUS_ERROR_FAULT; + } + + return LB_STATUS_SUCCESS; +} + +EAPI int livebox_del(struct livebox *handler, ret_cb_t cb, void *data) +{ + return livebox_del_NEW(handler, LB_DELETE_PERMANENTLY, cb, data); +} + +EAPI int livebox_set_fault_handler(int (*cb)(enum livebox_fault_type, const char *, const char *, const char *, void *), void *data) +{ + struct fault_info *info; + + if (!cb) { + return LB_STATUS_ERROR_INVALID; + } + + info = malloc(sizeof(*info)); + if (!info) { + ErrPrint("Heap: %s\n", strerror(errno)); + return LB_STATUS_ERROR_MEMORY; + } + + info->handler = cb; + info->user_data = data; + info->is_deleted = 0; + + s_info.fault_list = dlist_append(s_info.fault_list, info); + return LB_STATUS_SUCCESS; +} + +EAPI void *livebox_unset_fault_handler(int (*cb)(enum livebox_fault_type, const char *, const char *, const char *, void *)) +{ + struct fault_info *info; + struct dlist *l; + + dlist_foreach(s_info.fault_list, l, info) { + if (info->handler == cb) { + void *data; + + data = info->user_data; + + if (s_info.fault_state == INFO_STATE_CALLBACK_IN_PROCESSING) { + info->is_deleted = 1; + } else { + s_info.fault_list = dlist_remove(s_info.fault_list, l); + free(info); + } + + return data; + } + } + + return NULL; +} + +EAPI int livebox_set_event_handler(int (*cb)(struct livebox *, enum livebox_event_type, void *), void *data) +{ + struct event_info *info; + + if (!cb) { + ErrPrint("Invalid argument cb is nil\n"); + return LB_STATUS_ERROR_INVALID; + } + + info = malloc(sizeof(*info)); + if (!info) { + ErrPrint("Heap: %s\n", strerror(errno)); + return LB_STATUS_ERROR_MEMORY; + } + + info->handler = cb; + info->user_data = data; + info->is_deleted = 0; + + s_info.event_list = dlist_append(s_info.event_list, info); + return LB_STATUS_SUCCESS; +} + +EAPI void *livebox_unset_event_handler(int (*cb)(struct livebox *, enum livebox_event_type, void *)) +{ + struct event_info *info; + struct dlist *l; + + dlist_foreach(s_info.event_list, l, info) { + if (info->handler == cb) { + void *data; + + data = info->user_data; + + if (s_info.event_state == INFO_STATE_CALLBACK_IN_PROCESSING) { + info->is_deleted = 1; + } else { + s_info.event_list = dlist_remove(s_info.event_list, l); + free(info); + } + + return data; + } + } + + return NULL; +} + +EAPI int livebox_set_update_mode(struct livebox *handler, int active_update, ret_cb_t cb, void *data) +{ + struct packet *packet; + int ret; + + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is Invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is Invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Handler is Invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (handler->common->request.update_mode) { + ErrPrint("Previous update_mode cb is not finished yet\n"); + return LB_STATUS_ERROR_BUSY; + } + + if (handler->common->is_active_update == active_update) { + return LB_STATUS_ERROR_ALREADY; + } + + if (!handler->common->is_user) { + return LB_STATUS_ERROR_PERMISSION; + } + + packet = packet_create("update_mode", "ssi", handler->common->pkgname, handler->common->id, active_update); + if (!packet) { + return LB_STATUS_ERROR_FAULT; + } + + if (!cb) { + cb = default_update_mode_cb; + } + + ret = master_rpc_async_request(handler, packet, 0, update_mode_cb, NULL); + if (ret == (int)LB_STATUS_SUCCESS) { + handler->cbs.update_mode.cb = cb; + handler->cbs.update_mode.data = data; + handler->common->request.update_mode = 1; + } + + return ret; +} + +EAPI int livebox_is_active_update(struct livebox *handler) +{ + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is Invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is Invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + return LB_STATUS_ERROR_INVALID; + } + + return handler->common->is_active_update; +} + +static void resize_job_cb(struct livebox *handler, int ret, void *data) +{ + struct cb_info *info = data; + + if (info->cb) { + info->cb(handler, ret, info->data); + } + + free(info); + + /*! + * \note + * Forcely update the box + */ + lb_invoke_event_handler(handler, LB_EVENT_LB_UPDATED); +} + +EAPI int livebox_resize(struct livebox *handler, int type, ret_cb_t cb, void *data) +{ + struct livebox_common *common; + int w; + int h; + int ret; + + /*! + * \TODO + * If this handle is host instance or link instance, + * Create a new instance or find another linkable instance. + */ + + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Invalid handle\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + /*! + * \note + * resize operation should be separated by each handler. + * If a handler is resizing, the other handler can request resize too. + * So we should not use the common->request.size_changed flag. + */ + if (handler->cbs.size_changed.cb) { + ErrPrint("Previous resize request is not finished yet\n"); + return LB_STATUS_ERROR_BUSY; + } + + if (livebox_service_get_size(type, &w, &h) != 0) { + ErrPrint("Invalid size type\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (handler->common->lb.width == w && handler->common->lb.height == h) { + DbgPrint("No changes\n"); + return LB_STATUS_ERROR_ALREADY; + } + + if (!handler->common->is_user) { + ErrPrint("CA Livebox is not able to be resized\n"); + return LB_STATUS_ERROR_PERMISSION; + } + + if (handler->common->refcnt <= 1) { + struct packet *packet; + + /* Only 1 instance */ + packet = packet_create("resize", "ssii", handler->common->pkgname, handler->common->id, w, h); + if (!packet) { + ErrPrint("Failed to build param\n"); + return LB_STATUS_ERROR_FAULT; + } + + if (!cb) { + cb = default_lb_size_changed_cb; + } + + ret = master_rpc_async_request(handler, packet, 0, resize_cb, NULL); + if (ret == (int)LB_STATUS_SUCCESS) { + handler->cbs.size_changed.cb = cb; + handler->cbs.size_changed.data = data; + handler->common->request.size_changed = 1; + } + } else { + common = find_sharable_common_handle(handler->common->pkgname, handler->common->content, w, h, handler->common->cluster, handler->common->category); + if (!common) { + struct livebox_common *old_common; + /*! + * \note + * If the common handler is in resizing, + * if user tries to resize a hander, then simply create new one even if the requested size is same with this. + + if (handler->common->request.size_changed) { + } + + */ + + old_common = handler->common; + + common = lb_create_common_handle(handler, old_common->pkgname, old_common->cluster, old_common->category); + if (!common) { + ErrPrint("Failed to create common handle\n"); + return LB_STATUS_ERROR_FAULT; + } + + lb_set_size(common, w, h); + lb_set_content(common, old_common->content); + lb_set_period(common, old_common->lb.period); + + /*! + * \note + * Disconnecting from old one. + */ + if (lb_common_unref(old_common, handler) == 0) { + /*! + * \note + * Impossible + */ + ErrPrint("Common has no associated handler\n"); + } + + lb_common_ref(common, handler); + + /*! + * Connect to a new one + */ + handler->common = common; + + /*! + * \TODO + * Need to care, if it fails to create a common handle, + * the resize operation will be failed. + * in that case, we should reuse the old common handle + */ + ret = create_real_instance(handler, cb, data); + if (ret < 0) { + lb_common_unref(common, handler); + lb_destroy_common_handle(common); + + lb_common_ref(old_common, handler); + handler->common = old_common; + } else { + /*! + * In this case, we should update visibility of old_common's liveboxes + */ + if (handler->visible == LB_SHOW) { + lb_update_visibility(old_common); + } + } + } else { + struct cb_info *cbinfo; + + cbinfo = create_cb_info(cb, data); + if (!cbinfo) { + ErrPrint("Failed to create a cbinfo\n"); + ret = LB_STATUS_ERROR_MEMORY; + } else { + ret = job_add(handler, resize_job_cb, LB_STATUS_SUCCESS, cbinfo); + if (ret == (int)LB_STATUS_SUCCESS) { + struct livebox_common *old_common; + + old_common = handler->common; + + if (lb_common_unref(handler->common, handler) == 0) { + ErrPrint("Old common has no associated handler\n"); + } + + lb_common_ref(common, handler); + handler->common = common; + + if (handler->visible == LB_SHOW) { + lb_update_visibility(old_common); /* To update visibility: Show --> Paused */ + lb_update_visibility(common); /* To update visibility: Paused --> Show */ + } + } else { + destroy_cb_info(cbinfo); + } + } + } + } + + return ret; +} + +EAPI int livebox_click(struct livebox *handler, double x, double y) +{ + struct packet *packet; + double timestamp; + int ret; + + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (handler->common->lb.auto_launch) { + if (s_info.launch.handler) { + ret = s_info.launch.handler(handler, handler->common->lb.auto_launch, s_info.launch.data); + if (ret < 0) { + ErrPrint("launch handler app %s (%d)\n", handler->common->lb.auto_launch, ret); + } + } + } + + timestamp = util_timestamp(); + DbgPrint("CLICKED: %lf\n", timestamp); + + packet = packet_create_noack("clicked", "sssddd", handler->common->pkgname, handler->common->id, "clicked", timestamp, x, y); + if (!packet) { + ErrPrint("Failed to build param\n"); + return LB_STATUS_ERROR_FAULT; + } + + ret = master_rpc_request_only(handler, packet); + + if (!handler->common->lb.mouse_event && (handler->common->lb.type == _LB_TYPE_BUFFER || handler->common->lb.type == _LB_TYPE_SCRIPT)) { + int ret; /* Shadow variable */ + ret = send_mouse_event(handler, "lb_mouse_down", x * handler->common->lb.width, y * handler->common->lb.height); + if (ret < 0) { + ErrPrint("Failed to send Down: %d\n", ret); + } + + ret = send_mouse_event(handler, "lb_mouse_move", x * handler->common->lb.width, y * handler->common->lb.height); + if (ret < 0) { + ErrPrint("Failed to send Move: %d\n", ret); + } + + ret = send_mouse_event(handler, "lb_mouse_up", x * handler->common->lb.width, y * handler->common->lb.height); + if (ret < 0) { + ErrPrint("Failed to send Up: %d\n", ret); + } + } + + return ret; +} + +EAPI int livebox_has_pd(struct livebox *handler) +{ + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + return !!handler->common->pd.fb; +} + +EAPI int livebox_pd_is_created(struct livebox *handler) +{ + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->pd.fb || !handler->common->id) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + return handler->common->is_pd_created; +} + +EAPI int livebox_create_pd(struct livebox *handler, ret_cb_t cb, void *data) +{ + return livebox_create_pd_with_position(handler, -1.0, -1.0, cb, data); +} + +static void turn_off_pd_destroyed_flag_cb(struct livebox *handler, int ret, void *data) +{ + if (handler->common->request.pd_destroyed) { + ret_cb_t cb; + void *data; + + DbgPrint("pd_destroyed request is canceled\n"); + handler->common->request.pd_destroyed = 0; + cb = handler->cbs.pd_destroyed.cb; + data = handler->cbs.pd_destroyed.data; + handler->cbs.pd_destroyed.cb = NULL; + handler->cbs.pd_destroyed.data = NULL; + + if (cb) { + cb(handler, ret, data); + } + } +} + +EAPI int livebox_create_pd_with_position(struct livebox *handler, double x, double y, ret_cb_t cb, void *data) +{ + struct packet *packet; + int ret; + + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->pd.fb || !handler->common->id) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + /*! + * \note + * Only one handler can have a PD + */ + if (handler->common->is_pd_created) { + DbgPrint("PD is already created\n"); + return LB_STATUS_SUCCESS; + } + + if (handler->common->request.pd_created) { + ErrPrint("Previous request is not completed yet\n"); + return LB_STATUS_ERROR_BUSY; + } + + /*! + * \note + * Turn off the pd_destroyed request flag + */ + if (handler->common->request.pd_destroyed) { + if (job_add(handler, turn_off_pd_destroyed_flag_cb, LB_STATUS_ERROR_CANCEL, NULL) < 0) { + ErrPrint("Failed to add pd_destroyed job\n"); + } + } + + packet = packet_create("create_pd", "ssdd", handler->common->pkgname, handler->common->id, x, y); + if (!packet) { + ErrPrint("Failed to build param\n"); + return LB_STATUS_ERROR_FAULT; + } + + if (!cb) { + cb = default_pd_created_cb; + } + + DbgPrint("PERF_DBOX\n"); + ret = master_rpc_async_request(handler, packet, 0, pd_create_cb, NULL); + if (ret == (int)LB_STATUS_SUCCESS) { + handler->cbs.pd_created.cb = cb; + handler->cbs.pd_created.data = data; + handler->common->request.pd_created = 1; + } + + return ret; +} + +EAPI int livebox_move_pd(struct livebox *handler, double x, double y) +{ + struct packet *packet; + + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->pd.fb || !handler->common->id) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->is_pd_created) { + ErrPrint("PD is not created\n"); + return LB_STATUS_ERROR_INVALID; + } + + packet = packet_create_noack("pd_move", "ssdd", handler->common->pkgname, handler->common->id, x, y); + if (!packet) { + ErrPrint("Failed to build param\n"); + return LB_STATUS_ERROR_FAULT; + } + + return master_rpc_request_only(handler, packet); +} + +EAPI int livebox_activate(const char *pkgname, ret_cb_t cb, void *data) +{ + struct packet *packet; + struct cb_info *cbinfo; + int ret; + + if (!pkgname) { + return LB_STATUS_ERROR_INVALID; + } + + packet = packet_create("activate_package", "s", pkgname); + if (!packet) { + ErrPrint("Failed to build a param\n"); + return LB_STATUS_ERROR_FAULT; + } + + cbinfo = create_cb_info(cb, data); + if (!cbinfo) { + ErrPrint("Unable to create cbinfo\n"); + packet_destroy(packet); + return LB_STATUS_ERROR_FAULT; + } + + ret = master_rpc_async_request(NULL, packet, 0, activated_cb, cbinfo); + if (ret < 0) { + destroy_cb_info(cbinfo); + } + + return ret; +} + +static void turn_off_pd_created_flag_cb(struct livebox *handler, int ret, void *data) +{ + if (handler->common->request.pd_created) { + ret_cb_t cb; + void *data; + + DbgPrint("pd_created request is canceled\n"); + handler->common->request.pd_created = 0; + cb = handler->cbs.pd_created.cb; + data = handler->cbs.pd_created.data; + handler->cbs.pd_created.cb = NULL; + handler->cbs.pd_created.data = NULL; + + if (cb) { + cb(handler, ret, data); + } + } +} + +EAPI int livebox_destroy_pd(struct livebox *handler, ret_cb_t cb, void *data) +{ + struct packet *packet; + struct cb_info *cbinfo; + int ret; + + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->pd.fb || !handler->common->id) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + /*! + * \FIXME + * Replace the callback check code. + * Use the flag instead of callback. + * the flag should be in the ADT "common" + */ + if (!handler->common->is_pd_created && !handler->common->request.pd_created) { + ErrPrint("PD is not created\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (handler->common->request.pd_destroyed) { + ErrPrint("PD destroy request is already sent\n"); + return LB_STATUS_ERROR_ALREADY; + } + + /*! + * \note + * Disable the pd_created request flag + */ + if (handler->common->request.pd_created) { + if (job_add(handler, turn_off_pd_created_flag_cb, LB_STATUS_ERROR_CANCEL, NULL) < 0) { + ErrPrint("Failed to add a new job\n"); + } + } + + DbgPrint("[%s]\n", handler->common->pkgname); + + packet = packet_create("destroy_pd", "ss", handler->common->pkgname, handler->common->id); + if (!packet) { + ErrPrint("Failed to build a param\n"); + return LB_STATUS_ERROR_FAULT; + } + + if (!cb) { + cb = default_pd_destroyed_cb; + } + + cbinfo = create_cb_info(cb, data); + if (!cbinfo) { + packet_destroy(packet); + return LB_STATUS_ERROR_FAULT; + } + + ret = master_rpc_async_request(handler, packet, 0, pd_destroy_cb, cbinfo); + if (ret < 0) { + destroy_cb_info(cbinfo); + } else { + handler->common->request.pd_destroyed = 1; + } + + return ret; +} + +EAPI int livebox_access_event(struct livebox *handler, enum access_event_type type, double x, double y, ret_cb_t cb, void *data) +{ + int w = 1; + int h = 1; + char cmd[32] = { '\0', }; + char *ptr = cmd; + int ret; + + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (handler->common->request.access_event) { + ErrPrint("Previous access event is not yet done\n"); + return LB_STATUS_ERROR_BUSY; + } + + if (type & ACCESS_EVENT_PD_MASK) { + if (!handler->common->is_pd_created) { + ErrPrint("PD is not created\n"); + return LB_STATUS_ERROR_INVALID; + } + *ptr++ = 'p'; + *ptr++ = 'd'; + w = handler->common->pd.width; + h = handler->common->pd.height; + } else if (type & ACCESS_EVENT_LB_MASK) { + *ptr++ = 'l'; + *ptr++ = 'b'; + w = handler->common->lb.width; + h = handler->common->lb.height; + } else { + ErrPrint("Invalid event type\n"); + return LB_STATUS_ERROR_INVALID; + } + + switch (type & ~ACCESS_EVENT_PD_MASK) { + case ACCESS_EVENT_HIGHLIGHT: + strcpy(ptr, "_access_hl"); + break; + case ACCESS_EVENT_HIGHLIGHT_NEXT: + strcpy(ptr, "_access_hl_next"); + break; + case ACCESS_EVENT_HIGHLIGHT_PREV: + strcpy(ptr, "_access_hl_prev"); + break; + case ACCESS_EVENT_ACTIVATE: + strcpy(ptr, "_access_activate"); + break; + case ACCESS_EVENT_ACTION_DOWN: + strcpy(ptr, "_access_action_down"); + break; + case ACCESS_EVENT_ACTION_UP: + strcpy(ptr, "_access_action_up"); + break; + case ACCESS_EVENT_UNHIGHLIGHT: + strcpy(ptr, "_access_unhighlight"); + break; + case ACCESS_EVENT_SCROLL_DOWN: + strcpy(ptr, "_access_scroll_down"); + break; + case ACCESS_EVENT_SCROLL_MOVE: + strcpy(ptr, "_access_scroll_move"); + break; + case ACCESS_EVENT_SCROLL_UP: + strcpy(ptr, "_access_scroll_up"); + break; + default: + return LB_STATUS_ERROR_INVALID; + } + + if (!cb) { + cb = default_access_event_cb; + } + + ret = send_access_event(handler, cmd, x * w, y * h); + if (ret == (int)LB_STATUS_SUCCESS) { + handler->cbs.access_event.cb = cb; + handler->cbs.access_event.data = data; + handler->common->request.access_event = 1; + } + + return ret; +} + +EAPI int livebox_content_event(struct livebox *handler, enum content_event_type type, double x, double y) +{ + return livebox_mouse_event(handler, type, x, y); +} + +EAPI int livebox_mouse_event(struct livebox *handler, enum content_event_type type, double x, double y) +{ + int w = 1; + int h = 1; + char cmd[32] = { '\0', }; + char *ptr = cmd; + + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!(type & CONTENT_EVENT_MOUSE_MASK)) { + ErrPrint("Invalid content event is used\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (type & CONTENT_EVENT_PD_MASK) { + int flag = 1; + + if (!handler->common->is_pd_created) { + ErrPrint("PD is not created\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->pd.fb) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (type & CONTENT_EVENT_MOUSE_MOVE) { + if (fabs(x - handler->common->pd.x) < conf_event_filter() && fabs(y - handler->common->pd.y) < conf_event_filter()) { + return LB_STATUS_ERROR_BUSY; + } + } else if (type & CONTENT_EVENT_MOUSE_SET) { + flag = 0; + } + + if (flag) { + w = handler->common->pd.width; + h = handler->common->pd.height; + handler->common->pd.x = x; + handler->common->pd.y = y; + } + *ptr++ = 'p'; + *ptr++ = 'd'; + } else if (type & CONTENT_EVENT_LB_MASK) { + int flag = 1; + + if (!handler->common->lb.mouse_event) { + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->lb.fb) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (type & CONTENT_EVENT_MOUSE_MOVE) { + if (fabs(x - handler->common->lb.x) < conf_event_filter() && fabs(y - handler->common->lb.y) < conf_event_filter()) { + return LB_STATUS_ERROR_BUSY; + } + } else if (type & CONTENT_EVENT_MOUSE_SET) { + flag = 0; + } + + if (flag) { + w = handler->common->lb.width; + h = handler->common->lb.height; + handler->common->lb.x = x; + handler->common->lb.y = y; + } + *ptr++ = 'l'; + *ptr++ = 'b'; + } else { + ErrPrint("Invalid event type\n"); + return LB_STATUS_ERROR_INVALID; + } + + /*! + * Must be shorter than 29 bytes. + */ + switch ((type & ~(CONTENT_EVENT_PD_MASK | CONTENT_EVENT_LB_MASK))) { + case CONTENT_EVENT_MOUSE_ENTER | CONTENT_EVENT_MOUSE_MASK: + strcpy(ptr, "_mouse_enter"); + break; + case CONTENT_EVENT_MOUSE_LEAVE | CONTENT_EVENT_MOUSE_MASK: + strcpy(ptr, "_mouse_leave"); + break; + case CONTENT_EVENT_MOUSE_UP | CONTENT_EVENT_MOUSE_MASK: + strcpy(ptr, "_mouse_up"); + break; + case CONTENT_EVENT_MOUSE_DOWN | CONTENT_EVENT_MOUSE_MASK: + strcpy(ptr, "_mouse_down"); + break; + case CONTENT_EVENT_MOUSE_MOVE | CONTENT_EVENT_MOUSE_MASK: + strcpy(ptr, "_mouse_move"); + break; + case CONTENT_EVENT_MOUSE_SET | CONTENT_EVENT_MOUSE_MASK: + strcpy(ptr, "_mouse_set"); + break; + case CONTENT_EVENT_MOUSE_UNSET | CONTENT_EVENT_MOUSE_MASK: + strcpy(ptr, "_mouse_unset"); + break; + case CONTENT_EVENT_ON_SCROLL | CONTENT_EVENT_MOUSE_MASK: + strcpy(ptr, "_mouse_on_scroll"); + break; + case CONTENT_EVENT_ON_HOLD | CONTENT_EVENT_MOUSE_MASK: + strcpy(ptr, "_mouse_on_hold"); + break; + case CONTENT_EVENT_OFF_SCROLL | CONTENT_EVENT_MOUSE_MASK: + strcpy(ptr, "_mouse_off_scroll"); + break; + case CONTENT_EVENT_OFF_HOLD | CONTENT_EVENT_MOUSE_MASK: + strcpy(ptr, "_mouse_off_hold"); + break; + default: + ErrPrint("Invalid event type\n"); + return LB_STATUS_ERROR_INVALID; + } + + return send_mouse_event(handler, cmd, x * w, y * h); +} + +EAPI int livebox_key_event(struct livebox *handler, enum content_event_type type, unsigned int keycode, ret_cb_t cb, void *data) +{ + char cmd[32] = { '\0', }; + char *ptr = cmd; + int ret; + + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!(type & CONTENT_EVENT_KEY_MASK)) { + ErrPrint("Invalid key event is used\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (handler->common->request.key_event) { + ErrPrint("Previous key event is not completed yet\n"); + return LB_STATUS_ERROR_BUSY; + } + + if (type & CONTENT_EVENT_PD_MASK) { + if (!handler->common->is_pd_created) { + ErrPrint("PD is not created\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->pd.fb) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (type & CONTENT_EVENT_KEY_DOWN) { + /*! + * \TODO + * filtering the reproduced events if it is too fast + */ + } else if (type & CONTENT_EVENT_KEY_SET) { + /*! + * \TODO + * What can I do for this case? + */ + } + + *ptr++ = 'p'; + *ptr++ = 'd'; + } else if (type & CONTENT_EVENT_LB_MASK) { + if (!handler->common->lb.mouse_event) { + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->lb.fb) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (type & CONTENT_EVENT_KEY_DOWN) { + /*! + * \TODO + * filtering the reproduced events if it is too fast + */ + } else if (type & CONTENT_EVENT_KEY_SET) { + /*! + * What can I do for this case? + */ + } + + *ptr++ = 'l'; + *ptr++ = 'b'; + } else { + ErrPrint("Invalid event type\n"); + return LB_STATUS_ERROR_INVALID; + } + + /*! + * Must be short than 29 bytes. + */ + switch ((type & ~(CONTENT_EVENT_PD_MASK | CONTENT_EVENT_LB_MASK))) { + case CONTENT_EVENT_KEY_FOCUS_IN | CONTENT_EVENT_KEY_MASK: + strcpy(ptr, "_key_focus_in"); + break; + case CONTENT_EVENT_KEY_FOCUS_OUT | CONTENT_EVENT_KEY_MASK: + strcpy(ptr, "_key_focus_out"); + break; + case CONTENT_EVENT_KEY_UP | CONTENT_EVENT_KEY_MASK: + strcpy(ptr, "_key_up"); + break; + case CONTENT_EVENT_KEY_DOWN | CONTENT_EVENT_KEY_MASK: + strcpy(ptr, "_key_down"); + break; + case CONTENT_EVENT_KEY_SET | CONTENT_EVENT_KEY_MASK: + strcpy(ptr, "_key_set"); + break; + case CONTENT_EVENT_KEY_UNSET | CONTENT_EVENT_KEY_MASK: + strcpy(ptr, "_key_unset"); + break; + default: + ErrPrint("Invalid event type\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!cb) { + cb = default_key_event_cb; + } + + ret = send_key_event(handler, cmd, keycode); + if (ret == (int)LB_STATUS_SUCCESS) { + handler->cbs.key_event.cb = cb; + handler->cbs.key_event.data = data; + handler->common->request.key_event = 1; + } + + return ret; +} + +EAPI const char *livebox_filename(struct livebox *handler) +{ + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return NULL; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return NULL; + } + + if (!handler->common->id) { + ErrPrint("Handler is not valid\n"); + return NULL; + } + + if (handler->common->filename) { + return handler->common->filename; + } + + /* Oooops */ + return util_uri_to_path(handler->common->id); +} + +EAPI int livebox_get_pdsize(struct livebox *handler, int *w, int *h) +{ + int _w; + int _h; + + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!w) { + w = &_w; + } + if (!h) { + h = &_h; + } + + if (!handler->common->is_pd_created) { + *w = handler->common->pd.default_width; + *h = handler->common->pd.default_height; + } else { + *w = handler->common->pd.width; + *h = handler->common->pd.height; + } + + return LB_STATUS_SUCCESS; +} + +EAPI int livebox_size(struct livebox *handler) +{ + int w; + int h; + + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + w = handler->common->lb.width; + h = handler->common->lb.height; + + switch (handler->common->lb.type) { + case _LB_TYPE_BUFFER: + case _LB_TYPE_SCRIPT: + if (!fb_is_created(handler->common->lb.fb)) { + w = 0; + h = 0; + } + break; + default: + break; + } + + return livebox_service_size_type(w, h); +} + +EAPI int livebox_set_group(struct livebox *handler, const char *cluster, const char *category, ret_cb_t cb, void *data) +{ + struct packet *packet; + int ret; + + if (!handler) { + ErrPrint("Handler is NIL\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!cluster || !category || handler->state != CREATE) { + ErrPrint("Invalid argument\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Invalid argument\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Invalid argument\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (handler->common->request.group_changed) { + ErrPrint("Previous group changing request is not finished yet\n"); + return LB_STATUS_ERROR_BUSY; + } + + if (!handler->common->is_user) { + ErrPrint("CA Livebox is not able to change the group\n"); + return LB_STATUS_ERROR_PERMISSION; + } + + if (!strcmp(handler->common->cluster, cluster) && !strcmp(handler->common->category, category)) { + DbgPrint("No changes\n"); + return LB_STATUS_ERROR_ALREADY; + } + + packet = packet_create("change_group", "ssss", handler->common->pkgname, handler->common->id, cluster, category); + if (!packet) { + ErrPrint("Failed to build a param\n"); + return LB_STATUS_ERROR_FAULT; + } + + if (!cb) { + cb = default_group_changed_cb; + } + + ret = master_rpc_async_request(handler, packet, 0, set_group_ret_cb, NULL); + if (ret == (int)LB_STATUS_SUCCESS) { + handler->cbs.group_changed.cb = cb; + handler->cbs.group_changed.data = data; + handler->common->request.group_changed = 1; + } + + return ret; +} + +EAPI int livebox_get_group(struct livebox *handler, const char **cluster, const char **category) +{ + if (!handler) { + ErrPrint("Handler is NIL\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!cluster || !category || handler->state != CREATE) { + ErrPrint("Invalid argument\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Invalid argument\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Invalid argument\n"); + return LB_STATUS_ERROR_INVALID; + } + + *cluster = handler->common->cluster; + *category = handler->common->category; + return LB_STATUS_SUCCESS; +} + +EAPI int livebox_get_supported_sizes(struct livebox *handler, int *cnt, int *size_list) +{ + register int i; + register int j; + + if (!handler || !size_list) { + ErrPrint("Invalid argument, handler(%p), size_list(%p)\n", handler, size_list); + return LB_STATUS_ERROR_INVALID; + } + + if (!cnt || handler->state != CREATE) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + for (j = i = 0; i < NR_OF_SIZE_LIST; i++) { + if (handler->common->lb.size_list & (0x01 << i)) { + if (j == *cnt) { + break; + } + + size_list[j++] = (0x01 << i); + } + } + + *cnt = j; + return LB_STATUS_SUCCESS; +} + +EAPI const char *livebox_pkgname(struct livebox *handler) +{ + if (!handler) { + ErrPrint("Handler is NIL\n"); + return NULL; + } + + if (handler->state != CREATE) { + ErrPrint("Handler is not valid\n"); + return NULL; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is not valid\n"); + return NULL; + } + + return handler->common->pkgname; +} + +EAPI double livebox_priority(struct livebox *handler) +{ + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return -1.0f; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return -1.0f; + } + + if (!handler->common->id) { + ErrPrint("Handler is not valid (%p)\n", handler); + return -1.0f; + } + + return handler->common->lb.priority; +} + +EAPI int livebox_delete_cluster(const char *cluster, ret_cb_t cb, void *data) +{ + struct packet *packet; + struct cb_info *cbinfo; + int ret; + + packet = packet_create("delete_cluster", "s", cluster); + if (!packet) { + ErrPrint("Failed to build a param\n"); + return LB_STATUS_ERROR_FAULT; + } + + cbinfo = create_cb_info(cb, data); + if (!cbinfo) { + packet_destroy(packet); + return LB_STATUS_ERROR_FAULT; + } + + ret = master_rpc_async_request(NULL, packet, 0, delete_cluster_cb, cbinfo); + if (ret < 0) { + destroy_cb_info(cbinfo); + } + + return ret; +} + +EAPI int livebox_delete_category(const char *cluster, const char *category, ret_cb_t cb, void *data) +{ + struct packet *packet; + struct cb_info *cbinfo; + int ret; + + packet = packet_create("delete_category", "ss", cluster, category); + if (!packet) { + ErrPrint("Failed to build a param\n"); + return LB_STATUS_ERROR_FAULT; + } + + cbinfo = create_cb_info(cb, data); + if (!cbinfo) { + packet_destroy(packet); + return LB_STATUS_ERROR_FAULT; + } + + ret = master_rpc_async_request(NULL, packet, 0, delete_category_cb, cbinfo); + if (ret < 0) { + destroy_cb_info(cbinfo); + } + + return ret; +} + +EAPI enum livebox_lb_type livebox_lb_type(struct livebox *handler) +{ + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_TYPE_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_TYPE_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Handler is not valid\n"); + return LB_TYPE_INVALID; + } + + switch (handler->common->lb.type) { + case _LB_TYPE_FILE: + return LB_TYPE_IMAGE; + case _LB_TYPE_BUFFER: + case _LB_TYPE_SCRIPT: + { + const char *id; + id = fb_id(handler->common->lb.fb); + if (id && !strncasecmp(id, SCHEMA_PIXMAP, strlen(SCHEMA_PIXMAP))) { + return LB_TYPE_PIXMAP; + } + } + return LB_TYPE_BUFFER; + case _LB_TYPE_TEXT: + return LB_TYPE_TEXT; + default: + break; + } + + return LB_TYPE_INVALID; +} + +EAPI enum livebox_pd_type livebox_pd_type(struct livebox *handler) +{ + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return PD_TYPE_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return PD_TYPE_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Handler is not valid\n"); + return PD_TYPE_INVALID; + } + + switch (handler->common->pd.type) { + case _PD_TYPE_TEXT: + return PD_TYPE_TEXT; + case _PD_TYPE_BUFFER: + case _PD_TYPE_SCRIPT: + { + const char *id; + id = fb_id(handler->common->pd.fb); + if (id && !strncasecmp(id, SCHEMA_PIXMAP, strlen(SCHEMA_PIXMAP))) { + return PD_TYPE_PIXMAP; + } + } + return PD_TYPE_BUFFER; + default: + break; + } + + return PD_TYPE_INVALID; +} + +EAPI int livebox_set_pd_text_handler(struct livebox *handler, struct livebox_script_operators *ops) +{ + if (!handler) { + ErrPrint("Handler is NIL\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (handler->state != CREATE) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + memcpy(&handler->cbs.pd_ops, ops, sizeof(*ops)); + return LB_STATUS_SUCCESS; +} + +EAPI int livebox_set_text_handler(struct livebox *handler, struct livebox_script_operators *ops) +{ + if (!handler) { + ErrPrint("Handler is NIL\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (handler->state != CREATE) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + memcpy(&handler->cbs.lb_ops, ops, sizeof(*ops)); + return LB_STATUS_SUCCESS; +} + +EAPI int livebox_acquire_lb_pixmap(struct livebox *handler, ret_cb_t cb, void *data) +{ + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Invalid handle\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (handler->common->lb.type != _LB_TYPE_SCRIPT && handler->common->lb.type != _LB_TYPE_BUFFER) { + ErrPrint("Handler is not valid type\n"); + return LB_STATUS_ERROR_INVALID; + } + + return lb_acquire_lb_pixmap(handler, cb, data); +} + +/*! + * \note + * Do not check the state of handler and common-handler. + * If this function is used in the deleted callback, + * the handler and common-handler's state would be DELETE + * if this function check the state of handles, + * user cannot release the pixmap. + */ +EAPI int livebox_release_lb_pixmap(struct livebox *handler, int pixmap) +{ + struct packet *packet; + const char *pkgname; + const char *id; + + if (pixmap == 0 /* || handler->state != CREATE */ ) { + ErrPrint("Handler is invalid [%d]\n", pixmap); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler) { + /*! + * \note + * Even though the handler is NULL, we should send the release request to the master. + * Because the pixmap resource can be released after the handler is destroyed. + * Pixmap resource is used by client. and it cannot be guaranteed to release pixmap. + * In some cases, the pixmap can be released after the handler is deleted. + * + * Its implementation is up to the viewer app. + * But we cannot force it to use only with valid handler. + */ + DbgPrint("Using NULL handler\n"); + pkgname = NULL; + id = NULL; + /*! + * \note + * Master will try to find the buffer handler using given pixmap. if the pkgname and id is not valid. + */ + } else { + if (!handler->common /* || handler->common->state != CREATE */) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Invalid handle\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (handler->common->lb.type != _LB_TYPE_SCRIPT && handler->common->lb.type != _LB_TYPE_BUFFER) { + ErrPrint("Handler is not valid type\n"); + return LB_STATUS_ERROR_INVALID; + } + + pkgname = handler->common->pkgname; + id = handler->common->id; + } + + packet = packet_create_noack("lb_release_pixmap", "ssi", pkgname, id, pixmap); + if (!packet) { + ErrPrint("Failed to build a param\n"); + return LB_STATUS_ERROR_INVALID; + } + + return master_rpc_request_only(handler, packet); +} + +EAPI int livebox_acquire_pd_pixmap(struct livebox *handler, ret_cb_t cb, void *data) +{ + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Invalid handle\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (handler->common->pd.type != _PD_TYPE_SCRIPT && handler->common->pd.type != _PD_TYPE_BUFFER) { + ErrPrint("Handler is not valid type\n"); + return LB_STATUS_ERROR_INVALID; + } + + return lb_acquire_pd_pixmap(handler, cb, data); +} + +EAPI int livebox_pd_pixmap(const struct livebox *handler) +{ + const char *id; + int pixmap = 0; + + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return 0; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return 0; + } + + if (!handler->common->id) { + ErrPrint("Invalid handler\n"); + return 0; + } + + if (handler->common->pd.type != _PD_TYPE_SCRIPT && handler->common->pd.type != _PD_TYPE_BUFFER) { + ErrPrint("Invalid handler\n"); + return 0; + } + + id = fb_id(handler->common->pd.fb); + if (id && sscanf(id, SCHEMA_PIXMAP "%u", (unsigned int *)&pixmap) != 1) { + ErrPrint("PIXMAP Id is not valid\n"); + return 0; + } + + return pixmap; +} + +EAPI int livebox_lb_pixmap(const struct livebox *handler) +{ + const char *id; + int pixmap = 0; + + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return 0; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return 0; + } + + if (!handler->common->id) { + ErrPrint("Invalid handler\n"); + return 0; + } + + if (handler->common->lb.type != _LB_TYPE_SCRIPT && handler->common->lb.type != _LB_TYPE_BUFFER) { + ErrPrint("Invalid handler\n"); + return 0; + } + + id = fb_id(handler->common->lb.fb); + if (id && sscanf(id, SCHEMA_PIXMAP "%u", (unsigned int *)&pixmap) != 1) { + ErrPrint("PIXMAP Id is not valid\n"); + return 0; + } + + return pixmap; +} + +/*! + * \note + * Do not check the state of handler and common-handler. + * If this function is used in the deleted callback, + * the handler and common-handler's state would be DELETE + * if this function check the state of handles, + * user cannot release the pixmap. + */ +EAPI int livebox_release_pd_pixmap(struct livebox *handler, int pixmap) +{ + struct packet *packet; + const char *pkgname; + const char *id; + + if (pixmap == 0 /* || handler->state != CREATE */) { + ErrPrint("Pixmap is invalid [%d]\n", pixmap); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler) { + /*! + * \note + * Even though the handler is NULL, we should send the release request to the master. + * Because the pixmap resource can be released after the handler is destroyed. + * Pixmap resource is used by client. and it cannot be guaranteed to release pixmap. + * In some cases, the pixmap can be released after the handler is deleted. + * + * Its implementation is up to the viewer app. + * But we cannot force it to use only with valid handler. + */ + DbgPrint("Using NULL handler\n"); + pkgname = NULL; + id = NULL; + /*! + * \note + * Master will try to find the buffer handler using given pixmap. if the pkgname and id is not valid. + */ + } else { + if (!handler->common /* || handler-common->state != CREATE */) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Invalid handle\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (handler->common->pd.type != _PD_TYPE_SCRIPT && handler->common->pd.type != _PD_TYPE_BUFFER) { + ErrPrint("Handler is not valid type\n"); + return LB_STATUS_ERROR_INVALID; + } + + pkgname = handler->common->pkgname; + id = handler->common->id; + } + + packet = packet_create_noack("pd_release_pixmap", "ssi", pkgname, id, pixmap); + if (!packet) { + ErrPrint("Failed to build a param\n"); + return LB_STATUS_ERROR_FAULT; + } + + return master_rpc_request_only(handler, packet); +} + +EAPI void *livebox_acquire_fb(struct livebox *handler) +{ + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return NULL; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return NULL; + } + + if (!handler->common->id) { + ErrPrint("Invalid handle\n"); + return NULL; + } + + if (handler->common->lb.type != _LB_TYPE_SCRIPT && handler->common->lb.type != _LB_TYPE_BUFFER) { + ErrPrint("Handler is not valid type\n"); + return NULL; + } + + return fb_acquire_buffer(handler->common->lb.fb); +} + +EAPI int livebox_release_fb(void *buffer) +{ + return fb_release_buffer(buffer); +} + +EAPI int livebox_fb_refcnt(void *buffer) +{ + return fb_refcnt(buffer); +} + +EAPI void *livebox_acquire_pdfb(struct livebox *handler) +{ + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return NULL; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return NULL; + } + + if (!handler->common->id) { + ErrPrint("Invalid handler\n"); + return NULL; + } + + if (handler->common->pd.type != _PD_TYPE_SCRIPT && handler->common->pd.type != _PD_TYPE_BUFFER) { + ErrPrint("Handler is not valid type\n"); + return NULL; + } + + return fb_acquire_buffer(handler->common->pd.fb); +} + +EAPI int livebox_release_pdfb(void *buffer) +{ + return fb_release_buffer(buffer); +} + +EAPI int livebox_pdfb_refcnt(void *buffer) +{ + return fb_refcnt(buffer); +} + +EAPI int livebox_pdfb_bufsz(struct livebox *handler) +{ + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Invalid handler\n"); + return LB_STATUS_ERROR_INVALID; + } + + return fb_size(handler->common->pd.fb); +} + +EAPI int livebox_lbfb_bufsz(struct livebox *handler) +{ + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Invalid handler\n"); + return LB_STATUS_ERROR_INVALID; + } + + return fb_size(handler->common->lb.fb); +} + +EAPI int livebox_is_user(struct livebox *handler) +{ + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Invalid handler\n"); + return LB_STATUS_ERROR_INVALID; + } + + return handler->common->is_user; +} + +EAPI int livebox_set_pinup(struct livebox *handler, int flag, ret_cb_t cb, void *data) +{ + struct packet *packet; + int ret; + + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Invalid handler\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (handler->common->request.pinup) { + ErrPrint("Previous pinup request is not finished\n"); + return LB_STATUS_ERROR_BUSY; + } + + if (handler->common->is_pinned_up == flag) { + DbgPrint("No changes\n"); + return LB_STATUS_ERROR_ALREADY; + } + + packet = packet_create("pinup_changed", "ssi", handler->common->pkgname, handler->common->id, flag); + if (!packet) { + ErrPrint("Failed to build a param\n"); + return LB_STATUS_ERROR_FAULT; + } + + if (!cb) { + cb = default_pinup_cb; + } + + ret = master_rpc_async_request(handler, packet, 0, pinup_done_cb, NULL); + if (ret == (int)LB_STATUS_SUCCESS) { + handler->cbs.pinup.cb = cb; + handler->cbs.pinup.data = data; + handler->common->request.pinup = 1; + } + + return ret; +} + +EAPI int livebox_is_pinned_up(struct livebox *handler) +{ + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Invalid handler\n"); + return LB_STATUS_ERROR_INVALID; + } + + return handler->common->is_pinned_up; +} + +EAPI int livebox_has_pinup(struct livebox *handler) +{ + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Invalid handler\n"); + return LB_STATUS_ERROR_INVALID; + } + + return handler->common->lb.pinup_supported; +} + +EAPI int livebox_set_data(struct livebox *handler, void *data) +{ + if (!handler) { + ErrPrint("Handler is NIL\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + handler->data = data; + return LB_STATUS_SUCCESS; +} + +EAPI void *livebox_get_data(struct livebox *handler) +{ + if (!handler) { + ErrPrint("Handler is NIL\n"); + return NULL; + } + + if (handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return NULL; + } + + return handler->data; +} + +EAPI int livebox_is_exists(const char *pkgname) +{ + char *lb; + + lb = lb_pkgname(pkgname); + if (lb) { + free(lb); + return 1; + } + + return 0; +} + +EAPI const char *livebox_content(struct livebox *handler) +{ + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return NULL; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Invalid handle\n"); + return NULL; + } + + return handler->common->content; +} + +EAPI const char *livebox_category_title(struct livebox *handler) +{ + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return NULL; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Invalid handle\n"); + return NULL; + } + + return handler->common->title; +} + +EAPI int livebox_emit_text_signal(struct livebox *handler, const char *emission, const char *source, double sx, double sy, double ex, double ey, ret_cb_t cb, void *data) +{ + struct packet *packet; + struct cb_info *cbinfo; + int ret; + + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if ((handler->common->lb.type != _LB_TYPE_TEXT && handler->common->pd.type != _PD_TYPE_TEXT) || !handler->common->id) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!emission) { + emission = ""; + } + + if (!source) { + source = ""; + } + + packet = packet_create("text_signal", "ssssdddd", + handler->common->pkgname, handler->common->id, emission, source, sx, sy, ex, ey); + if (!packet) { + ErrPrint("Failed to build a param\n"); + return LB_STATUS_ERROR_FAULT; + } + + cbinfo = create_cb_info(cb, data); + if (!cbinfo) { + packet_destroy(packet); + return LB_STATUS_ERROR_FAULT; + } + + ret = master_rpc_async_request(handler, packet, 0, text_signal_cb, cbinfo); + if (ret < 0) { + destroy_cb_info(cbinfo); + } + + return ret; +} + +EAPI int livebox_subscribe_group(const char *cluster, const char *category) +{ + struct packet *packet; + + /*! + * \todo + * Validate the group info using DB + * If the group info is not valid, do not send this request + */ + + packet = packet_create_noack("subscribe", "ss", cluster ? cluster : "", category ? category : ""); + if (!packet) { + ErrPrint("Failed to create a packet\n"); + return LB_STATUS_ERROR_FAULT; + } + + return master_rpc_request_only(NULL, packet); +} + +EAPI int livebox_unsubscribe_group(const char *cluster, const char *category) +{ + struct packet *packet; + + /*! + * \todo + * Validate the group info using DB + * If the group info is not valid, do not send this request + * AND Check the subscribed or not too + */ + + packet = packet_create_noack("unsubscribe", "ss", cluster ? cluster : "", category ? category : ""); + if (!packet) { + ErrPrint("Failed to create a packet\n"); + return LB_STATUS_ERROR_FAULT; + } + + return master_rpc_request_only(NULL, packet); +} + +EAPI int livebox_refresh(struct livebox *handler, int force) +{ + struct packet *packet; + + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + packet = packet_create_noack("update", "ssi", handler->common->pkgname, handler->common->id, force); + if (!packet) { + ErrPrint("Failed to create a packet\n"); + return LB_STATUS_ERROR_FAULT; + } + + return master_rpc_request_only(handler, packet); +} + +EAPI int livebox_refresh_group(const char *cluster, const char *category, int force) +{ + struct packet *packet; + + if (!cluster || !category) { + ErrPrint("Invalid argument\n"); + return LB_STATUS_ERROR_INVALID; + } + + packet = packet_create_noack("refresh_group", "ssi", cluster, category, force); + if (!packet) { + ErrPrint("Failed to create a packet\n"); + return LB_STATUS_ERROR_FAULT; + } + + return master_rpc_request_only(NULL, packet); +} + +EAPI int livebox_set_visibility(struct livebox *handler, enum livebox_visible_state state) +{ + int old_state; + int ret; + + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->is_user) { + /* System cluster livebox cannot be changed its visible states */ + if (state == LB_HIDE_WITH_PAUSE) { + ErrPrint("CA Livebox is not able to change the visibility\n"); + return LB_STATUS_ERROR_PERMISSION; + } + } + + if (handler->visible == state) { + DbgPrint("%s has no changes\n", handler->common->pkgname); + return LB_STATUS_ERROR_ALREADY; + } + + old_state = handler->visible; + handler->visible = state; + + ret = lb_set_visibility(handler, state); + if (ret < 0) { + handler->visible = old_state; + } + + return ret; +} + +EAPI enum livebox_visible_state livebox_visibility(struct livebox *handler) +{ + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is invalid\n"); + return LB_VISIBLE_ERROR; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is not valid\n"); + return LB_VISIBLE_ERROR; + } + + if (!handler->common->id) { + ErrPrint("Handler is not valid\n"); + return LB_VISIBLE_ERROR; + } + + return handler->visible; +} + +int lb_set_group(struct livebox_common *common, const char *cluster, const char *category) +{ + void *pc = NULL; + void *ps = NULL; + + if (cluster) { + pc = strdup(cluster); + if (!pc) { + ErrPrint("Heap: %s (cluster: %s)\n", strerror(errno), cluster); + return LB_STATUS_ERROR_MEMORY; + } + } + + if (category) { + ps = strdup(category); + if (!ps) { + ErrPrint("Heap: %s (category: %s)\n", strerror(errno), category); + free(pc); + return LB_STATUS_ERROR_MEMORY; + } + } + + if (common->cluster) { + free(common->cluster); + } + + if (common->category) { + free(common->category); + } + + common->cluster = pc; + common->category = ps; + + return LB_STATUS_SUCCESS; +} + +void lb_set_size(struct livebox_common *common, int w, int h) +{ + int size_type; + + common->lb.width = w; + common->lb.height = h; + + size_type = livebox_service_size_type(w, h); + if (size_type != LB_SIZE_TYPE_UNKNOWN) { + common->lb.mouse_event = livebox_service_mouse_event(common->pkgname, size_type); + } +} + +void lb_set_update_mode(struct livebox_common *common, int active_mode) +{ + common->is_active_update = active_mode; +} + +void lb_set_pdsize(struct livebox_common *common, int w, int h) +{ + common->pd.width = w; + common->pd.height = h; +} + +void lb_set_default_pdsize(struct livebox_common *common, int w, int h) +{ + common->pd.default_width = w; + common->pd.default_height = h; +} + +void lb_invoke_fault_handler(enum livebox_fault_type event, const char *pkgname, const char *file, const char *func) +{ + struct dlist *l; + struct dlist *n; + struct fault_info *info; + + s_info.fault_state = INFO_STATE_CALLBACK_IN_PROCESSING; + + dlist_foreach_safe(s_info.fault_list, l, n, info) { + if (!info->is_deleted && info->handler(event, pkgname, file, func, info->user_data) == EXIT_FAILURE) { + info->is_deleted = 1; + } + + if (info->is_deleted) { + s_info.fault_list = dlist_remove(s_info.fault_list, l); + free(info); + } + } + + s_info.fault_state &= ~INFO_STATE_CALLBACK_IN_PROCESSING; +} + +void lb_invoke_event_handler(struct livebox *handler, enum livebox_event_type event) +{ + struct dlist *l; + struct dlist *n; + struct event_info *info; + + if (event == LB_EVENT_LB_UPDATED && handler->common->refcnt > 1) { + if (handler->visible != LB_SHOW) { + DbgPrint("Update requested(pending) - %s\n", handler->common->pkgname); + handler->paused_updating++; + return; + } else { + handler->paused_updating = 0; + } + } + + s_info.event_state = INFO_STATE_CALLBACK_IN_PROCESSING; + + dlist_foreach_safe(s_info.event_list, l, n, info) { + if (!info->is_deleted && info->handler(handler, event, info->user_data) == EXIT_FAILURE) { + DbgPrint("Event handler returns EXIT_FAILURE\n"); + info->is_deleted = 1; + } + + if (info->is_deleted) { + s_info.event_list = dlist_remove(s_info.event_list, l); + free(info); + } + } + + s_info.event_state &= ~INFO_STATE_CALLBACK_IN_PROCESSING; +} + +struct livebox_common *lb_find_common_handle(const char *pkgname, const char *id) +{ + struct dlist *l; + struct livebox_common *common; + + dlist_foreach(s_info.livebox_common_list, l, common) { + if (!common->id) { + continue; + } + + if (!strcmp(common->pkgname, pkgname) && !strcmp(common->id, id)) { + return common; + } + } + + return NULL; +} + +struct livebox_common *lb_find_common_handle_by_timestamp(double timestamp) +{ + struct dlist *l; + struct livebox_common *common; + + dlist_foreach(s_info.livebox_common_list, l, common) { + if (common->timestamp == timestamp) { + return common; + } + } + + return NULL; +} + +struct livebox *lb_new_livebox(const char *pkgname, const char *id, double timestamp, const char *cluster, const char *category) +{ + struct livebox *handler; + + handler = calloc(1, sizeof(*handler)); + if (!handler) { + ErrPrint("Failed to create a new livebox\n"); + return NULL; + } + + handler->common = lb_create_common_handle(handler, pkgname, cluster, category); + if (!handler->common) { + ErrPrint("Heap: %s\n", strerror(errno)); + free(handler); + return NULL; + } + + lb_common_ref(handler->common, handler); + lb_set_id(handler->common, id); + handler->common->timestamp = timestamp; + handler->common->state = CREATE; + handler->visible = LB_SHOW; + s_info.livebox_list = dlist_append(s_info.livebox_list, handler); + + return lb_ref(handler); +} + +int lb_delete_all(void) +{ + struct dlist *l; + struct dlist *n; + struct livebox *handler; + + dlist_foreach_safe(s_info.livebox_list, l, n, handler) { + lb_invoke_event_handler(handler, LB_EVENT_DELETED); + lb_unref(handler, 1); + } + + return LB_STATUS_SUCCESS; +} + +int lb_set_content(struct livebox_common *common, const char *content) +{ + char *pc = NULL; + + if (content) { + pc = strdup(content); + if (!pc) { + ErrPrint("heap: %s [%s]\n", strerror(errno), content); + return LB_STATUS_ERROR_MEMORY; + } + } + + free(common->content); + common->content = pc; + return LB_STATUS_SUCCESS; +} + +int lb_set_title(struct livebox_common *common, const char *title) +{ + char *pt = NULL; + + if (title) { + pt = strdup(title); + if (!pt) { + ErrPrint("heap: %s [%s]\n", strerror(errno), title); + return LB_STATUS_ERROR_MEMORY; + } + } + + free(common->title); + common->title = pt; + return LB_STATUS_SUCCESS; +} + +void lb_set_size_list(struct livebox_common *common, int size_list) +{ + common->lb.size_list = size_list; +} + +void lb_set_auto_launch(struct livebox_common *common, const char *auto_launch) +{ + char *pa = NULL; + + if (!auto_launch || !strlen(auto_launch)) { + return; + } + + pa = strdup(auto_launch); + if (!pa) { + ErrPrint("heap: %s, [%s]\n", strerror(errno), auto_launch); + return; + } + + free(common->lb.auto_launch); + common->lb.auto_launch = pa; +} + +void lb_set_priority(struct livebox_common *common, double priority) +{ + common->lb.priority = priority; +} + +void lb_set_id(struct livebox_common *common, const char *id) +{ + char *pi = NULL; + + if (id) { + pi = strdup(id); + if (!pi) { + ErrPrint("heap: %s [%s]\n", strerror(errno), pi); + return; + } + } + + free(common->id); + common->id = pi; +} + +void lb_set_filename(struct livebox_common *common, const char *filename) +{ + if (common->filename) { + if (common->lb.type == _LB_TYPE_FILE || common->lb.type == _LB_TYPE_TEXT) { + if (common->filename[0] && unlink(common->filename) < 0) { + ErrPrint("unlink: %s (%s)\n", strerror(errno), common->filename); + } + } + + free(common->filename); + } + + common->filename = strdup(filename); + if (!common->filename) { + ErrPrint("Heap: %s\n", strerror(errno)); + } +} + +void lb_set_alt_info(struct livebox_common *common, const char *icon, const char *name) +{ + char *_icon = NULL; + char *_name = NULL; + + if (icon && strlen(icon)) { + _icon = strdup(icon); + if (!_icon) { + ErrPrint("Heap: %s\n", strerror(errno)); + } + } + + if (name && strlen(name)) { + _name = strdup(name); + if (!_name) { + ErrPrint("Heap: %s\n", strerror(errno)); + } + } + + free(common->alt.icon); + common->alt.icon = _icon; + + free(common->alt.name); + common->alt.name = _name; +} + +int lb_set_lb_fb(struct livebox_common *common, const char *filename) +{ + struct fb_info *fb; + + if (!common) { + return LB_STATUS_ERROR_INVALID; + } + + fb = common->lb.fb; + if (fb && !strcmp(fb_id(fb), filename)) { /*!< BUFFER is not changed, */ + return LB_STATUS_SUCCESS; + } + + common->lb.fb = NULL; + + if (!filename || filename[0] == '\0') { + if (fb) { + fb_destroy(fb); + } + return LB_STATUS_SUCCESS; + } + + common->lb.fb = fb_create(filename, common->lb.width, common->lb.height); + if (!common->lb.fb) { + ErrPrint("Faield to create a FB\n"); + if (fb) { + fb_destroy(fb); + } + return LB_STATUS_ERROR_FAULT; + } + + if (fb) { + fb_destroy(fb); + } + + return LB_STATUS_SUCCESS; +} + +int lb_set_pd_fb(struct livebox_common *common, const char *filename) +{ + struct fb_info *fb; + + if (!common || common->state != CREATE) { + return LB_STATUS_ERROR_INVALID; + } + + fb = common->pd.fb; + if (fb && !strcmp(fb_id(fb), filename)) { + /* BUFFER is not changed, just update the content */ + return LB_STATUS_ERROR_EXIST; + } + common->pd.fb = NULL; + + if (!filename || filename[0] == '\0') { + if (fb) { + fb_destroy(fb); + } + return LB_STATUS_SUCCESS; + } + + common->pd.fb = fb_create(filename, common->pd.width, common->pd.height); + if (!common->pd.fb) { + ErrPrint("Failed to create a FB\n"); + if (fb) { + fb_destroy(fb); + } + return LB_STATUS_ERROR_FAULT; + } + + if (fb) { + fb_destroy(fb); + } + return LB_STATUS_SUCCESS; +} + +struct fb_info *lb_get_lb_fb(struct livebox_common *common) +{ + return common->lb.fb; +} + +struct fb_info *lb_get_pd_fb(struct livebox_common *common) +{ + return common->pd.fb; +} + +void lb_set_user(struct livebox_common *common, int user) +{ + common->is_user = user; +} + +void lb_set_pinup(struct livebox_common *common, int pinup_supported) +{ + common->lb.pinup_supported = pinup_supported; +} + +void lb_set_text_lb(struct livebox_common *common) +{ + common->lb.type = _LB_TYPE_TEXT; +} + +void lb_set_text_pd(struct livebox_common *common) +{ + common->pd.type = _PD_TYPE_TEXT; +} + +int lb_text_lb(struct livebox_common *common) +{ + return common->lb.type == _LB_TYPE_TEXT; +} + +int lb_text_pd(struct livebox_common *common) +{ + return common->pd.type == _PD_TYPE_TEXT; +} + +void lb_set_period(struct livebox_common *common, double period) +{ + common->lb.period = period; +} + +struct livebox *lb_ref(struct livebox *handler) +{ + if (!handler) { + return NULL; + } + + handler->refcnt++; + return handler; +} + +struct livebox *lb_unref(struct livebox *handler, int destroy_common) +{ + if (!handler) { + return NULL; + } + + handler->refcnt--; + if (handler->refcnt > 0) { + return handler; + } + + if (handler->cbs.created.cb) { + handler->cbs.created.cb(handler, LB_STATUS_ERROR_FAULT, handler->cbs.created.data); + handler->cbs.created.cb = NULL; + handler->cbs.created.data = NULL; + } + + if (handler->cbs.deleted.cb) { + handler->cbs.deleted.cb(handler, LB_STATUS_ERROR_FAULT, handler->cbs.deleted.data); + handler->cbs.deleted.cb = NULL; + handler->cbs.deleted.data = NULL; + } + + if (handler->cbs.pinup.cb) { + handler->cbs.pinup.cb(handler, LB_STATUS_ERROR_FAULT, handler->cbs.pinup.data); + handler->cbs.pinup.cb = NULL; + handler->cbs.pinup.data = NULL; + } + + if (handler->cbs.group_changed.cb) { + handler->cbs.group_changed.cb(handler, LB_STATUS_ERROR_FAULT, handler->cbs.group_changed.data); + handler->cbs.group_changed.cb = NULL; + handler->cbs.group_changed.data = NULL; + } + + if (handler->cbs.period_changed.cb) { + handler->cbs.period_changed.cb(handler, LB_STATUS_ERROR_FAULT, handler->cbs.period_changed.data); + handler->cbs.period_changed.cb = NULL; + handler->cbs.period_changed.data = NULL; + } + + if (handler->cbs.size_changed.cb) { + handler->cbs.size_changed.cb(handler, LB_STATUS_ERROR_FAULT, handler->cbs.size_changed.data); + handler->cbs.size_changed.cb = NULL; + handler->cbs.size_changed.data = NULL; + } + + if (handler->cbs.pd_created.cb) { + handler->cbs.pd_created.cb(handler, LB_STATUS_ERROR_FAULT, handler->cbs.pd_created.data); + handler->cbs.pd_created.cb = NULL; + handler->cbs.pd_created.data = NULL; + } + + if (handler->cbs.pd_destroyed.cb) { + handler->cbs.pd_destroyed.cb(handler, LB_STATUS_ERROR_FAULT, handler->cbs.pd_destroyed.data); + handler->cbs.pd_destroyed.cb = NULL; + handler->cbs.pd_destroyed.data = NULL; + } + + if (handler->cbs.update_mode.cb) { + handler->cbs.update_mode.cb(handler, LB_STATUS_ERROR_FAULT, handler->cbs.update_mode.data); + handler->cbs.update_mode.cb = NULL; + handler->cbs.update_mode.data = NULL; + } + + if (handler->cbs.access_event.cb) { + handler->cbs.access_event.cb(handler, LB_ACCESS_STATUS_ERROR, handler->cbs.access_event.data); + handler->cbs.access_event.cb = NULL; + handler->cbs.access_event.data = NULL; + } + + if (handler->cbs.key_event.cb) { + handler->cbs.key_event.cb(handler, LB_KEY_STATUS_ERROR, handler->cbs.key_event.data); + handler->cbs.key_event.cb = NULL; + handler->cbs.key_event.data = NULL; + } + + dlist_remove_data(s_info.livebox_list, handler); + + handler->state = DESTROYED; + if (lb_common_unref(handler->common, handler) == 0) { + if (destroy_common) { + /*! + * \note + * Lock file should be deleted after all callbacks are processed. + */ + lb_destroy_lock_file(handler->common, 0); + lb_destroy_common_handle(handler->common); + } + } + free(handler); + DbgPrint("Handler is released\n"); + return NULL; +} + +int lb_send_delete(struct livebox *handler, int type, ret_cb_t cb, void *data) +{ + struct packet *packet; + struct cb_info *cbinfo; + int ret; + + if (handler->common->request.deleted) { + ErrPrint("Already in-progress\n"); + if (cb) { + cb(handler, LB_STATUS_SUCCESS, data); + } + return LB_STATUS_ERROR_BUSY; + } + + if (!cb) { + cb = default_delete_cb; + } + + packet = packet_create("delete", "ssid", handler->common->pkgname, handler->common->id, type, handler->common->timestamp); + if (!packet) { + ErrPrint("Failed to build a param\n"); + if (cb) { + cb(handler, LB_STATUS_ERROR_FAULT, data); + } + + return LB_STATUS_ERROR_FAULT; + } + + cbinfo = create_cb_info(cb, data); + if (!cbinfo) { + packet_destroy(packet); + ErrPrint("Failed to create cbinfo\n"); + if (cb) { + cb(handler, LB_STATUS_ERROR_FAULT, data); + } + + return LB_STATUS_ERROR_FAULT; + } + + ret = master_rpc_async_request(handler, packet, 0, del_ret_cb, cbinfo); + if (ret < 0) { + /*! + * Packet is destroyed by master_rpc_async_request. + */ + destroy_cb_info(cbinfo); + + if (cb) { + cb(handler, LB_STATUS_ERROR_FAULT, data); + } + } else { + handler->common->request.deleted = 1; + } + + return ret; +} + +EAPI int livebox_client_paused(void) +{ + struct packet *packet; + + packet = packet_create_noack("client_paused", "d", util_timestamp()); + if (!packet) { + ErrPrint("Failed to create a pause packet\n"); + return LB_STATUS_ERROR_FAULT; + } + + return master_rpc_request_only(NULL, packet); +} + +EAPI int livebox_client_resumed(void) +{ + struct packet *packet; + + packet = packet_create_noack("client_resumed", "d", util_timestamp()); + if (!packet) { + ErrPrint("Failed to create a resume packet\n"); + return LB_STATUS_ERROR_FAULT; + } + + return master_rpc_request_only(NULL, packet); +} + +EAPI int livebox_sync_lb_fb(struct livebox *handler) +{ + if (!handler || handler->state != CREATE) { + ErrPrint("Invalid handle\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Invalid handle\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + return LB_STATUS_ERROR_INVALID; + } + + return lb_sync_lb_fb(handler->common); +} + +int lb_sync_lb_fb(struct livebox_common *common) +{ + int ret; + + if (fb_type(lb_get_lb_fb(common)) == BUFFER_TYPE_FILE && common->lb.lock_fd >= 0) { + (void)do_fb_lock(common->lb.lock_fd); + ret = fb_sync(lb_get_lb_fb(common)); + (void)do_fb_unlock(common->lb.lock_fd); + } else { + ret = fb_sync(lb_get_lb_fb(common)); + } + + return ret; +} + +int lb_sync_pd_fb(struct livebox_common *common) +{ + int ret; + + if (fb_type(lb_get_pd_fb(common)) == BUFFER_TYPE_FILE && common->pd.lock_fd >= 0) { + (void)do_fb_lock(common->pd.lock_fd); + ret = fb_sync(lb_get_pd_fb(common)); + (void)do_fb_unlock(common->pd.lock_fd); + } else { + ret = fb_sync(lb_get_pd_fb(common)); + } + + return ret; +} + +EAPI int livebox_sync_pd_fb(struct livebox *handler) +{ + if (!handler || handler->state != CREATE) { + ErrPrint("Invalid handle\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Invalid handle\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Invalid handle\n"); + return LB_STATUS_ERROR_INVALID; + } + + return lb_sync_pd_fb(handler->common); +} + +EAPI const char *livebox_alt_icon(struct livebox *handler) +{ + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is not valid[%p]\n", handler); + return NULL; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is not valid\n"); + return NULL; + } + + return handler->common->alt.icon; +} + +EAPI const char *livebox_alt_name(struct livebox *handler) +{ + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is not valid[%p]\n", handler); + return NULL; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is not valid\n"); + return NULL; + } + + return handler->common->alt.name; +} + +EAPI int livebox_acquire_fb_lock(struct livebox *handler, int is_pd) +{ + int ret = LB_STATUS_SUCCESS; + int fd; + + if (!handler || handler->state != CREATE) { + ErrPrint("Handler is not valid[%p]\n", handler); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Handler is not valid\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Handler is not valid[%p]\n", handler); + return LB_STATUS_ERROR_INVALID; + } + + if (is_pd) { + if (!handler->common->pd.lock || handler->common->pd.lock_fd < 0) { + DbgPrint("Lock: %s (%d)\n", handler->common->pd.lock, handler->common->pd.lock_fd); + return LB_STATUS_ERROR_INVALID; + } + + if (fb_type(lb_get_pd_fb(handler->common)) == BUFFER_TYPE_FILE) { + return LB_STATUS_SUCCESS; + } + + fd = handler->common->pd.lock_fd; + } else { + if (!handler->common->lb.lock || handler->common->lb.lock_fd < 0) { + DbgPrint("Lock: %s (%d)\n", handler->common->lb.lock, handler->common->lb.lock_fd); + return LB_STATUS_ERROR_INVALID; + } + + if (fb_type(lb_get_lb_fb(handler->common)) == BUFFER_TYPE_FILE) { + return LB_STATUS_SUCCESS; + } + + fd = handler->common->lb.lock_fd; + } + + ret = do_fb_lock(fd); + + return ret == 0 ? LB_STATUS_SUCCESS : LB_STATUS_ERROR_FAULT; +} + +EAPI int livebox_release_fb_lock(struct livebox *handler, int is_pd) +{ + int ret = LB_STATUS_SUCCESS; + int fd; + + if (!handler || handler->state != CREATE) { + ErrPrint("Invalid handle\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common || handler->common->state != CREATE) { + ErrPrint("Invalid handle\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!handler->common->id) { + ErrPrint("Handler is not valid[%p]\n", handler); + return LB_STATUS_ERROR_INVALID; + } + + if (is_pd) { + if (!handler->common->pd.lock || handler->common->pd.lock_fd < 0) { + DbgPrint("Unlock: %s (%d)\n", handler->common->pd.lock, handler->common->pd.lock_fd); + return LB_STATUS_ERROR_INVALID; + } + + if (fb_type(lb_get_pd_fb(handler->common)) == BUFFER_TYPE_FILE) { + return LB_STATUS_SUCCESS; + } + + fd = handler->common->pd.lock_fd; + } else { + if (!handler->common->lb.lock || handler->common->lb.lock_fd < 0) { + DbgPrint("Unlock: %s (%d)\n", handler->common->lb.lock, handler->common->lb.lock_fd); + return LB_STATUS_ERROR_INVALID; + } + + if (fb_type(lb_get_lb_fb(handler->common)) == BUFFER_TYPE_FILE) { + return LB_STATUS_SUCCESS; + } + + fd = handler->common->lb.lock_fd; + } + + ret = do_fb_unlock(fd); + + return ret == 0 ? LB_STATUS_SUCCESS : LB_STATUS_ERROR_FAULT; +} + +EAPI int livebox_set_option(enum livebox_option_type option, int state) +{ + int ret = LB_STATUS_SUCCESS; + + switch (option) { + case LB_OPTION_MANUAL_SYNC: + conf_set_manual_sync(state); + break; + case LB_OPTION_FRAME_DROP_FOR_RESIZE: + conf_set_frame_drop_for_resizing(state); + break; + case LB_OPTION_SHARED_CONTENT: + conf_set_shared_content(state); + break; + default: + ret = LB_STATUS_ERROR_INVALID; + break; + } + + return ret; +} + +EAPI int livebox_option(enum livebox_option_type option) +{ + int ret; + + switch (option) { + case LB_OPTION_MANUAL_SYNC: + ret = conf_manual_sync(); + break; + case LB_OPTION_FRAME_DROP_FOR_RESIZE: + ret = conf_frame_drop_for_resizing(); + break; + case LB_OPTION_SHARED_CONTENT: + ret = conf_shared_content(); + break; + default: + ret = LB_STATUS_ERROR_INVALID; + break; + } + + return ret; +} + +EAPI int livebox_set_auto_launch_handler(int (*launch_handler)(struct livebox *handler, const char *appid, void *data), void *data) +{ + s_info.launch.handler = launch_handler; + s_info.launch.data = data; + + return LB_STATUS_SUCCESS; +} + +/* End of a file */ diff --git a/src/master_rpc.c b/src/master_rpc.c new file mode 100644 index 0000000..ec3b4cb --- /dev/null +++ b/src/master_rpc.c @@ -0,0 +1,317 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <gio/gio.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include <dlog.h> + +#include <packet.h> +#include <com-core_packet.h> +#include <livebox-errno.h> +#include <livebox-service.h> + +#include "debug.h" +#include "dlist.h" +#include "livebox.h" +#include "livebox_internal.h" +#include "master_rpc.h" +#include "client.h" +#include "util.h" + +#define DEFAULT_TTL 10 +#define REQUEST_DELAY 10 + +struct command { + int ttl; + struct packet *packet; + struct livebox *handler; + void (*ret_cb)(struct livebox *handler, const struct packet *result, void *data); + void *data; + enum { + TYPE_ACK, + TYPE_NOACK + } type; +}; + +int errno; + +static struct { + guint cmd_timer; + struct dlist *cmd_list; +} s_info = { + .cmd_timer = 0, + .cmd_list = NULL, +}; + +static int done_cb(pid_t pid, int handle, const struct packet *packet, void *data); + +static inline struct command *pop_command(void) +{ + struct dlist *l; + struct command *command; + + l = dlist_nth(s_info.cmd_list, 0); + if (!l) { + return NULL; + } + + command = dlist_data(l); + s_info.cmd_list = dlist_remove(s_info.cmd_list, l); + return command; +} + +static inline struct command *create_command(struct livebox *handler, struct packet *packet) +{ + struct command *command; + + command = malloc(sizeof(*command)); + if (!command) { + ErrPrint("Failed to allocate mem for command\n"); + return NULL; + } + + command->handler = lb_ref(handler); + command->packet = packet_ref(packet); + return command; +} + +static inline void destroy_command(struct command *command) +{ + packet_unref(command->packet); + lb_unref(command->handler, 1); + free(command); +} + +static gboolean cmd_consumer(gpointer user_data) +{ + struct command *command; + + command = pop_command(); + if (!command) { + s_info.cmd_timer = 0; + return FALSE; + } + + /*! + * \NOTE: + * Item will be deleted in the "done_cb" + * + * item->param be release by the g_dbus_proxy_call + * so to use it again from the done_cb function, + * increate the reference counter of the item->param + */ + if (command->type == TYPE_NOACK) { + if (com_core_packet_send_only(client_fd(), command->packet) < 0) { + ErrPrint("Failed to send a packet to master\n"); + } + + destroy_command(command); + } else { + if (com_core_packet_async_send(client_fd(), command->packet, 0u, done_cb, command) < 0) { + ErrPrint("Failed to send a packet to master\n"); + if (command->ret_cb) { + command->ret_cb(command->handler, NULL, command->data); + } + destroy_command(command); + } + } + return TRUE; +} + +static inline void prepend_command(struct command *command) +{ + s_info.cmd_list = dlist_prepend(s_info.cmd_list, command); + master_rpc_check_and_fire_consumer(); +} + +void master_rpc_check_and_fire_consumer(void) +{ + if (!s_info.cmd_list || s_info.cmd_timer || client_fd() < 0) { + return; + } + + s_info.cmd_timer = g_timeout_add(REQUEST_DELAY, cmd_consumer, NULL); + if (!s_info.cmd_timer) { + ErrPrint("Failed to add timer\n"); + } +} + +static int done_cb(pid_t pid, int handle, const struct packet *packet, void *data) +{ + struct command *command; + int ret; + + command = data; + + if (!packet) { + /*! \NOTE: + * Release resource even if + * we failed to finish the method call + */ + command->ttl--; + if (command->ttl > 0) { + prepend_command(command); + return 0; + } + + goto out; + } + + if (packet_get(packet, "i", &ret) != 1) { + ErrPrint("Invalid result packet\n"); + ret = LB_STATUS_ERROR_INVALID; + } + +out: + if (command->ret_cb) { + command->ret_cb(command->handler, packet, command->data); + } + + destroy_command(command); + return 0; +} + +static inline void push_command(struct command *command) +{ + s_info.cmd_list = dlist_append(s_info.cmd_list, command); + master_rpc_check_and_fire_consumer(); +} + +/*! + * \note + * "handler" could be NULL + */ +int master_rpc_async_request(struct livebox *handler, struct packet *packet, int urgent, void (*ret_cb)(struct livebox *handler, const struct packet *result, void *data), void *data) +{ + struct command *command; + + command = create_command(handler, packet); + if (!command) { + ErrPrint("Failed to create a command\n"); + packet_unref(packet); + return LB_STATUS_ERROR_FAULT; + } + + command->ret_cb = ret_cb; + command->data = data; + command->ttl = DEFAULT_TTL; + command->type = TYPE_ACK; + + if (urgent) { + prepend_command(command); + } else { + push_command(command); + } + + packet_unref(packet); + return LB_STATUS_SUCCESS; +} + +int master_rpc_request_only(struct livebox *handler, struct packet *packet) +{ + struct command *command; + + command = create_command(handler, packet); + if (!command) { + ErrPrint("Failed to create a command\n"); + packet_unref(packet); + return LB_STATUS_ERROR_FAULT; + } + + command->ret_cb = NULL; + command->data = NULL; + command->ttl = 0; + command->type = TYPE_NOACK; + + push_command(command); + packet_unref(packet); + return LB_STATUS_SUCCESS; +} + +int master_rpc_clear_fault_package(const char *pkgname) +{ + struct dlist *l; + struct dlist *n; + struct command *command; + + if (!pkgname) { + return LB_STATUS_ERROR_INVALID; + } + + dlist_foreach_safe(s_info.cmd_list, l, n, command) { + if (!command->handler) { + continue; + } + + if (!strcmp(command->handler->common->pkgname, pkgname)) { + s_info.cmd_list = dlist_remove(s_info.cmd_list, l); + if (command->ret_cb) { + command->ret_cb(command->handler, NULL, command->data); + } + + destroy_command(command); + } + } + + return 0; +} + +int master_rpc_clear_all_request(void) +{ + struct command *command; + struct dlist *l; + struct dlist *n; + + dlist_foreach_safe(s_info.cmd_list, l, n, command) { + s_info.cmd_list = dlist_remove(s_info.cmd_list, l); + + if (command->ret_cb) { + command->ret_cb(command->handler, NULL, command->data); + } + + destroy_command(command); + } + + return 0; +} + +int master_rpc_sync_request(struct packet *packet) +{ + struct packet *result; + int ret; + + result = com_core_packet_oneshot_send(client_addr(), packet, 0.0f); + if (result) { + if (packet_get(result, "i", &ret) != 1) { + ErrPrint("Invalid result packet\n"); + ret = LB_STATUS_ERROR_INVALID; + } + + packet_unref(result); + } else { + ErrPrint("Failed to send a sync request\n"); + ret = LB_STATUS_ERROR_FAULT; + } + + packet_unref(packet); + return ret; +} + +/* End of a file */ diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..66431c6 --- /dev/null +++ b/src/util.c @@ -0,0 +1,214 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <sys/time.h> +#include <unistd.h> +#include <stdlib.h> +#include <time.h> + +#include <dlog.h> +#include <livebox-errno.h> /* For error code */ + +#include "debug.h" +#include "util.h" + +int errno; +#if defined(_USE_ECORE_TIME_GET) +static struct { + clockid_t type; +} s_info = { + .type = CLOCK_MONOTONIC, +}; +#endif + +int util_check_extension(const char *filename, const char *check_ptr) +{ + int name_len; + + name_len = strlen(filename); + while (--name_len >= 0 && *check_ptr) { + if (filename[name_len] != *check_ptr) { + return LB_STATUS_ERROR_INVALID; + } + + check_ptr ++; + } + + return 0; +} + +double util_timestamp(void) +{ +#if defined(_USE_ECORE_TIME_GET) + struct timespec ts; + + do { + if (clock_gettime(s_info.type, &ts) == 0) { + return ts.tv_sec + ts.tv_nsec / 1000000000.0f; + } + + ErrPrint("%d: %s\n", s_info.type, strerror(errno)); + if (s_info.type == CLOCK_MONOTONIC) { + s_info.type = CLOCK_REALTIME; + } else if (s_info.type == CLOCK_REALTIME) { + struct timeval tv; + if (gettimeofday(&tv, NULL) < 0) { + ErrPrint("gettimeofday: %s\n", strerror(errno)); + break; + } + + return tv.tv_sec + tv.tv_usec / 1000000.0f; + } + } while (1); + + return 0.0f; +#else + struct timeval tv; + + if (gettimeofday(&tv, NULL) < 0) { + ErrPrint("gettimeofday: %s\n", strerror(errno)); + tv.tv_sec = 0; + tv.tv_usec = 0; + } + + return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0f; +#endif +} + +const char *util_basename(const char *name) +{ + int length; + length = name ? strlen(name) : 0; + if (!length) { + return "."; + } + + while (--length > 0 && name[length] != '/'); + + return length <= 0 ? name : name + length + (name[length] == '/'); +} + +static inline int check_native_livebox(const char *pkgname) +{ + int len; + char *path; + + len = strlen(pkgname) * 2; + len += strlen("/opt/usr/live/%s/libexec/liblive-%s.so"); + + path = malloc(len + 1); + if (!path) { + ErrPrint("Heap: %s\n", strerror(errno)); + return LB_STATUS_ERROR_MEMORY; + } + + snprintf(path, len, "/opt/usr/live/%s/libexec/liblive-%s.so", pkgname, pkgname); + if (access(path, F_OK | R_OK) != 0) { + ErrPrint("%s is not a valid package\n", pkgname); + free(path); + return LB_STATUS_ERROR_INVALID; + } + + free(path); + return 0; +} + +static inline int check_web_livebox(const char *pkgname) +{ + int len; + char *path; + + len = strlen(pkgname) * 2; + len += strlen("/opt/usr/apps/%s/res/wgt/livebox/index.html"); + + path = malloc(len + 1); + if (!path) { + ErrPrint("Heap: %s\n", strerror(errno)); + return LB_STATUS_ERROR_MEMORY; + } + + snprintf(path, len, "/opt/usr/apps/%s/res/wgt/livebox/index.html", pkgname); + if (access(path, F_OK | R_OK) != 0) { + ErrPrint("%s is not a valid package\n", pkgname); + free(path); + return LB_STATUS_ERROR_INVALID; + } + + free(path); + return 0; +} + +int util_validate_livebox_package(const char *pkgname) +{ + if (!pkgname) { + ErrPrint("Invalid argument\n"); + return LB_STATUS_ERROR_INVALID; + } + + if (!check_native_livebox(pkgname) || !check_web_livebox(pkgname)) { + return 0; + } + + return LB_STATUS_ERROR_INVALID; +} + +const char *util_uri_to_path(const char *uri) +{ + int len; + + len = strlen(SCHEMA_FILE); + if (strncasecmp(uri, SCHEMA_FILE, len)) { + return NULL; + } + + return uri + len; +} + +int util_unlink(const char *filename) +{ + char *descfile; + int desclen; + int ret; + + if (!filename) { + return LB_STATUS_ERROR_INVALID; + } + + desclen = strlen(filename) + 6; /* .desc */ + descfile = malloc(desclen); + if (!descfile) { + ErrPrint("Heap: %s\n", strerror(errno)); + return LB_STATUS_ERROR_MEMORY; + } + + ret = snprintf(descfile, desclen, "%s.desc", filename); + if (ret < 0) { + ErrPrint("Error: %s\n", strerror(errno)); + free(descfile); + return LB_STATUS_ERROR_FAULT; + } + + (void)unlink(descfile); + free(descfile); + (void)unlink(filename); + + return LB_STATUS_SUCCESS; +} + +/* End of a file */ |