My SDL adventure

So as I’ve said many times before, C++ is not my strongest language! I’m putting that disclaimer out there before I start this post because I’m sure the whole world of skilled programmers will bear down and dismantle all of my efforts…

Anyway, disclaimers aside, while Qt is great for quick GUIs, its license can only apply to open source projects and if you ever want to use it commercially, some form of money is involved which I don’t have! So I’ve decided to learn another GUI package and that ended up being SDL.

After reading a bit about SDL, it turns out its been used for loads of cool stuff and surprisingly, I’ve played a couple of the games that are featured on the wikipedia page. Not forgetting once upon a time I used to play WoW too which the designer of SDL used to be the head coder for.

Libraries
Installing SDL was probably the hardest part, and even then it wasn’t particularly difficult. I needed to install the SDL framework (placed at my C:\ drive for ease), then move the SDL DLL file to my SysWOW64 folder. Once this had been done, it was a matter of generating a project in Codeblocks, setting the search directories and linking the correct libraries. This in total took about 20 minutes so it’d hardly a time consumer.

For all my microcontroller projects featuring an LCD, I’ve already written a graphics library for this so I thought the easiest thing for me would be to port this to SDL. Fortunately, that library is based around the implementation of one function :”Draw Pixel(x, y, colour)”, hardly a deal breaker. The rest of my graphics library is pretty portable as it merely calls this function. I have however added a couple of extra features:

Features:

  1. PChar(ch, x, y, size) – print the character “ch” at screen location x, y.The top left corner of the character is represented by x and y and is printed right and down from this point
  2. PStr(str, x, y, size) – print the string “str” starting at x, y
  3. PNum(num, x, y, padding, size) – print an integer number to the screen at x, y with “padding” number of 0’s preceding the number
  4. PNumf(num, x, y, prec, size) – print a floating point number at x, y with a precision of “prec”
  5. DrawLine(x0, y0, x1, y1, wd) – Draw a line of width “wd” from x0, y0, to x1, y1. This isn’t my function and was found on the internet
  6. DrawRectangle(Xs, Ys, Xe, Ye, wd) – Draw a rectangle from Xs, Ys to Xe, Ye with line width “wd”
  7. DrawFillRectangle(Xs, Ys, Xe, Ye, wd) – Draw a filled rectangle from Xs, Ys to Xe, Ye with line width “wd”. The line and centre can be different colours
  8. DrawCircle(Xm, Ym, R) – Draw a circle of radius R at Xm, Ym
  9. DrawBMPFN(Fn, X, Y, Scale) – Draw a bitmap from file “Fn” at location X, Y
  10. DrawBMP(img, X, Y, Scale) – Draw an SDL Surface at location X, Y
  11. Clear – Clear the graphics plane

For all of the text functions that feature the argument “Size”, the size of each character can be scaled. This value is a decimal number so non-integer scaling can be done. The algorithm however is exceptionally simple and doesn’t interpolate well giving a poor look when non integer values are used.

fonts.pngVarying size fonts

For the functions featuring the “Scale” argument, this allows for the same style of decimal point scaling of bitmaps. Scaling down works on pixel skipping and scaling up works on pixel duplicating, once again leading to some dodgy looking scaling compared to other techniques.

bmps.pngScaling down BMPs looks much better than scaling up!

The DrawBMPFN function loads and allocated memory, draws to the graphics plane and deallocates the memory after use so as far as efficiency is concerned, this isn’t a very efficient method of drawing! It does however allow you to quickly load a BMP without having to allocate the memory outside of the graphics library.

Buttons
Graphics are cool and all but a GUI really can’t do that much without user input (its kinda in the name… graphical user interface). Once I had done my graphics library port, I decided to concentrate on trying to implement a “simple” button interface. I’m not sure how professional implementations are done so my method might be ridiculously inefficient but it does the job and is surprisingly simple.

I started by designing a “Button” class. This class contains all the data required to draw the button: X position (top left), Y position (top left), width, height, line colour, fill colour, text background colour, text colour, text size, button text and button ID. In general, I’d choose to have the text background colour and the button background colour the same size though if someone wanted, they could change this! The class also features all the standard getter and setter functions for all of these variables, along with an overloaded equal operator and two constructors. Because I’m lazy, I’ve not followed the standard C++ class spec of creating an assignment operator etc. so I’d hardly get a good set of marks in my exams but hey ho, this isn’t an academic project!

Once I’d implemented the Button class, I needed to implement a ButtonHandler class. This abstracts the non data based functions from the Button class such as testing whether the button was clicked, storing all buttons within the application, replacing one button with another and most importantly, rendering buttons!

My ButtonHandler class is what actually draws the buttons to the graphics plane so there is a function named “SetGFXLib” which sets a private GFX_Lib pointer to the graphics library used in the application. This allows the use of all the graphics library functions within this class.

All buttons are stored in a button vector. This alleviates the need for me to worry about memory management as that alone is a challenge! Buttons can be added to this vector using the function “AddButton(Button b)”. Buttons can also be replaced using the function “ReplaceButton(string id, Button b)”. Buttons are referenced by a string identifier (id) meaning two buttons can’t have the same name. I found this a lot easier than referencing buttons by a number though this risks a slower system as string comparisons take a lot longer than single number comparisons. This could probably be sped up using string hashing but I don’t think its a big enough deal yet to worry about this.

Buttons are very simple objects and merely consist of a filled rectangle with text inside, therefore the render function is pretty simple and just consists of drawing a correctly sized rectangle and writing text inside. A button is added to the ButtonHandler like so:

AddButton(Button(“btn1”, 0, 0, GREEN, WHITE, RED, BLACK, 1, “Button”));

buttons
This is why you have the text background colour identical to the fill colour!

The ButtonHandler class is also what tests whether a button has been clicked or not. This is as simple as doing 4 comparisons consisting of checking the mouse was clicked inside the button. As the ButtonHandler will likely contain multiple buttons, all buttons are checked within a while loop inside of an event handler. For my test sketch, the event handler is a while loop containing an SDL_WaitEvent(&event). This function waits for an event to happen and is blocking. Underneath this line, I have a switch statement that chooses which event happened and runs a set of code. On the SDL_MOUSEBUTTONDOWN event, I have my ButtonHandler loop. This consists of checking all buttons for whether the mouse click was inside of them. A function named “ButtonsAvailable” was written to return a 1 until all buttons have been read. Once all buttons have been read, this function returns a 0. This is used as the loop conditional statement and terminates the while loop once all buttons have been tested. Inside of the loop, the function “IsPressed(&event)” is called and the value returned is stored in a string “btn”. If this variable contains an empty string, it means that button wasn’t pressed. If the variable however contains the ID of a button, it means that button was pressed. This allows for a simple cascade of if statements to test for each button. This whole section of code ends up looking like:

case SDL_MOUSEBUTTONDOWN:
string btn = “”;
while(bh.ButtonsAvailable()) {
btn = bh.IsPressed(&ev);
if(btn == “btn1”) {
cout << “Button 1 pressed” << endl;
} else if(btn == “btn2”) {
cout << “Button 2 pressed” << endl;
} else if(btn == “btn3”) {
cout << “Button 3 pressed” << endl;
}
}
break;

Its a pretty simple methodology and seems to work quite well for what I’d like to do with buttons.

buttonpress.png
Thank you console! Such a useful function for buttons…

That’s all for now! I couldn’t envisage anyone wanting to use this library but its on my Github regardless. It might be buggy because I’m still in the progress of writing it. Enjoy!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s