1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS Sound Volume Control 4 * FILE: base/applications/sndvol32/tray.c 5 * PROGRAMMERS: Eric Kohl <eric.kohl@reactos.org> 6 */ 7 8 #include "sndvol32.h" 9 10 typedef struct _DIALOG_DATA 11 { 12 HMIXER hMixer; 13 DWORD volumeControlID; 14 DWORD volumeChannels; 15 DWORD volumeMinimum; 16 DWORD volumeMaximum; 17 DWORD volumeStep; 18 19 DWORD maxVolume; 20 DWORD maxChannel; 21 PMIXERCONTROLDETAILS_UNSIGNED volumeInitValues; 22 PMIXERCONTROLDETAILS_UNSIGNED volumeCurrentValues; 23 24 DWORD muteControlID; 25 } DIALOG_DATA, *PDIALOG_DATA; 26 27 28 static VOID 29 OnTrayInitDialog( 30 HWND hwnd, 31 WPARAM wParam, 32 LPARAM lParam) 33 { 34 POINT ptCursor; 35 RECT rcWindow; 36 RECT rcScreen; 37 LONG x, y, cx, cy; 38 39 GetCursorPos(&ptCursor); 40 41 GetWindowRect(hwnd, &rcWindow); 42 43 GetWindowRect(GetDesktopWindow(), &rcScreen); 44 45 cx = rcWindow.right - rcWindow.left; 46 cy = rcWindow.bottom - rcWindow.top; 47 48 if (ptCursor.y + cy > rcScreen.bottom) 49 y = ptCursor.y - cy; 50 else 51 y = ptCursor.y; 52 53 if (ptCursor.x + cx > rcScreen.right) 54 x = ptCursor.x - cx; 55 else 56 x = ptCursor.x; 57 58 SetWindowPos(hwnd, HWND_TOPMOST, x, y, 0, 0, SWP_NOSIZE); 59 } 60 61 62 static 63 VOID 64 OnTrayInitMixer( 65 PDIALOG_DATA pDialogData, 66 HWND hwndDlg) 67 { 68 MIXERLINE mxln; 69 MIXERCONTROL mxc; 70 MIXERLINECONTROLS mxlctrl; 71 MIXERCONTROLDETAILS mxcd; 72 MIXERCONTROLDETAILS_BOOLEAN mxcdBool; 73 DWORD i; 74 75 /* Open the mixer */ 76 if (mixerOpen(&pDialogData->hMixer, 0, PtrToUlong(hwndDlg), 0, MIXER_OBJECTF_MIXER | CALLBACK_WINDOW) != MMSYSERR_NOERROR) 77 return; 78 79 /* Retrieve the mixer information */ 80 mxln.cbStruct = sizeof(MIXERLINE); 81 mxln.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS; 82 83 if (mixerGetLineInfo((HMIXEROBJ)pDialogData->hMixer, &mxln, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_COMPONENTTYPE) != MMSYSERR_NOERROR) 84 return; 85 86 pDialogData->volumeChannels = mxln.cChannels; 87 88 /* Retrieve the line information */ 89 mxlctrl.cbStruct = sizeof(MIXERLINECONTROLS); 90 mxlctrl.dwLineID = mxln.dwLineID; 91 mxlctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; 92 mxlctrl.cControls = 1; 93 mxlctrl.cbmxctrl = sizeof(MIXERCONTROL); 94 mxlctrl.pamxctrl = &mxc; 95 96 if (mixerGetLineControls((HMIXEROBJ)pDialogData->hMixer, &mxlctrl, MIXER_OBJECTF_HMIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE) != MMSYSERR_NOERROR) 97 return; 98 99 pDialogData->volumeControlID = mxc.dwControlID; 100 pDialogData->volumeMinimum = mxc.Bounds.dwMinimum; 101 pDialogData->volumeMaximum = mxc.Bounds.dwMaximum; 102 pDialogData->volumeStep = (pDialogData->volumeMaximum - pDialogData->volumeMinimum) / (VOLUME_MAX - VOLUME_MIN); 103 104 /* Allocate a buffer for all channel volume values */ 105 pDialogData->volumeInitValues = HeapAlloc(GetProcessHeap(), 106 0, 107 mxln.cChannels * sizeof(MIXERCONTROLDETAILS_UNSIGNED)); 108 if (pDialogData->volumeInitValues == NULL) 109 return; 110 111 pDialogData->volumeCurrentValues = HeapAlloc(GetProcessHeap(), 112 0, 113 mxln.cChannels * sizeof(MIXERCONTROLDETAILS_UNSIGNED)); 114 if (pDialogData->volumeCurrentValues == NULL) 115 return; 116 117 /* Retrieve the channel volume values */ 118 mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS); 119 mxcd.dwControlID = mxc.dwControlID; 120 mxcd.cChannels = mxln.cChannels; 121 mxcd.cMultipleItems = 0; 122 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); 123 mxcd.paDetails = pDialogData->volumeInitValues; 124 125 if (mixerGetControlDetails((HMIXEROBJ)pDialogData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_VALUE) != MMSYSERR_NOERROR) 126 return; 127 128 pDialogData->maxVolume = 0; 129 for (i = 0; i < pDialogData->volumeChannels; i++) 130 { 131 pDialogData->volumeCurrentValues[i].dwValue = pDialogData->volumeInitValues[i].dwValue; 132 133 if (pDialogData->volumeInitValues[i].dwValue > pDialogData->maxVolume) 134 pDialogData->maxVolume = pDialogData->volumeInitValues[i].dwValue; 135 } 136 137 /* Initialize the volume trackbar */ 138 SendDlgItemMessage(hwndDlg, IDC_LINE_SLIDER_VERT, TBM_SETRANGE, TRUE, MAKELONG(VOLUME_MIN, VOLUME_MAX)); 139 SendDlgItemMessage(hwndDlg, IDC_LINE_SLIDER_VERT, TBM_SETPAGESIZE, 0, VOLUME_PAGE_SIZE); 140 SendDlgItemMessage(hwndDlg, IDC_LINE_SLIDER_VERT, TBM_SETPOS, TRUE, VOLUME_MAX -(pDialogData->maxVolume - pDialogData->volumeMinimum) / pDialogData->volumeStep); 141 142 /* Retrieve the mute control information */ 143 mxlctrl.cbStruct = sizeof(MIXERLINECONTROLS); 144 mxlctrl.dwLineID = mxln.dwLineID; 145 mxlctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE; 146 mxlctrl.cControls = 1; 147 mxlctrl.cbmxctrl = sizeof(MIXERCONTROL); 148 mxlctrl.pamxctrl = &mxc; 149 150 if (mixerGetLineControls((HMIXEROBJ)pDialogData->hMixer, &mxlctrl, MIXER_OBJECTF_HMIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE) != MMSYSERR_NOERROR) 151 return; 152 153 pDialogData->muteControlID = mxc.dwControlID; 154 155 /* Retrieve the mute value */ 156 mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS); 157 mxcd.dwControlID = mxc.dwControlID; 158 mxcd.cChannels = 1; 159 mxcd.cMultipleItems = 0; 160 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN); 161 mxcd.paDetails = &mxcdBool; 162 163 if (mixerGetControlDetails((HMIXEROBJ)pDialogData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_VALUE) != MMSYSERR_NOERROR) 164 return; 165 166 /* Initialize the mute checkbox */ 167 SendDlgItemMessage(hwndDlg, IDC_LINE_SWITCH, BM_SETCHECK, (WPARAM)(mxcdBool.fValue ? BST_CHECKED : BST_UNCHECKED), 0); 168 } 169 170 171 static 172 VOID 173 OnCommand( 174 PDIALOG_DATA pDialogData, 175 HWND hwndDlg, 176 WPARAM wParam, 177 LPARAM lParam) 178 { 179 MIXERCONTROLDETAILS mxcd; 180 MIXERCONTROLDETAILS_BOOLEAN mxcdMute; 181 182 if ((LOWORD(wParam) == IDC_LINE_SWITCH) && 183 (HIWORD(wParam) == BN_CLICKED)) 184 { 185 mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS); 186 mxcd.dwControlID = pDialogData->muteControlID; 187 mxcd.cChannels = 1; 188 mxcd.cMultipleItems = 0; 189 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN); 190 mxcd.paDetails = &mxcdMute; 191 192 mxcdMute.fValue = (SendMessage((HWND)lParam, BM_GETCHECK, 0, 0) == BST_CHECKED); 193 194 mixerSetControlDetails((HMIXEROBJ)pDialogData->hMixer, 195 &mxcd, 196 MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE); 197 } 198 } 199 200 201 static 202 VOID 203 OnVScroll( 204 PDIALOG_DATA pDialogData, 205 HWND hwndDlg, 206 WPARAM wParam, 207 LPARAM lParam) 208 { 209 MIXERCONTROLDETAILS mxcd; 210 DWORD dwPosition, dwVolume, i; 211 212 switch (LOWORD(wParam)) 213 { 214 case TB_THUMBPOSITION: 215 break; 216 217 case TB_ENDTRACK: 218 PlaySound((LPCTSTR)SND_ALIAS_SYSTEMDEFAULT, NULL, SND_ASYNC | SND_ALIAS_ID); 219 break; 220 221 default: 222 dwPosition = VOLUME_MAX - (DWORD)SendDlgItemMessage(hwndDlg, IDC_LINE_SLIDER_VERT, TBM_GETPOS, 0, 0); 223 224 if (dwPosition == VOLUME_MIN) 225 dwVolume = pDialogData->volumeMinimum; 226 else if (dwPosition == VOLUME_MAX) 227 dwVolume = pDialogData->volumeMaximum; 228 else 229 dwVolume = (dwPosition * pDialogData->volumeStep) + pDialogData->volumeMinimum; 230 231 for (i = 0; i < pDialogData->volumeChannels; i++) 232 { 233 if (pDialogData->volumeInitValues[i].dwValue == pDialogData->maxVolume) 234 { 235 pDialogData->volumeCurrentValues[i].dwValue = dwVolume; 236 } 237 else 238 { 239 pDialogData->volumeCurrentValues[i].dwValue = 240 pDialogData->volumeInitValues[i].dwValue * dwVolume / pDialogData-> maxVolume; 241 } 242 } 243 244 mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS); 245 mxcd.dwControlID = pDialogData->volumeControlID; 246 mxcd.cChannels = pDialogData->volumeChannels; 247 mxcd.cMultipleItems = 0; 248 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); 249 mxcd.paDetails = pDialogData->volumeCurrentValues; 250 251 mixerSetControlDetails((HMIXEROBJ)pDialogData->hMixer, 252 &mxcd, 253 MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE); 254 break; 255 } 256 } 257 258 259 260 INT_PTR 261 CALLBACK 262 TrayDlgProc( 263 HWND hwndDlg, 264 UINT uMsg, 265 WPARAM wParam, 266 LPARAM lParam) 267 { 268 PDIALOG_DATA pDialogData; 269 270 pDialogData = (PDIALOG_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER); 271 272 switch (uMsg) 273 { 274 case WM_INITDIALOG: 275 OnTrayInitDialog(hwndDlg, wParam, lParam); 276 277 pDialogData = (PDIALOG_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DIALOG_DATA)); 278 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pDialogData); 279 280 if (pDialogData) 281 OnTrayInitMixer(pDialogData, hwndDlg); 282 break; 283 284 case WM_COMMAND: 285 if (pDialogData) 286 OnCommand(pDialogData, hwndDlg, wParam, lParam); 287 break; 288 289 case WM_VSCROLL: 290 if (pDialogData) 291 OnVScroll(pDialogData, hwndDlg, wParam, lParam); 292 break; 293 294 case WM_DESTROY: 295 if (pDialogData) 296 { 297 if (pDialogData->volumeInitValues) 298 HeapFree(GetProcessHeap(), 0, pDialogData->volumeInitValues); 299 300 if (pDialogData->volumeCurrentValues) 301 HeapFree(GetProcessHeap(), 0, pDialogData->volumeCurrentValues); 302 303 if (pDialogData->hMixer) 304 mixerClose(pDialogData->hMixer); 305 306 HeapFree(GetProcessHeap(), 0, pDialogData); 307 pDialogData = NULL; 308 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)NULL); 309 } 310 break; 311 312 case WM_ACTIVATE: 313 if (LOWORD(wParam) == WA_INACTIVE) 314 EndDialog(hwndDlg, IDOK); 315 break; 316 } 317 318 return 0; 319 } 320 321 /* EOF */ 322