Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(334)

Side by Side Diff: content/browser/gamepad/platform_data_fetcher_linux.cc

Issue 8899017: Add gamepad data fetcher for Linux (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fd and copy length bug fixes Created 9 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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 "content/browser/gamepad/platform_data_fetcher_linux.h"
6
7 #include <dlfcn.h>
8 #include <fcntl.h>
9 #include <libudev.h>
10 #include <linux/joystick.h>
11 #include <sys/stat.h>
12 #include <sys/types.h>
13
14 #include "base/debug/trace_event.h"
15 #include "base/eintr_wrapper.h"
16 #include "base/message_loop.h"
17 #include "base/string_number_conversions.h"
18 #include "base/stringprintf.h"
19 #include "base/string_util.h"
20 #include "base/utf_string_conversions.h"
21 #include "content/common/gamepad_hardware_buffer.h"
22
23 namespace content {
24
25 using WebKit::WebGamepad;
26 using WebKit::WebGamepads;
27
28 GamepadPlatformDataFetcherLinux::GamepadPlatformDataFetcherLinux() {
29 for (size_t i = 0; i < arraysize(device_fds_); ++i)
30 device_fds_[i] = -1;
31
32 udev_ = udev_new();
33
34 monitor_ = udev_monitor_new_from_netlink(udev_, "udev");
35 udev_monitor_filter_add_match_subsystem_devtype(monitor_, "input", NULL);
36 udev_monitor_enable_receiving(monitor_);
37 monitor_fd_ = udev_monitor_get_fd(monitor_);
38 MessageLoopForIO::current()->WatchFileDescriptor(monitor_fd_, true,
39 MessageLoopForIO::WATCH_READ, &monitor_watcher_, this);
40
41 EnumerateDevices();
42 }
43
44 GamepadPlatformDataFetcherLinux::~GamepadPlatformDataFetcherLinux() {
45 monitor_watcher_.StopWatchingFileDescriptor();
46 udev_unref(udev_);
47 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
48 if (device_fds_[i] >= 0)
49 HANDLE_EINTR(close(device_fds_[i]));
Ryan Sleevi 2011/12/19 23:32:59 Obscure POSIX trivia - Turns out there is no need
50 }
51 }
52
53 void GamepadPlatformDataFetcherLinux::GetGamepadData(WebGamepads* pads, bool) {
54 TRACE_EVENT0("GAMEPAD", "GetGamepadData");
55
56 data_.length = WebGamepads::itemsLengthCap;
57
58 // Update our internal state.
59 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
60 if (device_fds_[i] >= 0) {
61 ReadDeviceData(i);
62 }
63 }
64
65 // Copy to the current state to the output buffer, using the mapping
66 // function, if there is one available.
67 pads->length = data_.length;
68 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
69 if (mappers_[i])
70 mappers_[i](data_.items[i], &pads->items[i]);
71 else
72 pads->items[i] = data_.items[i];
73 }
74 }
75
76 void GamepadPlatformDataFetcherLinux::OnFileCanReadWithoutBlocking(int fd) {
77 // Events occur when devices attached to the system are added, removed, or
78 // change state. udev_monitor_receive_device() will return a device object
79 // representing the device which changed and what type of change occured.
80 DCHECK(monitor_fd_ == fd);
81 udev_device* dev = udev_monitor_receive_device(monitor_);
82 RefreshDevice(dev);
83 udev_device_unref(dev);
84 }
85
86 void GamepadPlatformDataFetcherLinux::OnFileCanWriteWithoutBlocking(int fd) {
87 }
88
89 bool GamepadPlatformDataFetcherLinux::IsGamepad(
90 udev_device* dev,
91 int& index,
92 std::string& path) {
93 if (!udev_device_get_property_value(dev, "ID_INPUT_JOYSTICK"))
94 return false;
95
96 const char* node_path = udev_device_get_devnode(dev);
97 if (!node_path)
98 return false;
99
100 static const char kJoystickRoot[] = "/dev/input/js";
101 bool is_gamepad =
102 strncmp(kJoystickRoot, node_path, sizeof(kJoystickRoot) - 1) == 0;
103 if (is_gamepad) {
104 if (!base::StringToInt(&node_path[sizeof(kJoystickRoot) - 1],
105 &node_path[strlen(node_path)],
106 &index))
107 return false;
108 if (index < 0 || index >= static_cast<int>(WebGamepads::itemsLengthCap))
109 return false;
110 path = std::string(node_path);
111 }
112 return is_gamepad;
113 }
114
115 // Used during enumeration, and monitor notifications.
116 void GamepadPlatformDataFetcherLinux::RefreshDevice(udev_device* dev) {
117 int index;
118 std::string node_path;
119 if (IsGamepad(dev, index, node_path)) {
120 int& device_fd = device_fds_[index];
121 WebGamepad& pad = data_.items[index];
122 GamepadStandardMappingFunction& mapper = mappers_[index];
123
124 if (device_fd >= 0)
125 HANDLE_EINTR(close(device_fd));
126
127 // The device pointed to by dev contains information about the input
128 // device. In order to get the information about the USB device, get the
129 // parent device with the subsystem/devtype pair of "usb"/"usb_device".
130 // This function walks up the tree several levels.
131 dev = udev_device_get_parent_with_subsystem_devtype(
132 dev,
133 "usb",
134 "usb_device");
135 if (!dev) {
136 // Unable to get device information, don't use this device.
137 device_fd = -1;
138 pad.connected = false;
139 return;
140 }
141
142 device_fd = HANDLE_EINTR(open(node_path.c_str(), O_RDONLY | O_NONBLOCK));
143 if (device_fd < 0) {
144 // Unable to open device, don't use.
145 pad.connected = false;
146 return;
147 }
148
149 const char* vendor_id = udev_device_get_sysattr_value(dev, "idVendor");
150 const char* product_id = udev_device_get_sysattr_value(dev, "idProduct");
151 mapper = GetGamepadStandardMappingFunction(vendor_id, product_id);
152
153 const char* manufacturer =
154 udev_device_get_sysattr_value(dev, "manufacturer");
155 const char* product = udev_device_get_sysattr_value(dev, "product");
156
157 // Driver returns utf-8 strings here, so combine in utf-8 and then convert
158 // to WebUChar to build the id string.
159 std::string id;
160 if (mapper) {
161 id = base::StringPrintf("%s %s (STANDARD GAMEPAD)",
162 manufacturer,
163 product);
164 } else {
165 id = base::StringPrintf("%s %s (Vendor: %s Product: %s)",
166 manufacturer,
167 product,
168 vendor_id,
169 product_id);
170 }
171 TruncateUTF8ToByteSize(id, WebGamepad::idLengthCap - 1, &id);
172 string16 tmp16 = UTF8ToUTF16(id);
173 memset(pad.id, 0, sizeof(pad.id));
174 tmp16.copy(pad.id, arraysize(pad.id) - 1);
175
176 pad.connected = true;
177 }
178 }
179
180 void GamepadPlatformDataFetcherLinux::EnumerateDevices() {
181 udev_enumerate* enumerate = udev_enumerate_new(udev_);
182 udev_enumerate_add_match_subsystem(enumerate, "input");
183 udev_enumerate_scan_devices(enumerate);
184
185 udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate);
186 for (udev_list_entry* dev_list_entry = devices;
187 dev_list_entry != NULL;
188 dev_list_entry = udev_list_entry_get_next(dev_list_entry)) {
189 // Get the filename of the /sys entry for the device and create a
190 // udev_device object (dev) representing it
191 const char* path = udev_list_entry_get_name(dev_list_entry);
192 udev_device* dev = udev_device_new_from_syspath(udev_, path);
193 RefreshDevice(dev);
194 udev_device_unref(dev);
195 }
196 // Free the enumerator object
197 udev_enumerate_unref(enumerate);
198 }
199
200 void GamepadPlatformDataFetcherLinux::ReadDeviceData(size_t index) {
201 int& fd = device_fds_[index];
202 WebGamepad& pad = data_.items[index];
203 DCHECK(fd >= 0);
204
205 js_event event;
206 while (HANDLE_EINTR(read(fd, &event, sizeof(struct js_event)) > 0)) {
207 size_t item = event.number;
208 if (event.type & JS_EVENT_AXIS) {
209 if (item >= WebGamepad::axesLengthCap)
210 continue;
211 pad.axes[item] = event.value / 32767.f;
212 if (item >= pad.axesLength)
213 pad.axesLength = item + 1;
214 } else if (event.type & JS_EVENT_BUTTON) {
215 if (item >= WebGamepad::buttonsLengthCap)
216 continue;
217 pad.buttons[item] = event.value ? 1.0 : 0.0;
218 if (item >= pad.buttonsLength)
219 pad.buttonsLength = item + 1;
220 }
221 pad.timestamp = event.time;
222 }
223 }
224
225
226 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/gamepad/platform_data_fetcher_linux.h ('k') | content/browser/gamepad/platform_data_fetcher_mac.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698