        |
sdlui: tutorial01: Hello World
(Volver a la lista de tutoriales)

Sources:sdlui_tutorial01.tar.gz
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| /* * sdlui_tutorial01.c * * "Hello World" */
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <signal.h> #include "sdlui.h"
#define DEFAULTWIDTH 320 #define DEFAULTHEIGHT 200
/* local vars */ volatile int flag_sigint;
/* local func prototypes */ static int install_signal(int num,void (*f)(int)); static void sigint(int num);
int main(int argc, char *argv[]) { sdlui_t *sdlui; SDL_Event event; int flag_exit; int neww,newh; char *actionstr, *actionparams; char *p; flag_sigint=flag_exit=0; install_signal(SIGINT,sigint); if((sdlui=sdlui_init("sdlui tutorial 01: Hello World",DEFAULTWIDTH,DEFAULTHEIGHT))==NULL) return(1); p=sdlui_add(sdlui,_F,_L,"label:.l","-text \"Hello World!\" -click \"clicked\""); sdlui_pack(sdlui,_F,_L,p,"-side top -fill both -expand true -anchor center"); while(flag_sigint==0 & flag_exit==0) { if(sdlui_isdirty(sdlui)) { sdlui_ghostrender(sdlui); sdlui_waitfps(sdlui); sdlui_render(sdlui); } else { sdlui_waitfps(sdlui); } sdlui_refreshevents(sdlui); while(sdlui_getevent(sdlui,&event)==0) { if(sdlui_event_isquit(sdlui,&event)) flag_exit=1; if(sdlui_event_isdirty(sdlui,&event)) sdlui_setdirty(sdlui); if(sdlui_event_isresize(sdlui,&event,&neww,&newh)) sdlui_resize(sdlui,neww,newh); } while((actionstr=sdlui_getactionstr(sdlui,&actionparams))!=NULL) { if(strcmp(actionstr,"clicked")==0) { fprintf(stderr,"Clicked!\n"); sdlui_config(sdlui,_F,_L,".l","-text \"Hello World Again!\""); } } } sdlui_free(sdlui),sdlui=NULL; return(0); }
/* local func bodies */ static int install_signal(int num,void (*f)(int)) { struct sigaction sact; sact.sa_handler=f; sigemptyset(&sact.sa_mask); sact.sa_flags=0; return(sigaction(num,&sact,0)); }
static void sigint(int num) { flag_sigint=1; }
|
A continuación, se explica cada parte relevante.
Cabecera
12 13 14 15
| #include "sdlui.h"
#define DEFAULTWIDTH 320 #define DEFAULTHEIGHT 200
|
El fichero de cabecera que hay que incluir es el "sdlui.h".
En estos momentos, el tamaño de la ventana es fijo. Para que sea fácil modificarlo, definimos unas constantes con el tamaño deseado.
Inicialización
27 ... 32 ... 35 36 37 38
| sdlui_t *sdlui; ... char *p; ... if((sdlui=sdlui_init("sdlui tutorial 01: Hello World",DEFAULTWIDTH,DEFAULTHEIGHT))==NULL) return(1); p=sdlui_add(sdlui,_F,_L,"label:.l","-text \"Hello World!\" -click \"clicked\""); sdlui_pack(sdlui,_F,_L,p,"-side top -fill both -expand true -anchor center");
|
Necesitamos dos variables, un "sdlui_t *" para guardar el manejador del ui, y un "char *" para guardar temporalmente el nombre del widget recién creado.
La inicialización de la librería se hace con el sdlui_init( char *titulo_ventana, int ancho_ventana_px, int alto_ventana_px). En caso de que la inicialización sea correcta, devuelve el manejador; en caso de error, devuelve NULL y nos salimos del programa.
Notas sobre los widgets
A continuación creamos el interfaz usando widgets.
Hay tres tipos de widgets principales:
- "label" que es un texto (opcionalmente clicable)
- "frame" que es un contenedor. Es necesario para poder agrupar cosas verticalmente u horizontalmente. También es útil para poner diferentes tipos de bordes.
- "image" que es para poner una imagen, icono, etc.
Aparte de esos, hay widgets "compuestos", que son plantillas formadas por varios widgets para conseguir un elemento más complicado. Una vez definida la plantilla (que se inicializa usando la función sdlui_composite_register() y luego se rellena de widgets de manera análoga a como se hace un UI), se puede usar como si fuera un nuevo tipo de widget.
El UI se construye en forma de árbol, y cada elemento tiene un path dentro de ese árbol. Los elementos del path se separan por puntos ("."), y el elemento raíz es sólamente el ".".
Todos los elementos pueden tener hijos, y los hijos ocupan un porción del elemento padre.
Así pues, si queremos generar un label que cuelga del raíz y le ponemos como nombre "l", su path sería ".l".
En un ejemplo más complicado podríamos tener un texto "menu_1" que cuelga de un frame "barra_menu" que a su vez cuelga del raíz. Eso nos daría el nombre ".barra_menu.menu_1".
Para añadir un elemento al UI, se usa la función sdlui_add(sdlui_t *sdlui, char *debug_file, int debug_line, char *nombre, char *format, ...). Tiene cinco parámetros "obligatorios", del cual el último es una cadena de formato tipo printf.
En el primer parámetro a sdlui_add() hay que poner el manejador que obtuvimos al inicializar la librería. Los siguientes dos parámetros se rellenan con las macros "_F,_L" y son para poder poner mensajes de error por pantalla que te permitan depurar las cadenas tipo printf. A continuación está el tipo y nombre del widget (con su path completo), separados por un dos-puntos (como en "label:.l"); para el sdlui_config() el tipo no es necesario ponerlo (basta con ".l"). El siguiente parámetro, el de formato, indica los atributos del widget.
Todos los widgets tienen unos atributos asociados (el texto de un label, la imagen de un image, el callback de un click, etc). Los atributos se pueden asignar en el momento de creación con sdlui_add() o bien añadirlos o modificarlos más adelante con sdlui_config().
Cada uno de los atributos se especifica con un guión seguido del nombre del atributo, y a continuación (separado por un solo espacio), el contenido del atributo, por ejemplo "-text Hola".
El contenido del atributo puede ser:
- Un literal; si tiene espacios hay que ponerlo entre comillas, como en
sdlui_add(sdlui,_F,_L,"label:.l","-text \"¡Hola mundo!\"");
- Un número pasado como parámetro,
sdlui_add(sdlui,_F,_L,"label:.l","-text %i", 18);
- Una cadena pasada como parámetro,
sdlui_add(sdlui,_F,_L,"label:.l","-text %s","¡Hola mundo!")
- Un búffer pasado como parámetro (que en sdlui se llaman "textvariable"),
char *mibuffer[1024]={"Hola"}; sdlui_add(sdlui,_F,_L,"label:.l","-text %i%s", sizeof(mibuffer),mibuffer);
- Un callback junto con su userptr (NOTA:additionaldata suele ser NULL)
void micallback(sdlui_t *sdlui,char *widgetname,void *userptr,char *additionaldata); ... sdlui_add(sdlui,_F,_L,"label:.l","-click %p%p", micallback,"miuserptr");
- Una imagen en un búffer rgba+ancho+alto (esto es sólo un ejemplo intentando no usar más API que el que hemos descrito hasta el momento; la librería incluye funciones para el manejo de imágenes que simplifican la carga/gestión de imágenes usando SDLUI_ASSET(sdlui,assetname))
#include <sicopng.h> ... char *img; int w,h; if((img=NULL)!=NULL || pngfileinfo("img.png",&w,&h)!=0 || (img=malloc(w*h*4))==NULL || pngfile2raw("img.png",w,h,img)!=0) free(img),img=NULL,w=0,h=0; sdlui_add(sdlui,_F,_L,"image:.l","-src %p%i%i", img, w, h);
Creación del interfaz
27 ... 32 ... 35 36 37 38
| sdlui_t *sdlui; ... char *p; ... if((sdlui=sdlui_init("sdlui tutorial 01: Hello World",DEFAULTWIDTH,DEFAULTHEIGHT))==NULL) return(1); p=sdlui_add(sdlui,_F,_L,"label:.l","-text \"Hello World!\" -click \"clicked\""); sdlui_pack(sdlui,_F,_L,p,"-side top -fill both -expand true -anchor center");
|
Hay dos fases para añadir un widget:
1. Creación del widget son sdlui_add()
2. Especificar cómo se pone dicho widget usando sdlui_pack() (alternativa: ponerlo en una posición fija usando sdlui_place() y no usar sdlui_pack() )
Además, a posteriori podemos reconfigurar el widget:
- modificar atributos con sdlui_config()
sdlui_add(): En el caso que nos ocupa, el widget tipo label tiene un atributo llamado "-text" que es el texto a poner en el label. Además damos de alta otro atributo llamado "-click" que le dice una acción a realizar cuando se clica en el elemento. El texto lo ponemos como un literal, y en el caso de click, al poner una cadena le decimos que en vez un un callback, queremos que mande una "actionstr" al bucle principal.
sdlui_pack(): Las opciones del pack no son atributos, sino que son estáticas. En caso de querer cambiarlas, hay que hacer un nuevo pack. Las opciones que hay que poner normalmente son:
- "-side { top | left | right | bottom }" A qué lado del hueco que representa el widget padre, después de haber quitado lo que ocupan los widgets ya empaquetados en dicho widget padre.
- "-fill { x | y | both | none }" Si se quiere que amplíe el tamaño del widget al tamaño del hueco que queda libre o no.
- "-expand { true | false }" Es el modo "avaricioso"; los que tienen "-expand true" se llevan el espacio libre, sin dejar nada para los que tienen "-expand false".
- "-anchor { nw | n | ne | w | center | e | sw | s | se }" Las letras representan la inicial de los puntos cardinales (north, east, south, west), y esto sirve para colocar el texto o la imagen pegado/a a donde digan esas iniciales.
Para el resto de las opciones de sdlui_pack(), ver "sdlui.h".
Refresco de la pantalla
40 41 42 43 44 45 46
| if(sdlui_isdirty(sdlui)) { sdlui_ghostrender(sdlui); sdlui_waitfps(sdlui); sdlui_render(sdlui); } else { sdlui_waitfps(sdlui); }
|
En el bucle principal hay que poner el refresco de la pantalla. Se hace la manera que dice este código para que no haya saltos en las animaciones.
- ghostrender hace los cálculos previos y deja preparada la imagen para poner en pantalla.
- waitfps hace un sleep de los ms necesarios hasta que sea el momento del siguiente refresco (si está a 60fps, son a lo más 17ms).
- render pone en pantalla la imagen que corresponde al momento actual.
Eventos y acciones
28 ... 31 ... 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| SDL_Event event; ... char *actionstr, *actionparams; ... sdlui_refreshevents(sdlui); while(sdlui_getevent(sdlui,&event)==0) { if(sdlui_event_isquit(sdlui,&event)) flag_exit=1; if(sdlui_event_isdirty(sdlui,&event)) sdlui_setdirty(sdlui); if(sdlui_event_isresize(sdlui,&event,&neww,&newh)) sdlui_resize(sdlui,neww,newh); } while((actionstr=sdlui_getactionstr(sdlui,&actionparams))!=NULL) { if(strcmp(actionstr,"clicked")==0) { fprintf(stderr,"Clicked!\n"); sdlui_config(sdlui,_F,_L,".l","-text \"Hello World Again!\""); } }
|
Para obtener los eventos (tanto del gestor de ventanas como del teclado/ratón), hay que llamar primero a sdlui_refreshevents().
Una vez hecho esto, hay que obtener evento a evento y comprobar si es alguno de los que queremos actuar, y realizar la acción que queramos en su caso.
Si hemos usado "actionstr" (si se han usando literales como callbacks de click, o se ha llamado a sdl_putactionstr() en alguna parte de nuestro código), deberemos obtenerlas una a una y realizar las acciones que veamos convenientes en cada una de ellas.
Salida del programa
63 64
| sdlui_free(sdlui),sdlui=NULL; return(0);
|
Para liberar la memoria ocupada por la librería, basta con llamar a sdlui_free().
|