diff -up surf-0.2/config.def.h surf-0.2-mouse/config.def.h --- surf-0.2/config.def.h 2009-10-17 03:00:22.000000000 -0400 +++ surf-0.2-mouse/config.def.h 2009-10-24 22:01:47.797670719 -0400 @@ -35,6 +35,17 @@ static Key keys[] = { { 0, GDK_Return, hideuri, { 0 }, UriBar }, }; +static Button buttons[] = { + /* modifier keyval function arg */ + { GDK_BUTTON3_MASK, 3, context, { 0 } }, + { 0, 8, navigate, { .i = -1 } }, + { 0, 9, navigate, { .i = +1 } }, + /* Example of chording. Press right mouse while holding left mouse to nav forward */ + { GDK_BUTTON1_MASK, 3, navigate, { .i = +1 } }, + /* Press left mouse while holding right mouse to nav backward. */ + { GDK_BUTTON3_MASK, 1, navigate, { .i = -1 } }, +}; + static Item items[] = { { "New Window", newwindow, { .v = NULL } }, { "Reload", reload, { .b = FALSE } }, diff -up surf-0.2/surf.c surf-0.2-mouse/surf.c --- surf-0.2/surf.c 2009-10-17 03:00:22.000000000 -0400 +++ surf-0.2-mouse/surf.c 2009-10-24 22:04:44.682236188 -0400 @@ -30,7 +30,7 @@ union Arg { }; typedef struct Client { - GtkWidget *win, *scroll, *vbox, *uribar, *searchbar, *indicator; + GtkWidget *win, *scroll, *vbox, *uribar, *searchbar, *indicator, *contextmenu; GtkWidget **items; WebKitWebView *view; WebKitDownload *download; @@ -60,6 +60,13 @@ typedef struct { KeyFocus focus; } Key; +typedef struct { + guint mod; + guint button; + void (*func)(Client *c, const Arg *arg); + const Arg arg; +} Button; + static Display *dpy; static Atom uriprop; static SoupCookieJar *cookiejar; @@ -73,9 +80,10 @@ static char *progname; static const char *autouri(Client *c); static char *buildpath(const char *path); +static gboolean buttonevent(WebKitWebView *v, GdkEventButton *ev, Client *c); static void cleanup(void); static void clipboard(Client *c, const Arg *arg); -static void context(WebKitWebView *v, GtkMenu *m, Client *c); +static void context(Client *c, const Arg *arg); static char *copystr(char **str, const char *src); static gboolean decidewindow(WebKitWebView *v, WebKitWebFrame *f, WebKitNetworkRequest *r, WebKitWebNavigationAction *n, WebKitWebPolicyDecision *p, Client *c); static void destroyclient(Client *c); @@ -100,6 +108,7 @@ static Client *newclient(void); static void newwindow(Client *c, const Arg *arg); static WebKitWebView *createwindow(WebKitWebView *v, WebKitWebFrame *f, Client *c); static void pasteuri(GtkClipboard *clipboard, const char *text, gpointer d); +static void populatepopup(WebKitWebView *v, GtkMenu *m, Client *c); static GdkFilterReturn processx(GdkXEvent *xevent, GdkEvent *event, gpointer d); static void print(Client *c, const Arg *arg); static void progresschange(WebKitWebView *v, gint p, Client *c); @@ -155,6 +164,43 @@ buildpath(const char *path) { return apath; } +gboolean +buttonevent(WebKitWebView *v, GdkEventButton *ev, Client *c) { + guint i; + gboolean processed = FALSE; + static guint maxpressed = 0; + static guint buttonspressed = 0; + updatewinid(c); + if(ev->type == GDK_BUTTON_PRESS) { + maxpressed++; + buttonspressed++; + for(i = 0; i < LENGTH(buttons); i++) { + if(ev->button == buttons[i].button && + CLEANMASK(ev->state) == buttons[i].mod && + buttons[i].func) { + buttons[i].func(c, &(buttons[i].arg)); + processed = TRUE; + } + } + } + else { /* GDK_BUTTON_RELEASE */ + buttonspressed--; + for(i = 0; i < LENGTH(buttons); i++) { + if(ev->button == buttons[i].button && + buttons[i].mod == CLEANMASK(ev->state) && + /* Prevents unintended release triggers while chording */ + (maxpressed == 1 || buttonspressed == 1) && + buttons[i].func) { + buttons[i].func(c, &(buttons[i].arg)); + processed = TRUE; + } + } + if(buttonspressed == 0) + maxpressed = 0; + } + return processed; +} + void cleanup(void) { while(clients) @@ -176,19 +222,10 @@ clipboard(Client *c, const Arg *arg) { } void -context(WebKitWebView *v, GtkMenu *m, Client *c) { - int i; - GtkContainer *parent; - - gtk_widget_hide_all(GTK_WIDGET(m)); - gtk_widget_show(GTK_WIDGET(m)); - for(i = 0; i < LENGTH(items); i++) { - parent = GTK_CONTAINER(gtk_widget_get_parent(c->items[i])); - if(parent) - gtk_container_remove(parent, c->items[i]); - gtk_menu_shell_append(GTK_MENU_SHELL(m), c->items[i]); - gtk_widget_show(c->items[i]); - } +context(Client *c, const Arg *arg) { + gtk_widget_show_all(c->contextmenu); + gtk_menu_popup(GTK_MENU(c->contextmenu), NULL, NULL, NULL, + NULL, 0, gtk_get_current_event_time()); } char * @@ -205,17 +242,15 @@ copystr(char **str, const char *src) { void destroyclient(Client *c) { - int i; Client *p; gtk_widget_destroy(GTK_WIDGET(c->view)); + gtk_widget_destroy(c->contextmenu); gtk_widget_destroy(c->scroll); gtk_widget_destroy(c->uribar); gtk_widget_destroy(c->searchbar); gtk_widget_destroy(c->vbox); gtk_widget_destroy(c->win); - for(i = 0; i < LENGTH(items); i++) - gtk_widget_destroy(c->items[i]); free(c->items); for(p = clients; p && p->next != c; p = p->next); @@ -457,10 +492,12 @@ newclient(void) { die("Cannot malloc!\n"); /* contextmenu */ + c->contextmenu = gtk_menu_new(); for(i = 0; i < LENGTH(items); i++) { c->items[i] = gtk_menu_item_new_with_label(items[i].label); g_signal_connect(G_OBJECT(c->items[i]), "activate", G_CALLBACK(itemclick), c); + gtk_menu_shell_append(GTK_MENU_SHELL(c->contextmenu), c->items[i]); } @@ -485,7 +522,9 @@ newclient(void) { g_signal_connect(G_OBJECT(c->view), "download-requested", G_CALLBACK(initdownload), c); g_signal_connect(G_OBJECT(c->view), "window-object-cleared", G_CALLBACK(windowobjectcleared), c); g_signal_connect(G_OBJECT(c->view), "focus-in-event", G_CALLBACK(focusview), c); - g_signal_connect(G_OBJECT(c->view), "populate-popup", G_CALLBACK(context), c); + g_signal_connect(G_OBJECT(c->view), "populate-popup", G_CALLBACK(populatepopup), c); + g_signal_connect(G_OBJECT(c->view), "button-press-event", G_CALLBACK(buttonevent), c); + g_signal_connect(G_OBJECT(c->view), "button-release-event", G_CALLBACK(buttonevent), c); /* uribar */ c->uribar = gtk_entry_new(); @@ -580,6 +619,11 @@ pasteuri(GtkClipboard *clipboard, const loaduri((Client *) d, &arg); } +void +populatepopup(WebKitWebView *v, GtkMenu *m, Client *c) { + gtk_widget_destroy(GTK_WIDGET(m)); +} + GdkFilterReturn processx(GdkXEvent *e, GdkEvent *event, gpointer d) { Client *c = (Client *)d;