diff options
Diffstat (limited to 'redhat/tdemultimedia/tdemultimedia-3.5.13.2-kmix_pulseaudio.patch')
-rw-r--r-- | redhat/tdemultimedia/tdemultimedia-3.5.13.2-kmix_pulseaudio.patch | 1519 |
1 files changed, 0 insertions, 1519 deletions
diff --git a/redhat/tdemultimedia/tdemultimedia-3.5.13.2-kmix_pulseaudio.patch b/redhat/tdemultimedia/tdemultimedia-3.5.13.2-kmix_pulseaudio.patch deleted file mode 100644 index 0759d6ebb..000000000 --- a/redhat/tdemultimedia/tdemultimedia-3.5.13.2-kmix_pulseaudio.patch +++ /dev/null @@ -1,1519 +0,0 @@ -diff -Nuar ./kmix-tde-ori/kmix-platforms.cpp ./kmix/kmix-platforms.cpp ---- ./kmix-tde-ori/kmix-platforms.cpp 2013-05-09 17:10:41.010638051 +0200 -+++ ./kmix/kmix-platforms.cpp 2013-05-09 17:18:23.202075902 +0200 -@@ -46,8 +46,10 @@ - #define ALSA_MIXER - #endif - -+#define PULSE_MIXER -+ - #define OSS_MIXER --#endif -+#endif // __linux__ - - #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(_UNIXWARE) || defined(__DragonFly__) - #define OSS_MIXER -@@ -79,6 +81,10 @@ - #include "mixer_alsa9.cpp" - #endif - -+#if defined(PULSE_MIXER) -+#include "mixer_pulse.cpp" -+#endif -+ - #if defined(OSS_MIXER) - #include "mixer_oss.cpp" - -@@ -91,7 +97,7 @@ - #define OSS4_MIXER - #endif - --#endif -+#endif // OSS_MIXER - - #if defined(OSS4_MIXER) - #include "mixer_oss4.cpp" -@@ -137,6 +143,10 @@ - { ALSA_getMixer, ALSA_getDriverName, ALSA_getDevIterator }, - #endif - -+#if defined(PULSE_MIXER) -+ { PULSE_getMixer, PULSE_getDriverName, NULL }, -+#endif -+ - #if defined(OSS4_MIXER) - { OSS4_getMixer, OSS4_getDriverName, NULL }, - #endif -diff -Nuar ./kmix-tde-ori/Makefile.am ./kmix/Makefile.am ---- ./kmix-tde-ori/Makefile.am 2013-05-09 17:10:41.010638051 +0200 -+++ ./kmix/Makefile.am 2013-05-21 23:47:47.607546135 +0200 -@@ -41,7 +41,7 @@ - verticaltext.cpp mixerIface.skel colorwidget.ui dialogviewconfiguration.cpp \ - kmixtoolbox.cpp mixertoolbox.cpp dialogselectmaster.cpp - --kmix_panelapplet_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module -+kmix_panelapplet_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -I/usr/include/glib-2.0 -module - kmix_panelapplet_la_LIBADD = $(LIB_KDEUI) $(LIB_KUTILS) $(LIBALIB) $(LIBOSSAUDIO) $(LIBASOUND) - - xdg_apps_DATA = kmix.desktop -diff -Nuar ./kmix-tde-ori/mixer_pulse.cpp ./kmix/mixer_pulse.cpp ---- ./kmix-tde-ori/mixer_pulse.cpp 1970-01-01 01:00:00.000000000 +0100 -+++ ./kmix/mixer_pulse.cpp 2013-05-21 23:37:43.565178347 +0200 -@@ -0,0 +1,1368 @@ -+/* -+ * KMix -- KDE's full featured mini mixer -+ * -+ * -+ * Copyright (C) 2008 Helio Chissini de Castro <helio@kde.org> -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Library General Public -+ * License as published by the Free Software Foundation; either -+ * version 2 of the License, or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Library General Public License for more details. -+ * -+ * You should have received a copy of the GNU Library General Public -+ * License along with this program; if not, write to the Free -+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -+ */ -+ -+#include "mixer_pulse.h" -+ -+#include <cstdlib> -+//#include <QtCore/QAbstractEventDispatcher> -+#include <tqtimer.h> -+ -+#include <klocale.h> -+ -+#include "mixer.h" -+//#include "core/ControlManager.h" -+//#include "core/GlobalConfig.h" -+ -+#include <pulse/glib-mainloop.h> -+#include <pulse/ext-stream-restore.h> -+#if defined(HAVE_CANBERRA) -+# include <canberra.h> -+#endif -+ -+// PA_VOLUME_UI_MAX landed in pulseaudio-0.9.23, so this can be removed when/if -+// minimum requirement is ever bumped up (from 0.9.12 currently) -+#ifndef PA_VOLUME_UI_MAX -+#define PA_VOLUME_UI_MAX (pa_sw_volume_from_dB(+11.0)) -+#endif -+ -+#define HAVE_SOURCE_OUTPUT_VOLUMES PA_CHECK_VERSION(1,0,0) -+ -+#define KMIXPA_PLAYBACK 0 -+#define KMIXPA_CAPTURE 1 -+#define KMIXPA_APP_PLAYBACK 2 -+#define KMIXPA_APP_CAPTURE 3 -+#define KMIXPA_WIDGET_MAX KMIXPA_APP_CAPTURE -+ -+#define KMIXPA_EVENT_KEY "sink-input-by-media-role:event" -+ -+static unsigned int refcount = 0; -+static pa_glib_mainloop *s_mainloop = NULL; -+static pa_context *s_context = NULL; -+static enum { UNKNOWN, ACTIVE, INACTIVE } s_pulseActive = UNKNOWN; -+static int s_outstandingRequests = 0; -+ -+#if defined(HAVE_CANBERRA) -+static ca_context *s_ccontext = NULL; -+#endif -+ -+TQMap<int,Mixer_PULSE*> s_mixers; -+ -+typedef TQMap<int,devinfo> devmap; -+static devmap outputDevices; -+static devmap captureDevices; -+static TQMap<int,TQString> clients; -+static devmap outputStreams; -+static devmap captureStreams; -+static devmap outputRoles; -+ -+typedef struct { -+ pa_channel_map channel_map; -+ pa_cvolume volume; -+ bool mute; -+ TQString device; -+} restoreRule; -+static TQMap<TQString,restoreRule> s_RestoreRules; -+ -+static void dec_outstanding(pa_context *c) { -+ if (s_outstandingRequests <= 0) -+ return; -+ -+ if (--s_outstandingRequests == 0) -+ { -+ s_pulseActive = ACTIVE; -+ -+ // If this is our probe phase, exit our context immediately -+ if (s_context != c) { -+ pa_context_disconnect(c); -+ } else -+ kDebug(67100) << "Reconnected to PulseAudio"; -+ } -+} -+ -+static void translateMasksAndMaps(devinfo& dev) -+{ -+ dev.chanMask = Volume::MNONE; -+ dev.chanIDs.clear(); -+ -+ if (dev.channel_map.channels != dev.volume.channels) { -+ kError() << "Hiddeous Channel mixup map says " << dev.channel_map.channels << ", volume says: " << dev.volume.channels; -+ return; -+ } -+ if (1 == dev.channel_map.channels && PA_CHANNEL_POSITION_MONO == dev.channel_map.map[0]) { -+ // We just use the left channel to represent this. -+ dev.chanMask = (Volume::ChannelMask)( dev.chanMask | Volume::MLEFT); -+ dev.chanIDs[0] = Volume::LEFT; -+ } else { -+ for (uint8_t i = 0; i < dev.channel_map.channels; ++i) { -+ switch (dev.channel_map.map[i]) { -+ case PA_CHANNEL_POSITION_MONO: -+ kWarning(67100) << "Channel Map contains a MONO element but has >1 channel - we can't handle this."; -+ return; -+ -+ case PA_CHANNEL_POSITION_FRONT_LEFT: -+ dev.chanMask = (Volume::ChannelMask)( dev.chanMask | Volume::MLEFT); -+ dev.chanIDs[i] = Volume::LEFT; -+ break; -+ case PA_CHANNEL_POSITION_FRONT_RIGHT: -+ dev.chanMask = (Volume::ChannelMask)( dev.chanMask | Volume::MRIGHT); -+ dev.chanIDs[i] = Volume::RIGHT; -+ break; -+ case PA_CHANNEL_POSITION_FRONT_CENTER: -+ dev.chanMask = (Volume::ChannelMask)( dev.chanMask | Volume::MCENTER); -+ dev.chanIDs[i] = Volume::CENTER; -+ break; -+ case PA_CHANNEL_POSITION_REAR_CENTER: -+ dev.chanMask = (Volume::ChannelMask)( dev.chanMask | Volume::MREARCENTER); -+ dev.chanIDs[i] = Volume::REARCENTER; -+ break; -+ case PA_CHANNEL_POSITION_REAR_LEFT: -+ dev.chanMask = (Volume::ChannelMask)( dev.chanMask | Volume::MSURROUNDLEFT); -+ dev.chanIDs[i] = Volume::SURROUNDLEFT; -+ break; -+ case PA_CHANNEL_POSITION_REAR_RIGHT: -+ dev.chanMask = (Volume::ChannelMask)( dev.chanMask | Volume::MSURROUNDRIGHT); -+ dev.chanIDs[i] = Volume::SURROUNDRIGHT; -+ break; -+ case PA_CHANNEL_POSITION_LFE: -+ dev.chanMask = (Volume::ChannelMask)( dev.chanMask | Volume::MWOOFER); -+ dev.chanIDs[i] = Volume::WOOFER; -+ break; -+ case PA_CHANNEL_POSITION_SIDE_LEFT: -+ dev.chanMask = (Volume::ChannelMask)( dev.chanMask | Volume::MREARSIDELEFT); -+ dev.chanIDs[i] = Volume::REARSIDELEFT; -+ break; -+ case PA_CHANNEL_POSITION_SIDE_RIGHT: -+ dev.chanMask = (Volume::ChannelMask)( dev.chanMask | Volume::MREARSIDERIGHT); -+ dev.chanIDs[i] = Volume::REARSIDERIGHT; -+ break; -+ default: -+ kWarning(67100) << "Channel Map contains a pa_channel_position we cannot handle " << dev.channel_map.map[i]; -+ break; -+ } -+ } -+ } -+} -+ -+static TQString getIconNameFromProplist(pa_proplist *l) { -+ const char *t; -+ -+ if ((t = pa_proplist_gets(l, PA_PROP_MEDIA_ICON_NAME))) -+ return TQString::fromUtf8(t); -+ -+ if ((t = pa_proplist_gets(l, PA_PROP_WINDOW_ICON_NAME))) -+ return TQString::fromUtf8(t); -+ -+ if ((t = pa_proplist_gets(l, PA_PROP_APPLICATION_ICON_NAME))) -+ return TQString::fromUtf8(t); -+ -+ if ((t = pa_proplist_gets(l, PA_PROP_MEDIA_ROLE))) { -+ -+ if (strcmp(t, "video") == 0 || strcmp(t, "phone") == 0) -+ return TQString::fromUtf8(t); -+ -+ if (strcmp(t, "music") == 0) -+ return "audio"; -+ -+ if (strcmp(t, "game") == 0) -+ return "applications-games"; -+ -+ if (strcmp(t, "event") == 0) -+ return "dialog-information"; -+ } -+ -+ return ""; -+} -+ -+static void sink_cb(pa_context *c, const pa_sink_info *i, int eol, void *) { -+ -+ if (eol < 0) { -+ if (pa_context_errno(c) == PA_ERR_NOENTITY) -+ return; -+ -+ kWarning(67100) << "Sink callback failure"; -+ return; -+ } -+ -+ if (eol > 0) { -+ dec_outstanding(c); -+ if (s_mixers.contains(KMIXPA_PLAYBACK)) -+ s_mixers[KMIXPA_PLAYBACK]->triggerUpdate(); -+ return; -+ } -+ -+ devinfo s; -+ s.index = s.device_index = i->index; -+ s.name = TQString::fromUtf8(i->name).replace(' ', '_'); -+ s.description = TQString::fromUtf8(i->description); -+ s.icon_name = TQString::fromUtf8(pa_proplist_gets(i->proplist, PA_PROP_DEVICE_ICON_NAME)); -+ s.volume = i->volume; -+ s.channel_map = i->channel_map; -+ s.mute = !!i->mute; -+ s.stream_restore_rule = ""; -+ -+ translateMasksAndMaps(s); -+ -+ bool is_new = !outputDevices.contains(s.index); -+ outputDevices[s.index] = s; -+// kDebug(67100) << "Got some info about sink: " << s.description; -+ -+ if (s_mixers.contains(KMIXPA_PLAYBACK)) { -+ if (is_new) -+ s_mixers[KMIXPA_PLAYBACK]->addWidget(s.index); -+ else { -+ int mid = s_mixers[KMIXPA_PLAYBACK]->id2num(s.name); -+ if (mid >= 0) { -+ MixSet *ms = s_mixers[KMIXPA_PLAYBACK]->getMixSet(); -+ (*ms)[mid]->setReadableName(s.description); -+ } -+ } -+ } -+} -+ -+static void source_cb(pa_context *c, const pa_source_info *i, int eol, void *) { -+ -+ if (eol < 0) { -+ if (pa_context_errno(c) == PA_ERR_NOENTITY) -+ return; -+ -+ kWarning(67100) << "Source callback failure"; -+ return; -+ } -+ -+ if (eol > 0) { -+ dec_outstanding(c); -+ if (s_mixers.contains(KMIXPA_CAPTURE)) -+ s_mixers[KMIXPA_CAPTURE]->triggerUpdate(); -+ return; -+ } -+ -+ // Do something.... -+ if (PA_INVALID_INDEX != i->monitor_of_sink) -+ { -+ kDebug(67100) << "Ignoring Monitor Source: " << i->description; -+ return; -+ } -+ -+ devinfo s; -+ s.index = s.device_index = i->index; -+ s.name = TQString::fromUtf8(i->name).replace(' ', '_'); -+ s.description = TQString::fromUtf8(i->description); -+ s.icon_name = TQString::fromUtf8(pa_proplist_gets(i->proplist, PA_PROP_DEVICE_ICON_NAME)); -+ s.volume = i->volume; -+ s.channel_map = i->channel_map; -+ s.mute = !!i->mute; -+ s.stream_restore_rule = ""; -+ -+ translateMasksAndMaps(s); -+ -+ bool is_new = !captureDevices.contains(s.index); -+ captureDevices[s.index] = s; -+// kDebug(67100) << "Got some info about source: " << s.description; -+ -+ if (s_mixers.contains(KMIXPA_CAPTURE)) { -+ if (is_new) -+ s_mixers[KMIXPA_CAPTURE]->addWidget(s.index); -+ else { -+ int mid = s_mixers[KMIXPA_CAPTURE]->id2num(s.name); -+ if (mid >= 0) { -+ MixSet *ms = s_mixers[KMIXPA_CAPTURE]->getMixSet(); -+ (*ms)[mid]->setReadableName(s.description); -+ } -+ } -+ } -+} -+ -+static void client_cb(pa_context *c, const pa_client_info *i, int eol, void *) { -+ -+ if (eol < 0) { -+ if (pa_context_errno(c) == PA_ERR_NOENTITY) -+ return; -+ -+ kWarning(67100) << "Client callback failure"; -+ return; -+ } -+ -+ if (eol > 0) { -+ dec_outstanding(c); -+ return; -+ } -+ -+ clients[i->index] = TQString::fromUtf8(i->name); -+ //kDebug(67100) << "Got some info about client: " << clients[i->index]; -+} -+ -+static void sink_input_cb(pa_context *c, const pa_sink_input_info *i, int eol, void *) { -+ -+ if (eol < 0) { -+ if (pa_context_errno(c) == PA_ERR_NOENTITY) -+ return; -+ -+ kWarning(67100) << "Sink Input callback failure"; -+ return; -+ } -+ -+ if (eol > 0) { -+ dec_outstanding(c); -+ if (s_mixers.contains(KMIXPA_APP_PLAYBACK)) -+ s_mixers[KMIXPA_APP_PLAYBACK]->triggerUpdate(); -+ return; -+ } -+ -+ const char *t; -+ if ((t = pa_proplist_gets(i->proplist, "module-stream-restore.id"))) { -+ if (strcmp(t, KMIXPA_EVENT_KEY) == 0) { -+ kWarning(67100) << "Ignoring sink-input due to it being designated as an event and thus handled by the Event slider"; -+ return; -+ } -+ } -+ -+ TQString appname = i18n("Unknown Application"); -+ if (clients.contains(i->client)) -+ appname = clients[i->client]; -+ -+ TQString prefix = TQString("%1: ").arg(appname); -+ -+ devinfo s; -+ s.index = i->index; -+ s.device_index = i->sink; -+ s.description = prefix + TQString::fromUtf8(i->name); -+ s.name = TQString("stream:") + TQString::number(i->index); //appname.replace(' ', '_').toLower(); -+ s.icon_name = getIconNameFromProplist(i->proplist); -+ s.channel_map = i->channel_map; -+ s.volume = i->volume; -+ s.mute = !!i->mute; -+ s.stream_restore_rule = TQString::fromUtf8(t); -+ -+ translateMasksAndMaps(s); -+ -+ bool is_new = !outputStreams.contains(s.index); -+ outputStreams[s.index] = s; -+// kDebug(67100) << "Got some info about sink input (playback stream): " << s.description; -+ -+ if (s_mixers.contains(KMIXPA_APP_PLAYBACK)) { -+ if (is_new) -+ s_mixers[KMIXPA_APP_PLAYBACK]->addWidget(s.index, true); -+ else { -+ int mid = s_mixers[KMIXPA_APP_PLAYBACK]->id2num(s.name); -+ if (mid >= 0) { -+ MixSet *ms = s_mixers[KMIXPA_APP_PLAYBACK]->getMixSet(); -+ (*ms)[mid]->setReadableName(s.description); -+ } -+ } -+ } -+} -+ -+static void source_output_cb(pa_context *c, const pa_source_output_info *i, int eol, void *) { -+ -+ if (eol < 0) { -+ if (pa_context_errno(c) == PA_ERR_NOENTITY) -+ return; -+ -+ kWarning(67100) << "Source Output callback failure"; -+ return; -+ } -+ -+ if (eol > 0) { -+ dec_outstanding(c); -+ if (s_mixers.contains(KMIXPA_APP_CAPTURE)) -+ s_mixers[KMIXPA_APP_CAPTURE]->triggerUpdate(); -+ return; -+ } -+ -+ /* NB Until Source Outputs support volumes, we just use the volume of the source itself */ -+ if (!captureDevices.contains(i->source)) { -+ kDebug(67100) << "Source Output refers to a Source we don't have any info for (probably just a peak meter or similar)"; -+ return; -+ } -+ -+ TQString appname = i18n("Unknown Application"); -+ if (clients.contains(i->client)) -+ appname = clients[i->client]; -+ -+ TQString prefix = TQString("%1: ").arg(appname); -+ -+ devinfo s; -+ s.index = i->index; -+ s.device_index = i->source; -+ s.description = prefix + TQString::fromUtf8(i->name); -+ s.name = TQString("stream:") + TQString::number(i->index); //appname.replace(' ', '_').toLower(); -+ s.icon_name = getIconNameFromProplist(i->proplist); -+ s.channel_map = i->channel_map; -+#if HAVE_SOURCE_OUTPUT_VOLUMES -+ s.volume = i->volume; -+ s.mute = !!i->mute; -+#else -+ s.volume = captureDevices[i->source].volume; -+ s.mute = captureDevices[i->source].mute; -+#endif -+ s.stream_restore_rule = TQString::fromUtf8(pa_proplist_gets(i->proplist, "module-stream-restore.id")); -+ -+ translateMasksAndMaps(s); -+ -+ bool is_new = !captureStreams.contains(s.index); -+ captureStreams[s.index] = s; -+// kDebug(67100) << "Got some info about source output (capture stream): " << s.description; -+ -+ if (s_mixers.contains(KMIXPA_APP_CAPTURE)) { -+ if (is_new) -+ s_mixers[KMIXPA_APP_CAPTURE]->addWidget(s.index, true); -+ else { -+ int mid = s_mixers[KMIXPA_APP_CAPTURE]->id2num(s.name); -+ if (mid >= 0) { -+ MixSet *ms = s_mixers[KMIXPA_APP_CAPTURE]->getMixSet(); -+ (*ms)[mid]->setReadableName(s.description); -+ } -+ } -+ } -+} -+ -+ -+static devinfo create_role_devinfo(TQString name) { -+ -+ TQ_ASSERT(s_RestoreRules.contains(name)); -+ -+ devinfo s; -+ s.index = s.device_index = PA_INVALID_INDEX; -+ s.description = i18n("Event Sounds"); -+ s.name = TQString("restore:") + name; -+ s.icon_name = "dialog-information"; -+ s.channel_map = s_RestoreRules[name].channel_map; -+ s.volume = s_RestoreRules[name].volume; -+ s.mute = s_RestoreRules[name].mute; -+ s.stream_restore_rule = name; -+ -+ translateMasksAndMaps(s); -+ return s; -+} -+ -+ -+void ext_stream_restore_read_cb(pa_context *c, const pa_ext_stream_restore_info *i, int eol, void *) { -+ -+ if (eol < 0) { -+ dec_outstanding(c); -+ kWarning(67100) << "Failed to initialize stream_restore extension: " << pa_strerror(pa_context_errno(s_context)); -+ return; -+ } -+ -+ if (eol > 0) { -+ dec_outstanding(c); -+ -+ // Special case: ensure that our media events exists. -+ // On first login by a new users, this wont be in our database so we should create it. -+ if (!s_RestoreRules.contains(KMIXPA_EVENT_KEY)) { -+ // Create a fake rule -+ restoreRule rule; -+ rule.channel_map.channels = 1; -+ rule.channel_map.map[0] = PA_CHANNEL_POSITION_MONO; -+ rule.volume.channels = 1; -+ rule.volume.values[0] = PA_VOLUME_NORM; -+ rule.mute = false; -+ rule.device = ""; -+ s_RestoreRules[KMIXPA_EVENT_KEY] = rule; -+ kDebug(67100) << "Initialising restore rule for new user: " << i18n("Event Sounds"); -+ } -+ -+ if (s_mixers.contains(KMIXPA_APP_PLAYBACK)) { -+ // If we have rules, it will be created below... but if no rules -+ // then we add it here. -+ if (!outputRoles.contains(PA_INVALID_INDEX)) { -+ devinfo s = create_role_devinfo(KMIXPA_EVENT_KEY); -+ outputRoles[s.index] = s; -+ -+ s_mixers[KMIXPA_APP_PLAYBACK]->addWidget(s.index); -+ } -+ -+ s_mixers[KMIXPA_APP_PLAYBACK]->triggerUpdate(); -+ } -+ -+ return; -+ } -+ -+ -+ TQString name = TQString::fromUtf8(i->name); -+// kDebug(67100) << TQString("Got some info about restore rule: '%1' (Device: %2)").arg(name).arg(i->device ? i->device : "None"); -+ restoreRule rule; -+ rule.channel_map = i->channel_map; -+ rule.volume = i->volume; -+ rule.mute = !!i->mute; -+ rule.device = i->device; -+ -+ if (rule.channel_map.channels < 1 && name == KMIXPA_EVENT_KEY) { -+ // Stream restore rules may not have valid volumes/channel maps (as these are optional) -+ // but we need a valid volume+channelmap for our events sounds so fix it up. -+ rule.channel_map.channels = 1; -+ rule.channel_map.map[0] = PA_CHANNEL_POSITION_MONO; -+ rule.volume.channels = 1; -+ rule.volume.values[0] = PA_VOLUME_NORM; -+ } -+ -+ s_RestoreRules[name] = rule; -+ -+ if (s_mixers.contains(KMIXPA_APP_PLAYBACK)) { -+ // We only want to know about Sound Events for now... -+ if (name == KMIXPA_EVENT_KEY) { -+ devinfo s = create_role_devinfo(name); -+ bool is_new = !outputRoles.contains(s.index); -+ outputRoles[s.index] = s; -+ -+ if (is_new) -+ s_mixers[KMIXPA_APP_PLAYBACK]->addWidget(s.index, true); -+ } -+ } -+} -+ -+static void ext_stream_restore_subscribe_cb(pa_context *c, void *) { -+ -+ TQ_ASSERT(c == s_context); -+ -+ pa_operation *o; -+ if (!(o = pa_ext_stream_restore_read(c, ext_stream_restore_read_cb, NULL))) { -+ kWarning(67100) << "pa_ext_stream_restore_read() failed"; -+ return; -+ } -+ -+ pa_operation_unref(o); -+} -+ -+ -+static void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t index, void *) { -+ -+ TQ_ASSERT(c == s_context); -+ -+ switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { -+ case PA_SUBSCRIPTION_EVENT_SINK: -+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { -+ if (s_mixers.contains(KMIXPA_PLAYBACK)) -+ s_mixers[KMIXPA_PLAYBACK]->removeWidget(index); -+ } else { -+ pa_operation *o; -+ if (!(o = pa_context_get_sink_info_by_index(c, index, sink_cb, NULL))) { -+ kWarning(67100) << "pa_context_get_sink_info_by_index() failed"; -+ return; -+ } -+ pa_operation_unref(o); -+ } -+ break; -+ -+ case PA_SUBSCRIPTION_EVENT_SOURCE: -+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { -+ if (s_mixers.contains(KMIXPA_CAPTURE)) -+ s_mixers[KMIXPA_CAPTURE]->removeWidget(index); -+ } else { -+ pa_operation *o; -+ if (!(o = pa_context_get_source_info_by_index(c, index, source_cb, NULL))) { -+ kWarning(67100) << "pa_context_get_source_info_by_index() failed"; -+ return; -+ } -+ pa_operation_unref(o); -+ } -+ break; -+ -+ case PA_SUBSCRIPTION_EVENT_SINK_INPUT: -+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { -+ if (s_mixers.contains(KMIXPA_APP_PLAYBACK)) -+ s_mixers[KMIXPA_APP_PLAYBACK]->removeWidget(index); -+ } else { -+ pa_operation *o; -+ if (!(o = pa_context_get_sink_input_info(c, index, sink_input_cb, NULL))) { -+ kWarning(67100) << "pa_context_get_sink_input_info() failed"; -+ return; -+ } -+ pa_operation_unref(o); -+ } -+ break; -+ -+ case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT: -+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { -+ if (s_mixers.contains(KMIXPA_APP_CAPTURE)) -+ s_mixers[KMIXPA_APP_CAPTURE]->removeWidget(index); -+ } else { -+ pa_operation *o; -+ if (!(o = pa_context_get_source_output_info(c, index, source_output_cb, NULL))) { -+ kWarning(67100) << "pa_context_get_sink_input_info() failed"; -+ return; -+ } -+ pa_operation_unref(o); -+ } -+ break; -+ -+ case PA_SUBSCRIPTION_EVENT_CLIENT: -+ if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { -+ clients.remove(index); -+ } else { -+ pa_operation *o; -+ if (!(o = pa_context_get_client_info(c, index, client_cb, NULL))) { -+ kWarning(67100) << "pa_context_get_client_info() failed"; -+ return; -+ } -+ pa_operation_unref(o); -+ } -+ break; -+ -+ } -+} -+ -+ -+static void context_state_callback(pa_context *c, void *) -+{ -+ pa_context_state_t state = pa_context_get_state(c); -+ if (state == PA_CONTEXT_READY) { -+ // Attempt to load things up -+ pa_operation *o; -+ -+ // 1. Register for the stream changes (except during probe) -+ if (s_context == c) { -+ pa_context_set_subscribe_callback(c, subscribe_cb, NULL); -+ -+ if (!(o = pa_context_subscribe(c, (pa_subscription_mask_t) -+ (PA_SUBSCRIPTION_MASK_SINK| -+ PA_SUBSCRIPTION_MASK_SOURCE| -+ PA_SUBSCRIPTION_MASK_CLIENT| -+ PA_SUBSCRIPTION_MASK_SINK_INPUT| -+ PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT), NULL, NULL))) { -+ kWarning(67100) << "pa_context_subscribe() failed"; -+ return; -+ } -+ pa_operation_unref(o); -+ } -+ -+ if (!(o = pa_context_get_sink_info_list(c, sink_cb, NULL))) { -+ kWarning(67100) << "pa_context_get_sink_info_list() failed"; -+ return; -+ } -+ pa_operation_unref(o); -+ s_outstandingRequests++; -+ -+ if (!(o = pa_context_get_source_info_list(c, source_cb, NULL))) { -+ kWarning(67100) << "pa_context_get_source_info_list() failed"; -+ return; -+ } -+ pa_operation_unref(o); -+ s_outstandingRequests++; -+ -+ -+ if (!(o = pa_context_get_client_info_list(c, client_cb, NULL))) { -+ kWarning(67100) << "pa_context_client_info_list() failed"; -+ return; -+ } -+ pa_operation_unref(o); -+ s_outstandingRequests++; -+ -+ if (!(o = pa_context_get_sink_input_info_list(c, sink_input_cb, NULL))) { -+ kWarning(67100) << "pa_context_get_sink_input_info_list() failed"; -+ return; -+ } -+ pa_operation_unref(o); -+ s_outstandingRequests++; -+ -+ if (!(o = pa_context_get_source_output_info_list(c, source_output_cb, NULL))) { -+ kWarning(67100) << "pa_context_get_source_output_info_list() failed"; -+ return; -+ } -+ pa_operation_unref(o); -+ s_outstandingRequests++; -+ -+ /* These calls are not always supported */ -+ if ((o = pa_ext_stream_restore_read(c, ext_stream_restore_read_cb, NULL))) { -+ pa_operation_unref(o); -+ s_outstandingRequests++; -+ -+ pa_ext_stream_restore_set_subscribe_cb(c, ext_stream_restore_subscribe_cb, NULL); -+ -+ if ((o = pa_ext_stream_restore_subscribe(c, 1, NULL, NULL))) -+ pa_operation_unref(o); -+ } else { -+ kWarning(67100) << "Failed to initialize stream_restore extension: " << pa_strerror(pa_context_errno(s_context)); -+ } -+ } else if (!PA_CONTEXT_IS_GOOD(state)) { -+ // If this is our probe phase, exit our context immediately -+ if (s_context != c) { -+ pa_context_disconnect(c); -+ } else { -+ // If we're not probing, it means we've been disconnected from our -+ // glib context -+ pa_context_unref(s_context); -+ s_context = NULL; -+ -+ // Remove all GUI elements -+ TQMap<int,Mixer_PULSE*>::iterator it; -+ for (it = s_mixers.begin(); it != s_mixers.end(); ++it) { -+ (*it)->removeAllWidgets(); -+ } -+ // This one is not handled above. -+ clients.clear(); -+ -+ if (s_mixers.contains(KMIXPA_PLAYBACK)) { -+ kWarning(67100) << "Connection to PulseAudio daemon closed. Attempting reconnection."; -+ s_pulseActive = UNKNOWN; -+ TQTimer::singleShot(50, s_mixers[KMIXPA_PLAYBACK], SLOT(reinit())); -+ } -+ } -+ } -+} -+ -+static void setVolumeFromPulse(Volume& volume, const devinfo& dev) -+{ -+ chanIDMap::const_iterator iter; -+ for (iter = dev.chanIDs.begin(); iter != dev.chanIDs.end(); ++iter) -+ { -+ //kDebug(67100) << "Setting volume for channel " << iter.value() << " to " << (long)dev.volume.values[iter.key()] << " (" << ((100*(long)dev.volume.values[iter.key()]) / PA_VOLUME_NORM) << "%)"; -+ volume.setVolume(iter.value(), (long)dev.volume.values[iter.key()]); -+ } -+} -+ -+static pa_cvolume genVolumeForPulse(const devinfo& dev, Volume& volume) -+{ -+ pa_cvolume cvol = dev.volume; -+ -+ chanIDMap::const_iterator iter; -+ for (iter = dev.chanIDs.begin(); iter != dev.chanIDs.end(); ++iter) -+ { -+ cvol.values[iter.key()] = (uint32_t)volume.getVolume(iter.value()); -+ //kDebug(67100) << "Setting volume for channel " << iter.value() << " to " << cvol.values[iter.key()] << " (" << ((100*cvol.values[iter.key()]) / PA_VOLUME_NORM) << "%)"; -+ } -+ return cvol; -+} -+ -+static devmap* get_widget_map(int type, TQString id = "") -+{ -+ TQ_ASSERT(type >= 0 && type <= KMIXPA_WIDGET_MAX); -+ -+ if (KMIXPA_PLAYBACK == type) -+ return &outputDevices; -+ else if (KMIXPA_CAPTURE == type) -+ return &captureDevices; -+ else if (KMIXPA_APP_PLAYBACK == type) { -+ if (id.startsWith("restore:")) -+ return &outputRoles; -+ return &outputStreams; -+ } else if (KMIXPA_APP_CAPTURE == type) -+ return &captureStreams; -+ -+ TQ_ASSERT(0); -+ return NULL; -+} -+static devmap* get_widget_map(int type, int index) -+{ -+ if (PA_INVALID_INDEX == (uint32_t)index) -+ return get_widget_map(type, "restore:"); -+ return get_widget_map(type); -+} -+ -+void Mixer_PULSE::emitControlsReconfigured() -+{ -+ ControlManager::instance().announce(_mixer->id(), ControlChangeType::ControlList, getDriverName()); -+} -+ -+void Mixer_PULSE::addWidget(int index, bool isAppStream) -+{ -+ devmap* map = get_widget_map(m_devnum, index); -+ -+ if (!map->contains(index)) { -+ kWarning(67100) << "New " << m_devnum << " widget notified for index " << index << " but I cannot find it in my list :s"; -+ return; -+ } -+ addDevice((*map)[index], isAppStream); -+ emitControlsReconfigured(); -+} -+ -+void Mixer_PULSE::removeWidget(int index) -+{ -+ devmap* map = get_widget_map(m_devnum); -+ -+ if (!map->contains(index)) { -+ //kWarning(67100) << "Removing " << m_devnum << " widget notified for index " << index << " but I cannot find it in my list :s"; -+ // Sometimes we ignore things (e.g. event sounds) so don't be too noisy here. -+ return; -+ } -+ -+ TQString id = (*map)[index].name; -+ map->remove(index); -+ -+ // We need to find the MixDevice that goes with this widget and remove it. -+ MixSet::iterator iter; -+ for (iter = m_mixDevices.begin(); iter != m_mixDevices.end(); ++iter) -+ { -+ if ((*iter)->id() == id) -+ { -+ shared_ptr<MixDevice> md = m_mixDevices.get(id); -+ kDebug() << "MixDevice 1 useCount=" << md.use_count(); -+ md->close(); -+ kDebug() << "MixDevice 2 useCount=" << md.use_count(); -+ -+ m_mixDevices.erase(iter); -+ kDebug() << "MixDevice 3 useCount=" << md.use_count(); -+ emitControlsReconfigured(); -+ kDebug() << "MixDevice 4 useCount=" << md.use_count(); -+ return; -+ } -+ } -+} -+ -+void Mixer_PULSE::removeAllWidgets() -+{ -+ devmap* map = get_widget_map(m_devnum); -+ map->clear(); -+ -+ // Special case -+ if (KMIXPA_APP_PLAYBACK == m_devnum) -+ outputRoles.clear(); -+ -+ freeMixDevices(); -+ emitControlsReconfigured(); -+} -+ -+void Mixer_PULSE::addDevice(devinfo& dev, bool isAppStream) -+{ -+ if (dev.chanMask != Volume::MNONE) { -+ MixSet *ms = 0; -+ if (m_devnum == KMIXPA_APP_PLAYBACK && s_mixers.contains(KMIXPA_PLAYBACK)) -+ ms = s_mixers[KMIXPA_PLAYBACK]->getMixSet(); -+ else if (m_devnum == KMIXPA_APP_CAPTURE && s_mixers.contains(KMIXPA_CAPTURE)) -+ ms = s_mixers[KMIXPA_CAPTURE]->getMixSet(); -+ -+ int maxVol = GlobalConfig::instance().volumeOverdrive ? PA_VOLUME_UI_MAX : PA_VOLUME_NORM; -+ Volume v(maxVol, PA_VOLUME_MUTED, true, false); -+ v.addVolumeChannels(dev.chanMask); -+ setVolumeFromPulse(v, dev); -+ MixDevice* md = new MixDevice( _mixer, dev.name, dev.description, dev.icon_name, ms); -+ if (isAppStream) -+ md->setApplicationStream(true); -+ -+ kDebug() << "Adding Pulse volume " << dev.name << ", isCapture= " << (m_devnum == KMIXPA_CAPTURE || m_devnum == KMIXPA_APP_CAPTURE) << ", isAppStream= " << isAppStream << "=" << md->isApplicationStream() << ", devnum=" << m_devnum; -+ md->addPlaybackVolume(v); -+ md->setMuted(dev.mute); -+ m_mixDevices.append(md->addToPool()); -+ } -+} -+ -+Mixer_Backend* PULSE_getMixer( Mixer *mixer, int devnum ) -+{ -+ Mixer_Backend *l_mixer; -+ l_mixer = new Mixer_PULSE( mixer, devnum ); -+ return l_mixer; -+} -+ -+bool Mixer_PULSE::connectToDaemon() -+{ -+ TQ_ASSERT(NULL == s_context); -+ -+ kDebug(67100) << "Attempting connection to PulseAudio sound daemon"; -+ pa_mainloop_api *api = pa_glib_mainloop_get_api(s_mainloop); -+ TQ_ASSERT(api); -+ -+ s_context = pa_context_new(api, "KMix"); -+ TQ_ASSERT(s_context); -+ -+ if (pa_context_connect(s_context, NULL, PA_CONTEXT_NOFAIL, 0) < 0) { -+ pa_context_unref(s_context); -+ s_context = NULL; -+ return false; -+ } -+ pa_context_set_state_callback(s_context, &context_state_callback, NULL); -+ return true; -+} -+ -+ -+Mixer_PULSE::Mixer_PULSE(Mixer *mixer, int devnum) : Mixer_Backend(mixer, devnum) -+{ -+ if ( devnum == -1 ) -+ m_devnum = 0; -+ -+ TQString pulseenv = qgetenv("KMIX_PULSEAUDIO_DISABLE"); -+ if (pulseenv.toInt()) -+ s_pulseActive = INACTIVE; -+ -+ // We require a glib event loop -+ if (!QByteArray(QAbstractEventDispatcher::instance()->metaObject()->className()).contains("EventDispatcherGlib")) { -+ kDebug(67100) << "Disabling PulseAudio integration for lack of GLib event loop"; -+ s_pulseActive = INACTIVE; -+ } -+ -+ -+ ++refcount; -+ if (INACTIVE != s_pulseActive && 1 == refcount) -+ { -+ // First of all conenct to PA via simple/blocking means and if that succeeds, -+ // use a fully async integrated mainloop method to connect and get proper support. -+ pa_mainloop *p_test_mainloop; -+ if (!(p_test_mainloop = pa_mainloop_new())) { -+ kDebug(67100) << "PulseAudio support disabled: Unable to create mainloop"; -+ s_pulseActive = INACTIVE; -+ goto endconstruct; -+ } -+ -+ pa_context *p_test_context; -+ if (!(p_test_context = pa_context_new(pa_mainloop_get_api(p_test_mainloop), "kmix-probe"))) { -+ kDebug(67100) << "PulseAudio support disabled: Unable to create context"; -+ pa_mainloop_free(p_test_mainloop); -+ s_pulseActive = INACTIVE; -+ goto endconstruct; -+ } -+ -+ kDebug(67100) << "Probing for PulseAudio..."; -+ // (cg) Convert to PA_CONTEXT_NOFLAGS when PulseAudio 0.9.19 is required -+ if (pa_context_connect(p_test_context, NULL, static_cast<pa_context_flags_t>(0), NULL) < 0) { -+ kDebug(67100) << TQString("PulseAudio support disabled: %1").arg(pa_strerror(pa_context_errno(p_test_context))); -+ pa_context_disconnect(p_test_context); -+ pa_context_unref(p_test_context); -+ pa_mainloop_free(p_test_mainloop); -+ s_pulseActive = INACTIVE; -+ goto endconstruct; -+ } -+ -+ // Assume we are inactive, it will be set to active if appropriate -+ s_pulseActive = INACTIVE; -+ pa_context_set_state_callback(p_test_context, &context_state_callback, NULL); -+ for (;;) { -+ pa_mainloop_iterate(p_test_mainloop, 1, NULL); -+ -+ if (!PA_CONTEXT_IS_GOOD(pa_context_get_state(p_test_context))) { -+ kDebug(67100) << "PulseAudio probe complete."; -+ break; -+ } -+ } -+ pa_context_disconnect(p_test_context); -+ pa_context_unref(p_test_context); -+ pa_mainloop_free(p_test_mainloop); -+ -+ -+ if (INACTIVE != s_pulseActive) -+ { -+ // Reconnect via integrated mainloop -+ s_mainloop = pa_glib_mainloop_new(NULL); -+ TQ_ASSERT(s_mainloop); -+ -+ connectToDaemon(); -+ -+#if defined(HAVE_CANBERRA) -+ int ret = ca_context_create(&s_ccontext); -+ if (ret < 0) { -+ kDebug(67100) << "Disabling Sound Feedback. Canberra context failed."; -+ s_ccontext = NULL; -+ } else -+ ca_context_set_driver(s_ccontext, "pulse"); -+#endif -+ } -+ -+ kDebug(67100) << "PulseAudio status: " << (s_pulseActive==UNKNOWN ? "Unknown (bug)" : (s_pulseActive==ACTIVE ? "Active" : "Inactive")); -+ } -+ -+endconstruct: -+ s_mixers[m_devnum] = this; -+} -+ -+Mixer_PULSE::~Mixer_PULSE() -+{ -+ s_mixers.remove(m_devnum); -+ -+ if (refcount > 0) -+ { -+ --refcount; -+ if (0 == refcount) -+ { -+#if defined(HAVE_CANBERRA) -+ if (s_ccontext) { -+ ca_context_destroy(s_ccontext); -+ s_ccontext = NULL; -+ } -+#endif -+ -+ if (s_context) { -+ pa_context_unref(s_context); -+ s_context = NULL; -+ } -+ -+ if (s_mainloop) { -+ pa_glib_mainloop_free(s_mainloop); -+ s_mainloop = NULL; -+ } -+ } -+ } -+ -+ closeCommon(); -+} -+ -+int Mixer_PULSE::open() -+{ -+ //kDebug(67100) << "Trying Pulse sink"; -+ -+ if (ACTIVE == s_pulseActive && m_devnum <= KMIXPA_APP_CAPTURE) -+ { -+ // Make sure the GUI layers know we are dynamic so as to always paint us -+ _mixer->setDynamic(); -+ -+ devmap::iterator iter; -+ if (KMIXPA_PLAYBACK == m_devnum) -+ { -+ _id = "Playback Devices"; -+ m_mixerName = i18n("Playback Devices"); -+ for (iter = outputDevices.begin(); iter != outputDevices.end(); ++iter) -+ addDevice(*iter); -+ } -+ else if (KMIXPA_CAPTURE == m_devnum) -+ { -+ _id = "Capture Devices"; -+ m_mixerName = i18n("Capture Devices"); -+ for (iter = captureDevices.begin(); iter != captureDevices.end(); ++iter) -+ addDevice(*iter); -+ } -+ else if (KMIXPA_APP_PLAYBACK == m_devnum) -+ { -+ _id = "Playback Streams"; -+ m_mixerName = i18n("Playback Streams"); -+ for (iter = outputRoles.begin(); iter != outputRoles.end(); ++iter) -+ addDevice(*iter, true); -+ for (iter = outputStreams.begin(); iter != outputStreams.end(); ++iter) -+ addDevice(*iter, true); -+ } -+ else if (KMIXPA_APP_CAPTURE == m_devnum) -+ { -+ _id = "Capture Streams"; -+ m_mixerName = i18n("Capture Streams"); -+ for (iter = captureStreams.begin(); iter != captureStreams.end(); ++iter) -+ addDevice(*iter); -+ } -+ -+ kDebug(67100) << "Using PulseAudio for mixer: " << m_mixerName; -+ m_isOpen = true; -+ } -+ -+ return 0; -+} -+ -+int Mixer_PULSE::close() -+{ -+ closeCommon(); -+ return 1; -+} -+ -+int Mixer_PULSE::id2num(const TQString& id) { -+ //kDebug(67100) << "id2num() id=" << id; -+ int num = -1; -+ // todo: Store this in a hash or similar -+ int i; -+ for (i = 0; i < m_mixDevices.size(); ++i) { -+ if (m_mixDevices[i]->id() == id) { -+ num = i; -+ break; -+ } -+ } -+ //kDebug(67100) << "id2num() num=" << num; -+ return num; -+} -+ -+int Mixer_PULSE::readVolumeFromHW( const TQString& id, shared_ptr<MixDevice> md ) -+{ -+ devmap *map = get_widget_map(m_devnum, id); -+ -+ devmap::iterator iter; -+ for (iter = map->begin(); iter != map->end(); ++iter) -+ { -+ if (iter->name == id) -+ { -+ setVolumeFromPulse(md->playbackVolume(), *iter); -+ md->setMuted(iter->mute); -+ break; -+ } -+ } -+ -+ return 0; -+} -+ -+int Mixer_PULSE::writeVolumeToHW( const TQString& id, shared_ptr<MixDevice> md ) -+{ -+ devmap::iterator iter; -+ if (KMIXPA_PLAYBACK == m_devnum) -+ { -+ for (iter = outputDevices.begin(); iter != outputDevices.end(); ++iter) -+ { -+ if (iter->name == id) -+ { -+ pa_operation *o; -+ -+ pa_cvolume volume = genVolumeForPulse(*iter, md->playbackVolume()); -+ if (!(o = pa_context_set_sink_volume_by_index(s_context, iter->index, &volume, NULL, NULL))) { -+ kWarning(67100) << "pa_context_set_sink_volume_by_index() failed"; -+ return Mixer::ERR_READ; -+ } -+ pa_operation_unref(o); -+ -+ if (!(o = pa_context_set_sink_mute_by_index(s_context, iter->index, (md->isMuted() ? 1 : 0), NULL, NULL))) { -+ kWarning(67100) << "pa_context_set_sink_mute_by_index() failed"; -+ return Mixer::ERR_READ; -+ } -+ pa_operation_unref(o); -+ -+#if defined(HAVE_CANBERRA) -+ if (s_ccontext && Mixer::getBeepOnVolumeChange() ) { -+ int playing = 0; -+ int cindex = 2; // Note "2" is simply the index we've picked. It's somewhat irrelevant. -+ -+ -+ ca_context_playing(s_ccontext, cindex, &playing); -+ -+ // NB Depending on how this is desired to work, we may want to simply -+ // skip playing, or cancel the currently playing sound and play our -+ // new one... for now, let's do the latter. -+ if (playing) { -+ ca_context_cancel(s_ccontext, cindex); -+ playing = 0; -+ } -+ -+ if (!playing) { -+ char dev[64]; -+ -+ snprintf(dev, sizeof(dev), "%lu", (unsigned long) iter->index); -+ ca_context_change_device(s_ccontext, dev); -+ -+ // Ideally we'd use something like ca_gtk_play_for_widget()... -+ ca_context_play( -+ s_ccontext, -+ cindex, -+ CA_PROP_EVENT_DESCRIPTION, i18n("Volume Control Feedback Sound").toUtf8().constData(), -+ CA_PROP_EVENT_ID, "audio-volume-change", -+ CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", -+ CA_PROP_CANBERRA_ENABLE, "1", -+ NULL -+ ); -+ -+ ca_context_change_device(s_ccontext, NULL); -+ } -+ } -+#endif -+ -+ return 0; -+ } -+ } -+ } -+ else if (KMIXPA_CAPTURE == m_devnum) -+ { -+ for (iter = captureDevices.begin(); iter != captureDevices.end(); ++iter) -+ { -+ if (iter->name == id) -+ { -+ pa_operation *o; -+ -+ pa_cvolume volume = genVolumeForPulse(*iter, md->playbackVolume()); -+ if (!(o = pa_context_set_source_volume_by_index(s_context, iter->index, &volume, NULL, NULL))) { -+ kWarning(67100) << "pa_context_set_source_volume_by_index() failed"; -+ return Mixer::ERR_READ; -+ } -+ pa_operation_unref(o); -+ -+ if (!(o = pa_context_set_source_mute_by_index(s_context, iter->index, (md->isMuted() ? 1 : 0), NULL, NULL))) { -+ kWarning(67100) << "pa_context_set_source_mute_by_index() failed"; -+ return Mixer::ERR_READ; -+ } -+ pa_operation_unref(o); -+ -+ return 0; -+ } -+ } -+ } -+ else if (KMIXPA_APP_PLAYBACK == m_devnum) -+ { -+ if (id.startsWith("stream:")) -+ { -+ for (iter = outputStreams.begin(); iter != outputStreams.end(); ++iter) -+ { -+ if (iter->name == id) -+ { -+ pa_operation *o; -+ -+ pa_cvolume volume = genVolumeForPulse(*iter, md->playbackVolume()); -+ if (!(o = pa_context_set_sink_input_volume(s_context, iter->index, &volume, NULL, NULL))) { -+ kWarning(67100) << "pa_context_set_sink_input_volume() failed"; -+ return Mixer::ERR_READ; -+ } -+ pa_operation_unref(o); -+ -+ if (!(o = pa_context_set_sink_input_mute(s_context, iter->index, (md->isMuted() ? 1 : 0), NULL, NULL))) { -+ kWarning(67100) << "pa_context_set_sink_input_mute() failed"; -+ return Mixer::ERR_READ; -+ } -+ pa_operation_unref(o); -+ -+ return 0; -+ } -+ } -+ } -+ else if (id.startsWith("restore:")) -+ { -+ for (iter = outputRoles.begin(); iter != outputRoles.end(); ++iter) -+ { -+ if (iter->name == id) -+ { -+ restoreRule &rule = s_RestoreRules[iter->stream_restore_rule]; -+ pa_ext_stream_restore_info info; -+ info.name = iter->stream_restore_rule.toUtf8().constData(); -+ info.channel_map = rule.channel_map; -+ info.volume = genVolumeForPulse(*iter, md->playbackVolume()); -+ info.device = rule.device.isEmpty() ? NULL : rule.device.toUtf8().constData(); -+ info.mute = (md->isMuted() ? 1 : 0); -+ -+ pa_operation* o; -+ if (!(o = pa_ext_stream_restore_write(s_context, PA_UPDATE_REPLACE, &info, 1, true, NULL, NULL))) { -+ kWarning(67100) << "pa_ext_stream_restore_write() failed" << info.channel_map.channels << info.volume.channels << info.name; -+ return Mixer::ERR_READ; -+ } -+ pa_operation_unref(o); -+ -+ return 0; -+ } -+ } -+ } -+ } -+ else if (KMIXPA_APP_CAPTURE == m_devnum) -+ { -+ for (iter = captureStreams.begin(); iter != captureStreams.end(); ++iter) -+ { -+ if (iter->name == id) -+ { -+ pa_operation *o; -+ -+#if HAVE_SOURCE_OUTPUT_VOLUMES -+ pa_cvolume volume = genVolumeForPulse(*iter, md->playbackVolume()); -+ if (!(o = pa_context_set_source_output_volume(s_context, iter->index, &volume, NULL, NULL))) { -+ kWarning(67100) << "pa_context_set_source_output_volume_by_index() failed"; -+ return Mixer::ERR_READ; -+ } -+ pa_operation_unref(o); -+ -+ if (!(o = pa_context_set_source_output_mute(s_context, iter->index, (md->isMuted() ? 1 : 0), NULL, NULL))) { -+ kWarning(67100) << "pa_context_set_source_output_mute_by_index() failed"; -+ return Mixer::ERR_READ; -+ } -+ pa_operation_unref(o); -+#else -+ // NB Note that this is different from APP_PLAYBACK in that we set the volume on the source itself. -+ pa_cvolume volume = genVolumeForPulse(*iter, md->playbackVolume()); -+ if (!(o = pa_context_set_source_volume_by_index(s_context, iter->device_index, &volume, NULL, NULL))) { -+ kWarning(67100) << "pa_context_set_source_volume_by_index() failed"; -+ return Mixer::ERR_READ; -+ } -+ pa_operation_unref(o); -+ -+ if (!(o = pa_context_set_source_mute_by_index(s_context, iter->device_index, (md->isMuted() ? 1 : 0), NULL, NULL))) { -+ kWarning(67100) << "pa_context_set_source_mute_by_index() failed"; -+ return Mixer::ERR_READ; -+ } -+ pa_operation_unref(o); -+#endif -+ -+ return 0; -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+/** -+* Move the stream to a new destination -+*/ -+bool Mixer_PULSE::moveStream( const TQString& id, const TQString& destId ) { -+ TQ_ASSERT(KMIXPA_APP_PLAYBACK == m_devnum || KMIXPA_APP_CAPTURE == m_devnum); -+ -+ kDebug(67100) << "Mixer_PULSE::moveStream(): Move Stream Requested - Stream: " << id << ", Destination: " << destId; -+ -+ // Lookup the stream index. -+ uint32_t stream_index = PA_INVALID_INDEX; -+ TQString stream_restore_rule = ""; -+ devmap::iterator iter; -+ devmap *map = get_widget_map(m_devnum); -+ for (iter = map->begin(); iter != map->end(); ++iter) { -+ if (iter->name == id) { -+ stream_index = iter->index; -+ stream_restore_rule = iter->stream_restore_rule; -+ break; -+ } -+ } -+ -+ if (PA_INVALID_INDEX == stream_index) { -+ kError(67100) << "Mixer_PULSE::moveStream(): Cannot find stream index"; -+ return false; -+ } -+ -+ if (destId.isEmpty()) { -+ // We want to remove any specific device in the stream restore rule. -+ if (stream_restore_rule.isEmpty() || !s_RestoreRules.contains(stream_restore_rule)) { -+ kWarning(67100) << "Mixer_PULSE::moveStream(): Trying to set Automatic on a stream with no rule"; -+ } else { -+ restoreRule &rule = s_RestoreRules[stream_restore_rule]; -+ pa_ext_stream_restore_info info; -+ info.name = stream_restore_rule.toUtf8().constData(); -+ info.channel_map = rule.channel_map; -+ info.volume = rule.volume; -+ info.device = NULL; -+ info.mute = rule.mute ? 1 : 0; -+ -+ pa_operation* o; -+ if (!(o = pa_ext_stream_restore_write(s_context, PA_UPDATE_REPLACE, &info, 1, true, NULL, NULL))) { -+ kWarning(67100) << "pa_ext_stream_restore_write() failed" << info.channel_map.channels << info.volume.channels << info.name; -+ return Mixer::ERR_READ; -+ } -+ pa_operation_unref(o); -+ } -+ } else { -+ pa_operation* o; -+ if (KMIXPA_APP_PLAYBACK == m_devnum) { -+ if (!(o = pa_context_move_sink_input_by_name(s_context, stream_index, destId.toUtf8().constData(), NULL, NULL))) { -+ kWarning(67100) << "pa_context_move_sink_input_by_name() failed"; -+ return false; -+ } -+ } else { -+ if (!(o = pa_context_move_source_output_by_name(s_context, stream_index, destId.toUtf8().constData(), NULL, NULL))) { -+ kWarning(67100) << "pa_context_move_source_output_by_name() failed"; -+ return false; -+ } -+ } -+ pa_operation_unref(o); -+ } -+ -+ return true; -+} -+ -+void Mixer_PULSE::reinit() -+{ -+ // We only support reinit on our primary mixer. -+ TQ_ASSERT(KMIXPA_PLAYBACK == m_devnum); -+ connectToDaemon(); -+} -+ -+void Mixer_PULSE::triggerUpdate() -+{ -+ readSetFromHWforceUpdate(); -+ readSetFromHW(); -+} -+ -+// Please see KMixWindow::initActionsAfterInitMixer(), it uses the driverName -+ -+TQString PULSE_getDriverName() { -+ return "PulseAudio"; -+} -+ -+TQString Mixer_PULSE::getDriverName() -+{ -+ return "PulseAudio"; -+} -+ -diff -Nuar ./kmix-tde-ori/mixer_pulse.h ./kmix/mixer_pulse.h ---- ./kmix-tde-ori/mixer_pulse.h 1970-01-01 01:00:00.000000000 +0100 -+++ ./kmix/mixer_pulse.h 2013-05-21 23:27:28.960083350 +0200 -@@ -0,0 +1,85 @@ -+/* -+ * KMix -- KDE's full featured mini mixer -+ * -+ * -+ * Copyright (C) 2008 Helio Chissini de Castro <helio@kde.org> -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Library General Public -+ * License as published by the Free Software Foundation; either -+ * version 2 of the License, or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Library General Public License for more details. -+ * -+ * You should have received a copy of the GNU Library General Public -+ * License along with this program; if not, write to the Free -+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -+ */ -+ -+#ifndef MIXER_PULSE_H -+#define MIXER_PULSE_H -+ -+#include <tqstring.h> -+ -+#include "mixer_backend.h" -+#include <pulse/pulseaudio.h> -+ -+typedef TQMap<uint8_t,Volume::ChannelID> chanIDMap; -+typedef struct { -+ int index; -+ int device_index; -+ TQString name; -+ TQString description; -+ TQString icon_name; -+ pa_cvolume volume; -+ pa_channel_map channel_map; -+ bool mute; -+ TQString stream_restore_rule; -+ -+ Volume::ChannelMask chanMask; -+ chanIDMap chanIDs; -+} devinfo; -+ -+class Mixer_PULSE : public Mixer_Backend -+{ -+ public: -+ Mixer_PULSE(Mixer *mixer, int devnum); -+ virtual ~Mixer_PULSE(); -+ -+ virtual int readVolumeFromHW( const TQString& id, shared_ptr<MixDevice> ); -+ virtual int writeVolumeToHW ( const TQString& id, shared_ptr<MixDevice> ); -+ -+ virtual bool moveStream( const TQString& id, const TQString& destId ); -+ -+ virtual TQString getDriverName(); -+ virtual TQString getId() const { return _id; }; -+ -+ virtual bool needsPolling() { return false; } -+ -+ void triggerUpdate(); -+ void addWidget(int index, bool = false); -+ void removeWidget(int index); -+ void removeAllWidgets(); -+ MixSet *getMixSet() { return &m_mixDevices; } -+ int id2num(const TQString& id); -+ -+ protected: -+ virtual int open(); -+ virtual int close(); -+ -+ int fd; -+ TQString _id; -+ -+ private: -+ void addDevice(devinfo& dev, bool = false); -+ bool connectToDaemon(); -+ void emitControlsReconfigured(); -+public: -+ void reinit(); -+ -+}; -+ -+#endif |