Source code of Windows XP (NT5)
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.

2979 lines
79 KiB

  1. /*****************************************************************************
  2. *
  3. * Component: sndvol32.exe
  4. * File: volume.c
  5. * Purpose: main application module
  6. *
  7. * Copyright (c) 1985-1999 Microsoft Corporation
  8. *
  9. *****************************************************************************/
  10. #include <windows.h>
  11. #include <windowsx.h>
  12. #include <mmsystem.h>
  13. #include <commctrl.h>
  14. #include <shellapi.h>
  15. #include <dbt.h>
  16. #include <htmlhelp.h>
  17. #include <regstr.h>
  18. #include "vu.h"
  19. #include "dlg.h"
  20. #include "volids.h"
  21. #include "volumei.h"
  22. #include "utils.h"
  23. #include "stdlib.h"
  24. #include "helpids.h"
  25. #if(WINVER >= 0x040A)
  26. //Support for new WM_DEVICECHANGE behaviour in NT5
  27. /////////////////////////////////////////////////
  28. #include <objbase.h>
  29. #include <setupapi.h>
  30. #include <cfgmgr32.h>
  31. #include <initguid.h>
  32. #include <devguid.h>
  33. #include <mmddkp.h>
  34. #include <ks.h>
  35. #include <ksmedia.h>
  36. #include <wchar.h>
  37. #define HMIXER_INDEX(i) ((HMIXER)IntToPtr(i))
  38. HDEVNOTIFY DeviceEventContext = NULL;
  39. BOOL bUseHandle = FALSE; //Indicates whether a handle is being used for device notification,
  40. //instead of the general KSCATEGORY_AUDIO
  41. //////////////////////////////////////////////////////
  42. //////////////////////////////////////////////////////
  43. #endif /* WINVER >= 0x040A */
  44. void Volume_SetControl(PMIXUIDIALOG pmxud, HWND hctl, int iLine, int iCtl);
  45. void Volume_GetControl(PMIXUIDIALOG pmxud, HWND hctl, int iLine, int iCtl);
  46. DWORD Volume_DialogBox(PMIXUIDIALOG pmxud);
  47. void Volume_Cleanup(PMIXUIDIALOG pmxud);
  48. void Volume_InitLine(PMIXUIDIALOG pmxud, DWORD iLine);
  49. HRESULT GetDestLineID(int mxid, DWORD *piDest);
  50. HRESULT GetSrcLineID(int mxid, DWORD *piDest);
  51. HRESULT GetDestination(DWORD mxid, int *piDest);
  52. HICON GetAppIcon (HINSTANCE hInst, UINT uiMixID);
  53. void FreeAppIcon ();
  54. HKEY OpenDeviceRegKey (UINT uiMixID, REGSAM sam);
  55. PTCHAR GetInterfaceName (DWORD dwMixerID);
  56. HKEY OpenDeviceBrandRegKey (UINT uiMixID);
  57. /* string declarations */
  58. const TCHAR gszParentClass[] = TEXT( "SNDVOL32" );
  59. const TCHAR gszAppClassName[] = TEXT( "Volume Control" );
  60. const TCHAR gszTrayClassName[] = TEXT( "Tray Volume" );
  61. /* app global
  62. * */
  63. TCHAR gszHelpFileName[MAX_PATH];
  64. TCHAR gszHtmlHelpFileName[MAX_PATH];
  65. BOOL gfIsRTL;
  66. BOOL fCanDismissWindow = FALSE;
  67. BOOL gfRecord = FALSE;
  68. HICON ghiconApp = NULL;
  69. static HHOOK fpfnOldMsgFilter;
  70. static HOOKPROC fpfnMsgHook;
  71. //Data used for supporting context menu help
  72. BOOL bF1InMenu=FALSE; //If true F1 was pressed on a menu item.
  73. UINT currMenuItem=0; //The current selected menu item if any.
  74. static HWND ghwndApp=NULL;
  75. /*
  76. * Number of uniquely supported devices.
  77. *
  78. * */
  79. int Volume_NumDevs()
  80. {
  81. int cNumDevs = 0;
  82. #pragma message("----Nonmixer issue here.")
  83. // cNumDevs = Nonmixer_GetNumDevs();
  84. cNumDevs += Mixer_GetNumDevs();
  85. return cNumDevs;
  86. }
  87. /*
  88. * Volume_EndDialog
  89. *
  90. * */
  91. void Volume_EndDialog(
  92. PMIXUIDIALOG pmxud,
  93. DWORD dwErr,
  94. MMRESULT mmr)
  95. {
  96. pmxud->dwReturn = dwErr;
  97. if (dwErr == MIXUI_MMSYSERR)
  98. pmxud->mmr = mmr;
  99. if (IsWindow(pmxud->hwnd))
  100. PostMessage(pmxud->hwnd, WM_CLOSE, 0, 0);
  101. }
  102. /*
  103. * Volume_OnMenuCommand
  104. *
  105. * */
  106. BOOL Volume_OnMenuCommand(
  107. HWND hwnd,
  108. int id,
  109. HWND hctl,
  110. UINT unotify)
  111. {
  112. PMIXUIDIALOG pmxud = GETMIXUIDIALOG(hwnd);
  113. switch(id)
  114. {
  115. case IDM_PROPERTIES:
  116. if (Properties(pmxud, hwnd))
  117. {
  118. Volume_GetSetStyle(&pmxud->dwStyle, SET);
  119. Volume_EndDialog(pmxud, MIXUI_RESTART, 0);
  120. }
  121. break;
  122. case IDM_HELPTOPICS:
  123. SendMessage(pmxud->hParent, MYWM_HELPTOPICS, 0, 0L);
  124. break;
  125. case IDM_HELPABOUT:
  126. {
  127. TCHAR ach[256];
  128. GetWindowText(hwnd, ach, SIZEOF(ach));
  129. ShellAbout(hwnd
  130. , ach
  131. , NULL
  132. , (HICON)SendMessage(hwnd, WM_QUERYDRAGICON, 0, 0L));
  133. break;
  134. }
  135. case IDM_ADVANCED:
  136. {
  137. HMENU hmenu;
  138. pmxud->dwStyle ^= MXUD_STYLEF_ADVANCED;
  139. hmenu = GetMenu(hwnd);
  140. if (hmenu)
  141. {
  142. CheckMenuItem(hmenu, IDM_ADVANCED, MF_BYCOMMAND
  143. | ((pmxud->dwStyle & MXUD_STYLEF_ADVANCED)?MF_CHECKED:MF_UNCHECKED));
  144. }
  145. Volume_GetSetStyle(&pmxud->dwStyle, SET);
  146. Volume_EndDialog(pmxud, MIXUI_RESTART, 0);
  147. break;
  148. }
  149. case IDM_SMALLMODESWITCH:
  150. if (!(pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER))
  151. {
  152. pmxud->dwStyle ^= MXUD_STYLEF_SMALL;
  153. if (pmxud->dwStyle & MXUD_STYLEF_SMALL)
  154. {
  155. pmxud->dwStyle &= ~MXUD_STYLEF_STATUS;
  156. }
  157. else
  158. pmxud->dwStyle |= MXUD_STYLEF_STATUS;
  159. Volume_GetSetStyle(&pmxud->dwStyle, SET);
  160. Volume_EndDialog(pmxud, MIXUI_RESTART, 0);
  161. }
  162. break;
  163. case IDM_EXIT:
  164. Volume_EndDialog(pmxud, MIXUI_EXIT, 0);
  165. return TRUE;
  166. }
  167. return FALSE;
  168. }
  169. /*
  170. * Volume_OnCommand
  171. *
  172. * - Process WM_COMMAND
  173. *
  174. * Note: We need a 2 way mapping. Dialog control -> Mixer control
  175. * and Mixer control -> Dialog control.
  176. *
  177. * */
  178. void Volume_OnCommand(
  179. HWND hdlg,
  180. int id,
  181. HWND hctl,
  182. UINT unotify)
  183. {
  184. int iMixerLine;
  185. PMIXUIDIALOG pmxud = GETMIXUIDIALOG(hdlg);
  186. //
  187. // Filter menu messages
  188. //
  189. if (Volume_OnMenuCommand(hdlg, id, hctl, unotify))
  190. return;
  191. // Each control is offset from the original template control by IDOFFSET.
  192. // e.g.
  193. // IDC_VOLUME, IDC_VOLUME+IDOFFSET, .. IDC_VOLUME+(IDOFFSET*cMixerLines)
  194. //
  195. iMixerLine = id/IDOFFSET - 1;
  196. switch ((id % IDOFFSET) + IDC_MIXERCONTROLS)
  197. {
  198. case IDC_SWITCH:
  199. Volume_SetControl(pmxud, hctl, iMixerLine, MIXUI_SWITCH);
  200. break;
  201. case IDC_ADVANCED:
  202. if (MXUD_ADVANCED(pmxud) && !(pmxud->dwStyle & MXUD_STYLEF_SMALL))
  203. Volume_SetControl(pmxud, hctl, iMixerLine, MIXUI_ADVANCED);
  204. break;
  205. case IDC_MULTICHANNEL:
  206. Volume_SetControl(pmxud, hctl, iMixerLine, MIXUI_MULTICHANNEL);
  207. break;
  208. }
  209. }
  210. /*
  211. * Volume_GetLineItem
  212. *
  213. * - Helper function.
  214. * */
  215. HWND Volume_GetLineItem(
  216. HWND hdlg,
  217. DWORD iLine,
  218. DWORD idCtrl)
  219. {
  220. HWND hwnd;
  221. DWORD id;
  222. id = (iLine * IDOFFSET) + idCtrl;
  223. hwnd = GetDlgItem(hdlg, id);
  224. return hwnd;
  225. }
  226. /* - - - - - - - - - */
  227. /*
  228. * Volume_TimeProc
  229. *
  230. * This is the callback for the periodic timer that does updates for
  231. * controls that need to be polled. We only allocate one per app to keep
  232. * the number of callbacks down.
  233. */
  234. void CALLBACK Volume_TimeProc(
  235. UINT idEvent,
  236. UINT uReserved,
  237. DWORD_PTR dwUser,
  238. DWORD_PTR dwReserved1,
  239. DWORD_PTR dwReserved2)
  240. {
  241. PMIXUIDIALOG pmxud = (PMIXUIDIALOG)dwUser;
  242. if (!(pmxud->dwFlags & MXUD_FLAGSF_USETIMER))
  243. return;
  244. if (pmxud->cTimeInQueue < 5)
  245. {
  246. pmxud->cTimeInQueue++;
  247. PostMessage(pmxud->hwnd, MYWM_TIMER, 0, 0L);
  248. }
  249. }
  250. #define PROPATOM TEXT("dingprivprop")
  251. const TCHAR gszDingPropAtom[] = PROPATOM;
  252. #define SETPROP(x,y) SetProp((x), gszDingPropAtom, (HANDLE)(y))
  253. #define GETPROP(x) (PMIXUIDIALOG)GetProp((x), gszDingPropAtom)
  254. #define REMOVEPROP(x) RemoveProp(x,gszDingPropAtom)
  255. LRESULT CALLBACK Volume_TrayVolProc(
  256. HWND hwnd,
  257. UINT umsg,
  258. WPARAM wParam,
  259. LPARAM lParam)
  260. {
  261. PMIXUIDIALOG pmxud = (PMIXUIDIALOG)GETPROP(hwnd);
  262. static const TCHAR cszDefSnd[] = TEXT(".Default");
  263. if (umsg == WM_KILLFOCUS)
  264. {
  265. //
  266. // if we've just been made inactive via keyboard, clear the signal
  267. //
  268. pmxud->dwTrayInfo &= ~MXUD_TRAYINFOF_SIGNAL;
  269. }
  270. if (umsg == WM_KEYUP && (pmxud->dwTrayInfo & MXUD_TRAYINFOF_SIGNAL))
  271. {
  272. if (wParam == VK_UP || wParam == VK_DOWN || wParam == VK_END ||
  273. wParam == VK_HOME || wParam == VK_LEFT || wParam == VK_RIGHT ||
  274. wParam == VK_PRIOR || wParam == VK_NEXT || wParam == VK_SPACE)
  275. {
  276. PlaySound(cszDefSnd, NULL, SND_ASYNC | SND_ALIAS);
  277. pmxud->dwTrayInfo &= ~MXUD_TRAYINFOF_SIGNAL;
  278. }
  279. }
  280. if (umsg == WM_LBUTTONUP && (pmxud->dwTrayInfo & MXUD_TRAYINFOF_SIGNAL))
  281. {
  282. PlaySound(cszDefSnd, NULL, SND_ASYNC | SND_ALIAS);
  283. pmxud->dwTrayInfo &= ~MXUD_TRAYINFOF_SIGNAL;
  284. }
  285. return CallWindowProc(pmxud->lpfnTrayVol, hwnd, umsg, wParam, lParam);
  286. }
  287. #if(WINVER >= 0x040A)
  288. void DeviceChange_Cleanup()
  289. {
  290. if (DeviceEventContext)
  291. {
  292. UnregisterDeviceNotification(DeviceEventContext);
  293. DeviceEventContext = 0;
  294. }
  295. bUseHandle = FALSE;
  296. return;
  297. }
  298. /*
  299. **************************************************************************************************
  300. GetDeviceHandle()
  301. given a mixerID this functions opens its corresponding device handle. This handle can be used
  302. to register for DeviceNotifications.
  303. dwMixerID -- The mixer ID
  304. phDevice -- a pointer to a handle. This pointer will hold the handle value if the function is
  305. successful
  306. return values -- If the handle could be obtained successfully the return vlaue is TRUE.
  307. **************************************************************************************************
  308. */
  309. BOOL GetDeviceHandle(DWORD dwMixerID, HANDLE *phDevice)
  310. {
  311. MMRESULT mmr;
  312. ULONG cbSize=0;
  313. TCHAR *szInterfaceName=NULL;
  314. //Query for the Device interface name
  315. mmr = mixerMessage((HMIXER)ULongToPtr(dwMixerID), DRV_QUERYDEVICEINTERFACESIZE, (DWORD_PTR)&cbSize, 0L);
  316. if(MMSYSERR_NOERROR == mmr)
  317. {
  318. szInterfaceName = (TCHAR *)GlobalAllocPtr(GHND, (cbSize+1)*sizeof(TCHAR));
  319. if(!szInterfaceName)
  320. {
  321. return FALSE;
  322. }
  323. mmr = mixerMessage((HMIXER)ULongToPtr(dwMixerID), DRV_QUERYDEVICEINTERFACE, (DWORD_PTR)szInterfaceName, cbSize);
  324. if(MMSYSERR_NOERROR != mmr)
  325. {
  326. GlobalFreePtr(szInterfaceName);
  327. return FALSE;
  328. }
  329. }
  330. else
  331. {
  332. return FALSE;
  333. }
  334. //Get an handle on the device interface name.
  335. *phDevice = CreateFile(szInterfaceName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
  336. NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  337. GlobalFreePtr(szInterfaceName);
  338. if(INVALID_HANDLE_VALUE == *phDevice)
  339. {
  340. return FALSE;
  341. }
  342. return TRUE;
  343. }
  344. /* DeviceChange_Init()
  345. * First time initialization for WM_DEVICECHANGE messages
  346. *
  347. * On NT 5.0, you have to register for device notification
  348. */
  349. BOOL DeviceChange_Init(HWND hWnd, DWORD dwMixerID)
  350. {
  351. DEV_BROADCAST_HANDLE DevBrodHandle;
  352. DEV_BROADCAST_DEVICEINTERFACE dbi;
  353. HANDLE hMixerDevice;
  354. MMRESULT mmr;
  355. //If we had registered already for device notifications, unregister ourselves.
  356. DeviceChange_Cleanup();
  357. //If we get the device handle register for device notifications on it.
  358. if(GetDeviceHandle(dwMixerID, &hMixerDevice))
  359. {
  360. memset(&DevBrodHandle, 0, sizeof(DEV_BROADCAST_HANDLE));
  361. DevBrodHandle.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
  362. DevBrodHandle.dbch_devicetype = DBT_DEVTYP_HANDLE;
  363. DevBrodHandle.dbch_handle = hMixerDevice;
  364. DeviceEventContext = RegisterDeviceNotification(hWnd, &DevBrodHandle,
  365. DEVICE_NOTIFY_WINDOW_HANDLE);
  366. if(hMixerDevice)
  367. {
  368. CloseHandle(hMixerDevice);
  369. hMixerDevice = NULL;
  370. }
  371. if(DeviceEventContext)
  372. {
  373. bUseHandle = TRUE;
  374. return TRUE;
  375. }
  376. }
  377. if(!DeviceEventContext)
  378. {
  379. //Register for notifications from all audio devices. KSCATEGORY_AUDIO gives notifications
  380. //on device arrival and removal. We cannot identify which device the notification has arrived for
  381. //but we can take some precautionary measures on these messages so that we do not crash.
  382. dbi.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
  383. dbi.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
  384. dbi.dbcc_reserved = 0;
  385. dbi.dbcc_classguid = KSCATEGORY_AUDIO;
  386. dbi.dbcc_name[0] = TEXT('\0');
  387. DeviceEventContext = RegisterDeviceNotification(hWnd,
  388. (PVOID)&dbi,
  389. DEVICE_NOTIFY_WINDOW_HANDLE);
  390. if(!DeviceEventContext)
  391. return FALSE;
  392. }
  393. return TRUE;
  394. }
  395. #endif /* WINVER >= 0x040A */
  396. //fixes bug where controls are lopped off on high-contract extra-large modes
  397. void AdjustForStatusBar(PMIXUIDIALOG pmxud)
  398. {
  399. RECT statusrect, windowrect;
  400. statusrect.bottom = 0;
  401. statusrect.top = 0;
  402. if (pmxud)
  403. {
  404. if (pmxud->hStatus)
  405. {
  406. GetClientRect(pmxud->hStatus,&statusrect);
  407. GetWindowRect(pmxud->hwnd,&windowrect);
  408. if (statusrect.bottom - statusrect.top > 20)
  409. {
  410. int y_adjustment = (statusrect.bottom - statusrect.top) - 20;
  411. MoveWindow(pmxud->hwnd, windowrect.left, windowrect.top, windowrect.right - windowrect.left,
  412. (windowrect.bottom - windowrect.top) + y_adjustment, FALSE );
  413. GetClientRect(pmxud->hwnd,&windowrect);
  414. MoveWindow(pmxud->hStatus, statusrect.left, windowrect.bottom - (statusrect.bottom - statusrect.top), statusrect.right - statusrect.left,
  415. statusrect.bottom - statusrect.top, FALSE );
  416. }
  417. } //end if hStatus is valid
  418. } //end if pmxud is not null
  419. }
  420. /*
  421. *
  422. * */
  423. BOOL Volume_Init(
  424. PMIXUIDIALOG pmxud)
  425. {
  426. DWORD iLine, ictrl;
  427. RECT rc, rcWnd;
  428. if (!Mixer_Init(pmxud) && !Nonmixer_Init(pmxud))
  429. Volume_EndDialog(pmxud, MIXUI_EXIT, 0);
  430. //
  431. // For all line controls, make sure we initialize the values.
  432. //
  433. for (iLine = 0; iLine < pmxud->cmxul; iLine++)
  434. {
  435. //
  436. // init the ui control
  437. //
  438. Volume_InitLine(pmxud, iLine);
  439. for (ictrl = MIXUI_FIRST; ictrl <= MIXUI_LAST; ictrl++)
  440. {
  441. PMIXUICTRL pmxc = &pmxud->amxul[iLine].acr[ictrl];
  442. //
  443. // set initial settings
  444. //
  445. if (pmxc->state == MIXUI_CONTROL_INITIALIZED)
  446. Volume_GetControl(pmxud, pmxc->hwnd, iLine, ictrl);
  447. }
  448. }
  449. if (!(pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER))
  450. {
  451. RECT rcBase;
  452. HWND hBase;
  453. RECT rcAdv,rcBorder;
  454. HWND hAdv,hBorder;
  455. DWORD i;
  456. LONG lPrev;
  457. POINT pos;
  458. HMENU hmenu;
  459. HMONITOR hMonitor;
  460. MONITORINFO monitorInfo;
  461. if (GetWindowRect(pmxud->hwnd, &rcWnd))
  462. {
  463. if (pmxud->cmxul == 1)
  464. {
  465. // Adjust size if small
  466. if (pmxud->dwStyle & MXUD_STYLEF_SMALL)
  467. rcWnd.right -= 20;
  468. ShowWindow(GetDlgItem(pmxud->hwnd, IDC_BORDER), SW_HIDE);
  469. }
  470. if (!Volume_GetSetRegistryRect(pmxud->szMixer
  471. , pmxud->szDestination
  472. , &rc
  473. , GET))
  474. {
  475. rc.left = rcWnd.left;
  476. rc.top = rcWnd.top;
  477. }
  478. else
  479. {
  480. // Check if the rect is visible is any of the monitors
  481. if (!MonitorFromRect(&rc, MONITOR_DEFAULTTONULL))
  482. {
  483. //The window is not visible. Let's center it in the nearest monitor.
  484. //Note: the window could be in this state if (1) the display mode was changed from
  485. //a high-resolution to a lower resolution, with the mixer in the corner. Or,
  486. //(2) the multi-mon configuration was rearranged.
  487. hMonitor = MonitorFromRect(&rc, MONITOR_DEFAULTTONEAREST);
  488. if (hMonitor)
  489. {
  490. monitorInfo.cbSize = sizeof(MONITORINFO);
  491. if (GetMonitorInfo(hMonitor, &monitorInfo))
  492. {
  493. rc.left = ((monitorInfo.rcWork.right - monitorInfo.rcWork.left) - (rcWnd.right - rcWnd.left)) / 2; //center in x
  494. rc.top = ((monitorInfo.rcWork.bottom - monitorInfo.rcWork.top) - (rcWnd.bottom - rcWnd.top)) / 3; //and a little towards the top
  495. }
  496. }
  497. }
  498. //else, the window is visible, so let's leave the (x,y) as read from the settings
  499. }
  500. //
  501. // Adjusted bottom to match switch bottom
  502. //
  503. if (!(pmxud->dwStyle & MXUD_STYLEF_SMALL))
  504. {
  505. hBase = GetDlgItem(pmxud->hwnd, IDC_SWITCH);
  506. if (hBase && GetWindowRect(hBase, &rcBase))
  507. {
  508. rcWnd.bottom = rcBase.bottom;
  509. }
  510. //
  511. // Adjusted bottom to match "Advanced" bottom
  512. //
  513. if (MXUD_ADVANCED(pmxud))
  514. {
  515. hAdv = GetDlgItem(pmxud->hwnd, IDC_ADVANCED);
  516. if (hAdv && GetWindowRect(hAdv, &rcAdv))
  517. {
  518. lPrev = rcWnd.bottom;
  519. rcWnd.bottom = rcAdv.bottom;
  520. //
  521. // Adjust height of all border lines
  522. //
  523. lPrev = rcWnd.bottom - lPrev;
  524. for (i = 0; i < pmxud->cmxul; i++)
  525. {
  526. hBorder = GetDlgItem(pmxud->hwnd,
  527. IDC_BORDER+(IDOFFSET*i));
  528. if (hBorder && GetWindowRect(hBorder, &rcBorder))
  529. {
  530. MapWindowPoints(NULL, pmxud->hwnd, (LPPOINT)&rcBorder, 2);
  531. pos.x = rcBorder.left;
  532. pos.y = rcBorder.top;
  533. MoveWindow(hBorder
  534. , pos.x
  535. , pos.y
  536. , rcBorder.right - rcBorder.left
  537. , (rcBorder.bottom - rcBorder.top) + lPrev
  538. , TRUE );
  539. }
  540. }
  541. }
  542. }
  543. //
  544. // Allocate some more space.
  545. //
  546. rcWnd.bottom += 28;
  547. }
  548. MoveWindow(pmxud->hwnd, rc.left, rc.top, rcWnd.right - rcWnd.left,
  549. rcWnd.bottom - rcWnd.top, FALSE );
  550. //
  551. // Tack on the status bar after resizing the dialog
  552. //
  553. //init status bar hwnd variable
  554. pmxud->hStatus = NULL;
  555. if (pmxud->dwStyle & MXUD_STYLEF_STATUS)
  556. {
  557. MapWindowPoints(NULL, pmxud->hwnd, (LPPOINT)&rcWnd, 2);
  558. pos.x = rcWnd.left;
  559. pos.y = rcWnd.bottom;
  560. pmxud->hStatus = CreateWindowEx ( gfIsRTL ? WS_EX_LEFTSCROLLBAR | WS_EX_RIGHT | WS_EX_RTLREADING : 0
  561. , STATUSCLASSNAME
  562. , TEXT ("X")
  563. , WS_VISIBLE | WS_CHILD
  564. , 0
  565. , pos.y
  566. , rcWnd.right - rcWnd.left
  567. , 14
  568. , pmxud->hwnd
  569. , NULL
  570. , pmxud->hInstance
  571. , NULL);
  572. if (pmxud->hStatus)
  573. {
  574. SendMessage(pmxud->hStatus, WM_SETTEXT, 0,
  575. (LPARAM)(LPVOID)(LPTSTR)pmxud->szMixer);
  576. }
  577. else
  578. pmxud->dwStyle ^= MXUD_STYLEF_STATUS;
  579. }
  580. AdjustForStatusBar(pmxud);
  581. hmenu = GetMenu(pmxud->hwnd);
  582. CheckMenuItem(hmenu, IDM_ADVANCED, MF_BYCOMMAND
  583. | ((pmxud->dwStyle & MXUD_STYLEF_ADVANCED)?MF_CHECKED:MF_UNCHECKED));
  584. if (pmxud->dwStyle & MXUD_STYLEF_SMALL || pmxud->dwFlags & MXUD_FLAGSF_NOADVANCED)
  585. EnableMenuItem(hmenu, IDM_ADVANCED, MF_BYCOMMAND | MF_GRAYED);
  586. }
  587. if (pmxud->dwFlags & MXUD_FLAGSF_USETIMER)
  588. {
  589. pmxud->cTimeInQueue = 0;
  590. pmxud->uTimerID = timeSetEvent(100
  591. , 50
  592. , Volume_TimeProc
  593. , (DWORD_PTR)pmxud
  594. , TIME_PERIODIC);
  595. if (!pmxud->uTimerID)
  596. pmxud->dwFlags &= ~MXUD_FLAGSF_USETIMER;
  597. }
  598. }
  599. else
  600. {
  601. WNDPROC lpfnOldTrayVol;
  602. HWND hVol;
  603. hVol = pmxud->amxul[0].acr[MIXUI_VOLUME].hwnd;
  604. lpfnOldTrayVol = SubclassWindow(hVol, Volume_TrayVolProc);
  605. if (lpfnOldTrayVol)
  606. {
  607. pmxud->lpfnTrayVol = lpfnOldTrayVol;
  608. SETPROP(hVol, pmxud);
  609. }
  610. }
  611. #if(WINVER >= 0x040A)
  612. //Register for WM_DEVICECHANGE messages
  613. DeviceChange_Init(pmxud->hwnd, pmxud->mxid);
  614. #endif /* WINVER >= 0x040A */
  615. return TRUE;
  616. }
  617. /*
  618. * Volume_OnInitDialog
  619. *
  620. * - Process WM_INITDIALOG
  621. *
  622. * */
  623. BOOL Volume_OnInitDialog(
  624. HWND hwnd,
  625. HWND hwndFocus,
  626. LPARAM lParam)
  627. {
  628. PMIXUIDIALOG pmxud;
  629. HWND hwndChild;
  630. RECT rc;
  631. //
  632. // set app instance data
  633. //
  634. SETMIXUIDIALOG(hwnd, lParam);
  635. pmxud = (PMIXUIDIALOG)(LPVOID)lParam;
  636. pmxud->hwnd = hwnd;
  637. if (!Volume_Init(pmxud))
  638. {
  639. Volume_EndDialog(pmxud, MIXUI_EXIT, 0);
  640. }
  641. else
  642. {
  643. if (pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER)
  644. PostMessage(hwnd, MYWM_WAKEUP, 0, 0);
  645. }
  646. //
  647. // If we are so big that we need a scroll bar, then make one.
  648. //
  649. rc.top = rc.bottom = 0;
  650. rc.left = 60; // typical width of a dialog template
  651. rc.right = Dlg_HorizSize(pmxud->lpDialog);
  652. MapDialogRect(hwnd, &rc);
  653. pmxud->cxDlgContent = rc.right;
  654. pmxud->cxScroll = rc.left;
  655. pmxud->xOffset = 0;
  656. GetClientRect(hwnd, &rc);
  657. pmxud->xOffset = 0;
  658. pmxud->cxDlgWidth = rc.right;
  659. if (rc.right < pmxud->cxDlgContent)
  660. {
  661. RECT rcWindow;
  662. SCROLLINFO si;
  663. LONG lStyle = GetWindowStyle(hwnd);
  664. SetWindowLong(hwnd, GWL_STYLE, lStyle | WS_HSCROLL);
  665. si.cbSize = sizeof(si);
  666. si.fMask = SIF_PAGE | SIF_RANGE;
  667. si.nMin = 0;
  668. si.nMax = pmxud->cxDlgContent - 1; // endpoint is inclusive
  669. si.nPage = rc.right;
  670. SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
  671. // Grow the dialog to accomodate the scrollbar
  672. GetWindowRect(hwnd, &rcWindow);
  673. SetWindowPos(hwnd, NULL, 0, 0, rcWindow.right - rcWindow.left,
  674. rcWindow.bottom - rcWindow.top + GetSystemMetrics(SM_CYHSCROLL),
  675. SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE |
  676. SWP_NOOWNERZORDER | SWP_NOZORDER);
  677. }
  678. //
  679. // If we are the tray master, don't ask to set focus
  680. //
  681. return (!(pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER));
  682. }
  683. /*
  684. * Volume_OnDestroy
  685. *
  686. * Shut down this dialog. DO NOT TOUCH the hmixer!
  687. *
  688. * */
  689. void Volume_OnDestroy(
  690. HWND hwnd)
  691. {
  692. PMIXUIDIALOG pmxud = GETMIXUIDIALOG(hwnd);
  693. DeviceChange_Cleanup();
  694. if (!pmxud)
  695. return;
  696. if (pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER)
  697. {
  698. HWND hVol;
  699. hVol = pmxud->amxul[0].acr[MIXUI_VOLUME].hwnd;
  700. SubclassWindow(hVol, pmxud->lpfnTrayVol);
  701. REMOVEPROP(hVol);
  702. }
  703. Volume_Cleanup(pmxud);
  704. if (!(pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER))
  705. {
  706. //
  707. // save window position
  708. //
  709. if (!IsIconic(hwnd))
  710. {
  711. RECT rc;
  712. GetWindowRect(hwnd, &rc);
  713. Volume_GetSetRegistryRect(pmxud->szMixer
  714. , pmxud->szDestination
  715. , &rc
  716. , SET);
  717. }
  718. }
  719. if (pmxud->dwReturn == MIXUI_RESTART)
  720. PostMessage(pmxud->hParent, MYWM_RESTART, 0, (LPARAM)pmxud);
  721. else
  722. PostMessage(pmxud->hParent, WM_CLOSE, 0, 0L);
  723. }
  724. /*
  725. * Volume_SetControl
  726. *
  727. * Update system controls from visual controls
  728. *
  729. * */
  730. void Volume_SetControl(
  731. PMIXUIDIALOG pmxud,
  732. HWND hctl,
  733. int imxul,
  734. int itype)
  735. {
  736. if (pmxud->dwFlags & MXUD_FLAGSF_MIXER)
  737. Mixer_SetControl(pmxud, hctl, imxul, itype);
  738. else
  739. Nonmixer_SetControl(pmxud, hctl, imxul, itype);
  740. }
  741. /*
  742. * Volume_GetControl
  743. *
  744. * Update visual controls from system controls
  745. * */
  746. void Volume_GetControl(
  747. PMIXUIDIALOG pmxud,
  748. HWND hctl,
  749. int imxul,
  750. int itype)
  751. {
  752. if (pmxud->dwFlags & MXUD_FLAGSF_MIXER)
  753. Mixer_GetControl(pmxud, hctl, imxul, itype);
  754. else
  755. Nonmixer_GetControl(pmxud, hctl, imxul, itype);
  756. }
  757. extern DWORD GetWaveOutID(BOOL *pfPreferred);
  758. /*
  759. * Volume_PlayDefaultSound
  760. *
  761. * Play the default sound on the current mixer
  762. *
  763. * */
  764. void Volume_PlayDefaultSound (PMIXUIDIALOG pmxud)
  765. {
  766. /*
  767. // TODO: Implement for all master volumes. Convert mixerid to wave id then
  768. // use wave API to play the file
  769. TCHAR szDefSnd[MAX_PATH];
  770. long lcb = sizeof (szDefSnd);
  771. // Get the default sound filename
  772. if (ERROR_SUCCESS != RegQueryValue (HKEY_CURRENT_USER, REGSTR_PATH_APPS_DEFAULT TEXT("\\.Default\\.Current"), szDefSnd, &lcb) ||
  773. 0 >= lstrlen (szDefSnd))
  774. return;
  775. */
  776. DWORD dwWave = GetWaveOutID (NULL);
  777. UINT uiMixID;
  778. // Check Parameter
  779. if (!pmxud)
  780. return;
  781. // Play the sound only if we are on the default mixer...
  782. if (MMSYSERR_NOERROR == mixerGetID (ULongToPtr(dwWave), &uiMixID, MIXER_OBJECTF_WAVEOUT) &&
  783. pmxud -> mxid == uiMixID)
  784. {
  785. static const TCHAR cszDefSnd[] = TEXT(".Default");
  786. PlaySound(cszDefSnd, NULL, SND_ASYNC | SND_ALIAS);
  787. }
  788. }
  789. /*
  790. * Volume_ScrollTo
  791. *
  792. * Move the scrollbar position.
  793. */
  794. void Volume_ScrollTo(
  795. PMIXUIDIALOG pmxud,
  796. int pos
  797. )
  798. {
  799. RECT rc;
  800. /*
  801. * Keep in range.
  802. */
  803. pos = max(pos, 0);
  804. pos = min(pos, pmxud->cxDlgContent - pmxud->cxDlgWidth);
  805. /*
  806. * Scroll the window contents accordingly. But don't scroll
  807. * the status bar.
  808. */
  809. GetClientRect(pmxud->hwnd, &rc);
  810. if (pmxud->hStatus)
  811. {
  812. RECT rcStatus;
  813. GetWindowRect(pmxud->hStatus, &rcStatus);
  814. MapWindowRect(NULL, pmxud->hwnd, &rcStatus);
  815. SubtractRect(&rc, &rc, &rcStatus);
  816. }
  817. rc.left = -pmxud->cxDlgContent;
  818. rc.right = pmxud->cxDlgContent;
  819. ScrollWindowEx(pmxud->hwnd, pmxud->xOffset - pos, 0,
  820. &rc, NULL, NULL, NULL,
  821. SW_ERASE | SW_INVALIDATE | SW_SCROLLCHILDREN);
  822. pmxud->xOffset = pos;
  823. /*
  824. * Move the scrollbar to match.
  825. */
  826. SetScrollPos(pmxud->hwnd, SB_HORZ, pos, TRUE);
  827. }
  828. /*
  829. * Volume_ScrollContent
  830. *
  831. * Process scroll bar messages for the dialog itself.
  832. */
  833. void Volume_ScrollContent(
  834. PMIXUIDIALOG pmxud,
  835. UINT code,
  836. int pos
  837. )
  838. {
  839. switch (code) {
  840. case SB_LINELEFT:
  841. Volume_ScrollTo(pmxud, pmxud->xOffset - pmxud->cxScroll);
  842. break;
  843. case SB_LINERIGHT:
  844. Volume_ScrollTo(pmxud, pmxud->xOffset + pmxud->cxScroll);
  845. break;
  846. case SB_PAGELEFT:
  847. Volume_ScrollTo(pmxud, pmxud->xOffset - pmxud->cxDlgWidth);
  848. break;
  849. case SB_PAGERIGHT:
  850. Volume_ScrollTo(pmxud, pmxud->xOffset + pmxud->cxDlgWidth);
  851. break;
  852. case SB_LEFT:
  853. Volume_ScrollTo(pmxud, 0);
  854. break;
  855. case SB_RIGHT:
  856. Volume_ScrollTo(pmxud, MAXLONG);
  857. break;
  858. case SB_THUMBPOSITION:
  859. case SB_THUMBTRACK:
  860. Volume_ScrollTo(pmxud, pos);
  861. break;
  862. }
  863. }
  864. /*
  865. * Volume_OnXScroll
  866. *
  867. * Process Scroll bar messages
  868. *
  869. * */
  870. void Volume_OnXScroll(
  871. HWND hwnd,
  872. HWND hwndCtl,
  873. UINT code,
  874. int pos)
  875. {
  876. PMIXUIDIALOG pmxud = GETMIXUIDIALOG(hwnd);
  877. UINT id;
  878. int ictl;
  879. int iline;
  880. // If this is a scroll message from the dialog itself, then we need
  881. // to scroll our content.
  882. if (hwndCtl == NULL)
  883. {
  884. Volume_ScrollContent(pmxud, code, pos);
  885. return;
  886. }
  887. id = GetDlgCtrlID(hwndCtl);
  888. iline = id/IDOFFSET - 1;
  889. ictl = ((id % IDOFFSET) + IDC_MIXERCONTROLS == IDC_BALANCE)
  890. ? MIXUI_BALANCE : MIXUI_VOLUME;
  891. Volume_SetControl(pmxud, hwndCtl, iline, ictl);
  892. //
  893. // Make sure a note gets played
  894. //
  895. if (pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER)
  896. pmxud->dwTrayInfo |= MXUD_TRAYINFOF_SIGNAL;
  897. // Play a sound on for the master volume or balance slider when the
  898. // user ends the scroll and we are still in focus and the topmost app.
  899. if (code == SB_ENDSCROLL && pmxud && !(pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER) &&
  900. pmxud->amxul[iline].pvcd &&
  901. (MXUL_STYLEF_DESTINATION & pmxud->amxul[iline].dwStyle)
  902. && hwndCtl == GetFocus() && hwnd == GetForegroundWindow ())
  903. {
  904. Volume_PlayDefaultSound (pmxud);
  905. }
  906. }
  907. /*
  908. * Volume_OnMyTimer
  909. *
  910. * Frequent update timer for meters
  911. * */
  912. void Volume_OnMyTimer(
  913. HWND hwnd)
  914. {
  915. PMIXUIDIALOG pmxud = GETMIXUIDIALOG(hwnd);
  916. if (!pmxud)
  917. return;
  918. if (pmxud->cTimeInQueue > 0)
  919. pmxud->cTimeInQueue--;
  920. if (!(pmxud->dwFlags & MXUD_FLAGSF_USETIMER))
  921. return;
  922. if (pmxud->dwFlags & MXUD_FLAGSF_MIXER)
  923. Mixer_PollingUpdate(pmxud);
  924. else
  925. Nonmixer_PollingUpdate(pmxud);
  926. }
  927. /*
  928. * Volume_OnTimer
  929. *
  930. * Infrequent update timer for tray shutdown
  931. * */
  932. void Volume_OnTimer(
  933. HWND hwnd,
  934. UINT id)
  935. {
  936. PMIXUIDIALOG pmxud = GETMIXUIDIALOG(hwnd);
  937. KillTimer(hwnd, VOLUME_TRAYSHUTDOWN_ID);
  938. Volume_EndDialog(pmxud, MIXUI_EXIT, 0);
  939. }
  940. /*
  941. * Volume_OnMixmControlChange
  942. *
  943. * Handle control changes
  944. *
  945. * */
  946. void Volume_OnMixmControlChange(
  947. HWND hwnd,
  948. HMIXER hmx,
  949. DWORD dwControlID)
  950. {
  951. PMIXUIDIALOG pmxud = GETMIXUIDIALOG(hwnd);
  952. Mixer_GetControlFromID(pmxud, dwControlID);
  953. }
  954. /*
  955. * Volume_EnableLine
  956. *
  957. * Enable/Disable a line
  958. *
  959. * */
  960. void Volume_EnableLine(
  961. PMIXUIDIALOG pmxud,
  962. DWORD iLine,
  963. BOOL fEnable)
  964. {
  965. DWORD iCtrl;
  966. PMIXUICTRL pmxc;
  967. for (iCtrl = MIXUI_FIRST; iCtrl <= MIXUI_LAST; iCtrl++ )
  968. {
  969. pmxc = &pmxud->amxul[iLine].acr[iCtrl];
  970. if (pmxc->state == MIXUI_CONTROL_INITIALIZED)
  971. EnableWindow(pmxc->hwnd, fEnable);
  972. }
  973. pmxud->amxul[iLine].dwStyle ^= MXUL_STYLEF_DISABLED;
  974. }
  975. /*
  976. * Volume_InitLine
  977. *
  978. * Initialize the UI controls for the dialog
  979. *
  980. * */
  981. void Volume_InitLine(
  982. PMIXUIDIALOG pmxud,
  983. DWORD iLine)
  984. {
  985. HWND ctrl;
  986. PMIXUICTRL pmxc;
  987. //
  988. // Peakmeter control
  989. //
  990. pmxc = &pmxud->amxul[iLine].acr[MIXUI_VUMETER];
  991. ctrl = Volume_GetLineItem(pmxud->hwnd, iLine, IDC_VUMETER);
  992. pmxc->hwnd = ctrl;
  993. if (! (pmxc->state == MIXUI_CONTROL_ENABLED) )
  994. {
  995. if (ctrl)
  996. ShowWindow(ctrl, SW_HIDE);
  997. }
  998. else if (ctrl)
  999. {
  1000. HWND hvol;
  1001. SendMessage(ctrl, VU_SETRANGEMAX, 0, VOLUME_TICS);
  1002. SendMessage(ctrl, VU_SETRANGEMIN, 0, 0);
  1003. hvol = Volume_GetLineItem(pmxud->hwnd, iLine, IDC_VOLUME);
  1004. if (hvol)
  1005. {
  1006. RECT rc;
  1007. POINT pos;
  1008. GetWindowRect(hvol, &rc);
  1009. MapWindowPoints(NULL, pmxud->hwnd, (LPPOINT)&rc, 2);
  1010. pos.x = rc.left;
  1011. pos.y = rc.top;
  1012. MoveWindow(hvol
  1013. , pos.x - 15
  1014. , pos.y
  1015. , rc.right - rc.left
  1016. , rc.bottom - rc.top
  1017. , FALSE);
  1018. }
  1019. //
  1020. // Signal use of update timer
  1021. //
  1022. pmxud->dwFlags |= MXUD_FLAGSF_USETIMER;
  1023. pmxc->state = MIXUI_CONTROL_INITIALIZED;
  1024. }
  1025. else
  1026. pmxc->state = MIXUI_CONTROL_UNINITIALIZED;
  1027. //
  1028. // Balance control
  1029. //
  1030. pmxc = &pmxud->amxul[iLine].acr[MIXUI_BALANCE];
  1031. ctrl = Volume_GetLineItem(pmxud->hwnd, iLine, IDC_BALANCE);
  1032. pmxc->hwnd = ctrl;
  1033. if (ctrl)
  1034. {
  1035. SendMessage(ctrl, TBM_SETRANGE, 0, MAKELONG(0, 64));
  1036. SendMessage(ctrl, TBM_SETTICFREQ, 32, 0 );
  1037. SendMessage(ctrl, TBM_SETPOS, TRUE, 32);
  1038. if (pmxc->state != MIXUI_CONTROL_ENABLED)
  1039. {
  1040. EnableWindow(ctrl, FALSE);
  1041. }
  1042. else
  1043. pmxc->state = MIXUI_CONTROL_INITIALIZED;
  1044. }
  1045. else
  1046. pmxc->state = MIXUI_CONTROL_UNINITIALIZED;
  1047. //
  1048. // Volume control
  1049. //
  1050. pmxc = &pmxud->amxul[iLine].acr[MIXUI_VOLUME];
  1051. ctrl = Volume_GetLineItem(pmxud->hwnd, iLine, IDC_VOLUME);
  1052. pmxc->hwnd = ctrl;
  1053. if (ctrl)
  1054. {
  1055. SendMessage(ctrl, TBM_SETRANGE, 0, MAKELONG(0, VOLUME_TICS));
  1056. SendMessage(ctrl, TBM_SETTICFREQ, (VOLUME_TICS + 5)/6, 0 );
  1057. if (pmxc->state != MIXUI_CONTROL_ENABLED)
  1058. {
  1059. SendMessage(ctrl, TBM_SETPOS, TRUE, 128);
  1060. EnableWindow(ctrl, FALSE);
  1061. }
  1062. else
  1063. pmxc->state = MIXUI_CONTROL_INITIALIZED;
  1064. }
  1065. else
  1066. pmxc->state = MIXUI_CONTROL_UNINITIALIZED;
  1067. //
  1068. // Switch
  1069. //
  1070. pmxc = &pmxud->amxul[iLine].acr[MIXUI_SWITCH];
  1071. ctrl = Volume_GetLineItem(pmxud->hwnd, iLine, IDC_SWITCH);
  1072. pmxc->hwnd = ctrl;
  1073. if (ctrl)
  1074. {
  1075. if (pmxc->state != MIXUI_CONTROL_ENABLED)
  1076. EnableWindow(ctrl, FALSE);
  1077. else
  1078. pmxc->state = MIXUI_CONTROL_INITIALIZED;
  1079. }
  1080. else
  1081. pmxc->state = MIXUI_CONTROL_UNINITIALIZED;
  1082. }
  1083. /*
  1084. * Volume_OnMixmLineChange
  1085. *
  1086. * */
  1087. void Volume_OnMixmLineChange(
  1088. HWND hwnd,
  1089. HMIXER hmx,
  1090. DWORD dwLineID)
  1091. {
  1092. PMIXUIDIALOG pmxud = GETMIXUIDIALOG(hwnd);
  1093. DWORD iLine;
  1094. for (iLine = 0; iLine < pmxud->cmxul; iLine++)
  1095. {
  1096. if ( dwLineID == pmxud->amxul[iLine].pvcd->dwLineID )
  1097. {
  1098. MIXERLINE ml;
  1099. MMRESULT mmr;
  1100. BOOL fEnable;
  1101. ml.cbStruct = sizeof(ml);
  1102. ml.dwLineID = dwLineID;
  1103. mmr = mixerGetLineInfo((HMIXEROBJ)hmx, &ml, MIXER_GETLINEINFOF_LINEID);
  1104. if (mmr != MMSYSERR_NOERROR)
  1105. {
  1106. fEnable = !(ml.fdwLine & MIXERLINE_LINEF_DISCONNECTED);
  1107. Volume_EnableLine(pmxud, iLine, fEnable);
  1108. }
  1109. }
  1110. }
  1111. }
  1112. /*
  1113. * Volume_OnActivate
  1114. *
  1115. * Important for tray volume only. Dismisses the dialog and starts an
  1116. * expiration timer.
  1117. *
  1118. * */
  1119. void Volume_OnActivate(
  1120. HWND hwnd,
  1121. UINT state,
  1122. HWND hwndActDeact,
  1123. BOOL fMinimized)
  1124. {
  1125. PMIXUIDIALOG pmxud = GETMIXUIDIALOG(hwnd);
  1126. if (!(pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER))
  1127. {
  1128. return;
  1129. }
  1130. if (state != WA_INACTIVE)
  1131. {
  1132. fCanDismissWindow = TRUE;
  1133. }
  1134. else if (fCanDismissWindow)
  1135. {
  1136. PostMessage(hwnd, WM_CLOSE, 0, 0L);
  1137. /*
  1138. DWORD dwTimeout = 5 * 60 * 1000;
  1139. fCanDismissWindow = FALSE;
  1140. ShowWindow(hwnd, SW_HIDE);
  1141. //
  1142. // Set expiration timer. If no one adjusts the volume, make the
  1143. // application go away after 5 minutes.
  1144. //
  1145. dwTimeout = Volume_GetTrayTimeout(dwTimeout);
  1146. SetTimer(hwnd, VOLUME_TRAYSHUTDOWN_ID, dwTimeout, NULL);
  1147. */
  1148. }
  1149. }
  1150. /*
  1151. * Volume_PropogateMessage
  1152. *
  1153. * WM_SYSCOLORCHANGE needs to be send to all child windows (esp. trackbars)
  1154. */
  1155. void Volume_PropagateMessage(
  1156. HWND hwnd,
  1157. UINT uMessage,
  1158. WPARAM wParam,
  1159. LPARAM lParam)
  1160. {
  1161. HWND hwndChild;
  1162. for (hwndChild = GetWindow(hwnd, GW_CHILD); hwndChild != NULL;
  1163. hwndChild = GetWindow(hwndChild, GW_HWNDNEXT))
  1164. {
  1165. SendMessage(hwndChild, uMessage, wParam, lParam);
  1166. }
  1167. }
  1168. /*
  1169. * Volume_OnPaint
  1170. *
  1171. * Handle custom painting
  1172. * */
  1173. void Volume_OnPaint(HWND hwnd)
  1174. {
  1175. PMIXUIDIALOG pmxud = GETMIXUIDIALOG(hwnd);
  1176. RECT rc;
  1177. POINT pos;
  1178. PAINTSTRUCT ps;
  1179. HDC hdc;
  1180. hdc = BeginPaint(hwnd, &ps);
  1181. //
  1182. // for all styles other than the tray master, draw an etched
  1183. // line to delinate the menu area
  1184. //
  1185. if (!(pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER))
  1186. {
  1187. GetClientRect(hwnd, &rc);
  1188. rc.bottom = 0;
  1189. DrawEdge(hdc, &rc, EDGE_ETCHED, BF_TOP);
  1190. EndPaint(hwnd, &ps);
  1191. return;
  1192. }
  1193. //
  1194. // for the tray master, draw some significant icon to indicate
  1195. // volume
  1196. //
  1197. GetWindowRect(GetDlgItem(hwnd, IDC_VOLUMECUE), &rc);
  1198. MapWindowPoints(NULL, hwnd, (LPPOINT)&rc, 2);
  1199. DrawEdge(hdc, &rc, BDR_RAISEDINNER, BF_DIAGONAL|BF_TOP|BF_LEFT);
  1200. DrawEdge(hdc, &rc, BDR_RAISEDINNER, BF_TOP);
  1201. rc.bottom -= 8;
  1202. DrawEdge(hdc, &rc, BDR_RAISEDINNER, BF_RIGHT);
  1203. EndPaint(hwnd, &ps);
  1204. }
  1205. /*
  1206. * Volume_OnClose
  1207. *
  1208. * */
  1209. void Volume_OnClose(
  1210. HWND hwnd)
  1211. {
  1212. DestroyWindow(hwnd);
  1213. }
  1214. /*
  1215. * Volume_OnEndSession
  1216. *
  1217. * */
  1218. void Volume_OnEndSession(
  1219. HWND hwnd,
  1220. BOOL fEnding)
  1221. {
  1222. if (!fEnding)
  1223. return;
  1224. //
  1225. // Be sure to call the close code to free open handles
  1226. //
  1227. Volume_OnClose(hwnd);
  1228. }
  1229. #define V_DC_STATEF_PENDING 0x00000001
  1230. #define V_DC_STATEF_REMOVING 0x00000002
  1231. #define V_DC_STATEF_ARRIVING 0x00000004
  1232. /*
  1233. * Volume_OnDeviceChange
  1234. *
  1235. * */
  1236. void Volume_OnDeviceChange(
  1237. HWND hwnd,
  1238. WPARAM wParam,
  1239. LPARAM lParam)
  1240. {
  1241. PMIXUIDIALOG pmxud = GETMIXUIDIALOG(hwnd);
  1242. MMRESULT mmr;
  1243. UINT uMxID;
  1244. PDEV_BROADCAST_DEVICEINTERFACE bdi = (PDEV_BROADCAST_DEVICEINTERFACE)lParam;
  1245. PDEV_BROADCAST_HANDLE bh = (PDEV_BROADCAST_HANDLE)lParam;
  1246. //
  1247. // Determine if this is our event.
  1248. //
  1249. if(!DeviceEventContext)
  1250. return;
  1251. //If we have an handle on the device then we get a DEV_BROADCAST_HDR structure as the lParam.
  1252. //Or else it means that we have registered for the general audio category KSCATEGORY_AUDIO.
  1253. if(bUseHandle)
  1254. {
  1255. if(!bh ||
  1256. bh->dbch_devicetype != DBT_DEVTYP_HANDLE)
  1257. {
  1258. return;
  1259. }
  1260. }
  1261. else if (!bdi ||
  1262. bdi->dbcc_devicetype != DBT_DEVTYP_DEVICEINTERFACE ||
  1263. !IsEqualGUID(&KSCATEGORY_AUDIO, &bdi->dbcc_classguid) ||
  1264. !(*bdi->dbcc_name)
  1265. )
  1266. {
  1267. return;
  1268. }
  1269. switch (wParam)
  1270. {
  1271. case DBT_DEVICEQUERYREMOVE:
  1272. //The mixer has to be shutdown now.
  1273. //Posting a WM_CLOSE message as Volume_EndDialog does will not help.
  1274. if (pmxud->dwFlags & MXUD_FLAGSF_MIXER)
  1275. Mixer_Shutdown(pmxud);
  1276. else
  1277. Nonmixer_Shutdown(pmxud);
  1278. // Don't attempt restart, just exit. The wavemapper is not
  1279. // updated with the new default device, so do not know what
  1280. // to restart as and we should NOT hardcode device #0!
  1281. // pmxud->mxid = (DWORD) 0;
  1282. // GetDestination(pmxud->mxid, &pmxud->iDest);
  1283. Volume_EndDialog(pmxud, MIXUI_EXIT, 0);
  1284. return;
  1285. case DBT_DEVICEQUERYREMOVEFAILED: // The query failed, the device will not be removed, so lets reopen it.
  1286. mmr = Volume_GetDefaultMixerID(&uMxID, gfRecord);
  1287. pmxud->mxid = (mmr == MMSYSERR_NOERROR)?uMxID:0;
  1288. GetDestination(pmxud->mxid, &pmxud->iDest);
  1289. Volume_EndDialog(pmxud, MIXUI_RESTART, 0);
  1290. return;
  1291. case DBT_DEVNODES_CHANGED:
  1292. //
  1293. // We cannot reliably determine the final state of the devices in
  1294. // the system until this message is broadcast.
  1295. //
  1296. if (pmxud->dwDeviceState & V_DC_STATEF_PENDING)
  1297. {
  1298. pmxud->dwDeviceState ^= V_DC_STATEF_PENDING;
  1299. break;
  1300. }
  1301. return;
  1302. case DBT_DEVICEREMOVECOMPLETE:
  1303. //The mixer has to be shutdown now.
  1304. //Posting a WM_CLOSE message as Volume_EndDialog does will not help.
  1305. if (pmxud->dwFlags & MXUD_FLAGSF_MIXER)
  1306. Mixer_Shutdown(pmxud);
  1307. else
  1308. Nonmixer_Shutdown(pmxud);
  1309. //A DBT_DEVICEQUERYREMOVE is not guaranteed before a DBT_DEVICEREMOVECOMPLETE.
  1310. //There should be a check here to see if this message is meant for this device.
  1311. //We do not know a way of doing that right now.
  1312. // Don't attempt restart, just exit. The wavemapper is not
  1313. // updated with the new default device, so do not know what
  1314. // to restart as and we should NOT hardcode device #0!
  1315. // pmxud->mxid = (DWORD) 0;
  1316. // GetDestination(pmxud->mxid, &pmxud->iDest);
  1317. Volume_EndDialog(pmxud, MIXUI_EXIT, 0);
  1318. pmxud->dwDeviceState = V_DC_STATEF_PENDING
  1319. | V_DC_STATEF_REMOVING;
  1320. return;
  1321. case DBT_DEVICEARRIVAL:
  1322. //
  1323. // A devnode is being added to the system
  1324. //
  1325. pmxud->dwDeviceState = V_DC_STATEF_PENDING
  1326. | V_DC_STATEF_ARRIVING;
  1327. return;
  1328. default:
  1329. return;
  1330. }
  1331. mmr = Volume_GetDefaultMixerID(&uMxID, gfRecord);
  1332. if (pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER)
  1333. {
  1334. if ( mmr == MMSYSERR_NOERROR
  1335. && (pmxud->dwDeviceState & V_DC_STATEF_ARRIVING))
  1336. {
  1337. DWORD dwDevNode;
  1338. if (!mixerMessage((HMIXER)UIntToPtr(uMxID), DRV_QUERYDEVNODE
  1339. , (DWORD_PTR)&dwDevNode, 0L))
  1340. {
  1341. if (dwDevNode == pmxud->dwDevNode)
  1342. {
  1343. //
  1344. // ignore this device, it doesn't affect us
  1345. //
  1346. pmxud->dwDeviceState = 0L;
  1347. return;
  1348. }
  1349. }
  1350. }
  1351. //
  1352. // Our device state has changed. Just go away.
  1353. //
  1354. Volume_EndDialog(pmxud, MIXUI_EXIT, 0);
  1355. }
  1356. else if (pmxud->dwDeviceState & V_DC_STATEF_REMOVING)
  1357. {
  1358. //
  1359. // Restart with the default mixer if we can.
  1360. //
  1361. pmxud->mxid = (mmr == MMSYSERR_NOERROR)?uMxID:0;
  1362. GetDestination(pmxud->mxid, &pmxud->iDest);
  1363. Volume_EndDialog(pmxud, MIXUI_RESTART, 0);
  1364. }
  1365. pmxud->dwDeviceState = 0L;
  1366. }
  1367. void Volume_OnWakeup(
  1368. HWND hwnd,
  1369. WPARAM wParam)
  1370. {
  1371. POINT pos;
  1372. RECT rc, rcPopup;
  1373. LONG w,h;
  1374. LONG scrw, scrh;
  1375. HWND hTrack;
  1376. HMONITOR hMonitor;
  1377. MONITORINFO moninfo;
  1378. PMIXUIDIALOG pmxud = GETMIXUIDIALOG(hwnd);
  1379. if (!(pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER))
  1380. return;
  1381. KillTimer(hwnd, VOLUME_TRAYSHUTDOWN_ID);
  1382. if (wParam != 0)
  1383. {
  1384. Volume_EndDialog(pmxud, MIXUI_EXIT, 0);
  1385. return;
  1386. }
  1387. //
  1388. // Make the tray volume come up.
  1389. //
  1390. //Get the current position.
  1391. GetCursorPos(&pos);
  1392. //Get the width and height of the popup.
  1393. GetWindowRect(hwnd, &rc);
  1394. w = rc.right - rc.left; //This value will always be positive as left is always lesser than right.
  1395. h = rc.bottom - rc.top; //This value will always be positive as top is always lesser than bottom.
  1396. //Initialize the rectangle for the popup. Position it so that the popup appears to the right,
  1397. //bottom of the cursor.
  1398. rcPopup.left = pos.x;
  1399. rcPopup.right = pos.x + w;
  1400. rcPopup.top = pos.y;
  1401. rcPopup.bottom = pos.y+h;
  1402. //Get the rectangle for the monitor.
  1403. hMonitor = MonitorFromPoint(pos, MONITOR_DEFAULTTONEAREST);
  1404. moninfo.cbSize = sizeof(moninfo);
  1405. GetMonitorInfo(hMonitor,&moninfo);
  1406. //If the popup rectangle is leaking off from the right of the screen. Make it appear on the
  1407. //left of the cursor.
  1408. if(rcPopup.right > moninfo.rcWork.right)
  1409. {
  1410. OffsetRect(&rcPopup, -w, 0);
  1411. }
  1412. //If the popup rectangle is leaking off from the bottom of the screen. Make it appear on top
  1413. //of the cursor.
  1414. if(rcPopup.bottom > moninfo.rcWork.bottom)
  1415. {
  1416. OffsetRect(&rcPopup, 0, -h);
  1417. }
  1418. SetWindowPos(hwnd
  1419. , HWND_TOPMOST
  1420. , rcPopup.left
  1421. , rcPopup.top
  1422. , w
  1423. , h
  1424. , SWP_SHOWWINDOW);
  1425. // make us come to the front
  1426. SetForegroundWindow(hwnd);
  1427. fCanDismissWindow = TRUE;
  1428. hTrack = GetDlgItem(hwnd, IDC_VOLUME);
  1429. if (hTrack)
  1430. SetFocus(hTrack);
  1431. }
  1432. /*
  1433. * VolumeProc
  1434. *
  1435. * */
  1436. INT_PTR CALLBACK VolumeProc(
  1437. HWND hdlg,
  1438. UINT msg,
  1439. WPARAM wparam,
  1440. LPARAM lparam)
  1441. {
  1442. switch (msg)
  1443. {
  1444. case WM_INITDIALOG:
  1445. return HANDLE_WM_INITDIALOG(hdlg, wparam, lparam, Volume_OnInitDialog);
  1446. case WM_COMMAND:
  1447. HANDLE_WM_COMMAND(hdlg, wparam, lparam, Volume_OnCommand);
  1448. break;
  1449. case WM_CLOSE:
  1450. HANDLE_WM_CLOSE(hdlg, wparam, lparam, Volume_OnClose);
  1451. break;
  1452. case WM_DESTROY:
  1453. HANDLE_WM_DESTROY(hdlg, wparam, lparam, Volume_OnDestroy);
  1454. break;
  1455. case WM_HSCROLL:
  1456. case WM_VSCROLL:
  1457. //
  1458. // balance and volume are essentially the same
  1459. //
  1460. HANDLE_WM_XSCROLL(hdlg, wparam, lparam, Volume_OnXScroll);
  1461. break;
  1462. case WM_MENUSELECT:
  1463. //Keep track of which menu bar item is currently popped up.
  1464. //This will be used for displaying the appropriate help from the mplayer.hlp file
  1465. //when the user presses the F1 key.
  1466. currMenuItem = (UINT)LOWORD(wparam);
  1467. break;
  1468. case MM_MIXM_LINE_CHANGE:
  1469. HANDLE_MM_MIXM_LINE_CHANGE(hdlg
  1470. , wparam
  1471. , lparam
  1472. , Volume_OnMixmLineChange);
  1473. return FALSE;
  1474. case MM_MIXM_CONTROL_CHANGE:
  1475. HANDLE_MM_MIXM_CONTROL_CHANGE(hdlg
  1476. , wparam
  1477. , lparam
  1478. , Volume_OnMixmControlChange);
  1479. return FALSE;
  1480. case WM_ACTIVATE:
  1481. HANDLE_WM_ACTIVATE(hdlg, wparam, lparam, Volume_OnActivate);
  1482. break;
  1483. case MYWM_TIMER:
  1484. HANDLE_MYWM_TIMER(hdlg, wparam, lparam, Volume_OnMyTimer);
  1485. break;
  1486. case WM_TIMER:
  1487. HANDLE_WM_TIMER(hdlg, wparam, lparam, Volume_OnTimer);
  1488. break;
  1489. case WM_PAINT:
  1490. HANDLE_WM_PAINT(hdlg, wparam, lparam, Volume_OnPaint);
  1491. break;
  1492. case WM_SYSCOLORCHANGE:
  1493. Volume_PropagateMessage(hdlg, msg, wparam, lparam);
  1494. break;
  1495. case WM_DEVICECHANGE:
  1496. HANDLE_WM_IDEVICECHANGE(hdlg, wparam, lparam, Volume_OnDeviceChange);
  1497. break;
  1498. case MYWM_WAKEUP:
  1499. HANDLE_MYWM_WAKEUP(hdlg, wparam, lparam, Volume_OnWakeup);
  1500. break;
  1501. case WM_ENDSESSION:
  1502. HANDLE_WM_ENDSESSION(hdlg, wparam, lparam, Volume_OnEndSession);
  1503. break;
  1504. default:
  1505. break;
  1506. }
  1507. return FALSE;
  1508. }
  1509. /*
  1510. * Volume_AddLine
  1511. *
  1512. * */
  1513. BOOL Volume_AddLine(
  1514. PMIXUIDIALOG pmxud,
  1515. LPBYTE lpAdd,
  1516. DWORD cbAdd,
  1517. DWORD dwStyle,
  1518. PVOLCTRLDESC pvcd)
  1519. {
  1520. LPBYTE pbNew;
  1521. DWORD cbNew;
  1522. PMIXUILINE pmxul;
  1523. if (pmxud->amxul)
  1524. {
  1525. pmxul = (PMIXUILINE)GlobalReAllocPtr(pmxud->amxul
  1526. , (pmxud->cmxul+1)*sizeof(MIXUILINE)
  1527. , GHND);
  1528. }
  1529. else
  1530. {
  1531. pmxul = (PMIXUILINE)GlobalAllocPtr(GHND, sizeof(MIXUILINE));
  1532. }
  1533. if (!pmxul)
  1534. return FALSE;
  1535. pbNew = Dlg_HorizAttach(pmxud->lpDialog
  1536. , pmxud->cbDialog
  1537. , lpAdd
  1538. , cbAdd
  1539. , (WORD)(IDOFFSET * pmxud->cmxul)
  1540. , &cbNew );
  1541. if (!pbNew)
  1542. {
  1543. if (!pmxud->amxul)
  1544. GlobalFreePtr(pmxul);
  1545. return FALSE;
  1546. }
  1547. pmxul[pmxud->cmxul].dwStyle = dwStyle;
  1548. pmxul[pmxud->cmxul].pvcd = pvcd;
  1549. pmxud->amxul = pmxul;
  1550. pmxud->lpDialog = pbNew;
  1551. pmxud->cbDialog = cbNew;
  1552. pmxud->cmxul ++;
  1553. return TRUE;
  1554. }
  1555. /*
  1556. * Volume_Cleanup
  1557. *
  1558. * */
  1559. void Volume_Cleanup(
  1560. PMIXUIDIALOG pmxud)
  1561. {
  1562. if (pmxud->dwFlags & MXUD_FLAGSF_USETIMER)
  1563. {
  1564. timeKillEvent(pmxud->uTimerID);
  1565. pmxud->dwFlags ^= MXUD_FLAGSF_USETIMER;
  1566. }
  1567. if (pmxud->dwFlags & MXUD_FLAGSF_BADDRIVER)
  1568. {
  1569. pmxud->dwFlags ^= MXUD_FLAGSF_BADDRIVER;
  1570. }
  1571. if (pmxud->dwFlags & MXUD_FLAGSF_NOADVANCED)
  1572. {
  1573. pmxud->dwFlags ^= MXUD_FLAGSF_NOADVANCED;
  1574. }
  1575. if (pmxud->dwFlags & MXUD_FLAGSF_MIXER)
  1576. Mixer_Shutdown(pmxud);
  1577. else
  1578. Nonmixer_Shutdown(pmxud);
  1579. if (pmxud->lpDialog)
  1580. GlobalFreePtr(pmxud->lpDialog);
  1581. if (pmxud->amxul)
  1582. GlobalFreePtr(pmxud->amxul);
  1583. if (pmxud->avcd)
  1584. GlobalFreePtr(pmxud->avcd);
  1585. pmxud->amxul = NULL;
  1586. pmxud->lpDialog = NULL;
  1587. pmxud->cbDialog = 0;
  1588. pmxud->cmxul = 0;
  1589. pmxud->hwnd = NULL;
  1590. pmxud->hStatus = NULL;
  1591. pmxud->uTimerID = 0;
  1592. pmxud->dwDevNode = 0L;
  1593. FreeAppIcon ();
  1594. }
  1595. /*
  1596. * Volume_CreateVolume
  1597. * */
  1598. BOOL Volume_CreateVolume(
  1599. PMIXUIDIALOG pmxud)
  1600. {
  1601. WNDCLASS wc;
  1602. LPBYTE lpDst = NULL, lpSrc = NULL, lpMaster = NULL;
  1603. DWORD cbDst, cbSrc, cbMaster;
  1604. PVOLCTRLDESC avcd;
  1605. DWORD cvcd;
  1606. DWORD ivcd;
  1607. DWORD imxul;
  1608. DWORD dwSupport = 0L;
  1609. BOOL fAddLine = TRUE;
  1610. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  1611. wc.hIcon = GetAppIcon (pmxud->hInstance, pmxud->mxid);
  1612. wc.lpszMenuName = NULL;
  1613. wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
  1614. wc.hInstance = pmxud->hInstance;
  1615. wc.style = CS_HREDRAW | CS_VREDRAW;
  1616. wc.lpfnWndProc = DefDlgProc;
  1617. wc.cbClsExtra = 0;
  1618. wc.cbWndExtra = DLGWINDOWEXTRA;
  1619. wc.lpszClassName = (pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER)
  1620. ? gszTrayClassName : gszAppClassName;
  1621. RegisterClass(&wc);
  1622. if (pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER)
  1623. {
  1624. lpMaster = (LPBYTE)Dlg_LoadResource(pmxud->hInstance
  1625. , MAKEINTRESOURCE(IDD_TRAYMASTER)
  1626. , &cbMaster);
  1627. if (!lpMaster)
  1628. return FALSE;
  1629. }
  1630. else
  1631. {
  1632. if (pmxud->dwStyle & MXUD_STYLEF_SMALL)
  1633. {
  1634. lpDst = (LPBYTE)Dlg_LoadResource(pmxud->hInstance
  1635. , MAKEINTRESOURCE(IDD_SMDST)
  1636. , &cbDst);
  1637. lpSrc = (LPBYTE)Dlg_LoadResource(pmxud->hInstance
  1638. , MAKEINTRESOURCE(IDD_SMSRC)
  1639. , &cbSrc);
  1640. }
  1641. else
  1642. {
  1643. lpDst = (LPBYTE)Dlg_LoadResource(pmxud->hInstance
  1644. , MAKEINTRESOURCE(IDD_DESTINATION)
  1645. , &cbDst);
  1646. lpSrc = (LPBYTE)Dlg_LoadResource(pmxud->hInstance
  1647. , MAKEINTRESOURCE(IDD_SOURCE)
  1648. , &cbSrc);
  1649. }
  1650. if (!lpDst || !lpSrc)
  1651. return FALSE;
  1652. }
  1653. pmxud->lpDialog = NULL;
  1654. pmxud->cbDialog = 0;
  1655. pmxud->amxul = NULL;
  1656. pmxud->cmxul = 0;
  1657. pmxud->avcd = NULL;
  1658. pmxud->cvcd = 0;
  1659. //
  1660. // Create the volume description
  1661. //
  1662. if (pmxud->dwFlags & MXUD_FLAGSF_MIXER)
  1663. {
  1664. HMIXER hmx;
  1665. MMRESULT mmr;
  1666. //
  1667. // Mixer API's work much more efficiently with a mixer handle...
  1668. //
  1669. mmr = mixerOpen(&hmx, pmxud->mxid, 0L, 0L, MIXER_OBJECTF_MIXER);
  1670. if(MMSYSERR_NOERROR == mmr)
  1671. {
  1672. avcd = Mixer_CreateVolumeDescription((HMIXEROBJ)hmx
  1673. , pmxud->iDest
  1674. , &cvcd);
  1675. mixerClose(hmx);
  1676. }
  1677. else
  1678. {
  1679. avcd = Mixer_CreateVolumeDescription((HMIXEROBJ)ULongToPtr(pmxud->mxid)
  1680. , pmxud->iDest
  1681. , &cvcd);
  1682. }
  1683. if (!Mixer_GetDeviceName(pmxud))
  1684. {
  1685. GlobalFreePtr(avcd);
  1686. avcd = NULL;
  1687. }
  1688. }
  1689. else
  1690. {
  1691. avcd = Nonmixer_CreateVolumeDescription(pmxud->iDest
  1692. , &cvcd);
  1693. if (!Nonmixer_GetDeviceName(pmxud))
  1694. {
  1695. GlobalFreePtr(avcd);
  1696. avcd = NULL;
  1697. }
  1698. }
  1699. //
  1700. // Create the dialog box to go along with it
  1701. //
  1702. if (avcd)
  1703. {
  1704. pmxud->avcd = avcd;
  1705. pmxud->cvcd = cvcd;
  1706. if (pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER)
  1707. {
  1708. if (!Volume_AddLine(pmxud
  1709. , lpMaster
  1710. , cbMaster
  1711. , MXUL_STYLEF_DESTINATION
  1712. , &avcd[0]))
  1713. {
  1714. return FALSE;
  1715. }
  1716. }
  1717. else
  1718. {
  1719. BOOL fFirstRun;
  1720. //
  1721. // Restore HIDDEN flags.
  1722. //
  1723. // On first run, be sure to re-save state so there's something
  1724. // there.
  1725. //
  1726. fFirstRun = !Volume_GetSetRegistryLineStates(pmxud->szMixer
  1727. , pmxud->avcd[0].szShortName
  1728. , avcd
  1729. , cvcd
  1730. , GET);
  1731. for (ivcd = 0; ivcd < cvcd; ivcd++)
  1732. {
  1733. //
  1734. // Lines are marked hidden if a state has been saved in the
  1735. // registry or no state has been saved and there are too many
  1736. // unnecessary lines.
  1737. //
  1738. if (avcd[ivcd].dwSupport & VCD_SUPPORTF_HIDDEN)
  1739. {
  1740. continue;
  1741. }
  1742. //
  1743. // Lines are marked VISIBLE if they have sufficient controls
  1744. // to be useful.
  1745. //
  1746. if (!(avcd[ivcd].dwSupport & VCD_SUPPORTF_VISIBLE))
  1747. {
  1748. continue;
  1749. }
  1750. //
  1751. // Show only defaults on first run.
  1752. //
  1753. if (fFirstRun && !(avcd[ivcd].dwSupport & VCD_SUPPORTF_DEFAULT))
  1754. {
  1755. avcd[ivcd].dwSupport |= VCD_SUPPORTF_HIDDEN;
  1756. continue;
  1757. }
  1758. //
  1759. // For those lines that have important controls, add them to
  1760. // the UI.
  1761. //
  1762. if ((pmxud->dwFlags & MXUD_FLAGSF_MIXER) && ivcd == 0 )
  1763. fAddLine = Volume_AddLine(pmxud
  1764. , lpDst
  1765. , cbDst
  1766. , MXUL_STYLEF_DESTINATION
  1767. , &avcd[ivcd]);
  1768. else
  1769. fAddLine = Volume_AddLine(pmxud
  1770. , lpSrc
  1771. , cbSrc
  1772. , MXUL_STYLEF_SOURCE
  1773. , &avcd[ivcd]);
  1774. if (!fAddLine)
  1775. {
  1776. return FALSE;
  1777. }
  1778. }
  1779. if (fFirstRun)
  1780. Volume_GetSetRegistryLineStates(pmxud->szMixer
  1781. , pmxud->avcd[0].szShortName
  1782. , avcd
  1783. , cvcd
  1784. , SET);
  1785. }
  1786. //
  1787. // Now that both arrays are now fixed, set back pointers for
  1788. // the vcd's to ui lines.
  1789. //
  1790. for (imxul = 0; imxul < pmxud->cmxul; imxul++)
  1791. {
  1792. pmxud->amxul[imxul].pvcd->pmxul = &pmxud->amxul[imxul];
  1793. //
  1794. // Accumulate support bits
  1795. //
  1796. dwSupport |= pmxud->amxul[imxul].pvcd->dwSupport;
  1797. }
  1798. //
  1799. // Support bits say we have no advanced controls, so don't make
  1800. // them available.
  1801. //
  1802. if (!(dwSupport & VCD_SUPPORTF_MIXER_ADVANCED))
  1803. {
  1804. pmxud->dwFlags |= MXUD_FLAGSF_NOADVANCED;
  1805. }
  1806. //
  1807. // Propogate bad driver bit to be app global. A bad driver was
  1808. // detected during the construction of a volume description.
  1809. //
  1810. for (ivcd = 0; ivcd < pmxud->cvcd; ivcd++)
  1811. {
  1812. if (pmxud->avcd[ivcd].dwSupport & VCD_SUPPORTF_BADDRIVER)
  1813. {
  1814. dlout("Bad Control->Line mapping. Marking bad driver.");
  1815. pmxud->dwFlags |= MXUD_FLAGSF_BADDRIVER;
  1816. break;
  1817. }
  1818. }
  1819. }
  1820. //
  1821. // Note: it isn't necessary to free/unlock the lpMaster/lpDst/lpSrc
  1822. // because they are ptr's to resources and Win32 is smart about resources
  1823. //
  1824. return (avcd != NULL);
  1825. }
  1826. /*
  1827. * Volume_DialogBox
  1828. *
  1829. * */
  1830. DWORD Volume_DialogBox(
  1831. PMIXUIDIALOG pmxud)
  1832. {
  1833. pmxud->dwReturn = MIXUI_EXIT;
  1834. if (Volume_CreateVolume(pmxud))
  1835. {
  1836. HWND hdlg;
  1837. if(NULL == pmxud->lpDialog)
  1838. {
  1839. Volume_Cleanup(pmxud);
  1840. return MIXUI_ERROR;
  1841. }
  1842. hdlg = CreateDialogIndirectParam(pmxud->hInstance
  1843. , (DLGTEMPLATE *)pmxud->lpDialog
  1844. , NULL
  1845. , VolumeProc
  1846. , (LPARAM)(LPVOID)pmxud );
  1847. if (!hdlg)
  1848. {
  1849. Volume_Cleanup(pmxud);
  1850. return MIXUI_ERROR;
  1851. }
  1852. else
  1853. {
  1854. // Unfortunately, re-registering the winclass does not re-apply any
  1855. // new icon correctly, so we must explicitly apply it here.
  1856. SendMessage (hdlg, WM_SETICON, (WPARAM) ICON_BIG,
  1857. (LPARAM) GetAppIcon (pmxud->hInstance, pmxud->mxid));
  1858. }
  1859. ShowWindow(hdlg, pmxud->nShowCmd);
  1860. }
  1861. else
  1862. {
  1863. return MIXUI_ERROR;
  1864. }
  1865. return (DWORD)(-1);
  1866. }
  1867. void DoHtmlHelp()
  1868. {
  1869. //note, using ANSI version of function because UNICODE is foobar in NT5 builds
  1870. char chDst[MAX_PATH];
  1871. WideCharToMultiByte(CP_ACP, 0, gszHtmlHelpFileName,
  1872. -1, chDst, MAX_PATH, NULL, NULL);
  1873. HtmlHelpA(GetDesktopWindow(), chDst, HH_DISPLAY_TOPIC, 0);
  1874. }
  1875. void ProcessHelp(HWND hwnd)
  1876. {
  1877. static TCHAR HelpFile[] = TEXT("SNDVOL32.HLP");
  1878. //Handle context menu help
  1879. if(bF1InMenu)
  1880. {
  1881. switch(currMenuItem)
  1882. {
  1883. case IDM_PROPERTIES:
  1884. WinHelp(hwnd, HelpFile, HELP_CONTEXTPOPUP, IDH_SNDVOL32_OPTIONS_PROPERTIES);
  1885. break;
  1886. case IDM_ADVANCED:
  1887. WinHelp(hwnd, HelpFile, HELP_CONTEXTPOPUP, IDH_SNDVOL32_OPTIONS_ADVANCED_CONTROLS);
  1888. break;
  1889. case IDM_EXIT:
  1890. WinHelp(hwnd, HelpFile, HELP_CONTEXTPOPUP, IDH_SNDVOL32_OPTIONS_EXIT);
  1891. break;
  1892. case IDM_HELPTOPICS:
  1893. WinHelp(hwnd, HelpFile, HELP_CONTEXTPOPUP, IDH_SNDVOL32_HELP_HELP_TOPICS);
  1894. break;
  1895. case IDM_HELPABOUT:
  1896. WinHelp(hwnd, HelpFile, HELP_CONTEXTPOPUP, IDH_SNDVOL32_HELP_ABOUT);
  1897. break;
  1898. default://In the default case just display the HTML Help.
  1899. DoHtmlHelp();
  1900. }
  1901. bF1InMenu = FALSE; //This flag will be set again if F1 is pressed in a menu.
  1902. }
  1903. else
  1904. DoHtmlHelp();
  1905. }
  1906. /*
  1907. * VolumeParent_WndProc
  1908. *
  1909. * A generic invisible parent window.
  1910. *
  1911. * */
  1912. LRESULT CALLBACK VolumeParent_WndProc(
  1913. HWND hwnd,
  1914. UINT msg,
  1915. WPARAM wparam,
  1916. LPARAM lparam)
  1917. {
  1918. PMIXUIDIALOG pmxud;
  1919. switch (msg)
  1920. {
  1921. case WM_CREATE:
  1922. {
  1923. LPCREATESTRUCT lpcs = (LPCREATESTRUCT)lparam;
  1924. pmxud = (PMIXUIDIALOG)lpcs->lpCreateParams;
  1925. SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pmxud);
  1926. pmxud->hParent = hwnd;
  1927. if (Volume_DialogBox(pmxud) == MIXUI_ERROR)
  1928. {
  1929. if ( !(pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER))
  1930. {
  1931. if ( Volume_NumDevs() == 0 )
  1932. Volume_ErrorMessageBox(NULL, pmxud->hInstance, IDS_ERR_NODEV);
  1933. else
  1934. Volume_ErrorMessageBox(NULL, pmxud->hInstance, IDS_ERR_HARDWARE);
  1935. }
  1936. PostMessage(hwnd, WM_CLOSE, 0, 0L);
  1937. }
  1938. return 0;
  1939. }
  1940. case WM_CLOSE:
  1941. DestroyWindow(hwnd);
  1942. return 0;
  1943. case WM_DESTROY:
  1944. //
  1945. // Post-close cleanup
  1946. //
  1947. pmxud = (PMIXUIDIALOG)GetWindowLongPtr(hwnd, GWLP_USERDATA);
  1948. if (!(pmxud->dwStyle & MXUD_STYLEF_NOHELP))
  1949. WinHelp(hwnd, gszHelpFileName, HELP_QUIT, 0L);
  1950. PostQuitMessage(0);
  1951. return 0;
  1952. case MYWM_HELPTOPICS:
  1953. //
  1954. // F1 Help
  1955. //
  1956. pmxud = (PMIXUIDIALOG)GetWindowLongPtr(hwnd, GWLP_USERDATA);
  1957. if (!(pmxud->dwStyle & MXUD_STYLEF_NOHELP))
  1958. {
  1959. ProcessHelp(hwnd);
  1960. }
  1961. break;
  1962. case MYWM_RESTART:
  1963. //
  1964. // A device change or other user property change caused a UI
  1965. // change. Sending a restart to the parent prevents ugly stuff
  1966. // like WinHelp shutting down and exiting our primary message
  1967. // loop.
  1968. //
  1969. pmxud = (PMIXUIDIALOG)GetWindowLongPtr(hwnd, GWLP_USERDATA);
  1970. if (!(pmxud->dwStyle & MXUD_STYLEF_TRAYMASTER))
  1971. {
  1972. if (Volume_NumDevs() == 0)
  1973. {
  1974. Volume_ErrorMessageBox(NULL
  1975. , pmxud->hInstance
  1976. , IDS_ERR_NODEV);
  1977. PostMessage(hwnd, WM_CLOSE, 0, 0L);
  1978. }
  1979. else if (Volume_DialogBox((PMIXUIDIALOG)lparam) == MIXUI_ERROR)
  1980. {
  1981. Volume_ErrorMessageBox(NULL
  1982. , pmxud->hInstance
  1983. , IDS_ERR_HARDWARE);
  1984. PostMessage(hwnd, WM_CLOSE, 0, 0L);
  1985. }
  1986. }
  1987. else
  1988. {
  1989. if (Mixer_GetNumDevs() == 0
  1990. || Volume_DialogBox((PMIXUIDIALOG)lparam) == MIXUI_ERROR)
  1991. PostMessage(hwnd, WM_CLOSE, 0, 0L);
  1992. }
  1993. break;
  1994. default:
  1995. break;
  1996. }
  1997. return (DefWindowProc(hwnd, msg, wparam, lparam));
  1998. }
  1999. const TCHAR szNull[] = TEXT ("");
  2000. /*
  2001. * Parent Dialog
  2002. * */
  2003. HWND VolumeParent_DialogMain(
  2004. PMIXUIDIALOG pmxud)
  2005. {
  2006. WNDCLASS wc;
  2007. HWND hwnd;
  2008. wc.lpszClassName = gszParentClass;
  2009. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  2010. wc.hIcon = NULL;
  2011. wc.lpszMenuName = NULL;
  2012. wc.hbrBackground = NULL;
  2013. wc.hInstance = pmxud->hInstance;
  2014. wc.style = 0;
  2015. wc.lpfnWndProc = VolumeParent_WndProc;
  2016. wc.cbClsExtra = 0;
  2017. wc.cbWndExtra = 0;
  2018. if (!RegisterClass(&wc))
  2019. return NULL;
  2020. hwnd = CreateWindow(gszParentClass
  2021. , szNull
  2022. , 0
  2023. , 0
  2024. , 0
  2025. , 0
  2026. , 0
  2027. , NULL
  2028. , NULL
  2029. , pmxud->hInstance
  2030. , (LPVOID)pmxud );
  2031. return hwnd;
  2032. }
  2033. /*
  2034. * Determines if what the recording destination ID
  2035. */
  2036. HRESULT GetRecordingDestID(int mxid, DWORD *piDest)
  2037. {
  2038. HRESULT hr = E_FAIL;
  2039. DWORD cDest;
  2040. int iDest;
  2041. MMRESULT mmr;
  2042. MIXERCAPS mxcaps;
  2043. if (piDest)
  2044. {
  2045. *piDest = 0;
  2046. mmr = mixerGetDevCaps(mxid, &mxcaps, sizeof(MIXERCAPS));
  2047. if (mmr == MMSYSERR_NOERROR)
  2048. {
  2049. cDest = mxcaps.cDestinations;
  2050. for (iDest = cDest - 1; iDest >= 0; iDest--)
  2051. {
  2052. MIXERLINE mlDst;
  2053. mlDst.cbStruct = sizeof ( mlDst );
  2054. mlDst.dwDestination = iDest;
  2055. if (mixerGetLineInfo((HMIXEROBJ)IntToPtr(mxid), &mlDst, MIXER_GETLINEINFOF_DESTINATION) != MMSYSERR_NOERROR)
  2056. continue;
  2057. if (Mixer_IsValidRecordingDestination ((HMIXEROBJ)IntToPtr(mxid), &mlDst))
  2058. {
  2059. *piDest = iDest;
  2060. hr = S_OK;
  2061. break;
  2062. }
  2063. }
  2064. }
  2065. }
  2066. return(hr);
  2067. }
  2068. /*------------------------------------------------------+
  2069. | HelpMsgFilter - filter for F1 key in dialogs |
  2070. | |
  2071. +------------------------------------------------------*/
  2072. DWORD FAR PASCAL HelpMsgFilter(int nCode, UINT wParam, DWORD_PTR lParam)
  2073. {
  2074. if (nCode >= 0)
  2075. {
  2076. LPMSG msg = (LPMSG)lParam;
  2077. if (ghwndApp && (msg->message == WM_KEYDOWN) && (msg->wParam == VK_F1))
  2078. {
  2079. if(nCode == MSGF_MENU)
  2080. bF1InMenu = TRUE;
  2081. SendMessage(ghwndApp, WM_COMMAND, (WPARAM)IDM_HELPTOPICS, 0L);
  2082. }
  2083. }
  2084. return 0;
  2085. }
  2086. /*
  2087. * Returns the correct Destination ID for the specified device ID
  2088. */
  2089. HRESULT GetDestination(DWORD mxid, int *piDest)
  2090. {
  2091. if (gfRecord)
  2092. {
  2093. return GetDestLineID(mxid,piDest);
  2094. }
  2095. else
  2096. {
  2097. return GetSrcLineID(mxid,piDest);
  2098. }
  2099. }
  2100. /*
  2101. * Determines line ID
  2102. */
  2103. HRESULT GetDestLineID(int mxid, DWORD *piDest)
  2104. {
  2105. HRESULT hr = E_FAIL;
  2106. MIXERLINE mlDst;
  2107. if (piDest)
  2108. {
  2109. hr = S_OK;
  2110. *piDest = 0;
  2111. mlDst.cbStruct = sizeof ( mlDst );
  2112. mlDst.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
  2113. if (mixerGetLineInfo((HMIXEROBJ)IntToPtr(mxid), &mlDst, MIXER_GETLINEINFOF_COMPONENTTYPE) == MMSYSERR_NOERROR)
  2114. {
  2115. *piDest = mlDst.dwDestination;
  2116. }
  2117. }
  2118. return(hr);
  2119. }
  2120. /*
  2121. * Determines line ID
  2122. */
  2123. HRESULT GetSrcLineID(int mxid, DWORD *piDest)
  2124. {
  2125. HRESULT hr = E_FAIL;
  2126. MIXERLINE mlDst;
  2127. if (piDest)
  2128. {
  2129. hr = S_OK;
  2130. *piDest = 0;
  2131. mlDst.cbStruct = sizeof ( mlDst );
  2132. mlDst.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
  2133. if (mixerGetLineInfo((HMIXEROBJ)IntToPtr(mxid), &mlDst, MIXER_GETLINEINFOF_COMPONENTTYPE ) == MMSYSERR_NOERROR)
  2134. {
  2135. *piDest = mlDst.dwDestination;
  2136. }
  2137. else
  2138. {
  2139. mlDst.cbStruct = sizeof ( mlDst );
  2140. mlDst.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_HEADPHONES;
  2141. if (mixerGetLineInfo((HMIXEROBJ)IntToPtr(mxid), &mlDst, MIXER_GETLINEINFOF_COMPONENTTYPE ) == MMSYSERR_NOERROR)
  2142. {
  2143. *piDest = mlDst.dwDestination;
  2144. }
  2145. else
  2146. {
  2147. mlDst.cbStruct = sizeof ( mlDst );
  2148. mlDst.dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
  2149. if (mixerGetLineInfo((HMIXEROBJ)IntToPtr(mxid), &mlDst, MIXER_GETLINEINFOF_COMPONENTTYPE ) == MMSYSERR_NOERROR)
  2150. {
  2151. *piDest = mlDst.dwDestination;
  2152. }
  2153. }
  2154. }
  2155. }
  2156. return(hr);
  2157. }
  2158. /* - - - - - - - - - */
  2159. /*
  2160. * entry point
  2161. * */
  2162. int WINAPI WinMain(
  2163. HINSTANCE hInst,
  2164. HINSTANCE hPrev,
  2165. LPSTR lpCmdLine,
  2166. int nShowCmd)
  2167. {
  2168. int err = 0;
  2169. MIXUIDIALOG mxud;
  2170. MSG msg;
  2171. HWND hwnd;
  2172. HANDLE hAccel;
  2173. MMRESULT mmr;
  2174. TCHAR ach[2];
  2175. UINT u;
  2176. BOOL fGotDevice = FALSE;
  2177. UINT uDeviceID;
  2178. ach[0] = '\0'; // PREFIX complains if we do not init this.
  2179. LoadString(hInst, IDS_IS_RTL, ach, SIZEOF(ach));
  2180. gfIsRTL = ach[0] == TEXT('1');
  2181. //
  2182. // initialize the app instance data
  2183. //
  2184. ZeroMemory(&mxud, sizeof(mxud));
  2185. mxud.hInstance = hInst;
  2186. mxud.dwFlags = MXUD_FLAGSF_MIXER;
  2187. /* setup the message filter to handle grabbing F1 for this task */
  2188. fpfnMsgHook = (HOOKPROC)MakeProcInstance((FARPROC)HelpMsgFilter, ghInst);
  2189. fpfnOldMsgFilter = (HHOOK)SetWindowsHook(WH_MSGFILTER, fpfnMsgHook);
  2190. //
  2191. // parse the command line for "/T"
  2192. //
  2193. u = 0;
  2194. while (lpCmdLine[u] != '\0')
  2195. {
  2196. switch (lpCmdLine[u])
  2197. {
  2198. case TEXT('-'):
  2199. case TEXT('/'):
  2200. {
  2201. u++;
  2202. if (lpCmdLine[u] != '\0')
  2203. {
  2204. switch (lpCmdLine[u])
  2205. {
  2206. case TEXT('T'):
  2207. case TEXT('t'):
  2208. mxud.dwStyle |= MXUD_STYLEF_TRAYMASTER;
  2209. u++;
  2210. break;
  2211. case TEXT('S'):
  2212. case TEXT('s'):
  2213. mxud.dwStyle |= MXUD_STYLEF_SMALL;
  2214. u++;
  2215. break;
  2216. case TEXT('R'): // Should run in Record mode, not Playback (default)
  2217. case TEXT('r'):
  2218. gfRecord = TRUE;
  2219. u++;
  2220. break;
  2221. case TEXT('X'):
  2222. case TEXT('x'):
  2223. mxud.dwStyle |= MXUD_STYLEF_TRAYMASTER | MXUD_STYLEF_CLOSE;
  2224. break;
  2225. case TEXT('D'): // Should use the specified device
  2226. case TEXT('d'):
  2227. {
  2228. u++; // Skip "d" and any following spaces
  2229. while (lpCmdLine[u] != '\0' && isspace(lpCmdLine[u]))
  2230. {
  2231. u++;
  2232. }
  2233. if (lpCmdLine[u] != '\0')
  2234. {
  2235. char szDeviceID[255];
  2236. UINT uDev = 0;
  2237. while (lpCmdLine[u] != '\0' && !isalpha(lpCmdLine[u]) && !isspace(lpCmdLine[u]))
  2238. {
  2239. szDeviceID[uDev] = lpCmdLine[u];
  2240. u++;
  2241. uDev++;
  2242. }
  2243. szDeviceID[uDev] = '\0';
  2244. uDeviceID = strtoul(szDeviceID,NULL,10);
  2245. fGotDevice = TRUE;
  2246. }
  2247. }
  2248. break;
  2249. default: // Unknown Command, just ignore it.
  2250. u++;
  2251. break;
  2252. }
  2253. }
  2254. }
  2255. break;
  2256. default:
  2257. {
  2258. u++;
  2259. }
  2260. break;
  2261. }
  2262. }
  2263. //
  2264. // Restore last style
  2265. //
  2266. if (!(mxud.dwStyle & (MXUD_STYLEF_TRAYMASTER|MXUD_STYLEF_SMALL)))
  2267. {
  2268. Volume_GetSetStyle(&mxud.dwStyle, GET);
  2269. }
  2270. if (mxud.dwStyle & MXUD_STYLEF_TRAYMASTER)
  2271. {
  2272. HWND hwndSV;
  2273. //
  2274. // Locate a waiting instance of the tray volume and wake it up
  2275. //
  2276. hwndSV = FindWindow(gszTrayClassName, NULL);
  2277. if (hwndSV) {
  2278. SendMessage(hwndSV, MYWM_WAKEUP,
  2279. (mxud.dwStyle & MXUD_STYLEF_CLOSE), 0);
  2280. goto mxendapp;
  2281. }
  2282. }
  2283. if (mxud.dwStyle & MXUD_STYLEF_CLOSE) {
  2284. goto mxendapp;
  2285. }
  2286. //
  2287. // Init to the default mixer
  2288. //
  2289. if (fGotDevice)
  2290. {
  2291. UINT cWaves;
  2292. if (gfRecord)
  2293. {
  2294. cWaves = waveInGetNumDevs();
  2295. }
  2296. else
  2297. {
  2298. cWaves = waveOutGetNumDevs();
  2299. }
  2300. if (uDeviceID >= cWaves)
  2301. {
  2302. fGotDevice = FALSE;
  2303. }
  2304. }
  2305. if (!fGotDevice)
  2306. {
  2307. mmr = Volume_GetDefaultMixerID(&mxud.mxid, gfRecord);
  2308. }
  2309. else
  2310. {
  2311. mxud.mxid = uDeviceID;
  2312. }
  2313. if (gfRecord)
  2314. {
  2315. if (FAILED(GetRecordingDestID(mxud.mxid,&mxud.iDest)))
  2316. {
  2317. goto mxendapp;
  2318. }
  2319. }
  2320. else
  2321. {
  2322. if (FAILED(GetDestination(mxud.mxid,&mxud.iDest)))
  2323. {
  2324. goto mxendapp;
  2325. }
  2326. }
  2327. //
  2328. // For the tray master, get the mix id associated with the default
  2329. // wave device. If this fails, go away.
  2330. //
  2331. if (mxud.dwStyle & MXUD_STYLEF_TRAYMASTER)
  2332. {
  2333. if (mmr != MMSYSERR_NOERROR)
  2334. goto mxendapp;
  2335. mxud.dwStyle |= MXUD_STYLEF_NOHELP;
  2336. mxud.nShowCmd = SW_HIDE;
  2337. }
  2338. else
  2339. {
  2340. if (!Volume_NumDevs())
  2341. {
  2342. Volume_ErrorMessageBox(NULL, hInst, IDS_ERR_NODEV);
  2343. goto mxendapp;
  2344. }
  2345. InitVUControl(hInst);
  2346. if (!LoadString(hInst
  2347. , IDS_HELPFILENAME
  2348. , gszHelpFileName
  2349. , SIZEOF(gszHelpFileName)))
  2350. mxud.dwStyle |= MXUD_STYLEF_NOHELP;
  2351. if (!LoadString(hInst
  2352. , IDS_HTMLHELPFILENAME
  2353. , gszHtmlHelpFileName
  2354. , SIZEOF(gszHtmlHelpFileName)))
  2355. mxud.dwStyle |= MXUD_STYLEF_NOHELP;
  2356. mxud.nShowCmd = (nShowCmd == SW_SHOWMAXIMIZED)
  2357. ? SW_SHOWNORMAL:nShowCmd;
  2358. if (!(mxud.dwStyle & MXUD_STYLEF_SMALL))
  2359. mxud.dwStyle |= MXUD_STYLEF_STATUS; // has status bar
  2360. }
  2361. //
  2362. // Use the common controls
  2363. //
  2364. InitCommonControls();
  2365. hAccel = LoadAccelerators(hInst, MAKEINTRESOURCE(IDR_VOLUMEACCEL));
  2366. hwnd = VolumeParent_DialogMain(&mxud);
  2367. //Initialize the handle which the hook for F1 help will use.
  2368. ghwndApp = mxud.hwnd;
  2369. if (hwnd)
  2370. {
  2371. while (GetMessage(&msg, NULL, 0, 0))
  2372. {
  2373. if (mxud.hwnd) {
  2374. if (hAccel && TranslateAccelerator(mxud.hwnd, hAccel, &msg))
  2375. continue;
  2376. if (IsDialogMessage(mxud.hwnd,&msg))
  2377. continue;
  2378. }
  2379. TranslateMessage(&msg);
  2380. DispatchMessage(&msg);
  2381. }
  2382. }
  2383. mxendapp:
  2384. /* if the message hook was installed, remove it and free */
  2385. /* up our proc instance for it. */
  2386. if (fpfnOldMsgFilter){
  2387. UnhookWindowsHook(WH_MSGFILTER, fpfnMsgHook);
  2388. }
  2389. return err;
  2390. }
  2391. void FreeAppIcon ()
  2392. {
  2393. if (ghiconApp)
  2394. {
  2395. DestroyIcon (ghiconApp);
  2396. ghiconApp = NULL;
  2397. }
  2398. }
  2399. // TODO: Move to "regstr.h"
  2400. #define REGSTR_KEY_BRANDING TEXT("Branding")
  2401. #define REGSTR_VAL_AUDIO_BITMAP TEXT("bitmap")
  2402. #define REGSTR_VAL_AUDIO_ICON TEXT("icon")
  2403. #define REGSTR_VAL_AUDIO_URL TEXT("url")
  2404. HKEY OpenDeviceBrandRegKey (UINT uiMixID)
  2405. {
  2406. HKEY hkeyBrand = NULL;
  2407. HKEY hkeyDevice = OpenDeviceRegKey (uiMixID, KEY_READ);
  2408. if (hkeyDevice)
  2409. {
  2410. if (ERROR_SUCCESS != RegOpenKey (hkeyDevice, REGSTR_KEY_BRANDING, &hkeyBrand))
  2411. hkeyBrand = NULL; // Make sure NULL on failure
  2412. // Close the Device key
  2413. RegCloseKey (hkeyDevice);
  2414. }
  2415. return hkeyBrand;
  2416. }
  2417. ///////////////////////////////////////////////////////////////////////////////////////////
  2418. // Microsoft Confidential - DO NOT COPY THIS METHOD INTO ANY APPLICATION, THIS MEANS YOU!!!
  2419. ///////////////////////////////////////////////////////////////////////////////////////////
  2420. PTCHAR GetInterfaceName (DWORD dwMixerID)
  2421. {
  2422. MMRESULT mmr;
  2423. ULONG cbSize=0;
  2424. TCHAR *szInterfaceName=NULL;
  2425. //Query for the Device interface name
  2426. mmr = mixerMessage(HMIXER_INDEX(dwMixerID), DRV_QUERYDEVICEINTERFACESIZE, (DWORD_PTR)&cbSize, 0L);
  2427. if(MMSYSERR_NOERROR == mmr)
  2428. {
  2429. szInterfaceName = (TCHAR *)GlobalAllocPtr(GHND, (cbSize+1)*sizeof(TCHAR));
  2430. if(!szInterfaceName)
  2431. {
  2432. return NULL;
  2433. }
  2434. mmr = mixerMessage(HMIXER_INDEX(dwMixerID), DRV_QUERYDEVICEINTERFACE, (DWORD_PTR)szInterfaceName, cbSize);
  2435. if(MMSYSERR_NOERROR != mmr)
  2436. {
  2437. GlobalFreePtr(szInterfaceName);
  2438. return NULL;
  2439. }
  2440. }
  2441. return szInterfaceName;
  2442. }
  2443. HKEY OpenDeviceRegKey (UINT uiMixID, REGSAM sam)
  2444. {
  2445. HKEY hkeyDevice = NULL;
  2446. PTCHAR szInterfaceName = GetInterfaceName (uiMixID);
  2447. if (szInterfaceName)
  2448. {
  2449. HDEVINFO DeviceInfoSet = SetupDiCreateDeviceInfoList (NULL, NULL);
  2450. if (INVALID_HANDLE_VALUE != DeviceInfoSet)
  2451. {
  2452. SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
  2453. DeviceInterfaceData.cbSize = sizeof (SP_DEVICE_INTERFACE_DATA);
  2454. if (SetupDiOpenDeviceInterface (DeviceInfoSet, szInterfaceName,
  2455. 0, &DeviceInterfaceData))
  2456. {
  2457. DWORD dwRequiredSize;
  2458. SP_DEVINFO_DATA DeviceInfoData;
  2459. DeviceInfoData.cbSize = sizeof (SP_DEVINFO_DATA);
  2460. // Ignore error, it always returns "ERROR_INSUFFICIENT_BUFFER" even though
  2461. // the "SP_DEVICE_INTERFACE_DETAIL_DATA" parameter is supposed to be optional.
  2462. (void) SetupDiGetDeviceInterfaceDetail (DeviceInfoSet, &DeviceInterfaceData,
  2463. NULL, 0, &dwRequiredSize, &DeviceInfoData);
  2464. // Open device reg key
  2465. hkeyDevice = SetupDiOpenDevRegKey (DeviceInfoSet, &DeviceInfoData,
  2466. DICS_FLAG_GLOBAL, 0,
  2467. DIREG_DRV, sam);
  2468. }
  2469. SetupDiDestroyDeviceInfoList (DeviceInfoSet);
  2470. }
  2471. GlobalFreePtr (szInterfaceName);
  2472. }
  2473. return hkeyDevice;
  2474. }
  2475. HICON GetAppIcon (HINSTANCE hInst, UINT uiMixID)
  2476. {
  2477. HKEY hkeyBrand = OpenDeviceBrandRegKey (uiMixID);
  2478. FreeAppIcon ();
  2479. if (hkeyBrand)
  2480. {
  2481. WCHAR szBuffer[MAX_PATH];
  2482. DWORD dwType = REG_SZ;
  2483. DWORD cb = sizeof (szBuffer) / sizeof (szBuffer[0]);
  2484. if (ERROR_SUCCESS == RegQueryValueEx (hkeyBrand, REGSTR_VAL_AUDIO_ICON, NULL, &dwType, (LPBYTE)szBuffer, &cb))
  2485. {
  2486. WCHAR* pszComma = wcschr (szBuffer, L',');
  2487. if (pszComma)
  2488. {
  2489. WCHAR* pszResourceID = pszComma + 1;
  2490. HANDLE hResource;
  2491. // Remove comma delimeter
  2492. *pszComma = L'\0';
  2493. // Should be a resource module and a resource ID
  2494. hResource = LoadLibrary (szBuffer);
  2495. if (!hResource)
  2496. {
  2497. WCHAR szDriversPath[MAX_PATH];
  2498. // If we didn't find it on the normal search path, try looking
  2499. // in the "drivers" directory.
  2500. if (GetSystemDirectory (szDriversPath, MAX_PATH))
  2501. {
  2502. wcscat (szDriversPath, TEXT("\\drivers\\"));
  2503. wcscat (szDriversPath, szBuffer);
  2504. hResource = LoadLibrary (szDriversPath);
  2505. }
  2506. }
  2507. if (hResource)
  2508. {
  2509. ghiconApp = LoadImage (hResource, MAKEINTRESOURCE(_wtoi (pszResourceID)), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
  2510. FreeLibrary (hResource);
  2511. }
  2512. }
  2513. else
  2514. // Should be an *.ico file
  2515. ghiconApp = LoadImage (NULL, szBuffer, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE);
  2516. }
  2517. RegCloseKey (hkeyBrand);
  2518. // Return the custom icon
  2519. if (ghiconApp)
  2520. return ghiconApp;
  2521. }
  2522. return (LoadIcon (hInst, MAKEINTRESOURCE (IDI_MIXER)));
  2523. }