owl icon indicating copy to clipboard operation
owl copied to clipboard

Portable getch?

Open andyl opened this issue 3 years ago • 1 comments

I’ve been unable to find a simple & portable getch implementation for Elixir. The use case is to detect single keypress events, without needing the return key.

Comparable tools like inquirer/npm and tty/ruby use getch to build features like type-ahead, pull-down menus, etc.

To implement getch I've looked at ex_ncurses, rustler, System.cmd, Ports, etc. This is the best I’ve come up with so far:

#!/usr/bin/env elixir

ruby_cmd = ~S"""
  ruby -e '
    require "io/console"
    @output = IO.new(4)
    @output.sync = true
    char = STDIN.getch
    @output.write(char)
  '
"""

IO.write("Press a key > ")
port = Port.open({:spawn, ruby_cmd}, [:binary, :nouse_stdio]) 
receive do
  {^port, {:data, result}} -> IO.puts("\nChar: #{result}")
end

This works on my Linux dev machine, but depends on Ruby.

Does anyone know of a portable getch implementation? Maybe with Rustler or precompiled binaries or ???

andyl avatar Dec 16 '21 16:12 andyl

I found this crate https://crates.io/crates/getch it is quite small, we can play with it and Rustler and if it works okay, then implement as a separate package

fuelen avatar Dec 16 '21 22:12 fuelen

@fuelen @andyl I tried using Rustler + getch but couldn't get it to work. I think it is due to BEAM blocking the stdin port (here).

So I came with a dirty solution that closes the stdin port opened by the user driver and re-opens it on a process I can control: https://gist.github.com/antedeguemon/565d65718a4625dcec2d83d51614a9c5

It works well but it has the big downside of overriding the IO port, meaning regular IO operations won't work. Here is an asciicast of the script:

asciicast

antedeguemon avatar Oct 11 '22 15:10 antedeguemon

Hey @antedeguemon

Cool, that's interesting. I went to Google to find what is tty_sl and quickly found this repo https://github.com/swalkerhppr/elixir-io_tty :) Seems like manual stop of the port can be replaced with -noinput flag. @andyl have you seen the repo?

fuelen avatar Oct 11 '22 19:10 fuelen

@antedeguemon thanks for posting your code I was able to run it locally. Studying.

@fuelen - have not seen elixir-io_tty before - it looks interesting!

andyl avatar Oct 11 '22 19:10 andyl

I had another look - seems like tty_sl is an Erlang module that makes it possible to read character-by-character, and is used in IEx.CLI.start. I am unable to find any docs on tty_sl. Anyone have a link for this?

andyl avatar Nov 17 '22 06:11 andyl

Anyone have a link for this?

Unfortunately no

fuelen avatar Dec 07 '22 09:12 fuelen

Here's a thread with some good resources:

https://groups.google.com/g/elixir-lang-talk/c/WtzbZcAk6XQ

andyl avatar Dec 07 '22 16:12 andyl