Como se comentó el mes pasado, un programa X Window debe comenzar por inicializar el sistema de ventanas (o más correctamente, realizar la apertura del display y pantalla y crear la ventana principal de la aplicación) para posteriormente introducir el código del programa propiamente dicho. Veamos dichas funciones de inicialización una a una, con una descripción de cada una de ellas y de los parámetros que estas aceptan (para obtener esta información OnLine, aunque en inglés, pruebe a ejecutar "man <nombre_de_función>" en la línea de comandos, respetando mayúsculas y minúsculas). Las páginas del manual suelen ser muy útiles (aunque es difícil encontrar lo que se busca) cuando se programa en X Window. Por otra parte, cuando se conoce el nombre de una función, lás páginas del manual se convierten en estupendas referencias donde consultar parámetros y nombres de estructuras. A título de ejemplo, en puede observarse la salida del comando "man XOpenDisplay" (función que comentaremos a continuación) ejecutando dicho comando en cualquier consola de Linux. En el presente texto trataremos de ofrecer un manual de referencia en castellano a todas las funciones de inicialización necesarias para nuestros programas, y por supuesto, en castellano.
La función XOpenDisplay devuelve una estructura tipo display que sirve como conexión con el servidor X y que contiene toda la información sobre dicho servidor. Esta función conecta a la aplicación al servidor X a través de protocolos de comunicaciones TCP o DECnet.
Si la llamada tiene éxito, XOpenDisplay devuelve un puntero a una estructura de tipo Display definida en /usr/X11R6/include/X11/Xlib.h, o NULL en caso de no tener éxito. Tras obtener la estructura de tipo Display ya se puede acceder a cualquiera de las pantallas (screens) de dicho display. Como veremos a continuación, es posible obtener información del display (anchura, altura, etc.) a partir de esta estructura, pero usando macros específicas para ello y sin tener que utilizar personalmente los campos de esta estructura.
Ejemplo: disp = XOpenDisplay( NULL );
Esta función cierra la comunicación con el servidor X para el display especificado y destruye todas las ventanas , identificadores de recursos (ventanas, fuentes, cursores, etc.) que se crearon en este display (a menos que se cambie el modo de cierre mediante la función XSetCloseDownMode()). Esta función debe ser llamada antes de salir del programa para que se reciban posibles errores en cola ya que la llamada a XCloseDisplay genera una operación XSync.
Ejemplo: XCloseDisplay( disp );
Esta función vacía el buffer de salida y espera a que todas las peticiones sean atendidas por el servidor X, de manera que cualquier error generado sea atendido por el manejador de errores del cliente. El parámetro discard especifica si se deben de descartar todos los eventos en la cola (False=no descartar, True=descartar). Es llamada automáticamente por XCloseDisplay al cierre de nuestro programa.
Esta función se utiliza para vaciar el buffer de peticiones de nuestra aplicación de tal modo que sean enviadas al servidor. Como veremos en la sección dedicada al bucle de eventos, en ocasiones no será necesario el uso de esta función pues será llamada automáticamente por funciones utilizadas en dicho bucle, aunque en otros casos nos puede ser necesario forzar el envío de los mensajes al servidor.
La función XSetCloseDownMode() especifica qué debe ocurrir a los recursos del cliente cuando se cierre la conexión con el servidor X. El parámetro close_mode indica el modo de actuación del servidor, entre DestroyAll (modo por defecto, y que especifica que todos los recursos del cliente desaparecerán), y RetainPermanent o RetainTemporary, que los mantendrán longeva o temporalmente (ver las man-pages para más información). Esta función permite especificar qué ocurriría con un icono o sonido de nuestro programa, por ejemplo, si éste fuera cerrado por parte del usuario. Como ejemplo podríamos usar el programa visualizador de gráfocps XV, que al ser cerrado permite dejar la ventana con el gráfico visualizado en nuestro desktop.
Esta función cerrará aquel cliente dueño del recurso especificado mediante XID resource, lo cual puede permitir al Window Manager la muerte de una aplicación cuyo recurso cause un error grave, o para aquellas aplicaciones que usan XSetCloseDownMode con RetainTemporary y mueren por un error, mientras que sus recursos no lo hacen. Esta función asegura que el Window Manager podrá cerrar aplicaciones cuyos recursos dañen la estabilidad del sistema.
Esta macro (la primera) o función (la segunda) devuelve el número asociado a la pantalla por defecto en referencia a un display abierto mediante XOpenDisplay. Ejemplo:
Display *display; int pantalla; /* primero es necesario abrir el display */ display = XOpenDisplay( NULL ); pantalla = DefaultScreen( display );Este número de pantalla se utilizará en llamadas a otras funciones, por lo que es aconsejable que sea almacenado en alguna variable global del programa. En general en la programación estructurada suele ser poco recomendable la utilización de variables globales, pero en el caso de la variable display, esta va a ser utilizada por el 95% (tal vez más) de las funciones Xlib de nuestro programa, de modo que declarándola como global (dotándole de un ámbito igual a todo el programa) se obtiene un ahorro en tiempo de ejecución, en escritura (habría que pasar dicha variable a todas las funciones de nuestro programa) y en longitud del código.
Devuelve el número de pantallas disponibles en el display.
Estas macros (las 2 primeras) y funciones (las 2 últimas) nos permiten conocer el tamaño de la pantalla especificada en los 2 parámetros display y pantalla.
int anchura, altura; anchura = DisplayWidth( display, pantalla ); altura = DisplayHeight( display, pantalla ); printf("Dimensiones: %dx%d .", anchura, altura );
Esta macro/función proporciona información sobre el fabricante o vendedor del servidor que se está ejecutando. Devuelve una cadena de texto que identifica al fabricante (vendor) de X Window. En un sistema Linux esta cadena será probablemente una alusión a XFree86, el servidor free de X Window.
Como la disponibilidad de colores puede variar entre los distintos displays y pantallas, estas 2 macros nos proporcionan los valores de pixel correspondientes a los colores blanco (WhitePixel) y negro (BlackPixel) para utilizarlos en la creación de la ventana, en rutinas gráficas, etc. Ejemplo:
unsigned long blanco, negro; blanco = WhitePixel( display, pantalla ); negro = BlackPixel( display, pantalla );La utilidad de esta función radica en que cada display, pantalla o modo de vídeo puede disponer de un valor numérico para estos colores, de manera que podemos pedir al propio sistema de que nos informe de estos valores para el display que ejecuta nuestro programa.
Esta función devuelve el nº de planos o bits por pixel del display actual (8bpp=256 colores, 15bpp=32768 colores, 16bpp=65536 colores, 24/32bpp=16.4 millones de colores), de tal modo que podamos adaptar nuestras rutinas de dibujo (si utilizamos DGA, por ejemplo) a la profundidad de color de la pantalla. Sabiendo este parámetros nuestras rutinas de trazado de gráficos pueden ser optimizadas y nuestros gráficos pueden ser convertidos desde el disco al formato de pantalla actual. Recordemos que el número de bits por pixel indica la forma en que se representa un pixel individual en pantalla, de forma que un programa sea capaz de componer los gráficos con el objetivo de que la pantalla los "comprenda" directamente.
Estas 2 funciones sirven para conocer el identificador de la ventana raíz en la pantalla por defecto o en la pantalla especificada, respectivamente. Su uso es necesario, por ejemplo, para la creación de nuestra ventana principal con el fin de que sea hija de la ventana raíz (como se vio en el ejemplo del mes pasado).
ConnectionNumber(display); DefaultColormap(display, screen_number); int *XListDepths(display, screen_number, count_return); DefaultGC(display, screen_number); DefaultScreenOfDisplay(display); DefaultVisual(display, screen_number); DisplayCells(display, screen_number); DisplayPlanes(display, screen_number); DisplayString(display); ProtocolVersion(display); ProtocolRevision(display); VendorRelease(display); XSetBackground(display, gc, color); XSetForeground(display, gc, color); XClearWindow(display, window);Aparte de estas funciones tenemos la sencilla función XSetStandardProperties que nos permite especificar entre otras cosas el título de la ventana de nuestro programa y el título que esta presentará al estar minimizada:
XSetStandardProperties( display, ventana, "Titulo", "TituloMinimizado", None, NULL, 0, NULL );
A esta función se le pasa el display, ventana y títulos para dicha ventana, así como una serie de parámetros adicionales que por ahora dejaremos en (None, NULL, 0 y NULL).
/* INFO.C : Información sobre el display */ /* headers necesarios para la compilación */ #include <X11/Xlib.h> #include <X11/Xutil.h> #include <stdio.h> main() { /* variables utilizadas en el programas */ /* Necesitaremos abrir el display */ Display *display; char *vendedor; int pantalla, pantallas, anchura, altura, depth; unsigned long blanco, negro; /* abrimos el display por defecto */ display = XOpenDisplay( NULL ); /* tomamos datos acerca de dicho display */ /* para ello utilizamos las macros comentadas */ /* en el artículo */ pantallas = ScreenCount( display ); pantalla = DefaultScreen( display ); blanco = WhitePixel( display, pantalla ); negro = BlackPixel( display, pantalla ); anchura = DisplayWidth( display, pantalla ); altura = DisplayHeight( display, pantalla ); depth = DefaultDepth( display, pantalla ); vendedor = ServerVendor( display ); /* mostramos los datos en la xterm */ printf("\nInformación sobre el display:\n\n"); printf("vendedor = %s\n", vendedor ); printf("pantallas = %d\n", pantallas ); printf("pantalla = %d\n", pantalla ); printf("blanco = %u\n", blanco ); printf("negro = %u\n", negro ); printf("depth = %u\n", depth ); printf("anchura = %d\n", anchura ); printf("altura = %d\n\n", altura ); }La salida del proceso de compilación y ejecución del anterior programa (disponible en el CD que acompaña a la revista como info.c) puede observarse en la siguiente figura:
FIGURA1: Salida del programa INFO.C: ==================================== [sromero@compiler codigo]$ ./info.exe Información sobre el display: vendedor = The XFree86 Project, Inc pantallas = 1 pantalla = 0 blanco = 65535 negro = 0 depth = 16 anchura = 1024 altura = 768
Ventana_Raíz: Aplicación_1 \ Hija_Aplic1_1 \ Hija_Aplic1_2 Aplicación_2 (sin hijas)Es posible observar esta jerarquía mediante el comando "xwininfo -tree" (y pinchando sobre la ventana deseada), o mediante "xwininfo -tree -root" para poder examinar todas las ventanas que cuelgan de la ventana raíz (cuya salida puede verse en la figura 2). Mediante el comando xwininfo sin parámetros simplemente se nos proporcionará información acerca de la ventana sobre la que pulsemos el botón del mouse.
FIGURA 2: SALIDA DE XWININFO -ROOT: ================================== [sromero@compiler art2]$ xwininfo -root xwininfo: Window id: 0x25 (the root window) (has no name) Absolute upper-left X: 0 Absolute upper-left Y: 0 Relative upper-left X: 0 Relative upper-left Y: 0 Width: 1024 Height: 768 Depth: 16 Visual Class: TrueColor Border width: 0 Class: InputOutput Colormap: 0x21 (installed) Bit Gravity State: NorthWestGravity Window Gravity State: NorthWestGravity Backing Store State: NotUseful Save Under State: no Map State: IsViewable Override Redirect State: no Corners: +0+0 -0+0 -0-0 +0-0 -geometry 1024x768+0+0Las ventanas en un determinado nivel del árbol se llaman ventanas hermanas (como Hija1 e Hija2 en nuestro ejemplo), y existe un orden en que son trazadas (especificado por la forma en que son apiladas), lo cual quiere decir que existe una pila que especifica el orden de solapamiento de las mismas, de tal modo que una ventana será visible si está mapeada y está en la cima de la pila (y recibe el foco del usuario), o bien pese a no estár en la cima de la pila se da la circunstancia de que no está solapada por ninguna otra ventana.
Más características configurables de la ventana son el cursor del apuntador asociado a la misma, el borde (que puede ser un color sólido o un borde a base de patrones de bitmaps o pixmaps), y el fondo (que también puede ser sólido o relleno mediante un patrón).
Window XCreateSimpleWindow( Display *display, Window Ventana_Madre, int x, int y, unsigned int Anchura, unsigned int Altura, unsigned int Anchura_Borde, unsigned long Color_Borde, unsigned long Color_Fondo );De estos parámetros, las coordenadas (x,y) especifican el lugar de creación de la ventana, y Anchura y Altura las dimensiones de la misma. El tamaño y color del borde también pueden especificarse, así como el color del fondo, para los que podemos usar las macros anteriormente descritas WhitePixel y BlackPixel. La variable display se puede obtener de XOpenDisplay, mientras que la ventana madre se puede especificar gracias a las macros DefaultRootWindow o RootWindow para que nuestra ventana cuelgue de la ventana madre o raíz (si es la ventana principal de nuestra aplicación).
Un ejemplo de creación de ventana sería el siguiente, que crearía una ventana de 500x400 en la posición (100,100) de la pantalla:
/* Primero se abre el display por defecto */ display = XOpenDisplay(NULL); /* a continuación se crea la ventana en dicho display, tomando el desktop como raíz, en 100,100, de 500x400, y de color y fondo blanco y negro */ ventana = XCreateSimpleWindow(display, RootWindow( display, 0), 100, 100, 500, 400, 2, WhitePixel(display, pantalla), BlackPixel(display, pantalla) );
XMoveWindow( Display *display, Window ventana, int x, int y ):
Mueve la ventana que se le indica a la posición de pantalla especificada
por las coordenadas (x,y) que se le pasen como parámetros.
XResizeWindow( Display *display, Window ventana, unsigned int anchura,
unsigned int altura ):
Esta función modifica el tamaño (anchura y
altura) de la ventana a los valores deseados y que se le pasan como
parámetros.
XSetWindowBorderWidth( Display *display, Window ventana, unsigned int
anchura ):
Permite especificar una nueva anchura en pixels del
borde.
XMoveResizeWindow( Display *display, Window ventana, int x, int y, int
anchura, int altura ):
esta función es la fusión de la 2 primeras y
permite modificar las dimensiones y posición simultáneamente en una sóla
llamada a XLib.
A continuación se incluyen una serie de funciones para el mapeado y desmapeado de ventanas, ya que una ventana no se mapea automáticamente al crearla y será necesario mapearla manualmente como se ha hecho en los ejemplos. Como se comentó anteriormente, mapear una ventana significa hacerla visible, y para que esté mapeada es imprescindible que ninguna ventana la solape.
XMapWindow( Display *display, Window ventana ):
Mapea la ventana especificada.
XMapSubWindows( Display *display, Window ventana ):
Mapea las ventanas hijas de la ventana que se le indica (pero no dicha
ventana).
XUnmapWindow( Display *display, Window ventana ):
Desmapea la ventana especificada.
XUnmapSubWindows( Display *display, Window ventana ):
Similar a la anterior pero con las ventanas hijas.
XMapRaised( Display *display, Window ventana ):
Mapea la ventana colocándola además en la cima de ventanas. Tras la ejecución
de esta llamada Xlib la ventana sobre la que es aplicado pasa a estar en
primer plano y recibiendo el foco de X Window.
Por otra parte, Xlib nos provee de las siguientes 2 funciones para modificar la posición de nuestra ventana (si deseamos colocarla en el fondo o cima de la pila):
XRaiseWindow( Display *display, Window ventana ):
Esta función eleva la ventana que se le especifica a la cima de la pila de
ventanas de forma que queda por encima de todas ellas, de forma similar a lo
que hace XMapRaised(), pero con ventanas ya mapeadas.
XLowerWindow( Display *display, Window ventana ):
Mediante esta llamada Xlib se coloca a la ventana especificada en el fondo
de la pila (y por tanto bajo todas las demás ventanas de la jerarquía).
Por último con respecto a modificación de geometría y apilamiento, la función XConfigureWindow permite modificar la posición, anchura, altura, borde y orden en la pila de una ventana mediante sola llamada, utilizando una estructura con los datos a pasarle y una variable donde cada bit indica si queremos modificar ese dato o no. Pero veamos las definiciones y un ejemplo de uso para aclarar su utilización:
XConfigureWindow( Display *display, Window ventana, unsigned int mascara, XWindowChanges *Valores );Símbolos de la variable máscara:
/* Diferentes bits On/Off */ #define CWX (1<<0) #define CWY (1<<1) #define CWWidth (1<<2) #define CWHeight (1<<3) #define CWBorderWidth (1<<4) #define CWSibling (1<<5) #define CWStackMode (1<<6)La estructura XWindowChanges es la siguiente:
/* parámetro 4 de la función */ typedef struct { int x, y; int width, height; int border, width; Window sibling; int stack_mode; } XWindowChanges;Un ejemplo de uso donde se modificaría el valor de las coordenadas X e Y y de la anchura de la ventana sería el siguiente:
/* declaramos la estructura */ XWindowChanges Valores; /* valores que deseamos modificar */ Valores.x = 100; Valores.y = 100; Valores.width = 200; /* llamada a la función indicando mediante OR (|) */ /* qué parámetros hay que modificar. */ XConfigureWindow( display, ventana, CWX | CWY | CWWidth, Valores );En el anterior ejemplo definimos los diferentes campos a modificar en la estructura XWindowChanges, y llamamos a la función especificando mediante la operación OR los parámetros a modificar tomándolos desde dicha estructura (para más información sobre sibling y stack_mode, los responsables de la modificación de la posición en la pila de la ventana, consultar el manual).
XSetWindowBackground(Display *display, Window ventana, long
background_pixel):
Permite modificar el color de fondo de la ventana
especificada.
XSetWindowBackgroundPixmap(Display *display, Window ventana, Pixmap
background_pixmap):
Permite mapear un bitmap (Pixmap) como fondo de la ventana
especificada, utilizándolo como un patrón a repetir.
XSetWindowBorder(Display *display, Window ventana, long border_pixel):
Permite modificar el color del borde de la ventana especificada.
XSetWindowBorderPixmap(Display *display, Window ventana, Pixmap
border_pixmap):
Permite mapear un bitmap (Pixmap) en el borde de la ventana
especificada.
XSetWindowColormap(Display *display, Window ventana, Colormap colormap):
Aunque por ahora no vamos a utilizar esta función, esta llamada permite
modificar el colormap (mapa de colores o paleta) asociado a una ventana. Esto
será necesario en modos de 8 bits por pixel o de 16 colores, ya que al
disponer de un número limitado de colores, cada aplicación podrá hacer uso de
su propia paleta de colores, aunque sólo puede estar activa una cada vez, de
modo que la aplicación que reciba el foco del usuario deberá especificar la
paleta que desea usar mientras mantenga el foco.
Pulse aquí para bajarse los ejemplos y listados del artículo (3 Kb).
Santiago Romero