Merge pull request #8484 from german77/irs_release
service: irs: Add camera support, split processors and implement ImageTransferProcessor
This commit is contained in:
		| @@ -196,7 +196,7 @@ if(ENABLE_QT) | ||||
|     # Check for system Qt on Linux, fallback to bundled Qt | ||||
|     if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") | ||||
|         if (NOT YUZU_USE_BUNDLED_QT) | ||||
|             find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets DBus) | ||||
|             find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets DBus Multimedia) | ||||
|         endif() | ||||
|         if (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT) | ||||
|             # Check for dependencies, then enable bundled Qt download | ||||
| @@ -300,9 +300,9 @@ if(ENABLE_QT) | ||||
|         set(YUZU_QT_NO_CMAKE_SYSTEM_PATH "NO_CMAKE_SYSTEM_PATH") | ||||
|     endif() | ||||
|     if ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux") AND YUZU_USE_BUNDLED_QT) | ||||
|         find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent DBus ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH}) | ||||
|         find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent Multimedia DBus ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH}) | ||||
|     else() | ||||
|         find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH}) | ||||
|         find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent Multimedia ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH}) | ||||
|     endif() | ||||
|     if (YUZU_USE_QT_WEB_ENGINE) | ||||
|         find_package(Qt5 REQUIRED COMPONENTS WebEngineCore WebEngineWidgets) | ||||
|   | ||||
| @@ -10,11 +10,13 @@ function(copy_yuzu_Qt5_deps target_dir) | ||||
|     set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/") | ||||
|     set(Qt5_PLATFORMTHEMES_DIR "${Qt5_DIR}/../../../plugins/platformthemes/") | ||||
|     set(Qt5_PLATFORMINPUTCONTEXTS_DIR "${Qt5_DIR}/../../../plugins/platforminputcontexts/") | ||||
|     set(Qt5_MEDIASERVICE_DIR "${Qt5_DIR}/../../../plugins/mediaservice/") | ||||
|     set(Qt5_XCBGLINTEGRATIONS_DIR "${Qt5_DIR}/../../../plugins/xcbglintegrations/") | ||||
|     set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/") | ||||
|     set(Qt5_IMAGEFORMATS_DIR "${Qt5_DIR}/../../../plugins/imageformats/") | ||||
|     set(Qt5_RESOURCES_DIR "${Qt5_DIR}/../../../resources/") | ||||
|     set(PLATFORMS ${DLL_DEST}plugins/platforms/) | ||||
|     set(MEDIASERVICE ${DLL_DEST}mediaservice/) | ||||
|     set(STYLES ${DLL_DEST}plugins/styles/) | ||||
|     set(IMAGEFORMATS ${DLL_DEST}plugins/imageformats/) | ||||
|     if (MSVC) | ||||
| @@ -22,6 +24,8 @@ function(copy_yuzu_Qt5_deps target_dir) | ||||
|             Qt5Core$<$<CONFIG:Debug>:d>.* | ||||
|             Qt5Gui$<$<CONFIG:Debug>:d>.* | ||||
|             Qt5Widgets$<$<CONFIG:Debug>:d>.* | ||||
|             Qt5Multimedia$<$<CONFIG:Debug>:d>.* | ||||
|             Qt5Network$<$<CONFIG:Debug>:d>.* | ||||
|         ) | ||||
|  | ||||
|         if (YUZU_USE_QT_WEB_ENGINE) | ||||
| @@ -53,6 +57,10 @@ function(copy_yuzu_Qt5_deps target_dir) | ||||
|             qjpeg$<$<CONFIG:Debug>:d>.* | ||||
|             qgif$<$<CONFIG:Debug>:d>.* | ||||
|         ) | ||||
|         windows_copy_files(yuzu ${Qt5_MEDIASERVICE_DIR} ${MEDIASERVICE} | ||||
|             dsengine$<$<CONFIG:Debug>:d>.* | ||||
|             wmfengine$<$<CONFIG:Debug>:d>.* | ||||
|         ) | ||||
|     else() | ||||
|         set(Qt5_DLLS | ||||
|             "${Qt5_DLL_DIR}libQt5Core.so.5" | ||||
|   | ||||
| @@ -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. | ||||
|   | ||||
| @@ -503,6 +503,9 @@ struct Values { | ||||
|     Setting<bool> enable_ring_controller{true, "enable_ring_controller"}; | ||||
|     RingconRaw ringcon_analogs; | ||||
|  | ||||
|     Setting<bool> enable_ir_sensor{false, "enable_ir_sensor"}; | ||||
|     Setting<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"}; | ||||
|   | ||||
| @@ -158,6 +158,7 @@ add_library(core STATIC | ||||
|     hid/input_converter.h | ||||
|     hid/input_interpreter.cpp | ||||
|     hid/input_interpreter.h | ||||
|     hid/irs_types.h | ||||
|     hid/motion_input.cpp | ||||
|     hid/motion_input.h | ||||
|     hle/api_version.h | ||||
| @@ -477,6 +478,20 @@ add_library(core STATIC | ||||
|     hle/service/hid/hidbus/starlink.h | ||||
|     hle/service/hid/hidbus/stubbed.cpp | ||||
|     hle/service/hid/hidbus/stubbed.h | ||||
|     hle/service/hid/irsensor/clustering_processor.cpp | ||||
|     hle/service/hid/irsensor/clustering_processor.h | ||||
|     hle/service/hid/irsensor/image_transfer_processor.cpp | ||||
|     hle/service/hid/irsensor/image_transfer_processor.h | ||||
|     hle/service/hid/irsensor/ir_led_processor.cpp | ||||
|     hle/service/hid/irsensor/ir_led_processor.h | ||||
|     hle/service/hid/irsensor/moment_processor.cpp | ||||
|     hle/service/hid/irsensor/moment_processor.h | ||||
|     hle/service/hid/irsensor/pointing_processor.cpp | ||||
|     hle/service/hid/irsensor/pointing_processor.h | ||||
|     hle/service/hid/irsensor/processor_base.cpp | ||||
|     hle/service/hid/irsensor/processor_base.h | ||||
|     hle/service/hid/irsensor/tera_plugin_processor.cpp | ||||
|     hle/service/hid/irsensor/tera_plugin_processor.h | ||||
|     hle/service/jit/jit_context.cpp | ||||
|     hle/service/jit/jit_context.h | ||||
|     hle/service/jit/jit.cpp | ||||
|   | ||||
| @@ -126,10 +126,14 @@ void EmulatedController::LoadDevices() { | ||||
|     battery_params[LeftIndex].Set("battery", true); | ||||
|     battery_params[RightIndex].Set("battery", true); | ||||
|  | ||||
|     camera_params = Common::ParamPackage{"engine:camera,camera:1"}; | ||||
|  | ||||
|     output_params[LeftIndex] = left_joycon; | ||||
|     output_params[RightIndex] = right_joycon; | ||||
|     output_params[2] = camera_params; | ||||
|     output_params[LeftIndex].Set("output", true); | ||||
|     output_params[RightIndex].Set("output", true); | ||||
|     output_params[2].Set("output", true); | ||||
|  | ||||
|     LoadTASParams(); | ||||
|  | ||||
| @@ -146,6 +150,7 @@ void EmulatedController::LoadDevices() { | ||||
|                    Common::Input::CreateDevice<Common::Input::InputDevice>); | ||||
|     std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(), | ||||
|                    Common::Input::CreateDevice<Common::Input::InputDevice>); | ||||
|     camera_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(camera_params); | ||||
|     std::transform(output_params.begin(), output_params.end(), output_devices.begin(), | ||||
|                    Common::Input::CreateDevice<Common::Input::OutputDevice>); | ||||
|  | ||||
| @@ -267,6 +272,14 @@ void EmulatedController::ReloadInput() { | ||||
|         motion_devices[index]->ForceUpdate(); | ||||
|     } | ||||
|  | ||||
|     if (camera_devices) { | ||||
|         camera_devices->SetCallback({ | ||||
|             .on_change = | ||||
|                 [this](const Common::Input::CallbackStatus& callback) { SetCamera(callback); }, | ||||
|         }); | ||||
|         camera_devices->ForceUpdate(); | ||||
|     } | ||||
|  | ||||
|     // Use a common UUID for TAS | ||||
|     static constexpr Common::UUID TAS_UUID = Common::UUID{ | ||||
|         {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; | ||||
| @@ -851,6 +864,25 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac | ||||
|     TriggerOnChange(ControllerTriggerType::Battery, true); | ||||
| } | ||||
|  | ||||
| void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) { | ||||
|     std::unique_lock lock{mutex}; | ||||
|     controller.camera_values = TransformToCamera(callback); | ||||
|  | ||||
|     if (is_configuring) { | ||||
|         lock.unlock(); | ||||
|         TriggerOnChange(ControllerTriggerType::IrSensor, false); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     controller.camera_state.sample++; | ||||
|     controller.camera_state.format = | ||||
|         static_cast<Core::IrSensor::ImageTransferProcessorFormat>(controller.camera_values.format); | ||||
|     controller.camera_state.data = controller.camera_values.data; | ||||
|  | ||||
|     lock.unlock(); | ||||
|     TriggerOnChange(ControllerTriggerType::IrSensor, true); | ||||
| } | ||||
|  | ||||
| bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { | ||||
|     if (device_index >= output_devices.size()) { | ||||
|         return false; | ||||
| @@ -928,6 +960,23 @@ bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) | ||||
|     return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None; | ||||
| } | ||||
|  | ||||
| bool EmulatedController::SetCameraFormat( | ||||
|     Core::IrSensor::ImageTransferProcessorFormat camera_format) { | ||||
|     LOG_INFO(Service_HID, "Set camera format {}", camera_format); | ||||
|  | ||||
|     auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; | ||||
|     auto& camera_output_device = output_devices[2]; | ||||
|  | ||||
|     if (right_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>( | ||||
|             camera_format)) == Common::Input::CameraError::None) { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     // Fallback to Qt camera if native device doesn't have support | ||||
|     return camera_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>( | ||||
|                camera_format)) == Common::Input::CameraError::None; | ||||
| } | ||||
|  | ||||
| void EmulatedController::SetLedPattern() { | ||||
|     for (auto& device : output_devices) { | ||||
|         if (!device) { | ||||
| @@ -1163,6 +1212,11 @@ BatteryValues EmulatedController::GetBatteryValues() const { | ||||
|     return controller.battery_values; | ||||
| } | ||||
|  | ||||
| CameraValues EmulatedController::GetCameraValues() const { | ||||
|     std::scoped_lock lock{mutex}; | ||||
|     return controller.camera_values; | ||||
| } | ||||
|  | ||||
| HomeButtonState EmulatedController::GetHomeButtons() const { | ||||
|     std::scoped_lock lock{mutex}; | ||||
|     if (is_configuring) { | ||||
| @@ -1251,6 +1305,11 @@ BatteryLevelState EmulatedController::GetBattery() const { | ||||
|     return controller.battery_state; | ||||
| } | ||||
|  | ||||
| const CameraState& EmulatedController::GetCamera() const { | ||||
|     std::scoped_lock lock{mutex}; | ||||
|     return controller.camera_state; | ||||
| } | ||||
|  | ||||
| void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) { | ||||
|     std::scoped_lock lock{callback_mutex}; | ||||
|     for (const auto& poller_pair : callback_list) { | ||||
|   | ||||
| @@ -15,10 +15,12 @@ | ||||
| #include "common/settings.h" | ||||
| #include "common/vector_math.h" | ||||
| #include "core/hid/hid_types.h" | ||||
| #include "core/hid/irs_types.h" | ||||
| #include "core/hid/motion_input.h" | ||||
|  | ||||
| namespace Core::HID { | ||||
| const std::size_t max_emulated_controllers = 2; | ||||
| const std::size_t output_devices = 3; | ||||
| struct ControllerMotionInfo { | ||||
|     Common::Input::MotionStatus raw_status{}; | ||||
|     MotionInput emulated{}; | ||||
| @@ -34,15 +36,16 @@ using TriggerDevices = | ||||
|     std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>; | ||||
| using BatteryDevices = | ||||
|     std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; | ||||
| using OutputDevices = | ||||
|     std::array<std::unique_ptr<Common::Input::OutputDevice>, max_emulated_controllers>; | ||||
| using CameraDevices = std::unique_ptr<Common::Input::InputDevice>; | ||||
| using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices>; | ||||
|  | ||||
| using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>; | ||||
| using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>; | ||||
| using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>; | ||||
| using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>; | ||||
| using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>; | ||||
| using OutputParams = std::array<Common::ParamPackage, max_emulated_controllers>; | ||||
| using CameraParams = Common::ParamPackage; | ||||
| using OutputParams = std::array<Common::ParamPackage, output_devices>; | ||||
|  | ||||
| using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>; | ||||
| using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>; | ||||
| @@ -51,6 +54,7 @@ using TriggerValues = | ||||
| using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::NativeMotion::NumMotions>; | ||||
| using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>; | ||||
| using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>; | ||||
| using CameraValues = Common::Input::CameraStatus; | ||||
| using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>; | ||||
|  | ||||
| struct AnalogSticks { | ||||
| @@ -70,6 +74,12 @@ struct BatteryLevelState { | ||||
|     NpadPowerInfo right{}; | ||||
| }; | ||||
|  | ||||
| struct CameraState { | ||||
|     Core::IrSensor::ImageTransferProcessorFormat format{}; | ||||
|     std::vector<u8> data{}; | ||||
|     std::size_t sample{}; | ||||
| }; | ||||
|  | ||||
| struct ControllerMotion { | ||||
|     Common::Vec3f accel{}; | ||||
|     Common::Vec3f gyro{}; | ||||
| @@ -96,6 +106,7 @@ struct ControllerStatus { | ||||
|     ColorValues color_values{}; | ||||
|     BatteryValues battery_values{}; | ||||
|     VibrationValues vibration_values{}; | ||||
|     CameraValues camera_values{}; | ||||
|  | ||||
|     // Data for HID serices | ||||
|     HomeButtonState home_button_state{}; | ||||
| @@ -107,6 +118,7 @@ struct ControllerStatus { | ||||
|     NpadGcTriggerState gc_trigger_state{}; | ||||
|     ControllerColors colors_state{}; | ||||
|     BatteryLevelState battery_state{}; | ||||
|     CameraState camera_state{}; | ||||
| }; | ||||
|  | ||||
| enum class ControllerTriggerType { | ||||
| @@ -117,6 +129,7 @@ enum class ControllerTriggerType { | ||||
|     Color, | ||||
|     Battery, | ||||
|     Vibration, | ||||
|     IrSensor, | ||||
|     Connected, | ||||
|     Disconnected, | ||||
|     Type, | ||||
| @@ -269,6 +282,9 @@ public: | ||||
|     /// Returns the latest battery status from the controller with parameters | ||||
|     BatteryValues GetBatteryValues() const; | ||||
|  | ||||
|     /// Returns the latest camera status from the controller with parameters | ||||
|     CameraValues GetCameraValues() const; | ||||
|  | ||||
|     /// Returns the latest status of button input for the hid::HomeButton service | ||||
|     HomeButtonState GetHomeButtons() const; | ||||
|  | ||||
| @@ -296,6 +312,9 @@ public: | ||||
|     /// Returns the latest battery status from the controller | ||||
|     BatteryLevelState GetBattery() const; | ||||
|  | ||||
|     /// Returns the latest camera status from the controller | ||||
|     const CameraState& GetCamera() const; | ||||
|  | ||||
|     /** | ||||
|      * Sends a specific vibration to the output device | ||||
|      * @return true if vibration had no errors | ||||
| @@ -315,6 +334,13 @@ public: | ||||
|      */ | ||||
|     bool SetPollingMode(Common::Input::PollingMode polling_mode); | ||||
|  | ||||
|     /** | ||||
|      * Sets the desired camera format to be polled from a controller | ||||
|      * @param camera_format size of each frame | ||||
|      * @return true if SetCameraFormat was successfull | ||||
|      */ | ||||
|     bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format); | ||||
|  | ||||
|     /// Returns the led pattern corresponding to this emulated controller | ||||
|     LedPattern GetLedPattern() const; | ||||
|  | ||||
| @@ -392,6 +418,12 @@ private: | ||||
|      */ | ||||
|     void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index); | ||||
|  | ||||
|     /** | ||||
|      * Updates the camera status of the controller | ||||
|      * @param callback A CallbackStatus containing the camera status | ||||
|      */ | ||||
|     void SetCamera(const Common::Input::CallbackStatus& callback); | ||||
|  | ||||
|     /** | ||||
|      * Triggers a callback that something has changed on the controller status | ||||
|      * @param type Input type of the event to trigger | ||||
| @@ -417,6 +449,7 @@ private: | ||||
|     ControllerMotionParams motion_params; | ||||
|     TriggerParams trigger_params; | ||||
|     BatteryParams battery_params; | ||||
|     CameraParams camera_params; | ||||
|     OutputParams output_params; | ||||
|  | ||||
|     ButtonDevices button_devices; | ||||
| @@ -424,6 +457,7 @@ private: | ||||
|     ControllerMotionDevices motion_devices; | ||||
|     TriggerDevices trigger_devices; | ||||
|     BatteryDevices battery_devices; | ||||
|     CameraDevices camera_devices; | ||||
|     OutputDevices output_devices; | ||||
|  | ||||
|     // TAS related variables | ||||
|   | ||||
| @@ -270,6 +270,20 @@ Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatu | ||||
|     return status; | ||||
| } | ||||
|  | ||||
| Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback) { | ||||
|     Common::Input::CameraStatus camera{}; | ||||
|     switch (callback.type) { | ||||
|     case Common::Input::InputType::IrSensor: | ||||
|         camera = callback.camera_status; | ||||
|         break; | ||||
|     default: | ||||
|         LOG_ERROR(Input, "Conversion from type {} to camera not implemented", callback.type); | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     return camera; | ||||
| } | ||||
|  | ||||
| void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) { | ||||
|     const auto& properties = analog.properties; | ||||
|     float& raw_value = analog.raw_value; | ||||
|   | ||||
| @@ -76,6 +76,14 @@ Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackSta | ||||
|  */ | ||||
| Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback); | ||||
|  | ||||
| /** | ||||
|  * Converts raw input data into a valid camera status. | ||||
|  * | ||||
|  * @param callback Supported callbacks: Camera. | ||||
|  * @return A valid CameraObject object. | ||||
|  */ | ||||
| Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback); | ||||
|  | ||||
| /** | ||||
|  * Converts raw analog data into a valid analog value | ||||
|  * @param analog An analog object containing raw data and properties | ||||
|   | ||||
							
								
								
									
										301
									
								
								src/core/hid/irs_types.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										301
									
								
								src/core/hid/irs_types.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,301 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "common/common_funcs.h" | ||||
| #include "common/common_types.h" | ||||
| #include "core/hid/hid_types.h" | ||||
|  | ||||
| namespace Core::IrSensor { | ||||
|  | ||||
| // This is nn::irsensor::CameraAmbientNoiseLevel | ||||
| enum class CameraAmbientNoiseLevel : u32 { | ||||
|     Low, | ||||
|     Medium, | ||||
|     High, | ||||
|     Unkown3, // This level can't be reached | ||||
| }; | ||||
|  | ||||
| // This is nn::irsensor::CameraLightTarget | ||||
| enum class CameraLightTarget : u32 { | ||||
|     AllLeds, | ||||
|     BrightLeds, | ||||
|     DimLeds, | ||||
|     None, | ||||
| }; | ||||
|  | ||||
| // This is nn::irsensor::PackedCameraLightTarget | ||||
| enum class PackedCameraLightTarget : u8 { | ||||
|     AllLeds, | ||||
|     BrightLeds, | ||||
|     DimLeds, | ||||
|     None, | ||||
| }; | ||||
|  | ||||
| // This is nn::irsensor::AdaptiveClusteringMode | ||||
| enum class AdaptiveClusteringMode : u32 { | ||||
|     StaticFov, | ||||
|     DynamicFov, | ||||
| }; | ||||
|  | ||||
| // This is nn::irsensor::AdaptiveClusteringTargetDistance | ||||
| enum class AdaptiveClusteringTargetDistance : u32 { | ||||
|     Near, | ||||
|     Middle, | ||||
|     Far, | ||||
| }; | ||||
|  | ||||
| // This is nn::irsensor::ImageTransferProcessorFormat | ||||
| enum class ImageTransferProcessorFormat : u32 { | ||||
|     Size320x240, | ||||
|     Size160x120, | ||||
|     Size80x60, | ||||
|     Size40x30, | ||||
|     Size20x15, | ||||
| }; | ||||
|  | ||||
| // This is nn::irsensor::PackedImageTransferProcessorFormat | ||||
| enum class PackedImageTransferProcessorFormat : u8 { | ||||
|     Size320x240, | ||||
|     Size160x120, | ||||
|     Size80x60, | ||||
|     Size40x30, | ||||
|     Size20x15, | ||||
| }; | ||||
|  | ||||
| // This is nn::irsensor::IrCameraStatus | ||||
| enum class IrCameraStatus : u32 { | ||||
|     Available, | ||||
|     Unsupported, | ||||
|     Unconnected, | ||||
| }; | ||||
|  | ||||
| // This is nn::irsensor::IrCameraInternalStatus | ||||
| enum class IrCameraInternalStatus : u32 { | ||||
|     Stopped, | ||||
|     FirmwareUpdateNeeded, | ||||
|     Unkown2, | ||||
|     Unkown3, | ||||
|     Unkown4, | ||||
|     FirmwareVersionRequested, | ||||
|     FirmwareVersionIsInvalid, | ||||
|     Ready, | ||||
|     Setting, | ||||
| }; | ||||
|  | ||||
| // This is nn::irsensor::detail::StatusManager::IrSensorMode | ||||
| enum class IrSensorMode : u64 { | ||||
|     None, | ||||
|     MomentProcessor, | ||||
|     ClusteringProcessor, | ||||
|     ImageTransferProcessor, | ||||
|     PointingProcessorMarker, | ||||
|     TeraPluginProcessor, | ||||
|     IrLedProcessor, | ||||
| }; | ||||
|  | ||||
| // This is nn::irsensor::ImageProcessorStatus | ||||
| enum ImageProcessorStatus : u32 { | ||||
|     Stopped, | ||||
|     Running, | ||||
| }; | ||||
|  | ||||
| // This is nn::irsensor::HandAnalysisMode | ||||
| enum class HandAnalysisMode : u32 { | ||||
|     None, | ||||
|     Silhouette, | ||||
|     Image, | ||||
|     SilhoueteAndImage, | ||||
|     SilhuetteOnly, | ||||
| }; | ||||
|  | ||||
| // This is nn::irsensor::IrSensorFunctionLevel | ||||
| enum class IrSensorFunctionLevel : u8 { | ||||
|     unknown0, | ||||
|     unknown1, | ||||
|     unknown2, | ||||
|     unknown3, | ||||
|     unknown4, | ||||
| }; | ||||
|  | ||||
| // This is nn::irsensor::MomentProcessorPreprocess | ||||
| enum class MomentProcessorPreprocess : u32 { | ||||
|     Unkown0, | ||||
|     Unkown1, | ||||
| }; | ||||
|  | ||||
| // This is nn::irsensor::PackedMomentProcessorPreprocess | ||||
| enum class PackedMomentProcessorPreprocess : u8 { | ||||
|     Unkown0, | ||||
|     Unkown1, | ||||
| }; | ||||
|  | ||||
| // This is nn::irsensor::PointingStatus | ||||
| enum class PointingStatus : u32 { | ||||
|     Unkown0, | ||||
|     Unkown1, | ||||
| }; | ||||
|  | ||||
| struct IrsRect { | ||||
|     s16 x; | ||||
|     s16 y; | ||||
|     s16 width; | ||||
|     s16 height; | ||||
| }; | ||||
|  | ||||
| struct IrsCentroid { | ||||
|     f32 x; | ||||
|     f32 y; | ||||
| }; | ||||
|  | ||||
| struct CameraConfig { | ||||
|     u64 exposure_time; | ||||
|     CameraLightTarget light_target; | ||||
|     u32 gain; | ||||
|     bool is_negative_used; | ||||
|     INSERT_PADDING_BYTES(7); | ||||
| }; | ||||
| static_assert(sizeof(CameraConfig) == 0x18, "CameraConfig is an invalid size"); | ||||
|  | ||||
| struct PackedCameraConfig { | ||||
|     u64 exposure_time; | ||||
|     PackedCameraLightTarget light_target; | ||||
|     u8 gain; | ||||
|     bool is_negative_used; | ||||
|     INSERT_PADDING_BYTES(5); | ||||
| }; | ||||
| static_assert(sizeof(PackedCameraConfig) == 0x10, "PackedCameraConfig is an invalid size"); | ||||
|  | ||||
| // This is nn::irsensor::IrCameraHandle | ||||
| struct IrCameraHandle { | ||||
|     u8 npad_id{}; | ||||
|     Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None}; | ||||
|     INSERT_PADDING_BYTES(2); | ||||
| }; | ||||
| static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size"); | ||||
|  | ||||
| // This is nn::irsensor::PackedMcuVersion | ||||
| struct PackedMcuVersion { | ||||
|     u16 major; | ||||
|     u16 minor; | ||||
| }; | ||||
| static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size"); | ||||
|  | ||||
| // This is nn::irsensor::PackedMomentProcessorConfig | ||||
| struct PackedMomentProcessorConfig { | ||||
|     PackedCameraConfig camera_config; | ||||
|     IrsRect window_of_interest; | ||||
|     PackedMcuVersion required_mcu_version; | ||||
|     PackedMomentProcessorPreprocess preprocess; | ||||
|     u8 preprocess_intensity_threshold; | ||||
|     INSERT_PADDING_BYTES(2); | ||||
| }; | ||||
| static_assert(sizeof(PackedMomentProcessorConfig) == 0x20, | ||||
|               "PackedMomentProcessorConfig is an invalid size"); | ||||
|  | ||||
| // This is nn::irsensor::PackedClusteringProcessorConfig | ||||
| struct PackedClusteringProcessorConfig { | ||||
|     PackedCameraConfig camera_config; | ||||
|     IrsRect window_of_interest; | ||||
|     PackedMcuVersion required_mcu_version; | ||||
|     u32 pixel_count_min; | ||||
|     u32 pixel_count_max; | ||||
|     u8 object_intensity_min; | ||||
|     bool is_external_light_filter_enabled; | ||||
|     INSERT_PADDING_BYTES(2); | ||||
| }; | ||||
| static_assert(sizeof(PackedClusteringProcessorConfig) == 0x28, | ||||
|               "PackedClusteringProcessorConfig is an invalid size"); | ||||
|  | ||||
| // This is nn::irsensor::PackedImageTransferProcessorConfig | ||||
| struct PackedImageTransferProcessorConfig { | ||||
|     PackedCameraConfig camera_config; | ||||
|     PackedMcuVersion required_mcu_version; | ||||
|     PackedImageTransferProcessorFormat format; | ||||
|     INSERT_PADDING_BYTES(3); | ||||
| }; | ||||
| static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18, | ||||
|               "PackedImageTransferProcessorConfig is an invalid size"); | ||||
|  | ||||
| // This is nn::irsensor::PackedTeraPluginProcessorConfig | ||||
| struct PackedTeraPluginProcessorConfig { | ||||
|     PackedMcuVersion required_mcu_version; | ||||
|     u8 mode; | ||||
|     u8 unknown_1; | ||||
|     u8 unknown_2; | ||||
|     u8 unknown_3; | ||||
| }; | ||||
| static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8, | ||||
|               "PackedTeraPluginProcessorConfig is an invalid size"); | ||||
|  | ||||
| // This is nn::irsensor::PackedPointingProcessorConfig | ||||
| struct PackedPointingProcessorConfig { | ||||
|     IrsRect window_of_interest; | ||||
|     PackedMcuVersion required_mcu_version; | ||||
| }; | ||||
| static_assert(sizeof(PackedPointingProcessorConfig) == 0xC, | ||||
|               "PackedPointingProcessorConfig is an invalid size"); | ||||
|  | ||||
| // This is nn::irsensor::PackedFunctionLevel | ||||
| struct PackedFunctionLevel { | ||||
|     IrSensorFunctionLevel function_level; | ||||
|     INSERT_PADDING_BYTES(3); | ||||
| }; | ||||
| static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size"); | ||||
|  | ||||
| // This is nn::irsensor::PackedImageTransferProcessorExConfig | ||||
| struct PackedImageTransferProcessorExConfig { | ||||
|     PackedCameraConfig camera_config; | ||||
|     PackedMcuVersion required_mcu_version; | ||||
|     PackedImageTransferProcessorFormat origin_format; | ||||
|     PackedImageTransferProcessorFormat trimming_format; | ||||
|     u16 trimming_start_x; | ||||
|     u16 trimming_start_y; | ||||
|     bool is_external_light_filter_enabled; | ||||
|     INSERT_PADDING_BYTES(5); | ||||
| }; | ||||
| static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20, | ||||
|               "PackedImageTransferProcessorExConfig is an invalid size"); | ||||
|  | ||||
| // This is nn::irsensor::PackedIrLedProcessorConfig | ||||
| struct PackedIrLedProcessorConfig { | ||||
|     PackedMcuVersion required_mcu_version; | ||||
|     u8 light_target; | ||||
|     INSERT_PADDING_BYTES(3); | ||||
| }; | ||||
| static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8, | ||||
|               "PackedIrLedProcessorConfig is an invalid size"); | ||||
|  | ||||
| // This is nn::irsensor::HandAnalysisConfig | ||||
| struct HandAnalysisConfig { | ||||
|     HandAnalysisMode mode; | ||||
| }; | ||||
| static_assert(sizeof(HandAnalysisConfig) == 0x4, "HandAnalysisConfig is an invalid size"); | ||||
|  | ||||
| // This is nn::irsensor::detail::ProcessorState contents are different for each processor | ||||
| struct ProcessorState { | ||||
|     std::array<u8, 0xE20> processor_raw_data{}; | ||||
| }; | ||||
| static_assert(sizeof(ProcessorState) == 0xE20, "ProcessorState is an invalid size"); | ||||
|  | ||||
| // This is nn::irsensor::detail::DeviceFormat | ||||
| struct DeviceFormat { | ||||
|     Core::IrSensor::IrCameraStatus camera_status{Core::IrSensor::IrCameraStatus::Unconnected}; | ||||
|     Core::IrSensor::IrCameraInternalStatus camera_internal_status{ | ||||
|         Core::IrSensor::IrCameraInternalStatus::Ready}; | ||||
|     Core::IrSensor::IrSensorMode mode{Core::IrSensor::IrSensorMode::None}; | ||||
|     ProcessorState state{}; | ||||
| }; | ||||
| static_assert(sizeof(DeviceFormat) == 0xE30, "DeviceFormat is an invalid size"); | ||||
|  | ||||
| // This is nn::irsensor::ImageTransferProcessorState | ||||
| struct ImageTransferProcessorState { | ||||
|     u64 sampling_number; | ||||
|     Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; | ||||
|     INSERT_PADDING_BYTES(4); | ||||
| }; | ||||
| static_assert(sizeof(ImageTransferProcessorState) == 0x10, | ||||
|               "ImageTransferProcessorState is an invalid size"); | ||||
|  | ||||
| } // namespace Core::IrSensor | ||||
| @@ -19,3 +19,10 @@ constexpr Result InvalidNpadId{ErrorModule::HID, 709}; | ||||
| constexpr Result NpadNotConnected{ErrorModule::HID, 710}; | ||||
|  | ||||
| } // namespace Service::HID | ||||
|  | ||||
| namespace Service::IRS { | ||||
|  | ||||
| constexpr Result InvalidProcessorState{ErrorModule::Irsensor, 78}; | ||||
| constexpr Result InvalidIrCameraHandle{ErrorModule::Irsensor, 204}; | ||||
|  | ||||
| } // namespace Service::IRS | ||||
|   | ||||
| @@ -2345,8 +2345,8 @@ void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system | ||||
|     std::make_shared<HidSys>(system)->InstallAsService(service_manager); | ||||
|     std::make_shared<HidTmp>(system)->InstallAsService(service_manager); | ||||
|  | ||||
|     std::make_shared<IRS>(system)->InstallAsService(service_manager); | ||||
|     std::make_shared<IRS_SYS>(system)->InstallAsService(service_manager); | ||||
|     std::make_shared<Service::IRS::IRS>(system)->InstallAsService(service_manager); | ||||
|     std::make_shared<Service::IRS::IRS_SYS>(system)->InstallAsService(service_manager); | ||||
|  | ||||
|     std::make_shared<XCD_SYS>(system)->InstallAsService(service_manager); | ||||
| } | ||||
|   | ||||
| @@ -1,16 +1,28 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #include <algorithm> | ||||
| #include <random> | ||||
|  | ||||
| #include "core/core.h" | ||||
| #include "core/core_timing.h" | ||||
| #include "core/hid/emulated_controller.h" | ||||
| #include "core/hid/hid_core.h" | ||||
| #include "core/hle/ipc_helpers.h" | ||||
| #include "core/hle/kernel/k_shared_memory.h" | ||||
| #include "core/hle/kernel/k_transfer_memory.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/service/hid/errors.h" | ||||
| #include "core/hle/service/hid/irs.h" | ||||
| #include "core/hle/service/hid/irsensor/clustering_processor.h" | ||||
| #include "core/hle/service/hid/irsensor/image_transfer_processor.h" | ||||
| #include "core/hle/service/hid/irsensor/ir_led_processor.h" | ||||
| #include "core/hle/service/hid/irsensor/moment_processor.h" | ||||
| #include "core/hle/service/hid/irsensor/pointing_processor.h" | ||||
| #include "core/hle/service/hid/irsensor/tera_plugin_processor.h" | ||||
| #include "core/memory.h" | ||||
|  | ||||
| namespace Service::HID { | ||||
| namespace Service::IRS { | ||||
|  | ||||
| IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} { | ||||
|     // clang-format off | ||||
| @@ -36,14 +48,19 @@ IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} { | ||||
|     }; | ||||
|     // clang-format on | ||||
|  | ||||
|     u8* raw_shared_memory = system.Kernel().GetIrsSharedMem().GetPointer(); | ||||
|     RegisterHandlers(functions); | ||||
|     shared_memory = std::construct_at(reinterpret_cast<StatusManager*>(raw_shared_memory)); | ||||
|  | ||||
|     npad_device = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); | ||||
| } | ||||
| IRS::~IRS() = default; | ||||
|  | ||||
| void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     const auto applet_resource_user_id{rp.Pop<u64>()}; | ||||
|  | ||||
|     LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", | ||||
|     LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", | ||||
|                 applet_resource_user_id); | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 2}; | ||||
| @@ -54,7 +71,7 @@ void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     const auto applet_resource_user_id{rp.Pop<u64>()}; | ||||
|  | ||||
|     LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", | ||||
|     LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", | ||||
|                 applet_resource_user_id); | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 2}; | ||||
| @@ -75,7 +92,7 @@ void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) { | ||||
| void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     struct Parameters { | ||||
|         IrCameraHandle camera_handle; | ||||
|         Core::IrSensor::IrCameraHandle camera_handle; | ||||
|         INSERT_PADDING_WORDS_NOINIT(1); | ||||
|         u64 applet_resource_user_id; | ||||
|     }; | ||||
| @@ -88,17 +105,23 @@ void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) { | ||||
|                 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | ||||
|                 parameters.applet_resource_user_id); | ||||
|  | ||||
|     auto result = IsIrCameraHandleValid(parameters.camera_handle); | ||||
|     if (result.IsSuccess()) { | ||||
|         // TODO: Stop Image processor | ||||
|         result = ResultSuccess; | ||||
|     } | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 2}; | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.Push(result); | ||||
| } | ||||
|  | ||||
| void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     struct Parameters { | ||||
|         IrCameraHandle camera_handle; | ||||
|         Core::IrSensor::IrCameraHandle camera_handle; | ||||
|         INSERT_PADDING_WORDS_NOINIT(1); | ||||
|         u64 applet_resource_user_id; | ||||
|         PackedMomentProcessorConfig processor_config; | ||||
|         Core::IrSensor::PackedMomentProcessorConfig processor_config; | ||||
|     }; | ||||
|     static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size."); | ||||
|  | ||||
| @@ -109,19 +132,28 @@ void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) { | ||||
|                 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | ||||
|                 parameters.applet_resource_user_id); | ||||
|  | ||||
|     const auto result = IsIrCameraHandleValid(parameters.camera_handle); | ||||
|  | ||||
|     if (result.IsSuccess()) { | ||||
|         auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); | ||||
|         MakeProcessor<MomentProcessor>(parameters.camera_handle, device); | ||||
|         auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle); | ||||
|         image_transfer_processor.SetConfig(parameters.processor_config); | ||||
|     } | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 2}; | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.Push(result); | ||||
| } | ||||
|  | ||||
| void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     struct Parameters { | ||||
|         IrCameraHandle camera_handle; | ||||
|         Core::IrSensor::IrCameraHandle camera_handle; | ||||
|         INSERT_PADDING_WORDS_NOINIT(1); | ||||
|         u64 applet_resource_user_id; | ||||
|         PackedClusteringProcessorConfig processor_config; | ||||
|         Core::IrSensor::PackedClusteringProcessorConfig processor_config; | ||||
|     }; | ||||
|     static_assert(sizeof(Parameters) == 0x40, "Parameters has incorrect size."); | ||||
|     static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size."); | ||||
|  | ||||
|     const auto parameters{rp.PopRaw<Parameters>()}; | ||||
|  | ||||
| @@ -130,17 +162,27 @@ void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) { | ||||
|                 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | ||||
|                 parameters.applet_resource_user_id); | ||||
|  | ||||
|     auto result = IsIrCameraHandleValid(parameters.camera_handle); | ||||
|  | ||||
|     if (result.IsSuccess()) { | ||||
|         auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); | ||||
|         MakeProcessor<ClusteringProcessor>(parameters.camera_handle, device); | ||||
|         auto& image_transfer_processor = | ||||
|             GetProcessor<ClusteringProcessor>(parameters.camera_handle); | ||||
|         image_transfer_processor.SetConfig(parameters.processor_config); | ||||
|     } | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 2}; | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.Push(result); | ||||
| } | ||||
|  | ||||
| void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     struct Parameters { | ||||
|         IrCameraHandle camera_handle; | ||||
|         Core::IrSensor::IrCameraHandle camera_handle; | ||||
|         INSERT_PADDING_WORDS_NOINIT(1); | ||||
|         u64 applet_resource_user_id; | ||||
|         PackedImageTransferProcessorConfig processor_config; | ||||
|         Core::IrSensor::PackedImageTransferProcessorConfig processor_config; | ||||
|         u32 transfer_memory_size; | ||||
|     }; | ||||
|     static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size."); | ||||
| @@ -151,20 +193,42 @@ void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) { | ||||
|     auto t_mem = | ||||
|         system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle); | ||||
|  | ||||
|     LOG_WARNING(Service_IRS, | ||||
|                 "(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, " | ||||
|     if (t_mem.IsNull()) { | ||||
|         LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(ResultUnknown); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     ASSERT_MSG(t_mem->GetSize() == parameters.transfer_memory_size, "t_mem has incorrect size"); | ||||
|  | ||||
|     u8* transfer_memory = system.Memory().GetPointer(t_mem->GetSourceAddress()); | ||||
|  | ||||
|     LOG_INFO(Service_IRS, | ||||
|              "called, npad_type={}, npad_id={}, transfer_memory_size={}, transfer_memory_size={}, " | ||||
|              "applet_resource_user_id={}", | ||||
|              parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | ||||
|                 parameters.transfer_memory_size, parameters.applet_resource_user_id); | ||||
|              parameters.transfer_memory_size, t_mem->GetSize(), parameters.applet_resource_user_id); | ||||
|  | ||||
|     const auto result = IsIrCameraHandleValid(parameters.camera_handle); | ||||
|  | ||||
|     if (result.IsSuccess()) { | ||||
|         auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); | ||||
|         MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device); | ||||
|         auto& image_transfer_processor = | ||||
|             GetProcessor<ImageTransferProcessor>(parameters.camera_handle); | ||||
|         image_transfer_processor.SetConfig(parameters.processor_config); | ||||
|         image_transfer_processor.SetTransferMemoryPointer(transfer_memory); | ||||
|     } | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 2}; | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.Push(result); | ||||
| } | ||||
|  | ||||
| void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     struct Parameters { | ||||
|         IrCameraHandle camera_handle; | ||||
|         Core::IrSensor::IrCameraHandle camera_handle; | ||||
|         INSERT_PADDING_WORDS_NOINIT(1); | ||||
|         u64 applet_resource_user_id; | ||||
|     }; | ||||
| @@ -172,32 +236,68 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) { | ||||
|  | ||||
|     const auto parameters{rp.PopRaw<Parameters>()}; | ||||
|  | ||||
|     LOG_WARNING(Service_IRS, | ||||
|                 "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", | ||||
|     LOG_DEBUG(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", | ||||
|               parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | ||||
|               parameters.applet_resource_user_id); | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 5}; | ||||
|     const auto result = IsIrCameraHandleValid(parameters.camera_handle); | ||||
|     if (result.IsError()) { | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(result); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); | ||||
|  | ||||
|     if (device.mode != Core::IrSensor::IrSensorMode::ImageTransferProcessor) { | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(InvalidProcessorState); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     std::vector<u8> data{}; | ||||
|     const auto& image_transfer_processor = | ||||
|         GetProcessor<ImageTransferProcessor>(parameters.camera_handle); | ||||
|     const auto& state = image_transfer_processor.GetState(data); | ||||
|  | ||||
|     ctx.WriteBuffer(data); | ||||
|     IPC::ResponseBuilder rb{ctx, 6}; | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.PushRaw<u64>(system.CoreTiming().GetCPUTicks()); | ||||
|     rb.PushRaw<u32>(0); | ||||
|     rb.PushRaw(state); | ||||
| } | ||||
|  | ||||
| void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     const auto camera_handle{rp.PopRaw<IrCameraHandle>()}; | ||||
|     const auto processor_config{rp.PopRaw<PackedTeraPluginProcessorConfig>()}; | ||||
|     const auto applet_resource_user_id{rp.Pop<u64>()}; | ||||
|     struct Parameters { | ||||
|         Core::IrSensor::IrCameraHandle camera_handle; | ||||
|         Core::IrSensor::PackedTeraPluginProcessorConfig processor_config; | ||||
|         INSERT_PADDING_WORDS_NOINIT(1); | ||||
|         u64 applet_resource_user_id; | ||||
|     }; | ||||
|     static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); | ||||
|  | ||||
|     LOG_WARNING(Service_IRS, | ||||
|     const auto parameters{rp.PopRaw<Parameters>()}; | ||||
|  | ||||
|     LOG_WARNING( | ||||
|         Service_IRS, | ||||
|         "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, " | ||||
|         "applet_resource_user_id={}", | ||||
|                 camera_handle.npad_type, camera_handle.npad_id, processor_config.mode, | ||||
|                 processor_config.required_mcu_version.major, | ||||
|                 processor_config.required_mcu_version.minor, applet_resource_user_id); | ||||
|         parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | ||||
|         parameters.processor_config.mode, parameters.processor_config.required_mcu_version.major, | ||||
|         parameters.processor_config.required_mcu_version.minor, parameters.applet_resource_user_id); | ||||
|  | ||||
|     const auto result = IsIrCameraHandleValid(parameters.camera_handle); | ||||
|  | ||||
|     if (result.IsSuccess()) { | ||||
|         auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); | ||||
|         MakeProcessor<TeraPluginProcessor>(parameters.camera_handle, device); | ||||
|         auto& image_transfer_processor = | ||||
|             GetProcessor<TeraPluginProcessor>(parameters.camera_handle); | ||||
|         image_transfer_processor.SetConfig(parameters.processor_config); | ||||
|     } | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 2}; | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.Push(result); | ||||
| } | ||||
|  | ||||
| void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) { | ||||
| @@ -207,17 +307,17 @@ void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) { | ||||
|     if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid && | ||||
|         npad_id != Core::HID::NpadIdType::Handheld) { | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
|         rb.Push(InvalidNpadId); | ||||
|         rb.Push(Service::HID::InvalidNpadId); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     IrCameraHandle camera_handle{ | ||||
|     Core::IrSensor::IrCameraHandle camera_handle{ | ||||
|         .npad_id = static_cast<u8>(NpadIdTypeToIndex(npad_id)), | ||||
|         .npad_type = Core::HID::NpadStyleIndex::None, | ||||
|     }; | ||||
|  | ||||
|     LOG_WARNING(Service_IRS, "(STUBBED) called, npad_id={}, camera_npad_id={}, camera_npad_type={}", | ||||
|                 npad_id, camera_handle.npad_id, camera_handle.npad_type); | ||||
|     LOG_INFO(Service_IRS, "called, npad_id={}, camera_npad_id={}, camera_npad_type={}", npad_id, | ||||
|              camera_handle.npad_id, camera_handle.npad_type); | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 3}; | ||||
|     rb.Push(ResultSuccess); | ||||
| @@ -226,8 +326,8 @@ void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) { | ||||
|  | ||||
| void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     const auto camera_handle{rp.PopRaw<IrCameraHandle>()}; | ||||
|     const auto processor_config{rp.PopRaw<PackedPointingProcessorConfig>()}; | ||||
|     const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; | ||||
|     const auto processor_config{rp.PopRaw<Core::IrSensor::PackedPointingProcessorConfig>()}; | ||||
|     const auto applet_resource_user_id{rp.Pop<u64>()}; | ||||
|  | ||||
|     LOG_WARNING( | ||||
| @@ -236,14 +336,23 @@ void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) { | ||||
|         camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major, | ||||
|         processor_config.required_mcu_version.minor, applet_resource_user_id); | ||||
|  | ||||
|     auto result = IsIrCameraHandleValid(camera_handle); | ||||
|  | ||||
|     if (result.IsSuccess()) { | ||||
|         auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); | ||||
|         MakeProcessor<PointingProcessor>(camera_handle, device); | ||||
|         auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle); | ||||
|         image_transfer_processor.SetConfig(processor_config); | ||||
|     } | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 2}; | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.Push(result); | ||||
| } | ||||
|  | ||||
| void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     struct Parameters { | ||||
|         IrCameraHandle camera_handle; | ||||
|         Core::IrSensor::IrCameraHandle camera_handle; | ||||
|         INSERT_PADDING_WORDS_NOINIT(1); | ||||
|         u64 applet_resource_user_id; | ||||
|     }; | ||||
| @@ -256,14 +365,20 @@ void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) { | ||||
|                 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | ||||
|                 parameters.applet_resource_user_id); | ||||
|  | ||||
|     auto result = IsIrCameraHandleValid(parameters.camera_handle); | ||||
|     if (result.IsSuccess()) { | ||||
|         // TODO: Suspend image processor | ||||
|         result = ResultSuccess; | ||||
|     } | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 2}; | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.Push(result); | ||||
| } | ||||
|  | ||||
| void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     const auto camera_handle{rp.PopRaw<IrCameraHandle>()}; | ||||
|     const auto mcu_version{rp.PopRaw<PackedMcuVersion>()}; | ||||
|     const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; | ||||
|     const auto mcu_version{rp.PopRaw<Core::IrSensor::PackedMcuVersion>()}; | ||||
|     const auto applet_resource_user_id{rp.Pop<u64>()}; | ||||
|  | ||||
|     LOG_WARNING( | ||||
| @@ -272,37 +387,45 @@ void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) { | ||||
|         camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major, | ||||
|         mcu_version.minor); | ||||
|  | ||||
|     auto result = IsIrCameraHandleValid(camera_handle); | ||||
|     if (result.IsSuccess()) { | ||||
|         // TODO: Check firmware version | ||||
|         result = ResultSuccess; | ||||
|     } | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 2}; | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.Push(result); | ||||
| } | ||||
|  | ||||
| void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     struct Parameters { | ||||
|         IrCameraHandle camera_handle; | ||||
|         PackedFunctionLevel function_level; | ||||
|         u64 applet_resource_user_id; | ||||
|     }; | ||||
|     static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||||
|     const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; | ||||
|     const auto function_level{rp.PopRaw<Core::IrSensor::PackedFunctionLevel>()}; | ||||
|     const auto applet_resource_user_id{rp.Pop<u64>()}; | ||||
|  | ||||
|     const auto parameters{rp.PopRaw<Parameters>()}; | ||||
|     LOG_WARNING( | ||||
|         Service_IRS, | ||||
|         "(STUBBED) called, npad_type={}, npad_id={}, function_level={}, applet_resource_user_id={}", | ||||
|         camera_handle.npad_type, camera_handle.npad_id, function_level.function_level, | ||||
|         applet_resource_user_id); | ||||
|  | ||||
|     LOG_WARNING(Service_IRS, | ||||
|                 "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", | ||||
|                 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | ||||
|                 parameters.applet_resource_user_id); | ||||
|     auto result = IsIrCameraHandleValid(camera_handle); | ||||
|     if (result.IsSuccess()) { | ||||
|         // TODO: Set Function level | ||||
|         result = ResultSuccess; | ||||
|     } | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 2}; | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.Push(result); | ||||
| } | ||||
|  | ||||
| void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     struct Parameters { | ||||
|         IrCameraHandle camera_handle; | ||||
|         Core::IrSensor::IrCameraHandle camera_handle; | ||||
|         INSERT_PADDING_WORDS_NOINIT(1); | ||||
|         u64 applet_resource_user_id; | ||||
|         PackedImageTransferProcessorExConfig processor_config; | ||||
|         Core::IrSensor::PackedImageTransferProcessorExConfig processor_config; | ||||
|         u64 transfer_memory_size; | ||||
|     }; | ||||
|     static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size."); | ||||
| @@ -313,20 +436,33 @@ void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) { | ||||
|     auto t_mem = | ||||
|         system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle); | ||||
|  | ||||
|     LOG_WARNING(Service_IRS, | ||||
|                 "(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, " | ||||
|     u8* transfer_memory = system.Memory().GetPointer(t_mem->GetSourceAddress()); | ||||
|  | ||||
|     LOG_INFO(Service_IRS, | ||||
|              "called, npad_type={}, npad_id={}, transfer_memory_size={}, " | ||||
|              "applet_resource_user_id={}", | ||||
|              parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | ||||
|              parameters.transfer_memory_size, parameters.applet_resource_user_id); | ||||
|  | ||||
|     auto result = IsIrCameraHandleValid(parameters.camera_handle); | ||||
|  | ||||
|     if (result.IsSuccess()) { | ||||
|         auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); | ||||
|         MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device); | ||||
|         auto& image_transfer_processor = | ||||
|             GetProcessor<ImageTransferProcessor>(parameters.camera_handle); | ||||
|         image_transfer_processor.SetConfig(parameters.processor_config); | ||||
|         image_transfer_processor.SetTransferMemoryPointer(transfer_memory); | ||||
|     } | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 2}; | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.Push(result); | ||||
| } | ||||
|  | ||||
| void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     const auto camera_handle{rp.PopRaw<IrCameraHandle>()}; | ||||
|     const auto processor_config{rp.PopRaw<PackedIrLedProcessorConfig>()}; | ||||
|     const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; | ||||
|     const auto processor_config{rp.PopRaw<Core::IrSensor::PackedIrLedProcessorConfig>()}; | ||||
|     const auto applet_resource_user_id{rp.Pop<u64>()}; | ||||
|  | ||||
|     LOG_WARNING(Service_IRS, | ||||
| @@ -336,14 +472,23 @@ void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) { | ||||
|                 processor_config.required_mcu_version.major, | ||||
|                 processor_config.required_mcu_version.minor, applet_resource_user_id); | ||||
|  | ||||
|     auto result = IsIrCameraHandleValid(camera_handle); | ||||
|  | ||||
|     if (result.IsSuccess()) { | ||||
|         auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); | ||||
|         MakeProcessor<IrLedProcessor>(camera_handle, device); | ||||
|         auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle); | ||||
|         image_transfer_processor.SetConfig(processor_config); | ||||
|     } | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 2}; | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.Push(result); | ||||
| } | ||||
|  | ||||
| void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     struct Parameters { | ||||
|         IrCameraHandle camera_handle; | ||||
|         Core::IrSensor::IrCameraHandle camera_handle; | ||||
|         INSERT_PADDING_WORDS_NOINIT(1); | ||||
|         u64 applet_resource_user_id; | ||||
|     }; | ||||
| @@ -356,14 +501,20 @@ void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) { | ||||
|                 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | ||||
|                 parameters.applet_resource_user_id); | ||||
|  | ||||
|     auto result = IsIrCameraHandleValid(parameters.camera_handle); | ||||
|     if (result.IsSuccess()) { | ||||
|         // TODO: Stop image processor async | ||||
|         result = ResultSuccess; | ||||
|     } | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 2}; | ||||
|     rb.Push(ResultSuccess); | ||||
|     rb.Push(result); | ||||
| } | ||||
|  | ||||
| void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     struct Parameters { | ||||
|         PackedFunctionLevel function_level; | ||||
|         Core::IrSensor::PackedFunctionLevel function_level; | ||||
|         INSERT_PADDING_WORDS_NOINIT(1); | ||||
|         u64 applet_resource_user_id; | ||||
|     }; | ||||
| @@ -378,7 +529,22 @@ void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) { | ||||
|     rb.Push(ResultSuccess); | ||||
| } | ||||
|  | ||||
| IRS::~IRS() = default; | ||||
| Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const { | ||||
|     if (camera_handle.npad_id > | ||||
|         static_cast<u8>(NpadIdTypeToIndex(Core::HID::NpadIdType::Handheld))) { | ||||
|         return InvalidIrCameraHandle; | ||||
|     } | ||||
|     if (camera_handle.npad_type != Core::HID::NpadStyleIndex::None) { | ||||
|         return InvalidIrCameraHandle; | ||||
|     } | ||||
|     return ResultSuccess; | ||||
| } | ||||
|  | ||||
| Core::IrSensor::DeviceFormat& IRS::GetIrCameraSharedMemoryDeviceEntry( | ||||
|     const Core::IrSensor::IrCameraHandle& camera_handle) { | ||||
|     ASSERT_MSG(sizeof(StatusManager::device) > camera_handle.npad_id, "invalid npad_id"); | ||||
|     return shared_memory->device[camera_handle.npad_id]; | ||||
| } | ||||
|  | ||||
| IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} { | ||||
|     // clang-format off | ||||
| @@ -395,4 +561,4 @@ IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} { | ||||
|  | ||||
| IRS_SYS::~IRS_SYS() = default; | ||||
|  | ||||
| } // namespace Service::HID | ||||
| } // namespace Service::IRS | ||||
|   | ||||
| @@ -4,13 +4,19 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "core/hid/hid_types.h" | ||||
| #include "core/hid/irs_types.h" | ||||
| #include "core/hle/service/hid/irsensor/processor_base.h" | ||||
| #include "core/hle/service/service.h" | ||||
|  | ||||
| namespace Core { | ||||
| class System; | ||||
| } | ||||
|  | ||||
| namespace Service::HID { | ||||
| namespace Core::HID { | ||||
| class EmulatedController; | ||||
| } // namespace Core::HID | ||||
|  | ||||
| namespace Service::IRS { | ||||
|  | ||||
| class IRS final : public ServiceFramework<IRS> { | ||||
| public: | ||||
| @@ -18,234 +24,19 @@ public: | ||||
|     ~IRS() override; | ||||
|  | ||||
| private: | ||||
|     // This is nn::irsensor::IrCameraStatus | ||||
|     enum IrCameraStatus : u32 { | ||||
|         Available, | ||||
|         Unsupported, | ||||
|         Unconnected, | ||||
|     // This is nn::irsensor::detail::AruidFormat | ||||
|     struct AruidFormat { | ||||
|         u64 sensor_aruid; | ||||
|         u64 sensor_aruid_status; | ||||
|     }; | ||||
|     static_assert(sizeof(AruidFormat) == 0x10, "AruidFormat is an invalid size"); | ||||
|  | ||||
|     // This is nn::irsensor::IrCameraInternalStatus | ||||
|     enum IrCameraInternalStatus : u32 { | ||||
|         Stopped, | ||||
|         FirmwareUpdateNeeded, | ||||
|         Unkown2, | ||||
|         Unkown3, | ||||
|         Unkown4, | ||||
|         FirmwareVersionRequested, | ||||
|         FirmwareVersionIsInvalid, | ||||
|         Ready, | ||||
|         Setting, | ||||
|     // This is nn::irsensor::detail::StatusManager | ||||
|     struct StatusManager { | ||||
|         std::array<Core::IrSensor::DeviceFormat, 9> device; | ||||
|         std::array<AruidFormat, 5> aruid; | ||||
|     }; | ||||
|  | ||||
|     // This is nn::irsensor::detail::StatusManager::IrSensorMode | ||||
|     enum IrSensorMode : u64 { | ||||
|         None, | ||||
|         MomentProcessor, | ||||
|         ClusteringProcessor, | ||||
|         ImageTransferProcessor, | ||||
|         PointingProcessorMarker, | ||||
|         TeraPluginProcessor, | ||||
|         IrLedProcessor, | ||||
|     }; | ||||
|  | ||||
|     // This is nn::irsensor::ImageProcessorStatus | ||||
|     enum ImageProcessorStatus : u8 { | ||||
|         stopped, | ||||
|         running, | ||||
|     }; | ||||
|  | ||||
|     // This is nn::irsensor::ImageTransferProcessorFormat | ||||
|     enum ImageTransferProcessorFormat : u8 { | ||||
|         Size320x240, | ||||
|         Size160x120, | ||||
|         Size80x60, | ||||
|         Size40x30, | ||||
|         Size20x15, | ||||
|     }; | ||||
|  | ||||
|     // This is nn::irsensor::AdaptiveClusteringMode | ||||
|     enum AdaptiveClusteringMode : u8 { | ||||
|         StaticFov, | ||||
|         DynamicFov, | ||||
|     }; | ||||
|  | ||||
|     // This is nn::irsensor::AdaptiveClusteringTargetDistance | ||||
|     enum AdaptiveClusteringTargetDistance : u8 { | ||||
|         Near, | ||||
|         Middle, | ||||
|         Far, | ||||
|     }; | ||||
|  | ||||
|     // This is nn::irsensor::IrsHandAnalysisMode | ||||
|     enum IrsHandAnalysisMode : u8 { | ||||
|         Silhouette, | ||||
|         Image, | ||||
|         SilhoueteAndImage, | ||||
|         SilhuetteOnly, | ||||
|     }; | ||||
|  | ||||
|     // This is nn::irsensor::IrSensorFunctionLevel | ||||
|     enum IrSensorFunctionLevel : u8 { | ||||
|         unknown0, | ||||
|         unknown1, | ||||
|         unknown2, | ||||
|         unknown3, | ||||
|         unknown4, | ||||
|     }; | ||||
|  | ||||
|     // This is nn::irsensor::IrCameraHandle | ||||
|     struct IrCameraHandle { | ||||
|         u8 npad_id{}; | ||||
|         Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None}; | ||||
|         INSERT_PADDING_BYTES(2); | ||||
|     }; | ||||
|     static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size"); | ||||
|  | ||||
|     struct IrsRect { | ||||
|         s16 x; | ||||
|         s16 y; | ||||
|         s16 width; | ||||
|         s16 height; | ||||
|     }; | ||||
|  | ||||
|     // This is nn::irsensor::PackedMcuVersion | ||||
|     struct PackedMcuVersion { | ||||
|         u16 major; | ||||
|         u16 minor; | ||||
|     }; | ||||
|     static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size"); | ||||
|  | ||||
|     // This is nn::irsensor::MomentProcessorConfig | ||||
|     struct MomentProcessorConfig { | ||||
|         u64 exposire_time; | ||||
|         u8 light_target; | ||||
|         u8 gain; | ||||
|         u8 is_negative_used; | ||||
|         INSERT_PADDING_BYTES(7); | ||||
|         IrsRect window_of_interest; | ||||
|         u8 preprocess; | ||||
|         u8 preprocess_intensity_threshold; | ||||
|         INSERT_PADDING_BYTES(5); | ||||
|     }; | ||||
|     static_assert(sizeof(MomentProcessorConfig) == 0x28, | ||||
|                   "MomentProcessorConfig is an invalid size"); | ||||
|  | ||||
|     // This is nn::irsensor::PackedMomentProcessorConfig | ||||
|     struct PackedMomentProcessorConfig { | ||||
|         u64 exposire_time; | ||||
|         u8 light_target; | ||||
|         u8 gain; | ||||
|         u8 is_negative_used; | ||||
|         INSERT_PADDING_BYTES(5); | ||||
|         IrsRect window_of_interest; | ||||
|         PackedMcuVersion required_mcu_version; | ||||
|         u8 preprocess; | ||||
|         u8 preprocess_intensity_threshold; | ||||
|         INSERT_PADDING_BYTES(2); | ||||
|     }; | ||||
|     static_assert(sizeof(PackedMomentProcessorConfig) == 0x20, | ||||
|                   "PackedMomentProcessorConfig is an invalid size"); | ||||
|  | ||||
|     // This is nn::irsensor::ClusteringProcessorConfig | ||||
|     struct ClusteringProcessorConfig { | ||||
|         u64 exposire_time; | ||||
|         u32 light_target; | ||||
|         u32 gain; | ||||
|         u8 is_negative_used; | ||||
|         INSERT_PADDING_BYTES(7); | ||||
|         IrsRect window_of_interest; | ||||
|         u32 pixel_count_min; | ||||
|         u32 pixel_count_max; | ||||
|         u32 object_intensity_min; | ||||
|         u8 is_external_light_filter_enabled; | ||||
|         INSERT_PADDING_BYTES(3); | ||||
|     }; | ||||
|     static_assert(sizeof(ClusteringProcessorConfig) == 0x30, | ||||
|                   "ClusteringProcessorConfig is an invalid size"); | ||||
|  | ||||
|     // This is nn::irsensor::PackedClusteringProcessorConfig | ||||
|     struct PackedClusteringProcessorConfig { | ||||
|         u64 exposire_time; | ||||
|         u8 light_target; | ||||
|         u8 gain; | ||||
|         u8 is_negative_used; | ||||
|         INSERT_PADDING_BYTES(5); | ||||
|         IrsRect window_of_interest; | ||||
|         PackedMcuVersion required_mcu_version; | ||||
|         u32 pixel_count_min; | ||||
|         u32 pixel_count_max; | ||||
|         u32 object_intensity_min; | ||||
|         u8 is_external_light_filter_enabled; | ||||
|         INSERT_PADDING_BYTES(2); | ||||
|     }; | ||||
|     static_assert(sizeof(PackedClusteringProcessorConfig) == 0x30, | ||||
|                   "PackedClusteringProcessorConfig is an invalid size"); | ||||
|  | ||||
|     // This is nn::irsensor::PackedImageTransferProcessorConfig | ||||
|     struct PackedImageTransferProcessorConfig { | ||||
|         u64 exposire_time; | ||||
|         u8 light_target; | ||||
|         u8 gain; | ||||
|         u8 is_negative_used; | ||||
|         INSERT_PADDING_BYTES(5); | ||||
|         PackedMcuVersion required_mcu_version; | ||||
|         u8 format; | ||||
|         INSERT_PADDING_BYTES(3); | ||||
|     }; | ||||
|     static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18, | ||||
|                   "PackedImageTransferProcessorConfig is an invalid size"); | ||||
|  | ||||
|     // This is nn::irsensor::PackedTeraPluginProcessorConfig | ||||
|     struct PackedTeraPluginProcessorConfig { | ||||
|         PackedMcuVersion required_mcu_version; | ||||
|         u8 mode; | ||||
|         INSERT_PADDING_BYTES(3); | ||||
|     }; | ||||
|     static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8, | ||||
|                   "PackedTeraPluginProcessorConfig is an invalid size"); | ||||
|  | ||||
|     // This is nn::irsensor::PackedPointingProcessorConfig | ||||
|     struct PackedPointingProcessorConfig { | ||||
|         IrsRect window_of_interest; | ||||
|         PackedMcuVersion required_mcu_version; | ||||
|     }; | ||||
|     static_assert(sizeof(PackedPointingProcessorConfig) == 0xC, | ||||
|                   "PackedPointingProcessorConfig is an invalid size"); | ||||
|  | ||||
|     // This is nn::irsensor::PackedFunctionLevel | ||||
|     struct PackedFunctionLevel { | ||||
|         IrSensorFunctionLevel function_level; | ||||
|         INSERT_PADDING_BYTES(3); | ||||
|     }; | ||||
|     static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size"); | ||||
|  | ||||
|     // This is nn::irsensor::PackedImageTransferProcessorExConfig | ||||
|     struct PackedImageTransferProcessorExConfig { | ||||
|         u64 exposire_time; | ||||
|         u8 light_target; | ||||
|         u8 gain; | ||||
|         u8 is_negative_used; | ||||
|         INSERT_PADDING_BYTES(5); | ||||
|         PackedMcuVersion required_mcu_version; | ||||
|         ImageTransferProcessorFormat origin_format; | ||||
|         ImageTransferProcessorFormat trimming_format; | ||||
|         u16 trimming_start_x; | ||||
|         u16 trimming_start_y; | ||||
|         u8 is_external_light_filter_enabled; | ||||
|         INSERT_PADDING_BYTES(3); | ||||
|     }; | ||||
|     static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20, | ||||
|                   "PackedImageTransferProcessorExConfig is an invalid size"); | ||||
|  | ||||
|     // This is nn::irsensor::PackedIrLedProcessorConfig | ||||
|     struct PackedIrLedProcessorConfig { | ||||
|         PackedMcuVersion required_mcu_version; | ||||
|         u8 light_target; | ||||
|         INSERT_PADDING_BYTES(3); | ||||
|     }; | ||||
|     static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8, | ||||
|                   "PackedIrLedProcessorConfig is an invalid size"); | ||||
|     static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size"); | ||||
|  | ||||
|     void ActivateIrsensor(Kernel::HLERequestContext& ctx); | ||||
|     void DeactivateIrsensor(Kernel::HLERequestContext& ctx); | ||||
| @@ -265,6 +56,56 @@ private: | ||||
|     void RunIrLedProcessor(Kernel::HLERequestContext& ctx); | ||||
|     void StopImageProcessorAsync(Kernel::HLERequestContext& ctx); | ||||
|     void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx); | ||||
|  | ||||
|     Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const; | ||||
|     Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry( | ||||
|         const Core::IrSensor::IrCameraHandle& camera_handle); | ||||
|  | ||||
|     template <typename T> | ||||
|     void MakeProcessor(const Core::IrSensor::IrCameraHandle& handle, | ||||
|                        Core::IrSensor::DeviceFormat& device_state) { | ||||
|         const auto index = static_cast<std::size_t>(handle.npad_id); | ||||
|         if (index > sizeof(processors)) { | ||||
|             LOG_CRITICAL(Service_IRS, "Invalid index {}", index); | ||||
|             return; | ||||
|         } | ||||
|         processors[index] = std::make_unique<T>(device_state); | ||||
|     } | ||||
|  | ||||
|     template <typename T> | ||||
|     void MakeProcessorWithCoreContext(const Core::IrSensor::IrCameraHandle& handle, | ||||
|                                       Core::IrSensor::DeviceFormat& device_state) { | ||||
|         const auto index = static_cast<std::size_t>(handle.npad_id); | ||||
|         if (index > sizeof(processors)) { | ||||
|             LOG_CRITICAL(Service_IRS, "Invalid index {}", index); | ||||
|             return; | ||||
|         } | ||||
|         processors[index] = std::make_unique<T>(system.HIDCore(), device_state, index); | ||||
|     } | ||||
|  | ||||
|     template <typename T> | ||||
|     T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) { | ||||
|         const auto index = static_cast<std::size_t>(handle.npad_id); | ||||
|         if (index > sizeof(processors)) { | ||||
|             LOG_CRITICAL(Service_IRS, "Invalid index {}", index); | ||||
|             return static_cast<T&>(*processors[0]); | ||||
|         } | ||||
|         return static_cast<T&>(*processors[index]); | ||||
|     } | ||||
|  | ||||
|     template <typename T> | ||||
|     const T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) const { | ||||
|         const auto index = static_cast<std::size_t>(handle.npad_id); | ||||
|         if (index > sizeof(processors)) { | ||||
|             LOG_CRITICAL(Service_IRS, "Invalid index {}", index); | ||||
|             return static_cast<T&>(*processors[0]); | ||||
|         } | ||||
|         return static_cast<T&>(*processors[index]); | ||||
|     } | ||||
|  | ||||
|     Core::HID::EmulatedController* npad_device = nullptr; | ||||
|     StatusManager* shared_memory = nullptr; | ||||
|     std::array<std::unique_ptr<ProcessorBase>, 9> processors{}; | ||||
| }; | ||||
|  | ||||
| class IRS_SYS final : public ServiceFramework<IRS_SYS> { | ||||
| @@ -273,4 +114,4 @@ public: | ||||
|     ~IRS_SYS() override; | ||||
| }; | ||||
|  | ||||
| } // namespace Service::HID | ||||
| } // namespace Service::IRS | ||||
|   | ||||
							
								
								
									
										34
									
								
								src/core/hle/service/hid/irsensor/clustering_processor.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/core/hle/service/hid/irsensor/clustering_processor.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
| #include "core/hle/service/hid/irsensor/clustering_processor.h" | ||||
|  | ||||
| namespace Service::IRS { | ||||
| ClusteringProcessor::ClusteringProcessor(Core::IrSensor::DeviceFormat& device_format) | ||||
|     : device(device_format) { | ||||
|     device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor; | ||||
|     device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; | ||||
|     device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; | ||||
| } | ||||
|  | ||||
| ClusteringProcessor::~ClusteringProcessor() = default; | ||||
|  | ||||
| void ClusteringProcessor::StartProcessor() {} | ||||
|  | ||||
| void ClusteringProcessor::SuspendProcessor() {} | ||||
|  | ||||
| void ClusteringProcessor::StopProcessor() {} | ||||
|  | ||||
| void ClusteringProcessor::SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config) { | ||||
|     current_config.camera_config.exposure_time = config.camera_config.exposure_time; | ||||
|     current_config.camera_config.gain = config.camera_config.gain; | ||||
|     current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; | ||||
|     current_config.camera_config.light_target = | ||||
|         static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); | ||||
|     current_config.pixel_count_min = config.pixel_count_min; | ||||
|     current_config.pixel_count_max = config.pixel_count_max; | ||||
|     current_config.is_external_light_filter_enabled = config.is_external_light_filter_enabled; | ||||
|     current_config.object_intensity_min = config.object_intensity_min; | ||||
| } | ||||
|  | ||||
| } // namespace Service::IRS | ||||
							
								
								
									
										74
									
								
								src/core/hle/service/hid/irsensor/clustering_processor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								src/core/hle/service/hid/irsensor/clustering_processor.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "common/common_types.h" | ||||
| #include "core/hid/irs_types.h" | ||||
| #include "core/hle/service/hid/irsensor/processor_base.h" | ||||
|  | ||||
| namespace Service::IRS { | ||||
| class ClusteringProcessor final : public ProcessorBase { | ||||
| public: | ||||
|     explicit ClusteringProcessor(Core::IrSensor::DeviceFormat& device_format); | ||||
|     ~ClusteringProcessor() override; | ||||
|  | ||||
|     // Called when the processor is initialized | ||||
|     void StartProcessor() override; | ||||
|  | ||||
|     // Called when the processor is suspended | ||||
|     void SuspendProcessor() override; | ||||
|  | ||||
|     // Called when the processor is stopped | ||||
|     void StopProcessor() override; | ||||
|  | ||||
|     // Sets config parameters of the camera | ||||
|     void SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config); | ||||
|  | ||||
| private: | ||||
|     // This is nn::irsensor::ClusteringProcessorConfig | ||||
|     struct ClusteringProcessorConfig { | ||||
|         Core::IrSensor::CameraConfig camera_config; | ||||
|         Core::IrSensor::IrsRect window_of_interest; | ||||
|         u32 pixel_count_min; | ||||
|         u32 pixel_count_max; | ||||
|         u32 object_intensity_min; | ||||
|         bool is_external_light_filter_enabled; | ||||
|         INSERT_PADDING_BYTES(3); | ||||
|     }; | ||||
|     static_assert(sizeof(ClusteringProcessorConfig) == 0x30, | ||||
|                   "ClusteringProcessorConfig is an invalid size"); | ||||
|  | ||||
|     // This is nn::irsensor::AdaptiveClusteringProcessorConfig | ||||
|     struct AdaptiveClusteringProcessorConfig { | ||||
|         Core::IrSensor::AdaptiveClusteringMode mode; | ||||
|         Core::IrSensor::AdaptiveClusteringTargetDistance target_distance; | ||||
|     }; | ||||
|     static_assert(sizeof(AdaptiveClusteringProcessorConfig) == 0x8, | ||||
|                   "AdaptiveClusteringProcessorConfig is an invalid size"); | ||||
|  | ||||
|     // This is nn::irsensor::ClusteringData | ||||
|     struct ClusteringData { | ||||
|         f32 average_intensity; | ||||
|         Core::IrSensor::IrsCentroid centroid; | ||||
|         u32 pixel_count; | ||||
|         Core::IrSensor::IrsRect bound; | ||||
|     }; | ||||
|     static_assert(sizeof(ClusteringData) == 0x18, "ClusteringData is an invalid size"); | ||||
|  | ||||
|     // This is nn::irsensor::ClusteringProcessorState | ||||
|     struct ClusteringProcessorState { | ||||
|         s64 sampling_number; | ||||
|         u64 timestamp; | ||||
|         u8 object_count; | ||||
|         INSERT_PADDING_BYTES(3); | ||||
|         Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; | ||||
|         std::array<ClusteringData, 0x10> data; | ||||
|     }; | ||||
|     static_assert(sizeof(ClusteringProcessorState) == 0x198, | ||||
|                   "ClusteringProcessorState is an invalid size"); | ||||
|  | ||||
|     ClusteringProcessorConfig current_config{}; | ||||
|     Core::IrSensor::DeviceFormat& device; | ||||
| }; | ||||
| } // namespace Service::IRS | ||||
							
								
								
									
										150
									
								
								src/core/hle/service/hid/irsensor/image_transfer_processor.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								src/core/hle/service/hid/irsensor/image_transfer_processor.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,150 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
| #include "core/hid/emulated_controller.h" | ||||
| #include "core/hid/hid_core.h" | ||||
| #include "core/hle/service/hid/irsensor/image_transfer_processor.h" | ||||
|  | ||||
| namespace Service::IRS { | ||||
| ImageTransferProcessor::ImageTransferProcessor(Core::HID::HIDCore& hid_core_, | ||||
|                                                Core::IrSensor::DeviceFormat& device_format, | ||||
|                                                std::size_t npad_index) | ||||
|     : device{device_format} { | ||||
|     npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index); | ||||
|  | ||||
|     Core::HID::ControllerUpdateCallback engine_callback{ | ||||
|         .on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); }, | ||||
|         .is_npad_service = true, | ||||
|     }; | ||||
|     callback_key = npad_device->SetCallback(engine_callback); | ||||
|  | ||||
|     device.mode = Core::IrSensor::IrSensorMode::ImageTransferProcessor; | ||||
|     device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; | ||||
|     device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; | ||||
| } | ||||
|  | ||||
| ImageTransferProcessor::~ImageTransferProcessor() { | ||||
|     npad_device->DeleteCallback(callback_key); | ||||
| }; | ||||
|  | ||||
| void ImageTransferProcessor::StartProcessor() { | ||||
|     is_active = true; | ||||
|     device.camera_status = Core::IrSensor::IrCameraStatus::Available; | ||||
|     device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready; | ||||
|     processor_state.sampling_number = 0; | ||||
|     processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; | ||||
| } | ||||
|  | ||||
| void ImageTransferProcessor::SuspendProcessor() {} | ||||
|  | ||||
| void ImageTransferProcessor::StopProcessor() {} | ||||
|  | ||||
| void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) { | ||||
|     if (type != Core::HID::ControllerTriggerType::IrSensor) { | ||||
|         return; | ||||
|     } | ||||
|     if (!is_transfer_memory_set) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const auto camera_data = npad_device->GetCamera(); | ||||
|  | ||||
|     // This indicates how much ambient light is precent | ||||
|     processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; | ||||
|     processor_state.sampling_number = camera_data.sample; | ||||
|  | ||||
|     if (camera_data.format != current_config.origin_format) { | ||||
|         LOG_WARNING(Service_IRS, "Wrong Input format {} expected {}", camera_data.format, | ||||
|                     current_config.origin_format); | ||||
|         memset(transfer_memory, 0, GetDataSize(current_config.trimming_format)); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (current_config.origin_format > current_config.trimming_format) { | ||||
|         LOG_WARNING(Service_IRS, "Origin format {} is smaller than trimming format {}", | ||||
|                     current_config.origin_format, current_config.trimming_format); | ||||
|         memset(transfer_memory, 0, GetDataSize(current_config.trimming_format)); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     std::vector<u8> window_data{}; | ||||
|     const auto origin_width = GetDataWidth(current_config.origin_format); | ||||
|     const auto origin_height = GetDataHeight(current_config.origin_format); | ||||
|     const auto trimming_width = GetDataWidth(current_config.trimming_format); | ||||
|     const auto trimming_height = GetDataHeight(current_config.trimming_format); | ||||
|     window_data.resize(GetDataSize(current_config.trimming_format)); | ||||
|  | ||||
|     if (trimming_width + current_config.trimming_start_x > origin_width || | ||||
|         trimming_height + current_config.trimming_start_y > origin_height) { | ||||
|         LOG_WARNING(Service_IRS, | ||||
|                     "Trimming area ({}, {}, {}, {}) is outside of origin area ({}, {})", | ||||
|                     current_config.trimming_start_x, current_config.trimming_start_y, | ||||
|                     trimming_width, trimming_height, origin_width, origin_height); | ||||
|         memset(transfer_memory, 0, GetDataSize(current_config.trimming_format)); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     for (std::size_t y = 0; y < trimming_height; y++) { | ||||
|         for (std::size_t x = 0; x < trimming_width; x++) { | ||||
|             const std::size_t window_index = (y * trimming_width) + x; | ||||
|             const std::size_t origin_index = | ||||
|                 ((y + current_config.trimming_start_y) * origin_width) + x + | ||||
|                 current_config.trimming_start_x; | ||||
|             window_data[window_index] = camera_data.data[origin_index]; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     memcpy(transfer_memory, window_data.data(), GetDataSize(current_config.trimming_format)); | ||||
|  | ||||
|     if (!IsProcessorActive()) { | ||||
|         StartProcessor(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void ImageTransferProcessor::SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config) { | ||||
|     current_config.camera_config.exposure_time = config.camera_config.exposure_time; | ||||
|     current_config.camera_config.gain = config.camera_config.gain; | ||||
|     current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; | ||||
|     current_config.camera_config.light_target = | ||||
|         static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); | ||||
|     current_config.origin_format = | ||||
|         static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format); | ||||
|     current_config.trimming_format = | ||||
|         static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format); | ||||
|     current_config.trimming_start_x = 0; | ||||
|     current_config.trimming_start_y = 0; | ||||
|  | ||||
|     npad_device->SetCameraFormat(current_config.origin_format); | ||||
| } | ||||
|  | ||||
| void ImageTransferProcessor::SetConfig( | ||||
|     Core::IrSensor::PackedImageTransferProcessorExConfig config) { | ||||
|     current_config.camera_config.exposure_time = config.camera_config.exposure_time; | ||||
|     current_config.camera_config.gain = config.camera_config.gain; | ||||
|     current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; | ||||
|     current_config.camera_config.light_target = | ||||
|         static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); | ||||
|     current_config.origin_format = | ||||
|         static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.origin_format); | ||||
|     current_config.trimming_format = | ||||
|         static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.trimming_format); | ||||
|     current_config.trimming_start_x = config.trimming_start_x; | ||||
|     current_config.trimming_start_y = config.trimming_start_y; | ||||
|  | ||||
|     npad_device->SetCameraFormat(current_config.origin_format); | ||||
| } | ||||
|  | ||||
| void ImageTransferProcessor::SetTransferMemoryPointer(u8* t_mem) { | ||||
|     is_transfer_memory_set = true; | ||||
|     transfer_memory = t_mem; | ||||
| } | ||||
|  | ||||
| Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState( | ||||
|     std::vector<u8>& data) const { | ||||
|     const auto size = GetDataSize(current_config.trimming_format); | ||||
|     data.resize(size); | ||||
|     memcpy(data.data(), transfer_memory, size); | ||||
|     return processor_state; | ||||
| } | ||||
|  | ||||
| } // namespace Service::IRS | ||||
							
								
								
									
										73
									
								
								src/core/hle/service/hid/irsensor/image_transfer_processor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/core/hle/service/hid/irsensor/image_transfer_processor.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "common/common_types.h" | ||||
| #include "core/hid/irs_types.h" | ||||
| #include "core/hle/service/hid/irsensor/processor_base.h" | ||||
|  | ||||
| namespace Core::HID { | ||||
| class EmulatedController; | ||||
| } // namespace Core::HID | ||||
|  | ||||
| namespace Service::IRS { | ||||
| class ImageTransferProcessor final : public ProcessorBase { | ||||
| public: | ||||
|     explicit ImageTransferProcessor(Core::HID::HIDCore& hid_core_, | ||||
|                                     Core::IrSensor::DeviceFormat& device_format, | ||||
|                                     std::size_t npad_index); | ||||
|     ~ImageTransferProcessor() override; | ||||
|  | ||||
|     // Called when the processor is initialized | ||||
|     void StartProcessor() override; | ||||
|  | ||||
|     // Called when the processor is suspended | ||||
|     void SuspendProcessor() override; | ||||
|  | ||||
|     // Called when the processor is stopped | ||||
|     void StopProcessor() override; | ||||
|  | ||||
|     // Sets config parameters of the camera | ||||
|     void SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config); | ||||
|     void SetConfig(Core::IrSensor::PackedImageTransferProcessorExConfig config); | ||||
|  | ||||
|     // Transfer memory where the image data will be stored | ||||
|     void SetTransferMemoryPointer(u8* t_mem); | ||||
|  | ||||
|     Core::IrSensor::ImageTransferProcessorState GetState(std::vector<u8>& data) const; | ||||
|  | ||||
| private: | ||||
|     // This is nn::irsensor::ImageTransferProcessorConfig | ||||
|     struct ImageTransferProcessorConfig { | ||||
|         Core::IrSensor::CameraConfig camera_config; | ||||
|         Core::IrSensor::ImageTransferProcessorFormat format; | ||||
|     }; | ||||
|     static_assert(sizeof(ImageTransferProcessorConfig) == 0x20, | ||||
|                   "ImageTransferProcessorConfig is an invalid size"); | ||||
|  | ||||
|     // This is nn::irsensor::ImageTransferProcessorExConfig | ||||
|     struct ImageTransferProcessorExConfig { | ||||
|         Core::IrSensor::CameraConfig camera_config; | ||||
|         Core::IrSensor::ImageTransferProcessorFormat origin_format; | ||||
|         Core::IrSensor::ImageTransferProcessorFormat trimming_format; | ||||
|         u16 trimming_start_x; | ||||
|         u16 trimming_start_y; | ||||
|         bool is_external_light_filter_enabled; | ||||
|         INSERT_PADDING_BYTES(3); | ||||
|     }; | ||||
|     static_assert(sizeof(ImageTransferProcessorExConfig) == 0x28, | ||||
|                   "ImageTransferProcessorExConfig is an invalid size"); | ||||
|  | ||||
|     void OnControllerUpdate(Core::HID::ControllerTriggerType type); | ||||
|  | ||||
|     ImageTransferProcessorExConfig current_config{}; | ||||
|     Core::IrSensor::ImageTransferProcessorState processor_state{}; | ||||
|     Core::IrSensor::DeviceFormat& device; | ||||
|     Core::HID::EmulatedController* npad_device; | ||||
|     int callback_key{}; | ||||
|  | ||||
|     u8* transfer_memory = nullptr; | ||||
|     bool is_transfer_memory_set = false; | ||||
| }; | ||||
| } // namespace Service::IRS | ||||
							
								
								
									
										27
									
								
								src/core/hle/service/hid/irsensor/ir_led_processor.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/core/hle/service/hid/irsensor/ir_led_processor.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
| #include "core/hle/service/hid/irsensor/ir_led_processor.h" | ||||
|  | ||||
| namespace Service::IRS { | ||||
| IrLedProcessor::IrLedProcessor(Core::IrSensor::DeviceFormat& device_format) | ||||
|     : device(device_format) { | ||||
|     device.mode = Core::IrSensor::IrSensorMode::IrLedProcessor; | ||||
|     device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; | ||||
|     device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; | ||||
| } | ||||
|  | ||||
| IrLedProcessor::~IrLedProcessor() = default; | ||||
|  | ||||
| void IrLedProcessor::StartProcessor() {} | ||||
|  | ||||
| void IrLedProcessor::SuspendProcessor() {} | ||||
|  | ||||
| void IrLedProcessor::StopProcessor() {} | ||||
|  | ||||
| void IrLedProcessor::SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config) { | ||||
|     current_config.light_target = | ||||
|         static_cast<Core::IrSensor::CameraLightTarget>(config.light_target); | ||||
| } | ||||
|  | ||||
| } // namespace Service::IRS | ||||
							
								
								
									
										47
									
								
								src/core/hle/service/hid/irsensor/ir_led_processor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/core/hle/service/hid/irsensor/ir_led_processor.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "common/bit_field.h" | ||||
| #include "common/common_types.h" | ||||
| #include "core/hid/irs_types.h" | ||||
| #include "core/hle/service/hid/irsensor/processor_base.h" | ||||
|  | ||||
| namespace Service::IRS { | ||||
| class IrLedProcessor final : public ProcessorBase { | ||||
| public: | ||||
|     explicit IrLedProcessor(Core::IrSensor::DeviceFormat& device_format); | ||||
|     ~IrLedProcessor() override; | ||||
|  | ||||
|     // Called when the processor is initialized | ||||
|     void StartProcessor() override; | ||||
|  | ||||
|     // Called when the processor is suspended | ||||
|     void SuspendProcessor() override; | ||||
|  | ||||
|     // Called when the processor is stopped | ||||
|     void StopProcessor() override; | ||||
|  | ||||
|     // Sets config parameters of the camera | ||||
|     void SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config); | ||||
|  | ||||
| private: | ||||
|     // This is nn::irsensor::IrLedProcessorConfig | ||||
|     struct IrLedProcessorConfig { | ||||
|         Core::IrSensor::CameraLightTarget light_target; | ||||
|     }; | ||||
|     static_assert(sizeof(IrLedProcessorConfig) == 0x4, "IrLedProcessorConfig is an invalid size"); | ||||
|  | ||||
|     struct IrLedProcessorState { | ||||
|         s64 sampling_number; | ||||
|         u64 timestamp; | ||||
|         std::array<u8, 0x8> data; | ||||
|     }; | ||||
|     static_assert(sizeof(IrLedProcessorState) == 0x18, "IrLedProcessorState is an invalid size"); | ||||
|  | ||||
|     IrLedProcessorConfig current_config{}; | ||||
|     Core::IrSensor::DeviceFormat& device; | ||||
| }; | ||||
|  | ||||
| } // namespace Service::IRS | ||||
							
								
								
									
										34
									
								
								src/core/hle/service/hid/irsensor/moment_processor.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/core/hle/service/hid/irsensor/moment_processor.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
| #include "core/hle/service/hid/irsensor/moment_processor.h" | ||||
|  | ||||
| namespace Service::IRS { | ||||
| MomentProcessor::MomentProcessor(Core::IrSensor::DeviceFormat& device_format) | ||||
|     : device(device_format) { | ||||
|     device.mode = Core::IrSensor::IrSensorMode::MomentProcessor; | ||||
|     device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; | ||||
|     device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; | ||||
| } | ||||
|  | ||||
| MomentProcessor::~MomentProcessor() = default; | ||||
|  | ||||
| void MomentProcessor::StartProcessor() {} | ||||
|  | ||||
| void MomentProcessor::SuspendProcessor() {} | ||||
|  | ||||
| void MomentProcessor::StopProcessor() {} | ||||
|  | ||||
| void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig config) { | ||||
|     current_config.camera_config.exposure_time = config.camera_config.exposure_time; | ||||
|     current_config.camera_config.gain = config.camera_config.gain; | ||||
|     current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; | ||||
|     current_config.camera_config.light_target = | ||||
|         static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); | ||||
|     current_config.window_of_interest = config.window_of_interest; | ||||
|     current_config.preprocess = | ||||
|         static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess); | ||||
|     current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold; | ||||
| } | ||||
|  | ||||
| } // namespace Service::IRS | ||||
							
								
								
									
										61
									
								
								src/core/hle/service/hid/irsensor/moment_processor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/core/hle/service/hid/irsensor/moment_processor.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "common/bit_field.h" | ||||
| #include "common/common_types.h" | ||||
| #include "core/hid/irs_types.h" | ||||
| #include "core/hle/service/hid/irsensor/processor_base.h" | ||||
|  | ||||
| namespace Service::IRS { | ||||
| class MomentProcessor final : public ProcessorBase { | ||||
| public: | ||||
|     explicit MomentProcessor(Core::IrSensor::DeviceFormat& device_format); | ||||
|     ~MomentProcessor() override; | ||||
|  | ||||
|     // Called when the processor is initialized | ||||
|     void StartProcessor() override; | ||||
|  | ||||
|     // Called when the processor is suspended | ||||
|     void SuspendProcessor() override; | ||||
|  | ||||
|     // Called when the processor is stopped | ||||
|     void StopProcessor() override; | ||||
|  | ||||
|     // Sets config parameters of the camera | ||||
|     void SetConfig(Core::IrSensor::PackedMomentProcessorConfig config); | ||||
|  | ||||
| private: | ||||
|     // This is nn::irsensor::MomentProcessorConfig | ||||
|     struct MomentProcessorConfig { | ||||
|         Core::IrSensor::CameraConfig camera_config; | ||||
|         Core::IrSensor::IrsRect window_of_interest; | ||||
|         Core::IrSensor::MomentProcessorPreprocess preprocess; | ||||
|         u32 preprocess_intensity_threshold; | ||||
|     }; | ||||
|     static_assert(sizeof(MomentProcessorConfig) == 0x28, | ||||
|                   "MomentProcessorConfig is an invalid size"); | ||||
|  | ||||
|     // This is nn::irsensor::MomentStatistic | ||||
|     struct MomentStatistic { | ||||
|         f32 average_intensity; | ||||
|         Core::IrSensor::IrsCentroid centroid; | ||||
|     }; | ||||
|     static_assert(sizeof(MomentStatistic) == 0xC, "MomentStatistic is an invalid size"); | ||||
|  | ||||
|     // This is nn::irsensor::MomentProcessorState | ||||
|     struct MomentProcessorState { | ||||
|         s64 sampling_number; | ||||
|         u64 timestamp; | ||||
|         Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; | ||||
|         INSERT_PADDING_BYTES(4); | ||||
|         std::array<MomentStatistic, 0x30> stadistic; | ||||
|     }; | ||||
|     static_assert(sizeof(MomentProcessorState) == 0x258, "MomentProcessorState is an invalid size"); | ||||
|  | ||||
|     MomentProcessorConfig current_config{}; | ||||
|     Core::IrSensor::DeviceFormat& device; | ||||
| }; | ||||
|  | ||||
| } // namespace Service::IRS | ||||
							
								
								
									
										26
									
								
								src/core/hle/service/hid/irsensor/pointing_processor.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/core/hle/service/hid/irsensor/pointing_processor.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
| #include "core/hle/service/hid/irsensor/pointing_processor.h" | ||||
|  | ||||
| namespace Service::IRS { | ||||
| PointingProcessor::PointingProcessor(Core::IrSensor::DeviceFormat& device_format) | ||||
|     : device(device_format) { | ||||
|     device.mode = Core::IrSensor::IrSensorMode::PointingProcessorMarker; | ||||
|     device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; | ||||
|     device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; | ||||
| } | ||||
|  | ||||
| PointingProcessor::~PointingProcessor() = default; | ||||
|  | ||||
| void PointingProcessor::StartProcessor() {} | ||||
|  | ||||
| void PointingProcessor::SuspendProcessor() {} | ||||
|  | ||||
| void PointingProcessor::StopProcessor() {} | ||||
|  | ||||
| void PointingProcessor::SetConfig(Core::IrSensor::PackedPointingProcessorConfig config) { | ||||
|     current_config.window_of_interest = config.window_of_interest; | ||||
| } | ||||
|  | ||||
| } // namespace Service::IRS | ||||
							
								
								
									
										61
									
								
								src/core/hle/service/hid/irsensor/pointing_processor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/core/hle/service/hid/irsensor/pointing_processor.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "common/common_types.h" | ||||
| #include "core/hid/irs_types.h" | ||||
| #include "core/hle/service/hid/irsensor/processor_base.h" | ||||
|  | ||||
| namespace Service::IRS { | ||||
| class PointingProcessor final : public ProcessorBase { | ||||
| public: | ||||
|     explicit PointingProcessor(Core::IrSensor::DeviceFormat& device_format); | ||||
|     ~PointingProcessor() override; | ||||
|  | ||||
|     // Called when the processor is initialized | ||||
|     void StartProcessor() override; | ||||
|  | ||||
|     // Called when the processor is suspended | ||||
|     void SuspendProcessor() override; | ||||
|  | ||||
|     // Called when the processor is stopped | ||||
|     void StopProcessor() override; | ||||
|  | ||||
|     // Sets config parameters of the camera | ||||
|     void SetConfig(Core::IrSensor::PackedPointingProcessorConfig config); | ||||
|  | ||||
| private: | ||||
|     // This is nn::irsensor::PointingProcessorConfig | ||||
|     struct PointingProcessorConfig { | ||||
|         Core::IrSensor::IrsRect window_of_interest; | ||||
|     }; | ||||
|     static_assert(sizeof(PointingProcessorConfig) == 0x8, | ||||
|                   "PointingProcessorConfig is an invalid size"); | ||||
|  | ||||
|     struct PointingProcessorMarkerData { | ||||
|         u8 pointing_status; | ||||
|         INSERT_PADDING_BYTES(3); | ||||
|         u32 unknown; | ||||
|         float unkown_float1; | ||||
|         float position_x; | ||||
|         float position_y; | ||||
|         float unkown_float2; | ||||
|         Core::IrSensor::IrsRect window_of_interest; | ||||
|     }; | ||||
|     static_assert(sizeof(PointingProcessorMarkerData) == 0x20, | ||||
|                   "PointingProcessorMarkerData is an invalid size"); | ||||
|  | ||||
|     struct PointingProcessorMarkerState { | ||||
|         s64 sampling_number; | ||||
|         u64 timestamp; | ||||
|         std::array<PointingProcessorMarkerData, 0x3> data; | ||||
|     }; | ||||
|     static_assert(sizeof(PointingProcessorMarkerState) == 0x70, | ||||
|                   "PointingProcessorMarkerState is an invalid size"); | ||||
|  | ||||
|     PointingProcessorConfig current_config{}; | ||||
|     Core::IrSensor::DeviceFormat& device; | ||||
| }; | ||||
|  | ||||
| } // namespace Service::IRS | ||||
							
								
								
									
										67
									
								
								src/core/hle/service/hid/irsensor/processor_base.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/core/hle/service/hid/irsensor/processor_base.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
| #include "core/hle/service/hid/irsensor/processor_base.h" | ||||
|  | ||||
| namespace Service::IRS { | ||||
|  | ||||
| ProcessorBase::ProcessorBase() {} | ||||
| ProcessorBase::~ProcessorBase() = default; | ||||
|  | ||||
| bool ProcessorBase::IsProcessorActive() const { | ||||
|     return is_active; | ||||
| } | ||||
|  | ||||
| std::size_t ProcessorBase::GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const { | ||||
|     switch (format) { | ||||
|     case Core::IrSensor::ImageTransferProcessorFormat::Size320x240: | ||||
|         return 320 * 240; | ||||
|     case Core::IrSensor::ImageTransferProcessorFormat::Size160x120: | ||||
|         return 160 * 120; | ||||
|     case Core::IrSensor::ImageTransferProcessorFormat::Size80x60: | ||||
|         return 80 * 60; | ||||
|     case Core::IrSensor::ImageTransferProcessorFormat::Size40x30: | ||||
|         return 40 * 30; | ||||
|     case Core::IrSensor::ImageTransferProcessorFormat::Size20x15: | ||||
|         return 20 * 15; | ||||
|     default: | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| std::size_t ProcessorBase::GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const { | ||||
|     switch (format) { | ||||
|     case Core::IrSensor::ImageTransferProcessorFormat::Size320x240: | ||||
|         return 320; | ||||
|     case Core::IrSensor::ImageTransferProcessorFormat::Size160x120: | ||||
|         return 160; | ||||
|     case Core::IrSensor::ImageTransferProcessorFormat::Size80x60: | ||||
|         return 80; | ||||
|     case Core::IrSensor::ImageTransferProcessorFormat::Size40x30: | ||||
|         return 40; | ||||
|     case Core::IrSensor::ImageTransferProcessorFormat::Size20x15: | ||||
|         return 20; | ||||
|     default: | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| std::size_t ProcessorBase::GetDataHeight( | ||||
|     Core::IrSensor::ImageTransferProcessorFormat format) const { | ||||
|     switch (format) { | ||||
|     case Core::IrSensor::ImageTransferProcessorFormat::Size320x240: | ||||
|         return 240; | ||||
|     case Core::IrSensor::ImageTransferProcessorFormat::Size160x120: | ||||
|         return 120; | ||||
|     case Core::IrSensor::ImageTransferProcessorFormat::Size80x60: | ||||
|         return 60; | ||||
|     case Core::IrSensor::ImageTransferProcessorFormat::Size40x30: | ||||
|         return 30; | ||||
|     case Core::IrSensor::ImageTransferProcessorFormat::Size20x15: | ||||
|         return 15; | ||||
|     default: | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| } // namespace Service::IRS | ||||
							
								
								
									
										33
									
								
								src/core/hle/service/hid/irsensor/processor_base.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/core/hle/service/hid/irsensor/processor_base.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "common/common_types.h" | ||||
| #include "core/hid/irs_types.h" | ||||
|  | ||||
| namespace Service::IRS { | ||||
| class ProcessorBase { | ||||
| public: | ||||
|     explicit ProcessorBase(); | ||||
|     virtual ~ProcessorBase(); | ||||
|  | ||||
|     virtual void StartProcessor() = 0; | ||||
|     virtual void SuspendProcessor() = 0; | ||||
|     virtual void StopProcessor() = 0; | ||||
|  | ||||
|     bool IsProcessorActive() const; | ||||
|  | ||||
| protected: | ||||
|     /// Returns the number of bytes the image uses | ||||
|     std::size_t GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const; | ||||
|  | ||||
|     /// Returns the width of the image | ||||
|     std::size_t GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const; | ||||
|  | ||||
|     /// Returns the height of the image | ||||
|     std::size_t GetDataHeight(Core::IrSensor::ImageTransferProcessorFormat format) const; | ||||
|  | ||||
|     bool is_active{false}; | ||||
| }; | ||||
| } // namespace Service::IRS | ||||
							
								
								
									
										29
									
								
								src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
| #include "core/hle/service/hid/irsensor/tera_plugin_processor.h" | ||||
|  | ||||
| namespace Service::IRS { | ||||
| TeraPluginProcessor::TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format) | ||||
|     : device(device_format) { | ||||
|     device.mode = Core::IrSensor::IrSensorMode::TeraPluginProcessor; | ||||
|     device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; | ||||
|     device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; | ||||
| } | ||||
|  | ||||
| TeraPluginProcessor::~TeraPluginProcessor() = default; | ||||
|  | ||||
| void TeraPluginProcessor::StartProcessor() {} | ||||
|  | ||||
| void TeraPluginProcessor::SuspendProcessor() {} | ||||
|  | ||||
| void TeraPluginProcessor::StopProcessor() {} | ||||
|  | ||||
| void TeraPluginProcessor::SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config) { | ||||
|     current_config.mode = config.mode; | ||||
|     current_config.unknown_1 = config.unknown_1; | ||||
|     current_config.unknown_2 = config.unknown_2; | ||||
|     current_config.unknown_3 = config.unknown_3; | ||||
| } | ||||
|  | ||||
| } // namespace Service::IRS | ||||
							
								
								
									
										53
									
								
								src/core/hle/service/hid/irsensor/tera_plugin_processor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/core/hle/service/hid/irsensor/tera_plugin_processor.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "common/bit_field.h" | ||||
| #include "common/common_types.h" | ||||
| #include "core/hid/irs_types.h" | ||||
| #include "core/hle/service/hid/irsensor/processor_base.h" | ||||
|  | ||||
| namespace Service::IRS { | ||||
| class TeraPluginProcessor final : public ProcessorBase { | ||||
| public: | ||||
|     explicit TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format); | ||||
|     ~TeraPluginProcessor() override; | ||||
|  | ||||
|     // Called when the processor is initialized | ||||
|     void StartProcessor() override; | ||||
|  | ||||
|     // Called when the processor is suspended | ||||
|     void SuspendProcessor() override; | ||||
|  | ||||
|     // Called when the processor is stopped | ||||
|     void StopProcessor() override; | ||||
|  | ||||
|     // Sets config parameters of the camera | ||||
|     void SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config); | ||||
|  | ||||
| private: | ||||
|     // This is nn::irsensor::TeraPluginProcessorConfig | ||||
|     struct TeraPluginProcessorConfig { | ||||
|         u8 mode; | ||||
|         u8 unknown_1; | ||||
|         u8 unknown_2; | ||||
|         u8 unknown_3; | ||||
|     }; | ||||
|     static_assert(sizeof(TeraPluginProcessorConfig) == 0x4, | ||||
|                   "TeraPluginProcessorConfig is an invalid size"); | ||||
|  | ||||
|     struct TeraPluginProcessorState { | ||||
|         s64 sampling_number; | ||||
|         u64 timestamp; | ||||
|         Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; | ||||
|         std::array<u8, 0x12c> data; | ||||
|     }; | ||||
|     static_assert(sizeof(TeraPluginProcessorState) == 0x140, | ||||
|                   "TeraPluginProcessorState is an invalid size"); | ||||
|  | ||||
|     TeraPluginProcessorConfig current_config{}; | ||||
|     Core::IrSensor::DeviceFormat& device; | ||||
| }; | ||||
|  | ||||
| } // namespace Service::IRS | ||||
| @@ -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
									
								
								src/input_common/drivers/camera.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/input_common/drivers/camera.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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
									
								
								src/input_common/drivers/camera.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/input_common/drivers/camera.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -36,11 +36,12 @@ struct BasicMotion { | ||||
| // Types of input that are stored in the engine | ||||
| enum class EngineInputType { | ||||
|     None, | ||||
|     Button, | ||||
|     HatButton, | ||||
|     Analog, | ||||
|     Motion, | ||||
|     Battery, | ||||
|     Button, | ||||
|     Camera, | ||||
|     HatButton, | ||||
|     Motion, | ||||
| }; | ||||
|  | ||||
| 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, | ||||
|   | ||||
| @@ -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); | ||||
|     } | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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(); | ||||
| } | ||||
|   | ||||
| @@ -30,6 +30,7 @@ enum Values : int; | ||||
| } | ||||
|  | ||||
| namespace InputCommon { | ||||
| class Camera; | ||||
| class Keyboard; | ||||
| class Mouse; | ||||
| class TouchScreen; | ||||
| @@ -95,6 +96,12 @@ public: | ||||
|     /// 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 | ||||
|   | ||||
| @@ -43,6 +43,9 @@ add_executable(yuzu | ||||
|     configuration/configure_audio.cpp | ||||
|     configuration/configure_audio.h | ||||
|     configuration/configure_audio.ui | ||||
|     configuration/configure_camera.cpp | ||||
|     configuration/configure_camera.h | ||||
|     configuration/configure_camera.ui | ||||
|     configuration/configure_cpu.cpp | ||||
|     configuration/configure_cpu.h | ||||
|     configuration/configure_cpu.ui | ||||
| @@ -254,7 +257,7 @@ endif() | ||||
| create_target_directory_groups(yuzu) | ||||
|  | ||||
| target_link_libraries(yuzu PRIVATE common core input_common video_core) | ||||
| target_link_libraries(yuzu PRIVATE Boost::boost glad Qt::Widgets) | ||||
| target_link_libraries(yuzu PRIVATE Boost::boost glad Qt::Widgets Qt::Multimedia) | ||||
| target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) | ||||
|  | ||||
| target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include) | ||||
|   | ||||
| @@ -5,6 +5,8 @@ | ||||
| #include <glad/glad.h> | ||||
|  | ||||
| #include <QApplication> | ||||
| #include <QCameraImageCapture> | ||||
| #include <QCameraInfo> | ||||
| #include <QHBoxLayout> | ||||
| #include <QMessageBox> | ||||
| #include <QPainter> | ||||
| @@ -31,6 +33,7 @@ | ||||
| #include "core/core.h" | ||||
| #include "core/cpu_manager.h" | ||||
| #include "core/frontend/framebuffer_layout.h" | ||||
| #include "input_common/drivers/camera.h" | ||||
| #include "input_common/drivers/keyboard.h" | ||||
| #include "input_common/drivers/mouse.h" | ||||
| #include "input_common/drivers/tas_input.h" | ||||
| @@ -801,6 +804,85 @@ void GRenderWindow::TouchEndEvent() { | ||||
|     input_subsystem->GetTouchScreen()->ReleaseAllTouch(); | ||||
| } | ||||
|  | ||||
| void GRenderWindow::InitializeCamera() { | ||||
|     if (!Settings::values.enable_ir_sensor) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     bool camera_found = false; | ||||
|     const QList<QCameraInfo> cameras = QCameraInfo::availableCameras(); | ||||
|     for (const QCameraInfo& cameraInfo : cameras) { | ||||
|         if (Settings::values.ir_sensor_device.GetValue() == cameraInfo.deviceName().toStdString() || | ||||
|             Settings::values.ir_sensor_device.GetValue() == "Auto") { | ||||
|             camera = std::make_unique<QCamera>(cameraInfo); | ||||
|             camera_found = true; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (!camera_found) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     camera_capture = std::make_unique<QCameraImageCapture>(camera.get()); | ||||
|     connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this, | ||||
|             &GRenderWindow::OnCameraCapture); | ||||
|     camera->unload(); | ||||
|     camera->setCaptureMode(QCamera::CaptureViewfinder); | ||||
|     camera->load(); | ||||
|     camera->start(); | ||||
|  | ||||
|     pending_camera_snapshots = 0; | ||||
|     is_virtual_camera = false; | ||||
|  | ||||
|     camera_timer = std::make_unique<QTimer>(); | ||||
|     connect(camera_timer.get(), &QTimer::timeout, [this] { RequestCameraCapture(); }); | ||||
|     // This timer should be dependent of camera resolution 5ms for every 100 pixels | ||||
|     camera_timer->start(100); | ||||
| } | ||||
|  | ||||
| void GRenderWindow::FinalizeCamera() { | ||||
|     if (camera_timer) { | ||||
|         camera_timer->stop(); | ||||
|     } | ||||
|     if (camera) { | ||||
|         camera->unload(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void GRenderWindow::RequestCameraCapture() { | ||||
|     if (!Settings::values.enable_ir_sensor) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     // If the camera doesn't capture, test for virtual cameras | ||||
|     if (pending_camera_snapshots > 5) { | ||||
|         is_virtual_camera = true; | ||||
|     } | ||||
|     // Virtual cameras like obs need to reset the camera every capture | ||||
|     if (is_virtual_camera) { | ||||
|         camera->stop(); | ||||
|         camera->start(); | ||||
|     } | ||||
|  | ||||
|     pending_camera_snapshots++; | ||||
|     camera_capture->capture(); | ||||
| } | ||||
|  | ||||
| void GRenderWindow::OnCameraCapture(int requestId, const QImage& img) { | ||||
|     constexpr std::size_t camera_width = 320; | ||||
|     constexpr std::size_t camera_height = 240; | ||||
|     const auto converted = | ||||
|         img.scaled(camera_width, camera_height, Qt::AspectRatioMode::IgnoreAspectRatio, | ||||
|                    Qt::TransformationMode::SmoothTransformation) | ||||
|             .mirrored(false, true); | ||||
|     std::vector<u32> camera_data{}; | ||||
|     camera_data.resize(camera_width * camera_height); | ||||
|     std::memcpy(camera_data.data(), converted.bits(), camera_width * camera_height * sizeof(u32)); | ||||
|     input_subsystem->GetCamera()->SetCameraData(camera_width, camera_height, camera_data); | ||||
|     pending_camera_snapshots = 0; | ||||
| } | ||||
|  | ||||
| bool GRenderWindow::event(QEvent* event) { | ||||
|     if (event->type() == QEvent::TouchBegin) { | ||||
|         TouchBeginEvent(static_cast<QTouchEvent*>(event)); | ||||
|   | ||||
| @@ -20,6 +20,8 @@ | ||||
|  | ||||
| class GRenderWindow; | ||||
| class GMainWindow; | ||||
| class QCamera; | ||||
| class QCameraImageCapture; | ||||
| class QKeyEvent; | ||||
|  | ||||
| namespace Core { | ||||
| @@ -164,6 +166,9 @@ public: | ||||
|     void mouseReleaseEvent(QMouseEvent* event) override; | ||||
|     void wheelEvent(QWheelEvent* event) override; | ||||
|  | ||||
|     void InitializeCamera(); | ||||
|     void FinalizeCamera(); | ||||
|  | ||||
|     bool event(QEvent* event) override; | ||||
|  | ||||
|     void focusOutEvent(QFocusEvent* event) override; | ||||
| @@ -207,6 +212,9 @@ private: | ||||
|     void TouchUpdateEvent(const QTouchEvent* event); | ||||
|     void TouchEndEvent(); | ||||
|  | ||||
|     void RequestCameraCapture(); | ||||
|     void OnCameraCapture(int requestId, const QImage& img); | ||||
|  | ||||
|     void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override; | ||||
|  | ||||
|     bool InitializeOpenGL(); | ||||
| @@ -232,6 +240,12 @@ private: | ||||
|     bool first_frame = false; | ||||
|     InputCommon::TasInput::TasState last_tas_state; | ||||
|  | ||||
|     bool is_virtual_camera; | ||||
|     int pending_camera_snapshots; | ||||
|     std::unique_ptr<QCamera> camera; | ||||
|     std::unique_ptr<QCameraImageCapture> camera_capture; | ||||
|     std::unique_ptr<QTimer> camera_timer; | ||||
|  | ||||
|     Core::System& system; | ||||
|  | ||||
| protected: | ||||
|   | ||||
| @@ -368,6 +368,11 @@ void Config::ReadHidbusValues() { | ||||
|     } | ||||
| } | ||||
|  | ||||
| void Config::ReadIrCameraValues() { | ||||
|     ReadBasicSetting(Settings::values.enable_ir_sensor); | ||||
|     ReadBasicSetting(Settings::values.ir_sensor_device); | ||||
| } | ||||
|  | ||||
| void Config::ReadAudioValues() { | ||||
|     qt_config->beginGroup(QStringLiteral("Audio")); | ||||
|  | ||||
| @@ -393,6 +398,7 @@ void Config::ReadControlValues() { | ||||
|     ReadTouchscreenValues(); | ||||
|     ReadMotionTouchValues(); | ||||
|     ReadHidbusValues(); | ||||
|     ReadIrCameraValues(); | ||||
|  | ||||
| #ifdef _WIN32 | ||||
|     ReadBasicSetting(Settings::values.enable_raw_input); | ||||
| @@ -1005,6 +1011,11 @@ void Config::SaveHidbusValues() { | ||||
|                  QString::fromStdString(default_param)); | ||||
| } | ||||
|  | ||||
| void Config::SaveIrCameraValues() { | ||||
|     WriteBasicSetting(Settings::values.enable_ir_sensor); | ||||
|     WriteBasicSetting(Settings::values.ir_sensor_device); | ||||
| } | ||||
|  | ||||
| void Config::SaveValues() { | ||||
|     if (global) { | ||||
|         SaveControlValues(); | ||||
| @@ -1047,6 +1058,7 @@ void Config::SaveControlValues() { | ||||
|     SaveTouchscreenValues(); | ||||
|     SaveMotionTouchValues(); | ||||
|     SaveHidbusValues(); | ||||
|     SaveIrCameraValues(); | ||||
|  | ||||
|     WriteGlobalSetting(Settings::values.use_docked_mode); | ||||
|     WriteGlobalSetting(Settings::values.vibration_enabled); | ||||
|   | ||||
| @@ -68,6 +68,7 @@ private: | ||||
|     void ReadTouchscreenValues(); | ||||
|     void ReadMotionTouchValues(); | ||||
|     void ReadHidbusValues(); | ||||
|     void ReadIrCameraValues(); | ||||
|  | ||||
|     // Read functions bases off the respective config section names. | ||||
|     void ReadAudioValues(); | ||||
| @@ -96,6 +97,7 @@ private: | ||||
|     void SaveTouchscreenValues(); | ||||
|     void SaveMotionTouchValues(); | ||||
|     void SaveHidbusValues(); | ||||
|     void SaveIrCameraValues(); | ||||
|  | ||||
|     // Save functions based off the respective config section names. | ||||
|     void SaveAudioValues(); | ||||
|   | ||||
							
								
								
									
										138
									
								
								src/yuzu/configuration/configure_camera.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								src/yuzu/configuration/configure_camera.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,138 @@ | ||||
| // Text : Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
| #include <memory> | ||||
| #include <QCameraImageCapture> | ||||
| #include <QCameraInfo> | ||||
| #include <QStandardItemModel> | ||||
| #include <QTimer> | ||||
|  | ||||
| #include "input_common/drivers/camera.h" | ||||
| #include "input_common/main.h" | ||||
| #include "ui_configure_camera.h" | ||||
| #include "yuzu/configuration/config.h" | ||||
| #include "yuzu/configuration/configure_camera.h" | ||||
|  | ||||
| ConfigureCamera::ConfigureCamera(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_) | ||||
|     : QDialog(parent), input_subsystem{input_subsystem_}, | ||||
|       ui(std::make_unique<Ui::ConfigureCamera>()) { | ||||
|     ui->setupUi(this); | ||||
|  | ||||
|     connect(ui->restore_defaults_button, &QPushButton::clicked, this, | ||||
|             &ConfigureCamera::RestoreDefaults); | ||||
|     connect(ui->preview_button, &QPushButton::clicked, this, &ConfigureCamera::PreviewCamera); | ||||
|  | ||||
|     auto blank_image = QImage(320, 240, QImage::Format::Format_RGB32); | ||||
|     blank_image.fill(Qt::black); | ||||
|     DisplayCapturedFrame(0, blank_image); | ||||
|  | ||||
|     LoadConfiguration(); | ||||
|     resize(0, 0); | ||||
| } | ||||
|  | ||||
| ConfigureCamera::~ConfigureCamera() = default; | ||||
|  | ||||
| void ConfigureCamera::PreviewCamera() { | ||||
|     const auto index = ui->ir_sensor_combo_box->currentIndex(); | ||||
|     bool camera_found = false; | ||||
|     const QList<QCameraInfo> cameras = QCameraInfo::availableCameras(); | ||||
|     for (const QCameraInfo& cameraInfo : cameras) { | ||||
|         if (input_devices[index] == cameraInfo.deviceName().toStdString() || | ||||
|             input_devices[index] == "Auto") { | ||||
|             LOG_INFO(Frontend, "Selected Camera {} {}", cameraInfo.description().toStdString(), | ||||
|                      cameraInfo.deviceName().toStdString()); | ||||
|             camera = std::make_unique<QCamera>(cameraInfo); | ||||
|             camera_found = true; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Clear previous frame | ||||
|     auto blank_image = QImage(320, 240, QImage::Format::Format_RGB32); | ||||
|     blank_image.fill(Qt::black); | ||||
|     DisplayCapturedFrame(0, blank_image); | ||||
|  | ||||
|     if (!camera_found) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     camera_capture = std::make_unique<QCameraImageCapture>(camera.get()); | ||||
|     connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this, | ||||
|             &ConfigureCamera::DisplayCapturedFrame); | ||||
|     camera->unload(); | ||||
|     camera->setCaptureMode(QCamera::CaptureViewfinder); | ||||
|     camera->load(); | ||||
|     camera->start(); | ||||
|  | ||||
|     pending_snapshots = 0; | ||||
|     is_virtual_camera = false; | ||||
|  | ||||
|     camera_timer = std::make_unique<QTimer>(); | ||||
|     connect(camera_timer.get(), &QTimer::timeout, [this] { | ||||
|         // If the camera doesn't capture, test for virtual cameras | ||||
|         if (pending_snapshots > 5) { | ||||
|             is_virtual_camera = true; | ||||
|         } | ||||
|         // Virtual cameras like obs need to reset the camera every capture | ||||
|         if (is_virtual_camera) { | ||||
|             camera->stop(); | ||||
|             camera->start(); | ||||
|         } | ||||
|         pending_snapshots++; | ||||
|         camera_capture->capture(); | ||||
|     }); | ||||
|  | ||||
|     camera_timer->start(250); | ||||
| } | ||||
|  | ||||
| void ConfigureCamera::DisplayCapturedFrame(int requestId, const QImage& img) { | ||||
|     LOG_INFO(Frontend, "ImageCaptured {} {}", img.width(), img.height()); | ||||
|     const auto converted = img.scaled(320, 240, Qt::AspectRatioMode::IgnoreAspectRatio, | ||||
|                                       Qt::TransformationMode::SmoothTransformation); | ||||
|     ui->preview_box->setPixmap(QPixmap::fromImage(converted)); | ||||
|     pending_snapshots = 0; | ||||
| } | ||||
|  | ||||
| void ConfigureCamera::changeEvent(QEvent* event) { | ||||
|     if (event->type() == QEvent::LanguageChange) { | ||||
|         RetranslateUI(); | ||||
|     } | ||||
|  | ||||
|     QDialog::changeEvent(event); | ||||
| } | ||||
|  | ||||
| void ConfigureCamera::RetranslateUI() { | ||||
|     ui->retranslateUi(this); | ||||
| } | ||||
|  | ||||
| void ConfigureCamera::ApplyConfiguration() { | ||||
|     const auto index = ui->ir_sensor_combo_box->currentIndex(); | ||||
|     Settings::values.ir_sensor_device.SetValue(input_devices[index]); | ||||
| } | ||||
|  | ||||
| void ConfigureCamera::LoadConfiguration() { | ||||
|     input_devices.clear(); | ||||
|     ui->ir_sensor_combo_box->clear(); | ||||
|     input_devices.push_back("Auto"); | ||||
|     ui->ir_sensor_combo_box->addItem(tr("Auto")); | ||||
|     const auto cameras = QCameraInfo::availableCameras(); | ||||
|     for (const QCameraInfo& cameraInfo : cameras) { | ||||
|         input_devices.push_back(cameraInfo.deviceName().toStdString()); | ||||
|         ui->ir_sensor_combo_box->addItem(cameraInfo.description()); | ||||
|     } | ||||
|  | ||||
|     const auto current_device = Settings::values.ir_sensor_device.GetValue(); | ||||
|  | ||||
|     const auto devices_it = std::find_if( | ||||
|         input_devices.begin(), input_devices.end(), | ||||
|         [current_device](const std::string& device) { return device == current_device; }); | ||||
|     const int device_index = | ||||
|         devices_it != input_devices.end() | ||||
|             ? static_cast<int>(std::distance(input_devices.begin(), devices_it)) | ||||
|             : 0; | ||||
|     ui->ir_sensor_combo_box->setCurrentIndex(device_index); | ||||
| } | ||||
|  | ||||
| void ConfigureCamera::RestoreDefaults() { | ||||
|     ui->ir_sensor_combo_box->setCurrentIndex(0); | ||||
| } | ||||
							
								
								
									
										54
									
								
								src/yuzu/configuration/configure_camera.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/yuzu/configuration/configure_camera.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| // Text : Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <memory> | ||||
| #include <QDialog> | ||||
|  | ||||
| class QTimer; | ||||
| class QCamera; | ||||
| class QCameraImageCapture; | ||||
|  | ||||
| namespace InputCommon { | ||||
| class InputSubsystem; | ||||
| } // namespace InputCommon | ||||
|  | ||||
| namespace Ui { | ||||
| class ConfigureCamera; | ||||
| } | ||||
|  | ||||
| class ConfigureCamera : public QDialog { | ||||
|     Q_OBJECT | ||||
|  | ||||
| public: | ||||
|     explicit ConfigureCamera(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_); | ||||
|     ~ConfigureCamera() override; | ||||
|  | ||||
|     void ApplyConfiguration(); | ||||
|  | ||||
| private: | ||||
|     void changeEvent(QEvent* event) override; | ||||
|     void RetranslateUI(); | ||||
|  | ||||
|     /// Load configuration settings. | ||||
|     void LoadConfiguration(); | ||||
|  | ||||
|     /// Restore all buttons to their default values. | ||||
|     void RestoreDefaults(); | ||||
|  | ||||
|     void DisplayCapturedFrame(int requestId, const QImage& img); | ||||
|  | ||||
|     /// Loads and signals the current selected camera to display a frame | ||||
|     void PreviewCamera(); | ||||
|  | ||||
|     InputCommon::InputSubsystem* input_subsystem; | ||||
|  | ||||
|     bool is_virtual_camera; | ||||
|     int pending_snapshots; | ||||
|     std::unique_ptr<QCamera> camera; | ||||
|     std::unique_ptr<QCameraImageCapture> camera_capture; | ||||
|     std::unique_ptr<QTimer> camera_timer; | ||||
|     std::vector<std::string> input_devices; | ||||
|     std::unique_ptr<Ui::ConfigureCamera> ui; | ||||
| }; | ||||
							
								
								
									
										170
									
								
								src/yuzu/configuration/configure_camera.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								src/yuzu/configuration/configure_camera.ui
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,170 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>ConfigureCamera</class> | ||||
|  <widget class="QDialog" name="ConfigureCamera"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>298</width> | ||||
|     <height>339</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>Configure Infrared Camera</string> | ||||
|   </property> | ||||
|   <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|    <item> | ||||
|     <widget class="QLabel" name="label_2"> | ||||
|      <property name="minimumSize"> | ||||
|       <size> | ||||
|        <width>280</width> | ||||
|        <height>0</height> | ||||
|       </size> | ||||
|      </property> | ||||
|      <property name="text"> | ||||
|       <string>Select where the image of the emulated camera comes from. It may be a virtual camera or a real camera.</string> | ||||
|      </property> | ||||
|      <property name="wordWrap"> | ||||
|       <bool>true</bool> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <spacer name="verticalSpacer_2"> | ||||
|      <property name="orientation"> | ||||
|       <enum>Qt::Vertical</enum> | ||||
|      </property> | ||||
|      <property name="sizeType"> | ||||
|       <enum>QSizePolicy::Fixed</enum> | ||||
|      </property> | ||||
|      <property name="sizeHint" stdset="0"> | ||||
|       <size> | ||||
|        <width>20</width> | ||||
|        <height>20</height> | ||||
|       </size> | ||||
|      </property> | ||||
|     </spacer> | ||||
|    </item> | ||||
|    <item> | ||||
|     <widget class="QGroupBox" name="gridGroupBox"> | ||||
|      <property name="title"> | ||||
|       <string>Camera Image Source:</string> | ||||
|      </property> | ||||
|      <layout class="QGridLayout" name="gridLayout"> | ||||
|       <item row="0" column="0"> | ||||
|        <spacer name="horizontalSpacer"> | ||||
|         <property name="orientation"> | ||||
|          <enum>Qt::Horizontal</enum> | ||||
|         </property> | ||||
|         <property name="sizeHint" stdset="0"> | ||||
|          <size> | ||||
|           <width>40</width> | ||||
|           <height>20</height> | ||||
|          </size> | ||||
|         </property> | ||||
|        </spacer> | ||||
|       </item> | ||||
|        <item row="0" column="1"> | ||||
|          <widget class="QLabel" name="label_3"> | ||||
|            <property name="text"> | ||||
|              <string>Input device:</string> | ||||
|            </property> | ||||
|          </widget> | ||||
|        </item> | ||||
|        <item row="0" column="2"> | ||||
|          <widget class="QComboBox" name="ir_sensor_combo_box"/> | ||||
|        </item> | ||||
|       <item row="0" column="3"> | ||||
|        <spacer name="horizontalSpacer_2"> | ||||
|         <property name="orientation"> | ||||
|          <enum>Qt::Horizontal</enum> | ||||
|         </property> | ||||
|         <property name="sizeHint" stdset="0"> | ||||
|          <size> | ||||
|           <width>40</width> | ||||
|           <height>20</height> | ||||
|          </size> | ||||
|         </property> | ||||
|        </spacer> | ||||
|       </item> | ||||
|      </layout> | ||||
|     </widget> | ||||
|    </item><item> | ||||
|     <widget class="QGroupBox" name="previewBox"> | ||||
|      <property name="title"> | ||||
|       <string>Preview</string> | ||||
|      </property> | ||||
|      <layout class="QVBoxLayout" name="verticalLayout_3"> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="preview_box"> | ||||
|          <property name="minimumSize"> | ||||
|            <size> | ||||
|              <width>320</width> | ||||
|              <height>240</height> | ||||
|            </size> | ||||
|          </property> | ||||
|         <property name="toolTip"> | ||||
|          <string>Resolution: 320*240</string> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QPushButton" name="preview_button"> | ||||
|         <property name="text"> | ||||
|          <string>Click to preview</string> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|      </layout> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <spacer name="verticalSpacer"> | ||||
|      <property name="orientation"> | ||||
|       <enum>Qt::Vertical</enum> | ||||
|      </property> | ||||
|      <property name="sizeHint" stdset="0"> | ||||
|       <size> | ||||
|        <width>20</width> | ||||
|        <height>40</height> | ||||
|       </size> | ||||
|      </property> | ||||
|     </spacer> | ||||
|    </item> | ||||
|    <item> | ||||
|     <layout class="QHBoxLayout" name="horizontalLayout"> | ||||
|      <item> | ||||
|       <widget class="QPushButton" name="restore_defaults_button"> | ||||
|        <property name="text"> | ||||
|         <string>Restore Defaults</string> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QDialogButtonBox" name="buttonBox"> | ||||
|        <property name="standardButtons"> | ||||
|         <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|     </layout> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>accepted()</signal> | ||||
|    <receiver>ConfigureCamera</receiver> | ||||
|    <slot>accept()</slot> | ||||
|   </connection> | ||||
|   <connection> | ||||
|    <sender>buttonBox</sender> | ||||
|    <signal>rejected()</signal> | ||||
|    <receiver>ConfigureCamera</receiver> | ||||
|    <slot>reject()</slot> | ||||
|    </connection> | ||||
|  </connections> | ||||
| </ui> | ||||
| @@ -15,6 +15,7 @@ | ||||
| #include "ui_configure_input.h" | ||||
| #include "ui_configure_input_advanced.h" | ||||
| #include "ui_configure_input_player.h" | ||||
| #include "yuzu/configuration/configure_camera.h" | ||||
| #include "yuzu/configuration/configure_debug_controller.h" | ||||
| #include "yuzu/configuration/configure_input.h" | ||||
| #include "yuzu/configuration/configure_input_advanced.h" | ||||
| @@ -163,6 +164,10 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, | ||||
|             [this, input_subsystem, &hid_core] { | ||||
|                 CallConfigureDialog<ConfigureRingController>(*this, input_subsystem, hid_core); | ||||
|             }); | ||||
|     connect(advanced, &ConfigureInputAdvanced::CallCameraDialog, | ||||
|             [this, input_subsystem, &hid_core] { | ||||
|                 CallConfigureDialog<ConfigureCamera>(*this, input_subsystem); | ||||
|             }); | ||||
|  | ||||
|     connect(ui->vibrationButton, &QPushButton::clicked, | ||||
|             [this, &hid_core] { CallConfigureDialog<ConfigureVibration>(*this, hid_core); }); | ||||
|   | ||||
| @@ -89,6 +89,7 @@ ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent) | ||||
|             [this] { CallMotionTouchConfigDialog(); }); | ||||
|     connect(ui->ring_controller_configure, &QPushButton::clicked, this, | ||||
|             [this] { CallRingControllerDialog(); }); | ||||
|     connect(ui->camera_configure, &QPushButton::clicked, this, [this] { CallCameraDialog(); }); | ||||
|  | ||||
| #ifndef _WIN32 | ||||
|     ui->enable_raw_input->setVisible(false); | ||||
| @@ -136,6 +137,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() { | ||||
|     Settings::values.enable_udp_controller = ui->enable_udp_controller->isChecked(); | ||||
|     Settings::values.controller_navigation = ui->controller_navigation->isChecked(); | ||||
|     Settings::values.enable_ring_controller = ui->enable_ring_controller->isChecked(); | ||||
|     Settings::values.enable_ir_sensor = ui->enable_ir_sensor->isChecked(); | ||||
| } | ||||
|  | ||||
| void ConfigureInputAdvanced::LoadConfiguration() { | ||||
| @@ -169,6 +171,7 @@ void ConfigureInputAdvanced::LoadConfiguration() { | ||||
|     ui->enable_udp_controller->setChecked(Settings::values.enable_udp_controller.GetValue()); | ||||
|     ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue()); | ||||
|     ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue()); | ||||
|     ui->enable_ir_sensor->setChecked(Settings::values.enable_ir_sensor.GetValue()); | ||||
|  | ||||
|     UpdateUIEnabled(); | ||||
| } | ||||
|   | ||||
| @@ -29,6 +29,7 @@ signals: | ||||
|     void CallTouchscreenConfigDialog(); | ||||
|     void CallMotionTouchConfigDialog(); | ||||
|     void CallRingControllerDialog(); | ||||
|     void CallCameraDialog(); | ||||
|  | ||||
| private: | ||||
|     void changeEvent(QEvent* event) override; | ||||
|   | ||||
| @@ -2617,6 +2617,20 @@ | ||||
|                  </property> | ||||
|                 </widget> | ||||
|                </item> | ||||
|                <item row="5" column="0"> | ||||
|                 <widget class="QCheckBox" name="enable_ir_sensor"> | ||||
|                  <property name="text"> | ||||
|                   <string>Infrared Camera</string> | ||||
|                  </property> | ||||
|                 </widget> | ||||
|                </item> | ||||
|                <item row="5" column="2"> | ||||
|                 <widget class="QPushButton" name="camera_configure"> | ||||
|                  <property name="text"> | ||||
|                   <string>Configure</string> | ||||
|                  </property> | ||||
|                 </widget> | ||||
|                </item> | ||||
|               </layout> | ||||
|              </widget> | ||||
|             </item> | ||||
|   | ||||
| @@ -1542,6 +1542,8 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t | ||||
|         mouse_hide_timer.start(); | ||||
|     } | ||||
|  | ||||
|     render_window->InitializeCamera(); | ||||
|  | ||||
|     std::string title_name; | ||||
|     std::string title_version; | ||||
|     const auto res = system->GetGameName(title_name); | ||||
| @@ -1623,6 +1625,7 @@ void GMainWindow::ShutdownGame() { | ||||
|     tas_label->clear(); | ||||
|     input_subsystem->GetTas()->Stop(); | ||||
|     OnTasStateChanged(); | ||||
|     render_window->FinalizeCamera(); | ||||
|  | ||||
|     // Enable all controllers | ||||
|     system->HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All}); | ||||
| @@ -2862,6 +2865,12 @@ void GMainWindow::OnConfigure() { | ||||
|         mouse_hide_timer.start(); | ||||
|     } | ||||
|  | ||||
|     // Restart camera config | ||||
|     if (emulation_running) { | ||||
|         render_window->FinalizeCamera(); | ||||
|         render_window->InitializeCamera(); | ||||
|     } | ||||
|  | ||||
|     if (!UISettings::values.has_broken_vulkan) { | ||||
|         renderer_status_button->setEnabled(!emulation_running); | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 liamwhite
					liamwhite