* Sync up to trunk head (r64894).
[reactos.git] / dll / shellext / stobject / volume.cpp
1 /*
2 * PROJECT: ReactOS system libraries
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll\win32\stobject\volume.cpp
5 * PURPOSE: Volume notification icon handler
6 * PROGRAMMERS: David Quintana <gigaherz@gmail.com>
7 */
8
9 #include "precomp.h"
10
11 #include <mmsystem.h>
12 #include <mmddk.h>
13
14 WINE_DEFAULT_DEBUG_CHANNEL(stobject);
15
16 HICON g_hIconVolume;
17 HICON g_hIconMute;
18
19 HMIXER g_hMixer;
20 UINT g_mixerId;
21 DWORD g_mixerLineID;
22 DWORD g_muteControlID;
23
24 UINT g_mmDeviceChange;
25
26 BOOL g_IsMute = FALSE;
27
28 static HRESULT __stdcall Volume_FindMixerControl(CSysTray * pSysTray)
29 {
30 MMRESULT result;
31 UINT mixerId = 0;
32 DWORD waveOutId = 0;
33 DWORD param2 = 0;
34
35 TRACE("Volume_FindDefaultMixerID\n");
36
37 result = waveOutMessage((HWAVEOUT) WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR) &waveOutId, (DWORD_PTR) &param2);
38 if (result)
39 return E_FAIL;
40
41 if (waveOutId == (DWORD)-1)
42 {
43 TRACE("WARNING: waveOut has no default device, trying with first available device...\n", waveOutId);
44
45 mixerId = 0;
46 }
47 else
48 {
49 TRACE("waveOut default device is %d\n", waveOutId);
50
51 result = mixerGetID((HMIXEROBJ) waveOutId, &mixerId, MIXER_OBJECTF_WAVEOUT);
52 if (result)
53 return E_FAIL;
54
55 TRACE("mixerId for waveOut default device is %d\n", mixerId);
56 }
57
58 g_mixerId = mixerId;
59 return S_OK;
60
61 MIXERCAPS mixerCaps;
62 MIXERLINE mixerLine;
63 MIXERCONTROL mixerControl;
64 MIXERLINECONTROLS mixerLineControls;
65
66 g_mixerLineID = -1;
67 g_muteControlID = -1;
68
69 if (mixerGetDevCapsW(g_mixerId, &mixerCaps, sizeof(mixerCaps)))
70 return E_FAIL;
71
72 if (mixerCaps.cDestinations == 0)
73 return S_FALSE;
74
75 TRACE("mixerCaps.cDestinations %d\n", mixerCaps.cDestinations);
76
77 DWORD idx;
78 for (idx = 0; idx < mixerCaps.cDestinations; idx++)
79 {
80 mixerLine.cbStruct = sizeof(mixerLine);
81 mixerLine.dwDestination = idx;
82 if (!mixerGetLineInfoW((HMIXEROBJ) g_mixerId, &mixerLine, 0))
83 {
84 if (mixerLine.dwComponentType >= MIXERLINE_COMPONENTTYPE_DST_SPEAKERS &&
85 mixerLine.dwComponentType <= MIXERLINE_COMPONENTTYPE_DST_HEADPHONES)
86 break;
87 TRACE("Destination %d was not speakers or headphones.\n");
88 }
89 }
90
91 if (idx >= mixerCaps.cDestinations)
92 return E_FAIL;
93
94 TRACE("Valid destination %d found.\n");
95
96 g_mixerLineID = mixerLine.dwLineID;
97
98 mixerLineControls.cbStruct = sizeof(mixerLineControls);
99 mixerLineControls.dwLineID = mixerLine.dwLineID;
100 mixerLineControls.cControls = 1;
101 mixerLineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
102 mixerLineControls.pamxctrl = &mixerControl;
103 mixerLineControls.cbmxctrl = sizeof(mixerControl);
104
105 if (mixerGetLineControlsW((HMIXEROBJ) g_mixerId, &mixerLineControls, MIXER_GETLINECONTROLSF_ONEBYTYPE))
106 return E_FAIL;
107
108 TRACE("Found control id %d for mute: %d\n", mixerControl.dwControlID);
109
110 g_muteControlID = mixerControl.dwControlID;
111
112 return S_OK;
113 }
114
115 HRESULT Volume_IsMute()
116 {
117 MIXERCONTROLDETAILS mixerControlDetails;
118
119 if (g_mixerId != (UINT)-1 && g_muteControlID != (DWORD)-1)
120 {
121 BOOL detailsResult = 0;
122 mixerControlDetails.cbStruct = sizeof(mixerControlDetails);
123 mixerControlDetails.hwndOwner = 0;
124 mixerControlDetails.dwControlID = g_muteControlID;
125 mixerControlDetails.cChannels = 1;
126 mixerControlDetails.paDetails = &detailsResult;
127 mixerControlDetails.cbDetails = sizeof(detailsResult);
128 if (mixerGetControlDetailsW((HMIXEROBJ) g_mixerId, &mixerControlDetails, 0))
129 return E_FAIL;
130
131 TRACE("Obtained mute status %d\n", detailsResult);
132
133 g_IsMute = detailsResult != 0;
134 }
135
136 return S_OK;
137 }
138
139 HRESULT STDMETHODCALLTYPE Volume_Init(_In_ CSysTray * pSysTray)
140 {
141 HRESULT hr;
142
143 TRACE("Volume_Init\n");
144
145 if (!g_hMixer)
146 {
147 hr = Volume_FindMixerControl(pSysTray);
148 if (FAILED(hr))
149 return hr;
150
151 g_mmDeviceChange = RegisterWindowMessageW(L"winmm_devicechange");
152 }
153
154 g_hIconVolume = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_VOLUME));
155 g_hIconMute = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_VOLMUTE));
156
157 Volume_IsMute();
158
159 HICON icon;
160 if (g_IsMute)
161 icon = g_hIconMute;
162 else
163 icon = g_hIconVolume;
164
165 pSysTray->NotifyIcon(NIM_ADD, ID_ICON_VOLUME, icon, L"Placeholder");
166 return pSysTray->NotifyIcon(NIM_MODIFY, ID_ICON_VOLUME, icon, L"Placeholder");
167 }
168
169 HRESULT STDMETHODCALLTYPE Volume_Update(_In_ CSysTray * pSysTray)
170 {
171 TRACE("Volume_Update\n");
172
173 Volume_IsMute();
174
175 HICON icon;
176 if (g_IsMute)
177 icon = g_hIconMute;
178 else
179 icon = g_hIconVolume;
180
181 return pSysTray->NotifyIcon(NIM_MODIFY, ID_ICON_VOLUME, icon, L"Placeholder");
182 }
183
184 HRESULT STDMETHODCALLTYPE Volume_Shutdown(_In_ CSysTray * pSysTray)
185 {
186 TRACE("Volume_Shutdown\n");
187
188 return pSysTray->NotifyIcon(NIM_DELETE, ID_ICON_VOLUME, NULL, NULL);
189 }
190
191 HRESULT Volume_OnDeviceChange(_In_ CSysTray * pSysTray, WPARAM wParam, LPARAM lParam)
192 {
193 return Volume_FindMixerControl(pSysTray);
194 }
195
196 HRESULT STDMETHODCALLTYPE Volume_Message(_In_ CSysTray * pSysTray, UINT uMsg, WPARAM wParam, LPARAM lParam)
197 {
198 if (uMsg == g_mmDeviceChange)
199 return Volume_OnDeviceChange(pSysTray, wParam, lParam);
200
201 if (uMsg != ID_ICON_VOLUME)
202 return S_FALSE;
203
204 TRACE("Volume_Message\n");
205
206 TRACE("Calling update...\n");
207 Volume_Update(pSysTray);
208
209 switch (lParam)
210 {
211 case WM_LBUTTONDOWN:
212 break;
213 case WM_LBUTTONUP:
214 TRACE("TODO: display volume slider\n");
215 break;
216 case WM_LBUTTONDBLCLK:
217 // FIXME: ensure we are loading the right one
218 ShellExecute(NULL, NULL, L"sndvol32.exe", NULL, NULL, SW_SHOWNORMAL);
219 break;
220 case WM_RBUTTONDOWN:
221 break;
222 case WM_RBUTTONUP:
223 break;
224 case WM_RBUTTONDBLCLK:
225 break;
226 case WM_MOUSEMOVE:
227 break;
228 }
229
230 return S_OK;
231 }