[UI] Image post-processing and full presentation/window rework
[GPU] Add FXAA post-processing [UI] Add FidelityFX FSR and CAS post-processing [UI] Add blue noise dithering from 10bpc to 8bpc [GPU] Apply the DC PWL gamma ramp closer to the spec, supporting fully white color [UI] Allow the GPU CP thread to present on the host directly, bypassing the UI thread OS paint event [UI] Allow variable refresh rate (or tearing) [UI] Present the newest frame (restart) on DXGI [UI] Replace GraphicsContext with a far more advanced Presenter with more coherent surface connection and UI overlay state management [UI] Connect presentation to windows via the Surface class, not native window handles [Vulkan] Switch to simpler Vulkan setup with no instance/device separation due to interdependencies and to pass fewer objects around [Vulkan] Lower the minimum required Vulkan version to 1.0 [UI/GPU] Various cleanup, mainly ComPtr usage [UI] Support per-monitor DPI awareness v2 on Windows [UI] DPI-scale Dear ImGui [UI] Replace the remaining non-detachable window delegates with unified window event and input listeners [UI] Allow listeners to safely destroy or close the window, and to register/unregister listeners without use-after-free and the ABA problem [UI] Explicit Z ordering of input listeners and UI overlays, top-down for input, bottom-up for drawing [UI] Add explicit window lifecycle phases [UI] Replace Window virtual functions with explicit desired state, its application, actual state, its feedback [UI] GTK: Apply the initial size to the drawing area [UI] Limit internal UI frame rate to that of the monitor [UI] Hide the cursor using a timer instead of polling due to no repeated UI thread paints with GPU CP thread presentation, and only within the window
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -24,10 +24,14 @@
|
||||
#include "xenia/base/threading.h"
|
||||
#include "xenia/hid/hid_flags.h"
|
||||
#include "xenia/hid/input_system.h"
|
||||
#include "xenia/ui/imgui_dialog.h"
|
||||
#include "xenia/ui/imgui_drawer.h"
|
||||
#include "xenia/ui/immediate_drawer.h"
|
||||
#include "xenia/ui/presenter.h"
|
||||
#include "xenia/ui/virtual_key.h"
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
#include "xenia/ui/window.h"
|
||||
#include "xenia/ui/window_listener.h"
|
||||
#include "xenia/ui/windowed_app.h"
|
||||
|
||||
// Available input drivers:
|
||||
@@ -59,12 +63,41 @@ class HidDemoApp final : public ui::WindowedApp {
|
||||
bool OnInitialize() override;
|
||||
|
||||
private:
|
||||
enum : size_t {
|
||||
kZOrderHidInput,
|
||||
kZOrderImGui,
|
||||
};
|
||||
|
||||
class HidDemoWindowListener final : public ui::WindowListener {
|
||||
public:
|
||||
explicit HidDemoWindowListener(ui::WindowedAppContext& app_context)
|
||||
: app_context_(app_context) {}
|
||||
void OnClosing(ui::UIEvent& e) override { app_context_.QuitFromUIThread(); }
|
||||
|
||||
private:
|
||||
ui::WindowedAppContext& app_context_;
|
||||
};
|
||||
|
||||
class HidDemoDialog final : public ui::ImGuiDialog {
|
||||
public:
|
||||
explicit HidDemoDialog(ui::ImGuiDrawer* imgui_drawer, HidDemoApp& app)
|
||||
: ui::ImGuiDialog(imgui_drawer), app_(app) {}
|
||||
|
||||
protected:
|
||||
void OnDraw(ImGuiIO& io) override;
|
||||
|
||||
private:
|
||||
HidDemoApp& app_;
|
||||
};
|
||||
|
||||
explicit HidDemoApp(ui::WindowedAppContext& app_context)
|
||||
: ui::WindowedApp(app_context, "xenia-hid-demo") {}
|
||||
: ui::WindowedApp(app_context, "xenia-hid-demo"),
|
||||
window_listener_(app_context) {}
|
||||
|
||||
static std::vector<std::unique_ptr<hid::InputDriver>> CreateInputDrivers(
|
||||
ui::Window* window);
|
||||
|
||||
void Draw(ImGuiIO& io);
|
||||
void DrawUserInputGetState(uint32_t user_index) const;
|
||||
void DrawInputGetState() const;
|
||||
void DrawUserInputGetKeystroke(uint32_t user_index, bool poll,
|
||||
@@ -72,9 +105,15 @@ class HidDemoApp final : public ui::WindowedApp {
|
||||
void DrawInputGetKeystroke(bool poll, bool hide_repeats,
|
||||
bool clear_log) const;
|
||||
|
||||
HidDemoWindowListener window_listener_;
|
||||
std::unique_ptr<ui::GraphicsProvider> graphics_provider_;
|
||||
std::unique_ptr<ui::Window> window_;
|
||||
std::unique_ptr<InputSystem> input_system_;
|
||||
std::unique_ptr<ui::Presenter> presenter_;
|
||||
std::unique_ptr<ui::ImmediateDrawer> immediate_drawer_;
|
||||
std::unique_ptr<ui::ImGuiDrawer> imgui_drawer_;
|
||||
std::unique_ptr<HidDemoDialog> demo_dialog_;
|
||||
|
||||
bool is_active_ = true;
|
||||
};
|
||||
|
||||
@@ -82,71 +121,64 @@ std::vector<std::unique_ptr<hid::InputDriver>> HidDemoApp::CreateInputDrivers(
|
||||
ui::Window* window) {
|
||||
std::vector<std::unique_ptr<hid::InputDriver>> drivers;
|
||||
if (cvars::hid.compare("nop") == 0) {
|
||||
drivers.emplace_back(xe::hid::nop::Create(window));
|
||||
drivers.emplace_back(xe::hid::nop::Create(window, kZOrderHidInput));
|
||||
} else if (cvars::hid.compare("sdl") == 0) {
|
||||
auto driver = xe::hid::sdl::Create(window);
|
||||
auto driver = xe::hid::sdl::Create(window, kZOrderHidInput);
|
||||
if (XSUCCEEDED(driver->Setup())) {
|
||||
drivers.emplace_back(std::move(driver));
|
||||
}
|
||||
#if XE_PLATFORM_WIN32
|
||||
} else if (cvars::hid.compare("winkey") == 0) {
|
||||
auto driver = xe::hid::winkey::Create(window);
|
||||
auto driver = xe::hid::winkey::Create(window, kZOrderHidInput);
|
||||
if (XSUCCEEDED(driver->Setup())) {
|
||||
drivers.emplace_back(std::move(driver));
|
||||
}
|
||||
} else if (cvars::hid.compare("xinput") == 0) {
|
||||
auto driver = xe::hid::xinput::Create(window);
|
||||
auto driver = xe::hid::xinput::Create(window, kZOrderHidInput);
|
||||
if (XSUCCEEDED(driver->Setup())) {
|
||||
drivers.emplace_back(std::move(driver));
|
||||
}
|
||||
#endif // XE_PLATFORM_WIN32
|
||||
} else {
|
||||
auto sdl_driver = xe::hid::sdl::Create(window);
|
||||
auto sdl_driver = xe::hid::sdl::Create(window, kZOrderHidInput);
|
||||
if (sdl_driver && XSUCCEEDED(sdl_driver->Setup())) {
|
||||
drivers.emplace_back(std::move(sdl_driver));
|
||||
}
|
||||
#if XE_PLATFORM_WIN32
|
||||
auto xinput_driver = xe::hid::xinput::Create(window);
|
||||
auto xinput_driver = xe::hid::xinput::Create(window, kZOrderHidInput);
|
||||
if (xinput_driver && XSUCCEEDED(xinput_driver->Setup())) {
|
||||
drivers.emplace_back(std::move(xinput_driver));
|
||||
}
|
||||
auto winkey_driver = xe::hid::winkey::Create(window);
|
||||
auto winkey_driver = xe::hid::winkey::Create(window, kZOrderHidInput);
|
||||
if (winkey_driver && XSUCCEEDED(winkey_driver->Setup())) {
|
||||
drivers.emplace_back(std::move(winkey_driver));
|
||||
}
|
||||
#endif // XE_PLATFORM_WIN32
|
||||
if (drivers.empty()) {
|
||||
// Fallback to nop if none created.
|
||||
drivers.emplace_back(xe::hid::nop::Create(window));
|
||||
drivers.emplace_back(xe::hid::nop::Create(window, kZOrderHidInput));
|
||||
}
|
||||
}
|
||||
return drivers;
|
||||
}
|
||||
|
||||
bool HidDemoApp::OnInitialize() {
|
||||
// Create graphics provider that provides the context for the window.
|
||||
graphics_provider_ = xe::ui::vulkan::VulkanProvider::Create();
|
||||
// Create the graphics provider that provides the presenter for the window.
|
||||
graphics_provider_ = xe::ui::vulkan::VulkanProvider::Create(true);
|
||||
if (!graphics_provider_) {
|
||||
XELOGE("Failed to initialize the graphics provider");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create the window.
|
||||
window_ = xe::ui::Window::Create(app_context(), GetName());
|
||||
if (!window_->Initialize()) {
|
||||
XELOGE("Failed to initialize main window");
|
||||
// Create and configure the window.
|
||||
window_ = xe::ui::Window::Create(app_context(), GetName(),
|
||||
COL_WIDTH_STATE + COL_WIDTH_STROKE,
|
||||
ROW_HEIGHT_GENERAL + 500);
|
||||
window_->AddListener(&window_listener_);
|
||||
if (!window_->Open()) {
|
||||
XELOGE("Failed to open the main window");
|
||||
return false;
|
||||
}
|
||||
window_->on_closed.AddListener([this](xe::ui::UIEvent* e) {
|
||||
XELOGI("User-initiated death!");
|
||||
app_context().QuitFromUIThread();
|
||||
});
|
||||
|
||||
// Initial size setting, done here so that it knows the menu exists.
|
||||
window_->Resize(COL_WIDTH_STATE + COL_WIDTH_STROKE, ROW_HEIGHT_GENERAL + 500);
|
||||
|
||||
// Create the graphics context for the window. The window will finish
|
||||
// initialization with the context (loading resources, etc).
|
||||
window_->set_context(graphics_provider_->CreateContext(window_.get()));
|
||||
|
||||
// Initialize input system and all drivers.
|
||||
input_system_ = std::make_unique<xe::hid::InputSystem>(window_.get());
|
||||
@@ -157,71 +189,83 @@ bool HidDemoApp::OnInitialize() {
|
||||
input_system_->AddDriver(std::move(driver));
|
||||
}
|
||||
|
||||
window_->Invalidate();
|
||||
|
||||
window_->set_imgui_input_enabled(true);
|
||||
|
||||
window_->on_painting.AddListener([this](xe::ui::UIEvent* e) {
|
||||
auto& io = window_->imgui_drawer()->GetIO();
|
||||
|
||||
const ImGuiWindowFlags wflags =
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings |
|
||||
ImGuiWindowFlags_NoScrollbar;
|
||||
|
||||
ImGui::Begin("General", nullptr, wflags);
|
||||
{
|
||||
ImGui::SetWindowPos(ImVec2(0, 0));
|
||||
ImGui::SetWindowSize(
|
||||
ImVec2(COL_WIDTH_STATE + COL_WIDTH_STROKE, ROW_HEIGHT_GENERAL));
|
||||
|
||||
ImGui::Text("Input System (hid) = \"%s\"", cvars::hid.c_str());
|
||||
ImGui::Checkbox("is_active", &is_active_);
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
ImGui::Begin("GetState()", nullptr, wflags);
|
||||
{
|
||||
ImGui::SetWindowPos(ImVec2(0, ROW_HEIGHT_GENERAL));
|
||||
ImGui::SetWindowSize(
|
||||
ImVec2(COL_WIDTH_STATE, io.DisplaySize.y - ROW_HEIGHT_GENERAL));
|
||||
|
||||
static bool enable_GetState = false;
|
||||
ImGui::Checkbox("Active", &enable_GetState);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Guide Button", &cvars::guide_button);
|
||||
if (enable_GetState) {
|
||||
ImGui::Spacing();
|
||||
DrawInputGetState();
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
ImGui::Begin("GetKeystroke()", nullptr, wflags);
|
||||
{
|
||||
ImGui::SetWindowPos(ImVec2(COL_WIDTH_STATE, ROW_HEIGHT_GENERAL));
|
||||
ImGui::SetWindowSize(
|
||||
ImVec2(COL_WIDTH_STROKE, io.DisplaySize.y - ROW_HEIGHT_GENERAL));
|
||||
|
||||
static bool enable_GetKeystroke = false;
|
||||
static bool hide_repeats = false;
|
||||
ImGui::Checkbox("Active", &enable_GetKeystroke);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Hide repeats", &hide_repeats);
|
||||
ImGui::SameLine();
|
||||
const bool clear_log = ImGui::Button("Clear");
|
||||
ImGui::Spacing();
|
||||
DrawInputGetKeystroke(enable_GetKeystroke, hide_repeats, clear_log);
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
// Continuous paint.
|
||||
window_->Invalidate();
|
||||
});
|
||||
// Setup drawing to the window.
|
||||
presenter_ = graphics_provider_->CreatePresenter();
|
||||
if (!presenter_) {
|
||||
XELOGE("Failed to initialize the presenter");
|
||||
return false;
|
||||
}
|
||||
immediate_drawer_ = graphics_provider_->CreateImmediateDrawer();
|
||||
if (!immediate_drawer_) {
|
||||
XELOGE("Failed to initialize the immediate drawer");
|
||||
return false;
|
||||
}
|
||||
immediate_drawer_->SetPresenter(presenter_.get());
|
||||
imgui_drawer_ =
|
||||
std::make_unique<ui::ImGuiDrawer>(window_.get(), kZOrderImGui);
|
||||
imgui_drawer_->SetPresenterAndImmediateDrawer(presenter_.get(),
|
||||
immediate_drawer_.get());
|
||||
demo_dialog_ = std::make_unique<HidDemoDialog>(imgui_drawer_.get(), *this);
|
||||
window_->SetPresenter(presenter_.get());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void HidDemoApp::HidDemoDialog::OnDraw(ImGuiIO& io) { app_.Draw(io); }
|
||||
|
||||
void HidDemoApp::Draw(ImGuiIO& io) {
|
||||
const ImGuiWindowFlags wflags =
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings |
|
||||
ImGuiWindowFlags_NoScrollbar;
|
||||
|
||||
ImGui::Begin("General", nullptr, wflags);
|
||||
{
|
||||
ImGui::SetWindowPos(ImVec2(0, 0));
|
||||
ImGui::SetWindowSize(
|
||||
ImVec2(COL_WIDTH_STATE + COL_WIDTH_STROKE, ROW_HEIGHT_GENERAL));
|
||||
|
||||
ImGui::Text("Input System (hid) = \"%s\"", cvars::hid.c_str());
|
||||
ImGui::Checkbox("is_active", &is_active_);
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
ImGui::Begin("GetState()", nullptr, wflags);
|
||||
{
|
||||
ImGui::SetWindowPos(ImVec2(0, ROW_HEIGHT_GENERAL));
|
||||
ImGui::SetWindowSize(
|
||||
ImVec2(COL_WIDTH_STATE, io.DisplaySize.y - ROW_HEIGHT_GENERAL));
|
||||
|
||||
static bool enable_GetState = false;
|
||||
ImGui::Checkbox("Active", &enable_GetState);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Guide Button", &cvars::guide_button);
|
||||
if (enable_GetState) {
|
||||
ImGui::Spacing();
|
||||
DrawInputGetState();
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
ImGui::Begin("GetKeystroke()", nullptr, wflags);
|
||||
{
|
||||
ImGui::SetWindowPos(ImVec2(COL_WIDTH_STATE, ROW_HEIGHT_GENERAL));
|
||||
ImGui::SetWindowSize(
|
||||
ImVec2(COL_WIDTH_STROKE, io.DisplaySize.y - ROW_HEIGHT_GENERAL));
|
||||
|
||||
static bool enable_GetKeystroke = false;
|
||||
static bool hide_repeats = false;
|
||||
ImGui::Checkbox("Active", &enable_GetKeystroke);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Hide repeats", &hide_repeats);
|
||||
ImGui::SameLine();
|
||||
const bool clear_log = ImGui::Button("Clear");
|
||||
ImGui::Spacing();
|
||||
DrawInputGetKeystroke(enable_GetKeystroke, hide_repeats, clear_log);
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void HidDemoApp::DrawUserInputGetState(uint32_t user_index) const {
|
||||
ImGui::Text("User %u:", user_index);
|
||||
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/hid/input_driver.h"
|
||||
|
||||
namespace xe {
|
||||
namespace hid {
|
||||
|
||||
InputDriver::InputDriver(xe::ui::Window* window) : window_(window) {}
|
||||
|
||||
InputDriver::~InputDriver() = default;
|
||||
|
||||
} // namespace hid
|
||||
} // namespace xe
|
||||
@@ -10,6 +10,7 @@
|
||||
#ifndef XENIA_HID_INPUT_DRIVER_H_
|
||||
#define XENIA_HID_INPUT_DRIVER_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
|
||||
#include "xenia/hid/input.h"
|
||||
@@ -29,7 +30,7 @@ class InputSystem;
|
||||
|
||||
class InputDriver {
|
||||
public:
|
||||
virtual ~InputDriver();
|
||||
virtual ~InputDriver() = default;
|
||||
|
||||
virtual X_STATUS Setup() = 0;
|
||||
|
||||
@@ -45,18 +46,21 @@ class InputDriver {
|
||||
is_active_callback_ = is_active_callback;
|
||||
}
|
||||
|
||||
private:
|
||||
xe::ui::Window* window_ = nullptr;
|
||||
std::function<bool()> is_active_callback_ = nullptr;
|
||||
|
||||
protected:
|
||||
explicit InputDriver(xe::ui::Window* window);
|
||||
explicit InputDriver(xe::ui::Window* window, size_t window_z_order)
|
||||
: window_(window), window_z_order_(window_z_order) {}
|
||||
|
||||
xe::ui::Window* window() const { return window_; }
|
||||
size_t window_z_order() const { return window_z_order_; }
|
||||
|
||||
bool is_active() const {
|
||||
return !is_active_callback_ || is_active_callback_();
|
||||
}
|
||||
|
||||
private:
|
||||
xe::ui::Window* window_;
|
||||
size_t window_z_order_;
|
||||
std::function<bool()> is_active_callback_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace hid
|
||||
|
||||
@@ -15,8 +15,9 @@ namespace xe {
|
||||
namespace hid {
|
||||
namespace nop {
|
||||
|
||||
std::unique_ptr<InputDriver> Create(xe::ui::Window* window) {
|
||||
return std::make_unique<NopInputDriver>(window);
|
||||
std::unique_ptr<InputDriver> Create(xe::ui::Window* window,
|
||||
size_t window_z_order) {
|
||||
return std::make_unique<NopInputDriver>(window, window_z_order);
|
||||
}
|
||||
|
||||
} // namespace nop
|
||||
|
||||
@@ -18,7 +18,8 @@ namespace xe {
|
||||
namespace hid {
|
||||
namespace nop {
|
||||
|
||||
std::unique_ptr<InputDriver> Create(xe::ui::Window* window);
|
||||
std::unique_ptr<InputDriver> Create(xe::ui::Window* window,
|
||||
size_t window_z_order);
|
||||
|
||||
} // namespace nop
|
||||
} // namespace hid
|
||||
|
||||
@@ -15,7 +15,8 @@ namespace xe {
|
||||
namespace hid {
|
||||
namespace nop {
|
||||
|
||||
NopInputDriver::NopInputDriver(xe::ui::Window* window) : InputDriver(window) {}
|
||||
NopInputDriver::NopInputDriver(xe::ui::Window* window, size_t window_z_order)
|
||||
: InputDriver(window, window_z_order) {}
|
||||
|
||||
NopInputDriver::~NopInputDriver() = default;
|
||||
|
||||
|
||||
@@ -16,9 +16,9 @@ namespace xe {
|
||||
namespace hid {
|
||||
namespace nop {
|
||||
|
||||
class NopInputDriver : public InputDriver {
|
||||
class NopInputDriver final : public InputDriver {
|
||||
public:
|
||||
explicit NopInputDriver(xe::ui::Window* window);
|
||||
explicit NopInputDriver(xe::ui::Window* window, size_t window_z_order);
|
||||
~NopInputDriver() override;
|
||||
|
||||
X_STATUS Setup() override;
|
||||
|
||||
@@ -41,7 +41,6 @@ project("xenia-hid-demo")
|
||||
filter("platforms:Linux")
|
||||
links({
|
||||
"SDL2",
|
||||
"vulkan",
|
||||
"X11",
|
||||
"xcb",
|
||||
"X11-xcb",
|
||||
|
||||
@@ -15,8 +15,9 @@ namespace xe {
|
||||
namespace hid {
|
||||
namespace sdl {
|
||||
|
||||
std::unique_ptr<InputDriver> Create(xe::ui::Window* window) {
|
||||
return std::make_unique<SDLInputDriver>(window);
|
||||
std::unique_ptr<InputDriver> Create(xe::ui::Window* window,
|
||||
size_t window_z_order) {
|
||||
return std::make_unique<SDLInputDriver>(window, window_z_order);
|
||||
}
|
||||
|
||||
} // namespace sdl
|
||||
|
||||
@@ -18,7 +18,8 @@ namespace xe {
|
||||
namespace hid {
|
||||
namespace sdl {
|
||||
|
||||
std::unique_ptr<InputDriver> Create(xe::ui::Window* window);
|
||||
std::unique_ptr<InputDriver> Create(xe::ui::Window* window,
|
||||
size_t window_z_order);
|
||||
|
||||
} // namespace sdl
|
||||
} // namespace hid
|
||||
|
||||
@@ -33,8 +33,8 @@ namespace xe {
|
||||
namespace hid {
|
||||
namespace sdl {
|
||||
|
||||
SDLInputDriver::SDLInputDriver(xe::ui::Window* window)
|
||||
: InputDriver(window),
|
||||
SDLInputDriver::SDLInputDriver(xe::ui::Window* window, size_t window_z_order)
|
||||
: InputDriver(window, window_z_order),
|
||||
sdl_events_initialized_(false),
|
||||
sdl_gamecontroller_initialized_(false),
|
||||
sdl_events_unflushed_(0),
|
||||
|
||||
@@ -28,9 +28,9 @@ namespace xe {
|
||||
namespace hid {
|
||||
namespace sdl {
|
||||
|
||||
class SDLInputDriver : public InputDriver {
|
||||
class SDLInputDriver final : public InputDriver {
|
||||
public:
|
||||
explicit SDLInputDriver(xe::ui::Window* window);
|
||||
explicit SDLInputDriver(xe::ui::Window* window, size_t window_z_order);
|
||||
~SDLInputDriver() override;
|
||||
|
||||
X_STATUS Setup() override;
|
||||
@@ -42,7 +42,7 @@ class SDLInputDriver : public InputDriver {
|
||||
X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags,
|
||||
X_INPUT_KEYSTROKE* out_keystroke) override;
|
||||
|
||||
protected:
|
||||
private:
|
||||
struct ControllerState {
|
||||
SDL_GameController* sdl;
|
||||
X_INPUT_CAPABILITIES caps;
|
||||
@@ -65,7 +65,6 @@ class SDLInputDriver : public InputDriver {
|
||||
uint32_t repeat_time;
|
||||
};
|
||||
|
||||
protected:
|
||||
void HandleEvent(const SDL_Event& event);
|
||||
void OnControllerDeviceAdded(const SDL_Event& event);
|
||||
void OnControllerDeviceRemoved(const SDL_Event& event);
|
||||
@@ -80,7 +79,6 @@ class SDLInputDriver : public InputDriver {
|
||||
void UpdateXCapabilities(ControllerState& state);
|
||||
void QueueControllerUpdate();
|
||||
|
||||
protected:
|
||||
bool sdl_events_initialized_;
|
||||
bool sdl_gamecontroller_initialized_;
|
||||
int sdl_events_unflushed_;
|
||||
|
||||
@@ -15,8 +15,9 @@ namespace xe {
|
||||
namespace hid {
|
||||
namespace winkey {
|
||||
|
||||
std::unique_ptr<InputDriver> Create(xe::ui::Window* window) {
|
||||
return std::make_unique<WinKeyInputDriver>(window);
|
||||
std::unique_ptr<InputDriver> Create(xe::ui::Window* window,
|
||||
size_t window_z_order) {
|
||||
return std::make_unique<WinKeyInputDriver>(window, window_z_order);
|
||||
}
|
||||
|
||||
} // namespace winkey
|
||||
|
||||
@@ -18,7 +18,8 @@ namespace xe {
|
||||
namespace hid {
|
||||
namespace winkey {
|
||||
|
||||
std::unique_ptr<InputDriver> Create(xe::ui::Window* window);
|
||||
std::unique_ptr<InputDriver> Create(xe::ui::Window* window,
|
||||
size_t window_z_order);
|
||||
|
||||
} // namespace winkey
|
||||
} // namespace hid
|
||||
|
||||
@@ -80,47 +80,22 @@ void WinKeyInputDriver::ParseKeyBinding(ui::VirtualKey output_key,
|
||||
}
|
||||
}
|
||||
|
||||
WinKeyInputDriver::WinKeyInputDriver(xe::ui::Window* window)
|
||||
: InputDriver(window), packet_number_(1) {
|
||||
// Register a key listener.
|
||||
window->on_key_down.AddListener([this](ui::KeyEvent* evt) {
|
||||
if (!is_active()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
|
||||
KeyEvent key;
|
||||
key.virtual_key = evt->virtual_key();
|
||||
key.transition = true;
|
||||
key.prev_state = evt->prev_state();
|
||||
key.repeat_count = evt->repeat_count();
|
||||
key_events_.push(key);
|
||||
});
|
||||
window->on_key_up.AddListener([this](ui::KeyEvent* evt) {
|
||||
if (!is_active()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
|
||||
KeyEvent key;
|
||||
key.virtual_key = evt->virtual_key();
|
||||
key.transition = false;
|
||||
key.prev_state = evt->prev_state();
|
||||
key.repeat_count = evt->repeat_count();
|
||||
key_events_.push(key);
|
||||
});
|
||||
|
||||
WinKeyInputDriver::WinKeyInputDriver(xe::ui::Window* window,
|
||||
size_t window_z_order)
|
||||
: InputDriver(window, window_z_order), window_input_listener_(*this) {
|
||||
#define XE_HID_WINKEY_BINDING(button, description, cvar_name, \
|
||||
cvar_default_value) \
|
||||
ParseKeyBinding(xe::ui::VirtualKey::kXInputPad##button, description, \
|
||||
cvars::cvar_name);
|
||||
#include "winkey_binding_table.inc"
|
||||
#undef XE_HID_WINKEY_BINDING
|
||||
|
||||
window->AddInputListener(&window_input_listener_, window_z_order);
|
||||
}
|
||||
|
||||
WinKeyInputDriver::~WinKeyInputDriver() = default;
|
||||
WinKeyInputDriver::~WinKeyInputDriver() {
|
||||
window()->RemoveInputListener(&window_input_listener_);
|
||||
}
|
||||
|
||||
X_STATUS WinKeyInputDriver::Setup() { return X_STATUS_SUCCESS; }
|
||||
|
||||
@@ -162,7 +137,7 @@ X_RESULT WinKeyInputDriver::GetState(uint32_t user_index,
|
||||
int16_t thumb_rx = 0;
|
||||
int16_t thumb_ry = 0;
|
||||
|
||||
if (window()->has_focus() && is_active()) {
|
||||
if (window()->HasFocus() && is_active()) {
|
||||
bool capital = IsKeyToggled(VK_CAPITAL) || IsKeyDown(VK_SHIFT);
|
||||
for (const KeyBinding& b : key_bindings_) {
|
||||
if (((b.lowercase == b.uppercase) || (b.lowercase && !capital) ||
|
||||
@@ -331,6 +306,29 @@ X_RESULT WinKeyInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags,
|
||||
return result;
|
||||
}
|
||||
|
||||
void WinKeyInputDriver::WinKeyWindowInputListener::OnKeyDown(ui::KeyEvent& e) {
|
||||
driver_.OnKey(e, true);
|
||||
}
|
||||
|
||||
void WinKeyInputDriver::WinKeyWindowInputListener::OnKeyUp(ui::KeyEvent& e) {
|
||||
driver_.OnKey(e, false);
|
||||
}
|
||||
|
||||
void WinKeyInputDriver::OnKey(ui::KeyEvent& e, bool is_down) {
|
||||
if (!is_active()) {
|
||||
return;
|
||||
}
|
||||
|
||||
KeyEvent key;
|
||||
key.virtual_key = e.virtual_key();
|
||||
key.transition = is_down;
|
||||
key.prev_state = e.prev_state();
|
||||
key.repeat_count = e.repeat_count();
|
||||
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
key_events_.push(key);
|
||||
}
|
||||
|
||||
} // namespace winkey
|
||||
} // namespace hid
|
||||
} // namespace xe
|
||||
|
||||
@@ -20,9 +20,9 @@ namespace xe {
|
||||
namespace hid {
|
||||
namespace winkey {
|
||||
|
||||
class WinKeyInputDriver : public InputDriver {
|
||||
class WinKeyInputDriver final : public InputDriver {
|
||||
public:
|
||||
explicit WinKeyInputDriver(xe::ui::Window* window);
|
||||
explicit WinKeyInputDriver(xe::ui::Window* window, size_t window_z_order);
|
||||
~WinKeyInputDriver() override;
|
||||
|
||||
X_STATUS Setup() override;
|
||||
@@ -49,15 +49,31 @@ class WinKeyInputDriver : public InputDriver {
|
||||
bool lowercase = false;
|
||||
};
|
||||
|
||||
xe::global_critical_region global_critical_region_;
|
||||
std::queue<KeyEvent> key_events_;
|
||||
std::vector<KeyBinding> key_bindings_;
|
||||
class WinKeyWindowInputListener final : public ui::WindowInputListener {
|
||||
public:
|
||||
explicit WinKeyWindowInputListener(WinKeyInputDriver& driver)
|
||||
: driver_(driver) {}
|
||||
|
||||
uint32_t packet_number_;
|
||||
void OnKeyDown(ui::KeyEvent& e) override;
|
||||
void OnKeyUp(ui::KeyEvent& e) override;
|
||||
|
||||
private:
|
||||
WinKeyInputDriver& driver_;
|
||||
};
|
||||
|
||||
void ParseKeyBinding(ui::VirtualKey virtual_key,
|
||||
const std::string_view description,
|
||||
const std::string_view binding);
|
||||
|
||||
void OnKey(ui::KeyEvent& e, bool is_down);
|
||||
|
||||
WinKeyWindowInputListener window_input_listener_;
|
||||
|
||||
xe::global_critical_region global_critical_region_;
|
||||
std::queue<KeyEvent> key_events_;
|
||||
std::vector<KeyBinding> key_bindings_;
|
||||
|
||||
uint32_t packet_number_ = 1;
|
||||
};
|
||||
|
||||
} // namespace winkey
|
||||
|
||||
@@ -15,8 +15,9 @@ namespace xe {
|
||||
namespace hid {
|
||||
namespace xinput {
|
||||
|
||||
std::unique_ptr<InputDriver> Create(xe::ui::Window* window) {
|
||||
return std::make_unique<XInputInputDriver>(window);
|
||||
std::unique_ptr<InputDriver> Create(xe::ui::Window* window,
|
||||
size_t window_z_order) {
|
||||
return std::make_unique<XInputInputDriver>(window, window_z_order);
|
||||
}
|
||||
|
||||
} // namespace xinput
|
||||
|
||||
@@ -18,7 +18,8 @@ namespace xe {
|
||||
namespace hid {
|
||||
namespace xinput {
|
||||
|
||||
std::unique_ptr<InputDriver> Create(xe::ui::Window* window);
|
||||
std::unique_ptr<InputDriver> Create(xe::ui::Window* window,
|
||||
size_t window_z_order);
|
||||
|
||||
} // namespace xinput
|
||||
} // namespace hid
|
||||
|
||||
@@ -21,8 +21,9 @@ namespace xe {
|
||||
namespace hid {
|
||||
namespace xinput {
|
||||
|
||||
XInputInputDriver::XInputInputDriver(xe::ui::Window* window)
|
||||
: InputDriver(window),
|
||||
XInputInputDriver::XInputInputDriver(xe::ui::Window* window,
|
||||
size_t window_z_order)
|
||||
: InputDriver(window, window_z_order),
|
||||
module_(nullptr),
|
||||
XInputGetCapabilities_(nullptr),
|
||||
XInputGetState_(nullptr),
|
||||
|
||||
@@ -16,9 +16,9 @@ namespace xe {
|
||||
namespace hid {
|
||||
namespace xinput {
|
||||
|
||||
class XInputInputDriver : public InputDriver {
|
||||
class XInputInputDriver final : public InputDriver {
|
||||
public:
|
||||
explicit XInputInputDriver(xe::ui::Window* window);
|
||||
explicit XInputInputDriver(xe::ui::Window* window, size_t window_z_order);
|
||||
~XInputInputDriver() override;
|
||||
|
||||
X_STATUS Setup() override;
|
||||
@@ -30,7 +30,7 @@ class XInputInputDriver : public InputDriver {
|
||||
X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags,
|
||||
X_INPUT_KEYSTROKE* out_keystroke) override;
|
||||
|
||||
protected:
|
||||
private:
|
||||
void* module_;
|
||||
void* XInputGetCapabilities_;
|
||||
void* XInputGetState_;
|
||||
|
||||
Reference in New Issue
Block a user