chromium/device/gamepad/xbox_data_fetcher_mac.h

// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef DEVICE_GAMEPAD_XBOX_DATA_FETCHER_MAC_H_
#define DEVICE_GAMEPAD_XBOX_DATA_FETCHER_MAC_H_

#include <stdint.h>

#include <set>
#include <vector>

#include <IOKit/IOMessage.h>

#include "base/containers/unique_ptr_adapters.h"
#include "base/mac/scoped_ionotificationportref.h"
#include "base/mac/scoped_ioobject.h"
#include "base/memory/raw_ptr.h"
#include "base/task/sequenced_task_runner.h"
#include "device/gamepad/gamepad_data_fetcher.h"
#include "device/gamepad/xbox_controller_mac.h"

namespace device {

class XboxDataFetcher : public GamepadDataFetcher,
                        public XboxControllerMac::Delegate {
 public:
  using Factory =
      GamepadDataFetcherFactoryImpl<XboxDataFetcher, GamepadSource::kMacXbox>;

  XboxDataFetcher();
  XboxDataFetcher(const XboxDataFetcher& entry) = delete;
  XboxDataFetcher& operator=(const XboxDataFetcher& entry) = delete;
  ~XboxDataFetcher() override;

  GamepadSource source() override;

  // GamepadDataFetcher overrides
  void GetGamepadData(bool devices_changed_hint) override;
  void PlayEffect(int source_id,
                  mojom::GamepadHapticEffectType,
                  mojom::GamepadEffectParametersPtr,
                  mojom::GamepadHapticsManager::PlayVibrationEffectOnceCallback,
                  scoped_refptr<base::SequencedTaskRunner>) override;
  void ResetVibration(
      int source_id,
      mojom::GamepadHapticsManager::ResetVibrationActuatorCallback,
      scoped_refptr<base::SequencedTaskRunner>) override;

  XboxControllerMac* ControllerForLocation(UInt32 location_id);

 private:
  struct PendingController {
   public:
    PendingController(XboxDataFetcher*, std::unique_ptr<XboxControllerMac>);
    PendingController(const PendingController& entry);
    PendingController& operator=(const PendingController& entry);
    ~PendingController();

    raw_ptr<XboxDataFetcher> fetcher;
    std::unique_ptr<XboxControllerMac> controller;
    base::mac::ScopedIOObject<io_iterator_t> notify;
  };

  static void DeviceAdded(void* context, io_iterator_t iterator);
  static void DeviceRemoved(void* context, io_iterator_t iterator);
  static void InterestCallback(void* context,
                               io_service_t service,
                               IOMessage message_type,
                               void* message_argument);

  bool TryOpenDevice(io_service_t iterator);
  bool RegisterForNotifications();
  bool RegisterForDeviceNotifications(int vendor_id, int product_id);
  bool RegisterForInterestNotifications(io_service_t service,
                                        PendingController* pending);
  void PendingControllerBecameAvailable(io_service_t service,
                                        PendingController* pending);
  void UnregisterFromNotifications();

  void OnAddedToProvider() override;
  void AddController(XboxControllerMac* controller);
  void RemoveController(XboxControllerMac* controller);
  void RemoveControllerByLocationID(uint32_t id);

  // XboxControllerMac::Delegate methods.
  void XboxControllerGotData(XboxControllerMac* controller,
                             const XboxControllerMac::Data& data) override;
  void XboxControllerGotGuideData(XboxControllerMac* controller,
                                  bool guide) override;
  void XboxControllerError(XboxControllerMac* controller) override;

  // The set of connected controllers.
  std::set<XboxControllerMac*> controllers_;

  // The set of enumerated controllers that received an exclusive access error
  // on opening the device. The data fetcher is notified when these devices
  // become available so we can try opening them again.
  std::set<std::unique_ptr<PendingController>, base::UniquePtrComparator>
      pending_controllers_;

  bool listening_ = false;

  // port_ owns source_, so this doesn't need to be a ScopedCFTypeRef, but we
  // do need to maintain a reference to it so we can invalidate it.
  CFRunLoopSourceRef source_ = nullptr;
  base::mac::ScopedIONotificationPortRef port_;

  // Iterators returned by calls to IOServiceAddMatchingNotification for
  // kIOFirstMatchNotification (connection) and kIOTerminatedNotification
  // (disconnection) events. These iterators are not referenced directly but
  // must be kept alive in order to continue to receive notifications.
  std::vector<base::mac::ScopedIOObject<io_iterator_t>> device_event_iterators_;
};

}  // namespace device

#endif  // DEVICE_GAMEPAD_XBOX_DATA_FETCHER_MAC_H_