[UI] Loop thread to main thread WindowedAppContext
This commit is contained in:
@@ -10,15 +10,17 @@
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <forward_list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "third_party/fmt/include/fmt/format.h"
|
||||
#include "third_party/imgui/imgui.h"
|
||||
#include "xenia/base/clock.h"
|
||||
#include "xenia/base/cvar.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/main.h"
|
||||
#include "xenia/base/threading.h"
|
||||
#include "xenia/hid/hid_flags.h"
|
||||
#include "xenia/hid/input_system.h"
|
||||
@@ -26,6 +28,7 @@
|
||||
#include "xenia/ui/virtual_key.h"
|
||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||
#include "xenia/ui/window.h"
|
||||
#include "xenia/ui/windowed_app.h"
|
||||
|
||||
// Available input drivers:
|
||||
#include "xenia/hid/nop/nop_hid.h"
|
||||
@@ -46,10 +49,36 @@ DEFINE_string(hid, "any", "Input system. Use: [any, nop, sdl, winkey, xinput]",
|
||||
namespace xe {
|
||||
namespace hid {
|
||||
|
||||
std::unique_ptr<xe::hid::InputSystem> input_system_;
|
||||
bool is_active = true;
|
||||
class HidDemoApp final : public ui::WindowedApp {
|
||||
public:
|
||||
static std::unique_ptr<ui::WindowedApp> Create(
|
||||
ui::WindowedAppContext& app_context) {
|
||||
return std::unique_ptr<ui::WindowedApp>(new HidDemoApp(app_context));
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<hid::InputDriver>> CreateInputDrivers(
|
||||
bool OnInitialize() override;
|
||||
|
||||
private:
|
||||
explicit HidDemoApp(ui::WindowedAppContext& app_context)
|
||||
: ui::WindowedApp(app_context, "xenia-hid-demo") {}
|
||||
|
||||
static std::vector<std::unique_ptr<hid::InputDriver>> CreateInputDrivers(
|
||||
ui::Window* window);
|
||||
|
||||
void DrawUserInputGetState(uint32_t user_index) const;
|
||||
void DrawInputGetState() const;
|
||||
void DrawUserInputGetKeystroke(uint32_t user_index, bool poll,
|
||||
bool hide_repeats, bool clear_log) const;
|
||||
void DrawInputGetKeystroke(bool poll, bool hide_repeats,
|
||||
bool clear_log) const;
|
||||
|
||||
std::unique_ptr<ui::GraphicsProvider> graphics_provider_;
|
||||
std::unique_ptr<ui::Window> window_;
|
||||
std::unique_ptr<InputSystem> input_system_;
|
||||
bool is_active_ = true;
|
||||
};
|
||||
|
||||
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) {
|
||||
@@ -94,60 +123,46 @@ std::vector<std::unique_ptr<hid::InputDriver>> CreateInputDrivers(
|
||||
return drivers;
|
||||
}
|
||||
|
||||
std::unique_ptr<xe::ui::GraphicsProvider> CreateDemoGraphicsProvider(
|
||||
xe::ui::Window* window) {
|
||||
return xe::ui::vulkan::VulkanProvider::Create(window);
|
||||
}
|
||||
bool HidDemoApp::OnInitialize() {
|
||||
// Create graphics provider that provides the context for the window.
|
||||
graphics_provider_ = xe::ui::vulkan::VulkanProvider::Create();
|
||||
if (!graphics_provider_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void DrawInputGetState();
|
||||
void DrawInputGetKeystroke(bool poll, bool hide_repeats, bool clear_log);
|
||||
|
||||
int hid_demo_main(const std::vector<std::string>& args) {
|
||||
// Create run loop and the window.
|
||||
auto loop = ui::Loop::Create();
|
||||
auto window = xe::ui::Window::Create(loop.get(), GetEntryInfo().name);
|
||||
loop->PostSynchronous([&window]() {
|
||||
xe::threading::set_name("Win32 Loop");
|
||||
if (!window->Initialize()) {
|
||||
FatalError("Failed to initialize main window");
|
||||
return;
|
||||
}
|
||||
});
|
||||
window->on_closed.AddListener([&loop](xe::ui::UIEvent* e) {
|
||||
loop->Quit();
|
||||
// Create the window.
|
||||
window_ = xe::ui::Window::Create(app_context(), GetName());
|
||||
if (!window_->Initialize()) {
|
||||
XELOGE("Failed to initialize main window");
|
||||
return false;
|
||||
}
|
||||
window_->on_closed.AddListener([this](xe::ui::UIEvent* e) {
|
||||
XELOGI("User-initiated death!");
|
||||
exit(1);
|
||||
app_context().QuitFromUIThread();
|
||||
});
|
||||
loop->on_quit.AddListener([&window](xe::ui::UIEvent* e) { window.reset(); });
|
||||
|
||||
// Initial size setting, done here so that it knows the menu exists.
|
||||
window->Resize(COL_WIDTH_STATE + COL_WIDTH_STROKE, ROW_HEIGHT_GENERAL + 500);
|
||||
window_->Resize(COL_WIDTH_STATE + COL_WIDTH_STROKE, ROW_HEIGHT_GENERAL + 500);
|
||||
|
||||
// Create the graphics context used for drawing and setup the window.
|
||||
std::unique_ptr<xe::ui::GraphicsProvider> graphics_provider;
|
||||
loop->PostSynchronous([&window, &graphics_provider]() {
|
||||
// Create context and give it to the window.
|
||||
// The window will finish initialization wtih the context (loading
|
||||
// resources, etc).
|
||||
graphics_provider = CreateDemoGraphicsProvider(window.get());
|
||||
window->set_context(graphics_provider->CreateContext(window.get()));
|
||||
// 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());
|
||||
auto drivers = CreateInputDrivers(window.get());
|
||||
for (size_t i = 0; i < drivers.size(); ++i) {
|
||||
auto& driver = drivers[i];
|
||||
driver->set_is_active_callback([]() -> bool { return is_active; });
|
||||
input_system_->AddDriver(std::move(driver));
|
||||
}
|
||||
// Initialize input system and all drivers.
|
||||
input_system_ = std::make_unique<xe::hid::InputSystem>(window_.get());
|
||||
auto drivers = CreateInputDrivers(window_.get());
|
||||
for (size_t i = 0; i < drivers.size(); ++i) {
|
||||
auto& driver = drivers[i];
|
||||
driver->set_is_active_callback([this]() -> bool { return is_active_; });
|
||||
input_system_->AddDriver(std::move(driver));
|
||||
}
|
||||
|
||||
window->Invalidate();
|
||||
});
|
||||
window_->Invalidate();
|
||||
|
||||
window->set_imgui_input_enabled(true);
|
||||
window_->set_imgui_input_enabled(true);
|
||||
|
||||
window->on_painting.AddListener([&](xe::ui::UIEvent* e) {
|
||||
auto& io = window->imgui_drawer()->GetIO();
|
||||
window_->on_painting.AddListener([this](xe::ui::UIEvent* e) {
|
||||
auto& io = window_->imgui_drawer()->GetIO();
|
||||
|
||||
const ImGuiWindowFlags wflags =
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
|
||||
@@ -161,7 +176,7 @@ int hid_demo_main(const std::vector<std::string>& args) {
|
||||
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::Checkbox("is_active", &is_active_);
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
@@ -201,21 +216,13 @@ int hid_demo_main(const std::vector<std::string>& args) {
|
||||
ImGui::End();
|
||||
|
||||
// Continuous paint.
|
||||
window->Invalidate();
|
||||
window_->Invalidate();
|
||||
});
|
||||
|
||||
// Wait until we are exited.
|
||||
loop->AwaitQuit();
|
||||
|
||||
input_system_.reset();
|
||||
|
||||
loop->PostSynchronous([&graphics_provider]() { graphics_provider.reset(); });
|
||||
window.reset();
|
||||
loop.reset();
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void DrawUserInputGetState(uint32_t user_index) {
|
||||
void HidDemoApp::DrawUserInputGetState(uint32_t user_index) const {
|
||||
ImGui::Text("User %u:", user_index);
|
||||
|
||||
X_INPUT_STATE state;
|
||||
@@ -274,7 +281,7 @@ void DrawUserInputGetState(uint32_t user_index) {
|
||||
ImGui::Text(" ");
|
||||
}
|
||||
|
||||
void DrawInputGetState() {
|
||||
void HidDemoApp::DrawInputGetState() const {
|
||||
ImGui::BeginChild("##input_get_state_scroll");
|
||||
for (uint32_t user_index = 0; user_index < MAX_USERS; ++user_index) {
|
||||
DrawUserInputGetState(user_index);
|
||||
@@ -282,46 +289,48 @@ void DrawInputGetState() {
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
static const std::unordered_map<ui::VirtualKey, const std::string> kVkPretty = {
|
||||
{ui::VirtualKey::kXInputPadA, "A"},
|
||||
{ui::VirtualKey::kXInputPadB, "B"},
|
||||
{ui::VirtualKey::kXInputPadX, "X"},
|
||||
{ui::VirtualKey::kXInputPadY, "Y"},
|
||||
{ui::VirtualKey::kXInputPadRShoulder, "R Shoulder"},
|
||||
{ui::VirtualKey::kXInputPadLShoulder, "L Shoulder"},
|
||||
{ui::VirtualKey::kXInputPadLTrigger, "L Trigger"},
|
||||
{ui::VirtualKey::kXInputPadRTrigger, "R Trigger"},
|
||||
void HidDemoApp::DrawUserInputGetKeystroke(uint32_t user_index, bool poll,
|
||||
bool hide_repeats,
|
||||
bool clear_log) const {
|
||||
static const std::unordered_map<ui::VirtualKey, const std::string> kVkPretty =
|
||||
{
|
||||
{ui::VirtualKey::kXInputPadA, "A"},
|
||||
{ui::VirtualKey::kXInputPadB, "B"},
|
||||
{ui::VirtualKey::kXInputPadX, "X"},
|
||||
{ui::VirtualKey::kXInputPadY, "Y"},
|
||||
{ui::VirtualKey::kXInputPadRShoulder, "R Shoulder"},
|
||||
{ui::VirtualKey::kXInputPadLShoulder, "L Shoulder"},
|
||||
{ui::VirtualKey::kXInputPadLTrigger, "L Trigger"},
|
||||
{ui::VirtualKey::kXInputPadRTrigger, "R Trigger"},
|
||||
|
||||
{ui::VirtualKey::kXInputPadDpadUp, "DPad up"},
|
||||
{ui::VirtualKey::kXInputPadDpadDown, "DPad down"},
|
||||
{ui::VirtualKey::kXInputPadDpadLeft, "DPad left"},
|
||||
{ui::VirtualKey::kXInputPadDpadRight, "DPad right"},
|
||||
{ui::VirtualKey::kXInputPadStart, "Start"},
|
||||
{ui::VirtualKey::kXInputPadBack, "Back"},
|
||||
{ui::VirtualKey::kXInputPadLThumbPress, "L Thumb press"},
|
||||
{ui::VirtualKey::kXInputPadRThumbPress, "R Thumb press"},
|
||||
{ui::VirtualKey::kXInputPadDpadUp, "DPad up"},
|
||||
{ui::VirtualKey::kXInputPadDpadDown, "DPad down"},
|
||||
{ui::VirtualKey::kXInputPadDpadLeft, "DPad left"},
|
||||
{ui::VirtualKey::kXInputPadDpadRight, "DPad right"},
|
||||
{ui::VirtualKey::kXInputPadStart, "Start"},
|
||||
{ui::VirtualKey::kXInputPadBack, "Back"},
|
||||
{ui::VirtualKey::kXInputPadLThumbPress, "L Thumb press"},
|
||||
{ui::VirtualKey::kXInputPadRThumbPress, "R Thumb press"},
|
||||
|
||||
{ui::VirtualKey::kXInputPadLThumbUp, "L Thumb up"},
|
||||
{ui::VirtualKey::kXInputPadLThumbDown, "L Thumb down"},
|
||||
{ui::VirtualKey::kXInputPadLThumbRight, "L Thumb right"},
|
||||
{ui::VirtualKey::kXInputPadLThumbLeft, "L Thumb left"},
|
||||
{ui::VirtualKey::kXInputPadLThumbUpLeft, "L Thumb up & left"},
|
||||
{ui::VirtualKey::kXInputPadLThumbUpRight, "L Thumb up & right"},
|
||||
{ui::VirtualKey::kXInputPadLThumbDownRight, "L Thumb down & right"},
|
||||
{ui::VirtualKey::kXInputPadLThumbDownLeft, "L Thumb down & left"},
|
||||
{ui::VirtualKey::kXInputPadLThumbUp, "L Thumb up"},
|
||||
{ui::VirtualKey::kXInputPadLThumbDown, "L Thumb down"},
|
||||
{ui::VirtualKey::kXInputPadLThumbRight, "L Thumb right"},
|
||||
{ui::VirtualKey::kXInputPadLThumbLeft, "L Thumb left"},
|
||||
{ui::VirtualKey::kXInputPadLThumbUpLeft, "L Thumb up & left"},
|
||||
{ui::VirtualKey::kXInputPadLThumbUpRight, "L Thumb up & right"},
|
||||
{ui::VirtualKey::kXInputPadLThumbDownRight, "L Thumb down & right"},
|
||||
{ui::VirtualKey::kXInputPadLThumbDownLeft, "L Thumb down & left"},
|
||||
|
||||
{ui::VirtualKey::kXInputPadRThumbUp, "R Thumb up"},
|
||||
{ui::VirtualKey::kXInputPadRThumbDown, "R Thumb down"},
|
||||
{ui::VirtualKey::kXInputPadRThumbRight, "R Thumb right"},
|
||||
{ui::VirtualKey::kXInputPadRThumbLeft, "R Thumb left"},
|
||||
{ui::VirtualKey::kXInputPadRThumbUpLeft, "R Thumb up & left"},
|
||||
{ui::VirtualKey::kXInputPadRThumbUpRight, "R Thumb up & right"},
|
||||
{ui::VirtualKey::kXInputPadRThumbDownRight, "R Thumb down & right"},
|
||||
{ui::VirtualKey::kXInputPadRThumbDownLeft, "R Thumb down & left"},
|
||||
};
|
||||
{ui::VirtualKey::kXInputPadRThumbUp, "R Thumb up"},
|
||||
{ui::VirtualKey::kXInputPadRThumbDown, "R Thumb down"},
|
||||
{ui::VirtualKey::kXInputPadRThumbRight, "R Thumb right"},
|
||||
{ui::VirtualKey::kXInputPadRThumbLeft, "R Thumb left"},
|
||||
{ui::VirtualKey::kXInputPadRThumbUpLeft, "R Thumb up & left"},
|
||||
{ui::VirtualKey::kXInputPadRThumbUpRight, "R Thumb up & right"},
|
||||
{ui::VirtualKey::kXInputPadRThumbDownRight, "R Thumb down & right"},
|
||||
{ui::VirtualKey::kXInputPadRThumbDownLeft, "R Thumb down & left"},
|
||||
};
|
||||
|
||||
void DrawUserInputGetKeystroke(uint32_t user_index, bool poll,
|
||||
bool hide_repeats, bool clear_log) {
|
||||
const size_t maxLog = 128;
|
||||
static std::array<std::forward_list<std::string>, MAX_USERS> event_logs;
|
||||
static std::array<uint64_t, MAX_USERS> last_event_times = {};
|
||||
@@ -400,7 +409,8 @@ void DrawUserInputGetKeystroke(uint32_t user_index, bool poll,
|
||||
}
|
||||
}
|
||||
|
||||
void DrawInputGetKeystroke(bool poll, bool hide_repeats, bool clear_log) {
|
||||
void HidDemoApp::DrawInputGetKeystroke(bool poll, bool hide_repeats,
|
||||
bool clear_log) const {
|
||||
bool tab_bar = ImGui::BeginTabBar("DrawInputGetKeystroke");
|
||||
for (uint32_t user_index = 0; user_index < MAX_USERS; ++user_index) {
|
||||
DrawUserInputGetKeystroke(user_index, poll, hide_repeats, clear_log);
|
||||
@@ -411,4 +421,4 @@ void DrawInputGetKeystroke(bool poll, bool hide_repeats, bool clear_log) {
|
||||
} // namespace hid
|
||||
} // namespace xe
|
||||
|
||||
DEFINE_ENTRY_POINT("xenia-hid-demo", xe::hid::hid_demo_main, "");
|
||||
XE_DEFINE_WINDOWED_APP(xenia_hid_demo, xe::hid::HidDemoApp::Create);
|
||||
|
||||
@@ -32,7 +32,7 @@ project("xenia-hid-demo")
|
||||
})
|
||||
files({
|
||||
"hid_demo.cc",
|
||||
"../base/main_"..platform_suffix..".cc",
|
||||
"../ui/windowed_app_main_"..platform_suffix..".cc",
|
||||
})
|
||||
resincludedirs({
|
||||
project_root,
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "xenia/hid/hid_flags.h"
|
||||
#include "xenia/ui/virtual_key.h"
|
||||
#include "xenia/ui/window.h"
|
||||
#include "xenia/ui/windowed_app_context.h"
|
||||
|
||||
// TODO(joellinn) make this path relative to the config folder.
|
||||
DEFINE_path(mappings_file, "gamecontrollerdb.txt",
|
||||
@@ -43,6 +44,12 @@ SDLInputDriver::SDLInputDriver(xe::ui::Window* window)
|
||||
keystroke_states_() {}
|
||||
|
||||
SDLInputDriver::~SDLInputDriver() {
|
||||
// Make sure the CallInUIThread is executed before destroying the references.
|
||||
if (sdl_pumpevents_queued_) {
|
||||
window()->app_context().CallInUIThreadSynchronous([this]() {
|
||||
window()->app_context().ExecutePendingFunctionsFromUIThread();
|
||||
});
|
||||
}
|
||||
for (size_t i = 0; i < controllers_.size(); i++) {
|
||||
if (controllers_.at(i).sdl) {
|
||||
SDL_GameControllerClose(controllers_.at(i).sdl);
|
||||
@@ -65,8 +72,9 @@ X_STATUS SDLInputDriver::Setup() {
|
||||
}
|
||||
|
||||
// SDL_PumpEvents should only be run in the thread that initialized SDL - we
|
||||
// are hijacking the window loop thread for that.
|
||||
window()->loop()->PostSynchronous([&]() {
|
||||
// are hijacking the UI thread for that. If this function fails to be queued,
|
||||
// the "initialized" variables will be false - that's handled safely.
|
||||
window()->app_context().CallInUIThreadSynchronous([this]() {
|
||||
if (!xe::helper::sdl::SDLHelper::Prepare()) {
|
||||
return;
|
||||
}
|
||||
@@ -129,7 +137,9 @@ X_STATUS SDLInputDriver::Setup() {
|
||||
}
|
||||
}
|
||||
});
|
||||
return sdl_events_initialized_ && sdl_gamecontroller_initialized_;
|
||||
return (sdl_events_initialized_ && sdl_gamecontroller_initialized_)
|
||||
? X_STATUS_SUCCESS
|
||||
: X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
X_RESULT SDLInputDriver::GetCapabilities(uint32_t user_index, uint32_t flags,
|
||||
@@ -693,7 +703,7 @@ void SDLInputDriver::QueueControllerUpdate() {
|
||||
bool is_queued = false;
|
||||
sdl_pumpevents_queued_.compare_exchange_strong(is_queued, true);
|
||||
if (!is_queued) {
|
||||
window()->loop()->Post([this]() {
|
||||
window()->app_context().CallInUIThread([this]() {
|
||||
SDL_PumpEvents();
|
||||
sdl_pumpevents_queued_ = false;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user