1
0
forked from drath/libratbag

ratbagd: add macro support

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
Peter Hutterer
2017-07-25 13:22:37 +10:00
committed by Benjamin Tissoires
parent 129551f3f4
commit b7f53effa1
4 changed files with 190 additions and 16 deletions

View File

@@ -454,6 +454,91 @@ static int ratbagd_button_get_action_types(sd_bus *bus,
return sd_bus_message_close_container(reply);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ratbag_button_macro *, ratbag_button_macro_unref);
static int ratbagd_button_get_macro(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error)
{
struct ratbagd_button *button = userdata;
_cleanup_(ratbag_button_macro_unrefp) struct ratbag_button_macro *macro = NULL;
int r;
unsigned int idx;
r = sd_bus_message_open_container(reply, 'a', "(uu)");
if (r < 0)
return r;
macro = ratbag_button_get_macro(button->lib_button);
if (!macro)
goto out;
for (idx = 0; idx < ratbag_button_macro_get_num_events(macro); idx++) {
enum ratbag_macro_event_type type;
int value;
type = ratbag_button_macro_get_event_type(macro, idx);
switch (type) {
case RATBAG_MACRO_EVENT_INVALID:
abort();
break;
case RATBAG_MACRO_EVENT_NONE:
goto out;
case RATBAG_MACRO_EVENT_KEY_PRESSED:
case RATBAG_MACRO_EVENT_KEY_RELEASED:
value = ratbag_button_macro_get_event_key(macro, idx);
break;
case RATBAG_MACRO_EVENT_WAIT:
value = ratbag_button_macro_get_event_timeout(macro, idx);
break;
}
r = sd_bus_message_append(reply, "(uu)", type, value);
if (r < 0)
return r;
}
out:
return sd_bus_message_close_container(reply);
}
static int ratbagd_button_set_macro(sd_bus_message *m,
void *userdata,
sd_bus_error *error)
{
struct ratbagd_button *button = userdata;
unsigned int type, value;
int r, idx = 0;
_cleanup_(ratbag_button_macro_unrefp) struct ratbag_button_macro *macro = NULL;
r = sd_bus_message_enter_container(m, 'a', "(uu)");
if (r < 0)
return r;
macro = ratbag_button_macro_new("macro");
while ((r = sd_bus_message_read(m, "(uu)", &type, &value)) > 0) {
r = ratbag_button_macro_set_event(macro, idx++, type, value);
if (r < 0)
return r;
}
if (r < 0)
return r;
r = sd_bus_message_exit_container(m);
if (r < 0)
return r;
r = ratbag_button_set_macro(button->lib_button, macro);
if (r < 0)
return r;
return sd_bus_reply_method_return(m, "u", r);
}
static int ratbagd_button_disable(sd_bus_message *m,
void *userdata,
sd_bus_error *error)
@@ -479,9 +564,11 @@ const sd_bus_vtable ratbagd_button_vtable[] = {
SD_BUS_PROPERTY("KeyMapping", "au", ratbagd_button_get_key, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("ActionType", "u", ratbagd_button_get_action_type, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("ActionTypes", "au", ratbagd_button_get_action_types, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Macro", "a(uu)", ratbagd_button_get_macro, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_METHOD("SetButtonMapping", "u", "u", ratbagd_button_set_button, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetSpecialMapping", "s", "u", ratbagd_button_set_special, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetKeyMapping", "au", "u", ratbagd_button_set_key, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetMacro", "a(uu)", "u", ratbagd_button_set_macro, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Disable", "", "u", ratbagd_button_disable, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END,
};

View File

@@ -143,17 +143,28 @@ def print_button(d, p, b, level):
elif b.action_type == RatbagdButton.ACTION_TYPE_KEY:
bmap = ""
for m in b.key:
try:
code = evdev.ecodes.KEY[m]
except KeyError:
code = m
code = evdev.ecodes.KEY.get(m, m)
bmap = bmap + "{}".format(code)
print("{}'{}'".format(header, bmap))
elif b.action_type == RatbagdButton.ACTION_TYPE_SPECIAL:
print("{}'{}'".format(header, b.special))
elif b.action_type == RatbagdButton.ACTION_TYPE_MACRO:
print("{}macro".format(header))
bmap = ""
for t, v in b.macro:
bt = 'unknown'
if t == RatbagdButton.MACRO_KEY_PRESS:
bt = '+'
bv = evdev.ecodes.KEY.get(v, v)
elif t == RatbagdButton.MACRO_KEY_RELEASE:
bt = '-'
bv = evdev.ecodes.KEY.get(v, v)
elif t == RatbagdButton.MACRO_WAIT:
bt = 't'
bv = v
bmap = bmap + "{}{} ".format(bt, bv)
print("{}macro '{}'".format(header, bmap[:-1]))
elif b.action_type == RatbagdButton.ACTION_TYPE_NONE:
print("{}none".format(header))
else:
@@ -306,6 +317,44 @@ def func_button_action_set_button(r, args):
b.mapping = args.target_button
d.commit()
def func_button_action_set_macro(r, args):
b, p, d = find_button(r, args)
macro_keys = args.target_macro
macro = []
for s in macro_keys:
is_press = True
is_release = True
is_timeout = False
s = s.upper()
if s[0] == 'T':
is_timeout = True
is_press = False
is_release = False
elif s[0] == '+':
is_release = False
s = s[1:]
elif s[0] == '-':
is_press = False
s = s[1:]
if is_timeout:
t = int(s[1:])
macro.append((RatbagdButton.MACRO_WAIT, t))
else:
if not s.startswith("KEY_") and not s.startswith("BTN_"):
msg = "Don't know how to convert {}".format(s)
raise argparse.ArgumentTypeError(msg)
code = evdev.ecodes.ecodes[s]
if is_press:
macro.append((RatbagdButton.MACRO_KEY_PRESS, code))
if is_release:
macro.append((RatbagdButton.MACRO_KEY_RELEASE, code))
b.macro = macro
d.commit()
def func_button_count(r, args):
p, d = find_profile(r, args)
@@ -825,7 +874,7 @@ parser_def = [
nargs: argparse.REMAINDER,
},
],
func: func_dummy, # FIXME: func_button_action_set_macro,
func: func_button_action_set_macro,
},
]
},

View File

@@ -377,7 +377,7 @@ class TestRatbgagCtlButton(TestRatbagCtl):
# FIXME: special maps to UNKNOWN????
self.assertEqual(r, "Button: 2 type unknown is mapped to UNKNOWN")
r = self.launch_good_test("button 3 get test_device")
self.assertEqual(r, "Button: 3 type unknown is mapped to macro")
self.assertEqual(r, "Button: 3 type unknown is mapped to macro '+KEY_B -KEY_B t300'")
self.launch_fail_test("button 1 get 10 test_device")
self.launch_fail_test("button 10 get test_device")
self.launch_fail_test("button 1 get test_device X")
@@ -408,10 +408,22 @@ class TestRatbgagCtlButton(TestRatbagCtl):
pass
# FIXME
@unittest.skip("Button action set macro not implemented")
def test_button_n_action_set_macro(self):
pass
# FIXME
command = "button 2 action set macro "
self.launch_good_test(command + '+KEY_B KEY_A -KEY_B KEY_C test_device')
r = self.launch_good_test("button 2 get test_device")
self.assertEqual(r, "Button: 2 type unknown is mapped to macro '+KEY_B +KEY_A -KEY_A -KEY_B +KEY_C -KEY_C'")
self.launch_good_test(command + 't100 t200 test_device')
r = self.launch_good_test("button 2 get test_device")
self.assertEqual(r, "Button: 2 type unknown is mapped to macro 't100 t200'")
self.launch_good_test(command + 'KeY_z test_device')
r = self.launch_good_test("button 2 get test_device")
self.assertEqual(r, "Button: 2 type unknown is mapped to macro '+KEY_Z -KEY_Z'")
self.launch_good_test(command + ' test_device')
r = self.launch_good_test("button 2 get test_device")
self.assertEqual(r, "Button: 2 type unknown is mapped to macro ''")
self.launch_fail_test(command)
self.launch_fail_test(command + " KEY_A test_device X")
def test_prefix_button(self):
r = self.launch_good_test("Profile 2 Button 3 get test_device")

View File

@@ -147,7 +147,7 @@ class _RatbagdDBus(GObject.GObject):
return p.unpack()
return p
def _set_dbus_property(self, property, type, value):
def _set_dbus_property(self, property, type, value, readwrite=True):
# Sets a cached property on the bus.
# Take our real value and wrap it into a variant. To call
@@ -155,10 +155,11 @@ class _RatbagdDBus(GObject.GObject):
# into a (ssv), where v is our value's variant.
# args to .Set are "interface name", "function name", value-variant
val = GLib.Variant("{}".format(type), value)
pval = GLib.Variant("(ssv)".format(type), (self._interface, property, val))
self._proxy.call_sync("org.freedesktop.DBus.Properties.Set",
pval, Gio.DBusCallFlags.NO_AUTO_START,
500, None)
if readwrite:
pval = GLib.Variant("(ssv)".format(type), (self._interface, property, val))
self._proxy.call_sync("org.freedesktop.DBus.Properties.Set",
pval, Gio.DBusCallFlags.NO_AUTO_START,
500, None)
# This is our local copy, so we don't have to wait for the async
# update
@@ -435,6 +436,10 @@ class RatbagdButton(_RatbagdDBus):
ACTION_TYPE_KEY = 3
ACTION_TYPE_MACRO = 4
MACRO_KEY_PRESS = 1
MACRO_KEY_RELEASE = 2
MACRO_WAIT = 3
def __init__(self, object_path):
_RatbagdDBus.__init__(self, "Button", object_path)
@@ -463,6 +468,27 @@ class RatbagdButton(_RatbagdDBus):
self._set_dbus_property("ButtonMapping", "u", button)
return ret
@GObject.Property
def macro(self):
"""A list of (type, value) tuples that form the currently set macro,
where type is either RatbagdButton.KEY_PRESS, RatbagdButton.KEY_RELEASE
or RatbagdButton.MACRO_WAIT and the value is the keycode as specified
in linux/input.h for key related types, or the timeout in milliseconds."""
return self._get_dbus_property("Macro")
@macro.setter
def macro(self, macro):
"""Set the macro to the given macro. Note that the type must be one of
RatbagdButton.KEY_PRESS, RatbagdButton.KEY_RELEASE or
RatbagdButton.MACRO_WAIT and the keycodes must be as specified in
linux/input.h, and the timeout must be in milliseconds.
@param macro A list of (type, value) tuples to form the new macro.
"""
ret = self._dbus_call("SetMacro", "a(uu)", macro)
self._set_dbus_property("Macro", "a(uu)", macro, readwrite=False)
return ret
@GObject.Property
def special(self):
"""A string of the current special mapping, if mapped to special."""