| 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 #include "chromeos/process.h" |
| 19 | 20 |
| 20 namespace { | 21 namespace { |
| 21 | 22 |
| 22 // The command we use to set/get the current XKB layout and modifier key | 23 // The default keyboard layout name in the xorg config file. |
| 23 // mapping. | 24 const char kDefaultLayoutName[] = "us"; |
| 25 // The command we use to set the current XKB layout and modifier key mapping. |
| 26 // TODO(yusukes): Use libxkbfile.so instead of the command (crosbug.com/13105) |
| 24 const char kSetxkbmapCommand[] = "/usr/bin/setxkbmap"; | 27 const char kSetxkbmapCommand[] = "/usr/bin/setxkbmap"; |
| 25 // See the comment at ModifierKey in the .h file. | 28 // See the comment at ModifierKey in the .h file. |
| 26 chromeos::ModifierKey kCustomizableKeys[] = { | 29 chromeos::ModifierKey kCustomizableKeys[] = { |
| 27 chromeos::kSearchKey, | 30 chromeos::kSearchKey, |
| 28 chromeos::kLeftControlKey, | 31 chromeos::kLeftControlKey, |
| 29 chromeos::kLeftAltKey | 32 chromeos::kLeftAltKey |
| 30 }; | 33 }; |
| 31 | 34 |
| 32 // This is a wrapper class around Display, that opens and closes X display in | 35 // This is a wrapper class around Display, that opens and closes X display in |
| 33 // the constructor and destructor. | 36 // the constructor and destructor. |
| (...skipping 26 matching lines...) Expand all Loading... |
| 60 public: | 63 public: |
| 61 // Returns the singleton instance of the class. Use LeakySingletonTraits. | 64 // Returns the singleton instance of the class. Use LeakySingletonTraits. |
| 62 // We don't delete the instance at exit. | 65 // We don't delete the instance at exit. |
| 63 static XKeyboard* Get() { | 66 static XKeyboard* Get() { |
| 64 return Singleton<XKeyboard, LeakySingletonTraits<XKeyboard> >::get(); | 67 return Singleton<XKeyboard, LeakySingletonTraits<XKeyboard> >::get(); |
| 65 } | 68 } |
| 66 | 69 |
| 67 // Sets the current keyboard layout to |layout_name|. This function does not | 70 // 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. | 71 // change the current mapping of the modifier keys. Returns true on success. |
| 69 bool SetLayout(const std::string& layout_name) { | 72 bool SetLayout(const std::string& layout_name) { |
| 70 // TODO(yusukes): write auto tests for the function. | 73 if (SetLayoutInternal(layout_name, current_modifier_map_, true)) { |
| 71 chromeos::ModifierMap modifier_map; | 74 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; | 75 return true; |
| 78 } | 76 } |
| 77 // TODO(satorux,yusukes): Remove +version hack. |
| 79 LOG(ERROR) << "SetLayoutInternal failed. Retrying without +version option"; | 78 LOG(ERROR) << "SetLayoutInternal failed. Retrying without +version option"; |
| 80 return SetLayoutInternal(layout_name, modifier_map, false); | 79 if (SetLayoutInternal(layout_name, current_modifier_map_, false)) { |
| 80 current_layout_name_ = layout_name; |
| 81 return true; |
| 82 } |
| 83 return false; |
| 81 } | 84 } |
| 82 | 85 |
| 83 // Remaps modifier keys. This function does not change the current keyboard | 86 // Remaps modifier keys. This function does not change the current keyboard |
| 84 // layout. Returns true on success. | 87 // layout. Returns true on success. |
| 85 bool RemapModifierKeys(const chromeos::ModifierMap& modifier_map) { | 88 bool RemapModifierKeys(const chromeos::ModifierMap& modifier_map) { |
| 86 // TODO(yusukes): write auto tests for the function. | 89 // TODO(yusukes): write auto tests for the function. |
| 87 modifier_keys_are_remapped_ = false; | 90 if (SetLayoutInternal(current_layout_name_, modifier_map, true)) { |
| 88 const std::string layout_name = GetLayout(); | 91 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; | 92 return true; |
| 95 } | 93 } |
| 94 // TODO(satorux,yusukes): Remove +version hack. |
| 96 LOG(ERROR) << "SetLayoutInternal failed. Retrying without +version option"; | 95 LOG(ERROR) << "SetLayoutInternal failed. Retrying without +version option"; |
| 97 if (SetLayoutInternal(layout_name, modifier_map, false)) { | 96 if (SetLayoutInternal(current_layout_name_, modifier_map, false)) { |
| 98 modifier_keys_are_remapped_ = true; | 97 current_modifier_map_ = modifier_map; |
| 99 return true; | 98 return true; |
| 100 } | 99 } |
| 101 return false; | 100 return false; |
| 102 } | 101 } |
| 103 | 102 |
| 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 | 103 // Gets the current auto-repeat mode of the keyboard. The result is stored in |
| 123 // |mode|. Returns true on success. | 104 // |mode|. Returns true on success. |
| 105 // TODO(yusukes): Remove this function. |
| 124 bool GetAutoRepeatEnabled(bool* enabled) { | 106 bool GetAutoRepeatEnabled(bool* enabled) { |
| 125 // TODO(yusukes): write auto tests for the function. | |
| 126 DCHECK(enabled); | 107 DCHECK(enabled); |
| 127 ScopedDisplay display(XOpenDisplay(NULL)); | 108 ScopedDisplay display(XOpenDisplay(NULL)); |
| 128 if (!display.get()) { | 109 if (!display.get()) { |
| 129 return false; | 110 return false; |
| 130 } | 111 } |
| 131 XKeyboardState values = {}; | 112 XKeyboardState values = {}; |
| 132 XGetKeyboardControl(display.get(), &values); | 113 XGetKeyboardControl(display.get(), &values); |
| 133 if (values.global_auto_repeat == 0) { | 114 if (values.global_auto_repeat == 0) { |
| 134 *enabled = false; | 115 *enabled = false; |
| 135 } else { | 116 } else { |
| 136 *enabled = true; | 117 *enabled = true; |
| 137 } | 118 } |
| 138 return true; | 119 return true; |
| 139 } | 120 } |
| 140 | 121 |
| 141 // Turns on and off the auto-repeat of the keyboard. Returns true on success. | 122 // Turns on and off the auto-repeat of the keyboard. Returns true on success. |
| 123 // TODO(yusukes): Remove this function. |
| 142 bool SetAutoRepeatEnabled(bool enabled) { | 124 bool SetAutoRepeatEnabled(bool enabled) { |
| 143 // TODO(yusukes): write auto tests for the function. | |
| 144 ScopedDisplay display(XOpenDisplay(NULL)); | 125 ScopedDisplay display(XOpenDisplay(NULL)); |
| 145 if (!display.get()) { | 126 if (!display.get()) { |
| 146 return false; | 127 return false; |
| 147 } | 128 } |
| 148 if (enabled) { | 129 if (enabled) { |
| 149 XAutoRepeatOn(display.get()); | 130 XAutoRepeatOn(display.get()); |
| 150 } else { | 131 } else { |
| 151 XAutoRepeatOff(display.get()); | 132 XAutoRepeatOff(display.get()); |
| 152 } | 133 } |
| 153 DLOG(INFO) << "Set auto-repeat mode to: " << (enabled ? "on" : "off"); | 134 DLOG(INFO) << "Set auto-repeat mode to: " << (enabled ? "on" : "off"); |
| 154 return true; | 135 return true; |
| 155 } | 136 } |
| 156 | 137 |
| 157 // Gets the current auto-repeat rate of the keyboard. The result is stored in | 138 // Gets the current auto-repeat rate of the keyboard. The result is stored in |
| 158 // |out_rate|. Returns true on success. | 139 // |out_rate|. Returns true on success. |
| 140 // TODO(yusukes): Remove this function. |
| 159 bool GetAutoRepeatRate(chromeos::AutoRepeatRate* out_rate) { | 141 bool GetAutoRepeatRate(chromeos::AutoRepeatRate* out_rate) { |
| 160 // TODO(yusukes): write auto tests for the function. | |
| 161 ScopedDisplay display(XOpenDisplay(NULL)); | 142 ScopedDisplay display(XOpenDisplay(NULL)); |
| 162 if (!display.get()) { | 143 if (!display.get()) { |
| 163 return false; | 144 return false; |
| 164 } | 145 } |
| 165 if (XkbGetAutoRepeatRate(display.get(), XkbUseCoreKbd, | 146 if (XkbGetAutoRepeatRate(display.get(), XkbUseCoreKbd, |
| 166 &(out_rate->initial_delay_in_ms), | 147 &(out_rate->initial_delay_in_ms), |
| 167 &(out_rate->repeat_interval_in_ms)) != True) { | 148 &(out_rate->repeat_interval_in_ms)) != True) { |
| 168 out_rate->initial_delay_in_ms = 0; | 149 out_rate->initial_delay_in_ms = 0; |
| 169 out_rate->repeat_interval_in_ms = 0; | 150 out_rate->repeat_interval_in_ms = 0; |
| 170 return false; | 151 return false; |
| 171 } | 152 } |
| 172 return true; | 153 return true; |
| 173 } | 154 } |
| 174 | 155 |
| 175 // Sets the auto-repeat rate of the keyboard, initial delay in ms, and repeat | 156 // Sets the auto-repeat rate of the keyboard, initial delay in ms, and repeat |
| 176 // interval in ms. Returns true on success. | 157 // interval in ms. Returns true on success. |
| 158 // TODO(yusukes): Call this function in non-UI thread or in an idle callback. |
| 177 bool SetAutoRepeatRate(const chromeos::AutoRepeatRate& rate) { | 159 bool SetAutoRepeatRate(const chromeos::AutoRepeatRate& rate) { |
| 178 // TODO(yusukes): write auto tests for the function. | 160 // TODO(yusukes): write auto tests for the function. |
| 179 ScopedDisplay display(XOpenDisplay(NULL)); | 161 ScopedDisplay display(XOpenDisplay(NULL)); |
| 180 if (!display.get()) { | 162 if (!display.get()) { |
| 181 return false; | 163 return false; |
| 182 } | 164 } |
| 183 | 165 |
| 184 DLOG(INFO) << "Set auto-repeat rate to: " | 166 DLOG(INFO) << "Set auto-repeat rate to: " |
| 185 << rate.initial_delay_in_ms << " ms delay, " | 167 << rate.initial_delay_in_ms << " ms delay, " |
| 186 << rate.repeat_interval_in_ms << " ms interval"; | 168 << rate.repeat_interval_in_ms << " ms interval"; |
| 187 if (XkbSetAutoRepeatRate(display.get(), XkbUseCoreKbd, | 169 if (XkbSetAutoRepeatRate(display.get(), XkbUseCoreKbd, |
| 188 rate.initial_delay_in_ms, | 170 rate.initial_delay_in_ms, |
| 189 rate.repeat_interval_in_ms) != True) { | 171 rate.repeat_interval_in_ms) != True) { |
| 190 LOG(ERROR) << "Failed to set auto-repeat rate"; | 172 LOG(ERROR) << "Failed to set auto-repeat rate"; |
| 191 return false; | 173 return false; |
| 192 } | 174 } |
| 193 return true; | 175 return true; |
| 194 } | 176 } |
| 195 | 177 |
| 196 private: | 178 private: |
| 197 friend struct DefaultSingletonTraits<XKeyboard>; | 179 friend struct DefaultSingletonTraits<XKeyboard>; |
| 198 | 180 |
| 199 XKeyboard() : modifier_keys_are_remapped_(false) { | 181 XKeyboard() : current_layout_name_(kDefaultLayoutName) { |
| 200 InitializeStringToModifierMap(&string_to_modifier_map_); | 182 for (size_t i = 0; i < arraysize(kCustomizableKeys); ++i) { |
| 183 chromeos::ModifierKey key = kCustomizableKeys[i]; |
| 184 current_modifier_map_.push_back(chromeos::ModifierKeyPair(key, key)); |
| 185 } |
| 201 } | 186 } |
| 202 ~XKeyboard() { | 187 ~XKeyboard() { |
| 203 } | 188 } |
| 204 | 189 |
| 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 | 190 // This function is used by SetLayout() and RemapModifierKeys(). Calls |
| 234 // setxkbmap command if needed, and updates the last_full_layout_name_ cache. | 191 // 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 | 192 // If |use_version| is false, the function does not add "+version(...)" to the |
| 236 // layout name. See http://crosbug.com/6261 for details. | 193 // layout name. See http://crosbug.com/6261 for details. |
| 237 bool SetLayoutInternal(const std::string& layout_name, | 194 bool SetLayoutInternal(const std::string& layout_name, |
| 238 const chromeos::ModifierMap& modifier_map, | 195 const chromeos::ModifierMap& modifier_map, |
| 239 bool use_version) { | 196 bool use_version) { |
| 240 const std::string layouts_to_set = chromeos::CreateFullXkbLayoutName( | 197 const std::string layouts_to_set = chromeos::CreateFullXkbLayoutName( |
| 241 layout_name, modifier_map, use_version); | 198 layout_name, modifier_map, use_version); |
| 242 if (layouts_to_set.empty()) { | 199 if (layouts_to_set.empty()) { |
| 243 return false; | 200 return false; |
| 244 } | 201 } |
| 245 | 202 |
| 246 // Since executing setxkbmap takes more than 200 ms on EeePC, and this | 203 const std::string current_layout = chromeos::CreateFullXkbLayoutName( |
| 247 // function is called on every focus-in event, try to reduce the number of | 204 current_layout_name_, current_modifier_map_, use_version); |
| 248 // the fork/exec calls. | 205 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; | 206 DLOG(INFO) << "The requested layout is already set: " << layouts_to_set; |
| 251 return true; | 207 return true; |
| 252 } | 208 } |
| 253 | 209 |
| 254 // Turn off caps lock if there is no kCapsLockKey in the remapped keys. | 210 // Turn off caps lock if there is no kCapsLockKey in the remapped keys. |
| 255 if (!ContainsModifierKeyAsReplacement( | 211 if (!ContainsModifierKeyAsReplacement( |
| 256 modifier_map, chromeos::kCapsLockKey)) { | 212 modifier_map, chromeos::kCapsLockKey)) { |
| 257 chromeos::SetCapsLockEnabled(false); | 213 chromeos::SetCapsLockEnabled(false); |
| 258 } | 214 } |
| 259 | 215 |
| 260 gint exit_status = -1; | 216 ExecuteSetLayoutCommand(layouts_to_set); |
| 261 const gboolean successful = | |
| 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 } | |
| 275 | |
| 276 // Executes 'setxkbmap -layout ...' command. Returns true if execve suceeds. | |
| 277 bool ExecuteSetLayoutCommand(const std::string& layouts_to_set, | |
| 278 gint* out_exit_status) { | |
| 279 *out_exit_status = -1; | |
| 280 | |
| 281 gchar* argv[] = { | |
| 282 g_strdup(kSetxkbmapCommand), g_strdup("-layout"), | |
| 283 g_strdup(layouts_to_set.c_str()), NULL | |
| 284 }; | |
| 285 GError* error = NULL; | |
| 286 const gboolean successful = g_spawn_sync(NULL, argv, NULL, | |
| 287 static_cast<GSpawnFlags>(0), | |
| 288 NULL, NULL, NULL, NULL, | |
| 289 out_exit_status, &error); | |
| 290 for (size_t i = 0; argv[i] != NULL; ++i) { | |
| 291 g_free(argv[i]); | |
| 292 } | |
| 293 | |
| 294 if (!successful) { | |
| 295 if (error && error->message) { | |
| 296 LOG(ERROR) << "Failed to execute setxkbmap: " << error->message; | |
| 297 } | |
| 298 g_error_free(error); | |
| 299 } | |
| 300 return (successful == TRUE); | |
| 301 } | |
| 302 | |
| 303 // Executes 'setxkbmap -print' command and parses the output (stdout) from the | |
| 304 // command. Returns true if both execve and parsing the output succeed. | |
| 305 // On success, it stores a string like "us+chromeos(..)+version(..)+inet(..)" | |
| 306 // on |out_command_output|. | |
| 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; | 217 return true; |
| 348 } | 218 } |
| 349 | 219 |
| 350 // The XKB layout name which we set last time like | 220 // Executes 'setxkbmap -layout ...' command asynchronously. |
| 351 // "us+chromeos(search_leftcontrol_leftalt)". | 221 // TODO(yusukes): Use libxkbfile.so instead of the command (crosbug.com/13105) |
| 352 std::string last_full_layout_name_; | 222 void ExecuteSetLayoutCommand(const std::string& layouts_to_set) { |
| 223 chromeos::ProcessImpl process; |
| 224 process.AddArg(kSetxkbmapCommand); |
| 225 process.AddStringOption("-layout", layouts_to_set); |
| 226 if (!process.Start()) { |
| 227 LOG(ERROR) << "Failed to execute setxkbmap: " << layouts_to_set; |
| 228 return; |
| 229 } |
| 230 // g_child_watch_add is necessary to prevent the process from becoming a |
| 231 // zombie. |
| 232 g_child_watch_add(process.pid(), |
| 233 reinterpret_cast<GChildWatchFunc>(OnSetLayoutFinish), |
| 234 this); |
| 235 process.Release(); // do not kill the setxkbmap process on function exit. |
| 236 } |
| 353 | 237 |
| 354 // A std::map that holds mappings like: "leftcontrol_disabled_leftalt" -> | 238 static void OnSetLayoutFinish(GPid pid, gint status, XKeyboard* self) { |
| 355 // { LEFT_CONTROL_KEY, VOID_KEY, LEFT_ALT_KEY }. | 239 DLOG(INFO) << "OnSetLayoutFinish: pid=" << pid; |
| 356 chromeos::StringToModifierMap string_to_modifier_map_; | 240 } |
| 357 | 241 |
| 358 // True if modifier keys are remapped. | 242 // The XKB layout name which we set last time like "us" and "us(dvorak)". |
| 359 bool modifier_keys_are_remapped_; | 243 std::string current_layout_name_; |
| 244 // The mapping of modifier keys we set last time. |
| 245 chromeos::ModifierMap current_modifier_map_; |
| 360 | 246 |
| 361 DISALLOW_COPY_AND_ASSIGN(XKeyboard); | 247 DISALLOW_COPY_AND_ASSIGN(XKeyboard); |
| 362 }; | 248 }; |
| 363 | 249 |
| 364 } // namespace | 250 } // namespace |
| 365 | 251 |
| 366 namespace chromeos { | 252 namespace chromeos { |
| 367 | 253 |
| 368 std::string CreateFullXkbLayoutName(const std::string& layout_name, | 254 std::string CreateFullXkbLayoutName(const std::string& layout_name, |
| 369 const ModifierMap& modifier_map, | 255 const ModifierMap& modifier_map, |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 433 version.c_str()); | 319 version.c_str()); |
| 434 | 320 |
| 435 if ((full_xkb_layout_name.substr(0, 3) != "us+") && | 321 if ((full_xkb_layout_name.substr(0, 3) != "us+") && |
| 436 (full_xkb_layout_name.substr(0, 3) != "us(")) { | 322 (full_xkb_layout_name.substr(0, 3) != "us(")) { |
| 437 full_xkb_layout_name += ",us"; | 323 full_xkb_layout_name += ",us"; |
| 438 } | 324 } |
| 439 | 325 |
| 440 return full_xkb_layout_name; | 326 return full_xkb_layout_name; |
| 441 } | 327 } |
| 442 | 328 |
| 443 std::string ExtractLayoutNameFromFullXkbLayoutName( | 329 // 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() { | 330 bool CapsLockIsEnabled() { |
| 514 ScopedDisplay display(XOpenDisplay(NULL)); | 331 ScopedDisplay display(XOpenDisplay(NULL)); |
| 515 if (!display.get()) { | 332 if (!display.get()) { |
| 516 return false; | 333 return false; |
| 517 } | 334 } |
| 518 XkbStateRec status; | 335 XkbStateRec status; |
| 519 XkbGetState(display.get(), XkbUseCoreKbd, &status); | 336 XkbGetState(display.get(), XkbUseCoreKbd, &status); |
| 520 return status.locked_mods & LockMask; | 337 return status.locked_mods & LockMask; |
| 521 } | 338 } |
| 522 | 339 |
| 340 // TODO(yusukes): Call this function in non-UI thread or in an idle callback. |
| 523 void SetCapsLockEnabled(bool enable_caps_lock) { | 341 void SetCapsLockEnabled(bool enable_caps_lock) { |
| 524 ScopedDisplay display(XOpenDisplay(NULL)); | 342 ScopedDisplay display(XOpenDisplay(NULL)); |
| 525 if (!display.get()) { | 343 if (!display.get()) { |
| 526 return; | 344 return; |
| 527 } | 345 } |
| 528 XkbLockModifiers( | 346 XkbLockModifiers( |
| 529 display.get(), XkbUseCoreKbd, LockMask, enable_caps_lock ? LockMask : 0); | 347 display.get(), XkbUseCoreKbd, LockMask, enable_caps_lock ? LockMask : 0); |
| 530 } | 348 } |
| 531 | 349 |
| 532 bool ContainsModifierKeyAsReplacement( | 350 bool ContainsModifierKeyAsReplacement( |
| (...skipping 15 matching lines...) Expand all Loading... |
| 548 extern "C" | 366 extern "C" |
| 549 bool ChromeOSSetCurrentKeyboardLayoutByName(const std::string& layout_name) { | 367 bool ChromeOSSetCurrentKeyboardLayoutByName(const std::string& layout_name) { |
| 550 return XKeyboard::Get()->SetLayout(layout_name); | 368 return XKeyboard::Get()->SetLayout(layout_name); |
| 551 } | 369 } |
| 552 | 370 |
| 553 extern "C" | 371 extern "C" |
| 554 bool ChromeOSRemapModifierKeys(const chromeos::ModifierMap& modifier_map) { | 372 bool ChromeOSRemapModifierKeys(const chromeos::ModifierMap& modifier_map) { |
| 555 return XKeyboard::Get()->RemapModifierKeys(modifier_map); | 373 return XKeyboard::Get()->RemapModifierKeys(modifier_map); |
| 556 } | 374 } |
| 557 | 375 |
| 376 // TODO(yusukes): Remove this function. |
| 558 extern "C" | 377 extern "C" |
| 559 bool ChromeOSGetAutoRepeatEnabled(bool* enabled) { | 378 bool ChromeOSGetAutoRepeatEnabled(bool* enabled) { |
| 560 return XKeyboard::Get()->GetAutoRepeatEnabled(enabled); | 379 return XKeyboard::Get()->GetAutoRepeatEnabled(enabled); |
| 561 } | 380 } |
| 562 | 381 |
| 382 // TODO(yusukes): We can remove this function since the default setting of the |
| 383 // repeat mode is true, and we don't change the default. |
| 563 extern "C" | 384 extern "C" |
| 564 bool ChromeOSSetAutoRepeatEnabled(bool enabled) { | 385 bool ChromeOSSetAutoRepeatEnabled(bool enabled) { |
| 565 return XKeyboard::Get()->SetAutoRepeatEnabled(enabled); | 386 return XKeyboard::Get()->SetAutoRepeatEnabled(enabled); |
| 566 } | 387 } |
| 567 | 388 |
| 389 // TODO(yusukes): Remove this function. |
| 568 extern "C" | 390 extern "C" |
| 569 bool ChromeOSGetAutoRepeatRate(chromeos::AutoRepeatRate* out_rate) { | 391 bool ChromeOSGetAutoRepeatRate(chromeos::AutoRepeatRate* out_rate) { |
| 570 return XKeyboard::Get()->GetAutoRepeatRate(out_rate); | 392 return XKeyboard::Get()->GetAutoRepeatRate(out_rate); |
| 571 } | 393 } |
| 572 | 394 |
| 573 extern "C" | 395 extern "C" |
| 574 bool ChromeOSSetAutoRepeatRate(const chromeos::AutoRepeatRate& rate) { | 396 bool ChromeOSSetAutoRepeatRate(const chromeos::AutoRepeatRate& rate) { |
| 575 return XKeyboard::Get()->SetAutoRepeatRate(rate); | 397 return XKeyboard::Get()->SetAutoRepeatRate(rate); |
| 576 } | 398 } |
| OLD | NEW |