Userspace: Start work on GUI and WindowServer

Current implementation can create custom windows and each window has
its own framebuffer. When window wants to write its framebuffer to the
screen it will send a message to the WindowServer using unix sockets.
This commit is contained in:
2024-05-29 16:00:54 +03:00
parent d4d530e6c8
commit c2b6ba0d5a
16 changed files with 1116 additions and 0 deletions

View File

@@ -39,9 +39,11 @@ set(USERSPACE_PROJECTS
test-tcp
test-udp
test-unix-socket
test-window
touch
u8sum
whoami
WindowServer
yes
)

View File

@@ -0,0 +1,18 @@
cmake_minimum_required(VERSION 3.26)
project(WindowServer CXX)
set(SOURCES
main.cpp
Framebuffer.cpp
WindowServer.cpp
)
add_executable(WindowServer ${SOURCES})
target_compile_options(WindowServer PUBLIC -O2 -g)
target_link_libraries(WindowServer PUBLIC libc ban libgui libinput)
add_custom_target(WindowServer-install
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/WindowServer ${BANAN_BIN}/
DEPENDS WindowServer
)

View File

@@ -0,0 +1,35 @@
/* GIMP header image file format (RGB) */
#include <stdint.h>
static int32_t s_cursor_width = 17;
static int32_t s_cursor_height = 26;
static const char* s_cursor_data =
"!!!!`Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$`"
"`Q$`!!!!!!!!`Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$`"
"`Q$``Q$`!!!!````!!!!`Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$`"
"`Q$``Q$``Q$`!!!!````````!!!!`Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$`"
"`Q$``Q$``Q$``Q$`!!!!````````````!!!!`Q$``Q$``Q$``Q$``Q$``Q$``Q$`"
"`Q$``Q$``Q$``Q$``Q$`!!!!````````````````!!!!`Q$``Q$``Q$``Q$``Q$`"
"`Q$``Q$``Q$``Q$``Q$``Q$`!!!!````````````````````!!!!`Q$``Q$``Q$`"
"`Q$``Q$``Q$``Q$``Q$``Q$``Q$`!!!!````````````````````````!!!!`Q$`"
"`Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$`!!!!````````````````````````````"
"!!!!`Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$`!!!!````````````````````````"
"````````!!!!`Q$``Q$``Q$``Q$``Q$``Q$``Q$`!!!!````````````````````"
"````````````````!!!!`Q$``Q$``Q$``Q$``Q$``Q$`!!!!````````````````"
"````````````````````````!!!!`Q$``Q$``Q$``Q$``Q$`!!!!````````````"
"````````````````````````````````!!!!`Q$``Q$``Q$``Q$`!!!!````````"
"````````````````````````````````````````!!!!`Q$``Q$``Q$`!!!!````"
"````````````````````````````````````````````````!!!!`Q$``Q$`!!!!"
"````````````````````````````````````````````````````````!!!!`Q$`"
"!!!!````````````````````````````````!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
"!!!!!!!!````````````````````````````````!!!!`Q$``Q$``Q$``Q$``Q$`"
"`Q$``Q$`!!!!````````````````!!!!````````````````!!!!`Q$``Q$``Q$`"
"`Q$``Q$``Q$`!!!!````````````!!!!`Q$`!!!!````````````!!!!`Q$``Q$`"
"`Q$``Q$``Q$``Q$`!!!!````````!!!!`Q$``Q$`!!!!````````````````!!!!"
"`Q$``Q$``Q$``Q$``Q$`!!!!````!!!!`Q$``Q$``Q$``Q$`!!!!````````````"
"!!!!`Q$``Q$``Q$``Q$``Q$`!!!!!!!!`Q$``Q$``Q$``Q$``Q$`!!!!````````"
"````````!!!!`Q$``Q$``Q$``Q$`!!!!`Q$``Q$``Q$``Q$``Q$``Q$``Q$`!!!!"
"````````````!!!!`Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$`"
"!!!!````````````!!!!`Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$``Q$`"
"`Q$``Q$`!!!!!!!!!!!!`Q$``Q$``Q$``Q$``Q$`";

View File

@@ -0,0 +1,45 @@
#include "Framebuffer.h"
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/framebuffer.h>
#include <sys/mman.h>
Framebuffer open_framebuffer()
{
int framebuffer_fd = open("/dev/fb0", O_RDWR);
if (framebuffer_fd == -1)
{
perror("open");
exit(1);
}
framebuffer_info_t framebuffer_info;
if (pread(framebuffer_fd, &framebuffer_info, sizeof(framebuffer_info), -1) == -1)
{
perror("pread");
exit(1);
}
const size_t framebuffer_bytes = framebuffer_info.width * framebuffer_info.height * (BANAN_FB_BPP / 8);
uint32_t* framebuffer_mmap = (uint32_t*)mmap(NULL, framebuffer_bytes, PROT_READ | PROT_WRITE, MAP_SHARED, framebuffer_fd, 0);
if (framebuffer_mmap == MAP_FAILED)
{
perror("mmap");
exit(1);
}
memset(framebuffer_mmap, 0, framebuffer_bytes);
msync(framebuffer_mmap, framebuffer_bytes, MS_SYNC);
Framebuffer framebuffer;
framebuffer.fd = framebuffer_fd;
framebuffer.mmap = framebuffer_mmap;
framebuffer.width = framebuffer_info.width;
framebuffer.height = framebuffer_info.height;
framebuffer.bpp = BANAN_FB_BPP;
return framebuffer;
}

View File

@@ -0,0 +1,16 @@
#pragma once
#include "Utils.h"
struct Framebuffer
{
int fd;
uint32_t* mmap;
int32_t width;
int32_t height;
uint8_t bpp;
Rectangle area() const { return { 0, 0, width, height }; }
};
Framebuffer open_framebuffer();

View File

@@ -0,0 +1,59 @@
#pragma once
#include <BAN/Math.h>
#include <BAN/Optional.h>
#include <stdint.h>
struct Position
{
int32_t x;
int32_t y;
};
struct Rectangle
{
int32_t x;
int32_t y;
int32_t width;
int32_t height;
bool contains(Position position) const
{
if (position.x < x || position.x >= x + width)
return false;
if (position.y < y || position.y >= y + height)
return false;
return true;
}
BAN::Optional<Rectangle> get_overlap(Rectangle other) const
{
const auto min_x = BAN::Math::max(x, other.x);
const auto min_y = BAN::Math::max(y, other.y);
const auto max_x = BAN::Math::min(x + width, other.x + other.width);
const auto max_y = BAN::Math::min(y + height, other.y + other.height);
if (min_x >= max_x || min_y >= max_y)
return {};
return Rectangle {
.x = min_x,
.y = min_y,
.width = max_x - min_x,
.height = max_y - min_y,
};
}
Rectangle get_bounding_box(Rectangle other) const
{
const auto min_x = BAN::Math::min(x, other.x);
const auto min_y = BAN::Math::min(y, other.y);
const auto max_x = BAN::Math::max(x + width, other.x + other.width);
const auto max_y = BAN::Math::max(y + height, other.y + other.height);
return Rectangle {
.x = min_x,
.y = min_y,
.width = max_x - min_x,
.height = max_y - min_y,
};
}
};

View File

@@ -0,0 +1,45 @@
#pragma once
#include "Utils.h"
#include <BAN/RefPtr.h>
class Window : public BAN::RefCounted<Window>
{
public:
Window(int fd)
: m_client_fd(fd)
{ }
void set_position(Position position)
{
m_area.x = position.x;
m_area.y = position.y;
}
void set_size(Position size, uint32_t* fb_addr)
{
m_area.width = size.x;
m_area.height = size.y;
m_fb_addr = fb_addr;
}
bool is_deleted() const { return m_deleted; }
void mark_deleted() { m_deleted = true; }
int client_fd() const { return m_client_fd; }
int32_t x() const { return m_area.x; }
int32_t y() const { return m_area.y; }
uint32_t width() const { return m_area.width; }
uint32_t height() const { return m_area.height; }
Rectangle size() const { return { 0, 0, m_area.width, m_area.height }; }
const Rectangle& area() const { return m_area; }
const uint32_t* framebuffer() const { return m_fb_addr; }
private:
const int m_client_fd { -1 };
uint32_t* m_fb_addr { nullptr };
Rectangle m_area { 0, 0, 0, 0 };
bool m_deleted { false };
};

View File

@@ -0,0 +1,224 @@
#include "Cursor.h"
#include "WindowServer.h"
#include <LibGUI/Window.h>
#include <LibInput/KeyboardLayout.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/socket.h>
void WindowServer::add_window(int fd, BAN::RefPtr<Window> window)
{
MUST(m_windows_ordered.insert(0, window));
MUST(m_windows.insert(fd, window));
set_focused_window(window);
}
void WindowServer::for_each_window(const BAN::Function<BAN::Iteration(int, Window&)>& callback)
{
BAN::Vector<int> deleted_windows;
for (auto it = m_windows.begin(); it != m_windows.end(); it++)
{
auto ret = callback(it->key, *it->value);
if (it->value->is_deleted())
MUST(deleted_windows.push_back(it->key));
if (ret == BAN::Iteration::Break)
break;
ASSERT(ret == BAN::Iteration::Continue);
}
for (int fd : deleted_windows)
{
auto window = m_windows[fd];
m_windows.remove(fd);
for (size_t i = 0; i < m_windows_ordered.size(); i++)
{
if (m_windows_ordered[i] == window)
{
m_windows_ordered.remove(i);
break;
}
}
}
}
void WindowServer::on_key_event(LibInput::KeyEvent event)
{
// Mod key is not passed to clients
if (event.key == LibInput::Key::Super)
{
m_is_mod_key_held = event.pressed();
return;
}
// Quick hack to stop the window server
if (event.pressed() && event.key == LibInput::Key::Escape)
exit(0);
if (m_focused_window)
{
LibGUI::EventPacket packet;
packet.type = LibGUI::EventPacket::Type::KeyEvent;
packet.key_event = event;
send(m_focused_window->client_fd(), &packet, sizeof(packet), 0);
}
}
void WindowServer::on_mouse_button(LibInput::MouseButtonEvent event)
{
BAN::RefPtr<Window> target_window;
for (size_t i = m_windows_ordered.size(); i > 0; i--)
{
if (m_windows_ordered[i - 1]->area().contains(m_cursor))
{
target_window = m_windows_ordered[i - 1];
break;
}
}
// Ignore mouse button events which are not on top of a window
if (!target_window)
return;
set_focused_window(target_window);
// Handle window moving when mod key is held
if (m_is_mod_key_held && event.pressed && event.button == LibInput::MouseButton::Left && !m_is_moving_window)
m_is_moving_window = true;
else if (m_is_moving_window && !event.pressed)
m_is_moving_window = false;
else
{
// NOTE: we always have target window if code reaches here
LibGUI::EventPacket packet;
packet.type = LibGUI::EventPacket::Type::MouseButtonEvent;
packet.mouse_button_event.button = event.button;
packet.mouse_button_event.pressed = event.pressed;
packet.mouse_button_event.x = m_cursor.x - m_focused_window->x();
packet.mouse_button_event.y = m_cursor.y - m_focused_window->y();
send(m_focused_window->client_fd(), &packet, sizeof(packet), 0);
}
}
void WindowServer::on_mouse_move(LibInput::MouseMoveEvent event)
{
Rectangle old_cursor { m_cursor.x, m_cursor.y, s_cursor_width, s_cursor_height };
const int32_t new_x = BAN::Math::clamp(m_cursor.x + event.rel_x, 0, m_framebuffer.width);
const int32_t new_y = BAN::Math::clamp(m_cursor.y - event.rel_y, 0, m_framebuffer.height);
event.rel_x = new_x - m_cursor.x;
event.rel_y = new_y - m_cursor.y;
if (event.rel_x == 0 && event.rel_y == 0)
return;
m_cursor.x = new_x;
m_cursor.y = new_y;
Rectangle new_cursor { m_cursor.x, m_cursor.y, s_cursor_width, s_cursor_height };
invalidate(old_cursor.get_bounding_box(old_cursor));
invalidate(new_cursor.get_bounding_box(old_cursor));
if (m_is_moving_window)
{
auto old_window = m_focused_window->area();
m_focused_window->set_position({
m_focused_window->x() + event.rel_x,
m_focused_window->y() + event.rel_y,
});
invalidate(old_window);
invalidate(m_focused_window->area());
return;
}
if (m_focused_window)
{
LibGUI::EventPacket packet;
packet.type = LibGUI::EventPacket::Type::MouseMoveEvent;
packet.mouse_move_event.x = m_cursor.x - m_focused_window->x();
packet.mouse_move_event.y = m_cursor.y - m_focused_window->y();
send(m_focused_window->client_fd(), &packet, sizeof(packet), 0);
}
}
void WindowServer::on_mouse_scroll(LibInput::MouseScrollEvent event)
{
if (m_focused_window)
{
LibGUI::EventPacket packet;
packet.type = LibGUI::EventPacket::Type::MouseScrollEvent;
packet.mouse_scroll_event = event;
send(m_focused_window->client_fd(), &packet, sizeof(packet), 0);
}
}
void WindowServer::set_focused_window(BAN::RefPtr<Window> window)
{
if (m_focused_window == window)
return;
for (size_t i = m_windows_ordered.size(); i > 0; i--)
{
if (m_windows_ordered[i - 1] == window)
{
m_focused_window = window;
m_windows_ordered.remove(i - 1);
MUST(m_windows_ordered.push_back(window));
invalidate(window->area());
break;
}
}
}
void WindowServer::invalidate(Rectangle area)
{
auto fb_overlap = area.get_overlap(m_framebuffer.area());
if (!fb_overlap.has_value())
return;
area = fb_overlap.release_value();
for (int32_t y = area.y; y < area.y + area.height; y++)
memset(&m_framebuffer.mmap[y * m_framebuffer.width + area.x], 0, area.width * 4);
for (auto& pwindow : m_windows_ordered)
{
auto& window = *pwindow;
auto overlap = window.area().get_overlap(area);
if (!overlap.has_value())
continue;
const int32_t src_x = overlap->x - window.x();
const int32_t src_y = overlap->y - window.y();
for (int32_t y_off = 0; y_off < overlap->height; y_off++)
memcpy(
&m_framebuffer.mmap[(overlap->y + y_off) * m_framebuffer.width + overlap->x],
&window.framebuffer()[(src_y + y_off) * window.width() + src_x],
overlap->width * 4
);
}
Rectangle cursor { m_cursor.x, m_cursor.y, s_cursor_width, s_cursor_height };
auto overlap = cursor.get_overlap(area);
if (overlap.has_value())
{
for (int32_t dy = overlap->y - cursor.y; dy < overlap->height; dy++)
{
for (int32_t dx = overlap->x - cursor.x; dx < overlap->width; dx++)
{
const uint32_t offset = (dy * s_cursor_width + dx) * 4;
uint32_t r = (((s_cursor_data[offset + 0] - 33) << 2) | ((s_cursor_data[offset + 1] - 33) >> 4));
uint32_t g = ((((s_cursor_data[offset + 1] - 33) & 0xF) << 4) | ((s_cursor_data[offset + 2] - 33) >> 2));
uint32_t b = ((((s_cursor_data[offset + 2] - 33) & 0x3) << 6) | ((s_cursor_data[offset + 3] - 33)));
uint32_t color = (r << 16) | (g << 8) | b;
if (color != 0xFF00FF)
m_framebuffer.mmap[(overlap->y + dy) * m_framebuffer.width + (overlap->x + dx)] = color;
}
}
}
uintptr_t mmap_start = reinterpret_cast<uintptr_t>(m_framebuffer.mmap) + area.y * m_framebuffer.width * 4;
uintptr_t mmap_end = mmap_start + (area.height + 1) * m_framebuffer.width * 4;
mmap_start &= ~(uintptr_t)0xFFF;
msync(reinterpret_cast<void*>(mmap_start), mmap_end - mmap_start, MS_SYNC);
}

View File

@@ -0,0 +1,44 @@
#pragma once
#include "Framebuffer.h"
#include "Window.h"
#include <BAN/Function.h>
#include <BAN/Iteration.h>
#include <BAN/Vector.h>
#include <BAN/HashMap.h>
#include <LibInput/KeyEvent.h>
#include <LibInput/MouseEvent.h>
class WindowServer
{
public:
WindowServer(Framebuffer& framebuffer)
: m_framebuffer(framebuffer)
, m_cursor({ framebuffer.width / 2, framebuffer.height / 2 })
{
invalidate(m_framebuffer.area());
}
void add_window(int fd, BAN::RefPtr<Window> window);
void for_each_window(const BAN::Function<BAN::Iteration(int, Window&)>& callback);
void on_key_event(LibInput::KeyEvent event);
void on_mouse_button(LibInput::MouseButtonEvent event);
void on_mouse_move(LibInput::MouseMoveEvent event);
void on_mouse_scroll(LibInput::MouseScrollEvent event);
void set_focused_window(BAN::RefPtr<Window> window);
void invalidate(Rectangle area);
private:
Framebuffer& m_framebuffer;
BAN::Vector<BAN::RefPtr<Window>> m_windows_ordered;
BAN::HashMap<int, BAN::RefPtr<Window>> m_windows;
bool m_is_mod_key_held { false };
bool m_is_moving_window { false };
BAN::RefPtr<Window> m_focused_window;
Position m_cursor;
};

View File

@@ -0,0 +1,267 @@
#include "WindowServer.h"
#include <BAN/Debug.h>
#include <LibGUI/Window.h>
#include <LibInput/KeyboardLayout.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/banan-os.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <unistd.h>
int open_server_fd()
{
struct stat st;
if (stat(LibGUI::s_window_server_socket.data(), &st) != -1)
unlink(LibGUI::s_window_server_socket.data());
int server_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (server_fd == -1)
{
perror("socket");
exit(1);
}
sockaddr_un server_addr;
server_addr.sun_family = AF_UNIX;
strcpy(server_addr.sun_path, LibGUI::s_window_server_socket.data());
if (bind(server_fd, (sockaddr*)&server_addr, sizeof(server_addr)) == -1)
{
perror("bind");
exit(1);
}
if (chmod("/tmp/resolver.sock", 0777) == -1)
{
perror("chmod");
exit(1);
}
if (listen(server_fd, SOMAXCONN) == -1)
{
perror("listen");
exit(1);
}
return server_fd;
}
int main()
{
srand(time(nullptr));
int server_fd = open_server_fd();
auto framebuffer = open_framebuffer();
if (framebuffer.bpp != 32)
{
dwarnln("Window server requires 32 bpp");
return 1;
}
if (tty_ctrl(STDIN_FILENO, TTY_CMD_UNSET, TTY_FLAG_ENABLE_INPUT) == -1)
{
perror("tty_ctrl");
exit(1);
}
atexit([]() { tty_ctrl(STDIN_FILENO, TTY_CMD_SET, TTY_FLAG_ENABLE_INPUT); });
MUST(LibInput::KeyboardLayout::initialize());
MUST(LibInput::KeyboardLayout::get().load_from_file("/usr/share/keymaps/us.keymap"sv));
int keyboard_fd = open("/dev/input0", O_RDONLY);
if (keyboard_fd == -1)
perror("open");
int mouse_fd = open("/dev/input1", O_RDONLY);
if (mouse_fd == -1)
perror("open");
dprintln("Window server started");
for (int i = 0; i < 2; i++)
{
if (fork() == 0)
{
execl("/bin/test-window", "test-window", NULL);
exit(1);
}
}
WindowServer window_server(framebuffer);
for (;;)
{
int max_socket = server_fd;
fd_set fds;
FD_ZERO(&fds);
FD_SET(server_fd, &fds);
if (keyboard_fd != -1)
{
FD_SET(keyboard_fd, &fds);
max_socket = BAN::Math::max(max_socket, keyboard_fd);
}
if (mouse_fd != -1)
{
FD_SET(mouse_fd, &fds);
max_socket = BAN::Math::max(max_socket, mouse_fd);
}
window_server.for_each_window(
[&](int fd, Window&) -> BAN::Iteration
{
FD_SET(fd, &fds);
max_socket = BAN::Math::max(max_socket, fd);
return BAN::Iteration::Continue;
}
);
if (select(max_socket + 1, &fds, nullptr, nullptr, nullptr) == -1)
{
dwarnln("select: {}", strerror(errno));
break;
}
if (FD_ISSET(server_fd, &fds))
{
int window_fd = accept(server_fd, nullptr, nullptr);
if (window_fd == -1)
{
dwarnln("accept: {}", strerror(errno));
continue;
}
auto window = MUST(BAN::RefPtr<Window>::create(window_fd));
window_server.add_window(window_fd, window);
}
if (keyboard_fd != -1 && FD_ISSET(keyboard_fd, &fds))
{
LibInput::RawKeyEvent event;
if (read(keyboard_fd, &event, sizeof(event)) == -1)
{
perror("read");
continue;
}
window_server.on_key_event(LibInput::KeyboardLayout::get().key_event_from_raw(event));
}
if (mouse_fd != -1 && FD_ISSET(mouse_fd, &fds))
{
LibInput::MouseEvent event;
if (read(mouse_fd, &event, sizeof(event)) == -1)
{
perror("read");
continue;
}
switch (event.type)
{
case LibInput::MouseEventType::MouseButtonEvent:
window_server.on_mouse_button(event.button_event);
break;
case LibInput::MouseEventType::MouseMoveEvent:
window_server.on_mouse_move(event.move_event);
break;
case LibInput::MouseEventType::MouseScrollEvent:
window_server.on_mouse_scroll(event.scroll_event);
break;
}
}
window_server.for_each_window(
[&](int fd, Window& window) -> BAN::Iteration
{
if (!FD_ISSET(fd, &fds))
return BAN::Iteration::Continue;
LibGUI::WindowPacket packet;
ssize_t nrecv = recv(fd, &packet, sizeof(packet), 0);
if (nrecv < 0)
dwarnln("recv: {}", strerror(errno));
if (nrecv <= 0)
{
window.mark_deleted();
return BAN::Iteration::Continue;
}
switch (packet.type)
{
case LibGUI::WindowPacketType::CreateWindow:
{
if (nrecv != sizeof(LibGUI::WindowCreatePacket))
{
dwarnln("Invalid WindowCreate packet size");
break;
}
const size_t window_fb_bytes = packet.create.width * packet.create.height * 4;
long smo_key = smo_create(window_fb_bytes, PROT_READ | PROT_WRITE);
if (smo_key == -1)
{
dwarnln("smo_create: {}", strerror(errno));
break;
}
void* smo_address = smo_map(smo_key);
if (smo_address == nullptr)
{
dwarnln("smo_map: {}", strerror(errno));
break;
}
memset(smo_address, 0, window_fb_bytes);
LibGUI::WindowCreateResponse response;
response.framebuffer_smo_key = smo_key;
if (send(fd, &response, sizeof(response), 0) != sizeof(response))
{
dwarnln("send: {}", strerror(errno));
break;
}
window.set_size({
static_cast<int32_t>(packet.create.width),
static_cast<int32_t>(packet.create.height)
}, reinterpret_cast<uint32_t*>(smo_address));
window.set_position({
static_cast<int32_t>(window.width() / 2),
static_cast<int32_t>(window.height() / 2)
});
break;
}
case LibGUI::WindowPacketType::Invalidate:
{
if (nrecv != sizeof(LibGUI::WindowInvalidatePacket))
{
dwarnln("Invalid Invalidate packet size");
break;
}
if (packet.invalidate.x + packet.invalidate.width > window.width() || packet.invalidate.y + packet.invalidate.height > window.height())
{
dwarnln("Invalid Invalidate packet parameters");
break;
}
window_server.invalidate({
window.x() + static_cast<int32_t>(packet.invalidate.x),
window.y() + static_cast<int32_t>(packet.invalidate.y),
static_cast<int32_t>(packet.invalidate.width),
static_cast<int32_t>(packet.invalidate.height),
});
break;
}
default:
dwarnln("Invalid window packet from {}", fd);
}
return BAN::Iteration::Continue;
}
);
}
}

View File

@@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.26)
project(test-window CXX)
set(SOURCES
main.cpp
)
add_executable(test-window ${SOURCES})
target_compile_options(test-window PUBLIC -O2 -g)
target_link_libraries(test-window PUBLIC libc ban libgui)
add_custom_target(test-window-install
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/test-window ${BANAN_BIN}/
DEPENDS test-window
)

View File

@@ -0,0 +1,61 @@
#include <BAN/Debug.h>
#include <LibGUI/Window.h>
#include <stdlib.h>
#include <unistd.h>
void randomize_color(BAN::UniqPtr<LibGUI::Window>& window)
{
uint32_t color = ((rand() % 255) << 16) | ((rand() % 255) << 8) | ((rand() % 255) << 0);
for (uint32_t y = 0; y < window->height(); y++)
for (uint32_t x = 0; x < window->width(); x++)
window->set_pixel(x, y, color);
window->invalidate();
}
int main()
{
timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
srand(ts.tv_nsec);
auto window_or_error = LibGUI::Window::create(300, 200);
if (window_or_error.is_error())
{
dprintln("{}", window_or_error.error());
return 1;
}
auto window = window_or_error.release_value();
window->set_mouse_button_event_callback(
[&](LibGUI::EventPacket::MouseButtonEvent event)
{
if (event.pressed && event.button == LibGUI::EventPacket::MouseButton::Left)
randomize_color(window);
const char* button;
switch (event.button)
{
case LibGUI::EventPacket::MouseButton::Left: button = "left"; break;
case LibGUI::EventPacket::MouseButton::Right: button = "right"; break;
case LibGUI::EventPacket::MouseButton::Middle: button = "middle"; break;
case LibGUI::EventPacket::MouseButton::Extra1: button = "extra1"; break;
case LibGUI::EventPacket::MouseButton::Extra2: button = "extra2"; break;
}
dprintln("mouse button '{}' {} at {}, {}", button, event.pressed ? "pressed" : "released", event.x, event.y);
}
);
randomize_color(window);
for (;;)
{
window->poll_events();
timespec duration;
duration.tv_sec = 0;
duration.tv_nsec = 16'666'666;
nanosleep(&duration, nullptr);
}
}