[OpenDocString] kdeconnect-kde (cpp)
pausemusicplugin-win.cpp
PauseMusicPlugin::PauseMusicPlugin(QObject *parent, const QVariantList &args)
    : KdeConnectPlugin(parent, args)
{
    CoInitialize(nullptr);
    deviceEnumerator = nullptr;
    HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (LPVOID *)&deviceEnumerator);
    valid = (hr == S_OK);
    if (!valid) {
        qWarning("Initialization failed: Failed to create MMDeviceEnumerator");
        qWarning("Error Code: %lx", hr);
    }

    sessionManager = GlobalSystemMediaTransportControlsSessionManager::RequestAsync().get();
}
This constructor builds a KdeConnectPlugin object and its d-pointer. It initializes the plugin, creates a MMDeviceEnumerator object, and requests the session manager. It returns the handle to the session manager and an error message if the initialization fails.
bool PauseMusicPlugin::updateSinksList()
{
    sinksList.clear();
    if (!valid)
        return false;

    IMMDeviceCollection *devices = nullptr;
    HRESULT hr = deviceEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &devices);

    if (hr != S_OK) {
        qWarning("Failed to Enumumerate AudioEndpoints");
        qWarning("Error Code: %lx", hr);
        return false;
    }

    unsigned int deviceCount;
    devices->GetCount(&deviceCount);

    for (unsigned int i = 0; i < deviceCount; i++) {
        IMMDevice *device = nullptr;

        IPropertyStore *deviceProperties = nullptr;
        PROPVARIANT deviceProperty;
        QString name;

        IAudioEndpointVolume *endpoint = nullptr;

        // Get Properties
        devices->Item(i, &device);
        device->OpenPropertyStore(STGM_READ, &deviceProperties);

        deviceProperties->GetValue(PKEY_Device_FriendlyName, &deviceProperty);
        name = QString::fromWCharArray(deviceProperty.pwszVal);
        // PropVariantClear(&deviceProperty);

        hr = device->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, (void **)&endpoint);
        if (hr != S_OK) {
            qWarning() << "Failed to create IAudioEndpointVolume for device:" << name;
            qWarning("Error Code: %lx", hr);

            device->Release();
            continue;
        }

        // Register Callback
        if (!sinksList.contains(name)) {
            sinksList[name] = endpoint;
        }
        device->Release();
    }
    devices->Release();
    return true;
}
This function is responsible for updating the sink list by enumerating over the available devices. It first constructs the device collection, creating the IAudioEndpointVolume objects for each device, and activating it. Then it releases the devices.
void PauseMusicPlugin::updatePlayersList()
{
    playersList.clear();
    auto sessions = sessionManager->GetSessions();
    for (uint32_t i = 0; i < sessions.Size(); i++) {
        const auto player = sessions.GetAt(i);
        auto playerName = player.SourceAppUserModelId();

        QString uniqueName = QString::fromWCharArray(playerName.c_str());
        for (int i = 2; playersList.contains(uniqueName); ++i) {
            uniqueName += QStringLiteral(" [") + QString::number(i) + QStringLiteral("]");
        }
        playersList.insert(uniqueName, player);
    }
}
This updates the list of players based on the sessions in the session manager.
PauseMusicPlugin::~PauseMusicPlugin()
{
    CoUninitialize();
}
This removes the music plugin object upon destruction.
bool PauseMusicPlugin::receivePacket(const NetworkPacket &np)
{
    bool pauseOnlyWhenTalking = config()->getBool(QStringLiteral("conditionTalking"), false);

    if (pauseOnlyWhenTalking) {
        if (np.get(QStringLiteral("event")) != QLatin1String("talking")) {
            return true;
        }
    } else {
        if (np.get(QStringLiteral("event")) != QLatin1String("ringing") && np.get(QStringLiteral("event")) != QLatin1String("talking")) {
            return true;
        }
    }

    bool pauseConditionFulfilled = !np.get(QStringLiteral("isCancel"));

    bool pause = config()->getBool(QStringLiteral("actionPause"), true);
    bool mute = config()->getBool(QStringLiteral("actionMute"), false);

    const bool autoResume = config()->getBool(QStringLiteral("actionResume"), true);

    if (pauseConditionFulfilled) {
        if (mute) {
            qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Muting all the unmuted sinks";
            this->updateSinksList();
            QHashIterator sinksIterator(sinksList);
            while (sinksIterator.hasNext()) {
                sinksIterator.next();
                BOOL muted;
                sinksIterator.value()->GetMute(&muted);
                if (!((bool)muted)) {
                    qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Trying to mute " << sinksIterator.key();
                    if (sinksIterator.value()->SetMute(true, NULL) == S_OK) {
                        qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Muted " << sinksIterator.key();
                        mutedSinks.insert(sinksIterator.key());
                    }
                }
            }
        }

        if (pause) {
            qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Pausing all the playing media";
            this->updatePlayersList();
            QHashIterator playersIterator(playersList);
            while (playersIterator.hasNext()) {
                playersIterator.next();
                auto &player = playersIterator.value();
                auto &playerName = playersIterator.key();

                auto playbackInfo = player.GetPlaybackInfo();
                auto playbackControls = playbackInfo.Controls();
                if (playbackInfo.PlaybackStatus() == GlobalSystemMediaTransportControlsSessionPlaybackStatus::Playing) {
                    if (playbackControls.IsPauseEnabled()) {
                        qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Trying to pause " << playerName;
                        if (player.TryPauseAsync().get()) {
                            qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Paused " << playerName;
                            pausedSources.insert(playerName);
                        }
                    } else {
                        qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Pause not supported by the app! Trying to stop " << playerName;
                        if (player.TryStopAsync().get()) {
                            qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Stopped " << playerName;
                            pausedSources.insert(playerName);
                        }
                    }
                }
            }
        }
    } else if (autoResume) {
        if (mute) {
            qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Unmuting sinks";
            QHashIterator sinksIterator(sinksList);
            while (sinksIterator.hasNext()) {
                sinksIterator.next();
                if (mutedSinks.contains(sinksIterator.key())) {
                    qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Trying to unmute " << sinksIterator.key();
                    if (sinksIterator.value()->SetMute(false, NULL) == S_OK) {
                        qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Unmuted " << sinksIterator.key();
                    }
                    mutedSinks.remove(sinksIterator.key());
                }
            }
        }
        if (pause) {
            qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Unpausing media";
            QHashIterator playersIterator(playersList);
            while (playersIterator.hasNext()) {
                playersIterator.next();
                auto &player = playersIterator.value();
                auto &playerName = playersIterator.key();

                auto playbackInfo = player.GetPlaybackInfo();
                auto playbackControls = playbackInfo.Controls();
                if (pausedSources.contains({playerName})) {
                    if (playbackInfo.PlaybackStatus() == GlobalSystemMediaTransportControlsSessionPlaybackStatus::Paused) {
                        qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Trying to resume " << playerName;
                        if (player.TryPlayAsync().get()) {
                            qCDebug(KDECONNECT_PLUGIN_PAUSEMUSIC) << "Resumed " << playerName;
                        }
                    }
                    pausedSources.remove(playerName);
                }
            }
        }
    }

    return true;
}
This method receives a NetworkPacket object and checks if the packet is allowed to be pauseed, mute and resume. If the condition is fulfilled and the current event is ringing, and the current event is not reachable, and the current event is not reachable, and the current event is not reachable, and the current event is not reachable, and the current event is not reachable, and the current event is ignored, and the function returns true.