Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

## Unreleased

- The Windows tray icon (`rivet::system::TrayIcon`) can update its tooltip
(`SetTooltip`) and re-register itself after an Explorer restart
(`Readd`, called from the host's `TaskbarCreated` broadcast handler) —
state-aware tray apps no longer re-allocate the icon to relabel it and no
longer lose it the first time the shell restarts. `TrayIcon` owns an icon
copy so recovery does not impose a new lifetime requirement on callers.

- Bound and retry Ubuntu runner package-manager operations so a stalled mirror
cannot consume an entire desktop validation job without a useful failure.
- Removed the misleading `rivet-type` runtime descriptors from the pre-1.0
Expand Down
50 changes: 43 additions & 7 deletions platform/windows/system/system_services.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,24 +123,60 @@ void Autostart::SetEnabled(std::wstring const& application_id,

TrayIcon::TrayIcon(HWND window, UINT identifier, UINT callback_message,
std::wstring const& tooltip, HICON icon)
: window_(window), identifier_(identifier) {
: window_(window),
identifier_(identifier),
callback_message_(callback_message),
tooltip_(tooltip) {
icon_ = ::CopyIcon(icon);
if (icon_ == nullptr) ThrowLastError("CopyIcon");
try {
Add();
} catch (...) {
::DestroyIcon(icon_);
icon_ = nullptr;
throw;
}
}

TrayIcon::~TrayIcon() {
NOTIFYICONDATAW data{};
data.cbSize = sizeof(data);
data.hWnd = window_;
data.uID = identifier_;
::Shell_NotifyIconW(NIM_DELETE, &data);
if (icon_ != nullptr) ::DestroyIcon(icon_);
}

void TrayIcon::Add() {
NOTIFYICONDATAW data{};
data.cbSize = sizeof(data);
data.hWnd = window;
data.uID = identifier;
data.hWnd = window_;
data.uID = identifier_;
data.uFlags = NIF_MESSAGE | NIF_TIP | NIF_ICON;
data.uCallbackMessage = callback_message;
data.hIcon = icon;
wcsncpy_s(data.szTip, tooltip.c_str(), _TRUNCATE);
data.uCallbackMessage = callback_message_;
data.hIcon = icon_;
wcsncpy_s(data.szTip, tooltip_.c_str(), _TRUNCATE);
if (!::Shell_NotifyIconW(NIM_ADD, &data)) ThrowLastError("Shell_NotifyIconW");
}

TrayIcon::~TrayIcon() {
void TrayIcon::SetTooltip(std::wstring const& tooltip) {
tooltip_ = tooltip;
NOTIFYICONDATAW data{};
data.cbSize = sizeof(data);
data.hWnd = window_;
data.uID = identifier_;
data.uFlags = NIF_TIP;
wcsncpy_s(data.szTip, tooltip_.c_str(), _TRUNCATE);
if (!::Shell_NotifyIconW(NIM_MODIFY, &data)) ThrowLastError("Shell_NotifyIconW");
}

void TrayIcon::Readd() {
NOTIFYICONDATAW data{};
data.cbSize = sizeof(data);
data.hWnd = window_;
data.uID = identifier_;
::Shell_NotifyIconW(NIM_DELETE, &data);
Add();
}

void TrayIcon::Notify(std::wstring const& title,
Expand Down
18 changes: 17 additions & 1 deletion platform/windows/system/system_services.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@ class Autostart final {
};

// Uses the first-party Shell_NotifyIcon surface. The owning HWND receives
// callback_message for tray activation and must stay on the WinUI UI thread.
// callback_message for tray activation and must stay on the WinUI UI thread;
// the application intercepts callback_message (SetWindowSubclass) and also
// forwards the TaskbarCreated broadcast to Readd() so the icon survives an
// Explorer restart.
class TrayIcon final {
public:
TrayIcon(HWND window, UINT identifier, UINT callback_message,
Expand All @@ -67,11 +70,24 @@ class TrayIcon final {
TrayIcon(TrayIcon const&) = delete;
TrayIcon& operator=(TrayIcon const&) = delete;

// The constructor copies `icon`; the caller retains ownership of its input
// handle and may release it immediately after construction.
// State labels change with the application (running/paused, today's
// totals); NIM_MODIFY with NIF_TIP, no re-add needed.
void SetTooltip(std::wstring const& tooltip);
// TaskbarCreated handler: NIM_DELETE + NIM_ADD with the stored payload.
// Cheap enough to call unconditionally from the broadcast path.
void Readd();
void Notify(std::wstring const& title, std::wstring const& body) const;

private:
void Add();

HWND window_{};
UINT identifier_{};
UINT callback_message_{};
std::wstring tooltip_;
HICON icon_{};
};

using CrashCallback = void (*)(EXCEPTION_POINTERS const*) noexcept;
Expand Down
Loading