0

I'm looking to execute some code just after my Gtk3 window closes. I've tried delete-event, destroy, and destroy-event which seems to not fire at all.

I get that there's some difference between Gdk and Gtk, but I'm not exactly sure about what that is. I assume I'm asking about the right thing.

I'm creating the window with the node.js binding of gtk_window_new(), and closing it with gtk_window_close(). When doing that, and using the events mentioned above, the event handlers fire while the window is still on the screen.

edit:

As requested, some example code:

const gi = require('node-gtk')
const Gtk = gi.require('Gtk', '3.0');

gi.startLoop();
Gtk.init();

const win = new Gtk.Window();
win.on('destroy', () => Gtk.mainQuit());
win.on('delete-event', () => false);

const button = new Gtk.Button({ label: 'Do it' });
win.add(button);
button.on('clicked', function () {win.close();});

win.showAll();
Gtk.main();

shell_command("wmctrl -lG");

function shell_command(cmd_str) {

    const execSync = require('child_process').execSync;
    code = execSync(cmd_str);
    return code.toString();

}

On my Linux Mint 19.3 system, the above code creates a small window with 1 button. Clicking the button crashes execution with the following terminal error:

Error: Command failed: wmctrl -lG
X Error of failed request:  BadWindow (invalid Window parameter)
  Major opcode of failed request:  20 (X_GetProperty)
  Resource id in failed request:  0x6c00003
  Serial number of failed request:  26
  Current serial number in output stream:  26

The window id 0x6c00003 is the window id of the window created by the above code, as proven by running wmctrl after running the script, but before pressing the button.

If I simply put the the call to wmctrl in a 50ms timer, it works without error.

setTimeout(function () {shell_command("wmctrl -lG");}, 50);

This is what leads me to believe it's a race condition. I think wmctrl is getting window ids when my window still exists, but then further querying against those ids after my window has closed, and it causes the above error. That's my theory, and if it's correct, Gtk.main() returns before the window is truly gone (and the same is true of the other events mentioned).

Duncan Marshall
  • 530
  • 1
  • 6
  • 15
  • generally, these signals delete-event, destroy and destroy-event work. Could you share a sample code maybe with that we could understand why it's not working. – Siva Guru Feb 10 '20 at 13:09
  • It's not that they don't work at all, it's that the window is still on screen while they are working. `destroy-event` seems to not actually work though, but that may have something to do with the need to enable it at creation time, which I haven't done, and don't know how to do. Sharing code is hard, because this is part of a broader application, and because I'm using a node module which most people aren't familiar with. However, I'll try to edit some in. – Duncan Marshall Feb 10 '20 at 13:20
  • @SivaGuru code is added. – Duncan Marshall Feb 10 '20 at 13:34
  • upon getting the destruction signal try calling a function that free's all reference to the main window (gtk_widget_destroy or g_object_unref or ) and then try calling Gtk.mainQuit(). I tried modifying the c code but it works over there. Your reasoning to call it a reace condition seems the be valid(this issue could also be related to node-gtk). – Siva Guru Feb 12 '20 at 16:11

1 Answers1

1
#include <gtk/gtk.h>

gboolean destroy(gpointer window, gpointer testing);

void destroy1(GtkWidget *window, gpointer testing);

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

  GtkWidget *window;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

  gtk_widget_add_events(GTK_WIDGET(window), GDK_ALL_EVENTS_MASK);

  gtk_widget_show(window);

  g_signal_connect(window, "destroy", G_CALLBACK(destroy1), NULL);
  g_signal_connect(window, "destroy-event", G_CALLBACK(destroy), NULL);

  gtk_main();

  printf("closing application \n");
  // you could try to add the code here 
  //which will be only be exited after the window is destroyed
  sleep(10);
  return 0;
}

void destroy1(GtkWidget *window, gpointer testing) {
  printf("sleeping for 5 seconds \n");
  gtk_widget_destroy(GTK_WIDGET(window));
  sleep(5);
  gtk_main_quit();
  printf("i have slept \n");
}

gboolean destroy(gpointer window, gpointer testing) {
  printf("sleeping for 5 seconds \n");
  sleep(5);
  printf("i have slept \n");

  return TRUE;
  }

After the window is destroyed completely the main loop will end. so, you could try adding you code after gtk_main();

The ::destroy-event signal is emitted when a gdk-window is destroyed. You rarely get this signal, because most widgets disconnect themselves from their window before they destroy it, so no widget owns the window at destroy time. (https://www.gnu.org/software/guile-gnome/docs/gtk/html/GtkWidget.html).

Siva Guru
  • 694
  • 4
  • 12
  • I can't believe what a moron I've been. Obviously just putting it after `main()` was the thing to do. – Duncan Marshall Feb 10 '20 at 14:08
  • This turned out not to do the trick. Instead it caused what appears to be a race condition in `wmctrl`. Essentially what happens is `Gtk.main()` returns after `win.close()`, then my code calls `wmctrl` which runs *while* Gtk *actually* closes the window, which causes `wmctrl` to throw an error. `Gtk.main()` does not end synchronously. – Duncan Marshall Feb 11 '20 at 14:34
  • i am not understanding this issue. could you just update your code with wmctrl like any sample working code would be nice.. – Siva Guru Feb 12 '20 at 06:46
  • I've further updated the code and expanded the explanation in the original post. I've since changed my application in a way that makes this no longer matter, but future searches may care. – Duncan Marshall Feb 12 '20 at 13:59