You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3000 lines
82 KiB
3000 lines
82 KiB
/*****************************************************************************
|
|
*
|
|
* Component: sndvol32.exe
|
|
* File: volume.c
|
|
* Purpose: main application module
|
|
*
|
|
* Copyright (c) 1985-1999 Microsoft Corporation
|
|
*
|
|
*****************************************************************************/
|
|
#include <windows.h>
|
|
#include <windowsx.h>
|
|
#include <mmsystem.h>
|
|
#include <commctrl.h>
|
|
#include <shellapi.h>
|
|
#include <dbt.h>
|
|
#include <htmlhelp.h>
|
|
#include <regstr.h>
|
|
|
|
#include "vu.h"
|
|
#include "dlg.h"
|
|
#include "volids.h"
|
|
|
|
#include "volumei.h"
|
|
#include "utils.h"
|
|
#include "stdlib.h"
|
|
#include "helpids.h"
|
|
|
|
#if(WINVER >= 0x040A)
|
|
//Support for new WM_DEVICECHANGE behaviour in NT5
|
|
/////////////////////////////////////////////////
|
|
#include <objbase.h>
|
|
#include <setupapi.h>
|
|
#include <cfgmgr32.h>
|
|
#include <initguid.h>
|
|
#include <devguid.h>
|
|
#include <mmddkp.h>
|
|
#include <ks.h>
|
|
#include <ksmedia.h>
|
|
#include <wchar.h>
|
|
|
|
#define STRSAFE_LIB
|
|
#include <strsafe.h>
|
|
|
|
#define HMIXER_INDEX(i) ((HMIXER)IntToPtr(i))
|
|
|
|
HDEVNOTIFY DeviceEventContext = NULL;
|
|
BOOL bUseHandle = FALSE; //Indicates whether a handle is being used for device notification,
|
|
//instead of the general KSCATEGORY_AUDIO
|
|
//////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////
|
|
#endif /* WINVER >= 0x040A */
|
|
|
|
void Volume_SetControl(PMIXUIDIALOG pmxud, HWND hctl, int iLine, int iCtl);
|
|
void Volume_GetControl(PMIXUIDIALOG pmxud, HWND hctl, int iLine, int iCtl);
|
|
DWORD Volume_DialogBox(PMIXUIDIALOG pmxud);
|
|
void Volume_Cleanup(PMIXUIDIALOG pmxud);
|
|
void Volume_InitLine(PMIXUIDIALOG pmxud, DWORD iLine);
|
|
HRESULT GetDestLineID(int mxid, DWORD *piDest);
|
|
HRESULT GetSrcLineID(int mxid, DWORD *piDest);
|
|
HRESULT GetDestination(DWORD mxid, int *piDest);
|
|
HICON GetAppIcon (HINSTANCE hInst, UINT uiMixID);
|
|
void FreeAppIcon ();
|
|
HKEY OpenDeviceRegKey (UINT uiMixID, REGSAM sam);
|
|
PTCHAR GetInterfaceName (DWORD dwMixerID);
|
|
HKEY OpenDeviceBrandRegKey (UINT uiMixID);
|
|
|
|
|
|
/* string declarations */
|
|
const TCHAR gszParentClass[] = TEXT( "SNDVOL32" );
|
|
|
|
const TCHAR gszAppClassName[] = TEXT( "Volume Control" );
|
|
const TCHAR gszTrayClassName[] = TEXT( "Tray Volume" );
|
|
|
|
|
|
#ifdef DEBUG
|
|
static void _dlout(LPSTR szExp, LPSTR szFile, UINT uLine)
|
|
{
|
|
char sz[256];
|
|
StringCchPrintfA(sz, SIZEOF(sz), "%s, file %s, line %u\r\n", szExp, szFile, uLine);
|
|
OutputDebugStringA(sz);
|
|
}
|
|
#define dlout(exp) (void)(_dlout(exp, __FILE__, __LINE__), 0)
|
|
#else
|
|
#define dlout(exp) ((void)0)
|
|
#endif
|
|
|
|
|
|
/* app global
|
|
* */
|
|
TCHAR gszHelpFileName[MAX_PATH];
|
|
TCHAR gszHtmlHelpFileName[MAX_PATH];
|
|
BOOL gfIsRTL;
|
|
BOOL fCanDismissWindow = FALSE;
|
|
BOOL gfRecord = FALSE;
|
|
HICON ghiconApp = NULL;
|
|
static HHOOK fpfnOldMsgFilter;
|
|
static HOOKPROC fpfnMsgHook;
|
|
//Data used for supporting context menu help
|
|
BOOL bF1InMenu=FALSE; //If true F1 was pressed on a menu item.
|
|
UINT currMenuItem=0; //The current selected menu item if any.
|
|
static HWND ghwndApp=NULL;
|
|
|
|
/*
|
|
* Number of uniquely supported devices.
|
|
*
|
|
* */
|
|
int Volume_NumDevs()
|
|
{
|
|
int cNumDevs = 0;
|
|
|
|
#pragma message("----Nonmixer issue here.")
|
|
// cNumDevs = Nonmixer_GetNumDevs();
|
|
cNumDevs += Mixer_GetNumDevs();
|
|
|
|
return cNumDevs;
|
|
}
|
|
|
|
/*
|
|
* Volume_EndDialog
|
|
*
|
|
* */
|
|
void Volume_EndDialog(
|
|
PMIXUIDIALOG pmxud,
|
|
DWORD dwErr,
|
|
MMRESULT mmr)
|
|
{
|
|
pmxud->dwReturn = dwErr;
|
|
if (dwErr == MIXUI_MMSYSERR)
|
|
pmxud->mmr = mmr;
|
|
|
|
if (IsWindow(pmxud->hwnd))
|
|
PostMessage(pmxud->hwnd, WM_CLOSE, 0, 0);
|
|
}
|
|
|
|
/*
|
|
* Volume_OnMenuCommand
|
|
*
|
|
* */
|
|
BOOL Volume_OnMenuCommand(
|
|
HWND hwnd,
|
|
int id,
|
|
HWND hctl,
|
|
UINT unotify)
|
|
{
|
|
PMIXUIDIALOG pmxud = GETMIXUIDIALOG(hwnd);
|
|
|
|
switch(id)
|
|
{
|
|
case IDM_PROPERTIES:
|
|
if (Properties(pmxud, hwnd))
|
|
{
|
|
Volume_GetSetStyle(&pmxud->dwStyle, SET);
|
|
Volume_EndDialog(pmxud, MIXUI_RESTART, 0);
|
|
}
|
|
break;
|
|
|
|
case IDM_HELPTOPICS:
|
|
SendMessage(pmxud->hParent, MYWM_HELPTOPICS, 0, 0L);
|
|
break;
|
|
|
|
case IDM_HELPABOUT:
|
|
{
|
|
TCHAR ach[256];
|
|
GetWindowText(hwnd, ach, SIZEOF(ach));
|
|
ShellAbout(hwnd
|
|
, ach
|
|
, NULL
|
|
, (HICON)SendMessage(hwnd, WM_QUERYDRAGICON, 0, 0L));
|
|
break;
|
|
}
|
|
|
|
case IDM_ADVANCED:
|
|
{
|
|
HMENU hmenu;
|
|
|
|
pmxud->dwStyle ^= MXUD_STYLEF_ADVANCED;
|
|
|
|
hmenu = GetMenu(hwnd);
|
|
if (hmenu)
|
|
{
|
|
CheckMenuItem(hmenu, IDM_ADVANCED, MF_BYCOMMAND
|
|
| ((pmxud->dwStyle & MXUD_STYLEF_ADVANCED)?MF_CHECKED:MF_UNCHECKED));
|
|
}
|
|
Volume_GetSetStyle(&pmxud->dwStyle, SET);
|
|
Volume_EndDialog(pmxud, MIXUI_RESTART, 0);
|
|
break;
|
|
}
|
|
|
|
case IDM_SMALLMODESWITCH:
|
|
if (!(pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER))
|
|
{
|
|
pmxud->dwStyle ^= MXUD_STYLEF_SMALL;
|
|
if (pmxud->dwStyle & MXUD_STYLEF_SMALL)
|
|
{
|
|
pmxud->dwStyle &= ~MXUD_STYLEF_STATUS;
|
|
}
|
|
else
|
|
pmxud->dwStyle |= MXUD_STYLEF_STATUS;
|
|
|
|
Volume_GetSetStyle(&pmxud->dwStyle, SET);
|
|
Volume_EndDialog(pmxud, MIXUI_RESTART, 0);
|
|
}
|
|
break;
|
|
|
|
case IDM_EXIT:
|
|
Volume_EndDialog(pmxud, MIXUI_EXIT, 0);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Volume_OnCommand
|
|
*
|
|
* - Process WM_COMMAND
|
|
*
|
|
* Note: We need a 2 way mapping. Dialog control -> Mixer control
|
|
* and Mixer control -> Dialog control.
|
|
*
|
|
* */
|
|
void Volume_OnCommand(
|
|
HWND hdlg,
|
|
int id,
|
|
HWND hctl,
|
|
UINT unotify)
|
|
{
|
|
int iMixerLine;
|
|
PMIXUIDIALOG pmxud = GETMIXUIDIALOG(hdlg);
|
|
|
|
//
|
|
// Filter menu messages
|
|
//
|
|
if (Volume_OnMenuCommand(hdlg, id, hctl, unotify))
|
|
return;
|
|
|
|
// Each control is offset from the original template control by IDOFFSET.
|
|
// e.g.
|
|
// IDC_VOLUME, IDC_VOLUME+IDOFFSET, .. IDC_VOLUME+(IDOFFSET*cMixerLines)
|
|
//
|
|
iMixerLine = id/IDOFFSET - 1;
|
|
switch ((id % IDOFFSET) + IDC_MIXERCONTROLS)
|
|
{
|
|
case IDC_SWITCH:
|
|
Volume_SetControl(pmxud, hctl, iMixerLine, MIXUI_SWITCH);
|
|
break;
|
|
case IDC_ADVANCED:
|
|
if (MXUD_ADVANCED(pmxud) && !(pmxud->dwStyle & MXUD_STYLEF_SMALL))
|
|
Volume_SetControl(pmxud, hctl, iMixerLine, MIXUI_ADVANCED);
|
|
break;
|
|
case IDC_MULTICHANNEL:
|
|
Volume_SetControl(pmxud, hctl, iMixerLine, MIXUI_MULTICHANNEL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Volume_GetLineItem
|
|
*
|
|
* - Helper function.
|
|
* */
|
|
HWND Volume_GetLineItem(
|
|
HWND hdlg,
|
|
DWORD iLine,
|
|
DWORD idCtrl)
|
|
{
|
|
HWND hwnd;
|
|
DWORD id;
|
|
|
|
id = (iLine * IDOFFSET) + idCtrl;
|
|
hwnd = GetDlgItem(hdlg, id);
|
|
|
|
return hwnd;
|
|
}
|
|
|
|
/* - - - - - - - - - */
|
|
|
|
/*
|
|
* Volume_TimeProc
|
|
*
|
|
* This is the callback for the periodic timer that does updates for
|
|
* controls that need to be polled. We only allocate one per app to keep
|
|
* the number of callbacks down.
|
|
*/
|
|
void CALLBACK Volume_TimeProc(
|
|
UINT idEvent,
|
|
UINT uReserved,
|
|
DWORD_PTR dwUser,
|
|
DWORD_PTR dwReserved1,
|
|
DWORD_PTR dwReserved2)
|
|
{
|
|
PMIXUIDIALOG pmxud = (PMIXUIDIALOG)dwUser;
|
|
|
|
if (!(pmxud->dwFlags & MXUD_FLAGSF_USETIMER))
|
|
return;
|
|
|
|
if (pmxud->cTimeInQueue < 5)
|
|
{
|
|
pmxud->cTimeInQueue++;
|
|
PostMessage(pmxud->hwnd, MYWM_TIMER, 0, 0L);
|
|
}
|
|
}
|
|
|
|
|
|
#define PROPATOM TEXT("dingprivprop")
|
|
const TCHAR gszDingPropAtom[] = PROPATOM;
|
|
#define SETPROP(x,y) SetProp((x), gszDingPropAtom, (HANDLE)(y))
|
|
#define GETPROP(x) (PMIXUIDIALOG)GetProp((x), gszDingPropAtom)
|
|
#define REMOVEPROP(x) RemoveProp(x,gszDingPropAtom)
|
|
|
|
LRESULT CALLBACK Volume_TrayVolProc(
|
|
HWND hwnd,
|
|
UINT umsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
PMIXUIDIALOG pmxud = (PMIXUIDIALOG)GETPROP(hwnd);
|
|
static const TCHAR cszDefSnd[] = TEXT(".Default");
|
|
|
|
if (umsg == WM_KILLFOCUS)
|
|
{
|
|
//
|
|
// if we've just been made inactive via keyboard, clear the signal
|
|
//
|
|
pmxud->dwTrayInfo &= ~MXUD_TRAYINFOF_SIGNAL;
|
|
}
|
|
|
|
if (umsg == WM_KEYUP && (pmxud->dwTrayInfo & MXUD_TRAYINFOF_SIGNAL))
|
|
{
|
|
if (wParam == VK_UP || wParam == VK_DOWN || wParam == VK_END ||
|
|
wParam == VK_HOME || wParam == VK_LEFT || wParam == VK_RIGHT ||
|
|
wParam == VK_PRIOR || wParam == VK_NEXT || wParam == VK_SPACE)
|
|
{
|
|
PlaySound(cszDefSnd, NULL, SND_ASYNC | SND_ALIAS);
|
|
pmxud->dwTrayInfo &= ~MXUD_TRAYINFOF_SIGNAL;
|
|
}
|
|
}
|
|
|
|
if (umsg == WM_LBUTTONUP && (pmxud->dwTrayInfo & MXUD_TRAYINFOF_SIGNAL))
|
|
{
|
|
PlaySound(cszDefSnd, NULL, SND_ASYNC | SND_ALIAS);
|
|
pmxud->dwTrayInfo &= ~MXUD_TRAYINFOF_SIGNAL;
|
|
|
|
}
|
|
return CallWindowProc(pmxud->lpfnTrayVol, hwnd, umsg, wParam, lParam);
|
|
}
|
|
|
|
|
|
#if(WINVER >= 0x040A)
|
|
|
|
void DeviceChange_Cleanup()
|
|
{
|
|
if (DeviceEventContext)
|
|
{
|
|
UnregisterDeviceNotification(DeviceEventContext);
|
|
DeviceEventContext = 0;
|
|
}
|
|
|
|
bUseHandle = FALSE;
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
**************************************************************************************************
|
|
GetDeviceHandle()
|
|
|
|
given a mixerID this functions opens its corresponding device handle. This handle can be used
|
|
to register for DeviceNotifications.
|
|
|
|
dwMixerID -- The mixer ID
|
|
phDevice -- a pointer to a handle. This pointer will hold the handle value if the function is
|
|
successful
|
|
|
|
return values -- If the handle could be obtained successfully the return vlaue is TRUE.
|
|
|
|
**************************************************************************************************
|
|
*/
|
|
BOOL GetDeviceHandle(DWORD dwMixerID, HANDLE *phDevice)
|
|
{
|
|
MMRESULT mmr;
|
|
ULONG cbSize=0;
|
|
TCHAR *szInterfaceName=NULL;
|
|
|
|
//Query for the Device interface name
|
|
mmr = mixerMessage((HMIXER)ULongToPtr(dwMixerID), DRV_QUERYDEVICEINTERFACESIZE, (DWORD_PTR)&cbSize, 0L);
|
|
if(MMSYSERR_NOERROR == mmr)
|
|
{
|
|
szInterfaceName = (TCHAR *)GlobalAllocPtr(GHND, (cbSize+1)*sizeof(TCHAR));
|
|
if(!szInterfaceName)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
mmr = mixerMessage((HMIXER)ULongToPtr(dwMixerID), DRV_QUERYDEVICEINTERFACE, (DWORD_PTR)szInterfaceName, cbSize);
|
|
if(MMSYSERR_NOERROR != mmr)
|
|
{
|
|
GlobalFreePtr(szInterfaceName);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//Get an handle on the device interface name.
|
|
*phDevice = CreateFile(szInterfaceName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
GlobalFreePtr(szInterfaceName);
|
|
if(INVALID_HANDLE_VALUE == *phDevice)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* DeviceChange_Init()
|
|
* First time initialization for WM_DEVICECHANGE messages
|
|
*
|
|
* On NT 5.0, you have to register for device notification
|
|
*/
|
|
BOOL DeviceChange_Init(HWND hWnd, DWORD dwMixerID)
|
|
{
|
|
|
|
DEV_BROADCAST_HANDLE DevBrodHandle;
|
|
DEV_BROADCAST_DEVICEINTERFACE dbi;
|
|
HANDLE hMixerDevice;
|
|
|
|
//If we had registered already for device notifications, unregister ourselves.
|
|
DeviceChange_Cleanup();
|
|
|
|
//If we get the device handle register for device notifications on it.
|
|
if(GetDeviceHandle(dwMixerID, &hMixerDevice))
|
|
{
|
|
memset(&DevBrodHandle, 0, sizeof(DEV_BROADCAST_HANDLE));
|
|
|
|
DevBrodHandle.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
|
|
DevBrodHandle.dbch_devicetype = DBT_DEVTYP_HANDLE;
|
|
DevBrodHandle.dbch_handle = hMixerDevice;
|
|
|
|
DeviceEventContext = RegisterDeviceNotification(hWnd, &DevBrodHandle,
|
|
DEVICE_NOTIFY_WINDOW_HANDLE);
|
|
|
|
if(hMixerDevice)
|
|
{
|
|
CloseHandle(hMixerDevice);
|
|
hMixerDevice = NULL;
|
|
}
|
|
|
|
if(DeviceEventContext)
|
|
{
|
|
bUseHandle = TRUE;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if(!DeviceEventContext)
|
|
{
|
|
//Register for notifications from all audio devices. KSCATEGORY_AUDIO gives notifications
|
|
//on device arrival and removal. We cannot identify which device the notification has arrived for
|
|
//but we can take some precautionary measures on these messages so that we do not crash.
|
|
dbi.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
|
|
dbi.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
|
|
dbi.dbcc_reserved = 0;
|
|
dbi.dbcc_classguid = KSCATEGORY_AUDIO;
|
|
dbi.dbcc_name[0] = TEXT('\0');
|
|
|
|
DeviceEventContext = RegisterDeviceNotification(hWnd,
|
|
(PVOID)&dbi,
|
|
DEVICE_NOTIFY_WINDOW_HANDLE);
|
|
if(!DeviceEventContext)
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#endif /* WINVER >= 0x040A */
|
|
|
|
|
|
//fixes bug where controls are lopped off on high-contract extra-large modes
|
|
void AdjustForStatusBar(PMIXUIDIALOG pmxud)
|
|
{
|
|
RECT statusrect, windowrect;
|
|
statusrect.bottom = 0;
|
|
statusrect.top = 0;
|
|
|
|
if (pmxud)
|
|
{
|
|
if (pmxud->hStatus)
|
|
{
|
|
GetClientRect(pmxud->hStatus,&statusrect);
|
|
GetWindowRect(pmxud->hwnd,&windowrect);
|
|
|
|
if (statusrect.bottom - statusrect.top > 20)
|
|
{
|
|
int y_adjustment = (statusrect.bottom - statusrect.top) - 20;
|
|
|
|
MoveWindow(pmxud->hwnd, windowrect.left, windowrect.top, windowrect.right - windowrect.left,
|
|
(windowrect.bottom - windowrect.top) + y_adjustment, FALSE );
|
|
|
|
GetClientRect(pmxud->hwnd,&windowrect);
|
|
|
|
MoveWindow(pmxud->hStatus, statusrect.left, windowrect.bottom - (statusrect.bottom - statusrect.top), statusrect.right - statusrect.left,
|
|
statusrect.bottom - statusrect.top, FALSE );
|
|
}
|
|
} //end if hStatus is valid
|
|
} //end if pmxud is not null
|
|
}
|
|
|
|
/*
|
|
*
|
|
* */
|
|
BOOL Volume_Init(
|
|
PMIXUIDIALOG pmxud)
|
|
{
|
|
DWORD iLine, ictrl;
|
|
RECT rc, rcWnd;
|
|
|
|
|
|
if (!Mixer_Init(pmxud) && !Nonmixer_Init(pmxud))
|
|
Volume_EndDialog(pmxud, MIXUI_EXIT, 0);
|
|
|
|
//
|
|
// For all line controls, make sure we initialize the values.
|
|
//
|
|
for (iLine = 0; iLine < pmxud->cmxul; iLine++)
|
|
{
|
|
//
|
|
// init the ui control
|
|
//
|
|
Volume_InitLine(pmxud, iLine);
|
|
|
|
for (ictrl = MIXUI_FIRST; ictrl <= MIXUI_LAST; ictrl++)
|
|
{
|
|
PMIXUICTRL pmxc = &pmxud->amxul[iLine].acr[ictrl];
|
|
|
|
//
|
|
// set initial settings
|
|
//
|
|
if (pmxc->state == MIXUI_CONTROL_INITIALIZED)
|
|
Volume_GetControl(pmxud, pmxc->hwnd, iLine, ictrl);
|
|
}
|
|
}
|
|
|
|
if (!(pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER))
|
|
{
|
|
RECT rcBase;
|
|
HWND hBase;
|
|
RECT rcAdv,rcBorder;
|
|
HWND hAdv,hBorder;
|
|
DWORD i;
|
|
LONG lPrev;
|
|
POINT pos;
|
|
HMENU hmenu;
|
|
HMONITOR hMonitor;
|
|
MONITORINFO monitorInfo;
|
|
|
|
if (GetWindowRect(pmxud->hwnd, &rcWnd))
|
|
{
|
|
if (pmxud->cmxul == 1)
|
|
{
|
|
// Adjust size if small
|
|
if (pmxud->dwStyle & MXUD_STYLEF_SMALL)
|
|
rcWnd.right -= 20;
|
|
ShowWindow(GetDlgItem(pmxud->hwnd, IDC_BORDER), SW_HIDE);
|
|
}
|
|
|
|
if (!Volume_GetSetRegistryRect(pmxud->szMixer
|
|
, pmxud->szDestination
|
|
, &rc
|
|
, GET))
|
|
{
|
|
rc.left = rcWnd.left;
|
|
rc.top = rcWnd.top;
|
|
}
|
|
else
|
|
{
|
|
// Check if the rect is visible is any of the monitors
|
|
if (!MonitorFromRect(&rc, MONITOR_DEFAULTTONULL))
|
|
{
|
|
//The window is not visible. Let's center it in the nearest monitor.
|
|
//Note: the window could be in this state if (1) the display mode was changed from
|
|
//a high-resolution to a lower resolution, with the mixer in the corner. Or,
|
|
//(2) the multi-mon configuration was rearranged.
|
|
hMonitor = MonitorFromRect(&rc, MONITOR_DEFAULTTONEAREST);
|
|
if (hMonitor)
|
|
{
|
|
monitorInfo.cbSize = sizeof(MONITORINFO);
|
|
if (GetMonitorInfo(hMonitor, &monitorInfo))
|
|
{
|
|
rc.left = ((monitorInfo.rcWork.right - monitorInfo.rcWork.left) - (rcWnd.right - rcWnd.left)) / 2; //center in x
|
|
rc.top = ((monitorInfo.rcWork.bottom - monitorInfo.rcWork.top) - (rcWnd.bottom - rcWnd.top)) / 3; //and a little towards the top
|
|
}
|
|
}
|
|
}
|
|
//else, the window is visible, so let's leave the (x,y) as read from the settings
|
|
}
|
|
//
|
|
// Adjusted bottom to match switch bottom
|
|
//
|
|
if (!(pmxud->dwStyle & MXUD_STYLEF_SMALL))
|
|
{
|
|
hBase = GetDlgItem(pmxud->hwnd, IDC_SWITCH);
|
|
if (hBase && GetWindowRect(hBase, &rcBase))
|
|
{
|
|
rcWnd.bottom = rcBase.bottom;
|
|
}
|
|
|
|
//
|
|
// Adjusted bottom to match "Advanced" bottom
|
|
//
|
|
if (MXUD_ADVANCED(pmxud))
|
|
{
|
|
hAdv = GetDlgItem(pmxud->hwnd, IDC_ADVANCED);
|
|
if (hAdv && GetWindowRect(hAdv, &rcAdv))
|
|
{
|
|
lPrev = rcWnd.bottom;
|
|
rcWnd.bottom = rcAdv.bottom;
|
|
|
|
//
|
|
// Adjust height of all border lines
|
|
//
|
|
lPrev = rcWnd.bottom - lPrev;
|
|
for (i = 0; i < pmxud->cmxul; i++)
|
|
{
|
|
hBorder = GetDlgItem(pmxud->hwnd,
|
|
IDC_BORDER+(IDOFFSET*i));
|
|
if (hBorder && GetWindowRect(hBorder, &rcBorder))
|
|
{
|
|
MapWindowPoints(NULL, pmxud->hwnd, (LPPOINT)&rcBorder, 2);
|
|
pos.x = rcBorder.left;
|
|
pos.y = rcBorder.top;
|
|
MoveWindow(hBorder
|
|
, pos.x
|
|
, pos.y
|
|
, rcBorder.right - rcBorder.left
|
|
, (rcBorder.bottom - rcBorder.top) + lPrev
|
|
, TRUE );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// Allocate some more space.
|
|
//
|
|
rcWnd.bottom += 28;
|
|
}
|
|
|
|
MoveWindow(pmxud->hwnd, rc.left, rc.top, rcWnd.right - rcWnd.left,
|
|
rcWnd.bottom - rcWnd.top, FALSE );
|
|
|
|
//
|
|
// Tack on the status bar after resizing the dialog
|
|
//
|
|
|
|
//init status bar hwnd variable
|
|
pmxud->hStatus = NULL;
|
|
|
|
if (pmxud->dwStyle & MXUD_STYLEF_STATUS)
|
|
{
|
|
MapWindowPoints(NULL, pmxud->hwnd, (LPPOINT)&rcWnd, 2);
|
|
pos.x = rcWnd.left;
|
|
pos.y = rcWnd.bottom;
|
|
|
|
pmxud->hStatus = CreateWindowEx ( gfIsRTL ? WS_EX_LEFTSCROLLBAR | WS_EX_RIGHT | WS_EX_RTLREADING : 0
|
|
, STATUSCLASSNAME
|
|
, TEXT ("X")
|
|
, WS_VISIBLE | WS_CHILD
|
|
, 0
|
|
, pos.y
|
|
, rcWnd.right - rcWnd.left
|
|
, 14
|
|
, pmxud->hwnd
|
|
, NULL
|
|
, pmxud->hInstance
|
|
, NULL);
|
|
|
|
if (pmxud->hStatus)
|
|
{
|
|
SendMessage(pmxud->hStatus, WM_SETTEXT, 0,
|
|
(LPARAM)(LPVOID)(LPTSTR)pmxud->szMixer);
|
|
}
|
|
else
|
|
pmxud->dwStyle ^= MXUD_STYLEF_STATUS;
|
|
}
|
|
|
|
AdjustForStatusBar(pmxud);
|
|
|
|
hmenu = GetMenu(pmxud->hwnd);
|
|
CheckMenuItem(hmenu, IDM_ADVANCED, MF_BYCOMMAND
|
|
| ((pmxud->dwStyle & MXUD_STYLEF_ADVANCED)?MF_CHECKED:MF_UNCHECKED));
|
|
|
|
if (pmxud->dwStyle & MXUD_STYLEF_SMALL || pmxud->dwFlags & MXUD_FLAGSF_NOADVANCED)
|
|
EnableMenuItem(hmenu, IDM_ADVANCED, MF_BYCOMMAND | MF_GRAYED);
|
|
|
|
}
|
|
|
|
if (pmxud->dwFlags & MXUD_FLAGSF_USETIMER)
|
|
{
|
|
pmxud->cTimeInQueue = 0;
|
|
pmxud->uTimerID = timeSetEvent(100
|
|
, 50
|
|
, Volume_TimeProc
|
|
, (DWORD_PTR)pmxud
|
|
, TIME_PERIODIC);
|
|
if (!pmxud->uTimerID)
|
|
pmxud->dwFlags &= ~MXUD_FLAGSF_USETIMER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WNDPROC lpfnOldTrayVol;
|
|
HWND hVol;
|
|
|
|
hVol = pmxud->amxul[0].acr[MIXUI_VOLUME].hwnd;
|
|
lpfnOldTrayVol = SubclassWindow(hVol, Volume_TrayVolProc);
|
|
|
|
if (lpfnOldTrayVol)
|
|
{
|
|
pmxud->lpfnTrayVol = lpfnOldTrayVol;
|
|
SETPROP(hVol, pmxud);
|
|
}
|
|
}
|
|
|
|
#if(WINVER >= 0x040A)
|
|
//Register for WM_DEVICECHANGE messages
|
|
DeviceChange_Init(pmxud->hwnd, pmxud->mxid);
|
|
#endif /* WINVER >= 0x040A */
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Volume_OnInitDialog
|
|
*
|
|
* - Process WM_INITDIALOG
|
|
*
|
|
* */
|
|
BOOL Volume_OnInitDialog(
|
|
HWND hwnd,
|
|
HWND hwndFocus,
|
|
LPARAM lParam)
|
|
{
|
|
PMIXUIDIALOG pmxud;
|
|
RECT rc;
|
|
|
|
//
|
|
// set app instance data
|
|
//
|
|
SETMIXUIDIALOG(hwnd, lParam);
|
|
|
|
pmxud = (PMIXUIDIALOG)(LPVOID)lParam;
|
|
pmxud->hwnd = hwnd;
|
|
|
|
if (!Volume_Init(pmxud))
|
|
{
|
|
Volume_EndDialog(pmxud, MIXUI_EXIT, 0);
|
|
}
|
|
else
|
|
{
|
|
if (pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER)
|
|
PostMessage(hwnd, MYWM_WAKEUP, 0, 0);
|
|
}
|
|
|
|
//
|
|
// If we are so big that we need a scroll bar, then make one.
|
|
//
|
|
rc.top = rc.bottom = 0;
|
|
rc.left = 60; // typical width of a dialog template
|
|
rc.right = Dlg_HorizSize(pmxud->lpDialog);
|
|
MapDialogRect(hwnd, &rc);
|
|
pmxud->cxDlgContent = rc.right;
|
|
pmxud->cxScroll = rc.left;
|
|
pmxud->xOffset = 0;
|
|
|
|
GetClientRect(hwnd, &rc);
|
|
pmxud->xOffset = 0;
|
|
pmxud->cxDlgWidth = rc.right;
|
|
if (rc.right < pmxud->cxDlgContent)
|
|
{
|
|
RECT rcWindow;
|
|
SCROLLINFO si;
|
|
LONG lStyle = GetWindowStyle(hwnd);
|
|
SetWindowLong(hwnd, GWL_STYLE, lStyle | WS_HSCROLL);
|
|
|
|
si.cbSize = sizeof(si);
|
|
si.fMask = SIF_PAGE | SIF_RANGE;
|
|
si.nMin = 0;
|
|
si.nMax = pmxud->cxDlgContent - 1; // endpoint is inclusive
|
|
si.nPage = rc.right;
|
|
SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
|
|
|
|
// Grow the dialog to accomodate the scrollbar
|
|
GetWindowRect(hwnd, &rcWindow);
|
|
SetWindowPos(hwnd, NULL, 0, 0, rcWindow.right - rcWindow.left,
|
|
rcWindow.bottom - rcWindow.top + GetSystemMetrics(SM_CYHSCROLL),
|
|
SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE |
|
|
SWP_NOOWNERZORDER | SWP_NOZORDER);
|
|
}
|
|
|
|
|
|
//
|
|
// If we are the tray master, don't ask to set focus
|
|
//
|
|
return (!(pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER));
|
|
}
|
|
|
|
|
|
/*
|
|
* Volume_OnDestroy
|
|
*
|
|
* Shut down this dialog. DO NOT TOUCH the hmixer!
|
|
*
|
|
* */
|
|
void Volume_OnDestroy(
|
|
HWND hwnd)
|
|
{
|
|
PMIXUIDIALOG pmxud = GETMIXUIDIALOG(hwnd);
|
|
|
|
DeviceChange_Cleanup();
|
|
|
|
if (!pmxud)
|
|
return;
|
|
|
|
if (pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER)
|
|
{
|
|
HWND hVol;
|
|
hVol = pmxud->amxul[0].acr[MIXUI_VOLUME].hwnd;
|
|
SubclassWindow(hVol, pmxud->lpfnTrayVol);
|
|
REMOVEPROP(hVol);
|
|
}
|
|
|
|
Volume_Cleanup(pmxud);
|
|
|
|
if (!(pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER))
|
|
{
|
|
//
|
|
// save window position
|
|
//
|
|
if (!IsIconic(hwnd))
|
|
{
|
|
RECT rc;
|
|
GetWindowRect(hwnd, &rc);
|
|
Volume_GetSetRegistryRect(pmxud->szMixer
|
|
, pmxud->szDestination
|
|
, &rc
|
|
, SET);
|
|
}
|
|
}
|
|
|
|
if (pmxud->dwReturn == MIXUI_RESTART)
|
|
PostMessage(pmxud->hParent, MYWM_RESTART, 0, (LPARAM)pmxud);
|
|
else
|
|
PostMessage(pmxud->hParent, WM_CLOSE, 0, 0L);
|
|
}
|
|
|
|
/*
|
|
* Volume_SetControl
|
|
*
|
|
* Update system controls from visual controls
|
|
*
|
|
* */
|
|
void Volume_SetControl(
|
|
PMIXUIDIALOG pmxud,
|
|
HWND hctl,
|
|
int imxul,
|
|
int itype)
|
|
{
|
|
if (pmxud->dwFlags & MXUD_FLAGSF_MIXER)
|
|
Mixer_SetControl(pmxud, hctl, imxul, itype);
|
|
else
|
|
Nonmixer_SetControl(pmxud, hctl, imxul, itype);
|
|
}
|
|
|
|
/*
|
|
* Volume_GetControl
|
|
*
|
|
* Update visual controls from system controls
|
|
* */
|
|
void Volume_GetControl(
|
|
PMIXUIDIALOG pmxud,
|
|
HWND hctl,
|
|
int imxul,
|
|
int itype)
|
|
{
|
|
if (pmxud->dwFlags & MXUD_FLAGSF_MIXER)
|
|
Mixer_GetControl(pmxud, hctl, imxul, itype);
|
|
else
|
|
Nonmixer_GetControl(pmxud, hctl, imxul, itype);
|
|
}
|
|
|
|
|
|
extern DWORD GetWaveOutID(BOOL *pfPreferred);
|
|
/*
|
|
* Volume_PlayDefaultSound
|
|
*
|
|
* Play the default sound on the current mixer
|
|
*
|
|
* */
|
|
void Volume_PlayDefaultSound (PMIXUIDIALOG pmxud)
|
|
{
|
|
/*
|
|
// TODO: Implement for all master volumes. Convert mixerid to wave id then
|
|
// use wave API to play the file
|
|
|
|
TCHAR szDefSnd[MAX_PATH];
|
|
long lcb = sizeof (szDefSnd);
|
|
|
|
// Get the default sound filename
|
|
if (ERROR_SUCCESS != RegQueryValue (HKEY_CURRENT_USER, REGSTR_PATH_APPS_DEFAULT TEXT("\\.Default\\.Current"), szDefSnd, &lcb) ||
|
|
0 >= lstrlen (szDefSnd))
|
|
return;
|
|
*/
|
|
|
|
DWORD dwWave = GetWaveOutID (NULL);
|
|
UINT uiMixID;
|
|
|
|
// Check Parameter
|
|
if (!pmxud)
|
|
return;
|
|
|
|
// Play the sound only if we are on the default mixer...
|
|
if (MMSYSERR_NOERROR == mixerGetID (ULongToPtr(dwWave), &uiMixID, MIXER_OBJECTF_WAVEOUT) &&
|
|
pmxud -> mxid == uiMixID)
|
|
{
|
|
|
|
static const TCHAR cszDefSnd[] = TEXT(".Default");
|
|
PlaySound(cszDefSnd, NULL, SND_ASYNC | SND_ALIAS);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Volume_ScrollTo
|
|
*
|
|
* Move the scrollbar position.
|
|
*/
|
|
void Volume_ScrollTo(
|
|
PMIXUIDIALOG pmxud,
|
|
int pos
|
|
)
|
|
{
|
|
RECT rc;
|
|
|
|
/*
|
|
* Keep in range.
|
|
*/
|
|
pos = max(pos, 0);
|
|
pos = min(pos, pmxud->cxDlgContent - pmxud->cxDlgWidth);
|
|
|
|
/*
|
|
* Scroll the window contents accordingly. But don't scroll
|
|
* the status bar.
|
|
*/
|
|
|
|
GetClientRect(pmxud->hwnd, &rc);
|
|
if (pmxud->hStatus)
|
|
{
|
|
RECT rcStatus;
|
|
GetWindowRect(pmxud->hStatus, &rcStatus);
|
|
MapWindowRect(NULL, pmxud->hwnd, &rcStatus);
|
|
SubtractRect(&rc, &rc, &rcStatus);
|
|
}
|
|
|
|
rc.left = -pmxud->cxDlgContent;
|
|
rc.right = pmxud->cxDlgContent;
|
|
|
|
ScrollWindowEx(pmxud->hwnd, pmxud->xOffset - pos, 0,
|
|
&rc, NULL, NULL, NULL,
|
|
SW_ERASE | SW_INVALIDATE | SW_SCROLLCHILDREN);
|
|
pmxud->xOffset = pos;
|
|
|
|
/*
|
|
* Move the scrollbar to match.
|
|
*/
|
|
SetScrollPos(pmxud->hwnd, SB_HORZ, pos, TRUE);
|
|
}
|
|
|
|
/*
|
|
* Volume_ScrollContent
|
|
*
|
|
* Process scroll bar messages for the dialog itself.
|
|
*/
|
|
|
|
void Volume_ScrollContent(
|
|
PMIXUIDIALOG pmxud,
|
|
UINT code,
|
|
int pos
|
|
)
|
|
{
|
|
switch (code) {
|
|
case SB_LINELEFT:
|
|
Volume_ScrollTo(pmxud, pmxud->xOffset - pmxud->cxScroll);
|
|
break;
|
|
|
|
case SB_LINERIGHT:
|
|
Volume_ScrollTo(pmxud, pmxud->xOffset + pmxud->cxScroll);
|
|
break;
|
|
|
|
case SB_PAGELEFT:
|
|
Volume_ScrollTo(pmxud, pmxud->xOffset - pmxud->cxDlgWidth);
|
|
break;
|
|
|
|
case SB_PAGERIGHT:
|
|
Volume_ScrollTo(pmxud, pmxud->xOffset + pmxud->cxDlgWidth);
|
|
break;
|
|
|
|
case SB_LEFT:
|
|
Volume_ScrollTo(pmxud, 0);
|
|
break;
|
|
|
|
case SB_RIGHT:
|
|
Volume_ScrollTo(pmxud, MAXLONG);
|
|
break;
|
|
|
|
case SB_THUMBPOSITION:
|
|
case SB_THUMBTRACK:
|
|
Volume_ScrollTo(pmxud, pos);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Volume_OnXScroll
|
|
*
|
|
* Process Scroll bar messages
|
|
*
|
|
* */
|
|
void Volume_OnXScroll(
|
|
HWND hwnd,
|
|
HWND hwndCtl,
|
|
UINT code,
|
|
int pos)
|
|
{
|
|
PMIXUIDIALOG pmxud = GETMIXUIDIALOG(hwnd);
|
|
UINT id;
|
|
int ictl;
|
|
int iline;
|
|
|
|
// If this is a scroll message from the dialog itself, then we need
|
|
// to scroll our content.
|
|
if (hwndCtl == NULL)
|
|
{
|
|
Volume_ScrollContent(pmxud, code, pos);
|
|
return;
|
|
}
|
|
|
|
id = GetDlgCtrlID(hwndCtl);
|
|
iline = id/IDOFFSET - 1;
|
|
ictl = ((id % IDOFFSET) + IDC_MIXERCONTROLS == IDC_BALANCE)
|
|
? MIXUI_BALANCE : MIXUI_VOLUME;
|
|
|
|
Volume_SetControl(pmxud, hwndCtl, iline, ictl);
|
|
|
|
//
|
|
// Make sure a note gets played
|
|
//
|
|
if (pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER)
|
|
pmxud->dwTrayInfo |= MXUD_TRAYINFOF_SIGNAL;
|
|
|
|
// Play a sound on for the master volume or balance slider when the
|
|
// user ends the scroll and we are still in focus and the topmost app.
|
|
if (code == SB_ENDSCROLL && pmxud && !(pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER) &&
|
|
pmxud->amxul[iline].pvcd &&
|
|
(MXUL_STYLEF_DESTINATION & pmxud->amxul[iline].dwStyle)
|
|
&& hwndCtl == GetFocus() && hwnd == GetForegroundWindow ())
|
|
{
|
|
Volume_PlayDefaultSound (pmxud);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Volume_OnMyTimer
|
|
*
|
|
* Frequent update timer for meters
|
|
* */
|
|
void Volume_OnMyTimer(
|
|
HWND hwnd)
|
|
{
|
|
PMIXUIDIALOG pmxud = GETMIXUIDIALOG(hwnd);
|
|
|
|
if (!pmxud)
|
|
return;
|
|
|
|
if (pmxud->cTimeInQueue > 0)
|
|
pmxud->cTimeInQueue--;
|
|
|
|
if (!(pmxud->dwFlags & MXUD_FLAGSF_USETIMER))
|
|
return;
|
|
|
|
if (pmxud->dwFlags & MXUD_FLAGSF_MIXER)
|
|
Mixer_PollingUpdate(pmxud);
|
|
else
|
|
Nonmixer_PollingUpdate(pmxud);
|
|
}
|
|
|
|
/*
|
|
* Volume_OnTimer
|
|
*
|
|
* Infrequent update timer for tray shutdown
|
|
* */
|
|
void Volume_OnTimer(
|
|
HWND hwnd,
|
|
UINT id)
|
|
{
|
|
PMIXUIDIALOG pmxud = GETMIXUIDIALOG(hwnd);
|
|
|
|
KillTimer(hwnd, VOLUME_TRAYSHUTDOWN_ID);
|
|
Volume_EndDialog(pmxud, MIXUI_EXIT, 0);
|
|
}
|
|
|
|
/*
|
|
* Volume_OnMixmControlChange
|
|
*
|
|
* Handle control changes
|
|
*
|
|
* */
|
|
void Volume_OnMixmControlChange(
|
|
HWND hwnd,
|
|
HMIXER hmx,
|
|
DWORD dwControlID)
|
|
{
|
|
PMIXUIDIALOG pmxud = GETMIXUIDIALOG(hwnd);
|
|
Mixer_GetControlFromID(pmxud, dwControlID);
|
|
}
|
|
|
|
/*
|
|
* Volume_EnableLine
|
|
*
|
|
* Enable/Disable a line
|
|
*
|
|
* */
|
|
void Volume_EnableLine(
|
|
PMIXUIDIALOG pmxud,
|
|
DWORD iLine,
|
|
BOOL fEnable)
|
|
{
|
|
DWORD iCtrl;
|
|
PMIXUICTRL pmxc;
|
|
|
|
for (iCtrl = MIXUI_FIRST; iCtrl <= MIXUI_LAST; iCtrl++ )
|
|
{
|
|
pmxc = &pmxud->amxul[iLine].acr[iCtrl];
|
|
if (pmxc->state == MIXUI_CONTROL_INITIALIZED)
|
|
EnableWindow(pmxc->hwnd, fEnable);
|
|
}
|
|
|
|
pmxud->amxul[iLine].dwStyle ^= MXUL_STYLEF_DISABLED;
|
|
}
|
|
|
|
/*
|
|
* Volume_InitLine
|
|
*
|
|
* Initialize the UI controls for the dialog
|
|
*
|
|
* */
|
|
void Volume_InitLine(
|
|
PMIXUIDIALOG pmxud,
|
|
DWORD iLine)
|
|
{
|
|
HWND ctrl;
|
|
PMIXUICTRL pmxc;
|
|
|
|
//
|
|
// Peakmeter control
|
|
//
|
|
pmxc = &pmxud->amxul[iLine].acr[MIXUI_VUMETER];
|
|
ctrl = Volume_GetLineItem(pmxud->hwnd, iLine, IDC_VUMETER);
|
|
|
|
pmxc->hwnd = ctrl;
|
|
|
|
if (! (pmxc->state == MIXUI_CONTROL_ENABLED) )
|
|
{
|
|
if (ctrl)
|
|
ShowWindow(ctrl, SW_HIDE);
|
|
}
|
|
else if (ctrl)
|
|
{
|
|
HWND hvol;
|
|
|
|
SendMessage(ctrl, VU_SETRANGEMAX, 0, VOLUME_TICS);
|
|
SendMessage(ctrl, VU_SETRANGEMIN, 0, 0);
|
|
|
|
hvol = Volume_GetLineItem(pmxud->hwnd, iLine, IDC_VOLUME);
|
|
if (hvol)
|
|
{
|
|
RECT rc;
|
|
POINT pos;
|
|
|
|
GetWindowRect(hvol, &rc);
|
|
MapWindowPoints(NULL, pmxud->hwnd, (LPPOINT)&rc, 2);
|
|
pos.x = rc.left;
|
|
pos.y = rc.top;
|
|
|
|
MoveWindow(hvol
|
|
, pos.x - 15
|
|
, pos.y
|
|
, rc.right - rc.left
|
|
, rc.bottom - rc.top
|
|
, FALSE);
|
|
}
|
|
//
|
|
// Signal use of update timer
|
|
//
|
|
pmxud->dwFlags |= MXUD_FLAGSF_USETIMER;
|
|
pmxc->state = MIXUI_CONTROL_INITIALIZED;
|
|
|
|
}
|
|
else
|
|
pmxc->state = MIXUI_CONTROL_UNINITIALIZED;
|
|
|
|
|
|
//
|
|
// Balance control
|
|
//
|
|
pmxc = &pmxud->amxul[iLine].acr[MIXUI_BALANCE];
|
|
ctrl = Volume_GetLineItem(pmxud->hwnd, iLine, IDC_BALANCE);
|
|
|
|
pmxc->hwnd = ctrl;
|
|
|
|
if (ctrl)
|
|
{
|
|
SendMessage(ctrl, TBM_SETRANGE, 0, MAKELONG(0, 64));
|
|
SendMessage(ctrl, TBM_SETTICFREQ, 32, 0 );
|
|
SendMessage(ctrl, TBM_SETPOS, TRUE, 32);
|
|
|
|
if (pmxc->state != MIXUI_CONTROL_ENABLED)
|
|
{
|
|
EnableWindow(ctrl, FALSE);
|
|
}
|
|
else
|
|
pmxc->state = MIXUI_CONTROL_INITIALIZED;
|
|
|
|
}
|
|
else
|
|
pmxc->state = MIXUI_CONTROL_UNINITIALIZED;
|
|
|
|
//
|
|
// Volume control
|
|
//
|
|
pmxc = &pmxud->amxul[iLine].acr[MIXUI_VOLUME];
|
|
ctrl = Volume_GetLineItem(pmxud->hwnd, iLine, IDC_VOLUME);
|
|
|
|
pmxc->hwnd = ctrl;
|
|
|
|
if (ctrl)
|
|
{
|
|
SendMessage(ctrl, TBM_SETRANGE, 0, MAKELONG(0, VOLUME_TICS));
|
|
SendMessage(ctrl, TBM_SETTICFREQ, (VOLUME_TICS + 5)/6, 0 );
|
|
|
|
if (pmxc->state != MIXUI_CONTROL_ENABLED)
|
|
{
|
|
SendMessage(ctrl, TBM_SETPOS, TRUE, 128);
|
|
EnableWindow(ctrl, FALSE);
|
|
}
|
|
else
|
|
pmxc->state = MIXUI_CONTROL_INITIALIZED;
|
|
|
|
}
|
|
else
|
|
pmxc->state = MIXUI_CONTROL_UNINITIALIZED;
|
|
|
|
//
|
|
// Switch
|
|
//
|
|
pmxc = &pmxud->amxul[iLine].acr[MIXUI_SWITCH];
|
|
ctrl = Volume_GetLineItem(pmxud->hwnd, iLine, IDC_SWITCH);
|
|
|
|
pmxc->hwnd = ctrl;
|
|
|
|
if (ctrl)
|
|
{
|
|
if (pmxc->state != MIXUI_CONTROL_ENABLED)
|
|
EnableWindow(ctrl, FALSE);
|
|
else
|
|
pmxc->state = MIXUI_CONTROL_INITIALIZED;
|
|
}
|
|
else
|
|
pmxc->state = MIXUI_CONTROL_UNINITIALIZED;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* Volume_OnMixmLineChange
|
|
*
|
|
* */
|
|
void Volume_OnMixmLineChange(
|
|
HWND hwnd,
|
|
HMIXER hmx,
|
|
DWORD dwLineID)
|
|
{
|
|
PMIXUIDIALOG pmxud = GETMIXUIDIALOG(hwnd);
|
|
DWORD iLine;
|
|
|
|
for (iLine = 0; iLine < pmxud->cmxul; iLine++)
|
|
{
|
|
if ( dwLineID == pmxud->amxul[iLine].pvcd->dwLineID )
|
|
{
|
|
MIXERLINE ml;
|
|
MMRESULT mmr;
|
|
BOOL fEnable;
|
|
|
|
ml.cbStruct = sizeof(ml);
|
|
ml.dwLineID = dwLineID;
|
|
|
|
mmr = mixerGetLineInfo((HMIXEROBJ)hmx, &ml, MIXER_GETLINEINFOF_LINEID);
|
|
|
|
if (mmr != MMSYSERR_NOERROR)
|
|
{
|
|
fEnable = !(ml.fdwLine & MIXERLINE_LINEF_DISCONNECTED);
|
|
Volume_EnableLine(pmxud, iLine, fEnable);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Volume_OnActivate
|
|
*
|
|
* Important for tray volume only. Dismisses the dialog and starts an
|
|
* expiration timer.
|
|
*
|
|
* */
|
|
void Volume_OnActivate(
|
|
HWND hwnd,
|
|
UINT state,
|
|
HWND hwndActDeact,
|
|
BOOL fMinimized)
|
|
{
|
|
PMIXUIDIALOG pmxud = GETMIXUIDIALOG(hwnd);
|
|
|
|
if (!(pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (state != WA_INACTIVE)
|
|
{
|
|
fCanDismissWindow = TRUE;
|
|
}
|
|
else if (fCanDismissWindow)
|
|
{
|
|
PostMessage(hwnd, WM_CLOSE, 0, 0L);
|
|
/*
|
|
DWORD dwTimeout = 5 * 60 * 1000;
|
|
fCanDismissWindow = FALSE;
|
|
ShowWindow(hwnd, SW_HIDE);
|
|
//
|
|
// Set expiration timer. If no one adjusts the volume, make the
|
|
// application go away after 5 minutes.
|
|
//
|
|
dwTimeout = Volume_GetTrayTimeout(dwTimeout);
|
|
SetTimer(hwnd, VOLUME_TRAYSHUTDOWN_ID, dwTimeout, NULL);
|
|
*/
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Volume_PropogateMessage
|
|
*
|
|
* WM_SYSCOLORCHANGE needs to be send to all child windows (esp. trackbars)
|
|
*/
|
|
void Volume_PropagateMessage(
|
|
HWND hwnd,
|
|
UINT uMessage,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
HWND hwndChild;
|
|
|
|
for (hwndChild = GetWindow(hwnd, GW_CHILD); hwndChild != NULL;
|
|
hwndChild = GetWindow(hwndChild, GW_HWNDNEXT))
|
|
{
|
|
SendMessage(hwndChild, uMessage, wParam, lParam);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Volume_OnPaint
|
|
*
|
|
* Handle custom painting
|
|
* */
|
|
void Volume_OnPaint(HWND hwnd)
|
|
{
|
|
PMIXUIDIALOG pmxud = GETMIXUIDIALOG(hwnd);
|
|
RECT rc;
|
|
PAINTSTRUCT ps;
|
|
HDC hdc;
|
|
|
|
hdc = BeginPaint(hwnd, &ps);
|
|
|
|
//
|
|
// for all styles other than the tray master, draw an etched
|
|
// line to delinate the menu area
|
|
//
|
|
if (!(pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER))
|
|
{
|
|
GetClientRect(hwnd, &rc);
|
|
rc.bottom = 0;
|
|
DrawEdge(hdc, &rc, EDGE_ETCHED, BF_TOP);
|
|
EndPaint(hwnd, &ps);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// for the tray master, draw some significant icon to indicate
|
|
// volume
|
|
//
|
|
GetWindowRect(GetDlgItem(hwnd, IDC_VOLUMECUE), &rc);
|
|
|
|
MapWindowPoints(NULL, hwnd, (LPPOINT)&rc, 2);
|
|
|
|
DrawEdge(hdc, &rc, BDR_RAISEDINNER, BF_DIAGONAL|BF_TOP|BF_LEFT);
|
|
DrawEdge(hdc, &rc, BDR_RAISEDINNER, BF_TOP);
|
|
rc.bottom -= 8;
|
|
DrawEdge(hdc, &rc, BDR_RAISEDINNER, BF_RIGHT);
|
|
|
|
EndPaint(hwnd, &ps);
|
|
}
|
|
|
|
/*
|
|
* Volume_OnClose
|
|
*
|
|
* */
|
|
void Volume_OnClose(
|
|
HWND hwnd)
|
|
{
|
|
DestroyWindow(hwnd);
|
|
}
|
|
|
|
/*
|
|
* Volume_OnEndSession
|
|
*
|
|
* */
|
|
void Volume_OnEndSession(
|
|
HWND hwnd,
|
|
BOOL fEnding)
|
|
{
|
|
if (!fEnding)
|
|
return;
|
|
|
|
//
|
|
// Be sure to call the close code to free open handles
|
|
//
|
|
Volume_OnClose(hwnd);
|
|
}
|
|
|
|
#define V_DC_STATEF_PENDING 0x00000001
|
|
#define V_DC_STATEF_REMOVING 0x00000002
|
|
#define V_DC_STATEF_ARRIVING 0x00000004
|
|
|
|
/*
|
|
* Volume_OnDeviceChange
|
|
*
|
|
* */
|
|
void Volume_OnDeviceChange(
|
|
HWND hwnd,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
PMIXUIDIALOG pmxud = GETMIXUIDIALOG(hwnd);
|
|
MMRESULT mmr;
|
|
UINT uMxID;
|
|
PDEV_BROADCAST_DEVICEINTERFACE bdi = (PDEV_BROADCAST_DEVICEINTERFACE)lParam;
|
|
PDEV_BROADCAST_HANDLE bh = (PDEV_BROADCAST_HANDLE)lParam;
|
|
|
|
//
|
|
// Determine if this is our event.
|
|
//
|
|
if(!DeviceEventContext)
|
|
return;
|
|
|
|
//If we have an handle on the device then we get a DEV_BROADCAST_HDR structure as the lParam.
|
|
//Or else it means that we have registered for the general audio category KSCATEGORY_AUDIO.
|
|
if(bUseHandle)
|
|
{
|
|
if(!bh ||
|
|
bh->dbch_devicetype != DBT_DEVTYP_HANDLE)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else if (!bdi ||
|
|
bdi->dbcc_devicetype != DBT_DEVTYP_DEVICEINTERFACE ||
|
|
!IsEqualGUID(&KSCATEGORY_AUDIO, &bdi->dbcc_classguid) ||
|
|
!(*bdi->dbcc_name)
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
switch (wParam)
|
|
{
|
|
case DBT_DEVICEQUERYREMOVE:
|
|
//The mixer has to be shutdown now.
|
|
//Posting a WM_CLOSE message as Volume_EndDialog does will not help.
|
|
if (pmxud->dwFlags & MXUD_FLAGSF_MIXER)
|
|
Mixer_Shutdown(pmxud);
|
|
else
|
|
Nonmixer_Shutdown(pmxud);
|
|
|
|
// Don't attempt restart, just exit. The wavemapper is not
|
|
// updated with the new default device, so do not know what
|
|
// to restart as and we should NOT hardcode device #0!
|
|
// pmxud->mxid = (DWORD) 0;
|
|
// GetDestination(pmxud->mxid, &pmxud->iDest);
|
|
Volume_EndDialog(pmxud, MIXUI_EXIT, 0);
|
|
return;
|
|
|
|
|
|
case DBT_DEVICEQUERYREMOVEFAILED: // The query failed, the device will not be removed, so lets reopen it.
|
|
|
|
mmr = Volume_GetDefaultMixerID(&uMxID, gfRecord);
|
|
pmxud->mxid = (mmr == MMSYSERR_NOERROR)?uMxID:0;
|
|
GetDestination(pmxud->mxid, &pmxud->iDest);
|
|
Volume_EndDialog(pmxud, MIXUI_RESTART, 0);
|
|
return;
|
|
|
|
case DBT_DEVNODES_CHANGED:
|
|
//
|
|
// We cannot reliably determine the final state of the devices in
|
|
// the system until this message is broadcast.
|
|
//
|
|
if (pmxud->dwDeviceState & V_DC_STATEF_PENDING)
|
|
{
|
|
pmxud->dwDeviceState ^= V_DC_STATEF_PENDING;
|
|
break;
|
|
}
|
|
return;
|
|
|
|
case DBT_DEVICEREMOVECOMPLETE:
|
|
//The mixer has to be shutdown now.
|
|
//Posting a WM_CLOSE message as Volume_EndDialog does will not help.
|
|
if (pmxud->dwFlags & MXUD_FLAGSF_MIXER)
|
|
Mixer_Shutdown(pmxud);
|
|
else
|
|
Nonmixer_Shutdown(pmxud);
|
|
|
|
//A DBT_DEVICEQUERYREMOVE is not guaranteed before a DBT_DEVICEREMOVECOMPLETE.
|
|
//There should be a check here to see if this message is meant for this device.
|
|
//We do not know a way of doing that right now.
|
|
|
|
// Don't attempt restart, just exit. The wavemapper is not
|
|
// updated with the new default device, so do not know what
|
|
// to restart as and we should NOT hardcode device #0!
|
|
// pmxud->mxid = (DWORD) 0;
|
|
// GetDestination(pmxud->mxid, &pmxud->iDest);
|
|
Volume_EndDialog(pmxud, MIXUI_EXIT, 0);
|
|
|
|
pmxud->dwDeviceState = V_DC_STATEF_PENDING
|
|
| V_DC_STATEF_REMOVING;
|
|
return;
|
|
case DBT_DEVICEARRIVAL:
|
|
//
|
|
// A devnode is being added to the system
|
|
//
|
|
pmxud->dwDeviceState = V_DC_STATEF_PENDING
|
|
| V_DC_STATEF_ARRIVING;
|
|
return;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
|
|
mmr = Volume_GetDefaultMixerID(&uMxID, gfRecord);
|
|
|
|
if (pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER)
|
|
{
|
|
if ( mmr == MMSYSERR_NOERROR
|
|
&& (pmxud->dwDeviceState & V_DC_STATEF_ARRIVING))
|
|
{
|
|
DWORD dwDevNode;
|
|
if (!mixerMessage((HMIXER)UIntToPtr(uMxID), DRV_QUERYDEVNODE
|
|
, (DWORD_PTR)&dwDevNode, 0L))
|
|
{
|
|
if (dwDevNode == pmxud->dwDevNode)
|
|
{
|
|
//
|
|
// ignore this device, it doesn't affect us
|
|
//
|
|
pmxud->dwDeviceState = 0L;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Our device state has changed. Just go away.
|
|
//
|
|
Volume_EndDialog(pmxud, MIXUI_EXIT, 0);
|
|
}
|
|
else if (pmxud->dwDeviceState & V_DC_STATEF_REMOVING)
|
|
{
|
|
//
|
|
// Restart with the default mixer if we can.
|
|
//
|
|
pmxud->mxid = (mmr == MMSYSERR_NOERROR)?uMxID:0;
|
|
GetDestination(pmxud->mxid, &pmxud->iDest);
|
|
Volume_EndDialog(pmxud, MIXUI_RESTART, 0);
|
|
}
|
|
pmxud->dwDeviceState = 0L;
|
|
}
|
|
|
|
|
|
void Volume_OnWakeup(
|
|
HWND hwnd,
|
|
WPARAM wParam)
|
|
{
|
|
POINT pos;
|
|
RECT rc, rcPopup;
|
|
LONG w,h;
|
|
HWND hTrack;
|
|
HMONITOR hMonitor;
|
|
MONITORINFO moninfo;
|
|
|
|
PMIXUIDIALOG pmxud = GETMIXUIDIALOG(hwnd);
|
|
|
|
if (!(pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER))
|
|
return;
|
|
|
|
KillTimer(hwnd, VOLUME_TRAYSHUTDOWN_ID);
|
|
|
|
if (wParam != 0)
|
|
{
|
|
Volume_EndDialog(pmxud, MIXUI_EXIT, 0);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Make the tray volume come up.
|
|
//
|
|
|
|
//Get the current position.
|
|
GetCursorPos(&pos);
|
|
|
|
//Get the width and height of the popup.
|
|
GetWindowRect(hwnd, &rc);
|
|
w = rc.right - rc.left; //This value will always be positive as left is always lesser than right.
|
|
h = rc.bottom - rc.top; //This value will always be positive as top is always lesser than bottom.
|
|
|
|
//Initialize the rectangle for the popup. Position it so that the popup appears to the right,
|
|
//bottom of the cursor.
|
|
rcPopup.left = pos.x;
|
|
rcPopup.right = pos.x + w;
|
|
rcPopup.top = pos.y;
|
|
rcPopup.bottom = pos.y+h;
|
|
|
|
//Get the rectangle for the monitor.
|
|
hMonitor = MonitorFromPoint(pos, MONITOR_DEFAULTTONEAREST);
|
|
moninfo.cbSize = sizeof(moninfo);
|
|
GetMonitorInfo(hMonitor,&moninfo);
|
|
|
|
//If the popup rectangle is leaking off from the right of the screen. Make it appear on the
|
|
//left of the cursor.
|
|
if(rcPopup.right > moninfo.rcWork.right)
|
|
{
|
|
OffsetRect(&rcPopup, -w, 0);
|
|
}
|
|
|
|
//If the popup rectangle is leaking off from the bottom of the screen. Make it appear on top
|
|
//of the cursor.
|
|
if(rcPopup.bottom > moninfo.rcWork.bottom)
|
|
{
|
|
OffsetRect(&rcPopup, 0, -h);
|
|
}
|
|
|
|
|
|
SetWindowPos(hwnd
|
|
, HWND_TOPMOST
|
|
, rcPopup.left
|
|
, rcPopup.top
|
|
, w
|
|
, h
|
|
, SWP_SHOWWINDOW);
|
|
|
|
// make us come to the front
|
|
SetForegroundWindow(hwnd);
|
|
fCanDismissWindow = TRUE;
|
|
|
|
hTrack = GetDlgItem(hwnd, IDC_VOLUME);
|
|
if (hTrack)
|
|
SetFocus(hTrack);
|
|
}
|
|
|
|
|
|
/*
|
|
* VolumeProc
|
|
*
|
|
* */
|
|
INT_PTR CALLBACK VolumeProc(
|
|
HWND hdlg,
|
|
UINT msg,
|
|
WPARAM wparam,
|
|
LPARAM lparam)
|
|
{
|
|
switch (msg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
return HANDLE_WM_INITDIALOG(hdlg, wparam, lparam, Volume_OnInitDialog);
|
|
|
|
case WM_COMMAND:
|
|
HANDLE_WM_COMMAND(hdlg, wparam, lparam, Volume_OnCommand);
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
HANDLE_WM_CLOSE(hdlg, wparam, lparam, Volume_OnClose);
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
HANDLE_WM_DESTROY(hdlg, wparam, lparam, Volume_OnDestroy);
|
|
break;
|
|
|
|
case WM_HSCROLL:
|
|
case WM_VSCROLL:
|
|
//
|
|
// balance and volume are essentially the same
|
|
//
|
|
HANDLE_WM_XSCROLL(hdlg, wparam, lparam, Volume_OnXScroll);
|
|
break;
|
|
|
|
case WM_MENUSELECT:
|
|
//Keep track of which menu bar item is currently popped up.
|
|
//This will be used for displaying the appropriate help from the mplayer.hlp file
|
|
//when the user presses the F1 key.
|
|
currMenuItem = (UINT)LOWORD(wparam);
|
|
break;
|
|
|
|
case MM_MIXM_LINE_CHANGE:
|
|
HANDLE_MM_MIXM_LINE_CHANGE(hdlg
|
|
, wparam
|
|
, lparam
|
|
, Volume_OnMixmLineChange);
|
|
return FALSE;
|
|
|
|
case MM_MIXM_CONTROL_CHANGE:
|
|
HANDLE_MM_MIXM_CONTROL_CHANGE(hdlg
|
|
, wparam
|
|
, lparam
|
|
, Volume_OnMixmControlChange);
|
|
return FALSE;
|
|
|
|
case WM_ACTIVATE:
|
|
HANDLE_WM_ACTIVATE(hdlg, wparam, lparam, Volume_OnActivate);
|
|
break;
|
|
|
|
case MYWM_TIMER:
|
|
HANDLE_MYWM_TIMER(hdlg, wparam, lparam, Volume_OnMyTimer);
|
|
break;
|
|
|
|
case WM_TIMER:
|
|
HANDLE_WM_TIMER(hdlg, wparam, lparam, Volume_OnTimer);
|
|
break;
|
|
|
|
case WM_PAINT:
|
|
HANDLE_WM_PAINT(hdlg, wparam, lparam, Volume_OnPaint);
|
|
break;
|
|
|
|
case WM_SYSCOLORCHANGE:
|
|
Volume_PropagateMessage(hdlg, msg, wparam, lparam);
|
|
break;
|
|
|
|
case WM_DEVICECHANGE:
|
|
HANDLE_WM_IDEVICECHANGE(hdlg, wparam, lparam, Volume_OnDeviceChange);
|
|
break;
|
|
|
|
case MYWM_WAKEUP:
|
|
HANDLE_MYWM_WAKEUP(hdlg, wparam, lparam, Volume_OnWakeup);
|
|
break;
|
|
|
|
case WM_ENDSESSION:
|
|
HANDLE_WM_ENDSESSION(hdlg, wparam, lparam, Volume_OnEndSession);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Volume_AddLine
|
|
*
|
|
* */
|
|
BOOL Volume_AddLine(
|
|
PMIXUIDIALOG pmxud,
|
|
LPBYTE lpAdd,
|
|
DWORD cbAdd,
|
|
DWORD dwStyle,
|
|
PVOLCTRLDESC pvcd)
|
|
{
|
|
LPBYTE pbNew;
|
|
DWORD cbNew;
|
|
PMIXUILINE pmxul;
|
|
|
|
if (pmxud->amxul)
|
|
{
|
|
pmxul = (PMIXUILINE)GlobalReAllocPtr(pmxud->amxul
|
|
, (pmxud->cmxul+1)*sizeof(MIXUILINE)
|
|
, GHND);
|
|
}
|
|
else
|
|
{
|
|
pmxul = (PMIXUILINE)GlobalAllocPtr(GHND, sizeof(MIXUILINE));
|
|
}
|
|
|
|
if (!pmxul)
|
|
return FALSE;
|
|
|
|
pbNew = Dlg_HorizAttach(pmxud->lpDialog
|
|
, pmxud->cbDialog
|
|
, lpAdd
|
|
, cbAdd
|
|
, (WORD)(IDOFFSET * pmxud->cmxul)
|
|
, &cbNew );
|
|
if (!pbNew)
|
|
{
|
|
if (!pmxud->amxul)
|
|
GlobalFreePtr(pmxul);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
pmxul[pmxud->cmxul].dwStyle = dwStyle;
|
|
pmxul[pmxud->cmxul].pvcd = pvcd;
|
|
|
|
pmxud->amxul = pmxul;
|
|
pmxud->lpDialog = pbNew;
|
|
pmxud->cbDialog = cbNew;
|
|
pmxud->cmxul ++;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Volume_Cleanup
|
|
*
|
|
* */
|
|
void Volume_Cleanup(
|
|
PMIXUIDIALOG pmxud)
|
|
{
|
|
if (pmxud->dwFlags & MXUD_FLAGSF_USETIMER)
|
|
{
|
|
timeKillEvent(pmxud->uTimerID);
|
|
pmxud->dwFlags ^= MXUD_FLAGSF_USETIMER;
|
|
}
|
|
if (pmxud->dwFlags & MXUD_FLAGSF_BADDRIVER)
|
|
{
|
|
pmxud->dwFlags ^= MXUD_FLAGSF_BADDRIVER;
|
|
}
|
|
if (pmxud->dwFlags & MXUD_FLAGSF_NOADVANCED)
|
|
{
|
|
pmxud->dwFlags ^= MXUD_FLAGSF_NOADVANCED;
|
|
}
|
|
|
|
if (pmxud->dwFlags & MXUD_FLAGSF_MIXER)
|
|
Mixer_Shutdown(pmxud);
|
|
else
|
|
Nonmixer_Shutdown(pmxud);
|
|
|
|
if (pmxud->lpDialog)
|
|
GlobalFreePtr(pmxud->lpDialog);
|
|
|
|
if (pmxud->amxul)
|
|
GlobalFreePtr(pmxud->amxul);
|
|
|
|
if (pmxud->avcd)
|
|
GlobalFreePtr(pmxud->avcd);
|
|
|
|
pmxud->amxul = NULL;
|
|
pmxud->lpDialog = NULL;
|
|
pmxud->cbDialog = 0;
|
|
pmxud->cmxul = 0;
|
|
pmxud->hwnd = NULL;
|
|
pmxud->hStatus = NULL;
|
|
pmxud->uTimerID = 0;
|
|
pmxud->dwDevNode = 0L;
|
|
|
|
FreeAppIcon ();
|
|
}
|
|
|
|
/*
|
|
* Volume_CreateVolume
|
|
* */
|
|
BOOL Volume_CreateVolume(
|
|
PMIXUIDIALOG pmxud)
|
|
{
|
|
WNDCLASS wc;
|
|
LPBYTE lpDst = NULL, lpSrc = NULL, lpMaster = NULL;
|
|
DWORD cbDst, cbSrc, cbMaster;
|
|
PVOLCTRLDESC avcd;
|
|
DWORD cvcd;
|
|
DWORD ivcd;
|
|
DWORD imxul;
|
|
DWORD dwSupport = 0L;
|
|
BOOL fAddLine = TRUE;
|
|
|
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wc.hIcon = GetAppIcon (pmxud->hInstance, pmxud->mxid);
|
|
wc.lpszMenuName = NULL;
|
|
wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
|
|
wc.hInstance = pmxud->hInstance;
|
|
wc.style = CS_HREDRAW | CS_VREDRAW;
|
|
wc.lpfnWndProc = DefDlgProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = DLGWINDOWEXTRA;
|
|
wc.lpszClassName = (pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER)
|
|
? gszTrayClassName : gszAppClassName;
|
|
RegisterClass(&wc);
|
|
|
|
if (pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER)
|
|
{
|
|
lpMaster = (LPBYTE)Dlg_LoadResource(pmxud->hInstance
|
|
, MAKEINTRESOURCE(IDD_TRAYMASTER)
|
|
, &cbMaster);
|
|
if (!lpMaster)
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
if (pmxud->dwStyle & MXUD_STYLEF_SMALL)
|
|
{
|
|
lpDst = (LPBYTE)Dlg_LoadResource(pmxud->hInstance
|
|
, MAKEINTRESOURCE(IDD_SMDST)
|
|
, &cbDst);
|
|
|
|
lpSrc = (LPBYTE)Dlg_LoadResource(pmxud->hInstance
|
|
, MAKEINTRESOURCE(IDD_SMSRC)
|
|
, &cbSrc);
|
|
|
|
}
|
|
else
|
|
{
|
|
lpDst = (LPBYTE)Dlg_LoadResource(pmxud->hInstance
|
|
, MAKEINTRESOURCE(IDD_DESTINATION)
|
|
, &cbDst);
|
|
|
|
lpSrc = (LPBYTE)Dlg_LoadResource(pmxud->hInstance
|
|
, MAKEINTRESOURCE(IDD_SOURCE)
|
|
, &cbSrc);
|
|
}
|
|
|
|
if (!lpDst || !lpSrc)
|
|
return FALSE;
|
|
}
|
|
|
|
pmxud->lpDialog = NULL;
|
|
pmxud->cbDialog = 0;
|
|
pmxud->amxul = NULL;
|
|
pmxud->cmxul = 0;
|
|
pmxud->avcd = NULL;
|
|
pmxud->cvcd = 0;
|
|
|
|
//
|
|
// Create the volume description
|
|
//
|
|
|
|
if (pmxud->dwFlags & MXUD_FLAGSF_MIXER)
|
|
{
|
|
HMIXER hmx;
|
|
MMRESULT mmr;
|
|
|
|
//
|
|
// Mixer API's work much more efficiently with a mixer handle...
|
|
//
|
|
mmr = mixerOpen(&hmx, pmxud->mxid, 0L, 0L, MIXER_OBJECTF_MIXER);
|
|
|
|
|
|
if(MMSYSERR_NOERROR == mmr)
|
|
{
|
|
avcd = Mixer_CreateVolumeDescription((HMIXEROBJ)hmx
|
|
, pmxud->iDest
|
|
, &cvcd);
|
|
|
|
mixerClose(hmx);
|
|
}
|
|
else
|
|
{
|
|
avcd = Mixer_CreateVolumeDescription((HMIXEROBJ)ULongToPtr(pmxud->mxid)
|
|
, pmxud->iDest
|
|
, &cvcd);
|
|
}
|
|
|
|
if (!Mixer_GetDeviceName(pmxud))
|
|
{
|
|
GlobalFreePtr(avcd);
|
|
avcd = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
avcd = Nonmixer_CreateVolumeDescription(pmxud->iDest
|
|
, &cvcd);
|
|
if (!Nonmixer_GetDeviceName(pmxud))
|
|
{
|
|
GlobalFreePtr(avcd);
|
|
avcd = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Create the dialog box to go along with it
|
|
//
|
|
if (avcd)
|
|
{
|
|
pmxud->avcd = avcd;
|
|
pmxud->cvcd = cvcd;
|
|
|
|
if (pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER)
|
|
{
|
|
if (!Volume_AddLine(pmxud
|
|
, lpMaster
|
|
, cbMaster
|
|
, MXUL_STYLEF_DESTINATION
|
|
, &avcd[0]))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BOOL fFirstRun;
|
|
//
|
|
// Restore HIDDEN flags.
|
|
//
|
|
// On first run, be sure to re-save state so there's something
|
|
// there.
|
|
//
|
|
fFirstRun = !Volume_GetSetRegistryLineStates(pmxud->szMixer
|
|
, pmxud->avcd[0].szShortName
|
|
, avcd
|
|
, cvcd
|
|
, GET);
|
|
|
|
|
|
for (ivcd = 0; ivcd < cvcd; ivcd++)
|
|
{
|
|
//
|
|
// Lines are marked hidden if a state has been saved in the
|
|
// registry or no state has been saved and there are too many
|
|
// unnecessary lines.
|
|
//
|
|
if (avcd[ivcd].dwSupport & VCD_SUPPORTF_HIDDEN)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Lines are marked VISIBLE if they have sufficient controls
|
|
// to be useful.
|
|
//
|
|
if (!(avcd[ivcd].dwSupport & VCD_SUPPORTF_VISIBLE))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Show only defaults on first run.
|
|
//
|
|
if (fFirstRun && !(avcd[ivcd].dwSupport & VCD_SUPPORTF_DEFAULT))
|
|
{
|
|
avcd[ivcd].dwSupport |= VCD_SUPPORTF_HIDDEN;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// For those lines that have important controls, add them to
|
|
// the UI.
|
|
//
|
|
if ((pmxud->dwFlags & MXUD_FLAGSF_MIXER) && ivcd == 0 )
|
|
fAddLine = Volume_AddLine(pmxud
|
|
, lpDst
|
|
, cbDst
|
|
, MXUL_STYLEF_DESTINATION
|
|
, &avcd[ivcd]);
|
|
else
|
|
fAddLine = Volume_AddLine(pmxud
|
|
, lpSrc
|
|
, cbSrc
|
|
, MXUL_STYLEF_SOURCE
|
|
, &avcd[ivcd]);
|
|
|
|
if (!fAddLine)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (fFirstRun)
|
|
Volume_GetSetRegistryLineStates(pmxud->szMixer
|
|
, pmxud->avcd[0].szShortName
|
|
, avcd
|
|
, cvcd
|
|
, SET);
|
|
}
|
|
|
|
//
|
|
// Now that both arrays are now fixed, set back pointers for
|
|
// the vcd's to ui lines.
|
|
//
|
|
for (imxul = 0; imxul < pmxud->cmxul; imxul++)
|
|
{
|
|
pmxud->amxul[imxul].pvcd->pmxul = &pmxud->amxul[imxul];
|
|
|
|
//
|
|
// Accumulate support bits
|
|
//
|
|
dwSupport |= pmxud->amxul[imxul].pvcd->dwSupport;
|
|
}
|
|
|
|
//
|
|
// Support bits say we have no advanced controls, so don't make
|
|
// them available.
|
|
//
|
|
if (!(dwSupport & VCD_SUPPORTF_MIXER_ADVANCED))
|
|
{
|
|
pmxud->dwFlags |= MXUD_FLAGSF_NOADVANCED;
|
|
}
|
|
|
|
//
|
|
// Propogate bad driver bit to be app global. A bad driver was
|
|
// detected during the construction of a volume description.
|
|
//
|
|
for (ivcd = 0; ivcd < pmxud->cvcd; ivcd++)
|
|
{
|
|
if (pmxud->avcd[ivcd].dwSupport & VCD_SUPPORTF_BADDRIVER)
|
|
{
|
|
dlout("Bad Control->Line mapping. Marking bad driver.");
|
|
pmxud->dwFlags |= MXUD_FLAGSF_BADDRIVER;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// Note: it isn't necessary to free/unlock the lpMaster/lpDst/lpSrc
|
|
// because they are ptr's to resources and Win32 is smart about resources
|
|
//
|
|
return (avcd != NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* Volume_DialogBox
|
|
*
|
|
* */
|
|
DWORD Volume_DialogBox(
|
|
PMIXUIDIALOG pmxud)
|
|
{
|
|
pmxud->dwReturn = MIXUI_EXIT;
|
|
if (Volume_CreateVolume(pmxud))
|
|
{
|
|
HWND hdlg;
|
|
|
|
if(NULL == pmxud->lpDialog)
|
|
{
|
|
Volume_Cleanup(pmxud);
|
|
return MIXUI_ERROR;
|
|
}
|
|
|
|
hdlg = CreateDialogIndirectParam(pmxud->hInstance
|
|
, (DLGTEMPLATE *)pmxud->lpDialog
|
|
, NULL
|
|
, VolumeProc
|
|
, (LPARAM)(LPVOID)pmxud );
|
|
|
|
if (!hdlg)
|
|
{
|
|
Volume_Cleanup(pmxud);
|
|
return MIXUI_ERROR;
|
|
}
|
|
else
|
|
{
|
|
// Unfortunately, re-registering the winclass does not re-apply any
|
|
// new icon correctly, so we must explicitly apply it here.
|
|
SendMessage (hdlg, WM_SETICON, (WPARAM) ICON_BIG,
|
|
(LPARAM) GetAppIcon (pmxud->hInstance, pmxud->mxid));
|
|
}
|
|
|
|
ShowWindow(hdlg, pmxud->nShowCmd);
|
|
}
|
|
else
|
|
{
|
|
return MIXUI_ERROR;
|
|
}
|
|
|
|
return (DWORD)(-1);
|
|
}
|
|
|
|
void DoHtmlHelp()
|
|
{
|
|
//note, using ANSI version of function because UNICODE is foobar in NT5 builds
|
|
char chDst[MAX_PATH];
|
|
WideCharToMultiByte(CP_ACP, 0, gszHtmlHelpFileName,
|
|
-1, chDst, MAX_PATH, NULL, NULL);
|
|
HtmlHelpA(GetDesktopWindow(), chDst, HH_DISPLAY_TOPIC, 0);
|
|
}
|
|
|
|
void ProcessHelp(HWND hwnd)
|
|
{
|
|
static TCHAR HelpFile[] = TEXT("SNDVOL32.HLP");
|
|
|
|
//Handle context menu help
|
|
if(bF1InMenu)
|
|
{
|
|
switch(currMenuItem)
|
|
{
|
|
case IDM_PROPERTIES:
|
|
WinHelp(hwnd, HelpFile, HELP_CONTEXTPOPUP, IDH_SNDVOL32_OPTIONS_PROPERTIES);
|
|
break;
|
|
case IDM_ADVANCED:
|
|
WinHelp(hwnd, HelpFile, HELP_CONTEXTPOPUP, IDH_SNDVOL32_OPTIONS_ADVANCED_CONTROLS);
|
|
break;
|
|
case IDM_EXIT:
|
|
WinHelp(hwnd, HelpFile, HELP_CONTEXTPOPUP, IDH_SNDVOL32_OPTIONS_EXIT);
|
|
break;
|
|
case IDM_HELPTOPICS:
|
|
WinHelp(hwnd, HelpFile, HELP_CONTEXTPOPUP, IDH_SNDVOL32_HELP_HELP_TOPICS);
|
|
break;
|
|
case IDM_HELPABOUT:
|
|
WinHelp(hwnd, HelpFile, HELP_CONTEXTPOPUP, IDH_SNDVOL32_HELP_ABOUT);
|
|
break;
|
|
default://In the default case just display the HTML Help.
|
|
DoHtmlHelp();
|
|
}
|
|
bF1InMenu = FALSE; //This flag will be set again if F1 is pressed in a menu.
|
|
}
|
|
else
|
|
DoHtmlHelp();
|
|
}
|
|
|
|
|
|
/*
|
|
* VolumeParent_WndProc
|
|
*
|
|
* A generic invisible parent window.
|
|
*
|
|
* */
|
|
LRESULT CALLBACK VolumeParent_WndProc(
|
|
HWND hwnd,
|
|
UINT msg,
|
|
WPARAM wparam,
|
|
LPARAM lparam)
|
|
{
|
|
PMIXUIDIALOG pmxud;
|
|
|
|
switch (msg)
|
|
{
|
|
case WM_CREATE:
|
|
{
|
|
LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lparam;
|
|
pmxud = (PMIXUIDIALOG)lpcs->lpCreateParams;
|
|
|
|
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pmxud);
|
|
pmxud->hParent = hwnd;
|
|
|
|
if (Volume_DialogBox(pmxud) == MIXUI_ERROR)
|
|
{
|
|
if ( !(pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER))
|
|
{
|
|
if ( Volume_NumDevs() == 0 )
|
|
Volume_ErrorMessageBox(NULL, pmxud->hInstance, IDS_ERR_NODEV);
|
|
else
|
|
Volume_ErrorMessageBox(NULL, pmxud->hInstance, IDS_ERR_HARDWARE);
|
|
}
|
|
PostMessage(hwnd, WM_CLOSE, 0, 0L);
|
|
}
|
|
return 0;
|
|
}
|
|
case WM_CLOSE:
|
|
DestroyWindow(hwnd);
|
|
return 0;
|
|
|
|
case WM_DESTROY:
|
|
//
|
|
// Post-close cleanup
|
|
//
|
|
pmxud = (PMIXUIDIALOG)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
if (!(pmxud->dwStyle & MXUD_STYLEF_NOHELP))
|
|
WinHelp(hwnd, gszHelpFileName, HELP_QUIT, 0L);
|
|
|
|
PostQuitMessage(0);
|
|
|
|
return 0;
|
|
|
|
case MYWM_HELPTOPICS:
|
|
//
|
|
// F1 Help
|
|
//
|
|
pmxud = (PMIXUIDIALOG)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
if (!(pmxud->dwStyle & MXUD_STYLEF_NOHELP))
|
|
{
|
|
ProcessHelp(hwnd);
|
|
}
|
|
break;
|
|
|
|
case MYWM_RESTART:
|
|
//
|
|
// A device change or other user property change caused a UI
|
|
// change. Sending a restart to the parent prevents ugly stuff
|
|
// like WinHelp shutting down and exiting our primary message
|
|
// loop.
|
|
//
|
|
pmxud = (PMIXUIDIALOG)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
|
|
if (!(pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER))
|
|
{
|
|
if (Volume_NumDevs() == 0)
|
|
{
|
|
Volume_ErrorMessageBox(NULL
|
|
, pmxud->hInstance
|
|
, IDS_ERR_NODEV);
|
|
PostMessage(hwnd, WM_CLOSE, 0, 0L);
|
|
|
|
}
|
|
else if (Volume_DialogBox((PMIXUIDIALOG)lparam) == MIXUI_ERROR)
|
|
{
|
|
Volume_ErrorMessageBox(NULL
|
|
, pmxud->hInstance
|
|
, IDS_ERR_HARDWARE);
|
|
PostMessage(hwnd, WM_CLOSE, 0, 0L);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Mixer_GetNumDevs() == 0
|
|
|| Volume_DialogBox((PMIXUIDIALOG)lparam) == MIXUI_ERROR)
|
|
PostMessage(hwnd, WM_CLOSE, 0, 0L);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return (DefWindowProc(hwnd, msg, wparam, lparam));
|
|
}
|
|
|
|
const TCHAR szNull[] = TEXT ("");
|
|
|
|
/*
|
|
* Parent Dialog
|
|
* */
|
|
HWND VolumeParent_DialogMain(
|
|
PMIXUIDIALOG pmxud)
|
|
{
|
|
WNDCLASS wc;
|
|
HWND hwnd;
|
|
|
|
wc.lpszClassName = gszParentClass;
|
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wc.hIcon = NULL;
|
|
wc.lpszMenuName = NULL;
|
|
wc.hbrBackground = NULL;
|
|
wc.hInstance = pmxud->hInstance;
|
|
wc.style = 0;
|
|
wc.lpfnWndProc = VolumeParent_WndProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
|
|
if (!RegisterClass(&wc))
|
|
return NULL;
|
|
|
|
hwnd = CreateWindow(gszParentClass
|
|
, szNull
|
|
, 0
|
|
, 0
|
|
, 0
|
|
, 0
|
|
, 0
|
|
, NULL
|
|
, NULL
|
|
, pmxud->hInstance
|
|
, (LPVOID)pmxud );
|
|
|
|
return hwnd;
|
|
}
|
|
|
|
/*
|
|
* Determines if what the recording destination ID
|
|
*/
|
|
|
|
HRESULT GetRecordingDestID(int mxid, DWORD *piDest)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
DWORD cDest;
|
|
int iDest;
|
|
MMRESULT mmr;
|
|
MIXERCAPS mxcaps;
|
|
|
|
if (piDest)
|
|
{
|
|
*piDest = 0;
|
|
|
|
mmr = mixerGetDevCaps(mxid, &mxcaps, sizeof(MIXERCAPS));
|
|
|
|
if (mmr == MMSYSERR_NOERROR)
|
|
{
|
|
cDest = mxcaps.cDestinations;
|
|
|
|
for (iDest = cDest - 1; iDest >= 0; iDest--)
|
|
{
|
|
MIXERLINE mlDst;
|
|
|
|
mlDst.cbStruct = sizeof ( mlDst );
|
|
mlDst.dwDestination = iDest;
|
|
|
|
if (mixerGetLineInfo((HMIXEROBJ)IntToPtr(mxid), &mlDst, MIXER_GETLINEINFOF_DESTINATION) != MMSYSERR_NOERROR)
|
|
continue;
|
|
|
|
if (Mixer_IsValidRecordingDestination ((HMIXEROBJ)IntToPtr(mxid), &mlDst))
|
|
{
|
|
*piDest = iDest;
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return(hr);
|
|
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------+
|
|
| HelpMsgFilter - filter for F1 key in dialogs |
|
|
| |
|
|
+------------------------------------------------------*/
|
|
|
|
DWORD FAR PASCAL HelpMsgFilter(int nCode, UINT wParam, DWORD_PTR lParam)
|
|
{
|
|
if (nCode >= 0)
|
|
{
|
|
LPMSG msg = (LPMSG)lParam;
|
|
|
|
if (ghwndApp && (msg->message == WM_KEYDOWN) && (msg->wParam == VK_F1))
|
|
{
|
|
if(nCode == MSGF_MENU)
|
|
bF1InMenu = TRUE;
|
|
SendMessage(ghwndApp, WM_COMMAND, (WPARAM)IDM_HELPTOPICS, 0L);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Returns the correct Destination ID for the specified device ID
|
|
*/
|
|
|
|
HRESULT GetDestination(DWORD mxid, int *piDest)
|
|
{
|
|
if (gfRecord)
|
|
{
|
|
return GetDestLineID(mxid,piDest);
|
|
}
|
|
else
|
|
{
|
|
return GetSrcLineID(mxid,piDest);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Determines line ID
|
|
*/
|
|
|
|
HRESULT GetDestLineID(int mxid, DWORD *piDest)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
MIXERLINE mlDst;
|
|
|
|
if (piDest)
|
|
{
|
|
hr = S_OK;
|
|
*piDest = 0;
|
|
|
|
mlDst.cbStruct = sizeof ( mlDst );
|
|
mlDst.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
|
|
|
|
if (mixerGetLineInfo((HMIXEROBJ)IntToPtr(mxid), &mlDst, MIXER_GETLINEINFOF_COMPONENTTYPE) == MMSYSERR_NOERROR)
|
|
{
|
|
*piDest = mlDst.dwDestination;
|
|
}
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
/*
|
|
* Determines line ID
|
|
*/
|
|
|
|
HRESULT GetSrcLineID(int mxid, DWORD *piDest)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
MIXERLINE mlDst;
|
|
|
|
if (piDest)
|
|
{
|
|
hr = S_OK;
|
|
*piDest = 0;
|
|
|
|
mlDst.cbStruct = sizeof ( mlDst );
|
|
mlDst.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
|
|
|
|
if (mixerGetLineInfo((HMIXEROBJ)IntToPtr(mxid), &mlDst, MIXER_GETLINEINFOF_COMPONENTTYPE ) == MMSYSERR_NOERROR)
|
|
{
|
|
*piDest = mlDst.dwDestination;
|
|
}
|
|
else
|
|
{
|
|
mlDst.cbStruct = sizeof ( mlDst );
|
|
mlDst.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_HEADPHONES;
|
|
|
|
if (mixerGetLineInfo((HMIXEROBJ)IntToPtr(mxid), &mlDst, MIXER_GETLINEINFOF_COMPONENTTYPE ) == MMSYSERR_NOERROR)
|
|
{
|
|
*piDest = mlDst.dwDestination;
|
|
}
|
|
else
|
|
{
|
|
mlDst.cbStruct = sizeof ( mlDst );
|
|
mlDst.dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
|
|
|
|
if (mixerGetLineInfo((HMIXEROBJ)IntToPtr(mxid), &mlDst, MIXER_GETLINEINFOF_COMPONENTTYPE ) == MMSYSERR_NOERROR)
|
|
{
|
|
*piDest = mlDst.dwDestination;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
/* - - - - - - - - - */
|
|
|
|
/*
|
|
* entry point
|
|
* */
|
|
int WINAPI WinMain(
|
|
HINSTANCE hInst,
|
|
HINSTANCE hPrev,
|
|
LPSTR lpCmdLine,
|
|
int nShowCmd)
|
|
{
|
|
int err = 0;
|
|
MIXUIDIALOG mxud;
|
|
MSG msg;
|
|
HWND hwnd;
|
|
HANDLE hAccel;
|
|
MMRESULT mmr;
|
|
TCHAR ach[2];
|
|
UINT u;
|
|
BOOL fGotDevice = FALSE;
|
|
UINT uDeviceID;
|
|
|
|
ach[0] = '\0'; // PREFIX complains if we do not init this.
|
|
LoadString(hInst, IDS_IS_RTL, ach, SIZEOF(ach));
|
|
gfIsRTL = ach[0] == TEXT('1');
|
|
|
|
//
|
|
// initialize the app instance data
|
|
//
|
|
ZeroMemory(&mxud, sizeof(mxud));
|
|
mxud.hInstance = hInst;
|
|
mxud.dwFlags = MXUD_FLAGSF_MIXER;
|
|
|
|
/* setup the message filter to handle grabbing F1 for this task */
|
|
fpfnMsgHook = (HOOKPROC)MakeProcInstance((FARPROC)HelpMsgFilter, ghInst);
|
|
fpfnOldMsgFilter = (HHOOK)SetWindowsHook(WH_MSGFILTER, fpfnMsgHook);
|
|
|
|
//
|
|
// parse the command line for "/T"
|
|
//
|
|
u = 0;
|
|
|
|
while (lpCmdLine[u] != '\0')
|
|
{
|
|
switch (lpCmdLine[u])
|
|
{
|
|
case TEXT('-'):
|
|
case TEXT('/'):
|
|
{
|
|
u++;
|
|
|
|
if (lpCmdLine[u] != '\0')
|
|
{
|
|
switch (lpCmdLine[u])
|
|
{
|
|
case TEXT('T'):
|
|
case TEXT('t'):
|
|
mxud.dwStyle |= MXUD_STYLEF_TRAYMASTER;
|
|
u++;
|
|
break;
|
|
|
|
case TEXT('S'):
|
|
case TEXT('s'):
|
|
mxud.dwStyle |= MXUD_STYLEF_SMALL;
|
|
u++;
|
|
break;
|
|
|
|
case TEXT('R'): // Should run in Record mode, not Playback (default)
|
|
case TEXT('r'):
|
|
gfRecord = TRUE;
|
|
u++;
|
|
break;
|
|
|
|
case TEXT('X'):
|
|
case TEXT('x'):
|
|
mxud.dwStyle |= MXUD_STYLEF_TRAYMASTER | MXUD_STYLEF_CLOSE;
|
|
break;
|
|
|
|
case TEXT('D'): // Should use the specified device
|
|
case TEXT('d'):
|
|
{
|
|
u++; // Skip "d" and any following spaces
|
|
while (lpCmdLine[u] != '\0' && isspace(lpCmdLine[u]))
|
|
{
|
|
u++;
|
|
}
|
|
|
|
if (lpCmdLine[u] != '\0')
|
|
{
|
|
char szDeviceID[255];
|
|
UINT uDev = 0;
|
|
|
|
while ((uDev < 2) && lpCmdLine[u] != '\0' && !isalpha(lpCmdLine[u]) && !isspace(lpCmdLine[u]))
|
|
{
|
|
szDeviceID[uDev] = lpCmdLine[u];
|
|
u++;
|
|
uDev++;
|
|
}
|
|
|
|
szDeviceID[uDev] = '\0';
|
|
|
|
uDeviceID = strtoul(szDeviceID,NULL,10);
|
|
|
|
fGotDevice = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default: // Unknown Command, just ignore it.
|
|
u++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
u++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Restore last style
|
|
//
|
|
if (!(mxud.dwStyle & (MXUD_STYLEF_TRAYMASTER|MXUD_STYLEF_SMALL)))
|
|
{
|
|
Volume_GetSetStyle(&mxud.dwStyle, GET);
|
|
}
|
|
|
|
if (mxud.dwStyle & MXUD_STYLEF_TRAYMASTER)
|
|
{
|
|
HWND hwndSV;
|
|
|
|
//
|
|
// Locate a waiting instance of the tray volume and wake it up
|
|
//
|
|
hwndSV = FindWindow(gszTrayClassName, NULL);
|
|
if (hwndSV) {
|
|
SendMessage(hwndSV, MYWM_WAKEUP,
|
|
(mxud.dwStyle & MXUD_STYLEF_CLOSE), 0);
|
|
goto mxendapp;
|
|
}
|
|
}
|
|
|
|
if (mxud.dwStyle & MXUD_STYLEF_CLOSE) {
|
|
goto mxendapp;
|
|
}
|
|
|
|
|
|
//
|
|
// Init to the default mixer
|
|
//
|
|
|
|
if (fGotDevice)
|
|
{
|
|
UINT cWaves;
|
|
|
|
if (gfRecord)
|
|
{
|
|
cWaves = waveInGetNumDevs();
|
|
}
|
|
else
|
|
{
|
|
cWaves = waveOutGetNumDevs();
|
|
}
|
|
|
|
if (uDeviceID >= cWaves)
|
|
{
|
|
fGotDevice = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
if (!fGotDevice)
|
|
{
|
|
mmr = Volume_GetDefaultMixerID(&mxud.mxid, gfRecord);
|
|
}
|
|
else
|
|
{
|
|
mxud.mxid = uDeviceID;
|
|
}
|
|
|
|
if (gfRecord)
|
|
{
|
|
if (FAILED(GetRecordingDestID(mxud.mxid,&mxud.iDest)))
|
|
{
|
|
goto mxendapp;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (FAILED(GetDestination(mxud.mxid,&mxud.iDest)))
|
|
{
|
|
goto mxendapp;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// For the tray master, get the mix id associated with the default
|
|
// wave device. If this fails, go away.
|
|
//
|
|
if (mxud.dwStyle & MXUD_STYLEF_TRAYMASTER)
|
|
{
|
|
if (mmr != MMSYSERR_NOERROR)
|
|
goto mxendapp;
|
|
mxud.dwStyle |= MXUD_STYLEF_NOHELP;
|
|
mxud.nShowCmd = SW_HIDE;
|
|
|
|
}
|
|
else
|
|
{
|
|
if (!Volume_NumDevs())
|
|
{
|
|
Volume_ErrorMessageBox(NULL, hInst, IDS_ERR_NODEV);
|
|
goto mxendapp;
|
|
}
|
|
InitVUControl(hInst);
|
|
if (!LoadString(hInst
|
|
, IDS_HELPFILENAME
|
|
, gszHelpFileName
|
|
, SIZEOF(gszHelpFileName)))
|
|
mxud.dwStyle |= MXUD_STYLEF_NOHELP;
|
|
|
|
if (!LoadString(hInst
|
|
, IDS_HTMLHELPFILENAME
|
|
, gszHtmlHelpFileName
|
|
, SIZEOF(gszHtmlHelpFileName)))
|
|
mxud.dwStyle |= MXUD_STYLEF_NOHELP;
|
|
|
|
mxud.nShowCmd = (nShowCmd == SW_SHOWMAXIMIZED)
|
|
? SW_SHOWNORMAL:nShowCmd;
|
|
if (!(mxud.dwStyle & MXUD_STYLEF_SMALL))
|
|
mxud.dwStyle |= MXUD_STYLEF_STATUS; // has status bar
|
|
}
|
|
|
|
//
|
|
// Use the common controls
|
|
//
|
|
InitCommonControls();
|
|
hAccel = LoadAccelerators(hInst, MAKEINTRESOURCE(IDR_VOLUMEACCEL));
|
|
|
|
hwnd = VolumeParent_DialogMain(&mxud);
|
|
|
|
//Initialize the handle which the hook for F1 help will use.
|
|
ghwndApp = mxud.hwnd;
|
|
|
|
if (hwnd)
|
|
{
|
|
while (GetMessage(&msg, NULL, 0, 0))
|
|
{
|
|
if (mxud.hwnd) {
|
|
if (hAccel && TranslateAccelerator(mxud.hwnd, hAccel, &msg))
|
|
continue;
|
|
|
|
if (IsDialogMessage(mxud.hwnd,&msg))
|
|
continue;
|
|
}
|
|
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
mxendapp:
|
|
/* if the message hook was installed, remove it and free */
|
|
/* up our proc instance for it. */
|
|
if (fpfnOldMsgFilter){
|
|
UnhookWindowsHook(WH_MSGFILTER, fpfnMsgHook);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
|
|
void FreeAppIcon ()
|
|
{
|
|
if (ghiconApp)
|
|
{
|
|
DestroyIcon (ghiconApp);
|
|
ghiconApp = NULL;
|
|
}
|
|
}
|
|
|
|
// TODO: Move to "regstr.h"
|
|
#define REGSTR_KEY_BRANDING TEXT("Branding")
|
|
#define REGSTR_VAL_AUDIO_BITMAP TEXT("bitmap")
|
|
#define REGSTR_VAL_AUDIO_ICON TEXT("icon")
|
|
#define REGSTR_VAL_AUDIO_URL TEXT("url")
|
|
|
|
HKEY OpenDeviceBrandRegKey (UINT uiMixID)
|
|
{
|
|
|
|
HKEY hkeyBrand = NULL;
|
|
HKEY hkeyDevice = OpenDeviceRegKey (uiMixID, KEY_READ);
|
|
|
|
if (hkeyDevice)
|
|
{
|
|
if (ERROR_SUCCESS != RegOpenKeyEx (hkeyDevice, REGSTR_KEY_BRANDING, 0, KEY_READ, &hkeyBrand))
|
|
hkeyBrand = NULL; // Make sure NULL on failure
|
|
|
|
// Close the Device key
|
|
RegCloseKey (hkeyDevice);
|
|
}
|
|
|
|
return hkeyBrand;
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// Microsoft Confidential - DO NOT COPY THIS METHOD INTO ANY APPLICATION, THIS MEANS YOU!!!
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
PTCHAR GetInterfaceName (DWORD dwMixerID)
|
|
{
|
|
MMRESULT mmr;
|
|
ULONG cbSize=0;
|
|
TCHAR *szInterfaceName=NULL;
|
|
|
|
//Query for the Device interface name
|
|
mmr = mixerMessage(HMIXER_INDEX(dwMixerID), DRV_QUERYDEVICEINTERFACESIZE, (DWORD_PTR)&cbSize, 0L);
|
|
if(MMSYSERR_NOERROR == mmr)
|
|
{
|
|
szInterfaceName = (TCHAR *)GlobalAllocPtr(GHND, (cbSize+1)*sizeof(TCHAR));
|
|
if(!szInterfaceName)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
mmr = mixerMessage(HMIXER_INDEX(dwMixerID), DRV_QUERYDEVICEINTERFACE, (DWORD_PTR)szInterfaceName, cbSize);
|
|
if(MMSYSERR_NOERROR != mmr)
|
|
{
|
|
GlobalFreePtr(szInterfaceName);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return szInterfaceName;
|
|
}
|
|
|
|
|
|
HKEY OpenDeviceRegKey (UINT uiMixID, REGSAM sam)
|
|
{
|
|
|
|
HKEY hkeyDevice = NULL;
|
|
PTCHAR szInterfaceName = GetInterfaceName (uiMixID);
|
|
|
|
if (szInterfaceName)
|
|
{
|
|
HDEVINFO DeviceInfoSet = SetupDiCreateDeviceInfoList (NULL, NULL);
|
|
|
|
if (INVALID_HANDLE_VALUE != DeviceInfoSet)
|
|
{
|
|
SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
|
|
DeviceInterfaceData.cbSize = sizeof (SP_DEVICE_INTERFACE_DATA);
|
|
|
|
if (SetupDiOpenDeviceInterface (DeviceInfoSet, szInterfaceName,
|
|
0, &DeviceInterfaceData))
|
|
{
|
|
DWORD dwRequiredSize;
|
|
SP_DEVINFO_DATA DeviceInfoData;
|
|
DeviceInfoData.cbSize = sizeof (SP_DEVINFO_DATA);
|
|
|
|
// Ignore error, it always returns "ERROR_INSUFFICIENT_BUFFER" even though
|
|
// the "SP_DEVICE_INTERFACE_DETAIL_DATA" parameter is supposed to be optional.
|
|
(void) SetupDiGetDeviceInterfaceDetail (DeviceInfoSet, &DeviceInterfaceData,
|
|
NULL, 0, &dwRequiredSize, &DeviceInfoData);
|
|
// Open device reg key
|
|
hkeyDevice = SetupDiOpenDevRegKey (DeviceInfoSet, &DeviceInfoData,
|
|
DICS_FLAG_GLOBAL, 0,
|
|
DIREG_DRV, sam);
|
|
|
|
}
|
|
SetupDiDestroyDeviceInfoList (DeviceInfoSet);
|
|
}
|
|
GlobalFreePtr (szInterfaceName);
|
|
}
|
|
|
|
return hkeyDevice;
|
|
|
|
}
|
|
|
|
|
|
HICON GetAppIcon (HINSTANCE hInst, UINT uiMixID)
|
|
{
|
|
|
|
HKEY hkeyBrand = OpenDeviceBrandRegKey (uiMixID);
|
|
|
|
FreeAppIcon ();
|
|
|
|
if (hkeyBrand)
|
|
{
|
|
WCHAR szBuffer[MAX_PATH];
|
|
DWORD dwType = REG_SZ;
|
|
DWORD cb = sizeof (szBuffer);
|
|
|
|
if (ERROR_SUCCESS == RegQueryValueEx (hkeyBrand, REGSTR_VAL_AUDIO_ICON, NULL, &dwType, (LPBYTE)szBuffer, &cb))
|
|
{
|
|
if (REG_SZ == dwType)
|
|
{
|
|
WCHAR* pszComma = wcschr (szBuffer, L',');
|
|
if (pszComma)
|
|
{
|
|
WCHAR* pszResourceID = pszComma + 1;
|
|
HANDLE hResource;
|
|
|
|
// Remove comma delimeter
|
|
*pszComma = L'\0';
|
|
|
|
// Should be a resource module and a resource ID
|
|
hResource = LoadLibrary (szBuffer);
|
|
if (!hResource)
|
|
{
|
|
TCHAR szDriversPath[MAX_PATH];
|
|
|
|
// If we didn't find it on the normal search path, try looking
|
|
// in the "drivers" directory.
|
|
if (GetSystemDirectory (szDriversPath, MAX_PATH))
|
|
{
|
|
HRESULT hr = StringCchCat(szDriversPath, SIZEOF(szDriversPath), TEXT("\\drivers\\"));
|
|
if( hr == S_OK )
|
|
{
|
|
hr = StringCchCat(szDriversPath, SIZEOF(szDriversPath), szBuffer);
|
|
if( hr == S_OK )
|
|
{
|
|
hResource = LoadLibrary (szDriversPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
if (hResource)
|
|
{
|
|
ghiconApp = LoadImage (hResource, MAKEINTRESOURCE(_wtoi (pszResourceID)), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
|
|
FreeLibrary (hResource);
|
|
}
|
|
}
|
|
else
|
|
// Should be an *.ico file
|
|
ghiconApp = LoadImage (NULL, szBuffer, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE);
|
|
}
|
|
}
|
|
RegCloseKey (hkeyBrand);
|
|
|
|
// Return the custom icon
|
|
if (ghiconApp)
|
|
return ghiconApp;
|
|
}
|
|
|
|
return (LoadIcon (hInst, MAKEINTRESOURCE (IDI_MIXER)));
|
|
|
|
}
|