Pages

Friday, August 22, 2014

[Minix][Tutorial 5] Adding keyboard input to a project

In the previous tutorial we've added a graphics library to our project. So far, when we run the program, a blue screen is shown for two seconds. In this tutorial we are going to add keyboard features to our program: when we run it a blue screen will appear and the program won't terminate until we press Esc on the keyboard.

Adding keyboard and KBC source code

During class, you should have developed functions to interact with the keyboard using the kbc. Now it is time to add these to our src folder and add them to the Makefile SRCS tag - I usually declare them by alphabetical order. Below is a screenshot with the changes I have made so far.



Introducing objects in C


As you might know, C++ is object-oriented, but C is not. But there is a way to make C programs resemble some object-oriented-like programming language. I think it is time for us to start implementing this in our project.

Let's create our main object, the engine of our application: flappy-nix. Create both FlappyNix.c and FlappyNix.h inside src.

After that, what should our "object" FlappyNix contain and represent?
It will have four methods any object should always have:
  • initialize;
  • update;
  • draw;
  • terminate.

It should also have three variables:
  • done - tells if the program is done/is going to terminate;
  • draw - a flag that tells the program needs to be redrawn;
  • scancode - whenever a keyboard key is pressed, this will contain the code of that key.

There will also be another variable: IRQ_SET_KB, which will be used to detect keyboard interruptions.

Here is what my FlappyNix.h looks like so far:



Now let's actually implement these methods in FlappyNix.c.

startFlappyNix()



Side note: notice I have declared a global const int FPS, which is the Frames Per Second at which the application will be redrawn once we implement the timer. For now, don't worry about it.

Regarding startFlappyNix(), this function returns a pointer to a FlappyNix "object".
First I allocate space for a new FlappyNix.
After that, I subscribe the keyboard to activate keyboard interruptions.
Then, I initialize the object variables.
Finally, I return the pointer to the newly created FlappyNix.

updateFlappyNix(FlappyNix* flappy)



This function should be familiar to you from the LCOM lectures. I'm not going to go into much detail about it: basically, we are checking for hardware interruptions. At the moment we are only registering keyboard interruptions and the code of the pressed key associated to that interruption (lines 32-34). Later we'll be adding code to this function in order to receive timer and mouse interruptions.
Afterwards, we check the scancode, and if it is different than zero, it means a key was pressed. We check if that key was the Esc key. If it was, we tell that the program is done! Easy, right?

Notice that, just like color names, I also have some key names - line 42 - because that way I don't have to remember every key code or check a table of codes, I just use the key name. As you can see below, I declared these names in Keyboard.h. You can use the snippet below the screenshot in your own projects, it contains every possible key of the keyboard and the respective key code.



Note: the KEY_UP macro presented in the picture above should make use of '&' instead of '|'.

/// Keys
typedef enum {
    KEY_NONE = 0x0000,
    KEY_ESC = 0x0001,
    KEY_1 = 0x0002,
    KEY_2 = 0x0003,
    KEY_3 = 0x0004,
    KEY_4 = 0x0005,
    KEY_5 = 0x0006,
    KEY_6 = 0x0007,
    KEY_7 = 0x0008,
    KEY_8 = 0x0009,
    KEY_9 = 0x000A,
    KEY_0 = 0x000B,
    KEY_APOSTROPHE = 0x000C,
    KEY_ANGLE_QUOTES = 0x000D,
    KEY_BKSP = 0x000E,
    KEY_TAB = 0x000F,
    KEY_Q = 0x0010,
    KEY_W = 0x0011,
    KEY_E = 0x0012,
    KEY_R = 0x0013,
    KEY_T = 0x0014,
    KEY_Y = 0x0015,
    KEY_U = 0x0016,
    KEY_I = 0x0017,
    KEY_O = 0x0018,
    KEY_P = 0x0019,
    KEY_PLUS = 0x001A,
    KEY_ACCENT = 0x001B,
    KEY_ENTER = 0x001C,
    KEY_L_CTRL = 0x001D,
    KEY_A = 0x001E,
    KEY_S = 0x001F,
    KEY_D = 0x0020,
    KEY_F = 0x0021,
    KEY_G = 0x0022,
    KEY_H = 0x0023,
    KEY_J = 0x0024,
    KEY_K = 0x0025,
    KEY_L = 0x0026,
    KEY_C_CEDILLA = 0x0027,
    KEY_ORDINAL = 0x0028,
    KEY_BACKSLASH = 0x0029,
    KEY_L_SHIFT = 0x002A,
    KEY_TILDE = 0x002B,
    KEY_Z = 0x002C,
    KEY_X = 0x002D,
    KEY_C = 0x002E,
    KEY_V = 0x002F,
    KEY_B = 0x0030,
    KEY_N = 0x0031,
    KEY_M = 0x0032,
    KEY_COMMA = 0x0033,
    KEY_DOT = 0x0034,
    KEY_MINUS = 0x0035,
    KEY_R_SHIFT = 0x0036,
    KEY_ALT = 0x0038,
    KEY_SPACE = 0x0039,
    KEY_CAPS = 0x003A,
    KEY_F1 = 0x003B,
    KEY_F2 = 0x003C,
    KEY_F3 = 0x003D,
    KEY_F4 = 0x003E,
    KEY_F5 = 0x003F,
    KEY_F6 = 0x0040,
    KEY_F7 = 0x0041,
    KEY_F8 = 0x0042,
    KEY_F9 = 0x0043,
    KEY_F10 = 0x0044,
    KEY_NUM = 0x0045,
    KEY_SCRLL = 0x0046,
    KEY_NUM_7 = 0x0047,
    KEY_NUM_8 = 0x0048,
    KEY_NUM_9 = 0x0049,
    KEY_NUM_MINUS = 0x004A,
    KEY_NUM_4 = 0x004B,
    KEY_NUM_5 = 0x004C,
    KEY_NUM_6 = 0x004D,
    KEY_NUM_PLUS = 0x004E,
    KEY_NUM_1 = 0x004F,
    KEY_NUM_2 = 0x0050,
    KEY_NUM_3 = 0x0051,
    KEY_NUM_0 = 0x0052,
    KEY_NUM_DEL = 0x0053,
    KEY_MINOR = 0x0056,
    KEY_F11 = 0x0057,
    KEY_F12 = 0x0058,
    KEY_NUM_ENTER = 0xE01C,
    KEY_R_CTRL = 0xE01D,
    KEY_NUM_SLASH = 0xE035,
    KEY_ALT_GR = 0xE038,
    KEY_HOME = 0xE047,
    KEY_ARR_UP = 0xE048,
    KEY_PGUP = 0xE049,
    KEY_ARR_LEFT = 0xE04B,
    KEY_ARR_RIGHT = 0xE04D,
    KEY_ARR_DOWN = 0xE050,
    KEY_PGDN = 0xE051,
    KEY_INS = 0xE052,
    KEY_DEL = 0xE053,
    KEY_WIN = 0xE05B,
    KEY_CNTX = 0xE05D,
    KEY_END = 0xE04F
} KEY;

drawFlappyNix(FlappyNix* flappy)



This one is pretty straight forward: we fill the display with the color blue.

stopFlappyNix(FlappyNix* flappy)



Here we unsubscribe the keyboard interruptions and free the memory allocated to the FlappyNix "object".

Updating the Makefile



Do not forget to update the Makefile each time you add a new source file!
I added a new line - line 8 - where, from now on, I will declare the source code of "objects" we create.

Improving main.c

Now comes the fun part: let's make use of everything we have been working on and see the results. Go to main.c and edit it according to the screen below.

First, declare and create a new FlappyNix object.
Then it is really simple: while flappy is not done do the following indefinitely:
  • Update flappy;
  • If flappy is not done and draw flag is active - draw flappy.
When flappy is done, the while cycle terminates and so we stop flappy. The rest of the code we already know.



Below is a snippet with the current content of main.c.
#include <minix/sysutil.h>
#include <minix/syslib.h>
#include <minix/drivers.h>

#include "FlappyNix.h"
#include "Graphics.h"

int main(int argc, char **argv) {
    srand(time(NULL));
    sef_startup();

    /*
     * -------------------
     * VESA graphics modes
     *      16-bit (5:6:5)
     * -------------------
     *   320×200 - 0x10E
     *   640×480 - 0x111
     *   800×600 - 0x114
     *  1024×768 - 0x117
     * 1280×1024 - 0x11A
     * -------------------
     */
    initGraphics(0x114);

    FlappyNix* flappy = (FlappyNix*) startFlappyNix();
    while (!flappy->done) {
        updateFlappyNix(flappy);

        if (!flappy->done) {
            if (flappy->draw)
                drawFlappyNix(flappy);

            flipMBuffer();
            flipDisplay();
        }
    }
    stopFlappyNix(flappy);

    exitGraphics();

    return 0;
}

Testing our project so far

It's time to test our program! Go to minix, browse to the project folder, compile and run the project.



If everything went well, when you run the program, a blue screen should appear and should only go away when you press the Esc key on the keyboard. And that terminates the program.

Back to index

Click here to go back to the index post.

1 comment: