zsh-users
 help / color / mirror / code / Atom feed
From: Philippe Troin <phil@fifi.org>
To: "Marc Cornellà" <hello@mcornella.com>, zsh-users@zsh.org
Subject: Re: Detect typed input without swallowing characters
Date: Thu, 10 Feb 2022 16:04:41 -0800	[thread overview]
Message-ID: <24180172015e6c9c3bab278c840eabcaa1a3249f.camel@fifi.org> (raw)
In-Reply-To: <CACn48Nq5hW0u9PhrTPSGOZjvohArKpVoXrGsryEM+jQpwfS-zg@mail.gmail.com>

On Thu, 2022-02-10 at 22:21 +0100, Marc Cornellà wrote:
> in Oh My Zsh we want to detect whether the user has typed characters
> before showing the prompt to auto-update, so that if they have, to
> skip it entirely and get them to the prompt as fast as possible.
> 
> The current solution is to detect whether there's input with `read -t
> -k 1`, but this solution has the downside of swallowing one
> character.
> 
> I've been looking at sysread and zselect to poll stdin (fd 0), but I
> believe they only tell whether stdin is ready for reading, not
> whether
> it holds any data. These are the commands I'm using by the way:
> 
>     sysread -t 0 -i 0
>     zselect -t 0 -r 0
> 
> Is there any way to do this or should I give up entirely?

You can use zselect, but you have to disable canonical mode with stty,
otherwise characters are not counted unless you press enter (the
terminal is set to read a whole line at once).

This function should work:

   type_ahead() {
     emulate -L zsh
     local termios=$(stty --save)
     stty -icanon
     zselect -t 0 -r 0
     local ret=$?
     stty $termios
     return ret
   }

There may be subtleties involved with the STTY variable as well as
ttyctl, check out the zsh documentation.  I only did minimal testing:

   sleep 5; if type_ahead; then echo TYPED AHEAD; fi

works as it should.

You can also get the number of bytes in the read buffer with the
FIONREAD ioctl() (man ioctl_tty for details).
This long python 1-liner does what you want:

   python -c 'import array, fcntl, termios; nbuffered=array.array("i", [0]); old=termios.tcgetattr(0); new=old[:]; new[3] &= ~termios.ICANON; termios.tcsetattr(0, termios.TCSANOW, new); fcntl.ioctl(0, termios.FIONREAD, nbuffered); termios.tcsetattr(0, termios.TCSANOW, old); print(nbuffered[0])'

Same script in a more readable form:

   #!/usr/bin/env python3
   
   import array, fcntl, termios
   
   nbuffered=array.array("i", [0])
   
   old=termios.tcgetattr(0)
   new=old[:]                # Array copy
   new[3] &= ~termios.ICANON # Disable canonical mode
   try: # try/finally cannot appear in the 1-liner above
     termios.tcsetattr(0, termios.TCSANOW, new)
     fcntl.ioctl(0, termios.FIONREAD, nbuffered)
   finally:
     termios.tcsetattr(0, termios.TCSANOW, old)
   print(nbuffered[0])

I doubt this would be useful for your use case, as firing up the whole
python interpreter to perform FIONREAD is likely to be slower than the
optimization gain you're trying to measure.

Phil.



  reply	other threads:[~2022-02-11  0:05 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-02-10 21:21 Marc Cornellà
2022-02-11  0:04 ` Philippe Troin [this message]
2022-02-11  9:08   ` Marc Cornellà
2022-02-11 16:15     ` Philippe Troin
2022-02-11 19:08     ` Bart Schaefer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=24180172015e6c9c3bab278c840eabcaa1a3249f.camel@fifi.org \
    --to=phil@fifi.org \
    --cc=hello@mcornella.com \
    --cc=zsh-users@zsh.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).