Implement controlling input capturing for mod UI contexts

This commit is contained in:
Mr-Wiseguy 2025-03-10 00:52:11 -04:00
parent 7261c055a1
commit 2dffaf6148
6 changed files with 121 additions and 21 deletions

View file

@ -49,7 +49,8 @@ namespace recompui {
void hide_context(ContextId context); void hide_context(ContextId context);
void hide_all_contexts(); void hide_all_contexts();
bool is_context_shown(ContextId context); bool is_context_shown(ContextId context);
bool is_context_taking_input(); bool is_context_capturing_input();
bool is_context_capturing_mouse();
bool is_any_context_shown(); bool is_any_context_shown();
ContextId get_launcher_context_id(); ContextId get_launcher_context_id();

View file

@ -103,7 +103,7 @@ bool sdl_event_filter(void* userdata, SDL_Event* event) {
SDL_KeyboardEvent* keyevent = &event->key; SDL_KeyboardEvent* keyevent = &event->key;
// Skip repeated events when not in the menu // Skip repeated events when not in the menu
if (!recompui::is_context_taking_input() && if (!recompui::is_context_capturing_input() &&
event->key.repeat) { event->key.repeat) {
break; break;
} }
@ -713,7 +713,7 @@ void recomp::set_right_analog_suppressed(bool suppressed) {
bool recomp::game_input_disabled() { bool recomp::game_input_disabled() {
// Disable input if any menu that blocks input is open. // Disable input if any menu that blocks input is open.
return recompui::is_context_taking_input(); return recompui::is_context_capturing_input();
} }
bool recomp::all_input_disabled() { bool recomp::all_input_disabled() {

View file

@ -34,6 +34,8 @@ namespace recompui {
Element root_element; Element root_element;
std::vector<Element*> loose_elements; std::vector<Element*> loose_elements;
std::unordered_set<ResourceId> to_update; std::unordered_set<ResourceId> to_update;
bool captures_input = true;
bool captures_mouse = true;
Context(Rml::ElementDocument* document) : document(document), root_element(document) {} Context(Rml::ElementDocument* document) : document(document), root_element(document) {}
}; };
} // namespace recompui } // namespace recompui
@ -371,6 +373,47 @@ void recompui::ContextId::process_updates() {
} }
} }
bool recompui::ContextId::captures_input() {
std::lock_guard lock{ context_state.all_contexts_lock };
Context* ctx = context_state.all_contexts.get(context_slotmap::key{ slot_id });
if (ctx == nullptr) {
return false;
}
return ctx->captures_input;
}
bool recompui::ContextId::captures_mouse() {
std::lock_guard lock{ context_state.all_contexts_lock };
Context* ctx = context_state.all_contexts.get(context_slotmap::key{ slot_id });
if (ctx == nullptr) {
return false;
}
return ctx->captures_mouse;
}
void recompui::ContextId::set_captures_input(bool captures_input) {
std::lock_guard lock{ context_state.all_contexts_lock };
Context* ctx = context_state.all_contexts.get(context_slotmap::key{ slot_id });
if (ctx == nullptr) {
return;
}
ctx->captures_input = captures_input;
}
void recompui::ContextId::set_captures_mouse(bool captures_mouse) {
std::lock_guard lock{ context_state.all_contexts_lock };
Context* ctx = context_state.all_contexts.get(context_slotmap::key{ slot_id });
if (ctx == nullptr) {
return;
}
ctx->captures_mouse = captures_mouse;
}
recompui::Style* recompui::ContextId::add_resource_impl(std::unique_ptr<Style>&& resource) { recompui::Style* recompui::ContextId::add_resource_impl(std::unique_ptr<Style>&& resource) {
// Ensure a context is currently opened by this thread. // Ensure a context is currently opened by this thread.
if (opened_context_id == ContextId::null()) { if (opened_context_id == ContextId::null()) {

View file

@ -47,8 +47,11 @@ namespace recompui {
static constexpr ContextId null() { return ContextId{ .slot_id = uint32_t(-1) }; } static constexpr ContextId null() { return ContextId{ .slot_id = uint32_t(-1) }; }
// TODO bool captures_input();
bool takes_input() { return true; } bool captures_mouse();
void set_captures_input(bool captures_input);
void set_captures_mouse(bool captures_input);
}; };
ContextId create_context(const std::filesystem::path& path); ContextId create_context(const std::filesystem::path& path);

View file

@ -138,6 +138,20 @@ void recompui_hide_context(uint8_t* rdram, recomp_context* ctx) {
recompui::hide_context(ui_context); recompui::hide_context(ui_context);
} }
void recompui_set_context_captures_input(uint8_t* rdram, recomp_context* ctx) {
ContextId ui_context = get_context(rdram, ctx);
bool captures_input = _arg<1, int>(rdram, ctx) != 0;
ui_context.set_captures_input(captures_input);
}
void recompui_set_context_captures_mouse(uint8_t* rdram, recomp_context* ctx) {
ContextId ui_context = get_context(rdram, ctx);
bool captures_mouse = _arg<1, int>(rdram, ctx) != 0;
ui_context.set_captures_mouse(captures_mouse);
}
// Resources // Resources
void recompui_create_style(uint8_t* rdram, recomp_context* ctx) { void recompui_create_style(uint8_t* rdram, recomp_context* ctx) {
ContextId ui_context = get_context(rdram, ctx); ContextId ui_context = get_context(rdram, ctx);
@ -780,6 +794,8 @@ void recompui::register_ui_exports() {
REGISTER_FUNC(recompui_context_root); REGISTER_FUNC(recompui_context_root);
REGISTER_FUNC(recompui_show_context); REGISTER_FUNC(recompui_show_context);
REGISTER_FUNC(recompui_hide_context); REGISTER_FUNC(recompui_hide_context);
REGISTER_FUNC(recompui_set_context_captures_input);
REGISTER_FUNC(recompui_set_context_captures_mouse);
REGISTER_FUNC(recompui_create_style); REGISTER_FUNC(recompui_create_style);
REGISTER_FUNC(recompui_create_element); REGISTER_FUNC(recompui_create_element);
REGISTER_FUNC(recompui_create_label); REGISTER_FUNC(recompui_create_label);

View file

@ -151,7 +151,6 @@ Rml::Element* find_autofocus_element(Rml::Element* start) {
struct ContextDetails { struct ContextDetails {
recompui::ContextId context; recompui::ContextId context;
Rml::ElementDocument* document; Rml::ElementDocument* document;
bool takes_input;
}; };
class UIState { class UIState {
@ -266,7 +265,7 @@ public:
recompui::set_cursor_visible(mouse_is_active); recompui::set_cursor_visible(mouse_is_active);
} }
Rml::ElementDocument* current_document = top_input_document(); Rml::ElementDocument* current_document = top_mouse_document();
if (current_document == nullptr) { if (current_document == nullptr) {
return; return;
} }
@ -286,7 +285,7 @@ public:
} }
void update_focus(bool mouse_moved, bool non_mouse_interacted) { void update_focus(bool mouse_moved, bool non_mouse_interacted) {
Rml::ElementDocument* current_document = top_input_document(); Rml::ElementDocument* current_document = top_mouse_document();
if (current_document == nullptr) { if (current_document == nullptr) {
return; return;
@ -341,12 +340,10 @@ public:
recompui::message_box("Attemped to show the same context twice"); recompui::message_box("Attemped to show the same context twice");
assert(false); assert(false);
} }
bool takes_input = context.takes_input();
Rml::ElementDocument* document = context.get_document(); Rml::ElementDocument* document = context.get_document();
shown_contexts.push_back(ContextDetails{ shown_contexts.push_back(ContextDetails{
.context = context, .context = context,
.document = document, .document = document
.takes_input = takes_input
}); });
// auto& on_show = context.on_show; // auto& on_show = context.on_show;
@ -383,8 +380,12 @@ public:
return std::find_if(shown_contexts.begin(), shown_contexts.end(), [context](auto& c){ return c.context == context; }) != shown_contexts.end(); return std::find_if(shown_contexts.begin(), shown_contexts.end(), [context](auto& c){ return c.context == context; }) != shown_contexts.end();
} }
bool is_context_taking_input() { bool is_context_capturing_input() {
return std::find_if(shown_contexts.begin(), shown_contexts.end(), [](auto& c){ return c.takes_input; }) != shown_contexts.end(); return std::find_if(shown_contexts.begin(), shown_contexts.end(), [](auto& c){ return c.context.captures_input(); }) != shown_contexts.end();
}
bool is_context_capturing_mouse() {
return std::find_if(shown_contexts.begin(), shown_contexts.end(), [](auto& c){ return c.context.captures_mouse(); }) != shown_contexts.end();
} }
bool is_any_context_shown() { bool is_any_context_shown() {
@ -394,7 +395,17 @@ public:
Rml::ElementDocument* top_input_document() { Rml::ElementDocument* top_input_document() {
// Iterate backwards and stop at the first context that takes input. // Iterate backwards and stop at the first context that takes input.
for (auto it = shown_contexts.rbegin(); it != shown_contexts.rend(); it++) { for (auto it = shown_contexts.rbegin(); it != shown_contexts.rend(); it++) {
if (it->takes_input) { if (it->context.captures_input()) {
return it->document;
}
}
return nullptr;
}
Rml::ElementDocument* top_mouse_document() {
// Iterate backwards and stop at the first context that takes input.
for (auto it = shown_contexts.rbegin(); it != shown_contexts.rend(); it++) {
if (it->context.captures_mouse()) {
return it->document; return it->document;
} }
} }
@ -556,8 +567,10 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s
bool config_was_open = recompui::is_context_shown(recompui::get_config_context_id()) || recompui::is_context_shown(recompui::get_config_sub_menu_context_id()); bool config_was_open = recompui::is_context_shown(recompui::get_config_context_id()) || recompui::is_context_shown(recompui::get_config_sub_menu_context_id());
while (recompui::try_deque_event(cur_event)) { while (recompui::try_deque_event(cur_event)) {
bool context_taking_input = recompui::is_context_taking_input(); bool context_capturing_input = recompui::is_context_capturing_input();
bool context_capturing_mouse = recompui::is_context_capturing_mouse();
if (!recomp::all_input_disabled()) { if (!recomp::all_input_disabled()) {
bool is_mouse_input = false;
// Implement some additional behavior for specific events on top of what RmlUi normally does with them. // Implement some additional behavior for specific events on top of what RmlUi normally does with them.
switch (cur_event.type) { switch (cur_event.type) {
case SDL_EventType::SDL_MOUSEMOTION: { case SDL_EventType::SDL_MOUSEMOTION: {
@ -582,11 +595,17 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s
case SDL_EventType::SDL_MOUSEBUTTONDOWN: case SDL_EventType::SDL_MOUSEBUTTONDOWN:
mouse_moved = true; mouse_moved = true;
mouse_clicked = true; mouse_clicked = true;
is_mouse_input = true;
break;
case SDL_EventType::SDL_MOUSEBUTTONUP:
case SDL_EventType::SDL_MOUSEWHEEL:
is_mouse_input = true;
break; break;
case SDL_EventType::SDL_CONTROLLERBUTTONDOWN: { case SDL_EventType::SDL_CONTROLLERBUTTONDOWN: {
int rml_key = cont_button_to_key(cur_event.cbutton); int rml_key = cont_button_to_key(cur_event.cbutton);
if (context_taking_input && rml_key) { if (context_capturing_input && rml_key) {
ui_state->context->ProcessKeyDown(RmlSDL::ConvertKey(rml_key), 0); ui_state->context->ProcessKeyDown(RmlSDL::ConvertKey(rml_key), 0);
} }
non_mouse_interacted = true; non_mouse_interacted = true;
@ -619,7 +638,7 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s
*await_stick_return = true; *await_stick_return = true;
non_mouse_interacted = true; non_mouse_interacted = true;
int rml_key = cont_axis_to_key(cur_event.caxis, axis_value); int rml_key = cont_axis_to_key(cur_event.caxis, axis_value);
if (context_taking_input && rml_key) { if (context_capturing_input && rml_key) {
ui_state->context->ProcessKeyDown(RmlSDL::ConvertKey(rml_key), 0); ui_state->context->ProcessKeyDown(RmlSDL::ConvertKey(rml_key), 0);
} }
} }
@ -632,8 +651,16 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s
break; break;
} }
if (context_taking_input) { // Send the event to RmlUi if this type of event is being captured.
RmlSDL::InputEventHandler(ui_state->context, cur_event); if (is_mouse_input) {
if (context_capturing_mouse) {
RmlSDL::InputEventHandler(ui_state->context, cur_event);
}
}
else {
if (context_capturing_input) {
RmlSDL::InputEventHandler(ui_state->context, cur_event);
}
} }
} }
@ -753,14 +780,24 @@ bool recompui::is_context_shown(ContextId context) {
return ui_state->is_context_shown(context); return ui_state->is_context_shown(context);
} }
bool recompui::is_context_taking_input() { bool recompui::is_context_capturing_input() {
std::lock_guard lock{ui_state_mutex}; std::lock_guard lock{ui_state_mutex};
if (!ui_state) { if (!ui_state) {
return false; return false;
} }
return ui_state->is_context_taking_input(); return ui_state->is_context_capturing_input();
}
bool recompui::is_context_capturing_mouse() {
std::lock_guard lock{ui_state_mutex};
if (!ui_state) {
return false;
}
return ui_state->is_context_capturing_mouse();
} }
bool recompui::is_any_context_shown() { bool recompui::is_any_context_shown() {