aboutsummaryrefslogtreecommitdiffstats
path: root/surf.c
diff options
context:
space:
mode:
authorAlexander Leonhardt <equinox.salexander@gmail.com>
2026-08-03 00:51:21 +0200
committerAlexander Leonhardt <equinox.salexander@gmail.com>
2026-08-03 00:51:21 +0200
commit8c2b6a684a3d054cfa5ef0bc48641ca19650f4f5 (patch)
treea506b1d24be827b7c45eec8b4fe35ff24755425e /surf.c
parent0f7f59a5071b82bbd1fb7845538cf93ed54b5afe (diff)
downloadsurf-8c2b6a684a3d054cfa5ef0bc48641ca19650f4f5.tar.gz
surf-8c2b6a684a3d054cfa5ef0bc48641ca19650f4f5.tar.bz2
surf-8c2b6a684a3d054cfa5ef0bc48641ca19650f4f5.zip
Added the latest changes
Diffstat (limited to 'surf.c')
-rw-r--r--surf.c264
1 files changed, 132 insertions, 132 deletions
diff --git a/surf.c b/surf.c
index 7a0f624..462608b 100644
--- a/surf.c
+++ b/surf.c
@@ -4,8 +4,10 @@
*/
#include <sys/file.h>
#include <sys/socket.h>
+#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <fcntl.h>
#include <glib.h>
#include <inttypes.h>
#include <libgen.h>
@@ -20,17 +22,12 @@
#include <gdk/gdk.h>
#include <gdk/gdkkeysyms.h>
-#include <gdk/gdkx.h>
#include <gio/gunixfdlist.h>
#include <glib/gstdio.h>
#include <gtk/gtk.h>
-#include <gtk/gtkx.h>
#include <gcr/gcr.h>
#include <JavaScriptCore/JavaScript.h>
#include <webkit2/webkit2.h>
-#include <X11/X.h>
-#include <X11/Xatom.h>
-#include <glib.h>
#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)
-{
- XChangeProperty(dpy, c->xid,
- atoms[a], atoms[AtomUTF8], 8, PropModeReplace,
- (unsigned char *)v, strlen(v) + 1);
- XSync(dpy, False);
-}
-
-const char *
-getatom(Client *c, int a)
+addhistory(const char *uri)
{
- static char buf[BUFSIZ];
- Atom adummy;
- int idummy;
- unsigned long ldummy;
- unsigned char *p = NULL;
+ char *line;
+ size_t len;
+ int fd;
- 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)
+{
+ GIOChannel *ch;
+ char *dir;
+ int fd;
+
+ 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);
+
+ 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);
+
+ 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;
- XPropertyEvent *ev;
+ char *line, *arg;
Arg a;
- if (((XEvent *)e)->type == PropertyNotify) {
- ev = &((XEvent *)e)->xproperty;
- if (ev->state == PropertyNewValue) {
- if (ev->atom == atoms[AtomFind]) {
- find(c, NULL);
-
- return GDK_FILTER_REMOVE;
- } else if (ev->atom == atoms[AtomGo]) {
- a.v = getatom(c, AtomGo);
- loaduri(c, &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 = "";
- return GDK_FILTER_REMOVE;
- }
+ 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;