2025-11-28 00:35:46 +09:00

264 lines
8.2 KiB
C++

// Abstract: Defines the entry point for the application.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved
// --------------------------------------------------------------------------------
#include "stdafx.h"
#include "OSD.h"
#include "endpointMonitor.h"
#include <new>
// Global Variables:
HINSTANCE g_hInstance = NULL;
wchar_t const g_szWindowClass[] = L"csl_osd";
CVolumeMonitor* g_pVolumeMonitor = NULL;
HWND g_hwndOSD = NULL;
BOOL g_bDblBuffered = FALSE;
VOLUME_INFO g_currentVolume = {0};
// Forward declarations of functions included in this code module:
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
//
// DPI Scaling logic - helper functions to allow us to respect DPI settings.
//
float g_fDPIScale = 1.0f;
void InitializeDPIScale(HWND hWnd)
{
//
// Create the font to be used in the OSD, making sure that
// the font size is scaled system DPI setting.
//
HDC hdc = GetDC(hWnd);
g_fDPIScale = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0f;
ReleaseDC(hWnd, hdc);
}
int DPIScale(int iValue)
{
return static_cast<int>(iValue * g_fDPIScale);
}
// Entry to the app
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR /*lpCmdLine*/, int /*nCmdShow*/)
{
g_hInstance = hInstance;
// Mark that this process is DPI aware.
SetProcessDPIAware();
// Init COM and double-buffered painting
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr))
{
hr = BufferedPaintInit();
g_bDblBuffered = SUCCEEDED(hr);
// Init volume monitor
g_pVolumeMonitor = new (std::nothrow) CVolumeMonitor();
if (g_pVolumeMonitor)
{
hr = g_pVolumeMonitor->Initialize();
if (SUCCEEDED(hr))
{
// Get initial volume level so that we can figure out a good window size
g_pVolumeMonitor->GetLevelInfo(&g_currentVolume);
WNDCLASSEX wcex = { sizeof(wcex) };
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.hInstance = g_hInstance;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszClassName = g_szWindowClass;
RegisterClassEx(&wcex);
// Create the (only) window
DWORD const dwStyle = WS_POPUP; // no border or title bar
DWORD const dwStyleEx = WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_NOACTIVATE; // transparent, topmost, with no taskbar item
g_hwndOSD = CreateWindowEx(dwStyleEx, g_szWindowClass, NULL, dwStyle, 0, 0, 0, 0, NULL, NULL, g_hInstance, NULL);
if (g_hwndOSD)
{
// Hide the window
ShowWindow(g_hwndOSD, SW_HIDE);
// Main message loop
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
if (g_bDblBuffered)
BufferedPaintUnInit();
g_pVolumeMonitor->Dispose();
g_pVolumeMonitor->Release();
}
}
CoUninitialize();
}
return 0;
}
// ----------------------------------------------------------------------
// Windows proc for the one and only window in this app.
//
// ----------------------------------------------------------------------
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HBRUSH hbrLit = NULL;
static HBRUSH hbrUnlit = NULL;
static HFONT hFont = NULL;
static UINT_PTR nTimerId = 101;
switch (message)
{
case WM_CREATE:
{
// Make BLACK the transparency color
SetLayeredWindowAttributes(hWnd, RGB(0,0,0), 0, LWA_COLORKEY);
// Initialize the DPI scalar.
InitializeDPIScale(hWnd);
// Create brushes and font that will be used in WM_PAINT
hbrLit = CreateSolidBrush(RGB(0,128,255));
hbrUnlit = CreateSolidBrush(RGB(0,64,128));
hFont = CreateFont(DPIScale(64), 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, 0, 0, 0, 0, 0, L"Segoe UI");
// Position at the center of the primary monitor
POINT const ptZeroZero = {};
HMONITOR hMonitor = MonitorFromPoint(ptZeroZero, MONITOR_DEFAULTTOPRIMARY);
MONITORINFO mi = {sizeof(mi)};
GetMonitorInfo(hMonitor, &mi);
SIZE const size = { g_currentVolume.cSteps * DPIScale(10), DPIScale(60) };
POINT const pt =
{
mi.rcMonitor.left + (mi.rcMonitor.left + mi.rcMonitor.right - size.cx) / 2,
mi.rcMonitor.top + (mi.rcMonitor.top + mi.rcMonitor.bottom - size.cy) / 2
};
SetWindowPos(hWnd, HWND_TOPMOST, pt.x, pt.y, size.cx, size.cy, SWP_SHOWWINDOW);
break;
}
case WM_DESTROY:
{
DeleteObject(hbrLit);
DeleteObject(hbrUnlit);
DeleteObject(hFont);
PostQuitMessage(0);
return 0;
}
case WM_ERASEBKGND:
{
// Don't do any erasing here. It's done in WM_PAINT to avoid flicker.
return 1;
}
case WM_VOLUMECHANGE:
{
// get the new volume level
g_pVolumeMonitor->GetLevelInfo(&g_currentVolume);
// make window visible for 2 seconds
ShowWindow(hWnd, SW_SHOW);
InvalidateRect(hWnd, NULL, TRUE);
nTimerId = SetTimer(hWnd, 101, 2000, NULL);
return 0;
}
case WM_ENDPOINTCHANGE:
{
g_pVolumeMonitor->ChangeEndpoint();
return 0;
}
case WM_TIMER:
{
// make the window go away
ShowWindow(hWnd, SW_HIDE);
KillTimer(hWnd, nTimerId);
return 0;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HPAINTBUFFER hBufferedPaint = NULL;
RECT rc;
GetClientRect(hWnd, &rc);
HDC hdc = BeginPaint(hWnd, &ps);
if (g_bDblBuffered)
{
// Get doublebuffered DC
HDC hdcMem;
hBufferedPaint = BeginBufferedPaint(hdc, &rc, BPBF_COMPOSITED, NULL, &hdcMem);
if (hBufferedPaint)
{
hdc = hdcMem;
}
}
// black background (transparency color)
FillRect(hdc, &rc, (HBRUSH)GetStockObject(BLACK_BRUSH));
// Draw LEDs
for (UINT i = 0; i < (g_currentVolume.cSteps-1); i++)
{
RECT const rcLed = { DPIScale(i * 10), DPIScale(10), DPIScale(i * 10 + 8), rc.bottom-DPIScale(15) };
if ((i < g_currentVolume.nStep) && (!g_currentVolume.bMuted))
FillRect(hdc, &rcLed, hbrLit);
else
FillRect(hdc, &rcLed, hbrUnlit);
}
if (g_currentVolume.bMuted)
{
HGDIOBJ hof = SelectObject(hdc, hFont);
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, RGB(255, 64, 64));
RECT rcText = rc;
rcText.bottom -= DPIScale(11);
DrawText(hdc, L"MUTED", -1, &rcText, DT_CENTER | DT_SINGLELINE | DT_VCENTER);
SelectObject(hdc, hof);
}
if (hBufferedPaint)
{
// end painting
BufferedPaintMakeOpaque(hBufferedPaint, NULL);
EndBufferedPaint(hBufferedPaint, TRUE);
}
EndPaint(hWnd, &ps);
return 0;
}
}
return DefWindowProc(hWnd, message, wParam, lParam);
}