Browse Source
Merge pull request #8484 from german77/irs_release
Merge pull request #8484 from german77/irs_release
service: irs: Add camera support, split processors and implement ImageTransferProcessornce_cpp
committed by
GitHub
50 changed files with 2342 additions and 320 deletions
-
6CMakeLists.txt
-
8CMakeModules/CopyYuzuQt5Deps.cmake
-
29src/common/input.h
-
3src/common/settings.h
-
15src/core/CMakeLists.txt
-
59src/core/hid/emulated_controller.cpp
-
40src/core/hid/emulated_controller.h
-
14src/core/hid/input_converter.cpp
-
8src/core/hid/input_converter.h
-
301src/core/hid/irs_types.h
-
7src/core/hle/service/hid/errors.h
-
4src/core/hle/service/hid/hid.cpp
-
324src/core/hle/service/hid/irs.cpp
-
295src/core/hle/service/hid/irs.h
-
34src/core/hle/service/hid/irsensor/clustering_processor.cpp
-
74src/core/hle/service/hid/irsensor/clustering_processor.h
-
150src/core/hle/service/hid/irsensor/image_transfer_processor.cpp
-
73src/core/hle/service/hid/irsensor/image_transfer_processor.h
-
27src/core/hle/service/hid/irsensor/ir_led_processor.cpp
-
47src/core/hle/service/hid/irsensor/ir_led_processor.h
-
34src/core/hle/service/hid/irsensor/moment_processor.cpp
-
61src/core/hle/service/hid/irsensor/moment_processor.h
-
26src/core/hle/service/hid/irsensor/pointing_processor.cpp
-
61src/core/hle/service/hid/irsensor/pointing_processor.h
-
67src/core/hle/service/hid/irsensor/processor_base.cpp
-
33src/core/hle/service/hid/irsensor/processor_base.h
-
29src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp
-
53src/core/hle/service/hid/irsensor/tera_plugin_processor.h
-
2src/input_common/CMakeLists.txt
-
82src/input_common/drivers/camera.cpp
-
29src/input_common/drivers/camera.h
-
38src/input_common/input_engine.cpp
-
19src/input_common/input_engine.h
-
60src/input_common/input_poller.cpp
-
11src/input_common/input_poller.h
-
21src/input_common/main.cpp
-
9src/input_common/main.h
-
5src/yuzu/CMakeLists.txt
-
82src/yuzu/bootmanager.cpp
-
14src/yuzu/bootmanager.h
-
12src/yuzu/configuration/config.cpp
-
2src/yuzu/configuration/config.h
-
138src/yuzu/configuration/configure_camera.cpp
-
54src/yuzu/configuration/configure_camera.h
-
170src/yuzu/configuration/configure_camera.ui
-
5src/yuzu/configuration/configure_input.cpp
-
3src/yuzu/configuration/configure_input_advanced.cpp
-
1src/yuzu/configuration/configure_input_advanced.h
-
14src/yuzu/configuration/configure_input_advanced.ui
-
9src/yuzu/main.cpp
@ -0,0 +1,301 @@ |
|||||
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
||||
|
// SPDX-License-Identifier: GPL-3.0-or-later |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include "common/common_funcs.h" |
||||
|
#include "common/common_types.h" |
||||
|
#include "core/hid/hid_types.h" |
||||
|
|
||||
|
namespace Core::IrSensor { |
||||
|
|
||||
|
// This is nn::irsensor::CameraAmbientNoiseLevel |
||||
|
enum class CameraAmbientNoiseLevel : u32 { |
||||
|
Low, |
||||
|
Medium, |
||||
|
High, |
||||
|
Unkown3, // This level can't be reached |
||||
|
}; |
||||
|
|
||||
|
// This is nn::irsensor::CameraLightTarget |
||||
|
enum class CameraLightTarget : u32 { |
||||
|
AllLeds, |
||||
|
BrightLeds, |
||||
|
DimLeds, |
||||
|
None, |
||||
|
}; |
||||
|
|
||||
|
// This is nn::irsensor::PackedCameraLightTarget |
||||
|
enum class PackedCameraLightTarget : u8 { |
||||
|
AllLeds, |
||||
|
BrightLeds, |
||||
|
DimLeds, |
||||
|
None, |
||||
|
}; |
||||
|
|
||||
|
// This is nn::irsensor::AdaptiveClusteringMode |
||||
|
enum class AdaptiveClusteringMode : u32 { |
||||
|
StaticFov, |
||||
|
DynamicFov, |
||||
|
}; |
||||
|
|
||||
|
// This is nn::irsensor::AdaptiveClusteringTargetDistance |
||||
|
enum class AdaptiveClusteringTargetDistance : u32 { |
||||
|
Near, |
||||
|
Middle, |
||||
|
Far, |
||||
|
}; |
||||
|
|
||||
|
// This is nn::irsensor::ImageTransferProcessorFormat |
||||
|
enum class ImageTransferProcessorFormat : u32 { |
||||
|
Size320x240, |
||||
|
Size160x120, |
||||
|
Size80x60, |
||||
|
Size40x30, |
||||
|
Size20x15, |
||||
|
}; |
||||
|
|
||||
|
// This is nn::irsensor::PackedImageTransferProcessorFormat |
||||
|
enum class PackedImageTransferProcessorFormat : u8 { |
||||
|
Size320x240, |
||||
|
Size160x120, |
||||
|
Size80x60, |
||||
|
Size40x30, |
||||
|
Size20x15, |
||||
|
}; |
||||
|
|
||||
|
// This is nn::irsensor::IrCameraStatus |
||||
|
enum class IrCameraStatus : u32 { |
||||
|
Available, |
||||
|
Unsupported, |
||||
|
Unconnected, |
||||
|
}; |
||||
|
|
||||
|
// This is nn::irsensor::IrCameraInternalStatus |
||||
|
enum class IrCameraInternalStatus : u32 { |
||||
|
Stopped, |
||||
|
FirmwareUpdateNeeded, |
||||
|
Unkown2, |
||||
|
Unkown3, |
||||
|
Unkown4, |
||||
|
FirmwareVersionRequested, |
||||
|
FirmwareVersionIsInvalid, |
||||
|
Ready, |
||||
|
Setting, |
||||
|
}; |
||||
|
|
||||
|
// This is nn::irsensor::detail::StatusManager::IrSensorMode |
||||
|
enum class IrSensorMode : u64 { |
||||
|
None, |
||||
|
MomentProcessor, |
||||
|
ClusteringProcessor, |
||||
|
ImageTransferProcessor, |
||||
|
PointingProcessorMarker, |
||||
|
TeraPluginProcessor, |
||||
|
IrLedProcessor, |
||||
|
}; |
||||
|
|
||||
|
// This is nn::irsensor::ImageProcessorStatus |
||||
|
enum ImageProcessorStatus : u32 { |
||||
|
Stopped, |
||||
|
Running, |
||||
|
}; |
||||
|
|
||||
|
// This is nn::irsensor::HandAnalysisMode |
||||
|
enum class HandAnalysisMode : u32 { |
||||
|
None, |
||||
|
Silhouette, |
||||
|
Image, |
||||
|
SilhoueteAndImage, |
||||
|
SilhuetteOnly, |
||||
|
}; |
||||
|
|
||||
|
// This is nn::irsensor::IrSensorFunctionLevel |
||||
|
enum class IrSensorFunctionLevel : u8 { |
||||
|
unknown0, |
||||
|
unknown1, |
||||
|
unknown2, |
||||
|
unknown3, |
||||
|
unknown4, |
||||
|
}; |
||||
|
|
||||
|
// This is nn::irsensor::MomentProcessorPreprocess |
||||
|
enum class MomentProcessorPreprocess : u32 { |
||||
|
Unkown0, |
||||
|
Unkown1, |
||||
|
}; |
||||
|
|
||||
|
// This is nn::irsensor::PackedMomentProcessorPreprocess |
||||
|
enum class PackedMomentProcessorPreprocess : u8 { |
||||
|
Unkown0, |
||||
|
Unkown1, |
||||
|
}; |
||||
|
|
||||
|
// This is nn::irsensor::PointingStatus |
||||
|
enum class PointingStatus : u32 { |
||||
|
Unkown0, |
||||
|
Unkown1, |
||||
|
}; |
||||
|
|
||||
|
struct IrsRect { |
||||
|
s16 x; |
||||
|
s16 y; |
||||
|
s16 width; |
||||
|
s16 height; |
||||
|
}; |
||||
|
|
||||
|
struct IrsCentroid { |
||||
|
f32 x; |
||||
|
f32 y; |
||||
|
}; |
||||
|
|
||||
|
struct CameraConfig { |
||||
|
u64 exposure_time; |
||||
|
CameraLightTarget light_target; |
||||
|
u32 gain; |
||||
|
bool is_negative_used; |
||||
|
INSERT_PADDING_BYTES(7); |
||||
|
}; |
||||
|
static_assert(sizeof(CameraConfig) == 0x18, "CameraConfig is an invalid size"); |
||||
|
|
||||
|
struct PackedCameraConfig { |
||||
|
u64 exposure_time; |
||||
|
PackedCameraLightTarget light_target; |
||||
|
u8 gain; |
||||
|
bool is_negative_used; |
||||
|
INSERT_PADDING_BYTES(5); |
||||
|
}; |
||||
|
static_assert(sizeof(PackedCameraConfig) == 0x10, "PackedCameraConfig is an invalid size"); |
||||
|
|
||||
|
// This is nn::irsensor::IrCameraHandle |
||||
|
struct IrCameraHandle { |
||||
|
u8 npad_id{}; |
||||
|
Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None}; |
||||
|
INSERT_PADDING_BYTES(2); |
||||
|
}; |
||||
|
static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size"); |
||||
|
|
||||
|
// This is nn::irsensor::PackedMcuVersion |
||||
|
struct PackedMcuVersion { |
||||
|
u16 major; |
||||
|
u16 minor; |
||||
|
}; |
||||
|
static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size"); |
||||
|
|
||||
|
// This is nn::irsensor::PackedMomentProcessorConfig |
||||
|
struct PackedMomentProcessorConfig { |
||||
|
PackedCameraConfig camera_config; |
||||
|
IrsRect window_of_interest; |
||||
|
PackedMcuVersion required_mcu_version; |
||||
|
PackedMomentProcessorPreprocess preprocess; |
||||
|
u8 preprocess_intensity_threshold; |
||||
|
INSERT_PADDING_BYTES(2); |
||||
|
}; |
||||
|
static_assert(sizeof(PackedMomentProcessorConfig) == 0x20, |
||||
|
"PackedMomentProcessorConfig is an invalid size"); |
||||
|
|
||||
|
// This is nn::irsensor::PackedClusteringProcessorConfig |
||||
|
struct PackedClusteringProcessorConfig { |
||||
|
PackedCameraConfig camera_config; |
||||
|
IrsRect window_of_interest; |
||||
|
PackedMcuVersion required_mcu_version; |
||||
|
u32 pixel_count_min; |
||||
|
u32 pixel_count_max; |
||||
|
u8 object_intensity_min; |
||||
|
bool is_external_light_filter_enabled; |
||||
|
INSERT_PADDING_BYTES(2); |
||||
|
}; |
||||
|
static_assert(sizeof(PackedClusteringProcessorConfig) == 0x28, |
||||
|
"PackedClusteringProcessorConfig is an invalid size"); |
||||
|
|
||||
|
// This is nn::irsensor::PackedImageTransferProcessorConfig |
||||
|
struct PackedImageTransferProcessorConfig { |
||||
|
PackedCameraConfig camera_config; |
||||
|
PackedMcuVersion required_mcu_version; |
||||
|
PackedImageTransferProcessorFormat format; |
||||
|
INSERT_PADDING_BYTES(3); |
||||
|
}; |
||||
|
static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18, |
||||
|
"PackedImageTransferProcessorConfig is an invalid size"); |
||||
|
|
||||
|
// This is nn::irsensor::PackedTeraPluginProcessorConfig |
||||
|
struct PackedTeraPluginProcessorConfig { |
||||
|
PackedMcuVersion required_mcu_version; |
||||
|
u8 mode; |
||||
|
u8 unknown_1; |
||||
|
u8 unknown_2; |
||||
|
u8 unknown_3; |
||||
|
}; |
||||
|
static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8, |
||||
|
"PackedTeraPluginProcessorConfig is an invalid size"); |
||||
|
|
||||
|
// This is nn::irsensor::PackedPointingProcessorConfig |
||||
|
struct PackedPointingProcessorConfig { |
||||
|
IrsRect window_of_interest; |
||||
|
PackedMcuVersion required_mcu_version; |
||||
|
}; |
||||
|
static_assert(sizeof(PackedPointingProcessorConfig) == 0xC, |
||||
|
"PackedPointingProcessorConfig is an invalid size"); |
||||
|
|
||||
|
// This is nn::irsensor::PackedFunctionLevel |
||||
|
struct PackedFunctionLevel { |
||||
|
IrSensorFunctionLevel function_level; |
||||
|
INSERT_PADDING_BYTES(3); |
||||
|
}; |
||||
|
static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size"); |
||||
|
|
||||
|
// This is nn::irsensor::PackedImageTransferProcessorExConfig |
||||
|
struct PackedImageTransferProcessorExConfig { |
||||
|
PackedCameraConfig camera_config; |
||||
|
PackedMcuVersion required_mcu_version; |
||||
|
PackedImageTransferProcessorFormat origin_format; |
||||
|
PackedImageTransferProcessorFormat trimming_format; |
||||
|
u16 trimming_start_x; |
||||
|
u16 trimming_start_y; |
||||
|
bool is_external_light_filter_enabled; |
||||
|
INSERT_PADDING_BYTES(5); |
||||
|
}; |
||||
|
static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20, |
||||
|
"PackedImageTransferProcessorExConfig is an invalid size"); |
||||
|
|
||||
|
// This is nn::irsensor::PackedIrLedProcessorConfig |
||||
|
struct PackedIrLedProcessorConfig { |
||||
|
PackedMcuVersion required_mcu_version; |
||||
|
u8 light_target; |
||||
|
INSERT_PADDING_BYTES(3); |
||||
|
}; |
||||
|
static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8, |
||||
|
"PackedIrLedProcessorConfig is an invalid size"); |
||||
|
|
||||
|
// This is nn::irsensor::HandAnalysisConfig |
||||
|
struct HandAnalysisConfig { |
||||
|
HandAnalysisMode mode; |
||||
|
}; |
||||
|
static_assert(sizeof(HandAnalysisConfig) == 0x4, "HandAnalysisConfig is an invalid size"); |
||||
|
|
||||
|
// This is nn::irsensor::detail::ProcessorState contents are different for each processor |
||||
|
struct ProcessorState { |
||||
|
std::array<u8, 0xE20> processor_raw_data{}; |
||||
|
}; |
||||
|
static_assert(sizeof(ProcessorState) == 0xE20, "ProcessorState is an invalid size"); |
||||
|
|
||||
|
// This is nn::irsensor::detail::DeviceFormat |
||||
|
struct DeviceFormat { |
||||
|
Core::IrSensor::IrCameraStatus camera_status{Core::IrSensor::IrCameraStatus::Unconnected}; |
||||
|
Core::IrSensor::IrCameraInternalStatus camera_internal_status{ |
||||
|
Core::IrSensor::IrCameraInternalStatus::Ready}; |
||||
|
Core::IrSensor::IrSensorMode mode{Core::IrSensor::IrSensorMode::None}; |
||||
|
ProcessorState state{}; |
||||
|
}; |
||||
|
static_assert(sizeof(DeviceFormat) == 0xE30, "DeviceFormat is an invalid size"); |
||||
|
|
||||
|
// This is nn::irsensor::ImageTransferProcessorState |
||||
|
struct ImageTransferProcessorState { |
||||
|
u64 sampling_number; |
||||
|
Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; |
||||
|
INSERT_PADDING_BYTES(4); |
||||
|
}; |
||||
|
static_assert(sizeof(ImageTransferProcessorState) == 0x10, |
||||
|
"ImageTransferProcessorState is an invalid size"); |
||||
|
|
||||
|
} // namespace Core::IrSensor |
||||
@ -0,0 +1,34 @@ |
|||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
|
||||
|
#include "core/hle/service/hid/irsensor/clustering_processor.h"
|
||||
|
|
||||
|
namespace Service::IRS { |
||||
|
ClusteringProcessor::ClusteringProcessor(Core::IrSensor::DeviceFormat& device_format) |
||||
|
: device(device_format) { |
||||
|
device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor; |
||||
|
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; |
||||
|
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; |
||||
|
} |
||||
|
|
||||
|
ClusteringProcessor::~ClusteringProcessor() = default; |
||||
|
|
||||
|
void ClusteringProcessor::StartProcessor() {} |
||||
|
|
||||
|
void ClusteringProcessor::SuspendProcessor() {} |
||||
|
|
||||
|
void ClusteringProcessor::StopProcessor() {} |
||||
|
|
||||
|
void ClusteringProcessor::SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config) { |
||||
|
current_config.camera_config.exposure_time = config.camera_config.exposure_time; |
||||
|
current_config.camera_config.gain = config.camera_config.gain; |
||||
|
current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; |
||||
|
current_config.camera_config.light_target = |
||||
|
static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); |
||||
|
current_config.pixel_count_min = config.pixel_count_min; |
||||
|
current_config.pixel_count_max = config.pixel_count_max; |
||||
|
current_config.is_external_light_filter_enabled = config.is_external_light_filter_enabled; |
||||
|
current_config.object_intensity_min = config.object_intensity_min; |
||||
|
} |
||||
|
|
||||
|
} // namespace Service::IRS
|
||||
@ -0,0 +1,74 @@ |
|||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
||||
|
// SPDX-License-Identifier: GPL-3.0-or-later |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include "common/common_types.h" |
||||
|
#include "core/hid/irs_types.h" |
||||
|
#include "core/hle/service/hid/irsensor/processor_base.h" |
||||
|
|
||||
|
namespace Service::IRS { |
||||
|
class ClusteringProcessor final : public ProcessorBase { |
||||
|
public: |
||||
|
explicit ClusteringProcessor(Core::IrSensor::DeviceFormat& device_format); |
||||
|
~ClusteringProcessor() override; |
||||
|
|
||||
|
// Called when the processor is initialized |
||||
|
void StartProcessor() override; |
||||
|
|
||||
|
// Called when the processor is suspended |
||||
|
void SuspendProcessor() override; |
||||
|
|
||||
|
// Called when the processor is stopped |
||||
|
void StopProcessor() override; |
||||
|
|
||||
|
// Sets config parameters of the camera |
||||
|
void SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config); |
||||
|
|
||||
|
private: |
||||
|
// This is nn::irsensor::ClusteringProcessorConfig |
||||
|
struct ClusteringProcessorConfig { |
||||
|
Core::IrSensor::CameraConfig camera_config; |
||||
|
Core::IrSensor::IrsRect window_of_interest; |
||||
|
u32 pixel_count_min; |
||||
|
u32 pixel_count_max; |
||||
|
u32 object_intensity_min; |
||||
|
bool is_external_light_filter_enabled; |
||||
|
INSERT_PADDING_BYTES(3); |
||||
|
}; |
||||
|
static_assert(sizeof(ClusteringProcessorConfig) == 0x30, |
||||
|
"ClusteringProcessorConfig is an invalid size"); |
||||
|
|
||||
|
// This is nn::irsensor::AdaptiveClusteringProcessorConfig |
||||
|
struct AdaptiveClusteringProcessorConfig { |
||||
|
Core::IrSensor::AdaptiveClusteringMode mode; |
||||
|
Core::IrSensor::AdaptiveClusteringTargetDistance target_distance; |
||||
|
}; |
||||
|
static_assert(sizeof(AdaptiveClusteringProcessorConfig) == 0x8, |
||||
|
"AdaptiveClusteringProcessorConfig is an invalid size"); |
||||
|
|
||||
|
// This is nn::irsensor::ClusteringData |
||||
|
struct ClusteringData { |
||||
|
f32 average_intensity; |
||||
|
Core::IrSensor::IrsCentroid centroid; |
||||
|
u32 pixel_count; |
||||
|
Core::IrSensor::IrsRect bound; |
||||
|
}; |
||||
|
static_assert(sizeof(ClusteringData) == 0x18, "ClusteringData is an invalid size"); |
||||
|
|
||||
|
// This is nn::irsensor::ClusteringProcessorState |
||||
|
struct ClusteringProcessorState { |
||||
|
s64 sampling_number; |
||||
|
u64 timestamp; |
||||
|
u8 object_count; |
||||
|
INSERT_PADDING_BYTES(3); |
||||
|
Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; |
||||
|
std::array<ClusteringData, 0x10> data; |
||||
|
}; |
||||
|
static_assert(sizeof(ClusteringProcessorState) == 0x198, |
||||
|
"ClusteringProcessorState is an invalid size"); |
||||
|
|
||||
|
ClusteringProcessorConfig current_config{}; |
||||
|
Core::IrSensor::DeviceFormat& device; |
||||
|
}; |
||||
|
} // namespace Service::IRS |
||||
@ -0,0 +1,150 @@ |
|||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
|
||||
|
#include "core/hid/emulated_controller.h"
|
||||
|
#include "core/hid/hid_core.h"
|
||||
|
#include "core/hle/service/hid/irsensor/image_transfer_processor.h"
|
||||
|
|
||||
|
namespace Service::IRS { |
||||
|
ImageTransferProcessor::ImageTransferProcessor(Core::HID::HIDCore& hid_core_, |
||||
|
Core::IrSensor::DeviceFormat& device_format, |
||||
|
std::size_t npad_index) |
||||
|
: device{device_format} { |
||||
|
npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index); |
||||
|
|
||||
|
Core::HID::ControllerUpdateCallback engine_callback{ |
||||
|
.on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); }, |
||||
|
.is_npad_service = true, |
||||
|
}; |
||||
|
callback_key = npad_device->SetCallback(engine_callback); |
||||
|
|
||||
|
device.mode = Core::IrSensor::IrSensorMode::ImageTransferProcessor; |
||||
|
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; |
||||
|
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; |
||||
|
} |
||||
|
|
||||
|
ImageTransferProcessor::~ImageTransferProcessor() { |
||||
|
npad_device->DeleteCallback(callback_key); |
||||
|
}; |
||||
|
|
||||
|
void ImageTransferProcessor::StartProcessor() { |
||||
|
is_active = true; |
||||
|
device.camera_status = Core::IrSensor::IrCameraStatus::Available; |
||||
|
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready; |
||||
|
processor_state.sampling_number = 0; |
||||
|
processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; |
||||
|
} |
||||
|
|
||||
|
void ImageTransferProcessor::SuspendProcessor() {} |
||||
|
|
||||
|
void ImageTransferProcessor::StopProcessor() {} |
||||
|
|
||||
|
void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) { |
||||
|
if (type != Core::HID::ControllerTriggerType::IrSensor) { |
||||
|
return; |
||||
|
} |
||||
|
if (!is_transfer_memory_set) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
const auto camera_data = npad_device->GetCamera(); |
||||
|
|
||||
|
// This indicates how much ambient light is precent
|
||||
|
processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low; |
||||
|
processor_state.sampling_number = camera_data.sample; |
||||
|
|
||||
|
if (camera_data.format != current_config.origin_format) { |
||||
|
LOG_WARNING(Service_IRS, "Wrong Input format {} expected {}", camera_data.format, |
||||
|
current_config.origin_format); |
||||
|
memset(transfer_memory, 0, GetDataSize(current_config.trimming_format)); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (current_config.origin_format > current_config.trimming_format) { |
||||
|
LOG_WARNING(Service_IRS, "Origin format {} is smaller than trimming format {}", |
||||
|
current_config.origin_format, current_config.trimming_format); |
||||
|
memset(transfer_memory, 0, GetDataSize(current_config.trimming_format)); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
std::vector<u8> window_data{}; |
||||
|
const auto origin_width = GetDataWidth(current_config.origin_format); |
||||
|
const auto origin_height = GetDataHeight(current_config.origin_format); |
||||
|
const auto trimming_width = GetDataWidth(current_config.trimming_format); |
||||
|
const auto trimming_height = GetDataHeight(current_config.trimming_format); |
||||
|
window_data.resize(GetDataSize(current_config.trimming_format)); |
||||
|
|
||||
|
if (trimming_width + current_config.trimming_start_x > origin_width || |
||||
|
trimming_height + current_config.trimming_start_y > origin_height) { |
||||
|
LOG_WARNING(Service_IRS, |
||||
|
"Trimming area ({}, {}, {}, {}) is outside of origin area ({}, {})", |
||||
|
current_config.trimming_start_x, current_config.trimming_start_y, |
||||
|
trimming_width, trimming_height, origin_width, origin_height); |
||||
|
memset(transfer_memory, 0, GetDataSize(current_config.trimming_format)); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
for (std::size_t y = 0; y < trimming_height; y++) { |
||||
|
for (std::size_t x = 0; x < trimming_width; x++) { |
||||
|
const std::size_t window_index = (y * trimming_width) + x; |
||||
|
const std::size_t origin_index = |
||||
|
((y + current_config.trimming_start_y) * origin_width) + x + |
||||
|
current_config.trimming_start_x; |
||||
|
window_data[window_index] = camera_data.data[origin_index]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
memcpy(transfer_memory, window_data.data(), GetDataSize(current_config.trimming_format)); |
||||
|
|
||||
|
if (!IsProcessorActive()) { |
||||
|
StartProcessor(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void ImageTransferProcessor::SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config) { |
||||
|
current_config.camera_config.exposure_time = config.camera_config.exposure_time; |
||||
|
current_config.camera_config.gain = config.camera_config.gain; |
||||
|
current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; |
||||
|
current_config.camera_config.light_target = |
||||
|
static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); |
||||
|
current_config.origin_format = |
||||
|
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format); |
||||
|
current_config.trimming_format = |
||||
|
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format); |
||||
|
current_config.trimming_start_x = 0; |
||||
|
current_config.trimming_start_y = 0; |
||||
|
|
||||
|
npad_device->SetCameraFormat(current_config.origin_format); |
||||
|
} |
||||
|
|
||||
|
void ImageTransferProcessor::SetConfig( |
||||
|
Core::IrSensor::PackedImageTransferProcessorExConfig config) { |
||||
|
current_config.camera_config.exposure_time = config.camera_config.exposure_time; |
||||
|
current_config.camera_config.gain = config.camera_config.gain; |
||||
|
current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; |
||||
|
current_config.camera_config.light_target = |
||||
|
static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); |
||||
|
current_config.origin_format = |
||||
|
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.origin_format); |
||||
|
current_config.trimming_format = |
||||
|
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.trimming_format); |
||||
|
current_config.trimming_start_x = config.trimming_start_x; |
||||
|
current_config.trimming_start_y = config.trimming_start_y; |
||||
|
|
||||
|
npad_device->SetCameraFormat(current_config.origin_format); |
||||
|
} |
||||
|
|
||||
|
void ImageTransferProcessor::SetTransferMemoryPointer(u8* t_mem) { |
||||
|
is_transfer_memory_set = true; |
||||
|
transfer_memory = t_mem; |
||||
|
} |
||||
|
|
||||
|
Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState( |
||||
|
std::vector<u8>& data) const { |
||||
|
const auto size = GetDataSize(current_config.trimming_format); |
||||
|
data.resize(size); |
||||
|
memcpy(data.data(), transfer_memory, size); |
||||
|
return processor_state; |
||||
|
} |
||||
|
|
||||
|
} // namespace Service::IRS
|
||||
@ -0,0 +1,73 @@ |
|||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
||||
|
// SPDX-License-Identifier: GPL-3.0-or-later |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include "common/common_types.h" |
||||
|
#include "core/hid/irs_types.h" |
||||
|
#include "core/hle/service/hid/irsensor/processor_base.h" |
||||
|
|
||||
|
namespace Core::HID { |
||||
|
class EmulatedController; |
||||
|
} // namespace Core::HID |
||||
|
|
||||
|
namespace Service::IRS { |
||||
|
class ImageTransferProcessor final : public ProcessorBase { |
||||
|
public: |
||||
|
explicit ImageTransferProcessor(Core::HID::HIDCore& hid_core_, |
||||
|
Core::IrSensor::DeviceFormat& device_format, |
||||
|
std::size_t npad_index); |
||||
|
~ImageTransferProcessor() override; |
||||
|
|
||||
|
// Called when the processor is initialized |
||||
|
void StartProcessor() override; |
||||
|
|
||||
|
// Called when the processor is suspended |
||||
|
void SuspendProcessor() override; |
||||
|
|
||||
|
// Called when the processor is stopped |
||||
|
void StopProcessor() override; |
||||
|
|
||||
|
// Sets config parameters of the camera |
||||
|
void SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config); |
||||
|
void SetConfig(Core::IrSensor::PackedImageTransferProcessorExConfig config); |
||||
|
|
||||
|
// Transfer memory where the image data will be stored |
||||
|
void SetTransferMemoryPointer(u8* t_mem); |
||||
|
|
||||
|
Core::IrSensor::ImageTransferProcessorState GetState(std::vector<u8>& data) const; |
||||
|
|
||||
|
private: |
||||
|
// This is nn::irsensor::ImageTransferProcessorConfig |
||||
|
struct ImageTransferProcessorConfig { |
||||
|
Core::IrSensor::CameraConfig camera_config; |
||||
|
Core::IrSensor::ImageTransferProcessorFormat format; |
||||
|
}; |
||||
|
static_assert(sizeof(ImageTransferProcessorConfig) == 0x20, |
||||
|
"ImageTransferProcessorConfig is an invalid size"); |
||||
|
|
||||
|
// This is nn::irsensor::ImageTransferProcessorExConfig |
||||
|
struct ImageTransferProcessorExConfig { |
||||
|
Core::IrSensor::CameraConfig camera_config; |
||||
|
Core::IrSensor::ImageTransferProcessorFormat origin_format; |
||||
|
Core::IrSensor::ImageTransferProcessorFormat trimming_format; |
||||
|
u16 trimming_start_x; |
||||
|
u16 trimming_start_y; |
||||
|
bool is_external_light_filter_enabled; |
||||
|
INSERT_PADDING_BYTES(3); |
||||
|
}; |
||||
|
static_assert(sizeof(ImageTransferProcessorExConfig) == 0x28, |
||||
|
"ImageTransferProcessorExConfig is an invalid size"); |
||||
|
|
||||
|
void OnControllerUpdate(Core::HID::ControllerTriggerType type); |
||||
|
|
||||
|
ImageTransferProcessorExConfig current_config{}; |
||||
|
Core::IrSensor::ImageTransferProcessorState processor_state{}; |
||||
|
Core::IrSensor::DeviceFormat& device; |
||||
|
Core::HID::EmulatedController* npad_device; |
||||
|
int callback_key{}; |
||||
|
|
||||
|
u8* transfer_memory = nullptr; |
||||
|
bool is_transfer_memory_set = false; |
||||
|
}; |
||||
|
} // namespace Service::IRS |
||||
@ -0,0 +1,27 @@ |
|||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
|
||||
|
#include "core/hle/service/hid/irsensor/ir_led_processor.h"
|
||||
|
|
||||
|
namespace Service::IRS { |
||||
|
IrLedProcessor::IrLedProcessor(Core::IrSensor::DeviceFormat& device_format) |
||||
|
: device(device_format) { |
||||
|
device.mode = Core::IrSensor::IrSensorMode::IrLedProcessor; |
||||
|
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; |
||||
|
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; |
||||
|
} |
||||
|
|
||||
|
IrLedProcessor::~IrLedProcessor() = default; |
||||
|
|
||||
|
void IrLedProcessor::StartProcessor() {} |
||||
|
|
||||
|
void IrLedProcessor::SuspendProcessor() {} |
||||
|
|
||||
|
void IrLedProcessor::StopProcessor() {} |
||||
|
|
||||
|
void IrLedProcessor::SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config) { |
||||
|
current_config.light_target = |
||||
|
static_cast<Core::IrSensor::CameraLightTarget>(config.light_target); |
||||
|
} |
||||
|
|
||||
|
} // namespace Service::IRS
|
||||
@ -0,0 +1,47 @@ |
|||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
||||
|
// SPDX-License-Identifier: GPL-3.0-or-later |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include "common/bit_field.h" |
||||
|
#include "common/common_types.h" |
||||
|
#include "core/hid/irs_types.h" |
||||
|
#include "core/hle/service/hid/irsensor/processor_base.h" |
||||
|
|
||||
|
namespace Service::IRS { |
||||
|
class IrLedProcessor final : public ProcessorBase { |
||||
|
public: |
||||
|
explicit IrLedProcessor(Core::IrSensor::DeviceFormat& device_format); |
||||
|
~IrLedProcessor() override; |
||||
|
|
||||
|
// Called when the processor is initialized |
||||
|
void StartProcessor() override; |
||||
|
|
||||
|
// Called when the processor is suspended |
||||
|
void SuspendProcessor() override; |
||||
|
|
||||
|
// Called when the processor is stopped |
||||
|
void StopProcessor() override; |
||||
|
|
||||
|
// Sets config parameters of the camera |
||||
|
void SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config); |
||||
|
|
||||
|
private: |
||||
|
// This is nn::irsensor::IrLedProcessorConfig |
||||
|
struct IrLedProcessorConfig { |
||||
|
Core::IrSensor::CameraLightTarget light_target; |
||||
|
}; |
||||
|
static_assert(sizeof(IrLedProcessorConfig) == 0x4, "IrLedProcessorConfig is an invalid size"); |
||||
|
|
||||
|
struct IrLedProcessorState { |
||||
|
s64 sampling_number; |
||||
|
u64 timestamp; |
||||
|
std::array<u8, 0x8> data; |
||||
|
}; |
||||
|
static_assert(sizeof(IrLedProcessorState) == 0x18, "IrLedProcessorState is an invalid size"); |
||||
|
|
||||
|
IrLedProcessorConfig current_config{}; |
||||
|
Core::IrSensor::DeviceFormat& device; |
||||
|
}; |
||||
|
|
||||
|
} // namespace Service::IRS |
||||
@ -0,0 +1,34 @@ |
|||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
|
||||
|
#include "core/hle/service/hid/irsensor/moment_processor.h"
|
||||
|
|
||||
|
namespace Service::IRS { |
||||
|
MomentProcessor::MomentProcessor(Core::IrSensor::DeviceFormat& device_format) |
||||
|
: device(device_format) { |
||||
|
device.mode = Core::IrSensor::IrSensorMode::MomentProcessor; |
||||
|
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; |
||||
|
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; |
||||
|
} |
||||
|
|
||||
|
MomentProcessor::~MomentProcessor() = default; |
||||
|
|
||||
|
void MomentProcessor::StartProcessor() {} |
||||
|
|
||||
|
void MomentProcessor::SuspendProcessor() {} |
||||
|
|
||||
|
void MomentProcessor::StopProcessor() {} |
||||
|
|
||||
|
void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig config) { |
||||
|
current_config.camera_config.exposure_time = config.camera_config.exposure_time; |
||||
|
current_config.camera_config.gain = config.camera_config.gain; |
||||
|
current_config.camera_config.is_negative_used = config.camera_config.is_negative_used; |
||||
|
current_config.camera_config.light_target = |
||||
|
static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target); |
||||
|
current_config.window_of_interest = config.window_of_interest; |
||||
|
current_config.preprocess = |
||||
|
static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess); |
||||
|
current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold; |
||||
|
} |
||||
|
|
||||
|
} // namespace Service::IRS
|
||||
@ -0,0 +1,61 @@ |
|||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
||||
|
// SPDX-License-Identifier: GPL-3.0-or-later |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include "common/bit_field.h" |
||||
|
#include "common/common_types.h" |
||||
|
#include "core/hid/irs_types.h" |
||||
|
#include "core/hle/service/hid/irsensor/processor_base.h" |
||||
|
|
||||
|
namespace Service::IRS { |
||||
|
class MomentProcessor final : public ProcessorBase { |
||||
|
public: |
||||
|
explicit MomentProcessor(Core::IrSensor::DeviceFormat& device_format); |
||||
|
~MomentProcessor() override; |
||||
|
|
||||
|
// Called when the processor is initialized |
||||
|
void StartProcessor() override; |
||||
|
|
||||
|
// Called when the processor is suspended |
||||
|
void SuspendProcessor() override; |
||||
|
|
||||
|
// Called when the processor is stopped |
||||
|
void StopProcessor() override; |
||||
|
|
||||
|
// Sets config parameters of the camera |
||||
|
void SetConfig(Core::IrSensor::PackedMomentProcessorConfig config); |
||||
|
|
||||
|
private: |
||||
|
// This is nn::irsensor::MomentProcessorConfig |
||||
|
struct MomentProcessorConfig { |
||||
|
Core::IrSensor::CameraConfig camera_config; |
||||
|
Core::IrSensor::IrsRect window_of_interest; |
||||
|
Core::IrSensor::MomentProcessorPreprocess preprocess; |
||||
|
u32 preprocess_intensity_threshold; |
||||
|
}; |
||||
|
static_assert(sizeof(MomentProcessorConfig) == 0x28, |
||||
|
"MomentProcessorConfig is an invalid size"); |
||||
|
|
||||
|
// This is nn::irsensor::MomentStatistic |
||||
|
struct MomentStatistic { |
||||
|
f32 average_intensity; |
||||
|
Core::IrSensor::IrsCentroid centroid; |
||||
|
}; |
||||
|
static_assert(sizeof(MomentStatistic) == 0xC, "MomentStatistic is an invalid size"); |
||||
|
|
||||
|
// This is nn::irsensor::MomentProcessorState |
||||
|
struct MomentProcessorState { |
||||
|
s64 sampling_number; |
||||
|
u64 timestamp; |
||||
|
Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; |
||||
|
INSERT_PADDING_BYTES(4); |
||||
|
std::array<MomentStatistic, 0x30> stadistic; |
||||
|
}; |
||||
|
static_assert(sizeof(MomentProcessorState) == 0x258, "MomentProcessorState is an invalid size"); |
||||
|
|
||||
|
MomentProcessorConfig current_config{}; |
||||
|
Core::IrSensor::DeviceFormat& device; |
||||
|
}; |
||||
|
|
||||
|
} // namespace Service::IRS |
||||
@ -0,0 +1,26 @@ |
|||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
|
||||
|
#include "core/hle/service/hid/irsensor/pointing_processor.h"
|
||||
|
|
||||
|
namespace Service::IRS { |
||||
|
PointingProcessor::PointingProcessor(Core::IrSensor::DeviceFormat& device_format) |
||||
|
: device(device_format) { |
||||
|
device.mode = Core::IrSensor::IrSensorMode::PointingProcessorMarker; |
||||
|
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; |
||||
|
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; |
||||
|
} |
||||
|
|
||||
|
PointingProcessor::~PointingProcessor() = default; |
||||
|
|
||||
|
void PointingProcessor::StartProcessor() {} |
||||
|
|
||||
|
void PointingProcessor::SuspendProcessor() {} |
||||
|
|
||||
|
void PointingProcessor::StopProcessor() {} |
||||
|
|
||||
|
void PointingProcessor::SetConfig(Core::IrSensor::PackedPointingProcessorConfig config) { |
||||
|
current_config.window_of_interest = config.window_of_interest; |
||||
|
} |
||||
|
|
||||
|
} // namespace Service::IRS
|
||||
@ -0,0 +1,61 @@ |
|||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
||||
|
// SPDX-License-Identifier: GPL-3.0-or-later |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include "common/common_types.h" |
||||
|
#include "core/hid/irs_types.h" |
||||
|
#include "core/hle/service/hid/irsensor/processor_base.h" |
||||
|
|
||||
|
namespace Service::IRS { |
||||
|
class PointingProcessor final : public ProcessorBase { |
||||
|
public: |
||||
|
explicit PointingProcessor(Core::IrSensor::DeviceFormat& device_format); |
||||
|
~PointingProcessor() override; |
||||
|
|
||||
|
// Called when the processor is initialized |
||||
|
void StartProcessor() override; |
||||
|
|
||||
|
// Called when the processor is suspended |
||||
|
void SuspendProcessor() override; |
||||
|
|
||||
|
// Called when the processor is stopped |
||||
|
void StopProcessor() override; |
||||
|
|
||||
|
// Sets config parameters of the camera |
||||
|
void SetConfig(Core::IrSensor::PackedPointingProcessorConfig config); |
||||
|
|
||||
|
private: |
||||
|
// This is nn::irsensor::PointingProcessorConfig |
||||
|
struct PointingProcessorConfig { |
||||
|
Core::IrSensor::IrsRect window_of_interest; |
||||
|
}; |
||||
|
static_assert(sizeof(PointingProcessorConfig) == 0x8, |
||||
|
"PointingProcessorConfig is an invalid size"); |
||||
|
|
||||
|
struct PointingProcessorMarkerData { |
||||
|
u8 pointing_status; |
||||
|
INSERT_PADDING_BYTES(3); |
||||
|
u32 unknown; |
||||
|
float unkown_float1; |
||||
|
float position_x; |
||||
|
float position_y; |
||||
|
float unkown_float2; |
||||
|
Core::IrSensor::IrsRect window_of_interest; |
||||
|
}; |
||||
|
static_assert(sizeof(PointingProcessorMarkerData) == 0x20, |
||||
|
"PointingProcessorMarkerData is an invalid size"); |
||||
|
|
||||
|
struct PointingProcessorMarkerState { |
||||
|
s64 sampling_number; |
||||
|
u64 timestamp; |
||||
|
std::array<PointingProcessorMarkerData, 0x3> data; |
||||
|
}; |
||||
|
static_assert(sizeof(PointingProcessorMarkerState) == 0x70, |
||||
|
"PointingProcessorMarkerState is an invalid size"); |
||||
|
|
||||
|
PointingProcessorConfig current_config{}; |
||||
|
Core::IrSensor::DeviceFormat& device; |
||||
|
}; |
||||
|
|
||||
|
} // namespace Service::IRS |
||||
@ -0,0 +1,67 @@ |
|||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
|
||||
|
#include "core/hle/service/hid/irsensor/processor_base.h"
|
||||
|
|
||||
|
namespace Service::IRS { |
||||
|
|
||||
|
ProcessorBase::ProcessorBase() {} |
||||
|
ProcessorBase::~ProcessorBase() = default; |
||||
|
|
||||
|
bool ProcessorBase::IsProcessorActive() const { |
||||
|
return is_active; |
||||
|
} |
||||
|
|
||||
|
std::size_t ProcessorBase::GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const { |
||||
|
switch (format) { |
||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size320x240: |
||||
|
return 320 * 240; |
||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size160x120: |
||||
|
return 160 * 120; |
||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size80x60: |
||||
|
return 80 * 60; |
||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size40x30: |
||||
|
return 40 * 30; |
||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size20x15: |
||||
|
return 20 * 15; |
||||
|
default: |
||||
|
return 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
std::size_t ProcessorBase::GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const { |
||||
|
switch (format) { |
||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size320x240: |
||||
|
return 320; |
||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size160x120: |
||||
|
return 160; |
||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size80x60: |
||||
|
return 80; |
||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size40x30: |
||||
|
return 40; |
||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size20x15: |
||||
|
return 20; |
||||
|
default: |
||||
|
return 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
std::size_t ProcessorBase::GetDataHeight( |
||||
|
Core::IrSensor::ImageTransferProcessorFormat format) const { |
||||
|
switch (format) { |
||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size320x240: |
||||
|
return 240; |
||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size160x120: |
||||
|
return 120; |
||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size80x60: |
||||
|
return 60; |
||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size40x30: |
||||
|
return 30; |
||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size20x15: |
||||
|
return 15; |
||||
|
default: |
||||
|
return 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} // namespace Service::IRS
|
||||
@ -0,0 +1,33 @@ |
|||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
||||
|
// SPDX-License-Identifier: GPL-3.0-or-later |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include "common/common_types.h" |
||||
|
#include "core/hid/irs_types.h" |
||||
|
|
||||
|
namespace Service::IRS { |
||||
|
class ProcessorBase { |
||||
|
public: |
||||
|
explicit ProcessorBase(); |
||||
|
virtual ~ProcessorBase(); |
||||
|
|
||||
|
virtual void StartProcessor() = 0; |
||||
|
virtual void SuspendProcessor() = 0; |
||||
|
virtual void StopProcessor() = 0; |
||||
|
|
||||
|
bool IsProcessorActive() const; |
||||
|
|
||||
|
protected: |
||||
|
/// Returns the number of bytes the image uses |
||||
|
std::size_t GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const; |
||||
|
|
||||
|
/// Returns the width of the image |
||||
|
std::size_t GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const; |
||||
|
|
||||
|
/// Returns the height of the image |
||||
|
std::size_t GetDataHeight(Core::IrSensor::ImageTransferProcessorFormat format) const; |
||||
|
|
||||
|
bool is_active{false}; |
||||
|
}; |
||||
|
} // namespace Service::IRS |
||||
@ -0,0 +1,29 @@ |
|||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
|
||||
|
#include "core/hle/service/hid/irsensor/tera_plugin_processor.h"
|
||||
|
|
||||
|
namespace Service::IRS { |
||||
|
TeraPluginProcessor::TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format) |
||||
|
: device(device_format) { |
||||
|
device.mode = Core::IrSensor::IrSensorMode::TeraPluginProcessor; |
||||
|
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected; |
||||
|
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped; |
||||
|
} |
||||
|
|
||||
|
TeraPluginProcessor::~TeraPluginProcessor() = default; |
||||
|
|
||||
|
void TeraPluginProcessor::StartProcessor() {} |
||||
|
|
||||
|
void TeraPluginProcessor::SuspendProcessor() {} |
||||
|
|
||||
|
void TeraPluginProcessor::StopProcessor() {} |
||||
|
|
||||
|
void TeraPluginProcessor::SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config) { |
||||
|
current_config.mode = config.mode; |
||||
|
current_config.unknown_1 = config.unknown_1; |
||||
|
current_config.unknown_2 = config.unknown_2; |
||||
|
current_config.unknown_3 = config.unknown_3; |
||||
|
} |
||||
|
|
||||
|
} // namespace Service::IRS
|
||||
@ -0,0 +1,53 @@ |
|||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project |
||||
|
// SPDX-License-Identifier: GPL-3.0-or-later |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include "common/bit_field.h" |
||||
|
#include "common/common_types.h" |
||||
|
#include "core/hid/irs_types.h" |
||||
|
#include "core/hle/service/hid/irsensor/processor_base.h" |
||||
|
|
||||
|
namespace Service::IRS { |
||||
|
class TeraPluginProcessor final : public ProcessorBase { |
||||
|
public: |
||||
|
explicit TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format); |
||||
|
~TeraPluginProcessor() override; |
||||
|
|
||||
|
// Called when the processor is initialized |
||||
|
void StartProcessor() override; |
||||
|
|
||||
|
// Called when the processor is suspended |
||||
|
void SuspendProcessor() override; |
||||
|
|
||||
|
// Called when the processor is stopped |
||||
|
void StopProcessor() override; |
||||
|
|
||||
|
// Sets config parameters of the camera |
||||
|
void SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config); |
||||
|
|
||||
|
private: |
||||
|
// This is nn::irsensor::TeraPluginProcessorConfig |
||||
|
struct TeraPluginProcessorConfig { |
||||
|
u8 mode; |
||||
|
u8 unknown_1; |
||||
|
u8 unknown_2; |
||||
|
u8 unknown_3; |
||||
|
}; |
||||
|
static_assert(sizeof(TeraPluginProcessorConfig) == 0x4, |
||||
|
"TeraPluginProcessorConfig is an invalid size"); |
||||
|
|
||||
|
struct TeraPluginProcessorState { |
||||
|
s64 sampling_number; |
||||
|
u64 timestamp; |
||||
|
Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level; |
||||
|
std::array<u8, 0x12c> data; |
||||
|
}; |
||||
|
static_assert(sizeof(TeraPluginProcessorState) == 0x140, |
||||
|
"TeraPluginProcessorState is an invalid size"); |
||||
|
|
||||
|
TeraPluginProcessorConfig current_config{}; |
||||
|
Core::IrSensor::DeviceFormat& device; |
||||
|
}; |
||||
|
|
||||
|
} // namespace Service::IRS |
||||
@ -0,0 +1,82 @@ |
|||||
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
|
||||
|
#include <fmt/format.h>
|
||||
|
|
||||
|
#include "common/param_package.h"
|
||||
|
#include "input_common/drivers/camera.h"
|
||||
|
|
||||
|
namespace InputCommon { |
||||
|
constexpr PadIdentifier identifier = { |
||||
|
.guid = Common::UUID{}, |
||||
|
.port = 0, |
||||
|
.pad = 0, |
||||
|
}; |
||||
|
|
||||
|
Camera::Camera(std::string input_engine_) : InputEngine(std::move(input_engine_)) { |
||||
|
PreSetController(identifier); |
||||
|
} |
||||
|
|
||||
|
void Camera::SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data) { |
||||
|
const std::size_t desired_width = getImageWidth(); |
||||
|
const std::size_t desired_height = getImageHeight(); |
||||
|
status.data.resize(desired_width * desired_height); |
||||
|
|
||||
|
// Resize image to desired format
|
||||
|
for (std::size_t y = 0; y < desired_height; y++) { |
||||
|
for (std::size_t x = 0; x < desired_width; x++) { |
||||
|
const std::size_t pixel_index = y * desired_width + x; |
||||
|
const std::size_t old_x = width * x / desired_width; |
||||
|
const std::size_t old_y = height * y / desired_height; |
||||
|
const std::size_t data_pixel_index = old_y * width + old_x; |
||||
|
status.data[pixel_index] = static_cast<u8>(data[data_pixel_index] & 0xFF); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
SetCamera(identifier, status); |
||||
|
} |
||||
|
|
||||
|
std::size_t Camera::getImageWidth() const { |
||||
|
switch (status.format) { |
||||
|
case Common::Input::CameraFormat::Size320x240: |
||||
|
return 320; |
||||
|
case Common::Input::CameraFormat::Size160x120: |
||||
|
return 160; |
||||
|
case Common::Input::CameraFormat::Size80x60: |
||||
|
return 80; |
||||
|
case Common::Input::CameraFormat::Size40x30: |
||||
|
return 40; |
||||
|
case Common::Input::CameraFormat::Size20x15: |
||||
|
return 20; |
||||
|
case Common::Input::CameraFormat::None: |
||||
|
default: |
||||
|
return 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
std::size_t Camera::getImageHeight() const { |
||||
|
switch (status.format) { |
||||
|
case Common::Input::CameraFormat::Size320x240: |
||||
|
return 240; |
||||
|
case Common::Input::CameraFormat::Size160x120: |
||||
|
return 120; |
||||
|
case Common::Input::CameraFormat::Size80x60: |
||||
|
return 60; |
||||
|
case Common::Input::CameraFormat::Size40x30: |
||||
|
return 30; |
||||
|
case Common::Input::CameraFormat::Size20x15: |
||||
|
return 15; |
||||
|
case Common::Input::CameraFormat::None: |
||||
|
default: |
||||
|
return 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Common::Input::CameraError Camera::SetCameraFormat( |
||||
|
[[maybe_unused]] const PadIdentifier& identifier_, |
||||
|
const Common::Input::CameraFormat camera_format) { |
||||
|
status.format = camera_format; |
||||
|
return Common::Input::CameraError::None; |
||||
|
} |
||||
|
|
||||
|
} // namespace InputCommon
|
||||
@ -0,0 +1,29 @@ |
|||||
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
||||
|
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include "input_common/input_engine.h" |
||||
|
|
||||
|
namespace InputCommon { |
||||
|
|
||||
|
/** |
||||
|
* A button device factory representing a keyboard. It receives keyboard events and forward them |
||||
|
* to all button devices it created. |
||||
|
*/ |
||||
|
class Camera final : public InputEngine { |
||||
|
public: |
||||
|
explicit Camera(std::string input_engine_); |
||||
|
|
||||
|
void SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data); |
||||
|
|
||||
|
std::size_t getImageWidth() const; |
||||
|
std::size_t getImageHeight() const; |
||||
|
|
||||
|
Common::Input::CameraError SetCameraFormat(const PadIdentifier& identifier_, |
||||
|
Common::Input::CameraFormat camera_format) override; |
||||
|
|
||||
|
Common::Input::CameraStatus status{}; |
||||
|
}; |
||||
|
|
||||
|
} // namespace InputCommon |
||||
@ -0,0 +1,138 @@ |
|||||
|
// Text : Copyright 2022 yuzu Emulator Project
|
||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
|
||||
|
#include <memory>
|
||||
|
#include <QCameraImageCapture>
|
||||
|
#include <QCameraInfo>
|
||||
|
#include <QStandardItemModel>
|
||||
|
#include <QTimer>
|
||||
|
|
||||
|
#include "input_common/drivers/camera.h"
|
||||
|
#include "input_common/main.h"
|
||||
|
#include "ui_configure_camera.h"
|
||||
|
#include "yuzu/configuration/config.h"
|
||||
|
#include "yuzu/configuration/configure_camera.h"
|
||||
|
|
||||
|
ConfigureCamera::ConfigureCamera(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_) |
||||
|
: QDialog(parent), input_subsystem{input_subsystem_}, |
||||
|
ui(std::make_unique<Ui::ConfigureCamera>()) { |
||||
|
ui->setupUi(this); |
||||
|
|
||||
|
connect(ui->restore_defaults_button, &QPushButton::clicked, this, |
||||
|
&ConfigureCamera::RestoreDefaults); |
||||
|
connect(ui->preview_button, &QPushButton::clicked, this, &ConfigureCamera::PreviewCamera); |
||||
|
|
||||
|
auto blank_image = QImage(320, 240, QImage::Format::Format_RGB32); |
||||
|
blank_image.fill(Qt::black); |
||||
|
DisplayCapturedFrame(0, blank_image); |
||||
|
|
||||
|
LoadConfiguration(); |
||||
|
resize(0, 0); |
||||
|
} |
||||
|
|
||||
|
ConfigureCamera::~ConfigureCamera() = default; |
||||
|
|
||||
|
void ConfigureCamera::PreviewCamera() { |
||||
|
const auto index = ui->ir_sensor_combo_box->currentIndex(); |
||||
|
bool camera_found = false; |
||||
|
const QList<QCameraInfo> cameras = QCameraInfo::availableCameras(); |
||||
|
for (const QCameraInfo& cameraInfo : cameras) { |
||||
|
if (input_devices[index] == cameraInfo.deviceName().toStdString() || |
||||
|
input_devices[index] == "Auto") { |
||||
|
LOG_INFO(Frontend, "Selected Camera {} {}", cameraInfo.description().toStdString(), |
||||
|
cameraInfo.deviceName().toStdString()); |
||||
|
camera = std::make_unique<QCamera>(cameraInfo); |
||||
|
camera_found = true; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Clear previous frame
|
||||
|
auto blank_image = QImage(320, 240, QImage::Format::Format_RGB32); |
||||
|
blank_image.fill(Qt::black); |
||||
|
DisplayCapturedFrame(0, blank_image); |
||||
|
|
||||
|
if (!camera_found) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
camera_capture = std::make_unique<QCameraImageCapture>(camera.get()); |
||||
|
connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this, |
||||
|
&ConfigureCamera::DisplayCapturedFrame); |
||||
|
camera->unload(); |
||||
|
camera->setCaptureMode(QCamera::CaptureViewfinder); |
||||
|
camera->load(); |
||||
|
camera->start(); |
||||
|
|
||||
|
pending_snapshots = 0; |
||||
|
is_virtual_camera = false; |
||||
|
|
||||
|
camera_timer = std::make_unique<QTimer>(); |
||||
|
connect(camera_timer.get(), &QTimer::timeout, [this] { |
||||
|
// If the camera doesn't capture, test for virtual cameras
|
||||
|
if (pending_snapshots > 5) { |
||||
|
is_virtual_camera = true; |
||||
|
} |
||||
|
// Virtual cameras like obs need to reset the camera every capture
|
||||
|
if (is_virtual_camera) { |
||||
|
camera->stop(); |
||||
|
camera->start(); |
||||
|
} |
||||
|
pending_snapshots++; |
||||
|
camera_capture->capture(); |
||||
|
}); |
||||
|
|
||||
|
camera_timer->start(250); |
||||
|
} |
||||
|
|
||||
|
void ConfigureCamera::DisplayCapturedFrame(int requestId, const QImage& img) { |
||||
|
LOG_INFO(Frontend, "ImageCaptured {} {}", img.width(), img.height()); |
||||
|
const auto converted = img.scaled(320, 240, Qt::AspectRatioMode::IgnoreAspectRatio, |
||||
|
Qt::TransformationMode::SmoothTransformation); |
||||
|
ui->preview_box->setPixmap(QPixmap::fromImage(converted)); |
||||
|
pending_snapshots = 0; |
||||
|
} |
||||
|
|
||||
|
void ConfigureCamera::changeEvent(QEvent* event) { |
||||
|
if (event->type() == QEvent::LanguageChange) { |
||||
|
RetranslateUI(); |
||||
|
} |
||||
|
|
||||
|
QDialog::changeEvent(event); |
||||
|
} |
||||
|
|
||||
|
void ConfigureCamera::RetranslateUI() { |
||||
|
ui->retranslateUi(this); |
||||
|
} |
||||
|
|
||||
|
void ConfigureCamera::ApplyConfiguration() { |
||||
|
const auto index = ui->ir_sensor_combo_box->currentIndex(); |
||||
|
Settings::values.ir_sensor_device.SetValue(input_devices[index]); |
||||
|
} |
||||
|
|
||||
|
void ConfigureCamera::LoadConfiguration() { |
||||
|
input_devices.clear(); |
||||
|
ui->ir_sensor_combo_box->clear(); |
||||
|
input_devices.push_back("Auto"); |
||||
|
ui->ir_sensor_combo_box->addItem(tr("Auto")); |
||||
|
const auto cameras = QCameraInfo::availableCameras(); |
||||
|
for (const QCameraInfo& cameraInfo : cameras) { |
||||
|
input_devices.push_back(cameraInfo.deviceName().toStdString()); |
||||
|
ui->ir_sensor_combo_box->addItem(cameraInfo.description()); |
||||
|
} |
||||
|
|
||||
|
const auto current_device = Settings::values.ir_sensor_device.GetValue(); |
||||
|
|
||||
|
const auto devices_it = std::find_if( |
||||
|
input_devices.begin(), input_devices.end(), |
||||
|
[current_device](const std::string& device) { return device == current_device; }); |
||||
|
const int device_index = |
||||
|
devices_it != input_devices.end() |
||||
|
? static_cast<int>(std::distance(input_devices.begin(), devices_it)) |
||||
|
: 0; |
||||
|
ui->ir_sensor_combo_box->setCurrentIndex(device_index); |
||||
|
} |
||||
|
|
||||
|
void ConfigureCamera::RestoreDefaults() { |
||||
|
ui->ir_sensor_combo_box->setCurrentIndex(0); |
||||
|
} |
||||
@ -0,0 +1,54 @@ |
|||||
|
// Text : Copyright 2022 yuzu Emulator Project |
||||
|
// SPDX-License-Identifier: GPL-3.0-or-later |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <memory> |
||||
|
#include <QDialog> |
||||
|
|
||||
|
class QTimer; |
||||
|
class QCamera; |
||||
|
class QCameraImageCapture; |
||||
|
|
||||
|
namespace InputCommon { |
||||
|
class InputSubsystem; |
||||
|
} // namespace InputCommon |
||||
|
|
||||
|
namespace Ui { |
||||
|
class ConfigureCamera; |
||||
|
} |
||||
|
|
||||
|
class ConfigureCamera : public QDialog { |
||||
|
Q_OBJECT |
||||
|
|
||||
|
public: |
||||
|
explicit ConfigureCamera(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_); |
||||
|
~ConfigureCamera() override; |
||||
|
|
||||
|
void ApplyConfiguration(); |
||||
|
|
||||
|
private: |
||||
|
void changeEvent(QEvent* event) override; |
||||
|
void RetranslateUI(); |
||||
|
|
||||
|
/// Load configuration settings. |
||||
|
void LoadConfiguration(); |
||||
|
|
||||
|
/// Restore all buttons to their default values. |
||||
|
void RestoreDefaults(); |
||||
|
|
||||
|
void DisplayCapturedFrame(int requestId, const QImage& img); |
||||
|
|
||||
|
/// Loads and signals the current selected camera to display a frame |
||||
|
void PreviewCamera(); |
||||
|
|
||||
|
InputCommon::InputSubsystem* input_subsystem; |
||||
|
|
||||
|
bool is_virtual_camera; |
||||
|
int pending_snapshots; |
||||
|
std::unique_ptr<QCamera> camera; |
||||
|
std::unique_ptr<QCameraImageCapture> camera_capture; |
||||
|
std::unique_ptr<QTimer> camera_timer; |
||||
|
std::vector<std::string> input_devices; |
||||
|
std::unique_ptr<Ui::ConfigureCamera> ui; |
||||
|
}; |
||||
@ -0,0 +1,170 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<ui version="4.0"> |
||||
|
<class>ConfigureCamera</class> |
||||
|
<widget class="QDialog" name="ConfigureCamera"> |
||||
|
<property name="geometry"> |
||||
|
<rect> |
||||
|
<x>0</x> |
||||
|
<y>0</y> |
||||
|
<width>298</width> |
||||
|
<height>339</height> |
||||
|
</rect> |
||||
|
</property> |
||||
|
<property name="windowTitle"> |
||||
|
<string>Configure Infrared Camera</string> |
||||
|
</property> |
||||
|
<layout class="QVBoxLayout" name="verticalLayout"> |
||||
|
<item> |
||||
|
<widget class="QLabel" name="label_2"> |
||||
|
<property name="minimumSize"> |
||||
|
<size> |
||||
|
<width>280</width> |
||||
|
<height>0</height> |
||||
|
</size> |
||||
|
</property> |
||||
|
<property name="text"> |
||||
|
<string>Select where the image of the emulated camera comes from. It may be a virtual camera or a real camera.</string> |
||||
|
</property> |
||||
|
<property name="wordWrap"> |
||||
|
<bool>true</bool> |
||||
|
</property> |
||||
|
</widget> |
||||
|
</item> |
||||
|
<item> |
||||
|
<spacer name="verticalSpacer_2"> |
||||
|
<property name="orientation"> |
||||
|
<enum>Qt::Vertical</enum> |
||||
|
</property> |
||||
|
<property name="sizeType"> |
||||
|
<enum>QSizePolicy::Fixed</enum> |
||||
|
</property> |
||||
|
<property name="sizeHint" stdset="0"> |
||||
|
<size> |
||||
|
<width>20</width> |
||||
|
<height>20</height> |
||||
|
</size> |
||||
|
</property> |
||||
|
</spacer> |
||||
|
</item> |
||||
|
<item> |
||||
|
<widget class="QGroupBox" name="gridGroupBox"> |
||||
|
<property name="title"> |
||||
|
<string>Camera Image Source:</string> |
||||
|
</property> |
||||
|
<layout class="QGridLayout" name="gridLayout"> |
||||
|
<item row="0" column="0"> |
||||
|
<spacer name="horizontalSpacer"> |
||||
|
<property name="orientation"> |
||||
|
<enum>Qt::Horizontal</enum> |
||||
|
</property> |
||||
|
<property name="sizeHint" stdset="0"> |
||||
|
<size> |
||||
|
<width>40</width> |
||||
|
<height>20</height> |
||||
|
</size> |
||||
|
</property> |
||||
|
</spacer> |
||||
|
</item> |
||||
|
<item row="0" column="1"> |
||||
|
<widget class="QLabel" name="label_3"> |
||||
|
<property name="text"> |
||||
|
<string>Input device:</string> |
||||
|
</property> |
||||
|
</widget> |
||||
|
</item> |
||||
|
<item row="0" column="2"> |
||||
|
<widget class="QComboBox" name="ir_sensor_combo_box"/> |
||||
|
</item> |
||||
|
<item row="0" column="3"> |
||||
|
<spacer name="horizontalSpacer_2"> |
||||
|
<property name="orientation"> |
||||
|
<enum>Qt::Horizontal</enum> |
||||
|
</property> |
||||
|
<property name="sizeHint" stdset="0"> |
||||
|
<size> |
||||
|
<width>40</width> |
||||
|
<height>20</height> |
||||
|
</size> |
||||
|
</property> |
||||
|
</spacer> |
||||
|
</item> |
||||
|
</layout> |
||||
|
</widget> |
||||
|
</item><item> |
||||
|
<widget class="QGroupBox" name="previewBox"> |
||||
|
<property name="title"> |
||||
|
<string>Preview</string> |
||||
|
</property> |
||||
|
<layout class="QVBoxLayout" name="verticalLayout_3"> |
||||
|
<item> |
||||
|
<widget class="QLabel" name="preview_box"> |
||||
|
<property name="minimumSize"> |
||||
|
<size> |
||||
|
<width>320</width> |
||||
|
<height>240</height> |
||||
|
</size> |
||||
|
</property> |
||||
|
<property name="toolTip"> |
||||
|
<string>Resolution: 320*240</string> |
||||
|
</property> |
||||
|
</widget> |
||||
|
</item> |
||||
|
<item> |
||||
|
<widget class="QPushButton" name="preview_button"> |
||||
|
<property name="text"> |
||||
|
<string>Click to preview</string> |
||||
|
</property> |
||||
|
</widget> |
||||
|
</item> |
||||
|
</layout> |
||||
|
</widget> |
||||
|
</item> |
||||
|
<item> |
||||
|
<spacer name="verticalSpacer"> |
||||
|
<property name="orientation"> |
||||
|
<enum>Qt::Vertical</enum> |
||||
|
</property> |
||||
|
<property name="sizeHint" stdset="0"> |
||||
|
<size> |
||||
|
<width>20</width> |
||||
|
<height>40</height> |
||||
|
</size> |
||||
|
</property> |
||||
|
</spacer> |
||||
|
</item> |
||||
|
<item> |
||||
|
<layout class="QHBoxLayout" name="horizontalLayout"> |
||||
|
<item> |
||||
|
<widget class="QPushButton" name="restore_defaults_button"> |
||||
|
<property name="text"> |
||||
|
<string>Restore Defaults</string> |
||||
|
</property> |
||||
|
</widget> |
||||
|
</item> |
||||
|
<item> |
||||
|
<widget class="QDialogButtonBox" name="buttonBox"> |
||||
|
<property name="standardButtons"> |
||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> |
||||
|
</property> |
||||
|
</widget> |
||||
|
</item> |
||||
|
</layout> |
||||
|
</item> |
||||
|
</layout> |
||||
|
</widget> |
||||
|
<resources/> |
||||
|
<connections> |
||||
|
<connection> |
||||
|
<sender>buttonBox</sender> |
||||
|
<signal>accepted()</signal> |
||||
|
<receiver>ConfigureCamera</receiver> |
||||
|
<slot>accept()</slot> |
||||
|
</connection> |
||||
|
<connection> |
||||
|
<sender>buttonBox</sender> |
||||
|
<signal>rejected()</signal> |
||||
|
<receiver>ConfigureCamera</receiver> |
||||
|
<slot>reject()</slot> |
||||
|
</connection> |
||||
|
</connections> |
||||
|
</ui> |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue