Me parece que el linux de GC trae una implementación de las SDL. Al de Wii no se si la han hecho ya...
Yo de tí consultaría con Núvalo que ha portado el MFE a Wii y seguro que de esto entiende.
EDIT: Señores he estado trasteando y después de algunos intentos fallidos he conseguido entender más o menos como poner texto en pantalla y leer los pads de gamecube. Para ello me he basado en el único ejemplo específico de Wii que viene en el devkitpro.
Os adjunto el fuente de un programa que detecta cuando se pulsa el botón A del mando 1 e imprime un mensaje en pantalla. A su vez mientras esté pulsado el botón B imprime los valores de los dos joysticks y de los pulsadores analógicos R y L. También enciende la vibración del mando al pulsar X y la apaga al pulsar Y o Z.
wiisample.zip
Para compilarlo copiais la carpeta "..\devkitPro\examples\wii\template" a alguna otra localización, la renombrais por ejemplo a "wiisample" o lo que querais, borrais el archivo "template.c" y lo sustituís por mi archivo "wiisample.c". Después desde una ventana de comandos entrais en la carpeta que habeis copiado y ejecutais "..\devkitPro\msys\bin\make.exe". Una vez creados el .elf y el .dol los lanzais con vuestro método de carga favorito. Yo he usado el twilight hack.
EXPLICACIÓN DEL CÓDIGO:
#include
#include
#include
#include
#include
#include
Inclusión de librerías estándar de C y las específicas de wii (las dos últimas)
static void *xfb = NULL;
static GXRModeObj *rmode = NULL;
Declaración del puntero que posteriormente se inicializará con la dirección del framebuffer y del puntero que contendrá la estructura que la librería usa para almacenar información sobre el modo de video.
int main(int argc, char **argv) {
VIDEO_Init();
PAD_Init();
PAD_Sync ();
Inicialización del sistema de vídeo y de los pads. Al parecer es muy importante llamar siempre a VIDEO_Init() antes de usar ninguna otra función. La llamada a PAD_Sync() la he añadido yo, aunque no se para que sirve realmente. Tal vez se use para resincronizar los Pads después de que el usuario los desconecte y los vuelva a conectar a la consola. ¿Alguna idea?
switch(VIDEO_GetCurrentTvMode()) {
case VI_NTSC:
rmode = &TVNtsc480IntDf;
break;
case VI_PAL:
rmode = &TVPal528IntDf;
break;
case VI_MPAL:
rmode = &TVMpal480IntDf;
break;
default:
rmode = &TVNtsc480IntDf;
break;
}
Este código comprueba el tipo de TV conectada a la Wii y selecciona un modo de vídeo compatible. Aunque aquí sólo aparecen 3 modos de vídeos la lista es mucho mayor y puede consultarse en el archivo "video_types.h":
extern GXRModeObj TVNtsc240Ds; /*!< Video and render mode configuration for 240 lines,singlefield NTSC mode */
extern GXRModeObj TVNtsc240DsAa; /*!< Video and render mode configuration for 240 lines,singlefield,antialiased NTSC mode */
extern GXRModeObj TVNtsc240Int; /*!< Video and render mode configuration for 240 lines,interlaced NTSC mode */
extern GXRModeObj TVNtsc240IntAa; /*!< Video and render mode configuration for 240 lines,interlaced,antialiased NTSC mode */
extern GXRModeObj TVNtsc480IntDf; /*!< Video and render mode configuration for 480 lines,interlaced,doublefield NTSC mode */
extern GXRModeObj TVNtsc480IntAa; /*!< Video and render mode configuration for 480 lines,interlaced,doublefield,antialiased NTSC mode */
extern GXRModeObj TVNtsc480Prog; /*!< Video and render mode configuration for 480 lines,progressive,singlefield NTSC mode */
extern GXRModeObj TVMpal480IntDf; /*!< Video and render mode configuration for 480 lines,interlaced,doublefield,antialiased PAL mode */
extern GXRModeObj TVPal264Ds; /*!< Video and render mode configuration for 264 lines,singlefield PAL mode */
extern GXRModeObj TVPal264DsAa; /*!< Video and render mode configuration for 264 lines,singlefield,antialiased PAL mode */
extern GXRModeObj TVPal264Int; /*!< Video and render mode configuration for 264 lines,interlaced PAL mode */
extern GXRModeObj TVPal264IntAa; /*!< Video and render mode configuration for 264 lines,interlaced,antialiased PAL mode */
extern GXRModeObj TVPal524IntAa; /*!< Video and render mode configuration for 524 lines,interlaced,antialiased PAL mode */
extern GXRModeObj TVPal528Int; /*!< Video and render mode configuration for 528 lines,interlaced,antialiased PAL mode */
extern GXRModeObj TVPal528IntDf; /*!< Video and render mode configuration for 264 lines,interlaced,doublefield antialiased PAL mode */
extern GXRModeObj TVPal574IntDfScale;
El modo por defecto que la librería escoge para PAL mi TV lo identifica como 480i. De todos esos no se cual será el modo 480p, si es que la librería lo soporta. Por cierto, al parecer MPAL es un sistema de TV que se usa en Brasil nada más. Que exclusivos.
xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
Aquí se solicita un trozo de memoria lo suficientemente grande como para alojar un frame del modo de vídeo que se ha seleccionado.
console_init(xfb,20,20,rmode->fbWidth,rmode->xfbHeight,rmode->fbWidth*VI_DISPLAY_PIX_SZ);
Función que inicia la consola de texto. Los dos primeros parámetros son las coordenadas X e Y donde comenzará el primer carácter. Los dos siguientes son el ancho y el alto en píxeles de la pantalla. El último es el tamaño en bytes que ocupa en memoria una línea horizontal. No es más que el ancho en píxeles multiplicado por 2. En la estructura GXRModeObj hay varios campos relativos al alto de la pantalla. ¿Alguien tiene idea de en que se diferencian?
VIDEO_Configure(rmode);
VIDEO_SetNextFramebuffer(xfb);
VIDEO_SetBlack(FALSE);
VIDEO_Flush();
Las dos primeras líneas le comunican al hardware que inicie el modo de vídeo seleccionado y que lea los gráficos de la zona de memoria que previamente hemos reservado. La tercera línea por más que lo pienso no se que hace. Creo que la cuarta línea se encarga de hacer efectivos todos los comandos de vídeo anteriores.
VIDEO_WaitVSync();
if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync();
Estas tres líneas son una incógnita. Supongo que la primera espera hasta que se produce la señal de retrazo vertical, pero ¿Y las otras dos? Por algún motivo si estamos en un modo de vídeo progresivo hay que esperar un segundo retrazo vertical...
printf("Hello World!\n");
El típico hola mundo.
console_setpos(40,120);
console_setcolor(COLOR_RED, COLOR_BLUE);
printf("And Good Bye!\n");
Estas tres líneas son de mi cosecha. La primera establece la posición en la que se imprimirá el siguiente carácter y la segunda indica que se imprimirá en azul sobre fondo rojo. La lista completa de colores predefinidos puede verse en el archivo "ogc\color.h". Finalmente se imprime un mensaje.
while(1) {
VIDEO_WaitVSync();
Inicio del bucle principal del programa. Antes de hacer nada se espera a la señal de retrazo vertica. Supongo que la última línea podría quitarse sin mayores consecuencias.
PAD_ScanPads ();
Lo que viene a partir de aquí hasta el final del programa es de mi cosecha. Esta función hace una lectura asíncrona de los pads y hay que llamarla para que las funciones PAD_* que vienen a continuación no fallen. Devuelve un u32 que no se que rayos contendrá, así que no lo uso.
if (PAD_ButtonsDown(PAD_CHAN0) & PAD_BUTTON_A)
printf("Button A Pressed!\n");
Si se ha pulsado el botón A del mando 1 desde la última llamada a PAD_ScanPads() imprimir un mensaje. Se pueden leer 4 pads, dede PAD_CHAN0 hasta PAD_CHAN3. La lista completa de botones que se pueden leer de cada pad figura en el archivo "ogc\pad.h".
if (PAD_ButtonsHeld(PAD_CHAN0) & PAD_BUTTON_B)
{
printf("Button B Held!\n");
printf("StickX: %d; StickY: %d\n", (s32)PAD_StickX(PAD_CHAN0), (s32)PAD_StickY(PAD_CHAN0));
printf("SubStickX: %d; SubStickY: %d\n", (s32)PAD_SubStickX(PAD_CHAN0), (s32)PAD_SubStickY(PAD_CHAN0));
printf("TriggerL: %u; TriggerR: %u\n", (u32)PAD_TriggerL(PAD_CHAN0), (u32)PAD_TriggerR(PAD_CHAN0));
}
Si se está pulsando el botón B del mando 1 ¿dede la última llamada a PAD_ScanPads()? imprimir un mensaje y la información de las coordenadas del Stick (mando analógico izquierdo), Substick (mando analógico derecho) y pulsadores R y L. Como todos sabreis los pulsadores R y L tienen un recorrido y se puede detectar como de pulsados están. Adicionalmente tienen una pulsación final (se nota un click) que habría que leer de forma análoga a como se leen los botones normales (A, B, X, Y, etc...).
if (PAD_ButtonsDown(PAD_CHAN0) & PAD_BUTTON_X)
PAD_ControlMotor (PAD_CHAN0, PAD_MOTOR_RUMBLE);
if (PAD_ButtonsDown(PAD_CHAN0) & PAD_BUTTON_Y)
PAD_ControlMotor (PAD_CHAN0, PAD_MOTOR_STOP);
if (PAD_ButtonsDown(PAD_CHAN0) & PAD_TRIGGER_Z)
PAD_ControlMotor (PAD_CHAN0, PAD_MOTOR_STOP_HARD);
Si se pulsa X activar el motor del mando. Si se pulsa Y desactivarlo. Si se pulsa Z desactivarlo "a lo bruto". No se en que situaciones conviene desactivarlo con PAD_MOTOR_STOP y cuando debe usarse PAD_MOTOR_STOP_HARD. ¿Alguna sugerencia?
}
return 0;
}
Se acabó lo que se daba.
He probado el programa con un wavebird de Nintendo y con un mando cutre del Game y ha funcionado correctamente con los dos. Tened en cuenta que el wavebird no tiene vibración. También he notado que los dos mandos tienen sus controles analógicos exactamente en el mismo rango: Los pulsadores R y L van de 0 a 150, el stick de -74 a 74 y el substick de -59 a 59. Que bien copian estos del Game...
Si consultais "ogc\pad.h" os dareis cuenta de que hay otras muchas funciones para acceder al pad. Algunas me imagino para que sirven, ya os comentaré cuando las pruebe. Las funciones PAD_SetSpec, PAD_Clamp, PAD_Reset, PAD_Recalibrate y PAD_SetSamplingCallback o no se cuando hay que usarlas o directamente no tengo ni idea de que hacen. Espero que entre todos lo podamos ir averiguando
Saludos.