Check USB device path access when prompting users to select a device.
[chromium-blink-merge.git] / chrome / browser / chromeos / system / input_device_settings_impl_x11.cc
blobaa41497a0fed8a6b7e50957fbac3cbebc10a855d
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"
7 #include <string>
8 #include <vector>
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"
26 namespace chromeos {
27 namespace system {
29 namespace {
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))
56 return;
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));
67 if (argv.size() == 1)
68 return;
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,
84 int value,
85 std::vector<std::string>* argv) {
86 DCHECK(value >= kMinPointerSensitivity && value <= kMaxPointerSensitivity);
87 argv->push_back(
88 base::StringPrintf("--%s_sensitivity=%d", device_type, value));
91 void AddTPControlArguments(const char* control,
92 bool enabled,
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))
102 return;
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");
108 std::string output;
109 // Output is empty if the device is not found.
110 exists->data =
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 {
142 public:
143 InputDeviceSettingsImplX11();
145 protected:
146 ~InputDeviceSettingsImplX11() override {}
148 private:
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
167 // to apply update.
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);
190 ExecuteScript(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) {
213 // For Alex/ZGB.
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);
236 ExecuteScript(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(),
278 argv);
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);
303 } // namespace
305 // static
306 InputDeviceSettings* InputDeviceSettings::Get() {
307 if (g_test_instance)
308 return g_test_instance;
309 if (!g_instance)
310 g_instance = new InputDeviceSettingsImplX11;
311 return g_instance;
314 // static
315 void InputDeviceSettings::SetSettingsForTesting(
316 InputDeviceSettings* test_settings) {
317 if (g_test_instance == test_settings)
318 return;
319 delete g_test_instance;
320 g_test_instance = test_settings;
323 } // namespace system
324 } // namespace chromeos