Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Cosmos Large Cursor/GUI tutorial

0.00/5 (No votes)
14 Nov 2014 1  
How to develop an advance operating system GUI in cosmos.

Welcome to my first cosmos tutorial (and in fact, my first article). We will be looking at drawing a mouse in cosmos today. I have found a few tutorials on the internet on how to do this, but none of them address the major problems. For example, if there is only one pixel and you add more, it lags. Also, they tend to leave pixels on the screen and they influence other drawings on the screen. I have written a full screen drawing manager that solves all these problems and allows you to easily draw other things like a task bar or a window.

To truly understand what we are doing we must first understand how cosmos draws to the screen. I will start by giving a code example and then explaining it.

First we create a variable VGAScreen.

public static VGAScreen VScreen = new VGAScreen();

Now we will set it up.

Console.WriteLine("Vga Driver Booting");
VScreen.SetGraphicsMode(VGAScreen.ScreenSize320x200, VGAScreen.ColorDepth.BitDepth8);
VScreen.Clear(0);
Console.WriteLine("Vga Driver Booted");

First we print "Vga Driver Booting". Next, we change the graphics mode to 320X200 and the color depth to 8. Then we clear the screen with the color black, or 0. After that, we print that the driver booted. This is kind of ironic because after the graphics mode has been set the console will no longer display. Maybe I should have made it, "vga failed to boot".

Now we will write a pixel.

VScreen.SetPixel320x200x8((uint) x, (uint) y, (uint) c);

Now that we have a basic black screen with one pixel of another color, we can go into the detail on how the operating actually sets the pixel. Your code ends up as assembly at the end of the day and that’s what the CPU runs, so to fully understand we must go back to the raw hardware. The way VGA works is simple, the pixels on the screen are stored in memory, which is usually on-board the display device. By changing the values in this memory we can change the pixels on the screen. The only problem is that, the bigger the resolution, the more space is needed. And considering that the memory is always the same size the idea of bigger resolutions is hard, the solution to this problem is using less space to store the pixels, and this is why a bigger resolutions has a smaller bit depth (hopefully that makes sense). If the screen size is 320 by 200, the color depth is 8. This means every pixel can be 1 of 255 colors (the size of a byte), which means the screens memory is 64 000 bytes in size. It is important that we remember this later because we will need to understand this when we write the screen buffer.

Back to how the pixels are actually manipulated in memory, let’s think of the screen as a byte array like this.

public static byte[] SBuffer = new byte[64000];

If every byte is one pixel the first byte would be the pixel at x = 0 and y = 0. The second byte in the array would be x = 1 and y = 0 etc. So if we wanted to index the array using x and y coordinates we would use a little formula that looks like this:

Index = (Screen Width * y) + x

For our screen size it would be:

Index = (320 * y) + x

Let’s look at the how this works then I will give you the C# code. OK so we know one row on the screen is 320 pixels long. This means that if we wanted the first pixel in any row on the screen we just have to multiply the width by the row number, and by adding the x to this we will have the exact number or index of that pixel in the array. This is precisely how the computer does it too.

Here is the C# code:

public static void SetPixel(int x, int y, int color)
{
    SBuffer[(y*320) + x] = (byte)color;
}

Let’s look at the reason why the cosmos screen driver is so slow and how to solve the problem. The reason it is so slow is because it takes a long time to calculate the index and the memory writing operation is hard on time. To make matters even worse, the way the driver works makes it even slower, but one operation that is not that costly is reading memory, and this is where the key lays. Basically the way we work around this problem is by only drawing a pixel if it changes. It turns out that it is faster to check if a pixel is not the same color before drawing it, and it is faster than just drawing it over. Anyway, here is the code, and then I will explain it.

public static void ReDraw()
{
    // VScreen.Clear(0);
    
    int c = 0;
    
    for (int y = 0; y < 200; y++)
    {
        for (int x = 0; x < 320; x++)
        {
            uint cl = VScreen.GetPixel320x200x8((uint) x, (uint) y);
            if (cl != (uint)SBuffer[c])
            {
                VScreen.SetPixel320x200x8((uint) x, (uint) y, SBuffer[c]);
            }
            c++;
        }
    }
    for (int i = 0; i < 64000; i++)
    {
        SBuffer[i] = 0;
    }
}

This is a bit more complex to understand, but I’m sure we will get it done. We have three loops in this method. Loop one is for the y axis and loop two is for the x axis. By looping row by row and the column by column we will still loop throw all 64000 pixels on the screen, but we will have the x and y coordinates. The variable c keeps track of the index that we use on the buffer. Overall, it is not a bad idea to understand what's going on inside the loops. We have the if statement that just makes sure we don’t do a costly set operation for no reason. After that, we have loop number three that just fills the buffer with black. Congrats! You now understand how to double buffer the screen. This will remove all flickering from the screen the screen will redraw frame by frame and not pixel by pixel which will greatly improve the overall feel and performance. It will remove the need to manually dispose the previous frame like all the other blogs recommend. Now that we can draw properly to the screen, let's draw the mouse.

First we will create a new mouse

public static Mouse m = new Mouse();

New we need to start the mouse you need to do this in your boot sequence somewhere.

public static class BootManager
{
    public static void Boot()
    {
        Screen.Boot();
        Desktop.m.Initialize(320, 200);
    }

}

Now we will draw the mouse like this.

    Screen.SetPixel(m.X,m.Y,40);
    Screen.SetPixel(m.X+ 1,m.Y,40);
    Screen.SetPixel(m.X + 2, m.Y, 40);
    Screen.SetPixel(m.X, m.Y + 1, 40);
    Screen.SetPixel(m.X, m.Y + 2, 40);
    Screen.SetPixel(m.X+ 1, m.Y + 1, 40);    
    Screen.SetPixel(m.X + 2, m.Y + 2, 40);    
    Screen.SetPixel(m.X +3 , m.Y +3, 40);

If we run this code we get this:

And there you have it, one sexy mouse in cosmos. If you move the mouse it will move flawlessly - no lag - and best of all it will never drop ghost pixels. Now let’s add a "task bar" (you will need to add this to your screen class).

public static void DrawFilledRectangle(uint x0, uint y0, int Width, int Height, int color)
{
    for (uint i = 0; i < Width; i++)
    {
        for (uint h = 0; h < Height; h++)
        {
            SetPixel((int)(x0 + i), (int)(y0 + h), color);
        }
    }
}

All we do now is add this to the code.

Screen.DrawFilledRectangle(0,0,320,25,50);

Remember to add it before you draw your mouse so it is on the bottom here is the end result:

And that is it for this tutorial. I will add a download link down below to the cosmos project.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here