5

Say I split a window into 2 panes, the right one has width 20 cells. But when I resize the outside terminal emulator, the pane's width changes.

Is there any way to fix this? Because I wanna display info which is formatted to fit for 20 cells

3 Answers 3

4

As far as I can tell, tmux does not have any settings for fixing the width to a minimum value, only to a max value. This is because terminal windows can be resized to smaller than tmux's min value and tmux may not have a suitable way of handling this.

That being said, if you have other tmux panes open, you could add a small script to PS1 (PS1='$(resizePane $id)other-stuff') or PROMPT_COMMAND in order to constantly resize the info-pane that you want to keep at 20c width as you use other panes within tmux. Essentially, 1) you can open the program, 2) resize the terminal, 3) move to another pane, 4) type Enter and blam, the pane is resized.

You should look at the man pages for

resize-pane [-DLRU] [-t target-pane] [adjustment]

If you are in the desired pane, all that must be done is to shrink the width from the the left -L or from the right -R by x characters necessary to arrive at 20. Essentially, if the pane is up against a wall taller than the pane, use it's direction for shrinking and the opposite for enlarging. If my pane is next to the right wall, use -R to reduce the width and -L to enlarge it.

To get the right amount of characters, you need to know the current width of the pane, which can be found along with the pane id if you type

tmux list-panes

I am not yet sure of a way to tell which pane number you are currently in except by Prefix+q or typing

tmux display-panes

This will give us the id number of all the panes in the current window. If we know the id# ($id) assuming it is against the right wall, we can resize the pane to 20 characters wide by:

paneWidths=$(tmux list-panes | awk '{print $2}' | cut -c 2- | cut -f 1 -d'x')
currentWidth=$(echo $paneWidths | sed $id'q;d')
resizeNum=$(expr $currentWidth - 20)

tmux resize-pane -R -t $id $resizeNum

Note that the resizeNum cannot be negative. If it is negative, then use the other side to resize to the absolute value of resizeNum (this can be automated, but I'll leave that up to you). Also, if you incorporate this into the code of your program, you can neglect the "-t $id" portion, but I do not know a way to automate finding $id from within your program.

It may also be of use for you to look at tmux setw main-pane-width tmux setw other-pane-width, which will set the specific pane width if you reload a new tile format. To cycle formats use Prefix+space

Hope this helps!

--- edited to add further info on automation ---

My previous thought, was for you to go to a new pane and run PS1="$(resizePane $id)${PS1}" and then hit ENTER within the other pane so that you can continue working in the newly resize pane, but I have considered a new option that may work for you ...

I have rethought this idea since originally posting and I think it can reasonably be automated if the program running has a loop that can send shell commands. This is because we can run the program by essentially splitting the window. Here is an example of what I mean. The following code

  1. User runs runCodeSplit.sh, which
  2. splits a new pane horizontally (20c width) to the right of the final pane and runs test.sh
  3. Enter Loop
  4. checks the current pane size and attempts to resize
  5. Sleeps for 10 seconds (but you can change this to do whatever you want)
  6. repeats 3 & 4 10 times, then exits

runCodeSplit.sh:

#!/bin/bash
# runCodeSplit.sh

paneID=$(tmux list-panes | wc -l)
echo "pane: $paneID"
echo $(expr $paneID - 1)

# now, open a new pane and run the func
tmux split-window -h -l 20 -t $(expr $paneID - 1 ) "~/test.sh $paneID"

test.sh:

#!/bin/bash
# test.sh
if [[ $# -lt 1 ]] ; then 
    echo "please provide the pane-number" 
    exit 
fi
paneID=$1
i=0
while [[ i -lt 10 ]] ; do
    # find the current width and resize it at every loop
    width=$(tmux list-panes | awk '{print $2}' | cut -c 2- | cut -f 1 -d'x' | sed $(expr $paneID + 1)'q;d')
    resizeNum=$(expr $width - 20)
    echo "$paneID : w $width     r $resizeNum"

    if [[ $resizeNum -gt 0 ]] ; then
        tmux resize-pane -R -t $paneID $resizeNum
    elif [[ $resizeNum -lt 0 ]] ; then
        tmux resize-pane -L -t $paneID $(expr 0 - $resizeNum)
    fi 

    #now, we can do stuff ... 
    let i++
    echo "i= $i / 10"
    sleep 10
done
read -p "Press any key to close this pane ... " -n1 -s

NOTE: Unfortunately, the way I implemented it above, you should only really run it once per tmux window. The problem is that each time you run it, they will bunch up next to each other, which in turn will set them competing for space (one will shrink the other in order to expand and vice versa). You may be able to work around this problem, but I leave that up to you to figure out.

4
  • Thanks for the detailed reply. You said use PS or Prompt command to resize the pane, does this mean I have to switch to that pane and press Enter to adjust the size? Is there any way to catch the event of terminal changing? so it can be automatic resizing. Since tmux itself can response to the terminal size changing.
    – haohaolee
    Aug 17, 2011 at 12:08
  • Sorry that my previous answer was less helpful to you. I rethought it and added a new section above which can let you automate the resizing process. FYI this is useful to me too :) Aug 18, 2011 at 8:22
  • mark as anwser. btw, finally I didn't follow this approach, because I found the shell I use -- zsh -- has the ability to call back when size changes, so I can use tmux resize in time.
    – haohaolee
    Aug 21, 2011 at 9:38
  • FYI: The current pane number can be found with tmux display-message -p '#P' Oct 25, 2013 at 11:52
2

I'll post my variety since it won't hurt anyone, and perhaps fits someone better as-is.

I use it to resize a pane in a irssi session inside tmux that reads the output from the nicklist.pl script. The pane frequently gets resized when I attach over different terminals, and it is needed to be >20 columns wide for the FIFO to work correctly (and it's unnecessary to have it wider, stealing my screen space).

Ignore the convoluted CURRENT_PANE_WIDTH calculation and use the grep+sed one instead if you want. The current one was mostly an experiment to keep the script entirely in shell (except the tmux calls, of course). It is most probably faster, which might be interesting for automation where the script runs frequently (I currently run it as a specific call, though).

#!/bin/sh
TMUX="/usr/bin/tmux"
TMUX_SESSION="irssi"
TMUX_WINDOW="0"
TMUX_WINDOW_PANE="1"
DESIRED_PANE_WIDTH="20"

error()
{
    printf '%s: error: %s\n' "${0##*/}" "${1}" 1>&2
    exit ${2}
}

# Find with grep and sed. More readable and easier to modify.
#CURRENT_PANE_WIDTH="$(${TMUX} list-panes -t "${TMUX_SESSION}:0 | grep "^${TMUX_WINDOW_PANE}: " | sed 's/^'"${TMUX_WINDOW_PANE}"': \[\([^x]\+\).*/\1/')"

# Find with shell expansion. Because it can be done!
CURRENT_PANE_WIDTH=$(${TMUX} list-panes -t "${TMUX_SESSION}:${TMUX_WINDOW}" |\
while read line; do
    PANE_MATCH="${line#${TMUX_WINDOW_PANE}:\ \[}"
    if [ "${line}" != "${PANE_MATCH}" ]; then
        CURRENT_PANE_WIDTH="${PANE_MATCH%%x*}"
        printf "${PANE_MATCH%%x*}"
        break;
    fi
done)

if [ -z "${CURRENT_PANE_WIDTH}" ]; then
    error 'no matching pane found.' 1
fi

if ! [ "${CURRENT_PANE_WIDTH}" -eq "${CURRENT_PANE_WIDTH}" ]; then
    error 'could not get integer width.' 1
elif ! [ "${CURRENT_PANE_WIDTH}" -eq "${DESIRED_PANE_WIDTH}" ]; then
    RESIZE_NUMBER=$((${CURRENT_PANE_WIDTH} - ${DESIRED_PANE_WIDTH}))
    if [ ${RESIZE_NUMBER} -ge 0 ]; then
        DIRECTION="R"
    else
        DIRECTION="L"
        RESIZE_NUMBER=$((-${RESIZE_NUMBER}))
    fi

    ${TMUX} resize-pane -t "${TMUX_SESSION}:${TMUX_WINDOW}" -${DIRECTION} ${RESIZE_NUMBER}
fi
3
  • Thanks. The situation that I'm in is exactly the same as yours, I use irssi and tmux too. But who should call this script? Should I call this in the pane 1?
    – haohaolee
    Mar 8, 2012 at 14:54
  • You can call it from anywhere, since it contains the tmux session name, etc. Personally, I rewrote the above as an irssi plugin instead - see the section "Auto update tmux pane size for nicklist.pl" at 510x.se/notes/posts/Install_and_setup_BitlBee . I've been using the plugin for a couple of weeeks at least, and it works great here. Mar 9, 2012 at 8:16
  • thanks very much. I would vote your answer up when I have enough reputation
    – haohaolee
    Mar 11, 2012 at 6:43
1

Since version 2.1, tmux supports hooks (grep the manual for ^HOOKS). The example given in the manual (of the version 3.1, which I only have):

A command's after hook is run after it completes, except when the command is run as part of a hook
itself.  They are named with an ‘after-’ prefix.  For example, the following command adds a hook to
select the even-vertical layout after every split-window:

      set-hook -g after-split-window "selectl even-vertical"

The commands to execute when the hook triggering even happens are given to set-hook in the same format as that used by bind-keys.

In addition to the hooks listed in the HOOKS section, you can use all events described in the CONTROL MODE section (search the manual for ^CONTROL). The hooks that you may use to resize panes when something interesting happens are client-resized, pane-exited and layout-change (not "changed"!). Read on prefixing the hooks with the prefix after-, as shown in the example cited above.

Different ways to refer to a pane which you can use with the resize-pane command are listed in the COMMANDS section of the manual. Incomplete list includes pane numbers, offset numbers, and tokens like {top-right} or {left-of}. The command accepts absolute and relative sizes in character cells, and percentages of the window width.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .