mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-02-23 19:49:43 +01:00
init
Signed-off-by: Shawn Yuan (from Dev Box) <shuaiyuan@microsoft.com>
This commit is contained in:
@@ -394,6 +394,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Plugin.WindowWalk
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerToys.Settings", "src\settings-ui\Settings.UI\PowerToys.Settings.csproj", "{020A7474-3601-4160-A159-D7B70B77B15F}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerToys.QuickAccess", "src\settings-ui\QuickAccess.UI\PowerToys.QuickAccess.csproj", "{A7C9C9C6-BA80-4CF3-9DB0-6DBBB4148F60}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRenameUI", "src\modules\powerrename\PowerRenameUILib\PowerRenameUI.vcxproj", "{27718999-C175-450A-861C-89F911E16A88}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRenameContextMenu", "src\modules\powerrename\PowerRenameContextMenu\PowerRenameContextMenu.vcxproj", "{1DBBB112-4BB1-444B-8EBB-E66555C76BA6}"
|
||||
@@ -1686,6 +1688,14 @@ Global
|
||||
{020A7474-3601-4160-A159-D7B70B77B15F}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{020A7474-3601-4160-A159-D7B70B77B15F}.Release|x64.ActiveCfg = Release|x64
|
||||
{020A7474-3601-4160-A159-D7B70B77B15F}.Release|x64.Build.0 = Release|x64
|
||||
{A7C9C9C6-BA80-4CF3-9DB0-6DBBB4148F60}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{A7C9C9C6-BA80-4CF3-9DB0-6DBBB4148F60}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{A7C9C9C6-BA80-4CF3-9DB0-6DBBB4148F60}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{A7C9C9C6-BA80-4CF3-9DB0-6DBBB4148F60}.Debug|x64.Build.0 = Debug|x64
|
||||
{A7C9C9C6-BA80-4CF3-9DB0-6DBBB4148F60}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{A7C9C9C6-BA80-4CF3-9DB0-6DBBB4148F60}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{A7C9C9C6-BA80-4CF3-9DB0-6DBBB4148F60}.Release|x64.ActiveCfg = Release|x64
|
||||
{A7C9C9C6-BA80-4CF3-9DB0-6DBBB4148F60}.Release|x64.Build.0 = Release|x64
|
||||
{27718999-C175-450A-861C-89F911E16A88}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{27718999-C175-450A-861C-89F911E16A88}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{27718999-C175-450A-861C-89F911E16A88}.Debug|x64.ActiveCfg = Debug|x64
|
||||
@@ -3140,6 +3150,7 @@ Global
|
||||
{FD464B4C-2F68-4D06-91E7-4208146C41F5} = {B9617A31-0F0A-4397-851D-BF2FBEE32D7F}
|
||||
{8FE5A5EE-1B59-401C-9FB3-B04ECD3E29C1} = {B9617A31-0F0A-4397-851D-BF2FBEE32D7F}
|
||||
{020A7474-3601-4160-A159-D7B70B77B15F} = {C3081D9A-1586-441A-B5F4-ED815B3719C1}
|
||||
{A7C9C9C6-BA80-4CF3-9DB0-6DBBB4148F60} = {C3081D9A-1586-441A-B5F4-ED815B3719C1}
|
||||
{27718999-C175-450A-861C-89F911E16A88} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3}
|
||||
{1DBBB112-4BB1-444B-8EBB-E66555C76BA6} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3}
|
||||
{5A1DB2F0-0715-4B3B-98E6-79BC41540045} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <RestartManager.h>
|
||||
#include "centralized_kb_hook.h"
|
||||
#include "centralized_hotkeys.h"
|
||||
#include "quick_access_host.h"
|
||||
|
||||
#if _DEBUG && _WIN64
|
||||
#include "unhandled_exception_handler.h"
|
||||
@@ -105,6 +106,7 @@ int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow
|
||||
#endif
|
||||
Trace::RegisterProvider();
|
||||
start_tray_icon(isProcessElevated);
|
||||
QuickAccessHost::start();
|
||||
set_tray_icon_visible(get_general_settings().showSystemTrayIcon);
|
||||
CentralizedKeyboardHook::Start();
|
||||
|
||||
@@ -242,6 +244,7 @@ int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow
|
||||
result = -1;
|
||||
}
|
||||
Trace::UnregisterProvider();
|
||||
QuickAccessHost::stop();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
157
src/runner/quick_access_host.cpp
Normal file
157
src/runner/quick_access_host.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
#include "pch.h"
|
||||
#include "quick_access_host.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <common/logger/logger.h>
|
||||
#include <common/utils/process_path.h>
|
||||
#include <wil/resource.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
wil::unique_handle quick_access_process;
|
||||
wil::unique_handle show_event;
|
||||
wil::unique_handle exit_event;
|
||||
std::wstring show_event_name;
|
||||
std::wstring exit_event_name;
|
||||
std::mutex quick_access_mutex;
|
||||
|
||||
bool is_process_active_locked()
|
||||
{
|
||||
if (!quick_access_process)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD exit_code = 0;
|
||||
if (!GetExitCodeProcess(quick_access_process.get(), &exit_code))
|
||||
{
|
||||
Logger::warn(L"QuickAccessHost: failed to read Quick Access process exit code. error={}.", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return exit_code == STILL_ACTIVE;
|
||||
}
|
||||
|
||||
void reset_state_locked()
|
||||
{
|
||||
quick_access_process.reset();
|
||||
show_event.reset();
|
||||
exit_event.reset();
|
||||
show_event_name.clear();
|
||||
exit_event_name.clear();
|
||||
}
|
||||
|
||||
std::wstring build_event_name(const wchar_t* suffix)
|
||||
{
|
||||
return L"Local\\PowerToysQuickAccess_" + std::to_wstring(GetCurrentProcessId()) + suffix;
|
||||
}
|
||||
|
||||
std::wstring build_command_line(const std::wstring& exe_path)
|
||||
{
|
||||
std::wstring command_line = L"\"";
|
||||
command_line += exe_path;
|
||||
command_line += L"\" --show-event=\"";
|
||||
command_line += show_event_name;
|
||||
command_line += L"\" --exit-event=\"";
|
||||
command_line += exit_event_name;
|
||||
command_line += L"\"";
|
||||
return command_line;
|
||||
}
|
||||
}
|
||||
|
||||
namespace QuickAccessHost
|
||||
{
|
||||
bool is_running()
|
||||
{
|
||||
std::scoped_lock lock(quick_access_mutex);
|
||||
return is_process_active_locked();
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
std::scoped_lock lock(quick_access_mutex);
|
||||
if (is_process_active_locked())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
reset_state_locked();
|
||||
|
||||
show_event_name = build_event_name(L"_Show");
|
||||
exit_event_name = build_event_name(L"_Exit");
|
||||
|
||||
show_event.reset(CreateEventW(nullptr, FALSE, FALSE, show_event_name.c_str()));
|
||||
if (!show_event)
|
||||
{
|
||||
Logger::error(L"QuickAccessHost: failed to create show event. error={}.", GetLastError());
|
||||
reset_state_locked();
|
||||
return;
|
||||
}
|
||||
|
||||
exit_event.reset(CreateEventW(nullptr, FALSE, FALSE, exit_event_name.c_str()));
|
||||
if (!exit_event)
|
||||
{
|
||||
Logger::error(L"QuickAccessHost: failed to create exit event. error={}.", GetLastError());
|
||||
reset_state_locked();
|
||||
return;
|
||||
}
|
||||
|
||||
const std::wstring exe_path = get_module_folderpath() + L"\\WinUI3Apps\\PowerToys.QuickAccess.exe";
|
||||
if (GetFileAttributesW(exe_path.c_str()) == INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
Logger::warn(L"QuickAccessHost: missing Quick Access executable at {}", exe_path);
|
||||
reset_state_locked();
|
||||
return;
|
||||
}
|
||||
|
||||
const std::wstring command_line = build_command_line(exe_path);
|
||||
std::vector<wchar_t> command_line_buffer(command_line.begin(), command_line.end());
|
||||
command_line_buffer.push_back(L'\0');
|
||||
STARTUPINFOW startup_info{};
|
||||
startup_info.cb = sizeof(startup_info);
|
||||
PROCESS_INFORMATION process_info{};
|
||||
|
||||
BOOL created = CreateProcessW(exe_path.c_str(), command_line_buffer.data(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &startup_info, &process_info);
|
||||
if (!created)
|
||||
{
|
||||
Logger::error(L"QuickAccessHost: failed to launch Quick Access host. error={}.", GetLastError());
|
||||
reset_state_locked();
|
||||
return;
|
||||
}
|
||||
|
||||
quick_access_process.reset(process_info.hProcess);
|
||||
CloseHandle(process_info.hThread);
|
||||
}
|
||||
|
||||
void show()
|
||||
{
|
||||
start();
|
||||
std::scoped_lock lock(quick_access_mutex);
|
||||
if (show_event)
|
||||
{
|
||||
if (!SetEvent(show_event.get()))
|
||||
{
|
||||
Logger::warn(L"QuickAccessHost: failed to signal show event. error={}.", GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
std::unique_lock lock(quick_access_mutex);
|
||||
if (exit_event)
|
||||
{
|
||||
SetEvent(exit_event.get());
|
||||
}
|
||||
|
||||
if (quick_access_process)
|
||||
{
|
||||
WaitForSingleObject(quick_access_process.get(), 2000);
|
||||
}
|
||||
|
||||
reset_state_locked();
|
||||
}
|
||||
}
|
||||
12
src/runner/quick_access_host.h
Normal file
12
src/runner/quick_access_host.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <optional>
|
||||
|
||||
namespace QuickAccessHost
|
||||
{
|
||||
void start();
|
||||
void show();
|
||||
void stop();
|
||||
bool is_running();
|
||||
}
|
||||
@@ -66,6 +66,7 @@
|
||||
</ClCompile>
|
||||
<ClCompile Include="powertoy_module.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="quick_access_host.cpp" />
|
||||
<ClCompile Include="restart_elevated.cpp" />
|
||||
<ClCompile Include="centralized_kb_hook.cpp" />
|
||||
<ClCompile Include="settings_telemetry.cpp" />
|
||||
@@ -80,6 +81,7 @@
|
||||
<ClInclude Include="auto_start_helper.h" />
|
||||
<ClInclude Include="bug_report.h" />
|
||||
<ClInclude Include="centralized_hotkeys.h" />
|
||||
<ClInclude Include="quick_access_host.h" />
|
||||
<ClInclude Include="general_settings.h" />
|
||||
<ClInclude Include="hotkey_conflict_detector.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
|
||||
@@ -48,6 +48,9 @@
|
||||
<ClCompile Include="hotkey_conflict_detector.cpp">
|
||||
<Filter>Utils</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="quick_access_host.cpp">
|
||||
<Filter>Utils</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
@@ -99,6 +102,9 @@
|
||||
<ClInclude Include="hotkey_conflict_detector.h">
|
||||
<Filter>Utils</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="quick_access_host.h">
|
||||
<Filter>Utils</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Utils">
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "general_settings.h"
|
||||
#include "centralized_hotkeys.h"
|
||||
#include "centralized_kb_hook.h"
|
||||
#include "quick_access_host.h"
|
||||
#include <Windows.h>
|
||||
|
||||
#include <common/utils/resources.h>
|
||||
@@ -71,7 +72,8 @@ void change_menu_item_text(const UINT item_id, wchar_t* new_text)
|
||||
|
||||
void open_quick_access_flyout_window(const POINT flyout_position)
|
||||
{
|
||||
open_settings_window(std::nullopt, true, flyout_position);
|
||||
UNREFERENCED_PARAMETER(flyout_position);
|
||||
QuickAccessHost::show();
|
||||
}
|
||||
|
||||
void handle_tray_command(HWND window, const WPARAM command_id, LPARAM lparam)
|
||||
|
||||
9
src/settings-ui/QuickAccess.UI/App.xaml
Normal file
9
src/settings-ui/QuickAccess.UI/App.xaml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Application
|
||||
x:Class="Microsoft.PowerToys.QuickAccess.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Microsoft.PowerToys.QuickAccess">
|
||||
<Application.Resources>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
20
src/settings-ui/QuickAccess.UI/App.xaml.cs
Normal file
20
src/settings-ui/QuickAccess.UI/App.xaml.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// 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 Microsoft.UI.Xaml;
|
||||
|
||||
namespace Microsoft.PowerToys.QuickAccess;
|
||||
|
||||
public partial class App : Application
|
||||
{
|
||||
private MainWindow? _window;
|
||||
|
||||
protected override void OnLaunched(LaunchActivatedEventArgs args)
|
||||
{
|
||||
var launchContext = QuickAccessLaunchContext.Parse(Environment.GetCommandLineArgs());
|
||||
_window = new MainWindow(launchContext);
|
||||
_window.Activate();
|
||||
}
|
||||
}
|
||||
16
src/settings-ui/QuickAccess.UI/MainWindow.xaml
Normal file
16
src/settings-ui/QuickAccess.UI/MainWindow.xaml
Normal file
@@ -0,0 +1,16 @@
|
||||
<Window
|
||||
x:Class="Microsoft.PowerToys.QuickAccess.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
Title="PowerToys Quick Access (Preview)">
|
||||
|
||||
<Grid>
|
||||
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Spacing="12">
|
||||
<TextBlock Text="PowerToys Quick Access placeholder" HorizontalAlignment="Center" />
|
||||
<TextBlock Text="This window stays resident to simulate a dedicated flyout host." TextWrapping="Wrap" Width="250" TextAlignment="Center" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
418
src/settings-ui/QuickAccess.UI/MainWindow.xaml.cs
Normal file
418
src/settings-ui/QuickAccess.UI/MainWindow.xaml.cs
Normal file
@@ -0,0 +1,418 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Windows.Graphics;
|
||||
using WinRT.Interop;
|
||||
|
||||
namespace Microsoft.PowerToys.QuickAccess;
|
||||
|
||||
public sealed partial class MainWindow : Window
|
||||
{
|
||||
private readonly QuickAccessLaunchContext _launchContext;
|
||||
private readonly DispatcherQueue _dispatcherQueue;
|
||||
private readonly IntPtr _hwnd;
|
||||
private readonly AppWindow? _appWindow;
|
||||
private EventWaitHandle? _showEvent;
|
||||
private EventWaitHandle? _exitEvent;
|
||||
private ManualResetEventSlim? _listenerShutdownEvent;
|
||||
private Thread? _showListenerThread;
|
||||
private Thread? _exitListenerThread;
|
||||
private bool _isWindowCloaked;
|
||||
private bool _initialActivationHandled;
|
||||
private bool _isPrimed;
|
||||
|
||||
private const int DefaultWidth = 320;
|
||||
private const int DefaultHeight = 480;
|
||||
private const int DwmWaCloak = 13;
|
||||
private const int GwlStyle = -16;
|
||||
private const int SwHide = 0;
|
||||
private const int SwShow = 5;
|
||||
private const int SwShowNoActivate = 8;
|
||||
private const uint SwpShowWindow = 0x0040;
|
||||
private const uint SwpNoZorder = 0x0004;
|
||||
private const uint SwpNoSize = 0x0001;
|
||||
private const uint SwpNoMove = 0x0002;
|
||||
private const uint SwpNoActivate = 0x0010;
|
||||
private const uint SwpFrameChanged = 0x0020;
|
||||
private const long WsSysmenu = 0x00080000L;
|
||||
private const long WsMinimizeBox = 0x00020000L;
|
||||
private const long WsMaximizeBox = 0x00010000L;
|
||||
private static readonly IntPtr HwndTopmost = new(-1);
|
||||
private static readonly IntPtr HwndBottom = new(1);
|
||||
|
||||
public MainWindow(QuickAccessLaunchContext launchContext)
|
||||
{
|
||||
InitializeComponent();
|
||||
_launchContext = launchContext;
|
||||
_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
_hwnd = WindowNative.GetWindowHandle(this);
|
||||
_appWindow = InitializeAppWindow(_hwnd);
|
||||
Title = "PowerToys Quick Access (Preview)";
|
||||
|
||||
CustomizeWindowChrome();
|
||||
HideWindow();
|
||||
InitializeEventListeners();
|
||||
Closed += OnClosed;
|
||||
Activated += OnActivated;
|
||||
}
|
||||
|
||||
private AppWindow? InitializeAppWindow(IntPtr hwnd)
|
||||
{
|
||||
var windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hwnd);
|
||||
return AppWindow.GetFromWindowId(windowId);
|
||||
}
|
||||
|
||||
private void HideWindow()
|
||||
{
|
||||
if (_hwnd != IntPtr.Zero)
|
||||
{
|
||||
var cloaked = CloakWindow();
|
||||
|
||||
if (!ShowWindowNative(_hwnd, SwHide) && _appWindow != null)
|
||||
{
|
||||
_appWindow.Hide();
|
||||
}
|
||||
|
||||
if (cloaked)
|
||||
{
|
||||
ShowWindowNative(_hwnd, SwShowNoActivate);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetWindowPosNative(_hwnd, HwndBottom, 0, 0, 0, 0, SwpNoMove | SwpNoSize | SwpNoActivate);
|
||||
}
|
||||
}
|
||||
else if (_appWindow != null)
|
||||
{
|
||||
_appWindow.Hide();
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeEventListeners()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_launchContext.ShowEventName))
|
||||
{
|
||||
try
|
||||
{
|
||||
_showEvent = EventWaitHandle.OpenExisting(_launchContext.ShowEventName!);
|
||||
EnsureListenerInfrastructure();
|
||||
StartShowListenerThread();
|
||||
}
|
||||
catch (WaitHandleCannotBeOpenedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(_launchContext.ExitEventName))
|
||||
{
|
||||
try
|
||||
{
|
||||
_exitEvent = EventWaitHandle.OpenExisting(_launchContext.ExitEventName!);
|
||||
EnsureListenerInfrastructure();
|
||||
StartExitListenerThread();
|
||||
}
|
||||
catch (WaitHandleCannotBeOpenedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowWindow()
|
||||
{
|
||||
if (_hwnd != IntPtr.Zero)
|
||||
{
|
||||
UncloakWindow();
|
||||
ShowWindowNative(_hwnd, SwShow);
|
||||
SetWindowPosNative(_hwnd, HwndTopmost, 0, 0, 0, 0, SwpNoMove | SwpNoSize | SwpShowWindow);
|
||||
SetForegroundWindowNative(_hwnd);
|
||||
}
|
||||
|
||||
Activate();
|
||||
}
|
||||
|
||||
private void OnActivated(object sender, WindowActivatedEventArgs args)
|
||||
{
|
||||
if (args.WindowActivationState == WindowActivationState.Deactivated)
|
||||
{
|
||||
HideWindow();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_initialActivationHandled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_initialActivationHandled = true;
|
||||
PrimeWindow();
|
||||
HideWindow();
|
||||
}
|
||||
|
||||
private void OnClosed(object sender, WindowEventArgs e)
|
||||
{
|
||||
StopEventListeners();
|
||||
_showEvent?.Dispose();
|
||||
_showEvent = null;
|
||||
_exitEvent?.Dispose();
|
||||
_exitEvent = null;
|
||||
if (_hwnd != IntPtr.Zero)
|
||||
{
|
||||
UncloakWindow();
|
||||
}
|
||||
}
|
||||
|
||||
private void PrimeWindow()
|
||||
{
|
||||
if (_isPrimed || _hwnd == IntPtr.Zero)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isPrimed = true;
|
||||
|
||||
if (_appWindow != null)
|
||||
{
|
||||
var currentPosition = _appWindow.Position;
|
||||
_appWindow.MoveAndResize(new RectInt32(currentPosition.X, currentPosition.Y, DefaultWidth, DefaultHeight));
|
||||
}
|
||||
|
||||
// Warm up the window while cloaked so the first summon does not pay XAML initialization cost.
|
||||
var cloaked = CloakWindow();
|
||||
if (cloaked)
|
||||
{
|
||||
ShowWindowNative(_hwnd, SwShowNoActivate);
|
||||
}
|
||||
}
|
||||
|
||||
private bool CloakWindow()
|
||||
{
|
||||
if (_hwnd == IntPtr.Zero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_isWindowCloaked)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int cloak = 1;
|
||||
var result = DwmSetWindowAttribute(_hwnd, DwmWaCloak, ref cloak, sizeof(int));
|
||||
if (result == 0)
|
||||
{
|
||||
_isWindowCloaked = true;
|
||||
SetWindowPosNative(_hwnd, HwndBottom, 0, 0, 0, 0, SwpNoMove | SwpNoSize | SwpNoActivate);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void UncloakWindow()
|
||||
{
|
||||
if (_hwnd == IntPtr.Zero || !_isWindowCloaked)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int cloak = 0;
|
||||
var result = DwmSetWindowAttribute(_hwnd, DwmWaCloak, ref cloak, sizeof(int));
|
||||
if (result == 0)
|
||||
{
|
||||
_isWindowCloaked = false;
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "ShowWindow", SetLastError = true)]
|
||||
private static extern bool ShowWindowNative(IntPtr hWnd, int nCmdShow);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "GetWindowLongPtr", SetLastError = true)]
|
||||
private static extern nint GetWindowLongPtrNative(IntPtr hWnd, int nIndex);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", SetLastError = true)]
|
||||
private static extern nint SetWindowLongPtrNative(IntPtr hWnd, int nIndex, nint dwNewLong);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "SetWindowPos", SetLastError = true)]
|
||||
private static extern bool SetWindowPosNative(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "SetForegroundWindow", SetLastError = true)]
|
||||
private static extern bool SetForegroundWindowNative(IntPtr hWnd);
|
||||
|
||||
[DllImport("dwmapi.dll", EntryPoint = "DwmSetWindowAttribute", SetLastError = true)]
|
||||
private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);
|
||||
|
||||
private void EnsureListenerInfrastructure()
|
||||
{
|
||||
_listenerShutdownEvent ??= new ManualResetEventSlim(false);
|
||||
}
|
||||
|
||||
private void StartShowListenerThread()
|
||||
{
|
||||
if (_showEvent == null || _listenerShutdownEvent == null || _showListenerThread != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_showListenerThread = new Thread(ListenForShowEvents)
|
||||
{
|
||||
IsBackground = true,
|
||||
Name = "QuickAccess-ShowEventListener",
|
||||
};
|
||||
_showListenerThread.Start();
|
||||
}
|
||||
|
||||
private void StartExitListenerThread()
|
||||
{
|
||||
if (_exitEvent == null || _listenerShutdownEvent == null || _exitListenerThread != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_exitListenerThread = new Thread(ListenForExitEvents)
|
||||
{
|
||||
IsBackground = true,
|
||||
Name = "QuickAccess-ExitEventListener",
|
||||
};
|
||||
_exitListenerThread.Start();
|
||||
}
|
||||
|
||||
private void ListenForShowEvents()
|
||||
{
|
||||
if (_showEvent == null || _listenerShutdownEvent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var handles = new WaitHandle[] { _showEvent, _listenerShutdownEvent.WaitHandle };
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var index = WaitHandle.WaitAny(handles);
|
||||
if (index == 0)
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(ShowWindow);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
catch (ThreadInterruptedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void ListenForExitEvents()
|
||||
{
|
||||
if (_exitEvent == null || _listenerShutdownEvent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var handles = new WaitHandle[] { _exitEvent, _listenerShutdownEvent.WaitHandle };
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var index = WaitHandle.WaitAny(handles);
|
||||
if (index == 0)
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(Close);
|
||||
break;
|
||||
}
|
||||
|
||||
if (index == 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
catch (ThreadInterruptedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void StopEventListeners()
|
||||
{
|
||||
if (_listenerShutdownEvent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_listenerShutdownEvent.Set();
|
||||
|
||||
JoinListenerThread(ref _showListenerThread);
|
||||
JoinListenerThread(ref _exitListenerThread);
|
||||
|
||||
_listenerShutdownEvent.Dispose();
|
||||
_listenerShutdownEvent = null;
|
||||
}
|
||||
|
||||
private static void JoinListenerThread(ref Thread? thread)
|
||||
{
|
||||
if (thread == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (!thread.Join(TimeSpan.FromMilliseconds(250)))
|
||||
{
|
||||
thread.Interrupt();
|
||||
thread.Join(TimeSpan.FromMilliseconds(250));
|
||||
}
|
||||
}
|
||||
catch (ThreadInterruptedException)
|
||||
{
|
||||
}
|
||||
catch (ThreadStateException)
|
||||
{
|
||||
}
|
||||
|
||||
thread = null;
|
||||
}
|
||||
|
||||
private void CustomizeWindowChrome()
|
||||
{
|
||||
if (_hwnd == IntPtr.Zero)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var stylePtr = GetWindowLongPtrNative(_hwnd, GwlStyle);
|
||||
var lastError = Marshal.GetLastWin32Error();
|
||||
if (stylePtr == nint.Zero && lastError != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var styleValue = (long)stylePtr;
|
||||
var newStyleValue = styleValue & ~(WsSysmenu | WsMinimizeBox | WsMaximizeBox);
|
||||
|
||||
if (newStyleValue == styleValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SetWindowLongPtrNative(_hwnd, GwlStyle, (nint)newStyleValue);
|
||||
|
||||
// Apply the new chrome immediately so caption buttons disappear right away.
|
||||
SetWindowPosNative(_hwnd, IntPtr.Zero, 0, 0, 0, 0, SwpNoMove | SwpNoSize | SwpNoZorder | SwpNoActivate | SwpFrameChanged);
|
||||
}
|
||||
}
|
||||
36
src/settings-ui/QuickAccess.UI/PowerToys.QuickAccess.csproj
Normal file
36
src/settings-ui/QuickAccess.UI/PowerToys.QuickAccess.csproj
Normal file
@@ -0,0 +1,36 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\Common.SelfContained.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net9.0-windows10.0.19041.0</TargetFramework>
|
||||
<RootNamespace>Microsoft.PowerToys.QuickAccess</RootNamespace>
|
||||
<AssemblyName>PowerToys.QuickAccess</AssemblyName>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
<WindowsPackageType>None</WindowsPackageType>
|
||||
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
||||
<EnablePreviewMsixTooling>true</EnablePreviewMsixTooling>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<OutputPath>..\..\..\$(Platform)\$(Configuration)\WinUI3Apps</OutputPath>
|
||||
<EnableDefaultPageItems>false</EnableDefaultPageItems>
|
||||
<EnableDefaultApplicationDefinition>false</EnableDefaultApplicationDefinition>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="App.xaml" />
|
||||
<Page Include="MainWindow.xaml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" />
|
||||
<PackageReference Include="WinUIEx" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Manifest Include="$(ApplicationManifest)" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
52
src/settings-ui/QuickAccess.UI/QuickAccessLaunchContext.cs
Normal file
52
src/settings-ui/QuickAccess.UI/QuickAccessLaunchContext.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
// 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.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Microsoft.PowerToys.QuickAccess;
|
||||
|
||||
public sealed record QuickAccessLaunchContext(string? ShowEventName, string? ExitEventName)
|
||||
{
|
||||
public static QuickAccessLaunchContext Parse(string[] args)
|
||||
{
|
||||
string? showEvent = null;
|
||||
string? exitEvent = null;
|
||||
|
||||
foreach (var arg in args)
|
||||
{
|
||||
if (TryReadValue(arg, "--show-event", out var value))
|
||||
{
|
||||
showEvent = value;
|
||||
}
|
||||
else if (TryReadValue(arg, "--exit-event", out value))
|
||||
{
|
||||
exitEvent = value;
|
||||
}
|
||||
}
|
||||
|
||||
return new QuickAccessLaunchContext(showEvent, exitEvent);
|
||||
}
|
||||
|
||||
private static bool TryReadValue(string candidate, string key, [NotNullWhen(true)] out string? value)
|
||||
{
|
||||
if (candidate.StartsWith(key, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (candidate.Length == key.Length)
|
||||
{
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (candidate[key.Length] == '=')
|
||||
{
|
||||
value = candidate[(key.Length + 1)..].Trim('"');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
16
src/settings-ui/QuickAccess.UI/app.manifest
Normal file
16
src/settings-ui/QuickAccess.UI/app.manifest
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="PowerToys.QuickAccess.app" />
|
||||
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
</application>
|
||||
</compatibility>
|
||||
</assembly>
|
||||
Reference in New Issue
Block a user