Previous Next Table of Contents

4. Manejo de eventos

4.1 Eventos y tipos de eventos

Las peticiones se envían siempre desde el cliente al servidor para que sean procesadas en el servidor. Hay otro tipo de mensaje en el protocolo X, denominado evento, que se envía siempre del servidor al cliente para informarle de lo que está ocurriendo en el display del servidor. Los eventos pueden ser tanto provocados por hardware: presión del botón del ratón, movimiento del ratón, tecla pulsada, etc. como por software: cambio del tamaño de la ventana, cambio del orden de apilamiento, ventana expuesta, etc.

En el sistema de la Xlib hay 33 eventos definidos, con identificadores del 2 al 34. Cada uno de éstos números tiene un nombre simbólico asociado denominado tipo de evento, definido en el fichero X11/X.h. Las columnas I y II de la tabla 4.1 muestran los números y tipos de los eventos.

Los eventos pueden clasificarse de la siguiente forma:

Eventos relacionados con el teclado y el ratón

Ratón

ButtonPress, ButtonRelease, MotionNotify, EnterNotify, LeaveNotify

Teclado

KeyPress, KeyRelease, FocusIn, FocusOut, KeymapNotify

Ambos

MappingNotify

Eventos relacionados con las ventanas

ConfigureNotify, CreateNotify, DestroyNotify, MapNotify, UnmapNotify, GravityNotify, VisibilityNotify, ReparentNotify

Eventos relacionados con gráficos

Expose, GraphicsExpose, NoExpose, ColormapNotify

Eventos relacionados con el administrador de ventanas

CirculateRequest, ConfigureRequest, MapRequest, ResizeRequest

Eventos relacionados con la comunicación entre clientes

ClientMessage, PropertyNotify, SelectionClear, SelectionRequest, SelectionNotify

4.2 Tipos de eventos y estructuras de eventos

Cada mensaje de evento consta de una estructura de evento que lleva varias clases de datos sobre el evento y de la que el cliente puede obtener información. Cada evento tiene una estructura de evento única definida en del fichero de cabecera X11/Xlib.h. Las columnas II y III de la tabla 4.1 muestran la relación entre los tipos de eventos y sus correspondientes estructuras de eventos. Algunas de las estructuras con idénticas, en cuyo caso del nombre genérico se da en la columna IV. Debido a ello, hay 29 estructuras de eventos para 33 tipos de eventos.

+----+-----------------+------------------------------------------+-----------------+
|    | Tipo del evento |           Estructura del evento          |   Campo XEvent  |
+----+-----------------+-----------------------+------------------+-----------------+
| I  |       II        |         III           |        IV        |        V        | 
+----+-----------------+-----------------------+------------------+-----------------+
|  2 |KeyPress         |XKeyPressEvent         |XKeyEvent         |xkey             |
|  3 |KeyRelease       |XKeyReleaseEvent       |XKeyEvent         |xkey             |
|  4 |ButtonPress      |XButtonPressEvent      |XButtonEvent      |xbutton          |
|  5 |ButtonRelease    |XButtonReleaseEvent    |XButtonEvent      |xbutton          |
|  6 |MotionNotify     |XPointerMoveEvent      |XMotinEvent       |xmotion          |
|  7 |EnterNotify      |XEnterWindowEvent      |XCrossingEvent    |xcrossing        |
|  8 |LeaveNotify      |XLeaveWindowEvent      |XCrossingEvent    |xcrossing        |
|  9 |FocusIn          |XFocusInEvent          |XFocusChangeEvent |xfocus           |
| 10 |FocusOut         |XFocusOutEvent         |XFocusChangeEvent |xfocus           |
| 11 |KeymapNotify     |XKeyMapEvent           |                  |xkeymap          |
| 12 |Expose           |XExposeEvent           |                  |xexpose          |
| 13 |GraphicsExpose   |XGraphicsExposeEvent   |                  |xgraphicsexpose  |
| 14 |NoExpose         |XNoExposeEvent         |                  |xnoexpose        |
| 15 |VisibilityNotify |XVisibilityEvent       |                  |xvisibility      |
| 16 |CreateNotify     |XCreateWindowEvent     |                  |xcreatewindow    |
| 17 |DestroyNotify    |XDestroyWindowEvent    |                  |xdestroywindow   |
| 18 |UnmapNotify      |XUnmapEvent            |                  |xunmap           |
| 19 |MapNotify        |XMapEvent              |                  |xmap             |
| 20 |MapRequest       |XMapRequestEvent       |                  |xmaprequest      |
| 21 |ReparentNotify   |XReparentEvent         |                  |xreparent        |
| 22 |ConfigureNotify  |XConfigureEvent        |                  |xconfigure       |
| 23 |ConfigureRequest |XConfigureRequestEvent |                  |xconfigurerequest|
| 24 |GravityNotify    |XGravityEvent          |                  |xgravity         |
| 25 |ResizeRequest    |XResizeRequestEvent    |                  |xresizerequest   |
| 26 |CirculateNotify  |XCirculateEvent        |                  |xcirculate       |
| 27 |CirculateRequest |XCirculateRequestEvent |                  |xcirculaterequest|
| 28 |PropertyNotify   |XPropertyEvent         |                  |xproperty        |
| 29 |SelectionClear   |XSelectionClearEvent   |                  |xselectionclear  |
| 30 |SelectionRequest |XSelectionRequestEvent |                  |xselectionrequest|
| 31 |SelectionNotify  |XSelectionEvent        |                  |xselection       |
| 32 |ColormapNotify   |XColormapEvent         |                  |xcolormap        |
| 33 |ClientMessage    |XClientMessageEvent    |                  |xclient          |
| 34 |MappingNotify    |XMappingEvent          |                  |xmapping         |
+----+-----------------+-----------------------+------------------+-----------------+
         Tabla 4.1: Tipos de eventos, estructuras de eventos y campos XEvent

4.3 Cola de eventos

El servidor envía eventos a los clientes de forma asíncrona. Las estructuras de eventos que envía el servidor se colocan en la cola de eventos, de donde el cliente puede coger cada evento cuando lo necesite. Hay muchas clases de funciones de la Xlib para obtener los eventos de la cola de eventos

4.4 Obtención de la información de los eventos

Se puede obtener información detallada a partir de los campos de la estructura de evento. Para acceder a cada campo de una estructura de evento es necesario conocer el nombre de la estructura de evento y los nombres de los campos de la estructura.

Puede ser difícil recordar cada uno de los 29 tipos de estructuras de evento que pueden obtenerse de la cola de eventos, por eso la Xlib define la unión XEvent, que incluye las 29 estructuras de evento, y que permite que se reciban todos los tipos de estructuras de evento como un XEvent.

La unión XEvent incluye un campo especial denominado type que indica de cúal de las 19 estructuras de evento se trata, incluyendo una estructura genérica que incluye los cinco campos comunes a todas las estructuras de evento denominada XAnyEvent.

A continuación se muestra la definición de la unión XEvent, de la estructura XAnyEvent y como ejemplo de una estructura de evento específica de XButtonEvent:

typedef union _XEvent {
    int type;                /* no debe cambiarse; primer elemento */
    XAnyEven xany;
    XKeyEvent xkey;
    XButtonEvent xbutton;
    XMotionEvent xmotion;
    XCrossingEvent xcrossing;
    XFocusChangeEvent xfocus;
    XExposeEvent xexpose;
    XGraphicsExposeEvent xgraphicsexpose;
    XNoExposeEvent xnoexpose;
    XVisibilityEvent xvisibility;
    XCreateWindowEvent xcreatewindow;
    XDestroyWindowEvent xdestroywindow;
    XUnmapEvent xunmap;
    XMapEvent xmap;
    XMapRequestEvent xmaprequest;
    XReparentEvent xreparent;
    XConfigureEvent xconfigure;
    XGravityEvent xgravity;
    XResizeRequestEvent xresizerequest;
    XConfigureRequestEvent xconfigurerequest;
    XCirculateEvent xcirculate;
    XCirculateRequestEvent xcirculaterequest;
    XPropertyEvent xproperty;
    XSelectionClearEvent xselectionclear;
    XSelectionRequestEvent xselectionrequest;
    XSelectionEvent xselection;
    XColormapEvent xcolormap;
    XClientMessageEvent xclient;
    XMappingEvent xmapping;
    XErrorEvent xerror;
    XKeymapEvent xkeymap;
    long pad[24];
} XEvent;
typedef struct {
    int type;
    unsigned long serial;   /* no. de la última petición atendida por el servidor */
    Bool send_event;        /* verdadero si procede de una petición SendEvent */
    Display *display;       /* Display donde ocurrió el evento */
    Window window;          /* ventana del evento */
} XAnyEvent;
typedef struct {
    int type;               /* tipo del evento */
    unsigned long serial;   /* no. de la última petición atendida por el servidor */
    Bool send_event;        /* verdadero si procede de una petición SendEvent */
    Display *display;       /* Display donde ocurrió el evento */
    Window window;          /* ventana del evento */
    Window root;            /* ventana raíz del evento */
    Window subwindow;       /* ventana hija */
    Time time;              /* milisegundos */
    int x, y;               /* coordenadas del puntero en la ventana del evento */
    int x_root, y_root;     /* coordenadas relativas a la ventana raíz */
    unsigned int state;     /* máscara de botón o tecla */
    unsigned int button;    /* detalle */
    Bool same_screen;       /* indicador de misma pantalla? */
} XButtonEvent;
typedef XButtonEvent XButtonPressedEvent;
typedef XButtonEvent XButtonReleasedEvent;

Por ejemplo, si se declara la variable evento de tipo XEvent, se pueden usar las siguientes expresiones para acceder a cada miembro:

evento.type
evento.xany.serial
evento.xany.send_event
evento.xany.display
evento.xany.window

Si se quiere acceder a los campos exclusivos de cada tipo de evento es necesario especificar el campo de la unión correspondiente. por ejemplo:

evento.xbutton.x
evento.xbutton.y
evento.xbutton.state

La relación entre los tipos de eventos y el correspondiente campo de XEvent puede verse en la tabla 4.1.

4.5 Selección de eventos mediante máscara

Si se enviaran a los clientes todos los eventos que se producen, éstos estarían demasiado ocupados procesándolos o podrían no saber como manejarlos. La Xlib proporciona un mecanismo para que sólo los tipos de eventos requeridos se envíen a los clientes. De ésta forma el cliente decide qué eventos va a manejar por su cuenta y se lo comunica al servidor mediante una máscara de bits.

Cuando un evento se produce, el servidor comprueba la máscara de eventos del cliente para determinar si el cliente aceptará el evento. Este método permite al cliente recibir sólo los eventos que quiera gestionar, filtrando aquellos no solicitados explícitamente.

La tabla 4.2 muestra la relación entre los tipos de eventos y las máscaras de eventos. Como puede verse, la relación entre máscara de evento y tipo de evento no es siempre 1:1, sino que puede ser 1:m o m:1

+-----------------+-----------------------------------------------------------+
|Tipos de eventos |Máscaras de eventos                                        |
+-----------------+-----------------------------------------------------------+
|KeyPress         |KeyPressMask                                               |
|KeyRelease       |KeyReleaseMask                                             |
|ButtonPress      |ButtonPressMask                                            |
|ButtonRelease    |ButtonReleaseMask                                          |
|MotionNotify     |PointerMotionMask, Button1MotionMask, Button2MotionMask,   |
|                 |Button3MotionMask, Button4MotionMask, Button5MotionMask,   |
|                 |ButtonMotionMask                                           |
|EnterNotify      |EnterWindowMask                                            |
|LeaveNotify      |LeaveWindowMask                                            |
|FocusIn          |FocusChangeMask                                            |
|FocusOut         |FocusChangeMask                                            |
|KeymapNotify     |KeymapStateMask                                            |
|Expose           |ExposureMask                                               |
|VisibilityNotify |VisibilityChangeMask                                       |
|CreateNotify     |SubstructureNotifyMask                                     |
|DestroyNotify    |StructureNotifyMask, SubstructureNotifyMask                |
|UnmapNotify      |StructureNotifyMask, SubstructureNotifyMask                |
|MapNotify        |StructureNotifyMask, SubstructureNotifyMask                |
|MapRequest       |SubstructureRedirectMask                                   |
|ReparentNotify   |StructureNotifyMask, SubstructureNotifyMask                |
|ConfigureNotify  |StructureNotifyMask, SubstructureNotifyMask                |
|ConfigureRequest |SubstructureRedirectMask                                   |
|GravityNotify    |StructureNotifyMask, SubstructureNotifyMask                |
|ResizeRequest    |ResizedRedirectMask                                        |
|CirculateNotify  |StructureNotifyMask, SubstructureNotifyMask                |
|CirculateRequest |SubstructureRedirectmask                                   |
|PropertyNotify   |PropertyChangeMask                                         |
|ColorMapNotify   |ColormapChangeMask                                         |
|GraphicExpose    |*                                                          |
|NoExpose         |*                                                          |
|SelectionClear   |*                                                          |
|SelectionRequest |*                                                          |
|SelectionNotify  |*                                                          |
|ClientMessage    |*                                                          |
|MappingNotify    |*                                                          |
|*                |PointerMotionHintMask                                      |
|*                |OwnerGrabButtonMask                                        |
+-----------------+-----------------------------------------------------------+
              Tabla 4.2: Tipos de eventos y máscaras de eventos

Por ejemplo, si un cliente especifica la máscara StructureNotifyMask se le notificará de los siguientes eventos cuando se produzcan: DestroyNotify, UnmapNotify, MapNotify, ReparentNotify, ConfigureNotify, GravityNotify y CirculateNotify. Si por el contrario, el cliente especifica las máscaras ButtonMotion1Mask o ButtonMotion2Mask sólo se le notificará del evento MotionNotifyEvent.

Los eventos y máscaras de eventos marcados con un asterisco tienen un comportamiento especial que se describirá más adelante.

4.6 Ventanas de eventos

Cada evento se considera que ocurre en una ventana determinada. Cuando un cliente especifica las máscaras de eventos lo hace para una ventana específica, a la que se denomina ventana de eventos.

4.7 Propagación de eventos

Algunas veces los eventos ocurren en las ventanas hijas de la ventana de eventos. Cuando ésto ocurre entra en acción el mecanismo denominado propagación de eventos, que hace que se reenvíe el evento a la ventana madre. A la ventana que tiene el cursor, y que por tanto es la única que puede provocar eventos se la denomina ventana fuente.

La propagación de eventos se lleva a cabo si se dan las siguientes condiciones:

El proceso se repite subiendo por la jerarquía de ventanas hasta llegar a la ventana raíz. Si la ventana raíz no quiere el evento, se descarta.

4.8 Llamada Xlib: seleccionar eventos

XSelectInput( Display *display, Window ventana, long mascaraEventos )

La ventana específica la ventana a la que se refieren los eventos. La mascaraEventos es la máscara de eventos, en la que se pueden especificar varios usando la operación OR. Ejemplos:

XSelectInput( display, w1, ButtonPressMask | ButtonReleaseMask );
XSelectInput( display, w2, EnterWindowMask | LeaveWindowMask );

4.9 Obtención de eventos de la cola de eventos

Para obtener los eventos de la cola de eventos se pueden usar 11 llamadas a la Xlib. Estas 11 llamadas pueden clasificarse según las condiciones que se impongan a los eventos, que sean bloqueantes o no y extraigan el evento de la cola o que sólo lo lean.

+-----------------------------+-------------------------------------+------------+
| Condiciones de recuperación |  Funciones de obtención de eventos  |  Funciones |
|   de la cola de eventos     +-------------+-----------------------+   de sólo  |
|                             | Bloqueantes |    No bloqueantes     |   lectura  |
+-----------------------------+-------------+-----------------------+------------+
|Ninguna condición            |  XNextEvent |                       | XPeekEvent |
|(cualquier ventana)          |             |                       |            |
+-----------------------------+-------------+-----------------------+------------+
|Máscara de evento            |  XMaskEvent |    XCheckMaskEvent    |            |
+-----------------------------+-------------+-----------------------+------------+
|Ventana y máscara de evento  | XWindowEvent|   XCheckWindowEvent   |            |
+-----------------------------+-------------+-----------------------+------------+
|Tipo de evento               |             |   XCheckTypedEvent    |            |
+-----------------------------+-------------+-----------------------+------------+
|Ventana y tipo de evento     |             |XCheckTypedWindowEvent |            |
+-----------------------------+-------------+-----------------------+------------+
|Condición definida por       |  XIfEvent   |     XCheckIfEvent     |XPeekIfEvent|
|el usuario                   |             |                       |            |
+-----------------------------+-------------+-----------------------+------------+
                  Tabla 4.3: Funciones de obtención de eventos

Hay seis clases de condiciones que se pueden usar para seleccionar los eventos de la cola de eventos. Son las siguientes:

  1. Ninguna condición: se recupera el primer evento de la cola.
  2. Máscara de eventos: se recupera el primer evento que coincida con la máscara especificada.
  3. Ventana y máscara de eventos: se recupera el primer evento que coincida con la ventana y con la máscara especificadas.
  4. Tipo de evento: se recupera el primer evento que coincida con el tipo especificado.
  5. Ventana y tipo de evento: se recupera el primer evento que coincida con la ventana y con el tipo especificado.
  6. Función definida por el usuario: se recupera el primer evento que coincida con el predicado definido por el usuario.

Hay dos opciones para el caso en que no haya eventos que satisfagan la condición especificada:

  1. Vaciar el búffer de peticiones y esperar hasta que llegue un evento (bloqueante)
  2. Regresar inmediatamente sin esperar (no bloqueante)

Otras opciones son:

  1. Sacar el evento de la cola de eventos después de recuperarlo.
  2. No sacarlo de la cola, sino solamente leerlo.

4.10 Llamadas Xlib: recuperación de eventos

XNextEvent( Display *display, XEvent *evento )
XMaskEvent( Display *display, long mascaraEventos, XEvent *evento )
XWindowEvent( Display *display, Window ventana,
              long mascaraEventos, XEvent *evento )
XCheckMaskEvent( Display *display, long mascaraEventos, XEvent *evento )
XCheckWindowEvent( Display *display, Window ventana,
                   long mascaraEventos, XEvent *evento )
XCheckTypeEvent( Display *display, int tipoEvento, XEvent *evento )
XCheckTypedWindowEvent( Display *display, Window ventana,
                        int tipoEvento, XEvent *evento )

En todas las funciones, evento es un puntero a una unión XEvent donde se almacena el evento recuperado.

4.11 Programación típica de eventos

Un programa que maneje eventos tiene casi siempre la misma estructura, que se muestra en la ilustración 4.2:

+----------------------------------------------------------+
|      1   Crear una ventana         XCreateSimpleWindow   |
|                 |                                        |
|                 V                                        |
|      2   Seleccionar eventos       XSelectInput          |
|                 |                                        |
|                 V                                        |
|      3   Mapear la ventana         XMapWindow            |
|                 |                                        |
|                 V                                        |
| +--> 4   Obtener los eventos       XNextEvent            |
| |               |                                        |
| |               V                                        |
| |    5   Procesar los eventos                            |
| |        dependiendo de su tipo                          |
| |               |                                        |
| +---------------+                                        |
|  Bucle de eventos                                        |
+----------------------------------------------------------+
 Ilu. 4.2: Estructura típica de un programa que usa eventos

4.12 Ejemplo: problema

Resolver el siguiente problema:

  1. Crear una ventana de 500 por 400 píxels
  2. Cuando el botón del ratón esté pulsado dibujar la letra "A" en la posición del cursor
  3. Cuando se suelte el botón dibujar la letra "B" en la posición del cursor

4.13 Ejemplo: solución


#include <X11/Xlib.h>
#include <X11/Xutil.h>
 
main()
    {
    Display *d;
    Window w;
    Font f;
    GC gc;
    XEvent e;
    int pantalla;
    unsigned long negro, blanco;
 
    d = XOpenDisplay( NULL );
    f = XLoadFont( d, "9x15" );
    pantalla = DefaultScreen( d );
    blanco   = WhitePixel( d, pantalla );
    negro    = BlackPixel( d, pantalla );
 
    w = XCreateSimpleWindow( d, DefaultRootWindow( d ),
                             350, 0, 500, 400, 2, negro, blanco );
 
    XSelectInput( d, w, ButtonPressMask | ButtonReleaseMask | KeyPressMask );
    XMapWindow( d, w );
 
    gc = XCreateGC( d, w, 0, NULL );
    XSetFont( d, gc, f );
 
    while( 1 )
        {
        XNextEvent( d, &e );
        switch( e.type )
            {
            case ButtonPress:
                XDrawString( d, w, gc, e.xbutton.x, e.xbutton.y, "A", 1 );
                break;
            case ButtonRelease:
                XDrawString( d, w, gc, e.xbutton.x, e.xbutton.y, "B", 1 );
                break;
            case KeyPress:
                exit( 0 );
            }
         }
     }


Previous Next Table of Contents