Files
soft/CristalDiskMark/source/CrystalDiskMark/Priscilla/ImageToast.cpp
T

270 lines
7.0 KiB
C++

/*---------------------------------------------------------------------------*/
// Author : hiyohiyo
// Mail : hiyohiyo@crystalmark.info
// Web : https://crystalmark.info/
// License : MIT License
/*---------------------------------------------------------------------------*/
#include "stdafx.h"
#include "ImageToast.h"
#ifndef CMK_MIN
#define CMK_MIN(a,b) (( (a) < (b) ) ? (a) : (b))
#endif
#ifndef CMK_MAX
#define CMK_MAX(a,b) (( (a) > (b) ) ? (a) : (b))
#endif
using namespace Gdiplus;
static const UINT IDT_CLOSE = 1;
static const UINT IDT_FADE = 2;
BEGIN_MESSAGE_MAP(CImageToast, CWnd)
ON_WM_CREATE()
ON_WM_TIMER()
ON_WM_LBUTTONDOWN()
ON_WM_RBUTTONDOWN()
ON_WM_ERASEBKGND()
ON_WM_KEYDOWN()
END_MESSAGE_MAP()
CImageToast::CImageToast() {}
CImageToast::~CImageToast() {
if (m_hDib) { ::DeleteObject(m_hDib); m_hDib = nullptr; }
}
int CImageToast::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CWnd::OnCreate(lpCreateStruct) == -1)
return -1;
return 0;
}
BOOL CImageToast::OnEraseBkgnd(CDC* /*pDC*/) { return TRUE; }
void CImageToast::OnLButtonDown(UINT, CPoint)
{
OpenUrlIfAny();
BeginClose(FALSE);
}
void CImageToast::OnRButtonDown(UINT, CPoint)
{
BeginClose(FALSE);
}
void CImageToast::OnKeyDown(UINT, UINT, UINT)
{
BeginClose(FALSE);
}
BOOL CImageToast::EnsureWindowCreated()
{
if (m_hWnd && ::IsWindow(m_hWnd)) return TRUE;
CString cls = AfxRegisterWndClass(0, ::LoadCursor(nullptr, IDC_HAND), 0, 0);
DWORD ex = WS_EX_TOPMOST | WS_EX_TOOLWINDOW | WS_EX_LAYERED;
DWORD st = WS_POPUP;
if (!CreateEx(ex, cls, _T(""), st, CRect(0,0,0,0), nullptr, 0))
return FALSE;
ShowWindow(SW_SHOWNOACTIVATE);
return TRUE;
}
static BOOL CreateARGBDIB(HDC hdc, int w, int h, HBITMAP& outBmp, void** outBits)
{
BITMAPINFO bi{};
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biWidth = w;
bi.bmiHeader.biHeight = -h; // top-down
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 32;
bi.bmiHeader.biCompression = BI_RGB;
void* bits = nullptr;
HBITMAP hbmp = CreateDIBSection(hdc, &bi, DIB_RGB_COLORS, &bits, nullptr, 0);
if (!hbmp || !bits) return FALSE;
outBmp = hbmp; *outBits = bits; return TRUE;
}
BOOL CImageToast::LoadPngToDIB(LPCWSTR path)
{
Gdiplus::Bitmap bmp(path);
if (bmp.GetLastStatus() != Gdiplus::Ok) return FALSE;
const int w = (int)bmp.GetWidth();
const int h = (int)bmp.GetHeight();
if (w <= 0 || h <= 0) return FALSE;
HDC hdcScreen = ::GetDC(nullptr);
HBITMAP hbmp = nullptr;
void* bits = nullptr;
if (!CreateARGBDIB(hdcScreen, w, h, hbmp, &bits)) {
::ReleaseDC(nullptr, hdcScreen);
return FALSE;
}
HDC hdcMem = ::CreateCompatibleDC(hdcScreen);
HGDIOBJ oldBmp = ::SelectObject(hdcMem, hbmp);
{
Gdiplus::Graphics g(hdcMem);
g.SetCompositingMode(Gdiplus::CompositingModeSourceCopy);
Gdiplus::SolidBrush clearBrush(Gdiplus::Color(0, 0, 0, 0));
g.FillRectangle(&clearBrush, 0, 0, w, h);
g.DrawImage(&bmp, 0, 0, w, h);
}
::SelectObject(hdcMem, oldBmp);
::DeleteDC(hdcMem);
::ReleaseDC(nullptr, hdcScreen);
if (m_hDib) ::DeleteObject(m_hDib);
m_hDib = hbmp;
m_bmpSize.cx = w; m_bmpSize.cy = h;
return TRUE;
}
void CImageToast::UpdateLayered()
{
if (!m_hWnd || !::IsWindow(m_hWnd) || !m_hDib) return;
HDC hdcScreen = ::GetDC(nullptr);
HDC hdcMem = ::CreateCompatibleDC(hdcScreen);
HBITMAP hOld = (HBITMAP)::SelectObject(hdcMem, m_hDib);
POINT ptSrc{ 0,0 };
SIZE sz{ m_bmpSize.cx, m_bmpSize.cy };
BLENDFUNCTION bf{};
bf.BlendOp = AC_SRC_OVER;
bf.SourceConstantAlpha = m_curAlpha;
bf.AlphaFormat = AC_SRC_ALPHA;
POINT cursor{}; ::GetCursorPos(&cursor);
HMONITOR mon = ::MonitorFromPoint(cursor, MONITOR_DEFAULTTONEAREST);
MONITORINFO mi{ sizeof(mi) }; ::GetMonitorInfo(mon, &mi);
RECT wa = mi.rcWork;
POINT ptDst{};
ptDst.x = wa.right - sz.cx - m_margin;
ptDst.y = wa.bottom - sz.cy - m_margin;
::UpdateLayeredWindow(m_hWnd, hdcScreen, &ptDst, &sz, hdcMem, &ptSrc, 0, &bf, ULW_ALPHA);
::SelectObject(hdcMem, hOld);
::DeleteDC(hdcMem);
::ReleaseDC(nullptr, hdcScreen);
}
void CImageToast::StartFadeTimer(BOOL fadeIn)
{
if (!m_enableFade) return;
m_closing = !fadeIn;
SetTimer(IDT_FADE, 16, nullptr);
}
void CImageToast::BeginClose(BOOL force)
{
if (!m_hWnd) return;
if (force || !m_enableFade) {
KillTimer(IDT_FADE);
KillTimer(IDT_CLOSE);
DestroyWindow();
return;
}
if (!m_closing) {
m_closing = TRUE;
SetTimer(IDT_FADE, 16, nullptr);
}
}
void CImageToast::OnTimer(UINT_PTR id)
{
if (id == IDT_CLOSE) {
KillTimer(IDT_CLOSE);
BeginClose(FALSE);
return;
}
if (id == IDT_FADE) {
int stepIn = CMK_MAX(1, (int)m_maxAlpha * 16 / CMK_MAX(1, m_fadeInMs));
int stepOut = CMK_MAX(1, (int)m_maxAlpha * 16 / CMK_MAX(1, m_fadeOutMs));
if (!m_closing) {
if (m_curAlpha < m_maxAlpha) {
m_curAlpha = (BYTE)CMK_MIN((int)m_maxAlpha, (int)m_curAlpha + stepIn);
UpdateLayered();
}
else {
KillTimer(IDT_FADE);
}
}
else {
if (m_curAlpha > 0) {
m_curAlpha = (BYTE)CMK_MAX(0, (int)m_curAlpha - stepOut);
UpdateLayered();
}
else {
KillTimer(IDT_FADE);
DestroyWindow();
}
}
return;
}
CWnd::OnTimer(id);
}
BOOL CImageToast::Show(LPCWSTR pngPath, UINT showMillis, BOOL enableFade, BYTE maxAlpha,
int margin, int fadeInMs, int fadeOutMs, LPCWSTR urlToOpen)
{
m_pngPath = pngPath;
m_showMillis = showMillis;
m_enableFade = enableFade;
m_maxAlpha = maxAlpha;
m_margin = margin;
m_fadeInMs = fadeInMs;
m_fadeOutMs = fadeOutMs;
m_curAlpha = enableFade ? 0 : maxAlpha;
m_closing = FALSE;
m_opened = FALSE;
m_url = (urlToOpen ? urlToOpen : L"");
if (!EnsureWindowCreated()) return FALSE;
if (!LoadPngToDIB(m_pngPath)) {
BeginClose(TRUE);
return FALSE;
}
UpdateLayered();
ShowWindow(SW_SHOWNOACTIVATE);
SetTimer(IDT_CLOSE, m_showMillis, nullptr);
if (m_enableFade) StartFadeTimer(TRUE);
return TRUE;
}
void CImageToast::SetLink(LPCWSTR urlToOpen)
{
m_url = (urlToOpen ? urlToOpen : L"");
}
void CImageToast::CloseNow()
{
BeginClose(FALSE);
}
void CImageToast::OpenUrlIfAny()
{
if (m_opened) return;
if (m_url.IsEmpty()) return;
m_opened = TRUE;
::ShellExecuteW(nullptr, _T("open"), m_url, nullptr, nullptr, SW_SHOWNORMAL);
}