File indexing completed on 2025-04-20 04:33:20
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"