mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-07-07 23:41:18 +12:00
Add camera settings window and save camera config
This commit is contained in:
parent
791e358bf7
commit
b9000bd667
10 changed files with 284 additions and 59 deletions
|
@ -234,8 +234,7 @@ namespace camera
|
||||||
return CAM_STATUS_UNINITIALIZED;
|
return CAM_STATUS_UNINITIALIZED;
|
||||||
if (s_instance.isOpen)
|
if (s_instance.isOpen)
|
||||||
return CAM_STATUS_DEVICE_IN_USE;
|
return CAM_STATUS_DEVICE_IN_USE;
|
||||||
if (!CameraManager::instance().Open(false))
|
CameraManager::instance().Open();
|
||||||
return CAM_STATUS_UVC_ERROR;
|
|
||||||
s_instance.isOpen = true;
|
s_instance.isOpen = true;
|
||||||
coreinit::OSSignalEvent(s_cameraOpenEvent);
|
coreinit::OSSignalEvent(s_cameraOpenEvent);
|
||||||
s_instance.inTargetBuffers.Clear();
|
s_instance.inTargetBuffers.Clear();
|
||||||
|
|
|
@ -9,3 +9,7 @@ set_property(TARGET CemuCamera PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<C
|
||||||
|
|
||||||
target_include_directories(CemuCamera PUBLIC "../")
|
target_include_directories(CemuCamera PUBLIC "../")
|
||||||
target_link_libraries(CemuCamera PRIVATE CemuCommon CemuUtil PUBLIC openpnp-capture)
|
target_link_libraries(CemuCamera PRIVATE CemuCommon CemuUtil PUBLIC openpnp-capture)
|
||||||
|
|
||||||
|
if (ENABLE_WXWIDGETS)
|
||||||
|
target_link_libraries(CemuCamera PRIVATE wx::base)
|
||||||
|
endif()
|
|
@ -1,5 +1,6 @@
|
||||||
#include "CameraManager.h"
|
#include "CameraManager.h"
|
||||||
#include "Rgb2Nv12.h"
|
#include "Rgb2Nv12.h"
|
||||||
|
#include "config/CemuConfig.h"
|
||||||
#include "util/helpers/helpers.h"
|
#include "util/helpers/helpers.h"
|
||||||
|
|
||||||
constexpr unsigned CAMERA_WIDTH = 640;
|
constexpr unsigned CAMERA_WIDTH = 640;
|
||||||
|
@ -10,93 +11,157 @@ CameraManager::CameraManager()
|
||||||
: m_ctx(Cap_createContext()),
|
: m_ctx(Cap_createContext()),
|
||||||
m_rgbBuffer(CAMERA_WIDTH * CAMERA_HEIGHT * 3),
|
m_rgbBuffer(CAMERA_WIDTH * CAMERA_HEIGHT * 3),
|
||||||
m_nv12Buffer(CAMERA_PITCH * CAMERA_HEIGHT * 3 / 2),
|
m_nv12Buffer(CAMERA_PITCH * CAMERA_HEIGHT * 3 / 2),
|
||||||
m_refCount(0)
|
m_refCount(0), m_capturing(false), m_running(true)
|
||||||
{
|
{
|
||||||
// Set default device if it exists
|
m_captureThread = std::thread(&CameraManager::CaptureWorker, this);
|
||||||
if (Cap_getDeviceCount(m_ctx) > 0)
|
|
||||||
m_device = 0;
|
const auto uniqueId = GetConfig().camera_id.GetValue();
|
||||||
|
if (!uniqueId.empty())
|
||||||
|
{
|
||||||
|
const auto devices = EnumerateDevices();
|
||||||
|
for (CapDeviceID deviceId = 0; deviceId < devices.size(); ++deviceId)
|
||||||
|
{
|
||||||
|
if (devices[deviceId].uniqueId == uniqueId)
|
||||||
|
{
|
||||||
|
m_device = deviceId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ResetBuffers();
|
||||||
}
|
}
|
||||||
CameraManager::~CameraManager()
|
CameraManager::~CameraManager()
|
||||||
{
|
{
|
||||||
Close();
|
m_running = false;
|
||||||
|
CloseStream();
|
||||||
Cap_releaseContext(m_ctx);
|
Cap_releaseContext(m_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CameraManager::SetDevice(uint32 deviceNo)
|
void CameraManager::SetDevice(uint32 deviceNo)
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(m_mutex);
|
std::scoped_lock lock(m_mutex);
|
||||||
if (m_device == deviceNo)
|
CloseStream();
|
||||||
|
if (deviceNo == DEVICE_NONE)
|
||||||
|
{
|
||||||
|
m_device = std::nullopt;
|
||||||
|
ResetBuffers();
|
||||||
return;
|
return;
|
||||||
if (m_stream)
|
}
|
||||||
Cap_closeStream(m_ctx, *m_stream);
|
|
||||||
m_device = deviceNo;
|
m_device = deviceNo;
|
||||||
|
if (m_refCount != 0)
|
||||||
|
OpenStream();
|
||||||
}
|
}
|
||||||
bool CameraManager::Open(bool weak)
|
std::vector<CameraManager::DeviceInfo> CameraManager::EnumerateDevices()
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(m_mutex);
|
std::scoped_lock lock(m_mutex);
|
||||||
if (!m_device)
|
std::vector<DeviceInfo> infos;
|
||||||
return false;
|
const auto deviceCount = Cap_getDeviceCount(m_ctx);
|
||||||
if (m_refCount == 0)
|
for (CapDeviceID deviceNo = 0; deviceNo < deviceCount; ++deviceNo)
|
||||||
{
|
{
|
||||||
const auto formatCount = Cap_getNumFormats(m_ctx, *m_device);
|
const auto uniqueId = Cap_getDeviceUniqueID(m_ctx, deviceNo);
|
||||||
CapFormatID formatId = 0;
|
const auto name = Cap_getDeviceName(m_ctx, deviceNo);
|
||||||
for (; formatId < formatCount; ++formatId)
|
|
||||||
|
if (name)
|
||||||
|
infos.emplace_back(uniqueId, fmt::format("{}: {}", deviceNo + 1, name));
|
||||||
|
else
|
||||||
|
infos.emplace_back(uniqueId, fmt::format("{}: Unknown", deviceNo + 1));
|
||||||
|
}
|
||||||
|
return infos;
|
||||||
|
}
|
||||||
|
void CameraManager::SaveDevice()
|
||||||
|
{
|
||||||
|
std::scoped_lock lock(m_mutex);
|
||||||
|
if (m_device)
|
||||||
|
GetConfig().camera_id = Cap_getDeviceUniqueID(m_ctx, *m_device);
|
||||||
|
else
|
||||||
|
GetConfig().camera_id = "";
|
||||||
|
}
|
||||||
|
void CameraManager::Open()
|
||||||
|
{
|
||||||
|
std::scoped_lock lock(m_mutex);
|
||||||
|
if (m_device && m_refCount == 0)
|
||||||
{
|
{
|
||||||
CapFormatInfo formatInfo;
|
OpenStream();
|
||||||
if (Cap_getFormatInfo(m_ctx, *m_device, formatId, &formatInfo) != CAPRESULT_OK)
|
|
||||||
continue;
|
|
||||||
if (formatInfo.width == CAMERA_WIDTH && formatInfo.height == CAMERA_HEIGHT)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if (formatId == formatCount)
|
|
||||||
return false;
|
|
||||||
auto stream = Cap_openStream(m_ctx, *m_device, formatId);
|
|
||||||
if (stream == -1)
|
|
||||||
return false;
|
|
||||||
m_capturing = true;
|
|
||||||
m_stream = stream;
|
|
||||||
m_captureThread = std::thread(&CameraManager::CaptureWorker, this);
|
|
||||||
}
|
|
||||||
if (!weak)
|
|
||||||
m_refCount += 1;
|
m_refCount += 1;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
void CameraManager::Close()
|
void CameraManager::Close()
|
||||||
{
|
{
|
||||||
{
|
|
||||||
std::scoped_lock lock(m_mutex);
|
std::scoped_lock lock(m_mutex);
|
||||||
if (m_refCount == 0)
|
if (m_refCount == 0)
|
||||||
return;
|
return;
|
||||||
m_refCount -= 1;
|
m_refCount -= 1;
|
||||||
if (m_refCount != 0)
|
if (m_refCount != 0)
|
||||||
return;
|
return;
|
||||||
Cap_closeStream(m_ctx, *m_stream);
|
CloseStream();
|
||||||
m_stream = std::nullopt;
|
}
|
||||||
m_capturing = false;
|
|
||||||
|
std::optional<CapFormatID> CameraManager::FindCorrectFormat()
|
||||||
|
{
|
||||||
|
const auto formatCount = Cap_getNumFormats(m_ctx, *m_device);
|
||||||
|
for (CapFormatID formatId = 0; formatId < formatCount; ++formatId)
|
||||||
|
{
|
||||||
|
CapFormatInfo formatInfo;
|
||||||
|
if (Cap_getFormatInfo(m_ctx, *m_device, formatId, &formatInfo) != CAPRESULT_OK)
|
||||||
|
continue;
|
||||||
|
if (formatInfo.width == CAMERA_WIDTH && formatInfo.height == CAMERA_HEIGHT)
|
||||||
|
return formatId;
|
||||||
}
|
}
|
||||||
m_captureThread.join();
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
void CameraManager::ResetBuffers()
|
||||||
|
{
|
||||||
|
std::ranges::fill(m_rgbBuffer, 0);
|
||||||
|
std::ranges::fill_n(m_nv12Buffer.begin(), CAMERA_WIDTH * CAMERA_PITCH, 16);
|
||||||
|
std::ranges::fill_n(m_nv12Buffer.begin() + CAMERA_WIDTH * CAMERA_PITCH, (CAMERA_WIDTH / 2), 128);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CameraManager::FillNV12Buffer(uint8* nv12Buffer) const
|
void CameraManager::FillNV12Buffer(uint8* nv12Buffer) const
|
||||||
{
|
{
|
||||||
std::shared_lock lock(m_mutex);
|
std::scoped_lock lock(m_mutex);
|
||||||
std::ranges::copy(m_nv12Buffer, nv12Buffer);
|
std::ranges::copy(m_nv12Buffer, nv12Buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CameraManager::FillRGBBuffer(uint8* rgbBuffer) const
|
||||||
|
{
|
||||||
|
std::scoped_lock lock(m_mutex);
|
||||||
|
std::ranges::copy(m_rgbBuffer, rgbBuffer);
|
||||||
|
}
|
||||||
void CameraManager::CaptureWorker()
|
void CameraManager::CaptureWorker()
|
||||||
{
|
{
|
||||||
SetThreadName("CameraManager");
|
SetThreadName("CameraManager");
|
||||||
|
while (m_running)
|
||||||
|
{
|
||||||
while (m_capturing)
|
while (m_capturing)
|
||||||
{
|
{
|
||||||
{
|
m_mutex.lock();
|
||||||
std::scoped_lock lock(m_mutex);
|
if (m_stream && Cap_hasNewFrame(m_ctx, *m_stream) &&
|
||||||
bool frameAvailable = Cap_hasNewFrame(m_ctx, *m_stream);
|
|
||||||
if (frameAvailable &&
|
|
||||||
Cap_captureFrame(m_ctx, *m_stream, m_rgbBuffer.data(), m_rgbBuffer.size()) == CAPRESULT_OK)
|
Cap_captureFrame(m_ctx, *m_stream, m_rgbBuffer.data(), m_rgbBuffer.size()) == CAPRESULT_OK)
|
||||||
{
|
|
||||||
Rgb2Nv12(m_rgbBuffer.data(), CAMERA_WIDTH, CAMERA_HEIGHT, m_nv12Buffer.data(), CAMERA_PITCH);
|
Rgb2Nv12(m_rgbBuffer.data(), CAMERA_WIDTH, CAMERA_HEIGHT, m_nv12Buffer.data(), CAMERA_PITCH);
|
||||||
|
m_mutex.unlock();
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(30));
|
||||||
}
|
}
|
||||||
}
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(16));
|
|
||||||
std::this_thread::yield();
|
std::this_thread::yield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void CameraManager::OpenStream()
|
||||||
|
{
|
||||||
|
const auto formatId = FindCorrectFormat();
|
||||||
|
if (!formatId)
|
||||||
|
return;
|
||||||
|
const auto stream = Cap_openStream(m_ctx, *m_device, *formatId);
|
||||||
|
if (stream == -1)
|
||||||
|
return;
|
||||||
|
m_capturing = true;
|
||||||
|
m_stream = stream;
|
||||||
|
}
|
||||||
|
void CameraManager::CloseStream()
|
||||||
|
{
|
||||||
|
m_capturing = false;
|
||||||
|
if (m_stream)
|
||||||
|
{
|
||||||
|
Cap_closeStream(m_ctx, *m_stream);
|
||||||
|
m_stream = std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <shared_mutex>
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
#include <openpnp-capture.h>
|
#include <openpnp-capture.h>
|
||||||
#include "util/helpers/Singleton.h"
|
#include "util/helpers/Singleton.h"
|
||||||
|
|
||||||
|
@ -13,19 +15,32 @@ class CameraManager : public Singleton<CameraManager>
|
||||||
int m_refCount;
|
int m_refCount;
|
||||||
std::thread m_captureThread;
|
std::thread m_captureThread;
|
||||||
std::atomic_bool m_capturing;
|
std::atomic_bool m_capturing;
|
||||||
mutable std::shared_mutex m_mutex;
|
std::atomic_bool m_running;
|
||||||
|
mutable std::recursive_mutex m_mutex;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
constexpr static uint32 DEVICE_NONE = std::numeric_limits<uint32>::max();
|
||||||
|
struct DeviceInfo
|
||||||
|
{
|
||||||
|
std::string uniqueId;
|
||||||
|
std::string name;
|
||||||
|
};
|
||||||
CameraManager();
|
CameraManager();
|
||||||
~CameraManager();
|
~CameraManager();
|
||||||
|
|
||||||
void SetDevice(uint32 deviceNo);
|
void SetDevice(uint32 deviceNo);
|
||||||
|
std::vector<DeviceInfo> EnumerateDevices();
|
||||||
|
void SaveDevice();
|
||||||
|
|
||||||
bool Open(bool weak);
|
void Open();
|
||||||
void Close();
|
void Close();
|
||||||
|
|
||||||
void FillNV12Buffer(uint8* nv12Buffer) const;
|
void FillNV12Buffer(uint8* nv12Buffer) const;
|
||||||
|
void FillRGBBuffer(uint8* rgbBuffer) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::optional<CapFormatID> FindCorrectFormat();
|
||||||
|
void ResetBuffers();
|
||||||
void CaptureWorker();
|
void CaptureWorker();
|
||||||
|
void OpenStream();
|
||||||
|
void CloseStream();
|
||||||
};
|
};
|
|
@ -343,6 +343,9 @@ void CemuConfig::Load(XMLConfigParser& parser)
|
||||||
dsu_client.host = dsuc.get_attribute("host", dsu_client.host);
|
dsu_client.host = dsuc.get_attribute("host", dsu_client.host);
|
||||||
dsu_client.port = dsuc.get_attribute("port", dsu_client.port);
|
dsu_client.port = dsuc.get_attribute("port", dsu_client.port);
|
||||||
|
|
||||||
|
auto camera = parser.get("Camera");
|
||||||
|
camera_id = camera.get_attribute("Id", "");
|
||||||
|
|
||||||
// emulatedusbdevices
|
// emulatedusbdevices
|
||||||
auto usbdevices = parser.get("EmulatedUsbDevices");
|
auto usbdevices = parser.get("EmulatedUsbDevices");
|
||||||
emulated_usb_devices.emulate_skylander_portal = usbdevices.get("EmulateSkylanderPortal", emulated_usb_devices.emulate_skylander_portal);
|
emulated_usb_devices.emulate_skylander_portal = usbdevices.get("EmulateSkylanderPortal", emulated_usb_devices.emulate_skylander_portal);
|
||||||
|
@ -544,6 +547,9 @@ void CemuConfig::Save(XMLConfigParser& parser)
|
||||||
dsuc.set_attribute("host", dsu_client.host);
|
dsuc.set_attribute("host", dsu_client.host);
|
||||||
dsuc.set_attribute("port", dsu_client.port);
|
dsuc.set_attribute("port", dsu_client.port);
|
||||||
|
|
||||||
|
auto camera = config.set("Camera");
|
||||||
|
camera.set("Id", camera_id);
|
||||||
|
|
||||||
// emulated usb devices
|
// emulated usb devices
|
||||||
auto usbdevices = config.set("EmulatedUsbDevices");
|
auto usbdevices = config.set("EmulatedUsbDevices");
|
||||||
usbdevices.set("EmulateSkylanderPortal", emulated_usb_devices.emulate_skylander_portal.GetValue());
|
usbdevices.set("EmulateSkylanderPortal", emulated_usb_devices.emulate_skylander_portal.GetValue());
|
||||||
|
|
|
@ -499,6 +499,9 @@ struct CemuConfig
|
||||||
ConfigValue<uint16> port{ 26760 };
|
ConfigValue<uint16> port{ 26760 };
|
||||||
}dsu_client{};
|
}dsu_client{};
|
||||||
|
|
||||||
|
// camera
|
||||||
|
ConfigValue<std::string> camera_id;
|
||||||
|
|
||||||
// debug
|
// debug
|
||||||
ConfigValueBounds<CrashDump> crash_dump{ CrashDump::Disabled };
|
ConfigValueBounds<CrashDump> crash_dump{ CrashDump::Disabled };
|
||||||
ConfigValue<uint16> gdb_port{ 1337 };
|
ConfigValue<uint16> gdb_port{ 1337 };
|
||||||
|
|
|
@ -127,6 +127,8 @@ add_library(CemuGui
|
||||||
wxcomponents/unchecked.xpm
|
wxcomponents/unchecked.xpm
|
||||||
wxgui.h
|
wxgui.h
|
||||||
wxHelper.h
|
wxHelper.h
|
||||||
|
CameraSettingsWindow.cpp
|
||||||
|
CameraSettingsWindow.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set_property(TARGET CemuGui PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
set_property(TARGET CemuGui PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||||
|
@ -139,6 +141,7 @@ target_include_directories(CemuGui PUBLIC ${RAPIDJSON_INCLUDE_DIRS})
|
||||||
target_link_libraries(CemuGui PRIVATE
|
target_link_libraries(CemuGui PRIVATE
|
||||||
CemuAudio
|
CemuAudio
|
||||||
CemuCafe
|
CemuCafe
|
||||||
|
CemuCamera
|
||||||
CemuCommon
|
CemuCommon
|
||||||
CemuComponents
|
CemuComponents
|
||||||
CemuConfig
|
CemuConfig
|
||||||
|
|
98
src/gui/CameraSettingsWindow.cpp
Normal file
98
src/gui/CameraSettingsWindow.cpp
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
#include "CameraSettingsWindow.h"
|
||||||
|
|
||||||
|
#include "camera/CameraManager.h"
|
||||||
|
|
||||||
|
#include <wx/sizer.h>
|
||||||
|
#include <wx/dcclient.h>
|
||||||
|
#include <wx/dcbuffer.h>
|
||||||
|
#include <wx/rawbmp.h>
|
||||||
|
|
||||||
|
constexpr unsigned CAMERA_WIDTH = 640;
|
||||||
|
constexpr unsigned CAMERA_HEIGHT = 480;
|
||||||
|
|
||||||
|
CameraSettingsWindow::CameraSettingsWindow(wxWindow* parent)
|
||||||
|
: wxDialog(parent, wxID_ANY, _("Camera settings"), wxDefaultPosition),
|
||||||
|
m_imageBitmap(CAMERA_WIDTH, CAMERA_HEIGHT, 24), m_imageBuffer(CAMERA_WIDTH * CAMERA_HEIGHT * 3)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
auto* rootSizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
{
|
||||||
|
auto* topSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
{
|
||||||
|
wxString choices[] = {_("None")};
|
||||||
|
m_cameraChoice = new wxChoice(this, wxID_ANY, wxDefaultPosition, {300, -1}, 1, choices);
|
||||||
|
m_cameraChoice->Bind(wxEVT_CHOICE, &CameraSettingsWindow::OnSelectCameraChoice, this);
|
||||||
|
|
||||||
|
m_refreshButton = new wxButton(this, wxID_ANY, wxString::FromUTF8("⟳"));
|
||||||
|
m_refreshButton->Fit();
|
||||||
|
m_refreshButton->Bind(wxEVT_BUTTON, &CameraSettingsWindow::OnRefreshPressed, this);
|
||||||
|
wxQueueEvent(m_refreshButton, new wxCommandEvent{wxEVT_BUTTON});
|
||||||
|
|
||||||
|
topSizer->Add(m_cameraChoice);
|
||||||
|
topSizer->Add(m_refreshButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_imageWindow = new wxWindow(this, wxID_ANY, wxDefaultPosition, {CAMERA_WIDTH, CAMERA_HEIGHT});
|
||||||
|
rootSizer->Add(topSizer);
|
||||||
|
rootSizer->Add(m_imageWindow, wxEXPAND);
|
||||||
|
}
|
||||||
|
SetSizerAndFit(rootSizer);
|
||||||
|
CameraManager::instance().Open();
|
||||||
|
m_imageUpdateTimer.Bind(wxEVT_TIMER, &CameraSettingsWindow::UpdateImage, this);
|
||||||
|
m_imageUpdateTimer.Start(33, wxTIMER_CONTINUOUS);
|
||||||
|
this->Bind(wxEVT_CLOSE_WINDOW, &CameraSettingsWindow::OnClose, this);
|
||||||
|
}
|
||||||
|
void CameraSettingsWindow::OnSelectCameraChoice(wxCommandEvent&)
|
||||||
|
{
|
||||||
|
const auto selection = m_cameraChoice->GetSelection();
|
||||||
|
if (selection < 0)
|
||||||
|
return;
|
||||||
|
if (selection == 0)
|
||||||
|
CameraManager::instance().SetDevice(CameraManager::DEVICE_NONE);
|
||||||
|
else
|
||||||
|
CameraManager::instance().SetDevice(selection - 1);
|
||||||
|
}
|
||||||
|
void CameraSettingsWindow::OnRefreshPressed(wxCommandEvent&)
|
||||||
|
{
|
||||||
|
wxArrayString choices = {_("None")};
|
||||||
|
for (const auto& entry : CameraManager::instance().EnumerateDevices())
|
||||||
|
{
|
||||||
|
choices.push_back(entry.name);
|
||||||
|
}
|
||||||
|
m_cameraChoice->Set(choices);
|
||||||
|
wxArrayString str;
|
||||||
|
|
||||||
|
}
|
||||||
|
void CameraSettingsWindow::UpdateImage(const wxTimerEvent&)
|
||||||
|
{
|
||||||
|
CameraManager::instance().FillRGBBuffer(m_imageBuffer.data());
|
||||||
|
|
||||||
|
wxNativePixelData data{m_imageBitmap};
|
||||||
|
if (!data)
|
||||||
|
return;
|
||||||
|
wxNativePixelData::Iterator p{data};
|
||||||
|
for (auto row = 0u; row < CAMERA_HEIGHT; ++row)
|
||||||
|
{
|
||||||
|
const auto* rowPtr = m_imageBuffer.data() + row * CAMERA_WIDTH * 3;
|
||||||
|
wxNativePixelData::Iterator rowStart = p;
|
||||||
|
for (auto col = 0u; col < CAMERA_WIDTH; ++col, ++p)
|
||||||
|
{
|
||||||
|
auto* colour = rowPtr + col * 3;
|
||||||
|
p.Red() = colour[0];
|
||||||
|
p.Green() = colour[1];
|
||||||
|
p.Blue() = colour[2];
|
||||||
|
}
|
||||||
|
p = rowStart;
|
||||||
|
p.OffsetY(data, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
wxClientDC dc{m_imageWindow};
|
||||||
|
dc.DrawBitmap(m_imageBitmap, 0, 0);
|
||||||
|
}
|
||||||
|
void CameraSettingsWindow::OnClose(wxCloseEvent& event)
|
||||||
|
{
|
||||||
|
CameraManager::instance().Close();
|
||||||
|
CameraManager::instance().SaveDevice();
|
||||||
|
event.Skip();
|
||||||
|
}
|
22
src/gui/CameraSettingsWindow.h
Normal file
22
src/gui/CameraSettingsWindow.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#pragma once
|
||||||
|
#include <wx/dialog.h>
|
||||||
|
#include <wx/timer.h>
|
||||||
|
#include <wx/choice.h>
|
||||||
|
#include <wx/bmpbuttn.h>
|
||||||
|
|
||||||
|
class CameraSettingsWindow : public wxDialog
|
||||||
|
{
|
||||||
|
wxChoice* m_cameraChoice;
|
||||||
|
wxButton* m_refreshButton;
|
||||||
|
wxWindow* m_imageWindow;
|
||||||
|
wxBitmap m_imageBitmap;
|
||||||
|
wxTimer m_imageUpdateTimer;
|
||||||
|
std::vector<uint8> m_imageBuffer;
|
||||||
|
public:
|
||||||
|
explicit CameraSettingsWindow(wxWindow* parent);
|
||||||
|
void OnSelectCameraChoice(wxCommandEvent&);
|
||||||
|
void OnRefreshPressed(wxCommandEvent&);
|
||||||
|
void UpdateImage(const wxTimerEvent&);
|
||||||
|
void OnClose(wxCloseEvent& event);
|
||||||
|
};
|
||||||
|
|
|
@ -61,6 +61,7 @@
|
||||||
#include "gamemode_client.h"
|
#include "gamemode_client.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "CameraSettingsWindow.h"
|
||||||
#include "Cafe/TitleList/TitleInfo.h"
|
#include "Cafe/TitleList/TitleInfo.h"
|
||||||
#include "Cafe/TitleList/TitleList.h"
|
#include "Cafe/TitleList/TitleList.h"
|
||||||
#include "wxHelper.h"
|
#include "wxHelper.h"
|
||||||
|
@ -91,6 +92,7 @@ enum
|
||||||
MAINFRAME_MENU_ID_OPTIONS_GENERAL2,
|
MAINFRAME_MENU_ID_OPTIONS_GENERAL2,
|
||||||
MAINFRAME_MENU_ID_OPTIONS_AUDIO,
|
MAINFRAME_MENU_ID_OPTIONS_AUDIO,
|
||||||
MAINFRAME_MENU_ID_OPTIONS_INPUT,
|
MAINFRAME_MENU_ID_OPTIONS_INPUT,
|
||||||
|
MAINFRAME_MENU_ID_OPTIONS_CAMERA,
|
||||||
// options -> account
|
// options -> account
|
||||||
MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_1 = 20350,
|
MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_1 = 20350,
|
||||||
MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_12 = 20350 + 11,
|
MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_12 = 20350 + 11,
|
||||||
|
@ -186,6 +188,7 @@ EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_GENERAL, MainWindow::OnOptionsInput)
|
||||||
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_GENERAL2, MainWindow::OnOptionsInput)
|
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_GENERAL2, MainWindow::OnOptionsInput)
|
||||||
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_AUDIO, MainWindow::OnOptionsInput)
|
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_AUDIO, MainWindow::OnOptionsInput)
|
||||||
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_INPUT, MainWindow::OnOptionsInput)
|
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_INPUT, MainWindow::OnOptionsInput)
|
||||||
|
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_CAMERA, MainWindow::OnOptionsInput)
|
||||||
// tools menu
|
// tools menu
|
||||||
EVT_MENU(MAINFRAME_MENU_ID_TOOLS_MEMORY_SEARCHER, MainWindow::OnToolsInput)
|
EVT_MENU(MAINFRAME_MENU_ID_TOOLS_MEMORY_SEARCHER, MainWindow::OnToolsInput)
|
||||||
EVT_MENU(MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER, MainWindow::OnToolsInput)
|
EVT_MENU(MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER, MainWindow::OnToolsInput)
|
||||||
|
@ -921,6 +924,12 @@ void MainWindow::OnOptionsInput(wxCommandEvent& event)
|
||||||
frame->Destroy();
|
frame->Destroy();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case MAINFRAME_MENU_ID_OPTIONS_CAMERA:
|
||||||
|
{
|
||||||
|
auto* frame = new CameraSettingsWindow(this);
|
||||||
|
frame->ShowModal();
|
||||||
|
frame->Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2159,6 +2168,7 @@ void MainWindow::RecreateMenu()
|
||||||
optionsMenu->AppendSeparator();
|
optionsMenu->AppendSeparator();
|
||||||
optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_GENERAL2, _("&General settings"));
|
optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_GENERAL2, _("&General settings"));
|
||||||
optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_INPUT, _("&Input settings"));
|
optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_INPUT, _("&Input settings"));
|
||||||
|
optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_CAMERA, _("&Camera settings"));
|
||||||
|
|
||||||
optionsMenu->AppendSeparator();
|
optionsMenu->AppendSeparator();
|
||||||
optionsMenu->AppendSubMenu(m_optionsAccountMenu, _("&Active account"));
|
optionsMenu->AppendSubMenu(m_optionsAccountMenu, _("&Active account"));
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue