diff --git a/src/modules/videoconference/VideoConferenceModule/VideoConferenceModule.cpp b/src/modules/videoconference/VideoConferenceModule/VideoConferenceModule.cpp index be90356674..5f2825c940 100644 --- a/src/modules/videoconference/VideoConferenceModule/VideoConferenceModule.cpp +++ b/src/modules/videoconference/VideoConferenceModule/VideoConferenceModule.cpp @@ -50,12 +50,12 @@ bool VideoConferenceModule::isHotkeyPressed(DWORD code, PowerToysSettings::Hotke void VideoConferenceModule::reverseMicrophoneMute() { - bool muted = false; + // All controlled mic should same state with _microphoneTrackedInUI + // Avoid manually change in Control Panel make controlled mic has different state + bool muted = !getMicrophoneMuteState(); for (auto& controlledMic : instance->_controlledMicrophones) { - const bool was_muted = controlledMic->muted(); - controlledMic->toggle_muted(); - muted = muted || !was_muted; + controlledMic->set_muted(muted); } if (muted) { @@ -283,6 +283,10 @@ void VideoConferenceModule::onModuleSettingsChanged() { toolbar.setToolbarHide(val.value()); } + if (const auto val = values.get_string_value(L"startup_action")) + { + settings.startupAction = val.value(); + } const auto selectedMic = values.get_string_value(L"selected_mic"); if (selectedMic && selectedMic != settings.selectedMicrophone) @@ -308,7 +312,7 @@ void VideoConferenceModule::onMicrophoneConfigurationChanged() return; } - const bool mutedStateForNewMics = _microphoneTrackedInUI ? _microphoneTrackedInUI->muted() : _mic_muted_state_during_disconnect; + const bool mutedStateForNewMics = getMicrophoneMuteState(); std::unordered_set currentlyTrackedMicsIds; for (const auto& controlledMic : _controlledMicrophones) { @@ -338,6 +342,7 @@ void VideoConferenceModule::onMicrophoneConfigurationChanged() toolbar.setMicrophoneMute(muted); }); } + setMuteChangedCallback(); } VideoConferenceModule::VideoConferenceModule() @@ -401,6 +406,25 @@ void VideoConferenceModule::set_config(const wchar_t* config) } } +void VideoConferenceModule::setMuteChangedCallback() +{ + // Keep all controlledMic mute state same _microphoneTrackedInUI + // Should not change manually in Control Panel + for (const auto& controlledMic : _controlledMicrophones) + { + if (controlledMic->id() != _microphoneTrackedInUI->id()) + { + controlledMic->set_mute_changed_callback([&](const bool muted) { + auto muteState = getMicrophoneMuteState(); + if (muted != muteState) + { + controlledMic->set_muted(muteState); + } + }); + } + } +} + void VideoConferenceModule::init_settings() { try @@ -447,6 +471,10 @@ void VideoConferenceModule::init_settings() { toolbar.setToolbarHide(val.value()); } + if (const auto val = powerToysSettings.get_string_value(L"startup_action")) + { + settings.startupAction = val.value(); + } if (const auto val = powerToysSettings.get_string_value(L"selected_mic"); val && *val != settings.selectedMicrophone) { settings.selectedMicrophone = *val; @@ -509,6 +537,22 @@ void VideoConferenceModule::updateControlledMicrophones(const std::wstring_view }); toolbar.setMicrophoneMute(_microphoneTrackedInUI->muted()); } + + if (settings.startupAction == L"Unmute") + { + for (auto& controlledMic : _controlledMicrophones) + { + controlledMic->set_muted(false); + } + } + else if (settings.startupAction == L"Mute") + { + for (auto& controlledMic : _controlledMicrophones) + { + controlledMic->set_muted(true); + } + } + setMuteChangedCallback(); } MicrophoneDevice* VideoConferenceModule::controlledDefaultMic() @@ -669,6 +713,14 @@ void VideoConferenceModule::sendSourceCameraNameUpdate() auto updatesChannel = reinterpret_cast(memory._data); updatesChannel->sourceCameraName.emplace(); std::copy(begin(settings.selectedCamera), end(settings.selectedCamera), begin(*updatesChannel->sourceCameraName)); + if (settings.startupAction == L"Unmute") + { + updatesChannel->useOverlayImage = false; + } + else if (settings.startupAction == L"Mute") + { + updatesChannel->useOverlayImage = true; + } }); } diff --git a/src/modules/videoconference/VideoConferenceModule/VideoConferenceModule.h b/src/modules/videoconference/VideoConferenceModule/VideoConferenceModule.h index fdd838e60d..6e327df25b 100644 --- a/src/modules/videoconference/VideoConferenceModule/VideoConferenceModule.h +++ b/src/modules/videoconference/VideoConferenceModule/VideoConferenceModule.h @@ -30,6 +30,8 @@ struct VideoConferenceSettings std::wstring imageOverlayPath; std::wstring selectedMicrophone; + std::wstring startupAction; + bool pushToReverseEnabled = false; }; @@ -71,6 +73,7 @@ public: private: + void setMuteChangedCallback(); void init_settings(); void updateControlledMicrophones(const std::wstring_view new_mic); MicrophoneDevice* controlledDefaultMic(); diff --git a/src/modules/videoconference/VideoConferenceShared/MicrophoneDevice.cpp b/src/modules/videoconference/VideoConferenceShared/MicrophoneDevice.cpp index 13f81b62aa..83a077b56a 100644 --- a/src/modules/videoconference/VideoConferenceShared/MicrophoneDevice.cpp +++ b/src/modules/videoconference/VideoConferenceShared/MicrophoneDevice.cpp @@ -53,11 +53,6 @@ bool MicrophoneDevice::muted() const noexcept return muted; } -void MicrophoneDevice::toggle_muted() noexcept -{ - set_muted(!muted()); -} - std::wstring_view MicrophoneDevice::id() const noexcept { return _id ? _id.get() : FALLBACK_ID; @@ -70,6 +65,10 @@ std::wstring_view MicrophoneDevice::name() const noexcept void MicrophoneDevice::set_mute_changed_callback(mute_changed_cb_t callback) noexcept { + if (_notifier) + { + _endpoint->UnregisterControlChangeNotify(_notifier.get()); + } _mute_changed_callback = std::move(callback); _notifier = winrt::make(this); diff --git a/src/modules/videoconference/VideoConferenceShared/MicrophoneDevice.h b/src/modules/videoconference/VideoConferenceShared/MicrophoneDevice.h index 03d262c09b..6a40fb5d54 100644 --- a/src/modules/videoconference/VideoConferenceShared/MicrophoneDevice.h +++ b/src/modules/videoconference/VideoConferenceShared/MicrophoneDevice.h @@ -54,7 +54,6 @@ public: bool active() const noexcept; void set_muted(const bool muted) noexcept; bool muted() const noexcept; - void toggle_muted() noexcept; std::wstring_view id() const noexcept; std::wstring_view name() const noexcept; diff --git a/src/settings-ui/Settings.UI.Library/VideoConferenceConfigProperties.cs b/src/settings-ui/Settings.UI.Library/VideoConferenceConfigProperties.cs index 964332bd90..16a0ab5dd9 100644 --- a/src/settings-ui/Settings.UI.Library/VideoConferenceConfigProperties.cs +++ b/src/settings-ui/Settings.UI.Library/VideoConferenceConfigProperties.cs @@ -95,6 +95,9 @@ namespace Microsoft.PowerToys.Settings.UI.Library [JsonPropertyName("toolbar_hide")] public StringProperty ToolbarHide { get; set; } = "When both camera and microphone are unmuted"; + [JsonPropertyName("startup_action")] + public StringProperty StartupAction { get; set; } = "Nothing"; + // converts the current to a json string. public string ToJsonString() { diff --git a/src/settings-ui/Settings.UI.UnitTests/BackwardsCompatibility/TestFiles/v0.22.0/Microsoft/PowerToys/Video Conference/settings.json b/src/settings-ui/Settings.UI.UnitTests/BackwardsCompatibility/TestFiles/v0.22.0/Microsoft/PowerToys/Video Conference/settings.json index ddd29b6658..8fc0aa493f 100644 --- a/src/settings-ui/Settings.UI.UnitTests/BackwardsCompatibility/TestFiles/v0.22.0/Microsoft/PowerToys/Video Conference/settings.json +++ b/src/settings-ui/Settings.UI.UnitTests/BackwardsCompatibility/TestFiles/v0.22.0/Microsoft/PowerToys/Video Conference/settings.json @@ -37,6 +37,7 @@ "selected_camera": { "value": "USB Video Device" }, "theme": { "value": "light" }, "toolbar_monitor": { "value": "All monitors" }, - "toolbar_position": { "value": "Bottom center" } + "toolbar_position": { "value": "Bottom center" }, + "startup_action": { "value": "Nothing" } } } diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/VideoConference.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/VideoConference.xaml index cfcf3d62e4..f4a187b9cd 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/VideoConference.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/VideoConference.xaml @@ -146,11 +146,22 @@ + + + + + + + + + + + diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw index 12d937e0d0..25b09ac683 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -561,6 +561,9 @@ When both camera and microphone are unmuted + + After timeout + Video Conference Mute @@ -576,6 +579,21 @@ Toolbar + + Behavior + + + Startup action + + + Nothing + + + Unmute + + + Mute + Shortcuts diff --git a/src/settings-ui/Settings.UI/ViewModels/VideoConferenceViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/VideoConferenceViewModel.cs index 9aec8e9a89..04c9d47f44 100644 --- a/src/settings-ui/Settings.UI/ViewModels/VideoConferenceViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/VideoConferenceViewModel.cs @@ -147,6 +147,22 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels case "When both camera and microphone are muted": _toolbarHideIndex = 2; break; + case "After timeout": + _toolbarHideIndex = 3; + break; + } + + switch (Settings.Properties.StartupAction.Value) + { + case "Nothing": + _startupActionIndex = 0; + break; + case "Unmute": + _startupActionIndex = 1; + break; + case "Mute": + _startupActionIndex = 2; + break; } if (shouldSaveSettings) @@ -176,6 +192,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private int _toolbarPositionIndex; private int _toolbarMonitorIndex; private int _toolbarHideIndex; + private int _startupActionIndex; private HotkeySettings _cameraAndMicrophoneMuteHotkey; private HotkeySettings _microphoneMuteHotkey; private HotkeySettings _microphonePushToTalkHotkey; @@ -497,6 +514,9 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels case 2: Settings.Properties.ToolbarHide.Value = "When both camera and microphone are muted"; break; + case 3: + Settings.Properties.ToolbarHide.Value = "After timeout"; + break; } RaisePropertyChanged(nameof(ToolbarHideIndex)); @@ -504,6 +524,36 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels } } + public int StartupActionIndex + { + get + { + return _startupActionIndex; + } + + set + { + if (value != _startupActionIndex) + { + _startupActionIndex = value; + switch (_startupActionIndex) + { + case 0: + Settings.Properties.StartupAction.Value = "Nothing"; + break; + case 1: + Settings.Properties.StartupAction.Value = "Unmute"; + break; + case 2: + Settings.Properties.StartupAction.Value = "Mute"; + break; + } + + RaisePropertyChanged(nameof(_startupActionIndex)); + } + } + } + public string GetSettingsSubPath() { return _settingsConfigFileFolder + (string.IsNullOrEmpty(_settingsConfigFileFolder) ? string.Empty : "\\") + ModuleName;