summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar liamwhite2022-07-24 13:31:28 -0400
committerGravatar GitHub2022-07-24 13:31:28 -0400
commit5af06d14337a61d9ed1093079d13f68cbb1f5451 (patch)
tree88df3fada076b04c2ab2da8972d1d785f492b520
parentMerge pull request #8545 from Kelebek1/Audio (diff)
parentyuzu: Add webcam support and rebase to latest master (diff)
downloadyuzu-5af06d14337a61d9ed1093079d13f68cbb1f5451.tar.gz
yuzu-5af06d14337a61d9ed1093079d13f68cbb1f5451.tar.xz
yuzu-5af06d14337a61d9ed1093079d13f68cbb1f5451.zip
Merge pull request #8484 from german77/irs_release
service: irs: Add camera support, split processors and implement ImageTransferProcessor
Diffstat (limited to '')
-rw-r--r--CMakeLists.txt6
-rw-r--r--CMakeModules/CopyYuzuQt5Deps.cmake8
-rw-r--r--src/common/input.h29
-rw-r--r--src/common/settings.h3
-rw-r--r--src/core/CMakeLists.txt15
-rw-r--r--src/core/hid/emulated_controller.cpp59
-rw-r--r--src/core/hid/emulated_controller.h40
-rw-r--r--src/core/hid/input_converter.cpp14
-rw-r--r--src/core/hid/input_converter.h8
-rw-r--r--src/core/hid/irs_types.h301
-rw-r--r--src/core/hle/service/hid/errors.h7
-rw-r--r--src/core/hle/service/hid/hid.cpp4
-rw-r--r--src/core/hle/service/hid/irs.cpp324
-rw-r--r--src/core/hle/service/hid/irs.h295
-rw-r--r--src/core/hle/service/hid/irsensor/clustering_processor.cpp34
-rw-r--r--src/core/hle/service/hid/irsensor/clustering_processor.h74
-rw-r--r--src/core/hle/service/hid/irsensor/image_transfer_processor.cpp150
-rw-r--r--src/core/hle/service/hid/irsensor/image_transfer_processor.h73
-rw-r--r--src/core/hle/service/hid/irsensor/ir_led_processor.cpp27
-rw-r--r--src/core/hle/service/hid/irsensor/ir_led_processor.h47
-rw-r--r--src/core/hle/service/hid/irsensor/moment_processor.cpp34
-rw-r--r--src/core/hle/service/hid/irsensor/moment_processor.h61
-rw-r--r--src/core/hle/service/hid/irsensor/pointing_processor.cpp26
-rw-r--r--src/core/hle/service/hid/irsensor/pointing_processor.h61
-rw-r--r--src/core/hle/service/hid/irsensor/processor_base.cpp67
-rw-r--r--src/core/hle/service/hid/irsensor/processor_base.h33
-rw-r--r--src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp29
-rw-r--r--src/core/hle/service/hid/irsensor/tera_plugin_processor.h53
-rw-r--r--src/input_common/CMakeLists.txt2
-rw-r--r--src/input_common/drivers/camera.cpp82
-rw-r--r--src/input_common/drivers/camera.h29
-rw-r--r--src/input_common/input_engine.cpp38
-rw-r--r--src/input_common/input_engine.h19
-rw-r--r--src/input_common/input_poller.cpp60
-rw-r--r--src/input_common/input_poller.h11
-rw-r--r--src/input_common/main.cpp21
-rw-r--r--src/input_common/main.h9
-rw-r--r--src/yuzu/CMakeLists.txt5
-rw-r--r--src/yuzu/bootmanager.cpp82
-rw-r--r--src/yuzu/bootmanager.h14
-rw-r--r--src/yuzu/configuration/config.cpp12
-rw-r--r--src/yuzu/configuration/config.h2
-rw-r--r--src/yuzu/configuration/configure_camera.cpp138
-rw-r--r--src/yuzu/configuration/configure_camera.h54
-rw-r--r--src/yuzu/configuration/configure_camera.ui170
-rw-r--r--src/yuzu/configuration/configure_input.cpp5
-rw-r--r--src/yuzu/configuration/configure_input_advanced.cpp3
-rw-r--r--src/yuzu/configuration/configure_input_advanced.h1
-rw-r--r--src/yuzu/configuration/configure_input_advanced.ui14
-rw-r--r--src/yuzu/main.cpp9
50 files changed, 2342 insertions, 320 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3f7dcc924..40ca8b149 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -196,7 +196,7 @@ if(ENABLE_QT)
196 # Check for system Qt on Linux, fallback to bundled Qt 196 # Check for system Qt on Linux, fallback to bundled Qt
197 if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") 197 if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
198 if (NOT YUZU_USE_BUNDLED_QT) 198 if (NOT YUZU_USE_BUNDLED_QT)
199 find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets DBus) 199 find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets DBus Multimedia)
200 endif() 200 endif()
201 if (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT) 201 if (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT)
202 # Check for dependencies, then enable bundled Qt download 202 # Check for dependencies, then enable bundled Qt download
@@ -300,9 +300,9 @@ if(ENABLE_QT)
300 set(YUZU_QT_NO_CMAKE_SYSTEM_PATH "NO_CMAKE_SYSTEM_PATH") 300 set(YUZU_QT_NO_CMAKE_SYSTEM_PATH "NO_CMAKE_SYSTEM_PATH")
301 endif() 301 endif()
302 if ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux") AND YUZU_USE_BUNDLED_QT) 302 if ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux") AND YUZU_USE_BUNDLED_QT)
303 find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent DBus ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH}) 303 find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent Multimedia DBus ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
304 else() 304 else()
305 find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH}) 305 find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent Multimedia ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
306 endif() 306 endif()
307 if (YUZU_USE_QT_WEB_ENGINE) 307 if (YUZU_USE_QT_WEB_ENGINE)
308 find_package(Qt5 REQUIRED COMPONENTS WebEngineCore WebEngineWidgets) 308 find_package(Qt5 REQUIRED COMPONENTS WebEngineCore WebEngineWidgets)
diff --git a/CMakeModules/CopyYuzuQt5Deps.cmake b/CMakeModules/CopyYuzuQt5Deps.cmake
index 0c27d51a6..6c5044caa 100644
--- a/CMakeModules/CopyYuzuQt5Deps.cmake
+++ b/CMakeModules/CopyYuzuQt5Deps.cmake
@@ -10,11 +10,13 @@ function(copy_yuzu_Qt5_deps target_dir)
10 set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/") 10 set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/")
11 set(Qt5_PLATFORMTHEMES_DIR "${Qt5_DIR}/../../../plugins/platformthemes/") 11 set(Qt5_PLATFORMTHEMES_DIR "${Qt5_DIR}/../../../plugins/platformthemes/")
12 set(Qt5_PLATFORMINPUTCONTEXTS_DIR "${Qt5_DIR}/../../../plugins/platforminputcontexts/") 12 set(Qt5_PLATFORMINPUTCONTEXTS_DIR "${Qt5_DIR}/../../../plugins/platforminputcontexts/")
13 set(Qt5_MEDIASERVICE_DIR "${Qt5_DIR}/../../../plugins/mediaservice/")
13 set(Qt5_XCBGLINTEGRATIONS_DIR "${Qt5_DIR}/../../../plugins/xcbglintegrations/") 14 set(Qt5_XCBGLINTEGRATIONS_DIR "${Qt5_DIR}/../../../plugins/xcbglintegrations/")
14 set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/") 15 set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/")
15 set(Qt5_IMAGEFORMATS_DIR "${Qt5_DIR}/../../../plugins/imageformats/") 16 set(Qt5_IMAGEFORMATS_DIR "${Qt5_DIR}/../../../plugins/imageformats/")
16 set(Qt5_RESOURCES_DIR "${Qt5_DIR}/../../../resources/") 17 set(Qt5_RESOURCES_DIR "${Qt5_DIR}/../../../resources/")
17 set(PLATFORMS ${DLL_DEST}plugins/platforms/) 18 set(PLATFORMS ${DLL_DEST}plugins/platforms/)
19 set(MEDIASERVICE ${DLL_DEST}mediaservice/)
18 set(STYLES ${DLL_DEST}plugins/styles/) 20 set(STYLES ${DLL_DEST}plugins/styles/)
19 set(IMAGEFORMATS ${DLL_DEST}plugins/imageformats/) 21 set(IMAGEFORMATS ${DLL_DEST}plugins/imageformats/)
20 if (MSVC) 22 if (MSVC)
@@ -22,6 +24,8 @@ function(copy_yuzu_Qt5_deps target_dir)
22 Qt5Core$<$<CONFIG:Debug>:d>.* 24 Qt5Core$<$<CONFIG:Debug>:d>.*
23 Qt5Gui$<$<CONFIG:Debug>:d>.* 25 Qt5Gui$<$<CONFIG:Debug>:d>.*
24 Qt5Widgets$<$<CONFIG:Debug>:d>.* 26 Qt5Widgets$<$<CONFIG:Debug>:d>.*
27 Qt5Multimedia$<$<CONFIG:Debug>:d>.*
28 Qt5Network$<$<CONFIG:Debug>:d>.*
25 ) 29 )
26 30
27 if (YUZU_USE_QT_WEB_ENGINE) 31 if (YUZU_USE_QT_WEB_ENGINE)
@@ -53,6 +57,10 @@ function(copy_yuzu_Qt5_deps target_dir)
53 qjpeg$<$<CONFIG:Debug>:d>.* 57 qjpeg$<$<CONFIG:Debug>:d>.*
54 qgif$<$<CONFIG:Debug>:d>.* 58 qgif$<$<CONFIG:Debug>:d>.*
55 ) 59 )
60 windows_copy_files(yuzu ${Qt5_MEDIASERVICE_DIR} ${MEDIASERVICE}
61 dsengine$<$<CONFIG:Debug>:d>.*
62 wmfengine$<$<CONFIG:Debug>:d>.*
63 )
56 else() 64 else()
57 set(Qt5_DLLS 65 set(Qt5_DLLS
58 "${Qt5_DLL_DIR}libQt5Core.so.5" 66 "${Qt5_DLL_DIR}libQt5Core.so.5"
diff --git a/src/common/input.h b/src/common/input.h
index bb42aaacc..995c35d9d 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -28,7 +28,7 @@ enum class InputType {
28 Color, 28 Color,
29 Vibration, 29 Vibration,
30 Nfc, 30 Nfc,
31 Ir, 31 IrSensor,
32}; 32};
33 33
34// Internal battery charge level 34// Internal battery charge level
@@ -53,6 +53,15 @@ enum class PollingMode {
53 IR, 53 IR,
54}; 54};
55 55
56enum class CameraFormat {
57 Size320x240,
58 Size160x120,
59 Size80x60,
60 Size40x30,
61 Size20x15,
62 None,
63};
64
56// Vibration reply from the controller 65// Vibration reply from the controller
57enum class VibrationError { 66enum class VibrationError {
58 None, 67 None,
@@ -68,6 +77,13 @@ enum class PollingError {
68 Unknown, 77 Unknown,
69}; 78};
70 79
80// Ir camera reply from the controller
81enum class CameraError {
82 None,
83 NotSupported,
84 Unknown,
85};
86
71// Hint for amplification curve to be used 87// Hint for amplification curve to be used
72enum class VibrationAmplificationType { 88enum class VibrationAmplificationType {
73 Linear, 89 Linear,
@@ -176,6 +192,12 @@ struct LedStatus {
176 bool led_4{}; 192 bool led_4{};
177}; 193};
178 194
195// Raw data fom camera
196struct CameraStatus {
197 CameraFormat format{CameraFormat::None};
198 std::vector<u8> data{};
199};
200
179// List of buttons to be passed to Qt that can be translated 201// List of buttons to be passed to Qt that can be translated
180enum class ButtonNames { 202enum class ButtonNames {
181 Undefined, 203 Undefined,
@@ -233,6 +255,7 @@ struct CallbackStatus {
233 BodyColorStatus color_status{}; 255 BodyColorStatus color_status{};
234 BatteryStatus battery_status{}; 256 BatteryStatus battery_status{};
235 VibrationStatus vibration_status{}; 257 VibrationStatus vibration_status{};
258 CameraStatus camera_status{};
236}; 259};
237 260
238// Triggered once every input change 261// Triggered once every input change
@@ -281,6 +304,10 @@ public:
281 virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) { 304 virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) {
282 return PollingError::NotSupported; 305 return PollingError::NotSupported;
283 } 306 }
307
308 virtual CameraError SetCameraFormat([[maybe_unused]] CameraFormat camera_format) {
309 return CameraError::NotSupported;
310 }
284}; 311};
285 312
286/// An abstract class template for a factory that can create input devices. 313/// An abstract class template for a factory that can create input devices.
diff --git a/src/common/settings.h b/src/common/settings.h
index 06d72c8bf..1079cf8cb 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -503,6 +503,9 @@ struct Values {
503 Setting<bool> enable_ring_controller{true, "enable_ring_controller"}; 503 Setting<bool> enable_ring_controller{true, "enable_ring_controller"};
504 RingconRaw ringcon_analogs; 504 RingconRaw ringcon_analogs;
505 505
506 Setting<bool> enable_ir_sensor{false, "enable_ir_sensor"};
507 Setting<std::string> ir_sensor_device{"auto", "ir_sensor_device"};
508
506 // Data Storage 509 // Data Storage
507 Setting<bool> use_virtual_sd{true, "use_virtual_sd"}; 510 Setting<bool> use_virtual_sd{true, "use_virtual_sd"};
508 Setting<bool> gamecard_inserted{false, "gamecard_inserted"}; 511 Setting<bool> gamecard_inserted{false, "gamecard_inserted"};
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 11d554bad..32cc2f392 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -158,6 +158,7 @@ add_library(core STATIC
158 hid/input_converter.h 158 hid/input_converter.h
159 hid/input_interpreter.cpp 159 hid/input_interpreter.cpp
160 hid/input_interpreter.h 160 hid/input_interpreter.h
161 hid/irs_types.h
161 hid/motion_input.cpp 162 hid/motion_input.cpp
162 hid/motion_input.h 163 hid/motion_input.h
163 hle/api_version.h 164 hle/api_version.h
@@ -477,6 +478,20 @@ add_library(core STATIC
477 hle/service/hid/hidbus/starlink.h 478 hle/service/hid/hidbus/starlink.h
478 hle/service/hid/hidbus/stubbed.cpp 479 hle/service/hid/hidbus/stubbed.cpp
479 hle/service/hid/hidbus/stubbed.h 480 hle/service/hid/hidbus/stubbed.h
481 hle/service/hid/irsensor/clustering_processor.cpp
482 hle/service/hid/irsensor/clustering_processor.h
483 hle/service/hid/irsensor/image_transfer_processor.cpp
484 hle/service/hid/irsensor/image_transfer_processor.h
485 hle/service/hid/irsensor/ir_led_processor.cpp
486 hle/service/hid/irsensor/ir_led_processor.h
487 hle/service/hid/irsensor/moment_processor.cpp
488 hle/service/hid/irsensor/moment_processor.h
489 hle/service/hid/irsensor/pointing_processor.cpp
490 hle/service/hid/irsensor/pointing_processor.h
491 hle/service/hid/irsensor/processor_base.cpp
492 hle/service/hid/irsensor/processor_base.h
493 hle/service/hid/irsensor/tera_plugin_processor.cpp
494 hle/service/hid/irsensor/tera_plugin_processor.h
480 hle/service/jit/jit_context.cpp 495 hle/service/jit/jit_context.cpp
481 hle/service/jit/jit_context.h 496 hle/service/jit/jit_context.h
482 hle/service/jit/jit.cpp 497 hle/service/jit/jit.cpp
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index bd2384515..8c3895937 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -126,10 +126,14 @@ void EmulatedController::LoadDevices() {
126 battery_params[LeftIndex].Set("battery", true); 126 battery_params[LeftIndex].Set("battery", true);
127 battery_params[RightIndex].Set("battery", true); 127 battery_params[RightIndex].Set("battery", true);
128 128
129 camera_params = Common::ParamPackage{"engine:camera,camera:1"};
130
129 output_params[LeftIndex] = left_joycon; 131 output_params[LeftIndex] = left_joycon;
130 output_params[RightIndex] = right_joycon; 132 output_params[RightIndex] = right_joycon;
133 output_params[2] = camera_params;
131 output_params[LeftIndex].Set("output", true); 134 output_params[LeftIndex].Set("output", true);
132 output_params[RightIndex].Set("output", true); 135 output_params[RightIndex].Set("output", true);
136 output_params[2].Set("output", true);
133 137
134 LoadTASParams(); 138 LoadTASParams();
135 139
@@ -146,6 +150,7 @@ void EmulatedController::LoadDevices() {
146 Common::Input::CreateDevice<Common::Input::InputDevice>); 150 Common::Input::CreateDevice<Common::Input::InputDevice>);
147 std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(), 151 std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(),
148 Common::Input::CreateDevice<Common::Input::InputDevice>); 152 Common::Input::CreateDevice<Common::Input::InputDevice>);
153 camera_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(camera_params);
149 std::transform(output_params.begin(), output_params.end(), output_devices.begin(), 154 std::transform(output_params.begin(), output_params.end(), output_devices.begin(),
150 Common::Input::CreateDevice<Common::Input::OutputDevice>); 155 Common::Input::CreateDevice<Common::Input::OutputDevice>);
151 156
@@ -267,6 +272,14 @@ void EmulatedController::ReloadInput() {
267 motion_devices[index]->ForceUpdate(); 272 motion_devices[index]->ForceUpdate();
268 } 273 }
269 274
275 if (camera_devices) {
276 camera_devices->SetCallback({
277 .on_change =
278 [this](const Common::Input::CallbackStatus& callback) { SetCamera(callback); },
279 });
280 camera_devices->ForceUpdate();
281 }
282
270 // Use a common UUID for TAS 283 // Use a common UUID for TAS
271 static constexpr Common::UUID TAS_UUID = Common::UUID{ 284 static constexpr Common::UUID TAS_UUID = Common::UUID{
272 {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; 285 {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
851 TriggerOnChange(ControllerTriggerType::Battery, true); 864 TriggerOnChange(ControllerTriggerType::Battery, true);
852} 865}
853 866
867void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) {
868 std::unique_lock lock{mutex};
869 controller.camera_values = TransformToCamera(callback);
870
871 if (is_configuring) {
872 lock.unlock();
873 TriggerOnChange(ControllerTriggerType::IrSensor, false);
874 return;
875 }
876
877 controller.camera_state.sample++;
878 controller.camera_state.format =
879 static_cast<Core::IrSensor::ImageTransferProcessorFormat>(controller.camera_values.format);
880 controller.camera_state.data = controller.camera_values.data;
881
882 lock.unlock();
883 TriggerOnChange(ControllerTriggerType::IrSensor, true);
884}
885
854bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { 886bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
855 if (device_index >= output_devices.size()) { 887 if (device_index >= output_devices.size()) {
856 return false; 888 return false;
@@ -928,6 +960,23 @@ bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode)
928 return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None; 960 return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None;
929} 961}
930 962
963bool EmulatedController::SetCameraFormat(
964 Core::IrSensor::ImageTransferProcessorFormat camera_format) {
965 LOG_INFO(Service_HID, "Set camera format {}", camera_format);
966
967 auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
968 auto& camera_output_device = output_devices[2];
969
970 if (right_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
971 camera_format)) == Common::Input::CameraError::None) {
972 return true;
973 }
974
975 // Fallback to Qt camera if native device doesn't have support
976 return camera_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
977 camera_format)) == Common::Input::CameraError::None;
978}
979
931void EmulatedController::SetLedPattern() { 980void EmulatedController::SetLedPattern() {
932 for (auto& device : output_devices) { 981 for (auto& device : output_devices) {
933 if (!device) { 982 if (!device) {
@@ -1163,6 +1212,11 @@ BatteryValues EmulatedController::GetBatteryValues() const {
1163 return controller.battery_values; 1212 return controller.battery_values;
1164} 1213}
1165 1214
1215CameraValues EmulatedController::GetCameraValues() const {
1216 std::scoped_lock lock{mutex};
1217 return controller.camera_values;
1218}
1219
1166HomeButtonState EmulatedController::GetHomeButtons() const { 1220HomeButtonState EmulatedController::GetHomeButtons() const {
1167 std::scoped_lock lock{mutex}; 1221 std::scoped_lock lock{mutex};
1168 if (is_configuring) { 1222 if (is_configuring) {
@@ -1251,6 +1305,11 @@ BatteryLevelState EmulatedController::GetBattery() const {
1251 return controller.battery_state; 1305 return controller.battery_state;
1252} 1306}
1253 1307
1308const CameraState& EmulatedController::GetCamera() const {
1309 std::scoped_lock lock{mutex};
1310 return controller.camera_state;
1311}
1312
1254void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) { 1313void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
1255 std::scoped_lock lock{callback_mutex}; 1314 std::scoped_lock lock{callback_mutex};
1256 for (const auto& poller_pair : callback_list) { 1315 for (const auto& poller_pair : callback_list) {
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index 3f02ed3c0..823c1700c 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -15,10 +15,12 @@
15#include "common/settings.h" 15#include "common/settings.h"
16#include "common/vector_math.h" 16#include "common/vector_math.h"
17#include "core/hid/hid_types.h" 17#include "core/hid/hid_types.h"
18#include "core/hid/irs_types.h"
18#include "core/hid/motion_input.h" 19#include "core/hid/motion_input.h"
19 20
20namespace Core::HID { 21namespace Core::HID {
21const std::size_t max_emulated_controllers = 2; 22const std::size_t max_emulated_controllers = 2;
23const std::size_t output_devices = 3;
22struct ControllerMotionInfo { 24struct ControllerMotionInfo {
23 Common::Input::MotionStatus raw_status{}; 25 Common::Input::MotionStatus raw_status{};
24 MotionInput emulated{}; 26 MotionInput emulated{};
@@ -34,15 +36,16 @@ using TriggerDevices =
34 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>; 36 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>;
35using BatteryDevices = 37using BatteryDevices =
36 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; 38 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
37using OutputDevices = 39using CameraDevices = std::unique_ptr<Common::Input::InputDevice>;
38 std::array<std::unique_ptr<Common::Input::OutputDevice>, max_emulated_controllers>; 40using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices>;
39 41
40using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>; 42using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>;
41using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>; 43using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>;
42using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>; 44using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>;
43using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>; 45using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>;
44using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>; 46using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>;
45using OutputParams = std::array<Common::ParamPackage, max_emulated_controllers>; 47using CameraParams = Common::ParamPackage;
48using OutputParams = std::array<Common::ParamPackage, output_devices>;
46 49
47using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>; 50using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
48using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>; 51using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>;
@@ -51,6 +54,7 @@ using TriggerValues =
51using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::NativeMotion::NumMotions>; 54using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::NativeMotion::NumMotions>;
52using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>; 55using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>;
53using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>; 56using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>;
57using CameraValues = Common::Input::CameraStatus;
54using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>; 58using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>;
55 59
56struct AnalogSticks { 60struct AnalogSticks {
@@ -70,6 +74,12 @@ struct BatteryLevelState {
70 NpadPowerInfo right{}; 74 NpadPowerInfo right{};
71}; 75};
72 76
77struct CameraState {
78 Core::IrSensor::ImageTransferProcessorFormat format{};
79 std::vector<u8> data{};
80 std::size_t sample{};
81};
82
73struct ControllerMotion { 83struct ControllerMotion {
74 Common::Vec3f accel{}; 84 Common::Vec3f accel{};
75 Common::Vec3f gyro{}; 85 Common::Vec3f gyro{};
@@ -96,6 +106,7 @@ struct ControllerStatus {
96 ColorValues color_values{}; 106 ColorValues color_values{};
97 BatteryValues battery_values{}; 107 BatteryValues battery_values{};
98 VibrationValues vibration_values{}; 108 VibrationValues vibration_values{};
109 CameraValues camera_values{};
99 110
100 // Data for HID serices 111 // Data for HID serices
101 HomeButtonState home_button_state{}; 112 HomeButtonState home_button_state{};
@@ -107,6 +118,7 @@ struct ControllerStatus {
107 NpadGcTriggerState gc_trigger_state{}; 118 NpadGcTriggerState gc_trigger_state{};
108 ControllerColors colors_state{}; 119 ControllerColors colors_state{};
109 BatteryLevelState battery_state{}; 120 BatteryLevelState battery_state{};
121 CameraState camera_state{};
110}; 122};
111 123
112enum class ControllerTriggerType { 124enum class ControllerTriggerType {
@@ -117,6 +129,7 @@ enum class ControllerTriggerType {
117 Color, 129 Color,
118 Battery, 130 Battery,
119 Vibration, 131 Vibration,
132 IrSensor,
120 Connected, 133 Connected,
121 Disconnected, 134 Disconnected,
122 Type, 135 Type,
@@ -269,6 +282,9 @@ public:
269 /// Returns the latest battery status from the controller with parameters 282 /// Returns the latest battery status from the controller with parameters
270 BatteryValues GetBatteryValues() const; 283 BatteryValues GetBatteryValues() const;
271 284
285 /// Returns the latest camera status from the controller with parameters
286 CameraValues GetCameraValues() const;
287
272 /// Returns the latest status of button input for the hid::HomeButton service 288 /// Returns the latest status of button input for the hid::HomeButton service
273 HomeButtonState GetHomeButtons() const; 289 HomeButtonState GetHomeButtons() const;
274 290
@@ -296,6 +312,9 @@ public:
296 /// Returns the latest battery status from the controller 312 /// Returns the latest battery status from the controller
297 BatteryLevelState GetBattery() const; 313 BatteryLevelState GetBattery() const;
298 314
315 /// Returns the latest camera status from the controller
316 const CameraState& GetCamera() const;
317
299 /** 318 /**
300 * Sends a specific vibration to the output device 319 * Sends a specific vibration to the output device
301 * @return true if vibration had no errors 320 * @return true if vibration had no errors
@@ -315,6 +334,13 @@ public:
315 */ 334 */
316 bool SetPollingMode(Common::Input::PollingMode polling_mode); 335 bool SetPollingMode(Common::Input::PollingMode polling_mode);
317 336
337 /**
338 * Sets the desired camera format to be polled from a controller
339 * @param camera_format size of each frame
340 * @return true if SetCameraFormat was successfull
341 */
342 bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format);
343
318 /// Returns the led pattern corresponding to this emulated controller 344 /// Returns the led pattern corresponding to this emulated controller
319 LedPattern GetLedPattern() const; 345 LedPattern GetLedPattern() const;
320 346
@@ -393,6 +419,12 @@ private:
393 void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index); 419 void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index);
394 420
395 /** 421 /**
422 * Updates the camera status of the controller
423 * @param callback A CallbackStatus containing the camera status
424 */
425 void SetCamera(const Common::Input::CallbackStatus& callback);
426
427 /**
396 * Triggers a callback that something has changed on the controller status 428 * Triggers a callback that something has changed on the controller status
397 * @param type Input type of the event to trigger 429 * @param type Input type of the event to trigger
398 * @param is_service_update indicates if this event should only be sent to HID services 430 * @param is_service_update indicates if this event should only be sent to HID services
@@ -417,6 +449,7 @@ private:
417 ControllerMotionParams motion_params; 449 ControllerMotionParams motion_params;
418 TriggerParams trigger_params; 450 TriggerParams trigger_params;
419 BatteryParams battery_params; 451 BatteryParams battery_params;
452 CameraParams camera_params;
420 OutputParams output_params; 453 OutputParams output_params;
421 454
422 ButtonDevices button_devices; 455 ButtonDevices button_devices;
@@ -424,6 +457,7 @@ private:
424 ControllerMotionDevices motion_devices; 457 ControllerMotionDevices motion_devices;
425 TriggerDevices trigger_devices; 458 TriggerDevices trigger_devices;
426 BatteryDevices battery_devices; 459 BatteryDevices battery_devices;
460 CameraDevices camera_devices;
427 OutputDevices output_devices; 461 OutputDevices output_devices;
428 462
429 // TAS related variables 463 // TAS related variables
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp
index 18d9f042d..68d143a01 100644
--- a/src/core/hid/input_converter.cpp
+++ b/src/core/hid/input_converter.cpp
@@ -270,6 +270,20 @@ Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatu
270 return status; 270 return status;
271} 271}
272 272
273Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback) {
274 Common::Input::CameraStatus camera{};
275 switch (callback.type) {
276 case Common::Input::InputType::IrSensor:
277 camera = callback.camera_status;
278 break;
279 default:
280 LOG_ERROR(Input, "Conversion from type {} to camera not implemented", callback.type);
281 break;
282 }
283
284 return camera;
285}
286
273void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) { 287void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) {
274 const auto& properties = analog.properties; 288 const auto& properties = analog.properties;
275 float& raw_value = analog.raw_value; 289 float& raw_value = analog.raw_value;
diff --git a/src/core/hid/input_converter.h b/src/core/hid/input_converter.h
index 2be36889f..143c50cc0 100644
--- a/src/core/hid/input_converter.h
+++ b/src/core/hid/input_converter.h
@@ -77,6 +77,14 @@ Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackSta
77Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback); 77Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback);
78 78
79/** 79/**
80 * Converts raw input data into a valid camera status.
81 *
82 * @param callback Supported callbacks: Camera.
83 * @return A valid CameraObject object.
84 */
85Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback);
86
87/**
80 * Converts raw analog data into a valid analog value 88 * Converts raw analog data into a valid analog value
81 * @param analog An analog object containing raw data and properties 89 * @param analog An analog object containing raw data and properties
82 * @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f. 90 * @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f.
diff --git a/src/core/hid/irs_types.h b/src/core/hid/irs_types.h
new file mode 100644
index 000000000..88c5b016d
--- /dev/null
+++ b/src/core/hid/irs_types.h
@@ -0,0 +1,301 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_funcs.h"
7#include "common/common_types.h"
8#include "core/hid/hid_types.h"
9
10namespace Core::IrSensor {
11
12// This is nn::irsensor::CameraAmbientNoiseLevel
13enum class CameraAmbientNoiseLevel : u32 {
14 Low,
15 Medium,
16 High,
17 Unkown3, // This level can't be reached
18};
19
20// This is nn::irsensor::CameraLightTarget
21enum class CameraLightTarget : u32 {
22 AllLeds,
23 BrightLeds,
24 DimLeds,
25 None,
26};
27
28// This is nn::irsensor::PackedCameraLightTarget
29enum class PackedCameraLightTarget : u8 {
30 AllLeds,
31 BrightLeds,
32 DimLeds,
33 None,
34};
35
36// This is nn::irsensor::AdaptiveClusteringMode
37enum class AdaptiveClusteringMode : u32 {
38 StaticFov,
39 DynamicFov,
40};
41
42// This is nn::irsensor::AdaptiveClusteringTargetDistance
43enum class AdaptiveClusteringTargetDistance : u32 {
44 Near,
45 Middle,
46 Far,
47};
48
49// This is nn::irsensor::ImageTransferProcessorFormat
50enum class ImageTransferProcessorFormat : u32 {
51 Size320x240,
52 Size160x120,
53 Size80x60,
54 Size40x30,
55 Size20x15,
56};
57
58// This is nn::irsensor::PackedImageTransferProcessorFormat
59enum class PackedImageTransferProcessorFormat : u8 {
60 Size320x240,
61 Size160x120,
62 Size80x60,
63 Size40x30,
64 Size20x15,
65};
66
67// This is nn::irsensor::IrCameraStatus
68enum class IrCameraStatus : u32 {
69 Available,
70 Unsupported,
71 Unconnected,
72};
73
74// This is nn::irsensor::IrCameraInternalStatus
75enum class IrCameraInternalStatus : u32 {
76 Stopped,
77 FirmwareUpdateNeeded,
78 Unkown2,
79 Unkown3,
80 Unkown4,
81 FirmwareVersionRequested,
82 FirmwareVersionIsInvalid,
83 Ready,
84 Setting,
85};
86
87// This is nn::irsensor::detail::StatusManager::IrSensorMode
88enum class IrSensorMode : u64 {
89 None,
90 MomentProcessor,
91 ClusteringProcessor,
92 ImageTransferProcessor,
93 PointingProcessorMarker,
94 TeraPluginProcessor,
95 IrLedProcessor,
96};
97
98// This is nn::irsensor::ImageProcessorStatus
99enum ImageProcessorStatus : u32 {
100 Stopped,
101 Running,
102};
103
104// This is nn::irsensor::HandAnalysisMode
105enum class HandAnalysisMode : u32 {
106 None,
107 Silhouette,
108 Image,
109 SilhoueteAndImage,
110 SilhuetteOnly,
111};
112
113// This is nn::irsensor::IrSensorFunctionLevel
114enum class IrSensorFunctionLevel : u8 {
115 unknown0,
116 unknown1,
117 unknown2,
118 unknown3,
119 unknown4,
120};
121
122// This is nn::irsensor::MomentProcessorPreprocess
123enum class MomentProcessorPreprocess : u32 {
124 Unkown0,
125 Unkown1,
126};
127
128// This is nn::irsensor::PackedMomentProcessorPreprocess
129enum class PackedMomentProcessorPreprocess : u8 {
130 Unkown0,
131 Unkown1,
132};
133
134// This is nn::irsensor::PointingStatus
135enum class PointingStatus : u32 {
136 Unkown0,
137 Unkown1,
138};
139
140struct IrsRect {
141 s16 x;
142 s16 y;
143 s16 width;
144 s16 height;
145};
146
147struct IrsCentroid {
148 f32 x;
149 f32 y;
150};
151
152struct CameraConfig {
153 u64 exposure_time;
154 CameraLightTarget light_target;
155 u32 gain;
156 bool is_negative_used;
157 INSERT_PADDING_BYTES(7);
158};
159static_assert(sizeof(CameraConfig) == 0x18, "CameraConfig is an invalid size");
160
161struct PackedCameraConfig {
162 u64 exposure_time;
163 PackedCameraLightTarget light_target;
164 u8 gain;
165 bool is_negative_used;
166 INSERT_PADDING_BYTES(5);
167};
168static_assert(sizeof(PackedCameraConfig) == 0x10, "PackedCameraConfig is an invalid size");
169
170// This is nn::irsensor::IrCameraHandle
171struct IrCameraHandle {
172 u8 npad_id{};
173 Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
174 INSERT_PADDING_BYTES(2);
175};
176static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size");
177
178// This is nn::irsensor::PackedMcuVersion
179struct PackedMcuVersion {
180 u16 major;
181 u16 minor;
182};
183static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size");
184
185// This is nn::irsensor::PackedMomentProcessorConfig
186struct PackedMomentProcessorConfig {
187 PackedCameraConfig camera_config;
188 IrsRect window_of_interest;
189 PackedMcuVersion required_mcu_version;
190 PackedMomentProcessorPreprocess preprocess;
191 u8 preprocess_intensity_threshold;
192 INSERT_PADDING_BYTES(2);
193};
194static_assert(sizeof(PackedMomentProcessorConfig) == 0x20,
195 "PackedMomentProcessorConfig is an invalid size");
196
197// This is nn::irsensor::PackedClusteringProcessorConfig
198struct PackedClusteringProcessorConfig {
199 PackedCameraConfig camera_config;
200 IrsRect window_of_interest;
201 PackedMcuVersion required_mcu_version;
202 u32 pixel_count_min;
203 u32 pixel_count_max;
204 u8 object_intensity_min;
205 bool is_external_light_filter_enabled;
206 INSERT_PADDING_BYTES(2);
207};
208static_assert(sizeof(PackedClusteringProcessorConfig) == 0x28,
209 "PackedClusteringProcessorConfig is an invalid size");
210
211// This is nn::irsensor::PackedImageTransferProcessorConfig
212struct PackedImageTransferProcessorConfig {
213 PackedCameraConfig camera_config;
214 PackedMcuVersion required_mcu_version;
215 PackedImageTransferProcessorFormat format;
216 INSERT_PADDING_BYTES(3);
217};
218static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18,
219 "PackedImageTransferProcessorConfig is an invalid size");
220
221// This is nn::irsensor::PackedTeraPluginProcessorConfig
222struct PackedTeraPluginProcessorConfig {
223 PackedMcuVersion required_mcu_version;
224 u8 mode;
225 u8 unknown_1;
226 u8 unknown_2;
227 u8 unknown_3;
228};
229static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8,
230 "PackedTeraPluginProcessorConfig is an invalid size");
231
232// This is nn::irsensor::PackedPointingProcessorConfig
233struct PackedPointingProcessorConfig {
234 IrsRect window_of_interest;
235 PackedMcuVersion required_mcu_version;
236};
237static_assert(sizeof(PackedPointingProcessorConfig) == 0xC,
238 "PackedPointingProcessorConfig is an invalid size");
239
240// This is nn::irsensor::PackedFunctionLevel
241struct PackedFunctionLevel {
242 IrSensorFunctionLevel function_level;
243 INSERT_PADDING_BYTES(3);
244};
245static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size");
246
247// This is nn::irsensor::PackedImageTransferProcessorExConfig
248struct PackedImageTransferProcessorExConfig {
249 PackedCameraConfig camera_config;
250 PackedMcuVersion required_mcu_version;
251 PackedImageTransferProcessorFormat origin_format;
252 PackedImageTransferProcessorFormat trimming_format;
253 u16 trimming_start_x;
254 u16 trimming_start_y;
255 bool is_external_light_filter_enabled;
256 INSERT_PADDING_BYTES(5);
257};
258static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20,
259 "PackedImageTransferProcessorExConfig is an invalid size");
260
261// This is nn::irsensor::PackedIrLedProcessorConfig
262struct PackedIrLedProcessorConfig {
263 PackedMcuVersion required_mcu_version;
264 u8 light_target;
265 INSERT_PADDING_BYTES(3);
266};
267static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8,
268 "PackedIrLedProcessorConfig is an invalid size");
269
270// This is nn::irsensor::HandAnalysisConfig
271struct HandAnalysisConfig {
272 HandAnalysisMode mode;
273};
274static_assert(sizeof(HandAnalysisConfig) == 0x4, "HandAnalysisConfig is an invalid size");
275
276// This is nn::irsensor::detail::ProcessorState contents are different for each processor
277struct ProcessorState {
278 std::array<u8, 0xE20> processor_raw_data{};
279};
280static_assert(sizeof(ProcessorState) == 0xE20, "ProcessorState is an invalid size");
281
282// This is nn::irsensor::detail::DeviceFormat
283struct DeviceFormat {
284 Core::IrSensor::IrCameraStatus camera_status{Core::IrSensor::IrCameraStatus::Unconnected};
285 Core::IrSensor::IrCameraInternalStatus camera_internal_status{
286 Core::IrSensor::IrCameraInternalStatus::Ready};
287 Core::IrSensor::IrSensorMode mode{Core::IrSensor::IrSensorMode::None};
288 ProcessorState state{};
289};
290static_assert(sizeof(DeviceFormat) == 0xE30, "DeviceFormat is an invalid size");
291
292// This is nn::irsensor::ImageTransferProcessorState
293struct ImageTransferProcessorState {
294 u64 sampling_number;
295 Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
296 INSERT_PADDING_BYTES(4);
297};
298static_assert(sizeof(ImageTransferProcessorState) == 0x10,
299 "ImageTransferProcessorState is an invalid size");
300
301} // namespace Core::IrSensor
diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h
index 46282f42e..4613a4e60 100644
--- a/src/core/hle/service/hid/errors.h
+++ b/src/core/hle/service/hid/errors.h
@@ -19,3 +19,10 @@ constexpr Result InvalidNpadId{ErrorModule::HID, 709};
19constexpr Result NpadNotConnected{ErrorModule::HID, 710}; 19constexpr Result NpadNotConnected{ErrorModule::HID, 710};
20 20
21} // namespace Service::HID 21} // namespace Service::HID
22
23namespace Service::IRS {
24
25constexpr Result InvalidProcessorState{ErrorModule::Irsensor, 78};
26constexpr Result InvalidIrCameraHandle{ErrorModule::Irsensor, 204};
27
28} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 89bb12442..5ecbddf94 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -2345,8 +2345,8 @@ void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system
2345 std::make_shared<HidSys>(system)->InstallAsService(service_manager); 2345 std::make_shared<HidSys>(system)->InstallAsService(service_manager);
2346 std::make_shared<HidTmp>(system)->InstallAsService(service_manager); 2346 std::make_shared<HidTmp>(system)->InstallAsService(service_manager);
2347 2347
2348 std::make_shared<IRS>(system)->InstallAsService(service_manager); 2348 std::make_shared<Service::IRS::IRS>(system)->InstallAsService(service_manager);
2349 std::make_shared<IRS_SYS>(system)->InstallAsService(service_manager); 2349 std::make_shared<Service::IRS::IRS_SYS>(system)->InstallAsService(service_manager);
2350 2350
2351 std::make_shared<XCD_SYS>(system)->InstallAsService(service_manager); 2351 std::make_shared<XCD_SYS>(system)->InstallAsService(service_manager);
2352} 2352}
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index d2a91d913..d5107e41f 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -1,16 +1,28 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <algorithm>
5#include <random>
6
4#include "core/core.h" 7#include "core/core.h"
5#include "core/core_timing.h" 8#include "core/core_timing.h"
9#include "core/hid/emulated_controller.h"
10#include "core/hid/hid_core.h"
6#include "core/hle/ipc_helpers.h" 11#include "core/hle/ipc_helpers.h"
7#include "core/hle/kernel/k_shared_memory.h" 12#include "core/hle/kernel/k_shared_memory.h"
8#include "core/hle/kernel/k_transfer_memory.h" 13#include "core/hle/kernel/k_transfer_memory.h"
9#include "core/hle/kernel/kernel.h" 14#include "core/hle/kernel/kernel.h"
10#include "core/hle/service/hid/errors.h" 15#include "core/hle/service/hid/errors.h"
11#include "core/hle/service/hid/irs.h" 16#include "core/hle/service/hid/irs.h"
17#include "core/hle/service/hid/irsensor/clustering_processor.h"
18#include "core/hle/service/hid/irsensor/image_transfer_processor.h"
19#include "core/hle/service/hid/irsensor/ir_led_processor.h"
20#include "core/hle/service/hid/irsensor/moment_processor.h"
21#include "core/hle/service/hid/irsensor/pointing_processor.h"
22#include "core/hle/service/hid/irsensor/tera_plugin_processor.h"
23#include "core/memory.h"
12 24
13namespace Service::HID { 25namespace Service::IRS {
14 26
15IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} { 27IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
16 // clang-format off 28 // clang-format off
@@ -36,14 +48,19 @@ IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
36 }; 48 };
37 // clang-format on 49 // clang-format on
38 50
51 u8* raw_shared_memory = system.Kernel().GetIrsSharedMem().GetPointer();
39 RegisterHandlers(functions); 52 RegisterHandlers(functions);
53 shared_memory = std::construct_at(reinterpret_cast<StatusManager*>(raw_shared_memory));
54
55 npad_device = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
40} 56}
57IRS::~IRS() = default;
41 58
42void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) { 59void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) {
43 IPC::RequestParser rp{ctx}; 60 IPC::RequestParser rp{ctx};
44 const auto applet_resource_user_id{rp.Pop<u64>()}; 61 const auto applet_resource_user_id{rp.Pop<u64>()};
45 62
46 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", 63 LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}",
47 applet_resource_user_id); 64 applet_resource_user_id);
48 65
49 IPC::ResponseBuilder rb{ctx, 2}; 66 IPC::ResponseBuilder rb{ctx, 2};
@@ -54,7 +71,7 @@ void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) {
54 IPC::RequestParser rp{ctx}; 71 IPC::RequestParser rp{ctx};
55 const auto applet_resource_user_id{rp.Pop<u64>()}; 72 const auto applet_resource_user_id{rp.Pop<u64>()};
56 73
57 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", 74 LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}",
58 applet_resource_user_id); 75 applet_resource_user_id);
59 76
60 IPC::ResponseBuilder rb{ctx, 2}; 77 IPC::ResponseBuilder rb{ctx, 2};
@@ -75,7 +92,7 @@ void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
75void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) { 92void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
76 IPC::RequestParser rp{ctx}; 93 IPC::RequestParser rp{ctx};
77 struct Parameters { 94 struct Parameters {
78 IrCameraHandle camera_handle; 95 Core::IrSensor::IrCameraHandle camera_handle;
79 INSERT_PADDING_WORDS_NOINIT(1); 96 INSERT_PADDING_WORDS_NOINIT(1);
80 u64 applet_resource_user_id; 97 u64 applet_resource_user_id;
81 }; 98 };
@@ -88,17 +105,23 @@ void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
88 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, 105 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
89 parameters.applet_resource_user_id); 106 parameters.applet_resource_user_id);
90 107
108 auto result = IsIrCameraHandleValid(parameters.camera_handle);
109 if (result.IsSuccess()) {
110 // TODO: Stop Image processor
111 result = ResultSuccess;
112 }
113
91 IPC::ResponseBuilder rb{ctx, 2}; 114 IPC::ResponseBuilder rb{ctx, 2};
92 rb.Push(ResultSuccess); 115 rb.Push(result);
93} 116}
94 117
95void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) { 118void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
96 IPC::RequestParser rp{ctx}; 119 IPC::RequestParser rp{ctx};
97 struct Parameters { 120 struct Parameters {
98 IrCameraHandle camera_handle; 121 Core::IrSensor::IrCameraHandle camera_handle;
99 INSERT_PADDING_WORDS_NOINIT(1); 122 INSERT_PADDING_WORDS_NOINIT(1);
100 u64 applet_resource_user_id; 123 u64 applet_resource_user_id;
101 PackedMomentProcessorConfig processor_config; 124 Core::IrSensor::PackedMomentProcessorConfig processor_config;
102 }; 125 };
103 static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size."); 126 static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
104 127
@@ -109,19 +132,28 @@ void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
109 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, 132 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
110 parameters.applet_resource_user_id); 133 parameters.applet_resource_user_id);
111 134
135 const auto result = IsIrCameraHandleValid(parameters.camera_handle);
136
137 if (result.IsSuccess()) {
138 auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
139 MakeProcessor<MomentProcessor>(parameters.camera_handle, device);
140 auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle);
141 image_transfer_processor.SetConfig(parameters.processor_config);
142 }
143
112 IPC::ResponseBuilder rb{ctx, 2}; 144 IPC::ResponseBuilder rb{ctx, 2};
113 rb.Push(ResultSuccess); 145 rb.Push(result);
114} 146}
115 147
116void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) { 148void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
117 IPC::RequestParser rp{ctx}; 149 IPC::RequestParser rp{ctx};
118 struct Parameters { 150 struct Parameters {
119 IrCameraHandle camera_handle; 151 Core::IrSensor::IrCameraHandle camera_handle;
120 INSERT_PADDING_WORDS_NOINIT(1); 152 INSERT_PADDING_WORDS_NOINIT(1);
121 u64 applet_resource_user_id; 153 u64 applet_resource_user_id;
122 PackedClusteringProcessorConfig processor_config; 154 Core::IrSensor::PackedClusteringProcessorConfig processor_config;
123 }; 155 };
124 static_assert(sizeof(Parameters) == 0x40, "Parameters has incorrect size."); 156 static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
125 157
126 const auto parameters{rp.PopRaw<Parameters>()}; 158 const auto parameters{rp.PopRaw<Parameters>()};
127 159
@@ -130,17 +162,27 @@ void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
130 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, 162 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
131 parameters.applet_resource_user_id); 163 parameters.applet_resource_user_id);
132 164
165 auto result = IsIrCameraHandleValid(parameters.camera_handle);
166
167 if (result.IsSuccess()) {
168 auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
169 MakeProcessor<ClusteringProcessor>(parameters.camera_handle, device);
170 auto& image_transfer_processor =
171 GetProcessor<ClusteringProcessor>(parameters.camera_handle);
172 image_transfer_processor.SetConfig(parameters.processor_config);
173 }
174
133 IPC::ResponseBuilder rb{ctx, 2}; 175 IPC::ResponseBuilder rb{ctx, 2};
134 rb.Push(ResultSuccess); 176 rb.Push(result);
135} 177}
136 178
137void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) { 179void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
138 IPC::RequestParser rp{ctx}; 180 IPC::RequestParser rp{ctx};
139 struct Parameters { 181 struct Parameters {
140 IrCameraHandle camera_handle; 182 Core::IrSensor::IrCameraHandle camera_handle;
141 INSERT_PADDING_WORDS_NOINIT(1); 183 INSERT_PADDING_WORDS_NOINIT(1);
142 u64 applet_resource_user_id; 184 u64 applet_resource_user_id;
143 PackedImageTransferProcessorConfig processor_config; 185 Core::IrSensor::PackedImageTransferProcessorConfig processor_config;
144 u32 transfer_memory_size; 186 u32 transfer_memory_size;
145 }; 187 };
146 static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size."); 188 static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
@@ -151,20 +193,42 @@ void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
151 auto t_mem = 193 auto t_mem =
152 system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle); 194 system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
153 195
154 LOG_WARNING(Service_IRS, 196 if (t_mem.IsNull()) {
155 "(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, " 197 LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
156 "applet_resource_user_id={}", 198 IPC::ResponseBuilder rb{ctx, 2};
157 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, 199 rb.Push(ResultUnknown);
158 parameters.transfer_memory_size, parameters.applet_resource_user_id); 200 return;
201 }
202
203 ASSERT_MSG(t_mem->GetSize() == parameters.transfer_memory_size, "t_mem has incorrect size");
204
205 u8* transfer_memory = system.Memory().GetPointer(t_mem->GetSourceAddress());
206
207 LOG_INFO(Service_IRS,
208 "called, npad_type={}, npad_id={}, transfer_memory_size={}, transfer_memory_size={}, "
209 "applet_resource_user_id={}",
210 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
211 parameters.transfer_memory_size, t_mem->GetSize(), parameters.applet_resource_user_id);
212
213 const auto result = IsIrCameraHandleValid(parameters.camera_handle);
214
215 if (result.IsSuccess()) {
216 auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
217 MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device);
218 auto& image_transfer_processor =
219 GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
220 image_transfer_processor.SetConfig(parameters.processor_config);
221 image_transfer_processor.SetTransferMemoryPointer(transfer_memory);
222 }
159 223
160 IPC::ResponseBuilder rb{ctx, 2}; 224 IPC::ResponseBuilder rb{ctx, 2};
161 rb.Push(ResultSuccess); 225 rb.Push(result);
162} 226}
163 227
164void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) { 228void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
165 IPC::RequestParser rp{ctx}; 229 IPC::RequestParser rp{ctx};
166 struct Parameters { 230 struct Parameters {
167 IrCameraHandle camera_handle; 231 Core::IrSensor::IrCameraHandle camera_handle;
168 INSERT_PADDING_WORDS_NOINIT(1); 232 INSERT_PADDING_WORDS_NOINIT(1);
169 u64 applet_resource_user_id; 233 u64 applet_resource_user_id;
170 }; 234 };
@@ -172,32 +236,68 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
172 236
173 const auto parameters{rp.PopRaw<Parameters>()}; 237 const auto parameters{rp.PopRaw<Parameters>()};
174 238
175 LOG_WARNING(Service_IRS, 239 LOG_DEBUG(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
176 "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", 240 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
177 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, 241 parameters.applet_resource_user_id);
178 parameters.applet_resource_user_id); 242
243 const auto result = IsIrCameraHandleValid(parameters.camera_handle);
244 if (result.IsError()) {
245 IPC::ResponseBuilder rb{ctx, 2};
246 rb.Push(result);
247 return;
248 }
249
250 const auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
251
252 if (device.mode != Core::IrSensor::IrSensorMode::ImageTransferProcessor) {
253 IPC::ResponseBuilder rb{ctx, 2};
254 rb.Push(InvalidProcessorState);
255 return;
256 }
179 257
180 IPC::ResponseBuilder rb{ctx, 5}; 258 std::vector<u8> data{};
259 const auto& image_transfer_processor =
260 GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
261 const auto& state = image_transfer_processor.GetState(data);
262
263 ctx.WriteBuffer(data);
264 IPC::ResponseBuilder rb{ctx, 6};
181 rb.Push(ResultSuccess); 265 rb.Push(ResultSuccess);
182 rb.PushRaw<u64>(system.CoreTiming().GetCPUTicks()); 266 rb.PushRaw(state);
183 rb.PushRaw<u32>(0);
184} 267}
185 268
186void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) { 269void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) {
187 IPC::RequestParser rp{ctx}; 270 IPC::RequestParser rp{ctx};
188 const auto camera_handle{rp.PopRaw<IrCameraHandle>()}; 271 struct Parameters {
189 const auto processor_config{rp.PopRaw<PackedTeraPluginProcessorConfig>()}; 272 Core::IrSensor::IrCameraHandle camera_handle;
190 const auto applet_resource_user_id{rp.Pop<u64>()}; 273 Core::IrSensor::PackedTeraPluginProcessorConfig processor_config;
274 INSERT_PADDING_WORDS_NOINIT(1);
275 u64 applet_resource_user_id;
276 };
277 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
191 278
192 LOG_WARNING(Service_IRS, 279 const auto parameters{rp.PopRaw<Parameters>()};
193 "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, " 280
194 "applet_resource_user_id={}", 281 LOG_WARNING(
195 camera_handle.npad_type, camera_handle.npad_id, processor_config.mode, 282 Service_IRS,
196 processor_config.required_mcu_version.major, 283 "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, "
197 processor_config.required_mcu_version.minor, applet_resource_user_id); 284 "applet_resource_user_id={}",
285 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
286 parameters.processor_config.mode, parameters.processor_config.required_mcu_version.major,
287 parameters.processor_config.required_mcu_version.minor, parameters.applet_resource_user_id);
288
289 const auto result = IsIrCameraHandleValid(parameters.camera_handle);
290
291 if (result.IsSuccess()) {
292 auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
293 MakeProcessor<TeraPluginProcessor>(parameters.camera_handle, device);
294 auto& image_transfer_processor =
295 GetProcessor<TeraPluginProcessor>(parameters.camera_handle);
296 image_transfer_processor.SetConfig(parameters.processor_config);
297 }
198 298
199 IPC::ResponseBuilder rb{ctx, 2}; 299 IPC::ResponseBuilder rb{ctx, 2};
200 rb.Push(ResultSuccess); 300 rb.Push(result);
201} 301}
202 302
203void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) { 303void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
@@ -207,17 +307,17 @@ void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
207 if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid && 307 if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid &&
208 npad_id != Core::HID::NpadIdType::Handheld) { 308 npad_id != Core::HID::NpadIdType::Handheld) {
209 IPC::ResponseBuilder rb{ctx, 2}; 309 IPC::ResponseBuilder rb{ctx, 2};
210 rb.Push(InvalidNpadId); 310 rb.Push(Service::HID::InvalidNpadId);
211 return; 311 return;
212 } 312 }
213 313
214 IrCameraHandle camera_handle{ 314 Core::IrSensor::IrCameraHandle camera_handle{
215 .npad_id = static_cast<u8>(NpadIdTypeToIndex(npad_id)), 315 .npad_id = static_cast<u8>(NpadIdTypeToIndex(npad_id)),
216 .npad_type = Core::HID::NpadStyleIndex::None, 316 .npad_type = Core::HID::NpadStyleIndex::None,
217 }; 317 };
218 318
219 LOG_WARNING(Service_IRS, "(STUBBED) called, npad_id={}, camera_npad_id={}, camera_npad_type={}", 319 LOG_INFO(Service_IRS, "called, npad_id={}, camera_npad_id={}, camera_npad_type={}", npad_id,
220 npad_id, camera_handle.npad_id, camera_handle.npad_type); 320 camera_handle.npad_id, camera_handle.npad_type);
221 321
222 IPC::ResponseBuilder rb{ctx, 3}; 322 IPC::ResponseBuilder rb{ctx, 3};
223 rb.Push(ResultSuccess); 323 rb.Push(ResultSuccess);
@@ -226,8 +326,8 @@ void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
226 326
227void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) { 327void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
228 IPC::RequestParser rp{ctx}; 328 IPC::RequestParser rp{ctx};
229 const auto camera_handle{rp.PopRaw<IrCameraHandle>()}; 329 const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
230 const auto processor_config{rp.PopRaw<PackedPointingProcessorConfig>()}; 330 const auto processor_config{rp.PopRaw<Core::IrSensor::PackedPointingProcessorConfig>()};
231 const auto applet_resource_user_id{rp.Pop<u64>()}; 331 const auto applet_resource_user_id{rp.Pop<u64>()};
232 332
233 LOG_WARNING( 333 LOG_WARNING(
@@ -236,14 +336,23 @@ void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
236 camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major, 336 camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major,
237 processor_config.required_mcu_version.minor, applet_resource_user_id); 337 processor_config.required_mcu_version.minor, applet_resource_user_id);
238 338
339 auto result = IsIrCameraHandleValid(camera_handle);
340
341 if (result.IsSuccess()) {
342 auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
343 MakeProcessor<PointingProcessor>(camera_handle, device);
344 auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle);
345 image_transfer_processor.SetConfig(processor_config);
346 }
347
239 IPC::ResponseBuilder rb{ctx, 2}; 348 IPC::ResponseBuilder rb{ctx, 2};
240 rb.Push(ResultSuccess); 349 rb.Push(result);
241} 350}
242 351
243void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) { 352void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
244 IPC::RequestParser rp{ctx}; 353 IPC::RequestParser rp{ctx};
245 struct Parameters { 354 struct Parameters {
246 IrCameraHandle camera_handle; 355 Core::IrSensor::IrCameraHandle camera_handle;
247 INSERT_PADDING_WORDS_NOINIT(1); 356 INSERT_PADDING_WORDS_NOINIT(1);
248 u64 applet_resource_user_id; 357 u64 applet_resource_user_id;
249 }; 358 };
@@ -256,14 +365,20 @@ void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
256 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, 365 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
257 parameters.applet_resource_user_id); 366 parameters.applet_resource_user_id);
258 367
368 auto result = IsIrCameraHandleValid(parameters.camera_handle);
369 if (result.IsSuccess()) {
370 // TODO: Suspend image processor
371 result = ResultSuccess;
372 }
373
259 IPC::ResponseBuilder rb{ctx, 2}; 374 IPC::ResponseBuilder rb{ctx, 2};
260 rb.Push(ResultSuccess); 375 rb.Push(result);
261} 376}
262 377
263void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) { 378void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) {
264 IPC::RequestParser rp{ctx}; 379 IPC::RequestParser rp{ctx};
265 const auto camera_handle{rp.PopRaw<IrCameraHandle>()}; 380 const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
266 const auto mcu_version{rp.PopRaw<PackedMcuVersion>()}; 381 const auto mcu_version{rp.PopRaw<Core::IrSensor::PackedMcuVersion>()};
267 const auto applet_resource_user_id{rp.Pop<u64>()}; 382 const auto applet_resource_user_id{rp.Pop<u64>()};
268 383
269 LOG_WARNING( 384 LOG_WARNING(
@@ -272,37 +387,45 @@ void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) {
272 camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major, 387 camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major,
273 mcu_version.minor); 388 mcu_version.minor);
274 389
390 auto result = IsIrCameraHandleValid(camera_handle);
391 if (result.IsSuccess()) {
392 // TODO: Check firmware version
393 result = ResultSuccess;
394 }
395
275 IPC::ResponseBuilder rb{ctx, 2}; 396 IPC::ResponseBuilder rb{ctx, 2};
276 rb.Push(ResultSuccess); 397 rb.Push(result);
277} 398}
278 399
279void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) { 400void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) {
280 IPC::RequestParser rp{ctx}; 401 IPC::RequestParser rp{ctx};
281 struct Parameters { 402 const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
282 IrCameraHandle camera_handle; 403 const auto function_level{rp.PopRaw<Core::IrSensor::PackedFunctionLevel>()};
283 PackedFunctionLevel function_level; 404 const auto applet_resource_user_id{rp.Pop<u64>()};
284 u64 applet_resource_user_id;
285 };
286 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
287
288 const auto parameters{rp.PopRaw<Parameters>()};
289 405
290 LOG_WARNING(Service_IRS, 406 LOG_WARNING(
291 "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", 407 Service_IRS,
292 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, 408 "(STUBBED) called, npad_type={}, npad_id={}, function_level={}, applet_resource_user_id={}",
293 parameters.applet_resource_user_id); 409 camera_handle.npad_type, camera_handle.npad_id, function_level.function_level,
410 applet_resource_user_id);
411
412 auto result = IsIrCameraHandleValid(camera_handle);
413 if (result.IsSuccess()) {
414 // TODO: Set Function level
415 result = ResultSuccess;
416 }
294 417
295 IPC::ResponseBuilder rb{ctx, 2}; 418 IPC::ResponseBuilder rb{ctx, 2};
296 rb.Push(ResultSuccess); 419 rb.Push(result);
297} 420}
298 421
299void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) { 422void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
300 IPC::RequestParser rp{ctx}; 423 IPC::RequestParser rp{ctx};
301 struct Parameters { 424 struct Parameters {
302 IrCameraHandle camera_handle; 425 Core::IrSensor::IrCameraHandle camera_handle;
303 INSERT_PADDING_WORDS_NOINIT(1); 426 INSERT_PADDING_WORDS_NOINIT(1);
304 u64 applet_resource_user_id; 427 u64 applet_resource_user_id;
305 PackedImageTransferProcessorExConfig processor_config; 428 Core::IrSensor::PackedImageTransferProcessorExConfig processor_config;
306 u64 transfer_memory_size; 429 u64 transfer_memory_size;
307 }; 430 };
308 static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size."); 431 static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
@@ -313,20 +436,33 @@ void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
313 auto t_mem = 436 auto t_mem =
314 system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle); 437 system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
315 438
316 LOG_WARNING(Service_IRS, 439 u8* transfer_memory = system.Memory().GetPointer(t_mem->GetSourceAddress());
317 "(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, " 440
318 "applet_resource_user_id={}", 441 LOG_INFO(Service_IRS,
319 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, 442 "called, npad_type={}, npad_id={}, transfer_memory_size={}, "
320 parameters.transfer_memory_size, parameters.applet_resource_user_id); 443 "applet_resource_user_id={}",
444 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
445 parameters.transfer_memory_size, parameters.applet_resource_user_id);
446
447 auto result = IsIrCameraHandleValid(parameters.camera_handle);
448
449 if (result.IsSuccess()) {
450 auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
451 MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device);
452 auto& image_transfer_processor =
453 GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
454 image_transfer_processor.SetConfig(parameters.processor_config);
455 image_transfer_processor.SetTransferMemoryPointer(transfer_memory);
456 }
321 457
322 IPC::ResponseBuilder rb{ctx, 2}; 458 IPC::ResponseBuilder rb{ctx, 2};
323 rb.Push(ResultSuccess); 459 rb.Push(result);
324} 460}
325 461
326void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) { 462void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
327 IPC::RequestParser rp{ctx}; 463 IPC::RequestParser rp{ctx};
328 const auto camera_handle{rp.PopRaw<IrCameraHandle>()}; 464 const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
329 const auto processor_config{rp.PopRaw<PackedIrLedProcessorConfig>()}; 465 const auto processor_config{rp.PopRaw<Core::IrSensor::PackedIrLedProcessorConfig>()};
330 const auto applet_resource_user_id{rp.Pop<u64>()}; 466 const auto applet_resource_user_id{rp.Pop<u64>()};
331 467
332 LOG_WARNING(Service_IRS, 468 LOG_WARNING(Service_IRS,
@@ -336,14 +472,23 @@ void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
336 processor_config.required_mcu_version.major, 472 processor_config.required_mcu_version.major,
337 processor_config.required_mcu_version.minor, applet_resource_user_id); 473 processor_config.required_mcu_version.minor, applet_resource_user_id);
338 474
475 auto result = IsIrCameraHandleValid(camera_handle);
476
477 if (result.IsSuccess()) {
478 auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
479 MakeProcessor<IrLedProcessor>(camera_handle, device);
480 auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle);
481 image_transfer_processor.SetConfig(processor_config);
482 }
483
339 IPC::ResponseBuilder rb{ctx, 2}; 484 IPC::ResponseBuilder rb{ctx, 2};
340 rb.Push(ResultSuccess); 485 rb.Push(result);
341} 486}
342 487
343void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) { 488void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
344 IPC::RequestParser rp{ctx}; 489 IPC::RequestParser rp{ctx};
345 struct Parameters { 490 struct Parameters {
346 IrCameraHandle camera_handle; 491 Core::IrSensor::IrCameraHandle camera_handle;
347 INSERT_PADDING_WORDS_NOINIT(1); 492 INSERT_PADDING_WORDS_NOINIT(1);
348 u64 applet_resource_user_id; 493 u64 applet_resource_user_id;
349 }; 494 };
@@ -356,14 +501,20 @@ void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
356 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, 501 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
357 parameters.applet_resource_user_id); 502 parameters.applet_resource_user_id);
358 503
504 auto result = IsIrCameraHandleValid(parameters.camera_handle);
505 if (result.IsSuccess()) {
506 // TODO: Stop image processor async
507 result = ResultSuccess;
508 }
509
359 IPC::ResponseBuilder rb{ctx, 2}; 510 IPC::ResponseBuilder rb{ctx, 2};
360 rb.Push(ResultSuccess); 511 rb.Push(result);
361} 512}
362 513
363void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) { 514void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) {
364 IPC::RequestParser rp{ctx}; 515 IPC::RequestParser rp{ctx};
365 struct Parameters { 516 struct Parameters {
366 PackedFunctionLevel function_level; 517 Core::IrSensor::PackedFunctionLevel function_level;
367 INSERT_PADDING_WORDS_NOINIT(1); 518 INSERT_PADDING_WORDS_NOINIT(1);
368 u64 applet_resource_user_id; 519 u64 applet_resource_user_id;
369 }; 520 };
@@ -378,7 +529,22 @@ void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) {
378 rb.Push(ResultSuccess); 529 rb.Push(ResultSuccess);
379} 530}
380 531
381IRS::~IRS() = default; 532Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const {
533 if (camera_handle.npad_id >
534 static_cast<u8>(NpadIdTypeToIndex(Core::HID::NpadIdType::Handheld))) {
535 return InvalidIrCameraHandle;
536 }
537 if (camera_handle.npad_type != Core::HID::NpadStyleIndex::None) {
538 return InvalidIrCameraHandle;
539 }
540 return ResultSuccess;
541}
542
543Core::IrSensor::DeviceFormat& IRS::GetIrCameraSharedMemoryDeviceEntry(
544 const Core::IrSensor::IrCameraHandle& camera_handle) {
545 ASSERT_MSG(sizeof(StatusManager::device) > camera_handle.npad_id, "invalid npad_id");
546 return shared_memory->device[camera_handle.npad_id];
547}
382 548
383IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} { 549IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} {
384 // clang-format off 550 // clang-format off
@@ -395,4 +561,4 @@ IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} {
395 561
396IRS_SYS::~IRS_SYS() = default; 562IRS_SYS::~IRS_SYS() = default;
397 563
398} // namespace Service::HID 564} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h
index 361dc2213..2e6115c73 100644
--- a/src/core/hle/service/hid/irs.h
+++ b/src/core/hle/service/hid/irs.h
@@ -4,13 +4,19 @@
4#pragma once 4#pragma once
5 5
6#include "core/hid/hid_types.h" 6#include "core/hid/hid_types.h"
7#include "core/hid/irs_types.h"
8#include "core/hle/service/hid/irsensor/processor_base.h"
7#include "core/hle/service/service.h" 9#include "core/hle/service/service.h"
8 10
9namespace Core { 11namespace Core {
10class System; 12class System;
11} 13}
12 14
13namespace Service::HID { 15namespace Core::HID {
16class EmulatedController;
17} // namespace Core::HID
18
19namespace Service::IRS {
14 20
15class IRS final : public ServiceFramework<IRS> { 21class IRS final : public ServiceFramework<IRS> {
16public: 22public:
@@ -18,234 +24,19 @@ public:
18 ~IRS() override; 24 ~IRS() override;
19 25
20private: 26private:
21 // This is nn::irsensor::IrCameraStatus 27 // This is nn::irsensor::detail::AruidFormat
22 enum IrCameraStatus : u32 { 28 struct AruidFormat {
23 Available, 29 u64 sensor_aruid;
24 Unsupported, 30 u64 sensor_aruid_status;
25 Unconnected,
26 };
27
28 // This is nn::irsensor::IrCameraInternalStatus
29 enum IrCameraInternalStatus : u32 {
30 Stopped,
31 FirmwareUpdateNeeded,
32 Unkown2,
33 Unkown3,
34 Unkown4,
35 FirmwareVersionRequested,
36 FirmwareVersionIsInvalid,
37 Ready,
38 Setting,
39 };
40
41 // This is nn::irsensor::detail::StatusManager::IrSensorMode
42 enum IrSensorMode : u64 {
43 None,
44 MomentProcessor,
45 ClusteringProcessor,
46 ImageTransferProcessor,
47 PointingProcessorMarker,
48 TeraPluginProcessor,
49 IrLedProcessor,
50 };
51
52 // This is nn::irsensor::ImageProcessorStatus
53 enum ImageProcessorStatus : u8 {
54 stopped,
55 running,
56 };
57
58 // This is nn::irsensor::ImageTransferProcessorFormat
59 enum ImageTransferProcessorFormat : u8 {
60 Size320x240,
61 Size160x120,
62 Size80x60,
63 Size40x30,
64 Size20x15,
65 };
66
67 // This is nn::irsensor::AdaptiveClusteringMode
68 enum AdaptiveClusteringMode : u8 {
69 StaticFov,
70 DynamicFov,
71 };
72
73 // This is nn::irsensor::AdaptiveClusteringTargetDistance
74 enum AdaptiveClusteringTargetDistance : u8 {
75 Near,
76 Middle,
77 Far,
78 };
79
80 // This is nn::irsensor::IrsHandAnalysisMode
81 enum IrsHandAnalysisMode : u8 {
82 Silhouette,
83 Image,
84 SilhoueteAndImage,
85 SilhuetteOnly,
86 };
87
88 // This is nn::irsensor::IrSensorFunctionLevel
89 enum IrSensorFunctionLevel : u8 {
90 unknown0,
91 unknown1,
92 unknown2,
93 unknown3,
94 unknown4,
95 };
96
97 // This is nn::irsensor::IrCameraHandle
98 struct IrCameraHandle {
99 u8 npad_id{};
100 Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
101 INSERT_PADDING_BYTES(2);
102 };
103 static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size");
104
105 struct IrsRect {
106 s16 x;
107 s16 y;
108 s16 width;
109 s16 height;
110 }; 31 };
32 static_assert(sizeof(AruidFormat) == 0x10, "AruidFormat is an invalid size");
111 33
112 // This is nn::irsensor::PackedMcuVersion 34 // This is nn::irsensor::detail::StatusManager
113 struct PackedMcuVersion { 35 struct StatusManager {
114 u16 major; 36 std::array<Core::IrSensor::DeviceFormat, 9> device;
115 u16 minor; 37 std::array<AruidFormat, 5> aruid;
116 }; 38 };
117 static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size"); 39 static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size");
118
119 // This is nn::irsensor::MomentProcessorConfig
120 struct MomentProcessorConfig {
121 u64 exposire_time;
122 u8 light_target;
123 u8 gain;
124 u8 is_negative_used;
125 INSERT_PADDING_BYTES(7);
126 IrsRect window_of_interest;
127 u8 preprocess;
128 u8 preprocess_intensity_threshold;
129 INSERT_PADDING_BYTES(5);
130 };
131 static_assert(sizeof(MomentProcessorConfig) == 0x28,
132 "MomentProcessorConfig is an invalid size");
133
134 // This is nn::irsensor::PackedMomentProcessorConfig
135 struct PackedMomentProcessorConfig {
136 u64 exposire_time;
137 u8 light_target;
138 u8 gain;
139 u8 is_negative_used;
140 INSERT_PADDING_BYTES(5);
141 IrsRect window_of_interest;
142 PackedMcuVersion required_mcu_version;
143 u8 preprocess;
144 u8 preprocess_intensity_threshold;
145 INSERT_PADDING_BYTES(2);
146 };
147 static_assert(sizeof(PackedMomentProcessorConfig) == 0x20,
148 "PackedMomentProcessorConfig is an invalid size");
149
150 // This is nn::irsensor::ClusteringProcessorConfig
151 struct ClusteringProcessorConfig {
152 u64 exposire_time;
153 u32 light_target;
154 u32 gain;
155 u8 is_negative_used;
156 INSERT_PADDING_BYTES(7);
157 IrsRect window_of_interest;
158 u32 pixel_count_min;
159 u32 pixel_count_max;
160 u32 object_intensity_min;
161 u8 is_external_light_filter_enabled;
162 INSERT_PADDING_BYTES(3);
163 };
164 static_assert(sizeof(ClusteringProcessorConfig) == 0x30,
165 "ClusteringProcessorConfig is an invalid size");
166
167 // This is nn::irsensor::PackedClusteringProcessorConfig
168 struct PackedClusteringProcessorConfig {
169 u64 exposire_time;
170 u8 light_target;
171 u8 gain;
172 u8 is_negative_used;
173 INSERT_PADDING_BYTES(5);
174 IrsRect window_of_interest;
175 PackedMcuVersion required_mcu_version;
176 u32 pixel_count_min;
177 u32 pixel_count_max;
178 u32 object_intensity_min;
179 u8 is_external_light_filter_enabled;
180 INSERT_PADDING_BYTES(2);
181 };
182 static_assert(sizeof(PackedClusteringProcessorConfig) == 0x30,
183 "PackedClusteringProcessorConfig is an invalid size");
184
185 // This is nn::irsensor::PackedImageTransferProcessorConfig
186 struct PackedImageTransferProcessorConfig {
187 u64 exposire_time;
188 u8 light_target;
189 u8 gain;
190 u8 is_negative_used;
191 INSERT_PADDING_BYTES(5);
192 PackedMcuVersion required_mcu_version;
193 u8 format;
194 INSERT_PADDING_BYTES(3);
195 };
196 static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18,
197 "PackedImageTransferProcessorConfig is an invalid size");
198
199 // This is nn::irsensor::PackedTeraPluginProcessorConfig
200 struct PackedTeraPluginProcessorConfig {
201 PackedMcuVersion required_mcu_version;
202 u8 mode;
203 INSERT_PADDING_BYTES(3);
204 };
205 static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8,
206 "PackedTeraPluginProcessorConfig is an invalid size");
207
208 // This is nn::irsensor::PackedPointingProcessorConfig
209 struct PackedPointingProcessorConfig {
210 IrsRect window_of_interest;
211 PackedMcuVersion required_mcu_version;
212 };
213 static_assert(sizeof(PackedPointingProcessorConfig) == 0xC,
214 "PackedPointingProcessorConfig is an invalid size");
215
216 // This is nn::irsensor::PackedFunctionLevel
217 struct PackedFunctionLevel {
218 IrSensorFunctionLevel function_level;
219 INSERT_PADDING_BYTES(3);
220 };
221 static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size");
222
223 // This is nn::irsensor::PackedImageTransferProcessorExConfig
224 struct PackedImageTransferProcessorExConfig {
225 u64 exposire_time;
226 u8 light_target;
227 u8 gain;
228 u8 is_negative_used;
229 INSERT_PADDING_BYTES(5);
230 PackedMcuVersion required_mcu_version;
231 ImageTransferProcessorFormat origin_format;
232 ImageTransferProcessorFormat trimming_format;
233 u16 trimming_start_x;
234 u16 trimming_start_y;
235 u8 is_external_light_filter_enabled;
236 INSERT_PADDING_BYTES(3);
237 };
238 static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20,
239 "PackedImageTransferProcessorExConfig is an invalid size");
240
241 // This is nn::irsensor::PackedIrLedProcessorConfig
242 struct PackedIrLedProcessorConfig {
243 PackedMcuVersion required_mcu_version;
244 u8 light_target;
245 INSERT_PADDING_BYTES(3);
246 };
247 static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8,
248 "PackedIrLedProcessorConfig is an invalid size");
249 40
250 void ActivateIrsensor(Kernel::HLERequestContext& ctx); 41 void ActivateIrsensor(Kernel::HLERequestContext& ctx);
251 void DeactivateIrsensor(Kernel::HLERequestContext& ctx); 42 void DeactivateIrsensor(Kernel::HLERequestContext& ctx);
@@ -265,6 +56,56 @@ private:
265 void RunIrLedProcessor(Kernel::HLERequestContext& ctx); 56 void RunIrLedProcessor(Kernel::HLERequestContext& ctx);
266 void StopImageProcessorAsync(Kernel::HLERequestContext& ctx); 57 void StopImageProcessorAsync(Kernel::HLERequestContext& ctx);
267 void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx); 58 void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
59
60 Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const;
61 Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry(
62 const Core::IrSensor::IrCameraHandle& camera_handle);
63
64 template <typename T>
65 void MakeProcessor(const Core::IrSensor::IrCameraHandle& handle,
66 Core::IrSensor::DeviceFormat& device_state) {
67 const auto index = static_cast<std::size_t>(handle.npad_id);
68 if (index > sizeof(processors)) {
69 LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
70 return;
71 }
72 processors[index] = std::make_unique<T>(device_state);
73 }
74
75 template <typename T>
76 void MakeProcessorWithCoreContext(const Core::IrSensor::IrCameraHandle& handle,
77 Core::IrSensor::DeviceFormat& device_state) {
78 const auto index = static_cast<std::size_t>(handle.npad_id);
79 if (index > sizeof(processors)) {
80 LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
81 return;
82 }
83 processors[index] = std::make_unique<T>(system.HIDCore(), device_state, index);
84 }
85
86 template <typename T>
87 T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) {
88 const auto index = static_cast<std::size_t>(handle.npad_id);
89 if (index > sizeof(processors)) {
90 LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
91 return static_cast<T&>(*processors[0]);
92 }
93 return static_cast<T&>(*processors[index]);
94 }
95
96 template <typename T>
97 const T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) const {
98 const auto index = static_cast<std::size_t>(handle.npad_id);
99 if (index > sizeof(processors)) {
100 LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
101 return static_cast<T&>(*processors[0]);
102 }
103 return static_cast<T&>(*processors[index]);
104 }
105
106 Core::HID::EmulatedController* npad_device = nullptr;
107 StatusManager* shared_memory = nullptr;
108 std::array<std::unique_ptr<ProcessorBase>, 9> processors{};
268}; 109};
269 110
270class IRS_SYS final : public ServiceFramework<IRS_SYS> { 111class IRS_SYS final : public ServiceFramework<IRS_SYS> {
@@ -273,4 +114,4 @@ public:
273 ~IRS_SYS() override; 114 ~IRS_SYS() override;
274}; 115};
275 116
276} // namespace Service::HID 117} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.cpp b/src/core/hle/service/hid/irsensor/clustering_processor.cpp
new file mode 100644
index 000000000..6479af212
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/clustering_processor.cpp
@@ -0,0 +1,34 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/hid/irsensor/clustering_processor.h"
5
6namespace Service::IRS {
7ClusteringProcessor::ClusteringProcessor(Core::IrSensor::DeviceFormat& device_format)
8 : device(device_format) {
9 device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor;
10 device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
11 device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
12}
13
14ClusteringProcessor::~ClusteringProcessor() = default;
15
16void ClusteringProcessor::StartProcessor() {}
17
18void ClusteringProcessor::SuspendProcessor() {}
19
20void ClusteringProcessor::StopProcessor() {}
21
22void ClusteringProcessor::SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config) {
23 current_config.camera_config.exposure_time = config.camera_config.exposure_time;
24 current_config.camera_config.gain = config.camera_config.gain;
25 current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
26 current_config.camera_config.light_target =
27 static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
28 current_config.pixel_count_min = config.pixel_count_min;
29 current_config.pixel_count_max = config.pixel_count_max;
30 current_config.is_external_light_filter_enabled = config.is_external_light_filter_enabled;
31 current_config.object_intensity_min = config.object_intensity_min;
32}
33
34} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.h b/src/core/hle/service/hid/irsensor/clustering_processor.h
new file mode 100644
index 000000000..6e2ba8846
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/clustering_processor.h
@@ -0,0 +1,74 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hid/irs_types.h"
8#include "core/hle/service/hid/irsensor/processor_base.h"
9
10namespace Service::IRS {
11class ClusteringProcessor final : public ProcessorBase {
12public:
13 explicit ClusteringProcessor(Core::IrSensor::DeviceFormat& device_format);
14 ~ClusteringProcessor() override;
15
16 // Called when the processor is initialized
17 void StartProcessor() override;
18
19 // Called when the processor is suspended
20 void SuspendProcessor() override;
21
22 // Called when the processor is stopped
23 void StopProcessor() override;
24
25 // Sets config parameters of the camera
26 void SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config);
27
28private:
29 // This is nn::irsensor::ClusteringProcessorConfig
30 struct ClusteringProcessorConfig {
31 Core::IrSensor::CameraConfig camera_config;
32 Core::IrSensor::IrsRect window_of_interest;
33 u32 pixel_count_min;
34 u32 pixel_count_max;
35 u32 object_intensity_min;
36 bool is_external_light_filter_enabled;
37 INSERT_PADDING_BYTES(3);
38 };
39 static_assert(sizeof(ClusteringProcessorConfig) == 0x30,
40 "ClusteringProcessorConfig is an invalid size");
41
42 // This is nn::irsensor::AdaptiveClusteringProcessorConfig
43 struct AdaptiveClusteringProcessorConfig {
44 Core::IrSensor::AdaptiveClusteringMode mode;
45 Core::IrSensor::AdaptiveClusteringTargetDistance target_distance;
46 };
47 static_assert(sizeof(AdaptiveClusteringProcessorConfig) == 0x8,
48 "AdaptiveClusteringProcessorConfig is an invalid size");
49
50 // This is nn::irsensor::ClusteringData
51 struct ClusteringData {
52 f32 average_intensity;
53 Core::IrSensor::IrsCentroid centroid;
54 u32 pixel_count;
55 Core::IrSensor::IrsRect bound;
56 };
57 static_assert(sizeof(ClusteringData) == 0x18, "ClusteringData is an invalid size");
58
59 // This is nn::irsensor::ClusteringProcessorState
60 struct ClusteringProcessorState {
61 s64 sampling_number;
62 u64 timestamp;
63 u8 object_count;
64 INSERT_PADDING_BYTES(3);
65 Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
66 std::array<ClusteringData, 0x10> data;
67 };
68 static_assert(sizeof(ClusteringProcessorState) == 0x198,
69 "ClusteringProcessorState is an invalid size");
70
71 ClusteringProcessorConfig current_config{};
72 Core::IrSensor::DeviceFormat& device;
73};
74} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp b/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp
new file mode 100644
index 000000000..98f0c579d
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp
@@ -0,0 +1,150 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hid/emulated_controller.h"
5#include "core/hid/hid_core.h"
6#include "core/hle/service/hid/irsensor/image_transfer_processor.h"
7
8namespace Service::IRS {
9ImageTransferProcessor::ImageTransferProcessor(Core::HID::HIDCore& hid_core_,
10 Core::IrSensor::DeviceFormat& device_format,
11 std::size_t npad_index)
12 : device{device_format} {
13 npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index);
14
15 Core::HID::ControllerUpdateCallback engine_callback{
16 .on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); },
17 .is_npad_service = true,
18 };
19 callback_key = npad_device->SetCallback(engine_callback);
20
21 device.mode = Core::IrSensor::IrSensorMode::ImageTransferProcessor;
22 device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
23 device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
24}
25
26ImageTransferProcessor::~ImageTransferProcessor() {
27 npad_device->DeleteCallback(callback_key);
28};
29
30void ImageTransferProcessor::StartProcessor() {
31 is_active = true;
32 device.camera_status = Core::IrSensor::IrCameraStatus::Available;
33 device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready;
34 processor_state.sampling_number = 0;
35 processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
36}
37
38void ImageTransferProcessor::SuspendProcessor() {}
39
40void ImageTransferProcessor::StopProcessor() {}
41
42void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) {
43 if (type != Core::HID::ControllerTriggerType::IrSensor) {
44 return;
45 }
46 if (!is_transfer_memory_set) {
47 return;
48 }
49
50 const auto camera_data = npad_device->GetCamera();
51
52 // This indicates how much ambient light is precent
53 processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
54 processor_state.sampling_number = camera_data.sample;
55
56 if (camera_data.format != current_config.origin_format) {
57 LOG_WARNING(Service_IRS, "Wrong Input format {} expected {}", camera_data.format,
58 current_config.origin_format);
59 memset(transfer_memory, 0, GetDataSize(current_config.trimming_format));
60 return;
61 }
62
63 if (current_config.origin_format > current_config.trimming_format) {
64 LOG_WARNING(Service_IRS, "Origin format {} is smaller than trimming format {}",
65 current_config.origin_format, current_config.trimming_format);
66 memset(transfer_memory, 0, GetDataSize(current_config.trimming_format));
67 return;
68 }
69
70 std::vector<u8> window_data{};
71 const auto origin_width = GetDataWidth(current_config.origin_format);
72 const auto origin_height = GetDataHeight(current_config.origin_format);
73 const auto trimming_width = GetDataWidth(current_config.trimming_format);
74 const auto trimming_height = GetDataHeight(current_config.trimming_format);
75 window_data.resize(GetDataSize(current_config.trimming_format));
76
77 if (trimming_width + current_config.trimming_start_x > origin_width ||
78 trimming_height + current_config.trimming_start_y > origin_height) {
79 LOG_WARNING(Service_IRS,
80 "Trimming area ({}, {}, {}, {}) is outside of origin area ({}, {})",
81 current_config.trimming_start_x, current_config.trimming_start_y,
82 trimming_width, trimming_height, origin_width, origin_height);
83 memset(transfer_memory, 0, GetDataSize(current_config.trimming_format));
84 return;
85 }
86
87 for (std::size_t y = 0; y < trimming_height; y++) {
88 for (std::size_t x = 0; x < trimming_width; x++) {
89 const std::size_t window_index = (y * trimming_width) + x;
90 const std::size_t origin_index =
91 ((y + current_config.trimming_start_y) * origin_width) + x +
92 current_config.trimming_start_x;
93 window_data[window_index] = camera_data.data[origin_index];
94 }
95 }
96
97 memcpy(transfer_memory, window_data.data(), GetDataSize(current_config.trimming_format));
98
99 if (!IsProcessorActive()) {
100 StartProcessor();
101 }
102}
103
104void ImageTransferProcessor::SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config) {
105 current_config.camera_config.exposure_time = config.camera_config.exposure_time;
106 current_config.camera_config.gain = config.camera_config.gain;
107 current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
108 current_config.camera_config.light_target =
109 static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
110 current_config.origin_format =
111 static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format);
112 current_config.trimming_format =
113 static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format);
114 current_config.trimming_start_x = 0;
115 current_config.trimming_start_y = 0;
116
117 npad_device->SetCameraFormat(current_config.origin_format);
118}
119
120void ImageTransferProcessor::SetConfig(
121 Core::IrSensor::PackedImageTransferProcessorExConfig config) {
122 current_config.camera_config.exposure_time = config.camera_config.exposure_time;
123 current_config.camera_config.gain = config.camera_config.gain;
124 current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
125 current_config.camera_config.light_target =
126 static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
127 current_config.origin_format =
128 static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.origin_format);
129 current_config.trimming_format =
130 static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.trimming_format);
131 current_config.trimming_start_x = config.trimming_start_x;
132 current_config.trimming_start_y = config.trimming_start_y;
133
134 npad_device->SetCameraFormat(current_config.origin_format);
135}
136
137void ImageTransferProcessor::SetTransferMemoryPointer(u8* t_mem) {
138 is_transfer_memory_set = true;
139 transfer_memory = t_mem;
140}
141
142Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState(
143 std::vector<u8>& data) const {
144 const auto size = GetDataSize(current_config.trimming_format);
145 data.resize(size);
146 memcpy(data.data(), transfer_memory, size);
147 return processor_state;
148}
149
150} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/image_transfer_processor.h b/src/core/hle/service/hid/irsensor/image_transfer_processor.h
new file mode 100644
index 000000000..393df492d
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/image_transfer_processor.h
@@ -0,0 +1,73 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hid/irs_types.h"
8#include "core/hle/service/hid/irsensor/processor_base.h"
9
10namespace Core::HID {
11class EmulatedController;
12} // namespace Core::HID
13
14namespace Service::IRS {
15class ImageTransferProcessor final : public ProcessorBase {
16public:
17 explicit ImageTransferProcessor(Core::HID::HIDCore& hid_core_,
18 Core::IrSensor::DeviceFormat& device_format,
19 std::size_t npad_index);
20 ~ImageTransferProcessor() override;
21
22 // Called when the processor is initialized
23 void StartProcessor() override;
24
25 // Called when the processor is suspended
26 void SuspendProcessor() override;
27
28 // Called when the processor is stopped
29 void StopProcessor() override;
30
31 // Sets config parameters of the camera
32 void SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config);
33 void SetConfig(Core::IrSensor::PackedImageTransferProcessorExConfig config);
34
35 // Transfer memory where the image data will be stored
36 void SetTransferMemoryPointer(u8* t_mem);
37
38 Core::IrSensor::ImageTransferProcessorState GetState(std::vector<u8>& data) const;
39
40private:
41 // This is nn::irsensor::ImageTransferProcessorConfig
42 struct ImageTransferProcessorConfig {
43 Core::IrSensor::CameraConfig camera_config;
44 Core::IrSensor::ImageTransferProcessorFormat format;
45 };
46 static_assert(sizeof(ImageTransferProcessorConfig) == 0x20,
47 "ImageTransferProcessorConfig is an invalid size");
48
49 // This is nn::irsensor::ImageTransferProcessorExConfig
50 struct ImageTransferProcessorExConfig {
51 Core::IrSensor::CameraConfig camera_config;
52 Core::IrSensor::ImageTransferProcessorFormat origin_format;
53 Core::IrSensor::ImageTransferProcessorFormat trimming_format;
54 u16 trimming_start_x;
55 u16 trimming_start_y;
56 bool is_external_light_filter_enabled;
57 INSERT_PADDING_BYTES(3);
58 };
59 static_assert(sizeof(ImageTransferProcessorExConfig) == 0x28,
60 "ImageTransferProcessorExConfig is an invalid size");
61
62 void OnControllerUpdate(Core::HID::ControllerTriggerType type);
63
64 ImageTransferProcessorExConfig current_config{};
65 Core::IrSensor::ImageTransferProcessorState processor_state{};
66 Core::IrSensor::DeviceFormat& device;
67 Core::HID::EmulatedController* npad_device;
68 int callback_key{};
69
70 u8* transfer_memory = nullptr;
71 bool is_transfer_memory_set = false;
72};
73} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/ir_led_processor.cpp b/src/core/hle/service/hid/irsensor/ir_led_processor.cpp
new file mode 100644
index 000000000..8e6dd99e4
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/ir_led_processor.cpp
@@ -0,0 +1,27 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/hid/irsensor/ir_led_processor.h"
5
6namespace Service::IRS {
7IrLedProcessor::IrLedProcessor(Core::IrSensor::DeviceFormat& device_format)
8 : device(device_format) {
9 device.mode = Core::IrSensor::IrSensorMode::IrLedProcessor;
10 device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
11 device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
12}
13
14IrLedProcessor::~IrLedProcessor() = default;
15
16void IrLedProcessor::StartProcessor() {}
17
18void IrLedProcessor::SuspendProcessor() {}
19
20void IrLedProcessor::StopProcessor() {}
21
22void IrLedProcessor::SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config) {
23 current_config.light_target =
24 static_cast<Core::IrSensor::CameraLightTarget>(config.light_target);
25}
26
27} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/ir_led_processor.h b/src/core/hle/service/hid/irsensor/ir_led_processor.h
new file mode 100644
index 000000000..c3d8693c9
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/ir_led_processor.h
@@ -0,0 +1,47 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/bit_field.h"
7#include "common/common_types.h"
8#include "core/hid/irs_types.h"
9#include "core/hle/service/hid/irsensor/processor_base.h"
10
11namespace Service::IRS {
12class IrLedProcessor final : public ProcessorBase {
13public:
14 explicit IrLedProcessor(Core::IrSensor::DeviceFormat& device_format);
15 ~IrLedProcessor() override;
16
17 // Called when the processor is initialized
18 void StartProcessor() override;
19
20 // Called when the processor is suspended
21 void SuspendProcessor() override;
22
23 // Called when the processor is stopped
24 void StopProcessor() override;
25
26 // Sets config parameters of the camera
27 void SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config);
28
29private:
30 // This is nn::irsensor::IrLedProcessorConfig
31 struct IrLedProcessorConfig {
32 Core::IrSensor::CameraLightTarget light_target;
33 };
34 static_assert(sizeof(IrLedProcessorConfig) == 0x4, "IrLedProcessorConfig is an invalid size");
35
36 struct IrLedProcessorState {
37 s64 sampling_number;
38 u64 timestamp;
39 std::array<u8, 0x8> data;
40 };
41 static_assert(sizeof(IrLedProcessorState) == 0x18, "IrLedProcessorState is an invalid size");
42
43 IrLedProcessorConfig current_config{};
44 Core::IrSensor::DeviceFormat& device;
45};
46
47} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/moment_processor.cpp b/src/core/hle/service/hid/irsensor/moment_processor.cpp
new file mode 100644
index 000000000..dbaca420a
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/moment_processor.cpp
@@ -0,0 +1,34 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/hid/irsensor/moment_processor.h"
5
6namespace Service::IRS {
7MomentProcessor::MomentProcessor(Core::IrSensor::DeviceFormat& device_format)
8 : device(device_format) {
9 device.mode = Core::IrSensor::IrSensorMode::MomentProcessor;
10 device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
11 device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
12}
13
14MomentProcessor::~MomentProcessor() = default;
15
16void MomentProcessor::StartProcessor() {}
17
18void MomentProcessor::SuspendProcessor() {}
19
20void MomentProcessor::StopProcessor() {}
21
22void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig config) {
23 current_config.camera_config.exposure_time = config.camera_config.exposure_time;
24 current_config.camera_config.gain = config.camera_config.gain;
25 current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
26 current_config.camera_config.light_target =
27 static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
28 current_config.window_of_interest = config.window_of_interest;
29 current_config.preprocess =
30 static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess);
31 current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold;
32}
33
34} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/moment_processor.h b/src/core/hle/service/hid/irsensor/moment_processor.h
new file mode 100644
index 000000000..d4bd22e0f
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/moment_processor.h
@@ -0,0 +1,61 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/bit_field.h"
7#include "common/common_types.h"
8#include "core/hid/irs_types.h"
9#include "core/hle/service/hid/irsensor/processor_base.h"
10
11namespace Service::IRS {
12class MomentProcessor final : public ProcessorBase {
13public:
14 explicit MomentProcessor(Core::IrSensor::DeviceFormat& device_format);
15 ~MomentProcessor() override;
16
17 // Called when the processor is initialized
18 void StartProcessor() override;
19
20 // Called when the processor is suspended
21 void SuspendProcessor() override;
22
23 // Called when the processor is stopped
24 void StopProcessor() override;
25
26 // Sets config parameters of the camera
27 void SetConfig(Core::IrSensor::PackedMomentProcessorConfig config);
28
29private:
30 // This is nn::irsensor::MomentProcessorConfig
31 struct MomentProcessorConfig {
32 Core::IrSensor::CameraConfig camera_config;
33 Core::IrSensor::IrsRect window_of_interest;
34 Core::IrSensor::MomentProcessorPreprocess preprocess;
35 u32 preprocess_intensity_threshold;
36 };
37 static_assert(sizeof(MomentProcessorConfig) == 0x28,
38 "MomentProcessorConfig is an invalid size");
39
40 // This is nn::irsensor::MomentStatistic
41 struct MomentStatistic {
42 f32 average_intensity;
43 Core::IrSensor::IrsCentroid centroid;
44 };
45 static_assert(sizeof(MomentStatistic) == 0xC, "MomentStatistic is an invalid size");
46
47 // This is nn::irsensor::MomentProcessorState
48 struct MomentProcessorState {
49 s64 sampling_number;
50 u64 timestamp;
51 Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
52 INSERT_PADDING_BYTES(4);
53 std::array<MomentStatistic, 0x30> stadistic;
54 };
55 static_assert(sizeof(MomentProcessorState) == 0x258, "MomentProcessorState is an invalid size");
56
57 MomentProcessorConfig current_config{};
58 Core::IrSensor::DeviceFormat& device;
59};
60
61} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/pointing_processor.cpp b/src/core/hle/service/hid/irsensor/pointing_processor.cpp
new file mode 100644
index 000000000..929f177fc
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/pointing_processor.cpp
@@ -0,0 +1,26 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/hid/irsensor/pointing_processor.h"
5
6namespace Service::IRS {
7PointingProcessor::PointingProcessor(Core::IrSensor::DeviceFormat& device_format)
8 : device(device_format) {
9 device.mode = Core::IrSensor::IrSensorMode::PointingProcessorMarker;
10 device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
11 device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
12}
13
14PointingProcessor::~PointingProcessor() = default;
15
16void PointingProcessor::StartProcessor() {}
17
18void PointingProcessor::SuspendProcessor() {}
19
20void PointingProcessor::StopProcessor() {}
21
22void PointingProcessor::SetConfig(Core::IrSensor::PackedPointingProcessorConfig config) {
23 current_config.window_of_interest = config.window_of_interest;
24}
25
26} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/pointing_processor.h b/src/core/hle/service/hid/irsensor/pointing_processor.h
new file mode 100644
index 000000000..cf4930794
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/pointing_processor.h
@@ -0,0 +1,61 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hid/irs_types.h"
8#include "core/hle/service/hid/irsensor/processor_base.h"
9
10namespace Service::IRS {
11class PointingProcessor final : public ProcessorBase {
12public:
13 explicit PointingProcessor(Core::IrSensor::DeviceFormat& device_format);
14 ~PointingProcessor() override;
15
16 // Called when the processor is initialized
17 void StartProcessor() override;
18
19 // Called when the processor is suspended
20 void SuspendProcessor() override;
21
22 // Called when the processor is stopped
23 void StopProcessor() override;
24
25 // Sets config parameters of the camera
26 void SetConfig(Core::IrSensor::PackedPointingProcessorConfig config);
27
28private:
29 // This is nn::irsensor::PointingProcessorConfig
30 struct PointingProcessorConfig {
31 Core::IrSensor::IrsRect window_of_interest;
32 };
33 static_assert(sizeof(PointingProcessorConfig) == 0x8,
34 "PointingProcessorConfig is an invalid size");
35
36 struct PointingProcessorMarkerData {
37 u8 pointing_status;
38 INSERT_PADDING_BYTES(3);
39 u32 unknown;
40 float unkown_float1;
41 float position_x;
42 float position_y;
43 float unkown_float2;
44 Core::IrSensor::IrsRect window_of_interest;
45 };
46 static_assert(sizeof(PointingProcessorMarkerData) == 0x20,
47 "PointingProcessorMarkerData is an invalid size");
48
49 struct PointingProcessorMarkerState {
50 s64 sampling_number;
51 u64 timestamp;
52 std::array<PointingProcessorMarkerData, 0x3> data;
53 };
54 static_assert(sizeof(PointingProcessorMarkerState) == 0x70,
55 "PointingProcessorMarkerState is an invalid size");
56
57 PointingProcessorConfig current_config{};
58 Core::IrSensor::DeviceFormat& device;
59};
60
61} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/processor_base.cpp b/src/core/hle/service/hid/irsensor/processor_base.cpp
new file mode 100644
index 000000000..4d43ca17a
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/processor_base.cpp
@@ -0,0 +1,67 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/hid/irsensor/processor_base.h"
5
6namespace Service::IRS {
7
8ProcessorBase::ProcessorBase() {}
9ProcessorBase::~ProcessorBase() = default;
10
11bool ProcessorBase::IsProcessorActive() const {
12 return is_active;
13}
14
15std::size_t ProcessorBase::GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const {
16 switch (format) {
17 case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
18 return 320 * 240;
19 case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
20 return 160 * 120;
21 case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
22 return 80 * 60;
23 case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
24 return 40 * 30;
25 case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
26 return 20 * 15;
27 default:
28 return 0;
29 }
30}
31
32std::size_t ProcessorBase::GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const {
33 switch (format) {
34 case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
35 return 320;
36 case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
37 return 160;
38 case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
39 return 80;
40 case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
41 return 40;
42 case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
43 return 20;
44 default:
45 return 0;
46 }
47}
48
49std::size_t ProcessorBase::GetDataHeight(
50 Core::IrSensor::ImageTransferProcessorFormat format) const {
51 switch (format) {
52 case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
53 return 240;
54 case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
55 return 120;
56 case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
57 return 60;
58 case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
59 return 30;
60 case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
61 return 15;
62 default:
63 return 0;
64 }
65}
66
67} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/processor_base.h b/src/core/hle/service/hid/irsensor/processor_base.h
new file mode 100644
index 000000000..bc0d2977b
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/processor_base.h
@@ -0,0 +1,33 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hid/irs_types.h"
8
9namespace Service::IRS {
10class ProcessorBase {
11public:
12 explicit ProcessorBase();
13 virtual ~ProcessorBase();
14
15 virtual void StartProcessor() = 0;
16 virtual void SuspendProcessor() = 0;
17 virtual void StopProcessor() = 0;
18
19 bool IsProcessorActive() const;
20
21protected:
22 /// Returns the number of bytes the image uses
23 std::size_t GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const;
24
25 /// Returns the width of the image
26 std::size_t GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const;
27
28 /// Returns the height of the image
29 std::size_t GetDataHeight(Core::IrSensor::ImageTransferProcessorFormat format) const;
30
31 bool is_active{false};
32};
33} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp b/src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp
new file mode 100644
index 000000000..e691c840a
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp
@@ -0,0 +1,29 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/hid/irsensor/tera_plugin_processor.h"
5
6namespace Service::IRS {
7TeraPluginProcessor::TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format)
8 : device(device_format) {
9 device.mode = Core::IrSensor::IrSensorMode::TeraPluginProcessor;
10 device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
11 device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
12}
13
14TeraPluginProcessor::~TeraPluginProcessor() = default;
15
16void TeraPluginProcessor::StartProcessor() {}
17
18void TeraPluginProcessor::SuspendProcessor() {}
19
20void TeraPluginProcessor::StopProcessor() {}
21
22void TeraPluginProcessor::SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config) {
23 current_config.mode = config.mode;
24 current_config.unknown_1 = config.unknown_1;
25 current_config.unknown_2 = config.unknown_2;
26 current_config.unknown_3 = config.unknown_3;
27}
28
29} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/tera_plugin_processor.h b/src/core/hle/service/hid/irsensor/tera_plugin_processor.h
new file mode 100644
index 000000000..bbea7ed0b
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/tera_plugin_processor.h
@@ -0,0 +1,53 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/bit_field.h"
7#include "common/common_types.h"
8#include "core/hid/irs_types.h"
9#include "core/hle/service/hid/irsensor/processor_base.h"
10
11namespace Service::IRS {
12class TeraPluginProcessor final : public ProcessorBase {
13public:
14 explicit TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format);
15 ~TeraPluginProcessor() override;
16
17 // Called when the processor is initialized
18 void StartProcessor() override;
19
20 // Called when the processor is suspended
21 void SuspendProcessor() override;
22
23 // Called when the processor is stopped
24 void StopProcessor() override;
25
26 // Sets config parameters of the camera
27 void SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config);
28
29private:
30 // This is nn::irsensor::TeraPluginProcessorConfig
31 struct TeraPluginProcessorConfig {
32 u8 mode;
33 u8 unknown_1;
34 u8 unknown_2;
35 u8 unknown_3;
36 };
37 static_assert(sizeof(TeraPluginProcessorConfig) == 0x4,
38 "TeraPluginProcessorConfig is an invalid size");
39
40 struct TeraPluginProcessorState {
41 s64 sampling_number;
42 u64 timestamp;
43 Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
44 std::array<u8, 0x12c> data;
45 };
46 static_assert(sizeof(TeraPluginProcessorState) == 0x140,
47 "TeraPluginProcessorState is an invalid size");
48
49 TeraPluginProcessorConfig current_config{};
50 Core::IrSensor::DeviceFormat& device;
51};
52
53} // namespace Service::IRS
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index 48e799cf5..90dd629c6 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -1,4 +1,6 @@
1add_library(input_common STATIC 1add_library(input_common STATIC
2 drivers/camera.cpp
3 drivers/camera.h
2 drivers/gc_adapter.cpp 4 drivers/gc_adapter.cpp
3 drivers/gc_adapter.h 5 drivers/gc_adapter.h
4 drivers/keyboard.cpp 6 drivers/keyboard.cpp
diff --git a/src/input_common/drivers/camera.cpp b/src/input_common/drivers/camera.cpp
new file mode 100644
index 000000000..dceea67e0
--- /dev/null
+++ b/src/input_common/drivers/camera.cpp
@@ -0,0 +1,82 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <fmt/format.h>
5
6#include "common/param_package.h"
7#include "input_common/drivers/camera.h"
8
9namespace InputCommon {
10constexpr PadIdentifier identifier = {
11 .guid = Common::UUID{},
12 .port = 0,
13 .pad = 0,
14};
15
16Camera::Camera(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
17 PreSetController(identifier);
18}
19
20void Camera::SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data) {
21 const std::size_t desired_width = getImageWidth();
22 const std::size_t desired_height = getImageHeight();
23 status.data.resize(desired_width * desired_height);
24
25 // Resize image to desired format
26 for (std::size_t y = 0; y < desired_height; y++) {
27 for (std::size_t x = 0; x < desired_width; x++) {
28 const std::size_t pixel_index = y * desired_width + x;
29 const std::size_t old_x = width * x / desired_width;
30 const std::size_t old_y = height * y / desired_height;
31 const std::size_t data_pixel_index = old_y * width + old_x;
32 status.data[pixel_index] = static_cast<u8>(data[data_pixel_index] & 0xFF);
33 }
34 }
35
36 SetCamera(identifier, status);
37}
38
39std::size_t Camera::getImageWidth() const {
40 switch (status.format) {
41 case Common::Input::CameraFormat::Size320x240:
42 return 320;
43 case Common::Input::CameraFormat::Size160x120:
44 return 160;
45 case Common::Input::CameraFormat::Size80x60:
46 return 80;
47 case Common::Input::CameraFormat::Size40x30:
48 return 40;
49 case Common::Input::CameraFormat::Size20x15:
50 return 20;
51 case Common::Input::CameraFormat::None:
52 default:
53 return 0;
54 }
55}
56
57std::size_t Camera::getImageHeight() const {
58 switch (status.format) {
59 case Common::Input::CameraFormat::Size320x240:
60 return 240;
61 case Common::Input::CameraFormat::Size160x120:
62 return 120;
63 case Common::Input::CameraFormat::Size80x60:
64 return 60;
65 case Common::Input::CameraFormat::Size40x30:
66 return 30;
67 case Common::Input::CameraFormat::Size20x15:
68 return 15;
69 case Common::Input::CameraFormat::None:
70 default:
71 return 0;
72 }
73}
74
75Common::Input::CameraError Camera::SetCameraFormat(
76 [[maybe_unused]] const PadIdentifier& identifier_,
77 const Common::Input::CameraFormat camera_format) {
78 status.format = camera_format;
79 return Common::Input::CameraError::None;
80}
81
82} // namespace InputCommon
diff --git a/src/input_common/drivers/camera.h b/src/input_common/drivers/camera.h
new file mode 100644
index 000000000..b8a7c75e5
--- /dev/null
+++ b/src/input_common/drivers/camera.h
@@ -0,0 +1,29 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "input_common/input_engine.h"
7
8namespace InputCommon {
9
10/**
11 * A button device factory representing a keyboard. It receives keyboard events and forward them
12 * to all button devices it created.
13 */
14class Camera final : public InputEngine {
15public:
16 explicit Camera(std::string input_engine_);
17
18 void SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data);
19
20 std::size_t getImageWidth() const;
21 std::size_t getImageHeight() const;
22
23 Common::Input::CameraError SetCameraFormat(const PadIdentifier& identifier_,
24 Common::Input::CameraFormat camera_format) override;
25
26 Common::Input::CameraStatus status{};
27};
28
29} // namespace InputCommon
diff --git a/src/input_common/input_engine.cpp b/src/input_common/input_engine.cpp
index 12214d146..6ede0e4b0 100644
--- a/src/input_common/input_engine.cpp
+++ b/src/input_common/input_engine.cpp
@@ -90,6 +90,18 @@ void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const B
90 TriggerOnMotionChange(identifier, motion, value); 90 TriggerOnMotionChange(identifier, motion, value);
91} 91}
92 92
93void InputEngine::SetCamera(const PadIdentifier& identifier,
94 const Common::Input::CameraStatus& value) {
95 {
96 std::scoped_lock lock{mutex};
97 ControllerData& controller = controller_list.at(identifier);
98 if (!configuring) {
99 controller.camera = value;
100 }
101 }
102 TriggerOnCameraChange(identifier, value);
103}
104
93bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const { 105bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
94 std::scoped_lock lock{mutex}; 106 std::scoped_lock lock{mutex};
95 const auto controller_iter = controller_list.find(identifier); 107 const auto controller_iter = controller_list.find(identifier);
@@ -165,6 +177,18 @@ BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion)
165 return controller.motions.at(motion); 177 return controller.motions.at(motion);
166} 178}
167 179
180Common::Input::CameraStatus InputEngine::GetCamera(const PadIdentifier& identifier) const {
181 std::scoped_lock lock{mutex};
182 const auto controller_iter = controller_list.find(identifier);
183 if (controller_iter == controller_list.cend()) {
184 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
185 identifier.pad, identifier.port);
186 return {};
187 }
188 const ControllerData& controller = controller_iter->second;
189 return controller.camera;
190}
191
168void InputEngine::ResetButtonState() { 192void InputEngine::ResetButtonState() {
169 for (const auto& controller : controller_list) { 193 for (const auto& controller : controller_list) {
170 for (const auto& button : controller.second.buttons) { 194 for (const auto& button : controller.second.buttons) {
@@ -317,6 +341,20 @@ void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int mot
317 }); 341 });
318} 342}
319 343
344void InputEngine::TriggerOnCameraChange(const PadIdentifier& identifier,
345 [[maybe_unused]] const Common::Input::CameraStatus& value) {
346 std::scoped_lock lock{mutex_callback};
347 for (const auto& poller_pair : callback_list) {
348 const InputIdentifier& poller = poller_pair.second;
349 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Camera, 0)) {
350 continue;
351 }
352 if (poller.callback.on_change) {
353 poller.callback.on_change();
354 }
355 }
356}
357
320bool InputEngine::IsInputIdentifierEqual(const InputIdentifier& input_identifier, 358bool InputEngine::IsInputIdentifierEqual(const InputIdentifier& input_identifier,
321 const PadIdentifier& identifier, EngineInputType type, 359 const PadIdentifier& identifier, EngineInputType type,
322 int index) const { 360 int index) const {
diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h
index 13295bd49..f6b3c4610 100644
--- a/src/input_common/input_engine.h
+++ b/src/input_common/input_engine.h
@@ -36,11 +36,12 @@ struct BasicMotion {
36// Types of input that are stored in the engine 36// Types of input that are stored in the engine
37enum class EngineInputType { 37enum class EngineInputType {
38 None, 38 None,
39 Analog,
40 Battery,
39 Button, 41 Button,
42 Camera,
40 HatButton, 43 HatButton,
41 Analog,
42 Motion, 44 Motion,
43 Battery,
44}; 45};
45 46
46namespace std { 47namespace std {
@@ -115,10 +116,17 @@ public:
115 // Sets polling mode to a controller 116 // Sets polling mode to a controller
116 virtual Common::Input::PollingError SetPollingMode( 117 virtual Common::Input::PollingError SetPollingMode(
117 [[maybe_unused]] const PadIdentifier& identifier, 118 [[maybe_unused]] const PadIdentifier& identifier,
118 [[maybe_unused]] const Common::Input::PollingMode vibration) { 119 [[maybe_unused]] const Common::Input::PollingMode polling_mode) {
119 return Common::Input::PollingError::NotSupported; 120 return Common::Input::PollingError::NotSupported;
120 } 121 }
121 122
123 // Sets camera format to a controller
124 virtual Common::Input::CameraError SetCameraFormat(
125 [[maybe_unused]] const PadIdentifier& identifier,
126 [[maybe_unused]] Common::Input::CameraFormat camera_format) {
127 return Common::Input::CameraError::NotSupported;
128 }
129
122 // Returns the engine name 130 // Returns the engine name
123 [[nodiscard]] const std::string& GetEngineName() const; 131 [[nodiscard]] const std::string& GetEngineName() const;
124 132
@@ -174,6 +182,7 @@ public:
174 f32 GetAxis(const PadIdentifier& identifier, int axis) const; 182 f32 GetAxis(const PadIdentifier& identifier, int axis) const;
175 Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const; 183 Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const;
176 BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const; 184 BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const;
185 Common::Input::CameraStatus GetCamera(const PadIdentifier& identifier) const;
177 186
178 int SetCallback(InputIdentifier input_identifier); 187 int SetCallback(InputIdentifier input_identifier);
179 void SetMappingCallback(MappingCallback callback); 188 void SetMappingCallback(MappingCallback callback);
@@ -185,6 +194,7 @@ protected:
185 void SetAxis(const PadIdentifier& identifier, int axis, f32 value); 194 void SetAxis(const PadIdentifier& identifier, int axis, f32 value);
186 void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value); 195 void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
187 void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value); 196 void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value);
197 void SetCamera(const PadIdentifier& identifier, const Common::Input::CameraStatus& value);
188 198
189 virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const { 199 virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const {
190 return "Unknown"; 200 return "Unknown";
@@ -197,6 +207,7 @@ private:
197 std::unordered_map<int, float> axes; 207 std::unordered_map<int, float> axes;
198 std::unordered_map<int, BasicMotion> motions; 208 std::unordered_map<int, BasicMotion> motions;
199 Common::Input::BatteryLevel battery{}; 209 Common::Input::BatteryLevel battery{};
210 Common::Input::CameraStatus camera{};
200 }; 211 };
201 212
202 void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value); 213 void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value);
@@ -205,6 +216,8 @@ private:
205 void TriggerOnBatteryChange(const PadIdentifier& identifier, Common::Input::BatteryLevel value); 216 void TriggerOnBatteryChange(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
206 void TriggerOnMotionChange(const PadIdentifier& identifier, int motion, 217 void TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
207 const BasicMotion& value); 218 const BasicMotion& value);
219 void TriggerOnCameraChange(const PadIdentifier& identifier,
220 const Common::Input::CameraStatus& value);
208 221
209 bool IsInputIdentifierEqual(const InputIdentifier& input_identifier, 222 bool IsInputIdentifierEqual(const InputIdentifier& input_identifier,
210 const PadIdentifier& identifier, EngineInputType type, 223 const PadIdentifier& identifier, EngineInputType type,
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp
index 49ccb4422..133422d5c 100644
--- a/src/input_common/input_poller.cpp
+++ b/src/input_common/input_poller.cpp
@@ -664,6 +664,47 @@ private:
664 InputEngine* input_engine; 664 InputEngine* input_engine;
665}; 665};
666 666
667class InputFromCamera final : public Common::Input::InputDevice {
668public:
669 explicit InputFromCamera(PadIdentifier identifier_, InputEngine* input_engine_)
670 : identifier(identifier_), input_engine(input_engine_) {
671 UpdateCallback engine_callback{[this]() { OnChange(); }};
672 const InputIdentifier input_identifier{
673 .identifier = identifier,
674 .type = EngineInputType::Camera,
675 .index = 0,
676 .callback = engine_callback,
677 };
678 callback_key = input_engine->SetCallback(input_identifier);
679 }
680
681 ~InputFromCamera() override {
682 input_engine->DeleteCallback(callback_key);
683 }
684
685 Common::Input::CameraStatus GetStatus() const {
686 return input_engine->GetCamera(identifier);
687 }
688
689 void ForceUpdate() override {
690 OnChange();
691 }
692
693 void OnChange() {
694 const Common::Input::CallbackStatus status{
695 .type = Common::Input::InputType::IrSensor,
696 .camera_status = GetStatus(),
697 };
698
699 TriggerOnChange(status);
700 }
701
702private:
703 const PadIdentifier identifier;
704 int callback_key;
705 InputEngine* input_engine;
706};
707
667class OutputFromIdentifier final : public Common::Input::OutputDevice { 708class OutputFromIdentifier final : public Common::Input::OutputDevice {
668public: 709public:
669 explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_) 710 explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_)
@@ -682,6 +723,10 @@ public:
682 return input_engine->SetPollingMode(identifier, polling_mode); 723 return input_engine->SetPollingMode(identifier, polling_mode);
683 } 724 }
684 725
726 Common::Input::CameraError SetCameraFormat(Common::Input::CameraFormat camera_format) override {
727 return input_engine->SetCameraFormat(identifier, camera_format);
728 }
729
685private: 730private:
686 const PadIdentifier identifier; 731 const PadIdentifier identifier;
687 InputEngine* input_engine; 732 InputEngine* input_engine;
@@ -920,6 +965,18 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateMotionDevice(
920 properties_y, properties_z, input_engine.get()); 965 properties_y, properties_z, input_engine.get());
921} 966}
922 967
968std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateCameraDevice(
969 const Common::ParamPackage& params) {
970 const PadIdentifier identifier = {
971 .guid = Common::UUID{params.Get("guid", "")},
972 .port = static_cast<std::size_t>(params.Get("port", 0)),
973 .pad = static_cast<std::size_t>(params.Get("pad", 0)),
974 };
975
976 input_engine->PreSetController(identifier);
977 return std::make_unique<InputFromCamera>(identifier, input_engine.get());
978}
979
923InputFactory::InputFactory(std::shared_ptr<InputEngine> input_engine_) 980InputFactory::InputFactory(std::shared_ptr<InputEngine> input_engine_)
924 : input_engine(std::move(input_engine_)) {} 981 : input_engine(std::move(input_engine_)) {}
925 982
@@ -928,6 +985,9 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::Create(
928 if (params.Has("battery")) { 985 if (params.Has("battery")) {
929 return CreateBatteryDevice(params); 986 return CreateBatteryDevice(params);
930 } 987 }
988 if (params.Has("camera")) {
989 return CreateCameraDevice(params);
990 }
931 if (params.Has("button") && params.Has("axis")) { 991 if (params.Has("button") && params.Has("axis")) {
932 return CreateTriggerDevice(params); 992 return CreateTriggerDevice(params);
933 } 993 }
diff --git a/src/input_common/input_poller.h b/src/input_common/input_poller.h
index 6ebe0dbf5..4410a8415 100644
--- a/src/input_common/input_poller.h
+++ b/src/input_common/input_poller.h
@@ -211,6 +211,17 @@ private:
211 */ 211 */
212 std::unique_ptr<Common::Input::InputDevice> CreateMotionDevice(Common::ParamPackage params); 212 std::unique_ptr<Common::Input::InputDevice> CreateMotionDevice(Common::ParamPackage params);
213 213
214 /**
215 * Creates a camera device from the parameters given.
216 * @param params contains parameters for creating the device:
217 * - "guid": text string for identifying controllers
218 * - "port": port of the connected device
219 * - "pad": slot of the connected controller
220 * @returns a unique input device with the parameters specified
221 */
222 std::unique_ptr<Common::Input::InputDevice> CreateCameraDevice(
223 const Common::ParamPackage& params);
224
214 std::shared_ptr<InputEngine> input_engine; 225 std::shared_ptr<InputEngine> input_engine;
215}; 226};
216} // namespace InputCommon 227} // namespace InputCommon
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index 21834fb6b..ca1cb9542 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -5,6 +5,7 @@
5#include <memory> 5#include <memory>
6#include "common/input.h" 6#include "common/input.h"
7#include "common/param_package.h" 7#include "common/param_package.h"
8#include "input_common/drivers/camera.h"
8#include "input_common/drivers/gc_adapter.h" 9#include "input_common/drivers/gc_adapter.h"
9#include "input_common/drivers/keyboard.h" 10#include "input_common/drivers/keyboard.h"
10#include "input_common/drivers/mouse.h" 11#include "input_common/drivers/mouse.h"
@@ -78,6 +79,15 @@ struct InputSubsystem::Impl {
78 Common::Input::RegisterFactory<Common::Input::OutputDevice>(tas_input->GetEngineName(), 79 Common::Input::RegisterFactory<Common::Input::OutputDevice>(tas_input->GetEngineName(),
79 tas_output_factory); 80 tas_output_factory);
80 81
82 camera = std::make_shared<Camera>("camera");
83 camera->SetMappingCallback(mapping_callback);
84 camera_input_factory = std::make_shared<InputFactory>(camera);
85 camera_output_factory = std::make_shared<OutputFactory>(camera);
86 Common::Input::RegisterFactory<Common::Input::InputDevice>(camera->GetEngineName(),
87 camera_input_factory);
88 Common::Input::RegisterFactory<Common::Input::OutputDevice>(camera->GetEngineName(),
89 camera_output_factory);
90
81#ifdef HAVE_SDL2 91#ifdef HAVE_SDL2
82 sdl = std::make_shared<SDLDriver>("sdl"); 92 sdl = std::make_shared<SDLDriver>("sdl");
83 sdl->SetMappingCallback(mapping_callback); 93 sdl->SetMappingCallback(mapping_callback);
@@ -317,6 +327,7 @@ struct InputSubsystem::Impl {
317 std::shared_ptr<TouchScreen> touch_screen; 327 std::shared_ptr<TouchScreen> touch_screen;
318 std::shared_ptr<TasInput::Tas> tas_input; 328 std::shared_ptr<TasInput::Tas> tas_input;
319 std::shared_ptr<CemuhookUDP::UDPClient> udp_client; 329 std::shared_ptr<CemuhookUDP::UDPClient> udp_client;
330 std::shared_ptr<Camera> camera;
320 331
321 std::shared_ptr<InputFactory> keyboard_factory; 332 std::shared_ptr<InputFactory> keyboard_factory;
322 std::shared_ptr<InputFactory> mouse_factory; 333 std::shared_ptr<InputFactory> mouse_factory;
@@ -324,12 +335,14 @@ struct InputSubsystem::Impl {
324 std::shared_ptr<InputFactory> touch_screen_factory; 335 std::shared_ptr<InputFactory> touch_screen_factory;
325 std::shared_ptr<InputFactory> udp_client_input_factory; 336 std::shared_ptr<InputFactory> udp_client_input_factory;
326 std::shared_ptr<InputFactory> tas_input_factory; 337 std::shared_ptr<InputFactory> tas_input_factory;
338 std::shared_ptr<InputFactory> camera_input_factory;
327 339
328 std::shared_ptr<OutputFactory> keyboard_output_factory; 340 std::shared_ptr<OutputFactory> keyboard_output_factory;
329 std::shared_ptr<OutputFactory> mouse_output_factory; 341 std::shared_ptr<OutputFactory> mouse_output_factory;
330 std::shared_ptr<OutputFactory> gcadapter_output_factory; 342 std::shared_ptr<OutputFactory> gcadapter_output_factory;
331 std::shared_ptr<OutputFactory> udp_client_output_factory; 343 std::shared_ptr<OutputFactory> udp_client_output_factory;
332 std::shared_ptr<OutputFactory> tas_output_factory; 344 std::shared_ptr<OutputFactory> tas_output_factory;
345 std::shared_ptr<OutputFactory> camera_output_factory;
333 346
334#ifdef HAVE_SDL2 347#ifdef HAVE_SDL2
335 std::shared_ptr<SDLDriver> sdl; 348 std::shared_ptr<SDLDriver> sdl;
@@ -382,6 +395,14 @@ const TasInput::Tas* InputSubsystem::GetTas() const {
382 return impl->tas_input.get(); 395 return impl->tas_input.get();
383} 396}
384 397
398Camera* InputSubsystem::GetCamera() {
399 return impl->camera.get();
400}
401
402const Camera* InputSubsystem::GetCamera() const {
403 return impl->camera.get();
404}
405
385std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const { 406std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
386 return impl->GetInputDevices(); 407 return impl->GetInputDevices();
387} 408}
diff --git a/src/input_common/main.h b/src/input_common/main.h
index 147c310c4..b756bb5c6 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -30,6 +30,7 @@ enum Values : int;
30} 30}
31 31
32namespace InputCommon { 32namespace InputCommon {
33class Camera;
33class Keyboard; 34class Keyboard;
34class Mouse; 35class Mouse;
35class TouchScreen; 36class TouchScreen;
@@ -92,9 +93,15 @@ public:
92 /// Retrieves the underlying tas input device. 93 /// Retrieves the underlying tas input device.
93 [[nodiscard]] TasInput::Tas* GetTas(); 94 [[nodiscard]] TasInput::Tas* GetTas();
94 95
95 /// Retrieves the underlying tas input device. 96 /// Retrieves the underlying tas input device.
96 [[nodiscard]] const TasInput::Tas* GetTas() const; 97 [[nodiscard]] const TasInput::Tas* GetTas() const;
97 98
99 /// Retrieves the underlying camera input device.
100 [[nodiscard]] Camera* GetCamera();
101
102 /// Retrieves the underlying camera input device.
103 [[nodiscard]] const Camera* GetCamera() const;
104
98 /** 105 /**
99 * Returns all available input devices that this Factory can create a new device with. 106 * Returns all available input devices that this Factory can create a new device with.
100 * Each returned ParamPackage should have a `display` field used for display, a `engine` field 107 * Each returned ParamPackage should have a `display` field used for display, a `engine` field
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 242867a4f..a11a3b908 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -43,6 +43,9 @@ add_executable(yuzu
43 configuration/configure_audio.cpp 43 configuration/configure_audio.cpp
44 configuration/configure_audio.h 44 configuration/configure_audio.h
45 configuration/configure_audio.ui 45 configuration/configure_audio.ui
46 configuration/configure_camera.cpp
47 configuration/configure_camera.h
48 configuration/configure_camera.ui
46 configuration/configure_cpu.cpp 49 configuration/configure_cpu.cpp
47 configuration/configure_cpu.h 50 configuration/configure_cpu.h
48 configuration/configure_cpu.ui 51 configuration/configure_cpu.ui
@@ -254,7 +257,7 @@ endif()
254create_target_directory_groups(yuzu) 257create_target_directory_groups(yuzu)
255 258
256target_link_libraries(yuzu PRIVATE common core input_common video_core) 259target_link_libraries(yuzu PRIVATE common core input_common video_core)
257target_link_libraries(yuzu PRIVATE Boost::boost glad Qt::Widgets) 260target_link_libraries(yuzu PRIVATE Boost::boost glad Qt::Widgets Qt::Multimedia)
258target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) 261target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
259 262
260target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include) 263target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include)
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 01acda22b..0ee3820a2 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -5,6 +5,8 @@
5#include <glad/glad.h> 5#include <glad/glad.h>
6 6
7#include <QApplication> 7#include <QApplication>
8#include <QCameraImageCapture>
9#include <QCameraInfo>
8#include <QHBoxLayout> 10#include <QHBoxLayout>
9#include <QMessageBox> 11#include <QMessageBox>
10#include <QPainter> 12#include <QPainter>
@@ -31,6 +33,7 @@
31#include "core/core.h" 33#include "core/core.h"
32#include "core/cpu_manager.h" 34#include "core/cpu_manager.h"
33#include "core/frontend/framebuffer_layout.h" 35#include "core/frontend/framebuffer_layout.h"
36#include "input_common/drivers/camera.h"
34#include "input_common/drivers/keyboard.h" 37#include "input_common/drivers/keyboard.h"
35#include "input_common/drivers/mouse.h" 38#include "input_common/drivers/mouse.h"
36#include "input_common/drivers/tas_input.h" 39#include "input_common/drivers/tas_input.h"
@@ -801,6 +804,85 @@ void GRenderWindow::TouchEndEvent() {
801 input_subsystem->GetTouchScreen()->ReleaseAllTouch(); 804 input_subsystem->GetTouchScreen()->ReleaseAllTouch();
802} 805}
803 806
807void GRenderWindow::InitializeCamera() {
808 if (!Settings::values.enable_ir_sensor) {
809 return;
810 }
811
812 bool camera_found = false;
813 const QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
814 for (const QCameraInfo& cameraInfo : cameras) {
815 if (Settings::values.ir_sensor_device.GetValue() == cameraInfo.deviceName().toStdString() ||
816 Settings::values.ir_sensor_device.GetValue() == "Auto") {
817 camera = std::make_unique<QCamera>(cameraInfo);
818 camera_found = true;
819 break;
820 }
821 }
822
823 if (!camera_found) {
824 return;
825 }
826
827 camera_capture = std::make_unique<QCameraImageCapture>(camera.get());
828 connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this,
829 &GRenderWindow::OnCameraCapture);
830 camera->unload();
831 camera->setCaptureMode(QCamera::CaptureViewfinder);
832 camera->load();
833 camera->start();
834
835 pending_camera_snapshots = 0;
836 is_virtual_camera = false;
837
838 camera_timer = std::make_unique<QTimer>();
839 connect(camera_timer.get(), &QTimer::timeout, [this] { RequestCameraCapture(); });
840 // This timer should be dependent of camera resolution 5ms for every 100 pixels
841 camera_timer->start(100);
842}
843
844void GRenderWindow::FinalizeCamera() {
845 if (camera_timer) {
846 camera_timer->stop();
847 }
848 if (camera) {
849 camera->unload();
850 }
851}
852
853void GRenderWindow::RequestCameraCapture() {
854 if (!Settings::values.enable_ir_sensor) {
855 return;
856 }
857
858 // If the camera doesn't capture, test for virtual cameras
859 if (pending_camera_snapshots > 5) {
860 is_virtual_camera = true;
861 }
862 // Virtual cameras like obs need to reset the camera every capture
863 if (is_virtual_camera) {
864 camera->stop();
865 camera->start();
866 }
867
868 pending_camera_snapshots++;
869 camera_capture->capture();
870}
871
872void GRenderWindow::OnCameraCapture(int requestId, const QImage& img) {
873 constexpr std::size_t camera_width = 320;
874 constexpr std::size_t camera_height = 240;
875 const auto converted =
876 img.scaled(camera_width, camera_height, Qt::AspectRatioMode::IgnoreAspectRatio,
877 Qt::TransformationMode::SmoothTransformation)
878 .mirrored(false, true);
879 std::vector<u32> camera_data{};
880 camera_data.resize(camera_width * camera_height);
881 std::memcpy(camera_data.data(), converted.bits(), camera_width * camera_height * sizeof(u32));
882 input_subsystem->GetCamera()->SetCameraData(camera_width, camera_height, camera_data);
883 pending_camera_snapshots = 0;
884}
885
804bool GRenderWindow::event(QEvent* event) { 886bool GRenderWindow::event(QEvent* event) {
805 if (event->type() == QEvent::TouchBegin) { 887 if (event->type() == QEvent::TouchBegin) {
806 TouchBeginEvent(static_cast<QTouchEvent*>(event)); 888 TouchBeginEvent(static_cast<QTouchEvent*>(event));
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 81fe52c0e..b4781e697 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -20,6 +20,8 @@
20 20
21class GRenderWindow; 21class GRenderWindow;
22class GMainWindow; 22class GMainWindow;
23class QCamera;
24class QCameraImageCapture;
23class QKeyEvent; 25class QKeyEvent;
24 26
25namespace Core { 27namespace Core {
@@ -164,6 +166,9 @@ public:
164 void mouseReleaseEvent(QMouseEvent* event) override; 166 void mouseReleaseEvent(QMouseEvent* event) override;
165 void wheelEvent(QWheelEvent* event) override; 167 void wheelEvent(QWheelEvent* event) override;
166 168
169 void InitializeCamera();
170 void FinalizeCamera();
171
167 bool event(QEvent* event) override; 172 bool event(QEvent* event) override;
168 173
169 void focusOutEvent(QFocusEvent* event) override; 174 void focusOutEvent(QFocusEvent* event) override;
@@ -207,6 +212,9 @@ private:
207 void TouchUpdateEvent(const QTouchEvent* event); 212 void TouchUpdateEvent(const QTouchEvent* event);
208 void TouchEndEvent(); 213 void TouchEndEvent();
209 214
215 void RequestCameraCapture();
216 void OnCameraCapture(int requestId, const QImage& img);
217
210 void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override; 218 void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
211 219
212 bool InitializeOpenGL(); 220 bool InitializeOpenGL();
@@ -232,6 +240,12 @@ private:
232 bool first_frame = false; 240 bool first_frame = false;
233 InputCommon::TasInput::TasState last_tas_state; 241 InputCommon::TasInput::TasState last_tas_state;
234 242
243 bool is_virtual_camera;
244 int pending_camera_snapshots;
245 std::unique_ptr<QCamera> camera;
246 std::unique_ptr<QCameraImageCapture> camera_capture;
247 std::unique_ptr<QTimer> camera_timer;
248
235 Core::System& system; 249 Core::System& system;
236 250
237protected: 251protected:
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 2840bc5eb..1f76e86b9 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -368,6 +368,11 @@ void Config::ReadHidbusValues() {
368 } 368 }
369} 369}
370 370
371void Config::ReadIrCameraValues() {
372 ReadBasicSetting(Settings::values.enable_ir_sensor);
373 ReadBasicSetting(Settings::values.ir_sensor_device);
374}
375
371void Config::ReadAudioValues() { 376void Config::ReadAudioValues() {
372 qt_config->beginGroup(QStringLiteral("Audio")); 377 qt_config->beginGroup(QStringLiteral("Audio"));
373 378
@@ -393,6 +398,7 @@ void Config::ReadControlValues() {
393 ReadTouchscreenValues(); 398 ReadTouchscreenValues();
394 ReadMotionTouchValues(); 399 ReadMotionTouchValues();
395 ReadHidbusValues(); 400 ReadHidbusValues();
401 ReadIrCameraValues();
396 402
397#ifdef _WIN32 403#ifdef _WIN32
398 ReadBasicSetting(Settings::values.enable_raw_input); 404 ReadBasicSetting(Settings::values.enable_raw_input);
@@ -1005,6 +1011,11 @@ void Config::SaveHidbusValues() {
1005 QString::fromStdString(default_param)); 1011 QString::fromStdString(default_param));
1006} 1012}
1007 1013
1014void Config::SaveIrCameraValues() {
1015 WriteBasicSetting(Settings::values.enable_ir_sensor);
1016 WriteBasicSetting(Settings::values.ir_sensor_device);
1017}
1018
1008void Config::SaveValues() { 1019void Config::SaveValues() {
1009 if (global) { 1020 if (global) {
1010 SaveControlValues(); 1021 SaveControlValues();
@@ -1047,6 +1058,7 @@ void Config::SaveControlValues() {
1047 SaveTouchscreenValues(); 1058 SaveTouchscreenValues();
1048 SaveMotionTouchValues(); 1059 SaveMotionTouchValues();
1049 SaveHidbusValues(); 1060 SaveHidbusValues();
1061 SaveIrCameraValues();
1050 1062
1051 WriteGlobalSetting(Settings::values.use_docked_mode); 1063 WriteGlobalSetting(Settings::values.use_docked_mode);
1052 WriteGlobalSetting(Settings::values.vibration_enabled); 1064 WriteGlobalSetting(Settings::values.vibration_enabled);
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index d511b3dbd..a71eabe8e 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -68,6 +68,7 @@ private:
68 void ReadTouchscreenValues(); 68 void ReadTouchscreenValues();
69 void ReadMotionTouchValues(); 69 void ReadMotionTouchValues();
70 void ReadHidbusValues(); 70 void ReadHidbusValues();
71 void ReadIrCameraValues();
71 72
72 // Read functions bases off the respective config section names. 73 // Read functions bases off the respective config section names.
73 void ReadAudioValues(); 74 void ReadAudioValues();
@@ -96,6 +97,7 @@ private:
96 void SaveTouchscreenValues(); 97 void SaveTouchscreenValues();
97 void SaveMotionTouchValues(); 98 void SaveMotionTouchValues();
98 void SaveHidbusValues(); 99 void SaveHidbusValues();
100 void SaveIrCameraValues();
99 101
100 // Save functions based off the respective config section names. 102 // Save functions based off the respective config section names.
101 void SaveAudioValues(); 103 void SaveAudioValues();
diff --git a/src/yuzu/configuration/configure_camera.cpp b/src/yuzu/configuration/configure_camera.cpp
new file mode 100644
index 000000000..73cdcf3f2
--- /dev/null
+++ b/src/yuzu/configuration/configure_camera.cpp
@@ -0,0 +1,138 @@
1// Text : Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include <memory>
5#include <QCameraImageCapture>
6#include <QCameraInfo>
7#include <QStandardItemModel>
8#include <QTimer>
9
10#include "input_common/drivers/camera.h"
11#include "input_common/main.h"
12#include "ui_configure_camera.h"
13#include "yuzu/configuration/config.h"
14#include "yuzu/configuration/configure_camera.h"
15
16ConfigureCamera::ConfigureCamera(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_)
17 : QDialog(parent), input_subsystem{input_subsystem_},
18 ui(std::make_unique<Ui::ConfigureCamera>()) {
19 ui->setupUi(this);
20
21 connect(ui->restore_defaults_button, &QPushButton::clicked, this,
22 &ConfigureCamera::RestoreDefaults);
23 connect(ui->preview_button, &QPushButton::clicked, this, &ConfigureCamera::PreviewCamera);
24
25 auto blank_image = QImage(320, 240, QImage::Format::Format_RGB32);
26 blank_image.fill(Qt::black);
27 DisplayCapturedFrame(0, blank_image);
28
29 LoadConfiguration();
30 resize(0, 0);
31}
32
33ConfigureCamera::~ConfigureCamera() = default;
34
35void ConfigureCamera::PreviewCamera() {
36 const auto index = ui->ir_sensor_combo_box->currentIndex();
37 bool camera_found = false;
38 const QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
39 for (const QCameraInfo& cameraInfo : cameras) {
40 if (input_devices[index] == cameraInfo.deviceName().toStdString() ||
41 input_devices[index] == "Auto") {
42 LOG_INFO(Frontend, "Selected Camera {} {}", cameraInfo.description().toStdString(),
43 cameraInfo.deviceName().toStdString());
44 camera = std::make_unique<QCamera>(cameraInfo);
45 camera_found = true;
46 break;
47 }
48 }
49
50 // Clear previous frame
51 auto blank_image = QImage(320, 240, QImage::Format::Format_RGB32);
52 blank_image.fill(Qt::black);
53 DisplayCapturedFrame(0, blank_image);
54
55 if (!camera_found) {
56 return;
57 }
58
59 camera_capture = std::make_unique<QCameraImageCapture>(camera.get());
60 connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this,
61 &ConfigureCamera::DisplayCapturedFrame);
62 camera->unload();
63 camera->setCaptureMode(QCamera::CaptureViewfinder);
64 camera->load();
65 camera->start();
66
67 pending_snapshots = 0;
68 is_virtual_camera = false;
69
70 camera_timer = std::make_unique<QTimer>();
71 connect(camera_timer.get(), &QTimer::timeout, [this] {
72 // If the camera doesn't capture, test for virtual cameras
73 if (pending_snapshots > 5) {
74 is_virtual_camera = true;
75 }
76 // Virtual cameras like obs need to reset the camera every capture
77 if (is_virtual_camera) {
78 camera->stop();
79 camera->start();
80 }
81 pending_snapshots++;
82 camera_capture->capture();
83 });
84
85 camera_timer->start(250);
86}
87
88void ConfigureCamera::DisplayCapturedFrame(int requestId, const QImage& img) {
89 LOG_INFO(Frontend, "ImageCaptured {} {}", img.width(), img.height());
90 const auto converted = img.scaled(320, 240, Qt::AspectRatioMode::IgnoreAspectRatio,
91 Qt::TransformationMode::SmoothTransformation);
92 ui->preview_box->setPixmap(QPixmap::fromImage(converted));
93 pending_snapshots = 0;
94}
95
96void ConfigureCamera::changeEvent(QEvent* event) {
97 if (event->type() == QEvent::LanguageChange) {
98 RetranslateUI();
99 }
100
101 QDialog::changeEvent(event);
102}
103
104void ConfigureCamera::RetranslateUI() {
105 ui->retranslateUi(this);
106}
107
108void ConfigureCamera::ApplyConfiguration() {
109 const auto index = ui->ir_sensor_combo_box->currentIndex();
110 Settings::values.ir_sensor_device.SetValue(input_devices[index]);
111}
112
113void ConfigureCamera::LoadConfiguration() {
114 input_devices.clear();
115 ui->ir_sensor_combo_box->clear();
116 input_devices.push_back("Auto");
117 ui->ir_sensor_combo_box->addItem(tr("Auto"));
118 const auto cameras = QCameraInfo::availableCameras();
119 for (const QCameraInfo& cameraInfo : cameras) {
120 input_devices.push_back(cameraInfo.deviceName().toStdString());
121 ui->ir_sensor_combo_box->addItem(cameraInfo.description());
122 }
123
124 const auto current_device = Settings::values.ir_sensor_device.GetValue();
125
126 const auto devices_it = std::find_if(
127 input_devices.begin(), input_devices.end(),
128 [current_device](const std::string& device) { return device == current_device; });
129 const int device_index =
130 devices_it != input_devices.end()
131 ? static_cast<int>(std::distance(input_devices.begin(), devices_it))
132 : 0;
133 ui->ir_sensor_combo_box->setCurrentIndex(device_index);
134}
135
136void ConfigureCamera::RestoreDefaults() {
137 ui->ir_sensor_combo_box->setCurrentIndex(0);
138}
diff --git a/src/yuzu/configuration/configure_camera.h b/src/yuzu/configuration/configure_camera.h
new file mode 100644
index 000000000..db9833b5c
--- /dev/null
+++ b/src/yuzu/configuration/configure_camera.h
@@ -0,0 +1,54 @@
1// Text : Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <QDialog>
8
9class QTimer;
10class QCamera;
11class QCameraImageCapture;
12
13namespace InputCommon {
14class InputSubsystem;
15} // namespace InputCommon
16
17namespace Ui {
18class ConfigureCamera;
19}
20
21class ConfigureCamera : public QDialog {
22 Q_OBJECT
23
24public:
25 explicit ConfigureCamera(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_);
26 ~ConfigureCamera() override;
27
28 void ApplyConfiguration();
29
30private:
31 void changeEvent(QEvent* event) override;
32 void RetranslateUI();
33
34 /// Load configuration settings.
35 void LoadConfiguration();
36
37 /// Restore all buttons to their default values.
38 void RestoreDefaults();
39
40 void DisplayCapturedFrame(int requestId, const QImage& img);
41
42 /// Loads and signals the current selected camera to display a frame
43 void PreviewCamera();
44
45 InputCommon::InputSubsystem* input_subsystem;
46
47 bool is_virtual_camera;
48 int pending_snapshots;
49 std::unique_ptr<QCamera> camera;
50 std::unique_ptr<QCameraImageCapture> camera_capture;
51 std::unique_ptr<QTimer> camera_timer;
52 std::vector<std::string> input_devices;
53 std::unique_ptr<Ui::ConfigureCamera> ui;
54};
diff --git a/src/yuzu/configuration/configure_camera.ui b/src/yuzu/configuration/configure_camera.ui
new file mode 100644
index 000000000..976a9b1ec
--- /dev/null
+++ b/src/yuzu/configuration/configure_camera.ui
@@ -0,0 +1,170 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureCamera</class>
4 <widget class="QDialog" name="ConfigureCamera">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>298</width>
10 <height>339</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Configure Infrared Camera</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <item>
18 <widget class="QLabel" name="label_2">
19 <property name="minimumSize">
20 <size>
21 <width>280</width>
22 <height>0</height>
23 </size>
24 </property>
25 <property name="text">
26 <string>Select where the image of the emulated camera comes from. It may be a virtual camera or a real camera.</string>
27 </property>
28 <property name="wordWrap">
29 <bool>true</bool>
30 </property>
31 </widget>
32 </item>
33 <item>
34 <spacer name="verticalSpacer_2">
35 <property name="orientation">
36 <enum>Qt::Vertical</enum>
37 </property>
38 <property name="sizeType">
39 <enum>QSizePolicy::Fixed</enum>
40 </property>
41 <property name="sizeHint" stdset="0">
42 <size>
43 <width>20</width>
44 <height>20</height>
45 </size>
46 </property>
47 </spacer>
48 </item>
49 <item>
50 <widget class="QGroupBox" name="gridGroupBox">
51 <property name="title">
52 <string>Camera Image Source:</string>
53 </property>
54 <layout class="QGridLayout" name="gridLayout">
55 <item row="0" column="0">
56 <spacer name="horizontalSpacer">
57 <property name="orientation">
58 <enum>Qt::Horizontal</enum>
59 </property>
60 <property name="sizeHint" stdset="0">
61 <size>
62 <width>40</width>
63 <height>20</height>
64 </size>
65 </property>
66 </spacer>
67 </item>
68 <item row="0" column="1">
69 <widget class="QLabel" name="label_3">
70 <property name="text">
71 <string>Input device:</string>
72 </property>
73 </widget>
74 </item>
75 <item row="0" column="2">
76 <widget class="QComboBox" name="ir_sensor_combo_box"/>
77 </item>
78 <item row="0" column="3">
79 <spacer name="horizontalSpacer_2">
80 <property name="orientation">
81 <enum>Qt::Horizontal</enum>
82 </property>
83 <property name="sizeHint" stdset="0">
84 <size>
85 <width>40</width>
86 <height>20</height>
87 </size>
88 </property>
89 </spacer>
90 </item>
91 </layout>
92 </widget>
93 </item><item>
94 <widget class="QGroupBox" name="previewBox">
95 <property name="title">
96 <string>Preview</string>
97 </property>
98 <layout class="QVBoxLayout" name="verticalLayout_3">
99 <item>
100 <widget class="QLabel" name="preview_box">
101 <property name="minimumSize">
102 <size>
103 <width>320</width>
104 <height>240</height>
105 </size>
106 </property>
107 <property name="toolTip">
108 <string>Resolution: 320*240</string>
109 </property>
110 </widget>
111 </item>
112 <item>
113 <widget class="QPushButton" name="preview_button">
114 <property name="text">
115 <string>Click to preview</string>
116 </property>
117 </widget>
118 </item>
119 </layout>
120 </widget>
121 </item>
122 <item>
123 <spacer name="verticalSpacer">
124 <property name="orientation">
125 <enum>Qt::Vertical</enum>
126 </property>
127 <property name="sizeHint" stdset="0">
128 <size>
129 <width>20</width>
130 <height>40</height>
131 </size>
132 </property>
133 </spacer>
134 </item>
135 <item>
136 <layout class="QHBoxLayout" name="horizontalLayout">
137 <item>
138 <widget class="QPushButton" name="restore_defaults_button">
139 <property name="text">
140 <string>Restore Defaults</string>
141 </property>
142 </widget>
143 </item>
144 <item>
145 <widget class="QDialogButtonBox" name="buttonBox">
146 <property name="standardButtons">
147 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
148 </property>
149 </widget>
150 </item>
151 </layout>
152 </item>
153 </layout>
154 </widget>
155 <resources/>
156 <connections>
157 <connection>
158 <sender>buttonBox</sender>
159 <signal>accepted()</signal>
160 <receiver>ConfigureCamera</receiver>
161 <slot>accept()</slot>
162 </connection>
163 <connection>
164 <sender>buttonBox</sender>
165 <signal>rejected()</signal>
166 <receiver>ConfigureCamera</receiver>
167 <slot>reject()</slot>
168 </connection>
169 </connections>
170</ui>
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 73d7ba24b..f1b061b13 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -15,6 +15,7 @@
15#include "ui_configure_input.h" 15#include "ui_configure_input.h"
16#include "ui_configure_input_advanced.h" 16#include "ui_configure_input_advanced.h"
17#include "ui_configure_input_player.h" 17#include "ui_configure_input_player.h"
18#include "yuzu/configuration/configure_camera.h"
18#include "yuzu/configuration/configure_debug_controller.h" 19#include "yuzu/configuration/configure_debug_controller.h"
19#include "yuzu/configuration/configure_input.h" 20#include "yuzu/configuration/configure_input.h"
20#include "yuzu/configuration/configure_input_advanced.h" 21#include "yuzu/configuration/configure_input_advanced.h"
@@ -163,6 +164,10 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
163 [this, input_subsystem, &hid_core] { 164 [this, input_subsystem, &hid_core] {
164 CallConfigureDialog<ConfigureRingController>(*this, input_subsystem, hid_core); 165 CallConfigureDialog<ConfigureRingController>(*this, input_subsystem, hid_core);
165 }); 166 });
167 connect(advanced, &ConfigureInputAdvanced::CallCameraDialog,
168 [this, input_subsystem, &hid_core] {
169 CallConfigureDialog<ConfigureCamera>(*this, input_subsystem);
170 });
166 171
167 connect(ui->vibrationButton, &QPushButton::clicked, 172 connect(ui->vibrationButton, &QPushButton::clicked,
168 [this, &hid_core] { CallConfigureDialog<ConfigureVibration>(*this, hid_core); }); 173 [this, &hid_core] { CallConfigureDialog<ConfigureVibration>(*this, hid_core); });
diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp
index f14bdc831..10f841b98 100644
--- a/src/yuzu/configuration/configure_input_advanced.cpp
+++ b/src/yuzu/configuration/configure_input_advanced.cpp
@@ -89,6 +89,7 @@ ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent)
89 [this] { CallMotionTouchConfigDialog(); }); 89 [this] { CallMotionTouchConfigDialog(); });
90 connect(ui->ring_controller_configure, &QPushButton::clicked, this, 90 connect(ui->ring_controller_configure, &QPushButton::clicked, this,
91 [this] { CallRingControllerDialog(); }); 91 [this] { CallRingControllerDialog(); });
92 connect(ui->camera_configure, &QPushButton::clicked, this, [this] { CallCameraDialog(); });
92 93
93#ifndef _WIN32 94#ifndef _WIN32
94 ui->enable_raw_input->setVisible(false); 95 ui->enable_raw_input->setVisible(false);
@@ -136,6 +137,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
136 Settings::values.enable_udp_controller = ui->enable_udp_controller->isChecked(); 137 Settings::values.enable_udp_controller = ui->enable_udp_controller->isChecked();
137 Settings::values.controller_navigation = ui->controller_navigation->isChecked(); 138 Settings::values.controller_navigation = ui->controller_navigation->isChecked();
138 Settings::values.enable_ring_controller = ui->enable_ring_controller->isChecked(); 139 Settings::values.enable_ring_controller = ui->enable_ring_controller->isChecked();
140 Settings::values.enable_ir_sensor = ui->enable_ir_sensor->isChecked();
139} 141}
140 142
141void ConfigureInputAdvanced::LoadConfiguration() { 143void ConfigureInputAdvanced::LoadConfiguration() {
@@ -169,6 +171,7 @@ void ConfigureInputAdvanced::LoadConfiguration() {
169 ui->enable_udp_controller->setChecked(Settings::values.enable_udp_controller.GetValue()); 171 ui->enable_udp_controller->setChecked(Settings::values.enable_udp_controller.GetValue());
170 ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue()); 172 ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue());
171 ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue()); 173 ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue());
174 ui->enable_ir_sensor->setChecked(Settings::values.enable_ir_sensor.GetValue());
172 175
173 UpdateUIEnabled(); 176 UpdateUIEnabled();
174} 177}
diff --git a/src/yuzu/configuration/configure_input_advanced.h b/src/yuzu/configuration/configure_input_advanced.h
index 644e56dd8..fc1230284 100644
--- a/src/yuzu/configuration/configure_input_advanced.h
+++ b/src/yuzu/configuration/configure_input_advanced.h
@@ -29,6 +29,7 @@ signals:
29 void CallTouchscreenConfigDialog(); 29 void CallTouchscreenConfigDialog();
30 void CallMotionTouchConfigDialog(); 30 void CallMotionTouchConfigDialog();
31 void CallRingControllerDialog(); 31 void CallRingControllerDialog();
32 void CallCameraDialog();
32 33
33private: 34private:
34 void changeEvent(QEvent* event) override; 35 void changeEvent(QEvent* event) override;
diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui
index 14403cb10..fac8cf827 100644
--- a/src/yuzu/configuration/configure_input_advanced.ui
+++ b/src/yuzu/configuration/configure_input_advanced.ui
@@ -2617,6 +2617,20 @@
2617 </property> 2617 </property>
2618 </widget> 2618 </widget>
2619 </item> 2619 </item>
2620 <item row="5" column="0">
2621 <widget class="QCheckBox" name="enable_ir_sensor">
2622 <property name="text">
2623 <string>Infrared Camera</string>
2624 </property>
2625 </widget>
2626 </item>
2627 <item row="5" column="2">
2628 <widget class="QPushButton" name="camera_configure">
2629 <property name="text">
2630 <string>Configure</string>
2631 </property>
2632 </widget>
2633 </item>
2620 </layout> 2634 </layout>
2621 </widget> 2635 </widget>
2622 </item> 2636 </item>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index a120f2662..08ccc1555 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1542,6 +1542,8 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
1542 mouse_hide_timer.start(); 1542 mouse_hide_timer.start();
1543 } 1543 }
1544 1544
1545 render_window->InitializeCamera();
1546
1545 std::string title_name; 1547 std::string title_name;
1546 std::string title_version; 1548 std::string title_version;
1547 const auto res = system->GetGameName(title_name); 1549 const auto res = system->GetGameName(title_name);
@@ -1623,6 +1625,7 @@ void GMainWindow::ShutdownGame() {
1623 tas_label->clear(); 1625 tas_label->clear();
1624 input_subsystem->GetTas()->Stop(); 1626 input_subsystem->GetTas()->Stop();
1625 OnTasStateChanged(); 1627 OnTasStateChanged();
1628 render_window->FinalizeCamera();
1626 1629
1627 // Enable all controllers 1630 // Enable all controllers
1628 system->HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All}); 1631 system->HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
@@ -2862,6 +2865,12 @@ void GMainWindow::OnConfigure() {
2862 mouse_hide_timer.start(); 2865 mouse_hide_timer.start();
2863 } 2866 }
2864 2867
2868 // Restart camera config
2869 if (emulation_running) {
2870 render_window->FinalizeCamera();
2871 render_window->InitializeCamera();
2872 }
2873
2865 if (!UISettings::values.has_broken_vulkan) { 2874 if (!UISettings::values.has_broken_vulkan) {
2866 renderer_status_button->setEnabled(!emulation_running); 2875 renderer_status_button->setEnabled(!emulation_running);
2867 } 2876 }