From 111f0ba3f4a4f14d39c8e3f7c00f13e852f47a51 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sun, 31 Oct 2021 12:50:05 +0100 Subject: Start on syntax highlighting, output correct key button event --- src/graphics/font.c | 9 ++-- src/graphics/text.c | 67 ++++++++++++++++++++++++----- src/mgl.c | 25 +++++++++-- src/window/window.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 195 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/src/graphics/font.c b/src/graphics/font.c index 8344a04..2ed54d2 100644 --- a/src/graphics/font.c +++ b/src/graphics/font.c @@ -47,8 +47,8 @@ int mgl_font_load_from_file(mgl_font *self, const char *filepath, unsigned int c /* TODO: Optimize */ /* Find optimal size for atlas, starting from small to large */ for(int i = 0; i < 4; ++i) { - self->font_atlas.width = (8 + (8 * i)) * self->character_size; - self->font_atlas.height = (8 + (8 * i)) * self->character_size; + self->font_atlas.width = (14 + (8 * i)) * self->character_size; + self->font_atlas.height = (14 + (8 * i)) * self->character_size; unsigned char *new_atlas = realloc(self->font_atlas.atlas, self->font_atlas.width * self->font_atlas.height); if(!new_atlas) { fprintf(stderr, "Error: failed to load font %s, error: out of memory\n", filepath); @@ -67,6 +67,8 @@ int mgl_font_load_from_file(mgl_font *self, const char *filepath, unsigned int c return -1; } + stbtt_PackSetOversampling(&pc, 2, 2); + if(!stbtt_PackFontRange(&pc, filedata.data, 0, self->character_size, 0, self->num_packed_chars, self->packed_chars)) { stbtt_PackEnd(&pc); continue; @@ -92,7 +94,6 @@ int mgl_font_load_from_file(mgl_font *self, const char *filepath, unsigned int c } /* TODO: Use stbtt_GetCodepointSDF */ - /* TODO: Use stbtt_PackSetOversampling */ mgl_filedata_free(&filedata); return 0; @@ -119,7 +120,7 @@ int mgl_font_get_glyph(const mgl_font *self, uint32_t codepoint, mgl_font_glyph float x = 0.0f; float y = 0.0f; stbtt_aligned_quad quad; - stbtt_GetPackedQuad(packed_chars, self->font_atlas.width, self->font_atlas.height, codepoint, &x, &y, &quad, 1); + stbtt_GetPackedQuad(packed_chars, self->font_atlas.width, self->font_atlas.height, codepoint, &x, &y, &quad, 0); glyph->position = (mgl_vec2f){ quad.x0, quad.y0 }; glyph->size = (mgl_vec2f){ quad.x1 - quad.x0, quad.y1 - quad.y0 }; diff --git a/src/graphics/text.c b/src/graphics/text.c index 76fd852..9a849a9 100644 --- a/src/graphics/text.c +++ b/src/graphics/text.c @@ -2,20 +2,48 @@ #include "../../include/mgl/graphics/font.h" #include "../../include/mgl/mgl.h" -int mgl_text_init(mgl_text *self, mgl_font *font, const char *text, float x, float y) { +/* + 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. +*/ + +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; +} + +int mgl_text_init(mgl_text *self, mgl_font *font, const char *str, size_t str_size, mgl_text_options *options) { self->font = font; - self->text = text; + self->text = str; + self->text_size = str_size; self->color = (mgl_color){ 255, 255, 255, 255 }; - self->position = (mgl_vec2f){ x, y }; + self->position = (mgl_vec2f){ 0.0f, 0.0f }; + + if(options) + self->options = *options; + else + self->options.userdata = NULL; + + if(!options || !options->syntax_highlight) + self->options.syntax_highlight = default_syntax_highlight; + return 0; } void mgl_text_deinit(mgl_text *self) { - + (void)self; } -void mgl_text_set_string(mgl_text *self, const char *str) { +void mgl_text_set_string(mgl_text *self, const char *str, size_t str_size) { self->text = str; + self->text_size = str_size; } void mgl_text_set_font(mgl_text *self, mgl_font *font) { @@ -44,25 +72,43 @@ 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) { - if(!text->text || !text->font) + if(!text->text || text->text_size == 0 || !text->font) return; - const char *str = text->text; - mgl_font_glyph glyph; 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); - while(*str) { - unsigned char c = *(unsigned char*)str; + 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; } @@ -78,7 +124,6 @@ void mgl_text_draw(mgl_context *context, mgl_text *text) { position.x = text->position.x; position.y += text->font->character_size; } - ++str; } context->gl.glEnd(); context->gl.glBindTexture(GL_TEXTURE_2D, 0); diff --git a/src/mgl.c b/src/mgl.c index 6407d9e..7940def 100644 --- a/src/mgl.c +++ b/src/mgl.c @@ -8,6 +8,7 @@ static mgl_context context; static int init_count = 0; static XErrorHandler prev_xerror = NULL; +static XIOErrorHandler prev_xioerror = NULL; static int ignore_xerror(Display *display, XErrorEvent *ee) { (void)display; @@ -15,6 +16,11 @@ static int ignore_xerror(Display *display, XErrorEvent *ee) { return 0; } +static int ignore_xioerror(Display *display) { + (void)display; + return 0; +} + static int glx_context_init() { const int attr[] = { GLX_RGBA, @@ -60,6 +66,13 @@ int mgl_init(void) { } prev_xerror = XSetErrorHandler(ignore_xerror); + prev_xioerror = XSetIOErrorHandler(ignore_xioerror); + + context.wm_delete_window_atom = XInternAtom(context.connection, "WM_DELETE_WINDOW", False); + if(!context.wm_delete_window_atom) { + mgl_deinit(); + return -1; + } if(mgl_gl_load(&context.gl) != 0) { mgl_deinit(); @@ -76,10 +89,16 @@ int mgl_init(void) { void mgl_deinit(void) { if(init_count == 1) { - glx_context_deinit(); - mgl_gl_unload(&context.gl); - XSetErrorHandler(prev_xerror); if(context.connection) { + glx_context_deinit(); + mgl_gl_unload(&context.gl); + + XSetIOErrorHandler(prev_xioerror); + prev_xioerror = NULL; + + XSetErrorHandler(prev_xerror); + prev_xerror = NULL; + XCloseDisplay(context.connection); context.connection = NULL; } diff --git a/src/window/window.c b/src/window/window.c index 94756cd..9a650ae 100644 --- a/src/window/window.c +++ b/src/window/window.c @@ -1,4 +1,5 @@ #include "../../include/mgl/window/window.h" +#include "../../include/mgl/window/event.h" #include "../../include/mgl/mgl.h" #include #include @@ -140,23 +141,127 @@ void mgl_window_deinit(mgl_window *self) { } } -static void mgl_window_on_receive_event(mgl_window *self, XEvent *xev) { +static mgl_key x11_keysym_to_mgl_key(KeySym key_sym) { + if(key_sym >= XK_A && key_sym <= XK_Z) + return MGL_KEY_A + (key_sym - XK_A); + /* TODO: Check if this ever happens */ + if(key_sym >= XK_a && key_sym <= XK_z) + return MGL_KEY_A + (key_sym - XK_a); + if(key_sym >= XK_0 && key_sym <= XK_9) + return MGL_KEY_NUM0 + (key_sym - XK_0); + if(key_sym >= XK_KP_0 && key_sym <= XK_KP_9) + return MGL_KEY_NUMPAD0 + (key_sym - XK_KP_0); + + /* TODO: Fill in the rest */ + switch(key_sym) { + case XK_space: return MGL_KEY_SPACE; + case XK_BackSpace: return MGL_KEY_BACKSPACE; + case XK_Tab: return MGL_KEY_TAB; + case XK_Return: return MGL_KEY_ENTER; + case XK_Delete: return MGL_KEY_DELETE; + case XK_Home: return MGL_KEY_HOME; + case XK_Left: return MGL_KEY_LEFT; + case XK_Up: return MGL_KEY_UP; + case XK_Right: return MGL_KEY_RIGHT; + case XK_Down: return MGL_KEY_DOWN; + case XK_Page_Up: return MGL_KEY_PAGEUP; + case XK_Page_Down: return MGL_KEY_PAGEDOWN; + case XK_End: return MGL_KEY_END; + case XK_F1: return MGL_KEY_F1; + case XK_F2: return MGL_KEY_F2; + case XK_F3: return MGL_KEY_F3; + case XK_F4: return MGL_KEY_F4; + case XK_F5: return MGL_KEY_F5; + case XK_F6: return MGL_KEY_F6; + case XK_F7: return MGL_KEY_F7; + case XK_F8: return MGL_KEY_F8; + case XK_F9: return MGL_KEY_F9; + case XK_F10: return MGL_KEY_F10; + case XK_F11: return MGL_KEY_F11; + case XK_F12: return MGL_KEY_F12; + case XK_F13: return MGL_KEY_F13; + case XK_F14: return MGL_KEY_F14; + case XK_F15: return MGL_KEY_F15; + } + return MGL_KEY_UNKNOWN; +} + +static mgl_mouse_button x11_button_to_mgl_button(unsigned int button) { + switch(button) { + case 1: return MGL_BUTTON_LEFT; + case 2: return MGL_BUTTON_MIDDLE; + case 3: return MGL_BUTTON_RIGHT; + case 8: return MGL_BUTTON_XBUTTON1; + case 9: return MGL_BUTTON_XBUTTON2; + } + return MGL_BUTTON_UNKNOWN; +} + +/* Returns true if processed */ +static bool mgl_window_on_receive_event(mgl_window *self, XEvent *xev, mgl_event *event, mgl_context *context) { + /* TODO: Handle wm_delete_window event */ switch(xev->type) { + case KeyPress: { + /* TODO: Fill with correct data */ + event->type = MGL_EVENT_KEY_PRESSED; + event->key.code = x11_keysym_to_mgl_key(XKeycodeToKeysym(context->connection, xev->xkey.keycode, 0)); + event->key.alt = ((xev->xkey.state & Mod1Mask) != 0); + event->key.control = ((xev->xkey.state & ControlMask) != 0); + event->key.shift = ((xev->xkey.state & ShiftMask) != 0); + event->key.system = ((xev->xkey.state & Mod5Mask) != 0); /* TODO: Fix, doesn't work */ + return true; + } + case KeyRelease: { + /* TODO: Fill with correct data */ + event->type = MGL_EVENT_KEY_RELEASED; + event->key.code = x11_keysym_to_mgl_key(XKeycodeToKeysym(context->connection, xev->xkey.keycode, 0)); + event->key.alt = ((xev->xkey.state & Mod1Mask) != 0); + event->key.control = ((xev->xkey.state & ControlMask) != 0); + event->key.shift = ((xev->xkey.state & ShiftMask) != 0); + event->key.system = ((xev->xkey.state & Mod5Mask) != 0); /* TODO: Fix, doesn't work */ + return true; + } + case ButtonPress: { + event->type = MGL_EVENT_MOUSE_BUTTON_PRESSED; + event->mouse_button.button = x11_button_to_mgl_button(xev->xbutton.button); + event->mouse_button.x = xev->xbutton.x; + event->mouse_button.y = xev->xbutton.y; + return true; + } + case ButtonRelease: { + event->type = MGL_EVENT_MOUSE_BUTTON_RELEASED; + event->mouse_button.button = x11_button_to_mgl_button(xev->xbutton.button); + event->mouse_button.x = xev->xbutton.x; + event->mouse_button.y = xev->xbutton.y; + return true; + } case ConfigureNotify: { while(XCheckTypedWindowEvent(mgl_get_context()->connection, self->window, ConfigureNotify, xev)) {} if(xev->xconfigure.width != self->size.x || xev->xconfigure.height != self->size.x) { mgl_window_on_resize(self, xev->xconfigure.width, xev->xconfigure.height); - /*fprintf(stderr, "resize!\n");*/ + + event->type = MGL_EVENT_RESIZED; + event->size.width = self->size.x; + event->size.height = self->size.y; + event->mouse_move.y = self->cursor_position.y; + return true; } - break; + return false; } case MotionNotify: { while(XCheckTypedWindowEvent(mgl_get_context()->connection, self->window, MotionNotify, xev)) {} self->cursor_position.x = xev->xmotion.x; self->cursor_position.y = xev->xmotion.y; - break; + + event->type = MGL_EVENT_MOUSE_MOVED; + event->mouse_move.x = self->cursor_position.x; + event->mouse_move.y = self->cursor_position.y; + return true; } } + /*fprintf(stderr, "unhandled event type: %d\n", xev->type);*/ + event->type = MGL_EVENT_UNKNOWN; + return false; } void mgl_window_clear(mgl_window *self, mgl_color color) { @@ -166,13 +271,12 @@ void mgl_window_clear(mgl_window *self, mgl_color color) { } bool mgl_window_poll_event(mgl_window *self, mgl_event *event) { - /* TODO: Use |event| */ - - Display *display = mgl_get_context()->connection; + mgl_context *context = mgl_get_context(); + Display *display = context->connection; if(XPending(display)) { XEvent xev; /* TODO: Move to window struct */ XNextEvent(display, &xev); - mgl_window_on_receive_event(self, &xev); + mgl_window_on_receive_event(self, &xev, event, context); return true; } else { return false; -- cgit v1.2.3