diff --git a/apps/sample-configs/osvr_server_config.gesture.sample.json b/apps/sample-configs/osvr_server_config.gesture.sample.json new file mode 100644 index 000000000..4a8d34a6f --- /dev/null +++ b/apps/sample-configs/osvr_server_config.gesture.sample.json @@ -0,0 +1,9 @@ +{ + "plugins": [ + "com_osvr_example_Gesture" + ], + "aliases": { + "/me/hands/left/gesture": "/com_osvr_example_Gesture/Gesture/gesture/0", + "/me/hands/right/gesture": "/com_osvr_example_Gesture/Gesture/gesture/1" + } +} diff --git a/examples/clients/CMakeLists.txt b/examples/clients/CMakeLists.txt index dc6abb225..c568c8ea6 100644 --- a/examples/clients/CMakeLists.txt +++ b/examples/clients/CMakeLists.txt @@ -16,6 +16,7 @@ set(CLIENTS Location2D Direction Locomotion + Gesture MinimalInit TrackerState ViewerEyeSurfaces) diff --git a/examples/clients/Gesture.cpp b/examples/clients/Gesture.cpp new file mode 100644 index 000000000..a3ff6186d --- /dev/null +++ b/examples/clients/Gesture.cpp @@ -0,0 +1,71 @@ +/** @file + @brief Implementation + + @date 2015 + + @author + Sensics, Inc. + + +*/ + +// Copyright 2015 Sensics, Inc. +// +// 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. + +// Internal Includes +#include +#include +#include +#include + +// Library/third-party includes +// - none + +// Standard includes +#include +#include + +void gestureCallback(void *userdata, const OSVR_TimeValue * /*timestamp*/, + const OSVR_GestureReport *report) { + auto &ctx = *static_cast(userdata); + + /// You would typically not do this every frame - you'd retrieve the ID + /// based on the string, and then just compare the ID. This is just to make + /// a more compelling example. + std::string name = ctx.getGestureNamefromID(osvr::util::StringID(report->gestureID)); + std::cout << "Gesture: Sensor " << report->sensor << ": ID " + << report->gestureID << " (" << name << ") " + << (report->state == OSVR_GESTURE_COMPLETE ? "COMPLETE" + : "IN PROGRESS") + << std::endl; +} + +int main() { + + osvr::clientkit::ClientContext ctx( + "com.osvr.exampleclients.GestureCallback"); + + osvr::clientkit::Interface gesture = + ctx.getInterface("/com_osvr_example_Gesture/Gesture/gesture"); + + gesture.registerCallback(&gestureCallback, &ctx); + + // Pretend that this is your application's mainloop. + while (1) { + ctx.update(); + } + + std::cout << "Library shut down, exiting." << std::endl; + return 0; +} diff --git a/examples/plugin/CMakeLists.txt b/examples/plugin/CMakeLists.txt index 00604e724..1a3444385 100644 --- a/examples/plugin/CMakeLists.txt +++ b/examples/plugin/CMakeLists.txt @@ -4,6 +4,7 @@ set(OSVR_EXAMPLE_DEVICE_PLUGINS_SIMPLE com_osvr_example_Configured com_osvr_example_DummyDetectAndCreateAsync com_osvr_example_EyeTracker + com_osvr_example_Gesture com_osvr_example_Locomotion com_osvr_example_MultipleAsync org_osvr_example_Tracker) @@ -39,6 +40,7 @@ endforeach() foreach(pluginname com_osvr_example_Configured com_osvr_example_EyeTracker + com_osvr_example_Gesture com_osvr_example_Locomotion org_osvr_example_Tracker com_osvr_example_MultipleAsync) diff --git a/examples/plugin/com_osvr_example_Gesture.cpp b/examples/plugin/com_osvr_example_Gesture.cpp new file mode 100644 index 000000000..8b4d5553a --- /dev/null +++ b/examples/plugin/com_osvr_example_Gesture.cpp @@ -0,0 +1,125 @@ +/** @date 2015 + + @author + Sensics, Inc. + +*/ + +// Copyright 2015 Sensics Inc. +// +// 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. + +// Internal Includes +#include +#include + +// Generated JSON header file +#include "com_osvr_example_Gesture_json.h" + +// Library/third-party includes + +// Standard includes +#include +#include +#include +#include +#include + +// Anonymous namespace to avoid symbol collision +namespace { + +OSVR_MessageType gestureMessage; + +class GestureDevice { + public: + GestureDevice(OSVR_PluginRegContext ctx) { + /// Create the initialization options + OSVR_DeviceInitOptions opts = osvrDeviceCreateInitOptions(ctx); + osvrDeviceGestureConfigure(opts, &m_gesture); + + /// Create the sync device token with the options + m_dev.initSync(ctx, "Gesture", opts); + + // get an ID for gesture names to be used in plugin + osvrDeviceGestureGetID(m_gesture, OSVR_GESTURE_DOUBLE_TAP, + &m_double_tap_gesture); + osvrDeviceGestureGetID(m_gesture, "LASSO", &m_lasso_gesture); + + /// Send JSON descriptor + m_dev.sendJsonDescriptor(com_osvr_example_Gesture_json); + + /// Register update callback + m_dev.registerUpdateCallback(this); + } + + OSVR_ReturnCode update() { + + std::this_thread::sleep_for(std::chrono::milliseconds( + 1000)); // Simulate waiting a quarter second for data. + + OSVR_TimeValue times; + + osvrTimeValueGetNow(×); + + osvrDeviceGestureReportData(m_gesture, m_double_tap_gesture, + OSVR_GESTURE_COMPLETE, 0, ×); + osvrDeviceGestureReportData(m_gesture, m_lasso_gesture, + OSVR_GESTURE_COMPLETE, 1, ×); + + return OSVR_RETURN_SUCCESS; + } + + private: + osvr::pluginkit::DeviceToken m_dev; + OSVR_GestureDeviceInterface m_gesture; + OSVR_GestureID m_lasso_gesture; + OSVR_GestureID m_double_tap_gesture; +}; + +class HardwareDetection { + public: + HardwareDetection() : m_found(false) {} + OSVR_ReturnCode operator()(OSVR_PluginRegContext ctx) { + + if (m_found) { + return OSVR_RETURN_SUCCESS; + } + + std::cout << "PLUGIN: Got a hardware detection request" << std::endl; + + /// we always detect device in sample plugin + m_found = true; + + std::cout << "PLUGIN: We have detected Gesture device! " << std::endl; + /// Create our device object + osvr::pluginkit::registerObjectForDeletion(ctx, new GestureDevice(ctx)); + + return OSVR_RETURN_SUCCESS; + } + + private: + bool m_found; +}; +} // namespace + +OSVR_PLUGIN(com_osvr_example_Gesture) { + + osvrDeviceRegisterMessageType(ctx, "GestureMessage", &gestureMessage); + + osvr::pluginkit::PluginContext context(ctx); + + /// Register a detection callback function object. + context.registerHardwareDetectCallback(new HardwareDetection()); + + return OSVR_RETURN_SUCCESS; +} \ No newline at end of file diff --git a/examples/plugin/com_osvr_example_Gesture.json b/examples/plugin/com_osvr_example_Gesture.json new file mode 100644 index 000000000..9ae999f32 --- /dev/null +++ b/examples/plugin/com_osvr_example_Gesture.json @@ -0,0 +1,20 @@ +{ + "deviceVendor": "Sensics", + "deviceName": "Gesture", + "author": "Georgiy Frolov georgiy@sensics.com", + "version": 1, + "lastModified": "", + "interfaces": { + "gesture": { + "count": 2 + } + }, + "semantic": { + "left": "gesture/0", + "right": "gesture/1" + }, + "automaticAliases": { + "/me/hands/left": "semantic/left", + "/me/hands/right": "semantic/right" + } +} \ No newline at end of file diff --git a/inc/osvr/ClientKit/Context.h b/inc/osvr/ClientKit/Context.h index 71d7b0043..9fd7fa097 100755 --- a/inc/osvr/ClientKit/Context.h +++ b/inc/osvr/ClientKit/Context.h @@ -112,6 +112,31 @@ namespace clientkit { return osvrClientCheckStatus(m_context) == OSVR_RETURN_SUCCESS; } + inline std::string ClientContext::getGestureNamefromID(util::StringID id) { + + size_t length = 0; + OSVR_ReturnCode ret = + osvrClientGetGestureNameLength(m_context, id.value(), &length); + if (OSVR_RETURN_SUCCESS != ret) { + throw std::runtime_error( + "Invalid context or null reference to length variable."); + } + + if (0 == length) { + return std::string(); + } + + util::StringBufferBuilder buf; + + ret = osvrClientGetGestureNameFromID( + m_context, id.value(), buf.getBufferOfSize(length), length); + if (OSVR_RETURN_SUCCESS != ret) { + throw std::runtime_error("Invalid context, null reference to " + "buffer, or buffer is too small."); + } + return buf.str(); + } + } // end namespace clientkit } // end namespace osvr diff --git a/inc/osvr/ClientKit/Context_decl.h b/inc/osvr/ClientKit/Context_decl.h index e805e1d57..52817440f 100644 --- a/inc/osvr/ClientKit/Context_decl.h +++ b/inc/osvr/ClientKit/Context_decl.h @@ -25,17 +25,9 @@ #ifndef INCLUDED_ClientContext_decl_h_GUID_1EFFF79A_3D9F_4794_9F98_37010949F386 #define INCLUDED_ClientContext_decl_h_GUID_1EFFF79A_3D9F_4794_9F98_37010949F386 -// Internal Includes -// - none - -// Library/third-party includes -// - none - -// Standard includes -// - none - // Internal Includes #include +#include // Library/third-party includes #include @@ -95,6 +87,11 @@ namespace clientkit { /// from false to true without calling update() - consider a loop. bool checkStatus() const; + /// @brief converts gesture ID to string name. Not for frequent use - + /// typical usage is to get a gesture ID from a string, and use that for + /// comparison. + std::string getGestureNamefromID(util::StringID id); + /// @brief Gets the bare OSVR_ClientContext. OSVR_ClientContext get(); diff --git a/inc/osvr/ClientKit/InterfaceC.h b/inc/osvr/ClientKit/InterfaceC.h index 728350536..249d75d76 100644 --- a/inc/osvr/ClientKit/InterfaceC.h +++ b/inc/osvr/ClientKit/InterfaceC.h @@ -40,7 +40,7 @@ /* none */ /* Standard includes */ -/* none */ +#include OSVR_EXTERN_C_BEGIN /** @addtogroup ClientKit @@ -69,6 +69,29 @@ osvrClientGetInterface(OSVR_ClientContext ctx, const char path[], OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode osvrClientFreeInterface(OSVR_ClientContext ctx, OSVR_ClientInterface iface); +/** @brief Get the length of a string parameter associated with the given path. +@param ctx Client context +@param path A resource path (null-terminated string) +@param[out] len The length of the string value, including null terminator. 0 +if the parameter does not exist or is not a string. + +*/ +OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode osvrClientGetGestureNameLength( + OSVR_ClientContext ctx, uint32_t id, size_t *len); + +/** @brief Convert the gesture ID to string name representation + + @param ctx Client context + @param id An id that corresponds to an entry in gesture string to ID map + @param [in, out] buf A buffer that you allocate of appropriate size. + Must be at least the length returned by osvrClientGetStringParameterLength. + Will contain the null-terminated string name of the gesture ID. + @param len The length of the buffer you're providing. If the buffer is too + short, an error is returned and the buffer is unchanged. +*/ +OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode osvrClientGetGestureNameFromID( + OSVR_ClientContext ctx, uint32_t id, char *buf, size_t len); + /** @} */ OSVR_EXTERN_C_END diff --git a/inc/osvr/ClientKit/InterfaceCallbackC.h b/inc/osvr/ClientKit/InterfaceCallbackC.h index dde1cef97..9f2825a9b 100644 --- a/inc/osvr/ClientKit/InterfaceCallbackC.h +++ b/inc/osvr/ClientKit/InterfaceCallbackC.h @@ -69,6 +69,7 @@ OSVR_INTERFACE_CALLBACK_METHOD(EyeTracker3D) OSVR_INTERFACE_CALLBACK_METHOD(EyeTrackerBlink) OSVR_INTERFACE_CALLBACK_METHOD(NaviVelocity) OSVR_INTERFACE_CALLBACK_METHOD(NaviPosition) +OSVR_INTERFACE_CALLBACK_METHOD(Gesture) #undef OSVR_INTERFACE_CALLBACK_METHOD diff --git a/inc/osvr/ClientKit/InterfaceStateC.h b/inc/osvr/ClientKit/InterfaceStateC.h index edf9f085c..2358c340f 100644 --- a/inc/osvr/ClientKit/InterfaceStateC.h +++ b/inc/osvr/ClientKit/InterfaceStateC.h @@ -71,6 +71,7 @@ OSVR_CALLBACK_METHODS(EyeTracker3D) OSVR_CALLBACK_METHODS(EyeTrackerBlink) OSVR_CALLBACK_METHODS(NaviVelocity) OSVR_CALLBACK_METHODS(NaviPosition) +OSVR_CALLBACK_METHODS(Gesture) #undef OSVR_CALLBACK_METHODS diff --git a/inc/osvr/Common/ClientContext.h b/inc/osvr/Common/ClientContext.h index da7c3c721..b4526fdb0 100644 --- a/inc/osvr/Common/ClientContext.h +++ b/inc/osvr/Common/ClientContext.h @@ -34,7 +34,7 @@ #include #include #include -#include +#include // Library/third-party includes #include @@ -87,6 +87,9 @@ struct OSVR_ClientContextObject : boost::noncopyable { /// @brief Accessor for the path tree. OSVR_COMMON_EXPORT osvr::common::PathTree const &getPathTree() const; + /// @brief Accessor for the system component + OSVR_COMMON_EXPORT osvr::common::SystemComponent *getSystemComponent(); + /// @brief Pass (smart-pointer) ownership of some object to the client /// context. template void *acquireObject(T obj) { @@ -153,6 +156,10 @@ struct OSVR_ClientContextObject : boost::noncopyable { virtual void m_setRoomToWorldTransform(osvr::common::Transform const &xform) = 0; + /// @brief Implementation of accessor for the system component + OSVR_COMMON_EXPORT virtual osvr::common::SystemComponent * + m_getSystemComponent() = 0; + std::string const m_appId; InterfaceList m_interfaces; osvr::common::ClientInterfaceFactory m_clientInterfaceFactory; diff --git a/inc/osvr/Common/GestureComponent.h b/inc/osvr/Common/GestureComponent.h new file mode 100644 index 000000000..b5a04a4b5 --- /dev/null +++ b/inc/osvr/Common/GestureComponent.h @@ -0,0 +1,121 @@ +/** @file + @brief Header + + @date 2015 + + @author + Sensics, Inc. + + +*/ + +// Copyright 2015 Sensics, Inc. +// +// 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 INCLUDED_GestureComponent_h_GUID_F9F3BC3E_1DB2_498F_FD8E_273A269220DA +#define INCLUDED_GestureComponent_h_GUID_F9F3BC3E_1DB2_498F_FD8E_273A269220DA + +// Internal Includes +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Library/third-party includes +#include + +// Standard includes +// - none + +namespace osvr { +namespace common { + + struct GestureData { + OSVR_ChannelCount sensor; + OSVR_GestureState gestureState; + util::StringID gestureID; + }; + + namespace messages { + class GestureRecord : public MessageRegistration { + public: + class MessageSerialization; + + static const char *identifier(); + }; + + } // namespace messages + + /// @brief BaseDevice component + class GestureComponent : public DeviceComponent { + public: + /// @brief Factory method + /// + /// Required to ensure that allocation and deallocation stay on the same + /// side of a DLL line. + static OSVR_COMMON_EXPORT shared_ptr + create(common::SystemComponent &systemComponent); + + /// @brief Message from server to client, containing Gesture data. + messages::GestureRecord gestureRecord; + + OSVR_COMMON_EXPORT void + sendGestureData(OSVR_GestureState gestureState, + OSVR_GestureID gestureID, OSVR_ChannelCount sensor, + OSVR_TimeValue const ×tamp); + + void m_sendGestureMap(OSVR_TimeValue const ×tamp); + + typedef std::function GestureHandler; + + OSVR_COMMON_EXPORT void registerGestureHandler(GestureHandler cb); + + OSVR_COMMON_EXPORT OSVR_GestureID getGestureID(const char *gestureName); + + private: + + explicit GestureComponent(common::SystemComponent &systemComponent); + // @brief Disable assignment operator, since we have reference + /// members + GestureComponent &operator=(const GestureComponent &) = delete; + + void m_parentSet() override; + + util::StringID m_getStringID(std::string const& str); + + static int VRPN_CALLBACK m_handleGestureRecord(void *userdata, + vrpn_HANDLERPARAM p); + + void m_checkFirst(OSVR_GestureState const &gesture); + + OSVR_ChannelCount m_numSensor; + std::vector m_cb; + + /// @brief System device component + common::SystemComponent &m_systemComponent; + + /// name to ID map used by the server + GestureDataPtr m_gestureNameMap; + }; + +} // namespace common +} // namespace osvr + +#endif // INCLUDED_GestureComponent_h_GUID_F9F3BC3E_1DB2_498F_FD8E_273A269220DA diff --git a/inc/osvr/Common/ReportState.h b/inc/osvr/Common/ReportState.h index 10be61702..0205f43d1 100644 --- a/inc/osvr/Common/ReportState.h +++ b/inc/osvr/Common/ReportState.h @@ -107,6 +107,15 @@ namespace common { } }; + // Template specialization to handle OSVR_GestureReport + template <> struct ReportStateGetter { + static OSVR_GestureState const &apply(OSVR_GestureReport const &r) { + return r.state; + } + static OSVR_GestureState apply(OSVR_GestureReport &r) { + return r.state; + } + }; } // namespace traits /// @brief Generic const accessor for the "state" member of a report. diff --git a/inc/osvr/Common/ReportTypes.h b/inc/osvr/Common/ReportTypes.h index 353e5ead4..d73331067 100644 --- a/inc/osvr/Common/ReportTypes.h +++ b/inc/osvr/Common/ReportTypes.h @@ -50,7 +50,7 @@ namespace common { OSVR_ImagingReport, OSVR_Location2DReport, OSVR_DirectionReport, OSVR_EyeTracker2DReport, OSVR_EyeTracker3DReport, OSVR_EyeTrackerBlinkReport, OSVR_NaviVelocityReport, - OSVR_NaviPositionReport>; + OSVR_NaviPositionReport, OSVR_GestureReport>; } // namespace traits } // namespace common diff --git a/inc/osvr/Common/SerializationTraits.h b/inc/osvr/Common/SerializationTraits.h index 943783611..1eca182cb 100644 --- a/inc/osvr/Common/SerializationTraits.h +++ b/inc/osvr/Common/SerializationTraits.h @@ -105,7 +105,7 @@ namespace common { /// @brief Serialize a value to a buffer, with optional tag to specify /// non-default traits. template > + typename Tag = DefaultSerializationTag> inline void serializeRaw(BufferType &buf, T const &v, Tag const &tag = Tag()) { SerializationTraits::serialize(buf, v, tag); @@ -114,7 +114,7 @@ namespace common { /// @brief Deserialize a value from a buffer, with optional tag to /// specify non-default traits. template > + typename Tag = DefaultSerializationTag> inline void deserializeRaw(BufferReaderType &reader, T &v, Tag const &tag = Tag()) { SerializationTraits::deserialize(reader, v, tag); @@ -122,7 +122,7 @@ namespace common { /// @brief Get the size a value from a buffer, with optional tag to /// specify non-default traits. - template > + template > inline size_t getBufferSpaceRequiredRaw(size_t existingBufferSize, T const &v, Tag const &tag = Tag()) { diff --git a/inc/osvr/Common/SystemComponent.h b/inc/osvr/Common/SystemComponent.h index 9c9bc8a5f..14a724614 100644 --- a/inc/osvr/Common/SystemComponent.h +++ b/inc/osvr/Common/SystemComponent.h @@ -31,15 +31,21 @@ #include #include #include +#include +#include +#include // Library/third-party includes #include +#include // Standard includes -// - none +#include +#include namespace osvr { namespace common { + namespace messages { class RoutesFromServer : public MessageRegistration { public: @@ -69,8 +75,27 @@ namespace common { class MessageSerialization; static const char *identifier(); }; + + /// message to send serialized name to ID map + class RegisteredStringMapRecord + : public MessageRegistration { + public: + class MessageSerialization; + + static const char *identifier(); + }; + } // namespace messages + struct RegStringMapData { + RegisteredStringMap map; + CorrelatedStringMap corrMap; + }; + + using GestureMapData = std::vector; + using GestureDataPtr = shared_ptr; + typedef shared_ptr SystemComponentPtr; + /// @brief BaseDevice component, to be used only with the "OSVR" special /// device. class SystemComponent : public DeviceComponent { @@ -108,17 +133,40 @@ namespace common { typedef std::function JsonHandler; - OSVR_COMMON_EXPORT void registerReplaceTreeHandler(JsonHandler cb); + OSVR_COMMON_EXPORT void + registerReplaceTreeHandler(JsonHandler const &cb); OSVR_COMMON_EXPORT void sendReplacementTree(PathTree &tree); + /// @brief Get shared ownership of the gesture map data structures + OSVR_COMMON_EXPORT GestureDataPtr getGestureMap(); + + /// @brief Message from server to client, containing registeredStringMap + messages::RegisteredStringMapRecord gestureStringMap; + + OSVR_COMMON_EXPORT void sendGestureMap(); + + typedef std::function GestureMapHandler; + OSVR_COMMON_EXPORT void + registerGestureMapHandler(GestureMapHandler const &cb); + private: SystemComponent(); virtual void m_parentSet(); - static int VRPN_CALLBACK - m_handleReplaceTree(void *userdata, vrpn_HANDLERPARAM p); + static int VRPN_CALLBACK m_handleReplaceTree(void *userdata, + vrpn_HANDLERPARAM p); std::vector m_replaceTreeHandlers; + + static int VRPN_CALLBACK m_handleRegStringMap(void *userdata, + vrpn_HANDLERPARAM p); + std::vector m_cb_map; + + // name to ID map used by the gesture interface class + GestureDataPtr m_nameToIDMap; + + /// @brief Common component for system device + common::CommonComponent *m_commonComponent; }; } // namespace common } // namespace osvr diff --git a/inc/osvr/Connection/DeviceInitObject.h b/inc/osvr/Connection/DeviceInitObject.h index 311aefab4..b7d277c5d 100644 --- a/inc/osvr/Connection/DeviceInitObject.h +++ b/inc/osvr/Connection/DeviceInitObject.h @@ -37,6 +37,7 @@ #include #include #include +#include // Library/third-party includes #include @@ -156,6 +157,9 @@ struct OSVR_DeviceInitObject : boost::noncopyable { return m_components; } + OSVR_CONNECTION_EXPORT osvr::pluginhost::RegistrationContext * + getParentContext(); + private: osvr::pluginhost::PluginSpecificRegistrationContext *m_context; osvr::connection::ConnectionPtr m_conn; diff --git a/inc/osvr/PluginHost/RegistrationContext.h b/inc/osvr/PluginHost/RegistrationContext.h index 51996dd1b..5c51158a3 100644 --- a/inc/osvr/PluginHost/RegistrationContext.h +++ b/inc/osvr/PluginHost/RegistrationContext.h @@ -33,6 +33,7 @@ #include #include #include +#include // Library/third-party includes #include @@ -90,8 +91,15 @@ namespace pluginhost { /// @brief Const access the data storage map. OSVR_PLUGINHOST_EXPORT util::AnyMap const &data() const; - /// @} + /// @brief Store a copy of system component + OSVR_PLUGINHOST_EXPORT void + setSystemComponent(common::SystemComponent *systemComponent); + + /// @brief Return a copy of system component + OSVR_PLUGINHOST_EXPORT common::SystemComponent *getSystemComponent(); + + /// @} private: /// @brief Map of plugin names to owning pointers for plugin /// registration. @@ -102,6 +110,8 @@ namespace pluginhost { struct Impl; /// Private impl. unique_ptr m_impl; + + common::SystemComponent *m_systemComponent; }; } // namespace pluginhost } // namespace osvr diff --git a/inc/osvr/PluginKit/GestureInterfaceC.h b/inc/osvr/PluginKit/GestureInterfaceC.h new file mode 100644 index 000000000..234508f29 --- /dev/null +++ b/inc/osvr/PluginKit/GestureInterfaceC.h @@ -0,0 +1,88 @@ +/** @file + @brief Header + + @date 2015 + + @author + Sensics, Inc. + + +*/ + +// Copyright 2015 Sensics, Inc. +// +// 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 INCLUDED_GestureInterfaceC_h_GUID_1BED0900_2C34_47B4_1B62_169A0E3E80D4 +#define INCLUDED_GestureInterfaceC_h_GUID_1BED0900_2C34_47B4_1B62_169A0E3E80D4 + +// Internal Includes +#include +#include +#include + +// Library/third-party includes +// - none + +// Standard includes + +OSVR_EXTERN_C_BEGIN + +/** @brief Opaque type used in conjunction with a device token to send data on + * Gesture Interface +*/ +typedef struct OSVR_GestureDeviceInterfaceObject *OSVR_GestureDeviceInterface; + +/** @brief Specify that your device will implement the Gesture interface. + + @param opts The device init options object. + @param [out] iface An interface object you should retain with the same + lifetime as the device token in order to send messages conforming to an + Gesture interface. +*/ +OSVR_PLUGINKIT_EXPORT +OSVR_ReturnCode +osvrDeviceGestureConfigure(OSVR_INOUT_PTR OSVR_DeviceInitOptions opts, + OSVR_OUT_PTR OSVR_GestureDeviceInterface *iface) + OSVR_FUNC_NONNULL((1, 2)); + +/** @brief Obtain an ID for a given gesture name + @param iface GestureInterface + @param gestureName String name of gesture + @param gestureID pointer to an id variable +*/ +OSVR_PLUGINKIT_EXPORT +void osvrDeviceGestureGetID(OSVR_IN_PTR OSVR_GestureDeviceInterface iface, + OSVR_IN_STRZ const char *gestureName, + OSVR_OUT_PTR OSVR_GestureID *gestureID) + OSVR_FUNC_NONNULL((1, 2)); + +/** @brief Report data for a specific sensor. + @param iface Gesture interface + @param gestureID Pre-fetched ID of the gesture + @param gestureState Current state of gesture (In process vs Completed) + @param sensor Sensor number + @param timestamp Timestamp correlating to Gesture data. +*/ +OSVR_PLUGINKIT_EXPORT +OSVR_ReturnCode osvrDeviceGestureReportData( + OSVR_IN_PTR OSVR_GestureDeviceInterface iface, + OSVR_IN OSVR_GestureID gestureID, OSVR_IN OSVR_GestureState gestureState, + OSVR_IN OSVR_ChannelCount sensor, + OSVR_IN_PTR OSVR_TimeValue const *timestamp) OSVR_FUNC_NONNULL((1, 5)); + +/** @} */ /* end of group */ + +OSVR_EXTERN_C_END + +#endif // INCLUDED_GestureInterfaceC_h_GUID_1BED0900_2C34_47B4_1B62_169A0E3E80D4 diff --git a/inc/osvr/Util/ClientReportTypesC.h b/inc/osvr/Util/ClientReportTypesC.h index 85fa5a5a1..65289833a 100644 --- a/inc/osvr/Util/ClientReportTypesC.h +++ b/inc/osvr/Util/ClientReportTypesC.h @@ -38,6 +38,7 @@ #include #include #include +#include /* Library/third-party includes */ /* none */ @@ -340,6 +341,31 @@ typedef struct OSVR_NaviPositionReport { OSVR_NaviPositionState state; } OSVR_NaviPositionReport; +/** @brief Type of int to identify gestures */ +typedef uint32_t OSVR_GestureID; + +/** @brief Type of string to identify gesture name */ +typedef char const *OSVR_GestureName; + +/** @brief Type of Gesture state */ +typedef uint8_t OSVR_GestureState; + +/** @brief OSVR_GestureState value indicating "gesture started/currently in + progress (occurring)" + (should be used for continuous gestures, since discrete start and complete + at the same time) */ +#define OSVR_GESTURE_IN_PROGRESS (1) + +/** @brief OSVR_GestureState value indicating "gesture is finished" */ +#define OSVR_GESTURE_COMPLETE (0) + +/** @brief Report type for a gesture event */ +typedef struct OSVR_GestureReport { + OSVR_GestureID gestureID; + OSVR_GestureState state; + OSVR_ChannelCount sensor; +} OSVR_GestureReport; + /** @} */ /** @} */ diff --git a/src/osvr/Client/AnalysisClientContext.cpp b/src/osvr/Client/AnalysisClientContext.cpp index d4c77bedc..4d979506d 100644 --- a/src/osvr/Client/AnalysisClientContext.cpp +++ b/src/osvr/Client/AnalysisClientContext.cpp @@ -115,5 +115,9 @@ namespace client { common::PathTree const &AnalysisClientContext::m_getPathTree() const { return m_pathTreeOwner.get(); } + + common::SystemComponent *AnalysisClientContext::m_getSystemComponent() { + return m_systemComponent; + } } // namespace client } // namespace osvr diff --git a/src/osvr/Client/AnalysisClientContext.h b/src/osvr/Client/AnalysisClientContext.h index b6b8706b5..aa55bc8db 100644 --- a/src/osvr/Client/AnalysisClientContext.h +++ b/src/osvr/Client/AnalysisClientContext.h @@ -80,6 +80,8 @@ namespace client { } bool m_getStatus() const override; + common::SystemComponent *m_getSystemComponent() override; + /// @brief the vrpn_Connection corresponding to m_host vrpn_ConnectionPtr m_mainConn; diff --git a/src/osvr/Client/CMakeLists.txt b/src/osvr/Client/CMakeLists.txt index c0ee766d0..56426a08a 100644 --- a/src/osvr/Client/CMakeLists.txt +++ b/src/osvr/Client/CMakeLists.txt @@ -33,6 +33,8 @@ set(SOURCE DisplayInput.cpp EyeTrackerRemoteFactory.cpp EyeTrackerRemoteFactory.h + GestureRemoteFactory.cpp + GestureRemoteFactory.h ImagingRemoteFactory.cpp ImagingRemoteFactory.h InterfaceTree.cpp diff --git a/src/osvr/Client/GestureRemoteFactory.cpp b/src/osvr/Client/GestureRemoteFactory.cpp new file mode 100644 index 000000000..b10ce6f70 --- /dev/null +++ b/src/osvr/Client/GestureRemoteFactory.cpp @@ -0,0 +1,144 @@ +/** @file + @brief Implementation + + @date 2015 + + @author + Sensics, Inc. + + +*/ + +// Copyright 2015 Sensics, Inc. +// +// 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. + +// Internal Includes +#include "GestureRemoteFactory.h" +#include "RemoteHandlerInternals.h" +#include "VRPNConnectionCollection.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Library/third-party includes +// - none + +// Standard includes +// - none + +namespace osvr { +namespace client { + + class GestureRemoteHandler : public RemoteHandler { + public: + GestureRemoteHandler(vrpn_ConnectionPtr const &conn, + std::string const &deviceName, + boost::optional sensor, + common::InterfaceList &ifaces, + common::ClientContext *ctx) + : m_dev(common::createClientDevice(deviceName, conn)), + m_internals(ifaces), m_all(!sensor.is_initialized()), + m_sensor(sensor), m_ctx(ctx) { + + m_sysComponent = m_ctx->getSystemComponent(); + m_gestureNameMap = m_sysComponent->getGestureMap(); + auto gesture = common::GestureComponent::create(*m_sysComponent); + m_dev->addComponent(gesture); + + gesture->registerGestureHandler( + [&](common::GestureData const &data, + util::time::TimeValue const ×tamp) { + m_handleGesture(data, timestamp); + }); + + /**/ + OSVR_DEV_VERBOSE("Constructed an Gesture Handler for " + << deviceName); + } + + /// @brief Deleted assignment operator. + GestureRemoteHandler &operator=(GestureRemoteHandler const &) = delete; + + virtual ~GestureRemoteHandler() { + /// @todo do we need to unregister? + } + + virtual void update() { m_dev->update(); } + + private: + void m_handleGesture(common::GestureData const &data, + util::time::TimeValue const ×tamp) { + if (!m_all && *m_sensor != data.sensor) { + /// doesn't match our filter. + return; + } + + OSVR_GestureReport report; + util::StringID id = m_gestureNameMap->corrMap.convertPeerToLocalID( + util::PeerStringID(data.gestureID.value())); + if (id.empty()) { + // could not find a peer to local mapping, discarding report + return; + } + + report.sensor = data.sensor; + report.state = data.gestureState; + report.gestureID = id.value(); + m_internals.setStateAndTriggerCallbacks(timestamp, report); + } + + common::BaseDevicePtr m_dev; + RemoteHandlerInternals m_internals; + bool m_all; + boost::optional m_sensor; + // map to keep track of gesture map and server to local ID map + common::GestureDataPtr m_gestureNameMap; + common::ClientContext *m_ctx; + common::SystemComponent *m_sysComponent; + }; + + GestureRemoteFactory::GestureRemoteFactory( + VRPNConnectionCollection const &conns) + : m_conns(conns) {} + + shared_ptr GestureRemoteFactory:: + operator()(common::OriginalSource const &source, + common::InterfaceList &ifaces, common::ClientContext &ctx) { + + shared_ptr ret; + + if (source.hasTransform()) { + OSVR_DEV_VERBOSE( + "Ignoring transform found on route for Gesture data!"); + } + + auto const &devElt = source.getDeviceElement(); + + /// @todo find out why make_shared causes a crash here + ret.reset(new GestureRemoteHandler( + m_conns.getConnection(devElt), devElt.getFullDeviceName(), + source.getSensorNumberAsChannelCount(), ifaces, &ctx)); + return ret; + } + +} // namespace client +} // namespace osvr diff --git a/src/osvr/Client/GestureRemoteFactory.h b/src/osvr/Client/GestureRemoteFactory.h new file mode 100644 index 000000000..a692aff2a --- /dev/null +++ b/src/osvr/Client/GestureRemoteFactory.h @@ -0,0 +1,65 @@ +/** @file + @brief Header + + @date 2015 + + @author + Sensics, Inc. + + +*/ + +// Copyright 2015 Sensics, Inc. +// +// 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 INCLUDED_GestureRemoteFactory_h_GUID_800D466C_603D_46AC_F20C_DAD679778CA6 +#define INCLUDED_GestureRemoteFactory_h_GUID_800D466C_603D_46AC_F20C_DAD679778CA6 + +// Internal Includes +#include "VRPNConnectionCollection.h" +#include +#include +#include +#include +#include + +// Library/third-party includes +// - none + +// Standard includes +// - none + +namespace osvr { +namespace client { + + class GestureRemoteFactory { + public: + GestureRemoteFactory(VRPNConnectionCollection const &conns); + + template void registerWith(T &factory) const { + factory.addFactory("gesture", *this); + } + + shared_ptr + operator()(common::OriginalSource const &source, + common::InterfaceList &ifaces, common::ClientContext &ctx); + + private: + VRPNConnectionCollection m_conns; + }; + +} // namespace client +} // namespace osvr + +#endif // INCLUDED_GestureRemoteFactory_h_GUID_800D466C_603D_46AC_F20C_DAD679778CA6 diff --git a/src/osvr/Client/PureClientContext.cpp b/src/osvr/Client/PureClientContext.cpp index 3a5f7cc70..dafc57d2e 100644 --- a/src/osvr/Client/PureClientContext.cpp +++ b/src/osvr/Client/PureClientContext.cpp @@ -106,6 +106,12 @@ namespace client { m_systemDevice = common::createClientDevice(sysDeviceName, m_mainConn); m_systemComponent = m_systemDevice->addComponent(common::SystemComponent::create()); + + /// Receive string map data whenever it comes + m_systemComponent->registerGestureMapHandler( + [&](common::GestureMapData const &dataMap) { + m_handleRegStringMap(dataMap); + }); using DedupJsonFunction = common::DeduplicatingFunctionWrapper; m_systemComponent->registerReplaceTreeHandler( @@ -205,9 +211,19 @@ namespace client { return m_roomToWorld; } + common::SystemComponent *PureClientContext::m_getSystemComponent() { + return m_systemComponent; + } + void PureClientContext::m_setRoomToWorldTransform( common::Transform const &xform) { m_roomToWorld = xform; } + + void PureClientContext::m_handleRegStringMap( + common::GestureMapData const &data) { + auto map = m_systemComponent->getGestureMap(); + map->corrMap.setupPeerMappings(data); + } } // namespace client } // namespace osvr diff --git a/src/osvr/Client/PureClientContext.h b/src/osvr/Client/PureClientContext.h index 73757aa1f..477a7838d 100644 --- a/src/osvr/Client/PureClientContext.h +++ b/src/osvr/Client/PureClientContext.h @@ -28,7 +28,7 @@ // Internal Includes #include #include -#include +#include #include #include #include @@ -38,6 +38,7 @@ #include #include #include +#include // Library/third-party includes #include @@ -74,6 +75,8 @@ namespace client { common::PathTree const &m_getPathTree() const override; + common::SystemComponent *m_getSystemComponent() override; + common::Transform const &m_getRoomToWorldTransform() const override; void m_setRoomToWorldTransform(common::Transform const &xform) override; @@ -114,6 +117,9 @@ namespace client { /// @brief Manager of client interface objects and their interaction /// with the path tree. ClientInterfaceObjectManager m_ifaceMgr; + + /// @brief Called whenever an updated string to ID map is available + void m_handleRegStringMap(common::GestureMapData const &data); }; } // namespace client } // namespace osvr diff --git a/src/osvr/Client/RemoteHandlerFactory.cpp b/src/osvr/Client/RemoteHandlerFactory.cpp index 806257763..e13d94f51 100644 --- a/src/osvr/Client/RemoteHandlerFactory.cpp +++ b/src/osvr/Client/RemoteHandlerFactory.cpp @@ -28,6 +28,7 @@ #include "ButtonRemoteFactory.h" #include "DirectionRemoteFactory.h" #include "EyeTrackerRemoteFactory.h" +#include "GestureRemoteFactory.h" #include "ImagingRemoteFactory.h" #include "Location2DRemoteFactory.h" #include "LocomotionRemoteFactory.h" @@ -49,6 +50,7 @@ namespace client { ButtonRemoteFactory(conns).registerWith(factory); ImagingRemoteFactory(conns).registerWith(factory); EyeTrackerRemoteFactory(conns).registerWith(factory); + GestureRemoteFactory(conns).registerWith(factory); Location2DRemoteFactory(conns).registerWith(factory); LocomotionRemoteFactory(conns).registerWith(factory); DirectionRemoteFactory(conns).registerWith(factory); diff --git a/src/osvr/ClientKit/CMakeLists.txt b/src/osvr/ClientKit/CMakeLists.txt index 48b0834af..71227e2e4 100644 --- a/src/osvr/ClientKit/CMakeLists.txt +++ b/src/osvr/ClientKit/CMakeLists.txt @@ -55,7 +55,9 @@ target_link_libraries(${LIBNAME_FULL} osvrClient osvrUtilCpp osvrCommon - eigen-headers) + eigen-headers + jsoncpp_lib + vendored-vrpn) ### # C++ (header-only) interface diff --git a/src/osvr/ClientKit/InterfaceC.cpp b/src/osvr/ClientKit/InterfaceC.cpp index 7116a415a..75587caf7 100644 --- a/src/osvr/ClientKit/InterfaceC.cpp +++ b/src/osvr/ClientKit/InterfaceC.cpp @@ -27,6 +27,8 @@ #include #include #include +#include +#include // Library/third-party includes // - none @@ -71,3 +73,49 @@ OSVR_ReturnCode osvrClientFreeInterface(OSVR_ClientContext ctx, } return OSVR_RETURN_SUCCESS; } + +OSVR_CLIENTKIT_EXPORT OSVR_ReturnCode osvrClientGetGestureNameLength( + OSVR_ClientContext ctx, uint32_t id, size_t *len) { + + if (ctx == nullptr) { + return OSVR_RETURN_FAILURE; + } + if (len == nullptr) { + return OSVR_RETURN_FAILURE; + } + /// @todo Shouldn't be hardcoded as the only registered string map - this + /// should be accessible as "interface-class-level" data for Gesture. + std::string entryName = + ctx->getSystemComponent()->getGestureMap()->corrMap.getStringFromId( + osvr::util::StringID(id)); + *len = entryName.empty() ? 0 : (entryName.size() + 1); + return OSVR_RETURN_SUCCESS; +} + +OSVR_ReturnCode osvrClientGetGestureNameFromID(OSVR_ClientContext ctx, + uint32_t id, char *buf, + size_t len) { + + if (ctx == nullptr) { + return OSVR_RETURN_FAILURE; + } + if (buf == nullptr) { + return OSVR_RETURN_FAILURE; + } + + /// @todo Shouldn't be hardcoded as the only registered string map - this + /// should be accessible as "interface-class-level" data for Gesture. + std::string entryName = + ctx->getSystemComponent()->getGestureMap()->corrMap.getStringFromId( + osvr::util::StringID(id)); + + if (entryName.size() + 1 > len) { + /// buffer too small. + return OSVR_RETURN_FAILURE; + } + + /// @todo refactor to eliminate duplication + entryName.copy(buf, entryName.size()); + buf[entryName.size()] = '\0'; + return OSVR_RETURN_SUCCESS; +} diff --git a/src/osvr/ClientKit/InterfaceCallbackC.cpp b/src/osvr/ClientKit/InterfaceCallbackC.cpp index 4dc266c64..795c3603b 100644 --- a/src/osvr/ClientKit/InterfaceCallbackC.cpp +++ b/src/osvr/ClientKit/InterfaceCallbackC.cpp @@ -59,5 +59,6 @@ OSVR_CALLBACK_METHODS(EyeTracker3D) OSVR_CALLBACK_METHODS(EyeTrackerBlink) OSVR_CALLBACK_METHODS(NaviVelocity) OSVR_CALLBACK_METHODS(NaviPosition) +OSVR_CALLBACK_METHODS(Gesture) #undef OSVR_CALLBACK_METHODS diff --git a/src/osvr/ClientKit/InterfaceStateC.cpp b/src/osvr/ClientKit/InterfaceStateC.cpp index 7ac485ae6..c64ebbf99 100644 --- a/src/osvr/ClientKit/InterfaceStateC.cpp +++ b/src/osvr/ClientKit/InterfaceStateC.cpp @@ -59,5 +59,6 @@ OSVR_CALLBACK_METHODS(EyeTracker3D) OSVR_CALLBACK_METHODS(EyeTrackerBlink) OSVR_CALLBACK_METHODS(NaviVelocity) OSVR_CALLBACK_METHODS(NaviPosition) +OSVR_CALLBACK_METHODS(Gesture) #undef OSVR_CALLBACK_METHODS diff --git a/src/osvr/Common/CMakeLists.txt b/src/osvr/Common/CMakeLists.txt index 4dd0aace9..48dece48e 100644 --- a/src/osvr/Common/CMakeLists.txt +++ b/src/osvr/Common/CMakeLists.txt @@ -62,6 +62,7 @@ set(API "${HEADER_LOCATION}/Endianness.h" "${HEADER_LOCATION}/EyeTrackerComponent.h" "${HEADER_LOCATION}/GeneralizedTransform.h" + "${HEADER_LOCATION}/GestureComponent.h" "${HEADER_LOCATION}/GetEnvironmentVariable.h" "${HEADER_LOCATION}/ImagingComponent.h" "${CMAKE_CURRENT_BINARY_DIR}/ImagingComponentConfig.h" @@ -105,6 +106,7 @@ set(API "${HEADER_LOCATION}/ReportStateTraits.h" "${HEADER_LOCATION}/ReportTraits.h" "${HEADER_LOCATION}/ReportTypes.h" + "${HEADER_LOCATION}/RegisteredStringMap.h" "${HEADER_LOCATION}/ResolveFullTree.h" "${HEADER_LOCATION}/ResolveTreeNode.h" "${HEADER_LOCATION}/RouteContainer.h" @@ -141,6 +143,7 @@ set(SOURCE DirectionComponent.cpp EyeTrackerComponent.cpp GeneralizedTransform.cpp + GestureComponent.cpp GetEnvironmentVariable.cpp GetJSONStringFromTree.h ImagingComponent.cpp diff --git a/src/osvr/Common/ClientContext.cpp b/src/osvr/Common/ClientContext.cpp index 64b1941a5..a9ed99e1d 100644 --- a/src/osvr/Common/ClientContext.cpp +++ b/src/osvr/Common/ClientContext.cpp @@ -122,6 +122,10 @@ osvr::common::PathTree const &OSVR_ClientContextObject::getPathTree() const { return m_getPathTree(); } +osvr::common::SystemComponent *OSVR_ClientContextObject::getSystemComponent() { + return m_getSystemComponent(); +} + void OSVR_ClientContextObject::sendRoute(std::string const &route) { m_sendRoute(route); } diff --git a/src/osvr/Common/DirectionComponent.cpp b/src/osvr/Common/DirectionComponent.cpp index a11f8d467..61b0d1f19 100644 --- a/src/osvr/Common/DirectionComponent.cpp +++ b/src/osvr/Common/DirectionComponent.cpp @@ -86,9 +86,8 @@ namespace common { timestamp); } - int VRPN_CALLBACK - DirectionComponent::m_handleDirectionRecord(void *userdata, - vrpn_HANDLERPARAM p) { + int VRPN_CALLBACK DirectionComponent::m_handleDirectionRecord( + void *userdata, vrpn_HANDLERPARAM p) { auto self = static_cast(userdata); auto bufReader = readExternalBuffer(p.buffer, p.payload_len); diff --git a/src/osvr/Common/GestureComponent.cpp b/src/osvr/Common/GestureComponent.cpp new file mode 100644 index 000000000..9b76bf204 --- /dev/null +++ b/src/osvr/Common/GestureComponent.cpp @@ -0,0 +1,171 @@ +/** @file + @brief Implementation + + @date 2015 + + @author + Sensics, Inc. + + +*/ + +// Copyright 2015 Sensics, Inc. +// +// 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. + +// Internal Includes +#include +#include +#include +#include +#include +#include + +// Library/third-party includes +// - none + +// Standard includes +// - none + +namespace osvr { +namespace common { + + namespace messages { + class GestureRecord::MessageSerialization { + public: + MessageSerialization(OSVR_GestureState const &state, + OSVR_GestureID gestureID, + OSVR_ChannelCount sensor) + : m_gestureState(state), m_gestureID(gestureID), + m_sensor(sensor) {} + + MessageSerialization() {} + + template void processMessage(T &p) { + p(m_gestureState); + p(m_gestureID); + p(m_sensor); + } + GestureData getData() const { + GestureData ret; + ret.sensor = m_sensor; + ret.gestureID = m_gestureID; + ret.gestureState = m_gestureState; + return ret; + } + + private: + OSVR_GestureState m_gestureState; + util::StringID m_gestureID; + OSVR_ChannelCount m_sensor; + }; + const char *GestureRecord::identifier() { + return "org.osvr.gesture.gesturerecord"; + } + + } // namespace messages + + shared_ptr + GestureComponent::create(common::SystemComponent &systemComponent) { + shared_ptr ret(new GestureComponent(systemComponent)); + return ret; + } + + GestureComponent::GestureComponent(common::SystemComponent &systemComponent) + : m_systemComponent(systemComponent), + m_gestureNameMap(m_systemComponent.getGestureMap()) { + + // populate the gesture map +#define OSVR_X(GESTURE) m_getStringID(GESTURE); + OSVR_GESTURE_X() +#undef OSVR_X + +#if 0 + m_getStringID(OSVR_GESTURE_SWIPE_LEFT); + m_getStringID(OSVR_GESTURE_SWIPE_RIGHT); + m_getStringID(OSVR_GESTURE_SCROLL_UP); + m_getStringID(OSVR_GESTURE_SCROLL_DOWN); + m_getStringID(OSVR_GESTURE_SINGLE_TAP); + m_getStringID(OSVR_GESTURE_DOUBLE_TAP); + m_getStringID(OSVR_GESTURE_PINCH); + m_getStringID(OSVR_GESTURE_FINGER_SPREAD); + m_getStringID(OSVR_GESTURE_CIRCLE); + m_getStringID(OSVR_GESTURE_LONG_PRESS); + m_getStringID(OSVR_GESTURE_OPEN_HAND); + m_getStringID(OSVR_GESTURE_CLOSED_HAND); +#endif + + // send out an updated map + m_systemComponent.sendGestureMap(); + } + + void GestureComponent::sendGestureData(OSVR_GestureState gestureState, + OSVR_GestureID gestureID, + OSVR_ChannelCount sensor, + OSVR_TimeValue const ×tamp) { + + Buffer<> buf; + + messages::GestureRecord::MessageSerialization msg(gestureState, + gestureID, sensor); + serialize(buf, msg); + + m_getParent().packMessage(buf, gestureRecord.getMessageType(), + timestamp); + } + + int VRPN_CALLBACK GestureComponent::m_handleGestureRecord( + void *userdata, vrpn_HANDLERPARAM p) { + auto self = static_cast(userdata); + auto bufReader = readExternalBuffer(p.buffer, p.payload_len); + + messages::GestureRecord::MessageSerialization msg; + deserialize(bufReader, msg); + auto data = msg.getData(); + auto timestamp = util::time::fromStructTimeval(p.msg_time); + + for (auto const &cb : self->m_cb) { + cb(data, timestamp); + } + return 0; + } + + util::StringID GestureComponent::m_getStringID(std::string const &str) { + return m_gestureNameMap->map.getStringID(str); + } + + void GestureComponent::registerGestureHandler(GestureHandler handler) { + if (m_cb.empty()) { + m_registerHandler(&GestureComponent::m_handleGestureRecord, this, + gestureRecord.getMessageType()); + } + m_cb.push_back(handler); + } + + void GestureComponent::m_parentSet() { + m_getParent().registerMessageType(gestureRecord); + } + + OSVR_GestureID GestureComponent::getGestureID(const char *gestureName) { + + OSVR_GestureID gestureID = m_getStringID(gestureName).value(); + + // if we just inserted new gesture ID then send gesture map + if (m_gestureNameMap->map.isModified()) { + m_systemComponent.sendGestureMap(); + } + return gestureID; + } + +} // namespace common +} // namespace osvr diff --git a/src/osvr/Common/SystemComponent.cpp b/src/osvr/Common/SystemComponent.cpp index a1c881091..86fbd9478 100644 --- a/src/osvr/Common/SystemComponent.cpp +++ b/src/osvr/Common/SystemComponent.cpp @@ -30,6 +30,7 @@ #include #include #include +#include // Library/third-party includes #include @@ -93,6 +94,23 @@ namespace common { const char *ReplacementTreeFromServer::identifier() { return "com.osvr.system.ReplacementTreeFromServer"; } + + class RegisteredStringMapRecord::MessageSerialization { + public: + explicit MessageSerialization(RegisteredStringMap const &myMap) + : m_data(myMap.getEntries()) {} + + MessageSerialization() = default; + + template void processMessage(T &p) { p(m_data); } + GestureMapData const &getData() const { return m_data; } + + private: + GestureMapData m_data; + }; + const char *RegisteredStringMapRecord::identifier() { + return "com.osvr.system.regstringmaprecord"; + } } // namespace messages const char *SystemComponent::deviceName() { @@ -104,7 +122,8 @@ namespace common { return ret; } - SystemComponent::SystemComponent() {} + SystemComponent::SystemComponent() + : m_nameToIDMap(make_shared()) {} void SystemComponent::sendRoutes(std::string const &routes) { Buffer<> buf; @@ -140,7 +159,7 @@ namespace common { m_getParent().sendPending(); // forcing this since it will cause // shuffling of remotes on the client. } - void SystemComponent::registerReplaceTreeHandler(JsonHandler cb) { + void SystemComponent::registerReplaceTreeHandler(JsonHandler const &cb) { if (m_replaceTreeHandlers.empty()) { m_registerHandler(&SystemComponent::m_handleReplaceTree, this, treeOut.getMessageType()); @@ -148,11 +167,39 @@ namespace common { m_replaceTreeHandlers.push_back(cb); } + GestureDataPtr SystemComponent::getGestureMap() { return m_nameToIDMap; } + + void SystemComponent::sendGestureMap() { + m_nameToIDMap->map.clearModifiedFlag(); + Buffer<> buf; + auto msg = messages::RegisteredStringMapRecord::MessageSerialization{ + m_nameToIDMap->map}; + serialize(buf, msg); + m_getParent().packMessage(buf, gestureStringMap.getMessageType()); + } + + void + SystemComponent::registerGestureMapHandler(GestureMapHandler const &cb) { + if (m_cb_map.empty()) { + m_registerHandler(&SystemComponent::m_handleRegStringMap, this, + gestureStringMap.getMessageType()); + } + m_cb_map.push_back(cb); + } + void SystemComponent::m_parentSet() { + + // add a ping handler to re-send string to ID map everytime the new + // connection(ping) occurs + m_commonComponent = + m_getParent().addComponent(osvr::common::CommonComponent::create()); + m_getParent().registerMessageType(routesOut); m_getParent().registerMessageType(appStartup); m_getParent().registerMessageType(routeIn); m_getParent().registerMessageType(treeOut); + m_getParent().registerMessageType(gestureStringMap); + m_commonComponent->registerPingHandler([&] { sendGestureMap(); }); } int SystemComponent::m_handleReplaceTree(void *userdata, @@ -169,5 +216,20 @@ namespace common { } return 0; } + + int VRPN_CALLBACK + SystemComponent::m_handleRegStringMap(void *userdata, vrpn_HANDLERPARAM p) { + auto self = static_cast(userdata); + auto bufReader = readExternalBuffer(p.buffer, p.payload_len); + messages::RegisteredStringMapRecord::MessageSerialization msg; + deserialize(bufReader, msg); + auto data = msg.getData(); + + for (auto const &cb : self->m_cb_map) { + cb(data); + } + return 0; + } + } // namespace common } // namespace osvr diff --git a/src/osvr/Connection/DeviceInitObject.cpp b/src/osvr/Connection/DeviceInitObject.cpp index 7950c160b..17adfa46d 100644 --- a/src/osvr/Connection/DeviceInitObject.cpp +++ b/src/osvr/Connection/DeviceInitObject.cpp @@ -127,3 +127,9 @@ osvr::pluginhost::PluginSpecificRegistrationContext * OSVR_DeviceInitObject::getContext() { return m_context; } + +osvr::pluginhost::RegistrationContext * +OSVR_DeviceInitObject::getParentContext() { + + return &m_context->getParent(); +} \ No newline at end of file diff --git a/src/osvr/GenerateForReportTypes.cmake b/src/osvr/GenerateForReportTypes.cmake index 50d4da6ea..1f36eab74 100644 --- a/src/osvr/GenerateForReportTypes.cmake +++ b/src/osvr/GenerateForReportTypes.cmake @@ -18,6 +18,7 @@ set(OSVR_REPORT_TYPES EyeTrackerBlink NaviVelocity NaviPosition + Gesture CACHE INTERNAL "" FORCE) # Generate a file using a template with the placeholder @BODY@, as well as a diff --git a/src/osvr/JointClientKit/JointClientContext.cpp b/src/osvr/JointClientKit/JointClientContext.cpp index e8c55f132..8690318a3 100644 --- a/src/osvr/JointClientKit/JointClientContext.cpp +++ b/src/osvr/JointClientKit/JointClientContext.cpp @@ -122,6 +122,10 @@ namespace client { common::ClientInterfacePtr const &iface) { m_ifaceMgr.releaseInterface(iface); } + + common::SystemComponent *JointClientContext::m_getSystemComponent() { + return m_systemComponent; + } bool JointClientContext::m_getStatus() const { /// Always connected, but don't always have a path tree. diff --git a/src/osvr/JointClientKit/JointClientContext.h b/src/osvr/JointClientKit/JointClientContext.h index dcc10f16e..2a100d0c7 100644 --- a/src/osvr/JointClientKit/JointClientContext.h +++ b/src/osvr/JointClientKit/JointClientContext.h @@ -78,6 +78,8 @@ namespace client { common::PathTree const &m_getPathTree() const override; + virtual osvr::common::SystemComponent *m_getSystemComponent(); + common::Transform const &m_getRoomToWorldTransform() const override { return m_roomToWorld; } diff --git a/src/osvr/PluginHost/RegistrationContext.cpp b/src/osvr/PluginHost/RegistrationContext.cpp index 8ea44c6c7..f81016de7 100644 --- a/src/osvr/PluginHost/RegistrationContext.cpp +++ b/src/osvr/PluginHost/RegistrationContext.cpp @@ -187,5 +187,15 @@ namespace pluginhost { util::AnyMap &RegistrationContext::data() { return m_data; } util::AnyMap const &RegistrationContext::data() const { return m_data; } + + void RegistrationContext::setSystemComponent( + common::SystemComponent *systemComponent) { + m_systemComponent = systemComponent; + } + + common::SystemComponent *RegistrationContext::getSystemComponent() { + return m_systemComponent; + } + } // namespace pluginhost } // namespace osvr diff --git a/src/osvr/PluginKit/CMakeLists.txt b/src/osvr/PluginKit/CMakeLists.txt index cdc28be5b..9dbc6cb11 100644 --- a/src/osvr/PluginKit/CMakeLists.txt +++ b/src/osvr/PluginKit/CMakeLists.txt @@ -8,6 +8,7 @@ set(API "${HEADER_LOCATION}/DeviceInterfaceC.h" "${HEADER_LOCATION}/DirectionInterfaceC.h" "${HEADER_LOCATION}/EyeTrackerInterfaceC.h" + "${HEADER_LOCATION}/GestureInterfaceC.h" "${HEADER_LOCATION}/ImagingInterface.h" "${HEADER_LOCATION}/ImagingInterfaceC.h" "${HEADER_LOCATION}/Location2DInterfaceC.h" @@ -25,6 +26,7 @@ set(SOURCE DirectionInterfaceC.cpp HandleNullContext.h EyeTrackerInterfaceC.cpp + GestureInterfaceC.cpp ImagingInterfaceC.cpp Location2DInterfaceC.cpp LocomotionInterfaceC.cpp @@ -54,6 +56,7 @@ target_link_libraries(${LIBNAME_FULL} osvrConnection osvrUtilCpp osvrCommon + jsoncpp_lib vendored-vrpn boost_thread) diff --git a/src/osvr/PluginKit/GestureInterfaceC.cpp b/src/osvr/PluginKit/GestureInterfaceC.cpp new file mode 100644 index 000000000..488f919d9 --- /dev/null +++ b/src/osvr/PluginKit/GestureInterfaceC.cpp @@ -0,0 +1,87 @@ +/** @file + @brief Implementation + + @date 2015 + + @author + Sensics, Inc. + + +*/ + +// Copyright 2015 Sensics, Inc. +// +// 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. + +// Internal Includes +#include +#include +#include +#include +#include +#include "HandleNullContext.h" +#include +#include +#include + +// Library/third-party includes +// - none + +// Standard includes +// - none + +struct OSVR_GestureDeviceInterfaceObject + : public osvr::connection::DeviceInterfaceBase { + osvr::common::GestureComponent *gesture; +}; + +OSVR_ReturnCode +osvrDeviceGestureConfigure(OSVR_INOUT_PTR OSVR_DeviceInitOptions opts, + OSVR_OUT_PTR OSVR_GestureDeviceInterface *iface) { + + OSVR_PLUGIN_HANDLE_NULL_CONTEXT("osvrDeviceGestureConfigure", opts); + OSVR_PLUGIN_HANDLE_NULL_CONTEXT("osvrDeviceGestureConfigure", iface); + OSVR_GestureDeviceInterface ifaceObj = + opts->makeInterfaceObject(); + *iface = ifaceObj; + + auto systemComponent = opts->getParentContext()->getSystemComponent(); + auto gesture = osvr::common::GestureComponent::create(*systemComponent); + ifaceObj->gesture = gesture.get(); + opts->addComponent(gesture); + return OSVR_RETURN_SUCCESS; +} + +void osvrDeviceGestureGetID(OSVR_IN_PTR OSVR_GestureDeviceInterface iface, + OSVR_IN_PTR const char *gestureName, + OSVR_IN_PTR OSVR_GestureID *gestureID) { + + *gestureID = iface->gesture->getGestureID(gestureName); +} + +OSVR_ReturnCode +osvrDeviceGestureReportData(OSVR_IN_PTR OSVR_GestureDeviceInterface iface, + OSVR_IN OSVR_GestureID gestureID, + OSVR_IN_PTR OSVR_GestureState gestureState, + OSVR_IN OSVR_ChannelCount sensor, + OSVR_IN_PTR OSVR_TimeValue const *timestamp) { + + auto guard = iface->getSendGuard(); + if (guard->lock()) { + iface->gesture->sendGestureData(gestureState, gestureID, sensor, + *timestamp); + return OSVR_RETURN_SUCCESS; + } + + return OSVR_RETURN_FAILURE; +} \ No newline at end of file diff --git a/src/osvr/Server/ServerImpl.cpp b/src/osvr/Server/ServerImpl.cpp index cfb41193f..05873b179 100644 --- a/src/osvr/Server/ServerImpl.cpp +++ b/src/osvr/Server/ServerImpl.cpp @@ -89,6 +89,8 @@ namespace server { m_systemComponent = m_systemDevice->addComponent(common::SystemComponent::create()); + m_ctx->setSystemComponent(m_systemComponent); + m_systemComponent->registerClientRouteUpdateHandler( &ServerImpl::m_handleUpdatedRoute, this); diff --git a/src/osvr/Util/CMakeLists.txt b/src/osvr/Util/CMakeLists.txt index 72618293c..69a11966f 100644 --- a/src/osvr/Util/CMakeLists.txt +++ b/src/osvr/Util/CMakeLists.txt @@ -24,6 +24,33 @@ include(CheckCXXSymbolExists) check_cxx_symbol_exists("std::align" "memory" OSVR_HAVE_STDALIGN) configure_file(StdAlignWrapper.h.in "${CMAKE_CURRENT_BINARY_DIR}/StdAlignWrapper.h" @ONLY) +### +# Gestures-related generation +### +set(GESTURE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/GesturesData.txt") +# Dummy configure to make us re-cmake +configure_file("${GESTURE_FILE}" "${CMAKE_CURRENT_BINARY_DIR}/GesturesData.dummy") + +# Parse the gesture list +file(STRINGS "${GESTURE_FILE}" _gestures) +set(BODY) +set(GESTURE_NAMES) +foreach(line ${_gestures}) + string(REGEX REPLACE ":.*" "" gesture_name "${line}") + string(REGEX REPLACE "[^:]*:" "" gesture_string "${line}") + set(BODY "${BODY}\n#define ${gesture_name} \"${gesture_string}\"") + list(APPEND GESTURE_NAMES "${gesture_name}") +endforeach() + +# Create the PredefinedGestures header. +configure_file("PredefinedGesturesC.h.in" "${CMAKE_CURRENT_BINARY_DIR}/PredefinedGesturesC.h" @ONLY) + +# Create the X-Macro header for gestures. +osvr_generate_x_macro(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/PredefinedGesturesXMacroC.h" + INVOCATION_NAME OSVR_GESTURE_X + DEPENDS "${GESTURE_FILE}" + ELEMENTS ${GESTURE_NAMES}) + ### # Library build ### @@ -122,7 +149,9 @@ set(API "${HEADER_LOCATION}/Vec2C.h" "${HEADER_LOCATION}/Vec3C.h" "${HEADER_LOCATION}/WindowsVariantC.h" - "${CMAKE_CURRENT_BINARY_DIR}/PlatformConfig.h") + "${CMAKE_CURRENT_BINARY_DIR}/PlatformConfig.h" + "${CMAKE_CURRENT_BINARY_DIR}/PredefinedGesturesC.h" + "${CMAKE_CURRENT_BINARY_DIR}/PredefinedGesturesXMacroC.h") set(SOURCE AlignedMemoryC.cpp diff --git a/src/osvr/Util/GesturesData.txt b/src/osvr/Util/GesturesData.txt new file mode 100644 index 000000000..832ed4196 --- /dev/null +++ b/src/osvr/Util/GesturesData.txt @@ -0,0 +1,12 @@ +OSVR_GESTURE_SWIPE_LEFT:SwipeLeft +OSVR_GESTURE_SWIPE_RIGHT:SwipeRight +OSVR_GESTURE_SCROLL_UP:ScrollUp +OSVR_GESTURE_SCROLL_DOWN:ScrollDown +OSVR_GESTURE_SINGLE_TAP:SingleTap +OSVR_GESTURE_DOUBLE_TAP:DoubleTap +OSVR_GESTURE_PINCH:Pinch +OSVR_GESTURE_FINGER_SPREAD:FingerSpread +OSVR_GESTURE_CIRCLE:Circle +OSVR_GESTURE_LONG_PRESS:LongPress +OSVR_GESTURE_OPEN_HAND:OpenHand +OSVR_GESTURE_CLOSED_HAND:ClosedHand \ No newline at end of file diff --git a/src/osvr/Util/PredefinedGesturesC.h.in b/src/osvr/Util/PredefinedGesturesC.h.in new file mode 100644 index 000000000..82d930501 --- /dev/null +++ b/src/osvr/Util/PredefinedGesturesC.h.in @@ -0,0 +1,58 @@ +/** @file + @brief Header + + Must be c-safe! + + @date 2015 + + @author + Sensics, Inc. + +*/ + +/* +// Copyright 2015 Sensics, Inc. +// +// 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 INCLUDED_PredefinedGesturesC_h_GUID_1B515E73_97E5_44BA_76DE_A51C4B201C62 +#define INCLUDED_PredefinedGesturesC_h_GUID_1B515E73_97E5_44BA_76DE_A51C4B201C62 + +/* Internal Includes */ +#include + +/* Library/third-party includes */ +/* none */ + +/* Standard includes */ +/* none */ + +OSVR_EXTERN_C_BEGIN + +/** @addtogroup ClientKit +@{ +*/ + +/** @name Pre-defined gestures and their strings. +@{ +*/ + +@BODY@ + +/** @} */ + +/** @} */ + +OSVR_EXTERN_C_END +#endif