tabbed

Simple tabbing application for X11.
git clone git://r-36.net/tabbed
Log | Files | Refs | README | LICENSE

commit ad005db13f571dabb72fce448eb0d93f79acf97d
parent d1cfea4e86d517177ced89eb96f7f51aa9200298
Author: Enno Boland (Gottox) <gottox@s01.de>
Date:   Fri,  5 Jun 2009 20:16:10 +0200

supporting multiple windows per instance
Diffstat:
Makefile | 10++++++----
surf.c | 196+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
2 files changed, 127 insertions(+), 79 deletions(-)

diff --git a/Makefile b/Makefile @@ -3,10 +3,10 @@ include config.mk -SRC = surf.c +SRC = surf.c tabbed.c OBJ = ${SRC:.c=.o} -all: options surf +all: options surf tabbed options: @echo surf build options: @@ -20,9 +20,11 @@ options: ${OBJ}: config.mk -surf: ${OBJ} +surf: surf.o @echo CC -o $@ - @${CC} -o $@ ${OBJ} ${LDFLAGS} + @${CC} -o $@ surf.o ${LDFLAGS} + +tabbed: tabbed.o clean: @echo cleaning diff --git a/surf.c b/surf.c @@ -1,3 +1,7 @@ +/* See LICENSE file for copyright and license details. + * + * To understand surf, start reading main(). + */ #include <X11/X.h> #include <X11/Xatom.h> #include <gtk/gtk.h> @@ -24,36 +28,48 @@ Display *dpy; Atom urlprop; -GtkWidget *win; -GtkWidget *browser; -WebKitWebView *view; -gchar *title; -gint progress = 100; +typedef struct Client { + GtkWidget *win; + GtkWidget *browser; + WebKitWebView *view; + gchar *title; + gint progress; + struct Client *next; +} Client; +Client *clients = NULL; gboolean embed = FALSE; gboolean showxid = FALSE; gboolean ignore_once = FALSE; +static Client *newclient(); +static void die(char *str); static void setup(void); static void cleanup(void); -static void updatetitle(void); +static void updatetitle(Client *c); static void windestroy(GtkWidget* w, gpointer d); static gboolean keypress(GtkWidget* w, GdkEventKey *ev); static void titlechange(WebKitWebView* view, WebKitWebFrame* frame, const gchar* title, gpointer d); static void progresschange(WebKitWebView *view, gint p, gpointer d); static void loadcommit(WebKitWebView *view, WebKitWebFrame *f, gpointer d); static void linkhover(WebKitWebView* page, const gchar* t, const gchar* l, gpointer d); +static void destroyclient(Client *c); static gboolean newwindow(WebKitWebView *view, WebKitWebFrame *f, WebKitNetworkRequest *r, WebKitWebNavigationAction *n, WebKitWebPolicyDecision *p, gpointer d); static gboolean download(WebKitWebView *view, GObject *o, gpointer d); -static void loaduri(gchar *uri); -static void loadfile(gchar *f); -static void setupx(); +static void loaduri(const Client *c, const gchar *uri); +static void loadfile(const Client *c, const gchar *f); GdkFilterReturn processx(GdkXEvent *xevent, GdkEvent *event, gpointer data); +void +cleanup(void) { + +} + GdkFilterReturn -processx(GdkXEvent *e, GdkEvent *event, gpointer data) { +processx(GdkXEvent *e, GdkEvent *event, gpointer d) { XPropertyEvent *ev; + Client *c = (Client *)d; Atom adummy; int idummy; unsigned long ldummy; @@ -62,8 +78,8 @@ processx(GdkXEvent *e, GdkEvent *event, gpointer data) { ev = &((XEvent *)e)->xproperty; if(ignore_once == FALSE && ev->atom == urlprop && ev->state == PropertyNewValue) { XGetWindowProperty(dpy, ev->window, urlprop, 0L, BUFSIZ, False, XA_STRING, - &adummy, &idummy, &ldummy, &ldummy, &buf); - loaduri((gchar *)buf); + &adummy, &idummy, &ldummy, &ldummy, &buf); + loaduri(c, (gchar *)buf); XFree(buf); return GDK_FILTER_REMOVE; } @@ -73,45 +89,36 @@ processx(GdkXEvent *e, GdkEvent *event, gpointer data) { } void -setupx() { - dpy = GDK_WINDOW_XDISPLAY(GTK_WIDGET(win)->window); - urlprop = XInternAtom(dpy, "_SURF_URL", False); - gdk_window_add_filter(GTK_WIDGET(win)->window, processx, NULL); - gdk_window_set_events(GTK_WIDGET(win)->window, GDK_ALL_EVENTS_MASK); -} - -void -loadfile(gchar *f) { - GIOChannel *c = NULL; +loadfile(const Client *c, const gchar *f) { + GIOChannel *chan = NULL; GError *e = NULL; GString *code = g_string_new(""); GString *uri = g_string_new(f); gchar *line; if(strcmp(f, "-") == 0) { - c = g_io_channel_unix_new(STDIN_FILENO); - if (c) { - while(g_io_channel_read_line(c, &line, NULL, NULL, &e) == G_IO_STATUS_NORMAL) { + chan = g_io_channel_unix_new(STDIN_FILENO); + if (chan) { + while(g_io_channel_read_line(chan, &line, NULL, NULL, &e) == G_IO_STATUS_NORMAL) { g_string_append(code, line); g_free(line); } - webkit_web_view_load_html_string(view, code->str, NULL); - g_free(code); - g_io_channel_shutdown(c, FALSE, NULL); + webkit_web_view_load_html_string(c->view, code->str, NULL); + g_io_channel_shutdown(chan, FALSE, NULL); } } else { g_string_prepend(uri, "file://"); - loaduri(uri->str); + loaduri(c, uri->str); } } -static void loaduri(gchar *uri) { +static void loaduri(const Client *c, const gchar *uri) { GString* u = g_string_new(uri); - if(g_strrstr(u->str, "://") == NULL) + if(g_strrstr(u->str, ":") == NULL) g_string_prepend(u, "http://"); - webkit_web_view_load_uri(view, u->str); + webkit_web_view_load_uri(c->view, u->str); g_string_free(u, TRUE); } @@ -125,8 +132,9 @@ gboolean newwindow(WebKitWebView *view, WebKitWebFrame *f, WebKitNetworkRequest *r, WebKitWebNavigationAction *n, WebKitWebPolicyDecision *p, gpointer d) { - /* TODO */ - return FALSE; + Client *c = newclient(); + webkit_web_view_load_request(c->view, r); + return TRUE; } void linkhover(WebKitWebView* page, const gchar* t, const gchar* l, gpointer d) { @@ -135,43 +143,64 @@ linkhover(WebKitWebView* page, const gchar* t, const gchar* l, gpointer d) { void loadcommit(WebKitWebView *view, WebKitWebFrame *f, gpointer d) { + Client *c = (Client *)d; gchar *uri; if(!(uri = (gchar *)webkit_web_view_get_uri(view))) uri = "(null)"; ignore_once = TRUE; - XChangeProperty(dpy, GDK_WINDOW_XID(GTK_WIDGET(win)->window), urlprop, + XChangeProperty(dpy, GDK_WINDOW_XID(GTK_WIDGET(c->win)->window), urlprop, XA_STRING, 8, PropModeReplace, (unsigned char *)uri, strlen(uri) + 1); } void progresschange(WebKitWebView* view, gint p, gpointer d) { - progress = p; - updatetitle(); + Client *c = (Client *)d; + + c->progress = p; + updatetitle(c); } void -updatetitle() { +updatetitle(Client *c) { char t[512]; - if(progress == 100) - snprintf(t, LENGTH(t), "%s", title); + if(c->progress == 100) + snprintf(t, LENGTH(t), "%s", c->title); else - snprintf(t, LENGTH(t), "%s [%i%%]", title, progress); - gtk_window_set_title(GTK_WINDOW(win), t); + snprintf(t, LENGTH(t), "%s [%i%%]", c->title, c->progress); + gtk_window_set_title(GTK_WINDOW(c->win), t); } void titlechange(WebKitWebView *v, WebKitWebFrame *f, const gchar *t, gpointer d) { - if(title) - g_free(title); - title = g_strdup(t); - updatetitle(); + Client *c = (Client *)d; + + if(c->title) + g_free(c->title); + c->title = g_strdup(t); + updatetitle(c); } void windestroy(GtkWidget* w, gpointer d) { - gtk_main_quit(); + Client *c = (Client *)d; + + destroyclient(c); +} + +void +destroyclient(Client *c) { + Client *p; + gtk_widget_destroy(c->win); + if(clients == c && c->next == NULL) + gtk_main_quit(); + for(p = clients; p && p->next != c; p = p->next); + if(p) + p->next = c->next; + else + clients = c->next; + free(c); } gboolean @@ -181,43 +210,60 @@ keypress(GtkWidget* w, GdkEventKey *ev) { } void setup(void) { + dpy = GDK_DISPLAY(); + urlprop = XInternAtom(dpy, "_SURF_URL", False); +} + +void die(char *str) { + fputs(str, stderr); + exit(EXIT_FAILURE); +} + +Client * +newclient(void) { + Client *c; + if(!(c = calloc(1, sizeof(Client)))) + die("Cannot malloc!\n"); if(embed) { - win = gtk_plug_new(0); + c->win = gtk_plug_new(0); } else { - win = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_wmclass(GTK_WINDOW(win), "surf", "surf"); + c->win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_wmclass(GTK_WINDOW(c->win), "surf", "surf"); } - gtk_window_set_default_size(GTK_WINDOW(win), 800, 600); - browser = gtk_scrolled_window_new(NULL, NULL); - g_signal_connect (G_OBJECT(win), "destroy", G_CALLBACK(windestroy), NULL); - g_signal_connect (G_OBJECT(win), "key-press-event", G_CALLBACK(keypress), NULL); + gtk_window_set_default_size(GTK_WINDOW(c->win), 800, 600); + c->browser = gtk_scrolled_window_new(NULL, NULL); + g_signal_connect (G_OBJECT(c->win), "destroy", G_CALLBACK(windestroy), c); + g_signal_connect (G_OBJECT(c->win), "key-press-event", G_CALLBACK(keypress), NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(browser), + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(c->browser), GTK_POLICY_NEVER, GTK_POLICY_NEVER); - view = WEBKIT_WEB_VIEW(webkit_web_view_new()); - gtk_container_add(GTK_CONTAINER(browser), GTK_WIDGET(view)); + c->view = WEBKIT_WEB_VIEW(webkit_web_view_new()); + gtk_container_add(GTK_CONTAINER(c->browser), GTK_WIDGET(c->view)); - g_signal_connect(G_OBJECT(view), "title-changed", G_CALLBACK(titlechange), view); - g_signal_connect(G_OBJECT(view), "load-progress-changed", G_CALLBACK(progresschange), view); - g_signal_connect(G_OBJECT(view), "load-committed", G_CALLBACK(loadcommit), view); - g_signal_connect(G_OBJECT(view), "hovering-over-link", G_CALLBACK(linkhover), view); - g_signal_connect(G_OBJECT(view), "new-window-policy-decision-requested", G_CALLBACK(newwindow), view); - g_signal_connect(G_OBJECT(view), "download-requested", G_CALLBACK(download), view); - /* g_signal_connect(G_OBJECT(view), "create-web-view", G_CALLBACK(createwebview), view); */ + g_signal_connect(G_OBJECT(c->view), "title-changed", G_CALLBACK(titlechange), c); + g_signal_connect(G_OBJECT(c->view), "load-progress-changed", G_CALLBACK(progresschange), c); + g_signal_connect(G_OBJECT(c->view), "load-committed", G_CALLBACK(loadcommit), c); + g_signal_connect(G_OBJECT(c->view), "hovering-over-link", G_CALLBACK(linkhover), c); + g_signal_connect(G_OBJECT(c->view), "new-window-policy-decision-requested", G_CALLBACK(newwindow), c); + g_signal_connect(G_OBJECT(c->view), "download-requested", G_CALLBACK(download), c); + /* g_signal_connect(G_OBJECT(c->view), "create-web-view", G_CALLBACK(createwebview), c); */ - gtk_container_add(GTK_CONTAINER(win), browser); - gtk_widget_grab_focus(GTK_WIDGET(view)); - gtk_widget_show_all(win); + gtk_container_add(GTK_CONTAINER(c->win), c->browser); + gtk_widget_grab_focus(GTK_WIDGET(c->view)); + gtk_widget_show_all(c->win); if(showxid) - printf("%u\n", (unsigned int)GDK_WINDOW_XID(GTK_WIDGET(win)->window)); -} - -void cleanup() { + printf("%u\n", (unsigned int)GDK_WINDOW_XID(GTK_WIDGET(c->win)->window)); + c->next = clients; + clients = c; + gdk_window_set_events(GTK_WIDGET(c->win)->window, GDK_ALL_EVENTS_MASK); + gdk_window_add_filter(GTK_WIDGET(c->win)->window, processx, c); + return c; } int main(int argc, char *argv[]) { gchar *uri = NULL, *file = NULL; + Client *c; ARG { case 'x': @@ -247,12 +293,12 @@ int main(int argc, char *argv[]) { if (!g_thread_supported()) g_thread_init(NULL); setup(); - setupx(); + c = newclient(); if(uri) - loaduri(uri); + loaduri(c, uri); else if(file) - loadfile(file); - updatetitle(); + loadfile(c, file); + updatetitle(c); gtk_main(); cleanup(); return EXIT_SUCCESS;