Copy Link
Add to Bookmark
Report

SET 035 0x0C

  

-[ 0x0C ]--------------------------------------------------------------------
-[ Interfaces con SDL ]------------------------------------------------------
-[ by blackngel ]----------------------------------------------------SET-35--


^^
*`* @@ *`* HACK THE WORLD
* *--* *
## by blackngel <blackngel1@gmail.com>
|| <black@set-ezine.org>
* *
* * (C) Copyleft 2008 everybody
_* *_


1 - Prologo
2 - Introduccion
3 - GUI's con SDL
3.1 - Eventos
3.1.1 - Raton
3.1.2 - Teclado
3.2 - Imagenes
3.2.1 - SDL_image
3.2.2 - Ventanas
3.2.3 - Botones
3.3 - Audio
3.3.1 - SDL_mixer
3.3.2 - CD-ROM
3.4 - Texto
3.5 - Varios
3.5.1 - Envoltorios
3.5.2 - SDL_strlcpy & SDL_strlcat
3.5.3 - Threads (hilos)
3.5.4 - Graficos Primitivos
3.6 - Efectos
3.6.1 - Zoom
3.6.2 - Rotaciones
4 - Consola Personal
4.1 - Representacion
4.2 - Escritura y Borrado
4.3 - Scroll
4.4 - Ejecutar comandos
4.5 - Utiles
5 - Conclusion
6 - Referencias



---[ 1 - Prologo

Aqui comienza el camino, estas dispuesto a recorrerlo?

Si estas aburrido de escribir programas en modo consola... si has
experimentado con "ncurses" y su potencia o gracia no te acaba de
persuadir; pero todavia no quieres introducirte en el mundo de Glade,
GTK, Borland C++ Builder o cosas por el estilo, entonces, y solo
entonces puede que SDL sea lo que estas buscando.

SDL es una libreria pensada inicialmente para la creacion de videojuegos
en 2D (puede ayudar en 3D junto con OpenGL). Pero este articulo no
se centra en tal habilidad. Nosotros aprovecharemos esta libreria para
crear interfaces graficas de usuario, mas conocidas como GUI.

Esta libreria nos dara una sensacion de Programacion Orientada a Eventos
(POE, oh Edgar... };-D)



---[ 2 - Introduccion

SDL [1], o Simple DirectMedia Layer es compatible con la mayoria de Sistemas
Operativos, incluyendo Linux, Windows, MacOS, las variantes BSD, y muchos
otros.
Desarrollaremos el codigo bajo Linux y mostraremos como compilar los
programas correctamente, no obstante, portar todo lo aqui descrito a
cualquier otro sistema acaba resultando en algo trivial.

Una de las caracteristicas mas importantes de SDL es su division en
subsistemas tales como video, audio, eventos, cdrom, red, hilos (threads),
manejo de texto y mas... varios de estos subsistemas forman parte de
extensiones que han venido al rescate de la, a veces, arcaica base de SDL.
Se explicaran en su momento y se indicaran las opciones de compilacion
correspondientes.

Gracias a esta forma de trabajar, nosotros podemos elegir los que nos
interesen y empezar a desarrollar nuestras aplicaciones inmediatamente.

Si necesitas un peque~o adelanto para ir cogiendo ideas, esto es para ti [2].

************** NO SUPRIMIRE EL MANEJO DE ERRORES EN EL CODIGO. MUCHOS LO
* IMPORTANTE * HACEN Y SOLO CONSIGUEN CREAR APTITUDES DE PROGRAMACION
************** PEREZOSAS, INCORRECTAS Y LA MAYOR DE LAS VECES PELIGROSAS.



---[ 3 - GUI's con SDL

Bien, empezaremos por a~adir a nuestro programa la cabecera principal:

#include "SDL/SDL.h"

Definiremos una superficie principal que representara la pantalla principal
de la aplicacion durante la ejecucion del programa:

SDL_Surface *pantalla;

Ahora, lo principal es iniciar el sistema SDL en si. La siguiente
funcion nos proporciona esta facilidad:

atexit(SDL_Quit);

if (SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0) {
fprintf(stderr, "Error al iniciar SDL: %s\n", SDL_GetError());
exit(-1);
}

La primera llamada puede haber llamado tu atencion, pero es muy sencillo. La
funcion 'atexit()' registra el nombre de las funciones que seran llamadas antes
de la finalizacion del programa.

Como puedes observar, al iniciar el sistema basico tambien podemos arrancar
otros subsistemas mediante el uso de constantes combinadas con un OR logico.
Podemos utilizar otros como:

SDL_INIT_CDROM
SDL_INIT_TIMER
SDL_INIT_JOYSTICK

O iniciarlos todos con:

SDL_INIT_EVERYTHING

Si has olvidado iniciar un subsistema y no lo has indicado en esta funcion,
todavia puedes iniciarlo de esta forma:

if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) {
fprintf(stderr, "No se puede iniciar el joystick %s\n", SDL_GetError());
exit(1);
}

Podras detenerlos analogamente con la funcion 'SDL_QuitSubSystem()';

Establezcamos seguidamente el modo de video, que creara nuestra ventana
principal con la resolucion y profundidad de color que indiquemos y ciertos
atributos que explicaremos a continuacion:

pantalla = SDL_SetVideoMode(1024, 768, 24, SDL_ANYFORMAT | SDL_DOUBLEBUF);
if (pantalla == NULL){
fprintf(stderr, "No se pudo iniciar el modo de pantalla: %s\n",
SDL_GetError());
SDL_Quit();
exit(1);
}

OPCIONES (flags):

SDL_SWSURFACE -> Superficie en memoria del sistema.
SDL_HWSURFACE -> Superficie en memoria de video.
SDL_ASYNCBLIT -> Actualizacion asincrona.
SDL_ANYFORMAT -> Fuerza el uso de los bpp de la surface actual. Hay que usarlo
cuando queramos crear la superficie en una ventana.
SDL_HWPALETTE -> Da a SDL acceso exclusivo a la paleta de color.
SDL_DOUBLEBUF -> Solo valido con SDL_HWSURFACE. Tecnica del "doble buffer".
SDL_FULLSCREEN -> Visualizacion a pantalla completa.
SDL_OPENGL -> Crea un contexto OpenGL.
SDL_OPENGLBLIT -> Igual a la anterior, pero SDL hace el renderizado 2D.
SDL_RESIZABLE -> La ventana puede cambiar de tama~o.
SDL_NOFRAME -> Crea una ventana sin borde.

Las constantes anteriores puedes encontrarlas directamente en la cabecera
correspondiente, no obstante, yo he cogido las descripciones utilizadas en
este documento [3].

Existen otras 6 constantes, pero son de uso interno y no las podras establecer
con la funcion anterior. Puedes estudiar mas en <SDL/SDL_video.h>.

Por ultimo, si quieres definir el titulo de la ventana de tu aplicacion, puedes
hacerlo mediante:

SDL_WM_SetCaption("Hack The World", NULL);

El segundo parametro es el nombre de un icono opcional, puedes obtener estos
valores con la analoga 'SDL_WM_GetCaption()' y establecer por separado un
icono con 'SDL_WM_SetIcon(SDL_LoadBMP("/home/usuario/icono.bmp"), NULL)'.

Puedes minimizar la pantalla durante la ejecucion del programa mediante la
funcion 'SDL_WM_IconifyWindow()'.

Si quieres ver como funciona tu primera ventana, puedes utilizar un codigo
general de compilacion como este:

$ gcc prog.c -lSDL -o prog

o

$ gcc proc.c `sdl-config --cflags` `sdl-config --libs` -o prog

Vale, hasta aqui tenemos lo basico para iniciar el sistema y comenzar a cargar
graficos (imagenes).



---[ 3.1 - Eventos

Esta es, sin duda alguna, la parte mas importante de una aplicacion creada con
SDL. Se inicia siempre junto al sistema de video y es en resumen la parte del
sistema que nos permite interactuar con la aplicacion, ya sea mediante el
teclado, el raton, un joystick u otros relaciones mas directamente con el
sistema operativo.

Como aperitivo mostrare el bucle principal (main loop) que yo suelo utilizar
para controlar los eventos en mis aplicaciones. Luego dare una peque~a
aclaracion sobre su estructura y funciones, dando paso, ya por ultimo, a las
siguientes secciones que explicaran particularmente el caso de cada dispositivo
de entrada.

**-------------------------------------------------------------------------**

SDL_Event evento;
int terminar = 0;

while (!terminar) {

SDL_WaitEvent(&evento);

switch (evento.type) {
case SDL_QUIT:
terminar = 1;
break;
case SDL_MOUSEBUTTONDOWN:
sonidos(1);
/* printf("\nX=%d - Y=%d\n", evento.button.x, evento.button.y); */
terminar = accion(evento.button.x, evento.button.y);
break;
case SDL_MOUSEMOTION:
motion_cursor(evento.motion.x, evento.motion.y);
break;
case SDL_KEYDOWN:
sonidos(2);
mkeys(evento);
break;
default:
break;
}
}

**-------------------------------------------------------------------------**

Empecemos por el principio. Lo mas basico es el bucle while que estara siempre
iterando hasta que la variable 'terminar' tenga un valor positivo.

Seguidamente viene la funcion mas importante. No me queda mas remedio que
explicar en este momento las funciones de captura de eventos que SDL nos
facilita. Son 3:

- SDL_WaitEvent() -> Esta funcion espera por la llegada de un evento, ya sea
un click, una pulsacion de teclado, la peticion de
cierre de la ventana u otro cualquiera. Se puede decir
que esta funcion es 'bloqueante' pues nada ocurrira
mientras un evento no se produzca y demos una respuesta
al mismo.

- SDL_PollEvent() -> Muy parecida a la anterior, pero esta es mas usual en
los videojuegos. En estos entornos, se precisa que la
aplicacion siga realizando tareas en segundo plano aun
a pesar de que no existan eventos a los que responder.
Esta llamada no es bloqueante y ese es el motivo.

- SDL_PumpEvents() -> Esta funcion nos permitira acceder directamente al
estado de un dispositivo para conocer si algun evento
se ha producido en ese mismo instante. Su uso es menos
habitual y puede que solo la veas ocasionalmente.

Lo que vemos a continuacion es una sentencia 'switch' que maneja el valor
almacenado en el campo 'type' de la estructura 'SDL_Event'. De este modo
sabremos exactamente que evento se ha producido. Puedes entonces introducir
tantas sentencias 'case' como constantes hay definidas en la enumeracion
'SDL_EventType' declarada en el archivo de cabecera <SDL/SDL_events.h>

En este caso hacemos lo siguiente:

1 - Para 'SDL_QUIT', que resulta de hacer click en la [X] de la ventana,
activamos la variable 'terminar' que provoca la salida inmediata del
bucle 'while'.

2 - Para 'SDL_MOUSEBUTTONDOWN', que resulta de hacer click en cualquier
region de la ventana, reproducimos un sonido (se vera en una seccion
posterior), y ejecutamos una accion segun donde se haya clickado (se
vera en la seccion 3.1.1).

3 - Para 'SDL_MOUSEMOTION', que resulta de haber movido el raton dentro
de la ventana, nos comportamos practicamente como en el evento anterior
pero sin reproducir sonido alguno.

4 - Para 'SDL_KEYDOWN', que resulta de presionar cualquier tecla,
reproducimos un sonido y ejecutamos una funcion que se encarga de
controlar que pulsacion hemos realizado exactamente y realizar un
cometido segun corresponda.

Habras observado una sentencia 'printf()' convenientemente comentada. Pues
bien; esta instruccion te sera practicamente imprescindible en la etapa de
desarrollo de tus programas. Imprime las coordenadas donde has hecho click,
y esto sera mas que necesario cuando quieras conocer la posicion de un objeto
situado en pantalla y definir "regiones de clickado" de un modo veloz.

Pasemos ahora a describir las acciones de comportamiento que utilizaremos para
cada dispositivo.



---[ 3.1.1 - Raton

Como acabas de ver hace tan solo unos instantes, cuando un evento 'click' se
produce, las coordenadas de la pulsacion son almacenadas en el elemento
'button' de la union 'SDL_Event'.

He dicho que 'button' es un elemento y no una variable porque en realidad es
otra estructura. Lo que es mas, menos uno, todos los elementos de 'SDL_Event'
son estructuras que controlan todas las propiedades de cada evento.

Pero no nos vayamos del tema. Si sigues echando un vistazo a la estructura
'SDL_MouseButtonEvent', podras ver otros elementos, como cual de los dos botones
del raton se ha pulsado, o si el boton pulsado esta bajando o subiendo, aparte
de otras mas.

Una funcion tipica que ejecute acciones segun las coordenadas donde hayas
pulsado suele tener la siguiente estructura:

**-------------------------------------------------------------------------**

int accion(Uint16 X, Uint16 Y)
{
/* BOTON 1 */
if ((X >= 915 && Y >= 685) && (X <= 1024 && Y <= 718)) {
funcion01(arg1, arg2, ...);
}
/* BOTON 2 */
else if ((X >= 915 && Y >= 648) && (X <= 1024 && Y <= 682)) {
funcion02(arg1, arg2, ...);
}
/* BOTON 3 */
else if ((X >= 915 && Y >= 614) && (X <= 1024 && Y <= 646)) {
funcion03(arg1, arg2, ...);
}
/* BOTON SALIR */
else if ((X >= 915 && Y >= 578) && (X <= 1024 && Y <= 610)) {
return 1;
}
return 0;
}

**-------------------------------------------------------------------------**

La funcion puede llegar a complicarse tanto como desees, pero al fin y al cabo
siempre acaba teniendo una estructura similar.

Puedes pensar ahora para que necesitas una funcion que controle el movimiento
del raton sobre la ventana. Pues es bastante facil, imaginate que tienes unas
imagenes representando botones (se vera mas adelante), podrias desear que los
botones se iluminen cada vez que pasas por encima de ellos.

Pero OJO: Debes prestar especial atencion a la hora de crear esta funcion.
Puede parecer invisible, pero esta funcion se ejecutara cada vez que
el cursor del raton se mueva un solo pixel en cualquier direccion.
Esto quiere decir que la funcion puede ejecutarse cientos de veces
por segundo. Si los condicionales no estan bien definidos y tiene
que recorrer mas de los que deberia, el rendimiento podria verse
seriamente afectado.

No tendras problema alguno en desarrollar la tuya propia, pero quizas veamos
algo mas adelante cuando tratemos con la representacion de botones o menus.



---[ 3.1.2 - Teclado

Para controlar las pulsaciones del teclado accederemos a un miembro de
'SDL_Event' llamado 'key' que es una estructura 'button' y que a su vez
contiene otra estructura mas con el nombre 'SDL_keysym' cuyo elemento mas
importante es el miembro 'sym' que, a pesar de que os entren unas ganas
enormes de asesinarme, es una ultima estructura que define la totalidad de
las constantes referentes a las posibles teclas pulsadas.

Segun la tecla pulsada tu podras hacer lo que creas conveniente. Yo mostrare
aqui un ejemplo que llama a una funcion 'escribir()' para cada tecla pulsada
cuyo objetivo es escribir el caracter correspondiente en la representacion
de una consola (o shell) que estudiaras en un capitulo posterior.

Recortare el codigo:

**-------------------------------------------------------------------------**

void mkeys(SDL_Event ev)
{
int shift = 0;
int altgr = 0;

if (ev.key.keysym.mod & (KMOD_LSHIFT|KMOD_RSHIFT))
shift = 1;
else if (ev.key.keysym.mod & (KMOD_LALT|KMOD_RALT))
altgr = 1;

switch (ev.key.keysym.sym){
case SDLK_ESCAPE:
terminar = 1;
break;
case SDLK_BACKSPACE:
borrar();
break;
case SDLK_RETURN:
ejecutar();
break;
case SDLK_TAB:
escribir('\t', 1);
break;
case SDLK_SPACE:
escribir(' ', 1);
break;
case SDLK_COMMA:
if (shift)
escribir(':', 1);
else
escribir('.', 1);
break;
case SDLK_a:
if (shift)
escribir('A', 1);
else
escribir('a', 1);
break;
case SDLK_b:
if (shift)
escribir('B', 1);
else
escribir('b', 1);
break;
...
...
...
case SDLK_1:
if (shift)
escribir('!', 1);
else if (altgr)
escribir('|', 1);
else
escribir('1', 1);
break;
case SDLK_2:
if (shift)
escribir('"', 1);
else if (altgr)
escribir('@', 1);
else
escribir('2', 1);
break;
...
...
...
default:
break;
}
}

**-------------------------------------------------------------------------**

Como ya he dicho, lo importante es comprender el procedimiento que seguimos
para llevar a cabo nuestros objetivos. Posteriormente echaremos un vistazo
a las funciones que en este caso he utilizado.

Es muy bueno observar el uso que hemos hecho del elemento 'mod' de la
estructura 'SDL_keysym'. Siempre comprobamos si cada vez que pulsamos una
tecla lo hacemos simultaneamente con SHIFT o ALT(GR).



---[ 3.2 - Imagenes


Lo principal es que conozcamos que es una SUPERFICIE. En realidad es una
estructura que controla todos los pixeles de una imagen o region de la pantalla
asi como su profundidad de color y otro tipo de propiedades. Nos sobrara con
saber que se definen siempre igual que la superficie principal:

SDL_Surface *imagen;

Otra estructura basica que debemos estudiar es 'SDL_Rect', sirve para definir
una region en la pantalla indicando sus coordenadas iniciales y el ancho/alto
de la misma. Se acostumbra a utilizar asi:

SDL_Rect rect = (SDL_Rect) {300, 300, 100, 100};

Esto define una region que comienza en las posiciones x=300, y=300, teniendo
una medida de 100 pixeles tanto para el ancho como para el alto.
Si los dos ultimos valores son iguales a 0, cuando dibujemos la superficie
se igualaran automaticamente al ancho y alto de la misma.
Podemos tambien acceder a sus elementos individualmente de esta forma:

rect.x = 300;
rect.y = 300;
rect.w = 100;
rect.h = 100;

SDL nos proporciona una funcion basica para la carga de imagenes, que es
conocida como:

imagen = SDL_LoadBMP("
/home/usuario/file.bmp");

Devuelve siempre el valor NULL cuando no ha podido cargar el archivo. Pero esta
funcion es pobre y no soporta otros formatos. Es aqui donde entran en juego las
librerias auxiliares. En este caso SDL_image.

Antes de pasar a estudiar el uso de esta libreria debemos conocer algunas
funciones mas para el manejo basico de las superficies.

Por ejemplo, ahora que ya tenemos cargada una superficie en la variable
'imagen', la pregunta es: como dibujarla en pantalla?

'SDL_BlitSurface()' al rescate. Mostraremos como se usa y luego explicaremos
sus argumentos:

SDL_BlitSurface(imagen, NULL, pantalla, &rect);

1 - La superficie que queremos dibujar.
2 - La porcion de la superficie que queremos dibujar (SDL_Rect). Un valor
NULL dibuja la superficie completa.
3 - La superficie sobre la que dibujaremos.
4 - Las coordenadas donde imprimiremos la superficie a pintar (SDL_Rect).

No siempre desearemos dibujar nuestras superficies o imagenes directamente
sobre la pantalla principal; pero quedas advertido, si realizas un 'blit' sobre
cualquier otra superficie, luego estaras obligado a realizar el 'blit' de esta
ultima sobre la pantalla principal.

Muy bien, si has llegado hasta aqui y has intentado dibujar una imagen propia
sobre la ventana principal, te preguntaras porque esta no es visible y parece
que la ejecucion de tu aplicacion ha fallado. No te precipites, aqui tienes la
solucion:

SDL_Flip(pantalla);

Esta funcion provoca que la representacion grafica que has creado en memoria
hasta el momento, sea volcada en pantalla. Podrias decir vulgarmente que estas
"
actualizando la pantalla" si te sientes mas comodo.



---[ 3.2.1 - SDL_image

La funcion mas importante que proporciona esta libreria es:

- SDL_Surface * IMG_Load(const char *file)

Puedes usarla tal y como lo has hecho con 'SDL_LoadBMP()'. Pero entonces, cual
es la diferencia?

Una y muy grande. Soporta los siguiente formatos:

- BMP, PNM, XPM, LBM, PCX, GIF, JPG, PNG y TGA.

Puedes incluir esta libreria muy facilmente a~adiendo "
-lSDL_image" a tus
opciones de compilacion normales.

No hay mas que decir, creo que esta mas que claro que esta es la funcion que
empezaras a utilizar a partir de ahora.



---[ 3.2.2 - Ventanas

La cruda realidad es que SDL no comprende entidades como ventanas o botones.
No tiene estructuras para manejar este tipo de objetos y eso supone un paso
hacia atras para nosotros.

En SDL todo es apariencia, y como tal, nuestro objetivo es utilizar una imagen
para darle la apariencia de una ventana y cierto comportamiento que se asemeje
lo suficiente.

Lo normal es utilizar siempre una imagen en formato "
PNG", dado que soporta
las transparencias y ese es un aspecto muy util a la hora de crear una interfaz
atractiva y eficiente. Las imagenes en este formato tiene una alta compresion
y ocupan relativamente poco espacio. Un editor como "
GIMP" puede ayudarte mucho
en tareas como esta.

Encontrar una imagen con el aspecto de una ventana es tan facil como navegar
durante unos minutos por la web o simplemente sacar una captura de pantalla de
una ventana de tu PC y editarla posteriormente para borrar todo su contenido,
dejando unicamente el marco de la misma.

Como aqui no vamos a ver la posibilidad de arrastrar una ventana por la
pantalla, que podria tornarse en una tarea infernal (pues requiere el repintado
continuo de todas las superficies por las que esta pasa), definiremos unas
regiones en las que interactuaran nuestras ventanas individualmente. Lo logico
seria situar las ventanas en cada una de las esquinas de la pantalla. Algo asi:

______________________
|------------- --------|
|| V1 | | V2 ||
|| | | ||
|------------- --------|
|------------- |
|| V3 | === |
|| | === |
|------------- === |
|______________________|

A mi me gusta utilizar dos peque~as funciones para cargar y mostrar en
pantalla una ventana. Las expongo aqui y las explico a continuacion:

**-------------------------------------------------------------------------**

SDL_Surface *v1;

void carga_v1(int put)
{
v1 = IMG_Load(IMAGES"
v1.png");
if (!v1) {
fprintf(stderr, "
No se pudo cargar v1.png\n");
SDL_Quit();
exit(-1);
}

if (put)
put_v1();
}

void put_v1(void)
{
SDL_Rect r1;

r1 = (SDL_Rect) {0, 0, 705, 375};
SDL_FillRect(pantalla, &r1, SDL_MapRGB(pantalla->format,0,0,0));
r1 = (SDL_Rect) {0, 0, 0, 0};
SDL_BlitSurface(v1, NULL, pantalla, &r1);
SDL_Flip(pantalla);
}

**-------------------------------------------------------------------------**

Te preguntaras por que no poner las dos funciones en una. El motivo es el
siguiente. La primera vez que entras en el programa llamaras a la funcion
'carga_v1()' con un argumento positivo; esto cargara la imagen (ventana) y
la mostrara en pantalla. Durante el resto de la ejecucucion del programa,
cuando realices un 'blit' sobre esta ventana, llamaras simplemente a
'put_v1()', que borrara la region y repintara la ventana nuevamente con los
cambios realizados. Solo llamaremos a 'carga_v1()' cuando querramos obtener
una ventana limpia, sin modificaciones.

Bien, imaginate ahora que ya tenemos la imagen de la ventana cargada en la
esquina superior izquierda de la ventana. Imaginate tambien que esa imagen
tiene dibujados dos cuadrados o circunferencias (o lo que sea) representando
los botones de cierre, minimizado, etc...

Tal como se ha explicado en una seccion anterior, podemos definir una "
region
de clickado" que realice una operacion al clickar en esta zona. Podriamos
a~adir a la funcion 'accion()' algo como:

/* BOTON "
CERRAR" DE V1 */
if ((X >= 642 && Y >= 6) && (X <= 662 && Y <= 26)) {
if (is_v1)
quitar_v1();
}

Lo mas facil es que cuando hagamos click en este "
boton", la ventana deje de
ser visible ya sea dibujando un rectangulo negro encima, haciendo que vaya
desapareciendo desplazandose hacia la izquierda, hacia arriba o incluso en
diagonal. Nosotros, que somos asi de atrevidos vamos a optar por esta ultima
eleccion:

**-------------------------------------------------------------------------**

void quitar_v1(void)
{
SDL_Rect pos;
int x=0, y=0;

while(x < 400 && y < 400){
pos = (SDL_Rect) {0, 0, 705, 375};
SDL_FillRect(pantalla, &pos, SDL_MapRGB(pantalla->format,0,0,0));
pos = (SDL_Rect) {0-x, 0-y, v1->w, v1->h};
SDL_BlitSurface(v1, NULL, pantalla, &pos);
SDL_Flip(pantalla);
x += 20;
y += 20;
SDL_Delay(20);
}
is_v1 = 0;
}

**-------------------------------------------------------------------------**

No te ciegues, la operacion no es nada complicada. Simplemente vamos restando
20 a las coordenadas iniciales (0,0) y redibujamos la ventana en cada vuelta.
Entremedias dibujamos siempre un rectangulo negro que cubra toda la zona. Es
esta secuencia de "
quitar y poner" la que nos ofrece una sensacion real de
movimiento. La ventana ira desapareciendo diagonalmente hasta perderse fuera
de la region visible de la pantalla.

'SDL_Delay()' hace una funcion similar a la 'usleep()' que es detener el
programa la cantidad de milisegundos especificados como unico argumento.
Puedes incrementar esta cifra si deseas un movimiento mas lento o bajarla
si deseas el efecto contrario.

Como puedes ver, utilizamos una ultima variable global 'is_v1' que usamos
como si de un dato booleano se tratase y que nos permitira saber en que
estado se encuentra la ventana (abierta/cerrada o visible/oculta).

Deberias tener una lista de botones en la parte inferior de la ventana, o en
aquel sitio que tu elijas, que te permita traer de nuevo las ventanas que
has cerrado (como crear botones simulados sera el tema del siguiente
apartado).

La funcion de regreso de una ventana puede parecerse a esto:

**-------------------------------------------------------------------------**

void traer_v1(void)
{
SDL_Rect pos;
int x=0, y=0;

while(x < 700 && y < 700){
pos = (SDL_Rect) {0, 0, 705, 375};
SDL_FillRect(pantalla, &pos, SDL_MapRGB(pantalla->format,0,0,0));
if (x > -50 && y > 365)
pos = (SDL_Rect) {0, 0, v1->w, v1->h};
else
pos = (SDL_Rect) {-710+x, -768+x, v1->w, v1->h};
SDL_BlitSurface(v1, NULL, pantalla, &pos);
SDL_Flip(pantalla);
x += 20;
y += 20;
SDL_Delay(20);
}
is_v1 = 1;
}

**-------------------------------------------------------------------------**

La operacion es muy parecida. Recuerda tambien que las cifras van a depender
siempre de tu caso en particular y que deberas ajustarlas en su momento.

La unica diferencia en esta ocasion es que es muy improbable que la ultima
iteracion del bucle termine colocando la ventana en sus coordenadas exactas.
Es por ello que controlamos cuando la ventana se acerca a esta posicion y
acabamos por colocarla nosotros mismos en el lugar correspondiente.



---[ 3.2.3 - Botones

Para practicar con la representacion de botones seguiremos el penoso ASCII ART
que utilizamos en el apartado anterior, es decir, que situaremos un boton en la
esquina inferior derecha que abrira un menu cuando hagamos click en el.

Tambien veremos como crear un efecto de iluminado cuando pasemos por encima de
cada uno de los botones. Esto les dara una sensacion de volumen y aumentara
notablemente su credibilidad.

Los botones deben ser sin duda, junto con las fotografias xxx, las imagenes
mas abundantes en la telara~a de la World Wide Web. Incluso si te molestas en
buscar un poco en Google, encontraras unos peque~os programas que te permitiran
crear, on-line, tus propios botones personalizados. Recuerda utilizar formato
"
PNG" preferentemente, si utilizas botones con esquinas redondas este requisito
se vuelve cuasi imprescindible.

La primera funcion que debemos definir, se encarga de cargar las imagenes en
las superficies correspondientes. Segun el argumento, se cargaran los botones
normales o los que hayamos creado con efecto iluminado. Piensa que podrias
utilizar otro 'case' para cargar botones, por ejemplo, con efecto presionado.

**-------------------------------------------------------------------------**

void carga_botones(int op)
{
switch (op) {
case 0: {
boton0 = IMG_Load(IMAGES"
boton0.png");
boton1 = IMG_Load(IMAGES"
boton1.png");
boton2 = IMG_Load(IMAGES"
boton2.png");
boton3 = IMG_Load(IMAGES"
boton3.png");
break;
}
case 1: {
boton0 = IMG_Load(IMAGES"
boton0_ilu.png");
boton1 = IMG_Load(IMAGES"
boton1_ilu.png");
boton2 = IMG_Load(IMAGES"
boton2_ilu.png");
boton3 = IMG_Load(IMAGES"
boton3_ilu.png");
break;
}
}

if (!boton0 || !boton1 || !boton2 || !boton3)
{
fprintf(stderr, "
Don't load some button image\n");
SDL_Quit();
exit(-1);
}
}

**-------------------------------------------------------------------------**

Comprobamos al final que todas las imagenes han podido ser cargadas
correctamente, evitamos asi caidas inesperadas.

La siguiente funcion se encarga de poner los botones que seran estaticos, es
decir, los que no forman parte del menu interno y seran visibles durante toda
la ejecucion. En nuestro caso solo pondremos uno en la esquina inferior
derecha, pero podriamos poner tres en horizontal y hacer que cada uno abriera
un menu diferente siguiendo los pasos que aqui explicaremos.

**-------------------------------------------------------------------------**

void poner_botones(void)
{
SDL_Rect pos;
int x=0, y=0;

carga_botones(0);

pos = (SDL_Rect) {910, 710, 0, 0};
SDL_BlitSurface(boton0, NULL, pantalla, &pos);
SDL_Flip(pantalla);
}

**-------------------------------------------------------------------------**

Puedes ver como llamamos antes de nada a 'carga_botones()' con un valor de 0.
Mientras no interactuemos con ellos, deben de estar en estado normal.

Para lograr recrear la apertura de un menu, primero tenemos que definir una
"
region de clikado" en torno a 'boton0'. Como dije antes, la instruccion
'printf()' que imprime las coordenadas puede servirte de mucho. Haz click
cerca de la esquina superior izquierda de 'boton0' anota la posicion en un
papel y repite el proceso para su esquina inferior derecha. Hecho esto, vete
a la funcion 'accion()' y a~ade algo como:


**-------------------------------------------------------------------------**

/* BOTON 0 */
else if ((X >= 920 && Y >= 720) && (X <= 1024 && Y <= 768)) {
if (is_menu)
cerrar_menu();
else
abrir_menu();
}


**-------------------------------------------------------------------------**

Bien hecho, y ahora vamos directamente a definir las dos funciones tan
esperadas.

**-------------------------------------------------------------------------**

void abrir_menu(void)
{
SDL_Rect rmenu;
int x = 0;

carga_botones(0);

while(x < 250){

if((1030 - x) >= 910){
rmenu = (SDL_Rect) {745, 675, 300, 40};
SDL_FillRect(pantalla, &rmenu, SDL_MapRGB(pantalla->format,0,0,0));
rmenu = (SDL_Rect) {1030-x, 675, 0, 0};
SDL_BlitSurface(boton1, NULL, pantalla, &rmenu);
}
if((1070 - x) >= 910){
rmenu = (SDL_Rect) {745, 640, 300, 40};
SDL_FillRect(pantalla, &rmenu, SDL_MapRGB(pantalla->format,0,0,0));
rmenu = (SDL_Rect) {1070-x, 640, 0, 0};
SDL_BlitSurface(boton2, NULL, pantalla, &rmenu);
}
if((1110 - x) >= 910){
rmenu = (SDL_Rect) {745, 605, 300, 40};
SDL_FillRect(pantalla, &rmenu, SDL_MapRGB(pantalla->format,0,0,0));
rmenu = (SDL_Rect) {1110-x, 605, 0, 0};
SDL_BlitSurface(boton3, NULL, pantalla, &rmenu);
}

SDL_Flip(pantalla);
x += 10;
SDL_Delay(20);
}
is_menu = 1;
}

**-------------------------------------------------------------------------**

El efecto que crea la funcion que acabamos de ver es realmente bonito. Los
botones (1, 2 y 3) van apareciendo escalonados por la parte derecha de la
ventana y se detienen finalmente para quedar situados en una vertical
perfecta.

En realidad lo que ocurre es que empezamos dibujando estos botones fuera de
la zona visible de la pantalla (lo hacemos ya de forma escalonada). Despues
vamos incrementando 'x', cifra que se resta a las coordenadas inciales de
cada boton. De esta forma los botones se van dibujando mas hacia la izquierda
en cada pasada del bucle. 'SDL_Delay()' controla la velocidad y cada boton
se detiene cuando su coordenada horizontal(rmenu.x) llega a 910.

No nos olvidamos de dibujar un rectangulo negro que borra cada boton de forma
individual en cada iteracion del bucle. Con esto evitamos el rastro que irian
dejando los botones en cada operacion de repintado.

Activamos por ultimo una variable global llamada 'is_menu' que indicara a
la funcion 'accion()' que el menu ya ha sido abierto y el proximo click en
'boton0' conllevara el cierre del mismo.

Aqui la funcion analoga:

**-------------------------------------------------------------------------**

void cerrar_menu(void)
{
SDL_Rect rmenu;
int x = 0;

carga_botones(0);

while(x < 150){
rmenu = (SDL_Rect) {745, 570, 300, 150};
SDL_FillRect(pantalla, &rmenu, SDL_MapRGB(pantalla->format,0,0,0));

rmenu = (SDL_Rect) {940+x, 675, 0, 0};
SDL_BlitSurface(boton1, NULL, pantalla, &rmenu);
rmenu = (SDL_Rect) {920+x, 640, 0, 0};
SDL_BlitSurface(boton2, NULL, pantalla, &rmenu);
rmenu = (SDL_Rect) {900+x, 605, 0, 0};
SDL_BlitSurface(boton3, NULL, pantalla, &rmenu);

SDL_Flip(pantalla);
x += 10;
SDL_Delay(20);
}
is_menu = 0;
}

**-------------------------------------------------------------------------**

La diferencia principal es que aqui la variable 'x' se suma a las coordenadas
en cada pasada para ir desplazando los botones hacia la derecha hasta que los
mismos desaparezcan de la pantalla.

Desactivamos is_menu para dejar las cosas limpias.

Muy bien, muy bien. Ya queda poco. Lo prometido es deuda, ahora veremos como
crear el efecto de iluminado de los botones. Veamos el primero de los metodos
para comentarlo posteriormente.

**-------------------------------------------------------------------------**

void motion_cursor(Uint16 X, Uint16 Y)
{
if (is_menu) {
if ((X >= 915 && Y >= 685) && (X <= 1024 && Y <= 718))
cambiar_estado(1, 1);
else if ((X >= 915 && Y >= 648) && (X <= 1024 && Y <= 682))
cambiar_estado(2, 1);
else if ((X >= 915 && Y >= 614) && (X <= 1024 && Y <= 646))
cambiar_estado(3, 1);
else if (st_ilu) {
cambiar_estado(0, 0);
cambiar_estado(1, 0);
cambiar_estado(2, 0);
cambiar_estado(3, 0);
}
}
else if ((X >= 920 && Y >= 720) && (X <= 1024 && Y <= 768))
cambiar_estado(0, 1);
else if (st_ilu) {
cambiar_estado(0, 0);
}
}

**-------------------------------------------------------------------------**

Esta es la esperada funcion que reacciona ante el movimiento del raton.

Pueden ocurrir varias cosas:

(1) Si el menu esta abierto:

(1.1) Si el puntero del raton se situa encima de alguno de los tres
botones (1, 2 o 3), llama a la funcion cambiar_estado() que carga
las imagenes iluminadas y redibuja el boton sobre el que esta el
puntero.

(1.2) En caso contrario, comprobamos si hay algun boton iluminado y los
pasamos todos a modo normal.

(2) Si el menu esta cerrado:

(2.1) Si estamos encima de 'boton0', lo iluminamos (ya dije como).

(2.2) En caso contrario lo apagamos :)

Y ya por fin la funcion que cambia el estado de un boton especifico:

**-------------------------------------------------------------------------**

void cambiar_estado(int b, int s)
{
SDL_Rect pos;

switch (s) {
case 0:{
carga_botones(0);
st_nor = 1;
st_ilu = 0;
break;
}
case 1:{
carga_botones(1);
st_ilu = 1;
st_nor = 0;
break;
}
}

switch (b) {
case 0:{
pos = (SDL_Rect) {910, 710, 0, 0};
SDL_BlitSurface(boton0, NULL, pantalla, &pos);
break;
}
case 1:{
pos = (SDL_Rect) {910, 675, 0, 0};
SDL_BlitSurface(boton1, NULL, pantalla, &pos);
break;
}
case 2:{
pos = (SDL_Rect) {910, 640, 0, 0};
SDL_BlitSurface(boton2, NULL, pantalla, &pos);
break;
}
case 3:{
pos = (SDL_Rect) {910, 605, 0, 0};
SDL_BlitSurface(boton3, NULL, pantalla, &pos);
break;
}
}
SDL_Flip(pantalla);
}

**-------------------------------------------------------------------------**

Facil. El primer argumento indica a que boton se le quiere cambiar su estado.
En el segundo que estado activaremos. Una sentencia 'switch()' para cada uno
y todo arreglado.



---[ 3.3 - Audio

Realizar tareas con el subsistema de audio que proporciona directamente la
libreria SDL es posible, pero puede convertirse en toda una haza~a digna de
comentar.

Es por ello que la dejaremos a un lado centrandonos en la implementacion de
la siguiente libreria auxiliar: SDL_mixer.



---[ 3.3.1 - SDL_mixer

Compilar un programa que utilice esta libreria es tan facil como a~adir el
archivo de cabecera <SDL/SDL_mixer.h> y ayudarse de la opcion "
-lSDL_mixer" a
la hora de utilizar tu version de 'gcc' favorita.

Lo primero es lo primero, que es iniciar el sistema de sonido. Suelo utilizar
algo como lo que veras a continuacion:

if (Mix_OpenAudio(44100, AUDIO_S16, 2, 4096)) {
fprintf(stderr, "
No se puede iniciar SDL_mixer %s\n", Mix_GetError());
SDL_Quit();
exit(1);
}
atexit(Mix_CloseAudio);

Los argumentos son estos:

1 - Frecuencia (en Hertzios) para reproducir un 'sample' (*).

(*) Yo utilizo la calidad CD, otros posibles son:

11025 -> Calidad telefono.
22050 -> Calidad radio.

2 - Formato del sample (revisa las constantes en <SDL_mixer.h>).
3 - Numero de canales (1 = mono, 2 = estereo)
4 - 'Chunksize', esto habitualmente siempre es 4096.

Utiliza Mix_CloseAudio() para detener el sistema.

Quizas te estes preguntando que significa eso de un "
Chunk". Para que lo
entiendas, podriamos decir escuetamente que es la estructura donde SDL_mixer
almacena un sonido para poder trabajar con el.

Ahora expondre una funcion que me gusta utilizar para la reproduccion de
sonidos y luego la explicare en detalle para que se entienda:

**-------------------------------------------------------------------------**

void sonidos(int op){

u_int canal;
Mix_Chunk *sonido;

switch(op){
case 1:{
sonido = Mix_LoadWAV("
/home/usuario/sonidos/sample1.wav");
if(sonido == NULL){
printf("
No pude cargar sonido: %s\n", Mix_GetError());
return;
}
canal = Mix_PlayChannel(-1, sonido, 0);
break;
}
case 2:{
sonido = Mix_LoadWAV("
/home/usuario/sonidos/sample2.wav");
if(sonido == NULL){
printf("
No pude cargar sonido: %s\n", Mix_GetError());
return;
}
canal = Mix_PlayChannel(-1, sonido, 0);
break;
}
...
...
...
default:
break;
}

Mix_FreeChunk(sonido);
}

**-------------------------------------------------------------------------**

'MixLoadWAV()' es analoga a la funcion de carga de imagenes, carga el fichero
indicado en su inco parametro y lo alamace en el chunk que hemos creado.

Comprobamos siempre que la funcion ha tenido exito y luego llamamos sin mas
dilaciones a 'Mix_PlayChannel()' cuyos argumentos son los siguientes:

1 - Numero de canal para reproducir el sonido (*).

(*) Un valor de '-1' para seleccion automatica del canal.

2 - Sonido (chunk) a reproducir.

3 - Numero de veces que se repetira el sonido (*).

(*) Un valor de '-1' para reproducir indefinidamente.

Liberamos finalmente el chunk para mejorar el rendimiento.

Aqui tienes mas funciones, sacadas del archivo de cabecera de esta libreria.
Dare una ligera explicacion de cada una:

- Mix_PlayChannelTimed() -> Igual que la anterior pero lleva un ultimo
argumento que indica cuantos milisegundos se
reproducira.

- Mix_FadeInChannel() y Mix_FadeInChannelTimed() ->

-> Analogas a las 2 anteriores pero va subiendo
el volumen del sonido de modo gradual.

- Mix_Pause(channel), Mix_Resume(channel), Mix_HaltChannel(channel) ->

-> Mas que obvios: Pausar, reanudar y detener.
'channel' es un int.

- Mix_Playing(int channel) y Mix_Paused(int channel) ->

-> Funciones de consulta, con ellas obtienes el
estado actual de un canal.

Y exiten muchisimas mas. Si te interesa crear un reproductor de sonidos o de
musica queda a tu eleccion seguir estudiando este tema.

Y hablando de "
musica", decir que SDL_mixer diferencia realmente entre la
estructura de "
un sonido" y de "una musica". Tanto que reserva un canal especial
para la reproduccion de esta ultima.

Los formatos que reconoce son:

- WAV, MP3, MOD, S3M, IT, XM, Ogg Vorbis, VOC y MIDI

Todas las funciones para manejar una estructura del tipo 'Mix_Music *' son
practicamente analogas a las que controlan "
chunks". No te sera dificil
investigar un poco por tu cuenta.



---[ 3.3.2 - CD-ROM

Para ejecutar las funciones que se ense~aran a continuacion debes incluir en
tus programas la cabecera <SDL/SDL_cdrom.h>.

Existen dos estructuras para el control de un dispositivo CD-ROM.

'SDL_CD' controla el estado del dispositivo, el numero de pistas, la pista
actual, el frame actual y un array de estructuras del tipo 'SDL_CDtrack'.
Tambien tiene un identificador unico para cada uno de los dispositivos.

Como no me voy a poner a mostrar ejemplos sobre el uso de estos metodos.
Indicare como siempre las funciones tal y como se pueden ver en la cabecera
principal y un escueto comentario.

- int SDL_CDNumDrives(void) -> Devuelve el numero de dispositivos CD-ROM.

- const char * SDL_CDName(int drive) -> Devuelve el nombre de un dispositivo.

- SDL_CD * SDL_CDOpen(int drive) -> Abre un dispositivo de CD-ROM.

- CDstatus SDL_CDStatus(SDL_CD *cdrom) -> Devuelve el estado de un CD-ROM.

- int SDL_CDPlayTracks(SDL_CD *cdrom, int start_track, int start_frame,
int ntracks, int nframes) ->

-> Reproduce tantas pistas como se le indique
en 'ntracks' empezando en 'star_track'.


- int SDL_CDPlay(SDL_CD *cdrom, int start, int length) ->

-> Reproduce un CD-ROM desde el frame 'start'
hasta la cantidad indicada por 'lenght'.

- int SDL_CDPause(SDL_CD *cdrom) -> Pausa la ejecucion de una pista.

- int SDL_CDResume(SDL_CD *cdrom) -> Continua la ejecucion de una pista.

- int SDL_CDStop(SDL_CD *cdrom) -> Detiene la ejecucion de una pista.

- int SDL_CDEject(SDL_CD *cdrom) -> Abre la bandeja del CD-ROM.

- SDL_CDClose(SDL_CD *cdrom) -> Cierra el dispositivo CD-ROM.



---[ 3.4 - Texto

Para hacer uso del texto en nuestras aplicaciones SDL utilizaremos otra de las
librerias auxiliares. Se llama 'SDL_ttf' y la estudiaremos inmediatamente.

Deberas utilizar en tu programa el archivo de cabecera <SDL/SDL_ttf.h> y a~adir
la opcion de compilacion "
-lSDL_ttf".

Para iniciar el sistema de texto solemos utilizar algo asi:

if (TTF_Init() < 0) {
fprintf(stderr, "
No se pudo iniciar SDL_ttf: %s\n", SDL_GetError());
SDL_Quit();
exit(1);
}
atexit(TTF_Quit);

Con 'TTF_Quit()' logramos el resultado contrario.

Para poder escribir algo en pantalla, primero debemos abrir una fuente que
tengamos guardada en un directorio e indicar que tama~o de letra deseamos
utilizar. La funcion que viene a continuacion empaqueta estas acciones:

TTF_Font *fuente;

if ((fuente = TTF_OpenFont("
FreeMono.ttf", 20)) == NULL){
fprintf(stderr, "
\nNo se puede abrir la fuente\n");
exit(-1);
}

Como puedes ver, primero declaramos una variable especial que podremos utilizar
posteriormente en las funciones de escritura.

Ahora toca definir el color. Seguimos el mismo procedimiento, declaramos una
variable y le asignamos los atributos:

SDL_Color color;

color.r=0; /* Rojo */
color.g=255; /* Verde */
color.b=0; /* Azul */

Una de las funciones de escritura requiere otro segundo color que se utilizara
como fondo del texto. No repetiremos el proceso pues se crea de la misma forma.

Bien, las funciones principales para escribir en pantalla son 3:

1. TTF_RenderText_Solid(TTF_Font *font, const char *text, SDL_Color fg);

2. TTF_RenderText_Shaded(TTF_Font *font, const char *text, SDL_Color fg,
SDL_color bg);

3.TTF_RenderText_Blended(TTF_Font *font, const char *text, SDL_Color fg);

Te preguntaras que las diferencia. Pues ademas de los ultimos argumentos
que son obvios, difieren tanto en rendimiento como en calidad.
La primera es la que ofrece una calidad mas baja, no obstante precisa de un
tiempo menor para ejecutar su cometido. La tercera es todo lo contrario.
Nosotros siempre utilizaremos la ultima, pues la definicion es perfecta y
en un PC normal no percibiras una merma de rendimiento visible.

Estas funciones no escriben directamente en pantalla, sino que devuelven una
superficie que sera la que posteriormente se imprimira sobre la pantalla en
la region que nosotros deseemos. Un uso habitual seria este:

**-------------------------------------------------------------------------**

SDL_Surface *texto;
SDL_Rect region = (SDL_Rect) {300, 300, 0, 0};

texto = TTF_RenderText_Blended(fuente, "
HACK THE WORLD", color);

SDL_BlitSurface(texto, NULL, pantalla, ®ion);

SDL_FreeSurface(texto);

**-------------------------------------------------------------------------**

La ultima llamada, como habras adivinado, libera la superficie y por lo tanto
la memoria que esta siendo utilizada. Es una forma de trabajar bastante
eficiente.

Con esto deberia ser suficiente para que puedas poner texto aleatorio sobre la
pantalla. No obstante, aqui tienes otras funciones que pueden ser de utilidad:

- TTF_OpenFontIndex() -> Igual a TTF_OpenFont(), pero ademas recibe un 3er
parametro que es un indice para ficheros tff que
contengan varios tipos de letra.

- TTF_SetFontStyle(TTF_Font *, int) y TTf_GetFontStyle(TTF_Font *).

La primera necesita una constante como segundo parametro que puede ser una
de estas:

TTF_STYLE_NORMAL -> Normal
TTF_STYLE_BOLD -> Negrita
TTF_STYLE_ITALIC -> Cursiva
TTF_STYLE_UNDERLINE -> Subrayado

Puedes investigar mas sobre esta libreria leyendo el contenido de su cabecera.



---[ 3.5 - Varios

Quiero dejar claro que esta seccion "
no es prescindible", aqui se explicaran
algunos aspectos que normalmente no entran dentro de las funciones mas basicas
de SDL, pero que pueden proporcionar ciertas habilidades a tu aplicacion que
la convertira en mucho mas eficiente.



---[ 3.5.1 - Envoltorios

El nombre de esta peque~a seccion puede parecer un poco raro; pero en realidad
es la mejor forma de definir a las funciones que SDL ha creado para poder llamar
a los metodos normalmente definidos en tu libreria habitual: <stdio.h>.

La cuestion es que en el archivo de cabecera <SDL/SDL_stdinc.h> podemos
encontrar metodos como:

SDL_calloc()
SDL_free()
SDL_putenv()
SDL_memset()
...
y asi con muchas otras.

Pero en realidad SDL hace siempre una comprobacion de este tipo:

#ifdef HAVE_PUTENV
#define SDL_putenv putenv
#else

lo que viene a decir que siempre que la funcion exista ya en nuestro sistema
operativo, sera utilizada aunque usemos el nombre proporcionado por SDL.



---[ 3.5.2 - SDL_strlcpy & SDL_strlcat

Esto es interesante, muy interesante.

Ya todos somos conocedores de los problemas que implica el uso de funciones
tales como: 'strcpy()' y 'strcat()'. En multiples ocasiones pueden llevar
a situaciones de desbordamiento de buffer indeseables y peligrosas.

Cuando has leido algo acerca de "
buffer overflows" y los autores de estos
articulos te han concienciado de que el uso de 'strncpy()' y 'strncat()' es
la panacea y todos tus problemas han desaparecido, entonces ya se te permite
darte de cabeza contra un muro.

El tema es el siguiente, y muchos deberian saberlo ya. Las funciones anteriores
no comprueban que el ultimo caracter de la cadena a copiar sea un \0. Lo que es
mas, aunque esta lo tuviera, podria llegar a no copiarse. Me explico:

Cadena Orignal: [H][O][L][A][\0] -> Longitud = 5

Si el invocador de la funcion comete un error y llama a la funcion
'strncpy() pensando que la longitud de la cadena "
hola" es 4. Entonces
la cadena destino quedara cortada sin terminar nunca en un caracter \0.

Cadena Destino: [H][O][L][A]

Esto puede dar lugar a implicaciones posteriores que conlleven a errores
dificiles de detectar e incluso fallos de segmentacion.

Existen 2 funciones en los sistemas operativos de la familia BSD llamadas:
'strlcpy' y 'strlcat' que siempre terminan el buffer destino con un caracter
nulo aunque ello conlleve la perdida de un caracter de la cadena original. Un
sacrificio que merece la pena.

Como en un sistema Linux normal no encontraremos estas funciones por defecto,
podemos evitar compilarlas junto con nuestro programa haciendo uso de las
proporcionadas por SDL, que, al no encontrarlas, hara uso de su propia
implementacion.



---[ 3.5.3 Threads (hilos)

Si llevas tiempo en esto de la programacion y las palabras 'thread' y 'proceso'
te son conocidas de sobra. La interpretacion de las funciones que ahora vendran
no te resultaran nada complicadas.

No te lleves la idea equivocada de que una aplicacion que utilice SDL debe
utilizar las siguientes funciones obligatoriamente. Es mas, yo suelo utilizar
directamente las que proporciona <pthread.h>. Pero si te gusta seguir la
armonia con la nomenclatura de tu aplicacion, esto es para tu uso y disfrute.

Debes crear siempre un puntero a una estructura 'SDL_Thread' tal que asi:

SDL_Thread *hilo;

En este puntero recibiras el resultado de la siguiente funcion:

- SDL_Thread * SDL_CreateThread(int (*fn)(void *), void *data)

- Primer argumento -> Una funcion que recibe un argumento 'void *'
- Segundo argumento -> Un valor opcional que puedes pasar a la funcion.

Y aqui tienes otras:

- Uint32 SDL_ThreadID(void) -> Devuelve el identificador del hilo actual.

- Uint32 SDL_GetThreadID(SDL_Thread *thread) -> Devuelve el identificador
del hilo especificado.

- SDL_WaitThread(SDL_Thread *thread, int *status) -> Espera la finalizacion
de un hilo.

- SDL_KillThread(SDL_Thread *th) -> Fuerza la finalizacion de un hilo.



---[ 3.5.4 - Graficos Primitivos

Puede, quizas, que en algun momento necesites dibujar alguna que otra figura
basica tal como un rectangulo, un circulo, una linea o tan siquiera un misero
pixel.

Las funciones incluidas en el archivo de cabecera <SDL/SDL_gfxPrimitives.h>
cubren mas que de sobra esta necesidad. Dare una breve rese~a acerca de la
mayoria de ellas pero no mostrare sus argumentos, pues varian muy poco de una
funcion a otra e inflarian esta guia de manera desproporcionada.

Cuando una de ellas te interese, te esforzaras en investigar como se ejecuta
correctamente.

- pixelColor() -> Dibuja un pixel en una superficie.
- hlineColor() -> Dibuja una linea horizontal en una superficie.
- vlineColor() -> Dibuja una linea vertical en una superficie.
- rectangleColor() -> Dibuja un rectangulo una superficie.
- boxColor() -> Dibuja un cuadrado en una superficie.
- lineColor() -> Dibuja una linea en una superficie.
- circleColor() -> Dibuja un circulo en una superficie.
- ellipseColor() -> Dibuja una elipse en una superifice.
- pieColor() -> Dibuja ... algo relacionado con graficos de quesitos???
- trigonColor() -> Dibuja un triangulo en una superficie.
- polygonColor() -> Dibuja un poligono de 'n' lados en una superficie.
- bezierColor() -> Dibuja una linea curva entre dos puntos de control.
- characterColor() -> Dibuja un caracter en una superficie.
- stringColor() -> Dibuja una cadena de caracteres en una superficie.
- gfxPrimitivesSetFont() -> Establece una fuente para las funciones previas.

Y existen otras "
variantes":

1 - Las que cambian el sufijo 'Color' por 'RGBA' requieren un parametro
'Alfa' que indicara el nivel de transparencia del color.

2 - Las que llevan el prefijo 'filled' que rellenan la figura o poligono
con el color que se le indique.

3 - Otras que utilizan un prefijo como 'aa'.



---[ 3.6 - Efectos

A continuacion detallaremos el funcionamiento de dos funciones que nos
proporciona la cabecera <SDL/SDL_rotozoom.h>. La primera de ellas nos servira
para ampliar superficies o regiones de superficies y la segunda, como indica
tambien su nombre, para rotarlas el angulo que le especifiquemos.

A~ade esto a las opciones de compilacion de tu programa: "
-lSDL_gfx".

---[ 3.6.1 - Zoom

Como ya se ha dicho, esta funcion que se define como:

- SDL_Surface *zoomSurface(SDL_Surface * src, double zoomx,
double zoomy, int smooth);

1 - Provoca el zoom de la superficie pasada com 1er argumento.
2 - Tanto como se indique en el 2do y 3er argumento (factores de escalado).
3 - El ultimo parametro suele ser 0. Si es '1' utiliza anti-aliasing.
4 - Devuelve una nueva superficie ya ampliada.

Podriamos utilizar una funcion bastante general como la que mostrare a
continuacion:

**-------------------------------------------------------------------------**

void do_zoom(SDL_Surface *sup, int sx, int sy, int x, int y)
{
SDL_Surface *szoom;
SDL_Rect pos;

szoom = zoomSurface(sup, sx, sy, 0);
pos = (SDL_Rect){x, y, 0, 0};
SDL_BlitSurface(szoom, NULL, pantalla, &pos);
SDL_Flip(pantalla);

SDL_FreeSurface(szoom);
}

**-------------------------------------------------------------------------**

A esta funcion le indicamos la superficie que queremos ampliar, los factores
de escalado (un valor de 2 doblara el tama~o de la imagen) y las nuevas
coordenadas donde situar esta nueva superficie ampliada.

Imaginese ahora que usted tiene la imagen de un mapa y que quiere que se
produzca la ampliacion de este. Si usted utiliza la funcion anterior, tendria
que indicarle que la nueva superficie se dibuje sobre las mismas coordenadas
en que esta la anterior, pero hay un error. Estas copiando completamente la
nueva superficie y esta es el doble de ancho y de alto. Se saldra de la region
esperada y podria solaparse encima de otros elementos que haya dibujado en su
aplicacion.

La solucion es definir un nuevo 'SDL_Rect' que defina una region igual al
tama~o completo de la imagen original. De esta forma la superficie ampliada
quedara recortada y ocupara el mismo espacio que la anterior.

Pero aun no esta todo dicho. La solucion anterior no es muy practica, pues
siempre estariamos ampliando la misma region de la imagen (esquina superior
izquierda).
Lo habitual es ampliar una zona circundante a aquel lugar donde nosotros
hemos "
clickado".

Entonces ahora debemos comprender un nuevo aspecto. Si nosotros hemos hecho
"
click" en la posicion (x, y), el mismo punto en la superficie ampliada deberia
ser mas o menos (x*2, y*2). Pero ese no debe ser el lugar donde empiece la
imagen ampliada, pues hemos dicho que queremos una region circundante, por lo
tanto tambien tenemos que mostrar una porcion del mapa anterior a esas
coordenadas. Lo logico, entonces, seria utilizar unos puntos de inicio parecidos
a estos ((x*2) - 100, (y*2) - 100). El ancho y el alto de esta nueva superficie
dependera, como siempre, del tama~o de la imagen original.

Un ejemplo practico, pero particular y personal, podria ser el siguiente:

**-------------------------------------------------------------------------**

void do_zoom(SDL_Surface *sup)
{
SDL_Surface *szoom;
SDL_Rect pos, region;

szoom = zoomSurface(sup, 2, 2, 0);
region = (SDL_Rect){(x*2)-50, (y*2)-100, 100, 100};
pos = (SDL_Rect){350, 200, 0, 0};
SDL_BlitSurface(szoom, NULL, pantalla, &pos);
SDL_Flip(pantalla);

SDL_FreeSurface(szoom);
}

**-------------------------------------------------------------------------**

La mejor forma de comprender como funciona esto, es que elabores tus propios
ejemplos y los adaptes a tus necesidades y a las necesidades de tus imagenes.



---[ 3.6.2 - Rotaciones

Bien. Para esta seccion tenemos dos funciones muy similares a la que se acaba
de presentar hace un momento. Aqui las tenemos:

- SDL_Surface *rotozoomSurface(SDL_Surface * src, double angle,
double zoom, int smooth);

- SDL_Surface *rotozoomSurfaceXY(SDL_Surface * src, double angle,
double zoomx, double zoomy, int smooth);

La diferencia entre las dos es bastante evidente. En la primera, el parametro
'zoom' es comun y se aplica proporcionalmente tanto al ancho como al alto de
la imagen a rotar. Un valor de 1 dejara la imagen en su tama~o actual.
En la segunda, como puedes observar, los factores de escalado son individuales.

En principio la imagen rotara en sentido contrario a las agujas del reloj. Lo
que quiere decir que un valor de 90 para el argumento 'angle' rotara la imagen
hacia la derecha.

IMPORTANTE: No te olvides de poner un '1' o '1.0' al valor de 'zoom'. Un valor
de 0 haria invisible tu nueva superficie rotada.

Algo mas hay que tener en cuenta. Y es que si vuelves a dibujar siempre la
imagen rotada en las mismas coordenadas que la original, la imagen estara
girando continuamente sobre una esquina. Seguramente no sea este el efecto que
deseamos.

El siguiente codigo mostrar una funcion generica para rotar imagenes y como
corregir la posicion en cada iteracion:

**-------------------------------------------------------------------------**

void do_rotozoom(SDL_Surface *sup, int x, int y,
float zoomx, float zoomy, float angle)
{
SDL_Surface *szoom;
SDL_Rect pos;

pos = {x, y, 0, 0};

szoom = rotozoomSurfaceXY(src, angle, zoomx, zoomy, 0);

/* Rotar siempre la imagen con respecto al centro */
pos.x -= (szoom->w - sup->w) / 2;
pos.y -= (szoom->h - sup->h) / 2;

SDL_BlitSurface(szoom, NULL, screen, &pos);
SDL_Flip(pantalla);

SDL_FreeSurface(szoom);
}

**-------------------------------------------------------------------------**

Si la imagen a rotar no es un cuadrado perfecto, recuerda siempre borrar esta
poniendo un rectangulo negro encima, asi no quedara solapada con la nueva
pudiendose ver regiones de la anterior.



---[ 4 - Consola Personal

Con lo aprendido hasta este momento, nos atreveremos a crear la
representacion de una consola o shell, bastante arcaica pero funcional.

¿Que utilidad tiene una consola si lo que queremos es precisamente
crear una GUI para hacer las cosas mas faciles? Muy facil, imagina que
estas creando una interfaz para una suite de hacking; seria interesante
que una ventana de la pantalla fuera una consola para poder ejecutar
comandos directamente en el sistema y capturar su salida para imprimirla
en nuestro programa sin tener que minimizarlo y abrir una nueva shell
(menos aun cuando este se ejecuta a pantalla completa).

Todavia mas. Crear la representacion de cuadros de texto (algo que
seguramente ya te habras preguntado) es mas que una odisea en una aplicacion
con SDL. Te invito a que lo intentes y me muestres tus resultados (si lo
hicieras te recomendaria C++, donde puedes convertir realmente cada elemento
de la GUI en un objeto).

Es por ello y mucho mas que necesitas algun modo de proporcionar informacion
a tu aplicacion, ya sean direcciones IP, MAC, registros de agenda, cuentas
bancarias o aquello que tu programa quiera consumir.

Si las razones no te convencen, entonces lee esto igual, aprenderas un
poco mas de como se comportan los graficos e incluso quizas recibas
algun que otro consejo.

<SDL_console.h> puede proporcionarte por si misma varias de las caracteristicas
que se presentaran a continuacion; pero nosotros somos artesanos del codigo y
vamos a hacer algo propio por una vez.

---[ 4.1 -

  
Representacion

Antes de nada necesitamos una estructura que contenga las coordenadas
de nuestro supuesto cursor (de momento sera invisible). La estructura
podria ser la siguiente:

struct position{
int x; /* columnas */
int y; /* lineas */
} post;

Definiremos tambien 2 variables "globales", una que contendra el
comando que estamos escribiendo hasta un maximo de 64 caracteres y
otra que llevara el contador de cuantos se han escrito hasta el
momento:

int ncar = 0; // Contador de caracteres
char comando[64]; // Nos cuidaremos bien de los desbordamientos

Ahora abriremos una fuente y definiremos su color:

TTF_Font *fuente;
SDL_Color fgcolor;

if ((fuente = TTF_OpenFont("FreeMono.ttf", 20)) == NULL){
fprintf(stderr, "\nNo se puede abrir la fuente\n");
exit(-1);
}

fgcolor.r=0;
fgcolor.g=255; // Verde
fgcolor.b=0;

Vale, ¿que es una consola visualmente? Pues diriamos de forma vulgar
que un simple rectangulo negro. Exacto, eso es lo que vamos a crear,
pero un rectangulo en medio de la pantalla quedaria bastante feo.
Seria mas agradable cargar la imagen de una ventana como se ha mostrado
en secciones anteriores y dibujar sobre esta la consola; lo que es mas,
si el interior de esa ventana es totalmente negro, ni siquiera haria
falta este ultimo paso.

Imaginemos entonces que la ventana se situa en las coordenadas (0, 385)
y que su tama~o en pixeles es de (z) por (y).
Podemos crear un rectangulo negro dentro de esta ventana de modo que el
marco quede bien ajustado:

/* Asumire que "pantalla" es la 'SDL_Surface *' principal */

rect = (SDL_Rect) {11, 420, 688, 328};
SDL_FillRect(pantalla, &rect, SDL_MapRGB(pantalla->format,0,0,0));

Se observa que empezamos varios pixeles mas adentro para no "montarnos"
encima de los marcos de la ventana.

Que mas necesitamos? El Prompt, si, eso es importante, lo escribiremos
con:

escribir('#', 0); // Esta funcion se explica en el siguiente apartado
// el 0 en el segundo argumento indica que no forma
// parte de un comando, es un caracter de control.



---[ 4.2 - Escritura y borrado

En esta primera funcion, controlaremos varias cosas.

1 - Si el caracter pertenece a un comando (*).
2 - Si el caracter es un tabulador (\t).
3 - Si el caracter es una nueva linea (\n).
4 - Si estamos al final de una linea, escribiremos en la siguiente.
5 - Si alcanzamos la ultima linea, realizar scroll().

(*) Los caracteres se escribiran en la consola de forma indefinida, pero
solo seran almacenados como parte del comando hasta alcanzar la longtiud
de 64.

**-------------------------------------------------------------------------**

void escribir(char car, int is_cmd)
{
int tab = 0;
int nline = 0;
char line[2];

snprintf(line, 2, "%c\0", car);

// Si aun no se han superado los 64 caracteres y forma
// parte de un comando, lo copiamos al buffer.
if (ncar != 0 && ncar < 64 && is_cmd == 1) {
SDL_strlcat(comando, line, 64);
}

// Si es una nueva linea bajamos el cursor aumentando
// post.y, nos vamos al principio restaurando post.x
if (strncmp(line, "\n", 1) == 0) {
post.y += 20;
post.x = 15;
nline = 1; // Boolean
}
// Si es un tabulador escribimos 7 espacios
else if (strncmp(line, "\t", 1) == 0) {
texto = TTF_RenderText_Blended(fuente, " ", fgcolor);
tab = 1; // Boolean
}
else
texto = TTF_RenderText_Blended(fuente, line, fgcolor);


if(nline != 1){
if (post.x >= 688) { // Si alcanzamos la ultima columna
post.x = 15; // empezamos en una linea nueva.
post.y += 20;
}
if (post.y >= 715) { // Si alcanzamos la ultima linea
scroll(); // realizamos un scroll de la consola
}

// Imprimimos el texto y liberamos la superficie
rconsola = (SDL_Rect) {post.x, post.y, 0, 0};
SDL_BlitSurface(texto, NULL, pantalla, &rconsola);
SDL_FreeSurface(texto);

if (tab == 1)
post.x += 12 * 7; // 12 x tamanyo tabulacion
else
post.x += 12; // Adelantamos el cursor un caracter
}

ncar += 1; // Incrementamos el contador de caracteres
SDL_Flip(pantalla); // Actualizamos la pantalla
}

**-------------------------------------------------------------------------**

Como siempre, la pregunta. Que ocurre visualmente al borrar un caracter?

Que desaparece (de Perogrullo). En nuestro caso, que un peque~o rectangulo
negro lo tapa.

Aqui controlamos lo siguiente:

1 - Si estamos al principio de la consola, no hacemos nada.
2 - Si no hay caracteres escritos, no hacemos nada.
3 - Si estamos al principio de una linea, siempre que esta
sea continuacion de otra anterior, nos movemos al final
de esta para borrar el ultimo caracter.

Nunca debemos olvidar anular el ultimo caracter del array 'comando[]'.

**-------------------------------------------------------------------------**

void borrar(void)
{
if (post.x >= 27) { // Cuidado de no borrar el PROMPT

// Si estamos al principio de todo o en una linea en
// la que no se ha escrito ningun caracter, salimos.
if ((post.x == 27 && post.y == 430) || (ncar == 1))
return;

// Eliminamos el ultimo caracter del buffer
comando[strlen(comando)-1] = '\0';

// Movemos el cursor un caracter atras, dibujamos un cuadradito
// negro para tapar el caracter a borrar y disminuimos el contador.
post.x -= 12;
rconsola = (SDL_Rect) {post.x, post.y, 13, 20};
SDL_FillRect(pantalla, &rconsola, SDL_MapRGB(pantalla->format,0,0,0));
ncar -= 1;
}
else if (post.y > 430) { // Si estamos al comienzo de una linea que es
post.x = 699; // continuacion de otra anterior, saltamos al
post.y -= 20; // final de esta para borrar el caracter que
borrar(); // alli se encuentra.
}
SDL_Flip(pantalla); // Actualizamos la pantalla
}

**-------------------------------------------------------------------------**



---[ 4.3 - Scroll

El metodo es sencillo. Solo debemos tener un poco de imaginacion e intentar
comprender como funcionan los graficos en este sentido.

¿Que ocurre visualmente cuando el scroll entra en accion?

1 - La primera fila de la consola desaparece.
2 - El resto de las lineas se mueven una fila hacia arriba.
3 - Aparece una nueva linea vacia al final de la consola.

¿Como realizar estos simples pasos?

Los pasos 1 y 2 se ejecutan en una misma accion, pues al copiar todo el
contenido de la consola excepto la primera linea, y copiarlo una fila mas
arriba, esta se pierde automaticamente.

El paso 3 es evidente. Creamos un rectangulo negro que se superponga a esta
ultima fila, que tras realizar el paso anterior seria igual a la penultima.

El codigo lo aclara todo:

**-------------------------------------------------------------------------**

static void scroll(void)
{
SDL_Rect orig;

// Copiamos toda la consola excepto la primera linea (la que se pierde)
// y lo pegamos desde el origen de las coordenadas.
orig = (SDL_Rect) {15, 450, 685, 285};
rconsola = (SDL_Rect) {15, 430, 685, 285};
SDL_BlitSurface(pantalla, &orig, pantalla, &rconsola);

// Tapamos la ultima linea con un rectangulo negro
rconsola = (SDL_Rect) {11, 715, 688, 20};
SDL_FillRect(pantalla, &rconsola, SDL_MapRGB(pantalla->format,0,0,0));

post.y -= 20; // Restauramos post.y para no salirnos de la consola
post.x = 15; // Nos vamos al principio de la linea
}

**-------------------------------------------------------------------------**



---[ 4.4 - Ejecutar comandos

Esta funcion es muy simple, aunque puede llegar a ser peligrosa si ejecutamos
el programa como ROOT (por ejemplo con sudo) y no somos conscientes de que
estamos ejecutando comandos con este permiso.

El comando almacenado en 'comando[]' es ejecutado mediante 'popen()' y su
salida es capturada para escribirla caracter a caracter directamente en nuestra
consola.

Los comentarios del metodo son mas que suficientes.

**-------------------------------------------------------------------------**

void ejecutar(void)
{
char c;
FILE *cmd;

// Nueva linea y al principio.
post.y += 20;
post.x = 15;

cmd = popen(comando, "r"); // Ejecuta comando
while((c = fgetc(cmd)) != EOF){ // Lee la salida
fflush(stdout); // Evitar comportamientos extranyos
escribir(c, 0); // Escribimos la salida en la consola
}
pclose(cmd); // Cerramos el descriptor

SDL_strlcpy(comando, "\0", 1); // Vaciamos el comando
ncar = 0; // Contador a 0
escribir('#', 0); // Escribir nuevo PROMPT
}

**-------------------------------------------------------------------------**

Te voy a mostrar un ejemplo real de la funcion que yo utilizo en uno de mis
programas para dar entrada a un monton de informacion que este precisa para
realizar sus operaciones. Tan solo echale un vistazo y quedate con la forma
de operar:

**-------------------------------------------------------------------------**

void ejecutar(void){

char c;
char *tmp;
int n, fp;
int spc = 0;
u_int32_t p[6];

// FILE *cmd;

post.y += 20;
post.x = 15;

if (comando[0] == '$') {
tmp = comando + 1;
free(thost);
thost = (struct hosts *) calloc(1, sizeof(struct hosts));
SDL_strlcpy(thost->h_addr, "\0", 1);
SDL_strlcpy(thost->h_addr, tmp, 15);
spc = 1;
} else if (comando[0] == '@') {
tmp = comando + 1;
n = sscanf(tmp, "%02x:%02x:%02x:%02x:%02x:%02x",
&p[0],&p[1],&p[2],&p[3],&p[4],&p[5]);
if (n != 6) {
asprintf(&cadena, "MAC Address is not valid\n");
salida(1);
}
for (n = 0 ; n < 6 ; n++)
thost->trg_ha[n] = (u_int8_t)p[n];
spc = 1;
} else if(comando[0] == '%') {
tmp = comando + 1;
asprintf(&device, "%s", tmp);
close_net();
init_net(2);
spc = 1;
}

if (strncmp(comando, "help", 4) == 0) {
show_help();
spc = 1;
} else if (strncmp(comando, "clear",5) == 0) {
clear_scr();
spc = 1;
} else if (strncmp(comando, "trace", 5) == 0) {
trace_lines();
put_mapa();
spc = 1;
} else if (strncmp(comando, "cleanmap", 8) == 0) {
clean_gh();
carga_mapa(1);
spc = 1;
} else if (strncmp(comando, "exit", 4) == 0) {
terminar = 1;
spc = 1;
} else if (strncmp(comando, "smsn", 4) == 0) {
sniff_msn();
spc = 1;
} else if (strncmp(comando, "surl", 4) == 0) {
sniff_url();
spc = 1;
} else if (strncmp(comando, "fprint", 6) == 0) {
fingerprint(thost->h_addr, 2);
spc = 1;
}

/* THIS FUNCTION IS VERY INSECURE, IT
* PLAY COMMANDS AS ROOT. UNCOMMENT
* THIS UNDER YOUR OWN RESPONSABILITY.

if (!spc) {
cmd = popen(comando, "r"); // Play command
while ((c = fgetc(cmd)) != EOF) { // Read output command
fflush(stdout); // Flush the buffer
escribir(c, 0); // Print output to console window
}
pclose(cmd); // Close descriptor
}
*/


SDL_strlcpy(comando, "\0", 1);
ncar = 0;
escribir('#', 0);
}

**-------------------------------------------------------------------------**

Deja volar a tu imaginacion.



---[ 4.5 - Utiles

Te presentare aqui dos funciones muy sencillitas que puedes utilizar en
aquellas aplicaciones en que utilices esta consola.

La primera de ellas es el conocido "clear screen" que borra todo el contenido
de la consola:

**-------------------------------------------------------------------------**

void clear_scr(void)
{
SDL_Rect rscr;

rscr = (SDL_Rect) {11, 420, 688, 328};
SDL_FillRect(pantalla, &rscr, SDL_MapRGB(pantalla->format,0,0,0));

post.x = 15;
post.y = 430;
}

**-------------------------------------------------------------------------**

Menuda tonteria, diras. Y estas en lo cierto. Un rectangulo negro que tapa
todo el contenido de la consola y un reinicio de coordenadas. Pensaras que
esta funcion no te vuelve a proporcionar un 'PROMPT', cierto tambien; pero
fijate, si la utilizas dentro de la funcion 'ejecutar()' activandola cuando
'strncmp(comando, "clear", 5) == 0' entonces ya tenemos cubierta nuestra
necesidad.

Imaginate ahora. Has programado un escaner de puertos que ejecutas haciendo
click en uno de los botones del menu y te gustaria que su resultado se
imprimiera por consola. Entonces puedes utilizar esta llamada:

**-------------------------------------------------------------------------**

char *cadena;

void salida(int is_end)
{
while (*cadena) {
escribir(*cadena++, 0);
}

SDL_strlcpy(cadena, "\0", 1);
SDL_strlcpy(comando, "\0", 1);

if (is_end) {
ncar = 0;
escribir('#', 0);
}
}

**-------------------------------------------------------------------------**

Cada vez que quieras imprimir una frase en la consola, puedes escribir en tu
codigo esto:

asprintf(&cadena, "Hack The World by %s\n", "blackngel");
salida(1);

Queda a tu eleccion aqui volver a situar un prompt o dejar la linea preparada
para imprimir a continuacion en otro momento.



---[ 5 - Conclusion

El objetivo de este articulo, que bien pudiera haberse mencionado al
principio, era que pudieses crear tus propias interfaces graficas sin
necesidad de vertelas con un entorno de desarrollo visual cuya potencia
puede a veces desbordar tus capacidades.

Crear aplicaciones con el aspecto de un juego puede llegar a ser
realmente estimulante. Es un mundo entero abierto a la imaginacion
donde puedes elaborar desde interfaces con aspecto futurista y de
ciencia-ficcion hasta lo mas exoterico que a ti se te pueda ocurrir.

Para terminar, un lugar donde encontraras todo o casi todo lo que necesitas
para programar con SDL. Podras leer una rese~a acerca de lo que el futuro le
reserva a SDL. Lo encontraras aqui [4].

Podeis contactar conmigo en <blackngel1@gmail.com>, los insultos seran
bien recibidos siempre que sean lo bastante originales.

Open your eyes, open your mind.



---[ 6 - Referencias

[1] SDL
http://www.libsdl.org

[2] Programacion con SDL
http://www.linux-magazine.es/issue/01/programacionSDL.pdf

[3] Programacion de videojuegos con SDL
http://www.agserrano.com/libros/sdl/%5Bebook%5DProgramacion%20de%20
videojuegos%20con%20SDL.pdf

[4] Tutorial de libSDL para la programacion de videojuegos
http://softwarelibre.uca.es/tutorialSDL/TutorialSDL-30012008.pdf

*EOF*

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT