/* * Copyright (c) 2025 ETH Zürich, IT Services * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ using SafeExamBrowser.Core.Contracts.OperationModel; using SafeExamBrowser.Core.Contracts.ResponsibilityModel; using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Runtime.Responsibilities; using SafeExamBrowser.UserInterface.Contracts.Windows; using System.Deployment.Internal; namespace SafeExamBrowser.Runtime { internal class RuntimeController { private readonly ILogger logger; private readonly IOperationSequence bootstrapSequence; private readonly IResponsibilityCollection responsibilities; private readonly RuntimeContext runtimeContext; private readonly IRuntimeWindow runtimeWindow; private readonly ISplashScreen splashScreen; private bool SessionIsRunning => runtimeContext.Current != default; internal RuntimeController( ILogger logger, IOperationSequence bootstrapSequence, IResponsibilityCollection responsibilities, RuntimeContext runtimeContext, IRuntimeWindow runtimeWindow, ISplashScreen splashScreen) { this.bootstrapSequence = bootstrapSequence; this.responsibilities = responsibilities; this.logger = logger; this.runtimeWindow = runtimeWindow; this.runtimeContext = runtimeContext; this.splashScreen = splashScreen; } internal bool TryStart() { logger.Info("Initiating startup procedure..."); // We need to show the runtime window here already, this way implicitly setting it as the runtime application's main window. // Otherwise, the splash screen is considered as the main window and thus the operating system and/or WPF does not correctly // activate the runtime window once bootstrapping has finished, which in turn leads to undesired user interface behavior. runtimeWindow.Show(); runtimeWindow.BringToForeground(); runtimeWindow.SetIndeterminate(); splashScreen.Show(); splashScreen.BringToForeground(); var initialized = bootstrapSequence.TryPerform() == OperationResult.Success; if (initialized) { responsibilities.Delegate(RuntimeTask.RegisterEvents); splashScreen.Hide(); logger.Info("Application successfully initialized."); logger.Log(string.Empty); logger.Subscribe(runtimeWindow); responsibilities.Delegate(RuntimeTask.StartSession); } else { logger.Info("Application startup aborted!"); logger.Log(string.Empty); responsibilities.Delegate(RuntimeTask.ShowStartupError); } return initialized && SessionIsRunning; } internal void Terminate() { responsibilities.Delegate(RuntimeTask.DeregisterEvents); if (SessionIsRunning) { responsibilities.Delegate(RuntimeTask.StopSession); } logger.Unsubscribe(runtimeWindow); runtimeWindow.Close(); splashScreen.Show(); splashScreen.BringToForeground(); logger.Log(string.Empty); logger.Info("Initiating shutdown procedure..."); var success = bootstrapSequence.TryRevert() == OperationResult.Success; if (success) { logger.Info("Application successfully finalized."); logger.Log(string.Empty); } else { logger.Info("Shutdown procedure failed!"); logger.Log(string.Empty); responsibilities.Delegate(RuntimeTask.ShowShutdownError); } splashScreen.Close(); } } }