Sophie

Sophie

distrib > Fedora > 13 > i386 > media > updates-src > by-pkgid > b9facf0654340569c1cf73ee9355fe33 > files > 6

kdebase-runtime-4.5.5-1.fc13.src.rpm

Index: runtime/phonon/kcm/testspeakerwidget.cpp
===================================================================
--- runtime/phonon/kcm/testspeakerwidget.cpp	(revision 0)
+++ runtime/phonon/kcm/testspeakerwidget.cpp	(revision 1154776)
@@ -0,0 +1,194 @@
+/*  This file is part of the KDE project
+    Copyright (C) 2010 Colin Guthrie <cguthrie@mandriva.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2
+    as published by the Free Software Foundation.
+
+    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 General Public License for more details.
+
+    You should have received a copy of the GNU 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 "testspeakerwidget.h"
+#include "speakersetup.h"
+#include <stdio.h>
+
+TestSpeakerWidget::TestSpeakerWidget(const pa_channel_position_t pos, ca_context *canberra, SpeakerSetup* ss)
+  : KPushButton(KIcon("preferences-desktop-sound"), "Test", ss)
+  , m_Ss(ss)
+  , m_Pos(pos)
+  , m_Canberra(canberra)
+{
+    setText(_positionName());
+    connect(this, SIGNAL(clicked()), SLOT(clicked()));
+}
+
+void TestSpeakerWidget::clicked()
+{
+    uint32_t sink_index = m_Ss->getCurrentSinkIndex();
+    char dev[64];
+    snprintf(dev, sizeof(dev), "%lu", (unsigned long) sink_index);
+    ca_context_change_device(m_Canberra, dev);
+
+    const char* sound_name = _positionSoundName();
+    ca_proplist* proplist;
+    ca_proplist_create(&proplist);
+    
+    ca_proplist_sets(proplist, CA_PROP_MEDIA_ROLE, "test");
+    ca_proplist_sets(proplist, CA_PROP_MEDIA_NAME, _positionName().toAscii().constData());
+    ca_proplist_sets(proplist, CA_PROP_CANBERRA_FORCE_CHANNEL, _positionAsString());
+    ca_proplist_sets(proplist, CA_PROP_CANBERRA_ENABLE, "1");
+
+    ca_proplist_sets(proplist, CA_PROP_EVENT_ID, sound_name);
+    if (ca_context_play_full(m_Canberra, 0, proplist, NULL, NULL) < 0) {
+        // Try a different sound name.
+        ca_proplist_sets(proplist, CA_PROP_EVENT_ID, "audio-test-signal");
+        if (ca_context_play_full(m_Canberra, 0, proplist, NULL, NULL) < 0) {
+            // Finaly try this... if this doesn't work, then stuff it.
+            ca_proplist_sets(proplist, CA_PROP_EVENT_ID, "bell-window-system");
+        }
+    }
+
+    ca_context_change_device(m_Canberra, NULL);
+    ca_proplist_destroy(proplist);
+}
+
+const char* TestSpeakerWidget::_positionAsString()
+{
+    switch (m_Pos)
+    {
+        case PA_CHANNEL_POSITION_FRONT_LEFT:
+            return "front-left";
+
+        case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
+            return "front-left-of-center";
+
+        case PA_CHANNEL_POSITION_FRONT_CENTER:
+            return "front-center";
+
+        case PA_CHANNEL_POSITION_MONO:
+            return "mono";
+
+        case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
+            return "front-right-of-center";
+
+        case PA_CHANNEL_POSITION_FRONT_RIGHT:
+            return "front-right";
+
+        case PA_CHANNEL_POSITION_SIDE_LEFT:
+            return "side-left";
+
+        case PA_CHANNEL_POSITION_SIDE_RIGHT:
+            return "side-right";
+
+        case PA_CHANNEL_POSITION_REAR_LEFT:
+            return "rear-left";
+
+        case PA_CHANNEL_POSITION_REAR_CENTER:
+            return "rear-center";
+
+        case PA_CHANNEL_POSITION_REAR_RIGHT:
+            return "rear-right";
+
+        case PA_CHANNEL_POSITION_LFE:
+            return "lfe";
+
+        default:
+            break;
+    }
+    return "invalid";
+}
+
+QString TestSpeakerWidget::_positionName()
+{
+    switch (m_Pos)
+    {
+        case PA_CHANNEL_POSITION_FRONT_LEFT:
+            return i18n("Front Left");
+
+        case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
+            return i18n("Front Left of Center");
+
+        case PA_CHANNEL_POSITION_FRONT_CENTER:
+            return i18n("Front Center");
+
+        case PA_CHANNEL_POSITION_MONO:
+            return i18n("Mono");
+
+        case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
+            return i18n("Front Right of Center");
+
+        case PA_CHANNEL_POSITION_FRONT_RIGHT:
+            return i18n("Front Right");
+
+        case PA_CHANNEL_POSITION_SIDE_LEFT:
+            return i18n("Side Left");
+
+        case PA_CHANNEL_POSITION_SIDE_RIGHT:
+            return i18n("Side Right");
+
+        case PA_CHANNEL_POSITION_REAR_LEFT:
+            return i18n("Rear Left");
+
+        case PA_CHANNEL_POSITION_REAR_CENTER:
+            return i18n("Rear Center");
+
+        case PA_CHANNEL_POSITION_REAR_RIGHT:
+            return i18n("Rear Right");
+
+        case PA_CHANNEL_POSITION_LFE:
+            return i18n("Subwoofer");
+
+        default:
+            break;
+    }
+    return i18n("Unknown Channel");
+}
+
+const char* TestSpeakerWidget::_positionSoundName()
+{
+    switch (m_Pos)
+    {
+        case PA_CHANNEL_POSITION_FRONT_LEFT:
+            return "audio-channel-front-left";
+
+        case PA_CHANNEL_POSITION_FRONT_RIGHT:
+            return "audio-channel-front-right";
+
+        case PA_CHANNEL_POSITION_FRONT_CENTER:
+            return "audio-channel-front-center";
+
+        case PA_CHANNEL_POSITION_REAR_LEFT:
+            return "audio-channel-rear-left";
+
+        case PA_CHANNEL_POSITION_REAR_RIGHT:
+            return "audio-channel-rear-right";
+
+        case PA_CHANNEL_POSITION_REAR_CENTER:
+            return "audio-channel-rear-center";
+
+        case PA_CHANNEL_POSITION_LFE:
+            return "audio-channel-lfe";
+
+        case PA_CHANNEL_POSITION_SIDE_LEFT:
+            return "audio-channel-side-left";
+
+        case PA_CHANNEL_POSITION_SIDE_RIGHT:
+            return "audio-channel-side-right";
+        default:
+            break;
+    }
+    return NULL;
+}
+
+
+#include "testspeakerwidget.moc"
+// vim: sw=4 sts=4 et tw=100

Property changes on: runtime/phonon/kcm/testspeakerwidget.cpp
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native

Index: runtime/phonon/kcm/testspeakerwidget.h
===================================================================
--- runtime/phonon/kcm/testspeakerwidget.h	(revision 0)
+++ runtime/phonon/kcm/testspeakerwidget.h	(revision 1154776)
@@ -0,0 +1,50 @@
+/*  This file is part of the KDE project
+    Copyright (C) 2010 Colin Guthrie <cguthrie@mandriva.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2
+    as published by the Free Software Foundation.
+
+    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 General Public License for more details.
+
+    You should have received a copy of the GNU 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 PHONON_TESTSPEAKERWIDGET_H
+#define PHONON_TESTSPEAKERWIDGET_H
+
+#include <kpushbutton.h>
+
+#include <canberra.h>
+#include <pulse/pulseaudio.h>
+
+
+class SpeakerSetup;
+
+class TestSpeakerWidget: public KPushButton
+{
+    Q_OBJECT
+    public:
+        TestSpeakerWidget(const pa_channel_position_t pos, ca_context *canberra, SpeakerSetup* ss);
+
+    private slots:
+        void clicked();
+
+    private:
+        QString _positionName();
+        const char* _positionAsString();
+        const char* _positionSoundName();
+
+        SpeakerSetup* m_Ss;
+        pa_channel_position_t m_Pos;
+        ca_context* m_Canberra;
+};
+
+#endif // PHONON_TESTSPEAKERWIDGET_H

Property changes on: runtime/phonon/kcm/testspeakerwidget.h
___________________________________________________________________
Added: svn:eol-style
   + native

Index: runtime/phonon/kcm/speakersetup.ui
===================================================================
--- runtime/phonon/kcm/speakersetup.ui	(revision 0)
+++ runtime/phonon/kcm/speakersetup.ui	(revision 1154776)
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <author>Matthias Kretz &lt;kretz@kde.org</author>
+ <class>SpeakerSetup</class>
+ <widget class="QWidget" name="SpeakerSetup">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>537</width>
+    <height>465</height>
+   </rect>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QGroupBox" name="hardwareGroupBox">
+     <property name="title">
+      <string>Hardware</string>
+     </property>
+     <layout class="QGridLayout" name="_3">
+      <item row="3" column="1">
+       <widget class="QComboBox" name="profileBox">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="1">
+       <widget class="KComboBox" name="cardBox">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="0">
+       <widget class="QLabel" name="profileLabel">
+        <property name="text">
+         <string>Profile</string>
+        </property>
+        <property name="buddy">
+         <cstring>profileBox</cstring>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="0">
+       <widget class="QLabel" name="cardLabel">
+        <property name="text">
+         <string>Sound Card</string>
+        </property>
+        <property name="buddy">
+         <cstring>cardBox</cstring>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="outputGroupBox">
+     <property name="title">
+      <string>Output</string>
+     </property>
+     <layout class="QGridLayout" name="_2">
+      <item row="0" column="1">
+       <widget class="KComboBox" name="sinkBox">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="portLabel">
+        <property name="text">
+         <string>Connector</string>
+        </property>
+        <property name="buddy">
+         <cstring>portBox</cstring>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="QComboBox" name="portBox">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="0">
+       <widget class="QLabel" name="sinkLabel">
+        <property name="text">
+         <string>Sound Output</string>
+        </property>
+        <property name="buddy">
+         <cstring>sinkBox</cstring>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="title">
+      <string>Speaker Placement and Testing</string>
+     </property>
+     <layout class="QVBoxLayout" name="_4">
+      <item>
+       <layout class="QGridLayout" name="placementGrid"/>
+      </item>
+     </layout>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>KComboBox</class>
+   <extends>QComboBox</extends>
+   <header>kcombobox.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
Index: runtime/phonon/kcm/speakersetup.cpp
===================================================================
--- runtime/phonon/kcm/speakersetup.cpp	(revision 0)
+++ runtime/phonon/kcm/speakersetup.cpp	(revision 1154776)
@@ -0,0 +1,664 @@
+/*  This file is part of the KDE project
+    Copyright (C) 2010 Colin Guthrie <cguthrie@mandriva.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2
+    as published by the Free Software Foundation.
+
+    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 General Public License for more details.
+
+    You should have received a copy of the GNU 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 "speakersetup.h"
+#include "testspeakerwidget.h"
+#include <kgenericfactory.h>
+#include <kconfiggroup.h>
+#include <kaboutdata.h>
+#include <kicon.h>
+
+#include <glib.h>
+#include <pulse/xmalloc.h>
+#include <pulse/glib-mainloop.h>
+#include <QtCore/QAbstractEventDispatcher>
+#include <QtCore/QFileInfo>
+#include <QtCore/QDir>
+#include <QtDBus/QDBusConnection>
+#include <QtDBus/QDBusInterface>
+
+#define SS_DEFAULT_ICON "audio-card"
+
+
+static pa_glib_mainloop *s_mainloop = NULL;
+static pa_context *s_context = NULL;
+
+typedef struct {
+  uint32_t index;
+  QString name;
+  QString icon;
+  QMap<uint32_t,QPair<QString,QString> > profiles;
+  QString activeProfile;
+} cardInfo;
+
+QMap<uint32_t,cardInfo> s_Cards;
+
+typedef struct {
+  uint32_t index;
+  uint32_t cardIndex;
+  QString name;
+  QString icon;
+  pa_channel_map channelMap;
+  QMap<uint32_t,QPair<QString,QString> > ports;
+  QString activePort;
+} sinkInfo;
+
+QMap<uint32_t,sinkInfo> s_Sinks;
+
+static int debugLevel() {
+    static int level = -1;
+    if (level < 1) {
+        level = 0;
+        QString pulseenv = qgetenv("PHONON_PULSEAUDIO_DEBUG");
+        int l = pulseenv.toInt();
+        if (l > 0)
+            level = (l > 2 ? 2 : l);
+    }
+    return level;
+}
+
+static void logMessage(const QString &message, int priority = 2, QObject *obj=0);
+static void logMessage(const QString &message, int priority, QObject *obj)
+{
+    if (debugLevel() > 0) {
+        QString output;
+        if (obj) {
+            // Strip away namespace from className
+            QString className(obj->metaObject()->className());
+            int nameLength = className.length() - className.lastIndexOf(':') - 1;
+            className = className.right(nameLength);
+            output.sprintf("%s %s (%s %p)", message.toLatin1().constData(), 
+                           obj->objectName().toLatin1().constData(), 
+                           className.toLatin1().constData(), obj);
+        }
+        else {
+            output = message;
+        }
+        if (priority <= debugLevel()) {
+            qDebug() << QString("PulseSupport(%1): %2").arg(priority).arg(output);
+        }
+    }
+}
+
+
+static void card_cb(pa_context *c, const pa_card_info *i, int eol, void *userdata) {
+    Q_ASSERT(c);
+    Q_ASSERT(userdata);
+
+    SpeakerSetup* ss = static_cast<SpeakerSetup*>(userdata);
+
+    if (eol < 0) {
+        if (pa_context_errno(c) == PA_ERR_NOENTITY)
+            return;
+
+        logMessage(QString("Card callback failure"));
+        return;
+    }
+
+    if (eol > 0) {
+        ss->updateFromPulse();
+        return;
+    }
+
+    Q_ASSERT(i);
+    ss->updateCard(i);
+}
+
+static void sink_cb(pa_context *c, const pa_sink_info *i, int eol, void *userdata) {
+    Q_ASSERT(c);
+    Q_ASSERT(userdata);
+
+    SpeakerSetup* ss = static_cast<SpeakerSetup*>(userdata);
+
+    if (eol < 0) {
+        if (pa_context_errno(c) == PA_ERR_NOENTITY)
+            return;
+
+        logMessage(QString("Sink callback failure"));
+        return;
+    }
+
+    if (eol > 0) {
+        ss->updateIndependantDevices();
+        ss->updateFromPulse();
+        return;
+    }
+
+    Q_ASSERT(i);
+    ss->updateSink(i);
+}
+
+static void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t index, void *userdata) {
+    Q_ASSERT(c);
+    Q_ASSERT(userdata);
+
+    SpeakerSetup* ss = static_cast<SpeakerSetup*>(userdata);
+
+    switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
+        case PA_SUBSCRIPTION_EVENT_CARD:
+            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+                ss->removeCard(index);
+            } else {
+                pa_operation *o;
+                if (!(o = pa_context_get_card_info_by_index(c, index, card_cb, ss))) {
+                    logMessage(QString("pa_context_get_card_info_by_index() failed"));
+                    return;
+                }
+                pa_operation_unref(o);
+            }
+            break;
+
+        case PA_SUBSCRIPTION_EVENT_SINK:
+            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+              ss->removeSink(index);
+            } else {
+                pa_operation *o;
+                if (!(o = pa_context_get_sink_info_by_index(c, index, sink_cb, ss))) {
+                    logMessage(QString("pa_context_get_sink_info_by_index() failed"));
+                    return;
+                }
+                pa_operation_unref(o);
+            }
+            break;
+    }
+}
+
+
+static const char* statename(pa_context_state_t state)
+{
+    switch (state)
+    {
+        case PA_CONTEXT_UNCONNECTED:  return "Unconnected";
+        case PA_CONTEXT_CONNECTING:   return "Connecting";
+        case PA_CONTEXT_AUTHORIZING:  return "Authorizing";
+        case PA_CONTEXT_SETTING_NAME: return "Setting Name";
+        case PA_CONTEXT_READY:        return "Ready";
+        case PA_CONTEXT_FAILED:       return "Failed";
+        case PA_CONTEXT_TERMINATED:   return "Terminated";
+    }
+
+    static QString unknown;
+    unknown = QString("Unknown state: %0").arg(state);
+    return unknown.toAscii().constData();
+}
+
+static void context_state_callback(pa_context *c, void *userdata)
+{
+    Q_ASSERT(c);
+    Q_ASSERT(userdata);
+
+    SpeakerSetup* ss = static_cast<SpeakerSetup*>(userdata);
+
+    logMessage(QString("context_state_callback %1").arg(statename(pa_context_get_state(c))));
+    pa_context_state_t state = pa_context_get_state(c);
+    if (state == PA_CONTEXT_READY) {
+        // Attempt to load things up
+        pa_operation *o;
+
+        pa_context_set_subscribe_callback(c, subscribe_cb, ss);
+
+        if (!(o = pa_context_subscribe(c, (pa_subscription_mask_t)
+                                          (PA_SUBSCRIPTION_MASK_CARD|
+                                           PA_SUBSCRIPTION_MASK_SINK), NULL, NULL))) {
+            logMessage(QString("pa_context_subscribe() failed"));
+            return;
+        }
+        pa_operation_unref(o);
+
+        if (!(o = pa_context_get_card_info_list(c, card_cb, ss))) {
+          logMessage(QString("pa_context_get_card_info_list() failed"));
+          return;
+        }
+        pa_operation_unref(o);
+
+        if (!(o = pa_context_get_sink_info_list(c, sink_cb, ss))) {
+          logMessage(QString("pa_context_get_sink_info_list() failed"));
+          return;
+        }
+        pa_operation_unref(o);
+
+        ss->load();
+
+    } else if (!PA_CONTEXT_IS_GOOD(state)) {
+        /// @todo Deal with reconnection...
+        //logMessage(QString("Connection to PulseAudio lost: %1").arg(pa_strerror(pa_context_errno(c))));
+
+        // If this is our probe phase, exit our context immediately
+        if (s_context != c)
+            pa_context_disconnect(c);
+        else {
+            pa_context_unref(s_context);
+            s_context = NULL;
+            //QTimer::singleShot(50, PulseSupport::getInstance(), SLOT(connectToDaemon()));
+        }
+    }
+}
+
+
+SpeakerSetup::SpeakerSetup(QWidget *parent)
+    : QWidget(parent)
+    , m_OutstandingRequests(2)
+    , m_Canberra(NULL)
+{
+    setupUi(this);
+
+    cardLabel->setEnabled(false);
+    cardBox->setEnabled(false);
+    profileLabel->setVisible(false);
+    profileBox->setVisible(false);
+
+    sinkLabel->setEnabled(false);
+    sinkBox->setEnabled(false);
+    portLabel->setVisible(false);
+    portBox->setVisible(false);
+
+    for (int i=0; i<5; ++i)
+        placementGrid->setColumnStretch(i, 1);
+    for (int i=0; i<3; ++i)
+        placementGrid->setRowStretch(i, 1);
+
+    m_Icon = new QLabel(this);
+    const QFileInfo fi(QDir(QDir::homePath()), ".face.icon");
+    if (fi.exists())
+        m_Icon->setPixmap(QPixmap(fi.absoluteFilePath()).scaled(KIconLoader::SizeHuge, KIconLoader::SizeHuge, Qt::KeepAspectRatio));
+    else
+        m_Icon->setPixmap(KIcon("system-users").pixmap(KIconLoader::SizeHuge, KIconLoader::SizeHuge));
+    placementGrid->addWidget(m_Icon, 1, 2, Qt::AlignCenter);
+
+    update();
+    connect(cardBox,    SIGNAL(currentIndexChanged(int)), SLOT(cardChanged()));
+    connect(profileBox, SIGNAL(currentIndexChanged(int)), SLOT(profileChanged()));
+    connect(sinkBox,    SIGNAL(currentIndexChanged(int)), SLOT(sinkChanged()));
+    connect(portBox,    SIGNAL(currentIndexChanged(int)), SLOT(portChanged()));
+
+    // We require a glib event loop
+    if (QLatin1String(QAbstractEventDispatcher::instance()->metaObject()->className())
+            != "QGuiEventDispatcherGlib") {
+        logMessage("Disabling PulseAudio integration for lack of GLib event loop.");
+        return;
+    }
+
+    s_mainloop = pa_glib_mainloop_new(NULL);
+    Q_ASSERT(s_mainloop);
+
+    pa_mainloop_api *api = pa_glib_mainloop_get_api(s_mainloop);
+
+    s_context = pa_context_new(api, "kspeakersetup");
+    int rv;
+    rv = pa_context_connect(s_context, NULL, PA_CONTEXT_NOFAIL, 0);
+    Q_ASSERT(rv >= 0);
+
+    pa_context_set_state_callback(s_context, &context_state_callback, this);
+
+    rv = ca_context_create(&m_Canberra);
+    Q_ASSERT(rv >= 0);
+}
+
+SpeakerSetup::~SpeakerSetup()
+{
+    ca_context_destroy(m_Canberra);
+    pa_context_unref(s_context);
+    s_context = NULL;
+    pa_glib_mainloop_free(s_mainloop);
+    s_mainloop = NULL;
+}
+
+void SpeakerSetup::load()
+{
+}
+
+void SpeakerSetup::save()
+{
+}
+
+void SpeakerSetup::defaults()
+{
+}
+
+void SpeakerSetup::updateCard(const pa_card_info* i)
+{
+    cardInfo info;
+    info.index = i->index;
+
+    const char* description = pa_proplist_gets(i->proplist, PA_PROP_DEVICE_DESCRIPTION);
+    info.name = description ? QString::fromUtf8(description) : i->name;
+
+    const char* icon = pa_proplist_gets(i->proplist, PA_PROP_DEVICE_ICON_NAME);
+    info.icon = icon ? icon : SS_DEFAULT_ICON;
+
+    for (uint32_t j = 0; j < i->n_profiles; ++j)
+      info.profiles[i->profiles[j].priority] = QPair<QString,QString>(i->profiles[j].name, QString::fromUtf8(i->profiles[j].description));
+    if (i->active_profile)
+      info.activeProfile = i->active_profile->name;
+
+
+    bool bs = cardBox->blockSignals(true);
+    if (s_Cards.contains(i->index)) {
+      int idx = cardBox->findData(i->index);
+      if (idx >= 0) {
+        cardBox->setItemIcon(idx, KIcon(info.icon));
+        cardBox->setItemText(idx, info.name);
+      }
+    }
+    else
+      cardBox->addItem(KIcon(info.icon), info.name, i->index);
+    cardBox->blockSignals(bs);
+
+    s_Cards[i->index] = info;
+
+    cardChanged();
+
+    logMessage(QString("Got info about card %1").arg(info.name));
+}
+
+void SpeakerSetup::removeCard(uint32_t index)
+{
+    s_Cards.remove(index);
+    updateFromPulse();
+    int idx = cardBox->findData(index);
+    if (idx >= 0)
+        cardBox->removeItem(idx);
+}
+
+void SpeakerSetup::updateSink(const pa_sink_info* i)
+{
+    sinkInfo info;
+    info.index = i->index;
+    info.cardIndex = i->card;
+    info.name = QString::fromUtf8(i->description);
+
+    const char* icon = pa_proplist_gets(i->proplist, PA_PROP_DEVICE_ICON_NAME);
+    info.icon = icon ? icon : SS_DEFAULT_ICON;
+
+    info.channelMap = i->channel_map;
+
+    for (uint32_t j = 0; j < i->n_ports; ++j)
+      info.ports[i->ports[j]->priority] = QPair<QString,QString>(i->ports[j]->name, QString::fromUtf8(i->ports[j]->description));
+    if (i->active_port)
+      info.activePort = i->active_port->name;
+
+    s_Sinks[i->index] = info;
+
+    // Need to update the currently displayed port if this sink is the currently displayed one.
+    if (info.ports.size()) {
+        int idx = sinkBox->currentIndex();
+        if (idx >= 0) {
+            uint32_t sink_index = sinkBox->itemData(idx).toUInt();
+            if (sink_index == i->index) {
+                bool bs = portBox->blockSignals(true);
+                portBox->setCurrentIndex(portBox->findData(info.activePort));
+                portBox->blockSignals(bs);
+            }
+        }
+    }
+
+    logMessage(QString("Got info about sink %1").arg(info.name));
+}
+
+void SpeakerSetup::removeSink(uint32_t index)
+{
+    s_Sinks.remove(index);
+    updateIndependantDevices();
+    updateFromPulse();
+    int idx = sinkBox->findData(index);
+    if (idx >= 0)
+        sinkBox->removeItem(idx);
+}
+
+void SpeakerSetup::updateFromPulse()
+{
+    if (m_OutstandingRequests > 0) {
+        if (0 == --m_OutstandingRequests) {
+            // Work out which seclector to pick by default (we want to choose a real Card if possible)
+            if (s_Cards.size() != cardBox->count())
+                cardBox->setCurrentIndex(1);
+            emit ready();
+        }
+    }
+
+    if (!m_OutstandingRequests) {
+        if (!s_Cards.size() && !s_Sinks.size()) {
+            cardLabel->setEnabled(false);
+            cardBox->setEnabled(false);
+            profileLabel->setVisible(false);
+            profileBox->setVisible(false);
+
+            sinkLabel->setEnabled(false);
+            sinkBox->setEnabled(false);
+            portLabel->setVisible(false);
+            portBox->setVisible(false);
+        }
+        if (s_Cards.size() && !cardBox->isEnabled()) {
+            cardLabel->setEnabled(true);
+            cardBox->setEnabled(true);
+            cardChanged();
+        }
+        if (s_Sinks.size() && !sinkBox->isEnabled()) {
+            sinkLabel->setEnabled(true);
+            sinkBox->setEnabled(true);
+            sinkChanged();
+        }
+    }
+}
+
+void SpeakerSetup::cardChanged()
+{
+    int idx = cardBox->currentIndex();
+    if (idx < 0) {
+        profileLabel->setVisible(false);
+        profileBox->setVisible(false);
+        return;
+    }
+
+    uint32_t card_index = cardBox->itemData(idx).toUInt();
+    Q_ASSERT(PA_INVALID_INDEX == card_index || s_Cards.contains(card_index));
+    bool show_profiles = (PA_INVALID_INDEX != card_index && s_Cards[card_index].profiles.size());
+    if (show_profiles) {
+        cardInfo &card_info = s_Cards[card_index];
+        bool bs = profileBox->blockSignals(true);
+        profileBox->clear();
+        for (QMap<uint32_t, QPair<QString,QString> >::iterator it = card_info.profiles.begin(); it != card_info.profiles.end(); ++it)
+            profileBox->insertItem(0, it.value().second, it.value().first);
+        profileBox->setCurrentIndex(profileBox->findData(card_info.activeProfile));
+        profileBox->blockSignals(bs);
+    }
+    profileLabel->setVisible(show_profiles);
+    profileBox->setVisible(show_profiles);
+
+
+    bool bs = sinkBox->blockSignals(true);
+    sinkBox->clear();
+    for (QMap<uint32_t,sinkInfo>::iterator it = s_Sinks.begin(); it != s_Sinks.end(); ++it) {
+        if (it->cardIndex == card_index)
+            sinkBox->addItem(KIcon(it->icon), it->name, it->index);
+    }
+    sinkBox->blockSignals(bs);
+
+    outputGroupBox->setEnabled(!!sinkBox->count());
+
+    sinkChanged();
+
+    logMessage(QString("Doing update %1").arg(cardBox->currentIndex()));
+
+    emit changed();
+}
+
+void SpeakerSetup::profileChanged()
+{
+    uint32_t card_index = cardBox->itemData(cardBox->currentIndex()).toUInt();
+    Q_ASSERT(PA_INVALID_INDEX != card_index);
+
+    QString profile = profileBox->itemData(profileBox->currentIndex()).toString();
+    logMessage(QString("Changing profile to %1").arg(profile));
+
+    cardInfo &card_info = s_Cards[card_index];
+    Q_ASSERT(card_info.profiles.size());
+
+    pa_operation *o;
+    if (!(o = pa_context_set_card_profile_by_index(s_context, card_index, profile.toAscii().constData(), NULL, NULL)))
+        logMessage(QString("pa_context_set_card_profile_by_name() failed"));
+    else
+        pa_operation_unref(o);
+
+    emit changed();
+}
+
+void SpeakerSetup::updateIndependantDevices()
+{
+    // Should we display the "Independant Devices" drop down?
+    // Count all the sinks without cards
+    bool showID = false;
+    for (QMap<uint32_t,sinkInfo>::iterator it = s_Sinks.begin(); it != s_Sinks.end(); ++it) {
+        if (PA_INVALID_INDEX == it->cardIndex) {
+            showID = true;
+            break;
+        }
+    }
+
+    bool haveID = (PA_INVALID_INDEX == cardBox->itemData(0).toUInt());
+
+    logMessage(QString("Want ID: %1; Have ID: %2").arg(showID?"Yes":"No").arg(haveID?"Yes":"No"));
+
+    bool bs = cardBox->blockSignals(true);
+    if (haveID && !showID)
+        cardBox->removeItem(0);
+    else if (!haveID && showID)
+        cardBox->insertItem(0, KIcon(SS_DEFAULT_ICON), "Independent Devices", PA_INVALID_INDEX);
+    cardBox->blockSignals(bs);
+}
+
+void SpeakerSetup::sinkChanged()
+{
+    int idx = sinkBox->currentIndex();
+    if (idx < 0) {
+        portLabel->setVisible(false);
+        portBox->setVisible(false);
+        _updatePlacementTester();
+        return;
+    }
+    uint32_t sink_index = sinkBox->itemData(idx).toUInt();
+    Q_ASSERT(s_Sinks.contains(sink_index));
+    sinkInfo &sink_info = s_Sinks[sink_index];
+    logMessage(QString("Updating ports for sink '%1' (%2 ports available)").arg(sink_info.name).arg(sink_info.ports.size()));
+
+    bool show_ports = !!sink_info.ports.size();
+    if (show_ports) {
+        bool bs = portBox->blockSignals(true);
+        portBox->clear();
+        for (QMap<uint32_t, QPair<QString,QString> >::iterator it = sink_info.ports.begin(); it != sink_info.ports.end(); ++it)
+            portBox->insertItem(0, it.value().second, it.value().first);
+        portBox->setCurrentIndex(portBox->findData(sink_info.activePort));
+        portBox->blockSignals(bs);
+    }
+    portLabel->setVisible(show_ports);
+    portBox->setVisible(show_ports);
+
+    if (sinkBox->currentIndex() >= 0)
+        _updatePlacementTester();
+
+    emit changed();
+}
+
+void SpeakerSetup::portChanged()
+{
+    uint32_t sink_index = sinkBox->itemData(sinkBox->currentIndex()).toUInt();
+    Q_ASSERT(PA_INVALID_INDEX != sink_index);
+
+    QString port = portBox->itemData(portBox->currentIndex()).toString();
+    logMessage(QString("Changing port to %1").arg(port));
+
+    sinkInfo &sink_info = s_Sinks[sink_index];
+    Q_ASSERT(sink_info.ports.size());
+
+    pa_operation *o;
+    if (!(o = pa_context_set_sink_port_by_index(s_context, sink_index, port.toAscii().constData(), NULL, NULL)))
+        logMessage(QString("pa_context_set_sink_port_by_index() failed"));
+    else
+        pa_operation_unref(o);
+
+    emit changed();
+}
+
+void SpeakerSetup::_updatePlacementTester()
+{
+    static const int position_table[] = {
+        /* Position, X, Y */
+        PA_CHANNEL_POSITION_FRONT_LEFT,            0, 0,
+        PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,  1, 0,
+        PA_CHANNEL_POSITION_FRONT_CENTER,          2, 0,
+        PA_CHANNEL_POSITION_MONO,                  2, 0,
+        PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, 3, 0,
+        PA_CHANNEL_POSITION_FRONT_RIGHT,           4, 0,
+        PA_CHANNEL_POSITION_SIDE_LEFT,             0, 1,
+        PA_CHANNEL_POSITION_SIDE_RIGHT,            4, 1,
+        PA_CHANNEL_POSITION_REAR_LEFT,             0, 2,
+        PA_CHANNEL_POSITION_REAR_CENTER,           2, 2,
+        PA_CHANNEL_POSITION_REAR_RIGHT,            4, 2,
+        PA_CHANNEL_POSITION_LFE,                   3, 2
+    };
+
+    QLayoutItem* w;
+    while ((w = placementGrid->takeAt(0))) {
+        if (w->widget() != m_Icon) {
+            if (w->widget())
+                delete w->widget();
+            delete w;
+        }
+    }
+    placementGrid->addWidget(m_Icon, 1, 2, Qt::AlignCenter);
+    int idx = sinkBox->currentIndex();
+    if (idx < 0)
+      return;
+
+    uint32_t sink_index = sinkBox->itemData(idx).toUInt();
+    Q_ASSERT(s_Sinks.contains(sink_index));
+    sinkInfo& sink_info = s_Sinks[sink_index];
+
+    for (int i = 0; i < 36; i += 3) {
+        pa_channel_position_t pos = (pa_channel_position_t)position_table[i];
+        // Check to see if we have this item in our current channel map.
+        bool have = false;
+        for (uint32_t j = 0; j < sink_info.channelMap.channels; ++j) {
+            if (sink_info.channelMap.map[j] == pos) {
+                have = true;
+                break;
+            }
+        }
+        if (!have) {
+            continue;
+        }
+
+        KPushButton* btn = new TestSpeakerWidget(pos, m_Canberra, this);//KPushButton(KIcon("audio-card"), (name ? name : "Unknown Channel"), this);
+        placementGrid->addWidget(btn, position_table[i+2], position_table[i+1], Qt::AlignCenter);
+    }
+
+}
+
+uint32_t SpeakerSetup::getCurrentSinkIndex()
+{
+    int idx = sinkBox->currentIndex();
+    if (idx < 0)
+        return PA_INVALID_INDEX;
+
+    return sinkBox->itemData(idx).toUInt();
+}
+
+
+#include "speakersetup.moc"
+// vim: sw=4 sts=4 et tw=100

Property changes on: runtime/phonon/kcm/speakersetup.cpp
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native

Index: runtime/phonon/kcm/speakersetup.h
===================================================================
--- runtime/phonon/kcm/speakersetup.h	(revision 0)
+++ runtime/phonon/kcm/speakersetup.h	(revision 1154776)
@@ -0,0 +1,70 @@
+/*  This file is part of the KDE project
+    Copyright (C) 2010 Colin Guthrie <cguthrie@mandriva.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2
+    as published by the Free Software Foundation.
+
+    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 General Public License for more details.
+
+    You should have received a copy of the GNU 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 PHONON_SPEAKERSETUP_H
+#define PHONON_SPEAKERSETUP_H
+
+#include <canberra.h>
+#include <pulse/pulseaudio.h>
+
+#include "ui_speakersetup.h"
+#define KDE3_SUPPORT
+#include <kcmodule.h>
+#undef KDE3_SUPPORT
+#include <kconfig.h>
+
+//class QLabel;
+
+class SpeakerSetup : public QWidget, private Ui::SpeakerSetup
+{
+    Q_OBJECT
+    public:
+        SpeakerSetup(QWidget *parent = 0);
+        ~SpeakerSetup();
+
+        void load();
+        void save();
+        void defaults();
+        uint32_t getCurrentSinkIndex();
+        void updateCard(const pa_card_info*);
+        void removeCard(uint32_t idx);
+        void updateSink(const pa_sink_info*);
+        void removeSink(uint32_t idx);
+        void updateFromPulse();
+        void updateIndependantDevices();
+
+    public Q_SLOTS:
+        void cardChanged();
+        void profileChanged();
+        void sinkChanged();
+        void portChanged();
+
+    Q_SIGNALS:
+        void changed();
+        void ready();
+
+    private:
+        void _updatePlacementTester();
+
+        QLabel* m_Icon;
+        int m_OutstandingRequests;
+        ca_context* m_Canberra;
+};
+
+#endif // PHONON_SPEAKERSETUP_H

Property changes on: runtime/phonon/kcm/speakersetup.h
___________________________________________________________________
Added: svn:eol-style
   + native

Index: runtime/phonon/kcm/main.cpp
===================================================================
--- runtime/phonon/kcm/main.cpp	(revision 1154775)
+++ runtime/phonon/kcm/main.cpp	(revision 1154776)
@@ -1,5 +1,6 @@
 /*  This file is part of the KDE project
     Copyright (C) 2006-2007 Matthias Kretz <kretz@kde.org>
+    Copyright (C) 2010 Colin Guthrie <cguthrie@mandriva.org>
 
     This program is free software; you can redistribute it and/or
     modify it under the terms of the GNU Library General Public
@@ -28,6 +29,10 @@
 #include "devicepreference.h"
 #include "backendselection.h"
 
+#ifdef HAVE_PULSEAUDIO
+#  include "speakersetup.h"
+#endif
+
 K_PLUGIN_FACTORY(PhononKcmFactory, registerPlugin<PhononKcm>();)
 K_EXPORT_PLUGIN(PhononKcmFactory("kcm_phonon"))
 
@@ -39,23 +44,33 @@
             KDE_VERSION_STRING, KLocalizedString(), KAboutData::License_GPL,
             ki18n("Copyright 2006 Matthias Kretz"));
     about->addAuthor(ki18n("Matthias Kretz"), KLocalizedString(), "kretz@kde.org");
+    about->addAuthor(ki18n("Colin Guthrie"), KLocalizedString(), "cguthrie@mandriva.org");
     setAboutData(about);
 
     setLayout(new QHBoxLayout);
     layout()->setMargin(0);
     layout()->setSpacing(0);
 
-    KTabWidget *tabs = new KTabWidget(this);
-    layout()->addWidget(tabs);
+    m_tabs = new KTabWidget(this);
+    layout()->addWidget(m_tabs);
 
     m_devicePreferenceWidget = new DevicePreference(this);
-    tabs->addTab(m_devicePreferenceWidget, i18n("Device Preference"));
+    m_tabs->addTab(m_devicePreferenceWidget, i18n("Device Preference"));
     m_backendSelection = new BackendSelection(this);
-    tabs->addTab(m_backendSelection, i18n("Backend"));
+    m_tabs->addTab(m_backendSelection, i18n("Backend"));
+
     load();
     connect(m_backendSelection, SIGNAL(changed()), SLOT(changed()));
     connect(m_devicePreferenceWidget, SIGNAL(changed()), SLOT(changed()));
+
     setButtons( KCModule::Default|KCModule::Apply|KCModule::Help );
+
+#ifdef HAVE_PULSEAUDIO
+    m_speakerSetup = new SpeakerSetup(this);
+    m_speakerSetup->setVisible(false);
+    connect(m_speakerSetup, SIGNAL(ready()), SLOT(speakerSetupReady()));
+    connect(m_speakerSetup, SIGNAL(changed()), SLOT(changed()));
+#endif
 }
 
 void PhononKcm::load()
@@ -76,5 +91,13 @@
     m_backendSelection->defaults();
 }
 
+#ifdef HAVE_PULSEAUDIO
+void PhononKcm::speakerSetupReady()
+{
+  m_tabs->insertTab(1, m_speakerSetup, i18n("Speaker Setup"));
+  emit changed();
+}
+#endif
+
 #include "main.moc"
 // vim: ts=4
Index: runtime/phonon/kcm/main.h
===================================================================
--- runtime/phonon/kcm/main.h	(revision 1154775)
+++ runtime/phonon/kcm/main.h	(revision 1154776)
@@ -25,6 +25,11 @@
 class DevicePreference;
 class BackendSelection;
 
+#ifdef HAVE_PULSEAUDIO
+class SpeakerSetup;
+#endif
+class KTabWidget;
+
 class PhononKcm : public KCModule
 {
     Q_OBJECT
@@ -35,9 +40,18 @@
         void save();
         void defaults();
 
+#ifdef HAVE_PULSEAUDIO
+    private Q_SLOTS:
+        void speakerSetupReady();
+#endif
+
     private:
+        KTabWidget* m_tabs;
         DevicePreference *m_devicePreferenceWidget;
         BackendSelection *m_backendSelection;
+#ifdef HAVE_PULSEAUDIO
+        SpeakerSetup* m_speakerSetup;
+#endif
 };
 
 #endif // MAIN_H
Index: runtime/phonon/kcm/CMakeLists.txt
===================================================================
--- runtime/phonon/kcm/CMakeLists.txt	(revision 1154775)
+++ runtime/phonon/kcm/CMakeLists.txt	(revision 1154776)
@@ -1,3 +1,11 @@
+set(PULSEAUDIO_MINIMUM_VERSION "0.9.16")
+macro_optional_find_package(PulseAudio)
+macro_log_feature(PULSEAUDIO_FOUND "PulseAudio" "PulseAudio Audio Server" "http://www.pulseaudio.org/" FALSE "0.9.16" "libpulse is needed for speaker setup GUI")
+find_package(GLIB2)
+
+pkg_check_modules(CANBERRA libcanberra)
+macro_log_feature(CANBERRA_FOUND "libcanberra" "libcanberra audio library" "http://0pointer.de/lennart/projects/libcanberra/" FALSE "" "libcanberra is needed for speaker setup GUI")
+
 add_subdirectory(xine)
 
 set(kcmphonon_SRCS main.cpp devicepreference.cpp backendselection.cpp)
@@ -2,6 +10,17 @@
 kde4_add_ui_files(kcmphonon_SRCS devicepreference.ui backendselection.ui)
+set(kcmphonon_LIBS ${KDE4_PHONON_LIBS} ${KDE4_KCMUTILS_LIBS} ${KDE4_KIO_LIBS})
 
+if(GLIB2_FOUND AND PULSEAUDIO_FOUND AND CANBERRA_FOUND)
+  add_definitions(-DHAVE_PULSEAUDIO)
+
+  set(kcmphonon_SRCS ${kcmphonon_SRCS} speakersetup.cpp testspeakerwidget.cpp)
+  kde4_add_ui_files(kcmphonon_SRCS speakersetup.ui)
+
+  include_directories(${GLIB2_INCLUDE_DIR} ${PULSEAUDIO_INCLUDE_DIR} ${CANBERRA_INCLUDE_DIRS})
+
+  set(kcmphonon_LIBS ${kcmphonon_LIBS} ${GLIB2_LIBRARIES} ${PULSEAUDIO_LIBRARY} ${PULSEAUDIO_MAINLOOP_LIBRARY} ${CANBERRA_LIBRARIES})
+endif(GLIB2_FOUND AND PULSEAUDIO_FOUND AND CANBERRA_FOUND)
+
 kde4_add_plugin(kcm_phonon ${kcmphonon_SRCS})
-target_link_libraries(kcm_phonon ${KDE4_PHONON_LIBS} ${KDE4_KCMUTILS_LIBS}
-   ${KDE4_KIO_LIBS})
+target_link_libraries(kcm_phonon ${kcmphonon_LIBS})