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