125 lines
4.1 KiB
C#
125 lines
4.1 KiB
C#
using MBN_STOCK_WEBVIEW.Core.Playout;
|
|
using Microsoft.UI.Windowing;
|
|
using WinRT.Interop;
|
|
|
|
namespace MBN_STOCK_WEBVIEW;
|
|
|
|
public sealed partial class MainWindow
|
|
{
|
|
private AppWindow? _closeConfirmationAppWindow;
|
|
private int _closeConfirmationPending;
|
|
private int _closeConfirmed;
|
|
|
|
private void EnsureCloseConfirmationAttached()
|
|
{
|
|
if (_closeConfirmationAppWindow is not null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
var windowHandle = WindowNative.GetWindowHandle(this);
|
|
var windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(windowHandle);
|
|
var appWindow = AppWindow.GetFromWindowId(windowId);
|
|
appWindow.Closing += OnAppWindowClosing;
|
|
_closeConfirmationAppWindow = appWindow;
|
|
}
|
|
catch
|
|
{
|
|
// Root.Loaded retries the attachment after the native window has been created.
|
|
}
|
|
}
|
|
|
|
private async void OnAppWindowClosing(AppWindow sender, AppWindowClosingEventArgs args)
|
|
{
|
|
if (Volatile.Read(ref _closeConfirmed) != 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// AppWindow.Closing cannot be deferred. Cancel this close synchronously and issue a
|
|
// second, explicitly confirmed close only after the modal result is known.
|
|
args.Cancel = true;
|
|
if (Interlocked.CompareExchange(ref _closeConfirmationPending, 1, 0) != 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
if (Root.XamlRoot is null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var dialog = new ContentDialog
|
|
{
|
|
XamlRoot = Root.XamlRoot,
|
|
Title = "프로그램 종료",
|
|
Content = BuildCloseConfirmationMessage(),
|
|
PrimaryButtonText = "종료",
|
|
CloseButtonText = "취소",
|
|
DefaultButton = ContentDialogButton.Close
|
|
};
|
|
|
|
var result = await dialog.ShowAsync();
|
|
if (result != ContentDialogResult.Primary)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Interlocked.Exchange(ref _closeConfirmed, 1);
|
|
Close();
|
|
}
|
|
catch
|
|
{
|
|
// If the confirmation surface cannot be shown, fail closed and keep the app open.
|
|
}
|
|
finally
|
|
{
|
|
Interlocked.Exchange(ref _closeConfirmationPending, 0);
|
|
}
|
|
}
|
|
|
|
private string BuildCloseConfirmationMessage()
|
|
{
|
|
var status = _playoutEngine?.Status;
|
|
var hasPreparedScene = !string.IsNullOrWhiteSpace(status?.PreparedSceneName);
|
|
var hasOnAirScene = !string.IsNullOrWhiteSpace(status?.OnAirSceneName);
|
|
var commandPending = Volatile.Read(ref _playoutCommandInFlight) != 0 ||
|
|
status?.IsPlayCompletionPending == true;
|
|
var outcomeUnknown = Volatile.Read(ref _browserCorrelationQuarantined) != 0 ||
|
|
status?.State == PlayoutConnectionState.OutcomeUnknown;
|
|
|
|
if (outcomeUnknown)
|
|
{
|
|
return "송출 결과를 확정할 수 없는 상태입니다. PGM과 Network Monitoring을 확인한 뒤 종료하세요. " +
|
|
"종료 과정에서 TAKE OUT 또는 다른 송출 명령을 자동으로 반복하지 않습니다. 그래도 종료하시겠습니까?";
|
|
}
|
|
|
|
if (commandPending)
|
|
{
|
|
return "송출 명령 또는 callback 처리가 진행 중입니다. 결과를 확인한 뒤 종료하는 것이 안전합니다. " +
|
|
"그래도 종료하시겠습니까?";
|
|
}
|
|
|
|
if (hasOnAirScene || hasPreparedScene)
|
|
{
|
|
return "PREPARED 또는 PROGRAM 장면이 남아 있습니다. 필요한 경우 먼저 TAKE OUT을 실행하고 " +
|
|
"PGM을 확인하세요. 종료 과정에서는 TAKE OUT을 자동 실행하지 않습니다. 그래도 종료하시겠습니까?";
|
|
}
|
|
|
|
return "프로그램을 종료하시겠습니까?";
|
|
}
|
|
|
|
private void DetachCloseConfirmation()
|
|
{
|
|
var appWindow = Interlocked.Exchange(ref _closeConfirmationAppWindow, null);
|
|
if (appWindow is not null)
|
|
{
|
|
appWindow.Closing -= OnAppWindowClosing;
|
|
}
|
|
}
|
|
}
|