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.
5 #include "chrome/browser/chromeos/system/input_device_settings.h"
8 #include "base/command_line.h"
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/process/kill.h"
14 #include "base/process/launch.h"
15 #include "base/process/process_handle.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/sys_info.h"
19 #include "base/task_runner.h"
20 #include "base/threading/sequenced_worker_pool.h"
21 #include "chrome/browser/browser_process.h"
22 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
23 #include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h"
24 #include "chrome/common/pref_names.h"
25 #include "chromeos/system/statistics_provider.h"
26 #include "content/public/browser/browser_thread.h"
33 InputDeviceSettings
* g_instance_
;
34 InputDeviceSettings
* g_test_instance_
;
36 const char kDeviceTypeTouchpad
[] = "touchpad";
37 const char kDeviceTypeMouse
[] = "mouse";
38 const char kInputControl
[] = "/opt/google/input/inputcontrol";
40 typedef base::RefCountedData
<bool> RefCountedBool
;
42 bool ScriptExists(const std::string
& script
) {
43 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
44 return base::PathExists(base::FilePath(script
));
47 // Executes the input control script asynchronously, if it exists.
48 void ExecuteScriptOnFileThread(const std::vector
<std::string
>& argv
) {
49 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
50 DCHECK(!argv
.empty());
51 const std::string
& script(argv
[0]);
53 // Script must exist on device.
54 DCHECK(!base::SysInfo::IsRunningOnChromeOS() || ScriptExists(script
));
56 if (!ScriptExists(script
))
59 base::ProcessHandle handle
;
60 base::LaunchProcess(CommandLine(argv
), base::LaunchOptions(), &handle
);
61 base::EnsureProcessGetsReaped(handle
);
64 void ExecuteScript(const std::vector
<std::string
>& argv
) {
65 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
70 VLOG(1) << "About to launch: \""
71 << 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
, int value
,
84 std::vector
<std::string
>* argv
) {
85 DCHECK(value
>= kMinPointerSensitivity
&& value
<= kMaxPointerSensitivity
);
86 argv
->push_back(base::StringPrintf("--%s_sensitivity=%d",
90 void AddTPControlArguments(const char* control
,
92 std::vector
<std::string
>* argv
) {
93 argv
->push_back(base::StringPrintf("--%s=%d", control
, enabled
? 1 : 0));
96 void DeviceExistsBlockingPool(const char* device_type
,
97 scoped_refptr
<RefCountedBool
> exists
) {
98 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
100 if (!ScriptExists(kInputControl
))
103 std::vector
<std::string
> argv
;
104 argv
.push_back(kInputControl
);
105 argv
.push_back(base::StringPrintf("--type=%s", device_type
));
106 argv
.push_back("--list");
108 // Output is empty if the device is not found.
109 exists
->data
= base::GetAppOutput(CommandLine(argv
), &output
) &&
111 DVLOG(1) << "DeviceExistsBlockingPool:" << device_type
<< "=" << exists
->data
;
114 void RunCallbackUIThread(
115 scoped_refptr
<RefCountedBool
> exists
,
116 const InputDeviceSettings::DeviceExistsCallback
& callback
) {
117 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
118 DVLOG(1) << "RunCallbackUIThread " << exists
->data
;
119 callback
.Run(exists
->data
);
122 void DeviceExists(const char* script
,
123 const InputDeviceSettings::DeviceExistsCallback
& callback
) {
124 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
126 // One or both of the control scripts can apparently hang during shutdown
127 // (http://crbug.com/255546). Run the blocking pool task with
128 // CONTINUE_ON_SHUTDOWN so it won't be joined when Chrome shuts down.
129 scoped_refptr
<RefCountedBool
> exists(new RefCountedBool(false));
130 base::SequencedWorkerPool
* pool
= content::BrowserThread::GetBlockingPool();
131 scoped_refptr
<base::TaskRunner
> runner
=
132 pool
->GetTaskRunnerWithShutdownBehavior(
133 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN
);
134 runner
->PostTaskAndReply(FROM_HERE
,
135 base::Bind(&DeviceExistsBlockingPool
, script
, exists
),
136 base::Bind(&RunCallbackUIThread
, exists
, callback
));
139 class InputDeviceSettingsImpl
: public InputDeviceSettings
{
141 InputDeviceSettingsImpl();
144 // Overridden from InputDeviceSettings.
145 virtual void TouchpadExists(const DeviceExistsCallback
& callback
) override
;
146 virtual void UpdateTouchpadSettings(const TouchpadSettings
& settings
)
148 virtual void SetTouchpadSensitivity(int value
) override
;
149 virtual void SetTapToClick(bool enabled
) override
;
150 virtual void SetThreeFingerClick(bool enabled
) override
;
151 virtual void SetTapDragging(bool enabled
) override
;
152 virtual void SetNaturalScroll(bool enabled
) override
;
153 virtual void MouseExists(const DeviceExistsCallback
& callback
) override
;
154 virtual void UpdateMouseSettings(const MouseSettings
& update
) override
;
155 virtual void SetMouseSensitivity(int value
) override
;
156 virtual void SetPrimaryButtonRight(bool right
) override
;
157 virtual bool ForceKeyboardDrivenUINavigation() override
;
158 virtual void ReapplyTouchpadSettings() override
;
159 virtual void ReapplyMouseSettings() override
;
162 TouchpadSettings current_touchpad_settings_
;
163 MouseSettings current_mouse_settings_
;
165 DISALLOW_COPY_AND_ASSIGN(InputDeviceSettingsImpl
);
168 InputDeviceSettingsImpl::InputDeviceSettingsImpl() {}
170 void InputDeviceSettingsImpl::TouchpadExists(
171 const DeviceExistsCallback
& callback
) {
172 DeviceExists(kDeviceTypeTouchpad
, callback
);
175 void InputDeviceSettingsImpl::UpdateTouchpadSettings(
176 const TouchpadSettings
& settings
) {
177 std::vector
<std::string
> argv
;
178 if (current_touchpad_settings_
.Update(settings
, &argv
))
182 void InputDeviceSettingsImpl::SetTouchpadSensitivity(int value
) {
183 TouchpadSettings settings
;
184 settings
.SetSensitivity(value
);
185 UpdateTouchpadSettings(settings
);
188 void InputDeviceSettingsImpl::SetNaturalScroll(bool enabled
) {
189 TouchpadSettings settings
;
190 settings
.SetNaturalScroll(enabled
);
191 UpdateTouchpadSettings(settings
);
194 void InputDeviceSettingsImpl::SetTapToClick(bool enabled
) {
195 TouchpadSettings settings
;
196 settings
.SetTapToClick(enabled
);
197 UpdateTouchpadSettings(settings
);
200 void InputDeviceSettingsImpl::SetThreeFingerClick(bool enabled
) {
202 TouchpadSettings settings
;
203 settings
.SetThreeFingerClick(enabled
);
204 UpdateTouchpadSettings(settings
);
207 void InputDeviceSettingsImpl::SetTapDragging(bool enabled
) {
208 TouchpadSettings settings
;
209 settings
.SetTapDragging(enabled
);
210 UpdateTouchpadSettings(settings
);
213 void InputDeviceSettingsImpl::MouseExists(
214 const DeviceExistsCallback
& callback
) {
215 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
216 DeviceExists(kDeviceTypeMouse
, callback
);
219 void InputDeviceSettingsImpl::UpdateMouseSettings(const MouseSettings
& update
) {
220 std::vector
<std::string
> argv
;
221 if (current_mouse_settings_
.Update(update
, &argv
))
225 void InputDeviceSettingsImpl::SetMouseSensitivity(int value
) {
226 MouseSettings settings
;
227 settings
.SetSensitivity(value
);
228 UpdateMouseSettings(settings
);
231 void InputDeviceSettingsImpl::SetPrimaryButtonRight(bool right
) {
232 MouseSettings settings
;
233 settings
.SetPrimaryButtonRight(right
);
234 UpdateMouseSettings(settings
);
237 bool InputDeviceSettingsImpl::ForceKeyboardDrivenUINavigation() {
238 policy::BrowserPolicyConnectorChromeOS
* connector
=
239 g_browser_process
->platform_part()->browser_policy_connector_chromeos();
243 policy::DeviceCloudPolicyManagerChromeOS
* policy_manager
=
244 connector
->GetDeviceCloudPolicyManager();
248 if (policy_manager
->IsRemoraRequisition() ||
249 policy_manager
->IsSharkRequisition()) {
253 bool keyboard_driven
= false;
254 if (chromeos::system::StatisticsProvider::GetInstance()->GetMachineFlag(
255 kOemKeyboardDrivenOobeKey
, &keyboard_driven
)) {
256 return keyboard_driven
;
262 void InputDeviceSettingsImpl::ReapplyTouchpadSettings() {
263 TouchpadSettings settings
= current_touchpad_settings_
;
264 current_touchpad_settings_
= TouchpadSettings();
265 UpdateTouchpadSettings(settings
);
268 void InputDeviceSettingsImpl::ReapplyMouseSettings() {
269 MouseSettings settings
= current_mouse_settings_
;
270 current_mouse_settings_
= MouseSettings();
271 UpdateMouseSettings(settings
);
276 TouchpadSettings::TouchpadSettings() {}
278 TouchpadSettings
& TouchpadSettings::operator=(const TouchpadSettings
& other
) {
279 if (&other
!= this) {
280 sensitivity_
= other
.sensitivity_
;
281 tap_to_click_
= other
.tap_to_click_
;
282 three_finger_click_
= other
.three_finger_click_
;
283 tap_dragging_
= other
.tap_dragging_
;
284 natural_scroll_
= other
.natural_scroll_
;
289 void TouchpadSettings::SetSensitivity(int value
) {
290 sensitivity_
.Set(value
);
293 int TouchpadSettings::GetSensitivity() const {
294 return sensitivity_
.value();
297 void TouchpadSettings::SetTapToClick(bool enabled
) {
298 tap_to_click_
.Set(enabled
);
301 bool TouchpadSettings::GetTapToClick() const {
302 return tap_to_click_
.value();
305 void TouchpadSettings::SetNaturalScroll(bool enabled
) {
306 natural_scroll_
.Set(enabled
);
309 bool TouchpadSettings::GetNaturalScroll() const {
310 return natural_scroll_
.value();
313 void TouchpadSettings::SetThreeFingerClick(bool enabled
) {
314 three_finger_click_
.Set(enabled
);
317 bool TouchpadSettings::GetThreeFingerClick() const {
318 return three_finger_click_
.value();
321 void TouchpadSettings::SetTapDragging(bool enabled
) {
322 tap_dragging_
.Set(enabled
);
325 bool TouchpadSettings::GetTapDragging() const {
326 return tap_dragging_
.value();
329 bool TouchpadSettings::Update(const TouchpadSettings
& settings
,
330 std::vector
<std::string
>* argv
) {
332 argv
->push_back(kInputControl
);
333 bool updated
= false;
334 if (sensitivity_
.Update(settings
.sensitivity_
)) {
337 AddSensitivityArguments(kDeviceTypeTouchpad
, sensitivity_
.value(), argv
);
339 if (tap_to_click_
.Update(settings
.tap_to_click_
)) {
342 AddTPControlArguments("tapclick", tap_to_click_
.value(), argv
);
344 if (three_finger_click_
.Update(settings
.three_finger_click_
)) {
347 AddTPControlArguments("t5r2_three_finger_click",
348 three_finger_click_
.value(),
351 if (tap_dragging_
.Update(settings
.tap_dragging_
)) {
354 AddTPControlArguments("tapdrag", tap_dragging_
.value(), argv
);
356 natural_scroll_
.Update(settings
.natural_scroll_
);
357 // Always send natural scrolling to the shell command, as a workaround.
358 // See crbug.com/406480
359 if (natural_scroll_
.is_set()) {
362 AddTPControlArguments("australian_scrolling", natural_scroll_
.value(),
368 MouseSettings::MouseSettings() {}
370 MouseSettings
& MouseSettings::operator=(const MouseSettings
& other
) {
371 if (&other
!= this) {
372 sensitivity_
= other
.sensitivity_
;
373 primary_button_right_
= other
.primary_button_right_
;
378 void MouseSettings::SetSensitivity(int value
) {
379 sensitivity_
.Set(value
);
382 int MouseSettings::GetSensitivity() const {
383 return sensitivity_
.value();
386 void MouseSettings::SetPrimaryButtonRight(bool right
) {
387 primary_button_right_
.Set(right
);
390 bool MouseSettings::GetPrimaryButtonRight() const {
391 return primary_button_right_
.value();
394 bool MouseSettings::Update(const MouseSettings
& settings
,
395 std::vector
<std::string
>* argv
) {
397 argv
->push_back(kInputControl
);
398 bool updated
= false;
399 if (sensitivity_
.Update(settings
.sensitivity_
)) {
402 AddSensitivityArguments(kDeviceTypeMouse
, sensitivity_
.value(), argv
);
404 if (primary_button_right_
.Update(settings
.primary_button_right_
)) {
407 AddTPControlArguments("mouse_swap_lr", primary_button_right_
.value(),
415 InputDeviceSettings
* InputDeviceSettings::Get() {
416 if (g_test_instance_
)
417 return g_test_instance_
;
419 g_instance_
= new InputDeviceSettingsImpl
;
424 void InputDeviceSettings::SetSettingsForTesting(
425 InputDeviceSettings
* test_settings
) {
426 if (g_test_instance_
== test_settings
)
428 delete g_test_instance_
;
429 g_test_instance_
= test_settings
;
432 } // namespace system
433 } // namespace chromeos