e941e8088b
The two parts were mapping windows to applications, and recording application usage statistics. The latter part (now called ShellAppUsage) is much more naturally built on top of the former (now called ShellWindowTracker). ShellWindowTracker retains the startup-notification handling. ShellWindowTracker also gains a focus-app property, which is what most things in the shell UI are interested in (instead of window focus). ShellAppSystem moves to exporting ShellApp from more of its public API, rather than ShellAppInfo. ShellAppSystem also ensures that ShellApp instances are unique by holding a hash on the ids. ShellApp's private API is split off into a shell-app-private.h, so shell-app.h can be included in shell-app-system.h. Favorites handling is removed from ShellAppSystem, now inside appFavorites.js. Port all of the JavaScript for these changes. https://bugzilla.gnome.org/show_bug.cgi?id=598646
374 lines
8.9 KiB
C
374 lines
8.9 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
|
|
#include "shell-app-private.h"
|
|
#include "shell-global.h"
|
|
|
|
/**
|
|
* SECTION:shell-app
|
|
* @short_description: Object representing an application
|
|
*
|
|
* This object wraps a #ShellAppInfo, providing methods and signals
|
|
* primarily useful for running applications.
|
|
*/
|
|
struct _ShellApp
|
|
{
|
|
GObject parent;
|
|
|
|
ShellAppInfo *info;
|
|
|
|
guint workspace_switch_id;
|
|
|
|
gboolean window_sort_stale;
|
|
GSList *windows;
|
|
};
|
|
|
|
G_DEFINE_TYPE (ShellApp, shell_app, G_TYPE_OBJECT);
|
|
|
|
enum {
|
|
WINDOWS_CHANGED,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static guint shell_app_signals[LAST_SIGNAL] = { 0 };
|
|
|
|
const char *
|
|
shell_app_get_id (ShellApp *app)
|
|
{
|
|
return shell_app_info_get_id (app->info);
|
|
}
|
|
|
|
/**
|
|
* shell_app_create_icon_texture:
|
|
*
|
|
* Look up the icon for this application, and create a #ClutterTexture
|
|
* for it at the given size.
|
|
*
|
|
* Return value: (transfer none): A floating #ClutterActor
|
|
*/
|
|
ClutterActor *
|
|
shell_app_create_icon_texture (ShellApp *app,
|
|
float size)
|
|
{
|
|
return shell_app_info_create_icon_texture (app->info, size);
|
|
}
|
|
|
|
char *
|
|
shell_app_get_name (ShellApp *app)
|
|
{
|
|
return shell_app_info_get_name (app->info);
|
|
}
|
|
|
|
char *
|
|
shell_app_get_description (ShellApp *app)
|
|
{
|
|
return shell_app_info_get_description (app->info);
|
|
}
|
|
|
|
gboolean
|
|
shell_app_is_transient (ShellApp *app)
|
|
{
|
|
return shell_app_info_is_transient (app->info);
|
|
}
|
|
|
|
gboolean
|
|
shell_app_launch (ShellApp *self,
|
|
GError **error)
|
|
{
|
|
return shell_app_info_launch (self->info, error);
|
|
}
|
|
|
|
/**
|
|
* _shell_app_get_info:
|
|
*
|
|
* Returns: (transfer none): Associated app info
|
|
*/
|
|
ShellAppInfo *
|
|
_shell_app_get_info (ShellApp *app)
|
|
{
|
|
return app->info;
|
|
}
|
|
|
|
typedef struct {
|
|
ShellApp *app;
|
|
MetaWorkspace *active_workspace;
|
|
} CompareWindowsData;
|
|
|
|
static int
|
|
shell_app_compare_windows (gconstpointer a,
|
|
gconstpointer b,
|
|
gpointer datap)
|
|
{
|
|
MetaWindow *win_a = (gpointer)a;
|
|
MetaWindow *win_b = (gpointer)b;
|
|
CompareWindowsData *data = datap;
|
|
gboolean ws_a, ws_b;
|
|
gboolean vis_a, vis_b;
|
|
|
|
ws_a = meta_window_get_workspace (win_a) == data->active_workspace;
|
|
ws_b = meta_window_get_workspace (win_b) == data->active_workspace;
|
|
|
|
if (ws_a && !ws_b)
|
|
return -1;
|
|
else if (!ws_a && ws_b)
|
|
return 1;
|
|
|
|
vis_a = meta_window_showing_on_its_workspace (win_a);
|
|
vis_b = meta_window_showing_on_its_workspace (win_b);
|
|
|
|
if (vis_a && !vis_b)
|
|
return -1;
|
|
else if (!vis_a && vis_b)
|
|
return 1;
|
|
|
|
return meta_window_get_user_time (win_b) - meta_window_get_user_time (win_a);
|
|
}
|
|
|
|
/**
|
|
* shell_app_get_windows:
|
|
* @app:
|
|
*
|
|
* Get the toplevel, interesting windows which are associated with this
|
|
* application. The returned list will be sorted first by whether
|
|
* they're on the active workspace, then by whether they're visible,
|
|
* and finally by the time the user last interacted with them.
|
|
*
|
|
* Returns: (transfer none) (element-type MetaWindow): List of windows
|
|
*/
|
|
GSList *
|
|
shell_app_get_windows (ShellApp *app)
|
|
{
|
|
if (app->window_sort_stale)
|
|
{
|
|
CompareWindowsData data;
|
|
data.app = app;
|
|
data.active_workspace = meta_screen_get_active_workspace (shell_global_get_screen (shell_global_get ()));
|
|
app->windows = g_slist_sort_with_data (app->windows, shell_app_compare_windows, &data);
|
|
app->window_sort_stale = FALSE;
|
|
}
|
|
|
|
return app->windows;
|
|
}
|
|
|
|
guint
|
|
shell_app_get_n_windows (ShellApp *app)
|
|
{
|
|
return g_slist_length (app->windows);
|
|
}
|
|
|
|
static gboolean
|
|
shell_app_has_visible_windows (ShellApp *app)
|
|
{
|
|
GSList *iter;
|
|
|
|
for (iter = app->windows; iter; iter = iter->next)
|
|
{
|
|
MetaWindow *window = iter->data;
|
|
|
|
if (meta_window_showing_on_its_workspace (window))
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
shell_app_is_on_workspace (ShellApp *app,
|
|
MetaWorkspace *workspace)
|
|
{
|
|
GSList *iter;
|
|
|
|
for (iter = app->windows; iter; iter = iter->next)
|
|
{
|
|
if (meta_window_get_workspace (iter->data) == workspace)
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* shell_app_compare:
|
|
* @app:
|
|
* @other: A #ShellApp
|
|
*
|
|
* Compare one #ShellApp instance to another, in the following way:
|
|
* - If one of them has visible windows and the other does not, the one
|
|
* with visible windows is first.
|
|
* - If one has no windows at all (i.e. it's not running) and the other
|
|
* does, the one with windows is first.
|
|
* - Finally, the application which the user interacted with most recently
|
|
* compares earlier.
|
|
*/
|
|
int
|
|
shell_app_compare (ShellApp *app,
|
|
ShellApp *other)
|
|
{
|
|
gboolean vis_app, vis_other;
|
|
GSList *windows_app, *windows_other;
|
|
|
|
vis_app = shell_app_has_visible_windows (app);
|
|
vis_other = shell_app_has_visible_windows (other);
|
|
|
|
if (vis_app && !vis_other)
|
|
return -1;
|
|
else if (!vis_app && vis_other)
|
|
return 1;
|
|
|
|
if (app->windows && !other->windows)
|
|
return -1;
|
|
else if (!app->windows && other->windows)
|
|
return 1;
|
|
|
|
windows_app = shell_app_get_windows (app);
|
|
windows_other = shell_app_get_windows (other);
|
|
|
|
return meta_window_get_user_time (windows_other->data) - meta_window_get_user_time (windows_app->data);
|
|
}
|
|
|
|
ShellApp *
|
|
_shell_app_new_for_window (MetaWindow *window)
|
|
{
|
|
ShellApp *app;
|
|
|
|
app = g_object_new (SHELL_TYPE_APP, NULL);
|
|
app->info = shell_app_system_create_from_window (shell_app_system_get_default (), window);
|
|
_shell_app_system_register_app (shell_app_system_get_default (), app);
|
|
_shell_app_add_window (app, window);
|
|
|
|
return app;
|
|
}
|
|
|
|
ShellApp *
|
|
_shell_app_new (ShellAppInfo *info)
|
|
{
|
|
ShellApp *app;
|
|
|
|
app = g_object_new (SHELL_TYPE_APP, NULL);
|
|
app->info = shell_app_info_ref (info);
|
|
_shell_app_system_register_app (shell_app_system_get_default (), app);
|
|
|
|
return app;
|
|
}
|
|
|
|
static void
|
|
shell_app_on_unmanaged (MetaWindow *window,
|
|
ShellApp *app)
|
|
{
|
|
_shell_app_remove_window (app, window);
|
|
}
|
|
|
|
static void
|
|
shell_app_on_user_time_changed (MetaWindow *window,
|
|
GParamSpec *pspec,
|
|
ShellApp *app)
|
|
{
|
|
/* Ideally we don't want to emit windows-changed if the sort order
|
|
* isn't actually changing. This check catches most of those.
|
|
*/
|
|
if (window != app->windows->data)
|
|
{
|
|
app->window_sort_stale = TRUE;
|
|
g_signal_emit (app, shell_app_signals[WINDOWS_CHANGED], 0);
|
|
}
|
|
}
|
|
|
|
static void
|
|
shell_app_on_ws_switch (MetaScreen *screen,
|
|
int from,
|
|
int to,
|
|
MetaMotionDirection direction,
|
|
gpointer data)
|
|
{
|
|
ShellApp *self = SHELL_APP (data);
|
|
self->window_sort_stale = TRUE;
|
|
g_signal_emit (self, shell_app_signals[WINDOWS_CHANGED], 0);
|
|
}
|
|
|
|
void
|
|
_shell_app_add_window (ShellApp *app,
|
|
MetaWindow *window)
|
|
{
|
|
if (g_slist_find (app->windows, window))
|
|
return;
|
|
|
|
app->windows = g_slist_prepend (app->windows, g_object_ref (window));
|
|
g_signal_connect (window, "unmanaged", G_CALLBACK(shell_app_on_unmanaged), app);
|
|
g_signal_connect (window, "notify::user-time", G_CALLBACK(shell_app_on_user_time_changed), app);
|
|
app->window_sort_stale = TRUE;
|
|
|
|
g_signal_emit (app, shell_app_signals[WINDOWS_CHANGED], 0);
|
|
|
|
if (app->workspace_switch_id == 0)
|
|
{
|
|
MetaScreen *screen = shell_global_get_screen (shell_global_get ());
|
|
|
|
app->workspace_switch_id =
|
|
g_signal_connect (screen, "workspace-switched", G_CALLBACK(shell_app_on_ws_switch), app);
|
|
}
|
|
}
|
|
|
|
static void
|
|
disconnect_workspace_switch (ShellApp *app)
|
|
{
|
|
MetaScreen *screen;
|
|
|
|
if (app->workspace_switch_id == 0)
|
|
return;
|
|
|
|
screen = shell_global_get_screen (shell_global_get ());
|
|
g_signal_handler_disconnect (screen, app->workspace_switch_id);
|
|
app->workspace_switch_id = 0;
|
|
}
|
|
|
|
void
|
|
_shell_app_remove_window (ShellApp *app,
|
|
MetaWindow *window)
|
|
{
|
|
g_object_unref (window);
|
|
app->windows = g_slist_remove (app->windows, window);
|
|
if (app->windows == NULL)
|
|
disconnect_workspace_switch (app);
|
|
}
|
|
|
|
static void
|
|
shell_app_init (ShellApp *self)
|
|
{
|
|
}
|
|
|
|
static void
|
|
shell_app_dispose (GObject *object)
|
|
{
|
|
ShellApp *app = SHELL_APP (object);
|
|
|
|
if (app->info)
|
|
{
|
|
shell_app_info_unref (app->info);
|
|
app->info = NULL;
|
|
}
|
|
|
|
if (app->windows)
|
|
{
|
|
g_slist_foreach (app->windows, (GFunc) g_object_unref, NULL);
|
|
g_slist_free (app->windows);
|
|
app->windows = NULL;
|
|
}
|
|
|
|
disconnect_workspace_switch (app);
|
|
}
|
|
|
|
static void
|
|
shell_app_class_init(ShellAppClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
gobject_class->dispose = shell_app_dispose;
|
|
|
|
shell_app_signals[WINDOWS_CHANGED] = g_signal_new ("windows-changed",
|
|
SHELL_TYPE_APP,
|
|
G_SIGNAL_RUN_LAST,
|
|
0,
|
|
NULL, NULL,
|
|
g_cclosure_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
}
|