mem optimization browser

This commit is contained in:
boreddevnl 2026-03-04 20:28:53 +01:00
parent 903d4e0510
commit 9bb62a5ade
7 changed files with 393 additions and 90 deletions

BIN
.DS_Store vendored

Binary file not shown.

Binary file not shown.

View file

@ -1,28 +0,0 @@
Traceback (most recent call last):
File "<frozen runpy>", line 198, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "/opt/homebrew/Cellar/python@3.14/3.14.2/Frameworks/Python.framework/Versions/3.14/lib/python3.14/http/server.py", line 1432, in <module>
test(
~~~~^
HandlerClass=handler_class,
^^^^^^^^^^^^^^^^^^^^^^^^^^^
...<6 lines>...
tls_password=tls_key_password,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/opt/homebrew/Cellar/python@3.14/3.14.2/Frameworks/Python.framework/Versions/3.14/lib/python3.14/http/server.py", line 1348, in test
server = ServerClass(addr, HandlerClass)
File "/opt/homebrew/Cellar/python@3.14/3.14.2/Frameworks/Python.framework/Versions/3.14/lib/python3.14/socketserver.py", line 457, in __init__
self.server_bind()
~~~~~~~~~~~~~~~~^^
File "/opt/homebrew/Cellar/python@3.14/3.14.2/Frameworks/Python.framework/Versions/3.14/lib/python3.14/http/server.py", line 1419, in server_bind
return super().server_bind()
~~~~~~~~~~~~~~~~~~~^^
File "/opt/homebrew/Cellar/python@3.14/3.14.2/Frameworks/Python.framework/Versions/3.14/lib/python3.14/http/server.py", line 148, in server_bind
socketserver.TCPServer.server_bind(self)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
File "/opt/homebrew/Cellar/python@3.14/3.14.2/Frameworks/Python.framework/Versions/3.14/lib/python3.14/socketserver.py", line 478, in server_bind
self.socket.bind(self.server_address)
~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
OSError: [Errno 48] Address already in use

View file

@ -38,5 +38,6 @@
#define PBUF_POOL_SIZE 256 #define PBUF_POOL_SIZE 256
#define MEMP_NUM_TCP_SEG 128 #define MEMP_NUM_TCP_SEG 128
#define MEMP_NUM_PBUF 256 #define MEMP_NUM_PBUF 256
#define MEMP_NUM_TCP_PCB 16
#endif /* LWIPOPTS_H */ #endif /* LWIPOPTS_H */

View file

@ -273,14 +273,25 @@ int network_tcp_recv(void *buf, size_t max_len) {
} }
int network_tcp_close(void) { int network_tcp_close(void) {
if (!current_tcp_pcb || network_processing) return 0; if (network_processing) return 0;
network_processing = 1; network_processing = 1;
tcp_close(current_tcp_pcb);
current_tcp_pcb = NULL; // Free any pending receive buffers first
if (tcp_recv_queue) { if (tcp_recv_queue) {
pbuf_free(tcp_recv_queue); pbuf_free(tcp_recv_queue);
tcp_recv_queue = NULL; tcp_recv_queue = NULL;
} }
if (current_tcp_pcb) {
// Use tcp_abort for immediate cleanup — tcp_close leaves PCBs in TIME_WAIT
// which pile up since sys_check_timeouts isn't called between page loads
tcp_abort(current_tcp_pcb);
current_tcp_pcb = NULL;
}
tcp_closed = 0;
tcp_connect_done = 0;
tcp_connect_error = 0;
network_processing = 0; network_processing = 0;
return 0; return 0;
} }

View file

@ -22,6 +22,16 @@
#define COLOR_SCROLL_BG 0xFFEEEEEE #define COLOR_SCROLL_BG 0xFFEEEEEE
#define COLOR_SCROLL_BTN 0xFFCCCCCC #define COLOR_SCROLL_BTN 0xFFCCCCCC
#define BTN_W 30
#define BTN_H 22
#define BTN_PAD 4
#define HOME_BTN_X (WIN_W - SCROLL_BAR_W - BTN_W - BTN_PAD)
#define BACK_BTN_X (HOME_BTN_X - BTN_W - BTN_PAD)
#define HISTORY_MAX 32
static char history_stack[HISTORY_MAX][512];
static int history_count = 0;
static char* strstr(const char* haystack, const char* needle) { static char* strstr(const char* haystack, const char* needle) {
if (!*needle) return (char*)haystack; if (!*needle) return (char*)haystack;
for (; *haystack; haystack++) { for (; *haystack; haystack++) {
@ -113,7 +123,9 @@ static int total_content_height = 0;
static int focused_element = -1; static int focused_element = -1;
static void parse_html(const char *html); static void parse_html(const char *html);
static void parse_html_incremental(const char *html, int safe_len);
static void browser_paint(void); static void browser_paint(void);
static int inc_parse_offset = 0;
static void browser_clear(void) { static void browser_clear(void) {
for (int i = 0; i < element_count; i++) { for (int i = 0; i < element_count; i++) {
@ -216,25 +228,33 @@ static int fetch_content(const char *url, char *dest_buf, int max_len, bool prog
int total = 0; int total = 0;
int last_render = 0; int last_render = 0;
if (progressive) inc_parse_offset = 0; // Reset incremental state for new page
while (1) { while (1) {
int len = sys_tcp_recv(dest_buf + total, max_len - 1 - total); int len = sys_tcp_recv(dest_buf + total, max_len - 1 - total);
if (len <= 0) break; if (len <= 0) break;
total += len; total += len;
if (total >= max_len - 1) break; if (total >= max_len - 1) break;
if (progressive && total - last_render > 16384) { if (progressive && total - last_render > 32768) {
dest_buf[total] = 0; dest_buf[total] = 0;
char *body = strstr(dest_buf, "\r\n\r\n"); char *body = strstr(dest_buf, "\r\n\r\n");
if (body) { if (body) {
body += 4; body += 4;
if (!strstr(dest_buf, "Transfer-Encoding: chunked")) { if (!strstr(dest_buf, "Transfer-Encoding: chunked")) {
parse_html(body); // Find last '>' to avoid splitting a tag
int body_len = total - (body - dest_buf);
int safe_len = body_len;
while (safe_len > 0 && body[safe_len - 1] != '>') safe_len--;
if (safe_len > inc_parse_offset) {
parse_html_incremental(body, safe_len);
browser_paint(); browser_paint();
ui_mark_dirty(win_browser, 0, 0, WIN_W, WIN_H);
last_render = total; last_render = total;
} }
} }
} }
} }
}
dest_buf[total] = 0; dest_buf[total] = 0;
sys_tcp_close(); sys_tcp_close();
return total; return total;
@ -323,6 +343,19 @@ static int cur_line_y = 10;
static int cur_line_x = 10; static int cur_line_x = 10;
static int list_depth = 0; static int list_depth = 0;
// Incremental parser state for progressive rendering
static int inc_center_depth = 0;
static int inc_table_depth = 0;
static bool inc_is_bold = false;
static uint32_t inc_current_color = COLOR_TEXT;
static char inc_current_link[256] = "";
static float inc_current_scale = 15.0f;
static float inc_base_scale = 15.0f;
static bool inc_is_space_pending = false;
static char inc_form_action[256] = "";
static int inc_form_id = 0;
static bool inc_skip_content = false;
static void flush_line(bool centered) { static void flush_line(bool centered) {
if (line_element_count == 0) return; if (line_element_count == 0) return;
int line_w = 0; int line_w = 0;
@ -558,7 +591,7 @@ static void parse_html(const char *html) {
current_form_action[l] = 0; current_form_action[l] = 0;
} else current_form_action[0] = 0; } else current_form_action[0] = 0;
} }
else if (str_istarts_with(tag_name, "head") || str_istarts_with(tag_name, "script") || str_istarts_with(tag_name, "style") || str_istarts_with(tag_name, "title") || str_istarts_with(tag_name, "noscript") || str_istarts_with(tag_name, "meta") || str_istarts_with(tag_name, "link") || str_istarts_with(tag_name, "!doctype")) skip_content = true; else if (str_istarts_with(tag_name, "head") || str_istarts_with(tag_name, "script") || str_istarts_with(tag_name, "style") || str_istarts_with(tag_name, "title") || str_istarts_with(tag_name, "noscript")) skip_content = true;
else if (str_istarts_with(tag_name, "body")) skip_content = false; else if (str_istarts_with(tag_name, "body")) skip_content = false;
else if (str_istarts_with(tag_name, "a")) { else if (str_istarts_with(tag_name, "a")) {
char *href = str_istrstr(attr_buf, "href=\""); char *href = str_istrstr(attr_buf, "href=\"");
@ -692,6 +725,228 @@ static void parse_html(const char *html) {
flush_line(EFF_CENTER); flush_line(EFF_CENTER);
} }
static void parse_html_incremental(const char *html, int safe_len) {
// On first call, reset everything
if (inc_parse_offset == 0) {
browser_clear();
list_depth = 0;
cur_line_y = 10; cur_line_x = 10; line_element_count = 0;
inc_center_depth = 0; inc_table_depth = 0; inc_is_bold = false;
inc_current_color = COLOR_TEXT; inc_current_link[0] = 0;
inc_current_scale = 15.0f; inc_base_scale = 15.0f;
inc_is_space_pending = false;
inc_form_action[0] = 0; inc_form_id = 0;
inc_skip_content = false;
next_form_id = 1;
}
int i = inc_parse_offset;
int center_depth = inc_center_depth;
int table_depth = inc_table_depth;
bool is_bold = inc_is_bold;
uint32_t current_color = inc_current_color;
char current_link[256];
{ int k=0; while(inc_current_link[k]) { current_link[k] = inc_current_link[k]; k++; } current_link[k] = 0; }
float current_scale = inc_current_scale;
float base_scale = inc_base_scale;
bool is_space_pending = inc_is_space_pending;
char current_form_action[256];
{ int k=0; while(inc_form_action[k]) { current_form_action[k] = inc_form_action[k]; k++; } current_form_action[k] = 0; }
int current_form_id = inc_form_id;
bool skip_content = inc_skip_content;
#undef EFF_CENTER
#define EFF_CENTER ((center_depth > 0) && (table_depth == 0))
while (i < safe_len && html[i] && element_count < MAX_ELEMENTS) {
if (html[i] == '<') {
if (html[i+1] == '!' && html[i+2] == '-' && html[i+3] == '-') {
i += 4;
while (i < safe_len && html[i] && !(html[i] == '-' && html[i+1] == '-' && html[i+2] == '>')) i++;
if (html[i]) i += 3;
continue;
}
i++; char tag_name[64]; int tag_idx = 0;
while (i < safe_len && html[i] && html[i] != '>' && html[i] != ' ' && tag_idx < 63) tag_name[tag_idx++] = html[i++];
tag_name[tag_idx] = 0;
char attr_buf[1024] = "";
if (html[i] == ' ') {
i++; int a_idx = 0;
while (i < safe_len && html[i] && html[i] != '>' && a_idx < 1023) attr_buf[a_idx++] = html[i++];
attr_buf[a_idx] = 0;
}
if (html[i] == '>') i++;
decode_html_entities(attr_buf);
if (tag_name[0] == '/') {
if (str_istarts_with(tag_name+1, "center")) { flush_line(EFF_CENTER); if (center_depth > 0) center_depth--; }
else if (str_istarts_with(tag_name+1, "table")) { flush_line(EFF_CENTER); if (table_depth > 0) table_depth--; }
else if (str_istarts_with(tag_name+1, "ul") || str_istarts_with(tag_name+1, "ol")) { flush_line(EFF_CENTER); if (list_depth > 0) list_depth--; }
else if (tag_name[1] == 'h' && tag_name[2] >= '1' && tag_name[2] <= '6') { flush_line(EFF_CENTER); cur_line_y += 10; is_bold = false; base_scale = 15.0f; current_scale = 15.0f; }
else if (str_istarts_with(tag_name+1, "form")) {
flush_line(EFF_CENTER);
current_form_id = 0; current_form_action[0] = 0;
}
else if (str_istarts_with(tag_name+1, "a")) current_link[0] = 0;
else if (str_istarts_with(tag_name+1, "p") || str_istarts_with(tag_name+1, "li") || str_istarts_with(tag_name+1, "div")) flush_line(EFF_CENTER);
else if (str_istarts_with(tag_name+1, "font")) { current_color = COLOR_TEXT; current_scale = base_scale; }
else if (str_istarts_with(tag_name+1, "head") || (tag_name[1] == 's' && tag_name[2] == 'c') || (tag_name[1] == 's' && tag_name[2] == 'i') || (tag_name[1] == 's' && tag_name[2] == 't') || str_istarts_with(tag_name+1, "title") || str_istarts_with(tag_name+1, "noscript") || str_istarts_with(tag_name+1, "style")) skip_content = false;
} else {
if (str_istarts_with(tag_name, "center")) { flush_line(EFF_CENTER); center_depth++; }
else if (str_istarts_with(tag_name, "table")) { flush_line(EFF_CENTER); table_depth++; }
else if (str_istarts_with(tag_name, "ul") || str_istarts_with(tag_name, "ol")) { flush_line(EFF_CENTER); list_depth++; }
else if (tag_name[0] == 'h' && tag_name[1] >= '1' && tag_name[1] <= '6') {
flush_line(EFF_CENTER); cur_line_y += 10; is_bold = true;
if (tag_name[1] == '1') base_scale = 32.0f;
else if (tag_name[1] == '2') base_scale = 24.0f;
else if (tag_name[1] == '3') base_scale = 20.0f;
else base_scale = 18.0f;
current_scale = base_scale;
}
else if (str_istarts_with(tag_name, "font")) {
char *color_str = str_istrstr(attr_buf, "color=\"");
if (color_str) {
current_color = parse_html_color(color_str + 7);
} else {
color_str = str_istrstr(attr_buf, "color=");
if (color_str) current_color = parse_html_color(color_str + 6);
}
char *size_str = str_istrstr(attr_buf, "size=\"");
int offset = 0;
if (size_str) { offset = 6; } else { size_str = str_istrstr(attr_buf, "size="); if (size_str) offset = 5; }
if (size_str) {
char s_char = size_str[offset];
if (s_char == '+') { int inc = size_str[offset+1] - '0'; int new_sz = 3 + inc; if (new_sz > 7) new_sz = 7; if (new_sz < 1) new_sz = 1; s_char = '0' + new_sz; }
else if (s_char == '-') { int dec = size_str[offset+1] - '0'; int new_sz = 3 - dec; if (new_sz > 7) new_sz = 7; if (new_sz < 1) new_sz = 1; s_char = '0' + new_sz; }
if (s_char == '1') current_scale = 10.0f;
else if (s_char == '2') current_scale = 13.0f;
else if (s_char == '3') current_scale = 15.0f;
else if (s_char == '4') current_scale = 18.0f;
else if (s_char == '5') current_scale = 24.0f;
else if (s_char == '6') current_scale = 32.0f;
else if (s_char >= '7' && s_char <= '9') current_scale = 48.0f;
}
}
else if (str_istarts_with(tag_name, "br")) flush_line(EFF_CENTER);
else if (str_istarts_with(tag_name, "p") || str_istarts_with(tag_name, "div")) flush_line(EFF_CENTER);
else if (str_istarts_with(tag_name, "li")) {
flush_line(EFF_CENTER);
RenderElement *el = &elements[element_count++];
for (int k=0; k<(int)sizeof(RenderElement); k++) ((char*)el)[k] = 0;
el->tag = TAG_NONE;
el->content[0] = '-'; el->content[1] = ' '; el->content[2] = 0;
el->w = ui_get_string_width_scaled("- ", current_scale);
el->h = ui_get_font_height_scaled(current_scale);
el->color = current_color; el->centered = EFF_CENTER; el->bold = is_bold; el->scale = current_scale;
line_elements[line_element_count++] = element_count - 1;
cur_line_x += el->w;
}
else if (str_istarts_with(tag_name, "form")) {
flush_line(EFF_CENTER);
current_form_id = next_form_id++;
char *action = str_istrstr(attr_buf, "action=\"");
if (action) { action += 8; int l = 0; while(action[l] && action[l] != '"' && l < 255) { current_form_action[l] = action[l]; l++; } current_form_action[l] = 0; }
else current_form_action[0] = 0;
}
else if (str_istarts_with(tag_name, "head") || str_istarts_with(tag_name, "script") || str_istarts_with(tag_name, "style") || str_istarts_with(tag_name, "title") || str_istarts_with(tag_name, "noscript")) skip_content = true;
else if (str_istarts_with(tag_name, "body")) skip_content = false;
else if (str_istarts_with(tag_name, "a")) {
char *href = str_istrstr(attr_buf, "href=\"");
if (href) { href += 6; int l = 0; while(href[l] && href[l] != '"' && l < 255) { current_link[l] = href[l]; l++; } current_link[l] = 0; }
} else if (str_istarts_with(tag_name, "hr")) {
flush_line(EFF_CENTER);
RenderElement *el = &elements[element_count++];
for (int k=0; k<(int)sizeof(RenderElement); k++) ((char*)el)[k] = 0;
el->tag = TAG_HR; el->w = WIN_W - SCROLL_BAR_W - 40; el->h = 10; el->centered = true;
line_elements[line_element_count++] = element_count - 1;
flush_line(EFF_CENTER);
} else if (str_istarts_with(tag_name, "img")) {
RenderElement *el = &elements[element_count++];
for (int k=0; k<(int)sizeof(RenderElement); k++) ((char*)el)[k] = 0;
el->tag = TAG_IMG; el->w = 100; el->h = 80; el->centered = EFF_CENTER;
char *src = str_istrstr(attr_buf, "src=\"");
if (src) { src += 5; int l = 0; while(src[l] && src[l] != '"' && l < 255) { el->attr_value[l] = src[l]; l++; } el->attr_value[l] = 0; load_image(el); }
if (el->img_pixels) { el->w = el->img_w; el->h = el->img_h; }
line_elements[line_element_count++] = element_count - 1;
if (EFF_CENTER || cur_line_x + el->w > WIN_W - SCROLL_BAR_W - 20) flush_line(EFF_CENTER);
else cur_line_x += el->w;
} else if (str_istarts_with(tag_name, "input")) {
RenderElement *el = &elements[element_count++];
for (int k=0; k<(int)sizeof(RenderElement); k++) ((char*)el)[k] = 0;
el->tag = TAG_INPUT; el->w = 160; el->h = 20; el->centered = EFF_CENTER;
char *val = str_istrstr(attr_buf, "value=\""); char *ph = str_istrstr(attr_buf, "placeholder=\"");
char *type = str_istrstr(attr_buf, "type=\""); char *name = str_istrstr(attr_buf, "name=\"");
el->form_id = current_form_id; el->input_cursor = 0; el->input_scroll = 0;
int l; l = 0; while(current_form_action[l]) { el->form_action[l] = current_form_action[l]; l++; } el->form_action[l] = 0;
if (name) { name += 6; l = 0; while(name[l] && name[l] != '"' && l < 63) { el->input_name[l] = name[l]; l++; } el->input_name[l] = 0; }
else { l = 0; const char *dn = "q"; while(dn[l]) { el->input_name[l] = dn[l]; l++; } el->input_name[l] = 0; }
if (type && str_istarts_with(type+6, "submit")) el->tag = TAG_BUTTON;
if (val) { val += 7; int l = 0; while(val[l] && val[l] != '"' && l < 255) { el->attr_value[l] = val[l]; l++; } el->attr_value[l] = 0; }
else if (ph) { ph += 13; int l = 0; while(ph[l] && ph[l] != '"' && l < 255) { el->attr_value[l] = ph[l]; l++; } el->attr_value[l] = 0; }
else el->attr_value[0] = 0;
if (el->tag == TAG_BUTTON) el->w = ui_get_string_width(el->attr_value) + 20;
line_elements[line_element_count++] = element_count - 1;
if (EFF_CENTER || cur_line_x + el->w > WIN_W - SCROLL_BAR_W - 20) flush_line(EFF_CENTER);
else cur_line_x += el->w;
}
}
} else {
if (!skip_content) {
while (i < safe_len && html[i] && (html[i] == ' ' || html[i] == '\n' || html[i] == '\r')) {
is_space_pending = true; i++;
}
while (i < safe_len && html[i] && html[i] != '<') {
char word[256]; int w_idx = 0;
if (is_space_pending) { if (cur_line_x > 10) word[w_idx++] = ' '; is_space_pending = false; }
while (i < safe_len && html[i] && html[i] != '<' && html[i] != ' ' && html[i] != '\n' && html[i] != '\r' && w_idx < 254) {
word[w_idx++] = html[i++];
}
if (html[i] == ' ' || html[i] == '\n' || html[i] == '\r') {
is_space_pending = true;
while (i < safe_len && html[i] && (html[i] == ' ' || html[i] == '\n' || html[i] == '\r')) i++;
}
word[w_idx] = 0;
decode_html_entities(word);
w_idx = 0; while (word[w_idx]) w_idx++;
if (w_idx > 0) {
if (element_count >= MAX_ELEMENTS) break;
int word_w = ui_get_string_width_scaled(word, current_scale);
if (cur_line_x + word_w > WIN_W - SCROLL_BAR_W - 20) {
flush_line(EFF_CENTER);
if (word[0] == ' ') { for (int k=0; k<w_idx; k++) word[k] = word[k+1]; word_w = ui_get_string_width_scaled(word, current_scale); if (word[0] == 0) continue; }
}
RenderElement *el = &elements[element_count++];
for (int k=0; k<(int)sizeof(RenderElement); k++) ((char*)el)[k] = 0;
int k=0; while(word[k]) { el->content[k] = word[k]; k++; } el->content[k] = 0;
el->w = word_w; el->h = ui_get_font_height_scaled(current_scale);
el->tag = TAG_NONE; el->color = current_link[0] ? COLOR_LINK : current_color;
el->centered = EFF_CENTER; el->bold = is_bold; el->scale = current_scale;
if (current_link[0]) { int k=0; while(current_link[k]) { el->link_url[k] = current_link[k]; k++; } el->link_url[k] = 0; }
line_elements[line_element_count++] = element_count - 1;
cur_line_x += el->w;
}
}
} else {
while (i < safe_len && html[i] && html[i] != '<') i++;
}
}
}
flush_line(EFF_CENTER);
// Save state for next incremental call
inc_parse_offset = i;
inc_center_depth = center_depth;
inc_table_depth = table_depth;
inc_is_bold = is_bold;
inc_current_color = current_color;
{ int k=0; while(current_link[k]) { inc_current_link[k] = current_link[k]; k++; } inc_current_link[k] = 0; }
inc_current_scale = current_scale;
inc_base_scale = base_scale;
inc_is_space_pending = is_space_pending;
{ int k=0; while(current_form_action[k]) { inc_form_action[k] = current_form_action[k]; k++; } inc_form_action[k] = 0; }
inc_form_id = current_form_id;
inc_skip_content = skip_content;
}
static void browser_paint(void) { static void browser_paint(void) {
ui_draw_rect(win_browser, 0, 0, WIN_W, WIN_H, COLOR_BG); ui_draw_rect(win_browser, 0, 0, WIN_W, WIN_H, COLOR_BG);
@ -758,6 +1013,24 @@ static void browser_paint(void) {
ui_draw_rect(win_browser, 10 + cx, 22, 8, 2, COLOR_URL_TEXT); ui_draw_rect(win_browser, 10 + cx, 22, 8, 2, COLOR_URL_TEXT);
} }
// Back button
int btn_y = (URL_BAR_H - BTN_H) / 2;
uint32_t back_col = history_count > 0 ? 0xFF505050 : 0xFF404040;
ui_draw_rect(win_browser, BACK_BTN_X, btn_y, BTN_W, BTN_H, back_col);
ui_draw_rect(win_browser, BACK_BTN_X, btn_y, BTN_W, 1, 0xFF606060);
ui_draw_rect(win_browser, BACK_BTN_X, btn_y, 1, BTN_H, 0xFF606060);
ui_draw_rect(win_browser, BACK_BTN_X, btn_y + BTN_H - 1, BTN_W, 1, 0xFF202020);
ui_draw_rect(win_browser, BACK_BTN_X + BTN_W - 1, btn_y, 1, BTN_H, 0xFF202020);
ui_draw_string(win_browser, BACK_BTN_X + 10, btn_y + 4, "<", history_count > 0 ? 0xFFFFFFFF : 0xFF808080);
// Home button
ui_draw_rect(win_browser, HOME_BTN_X, btn_y, BTN_W, BTN_H, 0xFF505050);
ui_draw_rect(win_browser, HOME_BTN_X, btn_y, BTN_W, 1, 0xFF606060);
ui_draw_rect(win_browser, HOME_BTN_X, btn_y, 1, BTN_H, 0xFF606060);
ui_draw_rect(win_browser, HOME_BTN_X, btn_y + BTN_H - 1, BTN_W, 1, 0xFF202020);
ui_draw_rect(win_browser, HOME_BTN_X + BTN_W - 1, btn_y, 1, BTN_H, 0xFF202020);
ui_draw_string(win_browser, HOME_BTN_X + 10, btn_y + 4, "H", 0xFFFFFFFF);
// Scroll bar // Scroll bar
ui_draw_rect(win_browser, WIN_W - SCROLL_BAR_W, URL_BAR_H, SCROLL_BAR_W, WIN_H - URL_BAR_H, COLOR_SCROLL_BG); ui_draw_rect(win_browser, WIN_W - SCROLL_BAR_W, URL_BAR_H, SCROLL_BAR_W, WIN_H - URL_BAR_H, COLOR_SCROLL_BG);
int thumb_h = (WIN_H - URL_BAR_H) * (WIN_H - URL_BAR_H) / (total_content_height > WIN_H ? total_content_height : WIN_H); int thumb_h = (WIN_H - URL_BAR_H) * (WIN_H - URL_BAR_H) / (total_content_height > WIN_H ? total_content_height : WIN_H);
@ -777,8 +1050,11 @@ static void navigate(const char *url) {
int body_len = resp_len - hdr_len; int body_len = resp_len - hdr_len;
if (strstr(main_resp, "Transfer-Encoding: chunked")) { if (strstr(main_resp, "Transfer-Encoding: chunked")) {
body_len = decode_chunked_bin(body, body_len); body_len = decode_chunked_bin(body, body_len);
}
parse_html(body); parse_html(body);
} else {
// For non-chunked: finish incremental parse instead of re-parsing from scratch
parse_html_incremental(body, body_len);
}
} }
} }
@ -807,7 +1083,24 @@ int main(int argc, char **argv) {
needs_repaint = true; needs_repaint = true;
continue; continue;
} }
if (ev.arg2 < URL_BAR_H) { focused_element = -1; needs_repaint = true; continue; } if (ev.arg2 < URL_BAR_H) {
// Check back button
if (mx >= BACK_BTN_X && mx < BACK_BTN_X + BTN_W && history_count > 0) {
history_count--;
int j=0; while(history_stack[history_count][j]) { url_input_buffer[j] = history_stack[history_count][j]; j++; } url_input_buffer[j] = 0; url_cursor = j;
navigate(url_input_buffer); scroll_y = 0; focused_element = -1;
needs_repaint = true; continue;
}
// Check home button
if (mx >= HOME_BTN_X && mx < HOME_BTN_X + BTN_W) {
if (history_count < HISTORY_MAX) { int j=0; while(url_input_buffer[j]) { history_stack[history_count][j] = url_input_buffer[j]; j++; } history_stack[history_count][j] = 0; history_count++; }
const char *home = "http://find.boreddev.nl";
int j=0; while(home[j]) { url_input_buffer[j] = home[j]; j++; } url_input_buffer[j] = 0; url_cursor = j;
navigate(url_input_buffer); scroll_y = 0; focused_element = -1;
needs_repaint = true; continue;
}
focused_element = -1; needs_repaint = true; continue;
}
int my = ev.arg2 - URL_BAR_H + scroll_y; int my = ev.arg2 - URL_BAR_H + scroll_y;
bool found = false; bool found = false;
for (int i = 0; i < element_count; i++) { for (int i = 0; i < element_count; i++) {
@ -864,6 +1157,7 @@ int main(int argc, char **argv) {
else *u++ = sc; else *u++ = sc;
} }
*u = 0; *u = 0;
if (history_count < HISTORY_MAX) { int j=0; while(url_input_buffer[j]) { history_stack[history_count][j] = url_input_buffer[j]; j++; } history_stack[history_count][j] = 0; history_count++; }
int j=0; while(search_url[j]) { url_input_buffer[j] = search_url[j]; j++; } url_input_buffer[j] = 0; url_cursor = j; int j=0; while(search_url[j]) { url_input_buffer[j] = search_url[j]; j++; } url_input_buffer[j] = 0; url_cursor = j;
navigate(url_input_buffer); scroll_y = 0; focused_element = -1; navigate(url_input_buffer); scroll_y = 0; focused_element = -1;
needs_repaint = true; needs_repaint = true;
@ -894,6 +1188,7 @@ int main(int argc, char **argv) {
if (current_host[0] && current_host[0] != '/') *u++ = '/'; if (current_host[0] && current_host[0] != '/') *u++ = '/';
s = el->link_url; while(*s) *u++ = *s++; *u = 0; s = el->link_url; while(*s) *u++ = *s++; *u = 0;
} }
if (history_count < HISTORY_MAX) { int j=0; while(url_input_buffer[j]) { history_stack[history_count][j] = url_input_buffer[j]; j++; } history_stack[history_count][j] = 0; history_count++; }
int j=0; while(new_url[j]) { url_input_buffer[j] = new_url[j]; j++; } url_input_buffer[j] = 0; url_cursor = j; int j=0; while(new_url[j]) { url_input_buffer[j] = new_url[j]; j++; } url_input_buffer[j] = 0; url_cursor = j;
navigate(url_input_buffer); scroll_y = 0; focused_element = -1; navigate(url_input_buffer); scroll_y = 0; focused_element = -1;
needs_repaint = true; needs_repaint = true;
@ -905,7 +1200,10 @@ int main(int argc, char **argv) {
} else if (ev.type == GUI_EVENT_KEY) { } else if (ev.type == GUI_EVENT_KEY) {
char c = (char)ev.arg1; char c = (char)ev.arg1;
if (focused_element == -1) { if (focused_element == -1) {
if (c == 13 || c == 10) { navigate(url_input_buffer); scroll_y = 0; } if (c == 13 || c == 10) {
if (history_count < HISTORY_MAX) { int j=0; while(url_input_buffer[j]) { history_stack[history_count][j] = url_input_buffer[j]; j++; } history_stack[history_count][j] = 0; history_count++; }
navigate(url_input_buffer); scroll_y = 0;
}
else if (c == 19) { if (url_cursor > 0) url_cursor--; } else if (c == 19) { if (url_cursor > 0) url_cursor--; }
else if (c == 20) { int len = 0; while(url_input_buffer[len]) len++; if (url_cursor < len) url_cursor++; } else if (c == 20) { int len = 0; while(url_input_buffer[len]) len++; if (url_cursor < len) url_cursor++; }
else if (c == 127 || c == 8) { else if (c == 127 || c == 8) {

View file

@ -1,85 +1,106 @@
#include "stdlib.h" #include "stdlib.h"
#include "syscall.h" #include "syscall.h"
// Simple block allocator over sys_sbrk // Block allocator over sys_sbrk with coalescing and splitting
typedef struct BlockMeta { typedef struct BlockMeta {
size_t size; size_t size;
int free; int free;
struct BlockMeta *next; struct BlockMeta *next;
struct BlockMeta *prev;
} BlockMeta; } BlockMeta;
#define META_SIZE sizeof(BlockMeta) #define META_SIZE sizeof(BlockMeta)
#define MIN_SPLIT_SIZE 64
#define ALIGN8(x) (((x) + 7) & ~(size_t)7)
static void *global_base = NULL; static BlockMeta *heap_head = NULL;
static BlockMeta *heap_tail = NULL;
static BlockMeta *find_free_block(BlockMeta **last, size_t size) {
BlockMeta *current = global_base;
while (current && !(current->free && current->size >= size)) {
*last = current;
current = current->next;
}
return current;
}
static BlockMeta *request_space(BlockMeta* last, size_t size) {
BlockMeta *block;
block = (BlockMeta *)sys_sbrk(0);
size_t align = 8;
if (size % align != 0) {
size += align - (size % align);
}
static BlockMeta *request_space(size_t size) {
BlockMeta *block = (BlockMeta *)sys_sbrk(0);
void *request = sys_sbrk(size + META_SIZE); void *request = sys_sbrk(size + META_SIZE);
if (request == (void*)-1) { if (request == (void*)-1) return NULL;
return NULL; // sbrk failed
}
if (last) { // NULL on first request
last->next = block;
}
block->size = size; block->size = size;
block->next = NULL;
block->free = 0; block->free = 0;
block->next = NULL;
block->prev = heap_tail;
if (heap_tail) heap_tail->next = block;
else heap_head = block;
heap_tail = block;
return block; return block;
} }
// Split a block if remainder is large enough
static void split_block(BlockMeta *block, size_t size) {
size_t remain = block->size - size - META_SIZE;
if (block->size < size + META_SIZE + MIN_SPLIT_SIZE) return;
BlockMeta *new_block = (BlockMeta *)((char *)(block + 1) + size);
new_block->size = remain;
new_block->free = 1;
new_block->next = block->next;
new_block->prev = block;
if (block->next) block->next->prev = new_block;
else heap_tail = new_block;
block->next = new_block;
block->size = size;
}
void *malloc(size_t size) { void *malloc(size_t size) {
BlockMeta *block; if (size == 0) return NULL;
if (size <= 0) {
return NULL; size = ALIGN8(size);
// Best-fit search
BlockMeta *best = NULL;
BlockMeta *current = heap_head;
while (current) {
if (current->free && current->size >= size) {
if (!best || current->size < best->size) {
best = current;
if (best->size == size) break; // Perfect fit
}
}
current = current->next;
} }
// Align size to 8 bytes for safety if (best) {
size_t align = 8; split_block(best, size);
if (size % align != 0) { best->free = 0;
size += align - (size % align); return (best + 1);
} }
if (!global_base) { // First call // No suitable block found, request more space
block = request_space(NULL, size); BlockMeta *block = request_space(size);
if (!block) return NULL; if (!block) return NULL;
global_base = block;
} else {
BlockMeta *last = global_base;
block = find_free_block(&last, size);
if (!block) { // Failed to find free block
block = request_space(last, size);
if (!block) return NULL;
} else { // Found free block
block->free = 0;
}
}
return (block + 1); return (block + 1);
} }
void free(void *ptr) { void free(void *ptr) {
if (!ptr) { if (!ptr) return;
return;
}
BlockMeta *block = (BlockMeta *)ptr - 1; BlockMeta *block = (BlockMeta *)ptr - 1;
block->free = 1; block->free = 1;
// Coalesce with next block
if (block->next && block->next->free) {
block->size += META_SIZE + block->next->size;
block->next = block->next->next;
if (block->next) block->next->prev = block;
else heap_tail = block;
}
// Coalesce with previous block
if (block->prev && block->prev->free) {
block->prev->size += META_SIZE + block->size;
block->prev->next = block->next;
if (block->next) block->next->prev = block->prev;
else heap_tail = block->prev;
}
} }
void *calloc(size_t nelem, size_t elsize) { void *calloc(size_t nelem, size_t elsize) {