2009-10-15 23:28:29 +00:00
|
|
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
2009-11-24 14:07:40 +00:00
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
2009-10-15 23:28:29 +00:00
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#include <X11/Xlib.h>
|
|
|
|
#include <X11/Xatom.h>
|
|
|
|
#include <gdk/gdk.h>
|
|
|
|
#include <gdk/gdkx.h>
|
2011-03-05 15:49:24 +00:00
|
|
|
#include <meta/display.h>
|
|
|
|
#include <meta/group.h>
|
|
|
|
#include <meta/util.h>
|
|
|
|
#include <meta/window.h>
|
2009-10-15 23:28:29 +00:00
|
|
|
|
|
|
|
#define SN_API_NOT_YET_FROZEN 1
|
|
|
|
#include <libsn/sn.h>
|
|
|
|
|
2010-06-07 20:31:30 +00:00
|
|
|
#include "shell-window-tracker-private.h"
|
2009-10-15 23:28:29 +00:00
|
|
|
#include "shell-app-private.h"
|
|
|
|
#include "shell-global.h"
|
2010-10-31 19:39:32 +00:00
|
|
|
#include "st.h"
|
2009-10-15 23:28:29 +00:00
|
|
|
|
|
|
|
/* This file includes modified code from
|
|
|
|
* desktop-data-engine/engine-dbus/hippo-application-monitor.c
|
|
|
|
* in the functions collecting application usage data.
|
|
|
|
* Written by Owen Taylor, originally licensed under LGPL 2.1.
|
|
|
|
* Copyright Red Hat, Inc. 2006-2008
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* SECTION:shell-window-tracker
|
|
|
|
* @short_description: Associate windows with applications
|
|
|
|
*
|
|
|
|
* Maintains a mapping from windows to applications (.desktop file ids).
|
|
|
|
* It currently implements this with some heuristics on the WM_CLASS X11
|
|
|
|
* property (and some static override regexps); in the future, we want to
|
|
|
|
* have it also track through startup-notification.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct _ShellWindowTracker
|
|
|
|
{
|
|
|
|
GObject parent;
|
|
|
|
|
|
|
|
ShellApp *focus_app;
|
|
|
|
|
|
|
|
/* <MetaWindow * window, ShellApp *app> */
|
|
|
|
GHashTable *window_to_app;
|
|
|
|
|
2010-12-21 02:06:03 +00:00
|
|
|
/* <int, ShellApp *app> */
|
|
|
|
GHashTable *launched_pid_to_app;
|
2009-10-15 23:28:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
G_DEFINE_TYPE (ShellWindowTracker, shell_window_tracker, G_TYPE_OBJECT);
|
|
|
|
|
|
|
|
enum {
|
|
|
|
PROP_0,
|
|
|
|
PROP_FOCUS_APP
|
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
|
|
|
STARTUP_SEQUENCE_CHANGED,
|
2010-04-20 18:47:45 +00:00
|
|
|
TRACKED_WINDOWS_CHANGED,
|
2009-10-15 23:28:29 +00:00
|
|
|
|
|
|
|
LAST_SIGNAL
|
|
|
|
};
|
|
|
|
|
|
|
|
static guint signals[LAST_SIGNAL] = { 0 };
|
|
|
|
|
|
|
|
static void shell_window_tracker_finalize (GObject *object);
|
Major ShellApp API cleanup, startup notification, window focus handling
This patch combines several high level changes which are conceptually
independent but in practice rather intertwined.
* Add a "state" property to ShellApp which reflects whether it's
stopped, starting, or started. This will allow us to later clean
up all the callers that are using ".get_windows().length > 0" as
a proxy for this property
* Replace shell_app_launch with shell_app_activate and shell_app_open_new_window
A lot of code was calling .launch, but it's signficantly clearer
if we call this ".open_new_window()", and later if we gain the ability
to call into an application's menu, we can implement this correctly rather
than trying to update all .launch callers.
* Because ShellApp now has a "starting" state, rebase panel.js on top of
this so that when we get a startup-notification sequence for an app
and transition it to starting, it becomes the focus app, and panel.js
cleanly just tracks the focus app, rather than bouncing between SN
sequences. This removes display of non-app startup sequences, which
I consider an acceptable action in light of the committed changes
to startup-notification and GTK+.
https://bugzilla.gnome.org/show_bug.cgi?id=614755
2010-04-03 18:07:44 +00:00
|
|
|
static void set_focus_app (ShellWindowTracker *tracker,
|
|
|
|
ShellApp *new_focus_app);
|
2009-10-15 23:28:29 +00:00
|
|
|
static void on_focus_window_changed (MetaDisplay *display, GParamSpec *spec, ShellWindowTracker *tracker);
|
|
|
|
|
2011-08-11 10:37:24 +00:00
|
|
|
static void track_window (ShellWindowTracker *tracker, MetaWindow *window);
|
|
|
|
static void disassociate_window (ShellWindowTracker *tracker, MetaWindow *window);
|
2009-10-15 23:28:29 +00:00
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
shell_window_tracker_get_property (GObject *gobject,
|
|
|
|
guint prop_id,
|
|
|
|
GValue *value,
|
|
|
|
GParamSpec *pspec)
|
|
|
|
{
|
|
|
|
ShellWindowTracker *tracker = SHELL_WINDOW_TRACKER (gobject);
|
|
|
|
|
|
|
|
switch (prop_id)
|
|
|
|
{
|
|
|
|
case PROP_FOCUS_APP:
|
|
|
|
g_value_set_object (value, tracker->focus_app);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
shell_window_tracker_class_init (ShellWindowTrackerClass *klass)
|
|
|
|
{
|
|
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
|
|
|
|
gobject_class->get_property = shell_window_tracker_get_property;
|
|
|
|
gobject_class->finalize = shell_window_tracker_finalize;
|
|
|
|
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
|
|
PROP_FOCUS_APP,
|
|
|
|
g_param_spec_object ("focus-app",
|
|
|
|
"Focus App",
|
|
|
|
"Focused application",
|
|
|
|
SHELL_TYPE_APP,
|
|
|
|
G_PARAM_READABLE));
|
|
|
|
|
|
|
|
signals[STARTUP_SEQUENCE_CHANGED] = g_signal_new ("startup-sequence-changed",
|
|
|
|
SHELL_TYPE_WINDOW_TRACKER,
|
|
|
|
G_SIGNAL_RUN_LAST,
|
|
|
|
0,
|
2011-10-18 22:19:32 +00:00
|
|
|
NULL, NULL, NULL,
|
2009-10-15 23:28:29 +00:00
|
|
|
G_TYPE_NONE, 1, SHELL_TYPE_STARTUP_SEQUENCE);
|
2010-04-20 18:47:45 +00:00
|
|
|
signals[TRACKED_WINDOWS_CHANGED] = g_signal_new ("tracked-windows-changed",
|
|
|
|
SHELL_TYPE_WINDOW_TRACKER,
|
|
|
|
G_SIGNAL_RUN_LAST,
|
|
|
|
0,
|
2011-10-18 22:19:32 +00:00
|
|
|
NULL, NULL, NULL,
|
2010-04-20 18:47:45 +00:00
|
|
|
G_TYPE_NONE, 0);
|
2009-10-15 23:28:29 +00:00
|
|
|
}
|
|
|
|
|
2014-01-04 16:33:20 +00:00
|
|
|
/*
|
2012-04-06 18:02:04 +00:00
|
|
|
* get_app_from_window_wmclass:
|
|
|
|
*
|
|
|
|
* Looks only at the given window, and attempts to determine
|
|
|
|
* an application based on WM_CLASS. If one can't be determined,
|
|
|
|
* return %NULL.
|
|
|
|
*
|
|
|
|
* Return value: (transfer full): A newly-referenced #ShellApp, or %NULL
|
|
|
|
*/
|
|
|
|
static ShellApp *
|
|
|
|
get_app_from_window_wmclass (MetaWindow *window)
|
|
|
|
{
|
|
|
|
ShellApp *app;
|
|
|
|
ShellAppSystem *appsys;
|
|
|
|
const char *wm_class;
|
|
|
|
const char *wm_instance;
|
|
|
|
|
|
|
|
appsys = shell_app_system_get_default ();
|
|
|
|
|
|
|
|
/* Notes on the heuristics used here:
|
|
|
|
much of the complexity here comes from the desire to support
|
|
|
|
Chrome apps.
|
|
|
|
|
|
|
|
From https://bugzilla.gnome.org/show_bug.cgi?id=673657#c13
|
|
|
|
|
|
|
|
Currently chrome sets WM_CLASS as follows (the first string is the 'instance',
|
|
|
|
the second one is the 'class':
|
|
|
|
|
|
|
|
For the normal browser:
|
|
|
|
WM_CLASS(STRING) = "chromium", "Chromium"
|
|
|
|
|
|
|
|
For a bookmarked page (through 'Tools -> Create application shortcuts')
|
|
|
|
WM_CLASS(STRING) = "wiki.gnome.org__GnomeShell_ApplicationBased", "Chromium"
|
|
|
|
|
|
|
|
For an application from the chrome store (with a .desktop file created through
|
|
|
|
right click, "Create shortcuts" from Chrome's apps overview)
|
|
|
|
WM_CLASS(STRING) = "crx_blpcfgokakmgnkcojhhkbfbldkacnbeo", "Chromium"
|
|
|
|
|
|
|
|
The .desktop file has a matching StartupWMClass, but the name differs, e.g. for
|
|
|
|
the store app (youtube) there is
|
|
|
|
|
|
|
|
.local/share/applications/chrome-blpcfgokakmgnkcojhhkbfbldkacnbeo-Default.desktop
|
|
|
|
|
|
|
|
with
|
|
|
|
|
|
|
|
StartupWMClass=crx_blpcfgokakmgnkcojhhkbfbldkacnbeo
|
|
|
|
|
2013-08-08 08:40:01 +00:00
|
|
|
Note that chromium (but not google-chrome!) includes a StartupWMClass=chromium
|
|
|
|
in their .desktop file, so we must match the instance first.
|
|
|
|
|
|
|
|
Also note that in the good case (regular gtk+ app without hacks), instance and
|
|
|
|
class are the same except for case and there is no StartupWMClass at all.
|
|
|
|
*/
|
2012-04-06 18:02:04 +00:00
|
|
|
|
2013-08-08 08:40:01 +00:00
|
|
|
/* first try a match from WM_CLASS (instance part) to StartupWMClass */
|
2012-04-06 18:02:04 +00:00
|
|
|
wm_instance = meta_window_get_wm_class_instance (window);
|
|
|
|
app = shell_app_system_lookup_startup_wmclass (appsys, wm_instance);
|
|
|
|
if (app != NULL)
|
|
|
|
return g_object_ref (app);
|
|
|
|
|
2013-08-08 08:40:01 +00:00
|
|
|
/* then try a match from WM_CLASS to StartupWMClass */
|
|
|
|
wm_class = meta_window_get_wm_class (window);
|
|
|
|
app = shell_app_system_lookup_startup_wmclass (appsys, wm_class);
|
2012-04-06 18:02:04 +00:00
|
|
|
if (app != NULL)
|
|
|
|
return g_object_ref (app);
|
|
|
|
|
2013-08-08 08:40:01 +00:00
|
|
|
/* then try a match from WM_CLASS (instance part) to .desktop */
|
2012-04-06 18:02:04 +00:00
|
|
|
app = shell_app_system_lookup_desktop_wmclass (appsys, wm_instance);
|
|
|
|
if (app != NULL)
|
|
|
|
return g_object_ref (app);
|
|
|
|
|
2013-08-08 08:40:01 +00:00
|
|
|
/* finally, try a match from WM_CLASS to .desktop */
|
|
|
|
app = shell_app_system_lookup_desktop_wmclass (appsys, wm_class);
|
|
|
|
if (app != NULL)
|
|
|
|
return g_object_ref (app);
|
|
|
|
|
2012-04-06 18:02:04 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-01-04 16:33:20 +00:00
|
|
|
/*
|
2014-01-04 15:49:34 +00:00
|
|
|
* get_app_from_gapplication_id:
|
|
|
|
* @monitor: a #ShellWindowTracker
|
|
|
|
* @window: a #MetaWindow
|
|
|
|
*
|
|
|
|
* Looks only at the given window, and attempts to determine
|
|
|
|
* an application based on _GTK_APPLICATION_ID. If one can't be determined,
|
|
|
|
* return %NULL.
|
|
|
|
*
|
|
|
|
* Return value: (transfer full): A newly-referenced #ShellApp, or %NULL
|
|
|
|
*/
|
2013-08-18 16:41:22 +00:00
|
|
|
static ShellApp *
|
|
|
|
get_app_from_gapplication_id (MetaWindow *window)
|
|
|
|
{
|
|
|
|
ShellApp *app;
|
|
|
|
ShellAppSystem *appsys;
|
|
|
|
const char *id;
|
|
|
|
char *desktop_file;
|
|
|
|
|
|
|
|
appsys = shell_app_system_get_default ();
|
|
|
|
|
|
|
|
id = meta_window_get_gtk_application_id (window);
|
|
|
|
if (!id)
|
2014-01-04 15:49:34 +00:00
|
|
|
return NULL;
|
2013-08-18 16:41:22 +00:00
|
|
|
|
|
|
|
desktop_file = g_strconcat (id, ".desktop", NULL);
|
|
|
|
app = shell_app_system_lookup_app (appsys, desktop_file);
|
2014-01-04 15:49:34 +00:00
|
|
|
if (app)
|
|
|
|
g_object_ref (app);
|
2013-08-18 16:41:22 +00:00
|
|
|
|
|
|
|
g_free (desktop_file);
|
|
|
|
return app;
|
|
|
|
}
|
|
|
|
|
2014-01-04 16:33:20 +00:00
|
|
|
/*
|
2010-02-24 16:29:44 +00:00
|
|
|
* get_app_from_window_group:
|
|
|
|
* @monitor: a #ShellWindowTracker
|
|
|
|
* @window: a #MetaWindow
|
2009-10-15 23:28:29 +00:00
|
|
|
*
|
2010-02-24 16:29:44 +00:00
|
|
|
* Check other windows in the group for @window to see if we have
|
|
|
|
* an application for one of them.
|
|
|
|
*
|
|
|
|
* Return value: (transfer full): A newly-referenced #ShellApp, or %NULL
|
2009-10-15 23:28:29 +00:00
|
|
|
*/
|
2010-02-24 16:29:44 +00:00
|
|
|
static ShellApp*
|
2011-08-11 10:37:24 +00:00
|
|
|
get_app_from_window_group (ShellWindowTracker *tracker,
|
2010-02-24 16:29:44 +00:00
|
|
|
MetaWindow *window)
|
2009-10-15 23:28:29 +00:00
|
|
|
{
|
|
|
|
ShellApp *result;
|
|
|
|
GSList *group_windows;
|
|
|
|
MetaGroup *group;
|
|
|
|
GSList *iter;
|
|
|
|
|
|
|
|
group = meta_window_get_group (window);
|
|
|
|
if (group == NULL)
|
2010-02-24 16:29:44 +00:00
|
|
|
return NULL;
|
2009-10-15 23:28:29 +00:00
|
|
|
|
2010-02-24 16:29:44 +00:00
|
|
|
group_windows = meta_group_list_windows (group);
|
2009-10-15 23:28:29 +00:00
|
|
|
|
|
|
|
result = NULL;
|
|
|
|
/* Try finding a window in the group of type NORMAL; if we
|
|
|
|
* succeed, use that as our source. */
|
|
|
|
for (iter = group_windows; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
MetaWindow *group_window = iter->data;
|
|
|
|
|
|
|
|
if (meta_window_get_window_type (group_window) != META_WINDOW_NORMAL)
|
|
|
|
continue;
|
|
|
|
|
2011-08-11 10:37:24 +00:00
|
|
|
result = g_hash_table_lookup (tracker->window_to_app, group_window);
|
2010-02-24 16:29:44 +00:00
|
|
|
if (result)
|
|
|
|
break;
|
2009-10-15 23:28:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
g_slist_free (group_windows);
|
|
|
|
|
2010-02-24 16:29:44 +00:00
|
|
|
if (result)
|
|
|
|
g_object_ref (result);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2014-01-04 16:33:20 +00:00
|
|
|
/*
|
2011-02-19 15:19:31 +00:00
|
|
|
* get_app_from_window_pid:
|
2011-08-11 10:37:24 +00:00
|
|
|
* @tracker: a #ShellWindowTracker
|
2011-02-19 15:19:31 +00:00
|
|
|
* @window: a #MetaWindow
|
|
|
|
*
|
|
|
|
* Check if the pid associated with @window corresponds to an
|
|
|
|
* application we launched.
|
|
|
|
*
|
|
|
|
* Return value: (transfer full): A newly-referenced #ShellApp, or %NULL
|
|
|
|
*/
|
2010-12-21 02:06:03 +00:00
|
|
|
static ShellApp *
|
|
|
|
get_app_from_window_pid (ShellWindowTracker *tracker,
|
|
|
|
MetaWindow *window)
|
|
|
|
{
|
2011-02-19 15:19:31 +00:00
|
|
|
ShellApp *result;
|
2010-12-21 02:06:03 +00:00
|
|
|
int pid;
|
|
|
|
|
|
|
|
if (meta_window_is_remote (window))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
pid = meta_window_get_pid (window);
|
|
|
|
|
2011-02-08 19:37:22 +00:00
|
|
|
if (pid == -1)
|
|
|
|
return NULL;
|
|
|
|
|
2011-02-19 15:19:31 +00:00
|
|
|
result = g_hash_table_lookup (tracker->launched_pid_to_app, GINT_TO_POINTER (pid));
|
|
|
|
if (result != NULL)
|
|
|
|
g_object_ref (result);
|
|
|
|
|
|
|
|
return result;
|
2010-12-21 02:06:03 +00:00
|
|
|
}
|
|
|
|
|
2010-02-24 16:29:44 +00:00
|
|
|
/**
|
|
|
|
* get_app_for_window:
|
|
|
|
*
|
|
|
|
* Determines the application associated with a window, using
|
|
|
|
* all available information such as the window's MetaGroup,
|
|
|
|
* and what we know about other windows.
|
2011-02-19 15:19:31 +00:00
|
|
|
*
|
|
|
|
* Returns: (transfer full): a #ShellApp, or NULL if none is found
|
2010-02-24 16:29:44 +00:00
|
|
|
*/
|
|
|
|
static ShellApp *
|
2011-08-11 10:37:24 +00:00
|
|
|
get_app_for_window (ShellWindowTracker *tracker,
|
Kill off ShellAppInfo, move into ShellApp
This dramatically thins down and sanitizes the application code.
The ShellAppSystem changes in a number of ways:
* Preferences are special cased more explicitly; they aren't apps,
they're shortcuts for an app), and we don't have many of them, so
don't need e.g. the optimizations in ShellAppSystem for searching.
* get_app() changes to lookup_app() and returns null if an app isn't
found. The semantics where it tried to find the .desktop file
if we didn't know about it were just broken; I am pretty sure no
caller needs this, and if they do we'll fix them.
* ShellAppSystem maintains two indexes on apps (by desktop file id
and by GMenuTreeEntry), but is no longer in the business of
dealing with GMenuTree as far as hierarchy and categories go. That
is moved up into js/ui/appDisplay.js. Actually, it flattens both
apps and settings.
Also, ShellWindowTracker is now the sole reference-owner for
window-backed apps. We still do the weird "window:0x1234beef" id
for these apps, but a reference is not stored in ShellAppSystem.
The js/ui/appDisplay.js code is rewritten, and sucks a lot less.
Variable names are clearer:
_apps -> _appIcons
_filterApp -> _visibleApps
_filters -> _categoryBox
Similarly for function names. We no longer call (for every app) a
recursive lookup in GMenuTree to see if it's in a particular section
on every category switch; it's all cached.
NOTE - this intentionally reverts the incremental loading code from
commit 7813c5b93f6bcde8c4beae286e82bfc472b2b656. It's fast enough
here without that.
https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-04-21 17:35:01 +00:00
|
|
|
MetaWindow *window)
|
2010-02-24 16:29:44 +00:00
|
|
|
{
|
Kill off ShellAppInfo, move into ShellApp
This dramatically thins down and sanitizes the application code.
The ShellAppSystem changes in a number of ways:
* Preferences are special cased more explicitly; they aren't apps,
they're shortcuts for an app), and we don't have many of them, so
don't need e.g. the optimizations in ShellAppSystem for searching.
* get_app() changes to lookup_app() and returns null if an app isn't
found. The semantics where it tried to find the .desktop file
if we didn't know about it were just broken; I am pretty sure no
caller needs this, and if they do we'll fix them.
* ShellAppSystem maintains two indexes on apps (by desktop file id
and by GMenuTreeEntry), but is no longer in the business of
dealing with GMenuTree as far as hierarchy and categories go. That
is moved up into js/ui/appDisplay.js. Actually, it flattens both
apps and settings.
Also, ShellWindowTracker is now the sole reference-owner for
window-backed apps. We still do the weird "window:0x1234beef" id
for these apps, but a reference is not stored in ShellAppSystem.
The js/ui/appDisplay.js code is rewritten, and sucks a lot less.
Variable names are clearer:
_apps -> _appIcons
_filterApp -> _visibleApps
_filters -> _categoryBox
Similarly for function names. We no longer call (for every app) a
recursive lookup in GMenuTree to see if it's in a particular section
on every category switch; it's all cached.
NOTE - this intentionally reverts the incremental loading code from
commit 7813c5b93f6bcde8c4beae286e82bfc472b2b656. It's fast enough
here without that.
https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-04-21 17:35:01 +00:00
|
|
|
ShellApp *result = NULL;
|
2014-01-17 15:54:13 +00:00
|
|
|
MetaWindow *transient_for;
|
2010-02-24 16:29:44 +00:00
|
|
|
const char *startup_id;
|
|
|
|
|
2014-01-17 15:54:13 +00:00
|
|
|
transient_for = meta_window_get_transient_for (window);
|
|
|
|
if (transient_for != NULL)
|
|
|
|
return get_app_for_window (tracker, transient_for);
|
|
|
|
|
2010-02-24 16:29:44 +00:00
|
|
|
/* First, we check whether we already know about this window,
|
|
|
|
* if so, just return that.
|
|
|
|
*/
|
Kill off ShellAppInfo, move into ShellApp
This dramatically thins down and sanitizes the application code.
The ShellAppSystem changes in a number of ways:
* Preferences are special cased more explicitly; they aren't apps,
they're shortcuts for an app), and we don't have many of them, so
don't need e.g. the optimizations in ShellAppSystem for searching.
* get_app() changes to lookup_app() and returns null if an app isn't
found. The semantics where it tried to find the .desktop file
if we didn't know about it were just broken; I am pretty sure no
caller needs this, and if they do we'll fix them.
* ShellAppSystem maintains two indexes on apps (by desktop file id
and by GMenuTreeEntry), but is no longer in the business of
dealing with GMenuTree as far as hierarchy and categories go. That
is moved up into js/ui/appDisplay.js. Actually, it flattens both
apps and settings.
Also, ShellWindowTracker is now the sole reference-owner for
window-backed apps. We still do the weird "window:0x1234beef" id
for these apps, but a reference is not stored in ShellAppSystem.
The js/ui/appDisplay.js code is rewritten, and sucks a lot less.
Variable names are clearer:
_apps -> _appIcons
_filterApp -> _visibleApps
_filters -> _categoryBox
Similarly for function names. We no longer call (for every app) a
recursive lookup in GMenuTree to see if it's in a particular section
on every category switch; it's all cached.
NOTE - this intentionally reverts the incremental loading code from
commit 7813c5b93f6bcde8c4beae286e82bfc472b2b656. It's fast enough
here without that.
https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-04-21 17:35:01 +00:00
|
|
|
if (meta_window_get_window_type (window) == META_WINDOW_NORMAL
|
|
|
|
|| meta_window_is_remote (window))
|
2010-02-24 16:29:44 +00:00
|
|
|
{
|
2011-08-11 10:37:24 +00:00
|
|
|
result = g_hash_table_lookup (tracker->window_to_app, window);
|
2010-02-24 16:29:44 +00:00
|
|
|
if (result != NULL)
|
|
|
|
{
|
|
|
|
g_object_ref (result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Kill off ShellAppInfo, move into ShellApp
This dramatically thins down and sanitizes the application code.
The ShellAppSystem changes in a number of ways:
* Preferences are special cased more explicitly; they aren't apps,
they're shortcuts for an app), and we don't have many of them, so
don't need e.g. the optimizations in ShellAppSystem for searching.
* get_app() changes to lookup_app() and returns null if an app isn't
found. The semantics where it tried to find the .desktop file
if we didn't know about it were just broken; I am pretty sure no
caller needs this, and if they do we'll fix them.
* ShellAppSystem maintains two indexes on apps (by desktop file id
and by GMenuTreeEntry), but is no longer in the business of
dealing with GMenuTree as far as hierarchy and categories go. That
is moved up into js/ui/appDisplay.js. Actually, it flattens both
apps and settings.
Also, ShellWindowTracker is now the sole reference-owner for
window-backed apps. We still do the weird "window:0x1234beef" id
for these apps, but a reference is not stored in ShellAppSystem.
The js/ui/appDisplay.js code is rewritten, and sucks a lot less.
Variable names are clearer:
_apps -> _appIcons
_filterApp -> _visibleApps
_filters -> _categoryBox
Similarly for function names. We no longer call (for every app) a
recursive lookup in GMenuTree to see if it's in a particular section
on every category switch; it's all cached.
NOTE - this intentionally reverts the incremental loading code from
commit 7813c5b93f6bcde8c4beae286e82bfc472b2b656. It's fast enough
here without that.
https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-04-21 17:35:01 +00:00
|
|
|
if (meta_window_is_remote (window))
|
|
|
|
return _shell_app_new_for_window (window);
|
|
|
|
|
2013-08-18 16:41:22 +00:00
|
|
|
/* Check if the window has a GApplication ID attached; this is
|
|
|
|
* canonical if it does
|
|
|
|
*/
|
|
|
|
result = get_app_from_gapplication_id (window);
|
|
|
|
if (result != NULL)
|
|
|
|
return result;
|
|
|
|
|
2011-04-05 16:48:44 +00:00
|
|
|
/* Check if the app's WM_CLASS specifies an app; this is
|
|
|
|
* canonical if it does.
|
|
|
|
*/
|
2012-04-06 18:02:04 +00:00
|
|
|
result = get_app_from_window_wmclass (window);
|
2010-12-21 02:06:03 +00:00
|
|
|
if (result != NULL)
|
2013-08-07 10:54:14 +00:00
|
|
|
return result;
|
2010-12-21 02:06:03 +00:00
|
|
|
|
2011-08-11 10:37:24 +00:00
|
|
|
result = get_app_from_window_pid (tracker, window);
|
2009-10-15 23:28:29 +00:00
|
|
|
if (result != NULL)
|
2010-02-24 16:29:44 +00:00
|
|
|
return result;
|
|
|
|
|
|
|
|
/* Now we check whether we have a match through startup-notification */
|
|
|
|
startup_id = meta_window_get_startup_id (window);
|
|
|
|
if (startup_id)
|
2009-10-15 23:28:29 +00:00
|
|
|
{
|
2010-02-24 16:29:44 +00:00
|
|
|
GSList *iter, *sequences;
|
|
|
|
|
2011-08-11 10:37:24 +00:00
|
|
|
sequences = shell_window_tracker_get_startup_sequences (tracker);
|
2010-02-24 16:29:44 +00:00
|
|
|
for (iter = sequences; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
ShellStartupSequence *sequence = iter->data;
|
|
|
|
const char *id = shell_startup_sequence_get_id (sequence);
|
|
|
|
if (strcmp (id, startup_id) != 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
result = shell_startup_sequence_get_app (sequence);
|
|
|
|
if (result)
|
Kill off ShellAppInfo, move into ShellApp
This dramatically thins down and sanitizes the application code.
The ShellAppSystem changes in a number of ways:
* Preferences are special cased more explicitly; they aren't apps,
they're shortcuts for an app), and we don't have many of them, so
don't need e.g. the optimizations in ShellAppSystem for searching.
* get_app() changes to lookup_app() and returns null if an app isn't
found. The semantics where it tried to find the .desktop file
if we didn't know about it were just broken; I am pretty sure no
caller needs this, and if they do we'll fix them.
* ShellAppSystem maintains two indexes on apps (by desktop file id
and by GMenuTreeEntry), but is no longer in the business of
dealing with GMenuTree as far as hierarchy and categories go. That
is moved up into js/ui/appDisplay.js. Actually, it flattens both
apps and settings.
Also, ShellWindowTracker is now the sole reference-owner for
window-backed apps. We still do the weird "window:0x1234beef" id
for these apps, but a reference is not stored in ShellAppSystem.
The js/ui/appDisplay.js code is rewritten, and sucks a lot less.
Variable names are clearer:
_apps -> _appIcons
_filterApp -> _visibleApps
_filters -> _categoryBox
Similarly for function names. We no longer call (for every app) a
recursive lookup in GMenuTree to see if it's in a particular section
on every category switch; it's all cached.
NOTE - this intentionally reverts the incremental loading code from
commit 7813c5b93f6bcde8c4beae286e82bfc472b2b656. It's fast enough
here without that.
https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-04-21 17:35:01 +00:00
|
|
|
{
|
|
|
|
result = g_object_ref (result);
|
|
|
|
break;
|
|
|
|
}
|
2010-02-24 16:29:44 +00:00
|
|
|
}
|
2009-10-15 23:28:29 +00:00
|
|
|
}
|
|
|
|
|
2010-02-24 16:29:44 +00:00
|
|
|
/* If we didn't get a startup-notification match, see if we matched
|
|
|
|
* any other windows in the group.
|
|
|
|
*/
|
|
|
|
if (result == NULL)
|
2011-08-11 10:37:24 +00:00
|
|
|
result = get_app_from_window_group (tracker, window);
|
2010-02-24 16:29:44 +00:00
|
|
|
|
|
|
|
/* Our last resort - we create a fake app from the window */
|
|
|
|
if (result == NULL)
|
Kill off ShellAppInfo, move into ShellApp
This dramatically thins down and sanitizes the application code.
The ShellAppSystem changes in a number of ways:
* Preferences are special cased more explicitly; they aren't apps,
they're shortcuts for an app), and we don't have many of them, so
don't need e.g. the optimizations in ShellAppSystem for searching.
* get_app() changes to lookup_app() and returns null if an app isn't
found. The semantics where it tried to find the .desktop file
if we didn't know about it were just broken; I am pretty sure no
caller needs this, and if they do we'll fix them.
* ShellAppSystem maintains two indexes on apps (by desktop file id
and by GMenuTreeEntry), but is no longer in the business of
dealing with GMenuTree as far as hierarchy and categories go. That
is moved up into js/ui/appDisplay.js. Actually, it flattens both
apps and settings.
Also, ShellWindowTracker is now the sole reference-owner for
window-backed apps. We still do the weird "window:0x1234beef" id
for these apps, but a reference is not stored in ShellAppSystem.
The js/ui/appDisplay.js code is rewritten, and sucks a lot less.
Variable names are clearer:
_apps -> _appIcons
_filterApp -> _visibleApps
_filters -> _categoryBox
Similarly for function names. We no longer call (for every app) a
recursive lookup in GMenuTree to see if it's in a particular section
on every category switch; it's all cached.
NOTE - this intentionally reverts the incremental loading code from
commit 7813c5b93f6bcde8c4beae286e82bfc472b2b656. It's fast enough
here without that.
https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-04-21 17:35:01 +00:00
|
|
|
result = _shell_app_new_for_window (window);
|
2010-02-24 16:29:44 +00:00
|
|
|
|
|
|
|
return result;
|
2009-10-15 23:28:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const char *
|
|
|
|
_shell_window_tracker_get_app_context (ShellWindowTracker *tracker, ShellApp *app)
|
|
|
|
{
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
2011-05-24 23:42:07 +00:00
|
|
|
static void
|
|
|
|
update_focus_app (ShellWindowTracker *self)
|
|
|
|
{
|
|
|
|
MetaWindow *new_focus_win;
|
|
|
|
ShellApp *new_focus_app;
|
|
|
|
|
2011-07-13 16:34:31 +00:00
|
|
|
new_focus_win = meta_display_get_focus_window (shell_global_get_display (shell_global_get ()));
|
2014-01-24 15:26:49 +00:00
|
|
|
|
|
|
|
/* we only consider an app focused if the focus window can be clearly
|
|
|
|
* associated with a running app; this is the case if the focus window
|
|
|
|
* or one of its parents is visible in the taskbar, e.g.
|
|
|
|
* - 'nautilus' should appear focused when its about dialog has focus
|
|
|
|
* - 'nautilus' should not appear focused when the DESKTOP has focus
|
|
|
|
*/
|
|
|
|
while (new_focus_win && meta_window_is_skip_taskbar (new_focus_win))
|
|
|
|
new_focus_win = meta_window_get_transient_for (new_focus_win);
|
|
|
|
|
2011-05-24 23:42:07 +00:00
|
|
|
new_focus_app = new_focus_win ? shell_window_tracker_get_window_app (self, new_focus_win) : NULL;
|
|
|
|
|
2011-12-15 06:25:35 +00:00
|
|
|
if (new_focus_app)
|
2012-05-17 16:18:53 +00:00
|
|
|
{
|
|
|
|
shell_app_update_window_actions (new_focus_app, new_focus_win);
|
|
|
|
shell_app_update_app_menu (new_focus_app, new_focus_win);
|
|
|
|
}
|
2011-12-15 06:25:35 +00:00
|
|
|
|
2011-05-24 23:42:07 +00:00
|
|
|
set_focus_app (self, new_focus_app);
|
2014-01-24 15:11:49 +00:00
|
|
|
|
|
|
|
g_clear_object (&new_focus_app);
|
2011-05-24 23:42:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
on_wm_class_changed (MetaWindow *window,
|
|
|
|
GParamSpec *pspec,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
ShellWindowTracker *self = SHELL_WINDOW_TRACKER (user_data);
|
|
|
|
|
|
|
|
/* It's simplest to just treat this as a remove + add. */
|
|
|
|
disassociate_window (self, window);
|
|
|
|
track_window (self, window);
|
2014-01-24 17:23:48 +00:00
|
|
|
/* also just recalculate the focused app, in case it was the focused
|
2011-05-24 23:42:07 +00:00
|
|
|
window that changed */
|
|
|
|
update_focus_app (self);
|
|
|
|
}
|
|
|
|
|
2009-10-15 23:28:29 +00:00
|
|
|
static void
|
|
|
|
track_window (ShellWindowTracker *self,
|
|
|
|
MetaWindow *window)
|
|
|
|
{
|
|
|
|
ShellApp *app;
|
|
|
|
|
|
|
|
app = get_app_for_window (self, window);
|
|
|
|
if (!app)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* At this point we've stored the association from window -> application */
|
|
|
|
g_hash_table_insert (self->window_to_app, window, app);
|
|
|
|
|
2011-05-24 23:42:07 +00:00
|
|
|
g_signal_connect (window, "notify::wm-class", G_CALLBACK (on_wm_class_changed), self);
|
|
|
|
|
2009-10-15 23:28:29 +00:00
|
|
|
_shell_app_add_window (app, window);
|
|
|
|
|
2010-04-20 18:47:45 +00:00
|
|
|
g_signal_emit (self, signals[TRACKED_WINDOWS_CHANGED], 0);
|
2009-10-15 23:28:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
shell_window_tracker_on_window_added (MetaWorkspace *workspace,
|
|
|
|
MetaWindow *window,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
ShellWindowTracker *self = SHELL_WINDOW_TRACKER (user_data);
|
|
|
|
|
|
|
|
track_window (self, window);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
disassociate_window (ShellWindowTracker *self,
|
|
|
|
MetaWindow *window)
|
|
|
|
{
|
|
|
|
ShellApp *app;
|
|
|
|
|
|
|
|
app = g_hash_table_lookup (self->window_to_app, window);
|
|
|
|
if (!app)
|
|
|
|
return;
|
|
|
|
|
|
|
|
g_object_ref (app);
|
|
|
|
|
|
|
|
g_hash_table_remove (self->window_to_app, window);
|
|
|
|
|
2013-12-04 16:26:01 +00:00
|
|
|
_shell_app_remove_window (app, window);
|
2014-10-13 00:20:12 +00:00
|
|
|
g_signal_handlers_disconnect_by_func (window, G_CALLBACK (on_wm_class_changed), self);
|
2009-10-15 23:28:29 +00:00
|
|
|
|
2010-04-20 18:47:45 +00:00
|
|
|
g_signal_emit (self, signals[TRACKED_WINDOWS_CHANGED], 0);
|
|
|
|
|
2009-10-15 23:28:29 +00:00
|
|
|
g_object_unref (app);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
shell_window_tracker_on_window_removed (MetaWorkspace *workspace,
|
|
|
|
MetaWindow *window,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
disassociate_window (SHELL_WINDOW_TRACKER (user_data), window);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2011-08-11 10:37:24 +00:00
|
|
|
load_initial_windows (ShellWindowTracker *tracker)
|
2009-10-15 23:28:29 +00:00
|
|
|
{
|
|
|
|
GList *workspaces, *iter;
|
|
|
|
MetaScreen *screen = shell_global_get_screen (shell_global_get ());
|
|
|
|
workspaces = meta_screen_get_workspaces (screen);
|
|
|
|
|
|
|
|
for (iter = workspaces; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
MetaWorkspace *workspace = iter->data;
|
|
|
|
GList *windows = meta_workspace_list_windows (workspace);
|
|
|
|
GList *window_iter;
|
|
|
|
|
|
|
|
for (window_iter = windows; window_iter; window_iter = window_iter->next)
|
|
|
|
{
|
|
|
|
MetaWindow *window = window_iter->data;
|
2011-08-11 10:37:24 +00:00
|
|
|
track_window (tracker, window);
|
2009-10-15 23:28:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
g_list_free (windows);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
shell_window_tracker_on_n_workspaces_changed (MetaScreen *screen,
|
|
|
|
GParamSpec *pspec,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
ShellWindowTracker *self = SHELL_WINDOW_TRACKER (user_data);
|
|
|
|
GList *workspaces, *iter;
|
|
|
|
|
|
|
|
workspaces = meta_screen_get_workspaces (screen);
|
|
|
|
|
|
|
|
for (iter = workspaces; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
MetaWorkspace *workspace = iter->data;
|
|
|
|
|
|
|
|
/* This pair of disconnect/connect is idempotent if we were
|
|
|
|
* already connected, while ensuring we get connected for
|
|
|
|
* new workspaces.
|
|
|
|
*/
|
|
|
|
g_signal_handlers_disconnect_by_func (workspace,
|
|
|
|
shell_window_tracker_on_window_added,
|
|
|
|
self);
|
|
|
|
g_signal_handlers_disconnect_by_func (workspace,
|
|
|
|
shell_window_tracker_on_window_removed,
|
|
|
|
self);
|
|
|
|
|
|
|
|
g_signal_connect (workspace, "window-added",
|
|
|
|
G_CALLBACK (shell_window_tracker_on_window_added), self);
|
|
|
|
g_signal_connect (workspace, "window-removed",
|
|
|
|
G_CALLBACK (shell_window_tracker_on_window_removed), self);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
init_window_tracking (ShellWindowTracker *self)
|
|
|
|
{
|
|
|
|
MetaDisplay *display;
|
|
|
|
MetaScreen *screen = shell_global_get_screen (shell_global_get ());
|
|
|
|
|
|
|
|
g_signal_connect (screen, "notify::n-workspaces",
|
|
|
|
G_CALLBACK (shell_window_tracker_on_n_workspaces_changed), self);
|
|
|
|
display = meta_screen_get_display (screen);
|
|
|
|
g_signal_connect (display, "notify::focus-window",
|
|
|
|
G_CALLBACK (on_focus_window_changed), self);
|
|
|
|
|
|
|
|
shell_window_tracker_on_n_workspaces_changed (screen, NULL, self);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
on_startup_sequence_changed (MetaScreen *screen,
|
|
|
|
SnStartupSequence *sequence,
|
|
|
|
ShellWindowTracker *self)
|
|
|
|
{
|
Major ShellApp API cleanup, startup notification, window focus handling
This patch combines several high level changes which are conceptually
independent but in practice rather intertwined.
* Add a "state" property to ShellApp which reflects whether it's
stopped, starting, or started. This will allow us to later clean
up all the callers that are using ".get_windows().length > 0" as
a proxy for this property
* Replace shell_app_launch with shell_app_activate and shell_app_open_new_window
A lot of code was calling .launch, but it's signficantly clearer
if we call this ".open_new_window()", and later if we gain the ability
to call into an application's menu, we can implement this correctly rather
than trying to update all .launch callers.
* Because ShellApp now has a "starting" state, rebase panel.js on top of
this so that when we get a startup-notification sequence for an app
and transition it to starting, it becomes the focus app, and panel.js
cleanly just tracks the focus app, rather than bouncing between SN
sequences. This removes display of non-app startup sequences, which
I consider an acceptable action in light of the committed changes
to startup-notification and GTK+.
https://bugzilla.gnome.org/show_bug.cgi?id=614755
2010-04-03 18:07:44 +00:00
|
|
|
ShellApp *app;
|
|
|
|
|
|
|
|
app = shell_startup_sequence_get_app ((ShellStartupSequence*)sequence);
|
|
|
|
if (app)
|
2010-10-06 21:30:30 +00:00
|
|
|
_shell_app_handle_startup_sequence (app, sequence);
|
Major ShellApp API cleanup, startup notification, window focus handling
This patch combines several high level changes which are conceptually
independent but in practice rather intertwined.
* Add a "state" property to ShellApp which reflects whether it's
stopped, starting, or started. This will allow us to later clean
up all the callers that are using ".get_windows().length > 0" as
a proxy for this property
* Replace shell_app_launch with shell_app_activate and shell_app_open_new_window
A lot of code was calling .launch, but it's signficantly clearer
if we call this ".open_new_window()", and later if we gain the ability
to call into an application's menu, we can implement this correctly rather
than trying to update all .launch callers.
* Because ShellApp now has a "starting" state, rebase panel.js on top of
this so that when we get a startup-notification sequence for an app
and transition it to starting, it becomes the focus app, and panel.js
cleanly just tracks the focus app, rather than bouncing between SN
sequences. This removes display of non-app startup sequences, which
I consider an acceptable action in light of the committed changes
to startup-notification and GTK+.
https://bugzilla.gnome.org/show_bug.cgi?id=614755
2010-04-03 18:07:44 +00:00
|
|
|
|
2009-10-15 23:28:29 +00:00
|
|
|
g_signal_emit (G_OBJECT (self), signals[STARTUP_SEQUENCE_CHANGED], 0, sequence);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
shell_window_tracker_init (ShellWindowTracker *self)
|
|
|
|
{
|
|
|
|
MetaScreen *screen;
|
|
|
|
|
|
|
|
self->window_to_app = g_hash_table_new_full (g_direct_hash, g_direct_equal,
|
|
|
|
NULL, (GDestroyNotify) g_object_unref);
|
|
|
|
|
2010-12-21 02:06:03 +00:00
|
|
|
self->launched_pid_to_app = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_object_unref);
|
|
|
|
|
2009-10-15 23:28:29 +00:00
|
|
|
screen = shell_global_get_screen (shell_global_get ());
|
|
|
|
|
|
|
|
g_signal_connect (G_OBJECT (screen), "startup-sequence-changed",
|
|
|
|
G_CALLBACK (on_startup_sequence_changed), self);
|
|
|
|
|
|
|
|
load_initial_windows (self);
|
|
|
|
init_window_tracking (self);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
shell_window_tracker_finalize (GObject *object)
|
|
|
|
{
|
|
|
|
ShellWindowTracker *self = SHELL_WINDOW_TRACKER (object);
|
|
|
|
|
|
|
|
g_hash_table_destroy (self->window_to_app);
|
2010-12-21 02:06:03 +00:00
|
|
|
g_hash_table_destroy (self->launched_pid_to_app);
|
|
|
|
|
2009-10-15 23:28:29 +00:00
|
|
|
G_OBJECT_CLASS (shell_window_tracker_parent_class)->finalize(object);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-09-02 00:48:11 +00:00
|
|
|
* shell_window_tracker_get_window_app:
|
2011-08-11 10:37:24 +00:00
|
|
|
* @tracker: An app monitor instance
|
2009-10-15 23:28:29 +00:00
|
|
|
* @metawin: A #MetaWindow
|
|
|
|
*
|
|
|
|
* Returns: (transfer full): Application associated with window
|
|
|
|
*/
|
|
|
|
ShellApp *
|
2011-08-11 10:37:24 +00:00
|
|
|
shell_window_tracker_get_window_app (ShellWindowTracker *tracker,
|
|
|
|
MetaWindow *metawin)
|
2009-10-15 23:28:29 +00:00
|
|
|
{
|
|
|
|
ShellApp *app;
|
|
|
|
|
2011-08-11 10:37:24 +00:00
|
|
|
app = g_hash_table_lookup (tracker->window_to_app, metawin);
|
2009-10-15 23:28:29 +00:00
|
|
|
if (app)
|
|
|
|
g_object_ref (app);
|
|
|
|
|
|
|
|
return app;
|
|
|
|
}
|
|
|
|
|
2010-02-03 23:42:43 +00:00
|
|
|
|
|
|
|
/**
|
2010-09-02 00:48:11 +00:00
|
|
|
* shell_window_tracker_get_app_from_pid:
|
2011-11-02 16:24:49 +00:00
|
|
|
* @tracker: A #ShellAppSystem
|
2010-02-03 23:42:43 +00:00
|
|
|
* @pid: A Unix process identifier
|
|
|
|
*
|
|
|
|
* Look up the application corresponding to a process.
|
|
|
|
*
|
2010-05-24 14:59:52 +00:00
|
|
|
* Returns: (transfer none): A #ShellApp, or %NULL if none
|
2010-02-03 23:42:43 +00:00
|
|
|
*/
|
|
|
|
ShellApp *
|
2011-11-02 16:24:49 +00:00
|
|
|
shell_window_tracker_get_app_from_pid (ShellWindowTracker *tracker,
|
2010-02-03 23:42:43 +00:00
|
|
|
int pid)
|
|
|
|
{
|
apps: Ensure running apps override new .desktop file data
This patch fixes the "apps vanish from alt-TAB bug".
If a "package system" rips away and possibly replaces .desktop files
at some random time, we have historically used inotify to detect this
and reread state (in a racy way, but...). In GNOME 2, this was
generally not too problematic because the menu widget was totally
separate from the list of windows - and the data they operate on was
disjoint as well.
In GNOME 3 we unify these, and this creates architectural problems
because the windows are tied to the app.
What this patch tries to do is, when rereading the application state,
if we have a running application, we keep that app around instead of
making a new instance. This ensures we preserve any state such as the
set of open windows.
This requires moving the running state into ShellAppSystem. Adjust
callers as necessary, and while we're at it drop the unused "contexts"
stuff.
This is just a somewhat quick band-aid; a REAL fix would require us
having low-level control over application installation. As long as
we're on top of random broken tar+wget wrappers, it will be gross.
A slight future improvement to this patch would add an explicit
"merge" between the old and new data. I think probably we always keep
around the ShellApp corresponding to a given ID, but replace its
GMenuTreeEntry.
https://bugzilla.gnome.org/show_bug.cgi?id=657990
2011-09-03 14:32:06 +00:00
|
|
|
GSList *running = shell_app_system_get_running (shell_app_system_get_default());
|
2010-05-24 14:59:52 +00:00
|
|
|
GSList *iter;
|
|
|
|
ShellApp *result = NULL;
|
|
|
|
|
|
|
|
for (iter = running; iter; iter = iter->next)
|
2010-02-03 23:42:43 +00:00
|
|
|
{
|
2010-05-24 14:59:52 +00:00
|
|
|
ShellApp *app = iter->data;
|
|
|
|
GSList *pids = shell_app_get_pids (app);
|
|
|
|
GSList *pids_iter;
|
|
|
|
|
|
|
|
for (pids_iter = pids; pids_iter; pids_iter = pids_iter->next)
|
|
|
|
{
|
|
|
|
int app_pid = GPOINTER_TO_INT (pids_iter->data);
|
|
|
|
if (app_pid == pid)
|
|
|
|
{
|
|
|
|
result = app;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g_slist_free (pids);
|
|
|
|
|
|
|
|
if (result != NULL)
|
|
|
|
break;
|
2010-02-03 23:42:43 +00:00
|
|
|
}
|
2010-05-24 14:59:52 +00:00
|
|
|
|
|
|
|
g_slist_free (running);
|
|
|
|
|
|
|
|
return result;
|
2010-02-03 23:42:43 +00:00
|
|
|
}
|
|
|
|
|
2010-12-21 02:06:03 +00:00
|
|
|
static void
|
|
|
|
on_child_exited (GPid pid,
|
|
|
|
gint status,
|
|
|
|
gpointer unused_data)
|
|
|
|
{
|
|
|
|
ShellWindowTracker *tracker;
|
|
|
|
|
|
|
|
tracker = shell_window_tracker_get_default ();
|
|
|
|
|
|
|
|
g_hash_table_remove (tracker->launched_pid_to_app, GINT_TO_POINTER((gint)pid));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
_shell_window_tracker_add_child_process_app (ShellWindowTracker *tracker,
|
|
|
|
GPid pid,
|
|
|
|
ShellApp *app)
|
|
|
|
{
|
2011-02-08 19:37:22 +00:00
|
|
|
gpointer pid_ptr = GINT_TO_POINTER((int)pid);
|
2010-12-21 02:06:03 +00:00
|
|
|
|
|
|
|
if (g_hash_table_lookup (tracker->launched_pid_to_app,
|
2011-02-08 19:37:22 +00:00
|
|
|
&pid_ptr))
|
2010-12-21 02:06:03 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
g_hash_table_insert (tracker->launched_pid_to_app,
|
2011-02-08 19:37:22 +00:00
|
|
|
pid_ptr,
|
2010-12-21 02:06:03 +00:00
|
|
|
g_object_ref (app));
|
|
|
|
g_child_watch_add (pid, on_child_exited, NULL);
|
|
|
|
/* TODO: rescan unassociated windows
|
2011-02-08 19:37:22 +00:00
|
|
|
* Unlikely in practice that the launched app gets ahead of us
|
2010-12-21 02:06:03 +00:00
|
|
|
* enough to map an X window before we get scheduled after the fork(),
|
|
|
|
* but adding this note for future reference.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
Major ShellApp API cleanup, startup notification, window focus handling
This patch combines several high level changes which are conceptually
independent but in practice rather intertwined.
* Add a "state" property to ShellApp which reflects whether it's
stopped, starting, or started. This will allow us to later clean
up all the callers that are using ".get_windows().length > 0" as
a proxy for this property
* Replace shell_app_launch with shell_app_activate and shell_app_open_new_window
A lot of code was calling .launch, but it's signficantly clearer
if we call this ".open_new_window()", and later if we gain the ability
to call into an application's menu, we can implement this correctly rather
than trying to update all .launch callers.
* Because ShellApp now has a "starting" state, rebase panel.js on top of
this so that when we get a startup-notification sequence for an app
and transition it to starting, it becomes the focus app, and panel.js
cleanly just tracks the focus app, rather than bouncing between SN
sequences. This removes display of non-app startup sequences, which
I consider an acceptable action in light of the committed changes
to startup-notification and GTK+.
https://bugzilla.gnome.org/show_bug.cgi?id=614755
2010-04-03 18:07:44 +00:00
|
|
|
static void
|
|
|
|
set_focus_app (ShellWindowTracker *tracker,
|
|
|
|
ShellApp *new_focus_app)
|
|
|
|
{
|
|
|
|
if (new_focus_app == tracker->focus_app)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (tracker->focus_app != NULL)
|
|
|
|
g_object_unref (tracker->focus_app);
|
|
|
|
|
|
|
|
tracker->focus_app = new_focus_app;
|
|
|
|
|
|
|
|
if (tracker->focus_app != NULL)
|
|
|
|
g_object_ref (tracker->focus_app);
|
|
|
|
|
|
|
|
g_object_notify (G_OBJECT (tracker), "focus-app");
|
|
|
|
}
|
|
|
|
|
2010-02-25 21:11:14 +00:00
|
|
|
static void
|
|
|
|
on_focus_window_changed (MetaDisplay *display,
|
|
|
|
GParamSpec *spec,
|
|
|
|
ShellWindowTracker *tracker)
|
2009-10-15 23:28:29 +00:00
|
|
|
{
|
2011-05-24 23:42:07 +00:00
|
|
|
update_focus_app (tracker);
|
2009-10-15 23:28:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* shell_window_tracker_get_startup_sequences:
|
2012-04-06 14:34:52 +00:00
|
|
|
* @tracker:
|
2009-10-15 23:28:29 +00:00
|
|
|
*
|
|
|
|
* Returns: (transfer none) (element-type ShellStartupSequence): Currently active startup sequences
|
|
|
|
*/
|
|
|
|
GSList *
|
|
|
|
shell_window_tracker_get_startup_sequences (ShellWindowTracker *self)
|
|
|
|
{
|
|
|
|
ShellGlobal *global = shell_global_get ();
|
|
|
|
MetaScreen *screen = shell_global_get_screen (global);
|
|
|
|
return meta_screen_get_startup_sequences (screen);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sn_startup_sequence_ref returns void, so make a
|
|
|
|
* wrapper which returns self */
|
|
|
|
static SnStartupSequence *
|
|
|
|
sequence_ref (SnStartupSequence *sequence)
|
|
|
|
{
|
|
|
|
sn_startup_sequence_ref (sequence);
|
|
|
|
return sequence;
|
|
|
|
}
|
|
|
|
|
|
|
|
GType
|
|
|
|
shell_startup_sequence_get_type (void)
|
|
|
|
{
|
|
|
|
static GType gtype = G_TYPE_INVALID;
|
|
|
|
if (gtype == G_TYPE_INVALID)
|
|
|
|
{
|
|
|
|
gtype = g_boxed_type_register_static ("ShellStartupSequence",
|
|
|
|
(GBoxedCopyFunc)sequence_ref,
|
|
|
|
(GBoxedFreeFunc)sn_startup_sequence_unref);
|
|
|
|
}
|
|
|
|
return gtype;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *
|
|
|
|
shell_startup_sequence_get_id (ShellStartupSequence *sequence)
|
|
|
|
{
|
|
|
|
return sn_startup_sequence_get_id ((SnStartupSequence*)sequence);
|
|
|
|
}
|
|
|
|
|
2010-02-24 16:29:44 +00:00
|
|
|
/**
|
|
|
|
* shell_startup_sequence_get_app:
|
|
|
|
* @sequence: A #ShellStartupSequence
|
|
|
|
*
|
Kill off ShellAppInfo, move into ShellApp
This dramatically thins down and sanitizes the application code.
The ShellAppSystem changes in a number of ways:
* Preferences are special cased more explicitly; they aren't apps,
they're shortcuts for an app), and we don't have many of them, so
don't need e.g. the optimizations in ShellAppSystem for searching.
* get_app() changes to lookup_app() and returns null if an app isn't
found. The semantics where it tried to find the .desktop file
if we didn't know about it were just broken; I am pretty sure no
caller needs this, and if they do we'll fix them.
* ShellAppSystem maintains two indexes on apps (by desktop file id
and by GMenuTreeEntry), but is no longer in the business of
dealing with GMenuTree as far as hierarchy and categories go. That
is moved up into js/ui/appDisplay.js. Actually, it flattens both
apps and settings.
Also, ShellWindowTracker is now the sole reference-owner for
window-backed apps. We still do the weird "window:0x1234beef" id
for these apps, but a reference is not stored in ShellAppSystem.
The js/ui/appDisplay.js code is rewritten, and sucks a lot less.
Variable names are clearer:
_apps -> _appIcons
_filterApp -> _visibleApps
_filters -> _categoryBox
Similarly for function names. We no longer call (for every app) a
recursive lookup in GMenuTree to see if it's in a particular section
on every category switch; it's all cached.
NOTE - this intentionally reverts the incremental loading code from
commit 7813c5b93f6bcde8c4beae286e82bfc472b2b656. It's fast enough
here without that.
https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-04-21 17:35:01 +00:00
|
|
|
* Returns: (transfer none): The application being launched, or %NULL if unknown.
|
2010-02-24 16:29:44 +00:00
|
|
|
*/
|
|
|
|
ShellApp *
|
|
|
|
shell_startup_sequence_get_app (ShellStartupSequence *sequence)
|
|
|
|
{
|
|
|
|
const char *appid;
|
2013-04-20 21:23:37 +00:00
|
|
|
char *basename;
|
2010-02-24 16:29:44 +00:00
|
|
|
ShellAppSystem *appsys;
|
|
|
|
ShellApp *app;
|
|
|
|
|
|
|
|
appid = sn_startup_sequence_get_application_id ((SnStartupSequence*)sequence);
|
|
|
|
if (!appid)
|
|
|
|
return NULL;
|
|
|
|
|
2013-04-20 21:23:37 +00:00
|
|
|
basename = g_path_get_basename (appid);
|
2010-02-24 16:29:44 +00:00
|
|
|
appsys = shell_app_system_get_default ();
|
2013-04-20 21:23:37 +00:00
|
|
|
app = shell_app_system_lookup_app (appsys, basename);
|
|
|
|
g_free (basename);
|
2010-02-24 16:29:44 +00:00
|
|
|
return app;
|
|
|
|
}
|
|
|
|
|
2009-10-15 23:28:29 +00:00
|
|
|
const char *
|
|
|
|
shell_startup_sequence_get_name (ShellStartupSequence *sequence)
|
|
|
|
{
|
|
|
|
return sn_startup_sequence_get_name ((SnStartupSequence*)sequence);
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
shell_startup_sequence_get_completed (ShellStartupSequence *sequence)
|
|
|
|
{
|
|
|
|
return sn_startup_sequence_get_completed ((SnStartupSequence*)sequence);
|
|
|
|
}
|
|
|
|
|
2012-02-17 14:57:27 +00:00
|
|
|
int
|
|
|
|
shell_startup_sequence_get_workspace (ShellStartupSequence *sequence)
|
|
|
|
{
|
|
|
|
return sn_startup_sequence_get_workspace ((SnStartupSequence*)sequence);
|
|
|
|
}
|
|
|
|
|
2009-10-15 23:28:29 +00:00
|
|
|
/**
|
|
|
|
* shell_startup_sequence_create_icon:
|
|
|
|
* @sequence:
|
|
|
|
* @size: Size in pixels of icon
|
|
|
|
*
|
|
|
|
* Returns: (transfer none): A new #ClutterTexture containing an icon for the sequence
|
|
|
|
*/
|
|
|
|
ClutterActor *
|
|
|
|
shell_startup_sequence_create_icon (ShellStartupSequence *sequence, guint size)
|
|
|
|
{
|
|
|
|
GIcon *themed;
|
|
|
|
const char *icon_name;
|
|
|
|
ClutterActor *texture;
|
2014-02-16 03:43:35 +00:00
|
|
|
gint scale;
|
|
|
|
ShellGlobal *global;
|
|
|
|
StThemeContext *context;
|
|
|
|
|
|
|
|
global = shell_global_get ();
|
|
|
|
context = st_theme_context_get_for_stage (shell_global_get_stage (global));
|
|
|
|
g_object_get (context, "scale-factor", &scale, NULL);
|
2009-10-15 23:28:29 +00:00
|
|
|
|
|
|
|
icon_name = sn_startup_sequence_get_icon_name ((SnStartupSequence*)sequence);
|
|
|
|
if (!icon_name)
|
|
|
|
{
|
|
|
|
texture = clutter_texture_new ();
|
2014-02-16 03:43:35 +00:00
|
|
|
clutter_actor_set_size (texture, size * scale, size * scale);
|
2009-10-15 23:28:29 +00:00
|
|
|
return texture;
|
|
|
|
}
|
|
|
|
|
|
|
|
themed = g_themed_icon_new (icon_name);
|
2010-02-09 17:42:07 +00:00
|
|
|
texture = st_texture_cache_load_gicon (st_texture_cache_get_default (),
|
2014-02-16 03:43:35 +00:00
|
|
|
NULL, themed, size, scale);
|
2009-10-15 23:28:29 +00:00
|
|
|
g_object_unref (G_OBJECT (themed));
|
|
|
|
return texture;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* shell_window_tracker_get_default:
|
|
|
|
*
|
|
|
|
* Return Value: (transfer none): The global #ShellWindowTracker instance
|
|
|
|
*/
|
|
|
|
ShellWindowTracker *
|
|
|
|
shell_window_tracker_get_default ()
|
|
|
|
{
|
|
|
|
static ShellWindowTracker *instance;
|
|
|
|
|
|
|
|
if (instance == NULL)
|
|
|
|
instance = g_object_new (SHELL_TYPE_WINDOW_TRACKER, NULL);
|
|
|
|
|
|
|
|
return instance;
|
|
|
|
}
|