early-access version 2859
This commit is contained in:
parent
e67346417b
commit
1f02fcfc6e
@ -196,7 +196,7 @@ if(ENABLE_QT)
|
|||||||
# Check for system Qt on Linux, fallback to bundled Qt
|
# Check for system Qt on Linux, fallback to bundled Qt
|
||||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
if (NOT YUZU_USE_BUNDLED_QT)
|
if (NOT YUZU_USE_BUNDLED_QT)
|
||||||
find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets DBus)
|
find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets DBus Multimedia)
|
||||||
endif()
|
endif()
|
||||||
if (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT)
|
if (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT)
|
||||||
# Check for dependencies, then enable bundled Qt download
|
# Check for dependencies, then enable bundled Qt download
|
||||||
@ -300,9 +300,9 @@ if(ENABLE_QT)
|
|||||||
set(YUZU_QT_NO_CMAKE_SYSTEM_PATH "NO_CMAKE_SYSTEM_PATH")
|
set(YUZU_QT_NO_CMAKE_SYSTEM_PATH "NO_CMAKE_SYSTEM_PATH")
|
||||||
endif()
|
endif()
|
||||||
if ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux") AND YUZU_USE_BUNDLED_QT)
|
if ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux") AND YUZU_USE_BUNDLED_QT)
|
||||||
find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent DBus ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
|
find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent Multimedia DBus ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
|
||||||
else()
|
else()
|
||||||
find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
|
find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Concurrent Multimedia ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
|
||||||
endif()
|
endif()
|
||||||
if (YUZU_USE_QT_WEB_ENGINE)
|
if (YUZU_USE_QT_WEB_ENGINE)
|
||||||
find_package(Qt5 REQUIRED COMPONENTS WebEngineCore WebEngineWidgets)
|
find_package(Qt5 REQUIRED COMPONENTS WebEngineCore WebEngineWidgets)
|
||||||
|
@ -10,11 +10,13 @@ function(copy_yuzu_Qt5_deps target_dir)
|
|||||||
set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/")
|
set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/")
|
||||||
set(Qt5_PLATFORMTHEMES_DIR "${Qt5_DIR}/../../../plugins/platformthemes/")
|
set(Qt5_PLATFORMTHEMES_DIR "${Qt5_DIR}/../../../plugins/platformthemes/")
|
||||||
set(Qt5_PLATFORMINPUTCONTEXTS_DIR "${Qt5_DIR}/../../../plugins/platforminputcontexts/")
|
set(Qt5_PLATFORMINPUTCONTEXTS_DIR "${Qt5_DIR}/../../../plugins/platforminputcontexts/")
|
||||||
|
set(Qt5_MEDIASERVICE_DIR "${Qt5_DIR}/../../../plugins/mediaservice/")
|
||||||
set(Qt5_XCBGLINTEGRATIONS_DIR "${Qt5_DIR}/../../../plugins/xcbglintegrations/")
|
set(Qt5_XCBGLINTEGRATIONS_DIR "${Qt5_DIR}/../../../plugins/xcbglintegrations/")
|
||||||
set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/")
|
set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/")
|
||||||
set(Qt5_IMAGEFORMATS_DIR "${Qt5_DIR}/../../../plugins/imageformats/")
|
set(Qt5_IMAGEFORMATS_DIR "${Qt5_DIR}/../../../plugins/imageformats/")
|
||||||
set(Qt5_RESOURCES_DIR "${Qt5_DIR}/../../../resources/")
|
set(Qt5_RESOURCES_DIR "${Qt5_DIR}/../../../resources/")
|
||||||
set(PLATFORMS ${DLL_DEST}plugins/platforms/)
|
set(PLATFORMS ${DLL_DEST}plugins/platforms/)
|
||||||
|
set(MEDIASERVICE ${DLL_DEST}mediaservice/)
|
||||||
set(STYLES ${DLL_DEST}plugins/styles/)
|
set(STYLES ${DLL_DEST}plugins/styles/)
|
||||||
set(IMAGEFORMATS ${DLL_DEST}plugins/imageformats/)
|
set(IMAGEFORMATS ${DLL_DEST}plugins/imageformats/)
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
@ -22,6 +24,8 @@ function(copy_yuzu_Qt5_deps target_dir)
|
|||||||
Qt5Core$<$<CONFIG:Debug>:d>.*
|
Qt5Core$<$<CONFIG:Debug>:d>.*
|
||||||
Qt5Gui$<$<CONFIG:Debug>:d>.*
|
Qt5Gui$<$<CONFIG:Debug>:d>.*
|
||||||
Qt5Widgets$<$<CONFIG:Debug>:d>.*
|
Qt5Widgets$<$<CONFIG:Debug>:d>.*
|
||||||
|
Qt5Multimedia$<$<CONFIG:Debug>:d>.*
|
||||||
|
Qt5Network$<$<CONFIG:Debug>:d>.*
|
||||||
)
|
)
|
||||||
|
|
||||||
if (YUZU_USE_QT_WEB_ENGINE)
|
if (YUZU_USE_QT_WEB_ENGINE)
|
||||||
@ -53,6 +57,10 @@ function(copy_yuzu_Qt5_deps target_dir)
|
|||||||
qjpeg$<$<CONFIG:Debug>:d>.*
|
qjpeg$<$<CONFIG:Debug>:d>.*
|
||||||
qgif$<$<CONFIG:Debug>:d>.*
|
qgif$<$<CONFIG:Debug>:d>.*
|
||||||
)
|
)
|
||||||
|
windows_copy_files(yuzu ${Qt5_MEDIASERVICE_DIR} ${MEDIASERVICE}
|
||||||
|
dsengine$<$<CONFIG:Debug>:d>.*
|
||||||
|
wmfengine$<$<CONFIG:Debug>:d>.*
|
||||||
|
)
|
||||||
else()
|
else()
|
||||||
set(Qt5_DLLS
|
set(Qt5_DLLS
|
||||||
"${Qt5_DLL_DIR}libQt5Core.so.5"
|
"${Qt5_DLL_DIR}libQt5Core.so.5"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
yuzu emulator early access
|
yuzu emulator early access
|
||||||
=============
|
=============
|
||||||
|
|
||||||
This is the source code for early-access 2858.
|
This is the source code for early-access 2859.
|
||||||
|
|
||||||
## Legal Notice
|
## Legal Notice
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ enum class InputType {
|
|||||||
Color,
|
Color,
|
||||||
Vibration,
|
Vibration,
|
||||||
Nfc,
|
Nfc,
|
||||||
Ir,
|
IrSensor,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Internal battery charge level
|
// Internal battery charge level
|
||||||
@ -53,6 +53,15 @@ enum class PollingMode {
|
|||||||
IR,
|
IR,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class CameraFormat {
|
||||||
|
Size320x240,
|
||||||
|
Size160x120,
|
||||||
|
Size80x60,
|
||||||
|
Size40x30,
|
||||||
|
Size20x15,
|
||||||
|
None,
|
||||||
|
};
|
||||||
|
|
||||||
// Vibration reply from the controller
|
// Vibration reply from the controller
|
||||||
enum class VibrationError {
|
enum class VibrationError {
|
||||||
None,
|
None,
|
||||||
@ -68,6 +77,13 @@ enum class PollingError {
|
|||||||
Unknown,
|
Unknown,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Ir camera reply from the controller
|
||||||
|
enum class CameraError {
|
||||||
|
None,
|
||||||
|
NotSupported,
|
||||||
|
Unknown,
|
||||||
|
};
|
||||||
|
|
||||||
// Hint for amplification curve to be used
|
// Hint for amplification curve to be used
|
||||||
enum class VibrationAmplificationType {
|
enum class VibrationAmplificationType {
|
||||||
Linear,
|
Linear,
|
||||||
@ -176,6 +192,12 @@ struct LedStatus {
|
|||||||
bool led_4{};
|
bool led_4{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Raw data fom camera
|
||||||
|
struct CameraStatus {
|
||||||
|
CameraFormat format{CameraFormat::None};
|
||||||
|
std::vector<u8> data{};
|
||||||
|
};
|
||||||
|
|
||||||
// List of buttons to be passed to Qt that can be translated
|
// List of buttons to be passed to Qt that can be translated
|
||||||
enum class ButtonNames {
|
enum class ButtonNames {
|
||||||
Undefined,
|
Undefined,
|
||||||
@ -233,6 +255,7 @@ struct CallbackStatus {
|
|||||||
BodyColorStatus color_status{};
|
BodyColorStatus color_status{};
|
||||||
BatteryStatus battery_status{};
|
BatteryStatus battery_status{};
|
||||||
VibrationStatus vibration_status{};
|
VibrationStatus vibration_status{};
|
||||||
|
CameraStatus camera_status{};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Triggered once every input change
|
// Triggered once every input change
|
||||||
@ -281,6 +304,10 @@ public:
|
|||||||
virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) {
|
virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) {
|
||||||
return PollingError::NotSupported;
|
return PollingError::NotSupported;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual CameraError SetCameraFormat([[maybe_unused]] CameraFormat camera_format) {
|
||||||
|
return CameraError::NotSupported;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An abstract class template for a factory that can create input devices.
|
/// An abstract class template for a factory that can create input devices.
|
||||||
|
@ -503,6 +503,9 @@ struct Values {
|
|||||||
Setting<bool> enable_ring_controller{true, "enable_ring_controller"};
|
Setting<bool> enable_ring_controller{true, "enable_ring_controller"};
|
||||||
RingconRaw ringcon_analogs;
|
RingconRaw ringcon_analogs;
|
||||||
|
|
||||||
|
Setting<bool> enable_ir_sensor{false, "enable_ir_sensor"};
|
||||||
|
Setting<std::string> ir_sensor_device{"auto", "ir_sensor_device"};
|
||||||
|
|
||||||
// Data Storage
|
// Data Storage
|
||||||
Setting<bool> use_virtual_sd{true, "use_virtual_sd"};
|
Setting<bool> use_virtual_sd{true, "use_virtual_sd"};
|
||||||
Setting<bool> gamecard_inserted{false, "gamecard_inserted"};
|
Setting<bool> gamecard_inserted{false, "gamecard_inserted"};
|
||||||
|
@ -156,6 +156,7 @@ add_library(core STATIC
|
|||||||
hid/input_converter.h
|
hid/input_converter.h
|
||||||
hid/input_interpreter.cpp
|
hid/input_interpreter.cpp
|
||||||
hid/input_interpreter.h
|
hid/input_interpreter.h
|
||||||
|
hid/irs_types.h
|
||||||
hid/motion_input.cpp
|
hid/motion_input.cpp
|
||||||
hid/motion_input.h
|
hid/motion_input.h
|
||||||
hle/api_version.h
|
hle/api_version.h
|
||||||
@ -443,6 +444,7 @@ add_library(core STATIC
|
|||||||
hle/service/hid/hidbus.h
|
hle/service/hid/hidbus.h
|
||||||
hle/service/hid/irs.cpp
|
hle/service/hid/irs.cpp
|
||||||
hle/service/hid/irs.h
|
hle/service/hid/irs.h
|
||||||
|
hle/service/hid/irs_ring_lifo.h
|
||||||
hle/service/hid/ring_lifo.h
|
hle/service/hid/ring_lifo.h
|
||||||
hle/service/hid/xcd.cpp
|
hle/service/hid/xcd.cpp
|
||||||
hle/service/hid/xcd.h
|
hle/service/hid/xcd.h
|
||||||
@ -475,6 +477,20 @@ add_library(core STATIC
|
|||||||
hle/service/hid/hidbus/starlink.h
|
hle/service/hid/hidbus/starlink.h
|
||||||
hle/service/hid/hidbus/stubbed.cpp
|
hle/service/hid/hidbus/stubbed.cpp
|
||||||
hle/service/hid/hidbus/stubbed.h
|
hle/service/hid/hidbus/stubbed.h
|
||||||
|
hle/service/hid/irsensor/clustering_processor.cpp
|
||||||
|
hle/service/hid/irsensor/clustering_processor.h
|
||||||
|
hle/service/hid/irsensor/image_transfer_processor.cpp
|
||||||
|
hle/service/hid/irsensor/image_transfer_processor.h
|
||||||
|
hle/service/hid/irsensor/ir_led_processor.cpp
|
||||||
|
hle/service/hid/irsensor/ir_led_processor.h
|
||||||
|
hle/service/hid/irsensor/moment_processor.cpp
|
||||||
|
hle/service/hid/irsensor/moment_processor.h
|
||||||
|
hle/service/hid/irsensor/pointing_processor.cpp
|
||||||
|
hle/service/hid/irsensor/pointing_processor.h
|
||||||
|
hle/service/hid/irsensor/processor_base.cpp
|
||||||
|
hle/service/hid/irsensor/processor_base.h
|
||||||
|
hle/service/hid/irsensor/tera_plugin_processor.cpp
|
||||||
|
hle/service/hid/irsensor/tera_plugin_processor.h
|
||||||
hle/service/jit/jit_context.cpp
|
hle/service/jit/jit_context.cpp
|
||||||
hle/service/jit/jit_context.h
|
hle/service/jit/jit_context.h
|
||||||
hle/service/jit/jit.cpp
|
hle/service/jit/jit.cpp
|
||||||
|
@ -17,6 +17,8 @@ struct KeyboardInitializeParameters {
|
|||||||
std::u16string sub_text;
|
std::u16string sub_text;
|
||||||
std::u16string guide_text;
|
std::u16string guide_text;
|
||||||
std::u16string initial_text;
|
std::u16string initial_text;
|
||||||
|
char16_t left_optional_symbol_key;
|
||||||
|
char16_t right_optional_symbol_key;
|
||||||
u32 max_text_length;
|
u32 max_text_length;
|
||||||
u32 min_text_length;
|
u32 min_text_length;
|
||||||
s32 initial_cursor_position;
|
s32 initial_cursor_position;
|
||||||
|
@ -126,10 +126,14 @@ void EmulatedController::LoadDevices() {
|
|||||||
battery_params[LeftIndex].Set("battery", true);
|
battery_params[LeftIndex].Set("battery", true);
|
||||||
battery_params[RightIndex].Set("battery", true);
|
battery_params[RightIndex].Set("battery", true);
|
||||||
|
|
||||||
|
camera_params = Common::ParamPackage{"engine:camera,camera:1"};
|
||||||
|
|
||||||
output_params[LeftIndex] = left_joycon;
|
output_params[LeftIndex] = left_joycon;
|
||||||
output_params[RightIndex] = right_joycon;
|
output_params[RightIndex] = right_joycon;
|
||||||
|
output_params[2] = camera_params;
|
||||||
output_params[LeftIndex].Set("output", true);
|
output_params[LeftIndex].Set("output", true);
|
||||||
output_params[RightIndex].Set("output", true);
|
output_params[RightIndex].Set("output", true);
|
||||||
|
output_params[2].Set("output", true);
|
||||||
|
|
||||||
LoadTASParams();
|
LoadTASParams();
|
||||||
|
|
||||||
@ -146,6 +150,7 @@ void EmulatedController::LoadDevices() {
|
|||||||
Common::Input::CreateDevice<Common::Input::InputDevice>);
|
Common::Input::CreateDevice<Common::Input::InputDevice>);
|
||||||
std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(),
|
std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(),
|
||||||
Common::Input::CreateDevice<Common::Input::InputDevice>);
|
Common::Input::CreateDevice<Common::Input::InputDevice>);
|
||||||
|
camera_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(camera_params);
|
||||||
std::transform(output_params.begin(), output_params.end(), output_devices.begin(),
|
std::transform(output_params.begin(), output_params.end(), output_devices.begin(),
|
||||||
Common::Input::CreateDevice<Common::Input::OutputDevice>);
|
Common::Input::CreateDevice<Common::Input::OutputDevice>);
|
||||||
|
|
||||||
@ -267,6 +272,14 @@ void EmulatedController::ReloadInput() {
|
|||||||
motion_devices[index]->ForceUpdate();
|
motion_devices[index]->ForceUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (camera_devices) {
|
||||||
|
camera_devices->SetCallback({
|
||||||
|
.on_change =
|
||||||
|
[this](const Common::Input::CallbackStatus& callback) { SetCamera(callback); },
|
||||||
|
});
|
||||||
|
camera_devices->ForceUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
// Use a common UUID for TAS
|
// Use a common UUID for TAS
|
||||||
static constexpr Common::UUID TAS_UUID = Common::UUID{
|
static constexpr Common::UUID TAS_UUID = Common::UUID{
|
||||||
{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
|
{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
|
||||||
@ -851,6 +864,25 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
|
|||||||
TriggerOnChange(ControllerTriggerType::Battery, true);
|
TriggerOnChange(ControllerTriggerType::Battery, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) {
|
||||||
|
std::unique_lock lock{mutex};
|
||||||
|
controller.camera_values = TransformToCamera(callback);
|
||||||
|
|
||||||
|
if (is_configuring) {
|
||||||
|
lock.unlock();
|
||||||
|
TriggerOnChange(ControllerTriggerType::IrSensor, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
controller.camera_state.sample++;
|
||||||
|
controller.camera_state.format =
|
||||||
|
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(controller.camera_values.format);
|
||||||
|
controller.camera_state.data = controller.camera_values.data;
|
||||||
|
|
||||||
|
lock.unlock();
|
||||||
|
TriggerOnChange(ControllerTriggerType::IrSensor, true);
|
||||||
|
}
|
||||||
|
|
||||||
bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
|
bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
|
||||||
if (device_index >= output_devices.size()) {
|
if (device_index >= output_devices.size()) {
|
||||||
return false;
|
return false;
|
||||||
@ -928,6 +960,23 @@ bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode)
|
|||||||
return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None;
|
return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EmulatedController::SetCameraFormat(
|
||||||
|
Core::IrSensor::ImageTransferProcessorFormat camera_format) {
|
||||||
|
LOG_INFO(Service_HID, "Set camera format {}", camera_format);
|
||||||
|
|
||||||
|
auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
|
||||||
|
auto& camera_output_device = output_devices[2];
|
||||||
|
|
||||||
|
if (right_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
|
||||||
|
camera_format)) == Common::Input::CameraError::None) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to Qt camera if native device doesn't have support
|
||||||
|
return camera_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
|
||||||
|
camera_format)) == Common::Input::CameraError::None;
|
||||||
|
}
|
||||||
|
|
||||||
void EmulatedController::SetLedPattern() {
|
void EmulatedController::SetLedPattern() {
|
||||||
for (auto& device : output_devices) {
|
for (auto& device : output_devices) {
|
||||||
if (!device) {
|
if (!device) {
|
||||||
@ -1163,6 +1212,11 @@ BatteryValues EmulatedController::GetBatteryValues() const {
|
|||||||
return controller.battery_values;
|
return controller.battery_values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CameraValues EmulatedController::GetCameraValues() const {
|
||||||
|
std::scoped_lock lock{mutex};
|
||||||
|
return controller.camera_values;
|
||||||
|
}
|
||||||
|
|
||||||
HomeButtonState EmulatedController::GetHomeButtons() const {
|
HomeButtonState EmulatedController::GetHomeButtons() const {
|
||||||
std::scoped_lock lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
if (is_configuring) {
|
if (is_configuring) {
|
||||||
@ -1251,6 +1305,11 @@ BatteryLevelState EmulatedController::GetBattery() const {
|
|||||||
return controller.battery_state;
|
return controller.battery_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CameraState& EmulatedController::GetCamera() const {
|
||||||
|
std::scoped_lock lock{mutex};
|
||||||
|
return controller.camera_state;
|
||||||
|
}
|
||||||
|
|
||||||
void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
|
void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
|
||||||
std::scoped_lock lock{callback_mutex};
|
std::scoped_lock lock{callback_mutex};
|
||||||
for (const auto& poller_pair : callback_list) {
|
for (const auto& poller_pair : callback_list) {
|
||||||
|
@ -15,10 +15,12 @@
|
|||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "common/vector_math.h"
|
#include "common/vector_math.h"
|
||||||
#include "core/hid/hid_types.h"
|
#include "core/hid/hid_types.h"
|
||||||
|
#include "core/hid/irs_types.h"
|
||||||
#include "core/hid/motion_input.h"
|
#include "core/hid/motion_input.h"
|
||||||
|
|
||||||
namespace Core::HID {
|
namespace Core::HID {
|
||||||
const std::size_t max_emulated_controllers = 2;
|
const std::size_t max_emulated_controllers = 2;
|
||||||
|
const std::size_t output_devices = 3;
|
||||||
struct ControllerMotionInfo {
|
struct ControllerMotionInfo {
|
||||||
Common::Input::MotionStatus raw_status{};
|
Common::Input::MotionStatus raw_status{};
|
||||||
MotionInput emulated{};
|
MotionInput emulated{};
|
||||||
@ -34,15 +36,16 @@ using TriggerDevices =
|
|||||||
std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>;
|
std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>;
|
||||||
using BatteryDevices =
|
using BatteryDevices =
|
||||||
std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
|
std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
|
||||||
using OutputDevices =
|
using CameraDevices = std::unique_ptr<Common::Input::InputDevice>;
|
||||||
std::array<std::unique_ptr<Common::Input::OutputDevice>, max_emulated_controllers>;
|
using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices>;
|
||||||
|
|
||||||
using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>;
|
using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>;
|
||||||
using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>;
|
using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>;
|
||||||
using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>;
|
using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>;
|
||||||
using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>;
|
using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>;
|
||||||
using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>;
|
using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>;
|
||||||
using OutputParams = std::array<Common::ParamPackage, max_emulated_controllers>;
|
using CameraParams = Common::ParamPackage;
|
||||||
|
using OutputParams = std::array<Common::ParamPackage, output_devices>;
|
||||||
|
|
||||||
using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
|
using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
|
||||||
using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>;
|
using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>;
|
||||||
@ -51,6 +54,7 @@ using TriggerValues =
|
|||||||
using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::NativeMotion::NumMotions>;
|
using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::NativeMotion::NumMotions>;
|
||||||
using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>;
|
using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>;
|
||||||
using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>;
|
using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>;
|
||||||
|
using CameraValues = Common::Input::CameraStatus;
|
||||||
using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>;
|
using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>;
|
||||||
|
|
||||||
struct AnalogSticks {
|
struct AnalogSticks {
|
||||||
@ -70,6 +74,12 @@ struct BatteryLevelState {
|
|||||||
NpadPowerInfo right{};
|
NpadPowerInfo right{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CameraState {
|
||||||
|
Core::IrSensor::ImageTransferProcessorFormat format{};
|
||||||
|
std::vector<u8> data{};
|
||||||
|
std::size_t sample{};
|
||||||
|
};
|
||||||
|
|
||||||
struct ControllerMotion {
|
struct ControllerMotion {
|
||||||
Common::Vec3f accel{};
|
Common::Vec3f accel{};
|
||||||
Common::Vec3f gyro{};
|
Common::Vec3f gyro{};
|
||||||
@ -96,6 +106,7 @@ struct ControllerStatus {
|
|||||||
ColorValues color_values{};
|
ColorValues color_values{};
|
||||||
BatteryValues battery_values{};
|
BatteryValues battery_values{};
|
||||||
VibrationValues vibration_values{};
|
VibrationValues vibration_values{};
|
||||||
|
CameraValues camera_values{};
|
||||||
|
|
||||||
// Data for HID serices
|
// Data for HID serices
|
||||||
HomeButtonState home_button_state{};
|
HomeButtonState home_button_state{};
|
||||||
@ -107,6 +118,7 @@ struct ControllerStatus {
|
|||||||
NpadGcTriggerState gc_trigger_state{};
|
NpadGcTriggerState gc_trigger_state{};
|
||||||
ControllerColors colors_state{};
|
ControllerColors colors_state{};
|
||||||
BatteryLevelState battery_state{};
|
BatteryLevelState battery_state{};
|
||||||
|
CameraState camera_state{};
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ControllerTriggerType {
|
enum class ControllerTriggerType {
|
||||||
@ -117,6 +129,7 @@ enum class ControllerTriggerType {
|
|||||||
Color,
|
Color,
|
||||||
Battery,
|
Battery,
|
||||||
Vibration,
|
Vibration,
|
||||||
|
IrSensor,
|
||||||
Connected,
|
Connected,
|
||||||
Disconnected,
|
Disconnected,
|
||||||
Type,
|
Type,
|
||||||
@ -269,6 +282,9 @@ public:
|
|||||||
/// Returns the latest battery status from the controller with parameters
|
/// Returns the latest battery status from the controller with parameters
|
||||||
BatteryValues GetBatteryValues() const;
|
BatteryValues GetBatteryValues() const;
|
||||||
|
|
||||||
|
/// Returns the latest camera status from the controller with parameters
|
||||||
|
CameraValues GetCameraValues() const;
|
||||||
|
|
||||||
/// Returns the latest status of button input for the hid::HomeButton service
|
/// Returns the latest status of button input for the hid::HomeButton service
|
||||||
HomeButtonState GetHomeButtons() const;
|
HomeButtonState GetHomeButtons() const;
|
||||||
|
|
||||||
@ -296,6 +312,9 @@ public:
|
|||||||
/// Returns the latest battery status from the controller
|
/// Returns the latest battery status from the controller
|
||||||
BatteryLevelState GetBattery() const;
|
BatteryLevelState GetBattery() const;
|
||||||
|
|
||||||
|
/// Returns the latest camera status from the controller
|
||||||
|
const CameraState& GetCamera() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a specific vibration to the output device
|
* Sends a specific vibration to the output device
|
||||||
* @return true if vibration had no errors
|
* @return true if vibration had no errors
|
||||||
@ -315,6 +334,13 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool SetPollingMode(Common::Input::PollingMode polling_mode);
|
bool SetPollingMode(Common::Input::PollingMode polling_mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the desired camera format to be polled from a controller
|
||||||
|
* @param camera_format size of each frame
|
||||||
|
* @return true if SetCameraFormat was successfull
|
||||||
|
*/
|
||||||
|
bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format);
|
||||||
|
|
||||||
/// Returns the led pattern corresponding to this emulated controller
|
/// Returns the led pattern corresponding to this emulated controller
|
||||||
LedPattern GetLedPattern() const;
|
LedPattern GetLedPattern() const;
|
||||||
|
|
||||||
@ -392,6 +418,12 @@ private:
|
|||||||
*/
|
*/
|
||||||
void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index);
|
void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the camera status of the controller
|
||||||
|
* @param callback A CallbackStatus containing the camera status
|
||||||
|
*/
|
||||||
|
void SetCamera(const Common::Input::CallbackStatus& callback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggers a callback that something has changed on the controller status
|
* Triggers a callback that something has changed on the controller status
|
||||||
* @param type Input type of the event to trigger
|
* @param type Input type of the event to trigger
|
||||||
@ -417,6 +449,7 @@ private:
|
|||||||
ControllerMotionParams motion_params;
|
ControllerMotionParams motion_params;
|
||||||
TriggerParams trigger_params;
|
TriggerParams trigger_params;
|
||||||
BatteryParams battery_params;
|
BatteryParams battery_params;
|
||||||
|
CameraParams camera_params;
|
||||||
OutputParams output_params;
|
OutputParams output_params;
|
||||||
|
|
||||||
ButtonDevices button_devices;
|
ButtonDevices button_devices;
|
||||||
@ -424,6 +457,7 @@ private:
|
|||||||
ControllerMotionDevices motion_devices;
|
ControllerMotionDevices motion_devices;
|
||||||
TriggerDevices trigger_devices;
|
TriggerDevices trigger_devices;
|
||||||
BatteryDevices battery_devices;
|
BatteryDevices battery_devices;
|
||||||
|
CameraDevices camera_devices;
|
||||||
OutputDevices output_devices;
|
OutputDevices output_devices;
|
||||||
|
|
||||||
// TAS related variables
|
// TAS related variables
|
||||||
|
@ -270,6 +270,20 @@ Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatu
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback) {
|
||||||
|
Common::Input::CameraStatus camera{};
|
||||||
|
switch (callback.type) {
|
||||||
|
case Common::Input::InputType::IrSensor:
|
||||||
|
camera = callback.camera_status;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_ERROR(Input, "Conversion from type {} to camera not implemented", callback.type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return camera;
|
||||||
|
}
|
||||||
|
|
||||||
void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) {
|
void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) {
|
||||||
const auto& properties = analog.properties;
|
const auto& properties = analog.properties;
|
||||||
float& raw_value = analog.raw_value;
|
float& raw_value = analog.raw_value;
|
||||||
|
@ -76,6 +76,14 @@ Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackSta
|
|||||||
*/
|
*/
|
||||||
Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback);
|
Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts raw input data into a valid camera status.
|
||||||
|
*
|
||||||
|
* @param callback Supported callbacks: Camera.
|
||||||
|
* @return A valid CameraObject object.
|
||||||
|
*/
|
||||||
|
Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts raw analog data into a valid analog value
|
* Converts raw analog data into a valid analog value
|
||||||
* @param analog An analog object containing raw data and properties
|
* @param analog An analog object containing raw data and properties
|
||||||
|
@ -536,6 +536,8 @@ void SoftwareKeyboard::InitializeFrontendNormalKeyboard() {
|
|||||||
.sub_text{std::move(sub_text)},
|
.sub_text{std::move(sub_text)},
|
||||||
.guide_text{std::move(guide_text)},
|
.guide_text{std::move(guide_text)},
|
||||||
.initial_text{initial_text},
|
.initial_text{initial_text},
|
||||||
|
.left_optional_symbol_key{swkbd_config_common.left_optional_symbol_key},
|
||||||
|
.right_optional_symbol_key{swkbd_config_common.right_optional_symbol_key},
|
||||||
.max_text_length{max_text_length},
|
.max_text_length{max_text_length},
|
||||||
.min_text_length{min_text_length},
|
.min_text_length{min_text_length},
|
||||||
.initial_cursor_position{initial_cursor_position},
|
.initial_cursor_position{initial_cursor_position},
|
||||||
@ -591,6 +593,8 @@ void SoftwareKeyboard::InitializeFrontendInlineKeyboardOld() {
|
|||||||
.sub_text{},
|
.sub_text{},
|
||||||
.guide_text{},
|
.guide_text{},
|
||||||
.initial_text{current_text},
|
.initial_text{current_text},
|
||||||
|
.left_optional_symbol_key{appear_arg.left_optional_symbol_key},
|
||||||
|
.right_optional_symbol_key{appear_arg.right_optional_symbol_key},
|
||||||
.max_text_length{max_text_length},
|
.max_text_length{max_text_length},
|
||||||
.min_text_length{min_text_length},
|
.min_text_length{min_text_length},
|
||||||
.initial_cursor_position{initial_cursor_position},
|
.initial_cursor_position{initial_cursor_position},
|
||||||
@ -632,6 +636,8 @@ void SoftwareKeyboard::InitializeFrontendInlineKeyboardNew() {
|
|||||||
.sub_text{},
|
.sub_text{},
|
||||||
.guide_text{},
|
.guide_text{},
|
||||||
.initial_text{current_text},
|
.initial_text{current_text},
|
||||||
|
.left_optional_symbol_key{appear_arg.left_optional_symbol_key},
|
||||||
|
.right_optional_symbol_key{appear_arg.right_optional_symbol_key},
|
||||||
.max_text_length{max_text_length},
|
.max_text_length{max_text_length},
|
||||||
.min_text_length{min_text_length},
|
.min_text_length{min_text_length},
|
||||||
.initial_cursor_position{initial_cursor_position},
|
.initial_cursor_position{initial_cursor_position},
|
||||||
|
@ -19,3 +19,10 @@ constexpr Result InvalidNpadId{ErrorModule::HID, 709};
|
|||||||
constexpr Result NpadNotConnected{ErrorModule::HID, 710};
|
constexpr Result NpadNotConnected{ErrorModule::HID, 710};
|
||||||
|
|
||||||
} // namespace Service::HID
|
} // namespace Service::HID
|
||||||
|
|
||||||
|
namespace Service::IRS {
|
||||||
|
|
||||||
|
constexpr Result InvalidProcessorState{ErrorModule::Irsensor, 78};
|
||||||
|
constexpr Result InvalidIrCameraHandle{ErrorModule::Irsensor, 204};
|
||||||
|
|
||||||
|
} // namespace Service::IRS
|
||||||
|
@ -2345,8 +2345,8 @@ void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system
|
|||||||
std::make_shared<HidSys>(system)->InstallAsService(service_manager);
|
std::make_shared<HidSys>(system)->InstallAsService(service_manager);
|
||||||
std::make_shared<HidTmp>(system)->InstallAsService(service_manager);
|
std::make_shared<HidTmp>(system)->InstallAsService(service_manager);
|
||||||
|
|
||||||
std::make_shared<IRS>(system)->InstallAsService(service_manager);
|
std::make_shared<Service::IRS::IRS>(system)->InstallAsService(service_manager);
|
||||||
std::make_shared<IRS_SYS>(system)->InstallAsService(service_manager);
|
std::make_shared<Service::IRS::IRS_SYS>(system)->InstallAsService(service_manager);
|
||||||
|
|
||||||
std::make_shared<XCD_SYS>(system)->InstallAsService(service_manager);
|
std::make_shared<XCD_SYS>(system)->InstallAsService(service_manager);
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,28 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
|
#include "core/hid/emulated_controller.h"
|
||||||
|
#include "core/hid/hid_core.h"
|
||||||
#include "core/hle/ipc_helpers.h"
|
#include "core/hle/ipc_helpers.h"
|
||||||
#include "core/hle/kernel/k_shared_memory.h"
|
#include "core/hle/kernel/k_shared_memory.h"
|
||||||
#include "core/hle/kernel/k_transfer_memory.h"
|
#include "core/hle/kernel/k_transfer_memory.h"
|
||||||
#include "core/hle/kernel/kernel.h"
|
#include "core/hle/kernel/kernel.h"
|
||||||
#include "core/hle/service/hid/errors.h"
|
#include "core/hle/service/hid/errors.h"
|
||||||
#include "core/hle/service/hid/irs.h"
|
#include "core/hle/service/hid/irs.h"
|
||||||
|
#include "core/hle/service/hid/irsensor/clustering_processor.h"
|
||||||
|
#include "core/hle/service/hid/irsensor/image_transfer_processor.h"
|
||||||
|
#include "core/hle/service/hid/irsensor/ir_led_processor.h"
|
||||||
|
#include "core/hle/service/hid/irsensor/moment_processor.h"
|
||||||
|
#include "core/hle/service/hid/irsensor/pointing_processor.h"
|
||||||
|
#include "core/hle/service/hid/irsensor/tera_plugin_processor.h"
|
||||||
|
#include "core/memory.h"
|
||||||
|
|
||||||
namespace Service::HID {
|
namespace Service::IRS {
|
||||||
|
|
||||||
IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
|
IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
@ -36,14 +48,19 @@ IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
|
|||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
u8* raw_shared_memory = system.Kernel().GetIrsSharedMem().GetPointer();
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
|
shared_memory = std::construct_at(reinterpret_cast<StatusManager*>(raw_shared_memory));
|
||||||
|
|
||||||
|
npad_device = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
||||||
}
|
}
|
||||||
|
IRS::~IRS() = default;
|
||||||
|
|
||||||
void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) {
|
void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
|
LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}",
|
||||||
applet_resource_user_id);
|
applet_resource_user_id);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
@ -54,7 +71,7 @@ void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) {
|
|||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
|
LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}",
|
||||||
applet_resource_user_id);
|
applet_resource_user_id);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
@ -75,7 +92,7 @@ void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
|
|||||||
void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
|
void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
struct Parameters {
|
struct Parameters {
|
||||||
IrCameraHandle camera_handle;
|
Core::IrSensor::IrCameraHandle camera_handle;
|
||||||
INSERT_PADDING_WORDS_NOINIT(1);
|
INSERT_PADDING_WORDS_NOINIT(1);
|
||||||
u64 applet_resource_user_id;
|
u64 applet_resource_user_id;
|
||||||
};
|
};
|
||||||
@ -88,17 +105,23 @@ void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
|
|||||||
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
||||||
parameters.applet_resource_user_id);
|
parameters.applet_resource_user_id);
|
||||||
|
|
||||||
|
auto result = IsIrCameraHandleValid(parameters.camera_handle);
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
// TODO: Stop Image processor
|
||||||
|
result = ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
|
void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
struct Parameters {
|
struct Parameters {
|
||||||
IrCameraHandle camera_handle;
|
Core::IrSensor::IrCameraHandle camera_handle;
|
||||||
INSERT_PADDING_WORDS_NOINIT(1);
|
INSERT_PADDING_WORDS_NOINIT(1);
|
||||||
u64 applet_resource_user_id;
|
u64 applet_resource_user_id;
|
||||||
PackedMomentProcessorConfig processor_config;
|
Core::IrSensor::PackedMomentProcessorConfig processor_config;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
|
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
|
||||||
|
|
||||||
@ -109,19 +132,28 @@ void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
|
|||||||
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
||||||
parameters.applet_resource_user_id);
|
parameters.applet_resource_user_id);
|
||||||
|
|
||||||
|
const auto result = IsIrCameraHandleValid(parameters.camera_handle);
|
||||||
|
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
|
||||||
|
MakeProcessor<MomentProcessor>(parameters.camera_handle, device);
|
||||||
|
auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle);
|
||||||
|
image_transfer_processor.SetConfig(parameters.processor_config);
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
|
void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
struct Parameters {
|
struct Parameters {
|
||||||
IrCameraHandle camera_handle;
|
Core::IrSensor::IrCameraHandle camera_handle;
|
||||||
INSERT_PADDING_WORDS_NOINIT(1);
|
INSERT_PADDING_WORDS_NOINIT(1);
|
||||||
u64 applet_resource_user_id;
|
u64 applet_resource_user_id;
|
||||||
PackedClusteringProcessorConfig processor_config;
|
Core::IrSensor::PackedClusteringProcessorConfig processor_config;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(Parameters) == 0x40, "Parameters has incorrect size.");
|
static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
|
||||||
|
|
||||||
const auto parameters{rp.PopRaw<Parameters>()};
|
const auto parameters{rp.PopRaw<Parameters>()};
|
||||||
|
|
||||||
@ -130,17 +162,27 @@ void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
|
|||||||
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
||||||
parameters.applet_resource_user_id);
|
parameters.applet_resource_user_id);
|
||||||
|
|
||||||
|
auto result = IsIrCameraHandleValid(parameters.camera_handle);
|
||||||
|
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
|
||||||
|
MakeProcessorWithCoreContext<ClusteringProcessor>(parameters.camera_handle, device);
|
||||||
|
auto& image_transfer_processor =
|
||||||
|
GetProcessor<ClusteringProcessor>(parameters.camera_handle);
|
||||||
|
image_transfer_processor.SetConfig(parameters.processor_config);
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
|
void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
struct Parameters {
|
struct Parameters {
|
||||||
IrCameraHandle camera_handle;
|
Core::IrSensor::IrCameraHandle camera_handle;
|
||||||
INSERT_PADDING_WORDS_NOINIT(1);
|
INSERT_PADDING_WORDS_NOINIT(1);
|
||||||
u64 applet_resource_user_id;
|
u64 applet_resource_user_id;
|
||||||
PackedImageTransferProcessorConfig processor_config;
|
Core::IrSensor::PackedImageTransferProcessorConfig processor_config;
|
||||||
u32 transfer_memory_size;
|
u32 transfer_memory_size;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
|
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
|
||||||
@ -151,20 +193,42 @@ void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
|
|||||||
auto t_mem =
|
auto t_mem =
|
||||||
system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
|
system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
|
||||||
|
|
||||||
LOG_WARNING(Service_IRS,
|
if (t_mem.IsNull()) {
|
||||||
"(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, "
|
LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(ResultUnknown);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_MSG(t_mem->GetSize() == parameters.transfer_memory_size, "t_mem has incorrect size");
|
||||||
|
|
||||||
|
u8* transfer_memory = system.Memory().GetPointer(t_mem->GetSourceAddress());
|
||||||
|
|
||||||
|
LOG_INFO(Service_IRS,
|
||||||
|
"called, npad_type={}, npad_id={}, transfer_memory_size={}, transfer_memory_size={}, "
|
||||||
"applet_resource_user_id={}",
|
"applet_resource_user_id={}",
|
||||||
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
||||||
parameters.transfer_memory_size, parameters.applet_resource_user_id);
|
parameters.transfer_memory_size, t_mem->GetSize(), parameters.applet_resource_user_id);
|
||||||
|
|
||||||
|
const auto result = IsIrCameraHandleValid(parameters.camera_handle);
|
||||||
|
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
|
||||||
|
MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device);
|
||||||
|
auto& image_transfer_processor =
|
||||||
|
GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
|
||||||
|
image_transfer_processor.SetConfig(parameters.processor_config);
|
||||||
|
image_transfer_processor.SetTransferMemoryPointer(transfer_memory);
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
|
void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
struct Parameters {
|
struct Parameters {
|
||||||
IrCameraHandle camera_handle;
|
Core::IrSensor::IrCameraHandle camera_handle;
|
||||||
INSERT_PADDING_WORDS_NOINIT(1);
|
INSERT_PADDING_WORDS_NOINIT(1);
|
||||||
u64 applet_resource_user_id;
|
u64 applet_resource_user_id;
|
||||||
};
|
};
|
||||||
@ -172,32 +236,68 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
|
|||||||
|
|
||||||
const auto parameters{rp.PopRaw<Parameters>()};
|
const auto parameters{rp.PopRaw<Parameters>()};
|
||||||
|
|
||||||
LOG_WARNING(Service_IRS,
|
LOG_DEBUG(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
|
||||||
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
|
|
||||||
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
||||||
parameters.applet_resource_user_id);
|
parameters.applet_resource_user_id);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 5};
|
const auto result = IsIrCameraHandleValid(parameters.camera_handle);
|
||||||
|
if (result.IsError()) {
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
|
||||||
|
|
||||||
|
if (device.mode != Core::IrSensor::IrSensorMode::ImageTransferProcessor) {
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(InvalidProcessorState);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> data{};
|
||||||
|
const auto& image_transfer_processor =
|
||||||
|
GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
|
||||||
|
const auto& state = image_transfer_processor.GetState(data);
|
||||||
|
|
||||||
|
ctx.WriteBuffer(data);
|
||||||
|
IPC::ResponseBuilder rb{ctx, 6};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.PushRaw<u64>(system.CoreTiming().GetCPUTicks());
|
rb.PushRaw(state);
|
||||||
rb.PushRaw<u32>(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) {
|
void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
|
struct Parameters {
|
||||||
const auto processor_config{rp.PopRaw<PackedTeraPluginProcessorConfig>()};
|
Core::IrSensor::IrCameraHandle camera_handle;
|
||||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
Core::IrSensor::PackedTeraPluginProcessorConfig processor_config;
|
||||||
|
INSERT_PADDING_WORDS_NOINIT(1);
|
||||||
|
u64 applet_resource_user_id;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
|
||||||
|
|
||||||
LOG_WARNING(Service_IRS,
|
const auto parameters{rp.PopRaw<Parameters>()};
|
||||||
|
|
||||||
|
LOG_WARNING(
|
||||||
|
Service_IRS,
|
||||||
"(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, "
|
"(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, "
|
||||||
"applet_resource_user_id={}",
|
"applet_resource_user_id={}",
|
||||||
camera_handle.npad_type, camera_handle.npad_id, processor_config.mode,
|
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
||||||
processor_config.required_mcu_version.major,
|
parameters.processor_config.mode, parameters.processor_config.required_mcu_version.major,
|
||||||
processor_config.required_mcu_version.minor, applet_resource_user_id);
|
parameters.processor_config.required_mcu_version.minor, parameters.applet_resource_user_id);
|
||||||
|
|
||||||
|
const auto result = IsIrCameraHandleValid(parameters.camera_handle);
|
||||||
|
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
|
||||||
|
MakeProcessor<TeraPluginProcessor>(parameters.camera_handle, device);
|
||||||
|
auto& image_transfer_processor =
|
||||||
|
GetProcessor<TeraPluginProcessor>(parameters.camera_handle);
|
||||||
|
image_transfer_processor.SetConfig(parameters.processor_config);
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
|
void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
|
||||||
@ -207,17 +307,17 @@ void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
|
|||||||
if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid &&
|
if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid &&
|
||||||
npad_id != Core::HID::NpadIdType::Handheld) {
|
npad_id != Core::HID::NpadIdType::Handheld) {
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(InvalidNpadId);
|
rb.Push(Service::HID::InvalidNpadId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
IrCameraHandle camera_handle{
|
Core::IrSensor::IrCameraHandle camera_handle{
|
||||||
.npad_id = static_cast<u8>(NpadIdTypeToIndex(npad_id)),
|
.npad_id = static_cast<u8>(NpadIdTypeToIndex(npad_id)),
|
||||||
.npad_type = Core::HID::NpadStyleIndex::None,
|
.npad_type = Core::HID::NpadStyleIndex::None,
|
||||||
};
|
};
|
||||||
|
|
||||||
LOG_WARNING(Service_IRS, "(STUBBED) called, npad_id={}, camera_npad_id={}, camera_npad_type={}",
|
LOG_INFO(Service_IRS, "called, npad_id={}, camera_npad_id={}, camera_npad_type={}", npad_id,
|
||||||
npad_id, camera_handle.npad_id, camera_handle.npad_type);
|
camera_handle.npad_id, camera_handle.npad_type);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
@ -226,8 +326,8 @@ void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
|
|||||||
|
|
||||||
void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
|
void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
|
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
|
||||||
const auto processor_config{rp.PopRaw<PackedPointingProcessorConfig>()};
|
const auto processor_config{rp.PopRaw<Core::IrSensor::PackedPointingProcessorConfig>()};
|
||||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
LOG_WARNING(
|
LOG_WARNING(
|
||||||
@ -236,14 +336,23 @@ void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
|
|||||||
camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major,
|
camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major,
|
||||||
processor_config.required_mcu_version.minor, applet_resource_user_id);
|
processor_config.required_mcu_version.minor, applet_resource_user_id);
|
||||||
|
|
||||||
|
auto result = IsIrCameraHandleValid(camera_handle);
|
||||||
|
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
|
||||||
|
MakeProcessor<PointingProcessor>(camera_handle, device);
|
||||||
|
auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle);
|
||||||
|
image_transfer_processor.SetConfig(processor_config);
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
|
void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
struct Parameters {
|
struct Parameters {
|
||||||
IrCameraHandle camera_handle;
|
Core::IrSensor::IrCameraHandle camera_handle;
|
||||||
INSERT_PADDING_WORDS_NOINIT(1);
|
INSERT_PADDING_WORDS_NOINIT(1);
|
||||||
u64 applet_resource_user_id;
|
u64 applet_resource_user_id;
|
||||||
};
|
};
|
||||||
@ -256,14 +365,20 @@ void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
|
|||||||
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
||||||
parameters.applet_resource_user_id);
|
parameters.applet_resource_user_id);
|
||||||
|
|
||||||
|
auto result = IsIrCameraHandleValid(parameters.camera_handle);
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
// TODO: Suspend image processor
|
||||||
|
result = ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) {
|
void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
|
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
|
||||||
const auto mcu_version{rp.PopRaw<PackedMcuVersion>()};
|
const auto mcu_version{rp.PopRaw<Core::IrSensor::PackedMcuVersion>()};
|
||||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
LOG_WARNING(
|
LOG_WARNING(
|
||||||
@ -272,37 +387,45 @@ void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) {
|
|||||||
camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major,
|
camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major,
|
||||||
mcu_version.minor);
|
mcu_version.minor);
|
||||||
|
|
||||||
|
auto result = IsIrCameraHandleValid(camera_handle);
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
// TODO: Check firmware version
|
||||||
|
result = ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) {
|
void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
struct Parameters {
|
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
|
||||||
IrCameraHandle camera_handle;
|
const auto function_level{rp.PopRaw<Core::IrSensor::PackedFunctionLevel>()};
|
||||||
PackedFunctionLevel function_level;
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
u64 applet_resource_user_id;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
|
|
||||||
|
|
||||||
const auto parameters{rp.PopRaw<Parameters>()};
|
LOG_WARNING(
|
||||||
|
Service_IRS,
|
||||||
|
"(STUBBED) called, npad_type={}, npad_id={}, function_level={}, applet_resource_user_id={}",
|
||||||
|
camera_handle.npad_type, camera_handle.npad_id, function_level.function_level,
|
||||||
|
applet_resource_user_id);
|
||||||
|
|
||||||
LOG_WARNING(Service_IRS,
|
auto result = IsIrCameraHandleValid(camera_handle);
|
||||||
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
|
if (result.IsSuccess()) {
|
||||||
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
// TODO: Set Function level
|
||||||
parameters.applet_resource_user_id);
|
result = ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
|
void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
struct Parameters {
|
struct Parameters {
|
||||||
IrCameraHandle camera_handle;
|
Core::IrSensor::IrCameraHandle camera_handle;
|
||||||
INSERT_PADDING_WORDS_NOINIT(1);
|
INSERT_PADDING_WORDS_NOINIT(1);
|
||||||
u64 applet_resource_user_id;
|
u64 applet_resource_user_id;
|
||||||
PackedImageTransferProcessorExConfig processor_config;
|
Core::IrSensor::PackedImageTransferProcessorExConfig processor_config;
|
||||||
u64 transfer_memory_size;
|
u64 transfer_memory_size;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
|
static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
|
||||||
@ -313,20 +436,33 @@ void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
|
|||||||
auto t_mem =
|
auto t_mem =
|
||||||
system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
|
system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
|
||||||
|
|
||||||
LOG_WARNING(Service_IRS,
|
u8* transfer_memory = system.Memory().GetPointer(t_mem->GetSourceAddress());
|
||||||
"(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, "
|
|
||||||
|
LOG_INFO(Service_IRS,
|
||||||
|
"called, npad_type={}, npad_id={}, transfer_memory_size={}, "
|
||||||
"applet_resource_user_id={}",
|
"applet_resource_user_id={}",
|
||||||
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
||||||
parameters.transfer_memory_size, parameters.applet_resource_user_id);
|
parameters.transfer_memory_size, parameters.applet_resource_user_id);
|
||||||
|
|
||||||
|
auto result = IsIrCameraHandleValid(parameters.camera_handle);
|
||||||
|
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
|
||||||
|
MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device);
|
||||||
|
auto& image_transfer_processor =
|
||||||
|
GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
|
||||||
|
image_transfer_processor.SetConfig(parameters.processor_config);
|
||||||
|
image_transfer_processor.SetTransferMemoryPointer(transfer_memory);
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
|
void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
|
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
|
||||||
const auto processor_config{rp.PopRaw<PackedIrLedProcessorConfig>()};
|
const auto processor_config{rp.PopRaw<Core::IrSensor::PackedIrLedProcessorConfig>()};
|
||||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
LOG_WARNING(Service_IRS,
|
LOG_WARNING(Service_IRS,
|
||||||
@ -336,14 +472,23 @@ void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
|
|||||||
processor_config.required_mcu_version.major,
|
processor_config.required_mcu_version.major,
|
||||||
processor_config.required_mcu_version.minor, applet_resource_user_id);
|
processor_config.required_mcu_version.minor, applet_resource_user_id);
|
||||||
|
|
||||||
|
auto result = IsIrCameraHandleValid(camera_handle);
|
||||||
|
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
|
||||||
|
MakeProcessor<IrLedProcessor>(camera_handle, device);
|
||||||
|
auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle);
|
||||||
|
image_transfer_processor.SetConfig(processor_config);
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
|
void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
struct Parameters {
|
struct Parameters {
|
||||||
IrCameraHandle camera_handle;
|
Core::IrSensor::IrCameraHandle camera_handle;
|
||||||
INSERT_PADDING_WORDS_NOINIT(1);
|
INSERT_PADDING_WORDS_NOINIT(1);
|
||||||
u64 applet_resource_user_id;
|
u64 applet_resource_user_id;
|
||||||
};
|
};
|
||||||
@ -356,14 +501,20 @@ void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
|
|||||||
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
||||||
parameters.applet_resource_user_id);
|
parameters.applet_resource_user_id);
|
||||||
|
|
||||||
|
auto result = IsIrCameraHandleValid(parameters.camera_handle);
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
// TODO: Stop image processor async
|
||||||
|
result = ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) {
|
void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
struct Parameters {
|
struct Parameters {
|
||||||
PackedFunctionLevel function_level;
|
Core::IrSensor::PackedFunctionLevel function_level;
|
||||||
INSERT_PADDING_WORDS_NOINIT(1);
|
INSERT_PADDING_WORDS_NOINIT(1);
|
||||||
u64 applet_resource_user_id;
|
u64 applet_resource_user_id;
|
||||||
};
|
};
|
||||||
@ -378,7 +529,22 @@ void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) {
|
|||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
IRS::~IRS() = default;
|
Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const {
|
||||||
|
if (camera_handle.npad_id >
|
||||||
|
static_cast<u8>(NpadIdTypeToIndex(Core::HID::NpadIdType::Handheld))) {
|
||||||
|
return InvalidIrCameraHandle;
|
||||||
|
}
|
||||||
|
if (camera_handle.npad_type != Core::HID::NpadStyleIndex::None) {
|
||||||
|
return InvalidIrCameraHandle;
|
||||||
|
}
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::IrSensor::DeviceFormat& IRS::GetIrCameraSharedMemoryDeviceEntry(
|
||||||
|
const Core::IrSensor::IrCameraHandle& camera_handle) {
|
||||||
|
ASSERT_MSG(sizeof(StatusManager::device) > camera_handle.npad_id, "invalid npad_id");
|
||||||
|
return shared_memory->device[camera_handle.npad_id];
|
||||||
|
}
|
||||||
|
|
||||||
IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} {
|
IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
@ -395,4 +561,4 @@ IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} {
|
|||||||
|
|
||||||
IRS_SYS::~IRS_SYS() = default;
|
IRS_SYS::~IRS_SYS() = default;
|
||||||
|
|
||||||
} // namespace Service::HID
|
} // namespace Service::IRS
|
||||||
|
@ -4,13 +4,19 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "core/hid/hid_types.h"
|
#include "core/hid/hid_types.h"
|
||||||
|
#include "core/hid/irs_types.h"
|
||||||
|
#include "core/hle/service/hid/irsensor/processor_base.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
class System;
|
class System;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Service::HID {
|
namespace Core::HID {
|
||||||
|
class EmulatedController;
|
||||||
|
} // namespace Core::HID
|
||||||
|
|
||||||
|
namespace Service::IRS {
|
||||||
|
|
||||||
class IRS final : public ServiceFramework<IRS> {
|
class IRS final : public ServiceFramework<IRS> {
|
||||||
public:
|
public:
|
||||||
@ -18,234 +24,19 @@ public:
|
|||||||
~IRS() override;
|
~IRS() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// This is nn::irsensor::IrCameraStatus
|
// This is nn::irsensor::detail::AruidFormat
|
||||||
enum IrCameraStatus : u32 {
|
struct AruidFormat {
|
||||||
Available,
|
u64 sensor_aruid;
|
||||||
Unsupported,
|
u64 sensor_aruid_status;
|
||||||
Unconnected,
|
|
||||||
};
|
};
|
||||||
|
static_assert(sizeof(AruidFormat) == 0x10, "AruidFormat is an invalid size");
|
||||||
|
|
||||||
// This is nn::irsensor::IrCameraInternalStatus
|
// This is nn::irsensor::detail::StatusManager
|
||||||
enum IrCameraInternalStatus : u32 {
|
struct StatusManager {
|
||||||
Stopped,
|
std::array<Core::IrSensor::DeviceFormat, 9> device;
|
||||||
FirmwareUpdateNeeded,
|
std::array<AruidFormat, 5> aruid;
|
||||||
Unkown2,
|
|
||||||
Unkown3,
|
|
||||||
Unkown4,
|
|
||||||
FirmwareVersionRequested,
|
|
||||||
FirmwareVersionIsInvalid,
|
|
||||||
Ready,
|
|
||||||
Setting,
|
|
||||||
};
|
};
|
||||||
|
static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size");
|
||||||
// This is nn::irsensor::detail::StatusManager::IrSensorMode
|
|
||||||
enum IrSensorMode : u64 {
|
|
||||||
None,
|
|
||||||
MomentProcessor,
|
|
||||||
ClusteringProcessor,
|
|
||||||
ImageTransferProcessor,
|
|
||||||
PointingProcessorMarker,
|
|
||||||
TeraPluginProcessor,
|
|
||||||
IrLedProcessor,
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is nn::irsensor::ImageProcessorStatus
|
|
||||||
enum ImageProcessorStatus : u8 {
|
|
||||||
stopped,
|
|
||||||
running,
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is nn::irsensor::ImageTransferProcessorFormat
|
|
||||||
enum ImageTransferProcessorFormat : u8 {
|
|
||||||
Size320x240,
|
|
||||||
Size160x120,
|
|
||||||
Size80x60,
|
|
||||||
Size40x30,
|
|
||||||
Size20x15,
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is nn::irsensor::AdaptiveClusteringMode
|
|
||||||
enum AdaptiveClusteringMode : u8 {
|
|
||||||
StaticFov,
|
|
||||||
DynamicFov,
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is nn::irsensor::AdaptiveClusteringTargetDistance
|
|
||||||
enum AdaptiveClusteringTargetDistance : u8 {
|
|
||||||
Near,
|
|
||||||
Middle,
|
|
||||||
Far,
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is nn::irsensor::IrsHandAnalysisMode
|
|
||||||
enum IrsHandAnalysisMode : u8 {
|
|
||||||
Silhouette,
|
|
||||||
Image,
|
|
||||||
SilhoueteAndImage,
|
|
||||||
SilhuetteOnly,
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is nn::irsensor::IrSensorFunctionLevel
|
|
||||||
enum IrSensorFunctionLevel : u8 {
|
|
||||||
unknown0,
|
|
||||||
unknown1,
|
|
||||||
unknown2,
|
|
||||||
unknown3,
|
|
||||||
unknown4,
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is nn::irsensor::IrCameraHandle
|
|
||||||
struct IrCameraHandle {
|
|
||||||
u8 npad_id{};
|
|
||||||
Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
|
|
||||||
INSERT_PADDING_BYTES(2);
|
|
||||||
};
|
|
||||||
static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size");
|
|
||||||
|
|
||||||
struct IrsRect {
|
|
||||||
s16 x;
|
|
||||||
s16 y;
|
|
||||||
s16 width;
|
|
||||||
s16 height;
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is nn::irsensor::PackedMcuVersion
|
|
||||||
struct PackedMcuVersion {
|
|
||||||
u16 major;
|
|
||||||
u16 minor;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size");
|
|
||||||
|
|
||||||
// This is nn::irsensor::MomentProcessorConfig
|
|
||||||
struct MomentProcessorConfig {
|
|
||||||
u64 exposire_time;
|
|
||||||
u8 light_target;
|
|
||||||
u8 gain;
|
|
||||||
u8 is_negative_used;
|
|
||||||
INSERT_PADDING_BYTES(7);
|
|
||||||
IrsRect window_of_interest;
|
|
||||||
u8 preprocess;
|
|
||||||
u8 preprocess_intensity_threshold;
|
|
||||||
INSERT_PADDING_BYTES(5);
|
|
||||||
};
|
|
||||||
static_assert(sizeof(MomentProcessorConfig) == 0x28,
|
|
||||||
"MomentProcessorConfig is an invalid size");
|
|
||||||
|
|
||||||
// This is nn::irsensor::PackedMomentProcessorConfig
|
|
||||||
struct PackedMomentProcessorConfig {
|
|
||||||
u64 exposire_time;
|
|
||||||
u8 light_target;
|
|
||||||
u8 gain;
|
|
||||||
u8 is_negative_used;
|
|
||||||
INSERT_PADDING_BYTES(5);
|
|
||||||
IrsRect window_of_interest;
|
|
||||||
PackedMcuVersion required_mcu_version;
|
|
||||||
u8 preprocess;
|
|
||||||
u8 preprocess_intensity_threshold;
|
|
||||||
INSERT_PADDING_BYTES(2);
|
|
||||||
};
|
|
||||||
static_assert(sizeof(PackedMomentProcessorConfig) == 0x20,
|
|
||||||
"PackedMomentProcessorConfig is an invalid size");
|
|
||||||
|
|
||||||
// This is nn::irsensor::ClusteringProcessorConfig
|
|
||||||
struct ClusteringProcessorConfig {
|
|
||||||
u64 exposire_time;
|
|
||||||
u32 light_target;
|
|
||||||
u32 gain;
|
|
||||||
u8 is_negative_used;
|
|
||||||
INSERT_PADDING_BYTES(7);
|
|
||||||
IrsRect window_of_interest;
|
|
||||||
u32 pixel_count_min;
|
|
||||||
u32 pixel_count_max;
|
|
||||||
u32 object_intensity_min;
|
|
||||||
u8 is_external_light_filter_enabled;
|
|
||||||
INSERT_PADDING_BYTES(3);
|
|
||||||
};
|
|
||||||
static_assert(sizeof(ClusteringProcessorConfig) == 0x30,
|
|
||||||
"ClusteringProcessorConfig is an invalid size");
|
|
||||||
|
|
||||||
// This is nn::irsensor::PackedClusteringProcessorConfig
|
|
||||||
struct PackedClusteringProcessorConfig {
|
|
||||||
u64 exposire_time;
|
|
||||||
u8 light_target;
|
|
||||||
u8 gain;
|
|
||||||
u8 is_negative_used;
|
|
||||||
INSERT_PADDING_BYTES(5);
|
|
||||||
IrsRect window_of_interest;
|
|
||||||
PackedMcuVersion required_mcu_version;
|
|
||||||
u32 pixel_count_min;
|
|
||||||
u32 pixel_count_max;
|
|
||||||
u32 object_intensity_min;
|
|
||||||
u8 is_external_light_filter_enabled;
|
|
||||||
INSERT_PADDING_BYTES(2);
|
|
||||||
};
|
|
||||||
static_assert(sizeof(PackedClusteringProcessorConfig) == 0x30,
|
|
||||||
"PackedClusteringProcessorConfig is an invalid size");
|
|
||||||
|
|
||||||
// This is nn::irsensor::PackedImageTransferProcessorConfig
|
|
||||||
struct PackedImageTransferProcessorConfig {
|
|
||||||
u64 exposire_time;
|
|
||||||
u8 light_target;
|
|
||||||
u8 gain;
|
|
||||||
u8 is_negative_used;
|
|
||||||
INSERT_PADDING_BYTES(5);
|
|
||||||
PackedMcuVersion required_mcu_version;
|
|
||||||
u8 format;
|
|
||||||
INSERT_PADDING_BYTES(3);
|
|
||||||
};
|
|
||||||
static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18,
|
|
||||||
"PackedImageTransferProcessorConfig is an invalid size");
|
|
||||||
|
|
||||||
// This is nn::irsensor::PackedTeraPluginProcessorConfig
|
|
||||||
struct PackedTeraPluginProcessorConfig {
|
|
||||||
PackedMcuVersion required_mcu_version;
|
|
||||||
u8 mode;
|
|
||||||
INSERT_PADDING_BYTES(3);
|
|
||||||
};
|
|
||||||
static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8,
|
|
||||||
"PackedTeraPluginProcessorConfig is an invalid size");
|
|
||||||
|
|
||||||
// This is nn::irsensor::PackedPointingProcessorConfig
|
|
||||||
struct PackedPointingProcessorConfig {
|
|
||||||
IrsRect window_of_interest;
|
|
||||||
PackedMcuVersion required_mcu_version;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(PackedPointingProcessorConfig) == 0xC,
|
|
||||||
"PackedPointingProcessorConfig is an invalid size");
|
|
||||||
|
|
||||||
// This is nn::irsensor::PackedFunctionLevel
|
|
||||||
struct PackedFunctionLevel {
|
|
||||||
IrSensorFunctionLevel function_level;
|
|
||||||
INSERT_PADDING_BYTES(3);
|
|
||||||
};
|
|
||||||
static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size");
|
|
||||||
|
|
||||||
// This is nn::irsensor::PackedImageTransferProcessorExConfig
|
|
||||||
struct PackedImageTransferProcessorExConfig {
|
|
||||||
u64 exposire_time;
|
|
||||||
u8 light_target;
|
|
||||||
u8 gain;
|
|
||||||
u8 is_negative_used;
|
|
||||||
INSERT_PADDING_BYTES(5);
|
|
||||||
PackedMcuVersion required_mcu_version;
|
|
||||||
ImageTransferProcessorFormat origin_format;
|
|
||||||
ImageTransferProcessorFormat trimming_format;
|
|
||||||
u16 trimming_start_x;
|
|
||||||
u16 trimming_start_y;
|
|
||||||
u8 is_external_light_filter_enabled;
|
|
||||||
INSERT_PADDING_BYTES(3);
|
|
||||||
};
|
|
||||||
static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20,
|
|
||||||
"PackedImageTransferProcessorExConfig is an invalid size");
|
|
||||||
|
|
||||||
// This is nn::irsensor::PackedIrLedProcessorConfig
|
|
||||||
struct PackedIrLedProcessorConfig {
|
|
||||||
PackedMcuVersion required_mcu_version;
|
|
||||||
u8 light_target;
|
|
||||||
INSERT_PADDING_BYTES(3);
|
|
||||||
};
|
|
||||||
static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8,
|
|
||||||
"PackedIrLedProcessorConfig is an invalid size");
|
|
||||||
|
|
||||||
void ActivateIrsensor(Kernel::HLERequestContext& ctx);
|
void ActivateIrsensor(Kernel::HLERequestContext& ctx);
|
||||||
void DeactivateIrsensor(Kernel::HLERequestContext& ctx);
|
void DeactivateIrsensor(Kernel::HLERequestContext& ctx);
|
||||||
@ -265,6 +56,56 @@ private:
|
|||||||
void RunIrLedProcessor(Kernel::HLERequestContext& ctx);
|
void RunIrLedProcessor(Kernel::HLERequestContext& ctx);
|
||||||
void StopImageProcessorAsync(Kernel::HLERequestContext& ctx);
|
void StopImageProcessorAsync(Kernel::HLERequestContext& ctx);
|
||||||
void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
|
void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const;
|
||||||
|
Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry(
|
||||||
|
const Core::IrSensor::IrCameraHandle& camera_handle);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void MakeProcessor(const Core::IrSensor::IrCameraHandle& handle,
|
||||||
|
Core::IrSensor::DeviceFormat& device_state) {
|
||||||
|
const auto index = static_cast<std::size_t>(handle.npad_id);
|
||||||
|
if (index > sizeof(processors)) {
|
||||||
|
LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
processors[index] = std::make_unique<T>(device_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void MakeProcessorWithCoreContext(const Core::IrSensor::IrCameraHandle& handle,
|
||||||
|
Core::IrSensor::DeviceFormat& device_state) {
|
||||||
|
const auto index = static_cast<std::size_t>(handle.npad_id);
|
||||||
|
if (index > sizeof(processors)) {
|
||||||
|
LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
processors[index] = std::make_unique<T>(system.HIDCore(), device_state, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) {
|
||||||
|
const auto index = static_cast<std::size_t>(handle.npad_id);
|
||||||
|
if (index > sizeof(processors)) {
|
||||||
|
LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
|
||||||
|
return static_cast<T&>(*processors[0]);
|
||||||
|
}
|
||||||
|
return static_cast<T&>(*processors[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) const {
|
||||||
|
const auto index = static_cast<std::size_t>(handle.npad_id);
|
||||||
|
if (index > sizeof(processors)) {
|
||||||
|
LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
|
||||||
|
return static_cast<T&>(*processors[0]);
|
||||||
|
}
|
||||||
|
return static_cast<T&>(*processors[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::HID::EmulatedController* npad_device = nullptr;
|
||||||
|
StatusManager* shared_memory = nullptr;
|
||||||
|
std::array<std::unique_ptr<ProcessorBase>, 9> processors{};
|
||||||
};
|
};
|
||||||
|
|
||||||
class IRS_SYS final : public ServiceFramework<IRS_SYS> {
|
class IRS_SYS final : public ServiceFramework<IRS_SYS> {
|
||||||
@ -273,4 +114,4 @@ public:
|
|||||||
~IRS_SYS() override;
|
~IRS_SYS() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::HID
|
} // namespace Service::IRS
|
||||||
|
47
src/core/hle/service/hid/irs_ring_lifo.h
Executable file
47
src/core/hle/service/hid/irs_ring_lifo.h
Executable file
@ -0,0 +1,47 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace Service::IRS {
|
||||||
|
|
||||||
|
template <typename State, std::size_t max_buffer_size>
|
||||||
|
struct Lifo {
|
||||||
|
s64 sampling_number{};
|
||||||
|
s64 buffer_count{};
|
||||||
|
std::array<State, max_buffer_size> entries{};
|
||||||
|
|
||||||
|
const State& ReadCurrentEntry() const {
|
||||||
|
return entries[GetBufferTail()];
|
||||||
|
}
|
||||||
|
|
||||||
|
const State& ReadPreviousEntry() const {
|
||||||
|
return entries[GetPreviousEntryIndex()];
|
||||||
|
}
|
||||||
|
|
||||||
|
s64 GetBufferTail() const {
|
||||||
|
return sampling_number % max_buffer_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t GetPreviousEntryIndex() const {
|
||||||
|
return static_cast<size_t>((GetBufferTail() + max_buffer_size - 1) % max_buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t GetNextEntryIndex() const {
|
||||||
|
return static_cast<size_t>((GetBufferTail() + 1) % max_buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteNextEntry(const State& new_state) {
|
||||||
|
if (buffer_count < static_cast<s64>(max_buffer_size)) {
|
||||||
|
buffer_count++;
|
||||||
|
}
|
||||||
|
sampling_number++;
|
||||||
|
entries[GetBufferTail()] = new_state;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Service::IRS
|
@ -1,34 +1,264 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
#include "core/hid/emulated_controller.h"
|
||||||
|
#include "core/hid/hid_core.h"
|
||||||
#include "core/hle/service/hid/irsensor/clustering_processor.h"
|
#include "core/hle/service/hid/irsensor/clustering_processor.h"
|
||||||
|
|
||||||
namespace Service::IRS {
|
namespace Service::IRS {
|
||||||
ClusteringProcessor::ClusteringProcessor(Core::IrSensor::DeviceFormat& device_format)
|
ClusteringProcessor::ClusteringProcessor(Core::HID::HIDCore& hid_core_,
|
||||||
: device(device_format) {
|
Core::IrSensor::DeviceFormat& device_format,
|
||||||
|
std::size_t npad_index)
|
||||||
|
: device{device_format} {
|
||||||
|
npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index);
|
||||||
|
|
||||||
device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor;
|
device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor;
|
||||||
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
|
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
|
||||||
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
|
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
|
||||||
|
SetDefaultConfig();
|
||||||
|
|
||||||
|
shared_memory = std::construct_at(
|
||||||
|
reinterpret_cast<ClusteringSharedMemory*>(&device_format.state.processor_raw_data));
|
||||||
|
|
||||||
|
Core::HID::ControllerUpdateCallback engine_callback{
|
||||||
|
.on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); },
|
||||||
|
.is_npad_service = true,
|
||||||
|
};
|
||||||
|
callback_key = npad_device->SetCallback(engine_callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
ClusteringProcessor::~ClusteringProcessor() = default;
|
ClusteringProcessor::~ClusteringProcessor() {
|
||||||
|
npad_device->DeleteCallback(callback_key);
|
||||||
|
};
|
||||||
|
|
||||||
void ClusteringProcessor::StartProcessor() {}
|
void ClusteringProcessor::StartProcessor() {
|
||||||
|
device.camera_status = Core::IrSensor::IrCameraStatus::Available;
|
||||||
|
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready;
|
||||||
|
}
|
||||||
|
|
||||||
void ClusteringProcessor::SuspendProcessor() {}
|
void ClusteringProcessor::SuspendProcessor() {}
|
||||||
|
|
||||||
void ClusteringProcessor::StopProcessor() {}
|
void ClusteringProcessor::StopProcessor() {}
|
||||||
|
|
||||||
|
void ClusteringProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) {
|
||||||
|
if (type != Core::HID::ControllerTriggerType::IrSensor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
next_state = {};
|
||||||
|
const auto camera_data = npad_device->GetCamera();
|
||||||
|
auto filtered_image = camera_data.data;
|
||||||
|
|
||||||
|
RemoveLowIntensityData(filtered_image);
|
||||||
|
|
||||||
|
const auto window_start_x = static_cast<std::size_t>(current_config.window_of_interest.x);
|
||||||
|
const auto window_start_y = static_cast<std::size_t>(current_config.window_of_interest.y);
|
||||||
|
const auto window_end_x =
|
||||||
|
window_start_x + static_cast<std::size_t>(current_config.window_of_interest.width);
|
||||||
|
const auto window_end_y =
|
||||||
|
window_start_y + static_cast<std::size_t>(current_config.window_of_interest.height);
|
||||||
|
|
||||||
|
for (std::size_t y = window_start_y; y < window_end_y; y++) {
|
||||||
|
for (std::size_t x = window_start_x; x < window_end_x; x++) {
|
||||||
|
u8 pixel = GetPixel(filtered_image, x, y);
|
||||||
|
if (pixel == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto cluster = GetClusterProperties(filtered_image, x, y);
|
||||||
|
if (cluster.pixel_count > current_config.pixel_count_max) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (cluster.pixel_count < current_config.pixel_count_min) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Cluster object limit reached
|
||||||
|
if (next_state.object_count >= next_state.data.size()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
next_state.data[next_state.object_count] = cluster;
|
||||||
|
next_state.object_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next_state.sampling_number = camera_data.sample;
|
||||||
|
next_state.timestamp = next_state.timestamp + 131;
|
||||||
|
next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
|
||||||
|
shared_memory->clustering_lifo.WriteNextEntry(next_state);
|
||||||
|
|
||||||
|
if (!IsProcessorActive()) {
|
||||||
|
StartProcessor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClusteringProcessor::RemoveLowIntensityData(std::vector<u8>& data) {
|
||||||
|
for (u8& pixel : data) {
|
||||||
|
if (pixel < current_config.pixel_count_min) {
|
||||||
|
pixel = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ClusteringProcessor::ClusteringData ClusteringProcessor::GetClusterProperties(std::vector<u8>& data,
|
||||||
|
std::size_t x,
|
||||||
|
std::size_t y) {
|
||||||
|
std::queue<Common::Point<std::size_t>> search_points{};
|
||||||
|
ClusteringData current_cluster = GetPixelProperties(data, x, y);
|
||||||
|
SetPixel(data, x, y, 0);
|
||||||
|
search_points.emplace(x, y);
|
||||||
|
|
||||||
|
while (!search_points.empty()) {
|
||||||
|
const auto point = search_points.front();
|
||||||
|
search_points.pop();
|
||||||
|
|
||||||
|
// Avoid negative numbers
|
||||||
|
if (point.x == 0 || point.y == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<Common::Point<std::size_t>, 4> new_points{
|
||||||
|
Common::Point<std::size_t>{point.x - 1, point.y},
|
||||||
|
{point.x, point.y - 1},
|
||||||
|
{point.x + 1, point.y},
|
||||||
|
{point.x, point.y + 1},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto new_point : new_points) {
|
||||||
|
if (new_point.x >= width) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (new_point.y >= height) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (GetPixel(data, new_point.x, new_point.y) < current_config.object_intensity_min) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const ClusteringData cluster = GetPixelProperties(data, new_point.x, new_point.y);
|
||||||
|
current_cluster = MergeCluster(current_cluster, cluster);
|
||||||
|
SetPixel(data, new_point.x, new_point.y, 0);
|
||||||
|
search_points.emplace(new_point.x, new_point.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return current_cluster;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClusteringProcessor::ClusteringData ClusteringProcessor::GetPixelProperties(
|
||||||
|
const std::vector<u8>& data, std::size_t x, std::size_t y) const {
|
||||||
|
return {
|
||||||
|
.average_intensity = GetPixel(data, x, y) / 255.0f,
|
||||||
|
.centroid =
|
||||||
|
{
|
||||||
|
.x = static_cast<f32>(x),
|
||||||
|
.y = static_cast<f32>(y),
|
||||||
|
|
||||||
|
},
|
||||||
|
.pixel_count = 1,
|
||||||
|
.bound =
|
||||||
|
{
|
||||||
|
.x = static_cast<s16>(x),
|
||||||
|
.y = static_cast<s16>(y),
|
||||||
|
.width = 1,
|
||||||
|
.height = 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ClusteringProcessor::ClusteringData ClusteringProcessor::MergeCluster(
|
||||||
|
const ClusteringData a, const ClusteringData b) const {
|
||||||
|
const f32 a_pixel_count = static_cast<f32>(a.pixel_count);
|
||||||
|
const f32 b_pixel_count = static_cast<f32>(b.pixel_count);
|
||||||
|
const f32 pixel_count = a_pixel_count + b_pixel_count;
|
||||||
|
const f32 average_intensity =
|
||||||
|
(a.average_intensity * a_pixel_count + b.average_intensity * b_pixel_count) / pixel_count;
|
||||||
|
const Core::IrSensor::IrsCentroid centroid = {
|
||||||
|
.x = (a.centroid.x * a_pixel_count + b.centroid.x * b_pixel_count) / pixel_count,
|
||||||
|
.y = (a.centroid.y * a_pixel_count + b.centroid.y * b_pixel_count) / pixel_count,
|
||||||
|
};
|
||||||
|
s16 bound_start_x = a.bound.x < b.bound.x ? a.bound.x : b.bound.x;
|
||||||
|
s16 bound_start_y = a.bound.y < b.bound.y ? a.bound.y : b.bound.y;
|
||||||
|
s16 a_bound_end_x = a.bound.x + a.bound.width;
|
||||||
|
s16 a_bound_end_y = a.bound.y + a.bound.height;
|
||||||
|
s16 b_bound_end_x = b.bound.x + b.bound.width;
|
||||||
|
s16 b_bound_end_y = b.bound.y + b.bound.height;
|
||||||
|
|
||||||
|
const Core::IrSensor::IrsRect bound = {
|
||||||
|
.x = bound_start_x,
|
||||||
|
.y = bound_start_y,
|
||||||
|
.width = a_bound_end_x > b_bound_end_x ? static_cast<s16>(a_bound_end_x - bound_start_x)
|
||||||
|
: static_cast<s16>(b_bound_end_x - bound_start_x),
|
||||||
|
.height = a_bound_end_y > b_bound_end_y ? static_cast<s16>(a_bound_end_y - bound_start_y)
|
||||||
|
: static_cast<s16>(b_bound_end_y - bound_start_y),
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
.average_intensity = average_intensity,
|
||||||
|
.centroid = centroid,
|
||||||
|
.pixel_count = static_cast<u32>(pixel_count),
|
||||||
|
.bound = bound,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 ClusteringProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const {
|
||||||
|
if ((y * width) + x > data.size()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return data[(y * width) + x];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClusteringProcessor::SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value) {
|
||||||
|
if ((y * width) + x > data.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data[(y * width) + x] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClusteringProcessor::SetDefaultConfig() {
|
||||||
|
using namespace std::literals::chrono_literals;
|
||||||
|
current_config.camera_config.exposure_time = std::chrono::microseconds(200ms).count();
|
||||||
|
current_config.camera_config.gain = 2;
|
||||||
|
current_config.camera_config.is_negative_used = false;
|
||||||
|
current_config.camera_config.light_target = Core::IrSensor::CameraLightTarget::BrightLeds;
|
||||||
|
current_config.window_of_interest = {
|
||||||
|
.x = 0,
|
||||||
|
.y = 0,
|
||||||
|
.width = width,
|
||||||
|
.height = height,
|
||||||
|
};
|
||||||
|
current_config.pixel_count_min = 3;
|
||||||
|
current_config.pixel_count_max = static_cast<u32>(GetDataSize(format));
|
||||||
|
current_config.is_external_light_filter_enabled = true;
|
||||||
|
current_config.object_intensity_min = 150;
|
||||||
|
|
||||||
|
npad_device->SetCameraFormat(format);
|
||||||
|
}
|
||||||
|
|
||||||
void ClusteringProcessor::SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config) {
|
void ClusteringProcessor::SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config) {
|
||||||
current_config.camera_config.exposure_time = config.camera_config.exposure_time;
|
current_config.camera_config.exposure_time = config.camera_config.exposure_time;
|
||||||
current_config.camera_config.gain = config.camera_config.gain;
|
current_config.camera_config.gain = config.camera_config.gain;
|
||||||
current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
|
current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
|
||||||
current_config.camera_config.light_target =
|
current_config.camera_config.light_target =
|
||||||
static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
|
static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
|
||||||
|
current_config.window_of_interest = config.window_of_interest;
|
||||||
current_config.pixel_count_min = config.pixel_count_min;
|
current_config.pixel_count_min = config.pixel_count_min;
|
||||||
current_config.pixel_count_max = config.pixel_count_max;
|
current_config.pixel_count_max = config.pixel_count_max;
|
||||||
current_config.is_external_light_filter_enabled = config.is_external_light_filter_enabled;
|
current_config.is_external_light_filter_enabled = config.is_external_light_filter_enabled;
|
||||||
current_config.object_intensity_min = config.object_intensity_min;
|
current_config.object_intensity_min = config.object_intensity_min;
|
||||||
|
|
||||||
|
LOG_INFO(Service_IRS,
|
||||||
|
"Processor config, exposure_time={}, gain={}, is_negative_used={}, "
|
||||||
|
"light_target={}, window_of_interest=({}, {}, {}, {}), pixel_count_min={}, "
|
||||||
|
"pixel_count_max={}, is_external_light_filter_enabled={}, object_intensity_min={}",
|
||||||
|
current_config.camera_config.exposure_time, current_config.camera_config.gain,
|
||||||
|
current_config.camera_config.is_negative_used,
|
||||||
|
current_config.camera_config.light_target, current_config.window_of_interest.x,
|
||||||
|
current_config.window_of_interest.y, current_config.window_of_interest.width,
|
||||||
|
current_config.window_of_interest.height, current_config.pixel_count_min,
|
||||||
|
current_config.pixel_count_max, current_config.is_external_light_filter_enabled,
|
||||||
|
current_config.object_intensity_min);
|
||||||
|
|
||||||
|
npad_device->SetCameraFormat(format);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Service::IRS
|
} // namespace Service::IRS
|
||||||
|
@ -5,12 +5,19 @@
|
|||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/hid/irs_types.h"
|
#include "core/hid/irs_types.h"
|
||||||
|
#include "core/hle/service/hid/irs_ring_lifo.h"
|
||||||
#include "core/hle/service/hid/irsensor/processor_base.h"
|
#include "core/hle/service/hid/irsensor/processor_base.h"
|
||||||
|
|
||||||
|
namespace Core::HID {
|
||||||
|
class EmulatedController;
|
||||||
|
} // namespace Core::HID
|
||||||
|
|
||||||
namespace Service::IRS {
|
namespace Service::IRS {
|
||||||
class ClusteringProcessor final : public ProcessorBase {
|
class ClusteringProcessor final : public ProcessorBase {
|
||||||
public:
|
public:
|
||||||
explicit ClusteringProcessor(Core::IrSensor::DeviceFormat& device_format);
|
explicit ClusteringProcessor(Core::HID::HIDCore& hid_core_,
|
||||||
|
Core::IrSensor::DeviceFormat& device_format,
|
||||||
|
std::size_t npad_index);
|
||||||
~ClusteringProcessor() override;
|
~ClusteringProcessor() override;
|
||||||
|
|
||||||
// Called when the processor is initialized
|
// Called when the processor is initialized
|
||||||
@ -26,6 +33,10 @@ public:
|
|||||||
void SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config);
|
void SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static constexpr auto format = Core::IrSensor::ImageTransferProcessorFormat::Size320x240;
|
||||||
|
static constexpr std::size_t width = 320;
|
||||||
|
static constexpr std::size_t height = 240;
|
||||||
|
|
||||||
// This is nn::irsensor::ClusteringProcessorConfig
|
// This is nn::irsensor::ClusteringProcessorConfig
|
||||||
struct ClusteringProcessorConfig {
|
struct ClusteringProcessorConfig {
|
||||||
Core::IrSensor::CameraConfig camera_config;
|
Core::IrSensor::CameraConfig camera_config;
|
||||||
@ -68,7 +79,32 @@ private:
|
|||||||
static_assert(sizeof(ClusteringProcessorState) == 0x198,
|
static_assert(sizeof(ClusteringProcessorState) == 0x198,
|
||||||
"ClusteringProcessorState is an invalid size");
|
"ClusteringProcessorState is an invalid size");
|
||||||
|
|
||||||
|
struct ClusteringSharedMemory {
|
||||||
|
Service::IRS::Lifo<ClusteringProcessorState, 6> clustering_lifo;
|
||||||
|
static_assert(sizeof(clustering_lifo) == 0x9A0, "clustering_lifo is an invalid size");
|
||||||
|
INSERT_PADDING_WORDS(0x11F);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(ClusteringSharedMemory) == 0xE20,
|
||||||
|
"ClusteringSharedMemory is an invalid size");
|
||||||
|
|
||||||
|
void OnControllerUpdate(Core::HID::ControllerTriggerType type);
|
||||||
|
void RemoveLowIntensityData(std::vector<u8>& data);
|
||||||
|
ClusteringData GetClusterProperties(std::vector<u8>& data, std::size_t x, std::size_t y);
|
||||||
|
ClusteringData GetPixelProperties(const std::vector<u8>& data, std::size_t x,
|
||||||
|
std::size_t y) const;
|
||||||
|
ClusteringData MergeCluster(const ClusteringData a, const ClusteringData b) const;
|
||||||
|
u8 GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const;
|
||||||
|
void SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value);
|
||||||
|
|
||||||
|
// Sets config parameters of the camera
|
||||||
|
void SetDefaultConfig();
|
||||||
|
|
||||||
|
ClusteringSharedMemory* shared_memory = nullptr;
|
||||||
|
ClusteringProcessorState next_state{};
|
||||||
|
|
||||||
ClusteringProcessorConfig current_config{};
|
ClusteringProcessorConfig current_config{};
|
||||||
Core::IrSensor::DeviceFormat& device;
|
Core::IrSensor::DeviceFormat& device;
|
||||||
|
Core::HID::EmulatedController* npad_device;
|
||||||
|
int callback_key{};
|
||||||
};
|
};
|
||||||
} // namespace Service::IRS
|
} // namespace Service::IRS
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
add_library(input_common STATIC
|
add_library(input_common STATIC
|
||||||
|
drivers/camera.cpp
|
||||||
|
drivers/camera.h
|
||||||
drivers/gc_adapter.cpp
|
drivers/gc_adapter.cpp
|
||||||
drivers/gc_adapter.h
|
drivers/gc_adapter.h
|
||||||
drivers/keyboard.cpp
|
drivers/keyboard.cpp
|
||||||
|
@ -90,6 +90,18 @@ void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const B
|
|||||||
TriggerOnMotionChange(identifier, motion, value);
|
TriggerOnMotionChange(identifier, motion, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InputEngine::SetCamera(const PadIdentifier& identifier,
|
||||||
|
const Common::Input::CameraStatus& value) {
|
||||||
|
{
|
||||||
|
std::scoped_lock lock{mutex};
|
||||||
|
ControllerData& controller = controller_list.at(identifier);
|
||||||
|
if (!configuring) {
|
||||||
|
controller.camera = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TriggerOnCameraChange(identifier, value);
|
||||||
|
}
|
||||||
|
|
||||||
bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
|
bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
|
||||||
std::scoped_lock lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
const auto controller_iter = controller_list.find(identifier);
|
const auto controller_iter = controller_list.find(identifier);
|
||||||
@ -165,6 +177,18 @@ BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion)
|
|||||||
return controller.motions.at(motion);
|
return controller.motions.at(motion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Common::Input::CameraStatus InputEngine::GetCamera(const PadIdentifier& identifier) const {
|
||||||
|
std::scoped_lock lock{mutex};
|
||||||
|
const auto controller_iter = controller_list.find(identifier);
|
||||||
|
if (controller_iter == controller_list.cend()) {
|
||||||
|
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
||||||
|
identifier.pad, identifier.port);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const ControllerData& controller = controller_iter->second;
|
||||||
|
return controller.camera;
|
||||||
|
}
|
||||||
|
|
||||||
void InputEngine::ResetButtonState() {
|
void InputEngine::ResetButtonState() {
|
||||||
for (const auto& controller : controller_list) {
|
for (const auto& controller : controller_list) {
|
||||||
for (const auto& button : controller.second.buttons) {
|
for (const auto& button : controller.second.buttons) {
|
||||||
@ -317,6 +341,20 @@ void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int mot
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InputEngine::TriggerOnCameraChange(const PadIdentifier& identifier,
|
||||||
|
[[maybe_unused]] const Common::Input::CameraStatus& value) {
|
||||||
|
std::scoped_lock lock{mutex_callback};
|
||||||
|
for (const auto& poller_pair : callback_list) {
|
||||||
|
const InputIdentifier& poller = poller_pair.second;
|
||||||
|
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Camera, 0)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (poller.callback.on_change) {
|
||||||
|
poller.callback.on_change();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool InputEngine::IsInputIdentifierEqual(const InputIdentifier& input_identifier,
|
bool InputEngine::IsInputIdentifierEqual(const InputIdentifier& input_identifier,
|
||||||
const PadIdentifier& identifier, EngineInputType type,
|
const PadIdentifier& identifier, EngineInputType type,
|
||||||
int index) const {
|
int index) const {
|
||||||
|
@ -36,11 +36,12 @@ struct BasicMotion {
|
|||||||
// Types of input that are stored in the engine
|
// Types of input that are stored in the engine
|
||||||
enum class EngineInputType {
|
enum class EngineInputType {
|
||||||
None,
|
None,
|
||||||
Button,
|
|
||||||
HatButton,
|
|
||||||
Analog,
|
Analog,
|
||||||
Motion,
|
|
||||||
Battery,
|
Battery,
|
||||||
|
Button,
|
||||||
|
Camera,
|
||||||
|
HatButton,
|
||||||
|
Motion,
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace std {
|
namespace std {
|
||||||
@ -115,10 +116,17 @@ public:
|
|||||||
// Sets polling mode to a controller
|
// Sets polling mode to a controller
|
||||||
virtual Common::Input::PollingError SetPollingMode(
|
virtual Common::Input::PollingError SetPollingMode(
|
||||||
[[maybe_unused]] const PadIdentifier& identifier,
|
[[maybe_unused]] const PadIdentifier& identifier,
|
||||||
[[maybe_unused]] const Common::Input::PollingMode vibration) {
|
[[maybe_unused]] const Common::Input::PollingMode polling_mode) {
|
||||||
return Common::Input::PollingError::NotSupported;
|
return Common::Input::PollingError::NotSupported;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sets camera format to a controller
|
||||||
|
virtual Common::Input::CameraError SetCameraFormat(
|
||||||
|
[[maybe_unused]] const PadIdentifier& identifier,
|
||||||
|
[[maybe_unused]] Common::Input::CameraFormat camera_format) {
|
||||||
|
return Common::Input::CameraError::NotSupported;
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the engine name
|
// Returns the engine name
|
||||||
[[nodiscard]] const std::string& GetEngineName() const;
|
[[nodiscard]] const std::string& GetEngineName() const;
|
||||||
|
|
||||||
@ -174,6 +182,7 @@ public:
|
|||||||
f32 GetAxis(const PadIdentifier& identifier, int axis) const;
|
f32 GetAxis(const PadIdentifier& identifier, int axis) const;
|
||||||
Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const;
|
Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const;
|
||||||
BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const;
|
BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const;
|
||||||
|
Common::Input::CameraStatus GetCamera(const PadIdentifier& identifier) const;
|
||||||
|
|
||||||
int SetCallback(InputIdentifier input_identifier);
|
int SetCallback(InputIdentifier input_identifier);
|
||||||
void SetMappingCallback(MappingCallback callback);
|
void SetMappingCallback(MappingCallback callback);
|
||||||
@ -185,6 +194,7 @@ protected:
|
|||||||
void SetAxis(const PadIdentifier& identifier, int axis, f32 value);
|
void SetAxis(const PadIdentifier& identifier, int axis, f32 value);
|
||||||
void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
|
void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
|
||||||
void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value);
|
void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value);
|
||||||
|
void SetCamera(const PadIdentifier& identifier, const Common::Input::CameraStatus& value);
|
||||||
|
|
||||||
virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const {
|
virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const {
|
||||||
return "Unknown";
|
return "Unknown";
|
||||||
@ -197,6 +207,7 @@ private:
|
|||||||
std::unordered_map<int, float> axes;
|
std::unordered_map<int, float> axes;
|
||||||
std::unordered_map<int, BasicMotion> motions;
|
std::unordered_map<int, BasicMotion> motions;
|
||||||
Common::Input::BatteryLevel battery{};
|
Common::Input::BatteryLevel battery{};
|
||||||
|
Common::Input::CameraStatus camera{};
|
||||||
};
|
};
|
||||||
|
|
||||||
void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value);
|
void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value);
|
||||||
@ -205,6 +216,8 @@ private:
|
|||||||
void TriggerOnBatteryChange(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
|
void TriggerOnBatteryChange(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
|
||||||
void TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
|
void TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
|
||||||
const BasicMotion& value);
|
const BasicMotion& value);
|
||||||
|
void TriggerOnCameraChange(const PadIdentifier& identifier,
|
||||||
|
const Common::Input::CameraStatus& value);
|
||||||
|
|
||||||
bool IsInputIdentifierEqual(const InputIdentifier& input_identifier,
|
bool IsInputIdentifierEqual(const InputIdentifier& input_identifier,
|
||||||
const PadIdentifier& identifier, EngineInputType type,
|
const PadIdentifier& identifier, EngineInputType type,
|
||||||
|
@ -664,6 +664,47 @@ private:
|
|||||||
InputEngine* input_engine;
|
InputEngine* input_engine;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class InputFromCamera final : public Common::Input::InputDevice {
|
||||||
|
public:
|
||||||
|
explicit InputFromCamera(PadIdentifier identifier_, InputEngine* input_engine_)
|
||||||
|
: identifier(identifier_), input_engine(input_engine_) {
|
||||||
|
UpdateCallback engine_callback{[this]() { OnChange(); }};
|
||||||
|
const InputIdentifier input_identifier{
|
||||||
|
.identifier = identifier,
|
||||||
|
.type = EngineInputType::Camera,
|
||||||
|
.index = 0,
|
||||||
|
.callback = engine_callback,
|
||||||
|
};
|
||||||
|
callback_key = input_engine->SetCallback(input_identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
~InputFromCamera() override {
|
||||||
|
input_engine->DeleteCallback(callback_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::Input::CameraStatus GetStatus() const {
|
||||||
|
return input_engine->GetCamera(identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ForceUpdate() override {
|
||||||
|
OnChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnChange() {
|
||||||
|
const Common::Input::CallbackStatus status{
|
||||||
|
.type = Common::Input::InputType::IrSensor,
|
||||||
|
.camera_status = GetStatus(),
|
||||||
|
};
|
||||||
|
|
||||||
|
TriggerOnChange(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const PadIdentifier identifier;
|
||||||
|
int callback_key;
|
||||||
|
InputEngine* input_engine;
|
||||||
|
};
|
||||||
|
|
||||||
class OutputFromIdentifier final : public Common::Input::OutputDevice {
|
class OutputFromIdentifier final : public Common::Input::OutputDevice {
|
||||||
public:
|
public:
|
||||||
explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_)
|
explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_)
|
||||||
@ -682,6 +723,10 @@ public:
|
|||||||
return input_engine->SetPollingMode(identifier, polling_mode);
|
return input_engine->SetPollingMode(identifier, polling_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Common::Input::CameraError SetCameraFormat(Common::Input::CameraFormat camera_format) override {
|
||||||
|
return input_engine->SetCameraFormat(identifier, camera_format);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const PadIdentifier identifier;
|
const PadIdentifier identifier;
|
||||||
InputEngine* input_engine;
|
InputEngine* input_engine;
|
||||||
@ -920,6 +965,18 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateMotionDevice(
|
|||||||
properties_y, properties_z, input_engine.get());
|
properties_y, properties_z, input_engine.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateCameraDevice(
|
||||||
|
const Common::ParamPackage& params) {
|
||||||
|
const PadIdentifier identifier = {
|
||||||
|
.guid = Common::UUID{params.Get("guid", "")},
|
||||||
|
.port = static_cast<std::size_t>(params.Get("port", 0)),
|
||||||
|
.pad = static_cast<std::size_t>(params.Get("pad", 0)),
|
||||||
|
};
|
||||||
|
|
||||||
|
input_engine->PreSetController(identifier);
|
||||||
|
return std::make_unique<InputFromCamera>(identifier, input_engine.get());
|
||||||
|
}
|
||||||
|
|
||||||
InputFactory::InputFactory(std::shared_ptr<InputEngine> input_engine_)
|
InputFactory::InputFactory(std::shared_ptr<InputEngine> input_engine_)
|
||||||
: input_engine(std::move(input_engine_)) {}
|
: input_engine(std::move(input_engine_)) {}
|
||||||
|
|
||||||
@ -928,6 +985,9 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::Create(
|
|||||||
if (params.Has("battery")) {
|
if (params.Has("battery")) {
|
||||||
return CreateBatteryDevice(params);
|
return CreateBatteryDevice(params);
|
||||||
}
|
}
|
||||||
|
if (params.Has("camera")) {
|
||||||
|
return CreateCameraDevice(params);
|
||||||
|
}
|
||||||
if (params.Has("button") && params.Has("axis")) {
|
if (params.Has("button") && params.Has("axis")) {
|
||||||
return CreateTriggerDevice(params);
|
return CreateTriggerDevice(params);
|
||||||
}
|
}
|
||||||
|
@ -211,6 +211,17 @@ private:
|
|||||||
*/
|
*/
|
||||||
std::unique_ptr<Common::Input::InputDevice> CreateMotionDevice(Common::ParamPackage params);
|
std::unique_ptr<Common::Input::InputDevice> CreateMotionDevice(Common::ParamPackage params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a camera device from the parameters given.
|
||||||
|
* @param params contains parameters for creating the device:
|
||||||
|
* - "guid": text string for identifying controllers
|
||||||
|
* - "port": port of the connected device
|
||||||
|
* - "pad": slot of the connected controller
|
||||||
|
* @returns a unique input device with the parameters specified
|
||||||
|
*/
|
||||||
|
std::unique_ptr<Common::Input::InputDevice> CreateCameraDevice(
|
||||||
|
const Common::ParamPackage& params);
|
||||||
|
|
||||||
std::shared_ptr<InputEngine> input_engine;
|
std::shared_ptr<InputEngine> input_engine;
|
||||||
};
|
};
|
||||||
} // namespace InputCommon
|
} // namespace InputCommon
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include "common/input.h"
|
#include "common/input.h"
|
||||||
#include "common/param_package.h"
|
#include "common/param_package.h"
|
||||||
|
#include "input_common/drivers/camera.h"
|
||||||
#include "input_common/drivers/gc_adapter.h"
|
#include "input_common/drivers/gc_adapter.h"
|
||||||
#include "input_common/drivers/keyboard.h"
|
#include "input_common/drivers/keyboard.h"
|
||||||
#include "input_common/drivers/mouse.h"
|
#include "input_common/drivers/mouse.h"
|
||||||
@ -78,6 +79,15 @@ struct InputSubsystem::Impl {
|
|||||||
Common::Input::RegisterFactory<Common::Input::OutputDevice>(tas_input->GetEngineName(),
|
Common::Input::RegisterFactory<Common::Input::OutputDevice>(tas_input->GetEngineName(),
|
||||||
tas_output_factory);
|
tas_output_factory);
|
||||||
|
|
||||||
|
camera = std::make_shared<Camera>("camera");
|
||||||
|
camera->SetMappingCallback(mapping_callback);
|
||||||
|
camera_input_factory = std::make_shared<InputFactory>(camera);
|
||||||
|
camera_output_factory = std::make_shared<OutputFactory>(camera);
|
||||||
|
Common::Input::RegisterFactory<Common::Input::InputDevice>(camera->GetEngineName(),
|
||||||
|
camera_input_factory);
|
||||||
|
Common::Input::RegisterFactory<Common::Input::OutputDevice>(camera->GetEngineName(),
|
||||||
|
camera_output_factory);
|
||||||
|
|
||||||
#ifdef HAVE_SDL2
|
#ifdef HAVE_SDL2
|
||||||
sdl = std::make_shared<SDLDriver>("sdl");
|
sdl = std::make_shared<SDLDriver>("sdl");
|
||||||
sdl->SetMappingCallback(mapping_callback);
|
sdl->SetMappingCallback(mapping_callback);
|
||||||
@ -317,6 +327,7 @@ struct InputSubsystem::Impl {
|
|||||||
std::shared_ptr<TouchScreen> touch_screen;
|
std::shared_ptr<TouchScreen> touch_screen;
|
||||||
std::shared_ptr<TasInput::Tas> tas_input;
|
std::shared_ptr<TasInput::Tas> tas_input;
|
||||||
std::shared_ptr<CemuhookUDP::UDPClient> udp_client;
|
std::shared_ptr<CemuhookUDP::UDPClient> udp_client;
|
||||||
|
std::shared_ptr<Camera> camera;
|
||||||
|
|
||||||
std::shared_ptr<InputFactory> keyboard_factory;
|
std::shared_ptr<InputFactory> keyboard_factory;
|
||||||
std::shared_ptr<InputFactory> mouse_factory;
|
std::shared_ptr<InputFactory> mouse_factory;
|
||||||
@ -324,12 +335,14 @@ struct InputSubsystem::Impl {
|
|||||||
std::shared_ptr<InputFactory> touch_screen_factory;
|
std::shared_ptr<InputFactory> touch_screen_factory;
|
||||||
std::shared_ptr<InputFactory> udp_client_input_factory;
|
std::shared_ptr<InputFactory> udp_client_input_factory;
|
||||||
std::shared_ptr<InputFactory> tas_input_factory;
|
std::shared_ptr<InputFactory> tas_input_factory;
|
||||||
|
std::shared_ptr<InputFactory> camera_input_factory;
|
||||||
|
|
||||||
std::shared_ptr<OutputFactory> keyboard_output_factory;
|
std::shared_ptr<OutputFactory> keyboard_output_factory;
|
||||||
std::shared_ptr<OutputFactory> mouse_output_factory;
|
std::shared_ptr<OutputFactory> mouse_output_factory;
|
||||||
std::shared_ptr<OutputFactory> gcadapter_output_factory;
|
std::shared_ptr<OutputFactory> gcadapter_output_factory;
|
||||||
std::shared_ptr<OutputFactory> udp_client_output_factory;
|
std::shared_ptr<OutputFactory> udp_client_output_factory;
|
||||||
std::shared_ptr<OutputFactory> tas_output_factory;
|
std::shared_ptr<OutputFactory> tas_output_factory;
|
||||||
|
std::shared_ptr<OutputFactory> camera_output_factory;
|
||||||
|
|
||||||
#ifdef HAVE_SDL2
|
#ifdef HAVE_SDL2
|
||||||
std::shared_ptr<SDLDriver> sdl;
|
std::shared_ptr<SDLDriver> sdl;
|
||||||
@ -382,6 +395,14 @@ const TasInput::Tas* InputSubsystem::GetTas() const {
|
|||||||
return impl->tas_input.get();
|
return impl->tas_input.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Camera* InputSubsystem::GetCamera() {
|
||||||
|
return impl->camera.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Camera* InputSubsystem::GetCamera() const {
|
||||||
|
return impl->camera.get();
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
|
std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
|
||||||
return impl->GetInputDevices();
|
return impl->GetInputDevices();
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ enum Values : int;
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace InputCommon {
|
namespace InputCommon {
|
||||||
|
class Camera;
|
||||||
class Keyboard;
|
class Keyboard;
|
||||||
class Mouse;
|
class Mouse;
|
||||||
class TouchScreen;
|
class TouchScreen;
|
||||||
@ -95,6 +96,12 @@ public:
|
|||||||
/// Retrieves the underlying tas input device.
|
/// Retrieves the underlying tas input device.
|
||||||
[[nodiscard]] const TasInput::Tas* GetTas() const;
|
[[nodiscard]] const TasInput::Tas* GetTas() const;
|
||||||
|
|
||||||
|
/// Retrieves the underlying camera input device.
|
||||||
|
[[nodiscard]] Camera* GetCamera();
|
||||||
|
|
||||||
|
/// Retrieves the underlying camera input device.
|
||||||
|
[[nodiscard]] const Camera* GetCamera() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all available input devices that this Factory can create a new device with.
|
* Returns all available input devices that this Factory can create a new device with.
|
||||||
* Each returned ParamPackage should have a `display` field used for display, a `engine` field
|
* Each returned ParamPackage should have a `display` field used for display, a `engine` field
|
||||||
|
@ -41,6 +41,9 @@ add_executable(yuzu
|
|||||||
configuration/configure_audio.cpp
|
configuration/configure_audio.cpp
|
||||||
configuration/configure_audio.h
|
configuration/configure_audio.h
|
||||||
configuration/configure_audio.ui
|
configuration/configure_audio.ui
|
||||||
|
configuration/configure_camera.cpp
|
||||||
|
configuration/configure_camera.h
|
||||||
|
configuration/configure_camera.ui
|
||||||
configuration/configure_cpu.cpp
|
configuration/configure_cpu.cpp
|
||||||
configuration/configure_cpu.h
|
configuration/configure_cpu.h
|
||||||
configuration/configure_cpu.ui
|
configuration/configure_cpu.ui
|
||||||
@ -254,7 +257,7 @@ endif()
|
|||||||
create_target_directory_groups(yuzu)
|
create_target_directory_groups(yuzu)
|
||||||
|
|
||||||
target_link_libraries(yuzu PRIVATE common core input_common video_core)
|
target_link_libraries(yuzu PRIVATE common core input_common video_core)
|
||||||
target_link_libraries(yuzu PRIVATE Boost::boost glad Qt::Widgets)
|
target_link_libraries(yuzu PRIVATE Boost::boost glad Qt::Widgets Qt::Multimedia)
|
||||||
target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
||||||
|
|
||||||
target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include)
|
target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include)
|
||||||
|
@ -213,9 +213,9 @@ QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog(
|
|||||||
ui->button_ok_num,
|
ui->button_ok_num,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
nullptr,
|
ui->button_left_optional_num,
|
||||||
ui->button_0_num,
|
ui->button_0_num,
|
||||||
nullptr,
|
ui->button_right_optional_num,
|
||||||
ui->button_ok_num,
|
ui->button_ok_num,
|
||||||
},
|
},
|
||||||
}};
|
}};
|
||||||
@ -330,7 +330,9 @@ QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog(
|
|||||||
ui->button_7_num,
|
ui->button_7_num,
|
||||||
ui->button_8_num,
|
ui->button_8_num,
|
||||||
ui->button_9_num,
|
ui->button_9_num,
|
||||||
|
ui->button_left_optional_num,
|
||||||
ui->button_0_num,
|
ui->button_0_num,
|
||||||
|
ui->button_right_optional_num,
|
||||||
};
|
};
|
||||||
|
|
||||||
SetupMouseHover();
|
SetupMouseHover();
|
||||||
@ -342,6 +344,9 @@ QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog(
|
|||||||
ui->label_header->setText(QString::fromStdU16String(initialize_parameters.header_text));
|
ui->label_header->setText(QString::fromStdU16String(initialize_parameters.header_text));
|
||||||
ui->label_sub->setText(QString::fromStdU16String(initialize_parameters.sub_text));
|
ui->label_sub->setText(QString::fromStdU16String(initialize_parameters.sub_text));
|
||||||
|
|
||||||
|
ui->button_left_optional_num->setText(QChar{initialize_parameters.left_optional_symbol_key});
|
||||||
|
ui->button_right_optional_num->setText(QChar{initialize_parameters.right_optional_symbol_key});
|
||||||
|
|
||||||
current_text = initialize_parameters.initial_text;
|
current_text = initialize_parameters.initial_text;
|
||||||
cursor_position = initialize_parameters.initial_cursor_position;
|
cursor_position = initialize_parameters.initial_cursor_position;
|
||||||
|
|
||||||
@ -932,6 +937,15 @@ void QtSoftwareKeyboardDialog::DisableKeyboardButtons() {
|
|||||||
button->setEnabled(true);
|
button->setEnabled(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto enable_left_optional = initialize_parameters.left_optional_symbol_key != '\0';
|
||||||
|
const auto enable_right_optional = initialize_parameters.right_optional_symbol_key != '\0';
|
||||||
|
|
||||||
|
ui->button_left_optional_num->setEnabled(enable_left_optional);
|
||||||
|
ui->button_left_optional_num->setVisible(enable_left_optional);
|
||||||
|
|
||||||
|
ui->button_right_optional_num->setEnabled(enable_right_optional);
|
||||||
|
ui->button_right_optional_num->setVisible(enable_right_optional);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1019,7 +1033,10 @@ bool QtSoftwareKeyboardDialog::ValidateInputText(const QString& input_text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (bottom_osk_index == BottomOSKIndex::NumberPad &&
|
if (bottom_osk_index == BottomOSKIndex::NumberPad &&
|
||||||
std::any_of(input_text.begin(), input_text.end(), [](QChar c) { return !c.isDigit(); })) {
|
std::any_of(input_text.begin(), input_text.end(), [this](QChar c) {
|
||||||
|
return !c.isDigit() && c != QChar{initialize_parameters.left_optional_symbol_key} &&
|
||||||
|
c != QChar{initialize_parameters.right_optional_symbol_key};
|
||||||
|
})) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1384,6 +1401,10 @@ void QtSoftwareKeyboardDialog::MoveButtonDirection(Direction direction) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Store the initial row and column.
|
||||||
|
const auto initial_row = row;
|
||||||
|
const auto initial_column = column;
|
||||||
|
|
||||||
switch (bottom_osk_index) {
|
switch (bottom_osk_index) {
|
||||||
case BottomOSKIndex::LowerCase:
|
case BottomOSKIndex::LowerCase:
|
||||||
case BottomOSKIndex::UpperCase: {
|
case BottomOSKIndex::UpperCase: {
|
||||||
@ -1394,6 +1415,11 @@ void QtSoftwareKeyboardDialog::MoveButtonDirection(Direction direction) {
|
|||||||
auto* curr_button = keyboard_buttons[index][row][column];
|
auto* curr_button = keyboard_buttons[index][row][column];
|
||||||
|
|
||||||
while (!curr_button || !curr_button->isEnabled() || curr_button == prev_button) {
|
while (!curr_button || !curr_button->isEnabled() || curr_button == prev_button) {
|
||||||
|
// If we returned back to where we started from, break the loop.
|
||||||
|
if (row == initial_row && column == initial_column) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
move_direction(NUM_ROWS_NORMAL, NUM_COLUMNS_NORMAL);
|
move_direction(NUM_ROWS_NORMAL, NUM_COLUMNS_NORMAL);
|
||||||
curr_button = keyboard_buttons[index][row][column];
|
curr_button = keyboard_buttons[index][row][column];
|
||||||
}
|
}
|
||||||
@ -1408,6 +1434,11 @@ void QtSoftwareKeyboardDialog::MoveButtonDirection(Direction direction) {
|
|||||||
auto* curr_button = numberpad_buttons[row][column];
|
auto* curr_button = numberpad_buttons[row][column];
|
||||||
|
|
||||||
while (!curr_button || !curr_button->isEnabled() || curr_button == prev_button) {
|
while (!curr_button || !curr_button->isEnabled() || curr_button == prev_button) {
|
||||||
|
// If we returned back to where we started from, break the loop.
|
||||||
|
if (row == initial_row && column == initial_column) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
move_direction(NUM_ROWS_NUMPAD, NUM_COLUMNS_NUMPAD);
|
move_direction(NUM_ROWS_NUMPAD, NUM_COLUMNS_NUMPAD);
|
||||||
curr_button = numberpad_buttons[row][column];
|
curr_button = numberpad_buttons[row][column];
|
||||||
}
|
}
|
||||||
|
@ -211,7 +211,7 @@ private:
|
|||||||
std::array<std::array<QPushButton*, NUM_COLUMNS_NUMPAD>, NUM_ROWS_NUMPAD> numberpad_buttons;
|
std::array<std::array<QPushButton*, NUM_COLUMNS_NUMPAD>, NUM_ROWS_NUMPAD> numberpad_buttons;
|
||||||
|
|
||||||
// Contains a set of all buttons used in keyboard_buttons and numberpad_buttons.
|
// Contains a set of all buttons used in keyboard_buttons and numberpad_buttons.
|
||||||
std::array<QPushButton*, 110> all_buttons;
|
std::array<QPushButton*, 112> all_buttons;
|
||||||
|
|
||||||
std::size_t row{0};
|
std::size_t row{0};
|
||||||
std::size_t column{0};
|
std::size_t column{0};
|
||||||
|
@ -3298,6 +3298,24 @@ p, li { white-space: pre-wrap; }
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="4" column="2">
|
||||||
|
<widget class="QPushButton" name="button_left_optional_num">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>1</horstretch>
|
||||||
|
<verstretch>1</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>28</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true"></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item row="4" column="3">
|
<item row="4" column="3">
|
||||||
<widget class="QPushButton" name="button_0_num">
|
<widget class="QPushButton" name="button_0_num">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
@ -3316,6 +3334,24 @@ p, li { white-space: pre-wrap; }
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="4" column="4">
|
||||||
|
<widget class="QPushButton" name="button_right_optional_num">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>1</horstretch>
|
||||||
|
<verstretch>1</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>28</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true"></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item row="1" column="4">
|
<item row="1" column="4">
|
||||||
<widget class="QPushButton" name="button_3_num">
|
<widget class="QPushButton" name="button_3_num">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
@ -3494,7 +3530,9 @@ p, li { white-space: pre-wrap; }
|
|||||||
<tabstop>button_7_num</tabstop>
|
<tabstop>button_7_num</tabstop>
|
||||||
<tabstop>button_8_num</tabstop>
|
<tabstop>button_8_num</tabstop>
|
||||||
<tabstop>button_9_num</tabstop>
|
<tabstop>button_9_num</tabstop>
|
||||||
|
<tabstop>button_left_optional_num</tabstop>
|
||||||
<tabstop>button_0_num</tabstop>
|
<tabstop>button_0_num</tabstop>
|
||||||
|
<tabstop>button_right_optional_num</tabstop>
|
||||||
</tabstops>
|
</tabstops>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="../../../dist/icons/overlay/overlay.qrc"/>
|
<include location="../../../dist/icons/overlay/overlay.qrc"/>
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
#include <glad/glad.h>
|
#include <glad/glad.h>
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
#include <QCameraImageCapture>
|
||||||
|
#include <QCameraInfo>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
@ -31,6 +33,7 @@
|
|||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/cpu_manager.h"
|
#include "core/cpu_manager.h"
|
||||||
#include "core/frontend/framebuffer_layout.h"
|
#include "core/frontend/framebuffer_layout.h"
|
||||||
|
#include "input_common/drivers/camera.h"
|
||||||
#include "input_common/drivers/keyboard.h"
|
#include "input_common/drivers/keyboard.h"
|
||||||
#include "input_common/drivers/mouse.h"
|
#include "input_common/drivers/mouse.h"
|
||||||
#include "input_common/drivers/tas_input.h"
|
#include "input_common/drivers/tas_input.h"
|
||||||
@ -801,6 +804,86 @@ void GRenderWindow::TouchEndEvent() {
|
|||||||
input_subsystem->GetTouchScreen()->ReleaseAllTouch();
|
input_subsystem->GetTouchScreen()->ReleaseAllTouch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GRenderWindow::InitializeCamera() {
|
||||||
|
constexpr auto camera_update_ms = std::chrono::milliseconds{50}; // (50ms, 20Hz)
|
||||||
|
if (!Settings::values.enable_ir_sensor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool camera_found = false;
|
||||||
|
const QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
|
||||||
|
for (const QCameraInfo& cameraInfo : cameras) {
|
||||||
|
if (Settings::values.ir_sensor_device.GetValue() == cameraInfo.deviceName().toStdString() ||
|
||||||
|
Settings::values.ir_sensor_device.GetValue() == "Auto") {
|
||||||
|
camera = std::make_unique<QCamera>(cameraInfo);
|
||||||
|
camera_found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!camera_found) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
camera_capture = std::make_unique<QCameraImageCapture>(camera.get());
|
||||||
|
connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this,
|
||||||
|
&GRenderWindow::OnCameraCapture);
|
||||||
|
camera->unload();
|
||||||
|
camera->setCaptureMode(QCamera::CaptureViewfinder);
|
||||||
|
camera->load();
|
||||||
|
camera->start();
|
||||||
|
|
||||||
|
pending_camera_snapshots = 0;
|
||||||
|
is_virtual_camera = false;
|
||||||
|
|
||||||
|
camera_timer = std::make_unique<QTimer>();
|
||||||
|
connect(camera_timer.get(), &QTimer::timeout, [this] { RequestCameraCapture(); });
|
||||||
|
// This timer should be dependent of camera resolution 5ms for every 100 pixels
|
||||||
|
camera_timer->start(camera_update_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GRenderWindow::FinalizeCamera() {
|
||||||
|
if (camera_timer) {
|
||||||
|
camera_timer->stop();
|
||||||
|
}
|
||||||
|
if (camera) {
|
||||||
|
camera->unload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GRenderWindow::RequestCameraCapture() {
|
||||||
|
if (!Settings::values.enable_ir_sensor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the camera doesn't capture, test for virtual cameras
|
||||||
|
if (pending_camera_snapshots > 5) {
|
||||||
|
is_virtual_camera = true;
|
||||||
|
}
|
||||||
|
// Virtual cameras like obs need to reset the camera every capture
|
||||||
|
if (is_virtual_camera) {
|
||||||
|
camera->stop();
|
||||||
|
camera->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
pending_camera_snapshots++;
|
||||||
|
camera_capture->capture();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GRenderWindow::OnCameraCapture(int requestId, const QImage& img) {
|
||||||
|
constexpr std::size_t camera_width = 320;
|
||||||
|
constexpr std::size_t camera_height = 240;
|
||||||
|
const auto converted =
|
||||||
|
img.scaled(camera_width, camera_height, Qt::AspectRatioMode::IgnoreAspectRatio,
|
||||||
|
Qt::TransformationMode::SmoothTransformation)
|
||||||
|
.mirrored(false, true);
|
||||||
|
std::vector<u32> camera_data{};
|
||||||
|
camera_data.resize(camera_width * camera_height);
|
||||||
|
std::memcpy(camera_data.data(), converted.bits(), camera_width * camera_height * sizeof(u32));
|
||||||
|
input_subsystem->GetCamera()->SetCameraData(camera_width, camera_height, camera_data);
|
||||||
|
pending_camera_snapshots = 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool GRenderWindow::event(QEvent* event) {
|
bool GRenderWindow::event(QEvent* event) {
|
||||||
if (event->type() == QEvent::TouchBegin) {
|
if (event->type() == QEvent::TouchBegin) {
|
||||||
TouchBeginEvent(static_cast<QTouchEvent*>(event));
|
TouchBeginEvent(static_cast<QTouchEvent*>(event));
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
class GRenderWindow;
|
class GRenderWindow;
|
||||||
class GMainWindow;
|
class GMainWindow;
|
||||||
|
class QCamera;
|
||||||
|
class QCameraImageCapture;
|
||||||
class QKeyEvent;
|
class QKeyEvent;
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
@ -164,6 +166,9 @@ public:
|
|||||||
void mouseReleaseEvent(QMouseEvent* event) override;
|
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||||
void wheelEvent(QWheelEvent* event) override;
|
void wheelEvent(QWheelEvent* event) override;
|
||||||
|
|
||||||
|
void InitializeCamera();
|
||||||
|
void FinalizeCamera();
|
||||||
|
|
||||||
bool event(QEvent* event) override;
|
bool event(QEvent* event) override;
|
||||||
|
|
||||||
void focusOutEvent(QFocusEvent* event) override;
|
void focusOutEvent(QFocusEvent* event) override;
|
||||||
@ -207,6 +212,9 @@ private:
|
|||||||
void TouchUpdateEvent(const QTouchEvent* event);
|
void TouchUpdateEvent(const QTouchEvent* event);
|
||||||
void TouchEndEvent();
|
void TouchEndEvent();
|
||||||
|
|
||||||
|
void RequestCameraCapture();
|
||||||
|
void OnCameraCapture(int requestId, const QImage& img);
|
||||||
|
|
||||||
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
|
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
|
||||||
|
|
||||||
bool InitializeOpenGL();
|
bool InitializeOpenGL();
|
||||||
@ -232,6 +240,12 @@ private:
|
|||||||
bool first_frame = false;
|
bool first_frame = false;
|
||||||
InputCommon::TasInput::TasState last_tas_state;
|
InputCommon::TasInput::TasState last_tas_state;
|
||||||
|
|
||||||
|
bool is_virtual_camera;
|
||||||
|
int pending_camera_snapshots;
|
||||||
|
std::unique_ptr<QCamera> camera;
|
||||||
|
std::unique_ptr<QCameraImageCapture> camera_capture;
|
||||||
|
std::unique_ptr<QTimer> camera_timer;
|
||||||
|
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -368,6 +368,11 @@ void Config::ReadHidbusValues() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Config::ReadIrCameraValues() {
|
||||||
|
ReadBasicSetting(Settings::values.enable_ir_sensor);
|
||||||
|
ReadBasicSetting(Settings::values.ir_sensor_device);
|
||||||
|
}
|
||||||
|
|
||||||
void Config::ReadAudioValues() {
|
void Config::ReadAudioValues() {
|
||||||
qt_config->beginGroup(QStringLiteral("Audio"));
|
qt_config->beginGroup(QStringLiteral("Audio"));
|
||||||
|
|
||||||
@ -393,6 +398,7 @@ void Config::ReadControlValues() {
|
|||||||
ReadTouchscreenValues();
|
ReadTouchscreenValues();
|
||||||
ReadMotionTouchValues();
|
ReadMotionTouchValues();
|
||||||
ReadHidbusValues();
|
ReadHidbusValues();
|
||||||
|
ReadIrCameraValues();
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
ReadBasicSetting(Settings::values.enable_raw_input);
|
ReadBasicSetting(Settings::values.enable_raw_input);
|
||||||
@ -998,6 +1004,11 @@ void Config::SaveHidbusValues() {
|
|||||||
QString::fromStdString(default_param));
|
QString::fromStdString(default_param));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Config::SaveIrCameraValues() {
|
||||||
|
WriteBasicSetting(Settings::values.enable_ir_sensor);
|
||||||
|
WriteBasicSetting(Settings::values.ir_sensor_device);
|
||||||
|
}
|
||||||
|
|
||||||
void Config::SaveValues() {
|
void Config::SaveValues() {
|
||||||
if (global) {
|
if (global) {
|
||||||
SaveControlValues();
|
SaveControlValues();
|
||||||
@ -1040,6 +1051,7 @@ void Config::SaveControlValues() {
|
|||||||
SaveTouchscreenValues();
|
SaveTouchscreenValues();
|
||||||
SaveMotionTouchValues();
|
SaveMotionTouchValues();
|
||||||
SaveHidbusValues();
|
SaveHidbusValues();
|
||||||
|
SaveIrCameraValues();
|
||||||
|
|
||||||
WriteGlobalSetting(Settings::values.use_docked_mode);
|
WriteGlobalSetting(Settings::values.use_docked_mode);
|
||||||
WriteGlobalSetting(Settings::values.vibration_enabled);
|
WriteGlobalSetting(Settings::values.vibration_enabled);
|
||||||
|
@ -68,6 +68,7 @@ private:
|
|||||||
void ReadTouchscreenValues();
|
void ReadTouchscreenValues();
|
||||||
void ReadMotionTouchValues();
|
void ReadMotionTouchValues();
|
||||||
void ReadHidbusValues();
|
void ReadHidbusValues();
|
||||||
|
void ReadIrCameraValues();
|
||||||
|
|
||||||
// Read functions bases off the respective config section names.
|
// Read functions bases off the respective config section names.
|
||||||
void ReadAudioValues();
|
void ReadAudioValues();
|
||||||
@ -96,6 +97,7 @@ private:
|
|||||||
void SaveTouchscreenValues();
|
void SaveTouchscreenValues();
|
||||||
void SaveMotionTouchValues();
|
void SaveMotionTouchValues();
|
||||||
void SaveHidbusValues();
|
void SaveHidbusValues();
|
||||||
|
void SaveIrCameraValues();
|
||||||
|
|
||||||
// Save functions based off the respective config section names.
|
// Save functions based off the respective config section names.
|
||||||
void SaveAudioValues();
|
void SaveAudioValues();
|
||||||
|
@ -62,12 +62,23 @@ void ConfigureCamera::PreviewCamera() {
|
|||||||
camera->unload();
|
camera->unload();
|
||||||
camera->setCaptureMode(QCamera::CaptureViewfinder);
|
camera->setCaptureMode(QCamera::CaptureViewfinder);
|
||||||
camera->load();
|
camera->load();
|
||||||
|
camera->start();
|
||||||
|
|
||||||
|
pending_snapshots = 0;
|
||||||
|
is_virtual_camera = false;
|
||||||
|
|
||||||
camera_timer = std::make_unique<QTimer>();
|
camera_timer = std::make_unique<QTimer>();
|
||||||
connect(camera_timer.get(), &QTimer::timeout, [this] {
|
connect(camera_timer.get(), &QTimer::timeout, [this] {
|
||||||
|
// If the camera doesn't capture, test for virtual cameras
|
||||||
|
if (pending_snapshots > 5) {
|
||||||
|
is_virtual_camera = true;
|
||||||
|
}
|
||||||
|
// Virtual cameras like obs need to reset the camera every capture
|
||||||
|
if (is_virtual_camera) {
|
||||||
camera->stop();
|
camera->stop();
|
||||||
camera->start();
|
camera->start();
|
||||||
|
}
|
||||||
|
pending_snapshots++;
|
||||||
camera_capture->capture();
|
camera_capture->capture();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -79,6 +90,7 @@ void ConfigureCamera::DisplayCapturedFrame(int requestId, const QImage& img) {
|
|||||||
const auto converted = img.scaled(320, 240, Qt::AspectRatioMode::IgnoreAspectRatio,
|
const auto converted = img.scaled(320, 240, Qt::AspectRatioMode::IgnoreAspectRatio,
|
||||||
Qt::TransformationMode::SmoothTransformation);
|
Qt::TransformationMode::SmoothTransformation);
|
||||||
ui->preview_box->setPixmap(QPixmap::fromImage(converted));
|
ui->preview_box->setPixmap(QPixmap::fromImage(converted));
|
||||||
|
pending_snapshots = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureCamera::changeEvent(QEvent* event) {
|
void ConfigureCamera::changeEvent(QEvent* event) {
|
||||||
|
@ -44,6 +44,8 @@ private:
|
|||||||
|
|
||||||
InputCommon::InputSubsystem* input_subsystem;
|
InputCommon::InputSubsystem* input_subsystem;
|
||||||
|
|
||||||
|
bool is_virtual_camera;
|
||||||
|
int pending_snapshots;
|
||||||
std::unique_ptr<QCamera> camera;
|
std::unique_ptr<QCamera> camera;
|
||||||
std::unique_ptr<QCameraImageCapture> camera_capture;
|
std::unique_ptr<QCameraImageCapture> camera_capture;
|
||||||
std::unique_ptr<QTimer> camera_timer;
|
std::unique_ptr<QTimer> camera_timer;
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "ui_configure_input.h"
|
#include "ui_configure_input.h"
|
||||||
#include "ui_configure_input_advanced.h"
|
#include "ui_configure_input_advanced.h"
|
||||||
#include "ui_configure_input_player.h"
|
#include "ui_configure_input_player.h"
|
||||||
|
#include "yuzu/configuration/configure_camera.h"
|
||||||
#include "yuzu/configuration/configure_debug_controller.h"
|
#include "yuzu/configuration/configure_debug_controller.h"
|
||||||
#include "yuzu/configuration/configure_input.h"
|
#include "yuzu/configuration/configure_input.h"
|
||||||
#include "yuzu/configuration/configure_input_advanced.h"
|
#include "yuzu/configuration/configure_input_advanced.h"
|
||||||
@ -163,6 +164,10 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
|
|||||||
[this, input_subsystem, &hid_core] {
|
[this, input_subsystem, &hid_core] {
|
||||||
CallConfigureDialog<ConfigureRingController>(*this, input_subsystem, hid_core);
|
CallConfigureDialog<ConfigureRingController>(*this, input_subsystem, hid_core);
|
||||||
});
|
});
|
||||||
|
connect(advanced, &ConfigureInputAdvanced::CallCameraDialog,
|
||||||
|
[this, input_subsystem, &hid_core] {
|
||||||
|
CallConfigureDialog<ConfigureCamera>(*this, input_subsystem);
|
||||||
|
});
|
||||||
|
|
||||||
connect(ui->vibrationButton, &QPushButton::clicked,
|
connect(ui->vibrationButton, &QPushButton::clicked,
|
||||||
[this, &hid_core] { CallConfigureDialog<ConfigureVibration>(*this, hid_core); });
|
[this, &hid_core] { CallConfigureDialog<ConfigureVibration>(*this, hid_core); });
|
||||||
|
@ -89,6 +89,7 @@ ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent)
|
|||||||
[this] { CallMotionTouchConfigDialog(); });
|
[this] { CallMotionTouchConfigDialog(); });
|
||||||
connect(ui->ring_controller_configure, &QPushButton::clicked, this,
|
connect(ui->ring_controller_configure, &QPushButton::clicked, this,
|
||||||
[this] { CallRingControllerDialog(); });
|
[this] { CallRingControllerDialog(); });
|
||||||
|
connect(ui->camera_configure, &QPushButton::clicked, this, [this] { CallCameraDialog(); });
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
ui->enable_raw_input->setVisible(false);
|
ui->enable_raw_input->setVisible(false);
|
||||||
@ -136,6 +137,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
|
|||||||
Settings::values.enable_udp_controller = ui->enable_udp_controller->isChecked();
|
Settings::values.enable_udp_controller = ui->enable_udp_controller->isChecked();
|
||||||
Settings::values.controller_navigation = ui->controller_navigation->isChecked();
|
Settings::values.controller_navigation = ui->controller_navigation->isChecked();
|
||||||
Settings::values.enable_ring_controller = ui->enable_ring_controller->isChecked();
|
Settings::values.enable_ring_controller = ui->enable_ring_controller->isChecked();
|
||||||
|
Settings::values.enable_ir_sensor = ui->enable_ir_sensor->isChecked();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureInputAdvanced::LoadConfiguration() {
|
void ConfigureInputAdvanced::LoadConfiguration() {
|
||||||
@ -169,6 +171,7 @@ void ConfigureInputAdvanced::LoadConfiguration() {
|
|||||||
ui->enable_udp_controller->setChecked(Settings::values.enable_udp_controller.GetValue());
|
ui->enable_udp_controller->setChecked(Settings::values.enable_udp_controller.GetValue());
|
||||||
ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue());
|
ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue());
|
||||||
ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue());
|
ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue());
|
||||||
|
ui->enable_ir_sensor->setChecked(Settings::values.enable_ir_sensor.GetValue());
|
||||||
|
|
||||||
UpdateUIEnabled();
|
UpdateUIEnabled();
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ signals:
|
|||||||
void CallTouchscreenConfigDialog();
|
void CallTouchscreenConfigDialog();
|
||||||
void CallMotionTouchConfigDialog();
|
void CallMotionTouchConfigDialog();
|
||||||
void CallRingControllerDialog();
|
void CallRingControllerDialog();
|
||||||
|
void CallCameraDialog();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void changeEvent(QEvent* event) override;
|
void changeEvent(QEvent* event) override;
|
||||||
|
@ -2617,6 +2617,20 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QCheckBox" name="enable_ir_sensor">
|
||||||
|
<property name="text">
|
||||||
|
<string>Infrared Camera</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="2">
|
||||||
|
<widget class="QPushButton" name="camera_configure">
|
||||||
|
<property name="text">
|
||||||
|
<string>Configure</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -147,6 +147,10 @@ void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size
|
|||||||
ui->progress_bar->setMaximum(static_cast<int>(total));
|
ui->progress_bar->setMaximum(static_cast<int>(total));
|
||||||
previous_total = total;
|
previous_total = total;
|
||||||
}
|
}
|
||||||
|
// Reset the progress bar ranges if compilation is done
|
||||||
|
if (stage == VideoCore::LoadCallbackStage::Complete) {
|
||||||
|
ui->progress_bar->setRange(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
QString estimate;
|
QString estimate;
|
||||||
// If theres a drastic slowdown in the rate, then display an estimate
|
// If theres a drastic slowdown in the rate, then display an estimate
|
||||||
|
@ -10,6 +10,10 @@
|
|||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
#include <unistd.h> // for chdir
|
#include <unistd.h> // for chdir
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef __linux__
|
||||||
|
#include <csignal>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
|
// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
|
||||||
#include "applets/qt_controller.h"
|
#include "applets/qt_controller.h"
|
||||||
@ -259,6 +263,10 @@ GMainWindow::GMainWindow(bool has_broken_vulkan)
|
|||||||
config{std::make_unique<Config>(*system)},
|
config{std::make_unique<Config>(*system)},
|
||||||
vfs{std::make_shared<FileSys::RealVfsFilesystem>()},
|
vfs{std::make_shared<FileSys::RealVfsFilesystem>()},
|
||||||
provider{std::make_unique<FileSys::ManualContentProvider>()} {
|
provider{std::make_unique<FileSys::ManualContentProvider>()} {
|
||||||
|
#ifdef __linux__
|
||||||
|
SetupSigInterrupts();
|
||||||
|
#endif
|
||||||
|
|
||||||
Common::Log::Initialize();
|
Common::Log::Initialize();
|
||||||
LoadTranslation();
|
LoadTranslation();
|
||||||
|
|
||||||
@ -376,6 +384,8 @@ GMainWindow::GMainWindow(bool has_broken_vulkan)
|
|||||||
SDL_EnableScreenSaver();
|
SDL_EnableScreenSaver();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
SetupPrepareForSleep();
|
||||||
|
|
||||||
Common::Log::Start();
|
Common::Log::Start();
|
||||||
|
|
||||||
QStringList args = QApplication::arguments();
|
QStringList args = QApplication::arguments();
|
||||||
@ -460,6 +470,11 @@ GMainWindow::~GMainWindow() {
|
|||||||
if (render_window->parent() == nullptr) {
|
if (render_window->parent() == nullptr) {
|
||||||
delete render_window;
|
delete render_window;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
::close(sig_interrupt_fds[0]);
|
||||||
|
::close(sig_interrupt_fds[1]);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::RegisterMetaTypes() {
|
void GMainWindow::RegisterMetaTypes() {
|
||||||
@ -1285,6 +1300,43 @@ void GMainWindow::OnDisplayTitleBars(bool show) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GMainWindow::SetupPrepareForSleep() {
|
||||||
|
#ifdef __linux__
|
||||||
|
auto bus = QDBusConnection::systemBus();
|
||||||
|
if (bus.isConnected()) {
|
||||||
|
const bool success = bus.connect(
|
||||||
|
QStringLiteral("org.freedesktop.login1"), QStringLiteral("/org/freedesktop/login1"),
|
||||||
|
QStringLiteral("org.freedesktop.login1.Manager"), QStringLiteral("PrepareForSleep"),
|
||||||
|
QStringLiteral("b"), this, SLOT(OnPrepareForSleep(bool)));
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
LOG_WARNING(Frontend, "Couldn't register PrepareForSleep signal");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG_WARNING(Frontend, "QDBusConnection system bus is not connected");
|
||||||
|
}
|
||||||
|
#endif // __linux__
|
||||||
|
}
|
||||||
|
|
||||||
|
void GMainWindow::OnPrepareForSleep(bool prepare_sleep) {
|
||||||
|
if (emu_thread == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prepare_sleep) {
|
||||||
|
if (emu_thread->IsRunning()) {
|
||||||
|
auto_paused = true;
|
||||||
|
OnPauseGame();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!emu_thread->IsRunning() && auto_paused) {
|
||||||
|
auto_paused = false;
|
||||||
|
RequestGameResume();
|
||||||
|
OnStartGame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
static std::optional<QDBusObjectPath> HoldWakeLockLinux(u32 window_id = 0) {
|
static std::optional<QDBusObjectPath> HoldWakeLockLinux(u32 window_id = 0) {
|
||||||
if (!QDBusConnection::sessionBus().isConnected()) {
|
if (!QDBusConnection::sessionBus().isConnected()) {
|
||||||
@ -1324,6 +1376,52 @@ static void ReleaseWakeLockLinux(QDBusObjectPath lock) {
|
|||||||
QString::fromLatin1("org.freedesktop.portal.Request"));
|
QString::fromLatin1("org.freedesktop.portal.Request"));
|
||||||
unlocker.call(QString::fromLatin1("Close"));
|
unlocker.call(QString::fromLatin1("Close"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::array<int, 3> GMainWindow::sig_interrupt_fds{0, 0, 0};
|
||||||
|
|
||||||
|
void GMainWindow::SetupSigInterrupts() {
|
||||||
|
if (sig_interrupt_fds[2] == 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
socketpair(AF_UNIX, SOCK_STREAM, 0, sig_interrupt_fds.data());
|
||||||
|
sig_interrupt_fds[2] = 1;
|
||||||
|
|
||||||
|
struct sigaction sa;
|
||||||
|
sa.sa_handler = &GMainWindow::HandleSigInterrupt;
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
sa.sa_flags = SA_RESETHAND;
|
||||||
|
sigaction(SIGINT, &sa, nullptr);
|
||||||
|
sigaction(SIGTERM, &sa, nullptr);
|
||||||
|
|
||||||
|
sig_interrupt_notifier = new QSocketNotifier(sig_interrupt_fds[1], QSocketNotifier::Read, this);
|
||||||
|
connect(sig_interrupt_notifier, &QSocketNotifier::activated, this,
|
||||||
|
&GMainWindow::OnSigInterruptNotifierActivated);
|
||||||
|
connect(this, &GMainWindow::SigInterrupt, this, &GMainWindow::close);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GMainWindow::HandleSigInterrupt(int sig) {
|
||||||
|
if (sig == SIGINT) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calling into Qt directly from a signal handler is not safe,
|
||||||
|
// so wake up a QSocketNotifier with this hacky write call instead.
|
||||||
|
char a = 1;
|
||||||
|
int ret = write(sig_interrupt_fds[0], &a, sizeof(a));
|
||||||
|
(void)ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GMainWindow::OnSigInterruptNotifierActivated() {
|
||||||
|
sig_interrupt_notifier->setEnabled(false);
|
||||||
|
|
||||||
|
char a;
|
||||||
|
int ret = read(sig_interrupt_fds[1], &a, sizeof(a));
|
||||||
|
(void)ret;
|
||||||
|
|
||||||
|
sig_interrupt_notifier->setEnabled(true);
|
||||||
|
|
||||||
|
emit SigInterrupt();
|
||||||
|
}
|
||||||
#endif // __linux__
|
#endif // __linux__
|
||||||
|
|
||||||
void GMainWindow::PreventOSSleep() {
|
void GMainWindow::PreventOSSleep() {
|
||||||
@ -1541,6 +1639,8 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
|
|||||||
mouse_hide_timer.start();
|
mouse_hide_timer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
render_window->InitializeCamera();
|
||||||
|
|
||||||
std::string title_name;
|
std::string title_name;
|
||||||
std::string title_version;
|
std::string title_version;
|
||||||
const auto res = system->GetGameName(title_name);
|
const auto res = system->GetGameName(title_name);
|
||||||
@ -1622,6 +1722,7 @@ void GMainWindow::ShutdownGame() {
|
|||||||
tas_label->clear();
|
tas_label->clear();
|
||||||
input_subsystem->GetTas()->Stop();
|
input_subsystem->GetTas()->Stop();
|
||||||
OnTasStateChanged();
|
OnTasStateChanged();
|
||||||
|
render_window->FinalizeCamera();
|
||||||
|
|
||||||
// Enable all controllers
|
// Enable all controllers
|
||||||
system->HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
|
system->HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
|
||||||
@ -2861,6 +2962,12 @@ void GMainWindow::OnConfigure() {
|
|||||||
mouse_hide_timer.start();
|
mouse_hide_timer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Restart camera config
|
||||||
|
if (emulation_running) {
|
||||||
|
render_window->FinalizeCamera();
|
||||||
|
render_window->InitializeCamera();
|
||||||
|
}
|
||||||
|
|
||||||
if (!UISettings::values.has_broken_vulkan) {
|
if (!UISettings::values.has_broken_vulkan) {
|
||||||
renderer_status_button->setEnabled(!emulation_running);
|
renderer_status_button->setEnabled(!emulation_running);
|
||||||
}
|
}
|
||||||
|
@ -161,6 +161,8 @@ signals:
|
|||||||
void WebBrowserExtractOfflineRomFS();
|
void WebBrowserExtractOfflineRomFS();
|
||||||
void WebBrowserClosed(Service::AM::Applets::WebExitReason exit_reason, std::string last_url);
|
void WebBrowserClosed(Service::AM::Applets::WebExitReason exit_reason, std::string last_url);
|
||||||
|
|
||||||
|
void SigInterrupt();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void OnLoadComplete();
|
void OnLoadComplete();
|
||||||
void OnExecuteProgram(std::size_t program_index);
|
void OnExecuteProgram(std::size_t program_index);
|
||||||
@ -200,6 +202,8 @@ private:
|
|||||||
void ConnectMenuEvents();
|
void ConnectMenuEvents();
|
||||||
void UpdateMenuState();
|
void UpdateMenuState();
|
||||||
|
|
||||||
|
void SetupPrepareForSleep();
|
||||||
|
|
||||||
void PreventOSSleep();
|
void PreventOSSleep();
|
||||||
void AllowOSSleep();
|
void AllowOSSleep();
|
||||||
|
|
||||||
@ -247,12 +251,19 @@ private:
|
|||||||
void RequestGameResume();
|
void RequestGameResume();
|
||||||
void closeEvent(QCloseEvent* event) override;
|
void closeEvent(QCloseEvent* event) override;
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
void SetupSigInterrupts();
|
||||||
|
static void HandleSigInterrupt(int);
|
||||||
|
void OnSigInterruptNotifierActivated();
|
||||||
|
#endif
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void OnStartGame();
|
void OnStartGame();
|
||||||
void OnRestartGame();
|
void OnRestartGame();
|
||||||
void OnPauseGame();
|
void OnPauseGame();
|
||||||
void OnPauseContinueGame();
|
void OnPauseContinueGame();
|
||||||
void OnStopGame();
|
void OnStopGame();
|
||||||
|
void OnPrepareForSleep(bool prepare_sleep);
|
||||||
void OnMenuReportCompatibility();
|
void OnMenuReportCompatibility();
|
||||||
void OnOpenModsPage();
|
void OnOpenModsPage();
|
||||||
void OnOpenQuickstartGuide();
|
void OnOpenQuickstartGuide();
|
||||||
@ -415,6 +426,9 @@ private:
|
|||||||
bool is_tas_recording_dialog_active{};
|
bool is_tas_recording_dialog_active{};
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
|
QSocketNotifier* sig_interrupt_notifier;
|
||||||
|
static std::array<int, 3> sig_interrupt_fds;
|
||||||
|
|
||||||
QDBusObjectPath wake_lock{};
|
QDBusObjectPath wake_lock{};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user