From 8859fc3d6af24b991708668b0f5e32550d796f97 Mon Sep 17 00:00:00 2001 From: apio Date: Thu, 3 Aug 2023 17:38:49 +0200 Subject: [PATCH] libui+wind: (Draggable) windows --- base/etc/init/99-login | 2 ++ libui/include/ui/Canvas.h | 30 ++++++++++++++++++++++++++++-- libui/include/ui/Color.h | 31 ++++++++++++++++++++++++++++++- libui/include/ui/Rect.h | 23 ++++++++++++++++++++++- libui/src/Canvas.cpp | 20 +++++++++++--------- libui/src/Rect.cpp | 16 +++++++++++----- wind/CMakeLists.txt | 2 ++ wind/Mouse.cpp | 36 ++++++++++++++++++++++++++++++++++-- wind/Mouse.h | 6 +++++- wind/Window.cpp | 20 ++++++++++++++++++++ wind/Window.h | 19 +++++++++++++++++++ wind/main.cpp | 16 +++++++++++----- 12 files changed, 195 insertions(+), 26 deletions(-) create mode 100644 wind/Window.cpp create mode 100644 wind/Window.h diff --git a/base/etc/init/99-login b/base/etc/init/99-login index 29cdbcaa..bb7e7ac9 100644 --- a/base/etc/init/99-login +++ b/base/etc/init/99-login @@ -1,4 +1,6 @@ Name=login Description=Start the display server. Command=/usr/bin/wind --user=selene +StandardOutput=/dev/uart0 +StandardError=/dev/uart0 Restart=true diff --git a/libui/include/ui/Canvas.h b/libui/include/ui/Canvas.h index c90f0ae7..02a662ba 100644 --- a/libui/include/ui/Canvas.h +++ b/libui/include/ui/Canvas.h @@ -17,14 +17,40 @@ namespace ui int stride; u8* ptr; + /** + * @brief Create a new Canvas object. + * + * @param ptr The memory to use for the canvas. It must be of at least width * height * 4 bytes of length. + * @param width The width of the canvas. + * @param height The height of the canvas. + * @return Canvas The new Canvas object. + */ static Canvas create(u8* ptr, int width, int height); - Result subcanvas(Point begin, int width, int height, bool clamp); + /** + * @brief Return a new Canvas that represents a subsection of the current one. + * + * @param rect The dimensions of the new canvas. If these exceed the bounds of the current canvas, they will be + * clamped. + * @return Canvas The new Canvas object. + */ + Canvas subcanvas(Rect rect); + + /** + * @brief Return the dimensions of the current canvas. + * + * @return Rect This canvas's dimensions, as a Rect object. + */ Rect rect() { - return Rect { .begin = { 0, 0 }, .width = width, .height = height }; + return Rect { .pos = { 0, 0 }, .width = width, .height = height }; } + /** + * @brief Fill the entire canvas with one color. + * + * @param color The color to use. + */ void fill(Color color); }; }; diff --git a/libui/include/ui/Color.h b/libui/include/ui/Color.h index 95a8d974..39d8db91 100644 --- a/libui/include/ui/Color.h +++ b/libui/include/ui/Color.h @@ -4,7 +4,7 @@ namespace ui { /** - * @brief A 32-bit RGBA color. + * @brief A 32-bit ARGB color. */ struct Color { @@ -13,16 +13,39 @@ namespace ui u8 colors[4]; }; + /** + * @brief Construct a new color from a 32-bit ARGB integer. + * + * @param raw The integer representing the color. + * @return constexpr Color The new color. + */ static constexpr Color from_u32(u32 raw) { return Color { .raw = raw }; } + /** + * @brief Construct a new color from its separate RGBA values (from 0 to 255). + * + * @param red The red value. + * @param green The green value. + * @param blue The blue value. + * @param alpha The alpha value. + * @return constexpr Color The new color. + */ static constexpr Color from_rgba(u8 red, u8 green, u8 blue, u8 alpha) { return Color { .colors = { blue, green, red, alpha } }; } + /** + * @brief Construct a new color from its separate RGB values (from 0 to 255). + * + * @param red The red value. + * @param green The green value. + * @param blue The blue value. + * @return constexpr Color The new color. + */ static constexpr Color from_rgb(u8 red, u8 green, u8 blue) { return from_rgba(red, green, blue, 0xff); @@ -31,4 +54,10 @@ namespace ui static constexpr Color WHITE = Color::from_rgb(0xff, 0xff, 0xff); static constexpr Color BLACK = Color::from_rgb(0x00, 0x00, 0x00); + + static constexpr Color BLUE = Color::from_rgb(0x00, 0x00, 0xff); + static constexpr Color GREEN = Color::from_rgb(0x00, 0xff, 0x00); + static constexpr Color RED = Color::from_rgb(0xff, 0x00, 0x00); + + static constexpr Color CYAN = Color::from_rgb(0x00, 0xff, 0xff); }; diff --git a/libui/include/ui/Rect.h b/libui/include/ui/Rect.h index 6d2bdec6..774013ad 100644 --- a/libui/include/ui/Rect.h +++ b/libui/include/ui/Rect.h @@ -8,11 +8,32 @@ namespace ui */ struct Rect { - Point begin; + Point pos; int width; int height; + /** + * @brief Check if a point is contained in this rectangle. + * + * @param point The point to check. + * @return true The point is contained inside the rectangle. + * @return false The point is not contained inside the rectangle. + */ bool contains(Point point); + + /** + * @brief Normalize a point to fit inside this rectangle. + * + * @param point The original point. + * @return Point The normalized point. + */ Point normalize(Point point); + + /** + * @brief Return a copy of this rectangle with no negative values (normalized to 0). + * + * @return Rect The new rectangle. + */ + Rect absolute(); }; } diff --git a/libui/src/Canvas.cpp b/libui/src/Canvas.cpp index 812610c0..d0e1b41e 100644 --- a/libui/src/Canvas.cpp +++ b/libui/src/Canvas.cpp @@ -8,22 +8,24 @@ namespace ui return Canvas { .width = width, .height = height, .stride = width, .ptr = ptr }; } - Result Canvas::subcanvas(Point begin, int w, int h, bool clamp) + Canvas Canvas::subcanvas(Rect rect) { - if (!clamp) + if (rect.pos.x < 0) { - if (begin.x + w > width) return err(ERANGE); - if (begin.y + h > height) return err(ERANGE); + rect.pos.x = 0; + rect.width = rect.width + rect.pos.x; } - else + if (rect.pos.y < 0) { - if (begin.x + w > width) w = width - begin.x; - if (begin.y + h > height) h = height - begin.y; + rect.pos.y = 0; + rect.height = rect.height + rect.pos.y; } + if (rect.pos.x + rect.width > width) rect.width = width - rect.pos.x; + if (rect.pos.y + rect.height > height) rect.height = height - rect.pos.y; - u8* p = ptr + begin.x * sizeof(Color) + (begin.y * sizeof(Color) * stride); + u8* p = ptr + rect.pos.x * sizeof(Color) + (rect.pos.y * sizeof(Color) * stride); - return Canvas { .width = w, .height = h, .stride = stride, .ptr = p }; + return Canvas { .width = rect.width, .height = rect.height, .stride = stride, .ptr = p }; } void Canvas::fill(Color color) diff --git a/libui/src/Rect.cpp b/libui/src/Rect.cpp index 5d180eab..444157a0 100644 --- a/libui/src/Rect.cpp +++ b/libui/src/Rect.cpp @@ -4,15 +4,21 @@ namespace ui { bool Rect::contains(Point point) { - return point.x >= begin.x && point.y >= begin.y && point.x <= begin.x + width && point.y <= begin.y + height; + return (point.x >= pos.x) && (point.y >= pos.y) && (point.x <= (pos.x + width)) && + (point.y <= (pos.y + height)); } Point Rect::normalize(Point point) { - if (point.x < begin.x) point.x = begin.x; - if (point.y < begin.y) point.y = begin.y; - if (point.x > begin.x + width) point.x = begin.x + width; - if (point.y > begin.y + height) point.y = begin.y + height; + if (point.x < pos.x) point.x = pos.x; + if (point.y < pos.y) point.y = pos.y; + if (point.x > pos.x + width) point.x = pos.x + width; + if (point.y > pos.y + height) point.y = pos.y + height; return point; } + + Rect Rect::absolute() + { + return Rect { ui::Point { pos.x < 0 ? 0 : pos.x, pos.y < 0 ? 0 : pos.y }, width, height }; + } }; diff --git a/wind/CMakeLists.txt b/wind/CMakeLists.txt index a7bb5bc9..8f5db052 100644 --- a/wind/CMakeLists.txt +++ b/wind/CMakeLists.txt @@ -4,6 +4,8 @@ set(SOURCES Screen.cpp Mouse.h Mouse.cpp + Window.h + Window.cpp ) add_executable(wind ${SOURCES}) diff --git a/wind/Mouse.cpp b/wind/Mouse.cpp index ed1f171f..b807e5c1 100644 --- a/wind/Mouse.cpp +++ b/wind/Mouse.cpp @@ -1,4 +1,5 @@ #include "Mouse.h" +#include Mouse::Mouse(ui::Canvas& screen) { @@ -9,13 +10,44 @@ Mouse::Mouse(ui::Canvas& screen) void Mouse::draw(ui::Canvas& screen) { - auto canvas = screen.subcanvas(m_position, 10, 10, true).release_value(); + auto canvas = screen.subcanvas(ui::Rect { m_position, 10, 10 }); canvas.fill(ui::WHITE); } -void Mouse::move(const moon::MousePacket& packet) +void Mouse::update(const moon::MousePacket& packet) { m_position.x += packet.xdelta; m_position.y -= packet.ydelta; m_position = m_screen_rect.normalize(m_position); + + if ((packet.buttons & moon::MouseButton::Left) && !m_dragging_window) + { + g_windows.for_each_reversed([this](Window* window) { + if (!this->m_dragging_window && window->surface.contains(this->m_position)) + { + this->m_dragging_window = window; + this->m_initial_drag_position = ui::Point { this->m_position.x - window->surface.pos.x, + this->m_position.y - window->surface.pos.y }; + os::println("Started drag: window at (%d,%d,%d,%d) with offset (%d,%d)", window->surface.pos.x, + window->surface.pos.y, window->surface.width, window->surface.height, + this->m_initial_drag_position.x, this->m_initial_drag_position.y); + } + }); + } + else if (m_dragging_window && !(packet.buttons & moon::MouseButton::Left)) + { + os::println("Stopped drag: window at (%d,%d,%d,%d) with offset (%d,%d)", m_dragging_window->surface.pos.x, + m_dragging_window->surface.pos.y, m_dragging_window->surface.width, + m_dragging_window->surface.height, this->m_initial_drag_position.x, + this->m_initial_drag_position.y); + m_dragging_window = nullptr; + } + + if (m_dragging_window) + { + m_dragging_window->surface.pos = + ui::Point { m_position.x - m_initial_drag_position.x, m_position.y - m_initial_drag_position.y }; + m_dragging_window->surface = m_dragging_window->surface.absolute(); + m_dragging_window->focus(); + } } diff --git a/wind/Mouse.h b/wind/Mouse.h index c188ceea..17d79c6d 100644 --- a/wind/Mouse.h +++ b/wind/Mouse.h @@ -1,5 +1,6 @@ #pragma once #include "Screen.h" +#include "Window.h" #include #include @@ -8,11 +9,14 @@ class Mouse public: Mouse(ui::Canvas& screen); - void move(const moon::MousePacket& packet); + void update(const moon::MousePacket& packet); void draw(ui::Canvas& screen); private: ui::Point m_position; ui::Rect m_screen_rect; + + Window* m_dragging_window = nullptr; + ui::Point m_initial_drag_position; }; diff --git a/wind/Window.cpp b/wind/Window.cpp new file mode 100644 index 00000000..fc2918f2 --- /dev/null +++ b/wind/Window.cpp @@ -0,0 +1,20 @@ +#include "Window.h" + +LinkedList g_windows; + +void Window::draw(ui::Canvas& screen) +{ + screen.subcanvas(surface).fill(color); +} + +void Window::focus() +{ + // Bring the window to the front of the list. + g_windows.remove(this); + g_windows.append(this); +} + +Window::Window(ui::Rect r, ui::Color c) : surface(r), color(c) +{ + g_windows.append(this); +} diff --git a/wind/Window.h b/wind/Window.h new file mode 100644 index 00000000..45f1655b --- /dev/null +++ b/wind/Window.h @@ -0,0 +1,19 @@ +#pragma once +#include +#include +#include +#include + +struct Window : public LinkedListNode +{ + ui::Rect surface; + ui::Color color; + + Window(ui::Rect, ui::Color); + + void focus(); + + void draw(ui::Canvas& screen); +}; + +extern LinkedList g_windows; diff --git a/wind/main.cpp b/wind/main.cpp index bda0fa86..ae8c2bcd 100644 --- a/wind/main.cpp +++ b/wind/main.cpp @@ -1,5 +1,6 @@ #include "Mouse.h" #include "Screen.h" +#include "Window.h" #include #include #include @@ -71,10 +72,19 @@ Result luna_main(int argc, char** argv) ui::Color background = ui::BLACK; + TRY(make(ui::Rect { 200, 200, 600, 400 }, ui::GREEN)); + TRY(make(ui::Rect { 100, 100, 300, 200 }, ui::RED)); + TRY(make(ui::Rect { 600, 130, 350, 250 }, ui::CYAN)); + Vector> clients; while (1) { + screen.canvas().fill(background); + for (auto* window : g_windows) window->draw(screen.canvas()); + mouse_pointer.draw(screen.canvas()); + screen.sync(); + struct pollfd fds[] = { { .fd = mouse->fd(), .events = POLLIN, .revents = 0 }, { .fd = keyboard->fd(), .events = POLLIN, .revents = 0 }, @@ -89,7 +99,7 @@ Result luna_main(int argc, char** argv) { moon::MousePacket packet; TRY(mouse->read_typed(packet)); - mouse_pointer.move(packet); + mouse_pointer.update(packet); } if (fds[1].revents & POLLIN) { @@ -103,9 +113,5 @@ Result luna_main(int argc, char** argv) os::println("wind: New client connected!"); TRY(clients.try_append(client)); } - - screen.canvas().fill(background); - mouse_pointer.draw(screen.canvas()); - screen.sync(); } }