input_device_settings.cc revision ca12bfac764ba476d6cd062bf1dde12cc64c3f40
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/system/input_device_settings.h"
6
7#include <stdarg.h>
8#include <string>
9#include <vector>
10
11#include "base/bind.h"
12#include "base/chromeos/chromeos_version.h"
13#include "base/command_line.h"
14#include "base/file_util.h"
15#include "base/files/file_path.h"
16#include "base/memory/ref_counted.h"
17#include "base/message_loop/message_loop.h"
18#include "base/process.h"
19#include "base/process_util.h"
20#include "base/strings/stringprintf.h"
21#include "base/task_runner.h"
22#include "base/threading/sequenced_worker_pool.h"
23#include "content/public/browser/browser_thread.h"
24
25namespace chromeos {
26namespace system {
27
28namespace {
29
30const char kTpControl[] = "/opt/google/touchpad/tpcontrol";
31const char kMouseControl[] = "/opt/google/mouse/mousecontrol";
32
33typedef base::RefCountedData<bool> RefCountedBool;
34
35bool ScriptExists(const std::string& script) {
36  DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
37  return base::PathExists(base::FilePath(script));
38}
39
40// Executes the input control script asynchronously, if it exists.
41void ExecuteScriptOnFileThread(const std::vector<std::string>& argv) {
42  DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
43  DCHECK(!argv.empty());
44  const std::string& script(argv[0]);
45
46  // Script must exist on device.
47  DCHECK(!base::chromeos::IsRunningOnChromeOS() || ScriptExists(script));
48
49  if (!ScriptExists(script))
50    return;
51
52  base::ProcessHandle handle;
53  base::LaunchProcess(CommandLine(argv), base::LaunchOptions(), &handle);
54  base::EnsureProcessGetsReaped(handle);
55}
56
57void ExecuteScript(int argc, ...) {
58  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
59  std::vector<std::string> argv;
60  va_list vl;
61  va_start(vl, argc);
62  for (int i = 0; i < argc; ++i) {
63    argv.push_back(va_arg(vl, const char*));
64  }
65  va_end(vl);
66
67  // Control scripts can take long enough to cause SIGART during shutdown
68  // (http://crbug.com/261426). Run the blocking pool task with
69  // CONTINUE_ON_SHUTDOWN so it won't be joined when Chrome shuts down.
70  base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
71  scoped_refptr<base::TaskRunner> runner =
72      pool->GetTaskRunnerWithShutdownBehavior(
73          base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
74  runner->PostTask(FROM_HERE, base::Bind(&ExecuteScriptOnFileThread, argv));
75}
76
77void SetPointerSensitivity(const char* script, int value) {
78  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
79  DCHECK(value >= kMinPointerSensitivity && value <= kMaxPointerSensitivity);
80  ExecuteScript(
81      3, script, "sensitivity", base::StringPrintf("%d", value).c_str());
82}
83
84void SetTPControl(const char* control, bool enabled) {
85  ExecuteScript(3, kTpControl, control, enabled ? "on" : "off");
86}
87
88void DeviceExistsBlockingPool(const char* script,
89                              scoped_refptr<RefCountedBool> exists) {
90  DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
91  exists->data = false;
92  if (!ScriptExists(script))
93    return;
94
95  std::vector<std::string> argv;
96  argv.push_back(script);
97  argv.push_back("status");
98  std::string output;
99  // Output is empty if the device is not found.
100  exists->data = base::GetAppOutput(CommandLine(argv), &output) &&
101      !output.empty();
102  DVLOG(1) << "DeviceExistsBlockingPool:" << script << "=" << exists->data;
103}
104
105void RunCallbackUIThread(scoped_refptr<RefCountedBool> exists,
106                         const DeviceExistsCallback& callback) {
107  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
108  DVLOG(1) << "RunCallbackUIThread " << exists->data;
109  callback.Run(exists->data);
110}
111
112void DeviceExists(const char* script, const DeviceExistsCallback& callback) {
113  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
114
115  // One or both of the control scripts can apparently hang during shutdown
116  // (http://crbug.com/255546). Run the blocking pool task with
117  // CONTINUE_ON_SHUTDOWN so it won't be joined when Chrome shuts down.
118  scoped_refptr<RefCountedBool> exists(new RefCountedBool(false));
119  base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
120  scoped_refptr<base::TaskRunner> runner =
121      pool->GetTaskRunnerWithShutdownBehavior(
122          base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
123  runner->PostTaskAndReply(FROM_HERE,
124      base::Bind(&DeviceExistsBlockingPool, script, exists),
125      base::Bind(&RunCallbackUIThread, exists, callback));
126}
127
128}  // namespace
129
130namespace touchpad_settings {
131
132void TouchpadExists(const DeviceExistsCallback& callback) {
133  DeviceExists(kTpControl, callback);
134}
135
136// Sets the touchpad sensitivity in the range [1, 5].
137void SetSensitivity(int value) {
138  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
139  SetPointerSensitivity(kTpControl, value);
140}
141
142void SetTapToClick(bool enabled) {
143  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
144  SetTPControl("taptoclick", enabled);
145}
146
147void SetThreeFingerClick(bool enabled) {
148  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
149
150  SetTPControl("three_finger_click", enabled);
151  // For Alex/ZGB.
152  SetTPControl("t5r2_three_finger_click", enabled);
153}
154
155void SetTapDragging(bool enabled) {
156  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
157  SetTPControl("tap_dragging", enabled);
158}
159
160}  // namespace touchpad_settings
161
162namespace mouse_settings {
163
164void MouseExists(const DeviceExistsCallback& callback) {
165  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
166  DeviceExists(kMouseControl, callback);
167}
168
169// Sets the touchpad sensitivity in the range [1, 5].
170void SetSensitivity(int value) {
171  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
172  SetPointerSensitivity(kMouseControl, value);
173}
174
175void SetPrimaryButtonRight(bool right) {
176  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
177  ExecuteScript(3, kMouseControl, "swap_left_right", right ? "1" : "0");
178}
179
180}  // namespace mouse_settings
181
182}  // namespace system
183}  // namespace chromeos
184