Cemu/src/gui/wxgui/CameraSettingsWindow.cpp
2026-07-21 16:03:53 +01:00

180 lines
5.8 KiB
C++

#include "CameraSettingsWindow.h"
#include "input/camera/CameraManager.h"
#include <wx/button.h>
#include <wx/combobox.h>
#include <wx/sizer.h>
#include <wx/dcbuffer.h>
#include <wx/rawbmp.h>
#include <wx/stattext.h>
struct wxCameraDeviceUniqueId : public wxClientData
{
std::string id;
explicit wxCameraDeviceUniqueId(std::string id) : id(std::move(id))
{
}
};
CameraSettingsWindow::CameraSettingsWindow(wxWindow* parent)
: wxDialog(parent, wxID_ANY, _("Camera settings"), wxDefaultPosition),
m_imageBitmap(CameraManager::CAMERA_WIDTH, CameraManager::CAMERA_HEIGHT, 24),
m_imageBuffer(CameraManager::CAMERA_RGB_BUFFER_SIZE)
{
CameraManager::Open();
auto* rootSizer = new wxBoxSizer(wxVERTICAL);
{
auto* topSizer = new wxBoxSizer(wxHORIZONTAL);
{
m_cameraComboBox = new wxComboBox(this, wxID_ANY);
m_cameraComboBox->Bind(wxEVT_COMBOBOX, &CameraSettingsWindow::OnSelectCameraChoice, this);
m_cameraComboBox->SetToolTip(_("Cameras are only listed if they support 640x480"));
m_refreshButton = new wxButton(this, wxID_ANY, _("Refresh"));
m_refreshButton->Bind(wxEVT_BUTTON, &CameraSettingsWindow::OnRefreshPressed, this);
wxQueueEvent(m_refreshButton, new wxCommandEvent{wxEVT_BUTTON});
topSizer->Add(m_cameraComboBox);
topSizer->Add(m_refreshButton);
}
m_imageWindow = new wxWindow(this, wxID_ANY, wxDefaultPosition,
{CameraManager::CAMERA_WIDTH, CameraManager::CAMERA_HEIGHT});
m_imageWindow->SetBackgroundStyle(wxBG_STYLE_PAINT);
m_statusInfoText = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
wxTE_READONLY);
rootSizer->Add(topSizer);
rootSizer->Add(m_imageWindow, wxEXPAND);
rootSizer->Add(m_statusInfoText);
}
SetSizerAndFit(rootSizer);
m_imageUpdateTimer.Bind(wxEVT_TIMER, &CameraSettingsWindow::Update, this);
m_imageWindow->Bind(wxEVT_PAINT, &CameraSettingsWindow::DrawImage, this);
this->Bind(wxEVT_CLOSE_WINDOW, &CameraSettingsWindow::OnClose, this);
m_imageUpdateTimer.Start(33, wxTIMER_CONTINUOUS);
}
void CameraSettingsWindow::OnSelectCameraChoice(wxCommandEvent&)
{
const auto selection = m_cameraComboBox->GetSelection();
if (selection < 0)
{
m_cameraComboBox->Select(0);
return;
}
if (selection == 0)
CameraManager::ResetDevice();
else
{
auto id = static_cast<wxCameraDeviceUniqueId*>(m_cameraComboBox->GetClientData(selection))->id;
CameraManager::SetDevice(id);
}
}
void CameraSettingsWindow::OnRefreshPressed(wxCommandEvent&)
{
wxArrayString choices = {_("None")};
const auto devices = CameraManager::EnumerateDevices();
const auto currentDevice = CameraManager::GetCurrentDevice();
auto currentIndex = 0;
for (auto i = 0; i < devices.size(); ++i)
{
auto& device = devices[i];
choices.push_back(fmt::format("{} ({})", device.name, device.uniqueId));
if (currentDevice && device.uniqueId == *currentDevice)
currentIndex = i + 1;
}
m_cameraComboBox->Set(choices);
m_cameraComboBox->SetSelection(currentIndex);
// Client data can only be set when there are entries
for (auto i = 0; i < devices.size(); ++i)
{
auto& device = devices[i];
const auto choiceIndex = i + 1;
m_cameraComboBox->SetClientData(choiceIndex, new wxCameraDeviceUniqueId{device.uniqueId});
}
}
void CameraSettingsWindow::Update(const wxTimerEvent&)
{
switch (CameraManager::GetState())
{
using enum CameraManager::State;
case NoSupport:
{
m_statusInfoText->SetLabel(_("This build does not support camera capture"));
return;
}
case NoDevice:
{
m_statusInfoText->SetLabel(_("No camera selected"));
return;
}
case UnsupportedFormat:
{
m_statusInfoText->SetLabel(_("Camera does not support the required format"));
}
case NotOpen:
{
m_statusInfoText->SetLabel(_("Camera is unavailable, or was closed due to an error"));
return;
}
case NeedsPermission:
{
m_statusInfoText->SetLabel(_("Waiting for permission to access camea"));
return;
}
case NoPermission:
{
m_statusInfoText->SetLabel(_("Cemu was denied permission to access camera"));
return;
}
case Capturing:
{
m_statusInfoText->SetLabel(_("Capturing"));
break;
}
default:
cemu_assert(false);
}
CameraManager::FillRGBBuffer(std::span<uint8, CameraManager::CAMERA_RGB_BUFFER_SIZE>(m_imageBuffer));
wxNativePixelData data{m_imageBitmap};
if (!data)
return;
wxNativePixelData::Iterator p{data};
for (auto row = 0u; row < CameraManager::CAMERA_HEIGHT; ++row)
{
const auto* rowPtr = m_imageBuffer.data() + row * CameraManager::CAMERA_WIDTH * 3;
wxNativePixelData::Iterator rowStart = p;
for (auto col = 0u; col < CameraManager::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);
}
m_imageWindow->Refresh();
}
void CameraSettingsWindow::DrawImage(const wxPaintEvent&)
{
wxAutoBufferedPaintDC dc{m_imageWindow};
dc.DrawBitmap(m_imageBitmap, 0, 0);
}
void CameraSettingsWindow::OnClose(wxCloseEvent& event)
{
m_imageUpdateTimer.Stop();
CameraManager::SaveDevice();
CameraManager::Close();
event.Skip();
}