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>
14 #define VOLUME_DIVIDER 0xFFF
16 typedef struct _IMGINFO
24 typedef struct _GLOBAL_DATA
34 DWORD volumeControlID
;
39 } GLOBAL_DATA
, *PGLOBAL_DATA
;
43 InitImageInfo(PIMGINFO ImgInfo
)
47 ZeroMemory(ImgInfo
, sizeof(*ImgInfo
));
49 ImgInfo
->hBitmap
= LoadImage(hApplet
,
50 MAKEINTRESOURCE(IDB_SPEAKIMG
),
56 if (ImgInfo
->hBitmap
!= NULL
)
58 GetObject(ImgInfo
->hBitmap
, sizeof(BITMAP
), &bitmap
);
60 ImgInfo
->cxSource
= bitmap
.bmWidth
;
61 ImgInfo
->cySource
= bitmap
.bmHeight
;
67 GetMuteControl(PGLOBAL_DATA pGlobalData
)
71 MIXERLINECONTROLS mxlctrl
;
73 if (pGlobalData
->hMixer
== NULL
)
76 mxln
.cbStruct
= sizeof(MIXERLINE
);
77 mxln
.dwComponentType
= MIXERLINE_COMPONENTTYPE_DST_SPEAKERS
;
79 if (mixerGetLineInfo((HMIXEROBJ
)pGlobalData
->hMixer
, &mxln
, MIXER_OBJECTF_HMIXER
| MIXER_GETLINEINFOF_COMPONENTTYPE
)
80 != MMSYSERR_NOERROR
) return;
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
;
89 if (mixerGetLineControls((HMIXEROBJ
)pGlobalData
->hMixer
, &mxlctrl
, MIXER_OBJECTF_HMIXER
| MIXER_GETLINECONTROLSF_ONEBYTYPE
)
90 != MMSYSERR_NOERROR
) return;
92 pGlobalData
->muteControlID
= mxc
.dwControlID
;
97 GetMuteState(PGLOBAL_DATA pGlobalData
)
99 MIXERCONTROLDETAILS_BOOLEAN mxcdMute
;
100 MIXERCONTROLDETAILS mxcd
;
102 if (pGlobalData
->hMixer
== NULL
)
105 mxcd
.cbStruct
= sizeof(MIXERCONTROLDETAILS
);
106 mxcd
.dwControlID
= pGlobalData
->muteControlID
;
108 mxcd
.cMultipleItems
= 0;
109 mxcd
.cbDetails
= sizeof(MIXERCONTROLDETAILS_BOOLEAN
);
110 mxcd
.paDetails
= &mxcdMute
;
112 if (mixerGetControlDetails((HMIXEROBJ
)pGlobalData
->hMixer
, &mxcd
, MIXER_OBJECTF_HMIXER
| MIXER_GETCONTROLDETAILSF_VALUE
)
116 pGlobalData
->muteVal
= mxcdMute
.fValue
;
121 SwitchMuteState(PGLOBAL_DATA pGlobalData
)
123 MIXERCONTROLDETAILS_BOOLEAN mxcdMute
;
124 MIXERCONTROLDETAILS mxcd
;
126 mxcd
.cbStruct
= sizeof(MIXERCONTROLDETAILS
);
127 mxcd
.dwControlID
= pGlobalData
->muteControlID
;
129 mxcd
.cMultipleItems
= 0;
130 mxcd
.cbDetails
= sizeof(MIXERCONTROLDETAILS_BOOLEAN
);
131 mxcd
.paDetails
= &mxcdMute
;
133 mxcdMute
.fValue
= !pGlobalData
->muteVal
;
134 if (mixerSetControlDetails((HMIXEROBJ
)pGlobalData
->hMixer
, &mxcd
, MIXER_OBJECTF_HMIXER
| MIXER_SETCONTROLDETAILSF_VALUE
)
138 pGlobalData
->muteVal
= mxcdMute
.fValue
;
143 GetVolumeControl(PGLOBAL_DATA pGlobalData
)
147 MIXERLINECONTROLS mxlc
;
149 if (pGlobalData
->hMixer
== NULL
)
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
)
158 mxlc
.cbStruct
= sizeof(MIXERLINECONTROLS
);
159 mxlc
.dwLineID
= mxln
.dwLineID
;
160 mxlc
.dwControlType
= MIXERCONTROL_CONTROLTYPE_VOLUME
;
162 mxlc
.cbmxctrl
= sizeof(MIXERCONTROL
);
163 mxlc
.pamxctrl
= &mxc
;
164 if (mixerGetLineControls((HMIXEROBJ
)pGlobalData
->hMixer
, &mxlc
, MIXER_OBJECTF_HMIXER
| MIXER_GETLINECONTROLSF_ONEBYTYPE
)
168 pGlobalData
->volumeMinimum
= mxc
.Bounds
.dwMinimum
;
169 pGlobalData
->volumeMaximum
= mxc
.Bounds
.dwMaximum
;
170 pGlobalData
->volumeControlID
= mxc
.dwControlID
;
175 GetVolumeValue(PGLOBAL_DATA pGlobalData
)
177 MIXERCONTROLDETAILS_UNSIGNED mxcdVolume
;
178 MIXERCONTROLDETAILS mxcd
;
180 if (pGlobalData
->hMixer
== NULL
)
183 mxcd
.cbStruct
= sizeof(MIXERCONTROLDETAILS
);
184 mxcd
.dwControlID
= pGlobalData
->volumeControlID
;
186 mxcd
.cMultipleItems
= 0;
187 mxcd
.cbDetails
= sizeof(MIXERCONTROLDETAILS_UNSIGNED
);
188 mxcd
.paDetails
= &mxcdVolume
;
190 if (mixerGetControlDetails((HMIXEROBJ
)pGlobalData
->hMixer
, &mxcd
, MIXER_OBJECTF_HMIXER
| MIXER_GETCONTROLDETAILSF_VALUE
)
194 pGlobalData
->volumeValue
= mxcdVolume
.dwValue
;
199 SetVolumeValue(PGLOBAL_DATA pGlobalData
){
200 MIXERCONTROLDETAILS_UNSIGNED mxcdVolume
;
201 MIXERCONTROLDETAILS mxcd
;
203 if (pGlobalData
->hMixer
== NULL
)
206 mxcdVolume
.dwValue
= pGlobalData
->volumeValue
;
207 mxcd
.cbStruct
= sizeof(MIXERCONTROLDETAILS
);
208 mxcd
.dwControlID
= pGlobalData
->volumeControlID
;
210 mxcd
.cMultipleItems
= 0;
211 mxcd
.cbDetails
= sizeof(MIXERCONTROLDETAILS_UNSIGNED
);
212 mxcd
.paDetails
= &mxcdVolume
;
214 if (mixerSetControlDetails((HMIXEROBJ
)pGlobalData
->hMixer
, &mxcd
, MIXER_OBJECTF_HMIXER
| MIXER_SETCONTROLDETAILSF_VALUE
)
218 pGlobalData
->volumeValue
= mxcdVolume
.dwValue
;
224 SetSystrayVolumeIconState(BOOL bEnabled
)
228 hwndTaskBar
= FindWindowW(L
"SystemTray_Main", NULL
);
229 if (hwndTaskBar
== NULL
)
232 SendMessageW(hwndTaskBar
, WM_USER
+ 220, 4, bEnabled
);
237 GetSystrayVolumeIconState(VOID
)
241 hwndTaskBar
= FindWindowW(L
"SystemTray_Main", NULL
);
242 if (hwndTaskBar
== NULL
)
247 return (BOOL
)SendMessageW(hwndTaskBar
, WM_USER
+ 221, 4, 0);
251 InitVolumeControls(HWND hwndDlg
, PGLOBAL_DATA pGlobalData
)
255 TCHAR szNoDevices
[256];
257 CheckDlgButton(hwndDlg
,
259 GetSystrayVolumeIconState() ? BST_CHECKED
: BST_UNCHECKED
);
261 LoadString(hApplet
, IDS_NO_DEVICES
, szNoDevices
, _countof(szNoDevices
));
263 NumMixers
= mixerGetNumDevs();
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
);
276 if (mixerOpen(&pGlobalData
->hMixer
, 0, PtrToUlong(hwndDlg
), 0, MIXER_OBJECTF_MIXER
| CALLBACK_WINDOW
) != MMSYSERR_NOERROR
)
278 MessageBox(hwndDlg
, _T("Cannot open mixer"), NULL
, MB_OK
);
282 ZeroMemory(&mxc
, sizeof(MIXERCAPS
));
283 if (mixerGetDevCaps(PtrToUint(pGlobalData
->hMixer
), &mxc
, sizeof(MIXERCAPS
)) != MMSYSERR_NOERROR
)
285 MessageBox(hwndDlg
, _T("mixerGetDevCaps failed"), NULL
, MB_OK
);
289 GetMuteControl(pGlobalData
);
290 GetMuteState(pGlobalData
);
291 if (pGlobalData
->muteVal
)
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
);
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
);
302 GetVolumeControl(pGlobalData
);
303 GetVolumeValue(pGlobalData
);
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
);
315 SaveData(HWND hwndDlg
)
319 bShowIcon
= (IsDlgButtonChecked(hwndDlg
, IDC_ICON_IN_TASKBAR
) == BST_CHECKED
);
321 SetSystrayVolumeIconState(!bShowIcon
);
325 LaunchSoundControl(HWND hwndDlg
)
327 if ((INT_PTR
)ShellExecuteW(NULL
, L
"open", L
"sndvol32.exe", NULL
, NULL
, SW_SHOWNORMAL
) > 32)
329 MessageBox(hwndDlg
, _T("Cannot run sndvol32.exe"), NULL
, MB_OK
);
332 /* Volume property page dialog callback */
333 //static INT_PTR CALLBACK
335 VolumeDlgProc(HWND hwndDlg
,
340 static IMGINFO ImgInfo
;
341 PGLOBAL_DATA pGlobalData
;
342 UNREFERENCED_PARAMETER(lParam
);
343 UNREFERENCED_PARAMETER(wParam
);
345 pGlobalData
= (PGLOBAL_DATA
)GetWindowLongPtr(hwndDlg
, DWLP_USER
);
351 case MM_MIXM_LINE_CHANGE
:
353 GetMuteState(pGlobalData
);
354 if (pGlobalData
->muteVal
)
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
);
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
);
366 case MM_MIXM_CONTROL_CHANGE
:
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
);
375 pGlobalData
= (GLOBAL_DATA
*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(GLOBAL_DATA
));
376 SetWindowLongPtr(hwndDlg
, DWLP_USER
, (LONG_PTR
)pGlobalData
);
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
);
382 InitImageInfo(&ImgInfo
);
383 InitVolumeControls(hwndDlg
, pGlobalData
);
389 LPDRAWITEMSTRUCT lpDrawItem
;
390 lpDrawItem
= (LPDRAWITEMSTRUCT
) lParam
;
391 if(lpDrawItem
->CtlID
== IDC_SPEAKIMG
)
396 /* Position image in centre of dialog */
397 left
= (lpDrawItem
->rcItem
.right
- ImgInfo
.cxSource
) / 2;
399 hdcMem
= CreateCompatibleDC(lpDrawItem
->hDC
);
402 SelectObject(hdcMem
, ImgInfo
.hBitmap
);
403 BitBlt(lpDrawItem
->hDC
,
405 lpDrawItem
->rcItem
.top
,
406 lpDrawItem
->rcItem
.right
- lpDrawItem
->rcItem
.left
,
407 lpDrawItem
->rcItem
.bottom
- lpDrawItem
->rcItem
.top
,
420 switch (LOWORD(wParam
))
422 case IDC_MUTE_CHECKBOX
:
423 if (HIWORD(wParam
) == BN_CLICKED
)
425 SwitchMuteState(pGlobalData
);
426 if (pGlobalData
->muteVal
)
428 SendDlgItemMessage(hwndDlg
, IDC_MUTE_ICON
, STM_SETIMAGE
, IMAGE_ICON
, (LPARAM
)pGlobalData
->hIconMuted
);
432 SendDlgItemMessage(hwndDlg
, IDC_MUTE_ICON
, STM_SETIMAGE
, IMAGE_ICON
, (LPARAM
)pGlobalData
->hIconUnMuted
);
435 PropSheet_Changed(GetParent(hwndDlg
), hwndDlg
);
439 case IDC_ICON_IN_TASKBAR
:
440 if (HIWORD(wParam
) == BN_CLICKED
)
442 PropSheet_Changed(GetParent(hwndDlg
), hwndDlg
);
446 case IDC_ADVANCED_BTN
:
447 LaunchSoundControl(hwndDlg
);
455 HWND hVolumeTrackbar
= GetDlgItem(hwndDlg
, IDC_VOLUME_TRACKBAR
);
456 if (hVolumeTrackbar
== (HWND
)lParam
)
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
));
467 mixerClose(pGlobalData
->hMixer
);
468 DestroyIcon(pGlobalData
->hIconMuted
);
469 DestroyIcon(pGlobalData
->hIconUnMuted
);
470 DestroyIcon(pGlobalData
->hIconNoHW
);
471 HeapFree(GetProcessHeap(), 0, pGlobalData
);
475 if (((LPNMHDR
)lParam
)->code
== (UINT
)PSN_APPLY
)