15

I have two GTK windows

  1. Normal (main) window that runs animation, draws stuff in callback registered by gtk_widget_add_tick_callback().

  2. At some point secondary window is created that runs modal loop:

    void show_modal() 
    {
       GtkWindow* gw = gtkwindow(this);
    
       if( parent() )
         gtk_window_set_transient_for(gw, gtkwindow( parent() ));
    
    
       gtk_widget_show(GTK_WIDGET(gw));
       gtk_window_set_modal(gw,TRUE);
       gtk_window_set_keep_above(gw,TRUE);
       this->update_window_state(gool::WINDOW_SHOWN);
    
       while( this->is_valid_window() )
       {
          if(this->_window_state == WINDOW_HIDDEN) break;
          if(this->_window_state == WINDOW_STATE_NA) break;
          gtk_main_iteration(); // gtk_main_iteration_do(true);
       }
    }
    

Problem: Animation in main window works fine until show_modal() is invoked. It appears as gtk_main_iteration(); blocks ticks added by gtk_widget_add_tick_callback() function. As soon as I close secondary window and so while() {gtk_main_iteration();} loop exits then animations in main window start running again.

Any idea of how to make "animation friendly" modal loops in GTK?

UPDATE: it appears as gtk_main_iteration(); blocks not only ticks but any updates of any windows other than "current" - they are simply frozen. What is the reasoning of such GTK behavior?

UPDATE #2:

gtk_dialog_run(); behaves exactly as gtk_main_iteration(); - locks any updates on any window in process other than active window.

c-smile
  • 26,734
  • 7
  • 59
  • 86
  • How is this secondary window created and `show_modal` invoked? In `gtk_widget_add_tick_callback`? This loop will then prevent callback from returning. – pan-mroku Mar 14 '17 at 12:10
  • That loop can be invoked on other UI events, e.g. on [mouse] button release event. "This loop will then prevent callback from returning" - exactly! So is the question: "how to make "animation friendly" modal loops in GTK? " and "how to define modal loop without locking updates of background windows?" – c-smile Mar 15 '17 at 19:11
  • Does it actually block the other loop or does it only prevent from updating the UI? Try adding a print statement in your tick callback. – antoyo May 29 '17 at 14:28

2 Answers2

1

It seems to be by definition: link

gboolean gtk_main_iteration (void); Runs a single iteration of the mainloop. If no events are waiting to be processed GTK+ will block until the next event is noticed. If you don’t want to block look at gtk_main_iteration_do() or check if any events are pending with gtk_events_pending() first.

The explanation suggests to use gtk_main_iteration_do(FALSE) if you don't want blocking:

gboolean gtk_main_iteration_do (gboolean blocking); Runs a single iteration of the mainloop. If no events are available either return or block depending on the value of blocking: TRUE if you want GTK+ to block if no events are pending

As for gtk_dialog_run: it also blocks by design link

gint gtk_dialog_run (GtkDialog *dialog); Blocks in a recursive main loop until the dialog either emits the “response” signal, or is destroyed.[...]

I read about people solving this using multiple threads: handle the GUI in the main thread and do background work in another one. There's an article about it here that might be useful.

JHBonarius
  • 10,824
  • 3
  • 22
  • 41
  • 2
    That's the nature of modal loops on all UI platforms - to block current execution by showing window with the question and running "message pump" loop until it gets closed. Like here: https://www.codeproject.com/Articles/348/Implementing-Modal-Message-Loops On Windows and Mac that works just fine. It is just on GTK `gtk_main_iteration_do(/*does not matter what*/)` does not dispatch messages to any other windows except the active one. For unknown reasons. – c-smile Mar 17 '17 at 16:26
0

I assume that show_modal is called from a callback or other activity in main context. You could try adding your modal window into main context using invoke or signal_idle.

This way execution of show_modal will end.

#include <gtkmm.h>
#include <string>

int main()
{
    auto Application = Gtk::Application::create();
    Gtk::Window window;
    Gtk::Window* window2;
    Gtk::Button button;
    window.add(button);

    //I hope timeout behaves similar to ticks. I have no idea how animations in GTK work
    int i=0;
    Glib::MainContext::get_default()->signal_timeout().connect([&]()->bool{
            button.set_label(std::to_string(i++));
            return true;
        }, 1000);

    button.signal_clicked().connect([&]{
            Glib::MainContext::get_default()->invoke([&]()->bool{
                    window2 = new Gtk::Window;
                    window2->set_modal(true);
                    window2->set_keep_above(true);
                    window2->signal_delete_event().connect([&](GdkEventAny* any_event)->bool{
                            delete window2;
                            return false;
                        });
                    window2->show_all();
                    return false;
                });
        });

    window.show_all();

    return Application->run(window);
}
pan-mroku
  • 803
  • 6
  • 17
  • 1
    That's not a modal dialog but rather modeless. And there is no problem with such windows/dialogs in GTK. This `window->show_modal()` means that next line after it will be executed only after user will close the dialog. Such show_modal() is implemented roughly as this: `window->show(); run_message_pump_until_hidden(window);` and correct implementation of that hypothetical `run_message_pump_until_hidden()` is the question. – c-smile Oct 03 '17 at 22:42
  • I'm guessing that running `show_modal` in a callback blocks redrawing of animation. `gtk_main_iteration_do` iterates the loop, but redraw is only queued, not executed. Could you post some code with your animation and where you are calling `show_modal`? (io watch, button click, idle?) – pan-mroku Oct 04 '17 at 09:34