Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# This file is part of Xpra. 

2# Copyright (C) 2010-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 

8from xpra.util import csv, print_nested_dict 

9from xpra.os_util import POSIX 

10from xpra.log import Logger 

11 

12log = Logger("keyboard") 

13 

14 

15def get_modifier_names(xkbmap_mod_meanings): 

16 #modifier names contains the internal modifiers list, ie: "mod1", "control", ... 

17 #but the user expects the name of the key to be used, ie: "alt" or "super" 

18 #whereas at best, we keep "Alt_L" : "mod1" mappings... (xposix) 

19 #so generate a map from one to the other: 

20 modifier_names = {} 

21 from xpra.keyboard.mask import DEFAULT_MODIFIER_MEANINGS, DEFAULT_MODIFIER_NUISANCE 

22 meanings = xkbmap_mod_meanings or DEFAULT_MODIFIER_MEANINGS 

23 DEFAULT_MODIFIER_IGNORE_KEYNAMES = ["Caps_Lock", "Num_Lock", "Scroll_Lock"] 

24 for pub_name,mod_name in meanings.items(): 

25 if mod_name in DEFAULT_MODIFIER_NUISANCE or pub_name in DEFAULT_MODIFIER_IGNORE_KEYNAMES: 

26 continue 

27 #just hope that xxx_L is mapped to the same modifier as xxx_R! 

28 if pub_name.endswith("_L") or pub_name.endswith("_R"): 

29 pub_name = pub_name[:-2] 

30 elif pub_name=="ISO_Level3_Shift": 

31 pub_name = "AltGr" 

32 if pub_name not in modifier_names: 

33 modifier_names[pub_name.lower()] = mod_name 

34 if pub_name.lower()=="control": 

35 #alias "control" to "ctrl" as it is often used: 

36 modifier_names["ctrl"] = mod_name 

37 log("parse_shortcuts: modifier names=%s", modifier_names) 

38 return modifier_names 

39 

40 

41def parse_shortcut_modifiers(s, modifier_names=()): 

42 #figure out the default shortcut modifiers 

43 #accept "," or "+" as delimiter: 

44 shortcut_modifiers = s.lower().replace(",", "+").split("+") 

45 def mod_defaults(): 

46 mods = ["meta", "shift"] 

47 if POSIX: 

48 #gnome intercepts too many of the Alt+Shift shortcuts, 

49 #so use control with gnome: 

50 if os.environ.get("XDG_CURRENT_DESKTOP")=="GNOME": 

51 mods = ["control", "shift"] 

52 return mods 

53 if shortcut_modifiers==["auto"]: 

54 shortcut_modifiers = mod_defaults() 

55 elif shortcut_modifiers==["none"]: 

56 shortcut_modifiers = [] 

57 else: 

58 r = [] 

59 for x in shortcut_modifiers: 

60 x = x.lower() 

61 if x not in modifier_names: 

62 log.warn("Warning: invalid shortcut modifier '%s'", x) 

63 else: 

64 r.append(x) 

65 if shortcut_modifiers and not r: 

66 log.warn(" using defaults instead") 

67 shortcut_modifiers = mod_defaults() 

68 else: 

69 shortcut_modifiers = r 

70 log("shortcut modifiers=%s", shortcut_modifiers) 

71 return shortcut_modifiers 

72 

73def parse_shortcuts(strs=(), shortcut_modifiers=(), modifier_names=()): 

74 if not strs: 

75 """ if none are defined, add this as default 

76 it would be nicer to specify it via OptionParser in main 

77 but then it would always have to be there with no way of removing it 

78 whereas now it is enough to define one (any shortcut) 

79 """ 

80 strs = ["meta+shift+F4:quit"] 

81 log("parse_shortcuts(%s)" % str(strs)) 

82 shortcuts = {} 

83 #figure out the default shortcut modifiers 

84 #accept "," or "+" as delimiter: 

85 for s in strs: 

86 #example for s: Control+F8:some_action() 

87 parts = s.split(":", 1) 

88 if len(parts)!=2: 

89 log.error("Error: invalid key shortcut '%s'", s) 

90 continue 

91 #example for action: "quit" 

92 action = parts[1].strip() 

93 args = () 

94 if action.find("(")>0 and action.endswith(")"): 

95 try: 

96 action, all_args = action[:-1].split("(", 1) 

97 args = [] 

98 for x in all_args.split(","): 

99 x = x.strip() 

100 if not x: 

101 continue 

102 if (x[0]=='"' and x[-1]=='"') or (x[0]=="'" and x[-1]=="'"): 

103 args.append(x[1:-1]) 

104 if x=="None": 

105 args.append(None) 

106 if x.find("."): 

107 args.append(float(x)) 

108 else: 

109 args.append(int(x)) 

110 args = tuple(args) 

111 except Exception as e: 

112 log.warn("Warning: failed to parse arguments of shortcut:") 

113 log.warn(" '%s': %s", s, e) 

114 continue 

115 action = action.replace("-", "_") #must be an object attribute 

116 log("action(%s)=%s%s", s, action, args) 

117 

118 #example for keyspec: ["Alt", "F8"] 

119 keyspec = parts[0].split("+") 

120 modifiers = [] 

121 if len(keyspec)>1: 

122 valid = True 

123 #ie: ["Alt"] 

124 for mod in keyspec[:len(keyspec)-1]: 

125 mod = mod.lower() 

126 if mod=="none": 

127 continue 

128 if mod=="#": 

129 #this is the placeholder for the list of shortcut modifiers: 

130 for x in shortcut_modifiers: 

131 imod = modifier_names.get(x) 

132 if imod: 

133 modifiers.append(imod) 

134 continue 

135 #find the real modifier for this name: 

136 #ie: "alt_l" -> "mod1" 

137 imod = modifier_names.get(mod) 

138 if not imod: 

139 log.warn("Warning: invalid modifier '%s' in keyboard shortcut '%s'", mod, s) 

140 log.warn(" the modifiers must be one of: %s", csv(modifier_names.keys())) 

141 valid = False 

142 break 

143 modifiers.append(imod) 

144 if not valid: 

145 continue 

146 #should we be validating the keyname? 

147 keyname = keyspec[len(keyspec)-1] 

148 shortcuts.setdefault(keyname, []).append((modifiers, action, args)) 

149 log("shortcut(%s)=%s", s, csv((modifiers, action, args))) 

150 log("parse_shortcuts(%s)=%s" % (str(strs), shortcuts)) 

151 print_nested_dict(shortcuts, print_fn=log) 

152 return shortcuts