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