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.
5 #include "chrome/browser/chromeos/system/input_device_settings.h"
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/process/kill.h"
17 #include "base/process/launch.h"
18 #include "base/process/process_handle.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/stringprintf.h"
21 #include "base/sys_info.h"
22 #include "base/task_runner.h"
23 #include "base/threading/sequenced_worker_pool.h"
24 #include "content/public/browser/browser_thread.h"
31 InputDeviceSettings
* g_instance
= nullptr;
32 InputDeviceSettings
* g_test_instance
= nullptr;
34 const char kDeviceTypeTouchpad
[] = "touchpad";
35 const char kDeviceTypeMouse
[] = "mouse";
36 const char kInputControl
[] = "/opt/google/input/inputcontrol";
38 typedef base::RefCountedData
<bool> RefCountedBool
;
40 bool ScriptExists(const std::string
& script
) {
41 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
42 return base::PathExists(base::FilePath(script
));
45 // Executes the input control script asynchronously, if it exists.
46 void ExecuteScriptOnFileThread(const std::vector
<std::string
>& argv
) {
47 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
48 DCHECK(!argv
.empty());
49 const std::string
& script(argv
[0]);
51 // Script must exist on device and is of correct format.
52 DCHECK(script
.compare(kInputControl
) == 0);
53 DCHECK(!base::SysInfo::IsRunningOnChromeOS() || ScriptExists(script
));
55 if (!ScriptExists(script
))
58 base::Process process
=
59 base::LaunchProcess(base::CommandLine(argv
), base::LaunchOptions());
60 if (process
.IsValid())
61 base::EnsureProcessGetsReaped(process
.pid());
64 void ExecuteScript(const std::vector
<std::string
>& argv
) {
65 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
70 VLOG(1) << "About to launch: \""
71 << base::CommandLine(argv
).GetCommandLineString() << "\"";
73 // Control scripts can take long enough to cause SIGART during shutdown
74 // (http://crbug.com/261426). Run the blocking pool task with
75 // CONTINUE_ON_SHUTDOWN so it won't be joined when Chrome shuts down.
76 base::SequencedWorkerPool
* pool
= content::BrowserThread::GetBlockingPool();
77 scoped_refptr
<base::TaskRunner
> runner
=
78 pool
->GetTaskRunnerWithShutdownBehavior(
79 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN
);
80 runner
->PostTask(FROM_HERE
, base::Bind(&ExecuteScriptOnFileThread
, argv
));
83 void AddSensitivityArguments(const char* device_type
,
85 std::vector
<std::string
>* argv
) {
86 DCHECK(value
>= kMinPointerSensitivity
&& value
<= kMaxPointerSensitivity
);
88 base::StringPrintf("--%s_sensitivity=%d", device_type
, value
));
91 void AddTPControlArguments(const char* control
,
93 std::vector
<std::string
>* argv
) {
94 argv
->push_back(base::StringPrintf("--%s=%d", control
, enabled
? 1 : 0));
97 void DeviceExistsBlockingPool(const char* device_type
,
98 scoped_refptr
<RefCountedBool
> exists
) {
99 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
100 exists
->data
= false;
101 if (!ScriptExists(kInputControl
))
104 std::vector
<std::string
> argv
;
105 argv
.push_back(kInputControl
);
106 argv
.push_back(base::StringPrintf("--type=%s", device_type
));
107 argv
.push_back("--list");
109 // Output is empty if the device is not found.
111 base::GetAppOutput(base::CommandLine(argv
), &output
) && !output
.empty();
112 DVLOG(1) << "DeviceExistsBlockingPool:" << device_type
<< "=" << exists
->data
;
115 void RunCallbackUIThread(
116 scoped_refptr
<RefCountedBool
> exists
,
117 const InputDeviceSettings::DeviceExistsCallback
& callback
) {
118 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
119 DVLOG(1) << "RunCallbackUIThread " << exists
->data
;
120 callback
.Run(exists
->data
);
123 void DeviceExists(const char* script
,
124 const InputDeviceSettings::DeviceExistsCallback
& callback
) {
125 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
127 // One or both of the control scripts can apparently hang during shutdown
128 // (http://crbug.com/255546). Run the blocking pool task with
129 // CONTINUE_ON_SHUTDOWN so it won't be joined when Chrome shuts down.
130 scoped_refptr
<RefCountedBool
> exists(new RefCountedBool(false));
131 base::SequencedWorkerPool
* pool
= content::BrowserThread::GetBlockingPool();
132 scoped_refptr
<base::TaskRunner
> runner
=
133 pool
->GetTaskRunnerWithShutdownBehavior(
134 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN
);
135 runner
->PostTaskAndReply(
136 FROM_HERE
, base::Bind(&DeviceExistsBlockingPool
, script
, exists
),
137 base::Bind(&RunCallbackUIThread
, exists
, callback
));
140 // InputDeviceSettings for Linux with X11.
141 class InputDeviceSettingsImplX11
: public InputDeviceSettings
{
143 InputDeviceSettingsImplX11();
146 ~InputDeviceSettingsImplX11() override
{}
149 // Overridden from InputDeviceSettings.
150 void TouchpadExists(const DeviceExistsCallback
& callback
) override
;
151 void UpdateTouchpadSettings(const TouchpadSettings
& settings
) override
;
152 void SetTouchpadSensitivity(int value
) override
;
153 void SetTapToClick(bool enabled
) override
;
154 void SetThreeFingerClick(bool enabled
) override
;
155 void SetTapDragging(bool enabled
) override
;
156 void SetNaturalScroll(bool enabled
) override
;
157 void MouseExists(const DeviceExistsCallback
& callback
) override
;
158 void UpdateMouseSettings(const MouseSettings
& settings
) override
;
159 void SetMouseSensitivity(int value
) override
;
160 void SetPrimaryButtonRight(bool right
) override
;
161 void ReapplyTouchpadSettings() override
;
162 void ReapplyMouseSettings() override
;
164 // Generate arguments for the inputcontrol script.
166 // |argv| is filled with arguments of script, that should be launched in order
168 void GenerateTouchpadArguments(std::vector
<std::string
>* argv
);
169 void GenerateMouseArguments(std::vector
<std::string
>* argv
);
171 TouchpadSettings current_touchpad_settings_
;
172 MouseSettings current_mouse_settings_
;
174 DISALLOW_COPY_AND_ASSIGN(InputDeviceSettingsImplX11
);
177 InputDeviceSettingsImplX11::InputDeviceSettingsImplX11() {
180 void InputDeviceSettingsImplX11::TouchpadExists(
181 const DeviceExistsCallback
& callback
) {
182 DeviceExists(kDeviceTypeTouchpad
, callback
);
185 void InputDeviceSettingsImplX11::UpdateTouchpadSettings(
186 const TouchpadSettings
& settings
) {
187 std::vector
<std::string
> argv
;
188 if (current_touchpad_settings_
.Update(settings
)) {
189 GenerateTouchpadArguments(&argv
);
194 void InputDeviceSettingsImplX11::SetTouchpadSensitivity(int value
) {
195 TouchpadSettings settings
;
196 settings
.SetSensitivity(value
);
197 UpdateTouchpadSettings(settings
);
200 void InputDeviceSettingsImplX11::SetNaturalScroll(bool enabled
) {
201 TouchpadSettings settings
;
202 settings
.SetNaturalScroll(enabled
);
203 UpdateTouchpadSettings(settings
);
206 void InputDeviceSettingsImplX11::SetTapToClick(bool enabled
) {
207 TouchpadSettings settings
;
208 settings
.SetTapToClick(enabled
);
209 UpdateTouchpadSettings(settings
);
212 void InputDeviceSettingsImplX11::SetThreeFingerClick(bool enabled
) {
214 TouchpadSettings settings
;
215 settings
.SetThreeFingerClick(enabled
);
216 UpdateTouchpadSettings(settings
);
219 void InputDeviceSettingsImplX11::SetTapDragging(bool enabled
) {
220 TouchpadSettings settings
;
221 settings
.SetTapDragging(enabled
);
222 UpdateTouchpadSettings(settings
);
225 void InputDeviceSettingsImplX11::MouseExists(
226 const DeviceExistsCallback
& callback
) {
227 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
228 DeviceExists(kDeviceTypeMouse
, callback
);
231 void InputDeviceSettingsImplX11::UpdateMouseSettings(
232 const MouseSettings
& update
) {
233 std::vector
<std::string
> argv
;
234 if (current_mouse_settings_
.Update(update
)) {
235 GenerateMouseArguments(&argv
);
240 void InputDeviceSettingsImplX11::SetMouseSensitivity(int value
) {
241 MouseSettings settings
;
242 settings
.SetSensitivity(value
);
243 UpdateMouseSettings(settings
);
246 void InputDeviceSettingsImplX11::SetPrimaryButtonRight(bool right
) {
247 MouseSettings settings
;
248 settings
.SetPrimaryButtonRight(right
);
249 UpdateMouseSettings(settings
);
252 void InputDeviceSettingsImplX11::ReapplyTouchpadSettings() {
253 TouchpadSettings settings
= current_touchpad_settings_
;
254 current_touchpad_settings_
= TouchpadSettings();
255 UpdateTouchpadSettings(settings
);
258 void InputDeviceSettingsImplX11::ReapplyMouseSettings() {
259 MouseSettings settings
= current_mouse_settings_
;
260 current_mouse_settings_
= MouseSettings();
261 UpdateMouseSettings(settings
);
264 void InputDeviceSettingsImplX11::GenerateTouchpadArguments(
265 std::vector
<std::string
>* argv
) {
266 argv
->push_back(kInputControl
);
267 if (current_touchpad_settings_
.IsSensitivitySet()) {
268 AddSensitivityArguments(kDeviceTypeTouchpad
,
269 current_touchpad_settings_
.GetSensitivity(), argv
);
271 if (current_touchpad_settings_
.IsTapToClickSet()) {
272 AddTPControlArguments("tapclick",
273 current_touchpad_settings_
.GetTapToClick(), argv
);
275 if (current_touchpad_settings_
.IsThreeFingerClickSet()) {
276 AddTPControlArguments("t5r2_three_finger_click",
277 current_touchpad_settings_
.GetThreeFingerClick(),
280 if (current_touchpad_settings_
.IsTapDraggingSet()) {
281 AddTPControlArguments("tapdrag",
282 current_touchpad_settings_
.GetTapDragging(), argv
);
284 if (current_touchpad_settings_
.IsNaturalScrollSet()) {
285 AddTPControlArguments("australian_scrolling",
286 current_touchpad_settings_
.GetNaturalScroll(), argv
);
290 void InputDeviceSettingsImplX11::GenerateMouseArguments(
291 std::vector
<std::string
>* argv
) {
292 argv
->push_back(kInputControl
);
293 if (current_mouse_settings_
.IsSensitivitySet()) {
294 AddSensitivityArguments(kDeviceTypeMouse
,
295 current_mouse_settings_
.GetSensitivity(), argv
);
297 if (current_mouse_settings_
.IsPrimaryButtonRightSet()) {
298 AddTPControlArguments(
299 "mouse_swap_lr", current_mouse_settings_
.GetPrimaryButtonRight(), argv
);
306 InputDeviceSettings
* InputDeviceSettings::Get() {
308 return g_test_instance
;
310 g_instance
= new InputDeviceSettingsImplX11
;
315 void InputDeviceSettings::SetSettingsForTesting(
316 InputDeviceSettings
* test_settings
) {
317 if (g_test_instance
== test_settings
)
319 delete g_test_instance
;
320 g_test_instance
= test_settings
;
323 } // namespace system
324 } // namespace chromeos