diff --git a/xbanan/CMakeLists.txt b/xbanan/CMakeLists.txt index 90af6bd..07e632d 100644 --- a/xbanan/CMakeLists.txt +++ b/xbanan/CMakeLists.txt @@ -3,6 +3,7 @@ set(SOURCES Base.cpp Extensions.cpp ExtBigReg.cpp + ExtGLX.cpp ExtRANDR.cpp Font.cpp Image.cpp diff --git a/xbanan/ExtGLX.cpp b/xbanan/ExtGLX.cpp new file mode 100644 index 0000000..e8a4424 --- /dev/null +++ b/xbanan/ExtGLX.cpp @@ -0,0 +1,427 @@ +#include "Extensions.h" +#include "Utils.h" + +#include +#include + +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 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 + { + auto it = g_objects.find(context); + if (it != g_objects.end() && it->value->type == Object::Type::Extension) + { + auto& ext = it->value->object.get(); + if (ext.type_major == s_glx_major_opcode && ext.type_minor == GLXBadContext) + return *static_cast(ext.c_private); + } + + xError error { + .type = X_Error, + .errorCode = static_cast(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(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::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(ext.c_private); }, + }, + })) + )); + + break; + } + case X_GLXDestroyContext: + { + auto request = decode(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(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(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(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(client_info.output_buffer, g_visual.visualID)); + TRY(encode(client_info.output_buffer, g_visual.c_class)); + TRY(encode(client_info.output_buffer, xTrue)); // rgba + TRY(encode(client_info.output_buffer, 8)); // red size + TRY(encode(client_info.output_buffer, 8)); // green size + TRY(encode(client_info.output_buffer, 8)); // blue size + TRY(encode(client_info.output_buffer, 8)); // alpha size + TRY(encode(client_info.output_buffer, 0)); // accum red size + TRY(encode(client_info.output_buffer, 0)); // accum green size + TRY(encode(client_info.output_buffer, 0)); // accum blue size + TRY(encode(client_info.output_buffer, 0)); // accum alpha size + TRY(encode(client_info.output_buffer, xTrue)); // double buffer + TRY(encode(client_info.output_buffer, xFalse)); // stereo + TRY(encode(client_info.output_buffer, 32)); // buffer size + TRY(encode(client_info.output_buffer, 24)); // depth size + TRY(encode(client_info.output_buffer, 8)); // stencil size + TRY(encode(client_info.output_buffer, 0)); // aux buffers + TRY(encode(client_info.output_buffer, 0)); // level + + break; + } + case X_GLXQueryServerString: + { + auto request = decode(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((string.size() + 3) / 4), + .n = static_cast(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(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(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(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::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(ext.c_private); }, + }, + })) + )); + + break; + } + case X_GLXGetDrawableAttributes: + { + auto request = decode(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().texture(); + + xGLXGetDrawableAttributesReply reply { + .type = X_Reply, + .sequenceNumber = client_info.sequence, + .length = 2 * 6, + .numAttribs = 6, + }; + TRY(encode(client_info.output_buffer, reply)); + + TRY(encode(client_info.output_buffer, GLX_WIDTH)); + TRY(encode(client_info.output_buffer, texture.width())); + + TRY(encode(client_info.output_buffer, GLX_HEIGHT)); + TRY(encode(client_info.output_buffer, texture.height())); + + TRY(encode(client_info.output_buffer, GLX_PRESERVED_CONTENTS)); + TRY(encode(client_info.output_buffer, xTrue)); + + TRY(encode(client_info.output_buffer, GLX_LARGEST_PBUFFER)); + TRY(encode(client_info.output_buffer, texture.width() * texture.height())); + + TRY(encode(client_info.output_buffer, GLX_FBCONFIG_ID)); + TRY(encode(client_info.output_buffer, 1)); + + TRY(encode(client_info.output_buffer, GLX_EVENT_MASK)); + TRY(encode(client_info.output_buffer, 0)); + + break; + } + case X_GLXCreateWindow: + { + auto request = decode(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(packet).value(); + const auto value = decode(packet).value(); + dprintln(" {} = {}", key, value); + } + + TRY(client_info.objects.insert(request.glxwindow)); + TRY(g_objects.insert( + request.glxwindow, + TRY(BAN::UniqPtr::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;