[OpenDocString] kdeconnect-kde (cpp)
systemvolumeplugin-win.cpp
SystemvolumePlugin::SystemvolumePlugin(QObject *parent, const QVariantList &args)
    : KdeConnectPlugin(parent, args)
    , sinkList()
{
    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);
    }
}
This constructor builds a plugin object and its sink list. It initializes the plugin, creates the MMDeviceEnumerator object, and sets up the memory controller for reading devices.
SystemvolumePlugin::~SystemvolumePlugin()
{
    if (valid) {
        deviceEnumerator->UnregisterEndpointNotificationCallback(deviceCallback);
        deviceEnumerator->Release();
        deviceEnumerator = nullptr;
    }
}
This removes the device callback and releases the enumeration object.
bool SystemvolumePlugin::sendSinkList()
{
    if (!valid)
        return false;

    QJsonDocument document;
    QJsonArray array;

    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);

    if (!deviceCount) {
        qWarning("No audio devices detected");
        return false;
    }

    IMMDevice *defaultDevice = nullptr;
    deviceEnumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &defaultDevice);

    if (!defaultDevice) {
        qWarning("No default audio device detected");
        return false;
    }

    LPWSTR defaultId = NULL;
    defaultDevice->GetId(&defaultId);

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

        IPropertyStore *deviceProperties = nullptr;
        PROPVARIANT deviceProperty;
        QString name;
        QString desc;
        LPWSTR deviceId;
        float volume;
        BOOL muted;

        IAudioEndpointVolume *endpoint = nullptr;
        CAudioEndpointVolumeCallback *callback;

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

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

#ifndef __MINGW32__
        deviceProperties->GetValue(PKEY_Device_DeviceDesc, &deviceProperty);
        desc = QString::fromWCharArray(deviceProperty.pwszVal);
        // PropVariantClear(&deviceProperty);
#else
        desc = name;
#endif

        QJsonObject sinkObject;
        sinkObject.insert(QStringLiteral("name"), name);
        sinkObject.insert(QStringLiteral("description"), desc);

        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;
        }

        device->GetId(&deviceId);
        endpoint->GetMasterVolumeLevelScalar(&volume);
        endpoint->GetMute(&muted);

        sinkObject.insert(QStringLiteral("muted"), (bool)muted);
        sinkObject.insert(QStringLiteral("volume"), (qint64)(volume * 100));
        sinkObject.insert(QStringLiteral("maxVolume"), (qint64)100);
        sinkObject.insert(QStringLiteral("enabled"), lstrcmpW(deviceId, defaultId) == 0);

        // Register Callback
        if (!sinkList.contains(name)) {
            callback = new CAudioEndpointVolumeCallback(*this, name);
            endpoint->RegisterControlChangeNotify(callback);
            sinkList[name] = qMakePair(endpoint, callback);
        }

        QString qDeviceId = QString::fromWCharArray(deviceId);
        idToNameMap[qDeviceId] = name;

        device->Release();
        array.append(sinkObject);
    }
    devices->Release();
    defaultDevice->Release();

    document.setArray(array);

    NetworkPacket np(PACKET_TYPE_SYSTEMVOLUME);
    np.set(QStringLiteral("sinkList"), document);
    sendPacket(np);
    return true;
}
This code creates a sink list and sends it to all active devices. It first constructs the sink list, and adds it to the device collection. It also creates a callback to handle change events.
void SystemvolumePlugin::connected()
{
    if (!valid)
        return;

    deviceCallback = new CMMNotificationClient(*this);
    deviceEnumerator->RegisterEndpointNotificationCallback(deviceCallback);
    sendSinkList();
}
This sends a list of sinks to the deviceEnumerator. It registers the device callback and sets up the signal sinkList.
static HRESULT setDefaultAudioPlaybackDevice(PCWSTR deviceId)
{
    if (deviceId == nullptr)
        return ERROR_BAD_UNIT;

    IPolicyConfigVista *pPolicyConfig;
    HRESULT hr = CoCreateInstance(__uuidof(CPolicyConfigVistaClient), NULL, CLSCTX_ALL, __uuidof(IPolicyConfigVista), (LPVOID *)&pPolicyConfig);

    if (SUCCEEDED(hr)) {
        hr = pPolicyConfig->SetDefaultEndpoint(deviceId, eMultimedia);
        pPolicyConfig->Release();
    }

    return hr;
}
This sets the default audio playback device by first creating it, and setting it to the endpoint of the device. It is a no - op if the device id is null.
HRESULT SystemvolumePlugin::setDefaultAudioPlaybackDevice(QString &name, bool enabled)
{
    if (!enabled)
        return S_OK;

    PWSTR deviceId = nullptr;
    for (auto &entry : idToNameMap.toStdMap()) {
        if (entry.second == name) {
            deviceId = new WCHAR[entry.first.length()];
            wcscpy(deviceId, entry.first.toStdWString().data());
            break;
        }
    }

    if (deviceId == nullptr)
        return ERROR_BAD_UNIT;

    HRESULT hr = ::setDefaultAudioPlaybackDevice(deviceId);

    delete[] deviceId;

    return hr;
}
This sets the default audio playback device by reading its id from the idToNameMap. If the device with the given name is not enabled, it returns S_OK. If the device is enabled, the function creates a new array of ids and sets the value to true. If the device is not enabled, the function returns ERROR_BAD_UNIT.
bool SystemvolumePlugin::receivePacket(const NetworkPacket &np)
{
    if (!valid)
        return false;

    if (np.has(QStringLiteral("requestSinks"))) {
        return sendSinkList();
    } else {
        QString name = np.get(QStringLiteral("name"));

        if (sinkList.contains(name)) {
            // unregister ControlChangeNotify before doing any changes to a sink
            HRESULT unregisterSuccess = E_POINTER;
            auto sinkListIterator = this->sinkList.find(name);
            auto &sink = sinkListIterator.value();
            if (!(sinkListIterator == this->sinkList.end())) {
                unregisterSuccess = sink.first->UnregisterControlChangeNotify(sink.second);
            }

            if (np.has(QStringLiteral("volume"))) {
                float currentVolume;
                sink.first->GetMasterVolumeLevelScalar(¤tVolume);
                float requestedVolume = (float)np.get(QStringLiteral("volume"), 100) / 100;
                if (currentVolume != requestedVolume) {
                    sinkList[name].first->SetMasterVolumeLevelScalar(requestedVolume, NULL);
                }
            }

            if (np.has(QStringLiteral("muted"))) {
                BOOL currentMuteStatus;
                sink.first->GetMute(¤tMuteStatus);
                BOOL requestedMuteStatus = np.get(QStringLiteral("muted"), false);
                if (currentMuteStatus != requestedMuteStatus) {
                    sinkList[name].first->SetMute(requestedMuteStatus, NULL);
                }
            }

            if (np.has(QStringLiteral("enabled"))) {
                // get the current default device ID
                IMMDevice *defaultDevice = nullptr;
                deviceEnumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &defaultDevice);
                LPWSTR defaultId = NULL;
                defaultDevice->GetId(&defaultId);
                defaultDevice->Release();
                // get current sink's device ID
                QString qDefaultId = QString::fromWCharArray(defaultId);
                QString currentDeviceId = idToNameMap.key(name);

                if ((bool)qDefaultId.compare(currentDeviceId)) {
                    setDefaultAudioPlaybackDevice(name, np.get(QStringLiteral("enabled")));
                }
            }

            // re-register ControlChangeNotify in case we unregistered it
            if (unregisterSuccess == S_OK) {
                sink.first->RegisterControlChangeNotify(sink.second);
            }
        }
    }
    return true;
}
This method receives a NetworkPacket object np and processes it. The method first checks if the packet is valid, if it is a sink, and if it is the first sink in the sink list, it sends the list of sinks to the first sink. Then it checks if the sink list contains the given name, if it exists, it checks if the sink list contains the given name, and if it is the first sink in the sink list, it sets the current volume level scalar and mute status. If the sink is enabled, it is set to true. Finally, it sets the default audio playback device to enabled. If the sink is the first sink in the sink list, the first sink is updated with the new values from the given network packet. Finally, the method returns true.