surf

Simple browser by suckless.org (fork)
git clone git://git.ckyln.com/surf.git
Log | Files | Refs | README | LICENSE

commit 7e12d072a2716bad730a238c28b32e23109c5d7d
parent d068a3878b6b9f2841a49cd7948cdf9d62b55585
Author: Cem Keylan <cem@ckyln.com>
Date:   Fri, 22 Nov 2019 17:01:47 +0300

initial recommit

Diffstat:
A.gitignore | 6++++++
Mconfig.def.h | 111+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Msurf.c | 145+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
3 files changed, 225 insertions(+), 37 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,6 @@ +surf +*.o +config.h +*.diff +*.orig +*.rej diff --git a/config.def.h b/config.def.h @@ -6,6 +6,17 @@ static char *styledir = "~/.surf/styles/"; static char *certdir = "~/.surf/certificates/"; static char *cachedir = "~/.surf/cache/"; static char *cookiefile = "~/.surf/cookies.txt"; +static char *historyfile = "~/.surf/history.txt"; + +/* Search Engines */ +static SearchEngine searchengines[] = { + { "s", "https://duckduckgo.com/?q=%s" }, + { "aw", "https://wiki.archlinux.org/?search=%s" }, + { "docker", "https://hub.docker.com/search?q=%s" }, + { "gl", "https://gitlab.cemkeylan.com/cemkeylan/%s"}, + { "gl3", "https://gitlab.cemkeylan.com/3c1b/%s" }, + { "r", "https://reddit.com/r/%s" }, +}; /* Webkit default features */ /* Highest priority value will be used. @@ -25,7 +36,7 @@ static Parameter defconfig[ParameterLast] = { [DiskCache] = { { .i = 1 }, }, [DNSPrefetch] = { { .i = 0 }, }, [FileURLsCrossAccess] = { { .i = 0 }, }, - [FontSize] = { { .i = 12 }, }, + [FontSize] = { { .i = 14 }, }, [FrameFlattening] = { { .i = 0 }, }, [Geolocation] = { { .i = 0 }, }, [HideBackground] = { { .i = 0 }, }, @@ -70,8 +81,9 @@ static WebKitFindOptions findopts = WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE | #define SETPROP(r, s, p) { \ .v = (const char *[]){ "/bin/sh", "-c", \ "prop=\"$(printf '%b' \"$(xprop -id $1 $2 " \ - "| sed \"s/^$2(STRING) = //;s/^\\\"\\(.*\\)\\\"$/\\1/\")\" " \ - "| dmenu -p \"$4\" -w $1)\" && xprop -id $1 -f $3 8s -set $3 \"$prop\"", \ + "| sed \"s/^$2(STRING) = //;s/^\\\"\\(.*\\)\\\"$/\\1/\" && cat ~/.surf/bookmarks)\" " \ + "| dmenu -l 10 -p \"$4\" -w $1)\" && " \ + "xprop -id $1 -f $3 8s -set $3 \"$prop\"", \ "surf-setprop", winid, r, s, p, NULL \ } \ } @@ -102,6 +114,23 @@ static WebKitFindOptions findopts = WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE | } \ } + +#define SETURI(p) { .v = (char *[]){ "/bin/sh", "-c", \ +"prop=\"`surf_history_dmenu.sh`\" &&" \ +"xprop -id $1 -f $0 8s -set $0 \"$prop\"", \ +p, winid, NULL } } + +/* BM_ADD(readprop) */ +#define BM_ADD(r) {\ + .v = (const char *[]){ "/bin/sh", "-c", \ + "(echo $(xprop -id $0 $1) | cut -d '\"' -f2 " \ + "| sed 's/.*https*:\\/\\/\\(www\\.\\)\\?//' && cat ~/.surf/bookmarks) " \ + "| awk '!seen[$0]++' > ~/.surf/bookmarks.tmp && " \ + "mv ~/.surf/bookmarks.tmp ~/.surf/bookmarks", \ + winid, r, NULL \ + } \ +} + /* styles */ /* * The iteration will stop at the first match, beginning at the beginning of @@ -121,6 +150,16 @@ static SiteSpecific certs[] = { { "://suckless\\.org/", "suckless.org.crt" }, }; +static char *linkselect_curwin [] = { "/bin/sh", "-c", + "surf_linkselect.sh $0 'Link' | xargs -r xprop -id $0 -f _SURF_GO 8s -set _SURF_GO", + winid, NULL +}; +static char *linkselect_newwin [] = { "/bin/sh", "-c", + "surf_linkselect.sh $0 'Link (new window)' | xargs -r surf", + winid, NULL +}; +static char *editscreen[] = { "/bin/sh", "-c", "edit_screen.sh", NULL }; + #define MODKEY GDK_CONTROL_MASK /* hotkeys */ @@ -129,43 +168,47 @@ static SiteSpecific certs[] = { * edit the CLEANMASK() macro. */ static Key keys[] = { - /* modifier keyval function arg */ - { MODKEY, GDK_KEY_g, spawn, SETPROP("_SURF_URI", "_SURF_GO", PROMPT_GO) }, - { MODKEY, GDK_KEY_f, spawn, SETPROP("_SURF_FIND", "_SURF_FIND", PROMPT_FIND) }, - { MODKEY, GDK_KEY_slash, spawn, SETPROP("_SURF_FIND", "_SURF_FIND", PROMPT_FIND) }, + /* modifier keyval function arg */ + { 0, GDK_KEY_o, spawn, SETPROP("_SURF_URI", "_SURF_GO", PROMPT_GO) }, + { 0, GDK_KEY_f, externalpipe, { .v = linkselect_curwin } }, + { 0|GDK_SHIFT_MASK, GDK_KEY_f, externalpipe, { .v = linkselect_newwin } }, + { 0, GDK_KEY_slash, spawn, SETPROP("_SURF_FIND", "_SURF_FIND", PROMPT_FIND) }, - { 0, GDK_KEY_Escape, stop, { 0 } }, - { MODKEY, GDK_KEY_c, stop, { 0 } }, + { 0, GDK_KEY_i, insert, { .i = 1 } }, + { 0, GDK_KEY_Escape, insert, { .i = 0 } }, - { MODKEY|GDK_SHIFT_MASK, GDK_KEY_r, reload, { .i = 1 } }, - { MODKEY, GDK_KEY_r, reload, { .i = 0 } }, + { 0, GDK_KEY_c, stop, { 0 } }, - { MODKEY, GDK_KEY_l, navigate, { .i = +1 } }, - { MODKEY, GDK_KEY_h, navigate, { .i = -1 } }, + { MODKEY, GDK_KEY_r, reload, { .i = 1 } }, + { 0, GDK_KEY_r, reload, { .i = 0 } }, - /* vertical and horizontal scrolling, in viewport percentage */ - { MODKEY, GDK_KEY_j, scrollv, { .i = +10 } }, - { MODKEY, GDK_KEY_k, scrollv, { .i = -10 } }, - { MODKEY, GDK_KEY_space, scrollv, { .i = +50 } }, - { MODKEY, GDK_KEY_b, scrollv, { .i = -50 } }, - { MODKEY, GDK_KEY_i, scrollh, { .i = +10 } }, - { MODKEY, GDK_KEY_u, scrollh, { .i = -10 } }, + { 0|GDK_SHIFT_MASK, GDK_KEY_l, navigate, { .i = +1 } }, + { 0|GDK_SHIFT_MASK, GDK_KEY_h, navigate, { .i = -1 } }, + /* vertical and horizontal scrolling, in viewport per centage */ + { 0, GDK_KEY_j, scrollv, { .i = +10 } }, + { 0, GDK_KEY_k, scrollv, { .i = -10 } }, + { MODKEY, GDK_KEY_d, scrollv, { .i = +50 } }, + { MODKEY, GDK_KEY_u, scrollv, { .i = -50 } }, + { 0, GDK_KEY_i, insert, { .i = 1 } }, + { 0, GDK_KEY_Escape, insert, { .i = 0 } }, - { MODKEY|GDK_SHIFT_MASK, GDK_KEY_j, zoom, { .i = -1 } }, - { MODKEY|GDK_SHIFT_MASK, GDK_KEY_k, zoom, { .i = +1 } }, - { MODKEY|GDK_SHIFT_MASK, GDK_KEY_q, zoom, { .i = 0 } }, - { MODKEY, GDK_KEY_minus, zoom, { .i = -1 } }, - { MODKEY, GDK_KEY_plus, zoom, { .i = +1 } }, - { MODKEY, GDK_KEY_p, clipboard, { .i = 1 } }, - { MODKEY, GDK_KEY_y, clipboard, { .i = 0 } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_j, zoom, { .i = -1 } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_k, zoom, { .i = +1 } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_h, zoom, { .i = 0 } }, + { 0, GDK_KEY_minus, zoom, { .i = -1 } }, + { 0|GDK_SHIFT_MASK, GDK_KEY_plus, zoom, { .i = +1 } }, + { 0, GDK_KEY_equal, zoom, { .i = 0 } }, - { MODKEY, GDK_KEY_n, find, { .i = +1 } }, - { MODKEY|GDK_SHIFT_MASK, GDK_KEY_n, find, { .i = -1 } }, + { 0, GDK_KEY_p, clipboard, { .i = 1 } }, + { 0, GDK_KEY_y, clipboard, { .i = 0 } }, - { MODKEY|GDK_SHIFT_MASK, GDK_KEY_p, print, { 0 } }, - { MODKEY, GDK_KEY_t, showcert, { 0 } }, + { 0, GDK_KEY_n, find, { .i = +1 } }, + { 0|GDK_SHIFT_MASK, GDK_KEY_n, find, { .i = -1 } }, + + { MODKEY, GDK_KEY_p, print, { 0 } }, + { MODKEY, GDK_KEY_t, showcert, { 0 } }, { MODKEY|GDK_SHIFT_MASK, GDK_KEY_a, togglecookiepolicy, { 0 } }, { 0, GDK_KEY_F11, togglefullscreen, { 0 } }, @@ -180,6 +223,10 @@ static Key keys[] = { { MODKEY|GDK_SHIFT_MASK, GDK_KEY_b, toggle, { .i = ScrollBars } }, { MODKEY|GDK_SHIFT_MASK, GDK_KEY_t, toggle, { .i = StrictTLS } }, { MODKEY|GDK_SHIFT_MASK, GDK_KEY_m, toggle, { .i = Style } }, + { MODKEY , GDK_KEY_Return, spawn, SETURI("_SURF_GO") }, + + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_e, externalpipe, { .v = editscreen } }, + { MODKEY, GDK_KEY_m, spawn, BM_ADD("_SURF_URI") }, }; /* button definitions */ @@ -193,3 +240,5 @@ static Button buttons[] = { { OnAny, 0, 9, clicknavigate, { .i = +1 }, 1 }, { OnMedia, MODKEY, 1, clickexternplayer, { 0 }, 1 }, }; + +#define HOMEPAGE "https://duckduckgo.com/" diff --git a/surf.c b/surf.c @@ -129,6 +129,11 @@ typedef struct { } Button; typedef struct { + char *token; + char *uri; +} SearchEngine; + +typedef struct { const char *uri; Parameter config[ParameterLast]; regex_t re; @@ -175,6 +180,8 @@ static void spawn(Client *c, const Arg *a); static void msgext(Client *c, char type, const Arg *a); static void destroyclient(Client *c); static void cleanup(void); +static int insertmode = 0; +static void updatehistory(const char *u, const char *t); /* GTK/WebKit */ static WebKitWebView *newview(Client *c, WebKitWebView *rv); @@ -214,6 +221,7 @@ static void webprocessterminated(WebKitWebView *v, Client *c); static void closeview(WebKitWebView *v, Client *c); static void destroywin(GtkWidget* w, Client *c); +static gchar *parseuri(const gchar *uri); /* Hotkeys */ static void pasteuri(GtkClipboard *clipboard, const char *text, gpointer d); @@ -231,6 +239,8 @@ static void togglefullscreen(Client *c, const Arg *a); static void togglecookiepolicy(Client *c, const Arg *a); static void toggleinspector(Client *c, const Arg *a); static void find(Client *c, const Arg *a); +static void insert(Client *c, const Arg *a); +static void externalpipe(Client *c, const Arg *a); /* Buttons */ static void clicknavigate(Client *c, const Arg *a, WebKitHitTestResult *h); @@ -301,6 +311,80 @@ static ParamName loadfinished[] = { /* configuration, allows nested code to access above variables */ #include "config.h" +static void +externalpipe_execute(char* buffer, Arg *arg) { + int to[2]; + void (*oldsigpipe)(int); + + if (pipe(to) == -1) + return; + + switch (fork()) { + case -1: + close(to[0]); + close(to[1]); + return; + case 0: + dup2(to[0], STDIN_FILENO); close(to[0]); close(to[1]); + execvp(((char **)arg->v)[0], (char **)arg->v); + fprintf(stderr, "st: execvp %s\n", ((char **)arg->v)[0]); + perror("failed"); + exit(0); + } + + close(to[0]); + oldsigpipe = signal(SIGPIPE, SIG_IGN); + write(to[1], buffer, strlen(buffer)); + close(to[1]); + signal(SIGPIPE, oldsigpipe); +} + +static void +externalpipe_resource_done(WebKitWebResource *r, GAsyncResult *s, Arg *arg) +{ + GError *gerr = NULL; + guchar *buffer = webkit_web_resource_get_data_finish(r, s, NULL, &gerr); + if (gerr == NULL) { + externalpipe_execute((char *) buffer, arg); + } else { + g_error_free(gerr); + } + g_free(buffer); +} + +static void +externalpipe_js_done(WebKitWebView *wv, GAsyncResult *s, Arg *arg) +{ + WebKitJavascriptResult *j = webkit_web_view_run_javascript_finish( + wv, s, NULL); + if (!j) { + return; + } + JSCValue *v = webkit_javascript_result_get_js_value(j); + if (jsc_value_is_string(v)) { + char *buffer = jsc_value_to_string(v); + externalpipe_execute(buffer, arg); + g_free(buffer); + } + webkit_javascript_result_unref(j); +} + +void +externalpipe(Client *c, const Arg *arg) +{ + if (curconfig[JavaScript].val.i) { + webkit_web_view_run_javascript( + c->view, "window.document.documentElement.outerHTML", + NULL, externalpipe_js_done, arg); + } else { + WebKitWebResource *resource = webkit_web_view_get_main_resource(c->view); + if (resource != NULL) { + webkit_web_resource_get_data( + resource, NULL, externalpipe_resource_done, arg); + } + } +} + void usage(void) { @@ -336,10 +420,11 @@ setup(void) curconfig = defconfig; /* dirs and files */ - cookiefile = buildfile(cookiefile); - scriptfile = buildfile(scriptfile); - cachedir = buildpath(cachedir); - certdir = buildpath(certdir); + cookiefile = buildfile(cookiefile); + historyfile = buildfile(historyfile); + scriptfile = buildfile(scriptfile); + cachedir = buildpath(cachedir); + certdir = buildpath(certdir); gdkkb = gdk_seat_get_keyboard(gdk_display_get_default_seat(gdpy)); @@ -559,7 +644,7 @@ loaduri(Client *c, const Arg *a) url = g_strdup_printf("file://%s", path); free(path); } else { - url = g_strdup_printf("http://%s", uri); + url = parseuri(uri); } if (apath != uri) free(apath); @@ -1076,12 +1161,28 @@ cleanup(void) close(pipein[0]); close(pipeout[1]); g_free(cookiefile); + g_free(historyfile); g_free(scriptfile); g_free(stylefile); g_free(cachedir); XCloseDisplay(dpy); } +void +updatehistory(const char *u, const char *t) +{ + FILE *f; + f = fopen(historyfile, "a+"); + + char b[20]; + time_t now = time (0); + strftime (b, 20, "%Y-%m-%d %H:%M:%S", localtime (&now)); + fputs(b, f); + + fprintf(f, " %s %s\n", u, t); + fclose(f); +} + WebKitWebView * newview(Client *c, WebKitWebView *rv) { @@ -1333,7 +1434,11 @@ winevent(GtkWidget *w, GdkEvent *e, Client *c) updatetitle(c); break; case GDK_KEY_PRESS: - if (!curconfig[KioskMode].val.i) { + if (!curconfig[KioskMode].val.i && + !insertmode || + CLEANMASK(e->key.state) == (MODKEY|GDK_SHIFT_MASK) || + CLEANMASK(e->key.state) == (MODKEY) || + gdk_keyval_to_lower(e->key.keyval) == (GDK_KEY_Escape)) { for (i = 0; i < LENGTH(keys); ++i) { if (gdk_keyval_to_lower(e->key.keyval) == keys[i].keyval && @@ -1491,6 +1596,7 @@ loadfailedtls(WebKitWebView *v, gchar *uri, GTlsCertificate *cert, return TRUE; } + void loadchanged(WebKitWebView *v, WebKitLoadEvent e, Client *c) { @@ -1519,6 +1625,7 @@ loadchanged(WebKitWebView *v, WebKitLoadEvent e, Client *c) break; case WEBKIT_LOAD_FINISHED: seturiparameters(c, uri, loadfinished); + updatehistory(uri, c->title); /* Disabled until we write some WebKitWebExtension for * manipulating the DOM directly. evalscript(c, "document.documentElement.style.overflow = '%s'", @@ -1765,6 +1872,22 @@ destroywin(GtkWidget* w, Client *c) gtk_main_quit(); } +gchar * +parseuri(const gchar *uri) { + guint i; + + for (i = 0; i < LENGTH(searchengines); i++) { + if (searchengines[i].token == NULL || searchengines[i].uri == NULL || + *(uri + strlen(searchengines[i].token)) != ' ') + continue; + if (g_str_has_prefix(uri, searchengines[i].token)) + return g_strdup_printf(searchengines[i].uri, + uri + strlen(searchengines[i].token) + 1); + } + + return g_strdup_printf("http://%s", uri); +} + void pasteuri(GtkClipboard *clipboard, const char *text, gpointer d) { @@ -1948,6 +2071,12 @@ find(Client *c, const Arg *a) } void +insert(Client *c, const Arg *a) +{ + insertmode = (a->i); +} + +void clicknavigate(Client *c, const Arg *a, WebKitHitTestResult *h) { navigate(c, a); @@ -2111,7 +2240,11 @@ main(int argc, char *argv[]) if (argc > 0) arg.v = argv[0]; else +#ifdef HOMEPAGE + arg.v = HOMEPAGE; +#else arg.v = "about:blank"; +#endif setup(); c = newclient(NULL);