summaryrefslogtreecommitdiffhomepage
path: root/proto-iokit-foohid/foohidjoystick.cpp
diff options
context:
space:
mode:
authorStanisław Halik <sthalik@misaki.pl>2017-11-28 15:16:27 +0100
committerGitHub <noreply@github.com>2017-11-28 15:16:27 +0100
commit1a91ba620dfbc420cfeee4530bc3f03127f3b967 (patch)
tree2a02884f633ddbc7ede387a5bf2aa8d7b0396e5f /proto-iokit-foohid/foohidjoystick.cpp
parentf548847ea43006b2dc540e84d11d3c3662a94148 (diff)
parent7bacbdf10b97040c9ed282a22485759bd082fa63 (diff)
Merge pull request #700 from e4z9/foohid
Add virtual joystick output for macOS
Diffstat (limited to 'proto-iokit-foohid/foohidjoystick.cpp')
-rw-r--r--proto-iokit-foohid/foohidjoystick.cpp162
1 files changed, 162 insertions, 0 deletions
diff --git a/proto-iokit-foohid/foohidjoystick.cpp b/proto-iokit-foohid/foohidjoystick.cpp
new file mode 100644
index 00000000..17a4bcb5
--- /dev/null
+++ b/proto-iokit-foohid/foohidjoystick.cpp
@@ -0,0 +1,162 @@
+/* Copyright (c) 2017, Eike Ziller *
+ * *
+ * 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 "foohidjoystick.h"
+
+#include <QCoreApplication>
+
+const char FOOHID_SERVICE_NAME[] = "it_unbit_foohid";
+
+enum class FooHIDMethod {
+ Create = 0,
+ Destroy,
+ Send,
+ List,
+ Subscribe
+};
+
+// Joystick USB descriptor
+static unsigned char report_descriptor[] = {
+ 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
+ 0x09, 0x04, // USAGE (Joystick)
+ 0xa1, 0x01, // COLLECTION (Application)
+ 0x09, 0x01, // USAGE (Pointer)
+ 0xa1, 0x00, // COLLECTION (Physical)
+ 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
+ 0x09, 0x30, // USAGE (X)
+ 0x09, 0x31, // USAGE (Y)
+ 0x09, 0x32, // USAGE (Z)
+ 0x09, 0x33, // USAGE (RX)
+ 0x09, 0x34, // USAGE (RY)
+ 0x09, 0x35, // USAGE (RZ)
+ 0x15, 0x00, // LOGICAL_MINIMUM (0)
+ 0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
+ 0x75, 0x08, // REPORT_SIZE (8)
+ 0x95, 0x06, // REPORT_COUNT (6)
+ 0x81, 0x02, // INPUT (Data,Var,Abs)
+ 0xc0, // END_COLLECTION
+ 0xc0 // END_COLLECTION
+};
+
+static bool connectToService(io_connect_t *connection, QString *errorMessage)
+{
+ io_iterator_t iterator;
+ io_service_t service;
+ // Get an iterator over all IOService instances
+ kern_return_t ret = IOServiceGetMatchingServices(kIOMasterPortDefault,
+ IOServiceMatching(FOOHID_SERVICE_NAME),
+ &iterator);
+ if (ret != KERN_SUCCESS) {
+ *errorMessage = QCoreApplication::translate("iokit-foohid", "Unable to find FooHID IOService.");
+ return false;
+ }
+ // Iterate over services and try to open connection
+ bool found = false;
+ while ((service = IOIteratorNext(iterator)) != IO_OBJECT_NULL) {
+ ret = IOServiceOpen(service, mach_task_self(), 0, connection);
+ if (ret == KERN_SUCCESS) {
+ found = true;
+ break;
+ }
+ IOObjectRelease(service);
+ }
+ IOObjectRelease(iterator);
+ if (!found) {
+ *errorMessage = QCoreApplication::translate("iokit-foohid", "Unable to connect to FooHID IOService.");
+ return false;
+ }
+ return true;
+}
+
+static void disconnectFromService(io_connect_t connection)
+{
+ IOServiceClose(connection);
+ // ignore errors
+}
+
+FooHIDJoystick::FooHIDJoystick(const QByteArray &name, const QByteArray &serialNumber)
+ : name(name), serialNumber(serialNumber)
+{
+ connectionOpened = connectToService(&connection, &_errorMessage);
+ _hasError = !connectionOpened;
+ if (!_hasError) {
+ // first try to destroy device, in case it was left-over (from a crash/interrupt)
+ destroyDevice();
+ deviceCreated = createDevice();
+ _hasError = !deviceCreated;
+ if (!deviceCreated)
+ _errorMessage = QCoreApplication::translate("iokit-foohid",
+ "Failed to create virtual joystick");
+ }
+}
+
+FooHIDJoystick::~FooHIDJoystick()
+{
+ if (deviceCreated)
+ destroyDevice();
+ if (connectionOpened)
+ disconnectFromService(connection);
+}
+
+bool FooHIDJoystick::hasError() const
+{
+ return _hasError;
+}
+
+QString FooHIDJoystick::errorMessage() const
+{
+ return _errorMessage;
+}
+
+void FooHIDJoystick::setValue(JoystickValues newValues)
+{
+ values = newValues;
+ if (!sendToDevice()) {
+ _hasError = true;
+ _errorMessage = QCoreApplication::translate("iokit-foohid",
+ "Failed to send values to virtual joystick");
+ }
+}
+
+bool FooHIDJoystick::createDevice() const
+{
+ uint64_t params[8];
+ params[0] = uint64_t(name.constData()); // pointer to name
+ params[1] = uint64_t(name.size()); // size of name without \0
+ params[2] = uint64_t(report_descriptor); // pointer to report descriptor
+ params[3] = sizeof(report_descriptor); // size of report descriptor
+ params[4] = uint64_t(serialNumber.constData()); // pointer to serial number
+ params[5] = uint64_t(serialNumber.size()); // size of serial number without \0
+ params[6] = uint64_t(2); // vendor ID
+ params[7] = uint64_t(3); // device ID
+
+ kern_return_t ret = IOConnectCallScalarMethod(connection, int(FooHIDMethod::Create), params,
+ sizeof(params)/sizeof(params[0]), NULL, 0);
+ return ret == KERN_SUCCESS;
+}
+
+bool FooHIDJoystick::sendToDevice() const
+{
+ uint64_t params[4];
+ params[0] = uint64_t(name.constData()); // pointer to name
+ params[1] = uint64_t(name.size()); // size of name without \0
+ params[2] = uint64_t(&values); // pointer to values
+ params[3] = sizeof(struct JoystickValues); // length of value struct
+ kern_return_t ret = IOConnectCallScalarMethod(connection, int(FooHIDMethod::Send), params,
+ sizeof(params)/sizeof(params[0]), NULL, 0);
+ return ret == KERN_SUCCESS;
+}
+
+void FooHIDJoystick::destroyDevice() const
+{
+ uint64_t params[2];
+ params[0] = uint64_t(name.constData()); // pointer to name
+ params[1] = uint64_t(name.size()); // size of name without \0
+ IOConnectCallScalarMethod(connection, int(FooHIDMethod::Destroy), params,
+ sizeof(params)/sizeof(params[0]), NULL, 0);
+ // ignore failure
+}