O primeiro paso e facernos co arquivo vroot.h do xscreensaver (se facemmos apt-get source xscreensaver atoparemos un no directorio utils/ [ou aquí]), e metelo no mesmo directorio no que desenrrolaremos o resto do código. vroot.h é un arquivo (baixo licencia de estilo BSD) usado comunmente para tomar a xanela raíz, na que temos que escreber, por exemplo, se queremos facer un salvapantallas.
Nota: Os comentarios serán en galego, pero os nomes de variables e funcións serán en inglés.
Nota2: Por experiencia aconsello firmemente que non vos limitedes a copiar-encolar o código, senón que o escribades vos mesmos, serve para comprendelo moito mellor ;).
Empezamos logo co noso arquivo, chamaremolo salvapantallas.c e empezamos co código, se ben vai todo xunto os comentarios fan as "presentacións":
#include <stdio.h> #include <stdlib.h> #include <string.h> /* Cabeceiras do X11 */ #include <X11/Xlib.h> #include "vroot.h" /* Este non é necesario para o programa mesmo, mais é moi útil no caso de querer facer comprobacións e que no caso de que resulten mal, deter o programa. */ #include <assert.h> /* Esta será a función que debuxe o salvapantallas por agora non fai nada. */ void drawScreen(Display* display, Window window, XWindowAttributes window_attributes, GC gc){ } /* Na función principal tomaremos a xanela na que escreberemos e pasaremola á función anterior. */ int main (int argc, char **argv){ /* Tomamos o identificador da pantaia. */ char *display_id = getenv("DISPLAY"); /* Supoñemos que o display_id non é nulo. */ assert(display_id != NULL); /* Conectamos coa pantaia. */ Display *display = XOpenDisplay(display_id); /* Obtemos a xanela raíz, é a que amosará o programa. */ Window root = DefaultRootWindow(display); /* Creamos un contexto gráfico, este é o lenzo onde debuxamos. */ GC gc = XCreateGC(display, root, 0, NULL); /* Por último lemos as dimensións da xanela, que sempre é bo saber cales son :P */ XWindowAttributes window_attributes; XGetWindowAttributes(display, root, &window_attributes); /* É só queda dar voltas esperando o usuario */ while (1){ /* Debuxamos algo. */ drawScreen(display, root, window_attributes, gc); /* Forzamos os cambios. */ XFlush(display); /* E damoslle un respiro a CPU. */ usleep(2000); } /* Isto só está para que o compilador esté quedo ;) */ return 0; }
É xa temos ó esqueleto dun salvapantallas, imos meter algun recheo no drawScreen é xa ó configuramos, así se pode ir probando antes de meterse a facer algunha animación en concreto.
(Este código iría no drawScreen)
/* Esta é a función que debuxa o salvapantallas. */ void drawScreen(Display* display, Window window, XWindowAttributes window_attributes, GC gc){ /* Creamos un segundo buffer, neste vanse facendo os cambios antes de pasalos a pantaia, de esta forma acadamos que todo vaia suave. (No primeiro exemplo non fai falla, pero é unha técnica que compre coñecer se se vai traballar cos gráficos directamente. Entón creamos un bitmap baseado na ventá coa mesma altura, anchura e profundidade. */ Pixmap double_buffer = XCreatePixmap(display, window, window_attributes.width, window_attributes.height, window_attributes.depth); /* Debuxamos en negro. */ XSetForeground(display, gc, 0); /* E limpamos o dobre buffer. */ XFillRectangle(display, double_buffer, gc, 0, 0, window_attributes.height, window_attributes.width); /* O código de exemplo é moi sinxelo, debuxa un cadrado de 4 pixeles que vai dando voltas arredor do centro da pantaia. */ /* Primeiro determinamos a posición. */ static int next_position = 0; /* Isto abulta moito pero non ten gran misterio, so calcula a posición no que debería estar o cadrado. */ int x_center = window_attributes.width / 2, y_center = window_attributes.height / 2; int x_position, y_position; if (next_position < 128){ // Borde superior x_position = x_center + (next_position - 64); y_position = y_center - 64; } else if (next_position < 256){ // Borde dereito x_position = x_center + 64; y_position = y_center + ((next_position % 128) - 64); } else if (next_position < 384){ // Borde inferior x_position = x_center + (64 - (next_position % 128)); y_position = y_center + 64; } else{ // Borde esquerdo x_position = x_center - 64; y_position = y_center + (64 - (next_position % 128)); } next_position = (next_position + 1) % 512; /* Agora debullamos o rectángulo. (Hay que ter en conta que a posición indica a esquina superior esquerda.) */ /* De cor branca. */ XSetForeground(display, gc, 0xFFFFFF); /* Con centro en x_position, y_position . */ XFillRectangle(display, double_buffer, gc, x_position - 2, y_position - 2, 4, 4); /* E pasamolo á pantaia. */ XCopyArea(display, double_buffer, window, gc, 0, 0, window_attributes.width, window_attributes.height, 0, 0); /* Por último eliminamo-lo dobre buffer da memoria. */ XFreePixmap(display, double_buffer); }O grupo de if/elses faise un pouco pesado no medio, pero é o máis sinxelo que atopei que puidese producir unha animación coherente e amosase un pouco de todo :/. Ben, agora a probalo, primeiro compilamolo, fai falla avisarlle o gcc que ten que linkalo coa librería do X11:
gcc salvapantallas.c -o salvapantallas -lX11Feito isto, temos que gardalo en /usr/lib/xscreensaver/
sudo mv salvapantallas /usr/lib/xscreensaver/Feito isto so queda añadilo o arquivo .xscreensaver na carpeta do $HOME, o final da lista para poder atopalo facilmente:
- salvapantallas -root \n\E xa o podemos probar, lanzamos xscreensaver-demo e seleccionandoo amosasenos unha demostración:
Ben, así se fai un salvapantallas, agora imos ver de poñerlle outra animación, o primeiro: borrar todo o código de drawAnimation :), imos debuxar a curva do dragón, de forma simple, e directamente na pantaia:
/* Esta é a función que debuxa o salvapantallas. */ void drawScreen(Display* display, Window window, XWindowAttributes window_attributes, GC gc){ static int step = 0; // nº de paso static int lastg = 0; // Último valor calculado (necesario para o fractal) static int x_position = -1; // Posicion actual na horizontal static int y_position = -1; // Posicion actual na vertical static int direction = 0; // 0 = arriba, 1 = dereita, 2 = abaixo, 3 = esquerda static long color = 0; // Cor actual, 0 = negro, 0xFFFFFF = branco /* Se é a primeira vez ou saímos da pantaia, reiniciamos as coordeadas, a dirección e o número do paso e imos cambiando a cor entre branco e negro. */ if ((x_position < 0) || (x_position >= window_attributes.width) || (y_position < 0) || (y_position >= window_attributes.height)){ x_position = window_attributes.width / 2; y_position = window_attributes.height / 2 ; // Dirección, numero de paso e último valor calculado a 0 direction = step = lastg = 0; color ^= 0xFFFFFF; // Se e negro cambia a branco e o revés } XSetForeground(display, gc, color); // Cálculos do fractal int g = step ^ (step >> 1); int t = (~g) & lastg; lastg = g; if (t == 0){ // Se t == 0, xiramos 90 graos direction = (direction + 1) % 4; } else{ // Dado que en C (-1 % 4) == -1, // usamos 3 como equivalente % 4 direction = (direction + 3) % 4; } // Avanzamos segundo a dirección switch(direction){ case 0: y_position--; break; case 1: x_position++; break; case 2: y_position++; break; case 3: x_position--; break; } XFillRectangle(display, window, gc, x_position, y_position, 1, 1); // Preparamonos para o seguinte paso step++; }
E este é o resultado:
Que vos parece, animadevos a facer un vos tamén, todo sexa por mirar para os colorinchos :P.
ps: Aínda que tirara de tantos static, non se debe facer, foi por mor de evitar andar a facer cambios arredor de todo o código, non é algo que se deba facer en código limpo ;)
Saúdos
No hay comentarios:
Publicar un comentario