summaryrefslogtreecommitdiffstats
path: root/src/plugins/generic/tuiotouch/qtuiohandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/generic/tuiotouch/qtuiohandler.cpp')
-rw-r--r--src/plugins/generic/tuiotouch/qtuiohandler.cpp257
1 files changed, 228 insertions, 29 deletions
diff --git a/src/plugins/generic/tuiotouch/qtuiohandler.cpp b/src/plugins/generic/tuiotouch/qtuiohandler.cpp
index 6026e06b55..86decd312b 100644
--- a/src/plugins/generic/tuiotouch/qtuiohandler.cpp
+++ b/src/plugins/generic/tuiotouch/qtuiohandler.cpp
@@ -43,10 +43,12 @@
#include <QWindow>
#include <QGuiApplication>
#include <QTouchDevice>
+#include <qmath.h>
#include <qpa/qwindowsysteminterface.h>
#include "qtuiocursor_p.h"
+#include "qtuiotoken_p.h"
#include "qtuiohandler_p.h"
#include "qoscbundle_p.h"
@@ -55,6 +57,12 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcTuioSource, "qt.qpa.tuio.source")
Q_LOGGING_CATEGORY(lcTuioSet, "qt.qpa.tuio.set")
+// With TUIO the first application takes exclusive ownership of the "device"
+// we cannot attach more than one application to the same port anyway.
+// Forcing delivery makes it easy to use simulators in the same machine
+// and forget about headaches about unfocused TUIO windows.
+static bool forceDelivery = qEnvironmentVariableIsSet("QT_TUIOTOUCH_DELIVER_WITHOUT_FOCUS");
+
QTuioHandler::QTuioHandler(const QString &specification)
: m_device(new QTouchDevice) // not leaked, QTouchDevice cleans up registered devices itself
{
@@ -157,29 +165,49 @@ void QTuioHandler::processPackets()
messages.push_back(msg);
}
- foreach (const QOscMessage &message, messages) {
- if (message.addressPattern() != "/tuio/2Dcur") {
- qWarning() << "Ignoring unknown address pattern " << message.addressPattern();
- continue;
- }
-
- QList<QVariant> arguments = message.arguments();
- if (arguments.count() == 0) {
- qWarning("Ignoring TUIO message with no arguments");
- continue;
- }
-
- QByteArray messageType = arguments.at(0).toByteArray();
- if (messageType == "source") {
- process2DCurSource(message);
- } else if (messageType == "alive") {
- process2DCurAlive(message);
- } else if (messageType == "set") {
- process2DCurSet(message);
- } else if (messageType == "fseq") {
- process2DCurFseq(message);
+ for (const QOscMessage &message : messages) {
+ if (message.addressPattern() == "/tuio/2Dcur") {
+ QList<QVariant> arguments = message.arguments();
+ if (arguments.count() == 0) {
+ qWarning("Ignoring TUIO message with no arguments");
+ continue;
+ }
+
+ QByteArray messageType = arguments.at(0).toByteArray();
+ if (messageType == "source") {
+ process2DCurSource(message);
+ } else if (messageType == "alive") {
+ process2DCurAlive(message);
+ } else if (messageType == "set") {
+ process2DCurSet(message);
+ } else if (messageType == "fseq") {
+ process2DCurFseq(message);
+ } else {
+ qWarning() << "Ignoring unknown TUIO message type: " << messageType;
+ continue;
+ }
+ } else if (message.addressPattern() == "/tuio/2Dobj") {
+ QList<QVariant> arguments = message.arguments();
+ if (arguments.count() == 0) {
+ qWarning("Ignoring TUIO message with no arguments");
+ continue;
+ }
+
+ QByteArray messageType = arguments.at(0).toByteArray();
+ if (messageType == "source") {
+ process2DObjSource(message);
+ } else if (messageType == "alive") {
+ process2DObjAlive(message);
+ } else if (messageType == "set") {
+ process2DObjSet(message);
+ } else if (messageType == "fseq") {
+ process2DObjFseq(message);
+ } else {
+ qWarning() << "Ignoring unknown TUIO message type: " << messageType;
+ continue;
+ }
} else {
- qWarning() << "Ignoring unknown TUIO message type: " << messageType;
+ qWarning() << "Ignoring unknown address pattern " << message.addressPattern();
continue;
}
}
@@ -328,11 +356,6 @@ void QTuioHandler::process2DCurFseq(const QOscMessage &message)
Q_UNUSED(message); // TODO: do we need to do anything with the frame id?
QWindow *win = QGuiApplication::focusWindow();
- // With TUIO the first application takes exclusive ownership of the "device"
- // we cannot attach more than one application to the same port anyway.
- // Forcing delivery makes it easy to use simulators in the same machine
- // and forget about headaches about unfocused TUIO windows.
- static bool forceDelivery = qEnvironmentVariableIsSet("QT_TUIOTOUCH_DELIVER_WITHOUT_FOCUS");
if (!win && QGuiApplication::topLevelWindows().length() > 0 && forceDelivery)
win = QGuiApplication::topLevelWindows().at(0);
@@ -342,12 +365,12 @@ void QTuioHandler::process2DCurFseq(const QOscMessage &message)
QList<QWindowSystemInterface::TouchPoint> tpl;
tpl.reserve(m_activeCursors.size() + m_deadCursors.size());
- foreach (const QTuioCursor &tc, m_activeCursors) {
+ for (const QTuioCursor &tc : m_activeCursors) {
QWindowSystemInterface::TouchPoint tp = cursorToTouchPoint(tc, win);
tpl.append(tp);
}
- foreach (const QTuioCursor &tc, m_deadCursors) {
+ for (const QTuioCursor &tc : m_deadCursors) {
QWindowSystemInterface::TouchPoint tp = cursorToTouchPoint(tc, win);
tp.state = Qt::TouchPointReleased;
tpl.append(tp);
@@ -357,5 +380,181 @@ void QTuioHandler::process2DCurFseq(const QOscMessage &message)
m_deadCursors.clear();
}
+void QTuioHandler::process2DObjSource(const QOscMessage &message)
+{
+ QList<QVariant> arguments = message.arguments();
+ if (arguments.count() != 2) {
+ qWarning() << "Ignoring malformed TUIO source message: " << arguments.count();
+ return;
+ }
+
+ if (QMetaType::Type(arguments.at(1).type()) != QMetaType::QByteArray) {
+ qWarning("Ignoring malformed TUIO source message (bad argument type)");
+ return;
+ }
+
+ qCDebug(lcTuioSource) << "Got TUIO source message from: " << arguments.at(1).toByteArray();
+}
+
+void QTuioHandler::process2DObjAlive(const QOscMessage &message)
+{
+ QList<QVariant> arguments = message.arguments();
+
+ // delta the notified tokens that are active, against the ones we already
+ // know of.
+ //
+ // TBD: right now we're assuming one 2DObj alive message corresponds to a
+ // new data source from the input. is this correct, or do we need to store
+ // changes and only process the deltas on fseq?
+ QMap<int, QTuioToken> oldActiveTokens = m_activeTokens;
+ QMap<int, QTuioToken> newActiveTokens;
+
+ for (int i = 1; i < arguments.count(); ++i) {
+ if (QMetaType::Type(arguments.at(i).type()) != QMetaType::Int) {
+ qWarning() << "Ignoring malformed TUIO alive message (bad argument on position" << i << arguments << ')';
+ return;
+ }
+
+ int sessionId = arguments.at(i).toInt();
+ if (!oldActiveTokens.contains(sessionId)) {
+ // newly active
+ QTuioToken token(sessionId);
+ token.setState(Qt::TouchPointPressed);
+ newActiveTokens.insert(sessionId, token);
+ } else {
+ // we already know about it, remove it so it isn't marked as released
+ QTuioToken token = oldActiveTokens.value(sessionId);
+ token.setState(Qt::TouchPointStationary); // position change in SET will update if needed
+ newActiveTokens.insert(sessionId, token);
+ oldActiveTokens.remove(sessionId);
+ }
+ }
+
+ // anything left is dead now
+ QMap<int, QTuioToken>::ConstIterator it = oldActiveTokens.constBegin();
+
+ // deadTokens should be cleared from the last FSEQ now
+ m_deadTokens.reserve(oldActiveTokens.size());
+
+ // TODO: there could be an issue of resource exhaustion here if FSEQ isn't
+ // sent in a timely fashion. we should probably track message counts and
+ // force-flush if we get too many built up.
+ while (it != oldActiveTokens.constEnd()) {
+ m_deadTokens.append(it.value());
+ ++it;
+ }
+
+ m_activeTokens = newActiveTokens;
+}
+
+void QTuioHandler::process2DObjSet(const QOscMessage &message)
+{
+ QList<QVariant> arguments = message.arguments();
+ if (arguments.count() < 7) {
+ qWarning() << "Ignoring malformed TUIO set message with too few arguments: " << arguments.count();
+ return;
+ }
+
+ if (QMetaType::Type(arguments.at(1).type()) != QMetaType::Int ||
+ QMetaType::Type(arguments.at(2).type()) != QMetaType::Int ||
+ QMetaType::Type(arguments.at(3).type()) != QMetaType::Float ||
+ QMetaType::Type(arguments.at(4).type()) != QMetaType::Float ||
+ QMetaType::Type(arguments.at(5).type()) != QMetaType::Float ||
+ QMetaType::Type(arguments.at(6).type()) != QMetaType::Float ||
+ QMetaType::Type(arguments.at(7).type()) != QMetaType::Float ||
+ QMetaType::Type(arguments.at(8).type()) != QMetaType::Float ||
+ QMetaType::Type(arguments.at(9).type()) != QMetaType::Float ||
+ QMetaType::Type(arguments.at(10).type()) != QMetaType::Float) {
+ qWarning() << "Ignoring malformed TUIO set message with bad types: " << arguments;
+ return;
+ }
+
+ int id = arguments.at(1).toInt();
+ int classId = arguments.at(2).toInt();
+ float x = arguments.at(3).toFloat();
+ float y = arguments.at(4).toFloat();
+ float angle = arguments.at(5).toFloat();
+ float vx = arguments.at(6).toFloat();
+ float vy = arguments.at(7).toFloat();
+ float angularVelocity = arguments.at(8).toFloat();
+ float acceleration = arguments.at(9).toFloat();
+ float angularAcceleration = arguments.at(10).toFloat();
+
+ QMap<int, QTuioToken>::Iterator it = m_activeTokens.find(id);
+ if (it == m_activeTokens.end()) {
+ qWarning() << "Ignoring malformed TUIO set for nonexistent token " << classId;
+ return;
+ }
+
+ qCDebug(lcTuioSet) << "Processing SET for token " << classId << id << " @ " << x << y << "∡" << angle <<
+ "vel" << vx << vy << angularVelocity << "acc" << acceleration << angularAcceleration;
+ QTuioToken &tok = *it;
+ tok.setClassId(classId);
+ tok.setX(x);
+ tok.setY(y);
+ tok.setVX(vx);
+ tok.setVY(vy);
+ tok.setAcceleration(acceleration);
+ tok.setAngle(angle);
+ tok.setAngularVelocity(angularAcceleration);
+ tok.setAngularAcceleration(angularAcceleration);
+}
+
+QWindowSystemInterface::TouchPoint QTuioHandler::tokenToTouchPoint(const QTuioToken &tc, QWindow *win)
+{
+ QWindowSystemInterface::TouchPoint tp;
+ tp.id = tc.id();
+ tp.uniqueId = tc.classId(); // TODO TUIO 2.0: populate a QVariant, and register the mapping from int to arbitrary UID data
+ tp.flags = QTouchEvent::TouchPoint::Token;
+ tp.pressure = 1.0f;
+
+ tp.normalPosition = QPointF(tc.x(), tc.y());
+
+ if (!m_transform.isIdentity())
+ tp.normalPosition = m_transform.map(tp.normalPosition);
+
+ tp.state = tc.state();
+ tp.area = QRectF(0, 0, 1, 1);
+
+ // We map the token position to the size of the window.
+ QPointF relPos = QPointF(win->size().width() * tp.normalPosition.x(), win->size().height() * tp.normalPosition.y());
+ QPointF delta = relPos - relPos.toPoint();
+ tp.area.moveCenter(win->mapToGlobal(relPos.toPoint()) + delta);
+ tp.velocity = QVector2D(win->size().width() * tc.vx(), win->size().height() * tc.vy());
+ tp.rotation = tc.angle() * 180.0 / M_PI; // convert radians to degrees
+ return tp;
+}
+
+
+void QTuioHandler::process2DObjFseq(const QOscMessage &message)
+{
+ Q_UNUSED(message); // TODO: do we need to do anything with the frame id?
+
+ QWindow *win = QGuiApplication::focusWindow();
+ if (!win && QGuiApplication::topLevelWindows().length() > 0 && forceDelivery)
+ win = QGuiApplication::topLevelWindows().at(0);
+
+ if (!win)
+ return;
+
+ QList<QWindowSystemInterface::TouchPoint> tpl;
+ tpl.reserve(m_activeTokens.size() + m_deadTokens.size());
+
+ for (const QTuioToken & t : m_activeTokens) {
+ QWindowSystemInterface::TouchPoint tp = tokenToTouchPoint(t, win);
+ tpl.append(tp);
+ }
+
+ for (const QTuioToken & t : m_deadTokens) {
+ QWindowSystemInterface::TouchPoint tp = tokenToTouchPoint(t, win);
+ tp.state = Qt::TouchPointReleased;
+ tp.velocity = QVector2D();
+ tpl.append(tp);
+ }
+ QWindowSystemInterface::handleTouchEvent(win, m_device, tpl);
+
+ m_deadTokens.clear();
+}
+
QT_END_NAMESPACE