Skip to content

Commit fbecab3

Browse files
committed
fix(hotkey): fix bosskey muting issue
1 parent 049d5fd commit fbecab3

1 file changed

Lines changed: 131 additions & 55 deletions

File tree

src/hotkey.cc

Lines changed: 131 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@
66
#include <endpointvolume.h>
77
#include <mmdeviceapi.h>
88
#include <tlhelp32.h>
9+
#include <wrl/client.h>
910

1011
#include <algorithm>
1112
#include <cwctype>
1213
#include <iterator>
14+
#include <memory>
15+
#include <optional>
1316
#include <string>
1417
#include <string_view>
1518
#include <thread>
@@ -21,12 +24,36 @@
2124

2225
namespace {
2326

27+
using Microsoft::WRL::ComPtr;
2428
using HotkeyAction = void (*)();
2529

30+
class ComInitializer {
31+
public:
32+
ComInitializer() {
33+
hr_ = CoInitialize(nullptr);
34+
initialized_ = SUCCEEDED(hr_) || hr_ == RPC_E_CHANGED_MODE;
35+
should_uninit_ = (hr_ == S_OK || hr_ == S_FALSE);
36+
}
37+
~ComInitializer() {
38+
if (should_uninit_) {
39+
CoUninitialize();
40+
}
41+
}
42+
ComInitializer(const ComInitializer&) = delete;
43+
ComInitializer& operator=(const ComInitializer&) = delete;
44+
45+
[[nodiscard]] bool IsInitialized() const { return initialized_; }
46+
47+
private:
48+
HRESULT hr_ = E_FAIL;
49+
bool initialized_ = false;
50+
bool should_uninit_ = false;
51+
};
52+
2653
// Static variables for internal use
2754
bool is_hide = false;
2855
std::vector<HWND> hwnd_list;
29-
std::unordered_map<DWORD, bool> original_mute_states;
56+
std::unordered_map<std::wstring, bool> original_mute_states;
3057

3158
#define MOD_NOREPEAT 0x4000
3259

@@ -80,68 +107,116 @@ std::vector<DWORD> GetAppPids() {
80107
return pids;
81108
}
82109

110+
std::optional<std::wstring> GetSessionKey(IAudioSessionControl2* session2) {
111+
if (!session2) {
112+
return std::nullopt;
113+
}
114+
LPWSTR session_id = nullptr;
115+
if (SUCCEEDED(session2->GetSessionInstanceIdentifier(&session_id)) &&
116+
session_id) {
117+
std::wstring key(session_id);
118+
CoTaskMemFree(session_id);
119+
return key;
120+
}
121+
return std::nullopt;
122+
}
123+
124+
void ProcessAudioSession(IAudioSessionControl* session,
125+
const std::vector<DWORD>& pids,
126+
bool set_mute,
127+
bool save_mute_state) {
128+
ComPtr<IAudioSessionControl2> session2;
129+
if (FAILED(session->QueryInterface(IID_PPV_ARGS(&session2)))) {
130+
return;
131+
}
132+
133+
DWORD session_pid = 0;
134+
if (FAILED(session2->GetProcessId(&session_pid))) {
135+
return;
136+
}
137+
138+
auto it = std::find(pids.begin(), pids.end(), session_pid);
139+
if (it == pids.end()) {
140+
return;
141+
}
142+
143+
ComPtr<ISimpleAudioVolume> volume;
144+
if (FAILED(session2->QueryInterface(IID_PPV_ARGS(&volume)))) {
145+
return;
146+
}
147+
148+
auto session_key = GetSessionKey(session2.Get());
149+
150+
if (save_mute_state && session_key) {
151+
BOOL is_muted = FALSE;
152+
if (SUCCEEDED(volume->GetMute(&is_muted))) {
153+
original_mute_states[*session_key] = (is_muted == TRUE);
154+
}
155+
}
156+
157+
if (set_mute) {
158+
volume->SetMute(TRUE, nullptr);
159+
} else {
160+
// Unmute logic:
161+
// - If we have recorded state for this session, respect it
162+
// - If session is new (not in our records), unmute it
163+
// (it was likely created after hide, so it should be unmuted)
164+
bool should_unmute = true;
165+
if (session_key) {
166+
auto state_it = original_mute_states.find(*session_key);
167+
if (state_it != original_mute_states.end()) {
168+
should_unmute = !state_it->second; // unmute only if was not muted
169+
}
170+
}
171+
if (should_unmute) {
172+
volume->SetMute(FALSE, nullptr);
173+
}
174+
}
175+
}
176+
83177
void MuteProcess(const std::vector<DWORD>& pids,
84178
bool set_mute,
85179
bool save_mute_state = false) {
86-
CoInitialize(nullptr);
87-
IMMDeviceEnumerator* enumerator = nullptr;
88-
IMMDevice* device = nullptr;
89-
IAudioSessionManager2* manager = nullptr;
90-
IAudioSessionEnumerator* session_enumerator = nullptr;
91-
92-
CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL,
93-
IID_PPV_ARGS(&enumerator));
94-
enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &device);
95-
device->Activate(__uuidof(IAudioSessionManager2), CLSCTX_ALL, nullptr,
96-
(void**)&manager);
97-
manager->GetSessionEnumerator(&session_enumerator);
180+
ComInitializer com;
181+
if (!com.IsInitialized()) {
182+
return;
183+
}
184+
185+
ComPtr<IMMDeviceEnumerator> enumerator;
186+
if (FAILED(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL,
187+
IID_PPV_ARGS(&enumerator)))) {
188+
return;
189+
}
190+
191+
ComPtr<IMMDevice> device;
192+
if (FAILED(
193+
enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &device))) {
194+
return;
195+
}
196+
197+
ComPtr<IAudioSessionManager2> manager;
198+
if (FAILED(
199+
device->Activate(__uuidof(IAudioSessionManager2), CLSCTX_ALL, nullptr,
200+
reinterpret_cast<void**>(manager.GetAddressOf())))) {
201+
return;
202+
}
203+
204+
ComPtr<IAudioSessionEnumerator> session_enumerator;
205+
if (FAILED(manager->GetSessionEnumerator(&session_enumerator))) {
206+
return;
207+
}
98208

99209
int session_count = 0;
100-
session_enumerator->GetCount(&session_count);
210+
if (FAILED(session_enumerator->GetCount(&session_count))) {
211+
return;
212+
}
213+
101214
for (int i = 0; i < session_count; ++i) {
102-
IAudioSessionControl* session = nullptr;
103-
session_enumerator->GetSession(i, &session);
104-
IAudioSessionControl2* session2 = nullptr;
105-
if (SUCCEEDED(session->QueryInterface(__uuidof(IAudioSessionControl2),
106-
(void**)&session2))) {
107-
DWORD session_pid = 0;
108-
session2->GetProcessId(&session_pid);
109-
110-
for (DWORD pid : pids) {
111-
if (session_pid == pid) {
112-
ISimpleAudioVolume* volume = nullptr;
113-
if (SUCCEEDED(session2->QueryInterface(__uuidof(ISimpleAudioVolume),
114-
(void**)&volume))) {
115-
if (save_mute_state) {
116-
BOOL is_muted;
117-
volume->GetMute(&is_muted);
118-
original_mute_states[pid] = (is_muted == TRUE);
119-
}
120-
121-
if (set_mute) {
122-
volume->SetMute(TRUE, nullptr);
123-
} else {
124-
// Only unmute if the original state was not muted beforehand
125-
auto it = original_mute_states.find(pid);
126-
if (it != original_mute_states.end() && !it->second) {
127-
volume->SetMute(FALSE, nullptr);
128-
}
129-
}
130-
volume->Release();
131-
}
132-
break;
133-
}
134-
}
135-
session2->Release();
215+
ComPtr<IAudioSessionControl> session;
216+
if (SUCCEEDED(session_enumerator->GetSession(i, &session)) && session) {
217+
ProcessAudioSession(session.Get(), pids, set_mute, save_mute_state);
136218
}
137-
session->Release();
138219
}
139-
140-
session_enumerator->Release();
141-
manager->Release();
142-
device->Release();
143-
enumerator->Release();
144-
CoUninitialize();
145220
}
146221

147222
void HideAndShow() {
@@ -162,6 +237,7 @@ void HideAndShow() {
162237
}
163238
hwnd_list.clear();
164239
MuteProcess(chrome_pids, false);
240+
original_mute_states.clear();
165241
}
166242
is_hide = !is_hide;
167243
}

0 commit comments

Comments
 (0)