Merge remote-tracking branch 'upstream/master' into feature/inotificationmanager

This commit is contained in:
Soreepeong 2024-03-14 13:06:04 +09:00
commit 033a57d19d
51 changed files with 3594 additions and 1284 deletions

View file

@ -44,6 +44,8 @@ internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable
private bool useBold;
private bool useMinimumBuild;
private SingleFontChooserDialog? chooserDialog;
/// <inheritdoc/>
public string[]? CommandShortcuts { get; init; }
@ -126,32 +128,75 @@ internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable
if (ImGui.Button("Test Lock"))
Task.Run(this.TestLock);
ImGui.SameLine();
if (ImGui.Button("Choose Editor Font"))
{
var fcd = new SingleFontChooserDialog(
Service<FontAtlasFactory>.Get().CreateFontAtlas(
$"{nameof(GamePrebakedFontsTestWidget)}:EditorFont",
FontAtlasAutoRebuildMode.Async));
fcd.SelectedFont = this.fontSpec;
fcd.IgnorePreviewGlobalScale = !this.atlasScaleMode;
Service<InterfaceManager>.Get().Draw += fcd.Draw;
fcd.ResultTask.ContinueWith(
r => Service<Framework>.Get().RunOnFrameworkThread(
() =>
{
Service<InterfaceManager>.Get().Draw -= fcd.Draw;
fcd.Dispose();
if (this.chooserDialog is null)
{
DoNext();
}
else
{
this.chooserDialog.Cancel();
this.chooserDialog.ResultTask.ContinueWith(_ => Service<Framework>.Get().RunOnFrameworkThread(DoNext));
this.chooserDialog = null;
}
_ = r.Exception;
if (!r.IsCompletedSuccessfully)
return;
void DoNext()
{
var fcd = new SingleFontChooserDialog(
Service<FontAtlasFactory>.Get(),
$"{nameof(GamePrebakedFontsTestWidget)}:EditorFont");
this.chooserDialog = fcd;
fcd.SelectedFont = this.fontSpec;
fcd.IgnorePreviewGlobalScale = !this.atlasScaleMode;
fcd.IsModal = false;
Service<InterfaceManager>.Get().Draw += fcd.Draw;
var prevSpec = this.fontSpec;
fcd.SelectedFontSpecChanged += spec =>
{
this.fontSpec = spec;
Log.Information("Selected font: {font}", this.fontSpec);
this.fontDialogHandle?.Dispose();
this.fontDialogHandle = null;
};
fcd.ResultTask.ContinueWith(
r => Service<Framework>.Get().RunOnFrameworkThread(
() =>
{
Service<InterfaceManager>.Get().Draw -= fcd.Draw;
fcd.Dispose();
this.fontSpec = r.Result;
Log.Information("Selected font: {font}", this.fontSpec);
this.fontDialogHandle?.Dispose();
this.fontDialogHandle = null;
}));
_ = r.Exception;
var spec = r.IsCompletedSuccessfully ? r.Result : prevSpec;
if (this.fontSpec != spec)
{
this.fontSpec = spec;
this.fontDialogHandle?.Dispose();
this.fontDialogHandle = null;
}
this.chooserDialog = null;
}));
}
}
if (this.chooserDialog is not null)
{
ImGui.SameLine();
ImGui.TextUnformatted($"{this.chooserDialog.PopupPosition}, {this.chooserDialog.PopupSize}");
ImGui.SameLine();
if (ImGui.Button("Random Location"))
{
var monitors = ImGui.GetPlatformIO().Monitors;
var monitor = monitors[Random.Shared.Next() % monitors.Size];
this.chooserDialog.PopupPosition = monitor.WorkPos + (monitor.WorkSize * new Vector2(
Random.Shared.NextSingle(),
Random.Shared.NextSingle()));
this.chooserDialog.PopupSize = monitor.WorkSize * new Vector2(
Random.Shared.NextSingle(),
Random.Shared.NextSingle());
}
}
this.privateAtlas ??=

View file

@ -1,13 +1,22 @@
// ReSharper disable MethodSupportsCancellation // Using alternative method of cancelling tasks by throwing exceptions.
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Dalamud.Game;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
using Dalamud.Interface.ImGuiFileDialog;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Logging.Internal;
using Dalamud.Utility;
using ImGuiNET;
using Serilog;
@ -18,6 +27,12 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
/// </summary>
internal class TaskSchedulerWidget : IDataWindowWidget
{
private readonly FileDialogManager fileDialogManager = new();
private readonly byte[] urlBytes = new byte[2048];
private readonly byte[] localPathBytes = new byte[2048];
private Task? downloadTask = null;
private (long Downloaded, long Total, float Percentage) downloadState;
private CancellationTokenSource taskSchedulerCancelSource = new();
/// <inheritdoc/>
@ -33,11 +48,16 @@ internal class TaskSchedulerWidget : IDataWindowWidget
public void Load()
{
this.Ready = true;
Encoding.UTF8.GetBytes(
"https://geo.mirror.pkgbuild.com/iso/2024.01.01/archlinux-2024.01.01-x86_64.iso",
this.urlBytes);
}
/// <inheritdoc/>
public void Draw()
{
var framework = Service<Framework>.Get();
if (ImGui.Button("Clear list"))
{
TaskTracker.Clear();
@ -84,8 +104,7 @@ internal class TaskSchedulerWidget : IDataWindowWidget
{
Thread.Sleep(200);
string a = null;
a.Contains("dalamud"); // Intentional null exception.
_ = ((string)null)!.Contains("dalamud"); // Intentional null exception.
});
}
@ -94,36 +113,156 @@ internal class TaskSchedulerWidget : IDataWindowWidget
if (ImGui.Button("ASAP"))
{
Task.Run(async () => await Service<Framework>.Get().RunOnTick(() => { }, cancellationToken: this.taskSchedulerCancelSource.Token));
_ = framework.RunOnTick(() => Log.Information("Framework.Update - ASAP"), cancellationToken: this.taskSchedulerCancelSource.Token);
}
ImGui.SameLine();
if (ImGui.Button("In 1s"))
{
Task.Run(async () => await Service<Framework>.Get().RunOnTick(() => { }, cancellationToken: this.taskSchedulerCancelSource.Token, delay: TimeSpan.FromSeconds(1)));
_ = framework.RunOnTick(() => Log.Information("Framework.Update - In 1s"), cancellationToken: this.taskSchedulerCancelSource.Token, delay: TimeSpan.FromSeconds(1));
}
ImGui.SameLine();
if (ImGui.Button("In 60f"))
{
Task.Run(async () => await Service<Framework>.Get().RunOnTick(() => { }, cancellationToken: this.taskSchedulerCancelSource.Token, delayTicks: 60));
_ = framework.RunOnTick(() => Log.Information("Framework.Update - In 60f"), cancellationToken: this.taskSchedulerCancelSource.Token, delayTicks: 60);
}
ImGui.SameLine();
if (ImGui.Button("In 1s+120f"))
{
_ = framework.RunOnTick(() => Log.Information("Framework.Update - In 1s+120f"), cancellationToken: this.taskSchedulerCancelSource.Token, delay: TimeSpan.FromSeconds(1), delayTicks: 120);
}
ImGui.SameLine();
if (ImGui.Button("In 2s+60f"))
{
_ = framework.RunOnTick(() => Log.Information("Framework.Update - In 2s+60f"), cancellationToken: this.taskSchedulerCancelSource.Token, delay: TimeSpan.FromSeconds(2), delayTicks: 60);
}
ImGui.SameLine();
if (ImGui.Button("Every 60 frames"))
{
_ = framework.RunOnTick(
async () =>
{
for (var i = 0L; ; i++)
{
Log.Information($"Loop #{i}; MainThread={ThreadSafety.IsMainThread}");
await framework.DelayTicks(60, this.taskSchedulerCancelSource.Token);
}
},
cancellationToken: this.taskSchedulerCancelSource.Token);
}
ImGui.SameLine();
if (ImGui.Button("Error in 1s"))
{
Task.Run(async () => await Service<Framework>.Get().RunOnTick(() => throw new Exception("Test Exception"), cancellationToken: this.taskSchedulerCancelSource.Token, delay: TimeSpan.FromSeconds(1)));
_ = framework.RunOnTick(() => throw new Exception("Test Exception"), cancellationToken: this.taskSchedulerCancelSource.Token, delay: TimeSpan.FromSeconds(1));
}
ImGui.SameLine();
if (ImGui.Button("As long as it's in Framework Thread"))
{
Task.Run(async () => await Service<Framework>.Get().RunOnFrameworkThread(() => { Log.Information("Task dispatched from non-framework.update thread"); }));
Service<Framework>.Get().RunOnFrameworkThread(() => { Log.Information("Task dispatched from framework.update thread"); }).Wait();
Task.Run(async () => await framework.RunOnFrameworkThread(() => { Log.Information("Task dispatched from non-framework.update thread"); }));
framework.RunOnFrameworkThread(() => { Log.Information("Task dispatched from framework.update thread"); }).Wait();
}
if (ImGui.CollapsingHeader("Download"))
{
ImGui.InputText("URL", this.urlBytes, (uint)this.urlBytes.Length);
ImGui.InputText("Local Path", this.localPathBytes, (uint)this.localPathBytes.Length);
ImGui.SameLine();
if (ImGuiComponents.IconButton("##localpathpicker", FontAwesomeIcon.File))
{
var defaultFileName = Encoding.UTF8.GetString(this.urlBytes).Split('\0', 2)[0].Split('/').Last();
this.fileDialogManager.SaveFileDialog(
"Choose a local path",
"*",
defaultFileName,
string.Empty,
(accept, newPath) =>
{
if (accept)
{
this.localPathBytes.AsSpan().Clear();
Encoding.UTF8.GetBytes(newPath, this.localPathBytes.AsSpan());
}
});
}
ImGui.TextUnformatted($"{this.downloadState.Downloaded:##,###}/{this.downloadState.Total:##,###} ({this.downloadState.Percentage:0.00}%)");
using var disabled =
ImRaii.Disabled(this.downloadTask?.IsCompleted is false || this.localPathBytes[0] == 0);
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Download");
ImGui.SameLine();
var downloadUsingGlobalScheduler = ImGui.Button("using default scheduler");
ImGui.SameLine();
var downloadUsingFramework = ImGui.Button("using Framework.Update");
if (downloadUsingGlobalScheduler || downloadUsingFramework)
{
var url = Encoding.UTF8.GetString(this.urlBytes).Split('\0', 2)[0];
var localPath = Encoding.UTF8.GetString(this.localPathBytes).Split('\0', 2)[0];
var ct = this.taskSchedulerCancelSource.Token;
this.downloadState = default;
var factory = downloadUsingGlobalScheduler
? Task.Factory
: framework.FrameworkThreadTaskFactory;
this.downloadState = default;
this.downloadTask = factory.StartNew(
async () =>
{
try
{
await using var to = File.Create(localPath);
using var client = new HttpClient();
using var conn = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, ct);
this.downloadState.Total = conn.Content.Headers.ContentLength ?? -1L;
await using var from = conn.Content.ReadAsStream(ct);
var buffer = new byte[8192];
while (true)
{
if (downloadUsingFramework)
ThreadSafety.AssertMainThread();
if (downloadUsingGlobalScheduler)
ThreadSafety.AssertNotMainThread();
var len = await from.ReadAsync(buffer, ct);
if (len == 0)
break;
await to.WriteAsync(buffer.AsMemory(0, len), ct);
this.downloadState.Downloaded += len;
if (this.downloadState.Total >= 0)
{
this.downloadState.Percentage =
(100f * this.downloadState.Downloaded) / this.downloadState.Total;
}
}
}
catch (Exception e)
{
Log.Error(e, "Failed to download {from} to {to}.", url, localPath);
try
{
File.Delete(localPath);
}
catch
{
// ignore
}
}
},
cancellationToken: ct).Unwrap();
}
}
if (ImGui.Button("Drown in tasks"))
@ -244,6 +383,8 @@ internal class TaskSchedulerWidget : IDataWindowWidget
ImGui.PopStyleColor(1);
}
this.fileDialogManager.Draw();
}
private async Task TestTaskInTaskDelay(CancellationToken token)