OK, screen ruler

This commit is contained in:
kaitao-ms
2025-12-01 16:29:43 +08:00
parent 83531de462
commit 8cf6ed6de5
11 changed files with 258 additions and 72 deletions

View File

@@ -8,6 +8,8 @@
#include <codecvt> #include <codecvt>
#include <common/utils/logger_helper.h> #include <common/utils/logger_helper.h>
#include "ThemeHelper.h" #include "ThemeHelper.h"
#include <thread>
#include <atomic>
extern "C" IMAGE_DOS_HEADER __ImageBase; extern "C" IMAGE_DOS_HEADER __ImageBase;
@@ -98,12 +100,18 @@ private:
HANDLE m_force_light_event_handle; HANDLE m_force_light_event_handle;
HANDLE m_force_dark_event_handle; HANDLE m_force_dark_event_handle;
HANDLE m_manual_override_event_handle; HANDLE m_manual_override_event_handle;
HANDLE m_toggle_event_handle{ nullptr };
std::thread m_toggle_thread;
std::atomic<bool> m_toggle_thread_running{ false };
static const constexpr int NUM_DEFAULT_HOTKEYS = 4; static const constexpr int NUM_DEFAULT_HOTKEYS = 4;
Hotkey m_toggle_theme_hotkey = { .win = true, .ctrl = true, .shift = true, .alt = false, .key = 'D' }; Hotkey m_toggle_theme_hotkey = { .win = true, .ctrl = true, .shift = true, .alt = false, .key = 'D' };
void init_settings(); void init_settings();
void ToggleTheme();
void StartToggleListener();
void StopToggleListener();
public: public:
LightSwitchInterface() LightSwitchInterface()
@@ -113,6 +121,7 @@ public:
m_force_light_event_handle = CreateDefaultEvent(L"POWERTOYS_LIGHTSWITCH_FORCE_LIGHT"); m_force_light_event_handle = CreateDefaultEvent(L"POWERTOYS_LIGHTSWITCH_FORCE_LIGHT");
m_force_dark_event_handle = CreateDefaultEvent(L"POWERTOYS_LIGHTSWITCH_FORCE_DARK"); m_force_dark_event_handle = CreateDefaultEvent(L"POWERTOYS_LIGHTSWITCH_FORCE_DARK");
m_manual_override_event_handle = CreateEventW(nullptr, TRUE, FALSE, L"POWERTOYS_LIGHTSWITCH_MANUAL_OVERRIDE"); m_manual_override_event_handle = CreateEventW(nullptr, TRUE, FALSE, L"POWERTOYS_LIGHTSWITCH_MANUAL_OVERRIDE");
m_toggle_event_handle = CreateDefaultEvent(L"Local\\PowerToys-LightSwitch-ToggleEvent-d8dc2f29-8c94-4ca1-8c5f-3e2b1e3c4f5a");
init_settings(); init_settings();
}; };
@@ -437,6 +446,8 @@ public:
Logger::info(L"Light Switch process launched successfully (PID: {}).", pi.dwProcessId); Logger::info(L"Light Switch process launched successfully (PID: {}).", pi.dwProcessId);
m_process = pi.hProcess; m_process = pi.hProcess;
CloseHandle(pi.hThread); CloseHandle(pi.hThread);
StartToggleListener();
} }
// Disable the powertoy // Disable the powertoy
@@ -462,6 +473,8 @@ public:
CloseHandle(m_process); CloseHandle(m_process);
m_process = nullptr; m_process = nullptr;
} }
StopToggleListener();
} }
// Returns if the powertoys is enabled // Returns if the powertoys is enabled
@@ -523,31 +536,8 @@ public:
} }
else if (hotkeyId == 0) else if (hotkeyId == 0)
{ {
// get current will return true if in light mode; otherwise false
Logger::info(L"[Light Switch] Hotkey triggered: Toggle Theme"); Logger::info(L"[Light Switch] Hotkey triggered: Toggle Theme");
if (g_settings.m_changeSystem) ToggleTheme();
{
SetSystemTheme(!GetCurrentSystemTheme());
}
if (g_settings.m_changeApps)
{
SetAppsTheme(!GetCurrentAppsTheme());
}
if (!m_manual_override_event_handle)
{
m_manual_override_event_handle = OpenEventW(SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, L"POWERTOYS_LIGHTSWITCH_MANUAL_OVERRIDE");
if (!m_manual_override_event_handle)
{
m_manual_override_event_handle = CreateEventW(nullptr, TRUE, FALSE, L"POWERTOYS_LIGHTSWITCH_MANUAL_OVERRIDE");
}
}
if (m_manual_override_event_handle)
{
SetEvent(m_manual_override_event_handle);
Logger::debug(L"[Light Switch] Manual override event set");
}
} }
return true; return true;
@@ -560,6 +550,77 @@ public:
{ {
return WaitForSingleObject(m_process, 0) == WAIT_TIMEOUT; return WaitForSingleObject(m_process, 0) == WAIT_TIMEOUT;
} }
void ToggleTheme()
{
if (g_settings.m_changeSystem)
{
SetSystemTheme(!GetCurrentSystemTheme());
}
if (g_settings.m_changeApps)
{
SetAppsTheme(!GetCurrentAppsTheme());
}
if (!m_manual_override_event_handle)
{
m_manual_override_event_handle = OpenEventW(SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, L"POWERTOYS_LIGHTSWITCH_MANUAL_OVERRIDE");
if (!m_manual_override_event_handle)
{
m_manual_override_event_handle = CreateEventW(nullptr, TRUE, FALSE, L"POWERTOYS_LIGHTSWITCH_MANUAL_OVERRIDE");
}
}
if (m_manual_override_event_handle)
{
SetEvent(m_manual_override_event_handle);
Logger::debug(L"[Light Switch] Manual override event set");
}
}
void StartToggleListener()
{
if (m_toggle_thread_running || !m_toggle_event_handle)
{
return;
}
m_toggle_thread_running = true;
m_toggle_thread = std::thread([this]() {
while (m_toggle_thread_running)
{
const DWORD wait_result = WaitForSingleObject(m_toggle_event_handle, 500);
if (!m_toggle_thread_running)
{
break;
}
if (wait_result == WAIT_OBJECT_0)
{
ToggleTheme();
ResetEvent(m_toggle_event_handle);
}
}
});
}
void StopToggleListener()
{
if (!m_toggle_thread_running)
{
return;
}
m_toggle_thread_running = false;
if (m_toggle_event_handle)
{
SetEvent(m_toggle_event_handle);
}
if (m_toggle_thread.joinable())
{
m_toggle_thread.join();
}
}
}; };
std::wstring utf8_to_wstring(const std::string& str) std::wstring utf8_to_wstring(const std::string& str)
@@ -639,4 +700,4 @@ void LightSwitchInterface::init_settings()
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create() extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
{ {
return new LightSwitchInterface(); return new LightSwitchInterface();
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,35 @@
// 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;
using System.Threading;
using Microsoft.CommandPalette.Extensions.Toolkit;
using PowerToysExtension.Helpers;
namespace PowerToysExtension.Commands;
/// <summary>
/// Toggles Light Switch via the shared event.
/// </summary>
internal sealed partial class ToggleLightSwitchCommand : InvokableCommand
{
public ToggleLightSwitchCommand()
{
Name = "Toggle Light Switch";
}
public override CommandResult Invoke()
{
try
{
using var evt = new EventWaitHandle(false, EventResetMode.AutoReset, PowerToysEventNames.LightSwitchToggle);
evt.Set();
return CommandResult.Dismiss();
}
catch (Exception ex)
{
return CommandResult.ShowToast($"Failed to toggle Light Switch: {ex.Message}");
}
}
}

View File

@@ -0,0 +1,32 @@
// 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;
using System.Threading;
using Microsoft.CommandPalette.Extensions.Toolkit;
using PowerToysExtension.Helpers;
namespace PowerToysExtension.Commands;
internal sealed partial class ToggleScreenRulerCommand : InvokableCommand
{
public ToggleScreenRulerCommand()
{
Name = "Toggle Screen Ruler";
}
public override CommandResult Invoke()
{
try
{
using var evt = new EventWaitHandle(false, EventResetMode.AutoReset, PowerToysEventNames.MeasureToolTrigger);
evt.Set();
return CommandResult.Dismiss();
}
catch (Exception ex)
{
return CommandResult.ShowToast($"Failed to toggle Screen Ruler: {ex.Message}");
}
}
}

View File

@@ -22,8 +22,10 @@ internal static class ModuleCommandCatalog
new AwakeModuleCommandProvider(), new AwakeModuleCommandProvider(),
new AdvancedPasteModuleCommandProvider(), new AdvancedPasteModuleCommandProvider(),
new WorkspacesModuleCommandProvider(), new WorkspacesModuleCommandProvider(),
new LightSwitchModuleCommandProvider(),
new PowerToysRunModuleCommandProvider(),
new ScreenRulerModuleCommandProvider(),
new ColorPickerModuleCommandProvider(), new ColorPickerModuleCommandProvider(),
new DefaultSettingsModuleCommandProvider(),
]; ];
public static IListItem[] FilteredItems(string query) public static IListItem[] FilteredItems(string query)

View File

@@ -25,6 +25,7 @@ internal static class PowerToysEventNames
internal const string PowerToysRunInvoke = "Local\\PowerToysRunInvokeEvent-30f26ad7-d36d-4c0e-ab02-68bb5ff3c4ab"; internal const string PowerToysRunInvoke = "Local\\PowerToysRunInvokeEvent-30f26ad7-d36d-4c0e-ab02-68bb5ff3c4ab";
internal const string RegistryPreviewTrigger = "Local\\RegistryPreviewEvent-4C559468-F75A-4E7F-BC4F-9C9688316687"; internal const string RegistryPreviewTrigger = "Local\\RegistryPreviewEvent-4C559468-F75A-4E7F-BC4F-9C9688316687";
internal const string ShortcutGuideTrigger = "Local\\ShortcutGuide-TriggerEvent-d4275ad3-2531-4d19-9252-c0becbd9b496"; internal const string ShortcutGuideTrigger = "Local\\ShortcutGuide-TriggerEvent-d4275ad3-2531-4d19-9252-c0becbd9b496";
internal const string LightSwitchToggle = "Local\\PowerToys-LightSwitch-ToggleEvent-d8dc2f29-8c94-4ca1-8c5f-3e2b1e3c4f5a";
internal const string WorkspacesLaunchEditor = "Local\\Workspaces-LaunchEditorEvent-a55ff427-cf62-4994-a2cd-9f72139296bf"; internal const string WorkspacesLaunchEditor = "Local\\Workspaces-LaunchEditorEvent-a55ff427-cf62-4994-a2cd-9f72139296bf";
internal const string WorkspacesHotkey = "Local\\PowerToys-Workspaces-HotkeyEvent-2625C3C8-BAC9-4DB3-BCD6-3B4391A26FD0"; internal const string WorkspacesHotkey = "Local\\PowerToys-Workspaces-HotkeyEvent-2625C3C8-BAC9-4DB3-BCD6-3B4391A26FD0";
internal const string CropAndLockThumbnail = "Local\\PowerToysCropAndLockThumbnailEvent-1637be50-da72-46b2-9220-b32b206b2434"; internal const string CropAndLockThumbnail = "Local\\PowerToysCropAndLockThumbnailEvent-1637be50-da72-46b2-9220-b32b206b2434";

View File

@@ -38,6 +38,7 @@ internal static class PowerToysResourcesHelper
SettingsWindow.FileLocksmith => "Assets\\FileLocksmith.png", SettingsWindow.FileLocksmith => "Assets\\FileLocksmith.png",
SettingsWindow.NewPlus => "Assets\\NewPlus.png", SettingsWindow.NewPlus => "Assets\\NewPlus.png",
SettingsWindow.Peek => "Assets\\Peek.png", SettingsWindow.Peek => "Assets\\Peek.png",
SettingsWindow.LightSwitch => "Assets\\LightSwitch.png",
SettingsWindow.AlwaysOnTop => "Assets\\AlwaysOnTop.png", SettingsWindow.AlwaysOnTop => "Assets\\AlwaysOnTop.png",
SettingsWindow.CmdNotFound => "Assets\\CommandNotFound.png", SettingsWindow.CmdNotFound => "Assets\\CommandNotFound.png",
SettingsWindow.MouseWithoutBorders => "Assets\\MouseWithoutBorders.png", SettingsWindow.MouseWithoutBorders => "Assets\\MouseWithoutBorders.png",
@@ -79,6 +80,7 @@ internal static class PowerToysResourcesHelper
SettingsWindow.FileLocksmith => "File Locksmith", SettingsWindow.FileLocksmith => "File Locksmith",
SettingsWindow.NewPlus => "New+", SettingsWindow.NewPlus => "New+",
SettingsWindow.Peek => "Peek", SettingsWindow.Peek => "Peek",
SettingsWindow.LightSwitch => "Light Switch",
SettingsWindow.AlwaysOnTop => "Always On Top", SettingsWindow.AlwaysOnTop => "Always On Top",
SettingsWindow.CmdNotFound => "Command Not Found", SettingsWindow.CmdNotFound => "Command Not Found",
SettingsWindow.MouseWithoutBorders => "Mouse Without Borders", SettingsWindow.MouseWithoutBorders => "Mouse Without Borders",

View File

@@ -1,46 +0,0 @@
// 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;
using System.Collections.Generic;
using System.Linq;
using Common.UI;
using Microsoft.CommandPalette.Extensions.Toolkit;
using PowerToysExtension.Commands;
using PowerToysExtension.Helpers;
namespace PowerToysExtension.Modules;
/// <summary>
/// Provides open-settings commands for modules without specialized commands.
/// </summary>
internal sealed class DefaultSettingsModuleCommandProvider : ModuleCommandProvider
{
private static readonly SettingsDeepLink.SettingsWindow[] _excluded =
[
SettingsDeepLink.SettingsWindow.Dashboard,
SettingsDeepLink.SettingsWindow.Workspaces,
SettingsDeepLink.SettingsWindow.Awake,
SettingsDeepLink.SettingsWindow.ColorPicker,
];
public override IEnumerable<ListItem> BuildCommands()
{
foreach (var module in Enum.GetValues<SettingsDeepLink.SettingsWindow>())
{
if (_excluded.Contains(module))
{
continue;
}
var title = module.ModuleDisplayName();
yield return new ListItem(new OpenInSettingsCommand(module, title))
{
Title = title,
Subtitle = "Open module settings",
Icon = module.ModuleIcon(),
};
}
}
}

View File

@@ -0,0 +1,38 @@
// 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.Collections.Generic;
using Microsoft.CommandPalette.Extensions.Toolkit;
using PowerToysExtension.Commands;
using PowerToysExtension.Helpers;
using static Common.UI.SettingsDeepLink;
namespace PowerToysExtension.Modules;
internal sealed class LightSwitchModuleCommandProvider : ModuleCommandProvider
{
public override IEnumerable<ListItem> BuildCommands()
{
var title = SettingsWindow.LightSwitch.ModuleDisplayName();
var icon = SettingsWindow.LightSwitch.ModuleIcon();
var items = new List<ListItem>
{
new ListItem(new ToggleLightSwitchCommand())
{
Title = "Toggle Light Switch",
Subtitle = "Toggle system/apps theme immediately",
Icon = icon,
},
new ListItem(new OpenInSettingsCommand(SettingsWindow.LightSwitch, title))
{
Title = title,
Subtitle = "Open Light Switch settings",
Icon = icon,
},
};
return items;
}
}

View File

@@ -0,0 +1,27 @@
// 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.Collections.Generic;
using Microsoft.CommandPalette.Extensions.Toolkit;
using PowerToysExtension.Commands;
using PowerToysExtension.Helpers;
using static Common.UI.SettingsDeepLink;
namespace PowerToysExtension.Modules;
internal sealed class PowerToysRunModuleCommandProvider : ModuleCommandProvider
{
public override IEnumerable<ListItem> BuildCommands()
{
var title = SettingsWindow.PowerLauncher.ModuleDisplayName();
var icon = SettingsWindow.PowerLauncher.ModuleIcon();
yield return new ListItem(new OpenInSettingsCommand(SettingsWindow.PowerLauncher, title))
{
Title = title,
Subtitle = "Open PowerToys Run settings",
Icon = icon,
};
}
}

View File

@@ -0,0 +1,34 @@
// 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.Collections.Generic;
using Microsoft.CommandPalette.Extensions.Toolkit;
using PowerToysExtension.Commands;
using PowerToysExtension.Helpers;
using static Common.UI.SettingsDeepLink;
namespace PowerToysExtension.Modules;
internal sealed class ScreenRulerModuleCommandProvider : ModuleCommandProvider
{
public override IEnumerable<ListItem> BuildCommands()
{
var title = SettingsWindow.MeasureTool.ModuleDisplayName();
var icon = SettingsWindow.MeasureTool.ModuleIcon();
yield return new ListItem(new ToggleScreenRulerCommand())
{
Title = "Toggle Screen Ruler",
Subtitle = "Start or close Screen Ruler",
Icon = icon,
};
yield return new ListItem(new OpenInSettingsCommand(SettingsWindow.MeasureTool, title))
{
Title = title,
Subtitle = "Open Screen Ruler settings",
Icon = icon,
};
}
}