summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/event.c2
-rw-r--r--core/meson.build16
-rw-r--r--core/renderer/gles3.c92
-rw-r--r--core/surface/egl.c147
-rw-r--r--core/window.c141
-rw-r--r--core/window/xcb.c232
6 files changed, 630 insertions, 0 deletions
diff --git a/core/event.c b/core/event.c
new file mode 100644
index 0000000..e559e8d
--- /dev/null
+++ b/core/event.c
@@ -0,0 +1,2 @@
+#include "core/event.h"
+#include "core/window.h"
diff --git a/core/meson.build b/core/meson.build
new file mode 100644
index 0000000..b58e1ef
--- /dev/null
+++ b/core/meson.build
@@ -0,0 +1,16 @@
+core_sources = files('window.c', 'event.c')
+core_deps = []
+
+if os == 'linux'
+ core_sources += ['window/xcb.c', 'surface/egl.c', 'renderer/gles3.c']
+ core_deps += [ dependency('egl'), dependency('glesv2'), dependency('xcb') ]
+endif
+
+core_library = library(
+ 'RuimCore',
+ core_sources,
+ include_directories: ruim_include,
+ dependencies: core_deps,
+ link_with: internal_lib,
+ install: true,
+)
diff --git a/core/renderer/gles3.c b/core/renderer/gles3.c
new file mode 100644
index 0000000..5dd1538
--- /dev/null
+++ b/core/renderer/gles3.c
@@ -0,0 +1,92 @@
+#include "core/renderer.h"
+#include "core/window.h"
+#include "internal/utils.h"
+
+#include <GLES2/gl2.h>
+#include <GLES3/gl3.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+GLuint load_shader(const char *code, uint type) {
+ int success;
+ char infoLog[512];
+ GLuint shader = glCreateShader(type);
+
+ glShaderSource(shader, 1, &code, NULL);
+ glCompileShader(shader);
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
+
+ if (!success) {
+ glGetShaderInfoLog(shader, 512, NULL, infoLog);
+ printf("Shader compilation failed: %s\n", infoLog);
+ }
+
+ return shader;
+}
+
+struct ruim_rendererGLES RuimSetupGLES(void) {
+ int success;
+ char infoLog[512];
+ struct ruim_rendererGLES renderer;
+ float vertices[] = {-0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f, 0.0f, 0.5f, 0.0f};
+
+ GLuint vertexShdr =
+ load_shader("#version 300 es\n"
+ "layout (location = 0) in vec3 aPos;\n"
+ "void main()\n"
+ "{\n"
+ "gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
+ "}\0",
+ GL_VERTEX_SHADER);
+
+ GLuint fragmentShdr = load_shader("#version 300 es\n"
+ "precision mediump float;\n"
+ "out vec4 FragColor;\n"
+ "void main()\n"
+ "{\n"
+ "FragColor = vec4(1.0, 0.5, 0.2, 1.0);\n"
+ "}\0",
+ GL_FRAGMENT_SHADER);
+
+ glGenBuffers(1, &renderer.VBO);
+ glGenVertexArrays(1, &renderer.VAO);
+
+ renderer.shaderProgram = glCreateProgram();
+ glAttachShader(renderer.shaderProgram, vertexShdr);
+ glAttachShader(renderer.shaderProgram, fragmentShdr);
+ glLinkProgram(renderer.shaderProgram);
+ glGetProgramiv(renderer.shaderProgram, GL_LINK_STATUS, &success);
+ if (!success) {
+ glGetProgramInfoLog(renderer.shaderProgram, 512, NULL, infoLog);
+ printf("Program linking failed: %s\n", infoLog);
+ }
+
+ glDeleteShader(vertexShdr);
+ glDeleteShader(fragmentShdr);
+
+ glBindVertexArray(renderer.VAO);
+
+ glBindBuffer(GL_ARRAY_BUFFER, renderer.VBO);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
+
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0);
+ glEnableVertexAttribArray(0);
+
+ glBindVertexArray(0);
+
+ return renderer;
+}
+
+void RuimResizeGLES(struct ruim_rendererGLES *renderer, int width, int height) {
+ glViewport(0, 0, width, height);
+}
+
+void RuimRenderGLES(struct ruim_rendererGLES *renderer) {
+ glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glUseProgram(renderer->shaderProgram);
+ glBindVertexArray(renderer->VAO);
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+}
diff --git a/core/surface/egl.c b/core/surface/egl.c
new file mode 100644
index 0000000..f0370ce
--- /dev/null
+++ b/core/surface/egl.c
@@ -0,0 +1,147 @@
+#include "core/event.h"
+#include "core/renderer.h"
+#include "core/window.h"
+#include "internal/utils.h"
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* Get human-readable EGL error string */
+const char *egl_error_string(EGLint error) {
+ switch (error) {
+ case EGL_SUCCESS:
+ return "EGL_SUCCESS";
+ case EGL_NOT_INITIALIZED:
+ return "EGL_NOT_INITIALIZED";
+ case EGL_BAD_ACCESS:
+ return "EGL_BAD_ACCESS";
+ case EGL_BAD_ALLOC:
+ return "EGL_BAD_ALLOC";
+ case EGL_BAD_ATTRIBUTE:
+ return "EGL_BAD_ATTRIBUTE";
+ case EGL_BAD_CONFIG:
+ return "EGL_BAD_CONFIG";
+ case EGL_BAD_CONTEXT:
+ return "EGL_BAD_CONTEXT";
+ case EGL_BAD_CURRENT_SURFACE:
+ return "EGL_BAD_CURRENT_SURFACE";
+ case EGL_BAD_DISPLAY:
+ return "EGL_BAD_DISPLAY";
+ case EGL_BAD_MATCH:
+ return "EGL_BAD_MATCH";
+ case EGL_BAD_NATIVE_PIXMAP:
+ return "EGL_BAD_NATIVE_PIXMAP";
+ case EGL_BAD_NATIVE_WINDOW:
+ return "EGL_BAD_NATIVE_WINDOW";
+ case EGL_BAD_PARAMETER:
+ return "EGL_BAD_PARAMETER";
+ case EGL_BAD_SURFACE:
+ return "EGL_BAD_SURFACE";
+ case EGL_CONTEXT_LOST:
+ return "EGL_CONTEXT_LOST";
+ default:
+ return "UNKNOWN_EGL_ERROR";
+ }
+}
+
+/* Crash with EGL error information
+ * */
+void crash_egl(const char *expr, const char *file, int line) {
+ EGLint error = eglGetError();
+ fprintf(stderr, "EGL Error at %s:%d\n", file, line);
+ fprintf(stderr, "Expression: %s\n", expr);
+ fprintf(stderr, "Error code: 0x%x (%s)\n", error, egl_error_string(error));
+ abort();
+}
+
+/* Macro for checking EGL boolean returns (EGL_TRUE/EGL_FALSE) */
+#define assert_egl(expr) \
+ do { \
+ if (!(expr)) \
+ crash_egl(#expr, __FILE__, __LINE__); \
+ } while (0)
+
+void RuimInitEGL(RuimToplevel *toplevel) {
+ RuimSurfaceEGL ruimSurface;
+ EGLint version_major, version_minor;
+ EGLConfig config;
+ EGLint num_config;
+ struct toplevel_x11 *x11_toplevel = (struct toplevel_x11 *)toplevel->data;
+ struct backend_x11 *x11_backend =
+ (struct backend_x11 *)toplevel->backend->data;
+ EGLint constraints[] = {
+ EGL_SURFACE_TYPE,
+ EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE,
+ EGL_OPENGL_ES3_BIT,
+ EGL_RED_SIZE,
+ 8,
+ EGL_GREEN_SIZE,
+ 8,
+ EGL_BLUE_SIZE,
+ 8,
+ EGL_ALPHA_SIZE,
+ 8,
+ EGL_DEPTH_SIZE,
+ 24,
+ EGL_NONE,
+ };
+
+ /* egl-contexts collect all state descriptions needed required for operation
+ */
+ EGLint ctxattr[] = {EGL_CONTEXT_MAJOR_VERSION, 3, EGL_CONTEXT_MINOR_VERSION,
+ 0, EGL_NONE};
+
+ const char *client_extensions =
+ eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
+
+ if (!client_extensions) {
+ abort();
+ }
+ if (!strstr(client_extensions, "EGL_EXT_platform_xcb")) {
+ abort();
+ }
+
+ ruimSurface.display = eglGetPlatformDisplay(EGL_PLATFORM_XCB_EXT,
+ x11_backend->connection, NULL);
+
+ assert_egl(ruimSurface.display);
+
+ assert_egl(
+ eglInitialize(ruimSurface.display, &version_major, &version_minor));
+ ;
+ assert_egl(eglChooseConfig(ruimSurface.display, constraints, &config, 1,
+ &num_config));
+ assert_egl(eglBindAPI(EGL_OPENGL_ES_API));
+
+ ruimSurface.surface = eglCreatePlatformWindowSurface(
+ ruimSurface.display, config, &x11_toplevel->window, NULL);
+ assert_egl(ruimSurface.surface);
+
+ ruimSurface.context =
+ eglCreateContext(ruimSurface.display, config, EGL_NO_CONTEXT, ctxattr);
+ assert_egl(ruimSurface.context);
+
+ assert_egl(eglMakeCurrent(ruimSurface.display, ruimSurface.surface,
+ ruimSurface.surface, ruimSurface.context));
+ assert_egl(eglSwapInterval(ruimSurface.display, 1));
+
+ x11_backend->surface = ruimSurface;
+}
+
+EGLBoolean RuimSwapEGL(RuimToplevel *toplevel) {
+ struct backend_x11 *x11_backend =
+ (struct backend_x11 *)toplevel->backend->data;
+
+ return eglSwapBuffers(x11_backend->surface.display,
+ x11_backend->surface.surface);
+}
+
+void RuimDeinitEGL(RuimSurfaceEGL *eglCtx) {
+ assert_egl(eglMakeCurrent(eglCtx->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT));
+ assert_egl(eglDestroyContext(eglCtx->display, eglCtx->context));
+ assert_egl(eglDestroySurface(eglCtx->display, eglCtx->surface));
+ assert_egl(eglTerminate(eglCtx->display));
+}
diff --git a/core/window.c b/core/window.c
new file mode 100644
index 0000000..6db3f5b
--- /dev/null
+++ b/core/window.c
@@ -0,0 +1,141 @@
+#include "core/window.h"
+#include "core/event.h"
+#include "core/renderer.h"
+#include "core/types.h"
+#include "internal/utils.h"
+
+/* other stuff */
+
+int RuimToplevelBackendInit(RuimToplevelBackend *backend) {
+ if (backend == NULL) {
+ crash_error("backend must be non-null");
+ }
+
+ if (backend->type == 0) {
+ /* TODO: select default one based on platform */
+ backend->type = RUIM_TOPLEVEL_X11;
+ }
+
+ switch (backend->type) {
+#ifdef RUIM_PLATFORM_LINUX
+ case RUIM_TOPLEVEL_X11: {
+ return _init_xcb_backend(backend);
+ } break;
+#endif
+ default:
+ printf("%d\n", backend->type);
+ crash_error("unrecognized backend type");
+ }
+
+ crash_error("unreachable");
+ return -1;
+}
+
+int RuimToplevelBackendDeinit(RuimToplevelBackend *backend) {
+ if (backend == NULL || backend->type == 0) {
+ crash_error("some crash");
+ }
+
+ switch (backend->type) {
+#ifdef RUIM_PLATFORM_LINUX
+ case RUIM_TOPLEVEL_X11: {
+ return _deinit_xcb_backend(backend);
+ } break;
+#endif
+ default:
+ crash_error("some crash");
+ }
+
+ return 0;
+}
+
+int RuimToplevelCreate(RuimToplevel *toplevel, RuimToplevelType type) {
+ if (type && type != RUIM_TOPLEVEL_WINDOW) {
+ crash_error("TODO: toplevel must be a window");
+ }
+
+ if (toplevel->backend == NULL) {
+ crash_error("TODO: backend must be set");
+ }
+
+ switch (toplevel->backend->type) {
+#ifdef RUIM_PLATFORM_LINUX
+ case RUIM_TOPLEVEL_X11: {
+ return _create_toplevel_xcb(toplevel);
+ } break;
+#endif
+ default:
+ crash_error("unknown backend type");
+ }
+
+ return 0;
+}
+
+int RuimToplevelDisplay(RuimToplevel *toplevel) {
+ if (toplevel == NULL) {
+ crash_error("toplevel must be non-null");
+ }
+
+ switch (toplevel->backend->type) {
+#ifdef RUIM_PLATFORM_LINUX
+ case RUIM_TOPLEVEL_X11: {
+ return _display_toplevel_xcb(toplevel);
+ } break;
+#endif
+ default:
+ crash_error("some crash");
+ }
+
+ return 0;
+}
+
+int RuimToplevelBackendPoll(RuimToplevelBackend *backend, RuimEvent *event) {
+ switch (backend->type) {
+#ifdef RUIM_PLATFORM_LINUX
+ case RUIM_TOPLEVEL_X11: {
+ if (_poll_event_xcb(backend, event) != 0) {
+ crash_error("xcb connection error");
+ }
+ } break;
+#endif
+ default:
+ crash_error("unknown backend type");
+ }
+
+ if (event->type == RUIM_EVENT_NOTHING) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+int RuimToplevelWaitForEvent(RuimToplevel *toplevel) {
+ switch (toplevel->backend->type) {
+#ifdef RUIM_PLATFORM_LINUX
+ case RUIM_TOPLEVEL_X11: {
+ return _wait_event_xcb(toplevel);
+ } break;
+#endif
+ default:
+ crash_error("unknown backend type");
+ }
+
+ crash_error("unreachable");
+ return -1;
+}
+
+int RuimToplevelDestroy(RuimToplevel *toplevel) {
+
+ switch (toplevel->backend->type) {
+#ifdef RUIM_PLATFORM_LINUX
+ case RUIM_TOPLEVEL_X11: {
+ return _destroy_toplevel_xcb(toplevel);
+ } break;
+#endif
+ default:
+ crash_error("unknown backend type");
+ }
+
+ crash_error("unreachable");
+ return -1;
+}
diff --git a/core/window/xcb.c b/core/window/xcb.c
new file mode 100644
index 0000000..94196ec
--- /dev/null
+++ b/core/window/xcb.c
@@ -0,0 +1,232 @@
+#include "core/types.h"
+#include "core/window.h"
+#include "internal/utils.h"
+
+#include "core/event.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <xcb/xcb.h>
+#include <xcb/xproto.h>
+
+/* TODO(roberto): put the correct error values */
+static void _xcb_handle_error(int error) {
+ switch (error) {
+ case XCB_WINDOW:
+ crash_error("Invalid window error\n");
+ case XCB_PIXMAP:
+ crash_error("Invalid pixmap error\n");
+ case XCB_ATOM:
+ crash_error("Invalid atom error\n");
+ case XCB_VALUE:
+ crash_error("Invalid value error\n");
+ case XCB_MATCH:
+ crash_error("Match error (incompatible parameters)\n");
+ case XCB_DRAWABLE:
+ crash_error("Invalid drawable error\n");
+ case XCB_ACCESS:
+ crash_error("Access denied error\n");
+ case XCB_ALLOC:
+ crash_error("Resource allocation error\n");
+ default:
+ crash_error("Unknown error\n");
+ }
+}
+
+static xcb_atom_t _get_atom_xcb(xcb_connection_t *conn, const char *name) {
+ xcb_intern_atom_cookie_t cookie;
+ xcb_intern_atom_reply_t *reply;
+ xcb_atom_t atom;
+
+ cookie = xcb_intern_atom(conn, 0, strlen(name), name);
+ reply = xcb_intern_atom_reply(conn, cookie, NULL);
+
+ if (reply) {
+ atom = reply->atom;
+ free(reply);
+ return atom;
+ }
+
+ return XCB_ATOM_NONE;
+}
+
+int _init_xcb_backend(RuimToplevelBackend *backend) {
+ struct backend_x11 *x11;
+ xcb_screen_iterator_t iter;
+ int error = 0;
+
+ x11 = (struct backend_x11 *)malloc(sizeof(struct backend_x11));
+ x11->connection = xcb_connect(NULL, &x11->screen_num);
+ if ((error = xcb_connection_has_error(x11->connection)) != 0) {
+ return error;
+ }
+
+ x11->poll.fd = xcb_get_file_descriptor(x11->connection);
+ x11->poll.events = POLLIN;
+
+ x11->setup = xcb_get_setup(x11->connection);
+ iter = xcb_setup_roots_iterator(x11->setup);
+ x11->screen = iter.data;
+
+ x11->wm_protocols = _get_atom_xcb(x11->connection, "WM_PROTOCOLS");
+ x11->wm_delete_window = _get_atom_xcb(x11->connection, "WM_DELETE_WINDOW");
+
+ backend->data = (void *)x11;
+
+ return 0;
+}
+
+int _deinit_xcb_backend(RuimToplevelBackend *backend) {
+ struct backend_x11 *x11 = (struct backend_x11 *)backend->data;
+ xcb_disconnect(x11->connection);
+
+ free(backend->data);
+ return 0;
+}
+
+/* TODO: support more toplevel types */
+int _create_toplevel_xcb(RuimToplevel *toplevel) {
+ int error;
+ struct backend_x11 *x11_backend;
+ struct toplevel_x11 *x11_toplevel;
+
+ x11_toplevel = (struct toplevel_x11 *)malloc(sizeof(struct toplevel_x11));
+ x11_toplevel->event_values = (uint32_t *)malloc(sizeof(uint32_t) * 2);
+ x11_backend = (struct backend_x11 *)toplevel->backend->data;
+ if ((error = xcb_connection_has_error(x11_backend->connection)) != 0) {
+ return error;
+ }
+
+ x11_toplevel->window = xcb_generate_id(x11_backend->connection);
+ x11_toplevel->event_mask = XCB_CW_EVENT_MASK;
+ x11_toplevel->event_values[0] =
+ XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS |
+ XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_STRUCTURE_NOTIFY;
+
+ xcb_create_window(x11_backend->connection, XCB_COPY_FROM_PARENT,
+ x11_toplevel->window, x11_backend->screen->root, 0, 0, 150,
+ 150, 10, XCB_WINDOW_CLASS_INPUT_OUTPUT,
+ x11_backend->screen->root_visual, x11_toplevel->event_mask,
+ x11_toplevel->event_values);
+
+ xcb_change_property(x11_backend->connection, XCB_PROP_MODE_REPLACE,
+ x11_toplevel->window, x11_backend->wm_protocols,
+ XCB_ATOM_ATOM, 32, 1, &x11_backend->wm_delete_window);
+
+ xcb_flush(x11_backend->connection);
+
+ toplevel->data = (void *)x11_toplevel;
+
+ return 0;
+}
+
+int _display_toplevel_xcb(RuimToplevel *toplevel) {
+ struct backend_x11 *x11_backend =
+ (struct backend_x11 *)toplevel->backend->data;
+ struct toplevel_x11 *x11_toplevel = (struct toplevel_x11 *)toplevel->data;
+
+ xcb_map_window(x11_backend->connection, x11_toplevel->window);
+ /* FIXME(roberto): decide where to flush the connection */
+ return xcb_flush(x11_backend->connection) > 0;
+}
+
+int _poll_event_xcb(RuimToplevelBackend *backend, RuimEvent *event) {
+ struct backend_x11 *x11_backend = (struct backend_x11 *)backend->data;
+ xcb_generic_event_t *x11_event;
+ int error = 0;
+ /* FIXME(roberto): make it actually poll */
+ /* FIXME(roberto): too laggy */
+ x11_event = xcb_poll_for_event(x11_backend->connection);
+
+ if (x11_event != NULL) {
+ switch (x11_event->response_type & ~0x80) {
+ case XCB_EXPOSE: {
+ RuimEventRedraw redraw;
+ xcb_configure_notify_event_t *cfg =
+ (xcb_configure_notify_event_t *)x11_event;
+
+ event->type = RUIM_EVENT_REDRAW;
+ redraw.width = cfg->width;
+ redraw.height = cfg->height;
+ redraw.x = cfg->x;
+ redraw.y = cfg->y;
+
+ event->data.redraw = redraw;
+ } break;
+ case XCB_CLIENT_MESSAGE: {
+ xcb_client_message_event_t *cm = (xcb_client_message_event_t *)x11_event;
+
+ if (cm->data.data32[0] == x11_backend->wm_delete_window) {
+ event->type = RUIM_EVENT_QUIT;
+ }
+ } break;
+ case XCB_KEY_PRESS: {
+ xcb_key_press_event_t *key = (xcb_key_press_event_t *)x11_event;
+
+ event->type = RUIM_EVENT_KEYDOWN;
+ event->data.mouse.keycode = key->state;
+ event->data.mouse.x = key->event_x;
+ event->data.mouse.y = key->event_y;
+ } break;
+ case XCB_KEY_RELEASE: {
+ xcb_key_release_event_t *key = (xcb_key_press_event_t *)x11_event;
+
+ event->type = RUIM_EVENT_KEYUP;
+ event->data.mouse.keycode = key->state;
+ event->data.mouse.x = key->event_x;
+ event->data.mouse.y = key->event_y;
+ } break;
+ case XCB_BUTTON_PRESS: {
+ xcb_button_press_event_t *button = (xcb_button_press_event_t *)x11_event;
+
+ event->type = RUIM_EVENT_MOUSEDOWN;
+ event->data.mouse.keycode = button->state;
+ event->data.mouse.x = button->event_x;
+ event->data.mouse.y = button->event_y;
+ } break;
+ case XCB_BUTTON_RELEASE: {
+ xcb_button_release_event_t *button =
+ (xcb_button_press_event_t *)x11_event;
+
+ event->type = RUIM_EVENT_MOUSEUP;
+ event->data.mouse.keycode = button->state;
+ event->data.mouse.x = button->event_x;
+ event->data.mouse.y = button->event_y;
+ } break;
+ case XCB_CONFIGURE_NOTIFY: {
+ xcb_configure_notify_event_t *cfg =
+ (xcb_configure_notify_event_t *)x11_event;
+
+ event->type = RUIM_EVENT_WINDOW;
+ event->data.window.width = cfg->width;
+ event->data.window.height = cfg->height;
+ event->data.window.x = cfg->x;
+ event->data.window.y = cfg->y;
+ } break;
+ default:
+ break;
+ }
+ } else if ((error = xcb_connection_has_error(x11_backend->connection)) != 0) {
+ _xcb_handle_error(error);
+ return error;
+ } else {
+ event->type = RUIM_EVENT_NOTHING;
+ }
+
+ free(x11_event);
+ return 0;
+}
+
+int _wait_event_xcb(RuimToplevel *toplevel) {
+ struct backend_x11 *backend = (struct backend_x11 *)toplevel->backend->data;
+ poll(&backend->poll, 1, 1);
+
+ return 0;
+}
+
+int _destroy_toplevel_xcb(RuimToplevel *toplevel) {
+ free(toplevel->data);
+
+ return 0;
+}