LCOV - code coverage report
Current view: top level - src/plugins/mpris - valent-mpris-impl.c (source / functions) Coverage Total Hit
Test: Code Coverage Lines: 87.8 % 368 323
Test Date: 2025-10-31 23:35:41 Functions: 96.0 % 25 24
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 62.1 % 264 164

             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-mpris-impl"
       5                 :             : 
       6                 :             : #include "config.h"
       7                 :             : 
       8                 :             : #include <gio/gio.h>
       9                 :             : #include <valent.h>
      10                 :             : 
      11                 :             : #include "valent-mpris-impl.h"
      12                 :             : #include "valent-mpris-utils.h"
      13                 :             : 
      14                 :             : 
      15                 :             : struct _ValentMPRISImpl
      16                 :             : {
      17                 :             :   GObject               parent_instance;
      18                 :             : 
      19                 :             :   ValentMediaPlayer    *player;
      20                 :             :   GDBusConnection      *connection;
      21                 :             :   char                 *bus_name;
      22                 :             :   unsigned int          bus_name_id;
      23                 :             : 
      24                 :             :   GHashTable           *cache;
      25                 :             :   GHashTable           *pending;
      26                 :             :   unsigned int          flush_id;
      27                 :             : 
      28                 :             :   /* org.mpris.MediaPlayer2 */
      29                 :             :   unsigned int          application_id;
      30                 :             :   GDBusInterfaceVTable  application_vtable;
      31                 :             : 
      32                 :             :   /* org.mpris.MediaPlayer2.Player */
      33                 :             :   unsigned int          player_id;
      34                 :             :   GDBusInterfaceVTable  player_vtable;
      35                 :             : };
      36                 :             : 
      37   [ +  +  +  - ]:         370 : G_DEFINE_FINAL_TYPE (ValentMPRISImpl, valent_mpris_impl, G_TYPE_OBJECT)
      38                 :             : 
      39                 :             : 
      40                 :             : typedef enum {
      41                 :             :   PROP_PLAYER = 1,
      42                 :             : } ValentMPRISImplProperty;
      43                 :             : 
      44                 :             : static GParamSpec *properties[PROP_PLAYER + 1] = { NULL, };
      45                 :             : 
      46                 :             : 
      47                 :             : /*
      48                 :             :  * org.mpris.MediaPlayer2
      49                 :             :  */
      50                 :             : static void
      51                 :           0 : application_method_call (GDBusConnection       *connection,
      52                 :             :                          const char            *sender,
      53                 :             :                          const char            *object_path,
      54                 :             :                          const char            *interface_name,
      55                 :             :                          const char            *method_name,
      56                 :             :                          GVariant              *parameters,
      57                 :             :                          GDBusMethodInvocation *invocation,
      58                 :             :                          gpointer               user_data)
      59                 :             : {
      60         [ #  # ]:           0 :   g_assert (VALENT_IS_MPRIS_IMPL (user_data));
      61         [ #  # ]:           0 :   g_assert (method_name != NULL);
      62                 :             : 
      63         [ #  # ]:           0 :   if (g_str_equal (method_name, "Raise"))
      64                 :             :     {
      65                 :           0 :       GApplication *application = g_application_get_default ();
      66                 :             : 
      67         [ #  # ]:           0 :       if (application != NULL)
      68                 :             :         {
      69                 :           0 :           g_action_group_activate_action (G_ACTION_GROUP (application),
      70                 :             :                                           "media-remote",
      71                 :             :                                           NULL);
      72                 :             :         }
      73                 :             :     }
      74                 :             : 
      75                 :             :   /* Silently ignore method calls */
      76                 :           0 :   g_dbus_method_invocation_return_value (invocation, NULL);
      77                 :           0 : }
      78                 :             : 
      79                 :             : static GVariant *
      80                 :          45 : application_get_property (GDBusConnection  *connection,
      81                 :             :                           const char       *sender,
      82                 :             :                           const char       *object_path,
      83                 :             :                           const char       *interface_name,
      84                 :             :                           const char       *property_name,
      85                 :             :                           GError          **error,
      86                 :             :                           gpointer          user_data)
      87                 :             : {
      88                 :          45 :   ValentMPRISImpl *self = VALENT_MPRIS_IMPL (user_data);
      89                 :          45 :   GVariant *value = NULL;
      90                 :             : 
      91         [ -  + ]:          45 :   g_assert (VALENT_IS_MPRIS_IMPL (self));
      92         [ +  - ]:          45 :   g_assert (property_name != NULL);
      93                 :             : 
      94         [ -  + ]:          45 :   if ((value = g_hash_table_lookup (self->cache, property_name)) != NULL)
      95                 :           0 :     return g_variant_ref_sink (value);
      96                 :             : 
      97         [ +  + ]:          45 :   if (g_str_equal (property_name, "Identity"))
      98                 :           5 :     value = g_variant_new_string (valent_media_player_get_name (self->player));
      99         [ +  + ]:          40 :   else if (g_str_equal (property_name, "CanQuit"))
     100                 :           5 :     value = g_variant_new_boolean (FALSE);
     101         [ +  + ]:          35 :   else if (g_str_equal (property_name, "CanRaise"))
     102                 :           5 :     value = g_variant_new_boolean (TRUE);
     103         [ +  + ]:          30 :   else if (g_str_equal (property_name, "CanSetFullscreen"))
     104                 :           5 :     value = g_variant_new_boolean (FALSE);
     105         [ +  + ]:          25 :   else if (g_str_equal (property_name, "DesktopEntry"))
     106                 :           5 :     value = g_variant_new_string (APPLICATION_ID".desktop");
     107         [ +  + ]:          20 :   else if (g_str_equal (property_name, "Fullscreen") ||
     108         [ +  + ]:          15 :            g_str_equal (property_name, "HasTrackList"))
     109                 :          10 :     value = g_variant_new_boolean (FALSE);
     110         [ +  + ]:          10 :   else if (g_str_equal (property_name, "SupportedMimeTypes") ||
     111         [ +  - ]:           5 :            g_str_equal (property_name, "SupportedUriSchemes"))
     112                 :          10 :     value = g_variant_new_strv (NULL, 0);
     113                 :             : 
     114         [ +  - ]:          45 :   if (value != NULL)
     115                 :             :     {
     116                 :          90 :       g_hash_table_replace (self->cache,
     117                 :          45 :                             g_strdup (property_name),
     118         [ -  + ]:          45 :                             g_variant_ref_sink (value));
     119                 :             : 
     120                 :          45 :       return g_variant_ref_sink (value);
     121                 :             :     }
     122                 :             : 
     123                 :           0 :   g_set_error (error,
     124                 :             :                G_DBUS_ERROR,
     125                 :             :                G_DBUS_ERROR_UNKNOWN_PROPERTY,
     126                 :             :                "Unknown property \"%s\"", property_name);
     127                 :             : 
     128                 :           0 :   return NULL;
     129                 :             : }
     130                 :             : 
     131                 :             : /* LCOV_EXCL_START */
     132                 :             : static gboolean
     133                 :             : application_set_property (GDBusConnection  *connection,
     134                 :             :                           const char       *sender,
     135                 :             :                           const char       *object_path,
     136                 :             :                           const char       *interface_name,
     137                 :             :                           const char       *property_name,
     138                 :             :                           GVariant         *value,
     139                 :             :                           GError          **error,
     140                 :             :                           gpointer          user_data)
     141                 :             : {
     142                 :             :   /* Silently ignore property setters */
     143                 :             :   return TRUE;
     144                 :             : }
     145                 :             : /* LCOV_EXCL_STOP */
     146                 :             : 
     147                 :             : /*
     148                 :             :  * org.mpris.MediaPlayer2.Player
     149                 :             :  */
     150                 :             : static void
     151                 :          20 : player_method_call (GDBusConnection       *connection,
     152                 :             :                     const char            *sender,
     153                 :             :                     const char            *object_path,
     154                 :             :                     const char            *interface_name,
     155                 :             :                     const char            *method_name,
     156                 :             :                     GVariant              *parameters,
     157                 :             :                     GDBusMethodInvocation *invocation,
     158                 :             :                     gpointer               user_data)
     159                 :             : {
     160                 :          20 :   ValentMPRISImpl *self = VALENT_MPRIS_IMPL (user_data);
     161                 :             : 
     162         [ -  + ]:          20 :   g_assert (VALENT_IS_MPRIS_IMPL (self));
     163         [ +  - ]:          20 :   g_assert (method_name != NULL);
     164                 :             : 
     165         [ +  + ]:          20 :   if (g_str_equal (method_name, "Next"))
     166                 :             :     {
     167                 :           3 :       valent_media_player_next (self->player);
     168                 :             :     }
     169         [ +  + ]:          17 :   else if (g_str_equal (method_name, "Pause"))
     170                 :             :     {
     171                 :           3 :       valent_media_player_pause (self->player);
     172                 :             :     }
     173         [ +  + ]:          14 :   else if (g_str_equal (method_name, "Play"))
     174                 :             :     {
     175                 :           3 :       valent_media_player_play (self->player);
     176                 :             :     }
     177         [ -  + ]:          11 :   else if (g_str_equal (method_name, "PlayPause"))
     178                 :             :     {
     179   [ #  #  #  #  :           0 :       valent_mpris_play_pause (self->player);
                   #  # ]
     180                 :             :     }
     181         [ +  + ]:          11 :   else if (g_str_equal (method_name, "Previous"))
     182                 :             :     {
     183                 :           3 :       valent_media_player_previous (self->player);
     184                 :             :     }
     185         [ +  + ]:           8 :   else if (g_str_equal (method_name, "Seek"))
     186                 :             :     {
     187                 :           3 :       int64_t offset_us;
     188                 :             : 
     189                 :             :       /* Convert microseconds to seconds */
     190                 :           3 :       g_variant_get (parameters, "(x)", &offset_us);
     191                 :           3 :       valent_media_player_seek (self->player, offset_us / G_TIME_SPAN_SECOND);
     192                 :             :     }
     193         [ +  + ]:           5 :   else if (g_str_equal (method_name, "SetPosition"))
     194                 :             :     {
     195                 :           2 :       int64_t position_us;
     196                 :             : 
     197                 :             :       /* Convert microseconds to seconds */
     198                 :           2 :       g_variant_get (parameters, "(&ox)", NULL, &position_us);
     199                 :           2 :       valent_media_player_set_position (self->player, position_us / G_TIME_SPAN_SECOND);
     200                 :             :     }
     201         [ +  - ]:           3 :   else if (g_str_equal (method_name, "Stop"))
     202                 :             :     {
     203                 :           3 :       valent_media_player_stop (self->player);
     204                 :             :     }
     205         [ #  # ]:           0 :   else if (g_str_equal (method_name, "OpenUri"))
     206                 :             :     {
     207                 :             :       /* Silently ignore method calls */
     208                 :             :     }
     209                 :             :   else
     210                 :             :     {
     211                 :           0 :       g_dbus_method_invocation_return_error (invocation,
     212                 :             :                                              G_DBUS_ERROR,
     213                 :             :                                              G_DBUS_ERROR_UNKNOWN_METHOD,
     214                 :             :                                              "Unknown method \"%s\"",
     215                 :             :                                              method_name);
     216                 :           0 :       return;
     217                 :             :     }
     218                 :             : 
     219                 :          20 :   g_dbus_method_invocation_return_value (invocation, NULL);
     220                 :             : }
     221                 :             : 
     222                 :             : static GVariant *
     223                 :          79 : player_get_property (GDBusConnection  *connection,
     224                 :             :                      const char       *sender,
     225                 :             :                      const char       *object_path,
     226                 :             :                      const char       *interface_name,
     227                 :             :                      const char       *property_name,
     228                 :             :                      GError          **error,
     229                 :             :                      gpointer          user_data)
     230                 :             : {
     231                 :          79 :   ValentMPRISImpl *self = VALENT_MPRIS_IMPL (user_data);
     232                 :          79 :   ValentMediaActions flags = VALENT_MEDIA_ACTION_NONE;
     233                 :          79 :   GVariant *value;
     234                 :             : 
     235         [ -  + ]:          79 :   g_assert (VALENT_IS_MPRIS_IMPL (self));
     236         [ +  - ]:          79 :   g_assert (property_name != NULL);
     237                 :             : 
     238                 :             :   /* Check cache */
     239         [ -  + ]:          79 :   if ((value = g_hash_table_lookup (self->cache, property_name)) != NULL)
     240                 :           0 :     return g_variant_ref (value);
     241                 :             : 
     242                 :             :   /* The `Position` is not cached, because `PropertiesChanged` is not emitted */
     243         [ +  + ]:          79 :   if (g_str_equal (property_name, "Position"))
     244                 :             :     {
     245                 :           9 :       double position = valent_media_player_get_position (self->player);
     246                 :             : 
     247                 :             :       /* Convert seconds to microseconds */
     248                 :           9 :       return g_variant_new_int64 ((int64_t)(position * G_TIME_SPAN_SECOND));
     249                 :             :     }
     250                 :             : 
     251                 :             :   /* Load properties */
     252         [ +  + ]:          70 :   if (*property_name == 'C')
     253                 :          30 :     flags = valent_media_player_get_flags (self->player);
     254                 :             : 
     255         [ +  + ]:          70 :   if (g_str_equal (property_name, "CanControl"))
     256                 :             :     {
     257                 :           5 :       value = g_variant_new_boolean (flags != 0);
     258                 :             :     }
     259         [ +  + ]:          65 :   else if (g_str_equal (property_name, "CanGoNext"))
     260                 :             :     {
     261                 :           5 :       value = g_variant_new_boolean ((flags & VALENT_MEDIA_ACTION_NEXT) != 0);
     262                 :             :     }
     263         [ +  + ]:          60 :   else if (g_str_equal (property_name, "CanGoPrevious"))
     264                 :             :     {
     265                 :           5 :       value = g_variant_new_boolean ((flags & VALENT_MEDIA_ACTION_PREVIOUS) != 0);
     266                 :             :     }
     267         [ +  + ]:          55 :   else if (g_str_equal (property_name, "CanPlay"))
     268                 :             :     {
     269                 :           5 :       value = g_variant_new_boolean ((flags & VALENT_MEDIA_ACTION_PLAY) != 0);
     270                 :             :     }
     271         [ +  + ]:          50 :   else if (g_str_equal (property_name, "CanPause"))
     272                 :             :     {
     273                 :           5 :       value = g_variant_new_boolean ((flags & VALENT_MEDIA_ACTION_PAUSE) != 0);
     274                 :             :     }
     275         [ +  + ]:          45 :   else if (g_str_equal (property_name, "CanSeek"))
     276                 :             :     {
     277                 :           5 :       value = g_variant_new_boolean ((flags & VALENT_MEDIA_ACTION_SEEK) != 0);
     278                 :             :     }
     279         [ +  + ]:          40 :   else if (g_str_equal (property_name, "Metadata"))
     280                 :             :     {
     281                 :           5 :       value = valent_media_player_get_metadata (self->player);
     282                 :             :     }
     283         [ +  + ]:          35 :   else if (g_str_equal (property_name, "LoopStatus"))
     284                 :             :     {
     285                 :           5 :       ValentMediaRepeat repeat = valent_media_player_get_repeat (self->player);
     286                 :             : 
     287                 :           5 :       value = g_variant_new_string (valent_mpris_repeat_to_string (repeat));
     288                 :             :     }
     289         [ +  + ]:          30 :   else if (g_str_equal (property_name, "Shuffle"))
     290                 :             :     {
     291                 :           5 :       gboolean shuffle = valent_media_player_get_shuffle (self->player);
     292                 :             : 
     293                 :           5 :       value = g_variant_new_boolean (shuffle);
     294                 :             :     }
     295         [ +  + ]:          25 :   else if (g_str_equal (property_name, "PlaybackStatus"))
     296                 :             :     {
     297                 :           5 :       ValentMediaState state = valent_media_player_get_state (self->player);
     298                 :             : 
     299                 :           5 :       value = g_variant_new_string (valent_mpris_state_to_string (state));
     300                 :             :     }
     301         [ +  + ]:          20 :   else if (g_str_equal (property_name, "Volume"))
     302                 :             :     {
     303                 :           5 :       double volume = valent_media_player_get_volume (self->player);
     304                 :             : 
     305                 :           5 :       value = g_variant_new_double (volume);
     306                 :             :     }
     307         [ +  + ]:          15 :   else if (g_str_equal (property_name, "Rate") ||
     308         [ +  + ]:          10 :            g_str_equal (property_name, "MaximumRate") ||
     309         [ +  - ]:           5 :            g_str_equal (property_name, "MinimumRate"))
     310                 :             :     {
     311                 :          15 :       value = g_variant_new_double (1.0);
     312                 :             :     }
     313                 :             : 
     314         [ +  - ]:          70 :   if (value != NULL)
     315                 :             :     {
     316                 :         140 :       g_hash_table_replace (self->cache,
     317                 :          70 :                             g_strdup (property_name),
     318         [ -  + ]:          70 :                             g_variant_take_ref (value));
     319                 :             : 
     320                 :          70 :       return g_variant_ref (value);
     321                 :             :     }
     322                 :             : 
     323                 :           0 :   g_set_error (error,
     324                 :             :                G_DBUS_ERROR,
     325                 :             :                G_DBUS_ERROR_UNKNOWN_PROPERTY,
     326                 :             :                "Unknown property \"%s\"",
     327                 :             :                property_name);
     328                 :             : 
     329                 :           0 :   return NULL;
     330                 :             : }
     331                 :             : 
     332                 :             : static gboolean
     333                 :           9 : player_set_property (GDBusConnection  *connection,
     334                 :             :                      const char       *sender,
     335                 :             :                      const char       *object_path,
     336                 :             :                      const char       *interface_name,
     337                 :             :                      const char       *property_name,
     338                 :             :                      GVariant         *value,
     339                 :             :                      GError          **error,
     340                 :             :                      gpointer          user_data)
     341                 :             : {
     342                 :           9 :   ValentMPRISImpl *self = VALENT_MPRIS_IMPL (user_data);
     343                 :             : 
     344         [ -  + ]:           9 :   g_assert (VALENT_IS_MPRIS_IMPL (self));
     345         [ +  - ]:           9 :   g_assert (property_name != NULL);
     346                 :             : 
     347         [ +  + ]:           9 :   if (g_str_equal (property_name, "LoopStatus"))
     348                 :             :     {
     349                 :           3 :       const char *loop_status = g_variant_get_string (value, NULL);
     350                 :           3 :       ValentMediaRepeat repeat = valent_mpris_repeat_from_string (loop_status);
     351                 :             : 
     352                 :           3 :       valent_media_player_set_repeat (self->player, repeat);
     353                 :           3 :       return TRUE;
     354                 :             :     }
     355                 :             : 
     356         [ +  + ]:           6 :   if (g_str_equal (property_name, "Shuffle"))
     357                 :             :     {
     358                 :           3 :       gboolean shuffle = g_variant_get_boolean (value);
     359                 :             : 
     360                 :           3 :       valent_media_player_set_shuffle (self->player, shuffle);
     361                 :           3 :       return TRUE;
     362                 :             :     }
     363                 :             : 
     364         [ +  - ]:           3 :   if (g_str_equal (property_name, "Volume"))
     365                 :             :     {
     366                 :           3 :       double volume = g_variant_get_double (value);
     367                 :             : 
     368                 :           3 :       valent_media_player_set_volume (self->player, volume);
     369                 :           3 :       return TRUE;
     370                 :             :     }
     371                 :             : 
     372                 :             :   return TRUE;
     373                 :             : }
     374                 :             : 
     375                 :             : static gboolean
     376                 :          28 : valent_mpris_impl_flush (gpointer data)
     377                 :             : {
     378                 :          28 :   ValentMPRISImpl *self = VALENT_MPRIS_IMPL (data);
     379                 :          56 :   g_autoptr (GError) error = NULL;
     380                 :          28 :   GVariant *parameters;
     381                 :          28 :   GVariantBuilder changed_props;
     382                 :          28 :   GVariantBuilder invalidated_props;
     383                 :          28 :   GHashTableIter iter;
     384                 :          28 :   GVariant *value;
     385                 :          28 :   char *name;
     386                 :             : 
     387         [ -  + ]:          28 :   g_assert (VALENT_IS_MPRIS_IMPL (self));
     388                 :             : 
     389         [ +  - ]:          28 :   if (self->connection != NULL)
     390                 :             :     {
     391                 :          28 :       g_variant_builder_init (&changed_props, G_VARIANT_TYPE_VARDICT);
     392                 :          28 :       g_variant_builder_init (&invalidated_props, G_VARIANT_TYPE_STRING_ARRAY);
     393                 :             : 
     394                 :          28 :       g_hash_table_iter_init (&iter, self->pending);
     395                 :             : 
     396         [ +  + ]:         169 :       while (g_hash_table_iter_next (&iter, (void**)&name, (void**)&value))
     397                 :             :         {
     398         [ +  - ]:         141 :           if (value)
     399                 :         141 :             g_variant_builder_add (&changed_props, "{sv}", name, value);
     400                 :             :           else
     401                 :           0 :             g_variant_builder_add (&invalidated_props, "s", name);
     402                 :             : 
     403                 :         141 :           g_hash_table_iter_remove (&iter);
     404                 :             :         }
     405                 :             : 
     406                 :          28 :       parameters = g_variant_new ("(s@a{sv}@as)",
     407                 :             :                                   "org.mpris.MediaPlayer2.Player",
     408                 :             :                                   g_variant_builder_end (&changed_props),
     409                 :             :                                   g_variant_builder_end (&invalidated_props));
     410                 :             : 
     411                 :          28 :       g_dbus_connection_emit_signal (self->connection,
     412                 :             :                                      NULL,
     413                 :             :                                      "/org/mpris/MediaPlayer2",
     414                 :             :                                      "org.freedesktop.DBus.Properties",
     415                 :             :                                      "PropertiesChanged",
     416                 :             :                                      parameters,
     417                 :             :                                      &error);
     418                 :             : 
     419         [ -  + ]:          28 :       if (error != NULL)
     420                 :           0 :         g_warning ("%s(): %s", G_STRFUNC, error->message);
     421                 :             :     }
     422                 :             : 
     423         [ +  - ]:          28 :   g_clear_handle_id (&self->flush_id, g_source_remove);
     424                 :             : 
     425         [ -  + ]:          28 :   return G_SOURCE_REMOVE;
     426                 :             : }
     427                 :             : 
     428                 :             : static void
     429                 :         150 : valent_mpris_impl_set_value (ValentMPRISImpl *self,
     430                 :             :                              const char      *name,
     431                 :             :                              GVariant        *value)
     432                 :             : {
     433         [ -  + ]:         150 :   g_assert (VALENT_IS_MPRIS_IMPL (self));
     434   [ +  -  +  - ]:         150 :   g_assert (name != NULL && *name != '\0');
     435         [ +  - ]:         150 :   g_assert (value != NULL);
     436                 :             : 
     437                 :         300 :   g_hash_table_replace (self->cache,
     438                 :         150 :                         g_strdup (name),
     439         [ -  + ]:         150 :                         g_variant_ref_sink (value));
     440                 :         300 :   g_hash_table_replace (self->pending,
     441                 :         150 :                         g_strdup (name),
     442         [ -  + ]:         150 :                         g_variant_ref_sink (value));
     443                 :             : 
     444         [ +  + ]:         150 :   if (self->flush_id == 0)
     445                 :          28 :     self->flush_id = g_idle_add (valent_mpris_impl_flush, self);
     446                 :         150 : }
     447                 :             : 
     448                 :             : static void
     449                 :           7 : valent_mpris_impl_propagate_seeked (ValentMPRISImpl *self,
     450                 :             :                                     int64_t          position)
     451                 :             : {
     452                 :           7 :   g_autoptr (GError) error = NULL;
     453                 :           7 :   gboolean ret;
     454                 :             : 
     455         [ -  + ]:           7 :   if (self->connection == NULL)
     456                 :           0 :     return;
     457                 :             : 
     458                 :           7 :   ret = g_dbus_connection_emit_signal (self->connection,
     459                 :             :                                        NULL,
     460                 :             :                                        "/org/mpris/MediaPlayer2",
     461                 :             :                                        "org.mpris.MediaPlayer2.Player",
     462                 :             :                                        "Seeked",
     463                 :             :                                        g_variant_new ("(x)", position),
     464                 :             :                                        &error);
     465                 :             : 
     466         [ -  + ]:           7 :   if (!ret)
     467                 :           0 :     g_warning ("%s(): %s", G_STRFUNC, error->message);
     468                 :             : }
     469                 :             : 
     470                 :             : static void
     471                 :          77 : valent_mpris_impl_propagate_notify (ValentMediaPlayer *player,
     472                 :             :                                     GParamSpec        *pspec,
     473                 :             :                                     ValentMPRISImpl   *self)
     474                 :             : {
     475                 :          77 :   const char *name = g_param_spec_get_name (pspec);
     476                 :          77 :   GVariant *value = NULL;
     477                 :             : 
     478         [ +  + ]:          77 :   if (g_str_equal (name, "flags"))
     479                 :             :     {
     480                 :          16 :       ValentMediaActions flags = valent_media_player_get_flags (self->player);
     481                 :             : 
     482                 :          16 :       value = g_variant_new_boolean (flags != 0);
     483                 :          16 :       valent_mpris_impl_set_value (self, "CanControl", value);
     484                 :          16 :       value = g_variant_new_boolean ((flags & VALENT_MEDIA_ACTION_NEXT) != 0);
     485                 :          16 :       valent_mpris_impl_set_value (self, "CanGoNext", value);
     486                 :          16 :       value = g_variant_new_boolean ((flags & VALENT_MEDIA_ACTION_PAUSE) != 0);
     487                 :          16 :       valent_mpris_impl_set_value (self, "CanPause", value);
     488                 :          16 :       value = g_variant_new_boolean ((flags & VALENT_MEDIA_ACTION_PLAY) != 0);
     489                 :          16 :       valent_mpris_impl_set_value (self, "CanPlay", value);
     490                 :          16 :       value = g_variant_new_boolean ((flags & VALENT_MEDIA_ACTION_PREVIOUS) != 0);
     491                 :          16 :       valent_mpris_impl_set_value (self, "CanGoPrevious", value);
     492                 :          16 :       value = g_variant_new_boolean ((flags & VALENT_MEDIA_ACTION_SEEK) != 0);
     493                 :          16 :       valent_mpris_impl_set_value (self, "CanSeek", value);
     494                 :             :     }
     495         [ +  + ]:          61 :   else if (g_str_equal (name, "metadata"))
     496                 :             :     {
     497                 :          19 :       value = valent_media_player_get_metadata (self->player);
     498                 :          19 :       valent_mpris_impl_set_value (self, "Metadata", value);
     499                 :          19 :       g_variant_unref (value);
     500                 :             :     }
     501         [ -  + ]:          42 :   else if (g_str_equal (name, "name"))
     502                 :             :     {
     503                 :           0 :       const char *identity = valent_media_player_get_name (self->player);
     504                 :             : 
     505                 :           0 :       value = g_variant_new_string (identity);
     506                 :           0 :       g_hash_table_replace (self->cache,
     507                 :           0 :                             g_strdup ("Identity"),
     508                 :           0 :                             g_variant_ref_sink (value));
     509                 :             :     }
     510         [ +  + ]:          42 :   else if (g_str_equal (name, "position"))
     511                 :             :     {
     512                 :           7 :       double position = valent_media_player_get_position (self->player);
     513                 :             : 
     514                 :             :       /* Convert seconds to microseconds */
     515                 :           7 :       value = g_variant_new_int64 ((int64_t)(position * G_TIME_SPAN_SECOND));
     516                 :          14 :       g_hash_table_replace (self->cache,
     517                 :           7 :                             g_strdup ("Position"),
     518                 :           7 :                             g_variant_ref_sink (value));
     519                 :             : 
     520                 :             :       /* Convert seconds to microseconds */
     521                 :           7 :       valent_mpris_impl_propagate_seeked (self, (int64_t)(position * G_TIME_SPAN_SECOND));
     522                 :             :     }
     523         [ +  + ]:          35 :   else if (g_str_equal (name, "repeat"))
     524                 :             :     {
     525                 :           3 :       ValentMediaRepeat repeat = valent_media_player_get_repeat (self->player);
     526                 :             : 
     527                 :           3 :       value = g_variant_new_string (valent_mpris_repeat_to_string (repeat));
     528                 :           3 :       valent_mpris_impl_set_value (self, "LoopStatus", value);
     529                 :             :     }
     530         [ +  + ]:          32 :   else if (g_str_equal (name, "shuffle"))
     531                 :             :     {
     532                 :           3 :       gboolean shuffle = valent_media_player_get_shuffle (self->player);
     533                 :             : 
     534                 :           3 :       value = g_variant_new_boolean (shuffle);
     535                 :           3 :       valent_mpris_impl_set_value (self, "Shuffle", value);
     536                 :             :     }
     537         [ +  + ]:          29 :   else if (g_str_equal (name, "state"))
     538                 :             :     {
     539                 :          26 :       ValentMediaState state = valent_media_player_get_state (self->player);
     540                 :             : 
     541                 :          26 :       value = g_variant_new_string (valent_mpris_state_to_string (state));
     542                 :          26 :       valent_mpris_impl_set_value (self, "PlaybackStatus", value);
     543                 :             :     }
     544         [ +  - ]:           3 :   else if (g_str_equal (name, "volume"))
     545                 :             :     {
     546                 :           3 :       double volume = valent_media_player_get_volume (self->player);
     547                 :             : 
     548                 :           3 :       value = g_variant_new_double (volume);
     549                 :           3 :       valent_mpris_impl_set_value (self, "Volume", value);
     550                 :             :     }
     551                 :          77 : }
     552                 :             : 
     553                 :             : /*
     554                 :             :  * GObject
     555                 :             :  */
     556                 :             : static void
     557                 :           5 : valent_mpris_impl_constructed (GObject *object)
     558                 :             : {
     559                 :           5 :   ValentMPRISImpl *self = VALENT_MPRIS_IMPL (object);
     560                 :             : 
     561                 :           5 :   G_OBJECT_CLASS (valent_mpris_impl_parent_class)->constructed (object);
     562                 :             : 
     563         [ -  + ]:           5 :   g_assert (VALENT_IS_MEDIA_PLAYER (self->player));
     564                 :             : 
     565                 :           5 :   g_signal_connect_object (self->player,
     566                 :             :                            "notify",
     567                 :             :                            G_CALLBACK (valent_mpris_impl_propagate_notify),
     568                 :             :                            self,
     569                 :             :                            G_CONNECT_DEFAULT);
     570                 :           5 : }
     571                 :             : 
     572                 :             : static void
     573                 :           5 : valent_mpris_impl_dispose (GObject *object)
     574                 :             : {
     575                 :           5 :   ValentMPRISImpl *self = VALENT_MPRIS_IMPL (object);
     576                 :             : 
     577                 :           5 :   g_signal_handlers_disconnect_by_data (self->player, self);
     578                 :           5 :   valent_mpris_impl_unexport (self);
     579                 :             : 
     580                 :           5 :   G_OBJECT_CLASS (valent_mpris_impl_parent_class)->dispose (object);
     581                 :           5 : }
     582                 :             : 
     583                 :             : static void
     584                 :           5 : valent_mpris_impl_finalize (GObject *object)
     585                 :             : {
     586                 :           5 :   ValentMPRISImpl *self = VALENT_MPRIS_IMPL (object);
     587                 :             : 
     588         [ +  - ]:           5 :   g_clear_pointer (&self->bus_name, g_free);
     589         [ -  + ]:           5 :   g_clear_object (&self->connection);
     590         [ +  - ]:           5 :   g_clear_object (&self->player);
     591                 :             : 
     592         [ +  - ]:           5 :   g_clear_pointer (&self->cache, g_hash_table_unref);
     593         [ +  - ]:           5 :   g_clear_pointer (&self->pending, g_hash_table_unref);
     594                 :             : 
     595                 :           5 :   G_OBJECT_CLASS (valent_mpris_impl_parent_class)->finalize (object);
     596                 :           5 : }
     597                 :             : 
     598                 :             : static void
     599                 :           1 : valent_mpris_impl_get_property (GObject    *object,
     600                 :             :                                 guint       prop_id,
     601                 :             :                                 GValue     *value,
     602                 :             :                                 GParamSpec *pspec)
     603                 :             : {
     604                 :           1 :   ValentMPRISImpl *self = VALENT_MPRIS_IMPL (object);
     605                 :             : 
     606         [ +  - ]:           1 :   switch ((ValentMPRISImplProperty)prop_id)
     607                 :             :     {
     608                 :           1 :     case PROP_PLAYER:
     609                 :           1 :       g_value_set_object (value, self->player);
     610                 :           1 :       break;
     611                 :             : 
     612                 :           0 :     default:
     613                 :           0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     614                 :             :     }
     615                 :           1 : }
     616                 :             : 
     617                 :             : static void
     618                 :           5 : valent_mpris_impl_set_property (GObject      *object,
     619                 :             :                                 guint         prop_id,
     620                 :             :                                 const GValue *value,
     621                 :             :                                 GParamSpec   *pspec)
     622                 :             : {
     623                 :           5 :   ValentMPRISImpl *self = VALENT_MPRIS_IMPL (object);
     624                 :             : 
     625         [ +  - ]:           5 :   switch ((ValentMPRISImplProperty)prop_id)
     626                 :             :     {
     627                 :           5 :     case PROP_PLAYER:
     628                 :           5 :       self->player = g_value_dup_object (value);
     629                 :           5 :       break;
     630                 :             : 
     631                 :           0 :     default:
     632                 :           0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     633                 :             :     }
     634                 :           5 : }
     635                 :             : 
     636                 :             : static void
     637                 :           2 : valent_mpris_impl_class_init (ValentMPRISImplClass *klass)
     638                 :             : {
     639                 :           2 :   GObjectClass *object_class = G_OBJECT_CLASS (klass);
     640                 :             : 
     641                 :           2 :   object_class->constructed = valent_mpris_impl_constructed;
     642                 :           2 :   object_class->dispose = valent_mpris_impl_dispose;
     643                 :           2 :   object_class->finalize = valent_mpris_impl_finalize;
     644                 :           2 :   object_class->get_property = valent_mpris_impl_get_property;
     645                 :           2 :   object_class->set_property = valent_mpris_impl_set_property;
     646                 :             : 
     647                 :             :   /**
     648                 :             :    * VdpMprisPlayer:player:
     649                 :             :    *
     650                 :             :    * The [class@Valent.MediaPlayer] being exported.
     651                 :             :    */
     652                 :           4 :   properties [PROP_PLAYER] =
     653                 :           2 :     g_param_spec_object ("player", NULL, NULL,
     654                 :             :                          VALENT_TYPE_MEDIA_PLAYER,
     655                 :             :                          (G_PARAM_READWRITE |
     656                 :             :                           G_PARAM_CONSTRUCT_ONLY |
     657                 :             :                           G_PARAM_EXPLICIT_NOTIFY |
     658                 :             :                           G_PARAM_STATIC_STRINGS));
     659                 :             : 
     660                 :           2 :   g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
     661                 :           2 : }
     662                 :             : 
     663                 :             : static void
     664                 :           5 : valent_mpris_impl_init (ValentMPRISImpl *self)
     665                 :             : {
     666                 :           5 :   self->application_vtable.method_call = application_method_call;
     667                 :           5 :   self->application_vtable.get_property = application_get_property;
     668                 :           5 :   self->application_vtable.set_property = application_set_property;
     669                 :             : 
     670                 :           5 :   self->player_vtable.method_call = player_method_call;
     671                 :           5 :   self->player_vtable.get_property = player_get_property;
     672                 :           5 :   self->player_vtable.set_property = player_set_property;
     673                 :             : 
     674                 :           5 :   self->bus_name = g_strdup (VALENT_MPRIS_DBUS_NAME);
     675                 :           5 :   self->cache = g_hash_table_new_full (g_str_hash,
     676                 :             :                                        g_str_equal,
     677                 :             :                                        g_free,
     678                 :             :                                        (GDestroyNotify)g_variant_unref);
     679                 :           5 :   self->pending = g_hash_table_new_full (g_str_hash,
     680                 :             :                                          g_str_equal,
     681                 :             :                                          g_free,
     682                 :             :                                          (GDestroyNotify)g_variant_unref);
     683                 :           5 : }
     684                 :             : 
     685                 :             : /**
     686                 :             :  * valent_mpris_impl_new:
     687                 :             :  * @player: a `ValentMediaPlayer`
     688                 :             :  *
     689                 :             :  * Get the `ValentMPRISImpl` instance.
     690                 :             :  *
     691                 :             :  * Returns: (transfer full) (nullable): a `ValentMPRISImpl`
     692                 :             :  */
     693                 :             : ValentMPRISImpl *
     694                 :           5 : valent_mpris_impl_new (ValentMediaPlayer *player)
     695                 :             : {
     696         [ -  + ]:           5 :   g_return_val_if_fail (VALENT_IS_MEDIA_PLAYER (player), NULL);
     697                 :             : 
     698                 :           5 :   return g_object_new (VALENT_TYPE_MPRIS_IMPL,
     699                 :             :                        "player", player,
     700                 :             :                        NULL);
     701                 :             : }
     702                 :             : 
     703                 :             : /**
     704                 :             :  * valent_media_player_impl_export:
     705                 :             :  * @impl: a `ValentMPRISImpl`
     706                 :             :  * @connection: a `GDBusConnection`
     707                 :             :  * @error:
     708                 :             :  *
     709                 :             :  * Impl @impl on @connection.
     710                 :             :  */
     711                 :             : gboolean
     712                 :           5 : valent_mpris_impl_export (ValentMPRISImpl  *impl,
     713                 :             :                           GDBusConnection  *connection,
     714                 :             :                           GError          **error)
     715                 :             : {
     716         [ -  + ]:           5 :   g_return_val_if_fail (VALENT_IS_MPRIS_IMPL (impl), FALSE);
     717   [ +  -  +  -  :           5 :   g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
             -  +  -  - ]
     718   [ +  -  +  - ]:           5 :   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
     719                 :             : 
     720         [ -  + ]:           5 :   if (impl->connection == connection)
     721                 :             :     return TRUE;
     722                 :             : 
     723                 :             :   /* Unexport from any existing connection */
     724                 :           5 :   valent_mpris_impl_unexport (impl);
     725                 :           5 :   impl->connection = g_object_ref (connection);
     726                 :             : 
     727                 :             :   /* Register org.mpris.MediaPlayer2 interface */
     728         [ +  - ]:           5 :   if (impl->application_id == 0)
     729                 :             :     {
     730                 :          10 :       impl->application_id =
     731                 :           5 :         g_dbus_connection_register_object (impl->connection,
     732                 :             :                                            "/org/mpris/MediaPlayer2",
     733                 :             :                                            VALENT_MPRIS_APPLICATION_INFO,
     734                 :           5 :                                            &impl->application_vtable,
     735                 :             :                                            impl, NULL,
     736                 :             :                                            error);
     737                 :             : 
     738         [ -  + ]:           5 :       if (impl->application_id == 0)
     739                 :             :         {
     740                 :           0 :           valent_mpris_impl_unexport (impl);
     741                 :           0 :           return FALSE;
     742                 :             :         }
     743                 :             :     }
     744                 :             : 
     745                 :             :   /* Register org.mpris.MediaPlayer2.Player interface */
     746         [ +  - ]:           5 :   if (impl->player_id == 0)
     747                 :             :     {
     748                 :          10 :       impl->player_id =
     749                 :           5 :         g_dbus_connection_register_object (impl->connection,
     750                 :             :                                            "/org/mpris/MediaPlayer2",
     751                 :             :                                            VALENT_MPRIS_PLAYER_INFO,
     752                 :           5 :                                            &impl->player_vtable,
     753                 :             :                                            impl, NULL,
     754                 :             :                                            error);
     755                 :             : 
     756         [ -  + ]:           5 :       if (impl->player_id == 0)
     757                 :             :         {
     758                 :           0 :           valent_mpris_impl_unexport (impl);
     759                 :           0 :           return FALSE;
     760                 :             :         }
     761                 :             :     }
     762                 :             : 
     763                 :             :   /* Own a well-known name on the connection */
     764         [ -  + ]:           5 :   if (impl->bus_name_id == 0)
     765                 :             :     {
     766                 :           5 :       impl->bus_name_id =
     767                 :           5 :         g_bus_own_name_on_connection (impl->connection,
     768                 :           5 :                                       impl->bus_name,
     769                 :             :                                       G_BUS_NAME_OWNER_FLAGS_NONE,
     770                 :             :                                       NULL, // NameAcquired
     771                 :             :                                       NULL, // NameLost
     772                 :             :                                       NULL,
     773                 :             :                                       NULL);
     774                 :             :     }
     775                 :             : 
     776                 :             :   return TRUE;
     777                 :             : }
     778                 :             : 
     779                 :             : static void
     780                 :           5 : valent_mpris_impl_export_full_cb (GObject      *object,
     781                 :             :                                   GAsyncResult *result,
     782                 :             :                                   gpointer      user_data)
     783                 :             : {
     784                 :           5 :   g_autoptr (GTask) task = G_TASK (user_data);
     785                 :           5 :   ValentMPRISImpl *self = g_task_get_source_object (task);
     786   [ +  -  -  - ]:           5 :   g_autoptr (GDBusConnection) connection = NULL;
     787         [ -  - ]:           5 :   g_autoptr (GError) error = NULL;
     788                 :             : 
     789                 :           5 :   connection = g_dbus_connection_new_for_address_finish (result, &error);
     790         [ -  + ]:           5 :   if (connection == NULL)
     791                 :             :     {
     792                 :           0 :       g_dbus_error_strip_remote_error (error);
     793                 :           0 :       g_task_return_error (task, g_steal_pointer (&error));
     794                 :           0 :       return;
     795                 :             :     }
     796                 :             : 
     797         [ -  + ]:           5 :   if (!valent_mpris_impl_export (self, connection, &error))
     798                 :             :     {
     799                 :           0 :       g_dbus_error_strip_remote_error (error);
     800                 :           0 :       g_task_return_error (task, g_steal_pointer (&error));
     801                 :           0 :       return;
     802                 :             :     }
     803                 :             : 
     804         [ -  + ]:           5 :   g_task_return_boolean (task, TRUE);
     805                 :             : }
     806                 :             : 
     807                 :             : /**
     808                 :             :  * valent_mpris_impl_export_full:
     809                 :             :  * @impl: a `ValentMPRISImpl`
     810                 :             :  * @bus_name: the well-known name to own
     811                 :             :  * @cancellable: (nullable): a `GCancellable`
     812                 :             :  * @callback: (scope async): a `GAsyncReadyCallback`
     813                 :             :  * @user_data: user supplied data
     814                 :             :  *
     815                 :             :  * Impl the test media player on the session bus.
     816                 :             :  */
     817                 :             : void
     818                 :           5 : valent_mpris_impl_export_full (ValentMPRISImpl     *impl,
     819                 :             :                                const char          *bus_name,
     820                 :             :                                GCancellable        *cancellable,
     821                 :             :                                GAsyncReadyCallback  callback,
     822                 :             :                                gpointer             user_data)
     823                 :             : {
     824                 :           5 :   g_autoptr (GTask) task = NULL;
     825         [ -  - ]:           5 :   g_autofree char *address = NULL;
     826                 :           5 :   g_autoptr (GError) error = NULL;
     827                 :             : 
     828         [ -  + ]:           5 :   g_return_if_fail (VALENT_IS_MPRIS_IMPL (impl));
     829         [ +  - ]:           5 :   g_return_if_fail (g_dbus_is_name (bus_name));
     830   [ +  +  +  -  :           5 :   g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
             -  +  -  - ]
     831                 :             : 
     832                 :           5 :   task = g_task_new (impl, cancellable, callback, user_data);
     833         [ +  - ]:           5 :   g_task_set_source_tag (task, valent_mpris_impl_export_full);
     834                 :             : 
     835                 :             :   /* Set the new bus name */
     836                 :           5 :   g_set_str (&impl->bus_name, bus_name);
     837                 :             : 
     838                 :             :   /* Set up a dedicated connection */
     839                 :           5 :   address = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION,
     840                 :             :                                              cancellable,
     841                 :             :                                              &error);
     842         [ -  + ]:           5 :   if (address == NULL)
     843                 :             :     {
     844                 :           0 :       g_dbus_error_strip_remote_error (error);
     845                 :           0 :       g_task_return_error (task, g_steal_pointer (&error));
     846                 :           0 :       return;
     847                 :             :     }
     848                 :             : 
     849         [ -  + ]:           5 :   g_dbus_connection_new_for_address (address,
     850                 :             :                                      G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
     851                 :             :                                      G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
     852                 :             :                                      NULL,
     853                 :             :                                      cancellable,
     854                 :             :                                      (GAsyncReadyCallback)valent_mpris_impl_export_full_cb,
     855                 :             :                                      g_steal_pointer (&task));
     856                 :             : }
     857                 :             : 
     858                 :             : /**
     859                 :             :  * valent_mpris_impl_export_finish:
     860                 :             :  * @impl: a `ValentMPRISImpl`
     861                 :             :  * @cancellable: (nullable): a `GCancellable`
     862                 :             :  * @error: (nullable): a `GError`
     863                 :             :  *
     864                 :             :  * Finish an operation started by valent_mpris_impl_export_full().
     865                 :             :  *
     866                 :             :  * Returns: %TRUE if successful, or %FALSE with @error set
     867                 :             :  */
     868                 :             : gboolean
     869                 :           5 : valent_mpris_impl_export_finish (ValentMPRISImpl  *impl,
     870                 :             :                                  GAsyncResult     *result,
     871                 :             :                                  GError          **error)
     872                 :             : {
     873         [ -  + ]:           5 :   g_return_val_if_fail (VALENT_IS_MPRIS_IMPL (impl), FALSE);
     874         [ +  - ]:           5 :   g_return_val_if_fail (g_task_is_valid (result, impl), FALSE);
     875   [ +  -  +  - ]:           5 :   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
     876                 :             : 
     877                 :           5 :   return g_task_propagate_boolean (G_TASK (result), error);
     878                 :             : }
     879                 :             : 
     880                 :             : /**
     881                 :             :  * valent_mpris_player_impl_unexport:
     882                 :             :  * @impl: a `ValentMPRISImpl`
     883                 :             :  *
     884                 :             :  * Unexport the player.
     885                 :             :  */
     886                 :             : void
     887                 :          15 : valent_mpris_impl_unexport (ValentMPRISImpl *impl)
     888                 :             : {
     889         [ -  + ]:          15 :   g_return_if_fail (VALENT_IS_MPRIS_IMPL (impl));
     890                 :             : 
     891         [ -  + ]:          15 :   g_clear_handle_id (&impl->flush_id, g_source_remove);
     892         [ +  + ]:          15 :   g_clear_handle_id (&impl->bus_name_id, g_bus_unown_name);
     893                 :             : 
     894         [ +  + ]:          15 :   if (impl->player_id > 0)
     895                 :             :     {
     896                 :           5 :       g_dbus_connection_unregister_object (impl->connection,
     897                 :             :                                            impl->player_id);
     898                 :           5 :       impl->player_id = 0;
     899                 :             :     }
     900                 :             : 
     901         [ +  + ]:          15 :   if (impl->application_id > 0)
     902                 :             :     {
     903                 :           5 :       g_dbus_connection_unregister_object (impl->connection,
     904                 :             :                                            impl->application_id);
     905                 :           5 :       impl->application_id = 0;
     906                 :             :     }
     907                 :             : 
     908         [ +  + ]:          15 :   g_clear_object (&impl->connection);
     909                 :             : }
     910                 :             : 
        

Generated by: LCOV version 2.0-1