DinX API Manual

This document describes the Application Programming Interface that is presented by the DinX windowing system. DinX is made up of the following components:

Windowing module

At the heart of the DinX system is the windowing module. Its basic job is to provide the abstraction of a set of independent top-level windows which can share a framebuffer without interfering with each other. A window is a rectangular area of the display with a certain position in the stacking order among the other windows. Client programs may draw to their windows by writing commands to the windowing module. All drawing is clipped within the destination window, and is also clipped to the edges of other windows higher in the stack which may obscure the target window.

At any given moment, a window may be mapped or unmapped. A new window is unmapped when created, which means that it holds a position in the stacking order but is not visible, does not participate in clipping, and cannot be drawn to. Only the window server may map and unmap windows, usually at the request of the client program via a message. The same goes for setting the size and position of a window.

Geometry

Coordinates in DinX are int values. Vectors and rectangles are defined as follows:

#include <linux/dinx.h>   

typedef struct dvect {
	int	x, y;
} dvect;

typedef struct drect {
	int	x1, y1;
	int	x2, y2;
} drect;
This diagram shows a rectangle with the coordinates marked on the edges between pixels

It is important to note that coordinates are not pixel numbers, rather they are offsets to the dividing lines between pixels. The diagram above shows the rectangle (x1 = 3, y1 = 2), (x2 = 9, y2 = 6). So when a rectangle is drawn to a buffer, it includes the pixels [x1 <= x < x2), [y1 <= y < y2).

Each window has a bounds rectangle and an origin vector. The window origin is relative to the origin of the display (top-left corner) and the bounds is relative to the origin. So a window whose origin is at the top-left of the window would have a bounds rectangle with (x1 = 0, y1 = 0), (x2 = width, y2 = height).

Vectors and rectangles that are relative to the origin of the display are said to be in the global coordinate space. Coordinates that are relative to the window origin are said to be in the window coordinate space.

Window identifiers

The windowing module uses unsigned long values to identify client windows when communicating with the server. These values are actually pointers to kernel data structures, but they are checked for validity before being dereferenced. A window identifier has the same lifetime as the window itself.

Colour values

Colour values for solid fills, palette entries etc. are represented as unsigned long words. Where more than one display pixel value will fit in a word, the pixel value is repeated throughout the word in the same packing as the display. This relieves the blitter from shifting a pixel value when performing word-wide accesses.

The windowing module does not deal with configuring display palettes. This is left to the server program, which should operate directly on the framebuffer device. Client window programs should communicate with the server to request palette entries, pixel format information etc.

Window trees

DinX does not deal with child windows, as it is assumed that these would be managed by the same client program that owns the top-level window. Putting the logic for trees of child windows in a user-space shared library greatly simplifies the kernel module and its API, which occupies precious non-swappable memory space.

Despite the fact that DinX only deals directly with lists of top-level windows, each such list is referred to here as a window tree. Each tree is managed by a server process attached to the /dev/dinxsvr node for that tree. Currently the number of window trees is limited to sixteen.

Device nodes

The DinX system presently uses character device number 60, which is allocated for local/experimental use. This is likely to change in the future. The minor number has the following format:

Bit:      7           6           5           4           3           2           1           0     
Meaning:
Server
reserved
Tree number

The `Server' flag is zero for dinxwin window nodes, and one for dinxsvr server nodes. For each of the sixteen trees (specified by the lower four bits) there should be a window node and a tree node. A typical set of device nodes might look like this:

lrwxrwxrwx   1 root     root            8     /dev/dinxwin -> dinxwin0
crw-rw-rw-   1 root     root      60,   0     /dev/dinxwin0
crw-rw-rw-   1 root     root      60,   1     /dev/dinxwin1
crw-rw-rw-   1 root     root      60,   2     /dev/dinxwin2
crw-rw-rw-   1 root     root      60,   3     /dev/dinxwin3
crw-rw-rw-   1 root     root      60,   4     /dev/dinxwin4
crw-rw-rw-   1 root     root      60,   5     /dev/dinxwin5
crw-rw-rw-   1 root     root      60,   6     /dev/dinxwin6
crw-rw-rw-   1 root     root      60,   7     /dev/dinxwin7
crw-rw-rw-   1 root     root      60,   8     /dev/dinxwin8
crw-rw-rw-   1 root     root      60,   9     /dev/dinxwin9
crw-rw-rw-   1 root     root      60,  10     /dev/dinxwin10
crw-rw-rw-   1 root     root      60,  11     /dev/dinxwin11
crw-rw-rw-   1 root     root      60,  12     /dev/dinxwin12
crw-rw-rw-   1 root     root      60,  13     /dev/dinxwin13
crw-rw-rw-   1 root     root      60,  14     /dev/dinxwin14
crw-rw-rw-   1 root     root      60,  15     /dev/dinxwin15
lrwxrwxrwx   1 root     root            8     /dev/dinxsvr -> dinxsvr0
crw-------   1 root     root      60, 128     /dev/dinxsvr0
crw-------   1 root     root      60, 129     /dev/dinxsvr1
crw-------   1 root     root      60, 130     /dev/dinxsvr2
crw-------   1 root     root      60, 131     /dev/dinxsvr3
crw-------   1 root     root      60, 132     /dev/dinxsvr4
crw-------   1 root     root      60, 133     /dev/dinxsvr5
crw-------   1 root     root      60, 134     /dev/dinxsvr6
crw-------   1 root     root      60, 135     /dev/dinxsvr7
crw-------   1 root     root      60, 136     /dev/dinxsvr8
crw-------   1 root     root      60, 137     /dev/dinxsvr9
crw-------   1 root     root      60, 138     /dev/dinxsvr10
crw-------   1 root     root      60, 139     /dev/dinxsvr11
crw-------   1 root     root      60, 140     /dev/dinxsvr12
crw-------   1 root     root      60, 141     /dev/dinxsvr13
crw-------   1 root     root      60, 142     /dev/dinxsvr14
crw-------   1 root     root      60, 143     /dev/dinxsvr15

Client dinxwin nodes

Each dinxwin node may be opened many times by the same client process and by seperate processes. Each new file stream creates a new window in the tree specified by the minor number of the node opened. The newly created window is unmapped and has zero size. The window will continue to exist until the file stream is closed. Opening may fail with one of the following error numbers:

ENOMEM The module was unable to allocate internal data structures.
EPIPE There is no server attached to the corresponding dinxsvr node.

Server dinxsvr nodes

Each dinxsvr node may be opened only once at a time. While a server is attached, the corresponding window tree is available for client programs. When the server detaches by closing the file stream, any remaining client programs still attached to the tree will receive a SIGPIPE signal when next they attempt to read or write events or commands. A new server may not attach to the tree until all the client programs have detached. Opening the dinxsvr node may fail with the following error number:

EBUSY The tree already has a server attached, or is waiting for
remaining clients to detach after the previous server closed.

Writing commands

All data written to DinX device nodes must be in the form of fixed-size command structures, contained in union dcommand defined in <linux/dinx.h>. Every write() operation must pass a buffer that is at least sizeof(union dcommand) bytes in size.

Some command structures contain pointers into user-space memory. Depending on the command, the memory pointed to may be read by the module (in which case the pointer will have a const qualifer) or it may be written back. The latter case is equivalent to calling a function with a pointer to an area where it may write results for return to the caller.

Passing variable-length data by reference in this way keeps the device protocol simple, because it always deals with fixed-size quantities. It is also efficient, because it eliminates an unneccessary copy of the data into kernel buffer memory before it is operated on. For example, blitting operations that copy raw pixmap data to a window can copy directly from the client's buffer to the framebuffer.

The return value from the write() operation is the number of bytes successfully written, or -1 and an error code in errno, as one would expect. The module will return errors that arise while performing a command; for example if the client writes a fill_rect command with an invalid pointer to the pixmap source buffer, an EFAULT error will result. If the failing command was preceeded by one or more successful commands in the same write() operation, the successful byte count will be returned. Then when the program retries the failing command, the error code will be returned. This prevents repeating the successful commands, and allows the offending command to be located in the command stream.

The first element of any command is an enumerated type value that specifies the command. Command names are of the form DCOMMAND_FILL_RECT, for example. The format of the rest of the buffer must correspond to the appropriate structure for that command, e.g. struct dcommand_fill_rect.

Errors that may be encountered when writing commands:

EINVAL The size of the buffer written is smaller than a complete command, the type of the command is invalid, or some other paramater in the command structure is invalid.
EFAULT The command structure contains a pointer to a buffer that is outside the accessible address space.
ENOSYS The command specifies a blitting operation that is not implemented by the current blitter. (Not all blitters implement all copy operations, see the next chapter for details.)
EPIPE The server for this window tree has detached.
ENOMEM The command was unable to allocate memory, e.g. to add an event to a queue.

Reading events

All data read from DinX device nodes is in the form of fixed-size event structures, contained in union devent. Every read() operation must provide a buffer that is at least sizeof(union devent) bytes in size.

The first element of every event structure is an enumerated type value that specifies the event. Event names are of the form DEVENT_REDRAW, for example. The format of the rest of the buffer corresponds to the appropriate structure for the event, e.g. struct devent_redraw.

Both blocking and non-blocking reading is supported. A read() operation will block until a complete event is ready, unless O_NONBLOCK is specified for the file stream. The poll(), and therefore select(), operations are also supported.

Errors that may be encountered when reading events:

EINVAL The size of the buffer provided is smaller than a complete event.
EAGAIN No event is waiting to be read, and O_NONBLOCK was specified.
EINTR A signal occured while waiting for an event.

Client window commands

The command structures described below are elements of the command union:
union dcommand {
	enum dcommand_type		type;
	struct dcommand_message		message;
	struct dcommand_fill_rect	fill_rect;
	struct dcommand_move_rect	move_rect;
	struct dcommand_copy_rect	copy_rect;
	...
};

For example, to send a DCOMMAND_FILL_RECT command:

	union dcommand command;
	
	command.type = DCOMMAND_FILL_RECT;
	command.fill_rect.rect = some_rectangle;
	command.fill_rect.col = some_colour;
	write(window_fd, &command, sizeof(union dcommand));

DCOMMAND_MESSAGE

Send a message to the server.

struct dcommand_message {
	enum dcommand_type	type;	/* DCOMMAND_MESSAGE */
	unsigned long		win;
	char			data[DINX_MESSAGE_SIZE];
};
win Ignored in the case of messages sent by windows.
data Contents of the message. The format of this block is defined by the server.
Refer to the chapter on the server program, and <dinx/messages.h>.

DCOMMAND_FILL_RECT

Fill a rectangular portion of the window with a solid colour.

struct dcommand_fill_rect {
	enum dcommand_type	type;	/* DCOMMAND_FILL_RECT */
	drect			rect;
	unsigned long		col;
};
rect The rectangle to be filled, in window coordinates.
col The colour with which to fill, as a pixel value.

DCOMMAND_MOVE_RECT

Move a rectanglular area of the window by a vector offset.

struct dcommand_move_rect {
	enum dcommand_type	type;	/* DCOMMAND_MOVE_RECT */
	drect			rect;
	dvect			moveby;
};
rect The destination rectangle, in window coordinates.
moveby The vector offset, which points from the source rectangle to the
destination rectangle. The source rectangle is implied by rect and moveby.

This diagram shows the source and destination rectangles and the moveby vector

This command generates redraw events for the portions of the destination rectangle for which the source rectangle is not visible, as shown in the diagram above.

DCOMMAND_COPY_RECT

Copy a pixmap from a buffer into a rectangle in the window, optionally performing colour space conversion for each pixel.

struct dcommand_copy_rect {
	enum dcommand_type	type;	/* DCOMMAND_COPY_RECT */
	drect			dest;
	dvect			base;
	const void *		src;
	int			src_width;
	int			src_bpp;
	const unsigned long *	pal;
};
dest The destination rectangle, in window coordinates.
base Point in the window that corresponds to the top-left
of the entire pixmap, i.e. src. May be outside the window.
src Pointer to the start of source pixmap data.
src_width Bytes per line in the source pixmap data. This implies that
each line of pixmap data must begin on a byte boundary.
src_bpp Depth (bits per pixel) of the source pixmap. Not all blitters
will copy from all source depths, see the next chapter on blitters.
pal Pointer to an array of palette entries, or NULL if no palette conversion
is required. The palette contains one unsigned long display
pixel value for each possible source pixel value, so the size of the
palette is determined by src_bpp.

This diagram shows the dest rectangle, base vector, src pointer and src_width.

The diagram above shows a portion of a source image (Tux's head) being copied to a window. The base vector positions the whole image, while the dest rectangle specifies which part of it is to be copied.

Server commands

The command structures described below are elements of the command union:
union dcommand {
	enum dcommand_type		type;
	struct dcommand_message		message;
	...
	struct dcommand_get_status	get_status;
	struct dcommand_set_bounds	set_bounds;
	struct dcommand_window		window;
	struct dcommand_select_fb	select_fb;
	...
};

DCOMMAND_MESSAGE

Send a message to a window.

struct dcommand_message {
	enum dcommand_type	type;	/* DCOMMAND_MESSAGE */
	unsigned long		win;
	char			data[DINX_MESSAGE_SIZE];
};
win Identifies the window to send the message to.
data Contents of the message. The format of this block is defined by the server.
Refer to the chapter on the server program, and <dinx/messages.h>.

DCOMMAND_GET_STATUS

Get the current state of a window.

struct dcommand_get_status {
	enum dcommand_type	type;	/* DCOMMAND_GET_STATUS */
	unsigned long		win;
	struct dwindow_status *	status;
};
win Identifies the window to get.
status Pointer to a buffer where the status information will be written.

The returned structure is defined as follows:

struct dwindow_status {
	unsigned long	nextabove;
	unsigned long	nextbelow;
	drect		bounds;
	dvect		origin;
	int		flags;
	int		event_count;
};
nextabove Identifies the window immediately above in the stacking order, or 0 if none.
nextbelow Identifies the window immediately below in the stacking order, or 0 if none.
bounds Current bounds rectangle, in window coordinates.
origin Current origin vector, in global coordinates.
flags Current flags; DINX_MAPPED indicates window is mapped.
event_count Number of events waiting in event queue.

DCOMMAND_SET_BOUNDS

Set the bounds and origin of a window.

struct dcommand_set_bounds {
	enum dcommand_type	type;	/* DCOMMAND_SET_BOUNDS */
	unsigned long		win;
	drect			bounds;
	dvect			origin;
};
win Identifies the window to set.
bounds New bounds rectangle, relative to the new origin.
origin New origin vector, in global coordinates.

DCOMMAND_MAP
DCOMMAND_UNMAP
DCOMMAND_RAISE
DCOMMAND_LOWER
DCOMMAND_PREFER_MOVE
DCOMMAND_PREFER_REDRAW

Map or unmap a window.
Raise or lower a window by one slot in the stacking order.
Set a window's preference for moving or redrawing.

struct dcommand_window {
	enum dcommand_type	type;	/* DCOMMAND_MAP/UNMAP/RAISE/LOWER/
						PREFER_MOVE/PREFER_REDRAW */
	unsigned long	win;
};
win Identifies the window to manipulate.

The move vs. redraw preference affects how the window's contents is updated when it is repositioned with set_bounds. The default is to use move_rect to move the window to the new position. This can be very slow on video hardware that is slow to read from, e.g. PC systems. In these cases, the redraw preference results in redraw events being generated for all visible parts of the window when it is moved. This preference can be set on a per-window basis, as the cost of a redraw depends on the complexity of the window's contents.

DCOMMAND_REFRESH

Cause the entire tree to be redrawn. This might be used after a virtual console switch, for example. No parameters.

DCOMMAND_DISABLE

Redirect all blitting routines to a set of do-nothing stubs. This should be used before allowing a virtual console switch away from the server. No parameters.

DCOMMAND_ENABLE

Direct all blitting routines to the blitter for the currently selected framebuffer. This should be done after selecting a framebuffer, and after a virtual console switch to the server. No parameters.

DCOMMAND_SELECT_FB

Select the framebuffer to associate with the window tree. This would normally be performed only once, during server startup. May return an error of ENOSYS if there is no blitter available that can handle the framebuffer.

struct dcommand_select_fb {
	enum dcommand_type	type;	/* DCOMMAND_SELECT_FB */
	int			framebuffer;
};
framebuffer The framebuffer number to attach to. Not to be confused with
the virtual console number, which is handled in the server.

Client window events

The event structures described below are members of the event union:
union devent {
	enum devent_type		type;
	struct devent_message		message;
	struct devent_redraw		redraw;
	...
};

To receive and dispatch an event, you might use code like this:

	union devent event;

	read(window_fd, &event, sizeof(union devent));
	switch (event.type) {
	case DEVENT_REDRAW:
		....
	}

DEVENT_MESSAGE

The server sent a message to the window.

struct devent_message {
	enum devent_type	type;	/* DEVENT_MESSAGE */
	unsigned long		win;
	char			data[DINX_MESSAGE_SIZE];
};
win Undefined in the case of messages sent to windows.
data Contents of the message. The format of this block is defined by the server.
Refer to the chapter on the server program, and <dinx/messages.h>.

DEVENT_REDRAW

A portion of the window needs to be redrawn. This occurs when a window is mapped, moved, or exposed by some other window that was previously obscuring it. It also occurs when part of the source rectangle for a DCOMMAND_MOVE_RECT was not available.

When a non-rectangular area of the window needs to be redrawn, a number of redraw events are generated - one for each rectangular portion of the dirty area.

struct devent_redraw {
	enum devent_type	type;	/* DEVENT_REDRAW */
	drect			rect;
	int			more;
};
rect The rectangle that needs redrawing, in window coordinates.
more If non-zero, this indicates that the next event in the queue is also a redraw event.

There are two strategies for dealing with redraws. The obvious and usually most appropriate is just to redraw each rectangle as events come in. The more flag allows another strategy, useful when it is inefficient to redraw many small parts of the window. The client program can choose to ignore redraw events that are followed by more redraw events, and then when the last redraw is received (i.e. more is false) to redraw the whole window. A more complicated option is to accumulate a dirty region as the events arrive, and then to redraw it all at once.

Server events

The event structures described below are members of the event union:
union devent {
	enum devent_type		type;
	struct devent_message		message;
	...
	struct devent_win_status	win_status;
	...
};

DEVENT_MESSAGE

The window sent a message to the server.

struct devent_message {
	enum devent_type	type;	/* DEVENT_MESSAGE */
	unsigned long		win;
	char			data[DINX_MESSAGE_SIZE];
};
win Identifies the window that sent the message.
data Contents of the message. The format of this block is defined by the server.
Refer to the chapter on the server program, and <dinx/messages.h>.

DEVENT_WIN_OPENED
DEVENT_WIN_CLOSED
DEVENT_WIN_EMPTY

A new window has opened, and placed at the top of the stacking order.
A window has closed.
A window has emptied it's event queue. This allows the server to hold off sending messages to avoid creating a large queue.

struct devent_win_status {
	enum devent_type	type;	/* DEVENT_WIN_OPENED / _CLOSED / _EMPTY */
	unsigned long		win;
};
win Identifies the window that opened, closed or emptied.

Blitter modules

Each blitter module contains routines for drawing in framebuffers of a certain depth. Only the blitters for the framebuffers in use actually need to be present in the kernel, saving some non-swappable memory. Each blitter module registers itself with the windowing module when it is inserted into the kernel. All interaction with the blitters is via the windowing module.

All blitters provide fill_rect and move_rect operations. In the following sections we describe which copy_rect operations are available, and the API that blitter modules must conform to.

Copy operations

All blitters provide copy_rect operations that copy from a source buffer to a framebuffer that is of the same or greater bit depth. Blitters will not copy from a source buffer that is deeper than the framebuffer.

DinX is designed so that blitters for small systems with shallow bit-depth (e.g. 4bpp) buffers are the smallest and simplest, while blitters for deeper buffers (e.g. 24bpp) contain routines to handle source data of most bit-depths. This reduces code size on small systems. It also simplifies the API, because blitting from a deep source pixmap to a shallow display buffer can require extra palette information from the server. If a client program needs to draw to a 4bpp display from a 24bpp source buffer (for example) it should perform the conversion in user space (perhaps dithering in the process) and then copy the converted buffer raw to the display.

The following tables show which operations are made available by each of the blitters, and describe how each operation works.

Source
Destination 1bpp 2bpp 4bpp 8bpp 16bpp 24bpp 32bpp
dinx-l1.o: pal/raw - - - - - -
dinx-l2.o: pal pal/raw - - - - -
dinx-l4.o: pal pal pal/raw - - - -
dinx-l8.o: pal pal pal pal/raw - - -
dinx-l16.o: pal pal pal pal raw - -
dinx-l24.o: pal pal pal pal cube raw -
dinx-l32.o: pal pal pal pal cube pad raw

pal Each pixel of the source is an index into the required palette.
pal/raw The palette is optional - if NULL, the data is copied raw.
raw The palette is ignored, the data is copied without conversion.
cube The 16bpp source data is treated as 5-6-5 RGB colour-cube data,
and is expanded into the 8-8-8 RGB 24bpp or 32bpp destination.
pad The 24bpp source data is padded to the 32-bit aligned destination.

Blitter API

When a blitter module is inserted into the kernel, its init_module() routine should register the blitter with the windowing module, by calling register_blitter(). When it is removed from the kernel, it should unregister by calling unregister_blitter(). These functions are exported by the windowing module, and are declared as follows.

extern int register_blitter(struct dinx_blitter *blitter);
extern int unregister_blitter(struct dinx_blitter *blitter);

The struct blitter structure contains the name of the blitter to appear in /proc/dinx, and pointers to the blitter functions, defined as follows.

struct dinx_blitter {
	struct dinx_blitter *next;
	const char *name;
	int	(*can_handle)(struct display *);
	void	(*open)(void);
	void	(*close)(void);
	void	(*fill_rect)(struct display *, const drect *, unsigned long);
	void	(*move_rect)(struct display *, const drect *, dvect);
	int	(*copy_raw)(struct display *, const drect *, const dvect *,
				const void *, int, const unsigned long *);
	int	(*copy_1bpp)(struct display *, const drect *, const dvect *,
				const void *, int, const unsigned long *);
	int	(*copy_2bpp)(struct display *, const drect *, const dvect *,
				const void *, int, const unsigned long *);
	int	(*copy_4bpp)(struct display *, const drect *, const dvect *,
				const void *, int, const unsigned long *);
	int	(*copy_8bpp)(struct display *, const drect *, const dvect *,
				const void *, int, const unsigned long *);
	int	(*copy_16bpp)(struct display *, const drect *, const dvect *,
				const void *, int, const unsigned long *);
	int	(*copy_24bpp)(struct display *, const drect *, const dvect *,
				const void *, int, const unsigned long *);
};

next For use by the windowing module, should be NULL.
name Points to a string that is the name of the blitter, as it is to appear in /proc/dinx.
can_handle Return non-zero if this blitter can handle the framebuffer.
open Called when a server selects this framebuffer, the blitter module should increment its usage count here.
close Called when a server selects some other framebuffer, the blitter module should decrement its usage count here.
fill_rect Fill a rectangle of the display with a solid colour. The rectangle passed has already been clipped to within the display.
move_rect Move a rectangular area of the display by a vector offset. The rectangle passed is the destination area. The vector is the offset from the source rectangle to the destination. Both the source and offset are already clipped within the display.
copy_* Copy a pixmap from user space to the display. The source data area has already been checked with access_ok(). The palette (if any) has already been copied to kernel memory. Parameters have the same meanings as described for DCOMMAND_COPY_RECT, above. Return 0 on success, or -EFAULT, -ENOSYS etc.
copy_raw The source is the same depth (bpp) as the display, and no palette transformation is to be applied. Ignore palette.
copy_1bpp The source is 1bpp, the palette contains 2 entries.
copy_2bpp The source is 2bpp, the palette contains 4 entries.
copy_4bpp The source is 4bpp, the palette contains 16 entries.
copy_8bpp The source is 8bpp, the palette contains 256 entries.
copy_16bpp The source is 16bpp 5-6-5 RGB, expand into 8-8-8 RGB for 24bpp or 32bpp displays. Ignore palette.
copy_24bpp The source is 24bpp 8-8-8 RGB, pad for 32bpp 8-8-8-x RGBx displays. Ignore palette.

Copyright © 1999 Ben Williamson