From 8c2b6a684a3d054cfa5ef0bc48641ca19650f4f5 Mon Sep 17 00:00:00 2001 From: Alexander Leonhardt Date: Mon, 3 Aug 2026 00:51:21 +0200 Subject: Added the latest changes --- surf.c | 266 ++++++++++++++++++++++++++++++++--------------------------------- 1 file changed, 133 insertions(+), 133 deletions(-) (limited to 'surf.c') diff --git a/surf.c b/surf.c index 7a0f624..462608b 100644 --- a/surf.c +++ b/surf.c @@ -4,8 +4,10 @@ */ #include #include +#include #include #include +#include #include #include #include @@ -20,17 +22,12 @@ #include #include -#include #include #include #include -#include #include #include #include -#include -#include -#include #include "arg.h" #include "common.h" @@ -38,8 +35,6 @@ #define LENGTH(x) (sizeof(x) / sizeof(x[0])) #define CLEANMASK(mask) (mask & (MODKEY|GDK_SHIFT_MASK)) -enum { AtomFind, AtomGo, AtomUri, AtomUTF8, AtomLast }; - enum { OnDoc = WEBKIT_HIT_TEST_RESULT_CONTEXT_DOCUMENT, OnLink = WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK, @@ -108,11 +103,12 @@ typedef struct Client { WebKitHitTestResult *mousepos; GTlsCertificate *cert, *failedcert; GTlsCertificateFlags tlserr; - Window xid; guint64 pageid; + char *ctlfile; /* control fifo, see initctl() */ + guint ctlsrc; int progress, fullscreen, https, insecure, errorpage; const char *title, *overtitle, *targeturi; - const char *needle; + char *needle; struct Client *next; } Client; @@ -158,8 +154,7 @@ static const char *getcurrentuserhomedir(void); static Client *newclient(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); -static const char *getatom(Client *c, int a); +static void addhistory(const char *uri); static void updatetitle(Client *c); static void gettogglestats(Client *c); static void getpagestats(Client *c); @@ -173,9 +168,9 @@ static const char *getstyle(const char *uri); static void setstyle(Client *c, const char *file); static void runscript(Client *c); static void evalscript(Client *c, const char *jsstr, ...); -static void updatewinid(Client *c); +static void updatewinvars(Client *c); static void handleplumb(Client *c, const char *uri); -static void newwindow(Client *c, const Arg *a, int noembed); +static void newwindow(Client *c, const Arg *a); static void spawn(Client *c, const Arg *a); static void msgext(Client *c, char type, const Arg *a); static void destroyclient(Client *c); @@ -187,8 +182,8 @@ static void initwebextensions(WebKitWebContext *wc, Client *c); static GtkWidget *createview(WebKitWebView *v, WebKitNavigationAction *a, Client *c); static gboolean buttonreleased(GtkWidget *w, GdkEvent *e, Client *c); -static GdkFilterReturn processx(GdkXEvent *xevent, GdkEvent *event, - gpointer d); +static void initctl(Client *c); +static gboolean readctl(GIOChannel *ch, GIOCondition cond, gpointer d); static gboolean winevent(GtkWidget *w, GdkEvent *e, Client *c); static void showview(WebKitWebView *v, Client *c); static GtkWidget *createwindow(Client *c); @@ -243,14 +238,15 @@ 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); -static char winid[64]; +/* state of the client last interacted with, for use in config.h commands; + * these must stay arrays, config.h puts them in static initializers */ +static char winid[PATH_MAX]; +static char winuri[BUFSIZ]; +static char winfind[BUFSIZ]; static char togglestats[11]; static char pagestats[2]; -static Atom atoms[AtomLast]; -static Window embed; -static int showxid; +static int showwinid; static int cookiepolicy; -static Display *dpy; static Client *clients; static GdkDevice *gdkkb; static char *stylefile; @@ -320,7 +316,7 @@ void usage(void) { die("usage: surf [-bBdDfFgGiIkKmMnNsStTvwxX]\n" - "[-a cookiepolicies ] [-c cookiefile] [-C stylefile] [-e xid]\n" + "[-a cookiepolicies ] [-c cookiefile] [-C stylefile]\n" "[-r scriptfile] [-u useragent] [-z zoomlevel] [uri]\n"); } @@ -336,16 +332,12 @@ setup(void) if (signal(SIGHUP, sighup) == SIG_ERR) die("Can't install SIGHUP handler"); - if (!(dpy = XOpenDisplay(NULL))) - die("Can't open default display"); - - /* atoms */ - atoms[AtomFind] = XInternAtom(dpy, "_SURF_FIND", False); - atoms[AtomGo] = XInternAtom(dpy, "_SURF_GO", False); - atoms[AtomUri] = XInternAtom(dpy, "_SURF_URI", False); - atoms[AtomUTF8] = XInternAtom(dpy, "UTF8_STRING", False); + /* the wayland app_id is taken from the program name */ + g_set_prgname("surf"); - gtk_init(NULL, NULL); + gdk_set_allowed_backends("wayland"); + if (!gtk_init_check(NULL, NULL)) + die("Can't open wayland display\n"); gdpy = gdk_display_get_default(); @@ -355,10 +347,13 @@ setup(void) cookiefile = buildfile(cookiefile); scriptfile = buildfile(scriptfile); certdir = buildpath(certdir); - if (curconfig[Ephemeral].val.i) + if (curconfig[Ephemeral].val.i) { cachedir = NULL; - else + histfile = NULL; + } else { cachedir = buildpath(cachedir); + histfile = buildfile(histfile); + } gdkkb = gdk_seat_get_keyboard(gdk_display_get_default_seat(gdpy)); @@ -587,8 +582,6 @@ loaduri(Client *c, const Arg *a) free(apath); } - setatom(c, AtomUri, url); - if (strcmp(url, geturi(c)) == 0) { reload(c, a); } else { @@ -609,35 +602,29 @@ geturi(Client *c) return uri; } +/* One visit per line, oldest first; rank them with sort(1), see GO(). + * Other surf processes append to the same file, so the line has to go out + * in a single write(2), or long URIs get interleaved with theirs. */ void -setatom(Client *c, int a, const char *v) +addhistory(const char *uri) { - XChangeProperty(dpy, c->xid, - atoms[a], atoms[AtomUTF8], 8, PropModeReplace, - (unsigned char *)v, strlen(v) + 1); - XSync(dpy, False); -} + char *line; + size_t len; + int fd; -const char * -getatom(Client *c, int a) -{ - static char buf[BUFSIZ]; - Atom adummy; - int idummy; - unsigned long ldummy; - unsigned char *p = NULL; - - XSync(dpy, False); - XGetWindowProperty(dpy, c->xid, - atoms[a], 0L, BUFSIZ, False, atoms[AtomUTF8], - &adummy, &idummy, &ldummy, &ldummy, &p); - if (p) - strncpy(buf, (char *)p, LENGTH(buf) - 1); - else - buf[0] = '\0'; - XFree(p); + if (!histfile || g_str_has_prefix(uri, "about:")) + return; - return buf; + line = g_strdup_printf("%s\n", uri); + len = strlen(line); + fd = open(histfile, O_WRONLY|O_APPEND|O_CREAT|O_CLOEXEC, 0600); + if (fd >= 0) { + if (write(fd, line, len) != (ssize_t)len) + fprintf(stderr, "%s: can't append to %s\n", + argv0, histfile); + close(fd); + } + g_free(line); } void @@ -980,10 +967,13 @@ evalscript(Client *c, const char *jsstr, ...) g_free(script); } +/* publish the state of c to the commands spawned on its behalf */ void -updatewinid(Client *c) +updatewinvars(Client *c) { - snprintf(winid, LENGTH(winid), "%lu", c->xid); + g_strlcpy(winid, c->ctlfile ? c->ctlfile : "", sizeof(winid)); + g_strlcpy(winuri, geturi(c), sizeof(winuri)); + g_strlcpy(winfind, c->needle ? c->needle : "", sizeof(winfind)); } void @@ -994,11 +984,10 @@ handleplumb(Client *c, const char *uri) } void -newwindow(Client *c, const Arg *a, int noembed) +newwindow(Client *c, const Arg *a) { int i = 0; - char tmp[64]; - const char *cmd[29], *uri; + const char *cmd[27], *uri; const Arg arg = { .v = cmd }; cmd[i++] = argv0; @@ -1014,11 +1003,6 @@ newwindow(Client *c, const Arg *a, int noembed) cmd[i++] = stylefile; } cmd[i++] = curconfig[DiskCache].val.i ? "-D" : "-d"; - if (embed && !noembed) { - cmd[i++] = "-e"; - snprintf(tmp, LENGTH(tmp), "%lu", embed); - cmd[i++] = tmp; - } cmd[i++] = curconfig[RunInFullscreen].val.i ? "-F" : "-f" ; cmd[i++] = curconfig[Geolocation].val.i ? "-G" : "-g" ; cmd[i++] = curconfig[LoadImages].val.i ? "-I" : "-i" ; @@ -1035,7 +1019,7 @@ newwindow(Client *c, const Arg *a, int noembed) cmd[i++] = "-u"; cmd[i++] = fulluseragent; } - if (showxid) + if (showwinid) cmd[i++] = "-w"; cmd[i++] = curconfig[Certificate].val.i ? "-X" : "-x" ; /* do not keep zoom level */ @@ -1051,8 +1035,6 @@ void spawn(Client *c, const Arg *a) { if (fork() == 0) { - if (dpy) - close(ConnectionNumber(dpy)); close(spair[0]); close(spair[1]); setsid(); @@ -1073,6 +1055,13 @@ destroyclient(Client *c) gtk_widget_destroy(c->win); */ + if (c->ctlfile) { + g_source_remove(c->ctlsrc); + unlink(c->ctlfile); + g_free(c->ctlfile); + } + g_free(c->needle); + for (p = clients; p && p->next != c; p = p->next) ; if (p) @@ -1094,7 +1083,7 @@ cleanup(void) g_free(scriptfile); g_free(stylefile); g_free(cachedir); - XCloseDisplay(dpy); + g_free(histfile); } WebKitWebView * @@ -1283,29 +1272,68 @@ buttonreleased(GtkWidget *w, GdkEvent *e, Client *c) return FALSE; } -GdkFilterReturn -processx(GdkXEvent *e, GdkEvent *event, gpointer d) +/* Every window is driven from the outside through a fifo of its own, taking + * one "command argument" line per write: + * + * printf 'uri %s\n' https://suckless.org >"$XDG_RUNTIME_DIR"/surf/1234-1 + */ +void +initctl(Client *c) { - Client *c = (Client *)d; - XPropertyEvent *ev; - Arg a; + GIOChannel *ch; + char *dir; + int fd; - if (((XEvent *)e)->type == PropertyNotify) { - ev = &((XEvent *)e)->xproperty; - if (ev->state == PropertyNewValue) { - if (ev->atom == atoms[AtomFind]) { - find(c, NULL); + dir = g_build_filename(g_get_user_runtime_dir(), "surf", NULL); + if (g_mkdir_with_parents(dir, 0700) < 0) + die("Can't create %s\n", dir); + c->ctlfile = g_strdup_printf("%s/%d-%"PRIu64, dir, getpid(), c->pageid); + g_free(dir); - return GDK_FILTER_REMOVE; - } else if (ev->atom == atoms[AtomGo]) { - a.v = getatom(c, AtomGo); - loaduri(c, &a); + unlink(c->ctlfile); /* leftover of a surf that died unclean */ + if (mkfifo(c->ctlfile, 0600) < 0) + die("Can't create %s\n", c->ctlfile); + /* O_RDWR so that we do not see EOF whenever a writer is done */ + if ((fd = open(c->ctlfile, O_RDWR|O_NONBLOCK|O_CLOEXEC)) < 0) + die("Can't open %s\n", c->ctlfile); - return GDK_FILTER_REMOVE; - } + ch = g_io_channel_unix_new(fd); + g_io_channel_set_encoding(ch, NULL, NULL); + g_io_channel_set_close_on_unref(ch, TRUE); + c->ctlsrc = g_io_add_watch(ch, G_IO_IN, readctl, c); + g_io_channel_unref(ch); +} + +gboolean +readctl(GIOChannel *ch, GIOCondition cond, gpointer d) +{ + Client *c = (Client *)d; + char *line, *arg; + Arg a; + + while (g_io_channel_read_line(ch, &line, NULL, NULL, NULL) + == G_IO_STATUS_NORMAL) { + g_strstrip(line); + if ((arg = strchr(line, ' '))) + *arg++ = '\0'; + else + arg = ""; + + if (!strcmp(line, "uri")) { + a.v = arg; + loaduri(c, &a); + } else if (!strcmp(line, "find")) { + g_free(c->needle); + c->needle = g_strdup(arg); + find(c, NULL); + } else if (line[0]) { + fprintf(stderr, "%s: no such command: %s\n", + argv0, line); } + g_free(line); } - return GDK_FILTER_CONTINUE; + + return TRUE; } gboolean @@ -1325,7 +1353,7 @@ winevent(GtkWidget *w, GdkEvent *e, Client *c) keys[i].keyval && CLEANMASK(e->key.state) == keys[i].mod && keys[i].func) { - updatewinid(c); + updatewinvars(c); keys[i].func(c, &(keys[i].arg)); return TRUE; } @@ -1352,7 +1380,6 @@ void showview(WebKitWebView *v, Client *c) { GdkRGBA bgcolor = { 0 }; - GdkWindow *gwin; c->finder = webkit_web_view_get_find_controller(c->view); c->inspector = webkit_web_view_get_inspector(c->view); @@ -1364,55 +1391,33 @@ showview(WebKitWebView *v, Client *c) gtk_widget_show_all(c->win); gtk_widget_grab_focus(GTK_WIDGET(c->view)); - gwin = gtk_widget_get_window(GTK_WIDGET(c->win)); - c->xid = gdk_x11_window_get_xid(gwin); - updatewinid(c); - if (showxid) { - gdk_display_sync(gtk_widget_get_display(c->win)); - puts(winid); - fflush(stdout); + if (!curconfig[KioskMode].val.i) { + initctl(c); + updatewinvars(c); + if (showwinid) { + puts(winid); + fflush(stdout); + } } if (curconfig[HideBackground].val.i) webkit_web_view_set_background_color(c->view, &bgcolor); - if (!curconfig[KioskMode].val.i) { - gdk_window_set_events(gwin, GDK_ALL_EVENTS_MASK); - gdk_window_add_filter(gwin, processx, c); - } - if (curconfig[RunInFullscreen].val.i) togglefullscreen(c, NULL); if (curconfig[ZoomLevel].val.f != 1.0) webkit_web_view_set_zoom_level(c->view, curconfig[ZoomLevel].val.f); - - setatom(c, AtomFind, ""); - setatom(c, AtomUri, "about:blank"); } GtkWidget * createwindow(Client *c) { - char *wmstr; GtkWidget *w; - if (embed) { - w = gtk_plug_new(embed); - } else { - w = gtk_window_new(GTK_WINDOW_TOPLEVEL); - - wmstr = g_path_get_basename(argv0); - gtk_window_set_wmclass(GTK_WINDOW(w), wmstr, "Surf"); - g_free(wmstr); - - wmstr = g_strdup_printf("%s[%"PRIu64"]", "Surf", c->pageid); - gtk_window_set_role(GTK_WINDOW(w), wmstr); - g_free(wmstr); - - gtk_window_set_default_size(GTK_WINDOW(w), winsize[0], winsize[1]); - } + w = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_default_size(GTK_WINDOW(w), winsize[0], winsize[1]); g_signal_connect(G_OBJECT(w), "destroy", G_CALLBACK(destroywin), c); @@ -1484,7 +1489,6 @@ loadchanged(WebKitWebView *v, WebKitLoadEvent e, Client *c) switch (e) { case WEBKIT_LOAD_STARTED: - setatom(c, AtomUri, uri); c->title = uri; c->https = c->insecure = 0; seturiparameters(c, uri, loadtransient); @@ -1494,13 +1498,12 @@ loadchanged(WebKitWebView *v, WebKitLoadEvent e, Client *c) g_clear_object(&c->failedcert); break; case WEBKIT_LOAD_REDIRECTED: - setatom(c, AtomUri, uri); c->title = uri; seturiparameters(c, uri, loadtransient); break; case WEBKIT_LOAD_COMMITTED: - setatom(c, AtomUri, uri); c->title = uri; + addhistory(uri); seturiparameters(c, uri, loadcommitted); c->https = webkit_web_view_get_tls_info(c->view, &c->cert, &c->tlserr); @@ -1674,7 +1677,7 @@ decidenewwindow(WebKitPolicyDecision *d, Client *c) * test for link clicked but no button ? */ arg.v = webkit_uri_request_get_uri( webkit_navigation_action_get_request(a)); - newwindow(c, &arg, 0); + newwindow(c, &arg); break; case WEBKIT_NAVIGATION_TYPE_OTHER: /* fallthrough */ default: @@ -1949,7 +1952,7 @@ find(Client *c, const Arg *a) else webkit_find_controller_search_previous(c->finder); } else { - s = getatom(c, AtomFind); + s = c->needle ? c->needle : ""; f = webkit_find_controller_get_search_text(c->finder); if (g_strcmp0(f, s) == 0) /* reset search */ @@ -1976,7 +1979,7 @@ clicknewwindow(Client *c, const Arg *a, WebKitHitTestResult *h) Arg arg; arg.v = webkit_hit_test_result_get_link_uri(h); - newwindow(c, &arg, a->i); + newwindow(c, &arg); } void @@ -2024,9 +2027,6 @@ main(int argc, char *argv[]) defconfig[DiskCache].val.i = 1; defconfig[DiskCache].prio = 2; break; - case 'e': - embed = strtol(EARGF(usage()), NULL, 0); - break; case 'f': defconfig[RunInFullscreen].val.i = 0; defconfig[RunInFullscreen].prio = 2; @@ -2100,7 +2100,7 @@ main(int argc, char *argv[]) case 'v': die("surf-"VERSION", see LICENSE for © details\n"); case 'w': - showxid = 1; + showwinid = 1; break; case 'x': defconfig[Certificate].val.i = 0; -- cgit