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