[gnome-shell/bilelmoussaoui/libcanberra-sound] sound player: Add Shell implementation of mutter#2375




commit 5961bf13052b9634a47fb645a9ebbb93e907eea0
Author: Bilal Elmoussaoui <belmouss redhat com>
Date:   Thu Apr 14 12:35:09 2022 +0200

    sound player: Add Shell implementation of mutter#2375

 meson.build                |   2 +
 src/gnome-shell-plugin.c   |  11 ++
 src/meson.build            |   5 +-
 src/shell-global-private.h |   4 +
 src/shell-global.c         |  19 ++++
 src/shell-sound-player.c   | 262 +++++++++++++++++++++++++++++++++++++++++++++
 src/shell-sound-player.h   |  37 +++++++
 7 files changed, 339 insertions(+), 1 deletion(-)
---
diff --git a/meson.build b/meson.build
index 8d7478f9b2..243d6d6938 100644
--- a/meson.build
+++ b/meson.build
@@ -31,6 +31,7 @@ schemas_req = '>= 42.beta'
 startup_req = '>= 0.11'
 ibus_req = '>= 1.5.19'
 gnome_desktop_req = '>= 3.35.90'
+libcanberra_req = '>= 0.26'
 
 bt_req = '>= 3.9.0'
 gst_req = '>= 0.11.92'
@@ -97,6 +98,7 @@ bt_dep = dependency('gnome-bluetooth-3.0', version: bt_req, required: false)
 gst_dep = dependency('gstreamer-1.0', version: gst_req, required: false)
 gst_base_dep = dependency('gstreamer-base-1.0', required: false)
 pipewire_dep = dependency('libpipewire-0.3', required: false)
+libcanberra_dep = dependency('libcanberra', version: libcanberra_req)
 
 recorder_deps = []
 enable_recorder = gst_dep.found() and gst_base_dep.found() and pipewire_dep.found()
diff --git a/src/gnome-shell-plugin.c b/src/gnome-shell-plugin.c
index 5364f043a0..c25d5b68e2 100644
--- a/src/gnome-shell-plugin.c
+++ b/src/gnome-shell-plugin.c
@@ -352,6 +352,15 @@ gnome_shell_plugin_locate_pointer (MetaPlugin *plugin)
   _shell_global_locate_pointer (shell_plugin->global);
 }
 
+static void
+gnome_shell_plugin_play_sound (MetaPlugin *plugin,
+                               const char *name,
+                               const char *description)
+{ 
+  GnomeShellPlugin *shell_plugin = GNOME_SHELL_PLUGIN (plugin);
+  _shell_global_play_sound (shell_plugin->global, name, description);
+}
+
 static void
 gnome_shell_plugin_class_init (GnomeShellPluginClass *klass)
 {
@@ -385,6 +394,8 @@ gnome_shell_plugin_class_init (GnomeShellPluginClass *klass)
   plugin_class->create_close_dialog = gnome_shell_plugin_create_close_dialog;
   plugin_class->create_inhibit_shortcuts_dialog = gnome_shell_plugin_create_inhibit_shortcuts_dialog;
 
+  plugin_class->play_sound = gnome_shell_plugin_play_sound;
+
   plugin_class->locate_pointer = gnome_shell_plugin_locate_pointer;
 }
 
diff --git a/src/meson.build b/src/meson.build
index 8dd0887c63..237e83ada4 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -61,7 +61,8 @@ gnome_shell_deps = [
   gi_dep,
   polkit_dep,
   gcr_dep,
-  libsystemd_dep
+  libsystemd_dep,
+  libcanberra_dep,
 ]
 
 gnome_shell_deps += nm_deps
@@ -151,6 +152,8 @@ libshell_sources = [
   'shell-secure-text-buffer.c',
   'shell-secure-text-buffer.h',
   'shell-square-bin.c',
+  'shell-sound-player.c',
+  'shell-sound-player.h',
   'shell-stack.c',
   'shell-tray-icon.c',
   'shell-tray-manager.c',
diff --git a/src/shell-global-private.h b/src/shell-global-private.h
index 9969691cb4..1e37267f15 100644
--- a/src/shell-global-private.h
+++ b/src/shell-global-private.h
@@ -20,4 +20,8 @@ gboolean _shell_global_check_xdnd_event (ShellGlobal  *global,
 
 void _shell_global_locate_pointer (ShellGlobal  *global);
 
+void _shell_global_play_sound (ShellGlobal *global,
+                               const char  *name,
+                               const char  *description);
+
 #endif /* __SHELL_GLOBAL_PRIVATE_H__ */
diff --git a/src/shell-global.c b/src/shell-global.c
index efe1271b5a..eddb104894 100644
--- a/src/shell-global.c
+++ b/src/shell-global.c
@@ -39,6 +39,7 @@
 #include "shell-enum-types.h"
 #include "shell-global-private.h"
 #include "shell-perf-log.h"
+#include "shell-sound-player.h"
 #include "shell-window-tracker.h"
 #include "shell-wm.h"
 #include "shell-util.h"
@@ -83,6 +84,8 @@ struct _ShellGlobal {
   gboolean frame_timestamps;
   gboolean frame_finish_timestamp;
 
+  ShellSoundPlayer *sound_player;
+
   GDBusProxy *switcheroo_control;
   GCancellable *switcheroo_cancellable;
 };
@@ -390,6 +393,8 @@ shell_global_init (ShellGlobal *global)
   byteorder_string = "BE";
 #endif
 
+  global->sound_player = g_object_new (SHELL_TYPE_SOUND_PLAYER, NULL);
+
   /* And the runtime state */
   path = g_strdup_printf ("%s/gnome-shell/runtime-state-%s.%s",
                           g_get_user_runtime_dir (),
@@ -473,6 +478,8 @@ shell_global_finalize (GObject *object)
   g_clear_object (&global->userdatadir_path);
   g_clear_object (&global->runtime_state_path);
 
+  g_clear_object (&global->sound_player);
+
   g_free (global->session_mode);
   g_free (global->imagedir);
   g_free (global->userdatadir);
@@ -1842,3 +1849,15 @@ _shell_global_locate_pointer (ShellGlobal *global)
 {
   g_signal_emit (global, shell_global_signals[LOCATE_POINTER], 0);
 }
+
+
+void 
+_shell_global_play_sound (ShellGlobal *global,
+                          const char  *name,
+                          const char  *description)
+{
+  shell_sound_player_play_from_theme (global->sound_player,
+                                      name,
+                                      description,
+                                      NULL);
+}
diff --git a/src/shell-sound-player.c b/src/shell-sound-player.c
new file mode 100644
index 0000000000..ba6fddaa62
--- /dev/null
+++ b/src/shell-sound-player.c
@@ -0,0 +1,262 @@
+/*
+ * 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 "shell-sound-player.h"
+
+#define EVENT_SOUNDS_KEY "event-sounds"
+#define THEME_NAME_KEY   "theme-name"
+
+typedef struct _ShellPlayRequest ShellPlayRequest;
+
+struct _ShellSoundPlayer
+{
+  GObject parent;
+  GThreadPool *queue;
+  GSettings *settings;
+  ca_context *context;
+  uint32_t id_pool;
+};
+
+struct _ShellPlayRequest
+{
+  ca_proplist *props;
+  uint32_t id;
+  gulong cancel_id;
+  GCancellable *cancellable;
+  ShellSoundPlayer *player;
+};
+
+const char * const cache_allow_list[] = {
+  "bell-window-system",
+  "desktop-switch-left",
+  "desktop-switch-right",
+  "desktop-switch-up",
+  "desktop-switch-down",
+  NULL
+};
+
+G_DEFINE_TYPE (ShellSoundPlayer, shell_sound_player, G_TYPE_OBJECT)
+
+static ShellPlayRequest *
+shell_play_request_new (ShellSoundPlayer *player,
+                        ca_proplist      *props,
+                        GCancellable     *cancellable)
+{
+  ShellPlayRequest *req;
+
+  req = g_new0 (ShellPlayRequest, 1);
+  req->props = props;
+  req->player = player;
+  g_set_object (&req->cancellable, cancellable);
+
+  return req;
+}
+
+static void
+shell_play_request_free (ShellPlayRequest *req)
+{
+  g_clear_object (&req->cancellable);
+  ca_proplist_destroy (req->props);
+  g_free (req);
+}
+
+static void
+shell_sound_player_finalize (GObject *object)
+{
+  ShellSoundPlayer *player = SHELL_SOUND_PLAYER (object);
+
+  g_object_unref (player->settings);
+  g_thread_pool_free (player->queue, FALSE, TRUE);
+  ca_context_destroy (player->context);
+
+  G_OBJECT_CLASS (shell_sound_player_parent_class)->finalize (object);
+}
+
+static void
+shell_sound_player_class_init (ShellSoundPlayerClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = shell_sound_player_finalize;
+}
+
+static void
+cancelled_cb (GCancellable    *cancellable,
+              ShellPlayRequest *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)
+{
+  ShellPlayRequest *req = user_data;
+
+  if (error_code != CA_ERROR_CANCELED)
+    g_cancellable_disconnect (req->cancellable, req->cancel_id);
+  else if (req->cancellable != NULL)
+    g_clear_signal_handler (&req->cancel_id, req->cancellable);
+
+  shell_play_request_free (req);
+}
+
+static void
+play_sound (ShellPlayRequest *req,
+            ShellSoundPlayer *player)
+{
+  req->id = player->id_pool++;
+
+  if (ca_context_play_full (player->context, req->id, req->props,
+                            finish_cb, req) != CA_SUCCESS)
+    {
+      shell_play_request_free (req);
+      return;
+    }
+
+  if (req->cancellable)
+    {
+      gulong cancel_id =
+        g_cancellable_connect (req->cancellable,
+                               G_CALLBACK (cancelled_cb), req, NULL);
+      if (cancel_id)
+        req->cancel_id = cancel_id;
+    }
+}
+
+static void
+settings_changed_cb (GSettings       *settings,
+                     const char      *key,
+                     ShellSoundPlayer *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
+shell_sound_player_init (ShellSoundPlayer *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);
+}
+
+/**
+ * shell_sound_player_play_from_theme:
+ * @player: a #ShellSoundPlayer
+ * @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
+shell_sound_player_play_from_theme (ShellSoundPlayer *player,
+                                   const char      *name,
+                                   const char      *description,
+                                   GCancellable    *cancellable)
+{
+  ShellPlayRequest *req;
+  ca_proplist *props;
+
+  g_return_if_fail (SHELL_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_allow_list, name))
+    ca_proplist_sets (props, CA_PROP_CANBERRA_CACHE_CONTROL, "permanent");
+  else
+    ca_proplist_sets (props, CA_PROP_CANBERRA_CACHE_CONTROL, "volatile");
+
+  req = shell_play_request_new (player, props, cancellable);
+  g_thread_pool_push (player->queue, req, NULL);
+}
+
diff --git a/src/shell-sound-player.h b/src/shell-sound-player.h
new file mode 100644
index 0000000000..4c9e6c3d25
--- /dev/null
+++ b/src/shell-sound-player.h
@@ -0,0 +1,37 @@
+/*
+ * 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 SHELL_SOUND_PLAYER_H
+#define SHELL_SOUND_PLAYER_H
+
+#include <gio/gio.h>
+
+#define SHELL_TYPE_SOUND_PLAYER (shell_sound_player_get_type ())
+
+G_DECLARE_FINAL_TYPE (ShellSoundPlayer, shell_sound_player,
+                      SHELL, SOUND_PLAYER, GObject)
+
+
+void shell_sound_player_play_from_theme (ShellSoundPlayer *player,
+                                         const char       *name,
+                                         const char       *description,
+                                         GCancellable     *cancellable);
+
+#endif /* SHELL_SOUND_PLAYER_H */


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