Split off running state handling into separate structure
This is a small memory usage optimization, and cleans up the code. In particular, this will help for later patches which perform more substantial operations on running apps. https://bugzilla.gnome.org/show_bug.cgi?id=621203
This commit is contained in:
parent
7f8f0f2358
commit
da4e24555b
209
src/shell-app.c
209
src/shell-app.c
@ -12,6 +12,26 @@
|
|||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
/* This is mainly a memory usage optimization - the user is going to
|
||||||
|
* be running far fewer of the applications at one time than they have
|
||||||
|
* installed. But it also just helps keep the code more logically
|
||||||
|
* separated.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
guint refcount;
|
||||||
|
|
||||||
|
/* Last time the user interacted with any of this application's windows */
|
||||||
|
guint32 last_user_time;
|
||||||
|
|
||||||
|
/* Signal connection to dirty window sort list on workspace changes */
|
||||||
|
guint workspace_switch_id;
|
||||||
|
|
||||||
|
GSList *windows;
|
||||||
|
|
||||||
|
/* Whether or not we need to resort the windows; this is done on demand */
|
||||||
|
gboolean window_sort_stale : 1;
|
||||||
|
} ShellAppRunningState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SECTION:shell-app
|
* SECTION:shell-app
|
||||||
* @short_description: Object representing an application
|
* @short_description: Object representing an application
|
||||||
@ -23,16 +43,11 @@ struct _ShellApp
|
|||||||
{
|
{
|
||||||
GObject parent;
|
GObject parent;
|
||||||
|
|
||||||
|
ShellAppState state;
|
||||||
|
|
||||||
ShellAppInfo *info;
|
ShellAppInfo *info;
|
||||||
|
|
||||||
guint32 last_user_time;
|
ShellAppRunningState *running_state;
|
||||||
|
|
||||||
guint workspace_switch_id;
|
|
||||||
|
|
||||||
GSList *windows;
|
|
||||||
|
|
||||||
ShellAppState state;
|
|
||||||
gboolean window_sort_stale : 1;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
G_DEFINE_TYPE (ShellApp, shell_app, G_TYPE_OBJECT);
|
G_DEFINE_TYPE (ShellApp, shell_app, G_TYPE_OBJECT);
|
||||||
@ -49,6 +64,9 @@ enum {
|
|||||||
|
|
||||||
static guint shell_app_signals[LAST_SIGNAL] = { 0 };
|
static guint shell_app_signals[LAST_SIGNAL] = { 0 };
|
||||||
|
|
||||||
|
static void create_running_state (ShellApp *app);
|
||||||
|
static void unref_running_state (ShellAppRunningState *state);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
shell_app_get_property (GObject *gobject,
|
shell_app_get_property (GObject *gobject,
|
||||||
guint prop_id,
|
guint prop_id,
|
||||||
@ -507,22 +525,27 @@ shell_app_compare_windows (gconstpointer a,
|
|||||||
GSList *
|
GSList *
|
||||||
shell_app_get_windows (ShellApp *app)
|
shell_app_get_windows (ShellApp *app)
|
||||||
{
|
{
|
||||||
if (app->window_sort_stale)
|
if (app->running_state == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (app->running_state->window_sort_stale)
|
||||||
{
|
{
|
||||||
CompareWindowsData data;
|
CompareWindowsData data;
|
||||||
data.app = app;
|
data.app = app;
|
||||||
data.active_workspace = meta_screen_get_active_workspace (shell_global_get_screen (shell_global_get ()));
|
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->running_state->windows = g_slist_sort_with_data (app->running_state->windows, shell_app_compare_windows, &data);
|
||||||
app->window_sort_stale = FALSE;
|
app->running_state->window_sort_stale = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return app->windows;
|
return app->running_state->windows;
|
||||||
}
|
}
|
||||||
|
|
||||||
guint
|
guint
|
||||||
shell_app_get_n_windows (ShellApp *app)
|
shell_app_get_n_windows (ShellApp *app)
|
||||||
{
|
{
|
||||||
return g_slist_length (app->windows);
|
if (app->running_state == NULL)
|
||||||
|
return 0;
|
||||||
|
return g_slist_length (app->running_state->windows);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
@ -530,7 +553,10 @@ shell_app_has_visible_windows (ShellApp *app)
|
|||||||
{
|
{
|
||||||
GSList *iter;
|
GSList *iter;
|
||||||
|
|
||||||
for (iter = app->windows; iter; iter = iter->next)
|
if (app->running_state == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
for (iter = app->running_state->windows; iter; iter = iter->next)
|
||||||
{
|
{
|
||||||
MetaWindow *window = iter->data;
|
MetaWindow *window = iter->data;
|
||||||
|
|
||||||
@ -547,7 +573,10 @@ shell_app_is_on_workspace (ShellApp *app,
|
|||||||
{
|
{
|
||||||
GSList *iter;
|
GSList *iter;
|
||||||
|
|
||||||
for (iter = app->windows; iter; iter = iter->next)
|
if (app->running_state == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
for (iter = app->running_state->windows; iter; iter = iter->next)
|
||||||
{
|
{
|
||||||
if (meta_window_get_workspace (iter->data) == workspace)
|
if (meta_window_get_workspace (iter->data) == workspace)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
@ -589,12 +618,16 @@ shell_app_compare (ShellApp *app,
|
|||||||
else if (!vis_app && vis_other)
|
else if (!vis_app && vis_other)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (app->windows && !other->windows)
|
if (app->state == SHELL_APP_STATE_RUNNING)
|
||||||
return -1;
|
{
|
||||||
else if (!app->windows && other->windows)
|
if (app->running_state->windows && !other->running_state->windows)
|
||||||
return 1;
|
return -1;
|
||||||
|
else if (!app->running_state->windows && other->running_state->windows)
|
||||||
|
return 1;
|
||||||
|
return other->running_state->last_user_time - app->running_state->last_user_time;
|
||||||
|
}
|
||||||
|
|
||||||
return other->last_user_time - app->last_user_time;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ShellApp *
|
ShellApp *
|
||||||
@ -631,9 +664,20 @@ shell_app_state_transition (ShellApp *app,
|
|||||||
g_return_if_fail (!(app->state == SHELL_APP_STATE_RUNNING &&
|
g_return_if_fail (!(app->state == SHELL_APP_STATE_RUNNING &&
|
||||||
state == SHELL_APP_STATE_STARTING));
|
state == SHELL_APP_STATE_STARTING));
|
||||||
app->state = state;
|
app->state = state;
|
||||||
g_object_notify (G_OBJECT (app), "state");
|
|
||||||
|
if (app->state != SHELL_APP_STATE_RUNNING && app->running_state)
|
||||||
|
{
|
||||||
|
unref_running_state (app->running_state);
|
||||||
|
app->running_state = NULL;
|
||||||
|
}
|
||||||
|
else if (app->state == SHELL_APP_STATE_RUNNING)
|
||||||
|
{
|
||||||
|
create_running_state (app);
|
||||||
|
}
|
||||||
|
|
||||||
_shell_window_tracker_notify_app_state_changed (shell_window_tracker_get_default (), app);
|
_shell_window_tracker_notify_app_state_changed (shell_window_tracker_get_default (), app);
|
||||||
|
|
||||||
|
g_object_notify (G_OBJECT (app), "state");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -648,14 +692,16 @@ shell_app_on_user_time_changed (MetaWindow *window,
|
|||||||
GParamSpec *pspec,
|
GParamSpec *pspec,
|
||||||
ShellApp *app)
|
ShellApp *app)
|
||||||
{
|
{
|
||||||
app->last_user_time = meta_window_get_user_time (window);
|
g_assert (app->running_state != NULL);
|
||||||
|
|
||||||
|
app->running_state->last_user_time = meta_window_get_user_time (window);
|
||||||
|
|
||||||
/* Ideally we don't want to emit windows-changed if the sort order
|
/* Ideally we don't want to emit windows-changed if the sort order
|
||||||
* isn't actually changing. This check catches most of those.
|
* isn't actually changing. This check catches most of those.
|
||||||
*/
|
*/
|
||||||
if (window != app->windows->data)
|
if (window != app->running_state->windows->data)
|
||||||
{
|
{
|
||||||
app->window_sort_stale = TRUE;
|
app->running_state->window_sort_stale = TRUE;
|
||||||
g_signal_emit (app, shell_app_signals[WINDOWS_CHANGED], 0);
|
g_signal_emit (app, shell_app_signals[WINDOWS_CHANGED], 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -667,9 +713,13 @@ shell_app_on_ws_switch (MetaScreen *screen,
|
|||||||
MetaMotionDirection direction,
|
MetaMotionDirection direction,
|
||||||
gpointer data)
|
gpointer data)
|
||||||
{
|
{
|
||||||
ShellApp *self = SHELL_APP (data);
|
ShellApp *app = SHELL_APP (data);
|
||||||
self->window_sort_stale = TRUE;
|
|
||||||
g_signal_emit (self, shell_app_signals[WINDOWS_CHANGED], 0);
|
g_assert (app->running_state != NULL);
|
||||||
|
|
||||||
|
app->running_state->window_sort_stale = TRUE;
|
||||||
|
|
||||||
|
g_signal_emit (app, shell_app_signals[WINDOWS_CHANGED], 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -678,65 +728,49 @@ _shell_app_add_window (ShellApp *app,
|
|||||||
{
|
{
|
||||||
guint32 user_time;
|
guint32 user_time;
|
||||||
|
|
||||||
if (g_slist_find (app->windows, window))
|
if (app->running_state && g_slist_find (app->running_state->windows, window))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
app->windows = g_slist_prepend (app->windows, g_object_ref (window));
|
g_object_freeze_notify (G_OBJECT (app));
|
||||||
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;
|
|
||||||
|
|
||||||
user_time = meta_window_get_user_time (window);
|
|
||||||
if (user_time > app->last_user_time)
|
|
||||||
app->last_user_time = user_time;
|
|
||||||
|
|
||||||
|
/* Ensure we've initialized running state */
|
||||||
if (app->state != SHELL_APP_STATE_RUNNING)
|
if (app->state != SHELL_APP_STATE_RUNNING)
|
||||||
shell_app_state_transition (app, SHELL_APP_STATE_RUNNING);
|
shell_app_state_transition (app, SHELL_APP_STATE_RUNNING);
|
||||||
|
|
||||||
|
g_assert (app->running_state != NULL);
|
||||||
|
|
||||||
|
app->running_state->window_sort_stale = TRUE;
|
||||||
|
app->running_state->windows = g_slist_prepend (app->running_state->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);
|
||||||
|
|
||||||
|
user_time = meta_window_get_user_time (window);
|
||||||
|
if (user_time > app->running_state->last_user_time)
|
||||||
|
app->running_state->last_user_time = user_time;
|
||||||
|
|
||||||
|
g_object_thaw_notify (G_OBJECT (app));
|
||||||
|
|
||||||
g_signal_emit (app, shell_app_signals[WINDOWS_CHANGED], 0);
|
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
|
void
|
||||||
_shell_app_remove_window (ShellApp *app,
|
_shell_app_remove_window (ShellApp *app,
|
||||||
MetaWindow *window)
|
MetaWindow *window)
|
||||||
{
|
{
|
||||||
if (!g_slist_find (app->windows, window))
|
g_assert (app->running_state != NULL);
|
||||||
|
|
||||||
|
if (!g_slist_find (app->running_state->windows, window))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
g_signal_handlers_disconnect_by_func (window, G_CALLBACK(shell_app_on_unmanaged), app);
|
g_signal_handlers_disconnect_by_func (window, G_CALLBACK(shell_app_on_unmanaged), app);
|
||||||
g_signal_handlers_disconnect_by_func (window, G_CALLBACK(shell_app_on_user_time_changed), app);
|
g_signal_handlers_disconnect_by_func (window, G_CALLBACK(shell_app_on_user_time_changed), app);
|
||||||
g_object_unref (window);
|
g_object_unref (window);
|
||||||
app->windows = g_slist_remove (app->windows, window);
|
app->running_state->windows = g_slist_remove (app->running_state->windows, window);
|
||||||
|
|
||||||
|
if (app->running_state->windows == NULL)
|
||||||
|
shell_app_state_transition (app, SHELL_APP_STATE_STOPPED);
|
||||||
|
|
||||||
g_signal_emit (app, shell_app_signals[WINDOWS_CHANGED], 0);
|
g_signal_emit (app, shell_app_signals[WINDOWS_CHANGED], 0);
|
||||||
|
|
||||||
if (app->windows == NULL)
|
|
||||||
{
|
|
||||||
disconnect_workspace_switch (app);
|
|
||||||
|
|
||||||
shell_app_state_transition (app, SHELL_APP_STATE_STOPPED);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -792,9 +826,12 @@ shell_app_request_quit (ShellApp *app)
|
|||||||
{
|
{
|
||||||
GSList *iter;
|
GSList *iter;
|
||||||
|
|
||||||
|
if (shell_app_get_state (app) != SHELL_APP_STATE_RUNNING)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
/* TODO - check for an XSMP connection; we could probably use that */
|
/* TODO - check for an XSMP connection; we could probably use that */
|
||||||
|
|
||||||
for (iter = app->windows; iter; iter = iter->next)
|
for (iter = app->running_state->windows; iter; iter = iter->next)
|
||||||
{
|
{
|
||||||
MetaWindow *win = iter->data;
|
MetaWindow *win = iter->data;
|
||||||
|
|
||||||
@ -806,6 +843,35 @@ shell_app_request_quit (ShellApp *app)
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
create_running_state (ShellApp *app)
|
||||||
|
{
|
||||||
|
MetaScreen *screen;
|
||||||
|
|
||||||
|
g_assert (app->running_state == NULL);
|
||||||
|
|
||||||
|
screen = shell_global_get_screen (shell_global_get ());
|
||||||
|
app->running_state = g_slice_new0 (ShellAppRunningState);
|
||||||
|
app->running_state->refcount = 1;
|
||||||
|
app->running_state->workspace_switch_id =
|
||||||
|
g_signal_connect (screen, "workspace-switched", G_CALLBACK(shell_app_on_ws_switch), app);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
unref_running_state (ShellAppRunningState *state)
|
||||||
|
{
|
||||||
|
MetaScreen *screen;
|
||||||
|
|
||||||
|
state->refcount--;
|
||||||
|
if (state->refcount > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
screen = shell_global_get_screen (shell_global_get ());
|
||||||
|
|
||||||
|
g_signal_handler_disconnect (screen, state->workspace_switch_id);
|
||||||
|
g_slice_free (ShellAppRunningState, state);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
shell_app_init (ShellApp *self)
|
shell_app_init (ShellApp *self)
|
||||||
{
|
{
|
||||||
@ -823,10 +889,11 @@ shell_app_dispose (GObject *object)
|
|||||||
app->info = NULL;
|
app->info = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (app->windows)
|
if (app->running_state)
|
||||||
_shell_app_remove_window (app, app->windows->data);
|
{
|
||||||
|
while (app->running_state->windows)
|
||||||
disconnect_workspace_switch (app);
|
_shell_app_remove_window (app, app->running_state->windows->data);
|
||||||
|
}
|
||||||
|
|
||||||
G_OBJECT_CLASS(shell_app_parent_class)->dispose (object);
|
G_OBJECT_CLASS(shell_app_parent_class)->dispose (object);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user