INTRODUCCION A LA PROGRAMACION EN X WINDOW

Artículo 6: GRAFICOS EN XLIB (I).

Autor: (c) Santiago Romero.
Revista: Programación Actual (Prensa Técnica) nº 24, Marzo-1999


A partir de esta entrega será posible contar con las diferentes primitivas gráficas de que provee Xlib, como uso de primitivas elementales, colores, trazado de imágenes y fuentes en ventanas o en contextos gráficos (GCs) que luego utilizaremos para transferirlos a las mismas (double buffering).

Antes de pasar a codificar gráficos en X-Lib se hacen necesarias algunas definiciones básicas relativas al mundo de los gráficos en general, estando algunas particularizadas al nombre que un determinado concepto recibe dentro de Xlib o del sistema X Window. Posteriormente veremos el sistema de gráficos de X Window y las primitivas que Xlib nos ofrece, pero siempre teniendo en cuenta que para escribir aplicaciones gráficas de alta velocidad se hará necesario el uso de librerías externas (como la conocida XwinAllegro) o de las extensiones MitSHM y Xfree86-DGA (buffers de memoria virtual para los gráficos y acceso directo al frame buffer de vídeo, respectivamente), aunque para aplicaciones que no requieran excesiva velocidad todo lo que veremos es más que suficiente para poder crear gran variedad de aplicaciones para X Window.


DEFINICIONES BASICAS

Lo primero de todo, cabe distinguir entre modos alfanuméricos y modos gráficos. Los primeros son aquellos modos de pantalla en que ésta se divide en celdillas de un tamaño determinado. Dentro de cada una de estas celdillas sólo puede haber un carácter, ya sea un número, una letra, o cualquier otro símbolo (de ahí el nombre de alfanumérico), y entre cuyas propiedades simplemente puede observarse el color del carácter o del fondo en cada celdilla. Este modo es el que se arranca en Linux o MSDos por defecto (si no utilizamos xdm o kdm o Windows), y suele ser de 80x25 (nº de columnas por número de filas de caracteres). Por contra, en los modos gráficos la pantalla se divide en una serie de puntos de color, llamados pixels, permitiendo acceder a los "puntos" individuales de la pantalla.

Los pixels (abreviatura del inglés picture element, PEL) son las unidades mínimas para los gráficos en dos dimensiones, una serie de pequeños puntos con un color asociado cuya conjunción define las diferentes imágenes.

Esos pixels se agrupan en la pantalla para formar imágenes, pero esta agrupación está limitada por la resolución de la pantalla, ya que en cada modo de vídeo (de los diferentes modos disponibles por la tarjeta) hay una cantidad determinada de pixels a lo largo y ancho de la pantalla. Las resoluciones se especifican en el formato NºPixelsAnchura x NºPixelsAltura. Así, un modo de vídeo de 800x600 píxeles de resolución indica que pueden activarse (cada uno de ellos con su color asociado) 800 píxeles en cada línea horizontal de pantalla, con un total de 600 líneas.

Cuando es necesario hacer referencia a una posición concreta de la pantalla (un pixel de la misma) se utilizan sus coordenadas de pantalla para describir su situación. Las coordenadas de pantalla son especificadas mediante un par de números en el formato (posición_x, posición_y). De tal forma, el píxel (400,300) podría considerarse situado aproximadamente en el centro de una pantalla de resolución 800x600, de la misma forma que el (0,0) es la esquina superior izquierda y el (799,599) la esquina inferior derecha (ver figura 1).

primitivas graficas

Existen diferentes modos de vídeo, es decir, la tarjeta es capaz de programar el sistema para diferentes resoluciones y número de colores, y gracias a esto el programador puede elegir entre todos ellos el más apropiado para su programa. Así, es posible que nuestro programa trabaje en 320x200 a 256 colores, 640x480 a 65.535 colores, o incluso a 1024x768 con 32 millones de colores. Algunas veces es tarea del programador seleccionar el modo de vídeo adecuado para el programa (aplicaciones fullscreen o pantalla completa), mientras que otras es necesario detectar cuál es el modo de vídeo actual y trabajar en este modo (como hace cualquiera de los programas de X Window o Windows que trabajan en modo ventana o modo windowed). Por suerte, en la mayoría de los casos las librerías nos permitirán trabajar con cualquiera de estos modos de vídeo de una manera transparente al programador.


EL VALOR DE UN PIXEL

A continuación vamos a ver las nociones básicas sobre gráficos en el PC a título de ejemplo para poder comprender todas las nuevas funciones y variables y los conceptos asociados a las mismas en otros sistemas.

En un PC, a partir de una determinada posición de memoria (0xA0000 en muchos casos) se encuentra la denominada memoria de vídeo (videomemoria o VRAM). La memoria de vídeo es memoria integrada en la tarjeta de vídeo, y cuyos contenidos son leídos por la tarjeta para dibujar en pantalla la imagen que es visualizada en cada momento.

Entre 50 y 90 veces por segundo (según el monitor y modo de vídeo) la tarjeta gráfica lee de esta memoria los valores que contiene y los envía al monitor (gracias al conversor DAC) en forma de haz electrones mediante el tubo de rayos catódicos que hay dentro de la misma. Explicado de una manera sencilla: en nuestro monitor haz un cañón de electrones (TRC) que bombardea la pantalla empezando desde la esquina superior izquierda hasta la esquina inferior derecha, moviendose horizontalmente y bajando a la siguiente línea al llegar al límite derecho de la pantalla. Estos electrones activan los diferentes pixels del monitor de forma que el usuario ve físicamente la imagen. El motivo de que éste se haga tantas veces por segundo es que la excitación de las partículas de la pantalla desaparece con el tiempo, de modo que es obligatorio refrescarlas, y de ahi la frecuencia de 50 a 90 veces por segundo (este proceso se llama retrazado o refresco de la pantalla).

Esto quiere decir que los datos en la videomemoria son leídos muchas veces por segundo y enviados en forma de colores al monitor, lo cual da una posibilidad muy rápida y sencilla de trazar puntos: simplemente escribiendo en la videomemoria (en el PC habitualmente a partir de 0xA0000) en la posición correcta y en el formato correcto (según el modo de vídeo) el valor adecuado, habremos cambiado automáticamente el valor de un pixel de la pantalla. Esta técnica se utilizaba en MSDOS al ser la posibilidad más rápida de que se disponía, pero en el caso de sistemas como Windows, X Window o similares esto es imposible (o difícil) ya que la memoria suele tener algun mecanismo de protección, o bien no es permitido por el propio Sistema Operativo, o, en el peor de los casos, no estamos ante un PC (recordemos que X Window corre en gran variedad de plataformas) y lo que estamos haciendo no tiene ningún sentido físico para la máquina. Para evitar esto se utilizan las funciones incluidas en el propio Sistema Operativo (o Sistema Distribuido, en este caso), que son las apropiadas para cada máquina, de forma que nuestro programa correrá perfectamente en un PC, un Alpha, o cualquier otra plataforma que soporte X Window.


LA PALETA DE COLORES

Cada color está formado por la mezcla de tres colores básicos: rojo, verde y azul, utilizando el sistema RGB (del inglés Red-Green-Blue) para especificar colores. A la hora de identificar un determinado color (por ejemplo, blanco intenso), éste se define como la suma de los tres colores básicos, indicando además la proporción de la mezcla de éstos. Esta proporción se especifica utilizando un byte (rango de 0 a 255), con lo que el blanco puro más intenso (mezcla de todos los colores) sería el color (255,255,255), mientras que el negro (ausencia de color) sería (0,0,0).

Cada vez que se especifica un color se hace como una tripleta (R,G,B) que define cuánta cantidad de rojo (valor de R), verde (G) y azúl (B) forman dicho color. Estos tres valores son llamados componentes RGB del color. Así, un rojo medianamente intenso se podría definir como (128,0,0) (128 de rojo y nada de las otras 2 componentes). De esta manera (mezclando estas tres componentes con las diferentes intensidades 0-255) se pueden obtener hasta 256*256*256 = 16.7 millones de colores diferentes.

Visto de una manera muy sencilla, si pudiesemos ir a la videomemoria y almacenar allí (en alguna posición de la misma) tres bytes de valores 128, 0 y 0 aparecería en pantalla un pixel de color rojo, cuyas coordenadas dependerian del lugar de la videomemoria (cuyo tamaño varía entre 64Kb y 8Mb según la tarjeta) donde hubiésemos escrito los tres valores (modos TrueColor RGB).


MODOS CON PALETA Y MODOS SIN PALETA

No todos los modos gráficos son iguales en cuanto al color que utilizan. La distinción fundamental que se puede hacer en los diferentes modos gráficos con respecto al color se basa en dividirlos entre modos con paleta y modos sin paleta.

En los modos sin paleta, los colores se definen de la forma explicada hasta ahora, y en el supuesto de que se deseara referirse a un determinado pixel, se hace siempre mediante las tres componentes de color RGB, y éstas son almacenadas en videomemoria (ya sea por nosotros o por las rutinas del S.O. a que llamemos para el trazado de gráficos) mediante las 3 componentes de color. Esto hace que para definir un color sean necesarios 3 bytes y que el tamaño de la videomemoria tenga que ser bastante grande. Por ejemplo, para una resolución de 1024x768 tenemos en total 1024*768=924.672 pixels, y si necesitamos 3 bytes (R,G,B) para definir cada color, entonces hará falta una videomemoria de 924.672*3 = 2.774.016 bytes para poder soportar ese modo gráfico (¡casi 3 MB de videoram!). Para ahorrar memoria gráfica (y por tanto obtener un aumento de velocidad en muchas ocasiones) se utilizan modos con paleta.

En los modos con paleta (paletized o modos pseudocolor) cada color se define con un único byte (en el rango 0-255) y no con tres como en el caso de los modos sin paleta de 24bpp. ¿Cómo puede hacerse esto? En estos modos, cuando decimos que un pixel tiene el valor cero (y así lo escribimos en videomemoria), ese valor es utilizado durante el retrazado de pantalla no cómo el color en sí mismo, sino como un índice a una tabla de 256 colores (conocida como paleta o mapa de colores) de donde se obtendrán los 3 valores RGB a reprensentar en pantalla.

Explicado de una manera esquemática, en la tarjeta existe una tabla de 256 elementos similar en formato a la siguiente:


paleta[256][3] = { 0, 0, 0, 0, 10, 63, 2, 23, 2 ... 63, 63, 63 };

A leer la tarjeta un cero de la videomemoria, va a esta tabla y obtiene los valores RGB asociados al color cero, representándolos en pantalla. En este caso R=paleta[0][0], G=paleta[0][1], y B=paleta[0][2]. Si se hubiese especificado como color el 128, por ejemplo, la tarjeta hubiese obtenido los valores RGB correspondientes a la entrada 128 (paleta[128][0], [1] y [2]) de la paleta de colores, y de igual forma para el resto de colores. De esta manera sólo necesitamos un byte para definir un color, aunque debido a esto reducimos el número de colores a 256 como máximo (desde el color 0 al color 255).

Por supuesto, en esos 256 colores hay verdes, azules, rojos, etc. predefinidos. Esto es lo que se llama la paleta por defecto, y consiste en que en esta tabla en principio no es necesario escribir nada porque viene con 256 colores ya predefinidos. Si dichos colores no son los necesarios para nuestra aplicación (ya sea porque necesitamos muchos colores de un matiz, porque vamos a cargarlos desde un fichero gráfico, etc.), es posible cambiarlos (uno a uno o incluso el bloque completo de colores) a los valores deseados. Por defecto el valor cero es el color negro (0,0,0), pero simplemente cambiando las tres primeras entradas de la paleta de colores (mediante funciones del S.O.) podemos hacer que el valor cero represente a otro color RGB especificado.

La única limitación al definir las componentes RGB de los diferentes colores es que el valor máximo de cada componente de color es de 63 (un (63,63,63) sería blanco puro), lo cual nos deja 63*63*63 posibilidades=262.144 colores diferentes, pero sólo 256 de ellos (los elegidos en la paleta) en pantalla. Gracias a la utilización de la paleta y la utilización de un sólo byte por pixel (8 bits por pixel u 8 bpp), ahora sólo son necesarios 1024*768*1=924.672 bytes para la resolución 1024x768 a 256 colores. En uno de los ejemplos de este artículo se muestra un programa que visualiza la paleta de colores activa. Como veremos, en Xlib la paleta de colores se denomina colormap (mapa de colores).

NOTA: La limitación a 63 del valor máximo de las componentes de color se aplica a tarjetas con un DAC de 6 bit (0-63), pero eso será transparente para nosotros usando las rutinas de Xlib, como veremos en los próximos apartados.


OTRAS DEFINICIONES

Ahora que ya conocemos cómo se definen los pixels y el significado de sus componentes (ya sean 3 ó una entrada a una paleta de colores), veremos el resto de conceptos básicos de este mes.

Profundidad de color: el número de bits por pixel (bpp) que utiliza un determinado modo de vídeo se conoce como profundidad de color de ese modo (depth), y puede variar en principio entre 8 bpp (Pseudocolor, 1 byte -> 256 colores), 15 bpp (32768 colores), 16 bpp (Hicolour, 65536 colores), 24 bpp ó 32 bpp (TrueColor, 16.7 millones de colores).

Pixmaps: Los pixmaps son (vistos de una manera sencilla) memoria (por ejemplo, un array) donde se almacenan gráficos de igual forma que si fuese la videomemoria, con la salvedad de que no aparecen en la pantalla, sino que se quedan ahi, listos para ser copiados a la videomemoria (a la pantalla) cuando sea necesario.

Un bitmap es un caso particular de pixmap donde cada color está representado por 1 bit (en los modos de vídeo antiguos de 2 colores, donde sólo hay 1 ó 0 representando si había color o no), pero la palabra se ha extendido adquiriendo prácticamente el mismo significado que pixmap. Hoy en día un bitmap/pixmap se puede definir como un array que contiene los valores de pixels que habría que escribir en videomemoria para obtener la imagen a la que representa.

 Ejemplo: letra 'A' (.=pixel blanco, #=pixel negro):

 ........
 .######.
 .##..##.
 .######.
 .##..##.
 .##..##.
 .##..##.
 ........

En memoria:


char letra[9*9]={ 0,0,0,0,0,0,0,1,1,1, ... , 0};

Hay varios de estos bitmaps en /usr/include/X11/bitmaps, y se pueden convertir a ascii mediante el comando bmtoa <nombre_de_bitmap>.

Los pixmaps se usan normalmente como imágenes fijas, fuentes de texto, cursores, fondos de ventana, patrones de rellenado, pantallas virtuales donde escribir para volcarlo a la pantalla real cuando sea necesario, etc. Además sobre los pixmaps es posible utilizar diferentes primitivas de dibujo proporcionadas por Xlib (trazado pixels, líneas, etc.), y transferir su contenido a otro pixmap o ventana (ya que la ventana misma es un pixmap).

Los Contextos gráficos (GC) son recursos utilizados para especificar atributos para las primitivas de dibujo de X, como colores, anchura de línea, patrones de rellenado, etc. Estos elementos serán comentados con mucho más detalle en el próximo número.


EL COLOR EN XLIB

A la hora de trabajar con gráficos el color es un elemento imprescindible, ya que sea cual sea la primitiva que se trata de dibujar (un pixel, un rectángulo, texto, o cualquier otra cosa) suele ser necesario indicarle al sistema las propiedades de dicha primitiva, siendo una de ellas fundamental y básica: su color. Por esto, antes de comenzar a explicar las diferentes primitivas gráficas es obligatorio comenzar aprendiendo cómo trata X Window los colores, cómo podemos encontrar un determinado color de entre los disponibles en el sistema, así como los nombres de estructuras que se usarán para indicarle a Xlib el uso de un determinado color u otro diferente.

A la hora de programar nuestro programa en Xlib nosotros no sabemos si el usuario final estará usando un modo de 256 colores (con paleta) o un modo HiColour, TrueColor (sin paleta, modos de 15, 16, 24 o 32 bpp), de modo que Xlib nos proporciona funciones de forma que mediante su uso nuestro programa funcione en todo tipo de displays.

El problema está, por ejemplo, a la hora de realizar las operaciones gráficas. Si nosotros queremos especificar como color de fondo de nuestra ventana el negro, esto se haría (en teoría) de forma diferente si estamos en un modo de 256 colores (siendo el negro el color 0, por ejemplo) o en un modo sin paleta (sería el color (0,0,0)). Para ello se utiliza una estructura de color y una serie de funciones que nos devolverán el valor del color, siendo ellas las encargadas de buscar en la paleta de colores el color más parecido al que buscamos, para realizar nuestro dibujo en ese color (recordemos que sólo hay 256 colores en los modos con paleta y puede ser que el pedido no esté disponible entre ellos), o de devolver las componentes RGB del color.

Por supuesto, existe un mapa de colores por defecto (al igual que ocurría en el caso de la tarjeta gráfica) definido por el servidor en el momento del arranque del mismo. En este mapa de colores, aparte de la disponibilidad o no del color necesitado, si necesitarámos el color blanco éste bien podría ser el 1, el 34, el 255... (cuál es, no lo sabemos a priori).

Es posible obtener el identificador de este mapa de colores (o paleta) mediante las siguientes funciones:


Colormap XDefaultColormap( Display *display, int pantalla );
Colormap DefaultColormap( display, pantalla );

Este identificador (Colormap ID) se utilizará para algunas funciones gráficas, incluida la modificación del mismo, aunque esto último no será necesario ya que el mapa de colores por defecto suele ser suficiente para la mayoría de aplicaciones básicas.


LA ESTRUCTURA XCOLOR

A la hora de referirse a colores, ya que el modo de vídeo podría estar basado en una paleta de colores se hace necesario localizar de entre los diferentes colores posibles el más parecido al color que deseamos utilizar para un determinado trazado (texto, pixels, etc.). Para ello se utiliza la función XAllocColor() mediante una estructura de tipo Xcolor:


typedef struct 
{
 unsigned long pixel; /* pixel value */
 unsigned short red, green, blue; /* rgb values */
 char flags;         /* DoRed, DoGreen, DoBlue */
 char pad;
} XColor;

Los valores red, green y blue son en este caso valores en el rango 0 a 65535 (blanco=(65535,65535,65535)), pero con la misma filosofía que lo visto hasta ahora en el rango 0-255. Estos valores tan elevados permiten representar colores independientemente del número de bits utilizados en el hardware de visualización (es el servidor quien escala estos valores a los valores apropiados para el display, ya sea al rango 0-63, a 0-255 o a otro que pudiese utilizarse en el futuro o en máquinas más potentes...).

Por otra parte, en el campo pixel se devolverá el valor de pixel en las llamadas a las rutinas apropiadas, mientras que pad es un campo de uso del sistema. El campo flags se usa para determinadas aplicaciones con mapa de colores descompuesto (OR inclusivo).

A la hora de localizar un color en el mapa de colores activo se utiliza la función XAllocColor() con los parámetros apropiados:


Status XAllocColor( Display *display, Colormap mapa, XColor *color );

Argumentos: El display es aquel obtenido mediante XOpenDisplay(). Mapa es el mapa de colores donde localizar el color (normalmente el ID del Colormap por defecto), y color es un puntero a una estructura XColor correctamente rellenada con los campos red, green y blue conteniendo los valores del color RGB a buscar. Esta función nos devolverá el color resultante (el color disponible más parecido al demandado) en el campo pixel de la estructura XColor utilizada, además de modificar los campos red, green y blue de esta con las componentes de color exactas del color encontrado.

Ejemplo de búsqueda del color blanco:


Display *display;
Window window;
Colormap mapa;
XColor color;
unsigned long blanco;

 (...más código...)
 display=XOpenDisplay(NULL);
 mapa=DefaultColorMap(display, DefaultScreen(display));
 color.red = 65535;
 color.blue = 65535;
 color.green = 65535;
 XAllocColor( display, mapa, &color );
 blanco = color.pixel;

Tras esto en la variable blanco dispondríamos de la entrada/valor/celdilla de dicho color, y podríamos utilizarla en las funciones gráficas apropiadas. Como el trozo de código visto arriba suele ser bastante utilizado si tratamos con muchos colores, vamos a crearnos una función que haga todo el trabajo desde una sola llamada:


unsigned long Color( Display *display, unsigned short red, 
              unsigned short green, unsigned short green, Colormap mapa )
{
  XColor color;
  color.red=red;
  color.blue=blue;
  color.green=green;
  XAllocColor( display, mapa, &color );
  return( color.pixel );
}


BUSQUEDA DE COLORES POR NOMBRE

A la hora de buscar un determinado color no es estrictamente necesario trabajar con tripletas (R,G,B) y valores numéricos, sino que X Window dispone de una base de datos de colores (con nombres asociados a los mismos) cuya definición se encuentra disponible en el fichero /usr/X11R6/lib/X11/rgb.txt, y cuyas 5 primeras líneas podemos ver a continuación a título de ejemplo:

! $XConsortium: rgb.txt,v 10.41 94/02/20 18:39:36 rws Exp $
255 250 250    snow
248 248 255    ghost white
248 248 255    GhostWhite
 (etc...)
144 238 144    LightGreen

En este fichero están disponibles las definiciones de los tripletes RGB de colores más usuales, y Xlib dispone de una función mediante la cual podemos localizar un color en el mapa de colores especificado dando como parámetro una cadena con el nombre del color deseado:


Status XAllocNamedColor(display, colormap,
  color_name, screen_def, exact_def);
Parámetros: Display *display; -> Display.
Colormap colormap; -> Mapa de colores donde buscar.
char *color_name; -> Cadena con el nombre del color a buscar.
XColor *screen_def, *exact_def; -> En la primera estructura se devuelve (campo pixel) el color encontrado, mientras que en la segunda se devuelven los valores RGB exactos del color que se pidió.

De nuevo podemos utilizar esta función dentro de otra que haga más sencillo la utilización de colores en el programa principal:


unsigned long ColorPorNombre( Display *display, char *Nombre, Colormap mapa )
{
  XColor color, temp;
  XAllocNamedColor( display, mapa, Nombre, &color, &temp );
  return( color.pixel );
}

Si vamos a trabajar en nuestra aplicación con el mapa de colores por defecto (muy recomendable a menos que se trate de un juego o programa de dibujo o visualización que requiera de toda la paleta de colores con valores específicos), podemos ahorrarnos también el parámetro mapa:


unsigned long ColorPorNombre( Display *display, char *Nombre )
{
  XColor color, temp;
  XAllocNamedColor( display,
     DefaultColormap(display,DefaultScreen(display)),
     Nombre, &color, &temp );
  return( color.pixel );
}

Mediante esta nueva función, las siguientes operaciones son ya perfectamente posibles en nuestro programa:


 blanco = ColorPorNombre( display, "snow" );
 azul = ColorPorNombre( display, "blue" );

Y mediante estas nuevas variables y funciones ya es posible indicarle a las diferentes funciones en qué color queremos realizar cada operación. Hasta ahora la única función que hemo visto que precise colores es la de creación de la ventana del programa, de modo que lo siguiente que veremos será un programa de ejemplo que cree la ventana con un color que no sea negro o blanco (macros WhitePixel y BlackPixel), sino que utilice ColorPorNombre() para localizar los colores deseados. En la próxima entrega, cuando veamos las diferentes primitivas gráficas, usaremos estas funciones y estructuras para dotar a las figuras del color que queramos otorgarle.


RESERVAR COLORES Y CREAR MAPAS

Si necesitamos reservar alguno de los colores de la paleta para nuestro uso personal (como hacen algunos programas que necesitan colores específicos para poder variarlos en tiempo real), es posible utilizar las funciones XAllocColorCells(), XStoreColor() y XStoreNamedColor() (más información en las correspondientes páginas man).

Respecto a los mapas de color, si nuestra aplicación utiliza muchos colores distribuidos de una manera concreta (por ejemplo, si usa imágenes pregrabadas a cargar desde fichero con unos colores y paleta prefijados), es posible crear un mapa de color propio mediante la función XCreateColorMap(). Cómo sólo puede haber un mapa de color activo, éste será el asociado a la ventana en primer plano en cada momento.


EJEMPLOS DE ESTE MES

En el listado 1 tenemos un programa que utiliza las funciones expuestas arriba para abrir una ventana de fondo azul y texto amarillo, donde además se visualiza un mensaje en este color cuando se pulsa el botón del mouse. Este ejemplo ha sido compilado mediante la orden:


gcc -o ejemplo1.exe ejemplo1.c -L/usr/X11R6/lib -lX11

Sin duda uno de los aspectos fundamentales de este ejemplo es que (como veremos en la próxima entrega) muestra la manera de obtener y modificar los atributos gráficos (GC) de un objeto para modificar diferentes parámetros (como su color asociado). En este caso lo hacemos para imprimir la cadena en amarillo (tras obtener el valor de dicho color con ColorPorNombre()):


 /* estructura utilizada para cambiar valores */
 XGCValues nuevo;
 
 /* 1º creamos un GC para texto amarillo */
 gc = XCreateGC( display, window, 0, NULL );
 nuevo.foreground=amarillo;

 /* cambiamos el valor del GC */
 XChangeGC( display, gc, GCForeground, &nuevo);

 /* Ahora seleccionamos la fuente con el GC deseado */
 XSetFont( display, gc, font );
Todo esto será comentado con más detalle en nuestra próxima entrega del curso. Aparte de esto, el color de la ventana podría haber sido cambiado tras su creación mediante XSetWindowBackground() y XSetWindowBorder(), cuyos parámetros son el display, la ventana y un color.


 LISTADO 1: LOCALIZACION DE COLORES:

 /* Ejemplo1.c -> Ventana de fondo azul y letras amarillas */

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <strings.h>
 
unsigned long ColorPorNombre( Display *, char * );
   
/*--- Funcion principal main() --------------------------------*/ 
main()
{
 Display *display;
 Window window;
 Font font;
 GC gc;
 XEvent evento;
 int pantalla;
 unsigned long azul, amarillo;
 char cadena[]="Esto deberia aparecer en amarillo.";
 XGCValues nuevo;
 
 display = XOpenDisplay( NULL );
 font = XLoadFont( display, "8x16" );
 pantalla = DefaultScreen( display );
 azul = ColorPorNombre( display, "blue");
 amarillo = ColorPorNombre( display, "yellow" );
 
 window = XCreateSimpleWindow( display,
            DefaultRootWindow( display ), 350, 0, 500, 400,
            1, amarillo, azul );
 
 XSelectInput( display, window, ButtonPressMask | KeyPressMask );
 XMapWindow( display, window );
 gc = XCreateGC( display, window, 0, NULL );
 nuevo.foreground=amarillo;
 XChangeGC( display, gc, GCForeground, &:nuevo);
 XSetFont( display, gc, font );
 
 while( 1 )
 {
               
  XNextEvent( display, &:evento );
  switch( evento.type )
  {
   case ButtonPress:
    XDrawString( display, window, gc,
                 evento.xbutton.x,
                 evento.xbutton.y, 
                 "X", 1);
    XDrawString( display, window, gc, 10, 150,
                 cadena, strlen(cadena));
    break;

    case KeyPress:   exit(0); 
  }
 }
}

/*--- Rutina para localizar colores por nombre ----------------*/
unsigned long ColorPorNombre( Display *display, char *Nombre )
{
  XColor color, temp;
  XAllocNamedColor( display,
     DefaultColormap(display,DefaultScreen(display)),
     Nombre, &:color, &:temp );
  return( color.pixel );
}

El otro ejemplo (listado 2) muestra el mapa de colores completo en subventanas de la ventana principal. Para poder ver bien este mapa de colores habremos de tener configurado el servidor X Window en un modo de 256 colores (sino sólo veremos 64 celdillas), y arrancarlo mediante la orden "startx -- -bpp 8". Este listado se ha obtenido de uno de los tutoriales de Xlib en Internet, y muestra cómo obtener el número de colores del mapa por defecto así como crear ventanas hijas.



 LISTADO 2: CAMBIO DE ATRIBUTOS DE COLOR:

/* Ejemplo2 -> Mapa de colores */

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>
 
#define ANCHO 8
#define ALTO  20
 
void main()
{
  Display *display;
  Window madre, hija;
  Colormap mapa;
  XColor borde, fondo, temp;
  int numColores, f;
  XSetWindowAttributes atributos;
 
  display = XOpenDisplay( NULL );
  mapa = DefaultColormap( display, DefaultScreen( display ) );
  numColores = XDisplayCells( display, DefaultScreen( display ) );
  printf( "\nHay %d colores en el DefaultColormap.", numColores );
 
  XAllocNamedColor( display, mapa, "yellow", &borde, &temp );
  XAllocNamedColor( display, mapa, "blue",   &fondo, &temp );
 
  madre = XCreateSimpleWindow( display, DefaultRootWindow( display ), 0, 0,
          (ANCHO*numColores) + 10, ALTO + 10, 10, borde.pixel, fondo.pixel );
 
  atributos.override_redirect = True;
  XChangeWindowAttributes( display, madre, CWOverrideRedirect, &atributos );
 
  XMapWindow( display, madre );
  XFlush( display );
 
  for ( f=0; f<numColores; f++)
  {
    hija = XCreateSimpleWindow( display, madre,
            (f*ANCHO)+5, 5, ANCHO, ALTO, 0, f, f );
    XMapWindow( display, hija );
  }
    XFlush(display);
    getchar();
}


EN RESUMEN

En la próxima entrega comenzaremos a ver las diferentes primitivas gráficas de trazado de gráficos y de texto disponibles, así como diversos ejemplos con diferentes colores y objetos, para los cuales utilizaremos las rutinas comentadas este mes. Para obtener más información sobre cualquiera de las funciones y estructuras utilizadas es posible acudir a la página man correspondiente (ejecutar "man <función/estructura>", respetando mayúsculas y minúsculas).

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

Figura 1: "El color en Xlib."

Santiago Romero


Volver a la tabla de contenidos.