├── LICENCE ├── Makefile ├── README.md ├── data ├── icofont.ttf ├── roboto-bold.ttf ├── roboto-regular.ttf └── robotomono-regular.ttf └── src ├── alfi.c ├── animation.c ├── animation.h ├── attribute.c ├── attribute.h ├── fons.c ├── fons.h ├── gridfmt.c ├── gridfmt.h ├── history.c ├── history.h ├── list.c ├── list.h ├── navi-resolve ├── navi.c ├── nvg.c ├── nvg.h ├── nvg_gl.c ├── nvg_gl.h ├── parser.c ├── parser.h ├── pool.c ├── pool.h ├── render.c ├── render.h ├── resource.c ├── resource.h ├── stb_image.h ├── stb_truetype.h ├── style.c ├── style.h ├── url.c ├── url.h ├── view.c ├── view.h ├── widget.c └── widget.h /LICENCE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Jens Nyberg 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | GL:=GLES3 2 | BIN_ALFI:=alfi 3 | OBJ_ALFI:=src/alfi.o src/list.o src/parser.o src/url.o src/resource.o src/pool.o src/attribute.o src/widget.o src/history.o 4 | BIN_NAVI:=navi 5 | OBJ_NAVI:=src/navi.o src/list.o src/parser.o src/url.o src/resource.o src/pool.o src/attribute.o src/widget.o src/history.o src/view.o src/gridfmt.o src/style.o src/nvg.o src/nvg_gl.o src/fons.o src/render.o src/animation.o 6 | BIN_NAVI_RESOLVE:=navi-resolve 7 | CFLAGS_GL2:=-DNVG_GL_VERSION_GL2 -DNVG_GL_GLEW 8 | CFLAGS_GL3:=-DNVG_GL_VERSION_GL3 -DNVG_GL_GLEW 9 | CFLAGS_GLES2:=-DNVG_GL_VERSION_GLES2 10 | CFLAGS_GLES3:=-DNVG_GL_VERSION_GLES3 11 | CFLAGS:=$(CFLAGS_$(GL)) 12 | LDFLAGS_GL2:=-lGL -lGLEW -lglfw 13 | LDFLAGS_GL3:=-lGL -lGLEW -lglfw 14 | LDFLAGS_GLES2:=-lGL -lglfw 15 | LDFLAGS_GLES3:=-lGL -lglfw 16 | LDFLAGS:=$(LDFLAGS_$(GL)) 17 | 18 | all: $(BIN_ALFI) $(BIN_NAVI) $(BIN_NAVI_RESOLVE) 19 | 20 | %.o: %.c 21 | @echo CC $@ 22 | @$(CC) -c -o $@ -Wall -Werror -Wno-misleading-indentation -pedantic $(CFLAGS) $^ 23 | 24 | $(BIN_ALFI): $(OBJ_ALFI) 25 | @echo LD $@ 26 | @$(CC) -o $@ -Wall -Werror -pedantic $^ -lm 27 | 28 | $(BIN_NAVI): $(OBJ_NAVI) 29 | @echo LD $@ 30 | @$(CC) -o $@ -Wall -Werror -pedantic $^ -lm $(LDFLAGS) 31 | 32 | $(BIN_NAVI_RESOLVE): src/$(BIN_NAVI_RESOLVE) 33 | @echo CP $@ 34 | @cp $^ $@ 35 | 36 | clean: 37 | @$(RM) $(BIN_ALFI) $(OBJ_ALFI) $(BIN_NAVI) $(OBJ_NAVI) $(BIN_NAVI_RESOLVE) 38 | 39 | install: 40 | mkdir -p /usr/bin 41 | cp $(BIN_NAVI) /usr/bin/$(BIN_NAVI) 42 | cp $(BIN_ALFI) /usr/bin/$(BIN_ALFI) 43 | cp $(BIN_NAVI_RESOLVE) /usr/bin/$(BIN_NAVI_RESOLVE) 44 | mkdir -p /usr/share/navi 45 | cp data/icofont.ttf /usr/share/navi/icofont.ttf 46 | cp data/roboto-bold.ttf /usr/share/navi/roboto-bold.ttf 47 | cp data/roboto-regular.ttf /usr/share/navi/roboto-regular.ttf 48 | cp data/robotomono-regular.ttf /usr/share/navi/robotomono-regular.ttf 49 | 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ALFI 2 | 3 | ALFI is a query language for graphical user interfaces. 4 | 5 | ALFI works in a similar way to SQL but instead of talking to a database you talk 6 | to an ALFI client (or ALFI browser). The ALFI browser will convert the ALFI 7 | queries into graphical elements (i.e. widgets) and render them on the screen. 8 | 9 | An ALFI query will tell the client to either insert, delete or update an 10 | element. ALFI is stateless so a typical use-case would be to have a regular web 11 | server serving ALFI queries as responses from regular HTTP requests (like GET, 12 | POST, etc). 13 | 14 | The biggest strength of ALFI is that it requires very little technical skill to 15 | get started. If you know basic SQL you can easily write ALFI too. Writing an 16 | ALFI application is much simpler than writing an application using 17 | HTML/JavaScript/CSS. 18 | 19 | NAVI is a proof of concept client/browser for ALFI. It takes ALFI queries and 20 | renders widgets in OpenGL. It is currently very much under development. 21 | 22 | ## Building / Installing 23 | 24 | You need to have the glfw3 libraries installed in order to build NAVI from 25 | soure. How you install those depends on your distribution of choice. 26 | 27 | Build: 28 | 29 | $ make 30 | 31 | Install: 32 | 33 | $ make install 34 | 35 | And that's it. 36 | 37 | ## Running 38 | 39 | To parse an alfi file: 40 | 41 | $ alfi < example.alfi 42 | 43 | To start navi: 44 | 45 | $ navi 46 | 47 | ## Contact 48 | 49 | jens.nyberg@gmail.com 50 | 51 | http://github.com/jezze/alfi 52 | 53 | -------------------------------------------------------------------------------- /data/icofont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jezze/alfi/ed3957c1cc2e4f208a12c4d69660e11289663b11/data/icofont.ttf -------------------------------------------------------------------------------- /data/roboto-bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jezze/alfi/ed3957c1cc2e4f208a12c4d69660e11289663b11/data/roboto-bold.ttf -------------------------------------------------------------------------------- /data/roboto-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jezze/alfi/ed3957c1cc2e4f208a12c4d69660e11289663b11/data/roboto-regular.ttf -------------------------------------------------------------------------------- /data/robotomono-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jezze/alfi/ed3957c1cc2e4f208a12c4d69660e11289663b11/data/robotomono-regular.ttf -------------------------------------------------------------------------------- /src/alfi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "list.h" 6 | #include "style.h" 7 | #include "url.h" 8 | #include "resource.h" 9 | #include "attribute.h" 10 | #include "widget.h" 11 | #include "parser.h" 12 | #include "pool.h" 13 | 14 | static struct parser parser; 15 | 16 | static struct widget *parser_create(unsigned int type, char *id, char *in) 17 | { 18 | 19 | struct widget *widget = pool_widget_create(); 20 | 21 | if (widget) 22 | { 23 | 24 | memset(&widget->header, 0, sizeof (struct widget_header)); 25 | memset(&widget->payload, 0, sizeof (union widget_payload)); 26 | 27 | widget_header_create(&widget->header, type, id, in); 28 | widget_payload_create(&widget->payload, widget->header.type); 29 | 30 | } 31 | 32 | return widget; 33 | 34 | } 35 | 36 | static struct widget *parser_destroy(struct widget *widget) 37 | { 38 | 39 | widget_payload_destroy(&widget->payload, widget->header.type); 40 | widget_header_destroy(&widget->header); 41 | 42 | return pool_widget_destroy(widget); 43 | 44 | } 45 | 46 | static void parser_clear(struct widget *widget) 47 | { 48 | 49 | struct widget *child = 0; 50 | 51 | while ((child = pool_widget_nextchild(child, widget))) 52 | { 53 | 54 | parser_clear(child); 55 | 56 | child = parser_destroy(child); 57 | 58 | } 59 | 60 | } 61 | 62 | static void parser_fail(void) 63 | { 64 | 65 | printf("Parsing failed on line %u\n", parser.expr.line + 1); 66 | exit(EXIT_FAILURE); 67 | 68 | } 69 | 70 | int main(int argc, char **argv) 71 | { 72 | 73 | char data[RESOURCE_PAGESIZE]; 74 | int count; 75 | 76 | pool_setup(); 77 | parser_init(&parser, parser_fail, pool_widget_find, parser_create, parser_destroy, parser_clear); 78 | parser_create(WIDGET_TYPE_WINDOW, "window", ""); 79 | parser_create(WIDGET_TYPE_TABLE, "main", "window"); 80 | 81 | while ((count = read(0, data, RESOURCE_PAGESIZE))) 82 | parser_parse(&parser, "main", count, data); 83 | 84 | return 0; 85 | 86 | } 87 | 88 | -------------------------------------------------------------------------------- /src/animation.h: -------------------------------------------------------------------------------- 1 | #define ANIMATION_CURSOR_ARROW 0 2 | #define ANIMATION_CURSOR_IBEAM 1 3 | #define ANIMATION_CURSOR_HAND 2 4 | #define ANIMATION_THEME_LIGHT 0 5 | #define ANIMATION_THEME_DARK 1 6 | 7 | struct frame 8 | { 9 | 10 | struct style_box bounds; 11 | struct style styles[8]; 12 | unsigned int animating; 13 | 14 | }; 15 | 16 | void animation_updateframe(unsigned int type, struct frame *frame, struct frame *keyframe, float u); 17 | void animation_initframe(struct frame *frame, int x, int y, int w, int h); 18 | void animation_step(struct widget *widget, struct frame *frame, struct view *view, float u); 19 | void animation_render(struct widget *widget, struct view *view); 20 | unsigned int animation_getcursor(struct widget *widget, int x, int y); 21 | void animation_setupfonts(void); 22 | void animation_settheme(unsigned int type); 23 | -------------------------------------------------------------------------------- /src/attribute.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "list.h" 4 | #include "style.h" 5 | #include "url.h" 6 | #include "resource.h" 7 | #include "view.h" 8 | #include "attribute.h" 9 | #include "pool.h" 10 | 11 | void attribute_data_create(struct attribute_data *attribute, char *content) 12 | { 13 | 14 | attribute->total = 128; 15 | attribute->offset = strlen(content); 16 | attribute->content = pool_string_create(ATTRIBUTE_DATA, attribute->content, attribute->total, attribute->offset + 1, content); 17 | 18 | } 19 | 20 | void attribute_data_destroy(struct attribute_data *attribute) 21 | { 22 | 23 | attribute->total = 0; 24 | attribute->offset = 0; 25 | attribute->content = pool_string_destroy(ATTRIBUTE_DATA, attribute->content); 26 | 27 | } 28 | 29 | void attribute_grid_create(struct attribute_grid *attribute, char *format) 30 | { 31 | 32 | attribute->format = pool_string_create(ATTRIBUTE_GRID, attribute->format, 0, strlen(format) + 1, format); 33 | 34 | } 35 | 36 | void attribute_grid_destroy(struct attribute_grid *attribute) 37 | { 38 | 39 | attribute->format = pool_string_destroy(ATTRIBUTE_GRID, attribute->format); 40 | 41 | } 42 | 43 | void attribute_icon_create(struct attribute_icon *attribute, unsigned int type) 44 | { 45 | 46 | attribute->type = type; 47 | 48 | } 49 | 50 | void attribute_icon_destroy(struct attribute_icon *attribute) 51 | { 52 | 53 | attribute->type = 0; 54 | 55 | } 56 | 57 | void attribute_id_create(struct attribute_id *attribute, char *name) 58 | { 59 | 60 | attribute->name = pool_string_create(ATTRIBUTE_ID, attribute->name, 0, strlen(name) + 1, name); 61 | 62 | } 63 | 64 | void attribute_id_destroy(struct attribute_id *attribute) 65 | { 66 | 67 | attribute->name = pool_string_destroy(ATTRIBUTE_ID, attribute->name); 68 | 69 | } 70 | 71 | void attribute_in_create(struct attribute_in *attribute, char *name) 72 | { 73 | 74 | attribute->name = pool_string_create(ATTRIBUTE_IN, attribute->name, 0, strlen(name) + 1, name); 75 | 76 | } 77 | 78 | void attribute_in_destroy(struct attribute_in *attribute) 79 | { 80 | 81 | attribute->name = pool_string_destroy(ATTRIBUTE_IN, attribute->name); 82 | 83 | } 84 | 85 | void attribute_label_create(struct attribute_label *attribute, char *content) 86 | { 87 | 88 | attribute->content = pool_string_create(ATTRIBUTE_LABEL, attribute->content, 0, strlen(content) + 1, content); 89 | 90 | } 91 | 92 | void attribute_label_destroy(struct attribute_label *attribute) 93 | { 94 | 95 | attribute->content = pool_string_destroy(ATTRIBUTE_LABEL, attribute->content); 96 | 97 | } 98 | 99 | void attribute_link_create(struct attribute_link *attribute, char *url) 100 | { 101 | 102 | attribute->url = pool_string_create(ATTRIBUTE_LINK, attribute->url, 0, strlen(url) + 1, url); 103 | 104 | } 105 | 106 | void attribute_link_destroy(struct attribute_link *attribute) 107 | { 108 | 109 | attribute->url = pool_string_destroy(ATTRIBUTE_LINK, attribute->url); 110 | 111 | } 112 | 113 | void attribute_mode_create(struct attribute_mode *attribute, unsigned int type) 114 | { 115 | 116 | attribute->type = type; 117 | 118 | } 119 | 120 | void attribute_mode_destroy(struct attribute_mode *attribute) 121 | { 122 | 123 | attribute->type = 0; 124 | 125 | } 126 | 127 | void attribute_onclick_create(struct attribute_event *attribute, unsigned int type, char *data) 128 | { 129 | 130 | attribute->type = type; 131 | attribute->data = pool_string_create(ATTRIBUTE_ONCLICK, attribute->data, 0, strlen(data) + 1, data); 132 | 133 | } 134 | 135 | void attribute_onclick_destroy(struct attribute_event *attribute) 136 | { 137 | 138 | attribute->type = 0; 139 | attribute->data = pool_string_destroy(ATTRIBUTE_ONCLICK, attribute->data); 140 | 141 | } 142 | 143 | void attribute_onlinebreak_create(struct attribute_event *attribute, unsigned int type, char *data) 144 | { 145 | 146 | attribute->type = type; 147 | attribute->data = pool_string_create(ATTRIBUTE_ONLINEBREAK, attribute->data, 0, strlen(data) + 1, data); 148 | 149 | } 150 | 151 | void attribute_onlinebreak_destroy(struct attribute_event *attribute) 152 | { 153 | 154 | attribute->type = 0; 155 | attribute->data = pool_string_destroy(ATTRIBUTE_ONLINEBREAK, attribute->data); 156 | 157 | } 158 | 159 | void attribute_range_create(struct attribute_range *attribute, unsigned int min, unsigned int max) 160 | { 161 | 162 | attribute->min = min; 163 | attribute->max = max; 164 | 165 | } 166 | 167 | void attribute_range_destroy(struct attribute_range *attribute) 168 | { 169 | 170 | attribute->min = 0; 171 | attribute->max = 0; 172 | 173 | } 174 | 175 | void attribute_target_create(struct attribute_target *attribute, unsigned int type) 176 | { 177 | 178 | attribute->type = type; 179 | 180 | } 181 | 182 | void attribute_target_destroy(struct attribute_target *attribute) 183 | { 184 | 185 | attribute->type = 0; 186 | 187 | } 188 | 189 | void attribute_type_create(struct attribute_type *attribute, unsigned int type) 190 | { 191 | 192 | attribute->type = type; 193 | 194 | } 195 | 196 | void attribute_type_destroy(struct attribute_type *attribute) 197 | { 198 | 199 | attribute->type = 0; 200 | 201 | } 202 | 203 | -------------------------------------------------------------------------------- /src/attribute.h: -------------------------------------------------------------------------------- 1 | #define ATTRIBUTE_DATA 1 2 | #define ATTRIBUTE_GRID 2 3 | #define ATTRIBUTE_ICON 3 4 | #define ATTRIBUTE_ICON_NONE 0 5 | #define ATTRIBUTE_ICON_ALARM 0xEEA3 6 | #define ATTRIBUTE_ICON_AT 0xEEA6 7 | #define ATTRIBUTE_ICON_ATTACHMENT 0xEEA7 8 | #define ATTRIBUTE_ICON_AUDIO 0xEEA8 9 | #define ATTRIBUTE_ICON_BAN 0xEEAD 10 | #define ATTRIBUTE_ICON_BIN 0xEEBB 11 | #define ATTRIBUTE_ICON_BOOKMARK 0xEEC0 12 | #define ATTRIBUTE_ICON_BUG 0xEEC7 13 | #define ATTRIBUTE_ICON_CART 0xEED2 14 | #define ATTRIBUTE_ICON_CHAT 0xEED5 15 | #define ATTRIBUTE_ICON_CHECK 0xEED8 16 | #define ATTRIBUTE_ICON_CLIP 0xEEDB 17 | #define ATTRIBUTE_ICON_CLOCK 0xEEDC 18 | #define ATTRIBUTE_ICON_CLOSE 0xEEE4 19 | #define ATTRIBUTE_ICON_COMMENT 0xEEEB 20 | #define ATTRIBUTE_ICON_COMPASS 0xEEED 21 | #define ATTRIBUTE_ICON_COMPUTER 0xEEEE 22 | #define ATTRIBUTE_ICON_CONSOLE 0xEEF0 23 | #define ATTRIBUTE_ICON_DOWNLOAD 0xEF08 24 | #define ATTRIBUTE_ICON_EARTH 0xEF0E 25 | #define ATTRIBUTE_ICON_EDIT 0xEF10 26 | #define ATTRIBUTE_ICON_ENVELOPE 0xEF14 27 | #define ATTRIBUTE_ICON_ERROR 0xEF16 28 | #define ATTRIBUTE_ICON_EXCLAMATION 0xEF19 29 | #define ATTRIBUTE_ICON_EYE 0xEF21 30 | #define ATTRIBUTE_ICON_FAVOURITE 0xEF25 31 | #define ATTRIBUTE_ICON_FLAG 0xEF2F 32 | #define ATTRIBUTE_ICON_HEART 0xEF45 33 | #define ATTRIBUTE_ICON_HISTORY 0xEF46 34 | #define ATTRIBUTE_ICON_IMAGE 0xEF4B 35 | #define ATTRIBUTE_ICON_INFO 0xEF4E 36 | #define ATTRIBUTE_ICON_KEY 0xEF59 37 | #define ATTRIBUTE_ICON_LIKE 0xEF6E 38 | #define ATTRIBUTE_ICON_LINK 0xEF71 39 | #define ATTRIBUTE_ICON_LOCK 0xEF7A 40 | #define ATTRIBUTE_ICON_LOGIN 0xEF7B 41 | #define ATTRIBUTE_ICON_LOGOUT 0xEF7C 42 | #define ATTRIBUTE_ICON_MAGNET 0xEF86 43 | #define ATTRIBUTE_ICON_MARKER 0xEF79 44 | #define ATTRIBUTE_ICON_MENU 0xEFA2 45 | #define ATTRIBUTE_ICON_MINUS 0xEF9A 46 | #define ATTRIBUTE_ICON_OPTIONS 0xEFB0 47 | #define ATTRIBUTE_ICON_PLUS 0xEFC2 48 | #define ATTRIBUTE_ICON_POWER 0xEFC4 49 | #define ATTRIBUTE_ICON_PRINT 0xEFC6 50 | #define ATTRIBUTE_ICON_QUESTION 0xEFCA 51 | #define ATTRIBUTE_ICON_REFRESH 0xEFD1 52 | #define ATTRIBUTE_ICON_REPLY 0xEFD4 53 | #define ATTRIBUTE_ICON_REPLYALL 0xEFD3 54 | #define ATTRIBUTE_ICON_RETWEET 0xEFD7 55 | #define ATTRIBUTE_ICON_SEARCH 0xED1B 56 | #define ATTRIBUTE_ICON_SHARE 0xEFE5 57 | #define ATTRIBUTE_ICON_SORT 0xEFEF 58 | #define ATTRIBUTE_ICON_TAG 0xF004 59 | #define ATTRIBUTE_ICON_TAGS 0xF005 60 | #define ATTRIBUTE_ICON_THUMBDOWN 0xF00B 61 | #define ATTRIBUTE_ICON_THUMBUP 0xF00C 62 | #define ATTRIBUTE_ICON_UNLOCK 0xF01B 63 | #define ATTRIBUTE_ICON_UPLOAD 0xF01C 64 | #define ATTRIBUTE_ICON_WARNING 0xF025 65 | #define ATTRIBUTE_ID 4 66 | #define ATTRIBUTE_IN 5 67 | #define ATTRIBUTE_LABEL 6 68 | #define ATTRIBUTE_LINK 7 69 | #define ATTRIBUTE_MODE 8 70 | #define ATTRIBUTE_MODE_OFF 0 71 | #define ATTRIBUTE_MODE_ON 1 72 | #define ATTRIBUTE_MODE_DISABLED 2 73 | #define ATTRIBUTE_ONCLICK 9 74 | #define ATTRIBUTE_ONLINEBREAK 10 75 | #define ATTRIBUTE_RANGE 11 76 | #define ATTRIBUTE_TARGET 12 77 | #define ATTRIBUTE_TARGET_BLANK 0 78 | #define ATTRIBUTE_TARGET_SELF 1 79 | #define ATTRIBUTE_TYPE 13 80 | #define ATTRIBUTE_TYPE_REGULAR 0 81 | #define ATTRIBUTE_TYPE_PASSWORD 1 82 | #define FUNCTION_NONE 0 83 | #define FUNCTION_ALFI 1 84 | #define FUNCTION_GET 2 85 | #define FUNCTION_POST 3 86 | 87 | struct attribute_data 88 | { 89 | 90 | char *content; 91 | unsigned int offset; 92 | unsigned int total; 93 | 94 | }; 95 | 96 | struct attribute_event 97 | { 98 | 99 | unsigned int type; 100 | char *data; 101 | 102 | }; 103 | 104 | struct attribute_grid 105 | { 106 | 107 | char *format; 108 | 109 | }; 110 | 111 | struct attribute_icon 112 | { 113 | 114 | unsigned int type; 115 | 116 | }; 117 | 118 | struct attribute_id 119 | { 120 | 121 | char *name; 122 | 123 | }; 124 | 125 | struct attribute_in 126 | { 127 | 128 | char *name; 129 | 130 | }; 131 | 132 | struct attribute_label 133 | { 134 | 135 | char *content; 136 | 137 | }; 138 | 139 | struct attribute_link 140 | { 141 | 142 | char *url; 143 | 144 | }; 145 | 146 | struct attribute_mode 147 | { 148 | 149 | int type; 150 | 151 | }; 152 | 153 | struct attribute_range 154 | { 155 | 156 | int min; 157 | int max; 158 | 159 | }; 160 | 161 | struct attribute_target 162 | { 163 | 164 | int type; 165 | 166 | }; 167 | 168 | struct attribute_type 169 | { 170 | 171 | int type; 172 | 173 | }; 174 | 175 | void attribute_data_create(struct attribute_data *attribute, char *content); 176 | void attribute_data_destroy(struct attribute_data *attribute); 177 | void attribute_onclick_create(struct attribute_event *attribute, unsigned int type, char *data); 178 | void attribute_onclick_destroy(struct attribute_event *attribute); 179 | void attribute_onlinebreak_create(struct attribute_event *attribute, unsigned int type, char *data); 180 | void attribute_onlinebreak_destroy(struct attribute_event *attribute); 181 | void attribute_grid_create(struct attribute_grid *attribute, char *format); 182 | void attribute_grid_destroy(struct attribute_grid *attribute); 183 | void attribute_icon_create(struct attribute_icon *attribute, unsigned int type); 184 | void attribute_icon_destroy(struct attribute_icon *attribute); 185 | void attribute_id_create(struct attribute_id *attribute, char *name); 186 | void attribute_id_destroy(struct attribute_id *attribute); 187 | void attribute_in_create(struct attribute_in *attribute, char *name); 188 | void attribute_in_destroy(struct attribute_in *attribute); 189 | void attribute_label_create(struct attribute_label *attribute, char *content); 190 | void attribute_label_destroy(struct attribute_label *attribute); 191 | void attribute_link_create(struct attribute_link *attribute, char *url); 192 | void attribute_link_destroy(struct attribute_link *attribute); 193 | void attribute_mode_create(struct attribute_mode *attribute, unsigned int type); 194 | void attribute_mode_destroy(struct attribute_mode *attribute); 195 | void attribute_range_create(struct attribute_range *attribute, unsigned int min, unsigned int max); 196 | void attribute_range_destroy(struct attribute_range *attribute); 197 | void attribute_target_create(struct attribute_target *attribute, unsigned int type); 198 | void attribute_target_destroy(struct attribute_target *attribute); 199 | void attribute_type_create(struct attribute_type *attribute, unsigned int type); 200 | void attribute_type_destroy(struct attribute_type *attribute); 201 | -------------------------------------------------------------------------------- /src/fons.c: -------------------------------------------------------------------------------- 1 | #include 2 | #define STB_TRUETYPE_IMPLEMENTATION 3 | #include "stb_truetype.h" 4 | #include "fons.h" 5 | 6 | static unsigned int hashint(unsigned int a) 7 | { 8 | 9 | a += ~(a << 15); 10 | a ^= (a >> 10); 11 | a += (a << 3); 12 | a ^= (a >> 6); 13 | a += ~(a << 11); 14 | a ^= (a >> 16); 15 | 16 | return a; 17 | 18 | } 19 | 20 | static int maxi(int a, int b) 21 | { 22 | 23 | return a > b ? a : b; 24 | 25 | } 26 | 27 | static unsigned int decutf8(unsigned int state, unsigned int *codep, unsigned int byte) 28 | { 29 | 30 | static const unsigned char utf8d[] = { 31 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 36 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 37 | 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 38 | 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 11, 6, 6, 6, 5, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 39 | 0, 12, 24, 36, 60, 96, 84, 12, 12, 12, 48, 72, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 40 | 12, 0, 12, 12, 12, 12, 12, 0, 12, 0, 12, 12, 12, 24, 12, 12, 12, 12, 12, 24, 12, 24, 12, 12, 41 | 12, 12, 12, 12, 12, 12, 12, 24, 12, 12, 12, 12, 12, 24, 12, 12, 12, 12, 12, 12, 12, 24, 12, 12, 42 | 12, 12, 12, 12, 12, 12, 12, 36, 12, 36, 12, 12, 12, 36, 12, 12, 12, 12, 12, 36, 12, 36, 12, 12, 43 | 12, 36, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12 44 | }; 45 | 46 | unsigned int type = utf8d[byte]; 47 | 48 | *codep = (state) ? (byte & 0x3fu) | (*codep << 6) : (0xff >> type) & (byte); 49 | 50 | return utf8d[256 + state + type]; 51 | 52 | } 53 | 54 | static int atlasinsertnode(struct fons_atlas *atlas, int idx, int x, int y, int w) 55 | { 56 | 57 | int i; 58 | 59 | for (i = atlas->nnodes; i > idx; i--) 60 | atlas->nodes[i] = atlas->nodes[i - 1]; 61 | 62 | atlas->nodes[idx].x = (short)x; 63 | atlas->nodes[idx].y = (short)y; 64 | atlas->nodes[idx].width = (short)w; 65 | atlas->nnodes++; 66 | 67 | return 1; 68 | 69 | } 70 | 71 | static void atlasremovenode(struct fons_atlas *atlas, int idx) 72 | { 73 | 74 | int i; 75 | 76 | if (!atlas->nnodes) 77 | return; 78 | 79 | for (i = idx; i < atlas->nnodes - 1; i++) 80 | atlas->nodes[i] = atlas->nodes[i + 1]; 81 | 82 | atlas->nnodes--; 83 | 84 | } 85 | 86 | static int atlasaddskylinelevel(struct fons_atlas *atlas, int idx, int x, int y, int w, int h) 87 | { 88 | 89 | int i; 90 | 91 | if (!atlasinsertnode(atlas, idx, x, y + h, w)) 92 | return 0; 93 | 94 | for (i = idx + 1; i < atlas->nnodes; i++) 95 | { 96 | 97 | if (atlas->nodes[i].x < atlas->nodes[i - 1].x + atlas->nodes[i - 1].width) 98 | { 99 | 100 | int shrink = atlas->nodes[i - 1].x + atlas->nodes[i - 1].width - atlas->nodes[i].x; 101 | 102 | atlas->nodes[i].x += shrink; 103 | atlas->nodes[i].width -= shrink; 104 | 105 | if (atlas->nodes[i].width <= 0) 106 | { 107 | 108 | atlasremovenode(atlas, i); 109 | 110 | i--; 111 | 112 | } 113 | 114 | else 115 | { 116 | 117 | break; 118 | 119 | } 120 | 121 | } 122 | 123 | else 124 | { 125 | 126 | break; 127 | 128 | } 129 | 130 | } 131 | 132 | for (i = 0; i < atlas->nnodes - 1; i++) 133 | { 134 | 135 | if (atlas->nodes[i].y == atlas->nodes[i + 1].y) 136 | { 137 | 138 | atlas->nodes[i].width += atlas->nodes[i + 1].width; 139 | 140 | atlasremovenode(atlas, i + 1); 141 | 142 | i--; 143 | 144 | } 145 | 146 | } 147 | 148 | return 1; 149 | 150 | } 151 | 152 | static int atlasrectfits(struct fons_atlas *atlas, int i, int w, int h) 153 | { 154 | 155 | int x = atlas->nodes[i].x; 156 | int y = atlas->nodes[i].y; 157 | int spaceLeft; 158 | 159 | if (x + w > atlas->width) 160 | return -1; 161 | 162 | spaceLeft = w; 163 | 164 | while (spaceLeft > 0) 165 | { 166 | 167 | if (i == atlas->nnodes) 168 | return -1; 169 | 170 | y = maxi(y, atlas->nodes[i].y); 171 | 172 | if (y + h > atlas->height) 173 | return -1; 174 | 175 | spaceLeft -= atlas->nodes[i].width; 176 | ++i; 177 | 178 | } 179 | 180 | return y; 181 | 182 | } 183 | 184 | static int atlasaddrect(struct fons_atlas *atlas, int rw, int rh, int *rx, int *ry) 185 | { 186 | 187 | int besth = atlas->height; 188 | int bestw = atlas->width; 189 | int besti = -1; 190 | int bestx = -1; 191 | int besty = -1; 192 | unsigned int i; 193 | 194 | for (i = 0; i < atlas->nnodes; i++) 195 | { 196 | 197 | int y = atlasrectfits(atlas, i, rw, rh); 198 | 199 | if (y != -1) 200 | { 201 | 202 | if (y + rh < besth || (y + rh == besth && atlas->nodes[i].width < bestw)) 203 | { 204 | 205 | besti = i; 206 | bestw = atlas->nodes[i].width; 207 | besth = y + rh; 208 | bestx = atlas->nodes[i].x; 209 | besty = y; 210 | 211 | } 212 | 213 | } 214 | 215 | } 216 | 217 | if (besti == -1) 218 | return 0; 219 | 220 | if (!atlasaddskylinelevel(atlas, besti, bestx, besty, rw, rh)) 221 | return 0; 222 | 223 | *rx = bestx; 224 | *ry = besty; 225 | 226 | return 1; 227 | 228 | } 229 | 230 | void fons_create(struct fons_context *fsctx, int width, int height) 231 | { 232 | 233 | fsctx->width = width; 234 | fsctx->height = height; 235 | fsctx->atlas.width = width; 236 | fsctx->atlas.height = height; 237 | fsctx->atlas.nodes[0].x = 0; 238 | fsctx->atlas.nodes[0].y = 0; 239 | fsctx->atlas.nodes[0].width = width; 240 | fsctx->atlas.nnodes = 1; 241 | fsctx->nfonts = 0; 242 | fsctx->texdata = malloc(fsctx->width * fsctx->height); 243 | 244 | if (!fsctx->texdata) 245 | return; 246 | 247 | memset(fsctx->texdata, 0, fsctx->width * fsctx->height); 248 | 249 | } 250 | 251 | int fons_addfont(struct fons_context *fsctx, unsigned char *data, unsigned int count) 252 | { 253 | 254 | int i, ascent, descent, fh, lineGap; 255 | struct fons_font *font = &fsctx->fonts[fsctx->nfonts]; 256 | 257 | font->nglyphs = 0; 258 | 259 | for (i = 0; i < FONS_HASH_LUT_SIZE; ++i) 260 | font->lut[i] = -1; 261 | 262 | font->data = data; 263 | 264 | if (!stbtt_InitFont(&font->font, font->data, 0)) 265 | return -1; 266 | 267 | stbtt_GetFontVMetrics(&font->font, &ascent, &descent, &lineGap); 268 | 269 | fh = ascent - descent; 270 | font->ascender = (float)ascent / (float)fh; 271 | font->descender = (float)descent / (float)fh; 272 | font->lineh = (float)(fh + lineGap) / (float)fh; 273 | 274 | fsctx->nfonts++; 275 | 276 | return fsctx->nfonts - 1; 277 | 278 | } 279 | 280 | struct fons_glyph *fons_getglyph(struct fons_context *fsctx, struct fons_font *font, unsigned int codepoint, short size) 281 | { 282 | 283 | int i, g, advance, lsb; 284 | int x0, y0, x1, y1; 285 | int gw, gh, gx, gy; 286 | int x, y; 287 | float scale; 288 | struct fons_glyph *glyph = 0; 289 | unsigned int h; 290 | int pad = 2; 291 | int added; 292 | unsigned char *dst; 293 | 294 | if (size < 2) 295 | return 0; 296 | 297 | h = hashint(codepoint) & (FONS_HASH_LUT_SIZE - 1); 298 | i = font->lut[h]; 299 | 300 | while (i != -1) 301 | { 302 | 303 | if (font->glyphs[i].codepoint == codepoint && font->glyphs[i].size == size) 304 | { 305 | 306 | glyph = &font->glyphs[i]; 307 | 308 | if (glyph->x0 >= 0 && glyph->y0 >= 0) 309 | return glyph; 310 | 311 | break; 312 | 313 | } 314 | 315 | i = font->glyphs[i].next; 316 | 317 | } 318 | 319 | g = stbtt_FindGlyphIndex(&font->font, codepoint); 320 | 321 | if (!g) 322 | return 0; 323 | 324 | scale = stbtt_ScaleForPixelHeight(&font->font, size); 325 | 326 | stbtt_GetGlyphHMetrics(&font->font, g, &advance, &lsb); 327 | stbtt_GetGlyphBitmapBox(&font->font, g, scale, scale, &x0, &y0, &x1, &y1); 328 | 329 | gw = x1 - x0 + pad * 2; 330 | gh = y1 - y0 + pad * 2; 331 | 332 | added = atlasaddrect(&fsctx->atlas, gw, gh, &gx, &gy); 333 | 334 | if (!added) 335 | return 0; 336 | 337 | if (!glyph) 338 | { 339 | 340 | glyph = &font->glyphs[font->nglyphs]; 341 | glyph->codepoint = codepoint; 342 | glyph->size = size; 343 | glyph->next = 0; 344 | glyph->next = font->lut[h]; 345 | font->lut[h] = font->nglyphs; 346 | font->nglyphs++; 347 | 348 | } 349 | 350 | glyph->index = g; 351 | glyph->x0 = gx; 352 | glyph->y0 = gy; 353 | glyph->x1 = glyph->x0 + gw; 354 | glyph->y1 = glyph->y0 + gh; 355 | glyph->xadv = scale * advance; 356 | glyph->xoff = x0 - pad; 357 | glyph->yoff = y0 - pad; 358 | 359 | dst = &fsctx->texdata[(glyph->x0 + pad) + (glyph->y0 + pad) * fsctx->width]; 360 | 361 | stbtt_MakeGlyphBitmap(&font->font, dst, gw - pad * 2, gh - pad * 2, fsctx->width, scale, scale, g); 362 | 363 | dst = &fsctx->texdata[glyph->x0 + glyph->y0 * fsctx->width]; 364 | 365 | for (y = 0; y < gh; y++) 366 | { 367 | 368 | dst[y * fsctx->width] = 0; 369 | dst[gw - 1 + y * fsctx->width] = 0; 370 | 371 | } 372 | 373 | for (x = 0; x < gw; x++) 374 | { 375 | 376 | dst[x] = 0; 377 | dst[x + (gh - 1) * fsctx->width] = 0; 378 | 379 | } 380 | 381 | return glyph; 382 | 383 | } 384 | 385 | void fons_getquad(struct fons_context *fsctx, struct fons_glyph *glyph, struct fons_quad *quad, float x, float y) 386 | { 387 | 388 | float xoff = glyph->xoff + 1; 389 | float yoff = glyph->yoff + 1; 390 | float x0 = glyph->x0 + 1; 391 | float y0 = glyph->y0 + 1; 392 | float x1 = glyph->x1 - 1; 393 | float y1 = glyph->y1 - 1; 394 | float itw = 1.0f / fsctx->width; 395 | float ith = 1.0f / fsctx->height; 396 | float rx = (int)(x + xoff); 397 | float ry = (int)(y + yoff); 398 | 399 | quad->x0 = rx; 400 | quad->y0 = ry; 401 | quad->x1 = rx + x1 - x0; 402 | quad->y1 = ry + y1 - y0; 403 | quad->s0 = x0 * itw; 404 | quad->t0 = y0 * ith; 405 | quad->s1 = x1 * itw; 406 | quad->t1 = y1 * ith; 407 | 408 | } 409 | 410 | float getglyphwidth(struct fons_context *fsctx, struct fons_font *font, int codepoint, float size) 411 | { 412 | 413 | struct fons_glyph *glyph = fons_getglyph(fsctx, font, codepoint, size); 414 | 415 | return (glyph) ? (int)(glyph->xadv) : 0; 416 | 417 | } 418 | 419 | static float getstringwidth(struct fons_context *fsctx, struct fons_textiter *iter, float x) 420 | { 421 | 422 | unsigned int state = 0; 423 | float startx = x; 424 | 425 | for (; iter->str != iter->end; ++iter->str) 426 | { 427 | 428 | unsigned int codepoint; 429 | 430 | state = decutf8(state, &codepoint, *(const unsigned char *)iter->str); 431 | 432 | if (state) 433 | continue; 434 | 435 | x += getglyphwidth(fsctx, iter->font, codepoint, iter->size); 436 | 437 | } 438 | 439 | return x - startx; 440 | 441 | } 442 | 443 | int fons_inititer(struct fons_context *fsctx, struct fons_textiter *iter, struct fons_font *font, int align, float size, float x, float y, const char *str, const char *end) 444 | { 445 | 446 | iter->font = font; 447 | iter->size = size; 448 | iter->str = str; 449 | iter->next = str; 450 | iter->end = end; 451 | iter->codepoint = 0; 452 | iter->utf8state = 0; 453 | 454 | if (align & FONS_ALIGN_LEFT) 455 | iter->x = x; 456 | else if (align & FONS_ALIGN_RIGHT) 457 | iter->x = x - getstringwidth(fsctx, iter, x); 458 | else if (align & FONS_ALIGN_CENTER) 459 | iter->x = x - getstringwidth(fsctx, iter, x) * 0.5f; 460 | 461 | if (align & FONS_ALIGN_BASELINE) 462 | iter->y = y; 463 | else if (align & FONS_ALIGN_TOP) 464 | iter->y = y + iter->font->ascender * iter->size; 465 | else if (align & FONS_ALIGN_MIDDLE) 466 | iter->y = y + (iter->font->ascender + iter->font->descender) / 2.0f * iter->size; 467 | else if (align & FONS_ALIGN_BOTTOM) 468 | iter->y = y + iter->font->descender * iter->size; 469 | 470 | iter->nextx = iter->x; 471 | iter->nexty = iter->y; 472 | 473 | return 1; 474 | 475 | } 476 | 477 | int fons_nextiter(struct fons_context *fsctx, struct fons_textiter *iter) 478 | { 479 | 480 | const char *str = iter->next; 481 | 482 | iter->str = iter->next; 483 | 484 | if (str == iter->end) 485 | return 0; 486 | 487 | for (; str != iter->end; str++) 488 | { 489 | 490 | iter->utf8state = decutf8(iter->utf8state, &iter->codepoint, *(const unsigned char *)str); 491 | 492 | if (iter->utf8state) 493 | continue; 494 | 495 | str++; 496 | iter->x = iter->nextx; 497 | iter->y = iter->nexty; 498 | iter->nextx += getglyphwidth(fsctx, iter->font, iter->codepoint, iter->size); 499 | 500 | break; 501 | 502 | } 503 | 504 | iter->next = str; 505 | 506 | return 1; 507 | 508 | } 509 | 510 | void fons_delete(struct fons_context *fsctx) 511 | { 512 | 513 | if (fsctx->texdata) 514 | free(fsctx->texdata); 515 | 516 | } 517 | 518 | -------------------------------------------------------------------------------- /src/fons.h: -------------------------------------------------------------------------------- 1 | #define FONS_HASH_LUT_SIZE 256 2 | #define FONS_INIT_FONTS 8 3 | #define FONS_INIT_GLYPHS 256 4 | #define FONS_INIT_ATLAS_NODES 256 5 | 6 | enum fons_align 7 | { 8 | 9 | FONS_ALIGN_LEFT = 1 << 0, 10 | FONS_ALIGN_CENTER = 1 << 1, 11 | FONS_ALIGN_RIGHT = 1 << 2, 12 | FONS_ALIGN_TOP = 1 << 3, 13 | FONS_ALIGN_MIDDLE = 1 << 4, 14 | FONS_ALIGN_BOTTOM = 1 << 5, 15 | FONS_ALIGN_BASELINE = 1 << 6 16 | 17 | }; 18 | 19 | struct fons_quad 20 | { 21 | 22 | float x0, y0, s0, t0; 23 | float x1, y1, s1, t1; 24 | 25 | }; 26 | 27 | struct fons_textiter 28 | { 29 | 30 | float x, y; 31 | float nextx, nexty; 32 | unsigned int codepoint; 33 | short size; 34 | struct fons_font *font; 35 | const char *str; 36 | const char *next; 37 | const char *end; 38 | unsigned int utf8state; 39 | 40 | }; 41 | 42 | struct fons_glyph 43 | { 44 | 45 | unsigned int codepoint; 46 | int index; 47 | int next; 48 | short size; 49 | short x0, y0, x1, y1; 50 | short xadv, xoff, yoff; 51 | 52 | }; 53 | 54 | struct fons_font 55 | { 56 | 57 | stbtt_fontinfo font; 58 | unsigned char *data; 59 | float ascender; 60 | float descender; 61 | float lineh; 62 | struct fons_glyph glyphs[FONS_INIT_GLYPHS]; 63 | unsigned int nglyphs; 64 | int lut[FONS_HASH_LUT_SIZE]; 65 | 66 | }; 67 | 68 | struct fons_atlasnode 69 | { 70 | 71 | short x, y; 72 | short width; 73 | 74 | }; 75 | 76 | struct fons_atlas 77 | { 78 | 79 | int width, height; 80 | struct fons_atlasnode nodes[FONS_INIT_ATLAS_NODES]; 81 | unsigned int nnodes; 82 | 83 | }; 84 | 85 | struct fons_context 86 | { 87 | 88 | int width, height; 89 | unsigned char *texdata; 90 | struct fons_atlas atlas; 91 | struct fons_font fonts[FONS_INIT_FONTS]; 92 | unsigned int nfonts; 93 | 94 | }; 95 | 96 | void fons_create(struct fons_context *fsctx, int width, int height); 97 | void fons_delete(struct fons_context *fsctx); 98 | int fons_addfont(struct fons_context *fsctx, unsigned char *data, unsigned int count); 99 | struct fons_glyph *fons_getglyph(struct fons_context *fsctx, struct fons_font *font, unsigned int codepoint, short size); 100 | void fons_getquad(struct fons_context *fsctx, struct fons_glyph *glyph, struct fons_quad *quad, float x, float y); 101 | int fons_inititer(struct fons_context *fsctx, struct fons_textiter *iter, struct fons_font *font, int align, float size, float x, float y, const char *str, const char *end); 102 | int fons_nextiter(struct fons_context *fsctx, struct fons_textiter *iter); 103 | -------------------------------------------------------------------------------- /src/gridfmt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "gridfmt.h" 4 | 5 | static unsigned int copycolumn(char *coldata, char *format) 6 | { 7 | 8 | unsigned int length = strlen(format); 9 | unsigned int i; 10 | 11 | for (i = 0; i < length; i++) 12 | { 13 | 14 | if (format[i] == ':') 15 | break; 16 | 17 | coldata[i] = format[i]; 18 | 19 | } 20 | 21 | coldata[i] = '\0'; 22 | 23 | return i; 24 | 25 | } 26 | 27 | static unsigned int getcolumn(char *coldata, unsigned int index, char *format) 28 | { 29 | 30 | unsigned int length = strlen(format); 31 | unsigned int i; 32 | 33 | if (!index) 34 | return copycolumn(coldata, format); 35 | 36 | for (i = 0; i < length; i++) 37 | { 38 | 39 | if (format[i] == ':') 40 | { 41 | 42 | if (!--index) 43 | return copycolumn(coldata, format + i + 1); 44 | 45 | } 46 | 47 | } 48 | 49 | return 0; 50 | 51 | } 52 | 53 | unsigned int gridfmt_size(char *format) 54 | { 55 | 56 | unsigned int length = strlen(format); 57 | unsigned int size = 0; 58 | unsigned int i; 59 | 60 | for (i = 0; i < length; i++) 61 | { 62 | 63 | if (format[i] == ':') 64 | size++; 65 | 66 | } 67 | 68 | return size + 1; 69 | 70 | } 71 | 72 | unsigned int gridfmt_colsize(char *format, unsigned int index) 73 | { 74 | 75 | char coldata[8]; 76 | unsigned int count = getcolumn(coldata, index, format); 77 | 78 | if (count >= 2) 79 | return 10 * (coldata[0] - '0') + (coldata[1] - '0'); 80 | 81 | return 0; 82 | 83 | } 84 | 85 | unsigned int gridfmt_colhalign(char *format, unsigned int index) 86 | { 87 | 88 | char coldata[8]; 89 | unsigned int count = getcolumn(coldata, index, format); 90 | 91 | if (count >= 3) 92 | { 93 | 94 | switch (coldata[2]) 95 | { 96 | 97 | case 'L': 98 | return GRIDFMT_HALIGN_LEFT; 99 | 100 | case 'R': 101 | return GRIDFMT_HALIGN_RIGHT; 102 | 103 | case 'C': 104 | return GRIDFMT_HALIGN_CENTER; 105 | 106 | } 107 | 108 | } 109 | 110 | return GRIDFMT_HALIGN_LEFT; 111 | 112 | } 113 | 114 | unsigned int gridfmt_colvalign(char *format, unsigned int index) 115 | { 116 | 117 | char coldata[8]; 118 | unsigned int count = getcolumn(coldata, index, format); 119 | 120 | if (count >= 4) 121 | { 122 | 123 | switch (coldata[3]) 124 | { 125 | 126 | case 'T': 127 | return GRIDFMT_VALIGN_TOP; 128 | 129 | case 'B': 130 | return GRIDFMT_VALIGN_BOTTOM; 131 | 132 | case 'M': 133 | return GRIDFMT_VALIGN_MIDDLE; 134 | 135 | } 136 | 137 | } 138 | 139 | return GRIDFMT_VALIGN_TOP; 140 | 141 | } 142 | 143 | -------------------------------------------------------------------------------- /src/gridfmt.h: -------------------------------------------------------------------------------- 1 | #define GRIDFMT_HALIGN_LEFT 1 2 | #define GRIDFMT_HALIGN_CENTER 2 3 | #define GRIDFMT_HALIGN_RIGHT 3 4 | #define GRIDFMT_VALIGN_TOP 1 5 | #define GRIDFMT_VALIGN_MIDDLE 2 6 | #define GRIDFMT_VALIGN_BOTTOM 3 7 | 8 | unsigned int gridfmt_size(char *format); 9 | unsigned int gridfmt_colsize(char *format, unsigned int index); 10 | unsigned int gridfmt_colhalign(char *format, unsigned int index); 11 | unsigned int gridfmt_colvalign(char *format, unsigned int index); 12 | -------------------------------------------------------------------------------- /src/history.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "url.h" 5 | #include "history.h" 6 | 7 | static struct history stack[32]; 8 | static unsigned int historycount; 9 | 10 | struct history *history_get(unsigned int index) 11 | { 12 | 13 | return &stack[historycount - index - 1]; 14 | 15 | } 16 | 17 | struct history *history_push(void) 18 | { 19 | 20 | if (historycount < 31) 21 | historycount++; 22 | 23 | return history_get(0); 24 | 25 | } 26 | 27 | struct history *history_pop(void) 28 | { 29 | 30 | if (historycount > 1) 31 | historycount--; 32 | 33 | return history_get(0); 34 | 35 | } 36 | 37 | char *history_geturl(unsigned int index) 38 | { 39 | 40 | struct history *info = history_get(index); 41 | 42 | if (info) 43 | return info->url; 44 | 45 | return 0; 46 | 47 | } 48 | 49 | -------------------------------------------------------------------------------- /src/history.h: -------------------------------------------------------------------------------- 1 | struct history 2 | { 3 | 4 | char url[URL_SIZE]; 5 | 6 | }; 7 | 8 | struct history *history_get(unsigned int index); 9 | struct history *history_push(void); 10 | struct history *history_pop(void); 11 | char *history_geturl(unsigned int index); 12 | -------------------------------------------------------------------------------- /src/list.c: -------------------------------------------------------------------------------- 1 | #include "list.h" 2 | 3 | void list_add(struct list *list, struct list_item *item) 4 | { 5 | 6 | item->prev = list->tail; 7 | item->next = 0; 8 | 9 | if (list->head) 10 | list->tail->next = item; 11 | else 12 | list->head = item; 13 | 14 | list->tail = item; 15 | list->count++; 16 | 17 | } 18 | 19 | void list_remove(struct list *list, struct list_item *item) 20 | { 21 | 22 | if (list->head == item) 23 | list->head = item->next; 24 | 25 | if (list->tail == item) 26 | list->tail = item->prev; 27 | 28 | if (list->head) 29 | list->head->prev = 0; 30 | 31 | if (list->tail) 32 | list->tail->next = 0; 33 | 34 | if (item->next) 35 | item->next->prev = item->prev; 36 | 37 | if (item->prev) 38 | item->prev->next = item->next; 39 | 40 | item->next = 0; 41 | item->prev = 0; 42 | list->count--; 43 | 44 | } 45 | 46 | void list_move(struct list *to, struct list *from, struct list_item *item) 47 | { 48 | 49 | list_remove(from, item); 50 | list_add(to, item); 51 | 52 | } 53 | 54 | struct list_item *list_pickhead(struct list *list) 55 | { 56 | 57 | struct list_item *item = list->head; 58 | 59 | if (item) 60 | list_remove(list, item); 61 | 62 | return item; 63 | 64 | } 65 | 66 | struct list_item *list_picktail(struct list *list) 67 | { 68 | 69 | struct list_item *item = list->tail; 70 | 71 | if (item) 72 | list_remove(list, item); 73 | 74 | return item; 75 | 76 | } 77 | 78 | void list_inititem(struct list_item *item, void *data) 79 | { 80 | 81 | item->next = 0; 82 | item->prev = 0; 83 | item->data = data; 84 | 85 | } 86 | 87 | -------------------------------------------------------------------------------- /src/list.h: -------------------------------------------------------------------------------- 1 | struct list_item 2 | { 3 | 4 | struct list_item *next; 5 | struct list_item *prev; 6 | void *data; 7 | 8 | }; 9 | 10 | struct list 11 | { 12 | 13 | struct list_item *head; 14 | struct list_item *tail; 15 | unsigned int count; 16 | 17 | }; 18 | 19 | void list_add(struct list *list, struct list_item *item); 20 | void list_remove(struct list *list, struct list_item *item); 21 | void list_move(struct list *to, struct list *from, struct list_item *item); 22 | struct list_item *list_pickhead(struct list *list); 23 | struct list_item *list_picktail(struct list *list); 24 | void list_inititem(struct list_item *item, void *data); 25 | -------------------------------------------------------------------------------- /src/navi-resolve: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | resolve_navi_bookmarks() { 4 | echo "= window label \"Bookmarks\"" 5 | echo "+ header label \"Bookmarks\"" 6 | 7 | if [ -f ~/.navi_bookmarks ] 8 | then 9 | cat ~/.navi_bookmarks | while read line 10 | do 11 | label=$(echo $line | cut -d ',' -f 1 | tr -d '"') 12 | link=$(echo $line | cut -d ',' -f 2 | tr -d '"') 13 | 14 | echo "+ anchor label \"$label\" onclick \"get\" \"$link\"" 15 | done 16 | else 17 | echo "+ text label \"No bookmarks found.\"" 18 | fi 19 | exit 0 20 | } 21 | 22 | resolve_navi_history() { 23 | echo "= window label \"History\"" 24 | echo "+ header label \"History\"" 25 | 26 | if [ -f ~/.navi_history ] 27 | then 28 | cat ~/.navi_history | while read line 29 | do 30 | label=$(echo $line | cut -d ',' -f 1 | tr -d '"') 31 | link=$(echo $line | cut -d ',' -f 2 | tr -d '"') 32 | 33 | echo "+ anchor label \"$label\" onclick \"get\" \"$link\"" 34 | done 35 | else 36 | echo "+ text label \"No history found.\"" 37 | fi 38 | exit 0 39 | } 40 | 41 | resolve_navi() { 42 | case "$1" in 43 | navi://bookmarks) 44 | resolve_navi_bookmarks $@ 45 | ;; 46 | navi://history) 47 | resolve_navi_history $@ 48 | ;; 49 | esac 50 | } 51 | 52 | resolve_file() { 53 | path=${1#file://} 54 | 55 | if test -z $path 56 | then 57 | path=$(pwd) 58 | fi 59 | 60 | if test -f $path 61 | then 62 | cat $path 63 | exit 0 64 | elif test -d $path 65 | then 66 | echo "= window label \"Index of $path\"" 67 | echo "+ header label \"Index of $path\"" 68 | echo "+ divider" 69 | echo "+ table id table1 grid \"06:02:04\"" 70 | echo "+ header2 in table1 label \"Name\"" 71 | echo "+ header2 in table1 label \"Size\"" 72 | echo "+ header2 in table1 label \"Modified\"" 73 | 74 | find $path -mindepth 1 -maxdepth 1 -type d | while read entry 75 | do 76 | name=$(stat --printf="%n" "$entry") 77 | name=$(basename "$name") 78 | size=$(stat --printf="%s" "$entry") 79 | size=$(numfmt --to=si $size) 80 | modified=$(stat --printf="%Y" "$entry") 81 | modified=$(date -d @$modified) 82 | 83 | echo "+ anchor in table1 label \"$name/\" onclick \"get\" \"file://$entry\"" 84 | echo "+ text in table1 label \"$size\"" 85 | echo "+ text in table1 label \"$modified\"" 86 | done 87 | 88 | find $path -mindepth 1 -maxdepth 1 -type f | while read entry 89 | do 90 | name=$(stat --printf="%n" "$entry") 91 | name=$(basename "$name") 92 | size=$(stat --printf="%s" "$entry") 93 | size=$(numfmt --to=si $size) 94 | modified=$(stat --printf="%Y" "$entry") 95 | modified=$(date -d @$modified) 96 | 97 | case "$name" in 98 | *.alfi) 99 | echo "+ anchor in table1 label \"$name\" onclick \"get\" \"file://$entry\"" 100 | ;; 101 | *) 102 | echo "+ text in table1 label \"$name\"" 103 | ;; 104 | esac 105 | 106 | echo "+ text in table1 label \"$size\"" 107 | echo "+ text in table1 label \"$modified\"" 108 | done 109 | 110 | exit 0 111 | fi 112 | 113 | exit 1 114 | } 115 | 116 | resolve() { 117 | case "$1" in 118 | navi://*) 119 | resolve_navi $@ 120 | ;; 121 | file://*) 122 | resolve_file $@ 123 | ;; 124 | esac 125 | } 126 | 127 | if [ $# -ge 1 ] 128 | then 129 | resolve $@ 130 | fi 131 | 132 | -------------------------------------------------------------------------------- /src/navi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #if defined NVG_GL_GLEW 7 | #include 8 | #endif 9 | 10 | #if defined NVG_GL_VERSION_GLES2 11 | #include 12 | #endif 13 | 14 | #if defined NVG_GL_VERSION_GLES3 15 | #include 16 | #endif 17 | 18 | #include 19 | #include "list.h" 20 | #include "style.h" 21 | #include "url.h" 22 | #include "resource.h" 23 | #include "view.h" 24 | #include "attribute.h" 25 | #include "widget.h" 26 | #include "animation.h" 27 | #include "parser.h" 28 | #include "pool.h" 29 | #include "render.h" 30 | #include "history.h" 31 | 32 | #define FLAG_NONE 0 33 | #define FLAG_FOCUSABLE 1 34 | 35 | static struct view view; 36 | static struct parser parser; 37 | static double mouse_x; 38 | static double mouse_y; 39 | static GLFWcursor *cursor_arrow; 40 | static GLFWcursor *cursor_ibeam; 41 | static GLFWcursor *cursor_hand; 42 | static struct widget *widget_root; 43 | static struct widget *widget_main; 44 | static struct widget *widget_hover; 45 | static struct widget *widget_focus; 46 | static unsigned int updatetitle; 47 | 48 | static struct widget *parser_create(unsigned int type, char *id, char *in) 49 | { 50 | 51 | struct widget *widget = pool_widget_create(); 52 | 53 | if (widget) 54 | { 55 | 56 | memset(&widget->header, 0, sizeof (struct widget_header)); 57 | memset(&widget->payload, 0, sizeof (union widget_payload)); 58 | 59 | widget_header_create(&widget->header, type, id, in); 60 | widget_payload_create(&widget->payload, widget->header.type); 61 | 62 | } 63 | 64 | return widget; 65 | 66 | } 67 | 68 | static struct widget *parser_destroy(struct widget *widget) 69 | { 70 | 71 | widget_payload_destroy(&widget->payload, widget->header.type); 72 | widget_header_destroy(&widget->header); 73 | 74 | return pool_widget_destroy(widget); 75 | 76 | } 77 | 78 | static void parser_clear(struct widget *widget) 79 | { 80 | 81 | struct widget *child = 0; 82 | 83 | while ((child = pool_widget_nextchild(child, widget))) 84 | { 85 | 86 | parser_clear(child); 87 | 88 | child = parser_destroy(child); 89 | 90 | } 91 | 92 | } 93 | 94 | static void parser_fail(void) 95 | { 96 | 97 | printf("Parsing failed on line %u\n", parser.expr.line + 1); 98 | 99 | } 100 | 101 | static unsigned int buildkeyvalue(void *buffer, unsigned int count, unsigned int offset, char *key, char *value) 102 | { 103 | 104 | if (offset) 105 | return snprintf(buffer, count, "&%s=%s", key, value); 106 | else 107 | return snprintf(buffer, count, "%s=%s", key, value); 108 | 109 | } 110 | 111 | static unsigned int builddata(char *buffer, unsigned int count) 112 | { 113 | 114 | struct widget *widget = 0; 115 | unsigned int offset = 0; 116 | 117 | while ((widget = pool_widget_next(widget))) 118 | { 119 | 120 | if (!strlen(widget->header.id.name)) 121 | continue; 122 | 123 | switch (widget->header.type) 124 | { 125 | 126 | case WIDGET_TYPE_FIELD: 127 | offset += buildkeyvalue(buffer + offset, count - offset, offset, widget->header.id.name, widget->payload.field.data.content); 128 | 129 | break; 130 | 131 | case WIDGET_TYPE_SELECT: 132 | offset += buildkeyvalue(buffer + offset, count - offset, offset, widget->header.id.name, widget->payload.select.data.content); 133 | 134 | break; 135 | 136 | case WIDGET_TYPE_TOGGLE: 137 | if (widget->payload.toggle.mode.type == ATTRIBUTE_MODE_ON) 138 | offset += buildkeyvalue(buffer + offset, count - offset, offset, widget->header.id.name, widget->payload.select.data.content); 139 | 140 | break; 141 | 142 | } 143 | 144 | } 145 | 146 | return offset; 147 | 148 | } 149 | 150 | static struct widget *findtouchingwidget(struct widget *widget, float x, float y) 151 | { 152 | 153 | struct widget *child = 0; 154 | 155 | while ((child = pool_widget_nextchild(child, widget))) 156 | { 157 | 158 | struct frame *frame = pool_getframe(child->index); 159 | 160 | if (style_box_istouching(&frame->bounds, x, y)) 161 | return findtouchingwidget(child, x, y); 162 | 163 | } 164 | 165 | return widget; 166 | 167 | } 168 | 169 | static unsigned int getflags(struct widget *widget) 170 | { 171 | 172 | switch (widget->header.type) 173 | { 174 | 175 | case WIDGET_TYPE_BUTTON: 176 | case WIDGET_TYPE_FIELD: 177 | case WIDGET_TYPE_SELECT: 178 | case WIDGET_TYPE_TOGGLE: 179 | return FLAG_FOCUSABLE; 180 | 181 | } 182 | 183 | return FLAG_NONE; 184 | 185 | } 186 | 187 | static unsigned int checkflag(struct widget *widget, unsigned int flag) 188 | { 189 | 190 | return getflags(widget) & flag; 191 | 192 | } 193 | 194 | static struct widget *prevflag(struct widget *widget, unsigned int flag) 195 | { 196 | 197 | while ((widget = pool_widget_prev(widget))) 198 | { 199 | 200 | if (checkflag(widget, flag)) 201 | return widget; 202 | 203 | } 204 | 205 | return 0; 206 | 207 | } 208 | 209 | static struct widget *nextflag(struct widget *widget, unsigned int flag) 210 | { 211 | 212 | while ((widget = pool_widget_next(widget))) 213 | { 214 | 215 | if (checkflag(widget, flag)) 216 | return widget; 217 | 218 | } 219 | 220 | return 0; 221 | 222 | } 223 | 224 | static void setcursor(struct GLFWwindow *window, unsigned int type) 225 | { 226 | 227 | switch (type) 228 | { 229 | 230 | case ANIMATION_CURSOR_HAND: 231 | glfwSetCursor(window, cursor_hand); 232 | 233 | break; 234 | 235 | case ANIMATION_CURSOR_IBEAM: 236 | glfwSetCursor(window, cursor_ibeam); 237 | 238 | break; 239 | 240 | case ANIMATION_CURSOR_ARROW: 241 | glfwSetCursor(window, cursor_arrow); 242 | 243 | break; 244 | 245 | default: 246 | glfwSetCursor(window, cursor_arrow); 247 | 248 | break; 249 | 250 | } 251 | 252 | } 253 | 254 | static void setfocus(struct widget *widget) 255 | { 256 | 257 | if (widget != widget_focus) 258 | { 259 | 260 | if (widget_focus) 261 | widget_changestate(&widget_focus->header, WIDGET_STATE_UNFOCUS); 262 | 263 | widget_focus = widget; 264 | 265 | if (widget_focus) 266 | widget_changestate(&widget_focus->header, WIDGET_STATE_FOCUS); 267 | 268 | } 269 | 270 | } 271 | 272 | static void sethover(struct widget *widget) 273 | { 274 | 275 | if (widget != widget_hover) 276 | { 277 | 278 | if (widget_hover) 279 | widget_changestate(&widget_hover->header, WIDGET_STATE_UNHOVER); 280 | 281 | widget_hover = widget; 282 | 283 | if (widget_hover) 284 | widget_changestate(&widget_hover->header, WIDGET_STATE_HOVER); 285 | 286 | } 287 | 288 | } 289 | 290 | static void loadresources_linklabel(struct attribute_link *link, struct attribute_label *label) 291 | { 292 | 293 | struct resource resource; 294 | char url[URL_SIZE]; 295 | 296 | if (!strlen(link->url)) 297 | return; 298 | 299 | url_merge(url, history_geturl(0), link->url); 300 | attribute_link_create(link, url); 301 | resource_init(&resource, link->url); 302 | resource_load(&resource, 0, 0); 303 | 304 | if (resource.count) 305 | { 306 | 307 | char *data = resource.data; 308 | 309 | data[resource.count - 1] = '\0'; 310 | 311 | attribute_label_create(label, data); 312 | 313 | } 314 | 315 | resource_unload(&resource); 316 | resource_destroy(&resource); 317 | 318 | } 319 | 320 | static void loadresources_image(struct attribute_link *link) 321 | { 322 | 323 | char url[URL_SIZE]; 324 | 325 | if (!strlen(link->url)) 326 | return; 327 | 328 | url_merge(url, history_geturl(0), link->url); 329 | attribute_link_create(link, url); 330 | render_loadimage(link->url); 331 | 332 | } 333 | 334 | static void loadresources(void) 335 | { 336 | 337 | struct widget *widget = 0; 338 | 339 | while ((widget = pool_widget_next(widget))) 340 | { 341 | 342 | switch (widget->header.type) 343 | { 344 | 345 | case WIDGET_TYPE_CODE: 346 | loadresources_linklabel(&widget->payload.code.link, &widget->payload.code.label); 347 | 348 | break; 349 | 350 | case WIDGET_TYPE_IMAGE: 351 | loadresources_image(&widget->payload.image.link); 352 | 353 | break; 354 | 355 | case WIDGET_TYPE_TEXT: 356 | loadresources_linklabel(&widget->payload.text.link, &widget->payload.text.label); 357 | 358 | break; 359 | 360 | } 361 | 362 | } 363 | 364 | } 365 | 366 | static void urlself(char *url, unsigned int count, void *data) 367 | { 368 | 369 | struct resource temp; 370 | 371 | resource_init(&temp, url); 372 | resource_load(&temp, count, data); 373 | 374 | if (temp.count) 375 | { 376 | 377 | parser_parse(&parser, "main", temp.count, temp.data); 378 | 379 | if (parser.errors) 380 | { 381 | 382 | urlself("navi://syntaxerror", strlen(temp.url), temp.url); 383 | 384 | } 385 | 386 | else 387 | { 388 | 389 | struct frame *frame = pool_getframe(widget_root->index); 390 | struct frame keyframe; 391 | 392 | loadresources(); 393 | animation_initframe(&keyframe, view.scrollx + view.padw, view.scrolly + view.padh, view.unitw * 24, 0); 394 | animation_step(widget_root, &keyframe, &view, 1.0); 395 | animation_updateframe(widget_root->header.type, frame, &keyframe, 1.0); 396 | 397 | updatetitle = 1; 398 | 399 | } 400 | 401 | } 402 | 403 | else 404 | { 405 | 406 | urlself("navi://notfound", strlen(temp.url), temp.url); 407 | 408 | } 409 | 410 | resource_unload(&temp); 411 | resource_destroy(&temp); 412 | 413 | } 414 | 415 | static void urlblank(char *url, unsigned int count, void *data) 416 | { 417 | 418 | struct frame *frame = pool_getframe(widget_root->index); 419 | 420 | sethover(0); 421 | setfocus(0); 422 | parser_clear(widget_main); 423 | view_reset(&view); 424 | view_adjust(&view, frame->bounds.w, frame->bounds.h); 425 | urlself(url, count, data); 426 | 427 | } 428 | 429 | static void loadself(char *url, unsigned int count, void *data) 430 | { 431 | 432 | char purl[URL_SIZE]; 433 | 434 | url_merge(purl, history_geturl(0), url); 435 | urlself(purl, count, data); 436 | 437 | } 438 | 439 | static void loadblank(char *url, unsigned int count, void *data) 440 | { 441 | 442 | struct history *current = history_get(0); 443 | struct history *next = history_push(); 444 | 445 | url_merge(next->url, current->url, url); 446 | urlblank(next->url, count, data); 447 | 448 | } 449 | 450 | static void loadfunction(unsigned int type, char *data, unsigned int target) 451 | { 452 | 453 | char pdata[RESOURCE_PAGESIZE]; 454 | 455 | switch (type) 456 | { 457 | 458 | case FUNCTION_ALFI: 459 | /* Need to unescape instead of strcpy */ 460 | strcpy(pdata, data); 461 | parser_parse(&parser, "main", strlen(pdata) + 1, pdata); 462 | 463 | break; 464 | 465 | case FUNCTION_GET: 466 | switch (target) 467 | { 468 | 469 | case ATTRIBUTE_TARGET_SELF: 470 | loadself(data, 0, 0); 471 | 472 | break; 473 | 474 | case ATTRIBUTE_TARGET_BLANK: 475 | loadblank(data, 0, 0); 476 | 477 | break; 478 | 479 | } 480 | 481 | break; 482 | 483 | case FUNCTION_POST: 484 | switch (target) 485 | { 486 | 487 | case ATTRIBUTE_TARGET_SELF: 488 | loadself(data, builddata(pdata, RESOURCE_PAGESIZE), pdata); 489 | 490 | break; 491 | 492 | case ATTRIBUTE_TARGET_BLANK: 493 | loadblank(data, builddata(pdata, RESOURCE_PAGESIZE), pdata); 494 | 495 | break; 496 | 497 | } 498 | 499 | break; 500 | 501 | } 502 | 503 | } 504 | 505 | static void create(char *title) 506 | { 507 | 508 | widget_root = parser_create(WIDGET_TYPE_WINDOW, "window", ""); 509 | widget_main = parser_create(WIDGET_TYPE_TABLE, "main", "window"); 510 | 511 | attribute_label_create(&widget_root->payload.window.label, title); 512 | 513 | } 514 | 515 | static void onerror(int error, const char *desc) 516 | { 517 | 518 | printf("GLFW error %d: %s\n", error, desc); 519 | 520 | } 521 | 522 | static void onwindowsize(GLFWwindow *window, int width, int height) 523 | { 524 | 525 | view_init(&view, width, height, 0); 526 | 527 | } 528 | 529 | static void onframebuffersize(GLFWwindow *window, int width, int height) 530 | { 531 | 532 | glViewport(0, 0, width, height); 533 | 534 | } 535 | 536 | static void oninput_field(struct widget_payload_field *payload, int key, int scancode, int action, int mods) 537 | { 538 | 539 | if (action == GLFW_PRESS || action == GLFW_REPEAT) 540 | { 541 | 542 | switch (key) 543 | { 544 | 545 | case GLFW_KEY_LEFT: 546 | if (payload->data.offset > 0) 547 | payload->data.offset--; 548 | 549 | break; 550 | 551 | case GLFW_KEY_RIGHT: 552 | if (payload->data.offset < strlen(payload->data.content)) 553 | payload->data.offset++; 554 | 555 | break; 556 | 557 | case GLFW_KEY_ENTER: 558 | if (payload->onlinebreak.type) 559 | { 560 | 561 | loadfunction(payload->onlinebreak.type, payload->onlinebreak.data, ATTRIBUTE_TARGET_BLANK); 562 | 563 | } 564 | 565 | else 566 | { 567 | 568 | if (payload->data.offset < payload->data.total - 1) 569 | { 570 | 571 | payload->data.content[payload->data.offset] = '\n'; 572 | payload->data.offset++; 573 | payload->data.content[payload->data.offset] = '\0'; 574 | 575 | } 576 | 577 | } 578 | 579 | break; 580 | 581 | case GLFW_KEY_BACKSPACE: 582 | if (payload->data.offset > 0) 583 | { 584 | 585 | payload->data.content[payload->data.offset] = '\0'; 586 | payload->data.offset--; 587 | payload->data.content[payload->data.offset] = '\0'; 588 | 589 | } 590 | 591 | break; 592 | 593 | } 594 | 595 | } 596 | 597 | } 598 | 599 | static void oninput_select(struct widget_payload_select *payload, int key, int scancode, int action, int mods) 600 | { 601 | 602 | if (action == GLFW_PRESS || action == GLFW_REPEAT) 603 | { 604 | 605 | switch (key) 606 | { 607 | 608 | case GLFW_KEY_UP: 609 | break; 610 | 611 | case GLFW_KEY_DOWN: 612 | break; 613 | 614 | case GLFW_KEY_ENTER: 615 | break; 616 | 617 | } 618 | 619 | } 620 | 621 | } 622 | 623 | static void oninput_toggle(struct widget_payload_toggle *payload, int key, int scancode, int action, int mods) 624 | { 625 | 626 | if (action == GLFW_PRESS || action == GLFW_REPEAT) 627 | { 628 | 629 | switch (key) 630 | { 631 | 632 | case GLFW_KEY_SPACE: 633 | switch (payload->mode.type) 634 | { 635 | 636 | case ATTRIBUTE_MODE_OFF: 637 | payload->mode.type = ATTRIBUTE_MODE_ON; 638 | 639 | break; 640 | 641 | case ATTRIBUTE_MODE_ON: 642 | payload->mode.type = ATTRIBUTE_MODE_OFF; 643 | 644 | break; 645 | 646 | } 647 | 648 | break; 649 | 650 | } 651 | 652 | } 653 | 654 | } 655 | 656 | static void onkey(GLFWwindow *window, int key, int scancode, int action, int mods) 657 | { 658 | 659 | if (action == GLFW_PRESS) 660 | { 661 | 662 | switch (key) 663 | { 664 | 665 | case GLFW_KEY_B: 666 | if (mods & GLFW_MOD_CONTROL) 667 | loadblank("navi://bookmarks", 0, 0); 668 | 669 | break; 670 | 671 | case GLFW_KEY_D: 672 | if (mods & GLFW_MOD_CONTROL) 673 | loadblank("file://", 0, 0); 674 | 675 | break; 676 | 677 | case GLFW_KEY_H: 678 | if (mods & GLFW_MOD_CONTROL) 679 | loadblank("navi://history", 0, 0); 680 | 681 | break; 682 | 683 | case GLFW_KEY_L: 684 | if (mods & GLFW_MOD_CONTROL) 685 | loadblank("navi://blank", 0, 0); 686 | 687 | break; 688 | 689 | case GLFW_KEY_M: 690 | if (mods & GLFW_MOD_CONTROL) 691 | animation_settheme(ANIMATION_THEME_DARK); 692 | 693 | break; 694 | 695 | case GLFW_KEY_N: 696 | if (mods & GLFW_MOD_CONTROL) 697 | animation_settheme(ANIMATION_THEME_LIGHT); 698 | 699 | break; 700 | 701 | case GLFW_KEY_Q: 702 | if (mods & GLFW_MOD_CONTROL) 703 | glfwSetWindowShouldClose(window, GL_TRUE); 704 | 705 | break; 706 | 707 | case GLFW_KEY_R: 708 | if (mods & GLFW_MOD_CONTROL) 709 | urlblank(history_geturl(0), 0, 0); 710 | 711 | break; 712 | 713 | case GLFW_KEY_V: 714 | if (mods & GLFW_MOD_CONTROL) 715 | { 716 | 717 | if (widget_focus && widget_focus->header.type == WIDGET_TYPE_FIELD) 718 | { 719 | 720 | struct widget_payload_field *payload = &widget_focus->payload.field; 721 | const char *c = glfwGetClipboardString(window); 722 | 723 | if (c) 724 | { 725 | 726 | unsigned int l = strlen(c); 727 | unsigned int i; 728 | 729 | for (i = 0; i < l; i++) 730 | { 731 | 732 | if (payload->data.offset < payload->data.total - 1) 733 | { 734 | 735 | payload->data.content[payload->data.offset] = c[i]; 736 | payload->data.offset++; 737 | payload->data.content[payload->data.offset] = '\0'; 738 | 739 | } 740 | 741 | } 742 | 743 | } 744 | 745 | } 746 | 747 | } 748 | 749 | break; 750 | 751 | } 752 | 753 | } 754 | 755 | if (action == GLFW_PRESS || action == GLFW_REPEAT) 756 | { 757 | 758 | switch (key) 759 | { 760 | 761 | case GLFW_KEY_TAB: 762 | if (mods & GLFW_MOD_SHIFT) 763 | { 764 | 765 | struct widget *widget = prevflag(widget_focus, FLAG_FOCUSABLE); 766 | 767 | if (!widget) 768 | widget = prevflag(0, FLAG_FOCUSABLE); 769 | 770 | setfocus(widget); 771 | 772 | } 773 | 774 | else 775 | { 776 | 777 | struct widget *widget = nextflag(widget_focus, FLAG_FOCUSABLE); 778 | 779 | if (!widget) 780 | widget = nextflag(0, FLAG_FOCUSABLE); 781 | 782 | setfocus(widget); 783 | 784 | } 785 | 786 | break; 787 | 788 | } 789 | 790 | } 791 | 792 | if (widget_focus && checkflag(widget_focus, FLAG_FOCUSABLE)) 793 | { 794 | 795 | switch (widget_focus->header.type) 796 | { 797 | 798 | case WIDGET_TYPE_FIELD: 799 | oninput_field(&widget_focus->payload.field, key, scancode, action, mods); 800 | 801 | break; 802 | 803 | case WIDGET_TYPE_SELECT: 804 | oninput_select(&widget_focus->payload.select, key, scancode, action, mods); 805 | 806 | break; 807 | 808 | case WIDGET_TYPE_TOGGLE: 809 | oninput_toggle(&widget_focus->payload.toggle, key, scancode, action, mods); 810 | 811 | break; 812 | 813 | } 814 | 815 | } 816 | 817 | else 818 | { 819 | 820 | if (action == GLFW_PRESS || action == GLFW_REPEAT) 821 | { 822 | 823 | switch (key) 824 | { 825 | 826 | case GLFW_KEY_UP: 827 | view_scroll(&view, 0, 1); 828 | 829 | break; 830 | 831 | case GLFW_KEY_DOWN: 832 | view_scroll(&view, 0, -1); 833 | 834 | break; 835 | 836 | case GLFW_KEY_LEFT: 837 | view_scroll(&view, 1, 0); 838 | 839 | break; 840 | 841 | case GLFW_KEY_RIGHT: 842 | view_scroll(&view, -1, 0); 843 | 844 | break; 845 | 846 | case GLFW_KEY_PAGE_UP: 847 | view_scroll(&view, 0, 16); 848 | 849 | break; 850 | 851 | case GLFW_KEY_PAGE_DOWN: 852 | view_scroll(&view, 0, -16); 853 | 854 | break; 855 | 856 | case GLFW_KEY_HOME: 857 | view_scroll(&view, 16, 0); 858 | 859 | break; 860 | 861 | case GLFW_KEY_END: 862 | view_scroll(&view, -16, 0); 863 | 864 | break; 865 | 866 | } 867 | 868 | } 869 | 870 | } 871 | 872 | } 873 | 874 | static void onclick_anchor(struct widget *widget, float x, float y) 875 | { 876 | 877 | struct widget_payload_anchor *payload = &widget->payload.anchor; 878 | struct frame *frame = pool_getframe(widget->index); 879 | 880 | if (!style_box_istouching(&frame->styles[0].box, x, y)) 881 | return; 882 | 883 | if (payload->onclick.type) 884 | loadfunction(payload->onclick.type, payload->onclick.data, payload->target.type); 885 | 886 | } 887 | 888 | static void onclick_button(struct widget *widget, float x, float y) 889 | { 890 | 891 | struct widget_payload_button *payload = &widget->payload.button; 892 | struct frame *frame = pool_getframe(widget->index); 893 | 894 | if (!style_box_istouching(&frame->styles[0].box, x, y)) 895 | return; 896 | 897 | setfocus(widget); 898 | 899 | if (payload->onclick.type) 900 | loadfunction(payload->onclick.type, payload->onclick.data, payload->target.type); 901 | 902 | } 903 | 904 | static void onclick_choice(struct widget *widget, float x, float y) 905 | { 906 | 907 | struct widget *parent = pool_widget_find(widget->header.in.name); 908 | struct widget_payload_choice *payload = &widget->payload.choice; 909 | 910 | switch (parent->header.type) 911 | { 912 | 913 | case WIDGET_TYPE_SELECT: 914 | attribute_data_create(&parent->payload.select.data, payload->label.content); 915 | 916 | break; 917 | 918 | } 919 | 920 | } 921 | 922 | static void onclick_field(struct widget *widget, float x, float y) 923 | { 924 | 925 | struct frame *frame = pool_getframe(widget->index); 926 | 927 | if (!style_box_istouching(&frame->styles[0].box, x, y)) 928 | return; 929 | 930 | setfocus(widget); 931 | 932 | } 933 | 934 | static void onclick_select(struct widget *widget, float x, float y) 935 | { 936 | 937 | struct frame *frame = pool_getframe(widget->index); 938 | 939 | if (!style_box_istouching(&frame->styles[0].box, x, y)) 940 | return; 941 | 942 | setfocus(widget); 943 | 944 | } 945 | 946 | static void onclick_toggle(struct widget *widget, float x, float y) 947 | { 948 | 949 | struct widget_payload_toggle *payload = &widget->payload.toggle; 950 | struct frame *frame = pool_getframe(widget->index); 951 | 952 | if (!style_box_istouching(&frame->styles[0].box, x, y)) 953 | return; 954 | 955 | setfocus(widget); 956 | 957 | switch (payload->mode.type) 958 | { 959 | 960 | case ATTRIBUTE_MODE_OFF: 961 | payload->mode.type = ATTRIBUTE_MODE_ON; 962 | 963 | break; 964 | 965 | case ATTRIBUTE_MODE_ON: 966 | payload->mode.type = ATTRIBUTE_MODE_OFF; 967 | 968 | break; 969 | 970 | } 971 | 972 | } 973 | 974 | static void onclick(struct widget *widget, float x, float y) 975 | { 976 | 977 | switch (widget->header.type) 978 | { 979 | 980 | case WIDGET_TYPE_ANCHOR: 981 | onclick_anchor(widget, x, y); 982 | 983 | break; 984 | 985 | case WIDGET_TYPE_BUTTON: 986 | onclick_button(widget, x, y); 987 | 988 | break; 989 | 990 | case WIDGET_TYPE_CHOICE: 991 | onclick_choice(widget, x, y); 992 | 993 | break; 994 | 995 | case WIDGET_TYPE_FIELD: 996 | onclick_field(widget, x, y); 997 | 998 | break; 999 | 1000 | case WIDGET_TYPE_SELECT: 1001 | onclick_select(widget, x, y); 1002 | 1003 | break; 1004 | 1005 | case WIDGET_TYPE_TOGGLE: 1006 | onclick_toggle(widget, x, y); 1007 | 1008 | break; 1009 | 1010 | } 1011 | 1012 | } 1013 | 1014 | static void onbutton(GLFWwindow *window, int button, int action, int mods) 1015 | { 1016 | 1017 | if (action == GLFW_PRESS) 1018 | { 1019 | 1020 | switch (button) 1021 | { 1022 | 1023 | case GLFW_MOUSE_BUTTON_LEFT: 1024 | setfocus(0); 1025 | 1026 | if (widget_hover) 1027 | onclick(widget_hover, mouse_x, mouse_y); 1028 | 1029 | break; 1030 | 1031 | case GLFW_MOUSE_BUTTON_MIDDLE: 1032 | loadblank("navi://blank", 0, 0); 1033 | 1034 | break; 1035 | 1036 | case GLFW_MOUSE_BUTTON_RIGHT: 1037 | if (mods & GLFW_MOD_SHIFT) 1038 | { 1039 | 1040 | if (widget_hover) 1041 | { 1042 | 1043 | parser_clear(widget_hover); 1044 | 1045 | widget_hover = parser_destroy(widget_hover); 1046 | 1047 | sethover(widget_hover); 1048 | 1049 | } 1050 | 1051 | } 1052 | 1053 | else 1054 | { 1055 | 1056 | struct history *last = history_pop(); 1057 | 1058 | if (last) 1059 | urlblank(last->url, 0, 0); 1060 | 1061 | } 1062 | 1063 | break; 1064 | 1065 | } 1066 | 1067 | } 1068 | 1069 | } 1070 | 1071 | static void oncursor(GLFWwindow *window, double x, double y) 1072 | { 1073 | 1074 | mouse_x = x; 1075 | mouse_y = y; 1076 | 1077 | } 1078 | 1079 | static void onscroll(GLFWwindow *window, double x, double y) 1080 | { 1081 | 1082 | view_scroll(&view, x, y); 1083 | 1084 | } 1085 | 1086 | static void onchar_field(struct widget_payload_field *payload, unsigned int codepoint) 1087 | { 1088 | 1089 | if (payload->data.offset < payload->data.total - 1) 1090 | { 1091 | 1092 | payload->data.content[payload->data.offset] = codepoint; 1093 | payload->data.offset++; 1094 | payload->data.content[payload->data.offset] = '\0'; 1095 | 1096 | } 1097 | 1098 | } 1099 | 1100 | static void onchar(GLFWwindow *window, unsigned int codepoint) 1101 | { 1102 | 1103 | if (widget_focus && checkflag(widget_focus, FLAG_FOCUSABLE)) 1104 | { 1105 | 1106 | switch (widget_focus->header.type) 1107 | { 1108 | 1109 | case WIDGET_TYPE_FIELD: 1110 | onchar_field(&widget_focus->payload.field, codepoint); 1111 | 1112 | break; 1113 | 1114 | } 1115 | 1116 | } 1117 | 1118 | } 1119 | 1120 | static void checkhover(GLFWwindow *window, double x, double y) 1121 | { 1122 | 1123 | struct widget *touch = findtouchingwidget(widget_root, x, y); 1124 | 1125 | sethover(touch); 1126 | 1127 | if (touch) 1128 | setcursor(window, animation_getcursor(touch, x, y)); 1129 | 1130 | } 1131 | 1132 | static void checktitle(GLFWwindow *window) 1133 | { 1134 | 1135 | if (updatetitle) 1136 | { 1137 | 1138 | glfwSetWindowTitle(window, widget_root->payload.window.label.content); 1139 | 1140 | updatetitle = 0; 1141 | 1142 | } 1143 | 1144 | } 1145 | 1146 | static unsigned int checkanimating(void) 1147 | { 1148 | 1149 | struct widget *widget = 0; 1150 | 1151 | while ((widget = pool_widget_next(widget))) 1152 | { 1153 | 1154 | struct frame *frame = pool_getframe(widget->index); 1155 | 1156 | if (frame->animating) 1157 | return 1; 1158 | 1159 | } 1160 | 1161 | return 0; 1162 | 1163 | } 1164 | 1165 | static void render(float u) 1166 | { 1167 | 1168 | struct frame *frame = pool_getframe(widget_root->index); 1169 | 1170 | render_reset(view.pagew, view.pageh); 1171 | render_background(view.pagew, view.pageh, &frame->styles[0].color); 1172 | 1173 | if (widget_root) 1174 | { 1175 | 1176 | struct frame keyframe; 1177 | 1178 | view_adjust(&view, frame->bounds.w, frame->bounds.h); 1179 | animation_initframe(&keyframe, view.scrollx + view.padw, view.scrolly + view.padh, view.unitw * 24, 0); 1180 | animation_step(widget_root, &keyframe, &view, u); 1181 | animation_updateframe(widget_root->header.type, frame, &keyframe, u); 1182 | animation_render(widget_root, &view); 1183 | 1184 | } 1185 | 1186 | render_flush(); 1187 | 1188 | } 1189 | 1190 | static void run(GLFWwindow *window) 1191 | { 1192 | 1193 | double prevt = glfwGetTime(); 1194 | 1195 | while (!glfwWindowShouldClose(window)) 1196 | { 1197 | 1198 | double t = glfwGetTime(); 1199 | double dt = t - prevt; 1200 | unsigned int frames = 0; 1201 | 1202 | if (dt > 0.016) 1203 | { 1204 | 1205 | frames++; 1206 | prevt = t; 1207 | 1208 | } 1209 | 1210 | if (frames) 1211 | { 1212 | 1213 | glfwPollEvents(); 1214 | checkhover(window, mouse_x, mouse_y); 1215 | checktitle(window); 1216 | render(0.5); 1217 | glfwSwapBuffers(window); 1218 | 1219 | if (!checkanimating()) 1220 | glfwWaitEvents(); 1221 | 1222 | } 1223 | 1224 | } 1225 | 1226 | } 1227 | 1228 | int main(int argc, char **argv) 1229 | { 1230 | 1231 | const GLFWvidmode *mode; 1232 | GLFWmonitor *monitor; 1233 | GLFWwindow *window; 1234 | 1235 | glfwInit(); 1236 | glfwSetErrorCallback(onerror); 1237 | 1238 | monitor = glfwGetPrimaryMonitor(); 1239 | mode = glfwGetVideoMode(monitor); 1240 | window = glfwCreateWindow(mode->width, mode->height, "Navi 0.1", 0, 0); 1241 | cursor_arrow = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); 1242 | cursor_ibeam = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); 1243 | cursor_hand = glfwCreateStandardCursor(GLFW_HAND_CURSOR); 1244 | 1245 | glfwSetWindowSizeCallback(window, onwindowsize); 1246 | glfwSetFramebufferSizeCallback(window, onframebuffersize); 1247 | glfwSetKeyCallback(window, onkey); 1248 | glfwSetMouseButtonCallback(window, onbutton); 1249 | glfwSetCursorPosCallback(window, oncursor); 1250 | glfwSetScrollCallback(window, onscroll); 1251 | glfwSetCharCallback(window, onchar); 1252 | glfwMakeContextCurrent(window); 1253 | glfwSwapInterval(1); 1254 | glfwSetTime(0); 1255 | 1256 | #ifdef NVG_GL_GLEW 1257 | glewInit(); 1258 | #endif 1259 | 1260 | view_init(&view, mode->width, mode->height, 0); 1261 | render_create(); 1262 | parser_init(&parser, parser_fail, pool_widget_find, parser_create, parser_destroy, parser_clear); 1263 | pool_setup(); 1264 | animation_setupfonts(); 1265 | animation_settheme(ANIMATION_THEME_LIGHT); 1266 | create("Navi 0.1"); 1267 | render(1.0); 1268 | loadblank("navi://blank", 0, 0); 1269 | run(window); 1270 | render_destroy(); 1271 | glfwTerminate(); 1272 | 1273 | return 0; 1274 | 1275 | } 1276 | 1277 | -------------------------------------------------------------------------------- /src/nvg.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "nvg.h" 6 | 7 | #define NVG_PI 3.14159265358979323846264338327f 8 | #define NVG_KAPPA90 0.5522847493f 9 | #define NVG_COUNTOF(arr) (sizeof (arr) / sizeof (0[arr])) 10 | #define NVG_DISTTOL 0.01f 11 | #define NVG_TESSTOL 0.25f 12 | 13 | enum nvg_commands 14 | { 15 | 16 | NVG_MOVETO = 0, 17 | NVG_LINETO = 1, 18 | NVG_BEZIERTO = 2, 19 | NVG_CLOSE = 3, 20 | NVG_WINDING = 4 21 | 22 | }; 23 | 24 | enum nvg_winding 25 | { 26 | 27 | NVG_CCW = 1, 28 | NVG_CW = 2 29 | 30 | }; 31 | 32 | static float minf(float a, float b) 33 | { 34 | 35 | return a < b ? a : b; 36 | 37 | } 38 | 39 | static float maxf(float a, float b) 40 | { 41 | 42 | return a > b ? a : b; 43 | 44 | } 45 | 46 | static float absf(float a) 47 | { 48 | 49 | return a >= 0.0f ? a : -a; 50 | 51 | } 52 | 53 | static float signf(float a) 54 | { 55 | 56 | return a >= 0.0f ? 1.0f : -1.0f; 57 | 58 | } 59 | 60 | static float normalize(float *x, float *y) 61 | { 62 | 63 | float d = sqrtf((*x) * (*x) + (*y) * (*y)); 64 | 65 | if (d > 1e-6f) 66 | { 67 | 68 | float id = 1.0f / d; 69 | 70 | *x *= id; 71 | *y *= id; 72 | 73 | } 74 | 75 | return d; 76 | 77 | } 78 | 79 | struct nvg_color nvg_rgba(unsigned char r, unsigned char g, unsigned char b, unsigned char a) 80 | { 81 | 82 | struct nvg_color color; 83 | 84 | color.r = r / 255.0f; 85 | color.g = g / 255.0f; 86 | color.b = b / 255.0f; 87 | color.a = a / 255.0f; 88 | 89 | return color; 90 | 91 | } 92 | 93 | struct nvg_color nvg_rgbaf(float r, float g, float b, float a) 94 | { 95 | 96 | struct nvg_color color; 97 | 98 | color.r = r; 99 | color.g = g; 100 | color.b = b; 101 | color.a = a; 102 | 103 | return color; 104 | 105 | } 106 | 107 | struct nvg_color nvg_premulrgba(struct nvg_color c) 108 | { 109 | 110 | c.r *= c.a; 111 | c.g *= c.a; 112 | c.b *= c.a; 113 | 114 | return c; 115 | 116 | } 117 | 118 | void nvg_paint_color(struct nvg_paint *paint, float r, float g, float b, float a) 119 | { 120 | 121 | memset(paint, 0, sizeof (struct nvg_paint)); 122 | nvg_xform_identity(paint->xform); 123 | 124 | paint->radius = 0.0f; 125 | paint->feather = 1.0f; 126 | paint->innerColor = nvg_rgba(r, g, b, a); 127 | paint->outerColor = nvg_rgba(r, g, b, a); 128 | 129 | } 130 | 131 | void nvg_paint_image(struct nvg_paint *paint, float cx, float cy, float w, float h, float angle, int image, float alpha) 132 | { 133 | 134 | memset(paint, 0, sizeof (struct nvg_paint)); 135 | nvg_xform_rotate(paint->xform, angle); 136 | 137 | paint->xform[4] = cx; 138 | paint->xform[5] = cy; 139 | paint->extent[0] = w; 140 | paint->extent[1] = h; 141 | paint->image = image; 142 | paint->innerColor = nvg_rgbaf(1, 1, 1, alpha); 143 | paint->outerColor = nvg_rgbaf(1, 1, 1, alpha); 144 | 145 | } 146 | 147 | void nvg_paint_boxgradient(struct nvg_paint *paint, float x, float y, float w, float h, float r, float f, struct nvg_color icol, struct nvg_color ocol) 148 | { 149 | 150 | memset(paint, 0, sizeof (struct nvg_paint)); 151 | nvg_xform_identity(paint->xform); 152 | 153 | paint->xform[4] = x + w * 0.5f; 154 | paint->xform[5] = y + h * 0.5f; 155 | paint->extent[0] = w * 0.5f; 156 | paint->extent[1] = h * 0.5f; 157 | paint->radius = r; 158 | paint->feather = maxf(1.0f, f); 159 | paint->innerColor = icol; 160 | paint->outerColor = ocol; 161 | 162 | } 163 | 164 | void nvg_paint_lineargradient(struct nvg_paint *paint, float sx, float sy, float ex, float ey, struct nvg_color icol, struct nvg_color ocol) 165 | { 166 | 167 | float dx = ex - sx; 168 | float dy = ey - sy; 169 | float d = sqrtf(dx * dx + dy * dy); 170 | const float large = 1e5; 171 | 172 | dx /= d; 173 | dy /= d; 174 | 175 | memset(paint, 0, sizeof (struct nvg_paint)); 176 | 177 | paint->xform[0] = dy; 178 | paint->xform[1] = -dx; 179 | paint->xform[2] = dx; 180 | paint->xform[3] = dy; 181 | paint->xform[4] = sx - dx * large; 182 | paint->xform[5] = sy - dy * large; 183 | paint->extent[0] = large; 184 | paint->extent[1] = large + d * 0.5f; 185 | paint->radius = 0.0f; 186 | paint->feather = maxf(1.0f, d); 187 | paint->innerColor = icol; 188 | paint->outerColor = ocol; 189 | 190 | } 191 | 192 | void nvg_paint_radialgradient(struct nvg_paint *paint, float cx, float cy, float inr, float outr, struct nvg_color icol, struct nvg_color ocol) 193 | { 194 | 195 | float r = (inr + outr) * 0.5f; 196 | float f = (outr - inr); 197 | 198 | memset(paint, 0, sizeof (struct nvg_paint)); 199 | nvg_xform_identity(paint->xform); 200 | 201 | paint->xform[4] = cx; 202 | paint->xform[5] = cy; 203 | paint->extent[0] = r; 204 | paint->extent[1] = r; 205 | paint->radius = r; 206 | paint->feather = maxf(1.0f, f); 207 | paint->innerColor = icol; 208 | paint->outerColor = ocol; 209 | 210 | } 211 | 212 | void nvg_setvertex(struct nvg_vertex *vtx, float x, float y, float u, float v) 213 | { 214 | 215 | vtx->x = x; 216 | vtx->y = y; 217 | vtx->u = u; 218 | vtx->v = v; 219 | 220 | } 221 | 222 | void nvg_xform_identity(float *t) 223 | { 224 | 225 | t[0] = 1.0f; 226 | t[1] = 0.0f; 227 | t[2] = 0.0f; 228 | t[3] = 1.0f; 229 | t[4] = 0.0f; 230 | t[5] = 0.0f; 231 | 232 | } 233 | 234 | void nvg_xform_translate(float *t, float tx, float ty) 235 | { 236 | 237 | t[0] = 1.0f; 238 | t[1] = 0.0f; 239 | t[2] = 0.0f; 240 | t[3] = 1.0f; 241 | t[4] = tx; 242 | t[5] = ty; 243 | 244 | } 245 | 246 | void nvg_xform_scale(float *t, float sx, float sy) 247 | { 248 | 249 | t[0] = sx; 250 | t[1] = 0.0f; 251 | t[2] = 0.0f; 252 | t[3] = sy; 253 | t[4] = 0.0f; 254 | t[5] = 0.0f; 255 | 256 | } 257 | 258 | void nvg_xform_rotate(float *t, float a) 259 | { 260 | 261 | float cs = cosf(a); 262 | float sn = sinf(a); 263 | 264 | t[0] = cs; 265 | t[1] = sn; 266 | t[2] = -sn; 267 | t[3] = cs; 268 | t[4] = 0.0f; 269 | t[5] = 0.0f; 270 | 271 | } 272 | 273 | void nvg_xform_skewx(float *t, float a) 274 | { 275 | 276 | t[0] = 1.0f; 277 | t[1] = 0.0f; 278 | t[2] = tanf(a); 279 | t[3] = 1.0f; 280 | t[4] = 0.0f; 281 | t[5] = 0.0f; 282 | 283 | } 284 | 285 | void nvg_xform_skewy(float *t, float a) 286 | { 287 | 288 | t[0] = 1.0f; 289 | t[1] = tanf(a); 290 | t[2] = 0.0f; 291 | t[3] = 1.0f; 292 | t[4] = 0.0f; 293 | t[5] = 0.0f; 294 | 295 | } 296 | 297 | void nvg_xform_multiply(float *t, const float *s) 298 | { 299 | 300 | float t0 = t[0] * s[0] + t[1] * s[2]; 301 | float t2 = t[2] * s[0] + t[3] * s[2]; 302 | float t4 = t[4] * s[0] + t[5] * s[2] + s[4]; 303 | 304 | t[1] = t[0] * s[1] + t[1] * s[3]; 305 | t[3] = t[2] * s[1] + t[3] * s[3]; 306 | t[5] = t[4] * s[1] + t[5] * s[3] + s[5]; 307 | t[0] = t0; 308 | t[2] = t2; 309 | t[4] = t4; 310 | 311 | } 312 | 313 | void nvg_xform_premultiply(float *t, const float *s) 314 | { 315 | 316 | float s2[6]; 317 | 318 | memcpy(s2, s, sizeof (float) * 6); 319 | nvg_xform_multiply(s2, t); 320 | memcpy(t, s2, sizeof (float) * 6); 321 | 322 | } 323 | 324 | void nvg_xform_inverse(float *t, const float *s) 325 | { 326 | 327 | double det = (double)s[0] * s[3] - (double)s[2] * s[1]; 328 | double invdet = 1.0 / det; 329 | 330 | if (det > -1e-6 && det < 1e-6) 331 | { 332 | 333 | nvg_xform_identity(t); 334 | 335 | return; 336 | 337 | } 338 | 339 | t[0] = (float)(s[3] * invdet); 340 | t[2] = (float)(-s[2] * invdet); 341 | t[4] = (float)(((double)s[2] * s[5] - (double)s[3] * s[4]) * invdet); 342 | t[1] = (float)(-s[1] * invdet); 343 | t[3] = (float)(s[0] * invdet); 344 | t[5] = (float)(((double)s[1] * s[4] - (double)s[0] * s[5]) * invdet); 345 | 346 | } 347 | 348 | void nvg_getpoints(float *dx, float *dy, const float *t, float sx, float sy) 349 | { 350 | 351 | *dx = sx * t[0] + sy * t[2] + t[4]; 352 | *dy = sx * t[1] + sy * t[3] + t[5]; 353 | 354 | } 355 | 356 | void nvg_transform(struct nvg_context *ctx, float a, float b, float c, float d, float e, float f) 357 | { 358 | 359 | float t[6] = { a, b, c, d, e, f }; 360 | 361 | nvg_xform_premultiply(ctx->xform, t); 362 | 363 | } 364 | 365 | void nvg_translate(struct nvg_context *ctx, float x, float y) 366 | { 367 | 368 | float t[6]; 369 | 370 | nvg_xform_translate(t, x, y); 371 | nvg_xform_premultiply(ctx->xform, t); 372 | 373 | } 374 | 375 | void nvg_rotate(struct nvg_context *ctx, float angle) 376 | { 377 | 378 | float t[6]; 379 | 380 | nvg_xform_rotate(t, angle); 381 | nvg_xform_premultiply(ctx->xform, t); 382 | 383 | } 384 | 385 | void nvg_skewx(struct nvg_context *ctx, float angle) 386 | { 387 | 388 | float t[6]; 389 | 390 | nvg_xform_skewx(t, angle); 391 | nvg_xform_premultiply(ctx->xform, t); 392 | 393 | } 394 | 395 | void nvg_skewy(struct nvg_context *ctx, float angle) 396 | { 397 | 398 | float t[6]; 399 | 400 | nvg_xform_skewy(t, angle); 401 | nvg_xform_premultiply(ctx->xform, t); 402 | 403 | } 404 | 405 | void nvg_scale(struct nvg_context *ctx, float x, float y) 406 | { 407 | 408 | float t[6]; 409 | 410 | nvg_xform_scale(t, x, y); 411 | nvg_xform_premultiply(ctx->xform, t); 412 | 413 | } 414 | 415 | void nvg_scissor_init(struct nvg_scissor *scissor) 416 | { 417 | 418 | memset(scissor->xform, 0, sizeof (scissor->xform)); 419 | 420 | scissor->extent[0] = -1.0f; 421 | scissor->extent[1] = -1.0f; 422 | 423 | } 424 | 425 | void nvg_scissor_set(struct nvg_scissor *scissor, float *xform, float x, float y, float w, float h) 426 | { 427 | 428 | w = maxf(0.0f, w); 429 | h = maxf(0.0f, h); 430 | 431 | nvg_xform_identity(scissor->xform); 432 | 433 | scissor->xform[4] = x + w * 0.5f; 434 | scissor->xform[5] = y + h * 0.5f; 435 | 436 | nvg_xform_multiply(scissor->xform, xform); 437 | 438 | scissor->extent[0] = w * 0.5f; 439 | scissor->extent[1] = h * 0.5f; 440 | 441 | } 442 | 443 | static int pointequals(float x1, float y1, float x2, float y2, float tol) 444 | { 445 | 446 | float dx = x2 - x1; 447 | float dy = y2 - y1; 448 | 449 | return dx * dx + dy * dy < tol * tol; 450 | 451 | } 452 | 453 | static void appendcommand(struct nvg_context *ctx, float *vals, int nvals) 454 | { 455 | 456 | unsigned int i; 457 | 458 | for (i = 0; i < nvals;) 459 | { 460 | 461 | int cmd = (int)vals[i]; 462 | 463 | switch (cmd) 464 | { 465 | 466 | case NVG_MOVETO: 467 | nvg_getpoints(&vals[i + 1], &vals[i + 2], ctx->xform, vals[i + 1], vals[i + 2]); 468 | 469 | i += 3; 470 | 471 | break; 472 | 473 | case NVG_LINETO: 474 | nvg_getpoints(&vals[i + 1], &vals[i + 2], ctx->xform, vals[i + 1],vals[i + 2]); 475 | 476 | i += 3; 477 | 478 | break; 479 | 480 | case NVG_BEZIERTO: 481 | nvg_getpoints(&vals[i + 1],&vals[i + 2], ctx->xform, vals[i + 1], vals[i + 2]); 482 | nvg_getpoints(&vals[i + 3],&vals[i + 4], ctx->xform, vals[i + 3], vals[i + 4]); 483 | nvg_getpoints(&vals[i + 5],&vals[i + 6], ctx->xform, vals[i + 5], vals[i + 6]); 484 | 485 | i += 7; 486 | 487 | break; 488 | 489 | case NVG_CLOSE: 490 | i++; 491 | 492 | break; 493 | 494 | case NVG_WINDING: 495 | i += 2; 496 | 497 | break; 498 | 499 | default: 500 | i++; 501 | 502 | } 503 | 504 | } 505 | 506 | memcpy(&ctx->commands[ctx->ncommands], vals, nvals * sizeof (float)); 507 | 508 | ctx->ncommands += nvals; 509 | 510 | } 511 | 512 | static struct nvg_path *lastpath(struct nvg_context *ctx) 513 | { 514 | 515 | if (ctx->npaths) 516 | return &ctx->paths[ctx->npaths - 1]; 517 | 518 | return 0; 519 | 520 | } 521 | 522 | static void addpath(struct nvg_context *ctx) 523 | { 524 | 525 | struct nvg_path *path = &ctx->paths[ctx->npaths]; 526 | 527 | memset(path, 0, sizeof (struct nvg_path)); 528 | 529 | path->first = ctx->npoints; 530 | path->winding = NVG_CCW; 531 | ctx->npaths++; 532 | 533 | } 534 | 535 | static struct nvg_point *lastpoint(struct nvg_context *ctx) 536 | { 537 | 538 | if (ctx->npoints) 539 | return &ctx->points[ctx->npoints - 1]; 540 | 541 | return 0; 542 | 543 | } 544 | 545 | static void addpoint(struct nvg_context *ctx, float x, float y) 546 | { 547 | 548 | struct nvg_path *path = lastpath(ctx); 549 | struct nvg_point *pt; 550 | 551 | if (!path) 552 | return; 553 | 554 | if (path->count > 0 && ctx->npoints > 0) 555 | { 556 | 557 | pt = lastpoint(ctx); 558 | 559 | if (pointequals(pt->x, pt->y, x, y, NVG_DISTTOL)) 560 | return; 561 | 562 | } 563 | 564 | pt = &ctx->points[ctx->npoints]; 565 | 566 | memset(pt, 0, sizeof (struct nvg_point)); 567 | 568 | pt->x = x; 569 | pt->y = y; 570 | ctx->npoints++; 571 | path->count++; 572 | 573 | } 574 | 575 | static void closepath(struct nvg_context *ctx) 576 | { 577 | 578 | struct nvg_path *path = lastpath(ctx); 579 | 580 | if (!path) 581 | return; 582 | 583 | path->closed = 1; 584 | 585 | } 586 | 587 | static void pathwinding(struct nvg_context *ctx, int winding) 588 | { 589 | 590 | struct nvg_path *path = lastpath(ctx); 591 | 592 | if (!path) 593 | return; 594 | 595 | path->winding = winding; 596 | 597 | } 598 | 599 | static float triarea2(float ax, float ay, float bx, float by, float cx, float cy) 600 | { 601 | 602 | float abx = bx - ax; 603 | float aby = by - ay; 604 | float acx = cx - ax; 605 | float acy = cy - ay; 606 | 607 | return acx * aby - abx * acy; 608 | 609 | } 610 | 611 | static float polyarea(struct nvg_point *pts, int npts) 612 | { 613 | 614 | float area = 0; 615 | unsigned int i; 616 | 617 | for (i = 2; i < npts; i++) 618 | { 619 | 620 | struct nvg_point *a = &pts[0]; 621 | struct nvg_point *b = &pts[i - 1]; 622 | struct nvg_point *c = &pts[i]; 623 | 624 | area += triarea2(a->x, a->y, b->x, b->y, c->x, c->y); 625 | 626 | } 627 | 628 | return area * 0.5f; 629 | 630 | } 631 | 632 | static void polyreverse(struct nvg_point *pts, int npts) 633 | { 634 | 635 | struct nvg_point tmp; 636 | unsigned int j = npts - 1; 637 | unsigned int i; 638 | 639 | for (i = 0; i < j; i++) 640 | { 641 | 642 | tmp = pts[i]; 643 | pts[i] = pts[j]; 644 | pts[j] = tmp; 645 | j--; 646 | 647 | } 648 | 649 | } 650 | 651 | static void tesselatebezier(struct nvg_context *ctx, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, int level) 652 | { 653 | 654 | float x12, y12, x23, y23, x34, y34, x123, y123, x234, y234, x1234, y1234; 655 | float dx, dy, d2, d3; 656 | 657 | if (level > 10) 658 | return; 659 | 660 | x12 = (x1 + x2) * 0.5f; 661 | y12 = (y1 + y2) * 0.5f; 662 | x23 = (x2 + x3) * 0.5f; 663 | y23 = (y2 + y3) * 0.5f; 664 | x34 = (x3 + x4) * 0.5f; 665 | y34 = (y3 + y4) * 0.5f; 666 | x123 = (x12 + x23) * 0.5f; 667 | y123 = (y12 + y23) * 0.5f; 668 | dx = x4 - x1; 669 | dy = y4 - y1; 670 | d2 = absf(((x2 - x4) * dy - (y2 - y4) * dx)); 671 | d3 = absf(((x3 - x4) * dy - (y3 - y4) * dx)); 672 | 673 | if ((d2 + d3) * (d2 + d3) < NVG_TESSTOL * (dx * dx + dy * dy)) 674 | { 675 | 676 | addpoint(ctx, x4, y4); 677 | 678 | return; 679 | 680 | } 681 | 682 | x234 = (x23 + x34) * 0.5f; 683 | y234 = (y23 + y34) * 0.5f; 684 | x1234 = (x123 + x234) * 0.5f; 685 | y1234 = (y123 + y234) * 0.5f; 686 | 687 | tesselatebezier(ctx, x1, y1, x12, y12, x123, y123, x1234, y1234, level + 1); 688 | tesselatebezier(ctx, x1234, y1234, x234, y234, x34, y34, x4, y4, level + 1); 689 | 690 | } 691 | 692 | void nvg_flatten(struct nvg_context *ctx) 693 | { 694 | 695 | unsigned int i; 696 | 697 | if (ctx->npaths > 0) 698 | return; 699 | 700 | for (i = 0; i < ctx->ncommands;) 701 | { 702 | 703 | int cmd = (int)ctx->commands[i]; 704 | struct nvg_point *last; 705 | float *p; 706 | 707 | switch (cmd) 708 | { 709 | 710 | case NVG_MOVETO: 711 | addpath(ctx); 712 | 713 | p = &ctx->commands[i + 1]; 714 | 715 | addpoint(ctx, p[0], p[1]); 716 | 717 | i += 3; 718 | 719 | break; 720 | 721 | case NVG_LINETO: 722 | p = &ctx->commands[i + 1]; 723 | 724 | addpoint(ctx, p[0], p[1]); 725 | 726 | i += 3; 727 | 728 | break; 729 | 730 | case NVG_BEZIERTO: 731 | last = lastpoint(ctx); 732 | 733 | if (last) 734 | { 735 | 736 | float *cp1 = &ctx->commands[i + 1]; 737 | float *cp2 = &ctx->commands[i + 3]; 738 | 739 | p = &ctx->commands[i + 5]; 740 | 741 | tesselatebezier(ctx, last->x, last->y, cp1[0], cp1[1], cp2[0], cp2[1], p[0], p[1], 0); 742 | 743 | } 744 | 745 | i += 7; 746 | 747 | break; 748 | 749 | case NVG_CLOSE: 750 | closepath(ctx); 751 | 752 | i++; 753 | 754 | break; 755 | 756 | case NVG_WINDING: 757 | pathwinding(ctx, (int)ctx->commands[i + 1]); 758 | 759 | i += 2; 760 | 761 | break; 762 | 763 | default: 764 | i++; 765 | 766 | } 767 | 768 | } 769 | 770 | ctx->bounds[0] = ctx->bounds[1] = 1e6f; 771 | ctx->bounds[2] = ctx->bounds[3] = -1e6f; 772 | 773 | for (i = 0; i < ctx->npaths; i++) 774 | { 775 | 776 | struct nvg_path *path = &ctx->paths[i]; 777 | struct nvg_point *pts = &ctx->points[path->first]; 778 | struct nvg_point *p0 = &pts[path->count - 1]; 779 | struct nvg_point *p1 = &pts[0]; 780 | unsigned int j; 781 | 782 | if (pointequals(p0->x, p0->y, p1->x, p1->y, NVG_DISTTOL)) 783 | { 784 | 785 | path->count--; 786 | p0 = &pts[path->count - 1]; 787 | path->closed = 1; 788 | 789 | } 790 | 791 | if (path->count > 2) 792 | { 793 | 794 | float area = polyarea(pts, path->count); 795 | 796 | if (path->winding == NVG_CCW && area < 0.0f) 797 | polyreverse(pts, path->count); 798 | 799 | if (path->winding == NVG_CW && area > 0.0f) 800 | polyreverse(pts, path->count); 801 | 802 | } 803 | 804 | for (j = 0; j < path->count; j++) 805 | { 806 | 807 | p0->dx = p1->x - p0->x; 808 | p0->dy = p1->y - p0->y; 809 | p0->len = normalize(&p0->dx, &p0->dy); 810 | ctx->bounds[0] = minf(ctx->bounds[0], p0->x); 811 | ctx->bounds[1] = minf(ctx->bounds[1], p0->y); 812 | ctx->bounds[2] = maxf(ctx->bounds[2], p0->x); 813 | ctx->bounds[3] = maxf(ctx->bounds[3], p0->y); 814 | p0 = p1++; 815 | 816 | } 817 | 818 | } 819 | 820 | } 821 | 822 | void nvg_expand(struct nvg_context *ctx) 823 | { 824 | 825 | struct nvg_vertex *verts = ctx->verts; 826 | unsigned int i; 827 | 828 | for (i = 0; i < ctx->npaths; i++) 829 | { 830 | 831 | struct nvg_path *path = &ctx->paths[i]; 832 | struct nvg_point *pts = &ctx->points[path->first]; 833 | struct nvg_vertex *dst = verts; 834 | unsigned int j; 835 | 836 | path->fill = dst; 837 | 838 | for (j = 0; j < path->count; j++) 839 | nvg_setvertex(dst++, pts[j].x, pts[j].y, 0.5f, 1); 840 | 841 | path->nfill = (int)(dst - verts); 842 | verts = dst; 843 | 844 | } 845 | 846 | } 847 | 848 | void nvg_path_moveto(struct nvg_context *ctx, float x, float y) 849 | { 850 | 851 | float vals[] = { NVG_MOVETO, x, y }; 852 | 853 | appendcommand(ctx, vals, NVG_COUNTOF(vals)); 854 | 855 | } 856 | 857 | void nvg_path_lineto(struct nvg_context *ctx, float x, float y) 858 | { 859 | 860 | float vals[] = { NVG_LINETO, x, y }; 861 | 862 | appendcommand(ctx, vals, NVG_COUNTOF(vals)); 863 | 864 | } 865 | 866 | void nvg_path_bezierto(struct nvg_context *ctx, float c1x, float c1y, float c2x, float c2y, float x, float y) 867 | { 868 | 869 | float vals[] = { NVG_BEZIERTO, c1x, c1y, c2x, c2y, x, y }; 870 | 871 | appendcommand(ctx, vals, NVG_COUNTOF(vals)); 872 | 873 | } 874 | 875 | void nvg_path_begin(struct nvg_context *ctx) 876 | { 877 | 878 | ctx->ncommands = 0; 879 | ctx->npoints = 0; 880 | ctx->npaths = 0; 881 | 882 | } 883 | 884 | void nvg_path_close(struct nvg_context *ctx) 885 | { 886 | 887 | float vals[] = { NVG_CLOSE }; 888 | 889 | appendcommand(ctx, vals, NVG_COUNTOF(vals)); 890 | 891 | } 892 | 893 | void nvg_path_solid(struct nvg_context *ctx) 894 | { 895 | 896 | float vals[] = { NVG_WINDING, (float)NVG_CCW }; 897 | 898 | appendcommand(ctx, vals, NVG_COUNTOF(vals)); 899 | 900 | } 901 | 902 | void nvg_path_hole(struct nvg_context *ctx) 903 | { 904 | 905 | float vals[] = { NVG_WINDING, (float)NVG_CW }; 906 | 907 | appendcommand(ctx, vals, NVG_COUNTOF(vals)); 908 | 909 | } 910 | 911 | void nvg_path_rect(struct nvg_context *ctx, float x, float y, float w, float h) 912 | { 913 | 914 | float vals[] = { 915 | NVG_MOVETO, x, y, 916 | NVG_LINETO, x, y + h, 917 | NVG_LINETO, x + w, y + h, 918 | NVG_LINETO, x + w, y, 919 | NVG_CLOSE 920 | }; 921 | 922 | appendcommand(ctx, vals, NVG_COUNTOF(vals)); 923 | 924 | } 925 | 926 | void nvg_path_roundedrect(struct nvg_context *ctx, float x, float y, float w, float h, float r) 927 | { 928 | 929 | nvg_path_roundedrectvarying(ctx, x, y, w, h, r, r, r, r); 930 | 931 | } 932 | 933 | void nvg_path_roundedrectvarying(struct nvg_context *ctx, float x, float y, float w, float h, float radTopLeft, float radTopRight, float radBottomRight, float radBottomLeft) 934 | { 935 | 936 | float halfw = absf(w) * 0.5f; 937 | float halfh = absf(h) * 0.5f; 938 | float rxBL = minf(radBottomLeft, halfw) * signf(w), ryBL = minf(radBottomLeft, halfh) * signf(h); 939 | float rxBR = minf(radBottomRight, halfw) * signf(w), ryBR = minf(radBottomRight, halfh) * signf(h); 940 | float rxTR = minf(radTopRight, halfw) * signf(w), ryTR = minf(radTopRight, halfh) * signf(h); 941 | float rxTL = minf(radTopLeft, halfw) * signf(w), ryTL = minf(radTopLeft, halfh) * signf(h); 942 | float vals[] = { 943 | NVG_MOVETO, x, y + ryTL, 944 | NVG_LINETO, x, y + h - ryBL, 945 | NVG_BEZIERTO, x, y + h - ryBL * (1 - NVG_KAPPA90), x + rxBL * (1 - NVG_KAPPA90), y + h, x + rxBL, y + h, 946 | NVG_LINETO, x + w - rxBR, y + h, 947 | NVG_BEZIERTO, x + w - rxBR * (1 - NVG_KAPPA90), y + h, x + w, y + h - ryBR * (1 - NVG_KAPPA90), x + w, y + h - ryBR, 948 | NVG_LINETO, x + w, y + ryTR, 949 | NVG_BEZIERTO, x + w, y + ryTR * (1 - NVG_KAPPA90), x + w - rxTR * (1 - NVG_KAPPA90), y, x + w - rxTR, y, 950 | NVG_LINETO, x + rxTL, y, 951 | NVG_BEZIERTO, x + rxTL * (1 - NVG_KAPPA90), y, x, y + ryTL * (1 - NVG_KAPPA90), x, y + ryTL, 952 | NVG_CLOSE 953 | }; 954 | 955 | appendcommand(ctx, vals, NVG_COUNTOF(vals)); 956 | 957 | } 958 | 959 | void nvg_path_ellipse(struct nvg_context *ctx, float cx, float cy, float rx, float ry) 960 | { 961 | 962 | float vals[] = { 963 | NVG_MOVETO, cx - rx, cy, 964 | NVG_BEZIERTO, cx - rx, cy + ry * NVG_KAPPA90, cx - rx * NVG_KAPPA90, cy + ry, cx, cy + ry, 965 | NVG_BEZIERTO, cx + rx * NVG_KAPPA90, cy + ry, cx + rx, cy + ry * NVG_KAPPA90, cx + rx, cy, 966 | NVG_BEZIERTO, cx + rx, cy - ry * NVG_KAPPA90, cx + rx * NVG_KAPPA90, cy - ry, cx, cy - ry, 967 | NVG_BEZIERTO, cx - rx * NVG_KAPPA90, cy - ry, cx - rx, cy - ry * NVG_KAPPA90, cx - rx, cy, 968 | NVG_CLOSE 969 | }; 970 | 971 | appendcommand(ctx, vals, NVG_COUNTOF(vals)); 972 | 973 | } 974 | 975 | void nvg_path_circle(struct nvg_context *ctx, float cx, float cy, float r) 976 | { 977 | 978 | nvg_path_ellipse(ctx, cx, cy, r, r); 979 | 980 | } 981 | 982 | void nvg_reset(struct nvg_context *ctx) 983 | { 984 | 985 | nvg_xform_identity(ctx->xform); 986 | 987 | } 988 | 989 | -------------------------------------------------------------------------------- /src/nvg.h: -------------------------------------------------------------------------------- 1 | #define NVG_COMMANDSSIZE 256 2 | #define NVG_POINTSSIZE 128 3 | #define NVG_PATHSSIZE 16 4 | #define NVG_VERTSSIZE 2048 5 | 6 | struct nvg_color 7 | { 8 | 9 | float r; 10 | float g; 11 | float b; 12 | float a; 13 | 14 | }; 15 | 16 | struct nvg_paint 17 | { 18 | 19 | float xform[6]; 20 | float extent[2]; 21 | float radius; 22 | float feather; 23 | struct nvg_color innerColor; 24 | struct nvg_color outerColor; 25 | int image; 26 | 27 | }; 28 | 29 | struct nvg_scissor 30 | { 31 | 32 | float xform[6]; 33 | float extent[2]; 34 | 35 | }; 36 | 37 | struct nvg_point 38 | { 39 | 40 | float x; 41 | float y; 42 | float dx; 43 | float dy; 44 | float len; 45 | float dmx; 46 | float dmy; 47 | 48 | }; 49 | 50 | struct nvg_path 51 | { 52 | 53 | int first; 54 | unsigned int count; 55 | unsigned char closed; 56 | struct nvg_vertex *fill; 57 | int nfill; 58 | int winding; 59 | int convex; 60 | 61 | }; 62 | 63 | struct nvg_vertex 64 | { 65 | 66 | float x; 67 | float y; 68 | float u; 69 | float v; 70 | 71 | }; 72 | 73 | struct nvg_context 74 | { 75 | 76 | struct nvg_point points[NVG_POINTSSIZE]; 77 | unsigned int npoints; 78 | struct nvg_path paths[NVG_PATHSSIZE]; 79 | unsigned int npaths; 80 | struct nvg_vertex verts[NVG_VERTSSIZE]; 81 | unsigned int nverts; 82 | float commands[NVG_COMMANDSSIZE]; 83 | unsigned int ncommands; 84 | float bounds[4]; 85 | float xform[6]; 86 | 87 | }; 88 | 89 | struct nvg_color nvg_rgba(unsigned char r, unsigned char g, unsigned char b, unsigned char a); 90 | struct nvg_color nvg_rgbaf(float r, float g, float b, float a); 91 | struct nvg_color nvg_premulrgba(struct nvg_color c); 92 | void nvg_paint_color(struct nvg_paint *paint, float r, float g, float b, float a); 93 | void nvg_paint_image(struct nvg_paint *paint, float ox, float oy, float ex, float ey, float angle, int image, float alpha); 94 | void nvg_paint_boxgradient(struct nvg_paint *paint, float x, float y, float w, float h, float r, float f, struct nvg_color icol, struct nvg_color ocol); 95 | void nvg_paint_lineargradient(struct nvg_paint *paint, float sx, float sy, float ex, float ey, struct nvg_color icol, struct nvg_color ocol); 96 | void nvg_paint_radialgradient(struct nvg_paint *paint, float cx, float cy, float inr, float outr, struct nvg_color icol, struct nvg_color ocol); 97 | void nvg_setvertex(struct nvg_vertex *vtx, float x, float y, float u, float v); 98 | void nvg_xform_identity(float *t); 99 | void nvg_xform_translate(float *t, float tx, float ty); 100 | void nvg_xform_scale(float *t, float sx, float sy); 101 | void nvg_xform_rotate(float *t, float a); 102 | void nvg_xform_skewx(float *t, float a); 103 | void nvg_xform_skewy(float *t, float a); 104 | void nvg_xform_multiply(float *t, const float *s); 105 | void nvg_xform_premultiply(float *t, const float *s); 106 | void nvg_xform_inverse(float *t, const float *s); 107 | void nvg_getpoints(float *dstx, float *dsty, const float *xform, float srcx, float srcy); 108 | void nvg_transform(struct nvg_context *ctx, float a, float b, float c, float d, float e, float f); 109 | void nvg_translate(struct nvg_context *ctx, float x, float y); 110 | void nvg_rotate(struct nvg_context *ctx, float angle); 111 | void nvg_skewx(struct nvg_context *ctx, float angle); 112 | void nvg_skewy(struct nvg_context *ctx, float angle); 113 | void nvg_scale(struct nvg_context *ctx, float x, float y); 114 | void nvg_scissor_init(struct nvg_scissor *scissor); 115 | void nvg_scissor_set(struct nvg_scissor *scissor, float *xform, float x, float y, float w, float h); 116 | void nvg_path_moveto(struct nvg_context *ctx, float x, float y); 117 | void nvg_path_lineto(struct nvg_context *ctx, float x, float y); 118 | void nvg_path_bezierto(struct nvg_context *ctx, float c1x, float c1y, float c2x, float c2y, float x, float y); 119 | void nvg_path_begin(struct nvg_context *ctx); 120 | void nvg_path_close(struct nvg_context *ctx); 121 | void nvg_path_solid(struct nvg_context *ctx); 122 | void nvg_path_hole(struct nvg_context *ctx); 123 | void nvg_path_rect(struct nvg_context *ctx, float x, float y, float w, float h); 124 | void nvg_path_roundedrect(struct nvg_context *ctx, float x, float y, float w, float h, float r); 125 | void nvg_path_roundedrectvarying(struct nvg_context *ctx, float x, float y, float w, float h, float radTopLeft, float radTopRight, float radBottomRight, float radBottomLeft); 126 | void nvg_path_ellipse(struct nvg_context *ctx, float cx, float cy, float rx, float ry); 127 | void nvg_path_circle(struct nvg_context *ctx, float cx, float cy, float r); 128 | void nvg_flatten(struct nvg_context *ctx); 129 | void nvg_expand(struct nvg_context *ctx); 130 | void nvg_reset(struct nvg_context *ctx); 131 | -------------------------------------------------------------------------------- /src/nvg_gl.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #if defined NVG_GL_GLEW 7 | #include 8 | #endif 9 | 10 | #if defined NVG_GL_VERSION_GLES2 11 | #include 12 | #endif 13 | 14 | #if defined NVG_GL_VERSION_GLES3 15 | #include 16 | #endif 17 | 18 | #include "nvg.h" 19 | #include "nvg_gl.h" 20 | 21 | #define NVG_GL_UNIFORMARRAYSIZE 11 22 | 23 | enum nvg_gl_uniformloc 24 | { 25 | 26 | NVG_GL_LOC_VIEWSIZE, 27 | NVG_GL_LOC_TEX, 28 | NVG_GL_LOC_FRAG 29 | 30 | }; 31 | 32 | enum nvg_gl_shadertype 33 | { 34 | 35 | NSVG_SHADER_FILLGRAD, 36 | NSVG_SHADER_FILLIMG, 37 | NSVG_SHADER_SIMPLE, 38 | NSVG_SHADER_IMG 39 | 40 | }; 41 | 42 | enum nvg_gl_calltype 43 | { 44 | 45 | NVG_GL_NONE = 0, 46 | NVG_GL_FILL, 47 | NVG_GL_CONVEXFILL, 48 | NVG_GL_TRIANGLES, 49 | 50 | }; 51 | 52 | struct nvg_gl_fraguniforms 53 | { 54 | 55 | union 56 | { 57 | 58 | struct 59 | { 60 | 61 | float scissorMat[12]; 62 | float paintMat[12]; 63 | struct nvg_color innerCol; 64 | struct nvg_color outerCol; 65 | float scissorExt[2]; 66 | float scissorScale[2]; 67 | float extent[2]; 68 | float radius; 69 | float feather; 70 | float strokeMult; 71 | float strokeThr; 72 | float texType; 73 | float type; 74 | 75 | }; 76 | 77 | float uniformArray[NVG_GL_UNIFORMARRAYSIZE][4]; 78 | 79 | }; 80 | 81 | }; 82 | 83 | static int maxi(int a, int b) 84 | { 85 | 86 | return a > b ? a : b; 87 | 88 | } 89 | 90 | struct nvg_gl_texture *nvg_gl_findtexture(struct nvg_gl_context *glctx, int id) 91 | { 92 | 93 | unsigned int i; 94 | 95 | for (i = 0; i < glctx->ntextures; i++) 96 | { 97 | 98 | if (glctx->textures[i].id == id) 99 | return &glctx->textures[i]; 100 | 101 | } 102 | 103 | return 0; 104 | 105 | } 106 | 107 | static struct nvg_gl_texture *alloctexture(struct nvg_gl_context *glctx) 108 | { 109 | 110 | struct nvg_gl_texture *tex = nvg_gl_findtexture(glctx, 0); 111 | 112 | if (!tex) 113 | { 114 | 115 | if (glctx->ntextures + 1 > glctx->ctextures) 116 | { 117 | 118 | int ctextures = maxi(glctx->ntextures + 1, 4) + glctx->ctextures / 2; 119 | struct nvg_gl_texture *textures = realloc(glctx->textures, sizeof (struct nvg_gl_texture) * ctextures); 120 | 121 | if (!textures) 122 | return 0; 123 | 124 | glctx->textures = textures; 125 | glctx->ctextures = ctextures; 126 | 127 | } 128 | 129 | tex = &glctx->textures[glctx->ntextures++]; 130 | 131 | } 132 | 133 | memset(tex, 0, sizeof (struct nvg_gl_texture)); 134 | 135 | tex->id = ++glctx->textureid; 136 | 137 | return tex; 138 | 139 | } 140 | 141 | static void xformtomat3x4(float *m3, float *t) 142 | { 143 | 144 | m3[0] = t[0]; 145 | m3[1] = t[1]; 146 | m3[2] = 0.0f; 147 | m3[3] = 0.0f; 148 | m3[4] = t[2]; 149 | m3[5] = t[3]; 150 | m3[6] = 0.0f; 151 | m3[7] = 0.0f; 152 | m3[8] = t[4]; 153 | m3[9] = t[5]; 154 | m3[10] = 1.0f; 155 | m3[11] = 0.0f; 156 | 157 | } 158 | 159 | static int convertpaint(struct nvg_gl_context *glctx, struct nvg_gl_fraguniforms *frag, struct nvg_paint *paint, struct nvg_scissor *scissor) 160 | { 161 | 162 | float invxform[6]; 163 | 164 | memset(frag, 0, sizeof (struct nvg_gl_fraguniforms)); 165 | 166 | frag->innerCol = nvg_premulrgba(paint->innerColor); 167 | frag->outerCol = nvg_premulrgba(paint->outerColor); 168 | 169 | if (scissor->extent[0] < -0.5f || scissor->extent[1] < -0.5f) 170 | { 171 | 172 | memset(frag->scissorMat, 0, sizeof (frag->scissorMat)); 173 | 174 | frag->scissorExt[0] = 1.0f; 175 | frag->scissorExt[1] = 1.0f; 176 | frag->scissorScale[0] = 1.0f; 177 | frag->scissorScale[1] = 1.0f; 178 | 179 | } 180 | 181 | else 182 | { 183 | 184 | nvg_xform_inverse(invxform, scissor->xform); 185 | xformtomat3x4(frag->scissorMat, invxform); 186 | 187 | frag->scissorExt[0] = scissor->extent[0]; 188 | frag->scissorExt[1] = scissor->extent[1]; 189 | frag->scissorScale[0] = sqrtf(scissor->xform[0] * scissor->xform[0] + scissor->xform[2] * scissor->xform[2]); 190 | frag->scissorScale[1] = sqrtf(scissor->xform[1] * scissor->xform[1] + scissor->xform[3] * scissor->xform[3]); 191 | 192 | } 193 | 194 | memcpy(frag->extent, paint->extent, sizeof (frag->extent)); 195 | 196 | if (paint->image != 0) 197 | { 198 | 199 | struct nvg_gl_texture *tex = nvg_gl_findtexture(glctx, paint->image); 200 | 201 | if (!tex) 202 | return 0; 203 | 204 | if ((tex->flags & NVG_IMAGE_FLIPY) != 0) 205 | { 206 | 207 | float m1[6], m2[6]; 208 | 209 | nvg_xform_translate(m1, 0.0f, frag->extent[1] * 0.5f); 210 | nvg_xform_multiply(m1, paint->xform); 211 | nvg_xform_scale(m2, 1.0f, -1.0f); 212 | nvg_xform_multiply(m2, m1); 213 | nvg_xform_translate(m1, 0.0f, -frag->extent[1] * 0.5f); 214 | nvg_xform_multiply(m1, m2); 215 | nvg_xform_inverse(invxform, m1); 216 | 217 | } 218 | 219 | else 220 | { 221 | 222 | nvg_xform_inverse(invxform, paint->xform); 223 | 224 | } 225 | 226 | frag->type = NSVG_SHADER_FILLIMG; 227 | 228 | if (tex->type == NVG_TEXTURE_RGBA) 229 | frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0.0f : 1.0f; 230 | else 231 | frag->texType = 2.0f; 232 | 233 | } 234 | 235 | else 236 | { 237 | 238 | frag->type = NSVG_SHADER_FILLGRAD; 239 | frag->radius = paint->radius; 240 | frag->feather = paint->feather; 241 | 242 | nvg_xform_inverse(invxform, paint->xform); 243 | 244 | } 245 | 246 | xformtomat3x4(frag->paintMat, invxform); 247 | 248 | return 1; 249 | 250 | } 251 | 252 | static struct nvg_gl_fraguniforms *fraguniformptr(struct nvg_gl_context *glctx, int i) 253 | { 254 | 255 | return (struct nvg_gl_fraguniforms *)&glctx->uniforms[i]; 256 | 257 | } 258 | 259 | static void setuniforms(struct nvg_gl_context *glctx, int uniformoffset, int image) 260 | { 261 | 262 | struct nvg_gl_fraguniforms *frag = fraguniformptr(glctx, uniformoffset); 263 | 264 | glUniform4fv(glctx->shader.loc[NVG_GL_LOC_FRAG], NVG_GL_UNIFORMARRAYSIZE, &(frag->uniformArray[0][0])); 265 | 266 | if (image != 0) 267 | { 268 | 269 | struct nvg_gl_texture *tex = nvg_gl_findtexture(glctx, image); 270 | 271 | glBindTexture(GL_TEXTURE_2D, tex ? tex->tex : 0); 272 | 273 | } 274 | 275 | else 276 | { 277 | 278 | glBindTexture(GL_TEXTURE_2D, 0); 279 | 280 | } 281 | 282 | } 283 | 284 | void nvg_gl_flush(struct nvg_gl_context *glctx) 285 | { 286 | 287 | unsigned int i; 288 | 289 | if (!glctx->ncalls) 290 | return; 291 | 292 | glUseProgram(glctx->shader.prog); 293 | glEnable(GL_CULL_FACE); 294 | glCullFace(GL_BACK); 295 | glFrontFace(GL_CCW); 296 | glEnable(GL_BLEND); 297 | glDisable(GL_DEPTH_TEST); 298 | glDisable(GL_SCISSOR_TEST); 299 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 300 | glStencilMask(0xffffffff); 301 | glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); 302 | glStencilFunc(GL_ALWAYS, 0, 0xffffffff); 303 | glActiveTexture(GL_TEXTURE0); 304 | glBindTexture(GL_TEXTURE_2D, 0); 305 | #if defined NVG_GL_VERSION_GL3 306 | glBindVertexArray(glctx->vertArr); 307 | #endif 308 | glBindBuffer(GL_ARRAY_BUFFER, glctx->vertBuf); 309 | glBufferData(GL_ARRAY_BUFFER, glctx->nverts * sizeof (struct nvg_vertex), glctx->verts, GL_STREAM_DRAW); 310 | glEnableVertexAttribArray(0); 311 | glEnableVertexAttribArray(1); 312 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof (struct nvg_vertex), (const GLvoid *)(size_t)0); 313 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof (struct nvg_vertex), (const GLvoid *)(2 * sizeof (float))); 314 | glUniform1i(glctx->shader.loc[NVG_GL_LOC_TEX], 0); 315 | glUniform2fv(glctx->shader.loc[NVG_GL_LOC_VIEWSIZE], 1, glctx->view); 316 | 317 | for (i = 0; i < glctx->ncalls; i++) 318 | { 319 | 320 | struct nvg_gl_call *call = &glctx->calls[i]; 321 | 322 | glBlendFuncSeparate(call->blendfunc.srcrgb, call->blendfunc.dstrgb, call->blendfunc.srcalpha, call->blendfunc.dstalpha); 323 | 324 | if (call->type == NVG_GL_FILL) 325 | { 326 | 327 | struct nvg_gl_path *paths = &glctx->paths[call->pathoffset]; 328 | unsigned int j; 329 | 330 | glEnable(GL_STENCIL_TEST); 331 | glStencilMask(0xff); 332 | glStencilFunc(GL_ALWAYS, 0, 0xff); 333 | glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); 334 | setuniforms(glctx, call->uniformoffset, 0); 335 | glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP); 336 | glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP); 337 | glDisable(GL_CULL_FACE); 338 | 339 | for (j = 0; j < call->pathcount; j++) 340 | glDrawArrays(GL_TRIANGLE_FAN, paths[j].filloffset, paths[j].fillcount); 341 | 342 | glEnable(GL_CULL_FACE); 343 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 344 | setuniforms(glctx, call->uniformoffset + glctx->fragsize, call->image); 345 | glStencilFunc(GL_NOTEQUAL, 0x0, 0xff); 346 | glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); 347 | glDrawArrays(GL_TRIANGLE_STRIP, call->triangleoffset, call->trianglecount); 348 | glDisable(GL_STENCIL_TEST); 349 | 350 | } 351 | 352 | else if (call->type == NVG_GL_CONVEXFILL) 353 | { 354 | 355 | struct nvg_gl_path *paths = &glctx->paths[call->pathoffset]; 356 | unsigned int j; 357 | 358 | setuniforms(glctx, call->uniformoffset, call->image); 359 | 360 | for (j = 0; j < call->pathcount; j++) 361 | glDrawArrays(GL_TRIANGLE_FAN, paths[j].filloffset, paths[j].fillcount); 362 | 363 | } 364 | 365 | else if (call->type == NVG_GL_TRIANGLES) 366 | { 367 | 368 | setuniforms(glctx, call->uniformoffset, call->image); 369 | glDrawArrays(GL_TRIANGLES, call->triangleoffset, call->trianglecount); 370 | 371 | } 372 | 373 | } 374 | 375 | glDisableVertexAttribArray(0); 376 | glDisableVertexAttribArray(1); 377 | #if defined NVG_GL_VERSION_GL3 378 | glBindVertexArray(0); 379 | #endif 380 | glDisable(GL_CULL_FACE); 381 | glBindBuffer(GL_ARRAY_BUFFER, 0); 382 | glUseProgram(0); 383 | glBindTexture(GL_TEXTURE_2D, 0); 384 | 385 | } 386 | 387 | static int maxvertcount(const struct nvg_path *paths, int npaths) 388 | { 389 | 390 | int count = 0; 391 | unsigned int i; 392 | 393 | for (i = 0; i < npaths; i++) 394 | count += paths[i].nfill; 395 | 396 | return count; 397 | 398 | } 399 | 400 | static struct nvg_gl_call *alloccall(struct nvg_gl_context *glctx) 401 | { 402 | 403 | struct nvg_gl_call *ret; 404 | 405 | if (glctx->ncalls + 1 > glctx->ccalls) 406 | { 407 | 408 | int ccalls = maxi(glctx->ncalls + 1, 128) + glctx->ccalls / 2; 409 | struct nvg_gl_call *calls = realloc(glctx->calls, sizeof (struct nvg_gl_call) * ccalls); 410 | 411 | if (!calls) 412 | return 0; 413 | 414 | glctx->calls = calls; 415 | glctx->ccalls = ccalls; 416 | 417 | } 418 | 419 | ret = &glctx->calls[glctx->ncalls++]; 420 | 421 | memset(ret, 0, sizeof (struct nvg_gl_call)); 422 | 423 | return ret; 424 | 425 | } 426 | 427 | static int allocpaths(struct nvg_gl_context *glctx, int n) 428 | { 429 | 430 | int ret; 431 | 432 | if (glctx->npaths + n > glctx->cpaths) 433 | { 434 | 435 | int cpaths = maxi(glctx->npaths + n, 128) + glctx->cpaths / 2; 436 | struct nvg_gl_path *paths = realloc(glctx->paths, sizeof (struct nvg_gl_path) * cpaths); 437 | 438 | if (!paths) 439 | return -1; 440 | 441 | glctx->paths = paths; 442 | glctx->cpaths = cpaths; 443 | 444 | } 445 | 446 | ret = glctx->npaths; 447 | glctx->npaths += n; 448 | 449 | return ret; 450 | 451 | } 452 | 453 | static int allocverts(struct nvg_gl_context *glctx, int n) 454 | { 455 | 456 | int ret; 457 | 458 | if (glctx->nverts + n > glctx->cverts) 459 | { 460 | 461 | int cverts = maxi(glctx->nverts + n, 4096) + glctx->cverts / 2; 462 | struct nvg_vertex *verts = realloc(glctx->verts, sizeof (struct nvg_vertex) * cverts); 463 | 464 | if (!verts) 465 | return -1; 466 | 467 | glctx->verts = verts; 468 | glctx->cverts = cverts; 469 | 470 | } 471 | 472 | ret = glctx->nverts; 473 | glctx->nverts += n; 474 | 475 | return ret; 476 | 477 | } 478 | 479 | static int allocfraguniforms(struct nvg_gl_context *glctx, int n) 480 | { 481 | 482 | int ret; 483 | 484 | if (glctx->nuniforms + n > glctx->cuniforms) 485 | { 486 | 487 | int cuniforms = maxi(glctx->nuniforms + n, 128) + glctx->cuniforms / 2; 488 | unsigned char *uniforms = realloc(glctx->uniforms, glctx->fragsize * cuniforms); 489 | 490 | if (!uniforms) 491 | return -1; 492 | 493 | glctx->uniforms = uniforms; 494 | glctx->cuniforms = cuniforms; 495 | 496 | } 497 | 498 | ret = glctx->nuniforms * glctx->fragsize; 499 | glctx->nuniforms += n; 500 | 501 | return ret; 502 | 503 | } 504 | 505 | void nvg_gl_render_paths(struct nvg_gl_context *glctx, struct nvg_paint *paint, struct nvg_scissor *scissor, const float *bounds, const struct nvg_path *paths, int npaths) 506 | { 507 | 508 | struct nvg_gl_call *call = alloccall(glctx); 509 | int maxverts, offset; 510 | unsigned int i; 511 | 512 | if (!call) 513 | return; 514 | 515 | call->type = NVG_GL_FILL; 516 | call->trianglecount = 4; 517 | call->pathoffset = allocpaths(glctx, npaths); 518 | 519 | if (call->pathoffset == -1) 520 | goto error; 521 | 522 | call->pathcount = npaths; 523 | call->image = paint->image; 524 | call->blendfunc = glctx->blendfunc; 525 | 526 | if (npaths == 1 && paths[0].convex) 527 | { 528 | 529 | call->type = NVG_GL_CONVEXFILL; 530 | call->trianglecount = 0; 531 | 532 | } 533 | 534 | maxverts = maxvertcount(paths, npaths) + call->trianglecount; 535 | offset = allocverts(glctx, maxverts); 536 | 537 | if (offset == -1) 538 | goto error; 539 | 540 | for (i = 0; i < npaths; i++) 541 | { 542 | 543 | struct nvg_gl_path *copy = &glctx->paths[call->pathoffset + i]; 544 | const struct nvg_path *path = &paths[i]; 545 | 546 | memset(copy, 0, sizeof (struct nvg_gl_path)); 547 | 548 | if (path->nfill > 0) 549 | { 550 | 551 | copy->filloffset = offset; 552 | copy->fillcount = path->nfill; 553 | 554 | memcpy(&glctx->verts[offset], path->fill, sizeof (struct nvg_vertex) * path->nfill); 555 | 556 | offset += path->nfill; 557 | 558 | } 559 | 560 | } 561 | 562 | if (call->type == NVG_GL_FILL) 563 | { 564 | 565 | struct nvg_vertex *quad; 566 | struct nvg_gl_fraguniforms *frag; 567 | 568 | call->triangleoffset = offset; 569 | quad = &glctx->verts[call->triangleoffset]; 570 | 571 | nvg_setvertex(&quad[0], bounds[2], bounds[3], 0.5f, 1.0f); 572 | nvg_setvertex(&quad[1], bounds[2], bounds[1], 0.5f, 1.0f); 573 | nvg_setvertex(&quad[2], bounds[0], bounds[3], 0.5f, 1.0f); 574 | nvg_setvertex(&quad[3], bounds[0], bounds[1], 0.5f, 1.0f); 575 | 576 | call->uniformoffset = allocfraguniforms(glctx, 2); 577 | 578 | if (call->uniformoffset == -1) 579 | goto error; 580 | 581 | frag = fraguniformptr(glctx, call->uniformoffset); 582 | 583 | memset(frag, 0, sizeof (struct nvg_gl_fraguniforms)); 584 | 585 | frag->type = NSVG_SHADER_SIMPLE; 586 | 587 | convertpaint(glctx, fraguniformptr(glctx, call->uniformoffset + glctx->fragsize), paint, scissor); 588 | 589 | } 590 | 591 | else 592 | { 593 | 594 | call->uniformoffset = allocfraguniforms(glctx, 1); 595 | 596 | if (call->uniformoffset == -1) 597 | goto error; 598 | 599 | convertpaint(glctx, fraguniformptr(glctx, call->uniformoffset), paint, scissor); 600 | 601 | } 602 | 603 | return; 604 | 605 | error: 606 | if (glctx->ncalls > 0) 607 | glctx->ncalls--; 608 | 609 | } 610 | 611 | void nvg_gl_render_vertices(struct nvg_gl_context *glctx, struct nvg_paint *paint, struct nvg_scissor *scissor, const struct nvg_vertex *verts, int nverts) 612 | { 613 | 614 | struct nvg_gl_call *call = alloccall(glctx); 615 | struct nvg_gl_fraguniforms *frag; 616 | 617 | if (!call) 618 | return; 619 | 620 | call->type = NVG_GL_TRIANGLES; 621 | call->image = paint->image; 622 | call->blendfunc = glctx->blendfunc; 623 | call->triangleoffset = allocverts(glctx, nverts); 624 | 625 | if (call->triangleoffset == -1) 626 | goto error; 627 | 628 | call->trianglecount = nverts; 629 | 630 | memcpy(&glctx->verts[call->triangleoffset], verts, sizeof (struct nvg_vertex) * nverts); 631 | 632 | call->uniformoffset = allocfraguniforms(glctx, 1); 633 | 634 | if (call->uniformoffset == -1) 635 | goto error; 636 | 637 | frag = fraguniformptr(glctx, call->uniformoffset); 638 | 639 | convertpaint(glctx, frag, paint, scissor); 640 | 641 | frag->type = NSVG_SHADER_IMG; 642 | 643 | return; 644 | 645 | error: 646 | if (glctx->ncalls > 0) 647 | glctx->ncalls--; 648 | 649 | } 650 | 651 | void nvg_gl_reset(struct nvg_gl_context *glctx, float width, float height) 652 | { 653 | 654 | glctx->view[0] = width; 655 | glctx->view[1] = height; 656 | glctx->blendfunc.srcrgb = GL_ONE; 657 | glctx->blendfunc.dstrgb = GL_ONE_MINUS_SRC_ALPHA; 658 | glctx->blendfunc.srcalpha = GL_ONE; 659 | glctx->blendfunc.dstalpha = GL_ONE_MINUS_SRC_ALPHA; 660 | glctx->nverts = 0; 661 | glctx->npaths = 0; 662 | glctx->ncalls = 0; 663 | glctx->nuniforms = 0; 664 | 665 | } 666 | 667 | int nvg_gl_texture_update(struct nvg_gl_context *glctx, int image, int x, int y, int w, int h, const unsigned char *data) 668 | { 669 | 670 | struct nvg_gl_texture *texture = nvg_gl_findtexture(glctx, image); 671 | 672 | if (!texture) 673 | return 0; 674 | 675 | glBindTexture(GL_TEXTURE_2D, texture->tex); 676 | glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 677 | #ifndef NVG_GL_VERSION_GLES2 678 | glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->width); 679 | glPixelStorei(GL_UNPACK_SKIP_PIXELS, x); 680 | glPixelStorei(GL_UNPACK_SKIP_ROWS, y); 681 | #else 682 | if (texture->type == NVG_TEXTURE_RGBA) 683 | data += y * texture->width * 4; 684 | else 685 | data += y * texture->width; 686 | 687 | x = 0; 688 | w = texture->width; 689 | #endif 690 | 691 | if (texture->type == NVG_TEXTURE_RGBA) 692 | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data); 693 | else 694 | #if defined(NVG_GL_VERSION_GLES2) || defined(NVG_GL_VERSION_GL2) 695 | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); 696 | #else 697 | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RED, GL_UNSIGNED_BYTE, data); 698 | #endif 699 | 700 | glPixelStorei(GL_UNPACK_ALIGNMENT, 4); 701 | #ifndef NVG_GL_VERSION_GLES2 702 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 703 | glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); 704 | glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); 705 | #endif 706 | glBindTexture(GL_TEXTURE_2D, 0); 707 | 708 | return 1; 709 | 710 | } 711 | 712 | int nvg_gl_texture_create(struct nvg_gl_context *glctx, int type, int w, int h, int flags, const unsigned char *data) 713 | { 714 | 715 | struct nvg_gl_texture *texture = alloctexture(glctx); 716 | 717 | if (!texture) 718 | return 0; 719 | 720 | glGenTextures(1, &texture->tex); 721 | 722 | texture->width = w; 723 | texture->height = h; 724 | texture->type = type; 725 | texture->flags = flags; 726 | 727 | glBindTexture(GL_TEXTURE_2D, texture->tex); 728 | glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 729 | #ifndef NVG_GL_VERSION_GLES2 730 | glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->width); 731 | glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); 732 | glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); 733 | #endif 734 | 735 | #if defined (NVG_GL_VERSION_GL2) 736 | if (flags & NVG_IMAGE_GENERATE_MIPMAPS) 737 | glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); 738 | #endif 739 | 740 | if (type == NVG_TEXTURE_RGBA) 741 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); 742 | else 743 | #if defined(NVG_GL_VERSION_GLES2) || defined (NVG_GL_VERSION_GL2) 744 | glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); 745 | #elif defined(NVG_GL_VERSION_GLES3) 746 | glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); 747 | #else 748 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); 749 | #endif 750 | 751 | if (flags & NVG_IMAGE_GENERATE_MIPMAPS) 752 | { 753 | 754 | if (flags & NVG_IMAGE_NEAREST) 755 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); 756 | else 757 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 758 | 759 | } 760 | 761 | else 762 | { 763 | 764 | if (flags & NVG_IMAGE_NEAREST) 765 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 766 | else 767 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 768 | 769 | } 770 | 771 | if (flags & NVG_IMAGE_NEAREST) 772 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 773 | else 774 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 775 | 776 | if (flags & NVG_IMAGE_REPEATX) 777 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 778 | else 779 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 780 | 781 | if (flags & NVG_IMAGE_REPEATY) 782 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 783 | else 784 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 785 | 786 | glPixelStorei(GL_UNPACK_ALIGNMENT, 4); 787 | #ifndef NVG_GL_VERSION_GLES2 788 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 789 | glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); 790 | glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); 791 | #endif 792 | 793 | #if !defined(NVG_GL_VERSION_GL2) 794 | if (flags & NVG_IMAGE_GENERATE_MIPMAPS) 795 | glGenerateMipmap(GL_TEXTURE_2D); 796 | #endif 797 | 798 | glBindTexture(GL_TEXTURE_2D, 0); 799 | 800 | return texture->id; 801 | 802 | } 803 | 804 | void nvg_gl_texture_destroy(struct nvg_gl_context *glctx, int id) 805 | { 806 | 807 | struct nvg_gl_texture *texture = nvg_gl_findtexture(glctx, id); 808 | 809 | if (texture) 810 | { 811 | 812 | if (texture->tex != 0) 813 | glDeleteTextures(1, &texture->tex); 814 | 815 | memset(texture, 0, sizeof (struct nvg_gl_texture)); 816 | 817 | } 818 | 819 | } 820 | 821 | void nvg_gl_create(struct nvg_gl_context *glctx, int w, int h) 822 | { 823 | 824 | GLuint prog, vert, frag; 825 | const char *str[2]; 826 | 827 | static const char *header = 828 | #if defined NVG_GL_VERSION_GL2 829 | "#define NVG_GL_VERSION_GL2 1\n" 830 | #elif defined NVG_GL_VERSION_GL3 831 | "#version 150 core\n" 832 | "#define NVG_GL_VERSION_GL3 1\n" 833 | #elif defined NVG_GL_VERSION_GLES2 834 | "#version 100\n" 835 | "#define NVG_GL_VERSION_GL2 1\n" 836 | #elif defined NVG_GL_VERSION_GLES3 837 | "#version 300 es\n" 838 | "#define NVG_GL_VERSION_GL3 1\n" 839 | #endif 840 | "#define UNIFORMARRAYSIZE 11\n" 841 | "\n"; 842 | 843 | static const char *vertexfill = 844 | "uniform vec2 viewsize;\n" 845 | "#ifdef NVG_GL_VERSION_GL3\n" 846 | " in vec2 vertex;\n" 847 | " in vec2 tcoord;\n" 848 | " out vec2 ftcoord;\n" 849 | " out vec2 fpos;\n" 850 | "#else\n" 851 | " attribute vec2 vertex;\n" 852 | " attribute vec2 tcoord;\n" 853 | " varying vec2 ftcoord;\n" 854 | " varying vec2 fpos;\n" 855 | "#endif\n" 856 | "void main(void) {\n" 857 | " ftcoord = tcoord;\n" 858 | " fpos = vertex;\n" 859 | " gl_Position = vec4(2.0 * vertex.x / viewsize.x - 1.0, 1.0 - 2.0 * vertex.y / viewsize.y, 0, 1);\n" 860 | "}\n"; 861 | 862 | static const char *fragmentfill = 863 | "#ifdef GL_ES\n" 864 | "#if defined(GL_FRAGMENT_PRECISION_HIGH) || defined(NVG_GL_VERSION_GL3)\n" 865 | " precision highp float;\n" 866 | "#else\n" 867 | " precision mediump float;\n" 868 | "#endif\n" 869 | "#endif\n" 870 | "uniform vec4 frag[UNIFORMARRAYSIZE];\n" 871 | "uniform sampler2D tex;\n" 872 | "#ifdef NVG_GL_VERSION_GL3\n" 873 | " in vec2 ftcoord;\n" 874 | " in vec2 fpos;\n" 875 | " out vec4 outColor;\n" 876 | "#else\n" 877 | " varying vec2 ftcoord;\n" 878 | " varying vec2 fpos;\n" 879 | "#endif\n" 880 | "#define scissorMat mat3(frag[0].xyz, frag[1].xyz, frag[2].xyz)\n" 881 | "#define paintMat mat3(frag[3].xyz, frag[4].xyz, frag[5].xyz)\n" 882 | "#define innerCol frag[6]\n" 883 | "#define outerCol frag[7]\n" 884 | "#define scissorExt frag[8].xy\n" 885 | "#define scissorScale frag[8].zw\n" 886 | "#define extent frag[9].xy\n" 887 | "#define radius frag[9].z\n" 888 | "#define feather frag[9].w\n" 889 | "#define texType int(frag[10].z)\n" 890 | "#define type int(frag[10].w)\n" 891 | "float sdroundrect(vec2 pt, vec2 ext, float rad) {\n" 892 | " vec2 ext2 = ext - vec2(rad, rad);\n" 893 | " vec2 d = abs(pt) - ext2;\n" 894 | " return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - rad;\n" 895 | "}\n" 896 | "float scissorMask(vec2 p) {\n" 897 | " vec2 sc = (abs((scissorMat * vec3(p, 1.0)).xy) - scissorExt);\n" 898 | " sc = vec2(0.5, 0.5) - sc * scissorScale;\n" 899 | " return clamp(sc.x, 0.0, 1.0) * clamp(sc.y, 0.0, 1.0);\n" 900 | "}\n" 901 | "void main(void) {\n" 902 | " vec4 result;\n" 903 | " float scissor = scissorMask(fpos);\n" 904 | " float strokeAlpha = 1.0;\n" 905 | " if (type == 0) {\n" 906 | " vec2 pt = (paintMat * vec3(fpos, 1.0)).xy;\n" 907 | " float d = clamp((sdroundrect(pt, extent, radius) + feather * 0.5) / feather, 0.0, 1.0);\n" 908 | " vec4 color = mix(innerCol,outerCol, d);\n" 909 | " color *= strokeAlpha * scissor;\n" 910 | " result = color;\n" 911 | " } else if (type == 1) {\n" 912 | " vec2 pt = (paintMat * vec3(fpos, 1.0)).xy / extent;\n" 913 | "#ifdef NVG_GL_VERSION_GL3\n" 914 | " vec4 color = texture(tex, pt);\n" 915 | "#else\n" 916 | " vec4 color = texture2D(tex, pt);\n" 917 | "#endif\n" 918 | " if (texType == 1) color = vec4(color.xyz * color.w, color.w);" 919 | " if (texType == 2) color = vec4(color.x);" 920 | " color *= innerCol;\n" 921 | " color *= strokeAlpha * scissor;\n" 922 | " result = color;\n" 923 | " } else if (type == 2) {\n" 924 | " result = vec4(1, 1, 1, 1);\n" 925 | " } else if (type == 3) {\n" 926 | "#ifdef NVG_GL_VERSION_GL3\n" 927 | " vec4 color = texture(tex, ftcoord);\n" 928 | "#else\n" 929 | " vec4 color = texture2D(tex, ftcoord);\n" 930 | "#endif\n" 931 | " if (texType == 1) color = vec4(color.xyz * color.w, color.w);" 932 | " if (texType == 2) color = vec4(color.x);" 933 | " color *= scissor;\n" 934 | " result = color * innerCol;\n" 935 | " }\n" 936 | "#ifdef NVG_GL_VERSION_GL3\n" 937 | " outColor = result;\n" 938 | "#else\n" 939 | " gl_FragColor = result;\n" 940 | "#endif\n" 941 | "}\n"; 942 | 943 | str[0] = header; 944 | 945 | prog = glCreateProgram(); 946 | vert = glCreateShader(GL_VERTEX_SHADER); 947 | frag = glCreateShader(GL_FRAGMENT_SHADER); 948 | 949 | str[1] = vertexfill; 950 | 951 | glShaderSource(vert, 2, str, 0); 952 | 953 | str[1] = fragmentfill; 954 | 955 | glShaderSource(frag, 2, str, 0); 956 | glCompileShader(vert); 957 | glCompileShader(frag); 958 | glAttachShader(prog, vert); 959 | glAttachShader(prog, frag); 960 | glBindAttribLocation(prog, 0, "vertex"); 961 | glBindAttribLocation(prog, 1, "tcoord"); 962 | glLinkProgram(prog); 963 | 964 | glctx->shader.prog = prog; 965 | glctx->shader.vert = vert; 966 | glctx->shader.frag = frag; 967 | glctx->shader.loc[NVG_GL_LOC_VIEWSIZE] = glGetUniformLocation(glctx->shader.prog, "viewsize"); 968 | glctx->shader.loc[NVG_GL_LOC_TEX] = glGetUniformLocation(glctx->shader.prog, "tex"); 969 | glctx->shader.loc[NVG_GL_LOC_FRAG] = glGetUniformLocation(glctx->shader.prog, "frag"); 970 | 971 | #if defined NVG_GL_VERSION_GL3 972 | glGenVertexArrays(1, &glctx->vertArr); 973 | #endif 974 | glGenBuffers(1, &glctx->vertBuf); 975 | glFinish(); 976 | 977 | glctx->fragsize = sizeof (struct nvg_gl_fraguniforms) + 4 - sizeof (struct nvg_gl_fraguniforms) % 4; 978 | glctx->fontimage = nvg_gl_texture_create(glctx, NVG_TEXTURE_ALPHA, w, h, 0, 0); 979 | 980 | } 981 | 982 | void nvg_gl_destroy(struct nvg_gl_context *glctx) 983 | { 984 | 985 | unsigned int i; 986 | 987 | nvg_gl_texture_destroy(glctx, glctx->fontimage); 988 | 989 | if (glctx->shader.prog) 990 | glDeleteProgram(glctx->shader.prog); 991 | 992 | if (glctx->shader.vert) 993 | glDeleteShader(glctx->shader.vert); 994 | 995 | if (glctx->shader.frag) 996 | glDeleteShader(glctx->shader.frag); 997 | 998 | #if NVG_GL_VERSION_GL3 999 | if (glctx->vertArr != 0) 1000 | glDeleteVertexArrays(1, &glctx->vertArr); 1001 | #endif 1002 | 1003 | if (glctx->vertBuf != 0) 1004 | glDeleteBuffers(1, &glctx->vertBuf); 1005 | 1006 | for (i = 0; i < glctx->ntextures; i++) 1007 | { 1008 | 1009 | if (glctx->textures[i].tex != 0) 1010 | glDeleteTextures(1, &glctx->textures[i].tex); 1011 | 1012 | } 1013 | 1014 | free(glctx->textures); 1015 | free(glctx->paths); 1016 | free(glctx->verts); 1017 | free(glctx->uniforms); 1018 | free(glctx->calls); 1019 | 1020 | } 1021 | 1022 | -------------------------------------------------------------------------------- /src/nvg_gl.h: -------------------------------------------------------------------------------- 1 | enum nvg_gl_texturetype 2 | { 3 | 4 | NVG_TEXTURE_ALPHA = 0x01, 5 | NVG_TEXTURE_RGBA = 0x02 6 | 7 | }; 8 | 9 | enum nvg_imageflags 10 | { 11 | 12 | NVG_IMAGE_GENERATE_MIPMAPS = 1 << 0, 13 | NVG_IMAGE_REPEATX = 1 << 1, 14 | NVG_IMAGE_REPEATY = 1 << 2, 15 | NVG_IMAGE_FLIPY = 1 << 3, 16 | NVG_IMAGE_PREMULTIPLIED = 1 << 4, 17 | NVG_IMAGE_NEAREST = 1 << 5 18 | 19 | }; 20 | 21 | struct nvg_gl_shader 22 | { 23 | 24 | GLuint prog; 25 | GLuint frag; 26 | GLuint vert; 27 | GLint loc[3]; 28 | 29 | }; 30 | 31 | struct nvg_gl_texture 32 | { 33 | 34 | int id; 35 | GLuint tex; 36 | int width; 37 | int height; 38 | int type; 39 | int flags; 40 | 41 | }; 42 | 43 | struct nvg_gl_blend 44 | { 45 | 46 | GLenum srcrgb; 47 | GLenum dstrgb; 48 | GLenum srcalpha; 49 | GLenum dstalpha; 50 | 51 | }; 52 | 53 | struct nvg_gl_call 54 | { 55 | 56 | int type; 57 | int image; 58 | int pathoffset; 59 | int pathcount; 60 | int triangleoffset; 61 | int trianglecount; 62 | int uniformoffset; 63 | struct nvg_gl_blend blendfunc; 64 | 65 | }; 66 | 67 | struct nvg_gl_path 68 | { 69 | 70 | int filloffset; 71 | int fillcount; 72 | 73 | }; 74 | 75 | struct nvg_gl_context 76 | { 77 | 78 | float view[2]; 79 | struct nvg_gl_shader shader; 80 | struct nvg_gl_texture *textures; 81 | unsigned int ntextures; 82 | unsigned int ctextures; 83 | unsigned int textureid; 84 | GLuint vertBuf; 85 | GLuint vertArr; 86 | GLuint fragBuf; 87 | unsigned int fragsize; 88 | struct nvg_gl_call *calls; 89 | unsigned int ccalls; 90 | unsigned int ncalls; 91 | struct nvg_gl_path *paths; 92 | unsigned int cpaths; 93 | unsigned int npaths; 94 | struct nvg_vertex *verts; 95 | unsigned int cverts; 96 | unsigned int nverts; 97 | unsigned char *uniforms; 98 | unsigned int cuniforms; 99 | unsigned int nuniforms; 100 | struct nvg_gl_blend blendfunc; 101 | int fontimage; 102 | 103 | }; 104 | 105 | struct nvg_gl_texture *nvg_gl_findtexture(struct nvg_gl_context *glctx, int id); 106 | void nvg_gl_flush(struct nvg_gl_context *glctx); 107 | void nvg_gl_reset(struct nvg_gl_context *glctx, float width, float height); 108 | void nvg_gl_fill(struct nvg_gl_context *glctx, struct nvg_context *ctx, struct nvg_paint *paint, struct nvg_scissor *scissor); 109 | int nvg_gl_texture_update(struct nvg_gl_context *glctx, int image, int x, int y, int w, int h, const unsigned char *data); 110 | int nvg_gl_texture_create(struct nvg_gl_context *glctx, int type, int w, int h, int imageFlags, const unsigned char *data); 111 | void nvg_gl_texture_destroy(struct nvg_gl_context *glctx, int id); 112 | void nvg_gl_create(struct nvg_gl_context *glctx, int w, int h); 113 | void nvg_gl_render_paths(struct nvg_gl_context *glctx, struct nvg_paint *paint, struct nvg_scissor *scissor, const float *bounds, const struct nvg_path *paths, int npaths); 114 | void nvg_gl_render_vertices(struct nvg_gl_context *glctx, struct nvg_paint *paint, struct nvg_scissor *scissor, const struct nvg_vertex *verts, int nverts); 115 | void nvg_gl_destroy(struct nvg_gl_context *glctx); 116 | -------------------------------------------------------------------------------- /src/parser.h: -------------------------------------------------------------------------------- 1 | struct parser 2 | { 3 | 4 | struct 5 | { 6 | 7 | char *data; 8 | unsigned int count; 9 | unsigned int offset; 10 | unsigned int line; 11 | unsigned int linebreak; 12 | unsigned int inside; 13 | unsigned int escaped; 14 | 15 | } expr; 16 | 17 | void (*fail)(void); 18 | struct widget *(*find)(char *name); 19 | struct widget *(*create)(unsigned int type, char *id, char *in); 20 | struct widget *(*destroy)(struct widget *widget); 21 | void (*clear)(struct widget *widget); 22 | unsigned int errors; 23 | 24 | }; 25 | 26 | void parser_parse(struct parser *parser, char *in, unsigned int count, void *data); 27 | void parser_init(struct parser *parser, void (*fail)(void), struct widget *(*find)(char *name), struct widget *(*create)(unsigned int type, char *id, char *in), struct widget *(*destroy)(struct widget *widget), void (*clear)(struct widget *widget)); 28 | -------------------------------------------------------------------------------- /src/pool.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "list.h" 4 | #include "style.h" 5 | #include "url.h" 6 | #include "resource.h" 7 | #include "attribute.h" 8 | #include "widget.h" 9 | #include "view.h" 10 | #include "animation.h" 11 | 12 | #define NWIDGETS 512 13 | #define NRESOURCES 128 14 | 15 | static struct frame frames[NWIDGETS]; 16 | static struct widget widgets[NWIDGETS]; 17 | static struct resource resources[NRESOURCES]; 18 | static struct list freewidgets; 19 | static struct list usedwidgets; 20 | static struct list freeresources; 21 | static struct list usedresources; 22 | 23 | static struct widget *prevwidget(struct list *list, struct widget *widget) 24 | { 25 | 26 | struct list_item *current = (widget) ? widget->item.prev : list->tail; 27 | 28 | return (current) ? current->data : 0; 29 | 30 | } 31 | 32 | static struct widget *nextwidget(struct list *list, struct widget *widget) 33 | { 34 | 35 | struct list_item *current = (widget) ? widget->item.next : list->head; 36 | 37 | return (current) ? current->data : 0; 38 | 39 | } 40 | 41 | struct widget *pool_widget_prev(struct widget *widget) 42 | { 43 | 44 | return prevwidget(&usedwidgets, widget); 45 | 46 | } 47 | 48 | struct widget *pool_widget_next(struct widget *widget) 49 | { 50 | 51 | return nextwidget(&usedwidgets, widget); 52 | 53 | } 54 | 55 | struct widget *pool_widget_find(char *name) 56 | { 57 | 58 | struct widget *widget = 0; 59 | 60 | while ((widget = pool_widget_next(widget))) 61 | { 62 | 63 | if (!strlen(widget->header.id.name)) 64 | continue; 65 | 66 | if (!strcmp(widget->header.id.name, name)) 67 | return widget; 68 | 69 | } 70 | 71 | return 0; 72 | 73 | } 74 | 75 | struct widget *pool_widget_nextchild(struct widget *widget, struct widget *parent) 76 | { 77 | 78 | while ((widget = pool_widget_next(widget))) 79 | { 80 | 81 | if (pool_widget_find(widget->header.in.name) == parent) 82 | return widget; 83 | 84 | } 85 | 86 | return 0; 87 | 88 | } 89 | 90 | struct widget *pool_widget_create(void) 91 | { 92 | 93 | struct list_item *item = list_pickhead(&freewidgets); 94 | 95 | if (!item) 96 | return 0; 97 | 98 | list_add(&usedwidgets, item); 99 | 100 | return item->data; 101 | 102 | } 103 | 104 | struct widget *pool_widget_destroy(struct widget *widget) 105 | { 106 | 107 | list_move(&freewidgets, &usedwidgets, &widget->item); 108 | 109 | return 0; 110 | 111 | } 112 | 113 | struct frame *pool_getframe(unsigned int index) 114 | { 115 | 116 | return &frames[index]; 117 | 118 | } 119 | 120 | static struct resource *nextresource(struct list *list, struct resource *resource) 121 | { 122 | 123 | struct list_item *current = (resource) ? resource->item.next : list->head; 124 | 125 | return (current) ? current->data : 0; 126 | 127 | } 128 | 129 | struct resource *pool_resource_find(char *url) 130 | { 131 | 132 | struct resource *resource = 0; 133 | 134 | while ((resource = nextresource(&usedresources, resource))) 135 | { 136 | 137 | if (!strlen(resource->url)) 138 | continue; 139 | 140 | if (!strcmp(resource->url, url)) 141 | return resource; 142 | 143 | } 144 | 145 | return 0; 146 | 147 | } 148 | 149 | struct resource *pool_resource_create(void) 150 | { 151 | 152 | struct list_item *item = list_pickhead(&freeresources); 153 | 154 | if (!item) 155 | return 0; 156 | 157 | list_add(&usedresources, item); 158 | 159 | return item->data; 160 | 161 | } 162 | 163 | struct resource *pool_resource_destroy(struct resource *resource) 164 | { 165 | 166 | list_move(&freeresources, &usedresources, &resource->item); 167 | 168 | return 0; 169 | 170 | } 171 | 172 | static char *allocate(unsigned int type, char *string, unsigned int size, unsigned int count, char *content) 173 | { 174 | 175 | if (string) 176 | { 177 | 178 | free(string); 179 | 180 | string = 0; 181 | 182 | } 183 | 184 | if (size) 185 | { 186 | 187 | string = malloc(size); 188 | 189 | if (count) 190 | memcpy(string, content, count); 191 | 192 | } 193 | 194 | return string; 195 | 196 | } 197 | 198 | char *pool_string_create(unsigned int type, char *string, unsigned int size, unsigned int count, char *content) 199 | { 200 | 201 | return allocate(type, string, (size) ? size : count, count, content); 202 | 203 | } 204 | 205 | char *pool_string_destroy(unsigned int type, char *string) 206 | { 207 | 208 | return allocate(type, string, 0, 0, 0); 209 | 210 | } 211 | 212 | void pool_setup(void) 213 | { 214 | 215 | unsigned int i; 216 | 217 | for (i = 0; i < NWIDGETS; i++) 218 | { 219 | 220 | widgets[i].index = i; 221 | 222 | list_inititem(&widgets[i].item, &widgets[i]); 223 | list_add(&freewidgets, &widgets[i].item); 224 | 225 | } 226 | 227 | for (i = 0; i < NRESOURCES; i++) 228 | { 229 | 230 | list_inititem(&resources[i].item, &resources[i]); 231 | list_add(&freeresources, &resources[i].item); 232 | 233 | } 234 | 235 | } 236 | 237 | -------------------------------------------------------------------------------- /src/pool.h: -------------------------------------------------------------------------------- 1 | struct widget *pool_widget_prev(struct widget *widget); 2 | struct widget *pool_widget_next(struct widget *widget); 3 | struct widget *pool_widget_find(char *name); 4 | struct widget *pool_widget_nextchild(struct widget *widget, struct widget *parent); 5 | struct widget *pool_widget_create(void); 6 | struct widget *pool_widget_destroy(struct widget *widget); 7 | struct frame *pool_getframe(unsigned int index); 8 | struct resource *pool_resource_find(char *url); 9 | struct resource *pool_resource_create(void); 10 | struct resource *pool_resource_destroy(struct resource *resource); 11 | char *pool_string_create(unsigned int type, char *string, unsigned int size, unsigned int count, char *content); 12 | char *pool_string_destroy(unsigned int type, char *string); 13 | void pool_setup(void); 14 | -------------------------------------------------------------------------------- /src/render.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #if defined NVG_GL_GLEW 8 | #include 9 | #endif 10 | 11 | #if defined NVG_GL_VERSION_GLES2 12 | #include 13 | #endif 14 | 15 | #if defined NVG_GL_VERSION_GLES3 16 | #include 17 | #endif 18 | 19 | #include "stb_truetype.h" 20 | #define STB_IMAGE_IMPLEMENTATION 21 | #include "stb_image.h" 22 | #include "fons.h" 23 | #include "nvg.h" 24 | #include "nvg_gl.h" 25 | #include "list.h" 26 | #include "style.h" 27 | #include "url.h" 28 | #include "resource.h" 29 | #include "attribute.h" 30 | #include "widget.h" 31 | #include "render.h" 32 | #include "pool.h" 33 | 34 | #define CHARTYPE_NONE 0 35 | #define CHARTYPE_SPACE 1 36 | #define CHARTYPE_NEWLINE 2 37 | #define CHARTYPE_CHAR 3 38 | 39 | static struct nvg_context ctx; 40 | static struct nvg_gl_context glctx; 41 | static struct fons_context fsctx; 42 | 43 | struct textrow 44 | { 45 | 46 | const char *start; 47 | const char *end; 48 | float width; 49 | 50 | }; 51 | 52 | static unsigned int getchartype(unsigned int codepoint) 53 | { 54 | 55 | switch (codepoint) 56 | { 57 | 58 | case 9: 59 | case 11: 60 | case 12: 61 | case 32: 62 | case 0x00a0: 63 | return CHARTYPE_SPACE; 64 | 65 | case 10: 66 | case 0x0085: 67 | return CHARTYPE_NEWLINE; 68 | 69 | default: 70 | return CHARTYPE_CHAR; 71 | 72 | } 73 | 74 | return 0; 75 | 76 | } 77 | 78 | static const char *calcline(struct style_font *font, float width, const char *string, const char *end, struct textrow *row) 79 | { 80 | 81 | struct fons_textiter iter; 82 | float rowWidth = 0; 83 | const char *rowStart = 0; 84 | const char *rowEnd = 0; 85 | const char *wordStart = 0; 86 | const char *breakEnd = 0; 87 | float breakWidth = 0; 88 | unsigned int ptype = CHARTYPE_SPACE; 89 | 90 | fons_inititer(&fsctx, &iter, &fsctx.fonts[font->face], font->align, font->size, 0, 0, string, end); 91 | 92 | while (fons_nextiter(&fsctx, &iter)) 93 | { 94 | 95 | unsigned int type = getchartype(iter.codepoint); 96 | 97 | switch (type) 98 | { 99 | 100 | case CHARTYPE_NEWLINE: 101 | row->start = rowStart != 0 ? rowStart : iter.str; 102 | row->end = rowEnd != 0 ? rowEnd : iter.str; 103 | row->width = rowWidth; 104 | 105 | return iter.next; 106 | 107 | case CHARTYPE_SPACE: 108 | if (ptype == CHARTYPE_CHAR) 109 | { 110 | 111 | breakEnd = iter.str; 112 | breakWidth = rowWidth; 113 | 114 | } 115 | 116 | break; 117 | 118 | case CHARTYPE_CHAR: 119 | if (!rowStart) 120 | { 121 | 122 | rowStart = iter.str; 123 | wordStart = iter.str; 124 | breakEnd = rowStart; 125 | breakWidth = 0.0; 126 | 127 | } 128 | 129 | rowEnd = iter.next; 130 | rowWidth = iter.nextx; 131 | 132 | if (ptype == CHARTYPE_SPACE) 133 | { 134 | 135 | wordStart = iter.str; 136 | 137 | } 138 | 139 | if (iter.nextx > width) 140 | { 141 | 142 | if (breakEnd == rowStart) 143 | { 144 | 145 | row->start = rowStart; 146 | row->end = iter.str; 147 | row->width = rowWidth; 148 | 149 | return iter.str; 150 | 151 | } 152 | 153 | else 154 | { 155 | 156 | row->start = rowStart; 157 | row->end = breakEnd; 158 | row->width = breakWidth; 159 | 160 | return wordStart; 161 | 162 | } 163 | 164 | } 165 | 166 | break; 167 | 168 | } 169 | 170 | ptype = type; 171 | 172 | } 173 | 174 | if (rowStart) 175 | { 176 | 177 | row->start = rowStart; 178 | row->end = rowEnd; 179 | row->width = rowWidth; 180 | 181 | return end; 182 | 183 | } 184 | 185 | return 0; 186 | 187 | } 188 | 189 | static void renderfill(struct nvg_paint *paint, struct nvg_scissor *scissor) 190 | { 191 | 192 | nvg_flatten(&ctx); 193 | nvg_expand(&ctx); 194 | nvg_gl_render_paths(&glctx, paint, scissor, ctx.bounds, ctx.paths, ctx.npaths); 195 | 196 | } 197 | 198 | static void rendericon(struct nvg_paint *paint, struct nvg_scissor *scissor, struct style_font *font, float x, float y, int codepoint) 199 | { 200 | 201 | struct fons_glyph *glyph = fons_getglyph(&fsctx, &fsctx.fonts[font->face], codepoint, font->size); 202 | unsigned int nverts = 0; 203 | 204 | if (glyph) 205 | { 206 | 207 | struct fons_quad q; 208 | float c[8]; 209 | 210 | fons_getquad(&fsctx, glyph, &q, x, y); 211 | nvg_getpoints(&c[0], &c[1], ctx.xform, q.x0, q.y0); 212 | nvg_getpoints(&c[2], &c[3], ctx.xform, q.x1, q.y0); 213 | nvg_getpoints(&c[4], &c[5], ctx.xform, q.x1, q.y1); 214 | nvg_getpoints(&c[6], &c[7], ctx.xform, q.x0, q.y1); 215 | 216 | if (nverts + 6 <= NVG_VERTSSIZE) 217 | { 218 | 219 | nvg_setvertex(&ctx.verts[nverts + 0], c[0], c[1], q.s0, q.t0); 220 | nvg_setvertex(&ctx.verts[nverts + 1], c[4], c[5], q.s1, q.t1); 221 | nvg_setvertex(&ctx.verts[nverts + 2], c[2], c[3], q.s1, q.t0); 222 | nvg_setvertex(&ctx.verts[nverts + 3], c[0], c[1], q.s0, q.t0); 223 | nvg_setvertex(&ctx.verts[nverts + 4], c[6], c[7], q.s0, q.t1); 224 | nvg_setvertex(&ctx.verts[nverts + 5], c[4], c[5], q.s1, q.t1); 225 | 226 | nverts += 6; 227 | 228 | } 229 | 230 | } 231 | 232 | nvg_gl_texture_update(&glctx, glctx.fontimage, 0, 0, fsctx.width, fsctx.height, fsctx.texdata); 233 | 234 | paint->image = glctx.fontimage; 235 | 236 | nvg_gl_render_vertices(&glctx, paint, scissor, ctx.verts, nverts); 237 | 238 | } 239 | 240 | static float rendertext(struct nvg_paint *paint, struct nvg_scissor *scissor, struct style_font *font, float x, float y, const char *string, const char *end) 241 | { 242 | 243 | struct fons_textiter iter; 244 | unsigned int nverts = 0; 245 | 246 | fons_inititer(&fsctx, &iter, &fsctx.fonts[font->face], font->align, font->size, x, y, string, end); 247 | 248 | while (fons_nextiter(&fsctx, &iter)) 249 | { 250 | 251 | struct fons_glyph *glyph = fons_getglyph(&fsctx, iter.font, iter.codepoint, iter.size); 252 | 253 | if (glyph) 254 | { 255 | 256 | struct fons_quad q; 257 | float c[8]; 258 | 259 | fons_getquad(&fsctx, glyph, &q, iter.x, iter.y); 260 | nvg_getpoints(&c[0], &c[1], ctx.xform, q.x0, q.y0); 261 | nvg_getpoints(&c[2], &c[3], ctx.xform, q.x1, q.y0); 262 | nvg_getpoints(&c[4], &c[5], ctx.xform, q.x1, q.y1); 263 | nvg_getpoints(&c[6], &c[7], ctx.xform, q.x0, q.y1); 264 | 265 | if (nverts + 6 <= NVG_VERTSSIZE) 266 | { 267 | 268 | nvg_setvertex(&ctx.verts[nverts + 0], c[0], c[1], q.s0, q.t0); 269 | nvg_setvertex(&ctx.verts[nverts + 1], c[4], c[5], q.s1, q.t1); 270 | nvg_setvertex(&ctx.verts[nverts + 2], c[2], c[3], q.s1, q.t0); 271 | nvg_setvertex(&ctx.verts[nverts + 3], c[0], c[1], q.s0, q.t0); 272 | nvg_setvertex(&ctx.verts[nverts + 4], c[6], c[7], q.s0, q.t1); 273 | nvg_setvertex(&ctx.verts[nverts + 5], c[4], c[5], q.s1, q.t1); 274 | 275 | nverts += 6; 276 | 277 | } 278 | 279 | } 280 | 281 | } 282 | 283 | nvg_gl_texture_update(&glctx, glctx.fontimage, 0, 0, fsctx.width, fsctx.height, fsctx.texdata); 284 | 285 | paint->image = glctx.fontimage; 286 | 287 | nvg_gl_render_vertices(&glctx, paint, scissor, ctx.verts, nverts); 288 | 289 | return iter.nextx; 290 | 291 | } 292 | 293 | static struct resource *getresource(char *url) 294 | { 295 | 296 | struct resource *resource = pool_resource_find(url); 297 | 298 | if (!resource) 299 | { 300 | 301 | resource = pool_resource_create(); 302 | 303 | if (!resource) 304 | return 0; 305 | 306 | resource_init(resource, url); 307 | 308 | } 309 | 310 | return resource; 311 | 312 | } 313 | 314 | struct resource *render_loadfont(char *url) 315 | { 316 | 317 | struct resource *resource = getresource(url); 318 | 319 | if (!resource) 320 | return 0; 321 | 322 | resource_iref(resource); 323 | 324 | if (resource->index) 325 | return resource; 326 | 327 | resource_load(resource, 0, 0); 328 | 329 | resource->index = fons_addfont(&fsctx, resource->data, resource->count); 330 | 331 | return resource; 332 | 333 | } 334 | 335 | struct resource *render_loadimage(char *url) 336 | { 337 | 338 | struct resource *resource = getresource(url); 339 | unsigned char *data; 340 | 341 | if (!resource) 342 | return 0; 343 | 344 | resource_iref(resource); 345 | 346 | if (resource->index) 347 | return resource; 348 | 349 | resource_load(resource, 0, 0); 350 | 351 | data = stbi_load_from_memory(resource->data, resource->count, &resource->w, &resource->h, &resource->n, 4); 352 | 353 | if (data) 354 | { 355 | 356 | resource->index = nvg_gl_texture_create(&glctx, NVG_TEXTURE_RGBA, resource->w, resource->h, 0, data); 357 | 358 | stbi_image_free(data); 359 | 360 | } 361 | 362 | return resource; 363 | 364 | } 365 | 366 | void render_unloadimage(struct resource *resource) 367 | { 368 | 369 | nvg_gl_texture_destroy(&glctx, resource->index); 370 | 371 | resource_dref(resource); 372 | 373 | } 374 | 375 | void render_reset(float w, float h) 376 | { 377 | 378 | nvg_reset(&ctx); 379 | nvg_gl_reset(&glctx, w, h); 380 | 381 | } 382 | 383 | void render_flush(void) 384 | { 385 | 386 | nvg_gl_flush(&glctx); 387 | 388 | } 389 | 390 | void render_create(void) 391 | { 392 | 393 | fons_create(&fsctx, 512, 512); 394 | nvg_gl_create(&glctx, fsctx.width, fsctx.height); 395 | 396 | } 397 | 398 | void render_destroy(void) 399 | { 400 | 401 | fons_delete(&fsctx); 402 | nvg_gl_destroy(&glctx); 403 | 404 | } 405 | 406 | float render_textwidth(struct style *style, char *text) 407 | { 408 | 409 | struct textrow row; 410 | const char *current = text; 411 | const char *end = current + strlen(text) + 1; 412 | float w = 0; 413 | 414 | while ((current = calcline(&style->font, style->box.w, current, end, &row))) 415 | { 416 | 417 | if (w < row.width) 418 | w = row.width; 419 | 420 | } 421 | 422 | return w + 2; 423 | 424 | } 425 | 426 | float render_textheight(struct style *style, char *text) 427 | { 428 | 429 | struct textrow row; 430 | const char *current = text; 431 | const char *end = current + strlen(text) + 1; 432 | unsigned int i; 433 | 434 | for (i = 0; (current = calcline(&style->font, style->box.w, current, end, &row)); i++); 435 | 436 | return i * style->font.size + 2; 437 | 438 | } 439 | 440 | void render_filltext(struct style *style, char *text) 441 | { 442 | 443 | struct nvg_scissor scissor; 444 | struct nvg_paint paint; 445 | struct textrow row; 446 | const char *current = text; 447 | const char *end = current + strlen(text) + 1; 448 | float x = style->box.x; 449 | float y = style->box.y; 450 | 451 | nvg_scissor_init(&scissor); 452 | nvg_paint_color(&paint, style->color.r, style->color.g, style->color.b, style->color.a); 453 | 454 | while ((current = calcline(&style->font, style->box.w, current, end, &row))) 455 | { 456 | 457 | if (style->font.align & STYLE_ALIGN_RIGHT) 458 | rendertext(&paint, &scissor, &style->font, x + style->box.w, y, row.start, row.end); 459 | else if (style->font.align & STYLE_ALIGN_CENTER) 460 | rendertext(&paint, &scissor, &style->font, x + style->box.w / 2, y, row.start, row.end); 461 | else 462 | rendertext(&paint, &scissor, &style->font, x, y, row.start, row.end); 463 | 464 | y += style->font.size; 465 | 466 | } 467 | 468 | } 469 | 470 | void render_filltextinput(struct style *style, char *text, int offset, struct style_color *frame_color_focus) 471 | { 472 | 473 | struct nvg_scissor scissor; 474 | struct nvg_paint textpaint; 475 | struct nvg_paint cursorpaint; 476 | struct textrow row; 477 | const char *current = text; 478 | const char *end = current + strlen(text) + 1; 479 | const char *last = text; 480 | float x = style->box.x; 481 | float y = style->box.y; 482 | 483 | nvg_scissor_init(&scissor); 484 | nvg_paint_color(&textpaint, style->color.r, style->color.g, style->color.b, style->color.a); 485 | nvg_paint_color(&cursorpaint, frame_color_focus->r, frame_color_focus->g, frame_color_focus->b, frame_color_focus->a); 486 | 487 | while ((current = calcline(&style->font, style->box.w, current, end, &row))) 488 | { 489 | 490 | if (offset >= 0 && offset <= row.end - row.start) 491 | { 492 | 493 | const char *split = row.start + offset; 494 | 495 | x = rendertext(&textpaint, &scissor, &style->font, x, y, row.start, split); 496 | 497 | rendertext(&textpaint, &scissor, &style->font, x, y, split, row.end); 498 | nvg_path_begin(&ctx); 499 | nvg_path_roundedrect(&ctx, x, y, 3, style->font.size, 0); 500 | renderfill(&cursorpaint, &scissor); 501 | 502 | x = style->box.x; 503 | 504 | } 505 | 506 | else 507 | { 508 | 509 | rendertext(&textpaint, &scissor, &style->font, x, y, row.start, row.end); 510 | 511 | } 512 | 513 | offset -= (int)(current - last); 514 | last = current; 515 | y += style->font.size; 516 | 517 | } 518 | 519 | } 520 | 521 | void render_fillicon(struct style *style, int type) 522 | { 523 | 524 | struct nvg_scissor scissor; 525 | struct nvg_paint paint; 526 | 527 | nvg_scissor_init(&scissor); 528 | nvg_paint_color(&paint, style->color.r, style->color.g, style->color.b, style->color.a); 529 | rendericon(&paint, &scissor, &style->font, style->box.x, style->box.y, type); 530 | 531 | } 532 | 533 | void render_fillrectborder(struct style *border, float bordersize) 534 | { 535 | 536 | struct nvg_scissor scissor; 537 | struct nvg_paint paint; 538 | 539 | nvg_scissor_init(&scissor); 540 | nvg_paint_color(&paint, border->color.r, border->color.g, border->color.b, border->color.a); 541 | nvg_path_begin(&ctx); 542 | nvg_path_roundedrect(&ctx, border->box.x, border->box.y, border->box.w, border->box.h, border->box.r); 543 | nvg_path_hole(&ctx); 544 | nvg_path_roundedrect(&ctx, border->box.x + bordersize, border->box.y + bordersize, border->box.w - bordersize * 2, border->box.h - bordersize * 2, border->box.r); 545 | renderfill(&paint, &scissor); 546 | 547 | } 548 | 549 | void render_fillrectbordergap(struct style *border, float bordersize, float x, float w) 550 | { 551 | 552 | struct nvg_scissor scissor; 553 | struct nvg_paint paint; 554 | 555 | nvg_scissor_init(&scissor); 556 | nvg_paint_color(&paint, border->color.r, border->color.g, border->color.b, border->color.a); 557 | nvg_path_begin(&ctx); 558 | nvg_path_roundedrect(&ctx, border->box.x, border->box.y, border->box.w, border->box.h, border->box.r); 559 | nvg_path_hole(&ctx); 560 | nvg_path_roundedrect(&ctx, border->box.x + bordersize, border->box.y + bordersize, border->box.w - bordersize * 2, border->box.h - bordersize * 2, border->box.r); 561 | nvg_path_rect(&ctx, x, border->box.y, w, bordersize); 562 | renderfill(&paint, &scissor); 563 | 564 | } 565 | 566 | void render_fillrect(struct style *style) 567 | { 568 | 569 | struct nvg_scissor scissor; 570 | struct nvg_paint paint; 571 | 572 | nvg_scissor_init(&scissor); 573 | nvg_paint_color(&paint, style->color.r, style->color.g, style->color.b, style->color.a); 574 | nvg_path_begin(&ctx); 575 | nvg_path_roundedrect(&ctx, style->box.x, style->box.y, style->box.w, style->box.h, style->box.r); 576 | renderfill(&paint, &scissor); 577 | 578 | } 579 | 580 | void render_fillrectgap(struct style *border, float x, float w) 581 | { 582 | 583 | struct nvg_scissor scissor; 584 | struct nvg_paint paint; 585 | 586 | nvg_scissor_init(&scissor); 587 | nvg_paint_color(&paint, border->color.r, border->color.g, border->color.b, border->color.a); 588 | nvg_path_begin(&ctx); 589 | nvg_path_roundedrect(&ctx, border->box.x, border->box.y, border->box.w, border->box.h, border->box.r); 590 | nvg_path_hole(&ctx); 591 | nvg_path_rect(&ctx, x, border->box.y, w, border->box.h); 592 | renderfill(&paint, &scissor); 593 | 594 | } 595 | 596 | void render_fillcircle(struct style *style) 597 | { 598 | 599 | struct nvg_scissor scissor; 600 | struct nvg_paint paint; 601 | 602 | nvg_scissor_init(&scissor); 603 | nvg_paint_color(&paint, style->color.r, style->color.g, style->color.b, style->color.a); 604 | nvg_path_begin(&ctx); 605 | nvg_path_circle(&ctx, style->box.x, style->box.y, style->box.r); 606 | renderfill(&paint, &scissor); 607 | 608 | } 609 | 610 | void render_fillimage(struct style *style, int image) 611 | { 612 | 613 | struct nvg_scissor scissor; 614 | struct nvg_paint paint; 615 | 616 | nvg_scissor_init(&scissor); 617 | nvg_paint_image(&paint, style->box.x, style->box.y, style->box.w, style->box.h, 0.0, image, 1.0); 618 | nvg_path_begin(&ctx); 619 | nvg_path_roundedrect(&ctx, style->box.x, style->box.y, style->box.w, style->box.h, style->box.r); 620 | renderfill(&paint, &scissor); 621 | 622 | } 623 | 624 | void render_background(int w, int h, struct style_color *color) 625 | { 626 | 627 | struct style background; 628 | 629 | style_box_init(&background.box, 0, 0, w, h, 0); 630 | style_color_clone(&background.color, color); 631 | render_fillrect(&background); 632 | 633 | } 634 | 635 | -------------------------------------------------------------------------------- /src/render.h: -------------------------------------------------------------------------------- 1 | struct resource *render_loadfont(char *url); 2 | struct resource *render_loadimage(char *url); 3 | void render_unloadimage(struct resource *resource); 4 | void render_reset(float w, float h); 5 | void render_flush(void); 6 | void render_create(void); 7 | void render_destroy(void); 8 | float render_textwidth(struct style *style, char *text); 9 | float render_textheight(struct style *style, char *text); 10 | void render_filltext(struct style *style, char *text); 11 | void render_filltextinput(struct style *style, char *text, int offset, struct style_color *frame_color_focus); 12 | void render_fillicon(struct style *style, int type); 13 | void render_fillrectborder(struct style *border, float bordersize); 14 | void render_fillrectbordergap(struct style *border, float bordersize, float x, float w); 15 | void render_fillrect(struct style *style); 16 | void render_fillrectgap(struct style *border, float x, float w); 17 | void render_fillcircle(struct style *style); 18 | void render_fillimage(struct style *style, int image); 19 | void render_background(int w, int h, struct style_color *color); 20 | -------------------------------------------------------------------------------- /src/resource.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "list.h" 6 | #include "url.h" 7 | #include "resource.h" 8 | #include "history.h" 9 | 10 | static void save(struct resource *resource, unsigned int count, void *data) 11 | { 12 | 13 | unsigned int free = resource->size - resource->count; 14 | 15 | if (free < count) 16 | { 17 | 18 | resource->size += RESOURCE_PAGESIZE; 19 | resource->data = realloc(resource->data, resource->size); 20 | 21 | } 22 | 23 | memcpy((char *)resource->data + resource->count, data, count); 24 | 25 | resource->count += count; 26 | 27 | } 28 | 29 | static unsigned int resolve(struct resource *resource, const char *name, char * const *args) 30 | { 31 | 32 | int fd[2]; 33 | pid_t pid; 34 | 35 | if (pipe(fd) < 0) 36 | return 0; 37 | 38 | pid = fork(); 39 | 40 | if (pid < 0) 41 | return 0; 42 | 43 | if (pid) 44 | { 45 | 46 | char bdata[RESOURCE_PAGESIZE]; 47 | int bcount; 48 | 49 | close(fd[1]); 50 | 51 | while ((bcount = read(fd[0], bdata, RESOURCE_PAGESIZE))) 52 | save(resource, bcount, bdata); 53 | 54 | close(fd[0]); 55 | 56 | return resource->count; 57 | 58 | } 59 | 60 | else 61 | { 62 | 63 | dup2(fd[0], 0); 64 | dup2(fd[1], 1); 65 | close(fd[0]); 66 | close(fd[1]); 67 | execvp(name, args); 68 | exit(EXIT_FAILURE); 69 | 70 | return 0; 71 | 72 | } 73 | 74 | } 75 | 76 | static unsigned int _curl_match(struct resource *resource) 77 | { 78 | 79 | return !strncmp(resource->url, "http:", 5) || !strncmp(resource->url, "https:", 6); 80 | 81 | } 82 | 83 | static unsigned int _curl_load(struct resource *resource, unsigned int count, void *data) 84 | { 85 | 86 | char *q[16]; 87 | 88 | if (count) 89 | { 90 | 91 | q[0] = "curl"; 92 | q[1] = "-s"; 93 | q[2] = "-L"; 94 | q[3] = "-A"; 95 | q[4] = "Navi/1.0"; 96 | q[5] = "-X"; 97 | q[6] = "POST"; 98 | q[7] = "-H"; 99 | q[8] = "Content-Type: application/x-www-form-urlencoded"; 100 | q[9] = "-d"; 101 | q[10] = data; 102 | q[11] = resource->url; 103 | q[12] = 0; 104 | 105 | 106 | } 107 | 108 | else 109 | { 110 | 111 | q[0] = "curl"; 112 | q[1] = "-s"; 113 | q[2] = "-L"; 114 | q[3] = "-A"; 115 | q[4] = "Navi/1.0"; 116 | q[5] = "-X"; 117 | q[6] = "GET"; 118 | q[7] = resource->url; 119 | q[8] = 0; 120 | 121 | } 122 | 123 | return resolve(resource, "curl", q); 124 | 125 | } 126 | 127 | static unsigned int _navi_match(struct resource *resource) 128 | { 129 | 130 | return !strncmp(resource->url, "navi:", 5); 131 | 132 | } 133 | 134 | static unsigned int _navi_load(struct resource *resource, unsigned int count, void *data) 135 | { 136 | 137 | char *path = resource->url + 7; 138 | 139 | if (!strncmp(path, "blank", 5)) 140 | { 141 | 142 | char buffer[4096]; 143 | static char *fmt = 144 | "= window label \"Navi 1.0\"\n" 145 | "+ table id bar grid \"08:04\"\n" 146 | "+ field id \"url\" in bar label \"URL\" data \"%s\" onlinebreak \"post\" \"navi://lookup\"\n" 147 | "+ button in bar label \"Lookup\" onclick \"post\" \"navi://lookup\" mode \"on\" icon \"earth\"\n" 148 | "+ text label \"Notice: Use the right mouse button to go back.\"\n" 149 | "+ divider\n" 150 | "+ header2 label \"Bookmarks\"\n" 151 | "+ text label \"No bookmarks\"\n" 152 | "+ divider\n" 153 | "+ button label \"Settings\" onclick \"get\" \"navi://settings\" icon \"options\"\n"; 154 | 155 | resource->count = sprintf(buffer, fmt, "http://"); 156 | resource->data = malloc(resource->count); 157 | 158 | strcpy(resource->data, buffer); 159 | 160 | return resource->count; 161 | 162 | } 163 | 164 | else if (!strncmp(path, "settings", 8)) 165 | { 166 | 167 | char buffer[4096]; 168 | static char *fmt = 169 | "= window label \"Settings\"\n" 170 | "+ header label \"Settings\"\n" 171 | "+ table id settings grid \"06\"\n" 172 | "+ text in settings label \"Left mouse button\"\n" 173 | "+ text in settings label \"Interact\"\n" 174 | "+ text in settings label \"Right mouse button\"\n" 175 | "+ text in settings label \"Go back\"\n" 176 | "+ text in settings label \"Middle mouse button\"\n" 177 | "+ text in settings label \"Go home\"\n" 178 | "+ text in settings label \"Ctrl+B *\"\n" 179 | "+ text in settings label \"Bookmarks view\"\n" 180 | "+ text in settings label \"Ctrl+D\"\n" 181 | "+ text in settings label \"File view\"\n" 182 | "+ text in settings label \"Ctrl+H *\"\n" 183 | "+ text in settings label \"History view\"\n" 184 | "+ text in settings label \"Ctrl+L\"\n" 185 | "+ text in settings label \"Home\"\n" 186 | "+ text in settings label \"Ctrl+M\"\n" 187 | "+ text in settings label \"Night mode\"\n" 188 | "+ text in settings label \"Ctrl+N\"\n" 189 | "+ text in settings label \"Light mode\"\n" 190 | "+ text in settings label \"Ctrl+Q\"\n" 191 | "+ text in settings label \"Quit\"\n" 192 | "+ text in settings label \"Ctrl+R\"\n" 193 | "+ text in settings label \"Refresh\"\n" 194 | "+ text in settings label \"Ctrl+V\"\n" 195 | "+ text in settings label \"Paste from clipboard\"\n" 196 | "+ text label \"* = Needs certain setup to work.\"\n"; 197 | 198 | resource->count = sprintf(buffer, fmt, data); 199 | resource->data = malloc(resource->count); 200 | 201 | strcpy(resource->data, buffer); 202 | 203 | return resource->count; 204 | 205 | } 206 | 207 | else if (!strncmp(path, "notfound", 8)) 208 | { 209 | 210 | char buffer[4096]; 211 | static char *fmt = 212 | "= window label \"Not found\"\n" 213 | "+ header label \"Not found\"\n" 214 | "+ text label \"The URL does not seem to exist.\"\n" 215 | "+ code label \"URL: %s\"\n" 216 | "+ text label \"Press the right mouse button to go back.\"\n"; 217 | 218 | resource->count = sprintf(buffer, fmt, data); 219 | resource->data = malloc(resource->count); 220 | 221 | strcpy(resource->data, buffer); 222 | 223 | return resource->count; 224 | 225 | } 226 | 227 | else if (!strncmp(path, "syntaxerror", 8)) 228 | { 229 | 230 | char buffer[4096]; 231 | static char *fmt = 232 | "= window label \"Syntax error\"\n" 233 | "+ header label \"Syntax error\"\n" 234 | "+ text label \"This page could not be understood. Maybe it is not an ALFI website?\"\n" 235 | "+ code label \"URL: %s\"\n" 236 | "+ text label \"Press the right mouse button to go back.\"\n"; 237 | 238 | resource->count = sprintf(buffer, fmt, data); 239 | resource->data = malloc(resource->count); 240 | 241 | strcpy(resource->data, buffer); 242 | 243 | return resource->count; 244 | 245 | } 246 | 247 | else if (!strncmp(path, "error", 5)) 248 | { 249 | 250 | char buffer[4096]; 251 | static char *fmt = 252 | "= window label \"Internal error\"\n" 253 | "+ header label \"Internal error\"\n" 254 | "+ text label \"An internal error occured.\"\n"; 255 | 256 | resource->count = sprintf(buffer, fmt); 257 | resource->data = malloc(resource->count); 258 | 259 | strcpy(resource->data, buffer); 260 | 261 | return resource->count; 262 | 263 | } 264 | 265 | else if (!strncmp(path, "lookup", 6)) 266 | { 267 | 268 | struct history *current = history_get(0); 269 | char *urlparam = data; 270 | 271 | strcpy(resource->url, urlparam + 4); 272 | strcpy(current->url, resource->url); 273 | resource_load(resource, 0, 0); 274 | 275 | return resource->count; 276 | 277 | } 278 | 279 | return 0; 280 | 281 | } 282 | 283 | static unsigned int _external_match(struct resource *resource) 284 | { 285 | 286 | return 1; 287 | 288 | } 289 | 290 | static unsigned int _external_load(struct resource *resource, unsigned int count, void *data) 291 | { 292 | 293 | char *q[4]; 294 | 295 | q[0] = "navi-resolve"; 296 | q[1] = resource->url; 297 | q[2] = data; 298 | q[3] = 0; 299 | 300 | return resolve(resource, "navi-resolve", q); 301 | 302 | } 303 | 304 | void resource_load(struct resource *resource, unsigned int count, void *data) 305 | { 306 | 307 | if (_navi_match(resource)) 308 | _navi_load(resource, count, data); 309 | else if (_curl_match(resource)) 310 | _curl_load(resource, count, data); 311 | else if (_external_match(resource)) 312 | _external_load(resource, count, data); 313 | 314 | } 315 | 316 | void resource_unload(struct resource *resource) 317 | { 318 | 319 | if (resource->data) 320 | free(resource->data); 321 | 322 | } 323 | 324 | unsigned int resource_iref(struct resource *resource) 325 | { 326 | 327 | return ++resource->refcount; 328 | 329 | } 330 | 331 | unsigned int resource_dref(struct resource *resource) 332 | { 333 | 334 | return --resource->refcount; 335 | 336 | } 337 | 338 | void resource_init(struct resource *resource, char *url) 339 | { 340 | 341 | strcpy(resource->url, url); 342 | 343 | resource->data = 0; 344 | resource->size = 0; 345 | resource->count = 0; 346 | resource->refcount = 0; 347 | 348 | } 349 | 350 | void resource_destroy(struct resource *resource) 351 | { 352 | 353 | strcpy(resource->url, ""); 354 | 355 | resource->data = 0; 356 | resource->size = 0; 357 | resource->count = 0; 358 | resource->refcount = 0; 359 | 360 | } 361 | 362 | -------------------------------------------------------------------------------- /src/resource.h: -------------------------------------------------------------------------------- 1 | #define RESOURCE_PAGESIZE 0x1000 2 | 3 | struct resource 4 | { 5 | 6 | struct list_item item; 7 | char url[URL_SIZE]; 8 | void *data; 9 | unsigned int size; 10 | unsigned int count; 11 | unsigned int refcount; 12 | int index; 13 | int w; 14 | int h; 15 | int n; 16 | 17 | }; 18 | 19 | void resource_load(struct resource *resource, unsigned int count, void *data); 20 | void resource_unload(struct resource *resource); 21 | unsigned int resource_iref(struct resource *resource); 22 | unsigned int resource_dref(struct resource *resource); 23 | void resource_init(struct resource *resource, char *url); 24 | void resource_destroy(struct resource *resource); 25 | -------------------------------------------------------------------------------- /src/style.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "style.h" 4 | 5 | static float flerp(float t, float c, float u) 6 | { 7 | 8 | if (fabs(t - c) <= 1) 9 | return t; 10 | 11 | return c + (t - c) * u; 12 | 13 | } 14 | 15 | void style_box_init(struct style_box *box, float x, float y, float w, float h, float r) 16 | { 17 | 18 | box->x = x; 19 | box->y = y; 20 | box->w = w; 21 | box->h = h; 22 | box->r = r; 23 | 24 | } 25 | 26 | void style_box_clone(struct style_box *box, struct style_box *target) 27 | { 28 | 29 | box->x = target->x; 30 | box->y = target->y; 31 | box->w = target->w; 32 | box->h = target->h; 33 | box->r = target->r; 34 | 35 | } 36 | 37 | void style_box_move(struct style_box *box, float x, float y) 38 | { 39 | 40 | box->x = x; 41 | box->y = y; 42 | 43 | } 44 | 45 | void style_box_scale(struct style_box *box, float w, float h) 46 | { 47 | 48 | box->w = w; 49 | box->h = h; 50 | 51 | } 52 | 53 | void style_box_translate(struct style_box *box, float x, float y) 54 | { 55 | 56 | box->x += x; 57 | box->y += y; 58 | 59 | } 60 | 61 | void style_box_resize(struct style_box *box, float w, float h) 62 | { 63 | 64 | box->w += w; 65 | box->h += h; 66 | 67 | } 68 | 69 | void style_box_shrink(struct style_box *box, float px, float py) 70 | { 71 | 72 | box->x += px; 73 | box->y += py; 74 | box->w -= px * 2; 75 | box->h -= py * 2; 76 | 77 | if (box->w < 0) 78 | box->w = 0; 79 | 80 | if (box->h < 0) 81 | box->h = 0; 82 | 83 | } 84 | 85 | float style_box_halign(struct style_box *box, float x, float w, int align) 86 | { 87 | 88 | /* 89 | switch (align) 90 | { 91 | 92 | case ALFI_HALIGN_LEFT: 93 | return x; 94 | 95 | case ALFI_HALIGN_CENTER: 96 | return x + w * 0.5 - box->w * 0.5; 97 | 98 | case ALFI_HALIGN_RIGHT: 99 | return x + w - box->w; 100 | 101 | } 102 | */ 103 | 104 | return 0; 105 | 106 | } 107 | 108 | float style_box_valign(struct style_box *box, float y, float h, int align) 109 | { 110 | 111 | /* 112 | switch (align) 113 | { 114 | 115 | case ALFI_VALIGN_TOP: 116 | return y; 117 | 118 | case ALFI_VALIGN_MIDDLE: 119 | return y + h * 0.5 - box->h * 0.5; 120 | 121 | case ALFI_VALIGN_BOTTOM: 122 | return y + h - box->h; 123 | 124 | } 125 | */ 126 | 127 | return 0; 128 | 129 | } 130 | 131 | unsigned int style_box_istouching(struct style_box *box, float x, float y) 132 | { 133 | 134 | if (x < box->x || x >= box->x + box->w) 135 | return 0; 136 | 137 | if (y < box->y || y >= box->y + box->h) 138 | return 0; 139 | 140 | return 1; 141 | 142 | } 143 | 144 | void style_box_expand(struct style_box *box, struct style_box *child, float px, float py) 145 | { 146 | 147 | if (box->w < child->w + px * 2) 148 | box->w = child->w + px * 2; 149 | 150 | if (box->h < child->h + py * 2) 151 | box->h = child->h + py * 2; 152 | 153 | } 154 | 155 | void style_box_lerp(struct style_box *box, float x, float y, float w, float h, float r, float u) 156 | { 157 | 158 | box->x = flerp(x, box->x, u); 159 | box->y = flerp(y, box->y, u); 160 | box->w = flerp(w, box->w, u); 161 | box->h = flerp(h, box->h, u); 162 | box->r = flerp(r, box->r, u); 163 | 164 | } 165 | 166 | void style_box_tween(struct style_box *box, struct style_box *from, float u) 167 | { 168 | 169 | style_box_lerp(box, from->x, from->y, from->w, from->h, from->r, u); 170 | 171 | } 172 | 173 | unsigned int style_box_compare(struct style_box *b1, struct style_box *b2) 174 | { 175 | 176 | if (b1->x != b2->x) 177 | return 1; 178 | 179 | if (b1->y != b2->y) 180 | return 1; 181 | 182 | if (b1->w != b2->w) 183 | return 1; 184 | 185 | if (b1->h != b2->h) 186 | return 1; 187 | 188 | if (b1->r != b2->r) 189 | return 1; 190 | 191 | return 0; 192 | 193 | } 194 | 195 | void style_color_init(struct style_color *color, unsigned char r, unsigned char g, unsigned char b, unsigned char a) 196 | { 197 | 198 | color->r = r; 199 | color->g = g; 200 | color->b = b; 201 | color->a = a; 202 | 203 | } 204 | 205 | void style_color_clone(struct style_color *color, struct style_color *target) 206 | { 207 | 208 | color->r = target->r; 209 | color->g = target->g; 210 | color->b = target->b; 211 | color->a = target->a; 212 | 213 | } 214 | 215 | void style_color_lerp(struct style_color *color, float r, float g, float b, float a, float u) 216 | { 217 | 218 | color->r = flerp(r, color->r, u); 219 | color->g = flerp(g, color->g, u); 220 | color->b = flerp(b, color->b, u); 221 | color->a = flerp(a, color->a, u); 222 | 223 | } 224 | 225 | void style_color_tween(struct style_color *color, struct style_color *from, float u) 226 | { 227 | 228 | style_color_lerp(color, from->r, from->g, from->b, from->a, u); 229 | 230 | } 231 | 232 | unsigned int style_color_compare(struct style_color *c1, struct style_color *c2) 233 | { 234 | 235 | if (c1->r != c2->r) 236 | return 1; 237 | 238 | if (c1->g != c2->g) 239 | return 1; 240 | 241 | if (c1->b != c2->b) 242 | return 1; 243 | 244 | if (c1->a != c2->a) 245 | return 1; 246 | 247 | return 0; 248 | 249 | } 250 | 251 | void style_font_init(struct style_font *font, int face, float size, int align) 252 | { 253 | 254 | font->face = face; 255 | font->size = size; 256 | font->align = align; 257 | 258 | } 259 | 260 | void style_font_lerp(struct style_font *font, int face, float size, int align, float u) 261 | { 262 | 263 | font->face = face; 264 | font->size = flerp(size, font->size, u); 265 | font->align = align; 266 | 267 | } 268 | 269 | void style_font_tween(struct style_font *font, struct style_font *from, float u) 270 | { 271 | 272 | style_font_lerp(font, from->face, from->size, from->align, u); 273 | 274 | } 275 | 276 | unsigned int style_font_compare(struct style_font *f1, struct style_font *f2) 277 | { 278 | 279 | if (f1->size != f2->size) 280 | return 1; 281 | 282 | return 0; 283 | 284 | } 285 | 286 | void style_tween(struct style *s1, struct style *s2, float u) 287 | { 288 | 289 | style_box_tween(&s1->box, &s2->box, u); 290 | style_color_tween(&s1->color, &s2->color, u); 291 | style_font_tween(&s1->font, &s2->font, u); 292 | 293 | } 294 | 295 | void style_init(struct style *style) 296 | { 297 | 298 | style_box_init(&style->box, 0, 0, 0, 0, 0); 299 | style_color_init(&style->color, 0, 0, 0, 0); 300 | style_font_init(&style->font, 0, 0, 0); 301 | 302 | } 303 | 304 | unsigned int style_compare(struct style *s1, struct style *s2) 305 | { 306 | 307 | if (style_box_compare(&s1->box, &s2->box)) 308 | return 1; 309 | 310 | if (style_color_compare(&s1->color, &s2->color)) 311 | return 1; 312 | 313 | if (style_font_compare(&s1->font, &s2->font)) 314 | return 1; 315 | 316 | return 0; 317 | 318 | } 319 | 320 | -------------------------------------------------------------------------------- /src/style.h: -------------------------------------------------------------------------------- 1 | enum style_align 2 | { 3 | 4 | STYLE_ALIGN_LEFT = 1 << 0, 5 | STYLE_ALIGN_CENTER = 1 << 1, 6 | STYLE_ALIGN_RIGHT = 1 << 2, 7 | STYLE_ALIGN_TOP = 1 << 3, 8 | STYLE_ALIGN_MIDDLE = 1 << 4, 9 | STYLE_ALIGN_BOTTOM = 1 << 5, 10 | STYLE_ALIGN_BASELINE = 1 << 6 11 | 12 | }; 13 | 14 | struct style_box 15 | { 16 | 17 | float x; 18 | float y; 19 | float w; 20 | float h; 21 | float r; 22 | 23 | }; 24 | 25 | struct style_color 26 | { 27 | 28 | unsigned char r; 29 | unsigned char g; 30 | unsigned char b; 31 | unsigned char a; 32 | 33 | }; 34 | 35 | struct style_font 36 | { 37 | 38 | int face; 39 | float size; 40 | unsigned int align; 41 | 42 | }; 43 | 44 | struct style 45 | { 46 | 47 | struct style_box box; 48 | struct style_color color; 49 | struct style_font font; 50 | 51 | }; 52 | 53 | void style_box_init(struct style_box *box, float x, float y, float w, float h, float r); 54 | void style_box_clone(struct style_box *box, struct style_box *target); 55 | void style_box_move(struct style_box *box, float x, float y); 56 | void style_box_scale(struct style_box *box, float w, float h); 57 | void style_box_translate(struct style_box *box, float x, float y); 58 | void style_box_resize(struct style_box *box, float w, float h); 59 | void style_box_shrink(struct style_box *box, float px, float py); 60 | float style_box_halign(struct style_box *box, float x, float w, int align); 61 | float style_box_valign(struct style_box *box, float y, float h, int align); 62 | unsigned int style_box_istouching(struct style_box *box, float x, float y); 63 | void style_box_expand(struct style_box *box, struct style_box *child, float px, float py); 64 | void style_box_lerp(struct style_box *box, float x, float y, float w, float h, float r, float u); 65 | void style_box_tween(struct style_box *box, struct style_box *from, float u); 66 | unsigned int style_box_compare(struct style_box *b1, struct style_box *b2); 67 | void style_color_init(struct style_color *color, unsigned char r, unsigned char g, unsigned char b, unsigned char a); 68 | void style_color_clone(struct style_color *color, struct style_color *target); 69 | void style_color_lerp(struct style_color *color, float r, float g, float b, float a, float u); 70 | void style_color_tween(struct style_color *color, struct style_color *from, float u); 71 | unsigned int style_color_compare(struct style_color *c1, struct style_color *c2); 72 | void style_font_init(struct style_font *font, int face, float size, int align); 73 | void style_font_lerp(struct style_font *font, int face, float size, int align, float u); 74 | void style_font_tween(struct style_font *font, struct style_font *from, float u); 75 | unsigned int style_font_compare(struct style_font *f1, struct style_font *f2); 76 | void style_tween(struct style *s1, struct style *s2, float u); 77 | void style_init(struct style *style); 78 | unsigned int style_compare(struct style *s1, struct style *s2); 79 | -------------------------------------------------------------------------------- /src/url.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "url.h" 4 | 5 | static void removepath(char *buffer) 6 | { 7 | 8 | char *c = strrchr(buffer, '/'); 9 | 10 | if (!c) 11 | return; 12 | 13 | c[0] = '\0'; 14 | 15 | } 16 | 17 | static void mergesimple(char *buffer, char *path, unsigned int count) 18 | { 19 | 20 | if (count == 2 && path[0] == '.' && path[1] == '.') 21 | { 22 | 23 | removepath(buffer); 24 | 25 | } 26 | 27 | else if (count == 1 && path[0] == '.') 28 | { 29 | 30 | } 31 | 32 | else 33 | { 34 | 35 | strcat(buffer, "/"); 36 | strncat(buffer, path, count); 37 | 38 | } 39 | 40 | } 41 | 42 | static void mergepath(char *buffer, char *path, char *end) 43 | { 44 | 45 | char *p; 46 | char *c; 47 | 48 | for (p = path; (c = strchr(p, '/')); p = c + 1) 49 | mergesimple(buffer, p, c - p); 50 | 51 | if (p < end) 52 | mergesimple(buffer, p, end - p); 53 | 54 | } 55 | 56 | void url_merge(char *out, char *current, char *url) 57 | { 58 | 59 | if (strchr(url, ':')) 60 | { 61 | 62 | strcpy(out, url); 63 | 64 | } 65 | 66 | else 67 | { 68 | 69 | char *path; 70 | 71 | strcpy(out, current); 72 | 73 | path = strchr(out, ':') + 3; 74 | 75 | removepath(path); 76 | mergepath(out, url, url + strlen(url)); 77 | 78 | } 79 | 80 | } 81 | 82 | -------------------------------------------------------------------------------- /src/url.h: -------------------------------------------------------------------------------- 1 | #define URL_SIZE 0x800 2 | 3 | void url_merge(char *out, char *current, char *url); 4 | -------------------------------------------------------------------------------- /src/view.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "view.h" 3 | 4 | static int clamp(int v, int min, int max) 5 | { 6 | 7 | if (v < min) 8 | return min; 9 | 10 | if (v > max) 11 | return max; 12 | 13 | return v; 14 | 15 | } 16 | 17 | void view_init(struct view *view, int w, int h, unsigned int size) 18 | { 19 | 20 | view->pagew = w; 21 | view->pageh = h; 22 | view->unitw = w / 28; 23 | 24 | if (view->unitw < 32) 25 | view->unitw = 32; 26 | 27 | if (view->unitw > 56) 28 | view->unitw = 56; 29 | 30 | view->unith = 40; 31 | view->marginw = 16; 32 | view->marginh = 16; 33 | view->padw = view->unitw; 34 | view->padh = view->unith; 35 | 36 | view_fontsize(view, size); 37 | 38 | } 39 | 40 | void view_fontsize(struct view *view, unsigned int size) 41 | { 42 | 43 | view->fontsizesmall = 20 + 8 * size; 44 | view->fontsizemedium = 28 + 8 * size; 45 | view->fontsizelarge = 44 + 8 * size; 46 | view->fontsizexlarge = 76 + 8 * size; 47 | 48 | } 49 | 50 | void view_reset(struct view *view) 51 | { 52 | 53 | view->scrollx = 0; 54 | view->scrolly = 0; 55 | 56 | } 57 | 58 | void view_scroll(struct view *view, int x, int y) 59 | { 60 | 61 | view->scrollx += x * view->unitw; 62 | view->scrolly += y * view->unith; 63 | 64 | } 65 | 66 | void view_adjust(struct view *view, float w, float h) 67 | { 68 | 69 | view->scrollx = (view->pagew < w) ? clamp(view->scrollx, view->pagew - w - view->padw * 2, 0) : view->pagew / 2 - (w + view->padw * 2) / 2; 70 | view->scrolly = (view->pageh < h + view->padh * 2) ? clamp(view->scrolly, view->pageh - h - view->padh * 2, 0) : 0; 71 | 72 | } 73 | 74 | -------------------------------------------------------------------------------- /src/view.h: -------------------------------------------------------------------------------- 1 | struct view 2 | { 3 | 4 | int pagew; 5 | int pageh; 6 | int unitw; 7 | int unith; 8 | int marginw; 9 | int marginh; 10 | int scrollx; 11 | int scrolly; 12 | int padw; 13 | int padh; 14 | int fontsizesmall; 15 | int fontsizemedium; 16 | int fontsizelarge; 17 | int fontsizexlarge; 18 | 19 | }; 20 | 21 | void view_init(struct view *view, int w, int h, unsigned int size); 22 | void view_fontsize(struct view *view, unsigned int size); 23 | void view_reset(struct view *view); 24 | void view_scroll(struct view *view, int x, int y); 25 | void view_adjust(struct view *view, float w, float h); 26 | -------------------------------------------------------------------------------- /src/widget.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "list.h" 4 | #include "style.h" 5 | #include "url.h" 6 | #include "resource.h" 7 | #include "view.h" 8 | #include "attribute.h" 9 | #include "widget.h" 10 | #include "pool.h" 11 | 12 | static void anchor_create(struct widget_payload_anchor *payload) 13 | { 14 | 15 | attribute_label_create(&payload->label, ""); 16 | attribute_onclick_create(&payload->onclick, FUNCTION_NONE, ""); 17 | attribute_target_create(&payload->target, ATTRIBUTE_TARGET_BLANK); 18 | 19 | } 20 | 21 | static void anchor_destroy(struct widget_payload_anchor *payload) 22 | { 23 | 24 | attribute_label_destroy(&payload->label); 25 | attribute_onclick_destroy(&payload->onclick); 26 | attribute_target_destroy(&payload->target); 27 | 28 | } 29 | 30 | static void button_create(struct widget_payload_button *payload) 31 | { 32 | 33 | attribute_icon_create(&payload->icon, ATTRIBUTE_ICON_NONE); 34 | attribute_label_create(&payload->label, ""); 35 | attribute_mode_create(&payload->mode, ATTRIBUTE_MODE_OFF); 36 | attribute_onclick_create(&payload->onclick, FUNCTION_NONE, ""); 37 | attribute_target_create(&payload->target, ATTRIBUTE_TARGET_BLANK); 38 | 39 | } 40 | 41 | static void button_destroy(struct widget_payload_button *payload) 42 | { 43 | 44 | attribute_icon_destroy(&payload->icon); 45 | attribute_label_destroy(&payload->label); 46 | attribute_mode_destroy(&payload->mode); 47 | attribute_onclick_destroy(&payload->onclick); 48 | attribute_target_destroy(&payload->target); 49 | 50 | } 51 | 52 | static unsigned int button_changestate(struct widget_header *header, unsigned int state) 53 | { 54 | 55 | switch (state) 56 | { 57 | 58 | case WIDGET_STATE_HOVER: 59 | case WIDGET_STATE_UNHOVER: 60 | if (header->state == WIDGET_STATE_FOCUS) 61 | return WIDGET_STATE_FOCUS; 62 | 63 | } 64 | 65 | return state; 66 | 67 | } 68 | 69 | static void choice_create(struct widget_payload_choice *payload) 70 | { 71 | 72 | attribute_label_create(&payload->label, ""); 73 | attribute_mode_create(&payload->mode, ATTRIBUTE_MODE_OFF); 74 | 75 | } 76 | 77 | static void choice_destroy(struct widget_payload_choice *payload) 78 | { 79 | 80 | attribute_label_destroy(&payload->label); 81 | attribute_mode_destroy(&payload->mode); 82 | 83 | } 84 | 85 | static void code_create(struct widget_payload_code *payload) 86 | { 87 | 88 | attribute_label_create(&payload->label, ""); 89 | attribute_link_create(&payload->link, ""); 90 | 91 | } 92 | 93 | static void code_destroy(struct widget_payload_code *payload) 94 | { 95 | 96 | attribute_label_destroy(&payload->label); 97 | attribute_link_destroy(&payload->link); 98 | 99 | } 100 | 101 | static void divider_create(struct widget_payload_divider *payload) 102 | { 103 | 104 | attribute_label_create(&payload->label, ""); 105 | 106 | } 107 | 108 | static void divider_destroy(struct widget_payload_divider *payload) 109 | { 110 | 111 | attribute_label_destroy(&payload->label); 112 | 113 | } 114 | 115 | static void field_create(struct widget_payload_field *payload) 116 | { 117 | 118 | attribute_data_create(&payload->data, ""); 119 | attribute_icon_create(&payload->icon, ATTRIBUTE_ICON_NONE); 120 | attribute_label_create(&payload->label, ""); 121 | attribute_onlinebreak_create(&payload->onlinebreak, FUNCTION_NONE, ""); 122 | attribute_range_create(&payload->range, 1, 1); 123 | attribute_type_create(&payload->type, ATTRIBUTE_TYPE_REGULAR); 124 | 125 | } 126 | 127 | static void field_destroy(struct widget_payload_field *payload) 128 | { 129 | 130 | attribute_data_destroy(&payload->data); 131 | attribute_icon_destroy(&payload->icon); 132 | attribute_label_destroy(&payload->label); 133 | attribute_onlinebreak_destroy(&payload->onlinebreak); 134 | attribute_range_destroy(&payload->range); 135 | attribute_type_destroy(&payload->type); 136 | 137 | } 138 | 139 | static unsigned int field_changestate(struct widget_header *header, unsigned int state) 140 | { 141 | 142 | switch (state) 143 | { 144 | 145 | case WIDGET_STATE_HOVER: 146 | case WIDGET_STATE_UNHOVER: 147 | if (header->state == WIDGET_STATE_FOCUS) 148 | return WIDGET_STATE_FOCUS; 149 | 150 | } 151 | 152 | return state; 153 | 154 | } 155 | 156 | static void header_create(struct widget_payload_header *payload) 157 | { 158 | 159 | attribute_label_create(&payload->label, ""); 160 | 161 | } 162 | 163 | static void header_destroy(struct widget_payload_header *payload) 164 | { 165 | 166 | attribute_label_destroy(&payload->label); 167 | 168 | } 169 | 170 | static void header2_create(struct widget_payload_header2 *payload) 171 | { 172 | 173 | attribute_label_create(&payload->label, ""); 174 | 175 | } 176 | 177 | static void header2_destroy(struct widget_payload_header2 *payload) 178 | { 179 | 180 | attribute_label_destroy(&payload->label); 181 | 182 | } 183 | 184 | static void header3_create(struct widget_payload_header3 *payload) 185 | { 186 | 187 | attribute_label_create(&payload->label, ""); 188 | 189 | } 190 | 191 | static void header3_destroy(struct widget_payload_header3 *payload) 192 | { 193 | 194 | attribute_label_destroy(&payload->label); 195 | 196 | } 197 | 198 | static void image_create(struct widget_payload_image *payload) 199 | { 200 | 201 | attribute_link_create(&payload->link, ""); 202 | 203 | } 204 | 205 | static void image_destroy(struct widget_payload_image *payload) 206 | { 207 | 208 | attribute_link_destroy(&payload->link); 209 | 210 | } 211 | 212 | static void list_create(struct widget_payload_list *payload) 213 | { 214 | 215 | attribute_label_create(&payload->label, ""); 216 | 217 | } 218 | 219 | static void list_destroy(struct widget_payload_list *payload) 220 | { 221 | 222 | attribute_label_destroy(&payload->label); 223 | 224 | } 225 | 226 | static void select_create(struct widget_payload_select *payload) 227 | { 228 | 229 | attribute_data_create(&payload->data, ""); 230 | attribute_label_create(&payload->label, ""); 231 | attribute_range_create(&payload->range, 1, 1); 232 | 233 | } 234 | 235 | static void select_destroy(struct widget_payload_select *payload) 236 | { 237 | 238 | attribute_data_destroy(&payload->data); 239 | attribute_label_destroy(&payload->label); 240 | attribute_range_destroy(&payload->range); 241 | 242 | } 243 | 244 | static unsigned int select_changestate(struct widget_header *header, unsigned int state) 245 | { 246 | 247 | switch (state) 248 | { 249 | 250 | case WIDGET_STATE_HOVER: 251 | case WIDGET_STATE_UNHOVER: 252 | if (header->state == WIDGET_STATE_FOCUS) 253 | return WIDGET_STATE_FOCUS; 254 | 255 | } 256 | 257 | return state; 258 | 259 | } 260 | 261 | static void table_create(struct widget_payload_table *payload) 262 | { 263 | 264 | attribute_grid_create(&payload->grid, ""); 265 | 266 | } 267 | 268 | static void table_destroy(struct widget_payload_table *payload) 269 | { 270 | 271 | attribute_grid_destroy(&payload->grid); 272 | 273 | } 274 | 275 | static void text_create(struct widget_payload_text *payload) 276 | { 277 | 278 | attribute_label_create(&payload->label, ""); 279 | attribute_link_create(&payload->link, ""); 280 | 281 | } 282 | 283 | static void text_destroy(struct widget_payload_text *payload) 284 | { 285 | 286 | attribute_label_destroy(&payload->label); 287 | attribute_link_destroy(&payload->link); 288 | 289 | } 290 | 291 | static void toggle_create(struct widget_payload_toggle *payload) 292 | { 293 | 294 | attribute_label_create(&payload->label, ""); 295 | attribute_mode_create(&payload->mode, ATTRIBUTE_MODE_OFF); 296 | 297 | } 298 | 299 | static void toggle_destroy(struct widget_payload_toggle *payload) 300 | { 301 | 302 | attribute_label_destroy(&payload->label); 303 | attribute_mode_destroy(&payload->mode); 304 | 305 | } 306 | 307 | static unsigned int toggle_changestate(struct widget_header *header, unsigned int state) 308 | { 309 | 310 | switch (state) 311 | { 312 | 313 | case WIDGET_STATE_HOVER: 314 | case WIDGET_STATE_UNHOVER: 315 | if (header->state == WIDGET_STATE_FOCUS) 316 | return WIDGET_STATE_FOCUS; 317 | 318 | } 319 | 320 | return state; 321 | 322 | } 323 | 324 | static void window_create(struct widget_payload_window *payload) 325 | { 326 | 327 | attribute_label_create(&payload->label, ""); 328 | 329 | } 330 | 331 | static void window_destroy(struct widget_payload_window *payload) 332 | { 333 | 334 | attribute_label_destroy(&payload->label); 335 | 336 | } 337 | 338 | unsigned int widget_changestate(struct widget_header *header, unsigned int state) 339 | { 340 | 341 | switch (header->type) 342 | { 343 | 344 | case WIDGET_TYPE_BUTTON: 345 | return header->state = button_changestate(header, state); 346 | 347 | case WIDGET_TYPE_FIELD: 348 | return header->state = field_changestate(header, state); 349 | 350 | case WIDGET_TYPE_SELECT: 351 | return header->state = select_changestate(header, state); 352 | 353 | case WIDGET_TYPE_TOGGLE: 354 | return header->state = toggle_changestate(header, state); 355 | 356 | } 357 | 358 | return header->state = state; 359 | 360 | } 361 | 362 | void widget_header_create(struct widget_header *header, unsigned int type, char *id, char *in) 363 | { 364 | 365 | header->type = type; 366 | header->state = WIDGET_STATE_NORMAL; 367 | 368 | attribute_id_create(&header->id, id); 369 | attribute_in_create(&header->in, in); 370 | 371 | } 372 | 373 | void widget_header_destroy(struct widget_header *header) 374 | { 375 | 376 | attribute_id_destroy(&header->id); 377 | attribute_in_destroy(&header->in); 378 | 379 | } 380 | 381 | void widget_payload_create(union widget_payload *payload, unsigned int type) 382 | { 383 | 384 | switch (type) 385 | { 386 | 387 | case WIDGET_TYPE_ANCHOR: 388 | anchor_create(&payload->anchor); 389 | 390 | break; 391 | 392 | case WIDGET_TYPE_BUTTON: 393 | button_create(&payload->button); 394 | 395 | break; 396 | 397 | case WIDGET_TYPE_CHOICE: 398 | choice_create(&payload->choice); 399 | 400 | break; 401 | 402 | case WIDGET_TYPE_CODE: 403 | code_create(&payload->code); 404 | 405 | break; 406 | 407 | case WIDGET_TYPE_DIVIDER: 408 | divider_create(&payload->divider); 409 | 410 | break; 411 | 412 | case WIDGET_TYPE_FIELD: 413 | field_create(&payload->field); 414 | 415 | break; 416 | 417 | case WIDGET_TYPE_HEADER: 418 | header_create(&payload->header); 419 | 420 | break; 421 | 422 | case WIDGET_TYPE_HEADER2: 423 | header2_create(&payload->header2); 424 | 425 | break; 426 | 427 | case WIDGET_TYPE_HEADER3: 428 | header3_create(&payload->header3); 429 | 430 | break; 431 | 432 | case WIDGET_TYPE_IMAGE: 433 | image_create(&payload->image); 434 | 435 | break; 436 | 437 | case WIDGET_TYPE_LIST: 438 | list_create(&payload->list); 439 | 440 | break; 441 | 442 | case WIDGET_TYPE_SELECT: 443 | select_create(&payload->select); 444 | 445 | break; 446 | 447 | case WIDGET_TYPE_TABLE: 448 | table_create(&payload->table); 449 | 450 | break; 451 | 452 | case WIDGET_TYPE_TEXT: 453 | text_create(&payload->text); 454 | 455 | break; 456 | 457 | case WIDGET_TYPE_TOGGLE: 458 | toggle_create(&payload->toggle); 459 | 460 | break; 461 | 462 | case WIDGET_TYPE_WINDOW: 463 | window_create(&payload->window); 464 | 465 | break; 466 | 467 | } 468 | 469 | } 470 | 471 | void widget_payload_destroy(union widget_payload *payload, unsigned int type) 472 | { 473 | 474 | switch (type) 475 | { 476 | 477 | case WIDGET_TYPE_ANCHOR: 478 | anchor_destroy(&payload->anchor); 479 | 480 | break; 481 | 482 | case WIDGET_TYPE_BUTTON: 483 | button_destroy(&payload->button); 484 | 485 | break; 486 | 487 | case WIDGET_TYPE_CHOICE: 488 | choice_destroy(&payload->choice); 489 | 490 | break; 491 | 492 | case WIDGET_TYPE_CODE: 493 | code_destroy(&payload->code); 494 | 495 | break; 496 | 497 | case WIDGET_TYPE_DIVIDER: 498 | divider_destroy(&payload->divider); 499 | 500 | break; 501 | 502 | case WIDGET_TYPE_FIELD: 503 | field_destroy(&payload->field); 504 | 505 | break; 506 | 507 | case WIDGET_TYPE_HEADER: 508 | header_destroy(&payload->header); 509 | 510 | break; 511 | 512 | case WIDGET_TYPE_HEADER2: 513 | header2_destroy(&payload->header2); 514 | 515 | break; 516 | 517 | case WIDGET_TYPE_HEADER3: 518 | header3_destroy(&payload->header3); 519 | 520 | break; 521 | 522 | case WIDGET_TYPE_IMAGE: 523 | image_destroy(&payload->image); 524 | 525 | break; 526 | 527 | case WIDGET_TYPE_LIST: 528 | list_destroy(&payload->list); 529 | 530 | break; 531 | 532 | case WIDGET_TYPE_SELECT: 533 | select_destroy(&payload->select); 534 | 535 | break; 536 | 537 | case WIDGET_TYPE_TABLE: 538 | table_destroy(&payload->table); 539 | 540 | break; 541 | 542 | case WIDGET_TYPE_TEXT: 543 | text_destroy(&payload->text); 544 | 545 | break; 546 | 547 | case WIDGET_TYPE_TOGGLE: 548 | toggle_destroy(&payload->toggle); 549 | 550 | break; 551 | 552 | case WIDGET_TYPE_WINDOW: 553 | window_destroy(&payload->window); 554 | 555 | break; 556 | 557 | } 558 | 559 | } 560 | 561 | -------------------------------------------------------------------------------- /src/widget.h: -------------------------------------------------------------------------------- 1 | #define WIDGET_TYPE_ANCHOR 1 2 | #define WIDGET_TYPE_AUDIO 2 3 | #define WIDGET_TYPE_BUTTON 3 4 | #define WIDGET_TYPE_CHOICE 4 5 | #define WIDGET_TYPE_CODE 5 6 | #define WIDGET_TYPE_DATETIME 6 7 | #define WIDGET_TYPE_DIVIDER 7 8 | #define WIDGET_TYPE_FIELD 8 9 | #define WIDGET_TYPE_HEADER 9 10 | #define WIDGET_TYPE_HEADER2 10 11 | #define WIDGET_TYPE_HEADER3 11 12 | #define WIDGET_TYPE_IMAGE 12 13 | #define WIDGET_TYPE_LIST 13 14 | #define WIDGET_TYPE_MAP 14 15 | #define WIDGET_TYPE_RICHTEXT 15 16 | #define WIDGET_TYPE_SELECT 16 17 | #define WIDGET_TYPE_TABLE 17 18 | #define WIDGET_TYPE_TEXT 18 19 | #define WIDGET_TYPE_TOGGLE 19 20 | #define WIDGET_TYPE_VIDEO 20 21 | #define WIDGET_TYPE_WINDOW 21 22 | #define WIDGET_STATE_NORMAL 0 23 | #define WIDGET_STATE_HOVER 1 24 | #define WIDGET_STATE_UNHOVER 2 25 | #define WIDGET_STATE_FOCUS 3 26 | #define WIDGET_STATE_UNFOCUS 4 27 | 28 | struct widget_header 29 | { 30 | 31 | unsigned int type; 32 | unsigned int state; 33 | struct attribute_id id; 34 | struct attribute_in in; 35 | 36 | }; 37 | 38 | struct widget_payload_anchor 39 | { 40 | 41 | struct attribute_label label; 42 | struct attribute_event onclick; 43 | struct attribute_target target; 44 | 45 | }; 46 | 47 | struct widget_payload_audio 48 | { 49 | 50 | struct attribute_link link; 51 | 52 | }; 53 | 54 | struct widget_payload_button 55 | { 56 | 57 | struct attribute_icon icon; 58 | struct attribute_label label; 59 | struct attribute_event onclick; 60 | struct attribute_target target; 61 | struct attribute_mode mode; 62 | 63 | }; 64 | 65 | struct widget_payload_choice 66 | { 67 | 68 | struct attribute_label label; 69 | struct attribute_mode mode; 70 | 71 | }; 72 | 73 | struct widget_payload_code 74 | { 75 | 76 | struct attribute_label label; 77 | struct attribute_link link; 78 | 79 | }; 80 | 81 | struct widget_payload_divider 82 | { 83 | 84 | struct attribute_label label; 85 | 86 | }; 87 | 88 | struct widget_payload_field 89 | { 90 | 91 | struct attribute_data data; 92 | struct attribute_icon icon; 93 | struct attribute_label label; 94 | struct attribute_event onlinebreak; 95 | struct attribute_range range; 96 | struct attribute_type type; 97 | 98 | }; 99 | 100 | struct widget_payload_header 101 | { 102 | 103 | struct attribute_label label; 104 | 105 | }; 106 | 107 | struct widget_payload_header2 108 | { 109 | 110 | struct attribute_label label; 111 | 112 | }; 113 | 114 | struct widget_payload_header3 115 | { 116 | 117 | struct attribute_label label; 118 | 119 | }; 120 | 121 | struct widget_payload_image 122 | { 123 | 124 | struct attribute_link link; 125 | 126 | }; 127 | 128 | struct widget_payload_list 129 | { 130 | 131 | struct attribute_label label; 132 | 133 | }; 134 | 135 | struct widget_payload_select 136 | { 137 | 138 | struct attribute_data data; 139 | struct attribute_label label; 140 | struct attribute_range range; 141 | 142 | }; 143 | 144 | struct widget_payload_table 145 | { 146 | 147 | struct attribute_grid grid; 148 | 149 | }; 150 | 151 | struct widget_payload_text 152 | { 153 | 154 | struct attribute_label label; 155 | struct attribute_link link; 156 | 157 | }; 158 | 159 | struct widget_payload_toggle 160 | { 161 | 162 | struct attribute_label label; 163 | struct attribute_mode mode; 164 | 165 | }; 166 | 167 | struct widget_payload_window 168 | { 169 | 170 | struct attribute_label label; 171 | 172 | }; 173 | 174 | struct widget_payload_video 175 | { 176 | 177 | struct attribute_link link; 178 | 179 | }; 180 | 181 | union widget_payload 182 | { 183 | 184 | struct widget_payload_anchor anchor; 185 | struct widget_payload_audio audio; 186 | struct widget_payload_button button; 187 | struct widget_payload_choice choice; 188 | struct widget_payload_code code; 189 | struct widget_payload_divider divider; 190 | struct widget_payload_field field; 191 | struct widget_payload_header header; 192 | struct widget_payload_header2 header2; 193 | struct widget_payload_header3 header3; 194 | struct widget_payload_image image; 195 | struct widget_payload_list list; 196 | struct widget_payload_select select; 197 | struct widget_payload_table table; 198 | struct widget_payload_text text; 199 | struct widget_payload_toggle toggle; 200 | struct widget_payload_video video; 201 | struct widget_payload_window window; 202 | 203 | }; 204 | 205 | struct widget 206 | { 207 | 208 | unsigned int index; 209 | struct list_item item; 210 | struct widget_header header; 211 | union widget_payload payload; 212 | 213 | }; 214 | 215 | unsigned int widget_changestate(struct widget_header *header, unsigned int state); 216 | void widget_header_create(struct widget_header *header, unsigned int type, char *id, char *in); 217 | void widget_header_destroy(struct widget_header *header); 218 | void widget_payload_create(union widget_payload *payload, unsigned int type); 219 | void widget_payload_destroy(union widget_payload *payload, unsigned int type); 220 | --------------------------------------------------------------------------------