Try to fix keys getting stuck when they aren't supposed to.
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
|
||||
pkgname=thunderpad-git
|
||||
_pkgname=thunderpad
|
||||
pkgver=r163.47f39ca
|
||||
pkgver=r164.66862ea
|
||||
pkgrel=1
|
||||
pkgdesc="Accessible joypad to keyboard and mouse mapper"
|
||||
arch=('aarch64' 'x86_64')
|
||||
|
||||
+24
-44
@@ -3,7 +3,6 @@
|
||||
#include "event.h"
|
||||
#include "time.h"
|
||||
#include <QRegularExpression>
|
||||
#include <QDateTime>
|
||||
|
||||
#define sqr(a) ((a)*(a))
|
||||
#define cub(a) ((a)*(a)*(a))
|
||||
@@ -22,8 +21,6 @@ Axis::Axis( int i, QObject *parent ) : QObject(parent) {
|
||||
interpretation = ZeroOne;
|
||||
gradient = false;
|
||||
absolute = false;
|
||||
lastKeyPressTime = 0;
|
||||
minKeyInterval = 100; // Default: 100ms minimum between key presses
|
||||
toDefault();
|
||||
tick = 0;
|
||||
connect(&sequenceRunner, &SequenceRunner::keyEvent, this, &Axis::compositeKeyEvent);
|
||||
@@ -301,52 +298,45 @@ void Axis::jsevent( int value ) {
|
||||
state = (value + JOYMIN) / 2;
|
||||
else
|
||||
state = (value + JOYMAX) / 2;
|
||||
const InputBinding &directionBinding = state >= 0 ? positiveBinding : negativeBinding;
|
||||
const bool compositeDirection = isOn ? compositeEngaged : directionBinding.isComposite();
|
||||
//set isOn, deal with state changing.
|
||||
bool stateChanged = false;
|
||||
const int newDirection = state > dZone ? 1 : state < -dZone ? -1 : 0;
|
||||
if (isOn && newDirection == activeDirection) {
|
||||
if (gradient && !compositeEngaged)
|
||||
duration = (abs(state) * FREQ) / JOYMAX;
|
||||
return;
|
||||
}
|
||||
|
||||
//if was on but now should be off:
|
||||
if (isOn && abs(state) <= dZone) {
|
||||
isOn = false;
|
||||
stateChanged = true;
|
||||
if (isOn) {
|
||||
if (compositeEngaged) {
|
||||
compositeEngaged = false;
|
||||
activeDirection = 0;
|
||||
if (newDirection != 0) sequenceRunner.cancel();
|
||||
}
|
||||
else if (isDown) {
|
||||
move(false);
|
||||
}
|
||||
else if (gradient) {
|
||||
duration = 0;
|
||||
release();
|
||||
timer.stop();
|
||||
disconnect(&timer, SIGNAL(timeout()), 0, 0);
|
||||
isOn = false;
|
||||
compositeEngaged = false;
|
||||
activeDirection = 0;
|
||||
tick = 0;
|
||||
duration = 0;
|
||||
}
|
||||
}
|
||||
//if was off but now should be on:
|
||||
else if (!isOn && abs(state) > dZone) {
|
||||
|
||||
if (newDirection == 0) return;
|
||||
|
||||
isOn = true;
|
||||
activeDirection = state >= 0 ? 1 : -1;
|
||||
stateChanged = true;
|
||||
if (compositeDirection) {
|
||||
activeDirection = newDirection;
|
||||
const InputBinding &directionBinding = newDirection > 0 ? positiveBinding : negativeBinding;
|
||||
if (directionBinding.isComposite()) {
|
||||
compositeEngaged = true;
|
||||
sequenceRunner.start(directionBinding);
|
||||
}
|
||||
else if (gradient) {
|
||||
duration = (abs(state) * FREQ) / JOYMAX;
|
||||
connect(&timer, SIGNAL(timeout()), this, SLOT(timerCalled()));
|
||||
connect(&timer, SIGNAL(timeout()), this, SLOT(timerCalled()), Qt::UniqueConnection);
|
||||
timer.start(MSEC);
|
||||
}
|
||||
}
|
||||
//if in gradient mode and state changed, update duration
|
||||
else if (gradient && !compositeDirection && abs(state) > dZone) {
|
||||
duration = (abs(state) * FREQ) / JOYMAX;
|
||||
}
|
||||
|
||||
//gradient will trigger movement on its own via timer().
|
||||
//non-gradient needs to be told to move only when state changes.
|
||||
if (!compositeDirection && !gradient && stateChanged) {
|
||||
move(isOn);
|
||||
if (!isOn) activeDirection = 0;
|
||||
else {
|
||||
move(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,7 +349,6 @@ void Axis::toDefault() {
|
||||
activeDirection = 0;
|
||||
tick = 0;
|
||||
duration = 0;
|
||||
lastKeyPressTime = 0;
|
||||
interpretation = ZeroOne;
|
||||
gradient = false;
|
||||
absolute = false;
|
||||
@@ -505,15 +494,6 @@ void Axis::move( bool press ) {
|
||||
//dialog being open and blocking events from happening.
|
||||
if (isDown == press) return;
|
||||
|
||||
// Key repeat filtering: prevent rapid-fire from controller hardware
|
||||
if (press && minKeyInterval > 0) {
|
||||
qint64 currentTime = QDateTime::currentMSecsSinceEpoch();
|
||||
if (currentTime - lastKeyPressTime < minKeyInterval) {
|
||||
return; // Too soon, ignore this key press
|
||||
}
|
||||
lastKeyPressTime = currentTime;
|
||||
}
|
||||
|
||||
isDown = press;
|
||||
if (press) {
|
||||
const int direction = activeDirection != 0
|
||||
|
||||
@@ -117,9 +117,6 @@ class Axis : public QObject {
|
||||
int duration;
|
||||
QTimer timer;
|
||||
|
||||
// Key repeat filtering to prevent controller spam
|
||||
qint64 lastKeyPressTime;
|
||||
int minKeyInterval; // milliseconds between key presses
|
||||
public slots:
|
||||
void timerCalled();
|
||||
};
|
||||
|
||||
+53
-4
@@ -5,6 +5,45 @@
|
||||
|
||||
//persistent X11 display connection to avoid opening/closing for each event
|
||||
static Display* g_display = nullptr;
|
||||
static SyntheticInputState g_inputState;
|
||||
|
||||
bool SyntheticInputState::updateCount(QHash<int, int> &counts, int code, bool press) {
|
||||
if (code <= 0) return false;
|
||||
if (press) {
|
||||
const int oldCount = counts.value(code);
|
||||
counts.insert(code, oldCount + 1);
|
||||
return oldCount == 0;
|
||||
}
|
||||
|
||||
const int oldCount = counts.value(code);
|
||||
if (oldCount <= 0) return false;
|
||||
if (oldCount == 1) {
|
||||
counts.remove(code);
|
||||
return true;
|
||||
}
|
||||
counts.insert(code, oldCount - 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SyntheticInputState::keyEvent(int keycode, bool press) {
|
||||
return updateCount(keyCounts, keycode, press);
|
||||
}
|
||||
|
||||
bool SyntheticInputState::mouseButtonEvent(int button, bool press) {
|
||||
return updateCount(mouseButtonCounts, button, press);
|
||||
}
|
||||
|
||||
QVector<int> SyntheticInputState::takePressedKeys() {
|
||||
const QVector<int> keys = keyCounts.keys().toVector();
|
||||
keyCounts.clear();
|
||||
return keys;
|
||||
}
|
||||
|
||||
QVector<int> SyntheticInputState::takePressedMouseButtons() {
|
||||
const QVector<int> buttons = mouseButtonCounts.keys().toVector();
|
||||
mouseButtonCounts.clear();
|
||||
return buttons;
|
||||
}
|
||||
|
||||
static Display* getDisplay() {
|
||||
if (!g_display) {
|
||||
@@ -21,6 +60,16 @@ void cleanupDisplay() {
|
||||
}
|
||||
}
|
||||
|
||||
void releaseAllSyntheticInputs() {
|
||||
Display* display = getDisplay();
|
||||
const QVector<int> keys = g_inputState.takePressedKeys();
|
||||
const QVector<int> buttons = g_inputState.takePressedMouseButtons();
|
||||
if (!display) return;
|
||||
for (int keycode : keys) XTestFakeKeyEvent(display, keycode, false, 0);
|
||||
for (int button : buttons) XTestFakeButtonEvent(display, button, false, 0);
|
||||
if (!keys.isEmpty() || !buttons.isEmpty()) XFlush(display);
|
||||
}
|
||||
|
||||
//actually creates an XWindows event :)
|
||||
void sendevent(const FakeEvent &e) {
|
||||
Display* display = getDisplay();
|
||||
@@ -46,22 +95,22 @@ void sendevent(const FakeEvent &e) {
|
||||
break;
|
||||
}
|
||||
case FakeEvent::KeyUp:
|
||||
if (e.keycode == 0) return;
|
||||
if (!g_inputState.keyEvent(e.keycode, false)) return;
|
||||
XTestFakeKeyEvent(display, e.keycode, false, 0);
|
||||
break;
|
||||
|
||||
case FakeEvent::KeyDown:
|
||||
if (e.keycode == 0) return;
|
||||
if (!g_inputState.keyEvent(e.keycode, true)) return;
|
||||
XTestFakeKeyEvent(display, e.keycode, true, 0);
|
||||
break;
|
||||
|
||||
case FakeEvent::MouseUp:
|
||||
if (e.keycode == 0) return;
|
||||
if (!g_inputState.mouseButtonEvent(e.keycode, false)) return;
|
||||
XTestFakeButtonEvent(display, e.keycode, false, 0);
|
||||
break;
|
||||
|
||||
case FakeEvent::MouseDown:
|
||||
if (e.keycode == 0) return;
|
||||
if (!g_inputState.mouseButtonEvent(e.keycode, true)) return;
|
||||
XTestFakeButtonEvent(display, e.keycode, true, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
+17
-2
@@ -1,8 +1,8 @@
|
||||
#ifndef THUNDERPAD_EVENT_H
|
||||
#define THUNDERPAD_EVENT_H
|
||||
|
||||
//for the functions we need to generate keypresses / mouse actions
|
||||
#include <X11/extensions/XTest.h>
|
||||
#include <QHash>
|
||||
#include <QVector>
|
||||
|
||||
//a simplified event structure that can handle buttons and mouse movements
|
||||
struct FakeEvent {
|
||||
@@ -21,7 +21,22 @@ struct FakeEvent {
|
||||
};
|
||||
};
|
||||
|
||||
class SyntheticInputState {
|
||||
public:
|
||||
bool keyEvent(int keycode, bool press);
|
||||
bool mouseButtonEvent(int button, bool press);
|
||||
QVector<int> takePressedKeys();
|
||||
QVector<int> takePressedMouseButtons();
|
||||
|
||||
private:
|
||||
bool updateCount(QHash<int, int> &counts, int code, bool press);
|
||||
|
||||
QHash<int, int> keyCounts;
|
||||
QHash<int, int> mouseButtonCounts;
|
||||
};
|
||||
|
||||
void sendevent(const FakeEvent& e);
|
||||
void releaseAllSyntheticInputs();
|
||||
void cleanupDisplay();
|
||||
|
||||
#endif
|
||||
|
||||
+21
-17
@@ -13,7 +13,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
JoyPad::JoyPad( int i, int dev, QObject *parent )
|
||||
: QObject(parent), joydev(-1), axisCount(0), buttonCount(0), jpw(0), readNotifier(0), errorNotifier(0) {
|
||||
: QObject(parent), joydev(-1), axisCount(0), buttonCount(0), jpw(0), readNotifier(0), errorNotifier(0), hasFocus(false) {
|
||||
debug_mesg("Constructing the joypad device with index %d and fd %d\n", i, dev);
|
||||
//remember the index,
|
||||
index = i;
|
||||
@@ -232,13 +232,19 @@ void JoyPad::jsevent(const js_event &msg) {
|
||||
jpw->jsevent(msg);
|
||||
return;
|
||||
}
|
||||
//if the dialog is open, stop here. We don't want to signal ourselves with
|
||||
//the input we generate.
|
||||
if (qApp->activeWindow() != 0 && qApp->activeModalWidget() != 0) return;
|
||||
unsigned int type = msg.type & ~JS_EVENT_INIT;
|
||||
//While a modal dialog is open, suppress new presses but continue to
|
||||
//process releases so an already held synthetic input cannot get stranded.
|
||||
if (qApp->activeWindow() != 0 && qApp->activeModalWidget() != 0) {
|
||||
if (type == JS_EVENT_AXIS && msg.number < axes.size() && axes[msg.number]->inDeadZone(msg.value))
|
||||
axes[msg.number]->jsevent(msg.value);
|
||||
else if (type == JS_EVENT_BUTTON && msg.number < buttons.size() && msg.value == 0)
|
||||
buttons[msg.number]->jsevent(msg.value);
|
||||
return;
|
||||
}
|
||||
|
||||
//otherwise, lets create us a fake event! Pass on the event to whichever
|
||||
//Button or Axis was pressed and let them decide what to do with it.
|
||||
unsigned int type = msg.type & ~JS_EVENT_INIT;
|
||||
if (type == JS_EVENT_AXIS) {
|
||||
debug_mesg("DEBUG: passing on an axis event\n");
|
||||
debug_mesg("DEBUG: %d %d\n", msg.number, msg.value);
|
||||
@@ -260,24 +266,22 @@ JoyPadWidget* JoyPad::widget( QWidget* parent, int i) {
|
||||
}
|
||||
|
||||
void JoyPad::handleJoyEvents() {
|
||||
for (;;) {
|
||||
js_event msg;
|
||||
ssize_t len = read(joydev, &msg, sizeof(js_event));
|
||||
//if there was a real event waiting,
|
||||
const ssize_t len = read(joydev, &msg, sizeof(js_event));
|
||||
if (len == sizeof(js_event)) {
|
||||
//pass that event on to the joypad!
|
||||
jsevent(msg);
|
||||
} else if (len < 0) {
|
||||
//handle read errors
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
//normal for non-blocking read when no data available
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
//device error - trigger error handling
|
||||
if (len < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) return;
|
||||
if (len < 0) {
|
||||
debug_mesg("Read error on joystick device fd %d: %s\n", joydev, strerror(errno));
|
||||
}
|
||||
else {
|
||||
debug_mesg("Unexpected read length %zd on joystick device fd %d\n", len, joydev);
|
||||
}
|
||||
errorRead();
|
||||
} else if (len > 0) {
|
||||
//partial read - should not happen with joystick events
|
||||
debug_mesg("Warning: partial read (%zd bytes) from joystick device fd %d\n", len, joydev);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-5
@@ -189,13 +189,10 @@ void LayoutEdit::updateJoypadWidgets() {
|
||||
void LayoutEdit::appFocusChanged(QWidget *old, QWidget *now) {
|
||||
if (now != NULL && old == NULL) {
|
||||
emit focusStateChanged(false);
|
||||
lm->release();
|
||||
} else if(old != NULL && now == NULL) {
|
||||
emit focusStateChanged(true);
|
||||
foreach (JoyPad *joypad, lm->available) {
|
||||
debug_mesg("iterating and releasing\n");
|
||||
joypad->release();
|
||||
}
|
||||
debug_mesg("done releasing!\n");
|
||||
lm->release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+33
-20
@@ -4,12 +4,15 @@
|
||||
//to create and handle signals for various events
|
||||
#include <signal.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
//to create a qapplication
|
||||
#include <QFile>
|
||||
#include <QSystemTrayIcon>
|
||||
#include <QPointer>
|
||||
#include <QFileInfo>
|
||||
#include <QSocketNotifier>
|
||||
|
||||
//to load layouts
|
||||
#include "layout.h"
|
||||
@@ -21,25 +24,13 @@
|
||||
|
||||
//variables needed in various functions in this file
|
||||
QPointer<LayoutManager> layoutManagerPtr;
|
||||
static int signalSockets[2] = {-1, -1};
|
||||
|
||||
//signal handler for SIGUSR2
|
||||
//SIGUSR2 means that a new layout should be loaded. It is saved in
|
||||
// ~/.config/thunderpad/layout, where the last used layout is put.
|
||||
void catchSIGUSR2( int sig ) {
|
||||
if (layoutManagerPtr) layoutManagerPtr->load();
|
||||
//remember to catch this signal again next time.
|
||||
signal( sig, catchSIGUSR2 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
//signal handler for SIGUSR1
|
||||
//SIGUSR1 means that we should update the available joystick device list.
|
||||
void catchSIGUSR1( int sig ) {
|
||||
//buildJoyDevices();
|
||||
if (layoutManagerPtr) layoutManagerPtr->updateJoyDevs();
|
||||
//remember to catch this signal again next time.
|
||||
signal( sig, catchSIGUSR1 );
|
||||
void catchSignal(int sig) {
|
||||
const int savedErrno = errno;
|
||||
const unsigned char signalNumber = static_cast<unsigned char>(sig);
|
||||
if (signalSockets[0] >= 0) write(signalSockets[0], &signalNumber, sizeof(signalNumber));
|
||||
errno = savedErrno;
|
||||
}
|
||||
|
||||
|
||||
@@ -245,6 +236,20 @@ int main( int argc, char **argv )
|
||||
LayoutManager layoutManager(useTrayIcon,devdir,settingsDir,headless);
|
||||
layoutManagerPtr = &layoutManager;
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, signalSockets) != 0) {
|
||||
perror("socketpair");
|
||||
return 1;
|
||||
}
|
||||
QSocketNotifier signalNotifier(signalSockets[1], QSocketNotifier::Read);
|
||||
QObject::connect(&signalNotifier, &QSocketNotifier::activated, &app, [&]() {
|
||||
unsigned char signalNumber;
|
||||
while (read(signalSockets[1], &signalNumber, sizeof(signalNumber)) == sizeof(signalNumber)) {
|
||||
if (signalNumber == SIGUSR1) layoutManager.updateJoyDevs();
|
||||
else if (signalNumber == SIGUSR2) layoutManager.load();
|
||||
else if (signalNumber == SIGINT || signalNumber == SIGTERM) app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
//build the joystick device list for the first time,
|
||||
//buildJoyDevices();
|
||||
layoutManager.updateJoyDevs();
|
||||
@@ -253,8 +258,10 @@ int main( int argc, char **argv )
|
||||
layoutManager.load();
|
||||
|
||||
//prepare the signal handlers
|
||||
signal( SIGUSR1, catchSIGUSR1 );
|
||||
signal( SIGUSR2, catchSIGUSR2 );
|
||||
signal(SIGUSR1, catchSignal);
|
||||
signal(SIGUSR2, catchSignal);
|
||||
signal(SIGINT, catchSignal);
|
||||
signal(SIGTERM, catchSignal);
|
||||
|
||||
//and run the program!
|
||||
int result = app.exec();
|
||||
@@ -267,6 +274,12 @@ int main( int argc, char **argv )
|
||||
|
||||
//Release every synthetic input and stop binding timers before closing X11.
|
||||
layoutManager.release();
|
||||
releaseAllSyntheticInputs();
|
||||
|
||||
close(signalSockets[0]);
|
||||
close(signalSockets[1]);
|
||||
signalSockets[0] = -1;
|
||||
signalSockets[1] = -1;
|
||||
|
||||
//cleanup X11 display connection
|
||||
cleanupDisplay();
|
||||
|
||||
+53
-8
@@ -2,6 +2,7 @@
|
||||
#include "bindingdialog.h"
|
||||
#include "button.h"
|
||||
#include "inputbinding.h"
|
||||
#include "event.h"
|
||||
|
||||
#include <QElapsedTimer>
|
||||
#include <QApplication>
|
||||
@@ -10,6 +11,8 @@
|
||||
#include <QTest>
|
||||
#include <QTextStream>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
class RecorderProbe : public BindingDialog {
|
||||
public:
|
||||
RecorderProbe() : BindingDialog("Test control", true) {}
|
||||
@@ -68,6 +71,7 @@ public:
|
||||
QVector<QPair<int, bool>> compositeEvents;
|
||||
bool currentBindingUsesMouse() const { return useMouse; }
|
||||
bool bindingIsDown() const { return isDown; }
|
||||
int pressedKey() const { return downkey; }
|
||||
protected:
|
||||
void compositeKeyEvent(int keycode, bool press) override {
|
||||
compositeEvents.append(qMakePair(keycode, press));
|
||||
@@ -85,9 +89,11 @@ private slots:
|
||||
void recorderModifiersRepeatLimitAndFinish();
|
||||
void runnerOrderTimingRetriggerAndCancel();
|
||||
void preservePlainButtonModes();
|
||||
void axisCompositeRearmsWithoutRepeating();
|
||||
void axisCompositeChangesDirectionWithoutRepeating();
|
||||
void axisCompositeRearmsWithZeroDeadzone();
|
||||
void axisGradientReleaseKeepsActiveDirection();
|
||||
void axisChangesDirectionWithoutNeutral();
|
||||
void syntheticInputReferenceCounts();
|
||||
};
|
||||
|
||||
void BindingTests::parseAndSerializeSequences() {
|
||||
@@ -324,7 +330,7 @@ void BindingTests::preservePlainButtonModes() {
|
||||
QCOMPARE(cancelling.compositeEvents, cancelled);
|
||||
}
|
||||
|
||||
void BindingTests::axisCompositeRearmsWithoutRepeating() {
|
||||
void BindingTests::axisCompositeChangesDirectionWithoutRepeating() {
|
||||
AxisProbe axis;
|
||||
QString input("Gradient, +sequence 50 10+11, -sequence 50 12+13");
|
||||
QTextStream reader(&input);
|
||||
@@ -335,17 +341,18 @@ void BindingTests::axisCompositeRearmsWithoutRepeating() {
|
||||
QTest::qWait(80);
|
||||
QCOMPARE(axis.compositeEvents.size(), 4);
|
||||
axis.jsevent(-25000);
|
||||
QTest::qWait(80);
|
||||
QCOMPARE(axis.compositeEvents.size(), 4);
|
||||
QTRY_COMPARE_WITH_TIMEOUT(axis.compositeEvents.size(), 8, 150);
|
||||
QCOMPARE(axis.compositeEvents.at(4), qMakePair(12, true));
|
||||
QCOMPARE(axis.compositeEvents.at(5), qMakePair(13, true));
|
||||
axis.jsevent(0);
|
||||
axis.jsevent(20000);
|
||||
QTRY_COMPARE_WITH_TIMEOUT(axis.compositeEvents.size(), 8, 150);
|
||||
QTRY_COMPARE_WITH_TIMEOUT(axis.compositeEvents.size(), 12, 150);
|
||||
|
||||
axis.jsevent(0);
|
||||
axis.jsevent(-20000);
|
||||
QTRY_COMPARE_WITH_TIMEOUT(axis.compositeEvents.size(), 12, 150);
|
||||
QCOMPARE(axis.compositeEvents.at(8), qMakePair(12, true));
|
||||
QCOMPARE(axis.compositeEvents.at(9), qMakePair(13, true));
|
||||
QTRY_COMPARE_WITH_TIMEOUT(axis.compositeEvents.size(), 16, 150);
|
||||
QCOMPARE(axis.compositeEvents.at(12), qMakePair(12, true));
|
||||
QCOMPARE(axis.compositeEvents.at(13), qMakePair(13, true));
|
||||
}
|
||||
|
||||
void BindingTests::axisCompositeRearmsWithZeroDeadzone() {
|
||||
@@ -380,5 +387,43 @@ void BindingTests::axisGradientReleaseKeepsActiveDirection() {
|
||||
QVERIFY(axis.currentBindingUsesMouse());
|
||||
}
|
||||
|
||||
void BindingTests::axisChangesDirectionWithoutNeutral() {
|
||||
AxisProbe axis;
|
||||
QString input("ZeroOne, +key 116, -key 111");
|
||||
QTextStream reader(&input);
|
||||
QVERIFY(axis.read(reader));
|
||||
|
||||
axis.jsevent(-32767);
|
||||
QVERIFY(axis.bindingIsDown());
|
||||
QCOMPARE(axis.pressedKey(), 111);
|
||||
|
||||
axis.jsevent(32767);
|
||||
QVERIFY(axis.bindingIsDown());
|
||||
QCOMPARE(axis.pressedKey(), 116);
|
||||
|
||||
axis.jsevent(0);
|
||||
QVERIFY(!axis.bindingIsDown());
|
||||
}
|
||||
|
||||
void BindingTests::syntheticInputReferenceCounts() {
|
||||
SyntheticInputState state;
|
||||
QVERIFY(state.keyEvent(111, true));
|
||||
QVERIFY(!state.keyEvent(111, true));
|
||||
QVERIFY(!state.keyEvent(111, false));
|
||||
QVERIFY(state.keyEvent(111, false));
|
||||
QVERIFY(!state.keyEvent(111, false));
|
||||
|
||||
QVERIFY(state.mouseButtonEvent(1, true));
|
||||
QVERIFY(!state.mouseButtonEvent(1, true));
|
||||
QCOMPARE(state.takePressedMouseButtons(), QVector<int>({1}));
|
||||
QVERIFY(!state.mouseButtonEvent(1, false));
|
||||
|
||||
QVERIFY(state.keyEvent(38, true));
|
||||
QVERIFY(state.keyEvent(39, true));
|
||||
QVector<int> keys = state.takePressedKeys();
|
||||
std::sort(keys.begin(), keys.end());
|
||||
QCOMPARE(keys, QVector<int>({38, 39}));
|
||||
}
|
||||
|
||||
QTEST_MAIN(BindingTests)
|
||||
#include "test_bindings.moc"
|
||||
|
||||
Reference in New Issue
Block a user