Преглед изворни кода

input_common: Add camera driver

german77 пре 4 година
родитељ
комит
f19e7be6e8

+ 28 - 1
src/common/input.h

@@ -28,7 +28,7 @@ enum class InputType {
     Color,
     Vibration,
     Nfc,
-    Ir,
+    IrSensor,
 };
 
 // Internal battery charge level
@@ -53,6 +53,15 @@ enum class PollingMode {
     IR,
 };
 
+enum class CameraFormat {
+    Size320x240,
+    Size160x120,
+    Size80x60,
+    Size40x30,
+    Size20x15,
+    None,
+};
+
 // Vibration reply from the controller
 enum class VibrationError {
     None,
@@ -68,6 +77,13 @@ enum class PollingError {
     Unknown,
 };
 
+// Ir camera reply from the controller
+enum class CameraError {
+    None,
+    NotSupported,
+    Unknown,
+};
+
 // Hint for amplification curve to be used
 enum class VibrationAmplificationType {
     Linear,
@@ -176,6 +192,12 @@ struct LedStatus {
     bool led_4{};
 };
 
+// Raw data fom camera
+struct CameraStatus {
+    CameraFormat format{CameraFormat::None};
+    std::vector<u8> data{};
+};
+
 // List of buttons to be passed to Qt that can be translated
 enum class ButtonNames {
     Undefined,
@@ -233,6 +255,7 @@ struct CallbackStatus {
     BodyColorStatus color_status{};
     BatteryStatus battery_status{};
     VibrationStatus vibration_status{};
+    CameraStatus camera_status{};
 };
 
 // Triggered once every input change
@@ -281,6 +304,10 @@ public:
     virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) {
         return PollingError::NotSupported;
     }
+
+    virtual CameraError SetCameraFormat([[maybe_unused]] CameraFormat camera_format) {
+        return CameraError::NotSupported;
+    }
 };
 
 /// An abstract class template for a factory that can create input devices.

+ 3 - 0
src/common/settings.h

@@ -503,6 +503,9 @@ struct Values {
     Setting<bool> enable_ring_controller{true, "enable_ring_controller"};
     RingconRaw ringcon_analogs;
 
+    BasicSetting<bool> enable_ir_sensor{false, "enable_ir_sensor"};
+    BasicSetting<std::string> ir_sensor_device{"auto", "ir_sensor_device"};
+
     // Data Storage
     Setting<bool> use_virtual_sd{true, "use_virtual_sd"};
     Setting<bool> gamecard_inserted{false, "gamecard_inserted"};

+ 2 - 0
src/input_common/CMakeLists.txt

@@ -1,4 +1,6 @@
 add_library(input_common STATIC
+    drivers/camera.cpp
+    drivers/camera.h
     drivers/gc_adapter.cpp
     drivers/gc_adapter.h
     drivers/keyboard.cpp

+ 82 - 0
src/input_common/drivers/camera.cpp

@@ -0,0 +1,82 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <fmt/format.h>
+
+#include "common/param_package.h"
+#include "input_common/drivers/camera.h"
+
+namespace InputCommon {
+constexpr PadIdentifier identifier = {
+    .guid = Common::UUID{},
+    .port = 0,
+    .pad = 0,
+};
+
+Camera::Camera(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
+    PreSetController(identifier);
+}
+
+void Camera::SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data) {
+    const std::size_t desired_width = getImageWidth();
+    const std::size_t desired_height = getImageHeight();
+    status.data.resize(desired_width * desired_height);
+
+    // Resize image to desired format
+    for (std::size_t y = 0; y < desired_height; y++) {
+        for (std::size_t x = 0; x < desired_width; x++) {
+            const std::size_t pixel_index = y * desired_width + x;
+            const std::size_t old_x = width * x / desired_width;
+            const std::size_t old_y = height * y / desired_height;
+            const std::size_t data_pixel_index = old_y * width + old_x;
+            status.data[pixel_index] = static_cast<u8>(data[data_pixel_index] & 0xFF);
+        }
+    }
+
+    SetCamera(identifier, status);
+}
+
+std::size_t Camera::getImageWidth() const {
+    switch (status.format) {
+    case Common::Input::CameraFormat::Size320x240:
+        return 320;
+    case Common::Input::CameraFormat::Size160x120:
+        return 160;
+    case Common::Input::CameraFormat::Size80x60:
+        return 80;
+    case Common::Input::CameraFormat::Size40x30:
+        return 40;
+    case Common::Input::CameraFormat::Size20x15:
+        return 20;
+    case Common::Input::CameraFormat::None:
+    default:
+        return 0;
+    }
+}
+
+std::size_t Camera::getImageHeight() const {
+    switch (status.format) {
+    case Common::Input::CameraFormat::Size320x240:
+        return 240;
+    case Common::Input::CameraFormat::Size160x120:
+        return 120;
+    case Common::Input::CameraFormat::Size80x60:
+        return 60;
+    case Common::Input::CameraFormat::Size40x30:
+        return 30;
+    case Common::Input::CameraFormat::Size20x15:
+        return 15;
+    case Common::Input::CameraFormat::None:
+    default:
+        return 0;
+    }
+}
+
+Common::Input::CameraError Camera::SetCameraFormat(
+    [[maybe_unused]] const PadIdentifier& identifier_,
+    const Common::Input::CameraFormat camera_format) {
+    status.format = camera_format;
+    return Common::Input::CameraError::None;
+}
+
+} // namespace InputCommon

+ 29 - 0
src/input_common/drivers/camera.h

@@ -0,0 +1,29 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "input_common/input_engine.h"
+
+namespace InputCommon {
+
+/**
+ * A button device factory representing a keyboard. It receives keyboard events and forward them
+ * to all button devices it created.
+ */
+class Camera final : public InputEngine {
+public:
+    explicit Camera(std::string input_engine_);
+
+    void SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data);
+
+    std::size_t getImageWidth() const;
+    std::size_t getImageHeight() const;
+
+    Common::Input::CameraError SetCameraFormat(const PadIdentifier& identifier_,
+                                               Common::Input::CameraFormat camera_format) override;
+
+    Common::Input::CameraStatus status{};
+};
+
+} // namespace InputCommon

+ 38 - 0
src/input_common/input_engine.cpp

@@ -90,6 +90,18 @@ void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const B
     TriggerOnMotionChange(identifier, motion, value);
 }
 
+void InputEngine::SetCamera(const PadIdentifier& identifier,
+                            const Common::Input::CameraStatus& value) {
+    {
+        std::scoped_lock lock{mutex};
+        ControllerData& controller = controller_list.at(identifier);
+        if (!configuring) {
+            controller.camera = value;
+        }
+    }
+    TriggerOnCameraChange(identifier, value);
+}
+
 bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
     std::scoped_lock lock{mutex};
     const auto controller_iter = controller_list.find(identifier);
@@ -165,6 +177,18 @@ BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion)
     return controller.motions.at(motion);
 }
 
+Common::Input::CameraStatus InputEngine::GetCamera(const PadIdentifier& identifier) const {
+    std::scoped_lock lock{mutex};
+    const auto controller_iter = controller_list.find(identifier);
+    if (controller_iter == controller_list.cend()) {
+        LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
+                  identifier.pad, identifier.port);
+        return {};
+    }
+    const ControllerData& controller = controller_iter->second;
+    return controller.camera;
+}
+
 void InputEngine::ResetButtonState() {
     for (const auto& controller : controller_list) {
         for (const auto& button : controller.second.buttons) {
@@ -317,6 +341,20 @@ void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int mot
     });
 }
 
+void InputEngine::TriggerOnCameraChange(const PadIdentifier& identifier,
+                                        [[maybe_unused]] const Common::Input::CameraStatus& value) {
+    std::scoped_lock lock{mutex_callback};
+    for (const auto& poller_pair : callback_list) {
+        const InputIdentifier& poller = poller_pair.second;
+        if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Camera, 0)) {
+            continue;
+        }
+        if (poller.callback.on_change) {
+            poller.callback.on_change();
+        }
+    }
+}
+
 bool InputEngine::IsInputIdentifierEqual(const InputIdentifier& input_identifier,
                                          const PadIdentifier& identifier, EngineInputType type,
                                          int index) const {

+ 16 - 3
src/input_common/input_engine.h

@@ -36,11 +36,12 @@ struct BasicMotion {
 // Types of input that are stored in the engine
 enum class EngineInputType {
     None,
+    Analog,
+    Battery,
     Button,
+    Camera,
     HatButton,
-    Analog,
     Motion,
-    Battery,
 };
 
 namespace std {
@@ -115,10 +116,17 @@ public:
     // Sets polling mode to a controller
     virtual Common::Input::PollingError SetPollingMode(
         [[maybe_unused]] const PadIdentifier& identifier,
-        [[maybe_unused]] const Common::Input::PollingMode vibration) {
+        [[maybe_unused]] const Common::Input::PollingMode polling_mode) {
         return Common::Input::PollingError::NotSupported;
     }
 
+    // Sets camera format to a controller
+    virtual Common::Input::CameraError SetCameraFormat(
+        [[maybe_unused]] const PadIdentifier& identifier,
+        [[maybe_unused]] Common::Input::CameraFormat camera_format) {
+        return Common::Input::CameraError::NotSupported;
+    }
+
     // Returns the engine name
     [[nodiscard]] const std::string& GetEngineName() const;
 
@@ -174,6 +182,7 @@ public:
     f32 GetAxis(const PadIdentifier& identifier, int axis) const;
     Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const;
     BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const;
+    Common::Input::CameraStatus GetCamera(const PadIdentifier& identifier) const;
 
     int SetCallback(InputIdentifier input_identifier);
     void SetMappingCallback(MappingCallback callback);
@@ -185,6 +194,7 @@ protected:
     void SetAxis(const PadIdentifier& identifier, int axis, f32 value);
     void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
     void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value);
+    void SetCamera(const PadIdentifier& identifier, const Common::Input::CameraStatus& value);
 
     virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const {
         return "Unknown";
@@ -197,6 +207,7 @@ private:
         std::unordered_map<int, float> axes;
         std::unordered_map<int, BasicMotion> motions;
         Common::Input::BatteryLevel battery{};
+        Common::Input::CameraStatus camera{};
     };
 
     void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value);
@@ -205,6 +216,8 @@ private:
     void TriggerOnBatteryChange(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
     void TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
                                const BasicMotion& value);
+    void TriggerOnCameraChange(const PadIdentifier& identifier,
+                               const Common::Input::CameraStatus& value);
 
     bool IsInputIdentifierEqual(const InputIdentifier& input_identifier,
                                 const PadIdentifier& identifier, EngineInputType type,

+ 60 - 0
src/input_common/input_poller.cpp

@@ -664,6 +664,47 @@ private:
     InputEngine* input_engine;
 };
 
+class InputFromCamera final : public Common::Input::InputDevice {
+public:
+    explicit InputFromCamera(PadIdentifier identifier_, InputEngine* input_engine_)
+        : identifier(identifier_), input_engine(input_engine_) {
+        UpdateCallback engine_callback{[this]() { OnChange(); }};
+        const InputIdentifier input_identifier{
+            .identifier = identifier,
+            .type = EngineInputType::Camera,
+            .index = 0,
+            .callback = engine_callback,
+        };
+        callback_key = input_engine->SetCallback(input_identifier);
+    }
+
+    ~InputFromCamera() override {
+        input_engine->DeleteCallback(callback_key);
+    }
+
+    Common::Input::CameraStatus GetStatus() const {
+        return input_engine->GetCamera(identifier);
+    }
+
+    void ForceUpdate() override {
+        OnChange();
+    }
+
+    void OnChange() {
+        const Common::Input::CallbackStatus status{
+            .type = Common::Input::InputType::IrSensor,
+            .camera_status = GetStatus(),
+        };
+
+        TriggerOnChange(status);
+    }
+
+private:
+    const PadIdentifier identifier;
+    int callback_key;
+    InputEngine* input_engine;
+};
+
 class OutputFromIdentifier final : public Common::Input::OutputDevice {
 public:
     explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_)
@@ -682,6 +723,10 @@ public:
         return input_engine->SetPollingMode(identifier, polling_mode);
     }
 
+    Common::Input::CameraError SetCameraFormat(Common::Input::CameraFormat camera_format) override {
+        return input_engine->SetCameraFormat(identifier, camera_format);
+    }
+
 private:
     const PadIdentifier identifier;
     InputEngine* input_engine;
@@ -920,6 +965,18 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateMotionDevice(
                                                  properties_y, properties_z, input_engine.get());
 }
 
+std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateCameraDevice(
+    const Common::ParamPackage& params) {
+    const PadIdentifier identifier = {
+        .guid = Common::UUID{params.Get("guid", "")},
+        .port = static_cast<std::size_t>(params.Get("port", 0)),
+        .pad = static_cast<std::size_t>(params.Get("pad", 0)),
+    };
+
+    input_engine->PreSetController(identifier);
+    return std::make_unique<InputFromCamera>(identifier, input_engine.get());
+}
+
 InputFactory::InputFactory(std::shared_ptr<InputEngine> input_engine_)
     : input_engine(std::move(input_engine_)) {}
 
@@ -928,6 +985,9 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::Create(
     if (params.Has("battery")) {
         return CreateBatteryDevice(params);
     }
+    if (params.Has("camera")) {
+        return CreateCameraDevice(params);
+    }
     if (params.Has("button") && params.Has("axis")) {
         return CreateTriggerDevice(params);
     }

+ 11 - 0
src/input_common/input_poller.h

@@ -211,6 +211,17 @@ private:
      */
     std::unique_ptr<Common::Input::InputDevice> CreateMotionDevice(Common::ParamPackage params);
 
+    /**
+     * Creates a camera device from the parameters given.
+     * @param params contains parameters for creating the device:
+     *               - "guid": text string for identifying controllers
+     *               - "port": port of the connected device
+     *               - "pad": slot of the connected controller
+     * @returns a unique input device with the parameters specified
+     */
+    std::unique_ptr<Common::Input::InputDevice> CreateCameraDevice(
+        const Common::ParamPackage& params);
+
     std::shared_ptr<InputEngine> input_engine;
 };
 } // namespace InputCommon

+ 21 - 0
src/input_common/main.cpp

@@ -5,6 +5,7 @@
 #include <memory>
 #include "common/input.h"
 #include "common/param_package.h"
+#include "input_common/drivers/camera.h"
 #include "input_common/drivers/gc_adapter.h"
 #include "input_common/drivers/keyboard.h"
 #include "input_common/drivers/mouse.h"
@@ -78,6 +79,15 @@ struct InputSubsystem::Impl {
         Common::Input::RegisterFactory<Common::Input::OutputDevice>(tas_input->GetEngineName(),
                                                                     tas_output_factory);
 
+        camera = std::make_shared<Camera>("camera");
+        camera->SetMappingCallback(mapping_callback);
+        camera_input_factory = std::make_shared<InputFactory>(camera);
+        camera_output_factory = std::make_shared<OutputFactory>(camera);
+        Common::Input::RegisterFactory<Common::Input::InputDevice>(camera->GetEngineName(),
+                                                                   camera_input_factory);
+        Common::Input::RegisterFactory<Common::Input::OutputDevice>(camera->GetEngineName(),
+                                                                    camera_output_factory);
+
 #ifdef HAVE_SDL2
         sdl = std::make_shared<SDLDriver>("sdl");
         sdl->SetMappingCallback(mapping_callback);
@@ -317,6 +327,7 @@ struct InputSubsystem::Impl {
     std::shared_ptr<TouchScreen> touch_screen;
     std::shared_ptr<TasInput::Tas> tas_input;
     std::shared_ptr<CemuhookUDP::UDPClient> udp_client;
+    std::shared_ptr<Camera> camera;
 
     std::shared_ptr<InputFactory> keyboard_factory;
     std::shared_ptr<InputFactory> mouse_factory;
@@ -324,12 +335,14 @@ struct InputSubsystem::Impl {
     std::shared_ptr<InputFactory> touch_screen_factory;
     std::shared_ptr<InputFactory> udp_client_input_factory;
     std::shared_ptr<InputFactory> tas_input_factory;
+    std::shared_ptr<InputFactory> camera_input_factory;
 
     std::shared_ptr<OutputFactory> keyboard_output_factory;
     std::shared_ptr<OutputFactory> mouse_output_factory;
     std::shared_ptr<OutputFactory> gcadapter_output_factory;
     std::shared_ptr<OutputFactory> udp_client_output_factory;
     std::shared_ptr<OutputFactory> tas_output_factory;
+    std::shared_ptr<OutputFactory> camera_output_factory;
 
 #ifdef HAVE_SDL2
     std::shared_ptr<SDLDriver> sdl;
@@ -382,6 +395,14 @@ const TasInput::Tas* InputSubsystem::GetTas() const {
     return impl->tas_input.get();
 }
 
+Camera* InputSubsystem::GetCamera() {
+    return impl->camera.get();
+}
+
+const Camera* InputSubsystem::GetCamera() const {
+    return impl->camera.get();
+}
+
 std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
     return impl->GetInputDevices();
 }

+ 8 - 1
src/input_common/main.h

@@ -30,6 +30,7 @@ enum Values : int;
 }
 
 namespace InputCommon {
+class Camera;
 class Keyboard;
 class Mouse;
 class TouchScreen;
@@ -92,9 +93,15 @@ public:
     /// Retrieves the underlying tas input device.
     [[nodiscard]] TasInput::Tas* GetTas();
 
-    /// Retrieves the underlying  tas input  device.
+    /// Retrieves the underlying tas input device.
     [[nodiscard]] const TasInput::Tas* GetTas() const;
 
+    /// Retrieves the underlying camera input device.
+    [[nodiscard]] Camera* GetCamera();
+
+    /// Retrieves the underlying camera input device.
+    [[nodiscard]] const Camera* GetCamera() const;
+
     /**
      * Returns all available input devices that this Factory can create a new device with.
      * Each returned ParamPackage should have a `display` field used for display, a `engine` field