From cb679636f77fe2a03e8dab3a511e28e1ab898316 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sat, 6 Nov 2021 15:55:42 +0100 Subject: Fix exit being called when closing window, respond to wm ping, add is_open function to window --- src/gl.c | 2 +- src/graphics/font.c | 7 ++- src/graphics/text.c | 121 +++++++++++++++++++--------------------------------- src/mgl.c | 12 ++++-- src/system/utf8.c | 73 +++++++++++++++++++++++++++++++ src/window/window.c | 27 +++++++++++- 6 files changed, 159 insertions(+), 83 deletions(-) create mode 100644 src/system/utf8.c (limited to 'src') 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 -/*#include */ #include 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 #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 #include +/* 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; +} -- cgit v1.2.3