chromium/components/exo/wayland/zcr_gaming_input.cc

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

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "components/exo/wayland/zcr_gaming_input.h"

#include <gaming-input-unstable-v2-server-protocol.h>
#include <wayland-server-core.h>
#include <wayland-server-protocol-core.h>

#include <memory>

#include "base/feature_list.h"
#include "base/memory/raw_ptr.h"
#include "components/exo/gamepad.h"
#include "components/exo/gamepad_delegate.h"
#include "components/exo/gamepad_observer.h"
#include "components/exo/gaming_seat.h"
#include "components/exo/gaming_seat_delegate.h"
#include "components/exo/wayland/server_util.h"
#include "ui/events/devices/gamepad_device.h"

namespace exo {
namespace wayland {

namespace {

unsigned int GetGamepadBusType(ui::InputDeviceType type) {
  switch (type) {
    case ui::INPUT_DEVICE_BLUETOOTH:
      return ZCR_GAMING_SEAT_V2_BUS_TYPE_BLUETOOTH;
    default:
      // Internal and unknown types also default to USB.
      return ZCR_GAMING_SEAT_V2_BUS_TYPE_USB;
  }
}

////////////////////////////////////////////////////////////////////////////////
// gaming_input_interface:

// Handles the vibration requests sent by the client for a gamepad.
class WaylandGamepadVibratorImpl : public GamepadObserver {
 public:
  explicit WaylandGamepadVibratorImpl(Gamepad* gamepad) : gamepad_(gamepad) {
    gamepad_->AddObserver(this);
  }

  WaylandGamepadVibratorImpl(const WaylandGamepadVibratorImpl& other) = delete;
  WaylandGamepadVibratorImpl& operator=(
      const WaylandGamepadVibratorImpl& other) = delete;

  ~WaylandGamepadVibratorImpl() override {
    if (gamepad_)
      gamepad_->RemoveObserver(this);
  }

  void OnVibrate(wl_array* duration_millis,
                 wl_array* amplitudes,
                 int32_t repeat) {
    std::vector<int64_t> extracted_durations;
    int64_t* p;
    const uint8_t* duration_millis_end =
        static_cast<uint8_t*>(duration_millis->data) + duration_millis->size;
    for (p = static_cast<int64_t*>(duration_millis->data);
         (const uint8_t*)p < duration_millis_end; p++) {
      extracted_durations.emplace_back(*p);
    }

    const uint8_t* amplitudes_start = static_cast<uint8_t*>(amplitudes->data);
    size_t amplitude_size = amplitudes->size / sizeof(uint8_t);
    const uint8_t* amplitudes_end = amplitudes_start + amplitude_size;
    std::vector<uint8_t> extracted_amplitudes(amplitudes_start, amplitudes_end);

    if (gamepad_)
      gamepad_->Vibrate(extracted_durations, extracted_amplitudes, repeat);
  }

  void OnCancelVibration() {
    if (gamepad_)
      gamepad_->CancelVibration();
  }

  // Overridden from GamepadObserver
  void OnGamepadDestroying(Gamepad* gamepad) override {
    DCHECK_EQ(gamepad_, gamepad);
    gamepad_ = nullptr;
  }

 private:
  raw_ptr<Gamepad> gamepad_;
};

void gamepad_vibrator_vibrate(wl_client* client,
                              wl_resource* resource,
                              wl_array* duration_millis,
                              wl_array* amplitudes,
                              int32_t repeat) {
  GetUserDataAs<WaylandGamepadVibratorImpl>(resource)->OnVibrate(
      duration_millis, amplitudes, repeat);
}

void gamepad_vibrator_cancel_vibration(wl_client* client,
                                       wl_resource* resource) {
  GetUserDataAs<WaylandGamepadVibratorImpl>(resource)->OnCancelVibration();
}

void gamepad_vibrator_destroy(wl_client* client, wl_resource* resource) {
  wl_resource_destroy(resource);
}

const struct zcr_gamepad_vibrator_v2_interface gamepad_vibrator_implementation =
    {gamepad_vibrator_vibrate, gamepad_vibrator_cancel_vibration,
     gamepad_vibrator_destroy};

// Gamepad delegate class that forwards gamepad events to the client resource.
class WaylandGamepadDelegate : public GamepadDelegate {
 public:
  explicit WaylandGamepadDelegate(wl_resource* gamepad_resource)
      : gamepad_resource_(gamepad_resource) {}

  WaylandGamepadDelegate(const WaylandGamepadDelegate&) = delete;
  WaylandGamepadDelegate& operator=(const WaylandGamepadDelegate&) = delete;

  ~WaylandGamepadDelegate() override = default;

  // If gamepad_resource_ is destroyed first, ResetGamepadResource will
  // be called to remove the resource from delegate, and delegate won't
  // do anything after that. If delegate is destructed first, it will
  // set the data to null in the gamepad_resource_, then the resource
  // destroy won't reset the delegate (cause it's gone).
  static void ResetGamepadResource(wl_resource* resource) {
    WaylandGamepadDelegate* delegate =
        GetUserDataAs<WaylandGamepadDelegate>(resource);
    if (delegate) {
      delegate->gamepad_resource_ = nullptr;
    }
  }

  // Override from GamepadDelegate:
  void OnRemoved() override {
    if (!gamepad_resource_) {
      return;
    }
    zcr_gamepad_v2_send_removed(gamepad_resource_);
    wl_client_flush(client());
    // Reset the user data in gamepad_resource.
    wl_resource_set_user_data(gamepad_resource_, nullptr);
  }
  void OnAxis(int axis, double value, base::TimeTicks time_stamp) override {
    if (!gamepad_resource_) {
      return;
    }
    zcr_gamepad_v2_send_axis(gamepad_resource_,
                             TimeTicksToMilliseconds(time_stamp), axis,
                             wl_fixed_from_double(value));
  }
  void OnButton(int button, bool pressed, base::TimeTicks time_stamp) override {
    if (!gamepad_resource_) {
      return;
    }
    uint32_t state = pressed ? ZCR_GAMEPAD_V2_BUTTON_STATE_PRESSED
                             : ZCR_GAMEPAD_V2_BUTTON_STATE_RELEASED;
    zcr_gamepad_v2_send_button(gamepad_resource_,
                               TimeTicksToMilliseconds(time_stamp), button,
                               state, wl_fixed_from_double(0));
  }
  void OnFrame(base::TimeTicks time_stamp) override {
    if (!gamepad_resource_) {
      return;
    }
    zcr_gamepad_v2_send_frame(gamepad_resource_,
                              TimeTicksToMilliseconds(time_stamp));
    wl_client_flush(client());
  }

  void ConfigureDevice(Gamepad* gamepad) {
    for (const auto& axis : gamepad->device.axes) {
      zcr_gamepad_v2_send_axis_added(gamepad_resource_, axis.code,
                                     axis.min_value, axis.max_value, axis.flat,
                                     axis.fuzz, axis.resolution);
    }

    if (gamepad->device.supports_vibration_rumble &&
        wl_resource_get_version(gamepad_resource_) >=
            ZCR_GAMEPAD_V2_VIBRATOR_ADDED_SINCE_VERSION) {
      wl_resource* gamepad_vibrator_resource =
          wl_resource_create(wl_resource_get_client(gamepad_resource_),
                             &zcr_gamepad_vibrator_v2_interface,
                             wl_resource_get_version(gamepad_resource_), 0);

      SetImplementation(gamepad_vibrator_resource,
                        &gamepad_vibrator_implementation,
                        std::make_unique<WaylandGamepadVibratorImpl>(gamepad));

      zcr_gamepad_v2_send_vibrator_added(gamepad_resource_,
                                         gamepad_vibrator_resource);
    }

    if (wl_resource_get_version(gamepad_resource_) >=
        ZCR_GAMEPAD_V2_SUPPORTED_KEY_BITS_SINCE_VERSION) {
      // Sending key_bits.
      wl_array wl_key_bits;
      wl_array_init(&wl_key_bits);
      std::vector<uint64_t> key_bits =
          ui::OzonePlatform::GetInstance()
              ->GetInputController()
              ->GetGamepadKeyBits(gamepad->device.id);
      size_t key_bits_len = key_bits.size() * sizeof(uint64_t);
      uint64_t* wl_key_bits_ptr =
          static_cast<uint64_t*>(wl_array_add(&wl_key_bits, key_bits_len));
      if (wl_key_bits_ptr) {
        memcpy(wl_key_bits_ptr, key_bits.data(), key_bits_len);
        zcr_gamepad_v2_send_supported_key_bits(gamepad_resource_, &wl_key_bits);
      }
      wl_array_release(&wl_key_bits);
    }

    zcr_gamepad_v2_send_activated(gamepad_resource_);
  }

 private:
  // The client who own this gamepad instance.
  wl_client* client() const {
    return wl_resource_get_client(gamepad_resource_);
  }

  // The gamepad resource associated with the gamepad.
  raw_ptr<wl_resource> gamepad_resource_;
};

void gamepad_destroy(wl_client* client, wl_resource* resource) {
  wl_resource_destroy(resource);
}

const struct zcr_gamepad_v2_interface gamepad_implementation = {
    gamepad_destroy};

// GamingSeat delegate that provide gamepad added.
class WaylandGamingSeatDelegate : public GamingSeatDelegate {
 public:
  explicit WaylandGamingSeatDelegate(wl_resource* gaming_seat_resource)
      : gaming_seat_resource_{gaming_seat_resource} {}

  WaylandGamingSeatDelegate(const WaylandGamingSeatDelegate&) = delete;
  WaylandGamingSeatDelegate& operator=(const WaylandGamingSeatDelegate&) =
      delete;

  // Override from GamingSeatDelegate:
  void OnGamingSeatDestroying(GamingSeat*) override { delete this; }
  bool CanAcceptGamepadEventsForSurface(Surface* surface) const override {
    wl_resource* surface_resource = GetSurfaceResource(surface);
    return surface_resource &&
           wl_resource_get_client(surface_resource) ==
               wl_resource_get_client(gaming_seat_resource_);
  }
  void GamepadAdded(Gamepad& gamepad) override {
    wl_resource* gamepad_resource =
        wl_resource_create(wl_resource_get_client(gaming_seat_resource_),
                           &zcr_gamepad_v2_interface,
                           wl_resource_get_version(gaming_seat_resource_), 0);

    zcr_gaming_seat_v2_send_gamepad_added_with_device_info(
        gaming_seat_resource_, gamepad_resource, gamepad.device.name.c_str(),
        GetGamepadBusType(gamepad.device.type), gamepad.device.vendor_id,
        gamepad.device.product_id, gamepad.device.version);

    std::unique_ptr<WaylandGamepadDelegate> gamepad_delegate =
        std::make_unique<WaylandGamepadDelegate>(gamepad_resource);

    wl_resource_set_implementation(
        gamepad_resource, &gamepad_implementation, gamepad_delegate.get(),
        &WaylandGamepadDelegate::ResetGamepadResource);

    gamepad_delegate->ConfigureDevice(&gamepad);
    gamepad.SetDelegate(std::move(gamepad_delegate));

    wl_client_flush(wl_resource_get_client(gaming_seat_resource_));
  }

 private:
  // The gaming seat resource associated with the gaming seat.
  const raw_ptr<wl_resource> gaming_seat_resource_;
};

void gaming_seat_destroy(wl_client* client, wl_resource* resource) {
  wl_resource_destroy(resource);
}

const struct zcr_gaming_seat_v2_interface gaming_seat_implementation = {
    gaming_seat_destroy};

void gaming_input_get_gaming_seat(wl_client* client,
                                  wl_resource* resource,
                                  uint32_t id,
                                  wl_resource* seat) {
  wl_resource* gaming_seat_resource =
      wl_resource_create(client, &zcr_gaming_seat_v2_interface,
                         wl_resource_get_version(resource), id);

  SetImplementation(gaming_seat_resource, &gaming_seat_implementation,
                    std::make_unique<GamingSeat>(
                        new WaylandGamingSeatDelegate(gaming_seat_resource)));
}

void gaming_input_destroy(wl_client* client, wl_resource* resource) {
  wl_resource_destroy(resource);
}

const struct zcr_gaming_input_v2_interface gaming_input_implementation = {
    gaming_input_get_gaming_seat, gaming_input_destroy};

}  // namespace

void bind_gaming_input(wl_client* client,
                       void* data,
                       uint32_t version,
                       uint32_t id) {
  wl_resource* resource =
      wl_resource_create(client, &zcr_gaming_input_v2_interface, version, id);

  wl_resource_set_implementation(resource, &gaming_input_implementation,
                                 nullptr, nullptr);
}

}  // namespace wayland
}  // namespace exo