diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..190cbfe --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +surf +*.orig +*.o +*.so diff --git a/Makefile b/Makefile index e5d4172..168d2df 100644 --- a/Makefile +++ b/Makefile @@ -64,6 +64,11 @@ install: all mkdir -p $(DESTDIR)$(MANPREFIX)/man1 sed "s/VERSION/$(VERSION)/g" < surf.1 > $(DESTDIR)$(MANPREFIX)/man1/surf.1 chmod 644 $(DESTDIR)$(MANPREFIX)/man1/surf.1 + cp surf.png suckless-surf.png + xdg-icon-resource install --size 128 suckless-surf.png + rm suckless-surf.png + xdg-desktop-menu install ./suckless-surf.directory ./suckless-surf.desktop + xdg-desktop-menu install ./suckless-surf-open.directory ./suckless-surf-open.desktop uninstall: rm -f $(DESTDIR)$(PREFIX)/bin/surf @@ -72,5 +77,8 @@ uninstall: rm -f $(DESTDIR)$(LIBDIR)/$$wlib; \ done - rmdir $(DESTDIR)$(LIBDIR) + xdg-desktop-menu uninstall suckless-surf.directory suckless-surf.desktop + xdg-desktop-menu uninstall suckless-surf-open.directory suckless-surf-open.desktop + xdg-icon-resource uninstall --size 128 suckless-surf.png .PHONY: all options distclean clean dist install uninstall diff --git a/config.def.h b/config.def.h index ef44721..deee2ca 100644 --- a/config.def.h +++ b/config.def.h @@ -6,6 +6,8 @@ static char *styledir = "~/.surf/styles/"; static char *certdir = "~/.surf/certificates/"; static char *cachedir = "~/.surf/cache/"; static char *cookiefile = "~/.surf/cookies.txt"; +static char *dldir = "~/dl/"; +static char *dlstatus = "~/.surf/dlstatus/"; /* Webkit default features */ /* Highest priority value will be used. @@ -74,13 +76,12 @@ static WebKitFindOptions findopts = WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE | } \ } -/* DOWNLOAD(URI, referer) */ -#define DOWNLOAD(u, r) { \ +#define DLSTATUS { \ .v = (const char *[]){ "st", "-e", "/bin/sh", "-c",\ - "curl -g -L -J -O -A \"$1\" -b \"$2\" -c \"$2\"" \ - " -e \"$3\" \"$4\"; read", \ - "surf-download", useragent, cookiefile, r, u, NULL \ - } \ + "while true; do cat $1/* 2>/dev/null || echo \"no hay descargas\";"\ + "A=; read A; "\ + "if [ $A = \"clean\" ]; then rm $1/*; fi; clear; done",\ + "surf-dlstatus", dlstatus, NULL } \ } /* PLUMB(URI) */ @@ -100,6 +101,11 @@ static WebKitFindOptions findopts = WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE | } \ } +/* Quick searching. */ +#define QSEARCH { \ + .v = (char *[]){"/bin/sh", "-c", "surf_qsearch $0 $1", winid, NULL } \ +} + /* styles */ /* * The iteration will stop at the first match, beginning at the beginning of @@ -177,6 +183,9 @@ 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_s, spawn, QSEARCH }, + /* download-console */ + { MODKEY, GDK_KEY_d, spawndls, { 0 } }, }; /* button definitions */ @@ -190,3 +199,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/config.h b/config.h new file mode 100644 index 0000000..e171cf4 --- /dev/null +++ b/config.h @@ -0,0 +1,203 @@ +/* modifier 0 means no modifier */ +static int surfuseragent = 1; /* Append Surf version to default WebKit user agent */ +static char *fulluseragent = ""; /* Or override the whole user agent string */ +static char *scriptfile = "~/.surf/script.js"; +static char *styledir = "~/.surf/styles/"; +static char *certdir = "~/.surf/certificates/"; +static char *cachedir = "~/.surf/cache/"; +static char *cookiefile = "~/.surf/cookies.txt"; +static char *dldir = "~/dl/"; +static char *dlstatus = "~/.surf/dlstatus/"; + +/* Webkit default features */ +/* Highest priority value will be used. + * Default parameters are priority 0 + * Per-uri parameters are priority 1 + * Command parameters are priority 2 + */ +static Parameter defconfig[ParameterLast] = { + /* parameter Arg value priority */ + [AccessMicrophone] = { { .i = 0 }, }, + [AccessWebcam] = { { .i = 0 }, }, + [Certificate] = { { .i = 0 }, }, + [CaretBrowsing] = { { .i = 0 }, }, + [CookiePolicies] = { { .v = "@Aa" }, }, + [DefaultCharset] = { { .v = "UTF-8" }, }, + [DiskCache] = { { .i = 1 }, }, + [DNSPrefetch] = { { .i = 0 }, }, + [Ephemeral] = { { .i = 0 }, }, + [FileURLsCrossAccess] = { { .i = 0 }, }, + [FontSize] = { { .i = 12 }, }, + [FrameFlattening] = { { .i = 0 }, }, + [Geolocation] = { { .i = 0 }, }, + [HideBackground] = { { .i = 0 }, }, + [Inspector] = { { .i = 0 }, }, + [Java] = { { .i = 1 }, }, + [JavaScript] = { { .i = 1 }, }, + [KioskMode] = { { .i = 0 }, }, + [LoadImages] = { { .i = 1 }, }, + [MediaManualPlay] = { { .i = 1 }, }, + [PreferredLanguages] = { { .v = (char *[]){ NULL } }, }, + [RunInFullscreen] = { { .i = 0 }, }, + [ScrollBars] = { { .i = 1 }, }, + [ShowIndicators] = { { .i = 1 }, }, + [SiteQuirks] = { { .i = 1 }, }, + [SmoothScrolling] = { { .i = 0 }, }, + [SpellChecking] = { { .i = 0 }, }, + [SpellLanguages] = { { .v = ((char *[]){ "en_US", NULL }) }, }, + [StrictTLS] = { { .i = 1 }, }, + [Style] = { { .i = 1 }, }, + [WebGL] = { { .i = 0 }, }, + [ZoomLevel] = { { .f = 1.0 }, }, +}; + +static UriParameters uriparams[] = { + { "(://|\\.)suckless\\.org(/|$)", { + [JavaScript] = { { .i = 0 }, 1 }, + }, }, +}; + +/* default window size: width, height */ +static int winsize[] = { 800, 600 }; + +static WebKitFindOptions findopts = WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE | + WEBKIT_FIND_OPTIONS_WRAP_AROUND; + +#define PROMPT_GO "Go:" +#define PROMPT_FIND "Find:" + +/* SETPROP(readprop, setprop, prompt)*/ +#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\"", \ + "surf-setprop", winid, r, s, p, NULL \ + } \ +} + +#define DLSTATUS { \ + .v = (const char *[]){ "st", "-e", "/bin/sh", "-c",\ + "while true; do cat $1/* 2>/dev/null || echo \"no hay descargas\";"\ + "A=; read A; "\ + "if [ $A = \"clean\" ]; then rm $1/*; fi; clear; done",\ + "surf-dlstatus", dlstatus, NULL } \ +} + +/* PLUMB(URI) */ +/* This called when some URI which does not begin with "about:", + * "http://" or "https://" should be opened. + */ +#define PLUMB(u) {\ + .v = (const char *[]){ "/bin/sh", "-c", \ + "xdg-open \"$0\"", u, NULL \ + } \ +} + +/* VIDEOPLAY(URI) */ +#define VIDEOPLAY(u) {\ + .v = (const char *[]){ "/bin/sh", "-c", \ + "mpv --really-quiet \"$0\"", u, NULL \ + } \ +} + +/* Quick searching. */ +#define QSEARCH { \ + .v = (char *[]){"/bin/sh", "-c", "surf_qsearch $0 $1", winid, NULL } \ +} + +/* styles */ +/* + * The iteration will stop at the first match, beginning at the beginning of + * the list. + */ +static SiteSpecific styles[] = { + /* regexp file in $styledir */ + { ".*", "default.css" }, +}; + +/* certificates */ +/* + * Provide custom certificate for urls + */ +static SiteSpecific certs[] = { + /* regexp file in $certdir */ + { "://suckless\\.org/", "suckless.org.crt" }, +}; + +#define MODKEY GDK_CONTROL_MASK + +/* hotkeys */ +/* + * If you use anything else but MODKEY and GDK_SHIFT_MASK, don't forget to + * 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) }, + + { 0, GDK_KEY_Escape, stop, { 0 } }, + { MODKEY, GDK_KEY_c, stop, { 0 } }, + + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_r, reload, { .i = 1 } }, + { MODKEY, GDK_KEY_r, reload, { .i = 0 } }, + + { MODKEY, GDK_KEY_l, navigate, { .i = +1 } }, + { MODKEY, GDK_KEY_h, navigate, { .i = -1 } }, + + /* 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 } }, + + + { 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_KEY_n, find, { .i = +1 } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_n, find, { .i = -1 } }, + + { MODKEY|GDK_SHIFT_MASK, 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 } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_o, toggleinspector, { 0 } }, + + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_c, toggle, { .i = CaretBrowsing } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_f, toggle, { .i = FrameFlattening } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_g, toggle, { .i = Geolocation } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_s, toggle, { .i = JavaScript } }, + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_i, toggle, { .i = LoadImages } }, + { 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_s, spawn, QSEARCH }, + /* download-console */ + { MODKEY, GDK_KEY_d, spawndls, { 0 } }, +}; + +/* button definitions */ +/* target can be OnDoc, OnLink, OnImg, OnMedia, OnEdit, OnBar, OnSel, OnAny */ +static Button buttons[] = { + /* target event mask button function argument stop event */ + { OnLink, 0, 2, clicknewwindow, { .i = 0 }, 1 }, + { OnLink, MODKEY, 2, clicknewwindow, { .i = 1 }, 1 }, + { OnLink, MODKEY, 1, clicknewwindow, { .i = 1 }, 1 }, + { OnAny, 0, 8, clicknavigate, { .i = -1 }, 1 }, + { OnAny, 0, 9, clicknavigate, { .i = +1 }, 1 }, + { OnMedia, MODKEY, 1, clickexternplayer, { 0 }, 1 }, +}; + +#define HOMEPAGE "https://surf.suckless.org/" diff --git a/suckless-surf-open.desktop b/suckless-surf-open.desktop new file mode 100644 index 0000000..f8ca41f --- /dev/null +++ b/suckless-surf-open.desktop @@ -0,0 +1,13 @@ +[Desktop Entry] +Version=2.0 +Name=surf-open +Icon=suckless-surf +GenericName=Web Browser +Comment=A simple web browser based on WebKit2/GTK+, with tabbed. +Keywords=Internet;WWW;Browser;Web;Explorer +Exec=/usr/local/bin/surf-open %u +Terminal=false +Type=Application +MimeType=text/html;text/xml;application/xhtml+xml;x-scheme-handler/http;x-scheme-handler/https; +Categories=Network;WebBrowser; + diff --git a/suckless-surf-open.directory b/suckless-surf-open.directory new file mode 100644 index 0000000..72ab23e --- /dev/null +++ b/suckless-surf-open.directory @@ -0,0 +1,4 @@ +[Desktop Entry] +Name=surf-open +Icon=suckless-surf +Type=Directory diff --git a/suckless-surf.desktop b/suckless-surf.desktop new file mode 100644 index 0000000..27f2f88 --- /dev/null +++ b/suckless-surf.desktop @@ -0,0 +1,12 @@ +[Desktop Entry] +Version=2.0 +Name=surf +Icon=suckless-surf +GenericName=Web Browser +Comment=A simple web browser based on WebKit2/GTK+. +Keywords=Internet;WWW;Browser;Web;Explorer +Exec=tabbed -c surf -e +Terminal=false +Type=Application +MimeType=text/html;text/xml;application/xhtml+xml;x-scheme-handler/http;x-scheme-handler/https; +Categories=Network;WebBrowser; diff --git a/suckless-surf.directory b/suckless-surf.directory new file mode 100644 index 0000000..53f0fbe --- /dev/null +++ b/suckless-surf.directory @@ -0,0 +1,4 @@ +[Desktop Entry] +Name=surf +Icon=suckless-surf +Type=Directory diff --git a/surf.c b/surf.c index af0fa74..bd6412d 100644 --- a/surf.c +++ b/surf.c @@ -239,6 +239,17 @@ static void clicknavigate(Client *c, const Arg *a, WebKitHitTestResult *h); static void clicknewwindow(Client *c, const Arg *a, WebKitHitTestResult *h); static void clickexternplayer(Client *c, const Arg *a, WebKitHitTestResult *h); +/* download-console */ +static void downloadstarted(WebKitWebContext *wc, WebKitDownload *d, + Client *c); +static void downloadfailed(WebKitDownload *d, GParamSpec *ps, void *arg); +static void downloadfinished(WebKitDownload *d, GParamSpec *ps, void *arg); +static gboolean decidedestination(WebKitDownload *d, + gchar *suggested_filename, void *arg); +static void printprogress(WebKitDownload *d, GParamSpec *ps, void *arg); +static void logdownload(WebKitDownload *d, gchar *tail); +static void spawndls(Client *c, const Arg *a); + static char winid[64]; static char togglestats[12]; static char pagestats[2]; @@ -354,6 +365,8 @@ setup(void) cachedir = NULL; else cachedir = buildpath(cachedir); + dlstatus = buildpath(dlstatus); + dldir = buildpath(dldir); gdkkb = gdk_seat_get_keyboard(gdk_display_get_default_seat(gdpy)); @@ -1088,6 +1101,8 @@ cleanup(void) g_free(scriptfile); g_free(stylefile); g_free(cachedir); + g_free(dldir); + g_free(dlstatus); XCloseDisplay(dpy); } @@ -1721,8 +1736,7 @@ decideresource(WebKitPolicyDecision *d, Client *c) if (webkit_response_policy_decision_is_mime_type_supported(r)) { webkit_policy_decision_use(d); } else { - webkit_policy_decision_ignore(d); - download(c, res); + webkit_policy_decision_download(d); } } @@ -1732,27 +1746,6 @@ insecurecontent(WebKitWebView *v, WebKitInsecureContentEvent e, Client *c) c->insecure = 1; } -void -downloadstarted(WebKitWebContext *wc, WebKitDownload *d, Client *c) -{ - g_signal_connect(G_OBJECT(d), "notify::response", - G_CALLBACK(responsereceived), c); -} - -void -responsereceived(WebKitDownload *d, GParamSpec *ps, Client *c) -{ - download(c, webkit_download_get_response(d)); - webkit_download_cancel(d); -} - -void -download(Client *c, WebKitURIResponse *r) -{ - Arg a = (Arg)DOWNLOAD(webkit_uri_response_get_uri(r), geturi(c)); - spawn(c, &a); -} - void webprocessterminated(WebKitWebView *v, WebKitWebProcessTerminationReason r, Client *c) @@ -1985,6 +1978,81 @@ clickexternplayer(Client *c, const Arg *a, WebKitHitTestResult *h) spawn(c, &arg); } +/* download-console */ + +void +downloadstarted(WebKitWebContext *wc, WebKitDownload *d, Client *c) +{ + webkit_download_set_allow_overwrite(d, TRUE); + g_signal_connect(G_OBJECT(d), "decide-destination", + G_CALLBACK(decidedestination), NULL); + g_signal_connect(G_OBJECT(d), "notify::estimated-progress", + G_CALLBACK(printprogress), NULL); + g_signal_connect(G_OBJECT(d), "failed", + G_CALLBACK(downloadfailed), NULL); + g_signal_connect(G_OBJECT(d), "finished", + G_CALLBACK(downloadfinished), NULL); +} + +void +downloadfailed(WebKitDownload *d, GParamSpec *ps, void *arg) +{ + logdownload(d, " -- FAILED"); +} + +void +downloadfinished(WebKitDownload *d, GParamSpec *ps, void *arg) +{ + logdownload(d, " -- COMPLETED"); +} + +gboolean +decidedestination(WebKitDownload *d, gchar *suggested_filename, void *arg) +{ + gchar *dest; + dest = g_strdup_printf("file://%s/%s", dldir, suggested_filename); + webkit_download_set_destination(d, dest); + return TRUE; +} + +void +printprogress(WebKitDownload *d, GParamSpec *ps, void *arg) +{ + logdownload(d, ""); +} + +void +logdownload(WebKitDownload *d, gchar *tail) +{ + gchar *filename, *statfile; + FILE *stat; + + filename = g_path_get_basename(webkit_download_get_destination(d)); + statfile = g_strdup_printf("%s/%s", dlstatus, filename); + + if ((stat = fopen(statfile, "w")) == NULL) { + perror("dlstatus"); + } else { + fprintf(stat, "%s: %d%% (%d.%ds)%s\n", + filename, + (int)(webkit_download_get_estimated_progress(d) * 100), + (int) webkit_download_get_elapsed_time(d), + (int)(webkit_download_get_elapsed_time(d) * 100), + tail); + fclose(stat); + } + + g_free(statfile); + g_free(filename); +} + +void +spawndls(Client *c, const Arg *a) +{ + Arg arg = (Arg)DLSTATUS; + spawn(c, &arg); +} + int main(int argc, char *argv[]) { @@ -2117,7 +2185,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);