File indexing completed on 2024-04-14 05:39:36

0001 // SPDX-License-Identifier: GPL-3.0-or-later
0002 /*
0003   Copyright 2017 - 2023 Martin Koller, kollix@aon.at
0004 
0005   This file is part of liquidshell.
0006 
0007   liquidshell is free software: you can redistribute it and/or modify
0008   it under the terms of the GNU General Public License as published by
0009   the Free Software Foundation, either version 3 of the License, or
0010   (at your option) any later version.
0011 
0012   liquidshell is distributed in the hope that it will be useful,
0013   but WITHOUT ANY WARRANTY; without even the implied warranty of
0014   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0015   GNU General Public License for more details.
0016 
0017   You should have received a copy of the GNU General Public License
0018   along with liquidshell.  If not, see <http://www.gnu.org/licenses/>.
0019 */
0020 
0021 #include <OnScreenVolume.hxx>
0022 
0023 #include <QDBusConnection>
0024 #include <QDBusConnectionInterface>
0025 #include <QDBusMessage>
0026 #include <QDBusReply>
0027 #include <QDBusServiceWatcher>
0028 #include <QRegularExpression>
0029 #include <QApplication>
0030 #include <QScreen>
0031 #include <QDebug>
0032 
0033 #include <KWinCompat.hxx>
0034 #include <KConfig>
0035 #include <KConfigGroup>
0036 
0037 //--------------------------------------------------------------------------------
0038 
0039 OnScreenVolume::OnScreenVolume(QWidget *parent)
0040   : QProgressBar(parent)
0041 {
0042   setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowDoesNotAcceptFocus);
0043 
0044   setFixedSize(400, 40);
0045   hide();
0046 
0047   KWindowSystem::setState(winId(), NET::KeepAbove);
0048   KWindowSystem::setType(winId(), NET::Dock);
0049   KWinCompat::setOnAllDesktops(winId(), true);
0050 
0051   hideTimer.setInterval(1000);
0052   hideTimer.setSingleShot(true);
0053   connect(&hideTimer, &QTimer::timeout, this, &QWidget::hide);
0054 
0055   QDBusConnection::sessionBus()
0056       .connect("org.kde.kmix", "/Mixers",
0057                "org.kde.KMix.MixSet", "masterChanged",
0058                this, SLOT(getMasterMixer()));
0059 
0060   if ( !QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.kmix") )
0061   {
0062     QDBusServiceWatcher *w = new QDBusServiceWatcher("org.kde.kmix", QDBusConnection::sessionBus(),
0063                                                      QDBusServiceWatcher::WatchForRegistration, this);
0064     connect(w, &QDBusServiceWatcher::serviceRegistered, this, &OnScreenVolume::getMasterMixer);
0065   }
0066   else
0067     getMasterMixer();
0068 }
0069 
0070 //--------------------------------------------------------------------------------
0071 
0072 void OnScreenVolume::getMasterMixer()
0073 {
0074   QDBusMessage msg = QDBusMessage::createMethodCall("org.kde.kmix", "/Mixers",
0075                                                     "org.freedesktop.DBus.Properties",
0076                                                     "GetAll");
0077   msg << "org.kde.KMix.MixSet";
0078 
0079   QDBusConnection::sessionBus()
0080       .callWithCallback(msg, this,
0081                         SLOT(gotMasterMixer(QDBusMessage)),
0082                         SLOT(gotMasterMixerError(QDBusError, QDBusMessage)));
0083 }
0084 
0085 //--------------------------------------------------------------------------------
0086 
0087 void OnScreenVolume::gotMasterMixerError(QDBusError error, QDBusMessage msg)
0088 {
0089   Q_UNUSED(error)
0090   Q_UNUSED(msg)
0091 
0092   if ( retryTimer.interval() == 0 )
0093   {
0094     // the service could already be registered but no object yet
0095     retryTimer.setInterval(1000);
0096     retryTimer.setSingleShot(true);
0097     connect(&retryTimer, &QTimer::timeout, this, &OnScreenVolume::getMasterMixer);
0098   }
0099   retryTimer.start();
0100 }
0101 
0102 //--------------------------------------------------------------------------------
0103 
0104 void OnScreenVolume::gotMasterMixer(QDBusMessage msg)
0105 {
0106   QDBusReply<QVariantMap> reply = msg;
0107 
0108   if ( !reply.isValid() )
0109     return;
0110 
0111   if ( !masterMixer.isEmpty() )
0112   {
0113     // disconnect previous master mixer
0114     QDBusConnection::sessionBus()
0115         .disconnect("org.kde.kmix", "/Mixers/" + masterMixer,
0116                     "org.kde.KMix.Mixer", "controlChanged",
0117                     this, SLOT(controlChanged()));
0118   }
0119 
0120   masterMixer = reply.value()["currentMasterMixer"].toString();
0121   masterMixer.replace(QRegularExpression("[^a-zA-Z0-9_]"), "_");
0122 
0123   //qDebug() << __FUNCTION__ << masterMixer;
0124 
0125   QDBusConnection::sessionBus()
0126       .connect("org.kde.kmix", "/Mixers/" + masterMixer,
0127                "org.kde.KMix.Mixer", "controlChanged",
0128                this, SLOT(controlChanged()));
0129 }
0130 
0131 //--------------------------------------------------------------------------------
0132 // called when e.g. volume changes but also if currentMasterControl changes
0133 // e.g. bluetooth headset connect/disconnect
0134 
0135 void OnScreenVolume::controlChanged()
0136 {
0137   QDBusMessage msg =
0138       QDBusMessage::createMethodCall("org.kde.kmix", "/Mixers",
0139                                      "org.freedesktop.DBus.Properties",
0140                                      "Get");
0141 
0142   msg << "org.kde.KMix.MixSet" << "currentMasterControl";
0143 
0144   QDBusConnection::sessionBus().callWithCallback(msg, this, SLOT(gotCurrentControl(QDBusMessage)));
0145 }
0146 
0147 //--------------------------------------------------------------------------------
0148 
0149 void OnScreenVolume::gotCurrentControl(QDBusMessage reply)
0150 {
0151   if ( reply.type() == QDBusMessage::ErrorMessage )
0152     return;
0153 
0154   QString masterControl = reply.arguments()[0].value<QDBusVariant>().variant().toString();
0155   masterControl.replace(QRegularExpression("[^a-zA-Z0-9_]"), "_");
0156 
0157   //qDebug() << __FUNCTION__ << masterMixer << masterControl;
0158 
0159   QDBusMessage msg =
0160       QDBusMessage::createMethodCall("org.kde.kmix", "/Mixers/" + masterMixer + '/' + masterControl,
0161                                      "org.freedesktop.DBus.Properties",
0162                                      "Get");
0163 
0164   msg << "org.kde.KMix.Control" << "volume";
0165 
0166   QDBusConnection::sessionBus().callWithCallback(msg, this, SLOT(volumeChanged(QDBusMessage)));
0167 }
0168 
0169 //--------------------------------------------------------------------------------
0170 
0171 void OnScreenVolume::volumeChanged(QDBusMessage reply)
0172 {
0173   if ( reply.type() == QDBusMessage::ErrorMessage )
0174     return;
0175 
0176   if ( reply.arguments().count() && (reply.arguments()[0].value<QDBusVariant>().variant().toInt() != value()) )
0177   {
0178     setValue(reply.arguments()[0].value<QDBusVariant>().variant().toInt());
0179 
0180     if ( !isVisible() )  // just always once before showing
0181     {
0182       KConfig config("kmixrc");
0183       KConfigGroup group = config.group("Global");
0184       if ( !group.readEntry("showOSD", true) )
0185         return;
0186     }
0187 
0188     move((QApplication::primaryScreen()->size().width() - width()) / 2,
0189           QApplication::primaryScreen()->size().height() * 0.8);
0190 
0191     show();
0192     hideTimer.start();
0193   }
0194 }
0195 
0196 //--------------------------------------------------------------------------------