From 2a3d7f2e295772c8a8a5b1d83879599ac982aea3 Mon Sep 17 00:00:00 2001 From: maj00r Date: Thu, 15 Oct 2026 17:29:04 +0200 Subject: [PATCH] feat(editor): scripted mouse and keys of a self-test are delivered between frames like real input events, so a press seen by ImGui starts a drag as it does by hand; while a script drives the input the real mouse and keyboard are ignored --- application/application.cpp | 12 +++++++ application/editormode.cpp | 1 + application/scriptedinput.cpp | 66 ++++++++++++++++++++++++----------- application/scriptedinput.h | 14 +++++++- launcher/launchermode.h | 2 +- 5 files changed, 73 insertions(+), 22 deletions(-) diff --git a/application/application.cpp b/application/application.cpp index 8afd50db..3edcc8c0 100644 --- a/application/application.cpp +++ b/application/application.cpp @@ -80,6 +80,8 @@ void focus_callback(GLFWwindow *window, int focus) void cursor_enter_callback(GLFWwindow *window, int entered) { + if (scripted_input::driving()) + return; ui_layer::cursor_enter_callback(entered); } @@ -96,6 +98,8 @@ void window_resize_callback(GLFWwindow *, int w, int h) void cursor_pos_callback(GLFWwindow *window, double x, double y) { + if (scripted_input::driving()) + return; ui_layer::cursor_pos_callback(x, y); Global.cursor_pos = glm::ivec2(x, y); Application.on_cursor_pos(x, y); @@ -103,22 +107,30 @@ void cursor_pos_callback(GLFWwindow *window, double x, double y) void mouse_button_callback(GLFWwindow *window, int button, int action, int mods) { + if (scripted_input::driving()) + return; Application.on_mouse_button(button, action, mods); } void scroll_callback(GLFWwindow *window, double xoffset, double yoffset) { + if (scripted_input::driving()) + return; Application.on_scroll(xoffset, yoffset); } void key_callback(GLFWwindow *window, int key, int scancode, int action, int mods) { + if (scripted_input::driving()) + return; Application.on_key(key, scancode, action, mods); } void char_callback(GLFWwindow *window, unsigned int c) { + if (scripted_input::driving()) + return; Application.on_char(c); } diff --git a/application/editormode.cpp b/application/editormode.cpp index 58c55f8e..cab472a1 100644 --- a/application/editormode.cpp +++ b/application/editormode.cpp @@ -6232,6 +6232,7 @@ void editor_mode::render_change_history(){ void editor_mode::on_event_poll() { + m_selftest.input.deliver(); // game-engine style camera: WSAD/EQ only fly the camera while the right mouse button is held. // when it's released the keyboard is free for gizmo shortcuts, and we flush a zero-movement // command once so the camera doesn't keep coasting on the last velocity it was given. diff --git a/application/scriptedinput.cpp b/application/scriptedinput.cpp index b11ff8b4..5b505936 100644 --- a/application/scriptedinput.cpp +++ b/application/scriptedinput.cpp @@ -46,24 +46,41 @@ int modifier_of(int const Key) return Key == GLFW_KEY_LEFT_CONTROL ? GLFW_MOD_CONTROL : Key == GLFW_KEY_LEFT_SHIFT ? GLFW_MOD_SHIFT : Key == GLFW_KEY_LEFT_ALT ? GLFW_MOD_ALT : 0; } -} // namespace - -void scripted_input::modifiers() const +void modifiers(int const Mods) { auto &io{ImGui::GetIO()}; - io.AddKeyEvent(ImGuiMod_Ctrl, (m_mods & GLFW_MOD_CONTROL) != 0); - io.AddKeyEvent(ImGuiMod_Shift, (m_mods & GLFW_MOD_SHIFT) != 0); - io.AddKeyEvent(ImGuiMod_Alt, (m_mods & GLFW_MOD_ALT) != 0); + io.AddKeyEvent(ImGuiMod_Ctrl, (Mods & GLFW_MOD_CONTROL) != 0); + io.AddKeyEvent(ImGuiMod_Shift, (Mods & GLFW_MOD_SHIFT) != 0); + io.AddKeyEvent(ImGuiMod_Alt, (Mods & GLFW_MOD_ALT) != 0); +} + +} // namespace + +void scripted_input::queue(std::function Event) +{ + s_driving = true; + m_events.emplace_back(std::move(Event)); +} + +void scripted_input::deliver() +{ + auto const events{std::move(m_events)}; + m_events.clear(); + for (auto const &event : events) + event(); } void scripted_input::mouse_to(glm::dvec2 const &Screen) { - if (false == m_mouse.has_value()) - ui_layer::cursor_enter_callback(1); + bool const entered{m_mouse.has_value()}; m_mouse = Screen; - ui_layer::cursor_pos_callback(Screen.x, Screen.y); - Global.cursor_pos = glm::ivec2(Screen); - Application.on_cursor_pos(Screen.x, Screen.y); + queue([Screen, entered]() { + if (false == entered) + ui_layer::cursor_enter_callback(1); + ui_layer::cursor_pos_callback(Screen.x, Screen.y); + Global.cursor_pos = glm::ivec2(Screen); + Application.on_cursor_pos(Screen.x, Screen.y); + }); } bool scripted_input::dispatch(std::string const &Command, std::istringstream &Words) @@ -82,16 +99,21 @@ bool scripted_input::dispatch(std::string const &Command, std::istringstream &Wo std::string action; Words >> which >> action; auto const button{which == "right" ? GLFW_MOUSE_BUTTON_RIGHT : which == "middle" ? GLFW_MOUSE_BUTTON_MIDDLE : GLFW_MOUSE_BUTTON_LEFT}; - Application.on_mouse_button(button, action == "release" ? GLFW_RELEASE : GLFW_PRESS, m_mods); - modifiers(); + auto const state{action == "release" ? GLFW_RELEASE : GLFW_PRESS}; + queue([button, state, mods = m_mods]() { + Application.on_mouse_button(button, state, mods); + modifiers(mods); + }); return true; } if (Command == "wheel") { double offset{0.0}; Words >> offset; - Application.on_scroll(0.0, offset); - modifiers(); + queue([offset, mods = m_mods]() { + Application.on_scroll(0.0, offset); + modifiers(mods); + }); return true; } if (Command == "press" || Command == "release" || Command == "tap") @@ -107,14 +129,18 @@ bool scripted_input::dispatch(std::string const &Command, std::istringstream &Wo if (Command != "release") { m_mods |= modifier_of(key); - Application.on_key(key, 0, GLFW_PRESS, m_mods); - modifiers(); + queue([key, mods = m_mods]() { + Application.on_key(key, 0, GLFW_PRESS, mods); + modifiers(mods); + }); } if (Command != "press") { m_mods &= ~modifier_of(key); - Application.on_key(key, 0, GLFW_RELEASE, m_mods); - modifiers(); + queue([key, mods = m_mods]() { + Application.on_key(key, 0, GLFW_RELEASE, mods); + modifiers(mods); + }); } return true; } @@ -129,7 +155,7 @@ bool scripted_input::dispatch(std::string const &Command, std::istringstream &Wo unsigned int code{extra == 0 ? lead : lead & (0x3Fu >> extra)}; for (int k = 1; k <= extra && index + k < text.size(); ++k) code = (code << 6) | (static_cast(text[index + k]) & 0x3Fu); - Application.on_char(code); + queue([code]() { Application.on_char(code); }); index += extra + 1; } return true; diff --git a/application/scriptedinput.h b/application/scriptedinput.h index ee12a07a..cefebfe9 100644 --- a/application/scriptedinput.h +++ b/application/scriptedinput.h @@ -9,6 +9,7 @@ http://mozilla.org/MPL/2.0/. #pragma once +#include #include #include #include @@ -20,16 +21,23 @@ class scripted_input { public: bool dispatch(std::string const &Command, std::istringstream &Words); + void deliver(); void mouse_to(glm::dvec2 const &Screen); std::optional const &mouse() const { return m_mouse; } + static bool driving() + { + return s_driving; + } private: - void modifiers() const; + void queue(std::function Event); std::optional m_mouse; int m_mods{0}; + std::vector> m_events; + static inline bool s_driving{false}; }; class input_script @@ -37,6 +45,10 @@ class input_script public: explicit input_script(char const *Variable) : m_variable{Variable} {} void step(); + void deliver() + { + m_input.deliver(); + } private: char const *m_variable; diff --git a/launcher/launchermode.h b/launcher/launchermode.h index c7c22878..06dfaea4 100644 --- a/launcher/launchermode.h +++ b/launcher/launchermode.h @@ -32,7 +32,7 @@ public: void on_mouse_button( int const Button, int const Action, int const Mods ) override { /* not handled by the launcher */ } void on_scroll( double const Xoffset, double const Yoffset ) override { /* not handled by the launcher */ } void on_window_resize( int w, int h ) override; - void on_event_poll() override { /* not handled by the launcher */ } + void on_event_poll() override { m_script.deliver(); } bool is_command_processor() const override { return false; } private: