Synchronize with trunk.
[reactos.git] / dll / cpl / mmsys / volume.c
1 /*
2 * PROJECT: ReactOS Multimedia Control Panel
3 * FILE: dll/cpl/mmsys/mmsys.c
4 * PURPOSE: ReactOS Multimedia Control Panel
5 * PROGRAMMER: Thomas Weidenmueller <w3seek@reactos.com>
6 * Johannes Anderwald <janderwald@reactos.com>
7 * Dmitry Chapyshev <dmitry@reactos.org>
8 */
9
10 #include "mmsys.h"
11
12 #define VOLUME_DIVIDER 0xFFF
13
14 typedef struct _IMGINFO
15 {
16 HBITMAP hBitmap;
17 INT cxSource;
18 INT cySource;
19 } IMGINFO, *PIMGINFO;
20
21
22 typedef struct _GLOBAL_DATA
23 {
24 HMIXER hMixer;
25 HICON hIconMuted;
26 HICON hIconUnMuted;
27
28 LONG muteVal;
29 DWORD muteControlID;
30
31 DWORD volumeControlID;
32 DWORD volumeMinimum;
33 DWORD volumeMaximum;
34 DWORD volumeValue;
35
36 } GLOBAL_DATA, *PGLOBAL_DATA;
37
38
39 static VOID
40 InitImageInfo(PIMGINFO ImgInfo)
41 {
42 BITMAP bitmap;
43
44 ZeroMemory(ImgInfo, sizeof(*ImgInfo));
45
46 ImgInfo->hBitmap = LoadImage(hApplet,
47 MAKEINTRESOURCE(IDB_SPEAKIMG),
48 IMAGE_BITMAP,
49 0,
50 0,
51 LR_DEFAULTCOLOR);
52
53 if (ImgInfo->hBitmap != NULL)
54 {
55 GetObject(ImgInfo->hBitmap, sizeof(BITMAP), &bitmap);
56
57 ImgInfo->cxSource = bitmap.bmWidth;
58 ImgInfo->cySource = bitmap.bmHeight;
59 }
60 }
61
62
63 VOID
64 GetMuteControl(PGLOBAL_DATA pGlobalData)
65 {
66 MIXERLINE mxln;
67 MIXERCONTROL mxc;
68 MIXERLINECONTROLS mxlctrl;
69
70 if (pGlobalData->hMixer == NULL)
71 return;
72
73 mxln.cbStruct = sizeof(MIXERLINE);
74 mxln.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
75
76 if (mixerGetLineInfo((HMIXEROBJ)pGlobalData->hMixer, &mxln, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_COMPONENTTYPE)
77 != MMSYSERR_NOERROR) return;
78
79 mxlctrl.cbStruct = sizeof(MIXERLINECONTROLS);
80 mxlctrl.dwLineID = mxln.dwLineID;
81 mxlctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
82 mxlctrl.cControls = 1;
83 mxlctrl.cbmxctrl = sizeof(MIXERCONTROL);
84 mxlctrl.pamxctrl = &mxc;
85
86 if (mixerGetLineControls((HMIXEROBJ)pGlobalData->hMixer, &mxlctrl, MIXER_OBJECTF_HMIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE)
87 != MMSYSERR_NOERROR) return;
88
89 pGlobalData->muteControlID = mxc.dwControlID;
90 }
91
92
93 VOID
94 GetMuteState(PGLOBAL_DATA pGlobalData)
95 {
96 MIXERCONTROLDETAILS_BOOLEAN mxcdMute;
97 MIXERCONTROLDETAILS mxcd;
98
99 if (pGlobalData->hMixer == NULL)
100 return;
101
102 mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
103 mxcd.dwControlID = pGlobalData->muteControlID;
104 mxcd.cChannels = 1;
105 mxcd.cMultipleItems = 0;
106 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
107 mxcd.paDetails = &mxcdMute;
108
109 if (mixerGetControlDetails((HMIXEROBJ)pGlobalData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_VALUE)
110 != MMSYSERR_NOERROR)
111 return;
112
113 pGlobalData->muteVal = mxcdMute.fValue;
114 }
115
116
117 VOID
118 SwitchMuteState(PGLOBAL_DATA pGlobalData)
119 {
120 MIXERCONTROLDETAILS_BOOLEAN mxcdMute;
121 MIXERCONTROLDETAILS mxcd;
122
123 mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
124 mxcd.dwControlID = pGlobalData->muteControlID;
125 mxcd.cChannels = 1;
126 mxcd.cMultipleItems = 0;
127 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
128 mxcd.paDetails = &mxcdMute;
129
130 mxcdMute.fValue = !pGlobalData->muteVal;
131 if (mixerSetControlDetails((HMIXEROBJ)pGlobalData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE)
132 != MMSYSERR_NOERROR)
133 return;
134
135 pGlobalData->muteVal = mxcdMute.fValue;
136 }
137
138
139 VOID
140 GetVolumeControl(PGLOBAL_DATA pGlobalData)
141 {
142 MIXERLINE mxln;
143 MIXERCONTROL mxc;
144 MIXERLINECONTROLS mxlc;
145
146 if (pGlobalData->hMixer == NULL)
147 return;
148
149 mxln.cbStruct = sizeof(MIXERLINE);
150 mxln.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
151 if (mixerGetLineInfo((HMIXEROBJ)pGlobalData->hMixer, &mxln, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_COMPONENTTYPE)
152 != MMSYSERR_NOERROR)
153 return;
154
155 mxlc.cbStruct = sizeof(MIXERLINECONTROLS);
156 mxlc.dwLineID = mxln.dwLineID;
157 mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
158 mxlc.cControls = 1;
159 mxlc.cbmxctrl = sizeof(MIXERCONTROL);
160 mxlc.pamxctrl = &mxc;
161 if (mixerGetLineControls((HMIXEROBJ)pGlobalData->hMixer, &mxlc, MIXER_OBJECTF_HMIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE)
162 != MMSYSERR_NOERROR)
163 return;
164
165 pGlobalData->volumeMinimum = mxc.Bounds.dwMinimum;
166 pGlobalData->volumeMaximum = mxc.Bounds.dwMaximum;
167 pGlobalData->volumeControlID = mxc.dwControlID;
168 }
169
170
171 VOID
172 GetVolumeValue(PGLOBAL_DATA pGlobalData)
173 {
174 MIXERCONTROLDETAILS_UNSIGNED mxcdVolume;
175 MIXERCONTROLDETAILS mxcd;
176
177 if (pGlobalData->hMixer == NULL)
178 return;
179
180 mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
181 mxcd.dwControlID = pGlobalData->volumeControlID;
182 mxcd.cChannels = 1;
183 mxcd.cMultipleItems = 0;
184 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
185 mxcd.paDetails = &mxcdVolume;
186
187 if (mixerGetControlDetails((HMIXEROBJ)pGlobalData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_VALUE)
188 != MMSYSERR_NOERROR)
189 return;
190
191 pGlobalData->volumeValue = mxcdVolume.dwValue;
192 }
193
194
195 VOID
196 SetVolumeValue(PGLOBAL_DATA pGlobalData){
197 MIXERCONTROLDETAILS_UNSIGNED mxcdVolume;
198 MIXERCONTROLDETAILS mxcd;
199
200 if (pGlobalData->hMixer == NULL)
201 return;
202
203 mxcdVolume.dwValue = pGlobalData->volumeValue;
204 mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
205 mxcd.dwControlID = pGlobalData->volumeControlID;
206 mxcd.cChannels = 1;
207 mxcd.cMultipleItems = 0;
208 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
209 mxcd.paDetails = &mxcdVolume;
210
211 if (mixerSetControlDetails((HMIXEROBJ)pGlobalData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE)
212 != MMSYSERR_NOERROR)
213 return;
214
215 pGlobalData->volumeValue = mxcdVolume.dwValue;
216 }
217
218
219 VOID
220 InitVolumeControls(HWND hwndDlg, PGLOBAL_DATA pGlobalData)
221 {
222 UINT NumMixers;
223 MIXERCAPS mxc;
224
225 NumMixers = mixerGetNumDevs();
226 if (!NumMixers)
227 {
228 EnableWindow(GetDlgItem(hwndDlg, IDC_VOLUME_TRACKBAR), FALSE);
229 EnableWindow(GetDlgItem(hwndDlg, IDC_MUTE_CHECKBOX), FALSE);
230 EnableWindow(GetDlgItem(hwndDlg, IDC_ICON_IN_TASKBAR), FALSE);
231 EnableWindow(GetDlgItem(hwndDlg, IDC_ADVANCED_BTN), FALSE);
232 EnableWindow(GetDlgItem(hwndDlg, IDC_SPEAKER_SET_BTN), FALSE);
233 EnableWindow(GetDlgItem(hwndDlg, IDC_SPEAKER_VOL_BTN), FALSE);
234 EnableWindow(GetDlgItem(hwndDlg, IDC_ADVANCED2_BTN), FALSE);
235 SendDlgItemMessage(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconMuted);
236 return;
237 }
238
239 if (mixerOpen(&pGlobalData->hMixer, 0, PtrToUlong(hwndDlg), 0, MIXER_OBJECTF_MIXER | CALLBACK_WINDOW) != MMSYSERR_NOERROR)
240 {
241 MessageBox(hwndDlg, _T("Cannot open mixer"), NULL, MB_OK);
242 return;
243 }
244
245 ZeroMemory(&mxc, sizeof(MIXERCAPS));
246 if (mixerGetDevCaps(PtrToUint(pGlobalData->hMixer), &mxc, sizeof(MIXERCAPS)) != MMSYSERR_NOERROR)
247 {
248 MessageBox(hwndDlg, _T("mixerGetDevCaps failed"), NULL, MB_OK);
249 return;
250 }
251
252 GetMuteControl(pGlobalData);
253 GetMuteState(pGlobalData);
254 if (pGlobalData->muteVal)
255 {
256 SendDlgItemMessage(hwndDlg, IDC_MUTE_CHECKBOX, BM_SETCHECK, (WPARAM)BST_CHECKED, (LPARAM)0);
257 SendDlgItemMessage(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconMuted);
258 }
259 else
260 {
261 SendDlgItemMessage(hwndDlg, IDC_MUTE_CHECKBOX, BM_SETCHECK, (WPARAM)BST_UNCHECKED, (LPARAM)0);
262 SendDlgItemMessage(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconUnMuted);
263 }
264
265 GetVolumeControl(pGlobalData);
266 GetVolumeValue(pGlobalData);
267
268 SendDlgItemMessage(hwndDlg, IDC_DEVICE_NAME, WM_SETTEXT, 0, (LPARAM)mxc.szPname);
269 SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETRANGE, (WPARAM)TRUE,
270 (LPARAM)MAKELONG(pGlobalData->volumeMinimum, pGlobalData->volumeMaximum/VOLUME_DIVIDER));
271 SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETPAGESIZE, (WPARAM)FALSE, (LPARAM)1);
272 SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETSEL, (WPARAM)FALSE,
273 (LPARAM)MAKELONG(pGlobalData->volumeMinimum, pGlobalData->volumeValue/VOLUME_DIVIDER));
274 SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)pGlobalData->volumeValue/VOLUME_DIVIDER);
275 }
276
277
278 VOID
279 LaunchSoundControl(HWND hwndDlg)
280 {
281 if ((INT_PTR)ShellExecuteW(NULL, L"open", L"sndvol32.exe", NULL, NULL, SW_SHOWNORMAL) > 32)
282 return;
283 MessageBox(hwndDlg, _T("Cannot run sndvol32.exe"), NULL, MB_OK);
284 }
285
286 /* Volume property page dialog callback */
287 //static INT_PTR CALLBACK
288 INT_PTR CALLBACK
289 VolumeDlgProc(HWND hwndDlg,
290 UINT uMsg,
291 WPARAM wParam,
292 LPARAM lParam)
293 {
294 static IMGINFO ImgInfo;
295 PGLOBAL_DATA pGlobalData;
296 UNREFERENCED_PARAMETER(lParam);
297 UNREFERENCED_PARAMETER(wParam);
298
299 pGlobalData = (PGLOBAL_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
300
301
302
303 switch(uMsg)
304 {
305 case MM_MIXM_LINE_CHANGE:
306 {
307 GetMuteState(pGlobalData);
308 if (pGlobalData->muteVal)
309 {
310 SendDlgItemMessage(hwndDlg, IDC_MUTE_CHECKBOX, BM_SETCHECK, (WPARAM)BST_CHECKED, (LPARAM)0);
311 SendDlgItemMessage(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconMuted);
312 }
313 else
314 {
315 SendDlgItemMessage(hwndDlg, IDC_MUTE_CHECKBOX, BM_SETCHECK, (WPARAM)BST_UNCHECKED, (LPARAM)0);
316 SendDlgItemMessage(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconUnMuted);
317 }
318 break;
319 }
320 case MM_MIXM_CONTROL_CHANGE:
321 {
322 GetVolumeValue(pGlobalData);
323 SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETSEL, (WPARAM)FALSE, (LPARAM)MAKELONG(pGlobalData->volumeMinimum, pGlobalData->volumeValue/VOLUME_DIVIDER));
324 SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)pGlobalData->volumeValue/VOLUME_DIVIDER);
325 break;
326 }
327 case WM_INITDIALOG:
328 {
329 pGlobalData = (GLOBAL_DATA*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(GLOBAL_DATA));
330 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pGlobalData);
331
332 pGlobalData->hIconUnMuted = LoadImage(hApplet, MAKEINTRESOURCE(IDI_CPLICON), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);
333 pGlobalData->hIconMuted = LoadImage(hApplet, MAKEINTRESOURCE(IDI_MUTED_ICON), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);
334
335 InitImageInfo(&ImgInfo);
336 InitVolumeControls(hwndDlg, pGlobalData);
337 break;
338 }
339
340 case WM_DRAWITEM:
341 {
342 LPDRAWITEMSTRUCT lpDrawItem;
343 lpDrawItem = (LPDRAWITEMSTRUCT) lParam;
344 if(lpDrawItem->CtlID == IDC_SPEAKIMG)
345 {
346 HDC hdcMem;
347 LONG left;
348
349 /* Position image in centre of dialog */
350 left = (lpDrawItem->rcItem.right - ImgInfo.cxSource) / 2;
351
352 hdcMem = CreateCompatibleDC(lpDrawItem->hDC);
353 if (hdcMem != NULL)
354 {
355 SelectObject(hdcMem, ImgInfo.hBitmap);
356 BitBlt(lpDrawItem->hDC,
357 left,
358 lpDrawItem->rcItem.top,
359 lpDrawItem->rcItem.right - lpDrawItem->rcItem.left,
360 lpDrawItem->rcItem.bottom - lpDrawItem->rcItem.top,
361 hdcMem,
362 0,
363 0,
364 SRCCOPY);
365 DeleteDC(hdcMem);
366 }
367 }
368 break;
369 }
370
371 case WM_COMMAND:
372 {
373 switch (LOWORD(wParam))
374 {
375 case IDC_MUTE_CHECKBOX:
376 SwitchMuteState(pGlobalData);
377 if (pGlobalData->muteVal)
378 {
379 SendDlgItemMessage(hwndDlg, IDC_MUTE_CHECKBOX, BM_SETCHECK, (WPARAM)BST_CHECKED, (LPARAM)0);
380 SendDlgItemMessage(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconMuted);
381 }
382 else
383 {
384 SendDlgItemMessage(hwndDlg, IDC_MUTE_CHECKBOX, BM_SETCHECK, (WPARAM)BST_UNCHECKED, (LPARAM)0);
385 SendDlgItemMessage(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconUnMuted);
386 }
387 break;
388 case IDC_ADVANCED_BTN:
389 LaunchSoundControl(hwndDlg);
390 break;
391 }
392 break;
393 }
394
395 case WM_HSCROLL:
396 {
397 HWND hVolumeTrackbar = GetDlgItem(hwndDlg, IDC_VOLUME_TRACKBAR);
398 if (hVolumeTrackbar == (HWND)lParam)
399 {
400 pGlobalData->volumeValue = (DWORD)SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_GETPOS, 0, 0)*VOLUME_DIVIDER;
401 SetVolumeValue(pGlobalData);
402 SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETSEL, (WPARAM)TRUE,
403 (LPARAM)MAKELONG(pGlobalData->volumeMinimum, pGlobalData->volumeValue/VOLUME_DIVIDER));
404 }
405 break;
406 }
407
408 case WM_DESTROY:
409 mixerClose(pGlobalData->hMixer);
410 DestroyIcon(pGlobalData->hIconMuted);
411 DestroyIcon(pGlobalData->hIconUnMuted);
412 HeapFree(GetProcessHeap(), 0, pGlobalData);
413 break;
414 }
415
416 return FALSE;
417 }