Electron support for Linux (#1074)

* init

* SQLite changes

* Move html folder, edit build scripts

* AppApi interface

* Build flags

* AppApi inheritance

* Finishing touches

* Merge upstream changes

* Test CI

* Fix class inits

* Rename AppApi

* Merge upstream changes

* Fix SQLiteLegacy on Linux, Add Linux interop, build tools

* Linux specific localisation strings

* Make it run

* Bring back most of Linux functionality

* Clean up

* Fix TTS voices

* Fix UI var

* Changes

* Electron minimise to tray

* Remove separate toggle for WlxOverlay

* Fixes

* Touchups

* Move csproj

* Window zoom, Desktop Notifications, VR check on Linux

* Fix desktop notifications, VR check spam

* Fix building on Linux

* Clean up

* Fix WebApi headers

* Rewrite VRCX updater

* Clean up

* Linux updater

* Add Linux to build action

* init

* SQLite changes

* Move html folder, edit build scripts

* AppApi interface

* Build flags

* AppApi inheritance

* Finishing touches

* Merge upstream changes

* Test CI

* Fix class inits

* Rename AppApi

* Merge upstream changes

* Fix SQLiteLegacy on Linux, Add Linux interop, build tools

* Linux specific localisation strings

* Make it run

* Bring back most of Linux functionality

* Clean up

* Fix TTS voices

* Changes

* Electron minimise to tray

* Remove separate toggle for WlxOverlay

* Fixes

* Touchups

* Move csproj

* Window zoom, Desktop Notifications, VR check on Linux

* Fix desktop notifications, VR check spam

* Fix building on Linux

* Clean up

* Fix WebApi headers

* Rewrite VRCX updater

* Clean up

* Linux updater

* Add Linux to build action

* Test updater

* Rebase and handle merge conflicts

* Fix Linux updater

* Fix Linux app restart

* Fix friend order

* Handle AppImageInstaller, show an install message on Linux

* Updates to the AppImage installer

* Fix Linux updater, fix set version, check for .NET, copy wine prefix

* Handle random errors

* Rotate tall prints

* try fix Linux restart bug

* Final

---------

Co-authored-by: rs189 <35667100+rs189@users.noreply.github.com>
This commit is contained in:
Natsumi
2025-01-11 13:09:44 +13:00
committed by GitHub
parent a39eb9d5ed
commit 938fff63d0
223 changed files with 15841 additions and 9562 deletions

View File

@@ -8,7 +8,6 @@ using NLog;
using NLog.Targets;
using System;
using System.Data.SQLite;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Windows.Forms;
@@ -22,13 +21,17 @@ namespace VRCX
public static string ConfigLocation { get; private set; }
public static string Version { get; private set; }
public static bool LaunchDebug;
private static readonly NLog.Logger logger = NLog.LogManager.GetLogger("VRCX");
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
#if !LINUX
public static VRCXVRInterface VRCXVRInstance { get; private set; }
#endif
public static AppApi AppApiInstance { get; private set; }
private static void SetProgramDirectories()
{
if (string.IsNullOrEmpty(AppDataDirectory))
AppDataDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "VRCX");
AppDataDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"VRCX");
BaseDirectory = AppDomain.CurrentDomain.BaseDirectory;
ConfigLocation = Path.Combine(AppDataDirectory, "VRCX.sqlite3");
@@ -41,37 +44,56 @@ namespace VRCX
if (File.Exists(Path.Combine(BaseDirectory, "VRCX.json")))
{
File.Move(Path.Combine(BaseDirectory, "VRCX.json"), Path.Combine(AppDataDirectory, "VRCX.json"));
File.Copy(Path.Combine(AppDataDirectory, "VRCX.json"), Path.Combine(AppDataDirectory, "VRCX-backup.json"));
File.Copy(Path.Combine(AppDataDirectory, "VRCX.json"),
Path.Combine(AppDataDirectory, "VRCX-backup.json"));
}
if (File.Exists(Path.Combine(BaseDirectory, "VRCX.sqlite3")))
{
File.Move(Path.Combine(BaseDirectory, "VRCX.sqlite3"), Path.Combine(AppDataDirectory, "VRCX.sqlite3"));
File.Copy(Path.Combine(AppDataDirectory, "VRCX.sqlite3"), Path.Combine(AppDataDirectory, "VRCX-backup.sqlite3"));
File.Move(Path.Combine(BaseDirectory, "VRCX.sqlite3"),
Path.Combine(AppDataDirectory, "VRCX.sqlite3"));
File.Copy(Path.Combine(AppDataDirectory, "VRCX.sqlite3"),
Path.Combine(AppDataDirectory, "VRCX-backup.sqlite3"));
}
}
// Migrate cache to userdata for Cef 115 update
var oldCachePath = Path.Combine(AppDataDirectory, "cache");
if (Directory.Exists(oldCachePath))
var newCachePath = Path.Combine(AppDataDirectory, "userdata", "cache");
if (Directory.Exists(oldCachePath) && !Directory.Exists(newCachePath))
{
var newCachePath = Path.Combine(AppDataDirectory, "userdata", "cache");
if (Directory.Exists(newCachePath))
Directory.Delete(newCachePath, true);
Directory.CreateDirectory(Path.Combine(AppDataDirectory, "userdata"));
Directory.Move(oldCachePath, newCachePath);
}
}
private static void GetVersion()
{
var buildName = "VRCX";
try
{
Version = $"{buildName} {File.ReadAllText(Path.Combine(BaseDirectory, "Version"))}";
}
catch (Exception)
{
Version = $"{buildName} Build";
}
Version = Version.Replace("\r", "").Replace("\n", "");
}
private static void ConfigureLogger()
{
NLog.LogManager.Setup().LoadConfiguration(builder =>
LogManager.Setup().LoadConfiguration(builder =>
{
var fileTarget = new FileTarget("fileTarget")
{
FileName = Path.Combine(AppDataDirectory, "logs", "VRCX.log"),
//Layout = "${longdate} [${level:uppercase=true}] ${logger} - ${message} ${exception:format=tostring}",
// Layout with padding between the level/logger and message so that the message always starts at the same column
Layout = "${longdate} [${level:uppercase=true:padding=-5}] ${logger:padding=-20} - ${message} ${exception:format=tostring}",
Layout =
"${longdate} [${level:uppercase=true:padding=-5}] ${logger:padding=-20} - ${message} ${exception:format=tostring}",
ArchiveFileName = Path.Combine(AppDataDirectory, "logs", "VRCX.{#}.log"),
ArchiveNumbering = ArchiveNumberingMode.DateAndSequence,
ArchiveEvery = FileArchivePeriod.Day,
@@ -84,57 +106,68 @@ namespace VRCX
AutoFlush = true,
Encoding = System.Text.Encoding.UTF8
};
if (Program.LaunchDebug)
builder.ForLogger().FilterMinLevel(LogLevel.Debug).WriteTo(fileTarget);
var consoleTarget = new ConsoleTarget("consoleTarget")
{
builder.ForLogger().FilterMinLevel(LogLevel.Debug).WriteTo(fileTarget);
}
else
{
#if DEBUG
// Archive maximum of 3 files 10MB each, kept for a maximum of 7 days
builder.ForLogger().FilterMinLevel(LogLevel.Debug).WriteTo(fileTarget);
#else
builder.ForLogger().FilterMinLevel(LogLevel.Debug).WriteTo(fileTarget);
#endif
}
Layout = "${longdate} [${level:uppercase=true:padding=-5}] ${logger:padding=-20} - ${message} ${exception:format=tostring}",
DetectConsoleAvailable = true
};
builder.ForLogger("VRCX").FilterMinLevel(LogLevel.Info).WriteTo(consoleTarget);
});
}
#if !LINUX
[STAThread]
private static void Main()
{
if (Wine.GetIfWine())
{
MessageBox.Show(
"VRCX Cef has detected Wine.\nPlease switch to our native Electron build for Linux.",
"Wine Detected", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
try
{
Run();
}
#region Handle CEF Explosion
catch (FileNotFoundException e)
{
logger.Error(e, "Handled Exception, Missing file found in Handle Cef Explosion.");
var result = MessageBox.Show("VRCX has encountered an error with the CefSharp backend,\nthis is typically caused by missing files or dependencies.\nWould you like to try autofix by automatically installing vc_redist?.", "VRCX CefSharp not found.", MessageBoxButtons.YesNo, MessageBoxIcon.Error);
var result = MessageBox.Show(
"VRCX has encountered an error with the CefSharp backend,\nthis is typically caused by missing files or dependencies.\nWould you like to try autofix by automatically installing vc_redist?.",
"VRCX CefSharp not found.", MessageBoxButtons.YesNo, MessageBoxIcon.Error);
switch (result)
{
case DialogResult.Yes:
logger.Fatal("Handled Exception, user selected auto install of vc_redist.");
Update.DownloadInstallRedist();
MessageBox.Show(
"vc_redist has finished installing, if the issue persists upon next restart, please reinstall VRCX From GitHub,\nVRCX Will now restart.", "vc_redist installation complete", MessageBoxButtons.OK);
"vc_redist has finished installing, if the issue persists upon next restart, please reinstall VRCX From GitHub,\nVRCX Will now restart.",
"vc_redist installation complete", MessageBoxButtons.OK);
Thread.Sleep(5000);
AppApi.Instance.RestartApplication(false);
AppApiInstance.RestartApplication(false);
break;
case DialogResult.No:
logger.Fatal("Handled Exception, user chose manual.");
MessageBox.Show("VRCX will now close, try reinstalling VRCX using the setup from Github as a potential fix.", "VRCX CefSharp not found", MessageBoxButtons.OK, MessageBoxIcon.Error);
MessageBox.Show(
"VRCX will now close, try reinstalling VRCX using the setup from Github as a potential fix.",
"VRCX CefSharp not found", MessageBoxButtons.OK, MessageBoxIcon.Error);
Thread.Sleep(5000);
Environment.Exit(0);
break;
}
}
#endregion
#region Handle Database Error
catch (SQLiteException e)
{
logger.Fatal(e, "Unhandled SQLite Exception, closing.");
@@ -146,61 +179,53 @@ namespace VRCX
e, "Database error", MessageBoxButtons.YesNo, MessageBoxIcon.Error);
if (messageBoxResult == DialogResult.Yes)
{
AppApi.Instance.OpenLink("https://github.com/vrcx-team/VRCX/wiki#how-to-repair-vrcx-database");
AppApiInstance.OpenLink("https://github.com/vrcx-team/VRCX/wiki#how-to-repair-vrcx-database");
}
}
#endregion
catch (Exception e)
{
var cpuError = WinApi.GetCpuErrorMessage();
if (cpuError != null)
{
var messageBoxResult = MessageBox.Show(cpuError.Value.Item1, "Potentially Faulty CPU Detected", MessageBoxButtons.YesNo, MessageBoxIcon.Error);
var messageBoxResult = MessageBox.Show(cpuError.Value.Item1, "Potentially Faulty CPU Detected",
MessageBoxButtons.YesNo, MessageBoxIcon.Error);
if (messageBoxResult == DialogResult.Yes)
{
AppApi.Instance.OpenLink(cpuError.Value.Item2);
AppApiInstance.OpenLink(cpuError.Value.Item2);
}
}
logger.Fatal(e, "Unhandled Exception, program dying");
MessageBox.Show(e.ToString(), "PLEASE REPORT IN https://vrcx.app/discord", MessageBoxButtons.OK, MessageBoxIcon.Error);
MessageBox.Show(e.ToString(), "PLEASE REPORT IN https://vrcx.app/discord", MessageBoxButtons.OK,
MessageBoxIcon.Error);
Environment.Exit(0);
}
}
private static void GetVersion()
{
var buildName = "VRCX";
try
{
Version = $"{buildName} {File.ReadAllText(Path.Combine(BaseDirectory, "Version"))}";
}
catch (Exception)
{
Version = $"{buildName} Build";
}
Version = Version.Replace("\r", "").Replace("\n", "");
}
private static void Run()
{
StartupArgs.ArgsCheck();
SetProgramDirectories();
VRCXStorage.Load();
VRCXStorage.Instance.Load();
BrowserSubprocess.Start();
ConfigureLogger();
Update.Check();
GetVersion();
Update.Check();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
logger.Info("{0} Starting...", Version);
logger.Debug("Wine support detection: {0}", Wine.GetIfWine());
ProcessMonitor.Instance.Init();
logger.Debug("Wine detection: {0}", Wine.GetIfWine());
SQLiteLegacy.Instance.Init();
AppApi.Instance.Init();
AppApiInstance = new AppApiCef();
AppApiVr.Instance.Init();
ProcessMonitor.Instance.Init();
Discord.Instance.Init();
WorldDBManager.Instance.Init();
WebApi.Instance.Init();
@@ -208,13 +233,13 @@ namespace VRCX
AutoAppLaunchManager.Instance.Init();
CefService.Instance.Init();
IPCServer.Instance.Init();
if (VRCXStorage.Instance.Get("VRCX_DisableVrOverlayGpuAcceleration") == "true")
VRCXVRInstance = new VRCXVRLegacy();
else
VRCXVRInstance = new VRCXVR();
VRCXVRInstance.Init();
Application.Run(new MainForm());
logger.Info("{0} Exiting...", Version);
WebApi.Instance.SaveCookies();
@@ -228,9 +253,43 @@ namespace VRCX
Discord.Instance.Exit();
SystemMonitor.Instance.Exit();
VRCXStorage.Save();
VRCXStorage.Instance.Save();
SQLiteLegacy.Instance.Exit();
ProcessMonitor.Instance.Exit();
}
#else
public static void PreInit(string version)
{
Version = version;
StartupArgs.ArgsCheck();
SetProgramDirectories();
}
public static void Init()
{
ConfigureLogger();
Update.Check();
logger.Info("{0} Starting...", Version);
AppApiInstance = new AppApiElectron();
// ProcessMonitor.Instance.Init();
}
#endif
}
}
#if LINUX
public class ProgramElectron
{
public void PreInit(string version)
{
Program.PreInit(version);
}
public void Init()
{
Program.Init();
}
}
#endif
}