using Hardcodet.Wpf.TaskbarNotification; using Playnite.API; using Playnite.Common; using Playnite.Controllers; using Playnite.Controls; using Playnite.Database; using Playnite.DesktopApp.API; using Playnite.DesktopApp.Controls; using Playnite.DesktopApp.Markup; using Playnite.DesktopApp.ViewModels; using Playnite.DesktopApp.Windows; using Playnite.Metadata; using Playnite.Plugins; using Playnite.SDK; using Playnite.SDK.Events; using Playnite.SDK.Models; using Playnite.Settings; using Playnite.ViewModels; using Playnite.WebView; using Playnite.Windows; using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Media.Imaging; namespace Playnite.DesktopApp { public class DesktopApplication : PlayniteApplication { private ILogger logger = LogManager.GetLogger(); private TaskbarIcon trayIcon; public const string DefaultThemeName = "Default"; private SplashScreen splashScreen; public List ThirdPartyTools { get; private set; } private DesktopAppViewModel mainModel; public DesktopAppViewModel MainModel { get => mainModel; set { mainModel = value; MainModelBase = value; } } public new static DesktopApplication Current { get => PlayniteApplication.Current == null ? null : (DesktopApplication)PlayniteApplication.Current; } public DesktopApplication(Func appInitializer, SplashScreen splashScreen, CmdLineOptions cmdLine) : base(appInitializer, ApplicationMode.Desktop, DefaultThemeName, cmdLine) { this.splashScreen = splashScreen; } public override bool Startup() { ProgressWindowFactory.SetWindowType(); CrashHandlerWindowFactory.SetWindowType(); ExtensionCrashHandlerWindowFactory.SetWindowType(); UpdateWindowFactory.SetWindowType(); LicenseAgreementWindowFactory.SetWindowType(); Dialogs = new DesktopDialogs(); Playnite.Dialogs.SetHandler(Dialogs); if (!ConfigureApplication()) { return false; } InstantiateApp(); AppUriHandler = MainModel.ProcessUriRequest; var isFirstStart = ProcessStartupWizard(); MigrateDatabase(); OpenMainViewAsync(isFirstStart); LoadTrayIcon(); #pragma warning disable CS4014 StartUpdateCheckerAsync(); SendUsageDataAsync(); #pragma warning restore CS4014 ProcessArguments(); splashScreen?.Close(new TimeSpan(0)); return true; } public override void InitializeNative() { ((App)CurrentNative).InitializeComponent(); } public override void Restore() { MainModel?.RestoreWindow(); } public override void Minimize() { MainModel.WindowState = WindowState.Minimized; } public override void ReleaseResources(bool releaseCefSharp = true) { trayIcon?.Dispose(); base.ReleaseResources(releaseCefSharp); } public override void Restart() { Restart(new CmdLineOptions { MasterInstance = true }); } public override void Restart(CmdLineOptions options) { options.MasterInstance = true; QuitAndStart(PlaynitePaths.DesktopExecutablePath, options.ToString()); } public override void InstantiateApp() { Database = new GameDatabase(); Database.SetAsSingletonInstance(); Controllers = new GameControllerFactory(Database); Extensions = new ExtensionFactory(Database, Controllers, GetApiInstance); GamesEditor = new DesktopGamesEditor( Database, Controllers, AppSettings, Dialogs, Extensions, this, new DesktopActionSelector()); Game.DatabaseReference = Database; ImageSourceManager.SetDatabase(Database); MainModel = new DesktopAppViewModel( Database, new MainWindowFactory(), Dialogs, new ResourceProvider(), AppSettings, (DesktopGamesEditor)GamesEditor, Extensions, this); PlayniteApiGlobal = GetApiInstance(); SDK.API.Instance = PlayniteApiGlobal; } private void LoadTrayIcon() { if (AppSettings.EnableTray) { try { trayIcon = new TaskbarIcon { MenuActivation = PopupActivationMode.LeftOrRightClick, DoubleClickCommand = MainModel.ShowWindowCommand, Icon = GetTrayIcon(), Visibility = Visibility.Visible, ContextMenu = new TrayContextMenu(MainModel) }; } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to initialize tray icon."); } } } private async void OpenMainViewAsync(bool isFirstStart) { if (!isFirstStart) { Extensions.LoadPlugins( AppSettings.DisabledPlugins, CmdLine.SafeStartup, AppSettings.DevelExtenions.Where(a => a.Selected == true).Select(a => a.Item).ToList()); } Extensions.LoadScripts( AppSettings.DisabledPlugins, CmdLine.SafeStartup, AppSettings.DevelExtenions.Where(a => a.Selected == true).Select(a => a.Item).ToList()); OnExtensionsLoaded(); try { MainModel.ThirdPartyTools = ThirdPartyToolsList.GetTools(Extensions.LibraryPlugins); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to load third party tools."); } MainModel.OpenView(); CurrentNative.MainWindow = MainModel.Window.Window; if (isFirstStart) { await MainModel.UpdateLibrary(false, true, false); await MainModel.DownloadMetadata(AppSettings.MetadataSettings); } else { await MainModel.ProcessStartupLibUpdate(); } // This is most likely safe place to consider application to be started properly FileSystem.DeleteFile(PlaynitePaths.SafeStartupFlagFile); } private bool ProcessStartupWizard() { // TODO test db path recovery var firstStartup = true; var defaultPath = GameDatabase.GetFullDbPath(GameDatabase.GetDefaultPath(PlayniteSettings.IsPortable)); if (!AppSettings.DatabasePath.IsNullOrEmpty()) { AppSettings.FirstTimeWizardComplete = true; firstStartup = false; } else if (AppSettings.DatabasePath.IsNullOrEmpty() && Directory.Exists(defaultPath)) { AppSettings.DatabasePath = GameDatabase.GetDefaultPath(PlayniteSettings.IsPortable); AppSettings.FirstTimeWizardComplete = true; firstStartup = false; } if (firstStartup) { AppSettings.DatabasePath = GameDatabase.GetDefaultPath(PlayniteSettings.IsPortable); AppSettings.SaveSettings(); Database.SetDatabasePath(AppSettings.DatabasePath); Database.OpenDatabase(); var wizardWindow = new FirstTimeStartupWindowFactory(); var wizardModel = new FirstTimeStartupViewModel( wizardWindow, Dialogs, new ResourceProvider(), Extensions, ServicesClient); if (wizardModel.OpenView() == true) { var settings = wizardModel.Settings; AppSettings.FirstTimeWizardComplete = true; AppSettings.DisabledPlugins = settings.DisabledPlugins; AppSettings.SaveSettings(); } else { AppSettings.FirstTimeWizardComplete = true; AppSettings.SaveSettings(); } } else { Database.SetDatabasePath(AppSettings.DatabasePath); } return firstStartup; } public override void ShowWindowsNotification(string title, string body, Action action) { var icon = GetTrayIcon(); if (AppSettings.EnableTray) { trayIcon.ShowBalloonTip(title, body, icon, true); } else { WindowsNotifyIconManager.Notify(icon, title, body, action); } } private Icon GetTrayIcon() { var trayIconImage = ResourceProvider.GetResource(AppSettings.TrayIcon.GetDescription()) as BitmapImage ?? ResourceProvider.GetResource("TrayIcon") as BitmapImage; return new Icon(trayIconImage.UriSource.LocalPath); } public override void SwitchAppMode(ApplicationMode mode) { if (mode == ApplicationMode.Fullscreen) { MainModel.SwitchToFullscreenMode(); } else { Restore(); } } public override PlayniteAPI GetApiInstance(ExtensionManifest pluginOwner) { return new PlayniteAPI { Addons = new AddonsAPI(Extensions, AppSettings), ApplicationInfo = new PlayniteInfoAPI(), ApplicationSettings = new PlayniteSettingsAPI(AppSettings, Database), Database = new DatabaseAPI(Database), Dialogs = Dialogs, Emulation = new Emulators.Emulation(), MainView = new MainViewAPI(MainModel), Notifications = Notifications, Paths = new PlaynitePathsAPI(), Resources = new ResourceProvider(), RootApi = new PlayniteApiRoot(GamesEditor, Extensions, Database), UriHandler = UriHandler, WebViews = new WebViewFactory(AppSettings) }; } public override PlayniteAPI GetApiInstance() { return new PlayniteAPI { Addons = new AddonsAPI(Extensions, AppSettings), ApplicationInfo = new PlayniteInfoAPI(), ApplicationSettings = new PlayniteSettingsAPI(AppSettings, Database), Database = new DatabaseAPI(Database), Dialogs = Dialogs, Emulation = new Emulators.Emulation(), MainView = new MainViewAPI(MainModel), Notifications = Notifications, Paths = new PlaynitePathsAPI(), Resources = new ResourceProvider(), RootApi = new PlayniteApiRoot(GamesEditor, Extensions, Database), UriHandler = UriHandler, WebViews = new WebViewFactory(AppSettings) }; } } }