commit abff7464e032d6d7b30cb6499734016fbaeaac4d Author: fanlusky <1730976608@qq.com> Date: Tue Mar 25 15:27:42 2025 +0800 first commit diff --git a/server/.clang-format b/server/.clang-format new file mode 100644 index 00000000..6f12b1ec --- /dev/null +++ b/server/.clang-format @@ -0,0 +1,6 @@ +--- +BasedOnStyle: Microsoft +IndentWidth: 4 + +SortIncludes: true +ColumnLimit: 80 diff --git a/server/.clangd b/server/.clangd new file mode 100644 index 00000000..f4f6ed6c --- /dev/null +++ b/server/.clangd @@ -0,0 +1,18 @@ +CompileFlags: + Add: [ + -DUNICODE, + -D_UNICODE, + -DFANY_DEBUG, + "-std=c++17", + "-IC:\\Users\\SonnyCalcr\\EDisk\\CppCodes\\Win32Codes\\DirectWriteExperiment\\includes", # you should replace this to your own include path of this project + "-IC:\\Users\\SonnyCalcr\\EDisk\\CppCodes\\Win32Codes\\DirectWriteExperiment\\fmt\\include", # you should replace this to your own include path of fmt + "-IC:\\Users\\SonnyCalcr\\.nuget\\packages\\microsoft.web.webview2\\1.0.3124.44\\build\\native\\include", + "-IC:\\Users\\SonnyCalcr\\.nuget\\packages\\microsoft.windows.implementationlibrary\\1.0.240803.1\\include", + -Wno-microsoft-goto, + -Wno-nonportable-include-path, + -Wno-microsoft-extra-qualification, + # -Wno-unused-value, + ] # ignore some specific warnings + CompilationDatabase: build/ # Search build/ directory for compile_commands.json +# Diagnostics: +# UnusedIncludes: None # ignore unused includes diff --git a/server/.gitignore b/server/.gitignore new file mode 100644 index 00000000..875136fb --- /dev/null +++ b/server/.gitignore @@ -0,0 +1,2 @@ +build/ +log.* \ No newline at end of file diff --git a/server/.gitmodules b/server/.gitmodules new file mode 100644 index 00000000..a00d0827 --- /dev/null +++ b/server/.gitmodules @@ -0,0 +1,3 @@ +[submodule "fmt"] + path = fmt + url = https://github.com/fmtlib/fmt.git diff --git a/server/.vscode/custom.code-snippets b/server/.vscode/custom.code-snippets new file mode 100644 index 00000000..f62f848a --- /dev/null +++ b/server/.vscode/custom.code-snippets @@ -0,0 +1,11 @@ +{ + "Debug snippets": { + "scope": "c,cpp", + "prefix": "dbug", + "body": [ + "#ifdef FANY_DEBUG", + "#endif", + ], + "description": "Add debug macro" + } +} \ No newline at end of file diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt new file mode 100644 index 00000000..74a1542a --- /dev/null +++ b/server/CMakeLists.txt @@ -0,0 +1,41 @@ +cmake_minimum_required(VERSION 3.25.0) + +set(PROJECT_NAME "fanimeui") + +project(${PROJECT_NAME} VERSION 1.0) + +add_subdirectory(fmt) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + + +add_definitions( + /D_UNICODE=1 + /DUNICODE=1 +) + +# add_definitions( +# /DFANY_DEBUG=1 +# ) + +include_directories("./includes/") + +set(HEADER_FILES + ./includes/main.h +) + +set(SRC_FILES + "src/webview2/MainWindow.cpp" +) + +add_executable(fanimeui WIN32 + ${HEADER_FILES} + ${SRC_FILES} +) + +set_target_properties(${PROJECT_NAME} PROPERTIES VS_GLOBAL_WebView2LoaderPreference "Static") +set_target_properties(${PROJECT_NAME} PROPERTIES VS_USER_PROPS "${CMAKE_SOURCE_DIR}/FanIME_UI.props") +set_target_properties(${PROJECT_NAME} PROPERTIES VS_PACKAGE_REFERENCES "Microsoft.Web.WebView2_1.0.3124.44;Microsoft.Windows.ImplementationLibrary_1.0.240803.1") + +target_link_libraries(${PROJECT_NAME} fmt::fmt) \ No newline at end of file diff --git a/server/FanIME_UI.props b/server/FanIME_UI.props new file mode 100644 index 00000000..bb243e0e --- /dev/null +++ b/server/FanIME_UI.props @@ -0,0 +1,29 @@ + + + + + + + + + + + + PackageReference + true + + + x64 + native + + + $(UserProfile)\.nuget\packages + + + Static + + + native,Version=v0.0 + win;win-x86;win-x64;win-arm;win-arm64 + + \ No newline at end of file diff --git a/server/README.md b/server/README.md new file mode 100644 index 00000000..7873fa48 --- /dev/null +++ b/server/README.md @@ -0,0 +1,7 @@ +# Win32 direct2d and some webview experiment + +Try to figure out all needed rendering things about Windows TSF input method. + +![](https://s2.loli.net/2025/03/20/4ZdbHLWAeBJOqIo.png) + +![](https://s2.loli.net/2025/03/24/n1X7RZr9LjEWO23.png) diff --git a/server/assets/background.jpg b/server/assets/background.jpg new file mode 100644 index 00000000..ca32989a Binary files /dev/null and b/server/assets/background.jpg differ diff --git a/server/fmt b/server/fmt new file mode 160000 index 00000000..b2dfcb2b --- /dev/null +++ b/server/fmt @@ -0,0 +1 @@ +Subproject commit b2dfcb2b805f8670d5a1fa4ed0a70b3c92c6a94e diff --git a/server/html/horizontal_candidate_window_dark.html b/server/html/horizontal_candidate_window_dark.html new file mode 100644 index 00000000..2f17d976 --- /dev/null +++ b/server/html/horizontal_candidate_window_dark.html @@ -0,0 +1,142 @@ + + + + + + + 横向候选框 + + + + +
+
+
+
ni'uo
+
+
+
+ +
+
+
1. 你说什么
+
+
+ +
+
+
2. 笔画
+
+
+ +
+
+
3. 量子
+
+
+ +
+
+
4. 牛魔
+
+
+ +
+
+
5. 仙人
+
+
+ +
+
+
6. 可恨
+
+
+ +
+
+
7. 木槿
+
+
+ +
+
+
8. 无量
+
+
+
+
+ + + \ No newline at end of file diff --git a/server/html/vertical_candidate_window_dark.html b/server/html/vertical_candidate_window_dark.html new file mode 100644 index 00000000..47936cc6 --- /dev/null +++ b/server/html/vertical_candidate_window_dark.html @@ -0,0 +1,143 @@ + + + + + + + 垂直候选框 + + + + + + +
+ +
+
ni'uo
+
+ +
+
+
1. 你说
+
+
+ +
+
+
2. 笔画
+
+
+ +
+
+
3. 量子
+
+
+ +
+
+
4. 牛魔
+
+
+ +
+
+
5. 仙人
+
+
+ +
+
+
6. 可恨
+
+
+ +
+
+
7. 木槿
+
+
+ +
+
+
8. 无量
+
+
+ +
+ + + \ No newline at end of file diff --git a/server/includes/main.h b/server/includes/main.h new file mode 100644 index 00000000..64fd1b18 --- /dev/null +++ b/server/includes/main.h @@ -0,0 +1,3 @@ +#include +#include +#include diff --git a/server/scripts/lcompile.ps1 b/server/scripts/lcompile.ps1 new file mode 100644 index 00000000..7cdd23e9 --- /dev/null +++ b/server/scripts/lcompile.ps1 @@ -0,0 +1,26 @@ +# generate compile to exe files +$currentDirectory = Get-Location +$cmakeListsPath = Join-Path -Path $currentDirectory -ChildPath "CMakeLists.txt" + +if (-not (Test-Path $cmakeListsPath)) +{ + Write-Host("No CMakeLists.txt in current directory, please check.") + return +} + +Write-Host "Start generating and compiling..." + +$buildFolderPath = ".\build" + +if (-not (Test-Path $buildFolderPath)) +{ + New-Item -ItemType Directory -Path $buildFolderPath | Out-Null + Write-Host "build folder created." +} + +cmake -G "Visual Studio 17 2022" -A x64 -S . -B ./build/ + +if ($LASTEXITCODE -eq 0) +{ + cmake --build ./build/ --config DEBUG +} diff --git a/server/scripts/llaunch.ps1 b/server/scripts/llaunch.ps1 new file mode 100644 index 00000000..6d6e1c3e --- /dev/null +++ b/server/scripts/llaunch.ps1 @@ -0,0 +1,65 @@ +# +# generate, compile and run exe files +# + +function getExePathFromCMakeLists() +{ + $content = Get-Content -Path "./CMakeLists.txt" + $lastLine = "" + $contentContainedExeName = "" + foreach($line in $content) + { + if ($line.StartsWith("add_executable")) + { + $index = $line.IndexOf("(") + $contentContainedExeName = $line.Substring($index + 1) + if ([string]::IsNullOrEmpty($contentContainedExeName)) + { + $lastLine = $line + continue + } + break + } elseif ($lastLine.StartsWith("add_executable")) + { + $contentContainedExeName = $line + break + } + $lastLine = $line + } + $result = -split $contentContainedExeName + $exePath = "./build/DEBUG/" + $result[0] + ".exe" + return $exePath +} + +$currentDirectory = Get-Location +$cmakeListsPath = Join-Path -Path $currentDirectory -ChildPath "CMakeLists.txt" + +if (-not (Test-Path $cmakeListsPath)) +{ + Write-Host("No CMakeLists.txt in current directory, please check.") + return +} + +Write-Host "Start generating and compiling..." + +$buildFolderPath = ".\build" + +if (-not (Test-Path $buildFolderPath)) +{ + New-Item -ItemType Directory -Path $buildFolderPath | Out-Null + Write-Host "build folder created." +} + +cmake -G "Visual Studio 17 2022" -A x64 -S . -B ./build/ + +if ($LASTEXITCODE -eq 0) +{ + cmake --build ./build/ --config DEBUG + if ($LASTEXITCODE -eq 0) + { + $exePath = getExePathFromCMakeLists + Write-Host "start running as follows..." + Write-Host "==================================================" + Invoke-Expression $exePath + } +} diff --git a/server/scripts/lrun.ps1 b/server/scripts/lrun.ps1 new file mode 100644 index 00000000..4b3d149f --- /dev/null +++ b/server/scripts/lrun.ps1 @@ -0,0 +1,38 @@ +# +# run exe file that has already been compiled before +# + + +function getExePathFromCMakeLists() +{ + $content = Get-Content -Path "./CMakeLists.txt" + $lastLine = "" + $contentContainedExeName = "" + foreach($line in $content) + { + if ($line.StartsWith("add_executable")) + { + $index = $line.IndexOf("(") + $contentContainedExeName = $line.Substring($index + 1) + if ([string]::IsNullOrEmpty($contentContainedExeName)) + { + $lastLine = $line + continue + } + break + } elseif ($lastLine.StartsWith("add_executable")) + { + $contentContainedExeName = $line + break + } + $lastLine = $line + } + $result = -split $contentContainedExeName + $exePath = "./build/DEBUG/" + $result[0] + ".exe" + return $exePath +} + +$exePath = getExePathFromCMakeLists +Write-Host "start running as follows..." +Write-Host "==================================================" +Invoke-Expression $exePath diff --git a/server/src/main.cpp b/server/src/main.cpp new file mode 100644 index 00000000..224fe12c --- /dev/null +++ b/server/src/main.cpp @@ -0,0 +1,252 @@ +#include +#include +#include +#include +#include +#include + +#pragma comment(lib, "d2d1") +#pragma comment(lib, "dwrite") +#pragma comment(lib, "dwmapi.lib") + +ID2D1Factory *pD2DFactory = nullptr; +ID2D1HwndRenderTarget *pRenderTarget = nullptr; +ID2D1SolidColorBrush *pBrush = nullptr; +IDWriteFactory *pDWriteFactory = nullptr; +IDWriteTextFormat *pTextFormat = nullptr; + +LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); + +FLOAT GetWindowScale(HWND hwnd); + +bool InitD2DAndDWrite() +{ + + HRESULT hr = D2D1CreateFactory( // + D2D1_FACTORY_TYPE_SINGLE_THREADED, // + &pD2DFactory // + ); + if (FAILED(hr)) + return false; + + hr = DWriteCreateFactory( // + DWRITE_FACTORY_TYPE_SHARED, // + __uuidof(IDWriteFactory), // + reinterpret_cast(&pDWriteFactory) // + ); + if (FAILED(hr)) + return false; + + hr = pDWriteFactory->CreateTextFormat(L"Segoe UI", // + nullptr, // + DWRITE_FONT_WEIGHT_NORMAL, // + DWRITE_FONT_STYLE_NORMAL, // + DWRITE_FONT_STRETCH_NORMAL, // + 24.0f, // + L"en-us", // + &pTextFormat // + ); + if (FAILED(hr)) + return false; + + return true; +} + +bool InitD2DRenderTarget(HWND hwnd) +{ + if (!pD2DFactory) + return false; + + RECT rc; + GetClientRect(hwnd, &rc); + + auto renderTargetProps = D2D1::RenderTargetProperties( // + D2D1_RENDER_TARGET_TYPE_DEFAULT, // + D2D1::PixelFormat( // + DXGI_FORMAT_UNKNOWN, // + D2D1_ALPHA_MODE_PREMULTIPLIED // + ) // + ); + auto hwndRenderTargetProps = D2D1::HwndRenderTargetProperties( // + hwnd, // + D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top), // + D2D1_PRESENT_OPTIONS_IMMEDIATELY // + ); + + HRESULT hr = pD2DFactory->CreateHwndRenderTarget( // + renderTargetProps, // + hwndRenderTargetProps, // + &pRenderTarget // + ); + + if (SUCCEEDED(hr)) + { + hr = pRenderTarget->CreateSolidColorBrush( // + D2D1::ColorF(D2D1::ColorF::White), // + &pBrush // + ); + } + + return SUCCEEDED(hr); +} + +void OnPaint(HWND hwnd) +{ + if (!pRenderTarget) + return; + + pRenderTarget->BeginDraw(); + + // Clear baakground to transparent + // pRenderTarget->Clear(D2D1::ColorF(0, 0, 0, 0)); + pRenderTarget->Clear(D2D1::ColorF(25 / 255.0, 25 / 255.0, 25 / 255.0, 0.5)); + + /* + FLOAT scale = GetWindowScale(hwnd); + + D2D1_RECT_F borderRect = D2D1::RectF( // + static_cast(0), // + static_cast(0), // + static_cast(600 / scale), // + static_cast(600 / scale) // + ); + pBrush->SetColor(D2D1::ColorF(D2D1::ColorF::White, 1.0f)); +E + pRenderTarget->FillRectangle(D2D1::RectF(50, 50, 400, 300), pBrush) + + pBrush->SetColor(D2D1::ColorF(D2D1::ColorF::Red, 1.0f)); + + pRenderTarget->FillRectangle(D2D1::RectF(200, 200, 500, 400), pBrush); + + pBrush->SetColor(D2D1::ColorF(D2D1::ColorF::Green, 0.3f)); + + pRenderTarget->FillRectangle(D2D1::RectF(300, 100, 600, 250), pBrush); + + pBrush->SetColor(D2D1::ColorF(D2D1::ColorF::White, 1.0f)); // + pRenderTarget->DrawTextW( // + L"Transparent Window and Direct2D", // + 31, // + pTextFormat, // + D2D1::RectF(5, 5, 600, 200), // + pBrush // + ); + */ + + HRESULT hr = pRenderTarget->EndDraw(); + if (hr == D2DERR_RECREATE_TARGET) + { + pRenderTarget->Release(); + InitD2DRenderTarget(hwnd); + } +} + +HWND CreateTransparentWindow(HINSTANCE hInstance) +{ + WNDCLASS wc = {0}; + wc.lpfnWndProc = WndProc; + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.hInstance = hInstance; + wc.lpszClassName = L"TransparentWindowClass"; + + if (!RegisterClass(&wc)) + return nullptr; + + HWND hwnd = CreateWindowEx(WS_EX_LAYERED, // + wc.lpszClassName, // + L"TransparentWindow", // + WS_POPUP, // + 100, 100, 600, 600, // + nullptr, nullptr, hInstance, nullptr); + + if (hwnd) + { + SetLayeredWindowAttributes(hwnd, 0, 255, LWA_ALPHA); + + MARGINS mar = {-1}; + DwmExtendFrameIntoClientArea(hwnd, &mar); + } + + return hwnd; +} + +LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_CREATE: + InitD2DRenderTarget(hwnd); + return 0; + + case WM_PAINT: + case WM_DISPLAYCHANGE: + OnPaint(hwnd); + ValidateRect(hwnd, nullptr); + return 0; + case WM_MOUSEMOVE: { + float x = (float)LOWORD(lParam); + float y = (float)HIWORD(lParam); + + RECT rc; + GetClientRect(hwnd, &rc); + if (x >= rc.left && x <= rc.right && y >= rc.top && y <= rc.bottom) + { + SetCursor(LoadCursor(nullptr, IDC_ARROW)); + } + break; + } + + case WM_DESTROY: + PostQuitMessage(0); + return 0; + + default: + return DefWindowProc(hwnd, message, wParam, lParam); + } + + return 0; +} + +int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, + LPWSTR lpCmdLine, int nCmdShow) +{ + SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + + if (!InitD2DAndDWrite()) + return -1; + + HWND hwnd = CreateTransparentWindow(hInstance); + + if (!hwnd) + return -1; + + ShowWindow(hwnd, nCmdShow); + UpdateWindow(hwnd); + + MSG msg; + while (GetMessage(&msg, nullptr, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + if (pBrush) + pBrush->Release(); + if (pRenderTarget) + pRenderTarget->Release(); + if (pDWriteFactory) + pDWriteFactory->Release(); + if (pTextFormat) + pTextFormat->Release(); + if (pD2DFactory) + pD2DFactory->Release(); + + return (int)msg.wParam; +} + +FLOAT GetWindowScale(HWND hwnd) +{ + UINT dpi = GetDpiForWindow(hwnd); + FLOAT scale = dpi / 96.0f; + return scale; +} \ No newline at end of file diff --git a/server/src/webview2/MainWindow.cpp b/server/src/webview2/MainWindow.cpp new file mode 100644 index 00000000..9784b5b7 --- /dev/null +++ b/server/src/webview2/MainWindow.cpp @@ -0,0 +1,574 @@ +#include "WebView2.h" +#include "fmt/core.h" +#include "fmt/xchar.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// +// Message define +// +UINT WM_SHOW_MAIN_WINDOW; +UINT WM_HIDE_MAIN_WINDOW; +UINT WM_MOVE_CANDIDATE_WINDOW; + +static TCHAR szWindowClass[] = _T("global_candidate_window"); +static TCHAR szTitle[] = _T("WebView sample"); + +std::wstring candStr = L""; + +static std::wstring HTMLString = LR"( + + + + + + + Vertical Candidate Window + + + + + + +
+
+
ni'uo
+
+
+
+
1. 你说
+
+
+
+
+
2. 笔画
+
+
+ +
+
+
3. 量子
+
+
+ +
+
+
4. 牛魔
+
+
+ +
+
+
5. 仙人
+
+
+ +
+
+
6. 可恨
+
+
+ +
+
+
7. 木槿
+
+
+ +
+
+
8. 无量
+
+
+
+ + + +)"; + +static std::wstring BodyString = LR"( +
+ +
+
{0}
+
+ +
+
+
{1}
+
+
+ +
+
+
{2}
+
+
+ +
+
+
{3}
+
+
+ +
+
+
{4}
+
+
+ +
+
+
{5}
+
+
+ +
+
+
{6}
+
+
+ +
+
+
{7}
+
+
+ +
+
+
{8}
+
+
+ +
+)"; + +HINSTANCE hInst = 0; + +using namespace Microsoft::WRL; + +static ComPtr webviewController; +static ComPtr webview; +ComPtr webview3; +ComPtr webviewController2; + +void LogMessageW(const wchar_t *message) +{ + std::time_t now = std::time(nullptr); + std::tm *localTime = std::localtime(&now); + + wchar_t timeBuffer[80]; + wcsftime(timeBuffer, sizeof(timeBuffer) / sizeof(wchar_t), + L"%Y-%m-%d %H:%M:%S", localTime); + + std::wstring logfilePath = L"C:" + L"\\Users\\SonnyCalcr\\EDisk\\CppCodes\\Win32Cod" + L"es\\DirectWriteExperiment\\log.txt"; + std::wofstream logFile(logfilePath, std::ios_base::app); + if (logFile.is_open()) + { + logFile.imbue(std::locale("Chinese_China.65001")); + logFile << L"[" << timeBuffer << L"] " << message; + logFile << std::endl; + logFile.close(); + } +} + +void UpdateHtmlContentWithJavaScript(ComPtr webview, + const std::wstring &newContent) +{ + if (webview != nullptr) + { + std::wstring script = + L"document.body.innerHTML = `" + newContent + L"`;"; + webview->ExecuteScript(script.c_str(), nullptr); + } +} + +void MeasureDomUpdateTime(ComPtr webview) +{ + std::wstring script = + LR"(document.body.innerHTML = '
1. 原来
2. 如此
3. 竟然
4. 这样
5. 可恶
6. 棋盘
7. 磨合
8. 樱花
';)"; + + auto start = std::chrono::high_resolution_clock::now(); + + webview->ExecuteScript(script.c_str(), nullptr); + + auto end = std::chrono::high_resolution_clock::now(); + + auto duration = + std::chrono::duration_cast(end - start); + std::wstring message = + L"DOM update time: " + std::to_wstring(duration.count()) + L" μs"; + LogMessageW(message.c_str()); +} + +void inflateCandidateWindow(std::wstring &str) +{ + std::wstringstream wss(str); + std::wstring token; + std::vector words; + + while (std::getline(wss, token, L',')) + { + words.push_back(token); + } + + int size = words.size(); + + while (words.size() < 9) + { + words.push_back(L""); + } + + std::wstring result = fmt::format( // + BodyString, // + words[0], // + words[1], // + words[2], // + words[3], // + words[4], // + words[5], // + words[6], // + words[7], // + words[8] // + ); // + + if (size < 9) + { + result = result.substr( // + 0, // + result.find(fmt::format(L"", size)) // + ) // + + // + L""; // + } + + UpdateHtmlContentWithJavaScript(webview, result); +} + +static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, + LPARAM lParam) +{ + + if (message == WM_SHOW_MAIN_WINDOW) + { + ShowWindow(hWnd, SW_SHOWNOACTIVATE); + return 0; + } + if (message == WM_HIDE_MAIN_WINDOW) + { + ShowWindow(hWnd, SW_HIDE); + if (candStr.find(L",") == 1) + { + inflateCandidateWindow(candStr); + } + else + { + UpdateHtmlContentWithJavaScript(webview, L""); + } + return 0; + } + + switch (message) + { + case WM_MOUSEACTIVATE: + // Stop the window from being activated by mouse click + return MA_NOACTIVATE; + case WM_COPYDATA: { + COPYDATASTRUCT *pcds = (COPYDATASTRUCT *)lParam; + if (pcds->dwData == 0) + { + POINT *pt = (POINT *)pcds->lpData; + MoveWindow( // + hWnd, // + pt->x, // + pt->y + 3, // + (108 + 15) * 1.5, // + (246 + 15) * 1.5, // + TRUE // + ); // + } + else if (pcds->dwData == 1) + { + WCHAR *p = (WCHAR *)pcds->lpData; + std::wstring str = p; + candStr = str; +#ifdef FANY_DEBUG + LogMessageW(str.c_str()); +#endif + inflateCandidateWindow(str); + } + return 1; + } + case WM_ACTIVATE: { + if (LOWORD(wParam) != WA_INACTIVE) + { + ShowWindow(hWnd, SW_SHOWNOACTIVATE); + } + break; + } + case WM_DESTROY: { + PostQuitMessage(0); + break; + } + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + + return 0; +} + +void InitWebview(HWND hWnd) +{ + CreateCoreWebView2EnvironmentWithOptions( + nullptr, nullptr, nullptr, + Callback( + [hWnd](HRESULT result, ICoreWebView2Environment *env) -> HRESULT { + if (result != S_OK) + { + MessageBox(hWnd, L"Failed to create WebView2 environment.", + L"Error", MB_OK); + return result; + } + + // Create a CoreWebView2Controller and get the associated + // CoreWebView2 + env->CreateCoreWebView2Controller( + hWnd, + Callback< + ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>( + [hWnd](HRESULT result, + ICoreWebView2Controller *controller) -> HRESULT { + if (controller == nullptr || FAILED(result)) + { + MessageBox( + hWnd, + L"Failed to create WebView2 controller.", + L"Error", MB_OK); + return E_FAIL; + } + + webviewController = controller; + webviewController->get_CoreWebView2( + webview.GetAddressOf()); + + if (webview == nullptr) + { + MessageBox(hWnd, + L"Failed to get WebView2 instance.", + L"Error", MB_OK); + return E_FAIL; + } + + // Add a few settings for the webview + ICoreWebView2Settings *settings; + webview->get_Settings(&settings); + settings->put_IsScriptEnabled(TRUE); + settings->put_AreDefaultScriptDialogsEnabled(TRUE); + settings->put_IsWebMessageEnabled(TRUE); + settings->put_AreHostObjectsAllowed(TRUE); + + if (webview->QueryInterface( + IID_PPV_ARGS(&webview3)) == S_OK) + { + webview3->SetVirtualHostNameToFolderMapping( + L"appassets", + L"C:" + L"\\Users\\SonnyCalcr\\AppData\\Roaming\\Po" + L"tPla" + L"yerMini64\\Capture", + COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND_DENY_CORS); + } + + if (controller->QueryInterface( + IID_PPV_ARGS(&webviewController2)) == S_OK) + { + COREWEBVIEW2_COLOR backgroundColor = {0, 0, 0, + 0}; + webviewController2->put_DefaultBackgroundColor( + backgroundColor); + } + + // Resize WebView to fit the bounds of the parent + // window + RECT bounds; + GetClientRect(hWnd, &bounds); + webviewController->put_Bounds(bounds); + // Navigate to a simple HTML string + auto hr = + webview->NavigateToString(HTMLString.c_str()); + if (FAILED(hr)) + { + MessageBox(hWnd, + L"Failed to navigate to string.", + L"Error", MB_OK); + } + + // webview->OpenDevToolsWindow(); + + return S_OK; + }) + .Get()); + return S_OK; + }) + .Get()); +} + +int CALLBACK WinMain(_In_ HINSTANCE hInstance, _In_ HINSTANCE hPrevInstance, + _In_ LPSTR lpCmdLine, _In_ int nCmdShow) +{ + SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + + WM_SHOW_MAIN_WINDOW = RegisterWindowMessage(L"WM_SHOW_MAIN_WINDOW"); + WM_HIDE_MAIN_WINDOW = RegisterWindowMessage(L"WM_HIDE_MAIN_WINDOW"); + WM_MOVE_CANDIDATE_WINDOW = + RegisterWindowMessage(L"WM_MOVE_CANDIDATE_WINDOW"); + + WNDCLASSEX wcex; + + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = hInstance; + wcex.hIcon = LoadIcon(hInstance, IDI_APPLICATION); + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wcex.lpszMenuName = NULL; + wcex.lpszClassName = szWindowClass; + wcex.hIconSm = LoadIcon(wcex.hInstance, IDI_APPLICATION); + + if (!RegisterClassEx(&wcex)) + { + MessageBox(NULL, _T("Call to RegisterClassEx failed!"), + _T("Windows Desktop Guided Tour"), NULL); + + return 1; + } + + // Store instance handle in our global variable + hInst = hInstance; + + HWND hWnd = CreateWindowEx(WS_EX_LAYERED | WS_EX_TOOLWINDOW | // + WS_EX_NOACTIVATE | // + WS_EX_TOPMOST, // + szWindowClass, // + L"fanycandidatewindow", // + WS_POPUP, // + 100, // + 100, // + (108 + 15) * 1.5, // + (246 + 15) * 1.5, // + nullptr, nullptr, hInstance, nullptr); + + if (!hWnd) + { + MessageBox(NULL, _T("Call to CreateWindow failed!"), + _T("Windows Desktop Guided Tour"), NULL); + + return 1; + } + + MoveWindow(hWnd, -1000, 100, (108 + 15) * 1.5, (246 + 15) * 1.5, TRUE); + ShowWindow(hWnd, SW_SHOW); + UpdateWindow(hWnd); + + InitWebview(hWnd); + + MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + return (int)msg.wParam; +} \ No newline at end of file