Introduce new data structure for handling settings
[surf.git] / surf.c
diff --git a/surf.c b/surf.c
index 30464d5..43b1531 100644 (file)
--- a/surf.c
+++ b/surf.c
 
 #define LENGTH(x)               (sizeof(x) / sizeof(x[0]))
 #define CLEANMASK(mask)         (mask & (MODKEY|GDK_SHIFT_MASK))
+#define SETB(p, s)              [p] = (Parameter){ { .b = s }, }
+#define SETI(p, s)              [p] = (Parameter){ { .i = s }, }
+#define SETV(p, s)              [p] = (Parameter){ { .v = s }, }
+#define SETF(p, s)              [p] = (Parameter){ { .f = s }, }
+#define FSETB(p, s)             [p] = (Parameter){ { .b = s }, 1 }
+#define FSETI(p, s)             [p] = (Parameter){ { .i = s }, 1 }
+#define FSETV(p, s)             [p] = (Parameter){ { .v = s }, 1 }
+#define FSETF(p, s)             [p] = (Parameter){ { .f = s }, 1 }
 
 enum { AtomFind, AtomGo, AtomUri, AtomLast };
 
-enum {
+typedef enum {
        CaretBrowsing,
+       CookiePolicies,
+       DiskCache,
+       DNSPrefetch,
+       FontSize,
        FrameFlattening,
        Geolocation,
+       HideBackground,
+       Inspector,
        JavaScript,
+       KioskMode,
        LoadImages,
        Plugins,
+       PreferredLanguages,
+       RunInFullscreen,
        ScrollBars,
-};
+       ShowIndicators,
+       SpellChecking,
+       SpellLanguages,
+       StrictSSL,
+       Style,
+       ZoomLevel,
+       ParameterLast,
+} ParamName;
 
 enum {
        OnDoc   = WEBKIT_HIT_TEST_RESULT_CONTEXT_DOCUMENT,
@@ -58,9 +82,15 @@ enum {
 typedef union {
        int b;
        int i;
+       float f;
        const void *v;
 } Arg;
 
+typedef struct {
+       Arg val;
+       int force;
+} Parameter;
+
 typedef struct Client {
        GtkWidget *win;
        WebKitWebView *view;
@@ -70,7 +100,7 @@ typedef struct Client {
        GTlsCertificateFlags tlsflags;
        Window xid;
        int progress, fullscreen;
-       const char *title, *targeturi;
+       const char *title, *overtitle, *targeturi;
        const char *needle;
        struct Client *next;
 } Client;
@@ -91,6 +121,12 @@ typedef struct {
        unsigned int stopevent;
 } Button;
 
+typedef struct {
+       const char *uri;
+       Parameter config[ParameterLast];
+       regex_t re;
+} UriParameters;
+
 typedef struct {
        char *regex;
        char *style;
@@ -105,7 +141,6 @@ static void sigchld(int unused);
 static char *buildfile(const char *path);
 static char *buildpath(const char *path);
 static Client *newclient(Client *c);
-static void addaccelgroup(Client *c);
 static void loaduri(Client *c, const Arg *a);
 static const char *geturi(Client *c);
 static void setatom(Client *c, int a, const char *v);
@@ -127,12 +162,11 @@ static void destroyclient(Client *c);
 static void cleanup(void);
 
 /* GTK/WebKit */
+static GdkDevice *getkbdevice(void);
 static WebKitWebView *newview(Client *c, WebKitWebView *rv);
 static GtkWidget *createview(WebKitWebView *v, WebKitNavigationAction *a,
                              Client *c);
 static gboolean buttonreleased(GtkWidget *w, GdkEvent *e, Client *c);
-static gboolean keypress(GtkAccelGroup *group, GObject *obj, guint key,
-                         GdkModifierType mods, Client *c);
 static GdkFilterReturn processx(GdkXEvent *xevent, GdkEvent *event,
                                 gpointer d);
 static gboolean winevent(GtkWidget *w, GdkEvent *e, Client *c);
@@ -163,8 +197,7 @@ static void reload(Client *c, const Arg *a);
 static void print(Client *c, const Arg *a);
 static void clipboard(Client *c, const Arg *a);
 static void zoom(Client *c, const Arg *a);
-static void scroll_v(Client *c, const Arg *a);
-static void scroll_h(Client *c, const Arg *a);
+static void scroll(Client *c, const Arg *a);
 static void navigate(Client *c, const Arg *a);
 static void stop(Client *c, const Arg *a);
 static void toggle(Client *c, const Arg *a);
@@ -188,6 +221,7 @@ static int showxid;
 static int cookiepolicy;
 static Display *dpy;
 static Client *clients;
+static GdkDevice *gdkkb;
 static char *stylefile;
 static const char *useragent;
 char *argv0;
@@ -235,6 +269,8 @@ setup(void)
        scriptfile = buildfile(scriptfile);
        cachedir   = buildpath(cachedir);
 
+       gdkkb = getkbdevice();
+
        if (!stylefile) {
                styledir = buildpath(styledir);
                for (i = 0; i < LENGTH(styles); ++i) {
@@ -343,21 +379,6 @@ newclient(Client *rc)
        return c;
 }
 
-void
-addaccelgroup(Client *c)
-{
-       int i;
-       GtkAccelGroup *group = gtk_accel_group_new();
-       GClosure *closure;
-
-       for (i = 0; i < LENGTH(keys); i++) {
-               closure = g_cclosure_new(G_CALLBACK(keypress), c, NULL);
-               gtk_accel_group_connect(group, keys[i].keyval, keys[i].mod, 0,
-                                       closure);
-       }
-       gtk_window_add_accel_group(GTK_WINDOW(c->win), group);
-}
-
 void
 loaduri(Client *c, const Arg *a)
 {
@@ -383,7 +404,6 @@ loaduri(Client *c, const Arg *a)
                reload(c, a);
        } else {
                webkit_web_view_load_uri(c->view, url);
-               c->title = geturi(c);
                updatetitle(c);
        }
 
@@ -433,7 +453,7 @@ void
 updatetitle(Client *c)
 {
        char *title;
-       const char *name = c->targeturi ? c->targeturi :
+       const char *name = c->overtitle ? c->overtitle :
                           c->title ? c->title : "";
 
        if (showindicators) {
@@ -679,6 +699,22 @@ cleanup(void)
        g_free(cachedir);
 }
 
+static GdkDevice *
+getkbdevice(void)
+{
+       GList *l, *gdl = gdk_device_manager_list_devices(
+                  gdk_display_get_device_manager(gdk_display_get_default()),
+                  GDK_DEVICE_TYPE_MASTER);
+       GdkDevice *gd = NULL;
+
+       for (l = gdl; l != NULL; l = l->next)
+               if (gdk_device_get_source(l->data) == GDK_SOURCE_KEYBOARD)
+                       gd = l->data;
+
+       g_list_free(gdl);
+       return gd;
+}
+
 WebKitWebView *
 newview(Client *c, WebKitWebView *rv)
 {
@@ -744,6 +780,13 @@ newview(Client *c, WebKitWebView *rv)
                webkit_cookie_manager_set_accept_policy(
                    webkit_web_context_get_cookie_manager(context),
                    cookiepolicy_get());
+               /* languages */
+               webkit_web_context_set_preferred_languages(context,
+                                                          preferedlanguages);
+               webkit_web_context_set_spell_checking_languages(context,
+                   spellinglanguages);
+               webkit_web_context_set_spell_checking_enabled(context,
+                   enablespellchecking);
 
                g_signal_connect(G_OBJECT(context), "download-started",
                                 G_CALLBACK(downloadstarted), c);
@@ -828,28 +871,6 @@ buttonreleased(GtkWidget *w, GdkEvent *e, Client *c)
        return FALSE;
 }
 
-gboolean
-keypress(GtkAccelGroup *group, GObject *obj, guint key, GdkModifierType mods,
-         Client *c)
-{
-       guint i;
-       gboolean processed = FALSE;
-
-       mods = CLEANMASK(mods);
-       key = gdk_keyval_to_lower(key);
-       updatewinid(c);
-       for (i = 0; i < LENGTH(keys); i++) {
-               if (key == keys[i].keyval
-                   && mods == keys[i].mod
-                   && keys[i].func) {
-                       keys[i].func(c, &(keys[i].arg));
-                       processed = TRUE;
-               }
-       }
-
-       return processed;
-}
-
 GdkFilterReturn
 processx(GdkXEvent *e, GdkEvent *event, gpointer d)
 {
@@ -878,23 +899,41 @@ processx(GdkXEvent *e, GdkEvent *event, gpointer d)
 gboolean
 winevent(GtkWidget *w, GdkEvent *e, Client *c)
 {
+       int i;
+
        switch (e->type) {
+       case GDK_ENTER_NOTIFY:
+               c->overtitle = c->targeturi;
+               updatetitle(c);
+               break;
+       case GDK_KEY_PRESS:
+               if (!kioskmode) {
+                       for (i = 0; i < LENGTH(keys); ++i) {
+                               if (gdk_keyval_to_lower(e->key.keyval) ==
+                                   keys[i].keyval &&
+                                   CLEANMASK(e->key.state) == keys[i].mod &&
+                                   keys[i].func) {
+                                       updatewinid(c);
+                                       keys[i].func(c, &(keys[i].arg));
+                                       return TRUE;
+                               }
+                       }
+               }
        case GDK_LEAVE_NOTIFY:
-               c->targeturi = NULL;
+               c->overtitle = NULL;
                updatetitle(c);
                break;
-       case GDK_WINDOW_STATE: /* fallthrough */
+       case GDK_WINDOW_STATE:
                if (e->window_state.changed_mask ==
-                   GDK_WINDOW_STATE_FULLSCREEN) {
+                   GDK_WINDOW_STATE_FULLSCREEN)
                        c->fullscreen = e->window_state.new_window_state &
                                        GDK_WINDOW_STATE_FULLSCREEN;
-                       break;
-               }
+               break;
        default:
-               return FALSE;
+               break;
        }
 
-       return TRUE;
+       return FALSE;
 }
 
 void
@@ -925,7 +964,6 @@ showview(WebKitWebView *v, Client *c)
                webkit_web_view_set_background_color(c->view, &bgcolor);
 
        if (!kioskmode) {
-               addaccelgroup(c);
                gdk_window_set_events(gwin, GDK_ALL_EVENTS_MASK);
                gdk_window_add_filter(gwin, processx, c);
        }
@@ -965,6 +1003,10 @@ createwindow(Client *c)
 
        g_signal_connect(G_OBJECT(w), "destroy",
                         G_CALLBACK(destroywin), c);
+       g_signal_connect(G_OBJECT(w), "enter-notify-event",
+                        G_CALLBACK(winevent), c);
+       g_signal_connect(G_OBJECT(w), "key-press-event",
+                        G_CALLBACK(winevent), c);
        g_signal_connect(G_OBJECT(w), "leave-notify-event",
                         G_CALLBACK(winevent), c);
        g_signal_connect(G_OBJECT(w), "window-state-event",
@@ -976,20 +1018,25 @@ createwindow(Client *c)
 void
 loadchanged(WebKitWebView *v, WebKitLoadEvent e, Client *c)
 {
+       const char *title = geturi(c);
+
        switch (e) {
        case WEBKIT_LOAD_STARTED:
+               setatom(c, AtomUri, title);
+               c->title = title;
                c->tlsflags = G_TLS_CERTIFICATE_VALIDATE_ALL + 1;
                break;
        case WEBKIT_LOAD_REDIRECTED:
-               setatom(c, AtomUri, geturi(c));
+               setatom(c, AtomUri, title);
+               c->title = title;
                break;
        case WEBKIT_LOAD_COMMITTED:
+               setatom(c, AtomUri, title);
+               c->title = title;
                if (!webkit_web_view_get_tls_info(c->view, NULL,
                    &(c->tlsflags)))
                        c->tlsflags = G_TLS_CERTIFICATE_VALIDATE_ALL + 1;
 
-               setatom(c, AtomUri, geturi(c));
-
                if (enablestyle)
                        setstyle(c, getstyle(geturi(c)));
                break;
@@ -1037,6 +1084,8 @@ mousetargetchanged(WebKitWebView *v, WebKitHitTestResult *h, guint modifiers,
                c->targeturi = webkit_hit_test_result_get_media_uri(h);
        else
                c->targeturi = NULL;
+
+       c->overtitle = c->targeturi;
        updatetitle(c);
 }
 
@@ -1263,17 +1312,38 @@ zoom(Client *c, const Arg *a)
 }
 
 void
-scroll_v(Client *c, const Arg *a)
+scroll(Client *c, const Arg *a)
 {
-       evalscript(c, "window.scrollBy(0, %d * (window.innerHeight / 100))",
-                  a->i);
-}
+       GdkEvent *ev = gdk_event_new(GDK_KEY_PRESS);
 
-void
-scroll_h(Client *c, const Arg *a)
-{
-       evalscript(c, "window.scrollBy(%d * (window.innerWidth / 100), 0)",
-                  a->i);
+       gdk_event_set_device(ev, gdkkb);
+//     gdk_event_set_screen(ev, gdk_screen_get_default());
+       ev->key.window = gtk_widget_get_window(GTK_WIDGET(c->win));
+       ev->key.state = GDK_CONTROL_MASK;
+       ev->key.time = GDK_CURRENT_TIME;
+
+       switch (a->i) {
+       case 'd':
+               ev->key.keyval = GDK_KEY_Down;
+               break;
+       case 'D':
+               ev->key.keyval = GDK_KEY_Page_Down;
+               break;
+       case 'l':
+               ev->key.keyval = GDK_KEY_Left;
+               break;
+       case 'r':
+               ev->key.keyval = GDK_KEY_Right;
+               break;
+       case 'U':
+               ev->key.keyval = GDK_KEY_Page_Up;
+               break;
+       case 'u':
+               ev->key.keyval = GDK_KEY_Up;
+               break;
+       }
+
+       gdk_event_put(ev);
 }
 
 void
@@ -1435,10 +1505,8 @@ clickexternplayer(Client *c, const Arg *a, WebKitHitTestResult *h)
 {
        Arg arg;
 
-       if (webkit_hit_test_result_get_context(h) & OnMedia) {
-               arg = (Arg)VIDEOPLAY(webkit_hit_test_result_get_media_uri(h));
-               spawn(c, &arg);
-       }
+       arg = (Arg)VIDEOPLAY(webkit_hit_test_result_get_media_uri(h));
+       spawn(c, &arg);
 }
 
 int
@@ -1527,7 +1595,7 @@ main(int argc, char *argv[])
                stylefile = EARGF(usage());
                break;
        case 'u':
-               useragent = EARGF(usage());
+               fulluseragent = EARGF(usage());
                break;
        case 'v':
                die("surf-"VERSION", ©2009-2015 surf engineers, "