Compare commits

...

10 Commits

Author SHA1 Message Date
Mike Griese
ad2f6a7649 I cannot believe setting an AUMID like this works this well 2023-08-16 16:08:37 -05:00
Mike Griese
32bd9d34ce this api is useless 2023-08-16 15:38:10 -05:00
Mike Griese
aefa5d64f1 derp 2023-08-16 15:37:49 -05:00
Mike Griese
ba9c3eb94b Okay this at least sometimes works 2023-08-16 15:37:23 -05:00
Mike Griese
ba35bff71a yep works with exes and such 2023-08-16 13:54:10 -05:00
Mike Griese
26c394c6c9 Successfully load ms-appx images 2023-08-16 13:38:41 -05:00
Mike Griese
f9bb867231 chef_kiss.png. Loads pngs, gifs, anything local. This is great 2023-08-16 12:44:18 -05:00
Mike Griese
bf20c753c8 try another method of manually cracking open bitmaps 2023-08-16 12:39:36 -05:00
Mike Griese
8ef2307b67 POC: Load an image to an HICON, and set that icon as the taskbar overlay. Now the much harder task of doing that with an arbitrary icon 2023-08-16 11:12:30 -05:00
Mike Griese
697e1189e0 fix build break in scratch 2023-08-16 06:58:03 -05:00
11 changed files with 397 additions and 10 deletions

View File

@@ -44,6 +44,10 @@
</ItemGroup>
<Import Project="$(OpenConsoleDir)src\wap-common.build.post.props" />
<!-- Import resources from the normal Terminal project, so there are images in our ms-appx -->
<Import Project="$(OpenConsoleDir)src\cascadia\CascadiaResources.build.items" />
<ItemGroup>
<ProjectReference Include="..\WindowExe\WindowExe.vcxproj" />
</ItemGroup>

View File

@@ -84,7 +84,7 @@ namespace winrt::SampleApp::implementation
if (content == nullptr)
{
auto logic = Logic();
logic.Create();
logic.Create(0u);
auto page = logic.GetRoot().as<MyPage>();

View File

@@ -7,6 +7,16 @@
#include "MyPage.g.cpp"
#include "MySettings.h"
#include <Shlwapi.h> // For PathCombine function
#include <wincodec.h> // Windows Imaging Component
#include <Shlobj.h>
#include <Shlobj_core.h>
#include <wincodec.h>
#include <propkey.h> // For PKEY_AppUserModel_ID
#include <propsys.h> // For IPropertyStore and related functions
#include "..\..\..\src\types\inc\utils.hpp"
using namespace std::chrono_literals;
using namespace winrt::Microsoft::Terminal;
@@ -17,15 +27,30 @@ namespace winrt
using IInspectable = Windows::Foundation::IInspectable;
}
struct __declspec(uuid("5b0d3235-4dba-4d44-865e-8f1d0e4fd04d")) __declspec(novtable) IMemoryBufferByteAccess : ::IUnknown
{
virtual HRESULT __stdcall GetBuffer(uint8_t** value, uint32_t* capacity) = 0;
};
namespace winrt::SampleApp::implementation
{
MyPage::MyPage()
{
// Well this immediately fucks up loading an image using
// `GetFileFromPathAsync`. I'd bet it fucks up all sorts of things.
// // instantiate a random guid
// winrt::guid g{ ::Microsoft::Console::Utils::CreateGuid() };
// auto str{ ::Microsoft::Console::Utils::GuidToString(g) };
// SetCurrentProcessExplicitAppUserModelID(str.c_str());
InitializeComponent();
}
void MyPage::Create()
void MyPage::Create(uint64_t hwnd)
{
_hwnd = reinterpret_cast<HWND>(hwnd);
auto settings = winrt::make_self<implementation::MySettings>();
auto connectionSettings{ TerminalConnection::ConptyConnection::CreateSettings(L"cmd.exe /k echo This TermControl is hosted in-proc...",
@@ -34,6 +59,7 @@ namespace winrt::SampleApp::implementation
nullptr,
32,
80,
winrt::guid(),
winrt::guid()) };
// "Microsoft.Terminal.TerminalConnection.ConptyConnection"
@@ -44,6 +70,8 @@ namespace winrt::SampleApp::implementation
Control::TermControl control{ *settings, *settings, conn };
InProcContent().Children().Append(control);
PathInput().Text(L"d:\\dev\\private\\OpenConsole\\res\\terminal.ico");
}
// Method Description:
@@ -58,4 +86,330 @@ namespace winrt::SampleApp::implementation
return { L"Sample Application" };
}
void MyPage::_attemptOne(const winrt::hstring& text)
{
IWICImagingFactory* pFactory = nullptr;
IWICBitmapDecoder* pDecoder = nullptr;
IWICBitmapFrameDecode* pFrame = nullptr;
IWICFormatConverter* pConverter = nullptr;
HICON hIcon = nullptr;
// Create WIC Imaging Factory
CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pFactory));
// Load the image from the URI
HRESULT hr = pFactory->CreateDecoderFromFilename(text.c_str(), nullptr, GENERIC_READ, WICDecodeMetadataCacheOnLoad, &pDecoder);
if (SUCCEEDED(hr))
{
hr = pDecoder->GetFrame(0, &pFrame);
if (SUCCEEDED(hr))
{
// Convert the image format to a compatible format for icons (e.g., 32bppBGRA)
hr = pFactory->CreateFormatConverter(&pConverter);
if (SUCCEEDED(hr))
{
hr = pConverter->Initialize(pFrame, GUID_WICPixelFormat32bppBGRA, WICBitmapDitherTypeNone, nullptr, 0.0f, WICBitmapPaletteTypeCustom);
if (SUCCEEDED(hr))
{
// Get the image dimensions
UINT width, height;
pFrame->GetSize(&width, &height);
// Create a DIB section to hold the image data
BITMAPINFO bmi = {};
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biHeight = -static_cast<LONG>(height); // Negative height indicates top-down bitmap
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32; // 32bpp for ARGB format
bmi.bmiHeader.biCompression = BI_RGB;
void* pBits = nullptr;
HBITMAP hBitmap = CreateDIBSection(nullptr, &bmi, DIB_RGB_COLORS, &pBits, nullptr, 0);
if (hBitmap && pBits)
{
// Copy the converted image data into the DIB section
hr = pConverter->CopyPixels(nullptr, width * 4, width * height * 4, static_cast<BYTE*>(pBits));
// Create an icon from the DIB section
ICONINFO iconInfo = {};
iconInfo.fIcon = TRUE;
// iconInfo.hbmMask = nullptr; // No mask is required for icons
iconInfo.hbmMask = CreateBitmap(width, height, 1, 1, 0); // ^ that was a fuckin lie
iconInfo.hbmColor = hBitmap;
hIcon = CreateIconIndirect(&iconInfo);
// get last error if it failed
if (!hIcon)
{
auto gle = GetLastError();
hr = HRESULT_FROM_WIN32(gle);
}
ITaskbarList3* pTaskbarList = nullptr;
HRESULT hr = CoCreateInstance(CLSID_TaskbarList, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pTaskbarList));
if (SUCCEEDED(hr))
{
// Set the overlay icon
hr = pTaskbarList->SetOverlayIcon(_hwnd, hIcon, L"Overlay Icon Description");
DestroyIcon(hIcon); // Release the icon
pTaskbarList->Release();
}
DeleteObject(hBitmap); // The HICON owns the bitmap now
}
}
}
}
}
// Release COM interfaces
if (pConverter)
pConverter->Release();
if (pFrame)
pFrame->Release();
if (pDecoder)
pDecoder->Release();
if (pFactory)
pFactory->Release();
}
HICON ConvertSoftwareBitmapToHICON(winrt::Windows::Graphics::Imaging::SoftwareBitmap softwareBitmap)
{
// Get the dimensions of the SoftwareBitmap
int width = softwareBitmap.PixelWidth();
int height = softwareBitmap.PixelHeight();
// Get the pixel data from the SoftwareBitmap
winrt::Windows::Graphics::Imaging::BitmapBuffer bitmapBuffer = softwareBitmap.LockBuffer(
winrt::Windows::Graphics::Imaging::BitmapBufferAccessMode::Read);
winrt::Windows::Foundation::IMemoryBufferReference reference = bitmapBuffer.CreateReference();
// winrt::Windows::Foundation::IMemoryBufferByteAccess byteAccess;
// IMemoryBufferByteAccess byteAccess;
auto byteAccess = reference.as<IMemoryBufferByteAccess>();
byte* pixelData = nullptr;
uint32_t capacity = 0;
byteAccess->GetBuffer(&pixelData, &capacity);
// Create an HBITMAP using CreateDIBSection
BITMAPINFO bmi = {};
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biHeight = -height; // Negative height indicates top-down bitmap
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32; // Assuming 32bpp RGBA format
bmi.bmiHeader.biCompression = BI_RGB;
void* pBits = nullptr;
HBITMAP hBitmap = CreateDIBSection(nullptr, &bmi, DIB_RGB_COLORS, &pBits, nullptr, 0);
if (hBitmap && pBits)
{
// Copy pixel data to the HBITMAP
memcpy(pBits, pixelData, width * height * 4); // Assuming 32bpp RGBA format
}
ICONINFO iconInfo = {};
iconInfo.fIcon = TRUE;
// iconInfo.hbmMask = nullptr; // No mask is required for icons
iconInfo.hbmMask = CreateBitmap(width, height, 1, 1, 0); // ^ that was a fuckin lie
// iconInfo.hbmMask = CreateBitmap(64, 64, 1, 1, 0); // ^ that was a fuckin lie
iconInfo.hbmColor = hBitmap;
HICON hIcon = CreateIconIndirect(&iconInfo);
// get last error if it failed
if (!hIcon)
{
auto gle = GetLastError();
auto hr = HRESULT_FROM_WIN32(gle);
LOG_IF_FAILED(hr);
}
DeleteObject(hBitmap);
return hIcon;
}
void MyPage::_setTaskbarBadge(HICON hIcon)
{
ITaskbarList3* pTaskbarList = nullptr;
HRESULT hr = CoCreateInstance(CLSID_TaskbarList, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pTaskbarList));
if (SUCCEEDED(hr))
{
// Set the overlay icon
hr = pTaskbarList->SetOverlayIcon(_hwnd, hIcon, L"Overlay Icon Description");
DestroyIcon(hIcon); // Release the icon
pTaskbarList->Release();
}
}
void MyPage::_setTaskbarIcon(HICON hIcon)
{
SendMessageW(_hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
SendMessageW(_hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
}
// Method Description:
// - Attempt to get the icon index from the icon path provided
// Arguments:
// - iconPath: the full icon path, including the index if present
// - iconPathWithoutIndex: the place to store the icon path, sans the index if present
// Return Value:
// - nullopt if the iconPath is not an exe/dll/lnk file in the first place
// - 0 if the iconPath is an exe/dll/lnk file but does not contain an index (i.e. we default
// to the first icon in the file)
// - the icon index if the iconPath is an exe/dll/lnk file and contains an index
std::optional<int> _getIconIndex(const winrt::hstring& iconPath, std::wstring_view& iconPathWithoutIndex)
{
const auto pathView = std::wstring_view{ iconPath };
// Does iconPath have a comma in it? If so, split the string on the
// comma and look for the index and extension.
const auto commaIndex = pathView.find(L',');
// split the path on the comma
iconPathWithoutIndex = pathView.substr(0, commaIndex);
// It's an exe, dll, or lnk, so we need to extract the icon from the file.
if (!til::ends_with(iconPathWithoutIndex, L".exe") &&
!til::ends_with(iconPathWithoutIndex, L".dll") &&
!til::ends_with(iconPathWithoutIndex, L".lnk"))
{
return std::nullopt;
}
if (commaIndex != std::wstring::npos)
{
// Convert the string iconIndex to a signed int to support negative numbers which represent an Icon's ID.
const auto index{ til::to_int(pathView.substr(commaIndex + 1)) };
if (index == til::to_int_error)
{
return std::nullopt;
}
return static_cast<int>(index);
}
// We had a binary path, but no index. Default to 0.
return 0;
}
winrt::fire_and_forget MyPage::_attemptTwo(winrt::hstring path)
{
// First things first:
// Is the path a path to an exe, a dll, or a resource in one of those files?
// If so, then we can use the icon from that file, without so much rigamarole.
std::wstring_view iconPathWithoutIndex;
const auto indexOpt = _getIconIndex(path, iconPathWithoutIndex);
if (indexOpt.has_value())
{
// Here, we know we have a path to an exe, dll, or resource in one of those files.
auto iconSize = 32;
HICON hIcon = nullptr;
winrt::hstring iconPath{ iconPathWithoutIndex };
LOG_IF_FAILED(SHDefExtractIcon(iconPath.c_str(),
indexOpt.value(),
0,
&hIcon,
nullptr,
iconSize));
if (hIcon)
{
_setTaskbarBadge(hIcon);
}
co_return;
}
// If not, then we'll have to do the rigamarole.
try
{
// Create a URI from the path
const Windows::Foundation::Uri uri{ path };
winrt::Windows::Storage::IStorageFile file{ nullptr };
// Is the URI a ms-appx URI? then load it from the app package
if (uri.SchemeName() == L"ms-appx")
{
file = co_await winrt::Windows::Storage::StorageFile::GetFileFromApplicationUriAsync(uri);
}
// // Is it a web URI? then download it else if (uri.SchemeName() ==
// L"http" || uri.SchemeName() == L"https")
// {
// auto downloader = winrt::Windows::Networking::BackgroundTransfer::BackgroundDownloader();
// auto download = downloader.CreateDownload(uri, nullptr);
// auto downloadOperation = download.StartAsync();
// co_await downloadOperation;
// file = download.ResultFile();
// }
//
// Actually, don't do anything for web URIs. BackgroundDownloader is not
// supported outside of packaged apps, but I think that also extends to
// centennial apps. Useless.
//
else
{
// Open the file, and load it into a SoftwareBitmap
file = co_await winrt::Windows::Storage::StorageFile::GetFileFromPathAsync(path);
}
// Get the software bitmap out of the file
auto stream = co_await file.OpenAsync(winrt::Windows::Storage::FileAccessMode::Read);
auto decoder = co_await winrt::Windows::Graphics::Imaging::BitmapDecoder::CreateAsync(stream);
auto softwareBitmap = co_await decoder.GetSoftwareBitmapAsync();
// Convert the SoftwareBitmap to an HBITMAP, using Windows Imaging Component
// auto hBitmap = ConvertSoftwareBitmapToHBITMAP(softwareBitmap);
// auto hIcon = _convertBitmapToHICON(hBitmap);
auto hIcon = ConvertSoftwareBitmapToHICON(softwareBitmap);
_setTaskbarIcon(hIcon);
_setTaskbarBadge(hIcon);
}
CATCH_LOG();
}
winrt::fire_and_forget MyPage::OnLoadIconClick(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e)
{
const auto text = PathInput().Text();
const auto selectedComboItem = GroupSelector().SelectedItem().try_as<winrt::Windows::UI::Xaml::Controls::ComboBoxItem>();
const auto groupText = winrt::unbox_value<winrt::hstring>(selectedComboItem.Content());
// auto hr = SetCurrentProcessExplicitAppUserModelID(groupText.c_str());
// LOG_IF_FAILED(hr);
IPropertyStore* pPropertyStore = nullptr;
HRESULT hr = SHGetPropertyStoreForWindow(_hwnd, IID_PPV_ARGS(&pPropertyStore));
if (SUCCEEDED(hr))
{
// Set the System.AppUserModel.ID property (replace "YourAppID" with your desired ID)
PROPVARIANT propVar;
PropVariantInit(&propVar);
propVar.vt = VT_LPWSTR;
std::wstring s = groupText.c_str();
auto cstr = s.data();
propVar.pwszVal = cstr;
hr = pPropertyStore->SetValue(PKEY_AppUserModel_ID, propVar);
PropVariantClear(&propVar);
pPropertyStore->Release();
}
LOG_IF_FAILED(hr);
co_await winrt::resume_background();
_attemptTwo(text);
co_return;
}
}

View File

@@ -13,12 +13,20 @@ namespace winrt::SampleApp::implementation
public:
MyPage();
void Create();
void Create(uint64_t hwnd);
hstring Title();
winrt::fire_and_forget OnLoadIconClick(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e);
private:
friend struct MyPageT<MyPage>; // for Xaml to bind events
HWND _hwnd{ nullptr };
void _attemptOne(const winrt::hstring& text);
winrt::fire_and_forget _attemptTwo(winrt::hstring text);
void _setTaskbarBadge(HICON hIcon);
void _setTaskbarIcon(HICON hIcon);
};
}

View File

@@ -27,6 +27,13 @@
Create
</Button>
<ComboBox x:Name="GroupSelector">
<ComboBoxItem Content="M.T.S.One" />
<ComboBoxItem Content="M.T.S.Two" />
<ComboBoxItem Content="M.T.S.Three" />
</ComboBox>
</StackPanel>
<Grid x:Name="TabContent"
@@ -51,7 +58,15 @@
Padding="16"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="#0000ff" />
Background="#0000ff">
<StackPanel Orientation="Vertical">
<TextBox x:Name="PathInput" />
<Button x:Name="LoadIconButton"
Click="OnLoadIconClick">
Load Icon
</Button>
</StackPanel>
</Grid>

View File

@@ -66,9 +66,9 @@ namespace winrt::SampleApp::implementation
// - <none>
// Return Value:
// - <none>
void SampleAppLogic::Create()
void SampleAppLogic::Create(uint64_t hwnd)
{
_root->Create();
_root->Create(hwnd);
}
UIElement SampleAppLogic::GetRoot() noexcept

View File

@@ -17,7 +17,7 @@ namespace winrt::SampleApp::implementation
SampleAppLogic();
~SampleAppLogic() = default;
void Create();
void Create(uint64_t hwnd);
Windows::UI::Xaml::UIElement GetRoot() noexcept;

View File

@@ -9,7 +9,7 @@ namespace SampleApp
{
SampleAppLogic();
void Create();
void Create(UInt64 hwnd);
Windows.UI.Xaml.UIElement GetRoot();

View File

@@ -103,7 +103,7 @@
<AdditionalIncludeDirectories>$(OpenConsoleDir)\dep\jsoncpp\json;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>$(OpenConsoleCommonOutDir)\ConTypes.lib;WindowsApp.lib;shell32.lib;user32.lib;Gdi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<!-- SampleAppLib contains a DllMain that we need to force the use of. -->
<AdditionalOptions Condition="'$(Platform)'=='Win32'">/INCLUDE:_DllMain@12 %(AdditionalOptions)</AdditionalOptions>
<AdditionalOptions Condition="'$(Platform)'!='Win32'">/INCLUDE:DllMain %(AdditionalOptions)</AdditionalOptions>

View File

@@ -46,6 +46,12 @@
#include "winrt/Windows.UI.Xaml.Markup.h"
#include "winrt/Windows.UI.ViewManagement.h"
#include "winrt/Windows.Storage.Streams.h"
#include <winrt/Windows.Graphics.Imaging.h>
#include <winrt/Windows.UI.Xaml.Media.Imaging.h>
// #include <winrt/Windows.Networking.h>
#include <winrt/Windows.Networking.BackgroundTransfer.h>
#include <winrt/Microsoft.UI.Xaml.Controls.h>
#include <winrt/Microsoft.UI.Xaml.Controls.Primitives.h>
#include <winrt/Microsoft.UI.Xaml.XamlTypeInfo.h>

View File

@@ -49,7 +49,7 @@ void SampleAppHost::Initialize()
{
_window->Initialize();
_logic.Create();
_logic.Create((uint64_t)_window->GetHandle());
_window->UpdateTitle(_logic.Title());