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
OnTrayInitDialog(HWND hwnd,WPARAM wParam,LPARAM lParam)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
OnTrayInitMixer(PDIALOG_DATA pDialogData,HWND hwndDlg)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
OnCommand(PDIALOG_DATA pDialogData,HWND hwndDlg,WPARAM wParam,LPARAM lParam)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
OnVScroll(PDIALOG_DATA pDialogData,HWND hwndDlg,WPARAM wParam,LPARAM lParam)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
TrayDlgProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)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