def set_all_keycodes(xkbmap_x11_keycodes, xkbmap_keycodes, preserve_server_keycodes, modifiers): """ Clients that have access to raw x11 keycodes should provide an xkbmap_x11_keycodes map, we otherwise fallback to using the xkbmap_keycodes gtk keycode list. We try to preserve the initial keycodes if asked to do so, we retrieve them from the current server keymap and combine them with the given keycodes. The modifiers dict can be obtained by calling get_modifiers_from_meanings or get_modifiers_from_keycodes. We use it to ensure that two modifiers are not mapped to the same keycode (which is not allowed). We return a translation map for keycodes after setting them up, the key is (keycode, keysym) and the value is the server keycode. """ debug("set_all_keycodes(%s.., %s.., %s.., %s)", str(xkbmap_x11_keycodes)[:60], str(xkbmap_keycodes)[:60], str(preserve_server_keycodes)[:60], modifiers) #so we can validate entries: keysym_to_modifier = {} for modifier, keysyms in modifiers.items(): for keysym in keysyms: existing_mod = keysym_to_modifier.get(keysym) if existing_mod and existing_mod!=modifier: log.error("ERROR: keysym %s is mapped to both %s and %s !", keysym, modifier, existing_mod) else: keysym_to_modifier[keysym] = modifier debug("keysym_to_modifier=%s", keysym_to_modifier) def modifiers_for(entries): """ entries can only point to a single modifier - verify """ modifiers = set() for keysym, _ in entries: modifier = keysym_to_modifier.get(keysym) if modifier: modifiers.add(modifier) return modifiers def filter_mappings(mappings): filtered = {} for keycode, entries in mappings.items(): mods = modifiers_for(entries) if len(mods)>1: log.warn("keymapping removed invalid keycode entry %s pointing to more than one modifier (%s): %s", keycode, mods, entries) continue #now remove entries for keysyms we don't have: f_entries = set([(keysym, index) for keysym, index in entries if parse_keysym(keysym) is not None]) if len(f_entries)==0: log("keymapping removed invalid keycode entry %s pointing to only unknown keysyms: %s", keycode, entries) continue filtered[keycode] = f_entries return filtered #get the list of keycodes (either from x11 keycodes or gtk keycodes): if xkbmap_x11_keycodes and len(xkbmap_x11_keycodes)>0: debug("using x11 keycodes: %s", xkbmap_x11_keycodes) dump_dict(xkbmap_x11_keycodes) keycodes = indexed_mappings(xkbmap_x11_keycodes) else: debug("using gtk keycodes: %s", xkbmap_keycodes) keycodes = gtk_keycodes_to_mappings(xkbmap_keycodes) #filter to ensure only valid entries remain: debug("keycodes=%s", keycodes) keycodes = filter_mappings(keycodes) #now lookup the current keycodes (if we need to preserve them) preserve_keycode_entries = {} if preserve_server_keycodes: import gtk.gdk preserve_keycode_entries = get_keycode_mappings(gtk.gdk.get_default_root_window()) debug("preserved mappings:") dump_dict(preserve_keycode_entries) debug("preserve_keycode_entries=%s", preserve_keycode_entries) preserve_keycode_entries = filter_mappings(indexed_mappings(preserve_keycode_entries)) kcmin, kcmax = get_minmax_keycodes() for try_harder in (False, True): trans, new_keycodes, missing_keycodes = translate_keycodes(kcmin, kcmax, keycodes, preserve_keycode_entries, keysym_to_modifier, try_harder) if len(missing_keycodes)==0: break instructions = keymap_to_xmodmap(new_keycodes) unset = apply_xmodmap(instructions) debug("unset=%s", unset) return trans
def set_keycodes(keycodes, preserve_keycodes={}): """ The keycodes given may not match the range that the server supports, so we return a translation map for those keycodes that have been remapped. The preserve_keycodes is a dict containing {keysym:keycode} for keys we want to preserve the keycode for. """ kcmin, kcmax = get_minmax_keycodes() free_keycodes = [] for i in range(kcmin, kcmax): if i not in keycodes.keys() and i not in preserve_keycodes.values(): free_keycodes.append(i) log.debug("set_keycodes(..) min=%s, max=%s, free_keycodes=%s", kcmin, kcmax, free_keycodes) used = [] trans = {} instructions = [] for keycode, entries in keycodes.items(): server_keycode = keycode if preserve_keycodes: for entry in entries: (_, name, _, group, level) = entry if group == 0 and level == 0 and name in preserve_keycodes: server_keycode = preserve_keycodes.get(name) if server_keycode != keycode: log.debug( "set_keycodes key %s(%s) mapped to keycode=%s", keycode, entries, server_keycode) if server_keycode == 0 or server_keycode in used or server_keycode < kcmin or server_keycode > kcmax: if len(free_keycodes) > 0: server_keycode = free_keycodes[0] free_keycodes = free_keycodes[1:] log.debug( "set_keycodes key %s(%s) out of range or already in use, using free keycode=%s", keycode, entries, server_keycode) else: log.error( "set_keycodes: no free keycodes!, cannot translate %s: %s", keycode, entries) continue if server_keycode != keycode and keycode != 0: trans[keycode] = server_keycode used.append(server_keycode) keysyms = [] #sort them by group then level def sort_key(entry): (_, _, _, group, level) = entry return group * 10 + level sentries = sorted(entries, key=sort_key) for (_, name, _keycode, _, _) in sentries: assert _keycode == keycode try: keysym = parse_keysym(name) except: keysym = None if keysym is None: log.error("cannot find keysym for %s", name) else: keysyms.append(keysym) if len(keysyms) >= 2 and len(keysyms) <= 6: keysyms = keysyms[:2] + keysyms if len(keysyms) > 0: instructions.append(("keycode", server_keycode, keysyms)) log.debug("instructions=%s", instructions) unset = apply_xmodmap(instructions) log.debug("unset=%s", unset) log.debug("translated keycodes=%s", trans) log.debug("%s free keycodes=%s", len(free_keycodes), free_keycodes) return trans
def set_keycodes(keycodes, preserve_keycodes={}): """ The keycodes given may not match the range that the server supports, so we return a translation map for those keycodes that have been remapped. The preserve_keycodes is a dict containing {keysym:keycode} for keys we want to preserve the keycode for. """ kcmin,kcmax = get_minmax_keycodes() free_keycodes = [] for i in range(kcmin, kcmax): if i not in keycodes.keys() and i not in preserve_keycodes.values(): free_keycodes.append(i) log.debug("set_keycodes(..) min=%s, max=%s, free_keycodes=%s", kcmin, kcmax, free_keycodes) used = [] trans = {} instructions = [] for keycode, entries in keycodes.items(): server_keycode = keycode if preserve_keycodes: for entry in entries: (_, name, _, group, level) = entry if group==0 and level==0 and name in preserve_keycodes: server_keycode = preserve_keycodes.get(name) if server_keycode!=keycode: log.debug("set_keycodes key %s(%s) mapped to keycode=%s", keycode, entries, server_keycode) if server_keycode==0 or server_keycode in used or server_keycode<kcmin or server_keycode>kcmax: if len(free_keycodes)>0: server_keycode = free_keycodes[0] free_keycodes = free_keycodes[1:] log.debug("set_keycodes key %s(%s) out of range or already in use, using free keycode=%s", keycode, entries, server_keycode) else: log.error("set_keycodes: no free keycodes!, cannot translate %s: %s", keycode, entries) continue if server_keycode!=keycode and keycode!=0: trans[keycode] = server_keycode used.append(server_keycode) keysyms = [] #sort them by group then level def sort_key(entry): (_, _, _, group, level) = entry return group*10+level sentries = sorted(entries, key=sort_key) for (_, name, _keycode, _, _) in sentries: assert _keycode == keycode keysym = parse_keysym(name) if keysym is None: log.error("cannot find keysym for %s", name) else: keysyms.append(keysym) if len(keysyms)>=2 and len(keysyms)<=6: keysyms = keysyms[:2]+keysyms if len(keysyms)>0: instructions.append(("keycode", server_keycode, keysyms)) log.debug("instructions=%s", instructions) unset = apply_xmodmap(instructions) log.debug("unset=%s", unset) log.debug("translated keycodes=%s", trans) log.debug("%s free keycodes=%s", len(free_keycodes), free_keycodes) return trans
def set_all_keycodes(xkbmap_x11_keycodes, xkbmap_keycodes, preserve_server_keycodes, modifiers): """ Clients that have access to raw x11 keycodes should provide an xkbmap_x11_keycodes map, we otherwise fallback to using the xkbmap_keycodes gtk keycode list. We try to preserve the initial keycodes if asked to do so, we retrieve them from the current server keymap and combine them with the given keycodes. The modifiers dict can be obtained by calling get_modifiers_from_meanings or get_modifiers_from_keycodes. We use it to ensure that two modifiers are not mapped to the same keycode (which is not allowed). We return a translation map for keycodes after setting them up, the key is (keycode, keysym) and the value is the server keycode. """ debug("set_all_keycodes(%s.., %s.., %s.., %s)", str(xkbmap_x11_keycodes)[:60], str(xkbmap_keycodes)[:60], str(preserve_server_keycodes)[:60], modifiers) #so we can validate entries: keysym_to_modifier = {} for modifier, keysyms in modifiers.items(): for keysym in keysyms: existing_mod = keysym_to_modifier.get(keysym) if existing_mod and existing_mod != modifier: log.error("ERROR: keysym %s is mapped to both %s and %s !", keysym, modifier, existing_mod) else: keysym_to_modifier[keysym] = modifier debug("keysym_to_modifier=%s", keysym_to_modifier) def modifiers_for(entries): """ entries can only point to a single modifier - verify """ modifiers = set() for keysym, _ in entries: modifier = keysym_to_modifier.get(keysym) if modifier: modifiers.add(modifier) return modifiers def filter_mappings(mappings): filtered = {} for keycode, entries in mappings.items(): mods = modifiers_for(entries) if len(mods) > 1: log.warn( "keymapping removed invalid keycode entry %s pointing to more than one modifier (%s): %s", keycode, mods, entries) continue #now remove entries for keysyms we don't have: f_entries = set([(keysym, index) for keysym, index in entries if parse_keysym(keysym) is not None]) if len(f_entries) == 0: log( "keymapping removed invalid keycode entry %s pointing to only unknown keysyms: %s", keycode, entries) continue filtered[keycode] = f_entries return filtered #get the list of keycodes (either from x11 keycodes or gtk keycodes): if xkbmap_x11_keycodes and len(xkbmap_x11_keycodes) > 0: debug("using x11 keycodes: %s", xkbmap_x11_keycodes) dump_dict(xkbmap_x11_keycodes) keycodes = indexed_mappings(xkbmap_x11_keycodes) else: debug("using gtk keycodes: %s", xkbmap_keycodes) keycodes = gtk_keycodes_to_mappings(xkbmap_keycodes) #filter to ensure only valid entries remain: debug("keycodes=%s", keycodes) keycodes = filter_mappings(keycodes) #now lookup the current keycodes (if we need to preserve them) preserve_keycode_entries = {} if preserve_server_keycodes: import gtk.gdk preserve_keycode_entries = get_keycode_mappings( gtk.gdk.get_default_root_window()) debug("preserved mappings:") dump_dict(preserve_keycode_entries) debug("preserve_keycode_entries=%s", preserve_keycode_entries) preserve_keycode_entries = filter_mappings( indexed_mappings(preserve_keycode_entries)) kcmin, kcmax = get_minmax_keycodes() for try_harder in (False, True): trans, new_keycodes, missing_keycodes = translate_keycodes( kcmin, kcmax, keycodes, preserve_keycode_entries, keysym_to_modifier, try_harder) if len(missing_keycodes) == 0: break instructions = keymap_to_xmodmap(new_keycodes) unset = apply_xmodmap(instructions) debug("unset=%s", unset) return trans