summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2020-06-26 22:48:02 +0000
committerGitHub <noreply@github.com>2020-06-26 22:48:02 +0000
commit4b858387c31f3d2778ff86789b4271d67343a7e3 (patch)
treeeef1f4a1da941012f9dcfe06e3977a10414c896e
parent6d9e24c5b98f5b2cce01fa4d99255bbeff8ee2f1 (diff)
parentbd7ce1df885dba290111cc9317adf951a90519c1 (diff)
Merge pull request #1107 from russell/linux_joystick
Add Linux Joystick Support
-rw-r--r--tracker-linux-joystick/CMakeLists.txt4
-rw-r--r--tracker-linux-joystick/ftnoir_tracker_linux_joystick.cpp87
-rw-r--r--tracker-linux-joystick/ftnoir_tracker_linux_joystick.h90
-rw-r--r--tracker-linux-joystick/ftnoir_tracker_linux_joystick_controls.ui492
-rw-r--r--tracker-linux-joystick/ftnoir_tracker_linux_joystick_dialog.cpp40
-rw-r--r--tracker-linux-joystick/lang/nl_NL.ts86
-rw-r--r--tracker-linux-joystick/lang/ru_RU.ts86
-rw-r--r--tracker-linux-joystick/lang/stub.ts86
-rw-r--r--tracker-linux-joystick/lang/zh_CN.ts86
-rw-r--r--tracker-linux-joystick/linux_joystick.cpp65
10 files changed, 1122 insertions, 0 deletions
diff --git a/tracker-linux-joystick/CMakeLists.txt b/tracker-linux-joystick/CMakeLists.txt
new file mode 100644
index 00000000..4e821b01
--- /dev/null
+++ b/tracker-linux-joystick/CMakeLists.txt
@@ -0,0 +1,4 @@
+if(LINUX)
+ otr_module(tracker-linux-joystick)
+ target_link_libraries(opentrack-tracker-linux-joystick)
+endif()
diff --git a/tracker-linux-joystick/ftnoir_tracker_linux_joystick.cpp b/tracker-linux-joystick/ftnoir_tracker_linux_joystick.cpp
new file mode 100644
index 00000000..8fa600e7
--- /dev/null
+++ b/tracker-linux-joystick/ftnoir_tracker_linux_joystick.cpp
@@ -0,0 +1,87 @@
+/* Copyright (c) 2013 Stanislaw Halik <sthalik@misaki.pl>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ */
+#include "ftnoir_tracker_linux_joystick.h"
+#include "api/plugin-api.hpp"
+#include "compat/math.hpp"
+#include <QMutexLocker>
+
+joystick::joystick()
+{
+ QString device = getJoystickDevice(s.guid);
+ joy_fd = open(device.toUtf8().data(), O_RDONLY | O_NONBLOCK);
+}
+
+
+joystick::~joystick() {
+ if (joy_fd > 0) close(joy_fd);
+}
+
+module_status joystick::start_tracker(QFrame *)
+{
+ if (joy_fd == -1) return error("Couldn't open joystick");
+ return status_ok();
+}
+
+
+void joystick::data(double *data)
+{
+ int map[6] = {
+ s.joy_1 - 1,
+ s.joy_2 - 1,
+ s.joy_3 - 1,
+ s.joy_4 - 1,
+ s.joy_5 - 1,
+ s.joy_6 - 1,
+ };
+
+ const double limits[] = {
+ 100,
+ 100,
+ 100,
+ 180,
+ 180,
+ 180
+ };
+
+ const QString guid = s.guid;
+ int axes[8];
+ struct js_event event;
+ bool ret = true;
+ if (read(joy_fd, &event, sizeof(event)) > 0)
+ {
+ switch (event.type)
+ {
+ case JS_EVENT_AXIS:
+ if (event.number >= 8) break;
+ axes_state[event.number] = event.value;
+
+ break;
+ default:
+ /* Ignore init/button events. */
+ break;
+ }
+ }
+
+ for (int i = 0; i < 6; i++)
+ {
+ axes[i] = axes_state[i];
+ }
+ if (ret)
+ {
+ for (int i = 0; i < 6; i++)
+ {
+ int k = map[i];
+ if (k < 0 || k >= 8)
+ data[i] = 0;
+ else
+ data[i] = clamp(axes[k] * limits[i] / AXIS_MAX,
+ -limits[i], limits[i]);
+ }
+ }
+}
+
+OPENTRACK_DECLARE_TRACKER(joystick, dialog_joystick, joystickDll)
diff --git a/tracker-linux-joystick/ftnoir_tracker_linux_joystick.h b/tracker-linux-joystick/ftnoir_tracker_linux_joystick.h
new file mode 100644
index 00000000..6ddc4909
--- /dev/null
+++ b/tracker-linux-joystick/ftnoir_tracker_linux_joystick.h
@@ -0,0 +1,90 @@
+/* Copyright (c) 2013 Stanislaw Halik <sthalik@misaki.pl>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ */
+#pragma once
+#include "ui_ftnoir_tracker_linux_joystick_controls.h"
+#include <QComboBox>
+#include <QCheckBox>
+#include <QSpinBox>
+#include <QMessageBox>
+#include <QSettings>
+#include <QList>
+#include <QFrame>
+#include <QStringList>
+#include <cmath>
+#include "api/plugin-api.hpp"
+
+#include <linux/joystick.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "options/options.hpp"
+using namespace options;
+
+struct settings : opts {
+ value<QString> guid;
+ value<int> joy_1, joy_2, joy_3, joy_4, joy_5, joy_6;
+ settings() :
+ opts("tracker-linux-joystick"),
+ guid(b, "joy-guid", ""),
+ joy_1(b, "axis-map-1", 1),
+ joy_2(b, "axis-map-2", 2),
+ joy_3(b, "axis-map-3", 3),
+ joy_4(b, "axis-map-4", 4),
+ joy_5(b, "axis-map-5", 5),
+ joy_6(b, "axis-map-6", 6)
+ {}
+};
+
+struct linux_joystick {
+ QString name;
+ QString device_id;
+ QString dev;
+};
+QList<linux_joystick> getJoysticks();
+QString getJoystickDevice(QString guid);
+
+class joystick : public ITracker
+{
+public:
+ joystick();
+ ~joystick();
+ module_status start_tracker(QFrame *);
+ void data(double *data);
+ settings s;
+ QString guid;
+ static constexpr int AXIS_MAX = USHRT_MAX;
+ int axes_state[6] = {0};
+ int joy_fd;
+};
+
+class dialog_joystick: public ITrackerDialog
+{
+ Q_OBJECT
+public:
+ dialog_joystick();
+ void register_tracker(ITracker *) {}
+ void unregister_tracker() {}
+ Ui::UILinuxJoystickControls ui;
+ joystick* tracker;
+ settings s;
+ struct joys {
+ QString name;
+ QString guid;
+ };
+ QList<joys> joys_;
+private slots:
+ void doOK();
+ void doCancel();
+};
+
+class joystickDll : public Metadata
+{
+ Q_OBJECT
+
+ QString name() { return tr("Linux Joystick input"); }
+ QIcon icon() { return QIcon(":/images/opentrack.png"); }
+};
diff --git a/tracker-linux-joystick/ftnoir_tracker_linux_joystick_controls.ui b/tracker-linux-joystick/ftnoir_tracker_linux_joystick_controls.ui
new file mode 100644
index 00000000..2a54c74a
--- /dev/null
+++ b/tracker-linux-joystick/ftnoir_tracker_linux_joystick_controls.ui
@@ -0,0 +1,492 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UILinuxJoystickControls</class>
+ <widget class="QWidget" name="UILinuxJoystickControls">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>498</width>
+ <height>334</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Tracker settings</string>
+ </property>
+ <property name="windowIcon">
+ <iconset>
+ <normaloff>../gui/images/opentrack.png</normaloff>../gui/images/opentrack.png</iconset>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="leftMargin">
+ <number>12</number>
+ </property>
+ <property name="topMargin">
+ <number>6</number>
+ </property>
+ <property name="rightMargin">
+ <number>12</number>
+ </property>
+ <property name="bottomMargin">
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QFrame" name="frame">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Device</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="joylist">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Mapping</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="1">
+ <widget class="QComboBox" name="joy_1">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="currentIndex">
+ <number>1</number>
+ </property>
+ <item>
+ <property name="text">
+ <string>Disabled</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #1</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #2</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #3</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #4</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #5</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #6</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #7</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #8</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="joy_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="currentIndex">
+ <number>2</number>
+ </property>
+ <item>
+ <property name="text">
+ <string>Disabled</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #1</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #2</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #3</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #4</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #5</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #6</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #7</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #8</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QComboBox" name="joy_3">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="currentIndex">
+ <number>3</number>
+ </property>
+ <item>
+ <property name="text">
+ <string>Disabled</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #1</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #2</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #3</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #4</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #5</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #6</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #7</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #8</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QComboBox" name="joy_4">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="currentIndex">
+ <number>4</number>
+ </property>
+ <item>
+ <property name="text">
+ <string>Disabled</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #1</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #2</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #3</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #4</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #5</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #6</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #7</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #8</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QComboBox" name="joy_5">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="currentIndex">
+ <number>5</number>
+ </property>
+ <item>
+ <property name="text">
+ <string>Disabled</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #1</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #2</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #3</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #4</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #5</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #6</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #7</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #8</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QComboBox" name="joy_6">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="currentIndex">
+ <number>6</number>
+ </property>
+ <item>
+ <property name="text">
+ <string>Disabled</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #1</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #2</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #3</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #4</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #5</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #6</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #7</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Joystick axis #8</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>X</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>Y</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>Z</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Yaw</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Pitch</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Roll</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+ <slots>
+ <slot>startEngineClicked()</slot>
+ <slot>stopEngineClicked()</slot>
+ <slot>cameraSettingsClicked()</slot>
+ </slots>
+</ui>
diff --git a/tracker-linux-joystick/ftnoir_tracker_linux_joystick_dialog.cpp b/tracker-linux-joystick/ftnoir_tracker_linux_joystick_dialog.cpp
new file mode 100644
index 00000000..1cf75bc1
--- /dev/null
+++ b/tracker-linux-joystick/ftnoir_tracker_linux_joystick_dialog.cpp
@@ -0,0 +1,40 @@
+#include "ftnoir_tracker_linux_joystick.h"
+#include "api/plugin-api.hpp"
+
+dialog_joystick::dialog_joystick() : tracker(nullptr)
+{
+ ui.setupUi( this );
+
+ // Connect Qt signals to member-functions
+ connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(doOK()));
+ connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(doCancel()));
+
+ QList<::linux_joystick> joysticks = getJoysticks();
+
+ for (int i = 0; i < joysticks.size(); i++) {
+ ::linux_joystick joy = joysticks[i];
+ joys_.push_back(joys { joy.name, joy.device_id});
+ ui.joylist->addItem(QString("%1 | %2").arg(joy.dev).arg(joy.name));
+ if (joysticks[i].device_id == s.guid) ui.joylist->setCurrentIndex(i);
+ }
+
+ tie_setting(s.joy_1, ui.joy_1);
+ tie_setting(s.joy_2, ui.joy_2);
+ tie_setting(s.joy_3, ui.joy_3);
+ tie_setting(s.joy_4, ui.joy_4);
+ tie_setting(s.joy_5, ui.joy_5);
+ tie_setting(s.joy_6, ui.joy_6);
+}
+
+void dialog_joystick::doOK() {
+ int idx = ui.joylist->currentIndex();
+ static const joys def { {}, {} };
+ auto val = joys_.value(idx, def);
+ s.guid = val.guid;
+ s.b->save();
+ close();
+}
+
+void dialog_joystick::doCancel() {
+ close();
+}
diff --git a/tracker-linux-joystick/lang/nl_NL.ts b/tracker-linux-joystick/lang/nl_NL.ts
new file mode 100644
index 00000000..1c9b89d0
--- /dev/null
+++ b/tracker-linux-joystick/lang/nl_NL.ts
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="nl_NL">
+<context>
+ <name>UILinuxJoystickControls</name>
+ <message>
+ <source>Tracker settings</source>
+ <translation type="unfinished">Tracker-instellingen</translation>
+ </message>
+ <message>
+ <source>Device</source>
+ <translation type="unfinished">Apparaat</translation>
+ </message>
+ <message>
+ <source>Mapping</source>
+ <translation type="unfinished">Verwijzing</translation>
+ </message>
+ <message>
+ <source>Disabled</source>
+ <translation type="unfinished">Uitgeschakeld</translation>
+ </message>
+ <message>
+ <source>Joystick axis #1</source>
+ <translation type="unfinished">Joystick-as #1</translation>
+ </message>
+ <message>
+ <source>Joystick axis #2</source>
+ <translation type="unfinished">Joystick-as #2</translation>
+ </message>
+ <message>
+ <source>Joystick axis #3</source>
+ <translation type="unfinished">Joystick-as #3</translation>
+ </message>
+ <message>
+ <source>Joystick axis #4</source>
+ <translation type="unfinished">Joystick-as #4</translation>
+ </message>
+ <message>
+ <source>Joystick axis #5</source>
+ <translation type="unfinished">Joystick-as #5</translation>
+ </message>
+ <message>
+ <source>Joystick axis #6</source>
+ <translation type="unfinished">Joystick-as #6</translation>
+ </message>
+ <message>
+ <source>Joystick axis #7</source>
+ <translation type="unfinished">Joystick-as #7</translation>
+ </message>
+ <message>
+ <source>Joystick axis #8</source>
+ <translation type="unfinished">Joystick-as #8</translation>
+ </message>
+ <message>
+ <source>X</source>
+ <translation type="unfinished">X</translation>
+ </message>
+ <message>
+ <source>Y</source>
+ <translation type="unfinished">Y</translation>
+ </message>
+ <message>
+ <source>Z</source>
+ <translation type="unfinished">Z</translation>
+ </message>
+ <message>
+ <source>Yaw</source>
+ <translation type="unfinished">Yaw</translation>
+ </message>
+ <message>
+ <source>Pitch</source>
+ <translation type="unfinished">Pitch</translation>
+ </message>
+ <message>
+ <source>Roll</source>
+ <translation type="unfinished">Rol</translation>
+ </message>
+</context>
+<context>
+ <name>joystickDll</name>
+ <message>
+ <source>Linux Joystick input</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-linux-joystick/lang/ru_RU.ts b/tracker-linux-joystick/lang/ru_RU.ts
new file mode 100644
index 00000000..34ed1089
--- /dev/null
+++ b/tracker-linux-joystick/lang/ru_RU.ts
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="ru_RU">
+<context>
+ <name>UILinuxJoystickControls</name>
+ <message>
+ <source>Tracker settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Device</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Mapping</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Disabled</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #4</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #5</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #6</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #7</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #8</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>X</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Y</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Z</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Yaw</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Pitch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Roll</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>joystickDll</name>
+ <message>
+ <source>Linux Joystick input</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-linux-joystick/lang/stub.ts b/tracker-linux-joystick/lang/stub.ts
new file mode 100644
index 00000000..12dc1400
--- /dev/null
+++ b/tracker-linux-joystick/lang/stub.ts
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1">
+<context>
+ <name>UILinuxJoystickControls</name>
+ <message>
+ <source>Tracker settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Device</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Mapping</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Disabled</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #4</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #5</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #6</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #7</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #8</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>X</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Y</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Z</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Yaw</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Pitch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Roll</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>joystickDll</name>
+ <message>
+ <source>Linux Joystick input</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-linux-joystick/lang/zh_CN.ts b/tracker-linux-joystick/lang/zh_CN.ts
new file mode 100644
index 00000000..12dc1400
--- /dev/null
+++ b/tracker-linux-joystick/lang/zh_CN.ts
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1">
+<context>
+ <name>UILinuxJoystickControls</name>
+ <message>
+ <source>Tracker settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Device</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Mapping</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Disabled</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #2</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #4</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #5</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #6</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #7</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Joystick axis #8</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>X</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Y</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Z</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Yaw</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Pitch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Roll</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>joystickDll</name>
+ <message>
+ <source>Linux Joystick input</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/tracker-linux-joystick/linux_joystick.cpp b/tracker-linux-joystick/linux_joystick.cpp
new file mode 100644
index 00000000..3369a29e
--- /dev/null
+++ b/tracker-linux-joystick/linux_joystick.cpp
@@ -0,0 +1,65 @@
+#include "ftnoir_tracker_linux_joystick.h"
+
+#include <QDir>
+#include <QFileInfo>
+#include <QVariant>
+
+// Discovery is done by searching for devices in the sys file system.
+//
+// Given a path like this
+// /sys/devices/pci0000:00/0000:00:14.0/usb1/1-10/1-10:1.2/0003:2341:8036.0170/input/input380/js0
+// we want to get this part of the string 2341:8036, it will allow us to
+// identify the device in the future.
+// alternative way of doing this https://stackoverflow.com/questions/21173988/linux-attempting-to-get-joystick-vendor-and-product-ids-via-ioctl-get-einval-i
+std::tuple<QString, QString> sysfsDeviceToJsDev(QFileInfo device) {
+ using ret = std::tuple<QString, QString>;
+ QString symlink = device.symLinkTarget();
+ QString js_dev = QString("/dev/input/%1").arg(device.fileName());
+
+ QRegExp sep(QString("[:.%1]").arg(QDir::separator()));
+ QString device_id = symlink.section(sep, -6, -5);
+ return ret(js_dev, device_id);
+}
+
+QList<linux_joystick> getJoysticks()
+{
+ char name[128];
+ QList<linux_joystick> joysticks;
+
+ QDir dir("/sys/class/input/");
+ dir.setNameFilters({ "js*" });
+ QFileInfoList list = dir.entryInfoList();
+ for (int i = 0; i < list.size(); ++i)
+ {
+ QFileInfo device = list.at(i);
+ auto [js_dev, device_id] = sysfsDeviceToJsDev(device);
+ int iFile = open(js_dev.toUtf8().data(), O_RDONLY | O_NONBLOCK);
+ if (iFile == -1) continue;
+ if (ioctl(iFile, JSIOCGNAME(sizeof(name)), &name) > 0)
+ {
+ linux_joystick j;
+ j.name = name;
+ j.dev = js_dev;
+ j.device_id = device_id;
+ joysticks.append(j);
+ }
+ close(iFile);
+
+ }
+
+ return joysticks;
+}
+
+QString getJoystickDevice(QString guid) {
+ QDir dir("/sys/class/input/");
+ dir.setNameFilters({ "js*" });
+ QFileInfoList list = dir.entryInfoList();
+ for (int i = 0; i < list.size(); ++i)
+ {
+ QFileInfo device = list.at(i);
+ auto [js_dev, device_id] = sysfsDeviceToJsDev(device);
+ if (device_id == guid) return js_dev;
+ }
+
+ return NULL;
+}