readkey function available in J?

25 views
Skip to first unread message

Jorrit Kirsten

unread,
Jun 6, 2026, 9:23:43 AM (11 days ago) Jun 6
to forum
Hi everyone,

I need a verb which prompts an input key or key combination without the user having to press Enter afterwards in the jconsole. It seems that this does not exist in J proper. One could use the ncurses library, but for me there are several reasons not to which I don't want to go into here. On the other hand writing a TUI application e.g. in C or Fortran and calling J from there would be possible but cumbersome for simpler input tasks.

I have a solution (after many failed iterations with "Claude Sonnet 4.6 low" and some modification), but this solution uses tty and thus only works on Unix systems.  Furthermore it requires the storage of the input characters in a file and subsequently reading this file back into a J variable.
One could store the file containing the character directly into memory and read it back in from there, but this causes permission denied issues in my editor (Vim with split terminal window).

I would greatly appreciate if in a future version of J this feature could be added which would work on all operating systems. Ideally I would very much like to have several options for entering keys ("silent" (e.g. for ignoring invalid choice options), verbose (WYSIWYG), obfuscated (***... for sesitive data)).

An available readkey verb would open the possibility for writing not only simple but also more complex TUI applications. A fast interpreted language like J without garbage collection would be ideal for such a task!

Best regards
Jorrit

----- Here is my readkey solution for Unix-Systems using tty and the shell verb, which is, of course, not thread safe:


NB. This works:
NB.----- readkey with storing key on disk:
readkey =: verb define
  NB. read one byte
  2!:0 'stty cbreak 2>/dev/null; dd bs=1 count=1 </dev/tty >/tmp/jkey 2>/dev/null; stty -cbreak 2>/dev/null'
  b0 =. 3 u: 1!:1 < '/tmp/jkey'

  NB. determine expected UTF-8 byte count from first byte
  NB. 0xxxxxxx = 1 byte  (0-127)
  NB. 110xxxxx = 2 bytes (192-223)
  NB. 1110xxxx = 3 bytes (224-239)
  NB. 11110xxx = 4 bytes (240-247)
  n =. 1 + (b0 >: 192) + (b0 >: 224) + (b0 >: 240)

  NB. read remaining bytes if needed
  if. n > 1 do.
    cmd =. 'stty cbreak 2>/dev/null; dd bs=1 count=' , (": n-1) , ' </dev/tty >/tmp/jkey2 2>/dev/null; stty -cbreak 2>/dev/null'
    2!:0 cmd
    rest =. 3 u: 1!:1 < '/tmp/jkey2'
    bytes =. b0 , rest
  else.
    bytes =. , b0
  end.

  NB. smoutput ''       NB. add a line feed after the input character (if desired)
  key =. ucp bytes { a.
  key

  NB. decode UTF-8 bytes to a single unicode code character:
  NB. ucp bytes { a.
  NB. bytes { a.  NB. also possible, but then 1<# bytes { a. is possible
  NB. bytes       NB. only integer values

)

NB.----- test readkey:
NB.-- single character:
key =. readkey '' NB. Now press any key without pressing Enter! Unicode characters are possible! Try e.g. € !
key
#key
NB.-- three characters without pressing <Enter>:
input_3_chars_long =. (],readkey)^:3 ''  NB. Enter e.g. 'ABC' or 'ÄÖÜ' without pressing the <Enter>-key.
input_3_chars_long

   key =. readkey '' NB. Now press and key without pressing Enter:
€   key    NB. '€' at index 0 in this line was my input character without pressing enter, key + <Enter> was my second input
€          NB. This is the displayed value of €
   #key
1          NB. = unicode point size of key value corresponding to 3 bytes

NB.------ remove your temporary files if desired:
shell 'rm -f /tmp/jkey'
shell 'rm -f /tmp/jkey2'

Viktor Grigorov

unread,
Jun 6, 2026, 10:28:41 AM (11 days ago) Jun 6
to J Mailing List J
Hello, Jorrit and all, 

I agree with You wholely and have in the past wanted to use J for various tasks which I could do in dash, bash, or zsh. The latter has shell built-in read, with options -r -e -k1 it reads any pressed single key into stdout (2. opt). Unfortunately, J has no scope of locality options that I know of. Sometimes these are required.

Jun 6, 2026, 16:23 by jorrit....@gmail.com:
> To unsubscribe from this group and stop receiving emails from it, send an email to > forum+un...@jsoftware.com> .
>

bill lam

unread,
Jun 6, 2026, 11:10:35 AM (11 days ago) Jun 6
to fo...@jsoftware.com
Can it be done by adding a new J primitive 1!:x that has the same
functionality of getchar() in c ?

Jorrit Kirsten

unread,
Jun 6, 2026, 1:00:38 PM (11 days ago) Jun 6
to forum, bill lam
Thank you, Viktor and Bill Lam for your comments and your support.
Ideally, besides a verb behaving like C's getch() for reading byte-size characters (J's atomic literal type) another one behaving like C's wgetch() for unicode characters (1-4 bytes, J's atomic unicode type) would be great.
(1!:x) 0 for the first and (1!:x) 1 for the second (or whatever convenient type representation integers) would be perfect from my point of view.

Viktor Grigorov

unread,
Jun 6, 2026, 2:39:33 PM (11 days ago) Jun 6
to J Mailing List J
The option for a new primitive would serve fine for those who install the newest version of the language. Additionally, I would (to me) improve (to me) Your suggestion by setting LC_ALL=C in whatever shell the OS uses to get an array of (Unicode) unsigned integers converted from hexadecimal to decimal. Possibly for the undesiring to use their shell 2 additional primitives for converting strings and lists of unsigned integers into one another. Though that might be a tad too string manipulatey. In zsh they are 2 short functions using only built-ins from the shell.

Jun 6, 2026, 20:00 by jorrit....@gmail.com:

bill lam

unread,
Jun 6, 2026, 8:08:11 PM (11 days ago) Jun 6
to fo...@jsoftware.com
I think getch is available only on windows.

Raul Miller

unread,
Jun 6, 2026, 11:05:30 PM (11 days ago) Jun 6
to fo...@jsoftware.com
There's a variety of issues to think about here. And, a fair bit of it is OS dependent.  That said, for the unix console interface:

1) There's read exactly one character (waiting for the next character to appear, if necessary - blocking read), and there's reading up to one character (reading an empty string if no character is yet available - nonblocking read). There's also the possibility of a "one time" non-blocking read, where after a single empty read the next attempt blocks (I think I have seen this occur in some cases on linux, though I do not remember the exact details for obtaining this behavior).

2) On unix/linux, input is buffered at the c library level, but non-blocking reads can only happen at the kernel level. If non-blocking reads are supported, it would be important (for consistency) to drain that buffer before performing any non-blocking reads.

3) the default behavior for reading from the keyboard is buffered - line-at-a-time. If you want character-at-a-time unbuffered input, I am pretty sure that you need to use the kernel's read() interface. It might also be important to use fcntl() to put the input stream into the right mode.



By the way, there's two different buffering modes for fread() - for the keyboard, the default behavior is line-at-a-time (for files, it's block-at-a-time). To empty the buffer you could read a character at a time until a newline character is read, probably using getchar(). (It would be nice if getchar() could be used in an unbuffered mode, but there's no portable way of doing that.) I do not know if there's a portable way of draining the input buffer when it's in a block-at-a-time mode.


--------------------------------------------------------------------

Anyways, it's going to be important to define the scope of this mechanism and what will happen when line-at-a-time reads are mixed with character-at-a-time reads.

--------------------------------------------------------------------

I imagine qt has some similarities, but I also imagine that it's more about event handlers than an input stream. (And, JHS would have a similar architecture.)

I hope some of this is relevant,

-- 
Raul

Viktor Grigorov

unread,
Jun 7, 2026, 1:01:45 AM (11 days ago) Jun 7
to J Mailing List J
There is an ANSI shell escape sequence for calling the alternate buffer, which is clear or empty---'\033[?1049' and ending with 'h' to enable it or 'l' to disable it. I do not know whether Window developers use any of ANSI, but it might be worth attempting it in the shell. The octal sequence is for the escape key.

Trying likewise at the kernel level will produce headaches, I presume.

Jun 7, 2026, 06:05 by rauld...@gmail.com:
>>> >> > To unsubscribe from this group and stop receiving emails from it, send an email to >> >>> forum+un...@jsoftware.com <mailto:forum%2Bun...@jsoftware.com>>>> <>>> .
>>> >>
>>> >
>>> >
>>> >
>>> > To unsubscribe from this group and stop receiving emails from it, send an email to > >>> forum+un...@jsoftware.com <mailto:forum%2Bunsu...@jsoftware.com>>>> > .
>>> >
>>>
>>> To unsubscribe from this group and stop receiving emails from it, send an email to >>> forum+un...@jsoftware.com <mailto:forum%2Bunsu...@jsoftware.com>>>> .

Michal Wallace

unread,
Jun 7, 2026, 8:55:58 AM (10 days ago) Jun 7
to fo...@jsoftware.com

To unsubscribe from this group and stop receiving emails from it, send an email to forum+un...@jsoftware.com.

Jorrit Kirsten

unread,
Jun 7, 2026, 11:46:28 AM (10 days ago) Jun 7
to forum, Michal Wallace
Thank you, Michal Wallace! Tagentstorm has implemented a readkey function in a crossplatfrom style. See


and here lines 156 (Windows) and 168 (Unix using unxlib 'c' via J's standard library). For the verbs unxlib and cd see


I will test this next weekend.
This will likely answer my question, but still, such a general readkey verb in J's standard library and z locale would be very convenient!

I very much appreciate everyone's contribution and help!
Jorrit
 
Reply all
Reply to author
Forward
0 new messages