c9f345bddd94d059f34e35f8253f67786ac02a9d
[reactos.git] / reactos / 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 <windows.h>
12 #include <commctrl.h>
13 #include <setupapi.h>
14 #include <cpl.h>
15 #include <tchar.h>
16 #include <stdio.h>
17 #include "mmsys.h"
18 #include "resource.h"
19
20 #define VOLUME_DIVIDER 0xFFF
21
22 typedef struct _IMGINFO
23 {
24 HBITMAP hBitmap;
25 INT cxSource;
26 INT cySource;
27 } IMGINFO, *PIMGINFO;
28
29
30 typedef struct _GLOBAL_DATA
31 {
32 HMIXER hMixer;
33 HICON hIconMuted;
34 HICON hIconUnMuted;
35
36 LONG muteVal;
37 DWORD muteControlID;
38
39 DWORD volumeControlID;
40 DWORD volumeMinimum;
41 DWORD volumeMaximum;
42 DWORD volumeValue;
43
44 } GLOBAL_DATA, *PGLOBAL_DATA;
45
46
47 static VOID
48 InitImageInfo(PIMGINFO ImgInfo)
49 {
50 BITMAP bitmap;
51
52 ZeroMemory(ImgInfo, sizeof(*ImgInfo));
53
54 ImgInfo->hBitmap = LoadImage(hApplet,
55 MAKEINTRESOURCE(IDB_SPEAKIMG),
56 IMAGE_BITMAP,
57 0,
58 0,
59 LR_DEFAULTCOLOR);
60
61 if (ImgInfo->hBitmap != NULL)
62 {
63 GetObject(ImgInfo->hBitmap, sizeof(BITMAP), &bitmap);
64
65 ImgInfo->cxSource = bitmap.bmWidth;
66 ImgInfo->cySource = bitmap.bmHeight;
67 }
68 }
69
70 VOID
71 GetMuteControl(PGLOBAL_DATA pGlobalData)
72 {
73 MIXERLINE mxln;
74 MIXERCONTROL mxc;
75 MIXERLINECONTROLS mxlctrl;
76
77 if (pGlobalData->hMixer == NULL)
78 return;
79
80 mxln.cbStruct = sizeof(MIXERLINE);
81 mxln.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
82
83 if (mixerGetLineInfo((HMIXEROBJ)pGlobalData->hMixer, &mxln, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_COMPONENTTYPE)
84 != MMSYSERR_NOERROR) return;
85
86 mxlctrl.cbStruct = sizeof(MIXERLINECONTROLS);
87 mxlctrl.dwLineID = mxln.dwLineID;
88 mxlctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
89 mxlctrl.cControls = 1;
90 mxlctrl.cbmxctrl = sizeof(MIXERCONTROL);
91 mxlctrl.pamxctrl = &mxc;
92
93 if (mixerGetLineControls((HMIXEROBJ)pGlobalData->hMixer, &mxlctrl, MIXER_OBJECTF_HMIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE)
94 != MMSYSERR_NOERROR) return;
95
96 pGlobalData->muteControlID = mxc.dwControlID;
97 }
98
99 VOID
100 GetMuteState(PGLOBAL_DATA pGlobalData)
101 {
102 MIXERCONTROLDETAILS_BOOLEAN mxcdMute;
103 MIXERCONTROLDETAILS mxcd;
104
105 if (pGlobalData->hMixer == NULL)
106 return;
107
108 mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
109 mxcd.dwControlID = pGlobalData->muteControlID;
110 mxcd.cChannels = 1;
111 mxcd.cMultipleItems = 0;
112 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
113 mxcd.paDetails = &mxcdMute;
114
115 if (mixerGetControlDetails((HMIXEROBJ)pGlobalData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_VALUE)
116 != MMSYSERR_NOERROR)
117 return;
118
119 pGlobalData->muteVal = mxcdMute.fValue;
120 }
121
122 VOID
123 SwitchMuteState(PGLOBAL_DATA pGlobalData)
124 {
125 MIXERCONTROLDETAILS_BOOLEAN mxcdMute;
126 MIXERCONTROLDETAILS mxcd;
127
128 mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
129 mxcd.dwControlID = pGlobalData->muteControlID;
130 mxcd.cChannels = 1;
131 mxcd.cMultipleItems = 0;
132 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
133 mxcd.paDetails = &mxcdMute;
134
135 mxcdMute.fValue = !pGlobalData->muteVal;
136 if (mixerSetControlDetails((HMIXEROBJ)pGlobalData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE)
137 != MMSYSERR_NOERROR)
138 return;
139
140 pGlobalData->muteVal = mxcdMute.fValue;
141 }
142
143 VOID
144 GetVolumeControl(PGLOBAL_DATA pGlobalData)
145 {
146 MIXERLINE mxln;
147 MIXERCONTROL mxc;
148 MIXERLINECONTROLS mxlc;
149
150 if (pGlobalData->hMixer == NULL)
151 return;
152
153 mxln.cbStruct = sizeof(MIXERLINE);
154 mxln.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
155 if (mixerGetLineInfo((HMIXEROBJ)pGlobalData->hMixer, &mxln, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_COMPONENTTYPE)
156 != MMSYSERR_NOERROR)
157 return;
158
159 mxlc.cbStruct = sizeof(MIXERLINECONTROLS);
160 mxlc.dwLineID = mxln.dwLineID;
161 mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
162 mxlc.cControls = 1;
163 mxlc.cbmxctrl = sizeof(MIXERCONTROL);
164 mxlc.pamxctrl = &mxc;
165 if (mixerGetLineControls((HMIXEROBJ)pGlobalData->hMixer, &mxlc, MIXER_OBJECTF_HMIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE)
166 != MMSYSERR_NOERROR)
167 return;
168
169 pGlobalData->volumeMinimum = mxc.Bounds.dwMinimum;
170 pGlobalData->volumeMaximum = mxc.Bounds.dwMaximum;
171 pGlobalData->volumeControlID = mxc.dwControlID;
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 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 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 switch(uMsg)
303 {
304 case WM_INITDIALOG:
305 {
306 pGlobalData = (GLOBAL_DATA*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(GLOBAL_DATA));
307 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pGlobalData);
308
309 pGlobalData->hIconUnMuted = LoadImage(hApplet, MAKEINTRESOURCE(IDI_CPLICON), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);
310 pGlobalData->hIconMuted = LoadImage(hApplet, MAKEINTRESOURCE(IDI_MUTED_ICON), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);
311
312 InitImageInfo(&ImgInfo);
313 InitVolumeControls(hwndDlg, pGlobalData);
314 break;
315 }
316
317 case WM_DRAWITEM:
318 {
319 LPDRAWITEMSTRUCT lpDrawItem;
320 lpDrawItem = (LPDRAWITEMSTRUCT) lParam;
321 if(lpDrawItem->CtlID == IDC_SPEAKIMG)
322 {
323 HDC hdcMem;
324 LONG left;
325
326 /* position image in centre of dialog */
327 left = (lpDrawItem->rcItem.right - ImgInfo.cxSource) / 2;
328
329 hdcMem = CreateCompatibleDC(lpDrawItem->hDC);
330 if (hdcMem != NULL)
331 {
332 SelectObject(hdcMem, ImgInfo.hBitmap);
333 BitBlt(lpDrawItem->hDC,
334 left,
335 lpDrawItem->rcItem.top,
336 lpDrawItem->rcItem.right - lpDrawItem->rcItem.left,
337 lpDrawItem->rcItem.bottom - lpDrawItem->rcItem.top,
338 hdcMem,
339 0,
340 0,
341 SRCCOPY);
342 DeleteDC(hdcMem);
343 }
344 }
345 break;
346 }
347
348 case WM_COMMAND:
349 {
350 switch (LOWORD(wParam))
351 {
352 case IDC_MUTE_CHECKBOX:
353 SwitchMuteState(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 case IDC_ADVANCED_BTN:
366 LaunchSoundControl(hwndDlg);
367 break;
368 }
369 break;
370 }
371
372 case WM_HSCROLL:
373 {
374 HWND hVolumeTrackbar = GetDlgItem(hwndDlg, IDC_VOLUME_TRACKBAR);
375 if (hVolumeTrackbar == (HWND)lParam)
376 {
377 pGlobalData->volumeValue = (DWORD)SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_GETPOS, 0, 0)*VOLUME_DIVIDER;
378 SetVolumeValue(pGlobalData);
379 SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETSEL, (WPARAM)TRUE,
380 (LPARAM)MAKELONG(pGlobalData->volumeMinimum, pGlobalData->volumeValue/VOLUME_DIVIDER));
381 }
382 break;
383 }
384
385 case WM_DESTROY:
386 mixerClose(pGlobalData->hMixer);
387 DestroyIcon(pGlobalData->hIconMuted);
388 DestroyIcon(pGlobalData->hIconUnMuted);
389 HeapFree(GetProcessHeap(), 0, pGlobalData);
390 break;
391 }
392
393 return FALSE;
394 }