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