partly implemented the mixer selection dialog
[reactos.git] / reactos / subsys / system / 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 * VMware is a registered trademark of VMware, Inc.
20 */
21 /* $Id$
22 *
23 * COPYRIGHT: See COPYING in the top level directory
24 * PROJECT: ReactOS Sound Volume Control
25 * FILE: subsys/system/sndvol32/sndvol32.c
26 * PROGRAMMERS: Thomas Weidenmueller <w3seek@reactos.com>
27 */
28 #include <sndvol32.h>
29
30 HINSTANCE hAppInstance;
31 ATOM MainWindowClass;
32 HWND hMainWnd;
33 HANDLE hAppHeap;
34 LPTSTR lpAppTitle;
35
36 #define GetDialogData(hwndDlg, type) \
37 ( P##type )GetWindowLongPtr((hwndDlg), DWLP_USER)
38 #define GetWindowData(hwnd, type) \
39 ( P##type )GetWindowLongPtr((hwnd), GWL_USERDATA)
40
41 /******************************************************************************/
42
43 typedef struct _PREFERENCES_CONTEXT
44 {
45 PMIXER_WINDOW MixerWindow;
46 PSND_MIXER Mixer;
47 HWND hwndDlg;
48
49 UINT Selected;
50 DWORD SelectedLine;
51 DWORD PlaybackID;
52 DWORD RecordingID;
53 UINT OtherLines;
54 } PREFERENCES_CONTEXT, *PPREFERENCES_CONTEXT;
55
56 typedef struct _PREFERENCES_FILL_DEVICES
57 {
58 PPREFERENCES_CONTEXT PrefContext;
59 HWND hComboBox;
60 UINT Selected;
61 } PREFERENCES_FILL_DEVICES, *PPREFERENCES_FILL_DEVICES;
62
63 static BOOL CALLBACK
64 FillDeviceComboBox(PSND_MIXER Mixer,
65 UINT Id,
66 LPCTSTR ProductName,
67 PVOID Context)
68 {
69 LRESULT lres;
70 PPREFERENCES_FILL_DEVICES FillContext = (PPREFERENCES_FILL_DEVICES)Context;
71
72 lres = SendMessage(FillContext->hComboBox,
73 CB_ADDSTRING,
74 0,
75 (LPARAM)ProductName);
76 if (lres != CB_ERR)
77 {
78 /* save the index so we don't screw stuff when the combobox is sorted... */
79 SendMessage(FillContext->hComboBox,
80 CB_SETITEMDATA,
81 (WPARAM)lres,
82 Id);
83
84 if (Id == FillContext->Selected)
85 {
86 SendMessage(FillContext->hComboBox,
87 CB_SETCURSEL,
88 (WPARAM)lres,
89 0);
90 }
91 }
92
93 return TRUE;
94 }
95
96 static BOOL CALLBACK
97 PrefDlgAddLine(PSND_MIXER Mixer,
98 LPMIXERLINE Line,
99 PVOID Context)
100 {
101 PPREFERENCES_CONTEXT PrefContext = (PPREFERENCES_CONTEXT)Context;
102
103 switch (Line->dwComponentType)
104 {
105 case MIXERLINE_COMPONENTTYPE_DST_SPEAKERS:
106 if (PrefContext->PlaybackID == (DWORD)-1)
107 {
108 PrefContext->PlaybackID = Line->dwLineID;
109
110 if (PrefContext->SelectedLine == (DWORD)-1)
111 {
112 PrefContext->SelectedLine = Line->dwLineID;
113 }
114 }
115 else
116 goto AddToOthersLines;
117
118 break;
119
120 case MIXERLINE_COMPONENTTYPE_DST_WAVEIN:
121 if (PrefContext->RecordingID == (DWORD)-1)
122 {
123 PrefContext->RecordingID = Line->dwLineID;
124
125 if (PrefContext->SelectedLine == (DWORD)-1)
126 {
127 PrefContext->SelectedLine = Line->dwLineID;
128 }
129 }
130 else
131 goto AddToOthersLines;
132
133 break;
134
135 default:
136 {
137 LRESULT lres;
138 HWND hwndCbOthers;
139
140 if (PrefContext->SelectedLine == (DWORD)-1)
141 {
142 PrefContext->SelectedLine = Line->dwLineID;
143 }
144
145 AddToOthersLines:
146 hwndCbOthers = GetDlgItem(PrefContext->hwndDlg,
147 IDC_LINE);
148
149 lres = SendMessage(hwndCbOthers,
150 CB_ADDSTRING,
151 0,
152 (LPARAM)Line->szName);
153 if (lres != CB_ERR)
154 {
155 SendMessage(hwndCbOthers,
156 CB_SETITEMDATA,
157 (WPARAM)lres,
158 Line->dwLineID);
159
160 PrefContext->OtherLines++;
161 }
162 break;
163 }
164 }
165
166 return TRUE;
167 }
168
169 static BOOL CALLBACK
170 PrefDlgAddConnection(PSND_MIXER Mixer,
171 DWORD LineID,
172 LPMIXERLINE Line,
173 PVOID Context)
174 {
175 PPREFERENCES_CONTEXT PrefContext = (PPREFERENCES_CONTEXT)Context;
176 LVITEM lvi;
177
178 lvi.mask = LVIF_TEXT | LVIF_PARAM;
179 lvi.iItem = 0;
180 lvi.iSubItem = 0;
181 lvi.pszText = Line->szName;
182 lvi.lParam = (LPARAM)Line->dwSource;
183
184 SendMessage(GetDlgItem(PrefContext->hwndDlg,
185 IDC_CONTROLS),
186 LVM_INSERTITEM,
187 0,
188 (LPARAM)&lvi);
189
190 return TRUE;
191 }
192
193 static VOID
194 UpdatePrefDlgControls(PPREFERENCES_CONTEXT Context,
195 DWORD LineID)
196 {
197 UINT OldID, MixerID = 0;
198 INT DeviceCbIndex;
199
200 /* select the mixer */
201 DeviceCbIndex = SendMessage(GetDlgItem(Context->hwndDlg,
202 IDC_MIXERDEVICE),
203 CB_GETCURSEL,
204 0,
205 0);
206 if (DeviceCbIndex != CB_ERR)
207 {
208 MixerID = SendMessage(GetDlgItem(Context->hwndDlg,
209 IDC_MIXERDEVICE),
210 CB_GETITEMDATA,
211 DeviceCbIndex,
212 0);
213 if (MixerID == CB_ERR)
214 {
215 MixerID = 0;
216 }
217 }
218
219 OldID = Context->Selected;
220 if (MixerID != OldID &&
221 SndMixerSelect(Context->Mixer,
222 MixerID))
223 {
224 Context->Selected = SndMixerGetSelection(Context->Mixer);
225
226 /* update the controls */
227 Context->PlaybackID = (DWORD)-1;
228 Context->RecordingID = (DWORD)-1;
229 Context->OtherLines = 0;
230 Context->SelectedLine = (DWORD)-1;
231
232 if (SndMixerEnumLines(Context->Mixer,
233 PrefDlgAddLine,
234 Context))
235 {
236 UINT SelBox = 0;
237
238 /* enable/disable controls and make default selection */
239 EnableWindow(GetDlgItem(Context->hwndDlg,
240 IDC_PLAYBACK),
241 Context->PlaybackID != (DWORD)-1);
242 CheckDlgButton(Context->hwndDlg,
243 IDC_PLAYBACK,
244 (Context->PlaybackID != (DWORD)-1 && SelBox++ == 0) ?
245 BST_CHECKED : BST_UNCHECKED);
246
247 EnableWindow(GetDlgItem(Context->hwndDlg,
248 IDC_RECORDING),
249 Context->RecordingID != (DWORD)-1);
250 CheckDlgButton(Context->hwndDlg,
251 IDC_RECORDING,
252 (Context->RecordingID != (DWORD)-1 && SelBox++ == 0) ?
253 BST_CHECKED : BST_UNCHECKED);
254
255 if (Context->OtherLines != 0)
256 {
257 /* select the first item in the other lines combo box by default */
258 SendMessage(GetDlgItem(Context->hwndDlg,
259 IDC_LINE),
260 CB_SETCURSEL,
261 0,
262 0);
263 }
264 EnableWindow(GetDlgItem(Context->hwndDlg,
265 IDC_LINE),
266 Context->OtherLines != 0);
267 CheckDlgButton(Context->hwndDlg,
268 IDC_LINE,
269 (Context->OtherLines != 0 && SelBox++ == 0) ?
270 BST_CHECKED : BST_UNCHECKED);
271
272 /* disable the OK button if the device doesn't have any lines */
273 EnableWindow(GetDlgItem(Context->hwndDlg,
274 IDOK),
275 Context->PlaybackID != (DWORD)-1 ||
276 Context->RecordingID != (DWORD)-1 ||
277 Context->OtherLines != 0);
278
279 LineID = Context->SelectedLine;
280 }
281 }
282
283 /* update the line sources list */
284 if ((MixerID != OldID && Context->SelectedLine != (DWORD)-1) ||
285 (Context->SelectedLine != LineID && LineID != (DWORD)-1))
286 {
287 Context->SelectedLine = LineID;
288
289 ListView_DeleteAllItems(GetDlgItem(Context->hwndDlg,
290 IDC_CONTROLS));
291
292 SndMixerEnumConnections(Context->Mixer,
293 LineID,
294 PrefDlgAddConnection,
295 Context);
296 }
297 }
298
299 static INT_PTR CALLBACK
300 DlgPreferencesProc(HWND hwndDlg,
301 UINT uMsg,
302 WPARAM wParam,
303 LPARAM lParam)
304 {
305 PPREFERENCES_CONTEXT Context;
306
307 switch (uMsg)
308 {
309 case WM_COMMAND:
310 {
311 Context = GetDialogData(hwndDlg,
312 PREFERENCES_CONTEXT);
313 switch (LOWORD(wParam))
314 {
315 case IDC_MIXERDEVICE:
316 {
317 if (HIWORD(wParam) == CBN_SELCHANGE)
318 {
319 UpdatePrefDlgControls(Context,
320 (DWORD)-1);
321 }
322 break;
323 }
324
325 case IDC_LINE:
326 {
327 if (HIWORD(wParam) == CBN_SELCHANGE)
328 {
329 UpdatePrefDlgControls(Context,
330 (DWORD)-1);
331 }
332 break;
333 }
334
335 case IDC_PLAYBACK:
336 {
337 UpdatePrefDlgControls(Context,
338 Context->PlaybackID);
339 break;
340 }
341
342 case IDC_RECORDING:
343 {
344 UpdatePrefDlgControls(Context,
345 Context->RecordingID);
346 break;
347 }
348
349 case IDC_OTHER:
350 {
351 INT LineCbIndex;
352 DWORD LineID;
353
354 LineCbIndex = SendMessage(GetDlgItem(Context->hwndDlg,
355 IDC_MIXERDEVICE),
356 CB_GETCURSEL,
357 0,
358 0);
359 if (LineCbIndex != CB_ERR)
360 {
361 LineID = SendMessage(GetDlgItem(Context->hwndDlg,
362 IDC_MIXERDEVICE),
363 CB_GETITEMDATA,
364 LineCbIndex,
365 0);
366 if (LineID != CB_ERR)
367 {
368 UpdatePrefDlgControls(Context,
369 LineID);
370 }
371 }
372 break;
373 }
374
375 case IDOK:
376 case IDCANCEL:
377 {
378 EndDialog(hwndDlg,
379 LOWORD(wParam));
380 break;
381 }
382 }
383 break;
384 }
385
386 case MM_MIXM_LINE_CHANGE:
387 {
388 DPRINT("MM_MIXM_LINE_CHANGE\n");
389 break;
390 }
391
392 case MM_MIXM_CONTROL_CHANGE:
393 {
394 DPRINT("MM_MIXM_CONTROL_CHANGE\n");
395 break;
396 }
397
398 case WM_INITDIALOG:
399 {
400 PREFERENCES_FILL_DEVICES FillDevContext;
401 LVCOLUMN lvc;
402 RECT rcClient;
403 HWND hwndControls;
404
405 SetWindowLongPtr(hwndDlg,
406 DWLP_USER,
407 (LONG_PTR)lParam);
408 Context = (PPREFERENCES_CONTEXT)((LONG_PTR)lParam);
409 Context->hwndDlg = hwndDlg;
410 Context->Mixer = SndMixerCreate(hwndDlg);
411 Context->Selected = (UINT)-1;
412
413 FillDevContext.PrefContext = Context;
414 FillDevContext.hComboBox = GetDlgItem(hwndDlg,
415 IDC_MIXERDEVICE);
416 FillDevContext.Selected = SndMixerGetSelection(Context->Mixer);
417 SndMixerEnumProducts(Context->Mixer,
418 FillDeviceComboBox,
419 &FillDevContext);
420
421 /* initialize the list view control */
422 hwndControls = GetDlgItem(hwndDlg,
423 IDC_CONTROLS);
424 ListView_SetExtendedListViewStyle(hwndControls,
425 LVS_EX_CHECKBOXES);
426
427 GetClientRect(hwndControls,
428 &rcClient);
429 lvc.mask = LVCF_TEXT | LVCF_WIDTH;
430 lvc.pszText = TEXT("");
431 lvc.cx = rcClient.right;
432 SendMessage(hwndControls,
433 LVM_INSERTCOLUMN,
434 0,
435 (LPARAM)&lvc);
436
437 /* update all controls */
438 UpdatePrefDlgControls(Context,
439 (DWORD)-1);
440 return TRUE;
441 }
442
443 case WM_DESTROY:
444 {
445 Context = GetDialogData(hwndDlg,
446 PREFERENCES_CONTEXT);
447 if (Context->Mixer != NULL)
448 {
449 SndMixerDestroy(Context->Mixer);
450 }
451 break;
452 }
453
454 case WM_CLOSE:
455 {
456 EndDialog(hwndDlg,
457 IDCANCEL);
458 break;
459 }
460 }
461
462 return 0;
463 }
464
465 /******************************************************************************/
466
467 static VOID
468 DeleteMixerWindowControls(PMIXER_WINDOW MixerWindow)
469 {
470 }
471
472 BOOL
473 RebuildMixerWindowControls(PMIXER_WINDOW MixerWindow)
474 {
475 DeleteMixerWindowControls(MixerWindow);
476
477 return TRUE;
478 }
479
480 LRESULT CALLBACK
481 MainWindowProc(HWND hwnd,
482 UINT uMsg,
483 WPARAM wParam,
484 LPARAM lParam)
485 {
486 PMIXER_WINDOW MixerWindow;
487 LRESULT Result = 0;
488
489 switch (uMsg)
490 {
491 case WM_COMMAND:
492 {
493 MixerWindow = GetWindowData(hwnd,
494 MIXER_WINDOW);
495
496 switch (LOWORD(wParam))
497 {
498 case IDC_PROPERTIES:
499 {
500 PREFERENCES_CONTEXT Preferences;
501
502 Preferences.MixerWindow = MixerWindow;
503 Preferences.Mixer = NULL;
504
505 if (DialogBoxParam(hAppInstance,
506 MAKEINTRESOURCE(IDD_PREFERENCES),
507 hwnd,
508 DlgPreferencesProc,
509 (LPARAM)&Preferences) == IDOK)
510 {
511 /* FIXME - update window */
512 }
513 break;
514 }
515
516 case IDC_EXIT:
517 {
518 PostQuitMessage(0);
519 break;
520 }
521
522 case IDC_ABOUT:
523 {
524 HICON hAppIcon = (HICON)GetClassLongPtrW(hwnd,
525 GCLP_HICON);
526 ShellAbout(hwnd,
527 lpAppTitle,
528 NULL,
529 hAppIcon);
530 break;
531 }
532 }
533 break;
534 }
535
536 case MM_MIXM_LINE_CHANGE:
537 {
538 DPRINT("MM_MIXM_LINE_CHANGE\n");
539 break;
540 }
541
542 case MM_MIXM_CONTROL_CHANGE:
543 {
544 DPRINT("MM_MIXM_CONTROL_CHANGE\n");
545 break;
546 }
547
548 case WM_CREATE:
549 {
550 MixerWindow = ((LPCREATESTRUCT)lParam)->lpCreateParams;
551 SetWindowLongPtr(hwnd,
552 GWL_USERDATA,
553 (LONG_PTR)MixerWindow);
554 MixerWindow->hWnd = hwnd;
555 MixerWindow->hStatusBar = CreateStatusWindow(WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS,
556 NULL,
557 hwnd,
558 0);
559 if (MixerWindow->hStatusBar != NULL)
560 {
561 MixerWindow->Mixer = SndMixerCreate(MixerWindow->hWnd);
562 if (MixerWindow->Mixer != NULL)
563 {
564 TCHAR szProduct[MAXPNAMELEN];
565
566 if (SndMixerGetProductName(MixerWindow->Mixer,
567 szProduct,
568 sizeof(szProduct) / sizeof(szProduct[0])) > 0)
569 {
570 SendMessage(MixerWindow->hStatusBar,
571 WM_SETTEXT,
572 0,
573 (LPARAM)szProduct);
574 }
575
576 if (!RebuildMixerWindowControls(MixerWindow))
577 {
578 DPRINT("Rebuilding mixer window controls failed!\n");
579 SndMixerDestroy(MixerWindow->Mixer);
580 Result = -1;
581 }
582 }
583 else
584 {
585 Result = -1;
586 }
587 }
588 else
589 {
590 DPRINT("Failed to create status window!\n");
591 Result = -1;
592 }
593 break;
594 }
595
596 case WM_DESTROY:
597 {
598 MixerWindow = GetWindowData(hwnd,
599 MIXER_WINDOW);
600 if (MixerWindow->Mixer != NULL)
601 {
602 SndMixerDestroy(MixerWindow->Mixer);
603 }
604 break;
605 }
606
607 case WM_CLOSE:
608 {
609 PostQuitMessage(0);
610 break;
611 }
612
613 default:
614 {
615 Result = DefWindowProc(hwnd,
616 uMsg,
617 wParam,
618 lParam);
619 break;
620 }
621 }
622
623 return Result;
624 }
625
626 static BOOL
627 RegisterApplicationClasses(VOID)
628 {
629 WNDCLASSEX wc;
630
631 wc.cbSize = sizeof(WNDCLASSEX);
632 wc.style = CS_HREDRAW | CS_VREDRAW;
633 wc.lpfnWndProc = MainWindowProc;
634 wc.cbClsExtra = 0;
635 wc.cbWndExtra = sizeof(PMIXER_WINDOW);
636 wc.hInstance = hAppInstance;
637 wc.hIcon = LoadIcon(hAppInstance,
638 MAKEINTRESOURCE(IDI_MAINAPP));
639 wc.hCursor = LoadCursor(NULL,
640 IDC_ARROW);
641 wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
642 wc.lpszMenuName = NULL;
643 wc.lpszClassName = SZ_APP_CLASS;
644 wc.hIconSm = NULL;
645 MainWindowClass = RegisterClassEx(&wc);
646
647 return MainWindowClass != 0;
648 }
649
650 static VOID
651 UnregisterApplicationClasses(VOID)
652 {
653 UnregisterClass(SZ_APP_CLASS,
654 hAppInstance);
655 }
656
657 static HWND
658 CreateApplicationWindow(VOID)
659 {
660 HWND hWnd;
661
662 PMIXER_WINDOW MixerWindow = HeapAlloc(hAppHeap,
663 0,
664 sizeof(MIXER_WINDOW));
665 if (MixerWindow == NULL)
666 {
667 return NULL;
668 }
669
670 if (mixerGetNumDevs() > 0)
671 {
672 hWnd = CreateWindowEx(WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT,
673 SZ_APP_CLASS,
674 lpAppTitle,
675 WS_DLGFRAME | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE,
676 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
677 NULL,
678 LoadMenu(hAppInstance,
679 MAKEINTRESOURCE(IDM_MAINMENU)),
680 hAppInstance,
681 MixerWindow);
682 }
683 else
684 {
685 LPTSTR lpErrMessage;
686
687 /*
688 * no mixer devices are available!
689 */
690
691 hWnd = NULL;
692 AllocAndLoadString(&lpErrMessage,
693 hAppInstance,
694 IDS_NOMIXERDEVICES);
695 MessageBox(NULL,
696 lpErrMessage,
697 lpAppTitle,
698 MB_ICONINFORMATION);
699 LocalFree(lpErrMessage);
700 }
701
702 if (hWnd == NULL)
703 {
704 HeapFree(hAppHeap,
705 0,
706 MixerWindow);
707 }
708
709 return hWnd;
710 }
711
712 int WINAPI
713 WinMain(HINSTANCE hInstance,
714 HINSTANCE hPrevInstance,
715 LPSTR lpszCmdLine,
716 int nCmdShow)
717 {
718 MSG Msg;
719
720 hAppInstance = hInstance;
721 hAppHeap = GetProcessHeap();
722
723 /* load the application title */
724 if (AllocAndLoadString(&lpAppTitle,
725 hAppInstance,
726 IDS_SNDVOL32) == 0)
727 {
728 lpAppTitle = NULL;
729 }
730
731 InitCommonControls();
732
733 if (!RegisterApplicationClasses())
734 {
735 DPRINT("Failed to register application classes (LastError: %d)!\n", GetLastError());
736 return 1;
737 }
738
739 hMainWnd = CreateApplicationWindow();
740 if (hMainWnd == NULL)
741 {
742 DPRINT("Failed to creat application window (LastError: %d)!\n", GetLastError());
743 return 1;
744 }
745
746 while (GetMessage(&Msg,
747 NULL,
748 0,
749 0))
750 {
751 TranslateMessage(&Msg);
752 DispatchMessage(&Msg);
753 }
754
755 DestroyWindow(hMainWnd);
756
757 UnregisterApplicationClasses();
758
759 if (lpAppTitle != NULL)
760 {
761 LocalFree(lpAppTitle);
762 }
763
764 return 0;
765 }
766