Compare commits
12 Commits
dev/vanzue
...
leilzh/adc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
528f9665cd | ||
|
|
552e305556 | ||
|
|
24d7ae54ce | ||
|
|
a271a2f8af | ||
|
|
a33c484c93 | ||
|
|
f20c3b832b | ||
|
|
9c2884ab41 | ||
|
|
076828f592 | ||
|
|
19d0f7827c | ||
|
|
b1474c4353 | ||
|
|
301d504db1 | ||
|
|
e4a8488a2a |
10
.github/actions/spell-check/expect.txt
vendored
@@ -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
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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/>
|
||||
|
||||
BIN
doc/images/icons/CursorWrap.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 21 KiB |
BIN
doc/images/icons/MouseJump.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 22 KiB |
@@ -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());
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -181,8 +181,6 @@ namespace AdvancedPaste.Services.CustomActions
|
||||
{
|
||||
AIServiceType.Onnx => false,
|
||||
AIServiceType.Ollama => false,
|
||||
AIServiceType.Anthropic => false,
|
||||
AIServiceType.AmazonBedrock => false,
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
548
src/modules/ZoomIt/ZoomIt/GifRecordingSession.cpp
Normal 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,
|
||||
®ion);
|
||||
|
||||
// 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();
|
||||
}
|
||||
69
src/modules/ZoomIt/ZoomIt/GifRecordingSession.h
Normal 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;
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,6 @@ public sealed partial class MainWindow : WindowEx,
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
HideWindow();
|
||||
|
||||
_hwnd = new HWND(WinRT.Interop.WindowNative.GetWindowHandle(this).ToInt32());
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>windowscodecs.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -17,10 +17,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
FoundryLocal,
|
||||
Mistral,
|
||||
Google,
|
||||
HuggingFace,
|
||||
AzureAIInference,
|
||||
Ollama,
|
||||
Anthropic,
|
||||
AmazonBedrock,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 1.7 KiB |
@@ -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 |
|
Before Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.3 KiB |
@@ -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/",
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||