Department of Engineering

IT Services

Events: Input from a Window

If a key on the server keyboard is pressed or part of a window is uncovered the server can inform the client which is effected by sending events. Events are usually delivered to windows and each window has to tell the server which events it is interested in. It does this using the XSelectInput call. If an event hasn't been selected then it will either be discarded or be passed on to another window, depending on how the window's do_not_propagate_mask has been set up.

It is possible for a window to hijack all events from the keyboard or the mouse. This is not to be encouraged and has been relegated to a later chapter.

These events are queued by the window system and subroutines are listed below which show how to ask if events are waiting for you and remove them from the queue. In order to find out what an event means it is necessary to be able to read the event structure. An XEvent structure always has type as the first entry. This uniquely identifies what kind of event it is. The second entry is always a pointer to the display the event was read from. The third entry is always a window of one type or another, (except for keymap events, which have no window.) The pointer to the generic event must be cast before use to access any other information in the structure.

<X11/Xlib.h> contains all the event structures with enough explanation for all but FocusIn, FocusOut, EnterNotify and LeaveNotify events. For precise details of these, you should consult the manual. XEvent is the union of all Event Structures, one of which is:-

typedef struct {
        int type;               /* of event */
        Display *display;       /* Display the event was read from */
        Window window;          /* window it is reported relative to */
        Window root;            /* root window that the event occured on */
        Window subwindow;       /* child window */
        unsigned long time;     /* milliseconds */
        int x, y;               /* pointer coordinates in event window */
        int x_root, y_root;     /* coordinates relative to root */
        unsigned int state;     /* key or button mask */
        unsigned int keycode;   /* detail */
        Bool same_screen;       /* same screen flag */
} XKeyEvent;

typedef XKeyEvent XKeyPressedEvent;
typedef XKeyEvent XKeyReleasedEvent;

Another useful event deals with button presses.

typedef struct {
        int type;               /* of event */
        unsigned long serial;   /* # of last request processed by server */
        Bool send_event;        /* true if this came from a SendEvent request */
        Display *display;       /* Display the event was read from */
        Window window;          /* "event" window it is reported relative to */
        Window root;            /* root window that the event occured on */
        Window subwindow;       /* child window */
        Time time;              /* milliseconds */
        int x, y;               /* pointer x, y coordinates in event window */
        int x_root, y_root;     /* coordinates relative to root */
        unsigned int state;     /* key or button mask */
        unsigned int button;    /* detail */
        Bool same_screen;       /* same screen flag */
} XButtonEvent;
typedef XButtonEvent XButtonPressedEvent;
typedef XButtonEvent XButtonReleasedEvent;

Event types

There now follows a complete list of events

tabular338

The usual call to get events is the following.

XNextEvent(display,rep)      /* flush output then fill in struct with event */
    XEvent *rep;     /* RETURN next event removed from the queue 
                        If queue is empty it waits until an event arrives */

Note that the returned event might well need to be cast before its fields are accessed. If, for example, you declare XEvent event and find by looking at event's type field that it's a ButtonEvent then you can use C's union concept to correctly access the x field by doing event.xbutton.x.

XSelectInput(display,w, mask)/* declare which events you wish to receive */
    int mask;        /* event mask: each bit permits 1 event type*/

XFlush(display)              /* flush all output buffers */

XSync(display,discard)       /* flush output buffers & wait till all events */
    int discard;     /* have been delivered.  If discard <> 0 
                        throw all the events away. */


XPeekEvent(display,rep)      /* flush output then fill in struct with event */
    XEvent *rep;     /* RETURN next event but DONT remove from queue
                        if queue is empty it waits until an event arrives */

int XQLength(display)        /* returns the length of the waiting event
                                queue as an integer */

XPutBackEvent(display,event) /* push an event back unto the top of the queue */
    XEvent *event;   /* the event which you want to push back    */

XWindowEvent(display,w, mask, rep) /* flush bufs then look down the queue for*/
    long mask;       /* event which matches this event mask */
    XEvent *rep;     /* RETURN the first event of this sort found
                        If there's no suitable event, wait */

Bool XCheckWindowEvent(display,w, mask, rep) /* flush bufs then look down the 
                        queue for event which fits mask and window */
    long mask;
    XEvent *rep;     /* RETURN the first event of this sort found.
                        If there is no suitable event, 0 is returned */

XMaskEvent(display,mask, rep)/* flush buffers then look down the queue for   */
    int mask;        /* an event which matches this event mask   */
    XEvent *rep;     /* RETURN the first event of this sort found
                        If there's no suitable event, wait */

Bool XCheckMaskEvent(display,mask, rep)/* flush buffers then look down the queue for   */
    int mask;        /* an event which matches this event mask   */
    XEvent *rep;     /* RETURN the first event of this sort found
                        If there's no suitable event, 0 is returned */

int XPending(display) /* flush output & return no of events in input queue */

KeyBoard Encoding

Each key produces a fixed keycode in the range [7,255]. A list of keysyms is associated with each KeyCode. The keysyms take into account whether Shift keys etc have been pressed. The list of defined KeySyms is in <X11/keysym.h>. The mapping from keycodes to keysyms can be changed server-wide. From the command line you can use xmodmap -pk to see the current mapping. The mapping that matches the keysym to a string can be changed for just one client.

XLookUpString is used to interpret a key event, producing both a keysym and an ASCII string

To get the current mapping use

KeySym *XGetKeyBoardMapping(display,first_keycode,count,keysyms_per_keycode);
   int first_keycode;
   int count;         /* how many keycodes to check */
   int *keysyms_per_keycode;  /*RETURN*/

To change the mapping globally use

XChangeKeyBoardMapping(display,first_code,keysyms_per_code,keysyms,num_codes);
   KeySym *keysyms;

to change it locally use

    /* ALTER KEYBOARD MAPPING FOR A GIVEN KEYCODE                */
    /* WARNING : THIS MAY NOT BE IMPLEMENTED */
XRebindKeySym(display,keysym,list,mod_count,string,num_bytes)
    KeySym keysym;   /* the keycode we wish to rebind  */
    KeySym *list;           /*these are used as modifiers*/
    int mod_count;
    char *string;       /* string we wish to associate with it      */
    int nun_bytes;      /* number of bytes in the string            */

To see which keycodes are to be used as modifiers, this structure is needed.

typedef struct{
   int lock;
   int shift_a,  shift_b;
   int control_a,control_b;
   int mod1_a,    mod1_b;
   int mod2_a,    mod2_b;
   int mod3_a,    mod3_b;
   int mod4_a,    mod4_b;
   int mod5_a,    mod5_b:
}XModifierKeys;


GetModifierMapping(display,modifier_keys)
   ModifierKeys *modifier_keys;  /*RETURN*/

SetModifierMapping(display,modifier_keys)
   ModifierKeys *modifier_keys;

    /* RETURN A CHAR STRING GIVEN AN ARRAY OF {KeyPressedEvent}s */
int XLookupString(event,buffer,num_bytes,keysym,status); nbytes) */
    XKeyEvent *event; /* array of events to be interpreted*/
    char *buffer;
    int numbytes;      /* number of bytes in the char string      */
    KeySym *keysym;        /*RETURN*/
    XComposeStatus *status;
    /* default mapping is in /usr/lib/keymap.txt but user may    */
    /* specify his own in ~/.Xkeymap - see keycomp(1)            */

Examples

This uses pixmaps, some graphics routines, and events.  

#include  <X11/cursorfont.h> 
#include <stdio.h> 
#include <X11/Xlib.h> 
      Display *display; 
      Window  win1; 
      XSetWindowAttributes attributes; 
      XFontStruct *fontinfo; 
      GC gr_context1; 
      XArc arcs[10]; 
      Pixmap pixmap; 
      Visual *visual; 
      int screen;
      int depth; 
      int i; 
 main (argc, argv) 
 char   *argv[]; 
 int     argc; 
 { 
      XGCValues gr_values; 
      XEvent event; 
  
      setbuf (stdout, NULL); 
      setbuf (stderr, NULL); 
      display = XOpenDisplay(NULL); 
      screen = DefaultScreen(display);
      visual = DefaultVisual(display,screen); 
      depth  = DefaultDepth(display,screen); 
      attributes.background_pixel      = XWhitePixel(display,screen); 
      attributes.border_pixel          = XBlackPixel(display,screen); 
      attributes.override_redirect     = 0; 
  
      for(i=0;i<10;i++){ 
      	   arcs[i].x = 100;arcs[i].y = 50; 
           arcs[i].width = 100;arcs[i].height = 50; 
      } 
      for(i=0;i<5;i++){ 
           arcs[i].angle1 = 72*64*i; 
           arcs[i].angle2 = 35*64; 
      } 
      for(i=5;i<10;i++){ 
           arcs[i].angle1 = 72*64*i + 36*64; 
           arcs[i].angle2 = 35*64; 
      } 
  
      win1= XCreateWindow(display, XRootWindow(display,screen),
                   200,200, 300,200,5, depth, InputOutput, visual,
                   CWBackPixel | CWBorderPixel | CWOverrideRedirect,&attributes);
  
      XSelectInput(display,win1,ExposureMask | ButtonPressMask | KeyPressMask);
      pixmap = XCreatePixmap(display,win1,200,100,depth);   
      fontinfo = XLoadQueryFont(display,"6x10"); 
      gr_values.font =   fontinfo->fid; 
      gr_values.function =   GXcopy; 
      gr_values.plane_mask = AllPlanes; 
      gr_values.foreground = BlackPixel(display,screen); 
      gr_values.background = WhitePixel(display,screen); 
      gr_context1=XCreateGC(display,win1, 
              GCFont | GCFunction | GCPlaneMask | GCForeground | GCBackground, 
              &gr_values); 
  
      XDefineCursor(display,win1,XCreateFontCursor(display,XC_heart)); 
      XMapWindow(display,win1); 
  
      do{ 
        XNextEvent(display,&event); 
        if (event.type == Expose){ 
              draw_ellipse(); 
              XCopyArea(display,win1,pixmap,gr_context1,50,25,200,100,0,0); 
              XSetFunction(display,gr_context1,GXinvert); 
              XDrawImageString(display,pixmap,gr_context1,80,45,"pixmap",6); 
              XDrawImageString(display,pixmap,gr_context1,90,60,"copy",4); 
              XSetFunction(display,gr_context1,GXcopy); 
              XDrawString(display,win1,gr_context1,10,20,
                  "Press a key in this window",26); 
        } 
      }while  (event.type !=KeyPress); 
  
      XCopyArea(display,pixmap,win1,gr_context1,0,0,200,100,100,125); 
      XDrawString(display,win1,gr_context1,10,32,
                  "Now press a key to exit",23); 
      XFlush(display); 
  
      do{ 
        XNextEvent(display,&event); 
      }while  (event.type !=KeyPress); 
  
      printf("closing display\n"); 
      XCloseDisplay(display); 
 } 
  
 draw_ellipse() 
 { 
      XSetArcMode(display,gr_context1,ArcPieSlice); 
      XFillArcs(display,win1,gr_context1,arcs,5); 
      XSetArcMode(display,gr_context1,ArcChord); 
      XFillArcs(display,win1,gr_context1,arcs+5,5); 
 }

This example deals with keyboard input.  

#include  <X11/cursorfont.h> 
#include <stdio.h> 
#include <X11/Xlib.h> 
#include<X11/keysym.h>
 char workstation[] = {""}; 
 char str[80];
      Display *display; 
      Window  win1; 
      XSetWindowAttributes attributes; 
      XFontStruct *fontinfo; 
      GC gr_context1; 
      Visual *visual; 
      int screen;
      int depth; 

 main (argc, argv) 
 char   *argv[]; 
 int     argc; 
 { 
      XGCValues gr_values; 
      XEvent event; 
  
      setbuf (stdout, NULL); 
      setbuf (stderr, NULL); 
      display = XOpenDisplay(workstation); 
      screen = DefaultScreen(display);
      visual = DefaultVisual(display,screen); 
      depth  = DefaultDepth(display,screen); 
      attributes.background_pixel      = XWhitePixel(display,screen); 
      attributes.border_pixel          = XBlackPixel(display,screen); 
      attributes.override_redirect     = 0; 
  
      win1=  XCreateWindow(display, XRootWindow(display,screen),
             200,200, 300,200,5, depth, InputOutput, visual,
             CWBackPixel | CWBorderPixel | CWOverrideRedirect,&attributes);
  
      XSelectInput(display,win1,ExposureMask | ButtonPressMask | KeyPressMask);
  
      fontinfo = XLoadQueryFont(display,"6x10"); 
      gr_values.font =   fontinfo->fid; 
      gr_values.function =   GXcopy; 
      gr_values.plane_mask = AllPlanes; 
      gr_values.foreground = BlackPixel(display,screen); 
      gr_values.background = WhitePixel(display,screen); 
      gr_context1=XCreateGC(display,win1, 
                  GCFont | GCFunction | GCPlaneMask | GCForeground | GCBackground,
                  &gr_values); 
  
      XDefineCursor(display,win1,XCreateFontCursor(display,XC_heart)); 
      XMapWindow(display,win1); 
      XFlush(display); 
  
      do{ 
          XNextEvent(display,&event); 
          switch(event.type){
          case ButtonPress:
            sprintf(str, "x=%d, y=%d", event.xbutton.x, event.xbutton.y);
            XDrawImageString(display,win1,gr_context1,event.xbutton.x, 
                event.xbutton.y, str, strlen(str));
            break;
          case KeyPress:
            deal_with_keys(&event);
            break;
          case Expose:
            XClearWindow(display,win1);
            XDrawImageString(display,win1,gr_context1,0,20,
                             "Press a key or Button. Use Break to exit", 40);
          }
      }while  (1); 
 } 
  
  
deal_with_keys(event)
XKeyEvent *event;
{
int count;
int buffer_size = 80;
char buffer[80];
KeySym keysym;
/* XComposeStatus compose; is not used, though it's in some books */
   count = XLookupString(event,buffer,buffer_size, &keysym);
   
   if ((keysym >= XK_space) && (keysym <= XK_asciitilde)){
      printf ("Ascii key:- ");
      if (event->state & ShiftMask)
             printf("(Shift) %s\n", buffer);
      else if (event->state & LockMask)
             printf("(Caps Lock) %s\n", buffer);
      else if (event->state & ControlMask)
             printf("(Control) %c\n", 'a'+ buffer[0]-1) ;
      else printf("%s\n", buffer) ;
   }
   else if ((keysym >= XK_Shift_L) && (keysym <= XK_Hyper_R)){
      printf ("modifier key:- ");
      switch (keysym){
      case XK_Shift_L: printf("Left Shift\n"); break;
      case XK_Shift_R: printf("Right Shift\n");break;
      case XK_Control_L: printf("Left Control\n");break;
      case XK_Control_R: printf("Right Control\n");	break;
      case XK_Caps_Lock: printf("Caps Lock\n");	break;
      case XK_Shift_Lock: printf("Shift Lock\n");break;
      case XK_Meta_L: printf("Left Meta\n");	break;
      case XK_Meta_R: printf("Right Meta\n");	break;

      }
    }
   else if ((keysym >= XK_Left) && (keysym <= XK_Down)){
      printf("Arrow Key:-");
      switch(keysym){
      case XK_Left: printf("Left\n");break;
      case XK_Up: printf("Up\n");break;
      case XK_Right: printf("Right\n");break;
      case XK_Down: printf("Down\n");break;	
      }
    }
   else if ((keysym >= XK_F1) && (keysym <= XK_F35)){
      printf ("function key %d pressed\n", keysym - XK_F1);

      if (buffer == NULL)
         printf("No matching string\n");
      else
         printf("matches <%s>\n",buffer);
   }

   else if ((keysym == XK_BackSpace) || (keysym == XK_Delete)){
      printf("Delete\n");
   }

   else if ((keysym >= XK_KP_0) && (keysym <= XK_KP_9)){
       printf("Number pad key %d\n", keysym -  XK_KP_0);
   }
   else if (keysym == XK_Break) {
        printf("closing display\n"); 
        XCloseDisplay(display); 
        exit (0);
   }else{
      printf("Not handled\n");
    }
}


/*
  if one wants to find out if another client has changed the key mappings,
  select MappingNotify and do
      XRefreshKeyboardMapping(Event *event);
  
 */