1st commit for ABC (advanced browser core)
authoryu.dongliang <18588496441@163.com>
Thu, 19 Sep 2024 04:57:10 +0000 (12:57 +0800)
committeryu.dongliang <18588496441@163.com>
Thu, 19 Sep 2024 04:57:10 +0000 (12:57 +0800)
38 files changed:
LICENSE [new file with mode: 0644]
README.md [new file with mode: 0644]
examples/1.html [new file with mode: 0644]
examples/2.html [new file with mode: 0644]
html/Makefile [new file with mode: 0644]
html/abc_html.c [new file with mode: 0644]
html/abc_html.h [new file with mode: 0644]
html/abc_html_util.c [new file with mode: 0644]
html/abc_obj.c [new file with mode: 0644]
html/abc_obj.h [new file with mode: 0644]
html/main.c [new file with mode: 0644]
ui/Makefile [new file with mode: 0644]
ui/abc.h [new file with mode: 0644]
ui/abc_layout.c [new file with mode: 0644]
ui/abc_layout_a.c [new file with mode: 0644]
ui/abc_layout_body.c [new file with mode: 0644]
ui/abc_layout_div.c [new file with mode: 0644]
ui/abc_layout_h1.c [new file with mode: 0644]
ui/abc_layout_head.c [new file with mode: 0644]
ui/abc_layout_html.c [new file with mode: 0644]
ui/abc_layout_title.c [new file with mode: 0644]
ui/abc_render.c [new file with mode: 0644]
ui/abc_render_a.c [new file with mode: 0644]
ui/abc_render_a_href.c [new file with mode: 0644]
ui/abc_render_body.c [new file with mode: 0644]
ui/abc_render_div.c [new file with mode: 0644]
ui/abc_render_h1.c [new file with mode: 0644]
ui/abc_render_head.c [new file with mode: 0644]
ui/abc_render_html.c [new file with mode: 0644]
ui/abc_render_title.c [new file with mode: 0644]
ui/abc_ui.glade [new file with mode: 0644]
ui/main.c [new file with mode: 0644]
util/scf_def.h [new file with mode: 0644]
util/scf_list.h [new file with mode: 0644]
util/scf_stack.h [new file with mode: 0644]
util/scf_string.c [new file with mode: 0644]
util/scf_string.h [new file with mode: 0644]
util/scf_vector.h [new file with mode: 0644]

diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
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. <http://fsf.org/>
+ 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 (file)
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 (file)
index 0000000..e73bc36
--- /dev/null
@@ -0,0 +1,7 @@
+<html>
+<title>test</title>
+<body>
+<h1>html test 1</h1>
+<a href=../examples/2.html>test2</a>
+</body>
+</html>
diff --git a/examples/2.html b/examples/2.html
new file mode 100644 (file)
index 0000000..472f03f
--- /dev/null
@@ -0,0 +1,7 @@
+<html>
+<title>test</title>
+<body>
+<h1>html test 2</h1>
+<a href=../examples/1.html>test1</a>
+</body>
+</html>
diff --git a/html/Makefile b/html/Makefile
new file mode 100644 (file)
index 0000000..2e9a348
--- /dev/null
@@ -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 (file)
index 0000000..1c4791e
--- /dev/null
@@ -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) { // <! note >
+                       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 (file)
index 0000000..c335f48
--- /dev/null
@@ -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 (file)
index 0000000..5053387
--- /dev/null
@@ -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 (file)
index 0000000..f92dd19
--- /dev/null
@@ -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 (file)
index 0000000..431e6f4
--- /dev/null
@@ -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 (file)
index 0000000..b8459d1
--- /dev/null
@@ -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 (file)
index 0000000..b06f22c
--- /dev/null
@@ -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 (file)
index 0000000..347e88b
--- /dev/null
+++ b/ui/abc.h
@@ -0,0 +1,75 @@
+#ifndef ABC_H
+#define ABC_H
+
+#include"abc_html.h"
+
+#include <gtk/gtk.h>
+
+#define  GL_GLEXT_PROTOTYPES
+#include <GL/gl.h>
+#include <GL/glext.h>
+
+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 (file)
index 0000000..3ebf72c
--- /dev/null
@@ -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 (file)
index 0000000..0a917b4
--- /dev/null
@@ -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 (file)
index 0000000..f374316
--- /dev/null
@@ -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 (file)
index 0000000..6878cc0
--- /dev/null
@@ -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 (file)
index 0000000..e39d3c0
--- /dev/null
@@ -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 (file)
index 0000000..cb4d6c2
--- /dev/null
@@ -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 (file)
index 0000000..4615d4a
--- /dev/null
@@ -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 (file)
index 0000000..35e1965
--- /dev/null
@@ -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 (file)
index 0000000..d97b6c2
--- /dev/null
@@ -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 (file)
index 0000000..02b37ba
--- /dev/null
@@ -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 (file)
index 0000000..96365c0
--- /dev/null
@@ -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 (file)
index 0000000..2008e77
--- /dev/null
@@ -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 (file)
index 0000000..5768b28
--- /dev/null
@@ -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 (file)
index 0000000..8a94b5c
--- /dev/null
@@ -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 (file)
index 0000000..cef9af5
--- /dev/null
@@ -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 (file)
index 0000000..37f13ec
--- /dev/null
@@ -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 (file)
index 0000000..3c53e7a
--- /dev/null
@@ -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 (file)
index 0000000..383c82b
--- /dev/null
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface>
+  <requires lib="gtk+" version="3.20"/>
+  <object class="GtkWindow" id="main_window">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="title" translatable="yes">浏览器</property>
+    <property name="default_width">1280</property>
+    <property name="default_height">720</property>
+    <child type="titlebar">
+      <placeholder/>
+    </child>
+    <child>
+      <object class="GtkPaned" id="menu_workspace">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="orientation">vertical</property>
+        <property name="position">80</property>
+        <child>
+          <object class="GtkPaned">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkBox" id="box_pages">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <child>
+                  <object class="GtkButton">
+                    <property name="label">gtk-add</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="use_stock">True</property>
+                    <property name="always_show_image">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+              <packing>
+                <property name="resize">False</property>
+                <property name="shrink">True</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkPaned">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="position">243</property>
+                <child>
+                  <object class="GtkBox">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <child>
+                      <object class="GtkButton" id="button_back">
+                        <property name="label">gtk-go-back</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="sensitive">False</property>
+                        <property name="receives_default">True</property>
+                        <property name="use_stock">True</property>
+                        <property name="always_show_image">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkButton" id="button_forward">
+                        <property name="label">gtk-go-forward</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="sensitive">False</property>
+                        <property name="receives_default">True</property>
+                        <property name="use_stock">True</property>
+                        <property name="always_show_image">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkButton" id="button_refresh">
+                        <property name="label">gtk-refresh</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">True</property>
+                        <property name="use_stock">True</property>
+                        <property name="always_show_image">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="resize">False</property>
+                    <property name="shrink">True</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkSearchEntry" id="search_url">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="primary_icon_name">edit-find-symbolic</property>
+                    <property name="primary_icon_activatable">False</property>
+                    <property name="primary_icon_sensitive">False</property>
+                  </object>
+                  <packing>
+                    <property name="resize">True</property>
+                    <property name="shrink">True</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="resize">True</property>
+                <property name="shrink">True</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="resize">False</property>
+            <property name="shrink">True</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkGLArea" id="gl_area">
+            <property name="visible">True</property>
+            <property name="app_paintable">True</property>
+            <property name="can_focus">False</property>
+          </object>
+          <packing>
+            <property name="resize">True</property>
+            <property name="shrink">True</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/ui/main.c b/ui/main.c
new file mode 100644 (file)
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 (file)
index 0000000..55b1def
--- /dev/null
@@ -0,0 +1,73 @@
+#ifndef SCF_DEF_H
+#define SCF_DEF_H
+
+#include<stdio.h>
+#include<stdint.h>
+#include<stdlib.h>
+#include<stddef.h>
+#include<string.h>
+#include<assert.h>
+#include<errno.h>
+#include<time.h>
+#include<unistd.h>
+#include<limits.h>
+#include<math.h>
+
+#if 1
+#include<sys/time.h>
+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 (file)
index 0000000..4b5812c
--- /dev/null
@@ -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 (file)
index 0000000..24ab942
--- /dev/null
@@ -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 (file)
index 0000000..a0b4587
--- /dev/null
@@ -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 (file)
index 0000000..cbf8717
--- /dev/null
@@ -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 (file)
index 0000000..d8abe21
--- /dev/null
@@ -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
+