rpms/kdemultimedia/devel kmix_pa-20100108.patch, NONE, 1.1 kdemultimedia.spec, 1.166, 1.167

Rex Dieter rdieter at fedoraproject.org
Wed Jan 6 15:25:43 UTC 2010


Author: rdieter

Update of /cvs/pkgs/rpms/kdemultimedia/devel
In directory cvs1.fedora.phx.redhat.com:/tmp/cvs-serv2655

Modified Files:
	kdemultimedia.spec 
Added Files:
	kmix_pa-20100108.patch 
Log Message:
* Wed Jan 08 2010 Rex Dieter <rdieter at fedoraproject.org> - 4.3.85-2
- (re)enable kmix/pa support, with patches from coling/mandriva
- tighten lib deps with %{?_isa}
- deprecate Obsoletes: dragonplayer


kmix_pa-20100108.patch:
 kmix-platforms.cpp |    8 
 kmix.cpp           |   21 -
 kmixerwidget.cpp   |    1 
 mixer.cpp          |    6 
 mixer.h            |    2 
 mixer_alsa9.cpp    |    4 
 mixer_backend.h    |    1 
 mixer_pulse.cpp    | 1078 ++++++++++++++++++++++++++++++++++++++++++++---------
 mixer_pulse.h      |   55 +-
 viewbase.cpp       |    6 
 viewbase.h         |    1 
 11 files changed, 980 insertions(+), 203 deletions(-)

--- NEW FILE kmix_pa-20100108.patch ---
diff --git a/kmix/kmix-platforms.cpp b/kmix/kmix-platforms.cpp
index f7b8c9a..5f61989 100644
--- a/kmix/kmix-platforms.cpp
+++ b/kmix/kmix-platforms.cpp
@@ -129,6 +129,10 @@ MixerFactory g_mixerFactories[] = {
     { IRIX_getMixer, IRIX_getDriverName },
 #endif
 
+#if defined(PULSE_MIXER)
+    { PULSE_getMixer, PULSE_getDriverName },
+#endif
+
 #if defined(ALSA_MIXER)
     { ALSA_getMixer, ALSA_getDriverName },
 #endif
@@ -145,10 +149,6 @@ MixerFactory g_mixerFactories[] = {
     { HPUX_getMixer, HPUX_getDriverName },
 #endif
 
-#if defined(PULSE_MIXER)
-    { PULSE_getMixer, PULSE_getDriverName },
-#endif
-
     { 0, 0 }
 };
 
diff --git a/kmix/kmix.cpp b/kmix/kmix.cpp
index 30cc5d4..b1d0743 100644
--- a/kmix/kmix.cpp
+++ b/kmix/kmix.cpp
@@ -49,6 +49,7 @@
 #include <ktoggleaction.h>
 
 // KMix
+#include "guiprofile.h"
 #include "mixertoolbox.h"
 #include "kmix.h"
 #include "kmixdevicemanager.h"
@@ -399,11 +400,25 @@ void KMixWindow::recreateGUIwithoutSavingView()
  */
 void KMixWindow::recreateGUI(bool saveConfig)
 {
-   saveViewConfig();  // save the state before recreating
+   // Find out which of the tabs is currently selected for restoration
+   int current_tab = -1;
+   if (m_wsMixers)
+      current_tab = m_wsMixers->currentIndex();
+
+   if (saveConfig)
+      saveViewConfig();  // save the state before recreating
+
+   // Before clearing the mixer widgets, we must increase the refcount on the guiprof to save it deleting the ViewBase object.
+   if ( Mixer::mixers().count() > 0 )
+      for (int i=0; i<Mixer::mixers().count(); ++i)
+         MixerToolBox::instance()->selectProfile((Mixer::mixers())[i])->increaseRefcount();
    clearMixerWidgets();
+
    if ( Mixer::mixers().count() > 0 ) {
       for (int i=0; i<Mixer::mixers().count(); ++i) {
          Mixer *mixer = (Mixer::mixers())[i];
+         // We've increased the refcount before clearing, so remember and decrease it again.
+         MixerToolBox::instance()->selectProfile(mixer)->decreaseRefcount();
          addMixerWidget(mixer->id());
       }
       bool dockingSucceded = updateDocking();
@@ -415,6 +430,10 @@ void KMixWindow::recreateGUI(bool saveConfig)
        updateDocking();  // -<- removes the DockIcon
        hide();
    }
+
+   if (current_tab >= 0) {
+      m_wsMixers->setCurrentIndex(current_tab);
+   }
 }
 
 
diff --git a/kmix/kmixerwidget.cpp b/kmix/kmixerwidget.cpp
index b7fdb2a..ad126b6 100644
--- a/kmix/kmixerwidget.cpp
+++ b/kmix/kmixerwidget.cpp
@@ -92,6 +92,7 @@ void KMixerWidget::createLayout(ViewBase::ViewFlags vflags)
    // delete old objects
    if( m_balanceSlider ) {
       delete m_balanceSlider;
+      m_balanceSlider = 0;
    }
    if( m_topLayout ) {
       delete m_topLayout;
diff --git a/kmix/mixer.cpp b/kmix/mixer.cpp
index 82bfba4..caf056e 100644
--- a/kmix/mixer.cpp
+++ b/kmix/mixer.cpp
@@ -150,6 +150,7 @@ bool Mixer::openIfValid() {
             setLocalMasterMD(noMaster); // no master
         }
         connect( _mixerBackend, SIGNAL(controlChanged()), SLOT(controlChangedForwarder()) );
+        connect( _mixerBackend, SIGNAL(controlsReconfigured(int)), SLOT(controlsReconfiguredForwarder(int)) );
         
         m_dbusName = "/Mixer" + QString::number(_mixerBackend->m_devnum);
         QDBusConnection::sessionBus().registerObject(m_dbusName, this);
@@ -163,6 +164,11 @@ void Mixer::controlChangedForwarder()
     emit controlChanged();
 }
 
+void Mixer::controlsReconfiguredForwarder(int mixerTabIndex)
+{
+    emit controlsReconfigured(mixerTabIndex);
+}
+
 /**
  * Closes the mixer.
  * Also, stops the polling timer.
diff --git a/kmix/mixer.h b/kmix/mixer.h
index e0cdf4d..e193b16 100644
--- a/kmix/mixer.h
+++ b/kmix/mixer.h
@@ -166,6 +166,7 @@ class Mixer : public QObject
    signals:
       void newBalance( Volume& );
       void controlChanged(void);
+      void controlsReconfigured(int mixerTabIndex);
 
    protected:
       int m_balance; // from -100 (just left) to 100 (just right)
@@ -173,6 +174,7 @@ class Mixer : public QObject
 
    private slots:
       void controlChangedForwarder();
+      void controlsReconfiguredForwarder(int mixerTabIndex);
 
    public:
       static QList<Mixer *>& mixers();
diff --git a/kmix/mixer_alsa9.cpp b/kmix/mixer_alsa9.cpp
index 5f12bdc..1799535 100644
--- a/kmix/mixer_alsa9.cpp
+++ b/kmix/mixer_alsa9.cpp
@@ -420,10 +420,10 @@ Volume* Mixer_ALSA::addVolume(snd_mixer_elem_t *elem, bool capture)
     }
 
 
-    // Chek if this control has at least one volume control
+    // Check if this control has at least one volume control
     bool hasVolume = (chn != Volume::MNONE);
 
-    // Chek if a appropriate switch is present (appropriate means, based o nthe "capture" parameer)
+    // Check if a appropriate switch is present (appropriate means, based o nthe "capture" parameer)
     bool hasCommonSwitch = snd_mixer_selem_has_common_switch ( elem );
 
     bool hasSwitch = hasCommonSwitch |
diff --git a/kmix/mixer_backend.h b/kmix/mixer_backend.h
index 26e9557..3a7b5d7 100644
--- a/kmix/mixer_backend.h
+++ b/kmix/mixer_backend.h
@@ -131,6 +131,7 @@ protected:
 
 signals:
   void controlChanged( void );
+  void controlsReconfigured( int mixerTabIndex );
 
 protected slots:
   virtual void readSetFromHW();
diff --git a/kmix/mixer_pulse.cpp b/kmix/mixer_pulse.cpp
index 694b9a9..89080a5 100644
--- a/kmix/mixer_pulse.cpp
+++ b/kmix/mixer_pulse.cpp
@@ -20,12 +20,658 @@
  */
 
 #include <cstdlib>
+#include <QEventLoop>
 
 #include "mixer_pulse.h"
 #include "mixer.h"
 
+#include <pulse/glib-mainloop.h>
+#include <pulse/ext-stream-restore.h>
+
+
+#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
+
+static unsigned int refcount = 0;
 static pa_context *context = NULL;
 static pa_glib_mainloop *mainloop = NULL;
+static QEventLoop *s_connectionEventloop = NULL;
+static enum { UNKNOWN, ACTIVE, INACTIVE } s_pulseActive = UNKNOWN;
+static int s_OutstandingRequests = 0;
+
+QMap<int,Mixer_PULSE*> s_Mixers;
+
+typedef QMap<int,devinfo> devmap;
+static devmap outputDevices;
+static devmap captureDevices;
+static QMap<int,QString> clients;
+static devmap outputStreams;
+static devmap captureStreams;
+static devmap outputRoles;
+
+static void dec_outstanding() {
+    if (s_OutstandingRequests <= 0)
+        return;
+
+    if (--s_OutstandingRequests <= 0)
+    {
+        s_pulseActive = ACTIVE;
+        if (s_connectionEventloop) {
+            s_connectionEventloop->exit(0);
+            s_connectionEventloop = NULL;
+
+            // If we have no devices then we consider PA to be 'INACTIVE'
+            if (outputDevices.isEmpty() && captureDevices.isEmpty())
+                s_pulseActive = INACTIVE;
+            else
+                s_pulseActive = ACTIVE;
+        }
+    }
+}
+
+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_FRONT_LEFT_OF_CENTER:
+                    dev.chanMask = (Volume::ChannelMask)( dev.chanMask | Volume::MREARSIDELEFT);
+                    dev.chanIDs[i] = Volume::REARSIDELEFT;
+                    break;
+                case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
+                    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 void sink_cb(pa_context *c, const pa_sink_info *i, int eol, void *) {
+
+    Q_ASSERT(c == context);
+
+    if (eol < 0) {
+        if (pa_context_errno(c) == PA_ERR_NOENTITY)
+            return;
+
+        kWarning(67100) << "Sink callback failure";
+        return;
+    }
+
+    if (eol > 0) {
+        dec_outstanding();
+        if (s_Mixers.contains(KMIXPA_PLAYBACK))
+            s_Mixers[KMIXPA_PLAYBACK]->triggerUpdate();
+        return;
+    }
+
+    devinfo s;
+    s.index = s.device_index = i->index;
+    s.restore.name = i->name;
+    s.restore.device = "";
+    s.name = QString(i->name).replace(' ', '_');
+    s.description = i->description;
+    s.volume = i->volume;
+    s.channel_map = i->channel_map;
+    s.mute = !!i->mute;
+
+    translateMasksAndMaps(s);
+
+    bool is_new = !outputDevices.contains(s.index);
+    outputDevices[s.index] = s;
+    kDebug(67100) << "Got some info about sink: " << s.description;
+
+    if (is_new && s_Mixers.contains(KMIXPA_PLAYBACK))
+        s_Mixers[KMIXPA_PLAYBACK]->addWidget(s.index);
+}
+
+static void source_cb(pa_context *c, const pa_source_info *i, int eol, void *) {
+
+    Q_ASSERT(c == context);
+
+    if (eol < 0) {
+        if (pa_context_errno(c) == PA_ERR_NOENTITY)
+            return;
+
+        kWarning(67100) << "Source callback failure";
+        return;
+    }
+
+    if (eol > 0) {
+        dec_outstanding();
+        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.restore.name = i->name;
+    s.restore.device = "";
+    s.name = QString(i->name).replace(' ', '_');
+    s.description = i->description;
+    s.volume = i->volume;
+    s.channel_map = i->channel_map;
+    s.mute = !!i->mute;
+
+    translateMasksAndMaps(s);
+
+    bool is_new = !captureDevices.contains(s.index);
+    captureDevices[s.index] = s;
+    kDebug(67100) << "Got some info about source: " << s.description;
+
+    if (is_new && s_Mixers.contains(KMIXPA_CAPTURE))
+        s_Mixers[KMIXPA_CAPTURE]->addWidget(s.index);
+}
+
+static void client_cb(pa_context *c, const pa_client_info *i, int eol, void *) {
+
+    Q_ASSERT(c == context);
+
+    if (eol < 0) {
+        if (pa_context_errno(c) == PA_ERR_NOENTITY)
+            return;
+
+        kWarning(67100) << "Client callback failure";
+        return;
+    }
+
+    if (eol > 0) {
+        dec_outstanding();
+        return;
+    }
+
+    clients[i->index] = i->name;
+    kDebug(67100) << "Got some info about client: " << i->name;
+}
+
+static void sink_input_cb(pa_context *c, const pa_sink_input_info *i, int eol, void *) {
+
+    Q_ASSERT(c == context);
+
+    if (eol < 0) {
+        if (pa_context_errno(c) == PA_ERR_NOENTITY)
+            return;
+
+        kWarning(67100) << "Sink Input callback failure";
+        return;
+    }
+
+    if (eol > 0) {
+        dec_outstanding();
+        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, "sink-input-by-media-role:event") == 0) {
+            kWarning(67100) << "Ignoring sink-input due to it being designated as an event and thus handled by the Event slider";
+            return;
+        }
+    }
+
+    QString prefix = QString("%1: ").arg(i18n("Unknown Application"));
+    if (clients.contains(i->client))
+        prefix = QString("%1: ").arg(clients[i->client]);
+
+    devinfo s;
+    s.index = i->index;
+    s.device_index = i->sink;
+    s.restore.name = i->name;
+    s.restore.device = "";
+    s.description = prefix + i->name;
+    s.name = QString("stream:") + QString(s.description).replace(' ', '_');
+    s.volume = i->volume;
+    s.channel_map = i->channel_map;
+    s.mute = !!i->mute;
+
+    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 (is_new && s_Mixers.contains(KMIXPA_APP_PLAYBACK))
+        s_Mixers[KMIXPA_APP_PLAYBACK]->addWidget(s.index);
+}
+
+static void source_output_cb(pa_context *c, const pa_source_output_info *i, int eol, void *) {
+
+    Q_ASSERT(c == context);
+
+    if (eol < 0) {
+        if (pa_context_errno(c) == PA_ERR_NOENTITY)
+            return;
+
+        kWarning(67100) << "Source Output callback failure";
+        return;
+    }
+
+    if (eol > 0) {
+        dec_outstanding();
+        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)) {
+        kWarning(67100) << "Source Output refers to a Source we don't have any info for :s";
+        return;
+    }
+
+    QString prefix = QString("%1: ").arg(i18n("Unknown Application"));
+    if (clients.contains(i->client))
+        prefix = QString("%1: ").arg(clients[i->client]);
+
+    devinfo s;
+    s.index = i->index;
+    s.device_index = i->source;
+    s.restore.name = i->name;
+    s.restore.device = "";
+    s.description = prefix + i->name;
+    s.name = QString("stream:") + QString(s.description).replace(' ', '_');
+    //s.volume = i->volume;
+    s.volume = captureDevices[i->source].volume;
+    s.channel_map = i->channel_map;
+    //s.mute = !!i->mute;
+    s.mute = captureDevices[i->source].mute;
+
+    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 (is_new && s_Mixers.contains(KMIXPA_APP_CAPTURE))
+        s_Mixers[KMIXPA_APP_CAPTURE]->addWidget(s.index);
+}
+
+
+void ext_stream_restore_read_cb(pa_context *c, const pa_ext_stream_restore_info *i, int eol, void *) {
+
+    Q_ASSERT(c == context);
+
+    if (eol < 0) {
+        dec_outstanding();
+        kWarning(67100) << "Failed to initialize stream_restore extension: " << pa_strerror(pa_context_errno(context));
+        return;
+    }
+
+    if (eol > 0) {
+        dec_outstanding();
+        if (s_Mixers.contains(KMIXPA_APP_PLAYBACK))
+            s_Mixers[KMIXPA_APP_PLAYBACK]->triggerUpdate();
+        return;
+    }
+
+    // We only want to know about Sound Events for now...
+    if (strcmp(i->name, "sink-input-by-media-role:event") != 0)
+        return;
+
+    // Fake a Mono Device/Volume
+    pa_cvolume volume;
+    volume.channels = 1;
+    volume.values[0] = pa_cvolume_max(&i->volume);
+    pa_channel_map channel_map;
+    channel_map.channels = 1;
+    channel_map.map[0] = PA_CHANNEL_POSITION_MONO;
+
+    devinfo s;
+    s.index = s.device_index = PA_INVALID_INDEX;
+    s.restore.name = i->name;
+    s.restore.device = i->device;
+    s.description = i18n("Event Sounds");
+    s.name = QString("restore:") + i->name;
+    s.volume = volume;
+    s.channel_map = channel_map;
+    s.mute = !!i->mute;
+
+    translateMasksAndMaps(s);
+
+    outputRoles[s.index] = s;
+    kDebug(67100) << "Got some info about restore rule: " << s.description;
+}
+
+static void ext_stream_restore_subscribe_cb(pa_context *c, void *) {
+
+    Q_ASSERT(c == 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 *) {
+
+    Q_ASSERT(c == 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 *)
+{
+    Q_ASSERT(c == context);
+
+    switch (pa_context_get_state(c)) {
+        case PA_CONTEXT_UNCONNECTED:
+        case PA_CONTEXT_CONNECTING:
+        case PA_CONTEXT_AUTHORIZING:
+        case PA_CONTEXT_SETTING_NAME:
+            break;
+
+        case PA_CONTEXT_READY:
+            // Attempt to load things up
+            pa_operation *o;
+
+            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(context));
+
+            break;
+
+        case PA_CONTEXT_FAILED:
+            s_pulseActive = INACTIVE;
+            if (s_connectionEventloop) {
+                s_connectionEventloop->exit(0);
+                s_connectionEventloop = NULL;
+            }
+            break;
+
+        case PA_CONTEXT_TERMINATED:
+        default:
+            s_pulseActive = INACTIVE;
+            /// @todo Deal with reconnection...
+            break;
+    }
+}
+
+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)
+{
+    Q_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)
+        return &outputStreams;
+    else if (KMIXPA_APP_CAPTURE == type)
+        return &captureStreams;
+
+    Q_ASSERT(0);
+    return NULL;
+}
+
+void Mixer_PULSE::addWidget(int index)
+{
+    devmap* map = get_widget_map(m_devnum);
+    bool capture = (KMIXPA_CAPTURE == m_devnum || KMIXPA_APP_CAPTURE == m_devnum);
+
+    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], capture);
+    emit controlsReconfigured(m_devnum);
+}
+
+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;
+    }
+
+    QString 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)
+        {
+            delete *iter;
+            m_mixDevices.erase(iter);
+            emit controlsReconfigured(m_devnum);
+            return;
+        }
+    }
+}
+
+void Mixer_PULSE::addDevice(devinfo& dev, bool capture)
+{
+    if (dev.chanMask != Volume::MNONE) {
+        Volume v(dev.chanMask, PA_VOLUME_NORM, PA_VOLUME_MUTED, false, capture);
+        setVolumeFromPulse(v, dev);
+        MixDevice* md = new MixDevice( _mixer, dev.name, dev.description);
+        if (capture)
+            md->addCaptureVolume(v);
+        else
+            md->addPlaybackVolume(v);
+        md->setMuted(dev.mute);
+        m_mixDevices.append(md);
+    }
+}
 
 Mixer_Backend* PULSE_getMixer( Mixer *mixer, int devnum )
 {
@@ -39,203 +685,284 @@ Mixer_PULSE::Mixer_PULSE(Mixer *mixer, int devnum) : Mixer_Backend(mixer, devnum
 {
    if ( devnum == -1 )
       m_devnum = 0;
+
+   QString pulseenv = qgetenv("KMIX_PULSEAUDIO_DISABLE");
+   if (pulseenv.toInt())
+       s_pulseActive = INACTIVE;
+
+   ++refcount;
+   if (INACTIVE != s_pulseActive && 1 == refcount)
+   {
+       mainloop = pa_glib_mainloop_new(g_main_context_default());
+       g_assert(mainloop);
+
+       pa_mainloop_api *api = pa_glib_mainloop_get_api(mainloop);
+       g_assert(api);
+
+       context = pa_context_new(api, "KMix KDE 4");
+       g_assert(context);
+
+       // We create a simple event loop to allow the glib loop
+       // to iterate until we've connected or not to the server.
+       s_connectionEventloop = new QEventLoop;
+
+       // (cg) Convert to PA_CONTEXT_NOFLAGS when PulseAudio 0.9.19 is required
+       if (pa_context_connect(context, NULL, static_cast<pa_context_flags_t>(0), 0) >= 0) {
+           pa_context_set_state_callback(context, &context_state_callback, s_connectionEventloop);
+           // Now we block until we connect or otherwise...
+           s_connectionEventloop->exec();
+       }
+   }
+
+   s_Mixers[m_devnum] = this;
 }
 
 Mixer_PULSE::~Mixer_PULSE()
 {
-   close();
+    s_Mixers.remove(m_devnum);
+
+    if (refcount > 0)
+    {
+        --refcount;
+        if (0 == refcount)
+        {
+            if (context) {
+                pa_context_unref(context);
+                context = NULL;
+            }
+
+            if (mainloop) {
+                pa_glib_mainloop_free(mainloop);
+                mainloop = NULL;
+            }
+        }
+    }
 }
 
 int Mixer_PULSE::open()
 {
-    kDebug(67100) <<  "Trying Pulse sink";
-    mainloop = pa_glib_mainloop_new(g_main_context_default());
-    g_assert(mainloop);
-    pa_mainloop_api *api = pa_glib_mainloop_get_api(mainloop);
-    g_assert(api);
-
-    context = pa_context_new(api, "KMix KDE 4");
-    g_assert(context);
-      //return Mixer::ERR_OPEN;
- 
-/* 
-      //
-      // Mixer is open. Now define all of the mix devices.
-      //
-
-         for ( int idx = 0; idx < numDevs; idx++ )
-         {
-            Volume vol( 2, AUDIO_MAX_GAIN );
-            QString id;
-            id.setNum(idx);
-            MixDevice* md = new MixDevice( _mixer, id,
-               QString(MixerDevNames[idx]), MixerChannelTypes[idx]);
-            md->addPlaybackVolume(vol);
-            md->setRecSource( isRecsrcHW( idx ) );
-            m_mixDevices.append( md );
-         }
-*/
-
-    m_mixerName = "PULSE Audio Mixer";
-
-    m_isOpen = true;
+    //kDebug(67100) <<  "Trying Pulse sink";
 
+    if (ACTIVE == s_pulseActive && m_devnum <= KMIXPA_APP_CAPTURE)
+    {
+        devmap::iterator iter;
+        if (KMIXPA_PLAYBACK == m_devnum)
+        {
+            m_mixerName = i18n("Playback Devices");
+            for (iter = outputDevices.begin(); iter != outputDevices.end(); ++iter)
+                addDevice(*iter, false);
+        }
+        else if (KMIXPA_CAPTURE == m_devnum)
+        {
+            m_mixerName = i18n("Capture Devices");
+            for (iter = captureDevices.begin(); iter != captureDevices.end(); ++iter)
+                addDevice(*iter, true);
+        }
+        else if (KMIXPA_APP_PLAYBACK == m_devnum)
+        {
+            m_mixerName = i18n("Playback Streams");
+            for (iter = outputRoles.begin(); iter != outputRoles.end(); ++iter)
+                addDevice(*iter, false);
+            for (iter = outputStreams.begin(); iter != outputStreams.end(); ++iter)
+                addDevice(*iter, false);
+        }
+        else if (KMIXPA_APP_CAPTURE == m_devnum)
+        {
+            m_mixerName = i18n("Capture Streams");
+            for (iter = captureStreams.begin(); iter != captureStreams.end(); ++iter)
+                addDevice(*iter, true);
+        }
+
+        kDebug(67100) <<  "Using PulseAudio for mixer: " << m_mixerName;
+        m_isOpen = true;
+    }
+ 
     return 0;
 }
 
 int Mixer_PULSE::close()
 {
-    if (context)
-    {
-        pa_context_unref(context);
-        context = NULL;
-    }
-    if (mainloop)
-    {
-        pa_glib_mainloop_free(mainloop);
-        mainloop = NULL;
-    }
     return 1;
 }
 
 int Mixer_PULSE::readVolumeFromHW( const QString& id, MixDevice *md )
 {
-/*   audio_info_t audioinfo;
-   uint_t devMask = MixerSunPortMasks[devnum];
-
-   Volume& volume = md->playbackVolume();
-   int devnum = id2num(id);
-   //
-   // Read the current audio information from the driver
-   //
-   if ( ioctl( fd, AUDIO_GETINFO, &audioinfo ) < 0 )
-   {
-      return( Mixer::ERR_READ );
-   }
-   else
-   {
-      //
-      // Extract the appropriate fields based on the requested device
-      //
-      switch ( devnum )
-      {
-         case MIXERDEV_MASTER_VOLUME :
-            volume.setSwitchActivated( audioinfo.output_muted );
-            GainBalanceToVolume( audioinfo.play.gain,
-                                 audioinfo.play.balance,
-                                 volume );
-            break;
-
-         case MIXERDEV_RECORD_MONITOR :
-            md->setMuted(false);
-            volume.setAllVolumes( audioinfo.monitor_gain );
-            break;
-
-         case MIXERDEV_INTERNAL_SPEAKER :
-         case MIXERDEV_HEADPHONE :
-         case MIXERDEV_LINE_OUT :
-            md->setMuted( (audioinfo.play.port & devMask) ? false : true );
-            GainBalanceToVolume( audioinfo.play.gain,
-                                 audioinfo.play.balance,
-                                 volume );
-            break;
+    devmap *map = NULL;
+    Volume *vol = NULL;
+
+    if (KMIXPA_PLAYBACK == m_devnum) {
+        map = &outputDevices;
+        vol = &md->playbackVolume();
+    } else if (KMIXPA_CAPTURE == m_devnum) {
+        map = &captureDevices;
+        vol = &md->captureVolume();
+    } else if (KMIXPA_APP_PLAYBACK == m_devnum) {
+        if (id.startsWith("stream:"))
+            map = &outputStreams;
+        else if (id.startsWith("restore:"))
+            map = &outputRoles;
+        vol = &md->playbackVolume();
+    } else if (KMIXPA_APP_CAPTURE == m_devnum) {
+        map = &captureStreams;
+        vol = &md->captureVolume();
+    }
 
-         case MIXERDEV_MICROPHONE :
-         case MIXERDEV_LINE_IN :
-         case MIXERDEV_CD :
-            md->setMuted( (audioinfo.record.port & devMask) ? false : true );
-            GainBalanceToVolume( audioinfo.record.gain,
-                                 audioinfo.record.balance,
-                                 volume );
+    Q_ASSERT(map);
+    Q_ASSERT(vol);
+ 
+    devmap::iterator iter;
+    for (iter = map->begin(); iter != map->end(); ++iter)
+    {
+        if (iter->name == id)
+        {
+            setVolumeFromPulse(*vol, *iter);
+            md->setMuted(iter->mute);
             break;
+        }
+    }
 
-         default :
-            return Mixer::ERR_READ;
-      }
-      return 0;
-   }*/
-   return 0;
+    return 0;
 }
 
 int Mixer_PULSE::writeVolumeToHW( const QString& id, MixDevice *md )
 {
-/*   uint_t gain;
-   uchar_t balance;
-   uchar_t mute;
-
-   Volume& volume = md->playbackVolume();
-   int devnum = id2num(id);
-   //
-   // Convert the Volume(left vol, right vol) to the Gain/Balance Sun uses
-   //
-   VolumeToGainBalance( volume, gain, balance );
-   mute = md->isMuted() ? 1 : 0;
-
-   //
-   // Read the current audio settings from the hardware
-   //
-   audio_info_t audioinfo;
-   if ( ioctl( fd, AUDIO_GETINFO, &audioinfo ) < 0 )
-   {
-      return( Mixer::ERR_READ );
-   }
+    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(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(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);
+
+                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->captureVolume());
+                if (!(o = pa_context_set_source_volume_by_index(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(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(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(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)
+                {
+                    pa_ext_stream_restore_info info;
+                    info.name = iter->restore.name.toLatin1().constData();
+                    info.channel_map = iter->channel_map;
+                    info.volume = genVolumeForPulse(*iter, md->playbackVolume());
+                    info.device = iter->restore.device.isEmpty() ? NULL : iter->restore.device.toLatin1().constData();
+                    info.mute = (md->isMuted() ? 1 : 0);
+
+                    pa_operation* o;
+                    if (!(o = pa_ext_stream_restore_write(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;
+
+                // 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->captureVolume());
+                if (!(o = pa_context_set_source_volume_by_index(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(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);
+
+                return 0;
+            }
+        }
+    }
 
-   //
-   // Now, based on the devnum that we are writing to, update the appropriate
-   // volume field and twiddle the appropriate bitmask to enable/mute the
-   // device as necessary.
-   //
-   switch ( devnum )
-   {
-      case MIXERDEV_MASTER_VOLUME :
-         audioinfo.play.gain = gain;
-         audioinfo.play.balance = balance;
-         audioinfo.output_muted = mute;
-         break;
-
-      case MIXERDEV_RECORD_MONITOR :
-         audioinfo.monitor_gain = gain;
-         // no mute or balance for record monitor
-         break;
-
-      case MIXERDEV_INTERNAL_SPEAKER :
-      case MIXERDEV_HEADPHONE :
-      case MIXERDEV_LINE_OUT :
-         audioinfo.play.gain = gain;
-         audioinfo.play.balance = balance;
-         if ( mute )
-            audioinfo.play.port &= ~MixerSunPortMasks[devnum];
-         else
-            audioinfo.play.port |= MixerSunPortMasks[devnum];
-         break;
-
-      case MIXERDEV_MICROPHONE :
-      case MIXERDEV_LINE_IN :
-      case MIXERDEV_CD :
-         audioinfo.record.gain = gain;
-         audioinfo.record.balance = balance;
-         if ( mute )
-            audioinfo.record.port &= ~MixerSunPortMasks[devnum];
-         else
-            audioinfo.record.port |= MixerSunPortMasks[devnum];
-         break;
-
-      default :
-         return Mixer::ERR_READ;
-   }
+    return 0;
+}
 
-   //
-   // Now that we've updated the audioinfo struct, write it back to the hardware
-   //
-   if ( ioctl( fd, AUDIO_SETINFO, &audioinfo ) < 0 )
-   {
-      return( Mixer::ERR_WRITE );
-   }
-   else
-   {
-      return 0;
-   }*/
-   return 0;
+void Mixer_PULSE::triggerUpdate()
+{
+    readSetFromHWforceUpdate();
+    readSetFromHW();
 }
 
 void Mixer_PULSE::setRecsrcHW( const QString& /*id*/, bool /* on */ )
@@ -243,19 +970,8 @@ void Mixer_PULSE::setRecsrcHW( const QString& /*id*/, bool /* on */ )
    return;
 }
 
-bool Mixer_PULSE::isRecsrcHW( const QString& id )
+bool Mixer_PULSE::isRecsrcHW( const QString& /*id*/ )
 {
-/*   int devnum = id2num(id);
-   switch ( devnum )
-   {
-      case MIXERDEV_MICROPHONE :
-      case MIXERDEV_LINE_IN :
-      case MIXERDEV_CD :
-         return true;
-
-      default :
-         return false;
-   }*/
    return false;
 }
 
diff --git a/kmix/mixer_pulse.h b/kmix/mixer_pulse.h
index 6dcd68b..1760a72 100644
--- a/kmix/mixer_pulse.h
+++ b/kmix/mixer_pulse.h
@@ -24,30 +24,55 @@
 
 #include <QString>
 
+#include "mixer_backend.h"
 #include <pulse/pulseaudio.h>
-#include <pulse/glib-mainloop.h>
-#include <pulse/ext-stream-restore.h>
 
-#include "mixer_backend.h"
+typedef QMap<uint8_t,Volume::ChannelID> chanIDMap;
+typedef struct {
+    int index;
+    int device_index;
+    QString name;
+    QString description;
+    pa_cvolume volume;
+    pa_channel_map channel_map;
+    bool mute;
+
+    struct {
+        QString name;
+        QString device;
+    } restore;
+
+    Volume::ChannelMask chanMask;
+    chanIDMap chanIDs;
+} devinfo;
 
 class Mixer_PULSE : public Mixer_Backend
 {
-public:
-  Mixer_PULSE(Mixer *mixer, int devnum);
-  virtual ~Mixer_PULSE();
+    public:
+        Mixer_PULSE(Mixer *mixer, int devnum);
+        virtual ~Mixer_PULSE();
+
+        virtual int readVolumeFromHW( const QString& id, MixDevice *md  );
+        virtual int writeVolumeToHW ( const QString& id, MixDevice *md  );
+        void setRecsrcHW              ( const QString& id, bool on );
+        bool isRecsrcHW               ( const QString& id );
+
+        virtual QString getDriverName();
+        virtual bool needsPolling() { return false; }
+
+        void triggerUpdate();
+        void addWidget(int index);
+        void removeWidget(int index);
 
-  virtual int readVolumeFromHW( const QString& id, MixDevice *md  );
-  virtual int writeVolumeToHW ( const QString& id, MixDevice *md  );
-  void setRecsrcHW              ( const QString& id, bool on );
-  bool isRecsrcHW               ( const QString& id );
+    protected:
+        virtual int open();
+        virtual int close();
 
-  virtual QString getDriverName();
+        int fd;
 
-protected:
-  virtual int open();
-  virtual int close();
+    private:
+        void addDevice(devinfo& dev, bool capture);
 
-  int fd;
 };
 
 #endif 
diff --git a/kmix/viewbase.cpp b/kmix/viewbase.cpp
index b53ecd4..4ab41da 100644
--- a/kmix/viewbase.cpp
+++ b/kmix/viewbase.cpp
@@ -76,6 +76,7 @@ ViewBase::ViewBase(QWidget* parent, const char* id, Mixer* mixer, Qt::WFlags f,
    action->setText(i18n("&Channels"));
    connect(action, SIGNAL(triggered(bool) ), SLOT(configureView()));
    connect ( _mixer, SIGNAL(controlChanged()), this, SLOT(refreshVolumeLevels()) );
+   connect ( _mixer, SIGNAL(controlsReconfigured(int)), this, SLOT(controlsReconfigured(int)) );
 }
 
 ViewBase::~ViewBase() {
@@ -203,6 +204,11 @@ void ViewBase::showContextMenu()
     _popMenu->popup( pos );
 }
 
+void ViewBase::controlsReconfigured(int mixerTabIndex)
+{
+    Q_UNUSED(mixerTabIndex);
+    emit rebuildGUI();
+}
 
 void ViewBase::refreshVolumeLevels()
 {
diff --git a/kmix/viewbase.h b/kmix/viewbase.h
index 0470f18..35cccfc 100644
--- a/kmix/viewbase.h
+++ b/kmix/viewbase.h
@@ -130,6 +130,7 @@ protected:
     GUIProfile* _guiprof;
    KActionCollection *_localActionColletion;
 public slots:
+   virtual void controlsReconfigured(int mixerTabIndex);
    virtual void refreshVolumeLevels();
    virtual void configureView(); 
    void toggleMenuBarSlot();


Index: kdemultimedia.spec
===================================================================
RCS file: /cvs/pkgs/rpms/kdemultimedia/devel/kdemultimedia.spec,v
retrieving revision 1.166
retrieving revision 1.167
diff -u -p -r1.166 -r1.167
--- kdemultimedia.spec	18 Dec 2009 19:46:07 -0000	1.166
+++ kdemultimedia.spec	6 Jan 2010 15:25:43 -0000	1.167
@@ -2,7 +2,7 @@
 Name:    kdemultimedia
 Epoch:   6
 Version: 4.3.85
-Release: 1%{?dist}
+Release: 2%{?dist}
 Summary: KDE Multimedia applications
 
 Group:   Applications/Multimedia
@@ -14,6 +14,9 @@ BuildRoot: %{_tmppath}/%{name}-%{version
 
 Patch1: kdemultimedia-4.3.75-nomplayerthumbs.patch
 Patch2: kdemultimedia-4.3.75-kscd_doc.patch
+# git clone git://colin.guthr.ie/kdemultimedia
+# git diff master..remotes/origin/pulse > kmix_pa-<date>.patch
+Patch3: kmix_pa-20100108.patch
 
 ## upstream patches
 
@@ -27,9 +30,7 @@ BuildRequires: glib2-devel
 BuildRequires: kdebase-workspace-devel >= %{version}
 BuildRequires: libtheora-devel
 BuildRequires: libvorbis-devel
-# KMix PulseAudio integration is not anywhere near shippable.
-# Almost everything is commented out, it basically does nothing.
-# BuildRequires: pulseaudio-libs-devel
+BuildRequires: pulseaudio-libs-devel
 BuildRequires: taglib-devel
 %if 0%{?fedora}
 BuildRequires: xine-lib-devel libxcb-devel
@@ -37,12 +38,14 @@ BuildRequires: xine-lib-devel libxcb-dev
 BuildRequires: libtunepimp-devel libmusicbrainz-devel
 %endif
 
-Requires: %{name}-libs = %{?epoch:%{epoch}:}%{version}-%{release}
-Requires: kdelibs4 >= %{version}
+Requires: %{name}-libs%{?isa} = %{?epoch:%{epoch}:}%{version}-%{release}
+Requires: kdelibs4%{?_isa} >= %{version}
 Requires: kdebase-workspace >= %{version}
 
 Provides: dragonplayer = 2.0.2-0.1
+%if 0%{?fedora} && 0%{?fedora} < 11
 Obsoletes: dragonplayer < 2.0.2-0.1
+%endif
 
 Obsoletes: %{name}-extras < %{?epoch:%{epoch}:}%{version}-%{release}
 
@@ -60,8 +63,9 @@ License: LGPLv2+ and GPLv2+
 Summary: Runtime libraries for %{name}
 Group:   System Environment/Libraries
 Obsoletes: %{name}-extras-libs < %{?epoch:%{epoch}:}%{version}-%{release}
+%if 0%{?fedora} && 0%{?fedora} < 11
 Conflicts: dragonplayer < 2.0.2-0.1
-
+%endif
 %description libs
 %{summary}.
 
@@ -69,7 +73,7 @@ Conflicts: dragonplayer < 2.0.2-0.1
 Group:    Development/Libraries
 Summary:  Developer files for %{name}
 License:  LGPLv2+ and GPLv2+
-Requires: %{name}-libs = %{?epoch:%{epoch}:}%{version}-%{release}
+Requires: %{name}-libs%{?_isa} = %{?epoch:%{epoch}:}%{version}-%{release}
 Requires: kdelibs4-devel
 %description devel
 %{summary}.
@@ -80,6 +84,7 @@ Requires: kdelibs4-devel
 
 %patch1 -p1 -b .nomplayerthumbs
 %patch2 -p1 -b .kscd_doc
+%patch3 -p1 -b .kmix_pa
 
 %build
 
@@ -171,6 +176,11 @@ fi
 
 
 %changelog
+* Wed Jan 08 2010 Rex Dieter <rdieter at fedoraproject.org> - 4.3.85-2
+- (re)enable kmix/pa support, with patches from coling/mandriva
+- tighten lib deps with %%{?_isa}
+- deprecate Obsoletes: dragonplayer
+
 * Fri Dec 18 2009 Rex Dieter <rdieter at fedoraproject.org> - 4.3.85-1
 - kde-4.3.85 (4.4beta2)
 




More information about the fedora-extras-commits mailing list