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

Generated by: LCOV version 2.0-1