FOSS Unleashed

Drawing with Plan 9 (Pt 0)

I found a neat little code repository the other day, and decided to play around with it. Now, I’m not totally unfamiliar with plan 9’s drawing routines, having grazed the manuals a few times. However those manual pages run into the problem I have with a number of plan 9’s manual pages, but that’s a post for another day. The manual pages are broadly split into three parts, draw(3), graphics(3), and window(3). Those three man pages contain the bull of the function documentation for the drawing system, and also the definitions for the three main graphical structures: Image, Display, and Screen.

Overall, at first glance this is all sane. Until you look at the function prototypes. The names are not that bad, but if you’re glancing through them looking for some kind of rectangle primitive, you’re not going to see one. Want to draw a rectangle or fill an Image or window? You want the draw() function. Okay, drawing rectangles is the implicit default. That’s sane. Let’s take a closer look: void draw(Image *dst, Rectangle r, Image *src, Image *mask, Point p); and we immediately have a problem. There are three Image pointers as parameters, and no color. This led me down a little rabbit-hole where I was trying to find out how to color an Image. Surely there’s some means to access the pixel data for an Image right? … right? Let’s take a look:

1
2
3
4
5
6
7
8
9
10
11
12
13
typedef
struct Image
{
Display *display; /* display holding data */
int id; /* id of system-held Image */
Rectangle r; /* rectangle in data area, local coords */
Rectangle clipr; /* clipping region */
ulong chan; /* pixel channel format descriptor */
int depth; /* number of bits per pixel */
int repl; /* flag: data replicates to tile clipr */
Screen *screen; /* 0 if not a window */
Image *next; /* next in list of windows */
} Image;

Okay, so it’s implicitly a part of a linked list, for some reason, and it has pointers to a Screen but only if there’s a window, and a Display which is noted as “display holding data”. Surely, that means that a Display holds the pixel data right?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef
struct Display
{
...
void (*error)(Display*, char*);
...
Image *black;
Image *white;
Image *opaque;
Image *transparent;
Image *image;
Font *defaultfont;
Subfont *defaultsubfont;
...
};

Nope. It contains a bunch of Images. I guess that makes sense as a “Display”, but what is with those? We have white, black, opaque? What? Maybe looking at Screen will enlighten us.

1
2
3
4
5
6
7
8
typedef
struct Screen
{
Display *display; /* display holding data */
int id; /* id of system-held Screen */
Image *image; /* unused; for reference only */
Image *fill; /* color to paint behind windows */
} Screen;

Okay, that’s reasonable, but not actually what we needed, it would be weird if it was to be honest. Okay, I don’t have any leads, so I may as well start poking around. My objective is to get the gui-menu.c program to not OLED-flashbang me every time it runs. This is actually pretty simple to solve: draw(screen, screen->r, display->black, nil, ZP); Here we’re working with two globals, display which is a Display * and screen which is an Image *. The latter bit tripped me up for a bit when I needed to access the Screen * because I wanted to muck with its fill property. There are two things that I learned here. First, the fill property doesn’t seem to do anything, doesn’t color the background at all in any situation I could try it on. Secondly, the draw() call I made needs to be in the event loop. Not sure why, there might be a paint event I should be listening to, but at the moment I’m just calling it every loop. Thankfully the event loop does block itself when empty.

So! Mission complete right? Yes. But I would really like to be able to draw my own color, and maybe figgure out how to work with the pixel data somehow. Now, it’s not referenced often, but there is a man page for allocimage(3) which has the following prototype: Image *allocimage(Display *d, Rectangle r, ulong chan, int repl, int col). This allocates a new Image that is filled with one single color. Each of the arguments aren’t too difficult to come by either. I wanted something like display->black but my own color so: (display, display->black->r, display->black->chan, 1, 0x004040FF); Nice and simple, duplicate the properties from display->black (though I manually set repl to 1, which is also the value that black and white have from display), compile, and… flashbang. A pure white screen. Now, a first I thought I was incoding the color incorrectly, but that wasn’t the case. The situation is the chan parameter, this indicates the color channel. Natually the black and white images don’t need to store a very complicated color, so that value is 0x31 (not sure what macro that expands to), which is an apparently low color depth. Given I was providing a 32-bit RGBA value, I needed to give RGBA32 as the channel value, and it worked. I have a nice dark non-black window.

Overall, this was an interesting adventure, but definately not one that enamoured me to the man pages. I feel like I’ll want to be taking notes and referencing those instead.