-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Core functionality working well; user configs remain to be implemented before public release.
- Loading branch information
0 parents
commit aeb700c
Showing
17 changed files
with
1,698 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[submodule "subprojects/gcemuhook"] | ||
path = subprojects/gcemuhook | ||
url = https://github.com/v1993/gcemuhook.git |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
# Cemuhook UDP server for devices with modern Linux drivers - successor to original evdevhook | ||
|
||
## Supported devices | ||
|
||
* Nintendo Switch Joy-Cons | ||
* Nintendo Switch Pro Controller | ||
* DualShock 3 controller | ||
* DualShock 4 controller | ||
* DualSense controller | ||
|
||
Please note that as of right now only Nintendo controllers were tested. DualShock | ||
and Dual Sense *should* work - feedback would be very welcome. | ||
|
||
## Configuration | ||
|
||
No configuration is required to get started - just run the resulting binary and | ||
it will expose all supported controllers! | ||
|
||
However, if you want to tweak controller orientations, run server on a different | ||
port, or use over four controllers at once by running multiple servers on | ||
different ports, it's possible to do so | ||
|
||
## Quick build guide | ||
|
||
```bash | ||
git clone --recursive https://github.com/v1993/evdevhook2.git | ||
cd evdevhook2 | ||
meson --buildtype=release -Db_lto=true --prefix=/usr build | ||
ninja -C build | ||
# Optional | ||
ninja -C build install | ||
``` | ||
|
||
### Updating | ||
```bash | ||
cd evdevhook2 | ||
git pull | ||
git submodule update --recursive --init | ||
ninja -C build | ||
# Optional | ||
ninja -C build install | ||
``` | ||
|
||
## Dependencies | ||
* libudev | ||
* libevdev | ||
* GLib 2.50+ | ||
* zlib | ||
* Vala 0.56+ and libgee-0.8 (Ubuntu and derivatives should use [Vala Next PPA](https://launchpad.net/~vala-team/+archive/ubuntu/next)) | ||
* meson and ninja | ||
* GCC/Clang | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
# evdevhook2 configs | ||
|
||
The idea is to split config into two files. | ||
|
||
## Device class settings | ||
|
||
Configures what devices are supported and how their axles map to standard orientation. Ships with application, is not supposed to be edited by end users unless they want to configure a new kind of device (which they should PR upstream anyways). | ||
|
||
### Proposed syntax | ||
|
||
```ini | ||
# Nintendo Switch Pro Controller | ||
[057e:2009] | ||
AccelMapping=y+z-x+ | ||
GyroMapping=y-z-x+ | ||
GyroSensitivity=0.858 | ||
``` | ||
|
||
Section name corresponds to VID:PID pair in hexadecimal form while other options are described below: | ||
|
||
* `AccelMapping` string - how accelerometer maps from evdev axles to DSU ones | ||
* `GyroMapping` string (optional but recommended) - how gyroscope maps from evdev axles to DSU ones | ||
* `GyroSensitivity` double (optional) - multiplier for gyrocope inputs, fixes issues faced by some drivers (notably hid_nintendo) | ||
|
||
## Per-device settings | ||
|
||
This is an optional file that can be supplied by users to further configure their devices. It closely corresponds to config file of linuxmotehook2. | ||
|
||
### Proposed syntax | ||
|
||
```ini | ||
# Main section | ||
[Evdevhook] | ||
Port=26761 | ||
AllowlistMode=true | ||
|
||
# Section for my left joycon when playing Citra | ||
[D4:F0:57:3E:25:C1] | ||
Orientation=sideways-left | ||
``` | ||
|
||
Main section, named `Evdevhook`, contains the following options: | ||
|
||
* `Port` integer (defaults to `26760`) - port to run server on | ||
* `AllowlistMode` boolean (defaults to `false`) - only provide devices provided in config file; useful if >4 devices are present and multiple servers are required | ||
|
||
Per-device sections, each named after device's `uniq` string (typically its MAC): | ||
|
||
* `Orientation` enum - a final transformation to apply to device input, identical to that of linuxmotehook2 (copy description from its wiki) | ||
|
||
## To consider - button/axis inputs? | ||
|
||
With this rewrite it might be possible to supply button inputs in addition to motion data. How good of an idea it is is still debatable - unlike wiimotes, those devices have modern-day drivers and don't have associated issues, thus I'll only work on it if a good enough usecase is demonstrated. Required extensions to config files: | ||
|
||
* Device class settings | ||
* * `MainNodeName` string (optional) - name of node that provides button inputs | ||
* * Possibly some sort of config to map buttons? | ||
* Per-device settings, main section | ||
* * `SendButtons` boolean (defaults to `false`) - if buttons should be sent for supported devices; deprecated due to latency issues | ||
|
||
If this is to be implemented, an issue arises when it comes to combining data from two devices. As such, button data and motion data should be stored in temporary buffers (as part of a class). On their respective SYN packets data from that interface is copied into main field and DSU packet is generated. | ||
|
||
## Further considerations - touchscreen inputs? | ||
|
||
I don't own nor plan to get DS4/5 controllers, so this is unlikely to be implemented unless someone interested in such support shows up and is willing to collaborate. I'd be far more willing to implement this compared to button inputs, though, due to uniqueness of this interface. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
project('evdevhook2', ['c', 'vala'], | ||
version: '0.1.0', | ||
meson_version: '>= 0.50.0', | ||
default_options: [ 'warning_level=2', | ||
], | ||
) | ||
|
||
gcemuhook_proj = subproject('gcemuhook', default_options: ['default_library=static']) | ||
gcemuhook_dep = gcemuhook_proj.get_variable('gcemuhook_dep') | ||
|
||
extra_vapi_dir = meson.current_source_dir() / 'vapi' | ||
add_project_arguments(['--vapidir', extra_vapi_dir], language: 'vala') | ||
|
||
subdir('src') | ||
|
||
#install_data('ExampleConfig.ini', install_dir : get_option('datadir') / 'evdevhook2') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
/* Config.vala | ||
* | ||
* Copyright 2022 v1993 <[email protected]> | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
* | ||
* SPDX-License-Identifier: GPL-3.0-or-later | ||
*/ | ||
|
||
using Gee; | ||
|
||
namespace Evdevhook { | ||
errordomain ConfigError { | ||
INVALID_DEVICE_TYPE_CONFIG | ||
} | ||
|
||
class DeviceTypeConfig: Object { | ||
public int axis_map[Linux.Input.ABS_RZ + 1]; | ||
public bool axis_inversion[Linux.Input.ABS_RZ + 1]; | ||
public float gyro_sensitivity; | ||
|
||
public void read_orientation(string str, int base_idx) throws Error { | ||
if (str.length != 6) throw new ConfigError.INVALID_DEVICE_TYPE_CONFIG("orientation string must be 6 characters long"); | ||
|
||
for (uint8 i = 0; i < 3; ++i) { | ||
int evdev_axis; | ||
bool invert; | ||
|
||
switch (str[2 * i]) { | ||
case 'x': | ||
case 'X': | ||
evdev_axis = base_idx; | ||
break; | ||
case 'y': | ||
case 'Y': | ||
evdev_axis = base_idx + 1; | ||
break; | ||
case 'z': | ||
case 'Z': | ||
evdev_axis = base_idx + 2; | ||
break; | ||
default: | ||
throw new ConfigError.INVALID_DEVICE_TYPE_CONFIG("invalid letter in orientation specifier"); | ||
} | ||
|
||
switch (str[2 * i + 1]) { | ||
case '+': | ||
invert = false; | ||
break; | ||
case '-': | ||
invert = true; | ||
break; | ||
default: | ||
throw new ConfigError.INVALID_DEVICE_TYPE_CONFIG("invalid sign in orientation specifier"); | ||
} | ||
|
||
if (axis_map[evdev_axis] != -1) | ||
throw new ConfigError.INVALID_DEVICE_TYPE_CONFIG("trying to map same evdev device axis (%i) to multiple DSU ones", evdev_axis); | ||
|
||
axis_map[evdev_axis] = base_idx + i; | ||
axis_inversion[evdev_axis] = invert; | ||
} | ||
} | ||
|
||
construct { | ||
axis_map = {-1, -1, -1, -1, -1, -1}; | ||
axis_inversion = {false, false, false, false, false, false}; | ||
gyro_sensitivity = 1.0f; | ||
} | ||
} | ||
|
||
private class DeviceTypeIdentifier: Object, Hashable<DeviceTypeIdentifier> { | ||
public uint16 vid; | ||
public uint16 pid; | ||
|
||
public DeviceTypeIdentifier(uint16 vid, uint16 pid) { | ||
this.vid = vid; | ||
this.pid = pid; | ||
} | ||
|
||
public bool equal_to(DeviceTypeIdentifier o) { | ||
return vid == o.vid && pid == o.pid; | ||
} | ||
|
||
public uint hash() { | ||
return (((uint)vid) << 16) | (uint)pid; | ||
} | ||
} | ||
|
||
class DeviceConfig: Object { | ||
public Cemuhook.DeviceOrientation orientation = NORMAL; | ||
} | ||
|
||
[SingleInstance] | ||
class Config: Object { | ||
private const string MAIN_GROUP = "Evdevhook"; | ||
|
||
private HashMap<DeviceTypeIdentifier, DeviceTypeConfig> device_type_configs; | ||
private HashMap<string, DeviceConfig> device_configs; | ||
|
||
public uint16 port { get; private set; default = 26760; } | ||
public bool allowlist_mode { get; private set; default = false; } | ||
|
||
construct { | ||
device_type_configs = new HashMap<DeviceTypeIdentifier, DeviceTypeConfig>(); | ||
device_configs = new HashMap<string, DeviceConfig>(); | ||
} | ||
|
||
public void init_device_types() throws Error { | ||
var kfile = new KeyFile(); | ||
kfile.load_from_bytes(resources_lookup_data("/org/v1993/evdevhook2/DeviceTypes.ini", NONE), NONE); | ||
|
||
// There's no main config group in this file. | ||
// This config is not meant to be modified, so we terminate on errors. | ||
|
||
foreach (unowned string group in kfile.get_groups()) { | ||
var regex = /^([[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]):([[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]])$/; | ||
MatchInfo minfo; | ||
if (!regex.match(group, 0, out minfo)) { | ||
throw new ConfigError.INVALID_DEVICE_TYPE_CONFIG("Unidentified device type configuration group %s", group); | ||
} | ||
|
||
var vid = (uint16)uint64.parse(minfo.fetch(1), 16); | ||
var pid = (uint16)uint64.parse(minfo.fetch(2), 16); | ||
var devtypeid = new DeviceTypeIdentifier(vid, pid); | ||
var devtypeconf = new DeviceTypeConfig(); | ||
|
||
foreach (unowned string key in kfile.get_keys(group)) { | ||
switch(key) { | ||
case "AccelMapping": | ||
devtypeconf.read_orientation(kfile.get_string(group, key), Linux.Input.ABS_X); | ||
break; | ||
case "GyroMapping": | ||
devtypeconf.read_orientation(kfile.get_string(group, key), Linux.Input.ABS_RX); | ||
break; | ||
case "GyroSensitivity": | ||
devtypeconf.gyro_sensitivity = (float)kfile.get_double(group, key); | ||
break; | ||
default: | ||
throw new ConfigError.INVALID_DEVICE_TYPE_CONFIG("Unknown device type configuration key %s", key); | ||
} | ||
} | ||
|
||
device_type_configs[devtypeid] = devtypeconf; | ||
} | ||
} | ||
|
||
// TODO: load per-device (and main section) settings from config file | ||
|
||
public DeviceTypeConfig? get_device_type_config(uint16 vid, uint16 pid) { | ||
var devtypeid = new DeviceTypeIdentifier(vid, pid); | ||
return device_type_configs.has_key(devtypeid) ? device_type_configs[devtypeid] : null; | ||
} | ||
|
||
public DeviceConfig? get_device_config(string uniq) { | ||
if (device_configs.has_key(uniq)) { | ||
return device_configs[uniq]; | ||
} | ||
|
||
return allowlist_mode ? null : new DeviceConfig(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# This file defines accelerometer and gyro mappings for different devices. | ||
# It is embedded directly into executable and editing it is usually DISCOURAGED unless you want to add support for a new device. | ||
# In that case please consider opening a pull request with your added configuration. | ||
# Otherwise, use a per-device config file instead. If it doesn't fit your needs open an issue. | ||
# | ||
# You can set environment variable | ||
# G_RESOURCE_OVERLAYS='/org/v1993/evdevhook2/DeviceTypes.ini=/path/to/your/replacement/file.ini' | ||
# when running evdevhook2 if you want to test your changes without recompiling. | ||
|
||
# ### Nintendo ### | ||
|
||
# Nintendo Switch Left Joy-Con | ||
[057e:2006] | ||
AccelMapping=y+z-x+ | ||
GyroMapping=y-z-x+ | ||
GyroSensitivity=0.858 | ||
|
||
# Nintendo Switch Right Joy-Con | ||
[057e:2007] | ||
AccelMapping=y+z-x+ | ||
GyroMapping=y-z-x+ | ||
GyroSensitivity=0.858 | ||
|
||
# Nintendo Switch Pro Controller | ||
[057e:2009] | ||
AccelMapping=y+z-x+ | ||
GyroMapping=y-z-x+ | ||
GyroSensitivity=0.858 | ||
|
||
# ### Sony ### | ||
|
||
# DualShock 3 (no gyroscope) | ||
[054c:0268] | ||
AccelMapping=x-y-z- | ||
|
||
# DualShock 4 | ||
[054c:05c4] | ||
AccelMapping=x-y-z- | ||
GyroMapping=x-y-z- | ||
|
||
# DualShock 4 (second gen) | ||
[054c:09cc] | ||
AccelMapping=x-y-z- | ||
GyroMapping=x-y-z- | ||
|
||
# DualSense (different gyroscope mapping) | ||
[054c:0ce6] | ||
AccelMapping=x-y-z- | ||
GyroMapping=x+y-z- |
Oops, something went wrong.