xref: /reactos/base/applications/sndvol32/tray.c (revision 0185ee46)
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