aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--.gitmodules3
-rw-r--r--LICENSE19
-rw-r--r--README.md9
m---------depends/mgl0
-rw-r--r--include/alloc.h10
-rw-r--r--include/common.h9
-rw-r--r--include/mgui/button.h22
-rw-r--r--include/mgui/list.h33
-rw-r--r--include/mgui/widget.h33
-rw-r--r--include/resource_loader.h15
-rw-r--r--project.conf9
-rw-r--r--src/alloc.c25
-rw-r--r--src/common.c6
-rw-r--r--src/mgui/button.c55
-rw-r--r--src/mgui/list.c65
-rw-r--r--src/mgui/widget.c48
-rw-r--r--src/resource_loader.c82
-rw-r--r--tests/main.c32
19 files changed, 480 insertions, 0 deletions
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
+Subproject 8792ee2cc5b501f0611e6304f529226a495825d
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 <stddef.h>
+
+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 <mgl/system/vec.h>
+#include <stdbool.h>
+
+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 <mgl/graphics/rectangle.h>
+#include <mgl/graphics/text.h>
+#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 <stddef.h>
+
+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 <mgl/system/vec.h>
+
+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 <stdlib.h>
+#include <stdio.h>
+
+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 <mgl/mgl.h>
+#include <mgl/window/event.h>
+#include <assert.h>
+
+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 <mgl/mgl.h>
+#include <assert.h>
+
+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 <mgl/system/fileutils.h>
+#include <mgl/graphics/font.h>
+#include <stdio.h>
+
+/* 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 <mgl/mgl.h>
+#include <mgl/window/window.h>
+#include <mgl/window/event.h>
+
+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;
+}