Nimbin[12]?SDL & Graphics / sdl_runtime_compiler / commits / 2001b84

sdl_runtime_compiler git · main

SDL3 game for running and compiling code at runtime

sdl3 c++ compiler dlopen cmake · first commit 2026-04-19 · last commit 2026-07-03 (3 months ago) · synced 3 days ago · upstream: git.ide3.de/hsnr/sdl-runtime-compiler

C++ 72.3% C 26.2%
git clone https://git.christianimmanuel.de/sdl-graphics/sdl_runtime_compiler.gitwget https://git.christianimmanuel.de/sdl-graphics/sdl_runtime_compiler/archive/sdl_runtime_compiler.tar.gz

Add Connection Pult (its a strart) With Claude

Christian Immanuel · 2026-06-05 14:26 · 2001b841de7947e67c48dd2647c291020c128ca0

 src/entities/pult.hpp          |   2 +
 src/states/game_state.cpp      | 140 ++++++++++++++++++++++++++++++++++++++++-
 src/states/game_state.hpp      |  18 ++++++
 src/systems/input_system.cpp   |  29 +++++++--
 src/systems/input_system.hpp   |   4 ++
 src/systems/program_system.cpp |   7 +++
 src/systems/program_system.hpp |   1 +
 src/systems/pult_system.cpp    |  12 +++-
 src/systems/pult_system.hpp    |  30 +++++----
 9 files changed, 223 insertions(+), 20 deletions(-)

diff --git a/src/entities/pult.hpp b/src/entities/pult.hpp
index 2692dea..042947a 100644
--- a/src/entities/pult.hpp
+++ b/src/entities/pult.hpp
@@ -18,6 +18,8 @@ enum class PultAction : uint8_t {
     ClearEditor,
     Compile,
     RunProgram,
+    ConnectProgram,
+    ConnectEditor,
 };
 
 
diff --git a/src/states/game_state.cpp b/src/states/game_state.cpp
index 5b34ec4..8ae80c7 100644
--- a/src/states/game_state.cpp
+++ b/src/states/game_state.cpp
@@ -199,6 +199,8 @@ GameState::pult_DispatchAction(PultAction act)
         case PultAction::RunProgram:
             on_Run();
             break;
+        case PultAction::ConnectProgram: connect_Begin(Conn::Program); break;
+        case PultAction::ConnectEditor:  break;
     }
 }
 
@@ -630,11 +632,18 @@ GameState::update(double dt)
                 case InputCommand::TextCommit:    on_TextCommit(input); break;
                 case InputCommand::TextCancel:    on_TextCancel(input); break;
                 case InputCommand::SandboxRestore:sandbox_Restore();    break;
+                case InputCommand::ConnectPultSubmit: conn_Submit();    break;
+                case InputCommand::ConnectPultExit:   connect_End();    break;
             }
         }
-        if (input.text_mode) {
+        if (input.capture_typing) {
+            // ctrl+WASD already set movement in InputSystem; don't clear it.
+            if (input.text_buffer != _conn_last) {
+                conn_RebuildPreview();
+                _conn_last = input.text_buffer;
+            }
+        } else if (input.text_mode) {
             input.forward = input.backward = input.left = input.right = false;
-
             if (input.text_buffer != _typing_last) {
                 typing_RebuildPreview(input.text_buffer);
                 _typing_last = input.text_buffer;
@@ -694,6 +703,8 @@ GameState::update(double dt)
         _plate_sys->update(dt, *_ctx.player, _step_plates);
 
         _editor_wall.update(dt, _ctx.camera->pos);
+
+        if (_conn != Conn::None) _conn_blink += dt;
     }
 
 
@@ -776,6 +787,14 @@ GameState::render(SDL_Window& window, GlRenderer& renderer)
             lbl.draw(renderer, *_ctx.camera, *_ctx.light);
     }
 
+    { FP_ZONE("render.wall_edit");
+        if (_conn != Conn::None) {
+            if (_conn_input) _conn_input->draw(renderer, *_ctx.camera, *_ctx.light);
+            conn_DrawCursor(renderer);
+            conn_DrawElectric(renderer);
+        }
+    }
+
     { FP_ZONE("render._carried");
         DynArray<CarriedWordNode*> nodes = _carried.nodes();
         for (const CarriedWordNode* node : nodes)
@@ -989,7 +1008,7 @@ GameState::substep(double dt)
 
     DynArray<ProjectileContactEvent> projectile_contacts;
 
-    DynArray<TextLabel*> all_labels = _pult_sys->labels();
+    DynArray<TextLabel*> all_labels = _pult_sys->get_Labels();
     { FP_ZONE("subst.hint_and_labels");
         _hint_sys->update(dt, *_ctx.camera);
         for (TextLabel* lbl : all_labels)
@@ -1348,3 +1367,118 @@ GameState::carry_BeginAnim(Text3dPtr word, bool with_pitch)
 
     _anims_add.push_back(std::move(anim));
 }
+
+
+void
+GameState::connect_Begin(Conn c)
+{
+    if (c != Conn::Program) return;   // Editor connect is Step 2
+    _conn          = c;
+    _conn_pult_pos = _pult_sys->get_ConnectProgramPos();
+    _frame_input.capture_typing = true;
+    _frame_input.text_buffer.clear();
+    _conn_last.clear();
+    _conn_input.reset();
+}
+
+void
+GameState::connect_End()
+{
+    _conn = Conn::None;
+    _frame_input.capture_typing = false;
+    _frame_input.text_buffer.clear();
+    _conn_last.clear();
+    _conn_input.reset();
+}
+
+void
+GameState::conn_Submit()
+{
+    const std::string line = _frame_input.text_buffer;
+    _frame_input.text_buffer.clear();
+    _conn_last.clear();
+    _conn_input.reset();
+
+    if (line == ":exit") { connect_End(); return; }
+    if (line == ":help") {
+        for (const char* h : { ":exit            leave the connection",
+                               "ctrl+WASD        move while connected",
+                               "<text> + Enter   send a line to the program (stdin)" })
+            _program_sys->echo_Input(_ctx.wall_texts, h);
+        return;
+    }
+
+    _program_sys->feed_Input(line);
+    _program_sys->echo_Input(_ctx.wall_texts, line);
+}
+
+void
+GameState::conn_RebuildPreview()
+{
+    Wall::Transform tf = Wall::get_Transform(Wall::Role::Program, ConfigGame::world_box_scale);
+    Vec3 pos = tf.position;
+    pos.y = ProgramSystem::start_y;
+
+    _conn_input = make_Text3d("> " + _frame_input.text_buffer,
+                              Text3d::depth_wall, 0.003f, pos, ConfigColor::white);
+    _conn_input->yaw = tf.yaw;
+}
+
+void
+GameState::conn_DrawCursor(GlRenderer& gl)
+{
+    if (!_conn_input) return;
+    if (SDL_fmod(_conn_blink, 0.8) > 0.4) return;   // blink: on half the cycle
+
+    Vec3 c = _conn_input->pos;
+    c.z   = _conn_input->pos.z - (static_cast<double>(_conn_input->half_extent) + 0.15);  // just past the line (wall +X → text runs along z)
+
+    Mat4 view  = buildViewMat4(*_ctx.camera);
+    Mat4 model = buildModelMat4(c, _conn_input->yaw);
+    Mat4 mvp   = Mat4::mul(gl.projection(), Mat4::mul(view, model));
+
+    gl.set_Wireframe(false);
+    gl.set_MVP(model, mvp);
+    gl.set_NormalMat(model);
+    gl.set_Light(*_ctx.light);
+    gl.set_Color(0.6f, 1.0f, 1.0f);
+    gl.set_Emissive(0.6f, 1.0f, 1.0f, 0.9f);
+    gl.draw_Box(0.04f, 0.22f, 0.02f);
+    gl.set_Emissive(0.0f, 0.0f, 0.0f, 0.0f);
+}
+
+void
+GameState::conn_DrawElectric(GlRenderer& gl)
+{
+    const Vec3 a = _ctx.camera->pos;
+    const Vec3 b = _conn_pult_pos;
+
+    constexpr int SEG = 16;
+    DynArray<float> pts;
+    pts.reserve(SEG * 6);
+    Vec3 prev = a;
+    for (int i = 1; i <= SEG; ++i) {
+        const double t = static_cast<double>(i) / SEG;
+        Vec3 p { a.x + (b.x - a.x) * t,
+                 a.y + (b.y - a.y) * t,
+                 a.z + (b.z - a.z) * t };
+        if (i < SEG) {                                  // jitter the interior for the spark look
+            const double j = 0.18;
+            p.x += ((rand() % 1000) / 1000.0 - 0.5) * j;
+            p.y += ((rand() % 1000) / 1000.0 - 0.5) * j;
+            p.z += ((rand() % 1000) / 1000.0 - 0.5) * j;
+        }
+        pts.push_back((float)prev.x); pts.push_back((float)prev.y); pts.push_back((float)prev.z);
+        pts.push_back((float)p.x);    pts.push_back((float)p.y);    pts.push_back((float)p.z);
+        prev = p;
+    }
+
+    Mat4 ident = Mat4::identity();
+    Mat4 mvp   = Mat4::mul(gl.projection(), buildViewMat4(*_ctx.camera));
+    gl.set_MVP(ident, mvp);
+    gl.set_NormalMat(ident);
+    gl.set_Wireframe(true);                 // wireframe path emits u_Color directly
+    gl.set_Color(0.5f, 0.9f, 1.0f);
+    gl.draw_Lines(pts.data(), static_cast<int>(pts.size()) / 3);
+    gl.set_Wireframe(false);
+}
diff --git a/src/states/game_state.hpp b/src/states/game_state.hpp
index 8a0adf0..4a92833 100644
--- a/src/states/game_state.hpp
+++ b/src/states/game_state.hpp
@@ -72,6 +72,24 @@ class GameState : public IState
     double _accumulator    { 0.0 };
     double _alpha          { 0.0 };
 
+
+
+    // TODO START: OBJECT ORIENT ME 
+    enum class Conn : uint8_t { None, Program, Editor };
+    Conn        _conn        { Conn::None };
+    std::string _conn_last;            // last previewed buffer
+    Text3dPtr   _conn_input;           // live typed-line preview on the wall
+    double      _conn_blink  { 0.0 };  // cursor blink accumulator
+    Vec3        _conn_pult_pos {};     // anchor for the electric line
+    void connect_Begin       (Conn c);
+    void connect_End         ();
+    void conn_Submit         ();
+    void conn_RebuildPreview ();
+    void conn_DrawCursor     (GlRenderer& gl);
+    void conn_DrawElectric   (GlRenderer& gl);
+    // TODO END: OBJECT ORIENT ME 
+
+
     DynArray<WallLabel> _wall_labels;
 
     DynArray<Text3d*>   _typing_words;
diff --git a/src/systems/input_system.cpp b/src/systems/input_system.cpp
index 1e0bc08..0d53365 100644
--- a/src/systems/input_system.cpp
+++ b/src/systems/input_system.cpp
@@ -29,6 +29,25 @@ InputSystem::handle_Event(Game& game, const SDL_Event& e, FrameInput& input)
         case SDL_EVENT_KEY_DOWN: {
             SDL_Keycode sym = e.key.key;
 
+            if (input.capture_typing) {
+                const bool ctrl = (e.key.mod & SDL_KMOD_CTRL);
+                if (ctrl) {
+                    switch (sym) {
+                        case SDLK_W: input.forward  = true; break;
+                        case SDLK_A: input.left     = true; break;
+                        case SDLK_S: input.backward = true; break;
+                        case SDLK_D: input.right    = true; break;
+                    }
+                } else if (sym == SDLK_RETURN) {
+                    input.commands.push_back(InputCommand::ConnectPultSubmit);
+                } else if (sym == SDLK_ESCAPE) {
+                    input.commands.push_back(InputCommand::ConnectPultExit);
+                } else if (sym == SDLK_BACKSPACE && !input.text_buffer.empty()) {
+                    input.text_buffer.pop_back();
+                }
+                break;
+            }
+
             if (input.text_mode) {
                 if (sym == SDLK_RETURN)
                     input.commands.push_back(InputCommand::TextCommit);
@@ -47,7 +66,7 @@ InputSystem::handle_Event(Game& game, const SDL_Event& e, FrameInput& input)
                 case SDLK_D: input.right    = true; break;
                 case SDLK_E: input.pick     = true; break;
 
-                case SDLK_F1:     game.change_State(cpputils::make_unique<GameState>());  break;
+                case SDLK_F1:     game.change_State(cpputils::make_unique<GameState>()); break;
                 case SDLK_F2:     input.commands.push_back(InputCommand::SandboxRestore);break;
                 case SDLK_SPACE:  input.commands.push_back(InputCommand::Jump);          break;
                 case SDLK_Q:      input.commands.push_back(InputCommand::Drop);          break;
@@ -58,7 +77,7 @@ InputSystem::handle_Event(Game& game, const SDL_Event& e, FrameInput& input)
                 case SDLK_T:      input.commands.push_back(InputCommand::TextStart);     break;
                 case SDLK_LSHIFT:
                 case SDLK_RSHIFT: input.commands.push_back(InputCommand::FreelookStart); break;
-                case SDLK_ESCAPE: game.change_State(cpputils::make_unique<MenuState>());  break;
+                case SDLK_ESCAPE: game.change_State(cpputils::make_unique<MenuState>()); break;
                 case SDLK_PLUS:
                     if (e.key.mod & SDL_KMOD_CTRL)
                         input.commands.push_back(InputCommand::NumberInc);
@@ -83,13 +102,15 @@ InputSystem::handle_Event(Game& game, const SDL_Event& e, FrameInput& input)
                 case SDLK_E: input.pick     = false; break;
 
                 case SDLK_LSHIFT:
-                case SDLK_RSHIFT: input.commands.push_back(InputCommand::FreelookEnd); break;
+                case SDLK_RSHIFT:
+                    if (!input.capture_typing) input.commands.push_back(InputCommand::FreelookEnd);
+                    break;
             }
             break;
         }
 
         case SDL_EVENT_TEXT_INPUT:
-            if (input.text_mode)
+            if (input.capture_typing || input.text_mode)
                 input.text_buffer += e.text.text;
             break;
     }
diff --git a/src/systems/input_system.hpp b/src/systems/input_system.hpp
index 298b7e8..c819ca5 100644
--- a/src/systems/input_system.hpp
+++ b/src/systems/input_system.hpp
@@ -19,6 +19,8 @@ enum class InputCommand : uint8_t {
     NumberInc, NumberDec,
     TextStart, TextCommit, TextCancel,
     SandboxRestore,
+    ConnectPultSubmit,
+    ConnectPultExit,
 };
 
 
@@ -29,6 +31,8 @@ struct FrameInput {
     bool right    = false;
     bool pick     = false;
     bool shoot    = false;
+    bool capture_typing = false;
+
 
     int  look_x   = 0;
     int  look_y   = 0;
diff --git a/src/systems/program_system.cpp b/src/systems/program_system.cpp
index 8b9f8f3..5982cd2 100644
--- a/src/systems/program_system.cpp
+++ b/src/systems/program_system.cpp
@@ -165,6 +165,13 @@ ProgramSystem::feed_Input(const std::string& text)
 }
 
 
+void
+ProgramSystem::echo_Input(TextWallList& texts, const std::string& line)
+{
+    push_Line(texts, line, ConfigGame::world_box_scale);   // scrolls up, lands at the prompt row
+}
+
+
 void
 ProgramSystem::echo_Words(TextWallList& texts, DynArray<Text3dPtr>& words, float world_scale)
 {
diff --git a/src/systems/program_system.hpp b/src/systems/program_system.hpp
index dbf5be5..34b964b 100644
--- a/src/systems/program_system.hpp
+++ b/src/systems/program_system.hpp
@@ -28,6 +28,7 @@ public:
     void run        (TextWallList&, float world_scale);
     void update     (TextWallList&, float world_scale);
     void feed_Input (const std::string& text);   // thrown word(s) → child stdin
+    void echo_Input (TextWallList&, const std::string& line);
     // Park already-thrown (animated) words on the Program wall as one echoed
     // input line, scrolling existing output up — terminal style.
     void echo_Words (TextWallList&, DynArray<Text3dPtr>& words, float world_scale);
diff --git a/src/systems/pult_system.cpp b/src/systems/pult_system.cpp
index f857828..7ab1f9a 100644
--- a/src/systems/pult_system.cpp
+++ b/src/systems/pult_system.cpp
@@ -71,6 +71,10 @@ PultSystem::init(DynArray<GameObjectPtr>& scenery,
 
     Pult* run     = spawn_pult("Run",     Wall::Role::Program,  lane_2);
     add_btn(run,     "RUN",   PultAction::RunProgram,       0.0f, 0.0f);
+
+    Pult* connp   = spawn_pult("Connect", Wall::Role::Program,  lane_3);
+    add_btn(connp,   "CONN",  PultAction::ConnectProgram,   0.0f, 0.0f);
+    _connect_program = connp;
 }
 
 
@@ -165,10 +169,16 @@ PultSystem::draw_Labels(GlRenderer& gl, const Camera& camera, const Light& light
 
 
 DynArray<TextLabel*>
-PultSystem::labels()
+PultSystem::get_Labels()
 {
     DynArray<TextLabel*> out;
     out.reserve(_pults.size());
     for (Pult* p : _pults) out.push_back(&p->label);
     return out;
 }
+
+Vec3
+PultSystem::get_ConnectProgramPos() const
+{
+    return _connect_program ? _connect_program->pos : Vec3{};
+}
diff --git a/src/systems/pult_system.hpp b/src/systems/pult_system.hpp
index 92d5079..a1a5819 100644
--- a/src/systems/pult_system.hpp
+++ b/src/systems/pult_system.hpp
@@ -7,30 +7,36 @@
 #include "entities/pult.hpp"
 #include "utils/globals.hpp"
 
-class GlRenderer;
-struct Light;
+class  GlRenderer;
 class  TextLabel;
-struct ProjectileContactEvent;
+struct Light;
 struct Player;
+struct ProjectileContactEvent;
 
 class PultSystem
 {
     DynArray<Pult*> _pults;
+    Pult*           _connect_program { nullptr };
 public:
-    void init(DynArray<GameObjectPtr>& scenery,
-              DynArray<PultButton*>&   buttons_out,
-              float                    world_scale);
+    void init                    (DynArray<GameObjectPtr>& scenery,
+                                  DynArray<PultButton*>&   buttons_out,
+                                  float                    world_scale);
     std::optional<PultAction>
-         try_Activate(GameObject* obj,
-                      const DynArray<PultButton*>& buttons);
+         try_Activate            (GameObject* obj,
+                                  const DynArray<PultButton*>& buttons);
     std::optional<PultAction>
          handle_ProjectileContact(const ProjectileContactEvent& ev,
                                   const DynArray<PultButton*>&  buttons);
     std::optional<PultAction>
-         handle_PlayerContact(const Player& player,
-                              const DynArray<PultButton*>& buttons);
-    void update(double dt, const Camera& camera);
+         handle_PlayerContact    (const Player& player,
+                                  const DynArray<PultButton*>& buttons);
+    Vec3 get_ConnectProgramPos   () const;
+
+    DynArray<TextLabel*>
+         get_Labels ();
+
+    void update     (double dt, const Camera& camera);
     void draw_Labels(GlRenderer& gl, const Camera& camera, const Light& light) const;
 
-    DynArray<TextLabel*> labels();
+
 };