Now, let's get some windows open. This is going to depend on what operating system you're using. We'll support Windows and X11/Linux. So, yeah, this article will be a boring one.

The platform layer will be required to implement 3 functions.

void Initialise(); // Initialise the platform layer.
Window *WindowCreate(const char *cTitle, int width, int height); // Open a new window and return a heap allocated 'window object'.
int MessageLoop(); // Keep the windows ticking along.

The user of the library will use these functions in the following manner:

int main() {
	Initialise();
	WindowCreate("Hello, world", 300, 200); // Open the first window.
	WindowCreate("Another window", 300, 200); // Open the second window.
	return MessageLoop(); // Pass control over to the platform layer until the application exits.
}

For now, we'll consider the application to exit when any window is closed.

What's in the Window structure, you ask? Good question. Not much -- it's mostly platform specific gubbins.

typedef struct Window {
	uint32_t *bits; // The bitmap image of the window's content.
	int width, height; // The size of the area of the window we can draw onto.

#ifdef PLATFORM_WIN32
	HWND hwnd;
	bool trackingLeave; // We'll need this when we implement mouse input later on in the tutorial.
#endif

#ifdef PLATFORM_LINUX
	X11Window window;
	XImage *image;
#endif
} Window;

We'll also need to include some platform specific headers. In typical fashion, these export symbols that clash with our naming schemes, so we'll have to #define/#undef our way out of trouble.

#ifdef PLATFORM_WIN32
#define Rectangle W32Rectangle
#include <windows.h>
#undef Rectangle
#endif

#ifdef PLATFORM_LINUX
#define Window X11Window
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
#undef Window
#endif

Let's put an array of pointers to the window objects into the global state structure. And, sigh, some X11 nonsense too...

typedef struct GlobalState {
	Window **windows;
	size_t windowCount; // The number of open windows; the number of pointers in the above array.

#ifdef PLATFORM_LINUX
	Display *display;
	Visual *visual;
	Atom windowClosedID;
#endif
} GlobalState;

We can now implement the three functions Initialise, WindowCreate and MessageLoop. These functions are all completely platform specific, and really aren't that terribly interesting or enlightening. They're in the source code below, should you want to read them. The most notable thing that happens is how we add the window objects to the global array.

Window *window = (Window *) calloc(1, sizeof(Window));
global.windowCount++;
global.windows = realloc(global.windows, sizeof(Window *) * global.windowCount);
global.windows[global.windowCount - 1] = window;

The window will be completely empty at this point. We'll add rendering in few articles from now :)

part2.c

Part 1: It begins.

Part 3: Adding the infrastructure of the element hierarchy.