mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-15 05:04:15 +01:00
Show logs on loading dialog
This commit is contained in:
parent
9db4e2f3a1
commit
877906ae15
4 changed files with 81 additions and 28 deletions
|
|
@ -7,6 +7,7 @@ using System.Threading.Tasks;
|
|||
|
||||
using Dalamud.Common;
|
||||
using Dalamud.Configuration.Internal;
|
||||
using Dalamud.Interface.Internal.Windows;
|
||||
using Dalamud.Logging.Internal;
|
||||
using Dalamud.Logging.Retention;
|
||||
using Dalamud.Plugin.Internal;
|
||||
|
|
@ -232,6 +233,10 @@ public sealed class EntryPoint
|
|||
|
||||
private static void SerilogOnLogLine(object? sender, (string Line, LogEvent LogEvent) ev)
|
||||
{
|
||||
if (!LoadingDialog.IsGloballyHidden)
|
||||
LoadingDialog.NewLogEntries.Enqueue(ev);
|
||||
ConsoleWindow.NewLogEntries.Enqueue(ev);
|
||||
|
||||
if (ev.LogEvent.Exception == null)
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ using Dalamud.Interface.ImGuiNotification.Internal;
|
|||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using Dalamud.Interface.Windowing;
|
||||
using Dalamud.Logging.Internal;
|
||||
using Dalamud.Plugin.Internal;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Dalamud.Utility;
|
||||
|
|
@ -39,9 +38,6 @@ internal class ConsoleWindow : Window, IDisposable
|
|||
private const int LogLinesMaximum = 1000000;
|
||||
private const int HistorySize = 50;
|
||||
|
||||
// Only this field may be touched from any thread.
|
||||
private readonly ConcurrentQueue<(string Line, LogEvent LogEvent)> newLogEntries;
|
||||
|
||||
// Fields below should be touched only from the main thread.
|
||||
private readonly RollingList<LogEntry> logText;
|
||||
private readonly RollingList<LogEntry> filteredLogEntries;
|
||||
|
|
@ -94,7 +90,6 @@ internal class ConsoleWindow : Window, IDisposable
|
|||
|
||||
this.autoScroll = configuration.LogAutoScroll;
|
||||
this.autoOpen = configuration.LogOpenAtStartup;
|
||||
SerilogEventSink.Instance.LogLine += this.OnLogLine;
|
||||
|
||||
Service<Framework>.GetAsync().ContinueWith(r => r.Result.Update += this.FrameworkOnUpdate);
|
||||
|
||||
|
|
@ -114,7 +109,6 @@ internal class ConsoleWindow : Window, IDisposable
|
|||
this.logLinesLimit = configuration.LogLinesLimit;
|
||||
|
||||
var limit = Math.Max(LogLinesMinimum, this.logLinesLimit);
|
||||
this.newLogEntries = new();
|
||||
this.logText = new(limit);
|
||||
this.filteredLogEntries = new(limit);
|
||||
|
||||
|
|
@ -126,6 +120,9 @@ internal class ConsoleWindow : Window, IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>Gets the queue where log entries that are not processed yet are stored.</summary>
|
||||
public static ConcurrentQueue<(string Line, LogEvent LogEvent)> NewLogEntries { get; } = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnOpen()
|
||||
{
|
||||
|
|
@ -136,7 +133,6 @@ internal class ConsoleWindow : Window, IDisposable
|
|||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
SerilogEventSink.Instance.LogLine -= this.OnLogLine;
|
||||
this.configuration.DalamudConfigurationSaved -= this.OnDalamudConfigurationSaved;
|
||||
if (Service<Framework>.GetNullable() is { } framework)
|
||||
framework.Update -= this.FrameworkOnUpdate;
|
||||
|
|
@ -324,7 +320,7 @@ internal class ConsoleWindow : Window, IDisposable
|
|||
ImGuiInputTextFlags.CallbackHistory | ImGuiInputTextFlags.CallbackEdit,
|
||||
this.CommandInputCallback))
|
||||
{
|
||||
this.newLogEntries.Enqueue((this.commandText, new LogEvent(DateTimeOffset.Now, LogEventLevel.Information, null, new MessageTemplate(string.Empty, []), [])));
|
||||
NewLogEntries.Enqueue((this.commandText, new LogEvent(DateTimeOffset.Now, LogEventLevel.Information, null, new MessageTemplate(string.Empty, []), [])));
|
||||
this.ProcessCommand();
|
||||
getFocus = true;
|
||||
}
|
||||
|
|
@ -372,7 +368,7 @@ internal class ConsoleWindow : Window, IDisposable
|
|||
this.pendingClearLog = false;
|
||||
this.logText.Clear();
|
||||
this.filteredLogEntries.Clear();
|
||||
this.newLogEntries.Clear();
|
||||
NewLogEntries.Clear();
|
||||
}
|
||||
|
||||
if (this.pendingRefilter)
|
||||
|
|
@ -388,7 +384,7 @@ internal class ConsoleWindow : Window, IDisposable
|
|||
|
||||
var numPrevFilteredLogEntries = this.filteredLogEntries.Count;
|
||||
var addedLines = 0;
|
||||
while (this.newLogEntries.TryDequeue(out var logLine))
|
||||
while (NewLogEntries.TryDequeue(out var logLine))
|
||||
addedLines += this.HandleLogLine(logLine.Line, logLine.LogEvent);
|
||||
this.newRolledLines = addedLines - (this.filteredLogEntries.Count - numPrevFilteredLogEntries);
|
||||
}
|
||||
|
|
@ -1062,11 +1058,6 @@ internal class ConsoleWindow : Window, IDisposable
|
|||
/// <summary>Queues filtering the log entries again, before next call to <see cref="Draw"/>.</summary>
|
||||
private void QueueRefilter() => this.pendingRefilter = true;
|
||||
|
||||
/// <summary>Enqueues the new log line to the log-to-be-processed queue.</summary>
|
||||
/// <remarks>See <see cref="FrameworkOnUpdate"/> for the handler for the queued log entries.</remarks>
|
||||
private void OnLogLine(object sender, (string Line, LogEvent LogEvent) logEvent) =>
|
||||
this.newLogEntries.Enqueue(logEvent);
|
||||
|
||||
private bool DrawToggleButtonWithTooltip(
|
||||
string buttonId, string tooltip, FontAwesomeIcon icon, ref bool enabledState)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.ComponentModel;
|
||||
using System.Collections.Concurrent;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
|
|
@ -14,6 +15,7 @@ using Dalamud.Plugin.Internal;
|
|||
using Dalamud.Utility;
|
||||
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
|
|
@ -31,7 +33,7 @@ namespace Dalamud;
|
|||
Justification = "Multiple fixed blocks")]
|
||||
internal sealed unsafe class LoadingDialog
|
||||
{
|
||||
private static int wasGloballyHidden;
|
||||
private readonly RollingList<string> logs = new(20);
|
||||
|
||||
private Thread? thread;
|
||||
private HWND hwndTaskDialog;
|
||||
|
|
@ -60,6 +62,13 @@ internal sealed unsafe class LoadingDialog
|
|||
AutoUpdatePlugins,
|
||||
}
|
||||
|
||||
/// <summary>Gets the queue where log entries that are not processed yet are stored.</summary>
|
||||
public static ConcurrentQueue<(string Line, LogEvent LogEvent)> NewLogEntries { get; } = new();
|
||||
|
||||
/// <summary>Gets a value indicating whether the initial Dalamud loading dialog will not show again until next
|
||||
/// game restart.</summary>
|
||||
public static bool IsGloballyHidden { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current state of the dialog.
|
||||
/// </summary>
|
||||
|
|
@ -98,7 +107,7 @@ internal sealed unsafe class LoadingDialog
|
|||
/// </summary>
|
||||
public void Show()
|
||||
{
|
||||
if (Volatile.Read(ref wasGloballyHidden) == 1)
|
||||
if (IsGloballyHidden)
|
||||
return;
|
||||
|
||||
if (this.thread?.IsAlive == true)
|
||||
|
|
@ -119,6 +128,7 @@ internal sealed unsafe class LoadingDialog
|
|||
/// </summary>
|
||||
public void HideAndJoin()
|
||||
{
|
||||
IsGloballyHidden = true;
|
||||
if (this.thread?.IsAlive is not true)
|
||||
return;
|
||||
|
||||
|
|
@ -210,6 +220,42 @@ internal sealed unsafe class LoadingDialog
|
|||
}
|
||||
}
|
||||
|
||||
private void UpdateExpandedInformation()
|
||||
{
|
||||
const int maxCharactersPerLine = 80;
|
||||
|
||||
if (NewLogEntries.IsEmpty)
|
||||
return;
|
||||
while (NewLogEntries.TryDequeue(out var e))
|
||||
{
|
||||
var t = e.Line.AsSpan();
|
||||
while (!t.IsEmpty)
|
||||
{
|
||||
var i = t.IndexOfAny('\r', '\n');
|
||||
var line = i == -1 ? t : t[..i];
|
||||
t = i == -1 ? ReadOnlySpan<char>.Empty : t[(i + 1)..];
|
||||
if (line.IsEmpty)
|
||||
continue;
|
||||
|
||||
this.logs.Add(
|
||||
line.Length < maxCharactersPerLine ? line.ToString() : $"{line[..(maxCharactersPerLine - 3)]}...");
|
||||
}
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
foreach (var l in this.logs)
|
||||
sb.AppendLine(l);
|
||||
|
||||
fixed (void* pszText = sb.ToString())
|
||||
{
|
||||
SendMessageW(
|
||||
this.hwndTaskDialog,
|
||||
(uint)TASKDIALOG_MESSAGES.TDM_SET_ELEMENT_TEXT,
|
||||
(WPARAM)(int)TASKDIALOG_ELEMENTS.TDE_EXPANDED_INFORMATION,
|
||||
(LPARAM)pszText);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateButtonEnabled()
|
||||
{
|
||||
if (this.hwndTaskDialog == default)
|
||||
|
|
@ -227,6 +273,7 @@ internal sealed unsafe class LoadingDialog
|
|||
|
||||
this.UpdateMainInstructionText();
|
||||
this.UpdateContentText();
|
||||
this.UpdateExpandedInformation();
|
||||
this.UpdateButtonEnabled();
|
||||
SendMessageW(hwnd, (int)TASKDIALOG_MESSAGES.TDM_SET_PROGRESS_BAR_MARQUEE, 1, 0);
|
||||
|
||||
|
|
@ -245,6 +292,7 @@ internal sealed unsafe class LoadingDialog
|
|||
|
||||
case TASKDIALOG_NOTIFICATIONS.TDN_TIMER:
|
||||
this.UpdateContentText();
|
||||
this.UpdateExpandedInformation();
|
||||
return S.S_OK;
|
||||
}
|
||||
|
||||
|
|
@ -260,10 +308,13 @@ internal sealed unsafe class LoadingDialog
|
|||
? null
|
||||
: Icon.ExtractAssociatedIcon(Path.Combine(workingDirectory, "Dalamud.Injector.exe"));
|
||||
|
||||
fixed (void* pszEmpty = "-")
|
||||
fixed (void* pszWindowTitle = "Dalamud")
|
||||
fixed (void* pszHide = Loc.Localize("LoadingDialogHide", "Hide"))
|
||||
fixed (void* pszThemesManifestResourceName = "RT_MANIFEST_THEMES")
|
||||
fixed (void* pszDalamudBoot = "Dalamud.Boot.dll")
|
||||
fixed (void* pszThemesManifestResourceName = "RT_MANIFEST_THEMES")
|
||||
fixed (void* pszHide = Loc.Localize("LoadingDialogHide", "Hide"))
|
||||
fixed (void* pszShowLatestLogs = Loc.Localize("LoadingDialogShowLatestLogs", "Show Latest Logs"))
|
||||
fixed (void* pszHideLatestLogs = Loc.Localize("LoadingDialogHideLatestLogs", "Hide Latest Logs"))
|
||||
{
|
||||
var taskDialogButton = new TASKDIALOG_BUTTON
|
||||
{
|
||||
|
|
@ -277,6 +328,7 @@ internal sealed unsafe class LoadingDialog
|
|||
hInstance = (HINSTANCE)Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().ManifestModule),
|
||||
dwFlags = (int)TDF_CAN_BE_MINIMIZED |
|
||||
(int)TDF_SHOW_MARQUEE_PROGRESS_BAR |
|
||||
(int)TDF_EXPAND_FOOTER_AREA |
|
||||
(int)TDF_CALLBACK_TIMER |
|
||||
(extractedIcon is null ? 0 : (int)TDF_USE_HICON_MAIN),
|
||||
dwCommonButtons = 0,
|
||||
|
|
@ -291,14 +343,14 @@ internal sealed unsafe class LoadingDialog
|
|||
pRadioButtons = null,
|
||||
nDefaultRadioButton = 0,
|
||||
pszVerificationText = null,
|
||||
pszExpandedInformation = null,
|
||||
pszExpandedControlText = null,
|
||||
pszCollapsedControlText = null,
|
||||
pszExpandedInformation = (ushort*)pszEmpty,
|
||||
pszExpandedControlText = (ushort*)pszShowLatestLogs,
|
||||
pszCollapsedControlText = (ushort*)pszHideLatestLogs,
|
||||
pszFooterIcon = null,
|
||||
pszFooter = null,
|
||||
pfCallback = &HResultFuncBinder,
|
||||
lpCallbackData = 0,
|
||||
cxWidth = 0,
|
||||
cxWidth = 360,
|
||||
};
|
||||
|
||||
HANDLE hActCtx = default;
|
||||
|
|
@ -338,7 +390,7 @@ internal sealed unsafe class LoadingDialog
|
|||
}
|
||||
}
|
||||
|
||||
Interlocked.Exchange(ref wasGloballyHidden, 1);
|
||||
IsGloballyHidden = true;
|
||||
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -250,19 +250,20 @@ internal static class ServiceManager
|
|||
try
|
||||
{
|
||||
// Wait for all blocking constructors to complete first.
|
||||
await WaitWithTimeoutConsent(blockingEarlyLoadingServices.Select(x => getAsyncTaskMap[x]),
|
||||
await WaitWithTimeoutConsent(
|
||||
blockingEarlyLoadingServices.Select(x => getAsyncTaskMap[x]),
|
||||
LoadingDialog.State.LoadingDalamud);
|
||||
|
||||
// All the BlockingEarlyLoadedService constructors have been run,
|
||||
// and blockerTasks now will not change. Now wait for them.
|
||||
// Note that ServiceManager.CallWhenServicesReady does not get to register a blocker.
|
||||
await WaitWithTimeoutConsent(blockerTasks,
|
||||
await WaitWithTimeoutConsent(
|
||||
blockerTasks,
|
||||
LoadingDialog.State.LoadingPlugins);
|
||||
|
||||
Log.Verbose("=============== BLOCKINGSERVICES & TASKS INITIALIZED ===============");
|
||||
Timings.Event("BlockingServices Initialized");
|
||||
BlockingServicesLoadedTaskCompletionSource.SetResult();
|
||||
loadingDialog.HideAndJoin();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
@ -277,6 +278,10 @@ internal static class ServiceManager
|
|||
|
||||
Log.Error(e, "Failed resolving blocking services");
|
||||
}
|
||||
finally
|
||||
{
|
||||
loadingDialog.HideAndJoin();
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue