[SNDVOL32]
[reactos.git] / reactos / base / applications / sndvol32 / sndvol32.c
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 /* $Id$
20 *
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS Sound Volume Control
23 * FILE: subsys/system/sndvol32/sndvol32.c
24 * PROGRAMMERS: Thomas Weidenmueller <w3seek@reactos.com>
25 */
26 #include "sndvol32.h"
27
28 HINSTANCE hAppInstance;
29 ATOM MainWindowClass;
30 HWND hMainWnd;
31 HANDLE hAppHeap;
32 LPTSTR lpAppTitle;
33 PREFERENCES_CONTEXT Preferences;
34
35 #define GetDialogData(hwndDlg, type) \
36 ( P##type )GetWindowLongPtr((hwndDlg), DWLP_USER)
37 #define GetWindowData(hwnd, type) \
38 ( P##type )GetWindowLongPtr((hwnd), GWL_USERDATA)
39
40 /******************************************************************************/
41
42
43
44 typedef struct _PREFERENCES_FILL_DEVICES
45 {
46 PPREFERENCES_CONTEXT PrefContext;
47 HWND hComboBox;
48 UINT Selected;
49 } PREFERENCES_FILL_DEVICES, *PPREFERENCES_FILL_DEVICES;
50
51 static BOOL CALLBACK
52 FillDeviceComboBox(PSND_MIXER Mixer,
53 UINT Id,
54 LPCTSTR ProductName,
55 PVOID Context)
56 {
57 LRESULT lres;
58 PPREFERENCES_FILL_DEVICES FillContext = (PPREFERENCES_FILL_DEVICES)Context;
59
60 UNREFERENCED_PARAMETER(Mixer);
61
62 lres = SendMessage(FillContext->hComboBox,
63 CB_ADDSTRING,
64 0,
65 (LPARAM)ProductName);
66 if (lres != CB_ERR)
67 {
68 /* save the index so we don't screw stuff when the combobox is sorted... */
69 SendMessage(FillContext->hComboBox,
70 CB_SETITEMDATA,
71 (WPARAM)lres,
72 Id);
73
74 if (Id == FillContext->Selected)
75 {
76 SendMessage(FillContext->hComboBox,
77 CB_SETCURSEL,
78 (WPARAM)lres,
79 0);
80 }
81 }
82
83 return TRUE;
84 }
85
86 static BOOL CALLBACK
87 PrefDlgAddLine(PSND_MIXER Mixer,
88 LPMIXERLINE Line,
89 UINT DisplayControls,
90 PVOID Context)
91 {
92 PPREFERENCES_CONTEXT PrefContext = (PPREFERENCES_CONTEXT)Context;
93
94 UNREFERENCED_PARAMETER(Mixer);
95 UNREFERENCED_PARAMETER(DisplayControls);
96
97 switch (Line->dwComponentType)
98 {
99 case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS:
100 if (PrefContext->PlaybackID == (DWORD)-1)
101 {
102 PrefContext->PlaybackID = Line->dwLineID;
103
104 if (PrefContext->SelectedLine == (DWORD)-1)
105 {
106 PrefContext->SelectedLine = Line->dwLineID;
107 }
108 }
109 else
110 goto AddToOthersLines;
111
112 break;
113
114 case MIXERLINE_COMPONENTTYPE_DST_WAVEIN:
115 if (PrefContext->RecordingID == (DWORD)-1)
116 {
117 PrefContext->RecordingID = Line->dwLineID;
118
119 if (PrefContext->SelectedLine == (DWORD)-1)
120 {
121 PrefContext->SelectedLine = Line->dwLineID;
122 }
123 }
124 else
125 goto AddToOthersLines;
126
127 break;
128
129 default:
130 {
131 LRESULT lres;
132 HWND hwndCbOthers;
133
134 if (PrefContext->SelectedLine == (DWORD)-1)
135 {
136 PrefContext->SelectedLine = Line->dwLineID;
137 }
138
139 AddToOthersLines:
140 hwndCbOthers = GetDlgItem(PrefContext->hwndDlg,
141 IDC_LINE);
142
143 lres = SendMessage(hwndCbOthers,
144 CB_ADDSTRING,
145 0,
146 (LPARAM)Line->szName);
147 if (lres != CB_ERR)
148 {
149 SendMessage(hwndCbOthers,
150 CB_SETITEMDATA,
151 (WPARAM)lres,
152 Line->dwLineID);
153
154 PrefContext->OtherLines++;
155 }
156 break;
157 }
158 }
159
160 return TRUE;
161 }
162
163 static BOOL CALLBACK
164 PrefDlgAddConnection(PSND_MIXER Mixer,
165 DWORD LineID,
166 LPMIXERLINE Line,
167 PVOID Context)
168 {
169 PPREFERENCES_CONTEXT PrefContext = (PPREFERENCES_CONTEXT)Context;
170 HWND hwndControls;
171 LVITEM lvi;
172 UINT i;
173
174 UNREFERENCED_PARAMETER(Mixer);
175 UNREFERENCED_PARAMETER(LineID);
176
177 if (Line->cControls != 0)
178 {
179 hwndControls = GetDlgItem(PrefContext->hwndDlg,
180 IDC_CONTROLS);
181
182 lvi.mask = LVIF_TEXT | LVIF_PARAM;
183 lvi.iItem = PrefContext->tmp++;
184 lvi.iSubItem = 0;
185 lvi.pszText = Line->szName;
186 lvi.lParam = (LPARAM)Line->dwSource;
187
188 i = SendMessage(hwndControls,
189 LVM_INSERTITEM,
190 0,
191 (LPARAM)&lvi);
192 if (i != (UINT)-1)
193 {
194 TCHAR LineName[MIXER_LONG_NAME_CHARS];
195 DWORD Flags;
196 BOOL SelLine = FALSE;
197
198 if (SndMixerGetLineName(PrefContext->Mixer,
199 PrefContext->SelectedLine,
200 LineName,
201 MIXER_LONG_NAME_CHARS,
202 FALSE) == -1)
203 {
204 LineName[0] = TEXT('\0');
205 }
206
207 if (ReadLineConfig(PrefContext->DeviceName,
208 LineName,
209 Line->szName,
210 &Flags))
211 {
212 if (Flags != 0x4)
213 {
214 SelLine = TRUE;
215 }
216 }
217
218 ListView_SetCheckState(hwndControls,
219 i,
220 SelLine);
221 }
222 }
223
224 return TRUE;
225 }
226
227 static VOID
228 UpdatePrefDlgControls(PPREFERENCES_CONTEXT Context,
229 DWORD LineID)
230 {
231 INT OldID, MixerID = 0;
232 INT DeviceCbIndex;
233
234 /* select the mixer */
235 DeviceCbIndex = SendDlgItemMessage(Context->hwndDlg,
236 IDC_MIXERDEVICE,
237 CB_GETCURSEL,
238 0,
239 0);
240 if (DeviceCbIndex != CB_ERR)
241 {
242 MixerID = SendDlgItemMessage(Context->hwndDlg,
243 IDC_MIXERDEVICE,
244 CB_GETITEMDATA,
245 DeviceCbIndex,
246 0);
247 if (MixerID == CB_ERR)
248 {
249 MixerID = 0;
250 }
251 }
252
253 OldID = Context->Selected;
254 if (MixerID != OldID &&
255 SndMixerSelect(Context->Mixer,
256 MixerID))
257 {
258 Context->Selected = SndMixerGetSelection(Context->Mixer);
259
260 /* update the controls */
261 Context->PlaybackID = (DWORD)-1;
262 Context->RecordingID = (DWORD)-1;
263 Context->OtherLines = 0;
264 Context->SelectedLine = (DWORD)-1;
265
266 SndMixerGetProductName(Context->Mixer,
267 Context->DeviceName,
268 sizeof(Context->DeviceName) / sizeof(Context->DeviceName[0]));
269
270 if (SndMixerEnumLines(Context->Mixer,
271 PrefDlgAddLine,
272 Context))
273 {
274 UINT SelBox = 0;
275
276 /* enable/disable controls and make default selection */
277 EnableWindow(GetDlgItem(Context->hwndDlg,
278 IDC_PLAYBACK),
279 Context->PlaybackID != (DWORD)-1);
280 CheckDlgButton(Context->hwndDlg,
281 IDC_PLAYBACK,
282 (Context->PlaybackID != (DWORD)-1 && SelBox++ == 0) ?
283 BST_CHECKED : BST_UNCHECKED);
284
285 EnableWindow(GetDlgItem(Context->hwndDlg,
286 IDC_RECORDING),
287 Context->RecordingID != (DWORD)-1);
288 CheckDlgButton(Context->hwndDlg,
289 IDC_RECORDING,
290 (Context->RecordingID != (DWORD)-1 && SelBox++ == 0) ?
291 BST_CHECKED : BST_UNCHECKED);
292
293 if (Context->OtherLines != 0)
294 {
295 /* select the first item in the other lines combo box by default */
296 SendDlgItemMessage(Context->hwndDlg,
297 IDC_LINE,
298 CB_SETCURSEL,
299 0,
300 0);
301 }
302 EnableWindow(GetDlgItem(Context->hwndDlg,
303 IDC_LINE),
304 FALSE);
305 EnableWindow(GetDlgItem(Context->hwndDlg,
306 IDC_OTHER),
307 Context->OtherLines != 0);
308 CheckDlgButton(Context->hwndDlg,
309 IDC_LINE,
310 (Context->OtherLines != 0 && SelBox++ == 0) ?
311 BST_CHECKED : BST_UNCHECKED);
312
313 /* disable the OK button if the device doesn't have any lines */
314 EnableWindow(GetDlgItem(Context->hwndDlg,
315 IDOK),
316 Context->PlaybackID != (DWORD)-1 ||
317 Context->RecordingID != (DWORD)-1 ||
318 Context->OtherLines != 0);
319
320 LineID = Context->SelectedLine;
321 }
322 }
323
324 /* update the line sources list */
325 if ((MixerID != OldID && Context->SelectedLine != (DWORD)-1) ||
326 (Context->SelectedLine != LineID && LineID != (DWORD)-1))
327 {
328 Context->SelectedLine = LineID;
329
330 (void)ListView_DeleteAllItems(GetDlgItem(Context->hwndDlg,
331 IDC_CONTROLS));
332
333 Context->tmp = 0;
334 SndMixerEnumConnections(Context->Mixer,
335 LineID,
336 PrefDlgAddConnection,
337 Context);
338 }
339 }
340
341 static
342 VOID
343 WriteLineSettings(PREFERENCES_CONTEXT Context, HWND hwndDlg)
344 {
345 HWND hwndControls;
346 INT Count, Index;
347 WCHAR LineName[MIXER_LONG_NAME_CHARS];
348 WCHAR DestinationName[MIXER_LONG_NAME_CHARS];
349 DWORD Flags;
350
351 /* get list view */
352 hwndControls = GetDlgItem(hwndDlg, IDC_CONTROLS);
353
354 /* get list item count */
355 Count = ListView_GetItemCount(hwndControls);
356
357 /* sanity check */
358 assert(Count);
359
360 if (SndMixerGetLineName(Preferences.MixerWindow->Mixer, Preferences.SelectedLine, DestinationName, MIXER_LONG_NAME_CHARS, TRUE) == -1)
361 {
362 /* failed to get destination line name */
363 return;
364 }
365
366 /* allocate line states array */
367 for(Index = 0; Index < Count; Index++)
368 {
369 /* set to empty */
370 LineName[0] = L'\0';
371
372 /* get item text */
373 ListView_GetItemText(hwndControls, Index, 0, LineName, MIXER_LONG_NAME_CHARS);
374
375 /* make sure it is null terminated */
376 LineName[MIXER_LONG_NAME_CHARS-1] = L'\0';
377
378 /* get check state */
379 Flags = (ListView_GetCheckState(hwndControls, Index) == 0 ? 0x4 : 0);
380
381 /* write configuration */
382 WriteLineConfig(Preferences.DeviceName, DestinationName, LineName, Flags);
383 }
384
385
386 }
387
388 static INT_PTR CALLBACK
389 DlgPreferencesProc(HWND hwndDlg,
390 UINT uMsg,
391 WPARAM wParam,
392 LPARAM lParam)
393 {
394 PPREFERENCES_CONTEXT Context;
395
396 switch (uMsg)
397 {
398 case WM_COMMAND:
399 {
400 Context = GetDialogData(hwndDlg,
401 PREFERENCES_CONTEXT);
402 switch (LOWORD(wParam))
403 {
404 case IDC_MIXERDEVICE:
405 {
406 if (HIWORD(wParam) == CBN_SELCHANGE)
407 {
408 UpdatePrefDlgControls(Context,
409 (DWORD)-1);
410 }
411 break;
412 }
413
414 case IDC_LINE:
415 {
416 if (HIWORD(wParam) == CBN_SELCHANGE)
417 {
418 INT LineID;
419 INT Index;
420
421 Index = SendDlgItemMessage(hwndDlg,
422 IDC_LINE,
423 CB_GETCURSEL,
424 0,
425 0);
426 if (Index != CB_ERR)
427 {
428 LineID = SendDlgItemMessage(hwndDlg,
429 IDC_LINE,
430 CB_GETITEMDATA,
431 Index,
432 0);
433 if (LineID != CB_ERR)
434 {
435 UpdatePrefDlgControls(Context,
436 LineID);
437 }
438 }
439 }
440 break;
441 }
442
443 case IDC_PLAYBACK:
444 {
445 UpdatePrefDlgControls(Context,
446 Context->PlaybackID);
447 EnableWindow(GetDlgItem(hwndDlg,
448 IDC_LINE),
449 FALSE);
450 break;
451 }
452
453 case IDC_RECORDING:
454 {
455 UpdatePrefDlgControls(Context,
456 Context->RecordingID);
457 EnableWindow(GetDlgItem(hwndDlg,
458 IDC_LINE),
459 FALSE);
460 break;
461 }
462
463 case IDC_OTHER:
464 {
465 INT LineCbIndex;
466 INT LineID;
467
468 EnableWindow(GetDlgItem(hwndDlg,
469 IDC_LINE),
470 TRUE);
471
472 LineCbIndex = SendDlgItemMessage(hwndDlg,
473 IDC_LINE,
474 CB_GETCURSEL,
475 0,
476 0);
477 if (LineCbIndex != CB_ERR)
478 {
479 LineID = SendDlgItemMessage(hwndDlg,
480 IDC_LINE,
481 CB_GETITEMDATA,
482 LineCbIndex,
483 0);
484 if (LineID != CB_ERR)
485 {
486 UpdatePrefDlgControls(Context,
487 LineID);
488 }
489 }
490 break;
491 }
492
493 case IDOK:
494 {
495 /* write line settings */
496 WriteLineSettings(Preferences, hwndDlg);
497
498 /* fall through */
499 }
500 case IDCANCEL:
501 {
502 EndDialog(hwndDlg,
503 LOWORD(wParam));
504 break;
505 }
506 }
507 break;
508 }
509
510 case WM_INITDIALOG:
511 {
512 PREFERENCES_FILL_DEVICES FillDevContext;
513 LVCOLUMN lvc;
514 RECT rcClient;
515 HWND hwndControls;
516
517 SetWindowLongPtr(hwndDlg,
518 DWLP_USER,
519 (LONG_PTR)lParam);
520 Context = (PPREFERENCES_CONTEXT)((LONG_PTR)lParam);
521 Context->hwndDlg = hwndDlg;
522 Context->Mixer = SndMixerCreate(hwndDlg);
523 Context->Selected = (UINT)-1;
524
525 FillDevContext.PrefContext = Context;
526 FillDevContext.hComboBox = GetDlgItem(hwndDlg,
527 IDC_MIXERDEVICE);
528 FillDevContext.Selected = SndMixerGetSelection(Context->Mixer);
529 SndMixerEnumProducts(Context->Mixer,
530 FillDeviceComboBox,
531 &FillDevContext);
532
533 /* initialize the list view control */
534 hwndControls = GetDlgItem(hwndDlg,
535 IDC_CONTROLS);
536 (void)ListView_SetExtendedListViewStyle(hwndControls,
537 LVS_EX_CHECKBOXES);
538
539 GetClientRect(hwndControls,
540 &rcClient);
541 lvc.mask = LVCF_TEXT | LVCF_WIDTH;
542 lvc.pszText = TEXT("");
543 lvc.cx = rcClient.right;
544 SendMessage(hwndControls,
545 LVM_INSERTCOLUMN,
546 0,
547 (LPARAM)&lvc);
548
549 /* update all controls */
550 UpdatePrefDlgControls(Context,
551 (DWORD)-1);
552 return TRUE;
553 }
554
555 case WM_DESTROY:
556 {
557 Context = GetDialogData(hwndDlg,
558 PREFERENCES_CONTEXT);
559 if (Context->Mixer != NULL)
560 {
561 SndMixerDestroy(Context->Mixer);
562 }
563 break;
564 }
565
566 case WM_CLOSE:
567 {
568 EndDialog(hwndDlg,
569 IDCANCEL);
570 break;
571 }
572 }
573
574 return 0;
575 }
576
577
578 /******************************************************************************/
579
580 static VOID
581 DeleteMixerWindowControls(PMIXER_WINDOW MixerWindow)
582 {
583 DWORD Index;
584
585 for(Index = 0; Index < MixerWindow->WindowCount; Index++)
586 {
587 /* destroys the window */
588 DestroyWindow(MixerWindow->Window[Index]);
589 }
590
591 /* free memory */
592 HeapFree(GetProcessHeap(), 0, MixerWindow->Window);
593
594 /* set to null */
595 MixerWindow->Window = NULL;
596 MixerWindow->WindowCount = 0;
597 }
598
599 static BOOL
600 RebuildMixerWindowControls(PPREFERENCES_CONTEXT PrefContext)
601 {
602 /* delete existing mixer controls */
603 DeleteMixerWindowControls(PrefContext->MixerWindow);
604
605 /* load new mixer controls */
606 LoadDialogCtrls(PrefContext);
607
608 return TRUE;
609 }
610
611 static
612 BOOL
613 CALLBACK
614 SetVolumeCallback(PSND_MIXER Mixer, DWORD LineID, LPMIXERLINE Line, PVOID Ctx)
615 {
616 UINT ControlCount = 0, Index;
617 LPMIXERCONTROL Control = NULL;
618 MIXERCONTROLDETAILS_UNSIGNED uDetails;
619 MIXERCONTROLDETAILS_BOOLEAN bDetails;
620 PSET_VOLUME_CONTEXT Context = (PSET_VOLUME_CONTEXT)Ctx;
621
622 /* check if the line name is equal */
623 if (wcsicmp(Line->szName, Context->LineName))
624 {
625 /* it is not */
626 return TRUE;
627 }
628
629 /* query controls */
630 if (SndMixerQueryControls(Mixer, &ControlCount, Line, &Control) == FALSE)
631 {
632 /* failed to query for controls */
633 return FALSE;
634 }
635
636 /* now go through all controls and compare control ids */
637 for(Index = 0; Index < ControlCount; Index++)
638 {
639 if (Context->bVertical)
640 {
641 if ((Control[Index].dwControlType & MIXERCONTROL_CT_CLASS_MASK) == MIXERCONTROL_CT_CLASS_FADER)
642 {
643 /* FIXME: give me granularity */
644 DWORD Step = 0x10000 / 5;
645
646 /* set up details */
647 uDetails.dwValue = 0x10000 - Step * Context->SliderPos;
648
649 /* set volume */
650 SndMixerSetVolumeControlDetails(Preferences.MixerWindow->Mixer, Control[Index].dwControlID, sizeof(MIXERCONTROLDETAILS_UNSIGNED), (LPVOID)&uDetails);
651
652 /* done */
653 break;
654 }
655 }
656 else if (Context->bSwitch)
657 {
658 if ((Control[Index].dwControlType & MIXERCONTROL_CT_CLASS_MASK) == MIXERCONTROL_CT_CLASS_SWITCH)
659 {
660 /* set up details */
661 bDetails.fValue = Context->SliderPos;
662
663 /* set volume */
664 SndMixerSetVolumeControlDetails(Preferences.MixerWindow->Mixer, Control[Index].dwControlID, sizeof(MIXERCONTROLDETAILS_BOOLEAN), (LPVOID)&bDetails);
665
666 /* done */
667 break;
668 }
669 }
670 else
671 {
672 /* FIXME: implement left - right channel switch support */
673 assert(0);
674 }
675 }
676
677 /* free controls */
678 HeapFree(GetProcessHeap(), 0, Control);
679
680
681 /* done */
682 return TRUE;
683 }
684
685 static
686 BOOL
687 CALLBACK
688 MixerControlChangeCallback(PSND_MIXER Mixer, DWORD LineID, LPMIXERLINE Line, PVOID Context)
689 {
690 UINT ControlCount = 0, Index;
691 LPMIXERCONTROL Control = NULL;
692
693 /* check if the line has controls */
694 if (Line->cControls == 0)
695 {
696 /* no controls */
697 return TRUE;
698 }
699
700 /* query controls */
701 if (SndMixerQueryControls(Mixer, &ControlCount, Line, &Control) == FALSE)
702 {
703 /* failed to query for controls */
704 return FALSE;
705 }
706
707 /* now go through all controls and compare control ids */
708 for(Index = 0; Index < ControlCount; Index++)
709 {
710 if (Control[Index].dwControlID == PtrToUlong(Context))
711 {
712 if ((Control[Index].dwControlType & MIXERCONTROL_CT_CLASS_MASK) == MIXERCONTROL_CT_CLASS_SWITCH)
713 {
714 MIXERCONTROLDETAILS_BOOLEAN Details;
715
716 /* get volume control details */
717 if (SndMixerGetVolumeControlDetails(Preferences.MixerWindow->Mixer, Control[Index].dwControlID, sizeof(MIXERCONTROLDETAILS_BOOLEAN), (LPVOID)&Details) != -1)
718 {
719 /* update dialog control */
720 UpdateDialogLineSwitchControl(&Preferences, Line, Details.fValue);
721 }
722 }
723 else if ((Control[Index].dwControlType & MIXERCONTROL_CT_CLASS_MASK) == MIXERCONTROL_CT_CLASS_FADER)
724 {
725 MIXERCONTROLDETAILS_UNSIGNED Details;
726
727 /* get volume control details */
728 if (SndMixerGetVolumeControlDetails(Preferences.MixerWindow->Mixer, Control[Index].dwControlID, sizeof(MIXERCONTROLDETAILS_UNSIGNED), (LPVOID)&Details) != -1)
729 {
730 /* update dialog control */
731 DWORD Position;
732 DWORD Step = 0x10000 / 5;
733
734 /* FIXME: give me granularity */
735 Position = 5 - (Details.dwValue / Step);
736
737 /* update volume control slider */
738 UpdateDialogLineSliderControl(&Preferences, Line, Control[Index].dwControlID, IDC_LINE_SLIDER_VERT, Position);
739 }
740 }
741 break;
742 }
743 }
744
745 /* free controls */
746 HeapFree(GetProcessHeap(), 0, Control);
747
748
749 /* done */
750 return TRUE;
751 }
752
753
754 static LRESULT CALLBACK
755 MainWindowProc(HWND hwnd,
756 UINT uMsg,
757 WPARAM wParam,
758 LPARAM lParam)
759 {
760 PMIXER_WINDOW MixerWindow;
761 DWORD CtrlID, LineOffset;
762 LRESULT Result = 0;
763 SET_VOLUME_CONTEXT Context;
764
765 switch (uMsg)
766 {
767 case WM_COMMAND:
768 {
769 MixerWindow = GetWindowData(hwnd,
770 MIXER_WINDOW);
771
772 switch (LOWORD(wParam))
773 {
774 case IDC_PROPERTIES:
775 {
776 PREFERENCES_CONTEXT Preferences;
777
778 Preferences.MixerWindow = MixerWindow;
779 Preferences.Mixer = NULL;
780
781 if (DialogBoxParam(hAppInstance,
782 MAKEINTRESOURCE(IDD_PREFERENCES),
783 hwnd,
784 DlgPreferencesProc,
785 (LPARAM)&Preferences) == IDOK)
786 {
787 /* update window */
788 TCHAR szProduct[MAXPNAMELEN];
789
790 /* get mixer product name */
791 if (SndMixerGetProductName(MixerWindow->Mixer,
792 szProduct,
793 sizeof(szProduct) / sizeof(szProduct[0])) == -1)
794 {
795 /* failed to get name */
796 szProduct[0] = L'\0';
797 }
798 else
799 {
800 /* copy product */
801 wcscpy(Preferences.DeviceName, szProduct);
802 }
803
804 /* destroy old status bar */
805 DestroyWindow(MixerWindow->hStatusBar);
806
807 /* rebuild dialog controls */
808 if (RebuildMixerWindowControls(&Preferences))
809 {
810 DPRINT("Rebuilding mixer window controls failed!\n");
811 }
812
813 /* create status window */
814 MixerWindow->hStatusBar = CreateStatusWindow(WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS,
815 NULL,
816 hwnd,
817 0);
818
819 /* set status bar */
820 if (MixerWindow->hStatusBar)
821 {
822 SendMessage(MixerWindow->hStatusBar,
823 WM_SETTEXT,
824 0,
825 (LPARAM)szProduct);
826 }
827 }
828 break;
829 }
830
831 case IDC_EXIT:
832 {
833 PostQuitMessage(0);
834 break;
835 }
836
837 case IDC_ABOUT:
838 {
839 HICON hAppIcon = (HICON)GetClassLongPtrW(hwnd,
840 GCLP_HICON);
841 ShellAbout(hwnd,
842 lpAppTitle,
843 NULL,
844 hAppIcon);
845 break;
846 }
847
848 default:
849 {
850 /* get button id */
851 CtrlID = LOWORD(wParam);
852
853 /* check if the message is from the line switch */
854 if (HIWORD(wParam) == BN_CLICKED && (CtrlID % IDC_LINE_SWITCH == 0))
855 {
856 /* compute line offset */
857 LineOffset = CtrlID / IDC_LINE_SWITCH;
858
859 /* compute window id of line name static control */
860 CtrlID = LineOffset * IDC_LINE_NAME;
861
862 /* get line name */
863 if (GetDlgItemTextW(hwnd, CtrlID, Context.LineName, MIXER_LONG_NAME_CHARS) != 0)
864 {
865 /* setup context */
866 Context.SliderPos = SendMessage((HWND)lParam, BM_GETCHECK, 0, 0);
867 Context.bVertical = FALSE;
868 Context.bSwitch = TRUE;
869
870 /* set volume */
871 SndMixerEnumConnections(Preferences.MixerWindow->Mixer, Preferences.SelectedLine, SetVolumeCallback, (LPVOID)&Context);
872 }
873 }
874 }
875
876 }
877 break;
878 }
879
880 case MM_MIXM_LINE_CHANGE:
881 {
882 DPRINT("MM_MIXM_LINE_CHANGE\n");
883 break;
884 }
885
886 case MM_MIXM_CONTROL_CHANGE:
887 {
888 DPRINT("MM_MIXM_CONTROL_CHANGE\n");
889
890 /* get mixer window */
891 MixerWindow = GetWindowData(hwnd,
892 MIXER_WINDOW);
893
894 /* sanity checks */
895 assert(MixerWindow);
896 assert(MixerWindow->Mixer->hmx == (HMIXER)wParam);
897
898 SndMixerEnumConnections(MixerWindow->Mixer, Preferences.SelectedLine, MixerControlChangeCallback, (PVOID)lParam);
899 break;
900 }
901
902 case WM_VSCROLL:
903 {
904 if (LOWORD(wParam) == TB_THUMBTRACK)
905 {
906 /* get dialog item ctrl */
907 CtrlID = GetDlgCtrlID((HWND)lParam);
908
909 /* get line index */
910 LineOffset = CtrlID / IDC_LINE_SLIDER_VERT;
911
912 /* compute window id of line name static control */
913 CtrlID = LineOffset * IDC_LINE_NAME;
914
915 /* get line name */
916 if (GetDlgItemTextW(hwnd, CtrlID, Context.LineName, MIXER_LONG_NAME_CHARS) != 0)
917 {
918 /* setup context */
919 Context.SliderPos = HIWORD(wParam);
920 Context.bVertical = TRUE;
921 Context.bSwitch = FALSE;
922
923 /* set volume */
924 SndMixerEnumConnections(Preferences.MixerWindow->Mixer, Preferences.SelectedLine, SetVolumeCallback, (LPVOID)&Context);
925 }
926 }
927
928 break;
929 }
930
931
932 case WM_CREATE:
933 {
934 MixerWindow = ((LPCREATESTRUCT)lParam)->lpCreateParams;
935 SetWindowLongPtr(hwnd,
936 GWL_USERDATA,
937 (LONG_PTR)MixerWindow);
938 MixerWindow->hWnd = hwnd;
939 MixerWindow->Mixer = SndMixerCreate(MixerWindow->hWnd);
940 if (MixerWindow->Mixer != NULL)
941 {
942 TCHAR szProduct[MAXPNAMELEN];
943
944 /* get mixer product name */
945 if (SndMixerGetProductName(MixerWindow->Mixer,
946 szProduct,
947 sizeof(szProduct) / sizeof(szProduct[0])) == -1)
948 {
949 /* failed to get name */
950 szProduct[0] = L'\0';
951 }
952
953
954 /* initialize perferences */
955 ZeroMemory(&Preferences, sizeof(Preferences));
956
957 /* store mixer */
958 Preferences.Mixer = MixerWindow->Mixer;
959
960 /* store mixer window */
961 Preferences.MixerWindow = MixerWindow;
962
963 /* first destination line id */
964 Preferences.SelectedLine = 0xFFFF0000;
965
966 /* copy product */
967 wcscpy(Preferences.DeviceName, szProduct);
968
969 if (!RebuildMixerWindowControls(&Preferences))
970 {
971 DPRINT("Rebuilding mixer window controls failed!\n");
972 SndMixerDestroy(MixerWindow->Mixer);
973 MixerWindow->Mixer = NULL;
974 Result = -1;
975 }
976
977 /* create status window */
978 MixerWindow->hStatusBar = CreateStatusWindow(WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS,
979 NULL,
980 hwnd,
981 0);
982 if (MixerWindow->hStatusBar)
983 {
984 SendMessage(MixerWindow->hStatusBar,
985 WM_SETTEXT,
986 0,
987 (LPARAM)szProduct);
988 }
989 }
990 break;
991 }
992
993 case WM_DESTROY:
994 {
995 MixerWindow = GetWindowData(hwnd,
996 MIXER_WINDOW);
997 if (MixerWindow->Mixer != NULL)
998 {
999 SndMixerDestroy(MixerWindow->Mixer);
1000 }
1001 break;
1002 }
1003
1004 case WM_CLOSE:
1005 {
1006 PostQuitMessage(0);
1007 break;
1008 }
1009
1010 default:
1011 {
1012 Result = DefWindowProc(hwnd,
1013 uMsg,
1014 wParam,
1015 lParam);
1016 break;
1017 }
1018 }
1019
1020 return Result;
1021 }
1022
1023 static BOOL
1024 RegisterApplicationClasses(VOID)
1025 {
1026 WNDCLASSEX wc;
1027
1028 wc.cbSize = sizeof(WNDCLASSEX);
1029 wc.style = CS_HREDRAW | CS_VREDRAW;
1030 wc.lpfnWndProc = MainWindowProc;
1031 wc.cbClsExtra = 0;
1032 wc.cbWndExtra = sizeof(PMIXER_WINDOW);
1033 wc.hInstance = hAppInstance;
1034 wc.hIcon = LoadIcon(hAppInstance,
1035 MAKEINTRESOURCE(IDI_MAINAPP));
1036 wc.hCursor = LoadCursor(NULL,
1037 IDC_ARROW);
1038 wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
1039 wc.lpszMenuName = NULL;
1040 wc.lpszClassName = SZ_APP_CLASS;
1041 wc.hIconSm = NULL;
1042 MainWindowClass = RegisterClassEx(&wc);
1043
1044 return MainWindowClass != 0;
1045 }
1046
1047 static VOID
1048 UnregisterApplicationClasses(VOID)
1049 {
1050 UnregisterClass(SZ_APP_CLASS,
1051 hAppInstance);
1052 }
1053
1054 static HWND
1055 CreateApplicationWindow(VOID)
1056 {
1057 HWND hWnd;
1058
1059 PMIXER_WINDOW MixerWindow = HeapAlloc(hAppHeap,
1060 HEAP_ZERO_MEMORY,
1061 sizeof(MIXER_WINDOW));
1062 if (MixerWindow == NULL)
1063 {
1064 return NULL;
1065 }
1066
1067 if (mixerGetNumDevs() > 0)
1068 {
1069 hWnd = CreateWindowEx(WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT,
1070 SZ_APP_CLASS,
1071 lpAppTitle,
1072 WS_DLGFRAME | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE,
1073 0, 0, 300, 315,
1074 NULL,
1075 LoadMenu(hAppInstance,
1076 MAKEINTRESOURCE(IDM_MAINMENU)),
1077 hAppInstance,
1078 MixerWindow);
1079 }
1080 else
1081 {
1082 LPTSTR lpErrMessage;
1083
1084 /*
1085 * no mixer devices are available!
1086 */
1087
1088 hWnd = NULL;
1089 if (AllocAndLoadString(&lpErrMessage,
1090 hAppInstance,
1091 IDS_NOMIXERDEVICES))
1092 {
1093 MessageBox(NULL,
1094 lpErrMessage,
1095 lpAppTitle,
1096 MB_ICONINFORMATION);
1097 LocalFree(lpErrMessage);
1098 }
1099 }
1100
1101 if (hWnd == NULL)
1102 {
1103 HeapFree(hAppHeap,
1104 0,
1105 MixerWindow);
1106 }
1107
1108 return hWnd;
1109 }
1110
1111 int WINAPI
1112 _tWinMain(HINSTANCE hInstance,
1113 HINSTANCE hPrevInstance,
1114 LPTSTR lpszCmdLine,
1115 int nCmdShow)
1116 {
1117 MSG Msg;
1118 int Ret = 1;
1119 INITCOMMONCONTROLSEX Controls;
1120
1121 UNREFERENCED_PARAMETER(hPrevInstance);
1122 UNREFERENCED_PARAMETER(lpszCmdLine);
1123 UNREFERENCED_PARAMETER(nCmdShow);
1124
1125 hAppInstance = hInstance;
1126 hAppHeap = GetProcessHeap();
1127
1128 if (InitAppConfig())
1129 {
1130 /* load the application title */
1131 if (!AllocAndLoadString(&lpAppTitle,
1132 hAppInstance,
1133 IDS_SNDVOL32))
1134 {
1135 lpAppTitle = NULL;
1136 }
1137
1138 Controls.dwSize = sizeof(INITCOMMONCONTROLSEX);
1139 Controls.dwICC = ICC_BAR_CLASSES | ICC_STANDARD_CLASSES;
1140
1141 InitCommonControlsEx(&Controls);
1142
1143 if (RegisterApplicationClasses())
1144 {
1145 hMainWnd = CreateApplicationWindow();
1146 if (hMainWnd != NULL)
1147 {
1148 BOOL bRet;
1149 while ((bRet =GetMessage(&Msg,
1150 NULL,
1151 0,
1152 0)) != 0)
1153 {
1154 if (bRet != -1)
1155 {
1156 TranslateMessage(&Msg);
1157 DispatchMessage(&Msg);
1158 }
1159 }
1160
1161 DestroyWindow(hMainWnd);
1162 Ret = 0;
1163 }
1164 else
1165 {
1166 DPRINT("Failed to create application window (LastError: %d)!\n", GetLastError());
1167 }
1168
1169 UnregisterApplicationClasses();
1170 }
1171 else
1172 {
1173 DPRINT("Failed to register application classes (LastError: %d)!\n", GetLastError());
1174 }
1175
1176 if (lpAppTitle != NULL)
1177 {
1178 LocalFree(lpAppTitle);
1179 }
1180
1181 CloseAppConfig();
1182 }
1183 else
1184 {
1185 DPRINT("Unable to open the Volume Control registry key!\n");
1186 }
1187
1188 return Ret;
1189 }
1190