Host: Add 'Auxiliary Render Windows'
i.e. debugging windows
This commit is contained in:
parent
e5c5ba22d7
commit
b2a4010661
|
@ -465,3 +465,167 @@ bool DisplayContainer::event(QEvent* event)
|
|||
|
||||
return res;
|
||||
}
|
||||
|
||||
AuxiliaryDisplayWidget::AuxiliaryDisplayWidget(QWidget* parent, u32 width, u32 height, const QString& title,
|
||||
void* userdata)
|
||||
: QWidget(parent), m_userdata(userdata)
|
||||
{
|
||||
// We want a native window for both D3D and OpenGL.
|
||||
setAutoFillBackground(false);
|
||||
setAttribute(Qt::WA_NativeWindow, true);
|
||||
setAttribute(Qt::WA_NoSystemBackground, true);
|
||||
setAttribute(Qt::WA_PaintOnScreen, true);
|
||||
setAttribute(Qt::WA_KeyCompression, false);
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
setMouseTracking(true);
|
||||
setWindowTitle(title);
|
||||
resize(width, height);
|
||||
}
|
||||
|
||||
AuxiliaryDisplayWidget::~AuxiliaryDisplayWidget() = default;
|
||||
|
||||
QPaintEngine* AuxiliaryDisplayWidget::paintEngine() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool AuxiliaryDisplayWidget::event(QEvent* event)
|
||||
{
|
||||
const QEvent::Type type = event->type();
|
||||
switch (type)
|
||||
{
|
||||
case QEvent::KeyPress:
|
||||
case QEvent::KeyRelease:
|
||||
{
|
||||
g_emu_thread->queueAuxiliaryRenderWindowInputEvent(
|
||||
m_userdata,
|
||||
(type == QEvent::KeyPress) ? Host::AuxiliaryRenderWindowEvent::KeyPressed :
|
||||
Host::AuxiliaryRenderWindowEvent::KeyReleased,
|
||||
Host::AuxiliaryRenderWindowEventParam{.uint_param =
|
||||
static_cast<u32>(static_cast<const QKeyEvent*>(event)->key())});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case QEvent::MouseMove:
|
||||
{
|
||||
const qreal dpr = QtUtils::GetDevicePixelRatioForWidget(this);
|
||||
const QPoint mouse_pos = static_cast<QMouseEvent*>(event)->pos();
|
||||
const float scaled_x = static_cast<float>(static_cast<qreal>(mouse_pos.x()) * dpr);
|
||||
const float scaled_y = static_cast<float>(static_cast<qreal>(mouse_pos.y()) * dpr);
|
||||
|
||||
g_emu_thread->queueAuxiliaryRenderWindowInputEvent(
|
||||
m_userdata, Host::AuxiliaryRenderWindowEvent::MouseMoved,
|
||||
Host::AuxiliaryRenderWindowEventParam{.float_param = scaled_x},
|
||||
Host::AuxiliaryRenderWindowEventParam{.float_param = scaled_y});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case QEvent::MouseButtonPress:
|
||||
case QEvent::MouseButtonDblClick:
|
||||
case QEvent::MouseButtonRelease:
|
||||
{
|
||||
const u32 button_index = CountTrailingZeros(static_cast<u32>(static_cast<const QMouseEvent*>(event)->button()));
|
||||
g_emu_thread->queueAuxiliaryRenderWindowInputEvent(
|
||||
m_userdata,
|
||||
(type == QEvent::MouseButtonRelease) ? Host::AuxiliaryRenderWindowEvent::MouseReleased :
|
||||
Host::AuxiliaryRenderWindowEvent::MousePressed,
|
||||
Host::AuxiliaryRenderWindowEventParam{.uint_param = button_index},
|
||||
Host::AuxiliaryRenderWindowEventParam{.uint_param = BoolToUInt32(type == QEvent::MouseButtonRelease)});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case QEvent::Wheel:
|
||||
{
|
||||
const QWheelEvent* wheel_event = static_cast<QWheelEvent*>(event);
|
||||
const QPoint delta = wheel_event->angleDelta();
|
||||
if (delta.x() != 0 || delta.y())
|
||||
{
|
||||
g_emu_thread->queueAuxiliaryRenderWindowInputEvent(
|
||||
m_userdata, Host::AuxiliaryRenderWindowEvent::MouseWheel,
|
||||
Host::AuxiliaryRenderWindowEventParam{.float_param = static_cast<float>(delta.x())},
|
||||
Host::AuxiliaryRenderWindowEventParam{.float_param = static_cast<float>(delta.y())});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case QEvent::Close:
|
||||
{
|
||||
if (m_destroying)
|
||||
return QWidget::event(event);
|
||||
|
||||
g_emu_thread->queueAuxiliaryRenderWindowInputEvent(m_userdata, Host::AuxiliaryRenderWindowEvent::CloseRequest);
|
||||
event->ignore();
|
||||
return true;
|
||||
}
|
||||
|
||||
case QEvent::Paint:
|
||||
case QEvent::Resize:
|
||||
{
|
||||
QWidget::event(event);
|
||||
|
||||
const float dpr = QtUtils::GetDevicePixelRatioForWidget(this);
|
||||
const u32 scaled_width =
|
||||
static_cast<u32>(std::max(static_cast<int>(std::ceil(static_cast<qreal>(width()) * dpr)), 1));
|
||||
const u32 scaled_height =
|
||||
static_cast<u32>(std::max(static_cast<int>(std::ceil(static_cast<qreal>(height()) * dpr)), 1));
|
||||
|
||||
// avoid spamming resize events for paint events (sent on move on windows)
|
||||
if (m_last_window_width != scaled_width || m_last_window_height != scaled_height || m_last_window_scale != dpr)
|
||||
{
|
||||
m_last_window_width = scaled_width;
|
||||
m_last_window_height = scaled_height;
|
||||
m_last_window_scale = dpr;
|
||||
g_emu_thread->queueAuxiliaryRenderWindowInputEvent(
|
||||
m_userdata, Host::AuxiliaryRenderWindowEvent::Resized,
|
||||
Host::AuxiliaryRenderWindowEventParam{.uint_param = scaled_width},
|
||||
Host::AuxiliaryRenderWindowEventParam{.uint_param = scaled_height},
|
||||
Host::AuxiliaryRenderWindowEventParam{.float_param = dpr});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
return QWidget::event(event);
|
||||
}
|
||||
}
|
||||
|
||||
AuxiliaryDisplayWidget* AuxiliaryDisplayWidget::create(s32 pos_x, s32 pos_y, u32 width, u32 height,
|
||||
const QString& title, const QString& icon_name, void* userdata)
|
||||
{
|
||||
// TODO: Wayland shit
|
||||
QWidget* parent = nullptr;
|
||||
AuxiliaryDisplayWidget* widget = new AuxiliaryDisplayWidget(parent, width, height, title, userdata);
|
||||
if (!icon_name.isEmpty())
|
||||
{
|
||||
if (const QIcon icon(icon_name); !icon.isNull())
|
||||
widget->setWindowIcon(icon);
|
||||
else
|
||||
widget->setWindowIcon(QtHost::GetAppIcon());
|
||||
}
|
||||
else
|
||||
{
|
||||
widget->setWindowIcon(QtHost::GetAppIcon());
|
||||
}
|
||||
|
||||
if (pos_x != std::numeric_limits<s32>::min() && pos_y != std::numeric_limits<s32>::min())
|
||||
widget->move(pos_x, pos_y);
|
||||
|
||||
widget->show();
|
||||
return widget;
|
||||
}
|
||||
|
||||
void AuxiliaryDisplayWidget::destroy()
|
||||
{
|
||||
m_destroying = true;
|
||||
|
||||
QWidget* container = static_cast<QWidget*>(parent());
|
||||
if (!container)
|
||||
container = this;
|
||||
container->close();
|
||||
container->deleteLater();
|
||||
}
|
||||
|
|
|
@ -89,3 +89,28 @@ protected:
|
|||
private:
|
||||
DisplayWidget* m_display_widget = nullptr;
|
||||
};
|
||||
|
||||
class AuxiliaryDisplayWidget final : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AuxiliaryDisplayWidget(QWidget* parent, u32 width, u32 height, const QString& title, void* userdata);
|
||||
~AuxiliaryDisplayWidget();
|
||||
|
||||
QPaintEngine* paintEngine() const override;
|
||||
|
||||
static AuxiliaryDisplayWidget* create(s32 pos_x, s32 pos_y, u32 width, u32 height, const QString& title,
|
||||
const QString& icon_name, void* userdata);
|
||||
void destroy();
|
||||
|
||||
protected:
|
||||
bool event(QEvent* event) override;
|
||||
|
||||
private:
|
||||
void* m_userdata = nullptr;
|
||||
u32 m_last_window_width = 0;
|
||||
u32 m_last_window_height = 0;
|
||||
float m_last_window_scale = 1.0f;
|
||||
bool m_destroying = false;
|
||||
};
|
||||
|
|
|
@ -2044,6 +2044,10 @@ void MainWindow::connectSignals()
|
|||
connect(g_emu_thread, &EmuThread::achievementsChallengeModeChanged, this,
|
||||
&MainWindow::onAchievementsChallengeModeChanged);
|
||||
connect(g_emu_thread, &EmuThread::onCoverDownloaderOpenRequested, this, &MainWindow::onToolsCoverDownloaderTriggered);
|
||||
connect(g_emu_thread, &EmuThread::onCreateAuxiliaryRenderWindow, this, &MainWindow::onCreateAuxiliaryRenderWindow,
|
||||
Qt::BlockingQueuedConnection);
|
||||
connect(g_emu_thread, &EmuThread::onDestroyAuxiliaryRenderWindow, this, &MainWindow::onDestroyAuxiliaryRenderWindow,
|
||||
Qt::BlockingQueuedConnection);
|
||||
|
||||
// These need to be queued connections to stop crashing due to menus opening/closing and switching focus.
|
||||
connect(m_game_list_widget, &GameListWidget::refreshProgress, this, &MainWindow::onGameListRefreshProgress);
|
||||
|
@ -2642,6 +2646,36 @@ void MainWindow::onAchievementsChallengeModeChanged(bool enabled)
|
|||
updateEmulationActions(false, System::IsValid(), enabled);
|
||||
}
|
||||
|
||||
bool MainWindow::onCreateAuxiliaryRenderWindow(qint32 x, qint32 y, quint32 width, quint32 height, const QString& title,
|
||||
const QString& icon_name, Host::AuxiliaryRenderWindowUserData userdata,
|
||||
Host::AuxiliaryRenderWindowHandle* handle, WindowInfo* wi, Error* error)
|
||||
{
|
||||
AuxiliaryDisplayWidget* widget = AuxiliaryDisplayWidget::create(x, y, width, height, title, icon_name, userdata);
|
||||
if (!widget)
|
||||
return false;
|
||||
|
||||
const std::optional<WindowInfo> owi = QtUtils::GetWindowInfoForWidget(widget, error);
|
||||
if (!owi.has_value())
|
||||
{
|
||||
widget->destroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
*handle = widget;
|
||||
*wi = owi.value();
|
||||
return true;
|
||||
}
|
||||
|
||||
void MainWindow::onDestroyAuxiliaryRenderWindow(Host::AuxiliaryRenderWindowHandle handle, QPoint* pos, QSize* size)
|
||||
{
|
||||
AuxiliaryDisplayWidget* widget = static_cast<AuxiliaryDisplayWidget*>(handle);
|
||||
DebugAssert(widget);
|
||||
|
||||
*pos = widget->pos();
|
||||
*size = widget->size();
|
||||
widget->destroy();
|
||||
}
|
||||
|
||||
void MainWindow::onToolsMemoryCardEditorTriggered()
|
||||
{
|
||||
openMemoryCardEditor(QString(), QString());
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "core/types.h"
|
||||
|
||||
#include "util/imgui_manager.h"
|
||||
#include "util/window_info.h"
|
||||
|
||||
#include <QtCore/QThread>
|
||||
|
@ -144,6 +145,11 @@ private Q_SLOTS:
|
|||
void onMediaCaptureStopped();
|
||||
void onAchievementsLoginRequested(Achievements::LoginRequestReason reason);
|
||||
void onAchievementsChallengeModeChanged(bool enabled);
|
||||
bool onCreateAuxiliaryRenderWindow(qint32 x, qint32 y, quint32 width, quint32 height, const QString& title,
|
||||
const QString& icon_name, Host::AuxiliaryRenderWindowUserData userdata,
|
||||
Host::AuxiliaryRenderWindowHandle* handle, WindowInfo* wi, Error* error);
|
||||
void onDestroyAuxiliaryRenderWindow(Host::AuxiliaryRenderWindowHandle handle, QPoint* pos, QSize* size);
|
||||
|
||||
void onApplicationStateChanged(Qt::ApplicationState state);
|
||||
|
||||
void onStartFileActionTriggered();
|
||||
|
|
|
@ -1700,6 +1700,58 @@ void Host::OpenHostFileSelectorAsync(std::string_view title, bool select_directo
|
|||
});
|
||||
}
|
||||
|
||||
bool Host::CreateAuxiliaryRenderWindow(s32 x, s32 y, u32 width, u32 height, std::string_view title,
|
||||
std::string_view icon_name, AuxiliaryRenderWindowUserData userdata,
|
||||
AuxiliaryRenderWindowHandle* handle, WindowInfo* wi, Error* error)
|
||||
{
|
||||
return emit g_emu_thread->onCreateAuxiliaryRenderWindow(x, y, width, height, QtUtils::StringViewToQString(title),
|
||||
QtUtils::StringViewToQString(icon_name), userdata, handle, wi,
|
||||
error);
|
||||
}
|
||||
|
||||
void Host::DestroyAuxiliaryRenderWindow(AuxiliaryRenderWindowHandle handle, s32* pos_x, s32* pos_y, u32* width,
|
||||
u32* height)
|
||||
{
|
||||
QPoint pos;
|
||||
QSize size;
|
||||
emit g_emu_thread->onDestroyAuxiliaryRenderWindow(handle, &pos, &size);
|
||||
|
||||
if (pos_x)
|
||||
*pos_x = pos.x();
|
||||
if (pos_y)
|
||||
*pos_y = pos.y();
|
||||
if (width)
|
||||
*width = size.width();
|
||||
if (height)
|
||||
*height = size.height();
|
||||
|
||||
// eat all pending events, to make sure we're not going to write input events back to a dead pointer
|
||||
g_emu_thread->getEventLoop()->processEvents(QEventLoop::AllEvents);
|
||||
}
|
||||
|
||||
void EmuThread::queueAuxiliaryRenderWindowInputEvent(Host::AuxiliaryRenderWindowUserData userdata,
|
||||
Host::AuxiliaryRenderWindowEvent event,
|
||||
Host::AuxiliaryRenderWindowEventParam param1,
|
||||
Host::AuxiliaryRenderWindowEventParam param2,
|
||||
Host::AuxiliaryRenderWindowEventParam param3)
|
||||
{
|
||||
DebugAssert(isOnUIThread());
|
||||
QMetaObject::invokeMethod(this, "processAuxiliaryRenderWindowInputEvent", Qt::QueuedConnection,
|
||||
Q_ARG(void*, userdata), Q_ARG(quint32, static_cast<quint32>(event)),
|
||||
Q_ARG(quint32, param1.uint_param), Q_ARG(quint32, param2.uint_param),
|
||||
Q_ARG(quint32, param3.uint_param));
|
||||
}
|
||||
|
||||
void EmuThread::processAuxiliaryRenderWindowInputEvent(void* userdata, quint32 event, quint32 param1, quint32 param2,
|
||||
quint32 param3)
|
||||
{
|
||||
DebugAssert(isOnThread());
|
||||
ImGuiManager::ProcessAuxiliaryRenderWindowInputEvent(userdata, static_cast<Host::AuxiliaryRenderWindowEvent>(event),
|
||||
Host::AuxiliaryRenderWindowEventParam{.uint_param = param1},
|
||||
Host::AuxiliaryRenderWindowEventParam{.uint_param = param2},
|
||||
Host::AuxiliaryRenderWindowEventParam{.uint_param = param3});
|
||||
}
|
||||
|
||||
void EmuThread::doBackgroundControllerPoll()
|
||||
{
|
||||
System::Internal::IdlePollUpdate();
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "core/types.h"
|
||||
|
||||
#include "util/gpu_device.h"
|
||||
#include "util/imgui_manager.h"
|
||||
#include "util/input_manager.h"
|
||||
|
||||
#include <QtCore/QByteArray>
|
||||
|
@ -119,6 +120,13 @@ public:
|
|||
/// This version is **only** for the system thread. UI thread should use the MainWindow variant.
|
||||
SystemLock pauseAndLockSystem();
|
||||
|
||||
/// Queues an input event for an additional render window to the emu thread.
|
||||
void queueAuxiliaryRenderWindowInputEvent(Host::AuxiliaryRenderWindowUserData userdata,
|
||||
Host::AuxiliaryRenderWindowEvent event,
|
||||
Host::AuxiliaryRenderWindowEventParam param1 = {},
|
||||
Host::AuxiliaryRenderWindowEventParam param2 = {},
|
||||
Host::AuxiliaryRenderWindowEventParam param3 = {});
|
||||
|
||||
Q_SIGNALS:
|
||||
void errorReported(const QString& title, const QString& message);
|
||||
bool messageConfirmed(const QString& title, const QString& message);
|
||||
|
@ -151,6 +159,11 @@ Q_SIGNALS:
|
|||
void mediaCaptureStarted();
|
||||
void mediaCaptureStopped();
|
||||
|
||||
bool onCreateAuxiliaryRenderWindow(qint32 x, qint32 y, quint32 width, quint32 height, const QString& title,
|
||||
const QString& icon_name, Host::AuxiliaryRenderWindowUserData userdata,
|
||||
Host::AuxiliaryRenderWindowHandle* handle, WindowInfo* wi, Error* error);
|
||||
void onDestroyAuxiliaryRenderWindow(Host::AuxiliaryRenderWindowHandle handle, QPoint* pos, QSize* size);
|
||||
|
||||
/// Big Picture UI requests.
|
||||
void onCoverDownloaderOpenRequested();
|
||||
|
||||
|
@ -210,6 +223,8 @@ private Q_SLOTS:
|
|||
void onDisplayWindowTextEntered(const QString& text);
|
||||
void doBackgroundControllerPoll();
|
||||
void runOnEmuThread(std::function<void()> callback);
|
||||
void processAuxiliaryRenderWindowInputEvent(void* userdata, quint32 event, quint32 param1, quint32 param2,
|
||||
quint32 param3);
|
||||
|
||||
protected:
|
||||
void run() override;
|
||||
|
|
|
@ -9,6 +9,9 @@
|
|||
#include "imgui_glyph_ranges.inl"
|
||||
#include "input_manager.h"
|
||||
|
||||
// TODO: Remove me when GPUDevice config is also cleaned up.
|
||||
#include "core/host.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/easing.h"
|
||||
#include "common/error.h"
|
||||
|
@ -64,7 +67,7 @@ struct OSDMessage
|
|||
static_assert(std::is_same_v<WCharType, ImWchar>);
|
||||
|
||||
static void UpdateScale();
|
||||
static void SetStyle();
|
||||
static void SetStyle(ImGuiStyle& style, float scale);
|
||||
static void SetKeyMap();
|
||||
static bool LoadFontData();
|
||||
static void ReloadFontDataIfActive();
|
||||
|
@ -91,6 +94,7 @@ static std::string s_font_path;
|
|||
static std::vector<WCharType> s_font_range;
|
||||
static std::vector<WCharType> s_emoji_range;
|
||||
|
||||
static ImGuiContext* s_imgui_context;
|
||||
static ImFont* s_standard_font;
|
||||
static ImFont* s_osd_font;
|
||||
static ImFont* s_fixed_font;
|
||||
|
@ -235,9 +239,9 @@ bool ImGuiManager::Initialize(float global_scale, Error* error)
|
|||
(g_gpu_device->HasMainSwapChain() ? g_gpu_device->GetMainSwapChain()->GetScale() : 1.0f) * global_scale, 1.0f);
|
||||
s_scale_changed = false;
|
||||
|
||||
ImGui::CreateContext();
|
||||
s_imgui_context = ImGui::CreateContext();
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGuiIO& io = s_imgui_context->IO;
|
||||
io.IniFilename = nullptr;
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasGamepad | ImGuiBackendFlags_RendererHasVtxOffset;
|
||||
io.BackendUsingLegacyKeyArrays = 0;
|
||||
|
@ -259,7 +263,7 @@ bool ImGuiManager::Initialize(float global_scale, Error* error)
|
|||
io.DisplaySize = ImVec2(s_window_width, s_window_height);
|
||||
|
||||
SetKeyMap();
|
||||
SetStyle();
|
||||
SetStyle(s_imgui_context->Style, s_global_scale);
|
||||
|
||||
if (!AddImGuiFonts(false) || !g_gpu_device->UpdateImGuiFontTexture())
|
||||
{
|
||||
|
@ -269,7 +273,7 @@ bool ImGuiManager::Initialize(float global_scale, Error* error)
|
|||
}
|
||||
|
||||
// don't need the font data anymore, save some memory
|
||||
ImGui::GetIO().Fonts->ClearTexData();
|
||||
io.Fonts->ClearTexData();
|
||||
|
||||
NewFrame();
|
||||
|
||||
|
@ -281,8 +285,11 @@ void ImGuiManager::Shutdown()
|
|||
{
|
||||
DestroySoftwareCursorTextures();
|
||||
|
||||
if (ImGui::GetCurrentContext())
|
||||
ImGui::DestroyContext();
|
||||
if (s_imgui_context)
|
||||
{
|
||||
ImGui::DestroyContext(s_imgui_context);
|
||||
s_imgui_context = nullptr;
|
||||
}
|
||||
|
||||
s_standard_font = nullptr;
|
||||
s_fixed_font = nullptr;
|
||||
|
@ -291,6 +298,11 @@ void ImGuiManager::Shutdown()
|
|||
ImGuiFullscreen::SetFonts(nullptr, nullptr);
|
||||
}
|
||||
|
||||
ImGuiContext* ImGuiManager::GetMainContext()
|
||||
{
|
||||
return s_imgui_context;
|
||||
}
|
||||
|
||||
float ImGuiManager::GetWindowWidth()
|
||||
{
|
||||
return s_window_width;
|
||||
|
@ -327,11 +339,7 @@ void ImGuiManager::UpdateScale()
|
|||
return;
|
||||
|
||||
s_global_scale = scale;
|
||||
|
||||
ImGui::GetStyle() = ImGuiStyle();
|
||||
ImGui::GetStyle().WindowMinSize = ImVec2(1.0f, 1.0f);
|
||||
SetStyle();
|
||||
ImGui::GetStyle().ScaleAllSizes(scale);
|
||||
SetStyle(s_imgui_context->Style, s_global_scale);
|
||||
|
||||
if (!AddImGuiFonts(HasFullscreenFonts()))
|
||||
Panic("Failed to create ImGui font text");
|
||||
|
@ -360,9 +368,8 @@ void ImGuiManager::NewFrame()
|
|||
s_imgui_wants_mouse.store(io.WantCaptureMouse, std::memory_order_release);
|
||||
}
|
||||
|
||||
void ImGuiManager::SetStyle()
|
||||
void ImGuiManager::SetStyle(ImGuiStyle& style, float scale)
|
||||
{
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
style = ImGuiStyle();
|
||||
style.WindowMinSize = ImVec2(1.0f, 1.0f);
|
||||
|
||||
|
@ -416,7 +423,7 @@ void ImGuiManager::SetStyle()
|
|||
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
|
||||
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
|
||||
|
||||
style.ScaleAllSizes(s_global_scale);
|
||||
style.ScaleAllSizes(scale);
|
||||
}
|
||||
|
||||
void ImGuiManager::SetKeyMap()
|
||||
|
@ -713,7 +720,7 @@ bool ImGuiManager::AddImGuiFonts(bool fullscreen_fonts)
|
|||
|
||||
void ImGuiManager::ReloadFontDataIfActive()
|
||||
{
|
||||
if (!ImGui::GetCurrentContext())
|
||||
if (!s_imgui_context)
|
||||
return;
|
||||
|
||||
ImGui::EndFrame();
|
||||
|
@ -1054,43 +1061,43 @@ bool ImGuiManager::WantsMouseInput()
|
|||
|
||||
void ImGuiManager::AddTextInput(std::string str)
|
||||
{
|
||||
if (!ImGui::GetCurrentContext())
|
||||
if (!s_imgui_context)
|
||||
return;
|
||||
|
||||
if (!s_imgui_wants_keyboard.load(std::memory_order_acquire))
|
||||
return;
|
||||
|
||||
ImGui::GetIO().AddInputCharactersUTF8(str.c_str());
|
||||
s_imgui_context->IO.AddInputCharactersUTF8(str.c_str());
|
||||
}
|
||||
|
||||
void ImGuiManager::UpdateMousePosition(float x, float y)
|
||||
{
|
||||
if (!ImGui::GetCurrentContext())
|
||||
if (!s_imgui_context)
|
||||
return;
|
||||
|
||||
ImGui::GetIO().MousePos = ImVec2(x, y);
|
||||
s_imgui_context->IO.MousePos = ImVec2(x, y);
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
}
|
||||
|
||||
bool ImGuiManager::ProcessPointerButtonEvent(InputBindingKey key, float value)
|
||||
{
|
||||
if (!ImGui::GetCurrentContext() || key.data >= std::size(ImGui::GetIO().MouseDown))
|
||||
if (!s_imgui_context || key.data >= std::size(ImGui::GetIO().MouseDown))
|
||||
return false;
|
||||
|
||||
// still update state anyway
|
||||
ImGui::GetIO().AddMouseButtonEvent(key.data, value != 0.0f);
|
||||
s_imgui_context->IO.AddMouseButtonEvent(key.data, value != 0.0f);
|
||||
|
||||
return s_imgui_wants_mouse.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
bool ImGuiManager::ProcessPointerAxisEvent(InputBindingKey key, float value)
|
||||
{
|
||||
if (!ImGui::GetCurrentContext() || key.data < static_cast<u32>(InputPointerAxis::WheelX))
|
||||
if (!s_imgui_context || key.data < static_cast<u32>(InputPointerAxis::WheelX))
|
||||
return false;
|
||||
|
||||
// still update state anyway
|
||||
const bool horizontal = (key.data == static_cast<u32>(InputPointerAxis::WheelX));
|
||||
ImGui::GetIO().AddMouseWheelEvent(horizontal ? value : 0.0f, horizontal ? 0.0f : value);
|
||||
s_imgui_context->IO.AddMouseWheelEvent(horizontal ? value : 0.0f, horizontal ? 0.0f : value);
|
||||
|
||||
return s_imgui_wants_mouse.load(std::memory_order_acquire);
|
||||
}
|
||||
|
@ -1098,11 +1105,11 @@ bool ImGuiManager::ProcessPointerAxisEvent(InputBindingKey key, float value)
|
|||
bool ImGuiManager::ProcessHostKeyEvent(InputBindingKey key, float value)
|
||||
{
|
||||
decltype(s_imgui_key_map)::iterator iter;
|
||||
if (!ImGui::GetCurrentContext() || (iter = s_imgui_key_map.find(key.data)) == s_imgui_key_map.end())
|
||||
if (!s_imgui_context || (iter = s_imgui_key_map.find(key.data)) == s_imgui_key_map.end())
|
||||
return false;
|
||||
|
||||
// still update state anyway
|
||||
ImGui::GetIO().AddKeyEvent(iter->second, value != 0.0);
|
||||
s_imgui_context->IO.AddKeyEvent(iter->second, value != 0.0);
|
||||
|
||||
return s_imgui_wants_keyboard.load(std::memory_order_acquire);
|
||||
}
|
||||
|
@ -1138,13 +1145,13 @@ bool ImGuiManager::ProcessGenericInputEvent(GenericInputBinding key, float value
|
|||
ImGuiKey_GamepadL2, // R2
|
||||
};
|
||||
|
||||
if (!ImGui::GetCurrentContext())
|
||||
if (!s_imgui_context)
|
||||
return false;
|
||||
|
||||
if (static_cast<u32>(key) >= std::size(key_map) || key_map[static_cast<u32>(key)] == ImGuiKey_None)
|
||||
return false;
|
||||
|
||||
ImGui::GetIO().AddKeyAnalogEvent(key_map[static_cast<u32>(key)], (value > 0.0f), value);
|
||||
s_imgui_context->IO.AddKeyAnalogEvent(key_map[static_cast<u32>(key)], (value > 0.0f), value);
|
||||
return s_imgui_wants_keyboard.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
|
@ -1275,3 +1282,213 @@ std::string ImGuiManager::StripIconCharacters(std::string_view str)
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifndef __ANDROID__
|
||||
|
||||
bool ImGuiManager::CreateAuxiliaryRenderWindow(AuxiliaryRenderWindowState* state, std::string_view title,
|
||||
std::string_view icon_name, const char* config_section,
|
||||
const char* config_prefix, u32 default_width, u32 default_height,
|
||||
Error* error)
|
||||
{
|
||||
constexpr s32 DEFAULT_POSITION = std::numeric_limits<s32>::min();
|
||||
|
||||
// figure out where to position it
|
||||
s32 pos_x = DEFAULT_POSITION;
|
||||
s32 pos_y = DEFAULT_POSITION;
|
||||
u32 width = default_width;
|
||||
u32 height = default_height;
|
||||
if (config_prefix)
|
||||
{
|
||||
pos_x = Host::GetBaseIntSettingValue(config_section, TinyString::from_format("{}PositionX", config_prefix),
|
||||
DEFAULT_POSITION);
|
||||
pos_y = Host::GetBaseIntSettingValue(config_section, TinyString::from_format("{}PositionY", config_prefix),
|
||||
DEFAULT_POSITION);
|
||||
width =
|
||||
Host::GetBaseUIntSettingValue(config_section, TinyString::from_format("{}Width", config_prefix), default_width);
|
||||
height =
|
||||
Host::GetBaseUIntSettingValue(config_section, TinyString::from_format("{}Height", config_prefix), default_height);
|
||||
}
|
||||
|
||||
WindowInfo wi;
|
||||
if (!Host::CreateAuxiliaryRenderWindow(pos_x, pos_y, width, height, title, icon_name, state, &state->window_handle,
|
||||
&wi, error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
state->swap_chain = g_gpu_device->CreateSwapChain(wi, GPUVSyncMode::Disabled, false, nullptr, std::nullopt, error);
|
||||
if (!state->swap_chain)
|
||||
{
|
||||
Host::DestroyAuxiliaryRenderWindow(state->window_handle);
|
||||
state->window_handle = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
state->imgui_context = ImGui::CreateContext(s_imgui_context->IO.Fonts);
|
||||
state->imgui_context->IO.DisplaySize =
|
||||
ImVec2(static_cast<float>(state->swap_chain->GetWidth()), static_cast<float>(state->swap_chain->GetHeight()));
|
||||
state->imgui_context->IO.IniFilename = nullptr;
|
||||
state->imgui_context->IO.BackendFlags |= ImGuiBackendFlags_HasGamepad;
|
||||
state->imgui_context->IO.BackendUsingLegacyKeyArrays = 0;
|
||||
state->imgui_context->IO.BackendUsingLegacyNavInputArray = 0;
|
||||
state->imgui_context->IO.KeyRepeatDelay = 0.5f;
|
||||
state->imgui_context->IO.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;
|
||||
|
||||
SetStyle(state->imgui_context->Style, state->swap_chain->GetScale());
|
||||
state->imgui_context->Style.WindowBorderSize = 0.0f;
|
||||
|
||||
state->close_request = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGuiManager::DestroyAuxiliaryRenderWindow(AuxiliaryRenderWindowState* state, const char* config_section,
|
||||
const char* config_prefix)
|
||||
{
|
||||
constexpr s32 DEFAULT_POSITION = std::numeric_limits<s32>::min();
|
||||
|
||||
if (!state->window_handle)
|
||||
return;
|
||||
|
||||
s32 old_pos_x = DEFAULT_POSITION, old_pos_y = DEFAULT_POSITION;
|
||||
u32 old_width = 0, old_height = 0;
|
||||
if (config_section)
|
||||
{
|
||||
old_pos_x = Host::GetBaseIntSettingValue(config_section, TinyString::from_format("{}PositionX", config_prefix),
|
||||
DEFAULT_POSITION);
|
||||
old_pos_y = Host::GetBaseIntSettingValue(config_section, TinyString::from_format("{}PositionY", config_prefix),
|
||||
DEFAULT_POSITION);
|
||||
old_width = Host::GetBaseUIntSettingValue(config_section, TinyString::from_format("{}Width", config_prefix), 0);
|
||||
old_height = Host::GetBaseUIntSettingValue(config_section, TinyString::from_format("{}Height", config_prefix), 0);
|
||||
}
|
||||
|
||||
ImGui::DestroyContext(state->imgui_context);
|
||||
state->imgui_context = nullptr;
|
||||
state->swap_chain.reset();
|
||||
state->close_request = false;
|
||||
|
||||
// store positioning for config
|
||||
s32 new_pos_x = old_pos_x, new_pos_y = old_pos_y;
|
||||
u32 new_width = old_width, new_height = old_height;
|
||||
Host::DestroyAuxiliaryRenderWindow(std::exchange(state->window_handle, nullptr), &new_pos_x, &new_pos_y, &new_width,
|
||||
&new_height);
|
||||
|
||||
if (config_section)
|
||||
{
|
||||
// update config if the window was moved
|
||||
if (old_pos_x != new_pos_x || old_pos_y != new_pos_y || old_width != new_width || old_height != new_height)
|
||||
{
|
||||
Host::SetBaseIntSettingValue(config_section, TinyString::from_format("{}PositionX", config_prefix), new_pos_x);
|
||||
Host::SetBaseIntSettingValue(config_section, TinyString::from_format("{}PositionY", config_prefix), new_pos_y);
|
||||
Host::SetBaseUIntSettingValue(config_section, TinyString::from_format("{}Width", config_prefix), new_width);
|
||||
Host::SetBaseUIntSettingValue(config_section, TinyString::from_format("{}Height", config_prefix), new_height);
|
||||
Host::CommitBaseSettingChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ImGuiManager::RenderAuxiliaryRenderWindow(AuxiliaryRenderWindowState* state, void (*draw_callback)(float scale))
|
||||
{
|
||||
DebugAssert(state->window_handle);
|
||||
if (state->close_request)
|
||||
return false;
|
||||
|
||||
ImGui::SetCurrentContext(state->imgui_context);
|
||||
|
||||
ImGui::NewFrame();
|
||||
ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f), ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(state->imgui_context->IO.DisplaySize, ImGuiCond_Always);
|
||||
if (ImGui::Begin("AuxRenderWindowMain", nullptr,
|
||||
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoCollapse))
|
||||
{
|
||||
draw_callback(state->swap_chain->GetScale());
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
|
||||
const GPUDevice::PresentResult pres = g_gpu_device->BeginPresent(state->swap_chain.get());
|
||||
if (pres == GPUDevice::PresentResult::OK)
|
||||
{
|
||||
g_gpu_device->RenderImGui(state->swap_chain.get());
|
||||
g_gpu_device->EndPresent(state->swap_chain.get(), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::EndFrame();
|
||||
}
|
||||
|
||||
ImGui::SetCurrentContext(GetMainContext());
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGuiManager::ProcessAuxiliaryRenderWindowInputEvent(Host::AuxiliaryRenderWindowUserData userdata,
|
||||
Host::AuxiliaryRenderWindowEvent event,
|
||||
Host::AuxiliaryRenderWindowEventParam param1,
|
||||
Host::AuxiliaryRenderWindowEventParam param2,
|
||||
Host::AuxiliaryRenderWindowEventParam param3)
|
||||
{
|
||||
// we can get bogus events here after the user closes it, so check we're not being destroyed
|
||||
AuxiliaryRenderWindowState* state = static_cast<AuxiliaryRenderWindowState*>(userdata);
|
||||
if (!state->window_handle) [[unlikely]]
|
||||
return;
|
||||
|
||||
ImGuiIO& io = state->imgui_context->IO;
|
||||
|
||||
switch (event)
|
||||
{
|
||||
case Host::AuxiliaryRenderWindowEvent::CloseRequest:
|
||||
{
|
||||
state->close_request = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case Host::AuxiliaryRenderWindowEvent::Resized:
|
||||
{
|
||||
Error error;
|
||||
if (!state->swap_chain->ResizeBuffers(param1.uint_param, param2.uint_param, param3.float_param, &error))
|
||||
{
|
||||
ERROR_LOG("Failed to resize aux window swap chain to {}x{}: {}", param1.uint_param, param2.uint_param,
|
||||
error.GetDescription());
|
||||
return;
|
||||
}
|
||||
|
||||
state->imgui_context->IO.DisplaySize.x = static_cast<float>(param1.uint_param);
|
||||
state->imgui_context->IO.DisplaySize.y = static_cast<float>(param2.uint_param);
|
||||
}
|
||||
break;
|
||||
|
||||
case Host::AuxiliaryRenderWindowEvent::KeyPressed:
|
||||
case Host::AuxiliaryRenderWindowEvent::KeyReleased:
|
||||
{
|
||||
const auto iter = s_imgui_key_map.find(param1.uint_param);
|
||||
if (iter != s_imgui_key_map.end())
|
||||
io.AddKeyEvent(iter->second, (event == Host::AuxiliaryRenderWindowEvent::KeyPressed));
|
||||
}
|
||||
break;
|
||||
|
||||
case Host::AuxiliaryRenderWindowEvent::MouseMoved:
|
||||
{
|
||||
io.MousePos.x = param1.float_param;
|
||||
io.MousePos.y = param2.float_param;
|
||||
}
|
||||
break;
|
||||
|
||||
case Host::AuxiliaryRenderWindowEvent::MousePressed:
|
||||
case Host::AuxiliaryRenderWindowEvent::MouseReleased:
|
||||
{
|
||||
io.AddMouseButtonEvent(param1.uint_param, (event == Host::AuxiliaryRenderWindowEvent::MousePressed));
|
||||
}
|
||||
break;
|
||||
|
||||
case Host::AuxiliaryRenderWindowEvent::MouseWheel:
|
||||
{
|
||||
io.AddMouseWheelEvent(param1.float_param, param2.float_param);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // __ANDROID__
|
||||
|
|
|
@ -4,17 +4,51 @@
|
|||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class Error;
|
||||
struct WindowInfo;
|
||||
|
||||
class GPUSwapChain;
|
||||
|
||||
struct ImGuiContext;
|
||||
struct ImFont;
|
||||
|
||||
union InputBindingKey;
|
||||
enum class GenericInputBinding : u8;
|
||||
|
||||
namespace Host {
|
||||
|
||||
/// Handle representing an auxiliary render window in the host.
|
||||
using AuxiliaryRenderWindowHandle = void*;
|
||||
using AuxiliaryRenderWindowUserData = void*;
|
||||
|
||||
enum class AuxiliaryRenderWindowEvent : u8
|
||||
{
|
||||
CloseRequest,
|
||||
Resized,
|
||||
KeyPressed,
|
||||
KeyReleased,
|
||||
MouseMoved,
|
||||
MousePressed,
|
||||
MouseReleased,
|
||||
MouseWheel,
|
||||
MaxCount,
|
||||
};
|
||||
|
||||
union AuxiliaryRenderWindowEventParam
|
||||
{
|
||||
s32 int_param;
|
||||
u32 uint_param;
|
||||
float float_param;
|
||||
};
|
||||
|
||||
} // namespace Host
|
||||
|
||||
namespace ImGuiManager {
|
||||
|
||||
using WCharType = u32;
|
||||
|
@ -42,6 +76,9 @@ bool Initialize(float global_scale, Error* error);
|
|||
/// Frees all ImGui resources.
|
||||
void Shutdown();
|
||||
|
||||
/// Returns main ImGui context.
|
||||
ImGuiContext* GetMainContext();
|
||||
|
||||
/// Returns the size of the display window. Can be safely called from any thread.
|
||||
float GetWindowWidth();
|
||||
float GetWindowHeight();
|
||||
|
@ -124,9 +161,45 @@ void RenderSoftwareCursors();
|
|||
|
||||
/// Strips icon characters from a string.
|
||||
std::string StripIconCharacters(std::string_view str);
|
||||
|
||||
#ifndef __ANDROID__
|
||||
|
||||
/// Auxiliary imgui windows.
|
||||
struct AuxiliaryRenderWindowState
|
||||
{
|
||||
Host::AuxiliaryRenderWindowHandle window_handle = nullptr;
|
||||
std::unique_ptr<GPUSwapChain> swap_chain;
|
||||
ImGuiContext* imgui_context = nullptr;
|
||||
bool close_request = false;
|
||||
};
|
||||
|
||||
/// Create a new aux render window. This creates the window itself, swap chain, and imgui context.
|
||||
/// Window position and dimensions are restored from the configuration file, under the specified section/key.
|
||||
bool CreateAuxiliaryRenderWindow(AuxiliaryRenderWindowState* state, std::string_view title, std::string_view icon_name,
|
||||
const char* config_section, const char* config_prefix, u32 default_width,
|
||||
u32 default_height, Error* error);
|
||||
|
||||
/// Destroys a previously-created aux render window, optionally saving its position information.
|
||||
void DestroyAuxiliaryRenderWindow(AuxiliaryRenderWindowState* state, const char* config_section = nullptr,
|
||||
const char* config_prefix = nullptr);
|
||||
|
||||
/// Renders the specified aux render window. draw_callback will be invoked if the window is not hidden.
|
||||
/// Returns false if the user has closed the window.
|
||||
bool RenderAuxiliaryRenderWindow(AuxiliaryRenderWindowState* state, void (*draw_callback)(float scale));
|
||||
|
||||
/// Processes input events from the host.
|
||||
void ProcessAuxiliaryRenderWindowInputEvent(Host::AuxiliaryRenderWindowUserData userdata,
|
||||
Host::AuxiliaryRenderWindowEvent event,
|
||||
Host::AuxiliaryRenderWindowEventParam param1,
|
||||
Host::AuxiliaryRenderWindowEventParam param2,
|
||||
Host::AuxiliaryRenderWindowEventParam param3);
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace ImGuiManager
|
||||
|
||||
namespace Host {
|
||||
|
||||
/// Typical durations for OSD messages.
|
||||
static constexpr float OSD_CRITICAL_ERROR_DURATION = 20.0f;
|
||||
static constexpr float OSD_ERROR_DURATION = 15.0f;
|
||||
|
@ -143,4 +216,16 @@ void AddIconOSDWarning(std::string key, const char* icon, std::string message, f
|
|||
void RemoveKeyedOSDMessage(std::string key);
|
||||
void RemoveKeyedOSDWarning(std::string key);
|
||||
void ClearOSDMessages(bool clear_warnings);
|
||||
|
||||
#ifndef __ANDROID__
|
||||
|
||||
/// Auxiliary window management.
|
||||
bool CreateAuxiliaryRenderWindow(s32 x, s32 y, u32 width, u32 height, std::string_view title,
|
||||
std::string_view icon_name, AuxiliaryRenderWindowUserData userdata,
|
||||
AuxiliaryRenderWindowHandle* handle, WindowInfo* wi, Error* error);
|
||||
void DestroyAuxiliaryRenderWindow(AuxiliaryRenderWindowHandle handle, s32* pos_x = nullptr, s32* pos_y = nullptr,
|
||||
u32* width = nullptr, u32* height = nullptr);
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace Host
|
||||
|
|
Loading…
Reference in New Issue