Internals - shabble/irssi-docs GitHub Wiki

Some Notes on Irssi Internals.

home |

Code Organisation

Design Hierarchy

sub1 sub2
   \ /
       xxx  IRC       COMMON ICQ  yyy
        |____|___________|____|____|
          |
         GUI (gtk/gnome, qt/kde, text, none)
          |
sub1 sub2 |
   \ /    |
       xxx  IRC    |  COMMON ICQ  yyy
        |____|_____|_____|____|____|
          |
      COMMON UI
          |
sub1 sub2 |
   \ /    |
       xxx  IRC    |    ICQ  yyy
        |____|_____|_____|____|
          |
        CORE
        /
        lib-config

    (IRC, ICQ, xxx and yyy are chat protocols ...)
    (sub1 and sub2 are submodules of IRC module,
     like DCC and flood protect)

See http://irssi.org/documentation/designimg/external_link.png for additional details

Filesystem Layout

Irssi is designed to be somewhat modular, so that different frontends can be attached to the IRC client bits for people who like shiney windows, and suchlike.

The code is organised into core, the really really essential bits, irc -- the bit that deals with talking to IRC servers, lib-config which handles the config file format, and perl, which implements the scripting functionality.

The other modules (cunningly prefixed with fe-*) contain code specific to various frontends, with fe-common looking after the shared code.

The rest of this document will probably only consider the fe-text frontend, because it's the most useful, and most widely used.

Mainloop

The fe-text/irssi.c contains the program entry-point, which consists of calling *_init() on just about every other module, parsing some commandline options, and then starting up the mainloop.

Irssi is built on GLib, and runs in an event-driven sort of way, using the GLib loop to handle IO Polling and timers.

MainLoop Docs The main-loop locks the terminal with a simple semaphore-like construct, executes one iteration of the GLib event loop, and then unlocks the terminal.

A SIGHUP handler is installed to re-read the config file, which is checked next.

Finally, dirty_check() determines if the terminal has been resized, or if a full screen update is needed.

Signals, And Their Handling

Signals are at the core of Irssi's design. They are how modules, both internal and external, can communicate with one another, how scripts can change the behaviour of various functionality, and a whole lot more. Signals can be thought of as a type of the Publisher/Subscriber pattern.

A Simplified Description

Because Irssi is single-threaded, only a single signal can be truly "active" at any given time. However, since a handler for a given signal can cause the emission of another, a "call-stack" of signals is maintained.

When a signal is emitted, A list of subscribers to that signal is fetched, and the callback function for each subscriber is called, in order of a priority value.

The following pseudocode demonstrates how the stack-like nature of signals works. We have 3 signals, A, B, and C.

Register A, handler A1, priority 1;
Register A, handler A2, priority 2;
Register A, handler A3, priority 3;

Register B, handler B1, priority 1;

Register C, handler C1, priority 1;
Register C, handler C2, priority 2;

Example 1:

Emit A:
  Call A1;
  Call A2;
    A2 Emits B:
      Call B1;
  Call A3;
    A3 Emits C:
      Call C1;
      Call C2;

Example 2:

Emit A(args_1):
  Call A1(args_1);
    A1 modifies args_1 -> args_2, calls Continue;
  Call A2(args_2);
    A2 Emits C:
      Call C1;
        C1 calls Signal_Stop
      <C2 not called>
  Call A3;

TODO: Someone please clarify this.

The ability for signal handlers to both modify the arguments during the handler chain, as well as terminate the chain at a given point, provides great flexibility in data handling. The use of signals rather than subroutine calls also decouples the different modules, allowing additional handlers to be inserted into the chain and other modifications to be made without any recompilation.

The Gory Details

core/signals.c handles the bulk of the signal implementation. Adapters are implemented in perl/perl-signals.c to allow scripts to produce and consume signals.

At their heart, signals are just a bundle of up to 6 parameters wrapped around a unique signal ID (mapped via a hash table in the [[signals.h:signal_get_uniq_id()|https://github.com/shabble/irssi/blob/links/src/core/signals.h#L67]] macro which in turn calls [[modules.c:module_get_uniq_id_str()|https://github.com/shabble/irssi/blob/links/src/core/modules.c#L81]]).

The following structures are the primary signal data-structures.

 typedef struct {
     int id; /* signal id */
     int refcount;
  
     int emitting;      /* signal is being emitted */
     int stop_emit;     /* this signal was stopped */
     int continue_emit; /* this signal emit was continued elsewhere */
     int remove_count;  /* number of invalid hooks in signal */
  
     SignalHook *hooks; /* head of linked-list, see above */
 } Signal;

The SignalHooks are the structures containing the actual callback functions. They are stored in the *hooks member of the Signal struct as a linked list, and are iterated over and their callback functions executed.

 typedef struct _SignalHook {
     struct _SignalHook *next; /* linked-list sibling */
     int priority;             /* typically -100 (HIGH) -- 100 (LOW) */
     const char *module;       /* where it came from */
     SIGNAL_FUNC func;         /* callback function pointer */
     void *user_data;          /* unused? */
 } SignalHook;

The signals.c:signals hashtable stores Signal structures which implement a reference-counting system, flags to indicate continue or stop, and SignalHook *hooks, a singly-linked list of callback functions.

signals.c:signal_emit_real() is where most of the work happens. It marshalls the varargs, and then travels over the list of callback hooks. It stops when it hits the end of the list, or when the signal record's continue_emit differs from the

stop_emit_count comes from the signal record (from the ID hash), as does continue_count.

TODO: FIgure out the termination - emit_counts

As I understand it, the Signal structure knows how many handlers it expects to call, and increments a counter each time. signal_stop() works by setting that counter to the expected total, thus aborting the handler call loop.

TODO: Confirm

signal_stop() works by making the signal record->stop_emit count greater than the number it's already done, (rec->emitting)

signal_continue() works by stopping the current signal, and re-emitting it with new arguments, but only to the later items in the hooks list.

Any stale callbacks are removed via signal_hooks_clean() at the end of a signal emission, allowing for some grouped garbage collection.

Adding Signals

Notes

Signals can be nested - that's why we need stop_by_name()

Any way to determine the current "signal stack"?

  • Irssi::signal_get_emitted()

  • Irssi::signal_get_emitted_id()

Both return the current signal in the stack, but probably no access to the others.

Curses and Terminals

  • mainwindow

  • drawing stuff

Input Processing (keybindings, etc)

Reading Keystrokes

fe-text/gui-readline is responsible for listening for keypresses, via a GLib input listener attached to STDIN.

When a keystroke is received, it calls the [[sig_input|https://github.com/shabble/irssi/blob/links/src/fe-text/gui-readline.c#L637]] function, which checks for a few things (e.g.: if it is likely part of a clipboard paste, due to the delay between characters), and then emits a "gui key pressed" signal.

The only default handler for "gui key pressed" also resides within gui-readline.c, and performs the following tasks:

  • Checks if a ENTRY_REDIRECT is in place, and if so, handles it.

  • Converts the numeric keystroke value into a string.

  • Passes the key-string to either keyboard.c:key_pressed() or gui-entry.c:gui_entry_insert_char() depending on the value of escape_next_key

    Note: gui_entry_insert_char() gets called anyway, if key_pressed() returns > 0

INPUT HANDLING

gui-readline.c

This is where the stdin listener is added to the main glib mainloop, and calls the sig_input function whenever data is ready to be received.

sig_input uses the term_gets function from term-terminfo or term-ncurses to read the actual character before processing.

sig_input handles some of the paste detection code (checking input against a timestamp of the previous input, and triggering a response if hte delay is too small).

It then sends a gui key pressed signal containing a single parameter, a single byte containing the keycode of the pressed key.

gui-readline.c also has a sig_gui_key_pressed signal handler which pre-processes the input, converting it from a single char into a short string conforming to the usual irssi 'bind' conventions (eg: ctrl-x is represented by "^X", delete (0x7f) by "^?".

The handler tests a flag escape_next_key, which is a function provided by gui-readline which can itself be bound to a key. If set, the key is printed (passed to gui_entry_insert_char) verbatim.

Otherwise, key_pressed in keyboard.c is called. The return value of this function determines whether the key should be printed via gui_entry_... as above. The value must be negative in order for the char to be printed.

keyboard.h

3 major datastrctures of interest:

 typedef struct _KEYBOARD_REC KEYBOARD_REC;
 typedef struct _KEYINFO_REC KEYINFO_REC;
 typedef struct _KEY_REC KEY_REC; 
 
 struct _KEYINFO_REC {
	char *id;
	char *description;
 
	GSList *keys, *default_keys;
 };
 
 struct _KEY_REC {
	KEYINFO_REC *info;
 
	char *key;
	char *data;
 };
 
 struct _KEYBOARD_REC {
     char *name;
     char *key_state; /* the ongoing key combo */
     void *gui_data; /* GUI specific data sent in "key pressed" signal */
 };

keyboard.c

The main API to this file contains the following functions:

 KEYBOARD_REC *keyboard_create(char *name, void *gui_data);
 
 /* Destroys a keyboard */
 void keyboard_destroy(KEYBOARD_REC *keyboard);
  
 
 /* Returns 1 if key press was consumed, -1 if not, 0 if it's beginning of a
    key combo. Control characters should be sent as "^@" .. "^_" instead of
    #0..#31 chars, #127 should be sent as ^? */
 int key_pressed(KEYBOARD_REC *keyboard, const char *key);
 
 void key_bind(const char *id, const char *description,
	       const char *key_default, const char *data, SIGNAL_FUNC func);
 void key_unbind(const char *id, SIGNAL_FUNC func);
 
 void key_configure_freeze(void);
 void key_configure_thaw(void);
 
 void key_configure_add(const char *id, const char *key, const char *data);
 void key_configure_remove(const char *key);
 
 KEYINFO_REC *key_info_find(const char *id);

Bindings are mostly handled by fe-common/core/keyboard.c.

default bindings are initialised from gui-readline.c:init(), whilst user bindings are handled by cmd_bind in keyboard.c. The primary binding setup is handled by key_configure_add(id, key, data).

Handling Key-Bindings.

Binding processing starts when key_pressed is called with a string representing the character typed.

keyboard.c maintains a list used_keys[256} of initial keys. The first test key_pressed performs is to check that:

  • key_state of keyboard object is null

  • length of input is 1 (key[1] contains '\0')

  • used_keys[key[0]] is 0

If all these tests are positive, the function returns with -1, inidicating that no binding exists, and hte key should just be printed to the input field.

Submitting Input Lines

Input is collected into a buffer ("line") until it's passed on when some binding invokes hte send_line function.

This generates a "send command" signal with the line contents, server, and active windowitem context.

"send command" is processed in fe-core-commands.c in the event_command(), event_default_command, and event_command_last() signal handlers.

TODO: link functions/file above.

  • event_command()

  • event_command_last()

    These mostly just handle the silencing of output for commands with $cmdchars^command args syntax (by adding and removing high priority signal handlers for "print starting", "print format", and "print text" which just call signal_stop()).

  • event_default_command()

    This checks if a line is actually a command (if it begins with one of the chars in $cmdchars), and dispatches it appropriately. If not (a command), it sends a "send text" signal with $line, $server, $witem.

    TODO: There's a bit of complicated to check if a possible command is actually a file-path like /foo/bar/..., and also stuff with paste detection (and possibly flood control?)

Screwing with bindings some more

Some keys post a "key *" signal - key backspace for example. Code that does it is key_emit_signal()

It's possible to receive these signals from perl (although you may have to preregister them all first).

# tiny PoC. Not sure if useful info can be extracted from the
# args. At least 2 of them are strings, but unlikely to contain anything
# useful, and hard to unmarshall.

signal_register({'key backspace' => [qw/int int int/]});
signal_add('key backspace', sub { print "Got backspace"; });

TODO: It might also be possible to register the "key "* signal the same way that message and event work.

TODO: Is it possible to inject a key event without any module/c-level hacking? You can register the signal and then pass it dud parameters, but it doesn't seem to work. Needs further tracking/investigation.

History (inc. window level history)

Window Management

How windows are created/manipulated, drawn.

Stuff about split windows

Message levels

Highlights / Activity

Formats, Abstracts, and Themes

Colors:

Colors are currently packaged into a single int. Actually, a single byte, with the top 24 bits being attribute flags.

foreground = x & 0x0f (low 4 bits)
background = x & 0xf0 >> 4 (high 4 bits, shifted down).

Both giving them a selection of 16 colours (0-15, 8 of each bright & dark ANSI standard ones).

Attribute flags are in the fe-text/term.h header.

/* text attributes */
#define ATTR_RESETFG    0x0100
#define ATTR_RESETBG    0x0200
#define ATTR_BOLD           0x0400
#define ATTR_BLINK     0x0800
#define ATTR_UNDERLINE  0x1000
#define ATTR_REVERSE    0x2000

Internals of parsing/colours/layering of formats/theme abstracts, defaults.

Formats.c

format_send_to_gui() * emits 'gui print text', which is pretty far down the chain. * which is processed by either fe-text/gui-printtext.c or fe-common/core/printtext.c. Probably teh former.

gui-printtext.c

 static void sig_gui_print_text(WINDOW_REC *window, void *fgcolor,
			        void *bgcolor, void *pflags,
			        char *str, TEXT_DEST_REC *dest);

Yeah, looks like it. Need to analyse what the colors, pflags are here.

``c

int fg, bg, flags, attr;
 
flags = GPOINTER_TO_INT(pflags);
fg = GPOINTER_TO_INT(fgcolor);
bg = GPOINTER_TO_INT(bgcolor);
get_colors(flags, &fg, &bg, &attr);
 
attr |= fg >= 0 ? fg : ATTR_RESETFG;
attr |= bg >= 0 ? (bg << 4) : ATTR_RESETBG;
term_set_extended_color(root_window, fg, bg);

C<get_colors()> looks like an interesting function. It limits the colours
from going out of range, applies the GUI_PRINT_FLAG_* flags to them. Also might
be responsible (at least in part) for mIRC color decoding.

C<sig_gui_print_text()> only prints if the window argument is null, otherwise it
is handled by:

C<textbuffer_line_add_colors(view-E<gt>buffer, &insert_after, fg, bg, flags)>

TODO: Why?

F<fe-text/textbuffer.c>

C<textbuffer_line_add_colors(view-E<gt>buffer, &insert_after, fg, bg, flags)>

adds a bunch of enums about attribute flags to a data buffer, then
passes it off to C<textbuffer_insert()>

C<set_color()>

appends a string in teh format "\004%c%c" to the provided str argument.
called from:

C<textbuffer_line2text()>

which also does a bunch of "\004%c" insertions, depending on attributes.
line2text is only ever called from F<lastlog.c>, C<textbuffer_find_text()>, and
exposed to the Perl API.

The \004 sequences are parsed by F<formats.c> as part of
C<format_send_to_gui()> function.  send_to_gui actually prints things by calling
the signal "gui print text", leading back to the gui-printtext.c.

Are loops possible?


There is also

C<get_ansi_color()> in formats.c

```c

 term_set_color(TERM_REC *win, int color):
 
 fg = color & 0x0f
 bg = (color & 0xf0) >> 4
 
 flags:
 #define ATTR_RESETFG	0x0100
 #define ATTR_RESETBG	0x0200
 #define ATTR_BOLD   	0x0400
 #define ATTR_BLINK      0x0800
 #define ATTR_UNDERLINE	0x1000
 #define ATTR_REVERSE	0x2000

New API:

 term_set_extended_color(TERM_REC *window, fg, bg).

Both fg and bg are signed ints. -1 is default. low byte (fg & 0xFF) is 256-mode colour. Additional

    case 'X':
    {
        /* TODO: Better numerical parsing code. */
        const char *buf = *format;
        char num_chars[4];
        strncpy(num_chars, buf + 1, 3);
        num_chars[3] = '\0';
        fprintf(stderr, "got esc code X '%s'\n", num_chars);
 
        int i;
        for (i = 0; i < 4; i++) {
            if (num_chars[i] > '9' || num_chars[i] < '0') {
                num_chars[i] = '\0';
            }
        }
 
        chars_consumed = strlen(num_chars);
 
        int col_num = atoi(num_chars);
        fprintf(stderr, "got esc code X num: %d\n", col_num);
 
        // do stuff with number here.
        /* g_string_append_c(out, 4); */
		/* g_string_append_c(out, FORMAT_STYLE_MONOSPACE); */
        break;
    }
    case 'x':
        fprintf(stderr, "got esc code x\n");
        break;

My goal for 256-color processing is to keep the standard colors exactly the way they are now, but provide additional feature for those who want them. This will be in the form of 2 items:

a /set enable_256_colour setting.

a pair of colour format markers, %x, and %X. Each will be followed by 1-3 numbers, indicating your actual colour. %X will be background, %x will be foreground.

E.g: prompt = "%x200[%n$*%x150]%n%X10:";

for parsing reasons, it may be necessary to ensure that all numbers are 3 characters, and padded with 0's like %X004 - %x255. Haven't got that far yet.

NOTE: This has now changed to require exactly 2 Hex digits (/[0-0a-f]{2}/i) to specify colour.

Perl Scripting Support

Where datastructures are set
how they are made
.xs files where everything lives / code organisation

Some of these things are:

hilights

For a standard message, such as a PUBLIC:

"message public" handled by fe-common/core/fe-messages.c

Message Levels
Abstract Replacements
Theme formats
Module (/format) formats

Message Levels

Message levels are defined in src/core/levels.h and some support functions in src/core/levels.c

We have:

int level_get(str level)

Special cases: ALL, * both return MSGLEVEL_ALL, and NEVER returns 0. The remainder are looked up in a big static string array by name to find the appropriate numeric level. Partial matches are permitted as long as they are unambigious.

int level2bits(str level, int *errorp)
str bits2level(int bits)
int combine_level(int dest, str src)

fe-text Initialisation.

core_preinit() is where all the irssi_dir and irssi_binary stuff is set up, as well as locating the config file. Commandline Arguments has already been parsed by then.

Irssi considers you a 'first-timer' if you don't have a ~/.irssi directory, or equivalent in whever you specified your --home.

textui_init() begins the init() process, calling core_init(), and hten a bunch of irc_* init functions.

core_init() calls init on just about every internal module not directly related to chatnets.

Shortly after all that, term_init() is called, which through various layers of indirection and abstraction, initialises the curses and terminfo structures.

term-curses.c

 term_init()
   try: { term_init_int()
             init_curses()
               initscr() actually starts Ncurses.
               Then a bunch of other init functions.  Many wrapped in #defines
                presumably determined at ./configure time.
                  start_color() called if they're enabled.
 
        } -- if init_curses fails, term_init returns false. Irssi then doesn't start.
   term_common_init()

TODO: meaning of HAVE_NCURSES_USE_DEFAULT_COLORS http://linux.die.net/man/3/use_default_colors - primarily used for things that don't draw over the whole of the screen, but Irssi generally does. Hmm.

after use_default_colors(), it loops over i .. COLOR_PAIRS (a define somewhere in curses header?), calling init_pair()

Actually:

char ansi_tab[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
  init_pair(num, ansi_tab[num & 7], num <= 7 ? -1 : ansi_tab[num >> 3]);

Where num is the pair ID, ansi_tab[ (num & 0b0111) ] is foreground, and the background is either -1, or ansi_tab[ num / 8 ].

color pair 63 is special, being 0, -1

Default curses definitions for colours are:

COLOR_BLACK   0  So ansi_tab becomes: black, blue,    green,  cyan,
COLOR_RED         1                       red,   magenta, yellow, white
COLOR_GREEN       2  For no apparent
COLOR_YELLOW  3  reason.
COLOR_BLUE        4
COLOR_MAGENTA 5
COLOR_CYAN        6
COLOR_WHITE       7

if HAVE_NCURSES_USE_DEFAULT_COLORS isn't defined, colours are initialised identically, except there's no -1 for background, and COLOR_PAIR(63) is (0,0) (black on black).

Clearly, -1 has something to do with the 'default colouring'.

clear() clears the screen, then returns.

Window dimensions are set.

term_common_init() is called:

This mostly handles the 'lookandfeel' settings regarding colours, some charset config, and binds the /resize and redraw commands. It also installs a SIGWINCH (window size change) handler if available.

The handler marks irssi as 'dirty', and also resize-dirty. Oddly, the same way as the /resize and /redraw commands work. (presumably as a nudge if SIGWINCH isn't handled properly by your system)

Oddly, term-curses.c doesn't even seem to get built (on my system).

TODO: checkout why - some configure/makefile jiggery? configure has a:

--without-terminfo      Use curses backend instead of terminfo

clearly they have similar interfaces.

term.h

Appears to act as an interface (which both term-curses and term-terminfo share.

term-terminfo.c

term-init()

Initialises lots of global (well, file-scoped static) vars, then calls terminfo_core_init().

terminfo-core.c

terminfo_core_init()

stores the current TERM_REC struct, and restores it on deinit(). then allocates a new one, and connects it up to STDIN/OUT.

Calls term_setup on the new term. If it fails, it restores the original frees itself, and returns.

term_setup()

appears to be a mish-mash of #defines regarding whether terminfo or termcap is to be used.

first the environment TERM-type is fetched, then the TERM_REC is initialised using either setuperm (see man terminfo), or tgetent if using termcap.

This does not appear to have significant effect on the TERM_REC struct, since that is subsequently dealt wth using the term_fill_capabilities() function.

term_fill_capabilities()

This iterates over tcaps, a huge static multi-dimensional struct array containing details of the various terminfo/termcap parameters, including their types, and their byte indices into the TERM_REC struct, using the G_STRUCT_OFFSET macro.

 typedef struct {
     const char *ti_name; /* terminfo name */
	 const char *tc_name; /* termcap name */
	 int type;
	 unsigned int offset;
 } TERMINFO_REC;

For each element, a pointer to the appropriate spot in the TERM_REC is found, and the value is retrieved (via macros to abstract the cap/info issue), cast appropriately, and saved into the struct.

After returning from term_fill_capabilities, a large number of function pointers are assigned into the TERM_REC struct, based on the results from the prior capability gathering.

After the cursor motion and character deletion features, text properties such as bold, standout, underline, etc are set. term->set_normal, a string with no attributes is also produced.

terminfo_setup_colors()

First, all existing/original colours are freed (terminfo_colors_deinit()), then a similar arrangement is used here:

static const char ansitab[16] = {
        0, 4, 2, 6, 1, 5, 3, 7,
        8, 12, 10, 14, 9, 13, 11, 15
};

The ansitab values are only used if colours are force enabled however.

term->set_fg and term->set_bg are mapped to _set_fg and _set_bg respectively.

These functions use:

 tput(tparm(term->TI_fg[color % term->TI_colors]))

tput is defined as a macro:

  #define tput(s) tputs(s, 0, term_putchar)
  inline static int term_putchar(int c)
  {
          return fputc(c, current_term->out);
  }

tparm() may be a terminfo function, or it could relate to tparm.c:tparm(). GDB is needed. (It may well be an actual part of curses, wedged in here for necessity).

Returning to the colour setup, if the term has TI_setaf capabilities, they are used to pack 16 colours into the term-TI_fg array using 16 item ansi colour table.

If term has TI_setf, then TI_fg is packed with TI_colors.

Finally, if forced, TI_fg is packed wth 8 ansi escape-sequence mappings, of the form '\e[%dm', using ansi_table[x] + 30

(see http://invisible-island.net/xterm/ctlseqs/ctlseqs.html)

The same thing is done iwth the background colours, first trying setab, then setb, then (optionally) forcing '\e[%dm', using ansi_table[x] + 40.

terminfo-core.h

struct _TERM_REC is a huge struct:

 struct _TERM_REC {
        /* Functions */
	void (*move)(TERM_REC *term, int x, int y);
	void (*move_relative)(TERM_REC *term, int oldx, int oldy, int x, int y);
	void (*set_cursor_visible)(TERM_REC *term, int set);
	void (*scroll)(TERM_REC *term, int y1, int y2, int count);
 
        void (*clear)(TERM_REC *term);
	void (*clrtoeol)(TERM_REC *term);
	void (*repeat)(TERM_REC *term, char chr, int count);
 
	void (*set_fg)(TERM_REC *term, int color);
	void (*set_bg)(TERM_REC *term, int color);
	void (*set_normal)(TERM_REC *term);
	void (*set_blink)(TERM_REC *term);
	void (*set_bold)(TERM_REC *term);
	void (*set_uline)(TERM_REC *term, int set);
	void (*set_standout)(TERM_REC *term, int set);
 
        void (*beep)(TERM_REC *term);
 
 #ifndef HAVE_TERMINFO
	char buffer1[1024], buffer2[1024];
 #endif
	FILE *in, *out;
	struct termios tio, old_tio;
 
        /* Terminal size */
        int width, height;
 
        /* Cursor movement */
	const char *TI_smcup, *TI_rmcup, *TI_cup;
	const char *TI_hpa, *TI_vpa, *TI_cub1, *TI_cuf1;
        const char *TI_civis, *TI_cnorm;
 
	/* Scrolling */
	const char *TI_csr, *TI_wind;
	const char *TI_ri, *TI_rin, *TI_ind, *TI_indn;
	const char *TI_il, *TI_il1, *TI_dl, *TI_dl1;
 
	/* Clearing screen */
	const char *TI_clear, *TI_ed; /* + *TI_dl, *TI_dl1; */
 
        /* Clearing to end of line */
	const char *TI_el;
 
	/* Repeating character */
	const char *TI_rep;
 
	/* Colors */
	int TI_colors; /* numbers of colors in TI_fg[] and TI_bg[] */
	const char *TI_sgr0; /* turn off all attributes */
	const char *TI_smul, *TI_rmul; /* underline on/off */
        const char *TI_smso, *TI_rmso; /* standout on/off */
        const char *TI_bold, *TI_blink;
	const char *TI_setaf, *TI_setab, *TI_setf, *TI_setb;
 
        /* Colors - generated and dynamically allocated */
	char **TI_fg, **TI_bg, *TI_normal;
 
	/* Beep */
        char *TI_bel;
 };

NOTE: TI_colors seems to be the major stumbling block here. If it can be correctly detected, things will allocate and hopefully render correctly.

term_refresh_freeze() and <term_refresh_thaw()> are used as counting semaphores, to lock to system from performing updates whilst the initialistion is occouring, allowing it to happen all at once after the final thaw() is called.

Hacking on the Perl API

Notes:

create a new blah.xs file in src/perl/blah.

Ensure that you have the following entry:

 MODULE = Irssi::blah::YourPackage PACKAGE = Irssi::Some::Namespace
 PROTOTYPES: ENABLE

in your .xs file

Add the appropriate entry to src/perl/Makefile.am

Probably under blah_sources.

Edit the base .xs file for your package

Find the BOOT: entry, and add

 irssi_boot(BLAH__YourPackage);
recompile

Running ./autogen.sh probably doesn't hurt. The configure script might regen all the makefiles, but it's hard to be sure.

See an example at https://github.com/shabble/irssi-scripts/blob/patches/patches/binding-api.patch

Playing with GDB.

Watching signals go by. Need to find a way to dump the stack/read out the varargs parameters to signals though.

gdb `which irssi` `pgrep irssi`>

break signal_emit
condition 1 !(int)strncmp(signal, "Some Signal", N)

gives

DCC

DCCs signals are distinguished by the type of request object they carry. In scripting, "dcc request" applies to all connection setup requests, but sub handle_req { return unless $_[0]-{type} eq 'GET' ... }> will deal with it.

This needs to be better documented in the individual DCC pages. (Which also need all their links fixed)

FILES

Full listing of all source files. (Minus some spurious makefiles and other cruft.)

.
|-- common.h
|-- core
|   |-- args.c
|   |-- args.h
|   |-- channel-rec.h
|   |-- channel-setup-rec.h
|   |-- channels-setup.c
|   |-- channels-setup.h
|   |-- channels.c
|   |-- channels.h
|   |-- chat-commands.c
|   |-- chat-protocols.c
|   |-- chat-protocols.h
|   |-- chatnet-rec.h
|   |-- chatnets.c
|   |-- chatnets.h
|   |-- commands.c
|   |-- commands.h
|   |-- core.c
|   |-- core.h
|   |-- expandos.c
|   |-- expandos.h
|   |-- ignore.c
|   |-- ignore.h
|   |-- levels.c
|   |-- levels.h
|   |-- line-split.c
|   |-- line-split.h
|   |-- log-away.c
|   |-- log.c
|   |-- log.h
|   |-- masks.c
|   |-- masks.h
|   |-- misc.c
|   |-- misc.h
|   |-- module.h
|   |-- modules-load.c
|   |-- modules-load.h
|   |-- modules.c
|   |-- modules.h
|   |-- net-disconnect.c
|   |-- net-disconnect.h
|   |-- net-nonblock.c
|   |-- net-nonblock.h
|   |-- net-sendbuffer.c
|   |-- net-sendbuffer.h
|   |-- network-openssl.c
|   |-- network.c
|   |-- network.h
|   |-- nick-rec.h
|   |-- nicklist.c
|   |-- nicklist.h
|   |-- nickmatch-cache.c
|   |-- nickmatch-cache.h
|   |-- pidwait.c
|   |-- pidwait.h
|   |-- queries.c
|   |-- queries.h
|   |-- query-rec.h
|   |-- rawlog.c
|   |-- rawlog.h
|   |-- recode.c
|   |-- recode.h
|   |-- server-connect-rec.h
|   |-- server-rec.h
|   |-- server-setup-rec.h
|   |-- servers-reconnect.c
|   |-- servers-reconnect.h
|   |-- servers-setup.c
|   |-- servers-setup.h
|   |-- servers.c
|   |-- servers.h
|   |-- session.c
|   |-- session.h
|   |-- settings.c
|   |-- settings.h
|   |-- signals.c
|   |-- signals.h
|   |-- special-vars.c
|   |-- special-vars.h
|   |-- window-item-def.h
|   |-- window-item-rec.h
|   |-- write-buffer.c
|   `-- write-buffer.h
|-- fe-common
|   |-- core
|   |   |-- chat-completion.c
|   |   |-- chat-completion.h
|   |   |-- command-history.c
|   |   |-- command-history.h
|   |   |-- completion.c
|   |   |-- completion.h
|   |   |-- fe-channels.c
|   |   |-- fe-channels.h
|   |   |-- fe-common-core.c
|   |   |-- fe-common-core.h
|   |   |-- fe-core-commands.c
|   |   |-- fe-core-commands.h
|   |   |-- fe-exec.c
|   |   |-- fe-exec.h
|   |   |-- fe-expandos.c
|   |   |-- fe-help.c
|   |   |-- fe-ignore-messages.c
|   |   |-- fe-ignore.c
|   |   |-- fe-log.c
|   |   |-- fe-messages.c
|   |   |-- fe-messages.h
|   |   |-- fe-modules.c
|   |   |-- fe-queries.c
|   |   |-- fe-queries.h
|   |   |-- fe-recode.c
|   |   |-- fe-recode.h
|   |   |-- fe-server.c
|   |   |-- fe-settings.c
|   |   |-- fe-windows.c
|   |   |-- fe-windows.h
|   |   |-- formats.c
|   |   |-- formats.h
|   |   |-- hilight-text.c
|   |   |-- hilight-text.h
|   |   |-- keyboard.c
|   |   |-- keyboard.h
|   |   |-- module-formats.c
|   |   |-- module-formats.h
|   |   |-- module.h
|   |   |-- printtext.c
|   |   |-- printtext.h
|   |   |-- themes.c
|   |   |-- themes.h
|   |   |-- utf8.c
|   |   |-- utf8.h
|   |   |-- wcwidth.c
|   |   |-- window-activity.c
|   |   |-- window-activity.h
|   |   |-- window-commands.c
|   |   |-- window-items.c
|   |   |-- window-items.h
|   |   |-- windows-layout.c
|   |   `-- windows-layout.h
|   `-- irc
|       |-- dcc
|       |   |-- fe-dcc-chat-messages.c
|       |   |-- fe-dcc-chat.c
|       |   |-- fe-dcc-get.c
|       |   |-- fe-dcc-send.c
|       |   |-- fe-dcc-server.c
|       |   |-- fe-dcc.c
|       |   |-- fe-dcc.h
|       |   |-- module-formats.c
|       |   |-- module-formats.h
|       |   `-- module.h
|       |-- fe-common-irc.c
|       |-- fe-ctcp.c
|       |-- fe-events-numeric.c
|       |-- fe-events.c
|       |-- fe-irc-channels.c
|       |-- fe-irc-commands.c
|       |-- fe-irc-messages.c
|       |-- fe-irc-queries.c
|       |-- fe-irc-server.c
|       |-- fe-irc-server.h
|       |-- fe-ircnet.c
|       |-- fe-modes.c
|       |-- fe-netjoin.c
|       |-- fe-netsplit.c
|       |-- fe-whois.c
|       |-- irc-completion.c
|       |-- module-formats.c
|       |-- module-formats.h
|       |-- module.h
|       `-- notifylist
|           |-- fe-notifylist.c
|           |-- module-formats.c
|           |-- module-formats.h
|           `-- module.h
|-- fe-none
|   |-- irssi.c
|   `-- module.h
|-- fe-text
|   |-- gui-entry.c
|   |-- gui-entry.h
|   |-- gui-expandos.c
|   |-- gui-printtext.c
|   |-- gui-printtext.h
|   |-- gui-readline.c
|   |-- gui-readline.h
|   |-- gui-windows.c
|   |-- gui-windows.h
|   |-- irssi.c
|   |-- lastlog.c
|   |-- mainwindow-activity.c
|   |-- mainwindows-layout.c
|   |-- mainwindows.c
|   |-- mainwindows.h
|   |-- module-formats.c
|   |-- module-formats.h
|   |-- module.h
|   |-- statusbar-config.c
|   |-- statusbar-config.h
|   |-- statusbar-item.h
|   |-- statusbar-items.c
|   |-- statusbar.c
|   |-- statusbar.h
|   |-- term-curses.c
|   |-- term-dummy.c
|   |-- term-terminfo.c
|   |-- term.c
|   |-- term.h
|   |-- terminfo-core.c
|   |-- terminfo-core.h
|   |-- textbuffer-commands.c
|   |-- textbuffer-view.c
|   |-- textbuffer-view.h
|   |-- textbuffer.c
|   |-- textbuffer.h
|   `-- tparm.c
|-- filelist.txt
|-- irc
|   |-- core
|   |   |-- bans.c
|   |   |-- bans.h
|   |   |-- channel-events.c
|   |   |-- channel-rejoin.c
|   |   |-- channel-rejoin.h
|   |   |-- channels-query.c
|   |   |-- ctcp.c
|   |   |-- ctcp.h
|   |   |-- irc-channels-setup.c
|   |   |-- irc-channels.c
|   |   |-- irc-channels.h
|   |   |-- irc-chatnets.c
|   |   |-- irc-chatnets.h
|   |   |-- irc-commands.c
|   |   |-- irc-commands.h
|   |   |-- irc-core.c
|   |   |-- irc-expandos.c
|   |   |-- irc-masks.c
|   |   |-- irc-masks.h
|   |   |-- irc-nicklist.c
|   |   |-- irc-nicklist.h
|   |   |-- irc-queries.c
|   |   |-- irc-queries.h
|   |   |-- irc-servers-reconnect.c
|   |   |-- irc-servers-setup.c
|   |   |-- irc-servers-setup.h
|   |   |-- irc-servers.c
|   |   |-- irc-servers.h
|   |   |-- irc-session.c
|   |   |-- irc.c
|   |   |-- irc.h
|   |   |-- lag.c
|   |   |-- massjoin.c
|   |   |-- mode-lists.c
|   |   |-- mode-lists.h
|   |   |-- modes.c
|   |   |-- modes.h
|   |   |-- module.h
|   |   |-- netsplit.c
|   |   |-- netsplit.h
|   |   |-- servers-idle.c
|   |   |-- servers-idle.h
|   |   |-- servers-redirect.c
|   |   `-- servers-redirect.h
|   |-- dcc
|   |   |-- dcc-autoget.c
|   |   |-- dcc-chat.c
|   |   |-- dcc-chat.h
|   |   |-- dcc-file-rec.h
|   |   |-- dcc-file.h
|   |   |-- dcc-get.c
|   |   |-- dcc-get.h
|   |   |-- dcc-queue.c
|   |   |-- dcc-queue.h
|   |   |-- dcc-rec.h
|   |   |-- dcc-resume.c
|   |   |-- dcc-send.c
|   |   |-- dcc-send.h
|   |   |-- dcc-server.c
|   |   |-- dcc-server.h
|   |   |-- dcc.c
|   |   |-- dcc.h
|   |   `-- module.h
|   |-- flood
|   |   |-- autoignore.c
|   |   |-- autoignore.h
|   |   |-- flood.c
|   |   |-- flood.h
|   |   `-- module.h
|   |-- notifylist
|   |   |-- module.h
|   |   |-- notify-commands.c
|   |   |-- notify-ison.c
|   |   |-- notify-setup.c
|   |   |-- notify-setup.h
|   |   |-- notify-whois.c
|   |   |-- notifylist.c
|   |   `-- notifylist.h
|   `-- proxy
|       |-- dump.c
|       |-- listen.c
|       |-- module.h
|       |-- proxy.c
|       `-- proxy.h
|-- lib-config
|   |-- get.c
|   |-- iconfig.h
|   |-- module.h
|   |-- parse.c
|   |-- set.c
|   `-- write.c
`-- perl
    |-- common
    |   |-- Channel.xs
    |   |-- Core.xs
    |   |-- Expando.xs
    |   |-- Ignore.xs
    |   |-- Irssi.bs
    |   |-- Irssi.pm
    |   |-- Irssi.xs
    |   |-- Log.xs
    |   |-- Makefile.PL.in
    |   |-- Masks.xs
    |   |-- Query.xs
    |   |-- Rawlog.xs
    |   |-- Server.xs
    |   |-- Settings.xs
    |   |-- module.h
    |   `-- typemap
    |-- get-signals.pl
    |-- irc
    |   |-- Channel.xs
    |   |-- Client.xs
    |   |-- Ctcp.xs
    |   |-- Dcc.xs
    |   |-- Irc.bs
    |   |-- Irc.pm
    |   |-- Irc.xs
    |   |-- Makefile.PL.in
    |   |-- Modes.xs
    |   |-- Netsplit.xs
    |   |-- Notifylist.xs
    |   |-- Query.xs
    |   |-- Server.xs
    |   |-- module.h
    |   `-- typemap
    |-- irssi-core.pl
    |-- irssi-core.pl.h
    |-- module-fe.h
    |-- module-formats.c
    |-- module-formats.h
    |-- module.h
    |-- perl-common.c
    |-- perl-common.h
    |-- perl-core.c
    |-- perl-core.h
    |-- perl-fe.c
    |-- perl-signals-list.h
    |-- perl-signals.c
    |-- perl-signals.h
    |-- perl-sources.c
    |-- perl-sources.h
    |-- textui
    |   |-- Makefile.PL.in
    |   |-- Statusbar.xs
    |   |-- TextBuffer.xs
    |   |-- TextBufferView.xs
    |   |-- TextUI.bs
    |   |-- TextUI.pm
    |   |-- TextUI.xs
    |   |-- module.h
    |   `-- typemap
    `-- ui
        |-- Formats.xs
        |-- Makefile.PL.in
        |-- Themes.xs
        |-- UI.bs
        |-- UI.pm
        |-- UI.xs
        |-- Window.xs
        |-- module.h
        `-- typemap
⚠️ **GitHub.com Fallback** ⚠️