1// Copyright 2014 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 "ash/wm/maximize_mode/scoped_disable_internal_mouse_and_keyboard_x11.h"
6
7#include <set>
8#include <X11/extensions/XInput2.h>
9#include <X11/Xlib.h>
10
11#include "ash/display/display_controller.h"
12#include "ash/screen_util.h"
13#include "ash/shell.h"
14#include "base/memory/scoped_ptr.h"
15#include "base/strings/string_util.h"
16#include "ui/aura/client/screen_position_client.h"
17#include "ui/aura/env.h"
18#include "ui/aura/window.h"
19#include "ui/aura/window_event_dispatcher.h"
20#include "ui/aura/window_tree_host.h"
21#include "ui/events/event.h"
22#include "ui/events/event_utils.h"
23#include "ui/events/keycodes/keyboard_codes_posix.h"
24#include "ui/events/platform/platform_event_source.h"
25#include "ui/events/x/device_data_manager_x11.h"
26#include "ui/events/x/device_list_cache_x.h"
27#include "ui/gfx/x/x11_types.h"
28
29namespace ash {
30
31namespace {
32
33// The name of the xinput device corresponding to the internal touchpad.
34const char kInternalTouchpadName[] = "Elan Touchpad";
35
36// The name of the xinput device corresponding to the internal keyboard.
37const char kInternalKeyboardName[] = "AT Translated Set 2 keyboard";
38
39// Repeated key events have their source set to the core keyboard device.
40// These must be disabled also until http://crbug.com/402898 is resolved.
41const char kCoreKeyboardName[] = "Virtual core keyboard";
42
43// Device id used to indicate that a device has not been detected.
44const int kDeviceIdNone = -1;
45
46gfx::Point GetMouseLocationInScreen() {
47  return aura::Env::GetInstance()->last_mouse_location();
48}
49
50void SetMouseLocationInScreen(const gfx::Point& screen_location) {
51  gfx::Display display = ash::ScreenUtil::FindDisplayContainingPoint(
52      screen_location);
53  if (!display.is_valid())
54    return;
55  aura::Window* root_window = Shell::GetInstance()->display_controller()->
56      GetRootWindowForDisplayId(display.id());
57  gfx::Point host_location(screen_location);
58  aura::client::ScreenPositionClient* client =
59      aura::client::GetScreenPositionClient(root_window);
60  if (client)
61    client->ConvertPointFromScreen(root_window, &host_location);
62  root_window->GetHost()->MoveCursorTo(host_location);
63}
64
65}  // namespace
66
67ScopedDisableInternalMouseAndKeyboardX11::
68    ScopedDisableInternalMouseAndKeyboardX11()
69    : touchpad_device_id_(kDeviceIdNone),
70      keyboard_device_id_(kDeviceIdNone),
71      core_keyboard_device_id_(kDeviceIdNone),
72      last_mouse_location_(GetMouseLocationInScreen()) {
73
74  ui::DeviceDataManagerX11* device_data_manager =
75      static_cast<ui::DeviceDataManagerX11*>(
76          ui::DeviceDataManager::GetInstance());
77  if (device_data_manager->IsXInput2Available()) {
78    XIDeviceList xi_dev_list = ui::DeviceListCacheX::GetInstance()->
79        GetXI2DeviceList(gfx::GetXDisplay());
80    for (int i = 0; i < xi_dev_list.count; ++i) {
81      std::string device_name(xi_dev_list[i].name);
82      base::TrimWhitespaceASCII(device_name, base::TRIM_TRAILING, &device_name);
83      if (device_name == kInternalTouchpadName) {
84        touchpad_device_id_ = xi_dev_list[i].deviceid;
85        device_data_manager->DisableDevice(touchpad_device_id_);
86      } else if (device_name == kInternalKeyboardName) {
87        keyboard_device_id_ = xi_dev_list[i].deviceid;
88        device_data_manager->DisableDevice(keyboard_device_id_);
89      } else if (device_name == kCoreKeyboardName) {
90        core_keyboard_device_id_ = xi_dev_list[i].deviceid;
91        device_data_manager->DisableDevice(core_keyboard_device_id_);
92      }
93    }
94  }
95  // Allow the accessible keys present on the side of some devices to continue
96  // working.
97  scoped_ptr<std::set<ui::KeyboardCode> > excepted_keys(
98      new std::set<ui::KeyboardCode>);
99  excepted_keys->insert(ui::VKEY_VOLUME_DOWN);
100  excepted_keys->insert(ui::VKEY_VOLUME_UP);
101  excepted_keys->insert(ui::VKEY_POWER);
102  device_data_manager->SetDisabledKeyboardAllowedKeys(excepted_keys.Pass());
103  ui::PlatformEventSource::GetInstance()->AddPlatformEventObserver(this);
104}
105
106ScopedDisableInternalMouseAndKeyboardX11::
107    ~ScopedDisableInternalMouseAndKeyboardX11() {
108  ui::DeviceDataManagerX11* device_data_manager =
109      static_cast<ui::DeviceDataManagerX11*>(
110          ui::DeviceDataManager::GetInstance());
111  if (touchpad_device_id_ != kDeviceIdNone)
112    device_data_manager->EnableDevice(touchpad_device_id_);
113  if (keyboard_device_id_ != kDeviceIdNone)
114    device_data_manager->EnableDevice(keyboard_device_id_);
115  if (core_keyboard_device_id_ != kDeviceIdNone)
116    device_data_manager->EnableDevice(core_keyboard_device_id_);
117  device_data_manager->SetDisabledKeyboardAllowedKeys(
118      scoped_ptr<std::set<ui::KeyboardCode> >());
119  ui::PlatformEventSource::GetInstance()->RemovePlatformEventObserver(this);
120}
121
122void ScopedDisableInternalMouseAndKeyboardX11::WillProcessEvent(
123    const ui::PlatformEvent& event) {
124}
125
126void ScopedDisableInternalMouseAndKeyboardX11::DidProcessEvent(
127    const ui::PlatformEvent& event) {
128  if (event->type != GenericEvent)
129    return;
130  XIDeviceEvent* xievent =
131      static_cast<XIDeviceEvent*>(event->xcookie.data);
132  ui::DeviceDataManagerX11* device_data_manager =
133      static_cast<ui::DeviceDataManagerX11*>(
134          ui::DeviceDataManager::GetInstance());
135  if (xievent->evtype != XI_Motion ||
136      device_data_manager->IsFlingEvent(event) ||
137      device_data_manager->IsScrollEvent(event) ||
138      device_data_manager->IsCMTMetricsEvent(event)) {
139    return;
140  }
141  if (xievent->sourceid == touchpad_device_id_) {
142    // The cursor will have already moved even though the move event will be
143    // blocked. Move the mouse cursor back to its last known location resulting
144    // from an external mouse to prevent the internal touchpad from moving it.
145    SetMouseLocationInScreen(last_mouse_location_);
146  } else {
147    // Track the last location seen from an external mouse event.
148    last_mouse_location_ = GetMouseLocationInScreen();
149  }
150}
151
152}  // namespace ash
153