From 14770f42df291753a240def91fb488a904a909c1 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Thu, 2 Dec 2021 14:20:48 +0100 Subject: Initial commit, set up skeleton with list and button --- .gitignore | 5 +++ .gitmodules | 3 ++ LICENSE | 19 +++++++++++ README.md | 9 ++++++ depends/mgl | 1 + include/alloc.h | 10 ++++++ include/common.h | 9 ++++++ include/mgui/button.h | 22 +++++++++++++ include/mgui/list.h | 33 +++++++++++++++++++ include/mgui/widget.h | 33 +++++++++++++++++++ include/resource_loader.h | 15 +++++++++ project.conf | 9 ++++++ src/alloc.c | 25 +++++++++++++++ src/common.c | 6 ++++ src/mgui/button.c | 55 +++++++++++++++++++++++++++++++ src/mgui/list.c | 65 +++++++++++++++++++++++++++++++++++++ src/mgui/widget.c | 48 +++++++++++++++++++++++++++ src/resource_loader.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++ tests/main.c | 32 ++++++++++++++++++ 19 files changed, 481 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 LICENSE create mode 100644 README.md create mode 160000 depends/mgl create mode 100644 include/alloc.h create mode 100644 include/common.h create mode 100644 include/mgui/button.h create mode 100644 include/mgui/list.h create mode 100644 include/mgui/widget.h create mode 100644 include/resource_loader.h create mode 100644 project.conf create mode 100644 src/alloc.c create mode 100644 src/common.c create mode 100644 src/mgui/button.c create mode 100644 src/mgui/list.c create mode 100644 src/mgui/widget.c create mode 100644 src/resource_loader.c create mode 100644 tests/main.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..636c6b9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# Compiled sibs files +sibs-build/ +compile_commands.json +tests/sibs-build/ +tests/compile_commands.json diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..bd1f76a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "depends/mgl"] + path = depends/mgl + url = git://git.dec05eba.com/mgl diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c2e5149 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2021 dec05eba + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..513d0cb --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# Minimal Graphics Library GUI +Written in C and uses OpenGL 2.0 to support as many platforms as possible. +# Dependencies +## Build +`xlib` +## Runtime +`libglvnd (libGL.so)` +# Notes +mglgui aborts on memory allocation failure to make the API as simple as possible. This can be changed if there is an incentive. \ No newline at end of file diff --git a/depends/mgl b/depends/mgl new file mode 160000 index 0000000..8792ee2 --- /dev/null +++ b/depends/mgl @@ -0,0 +1 @@ +Subproject commit 8792ee2cc5b501f0611e6304f529226a495825db diff --git a/include/alloc.h b/include/alloc.h new file mode 100644 index 0000000..272326e --- /dev/null +++ b/include/alloc.h @@ -0,0 +1,10 @@ +#ifndef MGUI_ALLOC_H +#define MGUI_ALLOC_H + +#include + +void* mgui_alloc(size_t size); +void* mgui_realloc(void *mem, size_t new_size); +void mgui_free(void *mem); + +#endif /* MGUI_ALLOC_H */ diff --git a/include/common.h b/include/common.h new file mode 100644 index 0000000..3fb4f6f --- /dev/null +++ b/include/common.h @@ -0,0 +1,9 @@ +#ifndef MGUI_COMMON_H +#define MGUI_COMMON_H + +#include +#include + +bool mgui_rectangle_contains(mgl_vec2f rectangle_pos, mgl_vec2f rectangle_size, mgl_vec2f point); + +#endif /* MGUI_COMMON_H */ diff --git a/include/mgui/button.h b/include/mgui/button.h new file mode 100644 index 0000000..8f9a2db --- /dev/null +++ b/include/mgui/button.h @@ -0,0 +1,22 @@ +#ifndef MGUI_BUTTON_H +#define MGUI_BUTTON_H + +#include +#include +#include "widget.h" + +typedef struct { + mgui_widget widget; + mgl_rectangle background; + mgl_text label; +} mgui_button; + +mgui_button* mgui_button_create(); +mgui_widget* mgui_button_to_widget(mgui_button *list); +mgui_button* mgui_widget_to_button(mgui_widget *widget); + +void mgui_button_set_position(mgui_button *self, mgl_vec2i position); +void mgui_button_on_event(mgui_button *self, mgl_window *window, mgl_event *event); +void mgui_button_draw(mgui_button *self, mgl_window *window); + +#endif /* MGUI_BUTTON_H */ diff --git a/include/mgui/list.h b/include/mgui/list.h new file mode 100644 index 0000000..1da326d --- /dev/null +++ b/include/mgui/list.h @@ -0,0 +1,33 @@ +#ifndef MGUI_LIST_H +#define MGUI_LIST_H + +#include "widget.h" +#include + +typedef struct mgl_window mgl_window; +typedef struct mgl_event mgl_event; + +typedef enum { + MGUI_LIST_HORIZONITAL, + MGUI_LIST_VERTICAL +} mgui_list_direction; + +typedef struct { + mgui_widget widget; + mgui_list_direction direction; + mgl_vec2i position; + mgui_widget **items; + size_t num_items; + size_t items_capacity; +} mgui_list; + +mgui_list* mgui_list_create(mgui_list_direction direction); +mgui_widget* mgui_list_to_widget(mgui_list *list); +mgui_list* mgui_widget_to_list(mgui_widget *widget); + +void mgui_list_set_position(mgui_list *self, mgl_vec2i position); +void mgui_list_append(mgui_list *self, mgui_widget *widget); +void mgui_list_on_event(mgui_list *self, mgl_window *window, mgl_event *event); +void mgui_list_draw(mgui_list *self, mgl_window *window); + +#endif /* MGUI_LIST_H */ diff --git a/include/mgui/widget.h b/include/mgui/widget.h new file mode 100644 index 0000000..60f850c --- /dev/null +++ b/include/mgui/widget.h @@ -0,0 +1,33 @@ +#ifndef MGUI_WIDGET_H +#define MGUI_WIDGET_H + +#include + +typedef struct mgl_window mgl_window; +typedef struct mgl_event mgl_event; +typedef struct mgui_widget mgui_widget; + +typedef enum { + MGUI_WIDGET_LIST, + MGUI_WIDGET_BUTTON +} mgui_widget_type; + +typedef struct { + int left; + int top; + int right; + int bottom; +} mgui_margin; + +struct mgui_widget { + mgui_widget_type type; + mgui_margin margin; +}; + +void mgui_widget_init(mgui_widget *self, mgui_widget_type type); +void mgui_widget_set_margin(mgui_widget *self, int left, int top, int right, int bottom); +void mgui_widget_set_position(mgui_widget *self, mgl_vec2i position); +void mgui_widget_on_event(mgui_widget *self, mgl_window *window, mgl_event *event); +void mgui_widget_draw(mgui_widget *self, mgl_window *window); + +#endif /* MGUI_WIDGET_H */ diff --git a/include/resource_loader.h b/include/resource_loader.h new file mode 100644 index 0000000..6120faa --- /dev/null +++ b/include/resource_loader.h @@ -0,0 +1,15 @@ +#ifndef MGUI_RESOURCE_LOADER_H +#define MGUI_RESOURCE_LOADER_H + +typedef struct mgl_font mgl_font; + +typedef enum { + MGUI_FONT_LATIN, + MGUI_FONT_LATIN_BOLD, + MGUI_FONT_CJK +} mgui_font_type; + +/* Note: can return NULL if the font fails to load */ +mgl_font* mgui_get_font(mgui_font_type type, unsigned int character_size); + +#endif /* MGUI_RESOURCE_LOADER_H */ diff --git a/project.conf b/project.conf new file mode 100644 index 0000000..7a9212b --- /dev/null +++ b/project.conf @@ -0,0 +1,9 @@ +[package] +name = "mgui" +type = "static" +version = "0.1.0" +platforms = ["posix"] + +[config] +expose_include_dirs = ["include"] +error_on_warning = "true" \ No newline at end of file diff --git a/src/alloc.c b/src/alloc.c new file mode 100644 index 0000000..90682b5 --- /dev/null +++ b/src/alloc.c @@ -0,0 +1,25 @@ +#include "../include/alloc.h" +#include +#include + +void* mgui_alloc(size_t size) { + void *mem = malloc(size); + if(!mem) { + fprintf(stderr, "mgui error: mgui_alloc failed to allocate %zu bytes\n", size); + abort(); + } + return mem; +} + +void* mgui_realloc(void *mem, size_t new_size) { + void *new_mem = realloc(mem, new_size); + if(!new_mem) { + fprintf(stderr, "mgui error: mgui_realloc failed to reallocate %p to %zu bytes\n", mem, new_size); + abort(); + } + return new_mem; +} + +void mgui_free(void *mem) { + free(mem); +} diff --git a/src/common.c b/src/common.c new file mode 100644 index 0000000..74dcf44 --- /dev/null +++ b/src/common.c @@ -0,0 +1,6 @@ +#include "../include/common.h" + +bool mgui_rectangle_contains(mgl_vec2f rectangle_pos, mgl_vec2f rectangle_size, mgl_vec2f point) { + return point.x >= rectangle_pos.x && point.x <= rectangle_pos.x + rectangle_size.x + && point.y >= rectangle_pos.y && point.y <= rectangle_pos.y + rectangle_size.y; +} diff --git a/src/mgui/button.c b/src/mgui/button.c new file mode 100644 index 0000000..d8d0c58 --- /dev/null +++ b/src/mgui/button.c @@ -0,0 +1,55 @@ +#include "../../include/mgui/button.h" +#include "../../include/resource_loader.h" +#include "../../include/common.h" +#include "../../include/alloc.h" +#include +#include +#include + +mgui_button* mgui_button_create() { + mgui_button *button = mgui_alloc(sizeof(mgui_button)); + mgui_widget_init(&button->widget, MGUI_WIDGET_BUTTON); + button->background.position = (mgl_vec2f){ 0.0f, 0.0f }; + button->background.size = (mgl_vec2f){ 0.0f, 0.0f }; + button->background.color = (mgl_color){ 45, 45, 45, 255 }; + mgl_text_init(&button->label, mgui_get_font(MGUI_FONT_LATIN, 32), "Label", 5); + return button; +} + +mgui_widget* mgui_button_to_widget(mgui_button *list) { + return &list->widget; +} + +mgui_button* mgui_widget_to_button(mgui_widget *widget) { + assert(widget->type == MGUI_WIDGET_BUTTON); + return (mgui_button*)widget; +} + +void mgui_button_set_position(mgui_button *self, mgl_vec2i position) { + const mgl_vec2f position_f = (mgl_vec2f){ position.x, position.y }; + self->background.position = position_f; + mgl_text_set_position(&self->label, position_f); +} + +void mgui_button_on_event(mgui_button *self, mgl_window *window, mgl_event *event) { + // TODO: Implement + (void)window; + if(event->type == MGL_EVENT_MOUSE_MOVED) { + if(mgui_rectangle_contains(self->background.position, self->background.size, (mgl_vec2f){ event->mouse_move.x, event->mouse_move.y })) { + self->background.color = (mgl_color){ 70, 70, 70, 255 }; + } else { + self->background.color = (mgl_color){ 45, 45, 45, 255 }; + } + } else if(event->type == MGL_EVENT_MOUSE_BUTTON_PRESSED && event->mouse_button.button == MGL_BUTTON_LEFT) { + if(mgui_rectangle_contains(self->background.position, self->background.size, (mgl_vec2f){ event->mouse_button.x, event->mouse_button.y })) { + + } + } +} + +void mgui_button_draw(mgui_button *self, mgl_window *window) { + (void)window; + self->background.size = mgl_text_get_bounds(&self->label); + mgl_rectangle_draw(mgl_get_context(), &self->background); + mgl_text_draw(mgl_get_context(), &self->label); +} diff --git a/src/mgui/list.c b/src/mgui/list.c new file mode 100644 index 0000000..8dc4283 --- /dev/null +++ b/src/mgui/list.c @@ -0,0 +1,65 @@ +#include "../../include/mgui/list.h" +#include "../../include/alloc.h" +#include +#include + +mgui_list* mgui_list_create(mgui_list_direction direction) { + mgui_list *list = mgui_alloc(sizeof(mgui_list)); + mgui_widget_init(&list->widget, MGUI_WIDGET_LIST); + list->direction = direction; + list->position = (mgl_vec2i){ 0, 0 }; + list->items = NULL; + list->num_items = 0; + list->items_capacity = 0; + return list; +} + +mgui_widget* mgui_list_to_widget(mgui_list *list) { + return &list->widget; +} + +mgui_list* mgui_widget_to_list(mgui_widget *widget) { + assert(widget->type == MGUI_WIDGET_LIST); + return (mgui_list*)widget; +} + +void mgui_list_set_position(mgui_list *self, mgl_vec2i position) { + self->position = position; +} + +static void mgui_list_ensure_capacity(mgui_list *self, size_t new_capacity) { + if(self->items_capacity >= new_capacity) + return; + + size_t capacity = self->items_capacity; + if(capacity == 0) + capacity = 8; + + while(capacity < new_capacity) { + capacity = capacity + (capacity >> 1); /* capacity *= 1.5 */ + } + + self->items = mgui_realloc(self->items, capacity); + self->items_capacity = capacity; +} + +void mgui_list_append(mgui_list *self, mgui_widget *widget) { + mgui_list_ensure_capacity(self, (self->num_items + 1) * sizeof(mgui_widget*)); + self->items[self->num_items] = widget; + ++self->num_items; +} + +void mgui_list_on_event(mgui_list *self, mgl_window *window, mgl_event *event) { + for(size_t i = 0; i < self->num_items; ++i) { + mgui_widget_on_event(self->items[i], window, event); + } +} + +void mgui_list_draw(mgui_list *self, mgl_window *window) { + mgl_context *context = mgl_get_context(); + context->gl.glTranslatef(self->position.x, self->position.y, 0.0f); + for(size_t i = 0; i < self->num_items; ++i) { + mgui_widget_draw(self->items[i], window); + } + context->gl.glLoadIdentity(); /* TODO: Remove, but what about glRotatef above */ +} diff --git a/src/mgui/widget.c b/src/mgui/widget.c new file mode 100644 index 0000000..242174d --- /dev/null +++ b/src/mgui/widget.c @@ -0,0 +1,48 @@ +#include "../../include/mgui/widget.h" +#include "../../include/mgui/list.h" +#include "../../include/mgui/button.h" + +void mgui_widget_init(mgui_widget *self, mgui_widget_type type) { + self->type = type; + mgui_widget_set_margin(self, 0, 0, 0, 0); +} + +void mgui_widget_set_margin(mgui_widget *self, int left, int top, int right, int bottom) { + self->margin.left = left; + self->margin.top = top; + self->margin.right = right; + self->margin.bottom = bottom; +} + +void mgui_widget_set_position(mgui_widget *self, mgl_vec2i position) { + switch(self->type) { + case MGUI_WIDGET_LIST: + mgui_list_set_position(mgui_widget_to_list(self), position); + break; + case MGUI_WIDGET_BUTTON: + mgui_button_set_position(mgui_widget_to_button(self), position); + break; + } +} + +void mgui_widget_on_event(mgui_widget *self, mgl_window *window, mgl_event *event) { + switch(self->type) { + case MGUI_WIDGET_LIST: + mgui_list_on_event(mgui_widget_to_list(self), window, event); + break; + case MGUI_WIDGET_BUTTON: + mgui_button_on_event(mgui_widget_to_button(self), window, event); + break; + } +} + +void mgui_widget_draw(mgui_widget *self, mgl_window *window) { + switch(self->type) { + case MGUI_WIDGET_LIST: + mgui_list_draw(mgui_widget_to_list(self), window); + break; + case MGUI_WIDGET_BUTTON: + mgui_button_draw(mgui_widget_to_button(self), window); + break; + } +} diff --git a/src/resource_loader.c b/src/resource_loader.c new file mode 100644 index 0000000..d589373 --- /dev/null +++ b/src/resource_loader.c @@ -0,0 +1,82 @@ +#include "../include/resource_loader.h" +#include "../include/alloc.h" +#include +#include +#include + +/* Note: max font size: 100 */ +static mgl_memory_mapped_file *font_file_cache[4]; +static mgl_font *font_cache[4][100]; + +/* TODO: If font fails to load, then create a new font with only a squares that represents all glyphs */ +/* TODO: Load default system font from fontconfig files */ +mgl_font* mgui_get_font(mgui_font_type type, unsigned int character_size) { + if(character_size > 0) + --character_size; + + /* TODO: Make this work for character size >= 100 */ + if(character_size >= 100) + character_size = 99; + + mgl_memory_mapped_file *font_file = font_file_cache[type]; + if(!font_file) { + const char *font_paths[2]; + size_t num_font_paths = 0; + + switch(type) { + case MGUI_FONT_LATIN: { + font_paths[0] = "/usr/share/fonts/noto/NotoSans-Regular.ttf"; + font_paths[1] = "/usr/share/fonts/truetype/noto/NotoSans-Regular.ttf"; + num_font_paths = 2; + break; + } + case MGUI_FONT_LATIN_BOLD: { + font_paths[0] = "/usr/share/fonts/noto/NotoSans-Bold.ttf"; + font_paths[1] = "/usr/share/fonts/truetype/noto/NotoSans-Bold.ttf"; + num_font_paths = 2; + break; + } + case MGUI_FONT_CJK: { + font_paths[0] = "/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttf"; + font_paths[1] = "/usr/share/fonts/truetype/noto-cjk/NotoSansCJK-Regular.ttf"; + num_font_paths = 2; + break; + } + } + + mgl_memory_mapped_file *new_font_file = mgui_alloc(sizeof(mgl_memory_mapped_file)); + + mgl_memory_mapped_file_load_options load_options = { + .readable = true, + .writable = false + }; + + bool successfully_loaded_file = false; + for(size_t i = 0; i < num_font_paths; ++i) { + if(mgl_mapped_file_load(font_paths[i], new_font_file, &load_options) == 0) { + successfully_loaded_file = true; + break; + } + } + + if(!successfully_loaded_file) + fprintf(stderr, "mgui warning: mgui_get_font failed to load font %d\n", (int)type); + + font_file_cache[type] = new_font_file; + font_file = new_font_file; + } + + if(!font_file) + return NULL; + + mgl_font *font = font_cache[type][character_size]; + if(!font) { + mgl_font *new_font = mgui_alloc(sizeof(mgl_font)); + if(mgl_font_load_from_file(new_font, font_file, character_size) != 0) + fprintf(stderr, "mgui warning: mgui_get_font failed to load font %d\n", (int)type); + + font_cache[type][character_size] = new_font; + font = new_font; + } + return font; +} diff --git a/tests/main.c b/tests/main.c new file mode 100644 index 0000000..6a94414 --- /dev/null +++ b/tests/main.c @@ -0,0 +1,32 @@ +#include "../include/mgui/list.h" +#include "../include/mgui/button.h" +#include +#include +#include + +int main() { + if(mgl_init() != 0) + return 1; + + mgl_window window; + if(mgl_window_create(&window, "mgl", &(mgl_window_create_params){ .size = {1280, 720} }) != 0) + return 1; + + mgui_list *list = mgui_list_create(MGUI_LIST_VERTICAL); + mgui_button *button = mgui_button_create(); + mgui_list_append(list, mgui_button_to_widget(button)); + + mgl_event event; + while(mgl_window_is_open(&window)) { + while(mgl_window_poll_event(&window, &event)) { + mgui_list_on_event(list, &window, &event); + } + + mgl_window_clear(&window, (mgl_color){0, 0, 0, 0}); + mgui_list_draw(list, &window); + mgl_window_display(&window); + } + + mgl_deinit(); + return 0; +} -- cgit v1.2.3