scklss/st-0.9/patches/st-newterm-0.9-tmux.diff

141 lines
3.7 KiB
Diff
Raw Normal View History

From 6640cf9809086d8cfb2363571d3e71a1a7a9f6bd Mon Sep 17 00:00:00 2001
From: meator <meator.dev@gmail.com>
Date: Tue, 25 Oct 2022 20:19:28 +0200
Subject: [PATCH] Add support for tmux in newterm
This commit tries to figure out if st's child is tmux and if so, it
launches a shell with the CWD of the current process in the tmux session
instead of the tmux client itself.
This is heavily inspired by
https://gist.github.com/TiddoLangerak/c61e1e48df91192f9554 (but
converted to C).
Tmux has to be a direct child of st. This means that you'll have to
usually use the 'exec' shell builtin to attach tmux sessions.
This patch only works on Linux. Other systems use different procfs which
is incompatible or don't have procfs at all (or it's optional).
---
st.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 82 insertions(+), 1 deletion(-)
diff --git a/st.c b/st.c
index 0261283..b95bf7a 100644
--- a/st.c
+++ b/st.c
@@ -221,6 +221,8 @@ static char base64dec_getc(const char **);
static ssize_t xwrite(int, const char *, size_t);
+static int gettmuxpts(void);
+
/* Globals */
static Term term;
static Selection sel;
@@ -1061,6 +1063,12 @@ tswapscreen(void)
void
newterm(const Arg* a)
{
+ int pts;
+ FILE *fsession, *fpid;
+ char session[5];
+ char pidstr[10];
+ char buf[48];
+ size_t size;
switch (fork()) {
case -1:
die("fork failed: %s\n", strerror(errno));
@@ -1072,7 +1080,37 @@ newterm(const Arg* a)
_exit(1);
break;
case 0:
- chdir_by_pid(pid);
+ signal(SIGCHLD, SIG_DFL); /* pclose() needs to use wait() */
+ pts = gettmuxpts();
+ if (pts != -1) {
+ snprintf(buf, sizeof buf, "tmux lsc -t /dev/pts/%d -F \"#{client_session}\"", pts);
+ fsession = popen(buf, "r");
+ if (!fsession) {
+ fprintf(stderr, "Couldn't launch tmux.");
+ _exit(1);
+ }
+ size = fread(session, 1, sizeof session, fsession);
+ if (pclose(fsession) != 0 || size == 0) {
+ fprintf(stderr, "Couldn't get tmux session.");
+ _exit(1);
+ }
+ session[size - 1] = '\0'; /* size - 1 is used to also trim the \n */
+
+ snprintf(buf, sizeof buf, "tmux list-panes -st %s -F \"#{pane_pid}\"", session);
+ fpid = popen(buf, "r");
+ if (!fpid) {
+ fprintf(stderr, "Couldn't launch tmux.");
+ _exit(1);
+ }
+ size = fread(pidstr, 1, sizeof pidstr, fpid);
+ if (pclose(fpid) != 0 || size == 0) {
+ fprintf(stderr, "Couldn't get tmux session.");
+ _exit(1);
+ }
+ pidstr[size - 1] = '\0';
+ }
+
+ chdir_by_pid(pts != -1 ? atol(pidstr) : pid);
execl("/proc/self/exe", argv0, NULL);
_exit(1);
break;
@@ -1092,6 +1130,49 @@ chdir_by_pid(pid_t pid)
return chdir(buf);
}
+/* returns the pty of tmux client or -1 if the child of st isn't tmux */
+static int
+gettmuxpts(void)
+{
+ char buf[32];
+ char comm[17];
+ int tty;
+ FILE *fstat;
+ FILE *fcomm;
+ size_t numread;
+
+ snprintf(buf, sizeof buf, "/proc/%ld/comm", (long)pid);
+
+ fcomm = fopen(buf, "r");
+ if (!fcomm) {
+ fprintf(stderr, "Couldn't open %s: %s\n", buf, strerror(errno));
+ _exit(1);
+ }
+
+ numread = fread(comm, 1, sizeof comm - 1, fcomm);
+ comm[numread] = '\0';
+
+ fclose(fcomm);
+
+ if (strcmp("tmux: client\n", comm) != 0)
+ return -1;
+
+ snprintf(buf, sizeof buf, "/proc/%ld/stat", (long)pid);
+ fstat = fopen(buf, "r");
+ if (!fstat) {
+ fprintf(stderr, "Couldn't open %s: %s\n", buf, strerror(errno));
+ _exit(1);
+ }
+
+ /*
+ * We can't skip the second field with %*s because it contains a space so
+ * we skip strlen("tmux: client") + 2 for the braces which is 14.
+ */
+ fscanf(fstat, "%*d %*14c %*c %*d %*d %*d %d", &tty);
+ fclose(fstat);
+ return ((0xfff00000 & tty) >> 12) | (0xff & tty);
+}
+
void
tscrolldown(int orig, int n)
{
--
2.38.0