SEBWIN-141: Basic draft of action center including keyboard and touch activators.

This commit is contained in:
dbuechel 2019-03-06 16:10:00 +01:00
parent 04641b9bc7
commit d99d46d086
80 changed files with 978 additions and 191 deletions

View file

@ -19,7 +19,7 @@ using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.MessageBox; using SafeExamBrowser.Contracts.UserInterface.MessageBox;
using SafeExamBrowser.Contracts.UserInterface.Taskbar; using SafeExamBrowser.Contracts.UserInterface.Shell;
using BrowserSettings = SafeExamBrowser.Contracts.Configuration.Settings.BrowserSettings; using BrowserSettings = SafeExamBrowser.Contracts.Configuration.Settings.BrowserSettings;
namespace SafeExamBrowser.Browser namespace SafeExamBrowser.Browser

View file

@ -24,7 +24,7 @@ using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring; using SafeExamBrowser.Contracts.Monitoring;
using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.MessageBox; using SafeExamBrowser.Contracts.UserInterface.MessageBox;
using SafeExamBrowser.Contracts.UserInterface.Taskbar; using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Windows; using SafeExamBrowser.Contracts.UserInterface.Windows;
using SafeExamBrowser.Contracts.WindowsApi; using SafeExamBrowser.Contracts.WindowsApi;
@ -34,6 +34,7 @@ namespace SafeExamBrowser.Client.UnitTests
public class ClientControllerTests public class ClientControllerTests
{ {
private AppConfig appConfig; private AppConfig appConfig;
private Mock<IActionCenter> actionCenter;
private Mock<IBrowserApplicationController> browserController; private Mock<IBrowserApplicationController> browserController;
private Mock<IClientHost> clientHost; private Mock<IClientHost> clientHost;
private Mock<IDisplayMonitor> displayMonitor; private Mock<IDisplayMonitor> displayMonitor;
@ -58,6 +59,7 @@ namespace SafeExamBrowser.Client.UnitTests
public void Initialize() public void Initialize()
{ {
appConfig = new AppConfig(); appConfig = new AppConfig();
actionCenter = new Mock<IActionCenter>();
browserController = new Mock<IBrowserApplicationController>(); browserController = new Mock<IBrowserApplicationController>();
clientHost = new Mock<IClientHost>(); clientHost = new Mock<IClientHost>();
displayMonitor = new Mock<IDisplayMonitor>(); displayMonitor = new Mock<IDisplayMonitor>();
@ -81,6 +83,7 @@ namespace SafeExamBrowser.Client.UnitTests
uiFactory.Setup(u => u.CreateSplashScreen(It.IsAny<AppConfig>())).Returns(new Mock<ISplashScreen>().Object); uiFactory.Setup(u => u.CreateSplashScreen(It.IsAny<AppConfig>())).Returns(new Mock<ISplashScreen>().Object);
sut = new ClientController( sut = new ClientController(
actionCenter.Object,
displayMonitor.Object, displayMonitor.Object,
explorerShell.Object, explorerShell.Object,
hashAlgorithm.Object, hashAlgorithm.Object,
@ -632,6 +635,21 @@ namespace SafeExamBrowser.Client.UnitTests
splashScreen.VerifySet(s => s.AppConfig = appConfig, Times.Once); splashScreen.VerifySet(s => s.AppConfig = appConfig, Times.Once);
} }
[TestMethod]
public void Startup_MustCorrectlyHandleTaskbar()
{
operationSequence.Setup(o => o.TryPerform()).Returns(OperationResult.Success);
sut.TryStart();
taskbar.Verify(t => t.Show(), Times.Once);
taskbar.Reset();
operationSequence.Setup(o => o.TryPerform()).Returns(OperationResult.Aborted);
sut.TryStart();
taskbar.Verify(t => t.Show(), Times.Never);
}
[TestMethod] [TestMethod]
public void WindowMonitor_MustHandleAllowedWindowChangeCorrectly() public void WindowMonitor_MustHandleAllowedWindowChangeCorrectly()
{ {

View file

@ -6,8 +6,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
using SafeExamBrowser.Contracts.UserInterface.Taskbar; using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events; using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
namespace SafeExamBrowser.Client.UnitTests.Notifications namespace SafeExamBrowser.Client.UnitTests.Notifications
{ {

View file

@ -12,7 +12,7 @@ using SafeExamBrowser.Client.Operations;
using SafeExamBrowser.Contracts.Applications; using SafeExamBrowser.Contracts.Applications;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.Taskbar; using SafeExamBrowser.Contracts.UserInterface.Shell;
namespace SafeExamBrowser.Client.UnitTests.Operations namespace SafeExamBrowser.Client.UnitTests.Operations
{ {

View file

@ -11,7 +11,7 @@ using Moq;
using SafeExamBrowser.Client.Operations; using SafeExamBrowser.Client.Operations;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring; using SafeExamBrowser.Contracts.Monitoring;
using SafeExamBrowser.Contracts.UserInterface.Taskbar; using SafeExamBrowser.Contracts.UserInterface.Shell;
namespace SafeExamBrowser.Client.UnitTests.Operations namespace SafeExamBrowser.Client.UnitTests.Operations
{ {

View file

@ -15,7 +15,7 @@ using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.SystemComponents; using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.Taskbar; using SafeExamBrowser.Contracts.UserInterface.Shell;
namespace SafeExamBrowser.Client.UnitTests.Operations namespace SafeExamBrowser.Client.UnitTests.Operations
{ {
@ -33,7 +33,6 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
private Mock<ISystemComponent<ISystemWirelessNetworkControl>> wirelessNetworkMock; private Mock<ISystemComponent<ISystemWirelessNetworkControl>> wirelessNetworkMock;
private Mock<ISystemInfo> systemInfoMock; private Mock<ISystemInfo> systemInfoMock;
private Mock<ITaskbar> taskbarMock; private Mock<ITaskbar> taskbarMock;
private Mock<IText> textMock;
private Mock<IUserInterfaceFactory> uiFactoryMock; private Mock<IUserInterfaceFactory> uiFactoryMock;
private TaskbarOperation sut; private TaskbarOperation sut;
@ -52,7 +51,6 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
systemInfoMock = new Mock<ISystemInfo>(); systemInfoMock = new Mock<ISystemInfo>();
taskbarMock = new Mock<ITaskbar>(); taskbarMock = new Mock<ITaskbar>();
settings = new TaskbarSettings(); settings = new TaskbarSettings();
textMock = new Mock<IText>();
uiFactoryMock = new Mock<IUserInterfaceFactory>(); uiFactoryMock = new Mock<IUserInterfaceFactory>();
settings.AllowApplicationLog = true; settings.AllowApplicationLog = true;
@ -73,7 +71,6 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
systemInfoMock.Object, systemInfoMock.Object,
taskbarMock.Object, taskbarMock.Object,
settings, settings,
textMock.Object,
uiFactoryMock.Object); uiFactoryMock.Object);
} }

View file

@ -62,12 +62,7 @@ namespace SafeExamBrowser.Client
var success = instances.ClientController.TryStart(); var success = instances.ClientController.TryStart();
if (success) if (!success)
{
MainWindow = instances.Taskbar;
MainWindow.Show();
}
else
{ {
Shutdown(); Shutdown();
} }

View file

@ -24,7 +24,7 @@ using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring; using SafeExamBrowser.Contracts.Monitoring;
using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.MessageBox; using SafeExamBrowser.Contracts.UserInterface.MessageBox;
using SafeExamBrowser.Contracts.UserInterface.Taskbar; using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Windows; using SafeExamBrowser.Contracts.UserInterface.Windows;
using SafeExamBrowser.Contracts.WindowsApi; using SafeExamBrowser.Contracts.WindowsApi;
@ -32,6 +32,7 @@ namespace SafeExamBrowser.Client
{ {
internal class ClientController : IClientController internal class ClientController : IClientController
{ {
private IActionCenter actionCenter;
private IDisplayMonitor displayMonitor; private IDisplayMonitor displayMonitor;
private IExplorerShell explorerShell; private IExplorerShell explorerShell;
private IHashAlgorithm hashAlgorithm; private IHashAlgorithm hashAlgorithm;
@ -67,6 +68,7 @@ namespace SafeExamBrowser.Client
} }
public ClientController( public ClientController(
IActionCenter actionCenter,
IDisplayMonitor displayMonitor, IDisplayMonitor displayMonitor,
IExplorerShell explorerShell, IExplorerShell explorerShell,
IHashAlgorithm hashAlgorithm, IHashAlgorithm hashAlgorithm,
@ -81,6 +83,7 @@ namespace SafeExamBrowser.Client
IUserInterfaceFactory uiFactory, IUserInterfaceFactory uiFactory,
IWindowMonitor windowMonitor) IWindowMonitor windowMonitor)
{ {
this.actionCenter = actionCenter;
this.displayMonitor = displayMonitor; this.displayMonitor = displayMonitor;
this.explorerShell = explorerShell; this.explorerShell = explorerShell;
this.hashAlgorithm = hashAlgorithm; this.hashAlgorithm = hashAlgorithm;
@ -109,14 +112,13 @@ namespace SafeExamBrowser.Client
if (success) if (success)
{ {
RegisterEvents(); RegisterEvents();
ShowShell();
StartBrowser(); StartBrowser();
var communication = runtime.InformClientReady(); var communication = runtime.InformClientReady();
if (communication.Success) if (communication.Success)
{ {
splashScreen.Close();
logger.Info("Application successfully initialized."); logger.Info("Application successfully initialized.");
logger.Log(string.Empty); logger.Log(string.Empty);
} }
@ -132,6 +134,8 @@ namespace SafeExamBrowser.Client
logger.Log(string.Empty); logger.Log(string.Empty);
} }
splashScreen.Close();
return success; return success;
} }
@ -141,7 +145,8 @@ namespace SafeExamBrowser.Client
logger.Info("Initiating shutdown procedure..."); logger.Info("Initiating shutdown procedure...");
splashScreen = uiFactory.CreateSplashScreen(appConfig); splashScreen = uiFactory.CreateSplashScreen(appConfig);
splashScreen.Show(); actionCenter.Close();
taskbar.Close();
DeregisterEvents(); DeregisterEvents();
@ -197,6 +202,14 @@ namespace SafeExamBrowser.Client
} }
} }
private void ShowShell()
{
if (Settings.Taskbar.EnableTaskbar)
{
taskbar.Show();
}
}
private void StartBrowser() private void StartBrowser()
{ {
logger.Info("Starting browser application..."); logger.Info("Starting browser application...");
@ -321,7 +334,6 @@ namespace SafeExamBrowser.Client
private void ClientHost_Shutdown() private void ClientHost_Shutdown()
{ {
taskbar.Close();
shutdown.Invoke(); shutdown.Invoke();
} }
@ -363,7 +375,6 @@ namespace SafeExamBrowser.Client
logger.Error("Lost connection to the runtime!"); logger.Error("Lost connection to the runtime!");
messageBox.Show(TextKey.MessageBox_ApplicationError, TextKey.MessageBox_ApplicationErrorTitle, icon: MessageBoxIcon.Error); messageBox.Show(TextKey.MessageBox_ApplicationError, TextKey.MessageBox_ApplicationErrorTitle, icon: MessageBoxIcon.Error);
taskbar.Close();
shutdown.Invoke(); shutdown.Invoke();
} }

View file

@ -30,6 +30,7 @@ using SafeExamBrowser.Contracts.Monitoring;
using SafeExamBrowser.Contracts.SystemComponents; using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.MessageBox; using SafeExamBrowser.Contracts.UserInterface.MessageBox;
using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.WindowsApi; using SafeExamBrowser.Contracts.WindowsApi;
using SafeExamBrowser.Core.OperationModel; using SafeExamBrowser.Core.OperationModel;
using SafeExamBrowser.Core.Operations; using SafeExamBrowser.Core.Operations;
@ -48,27 +49,31 @@ namespace SafeExamBrowser.Client
{ {
internal class CompositionRoot internal class CompositionRoot
{ {
private ClientConfiguration configuration;
private string logFilePath; private string logFilePath;
private LogLevel logLevel; private LogLevel logLevel;
private string runtimeHostUri; private string runtimeHostUri;
private Guid startupToken; private Guid startupToken;
private IActionCenter actionCenter;
private IBrowserApplicationController browserController; private IBrowserApplicationController browserController;
private ClientConfiguration configuration;
private IClientHost clientHost; private IClientHost clientHost;
private ILogger logger; private ILogger logger;
private IMessageBox messageBox; private IMessageBox messageBox;
private IProcessMonitor processMonitor; private IProcessMonitor processMonitor;
private INativeMethods nativeMethods; private INativeMethods nativeMethods;
private IRuntimeProxy runtimeProxy; private IRuntimeProxy runtimeProxy;
private ISystemComponent<ISystemKeyboardLayoutControl> keyboardLayout;
private ISystemComponent<ISystemPowerSupplyControl> powerSupply;
private ISystemComponent<ISystemWirelessNetworkControl> wirelessNetwork;
private ISystemInfo systemInfo; private ISystemInfo systemInfo;
private ITaskbar taskbar;
private IText text; private IText text;
private ITextResource textResource; private ITextResource textResource;
private IUserInterfaceFactory uiFactory; private IUserInterfaceFactory uiFactory;
private IWindowMonitor windowMonitor; private IWindowMonitor windowMonitor;
internal IClientController ClientController { get; private set; } internal IClientController ClientController { get; private set; }
internal Taskbar Taskbar { get; private set; }
internal void BuildObjectGraph(Action shutdown) internal void BuildObjectGraph(Action shutdown)
{ {
@ -82,18 +87,21 @@ namespace SafeExamBrowser.Client
InitializeLogging(); InitializeLogging();
InitializeText(); InitializeText();
actionCenter = new ActionCenter();
keyboardLayout = new KeyboardLayout(new ModuleLogger(logger, nameof(KeyboardLayout)), text);
messageBox = new MessageBox(text); messageBox = new MessageBox(text);
powerSupply = new PowerSupply(new ModuleLogger(logger, nameof(PowerSupply)), text);
processMonitor = new ProcessMonitor(new ModuleLogger(logger, nameof(ProcessMonitor)), nativeMethods); processMonitor = new ProcessMonitor(new ModuleLogger(logger, nameof(ProcessMonitor)), nativeMethods);
uiFactory = new UserInterfaceFactory(text); uiFactory = new UserInterfaceFactory(text);
runtimeProxy = new RuntimeProxy(runtimeHostUri, new ProxyObjectFactory(), new ModuleLogger(logger, nameof(RuntimeProxy))); runtimeProxy = new RuntimeProxy(runtimeHostUri, new ProxyObjectFactory(), new ModuleLogger(logger, nameof(RuntimeProxy)));
taskbar = new Taskbar(new ModuleLogger(logger, nameof(taskbar)));
windowMonitor = new WindowMonitor(new ModuleLogger(logger, nameof(WindowMonitor)), nativeMethods); windowMonitor = new WindowMonitor(new ModuleLogger(logger, nameof(WindowMonitor)), nativeMethods);
wirelessNetwork = new WirelessNetwork(new ModuleLogger(logger, nameof(WirelessNetwork)), text);
var displayMonitor = new DisplayMonitor(new ModuleLogger(logger, nameof(DisplayMonitor)), nativeMethods); var displayMonitor = new DisplayMonitor(new ModuleLogger(logger, nameof(DisplayMonitor)), nativeMethods);
var explorerShell = new ExplorerShell(new ModuleLogger(logger, nameof(ExplorerShell)), nativeMethods); var explorerShell = new ExplorerShell(new ModuleLogger(logger, nameof(ExplorerShell)), nativeMethods);
var hashAlgorithm = new HashAlgorithm(); var hashAlgorithm = new HashAlgorithm();
Taskbar = new Taskbar(new ModuleLogger(logger, nameof(Taskbar)));
var operations = new Queue<IOperation>(); var operations = new Queue<IOperation>();
operations.Enqueue(new InitializationOperation(logger)); operations.Enqueue(new InitializationOperation(logger));
@ -104,18 +112,33 @@ namespace SafeExamBrowser.Client
operations.Enqueue(new LazyInitializationOperation(BuildClientHostOperation)); operations.Enqueue(new LazyInitializationOperation(BuildClientHostOperation));
operations.Enqueue(new LazyInitializationOperation(BuildClientHostDisconnectionOperation)); operations.Enqueue(new LazyInitializationOperation(BuildClientHostDisconnectionOperation));
operations.Enqueue(new LazyInitializationOperation(BuildKeyboardInterceptorOperation)); operations.Enqueue(new LazyInitializationOperation(BuildKeyboardInterceptorOperation));
operations.Enqueue(new LazyInitializationOperation(BuildMouseInterceptorOperation));
operations.Enqueue(new LazyInitializationOperation(BuildWindowMonitorOperation)); operations.Enqueue(new LazyInitializationOperation(BuildWindowMonitorOperation));
operations.Enqueue(new LazyInitializationOperation(BuildProcessMonitorOperation)); operations.Enqueue(new LazyInitializationOperation(BuildProcessMonitorOperation));
operations.Enqueue(new DisplayMonitorOperation(displayMonitor, logger, Taskbar)); operations.Enqueue(new DisplayMonitorOperation(displayMonitor, logger, taskbar));
operations.Enqueue(new LazyInitializationOperation(BuildActionCenterOperation));
operations.Enqueue(new LazyInitializationOperation(BuildTaskbarOperation)); operations.Enqueue(new LazyInitializationOperation(BuildTaskbarOperation));
operations.Enqueue(new LazyInitializationOperation(BuildBrowserOperation)); operations.Enqueue(new LazyInitializationOperation(BuildBrowserOperation));
operations.Enqueue(new ClipboardOperation(logger, nativeMethods)); operations.Enqueue(new ClipboardOperation(logger, nativeMethods));
operations.Enqueue(new LazyInitializationOperation(BuildMouseInterceptorOperation));
operations.Enqueue(new DelegateOperation(UpdateClientControllerDependencies)); operations.Enqueue(new DelegateOperation(UpdateClientControllerDependencies));
var sequence = new OperationSequence(logger, operations); var sequence = new OperationSequence(logger, operations);
ClientController = new ClientController(displayMonitor, explorerShell, hashAlgorithm, logger, messageBox, sequence, processMonitor, runtimeProxy, shutdown, Taskbar, text, uiFactory, windowMonitor); ClientController = new ClientController(
actionCenter,
displayMonitor,
explorerShell,
hashAlgorithm,
logger,
messageBox,
sequence,
processMonitor,
runtimeProxy,
shutdown,
taskbar,
text,
uiFactory,
windowMonitor);
} }
internal void LogStartupInformation() internal void LogStartupInformation()
@ -173,12 +196,41 @@ namespace SafeExamBrowser.Client
textResource = new XmlTextResource(path); textResource = new XmlTextResource(path);
} }
private IOperation BuildActionCenterOperation()
{
var aboutInfo = new AboutNotificationInfo(text);
var aboutController = new AboutNotificationController(configuration.AppConfig, uiFactory);
var logInfo = new LogNotificationInfo(text);
var logController = new LogNotificationController(logger, uiFactory);
var activators = new IActionCenterActivator[]
{
new KeyboardActivator(new ModuleLogger(logger, nameof(KeyboardActivator))),
new TouchActivator(new ModuleLogger(logger, nameof(TouchActivator)))
};
var operation = new ActionCenterOperation(
actionCenter,
activators,
logger,
aboutInfo,
aboutController,
logInfo,
logController,
keyboardLayout,
powerSupply,
wirelessNetwork,
configuration.Settings.ActionCenter,
systemInfo,
uiFactory);
return operation;
}
private IOperation BuildBrowserOperation() private IOperation BuildBrowserOperation()
{ {
var moduleLogger = new ModuleLogger(logger, "BrowserController"); var moduleLogger = new ModuleLogger(logger, "BrowserController");
var browserController = new BrowserApplicationController(configuration.AppConfig, configuration.Settings.Browser, messageBox, moduleLogger, text, uiFactory); var browserController = new BrowserApplicationController(configuration.AppConfig, configuration.Settings.Browser, messageBox, moduleLogger, text, uiFactory);
var browserInfo = new BrowserApplicationInfo(); var browserInfo = new BrowserApplicationInfo();
var operation = new BrowserOperation(browserController, browserInfo, logger, Taskbar, uiFactory); var operation = new BrowserOperation(browserController, browserInfo, logger, taskbar, uiFactory);
this.browserController = browserController; this.browserController = browserController;
@ -232,12 +284,21 @@ namespace SafeExamBrowser.Client
{ {
var aboutInfo = new AboutNotificationInfo(text); var aboutInfo = new AboutNotificationInfo(text);
var aboutController = new AboutNotificationController(configuration.AppConfig, uiFactory); var aboutController = new AboutNotificationController(configuration.AppConfig, uiFactory);
var keyboardLayout = new KeyboardLayout(new ModuleLogger(logger, nameof(KeyboardLayout)), text);
var logController = new LogNotificationController(logger, uiFactory);
var logInfo = new LogNotificationInfo(text); var logInfo = new LogNotificationInfo(text);
var powerSupply = new PowerSupply(new ModuleLogger(logger, nameof(PowerSupply)), text); var logController = new LogNotificationController(logger, uiFactory);
var wirelessNetwork = new WirelessNetwork(new ModuleLogger(logger, nameof(WirelessNetwork)), text); var operation = new TaskbarOperation(
var operation = new TaskbarOperation(logger, aboutInfo, aboutController, logInfo, logController, keyboardLayout, powerSupply, wirelessNetwork, systemInfo, Taskbar, configuration.Settings.Taskbar, text, uiFactory); logger,
aboutInfo,
aboutController,
logInfo,
logController,
keyboardLayout,
powerSupply,
wirelessNetwork,
systemInfo,
taskbar,
configuration.Settings.Taskbar,
uiFactory);
return operation; return operation;
} }

View file

@ -9,7 +9,7 @@
using SafeExamBrowser.Contracts.Client; using SafeExamBrowser.Contracts.Client;
using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.Taskbar; using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Windows; using SafeExamBrowser.Contracts.UserInterface.Windows;
namespace SafeExamBrowser.Client.Notifications namespace SafeExamBrowser.Client.Notifications

View file

@ -9,7 +9,7 @@
using SafeExamBrowser.Contracts.Client; using SafeExamBrowser.Contracts.Client;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.Taskbar; using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Windows; using SafeExamBrowser.Contracts.UserInterface.Windows;
namespace SafeExamBrowser.Client.Notifications namespace SafeExamBrowser.Client.Notifications

View file

@ -0,0 +1,91 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* 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 System.Collections.Generic;
using SafeExamBrowser.Contracts.Client;
using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.Core.OperationModel;
using SafeExamBrowser.Contracts.Core.OperationModel.Events;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.Shell;
namespace SafeExamBrowser.Client.Operations
{
internal class ActionCenterOperation : IOperation
{
private IActionCenter actionCenter;
private IEnumerable<IActionCenterActivator> activators;
private ILogger logger;
private INotificationInfo aboutInfo;
private INotificationController aboutController;
private INotificationInfo logInfo;
private INotificationController logController;
private ISystemComponent<ISystemKeyboardLayoutControl> keyboardLayout;
private ISystemComponent<ISystemPowerSupplyControl> powerSupply;
private ISystemComponent<ISystemWirelessNetworkControl> wirelessNetwork;
private ActionCenterSettings settings;
private ISystemInfo systemInfo;
private IUserInterfaceFactory uiFactory;
public event ActionRequiredEventHandler ActionRequired { add { } remove { } }
public event StatusChangedEventHandler StatusChanged;
public ActionCenterOperation(
IActionCenter actionCenter,
IEnumerable<IActionCenterActivator> activators,
ILogger logger,
INotificationInfo aboutInfo,
INotificationController aboutController,
INotificationInfo logInfo,
INotificationController logController,
ISystemComponent<ISystemKeyboardLayoutControl> keyboardLayout,
ISystemComponent<ISystemPowerSupplyControl> powerSupply,
ISystemComponent<ISystemWirelessNetworkControl> wirelessNetwork,
ActionCenterSettings settings,
ISystemInfo systemInfo,
IUserInterfaceFactory uiFactory)
{
this.actionCenter = actionCenter;
this.activators = activators;
this.logger = logger;
this.aboutInfo = aboutInfo;
this.aboutController = aboutController;
this.logInfo = logInfo;
this.logController = logController;
this.keyboardLayout = keyboardLayout;
this.powerSupply = powerSupply;
this.wirelessNetwork = wirelessNetwork;
this.systemInfo = systemInfo;
this.settings = settings;
this.uiFactory = uiFactory;
}
public OperationResult Perform()
{
foreach (var activator in activators)
{
actionCenter.Register(activator);
activator.Start();
}
return OperationResult.Success;
}
public OperationResult Revert()
{
foreach (var activator in activators)
{
activator.Stop();
}
return OperationResult.Success;
}
}
}

View file

@ -12,7 +12,7 @@ using SafeExamBrowser.Contracts.Core.OperationModel.Events;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.Taskbar; using SafeExamBrowser.Contracts.UserInterface.Shell;
namespace SafeExamBrowser.Client.Operations namespace SafeExamBrowser.Client.Operations
{ {

View file

@ -11,7 +11,7 @@ using SafeExamBrowser.Contracts.Core.OperationModel.Events;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring; using SafeExamBrowser.Contracts.Monitoring;
using SafeExamBrowser.Contracts.UserInterface.Taskbar; using SafeExamBrowser.Contracts.UserInterface.Shell;
namespace SafeExamBrowser.Client.Operations namespace SafeExamBrowser.Client.Operations
{ {

View file

@ -14,7 +14,7 @@ using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.SystemComponents; using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.Taskbar; using SafeExamBrowser.Contracts.UserInterface.Shell;
namespace SafeExamBrowser.Client.Operations namespace SafeExamBrowser.Client.Operations
{ {
@ -32,7 +32,6 @@ namespace SafeExamBrowser.Client.Operations
private ISystemInfo systemInfo; private ISystemInfo systemInfo;
private ITaskbar taskbar; private ITaskbar taskbar;
private IUserInterfaceFactory uiFactory; private IUserInterfaceFactory uiFactory;
private IText text;
public event ActionRequiredEventHandler ActionRequired { add { } remove { } } public event ActionRequiredEventHandler ActionRequired { add { } remove { } }
public event StatusChangedEventHandler StatusChanged; public event StatusChangedEventHandler StatusChanged;
@ -49,7 +48,6 @@ namespace SafeExamBrowser.Client.Operations
ISystemInfo systemInfo, ISystemInfo systemInfo,
ITaskbar taskbar, ITaskbar taskbar,
TaskbarSettings settings, TaskbarSettings settings,
IText text,
IUserInterfaceFactory uiFactory) IUserInterfaceFactory uiFactory)
{ {
this.aboutInfo = aboutInfo; this.aboutInfo = aboutInfo;
@ -62,37 +60,44 @@ namespace SafeExamBrowser.Client.Operations
this.settings = settings; this.settings = settings;
this.systemInfo = systemInfo; this.systemInfo = systemInfo;
this.taskbar = taskbar; this.taskbar = taskbar;
this.text = text;
this.uiFactory = uiFactory; this.uiFactory = uiFactory;
this.wirelessNetwork = wirelessNetwork; this.wirelessNetwork = wirelessNetwork;
} }
public OperationResult Perform() public OperationResult Perform()
{ {
logger.Info("Initializing taskbar...");
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeTaskbar); StatusChanged?.Invoke(TextKey.OperationStatus_InitializeTaskbar);
AddAboutNotification(); if (settings.EnableTaskbar)
taskbar.ShowClock = settings.ShowClock;
if (settings.AllowApplicationLog)
{ {
AddLogNotification(); logger.Info("Initializing taskbar...");
AddAboutNotification();
taskbar.ShowClock = settings.ShowClock;
if (settings.AllowApplicationLog)
{
AddLogNotification();
}
if (settings.AllowKeyboardLayout)
{
AddKeyboardLayoutControl();
}
if (settings.AllowWirelessNetwork)
{
AddWirelessNetworkControl();
}
if (systemInfo.HasBattery)
{
AddPowerSupplyControl();
}
} }
else
if (settings.AllowKeyboardLayout)
{ {
AddKeyboardLayoutControl(); logger.Info("Taskbar is disabled, skipping initialization.");
}
if (settings.AllowWirelessNetwork)
{
AddWirelessNetworkControl();
}
if (systemInfo.HasBattery)
{
AddPowerSupplyControl();
} }
return OperationResult.Success; return OperationResult.Success;
@ -100,29 +105,36 @@ namespace SafeExamBrowser.Client.Operations
public OperationResult Revert() public OperationResult Revert()
{ {
logger.Info("Terminating taskbar...");
StatusChanged?.Invoke(TextKey.OperationStatus_TerminateTaskbar); StatusChanged?.Invoke(TextKey.OperationStatus_TerminateTaskbar);
aboutController.Terminate(); if (settings.EnableTaskbar)
if (settings.AllowApplicationLog)
{ {
logController.Terminate(); logger.Info("Terminating taskbar...");
aboutController.Terminate();
if (settings.AllowApplicationLog)
{
logController.Terminate();
}
if (settings.AllowKeyboardLayout)
{
keyboardLayout.Terminate();
}
if (settings.AllowWirelessNetwork)
{
wirelessNetwork.Terminate();
}
if (systemInfo.HasBattery)
{
powerSupply.Terminate();
}
} }
else
if (settings.AllowKeyboardLayout)
{ {
keyboardLayout.Terminate(); logger.Info("Taskbar was disabled, skipping termination.");
}
if (settings.AllowWirelessNetwork)
{
wirelessNetwork.Terminate();
}
if (systemInfo.HasBattery)
{
powerSupply.Terminate();
} }
return OperationResult.Success; return OperationResult.Success;

View file

@ -72,6 +72,7 @@
<ItemGroup> <ItemGroup>
<Compile Include="App.cs" /> <Compile Include="App.cs" />
<Compile Include="ClientController.cs" /> <Compile Include="ClientController.cs" />
<Compile Include="Operations\ActionCenterOperation.cs" />
<Compile Include="Operations\ClientHostDisconnectionOperation.cs" /> <Compile Include="Operations\ClientHostDisconnectionOperation.cs" />
<Compile Include="Operations\ConfigurationOperation.cs" /> <Compile Include="Operations\ConfigurationOperation.cs" />
<Compile Include="Operations\InitializationOperation.cs" /> <Compile Include="Operations\InitializationOperation.cs" />

View file

@ -88,6 +88,8 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
{ {
var settings = new Settings(); var settings = new Settings();
settings.ActionCenter.EnableActionCenter = true;
settings.Browser.StartUrl = "https://www.safeexambrowser.org/start"; settings.Browser.StartUrl = "https://www.safeexambrowser.org/start";
settings.Browser.AllowConfigurationDownloads = true; settings.Browser.AllowConfigurationDownloads = true;
settings.Browser.AllowDeveloperConsole = false; settings.Browser.AllowDeveloperConsole = false;
@ -139,12 +141,12 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
settings.Taskbar.AllowApplicationLog = false; settings.Taskbar.AllowApplicationLog = false;
settings.Taskbar.AllowKeyboardLayout = true; settings.Taskbar.AllowKeyboardLayout = true;
settings.Taskbar.AllowWirelessNetwork = false; settings.Taskbar.AllowWirelessNetwork = false;
settings.Taskbar.EnableTaskbar = true;
settings.Taskbar.ShowClock = true; settings.Taskbar.ShowClock = true;
// TODO: Default values for testing of alpha version only, remove for final release! // TODO: Default values for testing of alpha version only, remove for final release!
settings.Browser.AllowDeveloperConsole = true; settings.Browser.AllowDeveloperConsole = true;
settings.Browser.MainWindowSettings.AllowAddressBar = true; settings.Browser.MainWindowSettings.AllowAddressBar = true;
settings.KioskMode = KioskMode.None;
settings.Taskbar.AllowApplicationLog = true; settings.Taskbar.AllowApplicationLog = true;
return settings; return settings;

View file

@ -6,7 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
using SafeExamBrowser.Contracts.UserInterface.Taskbar; using SafeExamBrowser.Contracts.UserInterface.Shell;
namespace SafeExamBrowser.Contracts.Applications namespace SafeExamBrowser.Contracts.Applications
{ {

View file

@ -11,7 +11,7 @@ using SafeExamBrowser.Contracts.Core;
namespace SafeExamBrowser.Contracts.Applications namespace SafeExamBrowser.Contracts.Applications
{ {
/// <summary> /// <summary>
/// The information about a (third-party) application which can be accessed via the <see cref="UserInterface.Taskbar.ITaskbar"/>. /// The information about a (third-party) application which can be accessed via the shell.
/// </summary> /// </summary>
public interface IApplicationInfo public interface IApplicationInfo
{ {

View file

@ -12,7 +12,7 @@ using SafeExamBrowser.Contracts.UserInterface.Windows;
namespace SafeExamBrowser.Contracts.Applications namespace SafeExamBrowser.Contracts.Applications
{ {
/// <summary> /// <summary>
/// Defines an instance of a (third-party) application which can be accessed via the <see cref="UserInterface.Taskbar.ITaskbar"/>. /// Defines an instance of a (third-party) application which can be accessed via the shell.
/// </summary> /// </summary>
public interface IApplicationInstance public interface IApplicationInstance
{ {

View file

@ -6,7 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
using SafeExamBrowser.Contracts.UserInterface.Taskbar; using SafeExamBrowser.Contracts.UserInterface.Shell;
namespace SafeExamBrowser.Contracts.Client namespace SafeExamBrowser.Contracts.Client
{ {

View file

@ -11,7 +11,7 @@ using SafeExamBrowser.Contracts.Core;
namespace SafeExamBrowser.Contracts.Client namespace SafeExamBrowser.Contracts.Client
{ {
/// <summary> /// <summary>
/// The information about a notification which is part of the <see cref="UserInterface.Taskbar.ITaskbar"/>. /// The information about a notification.
/// </summary> /// </summary>
public interface INotificationInfo public interface INotificationInfo
{ {

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* 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 System;
namespace SafeExamBrowser.Contracts.Configuration.Settings
{
/// <summary>
/// Defines all configuration options for the <see cref="UserInterface.Shell.IActionCenter"/>.
/// </summary>
[Serializable]
public class ActionCenterSettings
{
/// <summary>
/// Determines whether the action center itself is enabled and visible to the user.
/// </summary>
public bool EnableActionCenter { get; set; }
}
}

View file

@ -17,6 +17,11 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings
[Serializable] [Serializable]
public class Settings public class Settings
{ {
/// <summary>
/// All action center-related settings.
/// </summary>
public ActionCenterSettings ActionCenter { get; set; }
/// <summary> /// <summary>
/// The hash code of the administrator password for the settings. /// The hash code of the administrator password for the settings.
/// </summary> /// </summary>
@ -74,6 +79,7 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings
public Settings() public Settings()
{ {
ActionCenter = new ActionCenterSettings();
Browser = new BrowserSettings(); Browser = new BrowserSettings();
Keyboard = new KeyboardSettings(); Keyboard = new KeyboardSettings();
Mouse = new MouseSettings(); Mouse = new MouseSettings();

View file

@ -11,7 +11,7 @@ using System;
namespace SafeExamBrowser.Contracts.Configuration.Settings namespace SafeExamBrowser.Contracts.Configuration.Settings
{ {
/// <summary> /// <summary>
/// Defines all configuration options for the <see cref="UserInterface.Taskbar.ITaskbar"/>. /// Defines all configuration options for the <see cref="UserInterface.Shell.ITaskbar"/>.
/// </summary> /// </summary>
[Serializable] [Serializable]
public class TaskbarSettings public class TaskbarSettings
@ -31,6 +31,11 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings
/// </summary> /// </summary>
public bool AllowWirelessNetwork { get; set; } public bool AllowWirelessNetwork { get; set; }
/// <summary>
/// Determines whether the taskbar itself is enabled and visible to the user.
/// </summary>
public bool EnableTaskbar { get; set; }
/// <summary> /// <summary>
/// Determines whether the current date and time will be rendered in the taskbar. /// Determines whether the current date and time will be rendered in the taskbar.
/// </summary> /// </summary>

View file

@ -73,6 +73,7 @@
<Compile Include="Configuration\DataResources\IResourceLoader.cs" /> <Compile Include="Configuration\DataResources\IResourceLoader.cs" />
<Compile Include="Configuration\DataResources\IResourceSaver.cs" /> <Compile Include="Configuration\DataResources\IResourceSaver.cs" />
<Compile Include="Configuration\SaveStatus.cs" /> <Compile Include="Configuration\SaveStatus.cs" />
<Compile Include="Configuration\Settings\ActionCenterSettings.cs" />
<Compile Include="Configuration\Settings\BrowserWindowSettings.cs" /> <Compile Include="Configuration\Settings\BrowserWindowSettings.cs" />
<Compile Include="Configuration\Settings\UserInterfaceMode.cs" /> <Compile Include="Configuration\Settings\UserInterfaceMode.cs" />
<Compile Include="Applications\Events\IconChangedEventHandler.cs" /> <Compile Include="Applications\Events\IconChangedEventHandler.cs" />
@ -186,26 +187,29 @@
<Compile Include="UserInterface\Browser\IBrowserWindow.cs" /> <Compile Include="UserInterface\Browser\IBrowserWindow.cs" />
<Compile Include="UserInterface\MessageBox\IMessageBox.cs" /> <Compile Include="UserInterface\MessageBox\IMessageBox.cs" />
<Compile Include="UserInterface\IProgressIndicator.cs" /> <Compile Include="UserInterface\IProgressIndicator.cs" />
<Compile Include="UserInterface\Taskbar\Events\ApplicationButtonClickedEventHandler.cs" /> <Compile Include="UserInterface\Shell\Events\ActivatorEventHandler.cs" />
<Compile Include="UserInterface\Taskbar\Events\KeyboardLayoutSelectedEventHandler.cs" /> <Compile Include="UserInterface\Shell\Events\ApplicationButtonClickedEventHandler.cs" />
<Compile Include="UserInterface\Taskbar\Events\NotificationButtonClickedEventHandler.cs" /> <Compile Include="UserInterface\Shell\Events\KeyboardLayoutSelectedEventHandler.cs" />
<Compile Include="UserInterface\Taskbar\Events\WindowClosingEventHandler.cs" /> <Compile Include="UserInterface\Shell\Events\NotificationButtonClickedEventHandler.cs" />
<Compile Include="UserInterface\Taskbar\Events\WirelessNetworkSelectedEventHandler.cs" /> <Compile Include="UserInterface\Shell\IActionCenter.cs" />
<Compile Include="UserInterface\Taskbar\Events\QuitButtonClickedEventHandler.cs" /> <Compile Include="UserInterface\Shell\IActionCenterActivator.cs" />
<Compile Include="UserInterface\Windows\Events\WindowClosingEventHandler.cs" />
<Compile Include="UserInterface\Shell\Events\WirelessNetworkSelectedEventHandler.cs" />
<Compile Include="UserInterface\Shell\Events\QuitButtonClickedEventHandler.cs" />
<Compile Include="UserInterface\Windows\IPasswordDialog.cs" /> <Compile Include="UserInterface\Windows\IPasswordDialog.cs" />
<Compile Include="UserInterface\Windows\IPasswordDialogResult.cs" /> <Compile Include="UserInterface\Windows\IPasswordDialogResult.cs" />
<Compile Include="UserInterface\Windows\IRuntimeWindow.cs" /> <Compile Include="UserInterface\Windows\IRuntimeWindow.cs" />
<Compile Include="UserInterface\MessageBox\MessageBoxResult.cs" /> <Compile Include="UserInterface\MessageBox\MessageBoxResult.cs" />
<Compile Include="UserInterface\Taskbar\INotificationButton.cs" /> <Compile Include="UserInterface\Shell\INotificationButton.cs" />
<Compile Include="UserInterface\Windows\ISplashScreen.cs" /> <Compile Include="UserInterface\Windows\ISplashScreen.cs" />
<Compile Include="UserInterface\Taskbar\ISystemKeyboardLayoutControl.cs" /> <Compile Include="UserInterface\Shell\ISystemKeyboardLayoutControl.cs" />
<Compile Include="UserInterface\Taskbar\ISystemPowerSupplyControl.cs" /> <Compile Include="UserInterface\Shell\ISystemPowerSupplyControl.cs" />
<Compile Include="UserInterface\Taskbar\ISystemControl.cs" /> <Compile Include="UserInterface\Shell\ISystemControl.cs" />
<Compile Include="UserInterface\Taskbar\ISystemWirelessNetworkControl.cs" /> <Compile Include="UserInterface\Shell\ISystemWirelessNetworkControl.cs" />
<Compile Include="UserInterface\Taskbar\ITaskbar.cs" /> <Compile Include="UserInterface\Shell\ITaskbar.cs" />
<Compile Include="I18n\ITextResource.cs" /> <Compile Include="I18n\ITextResource.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UserInterface\Taskbar\IApplicationButton.cs" /> <Compile Include="UserInterface\Shell\IApplicationButton.cs" />
<Compile Include="UserInterface\IUserInterfaceFactory.cs" /> <Compile Include="UserInterface\IUserInterfaceFactory.cs" />
<Compile Include="UserInterface\Windows\IWindow.cs" /> <Compile Include="UserInterface\Windows\IWindow.cs" />
<Compile Include="UserInterface\MessageBox\MessageBoxAction.cs" /> <Compile Include="UserInterface\MessageBox\MessageBoxAction.cs" />

View file

@ -6,7 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
using SafeExamBrowser.Contracts.UserInterface.Taskbar; using SafeExamBrowser.Contracts.UserInterface.Shell;
namespace SafeExamBrowser.Contracts.SystemComponents namespace SafeExamBrowser.Contracts.SystemComponents
{ {

View file

@ -13,7 +13,7 @@ using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface.Browser; using SafeExamBrowser.Contracts.UserInterface.Browser;
using SafeExamBrowser.Contracts.UserInterface.Taskbar; using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Windows; using SafeExamBrowser.Contracts.UserInterface.Windows;
namespace SafeExamBrowser.Contracts.UserInterface namespace SafeExamBrowser.Contracts.UserInterface

View file

@ -0,0 +1,15 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* 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/.
*/
namespace SafeExamBrowser.Contracts.UserInterface.Shell.Events
{
/// <summary>
/// Event handler fired by an <see cref="IActionCenterActivator"/> to control the visibility of the <see cref="IActionCenter"/>.
/// </summary>
public delegate void ActivatorEventHandler();
}

View file

@ -8,7 +8,7 @@
using SafeExamBrowser.Contracts.Applications; using SafeExamBrowser.Contracts.Applications;
namespace SafeExamBrowser.Contracts.UserInterface.Taskbar.Events namespace SafeExamBrowser.Contracts.UserInterface.Shell.Events
{ {
/// <summary> /// <summary>
/// Indicates that an <see cref="IApplicationButton"/> has been clicked, optionally specifying the ID of the selected instance (if /// Indicates that an <see cref="IApplicationButton"/> has been clicked, optionally specifying the ID of the selected instance (if

View file

@ -8,7 +8,7 @@
using SafeExamBrowser.Contracts.SystemComponents; using SafeExamBrowser.Contracts.SystemComponents;
namespace SafeExamBrowser.Contracts.UserInterface.Taskbar.Events namespace SafeExamBrowser.Contracts.UserInterface.Shell.Events
{ {
/// <summary> /// <summary>
/// Indicates that a particular <see cref="IKeyboardLayout"/> has been selected by the user. /// Indicates that a particular <see cref="IKeyboardLayout"/> has been selected by the user.

View file

@ -6,7 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
namespace SafeExamBrowser.Contracts.UserInterface.Taskbar.Events namespace SafeExamBrowser.Contracts.UserInterface.Shell.Events
{ {
/// <summary> /// <summary>
/// Indicates that the user clicked on a <see cref="INotificationButton"/> in the <see cref="ITaskbar"/>. /// Indicates that the user clicked on a <see cref="INotificationButton"/> in the <see cref="ITaskbar"/>.

View file

@ -8,7 +8,7 @@
using System.ComponentModel; using System.ComponentModel;
namespace SafeExamBrowser.Contracts.UserInterface.Taskbar.Events namespace SafeExamBrowser.Contracts.UserInterface.Shell.Events
{ {
/// <summary> /// <summary>
/// Event handler used to define the control flow when the <see cref="ITaskbar"/>'s quit button is clicked. /// Event handler used to define the control flow when the <see cref="ITaskbar"/>'s quit button is clicked.

View file

@ -8,7 +8,7 @@
using SafeExamBrowser.Contracts.SystemComponents; using SafeExamBrowser.Contracts.SystemComponents;
namespace SafeExamBrowser.Contracts.UserInterface.Taskbar.Events namespace SafeExamBrowser.Contracts.UserInterface.Shell.Events
{ {
/// <summary> /// <summary>
/// Indicates that a particular <see cref="IWirelessNetwork"/> has been selected by the user. /// Indicates that a particular <see cref="IWirelessNetwork"/> has been selected by the user.

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* 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/.
*/
namespace SafeExamBrowser.Contracts.UserInterface.Shell
{
/// <summary>
/// The action center is a user interface element via which the user can access and control various aspects of the application.
/// </summary>
public interface IActionCenter
{
/// <summary>
/// Closes the action center.
/// </summary>
void Close();
/// <summary>
/// Makes the action center invisible.
/// </summary>
void Hide();
/// <summary>
/// Registers the specified activator to control the visibility of the action center.
/// </summary>
void Register(IActionCenterActivator activator);
/// <summary>
/// Makes the action center visible.
/// </summary>
void Show();
}
}

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* 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.Contracts.UserInterface.Shell.Events;
namespace SafeExamBrowser.Contracts.UserInterface.Shell
{
/// <summary>
/// A module which can be used to control the visibility of the <see cref="IActionCenter"/>.
/// </summary>
public interface IActionCenterActivator
{
/// <summary>
/// Fired when the action center should be made visible.
/// </summary>
event ActivatorEventHandler Activate;
/// <summary>
/// Fired when the action center should be made invisible.
/// </summary>
event ActivatorEventHandler Deactivate;
/// <summary>
/// Fired when the action center visibility should be toggled.
/// </summary>
event ActivatorEventHandler Toggle;
/// <summary>
/// Starts monitoring user input events.
/// </summary>
void Start();
/// <summary>
/// Stops monitoring user input events.
/// </summary>
void Stop();
}
}

View file

@ -7,9 +7,9 @@
*/ */
using SafeExamBrowser.Contracts.Applications; using SafeExamBrowser.Contracts.Applications;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events; using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
namespace SafeExamBrowser.Contracts.UserInterface.Taskbar namespace SafeExamBrowser.Contracts.UserInterface.Shell
{ {
/// <summary> /// <summary>
/// The button of a (third-party) application which can be loaded into the <see cref="ITaskbar"/>. /// The button of a (third-party) application which can be loaded into the <see cref="ITaskbar"/>.

View file

@ -6,9 +6,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events; using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
namespace SafeExamBrowser.Contracts.UserInterface.Taskbar namespace SafeExamBrowser.Contracts.UserInterface.Shell
{ {
/// <summary> /// <summary>
/// The button of a notification which can be loaded into the <see cref="ITaskbar"/>. /// The button of a notification which can be loaded into the <see cref="ITaskbar"/>.

View file

@ -6,7 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
namespace SafeExamBrowser.Contracts.UserInterface.Taskbar namespace SafeExamBrowser.Contracts.UserInterface.Shell
{ {
/// <summary> /// <summary>
/// The control of a system component which can be loaded into the <see cref="ITaskbar"/>. /// The control of a system component which can be loaded into the <see cref="ITaskbar"/>.

View file

@ -7,9 +7,9 @@
*/ */
using SafeExamBrowser.Contracts.SystemComponents; using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events; using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
namespace SafeExamBrowser.Contracts.UserInterface.Taskbar namespace SafeExamBrowser.Contracts.UserInterface.Shell
{ {
/// <summary> /// <summary>
/// The control of the keyboard layout system component. /// The control of the keyboard layout system component.

View file

@ -8,7 +8,7 @@
using SafeExamBrowser.Contracts.SystemComponents; using SafeExamBrowser.Contracts.SystemComponents;
namespace SafeExamBrowser.Contracts.UserInterface.Taskbar namespace SafeExamBrowser.Contracts.UserInterface.Shell
{ {
/// <summary> /// <summary>
/// The control of the power supply system component. /// The control of the power supply system component.

View file

@ -8,9 +8,9 @@
using System.Collections.Generic; using System.Collections.Generic;
using SafeExamBrowser.Contracts.SystemComponents; using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events; using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
namespace SafeExamBrowser.Contracts.UserInterface.Taskbar namespace SafeExamBrowser.Contracts.UserInterface.Shell
{ {
/// <summary> /// <summary>
/// The control of the wireless network system component. /// The control of the wireless network system component.

View file

@ -6,13 +6,12 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events; using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
namespace SafeExamBrowser.Contracts.UserInterface.Taskbar namespace SafeExamBrowser.Contracts.UserInterface.Shell
{ {
/// <summary> /// <summary>
/// Defines the functionality of the application taskbar. The taskbar is the main user interface element via which the user can access /// The taskbar is a user interface element via which the user can access and control various aspects of the application.
/// the browser, third-party applications, system controls and so on.
/// </summary> /// </summary>
public interface ITaskbar public interface ITaskbar
{ {
@ -55,5 +54,10 @@ namespace SafeExamBrowser.Contracts.UserInterface.Taskbar
/// Moves the taskbar to the bottom of and resizes it according to the current working area. /// Moves the taskbar to the bottom of and resizes it according to the current working area.
/// </summary> /// </summary>
void InitializeBounds(); void InitializeBounds();
/// <summary>
/// Shows the taskbar.
/// </summary>
void Show();
} }
} }

View file

@ -6,7 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
namespace SafeExamBrowser.Contracts.UserInterface.Taskbar.Events namespace SafeExamBrowser.Contracts.UserInterface.Windows.Events
{ {
/// <summary> /// <summary>
/// Indicates that a window is about to be closed. /// Indicates that a window is about to be closed.

View file

@ -6,7 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events; using SafeExamBrowser.Contracts.UserInterface.Windows.Events;
namespace SafeExamBrowser.Contracts.UserInterface.Windows namespace SafeExamBrowser.Contracts.UserInterface.Windows
{ {

View file

@ -89,7 +89,18 @@ namespace SafeExamBrowser.Runtime
var bootstrapSequence = new OperationSequence(logger, bootstrapOperations); var bootstrapSequence = new OperationSequence(logger, bootstrapOperations);
var sessionSequence = new RepeatableOperationSequence(logger, sessionOperations); var sessionSequence = new RepeatableOperationSequence(logger, sessionOperations);
RuntimeController = new RuntimeController(appConfig, logger, messageBox, bootstrapSequence, sessionSequence, runtimeHost, serviceProxy, sessionContext, shutdown, text, uiFactory); RuntimeController = new RuntimeController(
appConfig,
logger,
messageBox,
bootstrapSequence,
sessionSequence,
runtimeHost,
serviceProxy,
sessionContext,
shutdown,
text,
uiFactory);
} }
internal void LogStartupInformation() internal void LogStartupInformation()
@ -126,11 +137,30 @@ namespace SafeExamBrowser.Runtime
var xmlParser = new XmlParser(ModuleLogger(nameof(XmlParser))); var xmlParser = new XmlParser(ModuleLogger(nameof(XmlParser)));
var xmlSerializer = new XmlSerializer(ModuleLogger(nameof(XmlSerializer))); var xmlSerializer = new XmlSerializer(ModuleLogger(nameof(XmlSerializer)));
configuration = new ConfigurationRepository(certificateStore, new HashAlgorithm(), repositoryLogger, executable.Location, programCopyright, programTitle, programVersion); configuration = new ConfigurationRepository(
certificateStore,
new HashAlgorithm(),
repositoryLogger,
executable.Location,
programCopyright,
programTitle,
programVersion);
appConfig = configuration.InitializeAppConfig(); appConfig = configuration.InitializeAppConfig();
configuration.Register(new BinaryParser(compressor, new HashAlgorithm(), ModuleLogger(nameof(BinaryParser)), passwordEncryption, publicKeyEncryption, symmetricEncryption, xmlParser)); configuration.Register(new BinaryParser(
configuration.Register(new BinarySerializer(compressor, ModuleLogger(nameof(BinarySerializer)), passwordEncryption, publicKeyEncryption, symmetricEncryption, xmlSerializer)); compressor,
new HashAlgorithm(),
ModuleLogger(nameof(BinaryParser)),
passwordEncryption,
publicKeyEncryption,
symmetricEncryption, xmlParser));
configuration.Register(new BinarySerializer(
compressor,
ModuleLogger(nameof(BinarySerializer)),
passwordEncryption,
publicKeyEncryption,
symmetricEncryption,
xmlSerializer));
configuration.Register(new XmlParser(ModuleLogger(nameof(XmlParser)))); configuration.Register(new XmlParser(ModuleLogger(nameof(XmlParser))));
configuration.Register(new XmlSerializer(ModuleLogger(nameof(XmlSerializer)))); configuration.Register(new XmlSerializer(ModuleLogger(nameof(XmlSerializer))));
configuration.Register(new FileResourceLoader(ModuleLogger(nameof(FileResourceLoader)))); configuration.Register(new FileResourceLoader(ModuleLogger(nameof(FileResourceLoader))));

View file

@ -12,7 +12,7 @@ using System.Windows.Forms;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.SystemComponents; using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface.Taskbar; using SafeExamBrowser.Contracts.UserInterface.Shell;
namespace SafeExamBrowser.SystemComponents namespace SafeExamBrowser.SystemComponents
{ {

View file

@ -11,7 +11,7 @@ using System.Timers;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.SystemComponents; using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface.Taskbar; using SafeExamBrowser.Contracts.UserInterface.Shell;
using PowerLineStatus = System.Windows.Forms.PowerLineStatus; using PowerLineStatus = System.Windows.Forms.PowerLineStatus;
using SystemInformation = System.Windows.Forms.SystemInformation; using SystemInformation = System.Windows.Forms.SystemInformation;

View file

@ -13,7 +13,7 @@ using System.Timers;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.SystemComponents; using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface.Taskbar; using SafeExamBrowser.Contracts.UserInterface.Shell;
using SimpleWifi; using SimpleWifi;
using SimpleWifi.Win32; using SimpleWifi.Win32;
using SimpleWifi.Win32.Interop; using SimpleWifi.Win32.Interop;

View file

@ -10,8 +10,8 @@ using System.Windows;
using System.Windows.Documents; using System.Windows.Documents;
using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events;
using SafeExamBrowser.Contracts.UserInterface.Windows; using SafeExamBrowser.Contracts.UserInterface.Windows;
using SafeExamBrowser.Contracts.UserInterface.Windows.Events;
namespace SafeExamBrowser.UserInterface.Desktop namespace SafeExamBrowser.UserInterface.Desktop
{ {

View file

@ -0,0 +1,12 @@
<Window x:Class="SafeExamBrowser.UserInterface.Desktop.ActionCenter"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop"
mc:Ignorable="d" Title="ActionCenter" Height="1000" Width="400" Background="#EEF0F0F0" AllowsTransparency="True" WindowStyle="None"
Topmost="True" ResizeMode="NoResize">
<Grid>
</Grid>
</Window>

View file

@ -0,0 +1,137 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* 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 System;
using System.Windows;
using System.Windows.Media.Animation;
using SafeExamBrowser.Contracts.UserInterface.Shell;
namespace SafeExamBrowser.UserInterface.Desktop
{
public partial class ActionCenter : Window, IActionCenter
{
public ActionCenter()
{
InitializeComponent();
RegisterEvents();
}
public new void Close()
{
Dispatcher.Invoke(base.Close);
}
public new void Hide()
{
Dispatcher.Invoke(HideAnimated);
}
public void Register(IActionCenterActivator activator)
{
activator.Activate += Activator_Activate;
activator.Deactivate += Activator_Deactivate;
activator.Toggle += Activator_Toggle;
}
public new void Show()
{
Dispatcher.Invoke(ShowAnimated);
}
private void HideAnimated()
{
var storyboard = new Storyboard();
var animation = new DoubleAnimation
{
EasingFunction = new CircleEase { EasingMode = EasingMode.EaseOut },
From = 0,
To = -Width,
Duration = new Duration(TimeSpan.FromMilliseconds(500))
};
Storyboard.SetTarget(animation, this);
Storyboard.SetTargetProperty(animation, new PropertyPath(LeftProperty));
storyboard.Children.Add(animation);
storyboard.Completed += (o, args) => Dispatcher.Invoke(base.Hide);
storyboard.Begin();
}
private void ShowAnimated()
{
var storyboard = new Storyboard();
var animation = new DoubleAnimation
{
EasingFunction = new CircleEase { EasingMode = EasingMode.EaseOut },
From = -Width,
To = 0,
Duration = new Duration(TimeSpan.FromMilliseconds(500))
};
Storyboard.SetTarget(animation, this);
Storyboard.SetTargetProperty(animation, new PropertyPath(LeftProperty));
InitializeBounds();
base.Show();
Activate();
storyboard.Children.Add(animation);
storyboard.Completed += (o, args) => Dispatcher.Invoke(Activate);
storyboard.Begin();
}
private void InitializeBounds()
{
Height = SystemParameters.WorkArea.Height;
Top = 0;
Left = -Width;
}
private void RegisterEvents()
{
Deactivated += (o, args) => HideAnimated();
}
private void Activator_Activate()
{
Dispatcher.InvokeAsync(() =>
{
if (Visibility != Visibility.Visible)
{
ShowAnimated();
}
});
}
private void Activator_Deactivate()
{
Dispatcher.InvokeAsync(() =>
{
if (Visibility == Visibility.Visible)
{
HideAnimated();
}
});
}
private void Activator_Toggle()
{
Dispatcher.InvokeAsync(() =>
{
if (Visibility != Visibility.Visible)
{
ShowAnimated();
}
else
{
HideAnimated();
}
});
}
}
}

View file

@ -18,8 +18,8 @@ using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.Browser; using SafeExamBrowser.Contracts.UserInterface.Browser;
using SafeExamBrowser.Contracts.UserInterface.Browser.Events; using SafeExamBrowser.Contracts.UserInterface.Browser.Events;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events;
using SafeExamBrowser.Contracts.UserInterface.Windows; using SafeExamBrowser.Contracts.UserInterface.Windows;
using SafeExamBrowser.Contracts.UserInterface.Windows.Events;
using SafeExamBrowser.UserInterface.Desktop.Utilities; using SafeExamBrowser.UserInterface.Desktop.Utilities;
namespace SafeExamBrowser.UserInterface.Desktop namespace SafeExamBrowser.UserInterface.Desktop
@ -117,33 +117,12 @@ namespace SafeExamBrowser.UserInterface.Desktop
private void InitializeBrowserWindow(IBrowserControl browserControl) private void InitializeBrowserWindow(IBrowserControl browserControl)
{ {
var originalBrush = MenuButton.Background;
if (browserControl is System.Windows.Forms.Control control) if (browserControl is System.Windows.Forms.Control control)
{ {
BrowserControlHost.Child = control; BrowserControlHost.Child = control;
} }
BackwardButton.Click += (o, args) => BackwardNavigationRequested?.Invoke(); RegisterEvents();
Closing += (o, args) => closing?.Invoke();
ForwardButton.Click += (o, args) => ForwardNavigationRequested?.Invoke();
MenuButton.Click += (o, args) => MenuPopup.IsOpen = !MenuPopup.IsOpen;
MenuButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = MenuPopup.IsMouseOver));
MenuPopup.Closed += (o, args) => { MenuButton.Background = originalBrush; };
MenuPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = IsMouseOver));
MenuPopup.Opened += (o, args) => { MenuButton.Background = Brushes.LightGray; };
KeyUp += BrowserWindow_KeyUp;
ReloadButton.Click += (o, args) => ReloadRequested?.Invoke();
UrlTextBox.GotKeyboardFocus += (o, args) => UrlTextBox.SelectAll();
UrlTextBox.GotMouseCapture += UrlTextBox_GotMouseCapture;
UrlTextBox.LostKeyboardFocus += (o, args) => UrlTextBox.Tag = null;
UrlTextBox.LostFocus += (o, args) => UrlTextBox.Tag = null;
UrlTextBox.KeyUp += UrlTextBox_KeyUp;
UrlTextBox.MouseDoubleClick += (o, args) => UrlTextBox.SelectAll();
ZoomInButton.Click += (o, args) => ZoomInRequested?.Invoke();
ZoomOutButton.Click += (o, args) => ZoomOutRequested?.Invoke();
ZoomResetButton.Click += (o, args) => ZoomResetRequested?.Invoke();
ApplySettings(); ApplySettings();
LoadIcons(); LoadIcons();
LoadText(); LoadText();
@ -174,6 +153,31 @@ namespace SafeExamBrowser.UserInterface.Desktop
} }
} }
private void RegisterEvents()
{
var originalBrush = MenuButton.Background;
BackwardButton.Click += (o, args) => BackwardNavigationRequested?.Invoke();
Closing += (o, args) => closing?.Invoke();
ForwardButton.Click += (o, args) => ForwardNavigationRequested?.Invoke();
MenuButton.Click += (o, args) => MenuPopup.IsOpen = !MenuPopup.IsOpen;
MenuButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = MenuPopup.IsMouseOver));
MenuPopup.Closed += (o, args) => { MenuButton.Background = originalBrush; };
MenuPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = IsMouseOver));
MenuPopup.Opened += (o, args) => { MenuButton.Background = Brushes.LightGray; };
KeyUp += BrowserWindow_KeyUp;
ReloadButton.Click += (o, args) => ReloadRequested?.Invoke();
UrlTextBox.GotKeyboardFocus += (o, args) => UrlTextBox.SelectAll();
UrlTextBox.GotMouseCapture += UrlTextBox_GotMouseCapture;
UrlTextBox.LostKeyboardFocus += (o, args) => UrlTextBox.Tag = null;
UrlTextBox.LostFocus += (o, args) => UrlTextBox.Tag = null;
UrlTextBox.KeyUp += UrlTextBox_KeyUp;
UrlTextBox.MouseDoubleClick += (o, args) => UrlTextBox.SelectAll();
ZoomInButton.Click += (o, args) => ZoomInRequested?.Invoke();
ZoomOutButton.Click += (o, args) => ZoomOutRequested?.Invoke();
ZoomResetButton.Click += (o, args) => ZoomResetRequested?.Invoke();
}
private void ApplySettings() private void ApplySettings()
{ {
if (isMainWindow) if (isMainWindow)

View file

@ -14,8 +14,8 @@ using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Threading; using System.Windows.Threading;
using SafeExamBrowser.Contracts.Applications; using SafeExamBrowser.Contracts.Applications;
using SafeExamBrowser.Contracts.UserInterface.Taskbar; using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events; using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
using SafeExamBrowser.UserInterface.Desktop.Utilities; using SafeExamBrowser.UserInterface.Desktop.Utilities;
namespace SafeExamBrowser.UserInterface.Desktop.Controls namespace SafeExamBrowser.UserInterface.Desktop.Controls

View file

@ -10,7 +10,7 @@ using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using SafeExamBrowser.Contracts.Applications; using SafeExamBrowser.Contracts.Applications;
using SafeExamBrowser.Contracts.Core; using SafeExamBrowser.Contracts.Core;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events; using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
using SafeExamBrowser.UserInterface.Desktop.Utilities; using SafeExamBrowser.UserInterface.Desktop.Utilities;
namespace SafeExamBrowser.UserInterface.Desktop.Controls namespace SafeExamBrowser.UserInterface.Desktop.Controls

View file

@ -12,8 +12,8 @@ using System.Threading.Tasks;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Media;
using SafeExamBrowser.Contracts.SystemComponents; using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface.Taskbar; using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events; using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
namespace SafeExamBrowser.UserInterface.Desktop.Controls namespace SafeExamBrowser.UserInterface.Desktop.Controls
{ {

View file

@ -9,8 +9,8 @@
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using SafeExamBrowser.Contracts.Client; using SafeExamBrowser.Contracts.Client;
using SafeExamBrowser.Contracts.UserInterface.Taskbar; using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events; using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
using SafeExamBrowser.UserInterface.Desktop.Utilities; using SafeExamBrowser.UserInterface.Desktop.Utilities;
namespace SafeExamBrowser.UserInterface.Desktop.Controls namespace SafeExamBrowser.UserInterface.Desktop.Controls

View file

@ -11,7 +11,7 @@ using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Threading; using System.Windows.Threading;
using SafeExamBrowser.Contracts.SystemComponents; using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface.Taskbar; using SafeExamBrowser.Contracts.UserInterface.Shell;
namespace SafeExamBrowser.UserInterface.Desktop.Controls namespace SafeExamBrowser.UserInterface.Desktop.Controls
{ {

View file

@ -10,7 +10,7 @@ using System;
using System.ComponentModel; using System.ComponentModel;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events; using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
using SafeExamBrowser.UserInterface.Desktop.Utilities; using SafeExamBrowser.UserInterface.Desktop.Utilities;
namespace SafeExamBrowser.UserInterface.Desktop.Controls namespace SafeExamBrowser.UserInterface.Desktop.Controls

View file

@ -14,8 +14,8 @@ using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Media;
using FontAwesome.WPF; using FontAwesome.WPF;
using SafeExamBrowser.Contracts.SystemComponents; using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface.Taskbar; using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events; using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
using SafeExamBrowser.UserInterface.Desktop.Utilities; using SafeExamBrowser.UserInterface.Desktop.Utilities;
namespace SafeExamBrowser.UserInterface.Desktop.Controls namespace SafeExamBrowser.UserInterface.Desktop.Controls

View file

@ -10,8 +10,8 @@ using System.ComponentModel;
using System.Windows; using System.Windows;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events;
using SafeExamBrowser.Contracts.UserInterface.Windows; using SafeExamBrowser.Contracts.UserInterface.Windows;
using SafeExamBrowser.Contracts.UserInterface.Windows.Events;
using SafeExamBrowser.UserInterface.Desktop.ViewModels; using SafeExamBrowser.UserInterface.Desktop.ViewModels;
namespace SafeExamBrowser.UserInterface.Desktop namespace SafeExamBrowser.UserInterface.Desktop

View file

@ -9,8 +9,8 @@
using System.Windows; using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events;
using SafeExamBrowser.Contracts.UserInterface.Windows; using SafeExamBrowser.Contracts.UserInterface.Windows;
using SafeExamBrowser.Contracts.UserInterface.Windows.Events;
namespace SafeExamBrowser.UserInterface.Desktop namespace SafeExamBrowser.UserInterface.Desktop
{ {

View file

@ -11,8 +11,8 @@ using System.Windows.Documents;
using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events;
using SafeExamBrowser.Contracts.UserInterface.Windows; using SafeExamBrowser.Contracts.UserInterface.Windows;
using SafeExamBrowser.Contracts.UserInterface.Windows.Events;
using SafeExamBrowser.UserInterface.Desktop.ViewModels; using SafeExamBrowser.UserInterface.Desktop.ViewModels;
namespace SafeExamBrowser.UserInterface.Desktop namespace SafeExamBrowser.UserInterface.Desktop

View file

@ -68,6 +68,9 @@
<Compile Include="AboutWindow.xaml.cs"> <Compile Include="AboutWindow.xaml.cs">
<DependentUpon>AboutWindow.xaml</DependentUpon> <DependentUpon>AboutWindow.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="ActionCenter.xaml.cs">
<DependentUpon>ActionCenter.xaml</DependentUpon>
</Compile>
<Compile Include="BrowserWindow.xaml.cs"> <Compile Include="BrowserWindow.xaml.cs">
<DependentUpon>BrowserWindow.xaml</DependentUpon> <DependentUpon>BrowserWindow.xaml</DependentUpon>
</Compile> </Compile>
@ -126,6 +129,10 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="ActionCenter.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="BrowserWindow.xaml"> <Page Include="BrowserWindow.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>

View file

@ -10,8 +10,8 @@ using System.Windows;
using System.Windows.Documents; using System.Windows.Documents;
using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events;
using SafeExamBrowser.Contracts.UserInterface.Windows; using SafeExamBrowser.Contracts.UserInterface.Windows;
using SafeExamBrowser.Contracts.UserInterface.Windows.Events;
using SafeExamBrowser.UserInterface.Desktop.ViewModels; using SafeExamBrowser.UserInterface.Desktop.ViewModels;
namespace SafeExamBrowser.UserInterface.Desktop namespace SafeExamBrowser.UserInterface.Desktop

View file

@ -5,9 +5,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop.Controls" xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop.Controls"
xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:s="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d" mc:Ignorable="d" Title="Taskbar" Background="#FFF0F0F0" Height="40" Width="750" WindowStyle="None" Topmost="True" ResizeMode="NoResize" Icon="./Images/SafeExamBrowser.ico">
Title="Taskbar" Background="#FFF0F0F0" Height="40" Width="750" WindowStyle="None" Topmost="True" Visibility="Visible"
ResizeMode="NoResize" Icon="./Images/SafeExamBrowser.ico">
<Window.Resources> <Window.Resources>
<ResourceDictionary> <ResourceDictionary>
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>

View file

@ -9,8 +9,8 @@
using System.ComponentModel; using System.ComponentModel;
using System.Windows; using System.Windows;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface.Taskbar; using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events; using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
using SafeExamBrowser.UserInterface.Desktop.Utilities; using SafeExamBrowser.UserInterface.Desktop.Utilities;
namespace SafeExamBrowser.UserInterface.Desktop namespace SafeExamBrowser.UserInterface.Desktop
@ -94,6 +94,11 @@ namespace SafeExamBrowser.UserInterface.Desktop
}); });
} }
public new void Show()
{
Dispatcher.Invoke(base.Show);
}
private void QuitButton_Clicked(CancelEventArgs args) private void QuitButton_Clicked(CancelEventArgs args)
{ {
QuitButtonClicked?.Invoke(args); QuitButtonClicked?.Invoke(args);

View file

@ -18,7 +18,7 @@ using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.Browser; using SafeExamBrowser.Contracts.UserInterface.Browser;
using SafeExamBrowser.Contracts.UserInterface.Taskbar; using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Windows; using SafeExamBrowser.Contracts.UserInterface.Windows;
using SafeExamBrowser.UserInterface.Desktop.Controls; using SafeExamBrowser.UserInterface.Desktop.Controls;

View file

@ -14,7 +14,7 @@ namespace SafeExamBrowser.WindowsApi.Constants
/// See https://docs.microsoft.com/de-de/windows/desktop/SecAuthZ/access-mask /// See https://docs.microsoft.com/de-de/windows/desktop/SecAuthZ/access-mask
/// </remarks> /// </remarks>
[Flags] [Flags]
internal enum ACCESS_MASK : uint internal enum AccessMask : uint
{ {
DESKTOP_NONE = 0, DESKTOP_NONE = 0,
DESKTOP_READOBJECTS = 0x0001, DESKTOP_READOBJECTS = 0x0001,

View file

@ -32,6 +32,20 @@ namespace SafeExamBrowser.WindowsApi.Constants
/// </summary> /// </summary>
internal const int MIN_ALL = 419; internal const int MIN_ALL = 419;
/// <summary>
/// Bitmask to evaluate the origin of a mouse event.
///
/// See https://docs.microsoft.com/en-us/windows/desktop/tablet/system-events-and-mouse-messages.
/// </summary>
internal const uint MOUSEEVENTF_MASK = 0xFFFFFF00;
/// <summary>
/// The constant for a mouse event generated by a touch interface.
///
/// See https://docs.microsoft.com/en-us/windows/desktop/tablet/system-events-and-mouse-messages.
/// </summary>
internal const uint MOUSEEVENTF_FROMTOUCH = 0xFF515700;
/// <summary> /// <summary>
/// Specifies the default priority class for processes, i.e. a process with no special scheduling needs. /// Specifies the default priority class for processes, i.e. a process with no special scheduling needs.
/// ///

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* 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/.
*/
namespace SafeExamBrowser.WindowsApi.Constants
{
/// <remarks>
/// See https://docs.microsoft.com/en-us/windows/desktop/inputdev/virtual-key-codes.
/// </remarks>
internal enum VirtualKeyCode
{
A = 0x41,
Delete = 0x2E,
LeftAlt = 0xA4,
LeftControl = 0xA2,
LeftWindows = 0x5B,
RightAlt = 0xA5,
RightControl = 0xA3
}
}

View file

@ -13,5 +13,5 @@ namespace SafeExamBrowser.WindowsApi.Delegates
/// <remarks> /// <remarks>
/// See https://docs.microsoft.com/de-de/windows/desktop/api/winuser/nc-winuser-hookproc /// See https://docs.microsoft.com/de-de/windows/desktop/api/winuser/nc-winuser-hookproc
/// </remarks> /// </remarks>
internal delegate IntPtr HookDelegate(int code, IntPtr wParam, IntPtr lParam); internal delegate IntPtr HookDelegate(int nCode, IntPtr wParam, IntPtr lParam);
} }

View file

@ -28,7 +28,7 @@ namespace SafeExamBrowser.WindowsApi
{ {
logger.Debug($"Attempting to create new desktop '{name}'..."); logger.Debug($"Attempting to create new desktop '{name}'...");
var handle = User32.CreateDesktop(name, IntPtr.Zero, IntPtr.Zero, 0, (uint) ACCESS_MASK.GENERIC_ALL, IntPtr.Zero); var handle = User32.CreateDesktop(name, IntPtr.Zero, IntPtr.Zero, 0, (uint) AccessMask.GENERIC_ALL, IntPtr.Zero);
if (handle == IntPtr.Zero) if (handle == IntPtr.Zero)
{ {

View file

@ -0,0 +1,110 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* 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 System;
using System.Runtime.InteropServices;
using System.Threading;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
using SafeExamBrowser.WindowsApi.Constants;
using SafeExamBrowser.WindowsApi.Delegates;
using SafeExamBrowser.WindowsApi.Types;
namespace SafeExamBrowser.WindowsApi
{
public class KeyboardActivator : IActionCenterActivator
{
private bool A, LeftWindows;
private IntPtr handle;
private HookDelegate hookDelegate;
private ILogger logger;
public event ActivatorEventHandler Activate { add { } remove { } }
public event ActivatorEventHandler Deactivate { add { } remove { } }
public event ActivatorEventHandler Toggle;
public KeyboardActivator(ILogger logger)
{
this.logger = logger;
}
public void Start()
{
var hookReadyEvent = new AutoResetEvent(false);
var hookThread = new Thread(() =>
{
var sleepEvent = new AutoResetEvent(false);
var process = System.Diagnostics.Process.GetCurrentProcess();
var module = process.MainModule;
var moduleHandle = Kernel32.GetModuleHandle(module.ModuleName);
// IMPORTANT:
// Ensures that the hook delegate does not get garbage collected prematurely, as it will be passed to unmanaged code.
// Not doing so will result in a <c>CallbackOnCollectedDelegate</c> error and subsequent application crash!
hookDelegate = new HookDelegate(LowLevelKeyboardProc);
handle = User32.SetWindowsHookEx(HookType.WH_KEYBOARD_LL, hookDelegate, moduleHandle, 0);
hookReadyEvent.Set();
while (true)
{
sleepEvent.WaitOne();
}
});
hookThread.SetApartmentState(ApartmentState.STA);
hookThread.IsBackground = true;
hookThread.Start();
hookReadyEvent.WaitOne();
}
public void Stop()
{
User32.UnhookWindowsHookEx(handle);
}
private IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0)
{
var changed = false;
var keyData = (KBDLLHOOKSTRUCT) Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));
var pressed = IsPressed(wParam.ToInt32());
switch (keyData.KeyCode)
{
case (uint) VirtualKeyCode.A:
changed = A != pressed;
A = pressed;
break;
case (uint) VirtualKeyCode.LeftWindows:
changed = LeftWindows != pressed;
LeftWindows = pressed;
break;
}
if (A && LeftWindows && changed)
{
logger.Debug("Detected toggle sequence for action center.");
Toggle?.Invoke();
return (IntPtr) 1;
}
}
return User32.CallNextHookEx(handle, nCode, wParam, lParam);
}
private bool IsPressed(int wParam)
{
return wParam == Constant.WM_KEYDOWN || wParam == Constant.WM_SYSKEYDOWN;
}
}
}

View file

@ -17,12 +17,6 @@ namespace SafeExamBrowser.WindowsApi.Monitoring
{ {
internal class KeyboardHook internal class KeyboardHook
{ {
private const int LEFT_CTRL = 162;
private const int RIGHT_CTRL = 163;
private const int LEFT_ALT = 164;
private const int RIGHT_ALT = 165;
private const int DELETE = 46;
private bool altPressed, ctrlPressed; private bool altPressed, ctrlPressed;
private HookDelegate hookDelegate; private HookDelegate hookDelegate;
@ -40,7 +34,7 @@ namespace SafeExamBrowser.WindowsApi.Monitoring
var module = process.MainModule; var module = process.MainModule;
var moduleHandle = Kernel32.GetModuleHandle(module.ModuleName); var moduleHandle = Kernel32.GetModuleHandle(module.ModuleName);
// IMORTANT: // IMPORTANT:
// Ensures that the hook delegate does not get garbage collected prematurely, as it will be passed to unmanaged code. // Ensures that the hook delegate does not get garbage collected prematurely, as it will be passed to unmanaged code.
// Not doing so will result in a <c>CallbackOnCollectedDelegate</c> error and subsequent application crash! // Not doing so will result in a <c>CallbackOnCollectedDelegate</c> error and subsequent application crash!
hookDelegate = new HookDelegate(LowLevelKeyboardProc); hookDelegate = new HookDelegate(LowLevelKeyboardProc);
@ -108,16 +102,16 @@ namespace SafeExamBrowser.WindowsApi.Monitoring
{ {
var keyCode = keyData.KeyCode; var keyCode = keyData.KeyCode;
if (keyCode == LEFT_CTRL || keyCode == RIGHT_CTRL) if (keyCode == (uint) VirtualKeyCode.LeftControl || keyCode == (uint) VirtualKeyCode.RightControl)
{ {
ctrlPressed = IsPressed(wParam); ctrlPressed = IsPressed(wParam);
} }
else if (keyCode == LEFT_ALT || keyCode == RIGHT_ALT) else if (keyCode == (uint) VirtualKeyCode.LeftAlt || keyCode == (uint) VirtualKeyCode.RightAlt)
{ {
altPressed = IsPressed(wParam); altPressed = IsPressed(wParam);
} }
if (ctrlPressed && altPressed && keyCode == DELETE) if (ctrlPressed && altPressed && keyCode == (uint) VirtualKeyCode.Delete)
{ {
// When the Secure Attention Sequence is pressed, the WM_KEYUP / WM_SYSKEYUP messages for CTRL and ALT get lost... // When the Secure Attention Sequence is pressed, the WM_KEYUP / WM_SYSKEYUP messages for CTRL and ALT get lost...
ctrlPressed = false; ctrlPressed = false;

View file

@ -33,7 +33,7 @@ namespace SafeExamBrowser.WindowsApi.Monitoring
var module = process.MainModule; var module = process.MainModule;
var moduleHandle = Kernel32.GetModuleHandle(module.ModuleName); var moduleHandle = Kernel32.GetModuleHandle(module.ModuleName);
// IMORTANT: // IMPORTANT:
// Ensures that the hook delegate does not get garbage collected prematurely, as it will be passed to unmanaged code. // Ensures that the hook delegate does not get garbage collected prematurely, as it will be passed to unmanaged code.
// Not doing so will result in a <c>CallbackOnCollectedDelegate</c> error and subsequent application crash! // Not doing so will result in a <c>CallbackOnCollectedDelegate</c> error and subsequent application crash!
hookDelegate = new HookDelegate(LowLevelMouseProc); hookDelegate = new HookDelegate(LowLevelMouseProc);

View file

@ -36,7 +36,7 @@ namespace SafeExamBrowser.WindowsApi.Monitoring
internal void Attach() internal void Attach()
{ {
// IMORTANT: // IMPORTANT:
// Ensures that the hook delegate does not get garbage collected prematurely, as it will be passed to unmanaged code. // Ensures that the hook delegate does not get garbage collected prematurely, as it will be passed to unmanaged code.
// Not doing so will result in a <c>CallbackOnCollectedDelegate</c> error and subsequent application crash! // Not doing so will result in a <c>CallbackOnCollectedDelegate</c> error and subsequent application crash!
eventDelegate = new EventDelegate(LowLevelSystemProc); eventDelegate = new EventDelegate(LowLevelSystemProc);

View file

@ -54,6 +54,7 @@
<Compile Include="Constants\Constant.cs" /> <Compile Include="Constants\Constant.cs" />
<Compile Include="Constants\HookType.cs" /> <Compile Include="Constants\HookType.cs" />
<Compile Include="Constants\ThreadAccess.cs" /> <Compile Include="Constants\ThreadAccess.cs" />
<Compile Include="Constants\VirtualKeyCode.cs" />
<Compile Include="Delegates\EnumDesktopDelegate.cs" /> <Compile Include="Delegates\EnumDesktopDelegate.cs" />
<Compile Include="Delegates\EnumWindowsDelegate.cs" /> <Compile Include="Delegates\EnumWindowsDelegate.cs" />
<Compile Include="Delegates\EventDelegate.cs" /> <Compile Include="Delegates\EventDelegate.cs" />
@ -61,11 +62,13 @@
<Compile Include="Desktop.cs" /> <Compile Include="Desktop.cs" />
<Compile Include="DesktopFactory.cs" /> <Compile Include="DesktopFactory.cs" />
<Compile Include="ExplorerShell.cs" /> <Compile Include="ExplorerShell.cs" />
<Compile Include="KeyboardActivator.cs" />
<Compile Include="Monitoring\MouseHook.cs" /> <Compile Include="Monitoring\MouseHook.cs" />
<Compile Include="Monitoring\SystemHook.cs" /> <Compile Include="Monitoring\SystemHook.cs" />
<Compile Include="Process.cs" /> <Compile Include="Process.cs" />
<Compile Include="ProcessFactory.cs" /> <Compile Include="ProcessFactory.cs" />
<Compile Include="Constants\ACCESS_MASK.cs" /> <Compile Include="Constants\AccessMask.cs" />
<Compile Include="TouchActivator.cs" />
<Compile Include="Types\Bounds.cs" /> <Compile Include="Types\Bounds.cs" />
<Compile Include="Types\EXECUTION_STATE.cs" /> <Compile Include="Types\EXECUTION_STATE.cs" />
<Compile Include="Types\KBDLLHOOKSTRUCT.cs" /> <Compile Include="Types\KBDLLHOOKSTRUCT.cs" />

View file

@ -0,0 +1,121 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* 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 System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
using SafeExamBrowser.WindowsApi.Constants;
using SafeExamBrowser.WindowsApi.Delegates;
using SafeExamBrowser.WindowsApi.Types;
namespace SafeExamBrowser.WindowsApi
{
public class TouchActivator : IActionCenterActivator
{
private HookDelegate hookDelegate;
private IntPtr handle;
private bool isDown;
private ILogger logger;
public event ActivatorEventHandler Activate;
public event ActivatorEventHandler Deactivate { add { } remove { } }
public event ActivatorEventHandler Toggle { add { } remove { } }
public TouchActivator(ILogger logger)
{
this.logger = logger;
}
public void Start()
{
var hookReadyEvent = new AutoResetEvent(false);
var hookThread = new Thread(() =>
{
var sleepEvent = new AutoResetEvent(false);
var process = System.Diagnostics.Process.GetCurrentProcess();
var module = process.MainModule;
var moduleHandle = Kernel32.GetModuleHandle(module.ModuleName);
// IMPORTANT:
// Ensures that the hook delegate does not get garbage collected prematurely, as it will be passed to unmanaged code.
// Not doing so will result in a <c>CallbackOnCollectedDelegate</c> error and subsequent application crash!
hookDelegate = new HookDelegate(LowLevelMouseProc);
handle = User32.SetWindowsHookEx(HookType.WH_MOUSE_LL, hookDelegate, moduleHandle, 0);
hookReadyEvent.Set();
while (true)
{
sleepEvent.WaitOne();
}
});
hookThread.SetApartmentState(ApartmentState.STA);
hookThread.IsBackground = true;
hookThread.Start();
hookReadyEvent.WaitOne();
}
public void Stop()
{
User32.UnhookWindowsHookEx(handle);
}
private IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && !Ignore(wParam.ToInt32()))
{
var message = wParam.ToInt32();
var mouseData = (MSLLHOOKSTRUCT) Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
var position = $"{mouseData.Point.X}/{mouseData.Point.Y}";
var extraInfo = mouseData.DwExtraInfo.ToUInt32();
var isTouch = (extraInfo & Constant.MOUSEEVENTF_MASK) == Constant.MOUSEEVENTF_FROMTOUCH;
if (isTouch)
{
if (message == Constant.WM_LBUTTONUP)
{
isDown = false;
}
if (message == Constant.WM_LBUTTONDOWN && 0 < mouseData.Point.X && mouseData.Point.X < 50)
{
isDown = true;
Task.Delay(100).ContinueWith(_ => CheckPosition());
}
}
}
return User32.CallNextHookEx(handle, nCode, wParam, lParam);
}
private void CheckPosition()
{
var position = new POINT();
User32.GetCursorPos(ref position);
if (isDown && position.X > 200)
{
logger.Debug("Detected activation gesture for action center.");
Activate?.Invoke();
}
}
private bool Ignore(int wParam)
{
// For performance reasons, ignore mouse movement and wheel rotation...
return wParam == Constant.WM_MOUSEMOVE || wParam == Constant.WM_MOUSEWHEEL;
}
}
}

View file

@ -48,6 +48,9 @@ namespace SafeExamBrowser.WindowsApi
[DllImport("user32.dll", SetLastError = true)] [DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr FindWindow(string lpClassName, string lpWindowName); internal static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool GetCursorPos(ref POINT pt);
[DllImport("user32.dll", SetLastError = true)] [DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr GetThreadDesktop(int dwThreadId); internal static extern IntPtr GetThreadDesktop(int dwThreadId);