From 421e0a68976928b7103e3f8ccae21e29bf6c91c6 Mon Sep 17 00:00:00 2001 From: Oskari Alaranta Date: Thu, 4 Jun 2026 01:29:32 +0300 Subject: [PATCH] Add very basic X-Resource extension This only supports XResQueryVersion and XResQueryClientIds but it is enough to get steam running --- xbanan/CMakeLists.txt | 1 + xbanan/ExtXRes.cpp | 119 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 xbanan/ExtXRes.cpp diff --git a/xbanan/CMakeLists.txt b/xbanan/CMakeLists.txt index 2032618..b4491af 100644 --- a/xbanan/CMakeLists.txt +++ b/xbanan/CMakeLists.txt @@ -6,6 +6,7 @@ set(XBANAN_SOURCES Extensions.cpp ExtBigReg.cpp ExtRANDR.cpp + ExtXRes.cpp Font.cpp Image.cpp Keymap.cpp diff --git a/xbanan/ExtXRes.cpp b/xbanan/ExtXRes.cpp new file mode 100644 index 0000000..2390952 --- /dev/null +++ b/xbanan/ExtXRes.cpp @@ -0,0 +1,119 @@ +#include "Extensions.h" +#include "Utils.h" + +#include +#include + +template +static BAN::ErrorOr for_each_client(uint32_t target_spec, const F& callback) +{ + for (auto [fd, thingy] : g_epoll_thingies) + { + if (thingy.type != EpollThingy::Type::Client) + continue; + + Client& client_info = thingy.value.get(); + if (target_spec && (target_spec >> 20) != client_info.fd) + continue; + + TRY(callback(client_info, target_spec ? target_spec : (client_info.fd << 20))); + } + return {}; +} + +BAN::ErrorOr extension_xres(Client& client_info, BAN::ConstByteSpan packet) +{ + const uint8_t major_opcode = packet[0]; + const uint8_t minor_opcode = packet[1]; + + switch (minor_opcode) + { + case X_XResQueryVersion: + { + auto request = decode(packet).value(); + + dprintln("XResQueryVersion"); + dprintln(" clientMajor: {}", reqType.client_major); + dprintln(" clientMinor: {}", reqType.client_minor); + + xXResQueryVersionReply reply { + .type = X_Reply, + .sequenceNumber = client_info.sequence, + .length = 0, + .server_major = 1, + .server_minor = 2, + }; + TRY(encode(client_info.output_buffer, reply)); + + break; + } + case X_XResQueryClientIds: + { + auto request = decode(packet).value(); + + dprintln("XResQueryClientIds"); + dprintln(" numSpecs: {}", request.numSpecs); + + uint32_t num_ids { 0 }; + BAN::Vector query_result; + + for (size_t i = 0; i < request.numSpecs; i++) + { + auto spec = decode(packet).value(); + + TRY(for_each_client(spec.client, [&](Client& client_info, uint32_t client_spec) -> BAN::ErrorOr { + if (spec.mask == None || (spec.mask & X_XResClientXIDMask)) + { + xXResClientIdValue value { + .spec = { + .client = client_spec, + .mask = X_XResClientXIDMask, + }, + .length = 0, + }; + TRY(encode(query_result, value)); + num_ids++; + } + if ((spec.mask == None || (spec.mask & X_XResLocalClientPIDMask)) && client_info.pid.has_value()) + { + xXResClientIdValue value { + .spec = { + .client = client_spec, + .mask = X_XResLocalClientPIDMask, + }, + .length = 4, + }; + TRY(encode(query_result, value)); + TRY(encode(query_result, client_info.pid.value())); + num_ids++; + } + return {}; + })); + } + + xXResQueryClientIdsReply reply { + .type = X_Reply, + .sequenceNumber = client_info.sequence, + .length = static_cast(query_result.size() / 4), + .numIds = num_ids, + }; + TRY(encode(client_info.output_buffer, reply)); + TRY(encode(client_info.output_buffer, query_result)); + + break; + } + default: + dwarnln("unsupported opcode XRes minor opcode {}", minor_opcode); + break; + } + + return {}; +} + +static struct XResInstaller +{ + XResInstaller() + { + install_extension(XRES_NAME, 6, 0, extension_xres); + } +} installer;