10

I'm Porting some code from Windows to XLib. In the windows code, I can force a redraw by calling InvalidateRect and then handling the corresponding WM_PAINT message. However, I am having trouble finding out how to do this in X11/XLib. I see there is an Expose message but not sure if that is the same thing.

If it matters, I need to do this to force the window to render at a certain frame rate for an OpenGL based program.

default
  • 2,637
  • 21
  • 44

2 Answers2

5

To expand slightly on the useful answers given by BЈовић,

With raw Xlib you can draw at any time in a single thread, because every Xlib function specifies the full display, window, and context. AFAIK, with multithreading all bets are off.

You also must have an Expose event handler, and select for those events, if you're in a desktop environment. And it won't hurt to have one even if you're writing a full screen program.

Most toolkits are not as flexible and only draw in a designated event handler (but much nicer to use in many other ways) and have some equivalent to the Windows InvalidateRect. In raw Xlib you get the same effect by sending yourself an Expose event. Doing so won't lead to any real performance problems and will make the code more understandable by other programmers, and easier to port, so you might as well.

There are also XClearArea and XClearWindow functions which will generate Expose events for you, but they first erase part/all with the background color, which might lead to flickering.

With OpenGL it gets a bit more complicated because you have to work with GLX as well. I have a very simple OpenGL/Xlib program online at http://cs.anu.edu.au/~hugh.fisher/3dteach/ which might be useful as an example.

Hugh Fisher
  • 2,321
  • 13
  • 8
  • 3
    Using `XClearArea(disp, win, 0, 0, 1, 1, true)` to clear a one pixel area is enough to cause an `Expose event` without adding flicker. You may want to follow the `XClearArea()` immediately with an `XFlush()`, if you don't see the `Expose` events right away. – Harvey Jan 25 '15 at 00:47
  • anu took down your example :( – ThorSummoner Jan 05 '20 at 23:11
4

You need to handle Expose events. This tutorial explains with an example how to handle Expose events :

#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
/*Linux users will need to add -ldl to the Makefile to compile 
 *this example.
 */
Display *dis;
Window win;
XEvent report;
GC green_gc;
XColor green_col;
Colormap colormap;
/*
Try changing the green[] = below to a different color.
The color can also be from /usr/X11R6/lib/X11/rgb.txt, such as RoyalBlue4.
A # (number sign) is only needed when using hexadecimal colors.
*/
char green[] = "#00FF00";

int main() {
    dis = XOpenDisplay(NULL);
    win = XCreateSimpleWindow(dis, RootWindow(dis, 0), 1, 1, 500, 500, 0, BlackPixel (dis, 0), BlackPixel(dis, 0));
    XMapWindow(dis, win);
    colormap = DefaultColormap(dis, 0);
    green_gc = XCreateGC(dis, win, 0, 0);
    XParseColor(dis, colormap, green, &green_col);
    XAllocColor(dis, colormap, &green_col);
    XSetForeground(dis, green_gc, green_col.pixel);

    XSelectInput(dis, win, ExposureMask | KeyPressMask | ButtonPressMask);

    XDrawRectangle(dis, win, green_gc, 1, 1, 497, 497);
    XDrawRectangle(dis, win, green_gc, 50, 50, 398, 398);
    XFlush(dis);

    while (1)  {
    XNextEvent(dis, &report);
        switch  (report.type) {
        case Expose:   
            fprintf(stdout, "I have been exposed.\n");
                XDrawRectangle(dis, win, green_gc, 1, 1, 497, 497);
                XDrawRectangle(dis, win, green_gc, 50, 50, 398, 398);
                XFlush(dis);
            break;
            case KeyPress:
        /*Close the program if q is pressed.*/
                if (XLookupKeysym(&report.xkey, 0) == XK_q) {
                exit(0);
                }
            break;
        }
    }

return 0;
}

I may have misunderstood the question. If you want to create Expose events in your application, you can create and set expose event, and send it using XSendEvent.

BЈовић
  • 62,405
  • 41
  • 173
  • 273
  • Ok. So in windows I call `InvalidateRect` which later triggers windows to send a `WM_PAINT` message, at which point I render my open gl scene. So in X11, I would call `XSendEvent` with the `Expose` message and then have an event loop that handles the `Expose` message and renders the open gl scene? – default Jun 10 '13 at 17:49
  • @pauld Not sure I follow you. X11 uses event system to notify application about various events. You tell it which events you want to handle. If you want to render your scene on expose events, then you need to add rendering code in that event handler. I don't see why would you generate expose event for your application. – BЈовић Jun 10 '13 at 18:41
  • 2
    Ok I think i understand. In windows, I can draw to the window _only_ during a `WM_PAINT` message. In X11, it appears I can draw to the window whenever I want to, not only in response to an `Expose` event. So in that case, I don't need to generate expose events, I can just draw when I'm ready. – default Jun 10 '13 at 19:10
  • 1
    @pauld You got that right. You need to handle x11 events, and update the image when needed (for example, when the window gets uncovered, or resized) – BЈовић Jun 10 '13 at 19:13