LCOV - code coverage report
Current view: top level - src/plugins/pulseaudio - valent-pa-mixer.c (source / functions) Coverage Total Hit
Test: Code Coverage Lines: 0.0 % 150 0
Test Date: 2024-12-21 23:29:11 Functions: 0.0 % 19 0
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : // SPDX-License-Identifier: GPL-3.0-or-later
       2                 :             : // SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>
       3                 :             : 
       4                 :             : #define G_LOG_DOMAIN "valent-pa-mixer"
       5                 :             : 
       6                 :             : #include "config.h"
       7                 :             : 
       8                 :             : #include <gvc-mixer-control.h>
       9                 :             : #include <gvc-mixer-sink.h>
      10                 :             : #include <gvc-mixer-source.h>
      11                 :             : #include <gvc-mixer-stream.h>
      12                 :             : #include <valent.h>
      13                 :             : 
      14                 :             : #include "valent-pa-mixer.h"
      15                 :             : #include "valent-pa-stream.h"
      16                 :             : 
      17                 :             : 
      18                 :             : struct _ValentPaMixer
      19                 :             : {
      20                 :             :   ValentMixerAdapter  parent_instance;
      21                 :             : 
      22                 :             :   GvcMixerControl    *control;
      23                 :             : 
      24                 :             :   GHashTable         *streams;
      25                 :             :   unsigned int        input;
      26                 :             :   unsigned int        output;
      27                 :             : };
      28                 :             : 
      29                 :           0 : G_DEFINE_FINAL_TYPE (ValentPaMixer, valent_pa_mixer, VALENT_TYPE_MIXER_ADAPTER)
      30                 :             : 
      31                 :             : 
      32                 :             : /*
      33                 :             :  * Gvc Callbacks
      34                 :             :  */
      35                 :             : static void
      36                 :           0 : on_default_sink_changed (GvcMixerControl *control,
      37                 :             :                          unsigned int     stream_id,
      38                 :             :                          ValentPaMixer   *self)
      39                 :             : {
      40                 :           0 :   g_assert (GVC_IS_MIXER_CONTROL (control));
      41                 :           0 :   g_assert (VALENT_IS_PA_MIXER (self));
      42                 :             : 
      43                 :           0 :   if (self->output == stream_id)
      44                 :             :     return;
      45                 :             : 
      46                 :           0 :   self->output = stream_id;
      47                 :           0 :   g_object_notify (G_OBJECT (self), "default-output");
      48                 :             : }
      49                 :             : 
      50                 :             : static void
      51                 :           0 : on_default_source_changed (GvcMixerControl *control,
      52                 :             :                            unsigned int     stream_id,
      53                 :             :                            ValentPaMixer   *self)
      54                 :             : {
      55                 :           0 :   g_assert (GVC_IS_MIXER_CONTROL (control));
      56                 :           0 :   g_assert (VALENT_IS_PA_MIXER (self));
      57                 :             : 
      58                 :           0 :   if (self->input == stream_id)
      59                 :             :     return;
      60                 :             : 
      61                 :           0 :   self->input = stream_id;
      62                 :           0 :   g_object_notify (G_OBJECT (self), "default-input");
      63                 :             : }
      64                 :             : 
      65                 :             : static void
      66                 :           0 : on_stream_added (GvcMixerControl *control,
      67                 :             :                  unsigned int     stream_id,
      68                 :             :                  ValentPaMixer   *self)
      69                 :             : {
      70                 :           0 :   GvcMixerStream *base_stream = NULL;
      71                 :           0 :   ValentMixerStream *stream = NULL;
      72                 :           0 :   ValentMixerDirection direction;
      73                 :             : 
      74                 :           0 :   base_stream = gvc_mixer_control_lookup_stream_id (control, stream_id);
      75                 :             : 
      76                 :             :   /* Ignore source outputs and sink inputs */
      77                 :           0 :   if (GVC_IS_MIXER_SINK (base_stream))
      78                 :             :     direction = VALENT_MIXER_OUTPUT;
      79                 :           0 :   else if (GVC_IS_MIXER_SOURCE (base_stream))
      80                 :             :     direction = VALENT_MIXER_INPUT;
      81                 :             :   else
      82                 :             :     return;
      83                 :             : 
      84                 :           0 :   stream = g_object_new (VALENT_TYPE_PA_STREAM,
      85                 :             :                          "base-stream", base_stream,
      86                 :             :                          "direction",   direction,
      87                 :             :                          NULL);
      88                 :             : 
      89                 :           0 :   if (!g_hash_table_replace (self->streams, GUINT_TO_POINTER (stream_id), stream))
      90                 :             :     {
      91                 :           0 :       g_warning ("%s: Duplicate Stream: %s",
      92                 :             :                  G_OBJECT_TYPE_NAME (self),
      93                 :             :                  valent_mixer_stream_get_name (stream));
      94                 :             :     }
      95                 :             : 
      96                 :           0 :   valent_mixer_adapter_stream_added (VALENT_MIXER_ADAPTER (self), stream);
      97                 :             : }
      98                 :             : 
      99                 :             : static void
     100                 :           0 : on_stream_removed (GvcMixerControl *control,
     101                 :             :                    unsigned int     stream_id,
     102                 :             :                    ValentPaMixer   *self)
     103                 :             : {
     104                 :           0 :   ValentMixerStream *stream = NULL;
     105                 :             : 
     106                 :           0 :   g_assert (GVC_IS_MIXER_CONTROL (control));
     107                 :           0 :   g_assert (VALENT_IS_PA_MIXER (self));
     108                 :             : 
     109                 :           0 :   stream = g_hash_table_lookup (self->streams, GUINT_TO_POINTER (stream_id));
     110                 :             : 
     111                 :           0 :   if (stream == NULL)
     112                 :             :     return;
     113                 :             : 
     114                 :             :   /* FIXME: If the stream being removed is the default, the change notification
     115                 :             :    *        will come after the removal notification. As a side effect, if the
     116                 :             :    *        kdeconnect-android activity is open it will automatically select a
     117                 :             :    *        remaining stream and override any automatic selection the system
     118                 :             :    *        wants to perform.
     119                 :             :    */
     120                 :             : 
     121                 :           0 :   valent_mixer_adapter_stream_removed (VALENT_MIXER_ADAPTER (self), stream);
     122                 :           0 :   g_hash_table_remove (self->streams, GUINT_TO_POINTER (stream_id));
     123                 :             : }
     124                 :             : 
     125                 :             : static void
     126                 :           0 : on_stream_changed (GvcMixerControl *control,
     127                 :             :                    unsigned int     stream_id,
     128                 :             :                    ValentPaMixer   *self)
     129                 :             : {
     130                 :           0 :   ValentMixerStream *stream = NULL;
     131                 :             : 
     132                 :           0 :   g_assert (GVC_IS_MIXER_CONTROL (control));
     133                 :           0 :   g_assert (VALENT_IS_PA_MIXER (self));
     134                 :             : 
     135                 :           0 :   stream = g_hash_table_lookup (self->streams, GUINT_TO_POINTER (stream_id));
     136                 :             : 
     137                 :           0 :   if (stream == NULL)
     138                 :             :     return;
     139                 :             : 
     140                 :           0 :   g_object_notify (G_OBJECT (stream), "level");
     141                 :             : }
     142                 :             : 
     143                 :             : static void
     144                 :           0 : valent_pa_mixer_load (ValentPaMixer *self)
     145                 :             : {
     146                 :           0 :   g_autoptr (GSList) sources = NULL;
     147                 :           0 :   g_autoptr (GSList) sinks = NULL;
     148                 :           0 :   GvcMixerStream *stream = NULL;
     149                 :             : 
     150                 :           0 :   g_assert (VALENT_IS_PA_MIXER (self));
     151                 :             : 
     152                 :             :   /* Query the default streams before emitting signals */
     153                 :           0 :   if ((stream = gvc_mixer_control_get_default_sink (self->control)) != NULL)
     154                 :           0 :     self->output = gvc_mixer_stream_get_id (stream);
     155                 :             : 
     156                 :           0 :   if ((stream = gvc_mixer_control_get_default_source (self->control)) != NULL)
     157                 :           0 :     self->input = gvc_mixer_stream_get_id (stream);
     158                 :             : 
     159                 :             :   /* Get current streams */
     160                 :           0 :   sinks = gvc_mixer_control_get_sinks (self->control);
     161                 :             : 
     162                 :           0 :   for (const GSList *iter = sinks; iter; iter = iter->next)
     163                 :           0 :     on_stream_added (self->control, gvc_mixer_stream_get_id (iter->data), self);
     164                 :             : 
     165                 :           0 :   sources = gvc_mixer_control_get_sources (self->control);
     166                 :             : 
     167                 :           0 :   for (const GSList *iter = sources; iter; iter = iter->next)
     168                 :           0 :     on_stream_added (self->control, gvc_mixer_stream_get_id (iter->data), self);
     169                 :             : 
     170                 :             :   /* Watch stream changes */
     171                 :           0 :   g_object_connect (self->control,
     172                 :             :                     "signal::default-sink-changed",   on_default_sink_changed,   self,
     173                 :             :                     "signal::default-source-changed", on_default_source_changed, self,
     174                 :             :                     "signal::stream-added",           on_stream_added,           self,
     175                 :             :                     "signal::stream-removed",         on_stream_removed,         self,
     176                 :             :                     "signal::stream-changed",         on_stream_changed,         self,
     177                 :             :                     NULL);
     178                 :           0 : }
     179                 :             : 
     180                 :             : static void
     181                 :           0 : valent_pa_mixer_unload (ValentPaMixer *self)
     182                 :             : {
     183                 :           0 :   ValentMixerAdapter *adapter = VALENT_MIXER_ADAPTER (self);
     184                 :           0 :   GHashTableIter iter;
     185                 :           0 :   ValentMixerStream *stream;
     186                 :             : 
     187                 :           0 :   g_assert (VALENT_IS_PA_MIXER (self));
     188                 :             : 
     189                 :             :   /* Clear the current defaults */
     190                 :           0 :   self->input = 0;
     191                 :           0 :   g_object_notify (G_OBJECT (self), "default-input");
     192                 :           0 :   self->output = 0;
     193                 :           0 :   g_object_notify (G_OBJECT (self), "default-output");
     194                 :             : 
     195                 :           0 :   g_hash_table_iter_init (&iter, self->streams);
     196                 :             : 
     197                 :           0 :   while (g_hash_table_iter_next (&iter, NULL, (void **)&stream))
     198                 :             :     {
     199                 :           0 :       valent_mixer_adapter_stream_removed (adapter, stream);
     200                 :           0 :       g_hash_table_iter_remove (&iter);
     201                 :             :     }
     202                 :             : 
     203                 :           0 :   g_signal_handlers_disconnect_by_func (self->control, on_default_sink_changed, self);
     204                 :           0 :   g_signal_handlers_disconnect_by_func (self->control, on_default_source_changed, self);
     205                 :           0 :   g_signal_handlers_disconnect_by_func (self->control, on_stream_added, self);
     206                 :           0 :   g_signal_handlers_disconnect_by_func (self->control, on_stream_removed, self);
     207                 :           0 :   g_signal_handlers_disconnect_by_func (self->control, on_stream_changed, self);
     208                 :           0 : }
     209                 :             : 
     210                 :             : static void
     211                 :           0 : on_state_changed (GvcMixerControl      *control,
     212                 :             :                   GvcMixerControlState  state,
     213                 :             :                   ValentPaMixer        *self)
     214                 :             : {
     215                 :           0 :   g_autoptr (GError) error = NULL;
     216                 :             : 
     217                 :           0 :   g_assert (VALENT_IS_PA_MIXER (self));
     218                 :             : 
     219                 :           0 :   switch (state)
     220                 :             :     {
     221                 :           0 :     case GVC_STATE_CLOSED:
     222                 :           0 :       valent_extension_plugin_state_changed (VALENT_EXTENSION (self),
     223                 :             :                                              VALENT_PLUGIN_STATE_INACTIVE,
     224                 :             :                                              error);
     225                 :           0 :       valent_pa_mixer_unload (self);
     226                 :           0 :       break;
     227                 :             : 
     228                 :           0 :     case GVC_STATE_READY:
     229                 :           0 :       valent_extension_plugin_state_changed (VALENT_EXTENSION (self),
     230                 :             :                                              VALENT_PLUGIN_STATE_ACTIVE,
     231                 :             :                                              error);
     232                 :           0 :       valent_pa_mixer_load (self);
     233                 :           0 :       break;
     234                 :             : 
     235                 :           0 :     case GVC_STATE_CONNECTING:
     236                 :           0 :       valent_extension_plugin_state_changed (VALENT_EXTENSION (self),
     237                 :             :                                              VALENT_PLUGIN_STATE_INACTIVE,
     238                 :             :                                              error);
     239                 :           0 :       break;
     240                 :             : 
     241                 :           0 :     case GVC_STATE_FAILED:
     242                 :           0 :       g_set_error_literal (&error,
     243                 :             :                            G_IO_ERROR,
     244                 :             :                            G_IO_ERROR_FAILED,
     245                 :             :                            "failed to connect to PulseAudio server");
     246                 :           0 :       valent_extension_plugin_state_changed (VALENT_EXTENSION (self),
     247                 :             :                                              VALENT_PLUGIN_STATE_ERROR,
     248                 :             :                                              error);
     249                 :           0 :       valent_pa_mixer_unload (self);
     250                 :           0 :       break;
     251                 :             :     }
     252                 :           0 : }
     253                 :             : 
     254                 :             : /*
     255                 :             :  * ValentMixerAdapter
     256                 :             :  */
     257                 :             : static ValentMixerStream *
     258                 :           0 : valent_pa_mixer_get_default_input (ValentMixerAdapter *adapter)
     259                 :             : {
     260                 :           0 :   ValentPaMixer *self = VALENT_PA_MIXER (adapter);
     261                 :             : 
     262                 :           0 :   return g_hash_table_lookup (self->streams, GUINT_TO_POINTER (self->input));
     263                 :             : }
     264                 :             : 
     265                 :             : static void
     266                 :           0 : valent_pa_mixer_set_default_input (ValentMixerAdapter *adapter,
     267                 :             :                                    ValentMixerStream  *stream)
     268                 :             : {
     269                 :           0 :   ValentPaMixer *self = VALENT_PA_MIXER (adapter);
     270                 :           0 :   GvcMixerStream *base_stream = NULL;
     271                 :             : 
     272                 :           0 :   g_object_get (stream, "base-stream", &base_stream, NULL);
     273                 :           0 :   gvc_mixer_control_set_default_source (self->control, base_stream);
     274                 :           0 :   g_clear_object (&base_stream);
     275                 :           0 : }
     276                 :             : 
     277                 :             : static ValentMixerStream *
     278                 :           0 : valent_pa_mixer_get_default_output (ValentMixerAdapter *adapter)
     279                 :             : {
     280                 :           0 :   ValentPaMixer *self = VALENT_PA_MIXER (adapter);
     281                 :             : 
     282                 :           0 :   return g_hash_table_lookup (self->streams, GUINT_TO_POINTER (self->output));
     283                 :             : }
     284                 :             : 
     285                 :             : static void
     286                 :           0 : valent_pa_mixer_set_default_output (ValentMixerAdapter *adapter,
     287                 :             :                                     ValentMixerStream  *stream)
     288                 :             : {
     289                 :           0 :   ValentPaMixer *self = VALENT_PA_MIXER (adapter);
     290                 :           0 :   GvcMixerStream *base_stream = NULL;
     291                 :             : 
     292                 :           0 :   g_object_get (stream, "base-stream", &base_stream, NULL);
     293                 :           0 :   gvc_mixer_control_set_default_sink (self->control, base_stream);
     294                 :           0 :   g_clear_object (&base_stream);
     295                 :           0 : }
     296                 :             : 
     297                 :             : /*
     298                 :             :  * ValentObject
     299                 :             :  */
     300                 :             : static void
     301                 :           0 : valent_pa_mixer_destroy (ValentObject *object)
     302                 :             : {
     303                 :           0 :   ValentPaMixer *self = VALENT_PA_MIXER (object);
     304                 :             : 
     305                 :           0 :   g_signal_handlers_disconnect_by_data (self->control, self);
     306                 :           0 :   gvc_mixer_control_close (self->control);
     307                 :           0 :   g_hash_table_remove_all (self->streams);
     308                 :             : 
     309                 :           0 :   VALENT_OBJECT_CLASS (valent_pa_mixer_parent_class)->destroy (object);
     310                 :           0 : }
     311                 :             : 
     312                 :             : /*
     313                 :             :  * GObject
     314                 :             :  */
     315                 :             : static void
     316                 :           0 : valent_pa_mixer_constructed (GObject *object)
     317                 :             : {
     318                 :           0 :   ValentPaMixer *self = VALENT_PA_MIXER (object);
     319                 :             : 
     320                 :           0 :   g_signal_connect_object (self->control,
     321                 :             :                            "state-changed",
     322                 :             :                            G_CALLBACK (on_state_changed),
     323                 :             :                            self,
     324                 :             :                            G_CONNECT_DEFAULT);
     325                 :           0 :   gvc_mixer_control_open (self->control);
     326                 :             : 
     327                 :           0 :   G_OBJECT_CLASS (valent_pa_mixer_parent_class)->constructed (object);
     328                 :           0 : }
     329                 :             : 
     330                 :             : static void
     331                 :           0 : valent_pa_mixer_finalize (GObject *object)
     332                 :             : {
     333                 :           0 :   ValentPaMixer *self = VALENT_PA_MIXER (object);
     334                 :             : 
     335                 :           0 :   g_clear_pointer (&self->streams, g_hash_table_unref);
     336                 :           0 :   g_clear_object (&self->control);
     337                 :             : 
     338                 :           0 :   G_OBJECT_CLASS (valent_pa_mixer_parent_class)->finalize (object);
     339                 :           0 : }
     340                 :             : 
     341                 :             : static void
     342                 :           0 : valent_pa_mixer_class_init (ValentPaMixerClass *klass)
     343                 :             : {
     344                 :           0 :   GObjectClass *object_class = G_OBJECT_CLASS (klass);
     345                 :           0 :   ValentObjectClass *vobject_class = VALENT_OBJECT_CLASS (klass);
     346                 :           0 :   ValentMixerAdapterClass *adapter_class = VALENT_MIXER_ADAPTER_CLASS (klass);
     347                 :             : 
     348                 :           0 :   object_class->constructed = valent_pa_mixer_constructed;
     349                 :           0 :   object_class->finalize = valent_pa_mixer_finalize;
     350                 :             : 
     351                 :           0 :   vobject_class->destroy = valent_pa_mixer_destroy;
     352                 :             : 
     353                 :           0 :   adapter_class->get_default_input = valent_pa_mixer_get_default_input;
     354                 :           0 :   adapter_class->set_default_input = valent_pa_mixer_set_default_input;
     355                 :           0 :   adapter_class->get_default_output = valent_pa_mixer_get_default_output;
     356                 :           0 :   adapter_class->set_default_output = valent_pa_mixer_set_default_output;
     357                 :             : }
     358                 :             : 
     359                 :             : static void
     360                 :           0 : valent_pa_mixer_init (ValentPaMixer *self)
     361                 :             : {
     362                 :           0 :   self->control = g_object_new (GVC_TYPE_MIXER_CONTROL,
     363                 :             :                                 "name", "Valent",
     364                 :             :                                 NULL);
     365                 :           0 :   self->streams = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref);
     366                 :           0 : }
     367                 :             : 
        

Generated by: LCOV version 2.0-1