Libwiigui/tutorial

From WiiBrew
< Libwiigui
Jump to navigation Jump to search

Foreward

LibwiiGui is written in C++ and uses classes. Classes are a collection of functions and variables related to a single thing. When using these classes, their collected functions and variables (if they are declared public) can be accessed by a dot (.) in some cases, and an arrow (->) in other cases. This will become more understandable later on in the tutorial, but for now, know that when the class instance is created with an asterisk (*) before it, you will be using the arrow (->). When the class is created without the asterisk, you will use the dot (.).

This tutorial uses the same design as the demo included with libwiigui. Every menu is created inside a function. The GUI updating routine is created as a function, and is in a thread. When wanting to change to a knew menu, you set the menu variable to a value that denotes the menu you wish to change to. Again, this will become more apparent later on.

Lastly, before attempting to read this tutorial (or try to use libwiigui for that matter) you should already have at least a basic understanding of C++, if not more. You can easily find some online tutorials (cprogramming.com, cplusplus.com) or a book from your local library.


Setting Up

You first must have devkitPro. DevkitPro is what allows programmer's to compile their programs for other systems, such as the Wii, PSP, and DS. This should be pretty straight-foreward for Windows users. Just download the latest Windows installer, and go through the installer. When asked what to install, you must install devkitPPC. That allows you to compile for the PowerPC architecture, which the Wii uses. You also will need libOGC, which is the library that lets you program for the Wii. Windows users may skip to the horizontal line below.

Linux and Mac users will need to download devkitPPC and libOGC. Select the appropriate version to download, and put them both in a directory (I would suggest making a devkitpro directory in your home folder and putting both in this folder). In Linux, you will want to edit your bash configuration file to define the DEVKITPRO and DEVKITPPC variables. The following code should do the trick for most linux users:

sudo gedit /usr/bash.bashrc

Then input your password, and add the following to the top of the file:

export DEVKITPPC="<path to devkitpro directory>"
eport DEVKITPPC="<path to devkitPPC directory>"

You also may want to download the examples from devkitpro. Put these in your devkitpro directory in a folder named examples.


If you haven't already done so, download libwiigui. You can extract this anywhere, as this is where your source will be for your entire project. You also will need to download the required devkitpro libraries. Extract these to your libogc folder (should be devkitPro/libogc) overwriting any existing files.


Class Creation

There are two ways a class instance can be created. LibwiiGui uses both methods. When you want a class to be able to be accessed by any other function, you will want to create it with an asterisk, which declares a pointer to your class. You will want to use this for your main GuiWindow, which should contain the background, and every other window (these other windows will be your menus). You also will want to initially set it to NULL for safety's sake.

GuiWindow * mainWindow = NULL;

These declarations should be at the top of the file, underneath the includes. Then, when using it, you will use ->

mainWindow->Append(image);

If you have a class that is only relavent to the function you are in (e.g. a menu) you will want to declare the instance inside the function:

int menu_function(){
     GuiWindow menuwindow;
     GuiImage menuimage;
}

and then use the dot:

int menu_function(){
     GuiWindow menuwindow;
     GuiImage menuimage;
     menuwindow.Append(&menuimage);
}


Your First Image

Images are created using the GuiImage class. Images can either be loaded from the sd card, or from a buffer. There are quite a few things you will have to do, though, before you can display your image. Open demo.cpp from the libwiigui template.

First, we must initialize the video hardware, by using the function from video.cpp:

InitVideo();

If you are going to be loading images from the sd card (or using the sd card for any other reason), you will need to initialize it:

fatInitDefault();

Next, you will need to start the Gui updating thread:

InitGUIThreads();

This is what writes any changes you made to the screen. Last, you would call the function that starts your main menu. In the demo, it is:

MainMenu(MENU_SETTINGS);

but it can be whatever you named your menu function.

Now, open menu.cpp. Find the MainMenu function. To load an image, you declare an instance of the GuiImage class. Unless, in the main window, you will want to declare it this way:

GuiImage image(ImageData);

What is the ImageData inside the initializer function? Well before actually creating the image, you must load your image. This can be done by creating an instance of the GuiImageData class:

GuiImageData imagedata(image_source);

The image source is your image buffer, where the contents of the image are help. The image must be in the png format, and have dimensions that are a multiple of 4. To load an image from an sd card to an image buffer, do the following:

FILE *fp = fopen("Path to image on sd card","r");
''todo''

You may be wondering how an image gets loaded into a buffer. Whenever you want to add a new image, you must copy the image to the source/images folder, and add it to filelist.h:

extern const u8		image_name_png[];

extern const u32	image_name_png_size;

Then your image will be put into the image_name_without_.png_png buffer when it is compiled.

Last, you must append your new image to the GuiWindow you are dealing with. Before doing do, you need to suspend the gui thread, so that you are not making changes to variables while the thread is reading them, and resume the thread after appending:

HaltGui();
GuiWindow.Append(&image);
ResumeGui();

Unless the image is declared as a pointer, you must have the & before it to pass the address of your GuiImage to the Append function.

So that's all it takes to load an image. Easy, right? Here's an example of the complete proccess:

main.cpp:

#include <gccore.h>
#include <stdio.h>
#include <stdlib.h>
#include <ogcsys.h>
#include <unistd.h>
#include "FreeTypeGX.h"
#include "video.h"
#include "audio.h"
#include "menu.h"
#include "input.h"
#include "filelist.h"
#include "demo.h"

int ExitRequested = 0;

void initall(){
	InitVideo(); // Initialise video
	InitGUIThreads();
	fatInitDefault();
}

void ExitApp()
{
	ShutoffRumble();
	StopGX();
	exit(0);
}

//---------------------------------------------------------------------------------

int main(int argc, char **argv) {
	initall();
	MainMenu(MAIN_SCREEN);
}

menu.cpp:

#include <gccore.h>
#include <ogcsys.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <wiiuse/wpad.h>

#include "libwiigui/gui.h"
#include "menu.h"
#include "demo.h"
#include "input.h"
#include "filelist.h"

static GuiImageData * pointer[4];
static GuiImage * bgImg = NULL;
static GuiSound * bgMusic = NULL;
static GuiWindow * mainWindow = NULL;
static lwp_t guithread = LWP_THREAD_NULL;
static bool guiHalt = true;


int MenuDefault(){

     int menu=MENU_DEFAULT;
     GuiImageData image_data(image_png);
     GuiImage image(&image_data);
     HaltGui();
     mainWindow->Append(&image);
     ResumeGui();
     while(menu==MENU_DEFAULT){
          VIDEO_WaitVSync();
     }
     return menu;
}

MainMenu(){
void MainMenu(int menu)
{
	int currentMenu = menu;

	#ifdef HW_RVL
	pointer[0] = new GuiImageData(player1_point_png);
	pointer[1] = new GuiImageData(player2_point_png);
	pointer[2] = new GuiImageData(player3_point_png);
	pointer[3] = new GuiImageData(player4_point_png);
	#endif

	mainWindow = new GuiWindow(screenwidth, screenheight);

	bgImg = new GuiImage(screenwidth, screenheight, (GXColor){50, 50, 50, 255});
	bgImg->ColorStripe(30);
	mainWindow->Append(bgImg);

	GuiTrigger trigA;
	trigA.SetSimpleTrigger(-1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A);

	ResumeGui();

	bgMusic = new GuiSound(bg_music_ogg, bg_music_ogg_size, SOUND_OGG);
	bgMusic->SetVolume(50);
	bgMusic->Play(); // startup music

	while(currentMenu != MENU_EXIT)
	{
		switch (currentMenu)
		{
			case MENU_DEFAULT:
				currentMenu = MenuDefault();
				break;
			default: // unrecognized menu
				currentMenu = MenuDefault();
				break;
		}
	}

	ResumeGui();
	ExitRequested = 1;
	while(1) usleep(50);

	HaltGui();

	bgMusic->Stop();
	delete bgMusic;
	delete bgImg;
	delete mainWindow;

	delete pointer[0];
	delete pointer[1];
	delete pointer[2];
	delete pointer[3];

	mainWindow = NULL;
}
}

If you want any other menu names (MENU_DEFAULT), you can define them in menu.h:

menu.h

#ifndef _MENU_H_
#define _MENU_H_

#include <ogcsys.h>

void InitGUIThreads();
void MainMenu (int menuitem);

enum
{
	MENU_EXIT = -1,
	MENU_NONE,
	MENU_SETTINGS,
	MENU_SETTINGS_FILE,
        MENU_DEFAULT
};

#endif

Buttons

Buttons are very useful, as they allow an image (or any other GuiElement) to be "clicked." Buttons are simple to create, just remember, you must Append the button to your GuiWindow, not the GuiImage. To create a button, declare an instance of the GuiButton class, giving the address your GuiImage (or another GuiElement) as the argument:

GuiImage image;
GuiButton button(&image);

Then you can use the button as if it were an image, except you can use SetTrigger, which sets when the button will be considered "clicked." To do this, you must first define a GuiTrigger. A GuiTrigger can be made by declaring an instance, the first argument being the Wiimote channel (use -1 for all), the second being the Wiimote buttons, and the third being the Gamecube buttons. For the buttons, you can do a bitwise-or ( | ) to "add" the buttons together, allowing the trigger to use more than one button.

GuiTrigger trigA;
trigA.SetSimpleTrigger(-1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A);

Here I am setting a GuiTrigger to trigger when the wiimote button A, classic controller button A, or gamecube button A is clicked.

Then, you must set your button's trigger using SetTrigger, giving the address of your GuiTrigger as the only argument:

button.SetTrigger(&trigA);

Now, if your button is clicked, the GetState function will return STATE_CLICKED:

if(button.GetState() == STATE_CLICKED){
   //Do something because button was clicked
}