diff --git a/WindowTranslator.Abstractions/Stores/IProcessInfoStore.cs b/WindowTranslator.Abstractions/Stores/IProcessInfoStore.cs
index d2307be2..7c44932a 100644
--- a/WindowTranslator.Abstractions/Stores/IProcessInfoStore.cs
+++ b/WindowTranslator.Abstractions/Stores/IProcessInfoStore.cs
@@ -14,4 +14,9 @@ public interface IProcessInfoStore
/// 対象のプロセスの名前
///
string Name { get; }
+
+ ///
+ /// 対象がモニター(ディスプレイ)かどうか
+ ///
+ bool IsMonitor { get; }
}
diff --git a/WindowTranslator/Modules/Capture/WindowsGraphicsCapture.cs b/WindowTranslator/Modules/Capture/WindowsGraphicsCapture.cs
index ceb4f9c8..e4a62bb2 100644
--- a/WindowTranslator/Modules/Capture/WindowsGraphicsCapture.cs
+++ b/WindowTranslator/Modules/Capture/WindowsGraphicsCapture.cs
@@ -8,19 +8,22 @@
using Windows.Graphics.DirectX;
using Windows.Graphics.DirectX.Direct3D11;
using WindowTranslator.ComponentModel;
+using WindowTranslator.Stores;
using static Windows.Win32.PInvoke;
namespace WindowTranslator.Modules.Capture;
[DefaultModule]
[DisplayName("Windows標準キャプチャー")]
-public sealed class WindowsGraphicsCapture(ILogger logger) : ICaptureModule, IDisposable
+public sealed class WindowsGraphicsCapture(ILogger logger, IProcessInfoStore processInfo) : ICaptureModule, IDisposable
{
private readonly IDirect3DDevice device = Direct3D11Helper.GetOrCreateDevice()!;
private readonly SemaphoreSlim processing = new(1, 1);
private readonly ILogger logger = logger;
+ private readonly IProcessInfoStore processInfo = processInfo;
private readonly CancellationTokenSource cts = new();
private nint targetWindow;
+ private bool isMonitor;
private Direct3D11CaptureFramePool? framePool;
private GraphicsCaptureSession? session;
private SizeInt32 lastSize = new(1000, 1000);
@@ -40,9 +43,29 @@ public void StartCapture(IntPtr targetWindow)
{
this.logger.LogDebug("StartCapture");
this.targetWindow = targetWindow;
- var item = CaptureHelper.CreateItemForWindow(targetWindow)!;
+
+ // ディスプレイかウィンドウかを判定
+ this.isMonitor = this.processInfo.IsMonitor;
+
+ GraphicsCaptureItem? item;
+ if (this.isMonitor)
+ {
+ this.logger.LogDebug("Creating capture item for monitor");
+ item = CaptureHelper.CreateItemForMonitor(targetWindow);
+ }
+ else
+ {
+ this.logger.LogDebug("Creating capture item for window");
+ item = CaptureHelper.CreateItemForWindow(targetWindow);
+ }
+
+ if (item is null)
+ {
+ throw new InvalidOperationException("Failed to create capture item");
+ }
+
this.lastSize = item.Size;
- this.lastMaximized = IsZoomed(new(targetWindow));
+ this.lastMaximized = this.isMonitor ? false : IsZoomed(new(targetWindow));
this.framePool = Direct3D11CaptureFramePool.Create(device, DirectXPixelFormat.B8G8R8A8UIntNormalized, 1, lastSize);
this.framePool.FrameArrived += FramePool_FrameArrived;
this.session = this.framePool.CreateCaptureSession(item);
@@ -78,7 +101,8 @@ private async void FramePool_FrameArrived(Direct3D11CaptureFramePool sender, obj
{
return;
}
- if (this.lastMaximized != IsZoomed(new(targetWindow)))
+ // モニターの場合は最大化チェックをスキップ
+ if (!this.isMonitor && this.lastMaximized != IsZoomed(new(targetWindow)))
{
this.lastMaximized = !this.lastMaximized;
this.logger.LogDebug("セッション再作成");
diff --git a/WindowTranslator/Modules/Main/CaptureMainWindow.xaml.cs b/WindowTranslator/Modules/Main/CaptureMainWindow.xaml.cs
index 56f30811..76650f43 100644
--- a/WindowTranslator/Modules/Main/CaptureMainWindow.xaml.cs
+++ b/WindowTranslator/Modules/Main/CaptureMainWindow.xaml.cs
@@ -33,6 +33,12 @@ private void Window_Loaded(object sender, RoutedEventArgs e)
private void CheckTargetWindow()
{
+ // ディスプレイの場合はウィンドウチェックをスキップ
+ if (this.processInfo.IsMonitor)
+ {
+ return;
+ }
+
var windowInfo = new WINDOWINFO() { cbSize = (uint)Marshal.SizeOf() };
if (!GetWindowInfo((HWND)this.processInfo.MainWindowHandle, ref windowInfo))
{
diff --git a/WindowTranslator/Modules/Main/OverlayMainWindow.xaml.cs b/WindowTranslator/Modules/Main/OverlayMainWindow.xaml.cs
index e9e1f108..1eb5e6e1 100644
--- a/WindowTranslator/Modules/Main/OverlayMainWindow.xaml.cs
+++ b/WindowTranslator/Modules/Main/OverlayMainWindow.xaml.cs
@@ -94,10 +94,14 @@ private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.windowHandle = new WindowInteropHelper(this).Handle;
- if (!this.desktopManager.IsWindowOnCurrentVirtualDesktop(this.processInfo.MainWindowHandle))
+ // ディスプレイの場合は仮想デスクトップチェックをスキップ
+ if (!this.processInfo.IsMonitor)
{
- var targetDesktop = this.desktopManager.GetWindowDesktopId(this.processInfo.MainWindowHandle);
- this.desktopManager.MoveWindowToDesktop(this.windowHandle, ref targetDesktop);
+ if (!this.desktopManager.IsWindowOnCurrentVirtualDesktop(this.processInfo.MainWindowHandle))
+ {
+ var targetDesktop = this.desktopManager.GetWindowDesktopId(this.processInfo.MainWindowHandle);
+ this.desktopManager.MoveWindowToDesktop(this.windowHandle, ref targetDesktop);
+ }
}
var extendedStyle = (WINDOW_EX_STYLE)GetWindowLong(new(windowHandle), WINDOW_LONG_PTR_INDEX.GWL_EXSTYLE) | WINDOW_EX_STYLE.WS_EX_TRANSPARENT;
@@ -146,6 +150,14 @@ protected override void OnClosed(EventArgs e)
private unsafe void UpdateWindowPositionAndSize()
{
var sw = Stopwatch.StartNew();
+
+ // ディスプレイの場合は専用の処理
+ if (this.processInfo.IsMonitor)
+ {
+ UpdateDisplayPositionAndSize();
+ return;
+ }
+
var windowInfo = new WINDOWINFO() { cbSize = (uint)Marshal.SizeOf() };
if (!GetWindowInfo(new(this.processInfo.MainWindowHandle), ref windowInfo))
{
@@ -213,6 +225,63 @@ private unsafe void UpdateWindowPositionAndSize()
this.SetCurrentValue(HeightProperty, height / eDpiScale);
}
+ private unsafe void UpdateDisplayPositionAndSize()
+ {
+ // モニターハンドルを取得(IntPtrとして既に持っている)
+ var monitorHandle = this.processInfo.MainWindowHandle;
+ var monitorInfo = default(MONITORINFOEXW);
+ monitorInfo.monitorInfo.cbSize = (uint)Marshal.SizeOf();
+
+ if (!GetMonitorInfo(monitorHandle, ref monitorInfo.monitorInfo))
+ {
+ this.logger.LogWarning("Failed to get monitor info");
+ return;
+ }
+
+ var left = monitorInfo.monitorInfo.rcMonitor.left;
+ var top = monitorInfo.monitorInfo.rcMonitor.top;
+ var width = monitorInfo.monitorInfo.rcMonitor.right - left;
+ var height = monitorInfo.monitorInfo.rcMonitor.bottom - top;
+
+ // モニター座標の検証
+ if (width <= 0 || height <= 0)
+ {
+ this.logger.LogWarning($"Invalid monitor dimensions: {width}x{height}");
+ return;
+ }
+
+ // モニターの解像度情報を取得
+ var mode = default(DEVMODEW);
+ var eDpiScale = GetDpiForSystem() / 96.0;
+ var rDpiScale = eDpiScale;
+
+ if (EnumDisplaySettings(monitorInfo.szDevice.ToString(), ENUM_DISPLAY_SETTINGS_MODE.ENUM_CURRENT_SETTINGS, ref mode))
+ {
+ // EnumDisplaySettings が成功した場合のみ rDpiScale を計算
+ if (mode.dmPelsWidth > 0)
+ {
+ rDpiScale = eDpiScale * mode.dmPelsWidth / width;
+ }
+ }
+ else
+ {
+ this.logger.LogWarning("Failed to get display settings, using default DPI scale");
+ }
+
+ GetCursorPos(out var nativePos);
+ var x = (nativePos.X - left) / eDpiScale;
+ var y = (nativePos.Y - top) / eDpiScale;
+
+ this.logger.LogDebug($"Display: (x:{left:f2}, y:{top:f2}, w:{width:f2}, h:{height:f2}), マウス位置:({x:f2}, {y:f2})");
+ this.SetCurrentValue(MousePosProperty, new Point(x, y));
+ this.SetCurrentValue(VisibilityProperty, Visibility.Visible);
+ this.SetCurrentValue(ScaleProperty, 1 / rDpiScale);
+ this.SetCurrentValue(LeftProperty, left / eDpiScale);
+ this.SetCurrentValue(TopProperty, top / eDpiScale);
+ this.SetCurrentValue(WidthProperty, width / eDpiScale);
+ this.SetCurrentValue(HeightProperty, height / eDpiScale);
+ }
+
private nint WndProc(nint hwnd, int msg, nint wParam, nint lParam, ref bool handled)
{
if (msg != WM_HOTKEY)
diff --git a/WindowTranslator/Modules/Startup/StartupViewModel.cs b/WindowTranslator/Modules/Startup/StartupViewModel.cs
index ae1a57e7..ae9770fd 100644
--- a/WindowTranslator/Modules/Startup/StartupViewModel.cs
+++ b/WindowTranslator/Modules/Startup/StartupViewModel.cs
@@ -1,6 +1,7 @@
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
+using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
@@ -10,6 +11,7 @@
using Kamishibai;
using Microsoft.Extensions.DependencyInjection;
using Windows.Graphics.Capture;
+using Windows.Win32.Graphics.Gdi;
using WindowTranslator.Extensions;
using WindowTranslator.Modules.Main;
using WindowTranslator.Properties;
@@ -101,7 +103,20 @@ public async Task RunAsync()
}
return;
}
+
+ // まずウィンドウとして検索
p = FindProcessByWindowTitle(item.DisplayName, item.Size);
+
+ // ウィンドウが見つからない場合、ディスプレイとして処理
+ if (p is null)
+ {
+ var displayInfo = FindDisplayBySize(item.Size);
+ if (displayInfo is not null)
+ {
+ p = new ProcessInfo(item.DisplayName, -1, displayInfo.Value.MonitorHandle, $"DISPLAY__{displayInfo.Value.Index}");
+ }
+ }
+
if (p is null)
{
this.presentationService.ShowMessage(string.Format(Resources.UnknownWindow, item.DisplayName), icon: Kamishibai.MessageBoxImage.Error, owner: window);
@@ -205,6 +220,39 @@ public static void Exit()
return result ?? candidate;
}
+ private (IntPtr MonitorHandle, int Index)? FindDisplayBySize(Windows.Graphics.SizeInt32 targetSize)
+ {
+ var monitors = new List<(IntPtr Handle, int Width, int Height)>();
+
+ // モニターを列挙
+ EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, (hMonitor, hdcMonitor, lprcMonitor, dwData) =>
+ {
+ var monitorInfo = new MONITORINFOEXW();
+ monitorInfo.monitorInfo.cbSize = (uint)Marshal.SizeOf();
+
+ if (GetMonitorInfo(hMonitor, ref monitorInfo.monitorInfo))
+ {
+ var width = monitorInfo.monitorInfo.rcMonitor.right - monitorInfo.monitorInfo.rcMonitor.left;
+ var height = monitorInfo.monitorInfo.rcMonitor.bottom - monitorInfo.monitorInfo.rcMonitor.top;
+ monitors.Add((hMonitor, width, height));
+ }
+
+ return true;
+ }, IntPtr.Zero);
+
+ // サイズが一致するモニターを探す
+ for (int i = 0; i < monitors.Count; i++)
+ {
+ var (handle, width, height) = monitors[i];
+ if (width == targetSize.Width && height == targetSize.Height)
+ {
+ return (handle, i);
+ }
+ }
+
+ return null;
+ }
+
private record ProcessInfo(string Title, int PID, IntPtr WindowHandle, string Name);
}
diff --git a/WindowTranslator/NativeMethods.txt b/WindowTranslator/NativeMethods.txt
index 7f4af304..2c646062 100644
--- a/WindowTranslator/NativeMethods.txt
+++ b/WindowTranslator/NativeMethods.txt
@@ -35,6 +35,9 @@ GetWindowTextLength
GetWindowThreadProcessId
GetClassName
DwmGetWindowAttribute
+EnumDisplayMonitors
+GetMonitorInfo
+MonitorFromPoint
// WindowMonitor
diff --git a/WindowTranslator/Properties/Resources.de.resx b/WindowTranslator/Properties/Resources.de.resx
index 37ec61bb..7e200d35 100644
--- a/WindowTranslator/Properties/Resources.de.resx
+++ b/WindowTranslator/Properties/Resources.de.resx
@@ -386,9 +386,7 @@
Bitte wählen Sie ein anderes Fenster als WindowTranslator aus
- Das ausgewählte Fenster "{0}" kann den Prozess nicht bestimmen und kann nicht erfasst werden.
-Monitore werden nicht unterstützt.
-
+ Das ausgewählte Fenster "{0}" kann den Prozess nicht bestimmen und kann nicht erfasst werden.
Einstellungsvalidierungsfehler
diff --git a/WindowTranslator/Properties/Resources.en.resx b/WindowTranslator/Properties/Resources.en.resx
index 6cfe85f0..36d48916 100644
--- a/WindowTranslator/Properties/Resources.en.resx
+++ b/WindowTranslator/Properties/Resources.en.resx
@@ -386,9 +386,7 @@
Please select a window other than WindowTranslator
- The selected window "{0}" cannot determine the process and cannot be captured.
-Monitors are not supported.
-
+ The selected window "{0}" cannot determine the process and cannot be captured.
Settings Validation Error
diff --git a/WindowTranslator/Properties/Resources.hi.resx b/WindowTranslator/Properties/Resources.hi.resx
index a2a38cde..4f621733 100644
--- a/WindowTranslator/Properties/Resources.hi.resx
+++ b/WindowTranslator/Properties/Resources.hi.resx
@@ -328,8 +328,7 @@
कृपया WindowTranslator के अलावा कोई अन्य विंडो चुनें
- चयनित विंडो "{0}" प्रक्रिया निर्धारित नहीं कर सकती और कैप्चर नहीं की जा सकती।
-मॉनिटर समर्थित नहीं हैं।
+ चयनित विंडो "{0}" प्रक्रिया निर्धारित नहीं कर सकती और कैप्चर नहीं की जा सकती।
सेटिंग्स सत्यापन त्रुटि
diff --git a/WindowTranslator/Properties/Resources.id.resx b/WindowTranslator/Properties/Resources.id.resx
index 152157fa..ca9fbc34 100644
--- a/WindowTranslator/Properties/Resources.id.resx
+++ b/WindowTranslator/Properties/Resources.id.resx
@@ -328,8 +328,7 @@
Please select a window other than WindowTranslator
- Jendela yang dipilih "{0}" tidak dapat menentukan proses dan tidak dapat ditangkap.
-Monitor tidak didukung.
+ Jendela yang dipilih "{0}" tidak dapat menentukan proses dan tidak dapat ditangkap.
Settings Validation Error
diff --git a/WindowTranslator/Properties/Resources.ko.resx b/WindowTranslator/Properties/Resources.ko.resx
index 0601b332..ec384314 100644
--- a/WindowTranslator/Properties/Resources.ko.resx
+++ b/WindowTranslator/Properties/Resources.ko.resx
@@ -386,9 +386,7 @@
WindowTranslator 이외의 윈도우를 선택해 주세요
- 선택한 윈도우 "{0}"는 프로세스를 특정할 수 없어 캡처할 수 없습니다.
-모니터는 지원 대상 외입니다.
-
+ 선택한 윈도우 "{0}"는 프로세스를 특정할 수 없어 캡처할 수 없습니다.
설정 검증 오류
diff --git a/WindowTranslator/Properties/Resources.ms.resx b/WindowTranslator/Properties/Resources.ms.resx
index ad7beea7..3b226c65 100644
--- a/WindowTranslator/Properties/Resources.ms.resx
+++ b/WindowTranslator/Properties/Resources.ms.resx
@@ -328,8 +328,7 @@
Please select a window other than WindowTranslator
- Tetingkap yang dipilih "{0}" tidak dapat menentukan proses dan tidak dapat ditangkap.
-Monitor tidak disokong.
+ Tetingkap yang dipilih "{0}" tidak dapat menentukan proses dan tidak dapat ditangkap.
Settings Validation Error
diff --git a/WindowTranslator/Properties/Resources.pt-BR.resx b/WindowTranslator/Properties/Resources.pt-BR.resx
index 7f4c96ea..d6298190 100644
--- a/WindowTranslator/Properties/Resources.pt-BR.resx
+++ b/WindowTranslator/Properties/Resources.pt-BR.resx
@@ -328,8 +328,7 @@
Please select a window other than WindowTranslator
- Jendela yang dipilih "{0}" tidak dapat menentukan proses dan tidak dapat ditangkap.
-Monitor tidak didukung.
+ A janela selecionada "{0}" não pode determinar o processo e não pode ser capturada.
Settings Validation Error
diff --git a/WindowTranslator/Properties/Resources.resx b/WindowTranslator/Properties/Resources.resx
index d2821e8c..594e6ec5 100644
--- a/WindowTranslator/Properties/Resources.resx
+++ b/WindowTranslator/Properties/Resources.resx
@@ -386,9 +386,7 @@
WindowTranslator以外のウィンドウを選択してください
- 選択したウィンドウ「{0}」はプロセスを特定できないため、キャプチャー出来ません。
-モニターはサポート対象外です。
-
+ 選択したウィンドウ「{0}」はプロセスを特定できないため、キャプチャー出来ません。
設定検証エラー
diff --git a/WindowTranslator/Properties/Resources.vi.resx b/WindowTranslator/Properties/Resources.vi.resx
index ab80f219..a90c467e 100644
--- a/WindowTranslator/Properties/Resources.vi.resx
+++ b/WindowTranslator/Properties/Resources.vi.resx
@@ -386,9 +386,7 @@
Vui lòng chọn cửa sổ khác ngoài WindowTranslator
- Cửa sổ đã chọn "{0}" không thể xác định tiến trình và không thể chụp.
-Màn hình không được hỗ trợ.
-
+ Cửa sổ đã chọn "{0}" không thể xác định tiến trình và không thể chụp.
Lỗi xác thực cài đặt
diff --git a/WindowTranslator/Properties/Resources.zh-CN.resx b/WindowTranslator/Properties/Resources.zh-CN.resx
index 86580c98..82a763b8 100644
--- a/WindowTranslator/Properties/Resources.zh-CN.resx
+++ b/WindowTranslator/Properties/Resources.zh-CN.resx
@@ -386,9 +386,7 @@
请选择WindowTranslator以外的窗口
- 选择的窗口"{0}"无法确定进程,无法捕获。
-不支持显示器。
-
+ 选择的窗口"{0}"无法确定进程,无法捕获。
设置验证错误
diff --git a/WindowTranslator/Properties/Resources.zh-TW.resx b/WindowTranslator/Properties/Resources.zh-TW.resx
index af9f1f9f..996c933f 100644
--- a/WindowTranslator/Properties/Resources.zh-TW.resx
+++ b/WindowTranslator/Properties/Resources.zh-TW.resx
@@ -386,9 +386,7 @@
請選擇WindowTranslator以外的視窗
- 選擇的視窗「{0}」無法確定程序,無法擷取。
-不支援顯示器。
-
+ 選擇的視窗「{0}」無法確定程序,無法擷取。
設定驗證錯誤
diff --git a/WindowTranslator/Stores/ProcessInfoStore.cs b/WindowTranslator/Stores/ProcessInfoStore.cs
index a0ee548e..6b8c32b5 100644
--- a/WindowTranslator/Stores/ProcessInfoStore.cs
+++ b/WindowTranslator/Stores/ProcessInfoStore.cs
@@ -6,11 +6,13 @@ public sealed class ProcessInfoStore(IAutoTargetStore targetStore) : IProcessInf
public IntPtr MainWindowHandle { get; private set; }
public string Name { get; private set; } = string.Empty;
+ public bool IsMonitor { get; private set; }
public void SetTargetProcess(IntPtr mainWindowHandle, string name)
{
this.MainWindowHandle = mainWindowHandle;
this.Name = name;
+ this.IsMonitor = name.StartsWith("DISPLAY__", StringComparison.OrdinalIgnoreCase);
this.targetStore.AddTarget(mainWindowHandle, name);
}