#include <set>
#include <utility>
+#include "base/bind.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
+#include "base/process/kill.h"
#include "base/process/launch.h"
#include "base/process/process_handle.h"
#include "base/strings/string_util.h"
// These includes conflict with base/tracked_objects.h so must come last.
#include <X11/XKBlib.h>
#include <X11/Xlib.h>
-#include <glib.h>
namespace chromeos {
namespace input_method {
return base::MessagePumpForUI::GetDefaultXDisplay();
}
+// The delay in milliseconds that we'll wait between checking if
+// setxkbmap command finished.
+const int kSetLayoutCommandCheckDelayMs = 100;
+
// The command we use to set the current XKB layout and modifier key mapping.
// TODO(yusukes): Use libxkbfile.so instead of the command (crosbug.com/13105)
const char kSetxkbmapCommand[] = "/usr/bin/setxkbmap";
virtual unsigned int GetNumLockMask() OVERRIDE;
virtual void GetLockedModifiers(bool* out_caps_lock_enabled,
bool* out_num_lock_enabled) OVERRIDE;
+ virtual bool SetAutoRepeatEnabled(bool enabled) OVERRIDE;
+ virtual bool SetAutoRepeatRate(const AutoRepeatRate& rate) OVERRIDE;
private:
// This function is used by SetLayout() and RemapModifierKeys(). Calls
// TODO(yusukes): Use libxkbfile.so instead of the command (crosbug.com/13105)
void MaybeExecuteSetLayoutCommand();
+ // Polls to see setxkbmap process exits.
+ void PollUntilChildFinish(const base::ProcessHandle handle);
+
// Called when execve'd setxkbmap process exits.
- static void OnSetLayoutFinish(pid_t pid, int status, XKeyboardImpl* self);
+ void OnSetLayoutFinish();
const bool is_running_on_chrome_os_;
unsigned int num_lock_mask_;
base::ThreadChecker thread_checker_;
+ base::WeakPtrFactory<XKeyboardImpl> weak_factory_;
+
DISALLOW_COPY_AND_ASSIGN(XKeyboardImpl);
};
XKeyboardImpl::XKeyboardImpl()
- : is_running_on_chrome_os_(base::SysInfo::IsRunningOnChromeOS()) {
+ : is_running_on_chrome_os_(base::SysInfo::IsRunningOnChromeOS()),
+ weak_factory_(this) {
// X must be already initialized.
CHECK(GetXDisplay());
return;
}
- // g_child_watch_add is necessary to prevent the process from becoming a
- // zombie.
- const base::ProcessId pid = base::GetProcId(handle);
- g_child_watch_add(pid,
- reinterpret_cast<GChildWatchFunc>(OnSetLayoutFinish),
- this);
- DVLOG(1) << "ExecuteSetLayoutCommand: " << layout_to_set << ": pid=" << pid;
+ PollUntilChildFinish(handle);
+
+ DVLOG(1) << "ExecuteSetLayoutCommand: "
+ << layout_to_set << ": pid=" << base::GetProcId(handle);
+}
+
+// Delay and loop until child process finishes and call the callback.
+void XKeyboardImpl::PollUntilChildFinish(const base::ProcessHandle handle) {
+ int exit_code;
+ DVLOG(1) << "PollUntilChildFinish: poll for pid=" << base::GetProcId(handle);
+ switch (base::GetTerminationStatus(handle, &exit_code)) {
+ case base::TERMINATION_STATUS_STILL_RUNNING:
+ DVLOG(1) << "PollUntilChildFinish: Try waiting again";
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&XKeyboardImpl::PollUntilChildFinish,
+ weak_factory_.GetWeakPtr(), handle),
+ base::TimeDelta::FromMilliseconds(kSetLayoutCommandCheckDelayMs));
+ return;
+
+ case base::TERMINATION_STATUS_NORMAL_TERMINATION:
+ DVLOG(1) << "PollUntilChildFinish: Child process finished";
+ OnSetLayoutFinish();
+ return;
+
+ case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
+ DVLOG(1) << "PollUntilChildFinish: Abnormal exit code: " << exit_code;
+ OnSetLayoutFinish();
+ return;
+
+ default:
+ NOTIMPLEMENTED();
+ OnSetLayoutFinish();
+ return;
+ }
}
bool XKeyboardImpl::NumLockIsEnabled() {
*out_num_lock_enabled = status.locked_mods & num_lock_mask_;
}
+bool XKeyboardImpl::SetAutoRepeatEnabled(bool enabled) {
+ if (enabled)
+ XAutoRepeatOn(GetXDisplay());
+ else
+ XAutoRepeatOff(GetXDisplay());
+ DVLOG(1) << "Set auto-repeat mode to: " << (enabled ? "on" : "off");
+ return true;
+}
+
+bool XKeyboardImpl::SetAutoRepeatRate(const AutoRepeatRate& rate) {
+ DVLOG(1) << "Set auto-repeat rate to: "
+ << rate.initial_delay_in_ms << " ms delay, "
+ << rate.repeat_interval_in_ms << " ms interval";
+ if (XkbSetAutoRepeatRate(GetXDisplay(), XkbUseCoreKbd,
+ rate.initial_delay_in_ms,
+ rate.repeat_interval_in_ms) != True) {
+ DVLOG(1) << "Failed to set auto-repeat rate";
+ return false;
+ }
+ return true;
+}
+
void XKeyboardImpl::SetLockedModifiers(ModifierLockStatus new_caps_lock_status,
ModifierLockStatus new_num_lock_status) {
DCHECK(thread_checker_.CalledOnValidThread());
current_num_lock_status_ ? kEnableLock : kDisableLock);
}
-// static
-void XKeyboardImpl::OnSetLayoutFinish(pid_t pid,
- int status,
- XKeyboardImpl* self) {
- DVLOG(1) << "OnSetLayoutFinish: pid=" << pid;
- if (self->execute_queue_.empty()) {
+void XKeyboardImpl::OnSetLayoutFinish() {
+ if (execute_queue_.empty()) {
DVLOG(1) << "OnSetLayoutFinish: execute_queue_ is empty. "
- << "base::LaunchProcess failed? pid=" << pid;
+ << "base::LaunchProcess failed?";
return;
}
- self->execute_queue_.pop();
- self->MaybeExecuteSetLayoutCommand();
+ execute_queue_.pop();
+ MaybeExecuteSetLayoutCommand();
}
} // namespace
// static
-bool XKeyboard::SetAutoRepeatEnabled(bool enabled) {
- if (enabled)
- XAutoRepeatOn(GetXDisplay());
- else
- XAutoRepeatOff(GetXDisplay());
- DVLOG(1) << "Set auto-repeat mode to: " << (enabled ? "on" : "off");
- return true;
-}
-
-// static
-bool XKeyboard::SetAutoRepeatRate(const AutoRepeatRate& rate) {
- DVLOG(1) << "Set auto-repeat rate to: "
- << rate.initial_delay_in_ms << " ms delay, "
- << rate.repeat_interval_in_ms << " ms interval";
- if (XkbSetAutoRepeatRate(GetXDisplay(), XkbUseCoreKbd,
- rate.initial_delay_in_ms,
- rate.repeat_interval_in_ms) != True) {
- DVLOG(1) << "Failed to set auto-repeat rate";
- return false;
- }
- return true;
-}
-
-// static
bool XKeyboard::GetAutoRepeatEnabledForTesting() {
XKeyboardState state = {};
XGetKeyboardControl(GetXDisplay(), &state);