diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3b9e525 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/kmix-4.10.5.tar.xz diff --git a/.kmix.metadata b/.kmix.metadata new file mode 100644 index 0000000..bc62d1e --- /dev/null +++ b/.kmix.metadata @@ -0,0 +1 @@ +80b29d1c360dbebf1516c92de144f823012491ee SOURCES/kmix-4.10.5.tar.xz diff --git a/SOURCES/0001-MPRIS2-backend-now-does-a-asynchonous-DBUS-instrospe.patch b/SOURCES/0001-MPRIS2-backend-now-does-a-asynchonous-DBUS-instrospe.patch new file mode 100644 index 0000000..6db2d66 --- /dev/null +++ b/SOURCES/0001-MPRIS2-backend-now-does-a-asynchonous-DBUS-instrospe.patch @@ -0,0 +1,114 @@ +From d962e54294f2b9e952cd013ba5fb12764d0ac0dc Mon Sep 17 00:00:00 2001 +From: Christian Esken +Date: Mon, 4 Feb 2013 23:20:57 +0100 +Subject: [PATCH 1/8] MPRIS2 backend now does a asynchonous DBUS instrospection + CCBUGS: 311189 + +--- + backends/mixer_mpris2.cpp | 51 +++++++++++++++++++++++++++++++---------------- + 1 file changed, 34 insertions(+), 17 deletions(-) + +diff --git a/backends/mixer_mpris2.cpp b/backends/mixer_mpris2.cpp +index 9b2adca..88e0a86 100644 +--- a/backends/mixer_mpris2.cpp ++++ b/backends/mixer_mpris2.cpp +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + + #include + +@@ -241,18 +242,34 @@ int Mixer_MPRIS2::addAllRunningPlayersAndInitHotplug() + * There is no simple solution (reversing could have the problem of not-adding), so we live for now with it. + */ + +- QDBusReply repl = dbusConn.interface()->registeredServiceNames(); ++ /* ++ * Bug 311189: Introspecting via "dbusConn.interface()->registeredServiceNames()" does not work too well in ++ * specific scenarios. Thus I now do a hand crafted 3-line asynchronous version of registeredServiceNames(). ++ */ ++ QDBusInterface dbusIfc("org.freedesktop.DBus", "/org/freedesktop/DBus", ++ "org.freedesktop.DBus", dbusConn); ++ QDBusPendingReply repl = dbusIfc.asyncCall("ListNames"); ++ repl.waitForFinished(); ++ + + if ( repl.isValid() ) + { ++ qDebug() << "Attaching Media Players"; + QStringList result = repl.value(); + QString s; + foreach ( s , result ) + { + if ( s.startsWith("org.mpris.MediaPlayer2") ) ++ { + addMprisControl(dbusConn, s); ++ kDebug() << "Attached " << s; ++ } + } + } ++ else ++ { ++ kError() << "Invalid reply while listing Media Players" << repl.error(); ++ } + + + return 0; +@@ -275,19 +292,19 @@ void Mixer_MPRIS2::addMprisControl(QDBusConnection& conn, QString busDestination + int lastDot = busDestination.lastIndexOf('.'); + QString id = ( lastDot == -1 ) ? busDestination : busDestination.mid(lastDot+1); + kDebug(67100) << "Get control of " << busDestination << "id=" << id; +- if (id.startsWith("clementine")) +- { +- // Bug 311189: Clementine hangs +- QString text; +- text = +- i18n( +- "Media player '%1' is not compatible with KMix. Integration skipped.", +- id); +- // We cannot do GUI stuff in the backend, so lets only log it for now +-// KMixToolBox::notification("Unsupported Media Player", text); +- kWarning() << text; +- return; +- } ++// if (id.startsWith("clementine")) ++// { ++// // Bug 311189: Clementine hangs ++// QString text; ++// text = ++// i18n( ++// "Media player '%1' is not compatible with KMix. Integration skipped.", ++// id); ++// // We cannot do GUI stuff in the backend, so lets only log it for now ++//// KMixToolBox::notification("Unsupported Media Player", text); ++// kWarning() << text; ++// return; ++// } + + + QDBusInterface *qdbiProps = new QDBusInterface(QString(busDestination), QString("/org/mpris/MediaPlayer2"), "org.freedesktop.DBus.Properties", conn, this); +@@ -302,8 +319,8 @@ void Mixer_MPRIS2::addMprisControl(QDBusConnection& conn, QString busDestination + + QString readableName = id; + +- if (id != "clementine") +- { ++// if (id != "clementine") ++// { + QList arg; + arg.append(QString("org.mpris.MediaPlayer2")); + arg.append(QString("Identity")); +@@ -328,7 +345,7 @@ void Mixer_MPRIS2::addMprisControl(QDBusConnection& conn, QString busDestination + { + qWarning() << "Error (" << msg.type() << "): " << msg.errorName() << " " << msg.errorMessage(); + } +- } ++// } + + // TODO This hardcoded application list is a quick hack. It should be generalized. + MixDevice::ChannelType ct = MixDevice::APPLICATION_STREAM; +-- +1.8.1.4 + diff --git a/SOURCES/0002-Use-a-fixed-volume-step-of-n-instead-of-honoring-mou.patch b/SOURCES/0002-Use-a-fixed-volume-step-of-n-instead-of-honoring-mou.patch new file mode 100644 index 0000000..883c927 --- /dev/null +++ b/SOURCES/0002-Use-a-fixed-volume-step-of-n-instead-of-honoring-mou.patch @@ -0,0 +1,471 @@ +From 207fa53763d83e8031be622c9006e41b97195d48 Mon Sep 17 00:00:00 2001 +From: Christian Esken +Date: Mon, 4 Feb 2013 23:30:47 +0100 +Subject: [PATCH 2/8] Use a fixed volume step of n% instead of honoring mouse + delta (which is more appropriate for text documents). Also Mouse Wheel now + only affects EITHER playback OR capture CCBUGS: 313579 CCBUGS: 300783 + +--- + apps/kmix.cpp | 2 +- + core/mixdevice.cpp | 1 - + core/mixer.cpp | 28 ++++++------ + core/mixer.h | 3 -- + core/volume.cpp | 37 ++++++++++++---- + core/volume.h | 7 ++- + gui/kmixdockwidget.cpp | 18 ++++---- + gui/ksmallslider.cpp | 16 ++++--- + gui/mdwslider.cpp | 113 +++++++++++++++++++++++++++++-------------------- + gui/mdwslider.h | 4 +- + 10 files changed, 136 insertions(+), 93 deletions(-) + +diff --git a/apps/kmix.cpp b/apps/kmix.cpp +index 189bb50..dd2861a 100644 +--- a/apps/kmix.cpp ++++ b/apps/kmix.cpp +@@ -576,7 +576,7 @@ KMixWindow::loadBaseConfig() + { + float volumePercentageStep = volumePercentageStepString.toFloat(); + if (volumePercentageStep > 0 && volumePercentageStep <= 100) +- Mixer::VOLUME_STEP_DIVISOR = (100 / volumePercentageStep); ++ Volume::VOLUME_STEP_DIVISOR = (100 / volumePercentageStep); + } + + +diff --git a/core/mixdevice.cpp b/core/mixdevice.cpp +index 6687f38..662fd85 100644 +--- a/core/mixdevice.cpp ++++ b/core/mixdevice.cpp +@@ -186,7 +186,6 @@ void MixDevice::addPlaybackVolume(Volume &playbackVol) + // Hint: "_playbackVolume" gets COPIED from "playbackVol", because the copy-constructor actually copies the volume levels. + _playbackVolume = playbackVol; + _playbackVolume.setSwitchType(Volume::PlaybackSwitch); +- playbackVol.hasSwitchDisallowRead(); // Only allowed to read once, and only here during migrating the switch back to MixDevice + } + + void MixDevice::addCaptureVolume (Volume &captureVol) +diff --git a/core/mixer.cpp b/core/mixer.cpp +index ebbeb9c..77c7dbc 100644 +--- a/core/mixer.cpp ++++ b/core/mixer.cpp +@@ -39,8 +39,6 @@ + QList Mixer::s_mixers; + MasterControl Mixer::_globalMasterCurrent; + MasterControl Mixer::_globalMasterPreferred; +-float Mixer::VOLUME_STEP_DIVISOR = 20; +-float Mixer::VOLUME_PAGESTEP_DIVISOR = 10; + bool Mixer::m_beepOnVolumeChange = false; + + int Mixer::numDrivers() +@@ -639,6 +637,14 @@ void Mixer::decreaseVolume( const QString& mixdeviceID ) + increaseOrDecreaseVolume(mixdeviceID, true); + } + ++/** ++ * Increase or decrease all playback and capture channels of the given control. ++ * This method is very similar to MDWSlider::increaseOrDecreaseVolume(), but it will ++ * NOT auto-unmute. ++ * ++ * @param mixdeviceID The control name ++ * @param decrease true for decrease. false for increase ++ */ + void Mixer::increaseOrDecreaseVolume( const QString& mixdeviceID, bool decrease ) + { + +@@ -646,21 +652,15 @@ void Mixer::increaseOrDecreaseVolume( const QString& mixdeviceID, bool decrease + if (md.get() != 0) + { + Volume& volP=md->playbackVolume(); +- if ( volP.hasVolume() ) { +- long volSpan = volP.volumeSpan(); +- double step = volSpan / Mixer::VOLUME_STEP_DIVISOR; +- if ( step < 1 ) step = 1; +- if ( decrease ) step = -step; +- volP.changeAllVolumes(step); ++ if ( volP.hasVolume() ) ++ { ++ volP.changeAllVolumes(volP.volumeStep(decrease)); + } + + Volume& volC=md->captureVolume(); +- if ( volC.hasVolume() ) { +- long volSpan = volC.volumeSpan(); +- double step = volSpan / Mixer::VOLUME_STEP_DIVISOR; +- if ( step < 1 ) step = 1; +- if ( decrease ) step = -step; +- volC.changeAllVolumes(step); ++ if ( volC.hasVolume() ) ++ { ++ volC.changeAllVolumes(volC.volumeStep(decrease)); + } + + _mixerBackend->writeVolumeToHW(mixdeviceID, md); +diff --git a/core/mixer.h b/core/mixer.h +index 961eed9..97e2775 100644 +--- a/core/mixer.h ++++ b/core/mixer.h +@@ -163,9 +163,6 @@ public: + /// get the actual MixSet + MixSet& getMixSet(); + +- static float VOLUME_STEP_DIVISOR; // The divisor for defining volume control steps (for mouse-wheel, DBUS and Normal step for Sliders ) +- static float VOLUME_PAGESTEP_DIVISOR; // The divisor for defining volume control steps (page-step for sliders) +- + /// DBUS oriented methods + virtual void increaseVolume( const QString& mixdeviceID ); + virtual void decreaseVolume( const QString& mixdeviceID ); +diff --git a/core/volume.cpp b/core/volume.cpp +index 418f5cc..f978709 100644 +--- a/core/volume.cpp ++++ b/core/volume.cpp +@@ -26,6 +26,10 @@ + + #include + ++float Volume::VOLUME_STEP_DIVISOR = 20; ++float Volume::VOLUME_PAGESTEP_DIVISOR = 10; ++ ++ + int Volume::_channelMaskEnum[9] = + { MLEFT, MRIGHT, MCENTER, + MWOOFER, +@@ -62,7 +66,6 @@ Volume::Volume() + _switchType = None; + _isCapture = false; + _chmask = MNONE; +- disallowSwitchDisallowRead = false; + } + + // IIRC we need the default constructor implicitly for a Collection operation +@@ -111,7 +114,6 @@ void Volume::init( ChannelMask chmask, long maxVolume, long minVolume, bool hasS + // a) Physical switches will be updated after start from the hardware. + // b) Emulated virtual/switches will not receive updates from the hardware, so they shouldn't disable the channels. + _switchActivated = true; +- disallowSwitchDisallowRead = false; + } + + QMap Volume::getVolumesWhenActive() const +@@ -122,14 +124,33 @@ QMap Volume::getVolumesWhenActive() const + QMap Volume::getVolumes() const + { + return _volumesL; +-// if ( isSwitchActivated() ) +-// return _volumesL; +-// else +-// { +-// return _volumesMuted; +-// } + } + ++/** ++ * Returns the absolute change to do one "step" for this volume. This is similar to a page step in a slider, ++ * namely a fixed percentage of the range. ++ * One step is the percentage given by 100/VOLUME_STEP_DIVISOR. The ++ * default VOLUME_STEP_DIVISOR is 20, so default change is 5% of the volumeSpan(). ++ * ++ * This method guarantees a minimum absolute change of 1, zero is never returned. ++ * ++ * It is NOT verified, that such a volume change would actually be possible. You might hit the upper or lower bounds ++ * of the volume range. ++ * ++ * ++ * @param decrease true, if you want a volume step that decreases the volume by one page step ++ * @return The volume step. It will be negative if you have used decrease==true ++ * ++ */ ++long Volume::volumeStep(bool decrease) ++{ ++ long inc = volumeSpan() / Volume::VOLUME_STEP_DIVISOR; ++ if ( inc == 0 ) inc = 1; ++ if ( decrease ) inc *= -1; ++ return inc; ++} ++ ++ + // @ compatibility + void Volume::setAllVolumes(long vol) + { +diff --git a/core/volume.h b/core/volume.h +index e6efea4..19d500d 100644 +--- a/core/volume.h ++++ b/core/volume.h +@@ -80,6 +80,8 @@ friend class MixDevice; + + enum VolumeType { PlaybackVT = 0 , CaptureVT = 1 }; + ++ enum VolumeTypeFlag { Playback = 1, Capture = 2, Both = 3 }; ++ + // regular constructor (old, deprecsted) + //Volume( ChannelMask chmask, long maxVolume, long minVolume, bool hasSwitch, bool isCapture ); + // regular constructor +@@ -144,8 +146,11 @@ friend class MixDevice; + static int _channelMaskEnum[9]; + QMap getVolumes() const; + QMap getVolumesWhenActive() const; +- void hasSwitchDisallowRead() { disallowSwitchDisallowRead = true; }; ++ long volumeStep(bool decrease); + ++ static float VOLUME_STEP_DIVISOR; // The divisor for defining volume control steps (for mouse-wheel, DBUS and Normal step for Sliders ) ++ static float VOLUME_PAGESTEP_DIVISOR; // The divisor for defining volume control steps (page-step for sliders) ++ + protected: + long _chmask; + QMap _volumesL; +diff --git a/gui/kmixdockwidget.cpp b/gui/kmixdockwidget.cpp +index 3bda22e..ad8d21b 100644 +--- a/gui/kmixdockwidget.cpp ++++ b/gui/kmixdockwidget.cpp +@@ -342,19 +342,19 @@ KMixDockWidget::trayWheelEvent(int delta,Qt::Orientation wheelOrientation) + return; + + +- Volume &vol = ( md->playbackVolume().hasVolume() ) ? md->playbackVolume() : md->captureVolume(); +- int inc = vol.volumeSpan() / Mixer::VOLUME_STEP_DIVISOR; ++ Volume &vol = ( md->playbackVolume().hasVolume() ) ? md->playbackVolume() : md->captureVolume(); ++ // bko313579 Do not use "delta", as that is setting more related to documents (Editor, Browser). KMix should ++ // simply always use its own VOLUME_STEP_DIVISOR as a base for percentage change. ++ bool decrease = delta < 0; ++ if (wheelOrientation == Qt::Horizontal) // Reverse horizontal scroll: bko228780 ++ decrease = !decrease; ++ long cv = vol.volumeStep(decrease); + +- if ( inc < 1 ) inc = 1; +- +- if (wheelOrientation == Qt::Horizontal) // Reverse horizontal scroll: bko228780 +- delta = -delta; +- +- long int cv = inc * (delta / 120 ); + bool isInactive = vol.isCapture() ? !md->isRecSource() : md->isMuted(); + kDebug() << "Operating on capture=" << vol.isCapture() << ", isInactive=" << isInactive; + if ( cv > 0 && isInactive) +- { // increasing from muted state: unmute and start with a low volume level ++ { ++ // increasing from muted state: unmute and start with a low volume level + if ( vol.isCapture()) + md->setRecSource(true); + else +diff --git a/gui/ksmallslider.cpp b/gui/ksmallslider.cpp +index ee9788d..a681b4f 100644 +--- a/gui/ksmallslider.cpp ++++ b/gui/ksmallslider.cpp +@@ -311,18 +311,20 @@ void KSmallSlider::mouseMoveEvent( QMouseEvent *e ) + void KSmallSlider::wheelEvent( QWheelEvent * qwe) + { + // kDebug(67100) << "KSmallslider::wheelEvent()"; +- int inc = ( maximum() - minimum() ) / Mixer::VOLUME_STEP_DIVISOR; ++ // bko313579 Do not use "delta", as that is setting more related to documents (Editor, Browser). KMix should ++ // simply always use its own VOLUME_STEP_DIVISOR as a base for percentage change. ++ bool decrease = qwe->delta() < 0; ++ if (qwe->orientation() == Qt::Horizontal) // Reverse horizontal scroll: bko228780 ++ decrease = !decrease; ++ ++ int inc = ( maximum() - minimum() ) / Volume::VOLUME_STEP_DIVISOR; + if ( inc < 1) +- inc = 1; ++ inc = 1; + + //kDebug(67100) << "KSmallslider::wheelEvent() inc=" << inc << "delta=" << e->delta(); + int newVal; + +- bool increase = (qwe->delta() > 0); +- if (qwe->orientation() == Qt::Horizontal) // Reverse horizontal scroll: bko228780 +- increase = !increase; +- +- if ( increase ) { ++ if ( !decrease ) { + newVal = QAbstractSlider::value() + inc; + } + else { +diff --git a/gui/mdwslider.cpp b/gui/mdwslider.cpp +index af6e725..7c14673 100644 +--- a/gui/mdwslider.cpp ++++ b/gui/mdwslider.cpp +@@ -548,14 +548,14 @@ void MDWSlider::addSliders( QBoxLayout *volLayout, char type, Volume& vol, QList + QAbstractSlider* slider; + if ( m_small ) + { +- slider = new KSmallSlider( minvol, maxvol, (maxvol-minvol+1) / Mixer::VOLUME_PAGESTEP_DIVISOR, ++ slider = new KSmallSlider( minvol, maxvol, (maxvol-minvol+1) / Volume::VOLUME_PAGESTEP_DIVISOR, + vol.getVolume( vc.chid ), _orientation, this ); + } // small + else { + slider = new VolumeSlider( _orientation, this ); + slider->setMinimum(minvol); + slider->setMaximum(maxvol); +- slider->setPageStep(maxvol / Mixer::VOLUME_PAGESTEP_DIVISOR); ++ slider->setPageStep(maxvol / Volume::VOLUME_PAGESTEP_DIVISOR); + slider->setValue( vol.getVolume( vc.chid ) ); + volumeValues.push_back( vol.getVolume( vc.chid ) ); + +@@ -959,54 +959,67 @@ void MDWSlider::setDisabled( bool value ) + + + /** +- This slot is called on a MouseWheel event. Also it is called by any other +- associated KAction like the context menu. ++ * This slot is called on a Keyboard Shortcut event. + */ + void MDWSlider::increaseVolume() + { +- increaseOrDecreaseVolume(false); ++ increaseOrDecreaseVolume(false, Volume::Both); + } + + /** +- * TOOD This should go to the Volume class, so we can use it anywhere, +- * like mouse wheel, OSD, Keyboard Shortcuts ++ * This slot is called on a Keyboard Shortcut event. + */ +-long MDWSlider::calculateStepIncrement ( Volume&vol, bool decrease ) ++void MDWSlider::decreaseVolume() + { +- long inc = vol.volumeSpan() / Mixer::VOLUME_STEP_DIVISOR; +- if ( inc == 0 ) inc = 1; +- if ( decrease ) inc *= -1; +- return inc; ++ increaseOrDecreaseVolume(true, Volume::Both); + } + +-void MDWSlider::increaseOrDecreaseVolume(bool decrease) ++/** ++ * Increase or decrease all playback and capture channels of the given control. ++ * This method is very similar to Mixer::increaseOrDecreaseVolume(), but it will ++ * auto-unmute on increase. ++ * ++ * @param mixdeviceID The control name ++ * @param decrease true for decrease. false for increase ++ */ ++void MDWSlider::increaseOrDecreaseVolume(bool decrease, Volume::VolumeTypeFlag volumeType) + { +- Volume& volP = m_mixdevice->playbackVolume(); +- long inc = calculateStepIncrement(volP, decrease); ++ kDebug() << "VolumeType=" << volumeType; ++ if (volumeType & Volume::Playback) ++ { ++ kDebug() << "VolumeType=" << volumeType << " p"; ++ Volume& volP = m_mixdevice->playbackVolume(); ++ long inc = volP.volumeStep(decrease); + +- if ( mixDevice()->id() == "PCM:0" ) +- kDebug() << ( decrease ? "decrease by " : "increase by " ) << inc ; ++ if ( mixDevice()->id() == "PCM:0" ) ++ kDebug() << ( decrease ? "decrease by " : "increase by " ) << inc ; + +- if (!decrease && m_mixdevice->isMuted()) +- { +- // increasing from muted state: unmute and start with a low volume level +- if (mixDevice()->id() == "PCM:0") +- kDebug() << "set all to " << inc << "muted old=" << m_mixdevice->isMuted(); ++ if (!decrease && m_mixdevice->isMuted()) ++ { ++ // increasing from muted state: unmute and start with a low volume level ++ if (mixDevice()->id() == "PCM:0") ++ kDebug() << "set all to " << inc << "muted old=" << m_mixdevice->isMuted(); + +- m_mixdevice->setMuted(false); +- volP.setAllVolumes(inc); ++ m_mixdevice->setMuted(false); ++ volP.setAllVolumes(inc); ++ } ++ else ++ { ++ volP.changeAllVolumes(inc); ++ if (mixDevice()->id() == "PCM:0") ++ kDebug() ++ << (decrease ? "decrease by " : "increase by ") << inc; ++ } + } +- else ++ ++ if (volumeType & Volume::Capture) + { +- volP.changeAllVolumes(inc); +- if (mixDevice()->id() == "PCM:0") +- kDebug() +- << (decrease ? "decrease by " : "increase by ") << inc; +- } ++ kDebug() << "VolumeType=" << volumeType << " c"; + +- Volume& volC = m_mixdevice->captureVolume(); +- inc = calculateStepIncrement(volC, decrease); +- volC.changeAllVolumes(inc); ++ Volume& volC = m_mixdevice->captureVolume(); ++ long inc = volC.volumeStep(decrease); ++ volC.changeAllVolumes(inc); ++ } + + // I should possibly not block, as the changes that come back from the Soundcard + // will be ignored (e.g. because of capture groups) +@@ -1017,14 +1030,6 @@ void MDWSlider::increaseOrDecreaseVolume(bool decrease) + // m_view->blockSignals(oldViewBlockSignalState); + } + +-/** +- This slot is called on a MouseWheel event. Also it is called by any other +- associated KAction like the context menu. +- */ +-void MDWSlider::decreaseVolume() +-{ +- increaseOrDecreaseVolume(true); +-} + + + void MDWSlider::moveStreamAutomatic() +@@ -1262,12 +1267,28 @@ bool MDWSlider::eventFilter( QObject* obj, QEvent* e ) + if (qwe->orientation() == Qt::Horizontal) // Reverse horizontal scroll: bko228780 + increase = !increase; + +- if (increase) { +- increaseVolume(); +- } +- else { +- decreaseVolume(); ++ Volume::VolumeTypeFlag volumeType = Volume::Playback; ++ QSlider *slider = static_cast(obj); ++ if (slider != 0) ++ { ++ kDebug(); ++ kDebug(); ++ kDebug() << "----------------------------- Slider is " << slider; ++ // Mouse is over a slider. So lets apply the wheel event to playback or capture only ++ if(m_slidersCapture.contains(slider)) ++ { ++ kDebug() << "Slider is capture " << slider; ++ volumeType = Volume::Capture; ++ } + } ++ ++ increaseOrDecreaseVolume(!increase, volumeType); ++// if (increase) { ++// increaseVolume(); ++// } ++// else { ++// decreaseVolume(); ++// } + + Volume& volP = m_mixdevice->playbackVolume(); + volumeValues.push_back(volP.getVolume(extraData((QAbstractSlider*)obj).getChid())); +diff --git a/gui/mdwslider.h b/gui/mdwslider.h +index 3299859..a9b056f 100644 +--- a/gui/mdwslider.h ++++ b/gui/mdwslider.h +@@ -101,7 +101,7 @@ public slots: + void update(); + void showMoveMenu(); + virtual void showContextMenu( const QPoint &pos = QCursor::pos() ); +- void increaseOrDecreaseVolume(bool arg1); ++ void increaseOrDecreaseVolume(bool arg1, Volume::VolumeTypeFlag volumeType); + VolumeSliderExtraData& extraData(QAbstractSlider *slider); + void addMediaControls(QBoxLayout* arg1); + +@@ -174,8 +174,6 @@ private: + bool m_sliderInWork; + int m_waitForSoundSetComplete; + QList volumeValues; +- +- long calculateStepIncrement ( Volume&vol, bool decrease ); + }; + + #endif +-- +1.8.1.4 + diff --git a/SOURCES/0003-Global-Keyboard-Shortcuts-XF86Audio-now-only-affect-.patch b/SOURCES/0003-Global-Keyboard-Shortcuts-XF86Audio-now-only-affect-.patch new file mode 100644 index 0000000..5292103 --- /dev/null +++ b/SOURCES/0003-Global-Keyboard-Shortcuts-XF86Audio-now-only-affect-.patch @@ -0,0 +1,186 @@ +From ff429cc9fd0889e632a7916b263bed4e4a64d4e1 Mon Sep 17 00:00:00 2001 +From: Christian Esken +Date: Tue, 5 Feb 2013 00:30:57 +0100 +Subject: [PATCH 3/8] Global Keyboard Shortcuts XF86Audio* now only affect + EITHER playback OR capture BUGS: 300783 + +--- + apps/kmix.cpp | 10 ++++------ + core/mixdevice.cpp | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ + core/mixdevice.h | 1 + + gui/mdwslider.cpp | 46 +++++----------------------------------------- + 4 files changed, 58 insertions(+), 47 deletions(-) + +diff --git a/apps/kmix.cpp b/apps/kmix.cpp +index dd2861a..8e1fa5e 100644 +--- a/apps/kmix.cpp ++++ b/apps/kmix.cpp +@@ -1133,12 +1133,10 @@ KMixWindow::increaseOrDecreaseVolume(bool increase) + if (md.get() == 0) + return; // shouldn't happen, but lets play safe + +- md->setMuted(false); +- if (increase) +- mixer->increaseVolume(md->id()); // this is awkward. Better move the increaseVolume impl to the Volume class. +- else +- mixer->decreaseVolume(md->id()); +- // md->playbackVolume().increase(); // not yet implemented ++ Volume::VolumeTypeFlag volumeType = md->playbackVolume().hasVolume() ? Volume::Playback : Volume::Capture; ++ md->increaseOrDecreaseVolume(!increase, volumeType); ++ md->mixer()->commitVolumeChange(md); ++ + showVolumeDisplay(); + } + +diff --git a/core/mixdevice.cpp b/core/mixdevice.cpp +index 662fd85..06b883e 100644 +--- a/core/mixdevice.cpp ++++ b/core/mixdevice.cpp +@@ -164,6 +164,54 @@ shared_ptr MixDevice::addToPool() + + + /** ++ * Changes the internal state of this MixDevice. ++ * It does not commit the change to the hardware. ++ * ++ * You might want to call something like m_mixdevice->mixer()->commitVolumeChange(m_mixdevice); after calling this method. ++ */ ++void MixDevice::increaseOrDecreaseVolume(bool decrease, Volume::VolumeTypeFlag volumeType) ++{ ++ bool debugme = id() == "PCM:0" ; ++ if (volumeType & Volume::Playback) ++ { ++ kDebug() << "VolumeType=" << volumeType << " p"; ++ Volume& volP = playbackVolume(); ++ long inc = volP.volumeStep(decrease); ++ ++ if (debugme) ++ kDebug() << ( decrease ? "decrease by " : "increase by " ) << inc ; ++ ++ if (!decrease && isMuted()) ++ { ++ // increasing from muted state: unmute and start with a low volume level ++ if (debugme) ++ kDebug() << "set all to " << inc << "muted old=" << isMuted(); ++ ++ setMuted(false); ++ volP.setAllVolumes(inc); ++ } ++ else ++ { ++ volP.changeAllVolumes(inc); ++ if (debugme) ++ kDebug() << (decrease ? "decrease by " : "increase by ") << inc; ++ } ++ } ++ ++ if (volumeType & Volume::Capture) ++ { ++ kDebug() << "VolumeType=" << volumeType << " c"; ++ ++ Volume& volC = captureVolume(); ++ long inc = volC.volumeStep(decrease); ++ volC.changeAllVolumes(inc); ++ } ++ ++} ++ ++ ++ ++/** + * Returns the name of the config group + * @param Prefix of the group, e.g. "View_ALSA_USB_01" + * @returns The config group name in the format "prefix.mixerId,controlId" +diff --git a/core/mixdevice.h b/core/mixdevice.h +index 556f809..177c3b2 100644 +--- a/core/mixdevice.h ++++ b/core/mixdevice.h +@@ -216,6 +216,7 @@ public: + bool write( KConfig *config, const QString& grp ); + int getUserfriendlyVolumeLevel(); + ++ void increaseOrDecreaseVolume(bool decrease, Volume::VolumeTypeFlag volumeType); + + + protected: +diff --git a/gui/mdwslider.cpp b/gui/mdwslider.cpp +index 7c14673..5572d63 100644 +--- a/gui/mdwslider.cpp ++++ b/gui/mdwslider.cpp +@@ -959,7 +959,8 @@ void MDWSlider::setDisabled( bool value ) + + + /** +- * This slot is called on a Keyboard Shortcut event. ++ * This slot is called on a Keyboard Shortcut event, except for the XF86Audio* shortcuts which hare handeled by the ++ * KMixWindow class. So for 99.9% of all users, this methos is never called. + */ + void MDWSlider::increaseVolume() + { +@@ -967,7 +968,8 @@ void MDWSlider::increaseVolume() + } + + /** +- * This slot is called on a Keyboard Shortcut event. ++ * This slot is called on a Keyboard Shortcut event, except for the XF86Audio* shortcuts which hare handeled by the ++ * KMixWindow class. So for 99.9% of all users, this methos is never called. + */ + void MDWSlider::decreaseVolume() + { +@@ -984,43 +986,7 @@ void MDWSlider::decreaseVolume() + */ + void MDWSlider::increaseOrDecreaseVolume(bool decrease, Volume::VolumeTypeFlag volumeType) + { +- kDebug() << "VolumeType=" << volumeType; +- if (volumeType & Volume::Playback) +- { +- kDebug() << "VolumeType=" << volumeType << " p"; +- Volume& volP = m_mixdevice->playbackVolume(); +- long inc = volP.volumeStep(decrease); +- +- if ( mixDevice()->id() == "PCM:0" ) +- kDebug() << ( decrease ? "decrease by " : "increase by " ) << inc ; +- +- if (!decrease && m_mixdevice->isMuted()) +- { +- // increasing from muted state: unmute and start with a low volume level +- if (mixDevice()->id() == "PCM:0") +- kDebug() << "set all to " << inc << "muted old=" << m_mixdevice->isMuted(); +- +- m_mixdevice->setMuted(false); +- volP.setAllVolumes(inc); +- } +- else +- { +- volP.changeAllVolumes(inc); +- if (mixDevice()->id() == "PCM:0") +- kDebug() +- << (decrease ? "decrease by " : "increase by ") << inc; +- } +- } +- +- if (volumeType & Volume::Capture) +- { +- kDebug() << "VolumeType=" << volumeType << " c"; +- +- Volume& volC = m_mixdevice->captureVolume(); +- long inc = volC.volumeStep(decrease); +- volC.changeAllVolumes(inc); +- } +- ++ m_mixdevice->increaseOrDecreaseVolume(decrease, volumeType); + // I should possibly not block, as the changes that come back from the Soundcard + // will be ignored (e.g. because of capture groups) + // kDebug() << "MDWSlider is blocking signals for " << m_view->id(); +@@ -1030,8 +996,6 @@ void MDWSlider::increaseOrDecreaseVolume(bool decrease, Volume::VolumeTypeFlag v + // m_view->blockSignals(oldViewBlockSignalState); + } + +- +- + void MDWSlider::moveStreamAutomatic() + { + m_mixdevice->mixer()->moveStream(m_mixdevice->id(), ""); +-- +1.8.1.4 + diff --git a/SOURCES/0004-memoryLeak.patch b/SOURCES/0004-memoryLeak.patch new file mode 100644 index 0000000..b81e175 --- /dev/null +++ b/SOURCES/0004-memoryLeak.patch @@ -0,0 +1,50 @@ +From 9b726962fddf04135afd32e99ac07929d36bc1c7 Mon Sep 17 00:00:00 2001 +From: Aaron Seigo +Date: Tue, 5 Feb 2013 12:59:40 +0100 +Subject: [PATCH 4/8] --memoryLeak; + +it's a minor one, though; so i wouldn't worry about backporting it to 4.10. +--- + gui/osdwidget.cpp | 19 +++++++++---------- + 1 file changed, 9 insertions(+), 10 deletions(-) + +diff --git a/gui/osdwidget.cpp b/gui/osdwidget.cpp +index 58f87a7..72b8d9c 100644 +--- a/gui/osdwidget.cpp ++++ b/gui/osdwidget.cpp +@@ -31,7 +31,7 @@ + #include + #include + #include +-#include ++#include + #include + #include + #include +@@ -151,15 +151,14 @@ void OSDWidget::themeUpdated() + if (!Plasma::Theme::defaultTheme()->imagePath("icons/audio").isEmpty()) { + QFontMetrics fm(m_volumeLabel->font()); + iconSize = QSize(fm.height(), fm.height()); +- // Leak | low prio | The old Plasma::Svg is not freed on a themeUpdated(), also it is not freed in the destructor +- Plasma::Svg *svgIcon = new Plasma::Svg(this); +- svgIcon->setImagePath("icons/audio"); +- svgIcon->setContainsMultipleImages(true); +- svgIcon->resize(iconSize); +- m_volumeHighPixmap = svgIcon->pixmap("audio-volume-high"); +- m_volumeMediumPixmap = svgIcon->pixmap("audio-volume-medium"); +- m_volumeLowPixmap = svgIcon->pixmap("audio-volume-low"); +- m_volumeMutedPixmap = svgIcon->pixmap("audio-volume-muted"); ++ Plasma::Svg svgIcon; ++ svgIcon.setImagePath("icons/audio"); ++ svgIcon.setContainsMultipleImages(true); ++ svgIcon.resize(iconSize); ++ m_volumeHighPixmap = svgIcon.pixmap("audio-volume-high"); ++ m_volumeMediumPixmap = svgIcon.pixmap("audio-volume-medium"); ++ m_volumeLowPixmap = svgIcon.pixmap("audio-volume-low"); ++ m_volumeMutedPixmap = svgIcon.pixmap("audio-volume-muted"); + } else { + iconSize = QSize(KIconLoader::SizeSmallMedium, KIconLoader::SizeSmallMedium); + m_volumeHighPixmap = KIcon( QLatin1String( "audio-volume-high" )).pixmap(iconSize); +-- +1.8.1.4 + diff --git a/SOURCES/0005-Less-debugging-output.patch b/SOURCES/0005-Less-debugging-output.patch new file mode 100644 index 0000000..ab25361 --- /dev/null +++ b/SOURCES/0005-Less-debugging-output.patch @@ -0,0 +1,26 @@ +From 71698628d49d235b0bca5935ef37230e9b550e53 Mon Sep 17 00:00:00 2001 +From: Christian Esken +Date: Tue, 2 Jul 2013 00:33:06 +0200 +Subject: [PATCH 1/4] Less debugging output + +--- + backends/mixer_backend.cpp | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/backends/mixer_backend.cpp b/backends/mixer_backend.cpp +index 46047ca..f4758d6 100644 +--- a/backends/mixer_backend.cpp ++++ b/backends/mixer_backend.cpp +@@ -170,7 +170,8 @@ void Mixer_Backend::readSetFromHW() + + foreach (shared_ptr md, m_mixDevices ) + { +- bool debugMe = (md->id() == "PCM:0" ); ++ //bool debugMe = (md->id() == "PCM:0" ); ++ bool debugMe = false; + if (debugMe) kDebug() << "Old PCM:0 playback state" << md->isMuted() + << ", vol=" << md->playbackVolume().getAvgVolumePercent(Volume::MALL); + +-- +1.8.3.1 + diff --git a/SOURCES/0006-Revert-my-direct-use-of-ControlManager-instance-.ann.patch b/SOURCES/0006-Revert-my-direct-use-of-ControlManager-instance-.ann.patch new file mode 100644 index 0000000..8c4a55b --- /dev/null +++ b/SOURCES/0006-Revert-my-direct-use-of-ControlManager-instance-.ann.patch @@ -0,0 +1,106 @@ +From 6edddf7c8e92bb499cb60fdee53622cab326f334 Mon Sep 17 00:00:00 2001 +From: Christian Esken +Date: Tue, 2 Jul 2013 00:44:51 +0200 +Subject: [PATCH 2/4] Revert my direct use of + ControlManager::instance().announce(). PA actually did call it in its own + thread and so in the end we have a non-GUI thread calling GUI code. Use + invokeMethod() again. CCBUGS: 309464 + +--- + backends/mixer_pulse.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++++++ + backends/mixer_pulse.h | 7 +++++++ + 2 files changed, 54 insertions(+) + +diff --git a/backends/mixer_pulse.cpp b/backends/mixer_pulse.cpp +index e08a881..5202b5a 100644 +--- a/backends/mixer_pulse.cpp ++++ b/backends/mixer_pulse.cpp +@@ -769,9 +769,55 @@ static devmap* get_widget_map(int type, int index) + + void Mixer_PULSE::emitControlsReconfigured() + { ++ // emit controlsReconfigured(_mixer->id()); ++ // Do not emit directly to ensure all connected slots are executed ++ // in their own event loop. ++ ++ /* ++ * Comment by cesken: I am not really sure what the comment above means. ++ * 1) IIRC coling told me "otherwise KMix crashes". ++ * 2) There are also bug reports that heavily indicate the crash when operation the "move stream" from a popup ++ * menu. ++ * 3) I don't know what the "executed in their own event loop" means. Are we in a "wrong" thread here (PA), ++ * which is not suitable for GUI code?!? ++ * ++ * Conclusions: ++ * a) It seems there seems to be some object deletion hazard with a QMenu (the one for "move stream") ++ * b) I do not see why executing it Queued is better, because you can never know when it is actually being ++ * executed: it could be "right now". It looks like Qt currently executes it after the QMenu hazard has ++ * resolved itselves miracously. ++ * c) I am definitely strongly opposed on this "execute later" approach. It is pure gambling IMO and might be ++ * broken any time (from DEBUG to RELEASE build, or by a new Qt or KDE version). ++ * ++ * TODO Somebody with more Qt and PA internal insight might help to clear up things here. ++ * ++ * Temporary solution: Do the QueuedConnection until we really know hat is going on. But the called code ++ * pulseControlsReconfigured() will then do the standard announce() so that every part of ++ * KMix automatically gets updated. ++ * ++ */ ++ QMetaObject::invokeMethod(this, ++ "pulseControlsReconfigured", ++ Qt::QueuedConnection); ++ ++// QMetaObject::invokeMethod(this, ++// "pulseControlsReconfigured", ++// Qt::QueuedConnection, ++// Q_ARG(QString, _mixer->id())); ++} ++ ++void Mixer_PULSE::pulseControlsReconfigured() ++{ ++ kDebug() << "Reconfigure " << _mixer->id(); + ControlManager::instance().announce(_mixer->id(), ControlChangeType::ControlList, getDriverName()); + } + ++void Mixer_PULSE::pulseControlsReconfigured(QString mixerId) ++{ ++ kDebug() << "Reconfigure " << mixerId; ++ ControlManager::instance().announce(mixerId, ControlChangeType::ControlList, getDriverName()); ++} ++ + void Mixer_PULSE::addWidget(int index, bool isAppStream) + { + devmap* map = get_widget_map(m_devnum, index); +@@ -1366,3 +1412,4 @@ QString Mixer_PULSE::getDriverName() + return "PulseAudio"; + } + ++#include "mixer_pulse.moc" +diff --git a/backends/mixer_pulse.h b/backends/mixer_pulse.h +index ac65534..ef2050c 100644 +--- a/backends/mixer_pulse.h ++++ b/backends/mixer_pulse.h +@@ -45,6 +45,8 @@ typedef struct { + + class Mixer_PULSE : public Mixer_Backend + { ++ Q_OBJECT ++ + public: + Mixer_PULSE(Mixer *mixer, int devnum); + virtual ~Mixer_PULSE(); +@@ -77,6 +79,11 @@ class Mixer_PULSE : public Mixer_Backend + void addDevice(devinfo& dev, bool = false); + bool connectToDaemon(); + void emitControlsReconfigured(); ++ ++ protected slots: ++ void pulseControlsReconfigured(QString mixerId); ++ void pulseControlsReconfigured(); ++ + public: + void reinit(); + +-- +1.8.3.1 + diff --git a/SPECS/kmix.spec b/SPECS/kmix.spec new file mode 100644 index 0000000..f08983a --- /dev/null +++ b/SPECS/kmix.spec @@ -0,0 +1,196 @@ +Name: kmix +Summary: KDE volume control +Version: 4.10.5 +Release: 4%{?dist} + +# code is LGPLv2+ except for gui/osdwidget.* which is GPLv2+ +# docs GFDL +License: GPLv2+ and GFDL +URL: https://projects.kde.org/projects/kde/kdemultimedia/%{name} +%global revision %(echo %{version} | cut -d. -f3) +%if %{revision} >= 50 +%global stable unstable +%else +%global stable stable +%endif +Source0: http://download.kde.org/%{stable}/%{version}/src/%{name}-%{version}.tar.xz + +## upstream patches +# pull a few from master/ branch, not officially backported +Patch101: 0001-MPRIS2-backend-now-does-a-asynchonous-DBUS-instrospe.patch +Patch102: 0002-Use-a-fixed-volume-step-of-n-instead-of-honoring-mou.patch +Patch103: 0003-Global-Keyboard-Shortcuts-XF86Audio-now-only-affect-.patch +Patch104: 0004-memoryLeak.patch +Patch105: 0005-Less-debugging-output.patch +Patch106: 0006-Revert-my-direct-use-of-ControlManager-instance-.ann.patch + +BuildRequires: desktop-file-utils +BuildRequires: kdelibs4-devel >= %{version} +BuildRequires: pkgconfig(alsa) +BuildRequires: pkgconfig(libcanberra) +BuildRequires: pkgconfig(libpulse) pkgconfig(libpulse-mainloop-glib) +BuildRequires: pkgconfig(phonon) + +Requires: kde-runtime%{?_kde4_version: >= %{_kde4_version}} + +# when split occurred +Obsoletes: kdemultimedia-kmix < 6:4.8.80 +Provides: kdemultimedia-kmix = 6:%{version}-%{release} + + +%description +%{summary}. + + +%prep +%setup -q + +%patch101 -p1 -b .0001 +%patch102 -p1 -b .0002 +%patch103 -p1 -b .0003 +%patch104 -p1 -b .0004 +%patch105 -p1 -b .0005 +%patch106 -p1 -b .0006 + + +%build +mkdir -p %{_target_platform} +pushd %{_target_platform} +%{cmake_kde4} .. +popd + +make %{?_smp_mflags} -C %{_target_platform} + + +%install +make install/fast DESTDIR=%{buildroot} -C %{_target_platform} + +# fix documentation multilib conflict in index.cache +bunzip2 %{buildroot}%{_kde4_docdir}/HTML/en/kmix/index.cache.bz2 +sed -i -e 's!name="id[a-z]*[0-9]*"!!g' %{buildroot}%{_kde4_docdir}/HTML/en/kmix/index.cache +sed -i -e 's!#id[a-z]*[0-9]*"!!g' %{buildroot}%{_kde4_docdir}/HTML/en/kmix/index.cache +bzip2 -9 %{buildroot}%{_kde4_docdir}/HTML/en/kmix/index.cache + +%find_lang %{name} --with-kde --without-mo + + +%check +desktop-file-validate %{buildroot}%{_kde4_datadir}/applications/kde4/kmix.desktop + + +%post +touch --no-create %{_kde4_iconsdir}/hicolor &> /dev/null || : + +%posttrans +gtk-update-icon-cache %{_kde4_iconsdir}/hicolor &> /dev/null || : + +%postun +if [ $1 -eq 0 ] ; then +touch --no-create %{_kde4_iconsdir}/hicolor &> /dev/null || : +gtk-update-icon-cache %{_kde4_iconsdir}/hicolor &> /dev/null || : +fi + +%files -f %{name}.lang +%doc AUTHORS ChangeLog COPYING COPYING.DOC TODO +%{_kde4_appsdir}/kmix/ +%{_kde4_appsdir}/plasma/services/mixer.operations +%{_kde4_bindir}/kmix +%{_kde4_bindir}/kmixctrl +%{_kde4_datadir}/applications/kde4/kmix.desktop +%{_kde4_datadir}/autostart/*.desktop +%{_kde4_datadir}/dbus-1/interfaces/org.kde.kmix.control.xml +%{_kde4_datadir}/dbus-1/interfaces/org.kde.kmix.mixer.xml +%{_kde4_datadir}/dbus-1/interfaces/org.kde.kmix.mixset.xml +%{_kde4_datadir}/kde4/services/kded/kmixd.desktop +%{_kde4_datadir}/kde4/services/kmixctrl_restore.desktop +%{_kde4_datadir}/kde4/services/plasma-engine-mixer.desktop +%{_kde4_iconsdir}/hicolor/*/*/kmix.* +%{_kde4_libdir}/libkdeinit4_kmix.so +%{_kde4_libdir}/libkdeinit4_kmixctrl.so +%{_kde4_libdir}/kde4/kded_kmixd.so +%{_kde4_libdir}/kde4/plasma_engine_mixer.so + + +%changelog +* Fri Jan 24 2014 Daniel Mach - 4.10.5-4 +- Mass rebuild 2014-01-24 + +* Fri Dec 27 2013 Daniel Mach - 4.10.5-3 +- Mass rebuild 2013-12-27 + +* Thu Jul 11 2013 Rex Dieter 4.10.5-2 +- backport memleak-on-track-change fix (kde#309464, #912457) + +* Sun Jun 30 2013 Than Ngo - 4.10.5-1 +- 4.10.5 + +* Sat Jun 01 2013 Rex Dieter - 4.10.4-1 +- 4.10.4 + +* Mon May 06 2013 Than Ngo - 4.10.3-1 +- 4.10.3 + +* Thu Apr 25 2013 Than Ngo - 4.10.2-3 +- fix multilib issue + +* Wed Apr 17 2013 Rex Dieter - 4.10.2-2 +- backport some patches from master branch + +* Mon Apr 01 2013 Rex Dieter - 4.10.2-1 +- 4.10.2 + +* Mon Mar 18 2013 Rex Dieter - 4.10.1-2 +- pull a few upstream patches, in particular... +- kmix crahes moving streams between sinks (#922849) + +* Sat Mar 02 2013 Rex Dieter - 4.10.1-1 +- 4.10.1 + +* Fri Feb 01 2013 Rex Dieter - 4.10.0-1 +- 4.10.0 + +* Tue Jan 22 2013 Rex Dieter - 4.9.98-1 +- 4.9.98 + +* Fri Jan 04 2013 Rex Dieter - 4.9.97-1 +- 4.9.97 + +* Thu Dec 20 2012 Rex Dieter - 4.9.95-1 +- 4.9.95 + +* Tue Dec 04 2012 Rex Dieter - 4.9.90-1 +- 4.9.90 + +* Mon Dec 03 2012 Than Ngo - 4.9.4-1 +- 4.9.4 + +* Thu Nov 22 2012 Than Ngo - 4.9.3-2 +- fix license issue + +* Sat Nov 03 2012 Rex Dieter - 4.9.3-1 +- 4.9.3 + +* Sat Sep 29 2012 Rex Dieter - 4.9.2-1 +- 4.9.2 + +* Mon Sep 03 2012 Than Ngo - 4.9.1-1 +- 4.9.1 + +* Thu Jul 26 2012 Lukas Tinkl - 4.9.0-1 +- 4.9.0 + +* Thu Jul 19 2012 Fedora Release Engineering - 4.8.97-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Wed Jul 11 2012 Rex Dieter - 4.8.97-1 +- 4.8.97 + +* Thu Jun 28 2012 Rex Dieter - 4.8.95-1 +- 4.8.95 + +* Wed Jun 13 2012 Rex Dieter 4.8.90-2 +- License: GPLv2+ and GFDL + +* Fri Jun 08 2012 Rex Dieter 4.8.90-1 +- kmix-4.8.90 +