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.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.MessageBox;
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
using SafeExamBrowser.Contracts.UserInterface.Shell;
using BrowserSettings = SafeExamBrowser.Contracts.Configuration.Settings.BrowserSettings;
namespace SafeExamBrowser.Browser

View file

@ -24,7 +24,7 @@ using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring;
using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.MessageBox;
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Windows;
using SafeExamBrowser.Contracts.WindowsApi;
@ -34,6 +34,7 @@ namespace SafeExamBrowser.Client.UnitTests
public class ClientControllerTests
{
private AppConfig appConfig;
private Mock<IActionCenter> actionCenter;
private Mock<IBrowserApplicationController> browserController;
private Mock<IClientHost> clientHost;
private Mock<IDisplayMonitor> displayMonitor;
@ -58,6 +59,7 @@ namespace SafeExamBrowser.Client.UnitTests
public void Initialize()
{
appConfig = new AppConfig();
actionCenter = new Mock<IActionCenter>();
browserController = new Mock<IBrowserApplicationController>();
clientHost = new Mock<IClientHost>();
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);
sut = new ClientController(
actionCenter.Object,
displayMonitor.Object,
explorerShell.Object,
hashAlgorithm.Object,
@ -632,6 +635,21 @@ namespace SafeExamBrowser.Client.UnitTests
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]
public void WindowMonitor_MustHandleAllowedWindowChangeCorrectly()
{

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -9,7 +9,7 @@
using SafeExamBrowser.Contracts.Client;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Windows;
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.Logging;
using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
using SafeExamBrowser.Contracts.UserInterface.Shell;
namespace SafeExamBrowser.Client.Operations
{

View file

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

View file

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

View file

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

View file

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

View file

@ -6,7 +6,7 @@
* 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
{

View file

@ -11,7 +11,7 @@ using SafeExamBrowser.Contracts.Core;
namespace SafeExamBrowser.Contracts.Applications
{
/// <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>
public interface IApplicationInfo
{

View file

@ -12,7 +12,7 @@ using SafeExamBrowser.Contracts.UserInterface.Windows;
namespace SafeExamBrowser.Contracts.Applications
{
/// <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>
public interface IApplicationInstance
{

View file

@ -6,7 +6,7 @@
* 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
{

View file

@ -11,7 +11,7 @@ using SafeExamBrowser.Contracts.Core;
namespace SafeExamBrowser.Contracts.Client
{
/// <summary>
/// The information about a notification which is part of the <see cref="UserInterface.Taskbar.ITaskbar"/>.
/// The information about a notification.
/// </summary>
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]
public class Settings
{
/// <summary>
/// All action center-related settings.
/// </summary>
public ActionCenterSettings ActionCenter { get; set; }
/// <summary>
/// The hash code of the administrator password for the settings.
/// </summary>
@ -74,6 +79,7 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings
public Settings()
{
ActionCenter = new ActionCenterSettings();
Browser = new BrowserSettings();
Keyboard = new KeyboardSettings();
Mouse = new MouseSettings();

View file

@ -11,7 +11,7 @@ using System;
namespace SafeExamBrowser.Contracts.Configuration.Settings
{
/// <summary>
/// Defines all configuration options for the <see cref="UserInterface.Taskbar.ITaskbar"/>.
/// Defines all configuration options for the <see cref="UserInterface.Shell.ITaskbar"/>.
/// </summary>
[Serializable]
public class TaskbarSettings
@ -31,6 +31,11 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings
/// </summary>
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>
/// Determines whether the current date and time will be rendered in the taskbar.
/// </summary>

View file

@ -73,6 +73,7 @@
<Compile Include="Configuration\DataResources\IResourceLoader.cs" />
<Compile Include="Configuration\DataResources\IResourceSaver.cs" />
<Compile Include="Configuration\SaveStatus.cs" />
<Compile Include="Configuration\Settings\ActionCenterSettings.cs" />
<Compile Include="Configuration\Settings\BrowserWindowSettings.cs" />
<Compile Include="Configuration\Settings\UserInterfaceMode.cs" />
<Compile Include="Applications\Events\IconChangedEventHandler.cs" />
@ -186,26 +187,29 @@
<Compile Include="UserInterface\Browser\IBrowserWindow.cs" />
<Compile Include="UserInterface\MessageBox\IMessageBox.cs" />
<Compile Include="UserInterface\IProgressIndicator.cs" />
<Compile Include="UserInterface\Taskbar\Events\ApplicationButtonClickedEventHandler.cs" />
<Compile Include="UserInterface\Taskbar\Events\KeyboardLayoutSelectedEventHandler.cs" />
<Compile Include="UserInterface\Taskbar\Events\NotificationButtonClickedEventHandler.cs" />
<Compile Include="UserInterface\Taskbar\Events\WindowClosingEventHandler.cs" />
<Compile Include="UserInterface\Taskbar\Events\WirelessNetworkSelectedEventHandler.cs" />
<Compile Include="UserInterface\Taskbar\Events\QuitButtonClickedEventHandler.cs" />
<Compile Include="UserInterface\Shell\Events\ActivatorEventHandler.cs" />
<Compile Include="UserInterface\Shell\Events\ApplicationButtonClickedEventHandler.cs" />
<Compile Include="UserInterface\Shell\Events\KeyboardLayoutSelectedEventHandler.cs" />
<Compile Include="UserInterface\Shell\Events\NotificationButtonClickedEventHandler.cs" />
<Compile Include="UserInterface\Shell\IActionCenter.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\IPasswordDialogResult.cs" />
<Compile Include="UserInterface\Windows\IRuntimeWindow.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\Taskbar\ISystemKeyboardLayoutControl.cs" />
<Compile Include="UserInterface\Taskbar\ISystemPowerSupplyControl.cs" />
<Compile Include="UserInterface\Taskbar\ISystemControl.cs" />
<Compile Include="UserInterface\Taskbar\ISystemWirelessNetworkControl.cs" />
<Compile Include="UserInterface\Taskbar\ITaskbar.cs" />
<Compile Include="UserInterface\Shell\ISystemKeyboardLayoutControl.cs" />
<Compile Include="UserInterface\Shell\ISystemPowerSupplyControl.cs" />
<Compile Include="UserInterface\Shell\ISystemControl.cs" />
<Compile Include="UserInterface\Shell\ISystemWirelessNetworkControl.cs" />
<Compile Include="UserInterface\Shell\ITaskbar.cs" />
<Compile Include="I18n\ITextResource.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\Windows\IWindow.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/.
*/
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
using SafeExamBrowser.Contracts.UserInterface.Shell;
namespace SafeExamBrowser.Contracts.SystemComponents
{

View file

@ -13,7 +13,7 @@ using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface.Browser;
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Windows;
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;
namespace SafeExamBrowser.Contracts.UserInterface.Taskbar.Events
namespace SafeExamBrowser.Contracts.UserInterface.Shell.Events
{
/// <summary>
/// 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;
namespace SafeExamBrowser.Contracts.UserInterface.Taskbar.Events
namespace SafeExamBrowser.Contracts.UserInterface.Shell.Events
{
/// <summary>
/// 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/.
*/
namespace SafeExamBrowser.Contracts.UserInterface.Taskbar.Events
namespace SafeExamBrowser.Contracts.UserInterface.Shell.Events
{
/// <summary>
/// Indicates that the user clicked on a <see cref="INotificationButton"/> in the <see cref="ITaskbar"/>.

View file

@ -8,7 +8,7 @@
using System.ComponentModel;
namespace SafeExamBrowser.Contracts.UserInterface.Taskbar.Events
namespace SafeExamBrowser.Contracts.UserInterface.Shell.Events
{
/// <summary>
/// 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;
namespace SafeExamBrowser.Contracts.UserInterface.Taskbar.Events
namespace SafeExamBrowser.Contracts.UserInterface.Shell.Events
{
/// <summary>
/// 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.UserInterface.Taskbar.Events;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
namespace SafeExamBrowser.Contracts.UserInterface.Taskbar
namespace SafeExamBrowser.Contracts.UserInterface.Shell
{
/// <summary>
/// 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/.
*/
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
namespace SafeExamBrowser.Contracts.UserInterface.Taskbar
namespace SafeExamBrowser.Contracts.UserInterface.Shell
{
/// <summary>
/// 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/.
*/
namespace SafeExamBrowser.Contracts.UserInterface.Taskbar
namespace SafeExamBrowser.Contracts.UserInterface.Shell
{
/// <summary>
/// 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.UserInterface.Taskbar.Events;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
namespace SafeExamBrowser.Contracts.UserInterface.Taskbar
namespace SafeExamBrowser.Contracts.UserInterface.Shell
{
/// <summary>
/// The control of the keyboard layout system component.

View file

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

View file

@ -8,9 +8,9 @@
using System.Collections.Generic;
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>
/// 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/.
*/
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
namespace SafeExamBrowser.Contracts.UserInterface.Taskbar
namespace SafeExamBrowser.Contracts.UserInterface.Shell
{
/// <summary>
/// Defines the functionality of the application taskbar. The taskbar is the main user interface element via which the user can access
/// the browser, third-party applications, system controls and so on.
/// The taskbar is a user interface element via which the user can access and control various aspects of the application.
/// </summary>
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.
/// </summary>
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/.
*/
namespace SafeExamBrowser.Contracts.UserInterface.Taskbar.Events
namespace SafeExamBrowser.Contracts.UserInterface.Windows.Events
{
/// <summary>
/// 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/.
*/
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events;
using SafeExamBrowser.Contracts.UserInterface.Windows.Events;
namespace SafeExamBrowser.Contracts.UserInterface.Windows
{

View file

@ -89,7 +89,18 @@ namespace SafeExamBrowser.Runtime
var bootstrapSequence = new OperationSequence(logger, bootstrapOperations);
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()
@ -126,11 +137,30 @@ namespace SafeExamBrowser.Runtime
var xmlParser = new XmlParser(ModuleLogger(nameof(XmlParser)));
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();
configuration.Register(new BinaryParser(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 BinaryParser(
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 XmlSerializer(ModuleLogger(nameof(XmlSerializer))));
configuration.Register(new FileResourceLoader(ModuleLogger(nameof(FileResourceLoader))));

View file

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

View file

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

View file

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

View file

@ -10,8 +10,8 @@ using System.Windows;
using System.Windows.Documents;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events;
using SafeExamBrowser.Contracts.UserInterface.Windows;
using SafeExamBrowser.Contracts.UserInterface.Windows.Events;
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.Browser;
using SafeExamBrowser.Contracts.UserInterface.Browser.Events;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events;
using SafeExamBrowser.Contracts.UserInterface.Windows;
using SafeExamBrowser.Contracts.UserInterface.Windows.Events;
using SafeExamBrowser.UserInterface.Desktop.Utilities;
namespace SafeExamBrowser.UserInterface.Desktop
@ -117,33 +117,12 @@ namespace SafeExamBrowser.UserInterface.Desktop
private void InitializeBrowserWindow(IBrowserControl browserControl)
{
var originalBrush = MenuButton.Background;
if (browserControl is System.Windows.Forms.Control control)
{
BrowserControlHost.Child = control;
}
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();
RegisterEvents();
ApplySettings();
LoadIcons();
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()
{
if (isMainWindow)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -9,8 +9,8 @@
using System.ComponentModel;
using System.Windows;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events;
using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
using SafeExamBrowser.UserInterface.Desktop.Utilities;
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)
{
QuitButtonClicked?.Invoke(args);

View file

@ -18,7 +18,7 @@ using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.Browser;
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Windows;
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
/// </remarks>
[Flags]
internal enum ACCESS_MASK : uint
internal enum AccessMask : uint
{
DESKTOP_NONE = 0,
DESKTOP_READOBJECTS = 0x0001,

View file

@ -32,6 +32,20 @@ namespace SafeExamBrowser.WindowsApi.Constants
/// </summary>
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>
/// 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>
/// See https://docs.microsoft.com/de-de/windows/desktop/api/winuser/nc-winuser-hookproc
/// </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}'...");
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)
{

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
{
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 HookDelegate hookDelegate;
@ -40,7 +34,7 @@ namespace SafeExamBrowser.WindowsApi.Monitoring
var module = process.MainModule;
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.
// Not doing so will result in a <c>CallbackOnCollectedDelegate</c> error and subsequent application crash!
hookDelegate = new HookDelegate(LowLevelKeyboardProc);
@ -108,16 +102,16 @@ namespace SafeExamBrowser.WindowsApi.Monitoring
{
var keyCode = keyData.KeyCode;
if (keyCode == LEFT_CTRL || keyCode == RIGHT_CTRL)
if (keyCode == (uint) VirtualKeyCode.LeftControl || keyCode == (uint) VirtualKeyCode.RightControl)
{
ctrlPressed = IsPressed(wParam);
}
else if (keyCode == LEFT_ALT || keyCode == RIGHT_ALT)
else if (keyCode == (uint) VirtualKeyCode.LeftAlt || keyCode == (uint) VirtualKeyCode.RightAlt)
{
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...
ctrlPressed = false;

View file

@ -33,7 +33,7 @@ namespace SafeExamBrowser.WindowsApi.Monitoring
var module = process.MainModule;
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.
// Not doing so will result in a <c>CallbackOnCollectedDelegate</c> error and subsequent application crash!
hookDelegate = new HookDelegate(LowLevelMouseProc);

View file

@ -36,7 +36,7 @@ namespace SafeExamBrowser.WindowsApi.Monitoring
internal void Attach()
{
// IMORTANT:
// 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!
eventDelegate = new EventDelegate(LowLevelSystemProc);

View file

@ -54,6 +54,7 @@
<Compile Include="Constants\Constant.cs" />
<Compile Include="Constants\HookType.cs" />
<Compile Include="Constants\ThreadAccess.cs" />
<Compile Include="Constants\VirtualKeyCode.cs" />
<Compile Include="Delegates\EnumDesktopDelegate.cs" />
<Compile Include="Delegates\EnumWindowsDelegate.cs" />
<Compile Include="Delegates\EventDelegate.cs" />
@ -61,11 +62,13 @@
<Compile Include="Desktop.cs" />
<Compile Include="DesktopFactory.cs" />
<Compile Include="ExplorerShell.cs" />
<Compile Include="KeyboardActivator.cs" />
<Compile Include="Monitoring\MouseHook.cs" />
<Compile Include="Monitoring\SystemHook.cs" />
<Compile Include="Process.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\EXECUTION_STATE.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)]
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)]
internal static extern IntPtr GetThreadDesktop(int dwThreadId);