Add mostly stubbed out GLX extensions

Surprisingly this lets most GLX apps run as they still render client
side, they just need the extension to exist
This commit is contained in:
Oskari Alaranta 2026-02-21 03:51:04 +02:00
parent 6da9cb7362
commit 7088be0e51
2 changed files with 428 additions and 0 deletions

View File

@ -3,6 +3,7 @@ set(SOURCES
Base.cpp Base.cpp
Extensions.cpp Extensions.cpp
ExtBigReg.cpp ExtBigReg.cpp
ExtGLX.cpp
ExtRANDR.cpp ExtRANDR.cpp
Font.cpp Font.cpp
Image.cpp Image.cpp

427
xbanan/ExtGLX.cpp Normal file
View File

@ -0,0 +1,427 @@
#include "Extensions.h"
#include "Utils.h"
#include <GL/glx.h>
#include <GL/glxproto.h>
using BOOL32 = CARD32;
CARD32 g_fb_configs[2][24][2] {
{
{ GLX_FBCONFIG_ID, 1 },
{ GLX_VISUAL_ID, g_visual.visualID },
{ GLX_BUFFER_SIZE, 32 },
{ GLX_LEVEL, 0 },
{ GLX_DOUBLEBUFFER, True },
{ GLX_STEREO, False },
{ GLX_RENDER_TYPE, GLX_RGBA_BIT },
{ GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT },
{ GLX_X_RENDERABLE, True },
{ GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR },
{ GLX_CONFIG_CAVEAT, GLX_NONE },
{ GLX_TRANSPARENT_TYPE, GLX_NONE },
{ GLX_RED_SIZE, 8 },
{ GLX_GREEN_SIZE, 8 },
{ GLX_BLUE_SIZE, 8 },
{ GLX_ALPHA_SIZE, 8 },
{ GLX_DEPTH_SIZE, 24 },
{ GLX_STENCIL_SIZE, 8 },
{ GLX_ACCUM_RED_SIZE, 0 },
{ GLX_ACCUM_GREEN_SIZE, 0 },
{ GLX_ACCUM_BLUE_SIZE, 0 },
{ GLX_ACCUM_ALPHA_SIZE, 0 },
{ GLX_SAMPLE_BUFFERS, 0 },
{ GLX_SAMPLES, 0 },
},
{
{ GLX_FBCONFIG_ID, 2 },
{ GLX_VISUAL_ID, g_visual.visualID },
{ GLX_BUFFER_SIZE, 32 },
{ GLX_LEVEL, 0 },
{ GLX_DOUBLEBUFFER, False },
{ GLX_STEREO, False },
{ GLX_RENDER_TYPE, GLX_RGBA_BIT },
{ GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT },
{ GLX_X_RENDERABLE, True },
{ GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR },
{ GLX_CONFIG_CAVEAT, GLX_NONE },
{ GLX_TRANSPARENT_TYPE, GLX_NONE },
{ GLX_RED_SIZE, 8 },
{ GLX_GREEN_SIZE, 8 },
{ GLX_BLUE_SIZE, 8 },
{ GLX_ALPHA_SIZE, 8 },
{ GLX_DEPTH_SIZE, 24 },
{ GLX_STENCIL_SIZE, 8 },
{ GLX_ACCUM_RED_SIZE, 0 },
{ GLX_ACCUM_GREEN_SIZE, 0 },
{ GLX_ACCUM_BLUE_SIZE, 0 },
{ GLX_ACCUM_ALPHA_SIZE, 0 },
{ GLX_SAMPLE_BUFFERS, 0 },
{ GLX_SAMPLES, 0 },
},
};
struct MyGLXContext
{
CARD32 fbconfig;
BOOL is_direct;
};
static BYTE s_glx_event_base;
static BYTE s_glx_error_base;
static BYTE s_glx_major_opcode;
BAN::ErrorOr<void> extension_glx(Client& client_info, BAN::ConstByteSpan packet)
{
const uint8_t major_opcode = packet[0];
const uint8_t minor_opcode = packet[1];
const auto get_glx_context =
[&client_info, minor_opcode, major_opcode](CARD32 context) -> BAN::ErrorOr<MyGLXContext&>
{
auto it = g_objects.find(context);
if (it != g_objects.end() && it->value->type == Object::Type::Extension)
{
auto& ext = it->value->object.get<Object::Extension>();
if (ext.type_major == s_glx_major_opcode && ext.type_minor == GLXBadContext)
return *static_cast<MyGLXContext*>(ext.c_private);
}
xError error {
.type = X_Error,
.errorCode = static_cast<BYTE>(s_glx_error_base + GLXBadContext),
.sequenceNumber = client_info.sequence,
.resourceID = context,
.minorCode = minor_opcode,
.majorCode = major_opcode,
};
TRY(encode(client_info.output_buffer, error));
return BAN::Error::from_errno(ENOENT);
};
switch (minor_opcode)
{
case X_GLXCreateContext:
{
auto request = decode<xGLXCreateContextReq>(packet).value();
dprintln("GLXCreateContext");
dprintln(" context: {}", request.context);
dprintln(" visual: {}", request.visual);
dprintln(" screen: {}", request.screen);
dprintln(" shareList: {}", request.shareList);
dprintln(" isDirect: {}", request.isDirect);
auto* object = new MyGLXContext({
.fbconfig = 1,
.is_direct = request.isDirect,
});
ASSERT(object);
TRY(client_info.objects.insert(request.context));
TRY(g_objects.insert(
request.context,
TRY(BAN::UniqPtr<Object>::create(Object {
.type = Object::Type::Extension,
.object = Object::Extension {
.type_major = s_glx_major_opcode,
.type_minor = GLXBadContext,
.c_private = object,
.destructor = [](Object::Extension& ext) { delete static_cast<MyGLXContext*>(ext.c_private); },
},
}))
));
break;
}
case X_GLXDestroyContext:
{
auto request = decode<xGLXDestroyContextReq>(packet).value();
dprintln("GLXDestroyContext");
dprintln(" context: {}", request.context);
delete &TRY_REF(get_glx_context(request.context));
client_info.objects.remove(request.context);
g_objects.remove(request.context);
break;
}
case X_GLXIsDirect:
{
auto request = decode<xGLXIsDirectReq>(packet).value();
dprintln("GLXIsDirect");
dprintln(" context: {}", request.context);
const auto& context = TRY_REF(get_glx_context(request.context));
xGLXIsDirectReply reply {
.type = X_Reply,
.sequenceNumber = client_info.sequence,
.length = 0,
.isDirect = context.is_direct,
};
TRY(encode(client_info.output_buffer, reply));
break;
}
case X_GLXQueryVersion:
{
auto request = decode<xGLXQueryVersionReq>(packet).value();
dprintln("GLXQueryVersion");
dprintln(" majorVersion: {}", request.majorVersion);
dprintln(" minorVersion: {}", request.minorVersion);
xGLXQueryVersionReply reply {
.type = X_Reply,
.sequenceNumber = client_info.sequence,
.length = 0,
.majorVersion = 1,
.minorVersion = 4,
};
TRY(encode(client_info.output_buffer, reply));
break;
}
case X_GLXGetVisualConfigs:
{
auto request = decode<xGLXGetVisualConfigsReq>(packet).value();
dprintln("GLXGetVisualConfigs");
dprintln(" screen: {}", request.screen);
xGLXGetVisualConfigsReply reply {
.type = X_Reply,
.sequenceNumber = client_info.sequence,
.length = 1 * 18,
.numVisuals = 1,
.numProps = 18,
};
TRY(encode(client_info.output_buffer, reply));
TRY(encode<VISUALID>(client_info.output_buffer, g_visual.visualID));
TRY(encode<CARD32>(client_info.output_buffer, g_visual.c_class));
TRY(encode<BOOL32>(client_info.output_buffer, xTrue)); // rgba
TRY(encode<CARD32>(client_info.output_buffer, 8)); // red size
TRY(encode<CARD32>(client_info.output_buffer, 8)); // green size
TRY(encode<CARD32>(client_info.output_buffer, 8)); // blue size
TRY(encode<CARD32>(client_info.output_buffer, 8)); // alpha size
TRY(encode<CARD32>(client_info.output_buffer, 0)); // accum red size
TRY(encode<CARD32>(client_info.output_buffer, 0)); // accum green size
TRY(encode<CARD32>(client_info.output_buffer, 0)); // accum blue size
TRY(encode<CARD32>(client_info.output_buffer, 0)); // accum alpha size
TRY(encode<CARD32>(client_info.output_buffer, xTrue)); // double buffer
TRY(encode<BOOL32>(client_info.output_buffer, xFalse)); // stereo
TRY(encode<CARD32>(client_info.output_buffer, 32)); // buffer size
TRY(encode<CARD32>(client_info.output_buffer, 24)); // depth size
TRY(encode<CARD32>(client_info.output_buffer, 8)); // stencil size
TRY(encode<CARD32>(client_info.output_buffer, 0)); // aux buffers
TRY(encode<INT32>(client_info.output_buffer, 0)); // level
break;
}
case X_GLXQueryServerString:
{
auto request = decode<xGLXQueryServerStringReq>(packet).value();
dprintln("GLXQueryServerString");
dprintln(" screen: {}", request.screen);
dprintln(" name: {}", request.name);
BAN::StringView string;
switch (request.name)
{
case GLX_VENDOR:
string = "XBANAN";
break;
case GLX_VERSION:
string = "1.4";
break;
case GLX_EXTENSIONS:
string = "";
break;
default:
ASSERT_NOT_REACHED();
}
xGLXQueryServerStringReply reply {
.type = X_Reply,
.sequenceNumber = client_info.sequence,
.length = static_cast<CARD32>((string.size() + 3) / 4),
.n = static_cast<CARD16>(string.size()),
};
TRY(encode(client_info.output_buffer, reply));
TRY(encode(client_info.output_buffer, string));
for (size_t i = 0; (string.size() + i) % 4; i++)
TRY(encode(client_info.output_buffer, '\0'));
break;
}
case X_GLXClientInfo:
{
auto request = decode<xGLXClientInfoReq>(packet).value();
dprintln("GLXClientInfo");
dprintln(" major: {}", request.major);
dprintln(" minor: {}", request.minor);
dprintln(" extension: {}", BAN::StringView((char*)packet.data(), request.numbytes));
break;
}
case X_GLXGetFBConfigs:
{
auto request = decode<xGLXGetFBConfigsReq>(packet).value();
dprintln("GLXGetFBConfigs");
dprintln(" screen: {}", request.screen);
constexpr size_t fbconfigs = sizeof(g_fb_configs) / sizeof(*g_fb_configs);
constexpr size_t attribs = sizeof(*g_fb_configs) / sizeof(**g_fb_configs);
xGLXGetFBConfigsReply reply {
.type = X_Reply,
.sequenceNumber = client_info.sequence,
.length = 2 * fbconfigs * attribs,
.numFBConfigs = fbconfigs,
.numAttribs = attribs,
};
TRY(encode(client_info.output_buffer, reply));
TRY(encode(client_info.output_buffer, g_fb_configs));
break;
}
case X_GLXCreateNewContext:
{
auto request = decode<xGLXCreateNewContextReq>(packet).value();
dprintln("GLXCreateNewContext");
dprintln(" context: {}", request.context);
dprintln(" fbconfig: {}", request.fbconfig);
dprintln(" screen: {}", request.screen);
dprintln(" renderType: {}", request.renderType);
dprintln(" shareList: {}", request.shareList);
dprintln(" isDirect: {}", request.isDirect);
auto* object = new MyGLXContext({
.fbconfig = request.fbconfig,
.is_direct = request.isDirect,
});
ASSERT(object);
TRY(client_info.objects.insert(request.context));
TRY(g_objects.insert(
request.context,
TRY(BAN::UniqPtr<Object>::create(Object {
.type = Object::Type::Extension,
.object = Object::Extension {
.type_major = s_glx_major_opcode,
.type_minor = GLXBadContext,
.c_private = object,
.destructor = [](Object::Extension& ext) { delete static_cast<MyGLXContext*>(ext.c_private); },
},
}))
));
break;
}
case X_GLXGetDrawableAttributes:
{
auto request = decode<xGLXGetDrawableAttributesReq>(packet).value();
dprintln("GLXGetDrawableAttributes");
dprintln(" drawable: {}", request.drawable);
const auto& object = g_objects[request.drawable];
ASSERT(object->type == Object::Type::Window);
const auto& texture = object->object.get<Object::Window>().texture();
xGLXGetDrawableAttributesReply reply {
.type = X_Reply,
.sequenceNumber = client_info.sequence,
.length = 2 * 6,
.numAttribs = 6,
};
TRY(encode(client_info.output_buffer, reply));
TRY(encode<CARD32>(client_info.output_buffer, GLX_WIDTH));
TRY(encode<CARD32>(client_info.output_buffer, texture.width()));
TRY(encode<CARD32>(client_info.output_buffer, GLX_HEIGHT));
TRY(encode<CARD32>(client_info.output_buffer, texture.height()));
TRY(encode<CARD32>(client_info.output_buffer, GLX_PRESERVED_CONTENTS));
TRY(encode<CARD32>(client_info.output_buffer, xTrue));
TRY(encode<CARD32>(client_info.output_buffer, GLX_LARGEST_PBUFFER));
TRY(encode<CARD32>(client_info.output_buffer, texture.width() * texture.height()));
TRY(encode<CARD32>(client_info.output_buffer, GLX_FBCONFIG_ID));
TRY(encode<CARD32>(client_info.output_buffer, 1));
TRY(encode<CARD32>(client_info.output_buffer, GLX_EVENT_MASK));
TRY(encode<CARD32>(client_info.output_buffer, 0));
break;
}
case X_GLXCreateWindow:
{
auto request = decode<xGLXCreateWindowReq>(packet).value();
dprintln("GLXCreateWindow");
dprintln(" screen: {}", request.screen);
dprintln(" fbconfig: {}", request.fbconfig);
dprintln(" window: {}", request.window);
dprintln(" glxwindow: {}", request.glxwindow);
dprintln(" numAttribs: {}", request.numAttribs);
for (size_t i = 0; i < request.numAttribs; i++)
{
const auto key = decode<CARD32>(packet).value();
const auto value = decode<CARD32>(packet).value();
dprintln(" {} = {}", key, value);
}
TRY(client_info.objects.insert(request.glxwindow));
TRY(g_objects.insert(
request.glxwindow,
TRY(BAN::UniqPtr<Object>::create(Object {
.type = Object::Type::Extension,
.object = Object::Extension {
.type_major = s_glx_major_opcode,
.type_minor = GLXBadWindow,
.c_private = nullptr,
.destructor = [](Object::Extension&) { },
},
}))
));
break;
}
default:
dwarnln("unsupported glx minor opcode {}", packet[1]);
break;
}
return {};
}
static struct GLXInstaller
{
GLXInstaller()
{
install_extension(GLX_EXTENSION_NAME, __GLX_NUMBER_EVENTS, __GLX_NUMBER_ERRORS, extension_glx);
for (const auto& extension : g_extensions)
{
if (extension.name != GLX_EXTENSION_NAME)
continue;
s_glx_event_base = extension.event_base;
s_glx_error_base = extension.error_base;
s_glx_major_opcode = extension.major_opcode;
break;
}
}
} installer;