From c077cc46f18e95cb14652bd88ba1729afee7674a Mon Sep 17 00:00:00 2001
From: Jaylyn Barbee <51131738+Jaylyn-Barbee@users.noreply.github.com>
Date: Fri, 12 Dec 2025 10:21:47 -0500
Subject: [PATCH] Adding telemetry events for Light Switch
---
DATA_AND_PRIVACY.md | 24 +++++++++
LightSwitchActionsUpdated.cs | 36 +++++++++++++
LightSwitchModeUpdated.cs | 30 +++++++++++
LightSwitchScheduleToggled.cs | 30 +++++++++++
LightSwitchShortcutInvoked.cs | 36 +++++++++++++
.../LightSwitchModuleInterface/dllmain.cpp | 6 +++
.../LightSwitchSettings.cpp | 12 +++++
.../LightSwitch/LightSwitchService/trace.cpp | 54 +++++++++++++++++++
.../LightSwitch/LightSwitchService/trace.h | 17 ++++++
9 files changed, 245 insertions(+)
create mode 100644 LightSwitchActionsUpdated.cs
create mode 100644 LightSwitchModeUpdated.cs
create mode 100644 LightSwitchScheduleToggled.cs
create mode 100644 LightSwitchShortcutInvoked.cs
create mode 100644 src/modules/LightSwitch/LightSwitchService/trace.cpp
create mode 100644 src/modules/LightSwitch/LightSwitchService/trace.h
diff --git a/DATA_AND_PRIVACY.md b/DATA_AND_PRIVACY.md
index 8aba94f12f..7c310e03f0 100644
--- a/DATA_AND_PRIVACY.md
+++ b/DATA_AND_PRIVACY.md
@@ -681,6 +681,30 @@ _If you want to find diagnostic data events in the source code, these two links
+### Light Switch
+
+
+ | Event Name |
+ Description |
+
+
+ | Microsoft.PowerToys.LightSwitch_EnableLightSwitch |
+ Triggered when Light Switch is enabled or disabled. |
+
+
+ | Microsoft.PowerToys.LightSwitch_ShortcutInvoked |
+ Occurs when the shortcut for Light Switch is invoked. |
+
+
+ | Microsoft.PowerToys.LightSwitch_ScheduleModeToggled |
+ Occurs when a new schedule mode is selected for Light Switch. |
+
+
+ | Microsoft.PowerToys.LightSwitch_ThemeTargetChanged |
+ Occurs when a the options for targeting the system or apps is updated. |
+
+
+
### Mouse Highlighter
diff --git a/LightSwitchActionsUpdated.cs b/LightSwitchActionsUpdated.cs
new file mode 100644
index 0000000000..85b4e83546
--- /dev/null
+++ b/LightSwitchActionsUpdated.cs
@@ -0,0 +1,36 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Tracing;
+using Microsoft.PowerToys.Telemetry;
+using Microsoft.PowerToys.Telemetry.Events;
+
+///
+/// Tracks which actions the users are using to apply theme changes
+/// Purpose: Identify which parts of the OS it is important for users to control.
+///
+[EventData]
+[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
+public class LightSwitchActionsUpdated : EventBase, IEvent
+{
+ ///
+ /// Gets or sets whether the user is changing their app theme.
+ ///
+ public bool ChangeApps { get; set; }
+
+ ///
+ /// Gets or sets whether the user is changing their system theme.
+ ///
+ public bool ChangeSystem { get; set; }
+
+ public LightSwitchActionsUpdated(bool, ChangeApps, bool ChangeSystem)
+ {
+ EventName = "LightSwitch_ModeUpdated";
+ ChangeApps = changeApps;
+ ChangeSystem = changeSystem
+ }
+
+ public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
+}
diff --git a/LightSwitchModeUpdated.cs b/LightSwitchModeUpdated.cs
new file mode 100644
index 0000000000..2cd19a304c
--- /dev/null
+++ b/LightSwitchModeUpdated.cs
@@ -0,0 +1,30 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Tracing;
+using Microsoft.PowerToys.Telemetry;
+using Microsoft.PowerToys.Telemetry.Events;
+
+///
+/// Tracks which mode users are using to schedule theme changes
+/// Purpose: Identify which modes users are using the most.
+///
+[EventData]
+[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
+public class LightSwitchModeUpdated : EventBase, IEvent
+{
+ ///
+ /// Gets or sets the mode the user is using.
+ ///
+ public string Mode { get; set; }
+
+ public LightSwitchModeUpdated(bool mode)
+ {
+ EventName = "LightSwitch_ModeUpdated";
+ Mode = mode;
+ }
+
+ public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
+}
diff --git a/LightSwitchScheduleToggled.cs b/LightSwitchScheduleToggled.cs
new file mode 100644
index 0000000000..42531224f3
--- /dev/null
+++ b/LightSwitchScheduleToggled.cs
@@ -0,0 +1,30 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Tracing;
+using Microsoft.PowerToys.Telemetry;
+using Microsoft.PowerToys.Telemetry.Events;
+
+///
+/// Tracks if users are using the schedule to control their themeing.
+/// Purpose: Identify how users are using Light Switch to control their theme.
+///
+[EventData]
+[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
+public class LightSwitchScheduleToggled : EventBase, IEvent
+{
+ ///
+ /// Gets or sets whether the schedule is on or off.
+ ///
+ public bool OnOrOff { get; set; }
+
+ public LightSwitchScheduleToggled(bool onOrOff)
+ {
+ EventName = "LightSwitch_ScheduleToggled";
+ OnOrOff = onOrOff;
+ }
+
+ public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
+}
diff --git a/LightSwitchShortcutInvoked.cs b/LightSwitchShortcutInvoked.cs
new file mode 100644
index 0000000000..221984d8cf
--- /dev/null
+++ b/LightSwitchShortcutInvoked.cs
@@ -0,0 +1,36 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Tracing;
+using Microsoft.PowerToys.Telemetry;
+using Microsoft.PowerToys.Telemetry.Events;
+
+///
+/// Tracks shortcut usage and which mode are we toggling from and to.
+/// Purpose: Identify how users are using the shortcut in their workflows.
+///
+[EventData]
+[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
+public class LightSwitchShortcutInvoked : EventBase, IEvent
+{
+ ///
+ /// Gets or sets the target the mode the user is switching to
+ ///
+ public string TargetMode { get; set; }
+
+ ///
+ /// Gets or sets the time of day the user is using this command
+ ///
+ public int MinuteOfDay { get; set; }
+
+ public LightSwitchShortcutInvoked(string targetMode, int minuteOfDay)
+ {
+ EventName = "LightSwitch_ShortcutInvoked";
+ TargetMode = targetMode;
+ MinuteOfDay = minuteOfDay;
+ }
+
+ public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
+}
diff --git a/src/modules/LightSwitch/LightSwitchModuleInterface/dllmain.cpp b/src/modules/LightSwitch/LightSwitchModuleInterface/dllmain.cpp
index a5973a396f..74caa5d0c9 100644
--- a/src/modules/LightSwitch/LightSwitchModuleInterface/dllmain.cpp
+++ b/src/modules/LightSwitch/LightSwitchModuleInterface/dllmain.cpp
@@ -8,6 +8,7 @@
#include
#include
#include "ThemeHelper.h"
+#include
extern "C" IMAGE_DOS_HEADER __ImageBase;
@@ -394,6 +395,7 @@ public:
{
m_enabled = true;
Logger::info(L"Enabling Light Switch module...");
+ Trace::LightSwitch::Enable(true);
unsigned long powertoys_pid = GetCurrentProcessId();
std::wstring args = L"--pid " + std::to_wstring(powertoys_pid);
@@ -469,6 +471,7 @@ public:
CloseHandle(m_process);
m_process = nullptr;
}
+ Trace::LightSwitch::Enable(false);
}
// Returns if the powertoys is enabled
@@ -524,6 +527,8 @@ public:
if (m_enabled)
{
Logger::trace(L"Light Switch hotkey pressed");
+ Trace::LightSwitch::ShortcutInvoked();
+
if (!is_process_running())
{
enable();
@@ -541,6 +546,7 @@ public:
SetAppsTheme(!GetCurrentAppsTheme());
}
+
if (!m_manual_override_event_handle)
{
m_manual_override_event_handle = OpenEventW(SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, L"POWERTOYS_LIGHTSWITCH_MANUAL_OVERRIDE");
diff --git a/src/modules/LightSwitch/LightSwitchService/LightSwitchSettings.cpp b/src/modules/LightSwitch/LightSwitchService/LightSwitchSettings.cpp
index 5221a197fe..488142b95b 100644
--- a/src/modules/LightSwitch/LightSwitchService/LightSwitchSettings.cpp
+++ b/src/modules/LightSwitch/LightSwitchService/LightSwitchSettings.cpp
@@ -5,6 +5,7 @@
#include
#include
#include
+#include
using namespace std;
@@ -151,6 +152,7 @@ void LightSwitchSettings::LoadSettings()
if (m_settings.scheduleMode != newMode)
{
m_settings.scheduleMode = newMode;
+ Trace::LightSwitch::ScheduleModeToggled(val);
NotifyObservers(SettingId::ScheduleMode);
}
}
@@ -220,6 +222,8 @@ void LightSwitchSettings::LoadSettings()
}
}
+ bool themeTargetChanged = false;
+
// ChangeSystem
if (const auto jsonVal = values.get_bool_value(L"changeSystem"))
{
@@ -227,6 +231,7 @@ void LightSwitchSettings::LoadSettings()
if (m_settings.changeSystem != val)
{
m_settings.changeSystem = val;
+ themeTargetChanged = true;
NotifyObservers(SettingId::ChangeSystem);
}
}
@@ -238,9 +243,16 @@ void LightSwitchSettings::LoadSettings()
if (m_settings.changeApps != val)
{
m_settings.changeApps = val;
+ themeTargetChanged = true;
NotifyObservers(SettingId::ChangeApps);
}
}
+
+ // For ChangeSystem/ChangeApps changes, log telemetry
+ if (themeTargetChanged)
+ {
+ Trace::LightSwitch::ThemeTargetChanged(m_settings.changeApps, m_settings.changeSystem);
+ }
}
catch (...)
{
diff --git a/src/modules/LightSwitch/LightSwitchService/trace.cpp b/src/modules/LightSwitch/LightSwitchService/trace.cpp
new file mode 100644
index 0000000000..e65d1d969d
--- /dev/null
+++ b/src/modules/LightSwitch/LightSwitchService/trace.cpp
@@ -0,0 +1,54 @@
+#include "pch.h"
+#include "trace.h"
+
+#include
+
+// Telemetry strings should not be localized.
+#define LoggingProviderKey "Microsoft.PowerToys"
+
+TRACELOGGING_DEFINE_PROVIDER(
+ g_hProvider,
+ LoggingProviderKey,
+ // {38e8889b-9731-53f5-e901-e8a7c1753074}
+ (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
+ TraceLoggingOptionProjectTelemetry());
+
+void Trace::LightSwitch::Enable(bool enabled) noexcept
+{
+ TraceLoggingWriteWrapper(
+ g_hProvider,
+ "LightSwitch_EnableLightSwitch",
+ ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
+ TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
+ TraceLoggingBoolean(enabled, "Enabled"));
+}
+
+void Trace::LightSwitch::ShortcutInvoked() noexcept
+{
+ TraceLoggingWriteWrapper(
+ g_hProvider,
+ "LightSwitch_ShortcutInvoked",
+ ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
+ TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
+}
+
+void Trace::LightSwitch::ScheduleModeToggled(const std::wstring& newMode) noexcept
+{
+ TraceLoggingWriteWrapper(
+ g_hProvider,
+ "LightSwitch_ScheduleModeToggled",
+ ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
+ TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
+ TraceLoggingWideString(newMode.c_str(), "NewMode"));
+}
+
+void Trace::LightSwitch::ThemeTargetChanged(bool changeApps, bool changeSystem) noexcept
+{
+ TraceLoggingWriteWrapper(
+ g_hProvider,
+ "LightSwitch_ThemeTargetChanged",
+ ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
+ TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
+ TraceLoggingBoolean(changeApps, "ChangeApps"),
+ TraceLoggingBoolean(changeSystem, "ChangeSystem"));
+}
\ No newline at end of file
diff --git a/src/modules/LightSwitch/LightSwitchService/trace.h b/src/modules/LightSwitch/LightSwitchService/trace.h
new file mode 100644
index 0000000000..fb951d3da1
--- /dev/null
+++ b/src/modules/LightSwitch/LightSwitchService/trace.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include
+#include
+
+class Trace
+{
+public:
+ class LightSwitch : public telemetry::TraceBase
+ {
+ public:
+ static void Enable(bool enabled) noexcept;
+ static void ShortcutInvoked() noexcept;
+ static void ScheduleModeToggled(const std::wstring& newMode) noexcept;
+ static void ThemeTargetChanged(bool changeApps, bool changeSystem) noexcept;
+ };
+};