1// Copyright (c) 2012 The Chromium 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#include "chrome/browser/chromeos/events/xinput_hierarchy_changed_event_listener.h"
6
7#include <X11/extensions/XInput2.h>
8#include <X11/Xlib.h>
9
10#include "chromeos/ime/ime_keyboard.h"
11#include "chromeos/ime/input_method_manager.h"
12#include "ui/base/x/x11_util.h"
13#include "ui/events/platform/platform_event_source.h"
14
15namespace chromeos {
16namespace {
17
18// Checks the |event| and asynchronously sets the XKB layout when necessary.
19void HandleHierarchyChangedEvent(
20    XIHierarchyEvent* event,
21    ObserverList<DeviceHierarchyObserver>* observer_list) {
22  if (!(event->flags & (XISlaveAdded | XISlaveRemoved)))
23    return;
24
25  bool update_keyboard_status = false;
26  for (int i = 0; i < event->num_info; ++i) {
27    XIHierarchyInfo* info = &event->info[i];
28    if ((info->flags & XISlaveAdded) && (info->use == XIFloatingSlave)) {
29      FOR_EACH_OBSERVER(DeviceHierarchyObserver,
30                        *observer_list,
31                        DeviceAdded(info->deviceid));
32      update_keyboard_status = true;
33    } else if (info->flags & XISlaveRemoved) {
34      // Can't check info->use here; it appears to always be 0.
35      FOR_EACH_OBSERVER(DeviceHierarchyObserver,
36                        *observer_list,
37                        DeviceRemoved(info->deviceid));
38    }
39  }
40
41  if (update_keyboard_status) {
42    chromeos::input_method::InputMethodManager* input_method_manager =
43        chromeos::input_method::InputMethodManager::Get();
44    chromeos::input_method::ImeKeyboard* keyboard =
45        input_method_manager->GetImeKeyboard();
46    keyboard->ReapplyCurrentModifierLockStatus();
47    keyboard->ReapplyCurrentKeyboardLayout();
48  }
49}
50
51}  // namespace
52
53// static
54XInputHierarchyChangedEventListener*
55XInputHierarchyChangedEventListener::GetInstance() {
56  return Singleton<XInputHierarchyChangedEventListener>::get();
57}
58
59XInputHierarchyChangedEventListener::XInputHierarchyChangedEventListener()
60    : stopped_(false) {
61  ui::PlatformEventSource::GetInstance()->AddPlatformEventObserver(this);
62}
63
64XInputHierarchyChangedEventListener::~XInputHierarchyChangedEventListener() {
65  Stop();
66}
67
68void XInputHierarchyChangedEventListener::Stop() {
69  if (stopped_)
70    return;
71
72  ui::PlatformEventSource::GetInstance()->RemovePlatformEventObserver(this);
73  stopped_ = true;
74}
75
76void XInputHierarchyChangedEventListener::AddObserver(
77    DeviceHierarchyObserver* observer) {
78  observer_list_.AddObserver(observer);
79}
80
81void XInputHierarchyChangedEventListener::RemoveObserver(
82    DeviceHierarchyObserver* observer) {
83  observer_list_.RemoveObserver(observer);
84}
85
86void XInputHierarchyChangedEventListener::WillProcessEvent(
87    const ui::PlatformEvent& event) {
88  ProcessedXEvent(event);
89}
90
91void XInputHierarchyChangedEventListener::DidProcessEvent(
92    const ui::PlatformEvent& event) {
93}
94
95void XInputHierarchyChangedEventListener::ProcessedXEvent(XEvent* xevent) {
96  if (xevent->xcookie.type != GenericEvent)
97    return;
98
99  XGenericEventCookie* cookie = &(xevent->xcookie);
100
101  if (cookie->evtype == XI_HierarchyChanged) {
102    XIHierarchyEvent* event = static_cast<XIHierarchyEvent*>(cookie->data);
103    HandleHierarchyChangedEvent(event, &observer_list_);
104    if (event->flags & XIDeviceEnabled || event->flags & XIDeviceDisabled)
105      NotifyDeviceHierarchyChanged();
106  }
107}
108
109void XInputHierarchyChangedEventListener::NotifyDeviceHierarchyChanged() {
110  FOR_EACH_OBSERVER(DeviceHierarchyObserver,
111                    observer_list_,
112                    DeviceHierarchyChanged());
113}
114
115}  // namespace chromeos
116