From e6c3a80b01ffb52079783cddd9be6d392d0f7039 Mon Sep 17 00:00:00 2001 From: Benji Dial Date: Mon, 29 Jul 2024 19:59:52 -0400 Subject: redesign compositor protocol, start widget library --- libraries/pake/include/pake/dirtiable-image.hpp | 27 ++++++++ libraries/pake/include/pake/widget.hpp | 19 +++++ libraries/pake/include/pake/widgets/fixed-text.hpp | 31 +++++++++ libraries/pake/include/pake/window.hpp | 32 +++++++++ libraries/pake/makefile | 12 ++++ libraries/pake/source/dirtiable-image.cpp | 80 ++++++++++++++++++++++ libraries/pake/source/widgets/fixed-text.cpp | 41 +++++++++++ libraries/pake/source/window.cpp | 78 +++++++++++++++++++++ 8 files changed, 320 insertions(+) create mode 100644 libraries/pake/include/pake/dirtiable-image.hpp create mode 100644 libraries/pake/include/pake/widget.hpp create mode 100644 libraries/pake/include/pake/widgets/fixed-text.hpp create mode 100644 libraries/pake/include/pake/window.hpp create mode 100644 libraries/pake/makefile create mode 100644 libraries/pake/source/dirtiable-image.cpp create mode 100644 libraries/pake/source/widgets/fixed-text.cpp create mode 100644 libraries/pake/source/window.cpp (limited to 'libraries/pake') diff --git a/libraries/pake/include/pake/dirtiable-image.hpp b/libraries/pake/include/pake/dirtiable-image.hpp new file mode 100644 index 0000000..af09ade --- /dev/null +++ b/libraries/pake/include/pake/dirtiable-image.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +namespace pake { + + struct region { + int start_x; + int start_y; + int width; + int height; + }; + + struct dirtiable_image { + + daguerre::image image; + daguerre::image dirty; + + std::vector get_dirty_regions(); + void clear_dirty(); + + dirtiable_image(int width, int height); + + }; + +} diff --git a/libraries/pake/include/pake/widget.hpp b/libraries/pake/include/pake/widget.hpp new file mode 100644 index 0000000..dad5651 --- /dev/null +++ b/libraries/pake/include/pake/widget.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include + +namespace pake { + + class widget { + + public: + virtual ~widget() {} + + virtual void render( + dirtiable_image &onto, int x_off, int y_off, bool force) = 0; + + virtual void notify_size(int width, int height) = 0; + + }; + +} diff --git a/libraries/pake/include/pake/widgets/fixed-text.hpp b/libraries/pake/include/pake/widgets/fixed-text.hpp new file mode 100644 index 0000000..c6dafab --- /dev/null +++ b/libraries/pake/include/pake/widgets/fixed-text.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +namespace pake::widgets { + + class fixed_text : public widget { + + const daguerre::fixed_font *font; + daguerre::hilbert_color bg, fg; + std::string text; + + int width, height; + + public: + //TODO: look up font in some kind of catalogue + fixed_text( + std::string &&text, + const daguerre::fixed_font *font, + daguerre::hilbert_color bg, + daguerre::hilbert_color fg); + + virtual void render( + dirtiable_image &onto, int x_off, int y_off, bool force) override; + + virtual void notify_size(int width, int height) override; + + }; + +} diff --git a/libraries/pake/include/pake/window.hpp b/libraries/pake/include/pake/window.hpp new file mode 100644 index 0000000..bb63b9d --- /dev/null +++ b/libraries/pake/include/pake/window.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include + +namespace pake { + + class window { + + euler::syscall::stream_handle socket; + + int width; + int height; + + dirtiable_image contents; + std::unique_ptr root; + + public: + window(int width, int height, const std::string &title); + ~window(); + + void show(); + void hide(); + + void set_root(std::unique_ptr &&w); + + void render_and_send_to_compositor(); + + }; + +} diff --git a/libraries/pake/makefile b/libraries/pake/makefile new file mode 100644 index 0000000..28e7d37 --- /dev/null +++ b/libraries/pake/makefile @@ -0,0 +1,12 @@ +SOURCES = \ + widgets/fixed-text.cpp dirtiable-image.cpp window.cpp + +build/%.cpp.o: source/%.cpp + @mkdir -p $(@D) + $(HILBERT_CC) -c $^ -o $@ + +build/libpake.a: $(SOURCES:%=build/%.o) + $(HILBERT_AR) rcs $@ $^ + +clean: + rm -rf build diff --git a/libraries/pake/source/dirtiable-image.cpp b/libraries/pake/source/dirtiable-image.cpp new file mode 100644 index 0000000..9e5c979 --- /dev/null +++ b/libraries/pake/source/dirtiable-image.cpp @@ -0,0 +1,80 @@ +#include + +namespace pake { + + struct dirty_region_builder { + + std::vector regions_not_on_bottom; + std::list regions_on_bottom; + + void add_row(const std::vector &row) { + + std::list new_regions_on_bottom; + + for (auto i = row.begin(); i < row.end(); ++i) { + bool expanded = false; + for (auto j = regions_on_bottom.begin(); j != regions_on_bottom.end(); ++j) + if (i->start_x == j->start_x && i->width == j->width) { + j->height += i->height; + new_regions_on_bottom.push_back(*j); + regions_on_bottom.erase(j); + expanded = true; + break; + } + if (!expanded) + new_regions_on_bottom.push_back(*i); + } + + for (auto i = regions_on_bottom.begin(); i != regions_on_bottom.end(); ++i) + regions_not_on_bottom.push_back(*i); + + regions_on_bottom = std::move(new_regions_on_bottom); + + } + + }; + + std::vector dirtiable_image::get_dirty_regions() { + + dirty_region_builder builder; + + std::vector row; + + for (int y = 0; y < dirty.height; ++y) { + + int r = 0; + for (int x = 0; x < dirty.width; ++x) + if (!dirty.at(x, y)) { + if (r != x) + row.push_back({ + .start_x = r, .start_y = y, + .width = x - r, .height = 1 + }); + r = x + 1; + } + if (r != dirty.width) + row.push_back({ + .start_x = r, .start_y = y, + .width = dirty.width - r, .height = 1 + }); + + builder.add_row(row); + row.clear(); + + } + + builder.add_row(row); + return builder.regions_not_on_bottom; + + } + + void dirtiable_image::clear_dirty() { + dirty.fill(false); + } + + dirtiable_image::dirtiable_image(int width, int height) + : image(width, height), dirty(width, height) { + dirty.fill(false); + } + +} diff --git a/libraries/pake/source/widgets/fixed-text.cpp b/libraries/pake/source/widgets/fixed-text.cpp new file mode 100644 index 0000000..9ae55dc --- /dev/null +++ b/libraries/pake/source/widgets/fixed-text.cpp @@ -0,0 +1,41 @@ +#include + +static void draw_if_true( + daguerre::hilbert_color &out, const bool &in, + const daguerre::hilbert_color ¶m) { + if (in) out = param; +} + +namespace pake::widgets { + + fixed_text::fixed_text( + std::string &&text, + const daguerre::fixed_font *font, + daguerre::hilbert_color bg, + daguerre::hilbert_color fg) + : font(font), bg(bg), fg(fg), text(std::move(text)) {} + + void fixed_text::render( + dirtiable_image &onto, int x_off, int y_off, bool force) { + + if (force) { + onto.image.fill( bg, x_off, y_off, width, height); + onto.dirty.fill(true, x_off, y_off, width, height); + //TODO: have options for alignment + //TODO: check overflow + onto.image.render_text( + *font, fg, x_off, y_off, + text.data(), draw_if_true); + onto.dirty.fill( + true, x_off, y_off, + font->glyph_width * text.size(), + font->glyph_height); + } + + } + + void fixed_text::notify_size(int width, int height) { + this->width = width; this->height = height; + } + +} diff --git a/libraries/pake/source/window.cpp b/libraries/pake/source/window.cpp new file mode 100644 index 0000000..c1c773f --- /dev/null +++ b/libraries/pake/source/window.cpp @@ -0,0 +1,78 @@ +#include +#include + +//TODO: handle errors on socket connection, read, and write + +namespace pake { + + window::window(int width, int height, const std::string &title) + : width(width), height(height), contents(width, height), root() { + + euler::syscall::connect_to_socket("hilbert.compositor", socket); + + assert(width >= 0 && height >= 0); + + struct [[gnu::packed]] { uint8_t type; uint32_t width; uint32_t height; } + dimensions_pkt = {.type = 0x02, .width = (uint32_t)width, .height = (uint32_t)height }; + euler::syscall::write_to_stream(socket, sizeof(dimensions_pkt), &dimensions_pkt); + + assert(title.size() <= UINT32_MAX); + + struct [[gnu::packed]] { uint8_t type; uint32_t length; } + title_pkt = {.type = 0x03, .length = (uint32_t)title.size() }; + euler::syscall::write_to_stream(socket, sizeof(title_pkt), &title_pkt); + euler::syscall::write_to_stream(socket, title.size(), title.data()); + + } + + window::~window() { + euler::syscall::close_stream(socket); + } + + void window::show() { + uint8_t packet = 0; + euler::syscall::write_to_stream(socket, 1, &packet); + } + + void window::hide() { + uint8_t packet = 1; + euler::syscall::write_to_stream(socket, 1, &packet); + } + + void window::set_root(std::unique_ptr &&w) { + root = std::move(w); + root->notify_size(width, height); + root->render(contents, 0, 0, true); + } + + void window::render_and_send_to_compositor() { + + root->render(contents, 0, 0, false); + auto dirties = contents.get_dirty_regions(); + + for (auto it = dirties.cbegin(); it != dirties.cend(); ++it) { + + struct [[gnu::packed]] { + uint8_t type; + uint32_t start_x; uint32_t start_y; + uint32_t width; uint32_t height; + } update_pkt = { + .type = 0x04, + .start_x = (uint32_t)it->start_x, .start_y = (uint32_t)it->start_y, + . width = (uint32_t)it-> width, . height = (uint32_t)it-> height + }; + + euler::syscall::write_to_stream(socket, sizeof(update_pkt), &update_pkt); + + for (int y = it->start_y; y < it->start_y + it->height; ++y) + euler::syscall::write_to_stream(socket, + it->width * sizeof(daguerre::hilbert_color), + &contents.image.at(it->start_x, y)); + + } + + contents.clear_dirty(); + + } + +} -- cgit v1.2.3