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