Running a module into a window

The modules seen so far draw in the root window only. It may be useful to make them run in a regular window as well. This pages tells how to create a window, and draw into it. Drawing in a window can be useful even for a xscreensaver module, as it allows people to see what it does without installing it. It is also good for the author, who can see how the module runs at different sizes (a window can be resized easily, while changing the size of the root window requires is slightly more complicated).

In order to run the module into a window, all is needed is to create a new window, and then to draw everything here instead of the root window. This second step is easy, as the functions to use to draw in a window, and in the root window, are exactly the same.

To create (open) a new window, the function to call is XCreateSimpleWindow. However, calling this function is not enough. Indeed, the window created by calling this function is not mapped into the screen, that is, is not visible. In order to make the window visible we need to call another function XMapRaised. This function issues a request to the X server to map the window (draw it into the screen). However, the X server does not guarantee that the window is mapped immediately. Before starting drawing into the window, the program should wait until the window is actually mapped.

Waiting until the window is mapped is the more complicated part of creating a window. The X server tells applications that windows are mapped by sending them events. An event is a sort of message that can be sent between the X server and applications, or between applications. Sending an event to an application does not cause it to run some procedure like signals do. On the contrary, events are simply stored into a queue, and applications are supposed to read them if they are interested into. The event queue is like a (snail mail) mailbox: letters are put there, but you have to go there to see if you have recieved something.

In practice, a program the wants to open a window has to:

  1. create the window with XCreateSimpleWindow;
    
        w = XCreateSimpleWindow (dpy, DefaultRootWindow (dpy),
                                 200, 200, 400, 400, 1,
                                 BlackPixel (dpy, DefaultScreen (dpy)),
                                 WhitePixel (dpy, DefaultScreen (dpy)));
    
  2. say to the X server that it is interested of receiving the events of mapping windows (the events that are generated whenever a window is mapped);
        XSelectInput (dpy, w, StructureNotifyMask);
    
  3. tell the X server to map the window;
        XMapRaised (dpy, w);
    
  4. wait until the window is mapped, that is, wait until an event that tells that the window is mapped is received
        do {
              XWindowEvent (dpy, w, StructureNotifyMask, &e);
            } while( e.type != MapNotify );
    

The variables involved into these code fragments are: w which is a Window, and e, which is of type XEvent *. In practice, what the program has to do is simply a sequence like this (here we use XStoreName to give a name the window):

  /* get the window to draw into */
  if( argc<1 )
    w = DefaultRootWindow (dpy);
  else { 
    /* create, name, and map window */
    w = XCreateSimpleWindow (dpy, DefaultRootWindow (dpy),
                             200, 200, 400, 400, 1,
                             BlackPixel (dpy, DefaultScreen (dpy)),
                             WhitePixel (dpy, DefaultScreen (dpy)));
    XStoreName (dpy, w, "This is a name");
    XSelectInput (dpy, w, StructureNotifyMask);
    XMapRaised (dpy, w);
    do {
      XWindowEvent (dpy, w, StructureNotifyMask, &e);
    } while( e.type != MapNotify );
  }

This check whether the program received any arguments. Depending on this, it either assign to w the identificator of the root window, or open a new window identify by w. At the end of this, w is a mapped window, that can be either the root window or a new window. From this point on, the difference does not matter any more, as the function for drawing into the root window and into the new window are exactly the same.

The complete module coloredwindow.c, besides the code above, is different from coloredsquares.c only because the drawing operations takes w as argument instead of root. For example, to draw the square:

      /* draw a square */
      XFillRectangle (dpy, w, g, random()%(wa.width-50),
                      random()%(wa.height-40), 50, 40);

Next: resizing windows.