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