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

Side by Side Diff: content/browser/gamepad/xbox_data_fetcher_mac.mm

Issue 1627643002: Revert of Refactoring gamepad polling to support dynamically added sources (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 11 months 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
« no previous file with comments | « content/browser/gamepad/xbox_data_fetcher_mac.cc ('k') | content/content_browser.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2013 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/xbox_data_fetcher_mac.h"
6
7 #include <algorithm>
8 #include <cmath>
9 #include <limits>
10
11 #include <CoreFoundation/CoreFoundation.h>
12 #include <IOKit/IOCFPlugIn.h>
13 #include <IOKit/IOKitLib.h>
14 #include <IOKit/usb/IOUSBLib.h>
15 #include <IOKit/usb/USB.h>
16
17 #include "base/logging.h"
18 #include "base/mac/foundation_util.h"
19
20 using blink::WebGamepad;
21
22 namespace content {
23
24 namespace {
25 const int kVendorMicrosoft = 0x045e;
26 const int kProductXbox360Controller = 0x028e;
27 const int kProductXboxOneController = 0x02d1;
28
29 const int kXbox360ReadEndpoint = 1;
30 const int kXbox360ControlEndpoint = 2;
31
32 const int kXboxOneReadEndpoint = 2;
33 const int kXboxOneControlEndpoint = 1;
34
35 enum {
36 STATUS_MESSAGE_BUTTONS = 0,
37 STATUS_MESSAGE_LED = 1,
38
39 // Apparently this message tells you if the rumble pack is disabled in the
40 // controller. If the rumble pack is disabled, vibration control messages
41 // have no effect.
42 STATUS_MESSAGE_RUMBLE = 3,
43 };
44
45 enum {
46 XBOX_ONE_STATUS_MESSAGE_BUTTONS = 0x20,
47 };
48
49 enum {
50 CONTROL_MESSAGE_SET_RUMBLE = 0,
51 CONTROL_MESSAGE_SET_LED = 1,
52 };
53
54 #pragma pack(push, 1)
55 struct Xbox360ButtonData {
56 bool dpad_up : 1;
57 bool dpad_down : 1;
58 bool dpad_left : 1;
59 bool dpad_right : 1;
60
61 bool start : 1;
62 bool back : 1;
63 bool stick_left_click : 1;
64 bool stick_right_click : 1;
65
66 bool bumper_left : 1;
67 bool bumper_right : 1;
68 bool guide : 1;
69 bool dummy1 : 1; // Always 0.
70
71 bool a : 1;
72 bool b : 1;
73 bool x : 1;
74 bool y : 1;
75
76 uint8_t trigger_left;
77 uint8_t trigger_right;
78
79 int16_t stick_left_x;
80 int16_t stick_left_y;
81 int16_t stick_right_x;
82 int16_t stick_right_y;
83
84 // Always 0.
85 uint32_t dummy2;
86 uint16_t dummy3;
87 };
88
89 struct XboxOneButtonData {
90 bool sync : 1;
91 bool dummy1 : 1; // Always 0.
92 bool start : 1;
93 bool back : 1;
94
95 bool a : 1;
96 bool b : 1;
97 bool x : 1;
98 bool y : 1;
99
100 bool dpad_up : 1;
101 bool dpad_down : 1;
102 bool dpad_left : 1;
103 bool dpad_right : 1;
104
105 bool bumper_left : 1;
106 bool bumper_right : 1;
107 bool stick_left_click : 1;
108 bool stick_right_click : 1;
109
110 uint16_t trigger_left;
111 uint16_t trigger_right;
112
113 int16_t stick_left_x;
114 int16_t stick_left_y;
115 int16_t stick_right_x;
116 int16_t stick_right_y;
117 };
118 #pragma pack(pop)
119
120 static_assert(sizeof(Xbox360ButtonData) == 18, "xbox button data wrong size");
121 static_assert(sizeof(XboxOneButtonData) == 14, "xbox button data wrong size");
122
123 // From MSDN:
124 // http://msdn.microsoft.com/en-us/library/windows/desktop/ee417001(v=vs.85).asp x#dead_zone
125 const int16_t kLeftThumbDeadzone = 7849;
126 const int16_t kRightThumbDeadzone = 8689;
127 const uint8_t kXbox360TriggerDeadzone = 30;
128 const uint16_t kXboxOneTriggerMax = 1023;
129 const uint16_t kXboxOneTriggerDeadzone = 120;
130
131 void NormalizeAxis(int16_t x,
132 int16_t y,
133 int16_t deadzone,
134 float* x_out,
135 float* y_out) {
136 float x_val = x;
137 float y_val = y;
138
139 // Determine how far the stick is pushed.
140 float real_magnitude = std::sqrt(x_val * x_val + y_val * y_val);
141
142 // Check if the controller is outside a circular dead zone.
143 if (real_magnitude > deadzone) {
144 // Clip the magnitude at its expected maximum value.
145 float magnitude = std::min(32767.0f, real_magnitude);
146
147 // Adjust magnitude relative to the end of the dead zone.
148 magnitude -= deadzone;
149
150 // Normalize the magnitude with respect to its expected range giving a
151 // magnitude value of 0.0 to 1.0
152 float ratio = (magnitude / (32767 - deadzone)) / real_magnitude;
153
154 // Y is negated because xbox controllers have an opposite sign from
155 // the 'standard controller' recommendations.
156 *x_out = x_val * ratio;
157 *y_out = -y_val * ratio;
158 } else {
159 // If the controller is in the deadzone zero out the magnitude.
160 *x_out = *y_out = 0.0f;
161 }
162 }
163
164 float NormalizeTrigger(uint8_t value) {
165 return value < kXbox360TriggerDeadzone
166 ? 0
167 : static_cast<float>(value - kXbox360TriggerDeadzone) /
168 (std::numeric_limits<uint8_t>::max() -
169 kXbox360TriggerDeadzone);
170 }
171
172 float NormalizeXboxOneTrigger(uint16_t value) {
173 return value < kXboxOneTriggerDeadzone ? 0 :
174 static_cast<float>(value - kXboxOneTriggerDeadzone) /
175 (kXboxOneTriggerMax - kXboxOneTriggerDeadzone);
176 }
177
178 void NormalizeXbox360ButtonData(const Xbox360ButtonData& data,
179 XboxController::Data* normalized_data) {
180 normalized_data->buttons[0] = data.a;
181 normalized_data->buttons[1] = data.b;
182 normalized_data->buttons[2] = data.x;
183 normalized_data->buttons[3] = data.y;
184 normalized_data->buttons[4] = data.bumper_left;
185 normalized_data->buttons[5] = data.bumper_right;
186 normalized_data->buttons[6] = data.back;
187 normalized_data->buttons[7] = data.start;
188 normalized_data->buttons[8] = data.stick_left_click;
189 normalized_data->buttons[9] = data.stick_right_click;
190 normalized_data->buttons[10] = data.dpad_up;
191 normalized_data->buttons[11] = data.dpad_down;
192 normalized_data->buttons[12] = data.dpad_left;
193 normalized_data->buttons[13] = data.dpad_right;
194 normalized_data->buttons[14] = data.guide;
195 normalized_data->triggers[0] = NormalizeTrigger(data.trigger_left);
196 normalized_data->triggers[1] = NormalizeTrigger(data.trigger_right);
197 NormalizeAxis(data.stick_left_x,
198 data.stick_left_y,
199 kLeftThumbDeadzone,
200 &normalized_data->axes[0],
201 &normalized_data->axes[1]);
202 NormalizeAxis(data.stick_right_x,
203 data.stick_right_y,
204 kRightThumbDeadzone,
205 &normalized_data->axes[2],
206 &normalized_data->axes[3]);
207 }
208
209 void NormalizeXboxOneButtonData(const XboxOneButtonData& data,
210 XboxController::Data* normalized_data) {
211 normalized_data->buttons[0] = data.a;
212 normalized_data->buttons[1] = data.b;
213 normalized_data->buttons[2] = data.x;
214 normalized_data->buttons[3] = data.y;
215 normalized_data->buttons[4] = data.bumper_left;
216 normalized_data->buttons[5] = data.bumper_right;
217 normalized_data->buttons[6] = data.back;
218 normalized_data->buttons[7] = data.start;
219 normalized_data->buttons[8] = data.stick_left_click;
220 normalized_data->buttons[9] = data.stick_right_click;
221 normalized_data->buttons[10] = data.dpad_up;
222 normalized_data->buttons[11] = data.dpad_down;
223 normalized_data->buttons[12] = data.dpad_left;
224 normalized_data->buttons[13] = data.dpad_right;
225 normalized_data->buttons[14] = data.sync;
226 normalized_data->triggers[0] = NormalizeXboxOneTrigger(data.trigger_left);
227 normalized_data->triggers[1] = NormalizeXboxOneTrigger(data.trigger_right);
228 NormalizeAxis(data.stick_left_x,
229 data.stick_left_y,
230 kLeftThumbDeadzone,
231 &normalized_data->axes[0],
232 &normalized_data->axes[1]);
233 NormalizeAxis(data.stick_right_x,
234 data.stick_right_y,
235 kRightThumbDeadzone,
236 &normalized_data->axes[2],
237 &normalized_data->axes[3]);
238 }
239
240 void CopyNSStringAsUTF16LittleEndian(
241 NSString* src, blink::WebUChar* dest, size_t dest_len) {
242 NSData* as16 = [src dataUsingEncoding:NSUTF16LittleEndianStringEncoding];
243 memset(dest, 0, dest_len);
244 [as16 getBytes:dest length:dest_len - sizeof(blink::WebUChar)];
245 }
246
247 } // namespace
248
249 XboxController::XboxController(Delegate* delegate)
250 : device_(NULL),
251 interface_(NULL),
252 device_is_open_(false),
253 interface_is_open_(false),
254 read_buffer_size_(0),
255 led_pattern_(LED_NUM_PATTERNS),
256 location_id_(0),
257 delegate_(delegate),
258 controller_type_(UNKNOWN_CONTROLLER),
259 read_endpoint_(0),
260 control_endpoint_(0) {
261 }
262
263 XboxController::~XboxController() {
264 if (source_)
265 CFRunLoopSourceInvalidate(source_);
266 if (interface_ && interface_is_open_)
267 (*interface_)->USBInterfaceClose(interface_);
268 if (device_ && device_is_open_)
269 (*device_)->USBDeviceClose(device_);
270 }
271
272 bool XboxController::OpenDevice(io_service_t service) {
273 IOCFPlugInInterface **plugin;
274 SInt32 score; // Unused, but required for IOCreatePlugInInterfaceForService.
275 kern_return_t kr =
276 IOCreatePlugInInterfaceForService(service,
277 kIOUSBDeviceUserClientTypeID,
278 kIOCFPlugInInterfaceID,
279 &plugin,
280 &score);
281 if (kr != KERN_SUCCESS)
282 return false;
283 base::mac::ScopedIOPluginInterface<IOCFPlugInInterface> plugin_ref(plugin);
284
285 HRESULT res =
286 (*plugin)->QueryInterface(plugin,
287 CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID320),
288 (LPVOID *)&device_);
289 if (!SUCCEEDED(res) || !device_)
290 return false;
291
292 UInt16 vendor_id;
293 kr = (*device_)->GetDeviceVendor(device_, &vendor_id);
294 if (kr != KERN_SUCCESS || vendor_id != kVendorMicrosoft)
295 return false;
296
297 UInt16 product_id;
298 kr = (*device_)->GetDeviceProduct(device_, &product_id);
299 if (kr != KERN_SUCCESS)
300 return false;
301
302 IOUSBFindInterfaceRequest request;
303 switch (product_id) {
304 case kProductXbox360Controller:
305 controller_type_ = XBOX_360_CONTROLLER;
306 read_endpoint_ = kXbox360ReadEndpoint;
307 control_endpoint_ = kXbox360ControlEndpoint;
308 request.bInterfaceClass = 255;
309 request.bInterfaceSubClass = 93;
310 request.bInterfaceProtocol = 1;
311 request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
312 break;
313 case kProductXboxOneController:
314 controller_type_ = XBOX_ONE_CONTROLLER;
315 read_endpoint_ = kXboxOneReadEndpoint;
316 control_endpoint_ = kXboxOneControlEndpoint;
317 request.bInterfaceClass = 255;
318 request.bInterfaceSubClass = 71;
319 request.bInterfaceProtocol = 208;
320 request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
321 break;
322 default:
323 return false;
324 }
325
326 // Open the device and configure it.
327 kr = (*device_)->USBDeviceOpen(device_);
328 if (kr != KERN_SUCCESS)
329 return false;
330 device_is_open_ = true;
331
332 // Xbox controllers have one configuration option which has configuration
333 // value 1. Try to set it and fail if it couldn't be configured.
334 IOUSBConfigurationDescriptorPtr config_desc;
335 kr = (*device_)->GetConfigurationDescriptorPtr(device_, 0, &config_desc);
336 if (kr != KERN_SUCCESS)
337 return false;
338 kr = (*device_)->SetConfiguration(device_, config_desc->bConfigurationValue);
339 if (kr != KERN_SUCCESS)
340 return false;
341
342 // The device has 4 interfaces. They are as follows:
343 // Protocol 1:
344 // - Endpoint 1 (in) : Controller events, including button presses.
345 // - Endpoint 2 (out): Rumble pack and LED control
346 // Protocol 2 has a single endpoint to read from a connected ChatPad device.
347 // Protocol 3 is used by a connected headset device.
348 // The device also has an interface on subclass 253, protocol 10 with no
349 // endpoints. It is unused.
350 //
351 // We don't currently support the ChatPad or headset, so protocol 1 is the
352 // only protocol we care about.
353 //
354 // For more detail, see
355 // https://github.com/Grumbel/xboxdrv/blob/master/PROTOCOL
356 io_iterator_t iter;
357 kr = (*device_)->CreateInterfaceIterator(device_, &request, &iter);
358 if (kr != KERN_SUCCESS)
359 return false;
360 base::mac::ScopedIOObject<io_iterator_t> iter_ref(iter);
361
362 // There should be exactly one USB interface which matches the requested
363 // settings.
364 io_service_t usb_interface = IOIteratorNext(iter);
365 if (!usb_interface)
366 return false;
367
368 // We need to make an InterfaceInterface to communicate with the device
369 // endpoint. This is the same process as earlier: first make a
370 // PluginInterface from the io_service then make the InterfaceInterface from
371 // that.
372 IOCFPlugInInterface **plugin_interface;
373 kr = IOCreatePlugInInterfaceForService(usb_interface,
374 kIOUSBInterfaceUserClientTypeID,
375 kIOCFPlugInInterfaceID,
376 &plugin_interface,
377 &score);
378 if (kr != KERN_SUCCESS || !plugin_interface)
379 return false;
380 base::mac::ScopedIOPluginInterface<IOCFPlugInInterface> interface_ref(
381 plugin_interface);
382
383 // Release the USB interface, and any subsequent interfaces returned by the
384 // iterator. (There shouldn't be any, but in case a future device does
385 // contain more interfaces, this will serve to avoid memory leaks.)
386 do {
387 IOObjectRelease(usb_interface);
388 } while ((usb_interface = IOIteratorNext(iter)));
389
390 // Actually create the interface.
391 res = (*plugin_interface)->QueryInterface(
392 plugin_interface,
393 CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID300),
394 (LPVOID *)&interface_);
395
396 if (!SUCCEEDED(res) || !interface_)
397 return false;
398
399 // Actually open the interface.
400 kr = (*interface_)->USBInterfaceOpen(interface_);
401 if (kr != KERN_SUCCESS)
402 return false;
403 interface_is_open_ = true;
404
405 CFRunLoopSourceRef source_ref;
406 kr = (*interface_)->CreateInterfaceAsyncEventSource(interface_, &source_ref);
407 if (kr != KERN_SUCCESS || !source_ref)
408 return false;
409 source_.reset(source_ref);
410 CFRunLoopAddSource(CFRunLoopGetCurrent(), source_, kCFRunLoopDefaultMode);
411
412 // The interface should have two pipes. Pipe 1 with direction kUSBIn and pipe
413 // 2 with direction kUSBOut. Both pipes should have type kUSBInterrupt.
414 uint8_t num_endpoints;
415 kr = (*interface_)->GetNumEndpoints(interface_, &num_endpoints);
416 if (kr != KERN_SUCCESS || num_endpoints < 2)
417 return false;
418
419 for (int i = 1; i <= 2; i++) {
420 uint8_t direction;
421 uint8_t number;
422 uint8_t transfer_type;
423 uint16_t max_packet_size;
424 uint8_t interval;
425
426 kr = (*interface_)->GetPipeProperties(interface_,
427 i,
428 &direction,
429 &number,
430 &transfer_type,
431 &max_packet_size,
432 &interval);
433 if (kr != KERN_SUCCESS || transfer_type != kUSBInterrupt) {
434 return false;
435 }
436 if (i == read_endpoint_) {
437 if (direction != kUSBIn)
438 return false;
439 read_buffer_.reset(new uint8_t[max_packet_size]);
440 read_buffer_size_ = max_packet_size;
441 QueueRead();
442 } else if (i == control_endpoint_) {
443 if (direction != kUSBOut)
444 return false;
445 if (controller_type_ == XBOX_ONE_CONTROLLER)
446 WriteXboxOneInit();
447 }
448 }
449
450 // The location ID is unique per controller, and can be used to track
451 // controllers through reconnections (though if a controller is detached from
452 // one USB hub and attached to another, the location ID will change).
453 kr = (*device_)->GetLocationID(device_, &location_id_);
454 if (kr != KERN_SUCCESS)
455 return false;
456
457 return true;
458 }
459
460 void XboxController::SetLEDPattern(LEDPattern pattern) {
461 led_pattern_ = pattern;
462 const UInt8 length = 3;
463
464 // This buffer will be released in WriteComplete when WritePipeAsync
465 // finishes.
466 UInt8* buffer = new UInt8[length];
467 buffer[0] = static_cast<UInt8>(CONTROL_MESSAGE_SET_LED);
468 buffer[1] = length;
469 buffer[2] = static_cast<UInt8>(pattern);
470 kern_return_t kr = (*interface_)->WritePipeAsync(interface_,
471 control_endpoint_,
472 buffer,
473 (UInt32)length,
474 WriteComplete,
475 buffer);
476 if (kr != KERN_SUCCESS) {
477 delete[] buffer;
478 IOError();
479 return;
480 }
481 }
482
483 int XboxController::GetVendorId() const {
484 return kVendorMicrosoft;
485 }
486
487 int XboxController::GetProductId() const {
488 if (controller_type_ == XBOX_360_CONTROLLER)
489 return kProductXbox360Controller;
490 else
491 return kProductXboxOneController;
492 }
493
494 XboxController::ControllerType XboxController::GetControllerType() const {
495 return controller_type_;
496 }
497
498 void XboxController::WriteComplete(void* context, IOReturn result, void* arg0) {
499 UInt8* buffer = static_cast<UInt8*>(context);
500 delete[] buffer;
501
502 // Ignoring any errors sending data, because they will usually only occur
503 // when the device is disconnected, in which case it really doesn't matter if
504 // the data got to the controller or not.
505 if (result != kIOReturnSuccess)
506 return;
507 }
508
509 void XboxController::GotData(void* context, IOReturn result, void* arg0) {
510 size_t bytes_read = reinterpret_cast<size_t>(arg0);
511 XboxController* controller = static_cast<XboxController*>(context);
512
513 if (result != kIOReturnSuccess) {
514 // This will happen if the device was disconnected. The gamepad has
515 // probably been destroyed by a meteorite.
516 controller->IOError();
517 return;
518 }
519
520 if (controller->GetControllerType() == XBOX_360_CONTROLLER)
521 controller->ProcessXbox360Packet(bytes_read);
522 else
523 controller->ProcessXboxOnePacket(bytes_read);
524
525 // Queue up another read.
526 controller->QueueRead();
527 }
528
529 void XboxController::ProcessXbox360Packet(size_t length) {
530 if (length < 2)
531 return;
532 DCHECK(length <= read_buffer_size_);
533 if (length > read_buffer_size_) {
534 IOError();
535 return;
536 }
537 uint8_t* buffer = read_buffer_.get();
538
539 if (buffer[1] != length)
540 // Length in packet doesn't match length reported by USB.
541 return;
542
543 uint8_t type = buffer[0];
544 buffer += 2;
545 length -= 2;
546 switch (type) {
547 case STATUS_MESSAGE_BUTTONS: {
548 if (length != sizeof(Xbox360ButtonData))
549 return;
550 Xbox360ButtonData* data = reinterpret_cast<Xbox360ButtonData*>(buffer);
551 Data normalized_data;
552 NormalizeXbox360ButtonData(*data, &normalized_data);
553 delegate_->XboxControllerGotData(this, normalized_data);
554 break;
555 }
556 case STATUS_MESSAGE_LED:
557 if (length != 3)
558 return;
559 // The controller sends one of these messages every time the LED pattern
560 // is set, as well as once when it is plugged in.
561 if (led_pattern_ == LED_NUM_PATTERNS && buffer[0] < LED_NUM_PATTERNS)
562 led_pattern_ = static_cast<LEDPattern>(buffer[0]);
563 break;
564 default:
565 // Unknown packet: ignore!
566 break;
567 }
568 }
569
570 void XboxController::ProcessXboxOnePacket(size_t length) {
571 if (length < 2)
572 return;
573 DCHECK(length <= read_buffer_size_);
574 if (length > read_buffer_size_) {
575 IOError();
576 return;
577 }
578 uint8_t* buffer = read_buffer_.get();
579
580 uint8_t type = buffer[0];
581 buffer += 4;
582 length -= 4;
583 switch (type) {
584 case XBOX_ONE_STATUS_MESSAGE_BUTTONS: {
585 if (length != sizeof(XboxOneButtonData))
586 return;
587 XboxOneButtonData* data = reinterpret_cast<XboxOneButtonData*>(buffer);
588 Data normalized_data;
589 NormalizeXboxOneButtonData(*data, &normalized_data);
590 delegate_->XboxControllerGotData(this, normalized_data);
591 break;
592 }
593 default:
594 // Unknown packet: ignore!
595 break;
596 }
597 }
598
599 void XboxController::QueueRead() {
600 kern_return_t kr = (*interface_)->ReadPipeAsync(interface_,
601 read_endpoint_,
602 read_buffer_.get(),
603 read_buffer_size_,
604 GotData,
605 this);
606 if (kr != KERN_SUCCESS)
607 IOError();
608 }
609
610 void XboxController::IOError() {
611 delegate_->XboxControllerError(this);
612 }
613
614 void XboxController::WriteXboxOneInit() {
615 const UInt8 length = 2;
616
617 // This buffer will be released in WriteComplete when WritePipeAsync
618 // finishes.
619 UInt8* buffer = new UInt8[length];
620 buffer[0] = 0x05;
621 buffer[1] = 0x20;
622 kern_return_t kr = (*interface_)->WritePipeAsync(interface_,
623 control_endpoint_,
624 buffer,
625 (UInt32)length,
626 WriteComplete,
627 buffer);
628 if (kr != KERN_SUCCESS) {
629 delete[] buffer;
630 IOError();
631 return;
632 }
633 }
634
635 //-----------------------------------------------------------------------------
636
637 XboxDataFetcher::XboxDataFetcher()
638 : listening_(false),
639 source_(NULL),
640 port_(NULL) {
641 }
642
643 XboxDataFetcher::~XboxDataFetcher() {
644 while (!controllers_.empty()) {
645 RemoveController(*controllers_.begin());
646 }
647 UnregisterFromNotifications();
648 }
649
650 void XboxDataFetcher::GetGamepadData(bool devices_changed_hint) {
651 // This just loops through all the connected pads and "pings" them to indicate
652 // that they're still active.
653 for (const auto& controller : controllers_) {
654 provider()->GetPadState(GAMEPAD_SOURCE_MAC_XBOX, controller->location_id());
655 }
656 }
657
658 void XboxDataFetcher::OnAddedToProvider() {
659 RegisterForNotifications();
660 }
661
662 void XboxDataFetcher::DeviceAdded(void* context, io_iterator_t iterator) {
663 DCHECK(context);
664 XboxDataFetcher* fetcher = static_cast<XboxDataFetcher*>(context);
665 io_service_t ref;
666 while ((ref = IOIteratorNext(iterator))) {
667 base::mac::ScopedIOObject<io_service_t> scoped_ref(ref);
668 XboxController* controller = new XboxController(fetcher);
669 if (controller->OpenDevice(ref)) {
670 fetcher->AddController(controller);
671 } else {
672 delete controller;
673 }
674 }
675 }
676
677 void XboxDataFetcher::DeviceRemoved(void* context, io_iterator_t iterator) {
678 DCHECK(context);
679 XboxDataFetcher* fetcher = static_cast<XboxDataFetcher*>(context);
680 io_service_t ref;
681 while ((ref = IOIteratorNext(iterator))) {
682 base::mac::ScopedIOObject<io_service_t> scoped_ref(ref);
683 base::ScopedCFTypeRef<CFNumberRef> number(
684 base::mac::CFCastStrict<CFNumberRef>(
685 IORegistryEntryCreateCFProperty(ref,
686 CFSTR(kUSBDevicePropertyLocationID),
687 kCFAllocatorDefault,
688 kNilOptions)));
689 UInt32 location_id = 0;
690 CFNumberGetValue(number, kCFNumberSInt32Type, &location_id);
691 fetcher->RemoveControllerByLocationID(location_id);
692 }
693 }
694
695 bool XboxDataFetcher::RegisterForNotifications() {
696 if (listening_)
697 return true;
698 port_ = IONotificationPortCreate(kIOMasterPortDefault);
699 if (!port_)
700 return false;
701 source_ = IONotificationPortGetRunLoopSource(port_);
702 if (!source_)
703 return false;
704 CFRunLoopAddSource(CFRunLoopGetCurrent(), source_, kCFRunLoopDefaultMode);
705
706 listening_ = true;
707
708 if (!RegisterForDeviceNotifications(
709 kVendorMicrosoft, kProductXboxOneController,
710 &xbox_one_device_added_iter_,
711 &xbox_one_device_removed_iter_))
712 return false;
713
714 if (!RegisterForDeviceNotifications(
715 kVendorMicrosoft, kProductXbox360Controller,
716 &xbox_360_device_added_iter_,
717 &xbox_360_device_removed_iter_))
718 return false;
719
720 return true;
721 }
722
723 bool XboxDataFetcher::RegisterForDeviceNotifications(
724 int vendor_id,
725 int product_id,
726 base::mac::ScopedIOObject<io_iterator_t>* added_iter,
727 base::mac::ScopedIOObject<io_iterator_t>* removed_iter) {
728 base::ScopedCFTypeRef<CFNumberRef> vendor_cf(CFNumberCreate(
729 kCFAllocatorDefault, kCFNumberSInt32Type, &vendor_id));
730 base::ScopedCFTypeRef<CFNumberRef> product_cf(CFNumberCreate(
731 kCFAllocatorDefault, kCFNumberSInt32Type, &product_id));
732 base::ScopedCFTypeRef<CFMutableDictionaryRef> matching_dict(
733 IOServiceMatching(kIOUSBDeviceClassName));
734 if (!matching_dict)
735 return false;
736 CFDictionarySetValue(matching_dict, CFSTR(kUSBVendorID), vendor_cf);
737 CFDictionarySetValue(matching_dict, CFSTR(kUSBProductID), product_cf);
738
739 // IOServiceAddMatchingNotification() releases the dictionary when it's done.
740 // Retain it before each call to IOServiceAddMatchingNotification to keep
741 // things balanced.
742 CFRetain(matching_dict);
743 io_iterator_t device_added_iter;
744 IOReturn ret;
745 ret = IOServiceAddMatchingNotification(port_,
746 kIOFirstMatchNotification,
747 matching_dict,
748 DeviceAdded,
749 this,
750 &device_added_iter);
751 added_iter->reset(device_added_iter);
752 if (ret != kIOReturnSuccess) {
753 LOG(ERROR) << "Error listening for Xbox controller add events: " << ret;
754 return false;
755 }
756 DeviceAdded(this, added_iter->get());
757
758 CFRetain(matching_dict);
759 io_iterator_t device_removed_iter;
760 ret = IOServiceAddMatchingNotification(port_,
761 kIOTerminatedNotification,
762 matching_dict,
763 DeviceRemoved,
764 this,
765 &device_removed_iter);
766 removed_iter->reset(device_removed_iter);
767 if (ret != kIOReturnSuccess) {
768 LOG(ERROR) << "Error listening for Xbox controller remove events: " << ret;
769 return false;
770 }
771 DeviceRemoved(this, removed_iter->get());
772 return true;
773 }
774
775 void XboxDataFetcher::UnregisterFromNotifications() {
776 if (!listening_)
777 return;
778 listening_ = false;
779 if (source_)
780 CFRunLoopSourceInvalidate(source_);
781 if (port_)
782 IONotificationPortDestroy(port_);
783 port_ = NULL;
784 }
785
786 XboxController* XboxDataFetcher::ControllerForLocation(UInt32 location_id) {
787 for (std::set<XboxController*>::iterator i = controllers_.begin();
788 i != controllers_.end();
789 ++i) {
790 if ((*i)->location_id() == location_id)
791 return *i;
792 }
793 return NULL;
794 }
795
796 void XboxDataFetcher::AddController(XboxController* controller) {
797 DCHECK(!ControllerForLocation(controller->location_id()))
798 << "Controller with location ID " << controller->location_id()
799 << " already exists in the set of controllers.";
800
801 PadState* state = provider()->GetPadState(GAMEPAD_SOURCE_MAC_XBOX,
802 controller->location_id());
803 if (!state) {
804 delete controller;
805 return; // No available slot for this device
806 }
807
808 controllers_.insert(controller);
809
810 controller->SetLEDPattern(
811 (XboxController::LEDPattern)(XboxController::LED_FLASH_TOP_LEFT +
812 controller->location_id()));
813
814 NSString* ident = [NSString stringWithFormat:
815 @"%@ (STANDARD GAMEPAD Vendor: %04x Product: %04x)",
816 controller->GetControllerType() == XboxController::XBOX_360_CONTROLLER
817 ? @"Xbox 360 Controller"
818 : @"Xbox One Controller",
819 controller->GetProductId(), controller->GetVendorId()];
820 CopyNSStringAsUTF16LittleEndian(
821 ident,
822 state->data.id,
823 sizeof(state->data.id));
824
825 CopyNSStringAsUTF16LittleEndian(
826 @"standard",
827 state->data.mapping,
828 sizeof(state->data.mapping));
829
830 state->data.connected = true;
831 state->data.axesLength = 4;
832 state->data.buttonsLength = 17;
833 state->data.timestamp = 0;
834 state->mapper = 0;
835 state->axis_mask = 0;
836 state->button_mask = 0;
837 }
838
839 void XboxDataFetcher::RemoveController(XboxController* controller) {
840 controllers_.erase(controller);
841 delete controller;
842 }
843
844 void XboxDataFetcher::RemoveControllerByLocationID(uint32_t location_id) {
845 XboxController* controller = NULL;
846 for (std::set<XboxController*>::iterator i = controllers_.begin();
847 i != controllers_.end();
848 ++i) {
849 if ((*i)->location_id() == location_id) {
850 controller = *i;
851 break;
852 }
853 }
854 if (controller)
855 RemoveController(controller);
856 }
857
858 void XboxDataFetcher::XboxControllerGotData(XboxController* controller,
859 const XboxController::Data& data) {
860 PadState* state = provider()->GetPadState(GAMEPAD_SOURCE_MAC_XBOX,
861 controller->location_id());
862 if (!state)
863 return; // No available slot for this device
864
865 WebGamepad& pad = state->data;
866
867 for (size_t i = 0; i < 6; i++) {
868 pad.buttons[i].pressed = data.buttons[i];
869 pad.buttons[i].value = data.buttons[i] ? 1.0f : 0.0f;
870 }
871 pad.buttons[6].pressed = data.triggers[0] > kDefaultButtonPressedThreshold;
872 pad.buttons[6].value = data.triggers[0];
873 pad.buttons[7].pressed = data.triggers[1] > kDefaultButtonPressedThreshold;
874 pad.buttons[7].value = data.triggers[1];
875 for (size_t i = 8; i < 17; i++) {
876 pad.buttons[i].pressed = data.buttons[i - 2];
877 pad.buttons[i].value = data.buttons[i - 2] ? 1.0f : 0.0f;
878 }
879 for (size_t i = 0; i < arraysize(data.axes); i++) {
880 pad.axes[i] = data.axes[i];
881 }
882
883 pad.timestamp = base::TimeTicks::Now().ToInternalValue();
884 }
885
886 void XboxDataFetcher::XboxControllerError(XboxController* controller) {
887 RemoveController(controller);
888 }
889
890 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/gamepad/xbox_data_fetcher_mac.cc ('k') | content/content_browser.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698