NAME Wily - Perl extension for the Wily editor/user interface ABSTRACT This is a Perl5 module that provides a (just about) object- oriented interface to the Wily editor/user interface. Perl client programs can connect to Wily, and send messages to it to manipulate the contents of Wily windows. Client programs can also request that Wily forwards particular kinds of events to the client program for processing. This module requires Perl 5.002 or later. THIS IS STILL AN ALPHA RELEASE OF THIS MODULE. Nasty things may happen if you use it. Sorry. INSTALLATION The tar file should be unpacked within the top-level directory of your Wily distribution. This will create a directory "perl" at the same level as, say, Doc, wily, sam and misc. If you've unpacked somewhere else, just move the files across. Before you install this module, apply the patch in msg/rpc.patch - this patches ../wily/libmsg/rpc.c to define another function, fileno(). This allows you to select() on input from Wily. After that, installation is pretty simple: perl Makefile.PL make make test make install Note that running the tests requires that you've got an instance of Wily running (and it must be the same release of Wily that you've just compiled the module with.:-) ). Also, to do "make install", you will probably have to be root. That's life with Perl modules. If you've got any sense, and you're using wily. 0.12.11, you should also install the patch that is in wily.patch. This fixes a couple of problems with Wily's event handling - see HANDLING EVENTS. SYNOPSIS use Wily; use Wily qw(...); $wily = new Wily; $win = $wily->create('/win/name'); %wins = $wily->list(); $id = $win->id(); if ($win->isattached()) { ... } $win->setname('/mumble'); $win->settools('foo bar baz'); $win->attach($mask); $win = $wily->open($name, $id); $text = $win->read($p0, $p1); $win->replace($p0, $p1, $newtext); $win->exec($cmd); $win->goto($q0, $q1, $addr, $setdot); $length = $win->length(); if ($win->wouldblock()) { ... } $msg = $wily->event(); $msg = $win->event(); $win->bounce($msg); $wily->bounce($msg); ($type, $msgid, $winid, $p0, $p1, $flag, $string) = unwrapmsg($msg); $msg = wrapmsg($type, $msgid, $winid, $p0, $p1, $flag, $string); $fd = $wily->fileno(); $errmsg = $wily->errstr(); DESCRIPTION CONNECTING TO WILY $wily = new Wily; This connects to Wily. It doesn't do anything else, so you're not going to receive any events, or see any new windows appear. $wily is an object that you can use to communicate with wily, however. There is no disconnect operation: this happens automatically when the Perl process dies. CREATING NEW WINDOWS $win = $wily->create($winname); This returns a new object that you can use for performing operations on the window. The window should appear on Wily's screen immediately. You can use $win for sending messages to Wily immediately - see below. Right now, you can't specify whether the window refers to a real or virtual file. This method used to be called newwin(), but was renamed to open() as of 0.03. newwin() will still work, but you'll get a non-suppressable warning. LISTING EXISTING WINDOWS %list = $wily->list(); or %list = $win->list(); The returned hash is the paths of each window currently open in Wily, indexed by their window ids. Note that it doesn't matter whether you use the connection object or a window object to get the list. OPENING EXITING WINDOWS $win = $wily->open($name); $win = $wily->open($id); $win = $wily->open($name, $id); This method returns a window object that is connected to the named window, but not attached to it; you won't receive any events from it yet. You can use the window's name, id or both to specify the window to be opened. This method was briefly called oldwin(), which was pretty daft. It's also the reason why newwin() was renamed to create(). WINDOW IDENTIFIERS $id = $win->id(); This just returns the id of the current window. You only really need this for finding out the window's pathname, with the list() method above. Window objects will return their identifiers, but the connection object will return a fake ID (because it's not really a window). This ID has the value $MainCon, which you can reference if you include it in the "use" statement, or if you explicitly give the package name: $Wily::MainCon. CHANGING TAG LINES $win->setname($newname); $win->settools($newtools); These methods allow you to change the pathname or tool list for a window. They return true if successful. Note that the tool listing is a single string. It should probably be a list, but it isn't, at the moment. READING WINDOW TEXT $text = $win->read($p0, $p1); This method returns a single UTF string, containing the text between character offsets $p0 and $p1 ($p1 is the offset of the first character AFTER the returned range). If the read fails - such as because the range is invalid - the method returns undef. WRITING WINDOW TEXT $win->replace($p0, $p1, $newtext); This method is used for updating window contents. The text specified by the range $p0-$p1 is removed, and $newtext is inserted in its place. If $p0==$p1, this is just a simple insertion. If $newtext is the empty string, then this is just a deletion. Returns false if it fails. SEARCHING AND ADDRESSES $win->goto($q0, $q1, $addr, $setdot); A slightly more complex method, this. It's used for two things: resolving a textual address into a character offset range, and for setting dot. $q0 and $q1 specify the starting range for the search. $addr is the text to search for. It can be any Wily address: If Wily manages to find what you're searching for, it sets the values of $q0 and $q1 to its address. If $setdot is also true, it also moves the window's current selection to be where the matched text is. Returns true if the pattern is found, and false otherwise. There are a couple of bugs in this method. The first is that you should be able to specify that dot will be used, and that $q0 and $q1 are ignored. The second is that if the address happens to reference a different window, it should return the window id of the new window. WINDOW CONTENTS LENGTH $len = $win->length(); This method is just a wrapper around goto(), but it seems to be quite handy. SENDING COMMANDS TO WILY $win->exec($cmd); Runs $cmd in the context of the window. $cmd can be a shell command, or it can be a Wily builtin. Returns false if something doesn't work. HANDLING EVENTS Attaching Initially, window objects aren't attached to the window they're associated with. This means that Wily handles all events itself. To start receiving events for a window you've created, use: $win->attach($mask); Mask specifies which events you're interested in. The events are: Just add the events that you're interested in together, as in: $win->attach($WEexec + $WEgoto); Again, name the events in the "use" statement, or use $Wily::WEexec, etc. Attach returns true for success, and false for failure. To attach to an existing window, use: $win = $wily->open($id); $win->attach($mask); where $id is the ID (or the name) of the window you want to attach to. The resulting object is an attached window object (or undef, if something goes wrong). Version 0.02 used a method called attachid(), which turned out to just a fairly limited improvement over version 0.01 - you could attach to windows just created with newwin(), but not to existing windows. attachid() has been replaced by a combination of open() and attach(); attachid() will still work, but you'll get a non-suppressable warning message. Wily will send goto and exec events directly to your client, without any processing. In contrast, replace events will already have been processed when you receive them; Wily is merely informing you of their occurrence. I'm not sure when wily sends destroy events; so far, I've never seen one. :-) Wily patch The wily.patch file contains a patch to ../wily/external.c - it fixes two problems. The first is that Wily 0.12.11 didn't expect any clients to bounce WEreplace events (after all, Wily's already processed them), so it didn't check for them. The result was that it reports them as unknown events. This patch just makes Wily ignore them. The other problem is that Wily echoes WEreplace events back to the client that sent them in the first place, if that client is attached to the window. This is a little silly, and means that clients have to be extremely careful about event handling. This patch stops Wily doing this, so you know that if you *cause* the event, you're not going to be notified about it too. Detaching $status = $win->isattached(); This method is to determine whether you're attached to the window. The Wily.pm module is supposed to notice the destroy events coming in from Wily, and mark the window object as detached, but since Wily doesn't seem to send these events, this doesn't work at the moment. As for explicitly detaching, there ISN'T a method for doing that. Nor, apparently, can you change the set of events you want to receive. Accepting events You receive events with: $msg = $win->event(); The returned value is an anonymous hash containing the components of the message. The keys are: 't' - the message type. =item 'm' - message ID. =item 'w' - window ID. =item 'p0' and 'p1' - the range. =item 'f' - the flag. =item 's' - the string. so you can do: if ($msg->{'t'} == $WEexec && $msg->{'s'} eq 'Del') { ... } The event() method blocks until it receives an event, so you can use: $win->wouldblock() which returns true if event() would block. For both event() and wouldblock(), if you call them from a window object, they'll only consider events for that window. If you call them from the connection object, they'll return events for any windows. WARNING: This is untested functionality - I *think* it'll queue events for other windows properly, but I haven't had a chance to check it yet. Also, bear in mind that if you have several windows accepting events, you should make sure that you read events for other windows eventually, to drain the queues. In the returned hash, 't' is a numeric value, equal to one of the four event types given previously. 'm' is the identifier of the message. I don't think it's actually useful, but Wily might pay attention to it in bounces - I'm not sure. 'w' is the window identifier of the window this event occurred in. This is necessary if you've got more than one window attached at once, and you're reading with the connection object, instead of the window object. 'p0' and 'p1' are range involved: In both exec and goto events, the range will have been expanded by Wily to something sensible, if it was a single-click event. Offhand, I can't remember what 'f', the flag signifies, in events. 's' is the inserted text, for replace events, the command to be executed in exec events, or the pattern to be searched for, in goto events. If you don't like using the returned hash, you can break it up into a list, using the function "unwrapmsg": ($type, $msgid, $winid, $p0, $p1, $flag, $string) = unwrapmsg($msg); Note that this isn't a method, which means that you have to name it in the "use" statement, or precede it with "Wily::". Bouncing events It's quite common that you're only interested in specific instances of a kind of event. For example, with exec events, you probably only care about the commands your client supports. The bounce() method allows you to send an event back to wily, if you're not interesting in handling it: $win->bounce($msg); $wily->bounce($msg); It doesn't matter whether you use the connection object or a window object - the information needed is all in the message anyway. If you to construct the message yourself, you can use the function "wrapmsg": $msg = wrapmsg($type, $msgid, $winid, $p0, $p1, $flag, $string); Again, this is a function, not a method, so you'll have to import it. WAITING ON MULTIPLE INPUT SOURCES If you need to select() on Wily's connection, because you've got other input sources to worry about, then you can use: $fd = $wily->fileno(); This will return a file descriptor that you can use with vec() and select() in the normal Perl way. Note that this relies on a small modification to libmsg/rpc.c, so you have to apply the patch in the file msg/rpc.patch before it'll work (or, in fact, compile). ERROR MESSAGES The errstr() method is supposed to return any error messages generated by libmsg, but it doesn't appear to be too reliable. Is that ironic, or what? print "Everything's broken: " $wily->errstr(), "\n"; DEBUGGING If you set $Wily::Debugging = 1, then you'll get buckets of diagnostics from the Perl module. If you define -DWILYPERLDEBUG in the Makefile.PLs, and rebuild *everything*, you'll get a little more from the underlying C code, but nothing startling. TESTING The test script, test.pl, attempts to exercise some of the module's functionality in a controlled way. It doesn't try out everything, because I can't think of a way of testing some of the methods where I can confirm their results reliably. You should have an instance of Wily running when you do "make test", and the Wily version must be the same as the one you originally linked the Wily module with, otherwise they probably won't talk to each other. Also check at the beginning of the script, for where it sets the value of $WILYFIFO: # $ Steve_Kilbane@cegelecproj.co.uk