[SNDVOL32] Implement the volume control for the tray window.
[reactos.git] / base / applications / sndvol32 / tray.c
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 } DIALOG_DATA, *PDIALOG_DATA;
24
25
26 static VOID
27 OnTrayInitDialog(
28 HWND hwnd,
29 WPARAM wParam,
30 LPARAM lParam)
31 {
32 POINT ptCursor;
33 RECT rcWindow;
34 RECT rcScreen;
35 LONG x, y, cx, cy;
36
37 GetCursorPos(&ptCursor);
38
39 GetWindowRect(hwnd, &rcWindow);
40
41 GetWindowRect(GetDesktopWindow(), &rcScreen);
42
43 cx = rcWindow.right - rcWindow.left;
44 cy = rcWindow.bottom - rcWindow.top;
45
46 if (ptCursor.y + cy > rcScreen.bottom)
47 y = ptCursor.y - cy;
48 else
49 y = ptCursor.y;
50
51 if (ptCursor.x + cx > rcScreen.right)
52 x = ptCursor.x - cx;
53 else
54 x = ptCursor.x;
55
56 SetWindowPos(hwnd, HWND_TOPMOST, x, y, 0, 0, SWP_NOSIZE);
57
58 /* Disable the controls for now */
59 EnableWindow(GetDlgItem(hwnd, IDC_LINE_SWITCH), FALSE);
60 }
61
62
63 static
64 VOID
65 OnTrayInitMixer(
66 PDIALOG_DATA pDialogData,
67 HWND hwndDlg)
68 {
69 MIXERLINE mxln;
70 MIXERCONTROL mxc;
71 MIXERLINECONTROLS mxlctrl;
72 MIXERCONTROLDETAILS mxcd;
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 = pDialogData->volumeInitValues[0].dwValue;
129 pDialogData->maxChannel = 0;
130 for (i = 1; i < pDialogData->volumeChannels; i++)
131 {
132 if (pDialogData->volumeInitValues[i].dwValue > pDialogData->maxVolume)
133 {
134 pDialogData->maxVolume = pDialogData->volumeInitValues[i].dwValue;
135 pDialogData->maxChannel = i;
136 }
137 }
138
139 /* Initialize the volume trackbar */
140 SendDlgItemMessage(hwndDlg, IDC_LINE_SLIDER_VERT, TBM_SETRANGE, TRUE, MAKELONG(VOLUME_MIN, VOLUME_MAX));
141 SendDlgItemMessage(hwndDlg, IDC_LINE_SLIDER_VERT, TBM_SETPAGESIZE, 0, VOLUME_PAGE_SIZE);
142 SendDlgItemMessage(hwndDlg, IDC_LINE_SLIDER_VERT, TBM_SETPOS, TRUE, VOLUME_MAX -(pDialogData->maxVolume - pDialogData->volumeMinimum) / pDialogData->volumeStep);
143 }
144
145
146 static
147 VOID
148 OnVScroll(
149 PDIALOG_DATA pDialogData,
150 HWND hwndDlg,
151 WPARAM wParam,
152 LPARAM lParam)
153 {
154 MIXERCONTROLDETAILS mxcd;
155 DWORD dwPos, dwVolume, i;
156
157 switch (LOWORD(wParam))
158 {
159 case TB_THUMBTRACK:
160
161 dwPos = VOLUME_MAX - (DWORD)SendDlgItemMessage(hwndDlg, IDC_LINE_SLIDER_VERT, TBM_GETPOS, 0, 0);
162 dwVolume = (dwPos * pDialogData->volumeStep) + pDialogData->volumeMinimum;
163
164 for (i = 0; i < pDialogData->volumeChannels; i++)
165 {
166 if (i == pDialogData->maxChannel)
167 {
168 pDialogData->volumeCurrentValues[i].dwValue = dwVolume;
169 }
170 else
171 {
172 pDialogData->volumeCurrentValues[i].dwValue =
173 pDialogData->volumeInitValues[i].dwValue * dwVolume / pDialogData-> maxVolume;
174 }
175 }
176
177 mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
178 mxcd.dwControlID = pDialogData->volumeControlID;
179 mxcd.cChannels = pDialogData->volumeChannels;
180 mxcd.cMultipleItems = 0;
181 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
182 mxcd.paDetails = pDialogData->volumeCurrentValues;
183
184 mixerSetControlDetails((HMIXEROBJ)pDialogData->hMixer,
185 &mxcd,
186 MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE);
187 break;
188
189 case TB_ENDTRACK:
190 PlaySound((LPCTSTR)SND_ALIAS_SYSTEMDEFAULT, NULL, SND_ASYNC | SND_ALIAS_ID);
191 break;
192
193 default:
194 break;
195 }
196 }
197
198
199
200 INT_PTR
201 CALLBACK
202 TrayDlgProc(
203 HWND hwndDlg,
204 UINT uMsg,
205 WPARAM wParam,
206 LPARAM lParam)
207 {
208 PDIALOG_DATA pDialogData;
209
210 pDialogData = (PDIALOG_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
211
212 switch (uMsg)
213 {
214 case WM_INITDIALOG:
215 OnTrayInitDialog(hwndDlg, wParam, lParam);
216
217 pDialogData = (PDIALOG_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DIALOG_DATA));
218 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pDialogData);
219
220 if (pDialogData)
221 OnTrayInitMixer(pDialogData, hwndDlg);
222 break;
223
224 case WM_VSCROLL:
225 if (pDialogData)
226 OnVScroll(pDialogData, hwndDlg, wParam, lParam);
227 break;
228
229 case WM_DESTROY:
230 if (pDialogData)
231 {
232 if (pDialogData->volumeInitValues)
233 HeapFree(GetProcessHeap(), 0, pDialogData->volumeInitValues);
234
235 if (pDialogData->volumeCurrentValues)
236 HeapFree(GetProcessHeap(), 0, pDialogData->volumeCurrentValues);
237
238 if (pDialogData->hMixer)
239 mixerClose(pDialogData->hMixer);
240
241 HeapFree(GetProcessHeap(), 0, pDialogData);
242 pDialogData = NULL;
243 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)NULL);
244 }
245 break;
246
247 case WM_ACTIVATE:
248 if (LOWORD(wParam) == WA_INACTIVE)
249 EndDialog(hwndDlg, IDOK);
250 break;
251 }
252
253 return 0;
254 }
255
256 /* EOF */