diff --git a/src/modules/videoconference/VideoConferenceModule/VideoConferenceModule.cpp b/src/modules/videoconference/VideoConferenceModule/VideoConferenceModule.cpp index be90356674f..5f2825c940f 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 fdd838e60d5..6e327df25b6 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 @@ class VideoConferenceModule : public PowertoyModuleIface 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 13f81b62aa6..83a077b56a1 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 03d262c09b7..6a40fb5d54d 100644 --- a/src/modules/videoconference/VideoConferenceShared/MicrophoneDevice.h +++ b/src/modules/videoconference/VideoConferenceShared/MicrophoneDevice.h @@ -54,7 +54,6 @@ class MicrophoneDevice 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 964332bd907..16a0ab5dd9d 100644 --- a/src/settings-ui/Settings.UI.Library/VideoConferenceConfigProperties.cs +++ b/src/settings-ui/Settings.UI.Library/VideoConferenceConfigProperties.cs @@ -95,6 +95,9 @@ public VideoConferenceConfigProperties() [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 ddd29b6658b..8fc0aa493fc 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 ca101558f17..f4a187b9cd7 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/VideoConference.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/VideoConference.xaml @@ -152,6 +152,16 @@ + + + + + + + + + + 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 b67d3ae513b..61e410d6a0f 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -579,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 67c4405dc90..04c9d47f448 100644 --- a/src/settings-ui/Settings.UI/ViewModels/VideoConferenceViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/VideoConferenceViewModel.cs @@ -152,6 +152,19 @@ public VideoConferenceViewModel( break; } + switch (Settings.Properties.StartupAction.Value) + { + case "Nothing": + _startupActionIndex = 0; + break; + case "Unmute": + _startupActionIndex = 1; + break; + case "Mute": + _startupActionIndex = 2; + break; + } + if (shouldSaveSettings) { _settingsUtils.SaveSettings(Settings.ToJsonString(), ModuleName); @@ -179,6 +192,7 @@ private void InitializeEnabledValue() private int _toolbarPositionIndex; private int _toolbarMonitorIndex; private int _toolbarHideIndex; + private int _startupActionIndex; private HotkeySettings _cameraAndMicrophoneMuteHotkey; private HotkeySettings _microphoneMuteHotkey; private HotkeySettings _microphonePushToTalkHotkey; @@ -510,6 +524,36 @@ public int ToolbarHideIndex } } + 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;