URI: 
       supporting multiple windows per instance - surf - [fork] surf browser, a WebKit based browser
  HTML git clone git@git.drkhsh.at/surf.git
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit ad005db13f571dabb72fce448eb0d93f79acf97d
   DIR parent d1cfea4e86d517177ced89eb96f7f51aa9200298
  HTML Author: Enno Boland (Gottox) <gottox@s01.de>
       Date:   Fri,  5 Jun 2009 20:16:10 +0200
       
       supporting multiple windows per instance
       Diffstat:
         M Makefile                            |      10 ++++++----
         M surf.c                              |     196 +++++++++++++++++++------------
       
       2 files changed, 127 insertions(+), 79 deletions(-)
       ---
   DIR 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
   DIR 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;