sites

public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log | Files | Refs

st-newterm-0.9-tmux.diff (3772B)


      1 From 6640cf9809086d8cfb2363571d3e71a1a7a9f6bd Mon Sep 17 00:00:00 2001
      2 From: meator <meator.dev@gmail.com>
      3 Date: Tue, 25 Oct 2022 20:19:28 +0200
      4 Subject: [PATCH] Add support for tmux in newterm
      5 
      6 This commit tries to figure out if st's child is tmux and if so, it
      7 launches a shell with the CWD of the current process in the tmux session
      8 instead of the tmux client itself.
      9 
     10 This is heavily inspired by
     11 https://gist.github.com/TiddoLangerak/c61e1e48df91192f9554 (but
     12 converted to C).
     13 
     14 Tmux has to be a direct child of st. This means that you'll have to
     15 usually use the 'exec' shell builtin to attach tmux sessions.
     16 
     17 This patch only works on Linux. Other systems use different procfs which
     18 is incompatible or don't have procfs at all (or it's optional).
     19 ---
     20  st.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
     21  1 file changed, 82 insertions(+), 1 deletion(-)
     22 
     23 diff --git a/st.c b/st.c
     24 index 0261283..b95bf7a 100644
     25 --- a/st.c
     26 +++ b/st.c
     27 @@ -221,6 +221,8 @@ static char base64dec_getc(const char **);
     28  
     29  static ssize_t xwrite(int, const char *, size_t);
     30  
     31 +static int gettmuxpts(void);
     32 +
     33  /* Globals */
     34  static Term term;
     35  static Selection sel;
     36 @@ -1061,6 +1063,12 @@ tswapscreen(void)
     37  void
     38  newterm(const Arg* a)
     39  {
     40 +	int pts;
     41 +	FILE *fsession, *fpid;
     42 +	char session[5];
     43 +	char pidstr[10];
     44 +	char buf[48];
     45 +	size_t size;
     46  	switch (fork()) {
     47  	case -1:
     48  		die("fork failed: %s\n", strerror(errno));
     49 @@ -1072,7 +1080,37 @@ newterm(const Arg* a)
     50  			_exit(1);
     51  			break;
     52  		case 0:
     53 -			chdir_by_pid(pid);
     54 +			signal(SIGCHLD, SIG_DFL); /* pclose() needs to use wait() */
     55 +			pts = gettmuxpts();
     56 +			if (pts != -1) {
     57 +				snprintf(buf, sizeof buf, "tmux lsc -t /dev/pts/%d -F \"#{client_session}\"", pts);
     58 +				fsession = popen(buf, "r");
     59 +				if (!fsession) {
     60 +					fprintf(stderr, "Couldn't launch tmux.");
     61 +					_exit(1);
     62 +				}
     63 +				size = fread(session, 1, sizeof session, fsession);
     64 +				if (pclose(fsession) != 0 || size == 0) {
     65 +					fprintf(stderr, "Couldn't get tmux session.");
     66 +					_exit(1);
     67 +				}
     68 +				session[size - 1] = '\0'; /* size - 1 is used to also trim the \n */
     69 +
     70 +				snprintf(buf, sizeof buf, "tmux list-panes -st %s -F \"#{pane_pid}\"", session);
     71 +				fpid = popen(buf, "r");
     72 +				if (!fpid) {
     73 +					fprintf(stderr, "Couldn't launch tmux.");
     74 +					_exit(1);
     75 +				}
     76 +				size = fread(pidstr, 1, sizeof pidstr, fpid);
     77 +				if (pclose(fpid) != 0 || size == 0) {
     78 +					fprintf(stderr, "Couldn't get tmux session.");
     79 +					_exit(1);
     80 +				}
     81 +				pidstr[size - 1] = '\0';
     82 +			}
     83 +
     84 +			chdir_by_pid(pts != -1 ? atol(pidstr) : pid);
     85  			execl("/proc/self/exe", argv0, NULL);
     86  			_exit(1);
     87  			break;
     88 @@ -1092,6 +1130,49 @@ chdir_by_pid(pid_t pid)
     89  	return chdir(buf);
     90  }
     91  
     92 +/* returns the pty of tmux client or -1 if the child of st isn't tmux */
     93 +static int
     94 +gettmuxpts(void)
     95 +{
     96 +	char buf[32];
     97 +	char comm[17];
     98 +	int tty;
     99 +	FILE *fstat;
    100 +	FILE *fcomm;
    101 +	size_t numread;
    102 +
    103 +	snprintf(buf, sizeof buf, "/proc/%ld/comm", (long)pid);
    104 +
    105 +	fcomm = fopen(buf, "r");
    106 +	if (!fcomm) {
    107 +		fprintf(stderr, "Couldn't open %s: %s\n", buf, strerror(errno));
    108 +		_exit(1);
    109 +	}
    110 +
    111 +	numread = fread(comm, 1, sizeof comm - 1, fcomm);
    112 +	comm[numread] = '\0';
    113 +
    114 +	fclose(fcomm);
    115 +
    116 +	if (strcmp("tmux: client\n", comm) != 0)
    117 +		return -1;
    118 +
    119 +	snprintf(buf, sizeof buf, "/proc/%ld/stat", (long)pid);
    120 +	fstat = fopen(buf, "r");
    121 +	if (!fstat) {
    122 +		fprintf(stderr, "Couldn't open %s: %s\n", buf, strerror(errno));
    123 +		_exit(1);
    124 +	}
    125 +
    126 +	/*
    127 +	 * We can't skip the second field with %*s because it contains a space so
    128 +	 * we skip strlen("tmux: client") + 2 for the braces which is 14.
    129 +	 */
    130 +	fscanf(fstat, "%*d %*14c %*c %*d %*d %*d %d", &tty);
    131 +	fclose(fstat);
    132 +	return ((0xfff00000 & tty) >> 12) | (0xff & tty);
    133 +}
    134 +
    135  void
    136  tscrolldown(int orig, int n)
    137  {
    138 -- 
    139 2.38.0
    140