From 63063136a0daaacf76cf16d5663cc39325a126d8 Mon Sep 17 00:00:00 2001 From: "yu.dongliang" <18588496441@163.com> Date: Thu, 19 Sep 2024 12:57:10 +0800 Subject: [PATCH 1/1] 1st commit for ABC (advanced browser core) --- LICENSE | 165 ++++++++++++ README.md | 52 ++++ examples/1.html | 7 + examples/2.html | 7 + html/Makefile | 17 ++ html/abc_html.c | 563 +++++++++++++++++++++++++++++++++++++++++ html/abc_html.h | 43 ++++ html/abc_html_util.c | 87 +++++++ html/abc_obj.c | 144 +++++++++++ html/abc_obj.h | 70 +++++ html/main.c | 31 +++ ui/Makefile | 40 +++ ui/abc.h | 75 ++++++ ui/abc_layout.c | 86 +++++++ ui/abc_layout_a.c | 52 ++++ ui/abc_layout_body.c | 6 + ui/abc_layout_div.c | 6 + ui/abc_layout_h1.c | 52 ++++ ui/abc_layout_head.c | 6 + ui/abc_layout_html.c | 6 + ui/abc_layout_title.c | 6 + ui/abc_render.c | 231 +++++++++++++++++ ui/abc_render_a.c | 190 ++++++++++++++ ui/abc_render_a_href.c | 215 ++++++++++++++++ ui/abc_render_body.c | 19 ++ ui/abc_render_div.c | 19 ++ ui/abc_render_h1.c | 179 +++++++++++++ ui/abc_render_head.c | 19 ++ ui/abc_render_html.c | 19 ++ ui/abc_render_title.c | 19 ++ ui/abc_ui.glade | 157 ++++++++++++ ui/main.c | 348 +++++++++++++++++++++++++ util/scf_def.h | 73 ++++++ util/scf_list.h | 81 ++++++ util/scf_stack.h | 60 +++++ util/scf_string.c | 390 ++++++++++++++++++++++++++++ util/scf_string.h | 48 ++++ util/scf_vector.h | 212 ++++++++++++++++ 38 files changed, 3800 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 examples/1.html create mode 100644 examples/2.html create mode 100644 html/Makefile create mode 100644 html/abc_html.c create mode 100644 html/abc_html.h create mode 100644 html/abc_html_util.c create mode 100644 html/abc_obj.c create mode 100644 html/abc_obj.h create mode 100644 html/main.c create mode 100644 ui/Makefile create mode 100644 ui/abc.h create mode 100644 ui/abc_layout.c create mode 100644 ui/abc_layout_a.c create mode 100644 ui/abc_layout_body.c create mode 100644 ui/abc_layout_div.c create mode 100644 ui/abc_layout_h1.c create mode 100644 ui/abc_layout_head.c create mode 100644 ui/abc_layout_html.c create mode 100644 ui/abc_layout_title.c create mode 100644 ui/abc_render.c create mode 100644 ui/abc_render_a.c create mode 100644 ui/abc_render_a_href.c create mode 100644 ui/abc_render_body.c create mode 100644 ui/abc_render_div.c create mode 100644 ui/abc_render_h1.c create mode 100644 ui/abc_render_head.c create mode 100644 ui/abc_render_html.c create mode 100644 ui/abc_render_title.c create mode 100644 ui/abc_ui.glade create mode 100644 ui/main.c create mode 100644 util/scf_def.h create mode 100644 util/scf_list.h create mode 100644 util/scf_stack.h create mode 100644 util/scf_string.c create mode 100644 util/scf_string.h create mode 100644 util/scf_vector.h diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/README.md b/README.md new file mode 100644 index 0000000..42649ca --- /dev/null +++ b/README.md @@ -0,0 +1,52 @@ +# abc, Advanced Browser Core +------- +#### 介绍 +一个自己编写的浏览器框架, + +1,使用GTK做为主界面, + +2,使用cairo做为2D矢量图形库, + +3, 使用OpenGL渲染HTML对象模型, + +4,运行在Linux系统上, + +a browser core written by me. + +1,GUI based on GTK, + +2,cairo for 2D drawing, + +3, OpenGL for rendering HTML Object Model, + +4, run in Linux, + +------ +#### 软件架构 + +#### 安装教程, usage + +1. 用git下载到本机,命令为: + +git clone http://baseworks.info/gitweb/abc.git + +download it with git, command above. + +2. 在abc/ui目录下直接执行make,即可获得编译器的可执行文件a.out,命令为: + +cd abc/ui + +make + +in directory abc/ui run make, then get the executable file named 'a.out'. + +3. 运行abc/examples目录下的html测试样例,即可看到运行效果,例如: + +./a.out ../examples/1.html + +细节在源码里:( +more details in source code. + +#### 参与贡献 + +yu.dongliang (底层技术栈) diff --git a/examples/1.html b/examples/1.html new file mode 100644 index 0000000..e73bc36 --- /dev/null +++ b/examples/1.html @@ -0,0 +1,7 @@ + +test + +

html test 1

+test2 + + diff --git a/examples/2.html b/examples/2.html new file mode 100644 index 0000000..472f03f --- /dev/null +++ b/examples/2.html @@ -0,0 +1,7 @@ + +test + +

html test 2

+test1 + + diff --git a/html/Makefile b/html/Makefile new file mode 100644 index 0000000..2e9a348 --- /dev/null +++ b/html/Makefile @@ -0,0 +1,17 @@ +CFILES += ../util/scf_string.c + +CFILES += main.c +CFILES += abc_obj.c +CFILES += abc_html.c +CFILES += abc_html_util.c + +CFLAGS += -g +CFLAGS += -I../util + +LDFLAGS += -ldl + +all: + gcc $(CFLAGS) $(CFILES) $(LDFLAGS) + +clean: + rm *.o diff --git a/html/abc_html.c b/html/abc_html.c new file mode 100644 index 0000000..1c4791e --- /dev/null +++ b/html/abc_html.c @@ -0,0 +1,563 @@ +#include"abc_html.h" + +typedef struct { + char* name; + char* value; + int type; +} html_attr_t; + +typedef struct { + char* name; + int type; + + int n_attrs; + html_attr_t* attrs; +} html_label_t; + + +static html_attr_t h1_attrs[] = +{ + {"font", "Liberation Serif", ABC_HTML_ATTR_FONT}, + {"font-size", "32", ABC_HTML_ATTR_FONT_SIZE}, +}; + +static html_attr_t a_attrs[] = +{ + {"href", "", ABC_HTML_ATTR_HREF}, + {"font", "serif", ABC_HTML_ATTR_FONT}, + {"font-size", "28", ABC_HTML_ATTR_FONT_SIZE}, + {"font-color", "blue", ABC_HTML_ATTR_FONT_COLOR}, +}; + +static html_label_t html_labels[] = +{ + {"html", ABC_HTML, 0, NULL}, + {"title", ABC_HTML_TITLE, 0, NULL}, + {"head", ABC_HTML_HEAD, 0, NULL}, + {"body", ABC_HTML_BODY, 0, NULL}, + + {"div", ABC_HTML_DIV, 0, NULL}, + + {"h1", ABC_HTML_H1, sizeof(h1_attrs) / sizeof(h1_attrs[0]), h1_attrs}, + {"a", ABC_HTML_A, sizeof(a_attrs) / sizeof(a_attrs[0]), a_attrs}, +}; + +static int __html_parse_obj(abc_html_t* html, abc_char_t* c); + + +static html_label_t* __html_find_label(const char* name) +{ + html_label_t* label; + + int i; + for (i = 0; i < sizeof(html_labels) / sizeof(html_labels[0]); i++) { + label = &html_labels[i]; + + if (!strcmp(label->name, name)) + return label; + } + + return NULL; +} + +static int __html_load_attrs(abc_obj_t* obj, html_attr_t* attrs, int n_attrs) +{ + abc_obj_t* attr; + + int i; + for (i = 0; i < n_attrs; i++) { + + attr = abc_obj_alloc(NULL, 0, 0, attrs[i].type); + if (!attr) + return -ENOMEM; + + attr->key = scf_string_cstr(attrs[i].name); + if (!attr->key) { + abc_obj_free(attr); + return -ENOMEM; + } + + attr->value = scf_string_cstr(attrs[i].value); + if (!attr->value) { + abc_obj_free(attr); + return -ENOMEM; + } + + scf_list_add_tail(&obj->attrs, &attr->list); + } + + return 0; +} + +int abc_html_open(abc_html_t** pp, const char* path) +{ + if (!pp || !path) + return -EINVAL; + + abc_html_t* html = calloc(1, sizeof(abc_html_t)); + if (!html) + return -ENOMEM; + + html->fp = fopen(path, "r"); + if (!html->fp) { + char cwd[4096]; + getcwd(cwd, 4095); + scf_loge("path: %s, errno: %d, pwd: %s\n", path, errno, cwd); + + free(html); + return -1; + } + + html->file = scf_string_cstr(path); + if (!html->file) { + fclose(html->fp); + free(html); + return -ENOMEM; + } + + html->n_lines = 1; + + *pp = html; + return 0; +} + +void abc_html_close(abc_html_t* html) +{ + abc_char_t* c; + + if (html) { + while ( html->tmp_list) { + c = html->tmp_list; + + html->tmp_list = c->next; + free(c); + } + + if (html->root) + abc_obj_free(html->root); + + if (html->fp) + fclose(html->fp); + + if (html->file) + scf_string_free(html->file); + + free(html); + } +} + +static int __html_parse_text(abc_html_t* html, abc_obj_t* obj) +{ + scf_string_t* text = scf_string_alloc(); + if (!text) + return -ENOMEM; + + abc_char_t* c; + abc_char_t* c2; + + while (1) { + c = __html_pop_char(html); + if (!c) { + scf_loge("\n"); + scf_string_free(text); + return -1; + } + + html->pos += c->len; + + if ('<' == c->c) + break; + + if ('\n' == c->c) { + html->n_lines++; + html->pos = 0; + free(c); + continue; + } + + scf_string_cat_cstr_len(text, c->utf8, c->len); + free(c); + c = NULL; + } + + c2 = __html_pop_char(html); + if (!c2) { + scf_loge("\n"); + free(c); + scf_string_free(text); + return -1; + } + + if ('a' <= c2->c && c2->c <= 'z') { + free(c); + c = NULL; + scf_string_free(text); + + int ret = __html_parse_obj(html, c2); + if (ret < 0) { + scf_loge("\n"); + return ret; + } + + return __html_parse_text(html, obj); + + } else if ('/' != c2->c) { + free(c); + c = NULL; + scf_string_free(text); + + scf_loge("invalid char '%c:%#x' in HTML attribute, file: %s, line: %d\n", + c2->c, c2->c, html->file->data, html->n_lines); + + free(c2); + return -1; + } + + html->pos++; + + free(c2); + free(c); + c2 = NULL; + c = NULL; + + if (text->len > 0) + obj->text = text; + else + scf_string_free(text); + text = NULL; + + // check the end label + scf_string_t* end = scf_string_alloc(); + if (!end) + return -ENOMEM; + + while (1) { + c = __html_pop_char(html); + if (!c) { + scf_loge("\n"); + scf_string_free(end); + return -1; + } + + html->pos += c->len; + + if ('>' == c->c) + break; + + scf_string_cat_cstr_len(end, c->utf8, c->len); + free(c); + c = NULL; + } + + html->pos++; + + free(c); + c = NULL; + + int ret = 0; + if (scf_string_cmp(obj->key, end)) { + ret = -1; + scf_loge("end label '%s' file: %s, line: %d, NOT for label '%s' line: %d\n", + end->data, html->file->data, html->n_lines, obj->key->data, obj->line); + } + + scf_string_free(end); + end = NULL; + + return ret; +} + +static int __html_parse_value(abc_html_t* html, abc_obj_t* attr) +{ + scf_string_t* value = scf_string_alloc(); + if (!value) + return -ENOMEM; + + abc_char_t* c = NULL; + + int flag = 0; + + while (1) { + c = __html_pop_char(html); + if (!c) { + scf_loge("\n"); + scf_string_free(value); + return -1; + } + + html->pos += c->len; + + if ((!flag && ' ' == c->c) || '>' == c->c) + break; + + if ('\"' == c->c) + flag = !flag; + else + scf_string_cat_cstr_len(value, c->utf8, c->len); + + free(c); + c = NULL; + } + + int tmp = c->c; + + free(c); + c = NULL; + + if (attr->value) + scf_string_free(attr->value); + + attr->value = value; + return '>' == tmp; +} + +static int __html_parse_attr2(abc_html_t* html, abc_obj_t* obj, const html_attr_t* attrs, int n_attrs) +{ + scf_string_t* key = scf_string_alloc(); + if (!key) + return -ENOMEM; + + abc_char_t* c = NULL; + + while (1) { + c = __html_pop_char(html); + if (!c) { + scf_loge("\n"); + scf_string_free(key); + return -1; + } + + html->pos += c->len; + + if ('=' == c->c) + break; + + if ('_' == c->c + || '-' == c->c + || ('a' <= c->c && 'z' >= c->c)) + scf_string_cat_cstr_len(key, c->utf8, c->len); + else { + scf_loge("invalid char in HTML attribute, file: %s, line: %d\n", + html->file->data, html->n_lines); + free(c); + scf_string_free(key); + return -1; + } + + free(c); + c = NULL; + } + + int tmp = c->c; + + free(c); + c = NULL; + + scf_list_t* l; + abc_obj_t* attr; + + for (l = scf_list_head(&obj->attrs); l != scf_list_sentinel(&obj->attrs); l = scf_list_next(l)) { + attr = scf_list_data(l, abc_obj_t, list); + + if (!strcmp(attr->key->data, key->data)) + break; + } + + if (l == scf_list_sentinel(&obj->attrs)) { + + scf_loge("invalid HTML attribute '%s' in file: %s, line: %d\n", key->data, html->file->data, html->n_lines); + scf_string_free(key); + return -1; + } + + scf_string_free(key); + key = NULL; + + assert(!attr->file); + + attr->file = scf_string_clone(html->file); + if (!attr->file) + return -ENOMEM; + + attr->line = html->n_lines; + attr->pos = html->pos; + + return __html_parse_value(html, attr); +} + +static int __html_parse_attr(abc_html_t* html, abc_obj_t* obj, const html_attr_t* attrs, int n_attrs) +{ + int ret = 0; + + while (!ret) { + ret = __html_parse_attr2(html, obj, attrs, n_attrs); + if (ret < 0) + return ret; + } + + return 0; +} + +static int __html_parse_obj(abc_html_t* html, abc_char_t* c) +{ + html_label_t* label; + abc_obj_t* obj; + + scf_string_t* key = scf_string_cstr_len(c->utf8, c->len); + + html->pos += c->len; + free(c); + c = NULL; + + if (!key) + return -ENOMEM; + + while (1) { + c = __html_pop_char(html); + if (!c) { + scf_loge("\n"); + scf_string_free(key); + return -1; + } + + html->pos += c->len; + + if (' ' == c->c || '>' == c->c) + break; + + if ('\n' == c->c) { + scf_loge("%c:%#x, html->n_lines: %d, pos: %d\n", c->c, c->c, html->n_lines, html->pos); + free(c); + return -1; + } + + scf_string_cat_cstr_len(key, c->utf8, c->len); + free(c); + c = NULL; + } + + int tmp = c->c; + + free(c); + c = NULL; + + label = __html_find_label(key->data); + if (!label) { + scf_loge("invalid HTML label '%s' in file: %s, line: %d\n", + key->data, html->file->data, html->n_lines); + scf_string_free(key); + return -1; + } + + obj = abc_obj_alloc(html->file, html->n_lines, html->pos, label->type); + if (!obj) { + scf_string_free(key); + return -ENOMEM; + } + + obj->key = key; + key = NULL; + + int ret = __html_load_attrs(obj, label->attrs, label->n_attrs); + if (ret < 0) { + abc_obj_free(obj); + return ret; + } + + if (' ' == tmp) { + ret = __html_parse_attr(html, obj, label->attrs, label->n_attrs); + if (ret < 0) { + abc_obj_free(obj); + return ret; + } + } + + scf_logi("key: %s\n", obj->key->data); + + if (!html->root) { + html->root = obj; + html->current = obj; + } else { + assert(html->current); + + scf_list_add_tail(&html->current->childs, &obj->list); + + obj->parent = html->current; + html->current = obj; + } + + ret = __html_parse_text(html, obj); + + html->current = obj->parent; + return ret; +} + +int abc_html_parse(abc_html_t* html) +{ + abc_char_t* c; + + while (1) { + c = __html_pop_char(html); + if (!c) + return -1; + + if (EOF == c->c) { + free(c); + html->current = NULL; + return 0; + } + + if ('\n' == c->c) { + html->n_lines++; + html->pos = 0; + continue; + } + + if ('<' != c->c) { + scf_loge("%c:%#x, html->n_lines: %d, pos: %d\n", c->c, c->c, html->n_lines, html->pos); + free(c); + return -1; + } + + free(c); + + c = __html_pop_char(html); + if ('!' == c->c) { // + free(c); + + while (1) { + c = __html_pop_char(html); + + int tmp = c->c; + free(c); + + if ('>' == tmp) + break; + + if ('\n' == tmp) { + scf_loge("%c:%#x, html->n_lines: %d, pos: %d\n", tmp, tmp, html->n_lines, html->pos); + return -1; + } + } + + c = NULL; + continue; + } + + if ('a' <= c->c && 'z' >= c->c) { + + int ret = __html_parse_obj(html, c); + if (ret < 0) { + scf_loge("html->n_lines: %d, pos: %d\n", html->n_lines, html->pos); + return ret; + } + + } else { + scf_loge("%c:%#x, html->n_lines: %d, pos: %d\n", c->c, c->c, html->n_lines, html->pos); + free(c); + return -1; + } + } + + return -1; +} diff --git a/html/abc_html.h b/html/abc_html.h new file mode 100644 index 0000000..c335f48 --- /dev/null +++ b/html/abc_html.h @@ -0,0 +1,43 @@ +#ifndef ABC_HTML_H +#define ABC_HTML_H + +#include"abc_obj.h" + +typedef struct abc_char_s abc_char_t; +typedef struct abc_html_s abc_html_t; + +#define ABC_UTF8_MAX 6 +struct abc_char_s +{ + abc_char_t* next; + int c; + + int len; + uint8_t utf8[ABC_UTF8_MAX]; +}; + +struct abc_html_s +{ + scf_list_t list; + + abc_char_t* tmp_list; + + abc_obj_t* root; + abc_obj_t* current; + + FILE* fp; + + scf_string_t* file; + int n_lines; + int pos; +}; + +int abc_html_open (abc_html_t** pp, const char* path); +void abc_html_close(abc_html_t* html); + +int abc_html_parse(abc_html_t* html); + +abc_char_t* __html_pop_char (abc_html_t* html); +void __html_push_char(abc_html_t* html, abc_char_t* c); + +#endif diff --git a/html/abc_html_util.c b/html/abc_html_util.c new file mode 100644 index 0000000..5053387 --- /dev/null +++ b/html/abc_html_util.c @@ -0,0 +1,87 @@ +#include"abc_html.h" + +abc_char_t* __html_pop_char(abc_html_t* html) +{ + assert(html); + assert(html->fp); + + abc_char_t* c; + + if (html->tmp_list) { + c = html->tmp_list; + html->tmp_list = c->next; + return c; + } + + c = malloc(sizeof(abc_char_t)); + if (!c) + return NULL; + + int ret = fgetc(html->fp); + if (EOF == ret) { + c->c = ret; + return c; + } + + if (ret < 0x80) { + c->c = ret; + c->len = 1; + c->utf8[0] = ret; + return c; + } + + if (0x6 == (ret >> 5)) { + c->c = ret & 0x1f; + c->len = 2; + + } else if (0xe == (ret >> 4)) { + c->c = ret & 0xf; + c->len = 3; + + } else if (0x1e == (ret >> 3)) { + c->c = ret & 0x7; + c->len = 4; + + } else if (0x3e == (ret >> 2)) { + c->c = ret & 0x3; + c->len = 5; + + } else if (0x7e == (ret >> 1)) { + c->c = ret & 0x1; + c->len = 6; + } else { + scf_loge("utf8 first byte wrong %#x, file: %s, line: %d\n", ret, html->file->data, html->n_lines); + free(c); + return NULL; + } + + c->utf8[0] = ret; + + int i; + for (i = 1; i < c->len; i++) { + + ret = fgetc(html->fp); + + if (0x2 == (ret >> 6)) { + c->c <<= 6; + c->c |= ret & 0x3f; + + c->utf8[i] = ret; + } else { + scf_loge("utf8 byte[%d] wrong %#x, file: %s, line: %d\n", i + 1, ret, html->file->data, html->n_lines); + free(c); + return NULL; + } + } + + return c; +} + +void __html_push_char(abc_html_t* html, abc_char_t* c) +{ + assert(html); + assert(c); + + c->next = html->tmp_list; + html->tmp_list = c; +} diff --git a/html/abc_obj.c b/html/abc_obj.c new file mode 100644 index 0000000..f92dd19 --- /dev/null +++ b/html/abc_obj.c @@ -0,0 +1,144 @@ +#include"abc_obj.h" + +abc_obj_t* abc_obj_alloc(scf_string_t* file, int line, int pos, int type) +{ + abc_obj_t* obj = calloc(1, sizeof(abc_obj_t)); + if (!obj) + return NULL; + + if (file) { + obj->file = scf_string_clone(file); + + if (!obj->file) { + free(obj); + return NULL; + } + } + + scf_list_init(&obj->list); + scf_list_init(&obj->attrs); + scf_list_init(&obj->childs); + + obj->type = type; + + obj->line = line; + obj->pos = pos; + + return obj; +} + +void abc_obj_free(abc_obj_t* obj) +{ + if (obj) { + if (obj->key) + scf_string_free(obj->key); + + if (obj->value) + scf_string_free(obj->value); + + if (obj->file) + scf_string_free(obj->file); + + scf_list_clear(&obj->attrs, abc_obj_t, list, abc_obj_free); + scf_list_clear(&obj->childs, abc_obj_t, list, abc_obj_free); + + free(obj); + } +} + +abc_obj_t* abc_obj_find(abc_obj_t* root, int x, int y) +{ + scf_list_t* l; + abc_obj_t* child; + abc_obj_t* obj; + + if (x < root->x + || x > root->x + root->w + || y < root->y + || y > root->y + root->h) + return NULL; + + for (l = scf_list_head(&root->childs); l != scf_list_sentinel(&root->childs); l = scf_list_next(l)) { + child = scf_list_data(l, abc_obj_t, list); + + obj = abc_obj_find(child, x, y); + if (obj) + return obj; + } + + return root; +} + +int abc_obj_set_attr(abc_obj_t* obj, int key, const char* value) +{ + scf_string_t* s; + scf_list_t* l; + abc_obj_t* attr; + + for (l = scf_list_head(&obj->attrs); l != scf_list_sentinel(&obj->attrs); l = scf_list_next(l)) { + attr = scf_list_data(l, abc_obj_t, list); + + if (attr->type == key) { + s = scf_string_cstr(value); + if (s) { + scf_string_free(attr->value); + attr->value = s; + return 0; + } + return -ENOMEM; + } + } + + return -EINVAL; +} + +abc_obj_t* abc_obj_get_attr(abc_obj_t* obj, int key) +{ + scf_list_t* l; + abc_obj_t* attr; + + for (l = scf_list_head(&obj->attrs); l != scf_list_sentinel(&obj->attrs); l = scf_list_next(l)) { + attr = scf_list_data(l, abc_obj_t, list); + + if (attr->type == key) + return attr; + } + + return NULL; +} + +void abc_obj_print(abc_obj_t* obj) +{ + scf_list_t* l; + abc_obj_t* attr; + abc_obj_t* child; + + if (!obj) + return; + + if (obj->value) + printf(" %s=%s", obj->key->data, obj->value->data); + else + printf("<%s", obj->key->data); + + for (l = scf_list_head(&obj->attrs); l != scf_list_sentinel(&obj->attrs); l = scf_list_next(l)) { + attr = scf_list_data(l, abc_obj_t, list); + + abc_obj_print(attr); + } + + if (!obj->value) + printf(">\n"); + + for (l = scf_list_head(&obj->childs); l != scf_list_sentinel(&obj->childs); l = scf_list_next(l)) { + child = scf_list_data(l, abc_obj_t, list); + + abc_obj_print(child); + } + + if (obj->text) + printf("%s\n", obj->text->data); + + if (!obj->value) + printf("\n", obj->key->data); +} diff --git a/html/abc_obj.h b/html/abc_obj.h new file mode 100644 index 0000000..431e6f4 --- /dev/null +++ b/html/abc_obj.h @@ -0,0 +1,70 @@ +#ifndef ABC_OBJ_H +#define ABC_OBJ_H + +#include"scf_string.h" +#include"scf_list.h" + +typedef struct abc_obj_s abc_obj_t; + +enum abc_objs +{ + ABC_HTML, + ABC_HTML_TITLE, + ABC_HTML_HEAD, + ABC_HTML_BODY, + + // 4 + ABC_HTML_DIV, + ABC_HTML_H1, + ABC_HTML_A, + ABC_HTML_A_HREF, + + ABC_HTML_NB, // total HTML objects + + ABC_HTML_ATTR_ID, + ABC_HTML_ATTR_HREF, + + ABC_HTML_ATTR_FONT, + ABC_HTML_ATTR_FONT_SIZE, + ABC_HTML_ATTR_FONT_COLOR, +}; + +struct abc_obj_s +{ + scf_list_t list; + + scf_list_t attrs; + scf_list_t childs; + + abc_obj_t* parent; + + void* gtk_builder; + + int type; + + int x; + int y; + int w; + int h; + + scf_string_t* key; + scf_string_t* value; + scf_string_t* text; + + scf_string_t* file; // file name + int line; // line + int pos; // position + + uint8_t move_flag :1; + uint8_t click_flag:1; +}; + +abc_obj_t* abc_obj_alloc(scf_string_t* file, int line, int pos, int type); +void abc_obj_free (abc_obj_t* obj); +abc_obj_t* abc_obj_find (abc_obj_t* root, int x, int y); +void abc_obj_print(abc_obj_t* obj); + +int abc_obj_set_attr(abc_obj_t* obj, int key, const char* value); +abc_obj_t* abc_obj_get_attr(abc_obj_t* obj, int key); + +#endif diff --git a/html/main.c b/html/main.c new file mode 100644 index 0000000..b8459d1 --- /dev/null +++ b/html/main.c @@ -0,0 +1,31 @@ +#include"abc_html.h" + +int main(int argc, char* argv[]) +{ + abc_html_t* html = NULL; + + if (argc < 2) { + scf_loge("\n"); + return -1; + } + + if (abc_html_open(&html, argv[1]) < 0) { + scf_loge("\n"); + return -1; + } + + if (abc_html_parse(html) < 0) { + scf_loge("\n"); + return -1; + } + + printf("\n"); + + if (html->root) + abc_obj_print(html->root); + + printf("\n"); + + scf_logi("main ok\n"); + return 0; +} diff --git a/ui/Makefile b/ui/Makefile new file mode 100644 index 0000000..b06f22c --- /dev/null +++ b/ui/Makefile @@ -0,0 +1,40 @@ +CFILES += main.c + +CFILES += abc_layout.c +CFILES += abc_layout_html.c +CFILES += abc_layout_title.c +CFILES += abc_layout_head.c +CFILES += abc_layout_body.c +CFILES += abc_layout_div.c +CFILES += abc_layout_h1.c +CFILES += abc_layout_a.c + +CFILES += abc_render.c +CFILES += abc_render_html.c +CFILES += abc_render_title.c +CFILES += abc_render_head.c +CFILES += abc_render_body.c +CFILES += abc_render_div.c +CFILES += abc_render_h1.c +CFILES += abc_render_a.c +CFILES += abc_render_a_href.c + +CFILES += ../html/abc_html.c +CFILES += ../html/abc_html_util.c +CFILES += ../html/abc_obj.c + +CFILES += ../util/scf_string.c + +CFLAGS += -g -O3 +CFLAGS += -I./ +CFLAGS += -I../html +CFLAGS += -I../util +CFLAGS += `pkg-config --cflags gtk+-3.0` + +LDFLAGS += -lavformat -lavcodec -lavfilter -lavutil +LDFLAGS += -lm +LDFLAGS += -lGL +LDFLAGS += `pkg-config --libs gtk+-3.0` + +all: + gcc $(CFLAGS) $(CFILES) $(LDFLAGS) diff --git a/ui/abc.h b/ui/abc.h new file mode 100644 index 0000000..347e88b --- /dev/null +++ b/ui/abc.h @@ -0,0 +1,75 @@ +#ifndef ABC_H +#define ABC_H + +#include"abc_html.h" + +#include + +#define GL_GLEXT_PROTOTYPES +#include +#include + +typedef struct abc_render_s abc_render_t; +typedef struct abc_layout_s abc_layout_t; +typedef struct abc_ctx_s abc_ctx_t; + +typedef int (*abc_layout_pt)(abc_layout_t* layout, abc_obj_t* obj, int width, int height); + +struct abc_layout_s +{ + void *priv; +}; + +struct abc_render_s +{ + int type; + + int (*draw)(abc_render_t* render, abc_obj_t* obj, int width, int height); + int (*fini)(abc_render_t* render); + + void *priv; +}; + +struct abc_ctx_s +{ + scf_list_t html_list; + abc_html_t* current; + + GtkBuilder* builder; + + GtkGLArea* gl_area; + + GtkButton* back; + GtkButton* forward; + GtkButton* refresh; +}; + +#define ABC_CTX_INIT(__ctx) {SCF_LIST_INIT((__ctx).html_list), NULL, NULL, NULL, NULL, NULL, NULL} + +static const GLfloat vert_array[] = { + -1.0f, -1.0f, + 1.0f, -1.0f, + -1.0f, 1.0f, + 1.0f, 1.0f +}; + +static const GLfloat texture_array[] = { + 0.0f, 1.0f, + 1.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f, +}; + +int abc_renders_fini(); +int abc_render_draw(abc_obj_t* obj, int width, int height); + +int abc_render_root(abc_ctx_t* ctx, abc_obj_t* root, int width, int height); +int abc_layout_root(abc_ctx_t* ctx, abc_obj_t* root, int width, int height); + +void __compute_mvp(float *res, float phi, float theta, float psi); + +int __init_texture(GLuint* ptex, GLenum format, int w, int h, uint8_t* data); + +int __init_program(GLuint* pprog, const char* vert_shader, const char* frag_shader); + +#endif diff --git a/ui/abc_layout.c b/ui/abc_layout.c new file mode 100644 index 0000000..3ebf72c --- /dev/null +++ b/ui/abc_layout.c @@ -0,0 +1,86 @@ +#include"abc.h" + +int abc_layout_html (abc_layout_t* layout, abc_obj_t* root, int width, int height); +int abc_layout_title(abc_layout_t* layout, abc_obj_t* root, int width, int height); +int abc_layout_head (abc_layout_t* layout, abc_obj_t* root, int width, int height); +int abc_layout_body (abc_layout_t* layout, abc_obj_t* root, int width, int height); + +int abc_layout_div (abc_layout_t* layout, abc_obj_t* root, int width, int height); +int abc_layout_h1 (abc_layout_t* layout, abc_obj_t* root, int width, int height); +int abc_layout_a (abc_layout_t* layout, abc_obj_t* root, int width, int height); + +static abc_layout_pt abc_layouts[ABC_HTML_NB] = +{ + abc_layout_html, + abc_layout_title, + abc_layout_head, + abc_layout_body, + + // 4 + abc_layout_div, + abc_layout_h1, + abc_layout_a, +}; + +int abc_layout_obj(abc_layout_t* layout, abc_obj_t* obj, int width, int height) +{ + if (obj->type >= ABC_HTML_NB) + return -EINVAL; + + abc_layout_pt f = abc_layouts[obj->type]; + if (!f) + return -EINVAL; + + return f(layout, obj, width, height); +} + +int abc_layout_root(abc_ctx_t* abc, abc_obj_t* root, int width, int height) +{ + scf_list_t* l; + abc_obj_t* child; + + if (!root) + return -EINVAL; + + if (width <= 0 || height <= 0) + return -EINVAL; + + int x = 4; + int y = 4; + int w = 0; + int h = 0; + + for (l = scf_list_head(&root->childs); l != scf_list_sentinel(&root->childs); l = scf_list_next(l)) { + child = scf_list_data(l, abc_obj_t, list); + + int ret = abc_layout_root(NULL, child, width, height); + if (ret < 0) + return ret; + + child->x = x; + child->y = y; + + scf_logd("key: %s, x: %d, y: %d, w: %d, h: %d\n\n", child->key->data, child->x, child->y, child->w, child->h); + + y = child->y + child->h; + + if (w < child->x + child->w) + w = child->x + child->w; + + if (h < child->y + child->h) + h = child->y + child->h; + } + + int ret = abc_layout_obj(NULL, root, width, height); + if (ret < 0) + return ret; + + if (root->w < w) + root->w = w; + + if (root->h < h) + root->h = h; + + scf_logd("key: %s, x: %d, y: %d, w: %d, h: %d\n", root->key->data, root->x, root->y, root->w, root->h); + return 0; +} diff --git a/ui/abc_layout_a.c b/ui/abc_layout_a.c new file mode 100644 index 0000000..0a917b4 --- /dev/null +++ b/ui/abc_layout_a.c @@ -0,0 +1,52 @@ +#include"abc.h" + +int abc_layout_a(abc_layout_t* layout, abc_obj_t* obj, int width, int height) +{ + cairo_text_extents_t extents; + cairo_surface_t* surface; + cairo_t* cr; + + scf_list_t* l; + abc_obj_t* attr; + + surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + cr = cairo_create(surface); + + for (l = scf_list_head(&obj->attrs); l != scf_list_sentinel(&obj->attrs); l = scf_list_next(l)) { + attr = scf_list_data(l, abc_obj_t, list); + + switch (attr->type) { + + case ABC_HTML_ATTR_FONT: + cairo_select_font_face(cr, attr->value->data, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); + break; + + case ABC_HTML_ATTR_FONT_SIZE: + cairo_set_font_size(cr, atoi(attr->value->data)); + break; + default: + break; + }; + } + + cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0); + + cairo_text_extents(cr, obj->text->data, &extents); + + obj->x = 4; + obj->y = 4; + obj->w = extents.width + extents.x_bearing; + obj->h = extents.height + extents.height / 2; + + obj->w = (obj->w + 3) & ~0x3; + obj->h = (obj->h + 3) & ~0x3; + + scf_logd("%s, w: %d, h: %d, x_bearing: %lg, y_bearing: %lg, width: %lg, height: %lg, x_advance: %lg, y_advance: %lg\n", + obj->text->data, obj->w, obj->h, + extents.x_bearing, extents.y_bearing, extents.width, extents.height, + extents.x_advance, extents.y_advance); + + cairo_destroy(cr); + cairo_surface_destroy(surface); + return 0; +} diff --git a/ui/abc_layout_body.c b/ui/abc_layout_body.c new file mode 100644 index 0000000..f374316 --- /dev/null +++ b/ui/abc_layout_body.c @@ -0,0 +1,6 @@ +#include"abc.h" + +int abc_layout_body(abc_layout_t* layout, abc_obj_t* obj, int width, int height) +{ + return 0; +} diff --git a/ui/abc_layout_div.c b/ui/abc_layout_div.c new file mode 100644 index 0000000..6878cc0 --- /dev/null +++ b/ui/abc_layout_div.c @@ -0,0 +1,6 @@ +#include"abc.h" + +int abc_layout_div(abc_layout_t* layout, abc_obj_t* obj, int width, int height) +{ + return 0; +} diff --git a/ui/abc_layout_h1.c b/ui/abc_layout_h1.c new file mode 100644 index 0000000..e39d3c0 --- /dev/null +++ b/ui/abc_layout_h1.c @@ -0,0 +1,52 @@ +#include"abc.h" + +int abc_layout_h1(abc_layout_t* layout, abc_obj_t* obj, int width, int height) +{ + cairo_text_extents_t extents; + cairo_surface_t* surface; + cairo_t* cr; + + scf_list_t* l; + abc_obj_t* attr; + + surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + cr = cairo_create(surface); + + for (l = scf_list_head(&obj->attrs); l != scf_list_sentinel(&obj->attrs); l = scf_list_next(l)) { + attr = scf_list_data(l, abc_obj_t, list); + + switch (attr->type) { + + case ABC_HTML_ATTR_FONT: + cairo_select_font_face(cr, attr->value->data, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); + break; + + case ABC_HTML_ATTR_FONT_SIZE: + cairo_set_font_size(cr, atoi(attr->value->data)); + break; + default: + break; + }; + } + + cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0); + + cairo_text_extents(cr, obj->text->data, &extents); + + obj->x = 4; + obj->y = 4; + obj->w = extents.width + extents.x_bearing; + obj->h = extents.height + extents.height / 2; + + obj->w = (obj->w + 3) & ~0x3; + obj->h = (obj->h + 3) & ~0x3; + + scf_logd("%s, w: %d, h: %d, x_bearing: %lg, y_bearing: %lg, width: %lg, height: %lg, x_advance: %lg, y_advance: %lg\n", + obj->text->data, obj->w, obj->h, + extents.x_bearing, extents.y_bearing, extents.width, extents.height, + extents.x_advance, extents.y_advance); + + cairo_destroy(cr); + cairo_surface_destroy(surface); + return 0; +} diff --git a/ui/abc_layout_head.c b/ui/abc_layout_head.c new file mode 100644 index 0000000..cb4d6c2 --- /dev/null +++ b/ui/abc_layout_head.c @@ -0,0 +1,6 @@ +#include"abc.h" + +int abc_layout_head(abc_layout_t* layout, abc_obj_t* obj, int width, int height) +{ + return 0; +} diff --git a/ui/abc_layout_html.c b/ui/abc_layout_html.c new file mode 100644 index 0000000..4615d4a --- /dev/null +++ b/ui/abc_layout_html.c @@ -0,0 +1,6 @@ +#include"abc.h" + +int abc_layout_html(abc_layout_t* layout, abc_obj_t* obj, int width, int height) +{ + return 0; +} diff --git a/ui/abc_layout_title.c b/ui/abc_layout_title.c new file mode 100644 index 0000000..35e1965 --- /dev/null +++ b/ui/abc_layout_title.c @@ -0,0 +1,6 @@ +#include"abc.h" + +int abc_layout_title(abc_layout_t* layout, abc_obj_t* obj, int width, int height) +{ + return 0; +} diff --git a/ui/abc_render.c b/ui/abc_render.c new file mode 100644 index 0000000..d97b6c2 --- /dev/null +++ b/ui/abc_render.c @@ -0,0 +1,231 @@ +#include"abc.h" + +extern abc_render_t abc_render_html; +extern abc_render_t abc_render_title; +extern abc_render_t abc_render_head; +extern abc_render_t abc_render_body; + +extern abc_render_t abc_render_div; +extern abc_render_t abc_render_h1; +extern abc_render_t abc_render_a; +extern abc_render_t abc_render_a_href; + +static abc_render_t* abc_renders[ABC_HTML_NB] = +{ + &abc_render_html, + &abc_render_title, + &abc_render_head, + &abc_render_body, + + // 4 + &abc_render_div, + &abc_render_h1, + &abc_render_a, + &abc_render_a_href, +}; + +int abc_renders_fini() +{ + abc_render_t* render; + int i; + + for (i = 0; i < ABC_HTML_NB; i++) { + render = abc_renders[i]; + + if (render && render->fini) { + int ret = render->fini(render); + if (ret < 0) + return ret; + } + } + + return 0; +} + +int abc_render_draw(abc_obj_t* obj, int width, int height) +{ + if (obj->type >= ABC_HTML_NB) + return -EINVAL; + + abc_render_t* render = abc_renders[obj->type]; + if (!render) + return -EINVAL; + + return render->draw(render, obj, width, height); +} + +static int __render_root(abc_ctx_t* ctx, abc_obj_t* root, int width, int height) +{ + scf_list_t* l; + abc_obj_t* child; + + int ret = abc_render_draw(root, width, height); + if (ret < 0) + return ret; + + for (l = scf_list_head(&root->childs); l != scf_list_sentinel(&root->childs); l = scf_list_next(l)) { + child = scf_list_data(l, abc_obj_t, list); + + ret = __render_root(ctx, child, width, height); + if (ret < 0) + return ret; + } + + return 0; +} + +int abc_render_root(abc_ctx_t* ctx, abc_obj_t* root, int width, int height) +{ + if (!ctx || !root) + return -EINVAL; + + if (width <= 0 || height <= 0) + return -EINVAL; + + int ret = __render_root(ctx, root, width, height); + if (ret < 0) + return ret; + + if (ctx->current->current) { + if (ABC_HTML_A == ctx->current->current->type) { + + abc_render_t* render = abc_renders[ABC_HTML_A_HREF]; + if (!render) + return -EINVAL; + + ret = render->draw(render, ctx->current->current, width, height); + if (ret < 0) + return ret; + } + } + + return 0; +} + +void __compute_mvp(float *res, float phi, float theta, float psi) +{ + float x = phi * (M_PI / 180.f); + float y = theta * (M_PI / 180.f); + float z = psi * (M_PI / 180.f); + float c1 = cosf (x), s1 = sinf (x); + float c2 = cosf (y), s2 = sinf (y); + float c3 = cosf (z), s3 = sinf (z); + float c3c2 = c3 * c2; + float s3c1 = s3 * c1; + float c3s2s1 = c3 * s2 * s1; + float s3s1 = s3 * s1; + float c3s2c1 = c3 * s2 * c1; + float s3c2 = s3 * c2; + float c3c1 = c3 * c1; + float s3s2s1 = s3 * s2 * s1; + float c3s1 = c3 * s1; + float s3s2c1 = s3 * s2 * c1; + float c2s1 = c2 * s1; + float c2c1 = c2 * c1; + + /* initialize to the identity matrix */ + res[0] = 1.f; res[4] = 0.f; res[8] = 0.f; res[12] = 0.f; + res[1] = 0.f; res[5] = 1.f; res[9] = 0.f; res[13] = 0.f; + res[2] = 0.f; res[6] = 0.f; res[10] = 1.f; res[14] = 0.f; + res[3] = 0.f; res[7] = 0.f; res[11] = 0.f; res[15] = 1.f; + + /* apply all three rotations using the three matrices: + * + * ⎡ c3 s3 0 ⎤ ⎡ c2 0 -s2 ⎤ ⎡ 1 0 0 ⎤ + * ⎢ -s3 c3 0 ⎥ ⎢ 0 1 0 ⎥ ⎢ 0 c1 s1 ⎥ + * ⎣ 0 0 1 ⎦ ⎣ s2 0 c2 ⎦ ⎣ 0 -s1 c1 ⎦ + */ + res[0] = c3c2; res[4] = s3c1 + c3s2s1; res[8] = s3s1 - c3s2c1; res[12] = 0.f; + res[1] = -s3c2; res[5] = c3c1 - s3s2s1; res[9] = c3s1 + s3s2c1; res[13] = 0.f; + res[2] = s2; res[6] = -c2s1; res[10] = c2c1; res[14] = 0.f; + res[3] = 0.f; res[7] = 0.f; res[11] = 0.f; res[15] = 1.f; +} + +int __init_texture(GLuint* ptex, GLenum format, int w, int h, uint8_t* data) +{ + GLuint tex; + + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0, format, GL_UNSIGNED_BYTE, data); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glBindTexture(GL_TEXTURE_2D, 0); + + *ptex = tex; + return 0; +} + +int __init_program(GLuint* pprog, const char* vert_shader, const char* frag_shader) +{ + GLint status = 0; + GLint len = 0; + + GLuint vshader = glCreateShader(GL_VERTEX_SHADER); + + glShaderSource (vshader, 1, &vert_shader, NULL); + glCompileShader(vshader); + glGetShaderiv (vshader, GL_COMPILE_STATUS, &status); + + if (!status) { + glGetShaderiv(vshader, GL_INFO_LOG_LENGTH, &len); + + char* log = calloc(1, len + 1); + if (!log) + return -ENOMEM; + + glGetShaderInfoLog(vshader, len, NULL, log); + + scf_loge("%s\n", log); + free(log); + log = NULL; + } + + GLuint fshader = glCreateShader(GL_FRAGMENT_SHADER); + + glShaderSource (fshader, 1, &frag_shader, NULL); + glCompileShader(fshader); + glGetShaderiv (fshader, GL_COMPILE_STATUS, &status); + + if (!status) { + glGetShaderiv(fshader, GL_INFO_LOG_LENGTH, &len); + + char* log = calloc(1, len + 1); + if (!log) + return -ENOMEM; + + glGetShaderInfoLog(fshader, len, NULL, log); + + scf_loge("%s\n", log); + free(log); + log = NULL; + } + + GLuint program = glCreateProgram(); + + glAttachShader(program, vshader); + glAttachShader(program, fshader); + glLinkProgram (program); + glGetProgramiv(program, GL_LINK_STATUS, &status); + + if (!status) { + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &len); + + char* log = calloc(1, len + 1); + if (!log) + return -ENOMEM; + + glGetProgramInfoLog(program, len, NULL, log); + + scf_loge("%s\n", log); + free(log); + log = NULL; + } + + *pprog = program; + return 0; +} diff --git a/ui/abc_render_a.c b/ui/abc_render_a.c new file mode 100644 index 0000000..02b37ba --- /dev/null +++ b/ui/abc_render_a.c @@ -0,0 +1,190 @@ +#include"abc.h" + +static const char* vert_shader = + "#version 330 core\n" + "layout(location = 0) in vec4 position; \n" + "layout(location = 1) in vec2 a_texCoord; \n" + "out vec2 v_texCoord; \n" + "uniform mat4 mvp; \n" + "void main() { \n" + "gl_Position = mvp * position; \n" + "v_texCoord = a_texCoord; \n" + "} \n"; + +static const char* frag_shader = + "#version 330 core\n" + "in vec2 v_texCoord; \n" + "out vec4 outputColor; \n" + "uniform sampler2D tex_rgba; \n" + "void main() { \n" + " vec2 xy = v_texCoord; \n" + " vec4 v = texture2D(tex_rgba, xy).rgba; \n" + " outputColor = vec4(v.b, v.g, v.r, 1.0); \n" + "} \n"; + + +static GLuint program = 0; + +static GLuint vao = 0; +static GLuint buffers[2] = {0}; +static GLuint texture_rgba = 0; + +static GLuint uniform_mvp; +static GLuint uniform_rgba; + +static int __width = 0; +static int __height = 0; + +static int __init_buffers(GLuint* vao, GLuint buffers[2]) +{ + glGenBuffers(2, buffers); + glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(vert_array), vert_array, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, buffers[1]); + glBufferData(GL_ARRAY_BUFFER, sizeof(texture_array), texture_array, GL_STATIC_DRAW); + + glGenVertexArrays(1, vao); + glBindVertexArray(*vao); + + glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0); + + glBindBuffer(GL_ARRAY_BUFFER, buffers[1]); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0); + + glBindVertexArray(0); + return 0; +} + +static int _render_fini_a(abc_render_t* render) +{ + return 0; +} + +static int _render_draw_a(abc_render_t* render, abc_obj_t* obj, int width, int height) +{ + if (0 == program) + __init_program(&program, vert_shader, frag_shader); + + if (0 == vao) + __init_buffers(&vao, buffers); + + if (0 == texture_rgba) + __init_texture(&texture_rgba, GL_RGBA, obj->w, obj->h, NULL); + + cairo_text_extents_t extents; + cairo_surface_t* surface; + cairo_t* cr; + + scf_list_t* l; + abc_obj_t* attr; + + uint8_t* rgba = calloc(1, obj->w * obj->h * 4); + if (!rgba) + return -ENOMEM; + + surface = cairo_image_surface_create_for_data(rgba, CAIRO_FORMAT_ARGB32, obj->w, obj->h, obj->w * 4); + cr = cairo_create(surface); + + cairo_set_line_width(cr, 1); + cairo_set_source_rgb(cr, 1, 1, 1); + cairo_rectangle(cr, 0, 0, obj->w, obj->h); + cairo_fill(cr); + cairo_stroke(cr); + + cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0); + + for (l = scf_list_head(&obj->attrs); l != scf_list_sentinel(&obj->attrs); l = scf_list_next(l)) { + attr = scf_list_data(l, abc_obj_t, list); + + switch (attr->type) { + + case ABC_HTML_ATTR_FONT: + cairo_select_font_face(cr, attr->value->data, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); + break; + + case ABC_HTML_ATTR_FONT_SIZE: + cairo_set_font_size(cr, atoi(attr->value->data)); + break; + + case ABC_HTML_ATTR_FONT_COLOR: + if (!strcmp(attr->value->data, "blue")) + cairo_set_source_rgba(cr, 0.0, 0.0, 1.0, 1.0); + break; + default: + break; + }; + } + + cairo_set_line_width(cr, 1); + + cairo_text_extents(cr, obj->text->data, &extents); + + cairo_move_to (cr, extents.x_bearing, -extents.y_bearing); + cairo_show_text(cr, obj->text->data); + + cairo_move_to (cr, extents.x_bearing, -extents.y_bearing); + cairo_line_to (cr, extents.width, -extents.y_bearing); + cairo_stroke(cr); + + cairo_destroy(cr); + cairo_surface_destroy(surface); + surface = NULL; + cr = NULL; + + float mvp[16]; + __compute_mvp(mvp, 0, 0, 0); + + scf_logi("%s, x: %d, y: %d, w: %d, h: %d\n", obj->text->data, obj->x, obj->y, obj->w, obj->h); + + GLfloat vert_update[] = + { + 2.0 * obj->x / (float)width - 1.0, + -2.0 * (obj->y + obj->h) / (float)height + 1.0, + + 2.0 * (obj->x + obj->w) / (float)width - 1.0, + -2.0 * (obj->y + obj->h) / (float)height + 1.0, + + 2.0 * obj->x / (float)width - 1.0, + -2.0 * obj->y / (float)height + 1.0, + + 2.0 * (obj->x + obj->w) / (float)width - 1.0, + -2.0 * obj->y / (float)height + 1.0, + }; + + glUseProgram(program); + uniform_rgba = glGetUniformLocation(program, "tex_rgba"); + uniform_mvp = glGetUniformLocation(program, "mvp"); + + glUniformMatrix4fv(uniform_mvp, 1, GL_FALSE, mvp); + + // board + glActiveTexture(GL_TEXTURE0); + glBindTexture (GL_TEXTURE_2D, texture_rgba); + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, obj->w, obj->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba); + glUniform1i(uniform_rgba, 0); + + // draw + glBindBuffer (GL_ARRAY_BUFFER, buffers[0]); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vert_update), vert_update); + + glBindVertexArray(vao); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glBindVertexArray(0); + glUseProgram(0); + + free(rgba); + return 0; +} + +abc_render_t abc_render_a = +{ + .type = ABC_HTML_A, + + .draw = _render_draw_a, + .fini = _render_fini_a, +}; diff --git a/ui/abc_render_a_href.c b/ui/abc_render_a_href.c new file mode 100644 index 0000000..96365c0 --- /dev/null +++ b/ui/abc_render_a_href.c @@ -0,0 +1,215 @@ +#include"abc.h" + +static const char* vert_shader = + "#version 330 core\n" + "layout(location = 0) in vec4 position; \n" + "layout(location = 1) in vec2 a_texCoord; \n" + "out vec2 v_texCoord; \n" + "uniform mat4 mvp; \n" + "void main() { \n" + "gl_Position = mvp * position; \n" + "v_texCoord = a_texCoord; \n" + "} \n"; + +static const char* frag_shader = + "#version 330 core\n" + "in vec2 v_texCoord; \n" + "out vec4 outputColor; \n" + "uniform sampler2D tex_rgba; \n" + "void main() { \n" + " vec2 xy = v_texCoord; \n" + " vec4 v = texture2D(tex_rgba, xy).rgba; \n" + " outputColor = vec4(v.b, v.g, v.r, 1.0); \n" + "} \n"; + + +static GLuint program = 0; + +static GLuint vao = 0; +static GLuint buffers[2] = {0}; +static GLuint texture_rgba = 0; + +static GLuint uniform_mvp; +static GLuint uniform_rgba; + +static int __width = 0; +static int __height = 0; + +static int __init_buffers(GLuint* vao, GLuint buffers[2]) +{ + glGenBuffers(2, buffers); + glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(vert_array), vert_array, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, buffers[1]); + glBufferData(GL_ARRAY_BUFFER, sizeof(texture_array), texture_array, GL_STATIC_DRAW); + + glGenVertexArrays(1, vao); + glBindVertexArray(*vao); + + glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0); + + glBindBuffer(GL_ARRAY_BUFFER, buffers[1]); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0); + + glBindVertexArray(0); + return 0; +} + +static int _render_fini_a_href(abc_render_t* render) +{ + return 0; +} + +static int _render_draw_a_href(abc_render_t* render, abc_obj_t* obj, int width, int height) +{ + if (0 == program) + __init_program(&program, vert_shader, frag_shader); + + if (0 == vao) + __init_buffers(&vao, buffers); + + if (0 == texture_rgba) + __init_texture(&texture_rgba, GL_RGBA, width, 32, NULL); + + cairo_text_extents_t extents; + cairo_surface_t* surface; + cairo_t* cr; + + scf_list_t* l; + abc_obj_t* attr; + + uint8_t* rgba = malloc(width * 32 * 4); + if (!rgba) + return -ENOMEM; + + surface = cairo_image_surface_create_for_data(rgba, CAIRO_FORMAT_ARGB32, width, 32, width * 4); + cr = cairo_create(surface); + + cairo_select_font_face(cr, "serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size (cr, 24); + cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0); + + for (l = scf_list_head(&obj->attrs); l != scf_list_sentinel(&obj->attrs); l = scf_list_next(l)) { + attr = scf_list_data(l, abc_obj_t, list); + + if (ABC_HTML_ATTR_HREF == attr->type) { + + cairo_text_extents(cr, attr->value->data, &extents); + break; + } + } + + int w = extents.width + extents.x_bearing; + int h = extents.height; + + w = (w + 7) & ~0x3; + h = (h + 7) & ~0x3; + + cairo_destroy(cr); + cairo_surface_destroy(surface); + surface = NULL; + cr = NULL; + + if (l == scf_list_sentinel(&obj->attrs)) { + scf_loge("hyper link '%s' not found a URL\n", obj->text->data); + + free(rgba); + rgba = NULL; + return -EINVAL; + } + + scf_logd("extents: x_bearing: %f, width: %f, y_bearing: %f, height: %f\n", + extents.x_bearing, extents.width, extents.y_bearing, extents.height); + + void* p = realloc(rgba, w * h * 4); + if (!p) { + free(rgba); + rgba = NULL; + return -ENOMEM; + } + rgba = p; + + surface = cairo_image_surface_create_for_data(rgba, CAIRO_FORMAT_ARGB32, w, h, w * 4); + cr = cairo_create(surface); + + cairo_set_line_width(cr, 1); + cairo_set_source_rgb(cr, 0.2, 0.2, 0.2); + cairo_rectangle(cr, 0, 0, w, h); + cairo_fill(cr); + cairo_stroke(cr); + + cairo_select_font_face(cr, "serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size (cr, 24); + cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0); + + cairo_move_to (cr, extents.x_bearing, -extents.y_bearing + 2); + cairo_show_text(cr, attr->value->data); + +// cairo_surface_write_to_png(surface, "tmp.png"); + + cairo_destroy(cr); + cairo_surface_destroy(surface); + surface = NULL; + cr = NULL; + + float mvp[16]; + __compute_mvp(mvp, 0, 0, 0); + + scf_logd("%s, href: %s, w: %d, h: %d\n", obj->text->data, attr->value->data, w, h); + + GLfloat vert_update[] = + { +//0, 1 + -1.0, + -1.0, + +//1, 1 + 2.0 * w / (float)width - 1.0, + -1.0, + +//0, 0 + -1.0, + 2.0 * h / (float)height - 1.0, + +//1, 0 + 2.0 * w / (float)width - 1.0, + 2.0 * h / (float)height - 1.0, + }; + + glUseProgram(program); + uniform_rgba = glGetUniformLocation(program, "tex_rgba"); + uniform_mvp = glGetUniformLocation(program, "mvp"); + + glUniformMatrix4fv(uniform_mvp, 1, GL_FALSE, mvp); + + // board + glActiveTexture(GL_TEXTURE0); + glBindTexture (GL_TEXTURE_2D, texture_rgba); + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba); + glUniform1i(uniform_rgba, 0); + + // draw + glBindBuffer (GL_ARRAY_BUFFER, buffers[0]); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vert_update), vert_update); + + glBindVertexArray(vao); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glBindVertexArray(0); + glUseProgram(0); + + free(rgba); + return 0; +} + +abc_render_t abc_render_a_href = +{ + .type = ABC_HTML_A_HREF, + + .draw = _render_draw_a_href, + .fini = _render_fini_a_href, +}; diff --git a/ui/abc_render_body.c b/ui/abc_render_body.c new file mode 100644 index 0000000..2008e77 --- /dev/null +++ b/ui/abc_render_body.c @@ -0,0 +1,19 @@ +#include"abc.h" + +static int _render_fini_body(abc_render_t* render) +{ + return 0; +} + +static int _render_draw_body(abc_render_t* render, abc_obj_t* obj, int width, int height) +{ + return 0; +} + +abc_render_t abc_render_body = +{ + .type = ABC_HTML_H1, + + .draw = _render_draw_body, + .fini = _render_fini_body, +}; diff --git a/ui/abc_render_div.c b/ui/abc_render_div.c new file mode 100644 index 0000000..5768b28 --- /dev/null +++ b/ui/abc_render_div.c @@ -0,0 +1,19 @@ +#include"abc.h" + +static int _render_fini_div(abc_render_t* render) +{ + return 0; +} + +static int _render_draw_div(abc_render_t* render, abc_obj_t* obj, int width, int height) +{ + return 0; +} + +abc_render_t abc_render_div = +{ + .type = ABC_HTML_H1, + + .draw = _render_draw_div, + .fini = _render_fini_div, +}; diff --git a/ui/abc_render_h1.c b/ui/abc_render_h1.c new file mode 100644 index 0000000..8a94b5c --- /dev/null +++ b/ui/abc_render_h1.c @@ -0,0 +1,179 @@ +#include"abc.h" + +const char* vert_shader = + "#version 330 core\n" + "layout(location = 0) in vec4 position; \n" + "layout(location = 1) in vec2 a_texCoord; \n" + "out vec2 v_texCoord; \n" + "uniform mat4 mvp; \n" + "void main() { \n" + "gl_Position = mvp * position; \n" + "v_texCoord = a_texCoord; \n" + "} \n"; + +const char* frag_shader = + "#version 330 core\n" + "in vec2 v_texCoord; \n" + "out vec4 outputColor; \n" + "uniform sampler2D tex_rgba; \n" + "void main() { \n" + " vec2 xy = v_texCoord; \n" + " vec4 v = texture2D(tex_rgba, xy).rgba; \n" + " outputColor = vec4(v.b, v.g, v.r, 1.0); \n" + "} \n"; + + +static GLuint program = 0; + +static GLuint vao = 0; +static GLuint buffers[2] = {0}; +static GLuint texture_rgba = 0; + +static GLuint uniform_mvp; +static GLuint uniform_rgba; + +static int __width = 0; +static int __height = 0; + +static int __init_buffers(GLuint* vao, GLuint buffers[2]) +{ + glGenBuffers(2, buffers); + glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(vert_array), vert_array, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, buffers[1]); + glBufferData(GL_ARRAY_BUFFER, sizeof(texture_array), texture_array, GL_STATIC_DRAW); + + glGenVertexArrays(1, vao); + glBindVertexArray(*vao); + + glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0); + + glBindBuffer(GL_ARRAY_BUFFER, buffers[1]); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0); + + glBindVertexArray(0); + return 0; +} + +static int _render_fini_h1(abc_render_t* render) +{ + return 0; +} + +static int _render_draw_h1(abc_render_t* render, abc_obj_t* obj, int width, int height) +{ + if (0 == program) + __init_program(&program, vert_shader, frag_shader); + + if (0 == vao) + __init_buffers(&vao, buffers); + + if (0 == texture_rgba) + __init_texture(&texture_rgba, GL_RGBA, obj->w, obj->h, NULL); + + cairo_text_extents_t extents; + cairo_surface_t* surface; + cairo_t* cr; + + scf_list_t* l; + abc_obj_t* attr; + + uint8_t* rgba = calloc(1, obj->w * obj->h * 4); + if (!rgba) + return -ENOMEM; + + surface = cairo_image_surface_create_for_data(rgba, CAIRO_FORMAT_ARGB32, obj->w, obj->h, obj->w * 4); + cr = cairo_create(surface); + + cairo_set_line_width(cr, 1); + cairo_set_source_rgb(cr, 1, 1, 1); + cairo_rectangle(cr, 0, 0, obj->w, obj->h); + cairo_fill(cr); + cairo_stroke(cr); + + for (l = scf_list_head(&obj->attrs); l != scf_list_sentinel(&obj->attrs); l = scf_list_next(l)) { + attr = scf_list_data(l, abc_obj_t, list); + + switch (attr->type) { + + case ABC_HTML_ATTR_FONT: + cairo_select_font_face(cr, attr->value->data, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); + break; + + case ABC_HTML_ATTR_FONT_SIZE: + cairo_set_font_size(cr, atoi(attr->value->data)); + break; + default: + break; + }; + } + + cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0); + + cairo_text_extents(cr, obj->text->data, &extents); + + cairo_move_to (cr, extents.x_bearing, -extents.y_bearing); + cairo_show_text(cr, obj->text->data); + + cairo_destroy(cr); + cairo_surface_destroy(surface); + surface = NULL; + cr = NULL; + + float mvp[16]; + __compute_mvp(mvp, 0, 0, 0); + + scf_logi("%s, x: %d, y: %d, w: %d, h: %d\n", obj->text->data, obj->x, obj->y, obj->w, obj->h); + + GLfloat vert_update[] = + { + 2.0 * obj->x / (float)width - 1.0, + -2.0 * (obj->y + obj->h) / (float)height + 1.0, + + 2.0 * (obj->x + obj->w) / (float)width - 1.0, + -2.0 * (obj->y + obj->h) / (float)height + 1.0, + + 2.0 * obj->x / (float)width - 1.0, + -2.0 * obj->y / (float)height + 1.0, + + 2.0 * (obj->x + obj->w) / (float)width - 1.0, + -2.0 * obj->y / (float)height + 1.0, + }; + + glUseProgram(program); + uniform_rgba = glGetUniformLocation(program, "tex_rgba"); + uniform_mvp = glGetUniformLocation(program, "mvp"); + + glUniformMatrix4fv(uniform_mvp, 1, GL_FALSE, mvp); + + // board + glActiveTexture(GL_TEXTURE0); + glBindTexture (GL_TEXTURE_2D, texture_rgba); + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, obj->w, obj->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba); + glUniform1i(uniform_rgba, 0); + + // draw + glBindBuffer (GL_ARRAY_BUFFER, buffers[0]); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vert_update), vert_update); + + glBindVertexArray(vao); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glBindVertexArray(0); + glUseProgram(0); + + free(rgba); + return 0; +} + +abc_render_t abc_render_h1 = +{ + .type = ABC_HTML_H1, + + .draw = _render_draw_h1, + .fini = _render_fini_h1, +}; diff --git a/ui/abc_render_head.c b/ui/abc_render_head.c new file mode 100644 index 0000000..cef9af5 --- /dev/null +++ b/ui/abc_render_head.c @@ -0,0 +1,19 @@ +#include"abc.h" + +static int _render_fini_head(abc_render_t* render) +{ + return 0; +} + +static int _render_draw_head(abc_render_t* render, abc_obj_t* obj, int width, int height) +{ + return 0; +} + +abc_render_t abc_render_head = +{ + .type = ABC_HTML_H1, + + .draw = _render_draw_head, + .fini = _render_fini_head, +}; diff --git a/ui/abc_render_html.c b/ui/abc_render_html.c new file mode 100644 index 0000000..37f13ec --- /dev/null +++ b/ui/abc_render_html.c @@ -0,0 +1,19 @@ +#include"abc.h" + +static int _render_fini_html(abc_render_t* render) +{ + return 0; +} + +static int _render_draw_html(abc_render_t* render, abc_obj_t* obj, int width, int height) +{ + return 0; +} + +abc_render_t abc_render_html = +{ + .type = ABC_HTML_H1, + + .draw = _render_draw_html, + .fini = _render_fini_html, +}; diff --git a/ui/abc_render_title.c b/ui/abc_render_title.c new file mode 100644 index 0000000..3c53e7a --- /dev/null +++ b/ui/abc_render_title.c @@ -0,0 +1,19 @@ +#include"abc.h" + +static int _render_fini_title(abc_render_t* render) +{ + return 0; +} + +static int _render_draw_title(abc_render_t* render, abc_obj_t* obj, int width, int height) +{ + return 0; +} + +abc_render_t abc_render_title = +{ + .type = ABC_HTML_H1, + + .draw = _render_draw_title, + .fini = _render_fini_title, +}; diff --git a/ui/abc_ui.glade b/ui/abc_ui.glade new file mode 100644 index 0000000..383c82b --- /dev/null +++ b/ui/abc_ui.glade @@ -0,0 +1,157 @@ + + + + + + True + False + 浏览器 + 1280 + 720 + + + + + + True + True + vertical + 80 + + + True + True + vertical + + + True + False + + + gtk-add + True + True + True + True + True + + + False + True + 0 + + + + + + + + + + + False + True + + + + + True + True + 243 + + + True + False + + + gtk-go-back + True + True + False + True + True + True + + + False + True + 0 + + + + + gtk-go-forward + True + True + False + True + True + True + + + False + True + 1 + + + + + gtk-refresh + True + True + True + True + True + + + False + True + 2 + + + + + False + True + + + + + True + True + edit-find-symbolic + False + False + + + True + True + + + + + True + True + + + + + False + True + + + + + True + True + False + + + True + True + + + + + + diff --git a/ui/main.c b/ui/main.c new file mode 100644 index 0000000..7181b54 --- /dev/null +++ b/ui/main.c @@ -0,0 +1,348 @@ +#include"abc.h" + +static void resize(GtkGLArea* self, gint width, gint height, gpointer user_data) +{ + GdkGLContext *context; + + gtk_gl_area_make_current(self); + + if (gtk_gl_area_get_error(self) != NULL) { + printf("%s(),%d, error:\n", __func__, __LINE__); + return; + } + + printf("%s(),%d, width: %d, height: %d\n", __func__, __LINE__, width, height); + + context = gtk_gl_area_get_context(self); + + if (gdk_gl_context_get_use_es(context)) + printf("%s(),%d, gles\n", __func__, __LINE__); + else + printf("%s(),%d, gl\n", __func__, __LINE__); +} + +static void unrealize(GtkWidget *widget) +{ + printf("%s(),%d\n", __func__, __LINE__); +} + +static gboolean render(GtkGLArea* self, GdkGLContext* context, gpointer user_data) +{ + if (gtk_gl_area_get_error(self) != NULL) + return FALSE; + + abc_ctx_t* ctx = user_data; + abc_obj_t* root = ctx->current->root; + + int width = gtk_widget_get_allocated_width (GTK_WIDGET(self)); + int height = gtk_widget_get_allocated_height(GTK_WIDGET(self)); + + scf_logd("width: %d, height: %d\n\n", width, height); + + glViewport(0, 0, width, height); + + glClearColor(1.0, 1.0, 1.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + int ret = abc_layout_root(NULL, root, width, height); + + ret = abc_render_root(ctx, root, width, height); + + glFlush(); + return TRUE; +} + +static int __do_button_move(abc_ctx_t* ctx, int x, int y) +{ + GdkDisplay* display; + GdkWindow* window; + GdkCursor* cursor; + + abc_obj_t* prev = ctx->current->current; + abc_obj_t* obj = abc_obj_find(ctx->current->root, x, y); + + int ret = 0; + + if (prev && prev != obj) { + ctx->current->current = NULL; + prev->move_flag = 0; + + display = gtk_widget_get_display(GTK_WIDGET(ctx->gl_area)); + window = gtk_widget_get_window (GTK_WIDGET(ctx->gl_area)); + cursor = gdk_cursor_new_for_display(display, GDK_LEFT_PTR); + + gdk_window_set_cursor(window, cursor); + g_object_unref(cursor); + + ret = 1; + } + + if (obj) { + scf_logd("obj: %s, x: %d, y: %d, w: %d, h: %d, event x: %d, y: %d\n", + obj->key->data, obj->x, obj->y, obj->w, obj->h, x, y); + + + switch (obj->type) { + + case ABC_HTML_A: + display = gtk_widget_get_display(GTK_WIDGET(ctx->gl_area)); + window = gtk_widget_get_window (GTK_WIDGET(ctx->gl_area)); + cursor = gdk_cursor_new_for_display(display, GDK_HAND2); + + gdk_window_set_cursor(window, cursor); + g_object_unref(cursor); + + obj->move_flag = 1; + ret = 1; + break; + default: + break; + }; + + ctx->current->current = obj; + } + + return ret; +} + +static int __do_button_release(abc_ctx_t* ctx, int x, int y) +{ + abc_html_t* html; + abc_obj_t* attr; + abc_obj_t* obj = abc_obj_find(ctx->current->root, x, y); + + if (!obj) + return -ENOMEM; + + scf_logd("obj: %s, x: %d, y: %d, w: %d, h: %d, event x: %d, y: %d\n", + obj->key->data, obj->x, obj->y, obj->w, obj->h, x, y); + + int ret = 0; + + if (ABC_HTML_A == obj->type) { + + html = NULL; + attr = abc_obj_get_attr(obj, ABC_HTML_ATTR_HREF); + + ret = abc_html_open(&html, attr->value->data); + if (ret < 0) + return ret; + + ret = abc_html_parse(html); + if (ret < 0) { + abc_html_close(html); + html = NULL; + return ret; + } + + scf_list_add_tail(&ctx->html_list, &html->list); + + html->root->gtk_builder = ctx->builder; + + ctx->current = html; + + int width = gtk_widget_get_allocated_width (GTK_WIDGET(ctx->gl_area)); + int height = gtk_widget_get_allocated_height(GTK_WIDGET(ctx->gl_area)); + + abc_layout_root(NULL, ctx->current->root, width, height); + + __do_button_move(ctx, x, y); + + gtk_widget_set_sensitive(GTK_WIDGET(ctx->back), TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(ctx->forward), FALSE); + + ret = 1; + } + + return ret; +} + +static void back_clicked(GtkButton* self, gpointer user_data) +{ + abc_ctx_t* ctx = user_data; + scf_list_t* l; + + if (ctx->current) { + l = scf_list_prev(&ctx->current->list); + + if (l != scf_list_sentinel(&ctx->html_list)) { + + ctx->current = scf_list_data(l, abc_html_t, list); + + if (scf_list_prev(l) != scf_list_sentinel(&ctx->html_list)) + gtk_widget_set_sensitive(GTK_WIDGET(ctx->back), TRUE); + else + gtk_widget_set_sensitive(GTK_WIDGET(ctx->back), FALSE); + + gtk_widget_set_sensitive(GTK_WIDGET(ctx->forward), TRUE); + + gtk_gl_area_queue_render(ctx->gl_area); + } + } +} + +static void forward_clicked(GtkButton* self, gpointer user_data) +{ + abc_ctx_t* ctx = user_data; + scf_list_t* l; + + if (ctx->current) { + l = scf_list_next(&ctx->current->list); + + if (l != scf_list_sentinel(&ctx->html_list)) { + + ctx->current = scf_list_data(l, abc_html_t, list); + + if (scf_list_next(l) != scf_list_sentinel(&ctx->html_list)) + gtk_widget_set_sensitive(GTK_WIDGET(ctx->forward), TRUE); + else + gtk_widget_set_sensitive(GTK_WIDGET(ctx->forward), FALSE); + + gtk_widget_set_sensitive(GTK_WIDGET(ctx->back), TRUE); + + gtk_gl_area_queue_render(ctx->gl_area); + } + } +} + +static void refresh_clicked(GtkButton* self, gpointer user_data) +{ + abc_html_t* html = NULL; + abc_ctx_t* ctx = user_data; + + if (ctx->current) { + + int ret = abc_html_open(&html, ctx->current->file->data); + if (ret < 0) { + scf_loge("refresh '%s' failed\n", ctx->current->file->data); + return; + } + + ret = abc_html_parse(html); + if (ret < 0) { + scf_loge("refresh '%s' failed\n", ctx->current->file->data); + + abc_html_close(html); + html = NULL; + return; + } + + scf_list_add_tail(&ctx->current->list, &html->list); + + html->root->gtk_builder = ctx->builder; + + scf_list_del(&ctx->current->list); + abc_html_close(ctx->current); + + ctx->current = html; + + gtk_gl_area_queue_render(ctx->gl_area); + } +} + +static gboolean button_release_event(GtkWidget* self, GdkEventButton* event, gpointer user_data) +{ + abc_ctx_t* ctx = user_data; + + if (gtk_widget_get_window(GTK_WIDGET(ctx->gl_area)) == event->window) { + + int ret = __do_button_release(ctx, event->x, event->y); + if (ret > 0) + gtk_gl_area_queue_render(ctx->gl_area); + } + + return TRUE; +} + +static gboolean button_move_event(GtkWidget* self, GdkEventMotion* event, gpointer user_data) +{ + abc_ctx_t* ctx = user_data; + + int ret = __do_button_move(ctx, event->x, event->y); + if (ret > 0) + gtk_gl_area_queue_render(ctx->gl_area); + + return TRUE; +} + +int main(int argc, char *argv[]) +{ + abc_ctx_t ctx = ABC_CTX_INIT(ctx); + + GtkBuilder* builder; + GObject* window; + GObject* gl_area; + + GObject* back; + GObject* forward; + GObject* refresh; + GError* error = NULL; + + abc_html_t* html = NULL; + + if (argc < 2) { + scf_loge("\n"); + return -1; + } + + gtk_init(&argc, &argv); + + builder = gtk_builder_new(); + + if (gtk_builder_add_from_file(builder, "abc_ui.glade", &error) == 0) { + g_printerr("Error loading file: %s\n", error->message); + g_clear_error(&error); + return 1; + } + + int ret = abc_html_open(&html, argv[1]); + if (ret < 0) { + scf_loge("\n"); + return ret; + } + + ret = abc_html_parse(html); + if (ret < 0) { + scf_loge("\n"); + return ret; + } + + scf_list_add_tail(&ctx.html_list, &html->list); + + html->root->gtk_builder = builder; + + window = gtk_builder_get_object(builder, "main_window"); + gl_area = gtk_builder_get_object(builder, "gl_area"); + + back = gtk_builder_get_object(builder, "button_back"); + forward = gtk_builder_get_object(builder, "button_forward"); + refresh = gtk_builder_get_object(builder, "button_refresh"); + + ctx.current = html; + ctx.builder = builder; + ctx.gl_area = GTK_GL_AREA(gl_area); + + ctx.back = GTK_BUTTON(back); + ctx.forward = GTK_BUTTON(forward); + ctx.refresh = GTK_BUTTON(refresh); + + g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); + + g_signal_connect(back, "clicked", G_CALLBACK(back_clicked), &ctx); + g_signal_connect(forward, "clicked", G_CALLBACK(forward_clicked), &ctx); + g_signal_connect(refresh, "clicked", G_CALLBACK(refresh_clicked), &ctx); + + g_signal_connect(gl_area, "resize", G_CALLBACK(resize), NULL); + g_signal_connect(gl_area, "unrealize", G_CALLBACK(unrealize), NULL); + g_signal_connect(gl_area, "render", G_CALLBACK(render), &ctx); + + gtk_widget_add_events(GTK_WIDGET(gl_area), GDK_POINTER_MOTION_MASK); + + g_signal_connect(window, "button-release-event", G_CALLBACK(button_release_event), &ctx); + g_signal_connect(gl_area, "motion-notify-event", G_CALLBACK(button_move_event), &ctx); + + gtk_widget_show_all(GTK_WIDGET(window)); + + gtk_main(); + return 0; +} diff --git a/util/scf_def.h b/util/scf_def.h new file mode 100644 index 0000000..55b1def --- /dev/null +++ b/util/scf_def.h @@ -0,0 +1,73 @@ +#ifndef SCF_DEF_H +#define SCF_DEF_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 1 +#include +static inline int64_t gettime() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + + return tv.tv_sec * 1000000LL + tv.tv_usec; +} +#endif + +// sign-extend low 'src_bits' of src to 64 bits +static inline uint64_t scf_sign_extend(uint64_t src, int src_bits) +{ + uint64_t sign = src >> (src_bits - 1) & 0x1; + uint64_t mask = (~sign + 1) << (src_bits - 1); + + src |= mask; + return src; +} + +// zero-extend low 'src_bits' of src to 64 bits +static inline uint64_t scf_zero_extend(uint64_t src, int src_bits) +{ + uint64_t mask = (1ULL << src_bits) - 1; + + src &= mask; + return src; +} + +#define SCF_XCHG(x, y) \ + do {\ + typeof(x) tmp = x; \ + x = y; \ + y = tmp; \ + } while (0) + +#ifdef SCF_DEBUG +#define scf_logd(fmt, ...) printf("%s(), %d, "fmt, __func__, __LINE__, ##__VA_ARGS__) +#else +#define scf_logd(fmt, ...) +#endif + +#define scf_logi(fmt, ...) printf("%s(), %d, info: "fmt, __func__, __LINE__, ##__VA_ARGS__) + +#define scf_loge(fmt, ...) printf("%s(), %d, \033[31m error:\033[0m "fmt, __func__, __LINE__, ##__VA_ARGS__) +#define scf_logw(fmt, ...) printf("%s(), %d, \033[33m warning:\033[0m "fmt, __func__, __LINE__, ##__VA_ARGS__) + +#define SCF_CHECK_ERROR(cond, ret, fmt, ...) \ + do { \ + if (cond) { \ + printf("%s(), %d, \033[31m error:\033[0m "fmt, __func__, __LINE__, ##__VA_ARGS__); \ + return ret; \ + } \ + } while (0) + +#endif + diff --git a/util/scf_list.h b/util/scf_list.h new file mode 100644 index 0000000..4b5812c --- /dev/null +++ b/util/scf_list.h @@ -0,0 +1,81 @@ +#ifndef SCF_LIST_H +#define SCF_LIST_H + +#include"scf_def.h" + +typedef struct scf_list_s scf_list_t; + +struct scf_list_s { + struct scf_list_s* prev; + struct scf_list_s* next; +}; + +static inline void scf_list_init(scf_list_t* h) +{ + h->prev = h; + h->next = h; +} + +static inline void scf_list_del(scf_list_t* n) +{ + n->prev->next = n->next; + n->next->prev = n->prev; + + // only to avoid some wrong operations for these 2 invalid pointers + n->prev = NULL; + n->next = NULL; +} + +static inline void scf_list_add_tail(scf_list_t* h, scf_list_t* n) +{ + h->prev->next = n; + n->prev = h->prev; + n->next = h; + h->prev = n; +} + +static inline void scf_list_add_front(scf_list_t* h, scf_list_t* n) +{ + h->next->prev = n; + n->next = h->next; + n->prev = h; + h->next = n; +} + +#define SCF_LIST_INIT(h) {&h, &h} + +#define scf_list_data(l, type, member) ((type*)((char*)l - offsetof(type, member))) + +#define scf_list_head(h) ((h)->next) +#define scf_list_tail(h) ((h)->prev) +#define scf_list_sentinel(h) (h) +#define scf_list_next(l) ((l)->next) +#define scf_list_prev(l) ((l)->prev) +#define scf_list_empty(h) ((h)->next == (h)) + +#define scf_list_clear(h, type, member, type_free) \ + do {\ + scf_list_t* l;\ + for (l = scf_list_head(h); l != scf_list_sentinel(h);) {\ + type* t = scf_list_data(l, type, member);\ + l = scf_list_next(l);\ + scf_list_del(&t->member);\ + type_free(t);\ + t = NULL;\ + }\ + } while(0) + +#define scf_list_mov(dst, src, type, member) \ + do {\ + scf_list_t* l;\ + type* t;\ + for (l = scf_list_head(src); l != scf_list_sentinel(src);) {\ + t = scf_list_data(l, type, member);\ + l = scf_list_next(l);\ + scf_list_del(&t->member);\ + scf_list_add_tail(dst, &t->member);\ + }\ + } while(0) + +#endif + diff --git a/util/scf_stack.h b/util/scf_stack.h new file mode 100644 index 0000000..24ab942 --- /dev/null +++ b/util/scf_stack.h @@ -0,0 +1,60 @@ +#ifndef SCF_STACK_H +#define SCF_STACK_H + +#include"scf_vector.h" + +typedef scf_vector_t scf_stack_t; + +static inline scf_stack_t* scf_stack_alloc() +{ + return scf_vector_alloc(); +} + +static inline int scf_stack_push(scf_stack_t* s, void* node) +{ + return scf_vector_add(s, node); +} + +static inline void* scf_stack_pop(scf_stack_t* s) +{ + if (!s || !s->data) + return NULL; + + assert(s->size >= 0); + + if (0 == s->size) + return NULL; + + void* node = s->data[--s->size]; + + if (s->size + NB_MEMBER_INC * 2 < s->capacity) { + void* p = realloc(s->data, sizeof(void*) * (s->capacity - NB_MEMBER_INC)); + if (p) { + s->data = p; + s->capacity -= NB_MEMBER_INC; + } + } + + return node; +} + +static inline void* scf_stack_top(scf_stack_t* s) +{ + if (!s || !s->data) + return NULL; + + assert(s->size >= 0); + + if (0 == s->size) + return NULL; + + return s->data[s->size - 1]; +} + +static inline void scf_stack_free(scf_stack_t* s) +{ + scf_vector_free(s); +} + +#endif + diff --git a/util/scf_string.c b/util/scf_string.c new file mode 100644 index 0000000..a0b4587 --- /dev/null +++ b/util/scf_string.c @@ -0,0 +1,390 @@ +#include"scf_string.h" + +#define SCF_STRING_NUMBER_INC 4 + +scf_string_t* scf_string_alloc() +{ + scf_string_t* s = malloc(sizeof(scf_string_t)); + if (!s) + return NULL; + + s->data = malloc(SCF_STRING_NUMBER_INC + 1); + if (!s->data) { + free(s); + return NULL; + } + + s->capacity = SCF_STRING_NUMBER_INC; + s->len = 0; + s->data[0] = '\0'; + return s; +} + +scf_string_t* scf_string_clone(scf_string_t* s) +{ + if (!s) + return NULL; + + scf_string_t* s1 = malloc(sizeof(scf_string_t)); + if (!s1) + return NULL; + + if (s->capacity > 0) + s1->capacity = s->capacity; + else if (s->len > 0) + s1->capacity = s->len; + else + s1->capacity = SCF_STRING_NUMBER_INC; + + s1->data = malloc(s1->capacity + 1); + if (!s1->data) { + free(s1); + return NULL; + } + + s1->len = s->len; + if (s->len > 0) + memcpy(s1->data, s->data, s->len); + + s1->data[s1->len] = '\0'; + return s1; +} + +scf_string_t* scf_string_cstr(const char* str) +{ + if (!str) + return NULL; + + return scf_string_cstr_len(str, strlen(str)); +} + +scf_string_t* scf_string_cstr_len(const char* str, size_t len) +{ + if (!str) + return NULL; + + scf_string_t s; + s.capacity = -1; + s.len = len; + s.data = (char*)str; + + return scf_string_clone(&s); +} + +void scf_string_free(scf_string_t* s) +{ + if (!s) + return; + + if (s->capacity > 0) + free(s->data); + + free(s); + s = NULL; +} + +void scf_string_print_bin(scf_string_t* s) +{ + if (!s) + return; + + int i; + for (i = 0; i < s->len; i++) { + + unsigned char c = s->data[i]; + + if (i > 0 && i % 10 == 0) + printf("\n"); + + printf("%#02x ", c); + } + printf("\n"); +} + +int scf_string_cmp(const scf_string_t* s0, const scf_string_t* s1) +{ + if (!s0 || !s1 || !s0->data || !s1->data) + return -EINVAL; + + if (s0->len < s1->len) + return -1; + + if (s0->len > s1->len) + return 1; + return strncmp(s0->data, s1->data, s0->len); +} + +int scf_string_cmp_cstr(const scf_string_t* s0, const char* str) +{ + if (!s0 || !s0->data || !str) + return -EINVAL; + + return scf_string_cmp_cstr_len(s0, str, strlen(str)); +} + +int scf_string_cmp_cstr_len(const scf_string_t* s0, const char* str, size_t len) +{ + if (!s0 || !s0->data || !str) + return -EINVAL; + + scf_string_t s1; + s1.capacity = -1; + s1.len = len; + s1.data = (char*)str; + + return scf_string_cmp(s0, &s1); +} + +int scf_string_copy(scf_string_t* s0, const scf_string_t* s1) +{ + if (!s0 || !s1 || !s0->data || !s1->data) + return -EINVAL; + + assert(s0->capacity > 0); + + if (s1->len > s0->capacity) { + char* p = realloc(s0->data, s1->len + 1); + if (!p) + return -ENOMEM; + + s0->data = p; + s0->capacity = s1->len; + } + + memcpy(s0->data, s1->data, s1->len); + s0->data[s1->len] = '\0'; + s0->len = s1->len; + return 0; +} + +int scf_string_cat(scf_string_t* s0, const scf_string_t* s1) +{ + if (!s0 || !s1 || !s0->data || !s1->data) + return -EINVAL; + + assert(s0->capacity > 0); + + if (s0->len + s1->len > s0->capacity) { + char* p = realloc(s0->data, s0->len + s1->len + SCF_STRING_NUMBER_INC + 1); + if (!p) + return -ENOMEM; + + s0->data = p; + s0->capacity = s0->len + s1->len + SCF_STRING_NUMBER_INC; + } + + memcpy(s0->data + s0->len, s1->data, s1->len); + s0->data[s0->len + s1->len] = '\0'; + s0->len += s1->len; + return 0; +} + +int scf_string_cat_cstr(scf_string_t* s0, const char* str) +{ + if (!s0 || !s0->data || !str) + return -EINVAL; + + return scf_string_cat_cstr_len(s0, str, strlen(str)); +} + +int scf_string_cat_cstr_len(scf_string_t* s0, const char* str, size_t len) +{ + if (!s0 || !s0->data || !str) + return -EINVAL; + + scf_string_t s1; + s1.capacity = -1; + s1.len = len; + s1.data = (char*)str; + + return scf_string_cat(s0, &s1); +} + +int scf_string_fill_zero(scf_string_t* s0, size_t len) +{ + if (!s0 || !s0->data) + return -EINVAL; + + assert(s0->capacity > 0); + + if (s0->len + len > s0->capacity) { + + char* p = realloc(s0->data, s0->len + len + SCF_STRING_NUMBER_INC + 1); + if (!p) + return -ENOMEM; + + s0->data = p; + s0->capacity = s0->len + len + SCF_STRING_NUMBER_INC; + } + + memset(s0->data + s0->len, 0, len); + s0->data[s0->len + len] = '\0'; + s0->len += len; + return 0; +} + +static int* _prefix_kmp(const uint8_t* P, int m) +{ + int* prefix = malloc(sizeof(int) * (m + 1)); + if (!prefix) + return NULL; + + prefix[0] = -1; + + int k = -1; + int q; + + for (q = 1; q < m; q++) { + + while (k > -1 && P[k + 1] != P[q]) + k = prefix[k]; + + if (P[k + 1] == P[q]) + k++; + + prefix[q] = k; + } + return prefix; +} + +static int _match_kmp(const uint8_t* T, int Tlen, const uint8_t* P, int Plen, scf_vector_t* offsets) +{ + if (Tlen <= 0 || Plen <= 0) + return -EINVAL; + + int n = Tlen; + int m = Plen; + + int* prefix = _prefix_kmp(P, m); + if (!prefix) + return -1; + + int q = -1; + int i; + + for (i = 0; i < n; i++) { + + while (q > -1 && P[q + 1] != T[i]) + q = prefix[q]; + + if (P[q + 1] == T[i]) + q++; + + if (q == m - 1) { + scf_logd("KMP find P: %s in T: %s, offset: %d\n", P, T, i - m + 1); + + int ret = scf_vector_add(offsets, (void*)(intptr_t)(i - m + 1)); + if (ret < 0) + return ret; + + q = prefix[q]; + } + } + + free(prefix); + return 0; +} + +int scf_string_match_kmp(const scf_string_t* T, const scf_string_t* P, scf_vector_t* offsets) +{ + if (!T || !P || !offsets) + return -EINVAL; + + return _match_kmp(T->data, T->len, P->data, P->len, offsets); +} + +int scf_string_match_kmp_cstr(const uint8_t* T, const uint8_t* P, scf_vector_t* offsets) +{ + if (!T || !P || !offsets) + return -EINVAL; + + return _match_kmp(T, strlen(T), P, strlen(P), offsets); +} + +int scf_string_match_kmp_cstr_len(const scf_string_t* T, const uint8_t* P, size_t Plen, scf_vector_t* offsets) +{ + if (!T || !P || 0 == Plen || !offsets) + return -EINVAL; + + return _match_kmp(T->data, T->len, P, Plen, offsets); +} + +int scf_string_get_offset(scf_string_t* str, const char* data, size_t len) +{ + int ret; + + if (0 == str->len) { + if (scf_string_cat_cstr_len(str, data, len) < 0) + return -1; + return 0; + } + + scf_vector_t* vec = scf_vector_alloc(); + if (!vec) + return -ENOMEM; + + ret = scf_string_match_kmp_cstr_len(str, data, len, vec); + if (ret < 0) { + + } else if (0 == vec->size) { + ret = str->len; + + if (scf_string_cat_cstr_len(str, data, len) < 0) + ret = -1; + } else + ret = (intptr_t)(vec->data[0]); + + scf_vector_free(vec); + return ret; +} + +#if 0 +int main(int argc, char* argv[]) +{ + scf_string_t* s0 = scf_string_alloc(); + scf_string_t* s1 = scf_string_cstr("hello, world!"); + scf_string_t* s2 = scf_string_clone(s1); + printf("s0: %s\n", s0->data); + printf("s1: %s\n", s1->data); + printf("s2: %s\n", s2->data); + + scf_string_copy(s0, s2); + printf("s0: %s\n", s0->data); + + printf("s0 cmp s1: %d\n", scf_string_cmp(s0, s1)); + + scf_string_t* s3 = scf_string_cstr("ha ha ha!"); + printf("s3: %s\n", s3->data); + + scf_string_cat(s0, s3); + printf("s0: %s\n", s0->data); + + scf_string_cat_cstr(s0, "he he he!"); + printf("s0: %s\n", s0->data); + + scf_string_t* s4 = scf_string_cstr("hello, world!"); + scf_string_t* s5 = scf_string_cstr("ll"); + scf_vector_t* vec = scf_vector_alloc(); + + int ret = scf_string_match_kmp(s4, s5, vec); + if (ret < 0) { + scf_loge("\n"); + return -1; + } + + int i; + for (i = 0; i < vec->size; i++) { + + int offset = (intptr_t)vec->data[i]; + + printf("i: %d, offset: %d\n", i, offset); + } + + scf_string_free(s0); + scf_string_free(s1); + scf_string_free(s2); + scf_string_free(s3); + return 0; +} +#endif + diff --git a/util/scf_string.h b/util/scf_string.h new file mode 100644 index 0000000..cbf8717 --- /dev/null +++ b/util/scf_string.h @@ -0,0 +1,48 @@ +#ifndef SCF_STRING_H +#define SCF_STRING_H + +#include"scf_vector.h" + +typedef struct { + int capacity; + size_t len; + char* data; +} scf_string_t; + + +scf_string_t* scf_string_alloc(); + +scf_string_t* scf_string_clone(scf_string_t* s); + +scf_string_t* scf_string_cstr(const char* str); + +scf_string_t* scf_string_cstr_len(const char* str, size_t len); + +void scf_string_free(scf_string_t* s); + +void scf_string_print_bin(scf_string_t* s); + +int scf_string_fill_zero(scf_string_t* s0, size_t len); + +int scf_string_cmp(const scf_string_t* s0, const scf_string_t* s1); + +int scf_string_cmp_cstr(const scf_string_t* s0, const char* str); + +int scf_string_cmp_cstr_len(const scf_string_t* s0, const char* str, size_t len); + +int scf_string_copy(scf_string_t* s0, const scf_string_t* s1); + +int scf_string_cat(scf_string_t* s0, const scf_string_t* s1); + +int scf_string_cat_cstr(scf_string_t* s0, const char* str); + +int scf_string_cat_cstr_len(scf_string_t* s0, const char* str, size_t len); + +int scf_string_match_kmp(const scf_string_t* T, const scf_string_t* P, scf_vector_t* offsets); +int scf_string_match_kmp_cstr(const uint8_t* T, const uint8_t* P, scf_vector_t* offsets); +int scf_string_match_kmp_cstr_len(const scf_string_t* T, const uint8_t* P, size_t Plen, scf_vector_t* offsets); + +int scf_string_get_offset(scf_string_t* str, const char* data, size_t len); + +#endif + diff --git a/util/scf_vector.h b/util/scf_vector.h new file mode 100644 index 0000000..d8abe21 --- /dev/null +++ b/util/scf_vector.h @@ -0,0 +1,212 @@ +#ifndef SCF_VECTOR_H +#define SCF_VECTOR_H + +#include"scf_def.h" + +typedef struct { + int capacity; + int size; + void** data; +} scf_vector_t; + +#undef NB_MEMBER_INC +#define NB_MEMBER_INC 16 + +static inline scf_vector_t* scf_vector_alloc() +{ + scf_vector_t* v = calloc(1, sizeof(scf_vector_t)); + if (!v) + return NULL; + + v->data = calloc(NB_MEMBER_INC, sizeof(void*)); + if (!v->data) { + free(v); + v = NULL; + return NULL; + } + + v->capacity = NB_MEMBER_INC; + return v; +} + +static inline scf_vector_t* scf_vector_clone(scf_vector_t* origin) +{ + scf_vector_t* clone = calloc(1, sizeof(scf_vector_t)); + if (!clone) + return NULL; + + clone->data = calloc(origin->capacity, sizeof(void*)); + if (!clone->data) { + free(clone); + clone = NULL; + return NULL; + } + + clone->capacity = origin->capacity; + clone->size = origin->size; + memcpy(clone->data, origin->data, origin->size * sizeof(void*)); + return clone; +} + +static inline int scf_vector_cat(scf_vector_t* dst, scf_vector_t* src) +{ + if (!dst || !src) + return -EINVAL; + + int size = dst->size + src->size; + if (size > dst->capacity) { + + void* p = realloc(dst->data, sizeof(void*) * (size + NB_MEMBER_INC)); + if (!p) + return -ENOMEM; + + dst->data = p; + dst->capacity = size + NB_MEMBER_INC; + } + + memcpy(dst->data + dst->size * sizeof(void*), src->data, src->size * sizeof(void*)); + dst->size += src->size; + return 0; +} + +static inline int scf_vector_add(scf_vector_t* v, void* node) +{ + if (!v || !v->data) + return -EINVAL; + + assert(v->size <= v->capacity); + + if (v->size == v->capacity) { + void* p = realloc(v->data, sizeof(void*) * (v->capacity + NB_MEMBER_INC)); + if (!p) + return -ENOMEM; + + v->data = p; + v->capacity += NB_MEMBER_INC; + } + + v->data[v->size++] = node; + return 0; +} + +static inline int scf_vector_add_front(scf_vector_t* v, void* node) +{ + int ret = scf_vector_add(v, node); + if (ret < 0) + return ret; + + int i; + for (i = v->size - 2; i >= 0; i--) + v->data[i + 1] = v->data[i]; + + v->data[0] = node; + return 0; +} + +static inline int scf_vector_del(scf_vector_t* v, void* node) +{ + if (!v || !v->data) + return -EINVAL; + + assert(v->size <= v->capacity); + + int i; + for (i = 0; i < v->size; i++) { + + if (v->data[i] != node) + continue; + + int j; + for (j = i + 1; j < v->size; j++) + v->data[j - 1] = v->data[j]; + + v->size--; + + if (v->size + NB_MEMBER_INC * 2 < v->capacity) { + void* p = realloc(v->data, sizeof(void*) * (v->capacity - NB_MEMBER_INC)); + if (p) { + v->data = p; + v->capacity -= NB_MEMBER_INC; + } + } + return 0; + } + + return -1; +} + +static inline void* scf_vector_find(const scf_vector_t* v, const void* node) +{ + int i; + for (i = 0; i < v->size; i++) { + if (node == v->data[i]) + return v->data[i]; + } + return NULL; +} + +static inline int scf_vector_add_unique(scf_vector_t* v, void* node) +{ + if (!scf_vector_find(v, node)) + return scf_vector_add(v, node); + return 0; +} + +static inline void* scf_vector_find_cmp(const scf_vector_t* v, const void* node, int (*cmp)(const void*, const void*)) +{ + int i; + for (i = 0; i < v->size; i++) { + if (0 == cmp(node, v->data[i])) + return v->data[i]; + } + return NULL; +} + +static inline int scf_vector_qsort(const scf_vector_t* v, int (*cmp)(const void*, const void*)) +{ + if (!v || !v->data || 0 == v->size || !cmp) + return -EINVAL; + + qsort(v->data, v->size, sizeof(void*), cmp); + return 0; +} + +static inline void scf_vector_clear(scf_vector_t* v, void (*type_free)(void*)) +{ + if (!v || !v->data) + return; + + if (type_free) { + int i; + for (i = 0; i < v->size; i++) { + if (v->data[i]) { + type_free(v->data[i]); + v->data[i] = NULL; + } + } + } + + v->size = 0; + + if (v->capacity > NB_MEMBER_INC) { + void* p = realloc(v->data, sizeof(void*) * NB_MEMBER_INC); + if (p) { + v->data = p; + v->capacity = NB_MEMBER_INC; + } + } +} + +static inline void scf_vector_free(scf_vector_t* v) +{ + if (v) { + if (v->data) + free(v->data); + + free(v); + v = NULL; + } +} + +#endif + -- 2.25.1