From 63063136a0daaacf76cf16d5663cc39325a126d8 Mon Sep 17 00:00:00 2001
From: "yu.dongliang" <18588496441@163.com>
Date: Thu, 19 Sep 2024 12:57:10 +0800
Subject: [PATCH] 1st commit for ABC (advanced browser core)
---
LICENSE | 165 ++++++++++++
README.md | 52 ++++
examples/1.html | 7 +
examples/2.html | 7 +
html/Makefile | 17 ++
html/abc_html.c | 563 +++++++++++++++++++++++++++++++++++++++++
html/abc_html.h | 43 ++++
html/abc_html_util.c | 87 +++++++
html/abc_obj.c | 144 +++++++++++
html/abc_obj.h | 70 +++++
html/main.c | 31 +++
ui/Makefile | 40 +++
ui/abc.h | 75 ++++++
ui/abc_layout.c | 86 +++++++
ui/abc_layout_a.c | 52 ++++
ui/abc_layout_body.c | 6 +
ui/abc_layout_div.c | 6 +
ui/abc_layout_h1.c | 52 ++++
ui/abc_layout_head.c | 6 +
ui/abc_layout_html.c | 6 +
ui/abc_layout_title.c | 6 +
ui/abc_render.c | 231 +++++++++++++++++
ui/abc_render_a.c | 190 ++++++++++++++
ui/abc_render_a_href.c | 215 ++++++++++++++++
ui/abc_render_body.c | 19 ++
ui/abc_render_div.c | 19 ++
ui/abc_render_h1.c | 179 +++++++++++++
ui/abc_render_head.c | 19 ++
ui/abc_render_html.c | 19 ++
ui/abc_render_title.c | 19 ++
ui/abc_ui.glade | 157 ++++++++++++
ui/main.c | 348 +++++++++++++++++++++++++
util/scf_def.h | 73 ++++++
util/scf_list.h | 81 ++++++
util/scf_stack.h | 60 +++++
util/scf_string.c | 390 ++++++++++++++++++++++++++++
util/scf_string.h | 48 ++++
util/scf_vector.h | 212 ++++++++++++++++
38 files changed, 3800 insertions(+)
create mode 100644 LICENSE
create mode 100644 README.md
create mode 100644 examples/1.html
create mode 100644 examples/2.html
create mode 100644 html/Makefile
create mode 100644 html/abc_html.c
create mode 100644 html/abc_html.h
create mode 100644 html/abc_html_util.c
create mode 100644 html/abc_obj.c
create mode 100644 html/abc_obj.h
create mode 100644 html/main.c
create mode 100644 ui/Makefile
create mode 100644 ui/abc.h
create mode 100644 ui/abc_layout.c
create mode 100644 ui/abc_layout_a.c
create mode 100644 ui/abc_layout_body.c
create mode 100644 ui/abc_layout_div.c
create mode 100644 ui/abc_layout_h1.c
create mode 100644 ui/abc_layout_head.c
create mode 100644 ui/abc_layout_html.c
create mode 100644 ui/abc_layout_title.c
create mode 100644 ui/abc_render.c
create mode 100644 ui/abc_render_a.c
create mode 100644 ui/abc_render_a_href.c
create mode 100644 ui/abc_render_body.c
create mode 100644 ui/abc_render_div.c
create mode 100644 ui/abc_render_h1.c
create mode 100644 ui/abc_render_head.c
create mode 100644 ui/abc_render_html.c
create mode 100644 ui/abc_render_title.c
create mode 100644 ui/abc_ui.glade
create mode 100644 ui/main.c
create mode 100644 util/scf_def.h
create mode 100644 util/scf_list.h
create mode 100644 util/scf_stack.h
create mode 100644 util/scf_string.c
create mode 100644 util/scf_string.h
create mode 100644 util/scf_vector.h
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..65c5ca8
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..42649ca
--- /dev/null
+++ b/README.md
@@ -0,0 +1,52 @@
+# abc, Advanced Browser Core
+-------
+#### ä»ç»
+ä¸ä¸ªèªå·±ç¼åçæµè§å¨æ¡æ¶,
+
+1ï¼ä½¿ç¨GTKå为主çé¢,
+
+2ï¼ä½¿ç¨cairoå为2Dç¢éå¾å½¢åº,
+
+3, 使ç¨OpenGL渲æHTML对象模å,
+
+4ï¼è¿è¡å¨Linuxç³»ç»ä¸,
+
+a browser core written by me.
+
+1ï¼GUI based on GTK,
+
+2ï¼cairo for 2D drawing,
+
+3, OpenGL for rendering HTML Object Model,
+
+4, run in Linux,
+
+------
+#### 软件æ¶æ
+
+#### å®è£
æç¨, usage
+
+1. ç¨gitä¸è½½å°æ¬æºï¼å½ä»¤ä¸ºï¼
+
+git clone http://baseworks.info/gitweb/abc.git
+
+download it with git, command above.
+
+2. å¨abc/uiç®å½ä¸ç´æ¥æ§è¡makeï¼å³å¯è·å¾ç¼è¯å¨çå¯æ§è¡æ件a.outï¼å½ä»¤ä¸º:
+
+cd abc/ui
+
+make
+
+in directory abc/ui run make, then get the executable file named 'a.out'.
+
+3. è¿è¡abc/examplesç®å½ä¸çhtmlæµè¯æ ·ä¾ï¼å³å¯çå°è¿è¡ææï¼ä¾å¦:
+
+./a.out ../examples/1.html
+
+ç»èå¨æºç é:(
+more details in source code.
+
+#### åä¸è´¡ç®
+
+yu.dongliang (åºå±ææ¯æ )
diff --git a/examples/1.html b/examples/1.html
new file mode 100644
index 0000000..e73bc36
--- /dev/null
+++ b/examples/1.html
@@ -0,0 +1,7 @@
+
+
test
+
+html test 1
+test2
+
+
diff --git a/examples/2.html b/examples/2.html
new file mode 100644
index 0000000..472f03f
--- /dev/null
+++ b/examples/2.html
@@ -0,0 +1,7 @@
+
+test
+
+html test 2
+test1
+
+
diff --git a/html/Makefile b/html/Makefile
new file mode 100644
index 0000000..2e9a348
--- /dev/null
+++ b/html/Makefile
@@ -0,0 +1,17 @@
+CFILES += ../util/scf_string.c
+
+CFILES += main.c
+CFILES += abc_obj.c
+CFILES += abc_html.c
+CFILES += abc_html_util.c
+
+CFLAGS += -g
+CFLAGS += -I../util
+
+LDFLAGS += -ldl
+
+all:
+ gcc $(CFLAGS) $(CFILES) $(LDFLAGS)
+
+clean:
+ rm *.o
diff --git a/html/abc_html.c b/html/abc_html.c
new file mode 100644
index 0000000..1c4791e
--- /dev/null
+++ b/html/abc_html.c
@@ -0,0 +1,563 @@
+#include"abc_html.h"
+
+typedef struct {
+ char* name;
+ char* value;
+ int type;
+} html_attr_t;
+
+typedef struct {
+ char* name;
+ int type;
+
+ int n_attrs;
+ html_attr_t* attrs;
+} html_label_t;
+
+
+static html_attr_t h1_attrs[] =
+{
+ {"font", "Liberation Serif", ABC_HTML_ATTR_FONT},
+ {"font-size", "32", ABC_HTML_ATTR_FONT_SIZE},
+};
+
+static html_attr_t a_attrs[] =
+{
+ {"href", "", ABC_HTML_ATTR_HREF},
+ {"font", "serif", ABC_HTML_ATTR_FONT},
+ {"font-size", "28", ABC_HTML_ATTR_FONT_SIZE},
+ {"font-color", "blue", ABC_HTML_ATTR_FONT_COLOR},
+};
+
+static html_label_t html_labels[] =
+{
+ {"html", ABC_HTML, 0, NULL},
+ {"title", ABC_HTML_TITLE, 0, NULL},
+ {"head", ABC_HTML_HEAD, 0, NULL},
+ {"body", ABC_HTML_BODY, 0, NULL},
+
+ {"div", ABC_HTML_DIV, 0, NULL},
+
+ {"h1", ABC_HTML_H1, sizeof(h1_attrs) / sizeof(h1_attrs[0]), h1_attrs},
+ {"a", ABC_HTML_A, sizeof(a_attrs) / sizeof(a_attrs[0]), a_attrs},
+};
+
+static int __html_parse_obj(abc_html_t* html, abc_char_t* c);
+
+
+static html_label_t* __html_find_label(const char* name)
+{
+ html_label_t* label;
+
+ int i;
+ for (i = 0; i < sizeof(html_labels) / sizeof(html_labels[0]); i++) {
+ label = &html_labels[i];
+
+ if (!strcmp(label->name, name))
+ return label;
+ }
+
+ return NULL;
+}
+
+static int __html_load_attrs(abc_obj_t* obj, html_attr_t* attrs, int n_attrs)
+{
+ abc_obj_t* attr;
+
+ int i;
+ for (i = 0; i < n_attrs; i++) {
+
+ attr = abc_obj_alloc(NULL, 0, 0, attrs[i].type);
+ if (!attr)
+ return -ENOMEM;
+
+ attr->key = scf_string_cstr(attrs[i].name);
+ if (!attr->key) {
+ abc_obj_free(attr);
+ return -ENOMEM;
+ }
+
+ attr->value = scf_string_cstr(attrs[i].value);
+ if (!attr->value) {
+ abc_obj_free(attr);
+ return -ENOMEM;
+ }
+
+ scf_list_add_tail(&obj->attrs, &attr->list);
+ }
+
+ return 0;
+}
+
+int abc_html_open(abc_html_t** pp, const char* path)
+{
+ if (!pp || !path)
+ return -EINVAL;
+
+ abc_html_t* html = calloc(1, sizeof(abc_html_t));
+ if (!html)
+ return -ENOMEM;
+
+ html->fp = fopen(path, "r");
+ if (!html->fp) {
+ char cwd[4096];
+ getcwd(cwd, 4095);
+ scf_loge("path: %s, errno: %d, pwd: %s\n", path, errno, cwd);
+
+ free(html);
+ return -1;
+ }
+
+ html->file = scf_string_cstr(path);
+ if (!html->file) {
+ fclose(html->fp);
+ free(html);
+ return -ENOMEM;
+ }
+
+ html->n_lines = 1;
+
+ *pp = html;
+ return 0;
+}
+
+void abc_html_close(abc_html_t* html)
+{
+ abc_char_t* c;
+
+ if (html) {
+ while ( html->tmp_list) {
+ c = html->tmp_list;
+
+ html->tmp_list = c->next;
+ free(c);
+ }
+
+ if (html->root)
+ abc_obj_free(html->root);
+
+ if (html->fp)
+ fclose(html->fp);
+
+ if (html->file)
+ scf_string_free(html->file);
+
+ free(html);
+ }
+}
+
+static int __html_parse_text(abc_html_t* html, abc_obj_t* obj)
+{
+ scf_string_t* text = scf_string_alloc();
+ if (!text)
+ return -ENOMEM;
+
+ abc_char_t* c;
+ abc_char_t* c2;
+
+ while (1) {
+ c = __html_pop_char(html);
+ if (!c) {
+ scf_loge("\n");
+ scf_string_free(text);
+ return -1;
+ }
+
+ html->pos += c->len;
+
+ if ('<' == c->c)
+ break;
+
+ if ('\n' == c->c) {
+ html->n_lines++;
+ html->pos = 0;
+ free(c);
+ continue;
+ }
+
+ scf_string_cat_cstr_len(text, c->utf8, c->len);
+ free(c);
+ c = NULL;
+ }
+
+ c2 = __html_pop_char(html);
+ if (!c2) {
+ scf_loge("\n");
+ free(c);
+ scf_string_free(text);
+ return -1;
+ }
+
+ if ('a' <= c2->c && c2->c <= 'z') {
+ free(c);
+ c = NULL;
+ scf_string_free(text);
+
+ int ret = __html_parse_obj(html, c2);
+ if (ret < 0) {
+ scf_loge("\n");
+ return ret;
+ }
+
+ return __html_parse_text(html, obj);
+
+ } else if ('/' != c2->c) {
+ free(c);
+ c = NULL;
+ scf_string_free(text);
+
+ scf_loge("invalid char '%c:%#x' in HTML attribute, file: %s, line: %d\n",
+ c2->c, c2->c, html->file->data, html->n_lines);
+
+ free(c2);
+ return -1;
+ }
+
+ html->pos++;
+
+ free(c2);
+ free(c);
+ c2 = NULL;
+ c = NULL;
+
+ if (text->len > 0)
+ obj->text = text;
+ else
+ scf_string_free(text);
+ text = NULL;
+
+ // check the end label
+ scf_string_t* end = scf_string_alloc();
+ if (!end)
+ return -ENOMEM;
+
+ while (1) {
+ c = __html_pop_char(html);
+ if (!c) {
+ scf_loge("\n");
+ scf_string_free(end);
+ return -1;
+ }
+
+ html->pos += c->len;
+
+ if ('>' == c->c)
+ break;
+
+ scf_string_cat_cstr_len(end, c->utf8, c->len);
+ free(c);
+ c = NULL;
+ }
+
+ html->pos++;
+
+ free(c);
+ c = NULL;
+
+ int ret = 0;
+ if (scf_string_cmp(obj->key, end)) {
+ ret = -1;
+ scf_loge("end label '%s' file: %s, line: %d, NOT for label '%s' line: %d\n",
+ end->data, html->file->data, html->n_lines, obj->key->data, obj->line);
+ }
+
+ scf_string_free(end);
+ end = NULL;
+
+ return ret;
+}
+
+static int __html_parse_value(abc_html_t* html, abc_obj_t* attr)
+{
+ scf_string_t* value = scf_string_alloc();
+ if (!value)
+ return -ENOMEM;
+
+ abc_char_t* c = NULL;
+
+ int flag = 0;
+
+ while (1) {
+ c = __html_pop_char(html);
+ if (!c) {
+ scf_loge("\n");
+ scf_string_free(value);
+ return -1;
+ }
+
+ html->pos += c->len;
+
+ if ((!flag && ' ' == c->c) || '>' == c->c)
+ break;
+
+ if ('\"' == c->c)
+ flag = !flag;
+ else
+ scf_string_cat_cstr_len(value, c->utf8, c->len);
+
+ free(c);
+ c = NULL;
+ }
+
+ int tmp = c->c;
+
+ free(c);
+ c = NULL;
+
+ if (attr->value)
+ scf_string_free(attr->value);
+
+ attr->value = value;
+ return '>' == tmp;
+}
+
+static int __html_parse_attr2(abc_html_t* html, abc_obj_t* obj, const html_attr_t* attrs, int n_attrs)
+{
+ scf_string_t* key = scf_string_alloc();
+ if (!key)
+ return -ENOMEM;
+
+ abc_char_t* c = NULL;
+
+ while (1) {
+ c = __html_pop_char(html);
+ if (!c) {
+ scf_loge("\n");
+ scf_string_free(key);
+ return -1;
+ }
+
+ html->pos += c->len;
+
+ if ('=' == c->c)
+ break;
+
+ if ('_' == c->c
+ || '-' == c->c
+ || ('a' <= c->c && 'z' >= c->c))
+ scf_string_cat_cstr_len(key, c->utf8, c->len);
+ else {
+ scf_loge("invalid char in HTML attribute, file: %s, line: %d\n",
+ html->file->data, html->n_lines);
+ free(c);
+ scf_string_free(key);
+ return -1;
+ }
+
+ free(c);
+ c = NULL;
+ }
+
+ int tmp = c->c;
+
+ free(c);
+ c = NULL;
+
+ scf_list_t* l;
+ abc_obj_t* attr;
+
+ for (l = scf_list_head(&obj->attrs); l != scf_list_sentinel(&obj->attrs); l = scf_list_next(l)) {
+ attr = scf_list_data(l, abc_obj_t, list);
+
+ if (!strcmp(attr->key->data, key->data))
+ break;
+ }
+
+ if (l == scf_list_sentinel(&obj->attrs)) {
+
+ scf_loge("invalid HTML attribute '%s' in file: %s, line: %d\n", key->data, html->file->data, html->n_lines);
+ scf_string_free(key);
+ return -1;
+ }
+
+ scf_string_free(key);
+ key = NULL;
+
+ assert(!attr->file);
+
+ attr->file = scf_string_clone(html->file);
+ if (!attr->file)
+ return -ENOMEM;
+
+ attr->line = html->n_lines;
+ attr->pos = html->pos;
+
+ return __html_parse_value(html, attr);
+}
+
+static int __html_parse_attr(abc_html_t* html, abc_obj_t* obj, const html_attr_t* attrs, int n_attrs)
+{
+ int ret = 0;
+
+ while (!ret) {
+ ret = __html_parse_attr2(html, obj, attrs, n_attrs);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __html_parse_obj(abc_html_t* html, abc_char_t* c)
+{
+ html_label_t* label;
+ abc_obj_t* obj;
+
+ scf_string_t* key = scf_string_cstr_len(c->utf8, c->len);
+
+ html->pos += c->len;
+ free(c);
+ c = NULL;
+
+ if (!key)
+ return -ENOMEM;
+
+ while (1) {
+ c = __html_pop_char(html);
+ if (!c) {
+ scf_loge("\n");
+ scf_string_free(key);
+ return -1;
+ }
+
+ html->pos += c->len;
+
+ if (' ' == c->c || '>' == c->c)
+ break;
+
+ if ('\n' == c->c) {
+ scf_loge("%c:%#x, html->n_lines: %d, pos: %d\n", c->c, c->c, html->n_lines, html->pos);
+ free(c);
+ return -1;
+ }
+
+ scf_string_cat_cstr_len(key, c->utf8, c->len);
+ free(c);
+ c = NULL;
+ }
+
+ int tmp = c->c;
+
+ free(c);
+ c = NULL;
+
+ label = __html_find_label(key->data);
+ if (!label) {
+ scf_loge("invalid HTML label '%s' in file: %s, line: %d\n",
+ key->data, html->file->data, html->n_lines);
+ scf_string_free(key);
+ return -1;
+ }
+
+ obj = abc_obj_alloc(html->file, html->n_lines, html->pos, label->type);
+ if (!obj) {
+ scf_string_free(key);
+ return -ENOMEM;
+ }
+
+ obj->key = key;
+ key = NULL;
+
+ int ret = __html_load_attrs(obj, label->attrs, label->n_attrs);
+ if (ret < 0) {
+ abc_obj_free(obj);
+ return ret;
+ }
+
+ if (' ' == tmp) {
+ ret = __html_parse_attr(html, obj, label->attrs, label->n_attrs);
+ if (ret < 0) {
+ abc_obj_free(obj);
+ return ret;
+ }
+ }
+
+ scf_logi("key: %s\n", obj->key->data);
+
+ if (!html->root) {
+ html->root = obj;
+ html->current = obj;
+ } else {
+ assert(html->current);
+
+ scf_list_add_tail(&html->current->childs, &obj->list);
+
+ obj->parent = html->current;
+ html->current = obj;
+ }
+
+ ret = __html_parse_text(html, obj);
+
+ html->current = obj->parent;
+ return ret;
+}
+
+int abc_html_parse(abc_html_t* html)
+{
+ abc_char_t* c;
+
+ while (1) {
+ c = __html_pop_char(html);
+ if (!c)
+ return -1;
+
+ if (EOF == c->c) {
+ free(c);
+ html->current = NULL;
+ return 0;
+ }
+
+ if ('\n' == c->c) {
+ html->n_lines++;
+ html->pos = 0;
+ continue;
+ }
+
+ if ('<' != c->c) {
+ scf_loge("%c:%#x, html->n_lines: %d, pos: %d\n", c->c, c->c, html->n_lines, html->pos);
+ free(c);
+ return -1;
+ }
+
+ free(c);
+
+ c = __html_pop_char(html);
+ if ('!' == c->c) { //
+ free(c);
+
+ while (1) {
+ c = __html_pop_char(html);
+
+ int tmp = c->c;
+ free(c);
+
+ if ('>' == tmp)
+ break;
+
+ if ('\n' == tmp) {
+ scf_loge("%c:%#x, html->n_lines: %d, pos: %d\n", tmp, tmp, html->n_lines, html->pos);
+ return -1;
+ }
+ }
+
+ c = NULL;
+ continue;
+ }
+
+ if ('a' <= c->c && 'z' >= c->c) {
+
+ int ret = __html_parse_obj(html, c);
+ if (ret < 0) {
+ scf_loge("html->n_lines: %d, pos: %d\n", html->n_lines, html->pos);
+ return ret;
+ }
+
+ } else {
+ scf_loge("%c:%#x, html->n_lines: %d, pos: %d\n", c->c, c->c, html->n_lines, html->pos);
+ free(c);
+ return -1;
+ }
+ }
+
+ return -1;
+}
diff --git a/html/abc_html.h b/html/abc_html.h
new file mode 100644
index 0000000..c335f48
--- /dev/null
+++ b/html/abc_html.h
@@ -0,0 +1,43 @@
+#ifndef ABC_HTML_H
+#define ABC_HTML_H
+
+#include"abc_obj.h"
+
+typedef struct abc_char_s abc_char_t;
+typedef struct abc_html_s abc_html_t;
+
+#define ABC_UTF8_MAX 6
+struct abc_char_s
+{
+ abc_char_t* next;
+ int c;
+
+ int len;
+ uint8_t utf8[ABC_UTF8_MAX];
+};
+
+struct abc_html_s
+{
+ scf_list_t list;
+
+ abc_char_t* tmp_list;
+
+ abc_obj_t* root;
+ abc_obj_t* current;
+
+ FILE* fp;
+
+ scf_string_t* file;
+ int n_lines;
+ int pos;
+};
+
+int abc_html_open (abc_html_t** pp, const char* path);
+void abc_html_close(abc_html_t* html);
+
+int abc_html_parse(abc_html_t* html);
+
+abc_char_t* __html_pop_char (abc_html_t* html);
+void __html_push_char(abc_html_t* html, abc_char_t* c);
+
+#endif
diff --git a/html/abc_html_util.c b/html/abc_html_util.c
new file mode 100644
index 0000000..5053387
--- /dev/null
+++ b/html/abc_html_util.c
@@ -0,0 +1,87 @@
+#include"abc_html.h"
+
+abc_char_t* __html_pop_char(abc_html_t* html)
+{
+ assert(html);
+ assert(html->fp);
+
+ abc_char_t* c;
+
+ if (html->tmp_list) {
+ c = html->tmp_list;
+ html->tmp_list = c->next;
+ return c;
+ }
+
+ c = malloc(sizeof(abc_char_t));
+ if (!c)
+ return NULL;
+
+ int ret = fgetc(html->fp);
+ if (EOF == ret) {
+ c->c = ret;
+ return c;
+ }
+
+ if (ret < 0x80) {
+ c->c = ret;
+ c->len = 1;
+ c->utf8[0] = ret;
+ return c;
+ }
+
+ if (0x6 == (ret >> 5)) {
+ c->c = ret & 0x1f;
+ c->len = 2;
+
+ } else if (0xe == (ret >> 4)) {
+ c->c = ret & 0xf;
+ c->len = 3;
+
+ } else if (0x1e == (ret >> 3)) {
+ c->c = ret & 0x7;
+ c->len = 4;
+
+ } else if (0x3e == (ret >> 2)) {
+ c->c = ret & 0x3;
+ c->len = 5;
+
+ } else if (0x7e == (ret >> 1)) {
+ c->c = ret & 0x1;
+ c->len = 6;
+ } else {
+ scf_loge("utf8 first byte wrong %#x, file: %s, line: %d\n", ret, html->file->data, html->n_lines);
+ free(c);
+ return NULL;
+ }
+
+ c->utf8[0] = ret;
+
+ int i;
+ for (i = 1; i < c->len; i++) {
+
+ ret = fgetc(html->fp);
+
+ if (0x2 == (ret >> 6)) {
+ c->c <<= 6;
+ c->c |= ret & 0x3f;
+
+ c->utf8[i] = ret;
+ } else {
+ scf_loge("utf8 byte[%d] wrong %#x, file: %s, line: %d\n", i + 1, ret, html->file->data, html->n_lines);
+ free(c);
+ return NULL;
+ }
+ }
+
+ return c;
+}
+
+void __html_push_char(abc_html_t* html, abc_char_t* c)
+{
+ assert(html);
+ assert(c);
+
+ c->next = html->tmp_list;
+ html->tmp_list = c;
+}
diff --git a/html/abc_obj.c b/html/abc_obj.c
new file mode 100644
index 0000000..f92dd19
--- /dev/null
+++ b/html/abc_obj.c
@@ -0,0 +1,144 @@
+#include"abc_obj.h"
+
+abc_obj_t* abc_obj_alloc(scf_string_t* file, int line, int pos, int type)
+{
+ abc_obj_t* obj = calloc(1, sizeof(abc_obj_t));
+ if (!obj)
+ return NULL;
+
+ if (file) {
+ obj->file = scf_string_clone(file);
+
+ if (!obj->file) {
+ free(obj);
+ return NULL;
+ }
+ }
+
+ scf_list_init(&obj->list);
+ scf_list_init(&obj->attrs);
+ scf_list_init(&obj->childs);
+
+ obj->type = type;
+
+ obj->line = line;
+ obj->pos = pos;
+
+ return obj;
+}
+
+void abc_obj_free(abc_obj_t* obj)
+{
+ if (obj) {
+ if (obj->key)
+ scf_string_free(obj->key);
+
+ if (obj->value)
+ scf_string_free(obj->value);
+
+ if (obj->file)
+ scf_string_free(obj->file);
+
+ scf_list_clear(&obj->attrs, abc_obj_t, list, abc_obj_free);
+ scf_list_clear(&obj->childs, abc_obj_t, list, abc_obj_free);
+
+ free(obj);
+ }
+}
+
+abc_obj_t* abc_obj_find(abc_obj_t* root, int x, int y)
+{
+ scf_list_t* l;
+ abc_obj_t* child;
+ abc_obj_t* obj;
+
+ if (x < root->x
+ || x > root->x + root->w
+ || y < root->y
+ || y > root->y + root->h)
+ return NULL;
+
+ for (l = scf_list_head(&root->childs); l != scf_list_sentinel(&root->childs); l = scf_list_next(l)) {
+ child = scf_list_data(l, abc_obj_t, list);
+
+ obj = abc_obj_find(child, x, y);
+ if (obj)
+ return obj;
+ }
+
+ return root;
+}
+
+int abc_obj_set_attr(abc_obj_t* obj, int key, const char* value)
+{
+ scf_string_t* s;
+ scf_list_t* l;
+ abc_obj_t* attr;
+
+ for (l = scf_list_head(&obj->attrs); l != scf_list_sentinel(&obj->attrs); l = scf_list_next(l)) {
+ attr = scf_list_data(l, abc_obj_t, list);
+
+ if (attr->type == key) {
+ s = scf_string_cstr(value);
+ if (s) {
+ scf_string_free(attr->value);
+ attr->value = s;
+ return 0;
+ }
+ return -ENOMEM;
+ }
+ }
+
+ return -EINVAL;
+}
+
+abc_obj_t* abc_obj_get_attr(abc_obj_t* obj, int key)
+{
+ scf_list_t* l;
+ abc_obj_t* attr;
+
+ for (l = scf_list_head(&obj->attrs); l != scf_list_sentinel(&obj->attrs); l = scf_list_next(l)) {
+ attr = scf_list_data(l, abc_obj_t, list);
+
+ if (attr->type == key)
+ return attr;
+ }
+
+ return NULL;
+}
+
+void abc_obj_print(abc_obj_t* obj)
+{
+ scf_list_t* l;
+ abc_obj_t* attr;
+ abc_obj_t* child;
+
+ if (!obj)
+ return;
+
+ if (obj->value)
+ printf(" %s=%s", obj->key->data, obj->value->data);
+ else
+ printf("<%s", obj->key->data);
+
+ for (l = scf_list_head(&obj->attrs); l != scf_list_sentinel(&obj->attrs); l = scf_list_next(l)) {
+ attr = scf_list_data(l, abc_obj_t, list);
+
+ abc_obj_print(attr);
+ }
+
+ if (!obj->value)
+ printf(">\n");
+
+ for (l = scf_list_head(&obj->childs); l != scf_list_sentinel(&obj->childs); l = scf_list_next(l)) {
+ child = scf_list_data(l, abc_obj_t, list);
+
+ abc_obj_print(child);
+ }
+
+ if (obj->text)
+ printf("%s\n", obj->text->data);
+
+ if (!obj->value)
+ printf("%s>\n", obj->key->data);
+}
diff --git a/html/abc_obj.h b/html/abc_obj.h
new file mode 100644
index 0000000..431e6f4
--- /dev/null
+++ b/html/abc_obj.h
@@ -0,0 +1,70 @@
+#ifndef ABC_OBJ_H
+#define ABC_OBJ_H
+
+#include"scf_string.h"
+#include"scf_list.h"
+
+typedef struct abc_obj_s abc_obj_t;
+
+enum abc_objs
+{
+ ABC_HTML,
+ ABC_HTML_TITLE,
+ ABC_HTML_HEAD,
+ ABC_HTML_BODY,
+
+ // 4
+ ABC_HTML_DIV,
+ ABC_HTML_H1,
+ ABC_HTML_A,
+ ABC_HTML_A_HREF,
+
+ ABC_HTML_NB, // total HTML objects
+
+ ABC_HTML_ATTR_ID,
+ ABC_HTML_ATTR_HREF,
+
+ ABC_HTML_ATTR_FONT,
+ ABC_HTML_ATTR_FONT_SIZE,
+ ABC_HTML_ATTR_FONT_COLOR,
+};
+
+struct abc_obj_s
+{
+ scf_list_t list;
+
+ scf_list_t attrs;
+ scf_list_t childs;
+
+ abc_obj_t* parent;
+
+ void* gtk_builder;
+
+ int type;
+
+ int x;
+ int y;
+ int w;
+ int h;
+
+ scf_string_t* key;
+ scf_string_t* value;
+ scf_string_t* text;
+
+ scf_string_t* file; // file name
+ int line; // line
+ int pos; // position
+
+ uint8_t move_flag :1;
+ uint8_t click_flag:1;
+};
+
+abc_obj_t* abc_obj_alloc(scf_string_t* file, int line, int pos, int type);
+void abc_obj_free (abc_obj_t* obj);
+abc_obj_t* abc_obj_find (abc_obj_t* root, int x, int y);
+void abc_obj_print(abc_obj_t* obj);
+
+int abc_obj_set_attr(abc_obj_t* obj, int key, const char* value);
+abc_obj_t* abc_obj_get_attr(abc_obj_t* obj, int key);
+
+#endif
diff --git a/html/main.c b/html/main.c
new file mode 100644
index 0000000..b8459d1
--- /dev/null
+++ b/html/main.c
@@ -0,0 +1,31 @@
+#include"abc_html.h"
+
+int main(int argc, char* argv[])
+{
+ abc_html_t* html = NULL;
+
+ if (argc < 2) {
+ scf_loge("\n");
+ return -1;
+ }
+
+ if (abc_html_open(&html, argv[1]) < 0) {
+ scf_loge("\n");
+ return -1;
+ }
+
+ if (abc_html_parse(html) < 0) {
+ scf_loge("\n");
+ return -1;
+ }
+
+ printf("\n");
+
+ if (html->root)
+ abc_obj_print(html->root);
+
+ printf("\n");
+
+ scf_logi("main ok\n");
+ return 0;
+}
diff --git a/ui/Makefile b/ui/Makefile
new file mode 100644
index 0000000..b06f22c
--- /dev/null
+++ b/ui/Makefile
@@ -0,0 +1,40 @@
+CFILES += main.c
+
+CFILES += abc_layout.c
+CFILES += abc_layout_html.c
+CFILES += abc_layout_title.c
+CFILES += abc_layout_head.c
+CFILES += abc_layout_body.c
+CFILES += abc_layout_div.c
+CFILES += abc_layout_h1.c
+CFILES += abc_layout_a.c
+
+CFILES += abc_render.c
+CFILES += abc_render_html.c
+CFILES += abc_render_title.c
+CFILES += abc_render_head.c
+CFILES += abc_render_body.c
+CFILES += abc_render_div.c
+CFILES += abc_render_h1.c
+CFILES += abc_render_a.c
+CFILES += abc_render_a_href.c
+
+CFILES += ../html/abc_html.c
+CFILES += ../html/abc_html_util.c
+CFILES += ../html/abc_obj.c
+
+CFILES += ../util/scf_string.c
+
+CFLAGS += -g -O3
+CFLAGS += -I./
+CFLAGS += -I../html
+CFLAGS += -I../util
+CFLAGS += `pkg-config --cflags gtk+-3.0`
+
+LDFLAGS += -lavformat -lavcodec -lavfilter -lavutil
+LDFLAGS += -lm
+LDFLAGS += -lGL
+LDFLAGS += `pkg-config --libs gtk+-3.0`
+
+all:
+ gcc $(CFLAGS) $(CFILES) $(LDFLAGS)
diff --git a/ui/abc.h b/ui/abc.h
new file mode 100644
index 0000000..347e88b
--- /dev/null
+++ b/ui/abc.h
@@ -0,0 +1,75 @@
+#ifndef ABC_H
+#define ABC_H
+
+#include"abc_html.h"
+
+#include
+
+#define GL_GLEXT_PROTOTYPES
+#include
+#include
+
+typedef struct abc_render_s abc_render_t;
+typedef struct abc_layout_s abc_layout_t;
+typedef struct abc_ctx_s abc_ctx_t;
+
+typedef int (*abc_layout_pt)(abc_layout_t* layout, abc_obj_t* obj, int width, int height);
+
+struct abc_layout_s
+{
+ void *priv;
+};
+
+struct abc_render_s
+{
+ int type;
+
+ int (*draw)(abc_render_t* render, abc_obj_t* obj, int width, int height);
+ int (*fini)(abc_render_t* render);
+
+ void *priv;
+};
+
+struct abc_ctx_s
+{
+ scf_list_t html_list;
+ abc_html_t* current;
+
+ GtkBuilder* builder;
+
+ GtkGLArea* gl_area;
+
+ GtkButton* back;
+ GtkButton* forward;
+ GtkButton* refresh;
+};
+
+#define ABC_CTX_INIT(__ctx) {SCF_LIST_INIT((__ctx).html_list), NULL, NULL, NULL, NULL, NULL, NULL}
+
+static const GLfloat vert_array[] = {
+ -1.0f, -1.0f,
+ 1.0f, -1.0f,
+ -1.0f, 1.0f,
+ 1.0f, 1.0f
+};
+
+static const GLfloat texture_array[] = {
+ 0.0f, 1.0f,
+ 1.0f, 1.0f,
+ 0.0f, 0.0f,
+ 1.0f, 0.0f,
+};
+
+int abc_renders_fini();
+int abc_render_draw(abc_obj_t* obj, int width, int height);
+
+int abc_render_root(abc_ctx_t* ctx, abc_obj_t* root, int width, int height);
+int abc_layout_root(abc_ctx_t* ctx, abc_obj_t* root, int width, int height);
+
+void __compute_mvp(float *res, float phi, float theta, float psi);
+
+int __init_texture(GLuint* ptex, GLenum format, int w, int h, uint8_t* data);
+
+int __init_program(GLuint* pprog, const char* vert_shader, const char* frag_shader);
+
+#endif
diff --git a/ui/abc_layout.c b/ui/abc_layout.c
new file mode 100644
index 0000000..3ebf72c
--- /dev/null
+++ b/ui/abc_layout.c
@@ -0,0 +1,86 @@
+#include"abc.h"
+
+int abc_layout_html (abc_layout_t* layout, abc_obj_t* root, int width, int height);
+int abc_layout_title(abc_layout_t* layout, abc_obj_t* root, int width, int height);
+int abc_layout_head (abc_layout_t* layout, abc_obj_t* root, int width, int height);
+int abc_layout_body (abc_layout_t* layout, abc_obj_t* root, int width, int height);
+
+int abc_layout_div (abc_layout_t* layout, abc_obj_t* root, int width, int height);
+int abc_layout_h1 (abc_layout_t* layout, abc_obj_t* root, int width, int height);
+int abc_layout_a (abc_layout_t* layout, abc_obj_t* root, int width, int height);
+
+static abc_layout_pt abc_layouts[ABC_HTML_NB] =
+{
+ abc_layout_html,
+ abc_layout_title,
+ abc_layout_head,
+ abc_layout_body,
+
+ // 4
+ abc_layout_div,
+ abc_layout_h1,
+ abc_layout_a,
+};
+
+int abc_layout_obj(abc_layout_t* layout, abc_obj_t* obj, int width, int height)
+{
+ if (obj->type >= ABC_HTML_NB)
+ return -EINVAL;
+
+ abc_layout_pt f = abc_layouts[obj->type];
+ if (!f)
+ return -EINVAL;
+
+ return f(layout, obj, width, height);
+}
+
+int abc_layout_root(abc_ctx_t* abc, abc_obj_t* root, int width, int height)
+{
+ scf_list_t* l;
+ abc_obj_t* child;
+
+ if (!root)
+ return -EINVAL;
+
+ if (width <= 0 || height <= 0)
+ return -EINVAL;
+
+ int x = 4;
+ int y = 4;
+ int w = 0;
+ int h = 0;
+
+ for (l = scf_list_head(&root->childs); l != scf_list_sentinel(&root->childs); l = scf_list_next(l)) {
+ child = scf_list_data(l, abc_obj_t, list);
+
+ int ret = abc_layout_root(NULL, child, width, height);
+ if (ret < 0)
+ return ret;
+
+ child->x = x;
+ child->y = y;
+
+ scf_logd("key: %s, x: %d, y: %d, w: %d, h: %d\n\n", child->key->data, child->x, child->y, child->w, child->h);
+
+ y = child->y + child->h;
+
+ if (w < child->x + child->w)
+ w = child->x + child->w;
+
+ if (h < child->y + child->h)
+ h = child->y + child->h;
+ }
+
+ int ret = abc_layout_obj(NULL, root, width, height);
+ if (ret < 0)
+ return ret;
+
+ if (root->w < w)
+ root->w = w;
+
+ if (root->h < h)
+ root->h = h;
+
+ scf_logd("key: %s, x: %d, y: %d, w: %d, h: %d\n", root->key->data, root->x, root->y, root->w, root->h);
+ return 0;
+}
diff --git a/ui/abc_layout_a.c b/ui/abc_layout_a.c
new file mode 100644
index 0000000..0a917b4
--- /dev/null
+++ b/ui/abc_layout_a.c
@@ -0,0 +1,52 @@
+#include"abc.h"
+
+int abc_layout_a(abc_layout_t* layout, abc_obj_t* obj, int width, int height)
+{
+ cairo_text_extents_t extents;
+ cairo_surface_t* surface;
+ cairo_t* cr;
+
+ scf_list_t* l;
+ abc_obj_t* attr;
+
+ surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
+ cr = cairo_create(surface);
+
+ for (l = scf_list_head(&obj->attrs); l != scf_list_sentinel(&obj->attrs); l = scf_list_next(l)) {
+ attr = scf_list_data(l, abc_obj_t, list);
+
+ switch (attr->type) {
+
+ case ABC_HTML_ATTR_FONT:
+ cairo_select_font_face(cr, attr->value->data, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
+ break;
+
+ case ABC_HTML_ATTR_FONT_SIZE:
+ cairo_set_font_size(cr, atoi(attr->value->data));
+ break;
+ default:
+ break;
+ };
+ }
+
+ cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
+
+ cairo_text_extents(cr, obj->text->data, &extents);
+
+ obj->x = 4;
+ obj->y = 4;
+ obj->w = extents.width + extents.x_bearing;
+ obj->h = extents.height + extents.height / 2;
+
+ obj->w = (obj->w + 3) & ~0x3;
+ obj->h = (obj->h + 3) & ~0x3;
+
+ scf_logd("%s, w: %d, h: %d, x_bearing: %lg, y_bearing: %lg, width: %lg, height: %lg, x_advance: %lg, y_advance: %lg\n",
+ obj->text->data, obj->w, obj->h,
+ extents.x_bearing, extents.y_bearing, extents.width, extents.height,
+ extents.x_advance, extents.y_advance);
+
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+ return 0;
+}
diff --git a/ui/abc_layout_body.c b/ui/abc_layout_body.c
new file mode 100644
index 0000000..f374316
--- /dev/null
+++ b/ui/abc_layout_body.c
@@ -0,0 +1,6 @@
+#include"abc.h"
+
+int abc_layout_body(abc_layout_t* layout, abc_obj_t* obj, int width, int height)
+{
+ return 0;
+}
diff --git a/ui/abc_layout_div.c b/ui/abc_layout_div.c
new file mode 100644
index 0000000..6878cc0
--- /dev/null
+++ b/ui/abc_layout_div.c
@@ -0,0 +1,6 @@
+#include"abc.h"
+
+int abc_layout_div(abc_layout_t* layout, abc_obj_t* obj, int width, int height)
+{
+ return 0;
+}
diff --git a/ui/abc_layout_h1.c b/ui/abc_layout_h1.c
new file mode 100644
index 0000000..e39d3c0
--- /dev/null
+++ b/ui/abc_layout_h1.c
@@ -0,0 +1,52 @@
+#include"abc.h"
+
+int abc_layout_h1(abc_layout_t* layout, abc_obj_t* obj, int width, int height)
+{
+ cairo_text_extents_t extents;
+ cairo_surface_t* surface;
+ cairo_t* cr;
+
+ scf_list_t* l;
+ abc_obj_t* attr;
+
+ surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
+ cr = cairo_create(surface);
+
+ for (l = scf_list_head(&obj->attrs); l != scf_list_sentinel(&obj->attrs); l = scf_list_next(l)) {
+ attr = scf_list_data(l, abc_obj_t, list);
+
+ switch (attr->type) {
+
+ case ABC_HTML_ATTR_FONT:
+ cairo_select_font_face(cr, attr->value->data, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
+ break;
+
+ case ABC_HTML_ATTR_FONT_SIZE:
+ cairo_set_font_size(cr, atoi(attr->value->data));
+ break;
+ default:
+ break;
+ };
+ }
+
+ cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
+
+ cairo_text_extents(cr, obj->text->data, &extents);
+
+ obj->x = 4;
+ obj->y = 4;
+ obj->w = extents.width + extents.x_bearing;
+ obj->h = extents.height + extents.height / 2;
+
+ obj->w = (obj->w + 3) & ~0x3;
+ obj->h = (obj->h + 3) & ~0x3;
+
+ scf_logd("%s, w: %d, h: %d, x_bearing: %lg, y_bearing: %lg, width: %lg, height: %lg, x_advance: %lg, y_advance: %lg\n",
+ obj->text->data, obj->w, obj->h,
+ extents.x_bearing, extents.y_bearing, extents.width, extents.height,
+ extents.x_advance, extents.y_advance);
+
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+ return 0;
+}
diff --git a/ui/abc_layout_head.c b/ui/abc_layout_head.c
new file mode 100644
index 0000000..cb4d6c2
--- /dev/null
+++ b/ui/abc_layout_head.c
@@ -0,0 +1,6 @@
+#include"abc.h"
+
+int abc_layout_head(abc_layout_t* layout, abc_obj_t* obj, int width, int height)
+{
+ return 0;
+}
diff --git a/ui/abc_layout_html.c b/ui/abc_layout_html.c
new file mode 100644
index 0000000..4615d4a
--- /dev/null
+++ b/ui/abc_layout_html.c
@@ -0,0 +1,6 @@
+#include"abc.h"
+
+int abc_layout_html(abc_layout_t* layout, abc_obj_t* obj, int width, int height)
+{
+ return 0;
+}
diff --git a/ui/abc_layout_title.c b/ui/abc_layout_title.c
new file mode 100644
index 0000000..35e1965
--- /dev/null
+++ b/ui/abc_layout_title.c
@@ -0,0 +1,6 @@
+#include"abc.h"
+
+int abc_layout_title(abc_layout_t* layout, abc_obj_t* obj, int width, int height)
+{
+ return 0;
+}
diff --git a/ui/abc_render.c b/ui/abc_render.c
new file mode 100644
index 0000000..d97b6c2
--- /dev/null
+++ b/ui/abc_render.c
@@ -0,0 +1,231 @@
+#include"abc.h"
+
+extern abc_render_t abc_render_html;
+extern abc_render_t abc_render_title;
+extern abc_render_t abc_render_head;
+extern abc_render_t abc_render_body;
+
+extern abc_render_t abc_render_div;
+extern abc_render_t abc_render_h1;
+extern abc_render_t abc_render_a;
+extern abc_render_t abc_render_a_href;
+
+static abc_render_t* abc_renders[ABC_HTML_NB] =
+{
+ &abc_render_html,
+ &abc_render_title,
+ &abc_render_head,
+ &abc_render_body,
+
+ // 4
+ &abc_render_div,
+ &abc_render_h1,
+ &abc_render_a,
+ &abc_render_a_href,
+};
+
+int abc_renders_fini()
+{
+ abc_render_t* render;
+ int i;
+
+ for (i = 0; i < ABC_HTML_NB; i++) {
+ render = abc_renders[i];
+
+ if (render && render->fini) {
+ int ret = render->fini(render);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int abc_render_draw(abc_obj_t* obj, int width, int height)
+{
+ if (obj->type >= ABC_HTML_NB)
+ return -EINVAL;
+
+ abc_render_t* render = abc_renders[obj->type];
+ if (!render)
+ return -EINVAL;
+
+ return render->draw(render, obj, width, height);
+}
+
+static int __render_root(abc_ctx_t* ctx, abc_obj_t* root, int width, int height)
+{
+ scf_list_t* l;
+ abc_obj_t* child;
+
+ int ret = abc_render_draw(root, width, height);
+ if (ret < 0)
+ return ret;
+
+ for (l = scf_list_head(&root->childs); l != scf_list_sentinel(&root->childs); l = scf_list_next(l)) {
+ child = scf_list_data(l, abc_obj_t, list);
+
+ ret = __render_root(ctx, child, width, height);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+int abc_render_root(abc_ctx_t* ctx, abc_obj_t* root, int width, int height)
+{
+ if (!ctx || !root)
+ return -EINVAL;
+
+ if (width <= 0 || height <= 0)
+ return -EINVAL;
+
+ int ret = __render_root(ctx, root, width, height);
+ if (ret < 0)
+ return ret;
+
+ if (ctx->current->current) {
+ if (ABC_HTML_A == ctx->current->current->type) {
+
+ abc_render_t* render = abc_renders[ABC_HTML_A_HREF];
+ if (!render)
+ return -EINVAL;
+
+ ret = render->draw(render, ctx->current->current, width, height);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+void __compute_mvp(float *res, float phi, float theta, float psi)
+{
+ float x = phi * (M_PI / 180.f);
+ float y = theta * (M_PI / 180.f);
+ float z = psi * (M_PI / 180.f);
+ float c1 = cosf (x), s1 = sinf (x);
+ float c2 = cosf (y), s2 = sinf (y);
+ float c3 = cosf (z), s3 = sinf (z);
+ float c3c2 = c3 * c2;
+ float s3c1 = s3 * c1;
+ float c3s2s1 = c3 * s2 * s1;
+ float s3s1 = s3 * s1;
+ float c3s2c1 = c3 * s2 * c1;
+ float s3c2 = s3 * c2;
+ float c3c1 = c3 * c1;
+ float s3s2s1 = s3 * s2 * s1;
+ float c3s1 = c3 * s1;
+ float s3s2c1 = s3 * s2 * c1;
+ float c2s1 = c2 * s1;
+ float c2c1 = c2 * c1;
+
+ /* initialize to the identity matrix */
+ res[0] = 1.f; res[4] = 0.f; res[8] = 0.f; res[12] = 0.f;
+ res[1] = 0.f; res[5] = 1.f; res[9] = 0.f; res[13] = 0.f;
+ res[2] = 0.f; res[6] = 0.f; res[10] = 1.f; res[14] = 0.f;
+ res[3] = 0.f; res[7] = 0.f; res[11] = 0.f; res[15] = 1.f;
+
+ /* apply all three rotations using the three matrices:
+ *
+ * â¡ c3 s3 0 ⤠⡠c2 0 -s2 ⤠⡠1 0 0 â¤
+ * ⢠-s3 c3 0 ⥠⢠0 1 0 ⥠⢠0 c1 s1 â¥
+ * ⣠0 0 1 ⦠⣠s2 0 c2 ⦠⣠0 -s1 c1 â¦
+ */
+ res[0] = c3c2; res[4] = s3c1 + c3s2s1; res[8] = s3s1 - c3s2c1; res[12] = 0.f;
+ res[1] = -s3c2; res[5] = c3c1 - s3s2s1; res[9] = c3s1 + s3s2c1; res[13] = 0.f;
+ res[2] = s2; res[6] = -c2s1; res[10] = c2c1; res[14] = 0.f;
+ res[3] = 0.f; res[7] = 0.f; res[11] = 0.f; res[15] = 1.f;
+}
+
+int __init_texture(GLuint* ptex, GLenum format, int w, int h, uint8_t* data)
+{
+ GLuint tex;
+
+ glGenTextures(1, &tex);
+ glBindTexture(GL_TEXTURE_2D, tex);
+ glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0, format, GL_UNSIGNED_BYTE, data);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ *ptex = tex;
+ return 0;
+}
+
+int __init_program(GLuint* pprog, const char* vert_shader, const char* frag_shader)
+{
+ GLint status = 0;
+ GLint len = 0;
+
+ GLuint vshader = glCreateShader(GL_VERTEX_SHADER);
+
+ glShaderSource (vshader, 1, &vert_shader, NULL);
+ glCompileShader(vshader);
+ glGetShaderiv (vshader, GL_COMPILE_STATUS, &status);
+
+ if (!status) {
+ glGetShaderiv(vshader, GL_INFO_LOG_LENGTH, &len);
+
+ char* log = calloc(1, len + 1);
+ if (!log)
+ return -ENOMEM;
+
+ glGetShaderInfoLog(vshader, len, NULL, log);
+
+ scf_loge("%s\n", log);
+ free(log);
+ log = NULL;
+ }
+
+ GLuint fshader = glCreateShader(GL_FRAGMENT_SHADER);
+
+ glShaderSource (fshader, 1, &frag_shader, NULL);
+ glCompileShader(fshader);
+ glGetShaderiv (fshader, GL_COMPILE_STATUS, &status);
+
+ if (!status) {
+ glGetShaderiv(fshader, GL_INFO_LOG_LENGTH, &len);
+
+ char* log = calloc(1, len + 1);
+ if (!log)
+ return -ENOMEM;
+
+ glGetShaderInfoLog(fshader, len, NULL, log);
+
+ scf_loge("%s\n", log);
+ free(log);
+ log = NULL;
+ }
+
+ GLuint program = glCreateProgram();
+
+ glAttachShader(program, vshader);
+ glAttachShader(program, fshader);
+ glLinkProgram (program);
+ glGetProgramiv(program, GL_LINK_STATUS, &status);
+
+ if (!status) {
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &len);
+
+ char* log = calloc(1, len + 1);
+ if (!log)
+ return -ENOMEM;
+
+ glGetProgramInfoLog(program, len, NULL, log);
+
+ scf_loge("%s\n", log);
+ free(log);
+ log = NULL;
+ }
+
+ *pprog = program;
+ return 0;
+}
diff --git a/ui/abc_render_a.c b/ui/abc_render_a.c
new file mode 100644
index 0000000..02b37ba
--- /dev/null
+++ b/ui/abc_render_a.c
@@ -0,0 +1,190 @@
+#include"abc.h"
+
+static const char* vert_shader =
+ "#version 330 core\n"
+ "layout(location = 0) in vec4 position; \n"
+ "layout(location = 1) in vec2 a_texCoord; \n"
+ "out vec2 v_texCoord; \n"
+ "uniform mat4 mvp; \n"
+ "void main() { \n"
+ "gl_Position = mvp * position; \n"
+ "v_texCoord = a_texCoord; \n"
+ "} \n";
+
+static const char* frag_shader =
+ "#version 330 core\n"
+ "in vec2 v_texCoord; \n"
+ "out vec4 outputColor; \n"
+ "uniform sampler2D tex_rgba; \n"
+ "void main() { \n"
+ " vec2 xy = v_texCoord; \n"
+ " vec4 v = texture2D(tex_rgba, xy).rgba; \n"
+ " outputColor = vec4(v.b, v.g, v.r, 1.0); \n"
+ "} \n";
+
+
+static GLuint program = 0;
+
+static GLuint vao = 0;
+static GLuint buffers[2] = {0};
+static GLuint texture_rgba = 0;
+
+static GLuint uniform_mvp;
+static GLuint uniform_rgba;
+
+static int __width = 0;
+static int __height = 0;
+
+static int __init_buffers(GLuint* vao, GLuint buffers[2])
+{
+ glGenBuffers(2, buffers);
+ glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vert_array), vert_array, GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, buffers[1]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(texture_array), texture_array, GL_STATIC_DRAW);
+
+ glGenVertexArrays(1, vao);
+ glBindVertexArray(*vao);
+
+ glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
+
+ glBindBuffer(GL_ARRAY_BUFFER, buffers[1]);
+ glEnableVertexAttribArray(1);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
+
+ glBindVertexArray(0);
+ return 0;
+}
+
+static int _render_fini_a(abc_render_t* render)
+{
+ return 0;
+}
+
+static int _render_draw_a(abc_render_t* render, abc_obj_t* obj, int width, int height)
+{
+ if (0 == program)
+ __init_program(&program, vert_shader, frag_shader);
+
+ if (0 == vao)
+ __init_buffers(&vao, buffers);
+
+ if (0 == texture_rgba)
+ __init_texture(&texture_rgba, GL_RGBA, obj->w, obj->h, NULL);
+
+ cairo_text_extents_t extents;
+ cairo_surface_t* surface;
+ cairo_t* cr;
+
+ scf_list_t* l;
+ abc_obj_t* attr;
+
+ uint8_t* rgba = calloc(1, obj->w * obj->h * 4);
+ if (!rgba)
+ return -ENOMEM;
+
+ surface = cairo_image_surface_create_for_data(rgba, CAIRO_FORMAT_ARGB32, obj->w, obj->h, obj->w * 4);
+ cr = cairo_create(surface);
+
+ cairo_set_line_width(cr, 1);
+ cairo_set_source_rgb(cr, 1, 1, 1);
+ cairo_rectangle(cr, 0, 0, obj->w, obj->h);
+ cairo_fill(cr);
+ cairo_stroke(cr);
+
+ cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
+
+ for (l = scf_list_head(&obj->attrs); l != scf_list_sentinel(&obj->attrs); l = scf_list_next(l)) {
+ attr = scf_list_data(l, abc_obj_t, list);
+
+ switch (attr->type) {
+
+ case ABC_HTML_ATTR_FONT:
+ cairo_select_font_face(cr, attr->value->data, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
+ break;
+
+ case ABC_HTML_ATTR_FONT_SIZE:
+ cairo_set_font_size(cr, atoi(attr->value->data));
+ break;
+
+ case ABC_HTML_ATTR_FONT_COLOR:
+ if (!strcmp(attr->value->data, "blue"))
+ cairo_set_source_rgba(cr, 0.0, 0.0, 1.0, 1.0);
+ break;
+ default:
+ break;
+ };
+ }
+
+ cairo_set_line_width(cr, 1);
+
+ cairo_text_extents(cr, obj->text->data, &extents);
+
+ cairo_move_to (cr, extents.x_bearing, -extents.y_bearing);
+ cairo_show_text(cr, obj->text->data);
+
+ cairo_move_to (cr, extents.x_bearing, -extents.y_bearing);
+ cairo_line_to (cr, extents.width, -extents.y_bearing);
+ cairo_stroke(cr);
+
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+ surface = NULL;
+ cr = NULL;
+
+ float mvp[16];
+ __compute_mvp(mvp, 0, 0, 0);
+
+ scf_logi("%s, x: %d, y: %d, w: %d, h: %d\n", obj->text->data, obj->x, obj->y, obj->w, obj->h);
+
+ GLfloat vert_update[] =
+ {
+ 2.0 * obj->x / (float)width - 1.0,
+ -2.0 * (obj->y + obj->h) / (float)height + 1.0,
+
+ 2.0 * (obj->x + obj->w) / (float)width - 1.0,
+ -2.0 * (obj->y + obj->h) / (float)height + 1.0,
+
+ 2.0 * obj->x / (float)width - 1.0,
+ -2.0 * obj->y / (float)height + 1.0,
+
+ 2.0 * (obj->x + obj->w) / (float)width - 1.0,
+ -2.0 * obj->y / (float)height + 1.0,
+ };
+
+ glUseProgram(program);
+ uniform_rgba = glGetUniformLocation(program, "tex_rgba");
+ uniform_mvp = glGetUniformLocation(program, "mvp");
+
+ glUniformMatrix4fv(uniform_mvp, 1, GL_FALSE, mvp);
+
+ // board
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture (GL_TEXTURE_2D, texture_rgba);
+ glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, obj->w, obj->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba);
+ glUniform1i(uniform_rgba, 0);
+
+ // draw
+ glBindBuffer (GL_ARRAY_BUFFER, buffers[0]);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vert_update), vert_update);
+
+ glBindVertexArray(vao);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ glBindVertexArray(0);
+ glUseProgram(0);
+
+ free(rgba);
+ return 0;
+}
+
+abc_render_t abc_render_a =
+{
+ .type = ABC_HTML_A,
+
+ .draw = _render_draw_a,
+ .fini = _render_fini_a,
+};
diff --git a/ui/abc_render_a_href.c b/ui/abc_render_a_href.c
new file mode 100644
index 0000000..96365c0
--- /dev/null
+++ b/ui/abc_render_a_href.c
@@ -0,0 +1,215 @@
+#include"abc.h"
+
+static const char* vert_shader =
+ "#version 330 core\n"
+ "layout(location = 0) in vec4 position; \n"
+ "layout(location = 1) in vec2 a_texCoord; \n"
+ "out vec2 v_texCoord; \n"
+ "uniform mat4 mvp; \n"
+ "void main() { \n"
+ "gl_Position = mvp * position; \n"
+ "v_texCoord = a_texCoord; \n"
+ "} \n";
+
+static const char* frag_shader =
+ "#version 330 core\n"
+ "in vec2 v_texCoord; \n"
+ "out vec4 outputColor; \n"
+ "uniform sampler2D tex_rgba; \n"
+ "void main() { \n"
+ " vec2 xy = v_texCoord; \n"
+ " vec4 v = texture2D(tex_rgba, xy).rgba; \n"
+ " outputColor = vec4(v.b, v.g, v.r, 1.0); \n"
+ "} \n";
+
+
+static GLuint program = 0;
+
+static GLuint vao = 0;
+static GLuint buffers[2] = {0};
+static GLuint texture_rgba = 0;
+
+static GLuint uniform_mvp;
+static GLuint uniform_rgba;
+
+static int __width = 0;
+static int __height = 0;
+
+static int __init_buffers(GLuint* vao, GLuint buffers[2])
+{
+ glGenBuffers(2, buffers);
+ glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vert_array), vert_array, GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, buffers[1]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(texture_array), texture_array, GL_STATIC_DRAW);
+
+ glGenVertexArrays(1, vao);
+ glBindVertexArray(*vao);
+
+ glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
+
+ glBindBuffer(GL_ARRAY_BUFFER, buffers[1]);
+ glEnableVertexAttribArray(1);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
+
+ glBindVertexArray(0);
+ return 0;
+}
+
+static int _render_fini_a_href(abc_render_t* render)
+{
+ return 0;
+}
+
+static int _render_draw_a_href(abc_render_t* render, abc_obj_t* obj, int width, int height)
+{
+ if (0 == program)
+ __init_program(&program, vert_shader, frag_shader);
+
+ if (0 == vao)
+ __init_buffers(&vao, buffers);
+
+ if (0 == texture_rgba)
+ __init_texture(&texture_rgba, GL_RGBA, width, 32, NULL);
+
+ cairo_text_extents_t extents;
+ cairo_surface_t* surface;
+ cairo_t* cr;
+
+ scf_list_t* l;
+ abc_obj_t* attr;
+
+ uint8_t* rgba = malloc(width * 32 * 4);
+ if (!rgba)
+ return -ENOMEM;
+
+ surface = cairo_image_surface_create_for_data(rgba, CAIRO_FORMAT_ARGB32, width, 32, width * 4);
+ cr = cairo_create(surface);
+
+ cairo_select_font_face(cr, "serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
+ cairo_set_font_size (cr, 24);
+ cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0);
+
+ for (l = scf_list_head(&obj->attrs); l != scf_list_sentinel(&obj->attrs); l = scf_list_next(l)) {
+ attr = scf_list_data(l, abc_obj_t, list);
+
+ if (ABC_HTML_ATTR_HREF == attr->type) {
+
+ cairo_text_extents(cr, attr->value->data, &extents);
+ break;
+ }
+ }
+
+ int w = extents.width + extents.x_bearing;
+ int h = extents.height;
+
+ w = (w + 7) & ~0x3;
+ h = (h + 7) & ~0x3;
+
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+ surface = NULL;
+ cr = NULL;
+
+ if (l == scf_list_sentinel(&obj->attrs)) {
+ scf_loge("hyper link '%s' not found a URL\n", obj->text->data);
+
+ free(rgba);
+ rgba = NULL;
+ return -EINVAL;
+ }
+
+ scf_logd("extents: x_bearing: %f, width: %f, y_bearing: %f, height: %f\n",
+ extents.x_bearing, extents.width, extents.y_bearing, extents.height);
+
+ void* p = realloc(rgba, w * h * 4);
+ if (!p) {
+ free(rgba);
+ rgba = NULL;
+ return -ENOMEM;
+ }
+ rgba = p;
+
+ surface = cairo_image_surface_create_for_data(rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4);
+ cr = cairo_create(surface);
+
+ cairo_set_line_width(cr, 1);
+ cairo_set_source_rgb(cr, 0.2, 0.2, 0.2);
+ cairo_rectangle(cr, 0, 0, w, h);
+ cairo_fill(cr);
+ cairo_stroke(cr);
+
+ cairo_select_font_face(cr, "serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
+ cairo_set_font_size (cr, 24);
+ cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0);
+
+ cairo_move_to (cr, extents.x_bearing, -extents.y_bearing + 2);
+ cairo_show_text(cr, attr->value->data);
+
+// cairo_surface_write_to_png(surface, "tmp.png");
+
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+ surface = NULL;
+ cr = NULL;
+
+ float mvp[16];
+ __compute_mvp(mvp, 0, 0, 0);
+
+ scf_logd("%s, href: %s, w: %d, h: %d\n", obj->text->data, attr->value->data, w, h);
+
+ GLfloat vert_update[] =
+ {
+//0, 1
+ -1.0,
+ -1.0,
+
+//1, 1
+ 2.0 * w / (float)width - 1.0,
+ -1.0,
+
+//0, 0
+ -1.0,
+ 2.0 * h / (float)height - 1.0,
+
+//1, 0
+ 2.0 * w / (float)width - 1.0,
+ 2.0 * h / (float)height - 1.0,
+ };
+
+ glUseProgram(program);
+ uniform_rgba = glGetUniformLocation(program, "tex_rgba");
+ uniform_mvp = glGetUniformLocation(program, "mvp");
+
+ glUniformMatrix4fv(uniform_mvp, 1, GL_FALSE, mvp);
+
+ // board
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture (GL_TEXTURE_2D, texture_rgba);
+ glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba);
+ glUniform1i(uniform_rgba, 0);
+
+ // draw
+ glBindBuffer (GL_ARRAY_BUFFER, buffers[0]);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vert_update), vert_update);
+
+ glBindVertexArray(vao);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ glBindVertexArray(0);
+ glUseProgram(0);
+
+ free(rgba);
+ return 0;
+}
+
+abc_render_t abc_render_a_href =
+{
+ .type = ABC_HTML_A_HREF,
+
+ .draw = _render_draw_a_href,
+ .fini = _render_fini_a_href,
+};
diff --git a/ui/abc_render_body.c b/ui/abc_render_body.c
new file mode 100644
index 0000000..2008e77
--- /dev/null
+++ b/ui/abc_render_body.c
@@ -0,0 +1,19 @@
+#include"abc.h"
+
+static int _render_fini_body(abc_render_t* render)
+{
+ return 0;
+}
+
+static int _render_draw_body(abc_render_t* render, abc_obj_t* obj, int width, int height)
+{
+ return 0;
+}
+
+abc_render_t abc_render_body =
+{
+ .type = ABC_HTML_H1,
+
+ .draw = _render_draw_body,
+ .fini = _render_fini_body,
+};
diff --git a/ui/abc_render_div.c b/ui/abc_render_div.c
new file mode 100644
index 0000000..5768b28
--- /dev/null
+++ b/ui/abc_render_div.c
@@ -0,0 +1,19 @@
+#include"abc.h"
+
+static int _render_fini_div(abc_render_t* render)
+{
+ return 0;
+}
+
+static int _render_draw_div(abc_render_t* render, abc_obj_t* obj, int width, int height)
+{
+ return 0;
+}
+
+abc_render_t abc_render_div =
+{
+ .type = ABC_HTML_H1,
+
+ .draw = _render_draw_div,
+ .fini = _render_fini_div,
+};
diff --git a/ui/abc_render_h1.c b/ui/abc_render_h1.c
new file mode 100644
index 0000000..8a94b5c
--- /dev/null
+++ b/ui/abc_render_h1.c
@@ -0,0 +1,179 @@
+#include"abc.h"
+
+const char* vert_shader =
+ "#version 330 core\n"
+ "layout(location = 0) in vec4 position; \n"
+ "layout(location = 1) in vec2 a_texCoord; \n"
+ "out vec2 v_texCoord; \n"
+ "uniform mat4 mvp; \n"
+ "void main() { \n"
+ "gl_Position = mvp * position; \n"
+ "v_texCoord = a_texCoord; \n"
+ "} \n";
+
+const char* frag_shader =
+ "#version 330 core\n"
+ "in vec2 v_texCoord; \n"
+ "out vec4 outputColor; \n"
+ "uniform sampler2D tex_rgba; \n"
+ "void main() { \n"
+ " vec2 xy = v_texCoord; \n"
+ " vec4 v = texture2D(tex_rgba, xy).rgba; \n"
+ " outputColor = vec4(v.b, v.g, v.r, 1.0); \n"
+ "} \n";
+
+
+static GLuint program = 0;
+
+static GLuint vao = 0;
+static GLuint buffers[2] = {0};
+static GLuint texture_rgba = 0;
+
+static GLuint uniform_mvp;
+static GLuint uniform_rgba;
+
+static int __width = 0;
+static int __height = 0;
+
+static int __init_buffers(GLuint* vao, GLuint buffers[2])
+{
+ glGenBuffers(2, buffers);
+ glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vert_array), vert_array, GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, buffers[1]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(texture_array), texture_array, GL_STATIC_DRAW);
+
+ glGenVertexArrays(1, vao);
+ glBindVertexArray(*vao);
+
+ glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
+
+ glBindBuffer(GL_ARRAY_BUFFER, buffers[1]);
+ glEnableVertexAttribArray(1);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
+
+ glBindVertexArray(0);
+ return 0;
+}
+
+static int _render_fini_h1(abc_render_t* render)
+{
+ return 0;
+}
+
+static int _render_draw_h1(abc_render_t* render, abc_obj_t* obj, int width, int height)
+{
+ if (0 == program)
+ __init_program(&program, vert_shader, frag_shader);
+
+ if (0 == vao)
+ __init_buffers(&vao, buffers);
+
+ if (0 == texture_rgba)
+ __init_texture(&texture_rgba, GL_RGBA, obj->w, obj->h, NULL);
+
+ cairo_text_extents_t extents;
+ cairo_surface_t* surface;
+ cairo_t* cr;
+
+ scf_list_t* l;
+ abc_obj_t* attr;
+
+ uint8_t* rgba = calloc(1, obj->w * obj->h * 4);
+ if (!rgba)
+ return -ENOMEM;
+
+ surface = cairo_image_surface_create_for_data(rgba, CAIRO_FORMAT_ARGB32, obj->w, obj->h, obj->w * 4);
+ cr = cairo_create(surface);
+
+ cairo_set_line_width(cr, 1);
+ cairo_set_source_rgb(cr, 1, 1, 1);
+ cairo_rectangle(cr, 0, 0, obj->w, obj->h);
+ cairo_fill(cr);
+ cairo_stroke(cr);
+
+ for (l = scf_list_head(&obj->attrs); l != scf_list_sentinel(&obj->attrs); l = scf_list_next(l)) {
+ attr = scf_list_data(l, abc_obj_t, list);
+
+ switch (attr->type) {
+
+ case ABC_HTML_ATTR_FONT:
+ cairo_select_font_face(cr, attr->value->data, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
+ break;
+
+ case ABC_HTML_ATTR_FONT_SIZE:
+ cairo_set_font_size(cr, atoi(attr->value->data));
+ break;
+ default:
+ break;
+ };
+ }
+
+ cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
+
+ cairo_text_extents(cr, obj->text->data, &extents);
+
+ cairo_move_to (cr, extents.x_bearing, -extents.y_bearing);
+ cairo_show_text(cr, obj->text->data);
+
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+ surface = NULL;
+ cr = NULL;
+
+ float mvp[16];
+ __compute_mvp(mvp, 0, 0, 0);
+
+ scf_logi("%s, x: %d, y: %d, w: %d, h: %d\n", obj->text->data, obj->x, obj->y, obj->w, obj->h);
+
+ GLfloat vert_update[] =
+ {
+ 2.0 * obj->x / (float)width - 1.0,
+ -2.0 * (obj->y + obj->h) / (float)height + 1.0,
+
+ 2.0 * (obj->x + obj->w) / (float)width - 1.0,
+ -2.0 * (obj->y + obj->h) / (float)height + 1.0,
+
+ 2.0 * obj->x / (float)width - 1.0,
+ -2.0 * obj->y / (float)height + 1.0,
+
+ 2.0 * (obj->x + obj->w) / (float)width - 1.0,
+ -2.0 * obj->y / (float)height + 1.0,
+ };
+
+ glUseProgram(program);
+ uniform_rgba = glGetUniformLocation(program, "tex_rgba");
+ uniform_mvp = glGetUniformLocation(program, "mvp");
+
+ glUniformMatrix4fv(uniform_mvp, 1, GL_FALSE, mvp);
+
+ // board
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture (GL_TEXTURE_2D, texture_rgba);
+ glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, obj->w, obj->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba);
+ glUniform1i(uniform_rgba, 0);
+
+ // draw
+ glBindBuffer (GL_ARRAY_BUFFER, buffers[0]);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vert_update), vert_update);
+
+ glBindVertexArray(vao);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ glBindVertexArray(0);
+ glUseProgram(0);
+
+ free(rgba);
+ return 0;
+}
+
+abc_render_t abc_render_h1 =
+{
+ .type = ABC_HTML_H1,
+
+ .draw = _render_draw_h1,
+ .fini = _render_fini_h1,
+};
diff --git a/ui/abc_render_head.c b/ui/abc_render_head.c
new file mode 100644
index 0000000..cef9af5
--- /dev/null
+++ b/ui/abc_render_head.c
@@ -0,0 +1,19 @@
+#include"abc.h"
+
+static int _render_fini_head(abc_render_t* render)
+{
+ return 0;
+}
+
+static int _render_draw_head(abc_render_t* render, abc_obj_t* obj, int width, int height)
+{
+ return 0;
+}
+
+abc_render_t abc_render_head =
+{
+ .type = ABC_HTML_H1,
+
+ .draw = _render_draw_head,
+ .fini = _render_fini_head,
+};
diff --git a/ui/abc_render_html.c b/ui/abc_render_html.c
new file mode 100644
index 0000000..37f13ec
--- /dev/null
+++ b/ui/abc_render_html.c
@@ -0,0 +1,19 @@
+#include"abc.h"
+
+static int _render_fini_html(abc_render_t* render)
+{
+ return 0;
+}
+
+static int _render_draw_html(abc_render_t* render, abc_obj_t* obj, int width, int height)
+{
+ return 0;
+}
+
+abc_render_t abc_render_html =
+{
+ .type = ABC_HTML_H1,
+
+ .draw = _render_draw_html,
+ .fini = _render_fini_html,
+};
diff --git a/ui/abc_render_title.c b/ui/abc_render_title.c
new file mode 100644
index 0000000..3c53e7a
--- /dev/null
+++ b/ui/abc_render_title.c
@@ -0,0 +1,19 @@
+#include"abc.h"
+
+static int _render_fini_title(abc_render_t* render)
+{
+ return 0;
+}
+
+static int _render_draw_title(abc_render_t* render, abc_obj_t* obj, int width, int height)
+{
+ return 0;
+}
+
+abc_render_t abc_render_title =
+{
+ .type = ABC_HTML_H1,
+
+ .draw = _render_draw_title,
+ .fini = _render_fini_title,
+};
diff --git a/ui/abc_ui.glade b/ui/abc_ui.glade
new file mode 100644
index 0000000..383c82b
--- /dev/null
+++ b/ui/abc_ui.glade
@@ -0,0 +1,157 @@
+
+
+
+
+
+ True
+ False
+ æµè§å¨
+ 1280
+ 720
+
+
+
+
+
+
+
+
diff --git a/ui/main.c b/ui/main.c
new file mode 100644
index 0000000..7181b54
--- /dev/null
+++ b/ui/main.c
@@ -0,0 +1,348 @@
+#include"abc.h"
+
+static void resize(GtkGLArea* self, gint width, gint height, gpointer user_data)
+{
+ GdkGLContext *context;
+
+ gtk_gl_area_make_current(self);
+
+ if (gtk_gl_area_get_error(self) != NULL) {
+ printf("%s(),%d, error:\n", __func__, __LINE__);
+ return;
+ }
+
+ printf("%s(),%d, width: %d, height: %d\n", __func__, __LINE__, width, height);
+
+ context = gtk_gl_area_get_context(self);
+
+ if (gdk_gl_context_get_use_es(context))
+ printf("%s(),%d, gles\n", __func__, __LINE__);
+ else
+ printf("%s(),%d, gl\n", __func__, __LINE__);
+}
+
+static void unrealize(GtkWidget *widget)
+{
+ printf("%s(),%d\n", __func__, __LINE__);
+}
+
+static gboolean render(GtkGLArea* self, GdkGLContext* context, gpointer user_data)
+{
+ if (gtk_gl_area_get_error(self) != NULL)
+ return FALSE;
+
+ abc_ctx_t* ctx = user_data;
+ abc_obj_t* root = ctx->current->root;
+
+ int width = gtk_widget_get_allocated_width (GTK_WIDGET(self));
+ int height = gtk_widget_get_allocated_height(GTK_WIDGET(self));
+
+ scf_logd("width: %d, height: %d\n\n", width, height);
+
+ glViewport(0, 0, width, height);
+
+ glClearColor(1.0, 1.0, 1.0, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ int ret = abc_layout_root(NULL, root, width, height);
+
+ ret = abc_render_root(ctx, root, width, height);
+
+ glFlush();
+ return TRUE;
+}
+
+static int __do_button_move(abc_ctx_t* ctx, int x, int y)
+{
+ GdkDisplay* display;
+ GdkWindow* window;
+ GdkCursor* cursor;
+
+ abc_obj_t* prev = ctx->current->current;
+ abc_obj_t* obj = abc_obj_find(ctx->current->root, x, y);
+
+ int ret = 0;
+
+ if (prev && prev != obj) {
+ ctx->current->current = NULL;
+ prev->move_flag = 0;
+
+ display = gtk_widget_get_display(GTK_WIDGET(ctx->gl_area));
+ window = gtk_widget_get_window (GTK_WIDGET(ctx->gl_area));
+ cursor = gdk_cursor_new_for_display(display, GDK_LEFT_PTR);
+
+ gdk_window_set_cursor(window, cursor);
+ g_object_unref(cursor);
+
+ ret = 1;
+ }
+
+ if (obj) {
+ scf_logd("obj: %s, x: %d, y: %d, w: %d, h: %d, event x: %d, y: %d\n",
+ obj->key->data, obj->x, obj->y, obj->w, obj->h, x, y);
+
+
+ switch (obj->type) {
+
+ case ABC_HTML_A:
+ display = gtk_widget_get_display(GTK_WIDGET(ctx->gl_area));
+ window = gtk_widget_get_window (GTK_WIDGET(ctx->gl_area));
+ cursor = gdk_cursor_new_for_display(display, GDK_HAND2);
+
+ gdk_window_set_cursor(window, cursor);
+ g_object_unref(cursor);
+
+ obj->move_flag = 1;
+ ret = 1;
+ break;
+ default:
+ break;
+ };
+
+ ctx->current->current = obj;
+ }
+
+ return ret;
+}
+
+static int __do_button_release(abc_ctx_t* ctx, int x, int y)
+{
+ abc_html_t* html;
+ abc_obj_t* attr;
+ abc_obj_t* obj = abc_obj_find(ctx->current->root, x, y);
+
+ if (!obj)
+ return -ENOMEM;
+
+ scf_logd("obj: %s, x: %d, y: %d, w: %d, h: %d, event x: %d, y: %d\n",
+ obj->key->data, obj->x, obj->y, obj->w, obj->h, x, y);
+
+ int ret = 0;
+
+ if (ABC_HTML_A == obj->type) {
+
+ html = NULL;
+ attr = abc_obj_get_attr(obj, ABC_HTML_ATTR_HREF);
+
+ ret = abc_html_open(&html, attr->value->data);
+ if (ret < 0)
+ return ret;
+
+ ret = abc_html_parse(html);
+ if (ret < 0) {
+ abc_html_close(html);
+ html = NULL;
+ return ret;
+ }
+
+ scf_list_add_tail(&ctx->html_list, &html->list);
+
+ html->root->gtk_builder = ctx->builder;
+
+ ctx->current = html;
+
+ int width = gtk_widget_get_allocated_width (GTK_WIDGET(ctx->gl_area));
+ int height = gtk_widget_get_allocated_height(GTK_WIDGET(ctx->gl_area));
+
+ abc_layout_root(NULL, ctx->current->root, width, height);
+
+ __do_button_move(ctx, x, y);
+
+ gtk_widget_set_sensitive(GTK_WIDGET(ctx->back), TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ctx->forward), FALSE);
+
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static void back_clicked(GtkButton* self, gpointer user_data)
+{
+ abc_ctx_t* ctx = user_data;
+ scf_list_t* l;
+
+ if (ctx->current) {
+ l = scf_list_prev(&ctx->current->list);
+
+ if (l != scf_list_sentinel(&ctx->html_list)) {
+
+ ctx->current = scf_list_data(l, abc_html_t, list);
+
+ if (scf_list_prev(l) != scf_list_sentinel(&ctx->html_list))
+ gtk_widget_set_sensitive(GTK_WIDGET(ctx->back), TRUE);
+ else
+ gtk_widget_set_sensitive(GTK_WIDGET(ctx->back), FALSE);
+
+ gtk_widget_set_sensitive(GTK_WIDGET(ctx->forward), TRUE);
+
+ gtk_gl_area_queue_render(ctx->gl_area);
+ }
+ }
+}
+
+static void forward_clicked(GtkButton* self, gpointer user_data)
+{
+ abc_ctx_t* ctx = user_data;
+ scf_list_t* l;
+
+ if (ctx->current) {
+ l = scf_list_next(&ctx->current->list);
+
+ if (l != scf_list_sentinel(&ctx->html_list)) {
+
+ ctx->current = scf_list_data(l, abc_html_t, list);
+
+ if (scf_list_next(l) != scf_list_sentinel(&ctx->html_list))
+ gtk_widget_set_sensitive(GTK_WIDGET(ctx->forward), TRUE);
+ else
+ gtk_widget_set_sensitive(GTK_WIDGET(ctx->forward), FALSE);
+
+ gtk_widget_set_sensitive(GTK_WIDGET(ctx->back), TRUE);
+
+ gtk_gl_area_queue_render(ctx->gl_area);
+ }
+ }
+}
+
+static void refresh_clicked(GtkButton* self, gpointer user_data)
+{
+ abc_html_t* html = NULL;
+ abc_ctx_t* ctx = user_data;
+
+ if (ctx->current) {
+
+ int ret = abc_html_open(&html, ctx->current->file->data);
+ if (ret < 0) {
+ scf_loge("refresh '%s' failed\n", ctx->current->file->data);
+ return;
+ }
+
+ ret = abc_html_parse(html);
+ if (ret < 0) {
+ scf_loge("refresh '%s' failed\n", ctx->current->file->data);
+
+ abc_html_close(html);
+ html = NULL;
+ return;
+ }
+
+ scf_list_add_tail(&ctx->current->list, &html->list);
+
+ html->root->gtk_builder = ctx->builder;
+
+ scf_list_del(&ctx->current->list);
+ abc_html_close(ctx->current);
+
+ ctx->current = html;
+
+ gtk_gl_area_queue_render(ctx->gl_area);
+ }
+}
+
+static gboolean button_release_event(GtkWidget* self, GdkEventButton* event, gpointer user_data)
+{
+ abc_ctx_t* ctx = user_data;
+
+ if (gtk_widget_get_window(GTK_WIDGET(ctx->gl_area)) == event->window) {
+
+ int ret = __do_button_release(ctx, event->x, event->y);
+ if (ret > 0)
+ gtk_gl_area_queue_render(ctx->gl_area);
+ }
+
+ return TRUE;
+}
+
+static gboolean button_move_event(GtkWidget* self, GdkEventMotion* event, gpointer user_data)
+{
+ abc_ctx_t* ctx = user_data;
+
+ int ret = __do_button_move(ctx, event->x, event->y);
+ if (ret > 0)
+ gtk_gl_area_queue_render(ctx->gl_area);
+
+ return TRUE;
+}
+
+int main(int argc, char *argv[])
+{
+ abc_ctx_t ctx = ABC_CTX_INIT(ctx);
+
+ GtkBuilder* builder;
+ GObject* window;
+ GObject* gl_area;
+
+ GObject* back;
+ GObject* forward;
+ GObject* refresh;
+ GError* error = NULL;
+
+ abc_html_t* html = NULL;
+
+ if (argc < 2) {
+ scf_loge("\n");
+ return -1;
+ }
+
+ gtk_init(&argc, &argv);
+
+ builder = gtk_builder_new();
+
+ if (gtk_builder_add_from_file(builder, "abc_ui.glade", &error) == 0) {
+ g_printerr("Error loading file: %s\n", error->message);
+ g_clear_error(&error);
+ return 1;
+ }
+
+ int ret = abc_html_open(&html, argv[1]);
+ if (ret < 0) {
+ scf_loge("\n");
+ return ret;
+ }
+
+ ret = abc_html_parse(html);
+ if (ret < 0) {
+ scf_loge("\n");
+ return ret;
+ }
+
+ scf_list_add_tail(&ctx.html_list, &html->list);
+
+ html->root->gtk_builder = builder;
+
+ window = gtk_builder_get_object(builder, "main_window");
+ gl_area = gtk_builder_get_object(builder, "gl_area");
+
+ back = gtk_builder_get_object(builder, "button_back");
+ forward = gtk_builder_get_object(builder, "button_forward");
+ refresh = gtk_builder_get_object(builder, "button_refresh");
+
+ ctx.current = html;
+ ctx.builder = builder;
+ ctx.gl_area = GTK_GL_AREA(gl_area);
+
+ ctx.back = GTK_BUTTON(back);
+ ctx.forward = GTK_BUTTON(forward);
+ ctx.refresh = GTK_BUTTON(refresh);
+
+ g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
+
+ g_signal_connect(back, "clicked", G_CALLBACK(back_clicked), &ctx);
+ g_signal_connect(forward, "clicked", G_CALLBACK(forward_clicked), &ctx);
+ g_signal_connect(refresh, "clicked", G_CALLBACK(refresh_clicked), &ctx);
+
+ g_signal_connect(gl_area, "resize", G_CALLBACK(resize), NULL);
+ g_signal_connect(gl_area, "unrealize", G_CALLBACK(unrealize), NULL);
+ g_signal_connect(gl_area, "render", G_CALLBACK(render), &ctx);
+
+ gtk_widget_add_events(GTK_WIDGET(gl_area), GDK_POINTER_MOTION_MASK);
+
+ g_signal_connect(window, "button-release-event", G_CALLBACK(button_release_event), &ctx);
+ g_signal_connect(gl_area, "motion-notify-event", G_CALLBACK(button_move_event), &ctx);
+
+ gtk_widget_show_all(GTK_WIDGET(window));
+
+ gtk_main();
+ return 0;
+}
diff --git a/util/scf_def.h b/util/scf_def.h
new file mode 100644
index 0000000..55b1def
--- /dev/null
+++ b/util/scf_def.h
@@ -0,0 +1,73 @@
+#ifndef SCF_DEF_H
+#define SCF_DEF_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if 1
+#include
+static inline int64_t gettime()
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+
+ return tv.tv_sec * 1000000LL + tv.tv_usec;
+}
+#endif
+
+// sign-extend low 'src_bits' of src to 64 bits
+static inline uint64_t scf_sign_extend(uint64_t src, int src_bits)
+{
+ uint64_t sign = src >> (src_bits - 1) & 0x1;
+ uint64_t mask = (~sign + 1) << (src_bits - 1);
+
+ src |= mask;
+ return src;
+}
+
+// zero-extend low 'src_bits' of src to 64 bits
+static inline uint64_t scf_zero_extend(uint64_t src, int src_bits)
+{
+ uint64_t mask = (1ULL << src_bits) - 1;
+
+ src &= mask;
+ return src;
+}
+
+#define SCF_XCHG(x, y) \
+ do {\
+ typeof(x) tmp = x; \
+ x = y; \
+ y = tmp; \
+ } while (0)
+
+#ifdef SCF_DEBUG
+#define scf_logd(fmt, ...) printf("%s(), %d, "fmt, __func__, __LINE__, ##__VA_ARGS__)
+#else
+#define scf_logd(fmt, ...)
+#endif
+
+#define scf_logi(fmt, ...) printf("%s(), %d, info: "fmt, __func__, __LINE__, ##__VA_ARGS__)
+
+#define scf_loge(fmt, ...) printf("%s(), %d, \033[31m error:\033[0m "fmt, __func__, __LINE__, ##__VA_ARGS__)
+#define scf_logw(fmt, ...) printf("%s(), %d, \033[33m warning:\033[0m "fmt, __func__, __LINE__, ##__VA_ARGS__)
+
+#define SCF_CHECK_ERROR(cond, ret, fmt, ...) \
+ do { \
+ if (cond) { \
+ printf("%s(), %d, \033[31m error:\033[0m "fmt, __func__, __LINE__, ##__VA_ARGS__); \
+ return ret; \
+ } \
+ } while (0)
+
+#endif
+
diff --git a/util/scf_list.h b/util/scf_list.h
new file mode 100644
index 0000000..4b5812c
--- /dev/null
+++ b/util/scf_list.h
@@ -0,0 +1,81 @@
+#ifndef SCF_LIST_H
+#define SCF_LIST_H
+
+#include"scf_def.h"
+
+typedef struct scf_list_s scf_list_t;
+
+struct scf_list_s {
+ struct scf_list_s* prev;
+ struct scf_list_s* next;
+};
+
+static inline void scf_list_init(scf_list_t* h)
+{
+ h->prev = h;
+ h->next = h;
+}
+
+static inline void scf_list_del(scf_list_t* n)
+{
+ n->prev->next = n->next;
+ n->next->prev = n->prev;
+
+ // only to avoid some wrong operations for these 2 invalid pointers
+ n->prev = NULL;
+ n->next = NULL;
+}
+
+static inline void scf_list_add_tail(scf_list_t* h, scf_list_t* n)
+{
+ h->prev->next = n;
+ n->prev = h->prev;
+ n->next = h;
+ h->prev = n;
+}
+
+static inline void scf_list_add_front(scf_list_t* h, scf_list_t* n)
+{
+ h->next->prev = n;
+ n->next = h->next;
+ n->prev = h;
+ h->next = n;
+}
+
+#define SCF_LIST_INIT(h) {&h, &h}
+
+#define scf_list_data(l, type, member) ((type*)((char*)l - offsetof(type, member)))
+
+#define scf_list_head(h) ((h)->next)
+#define scf_list_tail(h) ((h)->prev)
+#define scf_list_sentinel(h) (h)
+#define scf_list_next(l) ((l)->next)
+#define scf_list_prev(l) ((l)->prev)
+#define scf_list_empty(h) ((h)->next == (h))
+
+#define scf_list_clear(h, type, member, type_free) \
+ do {\
+ scf_list_t* l;\
+ for (l = scf_list_head(h); l != scf_list_sentinel(h);) {\
+ type* t = scf_list_data(l, type, member);\
+ l = scf_list_next(l);\
+ scf_list_del(&t->member);\
+ type_free(t);\
+ t = NULL;\
+ }\
+ } while(0)
+
+#define scf_list_mov(dst, src, type, member) \
+ do {\
+ scf_list_t* l;\
+ type* t;\
+ for (l = scf_list_head(src); l != scf_list_sentinel(src);) {\
+ t = scf_list_data(l, type, member);\
+ l = scf_list_next(l);\
+ scf_list_del(&t->member);\
+ scf_list_add_tail(dst, &t->member);\
+ }\
+ } while(0)
+
+#endif
+
diff --git a/util/scf_stack.h b/util/scf_stack.h
new file mode 100644
index 0000000..24ab942
--- /dev/null
+++ b/util/scf_stack.h
@@ -0,0 +1,60 @@
+#ifndef SCF_STACK_H
+#define SCF_STACK_H
+
+#include"scf_vector.h"
+
+typedef scf_vector_t scf_stack_t;
+
+static inline scf_stack_t* scf_stack_alloc()
+{
+ return scf_vector_alloc();
+}
+
+static inline int scf_stack_push(scf_stack_t* s, void* node)
+{
+ return scf_vector_add(s, node);
+}
+
+static inline void* scf_stack_pop(scf_stack_t* s)
+{
+ if (!s || !s->data)
+ return NULL;
+
+ assert(s->size >= 0);
+
+ if (0 == s->size)
+ return NULL;
+
+ void* node = s->data[--s->size];
+
+ if (s->size + NB_MEMBER_INC * 2 < s->capacity) {
+ void* p = realloc(s->data, sizeof(void*) * (s->capacity - NB_MEMBER_INC));
+ if (p) {
+ s->data = p;
+ s->capacity -= NB_MEMBER_INC;
+ }
+ }
+
+ return node;
+}
+
+static inline void* scf_stack_top(scf_stack_t* s)
+{
+ if (!s || !s->data)
+ return NULL;
+
+ assert(s->size >= 0);
+
+ if (0 == s->size)
+ return NULL;
+
+ return s->data[s->size - 1];
+}
+
+static inline void scf_stack_free(scf_stack_t* s)
+{
+ scf_vector_free(s);
+}
+
+#endif
+
diff --git a/util/scf_string.c b/util/scf_string.c
new file mode 100644
index 0000000..a0b4587
--- /dev/null
+++ b/util/scf_string.c
@@ -0,0 +1,390 @@
+#include"scf_string.h"
+
+#define SCF_STRING_NUMBER_INC 4
+
+scf_string_t* scf_string_alloc()
+{
+ scf_string_t* s = malloc(sizeof(scf_string_t));
+ if (!s)
+ return NULL;
+
+ s->data = malloc(SCF_STRING_NUMBER_INC + 1);
+ if (!s->data) {
+ free(s);
+ return NULL;
+ }
+
+ s->capacity = SCF_STRING_NUMBER_INC;
+ s->len = 0;
+ s->data[0] = '\0';
+ return s;
+}
+
+scf_string_t* scf_string_clone(scf_string_t* s)
+{
+ if (!s)
+ return NULL;
+
+ scf_string_t* s1 = malloc(sizeof(scf_string_t));
+ if (!s1)
+ return NULL;
+
+ if (s->capacity > 0)
+ s1->capacity = s->capacity;
+ else if (s->len > 0)
+ s1->capacity = s->len;
+ else
+ s1->capacity = SCF_STRING_NUMBER_INC;
+
+ s1->data = malloc(s1->capacity + 1);
+ if (!s1->data) {
+ free(s1);
+ return NULL;
+ }
+
+ s1->len = s->len;
+ if (s->len > 0)
+ memcpy(s1->data, s->data, s->len);
+
+ s1->data[s1->len] = '\0';
+ return s1;
+}
+
+scf_string_t* scf_string_cstr(const char* str)
+{
+ if (!str)
+ return NULL;
+
+ return scf_string_cstr_len(str, strlen(str));
+}
+
+scf_string_t* scf_string_cstr_len(const char* str, size_t len)
+{
+ if (!str)
+ return NULL;
+
+ scf_string_t s;
+ s.capacity = -1;
+ s.len = len;
+ s.data = (char*)str;
+
+ return scf_string_clone(&s);
+}
+
+void scf_string_free(scf_string_t* s)
+{
+ if (!s)
+ return;
+
+ if (s->capacity > 0)
+ free(s->data);
+
+ free(s);
+ s = NULL;
+}
+
+void scf_string_print_bin(scf_string_t* s)
+{
+ if (!s)
+ return;
+
+ int i;
+ for (i = 0; i < s->len; i++) {
+
+ unsigned char c = s->data[i];
+
+ if (i > 0 && i % 10 == 0)
+ printf("\n");
+
+ printf("%#02x ", c);
+ }
+ printf("\n");
+}
+
+int scf_string_cmp(const scf_string_t* s0, const scf_string_t* s1)
+{
+ if (!s0 || !s1 || !s0->data || !s1->data)
+ return -EINVAL;
+
+ if (s0->len < s1->len)
+ return -1;
+
+ if (s0->len > s1->len)
+ return 1;
+ return strncmp(s0->data, s1->data, s0->len);
+}
+
+int scf_string_cmp_cstr(const scf_string_t* s0, const char* str)
+{
+ if (!s0 || !s0->data || !str)
+ return -EINVAL;
+
+ return scf_string_cmp_cstr_len(s0, str, strlen(str));
+}
+
+int scf_string_cmp_cstr_len(const scf_string_t* s0, const char* str, size_t len)
+{
+ if (!s0 || !s0->data || !str)
+ return -EINVAL;
+
+ scf_string_t s1;
+ s1.capacity = -1;
+ s1.len = len;
+ s1.data = (char*)str;
+
+ return scf_string_cmp(s0, &s1);
+}
+
+int scf_string_copy(scf_string_t* s0, const scf_string_t* s1)
+{
+ if (!s0 || !s1 || !s0->data || !s1->data)
+ return -EINVAL;
+
+ assert(s0->capacity > 0);
+
+ if (s1->len > s0->capacity) {
+ char* p = realloc(s0->data, s1->len + 1);
+ if (!p)
+ return -ENOMEM;
+
+ s0->data = p;
+ s0->capacity = s1->len;
+ }
+
+ memcpy(s0->data, s1->data, s1->len);
+ s0->data[s1->len] = '\0';
+ s0->len = s1->len;
+ return 0;
+}
+
+int scf_string_cat(scf_string_t* s0, const scf_string_t* s1)
+{
+ if (!s0 || !s1 || !s0->data || !s1->data)
+ return -EINVAL;
+
+ assert(s0->capacity > 0);
+
+ if (s0->len + s1->len > s0->capacity) {
+ char* p = realloc(s0->data, s0->len + s1->len + SCF_STRING_NUMBER_INC + 1);
+ if (!p)
+ return -ENOMEM;
+
+ s0->data = p;
+ s0->capacity = s0->len + s1->len + SCF_STRING_NUMBER_INC;
+ }
+
+ memcpy(s0->data + s0->len, s1->data, s1->len);
+ s0->data[s0->len + s1->len] = '\0';
+ s0->len += s1->len;
+ return 0;
+}
+
+int scf_string_cat_cstr(scf_string_t* s0, const char* str)
+{
+ if (!s0 || !s0->data || !str)
+ return -EINVAL;
+
+ return scf_string_cat_cstr_len(s0, str, strlen(str));
+}
+
+int scf_string_cat_cstr_len(scf_string_t* s0, const char* str, size_t len)
+{
+ if (!s0 || !s0->data || !str)
+ return -EINVAL;
+
+ scf_string_t s1;
+ s1.capacity = -1;
+ s1.len = len;
+ s1.data = (char*)str;
+
+ return scf_string_cat(s0, &s1);
+}
+
+int scf_string_fill_zero(scf_string_t* s0, size_t len)
+{
+ if (!s0 || !s0->data)
+ return -EINVAL;
+
+ assert(s0->capacity > 0);
+
+ if (s0->len + len > s0->capacity) {
+
+ char* p = realloc(s0->data, s0->len + len + SCF_STRING_NUMBER_INC + 1);
+ if (!p)
+ return -ENOMEM;
+
+ s0->data = p;
+ s0->capacity = s0->len + len + SCF_STRING_NUMBER_INC;
+ }
+
+ memset(s0->data + s0->len, 0, len);
+ s0->data[s0->len + len] = '\0';
+ s0->len += len;
+ return 0;
+}
+
+static int* _prefix_kmp(const uint8_t* P, int m)
+{
+ int* prefix = malloc(sizeof(int) * (m + 1));
+ if (!prefix)
+ return NULL;
+
+ prefix[0] = -1;
+
+ int k = -1;
+ int q;
+
+ for (q = 1; q < m; q++) {
+
+ while (k > -1 && P[k + 1] != P[q])
+ k = prefix[k];
+
+ if (P[k + 1] == P[q])
+ k++;
+
+ prefix[q] = k;
+ }
+ return prefix;
+}
+
+static int _match_kmp(const uint8_t* T, int Tlen, const uint8_t* P, int Plen, scf_vector_t* offsets)
+{
+ if (Tlen <= 0 || Plen <= 0)
+ return -EINVAL;
+
+ int n = Tlen;
+ int m = Plen;
+
+ int* prefix = _prefix_kmp(P, m);
+ if (!prefix)
+ return -1;
+
+ int q = -1;
+ int i;
+
+ for (i = 0; i < n; i++) {
+
+ while (q > -1 && P[q + 1] != T[i])
+ q = prefix[q];
+
+ if (P[q + 1] == T[i])
+ q++;
+
+ if (q == m - 1) {
+ scf_logd("KMP find P: %s in T: %s, offset: %d\n", P, T, i - m + 1);
+
+ int ret = scf_vector_add(offsets, (void*)(intptr_t)(i - m + 1));
+ if (ret < 0)
+ return ret;
+
+ q = prefix[q];
+ }
+ }
+
+ free(prefix);
+ return 0;
+}
+
+int scf_string_match_kmp(const scf_string_t* T, const scf_string_t* P, scf_vector_t* offsets)
+{
+ if (!T || !P || !offsets)
+ return -EINVAL;
+
+ return _match_kmp(T->data, T->len, P->data, P->len, offsets);
+}
+
+int scf_string_match_kmp_cstr(const uint8_t* T, const uint8_t* P, scf_vector_t* offsets)
+{
+ if (!T || !P || !offsets)
+ return -EINVAL;
+
+ return _match_kmp(T, strlen(T), P, strlen(P), offsets);
+}
+
+int scf_string_match_kmp_cstr_len(const scf_string_t* T, const uint8_t* P, size_t Plen, scf_vector_t* offsets)
+{
+ if (!T || !P || 0 == Plen || !offsets)
+ return -EINVAL;
+
+ return _match_kmp(T->data, T->len, P, Plen, offsets);
+}
+
+int scf_string_get_offset(scf_string_t* str, const char* data, size_t len)
+{
+ int ret;
+
+ if (0 == str->len) {
+ if (scf_string_cat_cstr_len(str, data, len) < 0)
+ return -1;
+ return 0;
+ }
+
+ scf_vector_t* vec = scf_vector_alloc();
+ if (!vec)
+ return -ENOMEM;
+
+ ret = scf_string_match_kmp_cstr_len(str, data, len, vec);
+ if (ret < 0) {
+
+ } else if (0 == vec->size) {
+ ret = str->len;
+
+ if (scf_string_cat_cstr_len(str, data, len) < 0)
+ ret = -1;
+ } else
+ ret = (intptr_t)(vec->data[0]);
+
+ scf_vector_free(vec);
+ return ret;
+}
+
+#if 0
+int main(int argc, char* argv[])
+{
+ scf_string_t* s0 = scf_string_alloc();
+ scf_string_t* s1 = scf_string_cstr("hello, world!");
+ scf_string_t* s2 = scf_string_clone(s1);
+ printf("s0: %s\n", s0->data);
+ printf("s1: %s\n", s1->data);
+ printf("s2: %s\n", s2->data);
+
+ scf_string_copy(s0, s2);
+ printf("s0: %s\n", s0->data);
+
+ printf("s0 cmp s1: %d\n", scf_string_cmp(s0, s1));
+
+ scf_string_t* s3 = scf_string_cstr("ha ha ha!");
+ printf("s3: %s\n", s3->data);
+
+ scf_string_cat(s0, s3);
+ printf("s0: %s\n", s0->data);
+
+ scf_string_cat_cstr(s0, "he he he!");
+ printf("s0: %s\n", s0->data);
+
+ scf_string_t* s4 = scf_string_cstr("hello, world!");
+ scf_string_t* s5 = scf_string_cstr("ll");
+ scf_vector_t* vec = scf_vector_alloc();
+
+ int ret = scf_string_match_kmp(s4, s5, vec);
+ if (ret < 0) {
+ scf_loge("\n");
+ return -1;
+ }
+
+ int i;
+ for (i = 0; i < vec->size; i++) {
+
+ int offset = (intptr_t)vec->data[i];
+
+ printf("i: %d, offset: %d\n", i, offset);
+ }
+
+ scf_string_free(s0);
+ scf_string_free(s1);
+ scf_string_free(s2);
+ scf_string_free(s3);
+ return 0;
+}
+#endif
+
diff --git a/util/scf_string.h b/util/scf_string.h
new file mode 100644
index 0000000..cbf8717
--- /dev/null
+++ b/util/scf_string.h
@@ -0,0 +1,48 @@
+#ifndef SCF_STRING_H
+#define SCF_STRING_H
+
+#include"scf_vector.h"
+
+typedef struct {
+ int capacity;
+ size_t len;
+ char* data;
+} scf_string_t;
+
+
+scf_string_t* scf_string_alloc();
+
+scf_string_t* scf_string_clone(scf_string_t* s);
+
+scf_string_t* scf_string_cstr(const char* str);
+
+scf_string_t* scf_string_cstr_len(const char* str, size_t len);
+
+void scf_string_free(scf_string_t* s);
+
+void scf_string_print_bin(scf_string_t* s);
+
+int scf_string_fill_zero(scf_string_t* s0, size_t len);
+
+int scf_string_cmp(const scf_string_t* s0, const scf_string_t* s1);
+
+int scf_string_cmp_cstr(const scf_string_t* s0, const char* str);
+
+int scf_string_cmp_cstr_len(const scf_string_t* s0, const char* str, size_t len);
+
+int scf_string_copy(scf_string_t* s0, const scf_string_t* s1);
+
+int scf_string_cat(scf_string_t* s0, const scf_string_t* s1);
+
+int scf_string_cat_cstr(scf_string_t* s0, const char* str);
+
+int scf_string_cat_cstr_len(scf_string_t* s0, const char* str, size_t len);
+
+int scf_string_match_kmp(const scf_string_t* T, const scf_string_t* P, scf_vector_t* offsets);
+int scf_string_match_kmp_cstr(const uint8_t* T, const uint8_t* P, scf_vector_t* offsets);
+int scf_string_match_kmp_cstr_len(const scf_string_t* T, const uint8_t* P, size_t Plen, scf_vector_t* offsets);
+
+int scf_string_get_offset(scf_string_t* str, const char* data, size_t len);
+
+#endif
+
diff --git a/util/scf_vector.h b/util/scf_vector.h
new file mode 100644
index 0000000..d8abe21
--- /dev/null
+++ b/util/scf_vector.h
@@ -0,0 +1,212 @@
+#ifndef SCF_VECTOR_H
+#define SCF_VECTOR_H
+
+#include"scf_def.h"
+
+typedef struct {
+ int capacity;
+ int size;
+ void** data;
+} scf_vector_t;
+
+#undef NB_MEMBER_INC
+#define NB_MEMBER_INC 16
+
+static inline scf_vector_t* scf_vector_alloc()
+{
+ scf_vector_t* v = calloc(1, sizeof(scf_vector_t));
+ if (!v)
+ return NULL;
+
+ v->data = calloc(NB_MEMBER_INC, sizeof(void*));
+ if (!v->data) {
+ free(v);
+ v = NULL;
+ return NULL;
+ }
+
+ v->capacity = NB_MEMBER_INC;
+ return v;
+}
+
+static inline scf_vector_t* scf_vector_clone(scf_vector_t* origin)
+{
+ scf_vector_t* clone = calloc(1, sizeof(scf_vector_t));
+ if (!clone)
+ return NULL;
+
+ clone->data = calloc(origin->capacity, sizeof(void*));
+ if (!clone->data) {
+ free(clone);
+ clone = NULL;
+ return NULL;
+ }
+
+ clone->capacity = origin->capacity;
+ clone->size = origin->size;
+ memcpy(clone->data, origin->data, origin->size * sizeof(void*));
+ return clone;
+}
+
+static inline int scf_vector_cat(scf_vector_t* dst, scf_vector_t* src)
+{
+ if (!dst || !src)
+ return -EINVAL;
+
+ int size = dst->size + src->size;
+ if (size > dst->capacity) {
+
+ void* p = realloc(dst->data, sizeof(void*) * (size + NB_MEMBER_INC));
+ if (!p)
+ return -ENOMEM;
+
+ dst->data = p;
+ dst->capacity = size + NB_MEMBER_INC;
+ }
+
+ memcpy(dst->data + dst->size * sizeof(void*), src->data, src->size * sizeof(void*));
+ dst->size += src->size;
+ return 0;
+}
+
+static inline int scf_vector_add(scf_vector_t* v, void* node)
+{
+ if (!v || !v->data)
+ return -EINVAL;
+
+ assert(v->size <= v->capacity);
+
+ if (v->size == v->capacity) {
+ void* p = realloc(v->data, sizeof(void*) * (v->capacity + NB_MEMBER_INC));
+ if (!p)
+ return -ENOMEM;
+
+ v->data = p;
+ v->capacity += NB_MEMBER_INC;
+ }
+
+ v->data[v->size++] = node;
+ return 0;
+}
+
+static inline int scf_vector_add_front(scf_vector_t* v, void* node)
+{
+ int ret = scf_vector_add(v, node);
+ if (ret < 0)
+ return ret;
+
+ int i;
+ for (i = v->size - 2; i >= 0; i--)
+ v->data[i + 1] = v->data[i];
+
+ v->data[0] = node;
+ return 0;
+}
+
+static inline int scf_vector_del(scf_vector_t* v, void* node)
+{
+ if (!v || !v->data)
+ return -EINVAL;
+
+ assert(v->size <= v->capacity);
+
+ int i;
+ for (i = 0; i < v->size; i++) {
+
+ if (v->data[i] != node)
+ continue;
+
+ int j;
+ for (j = i + 1; j < v->size; j++)
+ v->data[j - 1] = v->data[j];
+
+ v->size--;
+
+ if (v->size + NB_MEMBER_INC * 2 < v->capacity) {
+ void* p = realloc(v->data, sizeof(void*) * (v->capacity - NB_MEMBER_INC));
+ if (p) {
+ v->data = p;
+ v->capacity -= NB_MEMBER_INC;
+ }
+ }
+ return 0;
+ }
+
+ return -1;
+}
+
+static inline void* scf_vector_find(const scf_vector_t* v, const void* node)
+{
+ int i;
+ for (i = 0; i < v->size; i++) {
+ if (node == v->data[i])
+ return v->data[i];
+ }
+ return NULL;
+}
+
+static inline int scf_vector_add_unique(scf_vector_t* v, void* node)
+{
+ if (!scf_vector_find(v, node))
+ return scf_vector_add(v, node);
+ return 0;
+}
+
+static inline void* scf_vector_find_cmp(const scf_vector_t* v, const void* node, int (*cmp)(const void*, const void*))
+{
+ int i;
+ for (i = 0; i < v->size; i++) {
+ if (0 == cmp(node, v->data[i]))
+ return v->data[i];
+ }
+ return NULL;
+}
+
+static inline int scf_vector_qsort(const scf_vector_t* v, int (*cmp)(const void*, const void*))
+{
+ if (!v || !v->data || 0 == v->size || !cmp)
+ return -EINVAL;
+
+ qsort(v->data, v->size, sizeof(void*), cmp);
+ return 0;
+}
+
+static inline void scf_vector_clear(scf_vector_t* v, void (*type_free)(void*))
+{
+ if (!v || !v->data)
+ return;
+
+ if (type_free) {
+ int i;
+ for (i = 0; i < v->size; i++) {
+ if (v->data[i]) {
+ type_free(v->data[i]);
+ v->data[i] = NULL;
+ }
+ }
+ }
+
+ v->size = 0;
+
+ if (v->capacity > NB_MEMBER_INC) {
+ void* p = realloc(v->data, sizeof(void*) * NB_MEMBER_INC);
+ if (p) {
+ v->data = p;
+ v->capacity = NB_MEMBER_INC;
+ }
+ }
+}
+
+static inline void scf_vector_free(scf_vector_t* v)
+{
+ if (v) {
+ if (v->data)
+ free(v->data);
+
+ free(v);
+ v = NULL;
+ }
+}
+
+#endif
+
--
2.25.1