Compare commits

..

12 Commits

Author SHA1 Message Date
Leilei Zhang
528f9665cd update tool 2025-11-11 12:57:36 +08:00
Leilei Zhang
552e305556 Merge branch 'main' of https://github.com/microsoft/PowerToys into leilzh/adclean 2025-11-11 10:14:02 +08:00
Kai Tao
24d7ae54ce Find my mouse: Fix the issue find my mouse xaml island could not be run in x64 environment (#43420)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
This pull request introduces a configuration update to the
`src/runner/runner.vcxproj` project file. The change enables undocked,
registration-free WinRT initialization for the Windows App SDK, which
can improve compatibility and reduce dependency issues in certain
deployment scenarios.

Windows App SDK configuration:

* Enabled `WindowsAppSdkUndockedRegFreeWinRTInitialize` to allow
registration-free WinRT initialization in the project file
`runner.vcxproj`.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] Closes: #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
Validate in x64 environment, and it can be run without problem, no
errors any more
2025-11-11 09:34:50 +08:00
Mario Hewardt
a271a2f8af Enable GIF support for ZoomIt (#43266)
Closes #43265
2025-11-10 13:57:13 -06:00
Michael Jolley
a33c484c93 CmdPal: Removing app tag from apps (#43439)
The "App" tag is pretty redundant and is adding a lot of visual noise.
Closes #38968

New:

![image](https://github.com/user-attachments/assets/5eff195c-5aaf-477a-b988-4b3b14583569)
2025-11-10 20:03:36 +01:00
Niels Laute
f20c3b832b Asset updates (#43431)
title
2025-11-10 19:04:16 +01:00
Mike Griese
9c2884ab41 CmdPal: Bump version to 0.7 (#43428)
title
2025-11-10 10:38:55 -06:00
Jiří Polášek
076828f592 CmdPal: Remove duplicate HideWindow call in MainWindow .ctor (#43360)
## Summary of the Pull Request

Refer to the title. Calling this here will just log an error. Another
HideWindow is at the end of the constructor, so the behavior remains
unchanged.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] Closes: #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-11-10 10:38:22 -06:00
Leilei Zhang
19d0f7827c format 2025-11-10 22:27:17 +08:00
Leilei Zhang
b1474c4353 clean unused provider 2025-11-10 22:24:40 +08:00
Niels Laute
301d504db1 • instead of . in README (#43353)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] Closes: #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-11-10 06:18:27 -06:00
moooyo
e4a8488a2a [PowerRename] Fix FuzzTest project configuration issue (#43299)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
This PR add the missing configuration for FuzzTest to avoid build issue
in local env.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] Closes: #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

Co-authored-by: Yu Leng <yuleng@microsoft.com>
2025-11-10 11:40:39 +08:00
51 changed files with 1430 additions and 630 deletions

View File

@@ -47,7 +47,6 @@ Allmodule
ALLOWUNDO
ALLVIEW
ALPHATYPE
amazonbedrock
AModifier
amr
ANDSCANS
@@ -66,6 +65,7 @@ APIIs
Apm
APPBARDATA
APPEXECLINK
appext
APPLICATIONFRAMEHOST
appmanifest
APPMODEL
@@ -187,6 +187,7 @@ CAPTUREBLT
CAPTURECHANGED
CARETBLINKING
CAtl
CBN
cch
CCHDEVICENAME
CCHFORMNAME
@@ -414,6 +415,9 @@ DNLEN
DONOTROUND
DONTVALIDATEPATH
dotnet
downsampled
downsampling
Downsampled
downscale
DPICHANGED
DPIs
@@ -598,6 +602,7 @@ getfilesiginforedist
geolocator
GETHOTKEY
GETICON
GETLBTEXT
GETMINMAXINFO
GETNONCLIENTMETRICS
GETPROPERTYSTOREFLAGS
@@ -605,6 +610,7 @@ GETSCREENSAVERRUNNING
GETSECKEY
GETSTICKYKEYS
GETTEXTLENGTH
GIFs
gitmodules
GHND
GMEM
@@ -615,6 +621,7 @@ GPOCA
gpp
gpu
gradians
grctlext
Gridcustomlayout
GSM
gtm
@@ -688,7 +695,6 @@ hmonitor
homies
homljgmgpmcbpjbnjpfijnhipfkiclkd
HOOKPROC
huggingface
HORZRES
HORZSIZE
Hostbackdropbrush

View File

@@ -51,10 +51,8 @@
<PackageVersion Include="Microsoft.AI.Foundry.Local" Version="0.3.0" />
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.66.0" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.OpenAI" Version="1.66.0" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Amazon" Version="1.66.0-alpha" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.AzureAIInference" Version="1.66.0-beta" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Google" Version="1.66.0-alpha" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.HuggingFace" Version="1.66.0-preview" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.MistralAI" Version="1.66.0-alpha" />
<PackageVersion Include="Microsoft.SemanticKernel.Connectors.Ollama" Version="1.66.0-alpha" />
<PackageVersion Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" />

View File

@@ -10,11 +10,11 @@
<h3 align="center">
<a href="#-installation">Installation</a>
<span> . </span>
<span> · </span>
<a href="https://aka.ms/powertoys-docs">Documentation</a>
<span> . </span>
<span> · </span>
<a href="https://aka.ms/powertoys-releaseblog">Blog</a>
<span> . </span>
<span> · </span>
<a href="#-whats-new">Release notes</a>
</h3>
<br/><br/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -216,10 +216,6 @@ namespace winrt::PowerToys::GPOWrapper::implementation
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getAllowedAdvancedPasteGoogleValue());
}
GpoRuleConfigured GPOWrapper::GetAllowedAdvancedPasteAnthropicValue()
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getAllowedAdvancedPasteAnthropicValue());
}
GpoRuleConfigured GPOWrapper::GetAllowedAdvancedPasteOllamaValue()
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getAllowedAdvancedPasteOllamaValue());

View File

@@ -60,7 +60,6 @@ namespace winrt::PowerToys::GPOWrapper::implementation
static GpoRuleConfigured GetAllowedAdvancedPasteAzureAIInferenceValue();
static GpoRuleConfigured GetAllowedAdvancedPasteMistralValue();
static GpoRuleConfigured GetAllowedAdvancedPasteGoogleValue();
static GpoRuleConfigured GetAllowedAdvancedPasteAnthropicValue();
static GpoRuleConfigured GetAllowedAdvancedPasteOllamaValue();
static GpoRuleConfigured GetAllowedAdvancedPasteFoundryLocalValue();
static GpoRuleConfigured GetConfiguredNewPlusEnabledValue();

View File

@@ -64,7 +64,6 @@ namespace PowerToys
static GpoRuleConfigured GetAllowedAdvancedPasteAzureAIInferenceValue();
static GpoRuleConfigured GetAllowedAdvancedPasteMistralValue();
static GpoRuleConfigured GetAllowedAdvancedPasteGoogleValue();
static GpoRuleConfigured GetAllowedAdvancedPasteAnthropicValue();
static GpoRuleConfigured GetAllowedAdvancedPasteOllamaValue();
static GpoRuleConfigured GetAllowedAdvancedPasteFoundryLocalValue();
static GpoRuleConfigured GetConfiguredNewPlusEnabledValue();

View File

@@ -89,7 +89,6 @@ namespace powertoys_gpo
const std::wstring POLICY_ALLOW_ADVANCED_PASTE_AZURE_AI_INFERENCE = L"AllowAdvancedPasteAzureAIInference";
const std::wstring POLICY_ALLOW_ADVANCED_PASTE_MISTRAL = L"AllowAdvancedPasteMistral";
const std::wstring POLICY_ALLOW_ADVANCED_PASTE_GOOGLE = L"AllowAdvancedPasteGoogle";
const std::wstring POLICY_ALLOW_ADVANCED_PASTE_ANTHROPIC = L"AllowAdvancedPasteAnthropic";
const std::wstring POLICY_ALLOW_ADVANCED_PASTE_OLLAMA = L"AllowAdvancedPasteOllama";
const std::wstring POLICY_ALLOW_ADVANCED_PASTE_FOUNDRY_LOCAL = L"AllowAdvancedPasteFoundryLocal";
const std::wstring POLICY_MWB_CLIPBOARD_SHARING_ENABLED = L"MwbClipboardSharingEnabled";
@@ -615,11 +614,6 @@ namespace powertoys_gpo
return getConfiguredValue(POLICY_ALLOW_ADVANCED_PASTE_GOOGLE);
}
inline gpo_rule_configured_t getAllowedAdvancedPasteAnthropicValue()
{
return getConfiguredValue(POLICY_ALLOW_ADVANCED_PASTE_ANTHROPIC);
}
inline gpo_rule_configured_t getAllowedAdvancedPasteOllamaValue()
{
return getConfiguredValue(POLICY_ALLOW_ADVANCED_PASTE_OLLAMA);

View File

@@ -59,10 +59,8 @@
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" />
<!-- Including MessagePack to force version, since it's used by StreamJsonRpc but contains vulnerabilities. After StreamJsonRpc updates the version of MessagePack, we can upgrade StreamJsonRpc instead. -->
<PackageReference Include="MessagePack" />
<PackageReference Include="Microsoft.SemanticKernel.Connectors.Amazon" />
<PackageReference Include="Microsoft.SemanticKernel.Connectors.AzureAIInference" />
<PackageReference Include="Microsoft.SemanticKernel.Connectors.Google" />
<PackageReference Include="Microsoft.SemanticKernel.Connectors.HuggingFace" />
<PackageReference Include="Microsoft.SemanticKernel.Connectors.MistralAI" />
<PackageReference Include="Microsoft.SemanticKernel.Connectors.Ollama" />
<PackageReference Include="Microsoft.Extensions.Hosting" />

View File

@@ -11,12 +11,6 @@ using AdvancedPaste.Settings;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.Amazon;
using Microsoft.SemanticKernel.Connectors.AzureAIInference;
using Microsoft.SemanticKernel.Connectors.Google;
using Microsoft.SemanticKernel.Connectors.HuggingFace;
using Microsoft.SemanticKernel.Connectors.MistralAI;
using Microsoft.SemanticKernel.Connectors.Ollama;
using Microsoft.SemanticKernel.Connectors.OpenAI;
namespace AdvancedPaste.Services;

View File

@@ -181,8 +181,6 @@ namespace AdvancedPaste.Services.CustomActions
{
AIServiceType.Onnx => false,
AIServiceType.Ollama => false,
AIServiceType.Anthropic => false,
AIServiceType.AmazonBedrock => false,
_ => true,
};
}

View File

@@ -11,10 +11,8 @@ using AdvancedPaste.Models;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.Amazon;
using Microsoft.SemanticKernel.Connectors.AzureAIInference;
using Microsoft.SemanticKernel.Connectors.Google;
using Microsoft.SemanticKernel.Connectors.HuggingFace;
using Microsoft.SemanticKernel.Connectors.MistralAI;
using Microsoft.SemanticKernel.Connectors.Ollama;
using Microsoft.SemanticKernel.Connectors.OpenAI;
@@ -29,11 +27,8 @@ namespace AdvancedPaste.Services.CustomActions
AIServiceType.AzureOpenAI,
AIServiceType.Mistral,
AIServiceType.Google,
AIServiceType.HuggingFace,
AIServiceType.AzureAIInference,
AIServiceType.Ollama,
AIServiceType.Anthropic,
AIServiceType.AmazonBedrock,
};
public static PasteAIProviderRegistration Registration { get; } = new(SupportedTypes, config => new SemanticKernelPasteProvider(config));
@@ -142,21 +137,12 @@ namespace AdvancedPaste.Services.CustomActions
case AIServiceType.Google:
kernelBuilder.AddGoogleAIGeminiChatCompletion(_config.Model, apiKey: apiKey);
break;
case AIServiceType.HuggingFace:
kernelBuilder.AddHuggingFaceChatCompletion(_config.Model, apiKey: apiKey);
break;
case AIServiceType.AzureAIInference:
kernelBuilder.AddAzureAIInferenceChatCompletion(_config.Model, apiKey: apiKey, endpoint: new Uri(endpoint));
break;
case AIServiceType.Ollama:
kernelBuilder.AddOllamaChatCompletion(_config.Model, endpoint: new Uri(endpoint));
break;
case AIServiceType.Anthropic:
kernelBuilder.AddBedrockChatCompletionService(_config.Model);
break;
case AIServiceType.AmazonBedrock:
kernelBuilder.AddBedrockChatCompletionService(_config.Model);
break;
default:
throw new NotSupportedException($"Provider '{_config.ProviderType}' is not supported by {nameof(SemanticKernelPasteProvider)}");
@@ -184,8 +170,6 @@ namespace AdvancedPaste.Services.CustomActions
return serviceType switch
{
AIServiceType.Ollama => false,
AIServiceType.Anthropic => false,
AIServiceType.AmazonBedrock => false,
_ => true,
};
}

View File

@@ -156,16 +156,10 @@ public sealed class EnhancedVaultCredentialsProvider : IAICredentialsProvider
resource = "https://ai.google.dev/";
serviceKey = "google";
break;
case AIServiceType.HuggingFace:
resource = "https://huggingface.co/settings/tokens";
serviceKey = "huggingface";
break;
case AIServiceType.FoundryLocal:
case AIServiceType.ML:
case AIServiceType.Onnx:
case AIServiceType.Ollama:
case AIServiceType.Anthropic:
case AIServiceType.AmazonBedrock:
return null;
default:
return null;

View File

@@ -15,7 +15,7 @@ namespace AdvancedPaste.Telemetry;
public class AdvancedPasteEndpointUsageEvent : EventBase, IEvent
{
/// <summary>
/// Gets or sets the AI provider type (e.g., OpenAI, AzureOpenAI, Anthropic).
/// Gets or sets the AI provider type (e.g., OpenAI, AzureOpenAI, Google).
/// </summary>
public string ProviderType { get; set; }

View File

@@ -798,7 +798,6 @@ namespace AdvancedPaste.ViewModels
AIServiceType.AzureAIInference => PowerToys.GPOWrapper.GPOWrapper.GetAllowedAdvancedPasteAzureAIInferenceValue() != PowerToys.GPOWrapper.GpoRuleConfigured.Disabled,
AIServiceType.Mistral => PowerToys.GPOWrapper.GPOWrapper.GetAllowedAdvancedPasteMistralValue() != PowerToys.GPOWrapper.GpoRuleConfigured.Disabled,
AIServiceType.Google => PowerToys.GPOWrapper.GPOWrapper.GetAllowedAdvancedPasteGoogleValue() != PowerToys.GPOWrapper.GpoRuleConfigured.Disabled,
AIServiceType.Anthropic => PowerToys.GPOWrapper.GPOWrapper.GetAllowedAdvancedPasteAnthropicValue() != PowerToys.GPOWrapper.GpoRuleConfigured.Disabled,
AIServiceType.Ollama => PowerToys.GPOWrapper.GPOWrapper.GetAllowedAdvancedPasteOllamaValue() != PowerToys.GPOWrapper.GpoRuleConfigured.Disabled,
AIServiceType.FoundryLocal => PowerToys.GPOWrapper.GPOWrapper.GetAllowedAdvancedPasteFoundryLocalValue() != PowerToys.GPOWrapper.GpoRuleConfigured.Disabled,
_ => true, // Allow unknown types by default

View File

@@ -0,0 +1,548 @@
//==============================================================================
//
// Zoomit
// Sysinternals - www.sysinternals.com
//
// GIF recording support using Windows Imaging Component (WIC)
//
//==============================================================================
#include "pch.h"
#include "GifRecordingSession.h"
#include "CaptureFrameWait.h"
#include <shcore.h>
extern DWORD g_RecordScaling;
namespace winrt
{
using namespace Windows::Foundation;
using namespace Windows::Graphics;
using namespace Windows::Graphics::Capture;
using namespace Windows::Graphics::DirectX;
using namespace Windows::Graphics::DirectX::Direct3D11;
using namespace Windows::Storage;
using namespace Windows::UI::Composition;
}
namespace util
{
using namespace robmikh::common::uwp;
}
const float CLEAR_COLOR[] = { 0.0f, 0.0f, 0.0f, 1.0f };
int32_t EnsureEvenGif(int32_t value)
{
if (value % 2 == 0)
{
return value;
}
else
{
return value + 1;
}
}
//----------------------------------------------------------------------------
//
// GifRecordingSession::GifRecordingSession
//
//----------------------------------------------------------------------------
GifRecordingSession::GifRecordingSession(
winrt::IDirect3DDevice const& device,
winrt::GraphicsCaptureItem const& item,
RECT const cropRect,
uint32_t frameRate,
winrt::Streams::IRandomAccessStream const& stream)
{
m_device = device;
m_d3dDevice = GetDXGIInterfaceFromObject<ID3D11Device>(m_device);
m_d3dDevice->GetImmediateContext(m_d3dContext.put());
m_item = item;
m_frameRate = frameRate;
m_stream = stream;
auto itemSize = item.Size();
auto inputWidth = EnsureEvenGif(itemSize.Width);
auto inputHeight = EnsureEvenGif(itemSize.Height);
m_frameWait = std::make_shared<CaptureFrameWait>(m_device, m_item, winrt::SizeInt32{ inputWidth, inputHeight });
auto weakPointer{ std::weak_ptr{ m_frameWait } };
m_itemClosed = item.Closed(winrt::auto_revoke, [weakPointer](auto&, auto&)
{
auto sharedPointer{ weakPointer.lock() };
if (sharedPointer)
{
sharedPointer->StopCapture();
}
});
// Get crop dimension
if ((cropRect.right - cropRect.left) != 0)
{
m_rcCrop = cropRect;
m_frameWait->ShowCaptureBorder(false);
}
else
{
m_rcCrop.left = 0;
m_rcCrop.top = 0;
m_rcCrop.right = inputWidth;
m_rcCrop.bottom = inputHeight;
}
// Apply scaling
constexpr int c_minimumSize = 34;
auto scaledWidth = MulDiv(m_rcCrop.right - m_rcCrop.left, g_RecordScaling, 100);
auto scaledHeight = MulDiv(m_rcCrop.bottom - m_rcCrop.top, g_RecordScaling, 100);
m_width = scaledWidth;
m_height = scaledHeight;
if (m_width < c_minimumSize)
{
m_width = c_minimumSize;
m_height = MulDiv(m_height, m_width, scaledWidth);
}
if (m_height < c_minimumSize)
{
m_height = c_minimumSize;
m_width = MulDiv(m_width, m_height, scaledHeight);
}
if (m_width > inputWidth)
{
m_width = inputWidth;
m_height = c_minimumSize, MulDiv(m_height, scaledWidth, m_width);
}
if (m_height > inputHeight)
{
m_height = inputHeight;
m_width = c_minimumSize, MulDiv(m_width, scaledHeight, m_height);
}
m_width = EnsureEvenGif(m_width);
m_height = EnsureEvenGif(m_height);
m_frameDelay = (frameRate > 0) ? (100 / frameRate) : 15;
// Initialize WIC
winrt::check_hresult(CoCreateInstance(
CLSID_WICImagingFactory,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(m_wicFactory.put())));
// Create WIC stream from IRandomAccessStream
winrt::check_hresult(m_wicFactory->CreateStream(m_wicStream.put()));
// Get the IStream from the IRandomAccessStream
winrt::com_ptr<IStream> streamInterop;
winrt::check_hresult(CreateStreamOverRandomAccessStream(
winrt::get_unknown(stream),
IID_PPV_ARGS(streamInterop.put())));
winrt::check_hresult(m_wicStream->InitializeFromIStream(streamInterop.get()));
// Create GIF encoder
winrt::check_hresult(m_wicFactory->CreateEncoder(
GUID_ContainerFormatGif,
nullptr,
m_gifEncoder.put()));
winrt::check_hresult(m_gifEncoder->Initialize(m_wicStream.get(), WICBitmapEncoderNoCache));
// Set global GIF metadata for looping (NETSCAPE2.0 application extension)
try
{
winrt::com_ptr<IWICMetadataQueryWriter> encoderMetadataWriter;
if (SUCCEEDED(m_gifEncoder->GetMetadataQueryWriter(encoderMetadataWriter.put())) && encoderMetadataWriter)
{
OutputDebugStringW(L"Setting NETSCAPE2.0 looping extension on encoder...\n");
// Set application extension
PROPVARIANT propValue;
PropVariantInit(&propValue);
propValue.vt = VT_UI1 | VT_VECTOR;
propValue.caub.cElems = 11;
propValue.caub.pElems = static_cast<UCHAR*>(CoTaskMemAlloc(11));
if (propValue.caub.pElems != nullptr)
{
memcpy(propValue.caub.pElems, "NETSCAPE2.0", 11);
HRESULT hr = encoderMetadataWriter->SetMetadataByName(L"/appext/application", &propValue);
if (SUCCEEDED(hr))
{
OutputDebugStringW(L"Encoder application extension set successfully\n");
}
else
{
OutputDebugStringW(L"Failed to set encoder application extension\n");
}
PropVariantClear(&propValue);
// Set loop count (0 = infinite)
PropVariantInit(&propValue);
propValue.vt = VT_UI1 | VT_VECTOR;
propValue.caub.cElems = 5;
propValue.caub.pElems = static_cast<UCHAR*>(CoTaskMemAlloc(5));
if (propValue.caub.pElems != nullptr)
{
propValue.caub.pElems[0] = 3;
propValue.caub.pElems[1] = 1;
propValue.caub.pElems[2] = 0;
propValue.caub.pElems[3] = 0;
propValue.caub.pElems[4] = 0;
hr = encoderMetadataWriter->SetMetadataByName(L"/appext/data", &propValue);
if (SUCCEEDED(hr))
{
OutputDebugStringW(L"Encoder loop count set successfully\n");
}
else
{
OutputDebugStringW(L"Failed to set encoder loop count\n");
}
PropVariantClear(&propValue);
}
}
}
else
{
OutputDebugStringW(L"Failed to get encoder metadata writer\n");
}
}
catch (...)
{
OutputDebugStringW(L"Warning: Failed to set GIF encoder looping metadata\n");
}
}
//----------------------------------------------------------------------------
//
// GifRecordingSession::~GifRecordingSession
//
//----------------------------------------------------------------------------
GifRecordingSession::~GifRecordingSession()
{
Close();
}
//----------------------------------------------------------------------------
//
// GifRecordingSession::Create
//
//----------------------------------------------------------------------------
std::shared_ptr<GifRecordingSession> GifRecordingSession::Create(
winrt::IDirect3DDevice const& device,
winrt::GraphicsCaptureItem const& item,
RECT const& crop,
uint32_t frameRate,
winrt::Streams::IRandomAccessStream const& stream)
{
return std::shared_ptr<GifRecordingSession>(new GifRecordingSession(device, item, crop, frameRate, stream));
}
//----------------------------------------------------------------------------
//
// GifRecordingSession::EncodeFrame
//
//----------------------------------------------------------------------------
HRESULT GifRecordingSession::EncodeFrame(ID3D11Texture2D* frameTexture)
{
try
{
// Create a staging texture for CPU access
D3D11_TEXTURE2D_DESC frameDesc;
frameTexture->GetDesc(&frameDesc);
// GIF encoding with palette generation is VERY slow at high resolutions (4K takes 1 second per frame!)
UINT targetWidth = frameDesc.Width;
UINT targetHeight = frameDesc.Height;
if (frameDesc.Width > static_cast<uint32_t>(m_width) || frameDesc.Height > static_cast<uint32_t>(m_height))
{
float scaleX = static_cast<float>(m_width) / frameDesc.Width;
float scaleY = static_cast<float>(m_height) / frameDesc.Height;
float scale = min(scaleX, scaleY);
targetWidth = static_cast<UINT>(frameDesc.Width * scale);
targetHeight = static_cast<UINT>(frameDesc.Height * scale);
// Ensure even dimensions for GIF
targetWidth = (targetWidth / 2) * 2;
targetHeight = (targetHeight / 2) * 2;
}
D3D11_TEXTURE2D_DESC stagingDesc = frameDesc;
stagingDesc.Usage = D3D11_USAGE_STAGING;
stagingDesc.BindFlags = 0;
stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
stagingDesc.MiscFlags = 0;
winrt::com_ptr<ID3D11Texture2D> stagingTexture;
winrt::check_hresult(m_d3dDevice->CreateTexture2D(&stagingDesc, nullptr, stagingTexture.put()));
// Copy the frame to staging texture
m_d3dContext->CopyResource(stagingTexture.get(), frameTexture);
// Map the staging texture
D3D11_MAPPED_SUBRESOURCE mappedResource;
winrt::check_hresult(m_d3dContext->Map(stagingTexture.get(), 0, D3D11_MAP_READ, 0, &mappedResource));
// Create a new frame in the GIF
winrt::com_ptr<IWICBitmapFrameEncode> frameEncode;
winrt::com_ptr<IPropertyBag2> propertyBag;
winrt::check_hresult(m_gifEncoder->CreateNewFrame(frameEncode.put(), propertyBag.put()));
// Initialize the frame encoder with property bag
winrt::check_hresult(frameEncode->Initialize(propertyBag.get()));
// CRITICAL: For GIF, we MUST set size and pixel format BEFORE WriteSource
// Use target dimensions (may be downsampled)
winrt::check_hresult(frameEncode->SetSize(targetWidth, targetHeight));
// Set the pixel format to 8-bit indexed (required for GIF)
WICPixelFormatGUID pixelFormat = GUID_WICPixelFormat8bppIndexed;
winrt::check_hresult(frameEncode->SetPixelFormat(&pixelFormat));
// Create a WIC bitmap from the BGRA texture data
winrt::com_ptr<IWICBitmap> sourceBitmap;
winrt::check_hresult(m_wicFactory->CreateBitmapFromMemory(
frameDesc.Width,
frameDesc.Height,
GUID_WICPixelFormat32bppBGRA,
mappedResource.RowPitch,
frameDesc.Height * mappedResource.RowPitch,
static_cast<BYTE*>(mappedResource.pData),
sourceBitmap.put()));
// If we need downsampling, use WIC scaler
winrt::com_ptr<IWICBitmapSource> finalSource = sourceBitmap;
if (targetWidth != frameDesc.Width || targetHeight != frameDesc.Height)
{
winrt::com_ptr<IWICBitmapScaler> scaler;
winrt::check_hresult(m_wicFactory->CreateBitmapScaler(scaler.put()));
winrt::check_hresult(scaler->Initialize(
sourceBitmap.get(),
targetWidth,
targetHeight,
WICBitmapInterpolationModeHighQualityCubic));
finalSource = scaler;
OutputDebugStringW((L"Downsampled from " + std::to_wstring(frameDesc.Width) + L"x" + std::to_wstring(frameDesc.Height) +
L" to " + std::to_wstring(targetWidth) + L"x" + std::to_wstring(targetHeight) + L"\n").c_str());
}
// Use WriteSource - WIC will handle the BGRA to 8bpp indexed conversion
winrt::check_hresult(frameEncode->WriteSource(finalSource.get(), nullptr));
try
{
winrt::com_ptr<IWICMetadataQueryWriter> frameMetadataWriter;
if (SUCCEEDED(frameEncode->GetMetadataQueryWriter(frameMetadataWriter.put())) && frameMetadataWriter)
{
// Set the frame delay in the metadata (in hundredths of a second)
PROPVARIANT propValue;
PropVariantInit(&propValue);
propValue.vt = VT_UI2;
propValue.uiVal = static_cast<USHORT>(m_frameDelay);
frameMetadataWriter->SetMetadataByName(L"/grctlext/Delay", &propValue);
PropVariantClear(&propValue);
// Set disposal method (2 = restore to background, needed for animation)
PropVariantInit(&propValue);
propValue.vt = VT_UI1;
propValue.bVal = 2; // Disposal method: restore to background color
frameMetadataWriter->SetMetadataByName(L"/grctlext/Disposal", &propValue);
PropVariantClear(&propValue);
}
}
catch (...)
{
// Metadata setting failed, continue anyway
OutputDebugStringW(L"Warning: Failed to set GIF frame metadata\n");
}
// Commit the frame
OutputDebugStringW(L"About to commit frame to encoder...\n");
winrt::check_hresult(frameEncode->Commit());
OutputDebugStringW(L"Frame committed successfully\n");
// Unmap the staging texture
m_d3dContext->Unmap(stagingTexture.get(), 0);
// Increment and log frame count
m_frameCount++;
OutputDebugStringW((L"GIF Frame #" + std::to_wstring(m_frameCount) + L" fully encoded and committed\n").c_str());
return S_OK;
}
catch (const winrt::hresult_error& error)
{
OutputDebugStringW(error.message().c_str());
return error.code();
}
}
//----------------------------------------------------------------------------
//
// GifRecordingSession::StartAsync
//
//----------------------------------------------------------------------------
winrt::IAsyncAction GifRecordingSession::StartAsync()
{
auto expected = false;
if (m_isRecording.compare_exchange_strong(expected, true))
{
auto self = shared_from_this();
try
{
// Start capturing frames
auto frameStartTime = std::chrono::high_resolution_clock::now();
int captureAttempts = 0;
int successfulCaptures = 0;
int duplicatedFrames = 0;
// Keep track of the last frame to duplicate when needed
winrt::com_ptr<ID3D11Texture2D> lastCroppedTexture;
while (m_isRecording && !m_closed)
{
captureAttempts++;
auto frame = m_frameWait->TryGetNextFrame();
winrt::com_ptr<ID3D11Texture2D> croppedTexture;
if (frame)
{
successfulCaptures++;
auto contentSize = frame->ContentSize;
auto frameTexture = GetDXGIInterfaceFromObject<ID3D11Texture2D>(frame->FrameTexture);
D3D11_TEXTURE2D_DESC desc = {};
frameTexture->GetDesc(&desc);
// Use the smaller of the crop size or content size
auto width = min(m_rcCrop.right - m_rcCrop.left, contentSize.Width);
auto height = min(m_rcCrop.bottom - m_rcCrop.top, contentSize.Height);
D3D11_TEXTURE2D_DESC croppedDesc = {};
croppedDesc.Width = width;
croppedDesc.Height = height;
croppedDesc.MipLevels = 1;
croppedDesc.ArraySize = 1;
croppedDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
croppedDesc.SampleDesc.Count = 1;
croppedDesc.Usage = D3D11_USAGE_DEFAULT;
croppedDesc.BindFlags = D3D11_BIND_RENDER_TARGET;
winrt::check_hresult(m_d3dDevice->CreateTexture2D(&croppedDesc, nullptr, croppedTexture.put()));
// Set the content region to copy and clamp the coordinates
D3D11_BOX region = {};
region.left = std::clamp(m_rcCrop.left, static_cast<LONG>(0), static_cast<LONG>(desc.Width));
region.right = std::clamp(m_rcCrop.left + width, static_cast<LONG>(0), static_cast<LONG>(desc.Width));
region.top = std::clamp(m_rcCrop.top, static_cast<LONG>(0), static_cast<LONG>(desc.Height));
region.bottom = std::clamp(m_rcCrop.top + height, static_cast<LONG>(0), static_cast<LONG>(desc.Height));
region.back = 1;
// Copy the cropped region
m_d3dContext->CopySubresourceRegion(
croppedTexture.get(),
0,
0, 0, 0,
frameTexture.get(),
0,
&region);
// Save this as the last frame for duplication
lastCroppedTexture = croppedTexture;
}
else if (lastCroppedTexture)
{
// No new frame, duplicate the last one
duplicatedFrames++;
croppedTexture = lastCroppedTexture;
}
// Encode the frame (either new or duplicated)
if (croppedTexture)
{
HRESULT hr = EncodeFrame(croppedTexture.get());
if (FAILED(hr))
{
CloseInternal();
break;
}
}
// Wait for the next frame interval
co_await winrt::resume_after(std::chrono::milliseconds(1000 / m_frameRate));
}
// Commit the GIF encoder
if (m_gifEncoder)
{
auto frameEndTime = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(frameEndTime - frameStartTime).count();
OutputDebugStringW(L"Recording stopped. Committing GIF encoder...\n");
OutputDebugStringW((L"Total frames captured: " + std::to_wstring(m_frameCount) + L"\n").c_str());
OutputDebugStringW((L"Capture attempts: " + std::to_wstring(captureAttempts) + L"\n").c_str());
OutputDebugStringW((L"Successful captures: " + std::to_wstring(successfulCaptures) + L"\n").c_str());
OutputDebugStringW((L"Duplicated frames: " + std::to_wstring(duplicatedFrames) + L"\n").c_str());
OutputDebugStringW((L"Recording duration: " + std::to_wstring(duration) + L"ms\n").c_str());
OutputDebugStringW((L"Actual FPS: " + std::to_wstring(m_frameCount * 1000.0 / duration) + L"\n").c_str());
winrt::check_hresult(m_gifEncoder->Commit());
OutputDebugStringW(L"GIF encoder committed successfully\n");
}
}
catch (const winrt::hresult_error& error)
{
OutputDebugStringW(L"Error in GIF recording: ");
OutputDebugStringW(error.message().c_str());
OutputDebugStringW(L"\n");
// Try to commit the encoder even on error
if (m_gifEncoder)
{
try
{
m_gifEncoder->Commit();
}
catch (...) {}
}
CloseInternal();
}
}
co_return;
}
//----------------------------------------------------------------------------
//
// GifRecordingSession::Close
//
//----------------------------------------------------------------------------
void GifRecordingSession::Close()
{
auto expected = false;
if (m_closed.compare_exchange_strong(expected, true))
{
expected = true;
if (!m_isRecording.compare_exchange_strong(expected, false))
{
CloseInternal();
}
else
{
m_frameWait->StopCapture();
}
}
}
//----------------------------------------------------------------------------
//
// GifRecordingSession::CloseInternal
//
//----------------------------------------------------------------------------
void GifRecordingSession::CloseInternal()
{
m_frameWait->StopCapture();
m_itemClosed.revoke();
}

View File

@@ -0,0 +1,69 @@
//==============================================================================
//
// Zoomit
// Sysinternals - www.sysinternals.com
//
// GIF recording support using Windows Imaging Component (WIC)
//
//==============================================================================
#pragma once
#include "CaptureFrameWait.h"
#include <d3d11_4.h>
#include <vector>
class GifRecordingSession : public std::enable_shared_from_this<GifRecordingSession>
{
public:
[[nodiscard]] static std::shared_ptr<GifRecordingSession> Create(
winrt::Direct3D11::IDirect3DDevice const& device,
winrt::GraphicsCaptureItem const& item,
RECT const& cropRect,
uint32_t frameRate,
winrt::Streams::IRandomAccessStream const& stream);
~GifRecordingSession();
winrt::IAsyncAction StartAsync();
void EnableCursorCapture(bool enable = true) { m_frameWait->EnableCursorCapture(enable); }
void Close();
private:
GifRecordingSession(
winrt::Direct3D11::IDirect3DDevice const& device,
winrt::Capture::GraphicsCaptureItem const& item,
RECT const cropRect,
uint32_t frameRate,
winrt::Streams::IRandomAccessStream const& stream);
void CloseInternal();
HRESULT EncodeFrame(ID3D11Texture2D* texture);
private:
winrt::Direct3D11::IDirect3DDevice m_device{ nullptr };
winrt::com_ptr<ID3D11Device> m_d3dDevice;
winrt::com_ptr<ID3D11DeviceContext> m_d3dContext;
RECT m_rcCrop;
uint32_t m_frameRate;
winrt::GraphicsCaptureItem m_item{ nullptr };
winrt::GraphicsCaptureItem::Closed_revoker m_itemClosed;
std::shared_ptr<CaptureFrameWait> m_frameWait;
winrt::Streams::IRandomAccessStream m_stream{ nullptr };
// WIC components for GIF encoding
winrt::com_ptr<IWICImagingFactory> m_wicFactory;
winrt::com_ptr<IWICStream> m_wicStream;
winrt::com_ptr<IWICBitmapEncoder> m_gifEncoder;
winrt::com_ptr<IWICMetadataQueryWriter> m_encoderMetadataWriter;
std::atomic<bool> m_isRecording = false;
std::atomic<bool> m_closed = false;
uint32_t m_frameWidth=0;
uint32_t m_frameHeight=0;
uint32_t m_frameDelay=0;
uint32_t m_frameCount = 0;
int32_t m_width=0;
int32_t m_height=0;
};

View File

@@ -32,18 +32,18 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
// TEXTINCLUDE
//
1 TEXTINCLUDE
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
3 TEXTINCLUDE
BEGIN
"#include ""binres.rc""\0"
END
@@ -121,8 +121,8 @@ FONT 8, "MS Shell Dlg", 0, 0, 0x0
BEGIN
DEFPUSHBUTTON "OK",IDOK,166,306,50,14
PUSHBUTTON "Cancel",IDCANCEL,223,306,50,14
LTEXT "ZoomIt v9.10",IDC_VERSION,42,7,73,10
LTEXT "Copyright © 2006-2025 Mark Russinovich",IDC_COPYRIGHT,42,17,231,8
LTEXT "ZoomIt v9.20",IDC_VERSION,42,7,73,10
LTEXT "Copyright <EFBFBD> 2006-2025 Mark Russinovich",IDC_COPYRIGHT,42,17,231,8
CONTROL "<a HREF=""https://www.sysinternals.com"">Sysinternals - www.sysinternals.com</a>",IDC_LINK,
"SysLink",WS_TABSTOP,42,26,150,9
ICON "APPICON",IDC_STATIC,12,9,20,20
@@ -272,13 +272,15 @@ BEGIN
LTEXT "Note: Recording is only available on Windows 10 (version 1903) and higher.",IDC_STATIC,7,77,246,19
LTEXT "Scaling:",IDC_STATIC,30,115,26,8
COMBOBOX IDC_RECORD_SCALING,61,114,26,30,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | CBS_OEMCONVERT | CBS_SORT | WS_VSCROLL | WS_TABSTOP
LTEXT "Format:",IDC_STATIC,30,132,26,8
COMBOBOX IDC_RECORD_FORMAT,61,131,60,30,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | CBS_OEMCONVERT | WS_VSCROLL | WS_TABSTOP
LTEXT "Frame Rate:",IDC_STATIC,119,115,44,8,NOT WS_VISIBLE
COMBOBOX IDC_RECORD_FRAME_RATE,166,114,42,30,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | CBS_OEMCONVERT | CBS_SORT | NOT WS_VISIBLE | WS_VSCROLL | WS_TABSTOP
LTEXT "To crop the portion of the screen that will be recorded, enter the hotkey with the Shift key in the opposite mode. ",IDC_STATIC,7,32,246,19
LTEXT "To record a specific window, enter the hotkey with the Alt key in the opposite mode.",IDC_STATIC,7,55,246,19
CONTROL "&Capture audio input:",IDC_CAPTURE_AUDIO,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,7,137,83,10
COMBOBOX IDC_MICROPHONE,81,152,172,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Microphone:",IDC_STATIC,32,154,47,8
CONTROL "&Capture audio input:",IDC_CAPTURE_AUDIO,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,7,149,83,10
COMBOBOX IDC_MICROPHONE,81,164,172,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "Microphone:",IDC_STATIC,32,166,47,8
END
SNIP DIALOGEX 0, 0, 260, 68

View File

@@ -234,6 +234,7 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="GifRecordingSession.cpp" />
<ClCompile Include="pch.cpp" />
<ClCompile Include="SelectRectangle.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Use</PrecompiledHeader>
@@ -288,6 +289,7 @@
<ClInclude Include="AudioSampleGenerator.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\..\common\sysinternals\Eula\Eula.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)..\ZoomItModuleInterface\Trace.h" />
<ClInclude Include="GifRecordingSession.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="Registry.h" />
<ClInclude Include="resource.h" />

View File

@@ -54,6 +54,9 @@
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\..\common\sysinternals\WindowsVersions.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="GifRecordingSession.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Registry.h">
@@ -95,6 +98,9 @@
<ClInclude Include="ZoomItSettings.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="GifRecordingSession.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Image Include="appicon.ico">

View File

@@ -3,6 +3,13 @@
#include "Registry.h"
#include "DemoType.h"
// Recording format enum
enum class RecordingFormat
{
GIF = 0,
MP4 = 1
};
DWORD g_ToggleKey = (HOTKEYF_CONTROL << 8)| '1';
DWORD g_LiveZoomToggleKey = ((HOTKEYF_CONTROL) << 8)| '4';
DWORD g_DrawToggleKey = ((HOTKEYF_CONTROL) << 8)| '2';
@@ -38,8 +45,10 @@ BOOLEAN g_DemoTypeUserDriven = false;
TCHAR g_DemoTypeFile[MAX_PATH] = {0};
DWORD g_DemoTypeSpeedSlider = static_cast<int>(((MIN_TYPING_SPEED - MAX_TYPING_SPEED) / 2) + MAX_TYPING_SPEED);
DWORD g_RecordFrameRate = 30;
// Divide by 100 to get actual scaling
DWORD g_RecordScaling = 100;
DWORD g_RecordScaling = 100;
DWORD g_RecordScalingGIF = 50;
DWORD g_RecordScalingMP4 = 100;
RecordingFormat g_RecordingFormat = RecordingFormat::GIF;
BOOLEAN g_CaptureAudio = FALSE;
TCHAR g_MicrophoneDeviceId[MAX_PATH] = {0};
@@ -79,7 +88,9 @@ REG_SETTING RegSettings[] = {
{ L"ZoominSliderLevel", SETTING_TYPE_DWORD, 0, &g_SliderZoomLevel, static_cast<DOUBLE>(g_SliderZoomLevel) },
{ L"Font", SETTING_TYPE_BINARY, sizeof g_LogFont, &g_LogFont, static_cast<DOUBLE>(0) },
{ L"RecordFrameRate", SETTING_TYPE_DWORD, 0, &g_RecordFrameRate, static_cast<DOUBLE>(g_RecordFrameRate) },
{ L"RecordScaling", SETTING_TYPE_DWORD, 0, &g_RecordScaling, static_cast<DOUBLE>(g_RecordScaling) },
{ L"RecordingFormat", SETTING_TYPE_DWORD, 0, &g_RecordingFormat, static_cast<DOUBLE>(0) },
{ L"RecordScalingGIF", SETTING_TYPE_DWORD, 0, &g_RecordScalingGIF, static_cast<DOUBLE>(g_RecordScalingGIF) },
{ L"RecordScalingMP4", SETTING_TYPE_DWORD, 0, &g_RecordScalingMP4, static_cast<DOUBLE>(g_RecordScalingMP4) },
{ L"CaptureAudio", SETTING_TYPE_BOOLEAN, 0, &g_CaptureAudio, static_cast<DOUBLE>(g_CaptureAudio) },
{ L"MicrophoneDeviceId", SETTING_TYPE_STRING, sizeof(g_MicrophoneDeviceId), g_MicrophoneDeviceId, static_cast<DOUBLE>(0) },
{ NULL, SETTING_TYPE_DWORD, 0, NULL, static_cast<DOUBLE>(0) }

File diff suppressed because it is too large Load Diff

View File

@@ -93,6 +93,7 @@
#define IDC_DEMOTYPE_SLIDER2 1074
#define IDC_DEMOTYPE_STATIC2 1074
#define IDC_COPYRIGHT 1075
#define IDC_RECORD_FORMAT 1076
#define IDC_PEN_WIDTH 1105
#define IDC_TIMER 1106
#define IDC_SMOOTH_IMAGE 1107

View File

@@ -14,6 +14,9 @@ namespace winrt::PowerToys::ZoomItSettingsInterop::implementation
const unsigned int SPECIAL_SEMANTICS_SHORTCUT = 1;
const unsigned int SPECIAL_SEMANTICS_COLOR = 2;
const unsigned int SPECIAL_SEMANTICS_LOG_FONT = 3;
const unsigned int SPECIAL_SEMANTICS_RECORDING_FORMAT = 4;
const unsigned int SPECIAL_SEMANTICS_RECORD_SCALING_GIF = 5;
const unsigned int SPECIAL_SEMANTICS_RECORD_SCALING_MP4 = 6;
std::vector<unsigned char> base64_decode(const std::wstring& base64_string)
{
@@ -72,6 +75,9 @@ namespace winrt::PowerToys::ZoomItSettingsInterop::implementation
{ L"PenColor", SPECIAL_SEMANTICS_COLOR },
{ L"BreakPenColor", SPECIAL_SEMANTICS_COLOR },
{ L"Font", SPECIAL_SEMANTICS_LOG_FONT },
{ L"RecordingFormat", SPECIAL_SEMANTICS_RECORDING_FORMAT },
{ L"RecordScalingGIF", SPECIAL_SEMANTICS_RECORD_SCALING_GIF },
{ L"RecordScalingMP4", SPECIAL_SEMANTICS_RECORD_SCALING_MP4 },
};
hstring ZoomItSettings::LoadSettingsJson()
@@ -103,6 +109,11 @@ namespace winrt::PowerToys::ZoomItSettingsInterop::implementation
value & 0xFF);
_settings.add_property(curSetting->ValueName, hotkey.get_json());
}
else if (special_semantics->second == SPECIAL_SEMANTICS_RECORDING_FORMAT)
{
std::wstring formatString = (value == 0) ? L"GIF" : L"MP4";
_settings.add_property(L"RecordFormat", formatString);
}
else if (special_semantics->second == SPECIAL_SEMANTICS_COLOR)
{
/* PowerToys settings likes colors as #FFFFFF strings.
@@ -156,6 +167,9 @@ namespace winrt::PowerToys::ZoomItSettingsInterop::implementation
curSetting++;
}
DWORD recordScaling = (g_RecordingFormat == static_cast<RecordingFormat>(0)) ? g_RecordScalingGIF : g_RecordScalingMP4;
_settings.add_property<DWORD>(L"RecordScaling", recordScaling);
return _settings.get_raw_json().Stringify();
}
@@ -167,6 +181,8 @@ namespace winrt::PowerToys::ZoomItSettingsInterop::implementation
PowerToysSettings::PowerToyValues valuesFromSettings =
PowerToysSettings::PowerToyValues::from_json_string(json, L"ZoomIt");
bool formatChanged = false;
PREG_SETTING curSetting = RegSettings;
while (curSetting->ValueName)
{
@@ -212,6 +228,42 @@ namespace winrt::PowerToys::ZoomItSettingsInterop::implementation
*static_cast<PDWORD>(curSetting->Setting) = value;
}
}
else if (special_semantics->second == SPECIAL_SEMANTICS_RECORDING_FORMAT)
{
// Convert string ("GIF" or "MP4") to DWORD enum value (0=GIF, 1=MP4)
auto possibleValue = valuesFromSettings.get_string_value(L"RecordFormat");
if (possibleValue.has_value())
{
RecordingFormat oldFormat = g_RecordingFormat;
DWORD formatValue = (possibleValue.value() == L"GIF") ? 0 : 1;
RecordingFormat newFormat = static_cast<RecordingFormat>(formatValue);
*static_cast<PDWORD>(curSetting->Setting) = formatValue;
if (oldFormat != newFormat)
{
formatChanged = true;
if (oldFormat == static_cast<RecordingFormat>(0))
{
g_RecordScalingGIF = g_RecordScaling;
}
else
{
g_RecordScalingMP4 = g_RecordScaling;
}
if (newFormat == static_cast<RecordingFormat>(0))
{
g_RecordScaling = g_RecordScalingGIF;
}
else
{
g_RecordScaling = g_RecordScalingMP4;
}
}
}
}
else if (special_semantics->second == SPECIAL_SEMANTICS_COLOR)
{
/* PowerToys settings likes colors as #FFFFFF strings.
@@ -275,6 +327,22 @@ namespace winrt::PowerToys::ZoomItSettingsInterop::implementation
}
curSetting++;
}
auto recordScalingValue = valuesFromSettings.get_uint_value(L"RecordScaling");
if (recordScalingValue.has_value() && !formatChanged)
{
g_RecordScaling = recordScalingValue.value();
if (g_RecordingFormat == static_cast<RecordingFormat>(0))
{
g_RecordScalingGIF = recordScalingValue.value();
}
else
{
g_RecordScalingMP4 = recordScalingValue.value();
}
}
reg.WriteRegSettings(RegSettings);
}
}

View File

@@ -68,7 +68,6 @@ public sealed partial class MainWindow : WindowEx,
public MainWindow()
{
InitializeComponent();
HideWindow();
_hwnd = new HWND(WinRT.Interop.WindowNative.GetWindowHandle(this).ToInt32());

View File

@@ -5,7 +5,7 @@
<XesUseOneStoreVersioning>true</XesUseOneStoreVersioning>
<XesBaseYearForStoreVersion>2025</XesBaseYearForStoreVersion>
<VersionMajor>0</VersionMajor>
<VersionMinor>6</VersionMinor>
<VersionMinor>7</VersionMinor>
<VersionInfoProductName>Microsoft Command Palette</VersionInfoProductName>
</PropertyGroup>
</Project>

View File

@@ -15,8 +15,6 @@ namespace Microsoft.CmdPal.Ext.Apps.Programs;
public sealed partial class AppListItem : ListItem
{
private static readonly Tag _appTag = new("App");
private readonly AppCommand _appCommand;
private readonly AppItem _app;
private readonly Lazy<Details> _details;
@@ -48,7 +46,6 @@ public sealed partial class AppListItem : ListItem
_app = app;
Title = app.Name;
Subtitle = app.Subtitle;
Tags = [_appTag];
Icon = Icons.GenericAppIcon;
MoreCommands = AddPinCommands(_app.Commands!, isPinned);

View File

@@ -71,6 +71,7 @@
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<AdditionalDependencies>windowscodecs.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>

View File

@@ -28,6 +28,7 @@
<PlatformToolset>v143</PlatformToolset>
<WindowsPackageType>None</WindowsPackageType>
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
<WindowsAppSdkUndockedRegFreeWinRTInitialize>true</WindowsAppSdkUndockedRegFreeWinRTInitialize>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">

View File

@@ -17,10 +17,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
FoundryLocal,
Mistral,
Google,
HuggingFace,
AzureAIInference,
Ollama,
Anthropic,
AmazonBedrock,
}
}

View File

@@ -29,11 +29,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library
"ml" or "windowsml" or "winml" => AIServiceType.ML,
"mistral" => AIServiceType.Mistral,
"google" or "googleai" or "googlegemini" => AIServiceType.Google,
"huggingface" => AIServiceType.HuggingFace,
"azureaiinference" or "azureinference" => AIServiceType.AzureAIInference,
"ollama" => AIServiceType.Ollama,
"anthropic" => AIServiceType.Anthropic,
"amazonbedrock" or "bedrock" => AIServiceType.AmazonBedrock,
_ => AIServiceType.Unknown,
};
}
@@ -52,11 +49,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library
AIServiceType.ML => "ML",
AIServiceType.Mistral => "Mistral",
AIServiceType.Google => "Google",
AIServiceType.HuggingFace => "HuggingFace",
AIServiceType.AzureAIInference => "AzureAIInference",
AIServiceType.Ollama => "Ollama",
AIServiceType.Anthropic => "Anthropic",
AIServiceType.AmazonBedrock => "AmazonBedrock",
AIServiceType.Unknown => string.Empty,
_ => throw new ArgumentOutOfRangeException(nameof(serviceType), serviceType, "Unsupported AI service type."),
};
@@ -76,11 +70,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library
AIServiceType.ML => "ml",
AIServiceType.Mistral => "mistral",
AIServiceType.Google => "google",
AIServiceType.HuggingFace => "huggingface",
AIServiceType.AzureAIInference => "azureaiinference",
AIServiceType.Ollama => "ollama",
AIServiceType.Anthropic => "anthropic",
AIServiceType.AmazonBedrock => "amazonbedrock",
_ => string.Empty,
};
}

View File

@@ -15,31 +15,6 @@ public static class AIServiceTypeRegistry
{
private static readonly Dictionary<AIServiceType, AIServiceTypeMetadata> MetadataMap = new()
{
[AIServiceType.AmazonBedrock] = new AIServiceTypeMetadata
{
ServiceType = AIServiceType.AmazonBedrock,
DisplayName = "Amazon Bedrock",
IsAvailableInUI = false, // Currently disabled in UI
IconPath = "ms-appx:///Assets/Settings/Icons/Models/Bedrock.svg",
IsOnlineService = true,
LegalDescription = "AdvancedPaste_AmazonBedrock_LegalDescription",
TermsLabel = "AdvancedPaste_AmazonBedrock_TermsLabel",
TermsUri = new Uri("https://aws.amazon.com/service-terms/"),
PrivacyLabel = "AdvancedPaste_AmazonBedrock_PrivacyLabel",
PrivacyUri = new Uri("https://aws.amazon.com/privacy/"),
},
[AIServiceType.Anthropic] = new AIServiceTypeMetadata
{
ServiceType = AIServiceType.Anthropic,
DisplayName = "Anthropic",
IconPath = "ms-appx:///Assets/Settings/Icons/Models/Anthropic.svg",
IsOnlineService = true,
LegalDescription = "AdvancedPaste_Anthropic_LegalDescription",
TermsLabel = "AdvancedPaste_Anthropic_TermsLabel",
TermsUri = new Uri("https://privacy.claude.com/en/collections/10672567-policies-terms-of-service"),
PrivacyLabel = "AdvancedPaste_Anthropic_PrivacyLabel",
PrivacyUri = new Uri("https://privacy.claude.com/en/"),
},
[AIServiceType.AzureAIInference] = new AIServiceTypeMetadata
{
ServiceType = AIServiceType.AzureAIInference,
@@ -85,14 +60,6 @@ public static class AIServiceTypeRegistry
PrivacyLabel = "AdvancedPaste_Google_PrivacyLabel",
PrivacyUri = new Uri("https://support.google.com/gemini/answer/13594961"),
},
[AIServiceType.HuggingFace] = new AIServiceTypeMetadata
{
ServiceType = AIServiceType.HuggingFace,
DisplayName = "Hugging Face",
IconPath = "ms-appx:///Assets/Settings/Icons/Models/HuggingFace.svg",
IsOnlineService = true,
IsAvailableInUI = false, // Currently disabled in UI
},
[AIServiceType.Mistral] = new AIServiceTypeMetadata
{
ServiceType = AIServiceType.Mistral,

View File

@@ -87,6 +87,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library
public IntProperty RecordScaling { get; set; }
public StringProperty RecordFormat { get; set; }
public BoolProperty CaptureAudio { get; set; }
public StringProperty MicrophoneDeviceId { get; set; }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -1,10 +0,0 @@
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2092_1822)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.718 2.34668H12.12L16.5 13.3333H14.098L9.718 2.34668ZM4.87933 2.34668H7.39067L11.7707 13.3333H9.32133L8.426 11.026H3.84467L2.94867 13.3327H0.5L4.88 2.34801L4.87933 2.34668ZM7.634 8.98601L6.13533 5.12468L4.63667 8.98668H7.63333L7.634 8.98601Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_2092_1822">
<rect width="16" height="16" fill="white" transform="translate(0.5)"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 585 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.7 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -317,7 +317,6 @@ namespace Microsoft.PowerToys.Settings.UI.Views
bool requiresEndpoint = serviceKind is AIServiceType.AzureOpenAI
or AIServiceType.AzureAIInference
or AIServiceType.Mistral
or AIServiceType.HuggingFace
or AIServiceType.Ollama;
bool requiresDeployment = serviceKind == AIServiceType.AzureOpenAI;
bool requiresApiVersion = serviceKind == AIServiceType.AzureOpenAI;
@@ -874,7 +873,6 @@ namespace Microsoft.PowerToys.Settings.UI.Views
AIServiceType.AzureOpenAI => "https://your-resource.openai.azure.com/",
AIServiceType.AzureAIInference => "https://{resource-name}.cognitiveservices.azure.com/",
AIServiceType.Mistral => "https://api.mistral.ai/v1/",
AIServiceType.HuggingFace => "https://api-inference.huggingface.co/models/",
AIServiceType.Ollama => "http://localhost:11434/",
_ => "https://your-resource.openai.azure.com/",
};

View File

@@ -241,6 +241,12 @@
<ComboBoxItem>1.0</ComboBoxItem>
</ComboBox>
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard Name="ZoomItRecordFormat" x:Uid="ZoomIt_Record_Format">
<ComboBox MinWidth="{StaticResource SettingActionControlMinWidth}" SelectedIndex="{x:Bind Path=ViewModel.RecordFormatIndex, Mode=TwoWay}">
<ComboBoxItem>GIF</ComboBoxItem>
<ComboBoxItem>MP4</ComboBoxItem>
</ComboBox>
</tkcontrols:SettingsCard>
<tkcontrols:SettingsCard Name="ZoomItRecordCaptureAudio" x:Uid="ZoomIt_Record_CaptureAudio">
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.RecordCaptureAudio, Mode=TwoWay}" />
</tkcontrols:SettingsCard>

View File

@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
@@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
@@ -708,15 +708,6 @@ Please review the placeholder content that represents the final terms and usage
<data name="AdvancedPaste_Google_PrivacyLabel" xml:space="preserve">
<value>Google Privacy Policy</value>
</data>
<data name="AdvancedPaste_Anthropic_LegalDescription" xml:space="preserve">
<value>Your API key connects directly to Anthropic services. By setting up this provider, you agree to comply with Anthropic's usage policies and data handling practices.</value>
</data>
<data name="AdvancedPaste_Anthropic_TermsLabel" xml:space="preserve">
<value>Anthropic Terms of Service</value>
</data>
<data name="AdvancedPaste_Anthropic_PrivacyLabel" xml:space="preserve">
<value>Anthropic Privacy Policy</value>
</data>
<data name="AdvancedPaste_Mistral_LegalDescription" xml:space="preserve">
<value>Your API key connects directly to Mistral services. By setting up this provider, you agree to comply with Mistral's usage policies and data handling practices.</value>
</data>
@@ -726,15 +717,6 @@ Please review the placeholder content that represents the final terms and usage
<data name="AdvancedPaste_Mistral_PrivacyLabel" xml:space="preserve">
<value>Mistral Privacy Policy</value>
</data>
<data name="AdvancedPaste_AmazonBedrock_LegalDescription" xml:space="preserve">
<value>Your API key connects directly to Amazon services. By setting up this provider, you agree to comply with Amazon's usage policies and data handling practices.</value>
</data>
<data name="AdvancedPaste_AmazonBedrock_TermsLabel" xml:space="preserve">
<value>AWS Service Terms</value>
</data>
<data name="AdvancedPaste_AmazonBedrock_PrivacyLabel" xml:space="preserve">
<value>AWS Privacy Notice</value>
</data>
<data name="AdvancedPaste_Ollama_TermsLabel" xml:space="preserve">
<value>Ollama Terms of Service</value>
</data>
@@ -2269,7 +2251,7 @@ Take a moment to preview the various utilities listed or view our comprehensive
<value>Press the **Restart as administrator** button from the File Locksmith UI to also get information on elevated processes that might be using the files.</value>
</data>
<data name="Oobe_FileExplorer_HowToEnable.Text" xml:space="preserve">
<value>Select **View** which is located at the top of File Explorer, followed by **Show**, and then **Preview pane**.
<value>Select **View** which is located at the top of File Explorer, followed by **Show**, and then **Preview pane**.
From there, simply click on one of the supported files in the File Explorer and observe the content on the preview pane!</value>
</data>
<data name="Oobe_HowToCreateMappings.Text" xml:space="preserve">
@@ -5046,6 +5028,9 @@ To record a specific window, enter the hotkey with the Alt key in the opposite m
<data name="ZoomIt_Record_Scaling.Header" xml:space="preserve">
<value>Scaling</value>
</data>
<data name="ZoomIt_Record_Format.Header" xml:space="preserve">
<value>Format</value>
</data>
<data name="ZoomIt_Record_CaptureAudio.Header" xml:space="preserve">
<value>Capture audio input</value>
</data>

View File

@@ -10,10 +10,8 @@ using System.ComponentModel;
using System.Globalization;
using System.IO.Abstractions;
using System.Linq;
using System.Reflection;
using System.Runtime.Versioning;
using System.Text.Json;
using System.Text.Json.Serialization;
using global::PowerToys.GPOWrapper;
using Microsoft.PowerToys.Settings.UI.Helpers;
@@ -57,6 +55,16 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
private Func<string, int> SendConfigMSG { get; }
private static readonly HashSet<string> CustomActionNonPersistedProperties = new(StringComparer.Ordinal)
{
nameof(AdvancedPasteCustomAction.CanMoveUp),
nameof(AdvancedPasteCustomAction.CanMoveDown),
nameof(AdvancedPasteCustomAction.IsValid),
nameof(AdvancedPasteCustomAction.HasConflict),
nameof(AdvancedPasteCustomAction.Tooltip),
nameof(AdvancedPasteCustomAction.SubActions),
};
public AdvancedPasteViewModel(
ISettingsUtils settingsUtils,
ISettingsRepository<GeneralSettings> settingsRepository,
@@ -558,11 +566,9 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
AIServiceType.OpenAI => "gpt-4",
AIServiceType.AzureOpenAI => "gpt-4",
AIServiceType.Mistral => "mistral-large-latest",
AIServiceType.Google => "gemini-1.5-pro",
AIServiceType.Google => "gemini-2.5-pro",
AIServiceType.AzureAIInference => "gpt-4o-mini",
AIServiceType.Ollama => "llama3",
AIServiceType.Anthropic => "claude-3-5-sonnet",
AIServiceType.AmazonBedrock => "anthropic.claude-3-haiku",
_ => string.Empty,
};
}
@@ -589,7 +595,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
AIServiceType.AzureAIInference => GPOWrapper.GetAllowedAdvancedPasteAzureAIInferenceValue(),
AIServiceType.Mistral => GPOWrapper.GetAllowedAdvancedPasteMistralValue(),
AIServiceType.Google => GPOWrapper.GetAllowedAdvancedPasteGoogleValue(),
AIServiceType.Anthropic => GPOWrapper.GetAllowedAdvancedPasteAnthropicValue(),
_ => GpoRuleConfigured.Unavailable,
};
@@ -844,9 +849,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
"azureaiinference" => "https://azure.microsoft.com/products/ai-services/ai-inference",
"mistral" => "https://console.mistral.ai/account/api-keys",
"google" => "https://ai.google.dev/",
"huggingface" => "https://huggingface.co/settings/tokens",
"anthropic" => "https://console.anthropic.com/account/keys",
"amazonbedrock" => "https://aws.amazon.com/bedrock/",
"ollama" => "https://ollama.com/",
_ => "https://platform.openai.com/api-keys",
};
@@ -1000,7 +1002,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
private void OnCustomActionPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (typeof(AdvancedPasteCustomAction).GetProperty(e.PropertyName).GetCustomAttribute<JsonIgnoreAttribute>() == null)
if (!string.IsNullOrEmpty(e.PropertyName) && !CustomActionNonPersistedProperties.Contains(e.PropertyName))
{
SaveCustomActions();
}

View File

@@ -652,6 +652,54 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
}
}
public int RecordFormatIndex
{
get
{
if (_zoomItSettings.Properties.RecordFormat.Value == "GIF")
{
return 0;
}
if (_zoomItSettings.Properties.RecordFormat.Value == "MP4")
{
return 1;
}
return 0;
}
set
{
int format = 0;
if (_zoomItSettings.Properties.RecordFormat.Value == "GIF")
{
format = 0;
}
if (_zoomItSettings.Properties.RecordFormat.Value == "MP4")
{
format = 1;
}
if (format != value)
{
_zoomItSettings.Properties.RecordFormat.Value = value == 0 ? "GIF" : "MP4";
OnPropertyChanged(nameof(RecordFormatIndex));
NotifySettingsChanged();
// Reload settings to get the new format's scaling value
var reloadedSettings = global::PowerToys.ZoomItSettingsInterop.ZoomItSettings.LoadSettingsJson();
var reloaded = JsonSerializer.Deserialize<ZoomItSettings>(reloadedSettings, _serializerOptions);
if (reloaded != null && reloaded.Properties != null)
{
_zoomItSettings.Properties.RecordScaling.Value = reloaded.Properties.RecordScaling.Value;
OnPropertyChanged(nameof(RecordScalingIndex));
}
}
}
}
public bool RecordCaptureAudio
{
get => _zoomItSettings.Properties.CaptureAudio.Value;

View File

@@ -92,7 +92,6 @@ void ReportGPOValues(const std::filesystem::path &tmpDir)
report << "getAllowedAdvancedPasteAzureAIInferenceValue: " << gpo_rule_configured_to_string(powertoys_gpo::getAllowedAdvancedPasteAzureAIInferenceValue()) << std::endl;
report << "getAllowedAdvancedPasteMistralValue: " << gpo_rule_configured_to_string(powertoys_gpo::getAllowedAdvancedPasteMistralValue()) << std::endl;
report << "getAllowedAdvancedPasteGoogleValue: " << gpo_rule_configured_to_string(powertoys_gpo::getAllowedAdvancedPasteGoogleValue()) << std::endl;
report << "getAllowedAdvancedPasteAnthropicValue: " << gpo_rule_configured_to_string(powertoys_gpo::getAllowedAdvancedPasteAnthropicValue()) << std::endl;
report << "getAllowedAdvancedPasteOllamaValue: " << gpo_rule_configured_to_string(powertoys_gpo::getAllowedAdvancedPasteOllamaValue()) << std::endl;
report << "getAllowedAdvancedPasteFoundryLocalValue: " << gpo_rule_configured_to_string(powertoys_gpo::getAllowedAdvancedPasteFoundryLocalValue()) << std::endl;
report << "getConfiguredMwbClipboardSharingEnabledValue: " << gpo_rule_configured_to_string(powertoys_gpo::getConfiguredMwbClipboardSharingEnabledValue()) << std::endl;