This is the entry point to surf on which gurf is built.
(def (surf (uri "about:blank") (rclient (current-surf-client)))
(start-surfing!)
(let (client (newclient rclient))
(showview client)
(loaduri client uri)
(updatetitle client)
(current-surf-client client)
client))
We only run if not yet running.
First we setup
, a C function.
(define-c-lambda setup () void "setup")
(def current-surf-driver (make-parameter #f))
(def (start-surfing!)
(cond ((current-surf-driver) => values)
(else
(setup)
(let (drv (spawn surf-driver!))
(current-surf-driver drv)
drv))))
There are probably better ways to go about this.
Essentially, GTK wants/needs to know when to iterate. If there’s a context-pending we need to iterate. Otherwise we wait.
(define-c-lambda gtk_surf_iteration
() bool "gboolean res = g_main_context_pending(NULL);
while (g_main_context_pending(NULL)) {
g_main_context_iteration(NULL, FALSE);
}; ___return(res);")
Now we play with time and sleep, trying to not use that much CPU while still being responsive.
(def (surf-driver!)
(def min-sleep 0.00001)
(def max-sleep 0.05)
(def sleep-incr 0.00000001)
(def sleep min-sleep)
(def (sleepy)
(thread-sleep! (max sleep max-sleep))
(when (< sleep max-sleep)
(+ sleep sleep-incr)))
(let lp ()
(def events? (gtk_surf_iteration))
(if events?
(set! sleep min-sleep)
(sleepy))
(lp)))
These are what gives us what we need to surf!
(define-c-lambda newclient (Client*) Client* "newclient")
(define-c-lambda showview (Client*) void
"showview(NULL, ___arg1); ___return;")
(define-c-lambda loaduri (Client* char-string) void
"Arg arg; arg.v = ___arg2 ; loaduri(___arg1, &arg); ___return;")
(define-c-lambda updatetitle (Client*) void "updatetitle")
A Client
is the “window” that contains a WebKitWebView
among other things.
Here’s the C.
typedef struct Client {
GtkWidget *win;
WebKitWebView *view;
WebKitWebInspector *inspector;
WebKitFindController *finder;
WebKitHitTestResult *mousepos;
GTlsCertificate *cert, *failedcert;
GTlsCertificateFlags tlserr;
Window xid;
guint64 pageid;
int progress, fullscreen, https, insecure, errorpage;
const char *title, *overtitle, *targeturi;
const char *needle;
struct Client *next;
} Client;
We’ll make it into Gerbil. A big deal here is that we do not want to free a client. That’s handled elsewhere.
But Gerbil’s define-c-struct
does have some nice setters and getters.
(c-declare "int ____nofreeclient(Client *c){ return 0;}")
(define-c-struct Client
((title . char-string)
(targeturi . char-string)
(next . Client-borrowed-ptr*))
"____nofreeclient")
Using that we can export the following.
Client Client* Client-next
Client-title Client-targeturi
A lot of functions expect a client. A lot of the time we have a master client or
something. Regardless, having a current-surf-client
mixes in a lot of scheme.
Surf has a global, clients
, which has all the current clients in this process, starting with the most recent one and ->next
‘ing the rest.
(define-c-lambda clients () Client* "___return(clients);")
We’ll call that surf-client
and use it + Client-next
to return a
list.
(defalias surf-client clients)
(def (surf-clients)
(let lp ((c (surf-client)))
(if (not c)
[]
(cons c (lp (Client-next c))))))
Then there’s current-surf-client
which
(def current-surf-client (make-parameter #f))
Surf has it, we just need to make it scheme-y.
Here’s the C.
void evalscript(Client *c, const char *jsstr, ...);
Here’s schemeifying the C.
(define-c-lambda evalscript (Client-borrowed-ptr* char-string) void
"evalscript(___arg1, \"%s\", ___arg2); ___return;")
And here’s the scheme.
Surf calls curl
. I don’t want that. This patch makes it work using
cd ~/me/src
cd ~/me/src/gurf/bootstrap/drewc/gurf/surf/
wget https://surf.suckless.org/patches/dlconsole/surf-dlconsole-20190919-d068a38.diff
Because we have other modifications it took some diff/patching, but now we have that
Surf ships with a fork and relaunch of itself when a new window is opened.
When using it from the REPL such things do not exist.
So we’ll use our own.
void
newsurf(Client *rc, const Arg *a)
{
Client* c = newclient(rc);
showview(NULL, c);
loaduri(c, a);
updatetitle(c);
}
Now rename newwindow
to spawnnewwindow
and redo newindow
.
void
newwindow(Client *c, const Arg *a, int noembed)
{
if (argv0 != NULL) {
spawnnewwindow(c, a, noembed);
} else {
newsurf(c, a);
}
}
This is all in the surf
code, so push the subtree.
git subtree push --prefix=bootstrap/drewc/gurf/surf ./ surf
This is so simple that it’s a brilliant start! It’s this easy to get a working browser? The future looks bright.
~/me/src/gurf
gxpkg link github.com/drewc/gurf $(pwd)
gxpkg build github.com/drewc/gurf
(export #t setup surf newclient showview current-surf-driver start-surfing! gtk_surf_iteration
<<Client-exports>>
<<surf-client-exports>>
)
(import :std/foreign
:gerbil/gambit/foreign
:gerbil/gambit/threads)
<<surf-driver!>>
<<start-surfing!>>
<<surf>>
<<surf-clients>>
(begin-ffi (setup
newclient clients loaduri showview
gtk_surf_iteration updatetitle
<<Client-exports>>
evalscript)
(c-declare "#include \"surf/surf.c\"")
<<setup>>
<<Client-struct>>
<<surf-c-functions>>
<<evalscript>>
<<clients>>
<<gtk_surf_iteration>>
)
surf is a simple web browser based on WebKit2/GTK+. It is able to display websites and follow links. It supports the XEmbed protocol which makes it possible to embed it in another application. Furthermore, one can point surf to another URI by setting its XProperties. – https://surf.suckless.org
Surf is a wonderful barebones browser that is the ideal starting point.
We want to start with it and go from there.
cd ~/me/src/gurf
git remote add surf https://git.suckless.org/surf
git fetch surf
git branch surf --track surf/surf-webkit2
git subtree add --prefix=bootstrap/drewc/gurf/surf ./ surf
mkdir doc
cd doc
wget http://www.troubleshooters.com/linux/surf.htm
pandoc -o Surf.org surf.htm
rm surf.htm