Show just logo in Dwm Preview when passcoded.

This commit is contained in:
John Preston 2023-02-02 13:36:46 +04:00
parent c7b3c95dc6
commit 88f3aeca5b
2 changed files with 234 additions and 3 deletions

View File

@ -47,7 +47,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <Windowsx.h>
#include <VersionHelpers.h>
// Taken from qtbase/src/gui/image/qpixmap_win.cpp
HICON qt_pixmapToWinHICON(const QPixmap &);
HBITMAP qt_imageToWinHBITMAP(const QImage &, int hbitmapFormat);
namespace ViewManagement = ABI::Windows::UI::ViewManagement;
@ -61,6 +63,15 @@ namespace {
// icon click (both left or right button) was made from the active app.
constexpr auto kKeepActiveForTrayIcon = crl::time(500);
using namespace Microsoft::WRL;
// Taken from qtbase/src/gui/image/qpixmap_win.cpp
enum HBitmapFormat {
HBitmapNoAlpha,
HBitmapPremultipliedAlpha,
HBitmapAlpha
};
class EventFilter final : public QAbstractNativeEventFilter {
public:
explicit EventFilter(not_null<MainWindow*> window);
@ -82,9 +93,6 @@ private:
};
using namespace Microsoft::WRL;
bool handleSessionNotification = false;
[[nodiscard]] HICON NativeIcon(const QIcon &icon, QSize size) {
if (!icon.isNull()) {
@ -96,6 +104,67 @@ bool handleSessionNotification = false;
return nullptr;
}
struct RealSize {
QSize value;
bool maximized = false;
};
[[nodiscard]] RealSize DetectRealSize(HWND hwnd) {
auto result = RECT();
auto placement = WINDOWPLACEMENT();
if (!GetWindowPlacement(hwnd, &placement)) {
return {};
} else if (placement.flags & WPF_RESTORETOMAXIMIZED) {
const auto monitor = MonitorFromRect(
&placement.rcNormalPosition,
MONITOR_DEFAULTTONULL);
if (!monitor) {
return {};
}
auto info = MONITORINFO{ .cbSize = sizeof(MONITORINFO) };
if (!GetMonitorInfo(monitor, &info)) {
return {};
}
result = info.rcWork;
} else {
CopyRect(&result, &placement.rcNormalPosition);
}
return {
{ int(result.right - result.left), int(result.bottom - result.top) },
((placement.flags & WPF_RESTORETOMAXIMIZED) != 0)
};
}
[[nodiscard]] QImage PrepareLogoPreview(
QSize size,
QImage::Format format,
int radius = 0) {
auto result = QImage(size, QImage::Format_RGB32);
result.fill(st::windowBg->c);
const auto logo = Window::Logo();
const auto width = size.width();
const auto height = size.height();
const auto side = logo.width();
const auto skip = width / 8;
const auto use = std::min({ width - skip, height - skip, side });
auto p = QPainter(&result);
if (use == side) {
p.drawImage((width - side) / 2, (height - side) / 2, logo);
} else {
const auto scaled = logo.scaled(
use,
use,
Qt::KeepAspectRatio,
Qt::SmoothTransformation);
p.drawImage((width - use) / 2, (height - use) / 2, scaled);
}
p.end();
return radius
? Images::Round(std::move(result), Images::CornersMask(radius))
: result;
}
EventFilter::EventFilter(not_null<MainWindow*> window) : _window(window) {
}
@ -150,6 +219,23 @@ bool EventFilter::mainWindowEvent(
_window->positionUpdated();
} return false;
case WM_DWMSENDICONICTHUMBNAIL: {
if (!Core::App().passcodeLocked()) {
return false;
}
const auto size = QSize(int(HIWORD(lParam)), int(LOWORD(lParam)));
return _window->setDwmThumbnail(size);
}
case WM_DWMSENDICONICLIVEPREVIEWBITMAP: {
if (!Core::App().passcodeLocked()) {
return false;
}
const auto size = DetectRealSize(hWnd);
const auto radius = size.maximized ? 0 : style::ConvertScale(8);
return _window->setDwmPreview(size.value, radius);
}
}
return false;
}
@ -164,6 +250,46 @@ struct MainWindow::Private {
ComPtr<ViewManagement::IUIViewSettings> viewSettings;
};
MainWindow::BitmapPointer::BitmapPointer(HBITMAP value) : _value(value) {
}
MainWindow::BitmapPointer::BitmapPointer(BitmapPointer &&other)
: _value(base::take(other._value)) {
}
MainWindow::BitmapPointer &MainWindow::BitmapPointer::operator=(
BitmapPointer &&other) {
if (_value != other._value) {
reset();
_value = base::take(other._value);
}
return *this;
}
MainWindow::BitmapPointer::~BitmapPointer() {
reset();
}
HBITMAP MainWindow::BitmapPointer::get() const {
return _value;
}
MainWindow::BitmapPointer::operator bool() const {
return _value != nullptr;
}
void MainWindow::BitmapPointer::release() {
_value = nullptr;
}
void MainWindow::BitmapPointer::reset(HBITMAP value) {
if (_value != value) {
if (const auto old = std::exchange(_value, value)) {
DeleteObject(old);
}
}
}
MainWindow::MainWindow(not_null<Window::Controller*> controller)
: Window::MainWindow(controller)
, _private(std::make_unique<Private>(this))
@ -180,6 +306,27 @@ MainWindow::MainWindow(not_null<Window::Controller*> controller)
) | rpl::filter(_1) | rpl::start_with_next([=] {
_lastDeactivateTime = crl::now();
}, lifetime());
setupPreviewPasscodeLock();
}
void MainWindow::setupPreviewPasscodeLock() {
Core::App().passcodeLockValue(
) | rpl::start_with_next([=](bool locked) {
// Use iconic bitmap instead of the window content if passcoded.
BOOL fForceIconic = locked ? TRUE : FALSE;
BOOL fHasIconicBitmap = fForceIconic;
DwmSetWindowAttribute(
_hWnd,
DWMWA_FORCE_ICONIC_REPRESENTATION,
&fForceIconic,
sizeof(fForceIconic));
DwmSetWindowAttribute(
_hWnd,
DWMWA_HAS_ICONIC_BITMAP,
&fHasIconicBitmap,
sizeof(fHasIconicBitmap));
}, lifetime());
}
void MainWindow::setupNativeWindowFrame() {
@ -211,6 +358,60 @@ void MainWindow::destroyedFromSystem() {
}
}
bool MainWindow::setDwmThumbnail(QSize size) {
validateDwmPreviewColors();
if (size.isEmpty()) {
return false;
} else if (!_dwmThumbnail || _dwmThumbnailSize != size) {
const auto result = PrepareLogoPreview(size, QImage::Format_RGB32);
const auto bitmap = qt_imageToWinHBITMAP(result, HBitmapNoAlpha);
if (!bitmap) {
return false;
}
_dwmThumbnail.reset(bitmap);
_dwmThumbnailSize = size;
}
DwmSetIconicThumbnail(_hWnd, _dwmThumbnail.get(), NULL);
return true;
}
bool MainWindow::setDwmPreview(QSize size, int radius) {
Expects(radius >= 0);
validateDwmPreviewColors();
if (size.isEmpty()) {
return false;
} else if (!_dwmPreview
|| _dwmPreviewSize != size
|| _dwmPreviewRadius != radius) {
const auto format = (radius > 0)
? QImage::Format_ARGB32_Premultiplied
: QImage::Format_RGB32;
const auto result = PrepareLogoPreview(size, format, radius);
const auto bitmap = qt_imageToWinHBITMAP(
result,
(radius > 0) ? HBitmapPremultipliedAlpha : HBitmapNoAlpha);
if (!bitmap) {
return false;
}
_dwmPreview.reset(bitmap);
_dwmPreviewRadius = radius;
_dwmPreviewSize = size;
}
const auto flags = 0;
DwmSetIconicLivePreviewBitmap(_hWnd, _dwmPreview.get(), NULL, flags);
return true;
}
void MainWindow::validateDwmPreviewColors() {
if (_dwmBackground == st::windowBg->c) {
return;
}
_dwmBackground = st::windowBg->c;
_dwmThumbnail.reset();
_dwmPreview.reset();
}
int32 MainWindow::screenNameChecksum(const QString &name) const {
constexpr int DeviceNameSize = base::array_size(MONITORINFOEX().szDevice);
wchar_t buffer[DeviceNameSize] = { 0 };

View File

@ -34,6 +34,9 @@ public:
void destroyedFromSystem();
bool setDwmThumbnail(QSize size);
bool setDwmPreview(QSize size, int radius);
~MainWindow();
protected:
@ -50,12 +53,32 @@ protected:
private:
struct Private;
class BitmapPointer {
public:
BitmapPointer(HBITMAP value = nullptr);
BitmapPointer(BitmapPointer &&other);
BitmapPointer& operator=(BitmapPointer &&other);
~BitmapPointer();
[[nodiscard]] HBITMAP get() const;
[[nodiscard]] explicit operator bool() const;
void release();
void reset(HBITMAP value = nullptr);
private:
HBITMAP _value = nullptr;
};
void setupNativeWindowFrame();
void setupPreviewPasscodeLock();
void updateTaskbarAndIconCounters();
void validateWindowTheme(bool native, bool night);
void forceIconRefresh();
void destroyCachedIcons();
void validateDwmPreviewColors();
const std::unique_ptr<Private> _private;
const std::unique_ptr<QWindow> _taskbarHiderWindow;
@ -65,6 +88,13 @@ private:
HICON _iconSmall = nullptr;
HICON _iconOverlay = nullptr;
BitmapPointer _dwmThumbnail;
BitmapPointer _dwmPreview;
QSize _dwmThumbnailSize;
QSize _dwmPreviewSize;
QColor _dwmBackground;
int _dwmPreviewRadius = 0;
// Workarounds for activation from tray icon.
crl::time _lastDeactivateTime = 0;