Equivalent of "Invalidate Rect" / "WM_PAINT" in X11
Asked Answered
C

2

11

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.

Closer answered 10/6, 2013 at 15:55 Comment(6)
Also if anyone could suggest a great XLib reference i would really appreciate it. The XLib manuals on X.org don't seem to be much help to a beginner, at least not for me.Closer
Yes it is the same thing. I strongly advise against using raw Xlib. Use a modern toolkit like Qt, gtk or wX.Camel
In addition, though Expose will work, I advise calling your redraw function directly. It is easier and more straightforward.Camel
All I need is a simple display. All the drawing is done through opengl, and this app must run on embedded systems so i need it to be really light weight, hence xlib.Closer
Ok so Expose is similar to WM_PAINT, but how to I tell Xlib send an Expose message so I can update the window?Closer
You call XSendEvent.Camel
A
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.

Alodium answered 10/6, 2013 at 17:40 Comment(4)
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?Closer
@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.Barter
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.Closer
@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)Barter
C
7

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.

Cano answered 11/6, 2013 at 2:43 Comment(2)
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.Octennial
anu took down your example :(Patronage
A
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.

Alodium answered 10/6, 2013 at 17:40 Comment(4)
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?Closer
@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.Barter
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.Closer
@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)Barter

© 2022 - 2024 — McMap. All rights reserved.