相关文章推荐
热情的橡皮擦  ·  Github有趣项目 - 知乎·  1 年前    · 
幸福的番茄  ·  What's New in TiDB ...·  1 年前    · 
气势凌人的电池  ·  [PHP] curl ...·  1 年前    · 
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I'm trying to use the Cairo graphics library on Linux in C to make a pretty lightweight x11 GUI.

After trying very hard to follow the woefully incomplete guide that cairo gives for x11, this is the best I've got:

#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]);
            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;

On Linux systems that have Cairo installed, it can be compiled with

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

And it should be run with a single argument.

For reasons I don't understand at all, inserting the line

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

Puts something on the screen, but there are 2 problems:

  • Text that I draw with this method is persistent, creating a smearing effect.
  • I don't want some .png file mediating between my program and an x11 window. Data should be sent directly!
  • Your problem is in your choice of backend for cairo. You can choose png, pdf, svg, or you can write directly to a gtk window (which it appears you want to do). See the examples in Cairo Tutorial - zetcode for examples of each. – David C. Rankin Oct 28, 2015 at 7:52 No worries, just slow down a little bit and work through the tutorials and you will slowly begin to digest the different capabilities. No matter what you are trying to learn in C, remember it is not a race, and the details that you fly by when in a hurry -- really are important. So enjoy the ride. (not race). – David C. Rankin Oct 28, 2015 at 8:01 @DavidC.Rankin I don't know, in principle you can draw to a GdkPixmap which is a drawable just like a GdkWindow is, and then use the GdkPixmap as a X11 pixmap to draw to a X11 window. And when I've compiled cairo from source I recall it has a x11 backend but I am not sure. – Iharob Al Asimi Oct 28, 2015 at 8:07 That is a good point, I didn't climb down to the bottom of the cairo API and compare all the various backends. There may be some limitation on a direct write to X11, but the pixmap or window provided the direct write to screen and there are a number of direct X11 conversions at the bottom of that rabbit-hole to check. – David C. Rankin Oct 28, 2015 at 8:15 @johnny_boy If you're in a hurry, why are you not targetting something higher-level and more complete? Like, of course, GTK+? – unwind Oct 28, 2015 at 8:55
  • In X11, the X11 server doesn't save what you drew to a window, but instead sends an ExposeEvent to your window that tells it to redraw. This means you get a black window, because you do not handle this event.
  • getchar only gives you something after a line break, so just typing something won't help.
  • libX11 buffers stuff and only sends it to the X11 server when you wait for an event (or the buffer fills up). Since you never wait for an event, it never flushes. Calling XFlush explicitly helps.
  • The group that you push is useless. Just get rid of it.
  • Your code to move the string one direction to the left easily goes beyond the end of the string. You apparently know this already, because you 'fixed' this with a strnlen.
  • Here is a little better solution, but it still gives you an initially black window, because you draw to it before it is mapped:

    #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;
            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);
                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;
    

    Edit: Also, why exactly do you feel like cairo only gives you a woefully incomplete guide? It tells you how to get the cairo parts working and it also explains you some parts about X11, even though you should already know those if you want to use cairo-x11. That's none of its business. The guide you linked to even provides a complete, working and self-contained example: https://www.cypherpunk.at/files/2014/11/cairo_xlib_simple.c

    "why exactly do you feel like cairo only gives you a woefully incomplete guide?" Maybe I was being a little harsh, but all of the links to cypherpunk.at are dead, so I wasn't able to view the example code (which would have been very helpful) – John M Oct 29, 2015 at 18:32 The links to cypherpunk.at are not dead, but its server uses a self-signed certificate (or something else that is not known) so Firefox warns you before continuing; Chrome is even more picky so it may appear to be broken, but it's only a security issue. – JvO Oct 29, 2015 at 20:38

    Thanks for contributing an answer to Stack Overflow!

    • Please be sure to answer the question. Provide details and share your research!

    But avoid

    • Asking for help, clarification, or responding to other answers.
    • Making statements based on opinion; back them up with references or personal experience.

    To learn more, see our tips on writing great answers.