circulo.c
con cualquier editor ASCII. Para usar funciones de Xlib tenemos que incluir el fichero X11/Xlib.h
. También incluimos los ficheros stdio.h
y math.h
pues usaremos funciones de estos ficheros de cabecera. Por ahora el fichero podría quedar como sigue:
[fontfamily=courier,fontsize=\relsize{-3},frame=single] #include <X11/Xlib.h> #include <stdio.h> #include <math.h> #define CHANGECENTER 0 #define CHANGERADIO 1 #define NOCHANGE 2 Display *display; int screen_num; int main(int argc,char **argv) { int currentX=0,currentY=0,currentR=10; int mode=NOCHANGE; Window win; unsigned int width, height, x, y; /* anchura de ventana y posición */ unsigned int borderwidth = 4; /* 4 pixels */ unsigned int display_width, display_height; unsigned long foreground_pixel, background_pixel; /* valores de pixel (color) */ XEvent report; GC gc; XGCValues values; }
El primer paso que hará el programa será la conexión con el display X mediante la función XOpenDisplay()
:
[fontfamily=courier,fontsize=\relsize{-3},frame=single] if ( (display=XOpenDisplay(NULL)) == NULL ){ (void) fprintf(stderr,"circulo: no puedo conectar al servidor X %s\\n"); exit( -1 ); }
A continuación inializamos la variable screen_num
con:
[fontfamily=courier,fontsize=\relsize{-3},frame=single] screen_num = DefaultScreen(display);
Ahora calculamos anchura, altura y posición de la ventana en la siguiente forma:
[fontfamily=courier,fontsize=\relsize{-3},frame=single] display_width = DisplayWidth(display, screen_num); display_height = DisplayHeight(display, screen_num); x = display_width/3, y = display_height/3; width = display_width/3, height = display_height/4;
Los colores usados como foreground y background los definimos de la siguiente forma:
[fontfamily=courier,fontsize=\relsize{-3},frame=single] background_pixel=WhitePixel(display,screen_num); foreground_pixel=BlackPixel(display,screen_num);
Creamos la ventana de la aplicación:
[fontfamily=courier,fontsize=\relsize{-3},frame=single] win = XCreateSimpleWindow(display, RootWindow(display,screen_num), x, y, width, height, borderwidth, BlackPixel(display,screen_num), background_pixel);
Seleccionamos los eventos Expose
(pérdida de contenidos en la ventana), ButtonPress
(pulsación de un botón del ratón) y MotionNotify
(movimiento del cursor del ratón) en la ventana de la aplicación:
[fontfamily=courier,fontsize=\relsize{-3},frame=single] XSelectInput(display, win, ExposureMask | ButtonPressMask| ButtonMotionMask);
Creamos el contexto gráfico que será usado para dibujar el círculo en la ventana:
[fontfamily=courier,fontsize=\relsize{-3},frame=single] gc = XCreateGC(display, win, 0 , &values);
Mapeamos la ventana en el display (hacemos que sea visible en pantalla):
[fontfamily=courier,fontsize=\relsize{-3},frame=single] XMapWindow(display, win);
El programa tendría por ahora el siguiente contenido:
[fontfamily=courier,fontsize=\relsize{-3},frame=single] #include <X11/Xlib.h> #include <stdio.h> #include <math.h> #define CHANGECENTER 0 #define CHANGERADIO 1 #define NOCHANGE 2 Display *display; int screen_num; int main(int argc,char **argv) { int currentX=0,currentY=0,currentR=10; int mode=NOCHANGE; Window win; unsigned int width, height, x, y; /* anchura de ventana y posición */ unsigned int borderwidth = 4; /* 4 pixels */ unsigned int display_width, display_height; unsigned long foreground_pixel, background_pixel; /* valores de pixel (color) */ XEvent report; GC gc; XGCValues values; if ( (display=XOpenDisplay(NULL)) == NULL ){ (void) fprintf(stderr,"circulo: no puedo conectar al servidor X %s\\n"); exit( -1 ); } screen_num = DefaultScreen(display); display_width = DisplayWidth(display, screen_num); display_height = DisplayHeight(display, screen_num); x = display_width/3, y = display_height/3; width = display_width/3, height = display_height/4; background_pixel=WhitePixel(display,screen_num); foreground_pixel=BlackPixel(display,screen_num); win = XCreateSimpleWindow(display, RootWindow(display,screen_num), x, y, width, height, borderwidth, BlackPixel(display,screen_num), background_pixel); XSelectInput(display, win, ExposureMask | ButtonPressMask| ButtonMotionMask); gc = XCreateGC(display, win, 0 , &values); XMapWindow(display, win); }
Ya sólo nos queda construir el bucle de eventos del programa. Es un bucle infinito en el que en cada pasada se lee un evento, se ve de qué tipo es y se realiza una u otra acción dependiendo del tipo. El esqueleto de este bucle a falta de incluir el código para cada evento quedaría de la siguiente forma:
[fontfamily=courier,fontsize=\relsize{-3},frame=single] while (1) { XNextEvent(display, &report); switch (report.type) { case Expose: break; case ButtonPress: break; case MotionNotify: break; } }
El código para el evento Expose
debe dibujar el círculo en su posición actual con su radio correspondiente:
[fontfamily=courier,fontsize=\relsize{-3},frame=single] case Expose: XSetFunction(display, gc, GXcopy); XSetForeground(display,gc,foreground_pixel); XDrawArc(display,win,gc,currentX-currentR,currentY-currentR, currentR*2,currentR*2,0,360*64); break;
En el evento ButtonPress
debemos ver si hemos pinchado en el botón izquierdo (Button1
) o el botón derecho (Button3
). Si se ha pinchado con el botón izquierdo pasaremos a modo CHANGECENTER
, borramos el círculo de su posición anterior, cambiamos la posición del centro a la posición del cursor, y dibujamos el círculo en la nueva posición. Si se ha pinchado con el botón derecho pasaremos a modo CHANGERADIO
, borramos el círculo de su posición anterior, asignamos al radio del círculo la distancia entre el centro del círculo y la posición del cursor del ratón, y dibujamos el círculo con el nuevo radio:
[fontfamily=courier,fontsize=\relsize{-3},frame=single] case ButtonPress: if(report.xbutton.button==Button1){ mode=CHANGECENTER; XSetFunction(display, gc, GXxor); XSetForeground(display,gc,foreground_pixel^background_pixel); XDrawArc(display, win, gc, currentX-currentR, currentY-currentR,currentR*2,currentR*2,0,360*64); currentX=report.xbutton.x; currentY=report.xbutton.y; XDrawArc(display, win, gc, currentX-currentR, currentY-currentR,currentR*2,currentR*2,0,360*64); } else if(report.xbutton.button==Button3){ mode=CHANGERADIO; XSetFunction(display, gc, GXxor); XSetForeground(display,gc,foreground_pixel^background_pixel); XDrawArc(display, win, gc, currentX-currentR, currentY-currentR,currentR*2,currentR*2,0,360*64); currentR=distancia(report.xbutton.x,report.xbutton.y, currentX,currentY); XDrawArc(display, win, gc, currentX-currentR, currentY-currentR,currentR*2,currentR*2,0,360*64); } else mode=NOCHANGE; break;
Se deja como ejercicio incluir el código para el evento MotionNotify
.