wind: Add a simple display server skeleton using libui

No client functionality yet, but it's a start.
This commit is contained in:
apio 2023-08-03 12:20:59 +02:00
parent 9df88bac3e
commit 36cc84c50d
Signed by: apio
GPG Key ID: B8A7D06E42258954
7 changed files with 213 additions and 0 deletions

View File

@ -51,3 +51,4 @@ add_subdirectory(kernel)
add_subdirectory(apps)
add_subdirectory(tests)
add_subdirectory(shell)
add_subdirectory(wind)

14
wind/CMakeLists.txt Normal file
View File

@ -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)

21
wind/Mouse.cpp Normal file
View File

@ -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);
}

18
wind/Mouse.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include "Screen.h"
#include <moon/Mouse.h>
#include <ui/Canvas.h>
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;
};

34
wind/Screen.cpp Normal file
View File

@ -0,0 +1,34 @@
#include "Screen.h"
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
Result<Screen> 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);
}

27
wind/Screen.h Normal file
View File

@ -0,0 +1,27 @@
#pragma once
#include <luna/OwnedPtr.h>
#include <ui/Canvas.h>
constexpr int BYTES_PER_PIXEL = 4;
class Screen
{
public:
static Result<Screen> open();
ui::Canvas& canvas()
{
return m_canvas;
}
int size() const
{
return m_size;
}
void sync();
private:
ui::Canvas m_canvas;
int m_size;
};

98
wind/main.cpp Normal file
View File

@ -0,0 +1,98 @@
#include "Mouse.h"
#include "Screen.h"
#include <errno.h>
#include <moon/Keyboard.h>
#include <os/ArgumentParser.h>
#include <os/File.h>
#include <pwd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <unistd.h>
Result<int> 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=<USERNAME> 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();
}
}