--- /dev/null
+include config.mk
+
+all: $(TARGET)
+
+-include $(DEPS)
+
+options:
+ @echo "$(PROJECT) build options:"
+ @echo "LIBS = $(LIBS)"
+ @echo "CC = $(CC)"
+ @echo "CFLAGS = $(CFLAGS)"
+ @echo "CPPFLAGS = $(CPPFLAGS)"
+ @echo "LDFLAGS = $(LDFLAGS)"
+
+all: $(TARGET)
+
+$(TARGET): $(OBJ)
+ @echo "$(CC) $@"
+ @$(CC) $(OBJ) -o $(TARGET) $(LDFLAGS)
+
+%.o: %.c $(HEAD)
+ @echo "${CC} $<"
+ @$(CC) -c -o $@ $< $(CPPFLAGS) $(CFLAGS)
+
+debug: $(DTARGET)
+
+$(DTARGET): $(DOBJ)
+ @echo "$(CC) $@"
+ @$(CC) $(DFLAGS) $(DOBJ) -o $(DTARGET) $(LDFLAGS)
+
+%.do: %.c $(HEAD)
+ @echo "${CC} $<"
+ @$(CC) -c -o $@ $< $(CPPFLAGS) $(DFLAGS)
+
+clean:
+ $(RM) $(OBJ) $(DOBJ) $(TARGET) $(DTARGET)
+
+dist: distclean
+ @echo "Creating tarball."
+ @git archive --format tar -o $(DIST_FILE) HEAD
+
+distclean:
+ $(RM) $(DIST_FILE)
+
+.PHONY: clean debug all install uninstall options dist
--- /dev/null
+#----------------user/install options----------------
+VERSION = 0.1.0
+
+PROJECT = vimprobable
+PREFIX ?= /usr/local
+BINDIR ?= $(PREFIX)/bin/
+MANDIR ?= $(PREFIX)/share/man/
+
+#----------------compile options---------------------
+LIBS = gtk+-2.0 webkit-1.0
+
+CFLAGS += $(shell pkg-config --cflags $(LIBS))
+CFLAGS += -Wall
+CFLAGS += -pipe
+CFLAGS += -ansi
+CFLAGS += -std=c99
+CFLAGS += -pedantic
+CFLAGS += -Wmissing-declarations
+CFLAGS += -Wmissing-parameter-type
+#CFLAGS += -Wstrict-prototypes
+
+LDFLAGS += $(shell pkg-config --libs $(LIBS)) -lX11 -lXext
+
+CPPFLAGS += -DPROJECT=\"$(PROJECT)\"
+CPPFLAGS += -DVERSION=\"${VERSION}\"
+
+#----------------developer options-------------------
+DFLAGS += $(CFLAGS)
+DFLAGS += -DDEBUG
+DFLAGS += -ggdb
+DFLAGS += -g
+
+#----------------end of options----------------------
+OBJ = $(patsubst %.c, %.o, $(wildcard src/*.c))
+DOBJ = $(patsubst %.c, %.do, $(wildcard src/*.c))
+HEAD = $(wildcard *.h)
+DEPS = $(OBJ:%.o=%.d)
+
+TARGET = $(PROJECT)
+DTARGET = $(TARGET)_dbg
+DIST_FILE = $(PROJECT)_$(VERSION).tar.gz
+
+FMOD = 0644
+
+MFLAGS = --no-print-directory
--- /dev/null
+#include "main.h"
+#include "callback.h"
+#include "config.h"
+
+void webview_load_status_cb(WebKitWebView* view, GParamSpec* pspec)
+{
+ Gui* gui = &vp.gui;
+ const char* uri = webkit_web_view_get_uri(gui->webview);
+
+ switch (webkit_web_view_get_load_status(gui->webview)) {
+ case WEBKIT_LOAD_COMMITTED:
+ vp_update_urlbar(uri);
+ break;
+
+ case WEBKIT_LOAD_FINISHED:
+ break;
+
+ default:
+ break;
+ }
+}
+
+void destroy_window_cb(GtkWidget* widget, GtkWidget* window)
+{
+ vp_close_browser(0);
+}
+
+gboolean dummy_cb(void)
+{
+ return TRUE;
+}
--- /dev/null
+#ifndef CALLBACKS_H
+#define CALLBACKS_H
+
+/* callbacks */
+void webview_load_status_cb(WebKitWebView* view, GParamSpec* pspec);
+void destroy_window_cb(GtkWidget* widget, GtkWidget* window);
+gboolean dummy_cb(void);
+
+#endif /* end of include guard: CALLBACKS_H */
--- /dev/null
+#include "command.h"
+
+static CommandInfo cmd_list[] = {
+ /* command function */
+ {"quit", quit},
+ {"source", view_source},
+};
+
+static void command_sharg_append(GArray* a, const gchar* str);
+
+
+void command_init()
+{
+ guint i;
+ vp.behave.commands = g_hash_table_new(g_str_hash, g_str_equal);
+
+ for (i = 0; i < LENGTH(cmd_list); i++) {
+ g_hash_table_insert(vp.behave.commands, (gpointer)cmd_list[i].name, &cmd_list[i]);
+ }
+}
+
+void command_parse_line(const gchar* line, GString* result)
+{
+ gchar* string = g_strdup(line);
+
+ /* strip trailing newline, and any other whitespace from left */
+ g_strstrip(string);
+
+ if (strcmp(string, "")) {
+ /* ignore comment lines */
+ if ((string[0] != '#')) {
+ GArray* a = g_array_new(TRUE, FALSE, sizeof(gchar*));
+ const CommandInfo* c = command_parse_parts(string, a);
+ if (c) {
+ command_run_command(c, a, result);
+ }
+ g_array_free(a, TRUE);
+ }
+ }
+
+ g_free(string);
+}
+
+/* static? */
+const CommandInfo* command_parse_parts(const gchar* line, GArray* a)
+{
+ CommandInfo* c = NULL;
+
+ /* split the line into the command and its parameters */
+ gchar **tokens = g_strsplit(line, " ", 2);
+
+ /* look up the command */
+ c = g_hash_table_lookup(vp.behave.commands, tokens[0]);
+ if (!c) {
+ g_strfreev(tokens);
+ return NULL;
+ }
+
+ gchar* p = g_strdup(tokens[1]);
+ command_sharg_append(a, p);
+
+ g_strfreev(tokens);
+ g_free(p);
+
+ return c;
+}
+
+void command_run_command(const CommandInfo* c, GArray* a, GString* result)
+{
+ c->function(a, result);
+}
+
+static void command_sharg_append(GArray* a, const gchar* str)
+{
+ const gchar* s = (str ? str : "");
+ g_array_append_val(a, s);
+}
+
+void quit(GArray* argv, GString* result)
+{
+ vp_close_browser();
+}
+
+void view_source(GArray* argv, GString* result)
+{
+ gboolean mode = webkit_web_view_get_view_source_mode(vp.gui.webview);
+ webkit_web_view_set_view_source_mode(vp.gui.webview, !mode);
+ webkit_web_view_reload(vp.gui.webview);
+}
--- /dev/null
+#ifndef COMMAND_H
+#define COMMAND_H
+
+#include "main.h"
+#include <webkit/webkit.h>
+
+typedef void (*Command)(GArray *argv, GString *result);
+
+typedef struct {
+ const gchar* name;
+ Command function;
+} CommandInfo;
+
+
+void command_init(void);
+void command_parse_line(const gchar* line, GString* result);
+const CommandInfo* command_parse_parts(const gchar* line, GArray* a);
+void command_run_command(const CommandInfo* c, GArray* a, GString* result);
+
+void quit(GArray* argv, GString* result);
+void view_source(GArray* argv, GString* result);
+
+#endif /* end of include guard: COMMAND_H */
--- /dev/null
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#define START_PAGE "http://sourceforge.net/apps/trac/vimprobable"
+
+#define STATUS_BG_COLOR "#000000" /* background color for status bar */
+#define STATUS_FG_COLOR "#ffffff" /* foreground color for status bar */
+#define STATUS_BAR_FONT "monospace bold 8"
+#define URL_BOX_FONT "monospace 8"
+
+#endif /* end of include guard: CONFIG_H */
--- /dev/null
+#include "main.h"
+#include "keybind.h"
+#include "command.h"
+
+static GSList* keys = NULL;
+static GString* modkeys = NULL;
+
+static gboolean keybind_keypress_callback(WebKitWebView* webview, GdkEventKey* event);
+
+
+void keybind_init(void)
+{
+ modkeys = g_string_new("");
+ g_signal_connect(G_OBJECT(vp.gui.webview), "key-press-event", G_CALLBACK(keybind_keypress_callback), NULL);
+}
+
+void keybind_add(int mode, guint modkey, guint modmask, guint keyval, const gchar* command)
+{
+ struct _keybind_key* keybind = g_new0(struct _keybind_key, 1);
+
+ keybind->mode = mode;
+ keybind->modkey = modkey;
+ keybind->modmask = modmask;
+ keybind->keyval = keyval;
+ keybind->command = g_strdup(command);
+
+ keys = g_slist_append(keys, keybind);
+
+ /* save the modkey also in the modkey string */
+ if (modkey) {
+ g_string_append_c(modkeys, modkey);
+ }
+}
+
+static gboolean keybind_keypress_callback(WebKitWebView* webview, GdkEventKey* event)
+{
+ GSList* tmp;
+ GdkModifierType irrelevant;
+ guint keyval;
+ static GdkKeymap *keymap;
+
+ keymap = gdk_keymap_get_default();
+
+ /* Get a mask of modifiers that shouldn't be considered for this event.
+ * E.g.: It shouldn't matter whether ';' is shifted or not. */
+ gdk_keymap_translate_keyboard_state(keymap, event->hardware_keycode,
+ event->state, event->group, &keyval, NULL, NULL, &irrelevant);
+
+ /* check for escape or modkeys or counts */
+ if ((CLEAN(event->state) & ~irrelevant) == 0) {
+ if (IS_ESCAPE(event)) {
+ /* TODO add function to main.c to switch the mode */
+ /* switch to normal mode */
+ vp.state.mode = VP_MODE_NORMAL;
+ /* remove current modkey and set count back to 0 */
+ vp.state.modkey = 0;
+ vp.state.count = 0;
+ vp_update_statusbar();
+
+ return TRUE;
+ } else if (vp.state.modkey == 0 && ((event->keyval >= GDK_1 && event->keyval <= GDK_9)
+ || (event->keyval == GDK_0 && vp.state.count))) {
+ /* append the new entered count to previous one */
+ vp.state.count = (vp.state.count ? vp.state.count * 10 : 0) + (event->keyval - GDK_0);
+ vp_update_statusbar();
+
+ return TRUE;
+ } else if (strchr(modkeys->str, event->keyval) && vp.state.modkey != event->keyval) {
+ vp.state.modkey = (gchar)event->keyval;
+ vp_update_statusbar();
+
+ return TRUE;
+ }
+ }
+ /* check for keybinding */
+ for (tmp = keys; tmp != NULL; tmp = tmp->next) {
+ struct _keybind_key* keybind = (struct _keybind_key*)tmp->data;
+
+ if (gdk_keyval_to_lower(event->keyval) == keybind->keyval
+ && (event->state & keybind->modmask) == keybind->modmask
+ && keybind->modkey == vp.state.modkey
+ && keybind->command
+ ) {
+ GArray* a = g_array_new(TRUE, FALSE, sizeof(gchar*));
+ const CommandInfo* c = command_parse_parts(keybind->command, a);
+ if (c) {
+ command_run_command(c, a, NULL);
+ }
+ g_array_free(a, TRUE);
+
+ /* if key binding used, remove the modkey */
+ vp.state.modkey = 0;
+ vp_update_statusbar();
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
--- /dev/null
+#ifndef KEYBIND_H
+#define KEYBIND_H
+
+#include <gdk/gdkkeysyms.h>
+
+/* the CLEAN_MOD_*_MASK defines have all the bits set that will be stripped from the modifier bit field */
+#define CLEAN_MOD_NUMLOCK_MASK (GDK_MOD2_MASK)
+#define CLEAN_MOD_BUTTON_MASK (GDK_BUTTON1_MASK|GDK_BUTTON2_MASK|GDK_BUTTON3_MASK|GDK_BUTTON4_MASK|GDK_BUTTON5_MASK)
+
+/* remove unused bits, numlock symbol and buttons from keymask */
+#define CLEAN(mask) (mask & (GDK_MODIFIER_MASK) & ~(CLEAN_MOD_NUMLOCK_MASK) & ~(CLEAN_MOD_BUTTON_MASK))
+#define IS_ESCAPE(event) (IS_ESCAPE_KEY(CLEAN(event->state), event->keyval))
+#define IS_ESCAPE_KEY(s, k) ((s == 0 && k == GDK_Escape) || \
+ (s == GDK_CONTROL_MASK && k == GDK_bracketleft))
+
+struct _keybind_key {
+ int mode; /* mode maks for allowed browser modes */
+ guint modkey;
+ guint modmask; /* modemask for the kayval */
+ guint keyval;
+ gchar* command; /* command to run */
+};
+
+void keybind_init(void);
+void keybind_add(int mode, guint modkey, guint modmask, guint keyval, const gchar* command);
+
+#endif /* end of include guard: KEYBIND_H */
--- /dev/null
+#include "config.h"
+#include "main.h"
+#include "command.h"
+#include "callback.h"
+#include "keybind.h"
+
+/* variables */
+VpCore vp;
+
+/* functions */
+static void vp_print_version(void);
+static void vp_init(void);
+static void vp_init_gui(void);
+static void vp_setup_signals(void);
+
+
+gboolean vp_load_uri(const Arg* arg)
+{
+ char* u;
+ const char* uri = arg->c;
+
+ if (strcmp(uri, "") == 0) {
+ return FALSE;
+ }
+ u = g_strrstr(uri, "://") ? g_strdup(uri) : g_strdup_printf("http://%s", uri);
+
+ /* Load a web page into the browser instance */
+ webkit_web_view_load_uri(vp.gui.webview, u);
+ g_free(u);
+
+ /* change state to normal mode */
+ vp.state.mode = VP_MODE_NORMAL;
+ vp_update_statusbar();
+
+ return TRUE;
+}
+
+gboolean vp_navigate(const Arg *arg)
+{
+ if (arg->i <= NAVIG_FORWARD) {
+ /* TODO allow to set a count for the navigation */
+ webkit_web_view_go_back_or_forward(
+ vp.gui.webview, (arg->i == NAVIG_BACK ? -1 : 1)
+ );
+ } else if (arg->i == NAVIG_RELOAD) {
+ webkit_web_view_reload(vp.gui.webview);
+ } else if (arg->i == NAVIG_RELOAD_FORCE) {
+ webkit_web_view_reload_bypass_cache(vp.gui.webview);
+ } else {
+ webkit_web_view_stop_loading(vp.gui.webview);
+ }
+
+ return TRUE;
+}
+
+void vp_close_browser()
+{
+ if (vp.behave.commands) {
+ g_hash_table_destroy(vp.behave.commands);
+ vp.behave.commands = NULL;
+ }
+ gtk_main_quit();
+}
+
+void vp_update_urlbar(const gchar* uri)
+{
+ gchar* markup;
+
+ markup = g_markup_printf_escaped("<span font=\"%s\">%s</span>", STATUS_BAR_FONT, uri);
+ gtk_label_set_markup(GTK_LABEL(vp.gui.statusbar.left), markup);
+ g_free(markup);
+}
+
+void vp_update_statusbar(void)
+{
+ GString* status = g_string_new("");
+ gchar* markup;
+
+ /* TODO show modekeys count */
+ /* at the moment we have only one modkey available */
+ /* http://current_uri.tld 34% */
+ g_string_append_printf(status, "%.0d", vp.state.count);
+ if (vp.state.modkey) {
+ g_string_append_c(status, vp.state.modkey);
+ }
+ g_string_append_printf(status, " %.0d%%", 46);
+ markup = g_markup_printf_escaped("<span font=\"%s\">%s</span>", STATUS_BAR_FONT, status->str);
+ gtk_label_set_markup(GTK_LABEL(vp.gui.statusbar.right), markup);
+ g_free(markup);
+}
+
+static void vp_print_version(void)
+{
+ fprintf(stderr, "%s/%s (build %s %s)\n", VERSION, PROJECT, __DATE__, __TIME__);
+}
+
+static void vp_init(void)
+{
+ /* initialize the gui elements and event callbacks */
+ vp_init_gui();
+ /* initialize the commands hash map */
+ command_init();
+ /* initialize the keybindings */
+ keybind_init();
+
+ /*command_parse_line("quit", NULL);*/
+ keybind_add(VP_MODE_NORMAL, GDK_g, 0, GDK_f, "source");
+ keybind_add(VP_MODE_NORMAL, 0, 0, GDK_d, "quit");
+}
+
+static void vp_init_gui(void)
+{
+ Gui* gui = &vp.gui;
+ GdkColor bg, fg;
+
+ GdkGeometry hints = {10, 10};
+ gui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_wmclass(GTK_WINDOW(gui->window), PROJECT, PROJECT);
+ gtk_window_set_default_size(GTK_WINDOW(gui->window), 640, 480);
+ gtk_window_set_title(GTK_WINDOW(gui->window), PROJECT);
+ gtk_window_set_geometry_hints(GTK_WINDOW(gui->window), NULL, &hints, GDK_HINT_MIN_SIZE);
+
+ /* Create a browser instance */
+ gui->webview = WEBKIT_WEB_VIEW(webkit_web_view_new());
+
+ /* Create a scrollable area */
+ gui->viewport = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(
+ GTK_SCROLLED_WINDOW(gui->viewport),
+ GTK_POLICY_NEVER, GTK_POLICY_NEVER
+ );
+
+ gui->box = GTK_BOX(gtk_vbox_new(FALSE, 0));
+
+ /* Prepare the imputbox */
+ gui->inputbox = gtk_entry_new();
+ gtk_entry_set_inner_border(GTK_ENTRY(gui->inputbox), NULL);
+ g_object_set(gtk_widget_get_settings(gui->inputbox), "gtk-entry-select-on-focus", FALSE, NULL);
+
+ PangoFontDescription* font = pango_font_description_from_string(URL_BOX_FONT);
+ gtk_widget_modify_font(GTK_WIDGET(gui->inputbox), font);
+ pango_font_description_free(font);
+
+ /* Prepare the statusbar */
+ gui->statusbar.box = GTK_BOX(gtk_hbox_new(FALSE, 0));
+ gui->statusbar.left = gtk_label_new(NULL);
+ gui->statusbar.right = gtk_label_new(NULL);
+
+ /* Prepare the event box */
+ gui->eventbox = gtk_event_box_new();
+ gdk_color_parse(STATUS_BG_COLOR, &bg);
+ gdk_color_parse(STATUS_FG_COLOR, &fg);
+ gtk_widget_modify_bg(gui->eventbox, GTK_STATE_NORMAL, &bg);
+ gtk_widget_modify_fg(gui->eventbox, GTK_STATE_NORMAL, &fg);
+
+ vp_setup_signals();
+
+ /* Put all part together */
+ gtk_container_add(GTK_CONTAINER(gui->viewport), GTK_WIDGET(gui->webview));
+ gtk_container_add(GTK_CONTAINER(gui->eventbox), GTK_WIDGET(gui->statusbar.box));
+ gtk_container_add(GTK_CONTAINER(gui->window), GTK_WIDGET(gui->box));
+ gtk_misc_set_alignment(GTK_MISC(gui->statusbar.left), 0.0, 0.0);
+ gtk_misc_set_alignment(GTK_MISC(gui->statusbar.right), 1.0, 0.0);
+ gtk_box_pack_start(gui->statusbar.box, gui->statusbar.left, TRUE, TRUE, 2);
+ gtk_box_pack_start(gui->statusbar.box, gui->statusbar.right, FALSE, FALSE, 2);
+ gtk_widget_modify_bg(GTK_WIDGET(gui->statusbar.left), GTK_STATE_NORMAL, &bg);
+ gtk_widget_modify_fg(GTK_WIDGET(gui->statusbar.left), GTK_STATE_NORMAL, &fg);
+ gtk_widget_modify_bg(GTK_WIDGET(gui->statusbar.right), GTK_STATE_NORMAL, &bg);
+ gtk_widget_modify_fg(GTK_WIDGET(gui->statusbar.right), GTK_STATE_NORMAL, &fg);
+
+ gtk_box_pack_start(gui->box, gui->viewport, TRUE, TRUE, 0);
+ gtk_box_pack_start(gui->box, gui->eventbox, FALSE, FALSE, 0);
+ gtk_entry_set_has_frame(GTK_ENTRY(gui->inputbox), FALSE);
+ gtk_box_pack_end(gui->box, gui->inputbox, FALSE, FALSE, 0);
+
+ /* Make sure that when the browser area becomes visible, it will get mouse
+ * and keyboard events */
+ gtk_widget_grab_focus(GTK_WIDGET(gui->webview));
+
+ /* Make sure the main window and all its contents are visible */
+ gtk_widget_show_all(gui->window);
+}
+
+static void vp_setup_signals(void)
+{
+ Gui* gui = &vp.gui;
+ WebKitWebFrame *frame = webkit_web_view_get_main_frame(gui->webview);
+
+ /* Set up callbacks so that if either the main window or the browser
+ * instance is closed, the program will exit */
+ g_signal_connect(gui->window, "destroy", G_CALLBACK(destroy_window_cb), NULL);
+ g_signal_connect(G_OBJECT(frame), "scrollbars-policy-changed", G_CALLBACK(dummy_cb), NULL);
+ g_signal_connect(G_OBJECT(gui->webview), "notify::load-status", G_CALLBACK(webview_load_status_cb), NULL);
+}
+
+int main(int argc, char* argv[])
+{
+ static gboolean ver = false;
+ static GError* err;
+ static GOptionEntry opts[] = {
+ { "version", 'v', 0, G_OPTION_ARG_NONE, &ver, "print version", NULL },
+ { NULL }
+ };
+ /* Initialize GTK+ */
+ if (!gtk_init_with_args(&argc, &argv, "[<uri>]", opts, NULL, &err)) {
+ g_printerr("can't init gtk: %s\n", err->message);
+ g_error_free(err);
+
+ return EXIT_FAILURE;
+ }
+
+ if (ver) {
+ vp_print_version();
+ return EXIT_SUCCESS;
+ }
+ vp_init();
+
+ /* command line argument: URL */
+ Arg arg;
+ if (argc > 1) {
+ arg.c = g_strdup(argv[argc - 1]);
+ } else {
+ arg.c = g_strdup(START_PAGE);
+ }
+ vp_load_uri(&arg);
+ g_free(arg.c);
+
+ /* Run the main GTK+ event loop */
+ gtk_main();
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+#ifndef MAIN_H
+#define MAIN_H
+
+#include <stdlib.h>
+#include <string.h>
+#include <webkit/webkit.h>
+
+#define LENGTH(x) (sizeof x / sizeof x[0])
+
+#ifdef DEBUG
+#define PRINT_DEBUG(...) do { \
+ fprintf(stderr, "\n\033[31;1mDEBUG:\033[0m %s:%d:%s()\t", __FILE__, __LINE__, __func__); \
+ fprintf(stderr, __VA_ARGS__);\
+ fprintf(stderr, "\n"); \
+} while(0);
+#define TIMER_START GTimer *__timer; do {__timer = g_timer_new(); g_timer_start(__timer);} while(0)
+#define TIMER_END do {gulong __debug_micro = 0; gdouble __debug_elapsed = g_timer_elapsed(__timer, &__debug_micro);\
+ PRINT_DEBUG("\033[33mtimer:\033[0m elapsed: %f, micro: %lu", __debug_elapsed, __debug_micro);\
+ g_timer_destroy(__timer); \
+} while(0)
+#else
+#define PRINT_DEBUG(message, ...)
+#define TIMER_START
+#define TIMER_END
+#endif
+
+/* enums */
+typedef enum _vp_mode {
+ VP_MODE_NORMAL = (1 << 0),
+ VP_MODE_PATH_THROUGH = (1 << 1),
+ VP_MODE_INSERT = (1 << 2),
+} Mode;
+
+enum {
+ NAVIG_BACK,
+ NAVIG_FORWARD,
+ NAVIG_RELOAD,
+ NAVIG_RELOAD_FORCE,
+ NAVIG_STOP_LOADING
+};
+
+/* structs */
+typedef struct {
+ gint i;
+ char* c;
+} Arg;
+
+/* statusbar */
+typedef struct {
+ GtkBox* box;
+ GtkWidget* left;
+ GtkWidget* right;
+} StatusBar;
+
+/* gui */
+typedef struct {
+ GtkWidget* window;
+ WebKitWebView* webview;
+ GtkWidget* viewport;
+ GtkBox* box;
+ GtkWidget* eventbox;
+ GtkWidget* inputbox;
+ StatusBar statusbar;
+} Gui;
+
+/* state */
+typedef struct {
+ Mode mode;
+ gchar modkey;
+ int count;
+} State;
+
+/* behaviour */
+typedef struct {
+ /* command list: (key)name -> (value)Command */
+ GHashTable* commands;
+} Behaviour;
+
+/* core struct */
+typedef struct {
+ Gui gui;
+ State state;
+ Behaviour behave;
+#if 0
+ Network net;
+ Ssl ssl;
+ Communication comm;
+ Info info;
+#endif
+} VpCore;
+
+/* main object */
+extern VpCore vp;
+
+/* functions */
+void vp_update_statusbar(void);
+void vp_update_urlbar(const gchar* uri);
+gboolean vp_load_uri(const Arg* arg);
+gboolean vp_navigate(const Arg* arg);
+void vp_close_browser();
+
+#endif /* end of include guard: MAIN_H */