Chromium Code Reviews| 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 |