Question

Status quo: I have a custom widget (MyWidget) with an event window.
Problem: if I create, show and then, later, hide and destroy the widget I get the following message from the application:

Gdk-WARNING **: losing last reference to undestroyed window

What I've found out: I've had a look in gdkwindow.c file and this message is reported when GDK_WINDOW_DESTROYED(window) == FALSE. So the thing I do not understand is how I should destroy my window correctly so that eventually gdk_window_destroy() function is called. I thought that the best place to call it was the Gdk::~Window() destructor. But it's empty. And moreover gdk_window_destroy() is absent in gdkwindow.cc file at all.

The on_realize() and on_unrealize() call-backs are below.

class MyWidget : public Gtk::Widget
{
...
private:
    Glib::RefPtr<Gdk::Window>   _event_window;
...
};

void Gtk::MyWidget::on_realize()
{
    GdkWindowAttr       attributes;
    const Allocation    & allocation = get_allocation();

    attributes.event_mask = GDK_BUTTON_PRESS_MASK;

    attributes.x = allocation.get_x();
    attributes.y = allocation.get_y();
    attributes.width = allocation.get_width();
    attributes.height = allocation.get_height();
    attributes.wclass = GDK_INPUT_ONLY;
    attributes.window_type = GDK_WINDOW_CHILD;

    _event_window = Gdk::Window::create(get_parent_window(), &attributes, GDK_WA_X | GDK_WA_Y);
    _event_window->set_user_data(Widget::gobj());

    set_window(get_parent_window());

    set_realized();
}

void Gtk::MyWidget::on_unrealize()
{
    _event_window->set_user_data(NULL);
    _event_window.reset();

    set_realized(false);
}
Was it helpful?

Solution

It's turned out that the correctest way to destroy the GDK window you have created with Gdk::Window::create() is.. guess what? to call Gtk::Widget::unrealize() in on_unrealize() method of your custom widget because apart from other things this base method calls gdk_window_destroy() for the widget's GDK window. And for that your widget has to be "windowful" (i.e you should call set_has_window(true); in the constructor and set_window(<your allocated GDK window>); in on_realize() callback. Very obvious approach, isn't it?

I should also say something about Gtk::Widget::realize(). Unlike Gtk::Widget::unrealize() you should call Gtk::Widget::realize() only if your widget doesn't have a GDK window (the method assumes that as an assertion).

Unfortunately I didn't have time and wish to get to the very bottom of it and endevour to understand why it has been done so and what reasons and consequences this approach has. Otherwise I would provide more detailed explanation.

You can find an official example from GTK's tutorial on custom widgets here.

Also the code of my widget now looks like that:

class MyWidget : public Gtk::Widget
{
...
private:
    Glib::RefPtr<Gdk::Window>   _event_window;
...
};

void Gtk::MyWidget::on_realize()
{
    GdkWindowAttr       attributes;
    const Allocation    & allocation = get_allocation();

    attributes.event_mask = GDK_BUTTON_PRESS_MASK | GDK_EXPOSURE;

    attributes.x = allocation.get_x();
    attributes.y = allocation.get_y();
    attributes.width = allocation.get_width();
    attributes.height = allocation.get_height();
    attributes.wclass = GDK_INPUT_OUTPUT;
    attributes.window_type = GDK_WINDOW_CHILD;

    _event_window = Gdk::Window::create(get_parent_window(), &attributes, GDK_WA_X | GDK_WA_Y);
    _event_window->set_user_data(Widget::gobj());

    set_window(_event_window);

    set_realized();
}

void Gtk::MyWidget::on_unrealize()
{
    _event_window->set_user_data(NULL);
    _event_window.reset();

    Widget::unrealize();
    // it will call gdk_destroy_window() and
    // set_realized(false);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top