From 36cc84c50dd0f54862b708c59b0e2e0ca3f07a5b Mon Sep 17 00:00:00 2001 From: apio Date: Thu, 3 Aug 2023 12:20:59 +0200 Subject: [PATCH] wind: Add a simple display server skeleton using libui No client functionality yet, but it's a start. --- CMakeLists.txt | 1 + wind/CMakeLists.txt | 14 +++++++ wind/Mouse.cpp | 21 ++++++++++ wind/Mouse.h | 18 +++++++++ wind/Screen.cpp | 34 ++++++++++++++++ wind/Screen.h | 27 +++++++++++++ wind/main.cpp | 98 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 213 insertions(+) create mode 100644 wind/CMakeLists.txt create mode 100644 wind/Mouse.cpp create mode 100644 wind/Mouse.h create mode 100644 wind/Screen.cpp create mode 100644 wind/Screen.h create mode 100644 wind/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ffd2f6d..dd08c362 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,3 +51,4 @@ add_subdirectory(kernel) add_subdirectory(apps) add_subdirectory(tests) add_subdirectory(shell) +add_subdirectory(wind) diff --git a/wind/CMakeLists.txt b/wind/CMakeLists.txt new file mode 100644 index 00000000..a7bb5bc9 --- /dev/null +++ b/wind/CMakeLists.txt @@ -0,0 +1,14 @@ +set(SOURCES + main.cpp + Screen.h + Screen.cpp + Mouse.h + Mouse.cpp +) + +add_executable(wind ${SOURCES}) +target_compile_options(wind PRIVATE -Os ${COMMON_FLAGS} -Wno-write-strings) +add_dependencies(wind libc) +target_include_directories(wind PRIVATE ${LUNA_BASE}/usr/include ${CMAKE_CURRENT_LIST_DIR}) +target_link_libraries(wind PRIVATE os ui) +install(TARGETS wind DESTINATION ${LUNA_BASE}/usr/bin) diff --git a/wind/Mouse.cpp b/wind/Mouse.cpp new file mode 100644 index 00000000..ed1f171f --- /dev/null +++ b/wind/Mouse.cpp @@ -0,0 +1,21 @@ +#include "Mouse.h" + +Mouse::Mouse(ui::Canvas& screen) +{ + m_position.x = screen.width / 2; + m_position.y = screen.height / 2; + m_screen_rect = screen.rect(); +} + +void Mouse::draw(ui::Canvas& screen) +{ + auto canvas = screen.subcanvas(m_position, 10, 10, true).release_value(); + canvas.fill(ui::WHITE); +} + +void Mouse::move(const moon::MousePacket& packet) +{ + m_position.x += packet.xdelta; + m_position.y -= packet.ydelta; + m_position = m_screen_rect.normalize(m_position); +} diff --git a/wind/Mouse.h b/wind/Mouse.h new file mode 100644 index 00000000..c188ceea --- /dev/null +++ b/wind/Mouse.h @@ -0,0 +1,18 @@ +#pragma once +#include "Screen.h" +#include +#include + +class Mouse +{ + public: + Mouse(ui::Canvas& screen); + + void move(const moon::MousePacket& packet); + + void draw(ui::Canvas& screen); + + private: + ui::Point m_position; + ui::Rect m_screen_rect; +}; diff --git a/wind/Screen.cpp b/wind/Screen.cpp new file mode 100644 index 00000000..b44874ba --- /dev/null +++ b/wind/Screen.cpp @@ -0,0 +1,34 @@ +#include "Screen.h" +#include +#include +#include +#include +#include + +Result Screen::open() +{ + int fd = ::open("/dev/fb0", O_RDWR | O_CLOEXEC); + if (fd < 0) return err(errno); + + int width = ioctl(fd, FB_GET_WIDTH); + int height = ioctl(fd, FB_GET_HEIGHT); + + void* p = mmap(nullptr, width * height * BYTES_PER_PIXEL, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (p == MAP_FAILED) + { + close(fd); + return err(errno); + } + + Screen screen; + + screen.m_canvas = ui::Canvas::create((u8*)p, width, height); + screen.m_size = width * height * BYTES_PER_PIXEL; + + return screen; +} + +void Screen::sync() +{ + msync(m_canvas.ptr, size(), MS_SYNC); +} diff --git a/wind/Screen.h b/wind/Screen.h new file mode 100644 index 00000000..a98295c6 --- /dev/null +++ b/wind/Screen.h @@ -0,0 +1,27 @@ +#pragma once +#include +#include + +constexpr int BYTES_PER_PIXEL = 4; + +class Screen +{ + public: + static Result open(); + + ui::Canvas& canvas() + { + return m_canvas; + } + + int size() const + { + return m_size; + } + + void sync(); + + private: + ui::Canvas m_canvas; + int m_size; +}; diff --git a/wind/main.cpp b/wind/main.cpp new file mode 100644 index 00000000..318da9b9 --- /dev/null +++ b/wind/main.cpp @@ -0,0 +1,98 @@ +#include "Mouse.h" +#include "Screen.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +Result luna_main(int argc, char** argv) +{ + StringView socket_path = "/tmp/wind.sock"; + StringView user; + + os::ArgumentParser parser; + parser.add_description("The display server for Luna's graphical user interface."_sv); + parser.add_system_program_info("wind"_sv); + parser.add_value_argument(socket_path, 's', "socket"_sv, "the path for the local IPC socket"_sv); + parser.add_value_argument(user, 'u', "user"_sv, "the user to run as"_sv); + parser.parse(argc, argv); + + if (geteuid() != 0) + { + os::eprintln("error: wind must be run as root to initialize resources, run with --user= to drop " + "privileges afterwards"); + return 1; + } + + auto mouse = TRY(os::File::open("/dev/mouse", os::File::ReadOnly)); + mouse->set_buffer(os::File::NotBuffered); + mouse->set_close_on_exec(); + + auto keyboard = TRY(os::File::open("/dev/kbd", os::File::ReadOnly)); + keyboard->set_buffer(os::File::NotBuffered); + keyboard->set_close_on_exec(); + + auto screen = TRY(Screen::open()); + + Mouse mouse_pointer { screen.canvas() }; + + ioctl(STDIN_FILENO, TTYSETGFX, 1); + + setpgid(0, 0); + + int fd = open("/dev/null", O_RDONLY); + if (fd >= 0) + { + dup2(fd, STDIN_FILENO); + close(fd); + } + + clearenv(); + + if (!user.is_empty()) + { + auto* pwd = getpwnam(user.chars()); + if (pwd) + { + setgid(pwd->pw_gid); + setuid(pwd->pw_uid); + } + } + + ui::Color background = ui::BLACK; + + while (1) + { + struct pollfd fds[] = { + { .fd = mouse->fd(), .events = POLLIN, .revents = 0 }, + { .fd = keyboard->fd(), .events = POLLIN, .revents = 0 }, + }; + + int rc = poll(fds, 2, 1000); + if (!rc) continue; + if (rc < 0) { os::println("poll: error: %s", strerror(errno)); } + + if (fds[0].revents & POLLIN) + { + moon::MousePacket packet; + TRY(mouse->read_typed(packet)); + mouse_pointer.move(packet); + } + if (fds[1].revents & POLLIN) + { + moon::KeyboardPacket packet; + TRY(keyboard->read_typed(packet)); + background = ui::Color::from_rgb(0x00, 0x00, packet.key * 2); + } + + screen.canvas().fill(background); + mouse_pointer.draw(screen.canvas()); + screen.sync(); + } +}