termui detect keyboard / mouse keyup event
Describe the feature
termios should detect a event of type keyup from keyboard devices.
One reason I think is that the terminal emulator is simply not raising those events.
Use Case
if a key is pressed the user expects that the keystroke should repeat itself after a short pause with a frequency greater than the short pause.
Please note that gg module raises the keyup event.
Uses cases:
- terminal text editors[1]
- terminal games[1]
- terminal editors[2]
[1] ( where key repeat or multiple key presses could be required ) [2] where arrow keys could be used to move visual stuff around.
Proposed Solution
One first step would be to actually introduce the event type key_up
pub enum EventType {
...
key_up
}
since the windows and unix implementations are quite different I assume that they could even be developed at different stages.
consider this as a first inquiry about the pertinence of introducing this feature in the standard library, its' cross-platform feasibility.
Other Information
Solution I currently employ.
notice the usage of that the struct input_event from input/input_event.h
the device is read raw from fopen("/dev/event*") and not from C.STDIN_FILENO.
#include <linux/input.h>
#include <linux/input-event-codes.h>
typedef struct Keyboard {
struct termios oldt;
struct termios newt;
// maps key_code to corresponding char
// if a key is not pressed the value of the corresponding key_code is 0
// if a key is pressed the value of the corresponding key_code is the corresponding char
char key_state[KEY_MAX];
// fd of the keyboard device
int device;
struct input_event ev;
} Keyboard;
// it reads the input device synchronously and sets or unsets the
// element that corresponds to the read event key code in the key_state key map
int read_events(Keyboard *self) {
ssize_t bytesRead = read(self->device, &(self->ev), sizeof(struct input_event));
if (bytesRead == -1) {
// perror("Error reading input event");
} else if (bytesRead == sizeof(struct input_event)) {
if (self->ev.type == EV_KEY) {
// when ev.value == 0 the key was up
if (self->ev.value == 0) {
self->key_state[self->ev.code] = 0;
} else
// when ev.value == 1 the key was down ( pressed )
if (self->ev.value == 1) {
self->key_state[self->ev.code] = map_keycode_to_char(self->ev.code);
}
}
}
return 0;
}
Acknowledgements
- [X] I may be able to implement this feature request
- [X] This feature might incur a breaking change
Version used
V 0.4.3 5b9d0f2
Environment details (OS name and version, etc.)
Linux 6.2.0-37-generic #38~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC x86_64 GNU/Linux
Distributor ID: Ubuntu Description: Ubuntu 22.04.3 LTS Release: 22.04 Codename: jammy
TERM=xterm-256color gnome-terminal-server (wayland)
I assume that on Windows the behaviour is the same
example of how it should behave:
- pressing multiple keys shows all of them as pressed ( active )
import gg
struct App{
pub mut:
current gg.Event
buffer map[string]string
}
mut a:= &App{}
gg.start(
window_title: 'Hello'
bg_color: gg.Color{240, 240, 128, 255}
width: 320
height: 240
event_fn: fn [mut a](e &gg.Event, data voidptr) {
a.current=e
cb := e.key_code.str()
cc := e.char_code
cd := if cb == 'invalid' { [u8(cc & 0xFF)].bytestr() } else { cb }
if e.typ == .char {
a.buffer[[u8(cc & 0xFF)].bytestr()]=[u8(cc & 0xFF)].bytestr()
} else if e.typ == .key_up {
a.buffer[cb]="-"
}
}
frame_fn: fn [a](mut ctx &gg.Context) {
ctx.begin()
ctx.draw_text(40, 100, 'GG frame: ${ctx.frame:06} ${a.buffer}',
size: 30
color: gg.Color{50, 50, 255, 255}
)
ctx.show_fps()
ctx.end()
}
)