Coverage for /home/antoine/projects/xpra-git/dist/python3/lib64/python/xpra/platform/xposix/keyboard.py : 59%
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.
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
13log = Logger("keyboard", "posix")
16class Keyboard(KeyboardBase):
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))
30 def __repr__(self):
31 return "xposix.Keyboard"
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
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 {}, [], []
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 {}
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
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
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
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
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
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)