Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions WindowTranslator.Abstractions/Stores/IProcessInfoStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,9 @@ public interface IProcessInfoStore
/// 対象のプロセスの名前
/// </summary>
string Name { get; }

/// <summary>
/// 対象がモニター(ディスプレイ)かどうか
/// </summary>
bool IsMonitor { get; }
}
32 changes: 28 additions & 4 deletions WindowTranslator/Modules/Capture/WindowsGraphicsCapture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<WindowsGraphicsCapture> logger) : ICaptureModule, IDisposable
public sealed class WindowsGraphicsCapture(ILogger<WindowsGraphicsCapture> logger, IProcessInfoStore processInfo) : ICaptureModule, IDisposable
{
private readonly IDirect3DDevice device = Direct3D11Helper.GetOrCreateDevice()!;
private readonly SemaphoreSlim processing = new(1, 1);
private readonly ILogger<WindowsGraphicsCapture> 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);
Expand All @@ -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);
Expand Down Expand Up @@ -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("セッション再作成");
Expand Down
6 changes: 6 additions & 0 deletions WindowTranslator/Modules/Main/CaptureMainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<WINDOWINFO>() };
if (!GetWindowInfo((HWND)this.processInfo.MainWindowHandle, ref windowInfo))
{
Expand Down
75 changes: 72 additions & 3 deletions WindowTranslator/Modules/Main/OverlayMainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<WINDOWINFO>() };
if (!GetWindowInfo(new(this.processInfo.MainWindowHandle), ref windowInfo))
{
Expand Down Expand Up @@ -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<MONITORINFOEXW>();

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)
Expand Down
48 changes: 48 additions & 0 deletions WindowTranslator/Modules/Startup/StartupViewModel.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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<MONITORINFOEXW>();

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);
}

Expand Down
3 changes: 3 additions & 0 deletions WindowTranslator/NativeMethods.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ GetWindowTextLength
GetWindowThreadProcessId
GetClassName
DwmGetWindowAttribute
EnumDisplayMonitors
GetMonitorInfo
MonitorFromPoint


// WindowMonitor
Expand Down
4 changes: 1 addition & 3 deletions WindowTranslator/Properties/Resources.de.resx
Original file line number Diff line number Diff line change
Expand Up @@ -386,9 +386,7 @@
<value>Bitte wählen Sie ein anderes Fenster als WindowTranslator aus</value>
</data>
<data name="UnknownWindow" xml:space="preserve">
<value>Das ausgewählte Fenster "{0}" kann den Prozess nicht bestimmen und kann nicht erfasst werden.
Monitore werden nicht unterstützt.
</value>
<value>Das ausgewählte Fenster "{0}" kann den Prozess nicht bestimmen und kann nicht erfasst werden.</value>
</data>
<data name="SettingsInvalid" xml:space="preserve">
<value>Einstellungsvalidierungsfehler</value>
Expand Down
4 changes: 1 addition & 3 deletions WindowTranslator/Properties/Resources.en.resx
Original file line number Diff line number Diff line change
Expand Up @@ -386,9 +386,7 @@
<value>Please select a window other than WindowTranslator</value>
</data>
<data name="UnknownWindow" xml:space="preserve">
<value>The selected window "{0}" cannot determine the process and cannot be captured.
Monitors are not supported.
</value>
<value>The selected window "{0}" cannot determine the process and cannot be captured.</value>
</data>
<data name="SettingsInvalid" xml:space="preserve">
<value>Settings Validation Error</value>
Expand Down
3 changes: 1 addition & 2 deletions WindowTranslator/Properties/Resources.hi.resx
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,7 @@
<value>कृपया WindowTranslator के अलावा कोई अन्य विंडो चुनें</value>
</data>
<data name="UnknownWindow" xml:space="preserve">
<value>चयनित विंडो "{0}" प्रक्रिया निर्धारित नहीं कर सकती और कैप्चर नहीं की जा सकती।
मॉनिटर समर्थित नहीं हैं।</value>
<value>चयनित विंडो "{0}" प्रक्रिया निर्धारित नहीं कर सकती और कैप्चर नहीं की जा सकती।</value>
</data>
<data name="SettingsInvalid" xml:space="preserve">
<value>सेटिंग्स सत्यापन त्रुटि</value>
Expand Down
3 changes: 1 addition & 2 deletions WindowTranslator/Properties/Resources.id.resx
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,7 @@
<value>Please select a window other than WindowTranslator</value>
</data>
<data name="UnknownWindow" xml:space="preserve">
<value>Jendela yang dipilih "{0}" tidak dapat menentukan proses dan tidak dapat ditangkap.
Monitor tidak didukung.</value>
<value>Jendela yang dipilih "{0}" tidak dapat menentukan proses dan tidak dapat ditangkap.</value>
</data>
<data name="SettingsInvalid" xml:space="preserve">
<value>Settings Validation Error</value>
Expand Down
4 changes: 1 addition & 3 deletions WindowTranslator/Properties/Resources.ko.resx
Original file line number Diff line number Diff line change
Expand Up @@ -386,9 +386,7 @@
<value>WindowTranslator 이외의 윈도우를 선택해 주세요</value>
</data>
<data name="UnknownWindow" xml:space="preserve">
<value>선택한 윈도우 "{0}"는 프로세스를 특정할 수 없어 캡처할 수 없습니다.
모니터는 지원 대상 외입니다.
</value>
<value>선택한 윈도우 "{0}"는 프로세스를 특정할 수 없어 캡처할 수 없습니다.</value>
</data>
<data name="SettingsInvalid" xml:space="preserve">
<value>설정 검증 오류</value>
Expand Down
3 changes: 1 addition & 2 deletions WindowTranslator/Properties/Resources.ms.resx
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,7 @@
<value>Please select a window other than WindowTranslator</value>
</data>
<data name="UnknownWindow" xml:space="preserve">
<value>Tetingkap yang dipilih "{0}" tidak dapat menentukan proses dan tidak dapat ditangkap.
Monitor tidak disokong.</value>
<value>Tetingkap yang dipilih "{0}" tidak dapat menentukan proses dan tidak dapat ditangkap.</value>
</data>
<data name="SettingsInvalid" xml:space="preserve">
<value>Settings Validation Error</value>
Expand Down
3 changes: 1 addition & 2 deletions WindowTranslator/Properties/Resources.pt-BR.resx
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,7 @@
<value>Please select a window other than WindowTranslator</value>
</data>
<data name="UnknownWindow" xml:space="preserve">
<value>Jendela yang dipilih "{0}" tidak dapat menentukan proses dan tidak dapat ditangkap.
Monitor tidak didukung.</value>
<value>A janela selecionada "{0}" não pode determinar o processo e não pode ser capturada.</value>
</data>
<data name="SettingsInvalid" xml:space="preserve">
<value>Settings Validation Error</value>
Expand Down
4 changes: 1 addition & 3 deletions WindowTranslator/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -386,9 +386,7 @@
<value>WindowTranslator以外のウィンドウを選択してください</value>
</data>
<data name="UnknownWindow" xml:space="preserve">
<value>選択したウィンドウ「{0}」はプロセスを特定できないため、キャプチャー出来ません。
モニターはサポート対象外です。
</value>
<value>選択したウィンドウ「{0}」はプロセスを特定できないため、キャプチャー出来ません。</value>
</data>
<data name="SettingsInvalid" xml:space="preserve">
<value>設定検証エラー</value>
Expand Down
4 changes: 1 addition & 3 deletions WindowTranslator/Properties/Resources.vi.resx
Original file line number Diff line number Diff line change
Expand Up @@ -386,9 +386,7 @@
<value>Vui lòng chọn cửa sổ khác ngoài WindowTranslator</value>
</data>
<data name="UnknownWindow" xml:space="preserve">
<value>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ợ.
</value>
<value>Cửa sổ đã chọn "{0}" không thể xác định tiến trình và không thể chụp.</value>
</data>
<data name="SettingsInvalid" xml:space="preserve">
<value>Lỗi xác thực cài đặt</value>
Expand Down
4 changes: 1 addition & 3 deletions WindowTranslator/Properties/Resources.zh-CN.resx
Original file line number Diff line number Diff line change
Expand Up @@ -386,9 +386,7 @@
<value>请选择WindowTranslator以外的窗口</value>
</data>
<data name="UnknownWindow" xml:space="preserve">
<value>选择的窗口"{0}"无法确定进程,无法捕获。
不支持显示器。
</value>
<value>选择的窗口"{0}"无法确定进程,无法捕获。</value>
</data>
<data name="SettingsInvalid" xml:space="preserve">
<value>设置验证错误</value>
Expand Down
4 changes: 1 addition & 3 deletions WindowTranslator/Properties/Resources.zh-TW.resx
Original file line number Diff line number Diff line change
Expand Up @@ -386,9 +386,7 @@
<value>請選擇WindowTranslator以外的視窗</value>
</data>
<data name="UnknownWindow" xml:space="preserve">
<value>選擇的視窗「{0}」無法確定程序,無法擷取。
不支援顯示器。
</value>
<value>選擇的視窗「{0}」無法確定程序,無法擷取。</value>
</data>
<data name="SettingsInvalid" xml:space="preserve">
<value>設定驗證錯誤</value>
Expand Down
Loading