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 |