Repository navigation
Expand file tree
/
Copy pathProgram.cs
More file actions
132 lines (115 loc) · 4.6 KB
/
Copy pathProgram.cs
File metadata and controls
132 lines (115 loc) · 4.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
using System;
using System.IO;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Avalonia.Win32;
using NullWave.Views;
using NullWave.Helpers;
using NullWave.Helpers.Diagnostics;
using NullWave.Helpers.Logging;
using NullWave.Services;
using Serilog;
using Velopack;
namespace NullWave;
class Program
{
private static FileStream? _singleInstanceLock;
[STAThread]
public static void Main(string[] args)
{
StartupTimeline.Mark("Main entered");
VelopackApp.Build().Run();
NullWavePaths.EnsureDirectories();
// Restrict data directory permissions on Linux (owner-only access)
if (OperatingSystem.IsLinux())
{
try
{
File.SetUnixFileMode(NullWavePaths.DataDir,
UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute);
Log.Debug("[Program] Set restrictive permissions on data directory");
}
catch (Exception ex)
{
Log.Warning(ex, "[Program] Could not set restrictive permissions on data directory");
}
}
if (!TryAcquireSingleInstanceLock())
{
Console.WriteLine("NullWave is already running.");
return;
}
NullWaveLogConfig.Initialize(PreferencesService.Shared.Current.VerboseLogging);
try
{
var appBuilder = BuildAvaloniaApp();
appBuilder.StartWithClassicDesktopLifetime(args);
}
catch (Exception ex)
{
// Catch synchronous startup crashes (like MainViewModel constructor failures)
NullActionLogger.Error("Program", ex, "Unhandled top-level exception");
CrashHandler.HandleFatalCrash(ex);
}
finally
{
NullWaveLogConfig.CloseAndFlush();
}
}
private static bool TryAcquireSingleInstanceLock()
{
try
{
_singleInstanceLock = new FileStream(
Path.Combine(NullWavePaths.DataDir, "single.instance.lock"),
FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
return true;
}
catch (IOException)
{
return false;
}
}
public static AppBuilder BuildAvaloniaApp()
{
var builder = AppBuilder.Configure<App>()
.UsePlatformDetect()
.WithInterFont()
.LogToTrace();
if (OperatingSystem.IsWindows())
{
// NULLWAVE_RENDER=software -> CPU rendering for GPU-less floor machines (i3 380M).
var forceSoftware = string.Equals(
Environment.GetEnvironmentVariable("NULLWAVE_RENDER"), "software",
StringComparison.OrdinalIgnoreCase);
// Compose mode: lowlatency (default) correctly V-Syncs at display refresh rate.
// direct bypasses V-Sync entirely and produces uncapped render storms (confirmed
// 40,000+ fps CPU churn) - do not use as a "smoother" alternative, it's the opposite.
var compose = (Environment.GetEnvironmentVariable("NULLWAVE_COMPOSE") ?? "lowlatency").Trim().ToLowerInvariant();
if (forceSoftware)
{
builder.With(new Win32PlatformOptions
{
RenderingMode = new[] { Win32RenderingMode.Software }
});
}
else
{
Win32CompositionMode[] composition = compose switch
{
"direct" => new[] { Win32CompositionMode.DirectComposition, Win32CompositionMode.WinUIComposition, Win32CompositionMode.LowLatencyDxgiSwapChain },
"winui" => new[] { Win32CompositionMode.WinUIComposition, Win32CompositionMode.DirectComposition, Win32CompositionMode.LowLatencyDxgiSwapChain },
_ => new[] { Win32CompositionMode.LowLatencyDxgiSwapChain, Win32CompositionMode.DirectComposition, Win32CompositionMode.WinUIComposition },
};
builder.With(new Win32PlatformOptions
{
RenderingMode = new[] { Win32RenderingMode.AngleEgl, Win32RenderingMode.Wgl, Win32RenderingMode.Software },
CompositionMode = composition
});
}
Log.Information("[STARTUP] Render config: software={Software}, compose={Compose}", forceSoftware, compose);
}
return builder;
}
}