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
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
on_gtk_application_id_changed (MetaWindow  *window,
                               GParamSpec  *pspec,
                               gpointer     user_data)
{
  ShellWindowTracker *self = SHELL_WINDOW_TRACKER (user_data);
  tracked_window_changed (self, window);
}
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
shell_window_tracker_finalize (GObject *object)
{
  ShellWindowTracker *self = SHELL_WINDOW_TRACKER (object);

  g_hash_table_destroy (self->window_to_app);
  g_hash_table_destroy (self->launched_pid_to_app);

  G_OBJECT_CLASS (shell_window_tracker_parent_class)->finalize(object);
}
static void
shell_window_tracker_finalize (GObject *object)
{
  ShellWindowTracker *self = SHELL_WINDOW_TRACKER (object);
  int i;

  g_hash_table_destroy (self->running_apps);
  g_hash_table_destroy (self->window_to_app);
  for (i = 0; title_patterns[i].app_id; i++)
    g_regex_unref (title_patterns[i].regex);

  G_OBJECT_CLASS (shell_window_tracker_parent_class)->finalize(object);
}
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);
  /* also just recaulcuate the focused app, in case it was the focused
     window that changed */
  update_focus_app (self);
}
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;
    }
}