Add stuff :)

This commit is contained in:
Oskari Alaranta 2026-02-08 20:03:14 +02:00
parent 971038fd4f
commit 301765a5bd
1 changed files with 154 additions and 28 deletions

View File

@ -1,3 +1,4 @@
#include <BAN/Array.h>
#include <BAN/ByteSpan.h> #include <BAN/ByteSpan.h>
#include <BAN/Endianness.h> #include <BAN/Endianness.h>
#include <BAN/Optional.h> #include <BAN/Optional.h>
@ -317,13 +318,15 @@ xVisualType visual {
struct Client struct Client
{ {
enum class State enum class State : uint8_t
{ {
ConnectionSetup, ConnectionSetup,
Connected, Connected,
}; };
int fd; int fd;
State state; State state;
bool has_epollout { false };
bool has_bigrequests { false };
CARD16 sequence { 0 }; CARD16 sequence { 0 };
BAN::Vector<uint8_t> input_buffer; BAN::Vector<uint8_t> input_buffer;
BAN::Vector<uint8_t> output_buffer; BAN::Vector<uint8_t> output_buffer;
@ -331,6 +334,17 @@ struct Client
BAN::HashMap<CARD32, BAN::UniqPtr<Object>> objects; BAN::HashMap<CARD32, BAN::UniqPtr<Object>> objects;
}; };
struct Extension
{
BAN::StringView name;
uint8_t major_opcode;
uint8_t opcode_count;
BAN::ErrorOr<void> (*handler)(Client&, BAN::ConstByteSpan);
};
uint8_t g_extension_opcode { 128 };
BAN::Array<Extension, 128> g_extensions;
BAN::HashMap<BAN::String, ATOM> g_atoms_name_to_id; BAN::HashMap<BAN::String, ATOM> g_atoms_name_to_id;
BAN::HashMap<ATOM, BAN::String> g_atoms_id_to_name; BAN::HashMap<ATOM, BAN::String> g_atoms_id_to_name;
ATOM atom_value = XA_LAST_PREDEFINED + 1; ATOM atom_value = XA_LAST_PREDEFINED + 1;
@ -634,6 +648,8 @@ void _invalidate_window_recursive(Client& client_info, WINDOW wid, int32_t x, in
{ {
ASSERT(wid != root.windowId); ASSERT(wid != root.windowId);
if (x + w <= 0 || y + h <= 0)
return;
if (w <= 0 || h <= 0) if (w <= 0 || h <= 0)
return; return;
@ -653,16 +669,22 @@ void _invalidate_window_recursive(Client& client_info, WINDOW wid, int32_t x, in
if (!child_window.mapped) if (!child_window.mapped)
continue; continue;
const auto child_x = x - child_window.x;
const auto child_y = y - child_window.y;
const auto child_w = BAN::Math::min<int32_t>(w - child_window.x, child_window.texture().width() - child_x);
const auto child_h = BAN::Math::min<int32_t>(h - child_window.y, child_window.texture().height() - child_y);
_invalidate_window_recursive( _invalidate_window_recursive(
client_info, client_info,
child_wid, child_wid,
x - child_window.x, child_x, child_y,
y - child_window.y, child_w, child_h
w - child_window.x,
h - child_window.y
); );
} }
dwarnln("invalidate {} {},{} -> {},{}", wid, x, y, x + w, y + h);
if (window.window.has<BAN::UniqPtr<LibGUI::Window>>()) if (window.window.has<BAN::UniqPtr<LibGUI::Window>>())
return; return;
@ -672,8 +694,8 @@ void _invalidate_window_recursive(Client& client_info, WINDOW wid, int32_t x, in
auto& parent_window = parent_object.object.get<Object::Window>(); auto& parent_window = parent_object.object.get<Object::Window>();
parent_window.texture().copy_texture( parent_window.texture().copy_texture(
window.texture(), window.texture(),
parent_window.x + x, window.x + x,
parent_window.y + y, window.y + y,
x, x,
y, y,
w, w,
@ -728,7 +750,7 @@ void invalidate_window(Client& client_info, WINDOW wid, int32_t x, int32_t y, in
} }
} }
window.window.get<BAN::UniqPtr<LibGUI::Window>>()->invalidate(x, y, w, h); window.window.get<BAN::UniqPtr<LibGUI::Window>>()->invalidate(min_x, min_y, max_x - min_x, max_y - min_y);
} }
BAN::ErrorOr<void> map_window(Client& client_info, WINDOW wid) BAN::ErrorOr<void> map_window(Client& client_info, WINDOW wid)
@ -753,10 +775,10 @@ BAN::ErrorOr<void> map_window(Client& client_info, WINDOW wid)
auto attributes = gui_window->get_attributes(); auto attributes = gui_window->get_attributes();
attributes.shown = true; attributes.shown = true;
gui_window->set_attributes(attributes); gui_window->set_attributes(attributes);
}
if (is_visible(client_info, window.parent)) gui_window->texture().clear();
TRY(send_visibility_events_recursively(client_info, wid, true)); gui_window->invalidate();
}
if (window.event_mask & StructureNotifyMask) if (window.event_mask & StructureNotifyMask)
{ {
@ -790,6 +812,9 @@ BAN::ErrorOr<void> map_window(Client& client_info, WINDOW wid)
TRY(encode(client_info.output_buffer, event)); TRY(encode(client_info.output_buffer, event));
} }
if (is_visible(client_info, window.parent))
TRY(send_visibility_events_recursively(client_info, wid, true));
if (window.event_mask & ExposureMask) if (window.event_mask & ExposureMask)
{ {
xEvent event = { .u = { xEvent event = { .u = {
@ -994,6 +1019,15 @@ BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
client_info.sequence++; client_info.sequence++;
if (packet[0] >= 128)
{
const auto& extension = g_extensions[packet[0] - 128];
if (extension.handler != nullptr)
return extension.handler(client_info, packet);
dwarnln("invalid opcode {}", packet[0]);
return BAN::Error::from_errno(EINVAL);
}
switch (packet[0]) switch (packet[0])
{ {
case X_CreateWindow: case X_CreateWindow:
@ -1014,7 +1048,7 @@ BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
dprintln(" mask: {8h}", request.mask); dprintln(" mask: {8h}", request.mask);
uint32_t event_mask { 0 }; uint32_t event_mask { 0 };
uint32_t background { 0x000000 }; uint32_t background { 0xFF00FF };
for (size_t i = 0; i < 32; i++) for (size_t i = 0; i < 32; i++)
{ {
@ -1077,7 +1111,7 @@ BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
auto& parent_window = parent_object->object.get<Object::Window>(); auto& parent_window = parent_object->object.get<Object::Window>();
TRY(parent_window.children.push_back(request.wid)); TRY(parent_window.children.push_back(request.wid));
auto it = TRY(client_info.objects.insert( TRY(client_info.objects.insert(
request.wid, request.wid,
TRY(BAN::UniqPtr<Object>::create(Object { TRY(BAN::UniqPtr<Object>::create(Object {
.type = Object::Type::Window, .type = Object::Type::Window,
@ -1637,7 +1671,6 @@ BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
} }
auto& property = it->value; auto& property = it->value;
ASSERT(property.type == request.type);
ASSERT(property.format == request.format); ASSERT(property.format == request.format);
const size_t bytes = (request.format / 8) * request.nUnits; const size_t bytes = (request.format / 8) * request.nUnits;
@ -1650,11 +1683,13 @@ BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
memcpy(property.data.data(), packet.data(), bytes); memcpy(property.data.data(), packet.data(), bytes);
break; break;
case PropModePrepend: case PropModePrepend:
ASSERT(property.type == request.type);
TRY(property.data.resize(old_bytes + bytes)); TRY(property.data.resize(old_bytes + bytes));
memmove(property.data.data() + old_bytes, property.data.data(), old_bytes); memmove(property.data.data() + old_bytes, property.data.data(), old_bytes);
memcpy(property.data.data(), packet.data(), bytes); memcpy(property.data.data(), packet.data(), bytes);
break; break;
case PropModeAppend: case PropModeAppend:
ASSERT(property.type == request.type);
TRY(property.data.resize(old_bytes + bytes)); TRY(property.data.resize(old_bytes + bytes));
memcpy(property.data.data() + old_bytes, packet.data(), bytes); memcpy(property.data.data() + old_bytes, packet.data(), bytes);
break; break;
@ -2223,7 +2258,7 @@ BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
data_u32[y * w + x] = foreground; data_u32[y * w + x] = foreground;
if (object.type == Object::Type::Window) if (object.type == Object::Type::Window)
invalidate_window(client_info, request.drawable, rect.x, rect.y, rect.width, rect.height); invalidate_window(client_info, request.drawable, min_x, min_y, max_x - min_x, max_y - min_y);
} }
break; break;
@ -2269,8 +2304,8 @@ BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
const int32_t min_x = BAN::Math::max<int32_t>(0, arc.x); const int32_t min_x = BAN::Math::max<int32_t>(0, arc.x);
const int32_t min_y = BAN::Math::max<int32_t>(0, arc.y); const int32_t min_y = BAN::Math::max<int32_t>(0, arc.y);
const int32_t max_x = BAN::Math::max<int32_t>(texture.width(), arc.x + arc.width); const int32_t max_x = BAN::Math::min<int32_t>(texture.width(), arc.x + arc.width);
const int32_t max_y = BAN::Math::max<int32_t>(texture.height(), arc.y + arc.height); const int32_t max_y = BAN::Math::min<int32_t>(texture.height(), arc.y + arc.height);
const auto rx = arc.width / 2; const auto rx = arc.width / 2;
const auto ry = arc.height / 2; const auto ry = arc.height / 2;
@ -2490,6 +2525,16 @@ BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
.first_event = 0, .first_event = 0,
.first_error = 0, .first_error = 0,
}; };
for (const auto& extension : g_extensions)
{
if (extension.name != name)
continue;
reply.present = xTrue;
reply.major_opcode = extension.major_opcode;
break;
}
TRY(encode(client_info.output_buffer, reply)); TRY(encode(client_info.output_buffer, reply));
break; break;
@ -2557,6 +2602,50 @@ BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
return {}; return {};
} }
BAN::ErrorOr<void> extension_bigrequests(Client& client_info, BAN::ConstByteSpan packet)
{
switch (packet[1])
{
case 0:
{
xGenericReply reply {
.type = X_Reply,
.sequenceNumber = client_info.sequence,
.length = 0,
.data00 = (16 << 20) / 4, // 16 MiB
};
TRY(encode(client_info.output_buffer, reply));
client_info.has_bigrequests = true;
dprintln("client enabled big requests");
break;
}
default:
dwarnln("invalid BIG-REQUESTS minor opcode {}", packet[1]);
return BAN::Error::from_errno(EINVAL);
}
return {};
}
void install_extensions()
{
const auto install_extension =
[](BAN::StringView name, BAN::ErrorOr<void> (*handler)(Client&, BAN::ConstByteSpan))
{
g_extensions[g_extension_opcode - 128] = {
.name = name,
.major_opcode = g_extension_opcode,
.handler = handler,
};
g_extension_opcode++;
};
install_extension("BIG-REQUESTS"_sv, extension_bigrequests);
}
int main() int main()
{ {
for (int sig = 1; sig < NSIG; sig++) for (int sig = 1; sig < NSIG; sig++)
@ -2694,6 +2783,8 @@ int main()
APPEND_ATOM(XA_WM_TRANSIENT_FOR); APPEND_ATOM(XA_WM_TRANSIENT_FOR);
#undef APPEND_ATOM #undef APPEND_ATOM
install_extensions();
printf("xbanan started\n"); printf("xbanan started\n");
const auto close_client = const auto close_client =
@ -2774,6 +2865,12 @@ int main()
auto& client_info = epoll_thingy.value.get<Client>(); auto& client_info = epoll_thingy.value.get<Client>();
if (events[i].events & EPOLLHUP)
{
close_client(client_info.fd);
continue;
}
if (events[i].events & EPOLLOUT) if (events[i].events & EPOLLOUT)
{ {
const ssize_t nsend = send( const ssize_t nsend = send(
@ -2783,11 +2880,14 @@ int main()
0 0
); );
if (nsend == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
goto send_done;
if (nsend < 0) if (nsend < 0)
perror("xbanan: send"); perror("xbanan: send");
if (nsend <= 0) if (nsend <= 0)
{ {
close_client(events[i].data.fd); close_client(client_info.fd);
continue; continue;
} }
@ -2804,10 +2904,15 @@ int main()
if (epoll_ctl(epoll_fd, EPOLL_CTL_MOD, client_info.fd, &event) == -1) if (epoll_ctl(epoll_fd, EPOLL_CTL_MOD, client_info.fd, &event) == -1)
{ {
perror("xbanan: epoll_ctl"); perror("xbanan: epoll_ctl");
close_client(events[i].data.fd); close_client(client_info.fd);
continue; continue;
} }
client_info.has_epollout = false;
} }
send_done:
(void)0;
} }
if (!(events[i].events & EPOLLIN)) if (!(events[i].events & EPOLLIN))
@ -2818,12 +2923,13 @@ int main()
case Client::State::ConnectionSetup: case Client::State::ConnectionSetup:
{ {
xConnClientPrefix client_prefix; xConnClientPrefix client_prefix;
ssize_t nrecv = recv(client_info.fd, &client_prefix, sizeof(client_prefix), 0);
const ssize_t nrecv = recv(client_info.fd, &client_prefix, sizeof(client_prefix), 0);
if (nrecv < 0) if (nrecv < 0)
perror("xbanan: recv"); perror("xbanan: recv");
if (nrecv <= 0) if (nrecv <= 0)
{ {
close_client(events[i].data.fd); close_client(client_info.fd);
continue; continue;
} }
@ -2832,7 +2938,7 @@ int main()
if (auto ret = setup_client_conneciton(client_info, client_prefix); ret.is_error()) if (auto ret = setup_client_conneciton(client_info, client_prefix); ret.is_error())
{ {
dwarnln("setup_client_connection: {}", ret.error()); dwarnln("setup_client_connection: {}", ret.error());
close_client(events[i].data.fd); close_client(client_info.fd);
continue; continue;
} }
@ -2842,12 +2948,12 @@ int main()
{ {
char buffer[1024] {}; char buffer[1024] {};
ssize_t nrecv = recv(client_info.fd, buffer, sizeof(buffer), 0); const ssize_t nrecv = recv(client_info.fd, buffer, sizeof(buffer), 0);
if (nrecv < 0) if (nrecv < 0)
perror("xbanan: recv"); perror("xbanan: recv");
if (nrecv <= 0) if (nrecv <= 0)
{ {
close_client(events[i].data.fd); close_client(client_info.fd);
continue; continue;
} }
@ -2860,16 +2966,34 @@ int main()
if (client_info.input_buffer.size() < 4) if (client_info.input_buffer.size() < 4)
break; break;
const uint32_t byte_length = 4 * reinterpret_cast<const uint16_t*>(client_info.input_buffer.data())[1]; bool is_big_request = false;
uint32_t byte_length = 4 * *reinterpret_cast<const uint16_t*>(client_info.input_buffer.data() + 2);
if (byte_length == 0 && client_info.has_bigrequests)
{
if (client_info.input_buffer.size() < 8)
break;
byte_length = 4 * *reinterpret_cast<const uint32_t*>(client_info.input_buffer.data() + 4);
is_big_request = true;
}
if (client_info.input_buffer.size() < byte_length) if (client_info.input_buffer.size() < byte_length)
break; break;
auto packet = BAN::ConstByteSpan(client_info.input_buffer.span()).slice(0, byte_length); auto packet = BAN::ConstByteSpan(client_info.input_buffer.span()).slice(0, byte_length);
if (is_big_request)
{
auto* input_u32 = reinterpret_cast<uint32_t*>(client_info.input_buffer.data());
input_u32[1] = input_u32[0];
packet = packet.slice(4);
dprintln("handling big request");
}
if (auto ret = handle_packet(client_info, packet); ret.is_error()) if (auto ret = handle_packet(client_info, packet); ret.is_error())
{ {
dwarnln("handle_packet: {}", ret.error()); dwarnln("handle_packet: {}", ret.error());
close_client(events[i].data.fd); close_client(client_info.fd);
continue; continue;
} }
@ -2889,7 +3013,7 @@ int main()
continue; continue;
auto& client_info = thingy.value.get<Client>(); auto& client_info = thingy.value.get<Client>();
if (client_info.output_buffer.empty()) if (client_info.output_buffer.empty() || client_info.has_epollout)
continue; continue;
epoll_event event { .events = EPOLLIN | EPOLLOUT, .data = { .fd = client_info.fd } }; epoll_event event { .events = EPOLLIN | EPOLLOUT, .data = { .fd = client_info.fd } };
@ -2899,6 +3023,8 @@ int main()
close_client(client_info.fd); close_client(client_info.fd);
goto iterator_invalidated; goto iterator_invalidated;
} }
client_info.has_epollout = true;
} }
} }
} }