CTask* browser_task = Fs; JsonObject* cyberia = Json.CreateObject(Fs); Window* win = NULL; TabPanelWidget* tabpanel1 = NULL; RectWidget* controlsbackdrop1 = NULL; TextLabelWidget* status1 = NULL; TextLabelWidget* status2 = NULL; RectWidget* statusbackdrop1 = NULL; VerticalScrollBarWidget* vscroll1 = NULL; U8* previous_hovered_href = NULL; ButtonWidget* backbtn1 = NULL; ButtonWidget* fwdbtn1 = NULL; ButtonWidget* refreshbtn1 = NULL; ButtonWidget* hanbagabtn1 = NULL; RectWidget* background1 = NULL; TextInputWidget* addressbar1 = NULL; @window_widgets_list* widgets_base = NULL; @html_dom_node* node_list = NULL; I64 old_window_width = -1; I64 old_window_height = -1; I64 old_mouse_z = Mouse.z; Window* hanbaga_menu = Menu.New("Cyberia"); Menu.AddItem(hanbaga_menu, "New Tab", NULL, &Reboot, NULL, NULL); Menu.AddItem(hanbaga_menu, "New Window", NULL, NULL, NULL, NULL); Menu.AddItem(hanbaga_menu, "New Private Window", NULL, NULL, NULL, NULL); class @browser { HtmlRenderer* renderer; JsonArray* bookmarks; JsonArray* history; I64 history_index; JsonObject* javascript_link_handlers; CTask* task; TabPanelTab* tab; U8* fetch_buffer; U8* lazyload_buffer; U8* lazyload_timeout_buffer; U8* go_to_url_string; U8* search_query; }; @browser* browser = NULL; I64 @cyberia_is_alphanum(I64 c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); } U8* @cyberia_urlencode_str(U8* str, I64* output_len) { if (str == NULL) { return NULL; } I64 len = StrLen(str); U8* encoded_str = NULL; U8* dest; I64 encoded_len = 0; U8* p; I64 c; Bool contains_plus = FALSE; // First pass: calculate the exact required length p = str; while (*p) { c = *p; // Check if the character should NOT be encoded if (@cyberia_is_alphanum(c) || c == '-' || c == '_' || c == '.') { encoded_len++; } else if (c == ' ') { encoded_len++; // Will be replaced by '+' contains_plus = TRUE; } else { encoded_len += 3; // Will be replaced by %XX } ++p; } *output_len = encoded_len; if (encoded_len == StrLen(str) && !contains_plus) return str; // Allocate memory for the encoded string + null terminator encoded_str = CAlloc(encoded_len + 1, browser_task); if (!encoded_str) return NULL; // Second pass: build the encoded string dest = encoded_str; p = str; while (*p) { c = *p; if (@cyberia_is_alphanum(c) || c == '-' || c == '_' || c == '.') { *dest++ = c; } else if (c == ' ') { *dest++ = '+'; } else { StrPrint(dest, "%%%02X", c); dest += 3; } ++p; } *dest = '\0'; return encoded_str; } U0 @cyberia_win_really_close(Window* win) { // Free everything if (win == Compositor.active_win) { Gui.Window.SetFocus(Compositor.GetWindowByTitle("Wallpaper")); } Compositor.UnregisterForGlobalInputEvents(win); Compositor.DestroyWindow(win); Kill(app_event_loop); } U0 @cyberia_win_close(Window* win) { if (KeyDown(SC_CTRL) && KeyDown(Char2ScanCode('w'))) return; @cyberia_win_really_close(win); } U8* @browser_tls_connection_state(@http_response* resp) { if (!resp || !resp->s || !resp->s->ctx) return NULL; I64 connect_state = @tls_connection_status(resp->s->ctx); switch (connect_state) { case 0: return "TLS: Sent Client Hello"; case 1: return "TLS: Parsed Server Hello"; case 2: return "TLS: Key Share"; case 0xff: return "TLS: Finished"; default: return ""; } } extern U0 @cyberia_navigate(Bool refresh = FALSE); U0 @cyberia_hanbaga_show(Widget* widget) { hanbaga_menu->x = win->x + win->width - hanbaga_menu->width - 5; hanbaga_menu->y = win->y + hanbagabtn1->y + hanbagabtn1->height + 24; Gui.Window.Show(hanbaga_menu); Gui.Window.SetFocus(hanbaga_menu); Gui.Window.Refresh(hanbaga_menu); } U0 @cyberia_link_clicked(Widget* widget) { @html_dom_node* node = @self_or_ancestor_matches_tag_name(widget->data, "a"); if (!node) return; U8* unresolved_href = node->attributes->@("href"); if (!unresolved_href) return; U8* resolved_href = @resolve_href(browser->renderer, unresolved_href); if (!resolved_href) return; StrCpy(&addressbar1->text, resolved_href); Free(resolved_href); Spawn(&@cyberia_navigate); } U0 @cyberia_collect_form_nodes(JsonArray* array, @html_dom_node* node) { if (!array || !node) return; if (!StrICmp(node->tagName, "input") || !StrICmp(node->tagName, "textarea")) { if (node->attributes->@("name")) { array->append(node, JSON_NUMBER); } } I64 i; if (node->children->length) { for (i = 0; i < node->children->length; i++) @cyberia_collect_form_nodes(array, node->children->@(i)); } } I64 @cyberia_count_text_input_elements(@html_dom_node* node) { I64 count = 0; if (!node) return count; if (!StrICmp(node->tagName, "input") && (!node->attributes->@("type") || (!StrICmp(node->attributes->@("type"), "text") || !StrICmp(node->attributes->@("type"), "password")))) ++count; I64 i; if (node->children->length) { for (i = 0; i < node->children->length; i++) count += @cyberia_count_text_input_elements(node->children->@(i)); } return count; } Bool @cyberia_form_has_one_text_input_element(@html_dom_node* form_node) { return @cyberia_count_text_input_elements(form_node); } U0 @cyberia_form_submit(@html_dom_node* form_node) { if (!form_node->attributes->@("method")) form_node->attributes->set("method", "get", JSON_STRING); if (!form_node->attributes->@("action")) form_node->attributes->set("action", "#", JSON_STRING); I64 i; U8* method = form_node->attributes->@("method"); U8* action = @resolve_href(browser->renderer, form_node->attributes->@("action")); @html_dom_node* element_node; JsonArray* form_elements = Json.CreateArray(browser->renderer->task); @cyberia_collect_form_nodes(form_elements, form_node); U8 get_request_str[1024]; if (!StrICmp(method, "get")) { StrPrint(get_request_str, "%s?", action); for (i = 0; i < form_elements->length; i++) { element_node = form_elements->@(i); U8* name = element_node->attributes->@("name"); U8* raw_value = ""; U8* encoded_value = NULL; I64 encoded_value_length = 0; Widget* element_gui_widget = element_node->attributes->@("cyberiaGuiWidget"); switch (element_gui_widget->type) { case WIDGET_TYPE_CHECKBOX: if (element_gui_widget(CheckBoxWidget*)->checked) { raw_value = @t(element_node->attributes->@("value"), element_node->attributes->@("value"), "on"); } else { raw_value = NULL; } break; case WIDGET_TYPE_INPUT: raw_value = &element_gui_widget(TextInputWidget*)->text; break; default: break; } encoded_value = @cyberia_urlencode_str(raw_value, &encoded_value_length); if (encoded_value) { String.Append(get_request_str, "%s=%s", name, encoded_value); if (i < form_elements->length - 1) String.Append(get_request_str, "&"); if (encoded_value_length != StrLen(raw_value)) { Free(encoded_value); } } } StrCpy(&addressbar1->text, &get_request_str); } Free(action); Spawn(&@cyberia_navigate); } U0 @cyberia_form_submit_clicked(Widget* widget) { if (!widget || !widget->data) return; @html_dom_node* form_node = @self_or_ancestor_matches_tag_name(widget->data, "form"); if (!form_node) return; @cyberia_form_submit(form_node); } U0 @cyberia_refresh_clicked() { if (refreshbtn1->disabled) return; Spawn(&@cyberia_navigate, TRUE); } U0 @cyberia_update_history_buttons() { backbtn1->disabled = TRUE; fwdbtn1->disabled = TRUE; if (browser->history_index > 0) backbtn1->disabled = FALSE; if (browser->history->length - 1 > browser->history_index) fwdbtn1->disabled = FALSE; } U0 @cyberia_set_tab_title_and_icon(TabPanelTab* tab, U8* title, Context2D* icon) { if (!tab) return; if (icon) { tab->icon = icon; } if (!title || !StrLen(title)) { StrCpy(&tab->text, "Untitled page"); } if (StrLen(title) > 32) { MemCpy(&tab->text, title, 29); StrCpy(&tab->text + 29, "..."); } else { StrCpy(&tab->text, title); } } U0 @cyberia_set_tab_title(U8* title) { @cyberia_set_tab_title_and_icon(browser->tab, title, NULL); } U0 @cyberia_history() { @cyberia_update_history_buttons; win->focused_widget = NULL; HtmlRenderer* renderer = browser->renderer = browser->history->@(browser->history_index); HttpUrl* url = renderer->current_url; StrCpy(&addressbar1->text, ""); if (!StrICmp(url->host, "127.0.0.255")) { StrCpy(&addressbar1->text, "about:newtab"); } else { Bool is_alternate_port = FALSE; if (!StrICmp(url->scheme, "http://") && url->port != 80) is_alternate_port = TRUE; if (!StrICmp(url->scheme, "https://") && url->port != 443) is_alternate_port = TRUE; if (is_alternate_port) String.Append(&addressbar1->text, "%s%s:%d%s%s", url->scheme, url->host, url->port, url->path, url->query); else String.Append(&addressbar1->text, "%s%s%s%s", url->scheme, url->host, url->path, url->query); } renderer->background_widget->color = renderer->background_color; @cyberia_set_tab_title_and_icon(browser->tab, renderer->current_title, @favicon_for_page(renderer)); widgets_base->next = renderer->widgets_base; @reflow_node_list(renderer); status1->SetText("Done"); refreshbtn1->disabled = FALSE; } U0 @cyberia_back_clicked() { if (backbtn1->disabled) return; if (browser->history_index > 0) { --browser->history_index; @cyberia_history; } } U0 @cyberia_fwd_clicked() { if (fwdbtn1->disabled) return; if (browser->history_index < browser->history->length - 1) { ++browser->history_index; @cyberia_history; } } @window_widgets_list* @cyberia_append_widget_to_list(@window_widgets_list* list, Widget* widget, CTask* task) { while (list->next) { list = list->next; } list->next = CAlloc(sizeof(@window_widgets_list), task); list->next->prev = list; list->next->widget = widget; return list->next; } U8* NEW_TAB_HTML = "New tab"; U0 @cyberia_navigate(Bool refresh = FALSE) { win->focused_widget = NULL; if (!StrLen(&addressbar1->text)) { return; } if (MemCmp(&addressbar1->text, "about:", 6) && MemCmp(&addressbar1->text, "http://", 7) && MemCmp(&addressbar1->text, "https://", 8)) { U8 prepend_buf[512]; StrPrint(prepend_buf, "https://%s", &addressbar1->text); StrCpy(&addressbar1->text, prepend_buf); } U8* url_string = StrNew(&addressbar1->text); if (!url_string || !browser || !browser_task) return; if (!StrCmp(&addressbar1->text, "about:newtab")) { url_string = StrNew("http://127.0.0.255"); } if (!refresh) { browser->renderer = CAlloc(sizeof(HtmlRenderer), browser_task); ++browser->history_index; while (browser->history->@(browser->history_index)) { browser->history->remove(browser->history_index); } browser->history->append(browser->renderer, JSON_NUMBER); @cyberia_update_history_buttons; } HtmlRenderer* renderer = browser->renderer; MemSet(renderer, 0, sizeof(HtmlRenderer)); widgets_base->next = CAlloc(sizeof(@window_widgets_list), browser_task); widgets_base->next->prev = widgets_base; renderer->images = NULL; renderer->link_pointer = Compositor.theme.pointer.link; renderer->link_callback = &@cyberia_link_clicked; renderer->form_submit_callback = &@cyberia_form_submit_clicked; renderer->image_load_callback = &@reflow_node_list; renderer->title_callback = &@cyberia_set_tab_title; renderer->widgets_base = widgets_base->next; renderer->status_widget = status1; renderer->background_widget = background1; renderer->background_color = Color(255, 255, 255); renderer->background_widget->color = renderer->background_color; renderer->vertical_scroll_widget = vscroll1; renderer->win = win; renderer->indent = -1; renderer->current_url_string = StrNew(url_string, browser_task); renderer->current_url = @http_parse_url(url_string); renderer->cache_directory = HTTP_CACHE_DIRECTORY; renderer->task = browser_task; U8 err_msg_buffer[128]; U8 status_text_buffer[1024]; if (!renderer->current_url) { StrCpy(err_msg_buffer, "ERROR: Could not parse URL"); MessageBox.Error(err_msg_buffer); Free(url_string); return; } if (!@is_supported_url_scheme(renderer->current_url)) { StrPrint(err_msg_buffer, "ERROR: Unsupported URL scheme: %s", renderer->current_url->scheme); MessageBox.Error(err_msg_buffer); Free(url_string); return; } HttpUrl* url = renderer->current_url; Bool is_alternate_port = FALSE; if (!StrICmp(url->scheme, "http://") && url->port != 80) is_alternate_port = TRUE; if (!StrICmp(url->scheme, "https://") && url->port != 443) is_alternate_port = TRUE; StrCpy(status_text_buffer, "Fetching "); if (is_alternate_port) String.Append(status_text_buffer, "%s%s:%d%s%s", url->scheme, url->host, url->port, url->path, url->query); else String.Append(status_text_buffer, "%s%s%s%s", url->scheme, url->host, url->path, url->query); String.Append(status_text_buffer, "..."); status1->SetText(status_text_buffer); U8* buffer = browser->fetch_buffer; MemSet(buffer, 0, HTTP_FETCH_BUFFER_SIZE); @http_response* resp = NULL; if (!StrCmp(&addressbar1->text, "about:newtab")) { resp = CAlloc(sizeof(@http_response)); resp->body.data = NEW_TAB_HTML; resp->body.length = StrLen(NEW_TAB_HTML); resp->state = HTTP_STATE_DONE; resp->status.code = 200; StrCpy(&addressbar1->text, ""); win->focused_widget = addressbar1; } else { resp = Http.Get(renderer->current_url, buffer); } while (resp->state != HTTP_STATE_DONE) { if (resp->state >= HTTP_STATE_HEADERS_RECEIVED) { StrPrint(status_text_buffer, "Received %d bytes", resp->body.length); status1->SetText(status_text_buffer); } else { if (@http_scheme_is_https(renderer->current_url)) { if (@browser_tls_connection_state(resp)) { StrPrint(status_text_buffer, "%s", @browser_tls_connection_state(resp)); status1->SetText(status_text_buffer); } } } Sleep(1); } if (resp->status.code == 301 || resp->status.code == 302) { U8* unresolved_location = resp->headers->@("Location"); if (!unresolved_location) return; U8* resolved_location = @resolve_href(browser->renderer, unresolved_location); if (!resolved_location) return; StrCpy(&addressbar1->text, resolved_location); Free(resolved_location); @cyberia_navigate(1); return; } // Create node tree I64 images_count = 0; node_list = @html_tokenize_and_create_node_list(resp->body.data, resp->body.length, renderer->task, &images_count); // Create empty CSS rules array, traverse node tree and populate CSS rules array renderer->css_rules = Json.Clone(CSS_DEFAULT_RULES, renderer->task); renderer->forms = Json.CreateArray(renderer->task); @process_css_rules_from_node_list(node_list, renderer); if (renderer->debug) { "=== Begin dump of CSS rules ===\n"; "%s\n", Json.Stringify(renderer->css_rules, renderer->task); "=== End dump of CSS rules ===\n"; } status1->SetText("Rendering page..."); Sleep(100); @render_node_list(node_list, renderer); @cyberia_set_tab_title_and_icon(browser->tab, renderer->current_title, @favicon_for_page(renderer)); @window_widgets_list* append = renderer->widgets_base; append = @cyberia_append_widget_to_list(append, controlsbackdrop1, browser_task); append = @cyberia_append_widget_to_list(append, backbtn1, browser_task); append = @cyberia_append_widget_to_list(append, fwdbtn1, browser_task); append = @cyberia_append_widget_to_list(append, refreshbtn1, browser_task); append = @cyberia_append_widget_to_list(append, hanbagabtn1, browser_task); append = @cyberia_append_widget_to_list(append, addressbar1, browser_task); append = @cyberia_append_widget_to_list(append, statusbackdrop1, browser_task); append = @cyberia_append_widget_to_list(append, status1, browser_task); append = @cyberia_append_widget_to_list(append, status2, browser_task); append = @cyberia_append_widget_to_list(append, vscroll1, browser_task); append = @cyberia_append_widget_to_list(append, tabpanel1, browser_task); vscroll1->scroll = 0; @reflow_node_list(renderer); @fetch_images_for_page(renderer); status1->SetText("Done"); refreshbtn1->disabled = FALSE; } U0 @cyberia_new_tab() { TabPanelTab* new_tab = CAlloc(sizeof(TabPanelTab)); new_tab->icon = DEFAULT_FAVICON; StrCpy(&new_tab->text, "New Tab"); @browser* new_browser = CAlloc(sizeof(@browser), browser_task); new_browser->history = Json.CreateArray(browser_task); new_browser->history_index = -1; new_browser->tab = new_tab; new_browser->renderer = NULL; new_browser->task = browser_task; new_browser->fetch_buffer = CAlloc(HTTP_FETCH_BUFFER_SIZE, browser_task); new_browser->go_to_url_string = NULL; new_browser->javascript_link_handlers = Json.CreateObject(browser_task); new_browser->lazyload_buffer = CAlloc(HTTP_FETCH_BUFFER_SIZE, browser_task); new_browser->lazyload_timeout_buffer = CAlloc(HTTP_FETCH_BUFFER_SIZE, browser_task); browser = new_tab->data = new_browser; I64 i = 0; TabPanelTab* tabs = tabpanel1->tabs; if (!tabs) { tabpanel1->tabs = new_tab; } else { i = 1; while (tabs->next) { tabs = tabs->next; ++i; } tabs->next = new_tab; new_tab->prev = tabs; } tabpanel1->index = i; ++tabpanel1->count; StrCpy(&addressbar1->text, "about:newtab"); Spawn(&@cyberia_navigate); win->focused_widget = addressbar1; } TabPanelTab* @cyberia_tab_index(TabPanelWidget* widget, I64 index) { TabPanelTab* tab = widget->tabs; if (!tab) { return NULL; } I64 i; for (i = 0; i < index; i++) { tab = tab->next; if (!tab) return NULL; } return tab; } U0 @cyberia_select_tab(I64 index) { tabpanel1->index = index; browser = @cyberia_tab_index(tabpanel1, index)->data; @cyberia_history; } U0 @cyberia_close_current_tab() { TabPanelTab* tab = @cyberia_tab_index(tabpanel1, tabpanel1->index); --tabpanel1->count; // FIXME: Free tab // Close first tab if (!tabpanel1->index) { if (tab->next) { tabpanel1->tabs = tab->next; } @cyberia_select_tab(tabpanel1->index); return; } // Close last tab if (tabpanel1->count == tabpanel1->index) { if (tab->prev) { tab->prev->next = NULL; } --tabpanel1->index; @cyberia_select_tab(tabpanel1->index); return; } // Close other tab if (tab->prev) { tab->prev->next = tab->next; } if (tab->next) { tab->next->prev = tab->prev; } @cyberia_select_tab(tabpanel1->index); } U0 @cyberia_handle_numbered_tab_navigation() { if (KeyDown(SC_ALT)) { if (KeyDown(Char2ScanCode('1')) && tabpanel1->count > 0) { @cyberia_select_tab(0); return; } if (KeyDown(Char2ScanCode('2')) && tabpanel1->count > 1) { @cyberia_select_tab(1); return; } if (KeyDown(Char2ScanCode('3')) && tabpanel1->count > 2) { @cyberia_select_tab(2); return; } if (KeyDown(Char2ScanCode('4')) && tabpanel1->count > 3) { @cyberia_select_tab(3); return; } if (KeyDown(Char2ScanCode('5')) && tabpanel1->count > 4) { @cyberia_select_tab(4); return; } if (KeyDown(Char2ScanCode('6')) && tabpanel1->count > 5) { @cyberia_select_tab(5); return; } if (KeyDown(Char2ScanCode('7')) && tabpanel1->count > 6) { @cyberia_select_tab(6); return; } if (KeyDown(Char2ScanCode('8')) && tabpanel1->count > 7) { @cyberia_select_tab(7); return; } } } U0 @cyberia_win_keypress(Window* w, I64 event) { if (!event) return; @cyberia_handle_numbered_tab_navigation; if (KeyDown(SC_ALT)) { if (KeyDown(Char2ScanCode('9'))) { @cyberia_select_tab(tabpanel1->count - 1); return; } } if (KeyDown(SC_CTRL) && KeyDown(Char2ScanCode('t'))) { @cyberia_new_tab; return; } if (KeyDown(SC_CTRL) && KeyDown(Char2ScanCode('w'))) { if (tabpanel1->count > 1) { @cyberia_close_current_tab; } else { @cyberia_win_really_close(win); } return; } if (!w || !w->focused_widget || !KeyDown(SC_ENTER)) return; if (w->focused_widget == addressbar1) { Spawn(&@cyberia_navigate); return; } @html_dom_node* node = w->focused_widget->data; if (!node) return; if (StrICmp(node->tagName, "input")) return; if (node->attributes->@("type") && (StrICmp(node->attributes->@("type"), "text") && StrICmp(node->attributes->@("type"), "password"))) return; @html_dom_node* form_node = @self_or_ancestor_matches_tag_name(node, "form"); if (!form_node) return; // If we are the only text input element, submit the form if (@cyberia_form_has_one_text_input_element(form_node)) { @cyberia_form_submit(form_node); } } U0 @cyberia_vscroll_change(Widget*) { if (!browser || !browser->renderer) return; @reflow_node_list(browser->renderer); } U0 @cyberia_win_repaint(Window*) { if (!win || !tabpanel1 || !addressbar1 || !background1 || !vscroll1 || !status1 || !statusbackdrop1) return; tabpanel1->width = win->width - 9; tabpanel1->height = 28; I64 offset_y = tabpanel1->y + tabpanel1->height + 1; background1->y = offset_y + 36; controlsbackdrop1->y = -8; controlsbackdrop1->height = background1->y + 8; backbtn1->y = offset_y + 0; fwdbtn1->y = offset_y + 0; refreshbtn1->y = offset_y + 0; hanbagabtn1->y = offset_y + 0; addressbar1->y = offset_y + 6; hanbagabtn1->x = win->width - hanbagabtn1->width - 9; addressbar1->width = hanbagabtn1->x - addressbar1->x - 3; background1->width = win->width - 9; background1->height = win->height - background1->y - 48; vscroll1->x = win->width; vscroll1->y = background1->y; vscroll1->width = 16; vscroll1->height = background1->height; I64 delta_z = Mouse.z - old_mouse_z; if (delta_z) { vscroll1->scroll += (RENDERER_DEFAULT_MAX_LINE_HEIGHT * delta_z); @cyberia_vscroll_change(vscroll1); } old_mouse_z = Mouse.z; if (StrLen(&status2->text)) { status1->y = win->height; status2->y = win->height - 40; } else { status1->y = win->height - 40; status2->y = win->height; } statusbackdrop1->y = background1->y + background1->height; if (!browser || !browser->renderer || !widgets_base) return; if (widgets_base->next && (old_window_width != win->width || old_window_height != win->height)) { @reflow_node_list(browser->renderer); old_window_width = win->width; old_window_height = win->height; } if (browser->renderer->calculated_page_height > background1->height) { vscroll1->max = browser->renderer->calculated_page_height; vscroll1->length = (vscroll1->height - 31) / MaxI64(2, ToI64(browser->renderer->calculated_page_height / background1->height)); vscroll1->x = win->width - 25; background1->width -= vscroll1->width; } } U0 @cyberia_unset_status_text() { StrCpy(&status2->text, ""); previous_hovered_href = NULL; } U0 @cyberia_addressbar_clicked(Widget*) { if (StrLen(&addressbar1->text) && addressbar1->selected_region_start == -1 && addressbar1->selected_region_end == -1) { addressbar1->selected_region_start = 0; addressbar1->selected_region_end = StrLen(&addressbar1->text) - 1; } } U0 @cyberia_tabpanel_clicked(TabPanelWidget* widget) { if (browser != @cyberia_tab_index(widget, widget->index)->data) { @cyberia_select_tab(widget->index); } } U0 @cyberia_win_mouseat(Window*) { if (addressbar1 && win->focused_widget != addressbar1) { addressbar1->selected_region_start = -1; addressbar1->selected_region_end = -1; } if (!win->hovered_widget || !win->hovered_widget->pointer) { @cyberia_unset_status_text; return; } @html_dom_node* node = @self_or_ancestor_matches_tag_name(win->hovered_widget->data, "a"); if (!node) return; U8* unresolved_href = node->attributes->@("href"); if (!unresolved_href || previous_hovered_href == unresolved_href) return; previous_hovered_href = unresolved_href; U8* resolved_href = @resolve_href(browser->renderer, unresolved_href); if (!resolved_href) return; StrCpy(&status2->text, resolved_href); Free(resolved_href); } U0 @cyberia_init() { win = Compositor.CreateWindow(24, 24, 992, 768, WIN_FLAGS_DEFAULT); // win->explicit_repaint = TRUE; Gui.Window.SetCallback(win, "close", &@cyberia_win_close); Gui.Window.SetCallback(win, "repaint", &@cyberia_win_repaint); Gui.Window.SetCallback(win, "mouseat", &@cyberia_win_mouseat); Gui.Window.SetIcon(win, DEFAULT_FAVICON); Gui.Window.SetTitle(win, "Cyberia Browser"); Gui.Window.Center(win); Gui.Window.SetFocus(win); background1 = Gui.CreateWidget(win, WIDGET_TYPE_RECT, 0, 36, 0, 0); background1->color = Color(255, 255, 255); controlsbackdrop1 = Gui.CreateWidget(win, WIDGET_TYPE_RECT, -14, 0, Display.Width(), 36 + 14); controlsbackdrop1->y = -14; controlsbackdrop1->color = Color(204, 204, 204); statusbackdrop1 = Gui.CreateWidget(win, WIDGET_TYPE_RECT, 0, 0, Display.Width(), 48); statusbackdrop1->color = Color(204, 204, 204); status1 = Gui.CreateWidget(win, WIDGET_TYPE_LABEL, 0, 0, 320, 16); Gui.Widget.SetFont(status1, "Eight Bit Dragon"); Gui.Widget.SetText(status1, "Idle"); status2 = Gui.CreateWidget(win, WIDGET_TYPE_LABEL, 0, 0, 320, 16); Gui.Widget.SetFont(status2, "Eight Bit Dragon"); Gui.Widget.SetText(status2, ""); backbtn1 = Gui.CreateWidget(win, WIDGET_TYPE_BUTTON, 0, 0, 24, 24); Gui.Widget.SetText(backbtn1, ""); Gui.Widget.SetCallback(backbtn1, "clicked", &@cyberia_back_clicked); backbtn1->image = @image_file_to_context2d("M:/Media/Themes/Umami/Icon/actions/back.png"); backbtn1->disabled_image = @image_file_to_context2d("M:/Media/Themes/Umami/Icon/actions/back-disabled.png"); backbtn1->disabled = TRUE; backbtn1->width = backbtn1->image->width + 8; fwdbtn1 = Gui.CreateWidget(win, WIDGET_TYPE_BUTTON, 33, 0, 24, 24); Gui.Widget.SetText(fwdbtn1, ""); Gui.Widget.SetCallback(fwdbtn1, "clicked", &@cyberia_fwd_clicked); fwdbtn1->image = @image_file_to_context2d("M:/Media/Themes/Umami/Icon/actions/forward.png"); fwdbtn1->disabled_image = @image_file_to_context2d("M:/Media/Themes/Umami/Icon/actions/forward-disabled.png"); fwdbtn1->disabled = TRUE; fwdbtn1->width = fwdbtn1->image->width + 8; refreshbtn1 = Gui.CreateWidget(win, WIDGET_TYPE_BUTTON, 66, 0, 24, 24); Gui.Widget.SetText(refreshbtn1, ""); Gui.Widget.SetCallback(refreshbtn1, "clicked", &@cyberia_refresh_clicked); refreshbtn1->image = @image_file_to_context2d("M:/Media/Themes/Umami/Icon/actions/reload.png"); refreshbtn1->disabled_image = @image_file_to_context2d("M:/Media/Themes/Umami/Icon/actions/reload-disabled.png"); refreshbtn1->disabled = TRUE; refreshbtn1->width = refreshbtn1->image->width + 8; hanbagabtn1 = Gui.CreateWidget(win, WIDGET_TYPE_BUTTON, U64_MAX, 0, 24, 24); Gui.Widget.SetText(hanbagabtn1, ""); Gui.Widget.SetCallback(hanbagabtn1, "clicked", &@cyberia_hanbaga_show); hanbagabtn1->image = @image_file_to_context2d("M:/Applications/Internet/Cyberia.app/Resources/hanbaga.png"); hanbagabtn1->width = hanbagabtn1->image->width + 8; vscroll1 = Gui.CreateWidget(win, WIDGET_TYPE_VERT_SCROLLBAR, -99999, -99999, 0, 0); Gui.Widget.SetCallback(vscroll1, "change", &@cyberia_vscroll_change); addressbar1 = Gui.CreateWidget(win, WIDGET_TYPE_INPUT, 100, 6, 320, 16); Gui.Widget.SetFont(addressbar1, "Eight Bit Dragon"); Gui.Widget.SetCallback(addressbar1, "clicked", &@cyberia_addressbar_clicked); Gui.Window.SetCallback(win, "keypress", &@cyberia_win_keypress); Compositor.RegisterForGlobalInputEvents(win); tabpanel1 = Gui.CreateWidget(win, WIDGET_TYPE_TAB_PANEL, 0, 0, 0, 0); tabpanel1->size = 256; Gui.Widget.SetCallback(tabpanel1, "clicked", &@cyberia_tabpanel_clicked); widgets_base = win->widget; while (widgets_base->next) { widgets_base = widgets_base->next; } @cyberia_new_tab; @cyberia_win_repaint(win); } @cyberia_init; U0 Main() { while (1) { if (Compositor.active_win != hanbaga_menu && Gui.Window.IsVisible(hanbaga_menu)) Gui.Window.Hide(hanbaga_menu); Sleep(1); } } Main;