From ebd1e222d75a53bcc1107d26ebafa79d7c01c97c Mon Sep 17 00:00:00 2001 From: Ritchie Cunningham Date: Tue, 30 Sep 2025 21:37:24 +0100 Subject: [PATCH] [Add] File saving for the fancy new text editor. --- client/src/game_state.cpp | 13 +++++++++++++ client/src/terminal.cpp | 5 +++++ client/src/terminal.h | 1 + client/src/ui/desktop.cpp | 11 +++++++++++ client/src/ui/desktop.h | 2 ++ client/src/ui/editor.cpp | 18 ++++++++++++++++-- client/src/ui/editor.h | 3 +++ client/src/ui/i_window_content.h | 3 +++ client/src/ui/window_action.h | 14 ++++++++++++++ common/src/command_processor.cpp | 5 +++++ common/src/command_processor.h | 1 + server/src/network_manager.cpp | 19 +++++++++++++++++-- 12 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 client/src/ui/window_action.h diff --git a/client/src/game_state.cpp b/client/src/game_state.cpp index 4959f29..6023a0a 100644 --- a/client/src/game_state.cpp +++ b/client/src/game_state.cpp @@ -12,6 +12,7 @@ #include "ui/desktop.h" #include "ui/i_window_content.h" #include "ui/ui_window.h" +#include "ui/window_action.h" #include #include @@ -153,6 +154,18 @@ void GameState::update(void) { } } if(_desktop) { + WindowAction action = _desktop->get_pending_action(); + switch(action.type) { + case ActionType::WRITE_FILE: { + std::string message = "WRITEF::" + action.payload1 + "::" + action.payload2; + _network->send(message); + break; + } + case ActionType::NONE: + default: + /* Do nothing. */ + break; + } /* * TODO: These fuck'in window dimensions just need to be global at this point!! * Pass GameState by reference ? diff --git a/client/src/terminal.cpp b/client/src/terminal.cpp index f65afa3..fcca88d 100644 --- a/client/src/terminal.cpp +++ b/client/src/terminal.cpp @@ -8,6 +8,7 @@ #include "client_network.h" #include "gfx/txt_renderer.h" #include "gfx/types.h" +#include "ui/window_action.h" Terminal::Terminal(ClientNetwork* network) : _network(network), _should_close(false), _scroll_offset(0), @@ -44,6 +45,10 @@ void Terminal::set_prompt(const std::string& prompt) { bool Terminal::should_close(void) { return _should_close; } +WindowAction Terminal::get_pending_action(void) { + return { ActionType::NONE }; +} + void Terminal::_on_ret_press(void) { std::string command = _input_buffer.get_line(0); _input_buffer.newline(); /* Add newline to buffer for histroy. */ diff --git a/client/src/terminal.h b/client/src/terminal.h index e3bbcbd..bb77b3c 100644 --- a/client/src/terminal.h +++ b/client/src/terminal.h @@ -22,6 +22,7 @@ public: void add_history(const std::string& line); void set_prompt(const std::string& prompt); bool should_close(void) override; + WindowAction get_pending_action(void) override; private: void _on_ret_press(void); diff --git a/client/src/ui/desktop.cpp b/client/src/ui/desktop.cpp index 1801be2..5d1007e 100644 --- a/client/src/ui/desktop.cpp +++ b/client/src/ui/desktop.cpp @@ -16,6 +16,7 @@ #include #include #include "ui/ui_window.h" +#include "ui/window_action.h" static const std::string& get_random_snippet(const std::vector& snippets) { if(snippets.empty()) { @@ -171,6 +172,16 @@ void Desktop::update(int screen_width, int screen_height) { _windows.end()); } +WindowAction Desktop::get_pending_action(void) { + if(_focused_window) { + IWindowContent* content = _focused_window->get_content(); + if(content) { + return content->get_pending_action(); + } + } + return { ActionType::NONE }; +} + UIWindow* Desktop::get_focused_window(void) { return _focused_window; } diff --git a/client/src/ui/desktop.h b/client/src/ui/desktop.h index e01efb3..ba56026 100644 --- a/client/src/ui/desktop.h +++ b/client/src/ui/desktop.h @@ -10,6 +10,7 @@ #include "ui/ui_window.h" #include "ui/taskbar.h" #include "ui/launcher.h" +#include "ui/window_action.h" /* Animated background stuff. */ struct ScrollingText { @@ -31,6 +32,7 @@ public: void render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer, int screen_height, bool show_cursor); + WindowAction get_pending_action(void); UIWindow* get_focused_window(void); private: diff --git a/client/src/ui/editor.cpp b/client/src/ui/editor.cpp index e66caa9..92a6d55 100644 --- a/client/src/ui/editor.cpp +++ b/client/src/ui/editor.cpp @@ -1,8 +1,10 @@ #include "editor.h" #include #include "gfx/txt_renderer.h" +#include "ui/window_action.h" -Editor::Editor(void) : _should_close(false) { +Editor::Editor(void) + : _should_close(false), _pending_action({ActionType::NONE}) { _view = std::make_unique(&_buffer, true); } @@ -14,7 +16,13 @@ void Editor::update(void) { void Editor::handle_input(SDL_Event* event) { /* We don't care about the return val here. RET is just newline. */ - _view->handle_event(event); + if(event->type == SDL_EVENT_KEY_DOWN && event->key.key == SDLK_S && + (event->key.mod & SDL_KMOD_CTRL)) { + /* C-S pressed, create a save action. */ + _pending_action = { ActionType::WRITE_FILE, "test.txt", _buffer.get_text() }; + } else { + _view->handle_event(event); + } } void Editor::render(TextRenderer* renderer, int x, int y, int width, int height, @@ -29,3 +37,9 @@ void Editor::scroll(int amount, int content_height) { bool Editor::should_close(void) { return _should_close; } + +WindowAction Editor::get_pending_action(void) { + WindowAction action = _pending_action; + _pending_action.type = ActionType::NONE; /* Clear action. */ + return action; +} diff --git a/client/src/ui/editor.h b/client/src/ui/editor.h index 3d16a10..13666b6 100644 --- a/client/src/ui/editor.h +++ b/client/src/ui/editor.h @@ -6,6 +6,7 @@ #include "i_window_content.h" #include "ui/text_buffer.h" #include "text_view.h" +#include "ui/window_action.h" class Editor : public IWindowContent { public: @@ -18,9 +19,11 @@ public: bool show_cursor) override; void scroll(int amount, int content_height) override; bool should_close(void) override; + WindowAction get_pending_action(void) override; private: TextBuffer _buffer; std::unique_ptr _view; bool _should_close; + WindowAction _pending_action; }; diff --git a/client/src/ui/i_window_content.h b/client/src/ui/i_window_content.h index fb7b35a..a6c4718 100644 --- a/client/src/ui/i_window_content.h +++ b/client/src/ui/i_window_content.h @@ -2,6 +2,8 @@ #include +#include "window_action.h" + class TextRenderer; class IWindowContent { @@ -13,4 +15,5 @@ public: bool show_cursor = 0) = 0; virtual void scroll(int amount, int content_height) = 0; virtual bool should_close(void) = 0; + virtual WindowAction get_pending_action() = 0; }; diff --git a/client/src/ui/window_action.h b/client/src/ui/window_action.h new file mode 100644 index 0000000..37e7b30 --- /dev/null +++ b/client/src/ui/window_action.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +enum class ActionType { + NONE, + WRITE_FILE +}; + +struct WindowAction { + ActionType type = ActionType::NONE; + std::string payload1; /* i.e., filename. */ + std::string payload2; /* i.e., content. */ +}; diff --git a/common/src/command_processor.cpp b/common/src/command_processor.cpp index d62581b..c56d566 100644 --- a/common/src/command_processor.cpp +++ b/common/src/command_processor.cpp @@ -3,6 +3,7 @@ #include "command_processor.h" #include "vfs.h" +#include "lua_api.h" #include "lua_processor.h" #include "machine_manager.h" #include "machine.h" @@ -74,6 +75,10 @@ std::string CommandProcessor::process_command(const std::string& command) { return "Unknown command: " + command_name + "\n"; } +std::string CommandProcessor::write_file(const std::string& path, const std::string& content) { + return api::write_file(*this, path, content); +} + /* Find a VFS node by it's absolute path. */ vfs_node* find_node_by_path(vfs_node* root, const std::string& path) { if(path == "/") { diff --git a/common/src/command_processor.h b/common/src/command_processor.h index e13ac17..5a0d6b3 100644 --- a/common/src/command_processor.h +++ b/common/src/command_processor.h @@ -13,6 +13,7 @@ public: ~CommandProcessor(void); std::string process_command(const std::string& command); + std::string write_file(const std::string& path, const std::string& content); /* Public interface for API functions. */ vfs_node* get_current_dir(void); diff --git a/server/src/network_manager.cpp b/server/src/network_manager.cpp index d25fd62..4750e12 100644 --- a/server/src/network_manager.cpp +++ b/server/src/network_manager.cpp @@ -107,13 +107,28 @@ void NetworkManager::on_message(std::shared_ptr connection, return; } - fprintf(stderr, "[Player %u] Command: '%s'\n", player->id, message.c_str()); + /* Check for "special" message prefixes. */ + if(message.rfind("WRITEF::", 0) == 0) { + /* Message format: WRITEF::/path/to/file::content. */ + std::string payload = message.substr(8); + size_t separator_pos = payload.find("::"); + if(separator_pos != std::string::npos) { + std::string filepath = payload.substr(0, separator_pos); + std::string content = payload.substr(separator_pos+2); + fprintf(stderr, "[Player %u] Write file: \'%s\'\n", player->id, filepath.c_str()); + player->cmd_processor->write_file(filepath, content); + /* Response not required for a file write. */ + } + return; + } + + /* If no prefix, treat as normal terminal command. */ + fprintf(stderr, "[Player %u] Command: '%s'\n", player->id, message.c_str()); std::string response = player->cmd_processor->process_command(message); if(response == "__CLOSE_CONNECTION__") { connection->send(response); - /* Just let me close the f.cking terminal? */ return; }