Vibed the cleanup a bit

This commit is contained in:
Rasmus Rasmussen 2025-12-15 19:06:44 +01:00
parent 6bd3102593
commit ead8dfe39f
12 changed files with 294 additions and 163 deletions

View File

@ -2,68 +2,76 @@
#include "desktop_hander.h" #include "desktop_hander.h"
#include "input_manager.h" #include "input_manager.h"
#include "tft_handler.h" #include "tft_handler.h"
#include "event_manager.h"
#include "GLOBALS.h" #include "GLOBALS.h"
WindowManager* wm; // Globals WindowManager* wm;
Desktop_Hander* dh; Desktop_Hander* dh;
InputManager* im; InputManager* im;
TFT_Handler* th; TFT_Handler* th;
DISPLAY_STATE* _display_state; DISPLAY_STATE* _display_state;
CLICK_EVENT* _click_event;
uint32_t totalHeap = ESP.getHeapSize(); TaskHandle_t InputTask;
uint32_t freeHeap = ESP.getFreeHeap();
uint32_t usedHeap = totalHeap - freeHeap;
float percentUsed = (float)usedHeap * 100.0 / totalHeap;
TaskHandle_t Task1; void input_task(void* pvParameters) {
for(;;) {
im->update();
vTaskDelay(1);
}
}
void setup() { void setup() {
Serial.begin(115200); Serial.begin(115200);
Serial.println("Initializing..."); Serial.println("Initializing...");
// Create shared state
_display_state = new DISPLAY_STATE(); _display_state = new DISPLAY_STATE();
_display_state->update_display.store(false); _display_state->update_display.store(false);
_click_event = new CLICK_EVENT(); // Create managers
_click_event->event = CLICK_EVENTS::NONE;
wm = new WindowManager(); wm = new WindowManager();
dh = new Desktop_Hander(); dh = new Desktop_Hander();
im = new InputManager(); im = new InputManager();
th = new TFT_Handler(); th = new TFT_Handler();
// Initialize in order
th->init(_display_state); th->init(_display_state);
im->init(_display_state, th, _click_event);
dh->init(th);
wm->init(th);
delay(500); // Initialize event manager (creates dispatcher task)
// Create InputManager thread EventManager::getInstance().init();
// Initialize components (they subscribe to events)
im->init(_display_state, th);
dh->init(th, _display_state);
wm->init(th, _display_state);
// Create input task on Core 0
xTaskCreatePinnedToCore( xTaskCreatePinnedToCore(
task1, /* Task function. */ input_task,
"task1", /* name of task. */ "input_task",
10000, /* Stack size of task */ 4096,
NULL, /* parameter of the task */ NULL,
1, /* priority of the task */ 1,
&Task1, /* Task handle to keep track of created task */ &InputTask,
0); /* pin task to core 0 */ 0
delay(500); );
delay(100);
// Create test windows
Window win1 = { Window win1 = {
.id = 0, .id = 0,
.x = 10, .x = 10,
.y = 30, .y = 30,
.width = 350, .width = 350,
.height = 250, .height = 250,
.background_color = 0xbdf7, // Blue .background_color = 0xbdf7,
.foreground_color = COL_DARK_BLUE, // White .foreground_color = COL_DARK_BLUE,
.focused = false, .focused = false,
.title = "Hello Worl!", .title = "Hello World!",
.window_actions = {}, .window_actions = {},
}; };
wm->create_window(win1); wm->create_window(win1);
Window win2 = { Window win2 = {
@ -72,30 +80,19 @@ void setup() {
.y = 50, .y = 50,
.width = 350, .width = 350,
.height = 250, .height = 250,
.background_color = 0xbdf7, // Blue .background_color = 0xbdf7,
.foreground_color = COL_DARK_BLUE, // White .foreground_color = COL_DARK_BLUE,
.focused = true, .focused = true,
.title = "lmao", .title = "Second Window",
.window_actions = {}, .window_actions = {},
}; };
wm->create_window(win2); wm->create_window(win2);
Serial.println("Initialization complete");
} }
void loop() { void loop() {
totalHeap = ESP.getHeapSize(); // Only handle rendering in main loop
freeHeap = ESP.getFreeHeap();
usedHeap = totalHeap - freeHeap;
percentUsed = (float)usedHeap * 100.0 / totalHeap;
int start = millis();
int id = wm->click_event(*_click_event);
if (id >= 0)
_display_state->update_display.store(true);
_click_event->event = CLICK_EVENTS::NONE;
if (_display_state->update_display.load()) { if (_display_state->update_display.load()) {
dh->re_draw_desktop(); dh->re_draw_desktop();
wm->draw_all(); wm->draw_all();
@ -103,19 +100,5 @@ void loop() {
_display_state->update_display.store(false); _display_state->update_display.store(false);
} }
int stop = millis();
//Serial.print("Total mem: "); Serial.println(ESP.getHeapSize());
//Serial.print("Mem left: "); Serial.println(ESP.getFreeHeap());
//Serial.print("Mem % used: "); Serial.println(percentUsed);
//Serial.println(stop - start);
delay(50); delay(50);
} }
void task1(void * pvParameters) {
for(;;) {
im->update();
vTaskDelay(1);
}
}

View File

@ -7,6 +7,11 @@ enum CLICK_EVENTS {
RIGHT_CLICK = 2, RIGHT_CLICK = 2,
}; };
enum REDRAW_EVENT {
DESKTOP = 0,
WINDOWS = 1,
};
struct CLICK_EVENT { struct CLICK_EVENT {
int x; int x;
int y; int y;

View File

@ -1,10 +1,15 @@
#include "desktop_hander.h" #include "desktop_hander.h"
#include "tft_handler.h" #include "tft_handler.h"
void Desktop_Hander::init(TFT_Handler* th) { void Desktop_Hander::init(TFT_Handler* th, DISPLAY_STATE* ds) {
tft = th; tft = th;
display_state = ds;
for (int i; i < 5; i++) { // Subscribe to events
EventManager::getInstance().subscribe(this);
// Create desktop items
for (int i = 0; i < 5; i++) {
Desktop_Item di = { Desktop_Item di = {
.id = i, .id = i,
.place_x = i * 50 + 1, .place_x = i * 50 + 1,
@ -34,7 +39,6 @@ void Desktop_Hander::draw_background(int color) {
void Desktop_Hander::draw_task_bar(int color) { void Desktop_Hander::draw_task_bar(int color) {
// bar // bar
tft->draw_box(0, 293, 480, 40, color); tft->draw_box(0, 293, 480, 40, color);
tft->draw_line(0, 294, 480, 294, 0xffff); tft->draw_line(0, 294, 480, 294, 0xffff);
// Outer shadow // Outer shadow
@ -50,10 +54,33 @@ void Desktop_Hander::draw_task_bar(int color) {
tft->draw_box(6, 298, 50, 18, color); tft->draw_box(6, 298, 50, 18, color);
} }
void Desktop_Hander::draw_desktop_Item(Desktop_Item desktop_item) { void Desktop_Hander::draw_desktop_Item(const Desktop_Item& desktop_item) {
tft->draw_box(desktop_item.place_x, desktop_item.place_y, desktop_item.icon_size_x, desktop_item.icon_size_y, 0x4648); tft->draw_box(desktop_item.place_x, desktop_item.place_y,
desktop_item.icon_size_x, desktop_item.icon_size_y, 0x4648);
} }
int Desktop_Hander::click_event(CLICK_EVENT event) { void Desktop_Hander::on_click_event(const CLICK_EVENT& event) {
return 0; if (event.event != CLICK_EVENTS::LEFT_CLICK) {
return;
}
handle_desktop_click(event);
}
void Desktop_Hander::handle_desktop_click(const 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);
// TODO: Handle icon click (open window, etc.)
break;
}
}
} }

View File

@ -1,19 +1,25 @@
#pragma once #pragma once
#include "desktop.h" #include "desktop.h"
#include "tft_handler.h" #include "tft_handler.h"
#include "event_manager.h"
#include <vector> #include <vector>
#include "GLOBALS.h" #include "GLOBALS.h"
class Desktop_Hander { class Desktop_Hander : public IEventListener {
private: private:
TFT_Handler* tft; TFT_Handler* tft;
DISPLAY_STATE* display_state;
std::vector<Desktop_Item> items; std::vector<Desktop_Item> items;
void handle_desktop_click(const CLICK_EVENT& event);
public: public:
void init(TFT_Handler* th); void init(TFT_Handler* th, DISPLAY_STATE* ds);
void re_draw_desktop(); void re_draw_desktop();
void draw_background(int color); void draw_background(int color);
void draw_task_bar(int color); void draw_task_bar(int color);
void draw_desktop_Item(Desktop_Item desktop_item); void draw_desktop_Item(const Desktop_Item& desktop_item);
int click_event(CLICK_EVENT event);
// IEventListener interface
void on_click_event(const CLICK_EVENT& event) override;
}; };

View File

@ -0,0 +1,43 @@
#include "event_manager.h"
void EventManager::init() {
xTaskCreatePinnedToCore(
dispatch_task,
"event_dispatch",
4096,
this,
2, // Higher priority than input
&_dispatcher_task,
0
);
}
void EventManager::publish(const CLICK_EVENT& event) {
xQueueSend(_event_queue, &event, 0);
}
void EventManager::subscribe(IEventListener* listener) {
_listeners.push_back(listener);
}
void EventManager::unsubscribe(IEventListener* listener) {
auto it = std::find(_listeners.begin(), _listeners.end(), listener);
if (it != _listeners.end()) {
_listeners.erase(it);
}
}
void EventManager::dispatch_task(void* pvParameters) {
EventManager* self = static_cast<EventManager*>(pvParameters);
CLICK_EVENT event;
for (;;) {
if (xQueueReceive(self->_event_queue, &event, portMAX_DELAY)) {
// Dispatch to all listeners
for (auto* listener : self->_listeners) {
listener->on_click_event(event);
}
}
vTaskDelay(1);
}
}

View File

@ -0,0 +1,43 @@
#pragma once
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "GLOBALS.h"
#include <functional>
#include <algorithm>
#include <vector>
// Forward declare to avoid circular dependency
class IEventListener {
public:
virtual void on_click_event(const CLICK_EVENT& event) = 0;
virtual ~IEventListener() = default;
};
class EventManager {
private:
QueueHandle_t _event_queue;
std::vector<IEventListener*> _listeners;
TaskHandle_t _dispatcher_task;
EventManager() {
_event_queue = xQueueCreate(10, sizeof(CLICK_EVENT));
}
EventManager(const EventManager&) = delete;
EventManager& operator=(const EventManager&) = delete;
static void dispatch_task(void* pvParameters);
public:
static EventManager& getInstance() {
static EventManager instance;
return instance;
}
void init();
void publish(const CLICK_EVENT& event);
void subscribe(IEventListener* listener);
void unsubscribe(IEventListener* listener);
};

View File

@ -1,11 +1,11 @@
#include "event_manager.h"
#include "GLOBALS.h" #include "GLOBALS.h"
#include "tft_handler.h" #include "tft_handler.h"
#include "HardwareSerial.h" #include "HardwareSerial.h"
#include "input_manager.h" #include "input_manager.h"
void InputManager::init(DISPLAY_STATE* display_state, TFT_Handler* tf, CLICK_EVENT* event) { void InputManager::init(DISPLAY_STATE* display_state, TFT_Handler* tf) {
_display_state = display_state; _display_state = display_state;
_event = event;
_tf = tf; _tf = tf;
mi = { mi = {
@ -25,19 +25,15 @@ void InputManager::update() {
for (int i = 0; i < NUM_BUTTONS; i++) { for (int i = 0; i < NUM_BUTTONS; i++) {
int reading = digitalRead(BUTTON_PINS[i]); int reading = digitalRead(BUTTON_PINS[i]);
// Check if button state changed
if (reading != lastButtonState[i]) { if (reading != lastButtonState[i]) {
lastDebounceTime[i] = millis(); lastDebounceTime[i] = millis();
} }
// Only register change after debounce delay
if ((millis() - lastDebounceTime[i]) > DEBOUNCE_DELAY) { if ((millis() - lastDebounceTime[i]) > DEBOUNCE_DELAY) {
if (reading != buttonState[i]) { if (reading != buttonState[i]) {
buttonState[i] = reading; buttonState[i] = reading;
// Button was pressed (went from HIGH to LOW)
if (buttonState[i] == LOW) { if (buttonState[i] == LOW) {
// Add your button handling code here
handle_button_press(i); handle_button_press(i);
} }
} }
@ -48,42 +44,55 @@ void InputManager::update() {
} }
void InputManager::handle_button_press(int buttonIndex) { void InputManager::handle_button_press(int buttonIndex) {
// Add your custom logic here for each button bool needs_redraw = false;
switch(buttonIndex) { switch(buttonIndex) {
case 0: case 0: // Right
if (mi.x > 480) mi.x = (mi.x + 5 > 476) ? 476 : mi.x + 5;
mi.x = 479; needs_redraw = true;
else
mi.x += 5;
break; break;
case 1:
if (mi.y > 320) case 1: // Down
mi.y = 319; mi.y = (mi.y + 5 > 316) ? 316 : mi.y + 5;
else needs_redraw = true;
mi.y += 5;
break; break;
case 2:
if (mi.y < 0) case 2: // Up
mi.y = 1; mi.y = (mi.y - 5 < 0) ? 0 : mi.y - 5;
else needs_redraw = true;
mi.y -= 5;
break; break;
case 3:
if (mi.x < 0) case 3: // Left
mi.x = 1; mi.x = (mi.x - 5 < 0) ? 0 : mi.x - 5;
else needs_redraw = true;
mi.x -= 5;
break; break;
case 4:
_event->event = CLICK_EVENTS::LEFT_CLICK; case 4: // Click
_event->x = mi.x; {
_event->y = mi.y; CLICK_EVENT event = {
.x = mi.x,
.y = mi.y,
.event = CLICK_EVENTS::LEFT_CLICK
};
EventManager::getInstance().publish(event);
}
break; break;
case 5:
case 5: // Right click
{
CLICK_EVENT event = {
.x = mi.x,
.y = mi.y,
.event = CLICK_EVENTS::RIGHT_CLICK
};
EventManager::getInstance().publish(event);
}
break; break;
} }
if (needs_redraw) {
_display_state->update_display.store(true); _display_state->update_display.store(true);
}
} }
void InputManager::draw_button() { void InputManager::draw_button() {

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "tft_handler.h" #include "tft_handler.h"
#include "event_manager.h"
#include "GLOBALS.h" #include "GLOBALS.h"
#include "icons.h" #include "icons.h"
#include <Arduino.h> #include <Arduino.h>
@ -14,7 +15,6 @@ private:
int buttonState[NUM_BUTTONS] = {HIGH, HIGH, HIGH, HIGH, HIGH, HIGH}; int buttonState[NUM_BUTTONS] = {HIGH, HIGH, HIGH, HIGH, HIGH, HIGH};
DISPLAY_STATE* _display_state; DISPLAY_STATE* _display_state;
CLICK_EVENT* _event;
TFT_Handler* _tf; TFT_Handler* _tf;
Mouse_Icon mi; Mouse_Icon mi;
@ -22,7 +22,7 @@ private:
bool are_buttons_pressed(int btn1, int btn2); bool are_buttons_pressed(int btn1, int btn2);
public: public:
void init(DISPLAY_STATE* display_state, TFT_Handler* tf, CLICK_EVENT* event); void init(DISPLAY_STATE* display_state, TFT_Handler* tf);
void update(); void update();
void draw_button(); void draw_button();
}; };

View File

@ -8,22 +8,22 @@ void TFT_Handler::init(DISPLAY_STATE* display_state) {
tft.fillScreen(0x0000); tft.fillScreen(0x0000);
} }
void TFT_Handler::draw_box(int x, int y, int size_x, int size_y, int color) { void TFT_Handler::start_draw() {
tft.startWrite(); tft.startWrite();
}
tft.setRotation(3); void TFT_Handler::end_draw() {
tft.fillRect(x, y, size_x, size_y, color);
tft.endWrite(); tft.endWrite();
} }
void TFT_Handler::draw_rect(int x, int y, int size_x, int size_y, int thickness, int color) { void TFT_Handler::draw_box(int x, int y, int size_x, int size_y, int color) {
tft.startWrite(); tft.setRotation(3);
tft.fillRect(x, y, size_x, size_y, color);
}
void TFT_Handler::draw_rect(int x, int y, int size_x, int size_y, int thickness, int color) {
tft.setRotation(3); tft.setRotation(3);
tft.drawRect(x, y, size_x, size_y, color); tft.drawRect(x, y, size_x, size_y, color);
tft.endWrite();
} }
void TFT_Handler::draw_line(int x1, int y1, int x2, int y2, int color) { void TFT_Handler::draw_line(int x1, int y1, int x2, int y2, int color) {
@ -31,19 +31,15 @@ void TFT_Handler::draw_line(int x1, int y1, int x2, int y2, int color) {
} }
void TFT_Handler::draw_text(int x, int y, std::string str) { void TFT_Handler::draw_text(int x, int y, std::string str) {
tft.startWrite();
tft.setTextColor(TFT_WHITE); tft.setTextColor(TFT_WHITE);
tft.setTextSize(2); tft.setTextSize(2);
tft.drawString(str.c_str(), x, y); tft.drawString(str.c_str(), x, y);
tft.endWrite();
} }
void TFT_Handler::fill_screen(int color) { void TFT_Handler::fill_screen(int color) {
tft.startWrite();
tft.fillScreen(color); tft.fillScreen(color);
}
tft.endWrite();
void TFT_Handler::draw_box_sprite(int x, int y, int size_x, int size_y, int color) {
} }

View File

@ -55,9 +55,15 @@ public:
DISPLAY_STATE* _display_state; DISPLAY_STATE* _display_state;
void init(DISPLAY_STATE* display_state); void init(DISPLAY_STATE* display_state);
void start_draw();
void end_draw();
void draw_box(int x, int y, int size_x, int size_y, int color); void draw_box(int x, int y, int size_x, int size_y, int color);
void draw_rect(int x, int y, int size_x, int size_y, int thickness, int color); void draw_rect(int x, int y, int size_x, int size_y, int thickness, int color);
void draw_line(int x1, int y1, int x2, int y2, int color); void draw_line(int x1, int y1, int x2, int y2, int color);
void draw_text(int x, int y, std::string str); void draw_text(int x, int y, std::string str);
void fill_screen(int color); void fill_screen(int color);
void draw_box_sprite(int x, int y, int size_x, int size_y, int color);
}; };

View File

@ -1,36 +1,39 @@
#include "HardwareSerial.h"
#include "GLOBALS.h"
#include "window_manager.h" #include "window_manager.h"
#include <algorithm> #include <algorithm>
void WindowManager::init(TFT_Handler* th) { void WindowManager::init(TFT_Handler* th, DISPLAY_STATE* ds) {
tft = th; tft = th;
display_state = ds;
// Subscribe to events
EventManager::getInstance().subscribe(this);
} }
void WindowManager::create_window(Window window) { void WindowManager::create_window(Window window) {
windows.push_back(window); windows.push_back(window);
draw_window(window); display_state->update_display.store(true);
} }
void WindowManager::close_window(int window_id) { void WindowManager::close_window(int window_id) {
for (auto it = windows.begin(); it != windows.end(); ++it) { for (auto it = windows.begin(); it != windows.end(); ++it) {
if (it->id == window_id) { if (it->id == window_id) {
windows.erase(it); windows.erase(it);
display_state->update_display.store(true);
break; break;
} }
} }
draw_all();
} }
void WindowManager::draw_all() { void WindowManager::draw_all() {
tft->start_draw();
for (const auto& win : windows) { for (const auto& win : windows) {
draw_window(win); draw_window(win);
} }
tft->end_draw();
} }
void WindowManager::draw_window(Window window) { void WindowManager::draw_window(const Window& window) {
// Outer shadow // Outer shadow
//tft->draw_box(window.x, window.y, window.width + 2, window.height + 2, 0x0000);
tft->draw_rect(window.x, window.y, window.width + 2, window.height + 2, 1, 0x0000); tft->draw_rect(window.x, window.y, window.width + 2, window.height + 2, 1, 0x0000);
// Inner shadow // Inner shadow
@ -55,45 +58,49 @@ void WindowManager::draw_window(Window window) {
tft->draw_text(window.x + 6 + 65, window.y + 10, window.title.c_str()); tft->draw_text(window.x + 6 + 65, window.y + 10, window.title.c_str());
// Window content // Window content
tft->draw_box(window.x + 3, window.y + 30 + 5, window.width - 6, window.height - 7 - 30, COL_WHITE); tft->draw_box(window.x + 3, window.y + 35, window.width - 6, window.height - 38, COL_WHITE);
} }
int WindowManager::click_event(CLICK_EVENT event) { void WindowManager::on_click_event(const CLICK_EVENT& event) {
int id = -1; if (event.event != CLICK_EVENTS::LEFT_CLICK) {
return;
if (event.event == CLICK_EVENTS::NONE) {
return -1;
} }
int clicked_id = handle_window_click(event);
if (clicked_id >= 0) {
display_state->update_display.store(true);
}
}
int WindowManager::handle_window_click(const CLICK_EVENT& event) {
// Iterate BACKWARDS - last window is on top // Iterate BACKWARDS - last window is on top
for (int i = windows.size() - 1; i >= 0; i--) { for (int i = windows.size() - 1; i >= 0; i--) {
Window& window = windows[i]; Window& window = windows[i];
if (event.event != CLICK_EVENTS::LEFT_CLICK) // Check if click is within window bounds
continue; if (event.x >= window.x && event.x <= (window.x + window.width) &&
event.y >= window.y && event.y <= (window.y + window.height)) {
if (event.x >= window.x && event.x <= (window.x + window.width)) { if (window.focused) {
return -1;
if (event.y >= window.y && event.y <= (window.y + window.height)) { }
if (window.focused)
break;
// Unfocus all windows // Unfocus all windows
for (size_t j = 0; j < windows.size(); j++) { for (auto& win : windows) {
windows[j].focused = false; win.focused = false;
} }
// Focus this window // Focus this window
window.focused = true; window.focused = true;
id = window.id;
// Move window to end (top of z-order) // Move window to end (top of z-order)
auto it = windows.begin() + i; auto it = windows.begin() + i;
std::rotate(it, it + 1, windows.end()); std::rotate(it, it + 1, windows.end());
}
return window.id;
} }
} }
return id; return -1;
} }

View File

@ -2,6 +2,7 @@
#include "window.h" #include "window.h"
#include "tft_handler.h" #include "tft_handler.h"
#include "event_manager.h"
#include <vector> #include <vector>
#include "GLOBALS.h" #include "GLOBALS.h"
@ -12,16 +13,21 @@
#define COL_DARK_BLUE 0x0014 #define COL_DARK_BLUE 0x0014
#define COL_RED 0xf800 #define COL_RED 0xf800
class WindowManager { class WindowManager : public IEventListener {
private: private:
TFT_Handler* tft; // Pointer to shared TFT handler TFT_Handler* tft;
std::vector<Window> windows; // Track all windows DISPLAY_STATE* display_state;
std::vector<Window> windows;
int handle_window_click(const CLICK_EVENT& event);
public: public:
void init(TFT_Handler* th); void init(TFT_Handler* th, DISPLAY_STATE* ds);
void create_window(Window window); void create_window(Window window);
void close_window(int window_id); void close_window(int window_id);
void draw_all(); // Redraw all windows void draw_all();
void draw_window(Window window); void draw_window(const Window& window);
int click_event(CLICK_EVENT event);
// IEventListener interface
void on_click_event(const CLICK_EVENT& event) override;
}; };