Reputation: 1008
I am trying to learn how to use Cairo 2D drawing library with xlib surfaces.
I wrote a little test program that allows creating multiple windows. Each function may have a custom paint() function that is called regularly to add some graphics content to the window, or redraw it completely if desired. There is also an option to define mouse and key listener. The main routine checks for X events (to delegate them to mouse and key listener) and for timeout for periodic call of those paint() functions.
I tried with the 1.14.6 version of Cairo (that is currently available as package in Ubuntu 16.04), and the latest 1.15.12, but the results are the same.
The expected behavior of this demo is to open 3 windows. One will have random rectangles being added, another one random texts, and the third random circles.
In addition, clicking into windows should produce lines (connecting to mouse click, or randomly), and using arrow keys should draw a red line in the window with circles.
The circles and text seem to show up regularly as expected. All three windows should have white background, but two of them are black. And the worst, the window with rectangles does not get updated much (and it does not matter if it is the first window created or not, it is always the rectangles that do not show up properly).
They are only shown when the focus changes to or from that window - then the remaining rectangles that should have been drawn meanwhile suddenly appear.
I am calling cairo_surface_flush() on the surface of each window after adding any content, but that does not help. I also tried posting XEvents to that window of various kind (such as focus), they arrive, but rectangles do not show up.
Furthermore, even though drawing lines with mouse works fine, drawing line with key arrows suffers from the same problem - it is drawn, but not shown properly.
I am obviously wrong in some of my assumptions about what this library can do, but I am not sure where.
It seems that there are some two competing versions of drawing being shown, since it happens sometimes that one or two rectangles, or pieces of the red line are flashing. Some kind of strange buffering, caching? It may just be some bug in my program, I do not know.
Another observation - the black background is because drawing white background happens before the window is shown, and thus those cairo_paint calls are somehow discarded. I do not know how to make the window appear earlier, it seems it appears only after some later changes on the screen.
I am stuck on this after a couple of desperate days, could you help me out at least in part, please?
The program is here: test_cairo.c
An example screenshot (with a broken red line drawn by keys, and rectangles not showing up properly): test_cairo.png
To compile (on Ubuntu 16.04 or similar system):
gcc -o test_cairo test_cairo.c -I/usr/include/cairo -lX11 -lcairo
Upvotes: 0
Views: 891
Reputation: 9867
X11 does not retain window content for you. When you get an Expose event, you have to repaint the area described by that event completely.
All three windows should have white background, but two of them are black.
You create your window with XCreateSimpleWindow
, so their background attribute is set to black. The X11 server will fill exposed areas with black for you before sending an expose event. Since you do not tell cairo to draw a white background, the black stays.
Try this:
--- test_cairo.c.orig 2018-07-28 09:53:10.000000000 +0200
+++ test_cairo.c 2018-07-29 10:52:43.268867754 +0200
@@ -63,6 +63,7 @@ static gui_mouse_callback mouse_callback
static cairo_t *windows[MAX_GUI_WINDOWS_COUNT];
static cairo_surface_t *surfaces[MAX_GUI_WINDOWS_COUNT];
+static cairo_surface_t *real_surfaces[MAX_GUI_WINDOWS_COUNT];
static Window x11windows[MAX_GUI_WINDOWS_COUNT];
static char *window_names[MAX_GUI_WINDOWS_COUNT];
@@ -79,7 +80,12 @@ long long usec()
void repaint_window(int window_handle)
{
draw_callbacks[window_handle](windows[window_handle]);
- cairo_surface_flush(surfaces[window_handle]);
+
+ cairo_t *cr = cairo_create(real_surfaces[window_handle]);
+ cairo_set_source_surface(cr, surfaces[window_handle], 0, 0);
+ cairo_paint(cr);
+ cairo_destroy(cr);
+ cairo_surface_flush(real_surfaces[window_handle]);
}
int gui_cairo_check_event(int *xclick, int *yclick, int *win)
@@ -149,7 +155,6 @@ void draw_windows_title(int window_handl
sprintf(fullname, "Mikes - %d - [%s]", window_handle, context_names[current_context]);
else
sprintf(fullname, "Mikes - %s - [%s]", window_names[window_handle], context_names[current_context]);
- cairo_surface_flush(surfaces[window_handle]);
XStoreName(dsp, x11windows[window_handle], fullname);
}
@@ -179,20 +184,17 @@ int gui_open_window(gui_draw_callback pa
}
if (window_handle < 0) return -1;
- surfaces[window_handle] = gui_cairo_create_x11_surface(&width, &height, window_handle);
+ real_surfaces[window_handle] = gui_cairo_create_x11_surface(&width, &height, window_handle);
+ surfaces[window_handle] = cairo_surface_create_similar(real_surfaces[window_handle], CAIRO_CONTENT_COLOR, width, height);
windows[window_handle] = cairo_create(surfaces[window_handle]);
mouse_callbacks[window_handle] = 0;
draw_callbacks[window_handle] = paint;
window_update_periods[window_handle] = update_period_in_ms;
window_names[window_handle] = 0;
-
- cairo_surface_flush(surfaces[window_handle]);
cairo_set_source_rgb(windows[window_handle], 1, 1, 1);
cairo_paint(windows[window_handle]);
-
- cairo_surface_flush(surfaces[window_handle]);
draw_callbacks[window_handle](windows[window_handle]);
@@ -201,7 +203,6 @@ int gui_open_window(gui_draw_callback pa
else next_window_update[window_handle] = 0;
draw_windows_title(window_handle);
- cairo_surface_flush(surfaces[window_handle]);
window_in_use[window_handle] = 1;
return window_handle;
@@ -213,6 +214,7 @@ void gui_close_window(int window_handle)
cairo_destroy(windows[window_handle]);
cairo_surface_destroy(surfaces[window_handle]);
+ cairo_surface_destroy(real_surfaces[window_handle]);
window_in_use[window_handle] = 0;
int no_more_windows = 1;
for (int i = 0; i < MAX_GUI_WINDOWS_COUNT; i++)
Upvotes: 1