5

I spent quite a bit of time messing around with tmux recently, and cannnot figure out what I am doing wrong. It seems to me that some layouts are simply not allowed. There is no error or warning thrown, but it seems my requests are simply ignored. Here is my goal:

enter image description here

Here is what I have been able to achieve:

enter image description here

And here is the shell script I got working:

#!/bin/bash
SESSION=$USER

tmux -2 new-session -d -s $SESSION


tmux new-window -t $SESSION:1 -n 'Logs'
tmux split-window -h
tmux split-window -h
tmux split-window -v
tmux split-window -v


tmux select-pane -t 0
tmux resize-pane -L 100
tmux send-keys 'watch -n1 "cat /proc/cpuinfo | grep "MHz" | sort -r | head -32"' C-m

tmux select-pane -t 1
tmux resize-pane -L 100
tmux send-keys 'watch -n1 "cat /proc/cpuinfo | grep "MHz" | sort -r | tail -32"' C-m


tmux send-keys 'watch -n1 inxi -s' C-m


tmux select-pane -t 2
tmux resize-pane -U 50
tmux send-keys 'watch -n1 ${HOME}/bin/clean_sensors.sh' C-m


tmux select-pane -t 3
tmux send-keys 'watch -n1 rocm-smi' C-m

tmux select-pane -t 4
tmux resize-pane -U 7
tmux send-keys 'watch -n1 ${HOME}/bin/gpu_sensors.sh' C-m


# # Set default window
tmux select-window -t $SESSION:1

# Attach to session
tmux -2 attach-session -t $SESSION

Edit: Here are my results for the solution of @KamilMaciorowski:

enter image description here

You can see it is the correct number of windows but the relative sizes are still off.

Livid
  • 153
  • 1
  • 5
  • I've not used it, but see perl script tmuxlayout. Also note, I believe you can fine tune the sizing by moving the dividing lines between panes with the mouse, when this is enabled. – meuh Nov 22 '19 at 18:46
  • tmux doesn't really make this easy, but in smtx you can simply do printf '\033]60;.1:.33 .8:.16 .8:.33 1:.33 .2:1 .55:1 .7:1 .85:1 1:1\007' smtx is still experimental. https://github.com/wrp/smtx – William Pursell Dec 23 '20 at 21:01
  • I constantly find this question, because I forgot how to retrieve the window layout of a tmux session. In case you came for the same reason: Detach from your session and run tmux list-windows to retrieve all layouts. If you need the layout for tmuxinator, yank everything between [layout and ] @ for to be inserted into your .tmuxinator.yml. – Martin Braun May 29 '23 at 16:14

2 Answers2

4

it seems my requests are simply ignored

It's not clear what requests are ignored. Your script kinda works for me, nothing seems to be ignored if I run it fresh, without tmux server already running. With tmux server already running there are problems:

  • the session may already exist and someone may be (actively) using it,
  • the window may already exist,
  • your split-window commands may target some other (pre-existing) window.

It seems to me that some layouts are simply not allowed.

If your goal is to work with the layout you already have and get to the desired layout by doing something more after, then it will be difficult because you cannot easily get the desired top-right or the bottom-right pane after the whole left "33%" column is split to two "50%" panes. You need to split in a different order.

One way to learn what the order may be is to draw the layout on a piece of paper and cut it with scissors with straight cuts that go through, so each cut divides the current piece into two smaller ones. Or imagine breaking a chocolate bar.

A possible scheme for your desired layout is like this:

+---------------------+
|       1             |
|22222221             |
|   3   1             |
|   3   1#############|
|   3   1             |
|22222221             |
|       1             |
+---------------------+

The numbers indicate in what order you need to cut. The cut marked with # represents multiple cuts in the right column, you can make them in any order after 1, before or after 2 or 3. These # cuts are not important to the issue. The important thing is you need to make the two 2 cuts (in any order) after 1 and before 3. Your script performs the 3 cut without making 2 and then there is no straightforward way to get what you want.

There is split-window -f, it creates a new pane spanning the full window height or full window width, instead of splitting a single pane. While it brings some flexibility and allows creating panes in yet different order, it won't help you get the desired layout from your achieved layout; nor it will make more layouts possible.


For comparison, this is a truly impossible layout, I think:

+---------------------+
|       #             |
|       #             |
|       ##############|
|       |       |     |
|       |       |     |
|---------------|     |
|               |     |
+---------------------+

In reality you can cut it with scissors but not with tmux, because at least the first cut with scissors needs to bend to create two separate pieces of paper. An example first cut is drawn with # characters. You can interpret it as two cuts that are not "through", so for tmux this interpretation changes nothing, it's still impossible.


This is an example script that works for me in tmux 2.3. Remarks and improvements are listed as notes below the code; please read them before you run the script.

#!/bin/sh

session="$USER"
window="$session:1"

lines="$(tput lines)"
columns="$(tput cols)"

tmux -2 new-session -d -x "$columns" -y "$lines" -s "$session" 'echo "step -1"; bash'
tmux new-window -t "$window" -n 'Logs' 'echo "step 0"; bash' || exit

tmux split-window -t "$window"             -h -p 67    -d 'echo "step 1";  bash'
tmux split-window -t "$window"             -v -p 10 -b -d 'echo "step 2a"; bash'
tmux split-window -t "$window"             -v -p 22    -d 'echo "step 2b"; bash'
tmux split-window -t "$window"             -h -p 50       'echo "step 3";  bash'
tmux split-window -t "$window.{top-right}" -v -p 55    -d
tmux split-window -t "$window.{top-right}" -v -p 64
tmux select-pane  -t "$window.{bottom-right}"
tmux split-window -t "$window"             -v -p 67
tmux split-window -t "$window"             -v -p 50

tmux select-window -t "$window"

# Attach to session
tmux -2 attach-session -t "$session"

Notes:

  • The shebang is /bin/sh, no need for Bash here.
  • Get used to double-quoting variables. It's way better to habitually quote regardless if you need it than forget to quote when it makes a real difference.
  • I used lowercase names for variables.
  • I used tput to determine the size of the current terminal. Then new-session -d -x … -y … uses proper values. But:
    • In my initial tests this seemed unnecessary, all worked without -x or -y
    • … although according to my local manual it should be necessary.
    • Anyway if the session already exists, its size is determined and new-session -d -x … -y … will have no impact. You may experience distorted proportions in the layout after you attach and the panes get resized. There are tmux commands and options that may help (resize-window, window-size, default-size), although not in my oldish tmux, so I won't elaborate.
    • tput operands lines and cols are not required by POSIX. See this answer for alternatives.
  • If window cannot be created, the script exits.
  • Each split-window targets the right window. If the session existed or someone attached in the meantime and changed the window, the current window would not be what you assume. To be even more robust each split-window should target a respective pane explicitly. Note that select-pane before split-window (or before resize-pane as in your script) is not really robust in a general setup where many clients can interact with the same tmux server: some other client (user or script) could select another pane between these two commands.
  • I do not resize panes. I create them with proper percentages in the first place. The -p option I use works in my oldish tmux. The documentation tells me the newer syntax is like split-window -l 20% and resize-pane can work with percentages as well. I don't know if the newest tmux understands split-window -p. The documentation doesn't mention this option any more, it may be deprecated and unsupported or it may be kept for compatibility; I cannot test this (feedback or edit is welcome).
  • split-window takes percentages that refer to the available space, i.e. to the old size of the pane that is about to shrink to make space for the new pane (at least this is how it works in my oldish tmux with -p). This is why numbers like 22 appear ( 20/(20+70) ≈ 22 ). According to the documentation resize-pane uses percentages of the window size.
  • I use or don't use -d to stay in the current pane or make the new one current, depending on which pane I need to target with the next split-window.
  • When I need to specify/select a pane that fits a token like {top-right}, I use the token.
  • I do not use send-keys to run commands. In general do not use send-keys to run commands. Any(?) tmux command that creates a new shell inside tmux can run a shell command instead. In the example script echo "step 0"; bash is such shell command. It includes bash at the end, so the pane doesn't exit when the actual command (echo) finishes. An alternative is the remain-on-exit option. Your shell commands (watch) can work indefinitely, so you may not need such tricks. Running a shell command with send-keys like you do in your script makes sense if the command affects the shell it runs in and your goal is to work interactively in the shell prepared this way. E.g. the command may set variables or source a script; or you want to be able to interrupt watch and have it in the shell history. In any other case the "right" way is to pass commands with split-window, respawn-pane or some similar tmux command.

Additional note:

  • cat /proc/cpuinfo | grep "MHz" | … is a useless use of cat. Better ways:

    </proc/cpuinfo grep "MHz" | …
    grep "MHz" /proc/cpuinfo  | …
    
  • Thanks, the relative sizes are still incorrect for me though. See the updated question. – Livid Nov 22 '19 at 16:37
  • @Livid It has something to do with the size of the initial widow in tmux. In your case it gets resized later. Looks like my tmux doesn't(?) follow the documentation. Working on it. – Kamil Maciorowski Nov 22 '19 at 16:48
  • I'm not sure if it is relevant but I have more than one monitor of different resolutions. I want the tmux window to be on a 1280x1024 one. – Livid Nov 22 '19 at 16:50
  • @Livid What is your version of tmux? It looks like there were some changes, window-size option was introduced. My tmux behaves differently. Check the updated code in my answer and tell me if it helps. Note if the session already exists, its size is determined and new-session -d -x … -y … will have no impact. – Kamil Maciorowski Nov 22 '19 at 17:22
  • I've got version 2.6.. the new code works perfectly. Thanks. – Livid Nov 22 '19 at 17:27
1

I would make the left first then the right, splitting each time by the proportion of the pane you want:

tmux new -d
tmux splitw -dh
tmux splitw -p90
tmux splitw -dp22 # 20/90
tmux splitw -h
tmux selectp -t'{right}'
tmux splitw -p80
tmux splitw -p56 # (15+15+15)/80
tmux splitw -p50
tmux selectp -t'{top-left}'
tmux a

I just added one split-window at a time and looked at the result to see what pane was the current pane and how I needed to split next.

  • Thanks, yours looks similar to @KamilMaciorowski's for me, except there is one extra window on the right. Do the panes look correctly sized on your monitor? I am wondering if it is due to my multi-monitor set up now. – Livid Nov 22 '19 at 16:49
  • tmux will resize the layout if you attach to a terminal bigger or smaller than the session was created (and if it is created with -d it'll be 80x24), use "tmux new -x $COLUMNS -y $LINES" to avoid this. – Nicholas Marriott Nov 22 '19 at 16:52
  • This seems to be working... but I need to pick the right number of columns and lines. Do you know what that would be for a 1280x1024 monitor? – Livid Nov 22 '19 at 17:09
  • 1
    Resize the terminal to the size you will use then do "echo $COLUMNS $LINES" outside tmux. – Nicholas Marriott Nov 22 '19 at 18:12