File indexing completed on 2024-04-21 04:56:52

0001 /**
0002  * SPDX-FileCopyrightText: 2013 Albert Vaca <albertvaka@gmail.com>
0003  * SPDX-FileCopyrightText: 2019 Piyush Aggarwal <piyushaggarwal002@gmail.com>
0004  *
0005  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006  */
0007 
0008 #include "pausemusicplugin-win.h"
0009 
0010 #include "plugin_pausemusic_debug.h"
0011 #include <KPluginFactory>
0012 
0013 #include <Functiondiscoverykeys_devpkey.h>
0014 
0015 K_PLUGIN_CLASS_WITH_JSON(PauseMusicPlugin, "kdeconnect_pausemusic.json")
0016 
0017 PauseMusicPlugin::PauseMusicPlugin(QObject *parent, const QVariantList &args)
0018     : KdeConnectPlugin(parent, args)
0019     , sessionManager(GlobalSystemMediaTransportControlsSessionManager::RequestAsync().get())
0020 {
0021     CoInitialize(nullptr);
0022     deviceEnumerator = nullptr;
0023     HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (LPVOID *)&deviceEnumerator);
0024     valid = (hr == S_OK);
0025     if (!valid) {
0026         qWarning("Initialization failed: Failed to create MMDeviceEnumerator");
0027         qWarning("Error Code: %lx", hr);
0028     }
0029 }
0030 
0031 bool PauseMusicPlugin::updateSinksList()
0032 {
0033     sinksList.clear();
0034     if (!valid)
0035         return false;
0036 
0037     IMMDeviceCollection *devices = nullptr;
0038     HRESULT hr = deviceEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &devices);
0039 
0040     if (hr != S_OK) {
0041         qWarning("Failed to Enumumerate AudioEndpoints");
0042         qWarning("Error Code: %lx", hr);
0043         return false;
0044     }
0045 
0046     unsigned int deviceCount;
0047     devices->GetCount(&deviceCount);
0048 
0049     for (unsigned int i = 0; i < deviceCount; i++) {
0050         IMMDevice *device = nullptr;
0051 
0052         IPropertyStore *deviceProperties = nullptr;
0053         PROPVARIANT deviceProperty;
0054         QString name;
0055 
0056         IAudioEndpointVolume *endpoint = nullptr;
0057 
0058         // Get Properties
0059         devices->Item(i, &device);
0060         device->OpenPropertyStore(STGM_READ, &deviceProperties);
0061 
0062         deviceProperties->GetValue(PKEY_Device_FriendlyName, &deviceProperty);
0063         name = QString::fromWCharArray(deviceProperty.pwszVal);
0064         // PropVariantClear(&deviceProperty);
0065 
0066         hr = device->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, (void **)&endpoint);
0067         if (hr != S_OK) {
0068             qWarning() << "Failed to create IAudioEndpointVolume for device:" << name;
0069             qWarning("Error Code: %lx", hr);
0070 
0071             device->Release();
0072             continue;
0073         }
0074 
0075         // Register Callback
0076         if (!sinksList.contains(name)) {
0077             sinksList[name] = endpoint;
0078         }
0079         device->Release();
0080     }
0081     devices->Release();
0082     return true;
0083 }
0084 
0085 void PauseMusicPlugin::updatePlayersList()
0086 {
0087     playersList.clear();
0088     auto sessions = sessionManager.GetSessions();
0089     for (uint32_t i = 0; i < sessions.Size(); i++) {
0090         const auto player = sessions.GetAt(i);
0091         auto playerName = player.SourceAppUserModelId();
0092 
0093         QString uniqueName = QString::fromWCharArray(playerName.c_str());
0094         for (int i = 2; playersList.contains(uniqueName); ++i) {
0095             uniqueName += QStringLiteral(" [") + QString::number(i) + QStringLiteral("]");
0096         }
0097         playersList.insert(uniqueName, player);
0098     }
0099 }
0100 
0101 PauseMusicPlugin::~PauseMusicPlugin()
0102 {
0103     CoUninitialize();
0104 }
0105 
0106 void PauseMusicPlugin::receivePacket(const NetworkPacket &np)
0107 {
0108     bool pauseOnlyWhenTalking = config()->getBool(QStringLiteral("conditionTalking"), false);
0109 
0110     if (pauseOnlyWhenTalking) {
0111         if (np.get<QString>(QStringLiteral("event")) != QLatin1String("talking")) {
0112             return;
0113         }
0114     } else {
0115         if (np.get<QString>(QStringLiteral("event")) != QLatin1String("ringing") && np.get<QString>(QStringLiteral("event")) != QLatin1String("talking")) {
0116             return;
0117         }
0118     }
0119 
0120     bool pauseConditionFulfilled = !np.get<bool>(QStringLiteral("isCancel"));
0121 
0122     bool pause = config()->getBool(QStringLiteral("actionPause"), true);
0123     bool mute = config()->getBool(QStringLiteral("actionMute"), false);
0124 
0125     const bool autoResume = config()->getBool(QStringLiteral("actionResume"), true);
0126 
0127     if (pauseConditionFulfilled) {
0128         if (mute) {
0129             qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Muting all the unmuted sinks";
0130             this->updateSinksList();
0131             QHashIterator<QString, IAudioEndpointVolume *> sinksIterator(sinksList);
0132             while (sinksIterator.hasNext()) {
0133                 sinksIterator.next();
0134                 BOOL muted;
0135                 sinksIterator.value()->GetMute(&muted);
0136                 if (!((bool)muted)) {
0137                     qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Trying to mute " << sinksIterator.key();
0138                     if (sinksIterator.value()->SetMute(true, NULL) == S_OK) {
0139                         qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Muted " << sinksIterator.key();
0140                         mutedSinks.insert(sinksIterator.key());
0141                     }
0142                 }
0143             }
0144         }
0145 
0146         if (pause) {
0147             qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Pausing all the playing media";
0148             this->updatePlayersList();
0149             QHashIterator<QString, GlobalSystemMediaTransportControlsSession> playersIterator(playersList);
0150             while (playersIterator.hasNext()) {
0151                 playersIterator.next();
0152                 auto &player = playersIterator.value();
0153                 auto &playerName = playersIterator.key();
0154 
0155                 auto playbackInfo = player.GetPlaybackInfo();
0156                 auto playbackControls = playbackInfo.Controls();
0157                 if (playbackInfo.PlaybackStatus() == GlobalSystemMediaTransportControlsSessionPlaybackStatus::Playing) {
0158                     if (playbackControls.IsPauseEnabled()) {
0159                         qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Trying to pause " << playerName;
0160                         if (player.TryPauseAsync().get()) {
0161                             qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Paused " << playerName;
0162                             pausedSources.insert(playerName);
0163                         }
0164                     } else {
0165                         qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Pause not supported by the app! Trying to stop " << playerName;
0166                         if (player.TryStopAsync().get()) {
0167                             qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Stopped " << playerName;
0168                             pausedSources.insert(playerName);
0169                         }
0170                     }
0171                 }
0172             }
0173         }
0174     } else if (autoResume) {
0175         if (mute) {
0176             qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Unmuting sinks";
0177             QHashIterator<QString, IAudioEndpointVolume *> sinksIterator(sinksList);
0178             while (sinksIterator.hasNext()) {
0179                 sinksIterator.next();
0180                 if (mutedSinks.contains(sinksIterator.key())) {
0181                     qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Trying to unmute " << sinksIterator.key();
0182                     if (sinksIterator.value()->SetMute(false, NULL) == S_OK) {
0183                         qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Unmuted " << sinksIterator.key();
0184                     }
0185                     mutedSinks.remove(sinksIterator.key());
0186                 }
0187             }
0188         }
0189         if (pause) {
0190             qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Unpausing media";
0191             QHashIterator<QString, GlobalSystemMediaTransportControlsSession> playersIterator(playersList);
0192             while (playersIterator.hasNext()) {
0193                 playersIterator.next();
0194                 auto &player = playersIterator.value();
0195                 auto &playerName = playersIterator.key();
0196 
0197                 auto playbackInfo = player.GetPlaybackInfo();
0198                 auto playbackControls = playbackInfo.Controls();
0199                 if (pausedSources.contains({playerName})) {
0200                     if (playbackInfo.PlaybackStatus() == GlobalSystemMediaTransportControlsSessionPlaybackStatus::Paused) {
0201                         qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Trying to resume " << playerName;
0202                         if (player.TryPlayAsync().get()) {
0203                             qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Resumed " << playerName;
0204                         }
0205                     }
0206                     pausedSources.remove(playerName);
0207                 }
0208             }
0209         }
0210     }
0211 }
0212 
0213 #include "moc_pausemusicplugin-win.cpp"
0214 #include "pausemusicplugin-win.moc"