Cairo C 程序不会绘制到 x11 window

Cairo C program won't draw to x11 window

我正在尝试使用 C 中 Linux 上的 Cairo 图形库来制作一个相当轻量级的 x11 GUI。

在非常努力地遵循 cairo 为 x11 提供的 woefully incomplete guide 之后,这是我得到的最好的:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <cairo.h>
#include <cairo-xlib.h>
#include <X11/Xlib.h>
#include <X11/extensions/Xrender.h>
#include <X11/extensions/renderproto.h>

//This function should give us a new x11 surface to draw on.
cairo_surface_t* create_x11_surface(int x, int y)
{
    Display* d;
    Drawable da;
    int screen;
    cairo_surface_t* sfc;

    if((d = XOpenDisplay(NULL)) == NULL)
    {
        printf("failed to open display\n");
        exit(1);
    }

    screen = DefaultScreen(d);
    da = XCreateSimpleWindow(d, DefaultRootWindow(d), 0, 0, x, y, 0, 0, 0);
    XSelectInput(d, da, ButtonPressMask | KeyPressMask);
    XMapWindow(d, da);

    sfc = cairo_xlib_surface_create(d, da, DefaultVisual(d, screen), x, y);
    cairo_xlib_surface_set_size(sfc, x, y);

    return sfc;
}

int main(int argc, char** argv)
{
    //create a new cairo surface in an x11 window as well as a cairo_t* to draw
    //on the x11 window with.
    cairo_surface_t* surface = create_x11_surface(300, 200);
    cairo_t* cr = cairo_create(surface);

    while(1)
    {
        //save the empty drawing for the next time through the loop.
        cairo_push_group(cr);

        //draw some text
        cairo_select_font_face(cr, "serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
        cairo_set_font_size(cr, 32.0);
        cairo_set_source_rgb(cr, 0, 0, 1.0);
        cairo_move_to(cr, 10.0, 25.0);

        if((argc == 2) && (strnlen(argv[1], 100) < 50))
            cairo_show_text(cr, argv[1]);
        else
            cairo_show_text(cr, "usage: ./p1 <string>");

        //put the drawn text onto the screen(?)
        cairo_pop_group_to_source(cr);
        cairo_paint(cr);
        cairo_surface_flush(surface);

        //pause for a little bit.
        int c = getchar();

        //change the text around so we can see the screen update.
        for(int i = 0; i < strnlen(argv[1], 100); i++)
        {
            argv[1][i] = argv[1][i + 1];
        }

        if(c == 'q')
        {
            break;
        }
    }

    cairo_surface_destroy(surface);
    return 0;
}

在安装了 Cairo 的 Linux 系统上,可以使用

进行编译
gcc -o myprog $(pkg-config --cflags --libs cairo x11) -std=gnu99 main.c

它应该是 运行 和一个参数。

由于我完全不明白的原因,插入行

cairo_pop_group_to_source(cr);
cairo_paint(cr);
cairo_surface_write_to_png (surface, "hello.png");    //<--------- inserted
cairo_surface_flush(surface);

在屏幕上放了一些东西,但是有 2 个问题:

  1. 我用这种方法绘制的文本是持久的,产生了涂抹效果。
  2. 我不想在我的程序和 x11 window 之间使用一些 .png 文件。直接发数据吧!

几个问题:

  • 在 X11 中,X11 服务器不会将您绘制的内容保存到 window,而是向您的 window 发送一个 ExposeEvent,告诉它重新绘制。这意味着你得到一个黑色 window,因为你没有处理这个事件。
  • getchar 只会在换行后给你一些东西,所以只输入一些东西是没有用的。
  • libX11 缓冲内容,仅在您等待事件(或缓冲区填满)时将其发送到 X11 服务器。由于您从不等待事件,因此它永远不会刷新。明确地调用 XFlush 有帮助。
  • 你推的群没用。摆脱它。
  • 您将字符串向左移动一个方向的代码很容易超出字符串的末尾。你显然已经知道了,因为你 'fixed' this with a strnlen.

这里有一个更好的解决方案,但它仍然给你一个最初的黑色 window,因为你在它被映射之前绘制它:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <cairo-xlib.h>
#include <X11/Xlib.h>

//This function should give us a new x11 surface to draw on.
cairo_surface_t* create_x11_surface(Display *d, int x, int y)
{
    Drawable da;
    int screen;
    cairo_surface_t* sfc;

    screen = DefaultScreen(d);
    da = XCreateSimpleWindow(d, DefaultRootWindow(d), 0, 0, x, y, 0, 0, 0);
    XSelectInput(d, da, ButtonPressMask | KeyPressMask);
    XMapWindow(d, da);

    sfc = cairo_xlib_surface_create(d, da, DefaultVisual(d, screen), x, y);

    return sfc;
}

int main(int argc, char** argv)
{
    Display *d = XOpenDisplay(NULL);
    if (d == NULL) {
        fprintf(stderr, "Failed to open display\n");
        return 1;
    }
    //create a new cairo surface in an x11 window as well as a cairo_t* to draw
    //on the x11 window with.
    cairo_surface_t* surface = create_x11_surface(d, 300, 200);
    cairo_t* cr = cairo_create(surface);
    char *text = argv[1];
    size_t text_len = 0;

    if (argc != 2)
        text = NULL;
    else
        text_len = strlen(text);

    while(1)
    {
        // Clear the background
        cairo_set_source_rgb(cr, 0, 0, 0);
        cairo_paint(cr);

        //draw some text
        cairo_select_font_face(cr, "serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
        cairo_set_font_size(cr, 32.0);
        cairo_set_source_rgb(cr, 0, 0, 1.0);
        cairo_move_to(cr, 10.0, 25.0);

        if (text)
            cairo_show_text(cr, text);
        else
            cairo_show_text(cr, "usage: ./p1 <string>");

        cairo_surface_flush(surface);
        XFlush(d);

        //pause for a little bit.
        int c = getchar();

        //change the text around so we can see the screen update.
        memmove(text, &text[1], text_len);
        if (text_len > 0)
            text_len--;

        printf("got char %c\n", c);
        if(c == 'q')
        {
            break;
        }
    }

    // XXX: Lots of other stuff isn't properly destroyed here
    cairo_surface_destroy(surface);
    return 0;
}

编辑:另外,为什么你觉得开罗只给你一个非常不完整的指南?它告诉你如何让 cairo 部分工作,它还解释了一些关于 X11 的部分,即使你应该已经知道那些如果你想使用 cairo-x11。这是它的业务 none。您链接到的指南甚至提供了一个完整的、有效的和独立的示例:https://www.cypherpunk.at/files/2014/11/cairo_xlib_simple.c

我知道你已经阅读了这个 "imcomplete guide" 的完整文本,你会看到完整示例中有一个 link:https://www.cypherpunk.at/files/2014/11/cairo_xlib_simple.c .