Tutorial 4: Cursors

In this fourth Wii programming tutorial, I will cover displaying a cursor by drawing a small square, movement of the cursor and then using the Wii IR to move the cursor. Before reading this tutorial I’m assuming you have picked up a few things and therefore I won’t be re-explaining the common code in the zip file or initial steps in detail. You can grab the PDF of this tutorial here: codemii-tutorial-4

Firstly download this tutorial4-blank which will contain the required files to get us started. Extract this zip file in C:\devkitPro\examples\gamecube. Open up tutorial4.pnproj and then click on main.c to show the source code.

From tutorial 3, we know how to incorporate the controller in our source code, so now all we need to use a cursor in our application, is to draw one. The source code below is our drawing functions. I’m not sure whose code it but thanks to the person whom made it.

void DrawHLine (int x1, int x2, int y, int color) {
    int i;
    y = 320 * y;
    x1 >>= 1;
    x2 >>= 1;
    for (i = x1; i <= x2; i++) {
        u32 *tmpfb = xfb;
        tmpfb[y+i] = color;
    }
}

void DrawVLine (int x, int y1, int y2, int color) {
    int i;
    x >>= 1;
    for (i = y1; i <= y2; i++) {
        u32 *tmpfb = xfb;
        tmpfb[x + (640 * i) / 2] = color;
    }
}

void DrawBox (int x1, int y1, int x2, int y2, int color) {
    DrawHLine (x1, x2, y1, color);
    DrawHLine (x1, x2, y2, color);
    DrawVLine (x1, y1, y2, color);
    DrawVLine (x2, y1, y2, color);
}

Place the above code after the Initialise function. We will be calling the DrawBox function and passing 5 parameters to it. The parameters of DrawBox are the first x co-ordinate, the first y co-ordinate, the second x co-ordinate, the second y co-ordinate and the colour of the lines as shown below:

DrawBox (5, 20, 50, 200, COLOR_WHITE); 

When drawing things on the screen, x = 0 and y = 0 means that we would be at the top left of the screen. If we put the following code as shown above, we would get a line that looks something like:

Once we call the DrawBox function, it breaks the square that we want to draw into 4 lines, which are 2 horizontal and 2 vertical lines. The DrawHLine and DrawVLine functions write to the frame buffer which will be displayed on the screen that we want a certain dot on the screen to be a certain colour. The loop is used so that we go through every dot that we want to change to a certain colour.

tutorial4-draw-square is the finished source code if you need it. Compile the source code (Alt + 1) and run the dol file with gcube. You should see a similar screen to the one shown below.

Moving the Cursor

We now wish to draw a smaller square which will represent our cursor. You can use something like:

DrawBox (300, 250, 301, 251, COLOR_WHITE); 

Now that we’ve got our cursor set up, we need to be able to use the controls to move the cursor. To do this we need to use variables to control the cursor’s x and y co-ordinates on the screen.

We can have two variables, one for the x axis named cursor_x and one for the y axis named cursor_y. These variables will have the default value of 200 and 200, so that it’s near the centre of the screen.

int cursor_x = 300;
int cursor_y = 250; 

The next thing to do is to capture the movement of the joystick, so when we move the joystick up or down it modifies the cursor_y variable and when we move it left or right it modifies the cursor_x variable. We can do this by either de-incrementing (subtracting 1) or incrementing (adding 1 to) the variables.

if (PAD_StickY(0) > 18) {
	cursor_y--;
}
if (PAD_StickY(0) < -18) {
	cursor_y++;
}
if (PAD_StickX(0) > 18) {
	cursor_x++;
}
if (PAD_StickX(0) < -18) {
	cursor_x--;
}

The — means de-increment and the ++ means increment. As an example, if we move the joystick up, we would de-increment cursor_y. So if cursor_y was 200 and we moved the joystick up, cursor_y’s value would be 199.

All that’s left now is to change where the image is being displayed every time we move the cursor. This is easily accomplished by using our variables with the DrawBox function as shown below:

DrawBox (cursor_x, cursor_y, cursor_x + 1, cursor_y + 1, COLOR_WHITE); 

What we’ve done is used our cursor_x and cursor_y in place instead of the 300 (x1) and 250 (y1) values and have added 1 to the cursor to that the values will be 301 (x2) and 251 (y2) in order to draw a square.

The next thing to do is to clear the screen to black before showing the cursor and then using waiting for vertical sync. The reason we are clearing the screen is so that our cursor is cleared every time we move it. If we didn’t clear the screen, then you will see the movement of the cursor stay on the screen.

The code below shows the complete controller code in a while loop with the screen being cleared:

int cursor_x = 300;
int cursor_y = 250;

while(1) {

	PAD_ScanPads();

	if (PAD_StickY(0) > 18) {
		cursor_y--;
	}
	if (PAD_StickY(0) < -18) {
		cursor_y++;
	}
	if (PAD_StickX(0) > 18) {
		cursor_x++;
	}
	if (PAD_StickX(0) < -18) {
		cursor_x--;
	}

	VIDEO_ClearFrameBuffer (rmode, xfb, COLOR_BLACK);

	DrawBox (cursor_x, cursor_y, cursor_x + 1, cursor_y + 1, COLOR_WHITE);

	VIDEO_WaitVSync();
}

The reason behind placing VIDEO_ClearFrameBuffer before DrawBox is so we don’t clear the screen and then waste time doing other functions and then draw the cursor. If you had a lot of code which took a considerable amount of time to execute, you wouldn’t see your cursor all of the time and it would be blinking. Instead we do the functions, clear the screen, draw the cursor and repeat the while loop, so therefore the cursor will stay on the screen all the time.

tutorial4-moving-cursor is what our source now looks like. Go ahead and re-compile the source and run the dol with gcube and use the Numpad up, down, left and right to move the small square which is our cursor.

Converting source to use Wii IR

We are now good to change our project to compile on the Wii instead of the gamecube. As per the last tutorial open your makefile and change the gamecube_rules to wii_rules in line that reads:

include $(DEVKITPPC)/gamecube_rules

Add the Wii and the Bluetooth libraries to our compiler as shown below:

LIBS	:=	-lwiiuse -lbte -logc -lm

In main.c include section include the wiiuse file:

#include <wiiuse/wpad.h>

As usual we will need to add a W in front of all the PAD_ commands and remove references to PAD_Substick.

When using the Wiimote, we firstly have to tell the system that we want to capture all the Wiimote’s functionality which includes the Infra-red (IR) and the accelerometer. Another thing to tell the system is the resolution we want to bound the IR to. Both these commands are shown below and should be after the WPAD_Init function:

WPAD_SetVRes(0, 640, 480);
WPAD_SetDataFormat(WPAD_CHAN_0, WPAD_FMT_BTNS_ACC_IR); 

The Wiimote’s IR has a data structure which tells us things like are the co-ordinates valid, the x and y axis, the rotation and so forth. This information can be found in “C:\devkitPro\libogc\include\wiiuse\wiiuse.h”.

We need to assign a variable to hold this data structure. We do this by naming the data structure which is ir_t and then assigning that data structure to our variable which is ir. The code below needs to be placed below the #includes which are at the top.

The reason we place the variable up the top is so that our variable will be a global which means it can be called and accessed from anywhere within our source code. If we were to assign it within a function, then the variable would only exist whilst inside that function and could not be called from anywhere else.

ir_t ir; 

So now that we have assigned ir as the data structure ir_t, we need to constantly update the ir variable to get the latest readings from the Wiimote. This is done by using the below code:

WPAD_IR(0, &ir); 

We are updating channel 0 (the first Wiimote’s) IR data and feeding all that information into our ir variable.

The final step involved is to change the variables in the DrawBox function to use ir.x and ir.y instead of cursor_x and cursor_y. As you recall ir is our variable which contains different data structures, one of them being the x and y co-ordinates. As these are like variables in the ir variable, we reference them by using a dot between our main variable (ir) and the data structure variable (ir.x or ir.y).

DrawBox (ir.x, ir.y, ir.x + 1, ir.y + 1, COLOR_WHITE); 

We can remove the cursor_x and cursor_y variables. The while loop looks as shown below:

while(1) {

	WPAD_ScanPads();
u32 pressed = WPAD_ButtonsDown(0);

	// IR Movement
	WPAD_IR(0, &ir);

if (pressed & WPAD_BUTTON_HOME) {
		exit(0);
	}

	VIDEO_ClearFrameBuffer (rmode, xfb, COLOR_BLACK);

	DrawBox (ir.x, ir.y, ir.x + 1, ir.y + 1, COLOR_WHITE);

	VIDEO_WaitVSync();
}

tutorial4-wii-ir is what our source now looks like. Clean the source code, re-compile and tutorialr.elf to boot.elf and place it in a directory called tutorialr under the /apps/ directory of your SD card and load it with the homebrew channel. You will be able to control the dot on your screen with the Wiimote.

48 Responses to “Tutorial 4: Cursors”

  1. Fennec says:

    Great work on these tutorials. They are really helping me develop my own wii homebrew. Keep up the awesome work.

  2. Jake says:

    Great tutorials! I started out with knowing nothing but these tutorials actually make sense and make you learn! Good stuff!

  3. Speggy says:

    there’s really are great but could you add more code sections as i was left wondering how you got from

    PAD_ScanPads();

    if (PAD_StickY(0) > 18) {
    cursor_y–;
    }
    if (PAD_StickY(0) 18) {
    cursor_x++;
    }
    if (PAD_StickX(0) < -18) {
    cursor_x–;
    }

    to

    WPAD_ScanPads();
    u32 pressed = WPAD_ButtonsDown(0);

    // IR Movement
    WPAD_IR(0, &ir);

    if (pressed & WPAD_BUTTON_HOME) {
    exit(0);
    }

    as i left all the Stick X/Y areas and simply changed them to WPAD until i saw the next set of code

  4. teknecal says:

    All the PAD_Substick functions are related to the Gamecube, as the Wiimote has no joystick we remove these Substick commands. For more in depth information about the “pressed” variable and the WPAD_BUTTON_HOME you need to read the programming tutorial 3 – http://www.codemii.com/2008/08/24/tutorial-3-controller-input/

  5. Speggy says:

    i’ve read all of them, it’s just you never mentioned removing the the Substick Commands Just to change PAD to WPAD

  6. lakotajames says:

    Speggy: He did mention removing the substick commands. In tutorial 3, he said that the wii has no sticks so you have to remove the stick commands if you use wiimode. When he said, “We are now good to change our project to compile on the Wii instead of the gamecube,” he assumed that you had read tutorial 3 and knew to remove the commands before you compile.

  7. nathan says:

    I was looking forward to making some wiibrew, but noticed that the thumbstick on the nunchuck is not supported? What’s that about? How come the IR was figured out, but not the thumbstick?

  8. teknecal says:

    The nunchuck has been figured out and can be used in Masteroids. I just haven’t gotten around to using it to be able to put it in the tutorials.

  9. nathan says:

    I also don’t see it in the wpad.h include file

    #define WPAD_NUNCHUK_BUTTON_Z (0×0001<<16)
    #define WPAD_NUNCHUK_BUTTON_C (0×0002<<16)

    That’s it for the nunchuck!

  10. teknecal says:

    You can have a read of this topic that has example nunchuck code: http://forum.wiibrew.org/read.php?11,1334

  11. Deozaan says:

    I like your tutorials. They’re a little confusing though because you introduce new code to type in but you don’t always say where it belongs.

    Thanks for what you’re doing!

  12. Ibrahim says:

    Hi, I was reading your tutorials and they’re pretty nice, so far they’re the only tutorials I can find on Wii development. One thing I was wondering though, you don’t have any error checking code for if the cursor goes off screen, if you try writing to the framebuffer at a negative value won’t that mess with the RAM or something? I haven’t done much C programming, mostly Java, so this stuff is useful for me to learn the Wii stuff since the syntax is more or less the same between C and Java. Thanks again, and hopefully you’ll see some releases from me in the not too distant future.

  13. teknecal says:

    I do recall once that I did get a codedump on the Wii by going too far left or right. Easiest way would be to just change it a bit.

    if (PAD_StickY(0) > 18 && cursor_y > 0) {
    cursor_y–;
    }
    if (PAD_StickY(0) < -18 && cursor_y < 480) {
    cursor_y++;
    }
    etc.

  14. chris says:

    Souldn’t the line xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode)); be xfb = (u32*)MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));?
    As far as I know xfb is of the type u32* while MEM_K0_TO_K1 returns void*, which should be cast to u32* in order to assign it to xfb.
    While your code seems to work in C, changing it as I suggested will fix compatibility to C++.

  15. AndyTheAngel says:

    Hi! I tryed to modify the code so the cursor leaves a line on the screen, and to use the A button so everytime I press it the cursor change color between 3 programmed colours.
    I obtained the line on the screen, but not the change of colour: it starts in red, when I press A it should change to gray, but it change directly to Blue, and I can’t change it anymore. Here is the code (hope you can explain me where I am wrong…):

    int main() {

    Initialise();

    int color = COLOR_RED;

    while(1){
    WPAD_ScanPads();

    u32 pressed = WPAD_ButtonsDown(0);
    WPAD_IR(0, &ir);

    if(pressed & WPAD_BUTTON_HOME){
    exit(0);
    }

    u32 ButtonUp = WPAD_ButtonsUp(0);
    if(ButtonUp & WPAD_BUTTON_A){
    if(color == COLOR_BLUE){
    color = COLOR_RED;
    }
    if(color == COLOR_RED){
    color = COLOR_GRAY;
    }
    if(color == COLOR_GRAY){
    color = COLOR_BLUE;
    }
    }

    /*VIDEO_ClearFrameBuffer (rmode, xfb, COLOR_BLACK);*/
    DrawBox (ir.x,ir.y,ir.x+1,ir.y+1,color);

    VIDEO_WaitVSync();
    }
    return 0;
    }

  16. teknecal says:

    Hi,

    The problem is your if statements. You need to use if and else if statements instead as currently it just executes all the if statements when you press the button.

    Using if and else if just means it will either execute only one if statement in the lot of if and else if statements.

    if(color == COLOR_BLUE){
    color = COLOR_RED;
    }
    else if(color == COLOR_RED){
    color = COLOR_GRAY;
    }
    else if(color == COLOR_GRAY){
    color = COLOR_BLUE;
    }

  17. AndyTheAngel says:

    By the way, another question… I’ve read all your previous tutorials, but I can’t find anywhere what type of variables are u32 and u16… Can you rapidly explain it, please?

  18. Jacic says:

    Great tut! The only thing i could suggest is to include more about where to put the code, i was confused at first. Also, I think the download with the gamecube stick moving the dot is the wrong link, it looks just like the part of the tut just before we make it able to move.

  19. kedest says:

    what are
    x1 >>= 1;
    x2 >>= 1;
    for in the line functions?

    • teknecal says:

      I’ve never really taken a good look at that but it’s a bitwise operator that shifts all the bits x times to the right. I’ve never had to use this one myself.

      For example:
      An integer (x) with a value of 5 would have the bits 00000101.
      Doing x >>= 1 would make that value 00000010. It has just moved all the bits one to the right.

      For more information you can check this link.

  20. north says:

    Hello,

    I downloaded the Gamecube project to test out, tutorial4-moving-cursor
    and when I clean/make then try to run, I’m getting this error:

    > “make” run
    psoload tutorial4.dol
    “make”: psoload: Command not found
    “make”: *** [run] Error 127

    > Process Exit Code: 2
    > Time Taken: 00:00

    I’ve been able to run the template file no prob, just can’t get past this for this example.

    Any suggestions? The tutorials are great by the way

    -north

    • teknecal says:

      If you are running it on the gamecube, you can just open up the dol file directly and then assign it to open with the gcube program found in devkitPro\emulators\gcube,

      For the Wii, you can download/copy wiiload.exe to your tutorial4 directory and then change the run part of the makefile to read:

      run:
      wiiload $(TARGET).dol

  21. Michele says:

    Doing this:

    For the Wii, you can download/copy wiiload.exe to your tutorial4 directory and then change the run part of the makefile to read:

    run:
    wiiload $(TARGET).dol

    I get (on the hbc) the message that’s not a valid wii application?!
    This happen for the next tutorial too!

    Any idea?

  22. n0phear says:

    Hi.
    Thanks so much for these tuorials. I am a developer and am just starting to write apps for the wii.

    I would appreciate to have an article on how to download a file (an xml file for exemple) from the wifi connection. I am sure it would be simple to use the cURL library for this, and how to manage exceptions such as : the internet connexion isn’t configured, the connexion is broken, etc…

  23. BLUWAK says:

    Is there a way to change the dot to a JPEG image?

  24. h0ffman says:

    Great tutorials, I recently started learning C# .BLOAT so this stuff is really similar. Thought I would do a little amendment so when you press and hold the A button on the WiiMote, it draws a box..

    int main() {

    Initialise();

    WPAD_Init();

    WPAD_SetVRes(0, 640, 480);
    WPAD_SetDataFormat(WPAD_CHAN_0, WPAD_FMT_BTNS_ACC_IR);

    int nCursX = 0;
    int nCursY = 0;

    while(1) {

    WPAD_ScanPads();

    u32 pressed = WPAD_ButtonsDown(0);
    u32 held = WPAD_ButtonsHeld(0);

    if (pressed & WPAD_BUTTON_HOME){
    exit(0);};

    WPAD_IR(0, &ir);

    if (pressed & WPAD_BUTTON_A){
    nCursX = ir.x;
    nCursY = ir.y;
    };

    VIDEO_ClearFrameBuffer (rmode, xfb, COLOR_BLACK);

    if (held & WPAD_BUTTON_A){
    DrawBox (nCursX, nCursY, ir.x, ir.y, COLOR_WHITE);
    }
    else{
    DrawBox (ir.x, ir.y, ir.x + 1, ir.y + 1, COLOR_WHITE);};

    VIDEO_WaitVSync();
    };
    return 0;
    }

  25. Jesse says:

    My problem is that whenever I try to run the .dol file with gcube, gcube just pops up for half a second and closes, and then when I put it on my SD Card and run it with the HBC the Wiimote doesn’t ever connect… help?

  26. kevin says:

    just thank you :)

  27. Thomas says:

    Thanks for the tutorial, very nice! Just wondering, how can I use more than one controller?

    • teknecal says:

      You would change the (0) value to (1) for the second controller, (2) for the third controller, etc.

      ir_t ir1;
      WPAD_IR(1, &ir1);
      u32 pressed_ctrl1 = WPAD_ButtonsDown(1);

  28. Joshua says:

    I am trouble adapting this technique to a project that uses double buffering and textured quads. I was going to directly access the frame buffer to do some blurring post effects, but it doesn’t seem to have any effect. I imagine it just has to do with certain GX calls, but I’m not getting any sort of errors. The following code draws my textures properly, but not any of the frame buffer adjustments.

    I have my frame buffer declared as :

    static void *frameBuffer[2] = { NULL, NULL};

    and my blur function which is currently just testing if I can draw to the frame buffer successfully:

    void BlurScreen(int amount)
    {
    u32 *tmpBuffer = (u32 *)frameBuffer[fb];

    int x, y = 50; //arbitrary position for now

    //Lets see if we can make a point appear

    tmpBuffer[y*320+x-1] = 0xffffffff;
    tmpBuffer[(y-1)*320+x-1] = 0xffffffff;
    tmpBuffer[(y-1)*320+x] = 0xffffffff;
    tmpBuffer[y*320+x] = 0xffffffff;
    }

    my GX setup is as follows:

    void Initialise()
    {
    VIDEO_Init();
    WPAD_Init();
    WPAD_SetVRes(0, 640, 480);
    WPAD_SetDataFormat(WPAD_CHAN_0, WPAD_FMT_BTNS_ACC_IR);
    Mtx44 view;
    guVector cam = {320, 240, -110.0f},
    up = { 0.0f, -1.0f, 0.0f},
    look = { 320, 240, 0.0f};

    rmode = VIDEO_GetPreferredMode(NULL);

    fb = 0;
    first_frame = 1;

    frameBuffer[0] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
    frameBuffer[1] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));

    VIDEO_Configure(rmode);
    VIDEO_SetNextFramebuffer(frameBuffer[fb]);
    VIDEO_SetBlack(FALSE);
    VIDEO_Flush();
    VIDEO_WaitVSync();
    if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync();

    fb ^= 1;

    gp_fifo = memalign(32,DEFAULT_FIFO_SIZE);
    memset(gp_fifo,0,DEFAULT_FIFO_SIZE);

    GX_Init(gp_fifo,DEFAULT_FIFO_SIZE);

    GX_SetCopyClear(background, 0×00ffffff);
    GX_SetViewport(0,0,rmode->fbWidth,rmode->efbHeight,0,1);
    yscale = GX_GetYScaleFactor(rmode->efbHeight,rmode->xfbHeight);
    xfbHeight = GX_SetDispCopyYScale(yscale);
    GX_SetScissor(0,0,rmode->fbWidth,rmode->efbHeight);
    GX_SetDispCopySrc(0,0,rmode->fbWidth,rmode->efbHeight);
    GX_SetDispCopyDst(rmode->fbWidth,xfbHeight);
    GX_SetCopyFilter(rmode->aa,rmode->sample_pattern,GX_TRUE,rmode->vfilter);
    GX_SetFieldMode(rmode->field_rendering,((rmode->viHeight==2*rmode->xfbHeight)?GX_ENABLE:GX_DISABLE));

    if (rmode->aa)
    GX_SetPixelFmt(GX_PF_RGB565_Z16, GX_ZC_LINEAR);
    else
    GX_SetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR);

    GX_SetCullMode(GX_CULL_NONE);
    GX_CopyDisp(frameBuffer[fb],GX_TRUE);
    GX_SetDispCopyGamma(GX_GM_1_0);

    GX_InvVtxCache();
    GX_ClearVtxDesc();
    GX_InvalidateTexAll();

    GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_F32, 0);
    GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
    GX_RGBA8, 0);

    GX_SetNumChans(1);
    GX_SetNumTexGens(1);
    GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE);
    GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
    GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2×4, GX_TG_TEX0, GX_IDENTITY);

    GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
    GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);

    guLookAt (view, &cam, &up, &look);
    GX_LoadPosMtxImm(view, GX_PNMTX0);

    guOrtho (perspective, 240, -240, -400, 400, 1.0F, 300.0F);
    GX_LoadProjectionMtx (perspective, GX_ORTHOGRAPHIC);
    }

    After that I just call BlurScreen in my main loop and there is no effect.

    also I have a post draw function:

    void PostDraw()
    {
    GX_DrawDone();

    GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
    GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR);
    GX_SetAlphaUpdate(GX_TRUE);
    GX_SetColorUpdate(GX_TRUE);
    GX_CopyDisp(frameBuffer[fb],GX_TRUE);

    VIDEO_SetNextFramebuffer(frameBuffer[fb]);
    if(first_frame) {
    VIDEO_SetBlack(FALSE);
    first_frame = 0;
    }
    VIDEO_Flush();
    VIDEO_WaitVSync();
    fb ^= 1;
    }

    I have to admit a lot of this is just copy and pasting from existing examples in order to understand GX better, but I’m a little lost in “Cargo” programming at the moment. That is, at lot of lines of code are included because they seem to do something and I’m afraid to remove them and break my textures which are finally working. With the libogc documentation site down I’m not really sure where to go to get info. Any help would be appreciated.

  29. kevin says:

    Thanks man! Coming from a C#/Java/PHP developer, C is insanely difficult, but your tutorials make it a lot easier! <3
    Thank you~

  30. Nelatonin says:

    what does tmpfb mean?

    • teknecal says:

      “Temporary framebuffer” pointer to xfb.

      You could just remove the line (u32 *tmpfb = xfb;) and change the tmpfb in the next line to xfb.

      • Nelatonin says:

        what does “pointer to xfb” mean?

        And what is a temporary framebuffer?

        thanks in advance

        • teknecal says:

          Frame buffer is where the video frame is stored and If you change any part of the frame buffer you will change what you see on the screen. The “tempfb” points to the variable xfb,when you address tempfb it’s like you are addressing the variable xfb.
          So in this code it’s not doing anything useful because you can easily just address xfb anyway. You can remove the line “u32 *tmpfb = xfb;” and modify the line below to read “xfb[ ……” and it will work just fine.

          • Nelatonin says:

            Looking through the code, found this line:

            // Allocate memory for the display in the uncached region
            xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));

            Is this what you mean by “Frame buffer is where the video frame is stored”?

            And one more thing, when you say “If you change any part of the frame buffer you will change what you see on the screen”, what part of the frame buffer are we exactly changing?

            Because, in the code (for eg):

            tmpfb[y+i] = color;

            It seems like you are setting the variable color to tmpfb[y+1] not actually changing tmpfb.

            Sorry if this is a stupid question, I just have a really curious mind. Thanks again in advance.

  31. JwopDk says:

    2 questions:
    1. Would the following code draw a single dot at a given location? (this is your code but edited by me)

    void DrawDot(int x, int y, u32 colour) {
    x >>= 1;
    u32 *tmpfb = xfb;
    tmpfb[x+(640*y)/2] = colour;
    }

    2. Are there any lines that are completely wrong in this code that I made to display a BMP image (yes, it’s been tested and it crashed my wii)?

    // x and y are the image dimensions defined in the BMP header

    int stoi(char *s) {
    int i, j = 0;
    int x[strlen(s)], k, l, a, b, num = 0;
    for (i = 0; s[i]; i++) {
    if (isdigit(s[i])) {
    x[j] = ((int)s[i] – 0×30);
    j++;
    }
    } for (k = 0, l = j-1; k < j; k++, l++) {
    a = (int)pow(10, k);
    if (k == 0) a = 1;
    b = x[j];
    b *= a;
    num += b;
    } return num;
    }

    int x1 = 0, x2 = 0, y1 = 0, y2 = 0;

    if (x % 2 != 0) {
    x1 = 320-((x-1)/2);
    x2 = x1+1;
    }
    else if (x % 2 == 0)
    x1 = x2 = 320-(x/2);
    if (y % 2 != 0) {
    y1 = 240-((y-1)/2);
    y2 = y1+1;
    }
    else if (y % 2 == 0)
    y1 = y2 = 240-(y/2);
    u32 cp[x*y], d;
    for (i = 0; i < y; i++) {
    for (j = i*x*3+i*2+54, k = 0; j <= i*x*3+i*2+66; j += 3, k++) {
    a = (int)buf[i+1];
    b = (int)buf[i];
    c = a|b;
    char t[] = {(char)c, buf[i+2], (char)c, buf[i], ''};
    d = stoi(t);
    cp[k+i*y] = d;
    }
    }
    i = j = k = l = 0;
    printf("\x1b[2J");
    while(1) {
    VIDEO_ClearFrameBuffer(rmode, xfb, COLOUR_BLACK);
    i = j = k = l = 0;
    for (l = 0, j = x1; l < x; l++, j++) {
    for (i = 0, k = y1; i < y; i++) {
    DrawDot(j, k, cp[i+l*y]);
    }
    } WPAD_ScanPads();
    u32 home = WPAD_ButtonsDown(0);
    if (home & WPAD_BUTTON_HOME) {
    printf("\x1b[2J");
    printf("\x1b[16;35HExiting…");
    return 0;
    } VIDEO_WaitVSync();
    }

  32. ese says:

    Love these tutorials!

  33. Blake says:

    How do i use colors not already defined. I want to be able to use any rgb color. I have tried using the hexadecimal colors but they come out as the wrong color, for example, 0xFFFFFF shows up as magenta.

Leave a Reply