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()