1 /*
2 * ReactOS Sound Volume Control
3 * Copyright (C) 2004-2005 Thomas Weidenmueller
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19 /*
20 * COPYRIGHT: See COPYING in the top level directory
21 * PROJECT: ReactOS Sound Volume Control
22 * FILE: base/applications/sndvol32/sndvol32.c
23 * PROGRAMMERS: Thomas Weidenmueller <w3seek@reactos.com>
24 */
25
26 #include "sndvol32.h"
27
28 #include <shellapi.h>
29
30 HINSTANCE hAppInstance;
31 ATOM MainWindowClass;
32 HWND hMainWnd;
33 HANDLE hAppHeap;
34 LPTSTR lpAppTitle;
35 PREFERENCES_CONTEXT Preferences;
36
37 #define GetDialogData(hwndDlg, type) \
38 ( P##type )GetWindowLongPtr((hwndDlg), DWLP_USER)
39 #define GetWindowData(hwnd, type) \
40 ( P##type )GetWindowLongPtr((hwnd), GWL_USERDATA)
41
42 /******************************************************************************/
43
44
45
46 typedef struct _PREFERENCES_FILL_DEVICES
47 {
48 PPREFERENCES_CONTEXT PrefContext;
49 HWND hComboBox;
50 UINT Selected;
51 } PREFERENCES_FILL_DEVICES, *PPREFERENCES_FILL_DEVICES;
52
53 static BOOL CALLBACK
FillDeviceComboBox(PSND_MIXER Mixer,UINT Id,LPCTSTR ProductName,PVOID Context)54 FillDeviceComboBox(PSND_MIXER Mixer,
55 UINT Id,
56 LPCTSTR ProductName,
57 PVOID Context)
58 {
59 LRESULT lres;
60 PPREFERENCES_FILL_DEVICES FillContext = (PPREFERENCES_FILL_DEVICES)Context;
61
62 UNREFERENCED_PARAMETER(Mixer);
63
64 lres = SendMessage(FillContext->hComboBox,
65 CB_ADDSTRING,
66 0,
67 (LPARAM)ProductName);
68 if (lres != CB_ERR)
69 {
70 /* save the index so we don't screw stuff when the combobox is sorted... */
71 SendMessage(FillContext->hComboBox,
72 CB_SETITEMDATA,
73 (WPARAM)lres,
74 Id);
75
76 if (Id == FillContext->Selected)
77 {
78 SendMessage(FillContext->hComboBox,
79 CB_SETCURSEL,
80 (WPARAM)lres,
81 0);
82 }
83 }
84
85 return TRUE;
86 }
87
88 static BOOL CALLBACK
PrefDlgAddLine(PSND_MIXER Mixer,LPMIXERLINE Line,UINT DisplayControls,PVOID Context)89 PrefDlgAddLine(PSND_MIXER Mixer,
90 LPMIXERLINE Line,
91 UINT DisplayControls,
92 PVOID Context)
93 {
94 PPREFERENCES_CONTEXT PrefContext = (PPREFERENCES_CONTEXT)Context;
95
96 UNREFERENCED_PARAMETER(Mixer);
97 UNREFERENCED_PARAMETER(DisplayControls);
98
99 switch (Line->dwComponentType)
100 {
101 case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS:
102 if (PrefContext->PlaybackID == (DWORD)-1)
103 {
104 PrefContext->PlaybackID = Line->dwLineID;
105
106 if (PrefContext->SelectedLine == (DWORD)-1)
107 {
108 PrefContext->SelectedLine = Line->dwLineID;
109 }
110 }
111 else
112 goto AddToOthersLines;
113
114 break;
115
116 case MIXERLINE_COMPONENTTYPE_DST_WAVEIN:
117 if (PrefContext->RecordingID == (DWORD)-1)
118 {
119 PrefContext->RecordingID = Line->dwLineID;
120
121 if (PrefContext->SelectedLine == (DWORD)-1)
122 {
123 PrefContext->SelectedLine = Line->dwLineID;
124 }
125 }
126 else
127 goto AddToOthersLines;
128
129 break;
130
131 default:
132 {
133 LRESULT lres;
134 HWND hwndCbOthers;
135
136 if (PrefContext->SelectedLine == (DWORD)-1)
137 {
138 PrefContext->SelectedLine = Line->dwLineID;
139 }
140
141 AddToOthersLines:
142 hwndCbOthers = GetDlgItem(PrefContext->hwndDlg,
143 IDC_LINE);
144
145 lres = SendMessage(hwndCbOthers,
146 CB_ADDSTRING,
147 0,
148 (LPARAM)Line->szName);
149 if (lres != CB_ERR)
150 {
151 SendMessage(hwndCbOthers,
152 CB_SETITEMDATA,
153 (WPARAM)lres,
154 Line->dwLineID);
155
156 PrefContext->OtherLines++;
157 }
158 break;
159 }
160 }
161
162 return TRUE;
163 }
164
165 static BOOL CALLBACK
PrefDlgAddConnection(PSND_MIXER Mixer,DWORD LineID,LPMIXERLINE Line,PVOID Context)166 PrefDlgAddConnection(PSND_MIXER Mixer,
167 DWORD LineID,
168 LPMIXERLINE Line,
169 PVOID Context)
170 {
171 PPREFERENCES_CONTEXT PrefContext = (PPREFERENCES_CONTEXT)Context;
172 HWND hwndControls;
173 LVITEM lvi;
174 UINT i;
175
176 UNREFERENCED_PARAMETER(Mixer);
177 UNREFERENCED_PARAMETER(LineID);
178
179 if (Line->cControls != 0)
180 {
181 hwndControls = GetDlgItem(PrefContext->hwndDlg,
182 IDC_CONTROLS);
183
184 lvi.mask = LVIF_TEXT | LVIF_PARAM;
185 lvi.iItem = PrefContext->tmp++;
186 lvi.iSubItem = 0;
187 lvi.pszText = Line->szName;
188 lvi.lParam = (LPARAM)Line->dwSource;
189
190 i = SendMessage(hwndControls,
191 LVM_INSERTITEM,
192 0,
193 (LPARAM)&lvi);
194 if (i != (UINT)-1)
195 {
196 TCHAR LineName[MIXER_LONG_NAME_CHARS];
197 DWORD Flags;
198 BOOL SelLine = FALSE;
199
200 if (SndMixerGetLineName(PrefContext->Mixer,
201 PrefContext->SelectedLine,
202 LineName,
203 MIXER_LONG_NAME_CHARS,
204 TRUE) == -1)
205 {
206 LineName[0] = TEXT('\0');
207 }
208
209 if (ReadLineConfig(PrefContext->DeviceName,
210 LineName,
211 Line->szName,
212 &Flags))
213 {
214 if (Flags != 0x4)
215 {
216 SelLine = TRUE;
217 }
218 }
219
220 ListView_SetCheckState(hwndControls,
221 i,
222 SelLine);
223 }
224 }
225
226 return TRUE;
227 }
228
229 static VOID
UpdatePrefDlgControls(PPREFERENCES_CONTEXT Context,DWORD LineID)230 UpdatePrefDlgControls(PPREFERENCES_CONTEXT Context,
231 DWORD LineID)
232 {
233 INT OldID, MixerID = 0;
234 INT DeviceCbIndex;
235
236 /* select the mixer */
237 DeviceCbIndex = SendDlgItemMessage(Context->hwndDlg,
238 IDC_MIXERDEVICE,
239 CB_GETCURSEL,
240 0,
241 0);
242 if (DeviceCbIndex != CB_ERR)
243 {
244 MixerID = SendDlgItemMessage(Context->hwndDlg,
245 IDC_MIXERDEVICE,
246 CB_GETITEMDATA,
247 DeviceCbIndex,
248 0);
249 if (MixerID == CB_ERR)
250 {
251 MixerID = 0;
252 }
253 }
254
255 OldID = Context->Selected;
256 if (MixerID != OldID &&
257 SndMixerSelect(Context->Mixer,
258 MixerID))
259 {
260 Context->Selected = SndMixerGetSelection(Context->Mixer);
261
262 /* update the controls */
263 Context->PlaybackID = (DWORD)-1;
264 Context->RecordingID = (DWORD)-1;
265 Context->OtherLines = 0;
266 Context->SelectedLine = (DWORD)-1;
267
268 SndMixerGetProductName(Context->Mixer,
269 Context->DeviceName,
270 sizeof(Context->DeviceName) / sizeof(Context->DeviceName[0]));
271
272 if (SndMixerEnumLines(Context->Mixer,
273 PrefDlgAddLine,
274 Context))
275 {
276 UINT SelBox = 0;
277
278 /* enable/disable controls and make default selection */
279 EnableWindow(GetDlgItem(Context->hwndDlg,
280 IDC_PLAYBACK),
281 Context->PlaybackID != (DWORD)-1);
282 CheckDlgButton(Context->hwndDlg,
283 IDC_PLAYBACK,
284 (Context->PlaybackID != (DWORD)-1 && SelBox++ == 0) ?
285 BST_CHECKED : BST_UNCHECKED);
286
287 EnableWindow(GetDlgItem(Context->hwndDlg,
288 IDC_RECORDING),
289 Context->RecordingID != (DWORD)-1);
290 CheckDlgButton(Context->hwndDlg,
291 IDC_RECORDING,
292 (Context->RecordingID != (DWORD)-1 && SelBox++ == 0) ?
293 BST_CHECKED : BST_UNCHECKED);
294
295 if (Context->OtherLines != 0)
296 {
297 /* select the first item in the other lines combo box by default */
298 SendDlgItemMessage(Context->hwndDlg,
299 IDC_LINE,
300 CB_SETCURSEL,
301 0,
302 0);
303 }
304 EnableWindow(GetDlgItem(Context->hwndDlg,
305 IDC_LINE),
306 FALSE);
307 EnableWindow(GetDlgItem(Context->hwndDlg,
308 IDC_OTHER),
309 Context->OtherLines != 0);
310 CheckDlgButton(Context->hwndDlg,
311 IDC_LINE,
312 (Context->OtherLines != 0 && SelBox++ == 0) ?
313 BST_CHECKED : BST_UNCHECKED);
314
315 /* disable the OK button if the device doesn't have any lines */
316 EnableWindow(GetDlgItem(Context->hwndDlg,
317 IDOK),
318 Context->PlaybackID != (DWORD)-1 ||
319 Context->RecordingID != (DWORD)-1 ||
320 Context->OtherLines != 0);
321
322 LineID = Context->SelectedLine;
323 }
324 }
325
326 /* update the line sources list */
327 if ((MixerID != OldID && Context->SelectedLine != (DWORD)-1) ||
328 (Context->SelectedLine != LineID && LineID != (DWORD)-1))
329 {
330 Context->SelectedLine = LineID;
331
332 (void)ListView_DeleteAllItems(GetDlgItem(Context->hwndDlg,
333 IDC_CONTROLS));
334
335 Context->tmp = 0;
336 SndMixerEnumConnections(Context->Mixer,
337 LineID,
338 PrefDlgAddConnection,
339 Context);
340 }
341 }
342
343 static
344 VOID
WriteLineSettings(PPREFERENCES_CONTEXT Context,HWND hwndDlg)345 WriteLineSettings(PPREFERENCES_CONTEXT Context, HWND hwndDlg)
346 {
347 HWND hwndControls;
348 INT Count, Index;
349 WCHAR LineName[MIXER_LONG_NAME_CHARS];
350 WCHAR DestinationName[MIXER_LONG_NAME_CHARS];
351 DWORD Flags;
352 PSNDVOL_REG_LINESTATE LineStates;
353
354 /* get list view */
355 hwndControls = GetDlgItem(hwndDlg, IDC_CONTROLS);
356
357 /* get list item count */
358 Count = ListView_GetItemCount(hwndControls);
359
360 /* sanity check */
361 assert(Count);
362
363 if (SndMixerGetLineName(Context->Mixer, Context->SelectedLine, DestinationName, MIXER_LONG_NAME_CHARS, TRUE) == -1)
364 {
365 /* failed to get destination line name */
366 return;
367 }
368
369 /* allocate line states array */
370 LineStates = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SNDVOL_REG_LINESTATE) * Count);
371 if (LineStates == NULL)
372 {
373 /* failed to allocate line states array */
374 return;
375 }
376
377
378 for(Index = 0; Index < Count; Index++)
379 {
380 /* set to empty */
381 LineName[0] = L'\0';
382
383 /* get item text */
384 ListView_GetItemText(hwndControls, Index, 0, LineName, MIXER_LONG_NAME_CHARS);
385
386 /* make sure it is null terminated */
387 LineName[MIXER_LONG_NAME_CHARS-1] = L'\0';
388
389 /* get check state */
390 Flags = (ListView_GetCheckState(hwndControls, Index) == 0 ? 0x4 : 0);
391
392 /* copy line name */
393 wcscpy(LineStates[Index].LineName, LineName);
394
395 /* store flags */
396 LineStates[Index].Flags = Flags;
397 }
398
399 /* now write the line config */
400 WriteLineConfig(Context->DeviceName, DestinationName, LineStates, sizeof(SNDVOL_REG_LINESTATE) * Count);
401
402 /* free line states */
403 HeapFree(GetProcessHeap(), 0, LineStates);
404 }
405
406 static INT_PTR CALLBACK
DlgPreferencesProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)407 DlgPreferencesProc(HWND hwndDlg,
408 UINT uMsg,
409 WPARAM wParam,
410 LPARAM lParam)
411 {
412 PPREFERENCES_CONTEXT Context;
413
414 switch (uMsg)
415 {
416 case WM_COMMAND:
417 {
418 Context = GetDialogData(hwndDlg,
419 PREFERENCES_CONTEXT);
420 switch (LOWORD(wParam))
421 {
422 case IDC_MIXERDEVICE:
423 {
424 if (HIWORD(wParam) == CBN_SELCHANGE)
425 {
426 UpdatePrefDlgControls(Context,
427 (DWORD)-1);
428 }
429 break;
430 }
431
432 case IDC_LINE:
433 {
434 if (HIWORD(wParam) == CBN_SELCHANGE)
435 {
436 INT LineID;
437 INT Index;
438
439 Index = SendDlgItemMessage(hwndDlg,
440 IDC_LINE,
441 CB_GETCURSEL,
442 0,
443 0);
444 if (Index != CB_ERR)
445 {
446 LineID = SendDlgItemMessage(hwndDlg,
447 IDC_LINE,
448 CB_GETITEMDATA,
449 Index,
450 0);
451 if (LineID != CB_ERR)
452 {
453 UpdatePrefDlgControls(Context,
454 LineID);
455 }
456 }
457 }
458 break;
459 }
460
461 case IDC_PLAYBACK:
462 {
463 UpdatePrefDlgControls(Context,
464 Context->PlaybackID);
465 EnableWindow(GetDlgItem(hwndDlg,
466 IDC_LINE),
467 FALSE);
468 break;
469 }
470
471 case IDC_RECORDING:
472 {
473 UpdatePrefDlgControls(Context,
474 Context->RecordingID);
475 EnableWindow(GetDlgItem(hwndDlg,
476 IDC_LINE),
477 FALSE);
478 break;
479 }
480
481 case IDC_OTHER:
482 {
483 INT LineCbIndex;
484 INT LineID;
485
486 EnableWindow(GetDlgItem(hwndDlg,
487 IDC_LINE),
488 TRUE);
489
490 LineCbIndex = SendDlgItemMessage(hwndDlg,
491 IDC_LINE,
492 CB_GETCURSEL,
493 0,
494 0);
495 if (LineCbIndex != CB_ERR)
496 {
497 LineID = SendDlgItemMessage(hwndDlg,
498 IDC_LINE,
499 CB_GETITEMDATA,
500 LineCbIndex,
501 0);
502 if (LineID != CB_ERR)
503 {
504 UpdatePrefDlgControls(Context,
505 LineID);
506 }
507 }
508 break;
509 }
510
511 case IDOK:
512 {
513 /* write line settings */
514 WriteLineSettings(Context, hwndDlg);
515
516 /* fall through */
517 }
518 case IDCANCEL:
519 {
520 EndDialog(hwndDlg,
521 LOWORD(wParam));
522 break;
523 }
524 }
525 break;
526 }
527
528 case WM_INITDIALOG:
529 {
530 PREFERENCES_FILL_DEVICES FillDevContext;
531 LVCOLUMN lvc;
532 RECT rcClient;
533 HWND hwndControls;
534
535 SetWindowLongPtr(hwndDlg,
536 DWLP_USER,
537 (LONG_PTR)lParam);
538 Context = (PPREFERENCES_CONTEXT)((LONG_PTR)lParam);
539 Context->hwndDlg = hwndDlg;
540 Context->Mixer = SndMixerCreate(hwndDlg, Context->MixerWindow->MixerId);
541 Context->Selected = (UINT)-1;
542
543 FillDevContext.PrefContext = Context;
544 FillDevContext.hComboBox = GetDlgItem(hwndDlg,
545 IDC_MIXERDEVICE);
546 FillDevContext.Selected = SndMixerGetSelection(Context->Mixer);
547 SndMixerEnumProducts(Context->Mixer,
548 FillDeviceComboBox,
549 &FillDevContext);
550
551 /* initialize the list view control */
552 hwndControls = GetDlgItem(hwndDlg,
553 IDC_CONTROLS);
554 (void)ListView_SetExtendedListViewStyle(hwndControls,
555 LVS_EX_CHECKBOXES);
556
557 GetClientRect(hwndControls,
558 &rcClient);
559 lvc.mask = LVCF_TEXT | LVCF_WIDTH;
560 lvc.pszText = TEXT("");
561 lvc.cx = rcClient.right;
562 SendMessage(hwndControls,
563 LVM_INSERTCOLUMN,
564 0,
565 (LPARAM)&lvc);
566
567 /* update all controls */
568 UpdatePrefDlgControls(Context,
569 (DWORD)Context->SelectedLine);
570 return TRUE;
571 }
572
573 case WM_CLOSE:
574 {
575 EndDialog(hwndDlg,
576 IDCANCEL);
577 break;
578 }
579
580 case WM_SYSCOLORCHANGE:
581 {
582 HWND hwndControls;
583
584 /* Forward WM_SYSCOLORCHANGE */
585 hwndControls = GetDlgItem(hwndDlg, IDC_CONTROLS);
586 SendMessage(hwndControls, WM_SYSCOLORCHANGE, 0, 0);
587 break;
588 }
589 }
590
591 return 0;
592 }
593
594 /******************************************************************************/
595
596 static VOID
DeleteMixerWindowControls(PMIXER_WINDOW MixerWindow)597 DeleteMixerWindowControls(PMIXER_WINDOW MixerWindow)
598 {
599 DWORD Index;
600
601 for(Index = 0; Index < MixerWindow->WindowCount; Index++)
602 {
603 /* destroys the window */
604 DestroyWindow(MixerWindow->Window[Index]);
605 }
606
607 /* free memory */
608 HeapFree(GetProcessHeap(), 0, MixerWindow->Window);
609
610 /* set to null */
611 MixerWindow->Window = NULL;
612 MixerWindow->WindowCount = 0;
613 }
614
615 static BOOL
RebuildMixerWindowControls(PPREFERENCES_CONTEXT PrefContext)616 RebuildMixerWindowControls(PPREFERENCES_CONTEXT PrefContext)
617 {
618 /* delete existing mixer controls */
619 DeleteMixerWindowControls(PrefContext->MixerWindow);
620
621 /* load new mixer controls */
622 LoadDialogCtrls(PrefContext);
623
624 return TRUE;
625 }
626
627 static
628 BOOL
629 CALLBACK
SetVolumeCallback(PSND_MIXER Mixer,DWORD LineID,LPMIXERLINE Line,PVOID Ctx)630 SetVolumeCallback(PSND_MIXER Mixer, DWORD LineID, LPMIXERLINE Line, PVOID Ctx)
631 {
632 UINT ControlCount = 0, Index;
633 LPMIXERCONTROL Control = NULL;
634 PMIXERCONTROLDETAILS_UNSIGNED puDetails = NULL;
635 MIXERCONTROLDETAILS_BOOLEAN bDetails;
636 PSET_VOLUME_CONTEXT Context = (PSET_VOLUME_CONTEXT)Ctx;
637
638 /* check if the line name is equal */
639 if (_wcsicmp(Line->szName, Context->LineName))
640 {
641 /* it is not */
642 return TRUE;
643 }
644
645 /* query controls */
646 if (SndMixerQueryControls(Mixer, &ControlCount, Line, &Control) == FALSE)
647 {
648 /* failed to query for controls */
649 return FALSE;
650 }
651
652 puDetails = HeapAlloc(GetProcessHeap(), 0, Line->cChannels * sizeof(MIXERCONTROLDETAILS_UNSIGNED));
653 if (puDetails == NULL)
654 return FALSE;
655
656 /* now go through all controls and compare control ids */
657 for (Index = 0; Index < ControlCount; Index++)
658 {
659 if (Context->bVertical)
660 {
661 if (Control[Index].dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
662 {
663 DWORD LineOffset, volumePosition, balancePosition;
664 DWORD volumeStep, balanceStep;
665
666 LineOffset = Context->SliderPos;
667
668 volumePosition = (DWORD)SendDlgItemMessage(Preferences.MixerWindow->hWnd, LineOffset * IDC_LINE_SLIDER_VERT, TBM_GETPOS, 0, 0);
669 volumeStep = (Control[Index].Bounds.dwMaximum - Control[Index].Bounds.dwMinimum) / (VOLUME_MAX - VOLUME_MIN);
670
671 if (Line->cChannels == 1)
672 {
673 /* set up details */
674 puDetails[0].dwValue = ((VOLUME_MAX - volumePosition) * volumeStep) + Control[Index].Bounds.dwMinimum;
675 }
676 else if (Line->cChannels == 2)
677 {
678 balancePosition = (DWORD)SendDlgItemMessage(Preferences.MixerWindow->hWnd, LineOffset * IDC_LINE_SLIDER_HORZ, TBM_GETPOS, 0, 0);
679 if (balancePosition == BALANCE_CENTER)
680 {
681 puDetails[0].dwValue = ((VOLUME_MAX - volumePosition) * volumeStep) + Control[Index].Bounds.dwMinimum;
682 puDetails[1].dwValue = ((VOLUME_MAX - volumePosition) * volumeStep) + Control[Index].Bounds.dwMinimum;
683 }
684 else if (balancePosition == BALANCE_LEFT)
685 {
686 puDetails[0].dwValue = ((VOLUME_MAX - volumePosition) * volumeStep) + Control[Index].Bounds.dwMinimum;
687 puDetails[1].dwValue = Control[Index].Bounds.dwMinimum;
688 }
689 else if (balancePosition == BALANCE_RIGHT)
690 {
691 puDetails[0].dwValue = Control[Index].Bounds.dwMinimum;
692 puDetails[1].dwValue = ((VOLUME_MAX - volumePosition) * volumeStep) + Control[Index].Bounds.dwMinimum;
693 }
694 else if (balancePosition < BALANCE_CENTER) // Left
695 {
696 puDetails[0].dwValue = ((VOLUME_MAX - volumePosition) * volumeStep) + Control[Index].Bounds.dwMinimum;
697
698 balanceStep = (puDetails[0].dwValue - Control[Index].Bounds.dwMinimum) / (BALANCE_STEPS / 2);
699
700 puDetails[1].dwValue = (balancePosition * balanceStep) + Control[Index].Bounds.dwMinimum;
701 }
702 else if (balancePosition > BALANCE_CENTER) // Right
703 {
704 puDetails[1].dwValue = ((VOLUME_MAX - volumePosition) * volumeStep) + Control[Index].Bounds.dwMinimum;
705
706 balanceStep = (puDetails[1].dwValue - Control[Index].Bounds.dwMinimum) / (BALANCE_STEPS / 2);
707
708 puDetails[0].dwValue = ((BALANCE_RIGHT - balancePosition) * balanceStep) + Control[Index].Bounds.dwMinimum;
709 }
710 }
711 else
712 {
713 SndMixerGetVolumeControlDetails(Preferences.MixerWindow->Mixer, Control[Index].dwControlID, Line->cChannels, sizeof(MIXERCONTROLDETAILS_UNSIGNED), (LPVOID)puDetails);
714
715 /* FIXME */
716 }
717
718 /* set volume */
719 SndMixerSetVolumeControlDetails(Preferences.MixerWindow->Mixer, Control[Index].dwControlID, Line->cChannels, sizeof(MIXERCONTROLDETAILS_UNSIGNED), (LPVOID)puDetails);
720
721 /* done */
722 break;
723 }
724 }
725 else if (Context->bSwitch)
726 {
727 if (Control[Index].dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
728 {
729 /* set up details */
730 bDetails.fValue = Context->SliderPos;
731
732 /* set volume */
733 SndMixerSetVolumeControlDetails(Preferences.MixerWindow->Mixer, Control[Index].dwControlID, 1, sizeof(MIXERCONTROLDETAILS_BOOLEAN), (LPVOID)&bDetails);
734
735 /* done */
736 break;
737 }
738 }
739 }
740
741 if (puDetails != NULL)
742 HeapFree(GetProcessHeap(), 0, puDetails);
743
744 /* free controls */
745 HeapFree(GetProcessHeap(), 0, Control);
746
747
748 /* done */
749 return TRUE;
750 }
751
752 static
753 BOOL
754 CALLBACK
MixerControlChangeCallback(PSND_MIXER Mixer,DWORD LineID,LPMIXERLINE Line,PVOID Context)755 MixerControlChangeCallback(PSND_MIXER Mixer, DWORD LineID, LPMIXERLINE Line, PVOID Context)
756 {
757 PMIXERCONTROLDETAILS_UNSIGNED pVolumeDetails = NULL;
758 UINT ControlCount = 0, Index;
759 LPMIXERCONTROL Control = NULL;
760
761 /* check if the line has controls */
762 if (Line->cControls == 0)
763 {
764 /* no controls */
765 return TRUE;
766 }
767
768 /* query controls */
769 if (SndMixerQueryControls(Mixer, &ControlCount, Line, &Control) == FALSE)
770 {
771 /* failed to query for controls */
772 return FALSE;
773 }
774
775 pVolumeDetails = HeapAlloc(GetProcessHeap(),
776 0,
777 Line->cChannels * sizeof(MIXERCONTROLDETAILS_UNSIGNED));
778 if (pVolumeDetails == NULL)
779 goto done;
780
781 /* now go through all controls and compare control ids */
782 for (Index = 0; Index < ControlCount; Index++)
783 {
784 if (Control[Index].dwControlID == PtrToUlong(Context))
785 {
786 if (Control[Index].dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
787 {
788 MIXERCONTROLDETAILS_BOOLEAN Details;
789
790 /* get volume control details */
791 if (SndMixerGetVolumeControlDetails(Preferences.MixerWindow->Mixer, Control[Index].dwControlID, 1, sizeof(MIXERCONTROLDETAILS_BOOLEAN), (LPVOID)&Details) != -1)
792 {
793 /* update dialog control */
794 UpdateDialogLineSwitchControl(&Preferences, Line, Details.fValue);
795 }
796 }
797 else if (Control[Index].dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
798 {
799 /* get volume control details */
800 if (SndMixerGetVolumeControlDetails(Preferences.MixerWindow->Mixer, Control[Index].dwControlID, Line->cChannels, sizeof(MIXERCONTROLDETAILS_UNSIGNED), (LPVOID)pVolumeDetails) != -1)
801 {
802 /* update dialog control */
803 DWORD volumePosition, volumeStep, maxVolume, i;
804 DWORD balancePosition, balanceStep;
805
806 volumeStep = (Control[Index].Bounds.dwMaximum - Control[Index].Bounds.dwMinimum) / (VOLUME_MAX - VOLUME_MIN);
807
808 maxVolume = 0;
809 for (i = 0; i < Line->cChannels; i++)
810 {
811 if (pVolumeDetails[i].dwValue > maxVolume)
812 maxVolume = pVolumeDetails[i].dwValue;
813 }
814
815 volumePosition = (maxVolume - Control[Index].Bounds.dwMinimum) / volumeStep;
816
817 if (Line->cChannels == 1)
818 {
819 balancePosition = BALANCE_CENTER;
820 }
821 else if (Line->cChannels == 2)
822 {
823 if (pVolumeDetails[0].dwValue == pVolumeDetails[1].dwValue)
824 {
825 balancePosition = BALANCE_CENTER;
826 }
827 else if (pVolumeDetails[0].dwValue == Control[Index].Bounds.dwMinimum)
828 {
829 balancePosition = BALANCE_RIGHT;
830 }
831 else if (pVolumeDetails[1].dwValue == Control[Index].Bounds.dwMinimum)
832 {
833 balancePosition = BALANCE_LEFT;
834 }
835 else
836 {
837 balanceStep = (maxVolume - Control[Index].Bounds.dwMinimum) / (BALANCE_STEPS / 2);
838
839 if (pVolumeDetails[0].dwValue < pVolumeDetails[1].dwValue)
840 {
841 balancePosition = (pVolumeDetails[0].dwValue - Control[Index].Bounds.dwMinimum) / balanceStep;
842 balancePosition = BALANCE_RIGHT - balancePosition;
843 }
844 else if (pVolumeDetails[1].dwValue < pVolumeDetails[0].dwValue)
845 {
846 balancePosition = (pVolumeDetails[1].dwValue - Control[Index].Bounds.dwMinimum) / balanceStep;
847 balancePosition = BALANCE_LEFT + balancePosition;
848 }
849 }
850 }
851
852 /* Update the volume control slider */
853 UpdateDialogLineSliderControl(&Preferences, Line, IDC_LINE_SLIDER_VERT, VOLUME_MAX - volumePosition);
854
855 /* Update the balance control slider */
856 UpdateDialogLineSliderControl(&Preferences, Line, IDC_LINE_SLIDER_HORZ, balancePosition);
857 }
858 }
859 break;
860 }
861 }
862
863 done:
864 /* Free the volume details */
865 if (pVolumeDetails)
866 HeapFree(GetProcessHeap(), 0, pVolumeDetails);
867
868 /* free controls */
869 HeapFree(GetProcessHeap(), 0, Control);
870
871 /* done */
872 return TRUE;
873 }
874
875 static LRESULT CALLBACK
MainWindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)876 MainWindowProc(HWND hwnd,
877 UINT uMsg,
878 WPARAM wParam,
879 LPARAM lParam)
880 {
881 PMIXER_WINDOW MixerWindow;
882 DWORD CtrlID, LineOffset;
883 BOOL bRet;
884 LRESULT Result = 0;
885 SET_VOLUME_CONTEXT Context;
886
887 switch (uMsg)
888 {
889 case WM_COMMAND:
890 {
891 MixerWindow = GetWindowData(hwnd,
892 MIXER_WINDOW);
893
894 switch (LOWORD(wParam))
895 {
896 case IDM_PROPERTIES:
897 {
898 PREFERENCES_CONTEXT Pref;
899
900 Pref.MixerWindow = MixerWindow;
901 Pref.Mixer = NULL;
902 Pref.SelectedLine = Preferences.SelectedLine;
903
904 if (DialogBoxParam(hAppInstance,
905 MAKEINTRESOURCE(IDD_PREFERENCES),
906 hwnd,
907 DlgPreferencesProc,
908 (LPARAM)&Pref) == IDOK)
909 {
910 /* update window */
911 TCHAR szProduct[MAXPNAMELEN];
912
913 /* get mixer product name */
914 if (SndMixerGetProductName(Pref.Mixer,
915 szProduct,
916 sizeof(szProduct) / sizeof(szProduct[0])) == -1)
917 {
918 /* failed to get name */
919 szProduct[0] = L'\0';
920 }
921 else
922 {
923 /* copy product */
924 wcscpy(Preferences.DeviceName, szProduct);
925 }
926
927 /* destroy old status bar */
928 if (MixerWindow->Mode == NORMAL_MODE)
929 DestroyWindow(MixerWindow->hStatusBar);
930
931 /* update details */
932 Preferences.SelectedLine = Pref.SelectedLine;
933
934 /* destroy old mixer */
935 SndMixerDestroy(Preferences.MixerWindow->Mixer);
936
937 /* use new selected mixer */
938 Preferences.MixerWindow->Mixer = Pref.Mixer;
939
940 /* create status window */
941 if (MixerWindow->Mode == NORMAL_MODE)
942 {
943 MixerWindow->hStatusBar = CreateStatusWindow(WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS,
944 NULL,
945 hwnd,
946 0);
947 if (MixerWindow->hStatusBar)
948 {
949 /* Set status bar */
950 SendMessage(MixerWindow->hStatusBar,
951 WM_SETTEXT,
952 0,
953 (LPARAM)szProduct);
954 }
955 }
956
957 /* rebuild dialog controls */
958 RebuildMixerWindowControls(&Preferences);
959 }
960 break;
961 }
962
963 case IDM_ADVANCED_CONTROLS:
964 MixerWindow->bShowExtendedControls = !MixerWindow->bShowExtendedControls;
965 CheckMenuItem(GetMenu(hwnd),
966 IDM_ADVANCED_CONTROLS,
967 MF_BYCOMMAND | (MixerWindow->bShowExtendedControls ? MF_CHECKED : MF_UNCHECKED));
968 RebuildMixerWindowControls(&Preferences);
969 break;
970
971 case IDM_EXIT:
972 {
973 PostQuitMessage(0);
974 UnregisterHotKey(hwnd, HOTKEY_CTRL_S);
975 break;
976 }
977
978 case IDM_ABOUT:
979 {
980 HICON hAppIcon = (HICON)GetClassLongPtrW(hwnd,
981 GCLP_HICON);
982 ShellAbout(hwnd,
983 lpAppTitle,
984 NULL,
985 hAppIcon);
986 break;
987 }
988
989 default:
990 {
991 /* get button id */
992 CtrlID = LOWORD(wParam);
993
994 /* check if the message is from the line switch */
995 if (HIWORD(wParam) == BN_CLICKED)
996 {
997 if (CtrlID % IDC_LINE_SWITCH == 0)
998 {
999 /* compute line offset */
1000 LineOffset = CtrlID / IDC_LINE_SWITCH;
1001
1002 /* compute window id of line name static control */
1003 CtrlID = LineOffset * IDC_LINE_NAME;
1004
1005 if (Preferences.MixerWindow->Mixer->MixerId == PLAY_MIXER)
1006 {
1007 /* get line name */
1008 if (GetDlgItemTextW(hwnd, CtrlID, Context.LineName, MIXER_LONG_NAME_CHARS) != 0)
1009 {
1010 /* setup context */
1011 Context.SliderPos = SendMessage((HWND)lParam, BM_GETCHECK, 0, 0);
1012 Context.bVertical = FALSE;
1013 Context.bSwitch = TRUE;
1014
1015 /* set volume */
1016 SndMixerEnumConnections(Preferences.MixerWindow->Mixer, Preferences.SelectedLine, SetVolumeCallback, (LPVOID)&Context);
1017 }
1018 }
1019 else if (Preferences.MixerWindow->Mixer->MixerId == RECORD_MIXER)
1020 {
1021 UINT i;
1022
1023 for (i = 0; i < Preferences.MixerWindow->DialogCount; i++)
1024 {
1025 SendDlgItemMessageW(hwnd, (i + 1) * IDC_LINE_SWITCH, BM_SETCHECK, (WPARAM)((i + 1) == LineOffset), 0);
1026 }
1027 }
1028 }
1029 else if (CtrlID % IDC_LINE_ADVANCED == 0)
1030 {
1031 ADVANCED_CONTEXT AdvancedContext;
1032
1033 /* compute line offset */
1034 LineOffset = CtrlID / IDC_LINE_ADVANCED;
1035
1036 /* compute window id of line name static control */
1037 CtrlID = LineOffset * IDC_LINE_NAME;
1038
1039 /* get line name */
1040 if (GetDlgItemTextW(hwnd, CtrlID, AdvancedContext.LineName, MIXER_LONG_NAME_CHARS) != 0)
1041 {
1042 AdvancedContext.MixerWindow = Preferences.MixerWindow;
1043 AdvancedContext.Mixer = Preferences.MixerWindow->Mixer;
1044 AdvancedContext.Line = SndMixerGetLineByName(Preferences.MixerWindow->Mixer,
1045 Preferences.SelectedLine,
1046 AdvancedContext.LineName);
1047 if (AdvancedContext.Line)
1048 {
1049 DialogBoxParam(hAppInstance,
1050 MAKEINTRESOURCE(IDD_ADVANCED),
1051 hwnd,
1052 AdvancedDlgProc,
1053 (LPARAM)&AdvancedContext);
1054 }
1055 }
1056 }
1057 }
1058 }
1059 }
1060 break;
1061 }
1062
1063 case MM_MIXM_LINE_CHANGE:
1064 {
1065 DPRINT("MM_MIXM_LINE_CHANGE\n");
1066 break;
1067 }
1068
1069 case MM_MIXM_CONTROL_CHANGE:
1070 {
1071 DPRINT("MM_MIXM_CONTROL_CHANGE\n");
1072
1073 /* get mixer window */
1074 MixerWindow = GetWindowData(hwnd,
1075 MIXER_WINDOW);
1076
1077 /* sanity checks */
1078 assert(MixerWindow);
1079 assert(MixerWindow->Mixer->hmx == (HMIXER)wParam);
1080
1081 SndMixerEnumConnections(MixerWindow->Mixer, Preferences.SelectedLine, MixerControlChangeCallback, (PVOID)lParam);
1082 break;
1083 }
1084
1085 case WM_VSCROLL:
1086 switch (LOWORD(wParam))
1087 {
1088 case TB_THUMBTRACK:
1089 /* get dialog item ctrl */
1090 CtrlID = GetDlgCtrlID((HWND)lParam);
1091
1092 /* get line index */
1093 LineOffset = CtrlID / IDC_LINE_SLIDER_VERT;
1094
1095 /* compute window id of line name static control */
1096 CtrlID = LineOffset * IDC_LINE_NAME;
1097
1098 /* get line name */
1099 if (GetDlgItemTextW(hwnd, CtrlID, Context.LineName, MIXER_LONG_NAME_CHARS) != 0)
1100 {
1101 /* setup context */
1102 Context.SliderPos = LineOffset;
1103 Context.bVertical = TRUE;
1104 Context.bSwitch = FALSE;
1105
1106 /* set volume */
1107 SndMixerEnumConnections(Preferences.MixerWindow->Mixer, Preferences.SelectedLine, SetVolumeCallback, (LPVOID)&Context);
1108 }
1109 break;
1110
1111 case TB_ENDTRACK:
1112 MixerWindow = GetWindowData(hwnd,
1113 MIXER_WINDOW);
1114
1115 /* get dialog item ctrl */
1116 CtrlID = GetDlgCtrlID((HWND)lParam);
1117
1118 /* get line index */
1119 LineOffset = CtrlID / IDC_LINE_SLIDER_VERT;
1120
1121 if (LineOffset == 1 && MixerWindow->Mixer->MixerId == 0)
1122 PlaySound((LPCTSTR)SND_ALIAS_SYSTEMDEFAULT, NULL, SND_ASYNC | SND_ALIAS_ID);
1123 break;
1124
1125 default:
1126 break;
1127 }
1128 break;
1129
1130 case WM_HSCROLL:
1131 switch (LOWORD(wParam))
1132 {
1133 case TB_THUMBTRACK:
1134 /* get dialog item ctrl */
1135 CtrlID = GetDlgCtrlID((HWND)lParam);
1136
1137 /* get line index */
1138 LineOffset = CtrlID / IDC_LINE_SLIDER_HORZ;
1139
1140 /* compute window id of line name static control */
1141 CtrlID = LineOffset * IDC_LINE_NAME;
1142
1143 /* get line name */
1144 if (GetDlgItemTextW(hwnd, CtrlID, Context.LineName, MIXER_LONG_NAME_CHARS) != 0)
1145 {
1146 /* setup context */
1147 Context.SliderPos = LineOffset;
1148 Context.bVertical = TRUE;
1149 Context.bSwitch = FALSE;
1150
1151 /* set volume */
1152 SndMixerEnumConnections(Preferences.MixerWindow->Mixer, Preferences.SelectedLine, SetVolumeCallback, (LPVOID)&Context);
1153 }
1154 break;
1155
1156 case TB_ENDTRACK:
1157 MixerWindow = GetWindowData(hwnd,
1158 MIXER_WINDOW);
1159
1160 /* get dialog item ctrl */
1161 CtrlID = GetDlgCtrlID((HWND)lParam);
1162
1163 /* get line index */
1164 LineOffset = CtrlID / IDC_LINE_SLIDER_HORZ;
1165
1166 if (LineOffset == 1 && MixerWindow->Mixer->MixerId == 0)
1167 PlaySound((LPCTSTR)SND_ALIAS_SYSTEMDEFAULT, NULL, SND_ASYNC | SND_ALIAS_ID);
1168 break;
1169
1170 default:
1171 break;
1172 }
1173 break;
1174
1175
1176 case WM_CREATE:
1177 {
1178 MixerWindow = ((LPCREATESTRUCT)lParam)->lpCreateParams;
1179 SetWindowLongPtr(hwnd,
1180 GWL_USERDATA,
1181 (LONG_PTR)MixerWindow);
1182 MixerWindow->hWnd = hwnd;
1183 MixerWindow->Mixer = SndMixerCreate(MixerWindow->hWnd, MixerWindow->MixerId);
1184 if (MixerWindow->Mixer != NULL)
1185 {
1186 TCHAR szProduct[MAXPNAMELEN];
1187
1188 /* get mixer product name */
1189 if (SndMixerGetProductName(MixerWindow->Mixer,
1190 szProduct,
1191 sizeof(szProduct) / sizeof(szProduct[0])) == -1)
1192 {
1193 /* failed to get name */
1194 szProduct[0] = L'\0';
1195 }
1196
1197
1198 /* initialize preferences */
1199 ZeroMemory(&Preferences, sizeof(Preferences));
1200
1201 /* store mixer */
1202 Preferences.Mixer = MixerWindow->Mixer;
1203
1204 /* store mixer window */
1205 Preferences.MixerWindow = MixerWindow;
1206
1207 /* first destination line id */
1208 Preferences.SelectedLine = 0xFFFF0000;
1209
1210 /* copy product */
1211 wcscpy(Preferences.DeviceName, szProduct);
1212
1213 /* Disable the 'Advanced Controls' menu item */
1214 EnableMenuItem(GetMenu(hwnd), IDM_ADVANCED_CONTROLS, MF_BYCOMMAND | MF_GRAYED);
1215
1216 /* Load the placement coordinate data of the window */
1217 bRet = LoadXYCoordWnd(&Preferences);
1218 if (bRet)
1219 {
1220 /*
1221 * LoadXYCoordWnd() might fail for the first time of opening the application which is normal as
1222 * the Sound Control's applet settings haven't been saved yet to the Registry. At this point SetWindowPos()
1223 * call is skipped.
1224 */
1225 SetWindowPos(hwnd, NULL, MixerWindow->WndPosX, MixerWindow->WndPosY, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
1226 }
1227
1228 /* create status window */
1229 if (MixerWindow->Mode == NORMAL_MODE)
1230 {
1231 MixerWindow->hStatusBar = CreateStatusWindow(WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS,
1232 NULL,
1233 hwnd,
1234 0);
1235 if (MixerWindow->hStatusBar)
1236 {
1237 SendMessage(MixerWindow->hStatusBar,
1238 WM_SETTEXT,
1239 0,
1240 (LPARAM)szProduct);
1241 }
1242 }
1243
1244 if (!RebuildMixerWindowControls(&Preferences))
1245 {
1246 DPRINT("Rebuilding mixer window controls failed!\n");
1247 SndMixerDestroy(MixerWindow->Mixer);
1248 MixerWindow->Mixer = NULL;
1249 Result = -1;
1250 }
1251 }
1252
1253 RegisterHotKey(hwnd, HOTKEY_CTRL_S, MOD_CONTROL, 'S');
1254 break;
1255 }
1256
1257 case WM_DESTROY:
1258 {
1259 MixerWindow = GetWindowData(hwnd,
1260 MIXER_WINDOW);
1261 if (MixerWindow != NULL)
1262 {
1263 if (MixerWindow->Mixer != NULL)
1264 {
1265 SndMixerDestroy(MixerWindow->Mixer);
1266 }
1267 if (MixerWindow->hFont)
1268 DeleteObject(MixerWindow->hFont);
1269 HeapFree(hAppHeap, 0, MixerWindow);
1270 }
1271 break;
1272 }
1273
1274 case WM_CLOSE:
1275 {
1276 SaveXYCoordWnd(hwnd, &Preferences);
1277 PostQuitMessage(0);
1278 break;
1279 }
1280
1281 case WM_HOTKEY:
1282 {
1283 if (wParam == HOTKEY_CTRL_S)
1284 {
1285 Preferences.MixerWindow->Mode = (Preferences.MixerWindow->Mode == NORMAL_MODE ? SMALL_MODE : NORMAL_MODE);
1286 RebuildMixerWindowControls(&Preferences);
1287 }
1288 break;
1289 }
1290
1291 default:
1292 {
1293 Result = DefWindowProc(hwnd,
1294 uMsg,
1295 wParam,
1296 lParam);
1297 break;
1298 }
1299 }
1300
1301 return Result;
1302 }
1303
1304 static BOOL
RegisterApplicationClasses(VOID)1305 RegisterApplicationClasses(VOID)
1306 {
1307 WNDCLASSEX wc;
1308
1309 wc.cbSize = sizeof(WNDCLASSEX);
1310 wc.style = CS_HREDRAW | CS_VREDRAW;
1311 wc.lpfnWndProc = MainWindowProc;
1312 wc.cbClsExtra = 0;
1313 wc.cbWndExtra = sizeof(PMIXER_WINDOW);
1314 wc.hInstance = hAppInstance;
1315 wc.hIcon = LoadIcon(hAppInstance,
1316 MAKEINTRESOURCE(IDI_MAINAPP));
1317 wc.hCursor = LoadCursor(NULL,
1318 IDC_ARROW);
1319 wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
1320 wc.lpszMenuName = NULL;
1321 wc.lpszClassName = SZ_APP_CLASS;
1322 wc.hIconSm = NULL;
1323 MainWindowClass = RegisterClassEx(&wc);
1324
1325 return MainWindowClass != 0;
1326 }
1327
1328 static VOID
UnregisterApplicationClasses(VOID)1329 UnregisterApplicationClasses(VOID)
1330 {
1331 UnregisterClass(SZ_APP_CLASS,
1332 hAppInstance);
1333 }
1334
1335 static HWND
CreateApplicationWindow(WINDOW_MODE WindowMode,UINT MixerId)1336 CreateApplicationWindow(
1337 WINDOW_MODE WindowMode,
1338 UINT MixerId)
1339 {
1340 HWND hWnd;
1341
1342 PMIXER_WINDOW MixerWindow = HeapAlloc(hAppHeap,
1343 HEAP_ZERO_MEMORY,
1344 sizeof(MIXER_WINDOW));
1345 if (MixerWindow == NULL)
1346 {
1347 return NULL;
1348 }
1349
1350 MixerWindow->Mode = WindowMode;
1351 MixerWindow->MixerId = MixerId;
1352
1353 if (mixerGetNumDevs() > 0)
1354 {
1355 hWnd = CreateWindowEx(WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT,
1356 SZ_APP_CLASS,
1357 lpAppTitle,
1358 WS_DLGFRAME | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE,
1359 0, 0, 300, 315,
1360 NULL,
1361 LoadMenu(hAppInstance,
1362 MAKEINTRESOURCE(IDM_MAINMENU)),
1363 hAppInstance,
1364 MixerWindow);
1365 }
1366 else
1367 {
1368 LPTSTR lpErrMessage;
1369
1370 /*
1371 * no mixer devices are available!
1372 */
1373
1374 hWnd = NULL;
1375 if (AllocAndLoadString(&lpErrMessage,
1376 hAppInstance,
1377 IDS_NOMIXERDEVICES))
1378 {
1379 MessageBox(NULL,
1380 lpErrMessage,
1381 lpAppTitle,
1382 MB_ICONINFORMATION);
1383 LocalFree(lpErrMessage);
1384 }
1385 }
1386
1387 if (hWnd == NULL)
1388 {
1389 HeapFree(hAppHeap,
1390 0,
1391 MixerWindow);
1392 }
1393
1394 return hWnd;
1395 }
1396
1397 static
1398 BOOL
HandleCommandLine(LPTSTR cmdline,DWORD dwStyle,PWINDOW_MODE pMode,PUINT pMixerId)1399 HandleCommandLine(LPTSTR cmdline,
1400 DWORD dwStyle,
1401 PWINDOW_MODE pMode,
1402 PUINT pMixerId)
1403 {
1404 TCHAR option;
1405
1406 *pMixerId = PLAY_MIXER;
1407 *pMode = (dwStyle & 0x20) ? SMALL_MODE : NORMAL_MODE;
1408
1409 while (*cmdline == _T(' ') || *cmdline == _T('-') || *cmdline == _T('/'))
1410 {
1411 if (*cmdline++ == _T(' '))
1412 continue;
1413
1414 option = *cmdline;
1415 if (option)
1416 cmdline++;
1417 while (*cmdline == _T(' '))
1418 cmdline++;
1419
1420 switch (option)
1421 {
1422 case 'd': /* Device */
1423 case 'D':
1424 break;
1425
1426 case 'n': /* Small size */
1427 case 'N':
1428 *pMode = NORMAL_MODE;
1429 break;
1430
1431 case 's': /* Small size */
1432 case 'S':
1433 *pMode = SMALL_MODE;
1434 break;
1435
1436 case 't': /* Tray size */
1437 case 'T':
1438 *pMode = TRAY_MODE;
1439 break;
1440
1441 case 'p': /* Play mode */
1442 case 'P':
1443 *pMixerId = PLAY_MIXER;
1444 break;
1445
1446 case 'r': /* Record mode */
1447 case 'R':
1448 *pMixerId = RECORD_MIXER;
1449 break;
1450
1451 default:
1452 return FALSE;
1453 }
1454 }
1455
1456 return TRUE;
1457 }
1458
1459 int WINAPI
_tWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR lpszCmdLine,int nCmdShow)1460 _tWinMain(HINSTANCE hInstance,
1461 HINSTANCE hPrevInstance,
1462 LPTSTR lpszCmdLine,
1463 int nCmdShow)
1464 {
1465 MSG Msg;
1466 int Ret = 1;
1467 INITCOMMONCONTROLSEX Controls;
1468 WINDOW_MODE WindowMode = SMALL_MODE;
1469 UINT MixerId = 0;
1470 DWORD dwStyle;
1471
1472 UNREFERENCED_PARAMETER(hPrevInstance);
1473 UNREFERENCED_PARAMETER(nCmdShow);
1474
1475 hAppInstance = hInstance;
1476 hAppHeap = GetProcessHeap();
1477
1478 if (InitAppConfig())
1479 {
1480 dwStyle = GetStyleValue();
1481 HandleCommandLine(lpszCmdLine, dwStyle, &WindowMode, &MixerId);
1482
1483 /* load the application title */
1484 if (!AllocAndLoadString(&lpAppTitle,
1485 hAppInstance,
1486 IDS_SNDVOL32))
1487 {
1488 lpAppTitle = NULL;
1489 }
1490
1491 Controls.dwSize = sizeof(INITCOMMONCONTROLSEX);
1492 Controls.dwICC = ICC_BAR_CLASSES | ICC_STANDARD_CLASSES;
1493
1494 InitCommonControlsEx(&Controls);
1495
1496 if (WindowMode == TRAY_MODE)
1497 {
1498 DialogBoxParam(hAppInstance,
1499 MAKEINTRESOURCE(IDD_TRAY_MASTER),
1500 NULL,
1501 TrayDlgProc,
1502 0);
1503 }
1504 else
1505 {
1506 if (RegisterApplicationClasses())
1507 {
1508 hMainWnd = CreateApplicationWindow(WindowMode, MixerId);
1509 if (hMainWnd != NULL)
1510 {
1511 BOOL bRet;
1512 while ((bRet =GetMessage(&Msg,
1513 NULL,
1514 0,
1515 0)) != 0)
1516 {
1517 if (bRet != -1)
1518 {
1519 TranslateMessage(&Msg);
1520 DispatchMessage(&Msg);
1521 }
1522 }
1523
1524 DestroyWindow(hMainWnd);
1525 Ret = 0;
1526 }
1527 else
1528 {
1529 DPRINT("Failed to create application window (LastError: %d)!\n", GetLastError());
1530 }
1531
1532 UnregisterApplicationClasses();
1533 }
1534 else
1535 {
1536 DPRINT("Failed to register application classes (LastError: %d)!\n", GetLastError());
1537 }
1538 }
1539
1540 if (lpAppTitle != NULL)
1541 {
1542 LocalFree(lpAppTitle);
1543 }
1544
1545 CloseAppConfig();
1546 }
1547 else
1548 {
1549 DPRINT("Unable to open the Volume Control registry key!\n");
1550 }
1551
1552 return Ret;
1553 }
1554