Implement ShmGetImage

Also SHM extension is now only compiled if the system has shmat and
shmdt
This commit is contained in:
Oskari Alaranta 2026-02-12 18:40:55 +02:00
parent 188f9182ea
commit 632787b142
5 changed files with 155 additions and 52 deletions

View File

@ -2780,63 +2780,36 @@ BAN::ErrorOr<void> 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<uint32_t> 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;
}

View File

@ -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)

View File

@ -258,6 +258,53 @@ static BAN::ErrorOr<void> extension_shm(Client& client_info, BAN::ConstByteSpan
break;
}
case X_ShmGetImage:
{
auto request = decode<xShmGetImageReq>(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;

View File

@ -3,6 +3,16 @@
#include <X11/X.h>
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<uint32_t*>(info.out_data);
const auto* in_data_u32 = static_cast<const uint32_t*>(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<uint32_t*>(info.out_data);
const auto* in_data_u32 = static_cast<const uint32_t*>(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;
}
}
}
}

View File

@ -1,6 +1,7 @@
#pragma once
#include <stdint.h>
#include <stddef.h>
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);