Programming Gtk+ interfaces using C language on Af-Arch

Introduction

Af-Arch framework is a middle-ware which allows to manage data, transport them between nodes, etc. But, as every N-tier framework, it is needed to program a client interface to enable users to perform operations.

In this case, what we want to create is a client interface (also know as GUI: graphical user interface) written in Gtk+ using C language.

Consideration about Af-Arch and Gtk+ implemenation

Af-Arch framework is mainly written using threading model. This means every task requested doesn't block the caller and the request response is handled by a function provided by the programmer.

This function (or handler) is executed inside a Thread managed by the framework so programmer doesn't needs to worry about that.

This is a great approach because allows us to write higher level non-blocking code which avoiding effect suchs ugly hanged apps while waiting for responses.

But, Gtk+ design was build on top of the pool system call and there is not warranty for the thread safety. This means nothing wrong, it is just the programming model chosen by Gtk+ authors.

As a consecuence, every user who wants to use Gtk+ with Threads will have to pay a litle more attention (and a litle effort) to avoid Gtk+ to break. In other words: Gtk+ and Threads doesn't mix well, so users have to take care about that.

How to manipulate Gtk+ interface (or when to do it)

Let's talk about important considerations:
    Gtk+ design requires that every Gtk+ interface manipulation 
    comes from the main thread which have issued the gtk_init function. 

This means you can manipulate the interface, read and write it using Gtk+ API, as long as the code is been run by the main thread.

In other word, you have to ensure all your interface updates comes from the main thread. In fact, you are likely to perform such updates when you receive data responses from remote nodes.

So, here is the next rule:

    You cannot manipulate Gtk+ interface directly from the process handler.
    If you do so, you will get messages like:

    Xlib: unexpected async reply (sequence 0x10e6b)!

In other words, if you get previous message the code is manipulating the Gtk+ interface at the wrong moment.

Lets see an example on how to make a *wrong* login while programming Gtk+ interfaces using C language.

// Callback executed when some accept button have been clicked
void login_window_button_accept_clicked () {
        
        // EXAMPLE STARTS HERE:

        gchar * static_user = (gchar *) gtk_entry_get_text (user_login_entry);
        gchar * static_pass = (gchar *) gtk_entry_get_text (pass_login_entry);
        gchar * static_host = "titan.aspl";
        // it is considered as a standard to run af-kernel servers
        // on tcp port 55000
        gchar * static_port = "55000";
        

        // perform login request setting process function:
        //    login_window_button_accept_clicked_process
        if (!afdal_session_login (static_user, static_pass, 
                                  static_host, static_port, 
                                  login_window_button_accept_clicked_process, NULL)) {
                // RIGHT: we can show any dialog, window or pop up because we are 
                // inside the Gtk main loop.
                gtk_show_my_dialog ("Unable to make the login request, exiting..\n");
                return;
        }
        
        // RIGHT: GtkLabel can be modified on this place because we are running
        // inside the Gtk main loop.
        gtk_label_set_markup (login_status_label, "<b>Performing login connection</b>");

        return;
}

// Process function for afdal_session_login request:
// 
// inside process function it is not posible to modify
// Gtk+ interface.
gboolean login_window_button_accept_clicked_process (AfDalNulData *data, gpointer usr_data) 
{


        // check login status
        if (AFDAL_IS_OK (data)) {
                // WRONG: source code cannot modify GtkLabel status on this place
                gtk_label_set_markup (login_status_label, "<b>Login Ok</b>");

                // WRONG: source code cannot modify Gtk status by showing a new window
                // on this place.
                main_window_show ();
                
        }else {
                // WRONG: source code cannot modify Gtk status by showing a new window
                // on this place.
                gtk_label_set_markup (login_status_label, "<b>Login failed: </b>");
        }
        
        // free no longer needed data
        afdal_nul_free (data);

        return TRUE;
}

The only question we didn't do right is to call directly the Gtk+ API from inside the process function. To solve this problems we have two approach:

A example about how to invoke Af-Arch and keep Gtk+ working (no more Xlib async reply)

Here is an example on how to make a *right* login avoiding xlib async reply errors, using AfDal Event Process module. Although in this example we are using the afdal_session_login function, the concept is applied and extended to every service we may want to execute.

// Callback executed when some accept button have been clicked
void login_window_button_accept_clicked () {
        
        // EXAMPLE STARTS HERE:
        AfDalDataList * datalist = NULL;

        gchar * static_user      = (gchar *) gtk_entry_get_text (user_login_entry);
        gchar * static_pass      = (gchar *) gtk_entry_get_text (pass_login_entry);
        gchar * static_host      = "titan.aspl";
        // it is considered as a standard to run af-kernel servers
        // on tcp port 55000
        gchar * static_port = "55000";
        

        // perform login request setting process function:
        //    login_window_button_accept_clicked_process
        // but now, using afdal_event_process module
        datalist = afdal_event_process_create_callback (login_window_button_accept_clicked_process);
        if (!afdal_session_login (static_user, static_pass, 
                                  static_host, static_port, 
                                  afdal_event_process_afdal_nuldata, NULL)) {
                // RIGHT: we can show any dialog, window or pop up because we are 
                // inside the Gtk main loop.
                gtk_show_my_dialog ("Unable to make the login request, exiting..\n");
                afdal_datalist_free (datalist);
                return;
        }
        
        // RIGHT: GtkLabel can be modified on this place because we are running
        // inside the Gtk main loop.
        gtk_label_set_markup (login_status_label, "<b>Performing login connection</b>");

        return;
}

// Process function for afdal_session_login request:
// 
// inside process function it is not posible to modify
// Gtk+ interface.
void login_window_button_accept_clicked_process (AfDalDataList * datalist)
{
        AfDalNulData * data = afdal_datalist_get (datalist, "DATA");
        // check login status
        if (AFDAL_IS_OK (data)) {
                // RIGHT: event source module have managed to queue our
                // function (this one) to be executed by the Gtk main loop.
                gtk_label_set_markup (login_status_label, "<b>Login Ok</b>");

                // RIGHT: event source module have managed to queue our
                // function (this one) to be executed by the Gtk main loop.
                main_window_show ();
                
        }else {
                // RIGHT: event source module have managed to queue our
                // function (this one) to be executed by the Gtk main loop.
                gtk_label_set_markup (login_status_label, "<b>Login failed: </b>");
        }
        
        // free no longer needed data
        afdal_datalist_free (data);
        afdal_nul_free      (data);

        return;
}

Well, what we have done is to change the runtime execution conditions for our process function. Now, it is executed inside the Gtk+ main loop allowing us to modify the interface without any problem.

The changes we have done are:

That's all you need to know about mixing Gtk+ with Af-Arch framework while programming using C language.