Exit a gtkmm application properly

Viewed 1340

I am creating a GUI using Glade, and am able to connect signals to it properly. I am trying to have a button that simply quit the application.

The doc is not very clear on how to do so. On some forums you should do:

Gtk::Main::quit(); 

Which does exit my application, but with a Segmentation Fault. Apparently I am supposed to call quit() directly from my application, like so:

p_application->quit(); 

But this returns me the resulting error at compile time:

error: invalid use of member ‘GUI::p_application’ in static member function
     Glib::RefPtr<Gtk::Application> p_application;
                                    ^
error: from this location
     p_application->quit();
     ^

I created the application using this:

 p_application = Gtk::Application::create(argc, argv, "org.app.app");

How should I proceed ?

2 Answers

Warning: All the online documentation I could find is for gtkmm-4. If you are stuck using gtkmm-3.0 for Ubuntu, download your own docs from the repository (see below). I've included links to gtkmm-4 documentation in the hopes that they are helpful; use at your own risk.

Here is a working solution for gtkmm-3.0. Compile with:

 g++ helloworld.cc -o main `pkg-config --cflags --libs gtkmm-3.0`

Code:

// In file helloworld.cc
// sigc::mem_fun is the important part:
// it lets you turn a member function into a static one (or something).
// I don't really get it.
// I got most of the code here from https://www.gtk.org/docs/language-bindings/cpp

#include <iostream>

#include <sigc++/sigc++.h>            // Unnecessary, because the gtkmm modules also include mem_fun.
#include <glibmm/refptr.h>            // The wrapper object that Application::create returns.

#include <gtkmm/application.h>        // C++ wrapper for C's gtk_main.
#include <gtkmm/button.h>
#include <gtkmm/window.h>

class HelloWorld : public Gtk::Window
{
public:
    HelloWorld(Glib::RefPtr<Gtk::Application>);

protected:
    void on_button_clicked();

    Gtk::Button m_button;
    Glib::RefPtr<Gtk::Application> app;
};


// Constructor for window class. app should be a pointer to the Application instance you made in main.
HelloWorld::HelloWorld(Glib::RefPtr<Gtk::Application> app)
: m_button("Quit this program")     // Sets button label. I have no idea what this syntax is. C++ be whack, yo
{
    this->app = app;
    // mem_fun does most of the magic. How does it work? idk lol
    m_button.signal_clicked().connect(sigc::mem_fun(*this,
                            &HelloWorld::on_button_clicked));
    add(m_button);
    m_button.show();
}

void HelloWorld::on_button_clicked()
{
    std::cout << "Exiting the program now." << std::endl;
    this->app->quit();
    std::cout << "Note: Program doesn't actually end until this function finishes." << std::endl;
}

int main (int argc, char *argv[])
{
    Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example");
    HelloWorld helloworld(app);
    return app->run(helloworld);
}

My thoughts: I don't really get it?

Gtk::Button.signal_clicked() (reference) returns a Glib::SignalProxy<void()>. SignalProxy (reference) has template <R(T...)>, which I think describes the kind of functions that can be connected--in this case, with a return type void and an empty list () of arguments. SignalProxy.connect() returns something I'll ignore, and accepts either a SlotType& or a SlotType&& (I have no idea what the difference is) where SlotType is sigc::slot<R(T...)>, or in this case sigc::slot<void()>.

sigc::slot<R(T...)> (reference) seems pretty fundamental. It looks like it's just a wrapper that holds a function (presumably, so SignalProxy can store it to be called later when the button is clicked). Up until now, it all makes sense.

The notes on sigc::mem_fun (reference) say that it returns a sigc::mem_functor, which sounds reasonable but mem_functor does not seem to fulfill what SignalProxy.connect() wants: sigc::slot and sigc::mem_functor do not inherit from each other. Baffling. I guess there's some kind of undocumented type coercion going on here? Maybe? The other lead is that mem_fun apparently returns an object of type decltype(auto)? Which I tried reading documentation on, and then they started talking about lvalues and unparenthesized id-expressions, and that's when my eyes glazed over. Looking at the old documentation, there used to be like 70 overloaded versions of mem_fun to do what one version can do now, so I guess decltype is doing its job. But the point is, that's where I gave up.

decltype(auto) and mem_functors are C++ deep-magic. mem_fun does the thing SignalProxy.connect() needs, and that's all I know.

References:

gtkmm-4.0 documentation

glibmm documentation

sigc++ documentation

gdk documentation in c << gtkmm refers to stuff like "GdkEventKey" and enumerations from the header <gdk/gdkkeysyms.h>. I couldn't find a c++ reference for this, or anything like "gdkmm". I think maybe gtkmm just uses the C structs for events directly?

Installing documentation: I'm on Ubuntu, 20.04.3 LTS. You can install html documentation for gtk 3.0 from the repository. Do sudo apt install libgtkmm-3.0-doc libglibmm-2.4-doc libsigc++-2.0-doc. To find out where the index file is for, for example, gtkmm, do dpkg -L libgtkmm-3.0-doc | grep index. sigc++ has multiple index files, two of which are traps. I could not figure out where the documentation for GDK is, possibly because there isn't any in the repository. Use the online stuff instead.

Related