From 95640f925a8f0c5ecd48fabefdbcf7a45ef6bd78 Mon Sep 17 00:00:00 2001 From: Nick Holland Date: Tue, 23 Jun 2015 08:32:26 +0100 Subject: [PATCH] libuv support for dali-adaptor for running in Node.JS Highlights: libuv support for timers, callbacks, file descriptor monitoring pure X11 support for touch / key events Exposed SceneCreated(), SetViewMode() and SetStereoBase in adaptor.h Change-Id: I62030ef76337c568852f07d0ef6418e708fee9a3 --- adaptors/base/interfaces/window-event-interface.h | 109 +++++++ adaptors/common/adaptor-impl.cpp | 5 + adaptors/common/adaptor-impl.h | 5 + adaptors/common/adaptor.cpp | 16 + .../ecore}/ecore-callback-manager.cpp | 0 .../ecore}/ecore-callback-manager.h | 12 +- .../ecore/ecore-file-descriptor-monitor.cpp} | 0 .../ecore/ecore-timer-impl.cpp} | 0 .../event-loop/lib-uv/uv-callback-manager.cpp | 186 +++++++++++ .../common/event-loop/lib-uv/uv-callback-manager.h | 95 ++++++ .../lib-uv/uv-file-descriptor-monitor.cpp | 118 +++++++ .../common/event-loop/lib-uv/uv-timer-impl.cpp | 205 ++++++++++++ adaptors/common/file.list | 14 +- adaptors/common/orientation-impl.cpp | 1 - adaptors/common/shared-file.cpp | 1 - adaptors/common/window-impl.h | 1 + adaptors/integration-api/adaptor.h | 29 +- ...ent-handler-x.cpp => ecore-x-event-handler.cpp} | 0 adaptors/x11/file.list | 12 +- adaptors/x11/window-impl-x.cpp | 24 +- adaptors/x11/x-event-handler.cpp | 345 +++++++++++++++++++++ adaptors/x11/x-events/debug/x-input2-debug.cpp | 289 +++++++++++++++++ adaptors/x11/x-events/debug/x-input2-debug.h | 72 +++++ adaptors/x11/x-events/x-event-manager.cpp | 86 +++++ adaptors/x11/x-events/x-event-manager.h | 95 ++++++ adaptors/x11/x-events/x-input2-device.cpp | 66 ++++ adaptors/x11/x-events/x-input2-device.h | 71 +++++ adaptors/x11/x-events/x-input2.cpp | 315 +++++++++++++++++++ adaptors/x11/x-events/x-input2.h | 141 +++++++++ build/tizen/adaptor/Makefile.am | 24 +- build/tizen/configure.ac | 31 ++ 31 files changed, 2348 insertions(+), 20 deletions(-) create mode 100644 adaptors/base/interfaces/window-event-interface.h rename adaptors/common/{ => event-loop/ecore}/ecore-callback-manager.cpp (100%) rename adaptors/common/{ => event-loop/ecore}/ecore-callback-manager.h (90%) rename adaptors/common/{file-descriptor-monitor.cpp => event-loop/ecore/ecore-file-descriptor-monitor.cpp} (100%) rename adaptors/common/{timer-impl.cpp => event-loop/ecore/ecore-timer-impl.cpp} (100%) create mode 100644 adaptors/common/event-loop/lib-uv/uv-callback-manager.cpp create mode 100644 adaptors/common/event-loop/lib-uv/uv-callback-manager.h create mode 100644 adaptors/common/event-loop/lib-uv/uv-file-descriptor-monitor.cpp create mode 100644 adaptors/common/event-loop/lib-uv/uv-timer-impl.cpp rename adaptors/x11/{event-handler-x.cpp => ecore-x-event-handler.cpp} (100%) create mode 100644 adaptors/x11/x-event-handler.cpp create mode 100644 adaptors/x11/x-events/debug/x-input2-debug.cpp create mode 100644 adaptors/x11/x-events/debug/x-input2-debug.h create mode 100644 adaptors/x11/x-events/x-event-manager.cpp create mode 100644 adaptors/x11/x-events/x-event-manager.h create mode 100644 adaptors/x11/x-events/x-input2-device.cpp create mode 100644 adaptors/x11/x-events/x-input2-device.h create mode 100644 adaptors/x11/x-events/x-input2.cpp create mode 100644 adaptors/x11/x-events/x-input2.h diff --git a/adaptors/base/interfaces/window-event-interface.h b/adaptors/base/interfaces/window-event-interface.h new file mode 100644 index 0000000..b689d58 --- /dev/null +++ b/adaptors/base/interfaces/window-event-interface.h @@ -0,0 +1,109 @@ +#ifndef __DALI_INTERNAL_BASE_WINDOW_EVENT_INTERFACE_H__ +#define __DALI_INTERNAL_BASE_WINDOW_EVENT_INTERFACE_H__ + +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * 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. + * + */ + +// EXTERNAL INCLUDES +#include +#include +#include + +namespace Dali +{ + +namespace Internal +{ + +namespace Adaptor +{ + +/** + * @brief Abstract interface for handling DALi events received from the native window system + * + */ +class WindowEventInterface +{ + +public: + + /** + * @brief Touch Event callback + * @param[in] point touch point + * @param[in] timeStamp time stamp + */ + virtual void TouchEvent( Dali::TouchPoint& point, unsigned long timeStamp ) = 0; + + /** + * @brief Key Event callback + * @param[in] keyEvent key event + */ + virtual void KeyEvent( Dali::KeyEvent& keyEvent ) = 0; + + /** + * @brief Wheel Event callback + * @param[in] wheelEvent wheel event + */ + virtual void WheelEvent( Dali::WheelEvent& wheelEvent ) = 0; + + /** + * @brief Window damage callback + * @param[in] damageArea Window damage area + */ + virtual void DamageEvent( Dali::Rect& damageArea ) = 0; + + /** + * @brief Window Focused + */ + virtual void WindowFocusIn() = 0; + + /** + * @brief Window lost focus + */ + virtual void WindowFocusOut() = 0; + +protected: + + /** + * @brief Constructor + */ + WindowEventInterface() + { + } + + /** + * Virtual destructor + */ + virtual ~WindowEventInterface() + { + } + + // Undefined copy constructor. + WindowEventInterface( const WindowEventInterface& ); + + // Undefined assignment operator. + WindowEventInterface& operator=( const WindowEventInterface& ); + +}; + +} // namespace Adaptor + +} // namespace Internal + +} // namespace Dali + +#endif // __DALI_INTERNAL_BASE_WINDOW_EVENT_INTERFACE_H__ diff --git a/adaptors/common/adaptor-impl.cpp b/adaptors/common/adaptor-impl.cpp index 9df9d70..70c829d 100644 --- a/adaptors/common/adaptor-impl.cpp +++ b/adaptors/common/adaptor-impl.cpp @@ -494,6 +494,11 @@ bool Adaptor::IsAvailable() return gThreadLocalAdaptor != NULL; } +void Adaptor::SceneCreated() +{ + mCore->SceneCreated(); +} + Dali::Integration::Core& Adaptor::GetCore() { return *mCore; diff --git a/adaptors/common/adaptor-impl.h b/adaptors/common/adaptor-impl.h index 9bebb38..8f2bd9f 100644 --- a/adaptors/common/adaptor-impl.h +++ b/adaptors/common/adaptor-impl.h @@ -132,6 +132,11 @@ public: */ static bool IsAvailable(); + /** + * @copydoc Dali::Core::SceneCreated(); + */ + void SceneCreated(); + public: // AdaptorInternalServices implementation /** * @copydoc Dali::Adaptor::Start() diff --git a/adaptors/common/adaptor.cpp b/adaptors/common/adaptor.cpp index bb4658a..e0af921 100644 --- a/adaptors/common/adaptor.cpp +++ b/adaptors/common/adaptor.cpp @@ -159,6 +159,22 @@ void Adaptor::FeedKeyEvent( KeyEvent& keyEvent ) mImpl->FeedKeyEvent(keyEvent); } +void Adaptor::SceneCreated() +{ + mImpl->SceneCreated(); +} + +void Adaptor::SetViewMode( ViewMode mode ) +{ + mImpl->SetViewMode( mode ); +} + +void Adaptor::SetStereoBase( float stereoBase ) +{ + mImpl->SetStereoBase( stereoBase ); +} + + Adaptor::Adaptor() : mImpl( NULL ) { diff --git a/adaptors/common/ecore-callback-manager.cpp b/adaptors/common/event-loop/ecore/ecore-callback-manager.cpp similarity index 100% rename from adaptors/common/ecore-callback-manager.cpp rename to adaptors/common/event-loop/ecore/ecore-callback-manager.cpp diff --git a/adaptors/common/ecore-callback-manager.h b/adaptors/common/event-loop/ecore/ecore-callback-manager.h similarity index 90% rename from adaptors/common/ecore-callback-manager.h rename to adaptors/common/event-loop/ecore/ecore-callback-manager.h index 9b961aa..f93cc72 100644 --- a/adaptors/common/ecore-callback-manager.h +++ b/adaptors/common/event-loop/ecore/ecore-callback-manager.h @@ -44,13 +44,13 @@ class EcoreCallbackManager : public CallbackManager public: - /** - * constructor + /** + * @brief constructor */ EcoreCallbackManager(); /** - * destructor + * @brief destructor */ ~EcoreCallbackManager() { @@ -74,21 +74,21 @@ public: private: /** - * Remove all idle call backs that are pending + * @brief Remove all idle call backs that are pending * Called by Stop() * Always called from the main thread */ void RemoveAllCallbacks(); /** - * Removes a single call back from the container + * @brief Removes a single call back from the container * Always called from main thread * @param callbackData callback data */ void RemoveCallbackFromContainer(CallbackData *callbackData); /** - * Remove a standard call back from ecore + * @brief Remove a standard call back from ecore * Always called from main thread * @param callbackData callback data */ diff --git a/adaptors/common/file-descriptor-monitor.cpp b/adaptors/common/event-loop/ecore/ecore-file-descriptor-monitor.cpp similarity index 100% rename from adaptors/common/file-descriptor-monitor.cpp rename to adaptors/common/event-loop/ecore/ecore-file-descriptor-monitor.cpp diff --git a/adaptors/common/timer-impl.cpp b/adaptors/common/event-loop/ecore/ecore-timer-impl.cpp similarity index 100% rename from adaptors/common/timer-impl.cpp rename to adaptors/common/event-loop/ecore/ecore-timer-impl.cpp diff --git a/adaptors/common/event-loop/lib-uv/uv-callback-manager.cpp b/adaptors/common/event-loop/lib-uv/uv-callback-manager.cpp new file mode 100644 index 0000000..ab8552d --- /dev/null +++ b/adaptors/common/event-loop/lib-uv/uv-callback-manager.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * 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. + * + */ + +// CLASS HEADER +#include "uv-callback-manager.h" + +// EXTERNAL INCLUDES +#include +#include + +// INTERNAL INCLUDES + + +namespace Dali +{ + +namespace Internal +{ + +namespace Adaptor +{ + +namespace +{ + +static void FreeHandleCallback(uv_handle_t* handle ) +{ + delete handle; +} + +} +/** + * Structure contains the callback function and control options + */ +struct CallbackData +{ + typedef void (*CallbackFunction)(uv_idle_t*); + + /** + * Constructor + */ + CallbackData( CallbackBase* callback ) + : mCallback(callback), + mRemoveFromContainerFunction(NULL), + mIdleHandle( NULL), + mExecute(true) + { + } + + /** + * Add the idle callback + */ + void AddIdle( CallbackFunction callback) + { + // heap allocate a handle as it will be alive after the CallbackData object is deleted. + mIdleHandle = new uv_idle_t; + + // Node.JS uses uv_default_loop + uv_idle_init( uv_default_loop() , mIdleHandle ); + + mIdleHandle->data = this; + + uv_idle_start( mIdleHandle, callback); + } + + /** + * Destructor + */ + ~CallbackData() + { + // the handle will still be alive for a short period after calling uv_close + // set the data to NULL to avoid a dangling pointer + mIdleHandle->data = NULL; + + uv_idle_stop( mIdleHandle ); + + uv_close( reinterpret_cast< uv_handle_t*>( mIdleHandle ) , FreeHandleCallback ); + + delete mCallback; + delete mRemoveFromContainerFunction; + } + + // Data + CallbackBase* mCallback; ///< call back + CallbackBase* mRemoveFromContainerFunction; ///< Called to remove the callbackdata from the callback container + uv_idle_t* mIdleHandle; ///< idle handle + bool mExecute; ///< whether to run the callback + +}; + +namespace +{ +void IdleCallback( uv_idle_t* handle ) +{ + CallbackData *callbackData = static_cast(handle->data); + + // remove callback data from the container first in case our callback tries to modify the container + CallbackBase::Execute( *callbackData->mRemoveFromContainerFunction, callbackData ); + + // run the function + CallbackBase::Execute( *callbackData->mCallback ); + + // will clear up the handle + delete callbackData; + +} +} + +UvCallbackManager::UvCallbackManager() +:mRunning(false) +{ +} + +void UvCallbackManager::Start() +{ + DALI_ASSERT_DEBUG( mRunning == false ); + mRunning = true; +} + +void UvCallbackManager::Stop() +{ + // make sure we're not called twice + DALI_ASSERT_DEBUG( mRunning == true ); + + mRunning = false; + + for( CallbackList::iterator iter = mCallbackContainer.begin(); iter != mCallbackContainer.end(); ++iter) + { + CallbackData* data = (*iter); + + delete data; + } + mCallbackContainer.clear(); +} + +bool UvCallbackManager::AddIdleCallback( CallbackBase* callback ) +{ + if( !mRunning ) + { + return false; + } + + CallbackData *callbackData = new CallbackData(callback ); + + // To inform the manager a callback has finished, we get it to call RemoveCallbackFromContainer + callbackData->mRemoveFromContainerFunction = MakeCallback( this, &UvCallbackManager::RemoveCallbackFromContainer ); + + // add the call back to the container + mCallbackContainer.push_front(callbackData); + + // init the callback + callbackData->AddIdle( &IdleCallback ); + + return true; +} + +void UvCallbackManager::RemoveCallbackFromContainer(CallbackData *callbackData) +{ + mCallbackContainer.remove(callbackData); +} + +// Creates a concrete interface for CallbackManager +CallbackManager* CallbackManager::New() +{ + return new UvCallbackManager; +} + +} // namespace Adaptor + +} // namespace Internal + +} // namespace Dali diff --git a/adaptors/common/event-loop/lib-uv/uv-callback-manager.h b/adaptors/common/event-loop/lib-uv/uv-callback-manager.h new file mode 100644 index 0000000..78abab6 --- /dev/null +++ b/adaptors/common/event-loop/lib-uv/uv-callback-manager.h @@ -0,0 +1,95 @@ +#ifndef __DALI_UV_CALLBACK_MANAGER_H__ +#define __DALI_UV_CALLBACK_MANAGER_H__ + +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * 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. + * + */ + +// EXTERNAL INCLUDES +#include + +// INTERNAL INCLUDES +#include + + +namespace Dali +{ + +namespace Internal +{ + +namespace Adaptor +{ + +struct CallbackData; + +/** + * @brief LibUV callback manager used to install call backs in the applications main loop. + * The manager keeps track of all callbacks, so that if Stop() is called it can remove them. + */ +class UvCallbackManager : public CallbackManager +{ + +public: + + /** + * @brief constructor + */ + UvCallbackManager(); + + /** + * @brief destructor + */ + ~UvCallbackManager(){} + + /** + * @copydoc CallbackManager::AddCallback() + */ + virtual bool AddIdleCallback( CallbackBase* callback ); + + /** + * @copydoc CallbackManager::Start() + */ + virtual void Start(); + + /** + * @copydoc CallbackManager::Stop() + */ + virtual void Stop(); + +private: + + /** + * @brief Removes a single call back from the container + * Always called from main thread + * @param callbackData callback data + */ + void RemoveCallbackFromContainer(CallbackData *callbackData); + + + typedef std::list CallbackList; ///< list of callbacks installed + + bool mRunning; ///< flag is set to true if when running + CallbackList mCallbackContainer; ///< container of live callbacks +}; + +} // namespace Adaptor + +} // namespace Internal + +} // namespace Dali + +#endif // __DALI_UV_CALLBACK_MANAGER_H__ diff --git a/adaptors/common/event-loop/lib-uv/uv-file-descriptor-monitor.cpp b/adaptors/common/event-loop/lib-uv/uv-file-descriptor-monitor.cpp new file mode 100644 index 0000000..0ce27cb --- /dev/null +++ b/adaptors/common/event-loop/lib-uv/uv-file-descriptor-monitor.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * 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. + * + */ + +// CLASS HEADER +#include "file-descriptor-monitor.h" + +// EXTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Internal +{ + +namespace Adaptor +{ + +namespace +{ +void FreeHandleCallback(uv_handle_t* handle ) +{ + delete handle; +} + +} + +/** + * Using Impl to hide away UV specific members + */ +struct FileDescriptorMonitor::Impl +{ +public: + + // Constructor + Impl( int fileDescriptor, CallbackBase* callback ) + : mFileDescriptor( fileDescriptor ), + mCallback( callback ), + pollHandle( NULL ) + { + + // heap allocate a handle as it will be alive after the FileDescriptorMonitor::Impl object is deleted. + pollHandle = new uv_poll_t; + + // Node.JS uses uv_default_loop + uv_poll_init( uv_default_loop(), pollHandle, fileDescriptor); + + pollHandle->data = this; + + uv_poll_start( pollHandle, UV_READABLE, PollCabllack); + } + + ~Impl() + { + uv_poll_stop( pollHandle ); + + // the handle will still be alive for a short period after calling uv_close + // set the data to NULL to avoid a dangling pointer + pollHandle->data = NULL; + + uv_close(reinterpret_cast ( pollHandle ) , FreeHandleCallback ); + + delete mCallback; + } + + static void PollCabllack(uv_poll_t* handle, int status, int events) + { + if( handle->data ) + { + FileDescriptorMonitor::Impl* impl= static_cast(handle->data); + // run the function + CallbackBase::Execute( *impl->mCallback ); + } + } + // Data + int mFileDescriptor; + CallbackBase* mCallback; + uv_poll_t* pollHandle; + +}; + + +FileDescriptorMonitor::FileDescriptorMonitor( int fileDescriptor, CallbackBase* callback ) +{ + if (fileDescriptor < 0) + { + return; + } + + // waiting for a write event on a file descriptor + mImpl = new Impl(fileDescriptor, callback); +} + +FileDescriptorMonitor::~FileDescriptorMonitor() +{ + delete mImpl; +} + +} // namespace Adaptor + +} // namespace Internal + +} // namespace Dali diff --git a/adaptors/common/event-loop/lib-uv/uv-timer-impl.cpp b/adaptors/common/event-loop/lib-uv/uv-timer-impl.cpp new file mode 100644 index 0000000..01ec2f4 --- /dev/null +++ b/adaptors/common/event-loop/lib-uv/uv-timer-impl.cpp @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * 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. + * + */ + +// CLASS HEADER +#include "timer-impl.h" +#include + +// EXTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace Internal +{ + +namespace Adaptor +{ + +namespace +{ +void TimerSourceFunc (uv_timer_t* handle) +{ + Timer* timer = static_cast(handle->data); + + bool keepRunning = timer->Tick(); + if( !keepRunning ) + { + timer->Stop(); + } +} +void FreeHandleCallback(uv_handle_t* handle ) +{ + delete handle; +} + +} // unnamed namespace + +/** + * Struct to hide away Ecore implementation details + */ +struct Timer::Impl +{ + Impl( unsigned int milliSec ) + : mTimerHandle( NULL ), + mInterval( milliSec ), + mRunning( false ) + { + } + + ~Impl() + { + // the handle will still be alive for a short period after calling uv_close + // set the data to NULL to avoid a dangling pointer + mTimerHandle->data = NULL; + + uv_close( reinterpret_cast< uv_handle_t* >( mTimerHandle ), FreeHandleCallback ); + } + + bool Running() + { + return mRunning; + } + + void Start( void* internalTimerPtr ) + { + Stop(); // make sure we stop first if its currently running + + if( !mTimerHandle ) + { + // heap allocate the handle as its lifetime will be longer than TimerImpl + mTimerHandle = new uv_timer_t; + + // initialize the handle + uv_timer_init( uv_default_loop(), mTimerHandle); + } + + mRunning = true; + + mTimerHandle->data = internalTimerPtr; + + uv_timer_start( mTimerHandle, TimerSourceFunc, mInterval, mInterval); + } + + void Stop() + { + if( mRunning ) + { + mTimerHandle->data = NULL; + uv_timer_stop( mTimerHandle ); + mRunning = false; + } + } + + uv_timer_t* mTimerHandle; + unsigned int mInterval; + bool mRunning; +}; + +TimerPtr Timer::New( unsigned int milliSec ) +{ + DALI_LOG_ERROR(" new timer"); + TimerPtr timer( new Timer( milliSec ) ); + return timer; +} + +Timer::Timer( unsigned int milliSec ) +: mImpl(new Impl(milliSec)) +{ +} + +Timer::~Timer() +{ + // stop timers + Stop(); + + delete mImpl; +} + +void Timer::Start() +{ + mImpl->Start( this ); +} + +void Timer::Stop() +{ + mImpl->Stop(); +} + +void Timer::SetInterval( unsigned int interval ) +{ + // stop existing timer + Stop(); + + mImpl->mInterval = interval; + + // start new tick + Start(); +} + +unsigned int Timer::GetInterval() const +{ + return mImpl->mInterval; +} + +bool Timer::Tick() +{ + // Guard against destruction during signal emission + Dali::Timer handle( this ); + + bool retVal( false ); + + // Override with new signal if used + if( !mTickSignal.Empty() ) + { + retVal = mTickSignal.Emit(); + + // Timer stops if return value is false + if (retVal == false) + { + Stop(); + } + else + { + retVal = true; // continue emission + } + } + else // no callbacks registered + { + // periodic timer is started but nobody listens, continue + retVal = true; + } + + return retVal; +} + +Dali::Timer::TimerSignalType& Timer::TickSignal() +{ + return mTickSignal; +} + +bool Timer::IsRunning() const +{ + return mImpl->mRunning; +} + +} // namespace Adaptor + +} // namespace Internal + +} // namespace Dali diff --git a/adaptors/common/file.list b/adaptors/common/file.list index 5cd748c..2560e25 100644 --- a/adaptors/common/file.list +++ b/adaptors/common/file.list @@ -10,8 +10,6 @@ adaptor_common_internal_src_files = \ $(adaptor_common_dir)/clipboard-event-notifier-impl.cpp \ $(adaptor_common_dir)/command-line-options.cpp \ $(adaptor_common_dir)/drag-and-drop-detector-impl.cpp \ - $(adaptor_common_dir)/ecore-callback-manager.cpp \ - $(adaptor_common_dir)/file-descriptor-monitor.cpp \ $(adaptor_common_dir)/haptic-player-impl.cpp \ $(adaptor_common_dir)/indicator-impl.cpp \ $(adaptor_common_dir)/indicator-buffer.cpp \ @@ -29,7 +27,6 @@ adaptor_common_internal_src_files = \ $(adaptor_common_dir)/singleton-service-impl.cpp \ $(adaptor_common_dir)/sound-player-impl.cpp \ $(adaptor_common_dir)/style-monitor-impl.cpp \ - $(adaptor_common_dir)/timer-impl.cpp \ $(adaptor_common_dir)/trigger-event.cpp \ $(adaptor_common_dir)/trigger-event-factory.cpp \ $(adaptor_common_dir)/virtual-keyboard-impl.cpp \ @@ -53,6 +50,17 @@ adaptor_common_internal_src_files = \ $(adaptor_common_dir)/gl/gl-proxy-implementation.cpp \ $(adaptor_common_dir)/gl/gl-extensions.cpp +# Different files depending on the event loop being used +adaptor_common_internal_ecore_src_files = \ + $(adaptor_common_dir)/event-loop/ecore/ecore-callback-manager.cpp \ + $(adaptor_common_dir)/event-loop/ecore/ecore-file-descriptor-monitor.cpp \ + $(adaptor_common_dir)/event-loop/ecore/ecore-timer-impl.cpp + + adaptor_common_internal_uv_src_files = \ + $(adaptor_common_dir)/event-loop/lib-uv/uv-callback-manager.cpp \ + $(adaptor_common_dir)/event-loop/lib-uv/uv-file-descriptor-monitor.cpp \ + $(adaptor_common_dir)/event-loop/lib-uv/uv-timer-impl.cpp + adaptor_common_internal_default_profile_src_files = \ $(adaptor_common_dir)/color-controller-impl.cpp \ $(adaptor_common_dir)/system-settings.cpp diff --git a/adaptors/common/orientation-impl.cpp b/adaptors/common/orientation-impl.cpp index 73c2a4d..cbd705e 100644 --- a/adaptors/common/orientation-impl.cpp +++ b/adaptors/common/orientation-impl.cpp @@ -19,7 +19,6 @@ #include "orientation-impl.h" // EXTERNAL INCLUDES -#include #include // INTERNAL INCLUDES diff --git a/adaptors/common/shared-file.cpp b/adaptors/common/shared-file.cpp index 0a1bb5e..9504246 100644 --- a/adaptors/common/shared-file.cpp +++ b/adaptors/common/shared-file.cpp @@ -30,7 +30,6 @@ #include -#include namespace Dali { diff --git a/adaptors/common/window-impl.h b/adaptors/common/window-impl.h index 3d63919..22f046e 100644 --- a/adaptors/common/window-impl.h +++ b/adaptors/common/window-impl.h @@ -261,6 +261,7 @@ private: bool mStarted:1; bool mIsTransparent:1; bool mWMRotationAppSet:1; + bool mEcoreEventHander:1; Indicator* mIndicator; Dali::Window::WindowOrientation mIndicatorOrientation; Dali::Window::WindowOrientation mNextIndicatorOrientation; diff --git a/adaptors/integration-api/adaptor.h b/adaptors/integration-api/adaptor.h index 6558e40..cc79c35 100644 --- a/adaptors/integration-api/adaptor.h +++ b/adaptors/integration-api/adaptor.h @@ -23,10 +23,20 @@ #include #include #include +#include // INTERNAL INCLUDES -#include "window.h" -#include "application-configuration.h" + + +#ifdef DALI_ADAPTOR_COMPILATION // full path doesn't exist until adaptor is installed so we have to use relative +// @todo Make dali-adaptor code folder structure mirror the folder structure installed to dali-env +#include +#include +#else +#include +#include +#endif + namespace Dali { @@ -295,6 +305,21 @@ public: */ void FeedKeyEvent( KeyEvent& keyEvent ); + /** + * @copydoc Dali::Core::SceneCreated(); + */ + void SceneCreated(); + + /** + * @copydoc Dali::Application::SetViewMode(); + */ + void SetViewMode( ViewMode viewMode ); + + /** + * @copydoc Dali::Application::SetStereoBase(); + */ + void SetStereoBase( float stereoBase ); + public: // Signals /** diff --git a/adaptors/x11/event-handler-x.cpp b/adaptors/x11/ecore-x-event-handler.cpp similarity index 100% rename from adaptors/x11/event-handler-x.cpp rename to adaptors/x11/ecore-x-event-handler.cpp diff --git a/adaptors/x11/file.list b/adaptors/x11/file.list index 9d66ebf..469fb2f 100644 --- a/adaptors/x11/file.list +++ b/adaptors/x11/file.list @@ -8,13 +8,21 @@ _adaptor_x11_internal_src_files = \ $(adaptor_x11_dir)/server-connection-x.cpp \ $(adaptor_x11_dir)/virtual-keyboard-impl-x.cpp \ $(adaptor_x11_dir)/window-impl-x.cpp \ - $(adaptor_x11_dir)/event-handler-x.cpp \ $(adaptor_x11_dir)/egl-implementation-x.cpp \ $(adaptor_x11_dir)/pixmap-render-surface-x.cpp \ $(adaptor_x11_dir)/ecore-x-render-surface.cpp \ $(adaptor_x11_dir)/window-render-surface-x.cpp \ $(adaptor_x11_dir)/ecore-x-window-interface.cpp +adaptor_ecore_x_event_handler_internal_src_files = \ + $(adaptor_x11_dir)/ecore-x-event-handler.cpp + +adaptor_uv_x_event_handler_internal_src_files = \ + $(adaptor_x11_dir)/x-event-handler.cpp \ + $(adaptor_x11_dir)/x-events/x-event-manager.cpp \ + $(adaptor_x11_dir)/x-events/x-input2.cpp \ + $(adaptor_x11_dir)/x-events/x-input2-device.cpp \ + $(adaptor_x11_dir)/x-events/debug/x-input2-debug.cpp adaptor_x11_ubuntu_internal_src_files = \ $(_adaptor_x11_internal_src_files) @@ -36,4 +44,4 @@ adaptor_x11_internal_default_profile_src_files = \ $(adaptor_x11_dir)/system-settings-x.cpp devel_api_adaptor_tizen_x11_header_files = \ - $(adaptor_x11_dir)/window-extensions.h + $(adaptor_x11_dir)/window-extensions.h \ No newline at end of file diff --git a/adaptors/x11/window-impl-x.cpp b/adaptors/x11/window-impl-x.cpp index dca0fb1..b189872 100644 --- a/adaptors/x11/window-impl-x.cpp +++ b/adaptors/x11/window-impl-x.cpp @@ -63,8 +63,8 @@ struct Window::EventHandler */ EventHandler( Window* window ) : mWindow( window ), - mWindowPropertyHandler( ecore_event_handler_add( ECORE_X_EVENT_WINDOW_PROPERTY, EcoreEventWindowPropertyChanged, this ) ), - mClientMessagehandler( ecore_event_handler_add( ECORE_X_EVENT_CLIENT_MESSAGE, EcoreEventClientMessage, this ) ), + mWindowPropertyHandler( NULL ), + mClientMessagehandler( NULL ), mEcoreWindow( 0 ) { // store ecore window handle @@ -83,7 +83,13 @@ struct Window::EventHandler &tmp, 1); #endif // DALI_PROFILE_UBUNTU - ecore_x_input_multi_select( mEcoreWindow ); + if( mWindow->mEcoreEventHander ) + { + ecore_x_input_multi_select( mEcoreWindow ); + mWindowPropertyHandler= ecore_event_handler_add( ECORE_X_EVENT_WINDOW_PROPERTY, EcoreEventWindowPropertyChanged, this ); + mClientMessagehandler = ecore_event_handler_add( ECORE_X_EVENT_CLIENT_MESSAGE, EcoreEventClientMessage, this ); + } + } /** @@ -318,6 +324,7 @@ Window::Window() mStarted(false), mIsTransparent(false), mWMRotationAppSet(false), + mEcoreEventHander(true), mIndicator(NULL), mIndicatorOrientation(Dali::Window::PORTRAIT), mNextIndicatorOrientation(Dali::Window::PORTRAIT), @@ -327,6 +334,17 @@ Window::Window() mEventHandler(NULL), mPreferredOrientation(Dali::Window::PORTRAIT) { + + // Detect if we're not running in a ecore main loop (e.g. libuv). + // Typically ecore_x_init is called by app_efl_main->elm_init + // but if we're not using app_efl_main then we have to call it ourselves + // This is a hack until we create a pure X Window class + if( ecore_x_display_get() == NULL ) + { + mEcoreEventHander = false; + ecore_x_init (NULL); // internally calls _ecore_x_input_init + } + } Window::~Window() diff --git a/adaptors/x11/x-event-handler.cpp b/adaptors/x11/x-event-handler.cpp new file mode 100644 index 0000000..b801cb2 --- /dev/null +++ b/adaptors/x11/x-event-handler.cpp @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * 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. + * + */ + +// CLASS HEADER +#include + +// EXTERNAL INCLUDES +#include +#include + +#include +#include +#include + +#include + +#include + +#ifndef DALI_PROFILE_UBUNTU +#include +#include +#endif // DALI_PROFILE_UBUNTU + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Dali +{ + +namespace Internal +{ + +namespace Adaptor +{ + +#if defined(DEBUG_ENABLED) +namespace +{ +Integration::Log::Filter* gTouchEventLogFilter = Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_ADAPTOR_EVENTS_TOUCH"); +} // unnamed namespace +#endif + + +namespace +{ + +const unsigned int PRIMARY_TOUCH_BUTTON_ID( 1 ); + +const unsigned int BYTES_PER_CHARACTER_FOR_ATTRIBUTES = 3; + + +// Copied from x server +static unsigned int GetCurrentMilliSeconds(void) +{ + struct timeval tv; + + struct timespec tp; + static clockid_t clockid; + + if (!clockid) + { +#ifdef CLOCK_MONOTONIC_COARSE + if (clock_getres(CLOCK_MONOTONIC_COARSE, &tp) == 0 && + (tp.tv_nsec / 1000) <= 1000 && clock_gettime(CLOCK_MONOTONIC_COARSE, &tp) == 0) + { + clockid = CLOCK_MONOTONIC_COARSE; + } + else +#endif + if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) + { + clockid = CLOCK_MONOTONIC; + } + else + { + clockid = ~0L; + } + } + if (clockid != ~0L && clock_gettime(clockid, &tp) == 0) + { + return (tp.tv_sec * 1000) + (tp.tv_nsec / 1000000L); + } + + gettimeofday(&tv, NULL); + return (tv.tv_sec * 1000) + (tv.tv_usec / 1000); +} + +} // unnamed namespace + + +struct EventHandler::Impl : public WindowEventInterface +{ + // Construction & Destruction + + /** + * Constructor + */ + Impl( EventHandler* handler, XID window, Display* display ) + : mXEventManager(window, display, this), + mHandler( handler ) + { + mXEventManager.Initialize(); + } + /** + * Destructor + */ + ~Impl() + { + } + // @todo Consider allowing the EventHandler class to inherit from WindowEventInterface directly + virtual void TouchEvent( Dali::TouchPoint& point, unsigned long timeStamp ) + { + mHandler->SendEvent( point, timeStamp ); + } + virtual void KeyEvent( Dali::KeyEvent& keyEvent ) + { + mHandler->SendEvent( keyEvent ); + } + virtual void WheelEvent( Dali::WheelEvent& wheelEvent ) + { + mHandler->SendWheelEvent( wheelEvent ); + } + virtual void DamageEvent( Rect& damageArea ) + { + mHandler->SendEvent( damageArea ); + } + virtual void WindowFocusOut( ) + { + // used to do some work with ime + } + virtual void WindowFocusIn() + { + // used to do some work with ime + } + + // Data + XEventManager mXEventManager; + EventHandler* mHandler; +}; + +EventHandler::EventHandler( RenderSurface* surface, CoreEventInterface& coreEventInterface, GestureManager& gestureManager, DamageObserver& damageObserver, DragAndDropDetectorPtr dndDetector ) +: mCoreEventInterface(coreEventInterface), + mGestureManager( gestureManager ), + mStyleMonitor( StyleMonitor::Get() ), + mDamageObserver( damageObserver ), + mRotationObserver( NULL ), + mDragAndDropDetector( dndDetector ), + mClipboardEventNotifier( ClipboardEventNotifier::Get() ), + mClipboard(Clipboard::Get()), + mImpl( NULL ) +{ + Ecore_X_Window window = 0; + + // this code only works with the EcoreX11 RenderSurface so need to downcast + ECore::WindowRenderSurface* ecoreSurface = dynamic_cast< ECore::WindowRenderSurface* >( surface ); + if( ecoreSurface ) + { + window = ecoreSurface->GetXWindow(); + Display* display = static_cast< Display* >(ecore_x_display_get()); + + mImpl = new Impl(this, window, display ); + + } + +} + +EventHandler::~EventHandler() +{ + if(mImpl) + { + delete mImpl; + } + + mGestureManager.Stop(); +} + +void EventHandler::SendEvent(TouchPoint& point, unsigned long timeStamp) +{ + if(timeStamp < 1) + { + timeStamp = GetCurrentMilliSeconds(); + } + + Integration::TouchEvent touchEvent; + Integration::HoverEvent hoverEvent; + Integration::TouchEventCombiner::EventDispatchType type = mCombiner.GetNextTouchEvent(point, timeStamp, touchEvent, hoverEvent); + if(type != Integration::TouchEventCombiner::DispatchNone ) + { + DALI_LOG_INFO(gTouchEventLogFilter, Debug::General, "%d: Device %d: Button state %d (%.2f, %.2f)\n", timeStamp, point.deviceId, point.state, point.local.x, point.local.y); + + // First the touch and/or hover event & related gesture events are queued + if(type == Integration::TouchEventCombiner::DispatchTouch || type == Integration::TouchEventCombiner::DispatchBoth) + { + mCoreEventInterface.QueueCoreEvent( touchEvent ); + mGestureManager.SendEvent(touchEvent); + } + + if(type == Integration::TouchEventCombiner::DispatchHover || type == Integration::TouchEventCombiner::DispatchBoth) + { + mCoreEventInterface.QueueCoreEvent( hoverEvent ); + } + + // Next the events are processed with a single call into Core + mCoreEventInterface.ProcessCoreEvents(); + } +} + +void EventHandler::SendEvent(KeyEvent& keyEvent) +{ + Dali::PhysicalKeyboard physicalKeyboard = PhysicalKeyboard::Get(); + if ( physicalKeyboard ) + { + if ( ! KeyLookup::IsDeviceButton( keyEvent.keyPressedName.c_str() ) ) + { + GetImplementation( physicalKeyboard ).KeyReceived( keyEvent.time > 1 ); + } + } + + // Create KeyEvent and send to Core. + Integration::KeyEvent event(keyEvent.keyPressedName, keyEvent.keyPressed, keyEvent.keyCode, + keyEvent.keyModifier, keyEvent.time, static_cast(keyEvent.state)); + mCoreEventInterface.QueueCoreEvent( event ); + mCoreEventInterface.ProcessCoreEvents(); +} + +void EventHandler::SendWheelEvent( WheelEvent& wheelEvent ) +{ + // Create WheelEvent and send to Core. + Integration::WheelEvent event( static_cast< Integration::WheelEvent::Type >(wheelEvent.type), wheelEvent.direction, wheelEvent.modifiers, wheelEvent.point, wheelEvent.z, wheelEvent.timeStamp ); + mCoreEventInterface.QueueCoreEvent( event ); + mCoreEventInterface.ProcessCoreEvents(); +} + +void EventHandler::SendEvent( StyleChange::Type styleChange ) +{ + DALI_ASSERT_DEBUG( mStyleMonitor && "StyleMonitor Not Available" ); + GetImplementation( mStyleMonitor ).StyleChanged(styleChange); +} + +void EventHandler::SendEvent( const DamageArea& area ) +{ + mDamageObserver.OnDamaged( area ); +} + +void EventHandler::SendRotationPrepareEvent( const RotationEvent& event ) +{ + if( mRotationObserver != NULL ) + { + mRotationObserver->OnRotationPrepare( event ); + } +} + +void EventHandler::SendRotationRequestEvent( ) +{ + if( mRotationObserver != NULL ) + { + mRotationObserver->OnRotationRequest( ); + } +} + +void EventHandler::FeedTouchPoint( TouchPoint& point, int timeStamp) +{ + SendEvent(point, timeStamp); +} + +void EventHandler::FeedWheelEvent( WheelEvent& wheelEvent ) +{ + SendWheelEvent( wheelEvent ); +} + +void EventHandler::FeedKeyEvent( KeyEvent& event ) +{ + SendEvent( event ); +} + +void EventHandler::FeedEvent( Integration::Event& event ) +{ + mCoreEventInterface.QueueCoreEvent( event ); + mCoreEventInterface.ProcessCoreEvents(); +} + +void EventHandler::Reset() +{ + mCombiner.Reset(); + + // Any touch listeners should be told of the interruption. + Integration::TouchEvent event; + TouchPoint point(0, TouchPoint::Interrupted, 0, 0); + event.AddPoint( point ); + + // First the touch event & related gesture events are queued + mCoreEventInterface.QueueCoreEvent( event ); + mGestureManager.SendEvent( event ); + + // Next the events are processed with a single call into Core + mCoreEventInterface.ProcessCoreEvents(); +} + +void EventHandler::SetDragAndDropDetector( DragAndDropDetectorPtr detector ) +{ + mDragAndDropDetector = detector; +} + +void EventHandler::SetRotationObserver( RotationObserver* observer ) +{ + mRotationObserver = observer; +} + +} // namespace Adaptor + +} // namespace Internal + +} // namespace Dali diff --git a/adaptors/x11/x-events/debug/x-input2-debug.cpp b/adaptors/x11/x-events/debug/x-input2-debug.cpp new file mode 100644 index 0000000..8191e81 --- /dev/null +++ b/adaptors/x11/x-events/debug/x-input2-debug.cpp @@ -0,0 +1,289 @@ + // EXTERNAL INCLUDES +#include +#include +#include +#include + +#ifdef DEBUG_ENABLED +#include +#include +#include +#include +#endif + +namespace Dali +{ + +namespace Internal +{ + +namespace Adaptor +{ + +namespace X11Debug +{ + +#ifdef DEBUG_ENABLED + +namespace +{ + + +Integration::Log::Filter* gInputDeviceLogFilter = Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_X_INPUT_DEVICES"); +Integration::Log::Filter* gInputEventLogFilter = Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_X_INPUT_EVENTS"); + +struct XNameId +{ + const char* const name; + int id; +}; + +const XNameId eventTable[]= +{ + { "XI_KeyPress" ,XI_KeyPress }, + { "XI_KeyRelease" ,XI_KeyRelease }, + { "XI_ButtonPress" ,XI_ButtonPress }, + { "XI_ButtonRelease" ,XI_ButtonRelease }, + { "XI_Motion" ,XI_Motion }, + { "XI_Enter" ,XI_Enter }, + { "XI_Leave" ,XI_Leave }, + { "XI_FocusIn" ,XI_FocusIn }, + { "XI_FocusOut" ,XI_FocusOut }, + { "XI_HierarchyChanged",XI_HierarchyChanged }, + { "XI_PropertyEvent" ,XI_PropertyEvent }, + { "XI_RawKeyPress" ,XI_RawKeyPress }, + { "XI_RawKeyRelease" ,XI_RawKeyRelease }, + { "XI_RawButtonPress" ,XI_RawButtonPress }, + { "XI_RawButtonRelease",XI_RawButtonRelease }, + { "XI_RawMotion" ,XI_RawMotion }, + { "XI_TouchBegin" ,XI_TouchBegin }, + { "XI_TouchUpdate" ,XI_TouchUpdate }, + { "XI_TouchEnd" ,XI_TouchEnd }, + { "XI_TouchOwnership" ,XI_TouchOwnership }, + { "XI_RawTouchBegin" ,XI_RawTouchBegin }, + { "XI_RawTouchUpdate" ,XI_RawTouchUpdate }, + { "XI_RawTouchEnd" ,XI_RawTouchEnd } +}; + +const XNameId deviceTypeTable[]= +{ + { "Master Pointer " ,XIMasterPointer }, + { "Master Keyboard" ,XIMasterKeyboard }, + { "Slave Pointer " ,XISlavePointer }, + { "Slave Keyboard " ,XISlaveKeyboard }, + { "Floating Slave " ,XIFloatingSlave } +}; + +const XNameId inputClassTable[]= +{ + { "Key" ,XIKeyClass }, + { "Button" ,XIButtonClass }, + { "Valuator" ,XIValuatorClass }, + { "Scroll" ,XIScrollClass }, + { "Touch" ,XITouchClass } +}; + +const unsigned int numberEvents = sizeof( eventTable ) / sizeof( eventTable[0] ); +const unsigned int numberDevices = sizeof( deviceTypeTable ) / sizeof( deviceTypeTable[0] ); +const unsigned int numberInputClasses = sizeof( inputClassTable ) / sizeof( inputClassTable[0] ); + +const char* GetEventName( int eventId ) +{ + for( unsigned int i = 0; i < numberEvents; ++i ) + { + if( eventTable[i].id == eventId ) + { + return eventTable[i].name; + } + } + return "unknown event"; +} +const char* GetDeviceHierachyName( int deviceType ) +{ + for( unsigned int i = 0; i < numberDevices; ++i ) + { + if( deviceTypeTable[i].id == deviceType ) + { + return deviceTypeTable[i].name; + } + } + return "unknown device"; +} +const char* GetInputClassName( int classId ) +{ + for( unsigned int i = 0; i < numberInputClasses; ++i ) + { + if( inputClassTable[i].id == classId ) + { + return inputClassTable[i].name; + } + } + return "unknown input class name"; +} + +std::string GetInputDeviceInfo( const XIDeviceInfo* device, bool master ) +{ + // formatted output similar to xinput -list except it includes class + source information + int startWidth = 45; + + std::string slavePadding=" ↳ "; + if( master ) + { + // slave entries are shifted to the right + startWidth += 4; + slavePadding=""; + } + + std::ostringstream oss; + oss << "⎜" << slavePadding << std::setw(startWidth) << std::left << device->name ; + oss << std::setw(1) << " id= " << std::setw(1) << device->deviceid ; + oss << "\t[" << GetDeviceHierachyName( device->use ) << " ("<< device->attachment << ") ]"; + oss << std::setw(1) << "\t Classes: "; + + for( int n = 0; n < device->num_classes; ++n ) + { + XIAnyClassInfo *classInfo = device->classes[n]; + oss << GetInputClassName( classInfo->type ) << ", source ( "<< classInfo->sourceid << ")"; + } + oss << "\n"; + + return oss.str(); +} + + +}// unanmed namespace + +void LogInputDeviceInfo( const XIDeviceInfo* devices, unsigned int numberOfDevices ) +{ + // early exit if the filter is not enabled in debug mode + if( ! gInputDeviceLogFilter->IsEnabledFor( Debug::General ) ) + { + return; + } + + const XIDeviceInfo* device = devices; + const XIDeviceInfo* masterKeyboard = NULL; + const XIDeviceInfo* masterPointer = NULL; + Dali::Vector< const XIDeviceInfo* > slaveKeyboards; + Dali::Vector< const XIDeviceInfo* > slavePointers; + Dali::Vector< const XIDeviceInfo* > floatingSlaves; + + // go through the device list and sort by type + for( unsigned int i = 0; i < numberOfDevices; ++i, ++device ) + { + switch( device->use ) + { + case XIMasterPointer: + { + masterPointer = device; + break; + } + case XIMasterKeyboard: + { + masterKeyboard = device; + break; + } + case XISlavePointer: + { + slavePointers.PushBack( device ); + break; + } + case XISlaveKeyboard: + { + slaveKeyboards.PushBack( device ); + break; + } + case XIFloatingSlave: + { + floatingSlaves.PushBack( device ); + break; + } + default: + { + break; + } + } + } + + std::ostringstream oss; + + oss << "\n" << GetInputDeviceInfo( masterKeyboard , true); + for( VectorBase::SizeType i = 0; i < slaveKeyboards.Count(); ++i ) + { + oss << GetInputDeviceInfo( slaveKeyboards[i], false ); + } + oss << "\n" << GetInputDeviceInfo( masterPointer, true ); + for( VectorBase::SizeType i = 0; i < slavePointers.Count(); ++i ) + { + oss << GetInputDeviceInfo( slavePointers[i], false); + } + for( VectorBase::SizeType i = 0; i < floatingSlaves.Count(); ++i ) + { + oss << GetInputDeviceInfo( floatingSlaves[i], false ); + } + + // DALI_LOG_ERROR_NOFN( "%s \n",oss.str().c_str() ); + DALI_LOG_INFO( gInputDeviceLogFilter, Debug::General, "%s\n", oss.str().c_str() ); +} + +void LogXI2Event( XGenericEventCookie* cookie ) +{ + // early exit if the filter is not enabled + if( ! gInputEventLogFilter->IsEnabledFor( Debug::General ) ) + { + return; + } + + std::ostringstream oss; + oss << "XI2 event:" << GetEventName( cookie->evtype ); + + XIDeviceEvent *event = static_cast< XIDeviceEvent* >(cookie->data); + + oss << ", device_id("<< event->deviceid << ") source_id( "<< event->sourceid << ")" << ", flags: " << event->flags; + oss << ", root-window: " << event->root << ", event-window: "<< event->event << ", child-window:" << event->child; + if( cookie->evtype == XI_KeyPress) + { + oss << "base " << event->mods.base << "latched " << event->mods.latched; + oss << "locked " << event->mods.locked << "effective " << event->mods.effective; + + if( event->mods.effective & ShiftMask) oss << "Shift"; + if( event->mods.effective & LockMask) oss << "LockMask"; // caps lock + if( event->mods.effective & ControlMask) oss << "ControlMask"; + if( event->mods.effective & Mod1Mask) oss << "Mod1Mask"; // alt + if( event->mods.effective & Mod2Mask) oss << "Mod2Mask"; // num lock + if( event->mods.effective & Mod3Mask) oss << "Mod3Mask"; + if( event->mods.effective & Mod4Mask) oss << "Mod4Mask"; // WINDOWS + if( event->mods.effective & Mod5Mask) oss << "Mod5Mask"; // Alt gr + + } + + // Mouse button state + oss << " button state\n"; + for( int i =0; i< event->buttons.mask_len ; i++) + { + oss << "," << int(event->buttons.mask[i]); + } + + // DALI_LOG_ERROR_NOFN( "%s \n",oss.str().c_str() ); + DALI_LOG_INFO( gInputEventLogFilter, Debug::General, "%s\n", oss.str().c_str() ); + +} + + + +#else + +void LogDeviceInfo( Display* display, XIDeviceInfo* device ) +{ +} +void LogXI2Event( XGenericEventCookie* cookie ) +{ +} + +#endif + + +} // X11 Debug +} // namespace Adaptor +} // namespace Internal +} // namespace Dali diff --git a/adaptors/x11/x-events/debug/x-input2-debug.h b/adaptors/x11/x-events/debug/x-input2-debug.h new file mode 100644 index 0000000..cbabe68 --- /dev/null +++ b/adaptors/x11/x-events/debug/x-input2-debug.h @@ -0,0 +1,72 @@ +#ifndef __DALI_INTERNAL_X_INPUT_2_DEBUG_H__ +#define __DALI_INTERNAL_X_INPUT_2_DEBUG_H__ + +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * 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. + * + */ + + + +namespace Dali +{ + +namespace Internal +{ + +namespace Adaptor +{ + +namespace X11Debug +{ + +/** + * To log input devices found on the system buid DALi in debug mode. + * Then on the command line: + * + * export LOG_X_INPUT_DEVICES=2 + * dali-demo + * + * + * To log XInput events + * + * export LOG_X_INPUT_EVENTS=2 + * dali-demo + * + * 2 = LogLevel::General + */ + + +/** + * @brief Debug log input device information. + * Similar output to command line tool 'xinput -list' except it includes class + source information + * Useful if the device doesn't have xinput tool installed + * @param devices array of XIDeviceInfo + * @param numberOfDevices number of devices + */ +void LogInputDeviceInfo( const XIDeviceInfo* devices, unsigned int numberOfDevices ); + +/** + * @brief Debug log input event information. + * @param cookie input event cookie + */ +void LogXI2Event( XGenericEventCookie* cookie ); + +} // X11 Debug +} // namespace Adaptor +} // namespace Internal +} // namespace Dali + +#endif diff --git a/adaptors/x11/x-events/x-event-manager.cpp b/adaptors/x11/x-events/x-event-manager.cpp new file mode 100644 index 0000000..36ab25d --- /dev/null +++ b/adaptors/x11/x-events/x-event-manager.cpp @@ -0,0 +1,86 @@ +// CLASS HEADER +#include "x-event-manager.h" + +// EXTERNAL INCLUDES +#include +#include +#include +#include + +namespace Dali +{ + +namespace Internal +{ + +namespace Adaptor +{ + +namespace +{ + +#if defined(DEBUG_ENABLED) +//Dali::Integration::Log::Filter* gInputDeviceLogFilter = Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_X_EVENT"); +#endif + +} + +XEventManager::XEventManager( XID window, Display* display, WindowEventInterface* eventInterface ) +: mXInput2( window, display, eventInterface ), + mFileDescriptorMonitor( NULL ), + mDisplay( display ), + mWindow( window ), + mInitialized( false ) +{ + +} +XEventManager::~XEventManager() +{ + delete mFileDescriptorMonitor; +} + +void XEventManager::Initialize() +{ + if( mInitialized ) + { + return; + } + + mXInput2.Initialize(); + + // Start monitoring for X events on a file descriptor return via ConnectionNumber. + int fileDescriptor = ConnectionNumber( mDisplay ); + + CallbackBase* callback = MakeCallback( this, &XEventManager::XEventReceived); + + mFileDescriptorMonitor = new FileDescriptorMonitor( fileDescriptor, callback ); + + mInitialized = true; +} + + +void XEventManager::XEventReceived() +{ + while( XPending( mDisplay) ) + { + XEvent xEvent; + XNextEvent( mDisplay, &xEvent ); + + // cookie data pointer is undefined until XGetEventData is called. + XGenericEventCookie* cookie = &xEvent.xcookie; + + if (XGetEventData( mDisplay, cookie)) + { + if( cookie->extension == mXInput2.GetExtensionId() ) + { + mXInput2.ProcessEvent( cookie ); + } + XFreeEventData( mDisplay, cookie ); + } + } +} + + +} // namespace internal +} // namespace adaptor +} // namespace dali diff --git a/adaptors/x11/x-events/x-event-manager.h b/adaptors/x11/x-events/x-event-manager.h new file mode 100644 index 0000000..d4dd963 --- /dev/null +++ b/adaptors/x11/x-events/x-event-manager.h @@ -0,0 +1,95 @@ +#ifndef __DALI_INTERNAL_X_EVENT_MANAGER_H__ +#define __DALI_INTERNAL_X_EVENT_MANAGER_H__ + +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * 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. + * + */ +// EXTERNAL INCLUDES +#include +#include +#include + +// INTERNAL INCLUDES +#include +#include "x-input2.h" + +namespace Dali +{ + +namespace Internal +{ + +namespace Adaptor +{ + +/** + * + * @brief Used to handle X events. + * The code is mainloop agnostic, so the monitoring of the X event file descriptor + * for X events is external to this class. + * + */ +class XEventManager +{ + +public: + + /** + * Constructor + * @param[in] window ID + * @param[in] display x-connection + * @param[in] eventInterface window event interface + */ + XEventManager( XID window, Display* display, WindowEventInterface* eventInterface ); + + /** + * @brief non virtual destructor + */ + ~XEventManager(); + + /** + * @brief Initialize + */ + void Initialize(); + +private: + + /** + * @brief Should be called when the Event file descriptor signals data is available + */ + void XEventReceived(); + + // Undefined copy constructor. + XEventManager( const XEventManager& ); + + // Undefined assignment operator. + XEventManager& operator=( const XEventManager& ); + +private: + + XInput2 mXInput2; ///< XInput2 handler + FileDescriptorMonitor* mFileDescriptorMonitor; ///< File descriptor monitor for X events + Display* mDisplay; ///< X connection + XID mWindow; ///< Window to receive events for + bool mInitialized:1; + +}; + +} // namespace Adaptor +} // namespace Internal +} // namespace Dali + +#endif // __DALI_INTERNAL_X_EVENT_MANAGER_H__ diff --git a/adaptors/x11/x-events/x-input2-device.cpp b/adaptors/x11/x-events/x-input2-device.cpp new file mode 100644 index 0000000..a1b4ede --- /dev/null +++ b/adaptors/x11/x-events/x-input2-device.cpp @@ -0,0 +1,66 @@ +//CLASS HEADER +#include "x-input2-device.h" + +// EXTERNAL INCLUDES + + +namespace Dali +{ + +namespace Internal +{ + +namespace Adaptor +{ + +void XInput2Device::AssignDeviceInfo( const XIDeviceInfo* device ) +{ + deviceId = device->deviceid; + attachment = device->attachment; + use = device->use; + + for( int n = 0; n < device->num_classes; ++n ) + { + XIAnyClassInfo *classInfo = device->classes[n]; + switch( classInfo->type ) + { + case XITouchClass: + { + touchClass = true; + break; + } + case XIButtonClass: + { + buttonClass = true; + break; + } + case XIValuatorClass: + { + valuatorClass = true; + break; + } + case XIScrollClass: + { + scrollClass = true; + break; + } + case XIKeyClass: + { + keyClass = true; + break; + } + default: + { + // unknown + break; + } + } + } + + +} + + +} // namespace Adaptor +} // namespace Internal +} // namespace Dali diff --git a/adaptors/x11/x-events/x-input2-device.h b/adaptors/x11/x-events/x-input2-device.h new file mode 100644 index 0000000..59d0e52 --- /dev/null +++ b/adaptors/x11/x-events/x-input2-device.h @@ -0,0 +1,71 @@ +#ifndef __DALI_INTERNAL_X_INPUT2_DEVICE_H__ +#define __DALI_INTERNAL_X_INPUT2_DEVICE_H__ + +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * 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. + * + */ + +// EXTERNAL INCLUDES +#include + +namespace Dali +{ +namespace Internal +{ +namespace Adaptor +{ + +/** + * @brief struct used to encpasulate XIDeviceInfo information. + * Kept as a POD so it can be used in a Dali::Vector + */ +struct XInput2Device +{ + /** + * @brief constructor + */ + XInput2Device() + : deviceId(0), + attachment(0), + use(0), + keyClass(false), + touchClass(false), + buttonClass(false), + valuatorClass(false), + scrollClass(false) + {} + + /** + * Assign device information to the object + */ + void AssignDeviceInfo( const XIDeviceInfo* device ); + + int deviceId; ///< X device ID + int attachment; ///< see XI2 DEVICEINFO struct for details + int use; ///< see XI2 DEVICEINFO struct for details + bool keyClass:1; ///< device supports key input + bool touchClass:1; ///< device supports touch input + bool buttonClass:1; ///< device supports button input + bool valuatorClass:1; ///< device supports an axis, e.g. mouse axis, tablet pen tilt angle.. + bool scrollClass:1; ///< device supports scroll + +}; + +} // namespace Adaptor +} // namespace Internal +} // namespace Dali + +#endif diff --git a/adaptors/x11/x-events/x-input2.cpp b/adaptors/x11/x-events/x-input2.cpp new file mode 100644 index 0000000..b965609 --- /dev/null +++ b/adaptors/x11/x-events/x-input2.cpp @@ -0,0 +1,315 @@ +// CLASS HEADER +#include "x-input2.h" + +// EXTERNAL INCLUDES +#include +#include + +// INTERNAL INCLUDES +#include "debug/x-input2-debug.h" + + +namespace Dali +{ + +namespace Internal +{ + +namespace Adaptor +{ + +namespace +{ +// For multi-touch we need XI2 version 2.2 +int XI2MinorVersionRequired = 2; +int XI2MajorVersionRequired = 2; +} + +XInput2::XInput2( XID window, Display* display, WindowEventInterface* eventInterface ) +: mEventInterface( eventInterface ), + mDisplay( display ), + mWindow( window ), + mXI2ExtensionId(-1), + mMultiTouchSupport( false ) +{ + +} +XInput2::~XInput2() +{ +} + +void XInput2::Initialize() +{ + // Check if X supports the multi-touch protocol + QueryMultiTouchSupport(); + + // Query what input devices are available on the system. + QueryDevices(); + + // Select the input events we want to receive from the input devices available + SelectInputEvents(); + +} + +int XInput2::GetExtensionId() const +{ + return mXI2ExtensionId; +} + +bool XInput2::FilteredDevice( int deviceId ) const +{ + for( VectorBase::SizeType i = 0; i < mInputDeviceInfo.Count(); ++i ) + { + if( mInputDeviceInfo[i].deviceId == deviceId ) + { + return true; + } + } + return false; +} + +bool XInput2::PreProcessEvent( XIDeviceEvent *deviceEvent ) const +{ + // @todo need to do some testing to see if this check is actually required + // E.g. if IME window is sending events, this check may fail + if( deviceEvent->event != mWindow ) + { + return false; + } + // emulated flags means that the event has been emulated from another XI 2.x event for legacy client support + // We don't call XISelectEvents on these events so hopefully shouldn't get them. + if( ( deviceEvent->flags & XIPointerEmulated ) || ( deviceEvent->flags & XITouchEmulatingPointer ) ) + { + return false; + } + + if( !FilteredDevice( deviceEvent->deviceid )) + { + return false; + } + return true; +} + +void XInput2::CreateKeyEvent( const XIDeviceEvent* deviceEvent, KeyEvent& keyEvent ) const +{ + // get the physical key code ( range 8..255) + KeyCode keycode = deviceEvent->detail; + + keyEvent.keyCode = keycode; + keyEvent.state = KeyEvent::Down; + keyEvent.keyModifier = deviceEvent->mods.effective; + + // extract key symbol. The symbol is typically the name visible on the key + // e.g. key code 201 might = Brightness increase, or a Korean character depending on the keyboard mapping. + // @todo For XKbKeycodeToKeysym to work correctly we need the group and level. + // investigate using XkbGetState to get initial state then start monitoring for XkbStateNotify events + KeySym sym = XkbKeycodeToKeysym( mDisplay, keycode, 0 /* group */ , keyEvent.IsShiftModifier() ); + char* keyname = XKeysymToString( sym ); + + keyEvent.keyPressedName = keyname; + keyEvent.time = deviceEvent->time; + +} + +void XInput2::ProcessEvent( XGenericEventCookie* cookie ) +{ + XIDeviceEvent* deviceEvent = static_cast< XIDeviceEvent* >(cookie->data); + + X11Debug::LogXI2Event( cookie ); + + bool requiresProcessing = PreProcessEvent( deviceEvent ); + + if( ! requiresProcessing ) + { + return; + } + + TouchPoint point ( deviceEvent->deviceid, TouchPoint::Last, deviceEvent->event_x, deviceEvent->event_y ); + Time time( deviceEvent->time ); // X is using uint32 for time field ( see XI2proto.h ) + + switch( cookie->evtype) + { + case XI_TouchUpdate: + case XI_Motion: + { + point.state = TouchPoint::Motion; + mEventInterface->TouchEvent( point, time ); + break; + } + case XI_TouchBegin: + case XI_ButtonPress: + { + point.state = TouchPoint::Down; + mEventInterface->TouchEvent( point, time ); + break; + } + case XI_TouchEnd: + case XI_ButtonRelease: + { + point.state = TouchPoint::Up; + mEventInterface->TouchEvent( point, time ); + break; + } + case XI_FocusIn: + { + mEventInterface->WindowFocusIn(); + break; + } + case XI_FocusOut: + { + mEventInterface->WindowFocusOut(); + break; + } + case XI_KeyPress: + { + KeyEvent keyEvent; + CreateKeyEvent( deviceEvent, keyEvent ); + mEventInterface->KeyEvent( keyEvent ); + break; + } + default: + { + break; + } + } +} + +void XInput2::QueryMultiTouchSupport() +{ + int minor = XI2MinorVersionRequired; + int major = XI2MajorVersionRequired; + int firstEventCode, firstErrorCode; + + // Check if the extension is available and get the extension id + if( !XQueryExtension(mDisplay, "XInputExtension", &mXI2ExtensionId, &firstEventCode, &firstErrorCode) ) + { + DALI_LOG_ERROR(" XInputExtension not available \n"); + return; + } + + // inform X that DALi ( the client ) supports XI2 version 2.2 + // it will assign the X servers supported version to the parameters + int ret = XIQueryVersion( mDisplay, &major, &minor); + if( ret == BadValue ) + { + DALI_LOG_ERROR(" XIQueryVersion %d,%d failed \n", major, minor ); + return; + } + + // check the version is supports multi-touch + if( ( major * 1000 + minor ) >= ( XI2MajorVersionRequired * 1000 + XI2MinorVersionRequired ) ) + { + mMultiTouchSupport = true; + } + else + { + DALI_LOG_ERROR( "XInput 2.2 or greater required for multi-touch\n"); + } + +} +void XInput2::QueryDevices() + { + int numberOfDevices( 0 ); + + // QueryDevice returns information about one or more input devices + XIDeviceInfo* deviceInfoArray = XIQueryDevice( mDisplay, XIAllDevices, &numberOfDevices); + XIDeviceInfo* device = deviceInfoArray; + + X11Debug::LogInputDeviceInfo( deviceInfoArray, numberOfDevices ); + + mInputDeviceInfo.Resize( numberOfDevices ); + + for( int i = 0; i < numberOfDevices; ++i, ++device ) + { + XInput2Device info; + + info.AssignDeviceInfo( device ); + + mInputDeviceInfo.PushBack( info ); + } + + XIFreeDeviceInfo( deviceInfoArray ); + } + +void XInput2::SelectEvents( int deviceId, const Dali::Vector< unsigned int >& filter ) +{ + if( filter.Size() == 0) + { + return; + } + + // each event like XI_ButtonPress is stored as unique bit, so if there's 32 events we need 4 bytes + // the XIMaskLen macro provides the length for us at compile time. + unsigned char mask[ XIMaskLen( XI_LASTEVENT ) ] = {}; + XIEventMask eventMask; + + eventMask.deviceid = deviceId; + eventMask.mask_len = sizeof( mask); + eventMask.mask = mask; + + for( VectorBase::SizeType i = 0; i< filter.Count(); ++i ) + { + XISetMask( mask, filter[i] ); + } + + XISelectEvents( mDisplay, mWindow, &eventMask, 1); + +} +void XInput2::SelectInputEvents() +{ + /* + * From the X documentation: + * "A master pointer is a virtual pointer device that does not represent a physical device. + * If a slave device generates an event, the event is also generated by the respective master device. + * Multiple slave devices can be attached to a single master device." + * master = cursor / keyboard focus, + * slave = physical device + * + * For motion events, we currently just listen to the slave devices. This allows us the ability to + * perform a XIGrabDevice on the slave if we need to, which will temporarily detach it from the master. + * In DALi we currently don't perform a grab as typically we just have a single x-window displayed. + * Where as other toolkits may have a window for a popup and want do know when the mouse is clicked outside + * of the popup, to close it. + */ + Dali::Vector< unsigned int > eventFilter; + eventFilter.Reserve( 6 ); // typically filter no more than 6 events + + for( VectorBase::SizeType i = 0; i < mInputDeviceInfo.Count(); ++i ) + { + const XInput2Device& device( mInputDeviceInfo[ i ] ); + + eventFilter.Clear(); + + if( ( device.use == XIFloatingSlave ) || ( device.use == XISlavePointer )) + { + if( device.buttonClass ) + { + eventFilter.PushBack( XI_ButtonPress ); + eventFilter.PushBack( XI_ButtonRelease ); + eventFilter.PushBack( XI_Motion ); + } + if( device.touchClass ) + { + eventFilter.PushBack( XI_TouchUpdate ); + eventFilter.PushBack( XI_TouchBegin ); + eventFilter.PushBack( XI_TouchEnd ); + } + SelectEvents( device.deviceId, eventFilter ); + } + // @todo work out if we should just be listening to MasterKeyboard + else if( device.use == XISlaveKeyboard ) + { + if( device.keyClass ) + { + eventFilter.PushBack( XI_KeyPress ); + eventFilter.PushBack( XI_KeyRelease ); + + SelectEvents( device.deviceId, eventFilter ); + } + + } + } +} +} // namespace internal +} // namespace adaptor +} // namespace dali diff --git a/adaptors/x11/x-events/x-input2.h b/adaptors/x11/x-events/x-input2.h new file mode 100644 index 0000000..abc3f5b --- /dev/null +++ b/adaptors/x11/x-events/x-input2.h @@ -0,0 +1,141 @@ +#ifndef __DALI_INTERNAL_X_INPUT_2_MANAGER_H__ +#define __DALI_INTERNAL_X_INPUT_2_MANAGER_H__ + +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * 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. + * + */ + +// EXTERNAL INCLUDES +#include +#include +#include +#include +#include + +// INTERNAL INCLUDES +#include +#include "x-input2-device.h" + +namespace Dali +{ + +namespace Internal +{ + +namespace Adaptor +{ + +/** + * + * @brief Used to setup and process XInput2 events. + * + * For help with debugging, build DALi in debug mode then set the environment variables + * export LOG_X_INPUT_EVENTS=2 + * export LOG_X_INPUT_DEVICES=2 + */ +class XInput2 +{ + +public: + + /** + * @brief Constructor + */ + XInput2( XID window, Display* display, WindowEventInterface* eventInterface ); + + /** + * @brief destructor + */ + ~XInput2(); + + /** + * @brief enumerates input devices using XIQueryDevice then sets up event filtering using XISelectEvents + */ + void Initialize(); + + /** + * @brief get X the extension id + * @return the Id + */ + int GetExtensionId() const; + + /** + * @brief process an XInput2 event + * @param cookie X cookie + */ + void ProcessEvent( XGenericEventCookie* cookie ); + + +private: + + /** + * @brief query x input devices + */ + void QueryDevices(); + + /** + * @brief query multi-touch support + */ + void QueryMultiTouchSupport(); + + /** + * Uses XISelectEvents to select the events we want to recieve from each input device + */ + void SelectInputEvents(); + + /** + * @brief checks if we are filtering events from a specific device + * @param[in] deviceId device id + * @return true if the device is being filtered + */ + bool FilteredDevice( int deviceId ) const; + + /** + * @brief Select specific events to be filtered on a device + * @param[in] device id + * @param[in] filter vector of X input events like XI_ButtonPress + */ + void SelectEvents( int deviceId, const Dali::Vector< unsigned int >& filter ); + + /** + * @brief checks if the event should be processed + * @param[in] deviceEvent device event + * @return true if should be processed + */ + bool PreProcessEvent( XIDeviceEvent *deviceEvent ) const; + + /** + * @brief creates a DALi key event given a XIDeviceEvent for a key press + * @param[in] deviceEvent device event + * @param[out] keyEvent DALi key event + */ + void CreateKeyEvent( const XIDeviceEvent* deviceEvent, KeyEvent& keyEvent ) const; + +private: + + Dali::Vector< XInput2Device > mInputDeviceInfo; ///< list of input devices + WindowEventInterface* mEventInterface; ///< window event interface + Display* mDisplay; ///< X display + XID mWindow; ///< X window + int mXI2ExtensionId; ///< XI2 extension id + bool mMultiTouchSupport:1; ///< whether multi-touch is supported +}; + +} // namespace Adaptor +} // namespace Internal +} // namespace Dali + +#endif diff --git a/build/tizen/adaptor/Makefile.am b/build/tizen/adaptor/Makefile.am index f39980d..85df3fc 100644 --- a/build/tizen/adaptor/Makefile.am +++ b/build/tizen/adaptor/Makefile.am @@ -164,6 +164,18 @@ endif # WAYLAND endif +# Node JS support for using an external libuv main loop. If not enabled then just use e-core as normal +# Used for things like callbacks, file-monintors, x input handling +if LIB_UV_EVENT_LOOP +main_loop_integration_src_files = $(adaptor_common_internal_uv_src_files) +input_event_handler_src_files = $(adaptor_uv_x_event_handler_internal_src_files) +else +main_loop_integration_src_files = $(adaptor_common_internal_ecore_src_files) +input_event_handler_src_files = $(adaptor_ecore_x_event_handler_internal_src_files) +endif + + + pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = dali-adaptor-integration.pc @@ -179,11 +191,14 @@ lib_LTLIBRARIES = libdali-adaptor.la libdali_adaptor_la_SOURCES = \ $(base_adaptor_src_files) \ + $(main_loop_integration_src_files) \ $(tizen_platform_abstraction_src_files) \ $(text_abstraction_src_files) \ $(devel_api_src_files) \ $(public_api_src_files) \ - $(adaptor_internal_src_files) + $(adaptor_internal_src_files) \ + $(input_event_handler_src_files) + libdali_adaptor_la_DEPENDENCIES = @@ -381,14 +396,19 @@ bin_SCRIPTS = ../../../adaptors/scripts/dalireslog.sh # linking test +# turn off the linker test if were building for libuv +# We can't link to LibUV becase it is statically linked to Node.JS (by default) +if !LIB_UV_EVENT_LOOP noinst_PROGRAMS = linker.test +endif # NOT LIB_UV_EVENT_LOOP linker_test_SOURCES = linker-test.cpp linker_test_CXXFLAGS = \ + -DDALI_ADAPTOR_COMPILATION \ -I../../../adaptors/common \ -I../../../adaptors/public-api \ - -I../../../adaptors/integration-api \ + -I../../../adaptors/integration-api \ -I../../../adaptors/base/interfaces \ -I../../../adaptors/public-api/adaptor-framework \ -I../../../adaptors/devel-api/adaptor-framework \ diff --git a/build/tizen/configure.ac b/build/tizen/configure.ac index 67566e6..0270fe1 100644 --- a/build/tizen/configure.ac +++ b/build/tizen/configure.ac @@ -136,6 +136,32 @@ AC_ARG_ENABLE([gles], DALI_ADAPTOR_CFLAGS="$DALI_ADAPTOR_CFLAGS -DDALI_GLES_VERSION=${enable_gles}" +# node.js by default statically links against libuv, so it doesn't need to install +# a libuv headers/ shared library. So we can't use pkg-config to access any headers. +# As a work around we pass the node deps path so we can access the libuv headers inside nodes +# directory +AC_ARG_WITH([node-js], + [AC_HELP_STRING([--with-node-js], + [Node.JS path that contains Lib UV headers. Setting this configures DALi to work with LibUV mainloop used in Node.JS. + For example /usr/tmp/downloads/node-v0.12.4/deps/uv/include/ ])], + [with_node_js=$withval], + [with_node_js=no]) + +# Node.JS already has a libuv main loop running,so we have to integrate with it +AM_CONDITIONAL(LIB_UV_EVENT_LOOP, test x$with_node_js != xno) + + +build_for_node_js=no +if test "x$with_node_js" != "xno"; then + AC_MSG_NOTICE("build for node_js == yes"); + [build_for_node_js=yes] + DALI_ADAPTOR_CFLAGS="$DALI_ADAPTOR_CFLAGS -DNODE_JS_SUPPORT -I${with_node_js}" +else + #not using node.js build + AC_MSG_NOTICE("build for node_js == no"); +fi + + AC_ARG_WITH([over-tizen_2_2], [AC_HELP_STRING([--with-over-tizen_2_2], [Use tizen API over ver. 2.2])], @@ -271,4 +297,9 @@ Configuration Data Dir (Read Only): $dataReadOnlyDir OVERTIZEN2.2: $with_over_tizen_2_2 EldBus: $eldbus_available + Build for Node.JS (LibUV) $build_for_node_js " +# optional output of node.js source path if we're building for node.js +if test "x$build_for_node_js" != "xno"; then +echo " Node.JS LibUV header path $with_node_js" +fi -- 2.7.4