Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qtuiohandler.cpp
Go to the documentation of this file.
1// Copyright (C) 2014 Robin Burchell <robin.burchell@viroteck.net>
2// Copyright (C) 2016 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qtuiohandler_p.h"
6
7#include "qtuiocursor_p.h"
8#include "qtuiotoken_p.h"
9#include "qoscbundle_p.h"
10#include "qoscmessage_p.h"
11
12#include <qpa/qwindowsysteminterface.h>
13
14#include <QPointingDevice>
15#include <QWindow>
16#include <QGuiApplication>
17
18#include <QLoggingCategory>
19#include <QRect>
20#include <qmath.h>
21
23
24Q_LOGGING_CATEGORY(lcTuioHandler, "qt.qpa.tuio.handler")
25Q_LOGGING_CATEGORY(lcTuioSource, "qt.qpa.tuio.source")
26Q_LOGGING_CATEGORY(lcTuioSet, "qt.qpa.tuio.set")
27
28// With TUIO the first application takes exclusive ownership of the "device"
29// we cannot attach more than one application to the same port anyway.
30// Forcing delivery makes it easy to use simulators in the same machine
31// and forget about headaches about unfocused TUIO windows.
32static bool forceDelivery = qEnvironmentVariableIsSet("QT_TUIOTOUCH_DELIVER_WITHOUT_FOCUS");
33
34QTuioHandler::QTuioHandler(const QString &specification)
35{
36 QStringList args = specification.split(':');
37 int portNumber = 3333;
38 int rotationAngle = 0;
39 bool invertx = false;
40 bool inverty = false;
41
42 for (int i = 0; i < args.size(); ++i) {
43 if (args.at(i).startsWith("udp=")) {
44 QString portString = args.at(i).section('=', 1, 1);
45 portNumber = portString.toInt();
46 } else if (args.at(i).startsWith("tcp=")) {
47 QString portString = args.at(i).section('=', 1, 1);
48 portNumber = portString.toInt();
49 qCWarning(lcTuioHandler) << "TCP is not yet supported. Falling back to UDP on " << portNumber;
50 } else if (args.at(i) == "invertx") {
51 invertx = true;
52 } else if (args.at(i) == "inverty") {
53 inverty = true;
54 } else if (args.at(i).startsWith("rotate=")) {
55 QString rotateArg = args.at(i).section('=', 1, 1);
56 int argValue = rotateArg.toInt();
57 switch (argValue) {
58 case 90:
59 case 180:
60 case 270:
61 rotationAngle = argValue;
62 default:
63 break;
64 }
65 }
66 }
67
68 if (rotationAngle)
69 m_transform = QTransform::fromTranslate(0.5, 0.5).rotate(rotationAngle).translate(-0.5, -0.5);
70
71 if (invertx)
72 m_transform *= QTransform::fromTranslate(0.5, 0.5).scale(-1.0, 1.0).translate(-0.5, -0.5);
73
74 if (inverty)
75 m_transform *= QTransform::fromTranslate(0.5, 0.5).scale(1.0, -1.0).translate(-0.5, -0.5);
76
77 // not leaked, QPointingDevice cleans up registered devices itself
78 // TODO register each device based on SOURCE, not just an all-purpose generic touchscreen
79 // TODO define seats when multiple connections occur
86 16, 0);
88
89 if (!m_socket.bind(QHostAddress::Any, portNumber)) {
90 qCWarning(lcTuioHandler) << "Failed to bind TUIO socket: " << m_socket.errorString();
91 return;
92 }
93
94 connect(&m_socket, &QUdpSocket::readyRead, this, &QTuioHandler::processPackets);
95}
96
98{
99}
100
101void QTuioHandler::processPackets()
102{
103 while (m_socket.hasPendingDatagrams()) {
104 QByteArray datagram;
105 datagram.resize(m_socket.pendingDatagramSize());
107 quint16 senderPort;
108
109 qint64 size = m_socket.readDatagram(datagram.data(), datagram.size(),
110 &sender, &senderPort);
111
112 if (size == -1)
113 continue;
114
115 if (size != datagram.size())
116 datagram.resize(size);
117
118 // "A typical TUIO bundle will contain an initial ALIVE message,
119 // followed by an arbitrary number of SET messages that can fit into the
120 // actual bundle capacity and a concluding FSEQ message. A minimal TUIO
121 // bundle needs to contain at least the compulsory ALIVE and FSEQ
122 // messages. The FSEQ frame ID is incremented for each delivered bundle,
123 // while redundant bundles can be marked using the frame sequence ID
124 // -1."
125 QList<QOscMessage> messages;
126
127 QOscBundle bundle(datagram);
128 if (bundle.isValid()) {
129 messages = bundle.messages();
130 } else {
131 QOscMessage msg(datagram);
132 if (!msg.isValid()) {
133 qCWarning(lcTuioSet) << "Got invalid datagram.";
134 continue;
135 }
136 messages.push_back(msg);
137 }
138
139 for (const QOscMessage &message : std::as_const(messages)) {
140 if (message.addressPattern() == "/tuio/2Dcur") {
141 QList<QVariant> arguments = message.arguments();
142 if (arguments.size() == 0) {
143 qCWarning(lcTuioHandler, "Ignoring TUIO message with no arguments");
144 continue;
145 }
146
147 QByteArray messageType = arguments.at(0).toByteArray();
148 if (messageType == "source") {
149 process2DCurSource(message);
150 } else if (messageType == "alive") {
151 process2DCurAlive(message);
152 } else if (messageType == "set") {
153 process2DCurSet(message);
154 } else if (messageType == "fseq") {
155 process2DCurFseq(message);
156 } else {
157 qCWarning(lcTuioHandler) << "Ignoring unknown TUIO message type: " << messageType;
158 continue;
159 }
160 } else if (message.addressPattern() == "/tuio/2Dobj") {
161 QList<QVariant> arguments = message.arguments();
162 if (arguments.size() == 0) {
163 qCWarning(lcTuioHandler, "Ignoring TUIO message with no arguments");
164 continue;
165 }
166
167 QByteArray messageType = arguments.at(0).toByteArray();
168 if (messageType == "source") {
169 process2DObjSource(message);
170 } else if (messageType == "alive") {
171 process2DObjAlive(message);
172 } else if (messageType == "set") {
173 process2DObjSet(message);
174 } else if (messageType == "fseq") {
175 process2DObjFseq(message);
176 } else {
177 qCWarning(lcTuioHandler) << "Ignoring unknown TUIO message type: " << messageType;
178 continue;
179 }
180 } else {
181 qCWarning(lcTuioHandler) << "Ignoring unknown address pattern " << message.addressPattern();
182 continue;
183 }
184 }
185 }
186}
187
188void QTuioHandler::process2DCurSource(const QOscMessage &message)
189{
190 QList<QVariant> arguments = message.arguments();
191 if (arguments.size() != 2) {
192 qCWarning(lcTuioSource) << "Ignoring malformed TUIO source message: " << arguments.size();
193 return;
194 }
195
196 if (QMetaType::Type(arguments.at(1).userType()) != QMetaType::QByteArray) {
197 qCWarning(lcTuioSource, "Ignoring malformed TUIO source message (bad argument type)");
198 return;
199 }
200
201 qCDebug(lcTuioSource) << "Got TUIO source message from: " << arguments.at(1).toByteArray();
202}
203
204void QTuioHandler::process2DCurAlive(const QOscMessage &message)
205{
206 QList<QVariant> arguments = message.arguments();
207
208 // delta the notified cursors that are active, against the ones we already
209 // know of.
210 //
211 // TBD: right now we're assuming one 2Dcur alive message corresponds to a
212 // new data source from the input. is this correct, or do we need to store
213 // changes and only process the deltas on fseq?
214 QMap<int, QTuioCursor> oldActiveCursors = m_activeCursors;
215 QMap<int, QTuioCursor> newActiveCursors;
216
217 for (int i = 1; i < arguments.size(); ++i) {
218 if (QMetaType::Type(arguments.at(i).userType()) != QMetaType::Int) {
219 qCWarning(lcTuioHandler) << "Ignoring malformed TUIO alive message (bad argument on position" << i << arguments << ')';
220 return;
221 }
222
223 int cursorId = arguments.at(i).toInt();
224 if (!oldActiveCursors.contains(cursorId)) {
225 // newly active
226 QTuioCursor cursor(cursorId);
228 newActiveCursors.insert(cursorId, cursor);
229 } else {
230 // we already know about it, remove it so it isn't marked as released
231 QTuioCursor cursor = oldActiveCursors.value(cursorId);
232 cursor.setState(QEventPoint::State::Stationary); // position change in SET will update if needed
233 newActiveCursors.insert(cursorId, cursor);
234 oldActiveCursors.remove(cursorId);
235 }
236 }
237
238 // anything left is dead now
240
241 // deadCursors should be cleared from the last FSEQ now
242 m_deadCursors.reserve(oldActiveCursors.size());
243
244 // TODO: there could be an issue of resource exhaustion here if FSEQ isn't
245 // sent in a timely fashion. we should probably track message counts and
246 // force-flush if we get too many built up.
247 while (it != oldActiveCursors.constEnd()) {
248 m_deadCursors.append(it.value());
249 ++it;
250 }
251
252 m_activeCursors = newActiveCursors;
253}
254
255void QTuioHandler::process2DCurSet(const QOscMessage &message)
256{
257 QList<QVariant> arguments = message.arguments();
258 if (arguments.size() < 7) {
259 qCWarning(lcTuioSet) << "Ignoring malformed TUIO set message with too few arguments: " << arguments.size();
260 return;
261 }
262
263 if (QMetaType::Type(arguments.at(1).userType()) != QMetaType::Int ||
264 QMetaType::Type(arguments.at(2).userType()) != QMetaType::Float ||
265 QMetaType::Type(arguments.at(3).userType()) != QMetaType::Float ||
266 QMetaType::Type(arguments.at(4).userType()) != QMetaType::Float ||
267 QMetaType::Type(arguments.at(5).userType()) != QMetaType::Float ||
268 QMetaType::Type(arguments.at(6).userType()) != QMetaType::Float
269 ) {
270 qCWarning(lcTuioSet) << "Ignoring malformed TUIO set message with bad types: " << arguments;
271 return;
272 }
273
274 int cursorId = arguments.at(1).toInt();
275 float x = arguments.at(2).toFloat();
276 float y = arguments.at(3).toFloat();
277 float vx = arguments.at(4).toFloat();
278 float vy = arguments.at(5).toFloat();
279 float acceleration = arguments.at(6).toFloat();
280
281 QMap<int, QTuioCursor>::Iterator it = m_activeCursors.find(cursorId);
282 if (it == m_activeCursors.end()) {
283 qCWarning(lcTuioSet) << "Ignoring malformed TUIO set for nonexistent cursor " << cursorId;
284 return;
285 }
286
287 qCDebug(lcTuioSet) << "Processing SET for " << cursorId << " x: " << x << y << vx << vy << acceleration;
288 QTuioCursor &cur = *it;
289 cur.setX(x);
290 cur.setY(y);
291 cur.setVX(vx);
292 cur.setVY(vy);
293 cur.setAcceleration(acceleration);
294}
295
296QWindowSystemInterface::TouchPoint QTuioHandler::cursorToTouchPoint(const QTuioCursor &tc, QWindow *win)
297{
299 tp.id = tc.id();
300 tp.pressure = 1.0f;
301
302 tp.normalPosition = QPointF(tc.x(), tc.y());
303
304 if (!m_transform.isIdentity())
305 tp.normalPosition = m_transform.map(tp.normalPosition);
306
307 tp.state = tc.state();
308
309 // we map the touch to the size of the window. we do this, because frankly,
310 // trying to figure out which part of the screen to hit in order to press an
311 // element on the UI is pretty tricky when one is not using an overlay-style
312 // TUIO device.
313 //
314 // in the future, it might make sense to make this choice optional,
315 // dependent on the spec.
316 QPointF relPos = QPointF(win->size().width() * tp.normalPosition.x(), win->size().height() * tp.normalPosition.y());
317 QPointF delta = relPos - relPos.toPoint();
318 tp.area.moveCenter(win->mapToGlobal(relPos.toPoint()) + delta);
319 tp.velocity = QVector2D(win->size().width() * tc.vx(), win->size().height() * tc.vy());
320 return tp;
321}
322
323
324void QTuioHandler::process2DCurFseq(const QOscMessage &message)
325{
326 Q_UNUSED(message); // TODO: do we need to do anything with the frame id?
327
331
332 if (!win)
333 return;
334
336 tpl.reserve(m_activeCursors.size() + m_deadCursors.size());
337
338 for (const QTuioCursor &tc : std::as_const(m_activeCursors)) {
339 QWindowSystemInterface::TouchPoint tp = cursorToTouchPoint(tc, win);
340 tpl.append(tp);
341 }
342
343 for (const QTuioCursor &tc : std::as_const(m_deadCursors)) {
344 QWindowSystemInterface::TouchPoint tp = cursorToTouchPoint(tc, win);
346 tpl.append(tp);
347 }
349
350 m_deadCursors.clear();
351}
352
353void QTuioHandler::process2DObjSource(const QOscMessage &message)
354{
355 QList<QVariant> arguments = message.arguments();
356 if (arguments.size() != 2) {
357 qCWarning(lcTuioSource ) << "Ignoring malformed TUIO source message: " << arguments.size();
358 return;
359 }
360
361 if (QMetaType::Type(arguments.at(1).userType()) != QMetaType::QByteArray) {
362 qCWarning(lcTuioSource, "Ignoring malformed TUIO source message (bad argument type)");
363 return;
364 }
365
366 qCDebug(lcTuioSource) << "Got TUIO source message from: " << arguments.at(1).toByteArray();
367}
368
369void QTuioHandler::process2DObjAlive(const QOscMessage &message)
370{
371 QList<QVariant> arguments = message.arguments();
372
373 // delta the notified tokens that are active, against the ones we already
374 // know of.
375 //
376 // TBD: right now we're assuming one 2DObj alive message corresponds to a
377 // new data source from the input. is this correct, or do we need to store
378 // changes and only process the deltas on fseq?
379 QMap<int, QTuioToken> oldActiveTokens = m_activeTokens;
380 QMap<int, QTuioToken> newActiveTokens;
381
382 for (int i = 1; i < arguments.size(); ++i) {
383 if (QMetaType::Type(arguments.at(i).userType()) != QMetaType::Int) {
384 qCWarning(lcTuioHandler) << "Ignoring malformed TUIO alive message (bad argument on position" << i << arguments << ')';
385 return;
386 }
387
388 int sessionId = arguments.at(i).toInt();
389 if (!oldActiveTokens.contains(sessionId)) {
390 // newly active
391 QTuioToken token(sessionId);
393 newActiveTokens.insert(sessionId, token);
394 } else {
395 // we already know about it, remove it so it isn't marked as released
396 QTuioToken token = oldActiveTokens.value(sessionId);
397 token.setState(QEventPoint::State::Stationary); // position change in SET will update if needed
398 newActiveTokens.insert(sessionId, token);
399 oldActiveTokens.remove(sessionId);
400 }
401 }
402
403 // anything left is dead now
405
406 // deadTokens should be cleared from the last FSEQ now
407 m_deadTokens.reserve(oldActiveTokens.size());
408
409 // TODO: there could be an issue of resource exhaustion here if FSEQ isn't
410 // sent in a timely fashion. we should probably track message counts and
411 // force-flush if we get too many built up.
412 while (it != oldActiveTokens.constEnd()) {
413 m_deadTokens.append(it.value());
414 ++it;
415 }
416
417 m_activeTokens = newActiveTokens;
418}
419
420void QTuioHandler::process2DObjSet(const QOscMessage &message)
421{
422 QList<QVariant> arguments = message.arguments();
423 if (arguments.size() < 7) {
424 qCWarning(lcTuioSet) << "Ignoring malformed TUIO set message with too few arguments: " << arguments.size();
425 return;
426 }
427
428 if (QMetaType::Type(arguments.at(1).userType()) != QMetaType::Int ||
429 QMetaType::Type(arguments.at(2).userType()) != QMetaType::Int ||
430 QMetaType::Type(arguments.at(3).userType()) != QMetaType::Float ||
431 QMetaType::Type(arguments.at(4).userType()) != QMetaType::Float ||
432 QMetaType::Type(arguments.at(5).userType()) != QMetaType::Float ||
433 QMetaType::Type(arguments.at(6).userType()) != QMetaType::Float ||
434 QMetaType::Type(arguments.at(7).userType()) != QMetaType::Float ||
435 QMetaType::Type(arguments.at(8).userType()) != QMetaType::Float ||
436 QMetaType::Type(arguments.at(9).userType()) != QMetaType::Float ||
437 QMetaType::Type(arguments.at(10).userType()) != QMetaType::Float) {
438 qCWarning(lcTuioSet) << "Ignoring malformed TUIO set message with bad types: " << arguments;
439 return;
440 }
441
442 int id = arguments.at(1).toInt();
443 int classId = arguments.at(2).toInt();
444 float x = arguments.at(3).toFloat();
445 float y = arguments.at(4).toFloat();
446 float angle = arguments.at(5).toFloat();
447 float vx = arguments.at(6).toFloat();
448 float vy = arguments.at(7).toFloat();
449 float angularVelocity = arguments.at(8).toFloat();
450 float acceleration = arguments.at(9).toFloat();
451 float angularAcceleration = arguments.at(10).toFloat();
452
453 QMap<int, QTuioToken>::Iterator it = m_activeTokens.find(id);
454 if (it == m_activeTokens.end()) {
455 qCWarning(lcTuioSet) << "Ignoring malformed TUIO set for nonexistent token " << classId;
456 return;
457 }
458
459 qCDebug(lcTuioSet) << "Processing SET for token " << classId << id << " @ " << x << y << " angle: " << angle <<
460 "vel" << vx << vy << angularVelocity << "acc" << acceleration << angularAcceleration;
461 QTuioToken &tok = *it;
462 tok.setClassId(classId);
463 tok.setX(x);
464 tok.setY(y);
465 tok.setVX(vx);
466 tok.setVY(vy);
467 tok.setAcceleration(acceleration);
468 tok.setAngle(angle);
469 tok.setAngularVelocity(angularAcceleration);
470 tok.setAngularAcceleration(angularAcceleration);
471}
472
473QWindowSystemInterface::TouchPoint QTuioHandler::tokenToTouchPoint(const QTuioToken &tc, QWindow *win)
474{
476 tp.id = tc.id();
477 tp.uniqueId = tc.classId(); // TODO TUIO 2.0: populate a QVariant, and register the mapping from int to arbitrary UID data
478 tp.pressure = 1.0f;
479
480 tp.normalPosition = QPointF(tc.x(), tc.y());
481
482 if (!m_transform.isIdentity())
483 tp.normalPosition = m_transform.map(tp.normalPosition);
484
485 tp.state = tc.state();
486
487 // We map the token position to the size of the window.
488 QPointF relPos = QPointF(win->size().width() * tp.normalPosition.x(), win->size().height() * tp.normalPosition.y());
489 QPointF delta = relPos - relPos.toPoint();
490 tp.area.moveCenter(win->mapToGlobal(relPos.toPoint()) + delta);
491 tp.velocity = QVector2D(win->size().width() * tc.vx(), win->size().height() * tc.vy());
492 tp.rotation = qRadiansToDegrees(tc.angle());
493 return tp;
494}
495
496
497void QTuioHandler::process2DObjFseq(const QOscMessage &message)
498{
499 Q_UNUSED(message); // TODO: do we need to do anything with the frame id?
500
504
505 if (!win)
506 return;
507
509 tpl.reserve(m_activeTokens.size() + m_deadTokens.size());
510
511 for (const QTuioToken & t : std::as_const(m_activeTokens)) {
512 QWindowSystemInterface::TouchPoint tp = tokenToTouchPoint(t, win);
513 tpl.append(tp);
514 }
515
516 for (const QTuioToken & t : std::as_const(m_deadTokens)) {
517 QWindowSystemInterface::TouchPoint tp = tokenToTouchPoint(t, win);
519 tp.velocity = QVector2D();
520 tpl.append(tp);
521 }
523
524 m_deadTokens.clear();
525}
526
528
529#include "moc_qtuiohandler_p.cpp"
530
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:534
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
void resize(qsizetype size)
Sets the size of the byte array to size bytes.
static QWindowList topLevelWindows()
Returns a list of the top-level windows in the application.
static QWindow * focusWindow()
Returns the QWindow that receives events tied to focus, such as key events.
The QHostAddress class provides an IP address.
void readyRead()
This signal is emitted once every time new data is available for reading from the device's current re...
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
void push_back(parameter_type t)
Definition qlist.h:672
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
void reserve(qsizetype size)
Definition qlist.h:746
void append(parameter_type t)
Definition qlist.h:441
void clear()
Definition qlist.h:417
Definition qmap.h:186
iterator insert(const Key &key, const T &value)
Definition qmap.h:687
T value(const Key &key, const T &defaultValue=T()) const
Definition qmap.h:356
bool contains(const Key &key) const
Definition qmap.h:340
size_type remove(const Key &key)
Definition qmap.h:299
iterator find(const Key &key)
Definition qmap.h:640
iterator end()
Definition qmap.h:601
const_iterator constBegin() const
Definition qmap.h:599
size_type size() const
Definition qmap.h:266
const_iterator constEnd() const
Definition qmap.h:603
Type
\macro Q_DECLARE_OPAQUE_POINTER(PointerType)
Definition qmetatype.h:324
QObject * sender() const
Returns a pointer to the object that sent the signal, if called in a slot activated by a signal; othe...
Definition qobject.cpp:2521
\inmodule QtCore\reentrant
Definition qpoint.h:214
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:333
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:338
constexpr QPoint toPoint() const
Rounds the coordinates of this point to the nearest integer, and returns a QPoint object with the rou...
Definition qpoint.h:394
The QPointingDevice class describes a device from which mouse, touch or tablet events originate.
constexpr void moveCenter(const QPointF &p) noexcept
Moves the rectangle, leaving the center point at the given position.
Definition qrect.h:712
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:132
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:129
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
int toInt(bool *ok=nullptr, int base=10) const
Returns the string converted to an int using base base, which is 10 by default and must be between 2 ...
Definition qstring.h:660
QTransform & rotate(qreal a, Qt::Axis axis=Qt::ZAxis, qreal distanceToPlane=1024.0f)
QTransform & scale(qreal sx, qreal sy)
Scales the coordinate system by sx horizontally and sy vertically, and returns a reference to the mat...
QPoint map(const QPoint &p) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
static QTransform fromTranslate(qreal dx, qreal dy)
Creates a matrix which corresponds to a translation of dx along the x axis and dy along the y axis.
QTransform & translate(qreal dx, qreal dy)
Moves the coordinate system dx along the x axis and dy along the y axis, and returns a reference to t...
bool isIdentity() const
Returns true if the matrix is the identity matrix, otherwise returns false.
Definition qtransform.h:169
void setX(float x)
void setVY(float vy)
void setVX(float vx)
void setAcceleration(float acceleration)
void setY(float y)
virtual ~QTuioHandler()
void setAngularAcceleration(float angularAcceleration)
void setX(float x)
void setAcceleration(float acceleration)
void setVY(float vy)
void setAngle(float angle)
void setClassId(int classId)
void setY(float y)
void setAngularVelocity(float angularVelocity)
void setVX(float vx)
bool hasPendingDatagrams() const
Returns true if at least one datagram is waiting to be read; otherwise returns false.
qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *host=nullptr, quint16 *port=nullptr)
Receives a datagram no larger than maxSize bytes and stores it in data.
qint64 pendingDatagramSize() const
Returns the size of the first pending UDP datagram.
int toInt(bool *ok=nullptr) const
Returns the variant as an int if the variant has userType() \l QMetaType::Int, \l QMetaType::Bool,...
int userType() const
Definition qvariant.h:336
float toFloat(bool *ok=nullptr) const
Returns the variant as a float if the variant has userType() \l QMetaType::Double,...
QByteArray toByteArray() const
Returns the variant as a QByteArray if the variant has userType() \l QMetaType::QByteArray or \l QMet...
The QVector2D class represents a vector or vertex in 2D space.
Definition qvectornd.h:31
QSize size
the size of the widget excluding any window frame
Definition qwidget.h:113
QPointF mapToGlobal(const QPointF &) const
Translates the widget coordinate pos to global screen coordinates.
static bool handleTouchEvent(QWindow *window, const QPointingDevice *device, const QList< struct TouchPoint > &points, Qt::KeyboardModifiers mods=Qt::NoModifier)
static void registerInputDevice(const QInputDevice *device)
\inmodule QtGui
Definition qwindow.h:63
QCursor cursor
QSet< QString >::iterator it
QList< QVariant > arguments
Token token
Definition keywords.cpp:444
Combined button and popup list for selecting options.
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
constexpr float qRadiansToDegrees(float radians)
Definition qmath.h:281
GLint GLint GLint GLint GLint x
[0]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLfloat angle
GLuint GLsizei const GLchar * message
GLint y
GLsizei GLsizei GLchar * source
const GLfloat * tc
GLdouble GLdouble t
Definition qopenglext.h:243
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept
#define Q_UNUSED(x)
static QT_BEGIN_NAMESPACE bool forceDelivery
unsigned short quint16
Definition qtypes.h:43
long long qint64
Definition qtypes.h:55
QWidget * win
Definition settings.cpp:6
QFuture< QSet< QChar > > set
[10]
QString bundle
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection)
QJSValueList args