mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 11:48:06 +01:00
Coding style (settings) (#1012)
This commit is contained in:
@@ -1,24 +1,26 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "StreamUriResolverFromFile.h"
|
#include "StreamUriResolverFromFile.h"
|
||||||
|
|
||||||
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Storage::Streams::IInputStream> StreamUriResolverFromFile::UriToStreamAsync(const winrt::Windows::Foundation::Uri & uri) const {
|
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Storage::Streams::IInputStream> StreamUriResolverFromFile::UriToStreamAsync(const winrt::Windows::Foundation::Uri& uri) const
|
||||||
|
{
|
||||||
|
winrt::Windows::Storage::StorageFolder folder = winrt::Windows::Storage::StorageFolder::GetFolderFromPathAsync(winrt::param::hstring(base_path)).get();
|
||||||
|
|
||||||
winrt::Windows::Storage::StorageFolder folder = winrt::Windows::Storage::StorageFolder::GetFolderFromPathAsync(winrt::param::hstring(base_path)).get();
|
std::wstring myuri = uri.Path().c_str();
|
||||||
|
myuri.erase(0, 1); // Removes the first slash from the URI
|
||||||
|
|
||||||
std::wstring myuri = uri.Path().c_str();
|
std::replace(myuri.begin(), myuri.end(), '/', '\\');
|
||||||
myuri.erase(0, 1); // Removes the first slash from the URI
|
winrt::Windows::Storage::StorageFile file = nullptr;
|
||||||
|
|
||||||
std::replace(myuri.begin(), myuri.end(), '/', '\\');
|
try
|
||||||
winrt::Windows::Storage::StorageFile file = nullptr;
|
{
|
||||||
|
file = folder.GetFileAsync(winrt::param::hstring(myuri)).get();
|
||||||
|
}
|
||||||
|
catch (winrt::hresult_error const& e)
|
||||||
|
{
|
||||||
|
WCHAR message[1024] = L"";
|
||||||
|
StringCchPrintf(message, ARRAYSIZE(message), L"failed: %ls", e.message().c_str());
|
||||||
|
MessageBox(NULL, message, L"Error", MB_OK);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
return file.OpenSequentialReadAsync();
|
||||||
file = folder.GetFileAsync(winrt::param::hstring(myuri)).get();
|
|
||||||
}
|
|
||||||
catch (winrt::hresult_error const& e) {
|
|
||||||
WCHAR message[1024] = L"";
|
|
||||||
StringCchPrintf(message, ARRAYSIZE(message), L"failed: %ls", e.message().c_str());
|
|
||||||
MessageBox(NULL, message, L"Error", MB_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
return file.OpenSequentialReadAsync();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
|
||||||
struct StreamUriResolverFromFile : winrt::implements <StreamUriResolverFromFile, winrt::Windows::Web::IUriToStreamResolver> {
|
struct StreamUriResolverFromFile : winrt::implements<StreamUriResolverFromFile, winrt::Windows::Web::IUriToStreamResolver>
|
||||||
WCHAR base_path[MAX_PATH];
|
{
|
||||||
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Storage::Streams::IInputStream> UriToStreamAsync(const winrt::Windows::Foundation::Uri & uri) const;
|
WCHAR base_path[MAX_PATH];
|
||||||
|
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Storage::Streams::IInputStream> UriToStreamAsync(const winrt::Windows::Foundation::Uri& uri) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -57,76 +57,84 @@ bool g_waiting_for_close_confirmation = false;
|
|||||||
bool g_start_in_dark_mode = false;
|
bool g_start_in_dark_mode = false;
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
void NavigateToLocalhostReactServer() {
|
void NavigateToLocalhostReactServer()
|
||||||
// Useful for connecting to instance running in react development server.
|
{
|
||||||
g_webview.Navigate(Uri(hstring(L"http://localhost:8080")));
|
// Useful for connecting to instance running in react development server.
|
||||||
|
g_webview.Navigate(Uri(hstring(L"http://localhost:8080")));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define URI_CONTENT_ID L"\\settings-html"
|
#define URI_CONTENT_ID L"\\settings-html"
|
||||||
|
|
||||||
void NavigateToUri(_In_ LPCWSTR uri_as_string) {
|
void NavigateToUri(_In_ LPCWSTR uri_as_string)
|
||||||
// initialize the base_path for the html content relative to the executable.
|
{
|
||||||
WINRT_VERIFY(GetModuleFileName(nullptr, local_uri_resolver.base_path, MAX_PATH));
|
// initialize the base_path for the html content relative to the executable.
|
||||||
WINRT_VERIFY(PathRemoveFileSpec(local_uri_resolver.base_path));
|
WINRT_VERIFY(GetModuleFileName(nullptr, local_uri_resolver.base_path, MAX_PATH));
|
||||||
wcscat_s(local_uri_resolver.base_path, URI_CONTENT_ID);
|
WINRT_VERIFY(PathRemoveFileSpec(local_uri_resolver.base_path));
|
||||||
Uri url = g_webview.BuildLocalStreamUri(hstring(URI_CONTENT_ID), hstring(uri_as_string));
|
wcscat_s(local_uri_resolver.base_path, URI_CONTENT_ID);
|
||||||
g_webview.NavigateToLocalStreamUri(url, local_uri_resolver);
|
Uri url = g_webview.BuildLocalStreamUri(hstring(URI_CONTENT_ID), hstring(uri_as_string));
|
||||||
|
g_webview.NavigateToLocalStreamUri(url, local_uri_resolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect client_rect_to_bounds_rect(_In_ HWND hwnd) {
|
Rect client_rect_to_bounds_rect(_In_ HWND hwnd)
|
||||||
RECT client_rect = { 0 };
|
{
|
||||||
WINRT_VERIFY(GetClientRect(hwnd, &client_rect));
|
RECT client_rect = { 0 };
|
||||||
|
WINRT_VERIFY(GetClientRect(hwnd, &client_rect));
|
||||||
|
|
||||||
Rect bounds =
|
Rect bounds = {
|
||||||
{
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
static_cast<float>(client_rect.right - client_rect.left),
|
||||||
static_cast<float>(client_rect.right - client_rect.left),
|
static_cast<float>(client_rect.bottom - client_rect.top)
|
||||||
static_cast<float>(client_rect.bottom - client_rect.top)
|
};
|
||||||
};
|
|
||||||
|
|
||||||
return bounds;
|
return bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
void resize_web_view() {
|
void resize_web_view()
|
||||||
Rect bounds = client_rect_to_bounds_rect(g_main_wnd);
|
{
|
||||||
IWebViewControlSite webViewControlSite = (IWebViewControlSite) g_webview;
|
Rect bounds = client_rect_to_bounds_rect(g_main_wnd);
|
||||||
webViewControlSite.Bounds(bounds);
|
IWebViewControlSite webViewControlSite = (IWebViewControlSite)g_webview;
|
||||||
|
webViewControlSite.Bounds(bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SEND_TO_WEBVIEW_MSG 1
|
#define SEND_TO_WEBVIEW_MSG 1
|
||||||
|
|
||||||
void send_message_to_webview(const std::wstring& msg) {
|
void send_message_to_webview(const std::wstring& msg)
|
||||||
if (g_main_wnd != nullptr && wm_data_for_webview != 0) {
|
{
|
||||||
// Allocate the COPYDATASTRUCT and message to pass to the Webview.
|
if (g_main_wnd != nullptr && wm_data_for_webview != 0)
|
||||||
// This is needed in order to use PostMessage, since COM calls to
|
{
|
||||||
// g_webview.InvokeScriptAsync can't be made from other threads.
|
// Allocate the COPYDATASTRUCT and message to pass to the Webview.
|
||||||
|
// This is needed in order to use PostMessage, since COM calls to
|
||||||
|
// g_webview.InvokeScriptAsync can't be made from other threads.
|
||||||
|
|
||||||
PCOPYDATASTRUCT message = new COPYDATASTRUCT();
|
PCOPYDATASTRUCT message = new COPYDATASTRUCT();
|
||||||
DWORD buff_size = (DWORD)(msg.length() + 1);
|
DWORD buff_size = (DWORD)(msg.length() + 1);
|
||||||
|
|
||||||
// 'wnd_static_proc()' will free the buffer allocated here.
|
// 'wnd_static_proc()' will free the buffer allocated here.
|
||||||
wchar_t* buffer = new wchar_t[buff_size];
|
wchar_t* buffer = new wchar_t[buff_size];
|
||||||
|
|
||||||
wcscpy_s(buffer, buff_size, msg.c_str());
|
wcscpy_s(buffer, buff_size, msg.c_str());
|
||||||
message->dwData = SEND_TO_WEBVIEW_MSG;
|
message->dwData = SEND_TO_WEBVIEW_MSG;
|
||||||
message->cbData = buff_size * sizeof(wchar_t);
|
message->cbData = buff_size * sizeof(wchar_t);
|
||||||
message->lpData = (PVOID)buffer;
|
message->lpData = (PVOID)buffer;
|
||||||
WINRT_VERIFY(PostMessage(g_main_wnd, wm_data_for_webview, (WPARAM)g_main_wnd, (LPARAM)message));
|
WINRT_VERIFY(PostMessage(g_main_wnd, wm_data_for_webview, (WPARAM)g_main_wnd, (LPARAM)message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_message_to_powertoys_runner(const std::wstring& msg) {
|
void send_message_to_powertoys_runner(const std::wstring& msg)
|
||||||
if (g_message_pipe != nullptr) {
|
{
|
||||||
g_message_pipe->send(msg);
|
if (g_message_pipe != nullptr)
|
||||||
} else {
|
{
|
||||||
// For Debug purposes, in case the webview is being run alone.
|
g_message_pipe->send(msg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// For Debug purposes, in case the webview is being run alone.
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
MessageBox(g_main_wnd, msg.c_str(), L"From Webview", MB_OK);
|
MessageBox(g_main_wnd, msg.c_str(), L"From Webview", MB_OK);
|
||||||
//throw in some sample data
|
//throw in some sample data
|
||||||
std::wstring debug_settings_info(LR"json({
|
std::wstring debug_settings_info(LR"json({
|
||||||
"general": {
|
"general": {
|
||||||
"startup": true,
|
"startup": true,
|
||||||
"enabled": {
|
"enabled": {
|
||||||
@@ -185,274 +193,326 @@ void send_message_to_powertoys_runner(const std::wstring& msg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})json");
|
})json");
|
||||||
send_message_to_webview(debug_settings_info);
|
send_message_to_webview(debug_settings_info);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void receive_message_from_webview(const std::wstring& msg) {
|
void receive_message_from_webview(const std::wstring& msg)
|
||||||
if (msg[0] == '{') {
|
{
|
||||||
// It's a JSON string, send the message to the PowerToys runner.
|
if (msg[0] == '{')
|
||||||
std::thread(send_message_to_powertoys_runner, msg).detach();
|
{
|
||||||
} else {
|
// It's a JSON string, send the message to the PowerToys runner.
|
||||||
// It's not a JSON string, check for expected control messages.
|
std::thread(send_message_to_powertoys_runner, msg).detach();
|
||||||
if (msg == L"exit") {
|
|
||||||
// WebView confirms the settings application can exit.
|
|
||||||
WINRT_VERIFY(PostMessage(g_main_wnd, wm_destroy_window, 0, 0));
|
|
||||||
} else if (msg == L"cancel-exit") {
|
|
||||||
// WebView canceled the exit request.
|
|
||||||
g_waiting_for_close_confirmation = false;
|
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
}
|
{
|
||||||
|
// It's not a JSON string, check for expected control messages.
|
||||||
void initialize_webview(int nShowCmd) {
|
if (msg == L"exit")
|
||||||
try {
|
{
|
||||||
g_webview_process = WebViewControlProcess();
|
// WebView confirms the settings application can exit.
|
||||||
auto asyncwebview = g_webview_process.CreateWebViewControlAsync((int64_t)g_main_wnd, client_rect_to_bounds_rect(g_main_wnd));
|
WINRT_VERIFY(PostMessage(g_main_wnd, wm_destroy_window, 0, 0));
|
||||||
asyncwebview.Completed([=](IAsyncOperation<WebViewControl> const& sender, AsyncStatus status) {
|
|
||||||
if (status == AsyncStatus::Completed) {
|
|
||||||
WINRT_VERIFY(sender != nullptr);
|
|
||||||
g_webview = sender.GetResults();
|
|
||||||
WINRT_VERIFY(g_webview != nullptr);
|
|
||||||
|
|
||||||
// In order to receive window.external.notify() calls in ScriptNotify
|
|
||||||
g_webview.Settings().IsScriptNotifyAllowed(true);
|
|
||||||
|
|
||||||
g_webview.Settings().IsJavaScriptEnabled(true);
|
|
||||||
|
|
||||||
g_webview.NewWindowRequested([=](IWebViewControl sender_requester, WebViewControlNewWindowRequestedEventArgs args) {
|
|
||||||
// Open the requested link in the default browser registered in the Shell
|
|
||||||
int res = static_cast<int>(reinterpret_cast<uintptr_t>(ShellExecute(nullptr, L"open", args.Uri().AbsoluteUri().c_str(), nullptr, nullptr, SW_SHOWNORMAL)));
|
|
||||||
WINRT_VERIFY(res > 32);
|
|
||||||
});
|
|
||||||
|
|
||||||
g_webview.ContentLoading([=](IWebViewControl sender, WebViewControlContentLoadingEventArgs const& args) {
|
|
||||||
ShowWindow(g_main_wnd, nShowCmd);
|
|
||||||
});
|
|
||||||
g_webview.ScriptNotify([=](IWebViewControl sender_script_notify, WebViewControlScriptNotifyEventArgs const& args_script_notify) {
|
|
||||||
// content called window.external.notify()
|
|
||||||
std::wstring message_sent = args_script_notify.Value().c_str();
|
|
||||||
receive_message_from_webview(message_sent);
|
|
||||||
});
|
|
||||||
g_webview.AcceleratorKeyPressed([&](IWebViewControl sender, WebViewControlAcceleratorKeyPressedEventArgs const& args) {
|
|
||||||
if (args.VirtualKey() == winrt::Windows::System::VirtualKey::F4) {
|
|
||||||
// WebView swallows key-events. Detect Alt-F4 one and close the window manually.
|
|
||||||
const auto _ = g_webview.InvokeScriptAsync(hstring(L"exit_settings_app"), {});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
resize_web_view();
|
|
||||||
#if defined(_DEBUG) && _DEBUG_WITH_LOCALHOST
|
|
||||||
// Navigates to localhost:8080
|
|
||||||
NavigateToLocalhostReactServer();
|
|
||||||
#else
|
|
||||||
// Navigates to settings-html/index.html or index-dark.html
|
|
||||||
NavigateToUri(g_start_in_dark_mode ? L"index-dark.html" : L"index.html");
|
|
||||||
#endif
|
|
||||||
} else if (status == AsyncStatus::Error) {
|
|
||||||
MessageBox(NULL, L"Failed to create the WebView control.\nPlease report the bug to https://github.com/microsoft/PowerToys/issues", L"PowerToys Settings Error", MB_OK);
|
|
||||||
exit(1);
|
|
||||||
} else if (status == AsyncStatus::Started) {
|
|
||||||
// Ignore.
|
|
||||||
} else if (status == AsyncStatus::Canceled) {
|
|
||||||
// Ignore.
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (hresult_error const& e) {
|
|
||||||
WCHAR message[1024] = L"";
|
|
||||||
StringCchPrintf(message, ARRAYSIZE(message), L"failed: %ls", e.message().c_str());
|
|
||||||
MessageBox(g_main_wnd, message, L"Error", MB_OK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LRESULT CALLBACK wnd_proc_static(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
|
|
||||||
switch (message) {
|
|
||||||
case WM_CLOSE:
|
|
||||||
if (g_waiting_for_close_confirmation) {
|
|
||||||
// If another WM_CLOSE is received while waiting for webview confirmation,
|
|
||||||
// allow DefWindowProc to be called and destroy the window.
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
// Allow user to confirm exit in the WebView in case there's possible data loss.
|
|
||||||
g_waiting_for_close_confirmation = true;
|
|
||||||
if (g_webview != nullptr) {
|
|
||||||
const auto _ = g_webview.InvokeScriptAsync(hstring(L"exit_settings_app"), {});
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
case WM_DESTROY:
|
|
||||||
PostQuitMessage(0);
|
|
||||||
break;
|
|
||||||
case WM_SIZE:
|
|
||||||
if (g_webview != nullptr) {
|
|
||||||
resize_web_view();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case WM_CREATE:
|
|
||||||
wm_data_for_webview = RegisterWindowMessageW(L"PTSettingsCopyDataWebView");
|
|
||||||
wm_destroy_window = RegisterWindowMessageW(L"PTSettingsParentTerminated");
|
|
||||||
break;
|
|
||||||
case WM_DPICHANGED:
|
|
||||||
{
|
|
||||||
// Resize the window using the suggested rect
|
|
||||||
RECT* const prcNewWindow = (RECT*)lParam;
|
|
||||||
SetWindowPos(hWnd,
|
|
||||||
nullptr,
|
|
||||||
prcNewWindow->left,
|
|
||||||
prcNewWindow->top,
|
|
||||||
prcNewWindow->right - prcNewWindow->left,
|
|
||||||
prcNewWindow->bottom - prcNewWindow->top,
|
|
||||||
SWP_NOZORDER | SWP_NOACTIVATE);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case WM_NCCREATE:
|
|
||||||
{
|
|
||||||
// Enable auto-resizing the title bar
|
|
||||||
EnableNonClientDpiScaling(hWnd);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (message == wm_data_for_webview) {
|
|
||||||
PCOPYDATASTRUCT msg = (PCOPYDATASTRUCT)lParam;
|
|
||||||
if (msg->dwData == SEND_TO_WEBVIEW_MSG) {
|
|
||||||
wchar_t* json_message = (wchar_t*)(msg->lpData);
|
|
||||||
if (g_webview != nullptr) {
|
|
||||||
const auto _ = g_webview.InvokeScriptAsync(hstring(L"receive_from_settings_app"), { hstring(json_message) });
|
|
||||||
}
|
}
|
||||||
delete[] json_message;
|
else if (msg == L"cancel-exit")
|
||||||
}
|
{
|
||||||
// wnd_proc_static is responsible for freeing memory.
|
// WebView canceled the exit request.
|
||||||
delete msg;
|
g_waiting_for_close_confirmation = false;
|
||||||
} else {
|
}
|
||||||
if (message == wm_destroy_window) {
|
}
|
||||||
DestroyWindow(hWnd);
|
}
|
||||||
}
|
|
||||||
|
void initialize_webview(int nShowCmd)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
g_webview_process = WebViewControlProcess();
|
||||||
|
auto asyncwebview = g_webview_process.CreateWebViewControlAsync((int64_t)g_main_wnd, client_rect_to_bounds_rect(g_main_wnd));
|
||||||
|
asyncwebview.Completed([=](IAsyncOperation<WebViewControl> const& sender, AsyncStatus status) {
|
||||||
|
if (status == AsyncStatus::Completed)
|
||||||
|
{
|
||||||
|
WINRT_VERIFY(sender != nullptr);
|
||||||
|
g_webview = sender.GetResults();
|
||||||
|
WINRT_VERIFY(g_webview != nullptr);
|
||||||
|
|
||||||
|
// In order to receive window.external.notify() calls in ScriptNotify
|
||||||
|
g_webview.Settings().IsScriptNotifyAllowed(true);
|
||||||
|
|
||||||
|
g_webview.Settings().IsJavaScriptEnabled(true);
|
||||||
|
|
||||||
|
g_webview.NewWindowRequested([=](IWebViewControl sender_requester, WebViewControlNewWindowRequestedEventArgs args) {
|
||||||
|
// Open the requested link in the default browser registered in the Shell
|
||||||
|
int res = static_cast<int>(reinterpret_cast<uintptr_t>(ShellExecute(nullptr, L"open", args.Uri().AbsoluteUri().c_str(), nullptr, nullptr, SW_SHOWNORMAL)));
|
||||||
|
WINRT_VERIFY(res > 32);
|
||||||
|
});
|
||||||
|
|
||||||
|
g_webview.ContentLoading([=](IWebViewControl sender, WebViewControlContentLoadingEventArgs const& args) {
|
||||||
|
ShowWindow(g_main_wnd, nShowCmd);
|
||||||
|
});
|
||||||
|
g_webview.ScriptNotify([=](IWebViewControl sender_script_notify, WebViewControlScriptNotifyEventArgs const& args_script_notify) {
|
||||||
|
// content called window.external.notify()
|
||||||
|
std::wstring message_sent = args_script_notify.Value().c_str();
|
||||||
|
receive_message_from_webview(message_sent);
|
||||||
|
});
|
||||||
|
g_webview.AcceleratorKeyPressed([&](IWebViewControl sender, WebViewControlAcceleratorKeyPressedEventArgs const& args) {
|
||||||
|
if (args.VirtualKey() == winrt::Windows::System::VirtualKey::F4)
|
||||||
|
{
|
||||||
|
// WebView swallows key-events. Detect Alt-F4 one and close the window manually.
|
||||||
|
const auto _ = g_webview.InvokeScriptAsync(hstring(L"exit_settings_app"), {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
resize_web_view();
|
||||||
|
#if defined(_DEBUG) && _DEBUG_WITH_LOCALHOST
|
||||||
|
// Navigates to localhost:8080
|
||||||
|
NavigateToLocalhostReactServer();
|
||||||
|
#else
|
||||||
|
// Navigates to settings-html/index.html or index-dark.html
|
||||||
|
NavigateToUri(g_start_in_dark_mode ? L"index-dark.html" : L"index.html");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else if (status == AsyncStatus::Error)
|
||||||
|
{
|
||||||
|
MessageBox(NULL, L"Failed to create the WebView control.\nPlease report the bug to https://github.com/microsoft/PowerToys/issues", L"PowerToys Settings Error", MB_OK);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
else if (status == AsyncStatus::Started)
|
||||||
|
{
|
||||||
|
// Ignore.
|
||||||
|
}
|
||||||
|
else if (status == AsyncStatus::Canceled)
|
||||||
|
{
|
||||||
|
// Ignore.
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (hresult_error const& e)
|
||||||
|
{
|
||||||
|
WCHAR message[1024] = L"";
|
||||||
|
StringCchPrintf(message, ARRAYSIZE(message), L"failed: %ls", e.message().c_str());
|
||||||
|
MessageBox(g_main_wnd, message, L"Error", MB_OK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT CALLBACK wnd_proc_static(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
switch (message)
|
||||||
|
{
|
||||||
|
case WM_CLOSE:
|
||||||
|
if (g_waiting_for_close_confirmation)
|
||||||
|
{
|
||||||
|
// If another WM_CLOSE is received while waiting for webview confirmation,
|
||||||
|
// allow DefWindowProc to be called and destroy the window.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Allow user to confirm exit in the WebView in case there's possible data loss.
|
||||||
|
g_waiting_for_close_confirmation = true;
|
||||||
|
if (g_webview != nullptr)
|
||||||
|
{
|
||||||
|
const auto _ = g_webview.InvokeScriptAsync(hstring(L"exit_settings_app"), {});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case WM_DESTROY:
|
||||||
|
PostQuitMessage(0);
|
||||||
|
break;
|
||||||
|
case WM_SIZE:
|
||||||
|
if (g_webview != nullptr)
|
||||||
|
{
|
||||||
|
resize_web_view();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WM_CREATE:
|
||||||
|
wm_data_for_webview = RegisterWindowMessageW(L"PTSettingsCopyDataWebView");
|
||||||
|
wm_destroy_window = RegisterWindowMessageW(L"PTSettingsParentTerminated");
|
||||||
|
break;
|
||||||
|
case WM_DPICHANGED: {
|
||||||
|
// Resize the window using the suggested rect
|
||||||
|
RECT* const prcNewWindow = (RECT*)lParam;
|
||||||
|
SetWindowPos(hWnd,
|
||||||
|
nullptr,
|
||||||
|
prcNewWindow->left,
|
||||||
|
prcNewWindow->top,
|
||||||
|
prcNewWindow->right - prcNewWindow->left,
|
||||||
|
prcNewWindow->bottom - prcNewWindow->top,
|
||||||
|
SWP_NOZORDER | SWP_NOACTIVATE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
case WM_NCCREATE: {
|
||||||
return DefWindowProc(hWnd, message, wParam, lParam);;
|
// Enable auto-resizing the title bar
|
||||||
}
|
EnableNonClientDpiScaling(hWnd);
|
||||||
|
|
||||||
void register_classes(HINSTANCE hInstance) {
|
|
||||||
WNDCLASSEXW wcex;
|
|
||||||
wcex.cbSize = sizeof(WNDCLASSEX);
|
|
||||||
|
|
||||||
wcex.style = 0;
|
|
||||||
wcex.lpfnWndProc = wnd_proc_static;
|
|
||||||
wcex.cbClsExtra = 0;
|
|
||||||
wcex.cbWndExtra = 0;
|
|
||||||
wcex.hInstance = hInstance;
|
|
||||||
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(APPICON));
|
|
||||||
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
|
||||||
wcex.hbrBackground = g_start_in_dark_mode ? CreateSolidBrush(0) : (HBRUSH)(COLOR_WINDOW + 1);
|
|
||||||
wcex.lpszMenuName = nullptr;
|
|
||||||
wcex.lpszClassName = L"PTSettingsClass";
|
|
||||||
wcex.hIconSm = nullptr;
|
|
||||||
|
|
||||||
WINRT_VERIFY(RegisterClassExW(&wcex));
|
|
||||||
}
|
|
||||||
|
|
||||||
HWND create_main_window(HINSTANCE hInstance) {
|
|
||||||
RECT desktopRect;
|
|
||||||
const HWND hDesktop = GetDesktopWindow();
|
|
||||||
WINRT_VERIFY(hDesktop != nullptr);
|
|
||||||
WINRT_VERIFY(GetWindowRect(hDesktop, &desktopRect));
|
|
||||||
|
|
||||||
int wind_width = 1024;
|
|
||||||
int wind_height = 700;
|
|
||||||
DPIAware::Convert(nullptr, wind_width, wind_height);
|
|
||||||
|
|
||||||
return CreateWindowW(
|
|
||||||
L"PTSettingsClass",
|
|
||||||
L"PowerToys Settings",
|
|
||||||
WS_OVERLAPPEDWINDOW,
|
|
||||||
(desktopRect.right - wind_width)/2,
|
|
||||||
(desktopRect.bottom - wind_height)/2,
|
|
||||||
wind_width,
|
|
||||||
wind_height,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
hInstance,
|
|
||||||
nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void wait_on_parent_process_thread(DWORD pid) {
|
|
||||||
HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, pid);
|
|
||||||
if (process != nullptr) {
|
|
||||||
if (WaitForSingleObject(process, INFINITE) == WAIT_OBJECT_0) {
|
|
||||||
// If it's possible to detect when the PowerToys process terminates, message the main window.
|
|
||||||
CloseHandle(process);
|
|
||||||
if (g_main_wnd) {
|
|
||||||
WINRT_VERIFY(PostMessage(g_main_wnd, wm_destroy_window, 0, 0));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
CloseHandle(process);
|
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
|
default:
|
||||||
|
if (message == wm_data_for_webview)
|
||||||
|
{
|
||||||
|
PCOPYDATASTRUCT msg = (PCOPYDATASTRUCT)lParam;
|
||||||
|
if (msg->dwData == SEND_TO_WEBVIEW_MSG)
|
||||||
|
{
|
||||||
|
wchar_t* json_message = (wchar_t*)(msg->lpData);
|
||||||
|
if (g_webview != nullptr)
|
||||||
|
{
|
||||||
|
const auto _ = g_webview.InvokeScriptAsync(hstring(L"receive_from_settings_app"), { hstring(json_message) });
|
||||||
|
}
|
||||||
|
delete[] json_message;
|
||||||
|
}
|
||||||
|
// wnd_proc_static is responsible for freeing memory.
|
||||||
|
delete msg;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (message == wm_destroy_window)
|
||||||
|
{
|
||||||
|
DestroyWindow(hWnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
void quit_when_parent_terminates(std::wstring parent_pid) {
|
void register_classes(HINSTANCE hInstance)
|
||||||
DWORD pid = std::stol(parent_pid);
|
{
|
||||||
std::thread(wait_on_parent_process_thread, pid).detach();
|
WNDCLASSEXW wcex;
|
||||||
|
wcex.cbSize = sizeof(WNDCLASSEX);
|
||||||
|
|
||||||
|
wcex.style = 0;
|
||||||
|
wcex.lpfnWndProc = wnd_proc_static;
|
||||||
|
wcex.cbClsExtra = 0;
|
||||||
|
wcex.cbWndExtra = 0;
|
||||||
|
wcex.hInstance = hInstance;
|
||||||
|
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(APPICON));
|
||||||
|
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||||
|
wcex.hbrBackground = g_start_in_dark_mode ? CreateSolidBrush(0) : (HBRUSH)(COLOR_WINDOW + 1);
|
||||||
|
wcex.lpszMenuName = nullptr;
|
||||||
|
wcex.lpszClassName = L"PTSettingsClass";
|
||||||
|
wcex.hIconSm = nullptr;
|
||||||
|
|
||||||
|
WINRT_VERIFY(RegisterClassExW(&wcex));
|
||||||
|
}
|
||||||
|
|
||||||
|
HWND create_main_window(HINSTANCE hInstance)
|
||||||
|
{
|
||||||
|
RECT desktopRect;
|
||||||
|
const HWND hDesktop = GetDesktopWindow();
|
||||||
|
WINRT_VERIFY(hDesktop != nullptr);
|
||||||
|
WINRT_VERIFY(GetWindowRect(hDesktop, &desktopRect));
|
||||||
|
|
||||||
|
int wind_width = 1024;
|
||||||
|
int wind_height = 700;
|
||||||
|
DPIAware::Convert(nullptr, wind_width, wind_height);
|
||||||
|
|
||||||
|
return CreateWindowW(
|
||||||
|
L"PTSettingsClass",
|
||||||
|
L"PowerToys Settings",
|
||||||
|
WS_OVERLAPPEDWINDOW,
|
||||||
|
(desktopRect.right - wind_width) / 2,
|
||||||
|
(desktopRect.bottom - wind_height) / 2,
|
||||||
|
wind_width,
|
||||||
|
wind_height,
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
hInstance,
|
||||||
|
nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wait_on_parent_process_thread(DWORD pid)
|
||||||
|
{
|
||||||
|
HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, pid);
|
||||||
|
if (process != nullptr)
|
||||||
|
{
|
||||||
|
if (WaitForSingleObject(process, INFINITE) == WAIT_OBJECT_0)
|
||||||
|
{
|
||||||
|
// If it's possible to detect when the PowerToys process terminates, message the main window.
|
||||||
|
CloseHandle(process);
|
||||||
|
if (g_main_wnd)
|
||||||
|
{
|
||||||
|
WINRT_VERIFY(PostMessage(g_main_wnd, wm_destroy_window, 0, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CloseHandle(process);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void quit_when_parent_terminates(std::wstring parent_pid)
|
||||||
|
{
|
||||||
|
DWORD pid = std::stol(parent_pid);
|
||||||
|
std::thread(wait_on_parent_process_thread, pid).detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse arguments: initialize two-way IPC message pipe and if settings window is to be started
|
// Parse arguments: initialize two-way IPC message pipe and if settings window is to be started
|
||||||
// in dark mode.
|
// in dark mode.
|
||||||
void parse_args() {
|
void parse_args()
|
||||||
// Expected calling arguments:
|
{
|
||||||
// [0] - This executable's path.
|
// Expected calling arguments:
|
||||||
// [1] - PowerToys pipe server.
|
// [0] - This executable's path.
|
||||||
// [2] - Settings pipe server.
|
// [1] - PowerToys pipe server.
|
||||||
// [3] - PowerToys process pid.
|
// [2] - Settings pipe server.
|
||||||
// [4] - optional "dark" parameter if the settings window is to be started in dark mode
|
// [3] - PowerToys process pid.
|
||||||
LPWSTR *argument_list;
|
// [4] - optional "dark" parameter if the settings window is to be started in dark mode
|
||||||
int n_args;
|
LPWSTR* argument_list;
|
||||||
|
int n_args;
|
||||||
|
|
||||||
argument_list = CommandLineToArgvW(GetCommandLineW(), &n_args);
|
argument_list = CommandLineToArgvW(GetCommandLineW(), &n_args);
|
||||||
if (n_args > 3) {
|
if (n_args > 3)
|
||||||
g_message_pipe = new TwoWayPipeMessageIPC(std::wstring(argument_list[2]), std::wstring(argument_list[1]), send_message_to_webview);
|
{
|
||||||
g_message_pipe->start(nullptr);
|
g_message_pipe = new TwoWayPipeMessageIPC(std::wstring(argument_list[2]), std::wstring(argument_list[1]), send_message_to_webview);
|
||||||
quit_when_parent_terminates(std::wstring(argument_list[3]));
|
g_message_pipe->start(nullptr);
|
||||||
} else {
|
quit_when_parent_terminates(std::wstring(argument_list[3]));
|
||||||
#ifndef _DEBUG
|
|
||||||
MessageBox(nullptr, L"This executable isn't supposed to be called as a stand-alone process", L"Error running settings", MB_OK);
|
|
||||||
exit(1);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
if (n_args > 4) {
|
|
||||||
g_start_in_dark_mode = wcscmp(argument_list[4], L"dark") == 0;
|
|
||||||
}
|
|
||||||
LocalFree(argument_list);
|
|
||||||
}
|
|
||||||
|
|
||||||
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) {
|
|
||||||
CoInitialize(nullptr);
|
|
||||||
|
|
||||||
if (is_process_elevated()) {
|
|
||||||
if (!drop_elevated_privileges()) {
|
|
||||||
MessageBox(NULL, L"Failed to drop admin privileges.\nPlease report the bug to https://github.com/microsoft/PowerToys/issues", L"PowerToys Settings Error", MB_OK);
|
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
g_hinst = hInstance;
|
#ifndef _DEBUG
|
||||||
parse_args();
|
MessageBox(nullptr, L"This executable isn't supposed to be called as a stand-alone process", L"Error running settings", MB_OK);
|
||||||
register_classes(hInstance);
|
exit(1);
|
||||||
g_main_wnd = create_main_window(hInstance);
|
#endif
|
||||||
if (g_main_wnd == nullptr) {
|
}
|
||||||
MessageBox(NULL, L"Failed to create main window.\nPlease report the bug to https://github.com/microsoft/PowerToys/issues", L"PowerToys Settings Error", MB_OK);
|
if (n_args > 4)
|
||||||
exit(1);
|
{
|
||||||
}
|
g_start_in_dark_mode = wcscmp(argument_list[4], L"dark") == 0;
|
||||||
initialize_webview(nShowCmd);
|
}
|
||||||
|
LocalFree(argument_list);
|
||||||
// Main message loop.
|
}
|
||||||
MSG msg;
|
|
||||||
while (GetMessage(&msg, nullptr, 0, 0)) {
|
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd)
|
||||||
TranslateMessage(&msg);
|
{
|
||||||
DispatchMessage(&msg);
|
CoInitialize(nullptr);
|
||||||
}
|
|
||||||
|
if (is_process_elevated())
|
||||||
return (int)msg.wParam;
|
{
|
||||||
|
if (!drop_elevated_privileges())
|
||||||
|
{
|
||||||
|
MessageBox(NULL, L"Failed to drop admin privileges.\nPlease report the bug to https://github.com/microsoft/PowerToys/issues", L"PowerToys Settings Error", MB_OK);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_hinst = hInstance;
|
||||||
|
parse_args();
|
||||||
|
register_classes(hInstance);
|
||||||
|
g_main_wnd = create_main_window(hInstance);
|
||||||
|
if (g_main_wnd == nullptr)
|
||||||
|
{
|
||||||
|
MessageBox(NULL, L"Failed to create main window.\nPlease report the bug to https://github.com/microsoft/PowerToys/issues", L"PowerToys Settings Error", MB_OK);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
initialize_webview(nShowCmd);
|
||||||
|
|
||||||
|
// Main message loop.
|
||||||
|
MSG msg;
|
||||||
|
while (GetMessage(&msg, nullptr, 0, 0))
|
||||||
|
{
|
||||||
|
TranslateMessage(&msg);
|
||||||
|
DispatchMessage(&msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)msg.wParam;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user