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 Nathaniel Smith <njs@pobox.com> 

3# Copyright (C) 2011-2020 Antoine Martin <antoine@xpra.org> 

4# Xpra is released under the terms of the GNU GPL v2, or, at your option, any 

5# later version. See the file COPYING for details. 

6 

7from xpra.platform.keyboard_base import KeyboardBase 

8from xpra.keyboard.mask import MODIFIER_MAP 

9from xpra.keyboard.layouts import xkbmap_query_tostring 

10from xpra.log import Logger 

11from xpra.os_util import is_X11, is_Wayland, bytestostr 

12 

13log = Logger("keyboard", "posix") 

14 

15 

16class Keyboard(KeyboardBase): 

17 

18 def __init__(self): 

19 KeyboardBase.__init__(self) 

20 self.keymap_modifiers = None 

21 self.keyboard_bindings = None 

22 if not is_Wayland(): 

23 try: 

24 from xpra.x11.bindings.keyboard_bindings import X11KeyboardBindings #@UnresolvedImport 

25 self.keyboard_bindings = X11KeyboardBindings() 

26 except Exception as e: 

27 log.error("Error: failed to load posix keyboard bindings") 

28 log.error(" %s", str(e) or type(e)) 

29 

30 def __repr__(self): 

31 return "xposix.Keyboard" 

32 

33 def get_keymap_modifiers(self): 

34 if self.keymap_modifiers is None: 

35 self.keymap_modifiers = self.do_get_keymap_modifiers() 

36 return self.keymap_modifiers 

37 

38 def do_get_keymap_modifiers(self): 

39 if not self.keyboard_bindings: 

40 log.warn("keyboard bindings are not available") 

41 log.warn(" expect keyboard mapping problems") 

42 if is_Wayland(): 

43 log.warn(" (incomplete wayland support)") 

44 return {}, [], [] 

45 try: 

46 from xpra.gtk_common.error import xsync 

47 with xsync: 

48 mod_mappings = self.keyboard_bindings.get_modifier_mappings() 

49 if mod_mappings: 

50 #ie: {"shift" : ["Shift_L", "Shift_R"], "mod1" : "Meta_L", ...]} 

51 log("modifier mappings=%s", mod_mappings) 

52 meanings = {} 

53 for modifier,keys in mod_mappings.items(): 

54 for _,keyname in keys: 

55 meanings[keyname] = modifier 

56 #probably a GTK bug? but easier to put here 

57 mod_missing = [] 

58 numlock_mod = meanings.get("Num_Lock", []) 

59 if numlock_mod: 

60 mod_missing.append(numlock_mod) 

61 return meanings, [], mod_missing 

62 except Exception: 

63 log.error("failed to use native get_modifier_mappings", exc_info=True) 

64 return {}, [], [] 

65 

66 def get_x11_keymap(self): 

67 if not self.keyboard_bindings: 

68 return {} 

69 try: 

70 from xpra.gtk_common.error import xsync 

71 with xsync: 

72 return self.keyboard_bindings.get_keycode_mappings() 

73 except Exception: 

74 log.error("Error: failed to use raw x11 keymap", exc_info=True) 

75 return {} 

76 

77 def get_locale_status(self): 

78 #parse the output into a dictionary: 

79 # $ localectl status 

80 # System Locale: LANG=en_GB.UTF-8 

81 # VC Keymap: gb 

82 # X11 Layout: gb 

83 from subprocess import getoutput 

84 out = getoutput("localectl status") 

85 if not out: 

86 return {} 

87 locale = {} 

88 for line in bytestostr(out).splitlines(): 

89 parts = line.lstrip(" ").split(": ") 

90 if len(parts)==2: 

91 locale[parts[0]]=parts[1] 

92 log("locale(%s)=%s", out, locale) 

93 return locale 

94 

95 def get_keymap_spec(self): 

96 log("get_keymap_spec() keyboard_bindings=%s", self.keyboard_bindings) 

97 if is_Wayland() or not self.keyboard_bindings: 

98 locale = self.get_locale_status() 

99 query_struct = {} 

100 if locale: 

101 layout = locale.get("X11 Layout") 

102 if layout: 

103 query_struct["layout"] = layout 

104 log("query_struct(%s)=%s", locale, query_struct) 

105 return None, None, query_struct 

106 query_struct = self.keyboard_bindings.getXkbProperties() 

107 _query = xkbmap_query_tostring(query_struct) 

108 log("get_keymap_spec() Xkb query tostring(%s)=%s", query_struct, _query) 

109 #we no longer support servers via xkbmap_print: 

110 xkbmap_print = "" 

111 log("get_keymap_spec()=(%r, %r, %r)", xkbmap_print, _query, query_struct) 

112 return xkbmap_print, _query, query_struct 

113 

114 

115 def get_xkb_rules_names_property(self): 

116 #parses the "_XKB_RULES_NAMES" X11 property 

117 if not is_X11(): 

118 return "" 

119 xkb_rules_names = "" 

120 from xpra.platform.xposix.gui import _get_X11_root_property 

121 prop = _get_X11_root_property("_XKB_RULES_NAMES", "STRING") 

122 log("get_xkb_rules_names_property() _XKB_RULES_NAMES=%s", prop) 

123 #ie: 'evdev\x00pc104\x00gb,us\x00,\x00\x00' 

124 if prop: 

125 xkb_rules_names = prop.split("\0") 

126 #ie: ['evdev', 'pc104', 'gb,us', ',', '', ''] 

127 log("get_xkb_rules_names_property()=%s", xkb_rules_names) 

128 return xkb_rules_names 

129 

130 def get_layout_spec(self): 

131 layout = "" 

132 layouts = [] 

133 options = "" 

134 v = None 

135 if self.keyboard_bindings: 

136 props = self.keyboard_bindings.getXkbProperties() 

137 v = props.get("layout") 

138 options = props.get("options", "") 

139 else: 

140 locale = self.get_locale_status() 

141 v = locale.get("X11 Layout") 

142 if not v: 

143 #fallback: 

144 v = self.get_xkb_rules_names_property() 

145 #ie: ['evdev', 'pc104', 'gb,us', ',', '', ''] 

146 if v and len(v)>=3: 

147 v = v[2] 

148 if v: 

149 layouts = v.split(",") 

150 layout = v 

151 def s(v): 

152 try: 

153 return v.encode("latin1") 

154 except Exception: 

155 return str(v) 

156 return s(layout), [s(x) for x in layouts], "", None, options 

157 

158 

159 def get_keyboard_repeat(self): 

160 v = None 

161 if self.keyboard_bindings: 

162 try: 

163 v = self.keyboard_bindings.get_key_repeat_rate() 

164 if v: 

165 assert len(v)==2 

166 except Exception as e: 

167 log.error("Error: failed to get keyboard repeat rate:") 

168 log.error(" %s", e) 

169 v = None 

170 log("get_keyboard_repeat()=%s", v) 

171 return v 

172 

173 def update_modifier_map(self, display, xkbmap_mod_meanings): 

174 try: 

175 from xpra.x11.gtk_x11.keys import grok_modifier_map 

176 self.modifier_map = grok_modifier_map(display, xkbmap_mod_meanings) 

177 except ImportError: 

178 self.modifier_map = MODIFIER_MAP 

179 #force re-query on next call: 

180 self.keymap_modifiers = None 

181 try: 

182 dn = "%s %s" % (type(display).__name__, display.get_name()) 

183 except Exception: 

184 dn = str(display) 

185 log("update_modifier_map(%s, %s) modifier_map=%s", dn, xkbmap_mod_meanings, self.modifier_map)