mirror of
https://github.com/marcriera/ddgo-converter.git
synced 2025-04-04 12:49:28 +02:00
Add GUI and support for more controllers
This commit is contained in:
parent
cc8a271883
commit
dce84dcc43
13 changed files with 1094 additions and 151 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
__pycache__/
|
||||
*.db
|
||||
**/.vscode/
|
|
@ -1,151 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
from evdev import InputDevice, list_devices, ecodes as e, UInput, AbsInfo
|
||||
|
||||
cap = {
|
||||
e.EV_KEY : [e.BTN_NORTH, e.BTN_SOUTH, e.BTN_EAST, e.BTN_WEST, e.BTN_SELECT, e.BTN_START],
|
||||
e.EV_ABS : [(e.ABS_X, AbsInfo(0, 0, 255, 0, 0, 0)), (e.ABS_Y, AbsInfo(0, 0, 255, 0, 0, 0))]
|
||||
}
|
||||
|
||||
# Mapping
|
||||
# A: BTN_EAST
|
||||
# B: BTN_SOUTH
|
||||
# C: BTN_NORTH
|
||||
# D: BTN_WEST
|
||||
# SL: BTN_SELECT
|
||||
# ST: BTN_START
|
||||
# BRAKE: ABS_X
|
||||
# POWER: ABS_Y
|
||||
|
||||
mascon_switch = None
|
||||
devices = [InputDevice(path) for path in list_devices()]
|
||||
for device in devices:
|
||||
dev_name = [device.info.vendor, device.info.product, device.name]
|
||||
if dev_name == [0x0f0d, 0x00c1, "One Handle MasCon for Nintendo Switch"]:
|
||||
mascon_switch = device
|
||||
break
|
||||
|
||||
if mascon_switch is None:
|
||||
print("No supported controller found.")
|
||||
exit()
|
||||
|
||||
ui = UInput(cap, vendor=0x0AE4, product=0x0003, name='Emulated DGOC-44U')
|
||||
|
||||
mascon_switch.grab()
|
||||
for event in mascon_switch.read_loop():
|
||||
if event.type == e.EV_KEY:
|
||||
match event.code:
|
||||
case 304: # Y
|
||||
ui.write(e.EV_KEY, e.BTN_EAST, event.value)
|
||||
ui.syn()
|
||||
case 305: # B
|
||||
ui.write(e.EV_KEY, e.BTN_SOUTH, event.value)
|
||||
ui.syn()
|
||||
case 306: # A
|
||||
ui.write(e.EV_KEY, e.BTN_NORTH, event.value)
|
||||
ui.syn()
|
||||
case 307: # X
|
||||
ui.write(e.EV_KEY, e.BTN_WEST, event.value)
|
||||
ui.syn()
|
||||
case 312: # MINUS
|
||||
ui.write(e.EV_KEY, e.BTN_SELECT, event.value)
|
||||
ui.syn()
|
||||
case 313: # PLUS
|
||||
ui.write(e.EV_KEY, e.BTN_START, event.value)
|
||||
ui.syn()
|
||||
if event.type == e.EV_ABS and event.code == e.ABS_HAT0X:
|
||||
match event.value:
|
||||
case -1: # LEFT
|
||||
ui.write(e.EV_KEY, e.BTN_SELECT, 1)
|
||||
ui.write(e.EV_KEY, e.BTN_EAST, 1)
|
||||
ui.write(e.EV_KEY, e.BTN_NORTH, 0)
|
||||
ui.syn()
|
||||
case 1: # RIGHT
|
||||
ui.write(e.EV_KEY, e.BTN_SELECT, 1)
|
||||
ui.write(e.EV_KEY, e.BTN_EAST, 0)
|
||||
ui.write(e.EV_KEY, e.BTN_NORTH, 1)
|
||||
ui.syn()
|
||||
case _: # NONE
|
||||
ui.write(e.EV_KEY, e.BTN_SELECT, 0)
|
||||
ui.write(e.EV_KEY, e.BTN_EAST, 0)
|
||||
ui.write(e.EV_KEY, e.BTN_NORTH, 0)
|
||||
ui.syn()
|
||||
if event.type == e.EV_ABS and event.code == e.ABS_HAT0Y:
|
||||
match event.value:
|
||||
case -1: # UP
|
||||
ui.write(e.EV_KEY, e.BTN_SELECT, 1)
|
||||
ui.write(e.EV_KEY, e.BTN_WEST, 1)
|
||||
ui.write(e.EV_KEY, e.BTN_SOUTH, 0)
|
||||
ui.syn()
|
||||
case 1: # DOWN
|
||||
ui.write(e.EV_KEY, e.BTN_SELECT, 1)
|
||||
ui.write(e.EV_KEY, e.BTN_WEST, 0)
|
||||
ui.write(e.EV_KEY, e.BTN_SOUTH, 1)
|
||||
ui.syn()
|
||||
case _: # NONE
|
||||
ui.write(e.EV_KEY, e.BTN_SELECT, 0)
|
||||
ui.write(e.EV_KEY, e.BTN_WEST, 0)
|
||||
ui.write(e.EV_KEY, e.BTN_SOUTH, 0)
|
||||
ui.syn()
|
||||
if event.type == e.EV_ABS and event.code == e.ABS_Y:
|
||||
match event.value:
|
||||
case 0x0: # EMG
|
||||
ui.write(e.EV_ABS, e.ABS_X, 0xB9)
|
||||
ui.write(e.EV_ABS, e.ABS_Y, 0x81)
|
||||
ui.syn()
|
||||
case 0x5:
|
||||
ui.write(e.EV_ABS, e.ABS_X, 0xB5)
|
||||
ui.write(e.EV_ABS, e.ABS_Y, 0x81)
|
||||
ui.syn()
|
||||
case 0x13:
|
||||
ui.write(e.EV_ABS, e.ABS_X, 0xB2)
|
||||
ui.write(e.EV_ABS, e.ABS_Y, 0x81)
|
||||
ui.syn()
|
||||
case 0x20:
|
||||
ui.write(e.EV_ABS, e.ABS_X, 0xAF)
|
||||
ui.write(e.EV_ABS, e.ABS_Y, 0x81)
|
||||
ui.syn()
|
||||
case 0x2E:
|
||||
ui.write(e.EV_ABS, e.ABS_X, 0xA8)
|
||||
ui.write(e.EV_ABS, e.ABS_Y, 0x81)
|
||||
ui.syn()
|
||||
case 0x3C:
|
||||
ui.write(e.EV_ABS, e.ABS_X, 0xA2)
|
||||
ui.write(e.EV_ABS, e.ABS_Y, 0x81)
|
||||
ui.syn()
|
||||
case 0x49:
|
||||
ui.write(e.EV_ABS, e.ABS_X, 0x9A)
|
||||
ui.write(e.EV_ABS, e.ABS_Y, 0x81)
|
||||
ui.syn()
|
||||
case 0x57:
|
||||
ui.write(e.EV_ABS, e.ABS_X, 0x94)
|
||||
ui.write(e.EV_ABS, e.ABS_Y, 0x81)
|
||||
ui.syn()
|
||||
case 0x65:
|
||||
ui.write(e.EV_ABS, e.ABS_X, 0x8A)
|
||||
ui.write(e.EV_ABS, e.ABS_Y, 0x81)
|
||||
ui.syn()
|
||||
case 0x80:
|
||||
ui.write(e.EV_ABS, e.ABS_X, 0x79)
|
||||
ui.write(e.EV_ABS, e.ABS_Y, 0x81)
|
||||
ui.syn()
|
||||
case 0x9F:
|
||||
ui.write(e.EV_ABS, e.ABS_X, 0x79)
|
||||
ui.write(e.EV_ABS, e.ABS_Y, 0x6D)
|
||||
ui.syn()
|
||||
case 0xB7:
|
||||
ui.write(e.EV_ABS, e.ABS_X, 0x79)
|
||||
ui.write(e.EV_ABS, e.ABS_Y, 0x54)
|
||||
ui.syn()
|
||||
case 0xCE:
|
||||
ui.write(e.EV_ABS, e.ABS_X, 0x79)
|
||||
ui.write(e.EV_ABS, e.ABS_Y, 0x3F)
|
||||
ui.syn()
|
||||
case 0xE6:
|
||||
ui.write(e.EV_ABS, e.ABS_X, 0x79)
|
||||
ui.write(e.EV_ABS, e.ABS_Y, 0x21)
|
||||
ui.syn()
|
||||
case 0xFF:
|
||||
ui.write(e.EV_ABS, e.ABS_X, 0x79)
|
||||
ui.write(e.EV_ABS, e.ABS_Y, 0x00)
|
||||
ui.syn()
|
44
ddgo-converter.spec
Normal file
44
ddgo-converter.spec
Normal file
|
@ -0,0 +1,44 @@
|
|||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
|
||||
block_cipher = None
|
||||
|
||||
|
||||
a = Analysis(
|
||||
['ddgo-converter/ddgo-converter.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[],
|
||||
hiddenimports=[],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
noarchive=False,
|
||||
)
|
||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
[],
|
||||
name='ddgo-converter',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=False,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=False,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
)
|
16
ddgo-converter/ddgo-converter.py
Executable file
16
ddgo-converter/ddgo-converter.py
Executable file
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import sys
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
from gui.main import MainWindow
|
||||
from handlers.gamepad import GamepadHandler
|
||||
|
||||
class App(QApplication):
|
||||
def __init__(self, sys_argv):
|
||||
super(App, self).__init__(sys_argv)
|
||||
self.main_view = MainWindow(GamepadHandler)
|
||||
self.main_view.show()
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = App(sys.argv)
|
||||
sys.exit(app.exec_())
|
28
ddgo-converter/events/input.py
Normal file
28
ddgo-converter/events/input.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
from enum import IntEnum
|
||||
|
||||
class InputEvent:
|
||||
|
||||
class EventType(IntEnum):
|
||||
RELEASE_BUTTON = 0
|
||||
PRESS_BUTTON = 1
|
||||
BRAKE_NOTCH = 2
|
||||
POWER_NOTCH = 3
|
||||
ERROR = 4
|
||||
|
||||
class Button(IntEnum):
|
||||
BUTTON_SELECT = 0
|
||||
BUTTON_START = 1
|
||||
BUTTON_A = 2
|
||||
BUTTON_B = 3
|
||||
BUTTON_C = 4
|
||||
BUTTON_D = 5
|
||||
BUTTON_UP = 6
|
||||
BUTTON_DOWN = 7
|
||||
BUTTON_LEFT = 8
|
||||
BUTTON_RIGHT = 9
|
||||
BUTTON_LDOOR = 10
|
||||
BUTTON_RDOOR = 11
|
||||
|
||||
def __init__(self, type, data):
|
||||
self.type = type
|
||||
self.data = data
|
410
ddgo-converter/gamepads/emulated.py
Executable file
410
ddgo-converter/gamepads/emulated.py
Executable file
|
@ -0,0 +1,410 @@
|
|||
from enum import IntEnum
|
||||
from events.input import InputEvent
|
||||
from evdev import ecodes, UInput, AbsInfo
|
||||
|
||||
class EmulatedGamepad:
|
||||
|
||||
class GamepadType(IntEnum):
|
||||
PC2HANDLE = 0
|
||||
PS1 = 1
|
||||
N64 = 2
|
||||
SAT = 3
|
||||
|
||||
class PC2HandleGamepad(EmulatedGamepad):
|
||||
|
||||
# Mapping
|
||||
# A: BTN_EAST
|
||||
# B: BTN_SOUTH
|
||||
# C: BTN_NORTH
|
||||
# D: BTN_WEST
|
||||
# SL: BTN_SELECT
|
||||
# ST: BTN_START
|
||||
# BRAKE: ABS_X
|
||||
# POWER: ABS_Y
|
||||
|
||||
def __init__(self):
|
||||
self.type = self.GamepadType.PC2HANDLE
|
||||
self.capabilities = {
|
||||
ecodes.EV_KEY : [ecodes.BTN_NORTH, ecodes.BTN_SOUTH, ecodes.BTN_EAST, ecodes.BTN_WEST, ecodes.BTN_SELECT, ecodes.BTN_START],
|
||||
ecodes.EV_ABS : [(ecodes.ABS_X, AbsInfo(0x79, 0, 255, 0, 0, 0)), (ecodes.ABS_Y, AbsInfo(0x81, 0, 255, 0, 0, 0))]
|
||||
}
|
||||
self.brake_notches = (0x79, 0x8A, 0x94, 0x9A, 0xA2, 0xA8, 0xAF, 0xB2, 0xB5, 0xB9)
|
||||
self.power_notches = (0x81, 0x6D, 0x54, 0x3F, 0x21, 0x00)
|
||||
|
||||
def start(self):
|
||||
self.ui = UInput(self.capabilities, vendor=0x0AE4, product=0x0003, name='Emulated DGOC-44U')
|
||||
|
||||
def stop(self):
|
||||
self.ui.close()
|
||||
|
||||
def write_input(self, event):
|
||||
match event.type:
|
||||
case (InputEvent.EventType.RELEASE_BUTTON | InputEvent.EventType.PRESS_BUTTON):
|
||||
match event.data:
|
||||
case InputEvent.Button.BUTTON_A:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_EAST, event.type)
|
||||
case InputEvent.Button.BUTTON_B:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_SOUTH, event.type)
|
||||
case InputEvent.Button.BUTTON_C:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_NORTH, event.type)
|
||||
case InputEvent.Button.BUTTON_D:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_WEST, event.type)
|
||||
case InputEvent.Button.BUTTON_SELECT:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_SELECT, event.type)
|
||||
case InputEvent.Button.BUTTON_START:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_START, event.type)
|
||||
case InputEvent.Button.BUTTON_UP:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_SELECT, event.type)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_WEST, event.type)
|
||||
case InputEvent.Button.BUTTON_DOWN:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_SELECT, event.type)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_SOUTH, event.type)
|
||||
case InputEvent.Button.BUTTON_LEFT:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_SELECT, event.type)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_EAST, event.type)
|
||||
case InputEvent.Button.BUTTON_RIGHT:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_SELECT, event.type)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_NORTH, event.type)
|
||||
case InputEvent.EventType.BRAKE_NOTCH:
|
||||
self.ui.write(ecodes.EV_ABS, ecodes.ABS_X, self.brake_notches[event.data])
|
||||
case InputEvent.EventType.POWER_NOTCH:
|
||||
self.ui.write(ecodes.EV_ABS, ecodes.ABS_Y, self.power_notches[event.data])
|
||||
self.ui.syn()
|
||||
|
||||
class PS1Gamepad(EmulatedGamepad):
|
||||
|
||||
def __init__(self):
|
||||
self.type = self.GamepadType.PS1
|
||||
self.capabilities = {
|
||||
ecodes.EV_KEY : [ecodes.BTN_NORTH, ecodes.BTN_SOUTH, ecodes.BTN_EAST, ecodes.BTN_WEST, ecodes.BTN_TL, ecodes.BTN_TR,
|
||||
ecodes.BTN_TL2, ecodes.BTN_TR2, ecodes.BTN_SELECT, ecodes.BTN_START, ecodes.BTN_THUMBL, ecodes.BTN_THUMBR,
|
||||
ecodes.BTN_MODE, ecodes.BTN_DPAD_UP, ecodes.BTN_DPAD_DOWN, ecodes.BTN_DPAD_LEFT, ecodes.BTN_DPAD_RIGHT],
|
||||
ecodes.EV_ABS : [(ecodes.ABS_X, AbsInfo(128, 0, 255, 0, 0, 0)), (ecodes.ABS_Y, AbsInfo(128, 0, 255, 0, 0, 0)),
|
||||
(ecodes.ABS_RX, AbsInfo(128, 0, 255, 0, 0, 0)), (ecodes.ABS_RY, AbsInfo(128, 0, 255, 0, 0, 0))]
|
||||
}
|
||||
|
||||
def start(self):
|
||||
self.ui = UInput(self.capabilities, vendor=0x54C, product=0x268, name='Sony PLAYSTATION(R)3 Controller')
|
||||
|
||||
def stop(self):
|
||||
self.ui.close()
|
||||
|
||||
def write_input(self, event):
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_UP, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_DOWN, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_UP, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_DOWN, 1)
|
||||
match event.type:
|
||||
case (InputEvent.EventType.RELEASE_BUTTON | InputEvent.EventType.PRESS_BUTTON):
|
||||
match event.data:
|
||||
case InputEvent.Button.BUTTON_A:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_WEST, event.type)
|
||||
case InputEvent.Button.BUTTON_B:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_SOUTH, event.type)
|
||||
case InputEvent.Button.BUTTON_C:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_EAST, event.type)
|
||||
case InputEvent.Button.BUTTON_SELECT:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_SELECT, event.type)
|
||||
case InputEvent.Button.BUTTON_START:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_START, event.type)
|
||||
case InputEvent.EventType.BRAKE_NOTCH:
|
||||
match event.data:
|
||||
case 0:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL2, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR2, 1)
|
||||
case 1:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL2, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR2, 1)
|
||||
case 2:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL2, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR2, 1)
|
||||
case 3:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL2, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR2, 1)
|
||||
case 4:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL2, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR2, 1)
|
||||
case 5:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL2, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR2, 1)
|
||||
case 6:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL2, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR2, 1)
|
||||
case 7:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL2, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR2, 0)
|
||||
case 8:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL2, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR2, 0)
|
||||
case 9:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL2, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR2, 0)
|
||||
case InputEvent.EventType.POWER_NOTCH:
|
||||
match event.data:
|
||||
case 0:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_NORTH, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_LEFT, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_RIGHT, 1)
|
||||
case 1:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_NORTH, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_LEFT, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_RIGHT, 1)
|
||||
case 2:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_NORTH, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_LEFT, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_RIGHT, 1)
|
||||
case 3:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_NORTH, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_LEFT, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_RIGHT, 0)
|
||||
case 4:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_NORTH, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_LEFT, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_RIGHT, 0)
|
||||
case 5:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_NORTH, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_LEFT, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_RIGHT, 0)
|
||||
self.ui.syn()
|
||||
|
||||
class N64Gamepad(EmulatedGamepad):
|
||||
|
||||
def __init__(self):
|
||||
self.type = self.GamepadType.N64
|
||||
self.capabilities = {
|
||||
ecodes.EV_KEY : [ecodes.BTN_NORTH, ecodes.BTN_SOUTH, ecodes.BTN_EAST, ecodes.BTN_WEST, ecodes.BTN_TL, ecodes.BTN_TR,
|
||||
ecodes.BTN_TL2, ecodes.BTN_TR2, ecodes.BTN_SELECT, ecodes.BTN_START, ecodes.BTN_THUMBL, ecodes.BTN_THUMBR,
|
||||
ecodes.BTN_MODE, ecodes.BTN_DPAD_UP, ecodes.BTN_DPAD_DOWN, ecodes.BTN_DPAD_LEFT, ecodes.BTN_DPAD_RIGHT],
|
||||
ecodes.EV_ABS : [(ecodes.ABS_X, AbsInfo(128, 0, 255, 0, 0, 0)), (ecodes.ABS_Y, AbsInfo(128, 0, 255, 0, 0, 0)),
|
||||
(ecodes.ABS_RX, AbsInfo(128, 0, 255, 0, 0, 0)), (ecodes.ABS_RY, AbsInfo(128, 0, 255, 0, 0, 0))]
|
||||
}
|
||||
|
||||
def start(self):
|
||||
self.ui = UInput(self.capabilities, vendor=0x54C, product=0x268, name='Sony PLAYSTATION(R)3 Controller')
|
||||
|
||||
def stop(self):
|
||||
self.ui.close()
|
||||
|
||||
def write_input(self, event):
|
||||
match event.type:
|
||||
case (InputEvent.EventType.RELEASE_BUTTON | InputEvent.EventType.PRESS_BUTTON):
|
||||
match event.data:
|
||||
case InputEvent.Button.BUTTON_A:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_WEST, event.type)
|
||||
case InputEvent.Button.BUTTON_B:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_SOUTH, event.type)
|
||||
case InputEvent.Button.BUTTON_C:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_SELECT, event.type)
|
||||
case InputEvent.Button.BUTTON_SELECT:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR2, event.type)
|
||||
case InputEvent.Button.BUTTON_START:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_START, event.type)
|
||||
case InputEvent.EventType.BRAKE_NOTCH:
|
||||
match event.data:
|
||||
case 0:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_EAST, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_NORTH, 1)
|
||||
case 1:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_EAST, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_NORTH, 1)
|
||||
case 2:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_EAST, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_NORTH, 1)
|
||||
case 3:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_EAST, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_NORTH, 1)
|
||||
case 4:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_EAST, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_NORTH, 1)
|
||||
case 5:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_EAST, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_NORTH, 1)
|
||||
case 6:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_EAST, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_NORTH, 1)
|
||||
case 7:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_EAST, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_NORTH, 0)
|
||||
case 8:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_EAST, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_NORTH, 0)
|
||||
case 9:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_EAST, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_NORTH, 0)
|
||||
case InputEvent.EventType.POWER_NOTCH:
|
||||
match event.data:
|
||||
case 0:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_RIGHT, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_UP, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL2, 1)
|
||||
case 1:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_RIGHT, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_UP, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL2, 1)
|
||||
case 2:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_RIGHT, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_UP, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL2, 1)
|
||||
case 3:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_RIGHT, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_UP, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL2, 0)
|
||||
case 4:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_RIGHT, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_UP, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL2, 0)
|
||||
case 5:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_RIGHT, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_UP, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL2, 0)
|
||||
self.ui.syn()
|
||||
|
||||
class SATGamepad(EmulatedGamepad):
|
||||
|
||||
def __init__(self):
|
||||
self.type = self.GamepadType.SAT
|
||||
self.capabilities = {
|
||||
ecodes.EV_KEY : [ecodes.BTN_NORTH, ecodes.BTN_SOUTH, ecodes.BTN_EAST, ecodes.BTN_WEST, ecodes.BTN_TL, ecodes.BTN_TR,
|
||||
ecodes.BTN_TL2, ecodes.BTN_TR2, ecodes.BTN_SELECT, ecodes.BTN_START, ecodes.BTN_THUMBL, ecodes.BTN_THUMBR,
|
||||
ecodes.BTN_MODE, ecodes.BTN_DPAD_UP, ecodes.BTN_DPAD_DOWN, ecodes.BTN_DPAD_LEFT, ecodes.BTN_DPAD_RIGHT],
|
||||
ecodes.EV_ABS : [(ecodes.ABS_X, AbsInfo(128, 0, 255, 0, 0, 0)), (ecodes.ABS_Y, AbsInfo(128, 0, 255, 0, 0, 0)),
|
||||
(ecodes.ABS_RX, AbsInfo(128, 0, 255, 0, 0, 0)), (ecodes.ABS_RY, AbsInfo(128, 0, 255, 0, 0, 0))]
|
||||
}
|
||||
|
||||
def start(self):
|
||||
self.ui = UInput(self.capabilities, vendor=0x54C, product=0x268, name='Sony PLAYSTATION(R)3 Controller')
|
||||
|
||||
def stop(self):
|
||||
self.ui.close()
|
||||
|
||||
def write_input(self, event):
|
||||
match event.type:
|
||||
case (InputEvent.EventType.RELEASE_BUTTON | InputEvent.EventType.PRESS_BUTTON):
|
||||
match event.data:
|
||||
case InputEvent.Button.BUTTON_A:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_SOUTH, event.type)
|
||||
case InputEvent.Button.BUTTON_B:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_EAST, event.type)
|
||||
case InputEvent.Button.BUTTON_C:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR, event.type)
|
||||
case InputEvent.Button.BUTTON_START:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_START, event.type)
|
||||
case InputEvent.EventType.BRAKE_NOTCH:
|
||||
match event.data:
|
||||
case 0:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL2, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR2, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_DOWN, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_LEFT, 1)
|
||||
case 1:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL2, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR2, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_DOWN, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_LEFT, 1)
|
||||
case 2:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL2, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR2, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_DOWN, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_LEFT, 1)
|
||||
case 3:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL2, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR2, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_DOWN, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_LEFT, 1)
|
||||
case 4:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL2, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR2, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_DOWN, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_LEFT, 1)
|
||||
case 5:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL2, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR2, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_DOWN, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_LEFT, 1)
|
||||
case 6:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL2, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR2, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_DOWN, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_LEFT, 1)
|
||||
case 7:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL2, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR2, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_DOWN, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_LEFT, 0)
|
||||
case 8:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL2, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR2, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_DOWN, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_LEFT, 0)
|
||||
case 9:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL2, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TR2, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_DOWN, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_DPAD_LEFT, 0)
|
||||
case InputEvent.EventType.POWER_NOTCH:
|
||||
match event.data:
|
||||
case 0:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_WEST, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_NORTH, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL, 1)
|
||||
case 1:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_WEST, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_NORTH, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL, 1)
|
||||
case 2:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_WEST, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_NORTH, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL, 1)
|
||||
case 3:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_WEST, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_NORTH, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL, 0)
|
||||
case 4:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_WEST, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_NORTH, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL, 0)
|
||||
case 5:
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_WEST, 1)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_NORTH, 0)
|
||||
self.ui.write(ecodes.EV_KEY, ecodes.BTN_TL, 0)
|
||||
self.ui.syn()
|
140
ddgo-converter/gamepads/physical.py
Executable file
140
ddgo-converter/gamepads/physical.py
Executable file
|
@ -0,0 +1,140 @@
|
|||
from enum import IntEnum
|
||||
import evdev
|
||||
from hashlib import sha1
|
||||
from events.input import InputEvent
|
||||
from select import select
|
||||
|
||||
def create_gamepad(vid, pid, name):
|
||||
match vid, pid:
|
||||
case 0x0f0d, 0x00c1:
|
||||
return SwitchGamepad(vid, pid, name)
|
||||
return PhysicalGamepad(vid, pid, name)
|
||||
|
||||
class PhysicalGamepad:
|
||||
|
||||
class GamepadType(IntEnum):
|
||||
UNKNOWN = 0
|
||||
CLASSIC = 1
|
||||
SWITCH = 2
|
||||
|
||||
def __init__(self, vid, pid, name):
|
||||
super().__init__()
|
||||
self.vid = vid
|
||||
self.pid = pid
|
||||
self.name = name
|
||||
self.id = self._get_gamepad_id()
|
||||
self.hash = self._get_gamepad_hash()
|
||||
self.type = self.GamepadType.UNKNOWN
|
||||
self.config = []
|
||||
|
||||
def _get_gamepad_id(self):
|
||||
vid = format(self.vid, "x").zfill(4)
|
||||
pid = format(self.pid, "x").zfill(4)
|
||||
id = str(vid + ":" + pid)
|
||||
return id
|
||||
|
||||
def _get_gamepad_hash(self):
|
||||
hash = sha1(str(self.id + ":" + self.name).encode('utf-8')).hexdigest()
|
||||
return hash
|
||||
|
||||
def _get_device(self):
|
||||
for device in [evdev.InputDevice(path) for path in evdev.list_devices()]:
|
||||
dev_name = [device.info.vendor, device.info.product, device.name]
|
||||
if dev_name == [self.vid, self.pid, self.name]:
|
||||
return device
|
||||
|
||||
class SwitchGamepad(PhysicalGamepad):
|
||||
|
||||
def __init__(self, * args):
|
||||
super().__init__(* args)
|
||||
self.type = self.GamepadType.SWITCH
|
||||
self.config = []
|
||||
self.device = self._get_device()
|
||||
|
||||
def start(self):
|
||||
self.device.grab()
|
||||
|
||||
def stop(self):
|
||||
self.device.ungrab()
|
||||
|
||||
def read_input(self):
|
||||
# time.sleep(5)
|
||||
# print("Read from ZKNS-001 correct")
|
||||
# return InputEvent(InputEvent.EventType.PRESS_BUTTON, InputEvent.Button.BUTTON_A)
|
||||
select([self.device], [], [], 5)
|
||||
try:
|
||||
event = self.device.read_one()
|
||||
input_events = []
|
||||
if event is not None:
|
||||
if event.type == evdev.ecodes.EV_KEY:
|
||||
match event.code:
|
||||
case 304: # Y
|
||||
input_events.append(InputEvent(InputEvent.EventType(event.value), InputEvent.Button.BUTTON_A))
|
||||
case 305: # B
|
||||
input_events.append(InputEvent(InputEvent.EventType(event.value), InputEvent.Button.BUTTON_B))
|
||||
case 306: # A
|
||||
input_events.append(InputEvent(InputEvent.EventType(event.value), InputEvent.Button.BUTTON_C))
|
||||
case 307: # X
|
||||
input_events.append(InputEvent(InputEvent.EventType(event.value), InputEvent.Button.BUTTON_D))
|
||||
case 312: # MINUS
|
||||
input_events.append(InputEvent(InputEvent.EventType(event.value), InputEvent.Button.BUTTON_SELECT))
|
||||
case 313: # PLUS
|
||||
input_events.append(InputEvent(InputEvent.EventType(event.value), InputEvent.Button.BUTTON_START))
|
||||
if event.type == evdev.ecodes.EV_ABS and event.code == evdev.ecodes.ABS_HAT0X:
|
||||
match event.value:
|
||||
case -1: # LEFT
|
||||
input_events.append(InputEvent(InputEvent.EventType.RELEASE_BUTTON, InputEvent.Button.BUTTON_RIGHT))
|
||||
input_events.append(InputEvent(InputEvent.EventType.PRESS_BUTTON, InputEvent.Button.BUTTON_LEFT))
|
||||
case 1: # RIGHT
|
||||
input_events.append(InputEvent(InputEvent.EventType.RELEASE_BUTTON, InputEvent.Button.BUTTON_LEFT))
|
||||
input_events.append(InputEvent(InputEvent.EventType.PRESS_BUTTON, InputEvent.Button.BUTTON_RIGHT))
|
||||
case _: # NONE
|
||||
input_events.append(InputEvent(InputEvent.EventType.RELEASE_BUTTON, InputEvent.Button.BUTTON_LEFT))
|
||||
input_events.append(InputEvent(InputEvent.EventType.RELEASE_BUTTON, InputEvent.Button.BUTTON_RIGHT))
|
||||
if event.type == evdev.ecodes.EV_ABS and event.code == evdev.ecodes.ABS_HAT0Y:
|
||||
match event.value:
|
||||
case -1: # UP
|
||||
input_events.append(InputEvent(InputEvent.EventType.RELEASE_BUTTON, InputEvent.Button.BUTTON_DOWN))
|
||||
input_events.append(InputEvent(InputEvent.EventType.PRESS_BUTTON, InputEvent.Button.BUTTON_UP))
|
||||
case 1: # DOWN
|
||||
input_events.append(InputEvent(InputEvent.EventType.RELEASE_BUTTON, InputEvent.Button.BUTTON_UP))
|
||||
input_events.append(InputEvent(InputEvent.EventType.PRESS_BUTTON, InputEvent.Button.BUTTON_DOWN))
|
||||
case _: # NONE
|
||||
input_events.append(InputEvent(InputEvent.EventType.RELEASE_BUTTON, InputEvent.Button.BUTTON_UP))
|
||||
input_events.append(InputEvent(InputEvent.EventType.RELEASE_BUTTON, InputEvent.Button.BUTTON_DOWN))
|
||||
if event.type == evdev.ecodes.EV_ABS and event.code == evdev.ecodes.ABS_Y:
|
||||
match event.value:
|
||||
case 0x0: # EMG
|
||||
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 9))
|
||||
case 0x5:
|
||||
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 8))
|
||||
case 0x13:
|
||||
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 7))
|
||||
case 0x20:
|
||||
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 6))
|
||||
case 0x2E:
|
||||
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 5))
|
||||
case 0x3C:
|
||||
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 4))
|
||||
case 0x49:
|
||||
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 3))
|
||||
case 0x57:
|
||||
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 2))
|
||||
case 0x65:
|
||||
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 1))
|
||||
case 0x80: # N
|
||||
input_events.append(InputEvent(InputEvent.EventType.BRAKE_NOTCH, 0))
|
||||
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 0))
|
||||
case 0x9F:
|
||||
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 1))
|
||||
case 0xB7:
|
||||
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 2))
|
||||
case 0xCE:
|
||||
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 3))
|
||||
case 0xE6:
|
||||
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 4))
|
||||
case 0xFF: # P5
|
||||
input_events.append(InputEvent(InputEvent.EventType.POWER_NOTCH, 5))
|
||||
return input_events
|
||||
except OSError:
|
||||
return [InputEvent(InputEvent.EventType.ERROR, None)]
|
96
ddgo-converter/gui/main.py
Executable file
96
ddgo-converter/gui/main.py
Executable file
|
@ -0,0 +1,96 @@
|
|||
import sys
|
||||
from PyQt5.QtCore import Qt, QTimer
|
||||
from PyQt5.QtWidgets import QMainWindow, QTableWidgetItem, QHeaderView
|
||||
import threading
|
||||
from gui.main_ui import Ui_MainWindow
|
||||
import gamepads.physical as gamepad_physical
|
||||
import gamepads.emulated as gamepad_emulated
|
||||
from models.gamepad import GamepadModel
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
def __init__(self, gamepad_handler):
|
||||
super().__init__()
|
||||
|
||||
self._gamepad_handler = gamepad_handler
|
||||
self._gui = Ui_MainWindow()
|
||||
self._gui.setupUi(self)
|
||||
self._emuthread = None
|
||||
self._emuthread_stop = threading.Event()
|
||||
|
||||
self.gamepad_model = GamepadModel(self._gamepad_handler.find_gamepads())
|
||||
self._gui.tableView_physicalControllerList.setModel(self.gamepad_model)
|
||||
self._gui.tableView_physicalControllerList.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeToContents)
|
||||
self._gui.tableView_physicalControllerList.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
|
||||
self._gui.tableView_physicalControllerList.horizontalHeader().setSectionResizeMode(2, QHeaderView.ResizeToContents)
|
||||
self._gui.tableView_physicalControllerList.selectionModel().selectionChanged.connect(self.controller_list_selection_changed)
|
||||
self._gui.tableView_physicalControllerList.selectRow(0)
|
||||
|
||||
self._gui.pushButton_physicalControllerRefresh.clicked.connect(self.controller_list_refresh)
|
||||
self._gui.pushButton_emulatedControllerStart.clicked.connect(self.controller_emulator_start)
|
||||
self._gui.pushButton_emulatedControllerStop.clicked.connect(self.controller_emulator_stop)
|
||||
|
||||
self._gui.pushButton_emulatedControllerStop.setVisible(False)
|
||||
self.populate_controller_combobox()
|
||||
|
||||
def closeEvent(self, event):
|
||||
self._emuthread_stop.set()
|
||||
|
||||
def populate_controller_combobox(self):
|
||||
self._gui.comboBox_emulatedControllerModel.addItem("PC two-handle controller (DGOC-44U)", gamepad_emulated.PC2HandleGamepad())
|
||||
self._gui.comboBox_emulatedControllerModel.setCurrentIndex(0)
|
||||
self._gui.comboBox_emulatedControllerModel.addItem("Sony PlayStation two-handle controller (SLPH-00051)", gamepad_emulated.PS1Gamepad())
|
||||
self._gui.comboBox_emulatedControllerModel.addItem("Nintendo 64 two-handle controller (TCPP-20003)", gamepad_emulated.N64Gamepad())
|
||||
self._gui.comboBox_emulatedControllerModel.addItem("SEGA Saturn two-handle controller (TC-5175290)", gamepad_emulated.SATGamepad())
|
||||
self._gui.comboBox_emulatedControllerModel.model().sort(0)
|
||||
|
||||
def controller_list_refresh(self):
|
||||
self.gamepad_model.beginResetModel()
|
||||
self.gamepad_model.gamepads = self._gamepad_handler.find_gamepads()
|
||||
self.gamepad_model.endResetModel()
|
||||
self.controller_list_selection_changed()
|
||||
|
||||
def controller_list_selection_changed(self):
|
||||
enabled = False
|
||||
rows = self._gui.tableView_physicalControllerList.selectionModel().selectedRows()
|
||||
if rows and self.gamepad_model.gamepads[rows[0].row()].type != gamepad_physical.PhysicalGamepad.GamepadType.UNKNOWN:
|
||||
enabled = True
|
||||
self._gui.pushButton_emulatedControllerStart.setEnabled(enabled)
|
||||
|
||||
def lock_interface(self,):
|
||||
self._gui.pushButton_emulatedControllerStart.setVisible(False)
|
||||
self._gui.pushButton_emulatedControllerStop.setVisible(True)
|
||||
self._gui.tableView_physicalControllerList.setEnabled(False)
|
||||
self._gui.pushButton_physicalControllerRefresh.setEnabled(False)
|
||||
self._gui.pushButton_physicalControllerConfig.setEnabled(False)
|
||||
self._gui.comboBox_emulatedControllerModel.setEnabled(False)
|
||||
|
||||
def unlock_interface(self,):
|
||||
self._gui.pushButton_emulatedControllerStart.setVisible(True)
|
||||
self._gui.pushButton_emulatedControllerStop.setVisible(False)
|
||||
self._gui.tableView_physicalControllerList.setEnabled(True)
|
||||
self._gui.pushButton_physicalControllerRefresh.setEnabled(True)
|
||||
self._gui.pushButton_physicalControllerConfig.setEnabled(True)
|
||||
self._gui.comboBox_emulatedControllerModel.setEnabled(True)
|
||||
|
||||
def controller_emulator_start(self):
|
||||
self.lock_interface()
|
||||
self._gui.statusbar.showMessage("Gamepad emulator running...")
|
||||
rows = self._gui.tableView_physicalControllerList.selectionModel().selectedRows()
|
||||
if rows:
|
||||
gamepad = self.gamepad_model.gamepads[rows[0].row()]
|
||||
emulated_gamepad = self._gui.comboBox_emulatedControllerModel.itemData(self._gui.comboBox_emulatedControllerModel.currentIndex())
|
||||
self._emuthread = threading.Thread(target=self._gamepad_handler.run_gamepad_emulator, args=(gamepad, emulated_gamepad, self._emuthread_stop))
|
||||
self._emuthread.start()
|
||||
|
||||
def controller_emulator_stop(self):
|
||||
self._gui.statusbar.showMessage("Stopping gamepad emulator...")
|
||||
self._emuthread_stop.set()
|
||||
self._emuthread.join(timeout=0.05)
|
||||
if self._emuthread.is_alive():
|
||||
QTimer.singleShot(50, self.controller_emulator_stop)
|
||||
return
|
||||
self._emuthread_stop.clear()
|
||||
self.unlock_interface()
|
||||
self.controller_list_refresh()
|
||||
self._gui.statusbar.showMessage("Gamepad emulator stopped successfully!", 5000)
|
||||
|
106
ddgo-converter/gui/main_ui.py
Normal file
106
ddgo-converter/gui/main_ui.py
Normal file
|
@ -0,0 +1,106 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'gui/main_ui.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.7
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_MainWindow(object):
|
||||
def setupUi(self, MainWindow):
|
||||
MainWindow.setObjectName("MainWindow")
|
||||
MainWindow.resize(800, 400)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth())
|
||||
MainWindow.setSizePolicy(sizePolicy)
|
||||
MainWindow.setMinimumSize(QtCore.QSize(800, 400))
|
||||
MainWindow.setMaximumSize(QtCore.QSize(800, 400))
|
||||
self.centralwidget = QtWidgets.QWidget(MainWindow)
|
||||
self.centralwidget.setObjectName("centralwidget")
|
||||
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
|
||||
self.gridLayout.setContentsMargins(9, 9, 9, 9)
|
||||
self.gridLayout.setObjectName("gridLayout")
|
||||
self.verticalLayout_main = QtWidgets.QVBoxLayout()
|
||||
self.verticalLayout_main.setObjectName("verticalLayout_main")
|
||||
self.groupBox_physicalControllers = QtWidgets.QGroupBox(self.centralwidget)
|
||||
self.groupBox_physicalControllers.setObjectName("groupBox_physicalControllers")
|
||||
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.groupBox_physicalControllers)
|
||||
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
||||
self.tableView_physicalControllerList = QtWidgets.QTableView(self.groupBox_physicalControllers)
|
||||
self.tableView_physicalControllerList.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
|
||||
self.tableView_physicalControllerList.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
||||
self.tableView_physicalControllerList.setObjectName("tableView_physicalControllerList")
|
||||
self.tableView_physicalControllerList.horizontalHeader().setHighlightSections(False)
|
||||
self.tableView_physicalControllerList.verticalHeader().setVisible(False)
|
||||
self.verticalLayout_2.addWidget(self.tableView_physicalControllerList)
|
||||
self.horizontalLayout_physicalControllerActions = QtWidgets.QHBoxLayout()
|
||||
self.horizontalLayout_physicalControllerActions.setObjectName("horizontalLayout_physicalControllerActions")
|
||||
self.pushButton_physicalControllerRefresh = QtWidgets.QPushButton(self.groupBox_physicalControllers)
|
||||
self.pushButton_physicalControllerRefresh.setObjectName("pushButton_physicalControllerRefresh")
|
||||
self.horizontalLayout_physicalControllerActions.addWidget(self.pushButton_physicalControllerRefresh)
|
||||
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||
self.horizontalLayout_physicalControllerActions.addItem(spacerItem)
|
||||
self.pushButton_physicalControllerConfig = QtWidgets.QPushButton(self.groupBox_physicalControllers)
|
||||
self.pushButton_physicalControllerConfig.setEnabled(False)
|
||||
self.pushButton_physicalControllerConfig.setObjectName("pushButton_physicalControllerConfig")
|
||||
self.horizontalLayout_physicalControllerActions.addWidget(self.pushButton_physicalControllerConfig)
|
||||
self.verticalLayout_2.addLayout(self.horizontalLayout_physicalControllerActions)
|
||||
self.verticalLayout_main.addWidget(self.groupBox_physicalControllers)
|
||||
self.groupBox_emulatedController = QtWidgets.QGroupBox(self.centralwidget)
|
||||
self.groupBox_emulatedController.setObjectName("groupBox_emulatedController")
|
||||
self.horizontalLayout = QtWidgets.QHBoxLayout(self.groupBox_emulatedController)
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
self.label_emulatedControllerModel = QtWidgets.QLabel(self.groupBox_emulatedController)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.label_emulatedControllerModel.sizePolicy().hasHeightForWidth())
|
||||
self.label_emulatedControllerModel.setSizePolicy(sizePolicy)
|
||||
self.label_emulatedControllerModel.setObjectName("label_emulatedControllerModel")
|
||||
self.horizontalLayout.addWidget(self.label_emulatedControllerModel)
|
||||
self.comboBox_emulatedControllerModel = QtWidgets.QComboBox(self.groupBox_emulatedController)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.comboBox_emulatedControllerModel.sizePolicy().hasHeightForWidth())
|
||||
self.comboBox_emulatedControllerModel.setSizePolicy(sizePolicy)
|
||||
self.comboBox_emulatedControllerModel.setInsertPolicy(QtWidgets.QComboBox.InsertAlphabetically)
|
||||
self.comboBox_emulatedControllerModel.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
|
||||
self.comboBox_emulatedControllerModel.setObjectName("comboBox_emulatedControllerModel")
|
||||
self.horizontalLayout.addWidget(self.comboBox_emulatedControllerModel)
|
||||
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||
self.horizontalLayout.addItem(spacerItem1)
|
||||
self.pushButton_emulatedControllerStart = QtWidgets.QPushButton(self.groupBox_emulatedController)
|
||||
self.pushButton_emulatedControllerStart.setEnabled(False)
|
||||
self.pushButton_emulatedControllerStart.setObjectName("pushButton_emulatedControllerStart")
|
||||
self.horizontalLayout.addWidget(self.pushButton_emulatedControllerStart)
|
||||
self.pushButton_emulatedControllerStop = QtWidgets.QPushButton(self.groupBox_emulatedController)
|
||||
self.pushButton_emulatedControllerStop.setObjectName("pushButton_emulatedControllerStop")
|
||||
self.horizontalLayout.addWidget(self.pushButton_emulatedControllerStop)
|
||||
self.verticalLayout_main.addWidget(self.groupBox_emulatedController)
|
||||
self.gridLayout.addLayout(self.verticalLayout_main, 0, 0, 1, 1)
|
||||
MainWindow.setCentralWidget(self.centralwidget)
|
||||
self.statusbar = QtWidgets.QStatusBar(MainWindow)
|
||||
self.statusbar.setObjectName("statusbar")
|
||||
MainWindow.setStatusBar(self.statusbar)
|
||||
|
||||
self.retranslateUi(MainWindow)
|
||||
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
||||
|
||||
def retranslateUi(self, MainWindow):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
MainWindow.setWindowTitle(_translate("MainWindow", "Densha de GO! Controller Converter"))
|
||||
self.groupBox_physicalControllers.setTitle(_translate("MainWindow", "Physical controllers"))
|
||||
self.pushButton_physicalControllerRefresh.setText(_translate("MainWindow", "Refresh"))
|
||||
self.pushButton_physicalControllerConfig.setText(_translate("MainWindow", "Configuration"))
|
||||
self.groupBox_emulatedController.setTitle(_translate("MainWindow", "Emulated controller"))
|
||||
self.label_emulatedControllerModel.setText(_translate("MainWindow", "Model"))
|
||||
self.pushButton_emulatedControllerStart.setText(_translate("MainWindow", "Start"))
|
||||
self.pushButton_emulatedControllerStop.setText(_translate("MainWindow", "Stop"))
|
185
ddgo-converter/gui/main_ui.ui
Normal file
185
ddgo-converter/gui/main_ui.ui
Normal file
|
@ -0,0 +1,185 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>400</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>800</width>
|
||||
<height>400</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>800</width>
|
||||
<height>400</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Densha de GO! Controller Converter</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_main">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_physicalControllers">
|
||||
<property name="title">
|
||||
<string>Physical controllers</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QTableView" name="tableView_physicalControllerList">
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SingleSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderHighlightSections">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_physicalControllerActions">
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_physicalControllerRefresh">
|
||||
<property name="text">
|
||||
<string>Refresh</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_physicalControllerActions">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_physicalControllerConfig">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Configuration</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_emulatedController">
|
||||
<property name="title">
|
||||
<string>Emulated controller</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_emulatedControllerModel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Model</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboBox_emulatedControllerModel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="insertPolicy">
|
||||
<enum>QComboBox::InsertAlphabetically</enum>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QComboBox::AdjustToContents</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_emulatedController">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_emulatedControllerStart">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Start</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_emulatedControllerStop">
|
||||
<property name="text">
|
||||
<string>Stop</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
29
ddgo-converter/handlers/gamepad.py
Executable file
29
ddgo-converter/handlers/gamepad.py
Executable file
|
@ -0,0 +1,29 @@
|
|||
from evdev import InputDevice, list_devices, ecodes as e, UInput, AbsInfo
|
||||
import threading
|
||||
import events.input as input_events
|
||||
import gamepads.physical as gamepad_physical
|
||||
import gamepads.emulated as gamepad_emulated
|
||||
|
||||
class GamepadHandler:
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def find_gamepads():
|
||||
gamepads = []
|
||||
devices = [InputDevice(path) for path in list_devices()]
|
||||
for device in devices:
|
||||
gamepads.append(gamepad_physical.create_gamepad(device.info.vendor, device.info.product, device.name))
|
||||
return gamepads
|
||||
|
||||
def run_gamepad_emulator(gamepad, emulated_gamepad, stop_event):
|
||||
gamepad.start()
|
||||
emulated_gamepad.start()
|
||||
while not stop_event.is_set():
|
||||
events = gamepad.read_input()
|
||||
if events is not None:
|
||||
for event in events:
|
||||
if event.type == input_events.InputEvent.EventType.ERROR:
|
||||
break
|
||||
emulated_gamepad.write_input(event)
|
||||
emulated_gamepad.stop()
|
||||
gamepad.stop()
|
35
ddgo-converter/models/gamepad.py
Normal file
35
ddgo-converter/models/gamepad.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
from PyQt5.QtCore import Qt, QVariant, QAbstractTableModel
|
||||
from gamepads.physical import PhysicalGamepad
|
||||
|
||||
headers = ["ID", "Name", "Status"]
|
||||
|
||||
class GamepadModel(QAbstractTableModel):
|
||||
def __init__(self, gamepads=None):
|
||||
super(GamepadModel, self).__init__()
|
||||
self.gamepads = gamepads or []
|
||||
|
||||
def data(self, index, role):
|
||||
if role == Qt.DisplayRole:
|
||||
match index.column():
|
||||
case 0:
|
||||
return self.gamepads[index.row()].id
|
||||
case 1:
|
||||
return self.gamepads[index.row()].name
|
||||
case 2:
|
||||
if self.gamepads[index.row()].type == PhysicalGamepad.GamepadType.UNKNOWN:
|
||||
return "Not configured"
|
||||
else:
|
||||
return "Configured"
|
||||
elif role == Qt.TextAlignmentRole and index.column() != 1:
|
||||
return Qt.AlignCenter
|
||||
|
||||
def rowCount(self, index):
|
||||
return len(self.gamepads)
|
||||
|
||||
def columnCount(self, index):
|
||||
return len(headers)
|
||||
|
||||
def headerData(self, section, orientation, role):
|
||||
if role != Qt.DisplayRole or orientation != Qt.Horizontal:
|
||||
return QVariant()
|
||||
return headers[section]
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
evdev
|
||||
PyQt5
|
Loading…
Add table
Reference in a new issue