2 * PROJECT: ReactOS system libraries
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/shellext/stobject/volume.cpp
5 * PURPOSE: Volume notification icon handler
6 * PROGRAMMERS: David Quintana <gigaherz@gmail.com>
19 DWORD g_muteControlID
;
21 UINT g_mmDeviceChange
;
23 static BOOL g_IsMute
= FALSE
;
25 static HRESULT __stdcall
Volume_FindMixerControl(CSysTray
* pSysTray
)
32 TRACE("Volume_FindDefaultMixerID\n");
34 result
= waveOutMessage((HWAVEOUT
)UlongToHandle(WAVE_MAPPER
), DRVM_MAPPER_PREFERRED_GET
, (DWORD_PTR
)&waveOutId
, (DWORD_PTR
)¶m2
);
38 if (waveOutId
== (DWORD
)-1)
40 TRACE("WARNING: waveOut has no default device, trying with first available device...\n", waveOutId
);
46 TRACE("waveOut default device is %d\n", waveOutId
);
48 result
= mixerGetID((HMIXEROBJ
)UlongToHandle(waveOutId
), &mixerId
, MIXER_OBJECTF_WAVEOUT
);
52 TRACE("mixerId for waveOut default device is %d\n", mixerId
);
60 MIXERCONTROL mixerControl
;
61 MIXERLINECONTROLS mixerLineControls
;
66 if (mixerGetDevCapsW(g_mixerId
, &mixerCaps
, sizeof(mixerCaps
)))
69 if (mixerCaps
.cDestinations
== 0)
72 TRACE("mixerCaps.cDestinations %d\n", mixerCaps
.cDestinations
);
75 for (idx
= 0; idx
< mixerCaps
.cDestinations
; idx
++)
77 mixerLine
.cbStruct
= sizeof(mixerLine
);
78 mixerLine
.dwDestination
= idx
;
79 if (!mixerGetLineInfoW((HMIXEROBJ
)UlongToHandle(g_mixerId
), &mixerLine
, 0))
81 if (mixerLine
.dwComponentType
>= MIXERLINE_COMPONENTTYPE_DST_SPEAKERS
&&
82 mixerLine
.dwComponentType
<= MIXERLINE_COMPONENTTYPE_DST_HEADPHONES
)
84 TRACE("Destination %d was not speakers or headphones.\n");
88 if (idx
>= mixerCaps
.cDestinations
)
91 TRACE("Valid destination %d found.\n");
93 g_mixerLineID
= mixerLine
.dwLineID
;
95 mixerLineControls
.cbStruct
= sizeof(mixerLineControls
);
96 mixerLineControls
.dwLineID
= mixerLine
.dwLineID
;
97 mixerLineControls
.cControls
= 1;
98 mixerLineControls
.dwControlType
= MIXERCONTROL_CONTROLTYPE_MUTE
;
99 mixerLineControls
.pamxctrl
= &mixerControl
;
100 mixerLineControls
.cbmxctrl
= sizeof(mixerControl
);
102 if (mixerGetLineControlsW((HMIXEROBJ
)UlongToHandle(g_mixerId
), &mixerLineControls
, MIXER_GETLINECONTROLSF_ONEBYTYPE
))
105 TRACE("Found control id %d for mute: %d\n", mixerControl
.dwControlID
);
107 g_muteControlID
= mixerControl
.dwControlID
;
112 HRESULT
Volume_IsMute()
115 MIXERCONTROLDETAILS mixerControlDetails
;
117 if (g_mixerId
!= (UINT
)-1 && g_muteControlID
!= (DWORD
)-1)
119 BOOL detailsResult
= 0;
120 mixerControlDetails
.cbStruct
= sizeof(mixerControlDetails
);
121 mixerControlDetails
.hwndOwner
= 0;
122 mixerControlDetails
.dwControlID
= g_muteControlID
;
123 mixerControlDetails
.cChannels
= 1;
124 mixerControlDetails
.paDetails
= &detailsResult
;
125 mixerControlDetails
.cbDetails
= sizeof(detailsResult
);
126 if (mixerGetControlDetailsW((HMIXEROBJ
) g_mixerId
, &mixerControlDetails
, 0))
129 TRACE("Obtained mute status %d\n", detailsResult
);
131 g_IsMute
= detailsResult
!= 0;
137 HRESULT STDMETHODCALLTYPE
Volume_Init(_In_ CSysTray
* pSysTray
)
140 WCHAR strTooltip
[128];
142 TRACE("Volume_Init\n");
146 hr
= Volume_FindMixerControl(pSysTray
);
150 g_mmDeviceChange
= RegisterWindowMessageW(L
"winmm_devicechange");
153 g_hIconVolume
= LoadIcon(g_hInstance
, MAKEINTRESOURCE(IDI_VOLUME
));
154 g_hIconMute
= LoadIcon(g_hInstance
, MAKEINTRESOURCE(IDI_VOLMUTE
));
162 icon
= g_hIconVolume
;
164 LoadStringW(g_hInstance
, IDS_VOL_VOLUME
, strTooltip
, _countof(strTooltip
));
165 return pSysTray
->NotifyIcon(NIM_ADD
, ID_ICON_VOLUME
, icon
, strTooltip
);
168 HRESULT STDMETHODCALLTYPE
Volume_Update(_In_ CSysTray
* pSysTray
)
172 TRACE("Volume_Update\n");
174 PrevState
= g_IsMute
;
177 if (PrevState
!= g_IsMute
)
179 WCHAR strTooltip
[128];
184 LoadStringW(g_hInstance
, IDS_VOL_MUTED
, strTooltip
, _countof(strTooltip
));
188 icon
= g_hIconVolume
;
189 LoadStringW(g_hInstance
, IDS_VOL_VOLUME
, strTooltip
, _countof(strTooltip
));
192 return pSysTray
->NotifyIcon(NIM_MODIFY
, ID_ICON_VOLUME
, icon
, strTooltip
);
200 HRESULT STDMETHODCALLTYPE
Volume_Shutdown(_In_ CSysTray
* pSysTray
)
202 TRACE("Volume_Shutdown\n");
204 return pSysTray
->NotifyIcon(NIM_DELETE
, ID_ICON_VOLUME
, NULL
, NULL
);
207 HRESULT
Volume_OnDeviceChange(_In_ CSysTray
* pSysTray
, WPARAM wParam
, LPARAM lParam
)
209 return Volume_FindMixerControl(pSysTray
);
212 static void _RunVolume(BOOL bTray
)
214 ShellExecuteW(NULL
, NULL
, bTray
? L
"sndvol32.exe /t" : L
"sndvol32.exe", NULL
, NULL
, SW_SHOWNORMAL
);
217 static void _RunMMCpl()
219 ShellExecuteW(NULL
, NULL
, L
"mmsys.cpl", NULL
, NULL
, SW_NORMAL
);
222 static void _ShowContextMenu(CSysTray
* pSysTray
)
224 WCHAR strAdjust
[128];
226 LoadStringW(g_hInstance
, IDS_VOL_OPEN
, strOpen
, _countof(strOpen
));
227 LoadStringW(g_hInstance
, IDS_VOL_ADJUST
, strAdjust
, _countof(strAdjust
));
229 HMENU hPopup
= CreatePopupMenu();
230 AppendMenuW(hPopup
, MF_STRING
, IDS_VOL_OPEN
, strOpen
);
231 AppendMenuW(hPopup
, MF_STRING
, IDS_VOL_ADJUST
, strAdjust
);
232 SetMenuDefaultItem(hPopup
, IDS_VOL_OPEN
, FALSE
);
234 DWORD flags
= TPM_RETURNCMD
| TPM_NONOTIFY
| TPM_RIGHTALIGN
| TPM_BOTTOMALIGN
;
236 SetForegroundWindow(pSysTray
->GetHWnd());
239 DWORD id
= TrackPopupMenuEx(hPopup
, flags
,
241 pSysTray
->GetHWnd(), NULL
);
256 HRESULT STDMETHODCALLTYPE
Volume_Message(_In_ CSysTray
* pSysTray
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
, LRESULT
&lResult
)
258 if (uMsg
== g_mmDeviceChange
)
259 return Volume_OnDeviceChange(pSysTray
, wParam
, lParam
);
264 TRACE("Volume_Message: WM_USER+220\n");
265 if (wParam
== VOLUME_SERVICE_FLAG
)
269 pSysTray
->EnableService(VOLUME_SERVICE_FLAG
, TRUE
);
270 return Volume_Init(pSysTray
);
274 pSysTray
->EnableService(VOLUME_SERVICE_FLAG
, FALSE
);
275 return Volume_Shutdown(pSysTray
);
281 TRACE("Volume_Message: WM_USER+221\n");
282 if (wParam
== VOLUME_SERVICE_FLAG
)
284 lResult
= (LRESULT
)pSysTray
->IsServiceEnabled(VOLUME_SERVICE_FLAG
);
290 if (wParam
== VOLUME_TIMER_ID
)
292 KillTimer(pSysTray
->GetHWnd(), VOLUME_TIMER_ID
);
298 TRACE("Volume_Message uMsg=%d, w=%x, l=%x\n", uMsg
, wParam
, lParam
);
300 Volume_Update(pSysTray
);
305 SetTimer(pSysTray
->GetHWnd(), VOLUME_TIMER_ID
, GetDoubleClickTime(), NULL
);
311 case WM_LBUTTONDBLCLK
:
312 KillTimer(pSysTray
->GetHWnd(), VOLUME_TIMER_ID
);
320 _ShowContextMenu(pSysTray
);
323 case WM_RBUTTONDBLCLK
:
332 TRACE("Volume_Message received for unknown ID %d, ignoring.\n");