def remapRemoteControl(self, device): filename = rc_model.getRcPositions() domRemote = self.loadRemoteControl(filename) logRemaps = [] remapButtons = {} if domRemote is not None: rc = domRemote.find("rc") if rc is not None: for button in rc.findall("button"): keyid = KEYIDS.get(button.attrib.get("keyid")) remap = KEYIDS.get(button.attrib.get("remap")) if keyid is not None and remap is not None: logRemaps.append((button.attrib.get("keyid"), button.attrib.get("remap"))) remapButtons[keyid] = remap if len(logRemaps): print("[InputDevice] Remapping remote control buttons for '%s':" % filename) for remap in logRemaps: print("[InputDevice] Remapping '%s' to '%s'." % (remap[0], remap[1])) for evdev, evdevinfo in iInputDevices.Devices.items(): if evdevinfo["type"] == "remote": res = eRCInput.getInstance().setKeyMapping(evdevinfo["name"], remapButtons) resStr = { eRCInput.remapOk: "Remap completed okay.", eRCInput.remapUnsupported: "Error: Remapping not supported on device!", eRCInput.remapFormatErr: "Error: Remap map in incorrect format!", eRCInput.remapNoSuchDevice: "Error: Unknown device!", }.get(res, "Error: Unknown error!") print("[InputDevice] Remote remap evdev='%s', name='%s': %s" % (evdev, evdevinfo["name"], resStr))
def __init__(self): self["rc"] = Pixmap() self.rcPosition = None buttonImages = 16 rcHeights = (500, ) * 2 self.selectPics = [] for indicator in range(buttonImages): self.selectPics.append( self.KeyIndicator( self, rcHeights, ("indicatorU%d" % indicator, "indicatorL%d" % indicator))) self.nSelectedKeys = 0 self.oldNSelectedKeys = 0 self.clearSelectedKeys() self.wizardConversion = { # This dictionary converts named buttons in the Wizards to keyIds. "OK": KEYIDS.get("KEY_OK"), "EXIT": KEYIDS.get("KEY_EXIT"), "LEFT": KEYIDS.get("KEY_LEFT"), "RIGHT": KEYIDS.get("KEY_RIGHT"), "UP": KEYIDS.get("KEY_UP"), "DOWN": KEYIDS.get("KEY_DOWN"), "RED": KEYIDS.get("KEY_RED"), "GREEN": KEYIDS.get("KEY_GREEN"), "YELLOW": KEYIDS.get("KEY_YELLOW"), "BLUE": KEYIDS.get("KEY_BLUE") } self.onLayoutFinish.append(self.initRemoteControl)
def loadRemoteControl(self, filename): print("[InputDevice] Loading remote control '%s'." % filename) rcs = fileReadXML(filename, source=MODULE_NAME) rcButtons = {} if rcs: rc = rcs.find("rc") if rc: logRemaps = [] remapButtons = {} placeHolder = 0 rcButtons["keyIds"] = [] rcButtons["image"] = rc.attrib.get("image") if config.crash.debugRemoteControls.value: print("[InputDevice] Remote control image file '%s'." % rcButtons["image"]) for button in rc.findall("button"): id = button.attrib.get("id", "KEY_RESERVED") remap = button.attrib.get("remap") keyId = KEYIDS.get(id) remapId = KEYIDS.get(remap) if keyId is not None and remapId is not None: logRemaps.append((id, remap)) remapButtons[keyId] = remapId keyId = remapId if keyId == 0: placeHolder -= 1 keyId = placeHolder rcButtons["keyIds"].append(keyId) rcButtons[keyId] = {} rcButtons[keyId]["id"] = id rcButtons[keyId]["label"] = button.attrib.get("label") rcButtons[keyId]["pos"] = [int(x.strip()) for x in button.attrib.get("pos", "0").split(",")] rcButtons[keyId]["title"] = button.attrib.get("title") rcButtons[keyId]["shape"] = button.attrib.get("shape") rcButtons[keyId]["coords"] = [int(x.strip()) for x in button.attrib.get("coords", "0").split(",")] if config.crash.debugRemoteControls.value: print("[InputDevice] Remote control button id='%s', keyId='%s', label='%s', pos='%s', title='%s', shape='%s', coords='%s'." % (id, keyId, rcButtons[keyId]["label"], rcButtons[keyId]["pos"], rcButtons[keyId]["title"], rcButtons[keyId]["shape"], rcButtons[keyId]["coords"])) if logRemaps: for remap in logRemaps: print("[InputDevice] Remapping '%s' to '%s'." % (remap[0], remap[1])) for evdev, evdevinfo in sorted(inputDevices.devices.items()): if evdevinfo["type"] == "remote": result = eRCInput.getInstance().setKeyMapping(evdevinfo["name"], remapButtons) resStr = { eRCInput.remapOk: "Remap completed okay.", eRCInput.remapUnsupported: "Error: Remapping not supported on device!", eRCInput.remapFormatErr: "Error: Remap map in incorrect format!", eRCInput.remapNoSuchDevice: "Error: Unknown device!", }.get(result, "Error: Unknown error!") print("[InputDevice] Remote remap evdev='%s', name='%s': %s" % (evdev, evdevinfo["name"], resStr)) return rcButtons
def selectionChanged(self): self.clearSelectedKeys() selection = self["list"].getCurrent() if selection: baseButtons = [] longButtons = [] shiftButtons = [] buttonList = [] for button in selection[3]: label = remoteControl.getRemoteControlKeyLabel(button[0]) if label is None: label = "Note: No button defined for this action!" if len(button) > 1: if button[1] == "SHIFT": self.selectKey(KEYIDS.get("KEY_SHIFT")) shiftButtons.append(label) elif button[1] == "LONG": longButtons.append(label) else: baseButtons.append(label) self.selectKey(button[0]) if baseButtons: buttonList.append( pgettext("Text list separator", ", ").join(sorted(baseButtons))) if longButtons: buttonList.append( _("Long press: %s") % pgettext( "Text list separator", ", ").join(sorted(longButtons))) if shiftButtons: buttonList.append( _("Shift: %s") % pgettext("Text list separator", ", ").join(sorted(shiftButtons))) self["buttonlist"].setText("; ".join(buttonList)) helpText = selection[4] self["description"].setText( isinstance(helpText, (list, tuple)) and len(helpText) > 1 and helpText[1] or "")
def __init__(self, helpList, callback): List.__init__(self) self.callback = callback self.rcKeyIndex = None self.buttonMap = {} self.longSeen = False formatFlags = 0 def actMapId(): return getattr(actionmap, "description", None) or id(actionmap) headings, sortCmp, sortKey = { "headings+alphabetic": (True, None, self._sortKeyAlpha), "flat+alphabetic": (False, None, self._sortKeyAlpha), "flat+remotepos": (False, self._sortCmpPos, None), "flat+remotegroups": (False, self._sortCmpInd, None) }.get(config.usage.helpSortOrder.value, (False, None, None)) if remoteControl is None: if sortCmp in (self._sortCmpPos, self._sortCmpInd): sortCmp = None else: if sortCmp == self._sortCmpInd: self.rcKeyIndex = dict((x[1], x[0]) for x in enumerate( remoteControl.getRemoteControlKeyList())) buttonsProcessed = set() helpSeen = defaultdict(list) sortedHelpList = sorted(helpList, key=lambda hle: hle[0].prio) actionMapHelp = defaultdict(list) for (actionmap, context, actions) in sortedHelpList: # print("[HelpMenu] HelpMenuList DEBUG: actionmap='%s', context='%s', actions='%s'." % (str(actionmap), context, str(actions))) if not actionmap.enabled: # print("[HelpMenu] Action map disabled.") continue amId = actMapId() if headings and actionmap.description and not (formatFlags & self.HEADINGS): # print("[HelpMenu] HelpMenuList DEBUG: Headings found.") formatFlags |= self.HEADINGS for (action, help) in actions: # DEBUG: Should help be response? helpTags = [ ] # if mapFlag else [pgettext("Abbreviation of 'Disabled'", "Disabled")] if callable(help): help = help() helpTags.append( pgettext("Abbreviation of 'Configurable'", "Configurable")) if help is None: # print("[HelpMenu] HelpMenuList DEBUG: No help text found.") # help = _("No help text available") continue buttons = queryKeyBinding(context, action) # print("[HelpMenu] HelpMenuList DEBUG: queryKeyBinding buttons=%s." % str(buttons)) if not buttons: # Do not display entries which are not accessible from keys. # print("[HelpMenu] HelpMenuList DEBUG: No buttons allocated.") # helpTags.append(pgettext("Abbreviation of 'Unassigned'", "Unassigned")) continue buttonLabels = [] for keyId, flags in buttons: if remoteControl.getRemoteControlKeyPos(keyId): buttonLabels.append( (keyId, "LONG") if flags & 8 else (keyId, ) ) # For long keypresses, make the second tuple item "LONG". if not buttonLabels: # Only show entries with keys that are available on the used rc. # print("[HelpMenu] HelpMenuList DEBUG: Button not available on current remote control.") # helpTags.append(pgettext("Abbreviation of 'No Button'", "No Button")) continue isExtended = isinstance(help, (list, tuple)) if isExtended and not (formatFlags & self.EXTENDED): # print("[HelpMenu] HelpMenuList DEBUG: Extended help entry found.") formatFlags |= self.EXTENDED if helpTags: helpStr = help[0] if isExtended else help tagsStr = pgettext("Text list separator", ", ").join(helpTags) helpStr = _("%s (%s)") % (helpStr, tagsStr) help = [helpStr, help[1]] if isExtended else helpStr entry = [(actionmap, context, action, buttonLabels, help), help] if self._filterHelpList(entry, helpSeen): actionMapHelp[actMapId()].append(entry) helpMenuList = [] extendedPadding = (None, ) if formatFlags & self.EXTENDED else () for (actionmap, context, actions) in helpList: amId = actMapId() if headings and amId in actionMapHelp and getattr( actionmap, "description", None): if sortCmp: actionMapHelp[amId].sort(key=cmp_to_key(sortCmp)) elif sortKey: actionMapHelp[amId].sort(key=sortKey) self.addListBoxContext(actionMapHelp[amId], formatFlags) helpMenuList.append((None, actionmap.description, None) + extendedPadding) helpMenuList.extend(actionMapHelp[amId]) del actionMapHelp[amId] if actionMapHelp: if formatFlags & self.HEADINGS: # Add a header if other actionmaps have descriptions. helpMenuList.append((None, _("Other Actions"), None) + extendedPadding) otherHelp = [] for (actionmap, context, actions) in helpList: amId = actMapId() if amId in actionMapHelp: otherHelp.extend(actionMapHelp[amId]) del actionMapHelp[amId] if sortCmp: otherHelp.sort(key=cmp_to_key(sortCmp)) elif sortKey: otherHelp.sort(key=sortKey) self.addListBoxContext(otherHelp, formatFlags) helpMenuList.extend(otherHelp) ignoredKeyIds = (KEYIDS.get("KEY_OK"), KEYIDS.get("KEY_EXIT")) for index, entry in enumerate(helpMenuList): if entry[0] and entry[0][3]: # This should not be required. for button in entry[0][3]: if button[0] not in ( ignoredKeyIds ): # Ignore "break" events from OK and EXIT on return from help popup. self.buttonMap[button] = index self.style = ( "default", "default+headings", "extended", "extended+headings", )[formatFlags] # [(actionmap, context, [(action, help), (action, help), ...]), ...] # [((ActionMap, Context, Action, [(Button, Device/Long), ...], HelpText), HelpText), ...] self.list = helpMenuList
def parseKeymap(filename, context, actionMapInstance, device, domKeys): unmapDict = {} error = False keyId = -1 for key in domKeys.findall("key"): keyName = key.attrib.get("id") if keyName is None: print("[ActionMap] Error: Keymap attribute 'id' in context '%s' in file '%s' must be specified!" % (context, filename)) error = True else: try: if len(keyName) == 1: keyId = ord(keyName) | 0x8000 elif keyName[0] == "\\": if keyName[1].lower() == "x": keyId = int(keyName[2:], 16) | 0x8000 elif keyName[1].lower() == "d": keyId = int(keyName[2:], 10) | 0x8000 elif keyName[1].lower() == "o": keyId = int(keyName[2:], 8) | 0x8000 elif keyName[1].lower() == "b": keyId = int(keyName[2:], 2) | 0x8000 else: print("[ActionMap] Error: Keymap id '%s' in context '%s' in file '%s' is not a hex, decimal, octal or binary number!" % (keyName, context, filename)) error = True else: keyId = KEYIDS.get(keyName, -1) if keyId is None: print("[ActionMap] Error: Keymap id '%s' in context '%s' in file '%s' is undefined/invalid!" % (keyName, context, filename)) error = True except ValueError: print("[ActionMap] Error: Keymap id '%s' in context '%s' in file '%s' can not be evaluated!" % (keyName, context, filename)) keyId = -1 error = True mapto = key.attrib.get("mapto") unmap = key.attrib.get("unmap") if mapto is None and unamp is None: print("[ActionMap] Error: At least one of the attributes 'mapto' or 'unmap' in context '%s' id '%s' (%d) in file '%s' must be specified!" % (context, keyName, keyId, filename)) error = True flags = key.attrib.get("flags") if flags is None: print("[ActionMap] Error: Attribute 'flag' in context '%s' id '%s' (%d) in file '%s' must be specified!" % (context, keyName, keyId, filename)) error = True else: flagToValue = lambda x: { 'm': 1, 'b': 2, 'r': 4, 'l': 8 }[x] newFlags = sum(map(flagToValue, flags)) if not newFlags: print("[ActionMap] Error: Attribute 'flag' value '%s' in context '%s' id '%s' (%d) in file '%s' appears invalid!" % (flags, context, keyName, keyId, filename)) errors += 1 flags = newFlags if not error: if unmap is None: # If a key was unmapped, it can only be assigned a new function in the same keymap file (avoid file parsing sequence dependency). if unmapDict.get((context, keyName, mapto)) in [filename, None]: if config.crash.debugActionMaps.value: print("[ActionMap] Context '%s' keyName '%s' (%d) mapped to '%s' (Device: %s)." % (context, keyName, keyId, mapto, device.capitalize())) actionMapInstance.bindKey(filename, device, keyId, flags, context, mapto) addKeyBinding(filename, keyId, context, mapto, flags) else: actionMapInstance.unbindPythonKey(context, keyId, unmap) unmapDict.update({(context, keyName, unmap): filename})