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
I have now found a way to make the SIGWINCH trap work. Tested with zsh 5.8.1 (x86_64-ubuntu-linux-gnu)
(with ssh) and zsh 5.9 (x86_64-apple-darwin22.0)
:
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() {
zcurses end
exit
}
echoti civis
zcurses init
loop
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.
So if now=$(date)
is missing, it looks like this: