[CMAKE]
[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 /* $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 TRUE) == -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(PPREFERENCES_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 PSNDVOL_REG_LINESTATE LineStates;
351
352 /* get list view */
353 hwndControls = GetDlgItem(hwndDlg, IDC_CONTROLS);
354
355 /* get list item count */
356 Count = ListView_GetItemCount(hwndControls);
357
358 /* sanity check */
359 assert(Count);
360
361 if (SndMixerGetLineName(Context->Mixer, Context->SelectedLine, DestinationName, MIXER_LONG_NAME_CHARS, TRUE) == -1)
362 {
363 /* failed to get destination line name */
364 return;
365 }
366
367 /* allocate line states array */
368 LineStates = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SNDVOL_REG_LINESTATE) * Count);
369 if (LineStates == NULL)
370 {
371 /* failed to allocate line states array */
372 return;
373 }
374
375
376 for(Index = 0; Index < Count; Index++)
377 {
378 /* set to empty */
379 LineName[0] = L'\0';
380
381 /* get item text */
382 ListView_GetItemText(hwndControls, Index, 0, LineName, MIXER_LONG_NAME_CHARS);
383
384 /* make sure it is null terminated */
385 LineName[MIXER_LONG_NAME_CHARS-1] = L'\0';
386
387 /* get check state */
388 Flags = (ListView_GetCheckState(hwndControls, Index) == 0 ? 0x4 : 0);
389
390 /* copy line name */
391 wcscpy(LineStates[Index].LineName, LineName);
392
393 /* store flags */
394 LineStates[Index].Flags = Flags;
395 }
396
397 /* now write the line config */
398 WriteLineConfig(Context->DeviceName, DestinationName, LineStates, sizeof(SNDVOL_REG_LINESTATE) * Count);
399
400 /* free line states */
401 HeapFree(GetProcessHeap(), 0, LineStates);
402 }
403
404 static INT_PTR CALLBACK
405 DlgPreferencesProc(HWND hwndDlg,
406 UINT uMsg,
407 WPARAM wParam,
408 LPARAM lParam)
409 {
410 PPREFERENCES_CONTEXT Context;
411
412 switch (uMsg)
413 {
414 case WM_COMMAND:
415 {
416 Context = GetDialogData(hwndDlg,
417 PREFERENCES_CONTEXT);
418 switch (LOWORD(wParam))
419 {
420 case IDC_MIXERDEVICE:
421 {
422 if (HIWORD(wParam) == CBN_SELCHANGE)
423 {
424 UpdatePrefDlgControls(Context,
425 (DWORD)-1);
426 }
427 break;
428 }
429
430 case IDC_LINE:
431 {
432 if (HIWORD(wParam) == CBN_SELCHANGE)
433 {
434 INT LineID;
435 INT Index;
436
437 Index = SendDlgItemMessage(hwndDlg,
438 IDC_LINE,
439 CB_GETCURSEL,
440 0,
441 0);
442 if (Index != CB_ERR)
443 {
444 LineID = SendDlgItemMessage(hwndDlg,
445 IDC_LINE,
446 CB_GETITEMDATA,
447 Index,
448 0);
449 if (LineID != CB_ERR)
450 {
451 UpdatePrefDlgControls(Context,
452 LineID);
453 }
454 }
455 }
456 break;
457 }
458
459 case IDC_PLAYBACK:
460 {
461 UpdatePrefDlgControls(Context,
462 Context->PlaybackID);
463 EnableWindow(GetDlgItem(hwndDlg,
464 IDC_LINE),
465 FALSE);
466 break;
467 }
468
469 case IDC_RECORDING:
470 {
471 UpdatePrefDlgControls(Context,
472 Context->RecordingID);
473 EnableWindow(GetDlgItem(hwndDlg,
474 IDC_LINE),
475 FALSE);
476 break;
477 }
478
479 case IDC_OTHER:
480 {
481 INT LineCbIndex;
482 INT LineID;
483
484 EnableWindow(GetDlgItem(hwndDlg,
485 IDC_LINE),
486 TRUE);
487
488 LineCbIndex = SendDlgItemMessage(hwndDlg,
489 IDC_LINE,
490 CB_GETCURSEL,
491 0,
492 0);
493 if (LineCbIndex != CB_ERR)
494 {
495 LineID = SendDlgItemMessage(hwndDlg,
496 IDC_LINE,
497 CB_GETITEMDATA,
498 LineCbIndex,
499 0);
500 if (LineID != CB_ERR)
501 {
502 UpdatePrefDlgControls(Context,
503 LineID);
504 }
505 }
506 break;
507 }
508
509 case IDOK:
510 {
511 /* write line settings */
512 WriteLineSettings(Context, hwndDlg);
513
514 /* fall through */
515 }
516 case IDCANCEL:
517 {
518 EndDialog(hwndDlg,
519 LOWORD(wParam));
520 break;
521 }
522 }
523 break;
524 }
525
526 case WM_INITDIALOG:
527 {
528 PREFERENCES_FILL_DEVICES FillDevContext;
529 LVCOLUMN lvc;
530 RECT rcClient;
531 HWND hwndControls;
532
533 SetWindowLongPtr(hwndDlg,
534 DWLP_USER,
535 (LONG_PTR)lParam);
536 Context = (PPREFERENCES_CONTEXT)((LONG_PTR)lParam);
537 Context->hwndDlg = hwndDlg;
538 Context->Mixer = SndMixerCreate(hwndDlg);
539 Context->Selected = (UINT)-1;
540
541 FillDevContext.PrefContext = Context;
542 FillDevContext.hComboBox = GetDlgItem(hwndDlg,
543 IDC_MIXERDEVICE);
544 FillDevContext.Selected = SndMixerGetSelection(Context->Mixer);
545 SndMixerEnumProducts(Context->Mixer,
546 FillDeviceComboBox,
547 &FillDevContext);
548
549 /* initialize the list view control */
550 hwndControls = GetDlgItem(hwndDlg,
551 IDC_CONTROLS);
552 (void)ListView_SetExtendedListViewStyle(hwndControls,
553 LVS_EX_CHECKBOXES);
554
555 GetClientRect(hwndControls,
556 &rcClient);
557 lvc.mask = LVCF_TEXT | LVCF_WIDTH;
558 lvc.pszText = TEXT("");
559 lvc.cx = rcClient.right;
560 SendMessage(hwndControls,
561 LVM_INSERTCOLUMN,
562 0,
563 (LPARAM)&lvc);
564
565 /* update all controls */
566 UpdatePrefDlgControls(Context,
567 (DWORD)Context->SelectedLine);
568 return TRUE;
569 }
570
571 case WM_CLOSE:
572 {
573 EndDialog(hwndDlg,
574 IDCANCEL);
575 break;
576 }
577
578 case WM_SYSCOLORCHANGE:
579 {
580 HWND hwndControls;
581
582 /* Forward WM_SYSCOLORCHANGE */
583 hwndControls = GetDlgItem(hwndDlg, IDC_CONTROLS);
584 SendMessage(hwndControls, WM_SYSCOLORCHANGE, 0, 0);
585 break;
586 }
587 }
588
589 return 0;
590 }
591
592
593 /******************************************************************************/
594
595 static VOID
596 DeleteMixerWindowControls(PMIXER_WINDOW MixerWindow)
597 {
598 DWORD Index;
599
600 for(Index = 0; Index < MixerWindow->WindowCount; Index++)
601 {
602 /* destroys the window */
603 DestroyWindow(MixerWindow->Window[Index]);
604 }
605
606 /* free memory */
607 HeapFree(GetProcessHeap(), 0, MixerWindow->Window);
608
609 /* set to null */
610 MixerWindow->Window = NULL;
611 MixerWindow->WindowCount = 0;
612 }
613
614 static BOOL
615 RebuildMixerWindowControls(PPREFERENCES_CONTEXT PrefContext)
616 {
617 /* delete existing mixer controls */
618 DeleteMixerWindowControls(PrefContext->MixerWindow);
619
620 /* load new mixer controls */
621 LoadDialogCtrls(PrefContext);
622
623 return TRUE;
624 }
625
626 static
627 BOOL
628 CALLBACK
629 SetVolumeCallback(PSND_MIXER Mixer, DWORD LineID, LPMIXERLINE Line, PVOID Ctx)
630 {
631 UINT ControlCount = 0, Index;
632 LPMIXERCONTROL Control = NULL;
633 MIXERCONTROLDETAILS_UNSIGNED uDetails;
634 MIXERCONTROLDETAILS_BOOLEAN bDetails;
635 PSET_VOLUME_CONTEXT Context = (PSET_VOLUME_CONTEXT)Ctx;
636
637 /* check if the line name is equal */
638 if (wcsicmp(Line->szName, Context->LineName))
639 {
640 /* it is not */
641 return TRUE;
642 }
643
644 /* query controls */
645 if (SndMixerQueryControls(Mixer, &ControlCount, Line, &Control) == FALSE)
646 {
647 /* failed to query for controls */
648 return FALSE;
649 }
650
651 /* now go through all controls and compare control ids */
652 for(Index = 0; Index < ControlCount; Index++)
653 {
654 if (Context->bVertical)
655 {
656 if ((Control[Index].dwControlType & MIXERCONTROL_CT_CLASS_MASK) == MIXERCONTROL_CT_CLASS_FADER)
657 {
658 /* FIXME: give me granularity */
659 DWORD Step = 0x10000 / 5;
660
661 /* set up details */
662 uDetails.dwValue = 0x10000 - Step * Context->SliderPos;
663
664 /* set volume */
665 SndMixerSetVolumeControlDetails(Preferences.MixerWindow->Mixer, Control[Index].dwControlID, sizeof(MIXERCONTROLDETAILS_UNSIGNED), (LPVOID)&uDetails);
666
667 /* done */
668 break;
669 }
670 }
671 else if (Context->bSwitch)
672 {
673 if ((Control[Index].dwControlType & MIXERCONTROL_CT_CLASS_MASK) == MIXERCONTROL_CT_CLASS_SWITCH)
674 {
675 /* set up details */
676 bDetails.fValue = Context->SliderPos;
677
678 /* set volume */
679 SndMixerSetVolumeControlDetails(Preferences.MixerWindow->Mixer, Control[Index].dwControlID, sizeof(MIXERCONTROLDETAILS_BOOLEAN), (LPVOID)&bDetails);
680
681 /* done */
682 break;
683 }
684 }
685 else
686 {
687 /* FIXME: implement left - right channel switch support */
688 assert(0);
689 }
690 }
691
692 /* free controls */
693 HeapFree(GetProcessHeap(), 0, Control);
694
695
696 /* done */
697 return TRUE;
698 }
699
700 static
701 BOOL
702 CALLBACK
703 MixerControlChangeCallback(PSND_MIXER Mixer, DWORD LineID, LPMIXERLINE Line, PVOID Context)
704 {
705 UINT ControlCount = 0, Index;
706 LPMIXERCONTROL Control = NULL;
707
708 /* check if the line has controls */
709 if (Line->cControls == 0)
710 {
711 /* no controls */
712 return TRUE;
713 }
714
715 /* query controls */
716 if (SndMixerQueryControls(Mixer, &ControlCount, Line, &Control) == FALSE)
717 {
718 /* failed to query for controls */
719 return FALSE;
720 }
721
722 /* now go through all controls and compare control ids */
723 for(Index = 0; Index < ControlCount; Index++)
724 {
725 if (Control[Index].dwControlID == PtrToUlong(Context))
726 {
727 if ((Control[Index].dwControlType & MIXERCONTROL_CT_CLASS_MASK) == MIXERCONTROL_CT_CLASS_SWITCH)
728 {
729 MIXERCONTROLDETAILS_BOOLEAN Details;
730
731 /* get volume control details */
732 if (SndMixerGetVolumeControlDetails(Preferences.MixerWindow->Mixer, Control[Index].dwControlID, sizeof(MIXERCONTROLDETAILS_BOOLEAN), (LPVOID)&Details) != -1)
733 {
734 /* update dialog control */
735 UpdateDialogLineSwitchControl(&Preferences, Line, Details.fValue);
736 }
737 }
738 else if ((Control[Index].dwControlType & MIXERCONTROL_CT_CLASS_MASK) == MIXERCONTROL_CT_CLASS_FADER)
739 {
740 MIXERCONTROLDETAILS_UNSIGNED Details;
741
742 /* get volume control details */
743 if (SndMixerGetVolumeControlDetails(Preferences.MixerWindow->Mixer, Control[Index].dwControlID, sizeof(MIXERCONTROLDETAILS_UNSIGNED), (LPVOID)&Details) != -1)
744 {
745 /* update dialog control */
746 DWORD Position;
747 DWORD Step = 0x10000 / 5;
748
749 /* FIXME: give me granularity */
750 Position = 5 - (Details.dwValue / Step);
751
752 /* update volume control slider */
753 UpdateDialogLineSliderControl(&Preferences, Line, Control[Index].dwControlID, IDC_LINE_SLIDER_VERT, Position);
754 }
755 }
756 break;
757 }
758 }
759
760 /* free controls */
761 HeapFree(GetProcessHeap(), 0, Control);
762
763
764 /* done */
765 return TRUE;
766 }
767
768
769 static LRESULT CALLBACK
770 MainWindowProc(HWND hwnd,
771 UINT uMsg,
772 WPARAM wParam,
773 LPARAM lParam)
774 {
775 PMIXER_WINDOW MixerWindow;
776 DWORD CtrlID, LineOffset;
777 LRESULT Result = 0;
778 SET_VOLUME_CONTEXT Context;
779
780 switch (uMsg)
781 {
782 case WM_COMMAND:
783 {
784 MixerWindow = GetWindowData(hwnd,
785 MIXER_WINDOW);
786
787 switch (LOWORD(wParam))
788 {
789 case IDC_PROPERTIES:
790 {
791 PREFERENCES_CONTEXT Pref;
792
793 Pref.MixerWindow = MixerWindow;
794 Pref.Mixer = NULL;
795 Pref.SelectedLine = Preferences.SelectedLine;
796
797 if (DialogBoxParam(hAppInstance,
798 MAKEINTRESOURCE(IDD_PREFERENCES),
799 hwnd,
800 DlgPreferencesProc,
801 (LPARAM)&Pref) == IDOK)
802 {
803 /* update window */
804 TCHAR szProduct[MAXPNAMELEN];
805
806 /* get mixer product name */
807 if (SndMixerGetProductName(MixerWindow->Mixer,
808 szProduct,
809 sizeof(szProduct) / sizeof(szProduct[0])) == -1)
810 {
811 /* failed to get name */
812 szProduct[0] = L'\0';
813 }
814 else
815 {
816 /* copy product */
817 wcscpy(Preferences.DeviceName, szProduct);
818 }
819
820 /* destroy old status bar */
821 DestroyWindow(MixerWindow->hStatusBar);
822
823 /* update details */
824 Preferences.SelectedLine = Pref.SelectedLine;
825
826 /* destroy old mixer */
827 SndMixerDestroy(Preferences.MixerWindow->Mixer);
828
829 /* use new selected mixer */
830 Preferences.MixerWindow->Mixer = Pref.Mixer;
831
832 /* rebuild dialog controls */
833 RebuildMixerWindowControls(&Preferences);
834
835 /* create status window */
836 MixerWindow->hStatusBar = CreateStatusWindow(WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS,
837 NULL,
838 hwnd,
839 0);
840
841 /* set status bar */
842 if (MixerWindow->hStatusBar)
843 {
844 SendMessage(MixerWindow->hStatusBar,
845 WM_SETTEXT,
846 0,
847 (LPARAM)szProduct);
848 }
849 }
850 break;
851 }
852
853 case IDC_EXIT:
854 {
855 PostQuitMessage(0);
856 break;
857 }
858
859 case IDC_ABOUT:
860 {
861 HICON hAppIcon = (HICON)GetClassLongPtrW(hwnd,
862 GCLP_HICON);
863 ShellAbout(hwnd,
864 lpAppTitle,
865 NULL,
866 hAppIcon);
867 break;
868 }
869
870 default:
871 {
872 /* get button id */
873 CtrlID = LOWORD(wParam);
874
875 /* check if the message is from the line switch */
876 if (HIWORD(wParam) == BN_CLICKED && (CtrlID % IDC_LINE_SWITCH == 0))
877 {
878 /* compute line offset */
879 LineOffset = CtrlID / IDC_LINE_SWITCH;
880
881 /* compute window id of line name static control */
882 CtrlID = LineOffset * IDC_LINE_NAME;
883
884 /* get line name */
885 if (GetDlgItemTextW(hwnd, CtrlID, Context.LineName, MIXER_LONG_NAME_CHARS) != 0)
886 {
887 /* setup context */
888 Context.SliderPos = SendMessage((HWND)lParam, BM_GETCHECK, 0, 0);
889 Context.bVertical = FALSE;
890 Context.bSwitch = TRUE;
891
892 /* set volume */
893 SndMixerEnumConnections(Preferences.MixerWindow->Mixer, Preferences.SelectedLine, SetVolumeCallback, (LPVOID)&Context);
894 }
895 }
896 }
897
898 }
899 break;
900 }
901
902 case MM_MIXM_LINE_CHANGE:
903 {
904 DPRINT("MM_MIXM_LINE_CHANGE\n");
905 break;
906 }
907
908 case MM_MIXM_CONTROL_CHANGE:
909 {
910 DPRINT("MM_MIXM_CONTROL_CHANGE\n");
911
912 /* get mixer window */
913 MixerWindow = GetWindowData(hwnd,
914 MIXER_WINDOW);
915
916 /* sanity checks */
917 assert(MixerWindow);
918 assert(MixerWindow->Mixer->hmx == (HMIXER)wParam);
919
920 SndMixerEnumConnections(MixerWindow->Mixer, Preferences.SelectedLine, MixerControlChangeCallback, (PVOID)lParam);
921 break;
922 }
923
924 case WM_VSCROLL:
925 {
926 if (LOWORD(wParam) == TB_THUMBTRACK)
927 {
928 /* get dialog item ctrl */
929 CtrlID = GetDlgCtrlID((HWND)lParam);
930
931 /* get line index */
932 LineOffset = CtrlID / IDC_LINE_SLIDER_VERT;
933
934 /* compute window id of line name static control */
935 CtrlID = LineOffset * IDC_LINE_NAME;
936
937 /* get line name */
938 if (GetDlgItemTextW(hwnd, CtrlID, Context.LineName, MIXER_LONG_NAME_CHARS) != 0)
939 {
940 /* setup context */
941 Context.SliderPos = HIWORD(wParam);
942 Context.bVertical = TRUE;
943 Context.bSwitch = FALSE;
944
945 /* set volume */
946 SndMixerEnumConnections(Preferences.MixerWindow->Mixer, Preferences.SelectedLine, SetVolumeCallback, (LPVOID)&Context);
947 }
948 }
949
950 break;
951 }
952
953
954 case WM_CREATE:
955 {
956 MixerWindow = ((LPCREATESTRUCT)lParam)->lpCreateParams;
957 SetWindowLongPtr(hwnd,
958 GWL_USERDATA,
959 (LONG_PTR)MixerWindow);
960 MixerWindow->hWnd = hwnd;
961 MixerWindow->Mixer = SndMixerCreate(MixerWindow->hWnd);
962 if (MixerWindow->Mixer != NULL)
963 {
964 TCHAR szProduct[MAXPNAMELEN];
965
966 /* get mixer product name */
967 if (SndMixerGetProductName(MixerWindow->Mixer,
968 szProduct,
969 sizeof(szProduct) / sizeof(szProduct[0])) == -1)
970 {
971 /* failed to get name */
972 szProduct[0] = L'\0';
973 }
974
975
976 /* initialize perferences */
977 ZeroMemory(&Preferences, sizeof(Preferences));
978
979 /* store mixer */
980 Preferences.Mixer = MixerWindow->Mixer;
981
982 /* store mixer window */
983 Preferences.MixerWindow = MixerWindow;
984
985 /* first destination line id */
986 Preferences.SelectedLine = 0xFFFF0000;
987
988 /* copy product */
989 wcscpy(Preferences.DeviceName, szProduct);
990
991 if (!RebuildMixerWindowControls(&Preferences))
992 {
993 DPRINT("Rebuilding mixer window controls failed!\n");
994 SndMixerDestroy(MixerWindow->Mixer);
995 MixerWindow->Mixer = NULL;
996 Result = -1;
997 }
998
999 /* create status window */
1000 MixerWindow->hStatusBar = CreateStatusWindow(WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS,
1001 NULL,
1002 hwnd,
1003 0);
1004 if (MixerWindow->hStatusBar)
1005 {
1006 SendMessage(MixerWindow->hStatusBar,
1007 WM_SETTEXT,
1008 0,
1009 (LPARAM)szProduct);
1010 }
1011 }
1012 break;
1013 }
1014
1015 case WM_DESTROY:
1016 {
1017 MixerWindow = GetWindowData(hwnd,
1018 MIXER_WINDOW);
1019 if (MixerWindow->Mixer != NULL)
1020 {
1021 SndMixerDestroy(MixerWindow->Mixer);
1022 }
1023 break;
1024 }
1025
1026 case WM_CLOSE:
1027 {
1028 PostQuitMessage(0);
1029 break;
1030 }
1031
1032 default:
1033 {
1034 Result = DefWindowProc(hwnd,
1035 uMsg,
1036 wParam,
1037 lParam);
1038 break;
1039 }
1040 }
1041
1042 return Result;
1043 }
1044
1045 static BOOL
1046 RegisterApplicationClasses(VOID)
1047 {
1048 WNDCLASSEX wc;
1049
1050 wc.cbSize = sizeof(WNDCLASSEX);
1051 wc.style = CS_HREDRAW | CS_VREDRAW;
1052 wc.lpfnWndProc = MainWindowProc;
1053 wc.cbClsExtra = 0;
1054 wc.cbWndExtra = sizeof(PMIXER_WINDOW);
1055 wc.hInstance = hAppInstance;
1056 wc.hIcon = LoadIcon(hAppInstance,
1057 MAKEINTRESOURCE(IDI_MAINAPP));
1058 wc.hCursor = LoadCursor(NULL,
1059 IDC_ARROW);
1060 wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
1061 wc.lpszMenuName = NULL;
1062 wc.lpszClassName = SZ_APP_CLASS;
1063 wc.hIconSm = NULL;
1064 MainWindowClass = RegisterClassEx(&wc);
1065
1066 return MainWindowClass != 0;
1067 }
1068
1069 static VOID
1070 UnregisterApplicationClasses(VOID)
1071 {
1072 UnregisterClass(SZ_APP_CLASS,
1073 hAppInstance);
1074 }
1075
1076 static HWND
1077 CreateApplicationWindow(VOID)
1078 {
1079 HWND hWnd;
1080
1081 PMIXER_WINDOW MixerWindow = HeapAlloc(hAppHeap,
1082 HEAP_ZERO_MEMORY,
1083 sizeof(MIXER_WINDOW));
1084 if (MixerWindow == NULL)
1085 {
1086 return NULL;
1087 }
1088
1089 if (mixerGetNumDevs() > 0)
1090 {
1091 hWnd = CreateWindowEx(WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT,
1092 SZ_APP_CLASS,
1093 lpAppTitle,
1094 WS_DLGFRAME | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE,
1095 0, 0, 300, 315,
1096 NULL,
1097 LoadMenu(hAppInstance,
1098 MAKEINTRESOURCE(IDM_MAINMENU)),
1099 hAppInstance,
1100 MixerWindow);
1101 }
1102 else
1103 {
1104 LPTSTR lpErrMessage;
1105
1106 /*
1107 * no mixer devices are available!
1108 */
1109
1110 hWnd = NULL;
1111 if (AllocAndLoadString(&lpErrMessage,
1112 hAppInstance,
1113 IDS_NOMIXERDEVICES))
1114 {
1115 MessageBox(NULL,
1116 lpErrMessage,
1117 lpAppTitle,
1118 MB_ICONINFORMATION);
1119 LocalFree(lpErrMessage);
1120 }
1121 }
1122
1123 if (hWnd == NULL)
1124 {
1125 HeapFree(hAppHeap,
1126 0,
1127 MixerWindow);
1128 }
1129
1130 return hWnd;
1131 }
1132
1133 int WINAPI
1134 _tWinMain(HINSTANCE hInstance,
1135 HINSTANCE hPrevInstance,
1136 LPTSTR lpszCmdLine,
1137 int nCmdShow)
1138 {
1139 MSG Msg;
1140 int Ret = 1;
1141 INITCOMMONCONTROLSEX Controls;
1142
1143 UNREFERENCED_PARAMETER(hPrevInstance);
1144 UNREFERENCED_PARAMETER(lpszCmdLine);
1145 UNREFERENCED_PARAMETER(nCmdShow);
1146
1147 hAppInstance = hInstance;
1148 hAppHeap = GetProcessHeap();
1149
1150 if (InitAppConfig())
1151 {
1152 /* load the application title */
1153 if (!AllocAndLoadString(&lpAppTitle,
1154 hAppInstance,
1155 IDS_SNDVOL32))
1156 {
1157 lpAppTitle = NULL;
1158 }
1159
1160 Controls.dwSize = sizeof(INITCOMMONCONTROLSEX);
1161 Controls.dwICC = ICC_BAR_CLASSES | ICC_STANDARD_CLASSES;
1162
1163 InitCommonControlsEx(&Controls);
1164
1165 if (RegisterApplicationClasses())
1166 {
1167 hMainWnd = CreateApplicationWindow();
1168 if (hMainWnd != NULL)
1169 {
1170 BOOL bRet;
1171 while ((bRet =GetMessage(&Msg,
1172 NULL,
1173 0,
1174 0)) != 0)
1175 {
1176 if (bRet != -1)
1177 {
1178 TranslateMessage(&Msg);
1179 DispatchMessage(&Msg);
1180 }
1181 }
1182
1183 DestroyWindow(hMainWnd);
1184 Ret = 0;
1185 }
1186 else
1187 {
1188 DPRINT("Failed to create application window (LastError: %d)!\n", GetLastError());
1189 }
1190
1191 UnregisterApplicationClasses();
1192 }
1193 else
1194 {
1195 DPRINT("Failed to register application classes (LastError: %d)!\n", GetLastError());
1196 }
1197
1198 if (lpAppTitle != NULL)
1199 {
1200 LocalFree(lpAppTitle);
1201 }
1202
1203 CloseAppConfig();
1204 }
1205 else
1206 {
1207 DPRINT("Unable to open the Volume Control registry key!\n");
1208 }
1209
1210 return Ret;
1211 }
1212