Test application and API doxygen updates for video360 functions 65/152865/6
authorAndriy Martynets <a.martynets@partner.samsung.com>
Wed, 27 Sep 2017 08:42:40 +0000 (11:42 +0300)
committerAndriy Martynets <a.martynets@partner.samsung.com>
Fri, 20 Oct 2017 14:45:19 +0000 (17:45 +0300)
Change-Id: I54b683e842f095fddfd708ae024c9f58fde0d4d3
Signed-off-by: Andriy Martynets <a.martynets@partner.samsung.com>
include/player_internal.h
packaging/capi-media-player.spec
test/CMakeLists.txt
test/event-handler/CMakeLists.txt [new file with mode: 0644]
test/event-handler/include/mm_navevent_handler.h [new file with mode: 0644]
test/event-handler/include/mm_navevent_handler_priv.h [new file with mode: 0644]
test/event-handler/include/mm_navevent_handler_util.h [new file with mode: 0644]
test/event-handler/src/mm_navevent_handler.c [new file with mode: 0644]
test/event-handler/src/mm_navevent_handler_internal.c [new file with mode: 0644]
test/player_test.c

index 02b4ed2..3f60d65 100644 (file)
@@ -533,19 +533,21 @@ int player_get_media_packet_video_frame_pool_size(player_h player, int *size);
 int player_enable_media_packet_video_frame_decoded_cb(player_h player, bool enable);
 
 /**
- * @brief Enables 360 video mode
+ * @brief Enables/disables 360 video mode.
  * @since_tizen 4.0
  * @details If it is @c true, the content will be displayed with 360 video mode.
  *          If it is @c false, the content will be displayed with full panorama mode. The default value is @c true.
  * @param[in] player   The handle to the media player
- * @param[in] enable   The 360 video display status : (@c true = display with 360 video mode, @c false = display with full panorama mode)
+ * @param[in] enable   The 360 video display status: @c true = display with 360 video mode,
+                       @c false = display with full panorama mode
  * @return @c 0 on success,
  *               otherwise a negative error value
  * @retval #PLAYER_ERROR_NONE              Successful
  * @retval #PLAYER_ERROR_INVALID_PARAMETER Invalid parameter
- * @pre The player state must be one of #PLAYER_STATE_IDLE, #PLAYER_STATE_READY, #PLAYER_STATE_PLAYING, or #PLAYER_STATE_PAUSED.
+ * @retval #PLAYER_ERROR_INVALID_OPERATION Not a spherical media
+ * @retval #PLAYER_ERROR_INVALID_STATE     Player isn't prepared
+ * @pre The player state must be one of #PLAYER_STATE_IDLE, #PLAYER_STATE_READY, #PLAYER_STATE_PLAYING or #PLAYER_STATE_PAUSED.
  * @see player_360_is_enabled()
- * @see player_360_set_direction_of_view()
  */
 int player_360_set_enable(player_h player, bool enable);
 
@@ -553,14 +555,16 @@ int player_360_set_enable(player_h player, bool enable);
  * @brief Gets the 360 video display status.
  * @since_tizen 4.0
  * @param[in]   player    The handle to the media player
- * @param[out]  enabled   The 360 video display status : (@c true = display with 360 video mode, @c false = display with full panorama mode)
+ * @param[out]  enabled   Pointer to store current 360 video display status:
+ *                        (@c true = display with 360 video mode,
+                          @c false = display with full panorama mode)
  * @return @c 0 on success,
  *         otherwise a negative error value
  * @retval #PLAYER_ERROR_NONE              Successful
  * @retval #PLAYER_ERROR_INVALID_PARAMETER Invalid parameter
- * @retval #PLAYER_ERROR_INVALID_OPERATION Invalid operation
- * @retval #PLAYER_ERROR_INVALID_STATE     Invalid player state
- * @pre The player state must be one of #PLAYER_STATE_IDLE, #PLAYER_STATE_READY, #PLAYER_STATE_PLAYING, or #PLAYER_STATE_PAUSED.
+ * @retval #PLAYER_ERROR_INVALID_OPERATION Not a spherical media
+ * @retval #PLAYER_ERROR_INVALID_STATE     Player isn't prepared
+ * @pre The player state must be one of #PLAYER_STATE_IDLE, #PLAYER_STATE_READY, #PLAYER_STATE_PLAYING or #PLAYER_STATE_PAUSED.
  * @see player_360_set_enable()
  */
 int player_360_is_enabled(player_h player, bool *enabled);
@@ -568,36 +572,45 @@ int player_360_is_enabled(player_h player, bool *enabled);
 /**
  * @brief Sets the 360 video direction of view.
  * @since_tizen 4.0
- * @details This function is to set the vertical and lateral axis value.
+ * @details This function is to set horizontal (yaw) and vertical (pitch) angles
+ *          of current direction of view in radians. Default direction of view
+ *          is taken from meta-data stored in the media. If meta-data omits
+ *          these values zeros are assumed that is equal to the centre of the
+ *          panorama image.
  * @param[in] player   The handle to the media player
- * @param[in] yaw      The vertical axis value
- * @param[in] pitch    The lateral axis value
+ * @param[in] yaw      The angle value around vertical axis. Valid values are in
+ *                     range +/- PI.
+ * @param[in] pitch    The angle value around lateral axis. Valid values are in
+ *                     range +/- PI/2.
  * @return @c 0 on success,
  *         otherwise a negative error value
- * @retval #PLAYER_ERROR_NONE                Successful
- * @retval #PLAYER_ERROR_INVALID_PARAMETER   Invalid parameter
- * @retval #PLAYER_ERROR_INVALID_OPERATION   Invalid operation
- * @retval #PLAYER_ERROR_INVALID_STATE       Invalid player state
- * @pre The player state must be one of: #PLAYER_STATE_READY, #PLAYER_STATE_PLAYING, or #PLAYER_STATE_PAUSED.
- * @see player_is_audio_only()
+ * @retval #PLAYER_ERROR_NONE              Successful
+ * @retval #PLAYER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #PLAYER_ERROR_INVALID_OPERATION Not a spherical media
+ * @retval #PLAYER_ERROR_INVALID_STATE     Player isn't prepared
+ * @pre The player state must be one of #PLAYER_STATE_IDLE, #PLAYER_STATE_READY, #PLAYER_STATE_PLAYING or #PLAYER_STATE_PAUSED.
+ * @see player_360_get_direction_of_view()
  */
 int player_360_set_direction_of_view(player_h player, float yaw, float pitch);
 
 /**
  * @brief Gets the 360 video direction of view.
  * @since_tizen 4.0
- * @details This function is to get the vertical and lateral axis value.
+ * @details This function is to get horizontal (yaw) and vertical (pitch) angles
+ *          of current direction of view in radians.
  * @param[in]  player   The handle to the media player
- * @param[out] yaw      The vertical axis value
- * @param[out] pitch    The lateral axis value
+ * @param[out] yaw      Pointer to store current value of direction of view
+ *                      angle around vertical axis
+ * @param[out] pitch    Pointer to store current value of direction of view
+ *                      angle around lateral axis
  * @return @c 0 on success,
  *         otherwise a negative error value
- * @retval #PLAYER_ERROR_NONE                Successful
- * @retval #PLAYER_ERROR_INVALID_PARAMETER   Invalid parameter
- * @retval #PLAYER_ERROR_INVALID_OPERATION   Invalid operation
- * @retval #PLAYER_ERROR_INVALID_STATE       Invalid player state
- * @pre The player state must be one of #PLAYER_STATE_IDLE, #PLAYER_STATE_READY, #PLAYER_STATE_PLAYING, or #PLAYER_STATE_PAUSED.
- * @see player_is_audio_only()
+ * @retval #PLAYER_ERROR_NONE              Successful
+ * @retval #PLAYER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #PLAYER_ERROR_INVALID_OPERATION Not a spherical media
+ * @retval #PLAYER_ERROR_INVALID_STATE     Player isn't prepared
+ * @pre The player state must be one of #PLAYER_STATE_IDLE, #PLAYER_STATE_READY, #PLAYER_STATE_PLAYING or #PLAYER_STATE_PAUSED.
+ * @see player_360_set_direction_of_view()
  */
 int player_360_get_direction_of_view(player_h player, float *yaw, float *pitch);
 
@@ -605,16 +618,17 @@ int player_360_get_direction_of_view(player_h player, float *yaw, float *pitch);
  * @brief Sets the zoom level of 360 video.
  * @since_tizen 4.0
  * @details  The zoom means scaling of the flat image cutted from the panorama.
- *           The valid range is from [TBD]. Default value is 1.0.
+ *           The valid range is from 1.0 to 10.0. Where 1.0 is actual image and
+ *           values above are zoom-in factor. Default value is 1.0 - no zoom.
  * @param[in] player    The handle to the media player
  * @param[in] level     The zoom level
  * @return @c 0 on success,
  *         otherwise a negative error value
- * @retval #PLAYER_ERROR_NONE                Successful
- * @retval #PLAYER_ERROR_INVALID_PARAMETER   Invalid parameter
- * @retval #PLAYER_ERROR_INVALID_OPERATION   Invalid operation
- * @retval #PLAYER_ERROR_INVALID_STATE       Invalid player state
- * @pre The player state must be one of #PLAYER_STATE_PLAYING, or #PLAYER_STATE_PAUSED.
+ * @retval #PLAYER_ERROR_NONE              Successful
+ * @retval #PLAYER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #PLAYER_ERROR_INVALID_OPERATION Not a spherical media
+ * @retval #PLAYER_ERROR_INVALID_STATE     Player isn't prepared
+ * @pre The player state must be one of #PLAYER_STATE_IDLE, #PLAYER_STATE_READY, #PLAYER_STATE_PLAYING or #PLAYER_STATE_PAUSED.
  * @see player_360_get_zoom()
  */
 int player_360_set_zoom(player_h player, float level);
@@ -623,16 +637,17 @@ int player_360_set_zoom(player_h player, float level);
  * @brief Gets the current zoom level of 360 video.
  * @since_tizen 4.0
  * @details  The zoom means scaling of the flat image cutted from the panorama.
- *           The valid range is from [TBD]. Default value is 1.0.
+ *           The valid range is from 1.0 to 10.0. Where 1.0 is actual image and
+ *           values above are zoom-in factor. Default value is 1.0 - no zoom.
  * @param[in]  player    The handle to the media player
- * @param[out] level     The zoom level
+ * @param[out] level     Pointer to store current value of zoom level
  * @return @c 0 on success,
  *         otherwise a negative error value
- * @retval #PLAYER_ERROR_NONE                Successful
- * @retval #PLAYER_ERROR_INVALID_PARAMETER   Invalid parameter
- * @retval #PLAYER_ERROR_INVALID_OPERATION   Invalid operation
- * @retval #PLAYER_ERROR_INVALID_STATE       Invalid player state
- * @pre The player state must be one of #PLAYER_STATE_PLAYING, or #PLAYER_STATE_PAUSED.
+ * @retval #PLAYER_ERROR_NONE              Successful
+ * @retval #PLAYER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #PLAYER_ERROR_INVALID_OPERATION Not a spherical media
+ * @retval #PLAYER_ERROR_INVALID_STATE     Player isn't prepared
+ * @pre The player state must be one of #PLAYER_STATE_IDLE, #PLAYER_STATE_READY, #PLAYER_STATE_PLAYING or #PLAYER_STATE_PAUSED.
  * @see player_360_set_zoom()
  */
 int player_360_get_zoom(player_h player, float *level);
@@ -641,16 +656,20 @@ int player_360_get_zoom(player_h player, float *level);
  * @brief Sets the field of view information of 360 video.
  * @since_tizen 4.0
  * @details This function is to set the field of view to decide the output frame size.
+ *          Note: values above the default ones extends field of view to significantly
+ *          distorted areas and thus don't make real sense.
  * @param[in] player              The handle to the media player
- * @param[in] horizontal_degrees  The horizontal degrees to display (The default value is [TBD])
- * @param[in] vertical_degrees    The vertical degrees to display (The default value is [TBD])
+ * @param[in] horizontal_degrees  The horizontal field of view to display in degrees.
+ *                                Valid range is 1-360 degrees. Default value is 120 degrees.
+ * @param[in] vertical_degrees    The vertical  field of view to display in degrees.
+ *                                Valid range is 1-180 degrees. Default value is 67 degrees.
  * @return @c 0 on success,
  *         otherwise a negative error value
- * @retval #PLAYER_ERROR_NONE                Successful
- * @retval #PLAYER_ERROR_INVALID_PARAMETER   Invalid parameter
- * @retval #PLAYER_ERROR_INVALID_OPERATION   Invalid operation
- * @retval #PLAYER_ERROR_INVALID_STATE       Invalid player state
- * @pre The player state must be one of #PLAYER_STATE_IDLE, #PLAYER_STATE_READY, #PLAYER_STATE_PLAYING, or #PLAYER_STATE_PAUSED.
+ * @retval #PLAYER_ERROR_NONE              Successful
+ * @retval #PLAYER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #PLAYER_ERROR_INVALID_OPERATION Not a spherical media
+ * @retval #PLAYER_ERROR_INVALID_STATE     Player isn't prepared
+ * @pre The player state must be one of #PLAYER_STATE_IDLE, #PLAYER_STATE_READY, #PLAYER_STATE_PLAYING or #PLAYER_STATE_PAUSED.
  * @see player_360_get_field_of_view()
  */
 int player_360_set_field_of_view(player_h player, int horizontal_degrees, int vertical_degrees);
@@ -660,15 +679,17 @@ int player_360_set_field_of_view(player_h player, int horizontal_degrees, int ve
  * @since_tizen 4.0
  * @details This function is to get the field of view information.
  * @param[in]  player              The handle to the media player
- * @param[out] horizontal_degrees  The horizontal degrees to display (The default value is [TBD])
- * @param[out] vertical_degrees    The vertical degrees to display (The default value is [TBD])
+ * @param[out] horizontal_degrees  Pointer to store current value of horizontal
+ *                                 field of view to display in degrees.
+ * @param[out] vertical_degrees    Pointer to store current value of vertical
+ *                                 field of view to display in degrees.
  * @return @c 0 on success,
  *         otherwise a negative error value
- * @retval #PLAYER_ERROR_NONE                Successful
- * @retval #PLAYER_ERROR_INVALID_PARAMETER   Invalid parameter
- * @retval #PLAYER_ERROR_INVALID_OPERATION   Invalid operation
- * @retval #PLAYER_ERROR_INVALID_STATE       Invalid player state
- * @pre The player state must be one of #PLAYER_STATE_IDLE, #PLAYER_STATE_READY, #PLAYER_STATE_PLAYING, or #PLAYER_STATE_PAUSED.
+ * @retval #PLAYER_ERROR_NONE              Successful
+ * @retval #PLAYER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #PLAYER_ERROR_INVALID_OPERATION Not a spherical media
+ * @retval #PLAYER_ERROR_INVALID_STATE     Player isn't prepared
+ * @pre The player state must be one of #PLAYER_STATE_IDLE, #PLAYER_STATE_READY, #PLAYER_STATE_PLAYING or #PLAYER_STATE_PAUSED.
  * @see player_360_set_field_of_view()
  */
 int player_360_get_field_of_view(player_h player, int *horizontal_degrees, int *vertical_degrees);
index 9423d7d..ca70726 100644 (file)
@@ -25,6 +25,7 @@ BuildRequires:  pkgconfig(libtbm)
 BuildRequires:  pkgconfig(eom)
 BuildRequires:  pkgconfig(storage)
 BuildRequires:  pkgconfig(capi-system-info)
+BuildRequires:  pkgconfig(libinput)
 %if "%{TIZEN_PRODUCT_TV}" != "1"
 BuildRequires:  pkgconfig(mm-evas-renderer)
 %endif
@@ -120,3 +121,4 @@ cp test/player_audio_test %{buildroot}/usr/bin
 %{_bindir}/player_media_packet_test
 %{_bindir}/player_es_push_test
 %{_bindir}/player_audio_test
+%{_libdir}/libmm-navevent-handler.so*
index dba7898..674431a 100644 (file)
@@ -7,12 +7,13 @@ ELSE (TIZEN_WEARABLE)
 INCLUDE_DIRECTORIES(../include/common)
 ENDIF (TIZEN_WEARABLE)
 
+INCLUDE_DIRECTORIES(event-handler/include)
 link_directories(${CMAKE_SOURCE_DIR}/../)
 
 SET(WIN_PKG "${WIN_PKG} ecore-wayland")
 
 INCLUDE(FindPkgConfig)
-pkg_check_modules(${fw_test} REQUIRED appcore-efl elementary ecore evas capi-media-sound-manager ${WIN_PKG})
+pkg_check_modules(${fw_test} REQUIRED libudev libinput capi-system-info appcore-efl elementary ecore evas capi-media-sound-manager ${WIN_PKG})
 FOREACH(flag ${${fw_test}_CFLAGS})
     SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
 ENDFOREACH(flag)
@@ -24,7 +25,8 @@ FOREACH(src ${sources})
     GET_FILENAME_COMPONENT(src_name ${src} NAME_WE)
     MESSAGE("${src_name}")
     ADD_EXECUTABLE(${src_name} ${src})
-    TARGET_LINK_LIBRARIES(${src_name} capi-media-player ${${fw_test}_LDFLAGS})
+    TARGET_LINK_LIBRARIES(${src_name} capi-media-player mm-navevent-handler ${${fw_test}_LDFLAGS})
     INSTALL(TARGETS ${src_name} DESTINATION bin)
 ENDFOREACH()
 
+ADD_SUBDIRECTORY(event-handler)
diff --git a/test/event-handler/CMakeLists.txt b/test/event-handler/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e6e4c78
--- /dev/null
@@ -0,0 +1,26 @@
+
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+SET(fw_handler "mm-navevent-handler")
+
+PROJECT(${fw_handler})
+
+SET(dependents "dlog libudev libinput capi-system-info")
+SET(pc_dependents "libudev libinput capi-system-info")
+
+INCLUDE(FindPkgConfig)
+pkg_check_modules(${fw_handler} REQUIRED ${dependents})
+FOREACH(flag ${${fw_handler}_CFLAGS})
+    SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+AUX_SOURCE_DIRECTORY (./src HANDLER_SRC)
+ADD_LIBRARY(${fw_handler} SHARED ${HANDLER_SRC})
+
+SET_TARGET_PROPERTIES(${fw_handler}
+     PROPERTIES
+     VERSION ${FULLVER}
+     SOVERSION ${MAJORVER}
+     CLEAN_DIRECT_OUTPUT 1
+)
+
+INSTALL(TARGETS ${fw_handler} DESTINATION ${LIB_INSTALL_DIR})
diff --git a/test/event-handler/include/mm_navevent_handler.h b/test/event-handler/include/mm_navevent_handler.h
new file mode 100644 (file)
index 0000000..99d9510
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *     @author: Volodymyr Brynza <v dot brynza at samsung dot com>
+ *                                      Andriy Martynets <a dot martynets at partner dot samsung dot com>
+ *
+ * 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.
+ */
+
+#ifndef __MM_NAVEVENT_HANDLER__
+#define __MM_NAVEVENT_HANDLER__
+
+#include <tizen.h>
+#include <libinput.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * @file mm_navevent_handler.h
+ * @brief Navigation event handler handle type.
+ *
+ * @since_tizen 3.0
+ */
+typedef void *mm_navevent_handler_h;
+
+/**
+ * @brief Convenience type for screen/window sizes representation.
+ *
+ * @since_tizen 3.0
+ */
+typedef struct {
+       int width;
+       int height;
+} mm_navevent_handler_size_s;
+
+/**
+ * @brief Enumeration of window orientation relatively to screen
+ *                             counter-clockwise for event handling. Valid values of this enum
+ *                             complies with corresponding enumeration of walandsink's rotate
+ *                             property.
+ *
+ * @since_tizen 3.0
+ */
+typedef enum {
+       MM_NAVEVENT_HANDLER_ORIENTATION_INVALID         = -1,
+       MM_NAVEVENT_HANDLER_ORIENTATION_DEGREE_0         = 0,
+       MM_NAVEVENT_HANDLER_ORIENTATION_DEGREE_90       = 1,
+       MM_NAVEVENT_HANDLER_ORIENTATION_DEGREE_180 = 2,
+       MM_NAVEVENT_HANDLER_ORIENTATION_DEGREE_270 = 3,
+       MM_NAVEVENT_HANDLER_ORIENTATION_MAX,
+} mm_navevent_handler_orientation_e;
+
+/**
+ * @brief Constants for default values of Field-Of-View angles in degrees.
+ * @details Human eye horizontal stereoscopic FOV is 120 degrees.
+ *                                     With image aspect ratio of 16:9 vertical FOV is roughly 68 degrees.
+ * @since_tizen 3.0
+ */
+#define MM_NAVEVENT_HANDLER_DEFAULT_HORIZONTAL_FOV_DEGREE                                       120
+#define MM_NAVEVENT_HANDLER_DEFAULT_VERTICAL_FOV_DEGREE                                                 68
+
+/**
+ * @brief Enumeration of device for event handling.
+ * @remarks Note that available device types can differ depending
+ *                                     on platform. #MM_NAVEVENT_HANDLER_DEVICE_TYPE_INVALID and
+ *                                     #MM_NAVEVENT_HANDLER_DEVICE_TYPE_MAX are invalid device types.
+ * @since_tizen 3.0
+ */
+typedef enum {
+       MM_NAVEVENT_HANDLER_DEVICE_TYPE_INVALID = -1,
+       MM_NAVEVENT_HANDLER_DEVICE_TYPE_TOUCH,
+       MM_NAVEVENT_HANDLER_DEVICE_TYPE_MAX,
+} mm_navevent_handler_device_type_e;
+
+/**
+ * @brief Enumeration for navigation event handler error.
+ *
+ * @since_tizen 3.0
+ */
+typedef enum {
+       MM_NAVEVENT_HANDLER_ERROR_NONE = TIZEN_ERROR_NONE,                                                                                                                                                              /**< Successful */
+       MM_NAVEVENT_HANDLER_ERROR_INVALID_PARAMETER = TIZEN_ERROR_INVALID_PARAMETER,                                                    /**< Invalid parameter */
+       MM_NAVEVENT_HANDLER_ERROR_INVALID_OPERATION     = TIZEN_ERROR_INVALID_OPERATION,                                                 /**< Invalid operation */
+       MM_NAVEVENT_HANDLER_ERROR_FILE_NO_SPACE_ON_DEVICE = TIZEN_ERROR_FILE_NO_SPACE_ON_DEVICE,        /**< No space left on the device */
+       MM_NAVEVENT_HANDLER_ERROR_NOT_SUPPORTED = TIZEN_ERROR_NOT_SUPPORTED,                                                                             /**< The feature is not supported */
+       MM_NAVEVENT_HANDLER_ERROR_INVALID_STATE,                                                                                                                                                                                                        /**< Invalid state */
+} mm_navenvent_handler_error_e;
+
+/**
+ * @brief Called after processing each input event.
+ * @since_tizen 3.0
+ * @param [in] ev_t            Type of the libinput event
+ * @param [in] x                        Value of pointer horizontal position in screen
+ *                                                                              coordinates
+ * @param [in] y                        Value of pointer vertical position in screen coordinates
+ * @param [in] data            The user data passed from the code where
+ *                                                                              mm_navevent_handler_set_cb() was invoked
+ *                                                                              This data will be accessible from mm_navevent_handler_cb
+ * @param [in] e                        Euler angles (XYZ configuration)
+ * @remarks @a Pointer coordinates are passed as screen coordinates regardless
+ *                                              given application window size and orientation
+ * @pre @a handle must be created by calling mm_navevent_handler_create()
+ * @see mm_navevent_handler_set_cb()
+ * @see mm_navevent_handler_unset_cb()
+ */
+typedef void (*mm_navevent_handler_cb) (enum libinput_event_type ev_t, int x, int y, void* data, float e[3]);
+
+/**
+ * @brief Creates an instance of navigation event handler and
+ *                             passes the handle to the caller.
+ * @since_tizen 3.0
+ * @remarks @a handle must be released by calling mm_navevent_handler_destroy()
+ * @param [out] handle Event handler handle
+ * @param [in] device  Device type
+ * @return @c 0 on success,
+ *                              otherwise a negative error value
+ * @retval #MM_NAVEVENT_HANDLER_ERROR_NONE Successful
+ * @retval #MM_NAVEVENT_HANDLER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MM_NAVEVENT_HANDLER_ERROR_INVALID_OPERATION Invalid operation
+ * @retval #MM_NAVEVENT_HANDLER_ERROR_INVALID_STATE Invalid state
+ * @see mm_navevent_handler_destroy()
+ * @see mm_navevent_handler_device_type_e
+ */
+int mm_navevent_handler_create(mm_navevent_handler_h *handle, mm_navevent_handler_device_type_e device);
+
+/**
+ * @brief Destroys event handler.
+ * @since_tizen 3.0
+ * @param [in] handle          Event handler handle
+ * @return @c 0 on success,
+ *                              otherwise a negative error value
+ * @retval #MM_NAVEVENT_HANDLER_ERROR_NONE Successful
+ * @retval #MM_NAVEVENT_HANDLER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #MM_NAVEVENT_HANDLER_ERROR_INVALID_OPERATION Invalid operation
+ * @pre @a handle must be created by calling mm_navevent_handler_create()
+ * @see mm_navevent_handler_create()
+ */
+int mm_navevent_handler_destroy(mm_navevent_handler_h handle);
+
+/**
+ * @brief Sets navigation event callback.
+ * @since_tizen 3.0
+ * @param [in] handle          Event handler handle
+ * @param [in] cb                              Navigation event callback
+ * @param [in] data                    Data that will be passed to callback
+ * @return @c 0 on success,
+ *                              otherwise a negative error value
+ * @retval #MM_NAVEVENT_HANDLER_ERROR_NONE Successful
+ * @retval #MM_NAVEVENT_HANDLER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @pre @a handle must be created by calling mm_navevent_handler_create()
+ * @see mm_navevent_handler_create()
+ */
+int mm_navevent_handler_set_cb(mm_navevent_handler_h handle, mm_navevent_handler_cb cb, void *data);
+
+/**
+ * @brief Unsets navigation event callback.
+ * @since_tizen 3.0
+ * @param [in] handle          Event handler handle
+ * @return @c 0 on success,
+ *                              otherwise a negative error value
+ * @retval #MM_NAVEVENT_HANDLER_ERROR_NONE Successful
+ * @retval #MM_NAVEVENT_HANDLER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @pre @a handle must be created by calling mm_navevent_handler_create()
+ * @see mm_navevent_handler_create()
+ */
+int mm_navevent_handler_unset_cb(mm_navevent_handler_h handle);
+
+/**
+ * @brief Sets application window orientation and size.
+ * @details Window size defines the step of FOV degrees. Window orientation
+ *                                     defines x and y coordinates exchange and coordinates
+ *                                     increment/decrement direction.
+ * @since_tizen 3.0
+ * @param [in] handle                  Event handler handle
+ * @param [in] wo                                      Window orientation
+ * @param [in] ws                                      Window size
+ * @return @c 0 on success,
+ *                              otherwise a negative error value
+ * @retval #MM_NAVEVENT_HANDLER_ERROR_NONE Successful
+ * @retval #MM_NAVEVENT_HANDLER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @pre @a handle must be created by calling mm_navevent_handler_create()
+ * @see mm_navevent_handler_create()
+ */
+int mm_navevent_handler_set_window_parameters(mm_navevent_handler_h handle, mm_navevent_handler_orientation_e wo, mm_navevent_handler_size_s ws);
+
+/**
+ * @brief Sets Field-Of-View size in degrees.
+ * @details Field-Of-View size defines scale factor from pixels to angles.
+ * @since_tizen 3.0
+ * @param [in] handle                  Event handler handle
+ * @param [in] fovs                            Field-Of-View size in degrees
+ * @return @c 0 on success,
+ *                              otherwise a negative error value
+ * @retval #MM_NAVEVENT_HANDLER_ERROR_NONE Successful
+ * @retval #MM_NAVEVENT_HANDLER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @pre @a handle must be created by calling mm_navevent_handler_create()
+ * @see mm_navevent_handler_create()
+ */
+int mm_navevent_handler_set_fov_size(mm_navevent_handler_h handle, mm_navevent_handler_size_s fovs);
+
+/**
+ * @brief Returns screen size in pixels.
+ * @details Provides application with screen size information.
+ * @since_tizen 3.0
+ * @param [in] handle                  Event handler handle
+ * @param [in] size                            Pointer to where the size to store
+ * @return @c 0 on success,
+ *                              otherwise a negative error value
+ * @retval #MM_NAVEVENT_HANDLER_ERROR_NONE Successful
+ * @retval #MM_NAVEVENT_HANDLER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @pre @a handle must be created by calling mm_navevent_handler_create()
+ * @see mm_navevent_handler_create()
+ */
+int mm_navevent_handler_get_screen_size(mm_navevent_handler_h handle, mm_navevent_handler_size_s *size);
+
+/**
+ * @brief Sets direction of view angles (yaw, pitch and roll) in radians.
+ * @details Subsequent navigation events modify these values.
+ * @since_tizen 3.0
+ * @param [in] handle                  Event handler handle
+ * @param [in] angles                  array of 3 angles in radians (yaw, pitch and roll)
+ * @return @c 0 on success,
+ *                              otherwise a negative error value
+ * @retval #MM_NAVEVENT_HANDLER_ERROR_NONE Successful
+ * @retval #MM_NAVEVENT_HANDLER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @pre @a handle must be created by calling mm_navevent_handler_create()
+ * @see mm_navevent_handler_create()
+ */
+int mm_navevent_handler_set_angles(mm_navevent_handler_h handle, float angles[3]);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __MM_NAVEVENT_HANDLER__ */
diff --git a/test/event-handler/include/mm_navevent_handler_priv.h b/test/event-handler/include/mm_navevent_handler_priv.h
new file mode 100644 (file)
index 0000000..8f637f9
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *     @author: Volodymyr Brynza <v dot brynza at samsung dot com>
+ *                                      Andriy Martynets <a dot martynets at partner dot samsung dot com>
+ *
+ * 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.
+ */
+
+#ifndef __MM_NAVEVENT_HANDLER_PRIV__
+#define __MM_NAVEVENT_HANDLER_PRIV__
+
+#include <libinput.h>
+#include <libudev.h>
+#include <pthread.h>
+
+#include "mm_navevent_handler.h"
+
+typedef struct _mm_navevent_handler_s mm_navevent_handler_s;
+
+struct _mm_navevent_handler_s {
+       struct libinput *lih;
+       mm_navevent_handler_device_type_e dev;
+       mm_navevent_handler_orientation_e window_orientation;
+       mm_navevent_handler_size_s window_size; /*<< Application window size in pixels */
+       mm_navevent_handler_size_s fov;                                 /*<< Field-Of-View size in degrees */
+       mm_navevent_handler_size_s screen_size; /*<< Screen size in pixels */
+
+       unsigned int current[2];
+       unsigned int last[2];
+
+       float angles[3];                                                                                                 /*<< yaw, pitch and roll angles */
+
+       mm_navevent_handler_cb cb;
+       void *user_data;
+
+       pthread_t event_thread;
+       pthread_mutex_t event_lock;
+       int event_loop_run;
+};
+
+mm_navevent_handler_s *_mm_navevent_handler_create(mm_navevent_handler_device_type_e device);
+int _mm_navevent_handler_destroy(mm_navevent_handler_s *handler);
+int _mm_navevent_handler_set_cb(mm_navevent_handler_s *handler, mm_navevent_handler_cb cb, void *data);
+int _mm_navevent_handler_unset_cb(mm_navevent_handler_s *handler);
+int _mm_navevent_handler_set_window_parameters(mm_navevent_handler_s *handler, mm_navevent_handler_orientation_e wo, mm_navevent_handler_size_s ws);
+int _mm_navevent_handler_set_fov_size(mm_navevent_handler_s *handler, mm_navevent_handler_size_s fovs);
+int _mm_navevent_handler_get_screen_size(mm_navevent_handler_s *handler, mm_navevent_handler_size_s *size);
+int _mm_navevent_handler_set_angles(mm_navevent_handler_s *handler, float angles[3]);
+
+#endif /* __MM_NAVEVENT_HANDLER_PRIV__ */
diff --git a/test/event-handler/include/mm_navevent_handler_util.h b/test/event-handler/include/mm_navevent_handler_util.h
new file mode 100644 (file)
index 0000000..5a30179
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *     @author: Volodymyr Brynza <v dot brynza at samsung dot com>
+ *                                      Andriy Martynets <a dot martynets at partner dot samsung dot com>
+ *
+ * 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.
+ */
+
+#ifndef __MM_NAVEVENT_HANDLER_UTIL__
+#define __MM_NAVEVENT_HANDLER_UTIL__
+
+#include <dlog.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+
+#define LOG_TAG "MM_NAVEVENT_HANDLER"
+
+#define FONT_COLOR_RESET               "\033[0m"
+#define FONT_COLOR_RED                 "\033[31m"
+#define FONT_COLOR_GREEN               "\033[32m"
+
+#define MM_EH_DEBUG(fmt, arg...)                                                                                                \
+       do {                                                                                                                                                                                    \
+               LOGD(FONT_COLOR_RESET""fmt""FONT_COLOR_RESET, ##arg);                            \
+       } while (0)
+
+#define MM_EH_INFO(fmt, arg...)                                                                                                        \
+       do {                                                                                                                                                                                    \
+               LOGI(FONT_COLOR_GREEN""fmt""FONT_COLOR_RESET, ##arg);                            \
+       } while (0)
+
+#define MM_EH_ERROR(fmt, arg...)                                                                                                \
+       do {                                                                                                                                                                                    \
+               LOGE(FONT_COLOR_RED""fmt""FONT_COLOR_RESET, ##arg);                                      \
+       } while (0)
+
+#define MM_EH_RETM_IF(expr, fmt, arg...)                                                                \
+       do {                                                                                                                                                                                    \
+               if (expr) {                                                                                                                                      \
+                       LOGE(FONT_COLOR_RED""fmt""FONT_COLOR_RESET, ##arg);                      \
+                       return;                                                                                                                                  \
+               }                                                                                                                                                                                \
+       } while (0)
+
+#define MM_EH_RETVM_IF(expr, val, fmt, arg...)                                  \
+       do {                                                                                                                                                                                    \
+               if (expr) {                                                                                                                                      \
+                       LOGE(FONT_COLOR_RED""fmt""FONT_COLOR_RESET, ##arg);                      \
+                       return(val);                                                                                                                    \
+               }                                                                                                                                                                                \
+       } while (0)
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MM_NAVEVENT_HANDLER_UTIL__ */
diff --git a/test/event-handler/src/mm_navevent_handler.c b/test/event-handler/src/mm_navevent_handler.c
new file mode 100644 (file)
index 0000000..c6e4fff
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *     @author: Volodymyr Brynza <v dot brynza at samsung dot com>
+ *                                      Andriy Martynets <a dot martynets at partner dot samsung dot com>
+ *
+ * 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 "mm_navevent_handler_util.h"
+#include "mm_navevent_handler_priv.h"
+
+int mm_navevent_handler_create(mm_navevent_handler_h *handle, mm_navevent_handler_device_type_e device)
+{
+       MM_EH_RETVM_IF(device <= MM_NAVEVENT_HANDLER_DEVICE_TYPE_INVALID ||
+               device >= MM_NAVEVENT_HANDLER_DEVICE_TYPE_MAX, MM_NAVEVENT_HANDLER_ERROR_INVALID_PARAMETER,
+               "Wrong device type");
+
+       *handle = _mm_navevent_handler_create(device);
+       if (NULL == *handle)
+               return MM_NAVEVENT_HANDLER_ERROR_INVALID_OPERATION;
+
+       return MM_NAVEVENT_HANDLER_ERROR_NONE;
+}
+
+int mm_navevent_handler_destroy(mm_navevent_handler_h handle)
+{
+       MM_EH_RETVM_IF(NULL == handle, MM_NAVEVENT_HANDLER_ERROR_INVALID_PARAMETER,
+               "Handle is NULL");
+
+       mm_navevent_handler_s *handler = (mm_navevent_handler_s *)handle;
+
+       return _mm_navevent_handler_destroy(handler);
+}
+
+int mm_navevent_handler_set_cb(mm_navevent_handler_h handle, mm_navevent_handler_cb cb, void *data)
+{
+       MM_EH_RETVM_IF(NULL == handle, MM_NAVEVENT_HANDLER_ERROR_INVALID_PARAMETER,
+               "Handle is NULL");
+       MM_EH_RETVM_IF(NULL == cb, MM_NAVEVENT_HANDLER_ERROR_INVALID_PARAMETER,
+               "cb is NULL");
+
+       mm_navevent_handler_s *handler = (mm_navevent_handler_s *)handle;
+
+       return _mm_navevent_handler_set_cb(handler, cb, data);
+}
+
+int mm_navevent_handler_unset_cb(mm_navevent_handler_h handle)
+{
+       MM_EH_RETVM_IF(NULL == handle, MM_NAVEVENT_HANDLER_ERROR_INVALID_PARAMETER,
+               "Handle is NULL");
+
+       mm_navevent_handler_s *handler = (mm_navevent_handler_s *)handle;
+
+       return _mm_navevent_handler_unset_cb(handler);
+}
+
+int mm_navevent_handler_set_window_parameters(mm_navevent_handler_h handle, mm_navevent_handler_orientation_e wo, mm_navevent_handler_size_s ws)
+{
+       MM_EH_RETVM_IF(NULL == handle, MM_NAVEVENT_HANDLER_ERROR_INVALID_PARAMETER,
+               "Handle is NULL");
+
+       mm_navevent_handler_s *handler = (mm_navevent_handler_s *)handle;
+
+       return _mm_navevent_handler_set_window_parameters(handler, wo, ws);
+}
+
+int mm_navevent_handler_set_fov_size(mm_navevent_handler_h handle, mm_navevent_handler_size_s fovs)
+{
+       MM_EH_RETVM_IF(NULL == handle, MM_NAVEVENT_HANDLER_ERROR_INVALID_PARAMETER,
+               "Handle is NULL");
+
+       mm_navevent_handler_s *handler = (mm_navevent_handler_s *)handle;
+
+       return _mm_navevent_handler_set_fov_size(handler, fovs);
+}
+
+int mm_navevent_handler_get_screen_size(mm_navevent_handler_h handle, mm_navevent_handler_size_s *size)
+{
+       MM_EH_RETVM_IF(NULL == handle, MM_NAVEVENT_HANDLER_ERROR_INVALID_PARAMETER,
+               "Handle is NULL");
+
+       MM_EH_RETVM_IF(NULL == size, MM_NAVEVENT_HANDLER_ERROR_INVALID_PARAMETER,
+               "Structure address is NULL");
+
+       mm_navevent_handler_s *handler = (mm_navevent_handler_s *)handle;
+
+       return _mm_navevent_handler_get_screen_size(handler, size);
+}
+
+int mm_navevent_handler_set_angles(mm_navevent_handler_h handle, float angles[3])
+{
+       MM_EH_RETVM_IF(NULL == handle, MM_NAVEVENT_HANDLER_ERROR_INVALID_PARAMETER,
+               "Handle is NULL");
+
+       MM_EH_RETVM_IF(NULL == angles, MM_NAVEVENT_HANDLER_ERROR_INVALID_PARAMETER,
+               "Array address is NULL");
+
+       mm_navevent_handler_s *handler = (mm_navevent_handler_s *)handle;
+
+       return _mm_navevent_handler_set_angles(handler, angles);
+}
diff --git a/test/event-handler/src/mm_navevent_handler_internal.c b/test/event-handler/src/mm_navevent_handler_internal.c
new file mode 100644 (file)
index 0000000..36f5ee4
--- /dev/null
@@ -0,0 +1,382 @@
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *     @author: Volodymyr Brynza <v dot brynza at samsung dot com>
+ *                                      Andriy Martynets <a dot martynets at partner dot samsung dot com>
+ *
+ * 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.
+ */
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <system_info.h>
+#include <poll.h>
+#include <math.h>
+
+#include "mm_navevent_handler_priv.h"
+#include "mm_navevent_handler_util.h"
+
+const char *UDEV_SEAT = "seat0";
+
+static int __mm_navevent_handler_input_open(const char *path, int flags, void *user_data)
+{
+       int fd = open(path, flags);
+       return fd < 0 ? -errno : fd;
+}
+
+static void __mm_navevent_handler_input_close(int fd, void *user_data)
+{
+       close(fd);
+}
+
+static const struct libinput_interface iface = {
+       .open_restricted = __mm_navevent_handler_input_open,
+       .close_restricted = __mm_navevent_handler_input_close,
+};
+
+static int __mm_navevent_handler_init_input(mm_navevent_handler_s *handler)
+{
+       struct udev *udev = udev_new();
+       if (NULL == udev) {
+               MM_EH_ERROR("Failed to initialize udev");
+               return MM_NAVEVENT_HANDLER_ERROR_INVALID_OPERATION;
+       }
+
+       handler->lih = libinput_udev_create_context(&iface, NULL, udev);
+       if (NULL == handler->lih) {
+               MM_EH_ERROR("Failed to initialize libinput context from udev");
+               udev_unref(udev);
+               return MM_NAVEVENT_HANDLER_ERROR_INVALID_OPERATION;
+       }
+
+       if (libinput_udev_assign_seat(handler->lih, UDEV_SEAT)) {
+               MM_EH_ERROR("Failed to set seat for context");
+               libinput_unref(handler->lih);
+               handler->lih = NULL;
+               udev_unref(udev);
+               return MM_NAVEVENT_HANDLER_ERROR_INVALID_OPERATION;
+       }
+
+       return MM_NAVEVENT_HANDLER_ERROR_NONE;
+}
+
+static void set_initial_coordinates(mm_navevent_handler_s *handler, struct libinput_event *ev)
+{
+       struct libinput_event_touch *t = libinput_event_get_touch_event(ev);
+       MM_EH_DEBUG("Got touch down event. Time - %f sec", libinput_event_touch_get_time(t)/1000.0);
+
+       pthread_mutex_lock(&handler->event_lock);
+
+       handler->last[0] = libinput_event_touch_get_x(t);
+       handler->last[1] = libinput_event_touch_get_y(t);
+
+       if (handler->cb) {
+               /* Below call simulates mouse depressed move */
+               handler->cb(LIBINPUT_EVENT_TOUCH_MOTION, handler->last[0], handler->last[1], handler->user_data, handler->angles);
+               handler->cb(libinput_event_get_type(ev), handler->last[0], handler->last[1], handler->user_data, handler->angles);
+       }
+
+       pthread_mutex_unlock(&handler->event_lock);
+}
+
+static void set_final_coordinates(mm_navevent_handler_s *handler, struct libinput_event *ev)
+{
+       struct libinput_event_touch *t = libinput_event_get_touch_event(ev);
+       MM_EH_DEBUG("Got touch up event. Time - %f sec", libinput_event_touch_get_time(t)/1000.0);
+
+       pthread_mutex_lock(&handler->event_lock);
+
+       if (handler->cb)
+               handler->cb(libinput_event_get_type(ev), handler->current[0], handler->current[1], handler->user_data, handler->angles);
+
+       pthread_mutex_unlock(&handler->event_lock);
+}
+
+/* NOTE: pointer slides the image, thus the direction of view moves in opposit
+ * direction.
+ * Orientation (degrees):
+ * 0           yaw: last_x - x; pitch: y - last_y;
+ *     0,0 ------> X
+ *     |
+ *     |
+ *     |
+ *     v Y
+ *
+ * 90   yaw: y - last_y; pitch: x - last_x;
+ *     Y <------ 0,0
+ *                                                     |
+ *                                                     |
+ *                                                     |
+ *                                             X v
+ *
+ * 180 yaw: x - last_x; pitch: last_y - y;
+ *                                             Y ^
+ *                                                     |
+ *                                                     |
+ *                                                     |
+ *     X <------ 0,0
+ *
+ * 270 yaw: last_y - y; pitch: last_x - x;
+ *     ^ X
+ *     |
+ *     |
+ *     |
+ *     0,0 ------> Y
+ */
+static void update_angles(mm_navevent_handler_s *handler)
+{
+       float x, last_x, y, last_y;
+
+       switch (handler->window_orientation) {
+       case MM_NAVEVENT_HANDLER_ORIENTATION_DEGREE_0:
+               x = handler->last[0];
+               last_x = handler->current[0];
+               y = handler->current[1];
+               last_y = handler->last[1];
+               break;
+       case MM_NAVEVENT_HANDLER_ORIENTATION_DEGREE_90:
+               x = handler->current[1];
+               last_x = handler->last[1];
+               y = handler->current[0];
+               last_y = handler->last[0];
+               break;
+       case MM_NAVEVENT_HANDLER_ORIENTATION_DEGREE_180:
+               x = handler->current[0];
+               last_x = handler->last[0];
+               y = handler->last[1];
+               last_y = handler->current[1];
+               break;
+       case MM_NAVEVENT_HANDLER_ORIENTATION_DEGREE_270:
+               x = handler->last[1];
+               last_x = handler->current[1];
+               y = handler->last[0];
+               last_y = handler->current[0];
+               break;
+       default:
+               return;
+       }
+
+       handler->angles[0] +=   ((float)handler->fov.width * (x - last_x) / (float)handler->window_size.width) * M_PI / 180.0;
+       handler->angles[1] += ((float)handler->fov.height * (y - last_y) / (float)handler->window_size.height) * M_PI / 180.0;
+
+       /* Fix yaw angle */
+       if (handler->angles[0] <= -(M_PI)) handler->angles[0] += 2 * M_PI;
+       else if (handler->angles[0] > M_PI) handler->angles[0] -= 2 * M_PI;
+
+       /* Fix pitch angle */
+       if (handler->angles[1] < -(M_PI_2)) handler->angles[1] = -(M_PI_2);
+       else if (handler->angles[1] > M_PI_2) handler->angles[1] = M_PI_2;
+}
+
+static void process_move(mm_navevent_handler_s *handler, struct libinput_event *ev)
+{
+       struct libinput_event_touch *t = libinput_event_get_touch_event(ev);
+       MM_EH_DEBUG("Got touch event. Time - %f sec", libinput_event_touch_get_time(t)/1000.0);
+
+       pthread_mutex_lock(&handler->event_lock);
+
+       handler->current[0] = libinput_event_touch_get_x(t);
+       handler->current[1] = libinput_event_touch_get_y(t);
+
+       update_angles(handler);
+
+       handler->last[0] = handler->current[0];
+       handler->last[1] = handler->current[1];
+
+       if (handler->cb) {
+               MM_EH_INFO("Sending position to user code");
+               handler->cb(libinput_event_get_type(ev), handler->current[0], handler->current[1], handler->user_data, handler->angles);
+       }
+
+       pthread_mutex_unlock(&handler->event_lock);
+}
+
+static void handle_events(mm_navevent_handler_s *handler)
+{
+       struct libinput_event *ev = NULL;
+
+       MM_EH_INFO("Process events");
+
+       libinput_dispatch(handler->lih);
+
+       while ((ev = libinput_get_event(handler->lih))) {
+               switch (libinput_event_get_type(ev)) {
+               case LIBINPUT_EVENT_TOUCH_MOTION:
+                       MM_EH_DEBUG("Got touch event with coords");
+                       /* Process coordinates and send event */
+                       if (handler->dev == MM_NAVEVENT_HANDLER_DEVICE_TYPE_TOUCH)
+                               process_move(handler, ev);
+                       break;
+               case LIBINPUT_EVENT_TOUCH_DOWN:
+                       if (handler->dev == MM_NAVEVENT_HANDLER_DEVICE_TYPE_TOUCH)
+                               set_initial_coordinates(handler, ev);
+                       break;
+               case LIBINPUT_EVENT_TOUCH_UP:
+                       if (handler->dev == MM_NAVEVENT_HANDLER_DEVICE_TYPE_TOUCH)
+                               set_final_coordinates(handler, ev);
+                       /* Do nothing as we don't get any coordinates here */
+                       MM_EH_DEBUG("Got touch event");
+                       break;
+               default:
+                       break;
+               }
+
+               libinput_event_destroy(ev);
+               libinput_dispatch(handler->lih);
+       }
+}
+
+static void *__mm_navevent_handler_handle_event(void *data)
+{
+       struct pollfd fds;
+       mm_navevent_handler_s *handler = (mm_navevent_handler_s *)data;
+
+       fds.fd = libinput_get_fd(handler->lih);
+       fds.events = POLLIN;
+       fds.revents = 0;
+
+       MM_EH_INFO("Start processing navigation event");
+       while (poll(&fds, 1, -1))
+               handle_events(handler);
+       MM_EH_INFO("End processing navigation event");
+
+       return NULL;
+}
+
+mm_navevent_handler_s *_mm_navevent_handler_create(mm_navevent_handler_device_type_e device)
+{
+       int res = 0;
+       mm_navevent_handler_s *handle = (mm_navevent_handler_s *)calloc(sizeof(mm_navevent_handler_s), 1);
+       if (NULL == handle) {
+               MM_EH_ERROR("Cannot allocate memory for event handler");
+               return NULL;
+       }
+
+       handle->dev = device;
+
+       res = system_info_get_platform_int("http://tizen.org/feature/screen.width", &handle->screen_size.width);
+       if (SYSTEM_INFO_ERROR_NONE != res) {
+               MM_EH_ERROR("Failed to get Device screen width from System Info with error - [%d]", res);
+               free(handle);
+               return NULL;
+       }
+
+       res = system_info_get_platform_int("http://tizen.org/feature/screen.height", &handle->screen_size.height);
+       if (SYSTEM_INFO_ERROR_NONE != res) {
+               MM_EH_ERROR("Failed to get Device screen height from System Info with error - [%d]", res);
+               free(handle);
+               return NULL;
+       }
+
+       res = __mm_navevent_handler_init_input(handle);
+       if (MM_NAVEVENT_HANDLER_ERROR_NONE != res) {
+               MM_EH_ERROR("Failed to initialize input for event handling with error - [%d]", res);
+               free(handle);
+               return NULL;
+       }
+
+       pthread_mutex_init(&handle->event_lock, NULL);
+
+       handle->window_orientation = MM_NAVEVENT_HANDLER_ORIENTATION_DEGREE_0;
+       handle->window_size.width = handle->screen_size.width;
+       handle->window_size.height = handle->screen_size.height;
+       handle->fov.width = MM_NAVEVENT_HANDLER_DEFAULT_HORIZONTAL_FOV_DEGREE;
+       handle->fov.height = MM_NAVEVENT_HANDLER_DEFAULT_VERTICAL_FOV_DEGREE;
+       handle->angles[0] = handle->angles[1] = handle->angles[2] = .0f;
+
+       pthread_create(&handle->event_thread, NULL, __mm_navevent_handler_handle_event, handle);
+
+       return handle;
+}
+
+int _mm_navevent_handler_destroy(mm_navevent_handler_s *handler)
+{
+       if (NULL == handler) {
+               MM_EH_ERROR("Invalid pointer to event handler");
+               return MM_NAVEVENT_HANDLER_ERROR_INVALID_PARAMETER;
+       }
+
+       pthread_mutex_lock(&handler->event_lock);
+       pthread_cancel(handler->event_thread);
+       pthread_mutex_unlock(&handler->event_lock);
+
+       void *thread_res = NULL;
+       pthread_join(handler->event_thread, &thread_res);
+
+       pthread_mutex_destroy(&handler->event_lock);
+
+       libinput_unref(handler->lih);
+
+       free(handler);
+
+       return MM_NAVEVENT_HANDLER_ERROR_NONE;
+}
+
+int _mm_navevent_handler_set_cb(mm_navevent_handler_s *handler, mm_navevent_handler_cb cb, void *data)
+{
+       if (NULL == handler) {
+               MM_EH_ERROR("Invalid pointer to event handler");
+               return MM_NAVEVENT_HANDLER_ERROR_INVALID_PARAMETER;
+       }
+
+       handler->cb = cb;
+       handler->user_data = data;
+
+       return MM_NAVEVENT_HANDLER_ERROR_NONE;
+}
+
+int _mm_navevent_handler_unset_cb(mm_navevent_handler_s *handler)
+{
+       if (NULL == handler) {
+               MM_EH_ERROR("Invalid pointer to event handler");
+               return MM_NAVEVENT_HANDLER_ERROR_INVALID_PARAMETER;
+       }
+
+       handler->cb = NULL;
+       handler->user_data = NULL;
+
+       return MM_NAVEVENT_HANDLER_ERROR_NONE;
+}
+
+int _mm_navevent_handler_set_window_parameters(mm_navevent_handler_s *handler, mm_navevent_handler_orientation_e wo, mm_navevent_handler_size_s ws)
+{
+       handler->window_size = ws;
+       handler->window_orientation = wo;
+
+       return MM_NAVEVENT_HANDLER_ERROR_NONE;
+}
+
+int _mm_navevent_handler_set_fov_size(mm_navevent_handler_s *handler, mm_navevent_handler_size_s fovs)
+{
+       handler->fov = fovs;
+
+       return MM_NAVEVENT_HANDLER_ERROR_NONE;
+}
+
+int _mm_navevent_handler_get_screen_size(mm_navevent_handler_s *handler, mm_navevent_handler_size_s *size)
+{
+       memcpy(size, &handler->screen_size, sizeof(*size));
+
+       return MM_NAVEVENT_HANDLER_ERROR_NONE;
+}
+
+int _mm_navevent_handler_set_angles(mm_navevent_handler_s *handler, float angles[3])
+{
+       memcpy(handler->angles, angles, sizeof(*angles) * 3);
+
+       return MM_NAVEVENT_HANDLER_ERROR_NONE;
+}
index a7e9e06..d8dd82e 100644 (file)
@@ -13,6 +13,8 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
+#define USE_EVENT_HANDLER
+
 #include <player.h>
 #include <player_internal.h>
 #include <sound_manager.h>
 #include <eom.h>
 #endif
 
+#ifdef USE_EVENT_HANDLER
+#include <mm_navevent_handler.h>
+#endif
+
 #ifdef PACKAGE
 #undef PACKAGE
 #endif
@@ -65,6 +71,14 @@ typedef struct {
        int rebuffer_ms;
 } buffer_size_t;
 
+#ifdef USE_EVENT_HANDLER
+static void event_handler_cb(enum libinput_event_type ev_t, int x, int y, void * data, float e[3]);
+static void event_handler_set_dov_fov();
+static void event_handler_set_window_parameters();
+mm_navevent_handler_h event_handler;
+mm_navevent_handler_size_s image_size;
+#endif
+
 static tizen_profile_t _get_tizen_profile()
 {
        char *profileName;
@@ -152,6 +166,12 @@ enum {
        CURRENT_STATUS_SET_AUDIO_ONLY,
        CURRENT_STATUS_SET_PRE_BUFFERING_SIZE,
        CURRENT_STATUS_SET_RE_BUFFERING_SIZE,
+       CURRENT_STATUS_VIDEO360_SET_ENABLE,
+       CURRENT_STATUS_VIDEO360_SET_DOV,
+       CURRENT_STATUS_VIDEO360_SET_DOV1,
+       CURRENT_STATUS_VIDEO360_SET_FOV,
+       CURRENT_STATUS_VIDEO360_SET_FOV1,
+       CURRENT_STATUS_VIDEO360_SET_ZOOM,
 };
 
 typedef struct {
@@ -456,6 +476,7 @@ static int app_create(void *data)
 #ifdef _ACTIVATE_EOM_
        eom_output_mode_e output_mode = EOM_OUTPUT_MODE_NONE;
 #endif
+
        /* use gl backend */
        elm_config_accel_preference_set("opengl");
 
@@ -510,6 +531,21 @@ static int app_create(void *data)
        eom_set_mode_changed_cb(eom_notify_cb_mode_changed, ad);
        eom_set_attribute_changed_cb(eom_notify_cb_attribute_changed, ad);
 #endif
+
+#ifdef USE_EVENT_HANDLER
+       if (mm_navevent_handler_create(&event_handler,
+                       MM_NAVEVENT_HANDLER_DEVICE_TYPE_TOUCH) != MM_NAVEVENT_HANDLER_ERROR_NONE) {
+               g_print ("Error during handler creation\n");
+               return -1;
+       }
+
+       if (mm_navevent_handler_set_cb(event_handler, event_handler_cb, NULL) !=
+                       MM_NAVEVENT_HANDLER_ERROR_NONE) {
+               g_print ("Error during callback set\n");
+               return -1;
+       }
+#endif
+
        return 0;
 }
 
@@ -518,6 +554,10 @@ static int app_terminate(void *data)
        appdata *ad = data;
        int i = 0;
 
+#ifdef USE_EVENT_HANDLER
+       mm_navevent_handler_destroy(event_handler);
+#endif
+
        for (i = 0; i < MAX_HANDLE; i++) {
                if (g_eo[i]) {
                        evas_object_del(g_eo[i]);
@@ -607,6 +647,12 @@ static void interrupted_cb(player_interrupted_code_e code, void *user_data)
 static void video_changed_cb(int width, int height, int fps, int bit_rate, void *user_data)
 {
        g_print("[Player_Test] video_changed_cb!!!! %d x %d, %d, %d \n", width, height, fps, bit_rate);
+
+#ifdef USE_EVENT_HANDLER
+       image_size.width = width;
+       image_size.height = height;
+       event_handler_set_window_parameters();
+#endif
 }
 
 #if 0
@@ -1051,6 +1097,9 @@ static void _player_prepare(bool async)
        if (is_es_push_mode)
                pthread_create(&g_feed_video_thread_id, NULL, (void *)feed_video_data_thread_func, NULL);
 
+#ifdef USE_EVENT_HANDLER
+       event_handler_set_dov_fov();
+#endif
 }
 
 static void _player_unprepare()
@@ -1701,6 +1750,9 @@ static void set_display_rotation(int rotation)
 {
        if (player_set_display_rotation(g_player[0], rotation) != PLAYER_ERROR_NONE)
                g_print("failed to set_display_rotation\n");
+#ifdef USE_EVENT_HANDLER
+       event_handler_set_window_parameters();
+#endif
 }
 
 static void get_display_rotation()
@@ -1860,6 +1912,139 @@ static void get_audio_eq()
        g_print("                                                            ==> [Player_Test] eq bands frequency range: [%d] \n", value);
 }
 
+static void video360_set_enable(bool enable)
+{
+#ifdef USE_EVENT_HANDLER
+       event_handler_set_dov_fov();
+#endif
+
+       if (player_360_set_enable(g_player[0], enable) != PLAYER_ERROR_NONE)
+               g_print("failed to %s video 360 mode\n", enable ? "enable" : "disable");
+}
+
+static void video360_get_enable()
+{
+       bool enable;
+
+       if (player_360_is_enabled(g_player[0], &enable) != PLAYER_ERROR_NONE)
+               g_print("failed to get video 360 mode status\n");
+       else
+               g_print("                                                          ==> [Player_Test] Video 360 mode = %s\n", enable ? "enabled" : "disabled");
+}
+
+static void video360_set_fov(int hfov, int vfov)
+{
+#ifdef USE_EVENT_HANDLER
+       mm_navevent_handler_size_s fov;
+
+       fov.width = hfov;
+       fov.height = vfov;
+       mm_navevent_handler_set_fov_size(event_handler, fov);
+#endif
+       if (player_360_set_field_of_view(g_player[0], hfov, vfov) != PLAYER_ERROR_NONE)
+               g_print("failed to set video 360 field of view\n");
+}
+
+static void video360_get_fov()
+{
+       int hfov, vfov;
+
+       if (player_360_get_field_of_view(g_player[0], &hfov, &vfov) != PLAYER_ERROR_NONE)
+               g_print("failed to get video 360 field of view\n");
+       else
+               g_print("                                                          ==> [Player_Test] Video 360 FOV = %dx%d deg.\n", hfov, vfov);
+}
+
+static void video360_set_dov(int yaw_deg, int pitch_deg)
+{
+       float angles[3];
+
+       angles[0] = M_PI * yaw_deg / 180.0f;
+       angles[1] = M_PI * pitch_deg / 180.0f;
+       angles[2] = 0;
+
+#ifdef USE_EVENT_HANDLER
+       mm_navevent_handler_set_angles(event_handler, angles);
+#endif
+       if (player_360_set_direction_of_view(g_player[0], angles[0], angles[1]) != PLAYER_ERROR_NONE)
+               g_print("failed to set video 360 direction of view\n");
+}
+
+static void video360_get_dov()
+{
+       float yaw, pitch;
+
+       if (player_360_get_direction_of_view(g_player[0], &yaw, &pitch) != PLAYER_ERROR_NONE)
+               g_print("failed to get video 360 direction of view\n");
+       else
+               g_print("                                                          ==> [Player_Test] Video 360 DOV yaw = %d, pitch = %d deg.\n", (int)(yaw * 180.0f / M_PI), (int)(pitch * 180.0f / M_PI));
+}
+
+static void video360_set_zoom(float zoom)
+{
+       if (player_360_set_zoom(g_player[0], zoom) != PLAYER_ERROR_NONE)
+               g_print("failed to set video 360 zoom\n");
+}
+
+static void video360_get_zoom()
+{
+       float zoom;
+
+       if (player_360_get_zoom(g_player[0], &zoom) != PLAYER_ERROR_NONE)
+               g_print("failed to get video 360 zoom\n");
+       else
+               g_print("                                                          ==> [Player_Test] Video 360 zoom = %f\n", zoom);
+}
+
+#ifdef USE_EVENT_HANDLER
+static void event_handler_cb(enum libinput_event_type ev_t, int x, int y, void * data, float e[3])
+{
+       if (ev_t == LIBINPUT_EVENT_TOUCH_MOTION)
+               if (player_360_set_direction_of_view(g_player[0], e[0], e[1]) != PLAYER_ERROR_NONE)
+                       g_print("Event handler callback: failed to set direction of view\n");
+}
+
+static void event_handler_set_dov_fov()
+{
+       float angles[3];
+       mm_navevent_handler_size_s fov;
+
+       if (player_360_get_direction_of_view(g_player[0], &angles[0], &angles[1]) != PLAYER_ERROR_NONE) {
+               g_print("Failed to get video360 direction of view\n");
+               return;
+       }
+       if (player_360_get_field_of_view(g_player[0], &fov.width, &fov.height) != PLAYER_ERROR_NONE) {
+               g_print("Failed to get video360 field of view\n");
+               return;
+       }
+       mm_navevent_handler_set_angles(event_handler, angles);
+       mm_navevent_handler_set_fov_size(event_handler, fov);
+}
+
+static void event_handler_set_window_parameters()
+{
+       player_display_rotation_e window_orientation;
+       mm_navevent_handler_size_s window_size;
+       float image_ratio;
+
+       player_get_display_rotation(g_player[0], &window_orientation);
+       elm_win_screen_size_get(selected_win_id, NULL, NULL, &window_size.width, &window_size.height);
+
+       image_ratio = (float) image_size.width / (float) image_size.height;
+
+       if (window_orientation == PLAYER_DISPLAY_ROTATION_NONE ||
+                       window_orientation == PLAYER_DISPLAY_ROTATION_180) {
+               window_size.height = (int)((float) window_size.width / image_ratio);
+       } else {
+               window_size.height = window_size.width;
+               window_size.width = (int)((float) window_size.width * image_ratio);
+       }
+
+       mm_navevent_handler_set_window_parameters(event_handler,
+                       (mm_navevent_handler_orientation_e) window_orientation, window_size);
+}
+#endif
+
 void quit_program()
 {
 
@@ -2023,12 +2208,32 @@ void _interpret_main_menu(char *cmd)
                } else {
                        g_print("unknown menu \n");
                }
-       } else {
+       } else if (len == 3) {
                if (strncmp(cmd, "trs", 3) == 0)
                        g_menu_state = CURRENT_STATUS_STREAMING_PLAYBACK_RATE;
                else
                        g_print("unknown menu \n");
-       }
+       } else if (len == 4) {
+               if (!strncmp(cmd, "v3se", 4))
+                       g_menu_state = CURRENT_STATUS_VIDEO360_SET_ENABLE;
+               else if (!strncmp(cmd, "v3ge", 4))
+                       video360_get_enable();
+               else if (!strncmp(cmd, "v3sd", 4))
+                       g_menu_state = CURRENT_STATUS_VIDEO360_SET_DOV;
+               else if (!strncmp(cmd, "v3gd", 4))
+                       video360_get_dov();
+               else if (!strncmp(cmd, "v3sf", 4))
+                       g_menu_state = CURRENT_STATUS_VIDEO360_SET_FOV;
+               else if (!strncmp(cmd, "v3gf", 4))
+                       video360_get_fov();
+               else if (!strncmp(cmd, "v3sz", 4))
+                       g_menu_state = CURRENT_STATUS_VIDEO360_SET_ZOOM;
+               else if (!strncmp(cmd, "v3gz", 4))
+                       video360_get_zoom();
+               else
+                       g_print("unknown menu \n");
+       } else
+               g_print("unknown menu \n");
 }
 
 void display_sub_basic()
@@ -2084,6 +2289,14 @@ void display_sub_basic()
        g_print("X4. set audio_cb with async \n");
        g_print("[video_frame_decoded_cb] ep. enable tbm surface pool\n");
        g_print("[buffering] bf. set new buffering size\n");
+       g_print("[Video 360] v3se. Set Enable\t\t");
+       g_print("v3ge. Get Enable\n");
+       g_print("[Video 360] v3sd. Set Direction Of View\t");
+       g_print("v3gd. Get Direction Of View\n");
+       g_print("[Video 360] v3sf. Set Field Of View\t");
+       g_print("v3gf. Get Field Of View\n");
+       g_print("[Video 360] v3sz. Set Zoom\t\t");
+       g_print("v3gz. Get Zoom\n");
        g_print("[etc] sp. Set Progressive Download\t");
        g_print("gp. Get Progressive Download status\t");
        g_print("mp. memory playback\n");
@@ -2159,6 +2372,18 @@ static void displaymenu()
                g_print("*** set audio only mode (0:disable, 1:enable) \n");
        } else if (g_menu_state == CURRENT_STATUS_SET_PRE_BUFFERING_SIZE) {
                g_print("*** set pre buffering size (ms) \n");
+       } else if (g_menu_state == CURRENT_STATUS_VIDEO360_SET_ENABLE) {
+               g_print("*** input video 360 status (0: disabled (full panorama), 1: enabled)\n");
+       } else if (g_menu_state == CURRENT_STATUS_VIDEO360_SET_DOV) {
+               g_print("*** input direction of view yaw angle (+/- 180 deg.)\n");
+       } else if (g_menu_state == CURRENT_STATUS_VIDEO360_SET_DOV1) {
+               g_print("*** input direction of view pitch angle (+/- 90 deg.)\n");
+       } else if (g_menu_state == CURRENT_STATUS_VIDEO360_SET_FOV) {
+               g_print("*** input horizontal field of view angle (1~360 deg.)\n");
+       } else if (g_menu_state == CURRENT_STATUS_VIDEO360_SET_FOV1) {
+               g_print("*** input vertical field of view angle (1~180 deg.)\n");
+       } else if (g_menu_state == CURRENT_STATUS_VIDEO360_SET_ZOOM) {
+               g_print("*** input zoom factor.(1.0~10.0, where 1.0 - no zoom, actual image) \n");
        } else {
                g_print("*** unknown status.\n");
                quit_program();
@@ -2185,6 +2410,8 @@ void reset_menu_state(void)
 
 static void interpret(char *cmd)
 {
+       static int value1, value2;
+
        switch (g_menu_state) {
        case CURRENT_STATUS_MAINMENU:
                {
@@ -2417,6 +2644,46 @@ static void interpret(char *cmd)
                        reset_menu_state();
                }
                break;
+       case CURRENT_STATUS_VIDEO360_SET_ENABLE:
+               {
+                       int enable = atoi(cmd);
+                       video360_set_enable(enable);
+                       reset_menu_state();
+               }
+               break;
+       case CURRENT_STATUS_VIDEO360_SET_DOV:
+               {
+                       value1 = atoi(cmd);
+                       g_menu_state = CURRENT_STATUS_VIDEO360_SET_DOV1;
+               }
+               break;
+       case CURRENT_STATUS_VIDEO360_SET_DOV1:
+               {
+                       value2 = atoi(cmd);
+                       video360_set_dov(value1, value2);
+                       reset_menu_state();
+               }
+               break;
+       case CURRENT_STATUS_VIDEO360_SET_FOV:
+               {
+                       value1 = atoi(cmd);
+                       g_menu_state = CURRENT_STATUS_VIDEO360_SET_FOV1;
+               }
+               break;
+       case CURRENT_STATUS_VIDEO360_SET_FOV1:
+               {
+                       value2 = atoi(cmd);
+                       video360_set_fov(value1, value2);
+                       reset_menu_state();
+               }
+               break;
+       case CURRENT_STATUS_VIDEO360_SET_ZOOM:
+               {
+                       float zoom = atof(cmd);
+                       video360_set_zoom(zoom);
+                       reset_menu_state();
+               }
+               break;
 
        }