-
Notifications
You must be signed in to change notification settings - Fork 6
Internals
Code Organisation
Design Hierarchy
Filesystem Layout
Mainloop
Signals, And Their Handling
Adding Signals
notes
Curses and Terminals
Input Processing (keybindings, etc)
Window Management
Formats, Abstracts, and Themes
Perl Scripting Support
Message Levels
Hacking on the Perl API
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)
[[design.txt|http://irssi.org/documentation/design]].
|-- core
|-- fe-common
| |-- core
| `-- irc
| |-- dcc
| `-- notifylist
|-- fe-none
|-- fe-text
|-- irc
| |-- core
| |-- dcc
| |-- flood
| |-- notifylist
| `-- proxy
|-- lib-config
`-- perl
|-- common
|-- irc
|-- textui
`-- uiIrssi 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.
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|http://library.gnome.org/devel/glib/stable/glib-The-Main-Event-Loop.html]] 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()|https://github.com/shabble/irssi/blob/links/src/fe-text/irssi.c#L112]] determines if the terminal has been resized, or if a full screen update is needed.
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.
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.
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.
Signals can be nested - that's why we need stop_by_name()
Any way to determine the current "signal stack"?
-
mainwindow
-
drawing stuff
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_REDIRECTis 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()|https://github.com/shabble/irssi/blob/links/src/fe-common/core/keyboard.c#L556]]or[[gui-entry.c:gui_entry_insert_char()|https://github.com/shabble/irssi/blob/links/src/fe-text/gui-entry.c#L488]]depending on the value ofescape_next_keyNote:
gui_entry_insert_char()gets called anyway, ifkey_pressed()returns > 0
Bindings are mostly handled by fe-common/core/keyboard.c.
How windows are created/manipulated, drawn.
Stuff about split windows
Message levels
Highlights / Activity
Internals of parsing/colours/layering of formats/theme abstracts, defaults.
- 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 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, andNEVERreturns 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)
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 addirssi_boot(BLAH__YourPackage);
- recompile
-
Running
./autogen.shprobably 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
Much of the content on these pages is taken from original Irssi documentation and is Copyright © 2000-2010 The Irssi project. Formatting and additional documentation, examples, etc by Tom Feist and the other editors of this wiki. This work is licensed under a Creative Commons Attribution-ShareAlike 2.5 License. Please see http://creativecommons.org/licenses/by-sa/2.5/ for details.