[mutter/wip/carlosg/sound-abstraction: 1/4] core: Add MetaSoundPlayer abstraction



commit f4d681e2480d123515cb4ca3e44712a20c95c8ec
Author: Carlos Garnacho <carlosg gnome org>
Date:   Sun Dec 9 12:44:20 2018 +0100

    core: Add MetaSoundPlayer abstraction
    
    This is a simple libcanberra abstraction object, so we are able
    to play file/theme sounds without poking into GTK+/X11. Play
    requests are delegated to a separate thread, so we don't block
    UI on cards that are slow to wake up from power saving.

 src/Makefile.am              |   2 +
 src/core/display-private.h   |   2 +
 src/core/display.c           |  16 +++
 src/core/meta-sound-player.c | 290 +++++++++++++++++++++++++++++++++++++++++++
 src/meson.build              |   1 +
 src/meta/display.h           |   3 +
 src/meta/meson.build         |   1 +
 src/meta/meta-sound-player.h |  39 ++++++
 8 files changed, 354 insertions(+)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 6e7c01158..117b6b802 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -293,6 +293,7 @@ libmutter_@LIBMUTTER_API_VERSION@_la_SOURCES =      \
        meta/meta-inhibit-shortcuts-dialog.h    \
        core/meta-inhibit-shortcuts-dialog-default.c    \
        core/meta-inhibit-shortcuts-dialog-default-private.h \
+       core/meta-sound-player.c                \
        core/delete.c                           \
        core/display.c                          \
        core/display-private.h                  \
@@ -583,6 +584,7 @@ libmutterinclude_headers =                  \
        meta/meta-settings.h                    \
        meta/meta-shaped-texture.h              \
        meta/meta-shadow-factory.h              \
+       meta/meta-sound-player.h                \
        meta/meta-stage.h                       \
        meta/meta-startup-notification.h        \
        meta/meta-window-actor.h                \
diff --git a/src/core/display-private.h b/src/core/display-private.h
index 576a4254e..4156c2342 100644
--- a/src/core/display-private.h
+++ b/src/core/display-private.h
@@ -241,6 +241,8 @@ struct _MetaDisplay
 
   MetaBell *bell;
   MetaWorkspaceManager *workspace_manager;
+
+  MetaSoundPlayer *sound_player;
 };
 
 struct _MetaDisplayClass
diff --git a/src/core/display.c b/src/core/display.c
index f96f36b38..02b6f92e0 100644
--- a/src/core/display.c
+++ b/src/core/display.c
@@ -66,6 +66,7 @@
 #include "meta/main.h"
 #include "meta/meta-backend.h"
 #include "meta/meta-enum-types.h"
+#include "meta/meta-sound-player.h"
 #include "meta/meta-x11-errors.h"
 #include "meta/prefs.h"
 #include "x11/meta-startup-notification-x11.h"
@@ -777,6 +778,8 @@ meta_display_open (void)
 
   meta_idle_monitor_init_dbus ();
 
+  display->sound_player = g_object_new (META_TYPE_SOUND_PLAYER, NULL);
+
   /* Done opening new display */
   display->display_opening = FALSE;
 
@@ -954,6 +957,7 @@ meta_display_close (MetaDisplay *display,
   g_clear_object (&display->bell);
   g_clear_object (&display->startup_notification);
   g_clear_object (&display->workspace_manager);
+  g_clear_object (&display->sound_player);
 
   g_object_unref (display);
   the_display = NULL;
@@ -3629,3 +3633,15 @@ meta_display_generate_window_id (MetaDisplay *display)
   /* We can overflow here, that's fine */
   return (base_window_id + last_window_id++);
 }
+
+/**
+ * meta_display_get_sound_player:
+ * @display: a #MetaDisplay
+ *
+ * Returns: (transfer none): The sound player of the display
+ */
+MetaSoundPlayer *
+meta_display_get_sound_player (MetaDisplay *display)
+{
+  return display->sound_player;
+}
diff --git a/src/core/meta-sound-player.c b/src/core/meta-sound-player.c
new file mode 100644
index 000000000..94ae18a79
--- /dev/null
+++ b/src/core/meta-sound-player.c
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2018 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Carlos Garnacho <carlosg gnome org>
+ */
+
+#include "config.h"
+
+#include <canberra.h>
+
+#include "meta/meta-sound-player.h"
+
+#define EVENT_SOUNDS_KEY "event-sounds"
+#define THEME_NAME_KEY   "theme-name"
+
+typedef struct _MetaPlayRequest MetaPlayRequest;
+
+struct _MetaSoundPlayer
+{
+  GObject parent;
+  GThreadPool *queue;
+  GSettings *settings;
+  ca_context *context;
+  uint32_t id_pool;
+};
+
+struct _MetaPlayRequest
+{
+  ca_proplist *props;
+  uint32_t id;
+  guint cancel_id;
+  GCancellable *cancellable;
+  MetaSoundPlayer *player;
+};
+
+const char * const cache_whitelist[] = {
+  "bell-window-system",
+  "desktop-switch-left",
+  "desktop-switch-right",
+  "desktop-switch-up",
+  "desktop-switch-down",
+  NULL
+};
+
+G_DEFINE_TYPE (MetaSoundPlayer, meta_sound_player, G_TYPE_OBJECT)
+
+static MetaPlayRequest *
+meta_play_request_new (MetaSoundPlayer *player,
+                       ca_proplist     *props,
+                       GCancellable    *cancellable)
+{
+  MetaPlayRequest *req;
+
+  req = g_new0 (MetaPlayRequest, 1);
+  req->props = props;
+  req->player = player;
+  g_set_object (&req->cancellable, cancellable);
+
+  return req;
+}
+
+static void
+meta_play_request_free (MetaPlayRequest *req)
+{
+  g_clear_object (&req->cancellable);
+  ca_proplist_destroy (req->props);
+  g_free (req);
+}
+
+static void
+meta_sound_player_finalize (GObject *object)
+{
+  MetaSoundPlayer *player = META_SOUND_PLAYER (object);
+
+  g_object_unref (player->settings);
+  g_thread_pool_free (player->queue, TRUE, TRUE);
+  ca_context_destroy (player->context);
+
+  G_OBJECT_CLASS (meta_sound_player_parent_class)->finalize (object);
+}
+
+static void
+meta_sound_player_class_init (MetaSoundPlayerClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = meta_sound_player_finalize;
+}
+
+static void
+cancelled_cb (GCancellable    *cancellable,
+              MetaPlayRequest *req)
+{
+  ca_context_cancel (req->player->context, req->id);
+}
+
+static void
+finish_cb (ca_context *context,
+           uint32_t    id,
+           int         error_code,
+           gpointer    user_data)
+{
+  MetaPlayRequest *req = user_data;
+
+  g_cancellable_disconnect (req->cancellable, req->cancel_id);
+  meta_play_request_free (req);
+}
+
+static void
+play_sound (MetaPlayRequest *req,
+            MetaSoundPlayer *player)
+{
+  req->id = player->id_pool++;
+
+  if (ca_context_play_full (player->context, req->id, req->props,
+                            finish_cb, req) != CA_SUCCESS)
+    {
+      meta_play_request_free (req);
+      return;
+    }
+
+  if (req->cancellable)
+    {
+      req->cancel_id =
+        g_cancellable_connect (req->cancellable,
+                               G_CALLBACK (cancelled_cb), req, NULL);
+    }
+}
+
+static void
+settings_changed_cb (GSettings       *settings,
+                     const char      *key,
+                     MetaSoundPlayer *player)
+{
+  if (strcmp (key, EVENT_SOUNDS_KEY) == 0)
+    {
+      gboolean enabled;
+
+      enabled = g_settings_get_boolean (settings, EVENT_SOUNDS_KEY);
+      ca_context_change_props (player->context, CA_PROP_CANBERRA_ENABLE,
+                               enabled ? "1" : "0", NULL);
+    }
+  else if (strcmp (key, THEME_NAME_KEY) == 0)
+    {
+      char *theme_name;
+
+      theme_name = g_settings_get_string (settings, THEME_NAME_KEY);
+      ca_context_change_props (player->context, CA_PROP_CANBERRA_XDG_THEME_NAME,
+                               theme_name, NULL);
+      g_free (theme_name);
+    }
+}
+
+static ca_context *
+create_context (GSettings *settings)
+{
+  ca_context *context;
+  ca_proplist *props;
+  gboolean enabled;
+  char *theme_name;
+
+  if (ca_context_create (&context) != CA_SUCCESS)
+    return NULL;
+
+  if (ca_proplist_create (&props) != CA_SUCCESS)
+    {
+      ca_context_destroy (context);
+      return NULL;
+    }
+
+  ca_proplist_sets (props, CA_PROP_APPLICATION_NAME, "Mutter");
+
+  enabled = g_settings_get_boolean (settings, EVENT_SOUNDS_KEY);
+  ca_proplist_sets (props, CA_PROP_CANBERRA_ENABLE, enabled ? "1" : "0");
+
+  theme_name = g_settings_get_string (settings, THEME_NAME_KEY);
+  ca_proplist_sets (props, CA_PROP_CANBERRA_XDG_THEME_NAME, theme_name);
+  g_free (theme_name);
+
+  ca_context_change_props_full (context, props);
+  ca_proplist_destroy (props);
+
+  return context;
+}
+
+static void
+meta_sound_player_init (MetaSoundPlayer *player)
+{
+  player->queue = g_thread_pool_new ((GFunc) play_sound,
+                                    player, 1, FALSE, NULL);
+  player->settings = g_settings_new ("org.gnome.desktop.sound");
+  player->context = create_context (player->settings);
+
+  g_signal_connect (player->settings, "changed",
+                    G_CALLBACK (settings_changed_cb), player);
+}
+
+static void
+build_ca_proplist (ca_proplist  *props,
+                   const char   *event_property,
+                   const char   *event_id,
+                   const char   *event_description)
+{
+  ca_proplist_sets (props, event_property, event_id);
+  ca_proplist_sets (props, CA_PROP_EVENT_DESCRIPTION, event_description);
+}
+
+/**
+ * meta_sound_player_play_from_theme:
+ * @sound: a #MetaSoundPlayer
+ * @name: sound theme name of the event
+ * @description: description of the event
+ * @cancellable: cancellable for the request
+ *
+ * Plays a sound from the sound theme.
+ **/
+void
+meta_sound_player_play_from_theme (MetaSoundPlayer *player,
+                                   const char      *name,
+                                   const char      *description,
+                                   GCancellable    *cancellable)
+{
+  MetaPlayRequest *req;
+  ca_proplist *props;
+
+  g_return_if_fail (META_IS_SOUND_PLAYER (player));
+  g_return_if_fail (name != NULL);
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  ca_proplist_create (&props);
+  build_ca_proplist (props, CA_PROP_EVENT_ID, name, description);
+
+  if (g_strv_contains (cache_whitelist, name))
+    ca_proplist_sets (props, CA_PROP_CANBERRA_CACHE_CONTROL, "permanent");
+  else
+    ca_proplist_sets (props, CA_PROP_CANBERRA_CACHE_CONTROL, "volatile");
+
+  req = meta_play_request_new (player, props, cancellable);
+  g_thread_pool_push (player->queue, req, NULL);
+}
+
+/**
+ * meta_sound_player_play_from_file:
+ * @sound: a #MetaSoundPlayer
+ * @file: file to play
+ * @description: description of the played sound
+ * @cancellable: cancellable for the request
+ *
+ * Plays a sound from a file.
+ **/
+void
+meta_sound_player_play_from_file (MetaSoundPlayer *player,
+                                  GFile           *file,
+                                  const char      *description,
+                                  GCancellable    *cancellable)
+{
+  MetaPlayRequest *req;
+  ca_proplist *props;
+  char *path;
+
+  g_return_if_fail (META_IS_SOUND_PLAYER (player));
+  g_return_if_fail (G_IS_FILE (file));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  path = g_file_get_path (file);
+  g_return_if_fail (path != NULL);
+
+  ca_proplist_create (&props);
+  build_ca_proplist (props, CA_PROP_MEDIA_FILENAME, path, description);
+  ca_proplist_sets (props, CA_PROP_CANBERRA_CACHE_CONTROL, "volatile");
+  g_free (path);
+
+  req = meta_play_request_new (player, props, cancellable);
+  g_thread_pool_push (player->queue, req, NULL);
+}
diff --git a/src/meson.build b/src/meson.build
index 3e077dd0d..44816ea27 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -333,6 +333,7 @@ mutter_sources = [
   'core/meta-inhibit-shortcuts-dialog.c',
   'core/meta-inhibit-shortcuts-dialog-default.c',
   'core/meta-inhibit-shortcuts-dialog-default-private.h',
+  'core/meta-sound-player.c',
   'core/meta-workspace-manager.c',
   'core/meta-workspace-manager-private.h',
   'core/place.c',
diff --git a/src/meta/display.h b/src/meta/display.h
index 68ddee7db..918817809 100644
--- a/src/meta/display.h
+++ b/src/meta/display.h
@@ -27,6 +27,7 @@
 #include <meta/prefs.h>
 #include <meta/common.h>
 #include <meta/workspace.h>
+#include <meta/meta-sound-player.h>
 #include <meta/meta-startup-notification.h>
 
 /**
@@ -230,4 +231,6 @@ MetaWorkspaceManager *meta_display_get_workspace_manager (MetaDisplay *display);
  */
 MetaStartupNotification * meta_display_get_startup_notification (MetaDisplay *display);
 
+MetaSoundPlayer * meta_display_get_sound_player (MetaDisplay *display);
+
 #endif
diff --git a/src/meta/meson.build b/src/meta/meson.build
index bdd77a08e..a5a6ad2be 100644
--- a/src/meta/meson.build
+++ b/src/meta/meson.build
@@ -24,6 +24,7 @@ mutter_public_headers = [
   'meta-settings.h',
   'meta-shadow-factory.h',
   'meta-shaped-texture.h',
+  'meta-sound-player.h',
   'meta-stage.h',
   'meta-startup-notification.h',
   'meta-window-actor.h',
diff --git a/src/meta/meta-sound-player.h b/src/meta/meta-sound-player.h
new file mode 100644
index 000000000..e6ddabd34
--- /dev/null
+++ b/src/meta/meta-sound-player.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Author: Carlos Garnacho <carlosg gnome org>
+ */
+#ifndef META_SOUND_PLAYER_H
+#define META_SOUND_PLAYER_H
+
+#include <gio/gio.h>
+
+#define META_TYPE_SOUND_PLAYER (meta_sound_player_get_type ())
+G_DECLARE_FINAL_TYPE (MetaSoundPlayer, meta_sound_player,
+                      META, SOUND_PLAYER, GObject)
+
+void meta_sound_player_play_from_theme (MetaSoundPlayer *player,
+                                        const char      *name,
+                                        const char      *description,
+                                        GCancellable    *cancellable);
+void meta_sound_player_play_from_file  (MetaSoundPlayer *player,
+                                        GFile           *file,
+                                        const char      *description,
+                                        GCancellable    *cancellable);
+
+#endif /* META_SOUND_PLAYER_H */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]