// BadWolf: Minimalist and privacy-oriented WebKitGTK+ browser // Copyright © 2019 Haelwenn (lanodan) Monnier // SPDX-License-Identifier: BSD-3-Clause #include "config.h" #include /* _() and other internationalization/localization helpers */ #include /* g_fprintf() */ #include #include /* LC_* */ #include /* realpath() */ #include const gchar *homepage = "https://hacktivis.me/projects/badwolf"; const gchar *version = "0.2.0"; struct Window { GtkWidget *main_window; GtkWidget *notebook; GtkWidget *new_tab; }; struct Client { GtkWidget *box; GtkWidget *toolbar; GtkWidget *javascript; GtkWidget *location; WebKitWebView *webView; struct Window *window; GtkWidget *statusbar; GtkWidget *statuslabel; GtkWidget *search; }; gchar *ensure_uri_scheme(gchar *text, gboolean try_file); static gboolean WebViewCb_close(WebKitWebView *webView, gpointer user_data); static gboolean commonCb_key_press_event(struct Window *window, GdkEvent *event, struct Client *browser); static gboolean WebViewCb_key_press_event(WebKitWebView *webView, GdkEvent *event, gpointer user_data); static gboolean main_windowCb_key_press_event(GtkWidget *widget, GdkEvent *event, gpointer user_data); static gboolean boxCb_key_press_event(GtkWidget *widget, GdkEvent *event, gpointer user_data); static gboolean WebViewCb_web_process_terminated(WebKitWebView *webView, WebKitWebProcessTerminationReason reason, gpointer user_data); static gboolean WebViewCb_notify__uri(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data); GtkWidget *badwolf_new_tab_box(const gchar *title, struct Client *browser); static gboolean WebViewCb_notify__title(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data); static gboolean WebViewCb_notify__is__playing__audio(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data); void webView_tab_label_change(struct Client *browser, const gchar *title); static gboolean WebViewCb_notify__estimated_load_progress(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data); static gboolean WebViewCb_mouse_target_changed(WebKitWebView *webView, WebKitHitTestResult *hit, guint modifiers, gpointer user_data); static WebKitWebView *WebViewCb_create(WebKitWebView *related_web_view, WebKitNavigationAction *navigation_action, gpointer user_data); static gboolean locationCb_activate(GtkEntry *location, gpointer user_data); static gboolean javascriptCb_toggled(GtkButton *javascript, gpointer user_data); static gboolean SearchEntryCb_next__match(GtkSearchEntry *search, gpointer user_data); static gboolean SearchEntryCb_previous__match(GtkSearchEntry *search, gpointer user_data); static gboolean SearchEntryCb_search__changed(GtkSearchEntry *search, gpointer user_data); static gboolean SearchEntryCb_stop__search(GtkSearchEntry *search, gpointer user_data); struct Client * new_browser(struct Window *window, gchar *target_url, WebKitWebView *related_web_view); int badwolf_new_tab(GtkNotebook *notebook, struct Client *browser); static void new_tabCb_clicked(GtkButton *new_tab, gpointer user_data); static void closeCb_clicked(GtkButton *close, gpointer user_data); static void notebookCb_switch__page(GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data); gint get_tab_position(GtkContainer *notebook, GtkWidget *child); gchar * ensure_uri_scheme(gchar *text, gboolean try_file) { if(text == NULL) return "about:blank"; char *scheme = g_uri_parse_scheme(text); if(scheme != NULL) return text; if(try_file) { gchar *f; char *path; path = realpath(text, NULL); if(path != NULL) { f = g_strdup_printf("file://%s", path); free(path); return f; } } return g_strdup_printf("http://%s", text); } static void badwolf_about_dialog(GtkWindow *main_window) { // clang-format off gtk_show_about_dialog( main_window, "license", "SPDX-License-Identifier: BSD-3-Clause", "copyright", "2019 Haelwenn (lanodan) Monnier ", "website", homepage, "comments", "Minimalist and privacy-oriented WebKitGTK+ browser", "version", version, //FIXME: "logo-icon-name", g_get_application_name(), NULL ); // clang-format on } static gboolean WebViewCb_close(WebKitWebView *webView, gpointer user_data) { (void)webView; struct Client *browser = (struct Client *)user_data; GtkNotebook *notebook = GTK_NOTEBOOK(browser->window->notebook); gtk_notebook_remove_page(notebook, get_tab_position(GTK_CONTAINER(notebook), browser->box)); gtk_widget_destroy(browser->box); free(browser); return TRUE; } /* commonCb_key_press_event: Global callback for keybindings * * Theses shortcuts should be avoided as much as possible: * - Single key shortcuts (ie. backspace and space) * - Triple key shortcuts (except for Ctrl+Shift) * - Unix Terminal shortcuts (specially Ctrl-W) * * loosely follows https://developer.gnome.org/hig/stable/keyboard-input.html */ static gboolean commonCb_key_press_event(struct Window *window, GdkEvent *event, struct Client *browser) { GtkNotebook *notebook = GTK_NOTEBOOK(window->notebook); if(((GdkEventKey *)event)->state & GDK_CONTROL_MASK) { if(browser != NULL) { switch(((GdkEventKey *)event)->keyval) { case GDK_KEY_F4: webkit_web_view_try_close(browser->webView); return TRUE; case GDK_KEY_r: if(((GdkEventKey *)event)->state & GDK_SHIFT_MASK) webkit_web_view_reload_bypass_cache(browser->webView); else webkit_web_view_reload(browser->webView); return TRUE; case GDK_KEY_f: gtk_widget_grab_focus(browser->search); return TRUE; case GDK_KEY_l: gtk_widget_grab_focus(browser->location); return TRUE; case GDK_KEY_bracketleft: webkit_web_view_go_back(browser->webView); return TRUE; case GDK_KEY_bracketright: webkit_web_view_go_forward(browser->webView); return TRUE; case GDK_KEY_0: webkit_web_view_set_zoom_level(WEBKIT_WEB_VIEW(browser->webView), 1); return TRUE; } } else { switch(((GdkEventKey *)event)->keyval) { case GDK_KEY_Page_Down: gtk_notebook_next_page(notebook); return TRUE; case GDK_KEY_Page_Up: gtk_notebook_prev_page(notebook); return TRUE; case GDK_KEY_t: badwolf_new_tab(notebook, new_browser(window, NULL, NULL)); return TRUE; } } } if((((GdkEventKey *)event)->state & GDK_MOD1_MASK) && browser != NULL) { switch(((GdkEventKey *)event)->keyval) { case GDK_KEY_Left: gtk_notebook_prev_page(notebook); return TRUE; case GDK_KEY_Right: gtk_notebook_next_page(notebook); return TRUE; } } if(browser != NULL) { switch(((GdkEventKey *)event)->keyval) { case GDK_KEY_F5: webkit_web_view_reload(browser->webView); return TRUE; } } else { switch(((GdkEventKey *)event)->keyval) { case GDK_KEY_F1: badwolf_about_dialog(GTK_WINDOW(window->main_window)); return TRUE; } } return FALSE; } static gboolean WebViewCb_key_press_event(WebKitWebView *webView, GdkEvent *event, gpointer user_data) { (void)webView; struct Client *browser = (struct Client *)user_data; if(commonCb_key_press_event(browser->window, event, browser)) return TRUE; return FALSE; } static gboolean boxCb_key_press_event(GtkWidget *widget, GdkEvent *event, gpointer user_data) { (void)widget; struct Client *browser = (struct Client *)user_data; if(commonCb_key_press_event(browser->window, event, browser)) return TRUE; return FALSE; } static gboolean main_windowCb_key_press_event(GtkWidget *widget, GdkEvent *event, gpointer user_data) { (void)widget; struct Window *window = (struct Window *)user_data; if(commonCb_key_press_event(window, event, NULL)) return TRUE; return FALSE; } static gboolean WebViewCb_web_process_terminated(WebKitWebView *webView, WebKitWebProcessTerminationReason reason, gpointer user_data) { (void)webView; struct Client *browser = (struct Client *)user_data; switch(reason) { case WEBKIT_WEB_PROCESS_CRASHED: g_fprintf(stderr, _("the web process crashed.\n")); webView_tab_label_change(browser, _("title|Crashed")); break; case WEBKIT_WEB_PROCESS_EXCEEDED_MEMORY_LIMIT: g_fprintf(stderr, _("the web process exceeded the memory limit.\n")); webView_tab_label_change(browser, _("title|Out of Memory")); break; default: g_fprintf(stderr, _("the web process terminated for an unknown reason.\n")); webView_tab_label_change(browser, _("title|Unknown Crash")); } return FALSE; } static gboolean WebViewCb_notify__uri(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data) { (void)webView; (void)pspec; const gchar *location_uri; struct Client *browser = (struct Client *)user_data; location_uri = webkit_web_view_get_uri(browser->webView); gtk_entry_set_text(GTK_ENTRY(browser->location), location_uri); if(webkit_uri_for_display(location_uri) != location_uri) gtk_widget_set_tooltip_text(browser->location, webkit_uri_for_display(location_uri)); else gtk_widget_set_has_tooltip(browser->location, false); return TRUE; } GtkWidget * badwolf_new_tab_box(const gchar *title, struct Client *browser) { (void)browser; GtkWidget *tab_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); GtkWidget *close = gtk_button_new_from_icon_name("window-close-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR); GtkWidget *label = gtk_label_new(title); GtkWidget *playing = gtk_image_new_from_icon_name("audio-volume-high-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR); #ifdef BADWOLF_TAB_BOX_WIDTH gtk_widget_set_size_request(label, BADWOLF_TAB_BOX_WIDTH, -1); #endif #ifdef BADWOLF_TAB_LABEL_CHARWIDTH gtk_label_set_width_chars(GTK_LABEL(label), BADWOLF_TAB_LABEL_CHARWIDTH); #endif gtk_widget_set_hexpand(tab_box, BADWOLF_TAB_HEXPAND); gtk_label_set_ellipsize(GTK_LABEL(label), BADWOLF_TAB_LABEL_ELLIPSIZE); gtk_label_set_single_line_mode(GTK_LABEL(label), TRUE); gtk_box_pack_start(GTK_BOX(tab_box), playing, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(tab_box), label, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(tab_box), close, FALSE, FALSE, 0); gtk_button_set_relief(GTK_BUTTON(close), GTK_RELIEF_NONE); g_signal_connect(close, "clicked", G_CALLBACK(closeCb_clicked), browser); gtk_widget_set_tooltip_text(tab_box, title); gtk_widget_show_all(tab_box); gtk_widget_set_visible(playing, webkit_web_view_is_playing_audio(browser->webView)); return tab_box; } static gboolean WebViewCb_notify__title(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data) { (void)webView; (void)pspec; struct Client *browser = (struct Client *)user_data; webView_tab_label_change(browser, NULL); return TRUE; } static gboolean WebViewCb_notify__is__playing__audio(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data) { (void)webView; (void)pspec; struct Client *browser = (struct Client *)user_data; webView_tab_label_change(browser, NULL); return TRUE; } void webView_tab_label_change(struct Client *browser, const gchar *title) { GtkWidget *notebook = browser->window->notebook; #define title_IS_EMPTY (title == NULL) || (title == (const gchar *)"") if(title_IS_EMPTY) title = webkit_web_view_get_title(browser->webView); if(title_IS_EMPTY) title = webkit_web_view_get_uri(browser->webView); if(title_IS_EMPTY) title = "BadWolf"; gtk_notebook_set_tab_label( GTK_NOTEBOOK(notebook), browser->box, badwolf_new_tab_box(title, browser)); if(get_tab_position(GTK_CONTAINER(notebook), browser->box) == gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook))) gtk_window_set_title(GTK_WINDOW(browser->window->main_window), title); } static gboolean WebViewCb_notify__estimated_load_progress(WebKitWebView *webView, GParamSpec *pspec, gpointer user_data) { (void)webView; (void)pspec; struct Client *browser = (struct Client *)user_data; gdouble progress; progress = webkit_web_view_get_estimated_load_progress(browser->webView); if(progress == 1) { progress = 0; } gtk_entry_set_progress_fraction(GTK_ENTRY(browser->location), progress); return TRUE; } static gboolean WebViewCb_mouse_target_changed(WebKitWebView *webView, WebKitHitTestResult *hit, guint modifiers, gpointer user_data) { (void)webView; (void)modifiers; struct Client *browser = (struct Client *)user_data; const gchar *link_uri = webkit_hit_test_result_get_link_uri(hit); gtk_label_set_text(GTK_LABEL(browser->statuslabel), webkit_uri_for_display(link_uri)); return TRUE; } static gboolean WebViewCb_scroll_event(GtkWidget *widget, GdkEvent *event, gpointer data) { (void)widget; struct Client *browser = (struct Client *)data; gdouble delta_x, delta_y; gfloat zoom; if(((GdkEventScroll *)event)->state & GDK_CONTROL_MASK) { gdk_event_get_scroll_deltas(event, &delta_x, &delta_y); zoom = webkit_web_view_get_zoom_level(WEBKIT_WEB_VIEW(browser->webView)); zoom -= delta_y * 0.1; webkit_web_view_set_zoom_level(WEBKIT_WEB_VIEW(browser->webView), zoom); return TRUE; } return FALSE; } static WebKitWebView * WebViewCb_create(WebKitWebView *related_web_view, WebKitNavigationAction *navigation_action, gpointer user_data) { (void)navigation_action; struct Window *window = (struct Window *)user_data; struct Client *browser = new_browser(window, NULL, related_web_view); if(badwolf_new_tab(GTK_NOTEBOOK(window->notebook), browser) < 0) { return NULL; } else { return browser->webView; } } static gboolean locationCb_activate(GtkEntry *location, gpointer user_data) { char *target_url; struct Client *browser = (struct Client *)user_data; target_url = ensure_uri_scheme((char *)gtk_entry_get_text(location), FALSE); if(target_url != NULL) webkit_web_view_load_uri(browser->webView, target_url); return TRUE; } static gboolean javascriptCb_toggled(GtkButton *javascript, gpointer user_data) { struct Client *browser = (struct Client *)user_data; WebKitSettings *settings = webkit_web_view_get_settings(browser->webView); webkit_settings_set_enable_javascript( settings, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(javascript))); webkit_web_view_set_settings(browser->webView, settings); return TRUE; } static gboolean SearchEntryCb_next__match(GtkSearchEntry *search, gpointer user_data) { (void)search; struct Client *browser = (struct Client *)user_data; WebKitFindController *findController = webkit_web_view_get_find_controller(browser->webView); webkit_find_controller_search_next(findController); return TRUE; } static gboolean SearchEntryCb_previous__match(GtkSearchEntry *search, gpointer user_data) { (void)search; struct Client *browser = (struct Client *)user_data; WebKitFindController *findController = webkit_web_view_get_find_controller(browser->webView); webkit_find_controller_search_previous(findController); return TRUE; } static gboolean SearchEntryCb_search__changed(GtkSearchEntry *search, gpointer user_data) { struct Client *browser = (struct Client *)user_data; WebKitFindController *findController = webkit_web_view_get_find_controller(browser->webView); const gchar *search_text = gtk_entry_get_text(GTK_ENTRY(search)); webkit_find_controller_search(findController, search_text, 0, 0); return TRUE; } static gboolean SearchEntryCb_stop__search(GtkSearchEntry *search, gpointer user_data) { (void)search; struct Client *browser = (struct Client *)user_data; WebKitFindController *findController = webkit_web_view_get_find_controller(browser->webView); webkit_find_controller_search_finish(findController); return TRUE; } struct Client * new_browser(struct Window *window, gchar *target_url, WebKitWebView *related_web_view) { struct Client *browser = g_malloc(sizeof(struct Client)); target_url = ensure_uri_scheme(target_url, (related_web_view != NULL)); browser->window = window; browser->box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); browser->toolbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); browser->location = gtk_entry_new(); browser->javascript = gtk_check_button_new(); browser->statusbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); browser->statuslabel = gtk_label_new(NULL); browser->search = gtk_search_entry_new(); WebKitWebContext *web_context = webkit_web_context_new_ephemeral(); webkit_web_context_set_process_model(web_context, WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES); WebKitSettings *settings = webkit_settings_new_with_settings(BADWOLF_WEBKIT_SETTINGS); browser->webView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW, "web-context", webkit_web_context_new_ephemeral(), "related-view", related_web_view, "settings", settings, NULL)); gtk_widget_set_tooltip_text(browser->javascript, _("Toggle javascript")); gtk_box_pack_start(GTK_BOX(browser->toolbar), GTK_WIDGET(browser->javascript), FALSE, FALSE, BADWOLF_TOOLBAR_PADDING); gtk_box_pack_start(GTK_BOX(browser->toolbar), GTK_WIDGET(browser->location), TRUE, TRUE, BADWOLF_TOOLBAR_PADDING); gtk_box_pack_start( GTK_BOX(browser->box), GTK_WIDGET(browser->toolbar), FALSE, FALSE, BADWOLF_BOX_PADDING); gtk_box_pack_start( GTK_BOX(browser->box), GTK_WIDGET(browser->webView), TRUE, TRUE, BADWOLF_BOX_PADDING); gtk_box_pack_start( GTK_BOX(browser->box), GTK_WIDGET(browser->statusbar), FALSE, FALSE, BADWOLF_BOX_PADDING); gtk_box_pack_start(GTK_BOX(browser->statusbar), GTK_WIDGET(browser->search), FALSE, FALSE, BADWOLF_STATUSBAR_PADDING); gtk_box_pack_start(GTK_BOX(browser->statusbar), GTK_WIDGET(browser->statuslabel), FALSE, FALSE, BADWOLF_STATUSBAR_PADDING); gtk_widget_set_halign(browser->statusbar, GTK_ALIGN_START); gtk_label_set_single_line_mode(GTK_LABEL(browser->statuslabel), TRUE); gtk_entry_set_text(GTK_ENTRY(browser->location), target_url); gtk_entry_set_has_frame(GTK_ENTRY(browser->location), FALSE); gtk_entry_set_input_purpose(GTK_ENTRY(browser->location), GTK_INPUT_PURPOSE_URL); gtk_entry_set_placeholder_text(GTK_ENTRY(browser->search), _("search in current page")); gtk_entry_set_has_frame(GTK_ENTRY(browser->search), FALSE); g_signal_connect(browser->location, "activate", G_CALLBACK(locationCb_activate), browser); g_signal_connect(browser->javascript, "toggled", G_CALLBACK(javascriptCb_toggled), browser); g_signal_connect(browser->webView, "web-process-terminated", G_CALLBACK(WebViewCb_web_process_terminated), browser); g_signal_connect(browser->webView, "notify::uri", G_CALLBACK(WebViewCb_notify__uri), browser); g_signal_connect(browser->webView, "notify::title", G_CALLBACK(WebViewCb_notify__title), browser); g_signal_connect(browser->webView, "notify::is-playing-audio", G_CALLBACK(WebViewCb_notify__is__playing__audio), browser); g_signal_connect(browser->webView, "mouse-target-changed", G_CALLBACK(WebViewCb_mouse_target_changed), browser); g_signal_connect(browser->webView, "notify::estimated-load-progress", G_CALLBACK(WebViewCb_notify__estimated_load_progress), browser); g_signal_connect(browser->webView, "create", G_CALLBACK(WebViewCb_create), window); g_signal_connect(browser->webView, "close", G_CALLBACK(WebViewCb_close), browser); g_signal_connect( browser->webView, "key-press-event", G_CALLBACK(WebViewCb_key_press_event), browser); g_signal_connect(browser->webView, "scroll-event", G_CALLBACK(WebViewCb_scroll_event), browser); g_signal_connect(browser->search, "next-match", G_CALLBACK(SearchEntryCb_next__match), browser); g_signal_connect( browser->search, "previous-match", G_CALLBACK(SearchEntryCb_previous__match), browser); g_signal_connect( browser->search, "search-changed", G_CALLBACK(SearchEntryCb_search__changed), browser); g_signal_connect(browser->search, "stop-search", G_CALLBACK(SearchEntryCb_stop__search), browser); g_signal_connect(browser->box, "key-press-event", G_CALLBACK(boxCb_key_press_event), browser); if(related_web_view == NULL) webkit_web_view_load_uri(browser->webView, target_url); return browser; } int badwolf_new_tab(GtkNotebook *notebook, struct Client *browser) { gint current_page = gtk_notebook_get_current_page(notebook); gchar *title = _("New tab"); gtk_widget_show_all(browser->box); if(gtk_notebook_insert_page(notebook, browser->box, NULL, (current_page + 2)) == -1) { return -1; } gtk_notebook_set_tab_reorderable(notebook, browser->box, TRUE); gtk_notebook_set_tab_label(notebook, browser->box, badwolf_new_tab_box(title, browser)); gtk_widget_queue_draw(GTK_WIDGET(notebook)); return 0; } static void new_tabCb_clicked(GtkButton *new_tab, gpointer user_data) { (void)new_tab; struct Window *window = (struct Window *)user_data; struct Client *browser = new_browser(window, NULL, NULL); badwolf_new_tab(GTK_NOTEBOOK(window->notebook), browser); } static void closeCb_clicked(GtkButton *close, gpointer user_data) { (void)close; struct Client *browser = (struct Client *)user_data; webkit_web_view_try_close(browser->webView); } static void notebookCb_switch__page(GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data) { (void)page_num; struct Window *window = (struct Window *)user_data; GtkWidget *label = gtk_notebook_get_tab_label(notebook, page); // TODO: Maybe find a better way to store the title gtk_window_set_title(GTK_WINDOW(window->main_window), gtk_widget_get_tooltip_text(label)); } gint get_tab_position(GtkContainer *notebook, GtkWidget *child) { GValue position = G_VALUE_INIT; g_value_init(&position, G_TYPE_INT); gtk_container_child_get_property(notebook, child, "position", &position); return g_value_get_int(&position); } int main(int argc, char *argv[]) { struct Window *window = g_malloc(sizeof(struct Client)); gchar *target_url = NULL; setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, DATADIR "/locale"); bind_textdomain_codeset(PACKAGE, "UTF-8"); textdomain(PACKAGE); g_fprintf(stderr, _("Running Badwolf version: %s\n"), version); g_fprintf(stderr, _("Buildtime WebKit version: %d.%d.%d\n"), WEBKIT_MAJOR_VERSION, WEBKIT_MINOR_VERSION, WEBKIT_MICRO_VERSION); g_fprintf(stderr, _("Runtime WebKit version: %d.%d.%d\n"), webkit_get_major_version(), webkit_get_minor_version(), webkit_get_micro_version()); gtk_init(&argc, &argv); if(argv[1]) target_url = argv[1]; window->main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); window->notebook = gtk_notebook_new(); window->new_tab = gtk_button_new_from_icon_name("tab-new-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR); gtk_window_set_default_size( GTK_WINDOW(window->main_window), BADWOLF_DEFAULT_WIDTH, BADWOLF_DEFAULT_HEIGHT); gtk_window_set_role(GTK_WINDOW(window->main_window), "browser"); gtk_widget_set_tooltip_text(window->new_tab, _("Open new tab")); gtk_notebook_set_action_widget(GTK_NOTEBOOK(window->notebook), window->new_tab, GTK_PACK_END); gtk_notebook_set_scrollable(GTK_NOTEBOOK(window->notebook), TRUE); gtk_notebook_set_tab_pos(GTK_NOTEBOOK(window->notebook), BADWOLF_TAB_POSITION); gtk_notebook_popup_enable(GTK_NOTEBOOK(window->notebook)); gtk_container_add(GTK_CONTAINER(window->main_window), window->notebook); badwolf_new_tab(GTK_NOTEBOOK(window->notebook), new_browser(window, target_url, NULL)); g_signal_connect( window->main_window, "key-press-event", G_CALLBACK(main_windowCb_key_press_event), window); g_signal_connect(window->main_window, "destroy", G_CALLBACK(gtk_main_quit), NULL); g_signal_connect(window->new_tab, "clicked", G_CALLBACK(new_tabCb_clicked), window); g_signal_connect(window->notebook, "switch-page", G_CALLBACK(notebookCb_switch__page), window); gtk_widget_show(window->new_tab); gtk_widget_show_all(window->main_window); gtk_main(); return 0; }