Intro to SkyGI Programming

In this Tutorial, I will outline the making of virtually the most basic graphical application in SkyOS.

Open your favorite text editor and follow along.

First, you'll need to #include "skygi/skygi.h" for Graphical Subsystem functions.

#include "skygi/skygi.h"

And, add the prototype for the Event Handler function. Don't worry if you aren't understanding what's going on just yet. You'll see very shortly.

void * EventHandler(HANDLE hWnd, s_gi_msg msg);

Now, we can begin on our main() function. Note that I've neglected to include the actual declaration of main() to prevent people from copying and pasting without reading the explanations.

The next things we'll add to the program are the HANDLEs, Message Interface, and an sCreateApplication struct.

Note: Always declare a pointer to an sCreateApplication instead of declaring a new sCreateApplication. Quote from Robert on IRC regarding this matter:

<Robert> Never use "sCreateApplication app;" because whenever the size of the sCreateApplication changes, you application will not be binary compatible anymore
sCreateApplication *app;
s_gi_msg msg;

In all SkyGI Applications, one of the first things you should do is run GI_Initialize(). GI_Initialize takes no arguments and returns a 0 on success, and a number other than 0 on failure. It never hurts to check your return values.

if (GI_Initialize() != 0) {
	fprintf(stderr, "Unable to Init GI");
	return -1;

Next, we'll set our values for the Application's basic settings: X and Y Coordinates, Window Width and Height, and Window Style through the means of sCreateApplication. If you haven't already, I'd highly suggest reading through the sCreateApplication Documentation.

First, we must allocate enough space for the members of an sCreateApplication struct. Fortunately for us, SkyOS has a function built in for this sole purpose. GI_ApplicationStructCreate takes no arguments and returns a pointer to an sCreateApplication struct.

app = GI_ApplicationStructCreate();

Now that the struct has been allocated, we can access its members by the 'app' pointer. We'll set this Application's settings accordingly:

strcpy(app->ucApplicationName, "Testing");
app->uiX = 0;
app->uiY = 0;
app->uiHeight = 480;
app->uiWidth = 640;
app->uiStyleApplication = 0;
app->uiStyleFrame = 0;
app->uiStyleTitle = 0;
app->uiStyleMenu = 0;
app->uiStyleBar = 0;
app->uiStyleClient = 0;
app->uiBackGroundColor = 0;
app->fwndClient = EventHandler;

Now, we'll create the Application according to the settings we've specified. For this purpose, we'll call GI_ApplicationCreate. GI_ApplicationCreate takes a pointer to an sCreateApplication struct as its parameter and returns a Window HANDLE. We'll pass 'app' as the parameter, since that is where we've stored all the settings, and have it return the Window HANDLE to MainWin which we declared earlier.

MainWin = GI_ApplicationCreate(app);

After configuring the settings for the Application it's about time to actually show something on the screen. This is where GI_ApplicationWindowShow comes in. GI_ApplicationWindowShow takes a Window HANDLE as a parameter and returns an HRESULT. We will pass MainWin as the parameter to this function and as always, check our return values.

if (GI_ApplicationWindowShow(MainWin) != S_OK) {
	fprintf(stderr, "Unable to Show Application Window");
	return -1;

We've now set up our Application and shown its Window, but at this point it will just flash on the screen and quickly exit. We need a main loop to keep the Application open and waiting for Messages or Commands. For this, we'll make an infinite loop that, in this case, will only wait for the command to exit. Comments have been left intact for better explanation of the code.

int run;
while (1) {                               /* Endless loop, to make the
                                             Window stay open */
      run = GI_MessageWait(&msg, NULL);   /* Get return value to check for
                                             messages */
                                          /* GI_MessageWait() returns 0 on
                                             MSG_QUIT */
      if (!run)                           /* If GI_MessageWait returned 0 */
         if ( == MainWin)          /* Check if MSG_QUIT was sent to 
                                             MainWin */
            break;                        /* If so, break out of the while
                                             loop */
      GI_MessageDispatch(, &msg);

At this point, any code beyond the while (1) loop in main() will be the closing of the program. Logically, there can be very few things to go here. For this program, we'll do the most basic exit routine by calling GI_WindowDestroy. GI_WindowDestroy takes a Window HANDLE as its only parameter and returns 0 on success. I can't see of a reason why it would be necessary to check return values for such a function unless there are multiple windows open. Therefore, we'll skip checking this time.

return 0;

Finally, our main() function is finished. Now, where the fun really begins: The Event Handler. This is the function that will control what your program does when a key is pressed or the mouse button is clicked for instance.

void * EventHandler(HANDLE hWnd, s_gi_msg *msg) {
    switch (msg->type) {                      /* Check for messages */
           case MSG_DESTROY:                  /* If message is MSG_DESTROY: */
                GI_MessageQuitPost(hWnd);     /* Send MSG_QUIT to MainWin */
           case MSG_COMMAND:                  /* If message is a command: */
                switch (msg->para1) {         /* Check type of message */
                 * This will be where you specify what action will take place
                 * when a button is clicked or a key pressed, et cetera
    return NULL;

The full source of the described application is available from my Example Code folder.

Congratulations. You've managed to read all the way through (hopefully) my tutorial. I sincerely hope I've explained the things covered in this tutorial well enough, though in the likely case that I have not, please feel free to shoot me a line and tell me what I need to clarify.