Add basic MIT-SHM support
Supports only Attach, Detach and PutImage but those seem to be enough for firefox and chromium
This commit is contained in:
parent
545ddefcce
commit
abaa239505
|
|
@ -1,5 +1,6 @@
|
|||
#include "Definitions.h"
|
||||
#include "Extensions.h"
|
||||
#include "Image.h"
|
||||
#include "Keymap.h"
|
||||
#include "Utils.h"
|
||||
|
||||
|
|
@ -331,7 +332,7 @@ static void invalidate_window_recursive(WINDOW wid, int32_t x, int32_t y, int32_
|
|||
);
|
||||
}
|
||||
|
||||
static void invalidate_window(WINDOW wid, int32_t x, int32_t y, int32_t w, int32_t h)
|
||||
void invalidate_window(WINDOW wid, int32_t x, int32_t y, int32_t w, int32_t h)
|
||||
{
|
||||
ASSERT(wid != g_root.windowId);
|
||||
|
||||
|
|
@ -2720,66 +2721,24 @@ BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet)
|
|||
break;
|
||||
}
|
||||
|
||||
uint8_t bpp = 0;
|
||||
for (const auto& format : g_formats)
|
||||
if (format.depth == request.depth)
|
||||
bpp = format.bitsPerPixel;
|
||||
ASSERT(bpp && 32 % bpp == 0);
|
||||
|
||||
uint32_t (*get_pixel)(int32_t x, int32_t y, uint8_t bpp, uint32_t width, const uint32_t* data) = nullptr;
|
||||
|
||||
switch (request.format)
|
||||
{
|
||||
case XYBitmap:
|
||||
ASSERT(request.depth == 1);
|
||||
[[fallthrough]];
|
||||
case ZPixmap:
|
||||
if (bpp == 32)
|
||||
{
|
||||
get_pixel =
|
||||
[](int32_t x, int32_t y, uint8_t bpp, uint32_t width, const uint32_t* data) -> uint32_t
|
||||
{
|
||||
(void)bpp;
|
||||
return data[y * width + x];
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
get_pixel =
|
||||
[](int32_t x, int32_t y, uint8_t bpp, uint32_t width, const uint32_t* data) -> uint32_t
|
||||
{
|
||||
const auto bits_per_scanline = (bpp * width + 31) / 32 * 32;
|
||||
const auto bit_offset = y * bits_per_scanline + x * bpp;
|
||||
const auto dword = bit_offset / 32;
|
||||
const auto shift = bit_offset % 32;
|
||||
const auto mask = (1u << bpp) - 1;
|
||||
return (data[dword] >> shift) & mask;
|
||||
};
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dwarnln("PutImage unsupported format {}, depth {}", request.format, request.depth);
|
||||
break;
|
||||
}
|
||||
|
||||
if (get_pixel != nullptr)
|
||||
{
|
||||
const auto* in_data_u32 = packet.as_span<const uint32_t>().data();
|
||||
auto* out_data_u32 = out_data.as_span<uint32_t>().data();
|
||||
|
||||
const auto min_x = BAN::Math::max<int32_t>(0, -request.dstX);
|
||||
const auto min_y = BAN::Math::max<int32_t>(0, -request.dstY);
|
||||
|
||||
const auto max_x = BAN::Math::min<int32_t>(request.width, out_w - request.dstX);
|
||||
const auto max_y = BAN::Math::min<int32_t>(request.height, out_h - request.dstY);
|
||||
|
||||
for (int32_t y = min_y; y < max_y; y++)
|
||||
{
|
||||
const auto row_off = (request.dstY + y) * out_w;
|
||||
for (int32_t x = min_x; x < max_x; x++)
|
||||
out_data_u32[row_off + (request.dstX + x)] = get_pixel(x, y, bpp, request.width, in_data_u32);
|
||||
}
|
||||
}
|
||||
put_image({
|
||||
.out_data = out_data.data(),
|
||||
.out_x = request.dstX,
|
||||
.out_y = request.dstY,
|
||||
.out_w = out_w,
|
||||
.out_h = out_h,
|
||||
.out_depth = out_depth,
|
||||
.in_data = packet.data(),
|
||||
.in_x = 0,
|
||||
.in_y = 0,
|
||||
.in_w = request.width,
|
||||
.in_h = request.height,
|
||||
.in_depth = request.depth,
|
||||
.w = request.width,
|
||||
.h = request.height,
|
||||
.left_pad = request.leftPad,
|
||||
.format = request.format,
|
||||
});
|
||||
|
||||
if (object.type == Object::Type::Window)
|
||||
invalidate_window(request.drawable, request.dstX, request.dstY, request.width, request.height);
|
||||
|
|
|
|||
|
|
@ -4,3 +4,5 @@
|
|||
|
||||
BAN::ErrorOr<void> setup_client_conneciton(Client& client_info, const xConnClientPrefix& client_prefix);
|
||||
BAN::ErrorOr<void> handle_packet(Client& client_info, BAN::ConstByteSpan packet);
|
||||
|
||||
void invalidate_window(WINDOW wid, int32_t x, int32_t y, int32_t w, int32_t h);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ set(SOURCES
|
|||
Extensions.cpp
|
||||
ExtBigReg.cpp
|
||||
ExtRANDR.cpp
|
||||
ExtSHM.cpp
|
||||
Image.cpp
|
||||
Keymap.cpp
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,283 @@
|
|||
#include "Base.h"
|
||||
#include "Extensions.h"
|
||||
#include "Image.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include <X11/X.h>
|
||||
#include <X11/extensions/shmproto.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct ShmSegment
|
||||
{
|
||||
void* addr;
|
||||
};
|
||||
|
||||
static BAN::HashMap<CARD32, ShmSegment> s_shm_segments;
|
||||
|
||||
static BYTE s_shm_event_base;
|
||||
static BYTE s_shm_error_base;
|
||||
|
||||
static bool is_local_socket(int socket)
|
||||
{
|
||||
sockaddr_storage addr;
|
||||
socklen_t addr_len = sizeof(addr);
|
||||
if (getpeername(socket, reinterpret_cast<sockaddr*>(&addr), &addr_len) == -1)
|
||||
return false;
|
||||
|
||||
switch (addr.ss_family)
|
||||
{
|
||||
case AF_UNIX:
|
||||
return true;
|
||||
case AF_INET:
|
||||
{
|
||||
const auto* addr_in = reinterpret_cast<const sockaddr_in*>(&addr);
|
||||
const auto ipv4 = ntohl(addr_in->sin_addr.s_addr);
|
||||
return (ipv4 & IN_CLASSA_NET) == IN_LOOPBACKNET;
|
||||
}
|
||||
case AF_INET6:
|
||||
{
|
||||
const auto* addr_in6 = reinterpret_cast<const sockaddr_in6*>(&addr);
|
||||
return IN6_IS_ADDR_LOOPBACK(&addr_in6->sin6_addr);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static BAN::ErrorOr<void> extension_shm(Client& client_info, BAN::ConstByteSpan packet)
|
||||
{
|
||||
struct DrawableInfo
|
||||
{
|
||||
BAN::ByteSpan data;
|
||||
CARD32 w, h;
|
||||
CARD8 depth;
|
||||
};
|
||||
|
||||
const uint8_t major_opcode = packet[0];
|
||||
const uint8_t minor_opcode = packet[1];
|
||||
|
||||
const auto get_drawable =
|
||||
[&client_info, minor_opcode, major_opcode](WINDOW drawable) -> BAN::ErrorOr<Object&>
|
||||
{
|
||||
auto it = g_objects.find(drawable);
|
||||
if (it == g_objects.end() || (it->value->type != Object::Type::Window && it->value->type != Object::Type::Pixmap))
|
||||
{
|
||||
xError error {
|
||||
.type = X_Error,
|
||||
.errorCode = BadDrawable,
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.resourceID = drawable,
|
||||
.minorCode = minor_opcode,
|
||||
.majorCode = major_opcode,
|
||||
};
|
||||
TRY(encode(client_info.output_buffer, error));
|
||||
return BAN::Error::from_errno(ENOENT);
|
||||
}
|
||||
|
||||
return *it->value;
|
||||
};
|
||||
|
||||
const auto get_drawable_info =
|
||||
[](Object& object) -> DrawableInfo
|
||||
{
|
||||
DrawableInfo info;
|
||||
|
||||
switch (object.type)
|
||||
{
|
||||
case Object::Type::Window:
|
||||
{
|
||||
auto& window = object.object.get<Object::Window>();
|
||||
auto& texture = window.texture();
|
||||
|
||||
info.data = { reinterpret_cast<uint8_t*>(texture.pixels().data()), texture.pixels().size() * 4 };
|
||||
info.w = texture.width();
|
||||
info.h = texture.height();
|
||||
info.depth = window.depth;
|
||||
|
||||
break;
|
||||
}
|
||||
case Object::Type::Pixmap:
|
||||
{
|
||||
auto& pixmap = object.object.get<Object::Pixmap>();
|
||||
|
||||
info.data = pixmap.data.span();
|
||||
info.w = pixmap.width;
|
||||
info.h = pixmap.height;
|
||||
info.depth = pixmap.depth;
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
return info;
|
||||
};
|
||||
|
||||
switch (packet[1])
|
||||
{
|
||||
case X_ShmQueryVersion:
|
||||
{
|
||||
dprintln("ShmQueryVersion");
|
||||
|
||||
xShmQueryVersionReply reply {
|
||||
.type = X_Reply,
|
||||
.sharedPixmaps = is_local_socket(client_info.fd),
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.length = 0,
|
||||
.majorVersion = 1,
|
||||
.minorVersion = 2,
|
||||
.uid = static_cast<CARD16>(getuid()),
|
||||
.gid = static_cast<CARD16>(getgid()),
|
||||
.pixmapFormat = ZPixmap,
|
||||
};
|
||||
TRY(encode(client_info.output_buffer, reply));
|
||||
|
||||
break;
|
||||
}
|
||||
case X_ShmAttach:
|
||||
{
|
||||
auto request = decode<xShmAttachReq>(packet).value();
|
||||
|
||||
dprintln("ShmAttach");
|
||||
dprintln(" shmseg: {}", request.shmseg);
|
||||
dprintln(" shmid: {}", request.shmid);
|
||||
dprintln(" readOnly: {}", request.readOnly);
|
||||
|
||||
void* addr = shmat(request.shmid, nullptr, request.readOnly ? SHM_RDONLY : 0);
|
||||
ASSERT(addr != (void*)-1);
|
||||
|
||||
TRY(s_shm_segments.insert(request.shmseg, {
|
||||
.addr = addr,
|
||||
}));
|
||||
|
||||
xShmCompletionEvent event {
|
||||
.type = static_cast<BYTE>(s_shm_event_base + ShmCompletion),
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.drawable = 0,
|
||||
.shmseg = request.shmseg,
|
||||
.offset = 0,
|
||||
};
|
||||
TRY(encode(client_info.output_buffer, event));
|
||||
|
||||
break;
|
||||
}
|
||||
case X_ShmDetach:
|
||||
{
|
||||
auto request = decode<xShmDetachReq>(packet).value();
|
||||
|
||||
dprintln("ShmDetach");
|
||||
dprintln(" shmseg: {}", request.shmseg);
|
||||
|
||||
auto it = s_shm_segments.find(request.shmseg);
|
||||
if (it != s_shm_segments.end())
|
||||
{
|
||||
shmdt(it->value.addr);
|
||||
s_shm_segments.remove(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
xError error {
|
||||
.type = X_Error,
|
||||
.errorCode = static_cast<BYTE>(s_shm_error_base + BadShmSeg),
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.resourceID = request.shmseg,
|
||||
.minorCode = minor_opcode,
|
||||
.majorCode = major_opcode,
|
||||
};
|
||||
TRY(encode(client_info.output_buffer, error));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case X_ShmPutImage:
|
||||
{
|
||||
auto request = decode<xShmPutImageReq>(packet).value();
|
||||
|
||||
#if 0
|
||||
dprintln("ShmPutImage");
|
||||
dprintln(" drawable: {}", request.drawable);
|
||||
dprintln(" gc: {}", request.gc);
|
||||
dprintln(" totalWidth: {}", request.totalWidth);
|
||||
dprintln(" totalHeight: {}", request.totalHeight);
|
||||
dprintln(" srcX: {}", request.srcX);
|
||||
dprintln(" srcY: {}", request.srcY);
|
||||
dprintln(" srcWidth: {}", request.srcWidth);
|
||||
dprintln(" srcHeight: {}", request.srcHeight);
|
||||
dprintln(" dstX: {}", request.dstX);
|
||||
dprintln(" dstY: {}", request.dstY);
|
||||
dprintln(" depth: {}", request.depth);
|
||||
dprintln(" format: {}", request.format);
|
||||
dprintln(" sendEvent: {}", request.sendEvent);
|
||||
dprintln(" shmseg: {}", request.shmseg);
|
||||
dprintln(" offset: {}", request.offset);
|
||||
#endif
|
||||
|
||||
auto& shm_segment = s_shm_segments[request.shmseg];
|
||||
|
||||
auto& object = TRY_REF(get_drawable(request.drawable));
|
||||
auto [out_data, out_w, out_h, out_depth] = get_drawable_info(object);
|
||||
|
||||
put_image({
|
||||
.out_data = out_data.data(),
|
||||
.out_x = request.dstX,
|
||||
.out_y = request.dstY,
|
||||
.out_w = out_w,
|
||||
.out_h = out_h,
|
||||
.out_depth = out_depth,
|
||||
.in_data = (const uint8_t*)shm_segment.addr + request.offset,
|
||||
.in_x = request.srcX,
|
||||
.in_y = request.srcY,
|
||||
.in_w = request.totalWidth,
|
||||
.in_h = request.totalHeight,
|
||||
.in_depth = request.depth,
|
||||
.w = request.srcWidth,
|
||||
.h = request.srcHeight,
|
||||
.left_pad = 0,
|
||||
.format = request.format,
|
||||
});
|
||||
|
||||
if (object.type == Object::Type::Window)
|
||||
invalidate_window(request.drawable, request.dstX, request.dstY, request.srcWidth, request.srcHeight);
|
||||
|
||||
if (request.sendEvent)
|
||||
{
|
||||
xShmCompletionEvent event {
|
||||
.type = static_cast<BYTE>(s_shm_event_base + ShmCompletion),
|
||||
.sequenceNumber = client_info.sequence,
|
||||
.drawable = request.drawable,
|
||||
.shmseg = request.shmseg,
|
||||
.offset = request.offset,
|
||||
};
|
||||
TRY(encode(client_info.output_buffer, event));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
dwarnln("unsupported shm minor opcode {}", packet[1]);
|
||||
break;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static struct SHMInstaller
|
||||
{
|
||||
SHMInstaller()
|
||||
{
|
||||
install_extension(SHMNAME, ShmNumberEvents, ShmNumberErrors, extension_shm);
|
||||
for (const auto& extension : g_extensions)
|
||||
{
|
||||
if (extension.name != SHMNAME)
|
||||
continue;
|
||||
s_shm_event_base = extension.event_base;
|
||||
s_shm_error_base = extension.error_base;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} installer;
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
#include "Definitions.h"
|
||||
#include "Image.h"
|
||||
|
||||
#include <X11/X.h>
|
||||
|
||||
void put_image(const PutImageInfo& info)
|
||||
{
|
||||
uint8_t in_bpp = 0;
|
||||
for (const auto& format : g_formats)
|
||||
if (format.depth == info.in_depth)
|
||||
in_bpp = format.bitsPerPixel;
|
||||
ASSERT(in_bpp && 32 % in_bpp == 0);
|
||||
|
||||
const auto min_x = BAN::Math::max<int32_t>(0, -info.out_x);
|
||||
const auto min_y = BAN::Math::max<int32_t>(0, -info.out_y);
|
||||
|
||||
const auto max_x = BAN::Math::min<int32_t>(info.w, info.out_w - info.out_x);
|
||||
const auto max_y = BAN::Math::min<int32_t>(info.h, info.out_h - info.out_y);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
switch (info.format)
|
||||
{
|
||||
case XYBitmap:
|
||||
ASSERT(info.in_depth == 1);
|
||||
[[fallthrough]];
|
||||
case XYPixmap:
|
||||
{
|
||||
const auto dwords_per_plane = (info.left_pad + info.in_w + 31) / 32;
|
||||
for (int32_t y = min_y; y < max_y; y++)
|
||||
{
|
||||
const auto dst_off = (info.out_y + y) * info.out_w + info.out_x;
|
||||
for (int32_t x = min_x; x < max_x; x++)
|
||||
{
|
||||
const auto bit_index = info.left_pad + (info.in_y + y) * info.in_w + (info.in_x + x);
|
||||
const auto dword = bit_index / 32;
|
||||
const auto bit_mask = 1u << (bit_index % 32);
|
||||
uint32_t pixel = 0;
|
||||
for (size_t i = 0; i < info.in_depth; i++)
|
||||
if (in_data_u32[dword + i * dwords_per_plane] & bit_mask)
|
||||
pixel |= 1u << i;
|
||||
out_data_u32[dst_off + x] = pixel;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ZPixmap:
|
||||
{
|
||||
ASSERT(info.left_pad == 0);
|
||||
if (in_bpp == 32)
|
||||
{
|
||||
const auto bytes_per_row = (max_x - min_x) * 4;
|
||||
for (int32_t y = min_y; y < max_y; y++)
|
||||
{
|
||||
const auto dst_off = (info.out_y + y) * info.out_w + (info.out_x + min_x);
|
||||
const auto src_off = (info. in_y + y) * info. in_w + (info. in_x + min_y);
|
||||
memcpy(&out_data_u32[dst_off], &in_data_u32[src_off], bytes_per_row);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto pixel_mask = (1u << info.in_depth) - 1;
|
||||
const auto bits_per_scanline = (info.in_w * in_bpp + 31) / 32 * 32;
|
||||
for (int32_t y = min_y; y < max_y; y++)
|
||||
{
|
||||
const auto dst_off = (info.out_y + y) * info.out_w + info.out_x;
|
||||
for (int32_t x = min_x; x < max_x; x++)
|
||||
{
|
||||
const auto bit_offset = (info.in_y + y) * bits_per_scanline + (info.in_x + x) * in_bpp;
|
||||
const auto dword = bit_offset / 32;
|
||||
const auto shift = bit_offset % 32;
|
||||
out_data_u32[dst_off + x] = (in_data_u32[dword] >> shift) & pixel_mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct PutImageInfo
|
||||
{
|
||||
void* out_data;
|
||||
int32_t out_x;
|
||||
int32_t out_y;
|
||||
uint32_t out_w;
|
||||
uint32_t out_h;
|
||||
uint8_t out_depth;
|
||||
|
||||
const void* in_data;
|
||||
int32_t in_x;
|
||||
int32_t in_y;
|
||||
uint32_t in_w;
|
||||
uint32_t in_h;
|
||||
uint8_t in_depth;
|
||||
|
||||
uint32_t w;
|
||||
uint32_t h;
|
||||
|
||||
uint32_t left_pad;
|
||||
uint8_t format;
|
||||
};
|
||||
|
||||
void put_image(const PutImageInfo& info);
|
||||
Loading…
Reference in New Issue