261 lines
9.3 KiB
C++
261 lines
9.3 KiB
C++
// Copyright (C) 2022 RozK
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
// Adapted from https://www.khronos.org/opengl/wiki/Tutorial:_OpenGL_3.0_Context_Creation_(GLX)
|
|
|
|
#include "../display.hpp"
|
|
#include "display_glx.hpp"
|
|
#include "../events/events_x11.hpp"
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
|
|
static bool rk_error_occured = false;
|
|
|
|
#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
|
|
#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
|
|
typedef GLXContext (*rk_CreateContextAttribsFunc)(Display *, GLXFBConfig, GLXContext, Bool, int const *);
|
|
|
|
static int const rk_context_attribs[] = {
|
|
GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
|
|
GLX_CONTEXT_MINOR_VERSION_ARB, 2,
|
|
GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_ES_PROFILE_BIT_EXT,
|
|
None
|
|
};
|
|
|
|
static void rk_x11_printf(
|
|
char const * messsage) {
|
|
printf("[X11] %s\n", messsage);
|
|
}
|
|
|
|
static void rk_glx_printf(
|
|
char const * messsage) {
|
|
printf("[GLX] %s\n", messsage);
|
|
}
|
|
|
|
bool rk_extension_supported(
|
|
char const * extensions,
|
|
char const * extension) {
|
|
char const * where = strchr(extension, ' ');
|
|
if (where || *extension == '\0') {
|
|
return false;
|
|
}
|
|
for (char const * start = extensions;;) {
|
|
where = strstr(start, extension);
|
|
if (!where) {
|
|
break;
|
|
}
|
|
char const * const terminator = where + strlen(extension);
|
|
if ((where == start || *(where - 1) == ' ') && (*terminator == ' ' || *terminator == '\0')) {
|
|
return true;
|
|
}
|
|
start = terminator;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
rk_func_ptr rk_resolve_extension(
|
|
char const * extensions,
|
|
char const * extension,
|
|
char const * fn) {
|
|
if (!extensions || !extension || !fn) {
|
|
return nullptr;
|
|
}
|
|
if (!rk_extension_supported(extensions, extension)) {
|
|
printf("[GLX] Extension %s not supported\n", extension);
|
|
return nullptr;
|
|
}
|
|
rk_func_ptr const ptr = glXGetProcAddressARB(reinterpret_cast<GLubyte const *>(fn));
|
|
if (!ptr) {
|
|
printf("[GLX] Function %s::%s not found\n", extension, fn);
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
static int rk_error_handler(
|
|
Display * display,
|
|
XErrorEvent * event) {
|
|
rk_error_occured = true;
|
|
return 0;
|
|
}
|
|
|
|
rk_display_t rk_create_display(
|
|
rk_char const * name,
|
|
rk_uint width,
|
|
rk_uint height) {
|
|
if (!name || !width || !height) {
|
|
return nullptr;
|
|
};
|
|
rk_display_glx * const display = new rk_display_glx;
|
|
display->display = XOpenDisplay(nullptr);
|
|
display->window = 0;
|
|
display->colormap = 0;
|
|
display->context = nullptr;
|
|
if (!display->display) {
|
|
rk_x11_printf("Failed to open X display.");
|
|
rk_destroy_display(reinterpret_cast<rk_display_t>(display));
|
|
return nullptr;
|
|
}
|
|
|
|
int glx_major, glx_minor;
|
|
if (!glXQueryVersion(display->display, &glx_major, &glx_minor) ||
|
|
(glx_major == 1 && glx_minor < 3) || glx_major < 1) {
|
|
rk_glx_printf("Invalid GLX version.");
|
|
rk_destroy_display(reinterpret_cast<rk_display_t>(display));
|
|
return nullptr;
|
|
}
|
|
|
|
char const * const glx_exts = glXQueryExtensionsString(display->display, DefaultScreen(display->display));
|
|
// rk_glx_printf(glx_exts);
|
|
rk_CreateContextAttribsFunc const glXCreateContextAttribs = reinterpret_cast<rk_CreateContextAttribsFunc>(
|
|
rk_resolve_extension(glx_exts, "GLX_ARB_create_context", "glXCreateContextAttribsARB"));
|
|
if (!glXCreateContextAttribs) {
|
|
rk_destroy_display(reinterpret_cast<rk_display_t>(display));
|
|
return nullptr;
|
|
}
|
|
|
|
int fb_count = 0;
|
|
GLXFBConfig * fb_configs;
|
|
bool const srgb_ext = rk_extension_supported(glx_exts, "GLX_EXT_framebuffer_sRGB");
|
|
if (srgb_ext) {
|
|
rk_glx_printf("GLX_EXT_framebuffer_sRGB extension supported.");
|
|
int const visual_attribs[] = { GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT, True, GLX_DOUBLEBUFFER, True, None };
|
|
fb_configs = glXChooseFBConfig(display->display, DefaultScreen(display->display), visual_attribs, &fb_count);
|
|
} else {
|
|
int const visual_attribs[] = { GLX_DOUBLEBUFFER, True, None };
|
|
fb_configs = glXChooseFBConfig(display->display, DefaultScreen(display->display), visual_attribs, &fb_count);
|
|
}
|
|
printf("[GLX] Found %d framebuffer configs.\n", fb_count);
|
|
if (!fb_configs || !fb_count) {
|
|
rk_glx_printf("Failed to retrieve framebuffer configs.");
|
|
rk_destroy_display(reinterpret_cast<rk_display_t>(display));
|
|
return nullptr;
|
|
}
|
|
GLXFBConfig fb_config;
|
|
int rs = 0;
|
|
int gs = 0;
|
|
int bs = 0;
|
|
int as = 0;
|
|
int ds = 0;
|
|
int ss = 0;
|
|
for (int fb_index = 0; fb_index < fb_count; ++fb_index) {
|
|
GLXFBConfig _config = fb_configs[fb_index];
|
|
int _rs = 0;
|
|
int _gs = 0;
|
|
int _bs = 0;
|
|
int _as = 0;
|
|
int _ds = 0;
|
|
int _ss = 0;
|
|
glXGetFBConfigAttrib(display->display, _config, GLX_RED_SIZE, &_rs);
|
|
glXGetFBConfigAttrib(display->display, _config, GLX_GREEN_SIZE, &_gs);
|
|
glXGetFBConfigAttrib(display->display, _config, GLX_BLUE_SIZE, &_bs);
|
|
glXGetFBConfigAttrib(display->display, _config, GLX_ALPHA_SIZE, &_as);
|
|
glXGetFBConfigAttrib(display->display, _config, GLX_DEPTH_SIZE, &_ds);
|
|
glXGetFBConfigAttrib(display->display, _config, GLX_STENCIL_SIZE, &_ss);
|
|
if ((_rs >= rs && _gs >= gs && _bs >= bs && _as >= as && _ds >= ds && _ss >= ss) &&
|
|
(_rs > rs || _gs > gs || _bs > bs || _as > as || _ds > ds || _ss > ss)) {
|
|
XVisualInfo * const vi = glXGetVisualFromFBConfig(display->display, _config);
|
|
if (vi) {
|
|
XFree(vi);
|
|
rs = _rs;
|
|
gs = _gs;
|
|
bs = _bs;
|
|
as = _as;
|
|
ds = _ds;
|
|
ss = _ss;
|
|
fb_config = _config;
|
|
}
|
|
}
|
|
}
|
|
printf("[RK] Select framebuffer config R%dG%dB%dA%d D%dS%d.\n", rs, gs, bs, as, ds, ss);
|
|
if (srgb_ext) {
|
|
int srgb = 0;
|
|
glXGetFBConfigAttrib(display->display, fb_config, GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT, &srgb);
|
|
if (srgb) {
|
|
rk_glx_printf("sRGB framebuffer selected.");
|
|
}
|
|
}
|
|
|
|
XVisualInfo * const vi = glXGetVisualFromFBConfig(display->display, fb_config);
|
|
Window root = RootWindow(display->display, vi->screen);
|
|
display->colormap = XCreateColormap(display->display, root, vi->visual, AllocNone);
|
|
XSetWindowAttributes win_attributes;
|
|
win_attributes.colormap = display->colormap;
|
|
win_attributes.event_mask = RK_EVENTS_MASK;
|
|
display->window = XCreateWindow(display->display, root,
|
|
0, 0, width, height, 0, vi->depth, InputOutput, vi->visual, CWColormap | CWEventMask, &win_attributes);
|
|
XFree(vi);
|
|
XFree(fb_configs);
|
|
if (!display->window) {
|
|
rk_x11_printf("Failed to create window.");
|
|
rk_destroy_display(reinterpret_cast<rk_display_t>(display));
|
|
return nullptr;
|
|
}
|
|
XStoreName(display->display, display->window, name);
|
|
XMapWindow(display->display, display->window);
|
|
|
|
rk_error_occured = false;
|
|
int (*oldHandler)(Display *, XErrorEvent *) = XSetErrorHandler(&rk_error_handler);
|
|
display->context = glXCreateContextAttribs(display->display, fb_config, 0, True, rk_context_attribs);
|
|
XSync(display->display, False);
|
|
XSetErrorHandler(oldHandler);
|
|
if (rk_error_occured || !display->context) {
|
|
rk_glx_printf("Failed to create context.");
|
|
rk_destroy_display(reinterpret_cast<rk_display_t>(display));
|
|
return nullptr;
|
|
}
|
|
|
|
if (!glXIsDirect(display->display, display->context)) {
|
|
rk_glx_printf("Warning: Rendering context is indirect.");
|
|
}
|
|
glXMakeCurrent(display->display, display->window, display->context);
|
|
if (srgb_ext) {
|
|
glEnable(GL_FRAMEBUFFER_SRGB_EXT);
|
|
if (glIsEnabled(GL_FRAMEBUFFER_SRGB_EXT)) {
|
|
rk_glx_printf("sRGB framebuffer enabled.");
|
|
}
|
|
}
|
|
|
|
return reinterpret_cast<rk_display_t>(display);
|
|
}
|
|
|
|
void rk_destroy_display(
|
|
rk_display_t _display) {
|
|
rk_display_glx * const display = reinterpret_cast<rk_display_glx *>(_display);
|
|
if (display) {
|
|
if (display->display) {
|
|
glXMakeCurrent(display->display, 0, nullptr);
|
|
if (display->context) {
|
|
glXDestroyContext(display->display, display->context);
|
|
}
|
|
if (display->window) {
|
|
XDestroyWindow(display->display, display->window);
|
|
}
|
|
if (display->colormap) {
|
|
XFreeColormap(display->display, display->colormap);
|
|
}
|
|
XCloseDisplay(display->display);
|
|
}
|
|
delete display;
|
|
}
|
|
}
|
|
|
|
void rk_swap_buffers(
|
|
rk_display_t _display) {
|
|
rk_display_glx const * const display = reinterpret_cast<rk_display_glx const *>(_display);
|
|
if (display && display->display && display->window) {
|
|
glXSwapBuffers(display->display, display->window);
|
|
}
|
|
}
|