Application Based GNOME 3

Gnome 3 is an "application based" system, as opposed to "window based". This page will describe this change in terms of concerns developers, testers, and documenters need to be aware of. For more information about the user experience, see this design PDF, and this blog post.

Executive summary of application X changes that should be made for GNOME 3

  • If your application is written using GTK+, use the GtkApplication API

  • Ensure that your .desktop file Has StartupNotify=true and that your app handles startup notification correctly

  • If it ever makes sense to run multiple windows for your application, you need to have a "new window" menu item inside your application

What's an application?

There are several components that technically compose to form an "application" from the user experience point of view.

  • required Exactly one visible .desktop file

    • A .desktop file has at least Name, and Icon

  • required One or more processes

  • required One or more windows

    • on Wayland, each window has an application id
    • on X11, this may be a system tray icon
    • on X11, each window has a window icon, a window title, and (usually) a WM_CLASS property

  • One or more DBus names owned on the session bus

GNOME 2

First, we'll explain briefly at how the GNOME 2 UI displays application data.

  • The panel menu (Reference UI is Applications in the top left) displays .desktop files. When clicked, the panel looks in .desktop file for an Exec line giving the command to run. When that command is executed, it creates one or more processes. Those processes create one or more X windows.

  • A "window list" (reference UI is at the bottom), which lists X windows.

The crucial thing to grasp about GNOME 2 is that there is no default association between what's in the menu and what's listed at the bottom. For example, if you select the "Calculator" item twice, the Exec line from the .desktop file will be re-executed, and by default you'll get two Calculator windows, and two Calculator processes.

What's changing in GNOME 3

In GNOME 3, the primary UI component is the .desktop file. An application can only be running once (but can still have multiple windows). When clicking on an already-running application, GNOME Shell will simply let the user select between the already open windows (or if there's just one, take the user to it). It will not re-run the Exec line in the .desktop file. So without any code changes to (for example) Calculator, you will have at most one calculator window.

There are a variety of heuristics used to make the association between application windows and .desktop files for backwards compatibility. These heuristics are complex and still evolving, and will not be detailed here.

The window data (icon, and title) are de-emphasized in GNOME 3. Instead, the top area will contain a persistent display of your application's .desktop file name.

Startup Notification

One mechanism GNOME 3 uses to associate windows with applications is startup notification. Ensure that your application's .desktop file has StartupNotify=true.

The Application ID

When using the GtkApplication API it is important to choose a unique application identifier. This identifier is the preferred mechanism used to match the application's own name on the session bus, as well as the .desktop file of the application itself. This is especially important when using GNOME Shell as a Wayland display server, as opposed to an X11 compositor.

When creating a new GtkApplication instance, ensure that your application identifier is the same as the name of your .desktop file, minus the extension:

com.example.MyApplication.desktop:

Name=MyApp
Icon=com.example.MyApplication.png

myapp-main.c:

  GtkApplication *app =
    gtk_application_new ("com.example.MyApplication", G_APPLICATION_FLAGS_NONE);

Note: it's recommended that you use the same identifier for your application id, .desktop file, .service file, and application icon. This allows the Shell to match all your assets to the application, and prepares your application for sandboxing.

The WM_CLASS X Window property

To ensure the GNOME 3 Shell will track your application, you can also set the WM_CLASS X window property to be the same as your application's .desktop file name, without the .desktop extension (the desktop file should be lower-case).

The easiest way to achieve this is to have your application's process name match the .desktop file name, and ensure you use g_option_context_parse.

Example

myapp.desktop:

Name=MyApp
Exec=myapp

And inside myapp.c:

int main(int argc, char **argv)
{
  /* ... set up option parsing context, see GOption link above */
  if (!g_option_context_parse (context, &argc, &argv, &error)) {
    g_print ("option parsing failed: %s\n", error->message);
    exit (1);
  }
  ...

The way this works default in GTK+, the WM_CLASS property is derived from the g_set_prgname function. This function is called by g_option_context_parse. GTK+ will uppercase the first letter of the program name; you can ignore this.

Inspecting your program's WM_CLASS PROPERTY

From a Unix shell, type:

sleep 5; xprop WM_CLASS

5 seconds later your cursor will change to a + symbol; click on your window. You will see output like this:

WM_CLASS(STRING) = "chromium-browser", "Chromium-browser"

The second item here is the WM_CLASS. Chromium-browser (when lowercased) matches the chromium-browser.desktop as we want.

Binding

Many language bindings use a run time environment like gjs, seed, python, java, mono.exe. You should use the GtkApplication API just like you'd use it in C:

JavaScript

const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;

let app = new Gtk.Application({ application_id: 'com.example.MyApplication',
                                         flags: Gio.ApplicationFlags.NONE,
                              });
app.signal_connect('activate', on_app_activate);
app.run();

Python

from gi.repository import Gio
from gi.repository import Gtk

app = Gtk.Application(application_id='com.example.MyApplication',
                      flags=Gio.ApplicationFlags.NONE)
app.connect('activate', on_app_activate)
app.run()

Legacy bindings

If you are not yet using GtkApplication, you should call the equivalent of g_set_prgname() manually before you map your first window. In this example we assume your application ships a .desktop file named virt-manager.desktop:

JavaScript

const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;

// Other code 

GLib.set_prgname('virt-manager');

// Create application windows

Gtk.main();

PyGObject

from gi.repository import GLib

GLib.set_prgname('virt-manager')

# create application windows

GLib.main()

Projects/GnomeShell/ApplicationBased (last edited 2017-04-16 22:54:00 by EmmanueleBassi)