1# This file is part of Xpra. 2# Copyright (C) 2011-2020 Antoine Martin <antoine@xpra.org> 3# Xpra is released under the terms of the GNU GPL v2, or, at your option, any 4# later version. See the file COPYING for details. 5 6import os 7 8#ensure that we use gtk as display source: 9from xpra.x11.gtk_x11.gdk_display_source import init_gdk_display_source 10from xpra.util import std, csv, envbool, typedict 11from xpra.os_util import bytestostr 12from xpra.gtk_common.error import xsync, xlog 13from xpra.x11.bindings.keyboard_bindings import X11KeyboardBindings #@UnresolvedImport 14from xpra.keyboard.layouts import parse_xkbmap_query 15from xpra.log import Logger 16 17init_gdk_display_source() 18X11Keyboard = X11KeyboardBindings() 19 20log = Logger("x11", "keyboard") 21 22XKB = envbool("XPRA_XKB", True) 23DEBUG_KEYSYMS = [x for x in os.environ.get("XPRA_DEBUG_KEYSYMS", "").split(",") if len(x)>0] 24 25#keys we choose not to map if the free space in the keymap is too limited 26#this list was generated using: 27#$ DISPLAY=:1 xmodmap -pke | awk -F= '{print $2}' | xargs -n 1 echo | sort -u | grep XF | xargs 28OPTIONAL_KEYS = [ 29 "XF86AudioForward", "XF86AudioLowerVolume", "XF86AudioMedia", "XF86AudioMicMute", "XF86AudioMute", 30 "XF86AudioNext", "XF86AudioPause", "XF86AudioPlay", "XF86AudioPrev", "XF86AudioRaiseVolume", 31 "XF86AudioRecord", "XF86AudioRewind", "XF86AudioStop", 32 "XF86Back", "XF86Battery", "XF86Bluetooth", "XF86Calculator", "XF86ClearGrab", "XF86Close", 33 "XF86Copy", "XF86Cut", "XF86Display", "XF86Documents", "XF86DOS", "XF86Eject", "XF86Explorer", 34 "XF86Favorites", "XF86Finance", "XF86Forward", "XF86Game", "XF86Go", "XF86HomePage", "XF86KbdBrightnessDown", 35 "XF86KbdBrightnessUp", "XF86KbdLightOnOff", 36 "XF86Launch1", "XF86Launch2", "XF86Launch3", "XF86Launch4", "XF86Launch5", "XF86Launch6", 37 "XF86Launch7", "XF86Launch8", "XF86Launch9", "XF86LaunchA", "XF86LaunchB", 38 "XF86Mail", "XF86MailForward", "XF86MenuKB", "XF86Messenger", "XF86MonBrightnessDown", 39 "XF86MonBrightnessUp", "XF86MyComputer", 40 "XF86New", "XF86Next_VMode", "XF86Open", "XF86Paste", "XF86Phone", "XF86PowerOff", 41 "XF86Prev_VMode", "XF86Reload", "XF86Reply", "XF86RotateWindows", "XF86Save", "XF86ScreenSaver", 42 "XF86ScrollDown", "XF86ScrollUp", "XF86Search", "XF86Send", "XF86Shop", "XF86Sleep", "XF86Suspend", 43 "XF86Switch_VT_1", "XF86Switch_VT_10", "XF86Switch_VT_11", "XF86Switch_VT_12", "XF86Switch_VT_2", "XF86Switch_VT_3", 44 "XF86Switch_VT_4", "XF86Switch_VT_5", "XF86Switch_VT_6", "XF86Switch_VT_7", "XF86Switch_VT_8", "XF86Switch_VT_9", 45 "XF86Tools", "XF86TouchpadOff", "XF86TouchpadOn", "XF86TouchpadToggle", "XF86Ungrab", "XF86WakeUp", "XF86WebCam", 46 "XF86WLAN", "XF86WWW", "XF86Xfer", 47 ] 48 49 50def clean_keyboard_state(): 51 with xlog: 52 X11Keyboard.ungrab_all_keys() 53 with xlog: 54 X11Keyboard.set_layout_group(0) 55 with xlog: 56 X11Keyboard.unpress_all_keys() 57 58################################################################################ 59# keyboard layouts 60 61def do_set_keymap(xkbmap_layout, xkbmap_variant, xkbmap_options, 62 xkbmap_query, xkbmap_query_struct): 63 """ xkbmap_layout is the generic layout name (used on non posix platforms) 64 xkbmap_variant is the layout variant (may not be set) 65 xkbmap_print is the output of "setxkbmap -print" on the client 66 xkbmap_query is the output of "setxkbmap -query" on the client 67 xkbmap_query_struct is xkbmap_query parsed into a dictionary 68 Use those to try to setup the correct keyboard map for the client 69 so that all the keycodes sent will be mapped 70 """ 71 #First we try to use data from setxkbmap -query, 72 #preferably as structured data: 73 if xkbmap_query and not xkbmap_query_struct: 74 xkbmap_query_struct = parse_xkbmap_query(xkbmap_query) 75 xkbmap_query_struct = typedict(xkbmap_query_struct) 76 if xkbmap_query_struct: 77 log("do_set_keymap using xkbmap_query struct=%s", xkbmap_query_struct) 78 #The xkbmap_query_struct data will look something like this: 79 # { 80 # b"rules" : b"evdev", 81 # b"model" : b"pc105", 82 # b"layout" : b"gb", 83 # b"options" : b"grp:shift_caps_toggle", 84 # } 85 #parse the data into a dict: 86 rules = xkbmap_query_struct.strget("rules") 87 model = xkbmap_query_struct.strget("model") 88 layout = xkbmap_query_struct.strget("layout") 89 variant = xkbmap_query_struct.strget("variant") 90 options = xkbmap_query_struct.strget("options") 91 if layout: 92 log.info("setting keymap: %s", 93 csv("%s=%s" % (std(k), std(v)) for k,v in xkbmap_query_struct.items() 94 if k in ("rules", "model", "layout", "variant", "options") and v)) 95 if safe_setxkbmap(rules, model, layout, variant, options): 96 return 97 else: 98 if safe_setxkbmap(rules, model, "", "", ""): 99 return 100 #fallback for non X11 clients: 101 layout = xkbmap_layout or "us" 102 log.info("setting keyboard layout to '%s'", std(layout)) 103 safe_setxkbmap("evdev", "pc105", layout, xkbmap_variant, xkbmap_options) 104 105def safe_setxkbmap(rules, model, layout, variant, options): 106 #(we execute the options separately in case that fails..) 107 try: 108 X11Keyboard.setxkbmap(rules, model, layout, variant, options) 109 return True 110 except Exception: 111 log("safe_setxkbmap%s", (rules, model, layout, variant, options), exc_info=True) 112 log.warn("Warning: failed to set exact keymap,") 113 log.warn(" rules=%s, model=%s, layout=%s, variant=%s, options=%s", 114 bytestostr(rules), bytestostr(model), bytestostr(layout), bytestostr(variant), bytestostr(options)) 115 if options: 116 #try again with no options: 117 try: 118 X11Keyboard.setxkbmap(rules, model, layout, variant, "") 119 return True 120 except Exception: 121 log("setxkbmap", exc_info=True) 122 log.error("Error: failed to set exact keymap") 123 log.error(" even without applying any options..") 124 return False 125 126################################################################################ 127# keycodes 128 129def apply_xmodmap(instructions): 130 try: 131 with xsync: 132 unset = X11Keyboard.set_xmodmap(instructions) 133 except Exception as e: 134 log("apply_xmodmap(%s)", instructions, exc_info=True) 135 log.error("Error configuring modifier map: %s", e) 136 unset = instructions 137 if unset is None: 138 #None means an X11 error occurred, re-do all: 139 unset = instructions 140 return unset 141 142 143def get_keycode_mappings(): 144 if XKB and X11Keyboard.hasXkb(): 145 return X11Keyboard.get_xkb_keycode_mappings() 146 return X11Keyboard.get_keycode_mappings() 147 148def set_keycode_translation(xkbmap_x11_keycodes, xkbmap_keycodes): 149 """ 150 Translate the given keycodes into the existing keymap 151 """ 152 #get the list of keycodes (either from x11 keycodes or gtk keycodes): 153 if xkbmap_x11_keycodes: 154 dump_dict(xkbmap_x11_keycodes) 155 keycodes = indexed_mappings(xkbmap_x11_keycodes) 156 else: 157 keycodes = gtk_keycodes_to_mappings(xkbmap_keycodes) 158 log("set_keycode_translation(%s, %s)", xkbmap_x11_keycodes, xkbmap_keycodes) 159 log(" keycodes=%s", keycodes) 160 #keycodes = { 161 # 9: set([('', 1), ('Escape', 4), ('', 3), ('Escape', 0), ('Escape', 2)]), 162 # 10: set([('onesuperior', 4), ('onesuperior', 8), ('exclam', 1), ('1', 6), ('exclam', 3), ('1', 2), ('exclamdown', 9), ('exclamdown', 5), ('1', 0), ('exclam', 7)]), 163 x11_keycodes = get_keycode_mappings() 164 log(" x11_keycodes=%s", x11_keycodes) 165 #x11_keycodes = { 166 # 8: ['Mode_switch', '', 'Mode_switch', '', 'Mode_switch'], 167 # 9: ['Escape', '', 'Escape', '', 'Escape'], 168 #for faster lookups: 169 x11_keycodes_for_keysym = {} 170 for keycode, keysyms in x11_keycodes.items(): 171 for keysym in keysyms: 172 x11_keycodes_for_keysym.setdefault(keysym, set()).add(keycode) 173 def find_keycode(kc, keysym, i): 174 keycodes = tuple(x11_keycodes_for_keysym.get(keysym, set())) 175 if keysym in DEBUG_KEYSYMS: 176 log.info("set_keycode_translation: find_keycode%s x11 keycodes=%s", (kc, keysym, i), keycodes) 177 def rlog(keycode, msg): 178 if keysym in DEBUG_KEYSYMS: 179 log.info("set_keycode_translation: find_keycode%s=%s (%s)", (kc, keysym, i), keycode, msg) 180 if not keycodes: 181 return None 182 #no other option, use it: 183 for keycode in keycodes: 184 defs = x11_keycodes.get(keycode) 185 if keysym in DEBUG_KEYSYMS: 186 log.info("server x11 keycode %i: %s", keycode, defs) 187 assert defs, "bug: keycode %i not found in %s" % (keycode, x11_keycodes) 188 if len(defs)>i and defs[i]==keysym: 189 rlog(keycode, "exact index match") 190 return keycode, True 191 #if possible, use the same one: 192 if kc in keycodes: 193 rlog(kc, "using same keycode as client") 194 return kc, False 195 keycode = keycodes[0] 196 rlog(keycode, "using first match") 197 return keycode, False 198 #generate the translation map: 199 trans = {} 200 for keycode, defs in keycodes.items(): 201 if bool(set(DEBUG_KEYSYMS) & set(bytestostr(d[0]) for d in defs)): 202 log.info("client keycode=%i, defs=%s", keycode, defs) 203 for bkeysym, i in tuple(defs): #ie: (b'1', 0) or (b'A', 1), etc 204 keysym = bytestostr(bkeysym) 205 m = find_keycode(keycode, keysym, i) 206 if m: 207 x11_keycode, index_matched = m 208 trans[(keycode, keysym)] = x11_keycode 209 trans[keysym] = x11_keycode 210 if index_matched: 211 trans[(keysym, i)] = x11_keycode 212 if not xkbmap_x11_keycodes: 213 #now add all the keycodes we may not have mapped yet 214 #(present in x11_keycodes but not keycodes) 215 for keycode, keysyms in x11_keycodes.items(): 216 for i, keysym in enumerate(keysyms): 217 if keysym not in trans: 218 if keysym in DEBUG_KEYSYMS: 219 log.info("x11 keycode %s: %s", keycode, keysym) 220 trans[keysym] = keycode 221 key = (keysym, i) 222 if key not in trans: 223 if keysym in DEBUG_KEYSYMS: 224 log.info("x11 keycode %s: %s", keycode, key) 225 trans[key] = keycode 226 log("set_keycode_translation(..)=%s", trans) 227 return trans 228 229def set_all_keycodes(xkbmap_x11_keycodes, xkbmap_keycodes, preserve_server_keycodes, modifiers): 230 """ 231 Clients that have access to raw x11 keycodes should provide 232 an xkbmap_x11_keycodes map, we otherwise fallback to using 233 the xkbmap_keycodes gtk keycode list. 234 We try to preserve the initial keycodes if asked to do so, 235 we retrieve them from the current server keymap and combine 236 them with the given keycodes. 237 The modifiers dict can be obtained by calling 238 get_modifiers_from_meanings or get_modifiers_from_keycodes. 239 We use it to ensure that two modifiers are not 240 mapped to the same keycode (which is not allowed). 241 We return a translation map for keycodes after setting them up, 242 the key is (keycode, keysym) and the value is the server keycode. 243 """ 244 log("set_all_keycodes(%s.., %s.., %s.., %s)", 245 str(xkbmap_x11_keycodes)[:60], str(xkbmap_keycodes)[:60], str(preserve_server_keycodes)[:60], modifiers) 246 247 #so we can validate entries: 248 keysym_to_modifier = {} 249 for modifier, keysyms in modifiers.items(): 250 for keysym in keysyms: 251 existing_mod = keysym_to_modifier.get(keysym) 252 if existing_mod and existing_mod!=modifier: 253 log.error("ERROR: keysym %s is mapped to both %s and %s !", keysym, modifier, existing_mod) 254 else: 255 keysym_to_modifier[keysym] = modifier 256 if keysym in DEBUG_KEYSYMS: 257 log.info("set_all_keycodes() keysym_to_modifier[%s]=%s", keysym, modifier) 258 log("keysym_to_modifier=%s", keysym_to_modifier) 259 260 def modifiers_for(entries): 261 """ entries can only point to a single modifier - verify """ 262 modifiers = set() 263 l = log 264 for keysym, _ in entries: 265 modifier = keysym_to_modifier.get(keysym) 266 if modifier: 267 modifiers.add(modifier) 268 if keysym in DEBUG_KEYSYMS: 269 l = log.info 270 l("modifiers_for(%s)=%s", entries, modifiers) 271 return modifiers 272 273 def filter_mappings(mappings, drop_extra_keys=False): 274 filtered = {} 275 invalid_keysyms = set() 276 def estr(entries): 277 try: 278 return csv(tuple(set(x[0] for x in entries))) 279 except Exception: 280 return csv(tuple(entries)) 281 for keycode, entries in mappings.items(): 282 mods = modifiers_for(entries) 283 if len(mods)>1: 284 log.warn("Warning: keymapping changed:") 285 log.warn(" keycode %s points to %i modifiers: %s", keycode, len(mods), csv(tuple(mods))) 286 log.warn(" from definition: %s", estr(entries)) 287 for mod in mods: 288 emod = [entry for entry in entries if mod in modifiers_for([entry])] 289 log.warn(" %s: %s", mod, estr(emod)) 290 #keep just the first one: 291 mods = tuple(mods)[:1] 292 mod = mods[0] 293 entries = [entry for entry in entries if mod in modifiers_for([entry])] 294 log.warn(" keeping: %s for %s", estr(entries), mod) 295 #now remove entries for keysyms we don't have: 296 f_entries = set((keysym, index) for keysym, index in entries 297 if keysym and X11Keyboard.parse_keysym(keysym) is not None) 298 for keysym, _ in entries: 299 if keysym and X11Keyboard.parse_keysym(keysym) is None: 300 invalid_keysyms.add(keysym) 301 if not f_entries: 302 log("keymapping removed invalid keycode entry %s pointing to only unknown keysyms: %s", 303 keycode, entries) 304 continue 305 if drop_extra_keys: 306 if not any(keysym for keysym, index in entries if ( 307 X11Keyboard.parse_keysym(keysym) is not None and keysym not in OPTIONAL_KEYS) 308 ): 309 log("keymapping removed keycode entry %s pointing to optional keys: %s", keycode, entries) 310 continue 311 filtered[keycode] = f_entries 312 if invalid_keysyms: 313 log.warn("Warning: the following keysyms are invalid and have not been mapped") 314 log.warn(" %s", csv(invalid_keysyms)) 315 return filtered 316 317 #get the list of keycodes (either from x11 keycodes or gtk keycodes): 318 if xkbmap_x11_keycodes: 319 log("using x11 keycodes: %s", xkbmap_x11_keycodes) 320 dump_dict(xkbmap_x11_keycodes) 321 keycodes = indexed_mappings(xkbmap_x11_keycodes) 322 else: 323 log("using gtk keycodes: %s", xkbmap_keycodes) 324 keycodes = gtk_keycodes_to_mappings(xkbmap_keycodes) 325 #filter to ensure only valid entries remain: 326 log("keycodes=%s", keycodes) 327 keycodes = filter_mappings(keycodes) 328 329 #now lookup the current keycodes (if we need to preserve them) 330 preserve_keycode_entries = {} 331 if preserve_server_keycodes: 332 preserve_keycode_entries = X11Keyboard.get_keycode_mappings() 333 log("preserved mappings:") 334 dump_dict(preserve_keycode_entries) 335 336 kcmin, kcmax = X11Keyboard.get_minmax_keycodes() 337 for try_harder in (False, True): 338 filtered_preserve_keycode_entries = filter_mappings(indexed_mappings(preserve_keycode_entries), try_harder) 339 log("filtered_preserve_keycode_entries=%s", filtered_preserve_keycode_entries) 340 trans, new_keycodes, missing_keycodes = translate_keycodes(kcmin, kcmax, keycodes, 341 filtered_preserve_keycode_entries, 342 keysym_to_modifier, 343 try_harder) 344 if not missing_keycodes: 345 break 346 instructions = keymap_to_xmodmap(new_keycodes) 347 unset = apply_xmodmap(instructions) 348 log("unset=%s", unset) 349 return trans 350 351def dump_dict(d): 352 for k,v in d.items(): 353 log("%s\t\t=\t%s", k, v) 354 355def group_by_keycode(entries): 356 keycodes = {} 357 log_keycodes = [] 358 for keysym, keycode, index in entries: 359 keycodes.setdefault(keycode, set()).add((keysym, index)) 360 if keysym in DEBUG_KEYSYMS: 361 log_keycodes.append(keycode) 362 if log_keycodes: 363 log.info("group_by_keycode: %s", dict((keycode, keycodes.get(keycode)) for keycode in log_keycodes)) 364 return keycodes 365 366def indexed_mappings(raw_mappings): 367 indexed = {} 368 for keycode, keysyms in raw_mappings.items(): 369 pairs = set() 370 l = log 371 for i, keysym in enumerate(keysyms): 372 if keysym in DEBUG_KEYSYMS: 373 l = log.info 374 pairs.add((keysym, i)) 375 indexed[keycode] = pairs 376 l("indexed_mappings: %s=%s", keycode, pairs) 377 return indexed 378 379 380def gtk_keycodes_to_mappings(gtk_mappings): 381 """ 382 Takes gtk keycodes as obtained by get_gtk_keymap, in the form: 383 #[(keyval, keyname, keycode, group, level), ..] 384 And returns a list of entries in the form: 385 [[keysym, keycode, index], ..] 386 """ 387 #use the keycodes supplied by gtk: 388 mappings = {} 389 for _, name, keycode, group, level in gtk_mappings: 390 if keycode<0: 391 continue #ignore old 'add_if_missing' client side code 392 index = group*2+level 393 mappings.setdefault(keycode, set()).add((name, index)) 394 return mappings 395 396def x11_keycodes_to_list(x11_mappings): 397 """ 398 Takes x11 keycodes as obtained by get_keycode_mappings(), in the form: 399 #{keycode : [keysyms], ..} 400 And returns a list of entries in the form: 401 [[keysym, keycode, index], ..] 402 """ 403 entries = [] 404 if x11_mappings: 405 for keycode, keysyms in x11_mappings.items(): 406 index = 0 407 for keysym in keysyms: 408 if keysym: 409 entries.append([keysym, int(keycode), index]) 410 if keysym in DEBUG_KEYSYMS: 411 log.info("x11_keycodes_to_list: (%s, %s) : %s", keycode, keysyms, (keysym, int(keycode), index)) 412 index += 1 413 return entries 414 415 416def translate_keycodes(kcmin, kcmax, keycodes, preserve_keycode_entries, keysym_to_modifier, try_harder=False): 417 """ 418 The keycodes given may not match the range that the server supports, 419 or some of those keycodes may not be usable (only one modifier can 420 be mapped to a single keycode) or we want to preserve a keycode, 421 or modifiers want to use the same keycode (which is not possible), 422 so we return a translation map for those keycodes that have been 423 remapped. 424 The preserve_keycodes is a dict containing {keycode:[entries]} 425 for keys we want to preserve the keycode for. 426 Note: a client_keycode of '0' is valid (osx uses that), 427 but server_keycode generally starts at 8... 428 """ 429 log("translate_keycodes(%s, %s, %s, %s, %s, %s)", 430 kcmin, kcmax, keycodes, preserve_keycode_entries, keysym_to_modifier, try_harder) 431 #list of free keycodes we can use: 432 free_keycodes = [i for i in range(kcmin, kcmax+1) if i not in preserve_keycode_entries] 433 keycode_trans = {} #translation map from client keycode to our server keycode 434 server_keycodes = {} #the new keycode definitions 435 missing_keycodes = [] #the groups of entries we failed to map due to lack of free keycodes 436 437 #to do faster lookups: 438 preserve_keysyms_map = {} 439 for keycode, entries in preserve_keycode_entries.items(): 440 for keysym, _ in entries: 441 preserve_keysyms_map.setdefault(keysym, set()).add(keycode) 442 for k in DEBUG_KEYSYMS: 443 log.info("preserve_keysyms_map[%s]=%s", k, preserve_keysyms_map.get(k)) 444 445 def do_assign(keycode, server_keycode, entries, override_server_keycode=False): 446 """ may change the keycode if needed 447 in which case we update the entries and populate 'keycode_trans' 448 """ 449 l = log 450 for name, _ in entries: 451 if name in DEBUG_KEYSYMS: 452 l = log.info 453 if (server_keycode in server_keycodes) and not override_server_keycode: 454 l("assign: server keycode %s already in use: %s", server_keycode, server_keycodes.get(server_keycode)) 455 server_keycode = -1 456 elif server_keycode>0 and (server_keycode<kcmin or server_keycode>kcmax): 457 l("assign: keycode %s out of range (%s to %s)", server_keycode, kcmin, kcmax) 458 server_keycode = -1 459 if server_keycode<=0: 460 if free_keycodes: 461 server_keycode = free_keycodes[0] 462 l("set_keycodes key %s using free keycode=%s", entries, server_keycode) 463 else: 464 msg = "set_keycodes: no free keycodes!, cannot translate %s: %s", server_keycode, tuple(entries) 465 if try_harder: 466 log.error(*msg) 467 else: 468 l(*msg) 469 missing_keycodes.append(entries) 470 server_keycode = -1 471 if server_keycode>0: 472 l("set_keycodes key %s (%s) mapped to keycode=%s", keycode, tuple(entries), server_keycode) 473 #can't use it any more! 474 if server_keycode in free_keycodes: 475 free_keycodes.remove(server_keycode) 476 #record it in trans map: 477 for name, _ in entries: 478 if keycode>=0 and server_keycode!=keycode: 479 keycode_trans[(keycode, name)] = server_keycode 480 l("keycode_trans[(%s, %s)]=%s", keycode, bytestostr(name), server_keycode) 481 keycode_trans[name] = server_keycode 482 l("keycode_trans[%s]=%s", bytestostr(name), server_keycode) 483 server_keycodes[server_keycode] = entries 484 return server_keycode 485 486 def assign(client_keycode, entries): 487 if not entries: 488 return 0 489 #all the keysyms for this keycode: 490 keysyms = set(keysym for keysym, _ in entries) 491 if not keysyms: 492 return 0 493 if len(keysyms)==1 and tuple(keysyms)[0]=='0xffffff': 494 log("skipped invalid keysym: %s / %s", client_keycode, entries) 495 return 0 496 l = log 497 if [k for k in keysyms if k in DEBUG_KEYSYMS]: 498 l = log.info 499 l("assign(%s, %s)", client_keycode, entries) 500 501 if not preserve_keycode_entries: 502 return do_assign(client_keycode, client_keycode, entries) 503 if len(keysyms)==1: 504 #only one keysym, replace with single entry 505 entries = set([(tuple(keysyms)[0], 0)]) 506 507 #the candidate preserve entries: those that have at least one of the keysyms: 508 preserve_keycode_matches = {} 509 for keysym in tuple(keysyms): 510 keycodes = preserve_keysyms_map.get(keysym, []) 511 for keycode in keycodes: 512 v = preserve_keycode_entries.get(keycode) 513 preserve_keycode_matches[keycode] = v 514 l("preserve_keycode_matches[%s]=%s", keycode, v) 515 516 if not preserve_keycode_matches: 517 l("no preserve matches for %s", tuple(entries)) 518 return do_assign(client_keycode, -1, entries) #nothing to preserve 519 520 l("preserve matches for %s : %s", entries, preserve_keycode_matches) 521 #direct superset: 522 for p_keycode, p_entries in preserve_keycode_matches.items(): 523 if entries.issubset(p_entries): 524 l("found direct preserve superset for client keycode %s %s -> server keycode %s %s", 525 client_keycode, tuple(entries), p_keycode, tuple(p_entries)) 526 return do_assign(client_keycode, p_keycode, p_entries, override_server_keycode=True) 527 if p_entries.issubset(entries): 528 l("found direct superset of preserve for client keycode %s %s -> server keycode %s %s", 529 client_keycode, tuple(entries), p_keycode, tuple(p_entries)) 530 return do_assign(client_keycode, p_keycode, entries, override_server_keycode=True) 531 532 #ignoring indexes, but requiring at least as many keysyms: 533 for p_keycode, p_entries in preserve_keycode_matches.items(): 534 p_keysyms = set(keysym for keysym,_ in p_entries) 535 if keysyms.issubset(p_keysyms): 536 if len(p_entries)>len(entries): 537 l("found keysym preserve superset with more keys for %s : %s", tuple(entries), tuple(p_entries)) 538 return do_assign(client_keycode, p_keycode, p_entries, override_server_keycode=True) 539 if p_keysyms.issubset(keysyms): 540 l("found keysym superset of preserve with more keys for %s : %s", tuple(entries), tuple(p_entries)) 541 return do_assign(client_keycode, p_keycode, entries, override_server_keycode=True) 542 543 if try_harder: 544 #try to match the main key only: 545 main_key = set((keysym, index) for keysym, index in entries if index==0) 546 if len(main_key)==1: 547 for p_keycode, p_entries in preserve_keycode_matches.items(): 548 p_keysyms = set(keysym for keysym,_ in p_entries) 549 if main_key.issubset(p_entries): 550 l("found main key superset for %s : %s", main_key, tuple(p_entries)) 551 return do_assign(client_keycode, p_keycode, p_entries, override_server_keycode=True) 552 553 l("no matches for %s", tuple(entries)) 554 return do_assign(client_keycode, -1, entries) 555 556 #now try to assign each keycode: 557 for keycode in sorted(keycodes.keys()): 558 entries = keycodes.get(keycode) 559 log("assign(%s, %s)", keycode, entries) 560 assign(keycode, entries) 561 562 #add all the other preserved ones that have not been mapped to any client keycode: 563 for server_keycode, entries in preserve_keycode_entries.items(): 564 if server_keycode not in server_keycodes: 565 do_assign(-1, server_keycode, entries) 566 567 #find all keysyms assigned so far: 568 all_keysyms = set() 569 for entries in server_keycodes.values(): 570 for x in [keysym for keysym, _ in entries]: 571 all_keysyms.add(x) 572 log("all_keysyms=%s", all_keysyms) 573 574 #defined keysyms for modifiers if some are missing: 575 for keysym, modifier in keysym_to_modifier.items(): 576 if keysym not in all_keysyms: 577 l = log 578 if keysym in DEBUG_KEYSYMS: 579 l = log.info 580 l("found missing keysym %s for modifier %s, will add it", keysym, modifier) 581 new_keycode = set([(keysym, 0)]) 582 server_keycode = assign(-1, new_keycode) 583 l("assigned keycode %s for key '%s' of modifier '%s'", server_keycode, keysym, modifier) 584 585 log("translated keycodes=%s", keycode_trans) 586 log("%s free keycodes=%s", len(free_keycodes), free_keycodes) 587 return keycode_trans, server_keycodes, missing_keycodes 588 589 590def keymap_to_xmodmap(trans_keycodes): 591 """ 592 Given a dict with keycodes as keys and lists of keyboard entries as values, 593 (keysym, keycode, index) 594 produce a list of xmodmap instructions to set the x11 keyboard to match it, 595 in the form: 596 ("keycode", keycode, [keysyms]) 597 """ 598 missing_keysyms = [] #the keysyms lookups which failed 599 instructions = [] 600 all_entries = [] 601 for entries in trans_keycodes.values(): 602 all_entries += entries 603 keysyms_per_keycode = max([index for _, index in all_entries])+1 604 for server_keycode, entries in trans_keycodes.items(): 605 keysyms = [None]*keysyms_per_keycode 606 names = [""]*keysyms_per_keycode 607 sentries = sorted(entries, key=lambda x:x[1]) 608 for name, index in sentries: 609 assert 0<=index<keysyms_per_keycode 610 try: 611 keysym = X11Keyboard.parse_keysym(name) 612 except Exception: 613 keysym = None 614 if keysym is None: 615 if name!="": 616 missing_keysyms.append(name) 617 else: 618 if keysyms[index] is not None: 619 #if the client provides multiple keysyms for the same index, 620 #replace with the new one if the old one exists elsewhere, 621 #or skip it if we have another entry for it 622 can_override = any(True for i,v in enumerate(keysyms) if i<index and v==keysyms[index]) 623 can_skip = any(True for i,v in enumerate(keysyms) if i!=index and v==keysym) 624 if can_override or can_skip: 625 l = log.debug 626 else: 627 l = log.warn 628 l("Warning: more than one keysym for keycode %-3i at index %i:", server_keycode, index) 629 l(" entries=%s", csv(tuple(sentries))) 630 l(" keysyms=%s", csv(keysyms)) 631 l(" assigned keysym=%s", names[index]) 632 l(" wanted keysym=%s", name) 633 if can_override: 634 l(" current value also found at another index, overriding it") 635 elif can_skip: 636 l(" new value also found elsewhere, skipping it") 637 else: 638 continue 639 names[index] = name 640 keysyms[index] = keysym 641 if name in DEBUG_KEYSYMS: 642 log.info("keymap_to_xmodmap: keysyms[%s]=%s (%s)", index, keysym, name) 643 #remove empty keysyms: 644 while keysyms and keysyms[0] is None: 645 keysyms = keysyms[1:] 646 l = log 647 if [k for k in keysyms if k in DEBUG_KEYSYMS]: 648 l = log.info 649 l("%s: %s -> %s", server_keycode, names, keysyms) 650 instructions.append(("keycode", server_keycode, keysyms)) 651 652 if missing_keysyms: 653 log.error("cannot find the X11 keysym for the following key names: %s", set(missing_keysyms)) 654 log("instructions=%s", instructions) 655 return instructions 656 657 658################################################################################ 659# modifiers 660 661def clear_modifiers(_modifiers): 662 instructions = [] 663 for i in range(0, 8): 664 instructions.append(("clear", i)) 665 apply_xmodmap(instructions) 666 667def set_modifiers(modifiers): 668 """ 669 modifiers is a dict: {modifier : [keynames]} 670 Note: the same keysym cannot appear in more than one modifier 671 """ 672 instructions = [] 673 for modifier, keynames in modifiers.items(): 674 mod = X11Keyboard.parse_modifier(bytestostr(modifier)) 675 if mod>=0: 676 instructions.append(("add", mod, keynames)) 677 else: 678 log.error("Error: unknown modifier %s", modifier) 679 log("set_modifiers: %s", instructions) 680 def apply_or_trim(instructions): 681 err = apply_xmodmap(instructions) 682 log("set_modifiers: err=%s", err) 683 if err: 684 log("set_modifiers %s failed, retrying one more at a time", instructions) 685 l = len(instructions) 686 for i in range(1, l): 687 subset = instructions[:i] 688 log("set_modifiers testing with [:%s]=%s", i, subset) 689 err = apply_xmodmap(subset) 690 log("err=%s", err) 691 if err: 692 log.warn("removing problematic modifier mapping: %s", instructions[i-1]) 693 instructions = instructions[:i-1]+instructions[i:] 694 return apply_or_trim(instructions) 695 apply_or_trim(instructions) 696 return modifiers 697 698 699def get_modifiers_from_meanings(xkbmap_mod_meanings): 700 """ 701 xkbmap_mod_meanings maps a keyname to a modifier 702 returns keynames_for_mod: {modifier : [keynames]} 703 """ 704 #first generate a {modifier : [keynames]} dict: 705 modifiers = {} 706 for keyname, modifier in xkbmap_mod_meanings.items(): 707 l = modifiers.setdefault(bytestostr(modifier), []) 708 kn = bytestostr(keyname) 709 if kn not in l: 710 l.append(kn) 711 log("get_modifiers_from_meanings(%s) modifier dict=%s", xkbmap_mod_meanings, modifiers) 712 return modifiers 713 714def get_modifiers_from_keycodes(xkbmap_keycodes, add_default_modifiers=True): 715 """ 716 Some platforms can't tell us about modifier mappings 717 So we try to find matches from the defaults below: 718 """ 719 from xpra.keyboard.mask import DEFAULT_MODIFIER_MEANINGS 720 pref = DEFAULT_MODIFIER_MEANINGS 721 #keycodes are: {keycode : (keyval, name, keycode, group, level)} 722 matches = {} 723 log("get_modifiers_from_keycodes(%s...)", str(xkbmap_keycodes)[:160]) 724 all_keynames = set() 725 for entry in xkbmap_keycodes: 726 _, keyname, _, _, _ = entry 727 modifier = pref.get(keyname) 728 if modifier: 729 keynames = matches.setdefault(modifier, []) 730 if keyname not in keynames: 731 keynames.append(keyname) 732 all_keynames.add(keyname) 733 if add_default_modifiers: 734 #try to add missings ones (magic!) 735 defaults = {} 736 for keyname, modifier in DEFAULT_MODIFIER_MEANINGS.items(): 737 if keyname in all_keynames: 738 continue #aleady defined 739 if modifier not in matches: 740 #define it since it is completely missing 741 keynames = defaults.setdefault(modifier, []) 742 if keyname not in keynames: 743 keynames.append(keyname) 744 elif modifier in ["shift", "lock", "control", "mod1", "mod2"] or keyname=="ISO_Level3_Shift": 745 #these ones we always add them, even if a record for this modifier already exists 746 keynames = matches.setdefault(modifier, []) 747 if keyname not in keynames: 748 keynames.append(keyname) 749 log("get_modifiers_from_keycodes(...) adding defaults: %s", defaults) 750 matches.update(defaults) 751 log("get_modifiers_from_keycodes(...)=%s", matches) 752 return matches 753 754def map_missing_modifiers(keynames_for_mod): 755 x11_keycodes = X11Keyboard.get_keycode_mappings() 756 min_keycode, max_keycode = X11Keyboard.get_minmax_keycodes() 757 free_keycodes = [x for x in range(min_keycode, max_keycode) if x not in x11_keycodes] 758 log("map_missing_modifiers(%s) min_keycode=%i max_keycode=%i, free_keycodes=%s", 759 keynames_for_mod, min_keycode, max_keycode, free_keycodes) 760 keysyms_to_keycode = {} 761 for keycode, keysyms in x11_keycodes.items(): 762 for keysym in keysyms: 763 keysyms_to_keycode.setdefault(keysym, []).append(keycode) 764 xmodmap_changes = [] 765 for mod, keysyms in keynames_for_mod.items(): 766 missing = [] 767 for keysym in keysyms: 768 if keysym not in keysyms_to_keycode: 769 missing.append(keysym) 770 if missing: 771 log("map_missing_modifiers: no keycode found for modifier keys %s (%s)", csv(missing), mod) 772 if not free_keycodes: 773 log.warn("Warning: keymap is full, cannot add '%s' for modifier '%s'", keysym, mod) 774 else: 775 keycode = free_keycodes.pop() 776 xmodmap_changes.append(("keycode", keycode, missing)) 777 if xmodmap_changes: 778 log("xmodmap_changes=%s", xmodmap_changes) 779 X11Keyboard.set_xmodmap(xmodmap_changes) 780