How to create a cairo object within a gtk window in GTK+3
Asked Answered
S

4

6

I'm trying to use cairo to draw some arcs but gcc warns me that gdk_cairo_create() is deprecated. Use gdk_window_begin_draw_frame() and gdk_drawing_context_get_cairo_context() instead.

To get around this I did some research and found out that for gdk_window_begin_draw_frame() I need "GdkWindow".I've always been using GtkWidget for my windows so I need to convert "GtkWidget" to "GdkWindow", but gtk_widget_get_window() returns NULL and causes segfault.

#include <gtk/gtk.h>
#include <cairo.h>
void main(int argc , char **argv){
    gtk_init(&argc , &argv);
    GtkWidget *win;
    GdkWindow *gdkwin;
    GdkDrawingContext *dc;

    cairo_region_t *region;

    cairo_t *cr;


    win = gtk_window_new(GTK_WINDOW_TOPLEVEL);

    region = cairo_region_create();

    gdkwin = gtk_widget_get_window(GTK_WIDGET(win));

    //Here gdkwin should contain a GdkWindow but it's NULL.

    gc = gdk_window_begin_draw_frame(gdkwin , (const cairo_region_t*)&region);
    ...
    ...

Here's the runtime errors:

(a.out:6852): Gdk-CRITICAL **: 23:53:06.042: gdk_window_begin_draw_frame: assertion 'GDK_IS_WINDOW (window)' failed

(a.out:6852): Gdk-CRITICAL **: 23:53:06.042: gdk_drawing_context_get_cairo_context: assertion 'GDK_IS_DRAWING_CONTEXT (context)' failed
Segmentation fault

I want to get a cairo object and use it for cairo_arc().

Thanks.Best regards.

Salzburg answered 28/8, 2019 at 19:38 Comment(4)
Why do you want to draw to your window in main()? At this point, your window is most likely not yet visible and any drawing (done on X11) will just be lost. I guess the NULL you get is because there is no GdkWindow yet for your GtkWindow. Please have a look at developer.gnome.org/gtk3/stable/ch01s05.html.Milka
Also, per developer.gnome.org/gtk3/stable/…, the only way gtk_widget_get_window returns NULL is if the widget is not yet realized. See also developer.gnome.org/gtk3/stable/GtkWidget.html#gtk-widget-show and developer.gnome.org/gtk3/stable/….Milka
I just needed a way to create a cairo object to use for drawing and seems "cairo_create()" in that example from gnome website gets the job done.Also I need an alternation to my coding style so that never put all those stuff in main routine.Thanks in advance @Uli SchlachterSalzburg
And also working directly with cairo is a better practice since there's no need to worry about gdk and those cumbersome warnings(gdk_cairo_create).Salzburg
G
6

The below is the complete source code to get Cairo working under GTK 3. It should be compilable as is.

As the others already pointed out, you have to use the draw signal to make things work.

    #include <gtk/gtk.h>
    #include <cairo.h>

    // ------------------------------------------------------------

    gboolean on_draw (GtkWidget *widget,
                    GdkEventExpose *event,
                    gpointer data) 
    {

        // "convert" the G*t*kWidget to G*d*kWindow (no, it's not a GtkWindow!)
        GdkWindow* window = gtk_widget_get_window(widget);  

        cairo_region_t * cairoRegion = cairo_region_create();

        GdkDrawingContext * drawingContext;
        drawingContext = gdk_window_begin_draw_frame (window,cairoRegion);

        { 
            // say: "I want to start drawing"
            cairo_t * cr = gdk_drawing_context_get_cairo_context (drawingContext);

            { // do your drawing
                cairo_move_to(cr, 30, 30);
                cairo_set_font_size(cr,15);
                cairo_show_text(cr, "hello world");
            }

            // say: "I'm finished drawing
            gdk_window_end_draw_frame(window,drawingContext);
        }

        // cleanup
        cairo_region_destroy(cairoRegion);

        return FALSE;
    }

    // ------------------------------------------------------------

    int main (int argc, char * argv[]) {
        gtk_init(&argc, &argv);

        GtkWindow * window; 
        { // window setup
            window = (GtkWindow*)gtk_window_new(GTK_WINDOW_TOPLEVEL);
            gtk_window_set_default_size (window, 200, 200);
            gtk_window_set_position     (window, GTK_WIN_POS_CENTER);
            gtk_window_set_title        (window, "Drawing");

            g_signal_connect(window, "destroy", gtk_main_quit, NULL);
        }  

        // create the are we can draw in
        GtkDrawingArea* drawingArea;
        {
            drawingArea = (GtkDrawingArea*) gtk_drawing_area_new();
            gtk_container_add(GTK_CONTAINER(window), (GtkWidget*)drawingArea);

            g_signal_connect((GtkWidget*)drawingArea, "draw", G_CALLBACK(on_draw), NULL);    
        }  


        gtk_widget_show_all ((GtkWidget*)window);
        gtk_main();

        return 0;
    }



    // ------------------------------------------------------------
Gulch answered 15/11, 2019 at 3:55 Comment(0)
S
2

In GTK+ 3, you're supposed to do your drawing in response to the draw signal. Doing it in the main makes no sense (the widgets have just been created, but initializing them further in done when running the main event loop).

Please read: https://developer.gnome.org/gtk3/stable/chap-drawing-model.html

Stratigraphy answered 16/9, 2019 at 13:26 Comment(0)
E
1

The Dark Trick's program is complete. He uses the functions as follows,

GdkWindow* window = gtk_widget_get_window (widget);
cairo_region_t *cairoRegion = cairo_region_create();
GdkDrawingContext *drawingContext;
drawingContext = gdk_window_begin_draw_frame (window, cairoRegion);
cairo_t *cr = gdk_drawing_context_get_cairo_context (drawingContext);

But I am using the the functions as follows,

GdkWindow *window = gtk_widget_get_window(widget);
cairo_rectangle_int_t cairoRectangle = {0, 0, 200, 200};
cairo_region_t *cairoRegion = cairo_region_create_rectangle (&cairoRectangle);
GdkDrawingContext *drawingContext;
drawingContext = gdk_window_begin_draw_frame (window,cairoRegion);
cairo_t *cr = gdk_drawing_context_get_cairo_context (drawingContext);

This worked, but I can not understand the differencies, for I am an OldUrologist.

Emplane answered 24/5, 2020 at 2:59 Comment(1)
I'm having the same problem. I think cairo_region_create returns a rectanglular region with width 0 and height 0 by default. It's the only way I can explain why you need to pass in a custom width and height.Deannedeans
S
0

I'm not positive, but I think you're trying to get the GdkWindow before it is ready. I think you need to connect to the window's "realize" signal, and only when that signal has been emitted should you try to access the underlying GdkWindow.

#include <gtk/gtk.h>
#include <cairo.h>


void OnWindowRealize(GtkWidget *pWidget, gpointer data)
{
    GdkWindow *pUnderlyingWindow = gtk_widget_get_window(pWidget);

    cairo_region_t *region = cairo_region_create();

    GdkDrawingContext *gc = gdk_window_begin_draw_frame(pUnderlyingWindow, (const cairo_region_t*)&region);

    //etc...
}


void main(int argc , char **argv)
{
    gtk_init(&argc , &argv);

    GtkWidget *win = gtk_window_new(GTK_WINDOW_TOPLEVEL);

    g_signal_connect(G_OBJECT(win), "realize", OnWindowRealize, NULL);

    //etc...
}


Sarita answered 5/9, 2019 at 21:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.