aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2021-11-06 15:55:42 +0100
committerdec05eba <dec05eba@protonmail.com>2021-11-06 15:55:42 +0100
commitcb679636f77fe2a03e8dab3a511e28e1ab898316 (patch)
treeb03cada4a37a50eb50cef1e21c75c56d938868a8 /src
parente2e7c0bf0747d55967c4be6374f3611cd96babb6 (diff)
Fix exit being called when closing window, respond to wm ping, add is_open function to window
Diffstat (limited to 'src')
-rw-r--r--src/gl.c2
-rw-r--r--src/graphics/font.c7
-rw-r--r--src/graphics/text.c121
-rw-r--r--src/mgl.c12
-rw-r--r--src/system/utf8.c73
-rw-r--r--src/window/window.c27
6 files changed, 159 insertions, 83 deletions
diff --git a/src/gl.c b/src/gl.c
index 966023a..44572af 100644
--- a/src/gl.c
+++ b/src/gl.c
@@ -1,6 +1,5 @@
#include "../include/mgl/gl.h"
#include <dlfcn.h>
-/*#include <GL/gl.h>*/
#include <stdio.h>
typedef struct {
@@ -100,6 +99,7 @@ int mgl_gl_load(mgl_gl *self) {
{ &self->glXSwapIntervalEXT, "glXSwapIntervalEXT" },
{ &self->glXSwapIntervalMESA, "glXGetSwapIntervalMESA" },
{ &self->glXSwapIntervalSGI, "glXSwapIntervalSGI" },
+
{ NULL, NULL }
};
diff --git a/src/graphics/font.c b/src/graphics/font.c
index c017755..175c571 100644
--- a/src/graphics/font.c
+++ b/src/graphics/font.c
@@ -84,6 +84,11 @@ int mgl_font_load_from_file(mgl_font *self, const char *filepath, unsigned int c
continue;
}
+ /*if(!stbtt_PackFontRange(&pc, filedata.data, 0, self->character_size, 0x00004E00, self->num_packed_chars, self->packed_chars)) {
+ stbtt_PackEnd(&pc);
+ continue;
+ }*/
+
stbtt_PackEnd(&pc);
atlas_created = true;
break;
@@ -124,7 +129,7 @@ void mgl_font_unload(mgl_font *self) {
int mgl_font_get_glyph(const mgl_font *self, uint32_t codepoint, mgl_font_glyph *glyph) {
stbtt_packedchar *packed_chars = self->packed_chars;
- if(codepoint >= self->num_packed_chars)
+ if(codepoint < 0 || codepoint >= 0 + self->num_packed_chars)
return -1;
float x = 0.0f;
diff --git a/src/graphics/text.c b/src/graphics/text.c
index 31ae794..9804ab2 100644
--- a/src/graphics/text.c
+++ b/src/graphics/text.c
@@ -1,27 +1,12 @@
#include "../../include/mgl/graphics/text.h"
#include "../../include/mgl/graphics/font.h"
+#include "../../include/mgl/system/utf8.h"
#include "../../include/mgl/mgl.h"
-
-/*
- TODO: Do syntax highlight in constructor/mgl_text_set_string for optimization.
- Syntax highlighting should the first text that is visible in the text editor and find the start and end,
- and start syntax highlight from there until the last visible text.
- In that case there should also be a function to rerun the syntax highlighting, to update it
- when an asynchronous process finishes that parses code, such as a lsp client parsing C++ code which
- takes a while.
-*/
+#include <stdio.h>
#define TAB_WIDTH 4
-static bool default_syntax_highlight(void *userdata, const char *str, size_t size, mgl_color *color) {
- (void)userdata;
- (void)str;
- (void)size;
- (void)color;
- return false;
-}
-
-static float fmax(float a, float b) {
+static float max_float(float a, float b) {
return a >= b ? a : b;
}
@@ -33,63 +18,65 @@ static mgl_vec2f mgl_text_calculate_bounds(mgl_text *self) {
/* TODO: Combine this with the loop in mgl_text_draw */
mgl_font_glyph glyph;
float width = 0.0f;
- for(size_t i = 0; i < self->text_size; ++i) {
- unsigned char c = *(unsigned char*)&self->text[i];
- if((c >= 32 && c < 128)) {
- if(mgl_font_get_glyph(self->font, c, &glyph) == 0) {
- width += glyph.advance;
- bounds.x = fmax(bounds.x, width);
- }
- } else if(c == '\t') {
+ for(size_t i = 0; i < self->text_size;) {
+ unsigned char *cp = (unsigned char*)&self->text[i];
+ uint32_t codepoint = 0;
+ const size_t clen = mgl_utf8_decode(cp, &codepoint);
+ if(codepoint == '\t') {
if(mgl_font_get_glyph(self->font, ' ', &glyph) == 0) {
width += (glyph.advance * TAB_WIDTH);
- bounds.x = fmax(bounds.x, width);
+ bounds.x = max_float(bounds.x, width);
}
- } else if(c == '\n') {
+ } else if(codepoint == '\n') {
width = 0.0f;
bounds.y += self->font->character_size;
+ } else {
+ if(mgl_font_get_glyph(self->font, codepoint, &glyph) == 0) {
+ width += glyph.advance;
+ bounds.x = max_float(bounds.x, width);
+ }
}
+ i += clen;
}
return bounds;
}
-int mgl_text_init(mgl_text *self, mgl_font *font, const char *str, size_t str_size, mgl_text_options *options) {
+int mgl_text_init(mgl_text *self, mgl_font *font, const char *str, size_t str_size) {
self->font = font;
- mgl_text_set_string(self, str, str_size);
self->color = (mgl_color){ 255, 255, 255, 255 };
self->position = (mgl_vec2f){ 0.0f, 0.0f };
-
- if(options) {
- self->options = *options;
- } else {
- self->options.userdata = NULL;
- self->options.before_syntax_highlight = NULL;
- }
-
- if(!options || !options->syntax_highlight)
- self->options.syntax_highlight = default_syntax_highlight;
-
- return 0;
+ self->text = NULL;
+ self->text_size = 0;
+ return mgl_text_set_string(self, str, str_size);
}
void mgl_text_deinit(mgl_text *self) {
(void)self;
}
-void mgl_text_set_string(mgl_text *self, const char *str, size_t str_size) {
+int mgl_text_set_string(mgl_text *self, const char *str, size_t str_size) {
+ if(str) {
+ if(!mgl_utf8_is_valid((const unsigned char*)str, str_size)) {
+ fprintf(stderr, "Error: mgl_text_set_string received an invalid utf8 string\n");
+ return -1;
+ }
+ }
+
self->text = str;
self->text_size = str_size;
if(self->text && self->text_size > 0 && self->font)
self->bounds = mgl_text_calculate_bounds(self);
else
self->bounds = (mgl_vec2f){ 0.0f, 0.0f };
+
+ return 0;
}
void mgl_text_set_font(mgl_text *self, mgl_font *font) {
self->font = font;
if(self->font) {
- if(self->bounds.x < 0.001f && self->bounds.y < 0.001f)
+ if(self->bounds.x < 0.001f && self->bounds.y < 0.001f && self->text && self->text_size > 0)
self->bounds = mgl_text_calculate_bounds(self);
} else {
self->bounds = (mgl_vec2f){ 0.0f, 0.0f };
@@ -122,19 +109,6 @@ static void mgl_text_draw_glyph(mgl_context *context, mgl_font_glyph *glyph, mgl
context->gl.glVertex3f(position.x + glyph->position.x, position.y + glyph->position.y + glyph->size.y, 0.0f);
}
-static void mgl_text_run_syntax_highlight(mgl_context *context, mgl_text *text, const char *str, size_t size, mgl_color *color, bool *color_changed) {
- if(text->options.syntax_highlight(text->options.userdata, str, size, color)) {
- context->gl.glColor4ub(color->r, color->g, color->b, color->a);
- *color_changed = true;
- } else {
- if(*color_changed) {
- *color = text->color;
- context->gl.glColor4ub(color->r, color->g, color->b, color->a);
- *color_changed = false;
- }
- }
-}
-
/* TODO: Use opengl buffer object instead */
/* TODO: Cache texture bind to not bind texture if its already bound and do not bind texture 0 */
void mgl_text_draw(mgl_context *context, mgl_text *text) {
@@ -145,34 +119,27 @@ void mgl_text_draw(mgl_context *context, mgl_text *text) {
mgl_vec2f position = text->position;
position.y += text->font->character_size;
- mgl_color color = text->color;
- bool color_changed = false;
-
- if(text->options.before_syntax_highlight)
- text->options.before_syntax_highlight(text->options.userdata);
-
context->gl.glColor4ub(text->color.r, text->color.g, text->color.b, text->color.a);
context->gl.glBindTexture(GL_TEXTURE_2D, text->font->texture.id);
context->gl.glBegin(GL_QUADS);
- for(size_t i = 0; i < text->text_size; ++i) {
- unsigned char c = *(unsigned char*)&text->text[i];
- if((c >= 32 && c < 128)) {
- if(mgl_font_get_glyph(text->font, c, &glyph) == 0) {
- mgl_text_run_syntax_highlight(context, text, (const char*)&c, 1, &color, &color_changed);
- mgl_text_draw_glyph(context, &glyph, position);
- position.x += glyph.advance;
- }
- } else if(c == '\t') {
+ for(size_t i = 0; i < text->text_size;) {
+ unsigned char *cp = (unsigned char*)&text->text[i];
+ uint32_t codepoint = 0;
+ const size_t clen = mgl_utf8_decode(cp, &codepoint);
+ if(codepoint == '\t') {
if(mgl_font_get_glyph(text->font, ' ', &glyph) == 0) {
- for(int i = 0; i < TAB_WIDTH; ++i) {
- mgl_text_draw_glyph(context, &glyph, position);
- position.x += glyph.advance;
- }
+ position.x += (glyph.advance * TAB_WIDTH);
}
- } else if(c == '\n') {
+ } else if(codepoint == '\n') {
position.x = text->position.x;
position.y += text->font->character_size;
+ } else {
+ if(mgl_font_get_glyph(text->font, codepoint, &glyph) == 0) {
+ mgl_text_draw_glyph(context, &glyph, position);
+ position.x += glyph.advance;
+ }
}
+ i += clen;
}
context->gl.glEnd();
context->gl.glBindTexture(GL_TEXTURE_2D, 0);
diff --git a/src/mgl.c b/src/mgl.c
index 7940def..02fe8a5 100644
--- a/src/mgl.c
+++ b/src/mgl.c
@@ -74,6 +74,8 @@ int mgl_init(void) {
return -1;
}
+ context.net_wm_ping_atom = XInternAtom(context.connection, "_NET_WM_PING", True);
+
if(mgl_gl_load(&context.gl) != 0) {
mgl_deinit();
return -1;
@@ -90,9 +92,6 @@ int mgl_init(void) {
void mgl_deinit(void) {
if(init_count == 1) {
if(context.connection) {
- glx_context_deinit();
- mgl_gl_unload(&context.gl);
-
XSetIOErrorHandler(prev_xioerror);
prev_xioerror = NULL;
@@ -101,6 +100,13 @@ void mgl_deinit(void) {
XCloseDisplay(context.connection);
context.connection = NULL;
+
+ /*
+ GLX needs to be unloaded after closing the display on nvidia because
+ nvidia registers cleanup callbacks on exit, that uses the x11 display.
+ */
+ glx_context_deinit();
+ mgl_gl_unload(&context.gl);
}
}
diff --git a/src/system/utf8.c b/src/system/utf8.c
new file mode 100644
index 0000000..cb14691
--- /dev/null
+++ b/src/system/utf8.c
@@ -0,0 +1,73 @@
+#include "../../include/mgl/system/utf8.h"
+
+/* TODO: Optimize (remove branching, etc) */
+bool mgl_utf8_is_valid(const unsigned char *str, size_t size) {
+ size_t i = 0;
+ while(i < size) {
+ size_t codepoint_length = 0;
+ const unsigned char b = str[i];
+
+ if((b & 0x80) == 0)
+ codepoint_length = 1;
+ else if((b & 0xE0) == 0xC0)
+ codepoint_length = 2;
+ else if((b & 0xF0) == 0xE0)
+ codepoint_length = 3;
+ else if((b & 0xF8) == 0xF0)
+ codepoint_length = 4;
+ else
+ return false;
+
+ const size_t next = i + codepoint_length;
+ if(next > size)
+ return false;
+
+ /* TODO: Remove this overflow check? */
+ /* Check overflow */
+ if(next <= i)
+ return false;
+
+ ++i;
+ for(; i < next; ++i) {
+ if((str[i] & 0xC0) != 0x80)
+ return false;
+ }
+ }
+ return true;
+}
+
+static inline size_t utf8_get_codepoint_length(unsigned char b) {
+ const unsigned int length1 = b >> 7;
+ const unsigned int length2 = length1 & ((b & 0x40) >> 6);
+ const unsigned int length3 = length2 & ((b & 0x20) >> 5);
+ const unsigned int length4 = length3 & ((b & 0x10) >> 4);
+ return (length1 ^ 0x01) + length1 + length2 + length3 + length4;
+}
+
+/* TODO: Optimize (remove branching, etc) */
+size_t mgl_utf8_decode(const unsigned char *str, uint32_t *decoded_codepoint) {
+ const size_t length = utf8_get_codepoint_length(str[0]);
+ uint32_t codepoint;
+ switch(length) {
+ case 1:
+ codepoint = (uint32_t)(str[0] & 0x7F);
+ break;
+ case 2:
+ codepoint = ((uint32_t)(str[0] & 0x1F) << 6);
+ codepoint |= (uint32_t)(str[1] & 0x3F);
+ break;
+ case 3:
+ codepoint = ((uint32_t)(str[0] & 0x0F) << 12);
+ codepoint |= ((uint32_t)(str[1] & 0x3F) << 6);
+ codepoint |= (uint32_t)(str[2] & 0x3F);
+ break;
+ case 4:
+ codepoint = ((uint32_t)(str[0] & 0x07) << 18);
+ codepoint |= ((uint32_t)(str[1] & 0x3F) << 12);
+ codepoint |= ((uint32_t)(str[2] & 0x3F) << 6);
+ codepoint |= (uint32_t)(str[3] & 0x3F);
+ break;
+ }
+ *decoded_codepoint = codepoint;
+ return length;
+}
diff --git a/src/window/window.c b/src/window/window.c
index 30abebb..b7f9fd8 100644
--- a/src/window/window.c
+++ b/src/window/window.c
@@ -5,6 +5,8 @@
#include <errno.h>
#include <stdio.h>
+/* TODO: Use gl OML present for other platforms than nvidia? nvidia doesn't support present yet */
+
/* TODO: check for glx swap control extension string (GLX_EXT_swap_control, etc) */
static void set_vertical_sync_enabled(Window window, int enabled) {
int result = 0;
@@ -95,6 +97,12 @@ static int mgl_window_init(mgl_window *self, const char *title, int width, int h
XMapWindow(context->connection, self->window);
}
+ /* TODO: Call XGetWMProtocols and add wm_delete_window_atom on top, to not overwrite existing wm protocol atoms */
+ Atom wm_protocol_atoms[2] = {
+ context->wm_delete_window_atom,
+ context->net_wm_ping_atom
+ };
+ XSetWMProtocols(context->connection, self->window, wm_protocol_atoms, 2);
XFlush(context->connection);
/* TODO: Check for failure? */
@@ -118,6 +126,7 @@ static int mgl_window_init(mgl_window *self, const char *title, int width, int h
XQueryPointer(context->connection, self->window, &dummy_w, &dummy_w, &dummy_i, &dummy_i, &self->cursor_position.x, &self->cursor_position.y, &dummy_u);
mgl_window_on_resize(self, gwa.width, gwa.height);
+ self->open = true;
return 0;
}
@@ -246,7 +255,7 @@ static bool mgl_window_on_receive_event(mgl_window *self, XEvent *xev, mgl_event
event->size.height = self->size.y;
return true;
}
- return false;
+ break;
}
case MotionNotify: {
while(XCheckTypedWindowEvent(mgl_get_context()->connection, self->window, MotionNotify, xev)) {}
@@ -258,6 +267,18 @@ static bool mgl_window_on_receive_event(mgl_window *self, XEvent *xev, mgl_event
event->mouse_move.y = self->cursor_position.y;
return true;
}
+ case ClientMessage: {
+ mgl_context *context = mgl_get_context();
+ if(xev->xclient.format == 32 && (unsigned long)xev->xclient.data.l[0] == context->wm_delete_window_atom) {
+ event->type = MGL_EVENT_CLOSED;
+ self->open = false;
+ return true;
+ } else if(xev->xclient.format == 32 && (unsigned long)xev->xclient.data.l[0] == context->net_wm_ping_atom) {
+ xev->xclient.window = DefaultRootWindow(context->connection);
+ XSendEvent(context->connection, DefaultRootWindow(context->connection), False, SubstructureNotifyMask | SubstructureRedirectMask, xev);
+ }
+ break;
+ }
}
/*fprintf(stderr, "unhandled event type: %d\n", xev->type);*/
event->type = MGL_EVENT_UNKNOWN;
@@ -300,3 +321,7 @@ void mgl_window_set_view(mgl_window *self, mgl_view *new_view) {
void mgl_window_get_view(mgl_window *self, mgl_view *view) {
*view = self->view;
}
+
+bool mgl_window_is_open(const mgl_window *self) {
+ return self->open;
+}