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, Context->MixerWindow->MixerId);
541             Context->Selected = (UINT)-1;
542 
543             FillDevContext.PrefContext = Context;
544             FillDevContext.hComboBox = GetDlgItem(hwndDlg,
545                                                   IDC_MIXERDEVICE);
546             FillDevContext.Selected = SndMixerGetSelection(Context->Mixer);
547             SndMixerEnumProducts(Context->Mixer,
548                                  FillDeviceComboBox,
549                                  &FillDevContext);
550 
551             /* initialize the list view control */
552             hwndControls = GetDlgItem(hwndDlg,
553                                       IDC_CONTROLS);
554             (void)ListView_SetExtendedListViewStyle(hwndControls,
555                                                     LVS_EX_CHECKBOXES);
556 
557             GetClientRect(hwndControls,
558                           &rcClient);
559             lvc.mask = LVCF_TEXT | LVCF_WIDTH;
560             lvc.pszText = TEXT("");
561             lvc.cx = rcClient.right;
562             SendMessage(hwndControls,
563                         LVM_INSERTCOLUMN,
564                         0,
565                         (LPARAM)&lvc);
566 
567             /* update all controls */
568             UpdatePrefDlgControls(Context,
569                                   (DWORD)Context->SelectedLine);
570             return TRUE;
571         }
572 
573         case WM_CLOSE:
574         {
575             EndDialog(hwndDlg,
576                       IDCANCEL);
577             break;
578         }
579 
580         case WM_SYSCOLORCHANGE:
581         {
582             HWND hwndControls;
583 
584             /* Forward WM_SYSCOLORCHANGE */
585             hwndControls = GetDlgItem(hwndDlg, IDC_CONTROLS);
586             SendMessage(hwndControls, WM_SYSCOLORCHANGE, 0, 0);
587             break;
588         }
589     }
590 
591     return 0;
592 }
593 
594 /******************************************************************************/
595 
596 static VOID
597 DeleteMixerWindowControls(PMIXER_WINDOW MixerWindow)
598 {
599     DWORD Index;
600 
601     for(Index = 0; Index < MixerWindow->WindowCount; Index++)
602     {
603         /* destroys the window */
604         DestroyWindow(MixerWindow->Window[Index]);
605     }
606 
607     /* free memory */
608     HeapFree(GetProcessHeap(), 0, MixerWindow->Window);
609 
610     /* set to null */
611     MixerWindow->Window = NULL;
612     MixerWindow->WindowCount = 0;
613 }
614 
615 static BOOL
616 RebuildMixerWindowControls(PPREFERENCES_CONTEXT PrefContext)
617 {
618     /* delete existing mixer controls */
619     DeleteMixerWindowControls(PrefContext->MixerWindow);
620 
621     /* load new mixer controls */
622     LoadDialogCtrls(PrefContext);
623 
624     return TRUE;
625 }
626 
627 static
628 BOOL
629 CALLBACK
630 SetVolumeCallback(PSND_MIXER Mixer, DWORD LineID, LPMIXERLINE Line, PVOID Ctx)
631 {
632     UINT ControlCount = 0, Index;
633     LPMIXERCONTROL Control = NULL;
634     PMIXERCONTROLDETAILS_UNSIGNED puDetails = NULL;
635     MIXERCONTROLDETAILS_BOOLEAN bDetails;
636     PSET_VOLUME_CONTEXT Context = (PSET_VOLUME_CONTEXT)Ctx;
637 
638     /* check if the line name is equal */
639     if (wcsicmp(Line->szName, Context->LineName))
640     {
641         /* it is not */
642         return TRUE;
643     }
644 
645     /* query controls */
646     if (SndMixerQueryControls(Mixer, &ControlCount, Line, &Control) == FALSE)
647     {
648         /* failed to query for controls */
649         return FALSE;
650     }
651 
652     puDetails = HeapAlloc(GetProcessHeap(), 0, Line->cChannels * sizeof(MIXERCONTROLDETAILS_UNSIGNED));
653     if (puDetails == NULL)
654         return FALSE;
655 
656     /* now go through all controls and compare control ids */
657     for (Index = 0; Index < ControlCount; Index++)
658     {
659         if (Context->bVertical)
660         {
661             if (Control[Index].dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
662             {
663                 DWORD LineOffset, volumePosition, balancePosition;
664                 DWORD volumeStep, balanceStep;
665 
666                 LineOffset = Context->SliderPos;
667 
668                 volumePosition = (DWORD)SendDlgItemMessage(Preferences.MixerWindow->hWnd, LineOffset * IDC_LINE_SLIDER_VERT, TBM_GETPOS, 0, 0);
669                 volumeStep = (Control[Index].Bounds.dwMaximum - Control[Index].Bounds.dwMinimum) / (VOLUME_MAX - VOLUME_MIN);
670 
671                 if (Line->cChannels == 1)
672                 {
673                     /* set up details */
674                     puDetails[0].dwValue = ((VOLUME_MAX - volumePosition) * volumeStep) + Control[Index].Bounds.dwMinimum;
675                 }
676                 else if (Line->cChannels == 2)
677                 {
678                     balancePosition = (DWORD)SendDlgItemMessage(Preferences.MixerWindow->hWnd, LineOffset * IDC_LINE_SLIDER_HORZ, TBM_GETPOS, 0, 0);
679                     if (balancePosition == BALANCE_CENTER)
680                     {
681                         puDetails[0].dwValue = ((VOLUME_MAX - volumePosition) * volumeStep) + Control[Index].Bounds.dwMinimum;
682                         puDetails[1].dwValue = ((VOLUME_MAX - volumePosition) * volumeStep) + Control[Index].Bounds.dwMinimum;
683                     }
684                     else if (balancePosition == BALANCE_LEFT)
685                     {
686                         puDetails[0].dwValue = ((VOLUME_MAX - volumePosition) * volumeStep) + Control[Index].Bounds.dwMinimum;
687                         puDetails[1].dwValue = Control[Index].Bounds.dwMinimum;
688                     }
689                     else if (balancePosition == BALANCE_RIGHT)
690                     {
691                         puDetails[0].dwValue = Control[Index].Bounds.dwMinimum;
692                         puDetails[1].dwValue = ((VOLUME_MAX - volumePosition) * volumeStep) + Control[Index].Bounds.dwMinimum;
693                     }
694                     else if (balancePosition < BALANCE_CENTER) // Left
695                     {
696                         puDetails[0].dwValue = ((VOLUME_MAX - volumePosition) * volumeStep) + Control[Index].Bounds.dwMinimum;
697 
698                         balanceStep = (puDetails[0].dwValue - Control[Index].Bounds.dwMinimum) / (BALANCE_STEPS / 2);
699 
700                         puDetails[1].dwValue = (balancePosition * balanceStep) + Control[Index].Bounds.dwMinimum;
701                     }
702                     else if (balancePosition > BALANCE_CENTER) // Right
703                     {
704                         puDetails[1].dwValue = ((VOLUME_MAX - volumePosition) * volumeStep) + Control[Index].Bounds.dwMinimum;
705 
706                         balanceStep = (puDetails[1].dwValue - Control[Index].Bounds.dwMinimum) / (BALANCE_STEPS / 2);
707 
708                         puDetails[0].dwValue = ((BALANCE_RIGHT - balancePosition) * balanceStep) + Control[Index].Bounds.dwMinimum;
709                     }
710                 }
711                 else
712                 {
713                     SndMixerGetVolumeControlDetails(Preferences.MixerWindow->Mixer, Control[Index].dwControlID, Line->cChannels, sizeof(MIXERCONTROLDETAILS_UNSIGNED), (LPVOID)puDetails);
714 
715                     /* FIXME */
716                 }
717 
718                 /* set volume */
719                 SndMixerSetVolumeControlDetails(Preferences.MixerWindow->Mixer, Control[Index].dwControlID, Line->cChannels, sizeof(MIXERCONTROLDETAILS_UNSIGNED), (LPVOID)puDetails);
720 
721                 /* done */
722                 break;
723             }
724         }
725         else if (Context->bSwitch)
726         {
727             if (Control[Index].dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
728             {
729                 /* set up details */
730                 bDetails.fValue = Context->SliderPos;
731 
732                 /* set volume */
733                 SndMixerSetVolumeControlDetails(Preferences.MixerWindow->Mixer, Control[Index].dwControlID, 1, sizeof(MIXERCONTROLDETAILS_BOOLEAN), (LPVOID)&bDetails);
734 
735                 /* done */
736                 break;
737             }
738         }
739     }
740 
741     if (puDetails != NULL)
742         HeapFree(GetProcessHeap(), 0, puDetails);
743 
744     /* free controls */
745     HeapFree(GetProcessHeap(), 0, Control);
746 
747 
748     /* done */
749     return TRUE;
750 }
751 
752 static
753 BOOL
754 CALLBACK
755 MixerControlChangeCallback(PSND_MIXER Mixer, DWORD LineID, LPMIXERLINE Line, PVOID Context)
756 {
757     PMIXERCONTROLDETAILS_UNSIGNED pVolumeDetails = NULL;
758     UINT ControlCount = 0, Index;
759     LPMIXERCONTROL Control = NULL;
760 
761     /* check if the line has controls */
762     if (Line->cControls == 0)
763     {
764         /* no controls */
765         return TRUE;
766     }
767 
768     /* query controls */
769     if (SndMixerQueryControls(Mixer, &ControlCount, Line, &Control) == FALSE)
770     {
771         /* failed to query for controls */
772         return FALSE;
773     }
774 
775     pVolumeDetails = HeapAlloc(GetProcessHeap(),
776                                0,
777                                Line->cChannels * sizeof(MIXERCONTROLDETAILS_UNSIGNED));
778     if (pVolumeDetails == NULL)
779         goto done;
780 
781     /* now go through all controls and compare control ids */
782     for (Index = 0; Index < ControlCount; Index++)
783     {
784         if (Control[Index].dwControlID == PtrToUlong(Context))
785         {
786             if (Control[Index].dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
787             {
788                 MIXERCONTROLDETAILS_BOOLEAN Details;
789 
790                 /* get volume control details */
791                 if (SndMixerGetVolumeControlDetails(Preferences.MixerWindow->Mixer, Control[Index].dwControlID, 1, sizeof(MIXERCONTROLDETAILS_BOOLEAN), (LPVOID)&Details) != -1)
792                 {
793                     /* update dialog control */
794                     UpdateDialogLineSwitchControl(&Preferences, Line, Details.fValue);
795                 }
796             }
797             else if (Control[Index].dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
798             {
799                 /* get volume control details */
800                 if (SndMixerGetVolumeControlDetails(Preferences.MixerWindow->Mixer, Control[Index].dwControlID, Line->cChannels, sizeof(MIXERCONTROLDETAILS_UNSIGNED), (LPVOID)pVolumeDetails) != -1)
801                 {
802                     /* update dialog control */
803                     DWORD volumePosition, volumeStep, maxVolume, i;
804                     DWORD balancePosition, balanceStep;
805 
806                     volumeStep = (Control[Index].Bounds.dwMaximum - Control[Index].Bounds.dwMinimum) / (VOLUME_MAX - VOLUME_MIN);
807 
808                     maxVolume = 0;
809                     for (i = 0; i < Line->cChannels; i++)
810                     {
811                         if (pVolumeDetails[i].dwValue > maxVolume)
812                             maxVolume = pVolumeDetails[i].dwValue;
813                     }
814 
815                     volumePosition = (maxVolume - Control[Index].Bounds.dwMinimum) / volumeStep;
816 
817                     if (Line->cChannels == 1)
818                     {
819                         balancePosition = BALANCE_CENTER;
820                     }
821                     else if (Line->cChannels == 2)
822                     {
823                         if (pVolumeDetails[0].dwValue == pVolumeDetails[1].dwValue)
824                         {
825                             balancePosition = BALANCE_CENTER;
826                         }
827                         else if (pVolumeDetails[0].dwValue == Control[Index].Bounds.dwMinimum)
828                         {
829                             balancePosition = BALANCE_RIGHT;
830                         }
831                         else if (pVolumeDetails[1].dwValue == Control[Index].Bounds.dwMinimum)
832                         {
833                             balancePosition = BALANCE_LEFT;
834                         }
835                         else
836                         {
837                             balanceStep = (maxVolume - Control[Index].Bounds.dwMinimum) / (BALANCE_STEPS / 2);
838 
839                             if (pVolumeDetails[0].dwValue < pVolumeDetails[1].dwValue)
840                             {
841                                 balancePosition = (pVolumeDetails[0].dwValue - Control[Index].Bounds.dwMinimum) / balanceStep;
842                                 balancePosition = BALANCE_RIGHT - balancePosition;
843                             }
844                             else if (pVolumeDetails[1].dwValue < pVolumeDetails[0].dwValue)
845                             {
846                                 balancePosition = (pVolumeDetails[1].dwValue - Control[Index].Bounds.dwMinimum) / balanceStep;
847                                 balancePosition = BALANCE_LEFT + balancePosition;
848                             }
849                         }
850                     }
851 
852                     /* Update the volume control slider */
853                     UpdateDialogLineSliderControl(&Preferences, Line, IDC_LINE_SLIDER_VERT, VOLUME_MAX - volumePosition);
854 
855                     /* Update the balance control slider */
856                     UpdateDialogLineSliderControl(&Preferences, Line, IDC_LINE_SLIDER_HORZ, balancePosition);
857                 }
858             }
859             break;
860         }
861     }
862 
863 done:
864     /* Free the volume details */
865     if (pVolumeDetails)
866         HeapFree(GetProcessHeap(), 0, pVolumeDetails);
867 
868     /* free controls */
869     HeapFree(GetProcessHeap(), 0, Control);
870 
871     /* done */
872     return TRUE;
873 }
874 
875 static LRESULT CALLBACK
876 MainWindowProc(HWND hwnd,
877                UINT uMsg,
878                WPARAM wParam,
879                LPARAM lParam)
880 {
881     PMIXER_WINDOW MixerWindow;
882     DWORD CtrlID, LineOffset;
883     BOOL bRet;
884     LRESULT Result = 0;
885     SET_VOLUME_CONTEXT Context;
886 
887     switch (uMsg)
888     {
889         case WM_COMMAND:
890         {
891             MixerWindow = GetWindowData(hwnd,
892                                         MIXER_WINDOW);
893 
894             switch (LOWORD(wParam))
895             {
896                 case IDM_PROPERTIES:
897                 {
898                     PREFERENCES_CONTEXT Pref;
899 
900                     Pref.MixerWindow = MixerWindow;
901                     Pref.Mixer = NULL;
902                     Pref.SelectedLine = Preferences.SelectedLine;
903 
904                     if (DialogBoxParam(hAppInstance,
905                                        MAKEINTRESOURCE(IDD_PREFERENCES),
906                                        hwnd,
907                                        DlgPreferencesProc,
908                                        (LPARAM)&Pref) == IDOK)
909                     {
910                         /* update window */
911                         TCHAR szProduct[MAXPNAMELEN];
912 
913                         /* get mixer product name */
914                         if (SndMixerGetProductName(Pref.Mixer,
915                                                    szProduct,
916                                                    sizeof(szProduct) / sizeof(szProduct[0])) == -1)
917                         {
918                             /* failed to get name */
919                             szProduct[0] = L'\0';
920                         }
921                         else
922                         {
923                             /* copy product */
924                             wcscpy(Preferences.DeviceName, szProduct);
925                         }
926 
927                         /* destroy old status bar */
928                         if (MixerWindow->Mode == NORMAL_MODE)
929                             DestroyWindow(MixerWindow->hStatusBar);
930 
931                         /* update details */
932                         Preferences.SelectedLine = Pref.SelectedLine;
933 
934                         /* destroy old mixer */
935                         SndMixerDestroy(Preferences.MixerWindow->Mixer);
936 
937                         /* use new selected mixer */
938                         Preferences.MixerWindow->Mixer = Pref.Mixer;
939 
940                         /* create status window */
941                         if (MixerWindow->Mode == NORMAL_MODE)
942                         {
943                             MixerWindow->hStatusBar = CreateStatusWindow(WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS,
944                                                                          NULL,
945                                                                          hwnd,
946                                                                          0);
947                             if (MixerWindow->hStatusBar)
948                             {
949                                 /* Set status bar */
950                                 SendMessage(MixerWindow->hStatusBar,
951                                     WM_SETTEXT,
952                                     0,
953                                     (LPARAM)szProduct);
954                             }
955                         }
956 
957                         /* rebuild dialog controls */
958                         RebuildMixerWindowControls(&Preferences);
959                     }
960                     break;
961                 }
962 
963                 case IDM_ADVANCED_CONTROLS:
964                     MixerWindow->bShowExtendedControls = !MixerWindow->bShowExtendedControls;
965                     CheckMenuItem(GetMenu(hwnd),
966                                   IDM_ADVANCED_CONTROLS,
967                                   MF_BYCOMMAND | (MixerWindow->bShowExtendedControls ? MF_CHECKED : MF_UNCHECKED));
968                     RebuildMixerWindowControls(&Preferences);
969                     break;
970 
971                 case IDM_EXIT:
972                 {
973                     PostQuitMessage(0);
974                     UnregisterHotKey(hwnd, HOTKEY_CTRL_S);
975                     break;
976                 }
977 
978                 case IDM_ABOUT:
979                 {
980                     HICON hAppIcon = (HICON)GetClassLongPtrW(hwnd,
981                                                              GCLP_HICON);
982                     ShellAbout(hwnd,
983                                lpAppTitle,
984                                NULL,
985                                hAppIcon);
986                     break;
987                 }
988 
989                 default:
990                 {
991                     /* get button id */
992                     CtrlID = LOWORD(wParam);
993 
994                     /* check if the message is from the line switch */
995                     if (HIWORD(wParam) == BN_CLICKED)
996                     {
997                         if (CtrlID % IDC_LINE_SWITCH == 0)
998                         {
999                             /* compute line offset */
1000                             LineOffset = CtrlID / IDC_LINE_SWITCH;
1001 
1002                             /* compute window id of line name static control */
1003                             CtrlID = LineOffset * IDC_LINE_NAME;
1004 
1005                             if (Preferences.MixerWindow->Mixer->MixerId == PLAY_MIXER)
1006                             {
1007                                 /* get line name */
1008                                 if (GetDlgItemTextW(hwnd, CtrlID, Context.LineName, MIXER_LONG_NAME_CHARS) != 0)
1009                                 {
1010                                     /* setup context */
1011                                     Context.SliderPos = SendMessage((HWND)lParam, BM_GETCHECK, 0, 0);
1012                                     Context.bVertical = FALSE;
1013                                     Context.bSwitch = TRUE;
1014 
1015                                     /* set volume */
1016                                     SndMixerEnumConnections(Preferences.MixerWindow->Mixer, Preferences.SelectedLine, SetVolumeCallback, (LPVOID)&Context);
1017                                 }
1018                             }
1019                             else if (Preferences.MixerWindow->Mixer->MixerId == RECORD_MIXER)
1020                             {
1021                                 UINT i;
1022 
1023                                 for (i = 0; i < Preferences.MixerWindow->DialogCount; i++)
1024                                 {
1025                                     SendDlgItemMessageW(hwnd, (i + 1) * IDC_LINE_SWITCH, BM_SETCHECK, (WPARAM)((i + 1) == LineOffset), 0);
1026                                 }
1027                             }
1028                         }
1029                         else if (CtrlID % IDC_LINE_ADVANCED == 0)
1030                         {
1031                             ADVANCED_CONTEXT AdvancedContext;
1032 
1033                             /* compute line offset */
1034                             LineOffset = CtrlID / IDC_LINE_ADVANCED;
1035 
1036                             /* compute window id of line name static control */
1037                             CtrlID = LineOffset * IDC_LINE_NAME;
1038 
1039                             /* get line name */
1040                             if (GetDlgItemTextW(hwnd, CtrlID, AdvancedContext.LineName, MIXER_LONG_NAME_CHARS) != 0)
1041                             {
1042                                 AdvancedContext.MixerWindow = Preferences.MixerWindow;
1043                                 AdvancedContext.Mixer = Preferences.MixerWindow->Mixer;
1044                                 AdvancedContext.Line = SndMixerGetLineByName(Preferences.MixerWindow->Mixer,
1045                                                                              Preferences.SelectedLine,
1046                                                                              AdvancedContext.LineName);
1047                                 if (AdvancedContext.Line)
1048                                 {
1049                                     DialogBoxParam(hAppInstance,
1050                                                    MAKEINTRESOURCE(IDD_ADVANCED),
1051                                                    hwnd,
1052                                                    AdvancedDlgProc,
1053                                                    (LPARAM)&AdvancedContext);
1054                                 }
1055                             }
1056                         }
1057                     }
1058                 }
1059             }
1060             break;
1061         }
1062 
1063         case MM_MIXM_LINE_CHANGE:
1064         {
1065             DPRINT("MM_MIXM_LINE_CHANGE\n");
1066             break;
1067         }
1068 
1069         case MM_MIXM_CONTROL_CHANGE:
1070         {
1071             DPRINT("MM_MIXM_CONTROL_CHANGE\n");
1072 
1073             /* get mixer window */
1074             MixerWindow = GetWindowData(hwnd,
1075                                         MIXER_WINDOW);
1076 
1077             /* sanity checks */
1078             assert(MixerWindow);
1079             assert(MixerWindow->Mixer->hmx == (HMIXER)wParam);
1080 
1081             SndMixerEnumConnections(MixerWindow->Mixer, Preferences.SelectedLine, MixerControlChangeCallback, (PVOID)lParam);
1082             break;
1083         }
1084 
1085         case WM_VSCROLL:
1086             switch (LOWORD(wParam))
1087             {
1088                 case TB_THUMBTRACK:
1089                     /* get dialog item ctrl */
1090                     CtrlID = GetDlgCtrlID((HWND)lParam);
1091 
1092                     /* get line index */
1093                     LineOffset = CtrlID / IDC_LINE_SLIDER_VERT;
1094 
1095                     /* compute window id of line name static control */
1096                     CtrlID = LineOffset * IDC_LINE_NAME;
1097 
1098                     /* get line name */
1099                     if (GetDlgItemTextW(hwnd, CtrlID, Context.LineName, MIXER_LONG_NAME_CHARS) != 0)
1100                     {
1101                         /* setup context */
1102                         Context.SliderPos = LineOffset;
1103                         Context.bVertical = TRUE;
1104                         Context.bSwitch = FALSE;
1105 
1106                         /* set volume */
1107                         SndMixerEnumConnections(Preferences.MixerWindow->Mixer, Preferences.SelectedLine, SetVolumeCallback, (LPVOID)&Context);
1108                     }
1109                     break;
1110 
1111                 case TB_ENDTRACK:
1112                     MixerWindow = GetWindowData(hwnd,
1113                                                 MIXER_WINDOW);
1114 
1115                     /* get dialog item ctrl */
1116                     CtrlID = GetDlgCtrlID((HWND)lParam);
1117 
1118                     /* get line index */
1119                     LineOffset = CtrlID / IDC_LINE_SLIDER_VERT;
1120 
1121                     if (LineOffset == 1 && MixerWindow->Mixer->MixerId == 0)
1122                         PlaySound((LPCTSTR)SND_ALIAS_SYSTEMDEFAULT, NULL, SND_ASYNC | SND_ALIAS_ID);
1123                     break;
1124 
1125                 default:
1126                     break;
1127             }
1128             break;
1129 
1130         case WM_HSCROLL:
1131             switch (LOWORD(wParam))
1132             {
1133                 case TB_THUMBTRACK:
1134                     /* get dialog item ctrl */
1135                     CtrlID = GetDlgCtrlID((HWND)lParam);
1136 
1137                     /* get line index */
1138                     LineOffset = CtrlID / IDC_LINE_SLIDER_HORZ;
1139 
1140                     /* compute window id of line name static control */
1141                     CtrlID = LineOffset * IDC_LINE_NAME;
1142 
1143                     /* get line name */
1144                     if (GetDlgItemTextW(hwnd, CtrlID, Context.LineName, MIXER_LONG_NAME_CHARS) != 0)
1145                     {
1146                         /* setup context */
1147                         Context.SliderPos = LineOffset;
1148                         Context.bVertical = TRUE;
1149                         Context.bSwitch = FALSE;
1150 
1151                         /* set volume */
1152                         SndMixerEnumConnections(Preferences.MixerWindow->Mixer, Preferences.SelectedLine, SetVolumeCallback, (LPVOID)&Context);
1153                     }
1154                     break;
1155 
1156                 case TB_ENDTRACK:
1157                     MixerWindow = GetWindowData(hwnd,
1158                                                 MIXER_WINDOW);
1159 
1160                     /* get dialog item ctrl */
1161                     CtrlID = GetDlgCtrlID((HWND)lParam);
1162 
1163                     /* get line index */
1164                     LineOffset = CtrlID / IDC_LINE_SLIDER_HORZ;
1165 
1166                     if (LineOffset == 1 && MixerWindow->Mixer->MixerId == 0)
1167                         PlaySound((LPCTSTR)SND_ALIAS_SYSTEMDEFAULT, NULL, SND_ASYNC | SND_ALIAS_ID);
1168                     break;
1169 
1170                 default:
1171                     break;
1172             }
1173             break;
1174 
1175 
1176         case WM_CREATE:
1177         {
1178             MixerWindow = ((LPCREATESTRUCT)lParam)->lpCreateParams;
1179             SetWindowLongPtr(hwnd,
1180                              GWL_USERDATA,
1181                              (LONG_PTR)MixerWindow);
1182             MixerWindow->hWnd = hwnd;
1183             MixerWindow->Mixer = SndMixerCreate(MixerWindow->hWnd, MixerWindow->MixerId);
1184             if (MixerWindow->Mixer != NULL)
1185             {
1186                 TCHAR szProduct[MAXPNAMELEN];
1187 
1188                 /* get mixer product name */
1189                 if (SndMixerGetProductName(MixerWindow->Mixer,
1190                                            szProduct,
1191                                            sizeof(szProduct) / sizeof(szProduct[0])) == -1)
1192                 {
1193                     /* failed to get name */
1194                     szProduct[0] = L'\0';
1195                 }
1196 
1197 
1198                 /* initialize preferences */
1199                 ZeroMemory(&Preferences, sizeof(Preferences));
1200 
1201                 /* store mixer */
1202                 Preferences.Mixer = MixerWindow->Mixer;
1203 
1204                 /* store mixer window */
1205                 Preferences.MixerWindow = MixerWindow;
1206 
1207                 /* first destination line id */
1208                 Preferences.SelectedLine = 0xFFFF0000;
1209 
1210                 /* copy product */
1211                 wcscpy(Preferences.DeviceName, szProduct);
1212 
1213                 /* Disable the 'Advanced Controls' menu item */
1214                 EnableMenuItem(GetMenu(hwnd), IDM_ADVANCED_CONTROLS, MF_BYCOMMAND | MF_GRAYED);
1215 
1216                 /* Load the placement coordinate data of the window */
1217                 bRet = LoadXYCoordWnd(&Preferences);
1218                 if (bRet)
1219                 {
1220                     /*
1221                      * LoadXYCoordWnd() might fail for the first time of opening the application which is normal as
1222                      * the Sound Control's applet settings haven't been saved yet to the Registry. At this point SetWindowPos()
1223                      * call is skipped.
1224                      */
1225                     SetWindowPos(hwnd, NULL, MixerWindow->WndPosX, MixerWindow->WndPosY, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
1226                 }
1227 
1228                 /* create status window */
1229                 if (MixerWindow->Mode == NORMAL_MODE)
1230                 {
1231                     MixerWindow->hStatusBar = CreateStatusWindow(WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS,
1232                                                                  NULL,
1233                                                                  hwnd,
1234                                                                  0);
1235                     if (MixerWindow->hStatusBar)
1236                     {
1237                         SendMessage(MixerWindow->hStatusBar,
1238                                     WM_SETTEXT,
1239                                     0,
1240                                    (LPARAM)szProduct);
1241                     }
1242                 }
1243 
1244                 if (!RebuildMixerWindowControls(&Preferences))
1245                 {
1246                     DPRINT("Rebuilding mixer window controls failed!\n");
1247                     SndMixerDestroy(MixerWindow->Mixer);
1248                     MixerWindow->Mixer = NULL;
1249                     Result = -1;
1250                 }
1251             }
1252 
1253             RegisterHotKey(hwnd, HOTKEY_CTRL_S, MOD_CONTROL, 'S');
1254             break;
1255         }
1256 
1257         case WM_DESTROY:
1258         {
1259             MixerWindow = GetWindowData(hwnd,
1260                                         MIXER_WINDOW);
1261             if (MixerWindow != NULL)
1262             {
1263                 if (MixerWindow->Mixer != NULL)
1264                 {
1265                     SndMixerDestroy(MixerWindow->Mixer);
1266                 }
1267                 if (MixerWindow->hFont)
1268                     DeleteObject(MixerWindow->hFont);
1269                 HeapFree(hAppHeap, 0, MixerWindow);
1270             }
1271             break;
1272         }
1273 
1274         case WM_CLOSE:
1275         {
1276             SaveXYCoordWnd(hwnd, &Preferences);
1277             PostQuitMessage(0);
1278             break;
1279         }
1280 
1281         case WM_HOTKEY:
1282         {
1283             if (wParam == HOTKEY_CTRL_S)
1284             {
1285                 Preferences.MixerWindow->Mode = (Preferences.MixerWindow->Mode == NORMAL_MODE ? SMALL_MODE : NORMAL_MODE);
1286                 RebuildMixerWindowControls(&Preferences);
1287             }
1288             break;
1289         }
1290 
1291         default:
1292         {
1293             Result = DefWindowProc(hwnd,
1294                                    uMsg,
1295                                    wParam,
1296                                    lParam);
1297             break;
1298         }
1299     }
1300 
1301     return Result;
1302 }
1303 
1304 static BOOL
1305 RegisterApplicationClasses(VOID)
1306 {
1307     WNDCLASSEX wc;
1308 
1309     wc.cbSize = sizeof(WNDCLASSEX);
1310     wc.style = CS_HREDRAW | CS_VREDRAW;
1311     wc.lpfnWndProc = MainWindowProc;
1312     wc.cbClsExtra = 0;
1313     wc.cbWndExtra = sizeof(PMIXER_WINDOW);
1314     wc.hInstance = hAppInstance;
1315     wc.hIcon = LoadIcon(hAppInstance,
1316                         MAKEINTRESOURCE(IDI_MAINAPP));
1317     wc.hCursor = LoadCursor(NULL,
1318                             IDC_ARROW);
1319     wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
1320     wc.lpszMenuName = NULL;
1321     wc.lpszClassName = SZ_APP_CLASS;
1322     wc.hIconSm = NULL;
1323     MainWindowClass = RegisterClassEx(&wc);
1324 
1325     return MainWindowClass != 0;
1326 }
1327 
1328 static VOID
1329 UnregisterApplicationClasses(VOID)
1330 {
1331     UnregisterClass(SZ_APP_CLASS,
1332                     hAppInstance);
1333 }
1334 
1335 static HWND
1336 CreateApplicationWindow(
1337     WINDOW_MODE WindowMode,
1338     UINT MixerId)
1339 {
1340     HWND hWnd;
1341 
1342     PMIXER_WINDOW MixerWindow = HeapAlloc(hAppHeap,
1343                                           HEAP_ZERO_MEMORY,
1344                                           sizeof(MIXER_WINDOW));
1345     if (MixerWindow == NULL)
1346     {
1347         return NULL;
1348     }
1349 
1350     MixerWindow->Mode = WindowMode;
1351     MixerWindow->MixerId = MixerId;
1352 
1353     if (mixerGetNumDevs() > 0)
1354     {
1355         hWnd = CreateWindowEx(WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT,
1356                               SZ_APP_CLASS,
1357                               lpAppTitle,
1358                               WS_DLGFRAME | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE,
1359                               0, 0, 300, 315,
1360                               NULL,
1361                               LoadMenu(hAppInstance,
1362                                        MAKEINTRESOURCE(IDM_MAINMENU)),
1363                               hAppInstance,
1364                               MixerWindow);
1365     }
1366     else
1367     {
1368         LPTSTR lpErrMessage;
1369 
1370         /*
1371          * no mixer devices are available!
1372          */
1373 
1374         hWnd = NULL;
1375         if (AllocAndLoadString(&lpErrMessage,
1376                                hAppInstance,
1377                                IDS_NOMIXERDEVICES))
1378         {
1379             MessageBox(NULL,
1380                        lpErrMessage,
1381                        lpAppTitle,
1382                        MB_ICONINFORMATION);
1383             LocalFree(lpErrMessage);
1384         }
1385     }
1386 
1387     if (hWnd == NULL)
1388     {
1389         HeapFree(hAppHeap,
1390                  0,
1391                  MixerWindow);
1392     }
1393 
1394     return hWnd;
1395 }
1396 
1397 static
1398 BOOL
1399 HandleCommandLine(LPTSTR cmdline,
1400                   DWORD dwStyle,
1401                   PWINDOW_MODE pMode,
1402                   PUINT pMixerId)
1403 {
1404     TCHAR option;
1405 
1406     *pMixerId = PLAY_MIXER;
1407     *pMode = (dwStyle & 0x20) ? SMALL_MODE : NORMAL_MODE;
1408 
1409     while (*cmdline == _T(' ') || *cmdline == _T('-') || *cmdline == _T('/'))
1410     {
1411         if (*cmdline++ == _T(' '))
1412             continue;
1413 
1414         option = *cmdline;
1415         if (option)
1416             cmdline++;
1417         while (*cmdline == _T(' '))
1418             cmdline++;
1419 
1420         switch (option)
1421         {
1422             case 'd': /* Device */
1423             case 'D':
1424                 break;
1425 
1426             case 'n': /* Small size */
1427             case 'N':
1428                 *pMode = NORMAL_MODE;
1429                 break;
1430 
1431             case 's': /* Small size */
1432             case 'S':
1433                 *pMode = SMALL_MODE;
1434                 break;
1435 
1436             case 't': /* Tray size */
1437             case 'T':
1438                 *pMode = TRAY_MODE;
1439                 break;
1440 
1441             case 'p': /* Play mode */
1442             case 'P':
1443                 *pMixerId = PLAY_MIXER;
1444                 break;
1445 
1446             case 'r': /* Record mode */
1447             case 'R':
1448                 *pMixerId = RECORD_MIXER;
1449                 break;
1450 
1451             default:
1452                 return FALSE;
1453         }
1454     }
1455 
1456     return TRUE;
1457 }
1458 
1459 int WINAPI
1460 _tWinMain(HINSTANCE hInstance,
1461           HINSTANCE hPrevInstance,
1462           LPTSTR lpszCmdLine,
1463           int nCmdShow)
1464 {
1465     MSG Msg;
1466     int Ret = 1;
1467     INITCOMMONCONTROLSEX Controls;
1468     WINDOW_MODE WindowMode = SMALL_MODE;
1469     UINT MixerId = 0;
1470     DWORD dwStyle;
1471 
1472     UNREFERENCED_PARAMETER(hPrevInstance);
1473     UNREFERENCED_PARAMETER(nCmdShow);
1474 
1475     hAppInstance = hInstance;
1476     hAppHeap = GetProcessHeap();
1477 
1478     if (InitAppConfig())
1479     {
1480         dwStyle = GetStyleValue();
1481         HandleCommandLine(lpszCmdLine, dwStyle, &WindowMode, &MixerId);
1482 
1483         /* load the application title */
1484         if (!AllocAndLoadString(&lpAppTitle,
1485                                 hAppInstance,
1486                                 IDS_SNDVOL32))
1487         {
1488             lpAppTitle = NULL;
1489         }
1490 
1491         Controls.dwSize = sizeof(INITCOMMONCONTROLSEX);
1492         Controls.dwICC = ICC_BAR_CLASSES | ICC_STANDARD_CLASSES;
1493 
1494         InitCommonControlsEx(&Controls);
1495 
1496         if (WindowMode == TRAY_MODE)
1497         {
1498             DialogBoxParam(hAppInstance,
1499                            MAKEINTRESOURCE(IDD_TRAY_MASTER),
1500                            NULL,
1501                            TrayDlgProc,
1502                            0);
1503         }
1504         else
1505         {
1506             if (RegisterApplicationClasses())
1507             {
1508                 hMainWnd = CreateApplicationWindow(WindowMode, MixerId);
1509                 if (hMainWnd != NULL)
1510                 {
1511                     BOOL bRet;
1512                     while ((bRet =GetMessage(&Msg,
1513                                              NULL,
1514                                              0,
1515                                              0)) != 0)
1516                     {
1517                         if (bRet != -1)
1518                         {
1519                             TranslateMessage(&Msg);
1520                             DispatchMessage(&Msg);
1521                         }
1522                     }
1523 
1524                     DestroyWindow(hMainWnd);
1525                     Ret = 0;
1526                 }
1527                 else
1528                 {
1529                     DPRINT("Failed to create application window (LastError: %d)!\n", GetLastError());
1530                 }
1531 
1532                 UnregisterApplicationClasses();
1533             }
1534             else
1535             {
1536                 DPRINT("Failed to register application classes (LastError: %d)!\n", GetLastError());
1537             }
1538         }
1539 
1540         if (lpAppTitle != NULL)
1541         {
1542             LocalFree(lpAppTitle);
1543         }
1544 
1545         CloseAppConfig();
1546     }
1547     else
1548     {
1549         DPRINT("Unable to open the Volume Control registry key!\n");
1550     }
1551 
1552     return Ret;
1553 }
1554