Chromium Code Reviews| Index: chromeos_keyboard.cc |
| diff --git a/chromeos_keyboard.cc b/chromeos_keyboard.cc |
| index 2b8014fa85420f50c7e82be97e16f2568b1f7985..a9be1319f001be4c92563b5c076fc7dec02fe581 100644 |
| --- a/chromeos_keyboard.cc |
| +++ b/chromeos_keyboard.cc |
| @@ -19,8 +19,10 @@ |
| namespace { |
| -// The command we use to set/get the current XKB layout and modifier key |
| -// mapping. |
| +// The default keyboard layout name in the xorg config file. |
| +const char kDefaultLayoutName[] = "us"; |
| +// The command we use to set the current XKB layout and modifier key mapping. |
| +// 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.
|
| const char kSetxkbmapCommand[] = "/usr/bin/setxkbmap"; |
| // See the comment at ModifierKey in the .h file. |
| chromeos::ModifierKey kCustomizableKeys[] = { |
| @@ -67,62 +69,40 @@ class XKeyboard { |
| // Sets the current keyboard layout to |layout_name|. This function does not |
| // change the current mapping of the modifier keys. Returns true on success. |
| bool SetLayout(const std::string& layout_name) { |
| - // TODO(yusukes): write auto tests for the function. |
| - chromeos::ModifierMap modifier_map; |
| - if (!GetModifierMapping(&modifier_map)) { |
| - LOG(ERROR) << "Failed to get modifier mapping."; |
| - return false; |
| - } |
| - if (SetLayoutInternal(layout_name, modifier_map, true)) { |
| + if (SetLayoutInternal(layout_name, current_modifier_map_, true)) { |
| + current_layout_name_ = layout_name; |
| return true; |
| } |
| + // TODO(satorux,yusukes): Remove +version hack. |
| LOG(ERROR) << "SetLayoutInternal failed. Retrying without +version option"; |
| - return SetLayoutInternal(layout_name, modifier_map, false); |
| + if (SetLayoutInternal(layout_name, current_modifier_map_, false)) { |
| + current_layout_name_ = layout_name; |
| + return true; |
| + } |
| + return false; |
| } |
| // Remaps modifier keys. This function does not change the current keyboard |
| // layout. Returns true on success. |
| bool RemapModifierKeys(const chromeos::ModifierMap& modifier_map) { |
| // TODO(yusukes): write auto tests for the function. |
| - modifier_keys_are_remapped_ = false; |
| - const std::string layout_name = GetLayout(); |
| - if (layout_name.empty()) { |
| - return false; |
| - } |
| - if (SetLayoutInternal(layout_name, modifier_map, true)) { |
| - modifier_keys_are_remapped_ = true; |
| + if (SetLayoutInternal(current_layout_name_, modifier_map, true)) { |
| + current_modifier_map_ = modifier_map; |
| return true; |
| } |
| + // TODO(satorux,yusukes): Remove +version hack. |
| LOG(ERROR) << "SetLayoutInternal failed. Retrying without +version option"; |
| - if (SetLayoutInternal(layout_name, modifier_map, false)) { |
| - modifier_keys_are_remapped_ = true; |
| + if (SetLayoutInternal(current_layout_name_, modifier_map, false)) { |
| + current_modifier_map_ = modifier_map; |
| return true; |
| } |
| return false; |
| } |
| - // Returns the current layout name like "us". On error, returns "". |
| - std::string GetLayout() { |
| - // TODO(yusukes): write auto tests for the function. |
| - std::string command_output = last_full_layout_name_; |
| - |
| - if (command_output.empty()) { |
| - // Cache is not available. Execute setxkbmap to get the current layout. |
| - if (!ExecuteGetLayoutCommand(&command_output)) { |
| - return ""; |
| - } |
| - } |
| - |
| - const std::string layout_name = |
| - chromeos::ExtractLayoutNameFromFullXkbLayoutName(command_output); |
| - LOG(INFO) << "Current XKB layout name: " << layout_name; |
| - return layout_name; |
| - } |
| - |
| // Gets the current auto-repeat mode of the keyboard. The result is stored in |
| // |mode|. Returns true on success. |
| + // TODO(yusukes): Remove this function. |
| bool GetAutoRepeatEnabled(bool* enabled) { |
| - // TODO(yusukes): write auto tests for the function. |
| DCHECK(enabled); |
| ScopedDisplay display(XOpenDisplay(NULL)); |
| if (!display.get()) { |
| @@ -139,8 +119,8 @@ class XKeyboard { |
| } |
| // Turns on and off the auto-repeat of the keyboard. Returns true on success. |
| + // TODO(yusukes): Remove this function. |
| bool SetAutoRepeatEnabled(bool enabled) { |
| - // TODO(yusukes): write auto tests for the function. |
| ScopedDisplay display(XOpenDisplay(NULL)); |
| if (!display.get()) { |
| return false; |
| @@ -156,8 +136,8 @@ class XKeyboard { |
| // Gets the current auto-repeat rate of the keyboard. The result is stored in |
| // |out_rate|. Returns true on success. |
| + // TODO(yusukes): Remove this function. |
| bool GetAutoRepeatRate(chromeos::AutoRepeatRate* out_rate) { |
| - // TODO(yusukes): write auto tests for the function. |
| ScopedDisplay display(XOpenDisplay(NULL)); |
| if (!display.get()) { |
| return false; |
| @@ -174,6 +154,7 @@ class XKeyboard { |
| // Sets the auto-repeat rate of the keyboard, initial delay in ms, and repeat |
| // interval in ms. Returns true on success. |
| + // TODO(yusukes): Call this function in non-UI thread or in an idle callback. |
| bool SetAutoRepeatRate(const chromeos::AutoRepeatRate& rate) { |
| // TODO(yusukes): write auto tests for the function. |
| ScopedDisplay display(XOpenDisplay(NULL)); |
| @@ -196,40 +177,15 @@ class XKeyboard { |
| private: |
| friend struct DefaultSingletonTraits<XKeyboard>; |
| - XKeyboard() : modifier_keys_are_remapped_(false) { |
| - InitializeStringToModifierMap(&string_to_modifier_map_); |
| + XKeyboard() : current_layout_name_(kDefaultLayoutName) { |
| + for (size_t i = 0; i < arraysize(kCustomizableKeys); ++i) { |
| + chromeos::ModifierKey key = kCustomizableKeys[i]; |
| + current_modifier_map_.push_back(chromeos::ModifierKeyPair(key, key)); |
| + } |
| } |
| ~XKeyboard() { |
| } |
| - // Gets the current modifier mapping and stores them on |out_modifier_map|. |
| - bool GetModifierMapping(chromeos::ModifierMap* out_modifier_map) { |
| - out_modifier_map->clear(); |
| - |
| - // If modifier keys are not remapped, return a map that doesn't change |
| - // any key mappings. |
| - if (!modifier_keys_are_remapped_) { |
| - for (size_t i = 0; i < arraysize(kCustomizableKeys); ++i) { |
| - chromeos::ModifierKey key = kCustomizableKeys[i]; |
| - out_modifier_map->push_back(chromeos::ModifierKeyPair(key, key)); |
| - } |
| - return true; |
| - } |
| - |
| - std::string command_output = last_full_layout_name_; |
| - if (command_output.empty()) { |
| - // Cache is not available. Execute setxkbmap to get the current mapping. |
| - if (!ExecuteGetLayoutCommand(&command_output)) { |
| - return false; |
| - } |
| - } |
| - if (!chromeos::ExtractModifierMapFromFullXkbLayoutName( |
| - command_output, string_to_modifier_map_, out_modifier_map)) { |
| - return false; |
| - } |
| - return true; |
| - } |
| - |
| // This function is used by SetLayout() and RemapModifierKeys(). Calls |
| // setxkbmap command if needed, and updates the last_full_layout_name_ cache. |
| // If |use_version| is false, the function does not add "+version(...)" to the |
| @@ -243,10 +199,9 @@ class XKeyboard { |
| return false; |
| } |
| - // Since executing setxkbmap takes more than 200 ms on EeePC, and this |
| - // function is called on every focus-in event, try to reduce the number of |
| - // the fork/exec calls. |
| - if (last_full_layout_name_ == layouts_to_set) { |
| + const std::string current_layout = chromeos::CreateFullXkbLayoutName( |
| + current_layout_name_, current_modifier_map_, use_version); |
| + if (current_layout == layouts_to_set) { |
| DLOG(INFO) << "The requested layout is already set: " << layouts_to_set; |
| return true; |
| } |
| @@ -257,106 +212,45 @@ class XKeyboard { |
| chromeos::SetCapsLockEnabled(false); |
| } |
| - gint exit_status = -1; |
| - const gboolean successful = |
| - ExecuteSetLayoutCommand(layouts_to_set, &exit_status); |
| - |
| - // On success, update the cache and return true. |
| - if (successful && (exit_status == 0)) { |
| - last_full_layout_name_ = layouts_to_set; |
| - DLOG(INFO) << "XKB layout is changed to " << layouts_to_set; |
| - return true; |
| - } |
| - |
| - LOG(ERROR) << "Failed to change XKB layout to: " << layouts_to_set; |
| - last_full_layout_name_.clear(); // invalidate the cache. |
| - return false; |
| + ExecuteSetLayoutCommand(layouts_to_set); |
| + return true; |
| } |
| - // Executes 'setxkbmap -layout ...' command. Returns true if execve suceeds. |
| - bool ExecuteSetLayoutCommand(const std::string& layouts_to_set, |
| - gint* out_exit_status) { |
| - *out_exit_status = -1; |
| - |
| + // Executes 'setxkbmap -layout ...' command asynchronously. |
| + // TODO(yusukes): Use libxkbfile.so instead of the command. |
| + void ExecuteSetLayoutCommand(const std::string& layouts_to_set) { |
| gchar* argv[] = { |
| g_strdup(kSetxkbmapCommand), g_strdup("-layout"), |
| g_strdup(layouts_to_set.c_str()), NULL |
| }; |
| - GError* error = NULL; |
| - const gboolean successful = g_spawn_sync(NULL, argv, NULL, |
| - static_cast<GSpawnFlags>(0), |
| - NULL, NULL, NULL, NULL, |
| - out_exit_status, &error); |
| - for (size_t i = 0; argv[i] != NULL; ++i) { |
| - g_free(argv[i]); |
| - } |
| - if (!successful) { |
| - if (error && error->message) { |
| - LOG(ERROR) << "Failed to execute setxkbmap: " << error->message; |
| - } |
| - g_error_free(error); |
| - } |
| - return (successful == TRUE); |
| - } |
| - |
| - // Executes 'setxkbmap -print' command and parses the output (stdout) from the |
| - // command. Returns true if both execve and parsing the output succeed. |
| - // On success, it stores a string like "us+chromeos(..)+version(..)+inet(..)" |
| - // on |out_command_output|. |
| - bool ExecuteGetLayoutCommand(std::string* out_command_output) { |
| - out_command_output->clear(); |
| - |
| - gint exit_status = -1; |
| - gchar* argv[] = { g_strdup(kSetxkbmapCommand), g_strdup("-print"), NULL }; |
| - gchar* raw_command_output = NULL; |
| + const GSpawnFlags flags = static_cast<GSpawnFlags>( |
| + G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL); |
| GError* error = NULL; |
| - const gboolean successful = g_spawn_sync(NULL, argv, NULL, |
| - static_cast<GSpawnFlags>(0), |
| - NULL, NULL, |
| - &raw_command_output, NULL, |
| - &exit_status, &error); |
| + g_spawn_async(NULL, // working_directory |
| + argv, |
| + NULL, // envp |
| + flags, |
| + NULL, // child_setup |
| + NULL, // user_data |
| + NULL, // child_pid |
| + &error); |
| + |
| for (size_t i = 0; argv[i] != NULL; ++i) { |
| g_free(argv[i]); |
| } |
| - |
| - if (!successful) { |
| - if (error && error->message) { |
| + if (error) { |
| + if (error->message) { |
| LOG(ERROR) << "Failed to execute setxkbmap: " << error->message; |
| } |
| g_error_free(error); |
| - return false; |
| } |
| - |
| - // g_spawn_sync succeeded. |
| - std::string command_output = raw_command_output ? raw_command_output : ""; |
| - g_free(raw_command_output); |
| - raw_command_output = NULL; // DO NOT USE |raw_command_output| below. |
| - |
| - if (exit_status != 0) { |
| - return false; |
| - } |
| - // Parse a line like: |
| - // "xkb_symbols { include "pc+us+chromeos(..)+version(..)+inet(pc105)" };" |
| - const size_t cursor = command_output.find("pc+"); |
| - if (cursor == std::string::npos) { |
| - LOG(ERROR) << "pc+ is not found: " << command_output; |
| - return false; |
| - } |
| - *out_command_output = command_output.substr(cursor + 3); // Skip "pc+". |
| - return true; |
| } |
| - // The XKB layout name which we set last time like |
| - // "us+chromeos(search_leftcontrol_leftalt)". |
| - std::string last_full_layout_name_; |
| - |
| - // A std::map that holds mappings like: "leftcontrol_disabled_leftalt" -> |
| - // { LEFT_CONTROL_KEY, VOID_KEY, LEFT_ALT_KEY }. |
| - chromeos::StringToModifierMap string_to_modifier_map_; |
| - |
| - // True if modifier keys are remapped. |
| - bool modifier_keys_are_remapped_; |
| + // The XKB layout name which we set last time like "us" and "us(dvorak)". |
| + std::string current_layout_name_; |
| + // The mapping of modifier keys we set last time. |
| + chromeos::ModifierMap current_modifier_map_; |
| DISALLOW_COPY_AND_ASSIGN(XKeyboard); |
| }; |
| @@ -440,76 +334,7 @@ std::string CreateFullXkbLayoutName(const std::string& layout_name, |
| return full_xkb_layout_name; |
| } |
| -std::string ExtractLayoutNameFromFullXkbLayoutName( |
| - const std::string& full_xkb_layout_name) { |
| - const size_t next_plus_pos = full_xkb_layout_name.find('+'); |
| - if (next_plus_pos == std::string::npos) { |
| - LOG(ERROR) << "Bad layout name: " << full_xkb_layout_name; |
| - return ""; |
| - } |
| - return full_xkb_layout_name.substr(0, next_plus_pos); |
| -} |
| - |
| -void InitializeStringToModifierMap(StringToModifierMap* out_map) { |
| - DCHECK(out_map); |
| - out_map->clear(); |
| - |
| - for (int i = 0; i < static_cast<int>(kNumModifierKeys); ++i) { |
| - for (int j = 0; j < static_cast<int>(kNumModifierKeys); ++j) { |
| - for (int k = 0; k < static_cast<int>(kNumModifierKeys); ++k) { |
| - const std::string string_rep = StringPrintf( |
| - "%s_%s_%s", |
| - ModifierKeyToString(ModifierKey(i)).c_str(), |
| - ModifierKeyToString(ModifierKey(j)).c_str(), |
| - ModifierKeyToString(ModifierKey(k)).c_str()); |
| - ModifierMap modifier_map; |
| - modifier_map.push_back(ModifierKeyPair(kSearchKey, ModifierKey(i))); |
| - modifier_map.push_back( |
| - ModifierKeyPair(kLeftControlKey, ModifierKey(j))); |
| - modifier_map.push_back(ModifierKeyPair(kLeftAltKey, ModifierKey(k))); |
| - out_map->insert(make_pair(string_rep, modifier_map)); |
| - } |
| - } |
| - } |
| -} |
| - |
| -bool ExtractModifierMapFromFullXkbLayoutName( |
| - const std::string& full_xkb_layout_name, |
| - const StringToModifierMap& string_to_modifier_map, |
| - ModifierMap* out_modifier_map) { |
| - static const char kMark[] = "+chromeos("; |
| - const size_t kMarkLen = strlen(kMark); |
| - |
| - out_modifier_map->clear(); |
| - const size_t mark_pos = full_xkb_layout_name.find(kMark); |
| - if (mark_pos == std::string::npos) { |
| - LOG(ERROR) << "Bad layout name: " << full_xkb_layout_name; |
| - return false; |
| - } |
| - |
| - const std::string tmp = // e.g. "leftcontrol_disabled_leftalt), us" |
| - full_xkb_layout_name.substr(mark_pos + kMarkLen); |
| - const size_t next_paren_pos = tmp.find(')'); |
| - if (next_paren_pos == std::string::npos) { |
| - LOG(ERROR) << "Bad layout name: " << full_xkb_layout_name; |
| - return false; |
| - } |
| - |
| - const std::string modifier_map_string = tmp.substr(0, next_paren_pos); |
| - DLOG(INFO) << "Modifier mapping is: " << modifier_map_string; |
| - |
| - StringToModifierMap::const_iterator iter = |
| - string_to_modifier_map.find(modifier_map_string); |
| - if (iter == string_to_modifier_map.end()) { |
| - LOG(ERROR) << "Bad mapping name '" << modifier_map_string |
| - << "' in layout name '" << full_xkb_layout_name << "'"; |
| - return false; |
| - } |
| - |
| - *out_modifier_map = iter->second; |
| - return true; |
| -} |
| - |
| +// This function is only for unittest. |
| bool CapsLockIsEnabled() { |
| ScopedDisplay display(XOpenDisplay(NULL)); |
| if (!display.get()) { |
| @@ -520,6 +345,7 @@ bool CapsLockIsEnabled() { |
| return status.locked_mods & LockMask; |
| } |
| +// TODO(yusukes): Call this function in non-UI thread or in an idle callback. |
| void SetCapsLockEnabled(bool enable_caps_lock) { |
| ScopedDisplay display(XOpenDisplay(NULL)); |
| if (!display.get()) { |
| @@ -555,16 +381,20 @@ bool ChromeOSRemapModifierKeys(const chromeos::ModifierMap& modifier_map) { |
| return XKeyboard::Get()->RemapModifierKeys(modifier_map); |
| } |
| +// TODO(yusukes): Remove this function. |
| extern "C" |
| bool ChromeOSGetAutoRepeatEnabled(bool* enabled) { |
| return XKeyboard::Get()->GetAutoRepeatEnabled(enabled); |
| } |
| +// TODO(yusukes): We can remove this function since the default setting of the |
| +// repeat mode is true, and we don't change the default. |
| extern "C" |
| bool ChromeOSSetAutoRepeatEnabled(bool enabled) { |
| return XKeyboard::Get()->SetAutoRepeatEnabled(enabled); |
| } |
| +// TODO(yusukes): Remove this function. |
| extern "C" |
| bool ChromeOSGetAutoRepeatRate(chromeos::AutoRepeatRate* out_rate) { |
| return XKeyboard::Get()->GetAutoRepeatRate(out_rate); |