Wily C Programmers Manual

Introduction

Although a lot can be done with simple scripts and Wily, some applications will need finer control. Wily can act as a "user interface server". Clients can create and manipulate windows, and/or monitor events which happen in windows, by sending and receiving messages.

There are two "levels" to the interface: an "RPC" level, where the library looks after low-level details of queueing, packing and unpacking messages, and a lower-level interface dealing more directly with message packets.

Warning this API hasn't been used very much. There could well be bugs lurking in unused sections of code, and the API could well change.

Establishing a Connection

Call client_connect(), which returns a file descriptor open for reading and writing to Wily, or a negative number if there's been some error. client_connect() uses $WILYFIFO if it is set.

RPC-style Interface

Opening, Closing and Checking the RPC interface

Handle* rpc_init (int fd);

Create a new RPC handle to use file descriptor fd, which was probably returned from client_connect

rpc_init cannot fail.

Bool rpc_isconnected(Handle*h);

Return whether or not h is still connected. If the RPC library starts receiving gibberish messages, it will cut the connection.

void rpc_freehandle(Handle*h);

Release any resources used by h, and free h itself.

Requests

If there's any sort of failure, all of these functions return a pointer to an error message, which is stored in a static buffer and only guaranteed to remain valid until the next rpc_* function is called. After a failure, it's possible that the connection to wily has been lost. This should be checked using rpc_isconnected

If successful, these functions return (char*)0.

List existing windows
char* rpc_list (Handle*, char **bufptr);

If successful, allocates a string, and stores a pointer to it in bufptr. It is the programmer's responsibility to free(*bufptr). The string contains one line for each window currently open in Wily. Each line contains the name of the window, some whitespace, and a unique number which identifies the window. The window identifiers are not reused within an invocation of Wily.

Below is some example output from rpc_list:

/n/staff/u13/pgrad/gary/src/wily/Doc/C.html	45
/n/staff/u13/pgrad/gary/src/wily/Doc/index.html	41
/n/staff/u13/pgrad/gary/src/wily/Doc/	40
/n/staff/u13/pgrad/gary/guide	0

Open or create a window
char* rpc_new (Handle*, char *s, Id*id, ushort backup);

Create or open window with name s. If Wily already has a window named 's', it uses that window, otherwise it creates a new window.

The identifier for the opened/created window will be stored in id.

If this request creates (not just opens) the window, Wily will make sure backups are kept for the window (and indicate when the window is dirty) if and only if backup is set.

Request events from a window
char* rpc_attach (Handle*, Id w, ushort mask);

Asks Wily to send us events which happen in window w and match event bitmask mask.

Each window can be sending events to at most one remote process. The window sends only events which match its event bitmask. An event bitmask is formed by ORing some combination of (WEexec, WEgoto, WEdestroy, WEreplace) (explained further below).

rpc_attach will fail if w doesn't exist, or is already sending its events somewhere else.

To stop receiving events from a window, use an rpc_attach with a mask of 0.

Change a window's name
char* rpc_setname (Handle*, Id w, char *s);

Set the name of window w to s.

Change a window's "tools"
char* rpc_settools (Handle*, Id w, char *s);

Set the tools section of w to s

Read data from a window
char* rpc_read (Handle*, Id w, Range r, char*buf);

Read range r from w into buf, which must have enough space (UTFmax * RLEN(r)).

The range r means the Rune offsets in w >= r.p0 and < r.p1. Don't forget that each Rune will be potentially encoded in UTFmax bytes.

rpc_read fails if r is not fully contained in window w. rpc_goto may be useful to obtain legal ranges.

Insert, delete or replace text
char* rpc_replace (Handle*, Id w, Range r, char*s);

Replace range r in w with UTF string s

Note that insertion and deletion are special cases of replacement.

Ask Wily to execute a command
char* rpc_exec (Handle*, Id w , char *s);

Cause Wily to act as though s were selected in w with button 2.

Note that builtin functions (e.g. Del) can also be executed with rpc_exec.

Ask Wily to "goto" a file or location
char* rpc_goto (Handle*, Id *w , Range* r, char*s, ushort flag);

Cause Wily to act as though s were selected in w with button 3.

If this makes Wily do a search within '*w', the start position for the search is usually taken from '*r'. The search can instead start from the current selection of the file if r->p0 > r->p1. If there are multiple views of the same file, one is chosen arbitrarily.

If the search is successful, '*w' and '*r' are set to the window and range found by the search. The range found will be selected and highlighted if and only if 'flag' is set.

Hint: rpc_goto can be used to find useful ranges for rpc_read and rpc_replace. Don't forget that the search string can be any address that Wily understands, e.g. :4 or :, or :.,

Events

int rpc_event (Handle*h, Msg *m);

Block until an event is received by h and fill in m. Returns 0 for success, -1 for failure. After a successful call to rpc_event, you will need to free(m->s).

char *rpc_bounce(Handle *h, Msg *m)

Returns m to Wily, and frees m->s. A precondition is that m must be an event previously filled in by rpc_event. Useful for events which we receive but we'd rather Wily took the default action on.

Bool rpc_event_iswaiting(Handle*h);

Returns true if rpc_event could be called without needing to block. Only useful if your program is reading from other input sources as well. (e.g. see win.c)

Message-Passing Interface

struct Msg {
        Mtype   t;	/* message type */
        Id      m;      /* message */
        Id      w;      /* window */    
        Range   r;
        uchar   flag;
        char    *s;
};

All the fields except the message type are optional. The string s is always null-terminated, is in UTF format, must be a complete UTF sequence, and is of zero length if not being used.

These are the message types declared in msg.h, with brief descriptions, and important parameters:

Request/Reply/Error

WRerror(s)

Message type returned by wily if there's an error with some request. s contains an error message. On the other hand, if the request completes successfully wily returns a message with message type one greater than the message type of the request.

WMlist

Asks wily to send a list of windows currently displayed.

WRlist(s)

Wily's reply to WMlist. Includes a string containing one line per active window. Each line contains a name and an id (as a textual number) separated by whitespace.

WMnew(s,flag)

Asks wily to create a window with a given label (which may be an existing file). If and only if flag is set, backups will be kept for the new window.

WRnew(w)

The id of the newly created window.

WMattach(w,flag)

Asks wily to send to the client events associated with this window, and matching the event mask set in flag

WRattach()

WMdetach(w)

Asks wily to stop sending to the client events associated with this window.

WRdetach()

WMsetname(w,s)

Change the name of this window.

WRsetname()

WMsettools(w,s)

Change the tools for this window. Tools are simply words appearing in the tag of a window, after the window name and before the 'pipe' symbol. They are (will be) automatically maintained, i.e. they will be replaced if accidentally deleted.

WRsettools()

WMread(w,r)

Read the text from w in range r.

WRread(s)

The requested text, as a UTF string.

WMreplace(w,r,s)

Replace the text from w in range r with UTF string s

WRreplace()

WMexec(w,s)

Act as if s had been clicked with b2 in w

WRexec()

WMgoto(w,r,flag,s)

Act as if s had been clicked with B3 in w at r. If flag is set then "dot" is set by wily, otherwise we return the window and range that would have been jumped to, but don't actually change "dot".

WRgoto(w,r)

The window and range we went to.

WMfencepost()

Not used for any message. Messages with Mtype less than WMfencepost are to do with our requests. Messages with Mtype greater than WMfencepost are events.

Events

These are the messages sent by Wily to clients who request to be notified of events in a particular window using WMattach

The event types are used to form a mask to be given as a parameter to the WMattach request. For example, to request only exec and goto events, set m.f = WEexec|WEgoto before sending message m

WEexec(w,s)

s was selected with the middle (execute) button somewhere in w

WEgoto(w,r,s)

s was selected with the right (goto) button at r in w

WEdestroy(w)

w has been destroyed.

WEreplace(w,r,s)

The text at r in w was replaced by s.

Packing and Unpacking Messages

int msg_size (Msg *m);

Returns the size of buffer that will be needed to "flatten" m

void msg_flatten (Msg*m, char*buf);

"Flatten" m into buf, which must have enough storage space allocated.

ulong msg_bufsize (char*buf);

Returns the total size of the flattened message starting at buf. Assumes that at least HSIZE bytes are stored in buf. A useful macro is #define FULLMSG(ptr,n) ( (n) >= HSIZE && (n) >= msg_bufsize(ptr) )

int msg_init (Msg*m, char*buf);

The reverse of msg_flatten. Fills in the fields of 'm' using the flat representation starting at buf Msg_init assumes that buf holds the complete message. m->s will point to an area within buf, but buf itself is not modified.

void msg_print (Msg *m);

Print a human-readable form of m to stderr