Nimbin[12]?SDL & Graphics / sdl_runtime_compiler / commits / 1288b14

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 Typing AND inc/dec numbers With Claude

Christian Immanuel · 2026-05-30 19:52 · 1288b140bc92bf2400556831084e2330d64359b5

 src/assets/sandbox.c.last_ok |   3 +-
 src/core/game.cpp            |   2 +
 src/states/game_state.cpp    | 116 ++++++++++++++++++++++++++++++++++++++++---
 src/states/game_state.hpp    |   7 +++
 src/systems/input_system.cpp |  25 ++++++++++
 src/systems/input_system.hpp |  12 ++++-
 6 files changed, 154 insertions(+), 11 deletions(-)

diff --git a/src/assets/sandbox.c.last_ok b/src/assets/sandbox.c.last_ok
index a6049fc..37f4703 100644
--- a/src/assets/sandbox.c.last_ok
+++ b/src/assets/sandbox.c.last_ok
@@ -21,7 +21,8 @@ int game_build ( ModObject * out , int max ) {
 ( void ) max ;
 o = out ; n = 0 ;
 box ( 0 , - 16 , 5 , 1 , 1 , 1 ) ;
+box(2,-15,4,2,2,2);
 text ( 0 , - 15 , 5 , "hi" ) ;
-box ( 3 , - 16 , 5 , 1 , 2 , 1 ) ;
+box ( 6 , - 15 , 10 , 1 , 1 , 5 ) ;
 return n ;
 }
diff --git a/src/core/game.cpp b/src/core/game.cpp
index 3d25d00..af565aa 100644
--- a/src/core/game.cpp
+++ b/src/core/game.cpp
@@ -35,6 +35,8 @@ Game::init()
     if (!gl_renderer->init(window))
         return false;
 
+    SDL_StartTextInput(window);
+
     changeState(cpputils::make_unique<MenuState>());
     return true;
 }
diff --git a/src/states/game_state.cpp b/src/states/game_state.cpp
index 0ec8b41..8a500ef 100644
--- a/src/states/game_state.cpp
+++ b/src/states/game_state.cpp
@@ -362,6 +362,27 @@ GameState::on_FreelookEnd()
                           ctx.player->yaw, ctx.player->pitch);
 }
 
+void
+GameState::on_NumberAdjust(int delta)
+{
+    if (carried.empty() || !carried.last || !carried.last->word) return;
+    Text3d* w = carried.last->word.get();
+
+    // Parse the text as an integer; bail if it isn't one.
+    const std::string& s = w->text;
+    if (s.empty()) return;
+    size_t start = (s[0] == '-') ? 1 : 0;
+    if (start == s.size()) return;
+    for (size_t i = start; i < s.size(); ++i)
+        if (!std::isdigit(static_cast<unsigned char>(s[i]))) return;  // not a plain integer
+
+    long val = std::strtol(s.c_str(), nullptr, 10);
+    val += delta;
+
+    w->text = std::to_string(val);
+    w->rebuild_Mesh();   // regenerate glyphs + half_extent for the new text
+}
+
 
 void
 GameState::step_Spawn(const std::vector<std::string>& tokens)
@@ -478,14 +499,42 @@ GameState::update(double dt)
     { FP_ZONE("update.input_dispatch");
         for (InputCommand cmd : input.commands) {
             switch (cmd) {
-                case InputCommand::Jump:          on_Jump();          break;
-                case InputCommand::Click:         on_Click();         break;
-                case InputCommand::Drop:          on_DropLast();      break;
-                case InputCommand::ShootAll:      on_ShootAll();      break;
-                case InputCommand::Compile:       on_Compile();       break;
-                case InputCommand::DebugPrint:    on_DebugPrint();    break;
-                case InputCommand::FreelookStart: on_FreelookStart(); break;
-                case InputCommand::FreelookEnd:   on_FreelookEnd();   break;
+                case InputCommand::Jump:          on_Jump();           break;
+                case InputCommand::Click:         on_Click();          break;
+                case InputCommand::Drop:          on_DropLast();       break;
+                case InputCommand::ShootAll:      on_ShootAll();       break;
+                case InputCommand::Compile:       on_Compile();        break;
+                case InputCommand::DebugPrint:    on_DebugPrint();     break;
+                case InputCommand::FreelookStart: on_FreelookStart();  break;
+                case InputCommand::FreelookEnd:   on_FreelookEnd();    break;
+                case InputCommand::NumberInc:     on_NumberAdjust(+1); break;
+                case InputCommand::NumberDec:     on_NumberAdjust(-1); break;
+                case InputCommand::TextStart:
+                    input.text_mode = true;
+                    input.text_buffer.clear();
+                    typing_words.clear();
+                    typing_last.clear();
+                    break;
+                case InputCommand::TextCommit:
+                    typing_words.clear();
+                    typing_last.clear();
+                    input.text_mode = false;
+                    input.text_buffer.clear();
+                    break;
+                case InputCommand::TextCancel:
+                    typing_ClearPreview();
+                    typing_last.clear();
+                    input.text_mode = false;
+                    input.text_buffer.clear();
+                    break;
+            }
+        }
+        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;
             }
         }
         if (pick_cooldown > 0.0) pick_cooldown -= dt;
@@ -1209,7 +1258,58 @@ GameState::sandbox_LoadFileToWall()
     }
 }
 
+
 void GameState::sandbox_Restore() {
     if (SourceTokens::copy_File(sandbox_BackupPath(), sandbox_CanonicalPath()))
         sandbox_LoadFileToWall();
 }
+
+
+void
+GameState::carry_TypedText(const std::string& s)
+{
+    auto [anchor, right, glyph_yaw] = anchorframe_Current();
+
+    std::istringstream in(s);
+    std::string tok;
+    while (in >> tok) {            // >> splits on any run of whitespace, skips leading/trailing
+        Text3dPtr t = make_Text3d(tok, Text3d::depth_floor, 0.003f,
+                                  anchor, Text3d::color_floor);
+        t->yaw = static_cast<float>(glyph_yaw);
+        t->body.mode = PhysicsMode::Carried;
+        carried.push(std::move(t));
+    }
+}
+
+
+void
+GameState::typing_RebuildPreview(const std::string& s)
+{
+    // Remove the previous preview words from the carry line.
+    typing_ClearPreview();
+
+    auto [anchor, right, glyph_yaw] = anchorframe_Current();
+    std::istringstream in(s);
+    std::string tok;
+    while (in >> tok) {
+        Text3dPtr t = make_Text3d(tok, Text3d::depth_floor, 0.003f,
+                                  anchor, Text3d::color_floor);
+        t->yaw = static_cast<float>(glyph_yaw);
+        t->body.mode = PhysicsMode::Carried;
+        typing_words.push_back(t.get());   // track raw ptr for later removal
+        carried.push(std::move(t));
+    }
+}
+
+
+void
+GameState::typing_ClearPreview()
+{
+    // Preview words are the most recently pushed → at the tail. Pop exactly
+    // as many as we added; let them be destroyed (don't re-add to scene).
+    for (size_t i = 0; i < typing_words.size(); ++i) {
+        if (carried.empty()) break;
+        carried.pop_back();   // returns Text3dPtr; dropping it frees the word
+    }
+    typing_words.clear();
+}
diff --git a/src/states/game_state.hpp b/src/states/game_state.hpp
index 639837a..e954df0 100644
--- a/src/states/game_state.hpp
+++ b/src/states/game_state.hpp
@@ -81,6 +81,9 @@ class GameState : public IState
     double accumulator    { 0.0 };
     double alpha          { 0.0 };
 
+    std::vector<Text3d*> typing_words;
+    std::string          typing_last;
+
     std::vector<Projectile>    projectiles;
     std::vector<WordAnim_Add>  anims_add;
     std::vector<WordAnim_Drop> anims_drop;
@@ -115,6 +118,7 @@ class GameState : public IState
     void on_FreelookStart();
     void on_FreelookEnd();
     void on_Compile();
+    void on_NumberAdjust(int dt);
 
     void scene_Setup(const Scene& scene);
     void scene_Teardown();
@@ -135,6 +139,9 @@ class GameState : public IState
     void sandbox_Restore();          // copy backup → canonical, reload wall
     std::string sandbox_CanonicalPath() const;
     std::string sandbox_BackupPath()    const;
+    void carry_TypedText(const std::string& s);
+    void typing_RebuildPreview(const std::string& s);
+    void typing_ClearPreview();
 
     bool        level_TryAdvance();
     void        level_Advance();
diff --git a/src/systems/input_system.cpp b/src/systems/input_system.cpp
index bd5ccf1..282d78f 100644
--- a/src/systems/input_system.cpp
+++ b/src/systems/input_system.cpp
@@ -29,6 +29,17 @@ InputSystem::handle_Event(Game& game, const SDL_Event& e, FrameInput& input)
         case SDL_EVENT_KEY_DOWN: {
             SDL_Keycode sym = e.key.key;
 
+            if (input.text_mode) {
+                if (sym == SDLK_RETURN)
+                    input.commands.push_back(InputCommand::TextCommit);
+                else if (sym == SDLK_ESCAPE)
+                    input.commands.push_back(InputCommand::TextCancel);
+                else if (sym == SDLK_BACKSPACE && !input.text_buffer.empty())
+                    input.text_buffer.pop_back();
+                break;   // swallow everything else — no movement while typing
+            }
+
+
             switch (sym) {
                 case SDLK_W: input.forward  = true; break;
                 case SDLK_A: input.left     = true; break;
@@ -42,9 +53,18 @@ InputSystem::handle_Event(Game& game, const SDL_Event& e, FrameInput& input)
                 case SDLK_F:      input.commands.push_back(InputCommand::ShootAll);      break;
                 case SDLK_C:      input.commands.push_back(InputCommand::Compile);       break;
                 case SDLK_P:      input.commands.push_back(InputCommand::DebugPrint);    break;
+                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.changeState(cpputils::make_unique<MenuState>());  break;
+                case SDLK_PLUS:
+                    if (e.key.mod & SDL_KMOD_CTRL)
+                        input.commands.push_back(InputCommand::NumberInc);
+                    break;
+                case SDLK_MINUS:
+                    if (e.key.mod & SDL_KMOD_CTRL)
+                        input.commands.push_back(InputCommand::NumberDec);
+                    break;
 
             }
             break;
@@ -65,5 +85,10 @@ InputSystem::handle_Event(Game& game, const SDL_Event& e, FrameInput& input)
             }
             break;
         }
+
+        case SDL_EVENT_TEXT_INPUT:
+            if (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 c93d7d4..85bba4b 100644
--- a/src/systems/input_system.hpp
+++ b/src/systems/input_system.hpp
@@ -1,5 +1,7 @@
 #pragma once
+
 #include <SDL3/SDL.h>
+#include <string>
 #include <vector>
 
 class Game;
@@ -11,8 +13,9 @@ enum class InputCommand : uint8_t {
     ShootAll,
     Compile,
     DebugPrint,
-    FreelookStart,
-    FreelookEnd
+    FreelookStart, FreelookEnd,
+    NumberInc, NumberDec,
+    TextStart, TextCommit, TextCancel
 };
 
 
@@ -27,6 +30,11 @@ struct FrameInput {
     int  look_x   = 0;
     int  look_y   = 0;
 
+    bool        text_mode   = false;     // currently typing
+    std::string text_buffer;             // accumulated text
+    bool        text_commit = false;     // Enter pressed this frame
+    bool        text_cancel = false;     // Esc pressed this frame
+
     std::vector<InputCommand> commands;
 };