Re: [wmii] [Semi-OT] libixp in wmii

From: Anselm R. Garbe <garbeam_AT_wmii.de>
Date: Thu, 18 May 2006 11:13:23 +0200

Hi David,

first of all, libixp depends on libcext.

On Thu, May 18, 2006 at 08:20:27AM +0000, David Tweed wrote:
> 1) It looks like the ixp_server code gets to run the main
> select()-based event loop, with non-ixp based interactions
> (eg, X events) being incorporated into that?

struct IXPConn {
        int fd;
        IXPServer *srv;
        void (*read) (IXPConn *);
        void (*close) (IXPConn *);
        MapVector map;
        Fcall pending;
        int is_pending;
};

This structure is created for each connection to the IXP server.
Its primary use has been designed with IXP in mind. However the
structure contains two function pointers, 'read' and 'close'.

The select-loop only selects for reading on the fd's (selecting
for writes is not necessary, because it is assumed that writes
are triggered as responses to reads). If select returns an fd
which is ready to be read, the IXP server calls the
IXPConn->read function. If a connection is closed (broken pipe
or whatever reason), IXP server calls IXPConn->close in that
case.

During the fact that X's Display-connections are socket based as
well, and that a select based application can only consist of
one main event loop, this IXPConn is also used for wmiiwm's X
display connection. Normally X applications use a XNextEvent()
based main event loop, which does select() internally and blocks
until an event is present. In wmii this cannot be done, because
defacto-parallel IXP client connections need to be handled,
that's why the IXP main event loop listens for reads also on the
ConnectionNumber(dpy) (which returns the fd of the currently
opened Display-connection of wmiiwm).

The read function for wmiiwm which handles X calls, is a loop
which first checks if there are still pending X events and
processes them until finished, this is the code from
wmii/cmd/wm/event.c:

void
check_x_event(IXPConn *c)
{
        XEvent ev;
        while(XPending(dpy)) { /* main event loop */
                XNextEvent(dpy, &ev);
                if(handler[ev.type])
                        handler[ev.type]) (&ev); /* call handler */
        }
}

In the whole wmii-code this is the only exceptional use of
IXPConn. All other use-cases are IXP protocol related.

> 2) With the current code structure, would it be simple to restructure things so
> something else gets to run the main select()-based event loop and then events on
> the ixp fd's get passed on to the ixp code, or would that require insane amounts of
> modification to libixp?

No, that is already done. The IXP main event loop is such a read
handler as well, in our case it is wmii/cmd/wm/fs.c:

void
new_ixp_conn(IXPConn *c)
{
        int fd = ixp_accept_sock(c->fd);

        if(fd >= 0)
                ixp_server_open_conn(c->srv, fd, do_fcall,
                        ixp_server_close_conn);
}

This is the read handler for new incoming IXP connections. c
points to the server connection, which means c->fd is the
server's fd. 'ixp_accept_sock' can be found in libixp/socket.c.

This handler simply accepts the new connection and creates an
IXPConn with a different read handler, namely 'do_fcall' from
wmii/cmd/wm/fs.c which looks as follows:

static void
do_fcall(IXPConn *c)
{
        static Fcall fcall;
        unsigned int msize;
        char *errstr;

        if((msize = ixp_server_receive_fcall(c, &fcall))) {
                switch(fcall.id) {
                case TVERSION: errstr = xversion(c, &fcall); break;
                case TATTACH: errstr = xattach(c, &fcall); break;
                case TWALK: errstr = xwalk(c, &fcall); break;
                case TCREATE: errstr = xcreate(c, &fcall); break;
                case TOPEN: errstr = xopen(c, &fcall); break;
                case TREMOVE: errstr = xremove(c, &fcall); break;
                case TREAD: errstr = xread(c, &fcall); break;
                case TWRITE: errstr = xwrite(c, &fcall); break;
                case TCLUNK: errstr = xclunk(c, &fcall); break;
                case TSTAT: errstr = xstat(c, &fcall); break;
                default: errstr = Enofunc; break;
                }
                if(errstr)
                        ixp_server_respond_error(c, &fcall, errstr);
        }
        check_x_event(nil);
}

As you see this handler mainly processes T-messages and here
begins the real IXP stuff. Fcall is similiar to the Fcall struct
from lib9p/fcall.h of Plan 9.

This handler also calls check_x_event, because this speeds up
the overall performance of wmii drastically (there are several X
events per second in average).

> 3) Is there any documentation/commentary relating how a particular path is related
> to the qid stuff? (I can sort-of see where the filenames get identified, but looking
> over the code it looks like there are some hardcoded assumptions about what
> paths mean that result in not needing to extract the textual paths: maybe I'm misreading
> it.)

I plan to redesign the filename handling into array-tables for
wmii-4, which might simplify the overall code. First I'll do
that prototypical for the IRC client ii asap.

The most important comment about Qid-calculation in
wmii/cmd/wm/fs.c is this:

/*
 * Qid->path is calculated related to the index of the
 * associated structure.
 * i1 is associated to tag, key, global client, or bar
 * i2 is associated to area
 * i3 is associated to client
 * ie /view/sel/ctl is i1id = sel tag id, i2id = sel area id
 * , i3id = 0 (no id)
 */

Each addressable object (which is not unique) in wmii has an
unique id (which is internal only), e.g. View->id, Area->id,
Frame->id, and Client->id (as most important ones).

The qid path of wmii contains following information:

The file type (Fs* enum entries), and the ids of the associated
indexes. E.g. following data would be packed as follows:

/view/1/0/name (external)
view.data[sel]->area.data[1]->frame.data[0]->client->name (intenal)

To encode a qid.path we need the id's of the indexed objects:

view.data[sel]->id == 2
view.data[sel]->area.data[1]->id == 5
view.data[sel]->area.data[1]->frame.data[0]->id == 1

(The id's are just examples)

In wmii there are functions to lookup indexes for id's for the
opposite way around, e.g. idx_of_frame_id(1) points to:
view.data[sel]->area.data[1]->frame.data[0].

With knowing that we want to pack the path of the client's name
file, with the presented path, the function type_of_name will
return FsFname as type (because the dir_type is FsDclient in
this case), thus we pack the path:

pack_qpath(FsFname, 2, 5, 1);

which addresses
view.data[sel]->area.data[1]->frame.data[0]->client->name
uniquely.

Each fid mapping (IXPMap) knows the complete Qid path, not only
for the file the fid points to, but also the Qid's of the
parents. This is necessary to unpack the name information from
the path.

Unpacking the path is the other way around as packing.

Hope this helped.

Regards,

-- 
 Anselm R. Garbe  ><><  www.ebrag.de  ><><  GPG key: 0D73F361
Received on Thu May 18 2006 - 11:13:23 UTC

This archive was generated by hypermail 2.2.0 : Sun Jul 13 2008 - 16:05:31 UTC