123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380 |
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "device/gamepad/gamepad_platform_data_fetcher_linux.h"
- #include <stddef.h>
- #include <string.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <unistd.h>
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/strings/string_number_conversions.h"
- #include "base/strings/utf_string_conversions.h"
- #include "base/trace_event/trace_event.h"
- #include "device/gamepad/gamepad_blocklist.h"
- #include "device/gamepad/gamepad_id_list.h"
- #include "device/gamepad/gamepad_uma.h"
- #include "device/gamepad/nintendo_controller.h"
- #include "device/udev_linux/scoped_udev.h"
- namespace device {
- GamepadPlatformDataFetcherLinux::Factory::Factory(
- scoped_refptr<base::SequencedTaskRunner> dbus_runner)
- : dbus_runner_(std::move(dbus_runner)) {}
- GamepadPlatformDataFetcherLinux::Factory::~Factory() = default;
- std::unique_ptr<GamepadDataFetcher>
- GamepadPlatformDataFetcherLinux::Factory::CreateDataFetcher() {
- return std::make_unique<GamepadPlatformDataFetcherLinux>(dbus_runner_);
- }
- GamepadSource GamepadPlatformDataFetcherLinux::Factory::source() {
- return GAMEPAD_SOURCE_LINUX_UDEV;
- }
- GamepadSource GamepadPlatformDataFetcherLinux::Factory::static_source() {
- return GAMEPAD_SOURCE_LINUX_UDEV;
- }
- GamepadPlatformDataFetcherLinux::GamepadPlatformDataFetcherLinux(
- scoped_refptr<base::SequencedTaskRunner> dbus_runner)
- : dbus_runner_(dbus_runner) {}
- GamepadPlatformDataFetcherLinux::~GamepadPlatformDataFetcherLinux() {
- for (auto it = devices_.begin(); it != devices_.end(); ++it) {
- GamepadDeviceLinux* device = it->get();
- device->Shutdown();
- }
- }
- GamepadSource GamepadPlatformDataFetcherLinux::source() {
- return Factory::static_source();
- }
- void GamepadPlatformDataFetcherLinux::OnAddedToProvider() {
- std::vector<UdevWatcher::Filter> filters;
- filters.emplace_back(UdevGamepadLinux::kInputSubsystem, "");
- filters.emplace_back(UdevGamepadLinux::kHidrawSubsystem, "");
- udev_watcher_ = UdevWatcher::StartWatching(this, filters);
- for (auto it = devices_.begin(); it != devices_.end(); ++it)
- it->get()->Shutdown();
- devices_.clear();
- udev_watcher_->EnumerateExistingDevices();
- }
- void GamepadPlatformDataFetcherLinux::OnDeviceAdded(
- ScopedUdevDevicePtr device) {
- RefreshDevice(device.get());
- }
- void GamepadPlatformDataFetcherLinux::OnDeviceRemoved(
- ScopedUdevDevicePtr device) {
- RefreshDevice(device.get());
- }
- void GamepadPlatformDataFetcherLinux::OnDeviceChanged(
- ScopedUdevDevicePtr device) {
- RefreshDevice(device.get());
- }
- void GamepadPlatformDataFetcherLinux::GetGamepadData(bool) {
- TRACE_EVENT0("GAMEPAD", "GetGamepadData");
- // Update our internal state.
- for (const auto& device : devices_) {
- if (device->GetJoydevIndex() >= 0)
- ReadDeviceData(device->GetJoydevIndex());
- }
- }
- // Used during enumeration, and monitor notifications.
- void GamepadPlatformDataFetcherLinux::RefreshDevice(udev_device* dev) {
- std::unique_ptr<UdevGamepadLinux> udev_gamepad =
- UdevGamepadLinux::Create(dev);
- if (udev_gamepad) {
- const UdevGamepadLinux& pad_info = *udev_gamepad.get();
- if (pad_info.type == UdevGamepadLinux::Type::JOYDEV) {
- // If |syspath_prefix| is empty, the device was already disconnected.
- if (pad_info.syspath_prefix.empty())
- DisconnectUnrecognizedGamepad(pad_info.index);
- else
- RefreshJoydevDevice(dev, pad_info);
- } else if (pad_info.type == UdevGamepadLinux::Type::EVDEV) {
- RefreshEvdevDevice(dev, pad_info);
- } else if (pad_info.type == UdevGamepadLinux::Type::HIDRAW) {
- RefreshHidrawDevice(dev, pad_info);
- }
- }
- }
- GamepadDeviceLinux* GamepadPlatformDataFetcherLinux::GetDeviceWithJoydevIndex(
- int joydev_index) {
- for (auto it = devices_.begin(); it != devices_.end(); ++it) {
- GamepadDeviceLinux* device = it->get();
- if (device->GetJoydevIndex() == joydev_index)
- return device;
- }
- return nullptr;
- }
- void GamepadPlatformDataFetcherLinux::RemoveDevice(GamepadDeviceLinux* device) {
- for (auto it = devices_.begin(); it != devices_.end(); ++it) {
- if (it->get() == device) {
- device->Shutdown();
- devices_.erase(it);
- return;
- }
- }
- }
- bool GamepadPlatformDataFetcherLinux::DisconnectUnrecognizedGamepad(int index) {
- for (auto it = devices_.begin(); it != devices_.end(); ++it) {
- const auto& device = *it;
- if (device->GetJoydevIndex() == index) {
- device->Shutdown();
- devices_.erase(it);
- return true;
- }
- }
- return false;
- }
- GamepadDeviceLinux* GamepadPlatformDataFetcherLinux::GetOrCreateMatchingDevice(
- const UdevGamepadLinux& pad_info) {
- for (auto it = devices_.begin(); it != devices_.end(); ++it) {
- GamepadDeviceLinux* device = it->get();
- if (device->IsSameDevice(pad_info))
- return device;
- }
- auto emplace_result = devices_.emplace(std::make_unique<GamepadDeviceLinux>(
- pad_info.syspath_prefix, dbus_runner_));
- return emplace_result.first->get();
- }
- void GamepadPlatformDataFetcherLinux::RefreshJoydevDevice(
- udev_device* dev,
- const UdevGamepadLinux& pad_info) {
- const int joydev_index = pad_info.index;
- GamepadDeviceLinux* device = GetOrCreateMatchingDevice(pad_info);
- if (device == nullptr)
- return;
- // If the device cannot be opened, the joystick has been disconnected.
- if (!device->OpenJoydevNode(pad_info, dev)) {
- if (device->IsEmpty())
- RemoveDevice(device);
- return;
- }
- std::string name = device->GetName();
- uint16_t vendor_id = device->GetVendorId();
- uint16_t product_id = device->GetProductId();
- // Filter out devices that have gamepad-like HID usages but aren't gamepads.
- if (GamepadIsExcluded(vendor_id, product_id)) {
- device->CloseJoydevNode();
- RemoveDevice(device);
- return;
- }
- const GamepadId gamepad_id =
- GamepadIdList::Get().GetGamepadId(name, vendor_id, product_id);
- if (NintendoController::IsNintendoController(gamepad_id)) {
- // Nintendo devices are handled by the Nintendo data fetcher.
- device->CloseJoydevNode();
- RemoveDevice(device);
- return;
- }
- bool is_recognized = gamepad_id != GamepadId::kUnknownGamepad;
- PadState* state = GetPadState(joydev_index, is_recognized);
- if (!state) {
- // No slot available for device, don't use.
- device->CloseJoydevNode();
- RemoveDevice(device);
- return;
- }
- // The device pointed to by dev contains information about the logical
- // joystick device. In order to get the information about the physical
- // hardware, get the parent device that is also in the "input" subsystem.
- // This function should just walk up the tree one level.
- udev_device* parent_dev =
- device::udev_device_get_parent_with_subsystem_devtype(
- dev, UdevGamepadLinux::kInputSubsystem, nullptr);
- if (!parent_dev) {
- device->CloseJoydevNode();
- if (device->IsEmpty())
- RemoveDevice(device);
- return;
- }
- // If the device is on our list, record it by ID. If the device is unknown,
- // record that an unknown gamepad was enumerated.
- if (is_recognized)
- RecordUnknownGamepad(source());
- else
- RecordConnectedGamepad(gamepad_id);
- state->mapper = device->GetMappingFunction();
- Gamepad& pad = state->data;
- UpdateGamepadStrings(name, vendor_id, product_id, state->mapper != nullptr,
- pad);
- pad.vibration_actuator.type = GamepadHapticActuatorType::kDualRumble;
- pad.vibration_actuator.not_null = device->SupportsVibration();
- pad.connected = true;
- }
- void GamepadPlatformDataFetcherLinux::RefreshEvdevDevice(
- udev_device* dev,
- const UdevGamepadLinux& pad_info) {
- GamepadDeviceLinux* device = GetOrCreateMatchingDevice(pad_info);
- if (device == nullptr)
- return;
- if (!device->OpenEvdevNode(pad_info)) {
- if (device->IsEmpty())
- RemoveDevice(device);
- return;
- }
- int joydev_index = device->GetJoydevIndex();
- if (joydev_index >= 0) {
- PadState* state = GetPadState(joydev_index);
- DCHECK(state);
- if (state) {
- Gamepad& pad = state->data;
- // To select the correct mapper for an arbitrary gamepad we may need info
- // from both the joydev and evdev nodes. For instance, a gamepad that
- // connects over USB and Bluetooth may need to select a mapper based on
- // the connection type, but this information is only available through
- // evdev. To ensure that gamepads are usable when evdev is unavailable, a
- // preliminary mapping is assigned when the joydev node is enumerated.
- //
- // Here we check if associating the evdev node changed the mapping
- // function that should be used for this gamepad. If so, assign the new
- // mapper and rebuild the gamepad strings.
- GamepadStandardMappingFunction mapper = device->GetMappingFunction();
- if (mapper != state->mapper) {
- state->mapper = mapper;
- UpdateGamepadStrings(device->GetName(), device->GetVendorId(),
- device->GetProductId(), mapper != nullptr, pad);
- }
- pad.vibration_actuator.not_null = device->SupportsVibration();
- }
- }
- }
- void GamepadPlatformDataFetcherLinux::RefreshHidrawDevice(
- udev_device* dev,
- const UdevGamepadLinux& pad_info) {
- GamepadDeviceLinux* device = GetOrCreateMatchingDevice(pad_info);
- if (device == nullptr)
- return;
- device->OpenHidrawNode(
- pad_info,
- base::BindOnce(&GamepadPlatformDataFetcherLinux::OnHidrawDeviceOpened,
- weak_factory_.GetWeakPtr()));
- }
- void GamepadPlatformDataFetcherLinux::OnHidrawDeviceOpened(
- GamepadDeviceLinux* device) {
- DCHECK(device);
- if (device->IsEmpty()) {
- RemoveDevice(device);
- return;
- }
- int joydev_index = device->GetJoydevIndex();
- if (joydev_index < 0)
- return;
- PadState* state = GetPadState(joydev_index);
- if (!state)
- return;
- Gamepad& pad = state->data;
- pad.vibration_actuator.type = GamepadHapticActuatorType::kDualRumble;
- pad.vibration_actuator.not_null = device->SupportsVibration();
- }
- void GamepadPlatformDataFetcherLinux::ReadDeviceData(size_t index) {
- GamepadDeviceLinux* device = GetDeviceWithJoydevIndex(index);
- if (!device)
- return;
- PadState* state = GetPadState(index);
- if (!state)
- return;
- device->ReadPadState(&state->data);
- }
- void GamepadPlatformDataFetcherLinux::PlayEffect(
- int pad_id,
- mojom::GamepadHapticEffectType type,
- mojom::GamepadEffectParametersPtr params,
- mojom::GamepadHapticsManager::PlayVibrationEffectOnceCallback callback,
- scoped_refptr<base::SequencedTaskRunner> callback_runner) {
- if (pad_id < 0 || pad_id >= static_cast<int>(Gamepads::kItemsLengthCap)) {
- RunVibrationCallback(
- std::move(callback), std::move(callback_runner),
- mojom::GamepadHapticsResult::GamepadHapticsResultError);
- return;
- }
- GamepadDeviceLinux* device = GetDeviceWithJoydevIndex(pad_id);
- if (!device) {
- RunVibrationCallback(
- std::move(callback), std::move(callback_runner),
- mojom::GamepadHapticsResult::GamepadHapticsResultError);
- return;
- }
- device->PlayEffect(type, std::move(params), std::move(callback),
- std::move(callback_runner));
- }
- void GamepadPlatformDataFetcherLinux::ResetVibration(
- int pad_id,
- mojom::GamepadHapticsManager::ResetVibrationActuatorCallback callback,
- scoped_refptr<base::SequencedTaskRunner> callback_runner) {
- if (pad_id < 0 || pad_id >= static_cast<int>(Gamepads::kItemsLengthCap)) {
- RunVibrationCallback(
- std::move(callback), std::move(callback_runner),
- mojom::GamepadHapticsResult::GamepadHapticsResultError);
- return;
- }
- GamepadDeviceLinux* device = GetDeviceWithJoydevIndex(pad_id);
- if (!device) {
- RunVibrationCallback(
- std::move(callback), std::move(callback_runner),
- mojom::GamepadHapticsResult::GamepadHapticsResultError);
- return;
- }
- device->ResetVibration(std::move(callback), std::move(callback_runner));
- }
- } // namespace device
|