mirror of https://github.com/yuzu-mirror/yuzu
Merge pull request #8484 from german77/irs_release
service: irs: Add camera support, split processors and implement ImageTransferProcessorpull/8/head
commit
5af06d1433
@ -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,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>
|
Loading…
Reference in New Issue