reactos/dll/cpl/mmsys/speakervolume.c
Thamatip Chitpong 781c247bd3
[MMSYS] Multimedia Control Panel diverse fixes (#4572)
- Use Unicode (WCHAR) instead of TCHAR
- Code formatting
- Use string safe functions
- Close handles after calling `CreateProcess`
- Save sound path as `REG_EXPAND_SZ` only if the path
  contains '%' character, like Windows does
- Fix `wcsdup` leaks

Reviewed-by: Mark Jansen <mark.jansen@reactos.org>
Reviewed-by: Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Reviewed-by: Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
Reviewed-by: Stanislav Motylkov <x86corez@gmail.com>
2022-09-14 20:06:22 +03:00

326 lines
9.7 KiB
C

/*
* PROJECT: ReactOS Multimedia Control Panel
* FILE: dll/cpl/mmsys/speakervolume.c
* PURPOSE: ReactOS Multimedia Control Panel
* PROGRAMMER: Eric Kohl <eric.kohl@reactos.com>
*/
#include "mmsys.h"
typedef struct _PAGE_DATA
{
HMIXER hMixer;
DWORD volumeControlID;
DWORD volumeChannels;
DWORD volumeMinimum;
DWORD volumeMaximum;
DWORD volumeStep;
PMIXERCONTROLDETAILS_UNSIGNED volumeValues;
BOOL volumeSync;
} PAGE_DATA, *PPAGE_DATA;
static
BOOL
OnInitDialog(
PPAGE_DATA pPageData,
HWND hwndDlg)
{
WCHAR szBuffer[256];
MIXERLINEW mxln;
MIXERCONTROLW mxc;
MIXERLINECONTROLSW mxlctrl;
MIXERCONTROLDETAILS mxcd;
INT i, j;
/* Open the mixer */
if (mixerOpen(&pPageData->hMixer, 0, PtrToUlong(hwndDlg), 0, MIXER_OBJECTF_MIXER | CALLBACK_WINDOW) != MMSYSERR_NOERROR)
{
MessageBoxW(hwndDlg, L"Cannot open mixer", NULL, MB_OK);
return FALSE;
}
/* Retrieve the mixer information */
mxln.cbStruct = sizeof(MIXERLINEW);
mxln.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
if (mixerGetLineInfoW((HMIXEROBJ)pPageData->hMixer, &mxln, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_COMPONENTTYPE) != MMSYSERR_NOERROR)
return FALSE;
pPageData->volumeChannels = mxln.cChannels;
/* Retrieve the line information */
mxlctrl.cbStruct = sizeof(MIXERLINECONTROLSW);
mxlctrl.dwLineID = mxln.dwLineID;
mxlctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
mxlctrl.cControls = 1;
mxlctrl.cbmxctrl = sizeof(MIXERCONTROLW);
mxlctrl.pamxctrl = &mxc;
if (mixerGetLineControlsW((HMIXEROBJ)pPageData->hMixer, &mxlctrl, MIXER_OBJECTF_HMIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE) != MMSYSERR_NOERROR)
return FALSE;
pPageData->volumeControlID = mxc.dwControlID;
pPageData->volumeMinimum = mxc.Bounds.dwMinimum;
pPageData->volumeMaximum = mxc.Bounds.dwMaximum;
pPageData->volumeStep = (pPageData->volumeMaximum - pPageData->volumeMinimum) / (VOLUME_MAX - VOLUME_MIN);
/* Allocate a buffer for all channel volume values */
pPageData->volumeValues = HeapAlloc(GetProcessHeap(),
0,
mxln.cChannels * sizeof(MIXERCONTROLDETAILS_UNSIGNED));
if (pPageData->volumeValues == NULL)
return FALSE;
/* Retrieve the channel volume values */
mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
mxcd.dwControlID = mxc.dwControlID;
mxcd.cChannels = mxln.cChannels;
mxcd.cMultipleItems = 0;
mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
mxcd.paDetails = pPageData->volumeValues;
if (mixerGetControlDetailsW((HMIXEROBJ)pPageData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_VALUE) != MMSYSERR_NOERROR)
return FALSE;
/* Initialize the channels */
for (i = 0; i < min(mxln.cChannels, 5); i++)
{
j = i * 4;
/* Set the channel name */
LoadStringW(hApplet, IDS_SPEAKER_LEFT + i, szBuffer, _countof(szBuffer));
SetWindowTextW(GetDlgItem(hwndDlg, 9472 + j), szBuffer);
/* Initialize the channel trackbar */
SendDlgItemMessageW(hwndDlg, 9475 + j, TBM_SETRANGE, (WPARAM)TRUE, MAKELPARAM(VOLUME_MIN, VOLUME_MAX));
SendDlgItemMessageW(hwndDlg, 9475 + j, TBM_SETTICFREQ, VOLUME_TICFREQ, 0);
SendDlgItemMessageW(hwndDlg, 9475 + j, TBM_SETPAGESIZE, 0, VOLUME_PAGESIZE);
SendDlgItemMessageW(hwndDlg, 9475 + j, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)(pPageData->volumeValues[i].dwValue - pPageData->volumeMinimum) / pPageData->volumeStep);
}
/* Hide the unused controls */
for (i = mxln.cChannels; i < 8; i++)
{
j = i * 4;
ShowWindow(GetDlgItem(hwndDlg, 9472 + j), SW_HIDE);
ShowWindow(GetDlgItem(hwndDlg, 9473 + j), SW_HIDE);
ShowWindow(GetDlgItem(hwndDlg, 9474 + j), SW_HIDE);
ShowWindow(GetDlgItem(hwndDlg, 9475 + j), SW_HIDE);
}
return TRUE;
}
static
VOID
OnMixerControlChange(
PPAGE_DATA pPageData,
HWND hwndDlg)
{
MIXERCONTROLDETAILS mxcd;
INT i, j;
/* Retrieve the channel volume values */
mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
mxcd.dwControlID = pPageData->volumeControlID;
mxcd.cChannels = pPageData->volumeChannels;
mxcd.cMultipleItems = 0;
mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
mxcd.paDetails = pPageData->volumeValues;
if (mixerGetControlDetailsW((HMIXEROBJ)pPageData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_VALUE) != MMSYSERR_NOERROR)
return;
for (i = 0; i < pPageData->volumeChannels; i++)
{
j = i * 4;
SendDlgItemMessageW(hwndDlg, 9475 + j, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)(pPageData->volumeValues[i].dwValue - pPageData->volumeMinimum) / pPageData->volumeStep);
}
}
static
VOID
OnHScroll(
PPAGE_DATA pPageData,
HWND hwndDlg,
WPARAM wParam,
LPARAM lParam)
{
MIXERCONTROLDETAILS mxcd;
DWORD dwValue, dwPosition;
INT id, idx, i, j;
id = (INT)GetWindowLongPtrW((HWND)lParam, GWLP_ID);
if (id < 9475 || id > 9503)
return;
if ((id - 9475) % 4 != 0)
return;
dwPosition = (DWORD)SendDlgItemMessageW(hwndDlg, id, TBM_GETPOS, 0, 0);
if (dwPosition == VOLUME_MIN)
dwValue = pPageData->volumeMinimum;
else if (dwPosition == VOLUME_MAX)
dwValue = pPageData->volumeMaximum;
else
dwValue = (dwPosition * pPageData->volumeStep) + pPageData->volumeMinimum;
if (pPageData->volumeSync)
{
for (i = 0; i < pPageData->volumeChannels; i++)
{
j = 9475 + (i * 4);
if (j != id)
SendDlgItemMessageW(hwndDlg, j, TBM_SETPOS, (WPARAM)TRUE, dwPosition);
pPageData->volumeValues[i].dwValue = dwValue;
}
}
else
{
idx = (id - 9475) / 4;
pPageData->volumeValues[idx].dwValue = dwValue;
}
mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
mxcd.dwControlID = pPageData->volumeControlID;
mxcd.cChannels = pPageData->volumeChannels;
mxcd.cMultipleItems = 0;
mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
mxcd.paDetails = pPageData->volumeValues;
if (mixerSetControlDetails((HMIXEROBJ)pPageData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE) != MMSYSERR_NOERROR)
return;
}
static
VOID
OnSetDefaults(
PPAGE_DATA pPageData,
HWND hwndDlg)
{
MIXERCONTROLDETAILS mxcd;
DWORD dwValue, i;
dwValue = ((VOLUME_MAX - VOLUME_MIN) / 2 * pPageData->volumeStep) + pPageData->volumeMinimum;
for (i = 0; i < pPageData->volumeChannels; i++)
pPageData->volumeValues[i].dwValue = dwValue;
mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
mxcd.dwControlID = pPageData->volumeControlID;
mxcd.cChannels = pPageData->volumeChannels;
mxcd.cMultipleItems = 0;
mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
mxcd.paDetails = pPageData->volumeValues;
if (mixerSetControlDetails((HMIXEROBJ)pPageData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE) != MMSYSERR_NOERROR)
return;
}
INT_PTR
CALLBACK
SpeakerVolumeDlgProc(
HWND hwndDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
PPAGE_DATA pPageData;
pPageData = (PPAGE_DATA)GetWindowLongPtrW(hwndDlg, DWLP_USER);
switch (uMsg)
{
case WM_INITDIALOG:
pPageData = (PPAGE_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PAGE_DATA));
SetWindowLongPtrW(hwndDlg, DWLP_USER, (LONG_PTR)pPageData);
if (pPageData)
{
OnInitDialog(pPageData, hwndDlg);
}
break;
case WM_DESTROY:
if (pPageData)
{
if (pPageData->volumeValues)
HeapFree(GetProcessHeap(), 0, pPageData->volumeValues);
if (pPageData->hMixer)
mixerClose(pPageData->hMixer);
HeapFree(GetProcessHeap(), 0, pPageData);
pPageData = NULL;
SetWindowLongPtrW(hwndDlg, DWLP_USER, (LONG_PTR)NULL);
}
break;
case WM_HSCROLL:
if (pPageData)
{
OnHScroll(pPageData, hwndDlg, wParam, lParam);
PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
}
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case 9504:
if (HIWORD(wParam) == BN_CLICKED)
pPageData->volumeSync = (SendDlgItemMessageW(hwndDlg, 9504, BM_GETCHECK, 0, 0) == BST_CHECKED);
break;
case 9505:
OnSetDefaults(pPageData, hwndDlg);
PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
break;
}
break;
case WM_NOTIFY:
return TRUE;
case MM_MIXM_CONTROL_CHANGE:
if (pPageData)
OnMixerControlChange(pPageData, hwndDlg);
break;
}
return FALSE;
}
INT_PTR
SpeakerVolume(
HWND hwndDlg)
{
PROPSHEETPAGEW psp[1];
PROPSHEETHEADERW psh;
ZeroMemory(&psh, sizeof(PROPSHEETHEADERW));
psh.dwSize = sizeof(PROPSHEETHEADERW);
psh.dwFlags = PSH_PROPSHEETPAGE;
psh.hwndParent = hwndDlg;
psh.hInstance = hApplet;
psh.pszCaption = MAKEINTRESOURCE(IDS_SPEAKER_VOLUME);
psh.nPages = _countof(psp);
psh.nStartPage = 0;
psh.ppsp = psp;
InitPropSheetPage(&psp[0], IDD_MULTICHANNEL, SpeakerVolumeDlgProc);
psp[0].dwFlags |= PSP_USETITLE;
psp[0].hInstance = hApplet;
psp[0].pszTitle = MAKEINTRESOURCE(IDS_SPEAKER_VOLUME);
return (LONG)(PropertySheetW(&psh) != -1);
}