[MMSYS] Take the balance between channels into account for the master volume trackbar.
[reactos.git] / dll / cpl / mmsys / volume.c
index 21127e1..10f61f8 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * PROJECT:         ReactOS Multimedia Control Panel
- * FILE:            dll/cpl/mmsys/mmsys.c
+ * FILE:            dll/cpl/mmsys/volume.c
  * PURPOSE:         ReactOS Multimedia Control Panel
  * PROGRAMMER:      Thomas Weidenmueller <w3seek@reactos.com>
  *                  Johannes Anderwald <janderwald@reactos.com>
@@ -11,7 +11,6 @@
 
 #include <shellapi.h>
 
-#define VOLUME_DIVIDER 0xFFF
 
 typedef struct _IMGINFO
 {
@@ -26,14 +25,20 @@ typedef struct _GLOBAL_DATA
     HMIXER hMixer;
     HICON hIconMuted;
     HICON hIconUnMuted;
+    HICON hIconNoHW;
 
     LONG muteVal;
     DWORD muteControlID;
 
     DWORD volumeControlID;
+    DWORD volumeChannels;
     DWORD volumeMinimum;
     DWORD volumeMaximum;
-    DWORD volumeValue;
+    DWORD volumeStep;
+
+    DWORD maxVolume;
+    PMIXERCONTROLDETAILS_UNSIGNED volumeInitValues;
+    PMIXERCONTROLDETAILS_UNSIGNED volumeCurrentValues;
 
 } GLOBAL_DATA, *PGLOBAL_DATA;
 
@@ -154,6 +159,8 @@ GetVolumeControl(PGLOBAL_DATA pGlobalData)
         != MMSYSERR_NOERROR)
         return;
 
+    pGlobalData->volumeChannels = mxln.cChannels;
+
     mxlc.cbStruct = sizeof(MIXERLINECONTROLS);
     mxlc.dwLineID = mxln.dwLineID;
     mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
@@ -167,74 +174,147 @@ GetVolumeControl(PGLOBAL_DATA pGlobalData)
     pGlobalData->volumeMinimum = mxc.Bounds.dwMinimum;
     pGlobalData->volumeMaximum = mxc.Bounds.dwMaximum;
     pGlobalData->volumeControlID = mxc.dwControlID;
+    pGlobalData->volumeStep = (pGlobalData->volumeMaximum - pGlobalData->volumeMinimum) / (VOLUME_MAX - VOLUME_MIN);
+
+    pGlobalData->volumeInitValues = HeapAlloc(GetProcessHeap(),
+                                              0,
+                                              mxln.cChannels * sizeof(MIXERCONTROLDETAILS_UNSIGNED));
+    if (pGlobalData->volumeInitValues == NULL)
+        return;
+
+    pGlobalData->volumeCurrentValues = HeapAlloc(GetProcessHeap(),
+                                                 0,
+                                                 mxln.cChannels * sizeof(MIXERCONTROLDETAILS_UNSIGNED));
+    if (pGlobalData->volumeCurrentValues == NULL)
+        return;
 }
 
 
 VOID
 GetVolumeValue(PGLOBAL_DATA pGlobalData)
 {
-    MIXERCONTROLDETAILS_UNSIGNED mxcdVolume;
     MIXERCONTROLDETAILS mxcd;
+    DWORD i;
 
     if (pGlobalData->hMixer == NULL)
         return;
 
     mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
     mxcd.dwControlID = pGlobalData->volumeControlID;
-    mxcd.cChannels = 1;
+    mxcd.cChannels = pGlobalData->volumeChannels;
     mxcd.cMultipleItems = 0;
     mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
-    mxcd.paDetails = &mxcdVolume;
+    mxcd.paDetails = pGlobalData->volumeInitValues;
 
     if (mixerGetControlDetails((HMIXEROBJ)pGlobalData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_VALUE)
         != MMSYSERR_NOERROR)
         return;
 
-    pGlobalData->volumeValue = mxcdVolume.dwValue;
+    pGlobalData->maxVolume = 0;
+    for (i = 0; i < pGlobalData->volumeChannels; i++)
+    {
+        pGlobalData->volumeCurrentValues[i].dwValue = pGlobalData->volumeInitValues[i].dwValue;
+
+        if (pGlobalData->volumeInitValues[i].dwValue > pGlobalData->maxVolume)
+            pGlobalData->maxVolume = pGlobalData->volumeInitValues[i].dwValue;
+    }
 }
 
 
 VOID
-SetVolumeValue(PGLOBAL_DATA pGlobalData){
-    MIXERCONTROLDETAILS_UNSIGNED mxcdVolume;
+SetVolumeValue(PGLOBAL_DATA pGlobalData,
+               DWORD dwPosition)
+{
     MIXERCONTROLDETAILS mxcd;
+    DWORD dwVolume, i;
 
     if (pGlobalData->hMixer == NULL)
         return;
 
-    mxcdVolume.dwValue = pGlobalData->volumeValue;
+    if (dwPosition == VOLUME_MIN)
+        dwVolume = pGlobalData->volumeMinimum;
+    else if (dwPosition == VOLUME_MAX)
+        dwVolume = pGlobalData->volumeMaximum;
+    else
+        dwVolume = (dwPosition * pGlobalData->volumeStep) + pGlobalData->volumeMinimum;
+
+    for (i = 0; i < pGlobalData->volumeChannels; i++)
+    {
+        if (pGlobalData->volumeInitValues[i].dwValue == pGlobalData->maxVolume)
+        {
+            pGlobalData->volumeCurrentValues[i].dwValue = dwVolume;
+        }
+        else
+        {
+            pGlobalData->volumeCurrentValues[i].dwValue =
+                pGlobalData->volumeInitValues[i].dwValue * dwVolume / pGlobalData-> maxVolume;
+        }
+    }
+
     mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
     mxcd.dwControlID = pGlobalData->volumeControlID;
-    mxcd.cChannels = 1;
+    mxcd.cChannels = pGlobalData->volumeChannels;
     mxcd.cMultipleItems = 0;
     mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
-    mxcd.paDetails = &mxcdVolume;
+    mxcd.paDetails = pGlobalData->volumeCurrentValues;
 
     if (mixerSetControlDetails((HMIXEROBJ)pGlobalData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE)
         != MMSYSERR_NOERROR)
         return;
+}
 
-    pGlobalData->volumeValue = mxcdVolume.dwValue;
+
+static
+VOID
+SetSystrayVolumeIconState(BOOL bEnabled)
+{
+    HWND hwndTaskBar;
+
+    hwndTaskBar = FindWindowW(L"SystemTray_Main", NULL);
+    if (hwndTaskBar == NULL)
+        return;
+
+    SendMessageW(hwndTaskBar, WM_USER + 220, 4, bEnabled);
 }
 
+static
+BOOL
+GetSystrayVolumeIconState(VOID)
+{
+    HWND hwndTaskBar;
+
+    hwndTaskBar = FindWindowW(L"SystemTray_Main", NULL);
+    if (hwndTaskBar == NULL)
+    {
+        return FALSE;
+    }
+
+    return (BOOL)SendMessageW(hwndTaskBar, WM_USER + 221, 4, 0);
+}
 
 VOID
 InitVolumeControls(HWND hwndDlg, PGLOBAL_DATA pGlobalData)
 {
     UINT NumMixers;
     MIXERCAPS mxc;
+    TCHAR szNoDevices[256];
+
+    CheckDlgButton(hwndDlg,
+                   IDC_ICON_IN_TASKBAR,
+                   GetSystrayVolumeIconState() ? BST_CHECKED : BST_UNCHECKED);
+
+    LoadString(hApplet, IDS_NO_DEVICES, szNoDevices, _countof(szNoDevices));
 
     NumMixers = mixerGetNumDevs();
     if (!NumMixers)
     {
         EnableWindow(GetDlgItem(hwndDlg, IDC_VOLUME_TRACKBAR), FALSE);
         EnableWindow(GetDlgItem(hwndDlg, IDC_MUTE_CHECKBOX),   FALSE);
-        EnableWindow(GetDlgItem(hwndDlg, IDC_ICON_IN_TASKBAR), FALSE);
         EnableWindow(GetDlgItem(hwndDlg, IDC_ADVANCED_BTN),    FALSE);
-        EnableWindow(GetDlgItem(hwndDlg, IDC_SPEAKER_SET_BTN), FALSE);
         EnableWindow(GetDlgItem(hwndDlg, IDC_SPEAKER_VOL_BTN), FALSE);
         EnableWindow(GetDlgItem(hwndDlg, IDC_ADVANCED2_BTN),   FALSE);
-        SendDlgItemMessage(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconMuted);
+        SendDlgItemMessage(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconNoHW);
+        SetDlgItemText(hwndDlg, IDC_DEVICE_NAME, szNoDevices);
         return;
     }
 
@@ -268,14 +348,21 @@ InitVolumeControls(HWND hwndDlg, PGLOBAL_DATA pGlobalData)
     GetVolumeValue(pGlobalData);
 
     SendDlgItemMessage(hwndDlg, IDC_DEVICE_NAME, WM_SETTEXT, 0, (LPARAM)mxc.szPname);
-    SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETRANGE, (WPARAM)TRUE,
-        (LPARAM)MAKELONG(pGlobalData->volumeMinimum, pGlobalData->volumeMaximum/VOLUME_DIVIDER));
-    SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETPAGESIZE, (WPARAM)FALSE, (LPARAM)1);
-    SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETSEL, (WPARAM)FALSE,
-        (LPARAM)MAKELONG(pGlobalData->volumeMinimum, pGlobalData->volumeValue/VOLUME_DIVIDER));
-    SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)pGlobalData->volumeValue/VOLUME_DIVIDER);
+    SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETRANGE, (WPARAM)TRUE, (LPARAM)MAKELONG(VOLUME_MIN, VOLUME_MAX));
+    SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETTICFREQ, VOLUME_TICFREQ, 0);
+    SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETPAGESIZE, 0, VOLUME_PAGESIZE);
+    SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)(pGlobalData->maxVolume - pGlobalData->volumeMinimum) / pGlobalData->volumeStep);
 }
 
+VOID
+SaveData(HWND hwndDlg)
+{
+    BOOL bShowIcon;
+
+    bShowIcon = (IsDlgButtonChecked(hwndDlg, IDC_ICON_IN_TASKBAR) == BST_CHECKED);
+
+    SetSystrayVolumeIconState(!bShowIcon);
+}
 
 VOID
 LaunchSoundControl(HWND hwndDlg)
@@ -300,8 +387,6 @@ VolumeDlgProc(HWND hwndDlg,
 
     pGlobalData = (PGLOBAL_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
 
-
-
     switch(uMsg)
     {
         case MM_MIXM_LINE_CHANGE:
@@ -322,8 +407,7 @@ VolumeDlgProc(HWND hwndDlg,
         case MM_MIXM_CONTROL_CHANGE:
         {
             GetVolumeValue(pGlobalData);
-            SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETSEL, (WPARAM)FALSE, (LPARAM)MAKELONG(pGlobalData->volumeMinimum, pGlobalData->volumeValue/VOLUME_DIVIDER));
-            SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)pGlobalData->volumeValue/VOLUME_DIVIDER);
+            SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)(pGlobalData->maxVolume - pGlobalData->volumeMinimum) / pGlobalData->volumeStep);
             break;
         }
         case WM_INITDIALOG:
@@ -333,6 +417,7 @@ VolumeDlgProc(HWND hwndDlg,
 
             pGlobalData->hIconUnMuted = LoadImage(hApplet, MAKEINTRESOURCE(IDI_CPLICON), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);
             pGlobalData->hIconMuted = LoadImage(hApplet, MAKEINTRESOURCE(IDI_MUTED_ICON), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);
+            pGlobalData->hIconNoHW = LoadImage(hApplet, MAKEINTRESOURCE(IDI_NO_HW), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);
 
             InitImageInfo(&ImgInfo);
             InitVolumeControls(hwndDlg, pGlobalData);
@@ -375,21 +460,36 @@ VolumeDlgProc(HWND hwndDlg,
             switch (LOWORD(wParam))
             {
                 case IDC_MUTE_CHECKBOX:
-                    SwitchMuteState(pGlobalData);
-                    if (pGlobalData->muteVal)
+                    if (HIWORD(wParam) == BN_CLICKED)
+                    {
+                        SwitchMuteState(pGlobalData);
+                        if (pGlobalData->muteVal)
                         {
-                            SendDlgItemMessage(hwndDlg, IDC_MUTE_CHECKBOX, BM_SETCHECK, (WPARAM)BST_CHECKED, (LPARAM)0);
                             SendDlgItemMessage(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconMuted);
                         }
                         else
                         {
-                            SendDlgItemMessage(hwndDlg, IDC_MUTE_CHECKBOX, BM_SETCHECK, (WPARAM)BST_UNCHECKED, (LPARAM)0);
                             SendDlgItemMessage(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconUnMuted);
                         }
+
+                        PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
+                    }
+                    break;
+
+                case IDC_ICON_IN_TASKBAR:
+                    if (HIWORD(wParam) == BN_CLICKED)
+                    {
+                        PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
+                    }
                     break;
+
                 case IDC_ADVANCED_BTN:
                     LaunchSoundControl(hwndDlg);
                     break;
+
+                case IDC_SPEAKER_VOL_BTN:
+                    SpeakerVolume(hwndDlg);
+                    break;
             }
             break;
         }
@@ -399,20 +499,47 @@ VolumeDlgProc(HWND hwndDlg,
             HWND hVolumeTrackbar = GetDlgItem(hwndDlg, IDC_VOLUME_TRACKBAR);
             if (hVolumeTrackbar == (HWND)lParam)
             {
-                pGlobalData->volumeValue = (DWORD)SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_GETPOS, 0, 0)*VOLUME_DIVIDER;
-                SetVolumeValue(pGlobalData);
-                SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETSEL, (WPARAM)TRUE,
-                    (LPARAM)MAKELONG(pGlobalData->volumeMinimum, pGlobalData->volumeValue/VOLUME_DIVIDER));
+                switch (LOWORD(wParam))
+                {
+                    case TB_THUMBPOSITION:
+                        break;
+
+                    case TB_ENDTRACK:
+                        PlaySound((LPCTSTR)SND_ALIAS_SYSTEMDEFAULT, NULL, SND_ALIAS_ID | SND_ASYNC);
+                        break;
+
+                    default:
+                        SetVolumeValue(pGlobalData,
+                                       (DWORD)SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_GETPOS, 0, 0));
+                        break;
+                }
             }
             break;
         }
 
         case WM_DESTROY:
-            mixerClose(pGlobalData->hMixer);
-            DestroyIcon(pGlobalData->hIconMuted);
-            DestroyIcon(pGlobalData->hIconUnMuted);
-            HeapFree(GetProcessHeap(), 0, pGlobalData);
+            if (pGlobalData)
+            {
+                if (pGlobalData->volumeInitValues)
+                    HeapFree(GetProcessHeap(), 0, pGlobalData->volumeInitValues);
+
+                if (pGlobalData->volumeCurrentValues)
+                    HeapFree(GetProcessHeap(), 0, pGlobalData->volumeCurrentValues);
+
+                mixerClose(pGlobalData->hMixer);
+                DestroyIcon(pGlobalData->hIconMuted);
+                DestroyIcon(pGlobalData->hIconUnMuted);
+                DestroyIcon(pGlobalData->hIconNoHW);
+                HeapFree(GetProcessHeap(), 0, pGlobalData);
+            }
             break;
+
+        case WM_NOTIFY:
+            if (((LPNMHDR)lParam)->code == (UINT)PSN_APPLY)
+            {
+                SaveData(hwndDlg);
+            }
+            return TRUE;
     }
 
     return FALSE;