OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 The Chromium OS Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 // | |
5 // DEPRECATED. TODO(satorux): Remove this file. | |
6 | |
7 #include "chromeos_keyboard.h" | |
8 | |
9 #include <utility> | |
10 | |
11 #include <X11/XKBlib.h> | |
12 #include <X11/Xlib.h> | |
13 #include <glib.h> | |
14 #include <stdlib.h> | |
15 #include <string.h> | |
16 | |
17 #include "base/logging.h" | |
18 #include "base/singleton.h" | |
19 #include "base/string_util.h" | |
20 #include "chromeos/process.h" | |
21 | |
22 namespace { | |
23 | |
24 // The default keyboard layout name in the xorg config file. | |
25 const char kDefaultLayoutName[] = "us"; | |
26 // The command we use to set the current XKB layout and modifier key mapping. | |
27 // TODO(yusukes): Use libxkbfile.so instead of the command (crosbug.com/13105) | |
28 const char kSetxkbmapCommand[] = "/usr/bin/setxkbmap"; | |
29 // See the comment at ModifierKey in the .h file. | |
30 chromeos::ModifierKey kCustomizableKeys[] = { | |
31 chromeos::kSearchKey, | |
32 chromeos::kLeftControlKey, | |
33 chromeos::kLeftAltKey | |
34 }; | |
35 | |
36 // This is a wrapper class around Display, that opens and closes X display in | |
37 // the constructor and destructor. | |
38 class ScopedDisplay { | |
39 public: | |
40 explicit ScopedDisplay(Display* display) : display_(display) { | |
41 if (!display_) { | |
42 LOG(ERROR) << "NULL display_ is passed"; | |
43 } | |
44 } | |
45 | |
46 ~ScopedDisplay() { | |
47 if (display_) { | |
48 XCloseDisplay(display_); | |
49 } | |
50 } | |
51 | |
52 Display* get() const { | |
53 return display_; | |
54 } | |
55 | |
56 private: | |
57 Display* display_; | |
58 | |
59 DISALLOW_COPY_AND_ASSIGN(ScopedDisplay); | |
60 }; | |
61 | |
62 // A singleton class which wraps the setxkbmap command. | |
63 class XKeyboard { | |
64 public: | |
65 // Returns the singleton instance of the class. Use LeakySingletonTraits. | |
66 // We don't delete the instance at exit. | |
67 static XKeyboard* Get() { | |
68 return Singleton<XKeyboard, LeakySingletonTraits<XKeyboard> >::get(); | |
69 } | |
70 | |
71 // Sets the current keyboard layout to |layout_name|. This function does not | |
72 // change the current mapping of the modifier keys. Returns true on success. | |
73 bool SetLayout(const std::string& layout_name) { | |
74 if (SetLayoutInternal(layout_name, current_modifier_map_)) { | |
75 current_layout_name_ = layout_name; | |
76 return true; | |
77 } | |
78 return false; | |
79 } | |
80 | |
81 // Remaps modifier keys. This function does not change the current keyboard | |
82 // layout. Returns true on success. | |
83 bool RemapModifierKeys(const chromeos::ModifierMap& modifier_map) { | |
84 if (SetLayoutInternal(current_layout_name_, modifier_map)) { | |
85 current_modifier_map_ = modifier_map; | |
86 return true; | |
87 } | |
88 return false; | |
89 } | |
90 | |
91 // Gets the current auto-repeat mode of the keyboard. The result is stored in | |
92 // |mode|. Returns true on success. | |
93 // TODO(yusukes): Remove this function. | |
94 bool GetAutoRepeatEnabled(bool* enabled) { | |
95 DCHECK(enabled); | |
96 ScopedDisplay display(XOpenDisplay(NULL)); | |
97 if (!display.get()) { | |
98 return false; | |
99 } | |
100 XKeyboardState values = {}; | |
101 XGetKeyboardControl(display.get(), &values); | |
102 if (values.global_auto_repeat == 0) { | |
103 *enabled = false; | |
104 } else { | |
105 *enabled = true; | |
106 } | |
107 return true; | |
108 } | |
109 | |
110 // Turns on and off the auto-repeat of the keyboard. Returns true on success. | |
111 // TODO(yusukes): Remove this function. | |
112 bool SetAutoRepeatEnabled(bool enabled) { | |
113 ScopedDisplay display(XOpenDisplay(NULL)); | |
114 if (!display.get()) { | |
115 return false; | |
116 } | |
117 if (enabled) { | |
118 XAutoRepeatOn(display.get()); | |
119 } else { | |
120 XAutoRepeatOff(display.get()); | |
121 } | |
122 DLOG(INFO) << "Set auto-repeat mode to: " << (enabled ? "on" : "off"); | |
123 return true; | |
124 } | |
125 | |
126 // Gets the current auto-repeat rate of the keyboard. The result is stored in | |
127 // |out_rate|. Returns true on success. | |
128 // TODO(yusukes): Remove this function. | |
129 bool GetAutoRepeatRate(chromeos::AutoRepeatRate* out_rate) { | |
130 ScopedDisplay display(XOpenDisplay(NULL)); | |
131 if (!display.get()) { | |
132 return false; | |
133 } | |
134 if (XkbGetAutoRepeatRate(display.get(), XkbUseCoreKbd, | |
135 &(out_rate->initial_delay_in_ms), | |
136 &(out_rate->repeat_interval_in_ms)) != True) { | |
137 out_rate->initial_delay_in_ms = 0; | |
138 out_rate->repeat_interval_in_ms = 0; | |
139 return false; | |
140 } | |
141 return true; | |
142 } | |
143 | |
144 // Sets the auto-repeat rate of the keyboard, initial delay in ms, and repeat | |
145 // interval in ms. Returns true on success. | |
146 // TODO(yusukes): Call this function in non-UI thread or in an idle callback. | |
147 bool SetAutoRepeatRate(const chromeos::AutoRepeatRate& rate) { | |
148 // TODO(yusukes): write auto tests for the function. | |
149 ScopedDisplay display(XOpenDisplay(NULL)); | |
150 if (!display.get()) { | |
151 return false; | |
152 } | |
153 | |
154 DLOG(INFO) << "Set auto-repeat rate to: " | |
155 << rate.initial_delay_in_ms << " ms delay, " | |
156 << rate.repeat_interval_in_ms << " ms interval"; | |
157 if (XkbSetAutoRepeatRate(display.get(), XkbUseCoreKbd, | |
158 rate.initial_delay_in_ms, | |
159 rate.repeat_interval_in_ms) != True) { | |
160 LOG(ERROR) << "Failed to set auto-repeat rate"; | |
161 return false; | |
162 } | |
163 return true; | |
164 } | |
165 | |
166 private: | |
167 friend struct DefaultSingletonTraits<XKeyboard>; | |
168 | |
169 XKeyboard() : current_layout_name_(kDefaultLayoutName) { | |
170 for (size_t i = 0; i < arraysize(kCustomizableKeys); ++i) { | |
171 chromeos::ModifierKey key = kCustomizableKeys[i]; | |
172 current_modifier_map_.push_back(chromeos::ModifierKeyPair(key, key)); | |
173 } | |
174 } | |
175 ~XKeyboard() { | |
176 } | |
177 | |
178 // This function is used by SetLayout() and RemapModifierKeys(). Calls | |
179 // setxkbmap command if needed, and updates the last_full_layout_name_ cache. | |
180 bool SetLayoutInternal(const std::string& layout_name, | |
181 const chromeos::ModifierMap& modifier_map) { | |
182 const std::string layouts_to_set = chromeos::CreateFullXkbLayoutName( | |
183 layout_name, modifier_map); | |
184 if (layouts_to_set.empty()) { | |
185 return false; | |
186 } | |
187 | |
188 const std::string current_layout = chromeos::CreateFullXkbLayoutName( | |
189 current_layout_name_, current_modifier_map_); | |
190 if (current_layout == layouts_to_set) { | |
191 DLOG(INFO) << "The requested layout is already set: " << layouts_to_set; | |
192 return true; | |
193 } | |
194 | |
195 // Turn off caps lock if there is no kCapsLockKey in the remapped keys. | |
196 if (!ContainsModifierKeyAsReplacement( | |
197 modifier_map, chromeos::kCapsLockKey)) { | |
198 chromeos::SetCapsLockEnabled(false); | |
199 } | |
200 | |
201 ExecuteSetLayoutCommand(layouts_to_set); | |
202 return true; | |
203 } | |
204 | |
205 // Executes 'setxkbmap -layout ...' command asynchronously. | |
206 // TODO(yusukes): Use libxkbfile.so instead of the command (crosbug.com/13105) | |
207 void ExecuteSetLayoutCommand(const std::string& layouts_to_set) { | |
208 chromeos::ProcessImpl process; | |
209 process.AddArg(kSetxkbmapCommand); | |
210 process.AddStringOption("-layout", layouts_to_set); | |
211 if (!process.Start()) { | |
212 LOG(ERROR) << "Failed to execute setxkbmap: " << layouts_to_set; | |
213 return; | |
214 } | |
215 // g_child_watch_add is necessary to prevent the process from becoming a | |
216 // zombie. | |
217 g_child_watch_add(process.pid(), | |
218 reinterpret_cast<GChildWatchFunc>(OnSetLayoutFinish), | |
219 this); | |
220 process.Release(); // do not kill the setxkbmap process on function exit. | |
221 } | |
222 | |
223 static void OnSetLayoutFinish(GPid pid, gint status, XKeyboard* self) { | |
224 DLOG(INFO) << "OnSetLayoutFinish: pid=" << pid; | |
225 } | |
226 | |
227 // The XKB layout name which we set last time like "us" and "us(dvorak)". | |
228 std::string current_layout_name_; | |
229 // The mapping of modifier keys we set last time. | |
230 chromeos::ModifierMap current_modifier_map_; | |
231 | |
232 DISALLOW_COPY_AND_ASSIGN(XKeyboard); | |
233 }; | |
234 | |
235 } // namespace | |
236 | |
237 namespace chromeos { | |
238 | |
239 std::string CreateFullXkbLayoutName(const std::string& layout_name, | |
240 const ModifierMap& modifier_map) { | |
241 static const char kValidLayoutNameCharacters[] = | |
242 "abcdefghijklmnopqrstuvwxyz0123456789()-_"; | |
243 | |
244 if (layout_name.empty()) { | |
245 LOG(ERROR) << "Invalid layout_name: " << layout_name; | |
246 return ""; | |
247 } | |
248 | |
249 if (layout_name.find_first_not_of(kValidLayoutNameCharacters) != | |
250 std::string::npos) { | |
251 LOG(ERROR) << "Invalid layout_name: " << layout_name; | |
252 return ""; | |
253 } | |
254 | |
255 std::string use_search_key_as_str; | |
256 std::string use_left_control_key_as_str; | |
257 std::string use_left_alt_key_as_str; | |
258 | |
259 for (size_t i = 0; i < modifier_map.size(); ++i) { | |
260 std::string* target = NULL; | |
261 switch (modifier_map[i].original) { | |
262 case kSearchKey: | |
263 target = &use_search_key_as_str; | |
264 break; | |
265 case kLeftControlKey: | |
266 target = &use_left_control_key_as_str; | |
267 break; | |
268 case kLeftAltKey: | |
269 target = &use_left_alt_key_as_str; | |
270 break; | |
271 default: | |
272 break; | |
273 } | |
274 if (!target) { | |
275 LOG(ERROR) << "We don't support remaping " | |
276 << ModifierKeyToString(modifier_map[i].original); | |
277 return ""; | |
278 } | |
279 if (!(target->empty())) { | |
280 LOG(ERROR) << ModifierKeyToString(modifier_map[i].original) | |
281 << " appeared twice"; | |
282 return ""; | |
283 } | |
284 *target = ModifierKeyToString(modifier_map[i].replacement); | |
285 } | |
286 | |
287 if (use_search_key_as_str.empty() || | |
288 use_left_control_key_as_str.empty() || | |
289 use_left_alt_key_as_str.empty()) { | |
290 LOG(ERROR) << "Incomplete ModifierMap: size=" << modifier_map.size(); | |
291 return ""; | |
292 } | |
293 | |
294 std::string full_xkb_layout_name = | |
295 StringPrintf("%s+chromeos(%s_%s_%s)", layout_name.c_str(), | |
296 use_search_key_as_str.c_str(), | |
297 use_left_control_key_as_str.c_str(), | |
298 use_left_alt_key_as_str.c_str()); | |
299 | |
300 if ((full_xkb_layout_name.substr(0, 3) != "us+") && | |
301 (full_xkb_layout_name.substr(0, 3) != "us(")) { | |
302 full_xkb_layout_name += ",us"; | |
303 } | |
304 | |
305 return full_xkb_layout_name; | |
306 } | |
307 | |
308 // This function is only for unittest. | |
309 bool CapsLockIsEnabled() { | |
310 ScopedDisplay display(XOpenDisplay(NULL)); | |
311 if (!display.get()) { | |
312 return false; | |
313 } | |
314 XkbStateRec status; | |
315 XkbGetState(display.get(), XkbUseCoreKbd, &status); | |
316 return status.locked_mods & LockMask; | |
317 } | |
318 | |
319 // TODO(yusukes): Call this function in non-UI thread or in an idle callback. | |
320 void SetCapsLockEnabled(bool enable_caps_lock) { | |
321 ScopedDisplay display(XOpenDisplay(NULL)); | |
322 if (!display.get()) { | |
323 return; | |
324 } | |
325 XkbLockModifiers( | |
326 display.get(), XkbUseCoreKbd, LockMask, enable_caps_lock ? LockMask : 0); | |
327 } | |
328 | |
329 bool ContainsModifierKeyAsReplacement( | |
330 const ModifierMap& modifier_map, ModifierKey key) { | |
331 for (size_t i = 0; i < modifier_map.size(); ++i) { | |
332 if (modifier_map[i].replacement == key) { | |
333 return true; | |
334 } | |
335 } | |
336 return false; | |
337 } | |
338 | |
339 } // namespace chromeos | |
340 | |
341 | |
342 // | |
343 // licros APIs. | |
344 // | |
345 extern "C" | |
346 bool ChromeOSSetCurrentKeyboardLayoutByName(const std::string& layout_name) { | |
347 return XKeyboard::Get()->SetLayout(layout_name); | |
348 } | |
349 | |
350 extern "C" | |
351 bool ChromeOSRemapModifierKeys(const chromeos::ModifierMap& modifier_map) { | |
352 return XKeyboard::Get()->RemapModifierKeys(modifier_map); | |
353 } | |
354 | |
355 // TODO(yusukes): Remove this function. | |
356 extern "C" | |
357 bool ChromeOSGetAutoRepeatEnabled(bool* enabled) { | |
358 return XKeyboard::Get()->GetAutoRepeatEnabled(enabled); | |
359 } | |
360 | |
361 // TODO(yusukes): We can remove this function since the default setting of the | |
362 // repeat mode is true, and we don't change the default. | |
363 extern "C" | |
364 bool ChromeOSSetAutoRepeatEnabled(bool enabled) { | |
365 return XKeyboard::Get()->SetAutoRepeatEnabled(enabled); | |
366 } | |
367 | |
368 // TODO(yusukes): Remove this function. | |
369 extern "C" | |
370 bool ChromeOSGetAutoRepeatRate(chromeos::AutoRepeatRate* out_rate) { | |
371 return XKeyboard::Get()->GetAutoRepeatRate(out_rate); | |
372 } | |
373 | |
374 extern "C" | |
375 bool ChromeOSSetAutoRepeatRate(const chromeos::AutoRepeatRate& rate) { | |
376 return XKeyboard::Get()->SetAutoRepeatRate(rate); | |
377 } | |
OLD | NEW |