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 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 / VOLUME_STEPS; 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 / VOLUME_STEPS; 750 751 /* FIXME: give me granularity */ 752 Position = VOLUME_STEPS - (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 /* done */ 766 return TRUE; 767 } 768 769 770 static LRESULT CALLBACK 771 MainWindowProc(HWND hwnd, 772 UINT uMsg, 773 WPARAM wParam, 774 LPARAM lParam) 775 { 776 PMIXER_WINDOW MixerWindow; 777 DWORD CtrlID, LineOffset; 778 LRESULT Result = 0; 779 SET_VOLUME_CONTEXT Context; 780 781 switch (uMsg) 782 { 783 case WM_COMMAND: 784 { 785 MixerWindow = GetWindowData(hwnd, 786 MIXER_WINDOW); 787 788 switch (LOWORD(wParam)) 789 { 790 case IDC_PROPERTIES: 791 { 792 PREFERENCES_CONTEXT Pref; 793 794 Pref.MixerWindow = MixerWindow; 795 Pref.Mixer = NULL; 796 Pref.SelectedLine = Preferences.SelectedLine; 797 798 if (DialogBoxParam(hAppInstance, 799 MAKEINTRESOURCE(IDD_PREFERENCES), 800 hwnd, 801 DlgPreferencesProc, 802 (LPARAM)&Pref) == IDOK) 803 { 804 /* update window */ 805 TCHAR szProduct[MAXPNAMELEN]; 806 807 /* get mixer product name */ 808 if (SndMixerGetProductName(Pref.Mixer, 809 szProduct, 810 sizeof(szProduct) / sizeof(szProduct[0])) == -1) 811 { 812 /* failed to get name */ 813 szProduct[0] = L'\0'; 814 } 815 else 816 { 817 /* copy product */ 818 wcscpy(Preferences.DeviceName, szProduct); 819 } 820 821 /* destroy old status bar */ 822 if (MixerWindow->Mode == NORMAL_MODE) 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 if (MixerWindow->Mode == NORMAL_MODE) 839 { 840 MixerWindow->hStatusBar = CreateStatusWindow(WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS, 841 NULL, 842 hwnd, 843 0); 844 if (MixerWindow->hStatusBar) 845 { 846 /* Set status bar */ 847 SendMessage(MixerWindow->hStatusBar, 848 WM_SETTEXT, 849 0, 850 (LPARAM)szProduct); 851 } 852 } 853 } 854 break; 855 } 856 857 case IDC_EXIT: 858 { 859 PostQuitMessage(0); 860 break; 861 } 862 863 case IDC_ABOUT: 864 { 865 HICON hAppIcon = (HICON)GetClassLongPtrW(hwnd, 866 GCLP_HICON); 867 ShellAbout(hwnd, 868 lpAppTitle, 869 NULL, 870 hAppIcon); 871 break; 872 } 873 874 default: 875 { 876 /* get button id */ 877 CtrlID = LOWORD(wParam); 878 879 /* check if the message is from the line switch */ 880 if (HIWORD(wParam) == BN_CLICKED && (CtrlID % IDC_LINE_SWITCH == 0)) 881 { 882 /* compute line offset */ 883 LineOffset = CtrlID / IDC_LINE_SWITCH; 884 885 /* compute window id of line name static control */ 886 CtrlID = LineOffset * IDC_LINE_NAME; 887 888 /* get line name */ 889 if (GetDlgItemTextW(hwnd, CtrlID, Context.LineName, MIXER_LONG_NAME_CHARS) != 0) 890 { 891 /* setup context */ 892 Context.SliderPos = SendMessage((HWND)lParam, BM_GETCHECK, 0, 0); 893 Context.bVertical = FALSE; 894 Context.bSwitch = TRUE; 895 896 /* set volume */ 897 SndMixerEnumConnections(Preferences.MixerWindow->Mixer, Preferences.SelectedLine, SetVolumeCallback, (LPVOID)&Context); 898 } 899 } 900 } 901 902 } 903 break; 904 } 905 906 case MM_MIXM_LINE_CHANGE: 907 { 908 DPRINT("MM_MIXM_LINE_CHANGE\n"); 909 break; 910 } 911 912 case MM_MIXM_CONTROL_CHANGE: 913 { 914 DPRINT("MM_MIXM_CONTROL_CHANGE\n"); 915 916 /* get mixer window */ 917 MixerWindow = GetWindowData(hwnd, 918 MIXER_WINDOW); 919 920 /* sanity checks */ 921 assert(MixerWindow); 922 assert(MixerWindow->Mixer->hmx == (HMIXER)wParam); 923 924 SndMixerEnumConnections(MixerWindow->Mixer, Preferences.SelectedLine, MixerControlChangeCallback, (PVOID)lParam); 925 break; 926 } 927 928 case WM_VSCROLL: 929 { 930 if (LOWORD(wParam) == TB_THUMBTRACK) 931 { 932 /* get dialog item ctrl */ 933 CtrlID = GetDlgCtrlID((HWND)lParam); 934 935 /* get line index */ 936 LineOffset = CtrlID / IDC_LINE_SLIDER_VERT; 937 938 /* compute window id of line name static control */ 939 CtrlID = LineOffset * IDC_LINE_NAME; 940 941 /* get line name */ 942 if (GetDlgItemTextW(hwnd, CtrlID, Context.LineName, MIXER_LONG_NAME_CHARS) != 0) 943 { 944 /* setup context */ 945 Context.SliderPos = HIWORD(wParam); 946 Context.bVertical = TRUE; 947 Context.bSwitch = FALSE; 948 949 /* set volume */ 950 SndMixerEnumConnections(Preferences.MixerWindow->Mixer, Preferences.SelectedLine, SetVolumeCallback, (LPVOID)&Context); 951 } 952 } 953 954 break; 955 } 956 957 958 case WM_CREATE: 959 { 960 MixerWindow = ((LPCREATESTRUCT)lParam)->lpCreateParams; 961 SetWindowLongPtr(hwnd, 962 GWL_USERDATA, 963 (LONG_PTR)MixerWindow); 964 MixerWindow->hWnd = hwnd; 965 MixerWindow->Mixer = SndMixerCreate(MixerWindow->hWnd, MixerWindow->MixerId); 966 if (MixerWindow->Mixer != NULL) 967 { 968 TCHAR szProduct[MAXPNAMELEN]; 969 970 /* get mixer product name */ 971 if (SndMixerGetProductName(MixerWindow->Mixer, 972 szProduct, 973 sizeof(szProduct) / sizeof(szProduct[0])) == -1) 974 { 975 /* failed to get name */ 976 szProduct[0] = L'\0'; 977 } 978 979 980 /* initialize preferences */ 981 ZeroMemory(&Preferences, sizeof(Preferences)); 982 983 /* store mixer */ 984 Preferences.Mixer = MixerWindow->Mixer; 985 986 /* store mixer window */ 987 Preferences.MixerWindow = MixerWindow; 988 989 /* first destination line id */ 990 Preferences.SelectedLine = 0xFFFF0000; 991 992 /* copy product */ 993 wcscpy(Preferences.DeviceName, szProduct); 994 995 if (!RebuildMixerWindowControls(&Preferences)) 996 { 997 DPRINT("Rebuilding mixer window controls failed!\n"); 998 SndMixerDestroy(MixerWindow->Mixer); 999 MixerWindow->Mixer = NULL; 1000 Result = -1; 1001 } 1002 1003 /* create status window */ 1004 if (MixerWindow->Mode == NORMAL_MODE) 1005 { 1006 MixerWindow->hStatusBar = CreateStatusWindow(WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS, 1007 NULL, 1008 hwnd, 1009 0); 1010 if (MixerWindow->hStatusBar) 1011 { 1012 SendMessage(MixerWindow->hStatusBar, 1013 WM_SETTEXT, 1014 0, 1015 (LPARAM)szProduct); 1016 } 1017 } 1018 } 1019 break; 1020 } 1021 1022 case WM_DESTROY: 1023 { 1024 MixerWindow = GetWindowData(hwnd, 1025 MIXER_WINDOW); 1026 if (MixerWindow != NULL) 1027 { 1028 if (MixerWindow->Mixer != NULL) 1029 { 1030 SndMixerDestroy(MixerWindow->Mixer); 1031 } 1032 if (MixerWindow->hFont) 1033 DeleteObject(MixerWindow->hFont); 1034 HeapFree(hAppHeap, 0, MixerWindow); 1035 } 1036 break; 1037 } 1038 1039 case WM_CLOSE: 1040 { 1041 PostQuitMessage(0); 1042 break; 1043 } 1044 1045 default: 1046 { 1047 Result = DefWindowProc(hwnd, 1048 uMsg, 1049 wParam, 1050 lParam); 1051 break; 1052 } 1053 } 1054 1055 return Result; 1056 } 1057 1058 static BOOL 1059 RegisterApplicationClasses(VOID) 1060 { 1061 WNDCLASSEX wc; 1062 1063 wc.cbSize = sizeof(WNDCLASSEX); 1064 wc.style = CS_HREDRAW | CS_VREDRAW; 1065 wc.lpfnWndProc = MainWindowProc; 1066 wc.cbClsExtra = 0; 1067 wc.cbWndExtra = sizeof(PMIXER_WINDOW); 1068 wc.hInstance = hAppInstance; 1069 wc.hIcon = LoadIcon(hAppInstance, 1070 MAKEINTRESOURCE(IDI_MAINAPP)); 1071 wc.hCursor = LoadCursor(NULL, 1072 IDC_ARROW); 1073 wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); 1074 wc.lpszMenuName = NULL; 1075 wc.lpszClassName = SZ_APP_CLASS; 1076 wc.hIconSm = NULL; 1077 MainWindowClass = RegisterClassEx(&wc); 1078 1079 return MainWindowClass != 0; 1080 } 1081 1082 static VOID 1083 UnregisterApplicationClasses(VOID) 1084 { 1085 UnregisterClass(SZ_APP_CLASS, 1086 hAppInstance); 1087 } 1088 1089 static HWND 1090 CreateApplicationWindow( 1091 WINDOW_MODE WindowMode, 1092 UINT MixerId) 1093 { 1094 HWND hWnd; 1095 1096 PMIXER_WINDOW MixerWindow = HeapAlloc(hAppHeap, 1097 HEAP_ZERO_MEMORY, 1098 sizeof(MIXER_WINDOW)); 1099 if (MixerWindow == NULL) 1100 { 1101 return NULL; 1102 } 1103 1104 MixerWindow->Mode = WindowMode; 1105 MixerWindow->MixerId = MixerId; 1106 1107 if (mixerGetNumDevs() > 0) 1108 { 1109 hWnd = CreateWindowEx(WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT, 1110 SZ_APP_CLASS, 1111 lpAppTitle, 1112 WS_DLGFRAME | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE, 1113 0, 0, 300, 315, 1114 NULL, 1115 LoadMenu(hAppInstance, 1116 MAKEINTRESOURCE(IDM_MAINMENU)), 1117 hAppInstance, 1118 MixerWindow); 1119 } 1120 else 1121 { 1122 LPTSTR lpErrMessage; 1123 1124 /* 1125 * no mixer devices are available! 1126 */ 1127 1128 hWnd = NULL; 1129 if (AllocAndLoadString(&lpErrMessage, 1130 hAppInstance, 1131 IDS_NOMIXERDEVICES)) 1132 { 1133 MessageBox(NULL, 1134 lpErrMessage, 1135 lpAppTitle, 1136 MB_ICONINFORMATION); 1137 LocalFree(lpErrMessage); 1138 } 1139 } 1140 1141 if (hWnd == NULL) 1142 { 1143 HeapFree(hAppHeap, 1144 0, 1145 MixerWindow); 1146 } 1147 1148 return hWnd; 1149 } 1150 1151 static 1152 BOOL 1153 HandleCommandLine(LPTSTR cmdline, 1154 PWINDOW_MODE pMode, 1155 PUINT pMixerId) 1156 { 1157 TCHAR option; 1158 1159 *pMixerId = 0; 1160 *pMode = SMALL_MODE; 1161 1162 while (*cmdline == _T(' ') || *cmdline == _T('-') || *cmdline == _T('/')) 1163 { 1164 if (*cmdline++ == _T(' ')) 1165 continue; 1166 1167 option = *cmdline; 1168 if (option) 1169 cmdline++; 1170 while (*cmdline == _T(' ')) 1171 cmdline++; 1172 1173 switch (option) 1174 { 1175 case 'd': /* Device */ 1176 case 'D': 1177 break; 1178 1179 case 'n': /* Normal size */ 1180 case 'N': 1181 *pMode = NORMAL_MODE; 1182 break; 1183 1184 case 's': /* Small size */ 1185 case 'S': 1186 *pMode = SMALL_MODE; 1187 break; 1188 1189 case 't': /* Tray size */ 1190 case 'T': 1191 *pMode = TRAY_MODE; 1192 break; 1193 1194 case 'p': /* Play mode */ 1195 case 'P': 1196 *pMixerId = 0; 1197 break; 1198 1199 case 'r': /* Record mode */ 1200 case 'R': 1201 *pMixerId = 1; 1202 break; 1203 1204 default: 1205 return FALSE; 1206 } 1207 } 1208 1209 return TRUE; 1210 } 1211 1212 int WINAPI 1213 _tWinMain(HINSTANCE hInstance, 1214 HINSTANCE hPrevInstance, 1215 LPTSTR lpszCmdLine, 1216 int nCmdShow) 1217 { 1218 MSG Msg; 1219 int Ret = 1; 1220 INITCOMMONCONTROLSEX Controls; 1221 WINDOW_MODE WindowMode = SMALL_MODE; 1222 UINT MixerId = 0; 1223 1224 UNREFERENCED_PARAMETER(hPrevInstance); 1225 UNREFERENCED_PARAMETER(nCmdShow); 1226 1227 hAppInstance = hInstance; 1228 hAppHeap = GetProcessHeap(); 1229 1230 HandleCommandLine(lpszCmdLine, &WindowMode, &MixerId); 1231 1232 if (InitAppConfig()) 1233 { 1234 /* load the application title */ 1235 if (!AllocAndLoadString(&lpAppTitle, 1236 hAppInstance, 1237 IDS_SNDVOL32)) 1238 { 1239 lpAppTitle = NULL; 1240 } 1241 1242 Controls.dwSize = sizeof(INITCOMMONCONTROLSEX); 1243 Controls.dwICC = ICC_BAR_CLASSES | ICC_STANDARD_CLASSES; 1244 1245 InitCommonControlsEx(&Controls); 1246 1247 if (WindowMode == TRAY_MODE) 1248 { 1249 DialogBoxParam(hAppInstance, 1250 MAKEINTRESOURCE(IDD_TRAY_MASTER), 1251 NULL, 1252 TrayDlgProc, 1253 0); 1254 } 1255 else 1256 { 1257 if (RegisterApplicationClasses()) 1258 { 1259 hMainWnd = CreateApplicationWindow(WindowMode, MixerId); 1260 if (hMainWnd != NULL) 1261 { 1262 BOOL bRet; 1263 while ((bRet =GetMessage(&Msg, 1264 NULL, 1265 0, 1266 0)) != 0) 1267 { 1268 if (bRet != -1) 1269 { 1270 TranslateMessage(&Msg); 1271 DispatchMessage(&Msg); 1272 } 1273 } 1274 1275 DestroyWindow(hMainWnd); 1276 Ret = 0; 1277 } 1278 else 1279 { 1280 DPRINT("Failed to create application window (LastError: %d)!\n", GetLastError()); 1281 } 1282 1283 UnregisterApplicationClasses(); 1284 } 1285 else 1286 { 1287 DPRINT("Failed to register application classes (LastError: %d)!\n", GetLastError()); 1288 } 1289 } 1290 1291 if (lpAppTitle != NULL) 1292 { 1293 LocalFree(lpAppTitle); 1294 } 1295 1296 CloseAppConfig(); 1297 } 1298 else 1299 { 1300 DPRINT("Unable to open the Volume Control registry key!\n"); 1301 } 1302 1303 return Ret; 1304 } 1305