/* * Copyright (C) 2014 Michal Ratajsky * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the licence, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ #include #include #include #include #include #include #include #include "pulse-connection.h" #include "pulse-ext-stream.h" #include "pulse-helpers.h" #include "pulse-stream.h" #include "pulse-stream-control.h" struct _PulseExtStreamPrivate { MateMixerAppInfo *app_info; MateMixerDirection direction; }; enum { PROP_0, PROP_DIRECTION }; static void mate_mixer_stored_control_interface_init (MateMixerStoredControlInterface *iface); static void pulse_ext_stream_class_init (PulseExtStreamClass *klass); static void pulse_ext_stream_get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec); static void pulse_ext_stream_set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec); static void pulse_ext_stream_init (PulseExtStream *ext); G_DEFINE_TYPE_WITH_CODE (PulseExtStream, pulse_ext_stream, PULSE_TYPE_STREAM_CONTROL, G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_STORED_CONTROL, mate_mixer_stored_control_interface_init)) static MateMixerDirection pulse_ext_stream_get_direction (MateMixerStoredControl *mmsc); static MateMixerAppInfo * pulse_ext_stream_get_app_info (MateMixerStreamControl *mmsc); static gboolean pulse_ext_stream_set_stream (MateMixerStreamControl *mmsc, MateMixerStream *mms); static gboolean pulse_ext_stream_set_mute (PulseStreamControl *control, gboolean mute); static gboolean pulse_ext_stream_set_volume (PulseStreamControl *control, pa_cvolume *cvolume); static void fill_ext_stream_restore_info (PulseStreamControl *control, pa_ext_stream_restore_info *info); static void mate_mixer_stored_control_interface_init (MateMixerStoredControlInterface *iface) { iface->get_direction = pulse_ext_stream_get_direction; } static void pulse_ext_stream_class_init (PulseExtStreamClass *klass) { GObjectClass *object_class; MateMixerStreamControlClass *control_class; PulseStreamControlClass *pulse_class; object_class = G_OBJECT_CLASS (klass); object_class->get_property = pulse_ext_stream_get_property; object_class->set_property = pulse_ext_stream_set_property; control_class = MATE_MIXER_STREAM_CONTROL_CLASS (klass); control_class->get_app_info = pulse_ext_stream_get_app_info; control_class->set_stream = pulse_ext_stream_set_stream; pulse_class = PULSE_STREAM_CONTROL_CLASS (klass); pulse_class->set_mute = pulse_ext_stream_set_mute; pulse_class->set_volume = pulse_ext_stream_set_volume; g_object_class_override_property (object_class, PROP_DIRECTION, "direction"); g_type_class_add_private (object_class, sizeof (PulseExtStreamPrivate)); } static void pulse_ext_stream_get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec) { PulseExtStream *ext; ext = PULSE_EXT_STREAM (object); switch (param_id) { case PROP_DIRECTION: g_value_set_enum (value, ext->priv->direction); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; } } static void pulse_ext_stream_set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec) { PulseExtStream *ext; ext = PULSE_EXT_STREAM (object); switch (param_id) { case PROP_DIRECTION: ext->priv->direction = g_value_get_enum (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; } } static void pulse_ext_stream_init (PulseExtStream *ext) { ext->priv = G_TYPE_INSTANCE_GET_PRIVATE (ext, PULSE_TYPE_EXT_STREAM, PulseExtStreamPrivate); } PulseExtStream * pulse_ext_stream_new (PulseConnection *connection, const pa_ext_stream_restore_info *info, PulseStream *parent) { PulseExtStream *ext; gchar *suffix; MateMixerDirection direction; MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE | MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE | MATE_MIXER_STREAM_CONTROL_MUTE_READABLE | MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE; MateMixerStreamControlRole role = MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN; MateMixerAppInfo *app_info; MateMixerStreamControlMediaRole media_role = MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_UNKNOWN; g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL); g_return_val_if_fail (info != NULL, NULL); if (g_str_has_prefix (info->name, "sink-input")) direction = MATE_MIXER_DIRECTION_OUTPUT; else if (g_str_has_prefix (info->name, "source-output")) direction = MATE_MIXER_DIRECTION_INPUT; else direction = MATE_MIXER_DIRECTION_UNKNOWN; app_info = _mate_mixer_app_info_new (); suffix = strchr (info->name, ':'); if (suffix != NULL) suffix++; if (strstr (info->name, "-by-media-role:")) { if (G_LIKELY (suffix != NULL)) media_role = pulse_convert_media_role_name (suffix); } else if (strstr (info->name, "-by-application-name:")) { role = MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION; if (G_LIKELY (suffix != NULL)) _mate_mixer_app_info_set_name (app_info, suffix); } else if (strstr (info->name, "-by-application-id:")) { role = MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION; if (G_LIKELY (suffix != NULL)) _mate_mixer_app_info_set_id (app_info, suffix); } ext = g_object_new (PULSE_TYPE_EXT_STREAM, "flags", flags, "role", role, "media-role", media_role, "name", info->name, "connection", connection, "direction", direction, "stream", parent, NULL); if (role == MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION) ext->priv->app_info = app_info; else _mate_mixer_app_info_free (app_info); pulse_ext_stream_update (ext, info, parent); return ext; } void pulse_ext_stream_update (PulseExtStream *ext, const pa_ext_stream_restore_info *info, PulseStream *parent) { g_return_if_fail (PULSE_IS_EXT_STREAM (ext)); g_return_if_fail (info != NULL); /* Let all the information update before emitting notify signals */ g_object_freeze_notify (G_OBJECT (ext)); _mate_mixer_stream_control_set_mute (MATE_MIXER_STREAM_CONTROL (ext), info->mute ? TRUE : FALSE); pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (ext), &info->channel_map); pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (ext), &info->volume, 0); _mate_mixer_stream_control_set_stream (MATE_MIXER_STREAM_CONTROL (ext), MATE_MIXER_STREAM (parent)); g_object_thaw_notify (G_OBJECT (ext)); } static MateMixerDirection pulse_ext_stream_get_direction (MateMixerStoredControl *mmsc) { g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), MATE_MIXER_DIRECTION_UNKNOWN); return PULSE_EXT_STREAM (mmsc)->priv->direction; } static MateMixerAppInfo * pulse_ext_stream_get_app_info (MateMixerStreamControl *mmsc) { g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), NULL); return PULSE_EXT_STREAM (mmsc)->priv->app_info; } static gboolean pulse_ext_stream_set_stream (MateMixerStreamControl *mmsc, MateMixerStream *mms) { pa_ext_stream_restore_info info; g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), FALSE); g_return_val_if_fail (mms == NULL || PULSE_IS_STREAM (mms), FALSE); fill_ext_stream_restore_info (PULSE_STREAM_CONTROL (mmsc), &info); if (mms != NULL) info.device = mate_mixer_stream_get_name (mms); else info.device = NULL; return pulse_connection_write_ext_stream (pulse_stream_control_get_connection (PULSE_STREAM_CONTROL (mmsc)), &info); } static gboolean pulse_ext_stream_set_mute (PulseStreamControl *control, gboolean mute) { pa_ext_stream_restore_info info; g_return_val_if_fail (PULSE_IS_EXT_STREAM (control), FALSE); fill_ext_stream_restore_info (control, &info); info.mute = mute; return pulse_connection_write_ext_stream (pulse_stream_control_get_connection (control), &info); } static gboolean pulse_ext_stream_set_volume (PulseStreamControl *control, pa_cvolume *cvolume) { pa_ext_stream_restore_info info; g_return_val_if_fail (PULSE_IS_EXT_STREAM (control), FALSE); g_return_val_if_fail (cvolume != NULL, FALSE); fill_ext_stream_restore_info (control, &info); info.volume = *cvolume; return pulse_connection_write_ext_stream (pulse_stream_control_get_connection (control), &info); } static void fill_ext_stream_restore_info (PulseStreamControl *control, pa_ext_stream_restore_info *info) { MateMixerStream *stream; MateMixerStreamControl *mmsc; const pa_channel_map *map; const pa_cvolume *cvolume; mmsc = MATE_MIXER_STREAM_CONTROL (control); info->name = mate_mixer_stream_control_get_name (mmsc); info->mute = mate_mixer_stream_control_get_mute (mmsc); map = pulse_stream_control_get_channel_map (control); if G_LIKELY (map != NULL) info->channel_map = *map; else pa_channel_map_init (&info->channel_map); cvolume = pulse_stream_control_get_cvolume (control); if G_LIKELY (cvolume != NULL) info->volume = *cvolume; else pa_cvolume_init (&info->volume); stream = mate_mixer_stream_control_get_stream (mmsc); if (stream != NULL) info->device = mate_mixer_stream_get_name (stream); else info->device = NULL; }