OLD | NEW |
---|---|
1 // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chromeos_keyboard.h" | 5 #include "chromeos_keyboard.h" |
6 | 6 |
7 #include <utility> | 7 #include <utility> |
8 | 8 |
9 #include <X11/XKBlib.h> | 9 #include <X11/XKBlib.h> |
10 #include <X11/Xlib.h> | 10 #include <X11/Xlib.h> |
11 #include <glib.h> | 11 #include <glib.h> |
12 #include <stdlib.h> | 12 #include <stdlib.h> |
13 #include <string.h> | 13 #include <string.h> |
14 #include <xkeyboard_config_version.h> | 14 #include <xkeyboard_config_version.h> |
15 | 15 |
16 #include "base/logging.h" | 16 #include "base/logging.h" |
17 #include "base/singleton.h" | 17 #include "base/singleton.h" |
18 #include "base/string_util.h" | 18 #include "base/string_util.h" |
19 | 19 |
20 namespace { | 20 namespace { |
21 | 21 |
22 // The command we use to set/get the current XKB layout and modifier key | 22 // The default keyboard layout name in the xorg config file. |
23 // mapping. | 23 const char kDefaultLayoutName[] = "us"; |
24 // The command we use to set the current XKB layout and modifier key mapping. | |
25 // TODO(yusukes): Use libxkbfile.so instead of the command. | |
satorux1
2011/03/14 20:52:33
Please file a bug and add a link to it.
Yusuke Sato
2011/03/15 03:09:41
Done.
| |
24 const char kSetxkbmapCommand[] = "/usr/bin/setxkbmap"; | 26 const char kSetxkbmapCommand[] = "/usr/bin/setxkbmap"; |
25 // See the comment at ModifierKey in the .h file. | 27 // See the comment at ModifierKey in the .h file. |
26 chromeos::ModifierKey kCustomizableKeys[] = { | 28 chromeos::ModifierKey kCustomizableKeys[] = { |
27 chromeos::kSearchKey, | 29 chromeos::kSearchKey, |
28 chromeos::kLeftControlKey, | 30 chromeos::kLeftControlKey, |
29 chromeos::kLeftAltKey | 31 chromeos::kLeftAltKey |
30 }; | 32 }; |
31 | 33 |
32 // This is a wrapper class around Display, that opens and closes X display in | 34 // This is a wrapper class around Display, that opens and closes X display in |
33 // the constructor and destructor. | 35 // the constructor and destructor. |
(...skipping 26 matching lines...) Expand all Loading... | |
60 public: | 62 public: |
61 // Returns the singleton instance of the class. Use LeakySingletonTraits. | 63 // Returns the singleton instance of the class. Use LeakySingletonTraits. |
62 // We don't delete the instance at exit. | 64 // We don't delete the instance at exit. |
63 static XKeyboard* Get() { | 65 static XKeyboard* Get() { |
64 return Singleton<XKeyboard, LeakySingletonTraits<XKeyboard> >::get(); | 66 return Singleton<XKeyboard, LeakySingletonTraits<XKeyboard> >::get(); |
65 } | 67 } |
66 | 68 |
67 // Sets the current keyboard layout to |layout_name|. This function does not | 69 // Sets the current keyboard layout to |layout_name|. This function does not |
68 // change the current mapping of the modifier keys. Returns true on success. | 70 // change the current mapping of the modifier keys. Returns true on success. |
69 bool SetLayout(const std::string& layout_name) { | 71 bool SetLayout(const std::string& layout_name) { |
70 // TODO(yusukes): write auto tests for the function. | 72 if (SetLayoutInternal(layout_name, current_modifier_map_, true)) { |
71 chromeos::ModifierMap modifier_map; | 73 current_layout_name_ = layout_name; |
72 if (!GetModifierMapping(&modifier_map)) { | |
73 LOG(ERROR) << "Failed to get modifier mapping."; | |
74 return false; | |
75 } | |
76 if (SetLayoutInternal(layout_name, modifier_map, true)) { | |
77 return true; | 74 return true; |
78 } | 75 } |
76 // TODO(satorux,yusukes): Remove +version hack. | |
79 LOG(ERROR) << "SetLayoutInternal failed. Retrying without +version option"; | 77 LOG(ERROR) << "SetLayoutInternal failed. Retrying without +version option"; |
80 return SetLayoutInternal(layout_name, modifier_map, false); | 78 if (SetLayoutInternal(layout_name, current_modifier_map_, false)) { |
79 current_layout_name_ = layout_name; | |
80 return true; | |
81 } | |
82 return false; | |
81 } | 83 } |
82 | 84 |
83 // Remaps modifier keys. This function does not change the current keyboard | 85 // Remaps modifier keys. This function does not change the current keyboard |
84 // layout. Returns true on success. | 86 // layout. Returns true on success. |
85 bool RemapModifierKeys(const chromeos::ModifierMap& modifier_map) { | 87 bool RemapModifierKeys(const chromeos::ModifierMap& modifier_map) { |
86 // TODO(yusukes): write auto tests for the function. | 88 // TODO(yusukes): write auto tests for the function. |
87 modifier_keys_are_remapped_ = false; | 89 if (SetLayoutInternal(current_layout_name_, modifier_map, true)) { |
88 const std::string layout_name = GetLayout(); | 90 current_modifier_map_ = modifier_map; |
89 if (layout_name.empty()) { | |
90 return false; | |
91 } | |
92 if (SetLayoutInternal(layout_name, modifier_map, true)) { | |
93 modifier_keys_are_remapped_ = true; | |
94 return true; | 91 return true; |
95 } | 92 } |
93 // TODO(satorux,yusukes): Remove +version hack. | |
96 LOG(ERROR) << "SetLayoutInternal failed. Retrying without +version option"; | 94 LOG(ERROR) << "SetLayoutInternal failed. Retrying without +version option"; |
97 if (SetLayoutInternal(layout_name, modifier_map, false)) { | 95 if (SetLayoutInternal(current_layout_name_, modifier_map, false)) { |
98 modifier_keys_are_remapped_ = true; | 96 current_modifier_map_ = modifier_map; |
99 return true; | 97 return true; |
100 } | 98 } |
101 return false; | 99 return false; |
102 } | 100 } |
103 | 101 |
104 // Returns the current layout name like "us". On error, returns "". | |
105 std::string GetLayout() { | |
106 // TODO(yusukes): write auto tests for the function. | |
107 std::string command_output = last_full_layout_name_; | |
108 | |
109 if (command_output.empty()) { | |
110 // Cache is not available. Execute setxkbmap to get the current layout. | |
111 if (!ExecuteGetLayoutCommand(&command_output)) { | |
112 return ""; | |
113 } | |
114 } | |
115 | |
116 const std::string layout_name = | |
117 chromeos::ExtractLayoutNameFromFullXkbLayoutName(command_output); | |
118 LOG(INFO) << "Current XKB layout name: " << layout_name; | |
119 return layout_name; | |
120 } | |
121 | |
122 // Gets the current auto-repeat mode of the keyboard. The result is stored in | 102 // Gets the current auto-repeat mode of the keyboard. The result is stored in |
123 // |mode|. Returns true on success. | 103 // |mode|. Returns true on success. |
104 // TODO(yusukes): Remove this function. | |
124 bool GetAutoRepeatEnabled(bool* enabled) { | 105 bool GetAutoRepeatEnabled(bool* enabled) { |
125 // TODO(yusukes): write auto tests for the function. | |
126 DCHECK(enabled); | 106 DCHECK(enabled); |
127 ScopedDisplay display(XOpenDisplay(NULL)); | 107 ScopedDisplay display(XOpenDisplay(NULL)); |
128 if (!display.get()) { | 108 if (!display.get()) { |
129 return false; | 109 return false; |
130 } | 110 } |
131 XKeyboardState values = {}; | 111 XKeyboardState values = {}; |
132 XGetKeyboardControl(display.get(), &values); | 112 XGetKeyboardControl(display.get(), &values); |
133 if (values.global_auto_repeat == 0) { | 113 if (values.global_auto_repeat == 0) { |
134 *enabled = false; | 114 *enabled = false; |
135 } else { | 115 } else { |
136 *enabled = true; | 116 *enabled = true; |
137 } | 117 } |
138 return true; | 118 return true; |
139 } | 119 } |
140 | 120 |
141 // Turns on and off the auto-repeat of the keyboard. Returns true on success. | 121 // Turns on and off the auto-repeat of the keyboard. Returns true on success. |
122 // TODO(yusukes): Remove this function. | |
142 bool SetAutoRepeatEnabled(bool enabled) { | 123 bool SetAutoRepeatEnabled(bool enabled) { |
143 // TODO(yusukes): write auto tests for the function. | |
144 ScopedDisplay display(XOpenDisplay(NULL)); | 124 ScopedDisplay display(XOpenDisplay(NULL)); |
145 if (!display.get()) { | 125 if (!display.get()) { |
146 return false; | 126 return false; |
147 } | 127 } |
148 if (enabled) { | 128 if (enabled) { |
149 XAutoRepeatOn(display.get()); | 129 XAutoRepeatOn(display.get()); |
150 } else { | 130 } else { |
151 XAutoRepeatOff(display.get()); | 131 XAutoRepeatOff(display.get()); |
152 } | 132 } |
153 DLOG(INFO) << "Set auto-repeat mode to: " << (enabled ? "on" : "off"); | 133 DLOG(INFO) << "Set auto-repeat mode to: " << (enabled ? "on" : "off"); |
154 return true; | 134 return true; |
155 } | 135 } |
156 | 136 |
157 // Gets the current auto-repeat rate of the keyboard. The result is stored in | 137 // Gets the current auto-repeat rate of the keyboard. The result is stored in |
158 // |out_rate|. Returns true on success. | 138 // |out_rate|. Returns true on success. |
139 // TODO(yusukes): Remove this function. | |
159 bool GetAutoRepeatRate(chromeos::AutoRepeatRate* out_rate) { | 140 bool GetAutoRepeatRate(chromeos::AutoRepeatRate* out_rate) { |
160 // TODO(yusukes): write auto tests for the function. | |
161 ScopedDisplay display(XOpenDisplay(NULL)); | 141 ScopedDisplay display(XOpenDisplay(NULL)); |
162 if (!display.get()) { | 142 if (!display.get()) { |
163 return false; | 143 return false; |
164 } | 144 } |
165 if (XkbGetAutoRepeatRate(display.get(), XkbUseCoreKbd, | 145 if (XkbGetAutoRepeatRate(display.get(), XkbUseCoreKbd, |
166 &(out_rate->initial_delay_in_ms), | 146 &(out_rate->initial_delay_in_ms), |
167 &(out_rate->repeat_interval_in_ms)) != True) { | 147 &(out_rate->repeat_interval_in_ms)) != True) { |
168 out_rate->initial_delay_in_ms = 0; | 148 out_rate->initial_delay_in_ms = 0; |
169 out_rate->repeat_interval_in_ms = 0; | 149 out_rate->repeat_interval_in_ms = 0; |
170 return false; | 150 return false; |
171 } | 151 } |
172 return true; | 152 return true; |
173 } | 153 } |
174 | 154 |
175 // Sets the auto-repeat rate of the keyboard, initial delay in ms, and repeat | 155 // Sets the auto-repeat rate of the keyboard, initial delay in ms, and repeat |
176 // interval in ms. Returns true on success. | 156 // interval in ms. Returns true on success. |
157 // TODO(yusukes): Call this function in non-UI thread or in an idle callback. | |
177 bool SetAutoRepeatRate(const chromeos::AutoRepeatRate& rate) { | 158 bool SetAutoRepeatRate(const chromeos::AutoRepeatRate& rate) { |
178 // TODO(yusukes): write auto tests for the function. | 159 // TODO(yusukes): write auto tests for the function. |
179 ScopedDisplay display(XOpenDisplay(NULL)); | 160 ScopedDisplay display(XOpenDisplay(NULL)); |
180 if (!display.get()) { | 161 if (!display.get()) { |
181 return false; | 162 return false; |
182 } | 163 } |
183 | 164 |
184 DLOG(INFO) << "Set auto-repeat rate to: " | 165 DLOG(INFO) << "Set auto-repeat rate to: " |
185 << rate.initial_delay_in_ms << " ms delay, " | 166 << rate.initial_delay_in_ms << " ms delay, " |
186 << rate.repeat_interval_in_ms << " ms interval"; | 167 << rate.repeat_interval_in_ms << " ms interval"; |
187 if (XkbSetAutoRepeatRate(display.get(), XkbUseCoreKbd, | 168 if (XkbSetAutoRepeatRate(display.get(), XkbUseCoreKbd, |
188 rate.initial_delay_in_ms, | 169 rate.initial_delay_in_ms, |
189 rate.repeat_interval_in_ms) != True) { | 170 rate.repeat_interval_in_ms) != True) { |
190 LOG(ERROR) << "Failed to set auto-repeat rate"; | 171 LOG(ERROR) << "Failed to set auto-repeat rate"; |
191 return false; | 172 return false; |
192 } | 173 } |
193 return true; | 174 return true; |
194 } | 175 } |
195 | 176 |
196 private: | 177 private: |
197 friend struct DefaultSingletonTraits<XKeyboard>; | 178 friend struct DefaultSingletonTraits<XKeyboard>; |
198 | 179 |
199 XKeyboard() : modifier_keys_are_remapped_(false) { | 180 XKeyboard() : current_layout_name_(kDefaultLayoutName) { |
200 InitializeStringToModifierMap(&string_to_modifier_map_); | 181 for (size_t i = 0; i < arraysize(kCustomizableKeys); ++i) { |
182 chromeos::ModifierKey key = kCustomizableKeys[i]; | |
183 current_modifier_map_.push_back(chromeos::ModifierKeyPair(key, key)); | |
184 } | |
201 } | 185 } |
202 ~XKeyboard() { | 186 ~XKeyboard() { |
203 } | 187 } |
204 | 188 |
205 // Gets the current modifier mapping and stores them on |out_modifier_map|. | |
206 bool GetModifierMapping(chromeos::ModifierMap* out_modifier_map) { | |
207 out_modifier_map->clear(); | |
208 | |
209 // If modifier keys are not remapped, return a map that doesn't change | |
210 // any key mappings. | |
211 if (!modifier_keys_are_remapped_) { | |
212 for (size_t i = 0; i < arraysize(kCustomizableKeys); ++i) { | |
213 chromeos::ModifierKey key = kCustomizableKeys[i]; | |
214 out_modifier_map->push_back(chromeos::ModifierKeyPair(key, key)); | |
215 } | |
216 return true; | |
217 } | |
218 | |
219 std::string command_output = last_full_layout_name_; | |
220 if (command_output.empty()) { | |
221 // Cache is not available. Execute setxkbmap to get the current mapping. | |
222 if (!ExecuteGetLayoutCommand(&command_output)) { | |
223 return false; | |
224 } | |
225 } | |
226 if (!chromeos::ExtractModifierMapFromFullXkbLayoutName( | |
227 command_output, string_to_modifier_map_, out_modifier_map)) { | |
228 return false; | |
229 } | |
230 return true; | |
231 } | |
232 | |
233 // This function is used by SetLayout() and RemapModifierKeys(). Calls | 189 // This function is used by SetLayout() and RemapModifierKeys(). Calls |
234 // setxkbmap command if needed, and updates the last_full_layout_name_ cache. | 190 // setxkbmap command if needed, and updates the last_full_layout_name_ cache. |
235 // If |use_version| is false, the function does not add "+version(...)" to the | 191 // If |use_version| is false, the function does not add "+version(...)" to the |
236 // layout name. See http://crosbug.com/6261 for details. | 192 // layout name. See http://crosbug.com/6261 for details. |
237 bool SetLayoutInternal(const std::string& layout_name, | 193 bool SetLayoutInternal(const std::string& layout_name, |
238 const chromeos::ModifierMap& modifier_map, | 194 const chromeos::ModifierMap& modifier_map, |
239 bool use_version) { | 195 bool use_version) { |
240 const std::string layouts_to_set = chromeos::CreateFullXkbLayoutName( | 196 const std::string layouts_to_set = chromeos::CreateFullXkbLayoutName( |
241 layout_name, modifier_map, use_version); | 197 layout_name, modifier_map, use_version); |
242 if (layouts_to_set.empty()) { | 198 if (layouts_to_set.empty()) { |
243 return false; | 199 return false; |
244 } | 200 } |
245 | 201 |
246 // Since executing setxkbmap takes more than 200 ms on EeePC, and this | 202 const std::string current_layout = chromeos::CreateFullXkbLayoutName( |
247 // function is called on every focus-in event, try to reduce the number of | 203 current_layout_name_, current_modifier_map_, use_version); |
248 // the fork/exec calls. | 204 if (current_layout == layouts_to_set) { |
249 if (last_full_layout_name_ == layouts_to_set) { | |
250 DLOG(INFO) << "The requested layout is already set: " << layouts_to_set; | 205 DLOG(INFO) << "The requested layout is already set: " << layouts_to_set; |
251 return true; | 206 return true; |
252 } | 207 } |
253 | 208 |
254 // Turn off caps lock if there is no kCapsLockKey in the remapped keys. | 209 // Turn off caps lock if there is no kCapsLockKey in the remapped keys. |
255 if (!ContainsModifierKeyAsReplacement( | 210 if (!ContainsModifierKeyAsReplacement( |
256 modifier_map, chromeos::kCapsLockKey)) { | 211 modifier_map, chromeos::kCapsLockKey)) { |
257 chromeos::SetCapsLockEnabled(false); | 212 chromeos::SetCapsLockEnabled(false); |
258 } | 213 } |
259 | 214 |
260 gint exit_status = -1; | 215 ExecuteSetLayoutCommand(layouts_to_set); |
261 const gboolean successful = | 216 return true; |
262 ExecuteSetLayoutCommand(layouts_to_set, &exit_status); | |
263 | |
264 // On success, update the cache and return true. | |
265 if (successful && (exit_status == 0)) { | |
266 last_full_layout_name_ = layouts_to_set; | |
267 DLOG(INFO) << "XKB layout is changed to " << layouts_to_set; | |
268 return true; | |
269 } | |
270 | |
271 LOG(ERROR) << "Failed to change XKB layout to: " << layouts_to_set; | |
272 last_full_layout_name_.clear(); // invalidate the cache. | |
273 return false; | |
274 } | 217 } |
275 | 218 |
276 // Executes 'setxkbmap -layout ...' command. Returns true if execve suceeds. | 219 // Executes 'setxkbmap -layout ...' command asynchronously. |
277 bool ExecuteSetLayoutCommand(const std::string& layouts_to_set, | 220 // TODO(yusukes): Use libxkbfile.so instead of the command. |
278 gint* out_exit_status) { | 221 void ExecuteSetLayoutCommand(const std::string& layouts_to_set) { |
279 *out_exit_status = -1; | |
280 | |
281 gchar* argv[] = { | 222 gchar* argv[] = { |
282 g_strdup(kSetxkbmapCommand), g_strdup("-layout"), | 223 g_strdup(kSetxkbmapCommand), g_strdup("-layout"), |
283 g_strdup(layouts_to_set.c_str()), NULL | 224 g_strdup(layouts_to_set.c_str()), NULL |
284 }; | 225 }; |
226 | |
227 const GSpawnFlags flags = static_cast<GSpawnFlags>( | |
228 G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL); | |
285 GError* error = NULL; | 229 GError* error = NULL; |
286 const gboolean successful = g_spawn_sync(NULL, argv, NULL, | 230 g_spawn_async(NULL, // working_directory |
287 static_cast<GSpawnFlags>(0), | 231 argv, |
288 NULL, NULL, NULL, NULL, | 232 NULL, // envp |
289 out_exit_status, &error); | 233 flags, |
234 NULL, // child_setup | |
235 NULL, // user_data | |
236 NULL, // child_pid | |
237 &error); | |
238 | |
290 for (size_t i = 0; argv[i] != NULL; ++i) { | 239 for (size_t i = 0; argv[i] != NULL; ++i) { |
291 g_free(argv[i]); | 240 g_free(argv[i]); |
292 } | 241 } |
293 | 242 if (error) { |
294 if (!successful) { | 243 if (error->message) { |
295 if (error && error->message) { | |
296 LOG(ERROR) << "Failed to execute setxkbmap: " << error->message; | 244 LOG(ERROR) << "Failed to execute setxkbmap: " << error->message; |
297 } | 245 } |
298 g_error_free(error); | 246 g_error_free(error); |
299 } | 247 } |
300 return (successful == TRUE); | |
301 } | 248 } |
302 | 249 |
303 // Executes 'setxkbmap -print' command and parses the output (stdout) from the | 250 // The XKB layout name which we set last time like "us" and "us(dvorak)". |
304 // command. Returns true if both execve and parsing the output succeed. | 251 std::string current_layout_name_; |
305 // On success, it stores a string like "us+chromeos(..)+version(..)+inet(..)" | 252 // The mapping of modifier keys we set last time. |
306 // on |out_command_output|. | 253 chromeos::ModifierMap current_modifier_map_; |
307 bool ExecuteGetLayoutCommand(std::string* out_command_output) { | |
308 out_command_output->clear(); | |
309 | |
310 gint exit_status = -1; | |
311 gchar* argv[] = { g_strdup(kSetxkbmapCommand), g_strdup("-print"), NULL }; | |
312 gchar* raw_command_output = NULL; | |
313 GError* error = NULL; | |
314 const gboolean successful = g_spawn_sync(NULL, argv, NULL, | |
315 static_cast<GSpawnFlags>(0), | |
316 NULL, NULL, | |
317 &raw_command_output, NULL, | |
318 &exit_status, &error); | |
319 for (size_t i = 0; argv[i] != NULL; ++i) { | |
320 g_free(argv[i]); | |
321 } | |
322 | |
323 if (!successful) { | |
324 if (error && error->message) { | |
325 LOG(ERROR) << "Failed to execute setxkbmap: " << error->message; | |
326 } | |
327 g_error_free(error); | |
328 return false; | |
329 } | |
330 | |
331 // g_spawn_sync succeeded. | |
332 std::string command_output = raw_command_output ? raw_command_output : ""; | |
333 g_free(raw_command_output); | |
334 raw_command_output = NULL; // DO NOT USE |raw_command_output| below. | |
335 | |
336 if (exit_status != 0) { | |
337 return false; | |
338 } | |
339 // Parse a line like: | |
340 // "xkb_symbols { include "pc+us+chromeos(..)+version(..)+inet(pc105)" };" | |
341 const size_t cursor = command_output.find("pc+"); | |
342 if (cursor == std::string::npos) { | |
343 LOG(ERROR) << "pc+ is not found: " << command_output; | |
344 return false; | |
345 } | |
346 *out_command_output = command_output.substr(cursor + 3); // Skip "pc+". | |
347 return true; | |
348 } | |
349 | |
350 // The XKB layout name which we set last time like | |
351 // "us+chromeos(search_leftcontrol_leftalt)". | |
352 std::string last_full_layout_name_; | |
353 | |
354 // A std::map that holds mappings like: "leftcontrol_disabled_leftalt" -> | |
355 // { LEFT_CONTROL_KEY, VOID_KEY, LEFT_ALT_KEY }. | |
356 chromeos::StringToModifierMap string_to_modifier_map_; | |
357 | |
358 // True if modifier keys are remapped. | |
359 bool modifier_keys_are_remapped_; | |
360 | 254 |
361 DISALLOW_COPY_AND_ASSIGN(XKeyboard); | 255 DISALLOW_COPY_AND_ASSIGN(XKeyboard); |
362 }; | 256 }; |
363 | 257 |
364 } // namespace | 258 } // namespace |
365 | 259 |
366 namespace chromeos { | 260 namespace chromeos { |
367 | 261 |
368 std::string CreateFullXkbLayoutName(const std::string& layout_name, | 262 std::string CreateFullXkbLayoutName(const std::string& layout_name, |
369 const ModifierMap& modifier_map, | 263 const ModifierMap& modifier_map, |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
433 version.c_str()); | 327 version.c_str()); |
434 | 328 |
435 if ((full_xkb_layout_name.substr(0, 3) != "us+") && | 329 if ((full_xkb_layout_name.substr(0, 3) != "us+") && |
436 (full_xkb_layout_name.substr(0, 3) != "us(")) { | 330 (full_xkb_layout_name.substr(0, 3) != "us(")) { |
437 full_xkb_layout_name += ",us"; | 331 full_xkb_layout_name += ",us"; |
438 } | 332 } |
439 | 333 |
440 return full_xkb_layout_name; | 334 return full_xkb_layout_name; |
441 } | 335 } |
442 | 336 |
443 std::string ExtractLayoutNameFromFullXkbLayoutName( | 337 // This function is only for unittest. |
444 const std::string& full_xkb_layout_name) { | |
445 const size_t next_plus_pos = full_xkb_layout_name.find('+'); | |
446 if (next_plus_pos == std::string::npos) { | |
447 LOG(ERROR) << "Bad layout name: " << full_xkb_layout_name; | |
448 return ""; | |
449 } | |
450 return full_xkb_layout_name.substr(0, next_plus_pos); | |
451 } | |
452 | |
453 void InitializeStringToModifierMap(StringToModifierMap* out_map) { | |
454 DCHECK(out_map); | |
455 out_map->clear(); | |
456 | |
457 for (int i = 0; i < static_cast<int>(kNumModifierKeys); ++i) { | |
458 for (int j = 0; j < static_cast<int>(kNumModifierKeys); ++j) { | |
459 for (int k = 0; k < static_cast<int>(kNumModifierKeys); ++k) { | |
460 const std::string string_rep = StringPrintf( | |
461 "%s_%s_%s", | |
462 ModifierKeyToString(ModifierKey(i)).c_str(), | |
463 ModifierKeyToString(ModifierKey(j)).c_str(), | |
464 ModifierKeyToString(ModifierKey(k)).c_str()); | |
465 ModifierMap modifier_map; | |
466 modifier_map.push_back(ModifierKeyPair(kSearchKey, ModifierKey(i))); | |
467 modifier_map.push_back( | |
468 ModifierKeyPair(kLeftControlKey, ModifierKey(j))); | |
469 modifier_map.push_back(ModifierKeyPair(kLeftAltKey, ModifierKey(k))); | |
470 out_map->insert(make_pair(string_rep, modifier_map)); | |
471 } | |
472 } | |
473 } | |
474 } | |
475 | |
476 bool ExtractModifierMapFromFullXkbLayoutName( | |
477 const std::string& full_xkb_layout_name, | |
478 const StringToModifierMap& string_to_modifier_map, | |
479 ModifierMap* out_modifier_map) { | |
480 static const char kMark[] = "+chromeos("; | |
481 const size_t kMarkLen = strlen(kMark); | |
482 | |
483 out_modifier_map->clear(); | |
484 const size_t mark_pos = full_xkb_layout_name.find(kMark); | |
485 if (mark_pos == std::string::npos) { | |
486 LOG(ERROR) << "Bad layout name: " << full_xkb_layout_name; | |
487 return false; | |
488 } | |
489 | |
490 const std::string tmp = // e.g. "leftcontrol_disabled_leftalt), us" | |
491 full_xkb_layout_name.substr(mark_pos + kMarkLen); | |
492 const size_t next_paren_pos = tmp.find(')'); | |
493 if (next_paren_pos == std::string::npos) { | |
494 LOG(ERROR) << "Bad layout name: " << full_xkb_layout_name; | |
495 return false; | |
496 } | |
497 | |
498 const std::string modifier_map_string = tmp.substr(0, next_paren_pos); | |
499 DLOG(INFO) << "Modifier mapping is: " << modifier_map_string; | |
500 | |
501 StringToModifierMap::const_iterator iter = | |
502 string_to_modifier_map.find(modifier_map_string); | |
503 if (iter == string_to_modifier_map.end()) { | |
504 LOG(ERROR) << "Bad mapping name '" << modifier_map_string | |
505 << "' in layout name '" << full_xkb_layout_name << "'"; | |
506 return false; | |
507 } | |
508 | |
509 *out_modifier_map = iter->second; | |
510 return true; | |
511 } | |
512 | |
513 bool CapsLockIsEnabled() { | 338 bool CapsLockIsEnabled() { |
514 ScopedDisplay display(XOpenDisplay(NULL)); | 339 ScopedDisplay display(XOpenDisplay(NULL)); |
515 if (!display.get()) { | 340 if (!display.get()) { |
516 return false; | 341 return false; |
517 } | 342 } |
518 XkbStateRec status; | 343 XkbStateRec status; |
519 XkbGetState(display.get(), XkbUseCoreKbd, &status); | 344 XkbGetState(display.get(), XkbUseCoreKbd, &status); |
520 return status.locked_mods & LockMask; | 345 return status.locked_mods & LockMask; |
521 } | 346 } |
522 | 347 |
348 // TODO(yusukes): Call this function in non-UI thread or in an idle callback. | |
523 void SetCapsLockEnabled(bool enable_caps_lock) { | 349 void SetCapsLockEnabled(bool enable_caps_lock) { |
524 ScopedDisplay display(XOpenDisplay(NULL)); | 350 ScopedDisplay display(XOpenDisplay(NULL)); |
525 if (!display.get()) { | 351 if (!display.get()) { |
526 return; | 352 return; |
527 } | 353 } |
528 XkbLockModifiers( | 354 XkbLockModifiers( |
529 display.get(), XkbUseCoreKbd, LockMask, enable_caps_lock ? LockMask : 0); | 355 display.get(), XkbUseCoreKbd, LockMask, enable_caps_lock ? LockMask : 0); |
530 } | 356 } |
531 | 357 |
532 bool ContainsModifierKeyAsReplacement( | 358 bool ContainsModifierKeyAsReplacement( |
(...skipping 15 matching lines...) Expand all Loading... | |
548 extern "C" | 374 extern "C" |
549 bool ChromeOSSetCurrentKeyboardLayoutByName(const std::string& layout_name) { | 375 bool ChromeOSSetCurrentKeyboardLayoutByName(const std::string& layout_name) { |
550 return XKeyboard::Get()->SetLayout(layout_name); | 376 return XKeyboard::Get()->SetLayout(layout_name); |
551 } | 377 } |
552 | 378 |
553 extern "C" | 379 extern "C" |
554 bool ChromeOSRemapModifierKeys(const chromeos::ModifierMap& modifier_map) { | 380 bool ChromeOSRemapModifierKeys(const chromeos::ModifierMap& modifier_map) { |
555 return XKeyboard::Get()->RemapModifierKeys(modifier_map); | 381 return XKeyboard::Get()->RemapModifierKeys(modifier_map); |
556 } | 382 } |
557 | 383 |
384 // TODO(yusukes): Remove this function. | |
558 extern "C" | 385 extern "C" |
559 bool ChromeOSGetAutoRepeatEnabled(bool* enabled) { | 386 bool ChromeOSGetAutoRepeatEnabled(bool* enabled) { |
560 return XKeyboard::Get()->GetAutoRepeatEnabled(enabled); | 387 return XKeyboard::Get()->GetAutoRepeatEnabled(enabled); |
561 } | 388 } |
562 | 389 |
390 // TODO(yusukes): We can remove this function since the default setting of the | |
391 // repeat mode is true, and we don't change the default. | |
563 extern "C" | 392 extern "C" |
564 bool ChromeOSSetAutoRepeatEnabled(bool enabled) { | 393 bool ChromeOSSetAutoRepeatEnabled(bool enabled) { |
565 return XKeyboard::Get()->SetAutoRepeatEnabled(enabled); | 394 return XKeyboard::Get()->SetAutoRepeatEnabled(enabled); |
566 } | 395 } |
567 | 396 |
397 // TODO(yusukes): Remove this function. | |
568 extern "C" | 398 extern "C" |
569 bool ChromeOSGetAutoRepeatRate(chromeos::AutoRepeatRate* out_rate) { | 399 bool ChromeOSGetAutoRepeatRate(chromeos::AutoRepeatRate* out_rate) { |
570 return XKeyboard::Get()->GetAutoRepeatRate(out_rate); | 400 return XKeyboard::Get()->GetAutoRepeatRate(out_rate); |
571 } | 401 } |
572 | 402 |
573 extern "C" | 403 extern "C" |
574 bool ChromeOSSetAutoRepeatRate(const chromeos::AutoRepeatRate& rate) { | 404 bool ChromeOSSetAutoRepeatRate(const chromeos::AutoRepeatRate& rate) { |
575 return XKeyboard::Get()->SetAutoRepeatRate(rate); | 405 return XKeyboard::Get()->SetAutoRepeatRate(rate); |
576 } | 406 } |
OLD | NEW |