-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
FEAT(windows client): Add support for the Universal Mute button #7136
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
citelao
wants to merge
2
commits into
mumble-voip:master
Choose a base branch
from
citelao:user/citelao/universal-mute-simplified
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| // Copyright The Mumble Developers. All rights reserved. | ||
| // Use of this source code is governed by a BSD-style license | ||
| // that can be found in the LICENSE file at the root of the | ||
| // Mumble source tree or at <https://www.mumble.info/LICENSE>. | ||
|
|
||
| #include "UniversalMute.h" | ||
|
|
||
| #include "win.h" | ||
|
|
||
| // clang-format off | ||
| #include <versionhelpers.h> | ||
| #include <wrl.h> | ||
| #include <wrl/event.h> | ||
| #include <wrl/wrappers/corewrappers.h> | ||
| #include <roapi.h> | ||
| #include <windows.applicationmodel.calls.h> | ||
| // clang-format on | ||
|
|
||
| using namespace Microsoft::WRL; | ||
| using namespace Microsoft::WRL::Wrappers; | ||
| using namespace ABI::Windows::ApplicationModel::Calls; | ||
| using namespace ABI::Windows::Foundation; | ||
|
|
||
| struct UniversalMuter::Impl { | ||
| std::function< void() > onMuted; | ||
| std::function< void() > onUnmuted; | ||
| ComPtr< IVoipCallCoordinator > coordinator; | ||
| ComPtr< IVoipPhoneCall > call; | ||
| EventRegistrationToken muteStateToken{}; | ||
| }; | ||
|
|
||
| namespace { | ||
| // C++/WinRT would be a bit simpler, but requires a newer couroutine ABI than | ||
| // we are currently using (requires _COROUTINE_ABI=2, while Qt is compiled with | ||
| // _COROUTINE_ABI=1). If the app fully upgrades to C++20, we can migrate to C++/WinRT. | ||
| // | ||
| // https://github.com/microsoft/cppwinrt/issues/1281 | ||
| ComPtr< IVoipCallCoordinator > tryCreateCallCoordinator() { | ||
| // WinRT was added in Windows 8; check for that before using WinRT. | ||
| // The /DELAYLOAD:runtimeobject.dll linker flag ensures we don't attempt | ||
| // to load the DLL on older versions. | ||
| if (!IsWindows8OrGreater()) { | ||
| return nullptr; | ||
| } | ||
|
|
||
| ComPtr< IVoipCallCoordinatorStatics > statics; | ||
| HRESULT hr = RoGetActivationFactory( | ||
| HStringReference(RuntimeClass_Windows_ApplicationModel_Calls_VoipCallCoordinator).Get(), | ||
| IID_PPV_ARGS(&statics)); | ||
| if (FAILED(hr)) { | ||
| return nullptr; | ||
| } | ||
|
|
||
| ComPtr< IVoipCallCoordinator > coordinator; | ||
| hr = statics->GetDefault(&coordinator); | ||
| if (FAILED(hr)) { | ||
|
citelao marked this conversation as resolved.
|
||
| return nullptr; | ||
| } | ||
|
|
||
| return coordinator; | ||
| } | ||
| } | ||
|
|
||
| UniversalMuter::UniversalMuter(std::function< void() > onMuted, std::function< void() > onUnmuted) | ||
| : m_impl(std::make_shared< Impl >()) { | ||
| m_impl->onMuted = std::move(onMuted); | ||
| m_impl->onUnmuted = std::move(onUnmuted); | ||
|
|
||
| m_impl->coordinator = tryCreateCallCoordinator(); | ||
| if (!m_impl->coordinator) | ||
| return; | ||
|
|
||
| // Capture a weak_ptr so the callback safely no-ops if UniversalMuter is destroyed | ||
| // while a callback is in flight (e.g. racing with remove_MuteStateChanged). | ||
| std::weak_ptr< Impl > weakImpl = m_impl; | ||
| auto handler = Callback< ITypedEventHandler< VoipCallCoordinator *, MuteChangeEventArgs * > >( | ||
| [weakImpl](IVoipCallCoordinator *, IMuteChangeEventArgs *args) -> HRESULT { | ||
| auto impl = weakImpl.lock(); | ||
| if (!impl) | ||
| return S_OK; | ||
| boolean muted = FALSE; | ||
| args->get_Muted(&muted); | ||
|
|
||
| // The callbacks are responsible for calling setMuted/setUnmuted when they have | ||
| // processed the event. | ||
| if (muted) { | ||
| if (impl->onMuted) | ||
| impl->onMuted(); | ||
| } else { | ||
| if (impl->onUnmuted) | ||
| impl->onUnmuted(); | ||
| } | ||
|
citelao marked this conversation as resolved.
|
||
| return S_OK; | ||
| }); | ||
|
|
||
| // Ignore failures; best effort. | ||
| m_impl->coordinator->add_MuteStateChanged(handler.Get(), &m_impl->muteStateToken); | ||
| } | ||
|
|
||
| UniversalMuter::~UniversalMuter() { | ||
| if (m_impl->coordinator) | ||
| m_impl->coordinator->remove_MuteStateChanged(m_impl->muteStateToken); | ||
| tryEndCall(); | ||
| } | ||
|
|
||
| void UniversalMuter::startCall(const std::wstring &contactName, const std::wstring &serviceName) { | ||
| if (!m_impl->coordinator) | ||
| return; | ||
|
|
||
| ComPtr< IVoipPhoneCall > call; | ||
| HString context, hContactName, hServiceName; | ||
| context.Set(L""); | ||
| hContactName.Set(contactName.c_str()); | ||
| hServiceName.Set(serviceName.c_str()); | ||
|
|
||
| // RequestNewOutgoingCall may fail with E_ACCESSDENIED if the app lacks package identity. | ||
| // The coordinator and MuteStateChanged events remain active regardless. | ||
| HRESULT hr = m_impl->coordinator->RequestNewOutgoingCall(context.Get(), hContactName.Get(), | ||
| hServiceName.Get(), | ||
| VoipPhoneCallMedia_Audio, &call); | ||
| if (SUCCEEDED(hr)) | ||
| m_impl->call = call; | ||
| } | ||
|
|
||
| void UniversalMuter::tryEndCall() { | ||
| if (!m_impl->call) | ||
| return; | ||
| m_impl->call->NotifyCallEnded(); | ||
| m_impl->call.Reset(); | ||
| } | ||
|
|
||
| void UniversalMuter::trySetCallName(const std::wstring &callName) { | ||
| if (!m_impl->call) | ||
| return; | ||
| HString name; | ||
| name.Set(callName.c_str()); | ||
| m_impl->call->put_ContactName(name.Get()); | ||
| } | ||
|
|
||
| void UniversalMuter::setMuted() { | ||
| if (m_impl->coordinator) | ||
| m_impl->coordinator->NotifyMuted(); | ||
| } | ||
|
|
||
| void UniversalMuter::setUnmuted() { | ||
| if (m_impl->coordinator) | ||
| m_impl->coordinator->NotifyUnmuted(); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| // Copyright The Mumble Developers. All rights reserved. | ||
| // Use of this source code is governed by a BSD-style license | ||
| // that can be found in the LICENSE file at the root of the | ||
| // Mumble source tree or at <https://www.mumble.info/LICENSE>. | ||
|
|
||
| #ifndef MUMBLE_MUMBLE_UNIVERSAL_MUTE_H_ | ||
| #define MUMBLE_MUMBLE_UNIVERSAL_MUTE_H_ | ||
|
|
||
| #include <functional> | ||
| #include <memory> | ||
| #include <string> | ||
|
|
||
| // A wrapper around the Windows 10 VoIP Call API, specifically enough to | ||
| // interact with the Windows 11 Universal Mute feature. This ensures physical mute buttons | ||
| // in recent laptops can mute Mumble. | ||
| // | ||
| // Universal Mute is only supported on Windows 11 22H2 and later, but this class | ||
| // gracefully no-ops on unsupported platforms. | ||
| // | ||
| // https://stackoverflow.com/questions/74683703/how-do-i-support-call-mute-universal-mute-in-my-app-for-windows-11-22h2 | ||
| class UniversalMuter { | ||
| public: | ||
| // The onMuted/onUnmuted callbacks are called when the user mutes/unmutes themselves using | ||
| // the Universal Mute button. They must call setMuted()/setUnmuted() here when they have | ||
| // processed the event, or the button won't actually change state. | ||
| UniversalMuter(std::function< void() > onMuted, std::function< void() > onUnmuted); | ||
| ~UniversalMuter(); | ||
|
|
||
| UniversalMuter(const UniversalMuter &) = delete; | ||
| UniversalMuter &operator=(const UniversalMuter &) = delete; | ||
| UniversalMuter(UniversalMuter &&) = delete; | ||
| UniversalMuter &operator=(UniversalMuter &&) = delete; | ||
|
|
||
| void setMuted(); | ||
| void setUnmuted(); | ||
|
|
||
| void startCall(const std::wstring &contactName, const std::wstring &serviceName); | ||
| void tryEndCall(); | ||
|
|
||
| void trySetCallName(const std::wstring &callName); | ||
|
|
||
| private: | ||
| // Use the PIMPL pattern to avoid including WRL/WinRT headers in the public header. | ||
| struct Impl; | ||
| std::shared_ptr< Impl > m_impl; | ||
| }; | ||
|
|
||
| #endif // MUMBLE_MUMBLE_UNIVERSAL_MUTE_H_ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.