Split display from render and rework init/terminate.
This commit is contained in:
parent
0c560890a4
commit
026ead0b33
4
Makefile
4
Makefile
@ -14,9 +14,9 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
SOURCES = \
|
||||
cpp/opengl/render_context_glx.cpp \
|
||||
cpp/opengl/render_opengles.cpp \
|
||||
cpp/display/display_glx.cpp \
|
||||
cpp/events/events_x11.cpp \
|
||||
cpp/render/render_opengles.cpp \
|
||||
cpp/math.cpp
|
||||
|
||||
OUTPUTFILE = engine.so
|
||||
|
172
__init__.py
172
__init__.py
@ -19,6 +19,31 @@ from pathlib import Path
|
||||
|
||||
_engine = ctypes.cdll.LoadLibrary(Path(__file__).parent / "engine.so")
|
||||
|
||||
# types.hpp
|
||||
|
||||
def _flag(x):
|
||||
return 1 << x
|
||||
|
||||
def buffer(type, size):
|
||||
return (type * size)()
|
||||
|
||||
def _voidp(x):
|
||||
return x.buffer_info()[0]
|
||||
|
||||
def _ubytep(x):
|
||||
assert x.typecode == 'B'
|
||||
return x.buffer_info()[0]
|
||||
|
||||
def _ushortp(x):
|
||||
assert x.typecode == 'H'
|
||||
return x.buffer_info()[0]
|
||||
|
||||
def _floatp(x):
|
||||
assert x.typecode == 'f'
|
||||
return x.buffer_info()[0]
|
||||
|
||||
# math.hpp
|
||||
|
||||
class vec3(ctypes.Structure):
|
||||
_fields_ = ('x', ctypes.c_float), ('y', ctypes.c_float), ('z', ctypes.c_float)
|
||||
|
||||
@ -42,71 +67,14 @@ class mat3(ctypes.Structure):
|
||||
class mat4(ctypes.Structure):
|
||||
_fields_ = ('x', vec4), ('y', vec4), ('z', vec4), ('w', vec4)
|
||||
|
||||
vec3_right = vec3(1.0, 0.0, 0.0)
|
||||
vec3_forward = vec3(0.0, 1.0, 0.0)
|
||||
vec3_up = vec3(0.0, 0.0, 1.0)
|
||||
|
||||
def _flag(x):
|
||||
return 1 << x
|
||||
|
||||
TEXTURE_FORMAT_SRGB8_A8 = 0
|
||||
TEXTURE_FORMAT_RGBA8 = 1
|
||||
TEXTURE_FORMAT_RGB10_A2 = 2
|
||||
TEXTURE_FORMAT_32F = 3
|
||||
|
||||
TEXTURE_FORMAT_TYPECODE = ('B', 'B', 'I', 'f')
|
||||
TEXTURE_FORMAT_NELEMS = (4, 4, 1, 1)
|
||||
|
||||
TEXTURE_FLAG_3D = _flag(0)
|
||||
TEXTURE_FLAG_MIPMAPS = _flag(1)
|
||||
TEXTURE_FLAG_MIN_NEAREST = 0
|
||||
TEXTURE_FLAG_MIN_LINEAR = _flag(2)
|
||||
TEXTURE_FLAG_MAG_NEAREST = 0
|
||||
TEXTURE_FLAG_MAG_LINEAR = _flag(3)
|
||||
|
||||
VERTEX_FORMAT_VEC3_FLOAT = 1
|
||||
VERTEX_FORMAT_VEC3_INT10 = 2
|
||||
VERTEX_FORMAT_VEC3_UINT10 = 3
|
||||
VERTEX_FORMAT_NORMALIZE = _flag(7)
|
||||
|
||||
def vertex_format(*format):
|
||||
return array('B', format).tobytes()
|
||||
|
||||
PARAM_FORMAT_VEC3_FLOAT = 1
|
||||
PARAM_FORMAT_VEC3_SHORT = 2
|
||||
PARAM_FORMAT_VEC3_INT10 = 3
|
||||
PARAM_FORMAT_NORMALIZE = _flag(7)
|
||||
|
||||
def params_format(*format):
|
||||
return array('B', format).tobytes()
|
||||
|
||||
INSTANCE_FLAG_SPAWNED = _flag(0)
|
||||
INSTANCE_FLAG_VISIBLE = _flag(1)
|
||||
|
||||
BATCH_MAX_SIZE = 65536
|
||||
|
||||
def buffer(type, size):
|
||||
return (type * size)()
|
||||
|
||||
_vec3p = ctypes.POINTER(vec3)
|
||||
_vec4p = ctypes.POINTER(vec4)
|
||||
_mat3p = ctypes.POINTER(mat3)
|
||||
_mat4p = ctypes.POINTER(mat4)
|
||||
|
||||
def _voidp(x):
|
||||
return x.buffer_info()[0]
|
||||
|
||||
def _ubytep(x):
|
||||
assert x.typecode == 'B'
|
||||
return x.buffer_info()[0]
|
||||
|
||||
def _ushortp(x):
|
||||
assert x.typecode == 'H'
|
||||
return x.buffer_info()[0]
|
||||
|
||||
def _floatp(x):
|
||||
assert x.typecode == 'f'
|
||||
return x.buffer_info()[0]
|
||||
vec3_right = vec3(1.0, 0.0, 0.0)
|
||||
vec3_forward = vec3(0.0, 1.0, 0.0)
|
||||
vec3_up = vec3(0.0, 0.0, 1.0)
|
||||
|
||||
vec3_rotate = _engine.rk_vec3_rotate
|
||||
vec3_rotate.argtypes = (
|
||||
@ -173,13 +141,64 @@ mat4_mul_mat4.argtypes = (
|
||||
_mat4p, # a
|
||||
_mat4p) # b
|
||||
|
||||
initialize = _engine.rk_initialize
|
||||
initialize.restype = ctypes.c_void_p
|
||||
initialize.argtypes = (
|
||||
# display.hpp
|
||||
|
||||
create_display = _engine.rk_create_display
|
||||
create_display.restype = ctypes.c_void_p
|
||||
create_display.argtypes = (
|
||||
ctypes.c_char_p, # name
|
||||
ctypes.c_uint, # width
|
||||
ctypes.c_uint) # height
|
||||
|
||||
destroy_display = _engine.rk_destroy_display
|
||||
destroy_display.argtypes = (
|
||||
ctypes.c_void_p,) # display
|
||||
|
||||
swap_buffers = _engine.rk_swap_buffers
|
||||
swap_buffers.argtypes = (
|
||||
ctypes.c_void_p,) # display
|
||||
|
||||
# render.hpp
|
||||
|
||||
TEXTURE_FORMAT_SRGB8_A8 = 0
|
||||
TEXTURE_FORMAT_RGBA8 = 1
|
||||
TEXTURE_FORMAT_RGB10_A2 = 2
|
||||
TEXTURE_FORMAT_32F = 3
|
||||
|
||||
TEXTURE_FORMAT_TYPECODE = ('B', 'B', 'I', 'f')
|
||||
TEXTURE_FORMAT_NELEMS = (4, 4, 1, 1)
|
||||
|
||||
TEXTURE_FLAG_3D = _flag(0)
|
||||
TEXTURE_FLAG_MIPMAPS = _flag(1)
|
||||
TEXTURE_FLAG_MIN_NEAREST = 0
|
||||
TEXTURE_FLAG_MIN_LINEAR = _flag(2)
|
||||
TEXTURE_FLAG_MAG_NEAREST = 0
|
||||
TEXTURE_FLAG_MAG_LINEAR = _flag(3)
|
||||
|
||||
VERTEX_FORMAT_VEC3_FLOAT = 1
|
||||
VERTEX_FORMAT_VEC3_INT10 = 2
|
||||
VERTEX_FORMAT_VEC3_UINT10 = 3
|
||||
VERTEX_FORMAT_NORMALIZE = _flag(7)
|
||||
|
||||
def vertex_format(*format):
|
||||
return array('B', format).tobytes()
|
||||
|
||||
PARAM_FORMAT_VEC3_FLOAT = 1
|
||||
PARAM_FORMAT_VEC3_SHORT = 2
|
||||
PARAM_FORMAT_VEC3_INT10 = 3
|
||||
PARAM_FORMAT_NORMALIZE = _flag(7)
|
||||
|
||||
def params_format(*format):
|
||||
return array('B', format).tobytes()
|
||||
|
||||
INSTANCE_FLAG_SPAWNED = _flag(0)
|
||||
INSTANCE_FLAG_VISIBLE = _flag(1)
|
||||
|
||||
BATCH_MAX_SIZE = 65536
|
||||
|
||||
render_initialize = _engine.rk_render_initialize
|
||||
render_terminate = _engine.rk_render_terminate
|
||||
|
||||
_load_shader = _engine.rk_load_shader
|
||||
_load_shader.restype = ctypes.c_void_p
|
||||
_load_shader.argtypes = (
|
||||
@ -343,7 +362,7 @@ destroy_shader = _engine.rk_destroy_shader
|
||||
destroy_shader.argtypes = (
|
||||
ctypes.c_void_p,) # shader
|
||||
|
||||
terminate = _engine.rk_terminate
|
||||
# events.hpp
|
||||
|
||||
EVENT_FOCUS_IN = 0
|
||||
EVENT_FOCUS_OUT = 1
|
||||
@ -382,12 +401,24 @@ class _Events(ctypes.Union):
|
||||
class Event(ctypes.Structure):
|
||||
_fields_ = ('type', ctypes.c_uint), ('data', _Events)
|
||||
|
||||
set_autorepeat = _engine.rk_set_autorepeat
|
||||
set_autorepeat.argtypes = (
|
||||
ctypes.c_bool,) # autorepeat
|
||||
create_events = _engine.rk_create_events
|
||||
create_events.restype = ctypes.c_void_p
|
||||
create_events.argtypes = (
|
||||
ctypes.c_void_p,) # display
|
||||
|
||||
set_acceleration = _engine.rk_set_acceleration
|
||||
set_acceleration.argtypes = (
|
||||
destroy_events = _engine.rk_destroy_events
|
||||
destroy_events.argtypes = (
|
||||
ctypes.c_void_p, # display
|
||||
ctypes.c_void_p) # events
|
||||
|
||||
set_key_autorepeat = _engine.rk_set_key_autorepeat
|
||||
set_key_autorepeat.argtypes = (
|
||||
ctypes.c_void_p, # events
|
||||
ctypes.c_bool) # autorepeat
|
||||
|
||||
set_motion_acceleration = _engine.rk_set_motion_acceleration
|
||||
set_motion_acceleration.argtypes = (
|
||||
ctypes.c_void_p, # events
|
||||
ctypes.c_uint, # numerator
|
||||
ctypes.c_uint, # denominator
|
||||
ctypes.c_uint) # threshold
|
||||
@ -395,5 +426,6 @@ set_acceleration.argtypes = (
|
||||
consume_events = _engine.rk_consume_events
|
||||
consume_events.restype = ctypes.c_uint
|
||||
consume_events.argtypes = (
|
||||
ctypes.POINTER(Event), # events
|
||||
ctypes.c_void_p, # events
|
||||
ctypes.POINTER(Event), # buffer
|
||||
ctypes.c_uint) # max_events
|
||||
|
34
cpp/display.hpp
Normal file
34
cpp/display.hpp
Normal file
@ -0,0 +1,34 @@
|
||||
// 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/>.
|
||||
|
||||
#ifndef _RK_ENGINE_DISPLAY_H
|
||||
#define _RK_ENGINE_DISPLAY_H
|
||||
|
||||
#include "types.hpp"
|
||||
|
||||
typedef rk_handle_t rk_display_t;
|
||||
|
||||
RK_EXPORT rk_display_t rk_create_display(
|
||||
rk_char const * name,
|
||||
rk_uint width,
|
||||
rk_uint height);
|
||||
|
||||
RK_EXPORT void rk_destroy_display(
|
||||
rk_display_t display);
|
||||
|
||||
RK_EXPORT void rk_swap_buffers(
|
||||
rk_display_t display);
|
||||
|
||||
#endif // _RK_ENGINE_DISPLAY_H
|
243
cpp/display/display_glx.cpp
Normal file
243
cpp/display/display_glx.cpp
Normal file
@ -0,0 +1,243 @@
|
||||
// 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 <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 *);
|
||||
|
||||
rk_DrawElementsInstancedBaseInstanceFunc rk_DrawElementsInstancedBaseInstance = nullptr;
|
||||
rk_MultiDrawElementsIndirectFunc rk_MultiDrawElementsIndirect = nullptr;
|
||||
|
||||
static int const rk_visual_attribs[] = {
|
||||
GLX_X_RENDERABLE, True,
|
||||
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
|
||||
GLX_RENDER_TYPE, GLX_RGBA_BIT,
|
||||
GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
|
||||
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_DOUBLEBUFFER, True,
|
||||
None
|
||||
};
|
||||
|
||||
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_glx_printf(
|
||||
char const * messsage) {
|
||||
printf("[GLX] %s\n", messsage);
|
||||
}
|
||||
|
||||
static bool rk_extension_supported(
|
||||
char const * extlist,
|
||||
char const * extension) {
|
||||
char const * where = strchr(extension, ' ');
|
||||
if (where || *extension == '\0') {
|
||||
return false;
|
||||
}
|
||||
for (char const * start = extlist;;) {
|
||||
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;
|
||||
}
|
||||
|
||||
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_glx_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));
|
||||
|
||||
int fbcount;
|
||||
GLXFBConfig * const fbc = glXChooseFBConfig(
|
||||
display->display, DefaultScreen(display->display), rk_visual_attribs, &fbcount);
|
||||
if (!fbc) {
|
||||
rk_glx_printf("Failed to retrieve framebuffer configs.");
|
||||
rk_destroy_display(reinterpret_cast<rk_display_t>(display));
|
||||
return nullptr;
|
||||
}
|
||||
printf("[GLX] Found %d framebuffer configs.\n", fbcount);
|
||||
|
||||
int best_fbc = -1;
|
||||
int best_num_samp = -1;
|
||||
for (int i = 0; i < fbcount; ++i) {
|
||||
XVisualInfo * const vi = glXGetVisualFromFBConfig(display->display, fbc[i]);
|
||||
if (vi) {
|
||||
int samp_buf, samples;
|
||||
glXGetFBConfigAttrib(display->display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf);
|
||||
glXGetFBConfigAttrib(display->display, fbc[i], GLX_SAMPLES, &samples);
|
||||
if (best_fbc < 0 || (samp_buf && samples > best_num_samp)) {
|
||||
best_fbc = i;
|
||||
best_num_samp = samples;
|
||||
}
|
||||
XFree(vi);
|
||||
}
|
||||
}
|
||||
if (best_fbc == -1) {
|
||||
XFree(fbc);
|
||||
rk_glx_printf("Failed to find a suitable framebuffer config.");
|
||||
rk_destroy_display(reinterpret_cast<rk_display_t>(display));
|
||||
return nullptr;
|
||||
}
|
||||
GLXFBConfig const bestFbc = fbc[best_fbc];
|
||||
XFree(fbc);
|
||||
printf("[GLX] Select framebuffer config with %d samples.\n", best_num_samp);
|
||||
|
||||
XVisualInfo * const vi = glXGetVisualFromFBConfig(display->display, bestFbc);
|
||||
display->colormap = XCreateColormap(
|
||||
display->display, RootWindow(display->display, vi->screen), vi->visual, AllocNone);
|
||||
XSetWindowAttributes swa;
|
||||
swa.colormap = display->colormap;
|
||||
// swa.background_pixmap = None;
|
||||
// swa.border_pixel = 0;
|
||||
swa.event_mask = RK_EVENTS_MASK;
|
||||
display->window = XCreateWindow(display->display, RootWindow(display->display, vi->screen),
|
||||
0, 0, width, height, 0, vi->depth, InputOutput, vi->visual,
|
||||
/*CWBorderPixel |*/ CWColormap | CWEventMask, &swa);
|
||||
XFree(vi);
|
||||
if (!display->window) {
|
||||
rk_glx_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_CreateContextAttribsFunc const glXCreateContextAttribs =
|
||||
reinterpret_cast<rk_CreateContextAttribsFunc>(
|
||||
glXGetProcAddressARB(reinterpret_cast<GLubyte const *>("glXCreateContextAttribsARB")));
|
||||
|
||||
if (!rk_extension_supported(glx_exts, "GLX_ARB_create_context") || !glXCreateContextAttribs) {
|
||||
rk_glx_printf("glXCreateContextAttribsARB extension not found.");
|
||||
rk_destroy_display(reinterpret_cast<rk_display_t>(display));
|
||||
return nullptr;
|
||||
} else {
|
||||
rk_error_occured = false;
|
||||
int (*oldHandler)(Display *, XErrorEvent *) = XSetErrorHandler(&rk_error_handler);
|
||||
display->context = glXCreateContextAttribs(display->display, bestFbc, 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);
|
||||
|
||||
char const * const gl_exts = reinterpret_cast<char const *>(glGetString(GL_EXTENSIONS));
|
||||
if (rk_extension_supported(gl_exts, "GL_EXT_base_instance")) {
|
||||
rk_DrawElementsInstancedBaseInstance =
|
||||
reinterpret_cast<rk_DrawElementsInstancedBaseInstanceFunc>(
|
||||
glXGetProcAddressARB(reinterpret_cast<GLubyte const *>("DrawElementsInstancedBaseInstance")));
|
||||
if (rk_DrawElementsInstancedBaseInstance) {
|
||||
rk_glx_printf("Using extension GL_EXT_base_instance::DrawElementsInstancedBaseInstance.");
|
||||
if (rk_extension_supported(gl_exts, "GL_EXT_multi_draw_indirect")) {
|
||||
rk_MultiDrawElementsIndirect =
|
||||
reinterpret_cast<rk_MultiDrawElementsIndirectFunc>(
|
||||
glXGetProcAddressARB(reinterpret_cast<GLubyte const *>("MultiDrawElementsIndirectEXT")));
|
||||
if (rk_MultiDrawElementsIndirect) {
|
||||
rk_glx_printf("Using extension GL_EXT_multi_draw_indirect::MultiDrawElementsIndirectEXT.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@ -13,10 +13,17 @@
|
||||
// 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/>.
|
||||
|
||||
#ifndef _RK_ENGINE_RENDER_CONTEXT_H
|
||||
#define _RK_ENGINE_RENDER_CONTEXT_H
|
||||
#ifndef _RK_ENGINE_DISPLAY_GLX_H
|
||||
#define _RK_ENGINE_DISPLAY_GLX_H
|
||||
|
||||
#include "../render.hpp"
|
||||
#include "../types.hpp"
|
||||
#include "display_x11.hpp"
|
||||
#include "../events/events_x11.hpp"
|
||||
#include <GL/glx.h>
|
||||
|
||||
struct rk_display_glx : public rk_display_x11 {
|
||||
GLXContext context;
|
||||
};
|
||||
|
||||
typedef void (*rk_DrawElementsInstancedBaseInstanceFunc)(rk_uint, rk_uint, rk_uint, const void *, rk_uint, rk_uint);
|
||||
typedef void (*rk_MultiDrawElementsIndirectFunc)(rk_uint, rk_uint, const void *, rk_uint, rk_uint);
|
||||
@ -24,12 +31,4 @@ typedef void (*rk_MultiDrawElementsIndirectFunc)(rk_uint, rk_uint, const void *,
|
||||
extern rk_DrawElementsInstancedBaseInstanceFunc rk_DrawElementsInstancedBaseInstance;
|
||||
extern rk_MultiDrawElementsIndirectFunc rk_MultiDrawElementsIndirect;
|
||||
|
||||
extern rk_window_t rk_create_context(
|
||||
char const * name,
|
||||
unsigned width,
|
||||
unsigned height);
|
||||
|
||||
extern void rk_swap_buffers();
|
||||
extern void rk_destroy_context();
|
||||
|
||||
#endif // _RK_ENGINE_RENDER_CONTEXT_H
|
||||
#endif // _RK_ENGINE_DISPLAY_GLX_H
|
27
cpp/display/display_x11.hpp
Normal file
27
cpp/display/display_x11.hpp
Normal file
@ -0,0 +1,27 @@
|
||||
// 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/>.
|
||||
|
||||
#ifndef _RK_ENGINE_DISPLAY_X11_H
|
||||
#define _RK_ENGINE_DISPLAY_X11_H
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
struct rk_display_x11 {
|
||||
Display * display;
|
||||
Window window;
|
||||
Colormap colormap;
|
||||
};
|
||||
|
||||
#endif // _RK_ENGINE_DISPLAY_X11_H
|
@ -17,6 +17,9 @@
|
||||
#define _RK_ENGINE_EVENTS_H
|
||||
|
||||
#include "types.hpp"
|
||||
#include "display.hpp"
|
||||
|
||||
typedef rk_handle_t rk_events_t;
|
||||
|
||||
enum rk_event_type : rk_uint {
|
||||
RK_EVENT_FOCUS_IN = 0,
|
||||
@ -52,19 +55,26 @@ struct rk_event {
|
||||
};
|
||||
};
|
||||
|
||||
extern bool rk_initialize_events();
|
||||
extern void rk_terminate_events();
|
||||
RK_EXPORT rk_events_t rk_create_events(
|
||||
rk_display_t display);
|
||||
|
||||
RK_EXPORT void rk_set_autorepeat(
|
||||
RK_EXPORT void rk_destroy_events(
|
||||
rk_display_t display,
|
||||
rk_events_t events);
|
||||
|
||||
RK_EXPORT void rk_set_key_autorepeat(
|
||||
rk_events_t events,
|
||||
rk_bool autorepeat);
|
||||
|
||||
RK_EXPORT void rk_set_acceleration(
|
||||
RK_EXPORT void rk_set_motion_acceleration(
|
||||
rk_events_t events,
|
||||
rk_uint numerator,
|
||||
rk_uint denominator,
|
||||
rk_uint threshold);
|
||||
|
||||
RK_EXPORT rk_uint rk_consume_events(
|
||||
rk_event * events,
|
||||
rk_events_t events,
|
||||
rk_event * buffer,
|
||||
rk_uint max_events);
|
||||
|
||||
#endif // _RK_ENGINE_EVENTS_H
|
||||
|
@ -14,144 +14,168 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "../events.hpp"
|
||||
#include <X11/Xlib.h>
|
||||
#include "events_x11.hpp"
|
||||
#include "../display/display_x11.hpp"
|
||||
#include <cstdio>
|
||||
|
||||
extern Display * rk_display;
|
||||
extern Window rk_window;
|
||||
|
||||
unsigned rk_events_mask = FocusChangeMask |
|
||||
KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask;
|
||||
|
||||
static XIM rk_input_manager = nullptr;
|
||||
static XIC rk_input_context = nullptr;
|
||||
|
||||
static bool rk_focused = false;
|
||||
static bool rk_keyboard_autorepeat = false;
|
||||
static XKeyboardState rk_previous_keyboard_state;
|
||||
static int rk_previous_accel_numerator, rk_accel_numerator = 1;
|
||||
static int rk_previous_accel_denominator, rk_accel_denominator = 1;
|
||||
static int rk_previous_threshold, rk_accel_threshold = 0;
|
||||
|
||||
static void rk_backup_setup() {
|
||||
XGetKeyboardControl(rk_display, &rk_previous_keyboard_state);
|
||||
XGetPointerControl(rk_display,
|
||||
&rk_previous_accel_numerator, &rk_previous_accel_denominator, &rk_previous_threshold);
|
||||
static void rk_x11_printf(
|
||||
char const * messsage) {
|
||||
printf("[X11] %s\n", messsage);
|
||||
}
|
||||
|
||||
static void rk_restore_setup() {
|
||||
if (AutoRepeatModeOn == rk_previous_keyboard_state.global_auto_repeat) {
|
||||
XAutoRepeatOn(rk_display);
|
||||
static void rk_backup(rk_events_x11 & events) {
|
||||
XKeyboardState backup;
|
||||
XGetKeyboardControl(events.display, &backup);
|
||||
events.backup_autorepeat = (AutoRepeatModeOn == backup.global_auto_repeat);
|
||||
XGetPointerControl(
|
||||
events.display, &events.backup_numerator, &events.backup_denominator, &events.backup_threshold);
|
||||
}
|
||||
|
||||
static void rk_restore(rk_events_x11 & events) {
|
||||
if (events.backup_autorepeat) {
|
||||
XAutoRepeatOn(events.display);
|
||||
} else {
|
||||
XAutoRepeatOff(rk_display);
|
||||
XAutoRepeatOff(events.display);
|
||||
}
|
||||
XChangePointerControl(rk_display, True, True,
|
||||
rk_previous_accel_numerator, rk_previous_accel_denominator, rk_previous_threshold);
|
||||
XChangePointerControl(
|
||||
events.display, True, True, events.backup_numerator, events.backup_denominator, events.backup_threshold);
|
||||
}
|
||||
|
||||
static void rk_apply_setup() {
|
||||
if (rk_keyboard_autorepeat) {
|
||||
XAutoRepeatOn(rk_display);
|
||||
static void rk_apply(rk_events_x11 & events) {
|
||||
if (events.key_autorepeat) {
|
||||
XAutoRepeatOn(events.display);
|
||||
} else {
|
||||
XAutoRepeatOff(rk_display);
|
||||
XAutoRepeatOff(events.display);
|
||||
}
|
||||
XChangePointerControl(rk_display, True, True, rk_accel_numerator, rk_accel_denominator, rk_accel_threshold);
|
||||
XChangePointerControl(
|
||||
events.display, True, True, events.motion_numerator, events.motion_denominator, events.motion_threshold);
|
||||
}
|
||||
|
||||
bool rk_initialize_events() {
|
||||
rk_focused = false;
|
||||
rk_backup_setup();
|
||||
rk_keyboard_autorepeat = (AutoRepeatModeOn == rk_previous_keyboard_state.global_auto_repeat);
|
||||
rk_accel_numerator = rk_previous_accel_numerator;
|
||||
rk_accel_denominator = rk_previous_accel_denominator;
|
||||
rk_accel_threshold = rk_previous_threshold;
|
||||
rk_input_manager = XOpenIM(rk_display, nullptr, nullptr, nullptr);
|
||||
if (!rk_input_manager) {
|
||||
return false;
|
||||
}
|
||||
rk_input_context = XCreateIC(rk_input_manager,
|
||||
XNInputStyle, XIMPreeditNone | XIMStatusNone,
|
||||
XNClientWindow, rk_window,
|
||||
NULL);
|
||||
if (!rk_input_context) {
|
||||
return false;
|
||||
}
|
||||
XSetICFocus(rk_input_context);
|
||||
return true;
|
||||
}
|
||||
|
||||
void rk_terminate_events() {
|
||||
rk_restore_setup();
|
||||
if (rk_input_context) {
|
||||
XUnsetICFocus(rk_input_context);
|
||||
XDestroyIC(rk_input_context);
|
||||
rk_input_context = nullptr;
|
||||
}
|
||||
if (rk_input_manager) {
|
||||
XCloseIM(rk_input_manager);
|
||||
rk_input_manager = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static void rk_focus_in() {
|
||||
if (!rk_focused) {
|
||||
rk_backup_setup();
|
||||
rk_apply_setup();
|
||||
XGrabPointer(rk_display, rk_window, True,
|
||||
static void rk_focus_in(rk_events_x11 & events) {
|
||||
if (!events.focused) {
|
||||
rk_backup(events);
|
||||
rk_apply(events);
|
||||
XGrabPointer(events.display, events.window, True,
|
||||
ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
|
||||
GrabModeAsync, GrabModeAsync, rk_window, None, CurrentTime);
|
||||
rk_focused = true;
|
||||
GrabModeAsync, GrabModeAsync, events.window, None, CurrentTime);
|
||||
events.focused = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void rk_focus_out() {
|
||||
if (rk_focused) {
|
||||
XUngrabPointer(rk_display, CurrentTime);
|
||||
rk_restore_setup();
|
||||
rk_focused = false;
|
||||
static void rk_focus_out(rk_events_x11 & events) {
|
||||
if (events.focused) {
|
||||
XUngrabPointer(events.display, CurrentTime);
|
||||
rk_restore(events);
|
||||
events.focused = false;
|
||||
}
|
||||
}
|
||||
|
||||
void rk_set_autorepeat(
|
||||
rk_events_t rk_create_events(
|
||||
rk_display_t _display) {
|
||||
rk_display_x11 const * const display = reinterpret_cast<rk_display_x11 const *>(_display);
|
||||
if (!display || !display->display || !display->window) {
|
||||
return nullptr;
|
||||
}
|
||||
rk_events_x11 * const events = new rk_events_x11;
|
||||
events->display = display->display;
|
||||
events->window = display->window;
|
||||
events->input_manager = XOpenIM(display->display, nullptr, nullptr, nullptr);
|
||||
if (!events->input_manager) {
|
||||
rk_x11_printf("Failed to open input manager.");
|
||||
delete events;
|
||||
return nullptr;
|
||||
}
|
||||
events->input_context = XCreateIC(events->input_manager,
|
||||
XNInputStyle, XIMPreeditNone | XIMStatusNone,
|
||||
XNClientWindow, display->window,
|
||||
NULL);
|
||||
if (!events->input_context) {
|
||||
rk_x11_printf("Failed to create input context.");
|
||||
XCloseIM(events->input_manager);
|
||||
delete events;
|
||||
return nullptr;
|
||||
}
|
||||
events->focused = false;
|
||||
rk_backup(*events);
|
||||
events->key_autorepeat = events->backup_autorepeat;
|
||||
events->motion_numerator = events->backup_numerator;
|
||||
events->motion_denominator = events->backup_denominator;
|
||||
events->motion_threshold = events->backup_threshold;
|
||||
XSetICFocus(events->input_context);
|
||||
return reinterpret_cast<rk_events_t>(events);
|
||||
}
|
||||
|
||||
void rk_destroy_events(
|
||||
rk_display_t _display,
|
||||
rk_events_t _events) {
|
||||
rk_display_x11 const * const display = reinterpret_cast<rk_display_x11 const *>(_display);
|
||||
rk_events_x11 * const events = reinterpret_cast<rk_events_x11 *>(_events);
|
||||
if (display && events && display->display && display->display == events->display) {
|
||||
rk_restore(*events);
|
||||
if (events->input_context) {
|
||||
XUnsetICFocus(events->input_context);
|
||||
XDestroyIC(events->input_context);
|
||||
}
|
||||
if (events->input_manager) {
|
||||
XCloseIM(events->input_manager);
|
||||
}
|
||||
}
|
||||
if (events) {
|
||||
delete events;
|
||||
}
|
||||
}
|
||||
|
||||
void rk_set_key_autorepeat(
|
||||
rk_events_t _events,
|
||||
rk_bool autorepeat) {
|
||||
rk_keyboard_autorepeat = autorepeat;
|
||||
if (rk_focused) {
|
||||
rk_apply_setup();
|
||||
rk_events_x11 * const events = reinterpret_cast<rk_events_x11 *>(_events);
|
||||
if (events) {
|
||||
events->key_autorepeat = autorepeat;
|
||||
if (events->focused) {
|
||||
rk_apply(*events);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rk_set_acceleration(
|
||||
void rk_set_motion_acceleration(
|
||||
rk_events_t _events,
|
||||
rk_uint numerator,
|
||||
rk_uint denominator,
|
||||
rk_uint threshold) {
|
||||
rk_accel_numerator = numerator;
|
||||
rk_accel_denominator = denominator;
|
||||
rk_accel_threshold = threshold;
|
||||
if (rk_focused) {
|
||||
rk_apply_setup();
|
||||
rk_events_x11 * const events = reinterpret_cast<rk_events_x11 *>(_events);
|
||||
if (events) {
|
||||
events->motion_numerator = numerator;
|
||||
events->motion_denominator = denominator;
|
||||
events->motion_threshold = threshold;
|
||||
if (events->focused) {
|
||||
rk_apply(*events);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rk_uint rk_consume_events(
|
||||
rk_event * events,
|
||||
rk_events_t _events,
|
||||
rk_event * buffer,
|
||||
rk_uint max_events) {
|
||||
static wchar_t string[256];
|
||||
if (!events || !max_events) {
|
||||
rk_events_x11 * const events = reinterpret_cast<rk_events_x11 *>(_events);
|
||||
if (!events || !buffer || !max_events) {
|
||||
return 0;
|
||||
}
|
||||
XEvent x11_event;
|
||||
KeySym keysym;
|
||||
Status status;
|
||||
unsigned nevents = 0;
|
||||
while (nevents < max_events && XCheckWindowEvent(rk_display, rk_window, rk_events_mask, &x11_event)) {
|
||||
while (nevents < max_events && XCheckWindowEvent(events->display, events->window, RK_EVENTS_MASK, &x11_event)) {
|
||||
if (XFilterEvent(&x11_event, 0)) {
|
||||
continue;
|
||||
}
|
||||
rk_event & event = events[nevents];
|
||||
rk_event & event = buffer[nevents];
|
||||
switch (x11_event.type) {
|
||||
|
||||
case FocusIn:
|
||||
if (NotifyNormal == x11_event.xfocus.mode) {
|
||||
rk_focus_in();
|
||||
rk_focus_in(*events);
|
||||
event.type = RK_EVENT_FOCUS_IN;
|
||||
++nevents;
|
||||
}
|
||||
@ -159,7 +183,7 @@ rk_uint rk_consume_events(
|
||||
|
||||
case FocusOut:
|
||||
if (NotifyNormal == x11_event.xfocus.mode) {
|
||||
rk_focus_out();
|
||||
rk_focus_out(*events);
|
||||
event.type = RK_EVENT_FOCUS_OUT;
|
||||
++nevents;
|
||||
}
|
||||
@ -168,7 +192,7 @@ rk_uint rk_consume_events(
|
||||
case KeyPress:
|
||||
event.type = RK_EVENT_KEY_PRESS;
|
||||
event.key.code = x11_event.xkey.keycode;
|
||||
XwcLookupString(rk_input_context, &x11_event.xkey, string, 256, &keysym, &status);
|
||||
XwcLookupString(events->input_context, &x11_event.xkey, string, 256, &keysym, &status);
|
||||
switch (status) {
|
||||
case XLookupChars:
|
||||
event.key.symbol = XLookupKeysym(&x11_event.xkey, 0);
|
||||
|
44
cpp/events/events_x11.hpp
Normal file
44
cpp/events/events_x11.hpp
Normal file
@ -0,0 +1,44 @@
|
||||
// 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/>.
|
||||
|
||||
#ifndef _RK_ENGINE_EVENTS_X11_H
|
||||
#define _RK_ENGINE_EVENTS_X11_H
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
enum : unsigned {
|
||||
RK_EVENTS_MASK = FocusChangeMask |
|
||||
KeyPressMask | KeyReleaseMask |
|
||||
ButtonPressMask | ButtonReleaseMask |
|
||||
PointerMotionMask
|
||||
};
|
||||
|
||||
struct rk_events_x11 {
|
||||
Display * display;
|
||||
Window window;
|
||||
XIM input_manager;
|
||||
XIC input_context;
|
||||
bool focused;
|
||||
bool key_autorepeat;
|
||||
bool backup_autorepeat;
|
||||
int motion_numerator;
|
||||
int backup_numerator;
|
||||
int motion_denominator;
|
||||
int backup_denominator;
|
||||
int motion_threshold;
|
||||
int backup_threshold;
|
||||
};
|
||||
|
||||
#endif // _RK_ENGINE_EVENTS_X11_H
|
@ -1,249 +0,0 @@
|
||||
// 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 "render_context.hpp"
|
||||
#include "../events.hpp"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <GLES3/gl32.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <GL/glx.h>
|
||||
|
||||
extern unsigned rk_events_mask;
|
||||
|
||||
Display * rk_display = nullptr;
|
||||
Window rk_window = 0;
|
||||
|
||||
static Colormap rk_colormap = 0;
|
||||
static GLXContext rk_context = nullptr;
|
||||
static bool rk_error_occured = false;
|
||||
|
||||
#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
|
||||
#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
|
||||
typedef GLXContext (*glXCreateContextAttribsARBProc)(Display *, GLXFBConfig, GLXContext, Bool, int const *);
|
||||
|
||||
rk_DrawElementsInstancedBaseInstanceFunc rk_DrawElementsInstancedBaseInstance = nullptr;
|
||||
rk_MultiDrawElementsIndirectFunc rk_MultiDrawElementsIndirect = nullptr;
|
||||
|
||||
static int const rk_visual_attribs[] = {
|
||||
GLX_X_RENDERABLE, True,
|
||||
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
|
||||
GLX_RENDER_TYPE, GLX_RGBA_BIT,
|
||||
GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
|
||||
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_DOUBLEBUFFER, True,
|
||||
None
|
||||
};
|
||||
|
||||
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_printf(
|
||||
char const * messsage) {
|
||||
printf("[GLX] %s\n", messsage);
|
||||
}
|
||||
|
||||
static bool rk_extension_supported(
|
||||
char const * extlist,
|
||||
char const * extension) {
|
||||
char const * where = strchr(extension, ' ');
|
||||
if (where || *extension == '\0') {
|
||||
return false;
|
||||
}
|
||||
for (char const * start = extlist;;) {
|
||||
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;
|
||||
}
|
||||
|
||||
static int rk_error_handler(
|
||||
Display * display,
|
||||
XErrorEvent * event) {
|
||||
rk_error_occured = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
rk_window_t rk_create_context(
|
||||
char const * name,
|
||||
unsigned width,
|
||||
unsigned height) {
|
||||
XSetLocaleModifiers("");
|
||||
|
||||
rk_display = XOpenDisplay(nullptr);
|
||||
if (!rk_display) {
|
||||
rk_printf("Failed to open X display.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int glx_major, glx_minor;
|
||||
if (!glXQueryVersion(rk_display, &glx_major, &glx_minor) || (glx_major == 1 && glx_minor < 3) || glx_major < 1) {
|
||||
rk_printf("Invalid GLX version.");
|
||||
rk_destroy_context();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int fbcount;
|
||||
GLXFBConfig * const fbc = glXChooseFBConfig(rk_display, DefaultScreen(rk_display), rk_visual_attribs, &fbcount);
|
||||
if (!fbc) {
|
||||
rk_printf("Failed to retrieve framebuffer configs.");
|
||||
rk_destroy_context();
|
||||
return nullptr;
|
||||
}
|
||||
printf("[GLX] Found %d framebuffer configs.\n", fbcount);
|
||||
|
||||
int best_fbc = -1;
|
||||
int best_num_samp = -1;
|
||||
for (int i = 0; i < fbcount; ++i) {
|
||||
XVisualInfo * vi = glXGetVisualFromFBConfig(rk_display, fbc[i]);
|
||||
if (vi) {
|
||||
int samp_buf, samples;
|
||||
glXGetFBConfigAttrib(rk_display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf);
|
||||
glXGetFBConfigAttrib(rk_display, fbc[i], GLX_SAMPLES, &samples);
|
||||
if (best_fbc < 0 || (samp_buf && samples > best_num_samp)) {
|
||||
best_fbc = i;
|
||||
best_num_samp = samples;
|
||||
}
|
||||
XFree(vi);
|
||||
}
|
||||
}
|
||||
if (best_fbc == -1) {
|
||||
XFree(fbc);
|
||||
rk_printf("Failed to find a suitable framebuffer config.");
|
||||
rk_destroy_context();
|
||||
return nullptr;
|
||||
}
|
||||
GLXFBConfig const bestFbc = fbc[best_fbc];
|
||||
XFree(fbc);
|
||||
printf("[GLX] Select framebuffer config with %d samples.\n", best_num_samp);
|
||||
|
||||
XVisualInfo * const vi = glXGetVisualFromFBConfig(rk_display, bestFbc);
|
||||
rk_colormap = XCreateColormap(rk_display, RootWindow(rk_display, vi->screen), vi->visual, AllocNone);
|
||||
XSetWindowAttributes swa;
|
||||
swa.colormap = rk_colormap;
|
||||
swa.background_pixmap = None;
|
||||
swa.border_pixel = 0;
|
||||
swa.event_mask = rk_events_mask;
|
||||
rk_window = XCreateWindow(rk_display, RootWindow(rk_display, vi->screen),
|
||||
0, 0, width, height, 0, vi->depth, InputOutput, vi->visual,
|
||||
CWBorderPixel | CWColormap | CWEventMask, &swa);
|
||||
XFree(vi);
|
||||
if (!rk_window) {
|
||||
rk_printf("Failed to create window.");
|
||||
rk_destroy_context();
|
||||
return nullptr;
|
||||
}
|
||||
XStoreName(rk_display, rk_window, name);
|
||||
|
||||
if (!rk_initialize_events()) {
|
||||
rk_printf("Failed to initialize events.");
|
||||
rk_destroy_context();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
XMapWindow(rk_display, rk_window);
|
||||
|
||||
char const * const glx_exts = glXQueryExtensionsString(rk_display, DefaultScreen(rk_display));
|
||||
glXCreateContextAttribsARBProc const glXCreateContextAttribsARB =
|
||||
reinterpret_cast<glXCreateContextAttribsARBProc>(
|
||||
glXGetProcAddressARB(reinterpret_cast<GLubyte const *>("glXCreateContextAttribsARB")));
|
||||
|
||||
rk_error_occured = false;
|
||||
int (*oldHandler)(Display *, XErrorEvent *) = XSetErrorHandler(&rk_error_handler);
|
||||
if (!rk_extension_supported(glx_exts, "GLX_ARB_create_context") || !glXCreateContextAttribsARB) {
|
||||
rk_printf("glXCreateContextAttribsARB() extension not found.");
|
||||
rk_destroy_context();
|
||||
return nullptr;
|
||||
} else {
|
||||
rk_context = glXCreateContextAttribsARB(rk_display, bestFbc, 0, True, rk_context_attribs);
|
||||
XSync(rk_display, False);
|
||||
if (rk_error_occured || !rk_context) {
|
||||
rk_printf("Failed to create context.");
|
||||
rk_destroy_context();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
XSetErrorHandler(oldHandler);
|
||||
|
||||
if (!glXIsDirect(rk_display, rk_context)) {
|
||||
rk_printf("Warning: Rendering context is indirect.");
|
||||
}
|
||||
glXMakeCurrent(rk_display, rk_window, rk_context);
|
||||
|
||||
char const * const gl_exts = reinterpret_cast<char const *>(glGetString(GL_EXTENSIONS));
|
||||
if (rk_extension_supported(gl_exts, "GL_EXT_base_instance")) {
|
||||
rk_DrawElementsInstancedBaseInstance =
|
||||
reinterpret_cast<rk_DrawElementsInstancedBaseInstanceFunc>(
|
||||
glXGetProcAddressARB(reinterpret_cast<GLubyte const *>("DrawElementsInstancedBaseInstance")));
|
||||
if (rk_DrawElementsInstancedBaseInstance) {
|
||||
rk_printf("Using extension GL_EXT_base_instance::DrawElementsInstancedBaseInstance.");
|
||||
if (rk_extension_supported(gl_exts, "GL_EXT_multi_draw_indirect")) {
|
||||
rk_MultiDrawElementsIndirect =
|
||||
reinterpret_cast<rk_MultiDrawElementsIndirectFunc>(
|
||||
glXGetProcAddressARB(reinterpret_cast<GLubyte const *>("MultiDrawElementsIndirectEXT")));
|
||||
if (rk_MultiDrawElementsIndirect) {
|
||||
rk_printf("Using extension GL_EXT_multi_draw_indirect::MultiDrawElementsIndirectEXT.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return reinterpret_cast<rk_window_t>(rk_window);
|
||||
}
|
||||
|
||||
void rk_swap_buffers() {
|
||||
if (rk_display && rk_window) {
|
||||
glXSwapBuffers(rk_display, rk_window);
|
||||
}
|
||||
}
|
||||
|
||||
void rk_destroy_context() {
|
||||
if (rk_display) {
|
||||
glXMakeCurrent(rk_display, 0, nullptr);
|
||||
if (rk_context) {
|
||||
glXDestroyContext(rk_display, rk_context);
|
||||
rk_context = nullptr;
|
||||
}
|
||||
rk_terminate_events();
|
||||
if (rk_window) {
|
||||
XDestroyWindow(rk_display, rk_window);
|
||||
rk_window = 0;
|
||||
}
|
||||
if (rk_colormap) {
|
||||
XFreeColormap(rk_display, rk_colormap);
|
||||
rk_colormap = 0;
|
||||
}
|
||||
XCloseDisplay(rk_display);
|
||||
rk_display = nullptr;
|
||||
}
|
||||
}
|
@ -19,7 +19,6 @@
|
||||
#include "types.hpp"
|
||||
#include "math.hpp"
|
||||
|
||||
typedef rk_handle_t rk_window_t;
|
||||
typedef rk_handle_t rk_shader_t;
|
||||
typedef rk_handle_t rk_input_t;
|
||||
typedef rk_handle_t rk_param_t;
|
||||
@ -28,8 +27,6 @@ typedef rk_handle_t rk_triangles_t;
|
||||
typedef rk_handle_t rk_vertices_t;
|
||||
typedef rk_handle_t rk_batch_t;
|
||||
|
||||
#define RK_FLAG(bit) (1 << (bit))
|
||||
|
||||
enum rk_texture_format : rk_uint {
|
||||
RK_TEXTURE_FORMAT_SRGB8_A8 = 0,
|
||||
RK_TEXTURE_FORMAT_RGBA8 = 1,
|
||||
@ -81,10 +78,9 @@ union rk_mesh {
|
||||
};
|
||||
};
|
||||
|
||||
RK_EXPORT rk_window_t rk_initialize(
|
||||
char const * name,
|
||||
rk_uint width,
|
||||
rk_uint height);
|
||||
RK_EXPORT void rk_render_initialize();
|
||||
|
||||
RK_EXPORT void rk_render_terminate();
|
||||
|
||||
RK_EXPORT rk_shader_t rk_load_shader(
|
||||
rk_uint const vert_nlines,
|
||||
@ -194,6 +190,4 @@ RK_EXPORT void rk_destroy_texture(
|
||||
RK_EXPORT void rk_destroy_shader(
|
||||
rk_shader_t shader);
|
||||
|
||||
RK_EXPORT void rk_terminate();
|
||||
|
||||
#endif // _RK_ENGINE_RENDER_H
|
||||
|
@ -13,17 +13,19 @@
|
||||
// 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/>.
|
||||
|
||||
#include "../render.hpp"
|
||||
#include "render_opengles.hpp"
|
||||
#include "../display/display_glx.hpp"
|
||||
#include <cstdio>
|
||||
|
||||
static rk_shader const * rk_current_shader = nullptr;
|
||||
static rk_vertices const * rk_current_vertices = nullptr;
|
||||
|
||||
static void rk_printf(char const * messsage) {
|
||||
printf("[RK_ENGINE] %s\n", messsage);
|
||||
static void rk_printf(char const * message) {
|
||||
printf("[RK] %s\n", message);
|
||||
}
|
||||
|
||||
#define rk_error(message) { if (glGetError() != GL_NO_ERROR) { rk_printf(message); } }
|
||||
#define rk_gl_error(_message) { if (glGetError() != GL_NO_ERROR) { printf("[GL] %s\n", (_message)); } }
|
||||
|
||||
static void rk_debug_message_callback(
|
||||
GLenum source,
|
||||
@ -33,21 +35,16 @@ static void rk_debug_message_callback(
|
||||
GLsizei length,
|
||||
GLchar const * message,
|
||||
void const * userParam) {
|
||||
printf("[RK_ENGINE][GL] (id=%d) %s\n", id, message);
|
||||
printf("[GL] (id=%d) %s\n", id, message);
|
||||
}
|
||||
|
||||
rk_window_t rk_initialize(
|
||||
char const * name,
|
||||
rk_uint width,
|
||||
rk_uint height) {
|
||||
rk_window_t const window = rk_create_context(name, width, height);
|
||||
if (window) {
|
||||
void rk_render_initialize() {
|
||||
GLubyte const * const vendor = glGetString(GL_VENDOR);
|
||||
GLubyte const * const renderer = glGetString(GL_RENDERER);
|
||||
printf("[RK_ENGINE] vendor: %s, renderer: %s\n", vendor, renderer);
|
||||
printf("[RK] vendor: %s, renderer: %s\n", vendor, renderer);
|
||||
GLubyte const * const version = glGetString(GL_VERSION);
|
||||
GLubyte const * const language = glGetString(GL_SHADING_LANGUAGE_VERSION);
|
||||
printf("[RK_ENGINE] version: %s, language: %s\n", version, language);
|
||||
printf("[RK] version: %s, language: %s\n", version, language);
|
||||
|
||||
glDebugMessageCallback(rk_debug_message_callback, nullptr);
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
@ -62,7 +59,8 @@ rk_window_t rk_initialize(
|
||||
glCullFace(GL_BACK);
|
||||
glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
|
||||
}
|
||||
return window;
|
||||
|
||||
void rk_render_terminate() {
|
||||
}
|
||||
|
||||
static void rk_print_shader_infolog(GLuint shader) {
|
||||
@ -104,18 +102,18 @@ rk_shader_t rk_load_shader(
|
||||
rk_printf("Compiling vertex shader...");
|
||||
glShaderSource(shader->vertex, vert_nlines, vert_lines, nullptr);
|
||||
glCompileShader(shader->vertex);
|
||||
rk_error("glCompileShader() failed.");
|
||||
rk_gl_error("glCompileShader() failed.");
|
||||
rk_print_shader_infolog(shader->vertex);
|
||||
rk_printf("Compiling fragment shader...");
|
||||
glShaderSource(shader->fragment, frag_nlines, frag_lines, nullptr);
|
||||
glCompileShader(shader->fragment);
|
||||
rk_error("glCompileShader() failed.");
|
||||
rk_gl_error("glCompileShader() failed.");
|
||||
rk_print_shader_infolog(shader->fragment);
|
||||
rk_printf("Linking program...");
|
||||
glAttachShader(shader->program, shader->vertex);
|
||||
glAttachShader(shader->program, shader->fragment);
|
||||
glLinkProgram(shader->program);
|
||||
rk_error("glLinkProgram() failed.");
|
||||
rk_gl_error("glLinkProgram() failed.");
|
||||
rk_print_program_infolog(shader->program);
|
||||
rk_printf("Done.");
|
||||
glReleaseShaderCompiler();
|
||||
@ -669,7 +667,6 @@ void rk_unselect_shader(
|
||||
}
|
||||
|
||||
void rk_end_frame() {
|
||||
rk_swap_buffers();
|
||||
}
|
||||
|
||||
void rk_destroy_batch(
|
||||
@ -730,7 +727,3 @@ void rk_destroy_shader(
|
||||
delete shader;
|
||||
}
|
||||
}
|
||||
|
||||
void rk_terminate() {
|
||||
rk_destroy_context();
|
||||
}
|
@ -16,8 +16,7 @@
|
||||
#ifndef _RK_ENGINE_RENDER_OPENGLES_H
|
||||
#define _RK_ENGINE_RENDER_OPENGLES_H
|
||||
|
||||
#include "../render.hpp"
|
||||
#include "render_context.hpp"
|
||||
#include "../types.hpp"
|
||||
#include <GLES3/gl32.h>
|
||||
#include <GLES3/gl3ext.h>
|
||||
#include <GLES3/gl3platform.h>
|
@ -20,6 +20,7 @@
|
||||
#include <cstdint>
|
||||
|
||||
#define RK_EXPORT extern "C"
|
||||
#define RK_FLAG(_bit) (1 << (_bit))
|
||||
|
||||
typedef bool rk_bool;
|
||||
typedef char rk_char;
|
||||
|
Loading…
Reference in New Issue
Block a user