[comp.os.minix] minix graphics available soon

dbell@pdact.pd.necisa.oz (David I. Bell) (04/03/91)

		ANNOUNCING "MINI-X" GRAPHICS ROUTINES FOR MINIX
				David I. Bell
			dbell@pdact.pd.necisa.oz.au


The following is a description of my graphics library for MINIX.
If there is interest in this, I will post it out to the network soon.
There is much that is not complete, but there is enough working to let
people play with it and improve it and give me feedback.

This implementation is inspired by X, and uses many of the same ideas.
However, I have adapted these ideas to MINIX and therefore broken apart
some features into two processes, and also reduced many of the features.
With enough time and effort, it could be enhanced to become even more
X-like someday.  But for the moment, what it does have is still useful
and fun to play with.  As a small joke on the names of MINIX and X, I am
calling it mini-X.  (This name is sure to be confusing unless pronounced
with a slight pause at the hyphen and with the second "i" pronounced as a
long "e".)

The main design goal is not to use too much memory!  In particular, the
graphics device driver in the MINIX kernel uses as little data storage as
possible.  Everything that requires extensive tables or dynamic memory
is not in the kernel, but is instead in a user process known as the
graphics server.  Examples of these are window structures, fonts, and
resource definitions.  Unfortunately, the 64K address space limit for
Intel boxes using non-32 bit mode addressing is too small for this package.
So this will probably only be of use for Bruce Evan's 32-bit kernel for
PC's, and for the other non-IBM computers like Macs and Amigas which do
not have pathetic memory limits.

The second design goal is speed of drawing operations.  The connection
between the graphics library and the graphics device driver is by using
a "/dev/graphics" device.  A set of library routines packages up all
requests into structures which are appended to a buffer in memory.
Many of these structures are variable length, so that for example, a
series of draw line calls will put all the lines into one structure.
When the buffer is full, or when an explicit flush is done, the process
does a single write(2) call to empty the buffer.  The device driver then
uses phys_copy to copy the buffer into its memory, and then interprets
each of the structures in turn, executing the functions.  Thus drawing
requests are buffered similarly to stdio, and a minimum of context switches
are made.  On my 20MH EGA, I can continuously draw over 650 randomly
orientated long lines a second, or draw 66 100-pixel diameter filled
ellipses a second, or have the cursor track the mouse with no flickering.
Text is currently slow, only 400 characters a second, but can be easily
improved.

The final design goal is device independence.  This means that this same
interface will work for all the different types of machines.  I have not
put in PC specific features, but instead stayed general so the code will
(I hope) work on Amigas and Macs without much trouble.  All that needs to
be done is have someone write the device-specific low level routines for
those machines.  As an example of device independence, the number of rows
and columns and colors of the screen is not assumed, but can vary.  Also,
color values can be full 32-bit numbers.  Finally, the code itself is
written in a modular manner, so that new devices can be integrated into
the code easily.  The only major problem I see is that the mouse interface
is not right, since I am not sure yet how to integrate it properly when it
could be a serial mouse, a bus mouse, or a hardware implemented mouse.
The current code assumes a serial mouse driven from user space.

The following is the process structure as I eventually desire it to be:

	MINIX kernel  ------  Graphics Server  -----  Client process 1
					       -----  Client process 2
					       -----        ...

The MINIX kernel does the drawing operations, including accesses to the
screen memory and executing of I/O instructions for the hardware.
This is not absolutely required, but is convenient for now.  To be
made into a user process, MINIX would need enhancing to provide memory
mapping of the screen into the user's address space, and to allow I/O
instructions to be executed by the user process.  Plus this would
require some sort of fast communication between processes.  I avoided
all this and just made a device driver which was easy.

The graphics server lies between the clients and the device driver.
It is a user mode process which runs the windows and resources for the
clients.  This is the similar to the standard "X" server.

The client processes are the actual applications that want to do drawing.
There can be multiple clients running, each outputting to their own windows.
These windows can be graphical, or simply text windows used for commands
and editing.  The only clients I have right now, though, are demo programs.

The above is the desired eventual goal, however the following picture is
the process structure as it actually exists:

	MINIX kernel  ------  Graphics Server + Client code

This means that you can run only one client, and the server code is loaded
together with the client code.  This still gives the single client all of
the features of the desired design, such as windows, events, and so on.
The server code has been written to handle multiple clients, and the
interface between the server and the process is the same as if they were
actual separate processes.  Therefore in order to support multiple clients,
all that is needed is to provide some sort of multi-process communication.

The implementation I have already has rectangular overlapping windows,
automatically changing cursors, graphics contexts, returned keyboard,
mouse, and exposure events, and error handling.  You can draw points,
lines, rectangles, ellipses, bitmaps, and text.  Many things are not
provided such as line width and style, and pixmaps for drawing into memory.
There is no capacity of defining your own fonts (only built-in fonts work).

Checking for button and keyboard events is crude.  I used the O_NONBLOCK
open flags which were already implemented in MINIX, and only made a small
change in tty.c to check for it.  With non-blocking tty input, I could
check both devices.  However, there is no poll or SIGPOLL functionality,
so the code just loops checking constantly.  This is a load on the machine,
but since there is only one client running at a time, and lots of MINIXs
only run one user, it is not too important (for now).

If I post this package, I would expect that people will provide some of
these missing features.  I especially expect that people will help provide
some necessary utilities such as xterm, and kernel support for it all.
In particular, pseudo-ttys and some form of poll are needed desperately!

The following is a correctly working demo program to demonstrate the use
of the graphics routines.  It puts up two windows with borders, draws a
rectangle in one of them, and responds to mouse and keyboard events.
While nothing else is happening, it draws random circles in the small
window.  When mouse is clicked in the large window, it either remembers a
location to draw text at, or clears the window.  When keystrokes are typed
in the large window, the characters are written in the window (with returns
and backspaces handled).  If the mouse is clicked in the small window, the
program exits.  If multiple buttons are clicked outside of both windows,
the program purposely generates a fatal error.


/*
 * Demonstration program for mini-X graphics.
 */
#include <stdio.h>
#include "graphics.h"

/*
 * Definitions to make it easy to define cursors
 */
#define	_	((unsigned) 0)		/* off bits */
#define	X	((unsigned) 1)		/* on bits */
#define	MASK(a,b,c,d,e,f,g) \
	(((((((((((((a * 2) + b) * 2) + c) * 2) + d) * 2) \
	+ e) * 2) + f) * 2) + g) << 9)


static	GR_WINDOW_ID	w1;		/* id for large window */
static	GR_WINDOW_ID	w2;		/* id for small window */
static	GR_GC_ID	gc1;		/* graphics context for text */
static	GR_GC_ID	gc2;		/* graphics context for rectangle */
static	GR_GC_ID	gc3;		/* graphics context for circles */
static	GR_COORD	begxpos;	/* beginning x position */
static	GR_COORD	xpos;		/* x position for text drawing */
static	GR_COORD	ypos;		/* y position for text drawing */
static	GR_SCREEN_INFO	si;		/* information about screen */

void	errorcatcher();			/* routine to handle errors */


main(argc, argv)
	char	**argv;
{
	GR_EVENT	event;		/* current event */
	GR_BITMAP	bitmap1fg[7];	/* bitmaps for first cursor */
	GR_BITMAP	bitmap1bg[7];
	GR_BITMAP	bitmap2fg[7];	/* bitmaps for second cursor */
	GR_BITMAP	bitmap2bg[7];

	if (GrOpen() < 0) {
		fprintf(stderr, "cannot open graphics\n");
		exit(1);
	}
	GrGetScreenInfo(&si);

	GrSetErrorHandler(errorcatcher);

	w1 = GrNewWindow(GR_ROOT_WINDOW_ID, 100, 50, si.cols - 120,
		si.rows - 60, 1, 6, 15);
	w2 = GrNewWindow(GR_ROOT_WINDOW_ID, 6, 6, 70, 40, 2, 2, 15);

	GrSelectEvents(w1, GR_EVENT_TYPE_BUTTON_DOWN |
		GR_EVENT_TYPE_KEY_DOWN | GR_EVENT_TYPE_EXPOSURE);
	GrSelectEvents(w2, GR_EVENT_TYPE_BUTTON_DOWN);
	GrSelectEvents(GR_ROOT_WINDOW_ID, GR_EVENT_TYPE_BUTTON_DOWN);

	GrMapWindow(w1);
	GrMapWindow(w2);

	gc1 = GrNewGC();
	gc2 = GrNewGC();
	gc3 = GrNewGC();

	GrSetGCForeground(gc1, 4);
	GrSetGCBackground(gc1, 6);
	GrSetGCForeground(gc2, 5);

	bitmap1fg[0] = MASK(_,_,_,X,_,_,_);
	bitmap1fg[1] = MASK(_,_,_,X,_,_,_);
	bitmap1fg[2] = MASK(_,_,_,X,_,_,_);
	bitmap1fg[3] = MASK(X,X,X,X,X,X,X);
	bitmap1fg[4] = MASK(_,_,_,X,_,_,_);
	bitmap1fg[5] = MASK(_,_,_,X,_,_,_);
	bitmap1fg[6] = MASK(_,_,_,X,_,_,_);

	bitmap1bg[0] = MASK(_,_,X,X,X,_,_);
	bitmap1bg[1] = MASK(_,_,X,X,X,_,_);
	bitmap1bg[2] = MASK(X,X,X,X,X,X,X);
	bitmap1bg[3] = MASK(X,X,X,X,X,X,X);
	bitmap1bg[4] = MASK(X,X,X,X,X,X,X);
	bitmap1bg[5] = MASK(_,_,X,X,X,_,_);
	bitmap1bg[6] = MASK(_,_,X,X,X,_,_);

	bitmap2fg[0] = MASK(_,_,X,X,X,_,_);
	bitmap2fg[1] = MASK(_,X,_,_,_,X,_);
	bitmap2fg[2] = MASK(X,_,_,_,_,_,X);
	bitmap2fg[3] = MASK(X,_,_,_,_,_,X);
	bitmap2fg[4] = MASK(_,X,_,_,_,X,_);
	bitmap2fg[5] = MASK(_,_,X,X,X,_,_);

	bitmap2bg[0] = MASK(_,_,X,X,X,_,_);
	bitmap2bg[1] = MASK(_,X,X,X,X,X,_);
	bitmap2bg[2] = MASK(X,X,X,X,X,X,X);
	bitmap2bg[3] = MASK(X,X,X,X,X,X,X);
	bitmap2bg[4] = MASK(_,X,X,X,X,X,_);
	bitmap2bg[5] = MASK(_,_,X,X,X,_,_);

	GrSetCursor(w1, 7, 7, 3, 3, si.white, si.black, bitmap1fg, bitmap1bg);
	GrSetCursor(w2, 7, 7, 3, 3, si.white, si.black, bitmap2fg, bitmap2bg);

	/*
	 * Draw lines around the edge of the screen.
	 */
	GrLine(GR_ROOT_WINDOW_ID, gc1, 0, 0, si.cols-1, 0);
	GrLine(GR_ROOT_WINDOW_ID, gc1, 0, si.rows-1, si.cols-1, si.rows-1);
	GrLine(GR_ROOT_WINDOW_ID, gc1, 0, 0, 0, si.rows-1);
	GrLine(GR_ROOT_WINDOW_ID, gc1, si.cols-1, 0, si.cols-1, si.rows-1);

	while (1) {
		GrCheckNextEvent(&event);

		switch (event.type) {
			case GR_EVENT_TYPE_BUTTON_DOWN:
				do_button((GR_EVENT_BUTTON *) &event);
				break;

			case GR_EVENT_TYPE_KEY_DOWN:
				do_keystroke((GR_EVENT_KEYSTROKE *) &event);
				break;

			case GR_EVENT_TYPE_EXPOSURE:
				do_exposure((GR_EVENT_EXPOSURE *) &event);
				break;

			case GR_EVENT_TYPE_NONE:
				do_idle();
				break;
		}
	}
}


/*
 * Here when a button is pressed.
 */
do_button(bp)
	GR_EVENT_BUTTON	*bp;
{
	if ((bp->wid == w2) && bp->buttons) {
		GrClose();
		exit(0);
	}

	if (bp->wid != w1) {
		/*
		 * Cause a fatal error for testing if more than one
		 * button is pressed.
		 */
		if ((bp->buttons & -((int) bp->buttons)) != bp->buttons)
			GrClearWindow(-1, 0);
		return;
	}

	if (bp->buttons & GR_BUTTON_1) {
		GrClearWindow(w1, GR_TRUE);
		return;
	}

	if (bp->buttons) {
		begxpos = bp->x;
		xpos = bp->x;
		ypos = bp->y;
	}
}


/*
 * Here when a keyboard press occurs.
 */
do_keystroke(kp)
	GR_EVENT_KEYSTROKE	*kp;
{
	GR_SIZE		width;		/* width of character */
	GR_SIZE		height;		/* height of character */
	GR_SIZE		base;		/* height of baseline */

	GrGetGCTextSize(gc1, &kp->ch, 1, &width, &height, &base);
	if ((kp->ch == '\r') || (kp->ch == '\n')) {
		xpos = begxpos;
		ypos += height;
		return;
	}
	if (kp->ch == '\b') {		/* assumes fixed width font!! */
		if (xpos <= begxpos)
			return;
		xpos -= width;
		GrSetGCForeground(gc3, 6);
		GrFillRect(w1, gc3, xpos, ypos - height + base + 1,
			width, height);
		return;
	}
	GrText(w1, gc1, xpos, ypos + base, &kp->ch, 1);
	xpos += width;
}


/*
 * Here when an exposure event occurs.
 */
do_exposure(ep)
	GR_EVENT_EXPOSURE	*ep;
{
	if (ep->wid != w1)
		return;
	GrFillRect(w1, gc2, 50, 50, 200, 100);
}


/*
 * Here to do an idle task when nothing else is happening.
 * Just draw a randomly colored filled circle in the small window.
 */
do_idle()
{
	GR_COORD	x;
	GR_COORD	y;
	GR_SIZE		rx;
	GR_SIZE		ry;
	GR_COLOR	color;

	x = rand() % 70;
	y = rand() % 40;
	rx = (rand() % 10) + 5;
	ry = (rx * si.ydpcm) / si.xdpcm;	/* make it appear circular */
	color = rand() % (si.maxcolor + 1);

	GrSetGCForeground(gc3, color);
	GrFillEllipse(w2, gc3, x, y, rx, ry);	
}


/*
 * Here on an unrecoverable error.
 */
void
errorcatcher(code, name, id)
	GR_ERROR	code;		/* error code */
	GR_FUNC_NAME	name;		/* function name which failed */
	GR_ID		id;		/* resource id */
{
	GrClose();
	printf("TEST PROGRAM ERROR: code %d, function %s, resource id %d\n",
		code, name, id);
	exit(1);
}


Well, that's enough talking.  If I get enough responses, I'll post the
complete code out in a short while.  Packaging it up correctly and
doing documentation will take a couple of weeks, I estimate.  Is anybody
interested?

-dbell-
dbell@pdact.pd.necisa.oz.au
D

"Ken Corey, CSCI Major..." <KENC@vaxb.acs.unt.edu> (04/08/91)

David,
 
  Yeah!  I'm interested in the graphics shell you're working on.  Do you have
any certain individuals working on any certain projects?  I'm sure you're gonna
be overloaded with requests for your code.  Might be easiest for you just to
post to the net...BUT if you decide to send to individuals, please include me.

Just for info, I run MacMinix, 4MB ram, 20MB partition for Minix.  (I'm soon
gonna have a 25 Mhz accelerator...they keep telling me it's on the way...;)

Anyway, talk to you later.

| Ken Corey  kenc@vaxb.acs.unt.edu  ken@isect.lonestar.org           |
| "Ladies strewn, those made to witness his charm, never daring or   |
|  allowed to touch the man behind the machine."  -Leah              |

brian <CRAWFORD_B%PLU.BITNET@cornellc.cit.cornell.edu> (04/09/91)

David,

I am very interested.  Sign me up for a copy of the code if you're sending
it to individual users.  I've been working on a serial mouse driver here
for my OS class and just finished it.  It only works on a text based screen.
I would like to see it have some potential for a graphics system...  Your
graphic routines sound very much like what is used in both the Mac OS and
the Atari ST OS.  Just for info: I'm running Minix on a 286 AT with extended
memory and a hard drive.  Please either post your code/documentation or
send me a copy.  I'm very interested.

Brian Crawford
CRAWFORD_B@PLU.BITNET

btenison@eng.auburn.edu (Bruce Tenison) (04/10/91)

David,

   I too am also interested in the graphics shell..  Matter of fact, there are
7 of us here who are interested.  If you decide not to post to the net and send
to individuals, please include me also...

Thanks!

--
===============================================================================
| "Offer me solutions and    | "all my nerves are naked     | Bruce Tenison   |
|  offer me alternatives and |  wires tender to the touch   | btenison@       |
|  I decline."---R.E.M.      |  sometimes super-sensitive   | eng.auburn.edu  |

bevan@ecr.mu.oz.au (Bevan Anderson) (04/10/91)

In article <btenison.910409181140@marconi.eng.auburn.edu>, btenison@eng.auburn.edu (Bruce Tenison) writes:
> David,
> 
>    I too am also interested in the graphics shell..  Matter of fact, there are
> 7 of us here who are interested.  If you decide not to post to the net and send
> to individuals, please include me also...
> 
> Thanks!

And me too, please!

Thanks,
Bevan Anderson

_______________________________________________________________________________
Bevan Anderson.						Engineering
bevan@ecr.mu.oz.au					Melbourne Uni.

s8824753@ipc05.tmc.edu (David Andrew CREELMAN) (04/10/91)

	Yeah !!!!

		Post the source to the news if you can, I'm sure that everyone would love it. Post it if you can

						David

hp@vmars.tuwien.ac.at (Peter Holzer) (04/16/91)

bevan@ecr.mu.oz.au (Bevan Anderson) writes:

>And me too, please!

And me, too. (How about a new newsgroup: comp.os.minx.i.want.mini-x?)

--
|    _  | Peter J. Holzer                       | Think of it   |
| |_|_) | Technical University Vienna           | as evolution  |
| | |   | Dept. for Real-Time Systems           | in action!    |
| __/   | hp@vmars.tuwien.ac.at                 |     Tony Rand |