Skip to content
Tom Feist edited this page Oct 24, 2011 · 6 revisions

Irssi Keyboard Input

This file is concerned only with the fe-text version of irssi (that is, the standard tty client), and investigates the inner workings of the binding, dispatch and key handling code.

Exciting Fun Discoveries

YMMV.

Calling /bind functions from scripts

If you register a signal of the form key <key_name> where <key_name> is one of the functions listed with /bind -list, you can trigger that action by subsequent emission of the signal.

For example:

/bind meta-q /se signal_register({'key backward_history' => \
[qw/string string int/]}); signal_emit('key backward_history', '', 0, 0)

In practice, the signal only needs to be registered once (is persistent across a whole irssi process instance, and survives even /script reset)

Open Questions

  • Is it possible to listen for signals for complex key events by binding a key sequence to key <foo>, and then observing the key <foo> signal? Apparently not, so far.

Interestingly, /eval /bind meta-q key bacon; /bind bacon /echo bacon pressed appears to work - indicating that arbitrary key names can be used.

  • Using "key nothing" as a hook seems to work. Registered with a single string argument:
Irssi::signal_register({'key nothing' => [qw/string/]});
Irssi::signal_add_first('key nothing' => \\&sig_key_nothing);
Irssi::command("bind meta-q nothing SOMESTRING");

sub sig_key_nothing {
    my ($data) = @_;
    print "key nothing: " . Dumper($data);

}

Not perfect (still have to observe every "key nothing", rather than a single "key mykey", but definitely progress.

Details

Main files:

Associated Signals:

  • "keyboard created"
  • "keyboard destroyed"
  • "key created"
  • "key destroyed"
  • "keyinfo created"
  • "keyinfo destroyed"
  • "key <keyname>"
  • "gui key pressed"

Data Structures:

  • KEYBOARD_REC

    char *key_state; /* the ongoing key combo */
    void *gui_data; /* GUI specific data sent in "key pressed" signal */

  • KEYINFO_REC

    char *id;
    char *description;
    GSList *keys, *default_keys;

  • KEY_REC

    KEYINFO_REC *info;
    char *key;
    char *data;

High Level

The following is a high-level description of the keyboard and binding system. The GLib event-loop that irssi operates around detects keystroke input. Each key triggers a gui key pressed signal, which is then further

Setup

Keyboard Creation

gui-readline is responsible for creating and owning the KEYBOARD_REC instance. The keyboard instance is created in gui_readline_init(), setting rec->gui_data to NULL. rec->key_state isn't initialised.

Only a single keyboard instance is ever created. The creation causes the emission of a "keyboard created" signal, but this occurs before the perl script interpreter is loaded, and hence cannot be used in scripts.

Once the keyboard object is created, is it populated with the default bindings, still within the gui_readline_init() function. A simple lock-style function-pair (key_configure_freeze(), key_configure_thaw()) allows the manipulation of large numbers of key entries at once without running the costly key_states_rescan() function each time.

Binding Defaults

void key_bind(const char *id, const char *description, const char *key_default, const char *data, SIGNAL_FUNC func)

is the entry-point for setting up both bindings, and the actions which bindings perform. An example of each:

key_bind("key", NULL, " ", "space", (SIGNAL_FUNC) key_combo);
key_bind("backward_character", "Move the cursor a character backward", "left", NULL, (SIGNAL_FUNC) key_backward_character);

There seem to be a few things a key can be bound to:

  • command -- fires a "key command" signal when the bound key-seq is pressed.
  • key -- alias one key to another. Seems to operate below the signal dispatch level.
  • multi -- allow multiple actions, separated by ;.
  • nothing -- Do...Nothing.

Dispatching Keypresses

sig_gui_key_pressed is the entry point to the key dispatcher, as a low-level signal handler listening for "gui key pressed" signals. It checks if keyboard entry is being redirected (for example, the password entry mode of /oper). If so, throws it off to handle_key_redirect, which we'll ignore for now.

Otherwise, it checks the value of the pressed key, and does various things depending on what it is. The most interesting Otherwise, if it's a control key, it

Lower level

things.

Clone this wiki locally