surf

surf
git clone git@git.zachrice.app:repos/surf.git
Log | Files | Refs | README | LICENSE

commit 0d237d63a804116d6a6155e8125d35137c5840c9
parent e4b120452ac0427a83ed05b7de14299f7b09618d
Author: Zach Rice <bynxmusic@gmail.com>
Date:   Mon, 18 May 2026 19:48:58 -0400

Add scroll-to-top/bottom (Ctrl+,/.), fix empty URI download crash

Diffstat:
Mconfig.def.h | 3+++
Apatches/surf-download-emptyuri.patch | 23+++++++++++++++++++++++
Apatches/surf-hardwareaccel.patch | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apatches/surf-scrolltobounds.patch | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Msurf.c | 21++++++++++++++++++++-
5 files changed, 157 insertions(+), 1 deletion(-)

diff --git a/config.def.h b/config.def.h @@ -183,6 +183,9 @@ static Key keys[] = { { MODKEY, GDK_KEY_i, scrollh, { .i = +10 } }, { MODKEY, GDK_KEY_u, scrollh, { .i = -10 } }, + /* scroll to top/bottom of page (vim gg / G) */ + { MODKEY, GDK_KEY_comma, scrolltop, { 0 } }, + { MODKEY, GDK_KEY_period, scrollbottom, { 0 } }, { MODKEY|GDK_SHIFT_MASK, GDK_KEY_j, zoom, { .i = -1 } }, { MODKEY|GDK_SHIFT_MASK, GDK_KEY_k, zoom, { .i = +1 } }, diff --git a/patches/surf-download-emptyuri.patch b/patches/surf-download-emptyuri.patch @@ -0,0 +1,23 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Zach Rice <bynxmusic@gmail.com> +Date: Sun, 18 May 2026 00:00:00 +0000 +Subject: [PATCH] Skip download when URI is empty + +WebKit sometimes fires a download response with an empty URI (e.g. +on YouTube). This passes an empty string to curl which errors out. +Guard against it by returning early. + +diff --git a/surf.c b/surf.c +--- a/surf.c ++++ b/surf.c +@@ -1819,6 +1819,10 @@ void + download(Client *c, WebKitURIResponse *r) + { + const char *dluri = webkit_uri_response_get_uri(r); ++ ++ if (!dluri || !dluri[0]) ++ return; ++ + Arg a = (Arg)DOWNLOAD(dluri, geturi(c)); + spawn(c, &a); + } diff --git a/patches/surf-hardwareaccel.patch b/patches/surf-hardwareaccel.patch @@ -0,0 +1,62 @@ +diff --git a/config.def.h b/config.def.h +index ea29f8c..545351a 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -30,6 +30,7 @@ static Parameter defconfig[ParameterLast] = { + [FileURLsCrossAccess] = { { .i = 0 }, }, + [FontSize] = { { .i = 12 }, }, + [Geolocation] = { { .i = 0 }, }, ++ [HardwareAcceleration] = { { .i = 1 }, }, + [HideBackground] = { { .i = 0 }, }, + [Inspector] = { { .i = 1 }, }, + [JavaScript] = { { .i = 1 }, }, +diff --git a/surf.c b/surf.c +index 39ed548..11af08d 100644 +--- a/surf.c ++++ b/surf.c +@@ -65,6 +65,7 @@ typedef enum { + FileURLsCrossAccess, + FontSize, + Geolocation, ++ HardwareAcceleration, + HideBackground, + Inspector, + JavaScript, +@@ -288,6 +289,7 @@ static ParamName loadcommitted[] = { + DefaultCharset, + FontSize, + Geolocation, ++ HardwareAcceleration, + HideBackground, + Inspector, + // KioskMode, +@@ -685,6 +687,7 @@ gettogglestats(Client *c) + togglestats[4] = curconfig[LoadImages].val.i ? 'I' : 'i'; + togglestats[5] = curconfig[JavaScript].val.i ? 'S' : 's'; + togglestats[6] = curconfig[Style].val.i ? 'M' : 'm'; ++ togglestats[7] = curconfig[HardwareAcceleration].val.i ? 'A' : 'a'; + togglestats[8] = curconfig[Certificate].val.i ? 'X' : 'x'; + togglestats[9] = curconfig[StrictTLS].val.i ? 'T' : 't'; + } +@@ -810,6 +813,11 @@ setparameter(Client *c, int refresh, ParamName p, const Arg *a) + case Geolocation: + refresh = 0; + break; ++ case HardwareAcceleration: ++ webkit_settings_set_hardware_acceleration_policy(c->settings, a->i ? ++ WEBKIT_HARDWARE_ACCELERATION_POLICY_ON_DEMAND : ++ WEBKIT_HARDWARE_ACCELERATION_POLICY_NEVER); ++ break; + case HideBackground: + if (a->i) + webkit_web_view_set_background_color(c->view, &bgcolor); +@@ -1177,6 +1185,9 @@ newview(Client *c, WebKitWebView *rv) + "enable-site-specific-quirks", curconfig[SiteQuirks].val.i, + "enable-smooth-scrolling", curconfig[SmoothScrolling].val.i, + "enable-webgl", curconfig[WebGL].val.i, ++ "hardware-acceleration-policy", curconfig[HardwareAcceleration].val.i ? ++ WEBKIT_HARDWARE_ACCELERATION_POLICY_ON_DEMAND : ++ WEBKIT_HARDWARE_ACCELERATION_POLICY_NEVER, + "media-playback-requires-user-gesture", curconfig[MediaManualPlay].val.i, + NULL); + /* For more interesting settings, have a look at diff --git a/patches/surf-scrolltobounds.patch b/patches/surf-scrolltobounds.patch @@ -0,0 +1,49 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Zach Rice <bynxmusic@gmail.com> +Date: Sun, 18 May 2026 00:00:00 +0000 +Subject: [PATCH] Add scroll-to-top and scroll-to-bottom (gg/G) + +Adds scrolltop() and scrollbottom() functions using evalscript() +to run window.scrollTo() directly. Bound to MODKEY+comma and +MODKEY+period by default. + +diff --git a/config.def.h b/config.def.h +--- a/config.def.h ++++ b/config.def.h +@@ -183,6 +183,9 @@ static Key keys[] = { + { MODKEY, GDK_KEY_i, scrollh, { .i = +10 } }, + { MODKEY, GDK_KEY_u, scrollh, { .i = -10 } }, + ++ /* scroll to top/bottom of page (vim gg / G) */ ++ { MODKEY, GDK_KEY_comma, scrolltop, { 0 } }, ++ { MODKEY, GDK_KEY_period, scrollbottom, { 0 } }, + + { MODKEY|GDK_SHIFT_MASK, GDK_KEY_j, zoom, { .i = -1 } }, +diff --git a/surf.c b/surf.c +--- a/surf.c ++++ b/surf.c +@@ -236,6 +236,8 @@ static void zoom(Client *c, const Arg *a); + static void scrollv(Client *c, const Arg *a); + static void scrollh(Client *c, const Arg *a); ++static void scrolltop(Client *c, const Arg *a); ++static void scrollbottom(Client *c, const Arg *a); + static void navigate(Client *c, const Arg *a); + static void stop(Client *c, const Arg *a); +@@ -1953,6 +1955,16 @@ scrollh(Client *c, const Arg *a) + msgext(c, 'h', a); + } + ++void ++scrolltop(Client *c, const Arg *a) ++{ ++ evalscript(c, "window.scrollTo(0,0);"); ++} ++ ++void ++scrollbottom(Client *c, const Arg *a) ++{ ++ evalscript(c, "window.scrollTo(0,document.body.scrollHeight);"); ++} ++ + void + navigate(Client *c, const Arg *a) diff --git a/surf.c b/surf.c @@ -235,6 +235,8 @@ static void clipboard(Client *c, const Arg *a); static void zoom(Client *c, const Arg *a); static void scrollv(Client *c, const Arg *a); static void scrollh(Client *c, const Arg *a); +static void scrolltop(Client *c, const Arg *a); +static void scrollbottom(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); @@ -1816,7 +1818,12 @@ responsereceived(WebKitDownload *d, GParamSpec *ps, Client *c) void download(Client *c, WebKitURIResponse *r) { - Arg a = (Arg)DOWNLOAD(webkit_uri_response_get_uri(r), geturi(c)); + const char *dluri = webkit_uri_response_get_uri(r); + + if (!dluri || !dluri[0]) + return; + + Arg a = (Arg)DOWNLOAD(dluri, geturi(c)); spawn(c, &a); } @@ -1954,6 +1961,18 @@ scrollh(Client *c, const Arg *a) } void +scrolltop(Client *c, const Arg *a) +{ + evalscript(c, "window.scrollTo(0,0);"); +} + +void +scrollbottom(Client *c, const Arg *a) +{ + evalscript(c, "window.scrollTo(0,document.body.scrollHeight);"); +} + +void navigate(Client *c, const Arg *a) { if (a->i < 0)