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/file_util.h"
10 #include "base/files/file_path.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 const char kRemoraRequisition
[] = "remora";
42 typedef base::RefCountedData
<bool> RefCountedBool
;
44 bool ScriptExists(const std::string
& script
) {
45 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
46 return base::PathExists(base::FilePath(script
));
49 // Executes the input control script asynchronously, if it exists.
50 void ExecuteScriptOnFileThread(const std::vector
<std::string
>& argv
) {
51 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
52 DCHECK(!argv
.empty());
53 const std::string
& script(argv
[0]);
55 // Script must exist on device.
56 DCHECK(!base::SysInfo::IsRunningOnChromeOS() || ScriptExists(script
));
58 if (!ScriptExists(script
))
61 base::ProcessHandle handle
;
62 base::LaunchProcess(CommandLine(argv
), base::LaunchOptions(), &handle
);
63 base::EnsureProcessGetsReaped(handle
);
66 void ExecuteScript(const std::vector
<std::string
>& argv
) {
67 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
72 VLOG(1) << "About to launch: \""
73 << CommandLine(argv
).GetCommandLineString() << "\"";
75 // Control scripts can take long enough to cause SIGART during shutdown
76 // (http://crbug.com/261426). Run the blocking pool task with
77 // CONTINUE_ON_SHUTDOWN so it won't be joined when Chrome shuts down.
78 base::SequencedWorkerPool
* pool
= content::BrowserThread::GetBlockingPool();
79 scoped_refptr
<base::TaskRunner
> runner
=
80 pool
->GetTaskRunnerWithShutdownBehavior(
81 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN
);
82 runner
->PostTask(FROM_HERE
, base::Bind(&ExecuteScriptOnFileThread
, argv
));
85 void AddSensitivityArguments(const char* device_type
, int value
,
86 std::vector
<std::string
>* argv
) {
87 DCHECK(value
>= kMinPointerSensitivity
&& value
<= kMaxPointerSensitivity
);
88 argv
->push_back(base::StringPrintf("--%s_sensitivity=%d",
92 void AddTPControlArguments(const char* control
,
94 std::vector
<std::string
>* argv
) {
95 argv
->push_back(base::StringPrintf("--%s=%d", control
, enabled
? 1 : 0));
98 void DeviceExistsBlockingPool(const char* device_type
,
99 scoped_refptr
<RefCountedBool
> exists
) {
100 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
101 exists
->data
= false;
102 if (!ScriptExists(kInputControl
))
105 std::vector
<std::string
> argv
;
106 argv
.push_back(kInputControl
);
107 argv
.push_back(base::StringPrintf("--type=%s", device_type
));
108 argv
.push_back("--list");
110 // Output is empty if the device is not found.
111 exists
->data
= base::GetAppOutput(CommandLine(argv
), &output
) &&
113 DVLOG(1) << "DeviceExistsBlockingPool:" << device_type
<< "=" << exists
->data
;
116 void RunCallbackUIThread(
117 scoped_refptr
<RefCountedBool
> exists
,
118 const InputDeviceSettings::DeviceExistsCallback
& callback
) {
119 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
120 DVLOG(1) << "RunCallbackUIThread " << exists
->data
;
121 callback
.Run(exists
->data
);
124 void DeviceExists(const char* script
,
125 const InputDeviceSettings::DeviceExistsCallback
& callback
) {
126 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
128 // One or both of the control scripts can apparently hang during shutdown
129 // (http://crbug.com/255546). Run the blocking pool task with
130 // CONTINUE_ON_SHUTDOWN so it won't be joined when Chrome shuts down.
131 scoped_refptr
<RefCountedBool
> exists(new RefCountedBool(false));
132 base::SequencedWorkerPool
* pool
= content::BrowserThread::GetBlockingPool();
133 scoped_refptr
<base::TaskRunner
> runner
=
134 pool
->GetTaskRunnerWithShutdownBehavior(
135 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN
);
136 runner
->PostTaskAndReply(FROM_HERE
,
137 base::Bind(&DeviceExistsBlockingPool
, script
, exists
),
138 base::Bind(&RunCallbackUIThread
, exists
, callback
));
141 class InputDeviceSettingsImpl
: public InputDeviceSettings
{
143 InputDeviceSettingsImpl();
146 // Overridden from InputDeviceSettings.
147 virtual void TouchpadExists(const DeviceExistsCallback
& callback
) OVERRIDE
;
148 virtual void UpdateTouchpadSettings(const TouchpadSettings
& settings
)
150 virtual void SetTouchpadSensitivity(int value
) OVERRIDE
;
151 virtual void SetTapToClick(bool enabled
) OVERRIDE
;
152 virtual void SetThreeFingerClick(bool enabled
) OVERRIDE
;
153 virtual void SetTapDragging(bool enabled
) OVERRIDE
;
154 virtual void SetNaturalScroll(bool enabled
) OVERRIDE
;
155 virtual void MouseExists(const DeviceExistsCallback
& callback
) OVERRIDE
;
156 virtual void UpdateMouseSettings(const MouseSettings
& update
) OVERRIDE
;
157 virtual void SetMouseSensitivity(int value
) OVERRIDE
;
158 virtual void SetPrimaryButtonRight(bool right
) OVERRIDE
;
159 virtual bool ForceKeyboardDrivenUINavigation() OVERRIDE
;
160 virtual void ReapplyTouchpadSettings() OVERRIDE
;
161 virtual void ReapplyMouseSettings() OVERRIDE
;
164 TouchpadSettings current_touchpad_settings_
;
165 MouseSettings current_mouse_settings_
;
167 DISALLOW_COPY_AND_ASSIGN(InputDeviceSettingsImpl
);
170 InputDeviceSettingsImpl::InputDeviceSettingsImpl() {}
172 void InputDeviceSettingsImpl::TouchpadExists(
173 const DeviceExistsCallback
& callback
) {
174 DeviceExists(kDeviceTypeTouchpad
, callback
);
177 void InputDeviceSettingsImpl::UpdateTouchpadSettings(
178 const TouchpadSettings
& settings
) {
179 std::vector
<std::string
> argv
;
180 if (current_touchpad_settings_
.Update(settings
, &argv
))
184 void InputDeviceSettingsImpl::SetTouchpadSensitivity(int value
) {
185 TouchpadSettings settings
;
186 settings
.SetSensitivity(value
);
187 UpdateTouchpadSettings(settings
);
190 void InputDeviceSettingsImpl::SetNaturalScroll(bool enabled
) {
191 TouchpadSettings settings
;
192 settings
.SetNaturalScroll(enabled
);
193 UpdateTouchpadSettings(settings
);
196 void InputDeviceSettingsImpl::SetTapToClick(bool enabled
) {
197 TouchpadSettings settings
;
198 settings
.SetTapToClick(enabled
);
199 UpdateTouchpadSettings(settings
);
202 void InputDeviceSettingsImpl::SetThreeFingerClick(bool enabled
) {
204 TouchpadSettings settings
;
205 settings
.SetThreeFingerClick(enabled
);
206 UpdateTouchpadSettings(settings
);
209 void InputDeviceSettingsImpl::SetTapDragging(bool enabled
) {
210 TouchpadSettings settings
;
211 settings
.SetTapDragging(enabled
);
212 UpdateTouchpadSettings(settings
);
215 void InputDeviceSettingsImpl::MouseExists(
216 const DeviceExistsCallback
& callback
) {
217 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
218 DeviceExists(kDeviceTypeMouse
, callback
);
221 void InputDeviceSettingsImpl::UpdateMouseSettings(const MouseSettings
& update
) {
222 std::vector
<std::string
> argv
;
223 if (current_mouse_settings_
.Update(update
, &argv
))
227 void InputDeviceSettingsImpl::SetMouseSensitivity(int value
) {
228 MouseSettings settings
;
229 settings
.SetSensitivity(value
);
230 UpdateMouseSettings(settings
);
233 void InputDeviceSettingsImpl::SetPrimaryButtonRight(bool right
) {
234 MouseSettings settings
;
235 settings
.SetPrimaryButtonRight(right
);
236 UpdateMouseSettings(settings
);
239 bool InputDeviceSettingsImpl::ForceKeyboardDrivenUINavigation() {
240 policy::BrowserPolicyConnectorChromeOS
* connector
=
241 g_browser_process
->platform_part()->browser_policy_connector_chromeos();
245 policy::DeviceCloudPolicyManagerChromeOS
* policy_manager
=
246 connector
->GetDeviceCloudPolicyManager();
250 if (base::strcasecmp(policy_manager
->GetDeviceRequisition().c_str(),
251 kRemoraRequisition
) == 0) {
255 bool keyboard_driven
= false;
256 if (chromeos::system::StatisticsProvider::GetInstance()->GetMachineFlag(
257 kOemKeyboardDrivenOobeKey
, &keyboard_driven
)) {
258 return keyboard_driven
;
264 void InputDeviceSettingsImpl::ReapplyTouchpadSettings() {
265 TouchpadSettings settings
= current_touchpad_settings_
;
266 current_touchpad_settings_
= TouchpadSettings();
267 UpdateTouchpadSettings(settings
);
270 void InputDeviceSettingsImpl::ReapplyMouseSettings() {
271 MouseSettings settings
= current_mouse_settings_
;
272 current_mouse_settings_
= MouseSettings();
273 UpdateMouseSettings(settings
);
278 TouchpadSettings::TouchpadSettings() {}
280 TouchpadSettings
& TouchpadSettings::operator=(const TouchpadSettings
& other
) {
281 if (&other
!= this) {
282 sensitivity_
= other
.sensitivity_
;
283 tap_to_click_
= other
.tap_to_click_
;
284 three_finger_click_
= other
.three_finger_click_
;
285 tap_dragging_
= other
.tap_dragging_
;
286 natural_scroll_
= other
.natural_scroll_
;
291 void TouchpadSettings::SetSensitivity(int value
) {
292 sensitivity_
.Set(value
);
295 int TouchpadSettings::GetSensitivity() const {
296 return sensitivity_
.value();
299 void TouchpadSettings::SetTapToClick(bool enabled
) {
300 tap_to_click_
.Set(enabled
);
303 bool TouchpadSettings::GetTapToClick() const {
304 return tap_to_click_
.value();
307 void TouchpadSettings::SetNaturalScroll(bool enabled
) {
308 natural_scroll_
.Set(enabled
);
311 bool TouchpadSettings::GetNaturalScroll() const {
312 return natural_scroll_
.value();
315 void TouchpadSettings::SetThreeFingerClick(bool enabled
) {
316 three_finger_click_
.Set(enabled
);
319 bool TouchpadSettings::GetThreeFingerClick() const {
320 return three_finger_click_
.value();
323 void TouchpadSettings::SetTapDragging(bool enabled
) {
324 tap_dragging_
.Set(enabled
);
327 bool TouchpadSettings::GetTapDragging() const {
328 return tap_dragging_
.value();
331 bool TouchpadSettings::Update(const TouchpadSettings
& settings
,
332 std::vector
<std::string
>* argv
) {
334 argv
->push_back(kInputControl
);
335 bool updated
= false;
336 if (sensitivity_
.Update(settings
.sensitivity_
)) {
339 AddSensitivityArguments(kDeviceTypeTouchpad
, sensitivity_
.value(), argv
);
341 if (tap_to_click_
.Update(settings
.tap_to_click_
)) {
344 AddTPControlArguments("tapclick", tap_to_click_
.value(), argv
);
346 if (three_finger_click_
.Update(settings
.three_finger_click_
)) {
349 AddTPControlArguments("t5r2_three_finger_click",
350 three_finger_click_
.value(),
353 if (tap_dragging_
.Update(settings
.tap_dragging_
)) {
356 AddTPControlArguments("tapdrag", tap_dragging_
.value(), argv
);
358 if (natural_scroll_
.Update(settings
.natural_scroll_
)) {
361 AddTPControlArguments("australian_scrolling", natural_scroll_
.value(),
367 MouseSettings::MouseSettings() {}
369 MouseSettings
& MouseSettings::operator=(const MouseSettings
& other
) {
370 if (&other
!= this) {
371 sensitivity_
= other
.sensitivity_
;
372 primary_button_right_
= other
.primary_button_right_
;
377 void MouseSettings::SetSensitivity(int value
) {
378 sensitivity_
.Set(value
);
381 int MouseSettings::GetSensitivity() const {
382 return sensitivity_
.value();
385 void MouseSettings::SetPrimaryButtonRight(bool right
) {
386 primary_button_right_
.Set(right
);
389 bool MouseSettings::GetPrimaryButtonRight() const {
390 return primary_button_right_
.value();
393 bool MouseSettings::Update(const MouseSettings
& settings
,
394 std::vector
<std::string
>* argv
) {
396 argv
->push_back(kInputControl
);
397 bool updated
= false;
398 if (sensitivity_
.Update(settings
.sensitivity_
)) {
401 AddSensitivityArguments(kDeviceTypeMouse
, sensitivity_
.value(), argv
);
403 if (primary_button_right_
.Update(settings
.primary_button_right_
)) {
406 AddTPControlArguments("mouse_swap_lr", primary_button_right_
.value(),
414 InputDeviceSettings
* InputDeviceSettings::Get() {
415 if (g_test_instance_
)
416 return g_test_instance_
;
418 g_instance_
= new InputDeviceSettingsImpl
;
423 void InputDeviceSettings::SetSettingsForTesting(
424 InputDeviceSettings
* test_settings
) {
425 if (g_test_instance_
== test_settings
)
427 delete g_test_instance_
;
428 g_test_instance_
= test_settings
;
431 } // namespace system
432 } // namespace chromeos