diff --git a/glade/app.glade b/glade/app.glade index 48e21fe9f..2dae31692 100644 --- a/glade/app.glade +++ b/glade/app.glade @@ -645,6 +645,22 @@ 1 + + + 170 + False + True + True + + + + + + False + True + 2 + + 170 @@ -658,7 +674,7 @@ False True - 2 + 3 @@ -674,7 +690,7 @@ False True - 3 + 4 @@ -721,6 +737,22 @@ 1 + + + 170 + False + True + True + + + + + + False + True + 2 + + 170 @@ -734,7 +766,7 @@ False True - 2 + 3 @@ -750,7 +782,24 @@ False True - 3 + 4 + + + + + 42 + False + True + True + 6 + + + + + + False + True + 5 @@ -781,8 +830,40 @@ 1 + + + 220 + False + True + True + + + + + + False + True + 2 + + + 220 + False + True + True + + + + + + False + True + 3 + + + + 220 True True @@ -794,7 +875,7 @@ False True - 2 + 4 @@ -807,7 +888,7 @@ False True - 3 + 5 @@ -823,7 +904,7 @@ False True - 4 + 6 diff --git a/images/RSTICK.svg b/images/RSTICK.svg new file mode 120000 index 000000000..a539ba212 --- /dev/null +++ b/images/RSTICK.svg @@ -0,0 +1 @@ +button-images/LSTICK.bw.svg \ No newline at end of file diff --git a/images/button-images/DOTS.bw.svg b/images/button-images/DOTS.bw.svg new file mode 100644 index 000000000..0a08bd228 --- /dev/null +++ b/images/button-images/DOTS.bw.svg @@ -0,0 +1,85 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/images/button-images/DOTS.svg b/images/button-images/DOTS.svg new file mode 100644 index 000000000..1aa42bda3 --- /dev/null +++ b/images/button-images/DOTS.svg @@ -0,0 +1,77 @@ + + + +image/svg+xml diff --git a/images/button-images/DPAD.bw.svg b/images/button-images/DPAD.bw.svg new file mode 100644 index 000000000..e77cb7add --- /dev/null +++ b/images/button-images/DPAD.bw.svg @@ -0,0 +1,91 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/images/button-images/ELIPSE.bw.svg b/images/button-images/ELIPSE.bw.svg new file mode 100644 index 000000000..7762731ab --- /dev/null +++ b/images/button-images/ELIPSE.bw.svg @@ -0,0 +1,75 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/images/button-images/ELIPSE.svg b/images/button-images/ELIPSE.svg new file mode 100644 index 000000000..b421c8dac --- /dev/null +++ b/images/button-images/ELIPSE.svg @@ -0,0 +1,76 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/images/button-images/LGRIP2.svg b/images/button-images/LGRIP2.svg new file mode 100644 index 000000000..9f4906b99 --- /dev/null +++ b/images/button-images/LGRIP2.svg @@ -0,0 +1,83 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/images/button-images/MENU.svg b/images/button-images/MENU.svg new file mode 100644 index 000000000..0134c0317 --- /dev/null +++ b/images/button-images/MENU.svg @@ -0,0 +1,105 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/images/button-images/RGRIP2.svg b/images/button-images/RGRIP2.svg new file mode 100644 index 000000000..fd03edd44 --- /dev/null +++ b/images/button-images/RGRIP2.svg @@ -0,0 +1,106 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff --git a/images/button-images/RSTICK.bw.svg b/images/button-images/RSTICK.bw.svg new file mode 120000 index 000000000..8d53bd689 --- /dev/null +++ b/images/button-images/RSTICK.bw.svg @@ -0,0 +1 @@ +STICK.bw.svg \ No newline at end of file diff --git a/images/button-images/TOUCHPAD.svg b/images/button-images/TOUCHPAD.svg new file mode 100644 index 000000000..9f2c74f37 --- /dev/null +++ b/images/button-images/TOUCHPAD.svg @@ -0,0 +1,71 @@ + + + + + + + + + + image/svg+xml + + + + + + + + diff --git a/images/button-images/VIEW.svg b/images/button-images/VIEW.svg new file mode 100644 index 000000000..29e573399 --- /dev/null +++ b/images/button-images/VIEW.svg @@ -0,0 +1,96 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/images/controller-icons/deck-0.svg b/images/controller-icons/deck-0.svg new file mode 100644 index 000000000..7e9d3e14a --- /dev/null +++ b/images/controller-icons/deck-0.svg @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + diff --git a/images/controller-images/deck.svg b/images/controller-images/deck.svg new file mode 100644 index 000000000..fdf7910e0 --- /dev/null +++ b/images/controller-images/deck.svg @@ -0,0 +1,741 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/deck.config.json b/images/deck.config.json new file mode 100644 index 000000000..04422b767 --- /dev/null +++ b/images/deck.config.json @@ -0,0 +1,20 @@ +{ + "gui": { + "background": "deck", + "buttons": [ + "A", "B", "X", "Y", "VIEW", "ELIPSE", "MENU", "LB", "RB", "LT", "RT", + "STICK", "TOUCHPAD", "TOUCHPAD", "RGRIP", "LGRIP", "DOTS" + ] + }, + "buttons": [ + "DOTS", "RSTICKTOUCH", "LSTICKTOUCH", "RGRIP2", "LGRIP2", "RSTICKPRESS", + "LSTICKPRESS", "RPADTOUCH", "LPADTOUCH", "RPADPRESS", "LPADPRESS", "C", + "RGRIP", "LGRIP", "START", "BACK", "A", "X", "B", "Y", "LB", "RB" + ], + "axes": [ + "stick_x", "stick_y", "rstick_x", "rstick_y", + "lpad_x", "lpad_x", "rpad_y", "rpad_y", + "dpad_x", "dpad_y", "ltrig", "rtrig" + ] +} + diff --git a/scc/constants.py b/scc/constants.py index f0cc32be7..35d5f9812 100644 --- a/scc/constants.py +++ b/scc/constants.py @@ -44,6 +44,8 @@ LEFT = "LEFT" RIGHT = "RIGHT" CPAD = "CPAD" +RSTICK = "RSTICK" +DPAD = "DPAD" WHOLE = "WHOLE" STICK = "STICK" GYRO = "GYRO" diff --git a/scc/drivers/scc_future.h b/scc/drivers/scc_future.h index 1bf453559..0ec856630 100644 --- a/scc/drivers/scc_future.h +++ b/scc/drivers/scc_future.h @@ -11,50 +11,62 @@ typedef int16_t AxisValue; typedef int16_t GyroValue; typedef enum SCButton { - B_RPADTOUCH = 0b0010000000000000000000000000000, - B_LPADTOUCH = 0b0001000000000000000000000000000, - B_RPAD = 0b0000100000000000000000000000000, - B_LPAD = 0b0000010000000000000000000000000, - B_RGRIP = 0b0000001000000000000000000000000, - B_LGRIP = 0b0000000100000000000000000000000, - B_START = 0b0000000010000000000000000000000, - B_C = 0b0000000001000000000000000000000, - B_BACK = 0b0000000000100000000000000000000, - B_A = 0b0000000000000001000000000000000, - B_X = 0b0000000000000000100000000000000, - B_B = 0b0000000000000000010000000000000, - B_Y = 0b0000000000000000001000000000000, - B_LB = 0b0000000000000000000100000000000, - B_RB = 0b0000000000000000000010000000000, - B_LT = 0b0000000000000000000001000000000, - B_RT = 0b0000000000000000000000100000000, + B_RPADTOUCH = 0b000010000000000000000000000000000, + B_LPADTOUCH = 0b000001000000000000000000000000000, + B_RPADPRESS = 0b000000100000000000000000000000000, + B_LPADPRESS = 0b000000010000000000000000000000000, + B_RGRIP = 0b000000001000000000000000000000000, + B_LGRIP = 0b000000000100000000000000000000000, + B_START = 0b000000000010000000000000000000000, + B_C = 0b000000000001000000000000000000000, + B_BACK = 0b000000000000100000000000000000000, + B_A = 0b000000000000000001000000000000000, + B_X = 0b000000000000000000100000000000000, + B_B = 0b000000000000000000010000000000000, + B_Y = 0b000000000000000000001000000000000, + B_LB = 0b000000000000000000000100000000000, + B_RB = 0b000000000000000000000010000000000, + B_LT = 0b000000000000000000000001000000000, + B_RT = 0b000000000000000000000000100000000, // CPADTOUCH and CPADPRESS is used only on DS4 pad - B_CPADTOUCH = 0b0000000000000000000000000000100, - B_CPADPRESS = 0b0000000000000000000000000000010, - B_STICKPRESS = 0b1000000000000000000000000000000, + B_CPADTOUCH = 0b000000000000000000000000000000100, + B_CPADPRESS = 0b000000000000000000000000000000010, + B_STICKPRESS = 0b001000000000000000000000000000000, + B_RSTICKPRESS = 0b010000000000000000000000000000000, + // SteamDeck only buttons + B_DOTS = 0b000000000000000000000000000001000, + B_RGRIP2 = 0b000000000000000000000000000100000, + B_LGRIP2 = 0b000000000000000000000000000010000, _SCButton_padding = 0xFFFFFFFF // uint32_t } SCButton; - struct ControllerInput { - SCButton buttons; - TriggerValue ltrig; - TriggerValue rtrig; - AxisValue stick_x; - AxisValue stick_y; - AxisValue lpad_x; - AxisValue lpad_y; - AxisValue rpad_x; - AxisValue rpad_y; - AxisValue cpad_x; - AxisValue cpad_y; - GyroValue gpitch; - GyroValue groll; - GyroValue gyaw; - GyroValue q1; - GyroValue q2; - GyroValue q3; - GyroValue q4; + SCButton buttons; + union { + TriggerValue triggers[2]; + struct { + TriggerValue ltrig; + TriggerValue rtrig; + }; + }; + union { + AxisValue axes[12]; + struct { + AxisValue stick_x; + AxisValue stick_y; + AxisValue lpad_x; + AxisValue lpad_y; + AxisValue rpad_x; + AxisValue rpad_y; + AxisValue cpad_x; + AxisValue cpad_y; + AxisValue dpad_x; + AxisValue dpad_y; + AxisValue rstick_x; + AxisValue rstick_y; + }; + }; + struct GyroInput gyro; }; typedef struct Mapper Mapper; diff --git a/scc/drivers/steamdeck.py b/scc/drivers/steamdeck.py index 59e2e8532..4d241c19d 100644 --- a/scc/drivers/steamdeck.py +++ b/scc/drivers/steamdeck.py @@ -138,9 +138,15 @@ def disconnected(self): # Overrided to skip returning serial# to pool. pass + def get_type(self): + return "deck" + def __repr__(self): return "" % (self.get_id(),) + def get_gui_config_file(self): + return "deck.config.json" + def configure(self, idle_timeout=None, enable_gyros=None, led_level=None): FORMAT = b'>BBBB60x' # Timeout & Gyros diff --git a/scc/gui/action_editor.py b/scc/gui/action_editor.py index 167571f79..af98f42ff 100644 --- a/scc/gui/action_editor.py +++ b/scc/gui/action_editor.py @@ -1118,7 +1118,10 @@ def set_input(self, id, action, mode=None): self.hide_macro() self.hide_ring() elif id in STICKS: - self.set_title(_("Stick")) + if id == Profile.DPAD: + self.set_title(_("DPAD")) + else: + self.set_title(_("Stick")) self._set_mode(action, mode or Action.AC_STICK) self.set_action(action) self.hide_macro() diff --git a/scc/gui/app.py b/scc/gui/app.py index 20b821e23..3e3fd4a19 100644 --- a/scc/gui/app.py +++ b/scc/gui/app.py @@ -81,7 +81,8 @@ def __init__(self, gladepath="/usr/share/scc", self.outdated_version = None self.profile_switchers = [] self.test_mode_controller = None - self.current_file = None # Currently edited file + self.current_ui_layout = "default" # only "default" and "deck" are supported + self.current_file = None # Currently edited file self.controller_count = 0 self.current = Profile(GuiActionParser()) self.just_started = True @@ -162,11 +163,11 @@ def load_gui_config_for_controller(self, controller, first): config = controller.load_gui_config(self.imagepath or {}) else: config = {} - config = self.background.use_config(config) + config = self.background.use_config(config, controller=controller) def do_loading(): """ Called after transition is finished """ - self.background.use_config(config) + self.background.use_config(config, controller=controller) self.apply_gui_config_buttons(config) if first: @@ -188,6 +189,8 @@ def apply_gui_config_buttons(self, config): stckEditor = self.builder.get_object('stckEditor') grEditor = self.builder.get_object('grEditor') btCPAD = self.builder.get_object('btCPAD') + btDPAD = self.builder.get_object('btDPAD') + btGYRO = self.builder.get_object('btGYRO') btC = self.builder.get_object('btC') buttons = ControllerImage.get_names(config.get('buttons', {})) @@ -226,12 +229,51 @@ def apply_gui_config_buttons(self, config): if w: # TODO: Maybe actual detection w.set_sensitive(gyros) - for w in (btC, btCPAD): + + for w in (btC, btCPAD, btDPAD, btGYRO): w.set_visible(w.get_sensitive()) + + # Re-layout if needed + expected_layout = "default" + if len(axes) >= 8 and btC.get_sensitive(): + expected_layout = "deck" + + if expected_layout != self.current_ui_layout: + self.apply_ui_layout(expected_layout) + stckEditor.set_visible_child(grEditor) GLib.idle_add(self.on_c_size_allocate) + def apply_ui_layout(self, layout): + """ + Changes layout of ui elements to fit additional buttons needed for Deck + """ + if layout == "deck": + # Move 'C' button bellow LGRIP + btRGRIP = self.builder.get_object("btRGRIP") + btC = self.builder.get_object("btC") + btC.get_parent().remove(btC) + btC.set_margin_right(0) + btRGRIP.get_parent().pack_start(btC, False, True, 0) + btRGRIP.get_parent().reorder_child(btC, 5) + # Move 'GYRO' button to middle of image (where C was) + btGYRO = self.builder.get_object("btGYRO") + btGYRO.get_parent().remove(btGYRO) + vbC = self.builder.get_object("vbC") + vbC.pack_start(btGYRO, False, True, 0) + btGYRO.set_margin_top(30) + # Resize buttons at bottom + #for w in ['btSTICK', 'btRSTICK', 'btLPAD', 'btRPAD']: + # w.set_size_request(150, -1) + # Move 'DPAD' bellow 'LGRIP' + btLGRIP = self.builder.get_object("btLGRIP") + btDPAD = self.builder.get_object("btDPAD") + btDPAD.get_parent().remove(btDPAD) + btLGRIP.get_parent().pack_start(btDPAD, False, True, 6) + btLGRIP.get_parent().reorder_child(btDPAD, 5) + + def setup_statusicon(self): menu = self.builder.get_object("mnuTray") self.statusicon = get_status_icon(self.imagepath, menu) diff --git a/scc/gui/controller_image.py b/scc/gui/controller_image.py index 2b5bf3e57..f3c3ece1e 100644 --- a/scc/gui/controller_image.py +++ b/scc/gui/controller_image.py @@ -8,6 +8,7 @@ from scc.tools import _ from scc.gui.svg_widget import SVGWidget, SVGEditor +from scc.paths import get_share_path from scc.constants import SCButtons from scc.tools import nameof @@ -19,7 +20,8 @@ class ControllerImage(SVGWidget): DEFAULT = "sc" BUTTONS_WITH_IMAGES = ( SCButtons.A, SCButtons.B, SCButtons.X, SCButtons.Y, - SCButtons.BACK, SCButtons.C, SCButtons.START + SCButtons.BACK, SCButtons.C, SCButtons.START, + SCButtons.DOTS, ) DEFAULT_AXES = ( @@ -41,7 +43,7 @@ class ControllerImage(SVGWidget): def __init__(self, app, config=None): self.app = app self.backup = None - self.current = self._ensure_config({}) + self.current = self._ensure_config({}, None) filename = self._make_controller_image_path(ControllerImage.DEFAULT) SVGWidget.__init__(self, filename) if config: @@ -60,7 +62,7 @@ def get_config(self): return self.current - def _ensure_config(self, data): + def _ensure_config(self, data, controller): """ Ensure that required keys are present in config data """ data['gui'] = data.get('gui', {}) data['gui']['background'] = data['gui'].get("background", "sc") @@ -86,13 +88,13 @@ def get_names(dict_or_tuple): ] - def use_config(self, config, backup=None): + def use_config(self, config, backup=None, controller=None): """ Loads controller settings from provided config, adding default values when needed. Returns same config. """ self.backup = backup - self.current = self._ensure_config(config or {}) + self.current = self._ensure_config(config or {}, controller) self.set_image(os.path.join(self.app.imagepath, "controller-images/%s.svg" % (self.current["gui"]["background"], ))) if not self.current["gui"]["no_buttons_in_gui"]: @@ -154,6 +156,10 @@ def _fill_button_images(self, buttons): target_x, target_y = SVGEditor.get_translation(target) for i in xrange(len(ControllerImage.BUTTONS_WITH_IMAGES)): b = nameof(ControllerImage.BUTTONS_WITH_IMAGES[i]) + if b == "DOTS": + # How did I managed to create this kind of special case? -_- + i = 16 + path = None try: elm = SVGEditor.get_element(e, "AREA_%s" % (b,)) if elm is None: @@ -179,6 +185,7 @@ def _fill_button_images(self, buttons): img.attrib["id"] = b SVGEditor.add_element(target, img) except Exception, err: - log.warning("Failed to add image for button %s", b) + log.warning("Failed to add image for button %s (from %s)", b, path) log.exception(err) e.commit() + diff --git a/scc/gui/controller_widget.py b/scc/gui/controller_widget.py index b54745469..a39bd6523 100644 --- a/scc/gui/controller_widget.py +++ b/scc/gui/controller_widget.py @@ -11,7 +11,7 @@ from scc.tools import _ from gi.repository import Gtk, Gdk, Pango -from scc.constants import SCButtons, STICK, GYRO, LEFT, RIGHT +from scc.constants import SCButtons, STICK, RSTICK, GYRO, LEFT, RIGHT from scc.actions import Action, XYAction, MultiAction from scc.gui.ae.gyro_action import is_gyro_enable from scc.modifiers import DoubleclickModifier @@ -23,7 +23,7 @@ TRIGGERS = [ "LT", "RT" ] PADS = [ Profile.LPAD, Profile.RPAD, Profile.CPAD ] -STICKS = [ STICK ] +STICKS = [ STICK, Profile.RSTICK, Profile.DPAD ] GYROS = [ GYRO ] PRESSABLE = [ SCButtons.LPAD, SCButtons.RPAD, SCButtons.STICKPRESS, SCButtons.CPADPRESS ] @@ -34,7 +34,7 @@ class ControllerWidget: ACTION_CONTEXT = None - + def __init__(self, app, id, use_icon, widget): self.app = app self.id = id @@ -161,13 +161,17 @@ def on_cursor_motion(self, trash, event): # self.icon.get_allocation().x + self.icon.get_allocation().width # yields nonsense ix2 = 74 # Check if cursor is placed on icon + what = None if event.x < ix2: what = { Profile.LPAD : LEFT, Profile.RPAD : RIGHT, Profile.CPAD : nameof(SCButtons.CPADPRESS), Profile.STICK : nameof(SCButtons.STICKPRESS), - }[self.name] + Profile.RSTICK : nameof(SCButtons.RSTICKPRESS), + Profile.DPAD: None, + }.get(self.name) + if what: self.app.hilight(what) self.over_icon = True else: diff --git a/scc/gui/daemon_manager.py b/scc/gui/daemon_manager.py index 5c0810a0a..6e8b264b6 100644 --- a/scc/gui/daemon_manager.py +++ b/scc/gui/daemon_manager.py @@ -12,6 +12,7 @@ from scc.tools import find_binary, find_button_image, nameof from scc.paths import get_daemon_socket +from scc.constants import SCButtons from scc.gui import BUTTON_ORDER from gi.repository import GObject, Gio, GLib @@ -93,7 +94,7 @@ def get_controllers(self): return [] + self._controllers - def get_controller(self, controller_id): + def get_controller(self, controller_id, type=None): """ Returns ControllerManager instance bound to provided controller_id. Note that this method will return instance for any controller_id, @@ -102,8 +103,11 @@ def get_controller(self, controller_id): For same controller_id, there is always same instance returned. """ if controller_id not in self._controller_by_id: - self._controller_by_id[controller_id] = ControllerManager(self, controller_id) - return self._controller_by_id[controller_id] + c = self._controller_by_id[controller_id] = ControllerManager(self, controller_id, type) + else: + c = self._controller_by_id[controller_id] + c.set_type(type) + return c def has_controller(self): @@ -193,7 +197,7 @@ def _on_read_data(self, sc, results): error_cb(line[5:].strip()) elif line.startswith("Controller:"): controller_id, type, flags, config_file = line[11:].strip().split(" ", 3) - c = self.get_controller(controller_id) + c = self.get_controller(controller_id, type) c._connected = True c._type = type c._flags = long(flags) @@ -343,18 +347,19 @@ class ControllerManager(GObject.GObject): } DEFAULT_ICONS = [ "A", "B", "X", "Y", "BACK", "C", "START", - "LB", "RB", "LT", "RT", "LG", "RG" ] + "LB", "RB", "LT", "RT", "STICK", "LPAD", "RPAD", "RGRIP", "LGRIP", "DOTS" ] # ^^ those are icon names - def __init__(self, daemon_manager, controller_id): + def __init__(self, daemon_manager, controller_id, controller_type): GObject.GObject.__init__(self) self._dm = daemon_manager self._controller_id = controller_id + self._type = controller_type self._config_file = None + self._connected = False self._profile = None self._type = None self._flags = 0 - self._connected = False def __repr__(self): @@ -386,6 +391,12 @@ def get_type(self): """ return self._type + def set_type(self, type): + """ + Sets type, if none is yet set. + """ + if self._type is None: + self._type = type def get_flags(self): """ @@ -430,7 +441,6 @@ def load_gui_config(self, default_path): log.exception(e) return None - @staticmethod def get_button_icon(config, button, prefer_bw=False): """ @@ -450,7 +460,10 @@ def get_button_name(config, button): As get_button_icon, but returns icon name instead of filename. """ name = nameof(button) + index = -1 try: + if type(button) is str: + button = SCButtons.__members__[button] index = BUTTON_ORDER.index(button) name = ControllerManager.DEFAULT_ICONS[index] name = config['gui']['buttons'][index] diff --git a/scc/gui/profile_switcher.py b/scc/gui/profile_switcher.py index ab53bb618..e8d6fdccc 100644 --- a/scc/gui/profile_switcher.py +++ b/scc/gui/profile_switcher.py @@ -162,6 +162,8 @@ def set_profile_list(self, lst): i, current_index = 0, 0 for f in sorted(lst, key=lambda f: f.get_basename()): name = f.get_basename().decode("utf-8") + if type(name) is str: + name = name.decode("utf-8") if name.endswith(".mod"): continue if name.startswith("."): diff --git a/scc/profile.py b/scc/profile.py index 4f9bb7f09..23a32fe8a 100644 --- a/scc/profile.py +++ b/scc/profile.py @@ -6,7 +6,7 @@ """ from __future__ import unicode_literals -from scc.constants import LEFT, RIGHT, CPAD, WHOLE, STICK, GYRO +from scc.constants import LEFT, RIGHT, CPAD, DPAD, WHOLE, STICK, RSTICK, GYRO from scc.constants import SCButtons, HapticPos from scc.special_actions import MenuAction from scc.modifiers import HoldModifier @@ -28,8 +28,10 @@ class Profile(object): LPAD = SCButtons.LPAD.name RPAD = SCButtons.RPAD.name CPAD = CPAD + DPAD = DPAD WHOLE = WHOLE STICK = STICK + RSTICK = RSTICK GYRO = GYRO X, Y, Z = "X", "Y", "Z" STICK_AXES = { X : "lpad_x", Y : "lpad_y" }