gamepad config via gui

pull/14/head
sezanzeb 4 years ago
parent 80a7e6b0ee
commit c768e2625b

@ -8,6 +8,7 @@
<p align="center"><img src="readme/pylint.svg"/> <img src="readme/coverage.svg"/></p>
<p align="center"><img src="readme/screenshot.png"/></p>
<p align="center"><img src="readme/screenshot_2.png"/></p>
<br/>
## Usage

@ -254,6 +254,12 @@
<property name="can-focus">False</property>
<property name="stock">gtk-redo</property>
</object>
<object class="GtkAdjustment" id="mouse_speed_adjustment">
<property name="lower">2</property>
<property name="upper">9</property>
<property name="step-increment">1</property>
<property name="page-increment">10</property>
</object>
<object class="GtkImage" id="new-icon">
<property name="visible">True</property>
<property name="can-focus">False</property>
@ -268,7 +274,7 @@
<property name="width-request">700</property>
<property name="can-focus">False</property>
<property name="title" translatable="yes">Key Mapper</property>
<property name="default-height">350</property>
<property name="default-height">400</property>
<property name="icon-name">mouse</property>
<signal name="delete-event" handler="on_close" swapped="no"/>
<child>
@ -528,7 +534,7 @@
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">To automatically apply the preset after your login if the device is alread connected.</property>
<property name="label" translatable="yes">Automatically load this preset</property>
<property name="label" translatable="yes">Autoload</property>
<property name="xalign">0</property>
</object>
<packing>
@ -556,6 +562,145 @@
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkBox" id="gamepad_config">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">10</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="spacing">10</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Left joystick</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="left_joystick_purpose">
<property name="width-request">100</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<items>
<item id="mouse" translatable="yes">Mouse</item>
<item id="wheel" translatable="yes">Wheel</item>
</items>
<signal name="changed" handler="on_left_joystick_purpose_changed" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="spacing">10</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Right joystick</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="right_joystick_purpose">
<property name="width-request">100</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<items>
<item id="mouse" translatable="yes">Mouse</item>
<item id="wheel" translatable="yes">Wheel</item>
</items>
<signal name="changed" handler="on_right_joystick_purpose_changed" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="spacing">10</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Mouse speed</property>
<property name="width-chars">13</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScale" id="joystick_mouse_speed">
<property name="width-request">0</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="adjustment">mouse_speed_adjustment</property>
<property name="round-digits">1</property>
<property name="draw-value">False</property>
<signal name="value-changed" handler="on_joystick_mouse_speed_change_value" swapped="no"/>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">6</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>

@ -28,7 +28,7 @@ import time
import asyncio
import evdev
from evdev.ecodes import EV_KEY, EV_ABS
from evdev.ecodes import EV_KEY, EV_ABS, ABS_X
from keymapper.logger import logger
@ -81,28 +81,34 @@ class _GetDevices(threading.Thread):
# only keyboard devices
# https://www.kernel.org/doc/html/latest/input/event-codes.html
capabilities = device.capabilities().keys()
capabilities = device.capabilities(absinfo=False)
if EV_KEY not in capabilities and EV_ABS not in capabilities:
# or gamepads, because they can be mapped like a keyboard
continue
is_gamepad = False
if ABS_X in capabilities.get(EV_ABS, []):
is_gamepad = True
usb = device.phys.split('/')[0]
if grouped.get(usb) is None:
grouped[usb] = []
logger.spam('Found "%s", %s, %s', device.name, device.path, usb)
grouped[usb].append((device.name, device.path))
grouped[usb].append((device.name, device.path, is_gamepad))
# now write down all the paths of that group
result = {}
for group in grouped.values():
names = [entry[0] for entry in group]
devs = [entry[1] for entry in group]
is_gamepad = True in [entry[2] for entry in group]
shortest_name = sorted(names, key=len)[0]
result[shortest_name] = {
'paths': devs,
'devices': names
'devices': names,
'gamepad': is_gamepad
}
self.pipe.send(result)

@ -23,6 +23,8 @@
import evdev
import math
from evdev.ecodes import EV_KEY
from gi.repository import Gtk, Gdk, GLib
@ -152,6 +154,25 @@ class Window:
# now show the proper finished content of the window
self.get('vertical-wrapper').set_opacity(1)
def initialize_gamepad_config(self):
"""Set slider and dropdown values when a gamepad is selected."""
devices = get_devices()
if devices[self.selected_device]['gamepad']:
self.get('gamepad_config').show()
else:
self.get('gamepad_config').hide()
return
left_purpose = custom_mapping.get('gamepad.joystick.left_purpose')
self.get('left_joystick_purpose').set_active_id(left_purpose)
right_purpose = custom_mapping.get('gamepad.joystick.right_purpose')
self.get('right_joystick_purpose').set_active_id(right_purpose)
pointer_speed = custom_mapping.get('gamepad.joystick.pointer_speed')
range_value = math.log(pointer_speed, 2)
self.get('joystick_mouse_speed').set_value(range_value)
def get(self, name):
"""Get a widget from the window"""
return self.builder.get_object(name)
@ -468,6 +489,23 @@ class Window:
self.get('preset_name_input').set_text('')
self.add_empty()
self.initialize_gamepad_config()
def on_left_joystick_purpose_changed(self, dropdown):
"""Set the purpose of the left joystick."""
purpose = dropdown.get_active_id()
custom_mapping.set('gamepad.joystick.left_purpose', purpose)
def on_right_joystick_purpose_changed(self, dropdown):
"""Set the purpose of the right joystick."""
purpose = dropdown.get_active_id()
custom_mapping.set('gamepad.joystick.right_purpose', purpose)
def on_joystick_mouse_speed_change_value(self, range):
"""Set how fast the joystick moves the mouse."""
speed = 2 ** range.get_value()
custom_mapping.set('gamepad.joystick.pointer_speed', speed)
def add_empty(self):
"""Add one empty row for a single mapped key."""
empty = Row(

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

@ -46,19 +46,23 @@ class TestGetDevices(unittest.TestCase):
'device 1 foo',
'device 1',
'device 1'
]
],
'gamepad': False
},
'device 2': {
'paths': ['/dev/input/event20'],
'devices': ['device 2']
'paths': ['/dev/input/event20'],
'devices': ['device 2'],
'gamepad': False
},
'gamepad': {
'paths': ['/dev/input/event30'],
'devices': ['gamepad']
'devices': ['gamepad'],
'gamepad': True
},
'key-mapper device 2': {
'paths': ['/dev/input/event40'],
'devices': ['key-mapper device 2']
'paths': ['/dev/input/event40'],
'devices': ['key-mapper device 2'],
'gamepad': False
},
})
self.assertDictEqual(pipe.devices, get_devices(include_keymapper=True))
@ -75,15 +79,18 @@ class TestGetDevices(unittest.TestCase):
'device 1 foo',
'device 1',
'device 1'
]
],
'gamepad': False
},
'device 2': {
'paths': ['/dev/input/event20'],
'devices': ['device 2']
'paths': ['/dev/input/event20'],
'devices': ['device 2'],
'gamepad': False
},
'gamepad': {
'paths': ['/dev/input/event30'],
'devices': ['gamepad']
'devices': ['gamepad'],
'gamepad': True
},
})

@ -38,7 +38,7 @@ from gi.repository import Gtk
from keymapper.state import custom_mapping, system_mapping
from keymapper.paths import CONFIG, get_config_path
from keymapper.config import config
from keymapper.config import config, WHEEL, MOUSE
from keymapper.dev.reader import keycode_reader
from keymapper.gtk.row import to_string
from keymapper.dev import permissions
@ -496,6 +496,37 @@ class TestIntegration(unittest.TestCase):
sorted(['abc 123.json', 'new preset 2.json'])
)
def test_gamepad_config(self):
# select a device that is not a gamepad
self.window.on_select_device(FakeDropdown('device 1'))
self.assertFalse(self.window.get('gamepad_config').is_visible())
# select a gamepad
self.window.on_select_device(FakeDropdown('gamepad'))
self.assertTrue(self.window.get('gamepad_config').is_visible())
# set stuff
self.window.get('left_joystick_purpose').set_active_id(WHEEL)
self.window.get('right_joystick_purpose').set_active_id(WHEEL)
joystick_mouse_speed = 5
self.window.get('joystick_mouse_speed').set_value(joystick_mouse_speed)
# it should be stored in custom_mapping, which overwrites the
# global config
config.set('gamepad.joystick.left_purpose', MOUSE)
config.set('gamepad.joystick.right_purpose', MOUSE)
config.set('gamepad.joystick.pointer_speed', 50)
left_purpose = custom_mapping.get('gamepad.joystick.left_purpose')
right_purpose = custom_mapping.get('gamepad.joystick.right_purpose')
pointer_speed = custom_mapping.get('gamepad.joystick.pointer_speed')
self.assertEqual(left_purpose, WHEEL)
self.assertEqual(right_purpose, WHEEL)
self.assertEqual(pointer_speed, 2 ** joystick_mouse_speed)
# select a device that is not a gamepad again
self.window.on_select_device(FakeDropdown('device 1'))
self.assertFalse(self.window.get('gamepad_config').is_visible())
def test_start_injecting(self):
keycode_from = 9
keycode_to = 200

Loading…
Cancel
Save