1155 lines
37 KiB
C++
1155 lines
37 KiB
C++
/* -*-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 <OpenThreads/Thread>
|
|
#include <osg/UserDataContainer>
|
|
#include <osg/ValueObject>
|
|
#include <osgDB/FileUtils>
|
|
#include <osgDB/FileNameUtils>
|
|
#include <osgPresentation/PropertyManager>
|
|
#include "osc/OscPrintReceivedElements.h"
|
|
#include "osc/OscHostEndianness.h"
|
|
|
|
namespace OscDevice {
|
|
|
|
template <class T, int SIZE>
|
|
struct NativeTypeTraits {
|
|
typedef T type;
|
|
static T create(const std::vector<T>& t) { return type(t); }
|
|
};
|
|
|
|
template<>
|
|
struct NativeTypeTraits<float,2> {
|
|
typedef osg::Vec2f type;
|
|
static type create(const std::vector<float>& t) { return type(t[0], t[1]); }
|
|
};
|
|
|
|
template<>
|
|
struct NativeTypeTraits<float,3> {
|
|
typedef osg::Vec3f type;
|
|
static type create(const std::vector<float>& t) { return type(t[0], t[1], t[2]); }
|
|
|
|
};
|
|
|
|
template<>
|
|
struct NativeTypeTraits<float,4> {
|
|
typedef osg::Vec4f type;
|
|
static type create(const std::vector<float>& t) { return type(t[0], t[1], t[2], t[3]); }
|
|
|
|
};
|
|
|
|
template<>
|
|
struct NativeTypeTraits<float,16> {
|
|
typedef osg::Matrixf type;
|
|
static type create(const std::vector<float>& t) { return type(&t.front()); }
|
|
|
|
};
|
|
|
|
|
|
template<>
|
|
struct NativeTypeTraits<double,2> {
|
|
typedef osg::Vec2d type;
|
|
static type create(const std::vector<double>& t) { return type(t[0], t[1]); }
|
|
};
|
|
|
|
template<>
|
|
struct NativeTypeTraits<double,3> {
|
|
typedef osg::Vec3d type;
|
|
static type create(const std::vector<double>& t) { return type(t[0], t[1], t[2]); }
|
|
};
|
|
|
|
template<>
|
|
struct NativeTypeTraits<double,4> {
|
|
typedef osg::Vec4d type;
|
|
static type create(const std::vector<double>& t) { return type(t[0], t[1], t[2], t[3]); }
|
|
};
|
|
|
|
template<>
|
|
struct NativeTypeTraits<double,16> {
|
|
typedef osg::Matrixd type;
|
|
static type create(const std::vector<double>& 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, const IpEndpointName& remoteEndPoint);
|
|
|
|
virtual void describeTo(std::ostream& out) const
|
|
{
|
|
out << getRequestPath() << ": add all transmitted arguments as ValueObjects to an 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 <class T>
|
|
bool addNativeTypeFromVector(osg::UserDataContainer* udc, const std::string& key, const std::vector<T>& arr)
|
|
{
|
|
switch (arr.size()) {
|
|
case 2:
|
|
udc->setUserValue(key, NativeTypeTraits<T,2>::create(arr));
|
|
return true;
|
|
break;
|
|
case 3:
|
|
udc->setUserValue(key, NativeTypeTraits<T,3>::create(arr));
|
|
return true;
|
|
break;
|
|
case 4:
|
|
udc->setUserValue(key, NativeTypeTraits<T,4>::create(arr));
|
|
return true;
|
|
break;
|
|
case 16:
|
|
udc->setUserValue(key, NativeTypeTraits<T,16>::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, const IpEndpointName& /*remoteEndPoint*/)
|
|
{
|
|
try
|
|
{
|
|
std::string path = osgDB::getFilePath(full_request_path);
|
|
std::string last_elem = osgDB::getSimpleFileName(full_request_path);
|
|
|
|
osg::ref_ptr<osgGA::Event> 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> float_vec;
|
|
std::vector<double> 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<unsigned int>((*itr).AsRgbaColorUnchecked()));
|
|
break;
|
|
|
|
case osc::INT64_TYPE_TAG:
|
|
// TODO 64bit ints not supported by ValueObject
|
|
udc->setUserValue(key, static_cast<double>((*itr).AsInt64Unchecked()));
|
|
break;
|
|
|
|
case osc::TIME_TAG_TYPE_TAG:
|
|
// TODO 64bit ints not supported by ValueObject
|
|
udc->setUserValue(key, static_cast<double>((*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, const IpEndpointName& /*remoteEndPoint*/)
|
|
{
|
|
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, const IpEndpointName& /*remoteEndPoint*/)
|
|
{
|
|
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, const IpEndpointName& /*remoteEndPoint*/)
|
|
{
|
|
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, const IpEndpointName& /*remoteEndPoint*/)
|
|
{
|
|
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";
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
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, const IpEndpointName& /*remoteEndPoint*/)
|
|
{
|
|
|
|
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, const IpEndpointName& /*remoteEndPoint*/)
|
|
{
|
|
|
|
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, const IpEndpointName& /*remoteEndPoint*/)
|
|
{
|
|
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<MouseMotionRequestHandler> _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, const IpEndpointName& /*remoteEndPoint*/)
|
|
{
|
|
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, const IpEndpointName& /*remoteEndPoint*/)
|
|
{
|
|
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, const IpEndpointName& /*remoteEndPoint*/)
|
|
{
|
|
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, const IpEndpointName& /*remoteEndPoint*/)
|
|
{
|
|
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";
|
|
}
|
|
};
|
|
|
|
|
|
class TUIO2DCursorRequestHandler : public OscReceivingDevice::RequestHandler {
|
|
|
|
public:
|
|
|
|
struct Cursor {
|
|
std::string end_point;
|
|
unsigned int id, frameId;
|
|
osg::Vec2f pos, vel;
|
|
float accel;
|
|
osgGA::GUIEventAdapter::TouchPhase phase;
|
|
|
|
Cursor() : end_point(), id(0), frameId(0), pos(), vel(), accel(), phase(osgGA::GUIEventAdapter::TOUCH_UNKNOWN) {}
|
|
|
|
};
|
|
struct EndpointData {
|
|
std::string source;
|
|
osc::int32 frameId;
|
|
bool mayClearUnhandledPointer;
|
|
std::set<unsigned int> unhandled;
|
|
};
|
|
|
|
typedef std::map<std::string, EndpointData> EndpointDataMap;
|
|
typedef std::map<unsigned int, Cursor> CursorMap;
|
|
typedef std::map<std::string, CursorMap> ApplicationCursorMap;
|
|
typedef std::map<std::string, unsigned int> SourceIdMap;
|
|
TUIO2DCursorRequestHandler()
|
|
: OscReceivingDevice::RequestHandler("/tuio/2Dcur")
|
|
{
|
|
}
|
|
|
|
virtual void setDevice(OscReceivingDevice* device) {
|
|
OscReceivingDevice::RequestHandler::setDevice(device);
|
|
device->addHandleOnCheckEvents(this);
|
|
}
|
|
|
|
|
|
virtual bool operator()(const std::string& /*request_path*/, const std::string& /*full_request_path*/, const osc::ReceivedMessage& m, const IpEndpointName& remoteEndPoint)
|
|
{
|
|
// std::cout << m << std::endl;
|
|
|
|
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
|
|
|
|
std::string end_point(' ', IpEndpointName::ADDRESS_AND_PORT_STRING_LENGTH);
|
|
remoteEndPoint.AddressAndPortAsString(&end_point[0]);
|
|
|
|
osc::ReceivedMessageArgumentStream args = m.ArgumentStream();
|
|
|
|
|
|
const char* str;
|
|
args >> str;
|
|
std::string what(str);
|
|
|
|
if (what == "source")
|
|
{
|
|
args >> str;
|
|
_endpointData[end_point].source = std::string(str);
|
|
updateSourceIdMap(_endpointData[end_point].source);
|
|
|
|
_endpointData[end_point].unhandled.clear();
|
|
_endpointData[end_point].mayClearUnhandledPointer = true;
|
|
|
|
return true;
|
|
}
|
|
else if (what == "fseq")
|
|
{
|
|
args >> _endpointData[end_point].frameId;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
std::string source = _endpointData[end_point].source;
|
|
unsigned int frame_id = _endpointData[end_point].frameId;
|
|
|
|
if (what == "alive")
|
|
{
|
|
while (!args.Eos())
|
|
{
|
|
osc::int32 id;
|
|
args >> id;
|
|
_endpointData[end_point].unhandled.insert(id);
|
|
}
|
|
return true;
|
|
}
|
|
else if (what == "set")
|
|
{
|
|
osc::int32 id;
|
|
args >> id;
|
|
if (_alive[source].find(id) == _alive[source].end())
|
|
{
|
|
_alive[source][id] = Cursor();
|
|
}
|
|
|
|
Cursor& c(_alive[source][id]);
|
|
args >> c.pos.x() >> c.pos.y() >> c.vel.x() >> c.vel.y() >> c.accel >> osc::EndMessage;
|
|
c.frameId = frame_id;
|
|
c.end_point = end_point;
|
|
_endpointData[end_point].unhandled.insert(id);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
virtual void operator()(osgGA::EventQueue* queue)
|
|
{
|
|
// dispatch all touchpoints in one GUIEventAdapter
|
|
|
|
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
|
|
|
|
osg::ref_ptr<osgGA::GUIEventAdapter> event = NULL;
|
|
|
|
|
|
for(ApplicationCursorMap::iterator i = _alive.begin(); i != _alive.end(); ++i)
|
|
{
|
|
const std::string& source(i->first);
|
|
CursorMap& cursormap = i->second;
|
|
|
|
/*
|
|
std::cout << source << ": ";
|
|
for(std::set<unsigned int>::iterator k = _endpointData[source].unhandled.begin();
|
|
k != _endpointData[source].unhandled.end();
|
|
++k)
|
|
{
|
|
std::cout << *k << " ";
|
|
}
|
|
std::cout << std::endl;
|
|
*/
|
|
|
|
// remove all touchpoints which are not transmitted via alive-message, dispatching TOUCH_ENDED
|
|
|
|
unsigned int source_id = getSourceId(source);
|
|
|
|
std::vector<unsigned int> to_delete;
|
|
|
|
for(CursorMap::iterator k = cursormap.begin(); k != cursormap.end(); ++k)
|
|
{
|
|
EndpointData& endpoint_data(_endpointData[k->second.end_point]);
|
|
/*if (!endpoint_data.mayClearUnhandledPointer)
|
|
{
|
|
continue;
|
|
}*/
|
|
|
|
//create a unique touchpoint-id
|
|
unsigned int touch_id = (source_id << 16) + k->first;
|
|
|
|
std::set<unsigned int>& unhandled(endpoint_data.unhandled);
|
|
if ((unhandled.find(k->first) == unhandled.end()))
|
|
{
|
|
// std::cout << "deleting: " << k->first << " from " << k->second.end_point << std::endl;
|
|
to_delete.push_back(k->first);
|
|
|
|
float win_x = k->second.pos.x();
|
|
float win_y = k->second.pos.y();
|
|
|
|
if (!event)
|
|
event = queue->touchEnded(touch_id, osgGA::GUIEventAdapter::TOUCH_ENDED, win_x, win_y, 1);
|
|
else
|
|
event->addTouchPoint(touch_id, osgGA::GUIEventAdapter::TOUCH_ENDED, win_x, win_y, 1);
|
|
}
|
|
}
|
|
// remove "dead" cursors
|
|
for(std::vector<unsigned int>::iterator k = to_delete.begin(); k != to_delete.end(); ++k)
|
|
{
|
|
cursormap.erase(cursormap.find(*k));
|
|
}
|
|
|
|
if (i->second.size() == 0)
|
|
{
|
|
// std::cout << "removing endpoint" << source << std::endl;
|
|
// _alive.erase(_alive.find(source));
|
|
}
|
|
}
|
|
|
|
|
|
// send all alive touchpoints
|
|
for(ApplicationCursorMap::iterator i = _alive.begin(); i != _alive.end(); ++i)
|
|
{
|
|
const std::string& source(i->first);
|
|
unsigned int source_id = getSourceId(source);
|
|
|
|
for(CursorMap::iterator k = i->second.begin(); k != i->second.end(); ++k)
|
|
{
|
|
unsigned int id = k->first;
|
|
unsigned int touch_id = (source_id << 16) + id;
|
|
|
|
Cursor& c(k->second);
|
|
float win_x = c.pos.x();
|
|
float win_y = c.pos.y();
|
|
|
|
bool down = c.phase != osgGA::GUIEventAdapter::TOUCH_MOVED && c.phase != osgGA::GUIEventAdapter::TOUCH_STATIONERY;
|
|
if(!event)
|
|
{
|
|
if(down)
|
|
event = queue->touchBegan(touch_id, osgGA::GUIEventAdapter::TOUCH_BEGAN, win_x, win_y);
|
|
else
|
|
event = queue->touchMoved(touch_id, osgGA::GUIEventAdapter::TOUCH_MOVED, win_x, win_y);
|
|
}
|
|
else
|
|
{
|
|
event->addTouchPoint(touch_id, down ? osgGA::GUIEventAdapter::TOUCH_BEGAN : osgGA::GUIEventAdapter::TOUCH_MOVED, win_x, win_y);
|
|
}
|
|
|
|
c.phase = osgGA::GUIEventAdapter::TOUCH_MOVED;
|
|
}
|
|
}
|
|
|
|
// adjust time + input range
|
|
if (event)
|
|
{
|
|
event->setInputRange(0, 0, 1.0, 1.0);
|
|
event->setTime(queue->getTime());
|
|
event->setMouseYOrientation(osgGA::GUIEventAdapter::Y_INCREASING_DOWNWARDS);
|
|
}
|
|
}
|
|
|
|
|
|
inline void updateSourceIdMap(const std::string& source)
|
|
{
|
|
if (_sourceIdMap.find(source) == _sourceIdMap.end())
|
|
_sourceIdMap[source] = _sourceIdMap.size();
|
|
}
|
|
|
|
inline unsigned int getSourceId(const std::string& source)
|
|
{
|
|
return _sourceIdMap[source];
|
|
}
|
|
|
|
private:
|
|
EndpointDataMap _endpointData;
|
|
ApplicationCursorMap _alive;
|
|
OpenThreads::Mutex _mutex;
|
|
SourceIdMap _sourceIdMap;
|
|
};
|
|
|
|
|
|
} // end of namespace
|
|
|
|
|
|
|
|
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()
|
|
, _lastMsgId(0)
|
|
{
|
|
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 OscDevice::KeyCodeRequestHandler(false));
|
|
addRequestHandler(new OscDevice::KeyCodeRequestHandler(true));
|
|
addRequestHandler(new OscDevice::KeyPressAndReleaseRequestHandler());
|
|
|
|
addRequestHandler(new OscDevice::SetMouseInputRangeRequestHandler());
|
|
addRequestHandler(new OscDevice::SetMouseOrientationRequestHandler());
|
|
|
|
OscDevice::MouseMotionRequestHandler* mm_handler = new OscDevice::MouseMotionRequestHandler();
|
|
addRequestHandler(mm_handler);
|
|
addRequestHandler(new OscDevice::MouseButtonRequestHandler(OscDevice::MouseButtonRequestHandler::PRESS));
|
|
addRequestHandler(new OscDevice::MouseButtonRequestHandler(OscDevice::MouseButtonRequestHandler::RELEASE));
|
|
addRequestHandler(new OscDevice::MouseButtonRequestHandler(OscDevice::MouseButtonRequestHandler::DOUBLE_PRESS));
|
|
addRequestHandler(new OscDevice::MouseScrollRequestHandler());
|
|
|
|
addRequestHandler(new OscDevice::MouseButtonToggleRequestHandler("1", mm_handler));
|
|
addRequestHandler(new OscDevice::MouseButtonToggleRequestHandler("2", mm_handler));
|
|
addRequestHandler(new OscDevice::MouseButtonToggleRequestHandler("3", mm_handler));
|
|
|
|
addRequestHandler(new OscDevice::PenPressureRequestHandler());
|
|
addRequestHandler(new OscDevice::PenOrientationRequestHandler());
|
|
addRequestHandler(new OscDevice::PenProximityRequestHandler(true));
|
|
addRequestHandler(new OscDevice::PenProximityRequestHandler(false));
|
|
|
|
addRequestHandler(new OscDevice::TUIO2DCursorRequestHandler());
|
|
|
|
addRequestHandler(new OscDevice::StandardRequestHandler("/osg/set_user_value", true));
|
|
|
|
addRequestHandler(new OscDevice::StandardRequestHandler("", false));
|
|
|
|
// getEventQueue()->setFirstTouchEmulatesMouse(false);
|
|
|
|
setSchedulePriority(OpenThreads::Thread::THREAD_PRIORITY_LOW);
|
|
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());
|
|
|
|
if (in_request_path == "/osc/msg_id")
|
|
return;
|
|
|
|
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<RequestHandlerMap::iterator,RequestHandlerMap::iterator> 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, remoteEndpoint) && !handled)
|
|
handled = true;
|
|
}
|
|
|
|
}
|
|
} while ((pos != std::string::npos) && (pos > 0) && !handled);
|
|
|
|
}
|
|
|
|
void OscReceivingDevice::ProcessBundle( const osc::ReceivedBundle& b,
|
|
const IpEndpointName& remoteEndpoint )
|
|
{
|
|
// find msg-id
|
|
MsgIdType msg_id(0);
|
|
|
|
for( osc::ReceivedBundle::const_iterator i = b.ElementsBegin(); i != b.ElementsEnd(); ++i ){
|
|
const osc::ReceivedMessage& m = osc::ReceivedMessage(*i);
|
|
std::string address_pattern(m.AddressPattern());
|
|
if(address_pattern == "/osc/msg_id")
|
|
{
|
|
osc::ReceivedMessageArgumentStream args = m.ArgumentStream();
|
|
args >> msg_id;
|
|
break;
|
|
}
|
|
}
|
|
if (msg_id)
|
|
{
|
|
osg::Timer_t now(osg::Timer::instance()->tick());
|
|
if (osg::Timer::instance()->delta_s(_lastMsgTimeStamp, now) > 0.5) {
|
|
OSG_INFO << "OscReceiver :: resetting msg_id to 0 " << std::endl;
|
|
_lastMsgId = 0;
|
|
}
|
|
_lastMsgTimeStamp = now;
|
|
|
|
if (msg_id <= _lastMsgId) {
|
|
// already handled
|
|
// OSG_WARN << "OscReceiver :: message with lower id received: " << msg_id << std::endl;
|
|
return;
|
|
}
|
|
else {
|
|
if ((msg_id > _lastMsgId+1) && (_lastMsgId > 0)) {
|
|
OSG_WARN << "OscReceiver :: missed " << (msg_id - _lastMsgId) << " messages, (" << msg_id << "/" << _lastMsgId << ")" << std::endl;
|
|
}
|
|
_lastMsgId = msg_id;
|
|
}
|
|
}
|
|
|
|
|
|
for( osc::ReceivedBundle::const_iterator i = b.ElementsBegin(); i != b.ElementsEnd(); ++i ){
|
|
if( i->IsBundle() )
|
|
ProcessBundle( osc::ReceivedBundle(*i), remoteEndpoint );
|
|
else
|
|
{
|
|
ProcessMessage( osc::ReceivedMessage(*i), remoteEndpoint );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void OscReceivingDevice::ProcessPacket( const char *data, int size, const IpEndpointName& remoteEndpoint )
|
|
{
|
|
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));
|
|
_userDataEvent->setTime(getEventQueue()->getTime());
|
|
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;
|
|
}
|
|
|
|
}
|