BuildSystem: Cleanup userspace directory layout
userspace programs are now in userspace/programs userspace tests are now in userspace/tests This makes listing userspace projects much cleaner. Libraries were already separated to their own directory, so other programs should also.
This commit is contained in:
16
userspace/programs/WindowServer/CMakeLists.txt
Normal file
16
userspace/programs/WindowServer/CMakeLists.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
set(SOURCES
|
||||
main.cpp
|
||||
Framebuffer.cpp
|
||||
Window.cpp
|
||||
WindowServer.cpp
|
||||
)
|
||||
|
||||
add_executable(WindowServer ${SOURCES})
|
||||
banan_include_headers(WindowServer ban)
|
||||
banan_include_headers(WindowServer libgui)
|
||||
banan_link_library(WindowServer libc)
|
||||
banan_link_library(WindowServer libfont)
|
||||
banan_link_library(WindowServer libimage)
|
||||
banan_link_library(WindowServer libinput)
|
||||
|
||||
install(TARGETS WindowServer OPTIONAL)
|
||||
35
userspace/programs/WindowServer/Cursor.h
Normal file
35
userspace/programs/WindowServer/Cursor.h
Normal 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$`";
|
||||
45
userspace/programs/WindowServer/Framebuffer.cpp
Normal file
45
userspace/programs/WindowServer/Framebuffer.cpp
Normal 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;
|
||||
}
|
||||
16
userspace/programs/WindowServer/Framebuffer.h
Normal file
16
userspace/programs/WindowServer/Framebuffer.h
Normal 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();
|
||||
80
userspace/programs/WindowServer/Utils.h
Normal file
80
userspace/programs/WindowServer/Utils.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#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,
|
||||
};
|
||||
}
|
||||
|
||||
bool operator==(const Rectangle& other) const
|
||||
{
|
||||
return x == other.x && y == other.y && width == other.width && height == other.height;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct Circle
|
||||
{
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
int32_t radius;
|
||||
|
||||
bool contains(Position position) const
|
||||
{
|
||||
int32_t dx = position.x - x;
|
||||
int32_t dy = position.y - y;
|
||||
return dx * dx + dy * dy <= radius * radius;
|
||||
}
|
||||
|
||||
};
|
||||
72
userspace/programs/WindowServer/Window.cpp
Normal file
72
userspace/programs/WindowServer/Window.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include "Window.h"
|
||||
|
||||
#include <BAN/Debug.h>
|
||||
|
||||
#include <LibGUI/Window.h>
|
||||
|
||||
#include <sys/banan-os.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
Window::Window(int fd, Rectangle area, long smo_key, BAN::StringView title, const LibFont::Font& font)
|
||||
: m_client_fd(fd)
|
||||
, m_client_area(area)
|
||||
, m_smo_key(smo_key)
|
||||
{
|
||||
MUST(m_title.append(title));
|
||||
prepare_title_bar(font);
|
||||
|
||||
m_fb_addr = static_cast<uint32_t*>(smo_map(smo_key));
|
||||
ASSERT(m_fb_addr);
|
||||
memset(m_fb_addr, 0, client_width() * client_height() * 4);
|
||||
}
|
||||
|
||||
Window::~Window()
|
||||
{
|
||||
munmap(m_fb_addr, client_width() * client_height() * 4);
|
||||
smo_delete(m_smo_key);
|
||||
|
||||
LibGUI::EventPacket event;
|
||||
event.type = LibGUI::EventPacket::Type::DestroyWindow;
|
||||
send(m_client_fd, &event, sizeof(event), 0);
|
||||
close(m_client_fd);
|
||||
}
|
||||
|
||||
void Window::prepare_title_bar(const LibFont::Font& font)
|
||||
{
|
||||
const size_t title_bar_bytes = title_bar_width() * title_bar_height() * 4;
|
||||
uint32_t* title_bar_data = new uint32_t[title_bar_bytes];
|
||||
ASSERT(title_bar_data);
|
||||
for (size_t i = 0; i < title_bar_bytes; i++)
|
||||
title_bar_data[i] = 0xFFFFFF;
|
||||
|
||||
const auto text_area = title_text_area();
|
||||
|
||||
for (size_t i = 0; i < m_title.size() && (i + 1) * font.width() < static_cast<uint32_t>(text_area.width); i++)
|
||||
{
|
||||
const auto* glyph = font.glyph(m_title[i]);
|
||||
if (glyph == nullptr)
|
||||
continue;
|
||||
|
||||
const int32_t y_off = (font.height() < (uint32_t)title_bar_height()) ? (title_bar_height() - font.height()) / 2 : 0;
|
||||
const int32_t x_off = y_off + i * font.width();
|
||||
for (int32_t y = 0; (uint32_t)y < font.height(); y++)
|
||||
{
|
||||
if (y + y_off >= title_bar_height())
|
||||
break;
|
||||
for (int32_t x = 0; (uint32_t)x < font.width(); x++)
|
||||
{
|
||||
if (x + x_off >= text_area.width)
|
||||
break;
|
||||
const uint8_t bitmask = 1 << (font.width() - x - 1);
|
||||
if (glyph[y * font.pitch()] & bitmask)
|
||||
title_bar_data[(y_off + y) * title_bar_width() + (x_off + x)] = 0x000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_title_bar_data)
|
||||
delete[] m_title_bar_data;
|
||||
m_title_bar_data = title_bar_data;
|
||||
}
|
||||
76
userspace/programs/WindowServer/Window.h
Normal file
76
userspace/programs/WindowServer/Window.h
Normal file
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utils.h"
|
||||
|
||||
#include <BAN/RefPtr.h>
|
||||
#include <BAN/String.h>
|
||||
|
||||
#include <LibFont/Font.h>
|
||||
|
||||
class Window : public BAN::RefCounted<Window>
|
||||
{
|
||||
public:
|
||||
Window(int fd, Rectangle area, long smo_key, BAN::StringView title, const LibFont::Font& font);
|
||||
~Window();
|
||||
|
||||
void set_position(Position position)
|
||||
{
|
||||
m_client_area.x = position.x;
|
||||
m_client_area.y = position.y;
|
||||
}
|
||||
|
||||
int client_fd() const { return m_client_fd; }
|
||||
|
||||
int32_t client_x() const { return m_client_area.x; }
|
||||
int32_t client_y() const { return m_client_area.y; }
|
||||
int32_t client_width() const { return m_client_area.width; }
|
||||
int32_t client_height() const { return m_client_area.height; }
|
||||
Rectangle client_size() const { return { 0, 0, client_width(), client_height() }; }
|
||||
Rectangle client_area() const { return m_client_area; }
|
||||
|
||||
int32_t title_bar_x() const { return client_x(); }
|
||||
int32_t title_bar_y() const { return client_y() - title_bar_height(); }
|
||||
int32_t title_bar_width() const { return client_width(); }
|
||||
int32_t title_bar_height() const { return m_title_bar_height; }
|
||||
Rectangle title_bar_size() const { return { 0, 0, title_bar_width(), title_bar_height() }; }
|
||||
Rectangle title_bar_area() const { return { title_bar_x(), title_bar_y(), title_bar_width(), title_bar_height() }; }
|
||||
|
||||
int32_t full_x() const { return title_bar_x(); }
|
||||
int32_t full_y() const { return title_bar_y(); }
|
||||
int32_t full_width() const { return client_width(); }
|
||||
int32_t full_height() const { return client_height() + title_bar_height(); }
|
||||
Rectangle full_size() const { return { 0, 0, full_width(), full_height() }; }
|
||||
Rectangle full_area() const { return { full_x(), full_y(), full_width(), full_height() }; }
|
||||
|
||||
const uint32_t* framebuffer() const { return m_fb_addr; }
|
||||
|
||||
uint32_t title_bar_pixel(int32_t abs_x, int32_t abs_y, Position cursor) const
|
||||
{
|
||||
ASSERT(title_bar_area().contains({ abs_x, abs_y }));
|
||||
|
||||
if (auto close_button = close_button_area(); close_button.contains({ abs_x, abs_y }))
|
||||
return close_button.contains(cursor) ? 0xFF0000 : 0xD00000;
|
||||
|
||||
int32_t rel_x = abs_x - title_bar_x();
|
||||
int32_t rel_y = abs_y - title_bar_y();
|
||||
return m_title_bar_data[rel_y * title_bar_width() + rel_x];
|
||||
}
|
||||
|
||||
Circle close_button_area() const { return { title_bar_x() + title_bar_width() - title_bar_height() / 2, title_bar_y() + title_bar_height() / 2, title_bar_height() * 3 / 8 }; }
|
||||
Rectangle title_text_area() const { return { title_bar_x(), title_bar_y(), title_bar_width() - title_bar_height(), title_bar_height() }; }
|
||||
|
||||
private:
|
||||
void prepare_title_bar(const LibFont::Font& font);
|
||||
|
||||
private:
|
||||
static constexpr int32_t m_title_bar_height { 20 };
|
||||
|
||||
const int m_client_fd { -1 };
|
||||
Rectangle m_client_area { 0, 0, 0, 0 };
|
||||
long m_smo_key { 0 };
|
||||
uint32_t* m_fb_addr { nullptr };
|
||||
uint32_t* m_title_bar_data { nullptr };
|
||||
BAN::String m_title;
|
||||
|
||||
friend class BAN::RefPtr<Window>;
|
||||
};
|
||||
475
userspace/programs/WindowServer/WindowServer.cpp
Normal file
475
userspace/programs/WindowServer/WindowServer.cpp
Normal file
@@ -0,0 +1,475 @@
|
||||
#include "Cursor.h"
|
||||
#include "WindowServer.h"
|
||||
|
||||
#include <BAN/Debug.h>
|
||||
|
||||
#include <LibGUI/Window.h>
|
||||
#include <LibInput/KeyboardLayout.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/banan-os.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
WindowServer::WindowServer(Framebuffer& framebuffer)
|
||||
: m_framebuffer(framebuffer)
|
||||
, m_cursor({ framebuffer.width / 2, framebuffer.height / 2 })
|
||||
, m_font(MUST(LibFont::Font::load("/usr/share/fonts/lat0-16.psfu"_sv)))
|
||||
{
|
||||
MUST(m_pages_to_sync_bitmap.resize(BAN::Math::div_round_up<size_t>(m_framebuffer.width * m_framebuffer.height * sizeof(uint32_t), 4096 * 8), 0));
|
||||
invalidate(m_framebuffer.area());
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> WindowServer::set_background_image(BAN::UniqPtr<LibImage::Image> image)
|
||||
{
|
||||
if (image->width() != (uint64_t)m_framebuffer.width || image->height() != (uint64_t)m_framebuffer.height)
|
||||
image = TRY(image->resize(m_framebuffer.width, m_framebuffer.height));
|
||||
m_background_image = BAN::move(image);
|
||||
invalidate(m_framebuffer.area());
|
||||
return {};
|
||||
}
|
||||
|
||||
void WindowServer::on_window_packet(int fd, LibGUI::WindowPacket packet)
|
||||
{
|
||||
switch (packet.type)
|
||||
{
|
||||
case LibGUI::WindowPacketType::CreateWindow:
|
||||
{
|
||||
// FIXME: This should be probably allowed
|
||||
for (auto& window : m_client_windows)
|
||||
{
|
||||
if (window->client_fd() == fd)
|
||||
{
|
||||
dwarnln("client {} tried to create window while already owning a window", fd);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Rectangle window_area {
|
||||
static_cast<int32_t>((m_framebuffer.width - packet.create.width) / 2),
|
||||
static_cast<int32_t>((m_framebuffer.height - packet.create.height) / 2),
|
||||
static_cast<int32_t>(packet.create.width),
|
||||
static_cast<int32_t>(packet.create.height)
|
||||
};
|
||||
|
||||
packet.create.title[sizeof(packet.create.title) - 1] = '\0';
|
||||
|
||||
// Window::Window(int fd, Rectangle area, long smo_key, BAN::StringView title, const LibFont::Font& font)
|
||||
auto window = MUST(BAN::RefPtr<Window>::create(
|
||||
fd,
|
||||
window_area,
|
||||
smo_key,
|
||||
packet.create.title,
|
||||
m_font
|
||||
));
|
||||
MUST(m_client_windows.push_back(window));
|
||||
set_focused_window(window);
|
||||
|
||||
LibGUI::WindowCreateResponse response;
|
||||
response.framebuffer_smo_key = smo_key;
|
||||
if (send(window->client_fd(), &response, sizeof(response), 0) != sizeof(response))
|
||||
{
|
||||
dwarnln("send: {}", strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case LibGUI::WindowPacketType::Invalidate:
|
||||
{
|
||||
if (packet.invalidate.width == 0 || packet.invalidate.height == 0)
|
||||
break;
|
||||
|
||||
BAN::RefPtr<Window> target_window;
|
||||
for (auto& window : m_client_windows)
|
||||
{
|
||||
if (window->client_fd() == fd)
|
||||
{
|
||||
target_window = window;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!target_window)
|
||||
{
|
||||
dwarnln("client {} tried to invalidate window while not owning a window", fd);
|
||||
break;
|
||||
}
|
||||
|
||||
const int32_t br_x = packet.invalidate.x + packet.invalidate.width - 1;
|
||||
const int32_t br_y = packet.invalidate.y + packet.invalidate.height - 1;
|
||||
if (!target_window->client_size().contains({ br_x, br_y }))
|
||||
{
|
||||
dwarnln("Invalid Invalidate packet parameters");
|
||||
break;
|
||||
}
|
||||
|
||||
invalidate({
|
||||
target_window->client_x() + static_cast<int32_t>(packet.invalidate.x),
|
||||
target_window->client_y() + static_cast<int32_t>(packet.invalidate.y),
|
||||
static_cast<int32_t>(packet.invalidate.width),
|
||||
static_cast<int32_t>(packet.invalidate.height),
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
m_is_stopped = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Kill window with mod+Q
|
||||
if (m_is_mod_key_held && event.pressed() && event.key == LibInput::Key::Q)
|
||||
{
|
||||
if (m_focused_window)
|
||||
remove_client_fd(m_focused_window->client_fd());
|
||||
return;
|
||||
}
|
||||
|
||||
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_client_windows.size(); i > 0; i--)
|
||||
{
|
||||
if (m_client_windows[i - 1]->full_area().contains(m_cursor))
|
||||
{
|
||||
target_window = m_client_windows[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 or mouse press on title bar
|
||||
const bool can_start_move = m_is_mod_key_held || target_window->title_text_area().contains(m_cursor);
|
||||
if (event.pressed && event.button == LibInput::MouseButton::Left && !m_is_moving_window && can_start_move)
|
||||
m_is_moving_window = true;
|
||||
else if (m_is_moving_window && !event.pressed)
|
||||
m_is_moving_window = false;
|
||||
else if (!event.pressed && event.button == LibInput::MouseButton::Left && target_window->close_button_area().contains(m_cursor))
|
||||
{
|
||||
// NOTE: we always have target window if code reaches here
|
||||
LibGUI::EventPacket packet;
|
||||
packet.type = LibGUI::EventPacket::Type::CloseWindow;
|
||||
send(m_focused_window->client_fd(), &packet, sizeof(packet), 0);
|
||||
}
|
||||
else if (target_window->client_area().contains(m_cursor))
|
||||
{
|
||||
// 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->client_x();
|
||||
packet.mouse_button_event.y = m_cursor.y - m_focused_window->client_y();
|
||||
send(m_focused_window->client_fd(), &packet, sizeof(packet), 0);
|
||||
}
|
||||
}
|
||||
|
||||
void WindowServer::on_mouse_move(LibInput::MouseMoveEvent event)
|
||||
{
|
||||
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;
|
||||
|
||||
auto old_cursor = cursor_area();
|
||||
m_cursor.x = new_x;
|
||||
m_cursor.y = new_y;
|
||||
auto new_cursor = cursor_area();
|
||||
|
||||
invalidate(old_cursor);
|
||||
invalidate(new_cursor);
|
||||
|
||||
// TODO: Really no need to loop over every window
|
||||
for (auto& window : m_client_windows)
|
||||
{
|
||||
auto title_bar = window->title_bar_area();
|
||||
if (title_bar.get_overlap(old_cursor).has_value() || title_bar.get_overlap(new_cursor).has_value())
|
||||
invalidate(title_bar);
|
||||
}
|
||||
|
||||
if (m_is_moving_window)
|
||||
{
|
||||
auto old_window = m_focused_window->full_area();
|
||||
m_focused_window->set_position({
|
||||
m_focused_window->client_x() + event.rel_x,
|
||||
m_focused_window->client_y() + event.rel_y,
|
||||
});
|
||||
auto new_window = m_focused_window->full_area();
|
||||
invalidate(old_window);
|
||||
invalidate(new_window);
|
||||
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->client_x();
|
||||
packet.mouse_move_event.y = m_cursor.y - m_focused_window->client_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_client_windows.size(); i > 0; i--)
|
||||
{
|
||||
if (m_client_windows[i - 1] == window)
|
||||
{
|
||||
m_focused_window = window;
|
||||
m_client_windows.remove(i - 1);
|
||||
MUST(m_client_windows.push_back(window));
|
||||
invalidate(window->full_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();
|
||||
|
||||
if (m_background_image)
|
||||
{
|
||||
ASSERT(m_background_image->width() == (uint64_t)m_framebuffer.width);
|
||||
ASSERT(m_background_image->height() == (uint64_t)m_framebuffer.height);
|
||||
for (int32_t y = area.y; y < area.y + area.height; y++)
|
||||
for (int32_t x = area.x; x < area.x + area.width; x++)
|
||||
m_framebuffer.mmap[y * m_framebuffer.width + x] = m_background_image->get_color(x, y).as_rgba();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int32_t y = area.y; y < area.y + area.height; y++)
|
||||
memset(&m_framebuffer.mmap[y * m_framebuffer.width + area.x], 0x10, area.width * 4);
|
||||
}
|
||||
|
||||
for (auto& pwindow : m_client_windows)
|
||||
{
|
||||
auto& window = *pwindow;
|
||||
|
||||
// window title bar
|
||||
if (auto overlap = window.title_bar_area().get_overlap(area); overlap.has_value())
|
||||
{
|
||||
for (int32_t y_off = 0; y_off < overlap->height; y_off++)
|
||||
{
|
||||
for (int32_t x_off = 0; x_off < overlap->width; x_off++)
|
||||
{
|
||||
uint32_t pixel = window.title_bar_pixel(
|
||||
overlap->x + x_off,
|
||||
overlap->y + y_off,
|
||||
m_cursor
|
||||
);
|
||||
m_framebuffer.mmap[(overlap->y + y_off) * m_framebuffer.width + overlap->x + x_off] = pixel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// window client area
|
||||
if (auto overlap = window.client_area().get_overlap(area); overlap.has_value())
|
||||
{
|
||||
const int32_t src_x = overlap->x - window.client_x();
|
||||
const int32_t src_y = overlap->y - window.client_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.client_width() + src_x],
|
||||
overlap->width * 4
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto cursor = cursor_area();
|
||||
if (auto overlap = cursor.get_overlap(area); overlap.has_value())
|
||||
{
|
||||
for (int32_t y_off = 0; y_off < overlap->height; y_off++)
|
||||
{
|
||||
for (int32_t x_off = 0; x_off < overlap->width; x_off++)
|
||||
{
|
||||
const int32_t rel_x = overlap->x - m_cursor.x + x_off;
|
||||
const int32_t rel_y = overlap->y - m_cursor.y + y_off;
|
||||
const uint32_t offset = (rel_y * s_cursor_width + rel_x) * 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 + y_off) * m_framebuffer.width + (overlap->x + x_off)] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const uintptr_t mmap_start = reinterpret_cast<uintptr_t>(m_framebuffer.mmap) + area.y * m_framebuffer.width * 4;
|
||||
const uintptr_t mmap_end = mmap_start + (area.height + 1) * m_framebuffer.width * 4;
|
||||
|
||||
uintptr_t mmap_addr = mmap_start & ~(uintptr_t)0xFFF;
|
||||
while (mmap_addr < mmap_end)
|
||||
{
|
||||
size_t index = (mmap_addr - reinterpret_cast<uintptr_t>(m_framebuffer.mmap)) / 4096;
|
||||
size_t byte = index / 8;
|
||||
size_t bit = index % 8;
|
||||
m_pages_to_sync_bitmap[byte] |= 1 << bit;
|
||||
mmap_addr += 4096;
|
||||
}
|
||||
}
|
||||
|
||||
void WindowServer::sync()
|
||||
{
|
||||
size_t synced_pages = 0;
|
||||
|
||||
for (size_t i = 0; i < m_pages_to_sync_bitmap.size() * 8; i++)
|
||||
{
|
||||
size_t byte = i / 8;
|
||||
size_t bit = i % 8;
|
||||
if (!(m_pages_to_sync_bitmap[byte] & (1 << bit)))
|
||||
continue;
|
||||
|
||||
size_t len = 1;
|
||||
while (i + len < m_pages_to_sync_bitmap.size() * 8)
|
||||
{
|
||||
size_t byte = (i + len) / 8;
|
||||
size_t bit = (i + len) % 8;
|
||||
if (!(m_pages_to_sync_bitmap[byte] & (1 << bit)))
|
||||
break;
|
||||
len++;
|
||||
}
|
||||
|
||||
msync(
|
||||
reinterpret_cast<uint8_t*>(m_framebuffer.mmap) + i * 4096,
|
||||
len * 4096,
|
||||
MS_SYNC
|
||||
);
|
||||
synced_pages += len;
|
||||
|
||||
i += len;
|
||||
}
|
||||
|
||||
memset(m_pages_to_sync_bitmap.data(), 0, m_pages_to_sync_bitmap.size());
|
||||
}
|
||||
|
||||
Rectangle WindowServer::cursor_area() const
|
||||
{
|
||||
return { m_cursor.x, m_cursor.y, s_cursor_width, s_cursor_height };
|
||||
}
|
||||
|
||||
|
||||
void WindowServer::add_client_fd(int fd)
|
||||
{
|
||||
MUST(m_client_fds.push_back(fd));
|
||||
}
|
||||
|
||||
void WindowServer::remove_client_fd(int fd)
|
||||
{
|
||||
for (size_t i = 0; i < m_client_fds.size(); i++)
|
||||
{
|
||||
if (m_client_fds[i] == fd)
|
||||
{
|
||||
m_client_fds.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_client_windows.size(); i++)
|
||||
{
|
||||
auto window = m_client_windows[i];
|
||||
if (window->client_fd() == fd)
|
||||
{
|
||||
auto window_area = window->full_area();
|
||||
m_client_windows.remove(i);
|
||||
invalidate(window_area);
|
||||
|
||||
if (window == m_focused_window)
|
||||
{
|
||||
m_focused_window = nullptr;
|
||||
if (!m_client_windows.empty())
|
||||
set_focused_window(m_client_windows.back());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_deleted_window = true;
|
||||
}
|
||||
|
||||
int WindowServer::get_client_fds(fd_set& fds) const
|
||||
{
|
||||
int max_fd = 0;
|
||||
for (int fd : m_client_fds)
|
||||
{
|
||||
FD_SET(fd, &fds);
|
||||
max_fd = BAN::Math::max(max_fd, fd);
|
||||
}
|
||||
return max_fd;
|
||||
}
|
||||
|
||||
void WindowServer::for_each_client_fd(const BAN::Function<BAN::Iteration(int)>& callback)
|
||||
{
|
||||
m_deleted_window = false;
|
||||
for (int fd : m_client_fds)
|
||||
{
|
||||
if (m_deleted_window)
|
||||
break;
|
||||
callback(fd);
|
||||
}
|
||||
}
|
||||
64
userspace/programs/WindowServer/WindowServer.h
Normal file
64
userspace/programs/WindowServer/WindowServer.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#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 <LibFont/Font.h>
|
||||
#include <LibGUI/Window.h>
|
||||
#include <LibImage/Image.h>
|
||||
#include <LibInput/KeyEvent.h>
|
||||
#include <LibInput/MouseEvent.h>
|
||||
|
||||
#include <sys/select.h>
|
||||
|
||||
class WindowServer
|
||||
{
|
||||
public:
|
||||
WindowServer(Framebuffer& framebuffer);
|
||||
|
||||
BAN::ErrorOr<void> set_background_image(BAN::UniqPtr<LibImage::Image>);
|
||||
|
||||
void on_window_packet(int fd, LibGUI::WindowPacket);
|
||||
|
||||
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);
|
||||
void sync();
|
||||
|
||||
Rectangle cursor_area() const;
|
||||
|
||||
void add_client_fd(int fd);
|
||||
void remove_client_fd(int fd);
|
||||
int get_client_fds(fd_set& fds) const;
|
||||
void for_each_client_fd(const BAN::Function<BAN::Iteration(int)>& callback);
|
||||
|
||||
bool is_stopped() const { return m_is_stopped; }
|
||||
|
||||
private:
|
||||
Framebuffer& m_framebuffer;
|
||||
BAN::Vector<BAN::RefPtr<Window>> m_client_windows;
|
||||
BAN::Vector<int> m_client_fds;
|
||||
|
||||
BAN::Vector<uint8_t> m_pages_to_sync_bitmap;
|
||||
|
||||
BAN::UniqPtr<LibImage::Image> m_background_image;
|
||||
|
||||
bool m_is_mod_key_held { false };
|
||||
bool m_is_moving_window { false };
|
||||
BAN::RefPtr<Window> m_focused_window;
|
||||
Position m_cursor;
|
||||
|
||||
bool m_deleted_window { false };
|
||||
bool m_is_stopped { false };
|
||||
|
||||
LibFont::Font m_font;
|
||||
};
|
||||
299
userspace/programs/WindowServer/main.cpp
Normal file
299
userspace/programs/WindowServer/main.cpp
Normal file
@@ -0,0 +1,299 @@
|
||||
#include "WindowServer.h"
|
||||
|
||||
#include <BAN/Debug.h>
|
||||
#include <BAN/ScopeGuard.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>
|
||||
|
||||
struct Config
|
||||
{
|
||||
BAN::UniqPtr<LibImage::Image> background_image;
|
||||
};
|
||||
|
||||
BAN::Optional<BAN::String> file_read_line(FILE* file)
|
||||
{
|
||||
BAN::String line;
|
||||
|
||||
char buffer[128];
|
||||
while (fgets(buffer, sizeof(buffer), file))
|
||||
{
|
||||
MUST(line.append(buffer));
|
||||
if (line.back() == '\n')
|
||||
{
|
||||
line.pop_back();
|
||||
return BAN::move(line);
|
||||
}
|
||||
}
|
||||
|
||||
if (line.empty())
|
||||
return {};
|
||||
return BAN::move(line);
|
||||
}
|
||||
|
||||
Config parse_config()
|
||||
{
|
||||
Config config;
|
||||
|
||||
auto home_env = getenv("HOME");
|
||||
if (!home_env)
|
||||
{
|
||||
dprintln("HOME environment variable not set");
|
||||
return config;
|
||||
}
|
||||
|
||||
auto config_path = MUST(BAN::String::formatted("{}/.config/WindowServer.conf", home_env));
|
||||
FILE* fconfig = fopen(config_path.data(), "r");
|
||||
if (!fconfig)
|
||||
{
|
||||
dprintln("Could not open '{}'", config_path);
|
||||
return config;
|
||||
}
|
||||
|
||||
BAN::ScopeGuard _([fconfig] { fclose(fconfig); });
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto line = file_read_line(fconfig);
|
||||
if (!line.has_value())
|
||||
break;
|
||||
if (line->empty())
|
||||
continue;
|
||||
|
||||
auto parts = MUST(line->sv().split('='));
|
||||
if (parts.size() != 2)
|
||||
{
|
||||
dwarnln("Invalid config line: {}", line.value());
|
||||
break;
|
||||
}
|
||||
|
||||
auto variable = parts[0];
|
||||
auto value = parts[1];
|
||||
|
||||
if (variable == "bg"_sv)
|
||||
{
|
||||
auto image = LibImage::Image::load_from_file(value);
|
||||
if (image.is_error())
|
||||
dwarnln("Could not load image: {}", image.error());
|
||||
else
|
||||
config.background_image = image.release_value();
|
||||
}
|
||||
else
|
||||
{
|
||||
dwarnln("Unknown config variable: {}", variable);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
size_t window_packet_sizes[LibGUI::WindowPacketType::COUNT] {};
|
||||
window_packet_sizes[LibGUI::WindowPacketType::INVALID] = 0;
|
||||
window_packet_sizes[LibGUI::WindowPacketType::CreateWindow] = sizeof(LibGUI::WindowCreatePacket);
|
||||
window_packet_sizes[LibGUI::WindowPacketType::Invalidate] = sizeof(LibGUI::WindowInvalidatePacket);
|
||||
static_assert(LibGUI::WindowPacketType::COUNT == 3);
|
||||
|
||||
auto config = parse_config();
|
||||
|
||||
WindowServer window_server(framebuffer);
|
||||
if (config.background_image)
|
||||
if (auto ret = window_server.set_background_image(BAN::move(config.background_image)); ret.is_error())
|
||||
dwarnln("Could not set background image: {}", ret.error());
|
||||
|
||||
constexpr uint64_t sync_interval_us = 1'000'000 / 60;
|
||||
timespec last_sync { .tv_sec = 0, .tv_nsec = 0 };
|
||||
while (!window_server.is_stopped())
|
||||
{
|
||||
timespec current_ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, ¤t_ts);
|
||||
|
||||
uint64_t us_since_last_sync = (current_ts.tv_sec - last_sync.tv_sec) * 1'000'000 + (current_ts.tv_nsec - last_sync.tv_nsec) / 1000;
|
||||
if (us_since_last_sync > sync_interval_us)
|
||||
{
|
||||
window_server.sync();
|
||||
us_since_last_sync = 0;
|
||||
last_sync = current_ts;
|
||||
}
|
||||
|
||||
int max_fd = server_fd;
|
||||
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(server_fd, &fds);
|
||||
if (keyboard_fd != -1)
|
||||
{
|
||||
FD_SET(keyboard_fd, &fds);
|
||||
max_fd = BAN::Math::max(max_fd, keyboard_fd);
|
||||
}
|
||||
if (mouse_fd != -1)
|
||||
{
|
||||
FD_SET(mouse_fd, &fds);
|
||||
max_fd = BAN::Math::max(max_fd, mouse_fd);
|
||||
}
|
||||
max_fd = BAN::Math::max(max_fd, window_server.get_client_fds(fds));
|
||||
|
||||
timeval select_timeout {
|
||||
.tv_sec = 0,
|
||||
.tv_usec = static_cast<long>(sync_interval_us - us_since_last_sync)
|
||||
};
|
||||
int nselect = select(max_fd + 1, &fds, nullptr, nullptr, &select_timeout);
|
||||
if (nselect == 0)
|
||||
continue;
|
||||
if (nselect == -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;
|
||||
}
|
||||
window_server.add_client_fd(window_fd);
|
||||
}
|
||||
|
||||
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_client_fd(
|
||||
[&](int fd) -> 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_server.remove_client_fd(fd);
|
||||
return BAN::Iteration::Continue;
|
||||
}
|
||||
|
||||
if (packet.type == LibGUI::WindowPacketType::INVALID || packet.type >= LibGUI::WindowPacketType::COUNT)
|
||||
dwarnln("Invalid WindowPacket (type {})", (int)packet.type);
|
||||
if (static_cast<size_t>(nrecv) != window_packet_sizes[packet.type])
|
||||
dwarnln("Invalid WindowPacket size (type {}, size {})", (int)packet.type, nrecv);
|
||||
else
|
||||
window_server.on_window_packet(fd, packet);
|
||||
return BAN::Iteration::Continue;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user