blob: fd4d51e07661244a941a642fa1d0c15bdbe87af2 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/xinput_hierarchy_changed_event_listener.h"
#include <X11/Xlib.h>
#include <X11/extensions/XInput2.h>
#include "chromeos/ime/input_method_manager.h"
#include "chromeos/ime/xkeyboard.h"
#include "ui/base/x/x11_util.h"
namespace chromeos {
namespace {
// Gets the major opcode for XInput2. Returns -1 on error.
int GetXInputOpCode() {
static const char kExtensionName[] = "XInputExtension";
int xi_opcode = -1;
int event;
int error;
if (!XQueryExtension(
gfx::GetXDisplay(), kExtensionName, &xi_opcode, &event, &error)) {
VLOG(1) << "X Input extension not available: error=" << error;
return -1;
}
return xi_opcode;
}
// Checks the |event| and asynchronously sets the XKB layout when necessary.
void HandleHierarchyChangedEvent(
XIHierarchyEvent* event,
ObserverList<DeviceHierarchyObserver>* observer_list) {
if (!(event->flags & (XISlaveAdded | XISlaveRemoved)))
return;
bool update_keyboard_status = false;
for (int i = 0; i < event->num_info; ++i) {
XIHierarchyInfo* info = &event->info[i];
if ((info->flags & XISlaveAdded) && (info->use == XIFloatingSlave)) {
FOR_EACH_OBSERVER(DeviceHierarchyObserver,
*observer_list,
DeviceAdded(info->deviceid));
update_keyboard_status = true;
} else if (info->flags & XISlaveRemoved) {
// Can't check info->use here; it appears to always be 0.
FOR_EACH_OBSERVER(DeviceHierarchyObserver,
*observer_list,
DeviceRemoved(info->deviceid));
}
}
if (update_keyboard_status) {
chromeos::input_method::InputMethodManager* input_method_manager =
chromeos::input_method::InputMethodManager::Get();
chromeos::input_method::XKeyboard* xkeyboard =
input_method_manager->GetXKeyboard();
xkeyboard->ReapplyCurrentModifierLockStatus();
xkeyboard->ReapplyCurrentKeyboardLayout();
}
}
} // namespace
// static
XInputHierarchyChangedEventListener*
XInputHierarchyChangedEventListener::GetInstance() {
return Singleton<XInputHierarchyChangedEventListener>::get();
}
XInputHierarchyChangedEventListener::XInputHierarchyChangedEventListener()
: stopped_(false),
xiopcode_(GetXInputOpCode()) {
Init();
}
XInputHierarchyChangedEventListener::~XInputHierarchyChangedEventListener() {
Stop();
}
void XInputHierarchyChangedEventListener::Stop() {
if (stopped_)
return;
StopImpl();
stopped_ = true;
xiopcode_ = -1;
}
void XInputHierarchyChangedEventListener::AddObserver(
DeviceHierarchyObserver* observer) {
observer_list_.AddObserver(observer);
}
void XInputHierarchyChangedEventListener::RemoveObserver(
DeviceHierarchyObserver* observer) {
observer_list_.RemoveObserver(observer);
}
bool XInputHierarchyChangedEventListener::ProcessedXEvent(XEvent* xevent) {
if ((xevent->xcookie.type != GenericEvent) ||
(xevent->xcookie.extension != xiopcode_)) {
return false;
}
XGenericEventCookie* cookie = &(xevent->xcookie);
bool handled = false;
if (cookie->evtype == XI_HierarchyChanged) {
XIHierarchyEvent* event = static_cast<XIHierarchyEvent*>(cookie->data);
HandleHierarchyChangedEvent(event, &observer_list_);
if (event->flags & XIDeviceEnabled || event->flags & XIDeviceDisabled)
NotifyDeviceHierarchyChanged();
handled = true;
} else if (cookie->evtype == XI_KeyPress || cookie->evtype == XI_KeyRelease) {
XIDeviceEvent* xiev = reinterpret_cast<XIDeviceEvent*>(cookie->data);
if (xiev->deviceid == xiev->sourceid) {
FOR_EACH_OBSERVER(DeviceHierarchyObserver,
observer_list_,
DeviceKeyPressedOrReleased(xiev->deviceid));
handled = true;
}
}
return handled;
}
void XInputHierarchyChangedEventListener::NotifyDeviceHierarchyChanged() {
FOR_EACH_OBSERVER(DeviceHierarchyObserver,
observer_list_,
DeviceHierarchyChanged());
}
} // namespace chromeos