Add notification support

This commit is contained in:
Noraa Junker
2026-02-12 18:09:55 +01:00
parent fc1195dc9c
commit b9755dc0c2
11 changed files with 149 additions and 14 deletions

View File

@@ -10,6 +10,7 @@ using System.Globalization;
using System.IO;
using System.IO.Pipelines;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Threading;
@@ -125,7 +126,9 @@ namespace RunnerV2.Helpers
break;
case "check_for_updates":
UpdateSettingsHelper.TriggerUpdateCheck();
var version = Assembly.GetExecutingAssembly().GetName().Version!;
var versionString = "v" + version.Major + "." + version.Minor + "." + version.Build;
UpdateSettingsHelper.TriggerUpdateCheck((version) => { PowerToys.Interop.Notifications.ShowUpdateAvailableNotification("PowerToys", "An update to PowerToys is available.\n" + versionString + " \u2192 " + version, "PTUpdateNotifyTag", "Update Now", "More info..."); });
break;
case "request_update_state_date":
JsonObject response = [];

View File

@@ -52,8 +52,17 @@ internal sealed class Program
case SpecialMode.CouldntToggleFileExplorerModulesNotification:
Environment.Exit(NotificationHelper.DisableToast(NotificationHelper.ToastType.CouldntToggleFileExplorerModules) ? 1 : 0);
return;
case SpecialMode.Win32ToastNotificationCOMServer:
PowerToys.Interop.Notifications.RunDesktopAppActivatorLoop();
return;
case SpecialMode.ReportSuccessfulUpdate:
PowerToys.Interop.Notifications.RemoveToastsByTag("PTUpdateNotifyTag");
PowerToys.Interop.Notifications.RemoveAllScheduledToasts();
PowerToys.Interop.Notifications.ShowToastWithActivation("PowerToys was updated successfully", "PowerToys", "PTUpdateNotifyTag");
return;
default:
throw new NotImplementedException("Special modes are not implemented yet.");
Logger.LogError("Unexpected special mode detected");
return;
}
// If PowerToys restarted the old process may still be around
@@ -159,6 +168,16 @@ internal sealed class Program
/// <returns>The <see cref="SpecialMode"/> the app should run in.</returns>
private static SpecialMode ShouldRunInSpecialMode(string[] args)
{
if (args.Contains("-ToastActivated"))
{
return SpecialMode.Win32ToastNotificationCOMServer;
}
if (args.Contains("-report_update_success"))
{
return SpecialMode.ReportSuccessfulUpdate;
}
if (args.Length > 0 && args[0].StartsWith("powertoys://", StringComparison.InvariantCultureIgnoreCase))
{
Uri uri = new(args[0]);

View File

@@ -150,7 +150,14 @@ namespace RunnerV2
continue;
}
HandleMessage(msg.HWnd, msg.Message, (nint)msg.WParam, (nint)msg.LParam);
try
{
HandleMessage(msg.HWnd, msg.Message, (nint)msg.WParam, (nint)msg.LParam);
}
catch (Exception e)
{
Logger.LogError("Uncaught error in message handling: ", e);
}
}
Close();

View File

@@ -76,7 +76,7 @@ internal sealed partial class Program
private static async Task PerformUpdateNowStage1()
{
UpdateSettingsHelper.TriggerUpdateCheck();
UpdateSettingsHelper.TriggerUpdateCheck((_) => { });
UpdateSettingsHelper.UpdateInfo updateInfo = await UpdateSettingsHelper.GetUpdateAvailableInfo();
if (updateInfo is not UpdateSettingsHelper.UpdateInfo.UpdateAvailable ua)

View File

@@ -24,7 +24,7 @@ namespace Update
private const string INSTALLERFILENAME = "powertoyssetup";
private const string USERINSTALLERFILENAME = "powertoysusersetup";
public static void TriggerUpdateCheck()
public static void TriggerUpdateCheck(Action<string> doIfUpdateAvailable)
{
if (_updateThread is not null && _updateThread.IsAlive)
{
@@ -41,6 +41,7 @@ namespace Update
break;
case UpdateInfo.UpdateAvailable ua:
ProcessUpdateAvailable(ua);
doIfUpdateAvailable($"v{ua.AvailableVersion.Major}.{ua.AvailableVersion.Minor}.{ua.AvailableVersion.Build}");
break;
case UpdateInfo.NoUpdateAvailable:
ProcessNoUpdateAvailable();
@@ -86,11 +87,11 @@ namespace Update
return new UpdateInfo.NoUpdateAvailable();
}
if (currentVersion is { Major: 0, Minor: 0 })
/*if (currentVersion is { Major: 0, Minor: 0 })
{
// Pre-release or local build, skip update check
return new UpdateInfo.NoUpdateAvailable();
}
}*/
try
{

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Version>0.0.1</Version>
<Version>0.1.1</Version>
<DevEnvironment>Local</DevEnvironment>
<!-- Forcing for every DLL on by default -->

View File

@@ -186,14 +186,20 @@ namespace ManagedCommon
private static void Log(string message, string type, string memberName, string sourceFilePath, int sourceLineNumber)
{
Trace.WriteLine("[" + DateTime.Now.TimeOfDay + "] [" + type + "] " + GetCallerInfo(memberName, sourceFilePath, sourceLineNumber));
Trace.Indent();
if (message != string.Empty)
try
{
Trace.WriteLine(message);
}
Trace.WriteLine("[" + DateTime.Now.TimeOfDay + "] [" + type + "] " + GetCallerInfo(memberName, sourceFilePath, sourceLineNumber));
Trace.Indent();
if (message != string.Empty)
{
Trace.WriteLine(message);
}
Trace.Unindent();
Trace.Unindent();
}
catch
{
}
}
private static string GetCallerInfo(string memberName, string sourceFilePath, int sourceLineNumber)

View File

@@ -0,0 +1,50 @@
#include "pch.h"
#include "Notifications.h"
#include "Notifications.g.cpp"
#include "../notifications/notifications.h"
namespace winrt::PowerToys::Interop::implementation
{
using namespace winrt::Windows::Foundation;
void Notifications::ShowToast(hstring const& title, hstring const& content)
{
notifications::show_toast(title.c_str(), content.c_str());
}
void Notifications::RemoveToastsByTag(hstring const& tag)
{
notifications::remove_toasts_by_tag(tag.c_str());
}
void Notifications::RemoveAllScheduledToasts()
{
notifications::remove_all_scheduled_toasts();
}
void Notifications::ShowToastWithActivation(hstring const& title, hstring const& content, hstring const& tag)
{
notifications::toast_params params;
params.tag = tag.c_str();
notifications::show_toast(title.c_str(), content.c_str(), params);
}
void Notifications::ShowUpdateAvailableNotification(hstring const& title, hstring const& content, hstring const& tag, hstring const& updateNowString, hstring const& openOverviewString) {
notifications::toast_params params;
params.tag = tag.c_str();
notifications::show_toast_with_activations(std::move(content.c_str()),
title.c_str(),
{},
{ notifications::link_button{ updateNowString.c_str(),
L"powertoys://update_now/" },
notifications::link_button{ openOverviewString.c_str(),
L"powertoys://open_overview/" } },
std::move(params),
L"powertoys://open_overview/");
}
void Notifications::RunDesktopAppActivatorLoop()
{
notifications::run_desktop_app_activator_loop();
}
}

View File

@@ -0,0 +1,26 @@
#pragma once
#include "Notifications.g.h"
#include "../notifications/notifications.h"
namespace winrt::PowerToys::Interop::implementation
{
using namespace winrt::Windows::Foundation;
struct Notifications : NotificationsT<Notifications>
{
static void ShowToast(hstring const& title, hstring const& content);
static void RemoveToastsByTag(hstring const& tag);
static void RemoveAllScheduledToasts();
static void ShowToastWithActivation(hstring const& title, hstring const& content, hstring const& tag);
static void RunDesktopAppActivatorLoop();
static void ShowUpdateAvailableNotification(hstring const& title, hstring const& content, hstring const& tag, hstring const& updateNowString, hstring const& openOverviewString);
};
}
namespace winrt::PowerToys::Interop::factory_implementation
{
struct Notifications : NotificationsT<Notifications, implementation::Notifications>
{
};
}

View File

@@ -0,0 +1,17 @@
namespace PowerToys
{
namespace Interop
{
[default_interface] runtimeclass Notifications
{
static void ShowToast(String title, String content);
static void RemoveToastsByTag(String tag);
static void RemoveAllScheduledToasts();
static void ShowToastWithActivation(String title, String content, String tag);
static void RunDesktopAppActivatorLoop();
static void ShowUpdateAvailableNotification(String title, String content, String tag, String updateNowString, String openOverviewString);
}
}
}

View File

@@ -113,6 +113,7 @@
<ClInclude Include="LayoutMapManaged.h">
<DependentUpon>LayoutMapManaged.idl</DependentUpon>
</ClInclude>
<ClInclude Include="Notifications.h" />
<ClInclude Include="Package.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="resource.h" />
@@ -144,6 +145,7 @@
<ClCompile Include="LayoutMapManaged.cpp">
<DependentUpon>LayoutMapManaged.idl</DependentUpon>
</ClCompile>
<ClCompile Include="Notifications.cpp" />
<ClCompile Include="Package.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
@@ -176,6 +178,7 @@
<Midl Include="HotkeyManager.idl" />
<Midl Include="KeyboardHook.idl" />
<Midl Include="LayoutMapManaged.idl" />
<Midl Include="Notifications.idl" />
<Midl Include="Package.idl" />
<Midl Include="ThemeHelper.idl" />
<Midl Include="TwoWayPipeMessageIPCManaged.idl" />
@@ -184,6 +187,9 @@
<ProjectReference Include="..\logger\logger.vcxproj">
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
</ProjectReference>
<ProjectReference Include="..\notifications\notifications.vcxproj">
<Project>{1d5be09d-78c0-4fd7-af00-ae7c1af7c525}</Project>
</ProjectReference>
</ItemGroup>
<ImportGroup Label="ExtensionTargets" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />