From 7ffde8abce24eff99cd8d4dc07414cc15898a000 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Wed, 28 Nov 2012 13:28:20 +0000 Subject: [PATCH] From Stephan Huber, New OscSendingDevice and OscReceivingDevice classes --- src/osgPlugins/osc/OscReceivingDevice.cpp | 858 ++++++++++++++++++++++ src/osgPlugins/osc/OscReceivingDevice.hpp | 127 ++++ src/osgPlugins/osc/OscSendingDevice.cpp | 271 +++++++ src/osgPlugins/osc/OscSendingDevice.hpp | 39 + 4 files changed, 1295 insertions(+) create mode 100755 src/osgPlugins/osc/OscReceivingDevice.cpp create mode 100755 src/osgPlugins/osc/OscReceivingDevice.hpp create mode 100755 src/osgPlugins/osc/OscSendingDevice.cpp create mode 100755 src/osgPlugins/osc/OscSendingDevice.hpp diff --git a/src/osgPlugins/osc/OscReceivingDevice.cpp b/src/osgPlugins/osc/OscReceivingDevice.cpp new file mode 100755 index 000000000..efe4aa5a2 --- /dev/null +++ b/src/osgPlugins/osc/OscReceivingDevice.cpp @@ -0,0 +1,858 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +#include "OscReceivingDevice.hpp" +#include +#include +#include +#include +#include +#include +#include "osc/OscPrintReceivedElements.h" +#include "osc/OscHostEndianness.h" + + +template +struct NativeTypeTraits { + typedef T type; + static T create(const std::vector& t) { return type(t); } +}; + +template<> +struct NativeTypeTraits { + typedef osg::Vec2f type; + static type create(const std::vector& t) { return type(t[0], t[1]); } +}; + +template<> +struct NativeTypeTraits { + typedef osg::Vec3f type; + static type create(const std::vector& t) { return type(t[0], t[1], t[2]); } + +}; + +template<> +struct NativeTypeTraits { + typedef osg::Vec4f type; + static type create(const std::vector& t) { return type(t[0], t[1], t[2], t[3]); } + +}; + +template<> +struct NativeTypeTraits { + typedef osg::Matrixf type; + static type create(const std::vector& t) { return type(&t.front()); } + +}; + + +template<> +struct NativeTypeTraits { + typedef osg::Vec2d type; + static type create(const std::vector& t) { return type(t[0], t[1]); } +}; + +template<> +struct NativeTypeTraits { + typedef osg::Vec3d type; + static type create(const std::vector& t) { return type(t[0], t[1], t[2]); } +}; + +template<> +struct NativeTypeTraits { + typedef osg::Vec4d type; + static type create(const std::vector& t) { return type(t[0], t[1], t[2], t[3]); } +}; + +template<> +struct NativeTypeTraits { + typedef osg::Matrixd type; + static type create(const std::vector& t) { return type(&t.front()); } + +}; + + + +class StandardRequestHandler : public OscReceivingDevice::RequestHandler { + +public: + StandardRequestHandler(const std::string& request_handler, bool treat_first_argument_as_value_name) + : OscReceivingDevice::RequestHandler(request_handler) + , _treatFirstArgumentAsValueName(treat_first_argument_as_value_name) + { + } + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m); + + virtual void describeTo(std::ostream& out) const + { + out << getRequestPath() << ": add all transmitted arguments as ValueObjects to an USER-event"; + if (_treatFirstArgumentAsValueName) + out << ", the first argument is used as the name of the value, if it's a string"; + } +private: + void addArgumentToUdc(osg::UserDataContainer* udc, const std::string& key, const osc::ReceivedMessageArgumentIterator& itr); + + template + bool addNativeTypeFromVector(osg::UserDataContainer* udc, const std::string& key, const std::vector& arr) + { + switch (arr.size()) { + case 2: + udc->setUserValue(key, NativeTypeTraits::create(arr)); + return true; + break; + case 3: + udc->setUserValue(key, NativeTypeTraits::create(arr)); + return true; + break; + case 4: + udc->setUserValue(key, NativeTypeTraits::create(arr)); + return true; + break; + case 16: + udc->setUserValue(key, NativeTypeTraits::create(arr)); + return true; + break; + default: + return false; + } + return false; + } + + bool _treatFirstArgumentAsValueName; + +}; + + + +bool StandardRequestHandler::operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m) +{ + try + { + std::string path = osgDB::getFilePath(full_request_path); + std::string last_elem = osgDB::getSimpleFileName(full_request_path); + + osg::ref_ptr ea = getDevice()->getOrCreateUserDataEvent(); + osg::UserDataContainer* udc = ea->getOrCreateUserDataContainer(); + + + ea->setName(_treatFirstArgumentAsValueName ? full_request_path : path); + udc->setName(ea->getName()); + + if (m.ArgumentCount() == 0) { + return true; + } + + // if we have only one argument, get it and save it to the udc + else if (m.ArgumentCount() == 1) + { + addArgumentToUdc(udc, last_elem, m.ArgumentsBegin()); + return true; + } + else + { + unsigned int i(0); + osc::ReceivedMessageArgumentIterator start = m.ArgumentsBegin(); + if ((_treatFirstArgumentAsValueName) && (start->TypeTag() == osc::STRING_TYPE_TAG)) + { + last_elem = start->AsString(); + ++start; + // if we hav only 2 arguments, then save the value and return + if (m.ArgumentCount() == 2) + { + addArgumentToUdc(udc, last_elem, start); + return true; + } + } + std::vector float_vec; + std::vector double_vec; + bool mixed_arguments(false); + for(osc::ReceivedMessageArgumentIterator itr = start; itr != m.ArgumentsEnd(); ++itr, ++i) + { + if(itr->TypeTag() == osc::FLOAT_TYPE_TAG) + { + float_vec.push_back(itr->AsFloat()); + } + else if(itr->TypeTag() == osc::DOUBLE_TYPE_TAG) + { + double_vec.push_back(itr->AsDouble()); + } + else if(itr->TypeTag() == osc::INT32_TYPE_TAG) + { + float_vec.push_back(itr->AsInt32()); + } + else { + mixed_arguments = true; + break; + } + } + if (!mixed_arguments) + { + unsigned int sum = float_vec.size() + double_vec.size(); + if (sum == float_vec.size()) + { + if (addNativeTypeFromVector(udc, last_elem, float_vec)) + return true; + } + else if (sum == double_vec.size()) + { + if (addNativeTypeFromVector(udc, last_elem, double_vec)) + return true; + } + } + + for(osc::ReceivedMessageArgumentIterator itr = start; itr != m.ArgumentsEnd(); ++itr, ++i) + { + std::ostringstream ss; + ss << last_elem << "_" << i; + addArgumentToUdc(udc, ss.str(), itr); + } + } + return true; + + } + catch(osc::Exception& e) + { + handleException(e); + return false; + } + return false; +} + + +void StandardRequestHandler::addArgumentToUdc(osg::UserDataContainer* udc, const std::string& key, const osc::ReceivedMessageArgumentIterator& itr) +{ + switch((*itr).TypeTag()) + { + case osc::TRUE_TYPE_TAG: + udc->setUserValue(key, true); + break; + + case osc::FALSE_TYPE_TAG: + udc->setUserValue(key, false); + break; + + case osc::INT32_TYPE_TAG: + udc->setUserValue(key, (int)((*itr).AsInt32Unchecked())); + break; + + case osc::FLOAT_TYPE_TAG: + udc->setUserValue(key, (*itr).AsFloatUnchecked()); + break; + + case osc::CHAR_TYPE_TAG: + udc->setUserValue(key, (*itr).AsCharUnchecked()); + break; + + case osc::RGBA_COLOR_TYPE_TAG: + // TODO: should we convert the color to an osg::Vec4? + udc->setUserValue(key, static_cast((*itr).AsRgbaColorUnchecked())); + break; + + case osc::INT64_TYPE_TAG: + // TODO 64bit ints not supported by ValueObject + udc->setUserValue(key, static_cast((*itr).AsInt64Unchecked())); + break; + + case osc::TIME_TAG_TYPE_TAG: + // TODO 64bit ints not supported by ValueObject + udc->setUserValue(key, static_cast((*itr).AsTimeTagUnchecked())); + break; + + case osc::DOUBLE_TYPE_TAG: + udc->setUserValue(key, (*itr).AsDoubleUnchecked()); + break; + + case osc::STRING_TYPE_TAG: + udc->setUserValue(key, std::string((*itr).AsStringUnchecked())); + break; + + case osc::SYMBOL_TYPE_TAG: + udc->setUserValue(key, std::string((*itr).AsSymbol())); + break; + + default: + break; + + } + +} + + + + +class SetMouseInputRangeRequestHandler : public OscReceivingDevice::RequestHandler { +public: + SetMouseInputRangeRequestHandler() + : OscReceivingDevice::RequestHandler("/osgga/mouse/set_input_range") + { + } + + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m) + { + try { + float x_min(-1.0f), y_min(-1.0f), x_max(1.0f), y_max(1.0f); + osc::ReceivedMessageArgumentStream args = m.ArgumentStream(); + args >> x_min >> y_min >> x_max >> y_max >> osc::EndMessage; + + getDevice()->getEventQueue()->setMouseInputRange(x_min, y_min, x_max, y_max); + + return true; + } + catch(osc::Exception e) { + handleException(e); + } + + return false; + } + + virtual void describeTo(std::ostream& out) const + { + out << getRequestPath() << "(float x_min, float y_min, float x_max, float y_max): sets the mouse-input-range" << std::dec; + } +}; + + +class SetMouseOrientationRequestHandler : public OscReceivingDevice::RequestHandler { +public: + SetMouseOrientationRequestHandler() + : OscReceivingDevice::RequestHandler("/osgga/mouse/y_orientation_increasing_upwards") + { + } + + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m) + { + try { + bool increasing_upwards(false); + osc::ReceivedMessageArgumentStream args = m.ArgumentStream(); + args >>increasing_upwards >> osc::EndMessage; + + getDevice()->getEventQueue()->getCurrentEventState()->setMouseYOrientation( + increasing_upwards ? osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS : osgGA::GUIEventAdapter::Y_INCREASING_DOWNWARDS); + + return true; + } + catch(osc::Exception e) { + handleException(e); + } + + return false; + } + + virtual void describeTo(std::ostream& out) const + { + out << getRequestPath() << "(float x_min, float y_min, float x_max, float y_max): sets the mouse-input-range" << std::dec; + } +}; + + +class KeyCodeRequestHandler : public OscReceivingDevice::RequestHandler { +public: + KeyCodeRequestHandler(bool handle_key_press) + : OscReceivingDevice::RequestHandler(std::string("/osgga/key/") + ((handle_key_press) ? "press" : "release")) + , _handleKeyPress(handle_key_press) + { + } + + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m) + { + try { + osc::int32 keycode(0); + osc::ReceivedMessageArgumentStream args = m.ArgumentStream(); + args >> keycode >> osc::EndMessage; + + if (_handleKeyPress) + getDevice()->getEventQueue()->keyPress(keycode, getLocalTime()); + else + getDevice()->getEventQueue()->keyRelease(keycode, getLocalTime()); + + return true; + } + catch(osc::Exception e) { + handleException(e); + } + + return false; + } + + virtual void describeTo(std::ostream& out) const + { + out << getRequestPath() << "(int keycode): send KEY_" << (_handleKeyPress ? "DOWN" : "UP"); + } +private: + bool _handleKeyPress; +}; + + +class KeyPressAndReleaseRequestHandler : public OscReceivingDevice::RequestHandler { +public: + KeyPressAndReleaseRequestHandler() + : OscReceivingDevice::RequestHandler("/osgga/key/press_and_release") + { + } + + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m) + { + try { + osc::int32 keycode(0); + osc::ReceivedMessageArgumentStream args = m.ArgumentStream(); + args >> keycode >> osc::EndMessage; + + getDevice()->getEventQueue()->keyPress(keycode, getLocalTime()); + getDevice()->getEventQueue()->keyRelease(keycode, getLocalTime()); + + return true; + } + catch(osc::Exception e) { + handleException(e); + } + + return false; + } + + virtual void describeTo(std::ostream& out) const + { + out << getRequestPath() << "(int keycode): send KEY_DOWN and KEY_UP"; + } +private: + bool _handleKeyPress; +}; + + + + +class MouseMotionRequestHandler : public OscReceivingDevice::RequestHandler { +public: + MouseMotionRequestHandler() + : OscReceivingDevice::RequestHandler("/osgga/mouse/motion") + , _lastX(0.0f) + , _lastY(0.0f) + { + } + + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m) + { + + try { + osc::ReceivedMessageArgumentStream args = m.ArgumentStream(); + args >> _lastX >> _lastY >> osc::EndMessage; + + getDevice()->getEventQueue()->mouseMotion(_lastX, _lastY, getLocalTime()); + + return true; + } + catch (osc::Exception e) { + handleException(e); + } + return false; + } + + virtual void describeTo(std::ostream& out) const + { + out << getRequestPath() << "(float x, float y): send mouse motion"; + } + float getLastX() const { return _lastX; } + float getLastY() const { return _lastY; } +private: + float _lastX, _lastY; +}; + + +class MouseScrollRequestHandler : public OscReceivingDevice::RequestHandler { +public: + MouseScrollRequestHandler() + : OscReceivingDevice::RequestHandler("/osgga/mouse/scroll") + { + } + + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m) + { + + try { + osc::int32 sm(osgGA::GUIEventAdapter::SCROLL_NONE); + float delta_x(0.0f), delta_y(0.0f); + + osc::ReceivedMessageArgumentStream args = m.ArgumentStream(); + args >> sm >> delta_x >> delta_y >> osc::EndMessage; + + if (sm != osgGA::GUIEventAdapter::SCROLL_NONE) + getDevice()->getEventQueue()->mouseScroll((osgGA::GUIEventAdapter::ScrollingMotion)sm, getLocalTime()); + + if ((delta_x != 0.0f) || (delta_y != 0.0f)) + getDevice()->getEventQueue()->mouseScroll2D(delta_x, delta_y, getLocalTime()); + + return true; + } + catch (osc::Exception e) { + handleException(e); + } + return false; + } + + virtual void describeTo(std::ostream& out) const + { + out << getRequestPath() << "(int scroll_motion, float x, float y): send mouse scroll-motion"; + } +}; + + + +class MouseButtonToggleRequestHandler : public OscReceivingDevice::RequestHandler { +public: + MouseButtonToggleRequestHandler(const std::string& btn_name, MouseMotionRequestHandler* mm_handler) + : OscReceivingDevice::RequestHandler("/osgga/mouse/toggle/"+btn_name) + , _mmHandler(mm_handler) + , _btnNum(atoi(btn_name.c_str())) + { + } + + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m) + { + float down(0.0f); + + try { + osc::ReceivedMessageArgumentStream args = m.ArgumentStream(); + args >> down >> osc::EndMessage; + + if (down > 0) + getDevice()->getEventQueue()->mouseButtonPress(_mmHandler->getLastX(), _mmHandler->getLastY(), _btnNum, getLocalTime()); + else + getDevice()->getEventQueue()->mouseButtonRelease(_mmHandler->getLastX(), _mmHandler->getLastY(), _btnNum, getLocalTime()); + + return true; + } + catch (osc::Exception e) { + handleException(e); + } + return false; + } + + virtual void describeTo(std::ostream& out) const + { + out << getRequestPath() << "(float down): toggle mouse button"; + } +private: + osg::observer_ptr _mmHandler; + int _btnNum; +}; + + +class MouseButtonRequestHandler : public OscReceivingDevice::RequestHandler { +public: + enum Mode { PRESS, RELEASE, DOUBLE_PRESS}; + + MouseButtonRequestHandler(Mode mode) + : OscReceivingDevice::RequestHandler("") + , _mode(mode) + { + switch(mode) { + case PRESS: + setRequestPath("/osgga/mouse/press"); + break; + case RELEASE: + setRequestPath("/osgga/mouse/release"); + break; + case DOUBLE_PRESS: + setRequestPath("/osgga/mouse/doublepress"); + break; + } + } + + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m) + { + float x(0.0f), y(0.0f); + osc::int32 btn(0); + + try { + osc::ReceivedMessageArgumentStream args = m.ArgumentStream(); + args >> x >> y >> btn >> osc::EndMessage; + switch (_mode) { + case PRESS: + getDevice()->getEventQueue()->mouseButtonPress(x,y, btn, getLocalTime()); + break; + case RELEASE: + getDevice()->getEventQueue()->mouseButtonRelease(x,y, btn, getLocalTime()); + break; + case DOUBLE_PRESS: + getDevice()->getEventQueue()->mouseDoubleButtonPress(x,y, btn, getLocalTime()); + break; + } + + return true; + } + catch (osc::Exception e) { + handleException(e); + } + return false; + } + + virtual void describeTo(std::ostream& out) const + { + out << getRequestPath() << "(float x, float y, int btn): send mouse "; + switch (_mode) { + case PRESS: + out << "press"; break; + case RELEASE: + out << "release"; break; + case DOUBLE_PRESS: + out << "double press"; break; + } + } + +private: + Mode _mode; +}; + + +class PenPressureRequestHandler : public OscReceivingDevice::RequestHandler { +public: + PenPressureRequestHandler() + : OscReceivingDevice::RequestHandler("/osgga/pen/pressure") + { + } + + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m) + { + try { + float pressure(0.0f); + osc::ReceivedMessageArgumentStream args = m.ArgumentStream(); + args >> pressure >> osc::EndMessage; + + getDevice()->getEventQueue()->penPressure(pressure, getLocalTime()); + + return true; + } + catch (osc::Exception e) { + handleException(e); + } + return false; + } + + virtual void describeTo(std::ostream& out) const + { + out << getRequestPath() << "(float pressure): send pen pressure"; + } +}; + +class PenProximityRequestHandler : public OscReceivingDevice::RequestHandler { +public: + PenProximityRequestHandler(bool handle_enter) + : OscReceivingDevice::RequestHandler(std::string("/osgga/pen/proximity/") + ((handle_enter) ? std::string("enter") : std::string("leave"))) + , _handleEnter(handle_enter) + { + } + + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m) + { + try { + osc::int32 pt(osgGA::GUIEventAdapter::UNKNOWN); + + osc::ReceivedMessageArgumentStream args = m.ArgumentStream(); + args >> pt >> osc::EndMessage; + + getDevice()->getEventQueue()->penProximity((osgGA::GUIEventAdapter::TabletPointerType)pt, _handleEnter, getLocalTime()); + + return true; + } + catch (osc::Exception e) { + handleException(e); + } + return false; + } + + virtual void describeTo(std::ostream& out) const + { + out << getRequestPath() << "(int table_pointer_type): send pen proximity " << (_handleEnter ? "enter":"leave"); + } +private: + bool _handleEnter; +}; + + +class PenOrientationRequestHandler : public OscReceivingDevice::RequestHandler { +public: + PenOrientationRequestHandler() + : OscReceivingDevice::RequestHandler("/osgga/pen/orientation") + { + } + + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m) + { + try { + float rotation(0.0f), tilt_x(0.0f), tilt_y(0.0f); + osc::ReceivedMessageArgumentStream args = m.ArgumentStream(); + args >> rotation >> tilt_x >> tilt_y >> osc::EndMessage; + + getDevice()->getEventQueue()->penOrientation(tilt_x, tilt_y, rotation, getLocalTime()); + + return true; + } + catch (osc::Exception e) { + handleException(e); + } + return false; + } + + virtual void describeTo(std::ostream& out) const + { + out << getRequestPath() << "(float rotation, float tilt_x, float tilt_y): send pen orientation"; + } +}; + + + + + +OscReceivingDevice::OscReceivingDevice(const std::string& server_address, int listening_port) + : osgGA::Device() + , OpenThreads::Thread() + , osc::OscPacketListener() + , _listeningAddress(server_address) + , _listeningPort(listening_port) + , _socket(NULL) + , _map() +{ + setCapabilities(RECEIVE_EVENTS); + OSG_NOTICE << "OscDevice :: listening on " << server_address << ":" << listening_port << " "; + #ifdef OSC_HOST_LITTLE_ENDIAN + OSG_NOTICE << "(little endian)"; + #elif OSC_HOST_BIG_ENDIAN + OSG_NOTICE << "(big endian)"; + #endif + OSG_NOTICE << std::endl; + + _socket = new UdpListeningReceiveSocket(IpEndpointName( server_address.c_str(), listening_port ), this); + + addRequestHandler(new KeyCodeRequestHandler(false)); + addRequestHandler(new KeyCodeRequestHandler(true)); + addRequestHandler(new KeyPressAndReleaseRequestHandler()); + + addRequestHandler(new SetMouseInputRangeRequestHandler()); + addRequestHandler(new SetMouseOrientationRequestHandler()); + + MouseMotionRequestHandler* mm_handler = new MouseMotionRequestHandler(); + addRequestHandler(mm_handler); + addRequestHandler(new MouseButtonRequestHandler(MouseButtonRequestHandler::PRESS)); + addRequestHandler(new MouseButtonRequestHandler(MouseButtonRequestHandler::RELEASE)); + addRequestHandler(new MouseButtonRequestHandler(MouseButtonRequestHandler::DOUBLE_PRESS)); + addRequestHandler(new MouseScrollRequestHandler()); + + addRequestHandler(new MouseButtonToggleRequestHandler("1", mm_handler)); + addRequestHandler(new MouseButtonToggleRequestHandler("2", mm_handler)); + addRequestHandler(new MouseButtonToggleRequestHandler("3", mm_handler)); + + addRequestHandler(new PenPressureRequestHandler()); + addRequestHandler(new PenOrientationRequestHandler()); + addRequestHandler(new PenProximityRequestHandler(true)); + addRequestHandler(new PenProximityRequestHandler(false)); + + addRequestHandler(new StandardRequestHandler("/osg/set_user_value", true)); + + addRequestHandler(new StandardRequestHandler("", false)); + + start(); +} + + +OscReceivingDevice::~OscReceivingDevice() +{ + _socket->AsynchronousBreak(); + join(); + delete _socket; +} + + +void OscReceivingDevice::run() +{ + _socket->Run(); +} + + +void OscReceivingDevice::ProcessMessage( const osc::ReceivedMessage& m, const IpEndpointName& remoteEndpoint ) +{ + std::string in_request_path(m.AddressPattern()); + std::string request_path = in_request_path + "/"; + + std::size_t pos(std::string::npos); + bool handled(false); + do { + pos = request_path.find_last_of('/', pos-1); + if (pos != std::string::npos) + { + std::string mangled_path = request_path.substr(0, pos); + + std::pair range = _map.equal_range(mangled_path); + + for(RequestHandlerMap::iterator i = range.first; i != range.second; ++i) + { + // OSG_INFO << "OscDevice :: handling " << mangled_path << " with " << i->second << std::endl; + + if (i->second->operator()(mangled_path, in_request_path, m) && !handled) + handled = true; + } + + } + } while ((pos != std::string::npos) && (pos > 0) && !handled); + +} + +void OscReceivingDevice::ProcessPacket( const char *data, int size, const IpEndpointName& remoteEndpoint ) +{ + OSG_INFO << "OscDevice :: receiving " << size << " bytes of data ..." << std::endl; + + try { + osc::OscPacketListener::ProcessPacket(data, size, remoteEndpoint); + } + catch(const osc::Exception& e) { + OSG_WARN << "OscDevice :: could not process UDP-packet: " << e.what() << std::endl; + } + catch(...) { + OSG_WARN << "OscDevice :: could not process UDP-packet because of an exception!" << std::endl; + } + + if (_userDataEvent.valid()) + { + char address[IpEndpointName::ADDRESS_AND_PORT_STRING_LENGTH]; + + + remoteEndpoint.AddressAndPortAsString(address); + + _userDataEvent->setUserValue("osc/remote_end_point", std::string(address)); + + getEventQueue()->addEvent(_userDataEvent.get()); + _userDataEvent = NULL; + } +} + +void OscReceivingDevice::addRequestHandler(RequestHandler* handler) +{ + if (handler) + { + _map.insert(std::make_pair(handler->getRequestPath(), handler)); + handler->setDevice(this); + } +} + +void OscReceivingDevice::describeTo(std::ostream& out) const +{ + out << "OscDevice :: listening on " << _listeningAddress << ":" << _listeningPort << std::endl; + out << std::endl; + + for(RequestHandlerMap::const_iterator i = _map.begin(); i != _map.end(); ++i) + { + const RequestHandler* handler(i->second.get()); + out << "OscDevice :: "; + handler->describeTo(out); + out << std::endl; + } + +} diff --git a/src/osgPlugins/osc/OscReceivingDevice.hpp b/src/osgPlugins/osc/OscReceivingDevice.hpp new file mode 100755 index 000000000..88ea6c2c9 --- /dev/null +++ b/src/osgPlugins/osc/OscReceivingDevice.hpp @@ -0,0 +1,127 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + + +#pragma once + +#include +#include +#include +#include +#include + + + + +class OscReceivingDevice : public osgGA::Device, OpenThreads::Thread, osc::OscPacketListener { + +public: + class RequestHandler : public osg::Referenced { + public: + RequestHandler(const std::string& request_path) + : osg::Referenced() + , _requestPath(request_path) + , _device(NULL) + { + } + + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& m) = 0; + + const std::string& getRequestPath() const { return _requestPath; } + + virtual void describeTo(std::ostream& out) const + { + out << getRequestPath() << ": no description available"; + } + + protected: + void setDevice(OscReceivingDevice* device) { _device = device; } + OscReceivingDevice* getDevice() const { return _device; } + + /// set the request-path, works only from the constructor + void setRequestPath(const std::string& request_path) { _requestPath = request_path; } + + void handleException(const osc::Exception& e) + { + OSG_WARN << "OscDevice :: error while handling " << getRequestPath() << ": " << e.what() << std::endl; + } + + double getLocalTime() const { return getDevice()->getEventQueue()->getTime(); } + private: + std::string _requestPath; + OscReceivingDevice* _device; + friend class OscReceivingDevice; + }; + + typedef std::multimap > RequestHandlerMap; + + OscReceivingDevice(const std::string& server_address, int listening_port); + ~OscReceivingDevice(); + + + virtual void checkEvents() {} + virtual void run(); + + + virtual void ProcessMessage( const osc::ReceivedMessage& m, const IpEndpointName& remoteEndpoint ); + virtual void ProcessPacket( const char *data, int size, const IpEndpointName& remoteEndpoint ); + + void addRequestHandler(RequestHandler* handler); + + void describeTo(std::ostream& out) const; + + friend std::ostream& operator<<(std::ostream& out, const OscReceivingDevice& device) + { + device.describeTo(out); + return out; + } + + osgGA::GUIEventAdapter* getOrCreateUserDataEvent() + { + if (!_userDataEvent.valid()) + { + _userDataEvent = new osgGA::GUIEventAdapter(); + _userDataEvent->setEventType(osgGA::GUIEventAdapter::USER); + } + return _userDataEvent.get(); + } + +private: + std::string _listeningAddress; + unsigned int _listeningPort; + UdpListeningReceiveSocket* _socket; + RequestHandlerMap _map; + osg::ref_ptr _userDataEvent; + +}; + + +class SendKeystrokeRequestHandler : public OscReceivingDevice::RequestHandler { +public: + SendKeystrokeRequestHandler(const std::string& request_path, int key) : OscReceivingDevice::RequestHandler(request_path), _key(key) {} + virtual bool operator()(const std::string& request_path, const std::string& full_request_path, const osc::ReceivedMessage& arguments) + { + getDevice()->getEventQueue()->keyPress(_key); + getDevice()->getEventQueue()->keyRelease(_key); + + return true; + } + + virtual void describeTo(std::ostream& out) const + { + out << getRequestPath() << ": send KEY_DOWN + KEY_UP, code: 0x" << std::hex << _key << std::dec; + } +private: + int _key; +}; + diff --git a/src/osgPlugins/osc/OscSendingDevice.cpp b/src/osgPlugins/osc/OscSendingDevice.cpp new file mode 100755 index 000000000..268b80f01 --- /dev/null +++ b/src/osgPlugins/osc/OscSendingDevice.cpp @@ -0,0 +1,271 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + + +#include "OscSendingDevice.hpp" +#include "osc/OscHostEndianness.h" +#include +#include + +static const unsigned long BUFFER_SIZE = 2048; + +OscSendingDevice::OscSendingDevice(const std::string& address, int port) + : osgGA::Device() + , _transmitSocket(IpEndpointName(address.c_str(), port)) + , _buffer(new char[BUFFER_SIZE]) + , _oscStream(_buffer, BUFFER_SIZE) + , _firstRun(true) +{ + setCapabilities(SEND_EVENTS); + + OSG_NOTICE << "OscDevice :: sending events to " << address << ":" << port << " "; + #ifdef OSC_HOST_LITTLE_ENDIAN + OSG_NOTICE << "(little endian)"; + #elif OSC_HOST_BIG_ENDIAN + OSG_NOTICE << "(big endian)"; + #endif + OSG_NOTICE << std::endl; + +} + + +OscSendingDevice::~OscSendingDevice() +{ + delete[] (_buffer); +} + +void OscSendingDevice::sendEvent(const osgGA::GUIEventAdapter &ea) +{ + bool do_send(false); + switch(ea.getEventType()) + { + case osgGA::GUIEventAdapter::RESIZE: + sendInit(ea); + do_send = true; + break; + + case osgGA::GUIEventAdapter::SCROLL: + _oscStream << osc::BeginMessage("/osgga/mouse/scroll") << ea.getScrollingMotion() << ea.getScrollingDeltaX() << ea.getScrollingDeltaY() << osc::EndMessage; + do_send = true; + break; + + case osgGA::GUIEventAdapter::PEN_PRESSURE: + _oscStream + << osc::BeginMessage("/osgga/pen/pressure") + << ea.getPenPressure() + << osc::EndMessage; + do_send = true; + break; + + case osgGA::GUIEventAdapter::PEN_ORIENTATION: + + _oscStream + << osc::BeginMessage("/osgga/pen/orientation") + << ea.getPenRotation() + << ea.getPenTiltX() + << ea.getPenTiltY() + << osc::EndMessage; + do_send = true; + break; + + case osgGA::GUIEventAdapter::PEN_PROXIMITY_ENTER: + _oscStream + << osc::BeginMessage("/osgga/pen/proximity/enter") + << ea.getTabletPointerType() + << osc::EndMessage; + do_send = true; + break; + + case osgGA::GUIEventAdapter::PEN_PROXIMITY_LEAVE: + _oscStream + << osc::BeginMessage("/osgga/pen/proximity/leave") + << ea.getTabletPointerType() + << osc::EndMessage; + do_send = true; + break; + + case osgGA::GUIEventAdapter::PUSH: + _oscStream << osc::BeginMessage("/osgga/mouse/press") << ea.getX() << ea.getY() << getButtonNum(ea) << osc::EndMessage; + do_send = true; + break; + + case osgGA::GUIEventAdapter::RELEASE: + _oscStream << osc::BeginMessage("/osgga/mouse/release") << ea.getX() << ea.getY() << getButtonNum(ea) << osc::EndMessage; + do_send = true; + break; + + case osgGA::GUIEventAdapter::DOUBLECLICK: + _oscStream << osc::BeginMessage("/osgga/mouse/doublepress") << ea.getX() << ea.getY() << getButtonNum(ea) << osc::EndMessage; + do_send = true; + break; + + case osgGA::GUIEventAdapter::MOVE: + if (_firstRun) + { + _firstRun = false; + sendInit(ea); + do_send = true; + break; + } + // break missing by intent; + + case osgGA::GUIEventAdapter::DRAG: + _oscStream << osc::BeginMessage("/osgga/mouse/motion") << ea.getX() << ea.getY() << osc::EndMessage; + do_send = true; + break; + + case osgGA::GUIEventAdapter::KEYDOWN: + _oscStream << osc::BeginMessage("/osgga/key/press") << ea.getKey() << osc::EndMessage; + do_send = true; + break; + + case osgGA::GUIEventAdapter::KEYUP: + _oscStream << osc::BeginMessage("/osgga/key/release") << ea.getKey() << osc::EndMessage; + do_send = true; + break; + + case osgGA::GUIEventAdapter::USER: + if (ea.getUserDataContainer()) + { + std::string key = ea.getUserDataContainer()->getName(); + if (key.empty()) key = ea.getName(); + if (key.empty()) key = "user_data"; + + sendUserDataContainer(transliterateKey(key), ea.getUserDataContainer(), true); + + do_send = true; + } + + default: + break; + + } + if (do_send) + { + OSG_INFO << "OscDevice :: sending event per OSC " << std::endl; + + _transmitSocket.Send( _oscStream.Data(), _oscStream.Size() ); + _oscStream.Clear(); + } +} + +int OscSendingDevice::getButtonNum(const osgGA::GUIEventAdapter& ea) +{ + switch(ea.getButton()) + { + case osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON: + return 1; + break; + case osgGA::GUIEventAdapter::MIDDLE_MOUSE_BUTTON: + return 2; + break; + case osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON: + return 3; + break; + default: + return -1; + } + return -1; +} + +void OscSendingDevice::sendInit(const osgGA::GUIEventAdapter &ea) +{ + _oscStream << osc::BeginBundle(); + _oscStream << osc::BeginMessage("/osgga/resize") << ea.getWindowX() << ea.getWindowY() << ea.getWindowWidth() << ea.getWindowHeight() << osc::EndMessage; + _oscStream << osc::BeginMessage("/osgga/mouse/set_input_range") << ea.getXmin() << ea.getYmin() << ea.getXmax() << ea.getYmax() << osc::EndMessage; + _oscStream << osc::BeginMessage("/osgga/mouse/y_orientation_increasing_upwards") << (bool)(ea.getMouseYOrientation() == osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS) << osc::EndMessage; + _oscStream << osc::EndBundle; +} + + +class OscSendingDeviceGetValueVisitor : public osg::ValueObject::GetValueVisitor { +public: + OscSendingDeviceGetValueVisitor(osc::OutboundPacketStream& stream) + : osg::ValueObject::GetValueVisitor() + , _stream(stream) + { + } + + virtual void apply(bool value) { _stream << value; } + virtual void apply(char value) { _stream << value; } + virtual void apply(unsigned char value) { _stream << value; } + virtual void apply(short value) { _stream << value; } + virtual void apply(unsigned short value) { _stream << value; } + virtual void apply(int value) { _stream << value; } + virtual void apply(unsigned int value) { _stream << static_cast(value); } + virtual void apply(float value) { _stream << value; } + virtual void apply(double value) { _stream << value; } + virtual void apply(const std::string& value) { _stream << value.c_str(); } + virtual void apply(const osg::Vec2f& value) { _stream << value[0] << value[1]; } + virtual void apply(const osg::Vec3f& value) { _stream << value[0] << value[1] << value[2]; } + virtual void apply(const osg::Vec4f& value) { _stream << value[0] << value[1] << value[2] << value[3]; } + virtual void apply(const osg::Vec2d& value) { _stream << value[0] << value[1]; } + virtual void apply(const osg::Vec3d& value) { _stream << value[0] << value[1] << value[2]; } + virtual void apply(const osg::Vec4d& value) { _stream << value[0] << value[1] << value[2] << value[3]; } + virtual void apply(const osg::Quat& value) { _stream << value[0] << value[1] << value[2] << value[3]; } + virtual void apply(const osg::Plane& value) { _stream << value[0] << value[1] << value[2] << value[3]; } + virtual void apply(const osg::Matrixf& value) { for(unsigned int i=0; i<16; ++i) _stream << (value.ptr())[i]; } + virtual void apply(const osg::Matrixd& value) { for(unsigned int i=0; i<16; ++i) _stream << (value.ptr())[i]; } + +private: + osc::OutboundPacketStream& _stream; + +}; + +std::string OscSendingDevice::transliterateKey(const std::string& key) const +{ + std::string result; + result.reserve(key.size()); + for(std::string::const_iterator itr=key.begin(); + itr!=key.end(); + ++itr) + { + if ((*itr == ' ') || (*itr == 9)) + result += "-"; + else if ((*itr >= 'A') && (*itr <= 'Z')) + result += tolower(*itr); + else if (((*itr >= '0') && (*itr <= '9')) || ((*itr >= 'a') && (*itr <= 'z')) || (*itr == '-') || (*itr == '/') || (*itr == '_')) + result += *itr; + } + return result; +} + +void OscSendingDevice::sendUserDataContainer(const std::string& key, const osg::UserDataContainer* udc, bool asBundle) +{ + if (asBundle) + _oscStream << osc::BeginBundle(); + + OscSendingDeviceGetValueVisitor gvv(_oscStream); + + unsigned int num_objects = udc->getNumUserObjects(); + for(unsigned int i = 0; i < num_objects; ++i) + { + const osg::Object* o = udc->getUserObject(i); + const osg::UserDataContainer* child_udc = dynamic_cast(o); + if (child_udc) + { + std::string new_key = key + "/" + (child_udc->getName().empty() ? "user_data" : child_udc->getName()); + sendUserDataContainer(transliterateKey(key), child_udc, false); + } + else if (const osg::ValueObject* vo = dynamic_cast(o)) + { + _oscStream << osc::BeginMessage(std::string("/" + key + "/" + transliterateKey(vo->getName())).c_str()); + vo->get(gvv); + _oscStream << osc::EndMessage; + } + } + + if (asBundle) + _oscStream << osc::EndBundle; + +} diff --git a/src/osgPlugins/osc/OscSendingDevice.hpp b/src/osgPlugins/osc/OscSendingDevice.hpp new file mode 100755 index 000000000..cac02dd7d --- /dev/null +++ b/src/osgPlugins/osc/OscSendingDevice.hpp @@ -0,0 +1,39 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + + +#pragma once + + + +#include +#include +#include + +class OscSendingDevice : public osgGA::Device { +public: + OscSendingDevice(const std::string& address, int port); + ~OscSendingDevice(); + virtual void sendEvent(const osgGA::GUIEventAdapter &ea); + +private: + void sendInit(const osgGA::GUIEventAdapter& ea); + int getButtonNum(const osgGA::GUIEventAdapter& ea); + void sendUserDataContainer(const std::string& key, const osg::UserDataContainer* udc, bool asBundle); + std::string transliterateKey(const std::string& key) const; + UdpTransmitSocket _transmitSocket; + char* _buffer; + osc::OutboundPacketStream _oscStream; + bool _firstRun; +}; +