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