5

I'm doing some programming with cairo and xlib in C++, my code is listed below.

I had some flickering issues but by modifying my code, it seems works good now generally.
But when the window is resized at a small size (about 600x450 on my laptop), it is still flickering.
How can I fix it?

#include <cairo.h>
#include <cairo-xlib.h>
#include <string>
#include <cstdio>

using namespace std;

int main (int argc, char *argv[])
{
    cairo_surface_t *surface;
    cairo_t *cr;

    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 800, 600);
    cr = cairo_create (surface);
    /* Examples are in 1.0 x
     * 1.0 coordinate space */
    cairo_scale (cr, 800, 600);

    cairo_set_source_rgb (cr, 1.0, 0, 0);
    cairo_rectangle (cr, 0, 0, 1.0, 1.0);
    cairo_fill(cr);
    cairo_destroy(cr);

    Display *display = XOpenDisplay(NULL);
    int default_scr = DefaultScreen(display);
    Visual *visual = DefaultVisual(display, default_scr);
    Window root_win = RootWindow(display, default_scr);
    Drawable drawable = XCreateSimpleWindow(display, root_win, 0, 0, 800, 600, 0,
            BlackPixel(display, default_scr), WhitePixel(display, default_scr));
    cairo_surface_t *x11_sf = cairo_xlib_surface_create(display, drawable, visual, 800, 600);

    XSelectInput(display, drawable, ExposureMask | StructureNotifyMask);
    XMapWindow(display, drawable);
    XFlush(display);
    XSync(display, default_scr);
    Atom wmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", False);
    XSetWMProtocols(display, drawable, &wmDeleteMessage, 1);

    int height;
    int width;
    XEvent event;
    bool running = true;
    while (running)
    {
        XNextEvent(display, &event);
        switch (event.type)
        {
        case Expose:
            {
                cairo_t *x11_cr = cairo_create(x11_sf);
                cairo_scale(x11_cr, double(width) / 800, double(height) / 600);
                cairo_set_source_surface(x11_cr, surface, 0, 0);
                cairo_paint(x11_cr);
                cairo_destroy (x11_cr);
            }
            break;
        case ClientMessage:
            if (event.xclient.data.l[0] == wmDeleteMessage)
            {
                running = false;
            }
            break;
        case ConfigureNotify:
            width = event.xconfigure.width;
            height = event.xconfigure.height;
            cairo_xlib_surface_set_size(x11_sf, width, height);
            break;
        }
    }

    cairo_surface_destroy (surface);

    return 0;
}

I use cairo_xlib_surface to draw a window, which has a whole red background color. I tried to catch XExpose event and redraw it. But sometimes the redraw result gets strange.

#include <cairo.h>
#include <cairo-xlib.h>
#include <string>
#include <cstdio>

using namespace std;

int main (int argc, char *argv[])
{
    cairo_surface_t *surface;
    cairo_t *cr;

    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 800, 600);
    cr = cairo_create (surface);
    /* Examples are in 1.0 x 1.0 coordinate space */
    cairo_scale (cr, 800, 600);

    /* Drawing code goes here */
    cairo_set_source_rgb (cr, 1.0, 0, 0);
    cairo_rectangle (cr, 0, 0, 1.0, 1.0);
    cairo_fill(cr);

    Display *display = XOpenDisplay(NULL);
    int default_scr = DefaultScreen(display);
    Visual *visual = DefaultVisual(display, default_scr);
    Window root_win = RootWindow(display, default_scr);
    Drawable drawable = XCreateSimpleWindow(display, root_win, 0, 0, 800, 600, 0,
            BlackPixel(display, default_scr), WhitePixel(display, default_scr));

    XSelectInput(display, drawable, ExposureMask);
    XMapWindow(display, drawable);
    XFlush(display);
    XSync(display, default_scr);


    int height;
    int width;
    XEvent event;
    while (1)
    {
        XNextEvent(display, &event);
        switch (event.type)
        {
        case Expose:
            {
                width = event.xexpose.width;
                height = event.xexpose.height;
                cairo_surface_t *x11_sf = cairo_xlib_surface_create(display, drawable, visual, width, height);
                cairo_t *x11_cr = cairo_create(x11_sf);
                cairo_scale(x11_cr, double(width) / 800, double(height) / 600);
                cairo_set_source_surface(x11_cr, surface, 0, 0);
                cairo_paint(x11_cr);
                cairo_destroy (x11_cr);
                cairo_surface_destroy (x11_sf);
            }
            break;
        }
    }


    /* Write output and clean up */
    cairo_destroy (cr);
    cairo_surface_destroy (surface);

    return 0;
}

When I resize the window, sometimes I get the result like this: The result window

It seems it hasn't finished to draw the red background.

Could someone tell me why this occurs and how to avoid it?

If someone want to compile it under Linux, could use: g++ \pkg-config --cflags --libs x11 cairo\ src.cpp

Thanks!

Léolol DB
  • 313
  • 3
  • 15
byhc
  • 173
  • 8
  • Is it on purpose that you give the width and height of the exposed are as the size of your window to cairo_xlib_surface_create()? Why don't you use the window's actual size for that? In your case that would initially be 800x600 and when your window is resized, well, you have to do catch the ConfigureNotify which tells you about it and use cairo_xlib_surface_set_size(). Is this info actually what you are asking for? If not, I don't get your question. – Uli Schlachter Dec 21 '15 at 14:38
  • Thanks for your reply. Your suggestion realy works. But the redrawing process is still filkering when the window size is small(under 600x450). I modified my code. Did I miss some other point? THX – byhc Dec 30 '15 at 14:57
  • I have tested some cases, found that when the 'surface' I created is 100x100, the flickering disappeared. However, then the surface is 1x1, the result x11 window had a strange background. – byhc Dec 30 '15 at 16:34
  • 1
    Use XCreateWindow instead of XCreateSimpleWindow. I guess your problem is that you are using a background color of white for your window through this mechanism. XCreateWindow(display, root_win, 0, 0, 800, 600, 0, 0, CopyFromParent, CopyFromParent, 0, NULL); should work. – Uli Schlachter Jan 02 '16 at 14:13
  • 2
    XCreateWindow works. Thank you! – byhc Jan 03 '16 at 15:57

1 Answers1

2

The issue is caused by the fact you're filling a red area on a white background to make a red background, that's not the most efficient way of doing it.

You should rather create the window with the background's color defined as red instead of white!

So, to do it, in XCreateSimpleWindow()'s background argument (the last one), you should put 0xFF0000, it's the hexadecimal value for red in a 24-bit color space, but you may need to change it if you use different color spaces. The line would look like this:

XCreateSimpleWindow(display, root_win, 0, 0, 800, 600, 0,
            BlackPixel(display, default_scr), WhitePixel(display, default_scr));
Léolol DB
  • 313
  • 3
  • 15