First things first, I made sure that the keyboard is always in view by using some nice existing functionality of xrdesktop and grouping all keyboard buttons into one G3kContainer and attaching it to the user’s head:

Event Handling

In the past few days of working on the virtual keyboard for xrdesktop, I’ve been trying to make the keyboard behave like a keyboard. That is, it should notice when one of its keys is clicked by the user, and it should generate the proper key event when this happens. This is the result:

In my previous blog post, you could already see the keys react to clicks, but this is not enough for a fully functioning keyboard system. For one, you don’t want to handle the events for each button separately; you would have to connect more than 100 events for a standard keyboard! You just want to connect a single key-press-event to your active window, which would contain all the information of the pressed key.

The second reason for handling the click events inside the keyboard, is that we need to keep track of the modifier keys (Shift, Ctrl, Alt, things like that). The keyboard should maintain the state of these keys (on or off) internally, so the key-press-event would send the key capitalized when Shift is active, or ‘Ctrl + X’ when Ctrl is active, etc.

So how is this done technically? We need to register a key-press-event signal for G3kKeyboard, which we can then emit at the right time. The user of the keyboard will write an event callback, and will connect the signal to it.

What is the right time to emit the key-press-event? Well, when a button has been clicked. So, we need to connect the button’s grab-start-event to a callback that emits the key-press-event.

You might be familiar with event handling in other languages, but in GLib style this is a fairly verbose affair. Once I understand the GObject system a bit better, I will write that part 2 of ‘Getting Started with GLib’ with a short tutorial on how to create objects with it. Find the raw event handling code below:

g3k_keyboard.c:

static void
g3k_keyboard_class_init(G3kKeyboardClass *klass)
{
  keyboard_signals[KEY_PRESS_EVENT] =
    g_signal_new ("key-press-event",
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_LAST,
                  0, NULL, NULL, NULL, G_TYPE_NONE,
                  1, G_TYPE_POINTER | G_SIGNAL_TYPE_STATIC_SCOPE);
}

void
g3k_keyboard_click_cb (XrdWindow     *window,
                       GxrController *controller,
                       gpointer       user_data)
{
  G3kKeyboard *keyboard = G3K_KEYBOARD (user_data);

  g_autofree gchar *title = NULL;
  g_object_get (window, "title", &title, NULL);

  G3kKeyEvent event = { .key = title[0] };

  g_signal_emit (keyboard, keyboard_signals[KEY_PRESS_EVENT], 0, &event);
}

Example usage:

void
keyboard_pressed_cb(G3kKeyboard *keyboard,
                    G3kKeyEvent *event,
                    gpointer     user_data)
{
  g_print ("Yay, key %c was pressed!\n", event->key);
}

static void
_init_keyboard(XrdShell *shell)
{
  G3kContext *context = xrd_shell_get_g3k (shell);
  G3kKeyboard *keyboard = g3k_keyboard_new (context);

  G3kContainer *container = g3k_keyboard_get_container(keyboard);
  GSList *buttons = g3k_container_get_windows (container);

  for (GSList *button = buttons; button != NULL; button = button->next)
    {
      g_signal_connect (button->data, "grab-start-event",
                        (GCallback) g3k_keyboard_click_cb, keyboard);
    }

  g_signal_connect (keyboard, "key-press-event",
                    G_CALLBACK (keyboard_pressed_cb), NULL);
}

That’s it for now. Up next, let’s start on the Swype prediction mode! For this, we need to show the path that the controller traced over the keyboard’s keys and we need to calculate the word that was most likely meant to be typed.