cairo / xlib 未更新 window 内容

cairo / xlib not updating window content

我正在尝试学习如何将 Cairo 2D drawing library 与 xlib 表面一起使用。

我写了一个允许创建多个 windows 的小测试程序。每个函数都可能有一个自定义的 paint() 函数,该函数会定期调用以将一些图形内容添加到 window,或者根据需要完全重绘它。还有一个选项可以定义鼠标和按键侦听器。主例程检查 X 事件(将它们委托给鼠标和按键侦听器)以及定期调用这些 paint() 函数的超时。

我尝试使用 1.14.6 版本的 Cairo(目前在 Ubuntu 16.04 中作为软件包提供)和最新的 1.15.12,但结果是一样的。

此演示的预期行为是打开 3 windows。一个将添加随机矩形,另一个将添加随机文本,第三个将添加随机圆圈。

此外,点击进入windows应该会产生线条(连接到鼠标点击,或随机),并且使用箭头键应该在window中用圆圈画一条红线。

圆圈和文字似乎按预期定期显示。所有三个 windows 都应该是白色背景,但其中两个是黑色的。最糟糕的是,带有矩形的 window 并没有得到太多更新(并且它是否是第一个 window 创建的并不重要,它总是没有正确显示的矩形)。

它们仅在焦点从 window 改变或改变时显示 - 然后本应同时绘制的其余矩形突然出现。

我在添加任何内容后在每个 window 的表面上调用 cairo_surface_flush(),但这没有帮助。我还尝试将 XEvents 发布到 window 各种类型(例如焦点),它们到达了,但是没有显示矩形。

此外,即使用鼠标画线工作正常,用键箭头画线也会遇到同样的问题——它被画了,但没有正确显示。

我对这个库可以做什么的一些假设显然是错误的,但我不确定在哪里。

似乎显示了两个竞争版本的绘图,因为有时会出现一个或两个矩形或红线段在闪烁。某种奇怪的缓冲、缓存? 这可能只是我程序中的一些错误,我不知道。

另一个观察 - 黑色背景是因为绘制白色背景发生在 window 显示之前,因此那些 cairo_paint 调用以某种方式被丢弃。我不知道如何让 window 出现得更早,它似乎只是在屏幕上进行一些稍后的更改后才会出现。

经过几天的绝望,我被困在这个问题上,你能至少帮助我部分吗?

节目在这里:test_cairo.c

示例屏幕截图(按键绘制的红色虚线,以及未正确显示的矩形):test_cairo.png

要编译(在 Ubuntu 16.04 或类似系统上):

gcc -o test_cairo test_cairo.c -I/usr/include/cairo -lX11 -lcairo 

X11 不会为您保留 window 内容。当您收到 Expose 事件时,您必须完全重新绘制该事件描述的区域。

All three windows should have white background, but two of them are black.

您使用 XCreateSimpleWindow 创建了 window,因此它们的背景属性设置为黑色。 X11 服务器会在发送暴露事件之前为您用黑色填充暴露区域。由于您没有告诉 cairo 绘制白色背景,因此黑色保持不变。

试试这个:

--- 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++)