Whowatch can load at runtime external programs compiled as
dynamic library. Such program is called plugin.

# First and important question: what for?

Answer is simple: someone may be interested in information
about process or logged in user other than whowatch provides.
In general, plugin is responsible for printing data inside
"details window" ('d' or 's' key).
So, one can write C program (that use whowatch API, read below),
compile it to .so dynamic library and then load it into running
whowatch. This plugin can (for example) print selected user mailbox
size or cpu load generated by a selected process.
It is up to you what kind of information plugin prints.
You have full flexibility.

There are 3 types of plugins. Whowatch has builtin plugins for all
these types:

- proc plugin
  If you hit 'd' when viewing processes tree you can see output
  produced by builtin proc plugin: detailed information about
  selected process.

- user plugin
  The same, when you watch list of logged in users. User plugin
  prints detailed information about selected user.

- sys plugin
  If you hit 's' key (both in users list or tree) you can see
  detailed system information.

Non-builtin plugins can be loaded at any time and they can overwrite
output produced by builtin plugin, or they can append/insert its own
information into the details window without losing output generated
by whowatch builtin plugin.

# How to write a plugin?

What method you use to obtain information about user/process/system is
up to you. Under Linux you'll probably use data from /proc.
Under BSD you'll use sysctl() (oh, why Linux has so poor sysctl...),
which is much more faster and less painful than reading from /proc.
Anyway, if you have appropriate code then you have to generate
output into the details window, using simple whowatch functions:

- println(const char *format, ...)
- print(const char *format, ...)
- title(const char *format, ...)
- newln()
- boldon()
- boldoff()

Without going into the details, let see how it looks like. Simple
code of concept for user plugin, that prints user mailbox size:

#-------- user_test_plugin.c ----------

#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

#include "pluglib.h"    // always include it in plugin source

/* let assume that following path is ok in our system */
#define MAILBOX_PATH    "/var/spool/mail"

/* you _have_ to define plugin_type */
int plugin_type = USER_PLUGIN

/* we will talk about it a little bit later */
int plugin_init(void *unused)
{
	println("Hello World, my first user plugin.");
	return APPEND | PERIODIC;
}

void plugin_draw(void *p)
{
	char *name = (char*)p;
	char buf[128];
	struct stat st;
	snprintf(buf, sizeof buf, "%s/%s", MAILBOX_PATH, name);

	title("MAILBOX SIZE: ");	/* whowatch API function */
	if(stat(buf, &st) == -1) {
		/* another whowatch API function - almost identical to printf */
		println("%s %s", buf, strerror(errno));
		return;
	}
	println("%ld", st.st_size);
}

#----------- end of user_test_plugin.c -----------

That's it. Then you have to compile it to .so library:

gcc user_test_plugin.c -shared -o user_test_plugin.so

Then you can load the plugin into the whowatch (by selecting "User plugin"
in whowatch menu) and you'll see in details window (if watching users list)
information produced by builtin whowatch plugin and something else...
At the last line there will be:
"MAILBOX SIZE: "
with selected user mailbox size, or error string (returned by stat()).

# API details.

API is rather simple, trivial even.
GENERAL RULES:
- all proc/user/sys plugins have the same structure (same plugin_something()
  functions, same flags), except few differences in rules for calling
  plugin_draw() (ie. when cursor moves, read below)
- plugin has no main() function
- plugin HAS to include pluglib.h
- plugin must define integer "plugin_type" with one of the following flags:
  USER_PLUGIN, PROC_PLUGIN, SYS_PLUGIN
- plugin is a normal C code with any external library/function/include.
- plugin HAS to use whowatch print family of functions to print
  data into the details window
- plugin CANNOT use normal printf function at all - you'll mess up
  whowatch screen.
- plugin MUST define 2 functions: plugin_init, plugin_draw
  and MAY define following functions:
  plugin_clear, plugin_cleanup
- if you do something wrong in your plugin you can make whowatch to:
  hang, generate 100% CPU load, dump core, begin 3 World War, whatever...
  Just the same if you write normal, standalone program.
  You have been warned :-)

Each plugin _HAS_ to define following functions:

1. int plugin_init(void *)

This function will be called when dynamic library (your plugin) is loaded
into the whowatch. If it doesn't exists plugin won't be loaded.
As you can see the argument is a void pointer.
The way it should be treated depends on type of plugin.
If you write proc plugin (plugin that prints information about selected
process) then inside plugin_init() argument must be casted to (int *).
This is the way you know pid of a selected process.
Take a look:

int plugin_init(void *p)
{
	int pid = (int *) p;
	println("Initializing proc plugin.");
	println("Current selected process has pid: %d", pid);
	return PERIODIC | OVERWRITE;
}

Next important thing:
plugin_init SHOULD return flags that may also be bitwise-or'd with
one or more of the following (flags are defined in pluglib.h):

PERIODIC - tells the whowatch that our plugin wants to be called
periodically (default whowatch timeout is 3 sec)
Builtin whowatch system and proc plugins are called periodically.
User plugin is not, since it gives rather static information.

OVERWRITE - plugins says that it owns the detailed window, and
default builtin plugin (of this type) shouldn't print any
information

INSERT - builtin whowatch plugin will print information but
it will be printed in details window _below_ output produced by
your plugin

APPEND - please guess ;-)

Yet few words about argument to plugin_init().
If you write an user plugin, then argument MUST be casted to (char *).
This way your plugin knows the USERNAME of currently selected user:

int plugin_init(void *p)
{
	char *username = (char *) p;
	println("Initializing user plugin.");
	println("Current selected user is : %s", username);
	return ~PERIODIC | OVERWRITE;
}

In fact in plugin_init() you rather don't need to bother about argument,
since plugin_init is called only once (loading plugin into the whowatch).
But you can use it if you need for example to initialize your own data
structures (malloc memory, init a hash table, whatever...).
Argument (which is a pointer to int - pid of a selected process, or
pointer to char - selected user's name) is really important in
plugin_draw() function.

plugin_draw() is a second and last function that you MUST define in
your code.

void plugin_draw(void *p)

The way you treat an argument is exactly the same as in plugin_init().
plugin_draw() is a heart of a plugin: it is responsible for printing
information inside details window.
In plugin_init you gave whowatch orders (by returning PERIODIC, OVERWRITE...)
how to deal with plugin_draw().
OVERWRITE, INSERT, APPEND flags tells whowatch how to print information, that
are generated in your plugin_draw().
PERIODIC (or ~PERIODIC :-) tells whowatch WHEN your plugin_draw should be called.
But...no matter what flags you returned in plugin_init, whowatch
ALWAYS calls your plugin_draw() if cursor moves.
If you returned ~PERIODIC then your plugin_draw() will not be called
as long you don't move a cursor.
If you watch for example processes tree and you move a cursor to another
process, then your plugin_draw() will be called with a current pid
as an argument (err...pointer to void that you have to cast to (int *)..
remember?)
In this way your plugin_draw() can do anything you want to obtain
information about current process.
Of course if details window is not active then plugin_draw() won't
be called since whowatch user is not interested in detailed information
at the moment, and we alaways want to save CPU time.

#########
ONE IMPORTANT NOTICE ABOUT SYS PLUGIN:
since sys plugin doesn't depend on any pid or username, then:
- plugin_draw() in this type of plugin it not called when cursor moves
- argument to plugin_draw() and plugin_init() is ALWAYS zero.
########

....(add info about println, print, title, newln, bold)....

With the above information you are ready to write your own plugins.
