skip to Main Content

I use zsh (zsh 5.9 (x86_64-apple-darwin22.0)) and zsh/curses for a small command line interface. The content of the view should automatically adjust when the window size changes. I tried a solution with trap SIGWINCH, but with zsh (I tried it on Ubuntu and macOS) trap SIGWINCH obviously doesn’t work. So I use a background shell with a busy loop (see monitorwindowsize below) that sends SIGUSR1 to the parent shell to signal it to resize the window. The only solution I’ve found to make the inner loop wait a while is to call sleep (e.g. call read -t 10 does not work here), but – you know – sleep creates a new process each time it’s called. And of course I want to call the resize function as fast as possible, so I’m interested in keeping the sleep time as short as possible, i.e. calling sleep quite often. So my question is if there is an alternative for sleep under zsh that works in a sub-shell. Or is there a better solution than using the busy loop with this zsh/curses code.

The code:

#!/bin/zsh

zmodload zsh/curses

loop ()
{
    monitorwindowsize $1 &
    view
    while true; do
        read -t 1000
    done
}

view ()
{
    zcurses addwin mywin $(( LINES-4 )) $(( COLUMNS-4 )) 2 2 
    zcurses clear mywin
    zcurses border mywin
    zcurses refresh mywin
    zcurses move mywin $(( (LINES-4)/2 )) $(( (COLUMNS-8)/2 ))
    zcurses string mywin "Box"
    zcurses refresh mywin
}

resetview ()
{
    zcurses end
    tput clear
    zcurses clear mywin
    zcurses refresh mywin
    zcurses delwin mywin
    view
}

monitorwindowsize ()
{
    currentLines=$LINES
    currentColumns=$COLUMNS
    while true; do
      if (( currentLines != LINES || currentColumns != COLUMNS )); then
        currentLines=$LINES
        currentColumns=$COLUMNS
        kill -SIGUSR1 $1
      fi
      sleep 0.1
    done
}

TRAPUSR1() {
    resetview
}

TRAPINT() {
    for jobs in $jobstates ; do
      jobid=${${jobs##*:*:}%=*}
      kill ${${jobs##*:*:}%=*}
    done
    zcurses end
    exit
}

zcurses init
loop $$
for jobs in $jobstates ; do
  jobid=${${jobs##*:*:}%=*}
  kill ${${jobs##*:*:}%=*}
done
zcurses end

The view:

enter image description here

After resizing the window:

enter image description here

2

Answers


  1. Chosen as BEST ANSWER

    I have now found a way to make the SIGWINCH trap work:

    #!/bin/zsh
    
    zmodload zsh/curses
    
    loop ()
    {
        view
        while true; do
            unset raw
            unset key
            zcurses timeout mywin 50
            zcurses input mywin raw key
            now=$(date)
        done
    }
    
    view ()
    {
        zcurses addwin mywin $(( LINES-4 )) $(( COLUMNS-4 )) 2 2 
        zcurses clear mywin
        zcurses border mywin
        zcurses refresh mywin
        zcurses move mywin $(( (LINES-4)/2 )) $(( (COLUMNS-8)/2 ))
        zcurses string mywin "Box"
        zcurses refresh mywin
    }
    
    resetview ()
    {
        zcurses end
        tput clear
        zcurses clear mywin
        zcurses refresh mywin
        zcurses delwin mywin
        view
    }
    
    TRAPWINCH() {
        resetview
    }
    
    TRAPINT() {
        for jobs in $jobstates ; do
          jobid=${${jobs##*:*:}%=*}
          kill ${${jobs##*:*:}%=*}
        done
        zcurses end
        exit
    }
    
    zcurses init
    loop $$
    for jobs in $jobstates ; do
      jobid=${${jobs##*:*:}%=*}
      kill ${${jobs##*:*:}%=*}
    done
    zcurses end
    

    I now use zcurses input instead of read. But the strange thing is that it also depends on now=$(date) (maybe another command works too). If now=$(date) is missing, the SIGWINCH trap will not work and the window will not be redrawn.


  2. You may be able to use zselect. From the zshmodules man page:

    It is possible to call zselect with no file descriptors and a non-zero
    timeout for use as a finer-grained replacement for ‘sleep’; note,
    however, the return status is always 1 for a timeout.

    The call to zselect here does appear to sleep for half a second:

    > zmodload zsh/zselect
    > type zselect
    zselect is a shell builtin
    > zselect -t 50
    >
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search