INTRODUCCIÓN A LA PROGRAMACIÓN EN X WINDOW

Artículo 1: Introducción al sistema X Window

Autor: (c) Santiago Romero.
Revista: Programación Actual (Prensa Técnica) nº 19, Octubre-1998


En este primer capítulo del curso introduciremos los conceptos básicos y fundamentales relativos al sistema de ventanas X Window con el fin de preparar el camino del lector hacia lo que es la programación de X Window mediante Xlib, la API básica de X Window.

El sistema X Window (figura 1) es un potente y complejo sistema de ventanas para sistemas distribuidos independiente de la arquitectura de la máquina que lo ejecuta, del Sistema Operativo utilizado (aunque es muy común en sistemas Unix, así como Linux) y del tipo de hardware empleado en estas máquinas (dispositivo o adaptador gráfico, tipo o protocolo de red, etc.), pensado para facilitar el desarollo de programas con interfaz gráfico, algo muy valorado hoy en día, pero también muy necesario (no sólo por cuestiones estilísticas) para dotar a las aplicaciones de un aspecto más uniforme, y como característica esencial a algunos tipos de programas (procesadores de textos como StarOffice, Lyx/KLyx, WordPerfect, programas de diseño o de C.A.D., y, en general, todo tipo de programas cuyo manejo resulta facilitado bajo sistemas de ventanas.).

XWindow

El disponer de tal interfaz gráfico y funciones de apoyo del mismo permite desarrollar aplicaciones de una manera más sencilla, centrándose en la aplicación en sí misma y olvidándose del proceso de creación del interfaz o del GUI del programa, y permitiendo a la aplicación un acceso avanzado a recursos de la máquina (en XWindow, las extensiones DGA, DBE, Mit Shared Extensions, etc.) implementados por parte del servidor.

Este curso es una introducción básica a lo que es la programación en el entorno de ventanas X Window (X11R6 actualmente), basado en sus librerías internas de más bajo nivel (XLib), a su vez basadas en la filosofía de eventos (o mensajes) en la que posteriormente se basó Windows 3.1 y 95/98/NT. Mediante la presente serie de artículos se tratará de introducir al lector en la programación en X Window mediante sus conceptos básicos y funciones principales, con el fin de que éste sea capaz de desarrollar programas bajo esta plataforma y se le pierda el miedo a Linux (uno de los mayores exponentes de X11 en estos últimos tiempos, en su revisión R6), potente donde los haya pero desasistido por parte de muchos programadores atraídos por la programación en Windows95/98 ante la falta de documentación para desarrollar programas en un Sistema Operativo mucho más estable: Linux.


X11R6: UN POCO DE HISTORIA

X Window es un sistema de ventanas desarrollado en el MIT (Masachussets Institute of Technology) y DEC (a mediados de los años 80) que se ha convertido en el sistema de ventanas estándar (en cualquiera de sus variantes) de los sistemas UNIX, y, en particular, de Linux. El sistema ha tenido numerosas versiones aunque hasta que en la versión 11R2 (versión 11, release 2) el sistema pasó del MIT al X Consortium (1993/94) , debido a que se pensó que era la mejor manera de proporcionar el liderazgo técnico y administrativo para que el desarrollo del sistema de ventanas X Window. A su vez, los derechos del sistema de ventanas pasaron a la fundación Open Software Foundation. Gracias a esto actualmente disponemos de una version free (libre) de este sistema de ventanas, la XFree86 11R6 (versión 11, release 6.3), esta última desarrollada para sistemas Intel con procesadores 80386/80486/Pentium, desarrollo en principio encabezado por David Wexelblat.

X Window corre bajo los siguientes sistemas operativos:

- SVR3.2: SCO 3.2.2, 3.2.4, ISC 3.x, 4.x
- SVR4.0: ESIX, Microport, Dell, UHC, Consensys, MST, ISC, AT&T, NCR, PANIX
- SVR4.2: Consensys, Univel (UnixWare)
- Solaris (x86) 2.1, 2.4, 2.5, 2.5.1, 2.6
- FreeBSD 2.0.5, a 3.0-current
- NetBSD 1.0, 1.1, 1.2, 1.2.1, 1.3 (i386 port only)
- OpenBSD 2.0, 2.1
- BSD/386 version 1.1 and BSD/OS 2.0
- Mach (from CMU)
- Linux
- Amoeba version 5.1
- Minix-386vm version 1.6.25.1
- LynxOS AT versions 2.2.1, 2.3.0 and 2.4.0, LynxOS microSPARC 2.4.0


ARQUITECTURA DE X WINDOW

X Window es un sistema de ventanas orientado a cliente/servidor, donde el servidor reside en cualquiera de los ordenadores de la red, y el cliente puede ser el mismo host que el servidor o cualquier otro host (u hosts) de la red. El cliente se dedica a hacer peticiones al servidor que es quien maneja el display o los dispositivos de entrada. Estos programas clientes son:

-Aplicaciones: son los programas en sí mismos: procesadores de textos (como la Suite StarOffice para Linux, o las aplicaciones de la figura 1), hojas de cálculo, juegos, etc. Como veremos a continuación, existen múltiples maneras de generar las aplicaciones (Gtk, Xlib, Imlib, etc.), aunque nosotros usaremos en principio llamadas a Xlib (similar a lo que es el API de Windows).

-Window Managers: Es el gestor de ventanas, un programa cliente con algunos privilegios extra que se desarrolla para que gestione el interfaz o GUI comunicándose con el servidor X y que es el encargado de la forma en que se presentan las ventanas en pantalla, de dar facilidades con menúes de configuración del sistema, barras de tareas o menúes de aplicaciones. Ejemplos de gestores de ventanas son el twm, fvwm, AfterStep, KDE o Gnome (siendo estos 2 últimos mucho más avanzados que los restantes). Esta disposición del sistema de ventanas nos permite cambiar el look al sistema cambiando el gestor de ventanas entre cualquiera de los disponibles (lo cual nos deja un sistema de ventanas realmente configurable a gusto del usuario).

En cambio, el servidor X (xserver) es el programa (residente en /usr/bin/X11/X, como link al servidor de nuestra tarjeta gráfica) que proporciona las primitivas (pixels, paletas, etc.) a las aplicaciones, con las que se comunica por medio de sockets. Los programas son ejecutados en la máquina servidora y se visualizan en la máquina cliente, lo cual nos permite abrir aplicaciones X Window bajo, por ejemplo, un Windows95, si disponemos de algún programa diseñado para esto, y haciendo un telnet a nuestra máquina Linux. El servidor controla el display ejecutando las peticiones de los clientes (de cualquiera parte de la red, ya sean peticiones remotas o locales), generando los eventos y notificándoselos a las aplicaciones. Hay que dejar claro que el servidor no hace nada (para comprobar esto, ejecutar el programa xinit), simplemente se dedica a esperar a que las aplicaciones le demanden servicios que el servidor cumple, ofreciendo a la máquina un display.

Esta comunicación entre cliente y servidor se realiza mediante 4 mensajes (request, reply, event y error) con un formato determinado, protocolo denominado Protocolo X:

-request: Mensaje de petición de servicio enviado del cliente al servidor, tales como peticiones de creación, redimensionado o movimiento de una ventana, de dibujo de primitivas gráficas como pixels o líneas, etc. Estos mensajes son procesados en el mismo orden con que son enviados a un buffer que proporcionan las Xlib y que se vaciará con el consecuente envío al servidor cuando sea necesario (cuando esté lleno, cuando se requiera una respuesta por parte del servidor, cuando sea desactivado (XSync) o cuando sea vaciado (XFlush)).

-reply: posible mensaje de respuesta esperado (si es necesario) por el cliente proveniente del servidor, como la respuesta a una peticion de tamaño de pantalla, ancho de la fuente actual de texto, o posición de cualquier ventana.

-event: es un mensaje enviado del servidor al cliente cuando ocurre un evento en el host que constituye el servidor (pulsación de teclado o botón del mouse, movimiento de una ventana, etc.). Cada uno de estos eventos son enviados a los clientes que especificaron qué tipo de eventos querían recibir, de manera que es posible que nuestra aplicación cliente no reciba ningún mensaje de teclado si, por ejemplo, todo su manejo se realiza con el ratón y así lo hemos especificado, con el consiguiente ahorro de procesamiento por nuestra parte.

-error: es enviado por el servidor al cliente en caso de error.

X Window funciona mediante el intercambio de mensajes de este tipo entre el servidor (el gestor del display, como veremos) y el cliente, por medio del protocolo X. Este protocolo ha sido ampliado con extensiones para determinadas tareas que no contemplaba la organización por este sistema de comunicaciones cliente/servidor. Si queremos conocer las diferentes extensiones al protocolo X instaladas en nuestra versión de X Window basta con ejecutar el programa /usr/X11R6/bin/xdpyinfo, el cual nos indicará el número y tipo de extensiones instaladas (como puede verse en la siguiente tabla:


name of display:    :0.0
version number:    11.0
vendor string:    The XFree86 Project, Inc
vendor release number:    3330
maximum request size:  4194300 bytes
motion buffer size:  256
bitmap unit, bit order, padding:    32, LSBFirst, 32
image byte order:    LSBFirst
number of supported pixmap formats:    2
supported pixmap formats:
    depth 1, bits_per_pixel 1, scanline_pad 32
    depth 16, bits_per_pixel 16, scanline_pad 32
keycode range:    minimum 8, maximum 134
focus:  window 0x1800006, revert to None
number of extensions:    19
    BIG-REQUESTS
    DOUBLE-BUFFER
    DPMS
    LBX
    MIT-SCREEN-SAVER
    MIT-SHM
    MIT-SUNDRY-NONSTANDARD
    RECORD
    SECURITY
    SHAPE
    SYNC
    XC-APPGROUP
    XC-MISC
    XFree86-DGA
    XFree86-Misc
    XFree86-VidModeExtension
    XInputExtension
    XKEYBOARD
    XTEST
default screen number:    0
number of screens:    1

screen #0:
  dimensions:    1024x768 pixels (347x260 millimeters)
  resolution:    75x75 dots per inch
  depths (1):    16
  root window id:    0x25
  depth of root window:    16 planes
  number of colormaps:    minimum 1, maximum 1
  default colormap:    0x21
  default number of colormap cells:    64
  preallocated pixels:    black 0, white 65535
  options:    backing-store YES, save-unders YES
  largest cursor:    64x64
  current input event mask:    0xfa603f
    KeyPressMask             KeyReleaseMask           ButtonPressMask
    ButtonReleaseMask        EnterWindowMask          LeaveWindowMask
    ButtonMotionMask         KeymapStateMask          StructureNotifyMask
    SubstructureNotifyMask   SubstructureRedirectMask FocusChangeMask
    PropertyChangeMask       ColormapChangeMask
  number of visuals:    1
  default visual id:  0x20
  visual:
    visual id:    0x20
    class:    TrueColor
    depth:    16 planes
    available colormap entries:    64 per subfield
    red, green, blue masks:    0xf800, 0x7e0, 0x1f
    significant bits in color specification:    6 bits
Entre estas extensiones podemos destacar las siguientes:

-XF86-DGA: Xfree86 Direct Graphic Access constituye un método de acceso directo de las aplicaciones a la vídeomemoria, proporcionando acceso al mismo buffer de vídeo (y no funciones de acceso a él). Es algo similar a lo que hace DirectDraw con Lock() o, en el caso del MSDOS, el acceso al Linear Frame Buffer en VESA 2.0. Mediante esta extensión es necesario escribir uno mismo el código gráfico de trazado de imágenes, etc., pero proporciona a cambio una gran velocidad en los gráficos (aunque algunas tarjetas antiguas no soportan Linear Frame Buffer (acceso lineal a la videomemoria) y necesitan trabajar con bancos de memoria (método de trabajo algo más lento)).

-Mit Shared Memory Extension (XSHM Ext) es un método para mapear gráficos en buffers de memoria para que sean copiados sobre la videomemoria. Es otra rápida técnica para gráficos que utilizan muchos programas que necesitan de alta velocidad, como Xmame (donde Juan Antonio Martínez ha hecho un estupendo trabajo y de cuyo código se puede aprender mucho sobre las X).

-Xkeyboard permite el control por parte del servidor de las características de los diferentes teclados.

-X Input Extensions es un interface que permite a X el control de pantallas táctiles, tabletas digitalizadoras, lectores de códigos de barras, joysticks, joypads y cualquier otro tipo de dispositivo de entrada de datos (entrada no pedida por el cliente sino generadora de eventos, también llamadas entradas asíncronas).

-DBE: Double Buffer Extension implementa bajo X la posibilidad de usar double buffering para animaciones fluidas.

Por último, las librerías unen el cliente y el servidor proporcionando funciones comprensibles por ambos (como XOpenDisplay o XDrawString). Éstas suelen ser dinámicas, lo cual significa que son linkadas con el cliente cuando es ejecutado), y de las cuales necesitaremos la versión apropiada para el correcto funcionamiento del programa (cada Release de X posee unas funciones determinadas que aumentan conforme aparecen nuevas versiones, aunque en principio con compatibilidad descendente).

Para nuestro curso de introducción a la programación en X Window usaremos XFree86 bajo el Sistema Operativo Linux y con el compilador de C/C++ GNU GCC. En nuestra calidad de programadores disponemos de diferentes librerías de abstracción a dichos mensajes del protocolo X, de manera que sean tales APIs las que se encarguen de informar al cliente/servidor de nuestras órdenes. Para ello disponemos de las Xlib, quienes controlan directamente y un nivel bastante bajo los protocolos X. También es posible recurrir a interfaces de más alto nivel que a su vez hagan uso de las Xlib, con el X Toolkit o las Gtk, los cuales definen una nueva estructura de control de elementos en las X llamados widgets (iconos, botones, barras de scroll, menúes, etc.), todo ello a un nivel muy superior al de Xlib. Estos widgets son componentes de diálogo, de tal modo que las librerías de este nivel suelen estar estructuradas como gestores de los mismos, orientadas a objectos, por eventos, y con un conjuntos de widgets predefinidos (como los Athena widgets o las librerías Motif y Open-Look).


CONCEPTOS BÁSICOS

A continuación veremos unos conceptos básicos previos a la programación en X, y que necesitaremos conocer para seguir con soltura la terminología que se siga en el curso. Lo primero que se debe señalar es que X Window es un sistema orientado a displays. Es decir, hemos de tener claro en qué display se está trabajando, ya que recordemos que X Window es un entorno de red, donde el display se puede encontrar en cualquier máquina. El display puede definirse como una o más pantallas y sus dispositivos de entrada (teclado y dispositivo apuntador, como el raton). En principio sólo se suele disponer de un display (el display 0, ya que estos van numerados) y una pantalla (la 0).

Los displays son sistemas de visualización de mapas de bits (también llamados bitmaps). Un mapa de bits es una imagen formada por pixels (puntos con un valor numérico asociado que representan un color en nuestro display), y el display es un dispositivo manejador de estos bloques de gráficos (formados por la unidades elementales de los gráficos 2d, los pixels).

Cada posición en pantalla puede determinarse a partir de 2 valores numéricos que representan las coordenadas del pixel (por ejemplo, (0,0) es la esquina superior izquierda de la pantalla y (1023,767) sería la esquina inferior derecha en un display de una anchura y altura de 1024x768 pixels de resolución. Estos pixels son almacenados en una porción de memoria denominada memoria de vídeo, donde, explicado de una manera simplista (aunque en las tarjetas de vídeo que existen desde hace algún tiempo es así) cada pixel es representando por medio de un valor numérico que representa el color del mismo, seguidos todos ellos de una manera lineal (aunque también en las tarjetas más antiguas se usaban planos de bits diferentes).

La relación entre un valor de pixel y el color asignado a este color (por ejemplo valor 0=color negro, 1=azul, etc.), es conocida como mapa de colores (colormap), y puede ser de 2 tipos: de tipo tabla, como en los modos de 16 y 256 colores, donde se dispone de una tabla de tripletes RGB (paleta) que especifica a qué color corresponde cada valor numérico, o de relación directa, como ocurre en los modos sin paleta donde cada color se construye a partir de las componentes RGB del color deseado y colocándo estas componentes en el lugar correcto dentro de la videomemoria, de forma que representen el color buscado.

Una ventana es un recuadro o área generalmente cuadrada en la pantalla donde generalmente cada programa produce su salida (de texto, gráfica, etc.). Gracias al Window Manager las ventanas pueden ser movidas, se pueden cerrar, modificar su tamaño, o, como hace el gestor AfterStep, se pueden tomar capturas de la misma. La ventana activa en cada momento (sobre la que recae el foco del usuario) se diferencia de las demás porque la barra de título de la misma tiene diferente color o forma que las que no estan activas (el concepto de ventanas esta muy extendido actualmente debido a la "copia" que del Mac hizo Microsoft Windows en su tiempo, en contra de lo que cree mucha gente nombrando a Windows como el S.O. origen de los entornos gráficos).

Los eventos son mensajes (de tipo event) enviados desde el servidor al cliente (ya que sólo pueden ser detectados por el servidor) para avisar de cambios en el sistema o de acciones del usuario o programa cliente, como pulsaciones o liberaciones de teclas, o pulsación o liberación del mouse. Este tipo de eventos son generados por hardware, pero tambien es posible generar eventos por medio del software, como cuando el usuario modifica el tamaño de una ventana o la mueve (es decir, no se especifica que ha ocurrido algo con el hardware sino que se indica que se ha redimensionado o movido la ventana). Xlib guarda los eventos que va recibiendo desde el servidor en una cola de la que los recoge el programa en una sección de código llamada bucle de mensajes, como veremos en alguno de nuestros primeros ejemplos. Por otra parte, no todos los eventos son enviados al cliente, sino sólo aquellos que el cliente especificó que se le deben enviar. Por ejemplo, la línea de código siguiente (que se encuentra en el código de inicialización de la ventana principal de nuestra aplicación en uno de los ejemplos que incluye el curso) selecciona que se le pasen los eventos relacionados con la pulsación de botones del mouse, así como eventos que le indiquen cuándo debe redibujarse la ventana (el usuario cambia a ella, la maximiza, etc), pero ningún otro evento (como por ejemplo los de teclado):


XSelectInput(display, window, ExposureMask | ButtonPressMask );

Los recursos son objetos almacenados en forma de información de diverso tipo en los servidores (recursos del servidor), identificados por lo que se conoce como identificadores de recursos. Ejemplos de recursos son las fuentes de letra, los mapas de bits (gráficos, iconos o pixmaps), los colormaps o mapas de colores, o los cursores. La ventaja de que sea el servidor quien almacena los recursos es que el cliente no debe preocuparse del almacenamiento de los mismos, ni tampoco deben ser enviados por la red, ya que el programa se ejecuta en la máquina servidora, con lo que el tamaño de los clientes en memoria es menor y no necesitan estar obteniendo continuamente información de la red de transmisión. Cuando el cliente le pide al servidor que cree una ventana, éste le devuelve el cliente un entero identificador de esa ventana, valor numérico que utilizará el cliente después para referir cualquier operación (de dibujo, por ejemplo) sobre dicha ventana. Por otra parte, las propiedades de un recurso indican las características concretas de un recurso, de tal modo que al hablar de las propiedades de una ventana se puede hablar de su anchura, altura, posición o color de fondo.

Un pixmap es un rectángulo donde se dibujan gráficos de manera transparente a la pantalla (el usuario no ve el dibujo), es decir: es un recurso de almacenaje de mapas de bits (y como tal dispone de un identificador), de tal modo que luego puedan ser trazados sobre las ventanas (algo similar a un sprite o bitmap). Sobre el tema de los gráficos también disponemos de los contextos de gráficos (GC, Graphics Context), que son bloques en los que se almacena la información sobre la manera de representar un determinado gráfico. Los identificadores de GC nos permiten especificar la manera con que representar una determinada primitiva gráfica (línea, pixel, etc.).

Por otra parte, un cursor es el símbolo gráfico (como el cursor en forma de X en X Window) que muestra la posición actual del dispositivo apuntador, así como la información de su tamaño y forma, y una fuente es un recurso que almacena información de cada fuente de letras (con la que se puede escribir información textual en una pantalla gráfica), de tal modo que es posible modificar el tipo de letra de nuestra aplicación seleccionando para la misma un nuevo identificador de fuente.

En principio también serán necesarios conocimientos básicos de programación en C o C++ para seguir el curso y los ejemplos del mismo, aunque el nivel requerido no será muy elevado ya que se tratará de una introducción a las X y a su metodología de trabajo y programación.


LIBRERÍA XLIB

Como se ha comentado anteriormente, Xlib es una librería de bastante bajo nivel que requiere algo más de trabajo por parte del programador que las librerías de alto nivel, pero será explicada aquí por motivos muy sencillos: el conocimiento de las funciones de bajo nivel nos hace capaces de hacer lo mismo que hacen otras librerías de una manera más adaptada a nuestras necesidades, e incluso nada nos impide crear nuestra propia librería de widgets. Por otra parte, será posible utilizar código Xlib para los gráficos (obteniendo por tanto velocidad al permitirnos usar las extensiones XF86-DGA y Mit Shared Extensions) y widgets para el resto del código, contentando tanto a los programadores de aplicaciones como a los que pretendan hacer cosas con gráficos en X Window. Además, Xlib proporciona más control al programador, produce programas más pequeños, rápidos y eficientes, y nos permite desarrollar nuestro propio look en contra de la imposición de los looks generados por los diferentes toolkits.

Por otra parte, la similitud de los esquemas de los programas X Window con los de Windows (ya que Windows adoptó este esquema de eventos tras demostrarse su eficacia con las X) hará muy fácil el aprendizaje del esquema general de un programa en X Window como una simple adaptación de funciones y nombres de estructuras.


COMPILACIÓN DE PROGRAMAS CON XLIB

Como en cualquier desarrollo de programas compilados para cualquier plataforma, los programas para X Window deben de ser creados con cualquier procesador de texto llano y compilados con un compilador para obtener el programa ejecutable. Los programas de ejemplo que se incluyan en el curso será necesario compilarlos mediante un compilador de C para UNIX. El compilador que utilizaremos será gcc, incluido con Linux, y los ficheros de cabecera básicos que deberá incluir cualquier programa de XWindow son los siguientes (disponibles normalmente en /usr/include/X11, siendo recomendable especificarlo relativamente y utilizando la opción -I del compilador):


#include <X11/Xlib.h>
#include <X11/Xutil>
#include <X11/Xos.h>

La compilación requiere especificar el uso de las Xlib para el correcto reconocimiento de las diferentes funciones, cosa que se realiza mediante el switch -l:


gcc -o programa.exe programa.c -lX11 

El parámetro -l indica la librería a usar, (-l<nombre> equivale a la librería lib<nombre>.a en cualquier de los subdirectorios donde el programa busca las librerías). El compilador buscará dicha librería en /usr/X11R6/lib, aunque si no es así puede especificársele mediante la opción -L/usr/X11R6/lib (caso normal si no compilamos el programa como root). El parámetro -o <nombre> indica el nombre que debe tener el fichero ejecutable (donde no es necesaria la extensión .exe, por supuesto). Para que estos ficheros de cabecera (y por tanto la Xlib) estén disponibles en el sistema, así como el propio sistema de ventanas, es preciso tener instalados los siguientes paquetes (en cualquiera de sus versiones):


[sromero@localhost sromero]$ rpm -qa | grep XFree86
XFree86-libs-3.3.2-8
XFree86-3.3.2-8
XFree86-75dpi-fonts-3.3.2-8
XFree86-devel-3.3.2-8
XFree86-SVGA-3.3.2-8

El paquete XFree86-devel es el contenedor de las XLib, así como de un par de sets de widgets llamados Xt y Xaw.

Sobre la ejecución del programa, una vez compilado, y si el directorio de trabajo no reside en el path actual (o si el path no incluye el directorio actual o '.'), deberá ser ejecutado mediante la inclusión del directorio actual en la ruta completa (desconocido para más de un recién iniciado en Linux como puede comprobarse muchas veces en es.comp.os.linux) y siempre dentro del entorno X Window:


./programa.exe

En caso de ejecutar el programa fuera del entorno X Window obtendremos el mensaje "Error: Can't open display", cuyo significado pronto se comprenderá, y que es debido a que, como veremos en el siguiente ejemplo, lo primero que debemos hacer en nuestro programa X es abrir el display para posteriormente poder crear la ventana principal de nuestra aplicación.


PROGRAMA DE EJEMPLO

Vamos a empezar la programación en sí misma con un listado donde puede observarse un sencillo ejemplo que abre una ventana de 500x400 pixels y no realiza ninguna otra acción (como veremos próximamente, esto es debido a que no gestionamos los eventos en el ejemplo), y cuya única misión es esperar a que la ventana muera por parte del usuario (o con un kill del Window Manager) matando la ventana mediante CTRL+C en la xterm donde fue lanzada.


#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>

void main()
{
    Display *display;
    Window ventana;

    /* abrimos el display estándar */
    display = XOpenDisplay( NULL );

    /* creamos una nueva ventana */
    ventana = XCreateSimpleWindow( display, RootWindow( display, pantalla),
                                   10, 50, 500, 400, 2, 0, 1 );

    /* Definimos las propiedades la ventana */
    XSetStandardProperties(display, ventana, 
                "Hola Mundo :)","Hola!",
                None,NULL,0,NULL);
    
    /* Mapeamos la ventana para hacerla visible */
    XMapWindow( display, ventana );
    XFlush( display );
    getchar();
}


COMENTARIO DEL PROGRAMA

Tras compilar (gcc -o ejemplo1.exe ejemplo1.c -lX11 -L/usr/X11R6/lib), ejecutar (./ejemplo1.exe) y observar el comportamiento del programa (el cual nos proporciona en apenas 20 líneas una ventana cuyo tamaño puede ser modificado, que puede moverse, etc.), pasemos a describir el funcionamiento del programa.


#include <X11/Xlib.h>
#include <X11/Xutil.h>

El fichero de cabecera Xlib.h define las estructuras y muchas de las macros que serán usadas en llamadas a las funciones de Xlib, mientras que Xutil.h define similares estructuras relacionadas con los gestores de ventanas. El header stdio.h ha sido incluido tan sólo para el uso de la función getchar() (esperar a que se reciba una tecla de la entrada estándar).


void main()

Como cualquier programa en C, un programa para X dispone de una función principal main() (la primera que será ejecutada, y en contra de lo que hace Windows modificando este esquema y utilizando una función WinMain()).


Display *display;

Como ya se ha comentado, X Window es un sistema de red, de tal modo que puede ejecutarse en cualquier host (ordenador de la red, incluyendo el propio, caso habitual común si no disponemos de red). El display para X es una unidad que contiene pantalla y dispositivos de entrada como teclado o ratón, sin importar su localización en la red. Cada uno de estos ordenadores puede tener más de un display, identificado cada uno de ellos mediante un número de display, mientras que cada uno de estos displays puede tener más de una pantalla (identificable por un número de pantalla), aunque lo más común es que cada servidor gestione un display y una pantalla.

Es decir, en lugar de necesitar una simple variable para identificar nuestra ventana (tal y como ocurre en Windows), utilizamos este dispositivo especial llamado display que nos permite especificar dónde debe ser ejecutada nuestra aplicación. De este modo, es posible en una red decidir ejecutar una aplicación en otra máquina, visualizando la ventana en la nuestra, cambiando el display donde ésta es mostrada. Para elegir el display podemos utilizar la función XOpenDisplay (como veremos a continuación) con el display deseado, o modificar el valor de la variable externa DISPLAY (abriendo el display NULL con la anterior función), con lo que el usuario puede especificar donde quiere visualizar la aplicación cambiando el valor de esta variable.

Cuando el cliente y el servidor se ejecutan en la misma máquina, el nombre del display es :0, mientras que si la máquina dispone de varios displays (utilizando por ejemplo VCN), cada uno se especificaría mediante :0.<nº>. Si no hablamos de cliente y servidor en la misma máquina, nos encontramos con que el display se especifica mediante DirecciónIP:<nº>.<nº>. Gracias a esto (y como ejemplo) es posible conseguir servidores X para Windows95/98 (se recomienda el servidor X Win32, que puede encontrarse en Internet), hacer un telnet a nuestro Linux por red y dentro de ese telnet modificar la variable display (supongamos que la dirección IP del Windows95 en esta red es 192.1.2.3):


DISPLAY=192.1.2.3:0
export DISPLAY

En ese momento, cualquier aplicación de X (por ejemplo /usr/X11/bin/xclock &) que ejecutemos en Linux se desplegará en el Windows95/98 gracias al servidor X instalado y a la disponibilidad de la variable DISPLAY (lo cual hace a los programas X realmente independientes del hardware :). Si todo el concepto de display os ha parecido bastante complicado, no hay motivos por los que preocuparse ya que el display es una característica de la red y no de los programas, es decir, nuestra obligación en el programa será abrir el display que haya por defecto (apuntado por DISPLAY), que comunmente será el display de la máquina cliente/servidor, mientras que es el usuario quien debe decidir donde quiere abrir la aplicación.


Window ventana;

Variable de tipo ventana (Window), que nos servirá para referenciar a nuestra ventana.


/* abrimos el display estándar */
display = XOpenDisplay( NULL );

Mediante XOpenDisplay abrimos el display especificado. Pidiendo la apertura del display NULL (0), forzamos al sistema a abrir el display referenciado por la variable DISPLAY. Una explicación más extensa de todas las funciones de este programa de ejemplo se verán en el próximo capítulo.


/* creamos una nueva ventana */
ventana = XCreateSimpleWindow( display, RootWindow( display, 0 ),
                               10, 50, 500, 400, 2, 0, 1 );
Mediante esta línea de código se crea una ventana que se despliega en el display previamente abierto mediante XOpenDisplay, y que se abre en la posición (10,50), de 500x400 pixels de tamaño, con un borde de ancho 2, y de colores del borde 0 y color de fondo 1. El segundo parámetro de la función (como veremos en la próxima entrega) es la ventana madre o raíz de la que debe colgar la ventana que estamos creando. En este ejemplo la hacemos colgar de la ventana raíz mediante la macro RootWindow(), que nos devolverá el identificador de esta ventana raíz.


/* Definimos las propiedades la ventana */
XSetStandardProperties( display, ventana, "Hola Mundo :)", "Hola!", None, NULL, 0, NULL);

Aquí estamos dotando a nuestra ventana de determinadas propiedades, como por ejemplo, de título ("Hola Mundo :)"), o el nombre de la ventana si es minimizada ("Hola!").


/* Mapeamos la ventana para hacerla visible */
XMapWindow( display, ventana );

Una ventana no es visible hasta que no es mapeada, proceso que realizamos mediante XMapWindow(). Mediante esta función hacemos visible nuestra ventana recién creada.


XFlush( display );

Mediante esta función vacíamos el buffer de peticiones nuestro programa. Como se comentó en la sección Arquitectura de X Window, las peticiones de nuestra ventana van a parar a un buffer (propio de cada programa) que no se vaciará hasta que una de estas peticiones sean de evento o de información, o que el buffer esté lleno.


EN LA PROXIMA ENTREGA

En el siguiente número de nuestro curso se describirán las funciones de inicialización de display y ventanas de manera que se puedan usar como referencia rápida cuando se necesite consultar los parámetros o nombre de una determinada función, y antes de pasar a comentar el bucle de mensajes, que és lo que le da a nuestra aplicación su verdadero cuerpo y funcionalidad. Hasta entonces sería conveniente el estudio de la arquitectura de X Window hasta comprender la totalidad de lo comentado en el presente artículo.

Nota: Direcciones de interés: www.xfree86.org

Pulse aquí para bajarse los ejemplos y listados del artículo (3 Kb).

Figura 1: "XWindow y aplicaciones clientes."

Santiago Romero


Volver a la tabla de contenidos.