예제 #1
0
static void
on_transient_window_title_changed (MetaWindow      *window,
                                   GParamSpec      *spec,
                                   ShellWindowTracker *self)
{
  ShellAppSystem *appsys;
  ShellApp *app;
  const char *id;

  /* Check if we now have a mapping using the window title */
  id = get_app_id_from_title (window);
  if (id == NULL)
    return;

  appsys = shell_app_system_get_default ();
  app = shell_app_system_get_app (appsys, id);
  if (app == NULL)
    return;
  g_object_unref (app);

  /* We found an app, don't listen for further title changes */
  g_signal_handlers_disconnect_by_func (window, G_CALLBACK (on_transient_window_title_changed),
                                        self);

  /* It's simplest to just treat this as a remove + add. */
  disassociate_window (self, window);
  track_window (self, window);
}
예제 #2
0
/**
 * 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;
  char *wmclass;
  char *with_desktop;

  appsys = shell_app_system_get_default ();
  wmclass = get_appid_from_window (window);

  if (!wmclass)
    return NULL;

  with_desktop = g_strjoin (NULL, wmclass, ".desktop", NULL);
  g_free (wmclass);

  app = shell_app_system_lookup_heuristic_basename (appsys, with_desktop);
  g_free (with_desktop);

  if (app == NULL)
    {
      const char *id = get_app_id_from_title (window);

      if (id != NULL)
        app = shell_app_system_get_app (appsys, id);
    }

  return app;
}
예제 #3
0
/**
 * shell_window_tracker_get_app_from_pid:
 * @tracker: A #ShellAppSystem
 * @pid: A Unix process identifier
 *
 * Look up the application corresponding to a process.
 *
 * Returns: (transfer none): A #ShellApp, or %NULL if none
 */
ShellApp *
shell_window_tracker_get_app_from_pid (ShellWindowTracker *tracker,
                                       int                 pid)
{
  GSList *running = shell_app_system_get_running (shell_app_system_get_default());
  GSList *iter;
  ShellApp *result = NULL;

  for (iter = running; iter; iter = iter->next)
    {
      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;
    }

  g_slist_free (running);

  return result;
}
예제 #4
0
/**
 * 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.
 */
static ShellApp *
get_app_for_window (ShellWindowTracker    *monitor,
                    MetaWindow         *window)
{
  ShellApp *result;
  const char *startup_id;

  result = NULL;
  /* First, we check whether we already know about this window,
   * if so, just return that.
   */
  if (meta_window_get_window_type (window) == META_WINDOW_NORMAL)
    {
      result = g_hash_table_lookup (monitor->window_to_app, window);
      if (result != NULL)
        {
          g_object_ref (result);
          return result;
        }
    }

  /* Check if the app's WM_CLASS specifies an app */
  result = get_app_from_window_wmclass (window);
  if (result != NULL)
    return result;

  /* Now we check whether we have a match through startup-notification */
  startup_id = meta_window_get_startup_id (window);
  if (startup_id)
    {
      GSList *iter, *sequences;

      sequences = shell_window_tracker_get_startup_sequences (monitor);
      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)
            break;
        }
    }

  /* If we didn't get a startup-notification match, see if we matched
   * any other windows in the group.
   */
  if (result == NULL)
    result = get_app_from_window_group (monitor, window);

  /* Our last resort - we create a fake app from the window */
  if (result == NULL)
    result = shell_app_system_get_app_for_window (shell_app_system_get_default (), window);

  return result;
}
예제 #5
0
static void
shell_app_usage_init (ShellAppUsage *self)
{
  ShellGlobal *global;
  char *shell_userdata_dir, *path;
  GDBusConnection *session_bus;
  ShellWindowTracker *tracker;
  ShellAppSystem *app_system;

  global = shell_global_get ();

  self->app_usages_for_context = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_destroy);

  tracker = shell_window_tracker_get_default ();
  g_signal_connect (tracker, "notify::focus-app", G_CALLBACK (on_focus_app_changed), self);

  app_system = shell_app_system_get_default ();
  g_signal_connect (app_system, "app-state-changed", G_CALLBACK (on_app_state_changed), self);

  session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
  self->session_proxy = g_dbus_proxy_new_sync (session_bus,
                                               G_DBUS_PROXY_FLAGS_NONE,
                                               NULL, /* interface info */
                                               "org.gnome.SessionManager",
                                               "/org/gnome/SessionManager/Presence",
                                               "org.gnome.SessionManager",
                                               NULL, /* cancellable */
                                               NULL /* error */);
  g_signal_connect (self->session_proxy, "g-signal", G_CALLBACK (session_proxy_signal), self);
  g_object_unref (session_bus);

  self->last_idle = 0;
  self->currently_idle = FALSE;
  self->enable_monitoring = FALSE;

  g_object_get (shell_global_get(), "userdatadir", &shell_userdata_dir, NULL),
  path = g_build_filename (shell_userdata_dir, DATA_FILENAME, NULL);
  g_free (shell_userdata_dir);
  self->configfile = g_file_new_for_path (path);
  g_free (path);
  restore_from_file (self);


  self->privacy_settings = g_settings_new(PRIVACY_SCHEMA);
  g_signal_connect (self->privacy_settings,
                    "changed::" ENABLE_MONITORING_KEY,
                    G_CALLBACK (on_enable_monitoring_key_changed),
                    self);
  update_enable_monitoring (self);
}
예제 #6
0
/**
 * shell_startup_sequence_get_app:
 * @sequence: A #ShellStartupSequence
 *
 * Returns: (transfer none): The application being launched, or %NULL if unknown.
 */
ShellApp *
shell_startup_sequence_get_app (ShellStartupSequence *sequence)
{
  const char *appid;
  ShellAppSystem *appsys;
  ShellApp *app;

  appid = sn_startup_sequence_get_application_id ((SnStartupSequence*)sequence);
  if (!appid)
    return NULL;

  appsys = shell_app_system_get_default ();
  app = shell_app_system_lookup_app_for_path (appsys, appid);
  return app;
}
/**
 * shell_startup_sequence_get_app:
 * @sequence: A #ShellStartupSequence
 *
 * Returns: (transfer none): The application being launched, or %NULL if unknown.
 */
ShellApp *
shell_startup_sequence_get_app (ShellStartupSequence *sequence)
{
  const char *appid;
  char *basename;
  ShellAppSystem *appsys;
  ShellApp *app;

  appid = sn_startup_sequence_get_application_id ((SnStartupSequence*)sequence);
  if (!appid)
    return NULL;

  basename = g_path_get_basename (appid);
  appsys = shell_app_system_get_default ();
  app = shell_app_system_lookup_app (appsys, basename);
  g_free (basename);
  return app;
}
예제 #8
0
/**
 * shell_startup_sequence_get_app:
 * @sequence: A #ShellStartupSequence
 *
 * Returns: (transfer full): The application being launched, or %NULL if unknown.
 */
ShellApp *
shell_startup_sequence_get_app (ShellStartupSequence *sequence)
{
#ifdef HAVE_SN_STARTUP_SEQUENCE_GET_APPLICATION_ID
  const char *appid;
  ShellAppSystem *appsys;
  ShellApp *app;

  appid = sn_startup_sequence_get_application_id ((SnStartupSequence*)sequence);
  if (!appid)
    return NULL;

  appsys = shell_app_system_get_default ();
  app = shell_app_system_get_app_for_path (appsys, appid);
  return app;
#else
  return NULL;
#endif
}
예제 #9
0
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)
    return FALSE;

  desktop_file = g_strconcat (id, ".desktop", NULL);
  app = shell_app_system_lookup_app (appsys, desktop_file);

  g_free (desktop_file);
  return app;
}
예제 #10
0
/**
 * shell_app_usage_get_most_used:
 * @usage: the usage instance to request
 * @context: Activity identifier
 *
 * Get a list of most popular applications for a given context.
 *
 * Returns: (element-type ShellApp) (transfer full): List of applications
 */
GSList *
shell_app_usage_get_most_used (ShellAppUsage   *self,
                               const char      *context)
{
  GSList *apps;
  GList *appids, *iter;
  GHashTable *usages;
  ShellAppSystem *appsys;
  SortAppsByUsageData data;

  usages = g_hash_table_lookup (self->app_usages_for_context, context);
  if (usages == NULL)
    return NULL;

  appsys = shell_app_system_get_default ();

  appids = g_hash_table_get_keys (usages);
  apps = NULL;
  for (iter = appids; iter; iter = iter->next)
    {
      const char *appid = iter->data;
      ShellApp *app;

      app = shell_app_system_lookup_app (appsys, appid);
      if (!app)
        continue;

      apps = g_slist_prepend (apps, g_object_ref (app));
    }

  g_list_free (appids);

  data.usage = self;
  data.context_usages = usages;
  apps = g_slist_sort_with_data (apps, sort_apps_by_usage, &data);

  return apps;
}
예제 #11
0
/* Save app data lists to file */
static gboolean
idle_save_application_usage (gpointer data)
{
  ShellAppUsage *self = SHELL_APP_USAGE (data);
  UsageIterator iter;
  const char *current_context;
  const char *context;
  const char *id;
  UsageData *usage;
  GFileOutputStream *output;
  GOutputStream *buffered_output;
  GDataOutputStream *data_output;
  GError *error = NULL;

  self->save_id = 0;

  /* Parent directory is already created by shell-global */
  output = g_file_replace (self->configfile, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error);
  if (!output)
    {
      g_debug ("Could not save applications usage data: %s", error->message);
      g_error_free (error);
      return FALSE;
    }
  buffered_output = g_buffered_output_stream_new (G_OUTPUT_STREAM (output));
  g_object_unref (output);
  data_output = g_data_output_stream_new (G_OUTPUT_STREAM (buffered_output));
  g_object_unref (buffered_output);

  if (!g_data_output_stream_put_string (data_output, "<?xml version=\"1.0\"?>\n<application-state>\n", NULL, &error))
    goto out;

  usage_iterator_init (self, &iter);

  current_context = NULL;
  while (usage_iterator_next (self, &iter, &context, &id, &usage))
    {
      ShellApp *app;

      app = shell_app_system_lookup_app (shell_app_system_get_default(), id);

      if (!app)
        continue;

      if (context != current_context)
        {
          if (current_context != NULL)
            {
              if (!g_data_output_stream_put_string (data_output, "  </context>", NULL, &error))
                goto out;
            }
          current_context = context;
          if (!g_data_output_stream_put_string (data_output, "  <context", NULL, &error))
            goto out;
          if (!write_attribute_string (data_output, "id", context, &error))
            goto out;
          if (!g_data_output_stream_put_string (data_output, ">\n", NULL, &error))
            goto out;
        }
      if (!g_data_output_stream_put_string (data_output, "    <application", NULL, &error))
        goto out;
      if (!write_attribute_string (data_output, "id", id, &error))
        goto out;
      if (!write_attribute_uint (data_output, "open-window-count", shell_app_get_n_windows (app), &error))
        goto out;

      if (!write_attribute_double (data_output, "score", usage->score, &error))
        goto out;
      if (!write_attribute_uint (data_output, "last-seen", usage->last_seen, &error))
        goto out;
      if (!g_data_output_stream_put_string (data_output, "/>\n", NULL, &error))
        goto out;
    }
  if (current_context != NULL)
    {
      if (!g_data_output_stream_put_string (data_output, "  </context>\n", NULL, &error))
        goto out;
    }
  if (!g_data_output_stream_put_string (data_output, "</application-state>\n", NULL, &error))
    goto out;

out:
  if (!error)
    g_output_stream_close_async (G_OUTPUT_STREAM (data_output), 0, NULL, NULL, NULL);
  g_object_unref (data_output);
  if (error)
    {
      g_debug ("Could not save applications usage data: %s", error->message);
      g_error_free (error);
    }
  return FALSE;
}
/**
 * shell_app_info_launch_full:
 * @timestamp: Event timestamp, or 0 for current event timestamp
 * @uris: List of uris to pass to application
 * @workspace: Start on this workspace, or -1 for default
 * @startup_id: (out): Returned startup notification ID, or %NULL if none
 * @error: A #GError
 */
gboolean
shell_app_info_launch_full (ShellAppInfo *info,
                            guint         timestamp,
                            GList        *uris,
                            int           workspace,
                            char        **startup_id,
                            GError      **error)
{
  ShellApp *shell_app;
  GDesktopAppInfo *gapp;
  GdkAppLaunchContext *context;
  gboolean ret;
  ShellGlobal *global;
  MetaScreen *screen;

  if (startup_id)
    *startup_id = NULL;

  if (info->type == SHELL_APP_INFO_TYPE_WINDOW)
    {
      /* We can't pass URIs into a window; shouldn't hit this
       * code path.  If we do, fix the caller to disallow it.
       */
      g_return_val_if_fail (uris == NULL, TRUE);

      meta_window_activate (info->window, timestamp);
      return TRUE;
    }
  else if (info->type == SHELL_APP_INFO_TYPE_ENTRY)
    {
      /* Can't use g_desktop_app_info_new, see bug 614879 */
      const char *filename = gmenu_tree_entry_get_desktop_file_path ((GMenuTreeEntry *)info->entry);
      gapp = g_desktop_app_info_new_from_filename (filename);
    }
  else
    {
      char *filename = shell_app_info_get_desktop_file_path (info);
      gapp = g_desktop_app_info_new_from_filename (filename);
      g_free (filename);
    }

  if (!gapp)
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Not found");
      return FALSE;
    }

  global = shell_global_get ();
  screen = shell_global_get_screen (global);

  if (timestamp == 0)
    timestamp = clutter_get_current_event_time ();

  if (workspace < 0)
    workspace = meta_screen_get_active_workspace_index (screen);

  context = gdk_app_launch_context_new ();
  gdk_app_launch_context_set_timestamp (context, timestamp);
  gdk_app_launch_context_set_desktop (context, workspace);

  shell_app = shell_app_system_get_app (shell_app_system_get_default (),
                                        shell_app_info_get_id (info));

  /* In the case where we know an app, we handle reaping the child internally,
   * in the window tracker.
   */
  if (shell_app != NULL)
    ret = g_desktop_app_info_launch_uris_as_manager (gapp, uris,
                                                     G_APP_LAUNCH_CONTEXT (context),
                                                     G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
                                                     NULL, NULL,
                                                     _gather_pid_callback, shell_app,
                                                     error);
  else
    ret = g_desktop_app_info_launch_uris_as_manager (gapp, uris,
                                                     G_APP_LAUNCH_CONTEXT (context),
                                                     G_SPAWN_SEARCH_PATH,
                                                     NULL, NULL,
                                                     NULL, NULL,
                                                     error);

  g_object_unref (G_OBJECT (gapp));

  return ret;
}
예제 #13
0
/*
 * 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

     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.
  */

  /* first try a match from WM_CLASS (instance part) to StartupWMClass */
  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);

  /* 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);
  if (app != NULL)
    return g_object_ref (app);

  /* then try a match from WM_CLASS (instance part) to .desktop */
  app = shell_app_system_lookup_desktop_wmclass (appsys, wm_instance);
  if (app != NULL)
    return g_object_ref (app);

  /* 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);

  return NULL;
}