This is the code in OCaml to detect keys using the Unix module and raw mode in the linux terminal.
The only key I can't detect is ESC. There could be others, but my main goal is to detect ESC key.
Here is the code of the function:
(* Function to read a character and recognize special keys *)
let read_character () =
let in_chan = in_channel_of_descr stdin in
let c = input_char in_chan in
if c = '\027' then
let next1 = input_char in_chan in
let next2 = input_char in_chan in
match (next1, next2) with
| ('[', 'A') -> "Up Arrow"
| ('[', 'B') -> "Down Arrow"
| ('[', 'C') -> "Right Arrow"
| ('[', 'D') -> "Left Arrow"
| ('[', 'H') -> "Home"
| ('[', 'F') -> "End"
| ('[', '3') when input_char in_chan = '~' -> "Delete"
| ('[', '2') when input_char in_chan = '~' -> "Insert"
| ('[', '5') when input_char in_chan = '~' -> "Page Up"
| ('[', '6') when input_char in_chan = '~' -> "Page Down"
| _ -> "Ignore"
else
match c with
| '\n' -> "Enter" (* Handle Enter key *)
| '\127' -> "Backspace" (* Handle Backspace (DEL) *)
| _ -> String.make 1 c (* Return single character *)
Please, take into account that C/curses works, but it is very slow to initialize/finish and it clears the screen, so it is not an optimal option.
It is kind of a challenge. AI suggested me using Lwt/Async, but none of that worked, since I didn't have success installing that in Arch Linux.
Fortunately there is a non-canonical mode with timeout which allows this. The complete source code is this:
open Unix
(* Function to set the terminal to raw mode with no echo and a short timeout *)
let set_raw_mode () =
let term = tcgetattr stdin in
let raw_term = { term with c_icanon = false; c_echo = false } in
raw_term.c_vmin <- 1;
raw_term.c_vtime <- 1; (* Timeout of 0.1 seconds *)
tcsetattr stdin TCSANOW raw_term
(* Function to restore the original terminal settings *)
let restore_terminal old_term =
tcsetattr stdin TCSANOW old_term
(* Function to read keys and recognize special keys *)
let read_keys () =
let buffer = Bytes.create 6 in
let len = Unix.read Unix.stdin buffer 0 6 in
let raw_input = Bytes.sub_string buffer 0 len in
match raw_input with
| "\027[A" -> "Up Arrow"
| "\027[B" -> "Down Arrow"
| "\027[C" -> "Right Arrow"
| "\027[D" -> "Left Arrow"
| "\027[H" -> "Home"
| "\027[F" -> "End"
| "\027[3~" -> "Delete"
| "\027[2~" -> "Insert"
| "\027[5~" -> "Page Up"
| "\027[6~" -> "Page Down"
| "\027" -> "Esc"
| _ ->
if len = 1 && Bytes.get buffer 0 = '\n' then "Enter"
else if len = 1 && Bytes.get buffer 0 = '\127' then "Backspace"
else raw_input
let () =
let old_term = tcgetattr stdin in
try
set_raw_mode ();
let rec loop () =
let key = read_keys () in
print_endline ("Key pressed: " ^ key);
if key <> "Esc" then loop ()
in
loop ();
restore_terminal old_term
with e ->
restore_terminal old_term;
raise e