mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-12 09:11:01 +00:00
1746 lines
68 KiB
Diff
1746 lines
68 KiB
Diff
From f33c69d8b4171ed43adc09113f2bd9975fdf4422 Mon Sep 17 00:00:00 2001
|
||
From: Mike Griese <migrie@microsoft.com>
|
||
Date: Fri, 17 Jul 2020 13:01:07 -0500
|
||
Subject: [PATCH] Add a scratch island project for testing
|
||
|
||
---
|
||
OpenConsole.sln | 38 ++
|
||
src/tools/ScratchIsland/AppHost.cpp | 176 +++++
|
||
src/tools/ScratchIsland/AppHost.h | 24 +
|
||
src/tools/ScratchIsland/BaseWindow.h | 228 +++++++
|
||
src/tools/ScratchIsland/IslandWindow.cpp | 604 ++++++++++++++++++
|
||
src/tools/ScratchIsland/IslandWindow.h | 77 +++
|
||
src/tools/ScratchIsland/ScratchIsland.def | 1 +
|
||
.../ScratchIsland/ScratchIsland.manifest | 24 +
|
||
src/tools/ScratchIsland/ScratchIsland.rc | 94 +++
|
||
src/tools/ScratchIsland/ScratchIsland.vcxproj | 164 +++++
|
||
src/tools/ScratchIsland/main.cpp | 54 ++
|
||
src/tools/ScratchIsland/packages.config | 8 +
|
||
src/tools/ScratchIsland/pch.cpp | 4 +
|
||
src/tools/ScratchIsland/pch.h | 74 +++
|
||
src/tools/ScratchIsland/resource.h | 24 +
|
||
15 files changed, 1594 insertions(+)
|
||
create mode 100644 src/tools/ScratchIsland/AppHost.cpp
|
||
create mode 100644 src/tools/ScratchIsland/AppHost.h
|
||
create mode 100644 src/tools/ScratchIsland/BaseWindow.h
|
||
create mode 100644 src/tools/ScratchIsland/IslandWindow.cpp
|
||
create mode 100644 src/tools/ScratchIsland/IslandWindow.h
|
||
create mode 100644 src/tools/ScratchIsland/ScratchIsland.def
|
||
create mode 100644 src/tools/ScratchIsland/ScratchIsland.manifest
|
||
create mode 100644 src/tools/ScratchIsland/ScratchIsland.rc
|
||
create mode 100644 src/tools/ScratchIsland/ScratchIsland.vcxproj
|
||
create mode 100644 src/tools/ScratchIsland/main.cpp
|
||
create mode 100644 src/tools/ScratchIsland/packages.config
|
||
create mode 100644 src/tools/ScratchIsland/pch.cpp
|
||
create mode 100644 src/tools/ScratchIsland/pch.h
|
||
create mode 100644 src/tools/ScratchIsland/resource.h
|
||
|
||
diff --git a/OpenConsole.sln b/OpenConsole.sln
|
||
index dcae33515..5099f457d 100644
|
||
--- a/OpenConsole.sln
|
||
+++ b/OpenConsole.sln
|
||
@@ -321,6 +321,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ScratchWinRTClient", "src\t
|
||
{D46D9547-F085-4645-B8F7-E8CD21559AB4} = {D46D9547-F085-4645-B8F7-E8CD21559AB4}
|
||
EndProjectSection
|
||
EndProject
|
||
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ScratchIsland", "src\tools\ScratchIsland\ScratchIsland.vcxproj", "{23a1f736-cd19-4196-980f-84bcd50cf783}"
|
||
+ ProjectSection(ProjectDependencies) = postProject
|
||
+ {06382349-D62A-4C7D-A7D3-9CA817EAE092} = {06382349-D62A-4C7D-A7D3-9CA817EAE092}
|
||
+ {D46D9547-F085-4645-B8F7-E8CD21559AB4} = {D46D9547-F085-4645-B8F7-E8CD21559AB4}
|
||
+ EndProjectSection
|
||
+EndProject
|
||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wt", "src\cascadia\wt\wt.vcxproj", "{506FD703-BAA7-4F6E-9361-64F550EC8FCA}"
|
||
EndProject
|
||
Global
|
||
@@ -2087,6 +2093,37 @@ Global
|
||
{506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x64.Build.0 = Release|x64
|
||
{506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x86.ActiveCfg = Release|Win32
|
||
{506FD703-BAA7-4F6E-9361-64F550EC8FCA}.Release|x86.Build.0 = Release|Win32
|
||
+
|
||
+
|
||
+
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|Any CPU.ActiveCfg = Release|x64
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|Any CPU.Build.0 = Release|x64
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|ARM64.ActiveCfg = Release|x64
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|ARM64.Build.0 = Release|x64
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|DotNet_x64Test.ActiveCfg = Release|x64
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|DotNet_x64Test.Build.0 = Release|x64
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|DotNet_x86Test.ActiveCfg = Release|x64
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|DotNet_x86Test.Build.0 = Release|x64
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|x64.ActiveCfg = Release|x64
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|x64.Build.0 = Release|x64
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|x86.ActiveCfg = Release|Win32
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.AuditMode|x86.Build.0 = Release|Win32
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|ARM64.ActiveCfg = Debug|Win32
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|x64.ActiveCfg = Debug|x64
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|x64.Build.0 = Debug|x64
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|x86.ActiveCfg = Debug|Win32
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Debug|x86.Build.0 = Debug|Win32
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Release|Any CPU.ActiveCfg = Release|Win32
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Release|ARM64.ActiveCfg = Release|Win32
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Release|DotNet_x64Test.ActiveCfg = Release|Win32
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Release|x64.ActiveCfg = Release|x64
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Release|x64.Build.0 = Release|x64
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Release|x86.ActiveCfg = Release|Win32
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783}.Release|x86.Build.0 = Release|Win32
|
||
EndGlobalSection
|
||
GlobalSection(SolutionProperties) = preSolution
|
||
HideSolutionNode = FALSE
|
||
@@ -2169,6 +2206,7 @@ Global
|
||
{1588FD7C-241E-4E7D-9113-43735F3E6BAD} = {59840756-302F-44DF-AA47-441A9D673202}
|
||
{D46D9547-F085-4645-B8F7-E8CD21559AB4} = {A10C4720-DCA4-4640-9749-67F4314F527C}
|
||
{06382349-D62A-4C7D-A7D3-9CA817EAE092} = {A10C4720-DCA4-4640-9749-67F4314F527C}
|
||
+ {23a1f736-cd19-4196-980f-84bcd50cf783} = {A10C4720-DCA4-4640-9749-67F4314F527C}
|
||
{506FD703-BAA7-4F6E-9361-64F550EC8FCA} = {59840756-302F-44DF-AA47-441A9D673202}
|
||
EndGlobalSection
|
||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||
diff --git a/src/tools/ScratchIsland/AppHost.cpp b/src/tools/ScratchIsland/AppHost.cpp
|
||
new file mode 100644
|
||
index 000000000..d1ec7b004
|
||
--- /dev/null
|
||
+++ b/src/tools/ScratchIsland/AppHost.cpp
|
||
@@ -0,0 +1,176 @@
|
||
+// Copyright (c) Microsoft Corporation.
|
||
+// Licensed under the MIT license.
|
||
+
|
||
+#include "pch.h"
|
||
+#include "AppHost.h"
|
||
+#include "../types/inc/Viewport.hpp"
|
||
+#include "../types/inc/utils.hpp"
|
||
+#include "../types/inc/User32Utils.hpp"
|
||
+
|
||
+#include "resource.h"
|
||
+
|
||
+using namespace winrt::Windows::UI;
|
||
+using namespace winrt::Windows::UI::Composition;
|
||
+using namespace winrt::Windows::UI::Xaml;
|
||
+using namespace winrt::Windows::UI::Xaml::Hosting;
|
||
+using namespace winrt::Windows::Foundation::Numerics;
|
||
+using namespace ::Microsoft::Console;
|
||
+using namespace ::Microsoft::Console::Types;
|
||
+
|
||
+AppHost::AppHost() noexcept :
|
||
+ _window{ nullptr }
|
||
+{
|
||
+ _window = std::make_unique<IslandWindow>();
|
||
+
|
||
+ // Tell the window to callback to us when it's about to handle a WM_CREATE
|
||
+ auto pfn = std::bind(&AppHost::_HandleCreateWindow,
|
||
+ this,
|
||
+ std::placeholders::_1,
|
||
+ std::placeholders::_2);
|
||
+ _window->SetCreateCallback(pfn);
|
||
+
|
||
+ _window->MakeWindow();
|
||
+}
|
||
+
|
||
+AppHost::~AppHost()
|
||
+{
|
||
+ // destruction order is important for proper teardown here
|
||
+ _window = nullptr;
|
||
+}
|
||
+
|
||
+// Method Description:
|
||
+// - Initializes the XAML island, creates the terminal app, and sets the
|
||
+// island's content to that of the terminal app's content. Also registers some
|
||
+// callbacks with TermApp.
|
||
+// !!! IMPORTANT!!!
|
||
+// This must be called *AFTER* WindowsXamlManager::InitializeForCurrentThread.
|
||
+// If it isn't, then we won't be able to create the XAML island.
|
||
+// Arguments:
|
||
+// - <none>
|
||
+// Return Value:
|
||
+// - <none>
|
||
+void AppHost::Initialize()
|
||
+{
|
||
+ _window->Initialize();
|
||
+
|
||
+ // Set up the content of the application. If the app has a custom titlebar,
|
||
+ // set that content as well.
|
||
+ winrt::Windows::UI::Xaml::Controls::Grid g;
|
||
+ // TODO: INITIALIZE THIS UI HERE
|
||
+ _window->SetContent(g);
|
||
+
|
||
+ _window->OnAppInitialized();
|
||
+}
|
||
+
|
||
+// Method Description:
|
||
+// - Resize the window we're about to create to the appropriate dimensions, as
|
||
+// specified in the settings. This will be called during the handling of
|
||
+// WM_CREATE. We'll load the settings for the app, then get the proposed size
|
||
+// of the terminal from the app. Using that proposed size, we'll resize the
|
||
+// window we're creating, so that it'll match the values in the settings.
|
||
+// Arguments:
|
||
+// - hwnd: The HWND of the window we're about to create.
|
||
+// - proposedRect: The location and size of the window that we're about to
|
||
+// create. We'll use this rect to determine which monitor the window is about
|
||
+// to appear on.
|
||
+// - launchMode: A LaunchMode enum reference that indicates the launch mode
|
||
+// Return Value:
|
||
+// - None
|
||
+void AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect)
|
||
+{
|
||
+ // Acquire the actual initial position
|
||
+ winrt::Windows::Foundation::Point initialPosition{ (float)proposedRect.left, (float)proposedRect.top };
|
||
+ proposedRect.left = gsl::narrow_cast<long>(initialPosition.X);
|
||
+ proposedRect.top = gsl::narrow_cast<long>(initialPosition.Y);
|
||
+
|
||
+ long adjustedHeight = 0;
|
||
+ long adjustedWidth = 0;
|
||
+
|
||
+ // Find nearest monitor.
|
||
+ HMONITOR hmon = MonitorFromRect(&proposedRect, MONITOR_DEFAULTTONEAREST);
|
||
+
|
||
+ // Get nearest monitor information
|
||
+ MONITORINFO monitorInfo;
|
||
+ monitorInfo.cbSize = sizeof(MONITORINFO);
|
||
+ GetMonitorInfo(hmon, &monitorInfo);
|
||
+
|
||
+ // This API guarantees that dpix and dpiy will be equal, but neither is an
|
||
+ // optional parameter so give two UINTs.
|
||
+ UINT dpix = USER_DEFAULT_SCREEN_DPI;
|
||
+ UINT dpiy = USER_DEFAULT_SCREEN_DPI;
|
||
+ // If this fails, we'll use the default of 96.
|
||
+ GetDpiForMonitor(hmon, MDT_EFFECTIVE_DPI, &dpix, &dpiy);
|
||
+
|
||
+ // We need to check if the top left point of the titlebar of the window is within any screen
|
||
+ RECT offScreenTestRect;
|
||
+ offScreenTestRect.left = proposedRect.left;
|
||
+ offScreenTestRect.top = proposedRect.top;
|
||
+ offScreenTestRect.right = offScreenTestRect.left + 1;
|
||
+ offScreenTestRect.bottom = offScreenTestRect.top + 1;
|
||
+
|
||
+ bool isTitlebarIntersectWithMonitors = false;
|
||
+ EnumDisplayMonitors(
|
||
+ nullptr, &offScreenTestRect, [](HMONITOR, HDC, LPRECT, LPARAM lParam) -> BOOL {
|
||
+ auto intersectWithMonitor = reinterpret_cast<bool*>(lParam);
|
||
+ *intersectWithMonitor = true;
|
||
+ // Continue the enumeration
|
||
+ return FALSE;
|
||
+ },
|
||
+ reinterpret_cast<LPARAM>(&isTitlebarIntersectWithMonitors));
|
||
+
|
||
+ if (!isTitlebarIntersectWithMonitors)
|
||
+ {
|
||
+ // If the title bar is out-of-screen, we set the initial position to
|
||
+ // the top left corner of the nearest monitor
|
||
+ proposedRect.left = monitorInfo.rcWork.left;
|
||
+ proposedRect.top = monitorInfo.rcWork.top;
|
||
+ }
|
||
+
|
||
+ winrt::Windows::Foundation::Size initialSize{ 800, 600 };
|
||
+
|
||
+ const short islandWidth = Utils::ClampToShortMax(
|
||
+ static_cast<long>(ceil(initialSize.Width)), 1);
|
||
+ const short islandHeight = Utils::ClampToShortMax(
|
||
+ static_cast<long>(ceil(initialSize.Height)), 1);
|
||
+
|
||
+ // Get the size of a window we'd need to host that client rect. This will
|
||
+ // add the titlebar space.
|
||
+ const auto nonClientSize = _window->GetTotalNonClientExclusiveSize(dpix);
|
||
+ adjustedWidth = islandWidth + nonClientSize.cx;
|
||
+ adjustedHeight = islandHeight + nonClientSize.cy;
|
||
+
|
||
+ const COORD origin{ gsl::narrow<short>(proposedRect.left),
|
||
+ gsl::narrow<short>(proposedRect.top) };
|
||
+ const COORD dimensions{ Utils::ClampToShortMax(adjustedWidth, 1),
|
||
+ Utils::ClampToShortMax(adjustedHeight, 1) };
|
||
+
|
||
+ const auto newPos = Viewport::FromDimensions(origin, dimensions);
|
||
+ bool succeeded = SetWindowPos(hwnd,
|
||
+ nullptr,
|
||
+ newPos.Left(),
|
||
+ newPos.Top(),
|
||
+ newPos.Width(),
|
||
+ newPos.Height(),
|
||
+ SWP_NOACTIVATE | SWP_NOZORDER);
|
||
+
|
||
+ // Refresh the dpi of HWND because the dpi where the window will launch may be different
|
||
+ // at this time
|
||
+ _window->RefreshCurrentDPI();
|
||
+
|
||
+ // If we can't resize the window, that's really okay. We can just go on with
|
||
+ // the originally proposed window size.
|
||
+ LOG_LAST_ERROR_IF(!succeeded);
|
||
+}
|
||
+
|
||
+// Method Description:
|
||
+// - Called when the app wants to change its theme. We'll forward this to the
|
||
+// IslandWindow, so it can update the root UI element of the entire XAML tree.
|
||
+// Arguments:
|
||
+// - sender: unused
|
||
+// - arg: the ElementTheme to use as the new theme for the UI
|
||
+// Return Value:
|
||
+// - <none>
|
||
+void AppHost::_UpdateTheme(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::UI::Xaml::ElementTheme& arg)
|
||
+{
|
||
+ _window->OnApplicationThemeChanged(arg);
|
||
+}
|
||
diff --git a/src/tools/ScratchIsland/AppHost.h b/src/tools/ScratchIsland/AppHost.h
|
||
new file mode 100644
|
||
index 000000000..e4a957cd8
|
||
--- /dev/null
|
||
+++ b/src/tools/ScratchIsland/AppHost.h
|
||
@@ -0,0 +1,24 @@
|
||
+// Copyright (c) Microsoft Corporation.
|
||
+// Licensed under the MIT license.
|
||
+
|
||
+#include "pch.h"
|
||
+
|
||
+#include "IslandWindow.h"
|
||
+
|
||
+class AppHost
|
||
+{
|
||
+public:
|
||
+ AppHost() noexcept;
|
||
+ virtual ~AppHost();
|
||
+
|
||
+ void Initialize();
|
||
+
|
||
+private:
|
||
+ bool _useNonClientArea{ false };
|
||
+
|
||
+ std::unique_ptr<IslandWindow> _window;
|
||
+
|
||
+ void _HandleCreateWindow(const HWND hwnd, RECT proposedRect);
|
||
+ void _UpdateTheme(const winrt::Windows::Foundation::IInspectable&,
|
||
+ const winrt::Windows::UI::Xaml::ElementTheme& arg);
|
||
+};
|
||
diff --git a/src/tools/ScratchIsland/BaseWindow.h b/src/tools/ScratchIsland/BaseWindow.h
|
||
new file mode 100644
|
||
index 000000000..d1a8b86d7
|
||
--- /dev/null
|
||
+++ b/src/tools/ScratchIsland/BaseWindow.h
|
||
@@ -0,0 +1,228 @@
|
||
+// Copyright (c) Microsoft Corporation.
|
||
+// Licensed under the MIT license.
|
||
+
|
||
+#pragma once
|
||
+
|
||
+// Custom window messages
|
||
+#define CM_UPDATE_TITLE (WM_USER)
|
||
+
|
||
+#include <wil/resource.h>
|
||
+
|
||
+template<typename T>
|
||
+class BaseWindow
|
||
+{
|
||
+public:
|
||
+ virtual ~BaseWindow() = 0;
|
||
+ static T* GetThisFromHandle(HWND const window) noexcept
|
||
+ {
|
||
+ return reinterpret_cast<T*>(GetWindowLongPtr(window, GWLP_USERDATA));
|
||
+ }
|
||
+
|
||
+ [[nodiscard]] static LRESULT __stdcall WndProc(HWND const window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept
|
||
+ {
|
||
+ WINRT_ASSERT(window);
|
||
+
|
||
+ if (WM_NCCREATE == message)
|
||
+ {
|
||
+ auto cs = reinterpret_cast<CREATESTRUCT*>(lparam);
|
||
+ T* that = static_cast<T*>(cs->lpCreateParams);
|
||
+ WINRT_ASSERT(that);
|
||
+ WINRT_ASSERT(!that->_window);
|
||
+ that->_window = wil::unique_hwnd(window);
|
||
+
|
||
+ return that->_OnNcCreate(wparam, lparam);
|
||
+ }
|
||
+ else if (T* that = GetThisFromHandle(window))
|
||
+ {
|
||
+ return that->MessageHandler(message, wparam, lparam);
|
||
+ }
|
||
+
|
||
+ return DefWindowProc(window, message, wparam, lparam);
|
||
+ }
|
||
+
|
||
+ [[nodiscard]] virtual LRESULT MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept
|
||
+ {
|
||
+ switch (message)
|
||
+ {
|
||
+ case WM_DPICHANGED:
|
||
+ {
|
||
+ return HandleDpiChange(_window.get(), wparam, lparam);
|
||
+ }
|
||
+
|
||
+ case WM_DESTROY:
|
||
+ {
|
||
+ PostQuitMessage(0);
|
||
+ return 0;
|
||
+ }
|
||
+
|
||
+ case WM_SIZE:
|
||
+ {
|
||
+ UINT width = LOWORD(lparam);
|
||
+ UINT height = HIWORD(lparam);
|
||
+
|
||
+ switch (wparam)
|
||
+ {
|
||
+ case SIZE_MAXIMIZED:
|
||
+ [[fallthrough]];
|
||
+ case SIZE_RESTORED:
|
||
+ if (_minimized)
|
||
+ {
|
||
+ _minimized = false;
|
||
+ OnRestore();
|
||
+ }
|
||
+
|
||
+ // We always need to fire the resize event, even when we're transitioning from minimized.
|
||
+ // We might be transitioning directly from minimized to maximized, and we'll need
|
||
+ // to trigger any size-related content changes.
|
||
+ OnResize(width, height);
|
||
+ break;
|
||
+ case SIZE_MINIMIZED:
|
||
+ if (!_minimized)
|
||
+ {
|
||
+ _minimized = true;
|
||
+ OnMinimize();
|
||
+ }
|
||
+ break;
|
||
+ default:
|
||
+ // do nothing.
|
||
+ break;
|
||
+ }
|
||
+ break;
|
||
+ }
|
||
+ case CM_UPDATE_TITLE:
|
||
+ {
|
||
+ SetWindowTextW(_window.get(), _title.c_str());
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return DefWindowProc(_window.get(), message, wparam, lparam);
|
||
+ }
|
||
+
|
||
+ // DPI Change handler. on WM_DPICHANGE resize the window
|
||
+ [[nodiscard]] LRESULT HandleDpiChange(const HWND hWnd, const WPARAM wParam, const LPARAM lParam)
|
||
+ {
|
||
+ _inDpiChange = true;
|
||
+ const HWND hWndStatic = GetWindow(hWnd, GW_CHILD);
|
||
+ if (hWndStatic != nullptr)
|
||
+ {
|
||
+ const UINT uDpi = HIWORD(wParam);
|
||
+
|
||
+ // Resize the window
|
||
+ auto lprcNewScale = reinterpret_cast<RECT*>(lParam);
|
||
+
|
||
+ SetWindowPos(hWnd, nullptr, lprcNewScale->left, lprcNewScale->top, lprcNewScale->right - lprcNewScale->left, lprcNewScale->bottom - lprcNewScale->top, SWP_NOZORDER | SWP_NOACTIVATE);
|
||
+
|
||
+ _currentDpi = uDpi;
|
||
+ }
|
||
+ _inDpiChange = false;
|
||
+ return 0;
|
||
+ }
|
||
+
|
||
+ virtual void OnResize(const UINT width, const UINT height) = 0;
|
||
+ virtual void OnMinimize() = 0;
|
||
+ virtual void OnRestore() = 0;
|
||
+
|
||
+ RECT GetWindowRect() const noexcept
|
||
+ {
|
||
+ RECT rc = { 0 };
|
||
+ ::GetWindowRect(_window.get(), &rc);
|
||
+ return rc;
|
||
+ }
|
||
+
|
||
+ HWND GetHandle() const noexcept
|
||
+ {
|
||
+ return _window.get();
|
||
+ }
|
||
+
|
||
+ float GetCurrentDpiScale() const noexcept
|
||
+ {
|
||
+ const auto dpi = ::GetDpiForWindow(_window.get());
|
||
+ const auto scale = static_cast<float>(dpi) / static_cast<float>(USER_DEFAULT_SCREEN_DPI);
|
||
+ return scale;
|
||
+ }
|
||
+
|
||
+ //// Gets the physical size of the client area of the HWND in _window
|
||
+ SIZE GetPhysicalSize() const noexcept
|
||
+ {
|
||
+ RECT rect = {};
|
||
+ GetClientRect(_window.get(), &rect);
|
||
+ const auto windowsWidth = rect.right - rect.left;
|
||
+ const auto windowsHeight = rect.bottom - rect.top;
|
||
+ return SIZE{ windowsWidth, windowsHeight };
|
||
+ }
|
||
+
|
||
+ //// Gets the logical (in DIPs) size of a physical size specified by the parameter physicalSize
|
||
+ //// Remarks:
|
||
+ //// XAML coordinate system is always in Display Independent Pixels (a.k.a DIPs or Logical). However Win32 GDI (because of legacy reasons)
|
||
+ //// in DPI mode "Per-Monitor and Per-Monitor (V2) DPI Awareness" is always in physical pixels.
|
||
+ //// The formula to transform is:
|
||
+ //// logical = (physical / dpi) + 0.5 // 0.5 is to ensure that we pixel snap correctly at the edges, this is necessary with odd DPIs like 1.25, 1.5, 1, .75
|
||
+ //// See also:
|
||
+ //// https://docs.microsoft.com/en-us/windows/desktop/LearnWin32/dpi-and-device-independent-pixels
|
||
+ //// https://docs.microsoft.com/en-us/windows/desktop/hidpi/high-dpi-desktop-application-development-on-windows#per-monitor-and-per-monitor-v2-dpi-awareness
|
||
+ winrt::Windows::Foundation::Size GetLogicalSize(const SIZE physicalSize) const noexcept
|
||
+ {
|
||
+ const auto scale = GetCurrentDpiScale();
|
||
+ // 0.5 is to ensure that we pixel snap correctly at the edges, this is necessary with odd DPIs like 1.25, 1.5, 1, .75
|
||
+ const auto logicalWidth = (physicalSize.cx / scale) + 0.5f;
|
||
+ const auto logicalHeight = (physicalSize.cy / scale) + 0.5f;
|
||
+ return winrt::Windows::Foundation::Size(logicalWidth, logicalHeight);
|
||
+ }
|
||
+
|
||
+ winrt::Windows::Foundation::Size GetLogicalSize() const noexcept
|
||
+ {
|
||
+ return GetLogicalSize(GetPhysicalSize());
|
||
+ }
|
||
+
|
||
+ // Method Description:
|
||
+ // - Sends a message to our message loop to update the title of the window.
|
||
+ // Arguments:
|
||
+ // - newTitle: a string to use as the new title of the window.
|
||
+ // Return Value:
|
||
+ // - <none>
|
||
+ void UpdateTitle(std::wstring_view newTitle)
|
||
+ {
|
||
+ _title = newTitle;
|
||
+ PostMessageW(_window.get(), CM_UPDATE_TITLE, 0, reinterpret_cast<LPARAM>(nullptr));
|
||
+ }
|
||
+
|
||
+ // Method Description:
|
||
+ // Reset the current dpi of the window. This method is only called after we change the
|
||
+ // initial launch position. This makes sure the dpi is consistent with the monitor on which
|
||
+ // the window will launch
|
||
+ void RefreshCurrentDPI()
|
||
+ {
|
||
+ _currentDpi = GetDpiForWindow(_window.get());
|
||
+ }
|
||
+
|
||
+protected:
|
||
+ using base_type = BaseWindow<T>;
|
||
+ wil::unique_hwnd _window;
|
||
+
|
||
+ unsigned int _currentDpi = 0;
|
||
+ bool _inDpiChange = false;
|
||
+
|
||
+ std::wstring _title = L"";
|
||
+
|
||
+ bool _minimized = false;
|
||
+
|
||
+ // Method Description:
|
||
+ // - This method is called when the window receives the WM_NCCREATE message.
|
||
+ // Return Value:
|
||
+ // - The value returned from the window proc.
|
||
+ virtual [[nodiscard]] LRESULT _OnNcCreate(WPARAM wParam, LPARAM lParam) noexcept
|
||
+ {
|
||
+ SetWindowLongPtr(_window.get(), GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
|
||
+
|
||
+ EnableNonClientDpiScaling(_window.get());
|
||
+ _currentDpi = GetDpiForWindow(_window.get());
|
||
+
|
||
+ return DefWindowProc(_window.get(), WM_NCCREATE, wParam, lParam);
|
||
+ };
|
||
+};
|
||
+
|
||
+template<typename T>
|
||
+inline BaseWindow<T>::~BaseWindow()
|
||
+{
|
||
+}
|
||
diff --git a/src/tools/ScratchIsland/IslandWindow.cpp b/src/tools/ScratchIsland/IslandWindow.cpp
|
||
new file mode 100644
|
||
index 000000000..3813c6e70
|
||
--- /dev/null
|
||
+++ b/src/tools/ScratchIsland/IslandWindow.cpp
|
||
@@ -0,0 +1,604 @@
|
||
+// Copyright (c) Microsoft Corporation.
|
||
+// Licensed under the MIT license.
|
||
+
|
||
+#include "pch.h"
|
||
+#include "IslandWindow.h"
|
||
+#include "../types/inc/Viewport.hpp"
|
||
+#include "resource.h"
|
||
+
|
||
+extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||
+
|
||
+using namespace winrt::Windows::UI;
|
||
+using namespace winrt::Windows::UI::Composition;
|
||
+using namespace winrt::Windows::UI::Xaml;
|
||
+using namespace winrt::Windows::UI::Xaml::Hosting;
|
||
+using namespace winrt::Windows::Foundation::Numerics;
|
||
+using namespace ::Microsoft::Console::Types;
|
||
+
|
||
+#define XAML_HOSTING_WINDOW_CLASS_NAME L"SCRATCH_ISLAND_CLASS"
|
||
+
|
||
+IslandWindow::IslandWindow() noexcept :
|
||
+ _interopWindowHandle{ nullptr },
|
||
+ _rootGrid{ nullptr },
|
||
+ _source{ nullptr },
|
||
+ _pfnCreateCallback{ nullptr }
|
||
+{
|
||
+}
|
||
+
|
||
+IslandWindow::~IslandWindow()
|
||
+{
|
||
+ _source.Close();
|
||
+}
|
||
+
|
||
+// Method Description:
|
||
+// - Create the actual window that we'll use for the application.
|
||
+// Arguments:
|
||
+// - <none>
|
||
+// Return Value:
|
||
+// - <none>
|
||
+void IslandWindow::MakeWindow() noexcept
|
||
+{
|
||
+ WNDCLASS wc{};
|
||
+ wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||
+ wc.hInstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
|
||
+ wc.lpszClassName = XAML_HOSTING_WINDOW_CLASS_NAME;
|
||
+ wc.style = CS_HREDRAW | CS_VREDRAW;
|
||
+ wc.lpfnWndProc = WndProc;
|
||
+ wc.hIcon = LoadIconW(wc.hInstance, MAKEINTRESOURCEW(IDI_APPICON));
|
||
+ RegisterClass(&wc);
|
||
+ WINRT_ASSERT(!_window);
|
||
+
|
||
+ // Create the window with the default size here - During the creation of the
|
||
+ // window, the system will give us a chance to set its size in WM_CREATE.
|
||
+ // WM_CREATE will be handled synchronously, before CreateWindow returns.
|
||
+ WINRT_VERIFY(CreateWindowEx(_alwaysOnTop ? WS_EX_TOPMOST : 0,
|
||
+ wc.lpszClassName,
|
||
+ L"Scratch Island",
|
||
+ WS_OVERLAPPEDWINDOW,
|
||
+ CW_USEDEFAULT,
|
||
+ CW_USEDEFAULT,
|
||
+ CW_USEDEFAULT,
|
||
+ CW_USEDEFAULT,
|
||
+ nullptr,
|
||
+ nullptr,
|
||
+ wc.hInstance,
|
||
+ this));
|
||
+
|
||
+ WINRT_ASSERT(_window);
|
||
+}
|
||
+
|
||
+// Method Description:
|
||
+// - Called when no tab is remaining to close the window.
|
||
+// Arguments:
|
||
+// - <none>
|
||
+// Return Value:
|
||
+// - <none>
|
||
+void IslandWindow::Close()
|
||
+{
|
||
+ PostQuitMessage(0);
|
||
+}
|
||
+
|
||
+// Method Description:
|
||
+// - Set a callback to be called when we process a WM_CREATE message. This gives
|
||
+// the AppHost a chance to resize the window to the proper size.
|
||
+// Arguments:
|
||
+// - pfn: a function to be called during the handling of WM_CREATE. It takes two
|
||
+// parameters:
|
||
+// * HWND: the HWND of the window that's being created.
|
||
+// * RECT: The position on the screen that the system has proposed for our
|
||
+// window.
|
||
+// Return Value:
|
||
+// - <none>
|
||
+void IslandWindow::SetCreateCallback(std::function<void(const HWND, const RECT)> pfn) noexcept
|
||
+{
|
||
+ _pfnCreateCallback = pfn;
|
||
+}
|
||
+
|
||
+// Method Description:
|
||
+// - Handles a WM_CREATE message. Calls our create callback, if one's been set.
|
||
+// Arguments:
|
||
+// - wParam: unused
|
||
+// - lParam: the lParam of a WM_CREATE, which is a pointer to a CREATESTRUCTW
|
||
+// Return Value:
|
||
+// - <none>
|
||
+void IslandWindow::_HandleCreateWindow(const WPARAM, const LPARAM lParam) noexcept
|
||
+{
|
||
+ // Get proposed window rect from create structure
|
||
+ CREATESTRUCTW* pcs = reinterpret_cast<CREATESTRUCTW*>(lParam);
|
||
+ RECT rc;
|
||
+ rc.left = pcs->x;
|
||
+ rc.top = pcs->y;
|
||
+ rc.right = rc.left + pcs->cx;
|
||
+ rc.bottom = rc.top + pcs->cy;
|
||
+
|
||
+ if (_pfnCreateCallback)
|
||
+ {
|
||
+ _pfnCreateCallback(_window.get(), rc);
|
||
+ }
|
||
+
|
||
+ int nCmdShow = SW_SHOW;
|
||
+
|
||
+ ShowWindow(_window.get(), nCmdShow);
|
||
+
|
||
+ UpdateWindow(_window.get());
|
||
+}
|
||
+
|
||
+// Method Description:
|
||
+// - Handles a WM_SIZING message, which occurs when user drags a window border
|
||
+// or corner. It intercepts this resize action and applies 'snapping' i.e.
|
||
+// aligns the terminal's size to its cell grid. We're given the window size,
|
||
+// which we then adjust based on the terminal's properties (like font size).
|
||
+// Arguments:
|
||
+// - wParam: Specifies which edge of the window is being dragged.
|
||
+// - lParam: Pointer to the requested window rectangle (this is, the one that
|
||
+// originates from current drag action). It also acts as the return value
|
||
+// (it's a ref parameter).
|
||
+// Return Value:
|
||
+// - <none>
|
||
+LRESULT IslandWindow::_OnSizing(const WPARAM /*wParam*/, const LPARAM /*lParam*/)
|
||
+{
|
||
+ // If we haven't been given the callback that would adjust the dimension,
|
||
+ // then we can't do anything, so just bail out.
|
||
+ return FALSE;
|
||
+}
|
||
+
|
||
+void IslandWindow::Initialize()
|
||
+{
|
||
+ const bool initialized = (_interopWindowHandle != nullptr);
|
||
+
|
||
+ _source = DesktopWindowXamlSource{};
|
||
+
|
||
+ auto interop = _source.as<IDesktopWindowXamlSourceNative>();
|
||
+ winrt::check_hresult(interop->AttachToWindow(_window.get()));
|
||
+
|
||
+ // stash the child interop handle so we can resize it when the main hwnd is resized
|
||
+ interop->get_WindowHandle(&_interopWindowHandle);
|
||
+
|
||
+ _rootGrid = winrt::Windows::UI::Xaml::Controls::Grid();
|
||
+ _source.Content(_rootGrid);
|
||
+}
|
||
+
|
||
+void IslandWindow::OnSize(const UINT width, const UINT height)
|
||
+{
|
||
+ // update the interop window size
|
||
+ SetWindowPos(_interopWindowHandle, nullptr, 0, 0, width, height, SWP_SHOWWINDOW);
|
||
+
|
||
+ if (_rootGrid)
|
||
+ {
|
||
+ const auto size = GetLogicalSize();
|
||
+ _rootGrid.Width(size.Width);
|
||
+ _rootGrid.Height(size.Height);
|
||
+ }
|
||
+}
|
||
+
|
||
+[[nodiscard]] LRESULT IslandWindow::MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept
|
||
+{
|
||
+ switch (message)
|
||
+ {
|
||
+ case WM_CREATE:
|
||
+ {
|
||
+ _HandleCreateWindow(wparam, lparam);
|
||
+ return 0;
|
||
+ }
|
||
+ case WM_SETFOCUS:
|
||
+ {
|
||
+ if (_interopWindowHandle != nullptr)
|
||
+ {
|
||
+ // send focus to the child window
|
||
+ SetFocus(_interopWindowHandle);
|
||
+ return 0; // eat the message
|
||
+ }
|
||
+ }
|
||
+
|
||
+ case WM_NCLBUTTONDOWN:
|
||
+ case WM_NCLBUTTONUP:
|
||
+ case WM_NCMBUTTONDOWN:
|
||
+ case WM_NCMBUTTONUP:
|
||
+ case WM_NCRBUTTONDOWN:
|
||
+ case WM_NCRBUTTONUP:
|
||
+ case WM_NCXBUTTONDOWN:
|
||
+ case WM_NCXBUTTONUP:
|
||
+ {
|
||
+ // If we clicked in the titlebar, raise an event so the app host can
|
||
+ // dispatch an appropriate event.
|
||
+ _DragRegionClickedHandlers();
|
||
+ break;
|
||
+ }
|
||
+ case WM_MENUCHAR:
|
||
+ {
|
||
+ // GH#891: return this LRESULT here to prevent the app from making a
|
||
+ // bell when alt+key is pressed. A menu is active and the user presses a
|
||
+ // key that does not correspond to any mnemonic or accelerator key,
|
||
+ return MAKELRESULT(0, MNC_CLOSE);
|
||
+ }
|
||
+ case WM_SIZING:
|
||
+ {
|
||
+ return _OnSizing(wparam, lparam);
|
||
+ }
|
||
+ case WM_CLOSE:
|
||
+ {
|
||
+ // If the user wants to close the app by clicking 'X' button,
|
||
+ // we hand off the close experience to the app layer. If all the tabs
|
||
+ // are closed, the window will be closed as well.
|
||
+ _windowCloseButtonClickedHandler();
|
||
+ return 0;
|
||
+ }
|
||
+ case WM_MOUSEWHEEL:
|
||
+ try
|
||
+ {
|
||
+ // This whole handler is a hack for GH#979.
|
||
+ //
|
||
+ // On some laptops, their trackpads won't scroll inactive windows
|
||
+ // _ever_. With our entire window just being one giant XAML Island, the
|
||
+ // touchpad driver thinks our entire window is inactive, and won't
|
||
+ // scroll the XAML island. On those types of laptops, we'll get a
|
||
+ // WM_MOUSEWHEEL here, in our root window, when the trackpad scrolls.
|
||
+ // We're going to take that message and manually plumb it through to our
|
||
+ // TermControl's, or anything else that implements IMouseWheelListener.
|
||
+
|
||
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms645617(v=vs.85).aspx
|
||
+ // Important! Do not use the LOWORD or HIWORD macros to extract the x-
|
||
+ // and y- coordinates of the cursor position because these macros return
|
||
+ // incorrect results on systems with multiple monitors. Systems with
|
||
+ // multiple monitors can have negative x- and y- coordinates, and LOWORD
|
||
+ // and HIWORD treat the coordinates as unsigned quantities.
|
||
+ const til::point eventPoint{ GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) };
|
||
+ // This mouse event is relative to the display origin, not the window. Convert here.
|
||
+ const til::rectangle windowRect{ GetWindowRect() };
|
||
+ const auto origin = windowRect.origin();
|
||
+ const auto relative = eventPoint - origin;
|
||
+ // Convert to logical scaling before raising the event.
|
||
+ const auto real = relative / GetCurrentDpiScale();
|
||
+
|
||
+ const short wheelDelta = static_cast<short>(HIWORD(wparam));
|
||
+
|
||
+ // Raise an event, so any listeners can handle the mouse wheel event manually.
|
||
+ _MouseScrolledHandlers(real, wheelDelta);
|
||
+ return 0;
|
||
+ }
|
||
+ CATCH_LOG();
|
||
+ }
|
||
+
|
||
+ // TODO: handle messages here...
|
||
+ return base_type::MessageHandler(message, wparam, lparam);
|
||
+}
|
||
+
|
||
+// Method Description:
|
||
+// - Called when the window has been resized (or maximized)
|
||
+// Arguments:
|
||
+// - width: the new width of the window _in pixels_
|
||
+// - height: the new height of the window _in pixels_
|
||
+void IslandWindow::OnResize(const UINT width, const UINT height)
|
||
+{
|
||
+ if (_interopWindowHandle)
|
||
+ {
|
||
+ OnSize(width, height);
|
||
+ }
|
||
+}
|
||
+
|
||
+// Method Description:
|
||
+// - Called when the window is minimized to the taskbar.
|
||
+void IslandWindow::OnMinimize()
|
||
+{
|
||
+ // TODO GH#1989 Stop rendering island content when the app is minimized.
|
||
+}
|
||
+
|
||
+// Method Description:
|
||
+// - Called when the window is restored from having been minimized.
|
||
+void IslandWindow::OnRestore()
|
||
+{
|
||
+ // TODO GH#1989 Stop rendering island content when the app is minimized.
|
||
+}
|
||
+
|
||
+void IslandWindow::SetContent(winrt::Windows::UI::Xaml::UIElement content)
|
||
+{
|
||
+ _rootGrid.Children().Clear();
|
||
+ _rootGrid.Children().Append(content);
|
||
+}
|
||
+
|
||
+// Method Description:
|
||
+// - Gets the difference between window and client area size.
|
||
+// Arguments:
|
||
+// - dpi: dpi of a monitor on which the window is placed
|
||
+// Return Value
|
||
+// - The size difference
|
||
+SIZE IslandWindow::GetTotalNonClientExclusiveSize(const UINT dpi) const noexcept
|
||
+{
|
||
+ const auto windowStyle = static_cast<DWORD>(GetWindowLong(_window.get(), GWL_STYLE));
|
||
+ RECT islandFrame{};
|
||
+
|
||
+ // If we failed to get the correct window size for whatever reason, log
|
||
+ // the error and go on. We'll use whatever the control proposed as the
|
||
+ // size of our window, which will be at least close.
|
||
+ LOG_IF_WIN32_BOOL_FALSE(AdjustWindowRectExForDpi(&islandFrame, windowStyle, false, 0, dpi));
|
||
+
|
||
+ return {
|
||
+ islandFrame.right - islandFrame.left,
|
||
+ islandFrame.bottom - islandFrame.top
|
||
+ };
|
||
+}
|
||
+
|
||
+void IslandWindow::OnAppInitialized()
|
||
+{
|
||
+ // Do a quick resize to force the island to paint
|
||
+ const auto size = GetPhysicalSize();
|
||
+ OnSize(size.cx, size.cy);
|
||
+}
|
||
+
|
||
+// Method Description:
|
||
+// - Called when the app wants to change its theme. We'll update the root UI
|
||
+// element of the entire XAML tree, so that all UI elements get the theme
|
||
+// applied.
|
||
+// Arguments:
|
||
+// - arg: the ElementTheme to use as the new theme for the UI
|
||
+// Return Value:
|
||
+// - <none>
|
||
+void IslandWindow::OnApplicationThemeChanged(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme)
|
||
+{
|
||
+ _rootGrid.RequestedTheme(requestedTheme);
|
||
+ // Invalidate the window rect, so that we'll repaint any elements we're
|
||
+ // drawing ourselves to match the new theme
|
||
+ ::InvalidateRect(_window.get(), nullptr, false);
|
||
+}
|
||
+
|
||
+// Method Description:
|
||
+// - Updates our focus mode state. See _SetIsBorderless for more details.
|
||
+// Arguments:
|
||
+// - <none>
|
||
+// Return Value:
|
||
+// - <none>
|
||
+void IslandWindow::FocusModeChanged(const bool focusMode)
|
||
+{
|
||
+ // Do nothing if the value was unchanged.
|
||
+ if (focusMode == _borderless)
|
||
+ {
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ _SetIsBorderless(focusMode);
|
||
+}
|
||
+
|
||
+// Method Description:
|
||
+// - Updates our fullscreen state. See _SetIsFullscreen for more details.
|
||
+// Arguments:
|
||
+// - <none>
|
||
+// Return Value:
|
||
+// - <none>
|
||
+void IslandWindow::FullscreenChanged(const bool fullscreen)
|
||
+{
|
||
+ // Do nothing if the value was unchanged.
|
||
+ if (fullscreen == _fullscreen)
|
||
+ {
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ _SetIsFullscreen(fullscreen);
|
||
+}
|
||
+
|
||
+// Method Description:
|
||
+// - Enter or exit the "always on top" state. Before the window is created, this
|
||
+// value will later be used when we create the window to create the window on
|
||
+// top of all others. After the window is created, it will either enter the
|
||
+// group of topmost windows, or exit the group of topmost windows.
|
||
+// Arguments:
|
||
+// - alwaysOnTop: whether we should be entering or exiting always on top mode.
|
||
+// Return Value:
|
||
+// - <none>
|
||
+void IslandWindow::SetAlwaysOnTop(const bool alwaysOnTop)
|
||
+{
|
||
+ _alwaysOnTop = alwaysOnTop;
|
||
+
|
||
+ const auto hwnd = GetHandle();
|
||
+ if (hwnd)
|
||
+ {
|
||
+ SetWindowPos(hwnd,
|
||
+ _alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
|
||
+ 0, // the window dimensions are unused, because we're passing SWP_NOSIZE
|
||
+ 0,
|
||
+ 0,
|
||
+ 0,
|
||
+ SWP_NOMOVE | SWP_NOSIZE);
|
||
+ }
|
||
+}
|
||
+
|
||
+// From GdiEngine::s_SetWindowLongWHelper
|
||
+void _SetWindowLongWHelper(const HWND hWnd, const int nIndex, const LONG dwNewLong) noexcept
|
||
+{
|
||
+ // SetWindowLong has strange error handling. On success, it returns the
|
||
+ // previous Window Long value and doesn't modify the Last Error state. To
|
||
+ // deal with this, we set the last error to 0/S_OK first, call it, and if
|
||
+ // the previous long was 0, we check if the error was non-zero before
|
||
+ // reporting. Otherwise, we'll get an "Error: The operation has completed
|
||
+ // successfully." and there will be another screenshot on the internet
|
||
+ // making fun of Windows. See:
|
||
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms633591(v=vs.85).aspx
|
||
+ SetLastError(0);
|
||
+ LONG const lResult = SetWindowLongW(hWnd, nIndex, dwNewLong);
|
||
+ if (0 == lResult)
|
||
+ {
|
||
+ LOG_LAST_ERROR_IF(0 != GetLastError());
|
||
+ }
|
||
+}
|
||
+
|
||
+// Method Description:
|
||
+// - This is a helper to figure out what the window styles should be, given the
|
||
+// current state of flags like borderless mode and fullscreen mode.
|
||
+// Arguments:
|
||
+// - <none>
|
||
+// Return Value:
|
||
+// - a LONG with the appropriate flags set for our current window mode, to be used with GWL_STYLE
|
||
+LONG IslandWindow::_getDesiredWindowStyle() const
|
||
+{
|
||
+ auto windowStyle = GetWindowLongW(GetHandle(), GWL_STYLE);
|
||
+
|
||
+ // If we're both fullscreen and borderless, fullscreen mode takes precedence.
|
||
+
|
||
+ if (_fullscreen)
|
||
+ {
|
||
+ // When moving to fullscreen, remove WS_OVERLAPPEDWINDOW, which specifies
|
||
+ // styles for non-fullscreen windows (e.g. caption bar), and add the
|
||
+ // WS_POPUP style to allow us to size ourselves to the monitor size.
|
||
+ // Do the reverse when restoring from fullscreen.
|
||
+ // Doing these modifications to that window will cause a vista-style
|
||
+ // window frame to briefly appear when entering and exiting fullscreen.
|
||
+ WI_ClearFlag(windowStyle, WS_BORDER);
|
||
+ WI_ClearFlag(windowStyle, WS_SIZEBOX);
|
||
+ WI_ClearAllFlags(windowStyle, WS_OVERLAPPEDWINDOW);
|
||
+
|
||
+ WI_SetFlag(windowStyle, WS_POPUP);
|
||
+ return windowStyle;
|
||
+ }
|
||
+ else if (_borderless)
|
||
+ {
|
||
+ // When moving to borderless, remove WS_OVERLAPPEDWINDOW, which
|
||
+ // specifies styles for non-fullscreen windows (e.g. caption bar), and
|
||
+ // add the WS_BORDER and WS_SIZEBOX styles. This allows us to still have
|
||
+ // a small resizing frame, but without a full titlebar, nor caption
|
||
+ // buttons.
|
||
+
|
||
+ WI_ClearAllFlags(windowStyle, WS_OVERLAPPEDWINDOW);
|
||
+ WI_ClearFlag(windowStyle, WS_POPUP);
|
||
+
|
||
+ WI_SetFlag(windowStyle, WS_BORDER);
|
||
+ WI_SetFlag(windowStyle, WS_SIZEBOX);
|
||
+ return windowStyle;
|
||
+ }
|
||
+
|
||
+ // Here, we're not in either fullscreen or borderless mode. Return to
|
||
+ // WS_OVERLAPPEDWINDOW.
|
||
+ WI_ClearFlag(windowStyle, WS_POPUP);
|
||
+ WI_ClearFlag(windowStyle, WS_BORDER);
|
||
+ WI_ClearFlag(windowStyle, WS_SIZEBOX);
|
||
+
|
||
+ WI_SetAllFlags(windowStyle, WS_OVERLAPPEDWINDOW);
|
||
+
|
||
+ return windowStyle;
|
||
+}
|
||
+
|
||
+// Method Description:
|
||
+// - Enable or disable focus mode. When entering focus mode, we'll
|
||
+// need to manually hide the entire titlebar.
|
||
+// - When we're entering focus we need to do some additional modification
|
||
+// of our window styles. However, the NonClientIslandWindow very explicitly
|
||
+// _doesn't_ need to do these steps.
|
||
+// Arguments:
|
||
+// - borderlessEnabled: If true, we're entering focus mode. If false, we're leaving.
|
||
+// Return Value:
|
||
+// - <none>
|
||
+void IslandWindow::_SetIsBorderless(const bool borderlessEnabled)
|
||
+{
|
||
+ _borderless = borderlessEnabled;
|
||
+
|
||
+ HWND const hWnd = GetHandle();
|
||
+
|
||
+ // First, modify regular window styles as appropriate
|
||
+ auto windowStyle = _getDesiredWindowStyle();
|
||
+ _SetWindowLongWHelper(hWnd, GWL_STYLE, windowStyle);
|
||
+
|
||
+ // Now modify extended window styles as appropriate
|
||
+ // When moving to fullscreen, remove the window edge style to avoid an
|
||
+ // ugly border when not focused.
|
||
+ auto exWindowStyle = GetWindowLongW(hWnd, GWL_EXSTYLE);
|
||
+ WI_UpdateFlag(exWindowStyle, WS_EX_WINDOWEDGE, !_fullscreen);
|
||
+ _SetWindowLongWHelper(hWnd, GWL_EXSTYLE, exWindowStyle);
|
||
+
|
||
+ // Resize the window, with SWP_FRAMECHANGED, to trigger user32 to
|
||
+ // recalculate the non/client areas
|
||
+ const til::rectangle windowPos{ GetWindowRect() };
|
||
+ SetWindowPos(GetHandle(),
|
||
+ HWND_TOP,
|
||
+ windowPos.left<int>(),
|
||
+ windowPos.top<int>(),
|
||
+ windowPos.width<int>(),
|
||
+ windowPos.height<int>(),
|
||
+ SWP_SHOWWINDOW | SWP_FRAMECHANGED);
|
||
+}
|
||
+
|
||
+// Method Description:
|
||
+// - Controls setting us into or out of fullscreen mode. Largely taken from
|
||
+// Window::SetIsFullscreen in conhost.
|
||
+// - When entering fullscreen mode, we'll save the current window size and
|
||
+// location, and expand to take the entire monitor size. When leaving, we'll
|
||
+// use that saved size to restore back to.
|
||
+// Arguments:
|
||
+// - fullscreenEnabled true if we should enable fullscreen mode, false to disable.
|
||
+// Return Value:
|
||
+// - <none>
|
||
+void IslandWindow::_SetIsFullscreen(const bool fullscreenEnabled)
|
||
+{
|
||
+ // It is possible to enter _SetIsFullscreen even if we're already in full
|
||
+ // screen. Use the old is in fullscreen flag to gate checks that rely on the
|
||
+ // current state.
|
||
+ const auto oldIsInFullscreen = _fullscreen;
|
||
+ _fullscreen = fullscreenEnabled;
|
||
+
|
||
+ HWND const hWnd = GetHandle();
|
||
+
|
||
+ // First, modify regular window styles as appropriate
|
||
+ auto windowStyle = _getDesiredWindowStyle();
|
||
+ _SetWindowLongWHelper(hWnd, GWL_STYLE, windowStyle);
|
||
+
|
||
+ // Now modify extended window styles as appropriate
|
||
+ // When moving to fullscreen, remove the window edge style to avoid an
|
||
+ // ugly border when not focused.
|
||
+ auto exWindowStyle = GetWindowLongW(hWnd, GWL_EXSTYLE);
|
||
+ WI_UpdateFlag(exWindowStyle, WS_EX_WINDOWEDGE, !_fullscreen);
|
||
+ _SetWindowLongWHelper(hWnd, GWL_EXSTYLE, exWindowStyle);
|
||
+
|
||
+ // When entering/exiting fullscreen mode, we also need to backup/restore the
|
||
+ // current window size, and resize the window to match the new state.
|
||
+ _BackupWindowSizes(oldIsInFullscreen);
|
||
+ _ApplyWindowSize();
|
||
+}
|
||
+
|
||
+// Method Description:
|
||
+// - Used in entering/exiting fullscreen mode. Saves the current window size,
|
||
+// and the full size of the monitor, for use in _ApplyWindowSize.
|
||
+// - Taken from conhost's Window::_BackupWindowSizes
|
||
+// Arguments:
|
||
+// - fCurrentIsInFullscreen: true if we're currently in fullscreen mode.
|
||
+// Return Value:
|
||
+// - <none>
|
||
+void IslandWindow::_BackupWindowSizes(const bool fCurrentIsInFullscreen)
|
||
+{
|
||
+ if (_fullscreen)
|
||
+ {
|
||
+ // Note: the current window size depends on the current state of the
|
||
+ // window. So don't back it up if we're already in full screen.
|
||
+ if (!fCurrentIsInFullscreen)
|
||
+ {
|
||
+ _nonFullscreenWindowSize = GetWindowRect();
|
||
+ }
|
||
+
|
||
+ // get and back up the current monitor's size
|
||
+ HMONITOR const hCurrentMonitor = MonitorFromWindow(GetHandle(), MONITOR_DEFAULTTONEAREST);
|
||
+ MONITORINFO currMonitorInfo;
|
||
+ currMonitorInfo.cbSize = sizeof(currMonitorInfo);
|
||
+ if (GetMonitorInfo(hCurrentMonitor, &currMonitorInfo))
|
||
+ {
|
||
+ _fullscreenWindowSize = currMonitorInfo.rcMonitor;
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+// Method Description:
|
||
+// - Applys the appropriate window size for transitioning to/from fullscreen mode.
|
||
+// - Taken from conhost's Window::_ApplyWindowSize
|
||
+// Arguments:
|
||
+// - <none>
|
||
+// Return Value:
|
||
+// - <none>
|
||
+void IslandWindow::_ApplyWindowSize()
|
||
+{
|
||
+ const auto newSize = _fullscreen ? _fullscreenWindowSize : _nonFullscreenWindowSize;
|
||
+ LOG_IF_WIN32_BOOL_FALSE(SetWindowPos(GetHandle(),
|
||
+ HWND_TOP,
|
||
+ newSize.left,
|
||
+ newSize.top,
|
||
+ newSize.right - newSize.left,
|
||
+ newSize.bottom - newSize.top,
|
||
+ SWP_FRAMECHANGED));
|
||
+}
|
||
+
|
||
+DEFINE_EVENT(IslandWindow, DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>);
|
||
+DEFINE_EVENT(IslandWindow, WindowCloseButtonClicked, _windowCloseButtonClickedHandler, winrt::delegate<>);
|
||
diff --git a/src/tools/ScratchIsland/IslandWindow.h b/src/tools/ScratchIsland/IslandWindow.h
|
||
new file mode 100644
|
||
index 000000000..109f7c1ec
|
||
--- /dev/null
|
||
+++ b/src/tools/ScratchIsland/IslandWindow.h
|
||
@@ -0,0 +1,77 @@
|
||
+// Copyright (c) Microsoft Corporation.
|
||
+// Licensed under the MIT license.
|
||
+
|
||
+#include "pch.h"
|
||
+#include "BaseWindow.h"
|
||
+#include "../../cascadia/inc/cppwinrt_utils.h"
|
||
+
|
||
+class IslandWindow :
|
||
+ public BaseWindow<IslandWindow>
|
||
+{
|
||
+public:
|
||
+ IslandWindow() noexcept;
|
||
+ virtual ~IslandWindow() override;
|
||
+
|
||
+ virtual void MakeWindow() noexcept;
|
||
+ void Close();
|
||
+ virtual void OnSize(const UINT width, const UINT height);
|
||
+
|
||
+ [[nodiscard]] virtual LRESULT MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept override;
|
||
+ void OnResize(const UINT width, const UINT height) override;
|
||
+ void OnMinimize() override;
|
||
+ void OnRestore() override;
|
||
+ virtual void OnAppInitialized();
|
||
+ virtual void SetContent(winrt::Windows::UI::Xaml::UIElement content);
|
||
+ virtual void OnApplicationThemeChanged(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme);
|
||
+ virtual SIZE GetTotalNonClientExclusiveSize(const UINT dpi) const noexcept;
|
||
+
|
||
+ virtual void Initialize();
|
||
+
|
||
+ void SetCreateCallback(std::function<void(const HWND, const RECT)> pfn) noexcept;
|
||
+
|
||
+ void FocusModeChanged(const bool focusMode);
|
||
+ void FullscreenChanged(const bool fullscreen);
|
||
+ void SetAlwaysOnTop(const bool alwaysOnTop);
|
||
+
|
||
+#pragma endregion
|
||
+
|
||
+ DECLARE_EVENT(DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>);
|
||
+ DECLARE_EVENT(WindowCloseButtonClicked, _windowCloseButtonClickedHandler, winrt::delegate<>);
|
||
+ WINRT_CALLBACK(MouseScrolled, winrt::delegate<void(til::point, int32_t)>);
|
||
+
|
||
+protected:
|
||
+ void ForceResize()
|
||
+ {
|
||
+ // Do a quick resize to force the island to paint
|
||
+ const auto size = GetPhysicalSize();
|
||
+ OnSize(size.cx, size.cy);
|
||
+ }
|
||
+
|
||
+ HWND _interopWindowHandle;
|
||
+
|
||
+ winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource _source;
|
||
+
|
||
+ winrt::Windows::UI::Xaml::Controls::Grid _rootGrid;
|
||
+
|
||
+ std::function<void(const HWND, const RECT)> _pfnCreateCallback;
|
||
+
|
||
+ void _HandleCreateWindow(const WPARAM wParam, const LPARAM lParam) noexcept;
|
||
+ [[nodiscard]] LRESULT _OnSizing(const WPARAM wParam, const LPARAM lParam);
|
||
+
|
||
+ bool _borderless{ false };
|
||
+ bool _fullscreen{ false };
|
||
+ bool _alwaysOnTop{ false };
|
||
+ RECT _fullscreenWindowSize;
|
||
+ RECT _nonFullscreenWindowSize;
|
||
+
|
||
+ virtual void _SetIsBorderless(const bool borderlessEnabled);
|
||
+ virtual void _SetIsFullscreen(const bool fullscreenEnabled);
|
||
+ void _BackupWindowSizes(const bool currentIsInFullscreen);
|
||
+ void _ApplyWindowSize();
|
||
+
|
||
+ LONG _getDesiredWindowStyle() const;
|
||
+
|
||
+private:
|
||
+ // This minimum width allows for width the tabs fit
|
||
+ static constexpr long minimumWidth = 460L;
|
||
+};
|
||
diff --git a/src/tools/ScratchIsland/ScratchIsland.def b/src/tools/ScratchIsland/ScratchIsland.def
|
||
new file mode 100644
|
||
index 000000000..5f282702b
|
||
--- /dev/null
|
||
+++ b/src/tools/ScratchIsland/ScratchIsland.def
|
||
@@ -0,0 +1 @@
|
||
+
|
||
\ No newline at end of file
|
||
diff --git a/src/tools/ScratchIsland/ScratchIsland.manifest b/src/tools/ScratchIsland/ScratchIsland.manifest
|
||
new file mode 100644
|
||
index 000000000..a447bc8fd
|
||
--- /dev/null
|
||
+++ b/src/tools/ScratchIsland/ScratchIsland.manifest
|
||
@@ -0,0 +1,24 @@
|
||
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||
+
|
||
+ <!-- This file is copied into ut_app/TerminalApp.Unit.Tests.manifest as part
|
||
+ of the pre-build step for that project. Changes should only be made to the
|
||
+ WindowsTerminal version of the file. -->
|
||
+
|
||
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||
+ <application>
|
||
+ <!-- Windows 10 1903 -->
|
||
+ <!-- See https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/xaml-islands -->
|
||
+ <!-- "maxversiontested" is CASE SENSITIVE. Do not change this.-->
|
||
+ <maxversiontested Id="10.0.18362.0"/>
|
||
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||
+ </application>
|
||
+ </compatibility>
|
||
+
|
||
+ <application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||
+ <windowsSettings>
|
||
+ <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||
+ <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
|
||
+ </windowsSettings>
|
||
+ </application>
|
||
+</assembly>
|
||
diff --git a/src/tools/ScratchIsland/ScratchIsland.rc b/src/tools/ScratchIsland/ScratchIsland.rc
|
||
new file mode 100644
|
||
index 000000000..78a784cf2
|
||
--- /dev/null
|
||
+++ b/src/tools/ScratchIsland/ScratchIsland.rc
|
||
@@ -0,0 +1,94 @@
|
||
+// Microsoft Visual C++ generated resource script.
|
||
+//
|
||
+#include "resource.h"
|
||
+
|
||
+#define APSTUDIO_READONLY_SYMBOLS
|
||
+/////////////////////////////////////////////////////////////////////////////
|
||
+//
|
||
+// Generated from the TEXTINCLUDE 2 resource.
|
||
+//
|
||
+#include "winres.h"
|
||
+
|
||
+/////////////////////////////////////////////////////////////////////////////
|
||
+#undef APSTUDIO_READONLY_SYMBOLS
|
||
+
|
||
+/////////////////////////////////////////////////////////////////////////////
|
||
+// English (United States) resources
|
||
+
|
||
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||
+#pragma code_page(1252)
|
||
+
|
||
+#ifdef APSTUDIO_INVOKED
|
||
+/////////////////////////////////////////////////////////////////////////////
|
||
+//
|
||
+// TEXTINCLUDE
|
||
+//
|
||
+
|
||
+1 TEXTINCLUDE
|
||
+BEGIN
|
||
+ "resource.h\0"
|
||
+END
|
||
+
|
||
+2 TEXTINCLUDE
|
||
+BEGIN
|
||
+ "#include ""winres.h""\r\n"
|
||
+ "\0"
|
||
+END
|
||
+
|
||
+3 TEXTINCLUDE
|
||
+BEGIN
|
||
+ "\r\n"
|
||
+ "\0"
|
||
+END
|
||
+
|
||
+#endif // APSTUDIO_INVOKED
|
||
+
|
||
+
|
||
+/////////////////////////////////////////////////////////////////////////////
|
||
+//
|
||
+// Icon
|
||
+//
|
||
+
|
||
+// Icon with lowest ID value placed first to ensure application icon
|
||
+// remains consistent on all systems.
|
||
+IDI_APPICON ICON "..\\..\\..\\res\\terminal.ico"
|
||
+
|
||
+
|
||
+/////////////////////////////////////////////////////////////////////////////
|
||
+//
|
||
+// String Table
|
||
+//
|
||
+
|
||
+STRINGTABLE
|
||
+BEGIN
|
||
+ IDS_ERROR_DIALOG_TITLE "Error"
|
||
+ IDS_HELP_DIALOG_TITLE "Help"
|
||
+ IDS_ERROR_ARCHITECTURE_FORMAT
|
||
+ "Windows Terminal is designed to run on your system's native architecture (%s).\nYou are currently using the %s version.\n\nPlease use the version of Windows Terminal that matches your system's native architecture."
|
||
+ IDS_X86_ARCHITECTURE "i386"
|
||
+END
|
||
+
|
||
+STRINGTABLE
|
||
+BEGIN
|
||
+ IDS_AMD64_ARCHITECTURE "AMD64"
|
||
+ IDS_ARM64_ARCHITECTURE "ARM64"
|
||
+ IDS_ARM_ARCHITECTURE "ARM"
|
||
+ IDS_UNKNOWN_ARCHITECTURE "Unknown"
|
||
+END
|
||
+
|
||
+#endif // English (United States) resources
|
||
+/////////////////////////////////////////////////////////////////////////////
|
||
+
|
||
+
|
||
+
|
||
+#ifndef APSTUDIO_INVOKED
|
||
+/////////////////////////////////////////////////////////////////////////////
|
||
+//
|
||
+// Generated from the TEXTINCLUDE 3 resource.
|
||
+//
|
||
+
|
||
+
|
||
+/////////////////////////////////////////////////////////////////////////////
|
||
+#endif // not APSTUDIO_INVOKED
|
||
+
|
||
diff --git a/src/tools/ScratchIsland/ScratchIsland.vcxproj b/src/tools/ScratchIsland/ScratchIsland.vcxproj
|
||
new file mode 100644
|
||
index 000000000..d155853ab
|
||
--- /dev/null
|
||
+++ b/src/tools/ScratchIsland/ScratchIsland.vcxproj
|
||
@@ -0,0 +1,164 @@
|
||
+<?xml version="1.0" encoding="utf-8"?>
|
||
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||
+ <Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props')" />
|
||
+
|
||
+ <PropertyGroup Label="Globals">
|
||
+ <ProjectGuid>{23a1f736-cd19-4196-980f-84bcd50cf783}</ProjectGuid>
|
||
+ <Keyword>Win32Proj</Keyword>
|
||
+ <RootNamespace>ScratchIsland</RootNamespace>
|
||
+ <ProjectName>ScratchIsland</ProjectName>
|
||
+ <TargetName>ScratchIsland</TargetName>
|
||
+ <ConfigurationType>Application</ConfigurationType>
|
||
+ <OpenConsoleUniversalApp>false</OpenConsoleUniversalApp>
|
||
+ <ApplicationType>Windows Store</ApplicationType>
|
||
+ <WindowsStoreApp>true</WindowsStoreApp>
|
||
+ <WindowsAppContainer>false</WindowsAppContainer>
|
||
+ <!-- IMPORTANT! Xaml Islands only works on >= 17709 -->
|
||
+ <!-- IMPORTANT! cppwinrt.pre.props specifies 17134 -->
|
||
+ <WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
|
||
+ <!-- DON'T REDIRECT OUR OUTPUT -->
|
||
+ <NoOutputRedirection>true</NoOutputRedirection>
|
||
+ </PropertyGroup>
|
||
+
|
||
+ <Import Project="..\..\..\common.openconsole.props" Condition="'$(OpenConsoleDir)'==''" />
|
||
+ <Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
|
||
+
|
||
+ <ItemDefinitionGroup>
|
||
+ <ClCompile>
|
||
+ <SDLCheck>true</SDLCheck>
|
||
+ </ClCompile>
|
||
+ </ItemDefinitionGroup>
|
||
+
|
||
+ <ItemDefinitionGroup>
|
||
+ <ClCompile>
|
||
+ <AdditionalIncludeDirectories>$(OpenConsoleDir)\src\inc;$(OpenConsoleDir)\dep;$(OpenConsoleDir)\dep\Console;$(OpenConsoleDir)\dep\Win32K;$(OpenConsoleDir)\dep\gsl\include;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
|
||
+ </ClCompile>
|
||
+ <ClCompile>
|
||
+ <!-- Manually include the generated TerminalCore header's path, because
|
||
+ adding a project reference will confuse msbuild, because TerminalCore
|
||
+ isn't a dll, it's a lib, and cppwinrt won't include a lib's header -->
|
||
+ <AdditionalIncludeDirectories>"$(OpenConsoleDir)src\cascadia\TerminalCore\lib\Generated Files";%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
|
||
+ </ClCompile>
|
||
+ <Link>
|
||
+ <AdditionalDependencies>gdi32.lib;dwmapi.lib;Shcore.lib;UxTheme.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||
+ </Link>
|
||
+ </ItemDefinitionGroup>
|
||
+ <PropertyGroup>
|
||
+ <GenerateManifest>true</GenerateManifest>
|
||
+ <EmbedManifest>true</EmbedManifest>
|
||
+ </PropertyGroup>
|
||
+ <!-- Source Files -->
|
||
+ <ItemGroup>
|
||
+ <ClInclude Include="pch.h" />
|
||
+ <ClInclude Include="resource.h" />
|
||
+ <ClInclude Include="AppHost.h" />
|
||
+ <ClInclude Include="BaseWindow.h" />
|
||
+ <ClInclude Include="IslandWindow.h" />
|
||
+ </ItemGroup>
|
||
+ <ItemGroup>
|
||
+ <ClCompile Include="pch.cpp">
|
||
+ <PrecompiledHeader>Create</PrecompiledHeader>
|
||
+ </ClCompile>
|
||
+ <ClCompile Include="main.cpp" />
|
||
+ <ClCompile Include="AppHost.cpp" />
|
||
+ <ClCompile Include="IslandWindow.cpp" />
|
||
+ </ItemGroup>
|
||
+ <ItemGroup>
|
||
+ <ResourceCompile Include="ScratchIsland.rc" />
|
||
+ </ItemGroup>
|
||
+ <ItemGroup>
|
||
+ <Manifest Include="ScratchIsland.manifest" />
|
||
+ </ItemGroup>
|
||
+ <ItemGroup>
|
||
+ <None Include="packages.config" />
|
||
+ </ItemGroup>
|
||
+ <!-- Dependencies -->
|
||
+ <ItemGroup>
|
||
+ <!-- Even though we do have proper recursive dependencies, we want to keep some of these here
|
||
+ so that the AppX Manifest contains their activatable classes. -->
|
||
+ <!-- <ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalSettings\TerminalSettings.vcxproj" /> -->
|
||
+ <!-- <ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalControl\TerminalControl.vcxproj" /> -->
|
||
+ <!-- <ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalConnection\TerminalConnection.vcxproj" /> -->
|
||
+
|
||
+ <!-- <ProjectReference Include="$(OpenConsoleDir)src\cascadia\TerminalApp\TerminalApp.vcxproj" /> -->
|
||
+
|
||
+ <ProjectReference Include="$(OpenConsoleDir)src\types\lib\types.vcxproj" />
|
||
+ </ItemGroup>
|
||
+
|
||
+ <!--
|
||
+ This ItemGroup and the Globals PropertyGroup below it are required in order
|
||
+ to enable F5 debugging for the unpackaged application
|
||
+ -->
|
||
+ <ItemGroup>
|
||
+ <PropertyPageSchema Include="$(VCTargetsPath)$(LangID)\debugger_general.xml" />
|
||
+ <PropertyPageSchema Include="$(VCTargetsPath)$(LangID)\debugger_local_windows.xml" />
|
||
+ </ItemGroup>
|
||
+ <PropertyGroup Label="Globals">
|
||
+ <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||
+ </PropertyGroup>
|
||
+
|
||
+ <Import Project="$(OpenConsoleDir)src\cppwinrt.build.post.props" />
|
||
+
|
||
+ <Import Project="..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.200609001\build\native\Microsoft.UI.Xaml.targets" Condition="Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.200609001\build\native\Microsoft.UI.Xaml.targets')" />
|
||
+ <Import Project="..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets" Condition="Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" />
|
||
+ <Import Project="..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.1-rc\build\native\Microsoft.VCRTForwarders.140.targets" Condition="Exists('..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.1-rc\build\native\Microsoft.VCRTForwarders.140.targets')" />
|
||
+ <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||
+ <PropertyGroup>
|
||
+ <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||
+ </PropertyGroup>
|
||
+ <Error Condition="!Exists('..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.200609001\build\native\Microsoft.UI.Xaml.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.UI.Xaml.2.5.0-prerelease.200609001\build\native\Microsoft.UI.Xaml.targets'))" />
|
||
+ <Error Condition="!Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.props'))" />
|
||
+ <Error Condition="!Exists('..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0\build\native\Microsoft.Toolkit.Win32.UI.XamlApplication.targets'))" />
|
||
+ <Error Condition="!Exists('..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.1-rc\build\native\Microsoft.VCRTForwarders.140.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.VCRTForwarders.140.1.0.1-rc\build\native\Microsoft.VCRTForwarders.140.targets'))" />
|
||
+ <Error Condition="!Exists('..\..\..\packages\Terminal.ThemeHelpers.0.2.200324001\build\native\Terminal.ThemeHelpers.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Terminal.ThemeHelpers.0.2.200324001\build\native\Terminal.ThemeHelpers.targets'))" />
|
||
+ </Target>
|
||
+
|
||
+ <!-- Override GetPackagingOutputs to roll up all our dependencies.
|
||
+ This ensures that when the WAP packaging project asks what files go into
|
||
+ the package, we tell it.
|
||
+ This is a heavily stripped version of the one in Microsoft.*.AppxPackage.targets.
|
||
+ -->
|
||
+ <PropertyGroup>
|
||
+ <_ContinueOnError Condition="'$(BuildingProject)' == 'true'">true</_ContinueOnError>
|
||
+ <_ContinueOnError Condition="'$(BuildingProject)' != 'true'">false</_ContinueOnError>
|
||
+ </PropertyGroup>
|
||
+ <Target Name="GetPackagingOutputs" Returns="@(PackagingOutputs)">
|
||
+ <MSBuild
|
||
+ Projects="@(ProjectReferenceWithConfiguration)"
|
||
+ Targets="GetPackagingOutputs"
|
||
+ BuildInParallel="$(BuildInParallel)"
|
||
+ Properties="%(ProjectReferenceWithConfiguration.SetConfiguration); %(ProjectReferenceWithConfiguration.SetPlatform)"
|
||
+ Condition="'@(ProjectReferenceWithConfiguration)' != ''
|
||
+ and '%(ProjectReferenceWithConfiguration.BuildReference)' == 'true'
|
||
+ and '%(ProjectReferenceWithConfiguration.ReferenceOutputAssembly)' == 'true'"
|
||
+ ContinueOnError="$(_ContinueOnError)">
|
||
+ <Output TaskParameter="TargetOutputs" ItemName="_PackagingOutputsFromOtherProjects"/>
|
||
+ </MSBuild>
|
||
+
|
||
+ <ItemGroup>
|
||
+ <PackagingOutputs Include="@(_PackagingOutputsFromOtherProjects)" />
|
||
+ </ItemGroup>
|
||
+
|
||
+ <!-- **BEGIN VC LIBS HACK** -->
|
||
+ <PropertyGroup>
|
||
+ <ReasonablePlatform Condition="'$(Platform)'=='Win32'">x86</ReasonablePlatform>
|
||
+ <ReasonablePlatform Condition="'$(ReasonablePlatform)'==''">$(Platform)</ReasonablePlatform>
|
||
+ </PropertyGroup>
|
||
+
|
||
+ <ItemGroup Condition="'$(ScratchIslandOfficialBuild)'=='true'">
|
||
+ <!-- Add all the CRT libs as content; these must be inside a Target as they are wildcards. -->
|
||
+ <_OpenConsoleVCLibToCopy Include="$(VCToolsRedistInstallDir)\$(ReasonablePlatform)\Microsoft.VC142.CRT\*.dll" />
|
||
+
|
||
+ <PackagingOutputs Include="@(_OpenConsoleVCLibToCopy)">
|
||
+ <ProjectName>$(ProjectName)</ProjectName>
|
||
+ <OutputGroup>BuiltProjectOutputGroup</OutputGroup>
|
||
+ <TargetPath>%(Filename)%(Extension)</TargetPath>
|
||
+ </PackagingOutputs>
|
||
+ </ItemGroup>
|
||
+ <!-- **END VC LIBS HACK** -->
|
||
+ </Target>
|
||
+
|
||
+ <Import Project="$(OpenConsoleDir)\build\rules\GenerateSxsManifestsFromWinmds.targets" />
|
||
+ <Import Project="..\..\..\packages\Terminal.ThemeHelpers.0.2.200324001\build\native\Terminal.ThemeHelpers.targets" Condition="Exists('..\..\..\packages\Terminal.ThemeHelpers.0.2.200324001\build\native\Terminal.ThemeHelpers.targets')" />
|
||
+</Project>
|
||
+
|
||
diff --git a/src/tools/ScratchIsland/main.cpp b/src/tools/ScratchIsland/main.cpp
|
||
new file mode 100644
|
||
index 000000000..777e3ee9e
|
||
--- /dev/null
|
||
+++ b/src/tools/ScratchIsland/main.cpp
|
||
@@ -0,0 +1,54 @@
|
||
+// Copyright (c) Microsoft Corporation.
|
||
+// Licensed under the MIT license.
|
||
+
|
||
+#include "pch.h"
|
||
+#include "AppHost.h"
|
||
+#include "resource.h"
|
||
+
|
||
+using namespace winrt;
|
||
+using namespace winrt::Windows::UI;
|
||
+using namespace winrt::Windows::UI::Composition;
|
||
+using namespace winrt::Windows::UI::Xaml::Hosting;
|
||
+using namespace winrt::Windows::Foundation::Numerics;
|
||
+
|
||
+int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
|
||
+{
|
||
+ // If Terminal is spawned by a shortcut that requests that it run in a new process group
|
||
+ // while attached to a console session, that request is nonsense. That request will, however,
|
||
+ // cause WT to start with Ctrl-C disabled. This wouldn't matter, because it's a Windows-subsystem
|
||
+ // application. Unfortunately, that state is heritable. In short, if you start WT using cmd in
|
||
+ // a weird way, ^C stops working _inside_ the terminal. Mad.
|
||
+ SetConsoleCtrlHandler(NULL, FALSE);
|
||
+
|
||
+ // Make sure to call this so we get WM_POINTER messages.
|
||
+ EnableMouseInPointer(true);
|
||
+
|
||
+ // !!! LOAD BEARING !!!
|
||
+ // We must initialize the main thread as a single-threaded apartment before
|
||
+ // constructing any Xaml objects. Failing to do so will cause some issues
|
||
+ // in accessibility somewhere down the line when a UIAutomation object will
|
||
+ // be queried on the wrong thread at the wrong time.
|
||
+ // We used to initialize as STA only _after_ initializing the application
|
||
+ // host, which loaded the settings. The settings needed to be loaded in MTA
|
||
+ // because we were using the Windows.Storage APIs. Since we're no longer
|
||
+ // doing that, we can safely init as STA before any WinRT dispatches.
|
||
+ winrt::init_apartment(winrt::apartment_type::single_threaded);
|
||
+
|
||
+ // Create the AppHost object, which will create both the window and the
|
||
+ // Terminal App. This MUST BE constructed before the Xaml manager as TermApp
|
||
+ // provides an implementation of Windows.UI.Xaml.Application.
|
||
+ AppHost host;
|
||
+
|
||
+ // Initialize the xaml content. This must be called AFTER the
|
||
+ // WindowsXamlManager is initialized.
|
||
+ host.Initialize();
|
||
+
|
||
+ MSG message;
|
||
+
|
||
+ while (GetMessage(&message, nullptr, 0, 0))
|
||
+ {
|
||
+ TranslateMessage(&message);
|
||
+ DispatchMessage(&message);
|
||
+ }
|
||
+ return 0;
|
||
+}
|
||
diff --git a/src/tools/ScratchIsland/packages.config b/src/tools/ScratchIsland/packages.config
|
||
new file mode 100644
|
||
index 000000000..166f8861f
|
||
--- /dev/null
|
||
+++ b/src/tools/ScratchIsland/packages.config
|
||
@@ -0,0 +1,8 @@
|
||
+<?xml version="1.0" encoding="utf-8"?>
|
||
+<packages>
|
||
+ <package id="Microsoft.Windows.CppWinRT" version="2.0.200316.3" targetFramework="native" />
|
||
+ <package id="Microsoft.Toolkit.Win32.UI.XamlApplication" version="6.0.0" targetFramework="native" />
|
||
+ <package id="Microsoft.UI.Xaml" version="2.5.0-prerelease.200609001" targetFramework="native" />
|
||
+ <package id="Microsoft.VCRTForwarders.140" version="1.0.1-rc" targetFramework="native" />
|
||
+ <package id="Terminal.ThemeHelpers" version="0.2.200324001" targetFramework="native" />
|
||
+</packages>
|
||
\ No newline at end of file
|
||
diff --git a/src/tools/ScratchIsland/pch.cpp b/src/tools/ScratchIsland/pch.cpp
|
||
new file mode 100644
|
||
index 000000000..398a99f66
|
||
--- /dev/null
|
||
+++ b/src/tools/ScratchIsland/pch.cpp
|
||
@@ -0,0 +1,4 @@
|
||
+// Copyright (c) Microsoft Corporation.
|
||
+// Licensed under the MIT license.
|
||
+
|
||
+#include "pch.h"
|
||
diff --git a/src/tools/ScratchIsland/pch.h b/src/tools/ScratchIsland/pch.h
|
||
new file mode 100644
|
||
index 000000000..40be55842
|
||
--- /dev/null
|
||
+++ b/src/tools/ScratchIsland/pch.h
|
||
@@ -0,0 +1,74 @@
|
||
+/*++
|
||
+Copyright (c) Microsoft Corporation
|
||
+Licensed under the MIT license.
|
||
+
|
||
+Module Name:
|
||
+- pch.h
|
||
+
|
||
+Abstract:
|
||
+- Contains external headers to include in the precompile phase of console build process.
|
||
+- Avoid including internal project headers. Instead include them only in the classes that need them (helps with test project building).
|
||
+--*/
|
||
+
|
||
+#pragma once
|
||
+
|
||
+// Ignore checked iterators warning from VC compiler.
|
||
+#define _SCL_SECURE_NO_WARNINGS
|
||
+
|
||
+// Block minwindef.h min/max macros to prevent <algorithm> conflict
|
||
+#define NOMINMAX
|
||
+
|
||
+#define WIN32_LEAN_AND_MEAN
|
||
+#include <unknwn.h>
|
||
+
|
||
+#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
|
||
+
|
||
+#include <windows.h>
|
||
+#include <UIAutomation.h>
|
||
+#include <cstdlib>
|
||
+#include <cstring>
|
||
+#include <shellscalingapi.h>
|
||
+#include <windowsx.h>
|
||
+
|
||
+// Manually include til after we include Windows.Foundation to give it winrt superpowers
|
||
+#define BLOCK_TIL
|
||
+#include "../inc/LibraryIncludes.h"
|
||
+
|
||
+// This is inexplicable, but for whatever reason, cppwinrt conflicts with the
|
||
+// SDK definition of this function, so the only fix is to undef it.
|
||
+// from WinBase.h
|
||
+// Windows::UI::Xaml::Media::Animation::IStoryboard::GetCurrentTime
|
||
+#ifdef GetCurrentTime
|
||
+#undef GetCurrentTime
|
||
+#endif
|
||
+
|
||
+#include <wil/cppwinrt.h>
|
||
+
|
||
+// Needed just for XamlIslands to work at all:
|
||
+#include <winrt/Windows.system.h>
|
||
+#include <winrt/Windows.Foundation.Collections.h>
|
||
+#include <winrt/Windows.UI.Xaml.Hosting.h>
|
||
+#include <windows.ui.xaml.hosting.desktopwindowxamlsource.h>
|
||
+
|
||
+// Additional headers for various xaml features. We need:
|
||
+// * Controls for grid
|
||
+// * Media for ScaleTransform
|
||
+#include <winrt/Windows.UI.Xaml.Controls.h>
|
||
+#include <winrt/Windows.ui.xaml.media.h>
|
||
+#include <windows.ui.xaml.media.dxinterop.h>
|
||
+
|
||
+#include <wil/resource.h>
|
||
+#include <wil/win32_helpers.h>
|
||
+
|
||
+// Including TraceLogging essentials for the binary
|
||
+#include <TraceLoggingProvider.h>
|
||
+#include <winmeta.h>
|
||
+TRACELOGGING_DECLARE_PROVIDER(g_hWindowsTerminalProvider);
|
||
+#include <telemetry\ProjectTelemetry.h>
|
||
+#include <TraceLoggingActivity.h>
|
||
+
|
||
+// For commandline argument processing
|
||
+#include <shellapi.h>
|
||
+#include <processenv.h>
|
||
+
|
||
+#include "til.h"
|
||
diff --git a/src/tools/ScratchIsland/resource.h b/src/tools/ScratchIsland/resource.h
|
||
new file mode 100644
|
||
index 000000000..011413eae
|
||
--- /dev/null
|
||
+++ b/src/tools/ScratchIsland/resource.h
|
||
@@ -0,0 +1,24 @@
|
||
+//{{NO_DEPENDENCIES}}
|
||
+// Microsoft Visual C++ generated include file.
|
||
+// Used by WindowsTerminal.rc
|
||
+//
|
||
+#define IDI_APPICON 101
|
||
+#define IDS_ERROR_DIALOG_TITLE 105
|
||
+#define IDS_HELP_DIALOG_TITLE 106
|
||
+#define IDS_ERROR_ARCHITECTURE_FORMAT 110
|
||
+#define IDS_X86_ARCHITECTURE 111
|
||
+#define IDS_AMD64_ARCHITECTURE 112
|
||
+#define IDS_ARM64_ARCHITECTURE 113
|
||
+#define IDS_ARM_ARCHITECTURE 114
|
||
+#define IDS_UNKNOWN_ARCHITECTURE 115
|
||
+
|
||
+// Next default values for new objects
|
||
+//
|
||
+#ifdef APSTUDIO_INVOKED
|
||
+#ifndef APSTUDIO_READONLY_SYMBOLS
|
||
+#define _APS_NEXT_RESOURCE_VALUE 104
|
||
+#define _APS_NEXT_COMMAND_VALUE 40001
|
||
+#define _APS_NEXT_CONTROL_VALUE 1001
|
||
+#define _APS_NEXT_SYMED_VALUE 101
|
||
+#endif
|
||
+#endif
|
||
--
|
||
2.20.1.windows.1
|
||
|