[RAPPS] Bulk install!
[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 /*
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
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
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
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
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
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
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);
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
597 static VOID
598 DeleteMixerWindowControls(PMIXER_WINDOW MixerWindow)
599 {
600 DWORD Index;
601
602 for(Index = 0; Index < MixerWindow->WindowCount; Index++)
603 {
604 /* destroys the window */
605 DestroyWindow(MixerWindow->Window[Index]);
606 }
607
608 /* free memory */
609 HeapFree(GetProcessHeap(), 0, MixerWindow->Window);
610
611 /* set to null */
612 MixerWindow->Window = NULL;
613 MixerWindow->WindowCount = 0;
614 }
615
616 static BOOL
617 RebuildMixerWindowControls(PPREFERENCES_CONTEXT PrefContext)
618 {
619 /* delete existing mixer controls */
620 DeleteMixerWindowControls(PrefContext->MixerWindow);
621
622 /* load new mixer controls */
623 LoadDialogCtrls(PrefContext);
624
625 return TRUE;
626 }
627
628 static
629 BOOL
630 CALLBACK
631 SetVolumeCallback(PSND_MIXER Mixer, DWORD LineID, LPMIXERLINE Line, PVOID Ctx)
632 {
633 UINT ControlCount = 0, Index;
634 LPMIXERCONTROL Control = NULL;
635 MIXERCONTROLDETAILS_UNSIGNED uDetails;
636 MIXERCONTROLDETAILS_BOOLEAN bDetails;
637 PSET_VOLUME_CONTEXT Context = (PSET_VOLUME_CONTEXT)Ctx;
638
639 /* check if the line name is equal */
640 if (wcsicmp(Line->szName, Context->LineName))
641 {
642 /* it is not */
643 return TRUE;
644 }
645
646 /* query controls */
647 if (SndMixerQueryControls(Mixer, &ControlCount, Line, &Control) == FALSE)
648 {
649 /* failed to query for controls */
650 return FALSE;
651 }
652
653 /* now go through all controls and compare control ids */
654 for(Index = 0; Index < ControlCount; Index++)
655 {
656 if (Context->bVertical)
657 {
658 if ((Control[Index].dwControlType & MIXERCONTROL_CT_CLASS_MASK) == MIXERCONTROL_CT_CLASS_FADER)
659 {
660 /* FIXME: give me granularity */
661 DWORD Step = 0x10000 / 5;
662
663 /* set up details */
664 uDetails.dwValue = 0x10000 - Step * Context->SliderPos;
665
666 /* set volume */
667 SndMixerSetVolumeControlDetails(Preferences.MixerWindow->Mixer, Control[Index].dwControlID, sizeof(MIXERCONTROLDETAILS_UNSIGNED), (LPVOID)&uDetails);
668
669 /* done */
670 break;
671 }
672 }
673 else if (Context->bSwitch)
674 {
675 if ((Control[Index].dwControlType & MIXERCONTROL_CT_CLASS_MASK) == MIXERCONTROL_CT_CLASS_SWITCH)
676 {
677 /* set up details */
678 bDetails.fValue = Context->SliderPos;
679
680 /* set volume */
681 SndMixerSetVolumeControlDetails(Preferences.MixerWindow->Mixer, Control[Index].dwControlID, sizeof(MIXERCONTROLDETAILS_BOOLEAN), (LPVOID)&bDetails);
682
683 /* done */
684 break;
685 }
686 }
687 else
688 {
689 /* FIXME: implement left - right channel switch support */
690 assert(0);
691 }
692 }
693
694 /* free controls */
695 HeapFree(GetProcessHeap(), 0, Control);
696
697
698 /* done */
699 return TRUE;
700 }
701
702 static
703 BOOL
704 CALLBACK
705 MixerControlChangeCallback(PSND_MIXER Mixer, DWORD LineID, LPMIXERLINE Line, PVOID Context)
706 {
707 UINT ControlCount = 0, Index;
708 LPMIXERCONTROL Control = NULL;
709
710 /* check if the line has controls */
711 if (Line->cControls == 0)
712 {
713 /* no controls */
714 return TRUE;
715 }
716
717 /* query controls */
718 if (SndMixerQueryControls(Mixer, &ControlCount, Line, &Control) == FALSE)
719 {
720 /* failed to query for controls */
721 return FALSE;
722 }
723
724 /* now go through all controls and compare control ids */
725 for(Index = 0; Index < ControlCount; Index++)
726 {
727 if (Control[Index].dwControlID == PtrToUlong(Context))
728 {
729 if ((Control[Index].dwControlType & MIXERCONTROL_CT_CLASS_MASK) == MIXERCONTROL_CT_CLASS_SWITCH)
730 {
731 MIXERCONTROLDETAILS_BOOLEAN Details;
732
733 /* get volume control details */
734 if (SndMixerGetVolumeControlDetails(Preferences.MixerWindow->Mixer, Control[Index].dwControlID, sizeof(MIXERCONTROLDETAILS_BOOLEAN), (LPVOID)&Details) != -1)
735 {
736 /* update dialog control */
737 UpdateDialogLineSwitchControl(&Preferences, Line, Details.fValue);
738 }
739 }
740 else if ((Control[Index].dwControlType & MIXERCONTROL_CT_CLASS_MASK) == MIXERCONTROL_CT_CLASS_FADER)
741 {
742 MIXERCONTROLDETAILS_UNSIGNED Details;
743
744 /* get volume control details */
745 if (SndMixerGetVolumeControlDetails(Preferences.MixerWindow->Mixer, Control[Index].dwControlID, sizeof(MIXERCONTROLDETAILS_UNSIGNED), (LPVOID)&Details) != -1)
746 {
747 /* update dialog control */
748 DWORD Position;
749 DWORD Step = 0x10000 / 5;
750
751 /* FIXME: give me granularity */
752 Position = 5 - (Details.dwValue / Step);
753
754 /* update volume control slider */
755 UpdateDialogLineSliderControl(&Preferences, Line, Control[Index].dwControlID, IDC_LINE_SLIDER_VERT, Position);
756 }
757 }
758 break;
759 }
760 }
761
762 /* free controls */
763 HeapFree(GetProcessHeap(), 0, Control);
764
765
766 /* done */
767 return TRUE;
768 }
769
770
771 static LRESULT CALLBACK
772 MainWindowProc(HWND hwnd,
773 UINT uMsg,
774 WPARAM wParam,
775 LPARAM lParam)
776 {
777 PMIXER_WINDOW MixerWindow;
778 DWORD CtrlID, LineOffset;
779 LRESULT Result = 0;
780 SET_VOLUME_CONTEXT Context;
781
782 switch (uMsg)
783 {
784 case WM_COMMAND:
785 {
786 MixerWindow = GetWindowData(hwnd,
787 MIXER_WINDOW);
788
789 switch (LOWORD(wParam))
790 {
791 case IDC_PROPERTIES:
792 {
793 PREFERENCES_CONTEXT Pref;
794
795 Pref.MixerWindow = MixerWindow;
796 Pref.Mixer = NULL;
797 Pref.SelectedLine = Preferences.SelectedLine;
798
799 if (DialogBoxParam(hAppInstance,
800 MAKEINTRESOURCE(IDD_PREFERENCES),
801 hwnd,
802 DlgPreferencesProc,
803 (LPARAM)&Pref) == IDOK)
804 {
805 /* update window */
806 TCHAR szProduct[MAXPNAMELEN];
807
808 /* get mixer product name */
809 if (SndMixerGetProductName(MixerWindow->Mixer,
810 szProduct,
811 sizeof(szProduct) / sizeof(szProduct[0])) == -1)
812 {
813 /* failed to get name */
814 szProduct[0] = L'\0';
815 }
816 else
817 {
818 /* copy product */
819 wcscpy(Preferences.DeviceName, szProduct);
820 }
821
822 /* destroy old status bar */
823 DestroyWindow(MixerWindow->hStatusBar);
824
825 /* update details */
826 Preferences.SelectedLine = Pref.SelectedLine;
827
828 /* destroy old mixer */
829 SndMixerDestroy(Preferences.MixerWindow->Mixer);
830
831 /* use new selected mixer */
832 Preferences.MixerWindow->Mixer = Pref.Mixer;
833
834 /* rebuild dialog controls */
835 RebuildMixerWindowControls(&Preferences);
836
837 /* create status window */
838 MixerWindow->hStatusBar = CreateStatusWindow(WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS,
839 NULL,
840 hwnd,
841 0);
842
843 /* set status bar */
844 if (MixerWindow->hStatusBar)
845 {
846 SendMessage(MixerWindow->hStatusBar,
847 WM_SETTEXT,
848 0,
849 (LPARAM)szProduct);
850 }
851 }
852 break;
853 }
854
855 case IDC_EXIT:
856 {
857 PostQuitMessage(0);
858 break;
859 }
860
861 case IDC_ABOUT:
862 {
863 HICON hAppIcon = (HICON)GetClassLongPtrW(hwnd,
864 GCLP_HICON);
865 ShellAbout(hwnd,
866 lpAppTitle,
867 NULL,
868 hAppIcon);
869 break;
870 }
871
872 default:
873 {
874 /* get button id */
875 CtrlID = LOWORD(wParam);
876
877 /* check if the message is from the line switch */
878 if (HIWORD(wParam) == BN_CLICKED && (CtrlID % IDC_LINE_SWITCH == 0))
879 {
880 /* compute line offset */
881 LineOffset = CtrlID / IDC_LINE_SWITCH;
882
883 /* compute window id of line name static control */
884 CtrlID = LineOffset * IDC_LINE_NAME;
885
886 /* get line name */
887 if (GetDlgItemTextW(hwnd, CtrlID, Context.LineName, MIXER_LONG_NAME_CHARS) != 0)
888 {
889 /* setup context */
890 Context.SliderPos = SendMessage((HWND)lParam, BM_GETCHECK, 0, 0);
891 Context.bVertical = FALSE;
892 Context.bSwitch = TRUE;
893
894 /* set volume */
895 SndMixerEnumConnections(Preferences.MixerWindow->Mixer, Preferences.SelectedLine, SetVolumeCallback, (LPVOID)&Context);
896 }
897 }
898 }
899
900 }
901 break;
902 }
903
904 case MM_MIXM_LINE_CHANGE:
905 {
906 DPRINT("MM_MIXM_LINE_CHANGE\n");
907 break;
908 }
909
910 case MM_MIXM_CONTROL_CHANGE:
911 {
912 DPRINT("MM_MIXM_CONTROL_CHANGE\n");
913
914 /* get mixer window */
915 MixerWindow = GetWindowData(hwnd,
916 MIXER_WINDOW);
917
918 /* sanity checks */
919 assert(MixerWindow);
920 assert(MixerWindow->Mixer->hmx == (HMIXER)wParam);
921
922 SndMixerEnumConnections(MixerWindow->Mixer, Preferences.SelectedLine, MixerControlChangeCallback, (PVOID)lParam);
923 break;
924 }
925
926 case WM_VSCROLL:
927 {
928 if (LOWORD(wParam) == TB_THUMBTRACK)
929 {
930 /* get dialog item ctrl */
931 CtrlID = GetDlgCtrlID((HWND)lParam);
932
933 /* get line index */
934 LineOffset = CtrlID / IDC_LINE_SLIDER_VERT;
935
936 /* compute window id of line name static control */
937 CtrlID = LineOffset * IDC_LINE_NAME;
938
939 /* get line name */
940 if (GetDlgItemTextW(hwnd, CtrlID, Context.LineName, MIXER_LONG_NAME_CHARS) != 0)
941 {
942 /* setup context */
943 Context.SliderPos = HIWORD(wParam);
944 Context.bVertical = TRUE;
945 Context.bSwitch = FALSE;
946
947 /* set volume */
948 SndMixerEnumConnections(Preferences.MixerWindow->Mixer, Preferences.SelectedLine, SetVolumeCallback, (LPVOID)&Context);
949 }
950 }
951
952 break;
953 }
954
955
956 case WM_CREATE:
957 {
958 MixerWindow = ((LPCREATESTRUCT)lParam)->lpCreateParams;
959 SetWindowLongPtr(hwnd,
960 GWL_USERDATA,
961 (LONG_PTR)MixerWindow);
962 MixerWindow->hWnd = hwnd;
963 MixerWindow->Mixer = SndMixerCreate(MixerWindow->hWnd);
964 if (MixerWindow->Mixer != NULL)
965 {
966 TCHAR szProduct[MAXPNAMELEN];
967
968 /* get mixer product name */
969 if (SndMixerGetProductName(MixerWindow->Mixer,
970 szProduct,
971 sizeof(szProduct) / sizeof(szProduct[0])) == -1)
972 {
973 /* failed to get name */
974 szProduct[0] = L'\0';
975 }
976
977
978 /* initialize preferences */
979 ZeroMemory(&Preferences, sizeof(Preferences));
980
981 /* store mixer */
982 Preferences.Mixer = MixerWindow->Mixer;
983
984 /* store mixer window */
985 Preferences.MixerWindow = MixerWindow;
986
987 /* first destination line id */
988 Preferences.SelectedLine = 0xFFFF0000;
989
990 /* copy product */
991 wcscpy(Preferences.DeviceName, szProduct);
992
993 if (!RebuildMixerWindowControls(&Preferences))
994 {
995 DPRINT("Rebuilding mixer window controls failed!\n");
996 SndMixerDestroy(MixerWindow->Mixer);
997 MixerWindow->Mixer = NULL;
998 Result = -1;
999 }
1000
1001 /* create status window */
1002 MixerWindow->hStatusBar = CreateStatusWindow(WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS,
1003 NULL,
1004 hwnd,
1005 0);
1006 if (MixerWindow->hStatusBar)
1007 {
1008 SendMessage(MixerWindow->hStatusBar,
1009 WM_SETTEXT,
1010 0,
1011 (LPARAM)szProduct);
1012 }
1013 }
1014 break;
1015 }
1016
1017 case WM_DESTROY:
1018 {
1019 MixerWindow = GetWindowData(hwnd,
1020 MIXER_WINDOW);
1021 if (MixerWindow != NULL)
1022 {
1023 if (MixerWindow->Mixer != NULL)
1024 {
1025 SndMixerDestroy(MixerWindow->Mixer);
1026 }
1027 HeapFree(hAppHeap, 0, MixerWindow);
1028 }
1029 break;
1030 }
1031
1032 case WM_CLOSE:
1033 {
1034 PostQuitMessage(0);
1035 break;
1036 }
1037
1038 default:
1039 {
1040 Result = DefWindowProc(hwnd,
1041 uMsg,
1042 wParam,
1043 lParam);
1044 break;
1045 }
1046 }
1047
1048 return Result;
1049 }
1050
1051 static BOOL
1052 RegisterApplicationClasses(VOID)
1053 {
1054 WNDCLASSEX wc;
1055
1056 wc.cbSize = sizeof(WNDCLASSEX);
1057 wc.style = CS_HREDRAW | CS_VREDRAW;
1058 wc.lpfnWndProc = MainWindowProc;
1059 wc.cbClsExtra = 0;
1060 wc.cbWndExtra = sizeof(PMIXER_WINDOW);
1061 wc.hInstance = hAppInstance;
1062 wc.hIcon = LoadIcon(hAppInstance,
1063 MAKEINTRESOURCE(IDI_MAINAPP));
1064 wc.hCursor = LoadCursor(NULL,
1065 IDC_ARROW);
1066 wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
1067 wc.lpszMenuName = NULL;
1068 wc.lpszClassName = SZ_APP_CLASS;
1069 wc.hIconSm = NULL;
1070 MainWindowClass = RegisterClassEx(&wc);
1071
1072 return MainWindowClass != 0;
1073 }
1074
1075 static VOID
1076 UnregisterApplicationClasses(VOID)
1077 {
1078 UnregisterClass(SZ_APP_CLASS,
1079 hAppInstance);
1080 }
1081
1082 static HWND
1083 CreateApplicationWindow(VOID)
1084 {
1085 HWND hWnd;
1086
1087 PMIXER_WINDOW MixerWindow = HeapAlloc(hAppHeap,
1088 HEAP_ZERO_MEMORY,
1089 sizeof(MIXER_WINDOW));
1090 if (MixerWindow == NULL)
1091 {
1092 return NULL;
1093 }
1094
1095 if (mixerGetNumDevs() > 0)
1096 {
1097 hWnd = CreateWindowEx(WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT,
1098 SZ_APP_CLASS,
1099 lpAppTitle,
1100 WS_DLGFRAME | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE,
1101 0, 0, 300, 315,
1102 NULL,
1103 LoadMenu(hAppInstance,
1104 MAKEINTRESOURCE(IDM_MAINMENU)),
1105 hAppInstance,
1106 MixerWindow);
1107 }
1108 else
1109 {
1110 LPTSTR lpErrMessage;
1111
1112 /*
1113 * no mixer devices are available!
1114 */
1115
1116 hWnd = NULL;
1117 if (AllocAndLoadString(&lpErrMessage,
1118 hAppInstance,
1119 IDS_NOMIXERDEVICES))
1120 {
1121 MessageBox(NULL,
1122 lpErrMessage,
1123 lpAppTitle,
1124 MB_ICONINFORMATION);
1125 LocalFree(lpErrMessage);
1126 }
1127 }
1128
1129 if (hWnd == NULL)
1130 {
1131 HeapFree(hAppHeap,
1132 0,
1133 MixerWindow);
1134 }
1135
1136 return hWnd;
1137 }
1138
1139 int WINAPI
1140 _tWinMain(HINSTANCE hInstance,
1141 HINSTANCE hPrevInstance,
1142 LPTSTR lpszCmdLine,
1143 int nCmdShow)
1144 {
1145 MSG Msg;
1146 int Ret = 1;
1147 INITCOMMONCONTROLSEX Controls;
1148
1149 UNREFERENCED_PARAMETER(hPrevInstance);
1150 UNREFERENCED_PARAMETER(lpszCmdLine);
1151 UNREFERENCED_PARAMETER(nCmdShow);
1152
1153 hAppInstance = hInstance;
1154 hAppHeap = GetProcessHeap();
1155
1156 if (InitAppConfig())
1157 {
1158 /* load the application title */
1159 if (!AllocAndLoadString(&lpAppTitle,
1160 hAppInstance,
1161 IDS_SNDVOL32))
1162 {
1163 lpAppTitle = NULL;
1164 }
1165
1166 Controls.dwSize = sizeof(INITCOMMONCONTROLSEX);
1167 Controls.dwICC = ICC_BAR_CLASSES | ICC_STANDARD_CLASSES;
1168
1169 InitCommonControlsEx(&Controls);
1170
1171 if (RegisterApplicationClasses())
1172 {
1173 hMainWnd = CreateApplicationWindow();
1174 if (hMainWnd != NULL)
1175 {
1176 BOOL bRet;
1177 while ((bRet =GetMessage(&Msg,
1178 NULL,
1179 0,
1180 0)) != 0)
1181 {
1182 if (bRet != -1)
1183 {
1184 TranslateMessage(&Msg);
1185 DispatchMessage(&Msg);
1186 }
1187 }
1188
1189 DestroyWindow(hMainWnd);
1190 Ret = 0;
1191 }
1192 else
1193 {
1194 DPRINT("Failed to create application window (LastError: %d)!\n", GetLastError());
1195 }
1196
1197 UnregisterApplicationClasses();
1198 }
1199 else
1200 {
1201 DPRINT("Failed to register application classes (LastError: %d)!\n", GetLastError());
1202 }
1203
1204 if (lpAppTitle != NULL)
1205 {
1206 LocalFree(lpAppTitle);
1207 }
1208
1209 CloseAppConfig();
1210 }
1211 else
1212 {
1213 DPRINT("Unable to open the Volume Control registry key!\n");
1214 }
1215
1216 return Ret;
1217 }