LekKit / RVVM

The RISC-V Virtual Machine
GNU General Public License v3.0
865 stars 60 forks source link

Virtual Terminal as UART backend #103

Open ghost opened 1 year ago

ghost commented 1 year ago

Hello I'd like to add VT support to RVVM.

Exact requirements

I think it should:

Suggested solution

My current plan are:

Expected results and side effects

The showcase can be TERM=xterm-mono emacs -nw running in OpenBSD editing UTF-8 text, via RetroArch...

How does this sound?

LekKit commented 1 year ago

Very good idea, some more things I'd love to add:

ghost commented 1 year ago

Very good idea, some more things I'd love to add:

  • Intermediate API to look at VT contents as a character array with color attributes. I need this for one of my side projects, and it also can bring proper VT state saving and VT support on older Windows hosts.

libvterm has those:

typedef struct {
  uint32_t chars[VTERM_MAX_CHARS_PER_CELL];
  char     width;
  VTermScreenCellAttrs attrs;
  VTermColor fg, bg;
} VTermScreenCell;

size_t vterm_screen_get_chars(const VTermScreen *screen, uint32_t *chars, size_t len, const VTermRect rect);
size_t vterm_screen_get_text(const VTermScreen *screen, char *str, size_t len, const VTermRect rect);
int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCell *cell);

which seems fine.

Now I'm trying to implement "input takeover" by hook hid_keyboard with:

+PUBLIC void hid_keyboard_hook(hid_keyboard_t* kb,
+                              bool (*on_press)(hid_key_t key),
+                              bool (*on_release)(hid_key_t key))
+{
+    kb->on_press = on_press;
+    kb->on_release = on_release;
+}
+
 PUBLIC void hid_keyboard_press(hid_keyboard_t* kb, hid_key_t key)
 {
     bool is_input_avail = false;
+    if (kb->on_press && kb->on_press(key))
+        return;
     spin_lock(&kb->lock);
     // key is guaranteed to be 1 byte according to HID spec
     if (key != HID_KEY_NONE) {
@@ -188,6 +204,8 @@ PUBLIC void hid_keyboard_press(hid_keyboard_t* kb, hid_key_t key)
 PUBLIC void hid_keyboard_release(hid_keyboard_t* kb, hid_key_t key)
 {
     bool is_input_avail = false;
+    if (kb->on_release && kb->on_release(key))
+        return;
     spin_lock(&kb->lock);
     if (key != HID_KEY_NONE) {
         kb->keys[key/32] &= ~(1U << (key%32));

So in fb_window (instead of in sdl_window, etc.) I can handle input with:

+static bool keyboard_press(hid_key_t key)
+{
+    rvvm_info("key pressed: %d", key);
+    return false; // !! return true to stop propagate event to the guest
+}
+
+static bool keyboard_release(hid_key_t key)
+{
+    rvvm_info("key released: %d", key);
+    return false;
+}
+
 bool fb_window_init_auto(rvvm_machine_t* machine, uint32_t width, uint32_t height)
 {
     fb_window_t* window = safe_calloc(sizeof(fb_window_t), 1);
@@ -125,15 +137,17 @@ bool fb_window_init_auto(rvvm_machine_t* machine, uint32_t width, uint32_t heigh
     window->machine = machine;
     window->keyboard = hid_keyboard_init_auto(machine);
     window->mouse = hid_mouse_init_auto(machine);
+    hid_keyboard_hook(window->keyboard, keyboard_press, keyboard_release);

Does this look reasonable? :thinking:

LekKit commented 1 year ago

I think we shouldn't pollute the hid_api itself, or the actual devices (reinventing something inside the device would be bad). What I think should be done is more refining for fb_window, it is purposed as a middle-ground between raw machine APIs (framebuffer, hid_api) and native windowing backends (SDL, X11, Win32, Haiku). Perhaps you could implement something like gui_keypress() with same semantics as the hid call, and then translate it to whatever you like.

ghost commented 1 year ago

Okay, that gui_keypress seems more clearly to me too. Haven't got much time to works on this yet, hope to pick this up later...