From 632787b142f2e3e72c6cf89b3170aadbefcdbf37 Mon Sep 17 00:00:00 2001 From: Oskari Alaranta Date: Thu, 12 Feb 2026 18:40:55 +0200 Subject: [PATCH] Implement ShmGetImage Also SHM extension is now only compiled if the system has shmat and shmdt --- xbanan/Base.cpp | 65 +++++++++++++------------------------------ xbanan/CMakeLists.txt | 9 +++++- xbanan/ExtSHM.cpp | 47 +++++++++++++++++++++++++++++++ xbanan/Image.cpp | 65 +++++++++++++++++++++++++++++++++++++++---- xbanan/Image.h | 21 ++++++++++++++ 5 files changed, 155 insertions(+), 52 deletions(-) diff --git a/xbanan/Base.cpp b/xbanan/Base.cpp index 4a32d4e..21cc4e4 100644 --- a/xbanan/Base.cpp +++ b/xbanan/Base.cpp @@ -2780,63 +2780,36 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) dprintln(" planeMask: {}", request.planeMask); auto& object = *g_objects[request.drawable]; - auto [out_data, out_w, out_h, out_depth] = get_drawable_info(object); + auto [in_data, in_w, in_h, in_depth] = get_drawable_info(object); - uint8_t bpp = 0; - for (const auto& format : g_formats) - if (format.depth == out_depth) - bpp = format.bitsPerPixel; - ASSERT(bpp && 32 % bpp == 0); + const auto dwords = image_dwords(request.width, request.height, in_depth); - ASSERT(request.x >= 0 && request.y >= 0); - ASSERT(request.x + request.width <= out_w); - ASSERT(request.y + request.height <= out_h); - - const CARD32 length = (request.width * bpp + 31) / 32 * request.height; xGetImageReply reply { .type = X_Reply, - .depth = out_depth, + .depth = in_depth, .sequenceNumber = client_info.sequence, - .length = length, + .length = dwords, .visual = g_visual.visualID, }; TRY(encode(client_info.output_buffer, reply)); - if (request.format != ZPixmap) - dwarnln("GetImage with format {}", request.format); + const auto old_size = client_info.output_buffer.size(); + TRY(client_info.output_buffer.resize(old_size + dwords * 4)); - if (bpp == 32) - { - for (int32_t y = 0; y < request.height; y++) - { - const size_t index = (request.y + y) * out_w + request.x; - const auto slice = out_data.slice(index * 4, request.width * 4); - TRY(encode(client_info.output_buffer, slice)); - } - } - else - { - BAN::Vector scanline; - TRY(scanline.resize((request.width * bpp + 31) / 32)); + auto* out_data = client_info.output_buffer.data() + old_size; - for (int32_t y = 0; y < request.height; y++) - { - for (auto& dword : scanline) - dword = 0; - - const auto row_off = (request.y + y) * out_w; - for (int32_t x = 0; x < request.width; x++) - { - const auto bit_offset = x * bpp; - const auto dword = bit_offset / 32; - const auto shift = bit_offset % 32; - const auto mask = (1u << bpp) - 1; - scanline[dword] |= (out_data[row_off + (request.x + x)] & mask) << shift; - } - - TRY(encode(client_info.output_buffer, scanline)); - } - } + get_image({ + .out_data = out_data, + .in_data = in_data.data(), + .in_x = request.x, + .in_y = request.y, + .in_w = in_w, + .in_h = in_h, + .w = request.width, + .h = request.height, + .depth = in_depth, + .format = request.format, + }); break; } diff --git a/xbanan/CMakeLists.txt b/xbanan/CMakeLists.txt index 1398213..3af10bc 100644 --- a/xbanan/CMakeLists.txt +++ b/xbanan/CMakeLists.txt @@ -4,11 +4,18 @@ set(SOURCES Extensions.cpp ExtBigReg.cpp ExtRANDR.cpp - ExtSHM.cpp Image.cpp Keymap.cpp ) +include(CheckSymbolExists) + +check_symbol_exists(shmat "sys/shm.h" HAVE_SHMAT) +check_symbol_exists(shmdt "sys/shm.h" HAVE_SHMDT) +if(HAVE_SHMAT AND HAVE_SHMDT) + set(SOURCES ${SOURCES} ExtSHM.cpp) +endif() + add_executable(xbanan ${SOURCES}) banan_link_library(xbanan ban) banan_link_library(xbanan libgui) diff --git a/xbanan/ExtSHM.cpp b/xbanan/ExtSHM.cpp index cb6a414..c8683d9 100644 --- a/xbanan/ExtSHM.cpp +++ b/xbanan/ExtSHM.cpp @@ -258,6 +258,53 @@ static BAN::ErrorOr extension_shm(Client& client_info, BAN::ConstByteSpan break; } + case X_ShmGetImage: + { + auto request = decode(packet).value(); + + dprintln("ShmGetImage"); + dprintln(" drawable: {}", request.drawable); + dprintln(" x: {}", request.x); + dprintln(" y: {}", request.y); + dprintln(" wigth: {}", request.width); + dprintln(" height: {}", request.height); + dprintln(" planeMask: {}", request.planeMask); + dprintln(" format: {}", request.format); + dprintln(" shmseg: {}", request.shmseg); + dprintln(" offset: {}", request.offset); + + auto& shm_segment = s_shm_segments[request.shmseg]; + + auto& object = TRY_REF(get_drawable(request.drawable)); + auto [in_data, in_w, in_h, in_depth] = get_drawable_info(object); + + const auto dwords = image_dwords(request.width, request.height, in_depth); + + get_image({ + .out_data = (uint8_t*)shm_segment.addr + request.offset, + .in_data = in_data.data(), + .in_x = request.x, + .in_y = request.y, + .in_w = in_w, + .in_h = in_h, + .w = request.width, + .h = request.height, + .depth = in_depth, + .format = request.format, + }); + + xShmGetImageReply reply { + .type = X_Reply, + .depth = in_depth, + .sequenceNumber = client_info.sequence, + .length = 0, + .visual = g_visual.visualID, + .size = dwords * 4, + }; + TRY(encode(client_info.output_buffer, reply)); + + break; + } default: dwarnln("unsupported shm minor opcode {}", packet[1]); break; diff --git a/xbanan/Image.cpp b/xbanan/Image.cpp index 5cd39de..c161d2f 100644 --- a/xbanan/Image.cpp +++ b/xbanan/Image.cpp @@ -3,6 +3,16 @@ #include +uint32_t image_dwords(uint32_t width, uint32_t height, uint8_t depth) +{ + uint8_t bpp = 0; + for (const auto& format : g_formats) + if (format.depth == depth) + bpp = format.bitsPerPixel; + ASSERT(bpp && 32 % bpp == 0); + return (width * bpp + 31) / 32 * height; +} + void put_image(const PutImageInfo& info) { uint8_t in_bpp = 0; @@ -20,11 +30,7 @@ void put_image(const PutImageInfo& info) auto* out_data_u32 = static_cast(info.out_data); const auto* in_data_u32 = static_cast(info.in_data); - if (!(info.format == XYBitmap || info.in_depth == info.out_depth)) - { - dwarnln("format {}, in depth {}, out depth {}", info.format, info.in_depth, info.out_depth); - ASSERT_NOT_REACHED(); - } + ASSERT(info.format == XYBitmap || info.in_depth == info.out_depth); switch (info.format) { @@ -86,3 +92,52 @@ void put_image(const PutImageInfo& info) ASSERT_NOT_REACHED(); } } + +void get_image(const GetImageInfo& info) +{ + uint8_t out_bpp = 0; + for (const auto& format : g_formats) + if (format.depth == info.depth) + out_bpp = format.bitsPerPixel; + ASSERT(out_bpp && 32 % out_bpp == 0); + + ASSERT(info.in_x >= 0 && info.in_y >= 0); + ASSERT(info.in_x + info.w <= info.in_w); + ASSERT(info.in_y + info.h <= info.in_h); + + if (info.format != XYBitmap && info.format != ZPixmap) + dwarnln("GetImage with format {}", info.format); + + auto* out_data_u32 = static_cast(info.out_data); + const auto* in_data_u32 = static_cast(info.in_data); + + if (out_bpp == 32) + { + for (int32_t y = 0; y < info.h; y++) + { + const size_t out_off = y * info.w; + const size_t in_off = (info.in_y + y) * info.in_w + info.in_x; + memcpy(&out_data_u32[out_off], &in_data_u32[in_off], info.w * 4); + } + } + else + { + const auto dwords_per_scanline = (info.w * out_bpp + 31) / 32; + + memset(out_data_u32, 0, dwords_per_scanline * info.h * 4); + + for (int32_t y = 0; y < info.h; y++) + { + const auto in_row_off = (info.in_y + y) * info.in_w + info.in_x; + for (int32_t x = 0; x < info.w; x++) + { + const auto bit_offset = x * out_bpp; + const auto dword = bit_offset / 32; + const auto shift = bit_offset % 32; + const auto mask = (1u << out_bpp) - 1; + out_data_u32[dword] |= (in_data_u32[in_row_off + x] & mask) << shift; + } + } + } + +} diff --git a/xbanan/Image.h b/xbanan/Image.h index 58e44c6..a974556 100644 --- a/xbanan/Image.h +++ b/xbanan/Image.h @@ -1,6 +1,7 @@ #pragma once #include +#include struct PutImageInfo { @@ -25,4 +26,24 @@ struct PutImageInfo uint8_t format; }; +struct GetImageInfo +{ + void* out_data; + + const void* in_data; + int32_t in_x; + int32_t in_y; + uint32_t in_w; + uint32_t in_h; + + // out matches these dimensions + uint32_t w; + uint32_t h; + + uint8_t depth; + uint8_t format; +}; + +uint32_t image_dwords(uint32_t width, uint32_t height, uint8_t depth); void put_image(const PutImageInfo& info); +void get_image(const GetImageInfo& info);