reactos/dll/shellext/stobject/volume.cpp

341 lines
8.8 KiB
C++
Raw Normal View History

/*
* PROJECT: ReactOS system libraries
* LICENSE: GPL - See COPYING in the top level directory
* FILE: dll/shellext/stobject/volume.cpp
* PURPOSE: Volume notification icon handler
* PROGRAMMERS: David Quintana <gigaherz@gmail.com>
*/
#include "precomp.h"
#include <mmddk.h>
HICON g_hIconVolume;
HICON g_hIconMute;
HMIXER g_hMixer;
UINT g_mixerId;
DWORD g_mixerLineID;
DWORD g_muteControlID;
UINT g_mmDeviceChange;
static BOOL g_IsMute = FALSE;
static BOOL g_IsRunning = FALSE;
static HRESULT __stdcall Volume_FindMixerControl(CSysTray * pSysTray)
{
MMRESULT result;
UINT mixerId = 0;
DWORD waveOutId = 0;
DWORD param2 = 0;
TRACE("Volume_FindDefaultMixerID\n");
result = waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&waveOutId, (DWORD_PTR)&param2);
if (result)
return E_FAIL;
if (waveOutId == (DWORD)-1)
{
TRACE("WARNING: waveOut has no default device, trying with first available device...\n", waveOutId);
mixerId = 0;
}
else
{
TRACE("waveOut default device is %d\n", waveOutId);
result = mixerGetID((HMIXEROBJ)waveOutId, &mixerId, MIXER_OBJECTF_WAVEOUT);
if (result)
return E_FAIL;
TRACE("mixerId for waveOut default device is %d\n", mixerId);
}
g_mixerId = mixerId;
return S_OK;
MIXERCAPS mixerCaps;
MIXERLINE mixerLine;
MIXERCONTROL mixerControl;
MIXERLINECONTROLS mixerLineControls;
g_mixerLineID = -1;
g_muteControlID = -1;
if (mixerGetDevCapsW(g_mixerId, &mixerCaps, sizeof(mixerCaps)))
return E_FAIL;
if (mixerCaps.cDestinations == 0)
return S_FALSE;
TRACE("mixerCaps.cDestinations %d\n", mixerCaps.cDestinations);
DWORD idx;
for (idx = 0; idx < mixerCaps.cDestinations; idx++)
{
mixerLine.cbStruct = sizeof(mixerLine);
mixerLine.dwDestination = idx;
if (!mixerGetLineInfoW((HMIXEROBJ)g_mixerId, &mixerLine, 0))
{
if (mixerLine.dwComponentType >= MIXERLINE_COMPONENTTYPE_DST_SPEAKERS &&
mixerLine.dwComponentType <= MIXERLINE_COMPONENTTYPE_DST_HEADPHONES)
break;
TRACE("Destination %d was not speakers or headphones.\n");
}
}
if (idx >= mixerCaps.cDestinations)
return E_FAIL;
TRACE("Valid destination %d found.\n");
g_mixerLineID = mixerLine.dwLineID;
mixerLineControls.cbStruct = sizeof(mixerLineControls);
mixerLineControls.dwLineID = mixerLine.dwLineID;
mixerLineControls.cControls = 1;
mixerLineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
mixerLineControls.pamxctrl = &mixerControl;
mixerLineControls.cbmxctrl = sizeof(mixerControl);
if (mixerGetLineControlsW((HMIXEROBJ)g_mixerId, &mixerLineControls, MIXER_GETLINECONTROLSF_ONEBYTYPE))
return E_FAIL;
TRACE("Found control id %d for mute: %d\n", mixerControl.dwControlID);
g_muteControlID = mixerControl.dwControlID;
return S_OK;
}
HRESULT Volume_IsMute()
{
#if 0
MIXERCONTROLDETAILS mixerControlDetails;
if (g_mixerId != (UINT)-1 && g_muteControlID != (DWORD)-1)
{
BOOL detailsResult = 0;
mixerControlDetails.cbStruct = sizeof(mixerControlDetails);
mixerControlDetails.hwndOwner = 0;
mixerControlDetails.dwControlID = g_muteControlID;
mixerControlDetails.cChannels = 1;
mixerControlDetails.paDetails = &detailsResult;
mixerControlDetails.cbDetails = sizeof(detailsResult);
if (mixerGetControlDetailsW((HMIXEROBJ) g_mixerId, &mixerControlDetails, 0))
return E_FAIL;
TRACE("Obtained mute status %d\n", detailsResult);
g_IsMute = detailsResult != 0;
}
#endif
return S_OK;
}
HRESULT STDMETHODCALLTYPE Volume_Init(_In_ CSysTray * pSysTray)
{
HRESULT hr;
WCHAR strTooltip[128];
TRACE("Volume_Init\n");
if (!g_hMixer)
{
hr = Volume_FindMixerControl(pSysTray);
if (FAILED(hr))
return hr;
g_mmDeviceChange = RegisterWindowMessageW(L"winmm_devicechange");
}
g_hIconVolume = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_VOLUME));
g_hIconMute = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_VOLMUTE));
Volume_IsMute();
g_IsRunning = TRUE;
HICON icon;
if (g_IsMute)
icon = g_hIconMute;
else
icon = g_hIconVolume;
LoadStringW(g_hInstance, IDS_VOL_VOLUME, strTooltip, _countof(strTooltip));
return pSysTray->NotifyIcon(NIM_ADD, ID_ICON_VOLUME, icon, strTooltip);
}
HRESULT STDMETHODCALLTYPE Volume_Update(_In_ CSysTray * pSysTray)
{
BOOL PrevState;
TRACE("Volume_Update\n");
PrevState = g_IsMute;
Volume_IsMute();
if (PrevState != g_IsMute)
{
WCHAR strTooltip[128];
HICON icon;
if (g_IsMute)
{
icon = g_hIconMute;
LoadStringW(g_hInstance, IDS_VOL_MUTED, strTooltip, _countof(strTooltip));
}
else
{
icon = g_hIconVolume;
LoadStringW(g_hInstance, IDS_VOL_VOLUME, strTooltip, _countof(strTooltip));
}
return pSysTray->NotifyIcon(NIM_MODIFY, ID_ICON_VOLUME, icon, strTooltip);
}
else
{
return S_OK;
}
}
HRESULT STDMETHODCALLTYPE Volume_Shutdown(_In_ CSysTray * pSysTray)
{
TRACE("Volume_Shutdown\n");
g_IsRunning = FALSE;
return pSysTray->NotifyIcon(NIM_DELETE, ID_ICON_VOLUME, NULL, NULL);
}
HRESULT Volume_OnDeviceChange(_In_ CSysTray * pSysTray, WPARAM wParam, LPARAM lParam)
{
return Volume_FindMixerControl(pSysTray);
}
static void _RunVolume(BOOL bTray)
{
[0.4.10][SHELL32] Squashed port of several RunDlg and ShellExecuteW() fixes This is a big backport, that brings us closer to releases/0.4.14/0.4.14-release-10-g1b0b852 The main motivation is to fix the three regressions: CORE-17351 'RunDlg fails calling URL without http' and CORE-16898 'RunCommand "iexplore" fails to open Wine IE' and CORE-15431 'RunDlg variable expansion not longer working' All 3 regressed by 0.4.10-dev-419-g bfcbda227f99c1b59e8ed71f5e0f59f793d496a1 By porting back the following commits: 0.4.15-dev-2883-g 33c7c91b36dbace264d9d70d606df5d6d1cd7552 CORE-17351 CORE-16898 (#3797) Improve performance of Start-Run Dialog Box Options 0.4.14-dev-1016-g 4e721f780e72bc7a0c780a477b76b7f09126c145 CORE-12266 (#1854) [SHELL32] This fixes the ERROR_DDE_FAIL in the 'www.google.de' testcase 0.4.13-dev-790-g 0f16d44b660266130f5c0807c01d01cfaa6e685d CORE-6412 + CORE-12927 but in return we have to fix all the callers of ShellExecuteW() that relied on this ancient hack-code, and we do this by picking 0.4.13-dev-930-g cb77f1701fea36e872a0be3a3a89b324cce4748c CORE-16299 [SHELL32][STOBJECT] ShellExecuteW() params part2 0.4.13-dev-860-g 944aba1fcce206af0e1d35187a0df12571dc318b CORE-16299 [SHELL32] ShellExecuteW() params part1 (#1833) 0.4.12-dev-94-g 932a812c66a6ee041f2f032fd25c2344c6a09db3 CORE-15434 [SHELL32] shlexec.cpp UNICODE_NULL Add backslash if path was like C: (#1152) 0.4.12-dev-49-g fad9f6677a8408ec94684a1061341362a35b7396 CORE-15434 [SHELL32] dialogs.cpp (#1117) Addendum 0.4.12-dev-48-g c64d31e9b86eec94d0c1abcd296bc74b15fd5250 CORE-15434 [BROWSEUI] Add backslash for directory (#1121) C: C:\ 0.4.12-dev-36-g 472787ffea068447d9ff529f836ceff2a8a04c31 CORE-15431 (#1117) Crash on %SYSTEMROOT% (yes, this was already within 0.4.11) 0.4.12-dev-19-g 06d717e3bc3923d6c1fc2e3420430dd597e517a6 CORE-15431 (#1111) Enable Environment Variables (yes, this was already within 0.4.11)
2022-02-20 20:14:03 +00:00
ShellExecuteW(NULL,
NULL,
L"sndvol32.exe",
bTray ? L"/t" : NULL,
NULL,
SW_SHOWNORMAL);
}
static void _RunMMCpl()
{
ShellExecuteW(NULL, NULL, L"mmsys.cpl", NULL, NULL, SW_NORMAL);
}
static void _ShowContextMenu(CSysTray * pSysTray)
{
WCHAR strAdjust[128];
WCHAR strOpen[128];
LoadStringW(g_hInstance, IDS_VOL_OPEN, strOpen, _countof(strOpen));
LoadStringW(g_hInstance, IDS_VOL_ADJUST, strAdjust, _countof(strAdjust));
HMENU hPopup = CreatePopupMenu();
AppendMenuW(hPopup, MF_STRING, IDS_VOL_OPEN, strOpen);
AppendMenuW(hPopup, MF_STRING, IDS_VOL_ADJUST, strAdjust);
DWORD flags = TPM_RETURNCMD | TPM_NONOTIFY | TPM_RIGHTALIGN | TPM_BOTTOMALIGN;
POINT pt;
SetForegroundWindow(pSysTray->GetHWnd());
GetCursorPos(&pt);
DWORD id = TrackPopupMenuEx(hPopup, flags,
pt.x, pt.y,
pSysTray->GetHWnd(), NULL);
DestroyMenu(hPopup);
switch (id)
{
case IDS_VOL_OPEN:
_RunVolume(FALSE);
break;
case IDS_VOL_ADJUST:
_RunMMCpl();
break;
}
}
HRESULT STDMETHODCALLTYPE Volume_Message(_In_ CSysTray * pSysTray, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT &lResult)
{
if (uMsg == g_mmDeviceChange)
return Volume_OnDeviceChange(pSysTray, wParam, lParam);
switch (uMsg)
{
case WM_USER + 220:
TRACE("Volume_Message: WM_USER+220\n");
if (wParam == 4)
{
if (lParam == FALSE)
return Volume_Init(pSysTray);
else
return Volume_Shutdown(pSysTray);
}
return S_FALSE;
case WM_USER + 221:
TRACE("Volume_Message: WM_USER+221\n");
if (wParam == 4)
{
lResult = (LRESULT)g_IsRunning;
return S_OK;
}
return S_FALSE;
case WM_TIMER:
if (wParam == VOLUME_TIMER_ID)
{
KillTimer(pSysTray->GetHWnd(), VOLUME_TIMER_ID);
_RunVolume(TRUE);
}
break;
case ID_ICON_VOLUME:
TRACE("Volume_Message uMsg=%d, w=%x, l=%x\n", uMsg, wParam, lParam);
Volume_Update(pSysTray);
switch (lParam)
{
case WM_LBUTTONDOWN:
SetTimer(pSysTray->GetHWnd(), VOLUME_TIMER_ID, GetDoubleClickTime(), NULL);
break;
case WM_LBUTTONUP:
break;
case WM_LBUTTONDBLCLK:
KillTimer(pSysTray->GetHWnd(), VOLUME_TIMER_ID);
_RunVolume(FALSE);
break;
case WM_RBUTTONDOWN:
break;
case WM_RBUTTONUP:
_ShowContextMenu(pSysTray);
break;
case WM_RBUTTONDBLCLK:
break;
case WM_MOUSEMOVE:
break;
}
return S_OK;
default:
TRACE("Volume_Message received for unknown ID %d, ignoring.\n");
return S_FALSE;
}
return S_FALSE;
}