From 63991fd940914be5ad22a6d835d05018f3abb327 Mon Sep 17 00:00:00 2001 From: rasmus Date: Sun, 21 Dec 2025 20:11:16 +0100 Subject: [PATCH] Works, I guess --- Desktop_Test/Desktop_Test.ino | 66 +----- Desktop_Test/GLOBALS.h | 4 +- Desktop_Test/helpers.cpp | 14 ++ Desktop_Test/helpers.h | 8 + Desktop_Test/program.cpp | 30 +++ Desktop_Test/program.h | 70 +++++++ Desktop_Test/shell.cpp | 356 ++++++++++++++++++++++++++++++++ Desktop_Test/shell.h | 42 ++++ Desktop_Test/system_manager.cpp | 26 +++ Desktop_Test/system_manager.h | 9 +- 10 files changed, 565 insertions(+), 60 deletions(-) create mode 100644 Desktop_Test/helpers.cpp create mode 100644 Desktop_Test/helpers.h create mode 100644 Desktop_Test/program.cpp create mode 100644 Desktop_Test/program.h create mode 100644 Desktop_Test/shell.cpp create mode 100644 Desktop_Test/shell.h diff --git a/Desktop_Test/Desktop_Test.ino b/Desktop_Test/Desktop_Test.ino index 2cd39f9..4d52f58 100644 --- a/Desktop_Test/Desktop_Test.ino +++ b/Desktop_Test/Desktop_Test.ino @@ -1,13 +1,15 @@ -#include "window_manager.h" +#include "shell.h" #include "input_manager.h" #include "tft_handler.h" #include "event_manager.h" #include "GLOBALS.h" +#include "system_manager.h" #include -WindowManager* wm; +Shell* _shell; InputManager* im; TFT_Handler* th; +SystemManager* sm; DISPLAY_STATE* _display_state; @@ -29,84 +31,34 @@ void setup() { _display_state->update_display.store(false); // Create managers - wm = new WindowManager(); + _shell = new Shell(); im = new InputManager(); th = new TFT_Handler(); + sm = new SystemManager(); // Initialize in order th->init(_display_state); + sm->init(_display_state); // Initialize event manager (creates dispatcher task) EventManager::getInstance().init(); // Initialize components (they subscribe to events) im->init(_display_state, th); - wm->init(th, _display_state); + _shell->init(th, _display_state, sm); // Create input task on Core 0 xTaskCreatePinnedToCore(input_task, "input_task", 4096, NULL, 1, &InputTask, 0); delay(100); - // Create test windows - - WindowDecoration dec1 = { - .x_offset = 6, - .y_offset = 5, - .width = 25, - .height = 25, - .action = WindowAction::CLOSE, - }; - - WindowDecoration dec2 = { - .x_offset = 36, - .y_offset = 5, - .width = 25, - .height = 25, - .action = WindowAction::MINIMIZE, - }; - - std::vector wdec; - wdec.push_back(dec1); - wdec.push_back(dec2); - - Window win1 = { - .id = 0, - .x = 10, - .y = 10, - .width = 350, - .height = 250, - .background_color = 0xbdf7, - .foreground_color = COL_DARK_BLUE, - .focused = false, - .minimized = false, - .title = "Hello World!", - .window_decorations = wdec, - }; - wm->create_window(win1); - - Window win2 = { - .id = 1, - .x = 70, - .y = 40, - .width = 350, - .height = 200, - .background_color = 0xbdf7, - .foreground_color = COL_DARK_BLUE, - .focused = true, - .minimized = false, - .title = "Second Window", - .window_decorations = wdec, - }; - wm->create_window(win2); - Serial.println("Initialization complete"); } void loop() { // Only handle rendering in main loop if (_display_state->update_display.load()) { - wm->draw_all(); + _shell->draw_all(); im->draw_button(); _display_state->update_display.store(false); } diff --git a/Desktop_Test/GLOBALS.h b/Desktop_Test/GLOBALS.h index 44e2451..223eb49 100644 --- a/Desktop_Test/GLOBALS.h +++ b/Desktop_Test/GLOBALS.h @@ -6,10 +6,12 @@ #define COL_WHITE 0xffff #define COL_BLACK 0x0000 -#define COL_GREY 0xbdf7 +#define COL_GREY 0xbdf7 // Standard color #define COL_LIGHT_GREY 0x8410 +#define COL_DARK_GREY 0x9cf3 #define COL_DARK_BLUE 0x0014 #define COL_RED 0xf800 +#define COL_TEAL 0x0410 enum CLICK_EVENTS { NONE = 0, diff --git a/Desktop_Test/helpers.cpp b/Desktop_Test/helpers.cpp new file mode 100644 index 0000000..485f847 --- /dev/null +++ b/Desktop_Test/helpers.cpp @@ -0,0 +1,14 @@ +#include "helpers.h" + +void Helpers::recalculate_taskbar(std::vector* task_bar_items) { + for (size_t i = 0; i < task_bar_items->size(); ++i) { + auto& item = (*task_bar_items)[i]; + + if (i == 0) { + item.place_x = 60; + } else { + const TaskBarItem& prev = (*task_bar_items)[i - 1]; + item.place_x = prev.place_x + prev.offset_x; + } + } +} \ No newline at end of file diff --git a/Desktop_Test/helpers.h b/Desktop_Test/helpers.h new file mode 100644 index 0000000..b6d2e5f --- /dev/null +++ b/Desktop_Test/helpers.h @@ -0,0 +1,8 @@ +#pragma once +#include "GLOBALS.h" +#include "program.h" +#include + +struct Helpers { + static void recalculate_taskbar(std::vector* task_bar_items); +}; \ No newline at end of file diff --git a/Desktop_Test/program.cpp b/Desktop_Test/program.cpp new file mode 100644 index 0000000..93717a9 --- /dev/null +++ b/Desktop_Test/program.cpp @@ -0,0 +1,30 @@ +#include "freertos/idf_additions.h" +#include "esp_heap_caps.h" +#include "program.h" + +Program::Program(DISPLAY_STATE* display_state) { + _display_state = display_state; +} + +int Program::init(int id, std::string name, Window* window) { + _id = id; + _task_name = name; + _window = window; + + xTaskCreatePinnedToCore(loop, _task_name.c_str(), 4096, this, 1, &_task_handle, 0); + + return _id; +} + +void Program::close() { + vTaskDelete(_task_handle); +} + +void Program::loop(void* pvParameters) { + Program* self = static_cast(pvParameters); + + for(;;) { + self->_display_state->update_display.store(true); + vTaskDelay(500); + } +} \ No newline at end of file diff --git a/Desktop_Test/program.h b/Desktop_Test/program.h new file mode 100644 index 0000000..92ddf02 --- /dev/null +++ b/Desktop_Test/program.h @@ -0,0 +1,70 @@ +#pragma once +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "GLOBALS.h" +#include +#include + +struct TaskBarItem { + int id; + int place_x; + int place_y; + int width; + int height; + int offset_x; + bool focused; + std::string name; +}; + +struct DesktopItem { + int id; + int place_x; + int place_y; + int icon_size_x; + int icon_size_y; +}; + +enum class WindowAction { + EXIT = 1, + FILE = 2, + HELP = 3, + MINIMIZE = 4, + CLOSE = 5, +}; + +struct WindowDecoration { + int x_offset; + int y_offset; + int width; + int height; + WindowAction action; +}; + +struct Window { + int id; + int x; + int y; + int width; + int height; + int background_color; + int foreground_color; + bool focused; + bool minimized; + std::string title; + std::vector window_decorations; +}; + +class Program { +private: + Window* _window; + DISPLAY_STATE* _display_state; + std::string _task_name = ""; + TaskHandle_t _task_handle; + static void loop(void* pvParameters); + +public: + int _id; + Program(DISPLAY_STATE* display_state); + int init(int id, std::string name, Window* window); + void close(); +}; \ No newline at end of file diff --git a/Desktop_Test/shell.cpp b/Desktop_Test/shell.cpp new file mode 100644 index 0000000..a802b8b --- /dev/null +++ b/Desktop_Test/shell.cpp @@ -0,0 +1,356 @@ +#include "shell.h" +#include "GLOBALS.h" +#include +#include "system_manager.h" + +void Shell::init(TFT_Handler* th, DISPLAY_STATE* ds, SystemManager* system_manager) { + tft = th; + display_state = ds; + _system_manager = system_manager; + + // Subscribe to click events + EventManager::getInstance().subscribe(this); + + draw_background(COL_TEAL); + draw_task_bar(COL_GREY); + + // Create desktop items + for (int i = 0; i < 5; i++) { + DesktopItem di = { + .id = i, + .place_x = i * 50 + 1, + .place_y = 10, + .icon_size_x = 45, + .icon_size_y = 50, + }; + items.push_back(di); + } + + for (int i = 0; i < items.size(); ++i) { + draw_desktop_Item(items[i]); + } +} + +void Shell::on_click_event(CLICK_EVENT event) { + if (event.event != CLICK_EVENTS::LEFT_CLICK) return; + + int window_id = handle_window_click(event); + if (window_id >= 0) { + display_state->update_display.store(true); + return; + } + + int icon_id = handle_desktop_click(event); + + if (icon_id != -1) { + display_state->update_display.store(true); + return; + } + + int tast_icon_id = handle_taskbar_click(event); + + if (tast_icon_id != -1) { + display_state->update_display.store(true); + return; + } + + bool ckicked_start_button = handle_start_button_click(event); + + if (ckicked_start_button) { + display_state->update_display.store(true); + return; + } +} + +// ======= Window ======= +void Shell::create_window(Window* window) { + int total_pos_x = window->x + window->width; + int total_pos_y = window->y + window->height; + + // Clamp window within the screen size AND taskbar + if (total_pos_x > MAX_SCREEN_WIDTH) window->x = MAX_SCREEN_WIDTH - window->width; + if (total_pos_y > MAX_SCREEN_HEIGHT) window->y = MAX_SCREEN_HEIGHT - window->height - 29; + + windows.push_back(window); + + TaskBarItem item; + item.id = window->id; + item.focused = window->focused; + item.name = window->title; + item.width = 95; + item.height = 18; + item.offset_x = 100; + item.place_y = 298; + + // Set place_x and place_y for ALL items + if (task_bar_items.size() == 0) { + item.place_x = 60; + } else { + // For subsequent items, use the previous item's position + offset + const TaskBarItem& prev = task_bar_items.back(); + item.place_x = prev.place_x + prev.offset_x; + } + + task_bar_items.push_back(item); + + display_state->update_display.store(true); +} + +void Shell::close_window(int window_id) { + for (auto it = windows.begin(); it != windows.end(); ++it) { + if ((*it)->id == window_id) { + windows.erase(it); + _system_manager->close_program((*it)->id); + display_state->update_display.store(true); + break; + } + } + + for (auto it = task_bar_items.begin(); it != task_bar_items.end(); ++it) { + if (it->id == window_id) { + task_bar_items.erase(it); + display_state->update_display.store(true); + break; + } + } + + for (int i = 0; i < task_bar_items.size(); ++i) { + TaskBarItem item = task_bar_items[i]; + + if (i == 0) { + item.place_x = 60; + } else { + // For subsequent items, use the previous item's position + offset + const TaskBarItem& prev = task_bar_items.back(); + item.place_x = prev.place_x + prev.offset_x; + } + + task_bar_items[i] = item; + } +} + +void Shell::minimize_window(int window_id) { + for (auto it = windows.begin(); it != windows.end(); ++it) { + for (int i = 0; i < task_bar_items.size(); ++i) { + if ((*it)->id == window_id && (*it)->id == task_bar_items[i].id) { + (*it)->minimized = true; + task_bar_items[i].focused = false; + display_state->update_display.store(true); + break; + } + } + } +} + +void Shell::draw_all() { + tft->start_draw(); + + draw_background(0x0410); + draw_task_bar(0xbdf7); + + for (const auto& item : items) { + draw_desktop_Item(item); + } + + for (const auto& win : windows) { + if (!win->minimized) draw_window(*win); + } + tft->end_draw(); +} + +void Shell::draw_window(Window window) { + if (window.minimized) return; + + // Outer shadow + tft->draw_rect(window.x, window.y, window.width + 2, window.height + 2, 1, 0x0000); + + // Inner shadow + tft->draw_rect(window.x, window.y, window.width + 1, window.height + 1, 1, 0x8410); + + // Highlight + tft->draw_rect(window.x - 1, window.y - 1, window.width + 1, window.height + 1, 1, 0xffff); + + // Window + tft->draw_box(window.x, window.y, window.width, window.height, window.background_color); + + // Decorations + tft->draw_box(window.x + 3, window.y + 3, window.width - 6, 30, window.foreground_color); + + for (auto it = window.window_decorations.begin(); it != window.window_decorations.end(); ++it) { + if (it->action == WindowAction::CLOSE) { + // Close button + tft->draw_box(window.x + it->x_offset, window.y + it->y_offset, it->height, it->width, COL_RED); + } + + if (it->action == WindowAction::MINIMIZE) { + // Minimize button + tft->draw_box(window.x + it->x_offset, window.y + it->y_offset, it->height, it->width, COL_LIGHT_GREY); + } + } + + // Window title + tft->draw_text(window.x + 6 + 65, window.y + 10, window.title.c_str()); + + // Window content + tft->draw_box(window.x + 3, window.y + 35, window.width - 6, window.height - 38, COL_WHITE); +} + +// ======= Desktop ======= +void Shell::draw_background(int color) { + tft->fill_screen(color); +} + +void Shell::draw_desktop_Item(DesktopItem desktop_item) { + tft->draw_box(desktop_item.place_x, desktop_item.place_y, desktop_item.icon_size_x, desktop_item.icon_size_y, 0x4648); +} + +void Shell::draw_task_bar(int color) { + // bar + tft->draw_box(0, 293, 480, 40, color); + tft->draw_line(0, 294, 480, 294, 0xffff); + + // Outer shadow + tft->draw_box(5, 297, 53, 21, 0x0000); + + // Inner shadow + tft->draw_box(6, 298, 51, 19, 0x8410); + + // Highlight + tft->draw_box(5, 297, 51, 19, 0xffff); + + // Button + tft->draw_box(6, 298, 50, 18, color); + + Serial.println(task_bar_items.size()); + + for (TaskBarItem item : task_bar_items) { + if (item.focused) { + tft->draw_box(item.place_x - 2, item.place_y - 2, item.width + 3, item.height + 3, COL_BLACK); + tft->draw_box(item.place_x - 1, item.place_y - 1, item.width + 2, item.height + 2, COL_DARK_GREY); + tft->draw_box(item.place_x, item.place_y, item.width + 2, item.height + 2, COL_WHITE); + tft->draw_box(item.place_x, item.place_y, item.width, item.height, COL_GREY); + } + else { + tft->draw_box(item.place_x - 1, item.place_y - 1, item.width + 1, item.height + 1, COL_WHITE); + tft->draw_box(item.place_x, item.place_y, item.width + 2, item.height + 2, COL_BLACK); + tft->draw_rect(item.place_x, item.place_y, item.width + 1, item.height + 1, 1, COL_DARK_GREY); + tft->draw_box(item.place_x, item.place_y, item.width, item.height, COL_GREY); + } + } +} + +int Shell::handle_window_click(CLICK_EVENT event) { + // Iterate BACKWARDS - last window is on top + for (int i = windows.size() - 1; i >= 0; i--) { + Window* window = windows[i]; + + // Check if click is within window bounds + if (event.x >= window->x && event.x <= (window->x + window->width) && + event.y >= window->y && event.y <= (window->y + window->height)) { + + for (auto it = window->window_decorations.begin(); it != window->window_decorations.end(); ++it) { + int x = window->x + it->x_offset; + int y = window->y + it->y_offset; + + if (event.x >= x && event.x <= (x + it->width) && event.y >= y && event.y <= (y + it->height) && it->action == WindowAction::CLOSE) { + close_window(window->id); + return window->id; + } + + if (event.x >= x && event.x <= (x + it->width) && event.y >= y && event.y <= (y + it->height) && it->action == WindowAction::MINIMIZE) { + minimize_window(window->id); + return window->id; + } + } + + if (window->focused) return -1; + + // Unfocus all windows + for (auto& win : windows) { + win->focused = false; + } + + // Focus this window + window->focused = true; + + // Move window to end (top of z-order) + auto it = windows.begin() + i; + std::rotate(it, it + 1, windows.end()); + + return window->id; + } + } + + return -1; +} + +int Shell::handle_desktop_click(CLICK_EVENT event) { + // Check if click is on desktop (not on taskbar or windows) + // This is where you'd check if an icon was clicked + for (const auto& item : items) { + if (event.x >= item.place_x && + event.x <= (item.place_x + item.icon_size_x) && + event.y >= item.place_y && + event.y <= (item.place_y + item.icon_size_y)) { + + Serial.print("Desktop item clicked: "); + Serial.println(item.id); + + WindowDecoration dec; + Window* win = new Window(); + + WindowDecoration dec1 = { + .x_offset = 6, + .y_offset = 5, + .width = 25, + .height = 25, + .action = WindowAction::CLOSE, + }; + + WindowDecoration dec2 = { + .x_offset = 36, + .y_offset = 5, + .width = 25, + .height = 25, + .action = WindowAction::MINIMIZE, + }; + + win->window_decorations.push_back(dec1); + win->window_decorations.push_back(dec2); + + win->id = 0; + win->x = 10; + win->y = 0; + win->width = 350; + win->height = 250; + win->background_color = 0xbdf7; + win->foreground_color = COL_DARK_BLUE; + win->focused = true; + win->minimized = false; + win->title = "Hello World!"; + + _system_manager->spawn_program(win); + create_window(win); + + // TODO: Handle icon click (open window, etc.) + return item.id; + } + } + + return -1; +} + +int Shell::handle_taskbar_click(CLICK_EVENT event) { + return -1; +} + +bool Shell::handle_start_button_click(CLICK_EVENT event) { + if (event.x >= 6 && event.x <= (6 + 50) && event.y >= 298 && event.y <= (298 + 18)) { + Serial.println("Taskbar button clicked"); + + // TODO: Handle icon click (open window, etc.) + return true; + } + + return false; +} \ No newline at end of file diff --git a/Desktop_Test/shell.h b/Desktop_Test/shell.h new file mode 100644 index 0000000..fbe861a --- /dev/null +++ b/Desktop_Test/shell.h @@ -0,0 +1,42 @@ +#pragma once +#include "tft_handler.h" +#include "event_manager.h" +#include "system_manager.h" +#include +#include "GLOBALS.h" +#include "program.h" + +class Shell : public IEventListener { +private: + TFT_Handler* tft; + DISPLAY_STATE* display_state; + std::vector windows; + std::vector task_bar_items; + std::vector items; + + SystemManager* _system_manager; + + int handle_window_click(CLICK_EVENT event); + int handle_desktop_click(CLICK_EVENT event); + int handle_taskbar_click(CLICK_EVENT event); + bool handle_start_button_click(CLICK_EVENT event); + +public: + void init(TFT_Handler* th, DISPLAY_STATE* ds, SystemManager* system_manager); + + // ======= Window ======= + void create_window(Window* window); + void close_window(int window_id); + void minimize_window(int window_id); + void draw_window(Window window); + + // ======= Desktop ======= + void draw_background(int color); + void draw_task_bar(int color); + void draw_desktop_Item(DesktopItem desktop_item); + + // ======= Shared ======= + void draw_all(); + // IEventListener interface + void on_click_event(CLICK_EVENT event) override; +}; \ No newline at end of file diff --git a/Desktop_Test/system_manager.cpp b/Desktop_Test/system_manager.cpp index e69de29..4c0ae84 100644 --- a/Desktop_Test/system_manager.cpp +++ b/Desktop_Test/system_manager.cpp @@ -0,0 +1,26 @@ +#include "program.h" +#include "system_manager.h" + +std::vector SystemManager::init(DISPLAY_STATE* display_state) { + _display_state = display_state; + return _programs; +} + +void SystemManager::spawn_program(Window* window) { + Program* program = new Program(_display_state); + + window->id = _programs.size() + 1; + program->init(_programs.size() + 1, "lol", window); + _programs.push_back(program); +} + +void SystemManager::close_program(int id) { + for (size_t i = 0; i < _programs.size(); ++i) { + if (_programs[i]->_id == id) { + _programs[i]->close(); + delete _programs[i]; // Free the memory + _programs.erase(_programs.begin() + i); + break; // Exit after erase + } + } +} \ No newline at end of file diff --git a/Desktop_Test/system_manager.h b/Desktop_Test/system_manager.h index 7ac7f8b..2abecb6 100644 --- a/Desktop_Test/system_manager.h +++ b/Desktop_Test/system_manager.h @@ -1,9 +1,14 @@ #pragma once +#include "program.h" +#include class SystemManager { private: - + DISPLAY_STATE* _display_state; public: - void init(); + std::vector _programs; + std::vector init(DISPLAY_STATE* display_state); + void spawn_program(Window* window); + void close_program(int id); }; \ No newline at end of file