| Un vistazo a Windows desde dentro |
Entradas desde el 20/10/2003 :
Dejando a un lado Windows NT, las versiones anteriores a la 95 no eran Sistemas Operativos, se sustentaban en
MS-DOS. A partir de la aparición de ésta, MS-DOS abandona el mercado para dejar paso al nuevo producto
de Microsoft, que incluye un simulador para no perder la compatibilidad con los antiguos programas (lo que es un
intento poco fructífero, y los resultados son mediocres).
He aquí, pues, que aparece el Sistema Operativo Windows en sus versiones domésticas, 95, 98 y ahora
2000, y lo que vemos como un avance que es la utilización de ventanas y el entorno gráfico inexistente
hasta ese momento, en realidad no lo es, pues los usuarios de Macintosh llevan años utilizándolo.
En cualquier caso sí supone un paso adelante, especialmente porque se rompe con las barreras de las 640
kb de memoria RAM que eran la pesadilla de los programadores, y se introduce un entorno multitarea inexistente
en su antecesor, que obligaba a la utilización de programas residentes en memoria (con la consiguiente merma
de ésta).
La primera pregunta que considero se debe de hacer es en qué se basa el entorno gráfico, principio
de todo el Sistema. Y la respuesta es inmediata, en el GUI (Graphical User Interface), y bajo este interfaz
todo es gráfico, podemos decir que los textos son dibujos de texto, los impresos son dibujos de impresos.
Los pixels se han hecho los reyes del ambiente. Lo que ocurre es que nosotros no tenemos por qué acceder
a él para nada, lo que realmente utilizamos es otro método distintos en forma de un nuevo interfaz,
el GDI (Graphics Device Interface), que es un lenguaje que nos facilita las tareas. ¿Qué quiero
escribir? este nos proporciona las funciones para hacerlo, concretamente "DrawText", ¿quiero leer
lo escrito? pues igual, "TextOut". Por supuesto no es tan simple como invocar una función desde
un editor, pero tampoco mucho mas complejo. Claro que el GDI nos proporciona como cientos de llamadas a funciones,
pero es que prácticamente todo se basa en él, estamos en un sistema gráfico y es nuestro interlocutor
válido.
A pesar de tener tantas posibilidades, se pueden sintetizar de forma fácil, de hecho ya lo están
porque son las cuatro denominadas "primitivas":
Líneas y curvas: Que abarcan hasta las denominadas curvas Bèzier (se lo dejamos a los matemáticos) que gracias a ellas se consiguen formas elípticas, y que cuando resultan insuficientes, pues Windows no nos ofrece todas las posibilidades, hay que programar punto a punto lo que pretendemos ¿quién no ha visto en determinados gráficos generados, por ejemplo, con el Excel de Microsoft resultados que no se atienen a la realidad?
Areas: O superficies rellenas de color, como son las "ventanas" (windows).
Texto: Tanto en las llamadas "fuentes de contornos rellenos" como en los bitmaps.
Mapas de bits: O matrices de dos dimensiones con un determinado número de pixels y que son la base de la construcción de polígonos, que unidos, pueden llegar a dar el desarrollo de aplicaciones 3D como los celebres juegos de ordenador.
Bien, estas serían las cuatro grandes formas de funcionamiento gráfico, por supuesto no son las únicas,
pero ¿cómo hace Windows para manejarse entre los distintos componentes que lo forman? pues a base
de un servicio de mensajería. Sí, los mensajes, y la llamada cola de mensajes, es otra
de las partes fundamentales. Si nos encontrásemos inmersos en el núcleo del Sistema Operativo (lo
que suelen llamar Kernel) veríamos un trasiego enorme de mensajes en todos los sentidos. Si pulsamos una
tecla, ahí se envía rápidamente un mensaje (en este caso varían en Windows NT), pero
si la soltamos también hay otro, o si movemos el ratón, o pulsamos el botón izquierdo o derecho,
o cualquier cosa que hagamos en un periférico de este tipo, son mensajes que estamos generando, y cada uno
es distinto a los otros, una pulsación de tecla corriente genera un WM_KEYDOWN, si la soltamos, un WM_KEYUP,
pulsamos el botón izquierdo del ratón, WM_LBUTTONDOWN, lo desplazamos, WM_MOUSEMOVE, y así
muchísimos.
Vale, lo primero que cabe pensar es para que sirve ese trasiego de mensajes de nombre raro, pues para que los programas
que se ejecutan en Windows se enteren de lo que está ocurriendo, ellos serán los receptores de esos
mensajes y actuarán en consecuencia. Ya sabrán ellos o el programador que los ha realizado lo que
quiere hacer cuando, o bien su lenguaje de programación o bien él mismo directamente, capture el
mensaje. En unos casos enterarse de la pulsación de una tecla indicará sin más que se escriba
el carácter correspondiente o que se dispare un proceso que realizará no sé que cosas. En
principio no es asunto del Sistema Operativo, su obligación es advertir que se ha realizado una acción,
y lo hace.
Lo segundo que yo pensaría es, si en una pantalla tengo muchos iconos, aplicaciones abiertas, ventanas,
etc. ¿cómo sé que el usuario que ha hecho un "clic" se está refiriendo a
una y no a cualquiera de las otras? Pues de una forma ingeniosa que se llama "el foco". Si hay
10 ventanas abiertas, por ejemplo, y pulso una tecla, el mensaje se va a dirigir a quien posea el foco, como si
fuese una banderola que indica el sujeto que está activo. Por supuesto hay convenciones que se han considerado
estándar y que son las que Windows utiliza, si una ventana está activa y pulso con el ratón
en la de al lado, el foco se traslada a esta, pero no hay que equivocarse, es simplemente eso, una convención
que podemos modificar a base de programación cuando nos apetezca, solamente hay que pensar nuevamente en
mensajes, si al realizar esa operación de ratón permito que la ventana reciba el mensaje WM_SETFOCUS
estoy dejando que se convierta en la activa, y para ello habrá tenido que "matar el foco" de la
anterior en que estuviese, a través de WM_KILLFOCUS. Pero todos estos mensajes normales se almacenan de
forma secuencial en lo que se llama "cola de mensajes", y esa es manipulable, bien puedo actuar sobre
ella, o bien puedo dejar que transcurran y simplemente cambiar su forma de actuación por defecto.
Todo esto con el teclado es muy simple, con el ratón se complica un poco, porque el primero es previsible,
el segundo puede actuar o pulsar en cualquier parte sin que nos lo esperemos. Resulta que mientras estamos haciendo
una pregunta que precisa de un "si" o un "no" como respuesta, el usuario va y pincha con el
ratón en una zona que no tiene nada que ver con lo que sería previsible. ¡Qué problema!
La solución es ver como maneja Windows las ventanas.
La pantalla tiene unas coordenadas, X e Y, cuyo valor (0,0) es el ángulo superior izquierdo, y el máximo
depende de la resolución a que esté configurada, por ejemplo X = 800, Y = 600, lo más típico.
Esto quiere decir que el eje X admite 800 pixels y el Y 600. Bien, ahora supongamos que tenemos una ventana normal
en algún lugar de la pantalla, está tendrá sus propias coordenadas cuyo punto (0,0) será
el vértice inmediatamente inferior al de la barra de título (que se maneja de forma independiente),
también en la zona izquierda. Aquí no podemos saber su tamaño máximo en principio,
pues entre otras cosas una ventana, salvo que se programe lo contrario, se agranda o empequeñece a gusto
del usuario. ¿Qué hace Windows en este caso? Pues dividir en zonas o áreas (lo vimos unas
líneas arriba) y diferenciar entre las que denomina "clientes" y las que no lo son. Una
ventana sería un área cliente, un punto cualquiera de la pantalla no lo sería, y los mensajes
generados por el ratón harán referencia a cual de ellas se trata. De manera que si el usuario no
nos ha hecho caso y ha pinchado fuera de la ventana, podemos no hacer nada, pasar un mensaje, o lo que se nos ocurra.
Si lo ha hecho dentro de la ventana pero fuera del "si" y el "no", lo mismo, pero con la conciencia
de que está actuando dentro de nuestro programa, sobre un área que hemos creado, nosotros sabremos
que queremos hacer en ese caso, actuar o no, cambiar el foco al nuevo sitio elegido por el ratón o negarnos
a ello, depende de los casos y de lo que nos interese.
Como se ve, Windows nos va advirtiendo a través de mensajes, es responsabilidad del programador actuar en
consecuencia, y esto lo lograríamos a base de manipular otros procesos, como son los menús, cuadros
de diálogo, etc. que creo más propios de programación que de funcionamiento de un Sistema
Operativo, y por lo tanto omito dedicarles atención por ahora.
De forma rústica he intentado hacer un acercamiento a lo que es el sistema gráfico y a los mensajes
de Windows, pero hay un aspecto, que mencionaba al principio, que es la multitarea. Estamos dentro de los microordenadores
o PCs, es decir, no tenemos más que un procesador ¿cómo conseguir entonces que este se encargue
de varios trabajos a la vez y que parezca que no es así? Pues en la realidad, con mucho esfuerzo, una de
las características de Windows es que su construcción es "muy pesada", las tareas propias
del Sistema Operativo ya consumen mucho esfuerzo, así que cada vez que se encuentra con un proceso nuevo,
casi se le escucha un !uffff! trabajoso al procesador. Pero dejando de lado que para utilizar Windows de forma
eficaz se necesita un equipo cuanto más potente mejor (y ni aún así, a veces) y volviendo
a la teoría de su funcionamiento, es cuestión de reparto de tiempos, e incluye un temporizador
que utiliza los tics del reloj que la BIOS del sistema le ofrece en forma de interrupciones hardware, o en términos
coloquiales, funciona apoyándose en el reloj del ordenador. Pero ocurre que distintos programas pueden tener
temporizadores abiertos (el número de ellos a partir de Windows 95 no es indefinido, pero sí alto)
¿qué ocurre entonces? que el sistema se encarga de hacer de contador. Por ejemplo, si he generado
una aplicación en la que le indico que cada 10 segundos me actualice un proceso cualquiera, y teniendo en
cuenta que los temporizadores funcionan en intervalos de milisegundos, habré creado en realidad un contador
que va desde 10.000 a 0 milisegundos. Pues de esto se encarga el Sistema Operativo, cada vez que llegue al 0 enviará
un mensaje ¡cómo no! del tipo WM_TIMER al temporizador correspondiente, y al recibirlo ya sabré
lo que tengo que hacer con él, puesto que precisamente estoy esperando ese mensaje.
Esto, que parece una gran idea, bajo mi muy humilde opinión, es un fallo grave que imposibilita la utilización
de temporizadores a excepción del propio del Sistema. Veamos un ejemplo, programo un reloj de manera que
me desplace la aguja cada segundo, es decir, creo un temporizador con valor 1.000 y cada vez que reciba el mensaje
correspondiente muevo la aguja. Pero resulta que el WM_TIMER entra en la cola de mensajes, y de repente lanzo un
proceso, por ejemplo sobre una base de datos, que monopoliza al procesador, pues como es único solo se encarga
de esa tarea prioritaria. Si ese proceso ha durado 10 segundos, el resultado teórico es que en la cola de
mensajes tendrían que figurar los correspondientes 10 WM_TIMER, y al recibirlos en lugar de saltar mi reloj
1 segundo saltaría 10 de golpe, pero qué curioso que no es así. Cuando un mensaje de este
tipo entra en la cola, si encuentra otro igual mata al anterior, con lo que solamente podré obtener uno
de ellos sin control del tiempo que ha pasado, el último, es decir, en este ejemplo habría retrasado
9 segundos, lo que hace su funcionamiento inútil. ¿Qué tendría que hacer? utilizar
lo que se denominan "hilos de ejecución" distintos, pero ese ya es otro tema y un cantar más
complejo.
José Luis Freire