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