Compare commits

...

4 Commits

Author SHA1 Message Date
5c96a31cd8
base: Start wind on startup instead of the shell
All checks were successful
continuous-integration/drone/pr Build is passing
2023-08-03 12:21:29 +02:00
07a1811102
wind: Add a simple display server skeleton using libui
No client functionality yet, but it's a start.
2023-08-03 12:21:29 +02:00
8f170ff1f0
libui: Add a GUI and graphics library 2023-08-03 12:21:29 +02:00
7076ff2f6b
kernel: Fix negative movement in the PS/2 mouse driver 2023-08-03 12:18:30 +02:00
19 changed files with 392 additions and 5 deletions

View File

@ -45,8 +45,10 @@ endif()
add_subdirectory(libluna)
add_subdirectory(libos)
add_subdirectory(libui)
add_subdirectory(libc)
add_subdirectory(kernel)
add_subdirectory(apps)
add_subdirectory(tests)
add_subdirectory(shell)
add_subdirectory(wind)

View File

@ -1,4 +1,4 @@
Name=login
Description=Start the command-line login program.
Command=/usr/bin/login
Description=Start the display server.
Command=/usr/bin/wind --user=selene
Restart=true

View File

@ -43,8 +43,8 @@ static void process_mouse_event(u8 data)
packet.buttons = 0;
u8 flags = g_mouse_packet[0];
if (flags & PS2_MOUSE_X_SIGN) packet.xdelta = -packet.xdelta;
if (flags & PS2_MOUSE_Y_SIGN) packet.ydelta = -packet.ydelta;
if (flags & PS2_MOUSE_X_SIGN) packet.xdelta = -(256 - packet.xdelta);
if (flags & PS2_MOUSE_Y_SIGN) packet.ydelta = -(256 - packet.ydelta);
if (flags & PS2_MOUSE_MIDDLE_BTN) packet.buttons |= moon::MouseButton::Middle;
if (flags & PS2_MOUSE_RIGHT_BTN) packet.buttons |= moon::MouseButton::Right;

15
libui/CMakeLists.txt Normal file
View File

@ -0,0 +1,15 @@
# The UI and graphics library for Luna.
file(GLOB HEADERS include/ui/*.h)
set(SOURCES
${HEADERS}
src/Canvas.cpp
src/Color.cpp
src/Rect.cpp
)
add_library(ui ${SOURCES})
target_compile_options(ui PRIVATE ${COMMON_FLAGS})
target_include_directories(ui PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include/)
target_include_directories(ui PUBLIC ${LUNA_BASE}/usr/include)

30
libui/include/ui/Canvas.h Normal file
View File

@ -0,0 +1,30 @@
#pragma once
#include <luna/Result.h>
#include <luna/Types.h>
#include <ui/Color.h>
#include <ui/Point.h>
#include <ui/Rect.h>
namespace ui
{
/**
* @brief A drawable surface.
*/
struct Canvas
{
int width;
int height;
int stride;
u8* ptr;
static Canvas create(u8* ptr, int width, int height);
Result<Canvas> subcanvas(Point begin, int width, int height, bool clamp);
Rect rect()
{
return Rect { .begin = { 0, 0 }, .width = width, .height = height };
}
void fill(Color color);
};
};

34
libui/include/ui/Color.h Normal file
View File

@ -0,0 +1,34 @@
#pragma once
#include <luna/Types.h>
namespace ui
{
/**
* @brief A 32-bit RGBA color.
*/
struct Color
{
union {
u32 raw;
u8 colors[4];
};
static constexpr Color from_u32(u32 raw)
{
return Color { .raw = raw };
}
static constexpr Color from_rgba(u8 red, u8 green, u8 blue, u8 alpha)
{
return Color { .colors = { blue, green, red, alpha } };
}
static constexpr Color from_rgb(u8 red, u8 green, u8 blue)
{
return from_rgba(red, green, blue, 0xff);
}
};
static constexpr Color WHITE = Color::from_rgb(0xff, 0xff, 0xff);
static constexpr Color BLACK = Color::from_rgb(0x00, 0x00, 0x00);
};

13
libui/include/ui/Point.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
namespace ui
{
/**
* @brief A point in 2D space.
*/
struct Point
{
int x { 0 };
int y { 0 };
};
}

18
libui/include/ui/Rect.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include <ui/Point.h>
namespace ui
{
/**
* @brief A simple rectangle.
*/
struct Rect
{
Point begin;
int width;
int height;
bool contains(Point point);
Point normalize(Point point);
};
}

43
libui/src/Canvas.cpp Normal file
View File

@ -0,0 +1,43 @@
#include <string.h>
#include <ui/Canvas.h>
namespace ui
{
Canvas Canvas::create(u8* ptr, int width, int height)
{
return Canvas { .width = width, .height = height, .stride = width, .ptr = ptr };
}
Result<Canvas> Canvas::subcanvas(Point begin, int w, int h, bool clamp)
{
if (!clamp)
{
if (begin.x + w > width) return err(ERANGE);
if (begin.y + h > height) return err(ERANGE);
}
else
{
if (begin.x + w > width) w = width - begin.x;
if (begin.y + h > height) h = height - begin.y;
}
u8* p = ptr + begin.x * sizeof(Color) + (begin.y * sizeof(Color) * stride);
return Canvas { .width = w, .height = h, .stride = stride, .ptr = p };
}
void Canvas::fill(Color color)
{
u8* p = ptr;
for (int i = 0; i < height; i++)
{
u32* colorp = (u32*)p;
for (int j = 0; j < width; j++)
{
*colorp = color.raw;
colorp++;
}
p += stride * sizeof(Color);
}
}
}

0
libui/src/Color.cpp Normal file
View File

18
libui/src/Rect.cpp Normal file
View File

@ -0,0 +1,18 @@
#include <ui/Rect.h>
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;
}
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;
return point;
}
};

View File

@ -8,10 +8,12 @@ cd $LUNA_ROOT
mkdir -p $LUNA_BASE
mkdir -p $LUNA_BASE/usr/include
mkdir -p $LUNA_BASE/usr/include/luna
mkdir -p $LUNA_BASE/usr/include/ui
mkdir -p $LUNA_BASE/usr/include/os
mkdir -p $LUNA_BASE/usr/include/moon
cp --preserve=timestamps -RT libc/include/ $LUNA_BASE/usr/include
cp --preserve=timestamps -RT libluna/include/luna/ $LUNA_BASE/usr/include/luna
cp --preserve=timestamps -RT libui/include/ui/ $LUNA_BASE/usr/include/ui
cp --preserve=timestamps -RT libos/include/os/ $LUNA_BASE/usr/include/os
cp --preserve=timestamps -RT kernel/src/api/ $LUNA_BASE/usr/include/moon

View File

@ -4,7 +4,7 @@ source $(dirname $0)/env.sh
cd $LUNA_ROOT
FOLDERS=(kernel libc libos libluna apps shell tests)
FOLDERS=(kernel libc libos libui libluna apps shell tests)
SOURCES=($(find ${FOLDERS[@]} -type f -name "*.cpp"))
SOURCES+=($(find ${FOLDERS[@]} -type f -name "*.h"))

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