"--help" closes other instances of app?

I’ve forked a GTK app and I’ve just had a user report that “–help” isn’t working.

From what I can tell, it is working, but not in the way I’d expect.

The app is a Gtk.Application and overrides command_line(). It then uses OptionContext and OptionEntry to parse the command-line arguments. This works fine when running a single instance of the app. app --help prints the help info and quits.

But if you’ve already got an instance of the app open then app --help just displays GDBus.Error:org.freedesktop.DBus.Error.NoReply: Message recipient disconnected from message bus without replying on the command-line and kills the existing instance of the app. If you ran the existing instance from a CLI as well then you see the help info printed there.

I think we need the “single instance” behaviour of Gtk.Application, but I wouldn’t expect --help to print an unhelpful error message and kill the existing instance. Is it possible to keep the single instance behaviour and have the help output print to the current terminal?

I have these type of options in a handle_local_options override.

But I’m not sure if my app exhibits the same behavior or not… I don’t think it does but now I’m curious. I’ll let you know for certain this evening if no one else can confirm.

Interestingly, if I use a valid app-specific argument then it works fine and doesn’t close the existing instance of the app (at least for the two I tested). If I pass an invalid argument (e.g. --blah) then the command-line doesn’t get the DBUS message but gets Use --help to see available options (but the existing instance of the app still closes).

I also get a segfault from the existing instance when I pass invalid options that I don’t get with --help, but that appears to be because my shutdown tidy-up is doing something odd, but it never gets hit when --help prints and quits.

My application doesn’t have this issue. It works as intended.

Have a good look at the GApplication documentation and move anything that makes sense to move into a handle_local_options override or signal handler.

Options which simply print something to the terminal and then exit ( --version, --about, --help, etc. ) all belong in handle_local_options.

Okay, thanks. I’ll see what I can work out. Some examples would be helpful, though. The documentation makes a lot of assumptions about understanding of GTK’s internal process, the Vala docs are obviously just the C docs with a few bits altered with find-and-replace, and all I’ve found so far is one test in C that uses this signal.

Although, this bit seems suspiciously relevant to the behaviour I’m seeing:

In the event that the application is marked G_APPLICATION_HANDLES_COMMAND_LINE the “normal processing” will send the options dictionary to the primary instance…

Also, how does the handle_local_options approach integrate with adding Gtk.get_option_group (false) and Gst.init_get_option_group () to OptionContext? I’m assuming the previous dev had a reason for including them. Should it just happen in the class constructor of the GApplication?

(Also, --help seems to be handled centrally rather than by each app?)

I think I’m starting to understand some of this from bits and pieces. But it’s still confusing and I need a better explanation.

“local” options are options that are handled by the “local instance”, which means the one that you just ran rather than any pre-existing instances. Anything that just runs and prints should be using that, because it does the right thing and doesn’t cause the behaviour I first saw.

The previous dev was overriding command_line and doing all of the OptionEntry creation in there (including translation, in theory - although I think that was broken). Edit: That appears to be the equivalent of custom “remote” behaviour (i.e. handled in the ‘primary’ instance)

I’m now doing add_main_option_entries(OptionEntry[]) and .add_option_group (Gst.init_get_option_group ()) in the constructor, and then connecting to command_line (where I do cmd_line.get_options_dict()to got a GVariantDict that I can call contains() on) to do all of the “remote” argument handling - i.e. things that the ‘primary’ running instance need to handle, like popping up windows on command.

Now I’ve just got to work out how to get a normal start working again. The docs for run() suggest that I should just connect to activate (because it isn’t being passed a file name) but that’s not getting triggered. And it feels wrong to just do it as an else in command-line handling, because you’re supposed to return -1 if there’s more to do.

[Edit] Actually, one of the arguments can be a local option, so I’m handling arguments in both ways. That makes a bit more sense now, but would benefit from a tutorial (written by someone who understands it, rather than someone who has just learned it and might have it right but might have it wrong in an important way!).

So, without actually seeing the code it’s quite difficult to guess why things were done a certain way or where you can make changes without breaking everything.

If the code is open source, posting a link to the repo might get you better suggestions.

Also, there’s quite a bit of software written in Vala out there so while it’s not the most efficient way of learning it is helpful in cases such as this to look through a few and see how others have approached this.

And yes, the docs aren’t the greatest. I usually just use the C docs and only reference the Vala ones when something doesn’t work as expected.

It’s open source (Cawbird) but I’ve not committed anything yet so nothing to see for now :slightly_smiling_face:

The CommandLine class has one example in Vala (and the rest in C), but it explicitly overrides command_line, which is what got me into these issues in the first place.

The relevant parts of the code are:

  public Cawbird () {
    …
    OptionEntry[] options = new OptionEntry[7];
    options[0] = …
    …
    options[6] = {null};
    this.add_main_option_entries(options);
#if VIDEO
    this.add_option_group (Gst.init_get_option_group ());
#endif
    this.handle_local_options.connect(do_handle_local_options);
    this.command_line.connect(do_handle_global_options);
  }

  private int do_handle_local_options(GLib.VariantDict options) {
    if (options.contains("print-startup-accounts")) {
      …
      return 0;
    }
    return -1;
  }

  private int do_handle_global_options (GLib.ApplicationCommandLine cmd_line) {
    var name = null;
    var options = cmd_line.get_options_dict();
    if (options.contains("stop-service")) {
      …
      return 0;
    } else if (options.contains("start-service")) {
      …
      return 0;
    } else if (options.lookup("tweet", "s", ref name)) {
      open_startup_windows (name, null);        
      return 0;
    }
    else if (options.lookup("account", "s", ref name)) {
      open_startup_windows(null, name);
      return 0;
    }

    return -1;
  }

  public override void activate () {
    debug("Activating");
    …
  }

But it never prints “Activating”. I’ve also tried hooking in to the activate signal and that doesn’t get triggered either. The argument handling code gets called, but not the activate. As far as I can tell, the only reason it worked before is because the command_line override has an else block that always opened the main window.

[Edit] Also, any recommendations on Vala apps to look at would be greatly appreciated. It feels like I’ve mainly been finding JavaScript apps when I’ve looked around. Or some C. And the odd Vala app that is either really trivial and doesn’t do what I need to do or really complex and does all sorts of things with various APIs that I’ve not been able to trace through to understand what they’re doing.

1 Like

Ahah! By trudging through the C source code for GApplication, I found that setting HANDLES_COMMAND_LINE (which the app already had set) goes down a specific branch that doesn’t appear to call activate but calls your command_line() implementation instead. Which I no longer had. Removing that flag made it fire “Activate” on first load.

I think I’m sorted now, thanks :slightly_smiling_face:

(Relevant commit)

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.