single instance (was Re: Minutes of the GTK+ meeting at GUADEC)



Hi,

Kristian Rietveld wrote:
GUnique was brought up as a possible feature for 2.14, a small discussion
about its usefulness as "stand-alone" feature emerged.  It doesn't make all
that much sense without an application window class, subsequently an
application window class doesn't make much sense without session
management.  Basically, you start questioning whether you want to introduce
a document based window/application model (like Cocoa has) -- getting this
done requires a good amount of work and discussion because it possibly
takes over a lot.  In the end we concluded that all of this would be more
useful as a higher level library including all these pieces.

I don't buy this really, though obviously I missed the conversation ;-)

I would expect a basic API like the following, though a high-level app window thing could be useful too.

void
handle_application_launch(GdkScreen *screen,
                          GtkLaunch *launch,
                          void      *data)
{
  GSList *urls;
  GtkWindow *document_window;

  urls = gtk_launch_get_property(launch, "urls");

  foreach (url in urls) {
    /* get a GtkWindow with startup notification stuff
     * from the launch properly configured; in real life
     * you'd probably have an application window subclass,
     * or a GtkAppWindow, or whatever, but those things
     * would build on this.
     */
    if (already have window for url) {
       document_window = window we already have;
       gtk_window_present_from_launch(document_window, launch);
    } else {
       document_window = gtk_window_new_for_launch(screen, launch);
       gtk_window_show (document_window);
    }
  }
}

void
handle_lost_single_instance(void *data)
{
  exit(0);
}

int
main(int argc, char **argv)
{
  gtk_init_single_instance(argc, argv, "org.gnome.MyAppBusName",
                           0, /* flags or something */
                           handle_application_launch, NULL,
                           handle_lost_single_instance, NULL);

  gtk_main();

  return 0;
}

Where init_single_instance will try to send the launch details to the bus name, which will be activated if necessary; if that fails, then it would try to get the bus name itself, and if it gets it, calls handle_application_launch within the current process. If the bus name is never obtained or is later lost, handle_lost_single_instance is called.

This can implement a --replace option automatically.

Details forwarded to the existing bus name would include env variables and command line options, as needed, there are some tricky bits to sort that out.

init_single_instance need not open the X display until it knows it needs to call handle_application_launch().

The whole connect-to-bus-and-wait-for-bus-name thing can be done asynchronously, so if the app wanted to do something else after init_single_instance then it could. gtk_init_single_instance() would "fire off" the bus name request, then return, so in gtk_main() either handle_application_launch or handle_lost_single_instance ends up getting called. This means that initial app startup and later opening of a new window uses *the same codepath*

Standard API could be offered to "simulate" a launch within the process, for e.g. a New... or Open menu item, which ideally ends up just calling handle_application_launch():
  gtk_launch_open_document(GdkScreen *screen, const char * bus_name,
                           const char *url);
or whatever. Heck this could also work fine with other apps, it could just happen to send the dbus message to itself if the current app owned the bus name.

Obviously there is some API fine-tuning, e.g. maybe a struct of handlers is better than a bunch of function args for gtk_init_single_instance, as one drop in the fine-tuning bucket. Or another example is that having to do the full gtk_init is a pain, ideally there's a gtk_launch_set_handlers() that is separate and the gtk_init thing is just a convenience API.

Anyway this is a ton of good functionality for an app, and something everyone is getting wrong hand-coding, so I would not wait for an AppWindow widget and stuff.

Havoc



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]