diff --git a/UnitTestProject1/CoreUnitTests.csproj b/UnitTestProject1/CoreUnitTests.csproj
new file mode 100644
index 0000000..99e4b6a
--- /dev/null
+++ b/UnitTestProject1/CoreUnitTests.csproj
@@ -0,0 +1,19 @@
+
+
+
+ netcoreapp2.1
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/UnitTestProject1/SmarteyeTests.cs b/UnitTestProject1/SmarteyeTests.cs
new file mode 100644
index 0000000..5cccb18
--- /dev/null
+++ b/UnitTestProject1/SmarteyeTests.cs
@@ -0,0 +1,70 @@
+/********************************************************************************************************************************************************
+* @file SmarteyeTests.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using iTrace_Core;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+
+namespace UnitTestProject1
+{
+ [TestClass]
+ public class SmarteyeTests
+ {
+ [TestMethod]
+ public void ValidNetstringPass()
+ {
+ string netstring = "7:jeffrey,";
+ string result = NetstringUtils.TrimSENetstring(netstring);
+
+ Assert.AreEqual(result, "jeffrey");
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(ArgumentOutOfRangeException), "")]
+ public void BadLengthNetstringNoPass()
+ {
+ string netstring = "9:jeffrey,";
+ string result = NetstringUtils.TrimSENetstring(netstring);
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(ArgumentOutOfRangeException), "")]
+ public void ZeroLengthNetstringNoPass()
+ {
+ string netstring = "2:,";
+ string result = NetstringUtils.TrimSENetstring(netstring);
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(ArgumentException),"")]
+ public void NoLengthNetstringNoPass()
+ {
+ string netstring = ":jeffrey,";
+ string result = NetstringUtils.TrimSENetstring(netstring);
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(ArgumentException), "")]
+ public void NoSeperatorNetstringNoPass()
+ {
+ string netstring = "7jeffrey,";
+ string result = NetstringUtils.TrimSENetstring(netstring);
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(ArgumentException), "")]
+ public void NoCommaNetstringNoPass()
+ {
+ string netstring = "7:jeffrey";
+ string result = NetstringUtils.TrimSENetstring(netstring);
+ }
+ }
+}
diff --git a/XUnitTestProject1/UnitTest1.cs b/XUnitTestProject1/UnitTest1.cs
new file mode 100644
index 0000000..48329ae
--- /dev/null
+++ b/XUnitTestProject1/UnitTest1.cs
@@ -0,0 +1,14 @@
+using System;
+using Xunit;
+
+namespace XUnitTestProject1
+{
+ public class UnitTest1
+ {
+ [Fact]
+ public void Test1()
+ {
+ Console.WriteLine("Test");
+ }
+ }
+}
diff --git a/XUnitTestProject1/XUnitTestProject1.csproj b/XUnitTestProject1/XUnitTestProject1.csproj
new file mode 100644
index 0000000..4ec64e7
--- /dev/null
+++ b/XUnitTestProject1/XUnitTestProject1.csproj
@@ -0,0 +1,15 @@
+
+
+
+ netcoreapp2.1
+
+ false
+
+
+
+
+
+
+
+
+
diff --git a/itrace_core.sln b/itrace_core.sln
index 6bcd9b0..c191136 100644
--- a/itrace_core.sln
+++ b/itrace_core.sln
@@ -1,31 +1,41 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.27703.2047
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "itrace_core", "itrace_core\itrace_core.csproj", "{5783FB9E-5502-48CF-89BF-12B3A4DFFCB2}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Debug|x64 = Debug|x64
- Release|Any CPU = Release|Any CPU
- Release|x64 = Release|x64
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {5783FB9E-5502-48CF-89BF-12B3A4DFFCB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {5783FB9E-5502-48CF-89BF-12B3A4DFFCB2}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {5783FB9E-5502-48CF-89BF-12B3A4DFFCB2}.Debug|x64.ActiveCfg = Debug|x64
- {5783FB9E-5502-48CF-89BF-12B3A4DFFCB2}.Debug|x64.Build.0 = Debug|x64
- {5783FB9E-5502-48CF-89BF-12B3A4DFFCB2}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {5783FB9E-5502-48CF-89BF-12B3A4DFFCB2}.Release|Any CPU.Build.0 = Release|Any CPU
- {5783FB9E-5502-48CF-89BF-12B3A4DFFCB2}.Release|x64.ActiveCfg = Release|x64
- {5783FB9E-5502-48CF-89BF-12B3A4DFFCB2}.Release|x64.Build.0 = Release|x64
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {FD8E01EA-52AD-4714-995D-89FF2A3CAE5F}
- EndGlobalSection
-EndGlobal
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.31624.102
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "itrace_core", "itrace_core\itrace_core.csproj", "{5783FB9E-5502-48CF-89BF-12B3A4DFFCB2}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoreUnitTests", "UnitTestProject1\CoreUnitTests.csproj", "{F7B7FC84-7401-4185-BAE0-B78524B26BAE}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {5783FB9E-5502-48CF-89BF-12B3A4DFFCB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5783FB9E-5502-48CF-89BF-12B3A4DFFCB2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5783FB9E-5502-48CF-89BF-12B3A4DFFCB2}.Debug|x64.ActiveCfg = Debug|x64
+ {5783FB9E-5502-48CF-89BF-12B3A4DFFCB2}.Debug|x64.Build.0 = Debug|x64
+ {5783FB9E-5502-48CF-89BF-12B3A4DFFCB2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5783FB9E-5502-48CF-89BF-12B3A4DFFCB2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5783FB9E-5502-48CF-89BF-12B3A4DFFCB2}.Release|x64.ActiveCfg = Release|x64
+ {5783FB9E-5502-48CF-89BF-12B3A4DFFCB2}.Release|x64.Build.0 = Release|x64
+ {F7B7FC84-7401-4185-BAE0-B78524B26BAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F7B7FC84-7401-4185-BAE0-B78524B26BAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F7B7FC84-7401-4185-BAE0-B78524B26BAE}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {F7B7FC84-7401-4185-BAE0-B78524B26BAE}.Debug|x64.Build.0 = Debug|Any CPU
+ {F7B7FC84-7401-4185-BAE0-B78524B26BAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F7B7FC84-7401-4185-BAE0-B78524B26BAE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F7B7FC84-7401-4185-BAE0-B78524B26BAE}.Release|x64.ActiveCfg = Release|Any CPU
+ {F7B7FC84-7401-4185-BAE0-B78524B26BAE}.Release|x64.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {FD8E01EA-52AD-4714-995D-89FF2A3CAE5F}
+ EndGlobalSection
+EndGlobal
diff --git a/itrace_core/App.config b/itrace_core/App.config
index 80b50d4..41449f7 100644
--- a/itrace_core/App.config
+++ b/itrace_core/App.config
@@ -18,6 +18,15 @@
7007
+
+ 192.169.100.42
+
+
+ 5800
+
+
+ 0
+
\ No newline at end of file
diff --git a/itrace_core/App.xaml b/itrace_core/App.xaml
index f601ff7..3fbac2f 100644
--- a/itrace_core/App.xaml
+++ b/itrace_core/App.xaml
@@ -1,4 +1,16 @@
-.
+********************************************************************************************************************************************************/
+-->
+.
+********************************************************************************************************************************************************/
+
+using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
diff --git a/itrace_core/CalibrationResult.cs b/itrace_core/CalibrationResult.cs
index 737d8e7..c874ef2 100644
--- a/itrace_core/CalibrationResult.cs
+++ b/itrace_core/CalibrationResult.cs
@@ -1,5 +1,17 @@
-using System.Collections.Generic;
+/********************************************************************************************************************************************************
+* @file CalibrationResult.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System.Collections.Generic;
using System.Windows;
+using System.Windows.Forms;
using System.Xml;
namespace iTrace_Core
@@ -7,11 +19,16 @@ namespace iTrace_Core
abstract class CalibrationResult
{
public abstract void WriteToXMLWriter(XmlTextWriter xmlTextWriter);
+ public abstract bool IsValid();
}
class EmptyCalibrationResult : CalibrationResult
{
public override void WriteToXMLWriter(XmlTextWriter xmlTextWriter) { }
+ public override bool IsValid() //TODO
+ {
+ throw new System.NotImplementedException();
+ }
}
class TobiiCalibrationResult : CalibrationResult
@@ -29,6 +46,11 @@ public TobiiCalibrationResult(Tobii.Research.CalibrationResult calibrationResult
SessionManager.GetInstance().GenerateCalibrationTimeStamp();
}
+ public override bool IsValid() //TODO
+ {
+ throw new System.NotImplementedException();
+ }
+
public List GetLeftEyePoints()
{
List leftEyePoints = new List();
@@ -121,6 +143,11 @@ public GazePointCalibrationResult(string xmlCalibrationData, int numberOfPoints)
SessionManager.GetInstance().GenerateCalibrationTimeStamp();
}
+ public override bool IsValid() //TODO
+ {
+ throw new System.NotImplementedException();
+ }
+
public override void WriteToXMLWriter(XmlTextWriter xmlTextWriter)
{
XmlNode recNode = XmlDoc.FirstChild;
@@ -154,4 +181,84 @@ public override void WriteToXMLWriter(XmlTextWriter xmlTextWriter)
xmlTextWriter.WriteEndElement();
}
}
+
+ class SmartEyeCalibrationResult : CalibrationResult
+ {
+ //These are the calibration vectors produced by the SE gaze calibration dialogue
+ private List calibrationTargets;
+ private SEWorldModel worldModel;
+ public ScreenMapping screenMapping { get; private set; }
+
+ public SmartEyeCalibrationResult(SEWorldModel worldModel, List calibrationTargets)
+ {
+ //TODO: world model sanity checks
+
+ this.worldModel = worldModel;
+ this.calibrationTargets = calibrationTargets;
+ this.screenMapping = new ScreenMapping(worldModel.GetScreens(), Screen.AllScreens);
+
+ SessionManager.GetInstance().GenerateCalibrationTimeStamp();
+ }
+
+ public override bool IsValid() //TODO
+ {
+ return screenMapping.IsValid() && calibrationTargets.Count > 0;
+ }
+
+ public override void WriteToXMLWriter(XmlTextWriter xmlTextWriter)
+ {
+ worldModel.WriteToXMLWriter(xmlTextWriter);
+
+ xmlTextWriter.WriteStartElement("calibration");
+ xmlTextWriter.WriteAttributeString("timestamp", SessionManager.GetInstance().CurrentCalibrationTimeStamp);
+
+ foreach (SETarget target in calibrationTargets)
+ {
+ xmlTextWriter.WriteStartElement("calibration_point");
+ xmlTextWriter.WriteAttributeString("targetId", target.targetId.ToString());
+
+ //Write X and Y of calibration point as percent of screen size. This may require extracting from the world model string!
+ //xmlTextWriter.WriteAttributeString("x", "0.5");
+ //xmlTextWriter.WriteAttributeString("y", "0.5");
+
+ int max = target.errorsxl.Length >= target.errorsxr.Length ? target.errorsxl.Length : target.errorsxr.Length;
+
+ for (int i = 0; i < max; i++)
+ {
+ xmlTextWriter.WriteStartElement("sample");
+
+ if (i < target.errorsxl.Length)
+ {
+ xmlTextWriter.WriteAttributeString("left_x", target.errorsxl[i].ToString());
+ xmlTextWriter.WriteAttributeString("left_y", target.errorsyl[i].ToString());
+ xmlTextWriter.WriteAttributeString("left_validity", "1");
+ } else
+ {
+ xmlTextWriter.WriteAttributeString("left_x", "0.0");
+ xmlTextWriter.WriteAttributeString("left_y", "0.0");
+ xmlTextWriter.WriteAttributeString("left_validity", "0");
+ }
+
+ if (i < target.errorsxr.Length)
+ {
+ xmlTextWriter.WriteAttributeString("right_x", target.errorsxr[i].ToString());
+ xmlTextWriter.WriteAttributeString("right_y", target.errorsyr[i].ToString());
+ xmlTextWriter.WriteAttributeString("right_validity", "1");
+ }
+ else
+ {
+ xmlTextWriter.WriteAttributeString("right_x", "0.0");
+ xmlTextWriter.WriteAttributeString("right_y", "0.0");
+ xmlTextWriter.WriteAttributeString("right_validity", "0");
+ }
+
+ xmlTextWriter.WriteEndElement();
+ }
+
+ xmlTextWriter.WriteEndElement();
+ }
+
+ xmlTextWriter.WriteEndElement();
+ }
+ }
}
diff --git a/itrace_core/CalibrationWindow.xaml b/itrace_core/CalibrationWindow.xaml
index d510588..139981b 100644
--- a/itrace_core/CalibrationWindow.xaml
+++ b/itrace_core/CalibrationWindow.xaml
@@ -1,4 +1,16 @@
-.
+********************************************************************************************************************************************************/
+-->
+.
+********************************************************************************************************************************************************/
+
+using System;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
@@ -6,6 +17,7 @@
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Threading;
+using iTrace_Core.Properties;
namespace iTrace_Core
{
@@ -49,9 +61,11 @@ private enum AnimationStates
public CalibrationWindow()
{
InitializeComponent();
-
- Left = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Left;
- Top = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Top;
+
+ System.Windows.Forms.Screen calibrationMonitor = System.Windows.Forms.Screen.AllScreens[Settings.Default.calibration_monitor];
+
+ Left = calibrationMonitor.Bounds.Left;
+ Top = calibrationMonitor.Bounds.Top;
}
private void WindowLoaded(object sender, RoutedEventArgs e)
diff --git a/itrace_core/DejaVu/ComputerEventReader.cs b/itrace_core/DejaVu/ComputerEventReader.cs
new file mode 100644
index 0000000..318d989
--- /dev/null
+++ b/itrace_core/DejaVu/ComputerEventReader.cs
@@ -0,0 +1,94 @@
+/********************************************************************************************************************************************************
+* @file ComputerEventReader.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+
+namespace iTrace_Core
+{
+ public class ComputerEventReader
+ {
+ string filename;
+ StreamReader streamReader;
+ EventFactoryMap eventFactory;
+ List events;
+ int current;
+
+ bool skipPause = false;
+
+ public ComputerEventReader(string filename, EventFactoryMap eventFactoryMap)
+ {
+ this.filename = filename;
+ streamReader = new StreamReader(filename);
+ eventFactory = eventFactoryMap;
+ events = new List();
+ string line;
+ while((line = streamReader.ReadLine()) != null)
+ {
+ events.Add(line);
+ }
+ current = 0;
+ streamReader.Close();
+ }
+
+ public bool Finished()
+ {
+ //return streamReader.EndOfStream;
+ return current == events.Count;
+ }
+
+ public ComputerEvent ReadEvent()
+ {
+ //string line = streamReader.ReadLine();
+ string line = events[current];
+
+ ComputerEvent result = eventFactory.GenerateFromCSV(line);
+ if(skipPause)
+ {
+ skipPause = false;
+ //result.PauseStrategy = new FixedLengthPause(0);
+ result.PauseStrategy = new EmptyPause();
+ }
+
+
+ // If the event is a left mouse click, we need to check for a double click
+ if(result.Serialize().Contains("LeftMouseDown,") && current < events.Count - 3)
+ {
+ // Check the next 2 (3?) events to see if a double click is possible
+ string check1 = events[current + 1],
+ check2 = events[current + 2];
+
+ if (check1.Contains("LeftMouseUp,") && check2.Contains("LeftMouseDown,"))
+ {
+ // Next, make sure the last mouse event happened within the click interval
+ long changeInNS = (long.Parse(check2.Split(',')[1]) - long.Parse(events[current].Split(',')[1])) * 100;
+ long doubleClickTimeInNS = System.Windows.Forms.SystemInformation.DoubleClickTime * 1000000;
+ if(changeInNS <= doubleClickTimeInNS)
+ {
+ skipPause = true;
+ //result.PauseStrategy = new FixedLengthPause(0);
+ result.PauseStrategy = new EmptyPause();
+ }
+ }
+ }
+
+ ++current;
+ return result;
+ }
+
+ public void Close()
+ {
+ streamReader.Close();
+ }
+ }
+}
diff --git a/itrace_core/DejaVu/ComputerEventWriter.cs b/itrace_core/DejaVu/ComputerEventWriter.cs
new file mode 100644
index 0000000..4117c0f
--- /dev/null
+++ b/itrace_core/DejaVu/ComputerEventWriter.cs
@@ -0,0 +1,41 @@
+/********************************************************************************************************************************************************
+* @file ComputerEventWriter.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System.IO;
+
+namespace iTrace_Core
+{
+ public class ComputerEventWriter
+ {
+ string filename;
+ StreamWriter streamWriter;
+
+ public ComputerEventWriter(string filename)
+ {
+ this.filename = filename;
+ streamWriter = new StreamWriter(filename);
+ }
+ private object locker = new object();
+ public void Write(ComputerEvent computerEvent)
+ {
+ lock(locker)
+ {
+ streamWriter.Write(computerEvent.Serialize());
+ }
+ }
+
+ public void Close()
+ {
+ streamWriter.Flush();
+ streamWriter.Close();
+ }
+ }
+}
diff --git a/itrace_core/DejaVu/EventRecorder.cs b/itrace_core/DejaVu/EventRecorder.cs
new file mode 100644
index 0000000..a097955
--- /dev/null
+++ b/itrace_core/DejaVu/EventRecorder.cs
@@ -0,0 +1,70 @@
+/********************************************************************************************************************************************************
+* @file EventRecorder.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace iTrace_Core
+{
+ public class EventRecorder
+ {
+ public bool IsRecordInProgress { get; private set; }
+
+ KeyboardHook keyPressListener = new KeyboardHook();
+ MouseHook mouseListener = new MouseHook();
+ CoreClient gazeListener = new CoreClient();
+ ComputerEventWriter eventWriter;
+
+ public EventRecorder(ComputerEventWriter writer)
+ {
+ eventWriter = writer;
+
+ mouseListener.OnMouseEvent += ComputerEventListener;
+ mouseListener.HookMouse();
+
+ keyPressListener.OnKeyboardEvent += ComputerEventListener;
+ keyPressListener.HookKeyboard();
+
+ gazeListener.OnCoreMessage += ComputerEventListener;
+ }
+
+ private void ComputerEventListener(object sender, ComputerEvent e)
+ {
+ if (IsRecordInProgress)
+ eventWriter.Write(e);
+ }
+
+ public void StartRecording()
+ {
+ IsRecordInProgress = true;
+ }
+
+ public void StopRecording()
+ {
+ IsRecordInProgress = false;
+ }
+
+ public void ConnectToCore()
+ {
+ gazeListener.Connect();
+ }
+
+ public void Dispose()
+ {
+ keyPressListener.Dispose();
+ mouseListener.Dispose();
+ eventWriter.Close();
+ }
+ }
+}
diff --git a/itrace_core/DejaVu/EventReplayer.cs b/itrace_core/DejaVu/EventReplayer.cs
new file mode 100644
index 0000000..583e89c
--- /dev/null
+++ b/itrace_core/DejaVu/EventReplayer.cs
@@ -0,0 +1,131 @@
+/********************************************************************************************************************************************************
+* @file EventReplayer.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System;
+using System.Threading;
+using System.Windows.Forms;
+
+namespace iTrace_Core
+{
+ public abstract class EventReplayer
+ {
+ protected ComputerEventReader eventReader;
+ protected Thread replayerThread;
+
+ public bool IsReplayInProgress { get; protected set; }
+
+ public event EventHandler OnReplayFinished;
+
+ protected virtual void Replay()
+ {
+ while (!eventReader.Finished())
+ {
+ ComputerEvent computerEvent = eventReader.ReadEvent();
+ computerEvent.Replay();
+ computerEvent.Pause();
+ //SocketServer.Instance().SendToClients(computerEvent.Serialize());
+ //WebSocketServer.Instance().SendToClients(computerEvent.Serialize());
+ }
+ ReplayDone();
+ }
+
+ protected void ReplayDone()
+ {
+ IsReplayInProgress = false;
+ OnReplayFinished?.Invoke(this, new EventArgs());
+ }
+
+ public void StartReplay()
+ {
+ if (!IsReplayInProgress)
+ {
+ IsReplayInProgress = true;
+
+ replayerThread = new Thread(Replay);
+ replayerThread.Start();
+ }
+ }
+
+ public void StopReplay()
+ {
+ eventReader.Close();
+
+ if (replayerThread != null)
+ {
+ SocketServer.Instance().CancelWait();
+ replayerThread.Abort();
+ }
+ }
+ }
+
+ public class FixedPauseEventReplayer : EventReplayer
+ {
+ public FixedPauseEventReplayer(string filename, int pause)
+ {
+ eventReader = new ComputerEventReader(filename, new FixedPauseEventFactoryMap(pause));
+ }
+ }
+
+ public class BidirectionalCommunicationEventReplayer : EventReplayer
+ {
+ public BidirectionalCommunicationEventReplayer(string filename, int pause)
+ {
+ eventReader = new ComputerEventReader(filename, new BidirectionalCommunicationFactoryMap(pause));
+ }
+ }
+
+ public class ProportionalEventReplayer : EventReplayer
+ {
+ private ComputerEvent nextEvent;
+ private ComputerEvent currentEvent;
+
+ public ProportionalEventReplayer(string filename, int scale)
+ {
+ eventReader = new ComputerEventReader(filename, new ProportionalFactoryMap(scale));
+ }
+
+ // Warning: A minimum of 3 events is required to be present in the file
+ // being read from. Otherwise, this function will crash.
+ protected override void Replay()
+ {
+ currentEvent = eventReader.ReadEvent();
+ nextEvent = eventReader.ReadEvent();
+
+ do
+ {
+ if (currentEvent.PauseStrategy is EmptyPause)
+ {
+ currentEvent.Replay();
+ currentEvent.Pause();
+ }
+ else
+ {
+ ProportionalLengthPause strategy = currentEvent.PauseStrategy as ProportionalLengthPause;
+ strategy.NextEventTime = nextEvent.EventTime;
+
+ currentEvent.Replay();
+ currentEvent.Pause();
+ }
+
+ currentEvent = nextEvent;
+ nextEvent = eventReader.ReadEvent();
+
+ } while (!eventReader.Finished());
+
+ currentEvent.Replay();
+ currentEvent.Pause();
+ nextEvent.Replay();
+
+ ReplayDone();
+ }
+ }
+
+}
diff --git a/itrace_core/DejaVuLib/ComputerEvent.cs b/itrace_core/DejaVuLib/ComputerEvent.cs
new file mode 100644
index 0000000..e933047
--- /dev/null
+++ b/itrace_core/DejaVuLib/ComputerEvent.cs
@@ -0,0 +1,458 @@
+/********************************************************************************************************************************************************
+* @file ComputerEvent.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System;
+using System.Threading;
+using System.Windows.Forms;
+using System.Drawing;
+
+
+
+namespace iTrace_Core
+{
+ public abstract class ComputerEvent : EventArgs
+ {
+ public IPauseStrategy PauseStrategy { get; set; }
+
+ protected ComputerEvent()
+ {
+ EventTime = PreciseSystemTime.GetTime();
+ }
+
+ public long EventTime { get; protected set; }
+
+ public abstract void Replay();
+ public abstract string Serialize();
+ public abstract void DeserializeFrom(string msg);
+
+ public void Pause()
+ {
+ if (PauseStrategy != null)
+ {
+ PauseStrategy.Pause(this);
+ }
+ }
+ }
+
+ public class KeyDown : ComputerEvent
+ {
+ private byte virtualKeyCode;
+
+ public KeyDown() { }
+
+ public KeyDown(byte vk) : base()
+ {
+ virtualKeyCode = vk;
+ }
+
+ public override void Replay()
+ {
+ Win32.keybd_event(virtualKeyCode, 0, 0, 0);
+ }
+
+ public override string Serialize()
+ {
+ return "KeyDown," + EventTime.ToString() + "," + virtualKeyCode + "\n";
+ }
+
+ public override void DeserializeFrom(string msg)
+ {
+ string[] split = msg.Split(',');
+
+ EventTime = Convert.ToInt64(split[1]);
+ virtualKeyCode = Convert.ToByte(split[2]);
+ }
+ }
+
+ public class KeyUp : ComputerEvent
+ {
+ byte virtualKeyCode;
+
+ public KeyUp() { }
+
+ public KeyUp(byte vk) : base()
+ {
+ virtualKeyCode = vk;
+ }
+
+ public override void Replay()
+ {
+ Win32.keybd_event(virtualKeyCode, 0, Win32.KEYEVENTF_KEYUP, 0);
+ }
+
+ public override string Serialize()
+ {
+ return "KeyUp," + EventTime.ToString() + "," + virtualKeyCode + "\n";
+ }
+
+ public override void DeserializeFrom(string msg)
+ {
+ string[] split = msg.Split(',');
+
+ EventTime = Convert.ToInt64(split[1]);
+ virtualKeyCode = Convert.ToByte(split[2]);
+ }
+ }
+
+ public class LeftMouseDown : ComputerEvent
+ {
+ public LeftMouseDown() : base() { }
+
+ public override void Replay()
+ {
+ Win32.mouse_event(Win32.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, (UIntPtr)0);
+ }
+
+ public override string Serialize()
+ {
+ return "LeftMouseDown," + EventTime.ToString() + "\n";
+ }
+
+ public override void DeserializeFrom(string msg)
+ {
+ string[] split = msg.Split(',');
+
+ EventTime = Convert.ToInt64(split[1]);
+ }
+ }
+
+ public class LeftMouseUp : ComputerEvent
+ {
+ public LeftMouseUp() : base() { }
+
+
+ public override void Replay()
+ {
+ Win32.mouse_event(Win32.MOUSEEVENTF_LEFTUP, 0, 0, 0, (UIntPtr)0);
+ }
+
+ public override string Serialize()
+ {
+ return "LeftMouseUp," + EventTime.ToString() + "\n";
+ }
+
+ public override void DeserializeFrom(string msg)
+ {
+ string[] split = msg.Split(',');
+
+ EventTime = Convert.ToInt64(split[1]);
+ }
+ }
+
+ public class RightMouseDown : ComputerEvent
+ {
+ public RightMouseDown() : base() { }
+
+ public override void Replay()
+ {
+ Win32.mouse_event(Win32.MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, (UIntPtr)0);
+ }
+
+ public override string Serialize()
+ {
+ return "RightMouseDown," + EventTime.ToString() + "\n";
+ }
+
+ public override void DeserializeFrom(string msg)
+ {
+ string[] split = msg.Split(',');
+
+ EventTime = Convert.ToInt64(split[1]);
+ }
+ }
+
+ public class RightMouseUp : ComputerEvent
+ {
+ public RightMouseUp() : base() { }
+
+ public override void Replay()
+ {
+ Win32.mouse_event(Win32.MOUSEEVENTF_RIGHTUP, 0, 0, 0, (UIntPtr)0);
+ }
+
+ public override string Serialize()
+ {
+ return "RightMouseUp," + EventTime.ToString() + "\n";
+ }
+
+ public override void DeserializeFrom(string msg)
+ {
+ string[] split = msg.Split(',');
+
+ EventTime = Convert.ToInt64(split[1]);
+ }
+ }
+
+ public class MiddleMouseDown : ComputerEvent
+ {
+ public MiddleMouseDown() : base() { }
+
+ public override void Replay()
+ {
+ Win32.mouse_event(Win32.MOUSEEVENTF_MIDDLEDOWN, 0, 0, 0, (UIntPtr)0);
+ }
+
+ public override string Serialize()
+ {
+ return "MiddleMouseDown," + EventTime.ToString() + "\n";
+ }
+
+ public override void DeserializeFrom(string msg)
+ {
+ string[] split = msg.Split(',');
+
+ EventTime = Convert.ToInt64(split[1]);
+ }
+ }
+
+ public class MiddleMouseUp : ComputerEvent
+ {
+ public MiddleMouseUp() : base() { }
+
+ public override void Replay()
+ {
+ Win32.mouse_event(Win32.MOUSEEVENTF_MIDDLEUP, 0, 0, 0, (UIntPtr)0);
+ }
+
+ public override string Serialize()
+ {
+ return "MiddleMouseUp," + EventTime.ToString() + "\n";
+ }
+
+ public override void DeserializeFrom(string msg)
+ {
+ string[] split = msg.Split(',');
+
+ EventTime = Convert.ToInt64(split[1]);
+ }
+ }
+
+ public class ForwardMouseDown : ComputerEvent
+ {
+ public ForwardMouseDown() : base() { }
+
+ public override void Replay()
+ {
+ Win32.mouse_event(Win32.MOUSEEVENTF_XDOWN, 0, 0, Win32.XBUTTON2, (UIntPtr)0);
+ }
+
+ public override string Serialize()
+ {
+ return "ForwardMouseDown," + EventTime.ToString() + "\n";
+ }
+
+ public override void DeserializeFrom(string msg)
+ {
+ string[] split = msg.Split(',');
+
+ EventTime = Convert.ToInt64(split[1]);
+ }
+ }
+
+ public class ForwardMouseUp : ComputerEvent
+ {
+ public ForwardMouseUp() : base() { }
+
+ public override void Replay()
+ {
+ Win32.mouse_event(Win32.MOUSEEVENTF_XUP, 0, 0, Win32.XBUTTON2, (UIntPtr)0);
+ }
+
+ public override string Serialize()
+ {
+ return "ForwardMouseUp," + EventTime.ToString() + "\n";
+ }
+
+ public override void DeserializeFrom(string msg)
+ {
+ string[] split = msg.Split(',');
+
+ EventTime = Convert.ToInt64(split[1]);
+ }
+ }
+
+
+ public class BackMouseDown : ComputerEvent
+ {
+ public BackMouseDown() : base() { }
+
+ public override void Replay()
+ {
+ Win32.mouse_event(Win32.MOUSEEVENTF_XDOWN, 0, 0, Win32.XBUTTON1, (UIntPtr)0);
+ }
+
+ public override string Serialize()
+ {
+ return "BackMouseDown," + EventTime.ToString() + "\n";
+ }
+
+ public override void DeserializeFrom(string msg)
+ {
+ string[] split = msg.Split(',');
+
+ EventTime = Convert.ToInt64(split[1]);
+ }
+ }
+
+ public class BackMouseUp : ComputerEvent
+ {
+ public BackMouseUp() : base() { }
+
+ public override void Replay()
+ {
+ Win32.mouse_event(Win32.MOUSEEVENTF_XUP, 0, 0, Win32.XBUTTON1, (UIntPtr)0);
+ }
+
+ public override string Serialize()
+ {
+ return "BackMouseUp," + EventTime.ToString() + "\n";
+ }
+
+ public override void DeserializeFrom(string msg)
+ {
+ string[] split = msg.Split(',');
+
+ EventTime = Convert.ToInt64(split[1]);
+ }
+ }
+
+ public class MouseMove : ComputerEvent
+ {
+ int x;
+ int y;
+
+ public MouseMove() { }
+
+ public MouseMove(int newX, int newY) : base()
+ {
+ x = (newX < 0) ? -newX : newX;
+ y = (newY < 0) ? -newY : newY;
+ }
+ public override void Replay()
+ {
+ float dpiX, dpiY;
+ Graphics graphics = Graphics.FromHwnd(IntPtr.Zero);
+ dpiX = graphics.DpiX;
+ dpiY = graphics.DpiY;
+
+ uint defaultDPI = 96; // Considered default screen DPI? Check this
+ //mouse_event wants position to be specified in a weird way - also need to account for screen's DPI
+ uint computedX = Convert.ToUInt32((x / System.Windows.SystemParameters.PrimaryScreenWidth) * 65536.0 * (defaultDPI / dpiX));
+ uint computedY = Convert.ToUInt32((y / System.Windows.SystemParameters.PrimaryScreenHeight) * 65536.0 * (defaultDPI / dpiY));
+
+ Win32.mouse_event(Win32.MOUSEEVENTF_ABSOLUTE | Win32.MOUSEEVENTF_MOVE, computedX, computedY, 0, (UIntPtr)0);
+ }
+
+ public override string Serialize()
+ {
+ return "MouseMove," + EventTime.ToString() + "," + x.ToString() + "," + y.ToString() + "\n";
+ }
+
+ public override void DeserializeFrom(string msg)
+ {
+ string[] split = msg.Split(',');
+
+ EventTime = Convert.ToInt64(split[1]);
+ x = Convert.ToInt32(split[2]);
+ y = Convert.ToInt32(split[3].TrimEnd('\n'));
+ }
+ }
+
+ public class MouseWheel : ComputerEvent
+ {
+ int scrollAmount;
+
+ public MouseWheel() { }
+
+ public MouseWheel(uint rawScrollAmount) : base()
+ {
+ scrollAmount = Convert.ToInt32((rawScrollAmount & 0xffff0000) >> 16);
+
+ if (scrollAmount > SystemInformation.MouseWheelScrollDelta)
+ scrollAmount = scrollAmount - (ushort.MaxValue + 1);
+ }
+
+ public override void Replay()
+ {
+ Win32.mouse_event(Win32.MOUSEEVENTF_WHEEL, 0, 0, (uint)scrollAmount, (UIntPtr)0);
+ }
+
+ public override string Serialize()
+ {
+ return "MouseWheel," + EventTime.ToString() + "," + scrollAmount.ToString() + "\n";
+ }
+
+ public override void DeserializeFrom(string msg)
+ {
+ string[] split = msg.Split(',');
+
+ EventTime = Convert.ToInt64(split[1]);
+ scrollAmount = Convert.ToInt32(split[2]);
+ }
+ }
+
+ public class CoreMessage : ComputerEvent
+ {
+ string message;
+
+ public CoreMessage() { }
+
+ public CoreMessage(string msg) : base()
+ {
+ message = msg;
+ }
+
+ public override void Replay()
+ {
+ SocketServer.Instance().SendToClients(message + "\n");
+ WebSocketServer.Instance().SendToClients(message + "\n");
+ }
+
+ public override string Serialize()
+ {
+ return message + "\n";
+ }
+
+ public override void DeserializeFrom(string msg)
+ {
+ string[] split = msg.Split(',');
+ if (msg != "session_end")
+ {
+ EventTime = Convert.ToInt64(split[1]);
+ }
+ else
+ {
+ EventTime = 0;
+ }
+
+ message = msg;
+ }
+
+ public bool IsSessionStart()
+ {
+ return message.Split(',')[0] == "session_start";
+ }
+ }
+
+ public class EmptyComputerEvent : ComputerEvent
+ {
+ public EmptyComputerEvent() : base() { }
+
+ public override void Replay() { }
+
+ public override string Serialize()
+ {
+ return "";
+ }
+
+ public override void DeserializeFrom(string msg) { }
+ }
+}
diff --git a/itrace_core/DejaVuLib/CoreClient.cs b/itrace_core/DejaVuLib/CoreClient.cs
new file mode 100644
index 0000000..e95943a
--- /dev/null
+++ b/itrace_core/DejaVuLib/CoreClient.cs
@@ -0,0 +1,72 @@
+/********************************************************************************************************************************************************
+* @file CoreClient.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System;
+using System.IO;
+using System.Net.Sockets;
+using System.Threading;
+
+namespace iTrace_Core
+{
+ public class CoreClient
+ {
+ public event EventHandler OnCoreMessage;
+
+ const string localhostAddress = "127.0.0.1";
+ const int port = 8008;
+
+ TcpClient tcpClient;
+ NetworkStream networkStream;
+ StreamReader streamReader;
+
+ Thread listener;
+
+ public CoreClient()
+ {
+ tcpClient = new TcpClient();
+ }
+
+ public void Connect()
+ {
+ tcpClient.Connect(localhostAddress, port);
+ networkStream = tcpClient.GetStream();
+ streamReader = new StreamReader(networkStream);
+ listener = new Thread(Listen);
+ listener.IsBackground = true;
+ listener.Start();
+ }
+
+ public void Disconnect()
+ {
+ listener.Abort();
+ }
+
+ public void Listen()
+ {
+ try
+ {
+ while (true)
+ {
+ string text = streamReader.ReadLine();
+ OnCoreMessage(this, new CoreMessage(text));
+ }
+ }
+ catch (ThreadAbortException e)
+ {
+
+ }
+ catch (IOException e)
+ {
+
+ }
+ }
+ }
+}
diff --git a/itrace_core/DejaVuLib/EventFactory.cs b/itrace_core/DejaVuLib/EventFactory.cs
new file mode 100644
index 0000000..3924857
--- /dev/null
+++ b/itrace_core/DejaVuLib/EventFactory.cs
@@ -0,0 +1,39 @@
+/********************************************************************************************************************************************************
+* @file EventFactory.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+namespace iTrace_Core
+{
+ public interface IEventFactory
+ {
+ ComputerEvent Create(string serialized);
+ }
+
+ public class EventFactory : IEventFactory
+ where T: ComputerEvent, new()
+ {
+ protected IPauseStrategy strategy;
+
+ public EventFactory(IPauseStrategy strategy)
+ {
+ this.strategy = strategy;
+ }
+
+ public ComputerEvent Create(string serialized)
+ {
+ ComputerEvent result = new T();
+
+ result.DeserializeFrom(serialized);
+ result.PauseStrategy = strategy;
+
+ return result;
+ }
+ }
+}
diff --git a/itrace_core/DejaVuLib/EventFactoryMap.cs b/itrace_core/DejaVuLib/EventFactoryMap.cs
new file mode 100644
index 0000000..b496d0f
--- /dev/null
+++ b/itrace_core/DejaVuLib/EventFactoryMap.cs
@@ -0,0 +1,117 @@
+/********************************************************************************************************************************************************
+* @file EventFactoryMap.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System.Collections.Generic;
+
+namespace iTrace_Core
+{
+ public class EventFactoryMap
+ {
+ protected Dictionary factories;
+
+ public ComputerEvent GenerateFromCSV(string line)
+ {
+ string type = line.Split(',')[0];
+
+ return factories.TryGetValue(type, out var factory) ? factory.Create(line) : new EmptyComputerEvent();
+ }
+ }
+
+ public class FixedPauseEventFactoryMap : EventFactoryMap
+ {
+ public FixedPauseEventFactoryMap(int pause)
+ {
+ FixedLengthPause strategy = new FixedLengthPause(pause);
+
+ factories = new Dictionary
+ {
+ ["KeyDown"] = new EventFactory(strategy),
+ ["KeyUp"] = new EventFactory(strategy),
+ ["LeftMouseDown"] = new EventFactory(strategy),
+ ["LeftMouseUp"] = new EventFactory(strategy),
+ ["RightMouseDown"] = new EventFactory(strategy),
+ ["RightMouseUp"] = new EventFactory(strategy),
+ ["MouseMove"] = new EventFactory(strategy),
+ ["MouseWheel"] = new EventFactory(strategy),
+ ["gaze"] = new EventFactory(strategy),
+ ["session_start"] = new EventFactory(strategy),
+ ["session_end"] = new EventFactory(strategy),
+ ["MiddleMouseDown"] = new EventFactory(strategy),
+ ["MiddleMouseUp"] = new EventFactory(strategy),
+ ["ForwardMouseDown"] = new EventFactory(strategy),
+ ["ForwardMouseUp"] = new EventFactory(strategy),
+ ["BackMouseDown"] = new EventFactory(strategy),
+ ["BackMouseUp"] = new EventFactory(strategy)
+ };
+ }
+ }
+
+ public class BidirectionalCommunicationFactoryMap : EventFactoryMap
+ {
+ public BidirectionalCommunicationFactoryMap(int pause)
+ {
+ FixedLengthPause fixedLengthPause = new FixedLengthPause(pause);
+ WaitForClientPause waitForClientsPause = new WaitForClientPause();
+
+ factories = new Dictionary
+ {
+ ["KeyDown"] = new EventFactory(fixedLengthPause),
+ ["KeyUp"] = new EventFactory(fixedLengthPause),
+ ["LeftMouseDown"] = new EventFactory(fixedLengthPause),
+ ["LeftMouseUp"] = new EventFactory(fixedLengthPause),
+ ["RightMouseDown"] = new EventFactory(fixedLengthPause),
+ ["RightMouseUp"] = new EventFactory(fixedLengthPause),
+ ["MouseMove"] = new EventFactory(fixedLengthPause),
+ ["MouseWheel"] = new EventFactory(fixedLengthPause),
+ ["gaze"] = new EventFactory(waitForClientsPause),
+ ["session_start"] = new EventFactory(waitForClientsPause),
+ ["session_end"] = new EventFactory(waitForClientsPause),
+ ["MiddleMouseDown"] = new EventFactory(fixedLengthPause),
+ ["MiddleMouseUp"] = new EventFactory(fixedLengthPause),
+ ["ForwardMouseDown"] = new EventFactory(fixedLengthPause),
+ ["ForwardMouseUp"] = new EventFactory(fixedLengthPause),
+ ["BackMouseDown"] = new EventFactory(fixedLengthPause),
+ ["BackMouseUp"] = new EventFactory(fixedLengthPause)
+ };
+
+ }
+ }
+
+
+ public class ProportionalFactoryMap : EventFactoryMap
+ {
+ public ProportionalFactoryMap(int scale)
+ {
+ ProportionalLengthPause strategy = new ProportionalLengthPause(scale);
+
+ factories = new Dictionary
+ {
+ ["KeyDown"] = new EventFactory(strategy),
+ ["KeyUp"] = new EventFactory(strategy),
+ ["LeftMouseDown"] = new EventFactory(strategy),
+ ["LeftMouseUp"] = new EventFactory(strategy),
+ ["RightMouseDown"] = new EventFactory(strategy),
+ ["RightMouseUp"] = new EventFactory(strategy),
+ ["MouseMove"] = new EventFactory(strategy),
+ ["MouseWheel"] = new EventFactory(strategy),
+ ["gaze"] = new EventFactory(strategy),
+ ["session_start"] = new EventFactory(strategy),
+ ["session_end"] = new EventFactory(strategy),
+ ["MiddleMouseDown"] = new EventFactory(strategy),
+ ["MiddleMouseUp"] = new EventFactory(strategy),
+ ["ForwardMouseDown"] = new EventFactory(strategy),
+ ["ForwardMouseUp"] = new EventFactory(strategy),
+ ["BackMouseDown"] = new EventFactory(strategy),
+ ["BackMouseUp"] = new EventFactory(strategy)
+ };
+ }
+ }
+}
diff --git a/itrace_core/DejaVuLib/IPauseStrategy.cs b/itrace_core/DejaVuLib/IPauseStrategy.cs
new file mode 100644
index 0000000..ee9df28
--- /dev/null
+++ b/itrace_core/DejaVuLib/IPauseStrategy.cs
@@ -0,0 +1,82 @@
+/********************************************************************************************************************************************************
+* @file IPauseStrategy.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace iTrace_Core
+{
+ public interface IPauseStrategy
+ {
+ void Pause(ComputerEvent toPause);
+ }
+
+ public class EmptyPause : IPauseStrategy
+ {
+ public void Pause(ComputerEvent toPause) { }
+ }
+
+ public class WaitForClientPause : IPauseStrategy
+ {
+ const int timeoutLength = 5000;
+
+ public void Pause (ComputerEvent toPause)
+ {
+ SocketServer.Instance().WaitUntilClientsAreReady(timeoutLength);
+ }
+ }
+
+ public class FixedLengthPause : IPauseStrategy
+ {
+ private int lengthInMilliseconds;
+
+ public FixedLengthPause(int periodInMilliseconds)
+ {
+ lengthInMilliseconds = periodInMilliseconds;
+ }
+
+ public void Pause (ComputerEvent toPause)
+ {
+ Thread.Sleep(lengthInMilliseconds);
+ }
+ }
+
+ public class ProportionalLengthPause : IPauseStrategy
+ {
+ public long NextEventTime { get; set; }
+
+ private int scaleProportion;
+
+ private static long HundredNanosecondsToMilliseconds(long hundredNanoseconds)
+ {
+ return hundredNanoseconds / 10000;
+ }
+
+ public ProportionalLengthPause(int scaleProportion)
+ {
+ this.scaleProportion = scaleProportion;
+ }
+
+ public void Pause(ComputerEvent toPause)
+ {
+ long difference = NextEventTime - toPause.EventTime;
+
+ if (difference < 0)
+ return;
+
+ Thread.Sleep(Convert.ToInt32(HundredNanosecondsToMilliseconds(difference)) * scaleProportion);
+ }
+ }
+}
diff --git a/itrace_core/DejaVuLib/KeyboardHook.cs b/itrace_core/DejaVuLib/KeyboardHook.cs
new file mode 100644
index 0000000..6f0f601
--- /dev/null
+++ b/itrace_core/DejaVuLib/KeyboardHook.cs
@@ -0,0 +1,65 @@
+/********************************************************************************************************************************************************
+* @file KeyboardHook.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace iTrace_Core
+{
+ public class KeyboardHook : IDisposable
+ {
+ private Win32.CallBackHandler callBackHandler;
+ private IntPtr hookID = IntPtr.Zero;
+
+ public event EventHandler OnKeyboardEvent;
+
+ public KeyboardHook()
+ {
+ callBackHandler = HookCallback;
+ }
+
+ public void HookKeyboard()
+ {
+ using (Process curProcess = Process.GetCurrentProcess())
+ using (ProcessModule curModule = curProcess.MainModule)
+ {
+ hookID = Win32.SetWindowsHookEx(Win32.WH_KEYBOARD_LL, callBackHandler, Win32.GetModuleHandle(curModule.ModuleName), 0);
+ }
+ }
+
+ public void Dispose()
+ {
+ Win32.UnhookWindowsHookEx(hookID);
+ }
+
+ private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
+ {
+ if (nCode >= 0)
+ {
+ if ((wParam == (IntPtr)Win32.WM_KEYDOWN || wParam == (IntPtr)Win32.WM_SYSKEYDOWN))
+ {
+ byte virtualKeyCode = Marshal.ReadByte(lParam);
+
+ OnKeyboardEvent(this, new KeyDown(virtualKeyCode));
+ }
+ else
+ {
+ byte virtualKeyCode = Marshal.ReadByte(lParam);
+
+ OnKeyboardEvent(this, new KeyUp(virtualKeyCode));
+ }
+ }
+
+ return Win32.CallNextHookEx(hookID, nCode, wParam, lParam);
+ }
+ }
+}
diff --git a/itrace_core/DejaVuLib/MouseHook.cs b/itrace_core/DejaVuLib/MouseHook.cs
new file mode 100644
index 0000000..598ec79
--- /dev/null
+++ b/itrace_core/DejaVuLib/MouseHook.cs
@@ -0,0 +1,115 @@
+/********************************************************************************************************************************************************
+* @file MouseHook.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace iTrace_Core
+{
+ public class MouseHook : IDisposable
+ {
+ private Win32.CallBackHandler callBackHandler;
+ private IntPtr hookID = IntPtr.Zero;
+
+ public event EventHandler OnMouseEvent;
+
+ public MouseHook()
+ {
+ callBackHandler = HookCallback;
+ }
+
+ public void HookMouse()
+ {
+ using (Process currentProcess = Process.GetCurrentProcess())
+ using (ProcessModule currentModule = currentProcess.MainModule)
+ {
+ hookID = Win32.SetWindowsHookEx(Win32.WH_MOUSE_LL, callBackHandler, Win32.GetModuleHandle(currentModule.ModuleName), 0);
+ }
+ }
+
+ public void Dispose()
+ {
+ Win32.UnhookWindowsHookEx(hookID);
+ }
+
+ private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
+ {
+ if (nCode >= 0 && OnMouseEvent != null)
+ {
+ if (wParam == (IntPtr)Win32.WM_LBUTTONDOWN)
+ {
+ OnMouseEvent(this, new LeftMouseDown());
+ }
+ else if (wParam == (IntPtr)Win32.WM_LBUTTONUP)
+ {
+ OnMouseEvent(this, new LeftMouseUp());
+ }
+ else if (wParam == (IntPtr)Win32.WM_RBUTTONDOWN)
+ {
+ OnMouseEvent(this, new RightMouseDown());
+ }
+ else if (wParam == (IntPtr)Win32.WM_RBUTTONUP)
+ {
+ OnMouseEvent(this, new RightMouseUp());
+ }
+ else if (wParam == (IntPtr)Win32.WM_MOUSEMOVE)
+ {
+ Win32.MSLHOOKSTRUCT hookStruct = (Win32.MSLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(Win32.MSLHOOKSTRUCT));
+ OnMouseEvent(this, new MouseMove(hookStruct.pt.x, hookStruct.pt.y));
+ }
+ else if (wParam == (IntPtr)Win32.WM_MOUSEWHEEL)
+ {
+ Win32.MSLHOOKSTRUCT hookStruct = (Win32.MSLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(Win32.MSLHOOKSTRUCT));
+ OnMouseEvent(this, new MouseWheel(hookStruct.mouseData));
+ }
+ else if (wParam == (IntPtr)Win32.WM_MBUTTONDOWN)
+ {
+ OnMouseEvent(this, new MiddleMouseDown());
+ }
+ else if (wParam == (IntPtr)Win32.WM_MBUTTONUP)
+ {
+ OnMouseEvent(this, new MiddleMouseUp());
+ }
+ else if (wParam == (IntPtr)Win32.WM_XBUTTONDOWN)
+ {
+ Win32.MSLHOOKSTRUCT hookStruct = (Win32.MSLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(Win32.MSLHOOKSTRUCT));
+
+ UInt16 mouseDataHighOrderWord = (UInt16)(hookStruct.mouseData >> 16);
+
+ if (mouseDataHighOrderWord == Win32.XBUTTON2)
+ {
+ OnMouseEvent(this, new ForwardMouseDown());
+ }
+ else if (mouseDataHighOrderWord == Win32.XBUTTON1)
+ {
+ OnMouseEvent(this, new BackMouseDown());
+ }
+ }
+ else if (wParam == (IntPtr)Win32.WM_XBUTTONUP)
+ {
+ Win32.MSLHOOKSTRUCT hookStruct = (Win32.MSLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(Win32.MSLHOOKSTRUCT));
+ UInt16 mouseDataHighOrderWord = (UInt16)(hookStruct.mouseData >> 16);
+
+ if (mouseDataHighOrderWord == Win32.XBUTTON2)
+ {
+ OnMouseEvent(this, new ForwardMouseUp());
+ }
+ else if (mouseDataHighOrderWord == Win32.XBUTTON1)
+ {
+ OnMouseEvent(this, new BackMouseUp());
+ }
+ }
+ }
+ return Win32.CallNextHookEx(hookID, nCode, wParam, lParam);
+ }
+ }
+}
diff --git a/itrace_core/DejaVuLib/Win32.cs b/itrace_core/DejaVuLib/Win32.cs
new file mode 100644
index 0000000..d64ec26
--- /dev/null
+++ b/itrace_core/DejaVuLib/Win32.cs
@@ -0,0 +1,147 @@
+/********************************************************************************************************************************************************
+* @file Win32.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace iTrace_Core
+{
+ public class Win32
+ {
+ //Keyboard hook constants
+ public const int WH_KEYBOARD_LL = 13;
+ public const int WH_CALLWNDPROCRET = 12;
+ public const int WH_KEYBOARD = 2;
+ public const int WM_KEYDOWN = 0x0100;
+ public const int WM_SYSKEYDOWN = 0x0104;
+ public const int WM_KEYUP = 0x0101;
+ public const int WM_SYSKEYUP = 0x0105;
+
+
+ //Constants for simulating key strokes
+ public const int KEYEVENTF_EXTENDEDKEY = 0x0001;
+ public const int KEYEVENTF_KEYUP = 0x0002;
+
+ //Mouse Side buttons
+ public const int XBUTTON1 = 0x0001;
+ public const int XBUTTON2 = 0x0002;
+
+ //Mouse hook constants
+ public const int WH_MOUSE_LL = 14;
+ public const int WM_LBUTTONDOWN = 0x0201;
+ public const int WM_LBUTTONUP = 0x0202;
+ public const int WM_MOUSEMOVE = 0x0200;
+ public const int WM_MOUSEWHEEL = 0x020A;
+ public const int WM_RBUTTONDOWN = 0x0204;
+ public const int WM_RBUTTONUP = 0x0205;
+ public const int WM_MBUTTONDOWN = 0x0207;
+ public const int WM_MBUTTONUP = 0x0208;
+ public const int WM_XBUTTONDOWN = 523;
+ public const int WM_XBUTTONUP = 524;
+
+ //Constants for simulating mouse events
+ public const int MOUSEEVENTF_ABSOLUTE = 0x8000;
+ public const int MOUSEEVENTF_LEFTDOWN = 0x0002;
+ public const int MOUSEEVENTF_LEFTUP = 0x0004;
+ public const int MOUSEEVENTF_MIDDLEDOWN = 0x0020;
+ public const int MOUSEEVENTF_MIDDLEUP = 0x0040;
+ public const int MOUSEEVENTF_MOVE = 0x0001;
+ public const int MOUSEEVENTF_RIGHTDOWN = 0x0008;
+ public const int MOUSEEVENTF_RIGHTUP = 0x0010;
+ public const int MOUSEEVENTF_WHEEL = 0x0800;
+ public const int MOUSEEVENTF_XDOWN = 0x0080;
+ public const int MOUSEEVENTF_XUP = 0x0100;
+
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct POINT
+ {
+ public int x;
+ public int y;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct MSLHOOKSTRUCT
+ {
+ public POINT pt;
+ public uint mouseData;
+ public uint flags;
+ public uint time;
+ public IntPtr dwExtraInfo;
+ }
+
+ //For creating hooks
+ [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ public static extern IntPtr GetModuleHandle(string lpModuleName);
+
+ [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ public static extern IntPtr SetWindowsHookEx(int idHook, CallBackHandler lpfn, IntPtr hMod, uint dwThreadId);
+
+ [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool UnhookWindowsHookEx(IntPtr hhk);
+
+ [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
+
+ public delegate IntPtr CallBackHandler(int nCode, IntPtr wParam, IntPtr lParam);
+
+ //For simulating mouse and keyboard events
+ [DllImport("user32.dll", SetLastError = true)]
+ public static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo);
+
+ [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
+ public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint dwData, UIntPtr dwExtraInfo);
+
+ // WindowPositionManager related definitions
+ [StructLayout(LayoutKind.Sequential)]
+ public struct Rect
+ {
+ public int Left;
+ public int Top;
+ public int Right;
+ public int Bottom;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct WindowInfo
+ {
+ public uint cbSize;
+ public Rect rcWindow;
+ public Rect rcClient;
+ public uint dwStyle;
+ public uint dwExStyle;
+ public uint dwWindowStatus;
+ public uint cxWindowBorders;
+ public uint cyWindowBorders;
+ public long atom; // Todo: should be of type Atom (struct containing a pointer)
+ public uint wCreatorVersion;
+ }
+
+ [DllImport("user32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool GetWindowRect(IntPtr hWnd, out Rect rect);
+
+ [DllImport("user32.dll")]
+ public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
+
+ public delegate bool CallBackPtr(IntPtr hWnd, int lParam);
+
+ [DllImport("user32.dll")]
+ public static extern int EnumWindows(CallBackPtr callPtr, int lPar);
+
+ [DllImport("user32.dll")]
+ public static extern bool GetWindowInfo(IntPtr hWnd, out WindowInfo pWi);
+
+ public const uint WS_MINIMIZEBOX = 0x00020000;
+ public const uint WS_MAXIMIZEBOX = 0x00010000;
+ }
+}
diff --git a/itrace_core/DejaVuLib/WindowPositionManager.cs b/itrace_core/DejaVuLib/WindowPositionManager.cs
new file mode 100644
index 0000000..871b863
--- /dev/null
+++ b/itrace_core/DejaVuLib/WindowPositionManager.cs
@@ -0,0 +1,111 @@
+/********************************************************************************************************************************************************
+* @file WindowPositionManager.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace iTrace_Core
+{
+ public class WindowPositionManager
+ {
+ private Thread forcerThread;
+
+ private HashSet handles;
+ private HashSet refreshedHandles;
+
+ public WindowPositionManager()
+ {
+ handles = new HashSet();
+ refreshedHandles = new HashSet();
+ }
+
+ public void Start()
+ {
+ Win32.EnumWindows(InitialSeedCallback, 0);
+ StartForcingFixedStartLocation();
+ }
+
+ public void Stop()
+ {
+ if(forcerThread != null)
+ {
+ forcerThread.Abort();
+ }
+ }
+
+ private bool MoveIfNotInSetCallback(IntPtr hWnd, int lParam)
+ {
+ refreshedHandles.Add(hWnd);
+
+ if (!handles.Contains(hWnd))
+ {
+ handles.Add(hWnd);
+
+ new Thread(new ThreadStart(() =>
+ {
+ Thread.Sleep(200);
+
+ var info = new Win32.WindowInfo();
+
+ if (Win32.GetWindowInfo(hWnd, out info))
+ {
+ bool maximizable = (info.dwStyle & Win32.WS_MAXIMIZEBOX) != 0;
+ bool minimizable = (info.dwStyle & Win32.WS_MINIMIZEBOX) != 0;
+
+ if (!maximizable && minimizable)
+ {
+ int height = Convert.ToInt32(info.rcWindow.Bottom - info.rcWindow.Top);
+ int width = Convert.ToInt32(info.rcWindow.Right - info.rcWindow.Left);
+
+ Win32.MoveWindow(hWnd, 10, 10, width, height, false);
+ }
+ else if (minimizable)
+ {
+ Win32.MoveWindow(hWnd, 10, 10, 500, 500, false);
+ }
+ }
+ })).Start();
+ }
+
+ return true;
+ }
+
+ private void StartForcingFixedStartLocation()
+ {
+ forcerThread = new Thread(new ThreadStart(() =>
+ {
+ try
+ {
+ while (true)
+ {
+ Win32.EnumWindows(MoveIfNotInSetCallback, 0);
+ handles = refreshedHandles;
+ refreshedHandles = new HashSet();
+ }
+ }
+ catch (ThreadAbortException e) { }
+ }));
+
+ forcerThread.Start();
+ }
+
+ private bool InitialSeedCallback(IntPtr hWnd, int lParam)
+ {
+ handles.Add(hWnd);
+
+ return true;
+ }
+ }
+}
diff --git a/itrace_core/EyeStatusWindow.xaml b/itrace_core/EyeStatusWindow.xaml
index fe34e8e..6c964e8 100644
--- a/itrace_core/EyeStatusWindow.xaml
+++ b/itrace_core/EyeStatusWindow.xaml
@@ -1,4 +1,16 @@
-.
+********************************************************************************************************************************************************/
+-->
+.
+********************************************************************************************************************************************************/
+
+using System;
using System.Numerics;
using System.Windows;
using System.Windows.Media;
diff --git a/itrace_core/GazeData.cs b/itrace_core/GazeData.cs
index def478d..004f3e2 100644
--- a/itrace_core/GazeData.cs
+++ b/itrace_core/GazeData.cs
@@ -1,4 +1,16 @@
-using System;
+/********************************************************************************************************************************************************
+* @file GazeData.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using iTrace_Core.Properties;
+using System;
using System.Windows.Forms;
using System.Xml;
@@ -96,25 +108,35 @@ public TobiiGazeData(Tobii.Research.GazeDataEventArgs tobiiRawGaze) : base()
{
bool isLeftEyeValid = tobiiRawGaze.LeftEye.GazePoint.Validity == Tobii.Research.Validity.Valid;
bool isRightEyeValid = tobiiRawGaze.RightEye.GazePoint.Validity == Tobii.Research.Validity.Valid;
+ Screen screen = Screen.AllScreens[Settings.Default.calibration_monitor];
+
+ RightX = tobiiRawGaze.RightEye.GazePoint.PositionOnDisplayArea.X * screen.Bounds.Width + screen.Bounds.Left;
+ RightY = tobiiRawGaze.RightEye.GazePoint.PositionOnDisplayArea.Y * screen.Bounds.Height + screen.Bounds.Top;
+ RightPupil = tobiiRawGaze.RightEye.Pupil.PupilDiameter;
+ RightValidation = Convert.ToInt32(isRightEyeValid);
+
+ LeftX = tobiiRawGaze.LeftEye.GazePoint.PositionOnDisplayArea.X * screen.Bounds.Width + screen.Bounds.Left;
+ LeftY = tobiiRawGaze.LeftEye.GazePoint.PositionOnDisplayArea.Y * screen.Bounds.Height + screen.Bounds.Top;
+ LeftPupil = tobiiRawGaze.LeftEye.Pupil.PupilDiameter;
+ LeftValidation = Convert.ToInt32(isLeftEyeValid);
if (isLeftEyeValid && isRightEyeValid)
{
- double avgX = (tobiiRawGaze.LeftEye.GazePoint.PositionOnDisplayArea.X + tobiiRawGaze.RightEye.GazePoint.PositionOnDisplayArea.X) / 2.0D;
- double avgY = (tobiiRawGaze.LeftEye.GazePoint.PositionOnDisplayArea.Y + tobiiRawGaze.RightEye.GazePoint.PositionOnDisplayArea.Y) / 2.0D;
-
- X = Convert.ToInt32(avgX * Screen.PrimaryScreen.Bounds.Width);
- Y = Convert.ToInt32(avgY * Screen.PrimaryScreen.Bounds.Height);
+ double avgX = (RightX + LeftX) / 2.0D;
+ double avgY = (RightY + LeftY) / 2.0D;
+ X = Convert.ToInt32(avgX);
+ Y = Convert.ToInt32(avgY);
}
else if (isLeftEyeValid)
{
- X = Convert.ToInt32(tobiiRawGaze.LeftEye.GazePoint.PositionOnDisplayArea.X * Screen.PrimaryScreen.Bounds.Width);
- Y = Convert.ToInt32(tobiiRawGaze.LeftEye.GazePoint.PositionOnDisplayArea.Y * Screen.PrimaryScreen.Bounds.Height);
+ X = Convert.ToInt32(LeftX);
+ Y = Convert.ToInt32(LeftY);
}
else if (isRightEyeValid)
{
- X = Convert.ToInt32(tobiiRawGaze.RightEye.GazePoint.PositionOnDisplayArea.X * Screen.PrimaryScreen.Bounds.Width);
- Y = Convert.ToInt32(tobiiRawGaze.RightEye.GazePoint.PositionOnDisplayArea.Y * Screen.PrimaryScreen.Bounds.Height);
+ X = Convert.ToInt32(RightX);
+ Y = Convert.ToInt32(RightY);
}
else
{
@@ -122,17 +144,6 @@ public TobiiGazeData(Tobii.Research.GazeDataEventArgs tobiiRawGaze) : base()
X = null;
Y = null;
}
-
- RightX = tobiiRawGaze.RightEye.GazePoint.PositionOnDisplayArea.X * Screen.PrimaryScreen.Bounds.Width;
- RightY = tobiiRawGaze.RightEye.GazePoint.PositionOnDisplayArea.Y * Screen.PrimaryScreen.Bounds.Height;
- RightPupil = tobiiRawGaze.RightEye.Pupil.PupilDiameter;
- RightValidation = Convert.ToInt32(isRightEyeValid);
-
- LeftX = tobiiRawGaze.LeftEye.GazePoint.PositionOnDisplayArea.X * Screen.PrimaryScreen.Bounds.Width;
- LeftY = tobiiRawGaze.LeftEye.GazePoint.PositionOnDisplayArea.Y * Screen.PrimaryScreen.Bounds.Height;
- LeftPupil = tobiiRawGaze.LeftEye.Pupil.PupilDiameter;
- LeftValidation = Convert.ToInt32(isLeftEyeValid);
-
UserLeftX = tobiiRawGaze.LeftEye.GazeOrigin.PositionInUserCoordinates.X;
UserLeftY = tobiiRawGaze.LeftEye.GazeOrigin.PositionInUserCoordinates.Y;
UserLeftZ = tobiiRawGaze.LeftEye.GazeOrigin.PositionInUserCoordinates.Z;
@@ -140,7 +151,7 @@ public TobiiGazeData(Tobii.Research.GazeDataEventArgs tobiiRawGaze) : base()
UserRightX = tobiiRawGaze.RightEye.GazeOrigin.PositionInUserCoordinates.X;
UserRightY = tobiiRawGaze.RightEye.GazeOrigin.PositionInUserCoordinates.Y;
UserRightZ = tobiiRawGaze.RightEye.GazeOrigin.PositionInUserCoordinates.Z;
-
+
TrackerTime = tobiiRawGaze.DeviceTimeStamp;
}
}
@@ -164,6 +175,410 @@ public MouseTrackerGazeData(int mousePosX, int mousePosY) : base()
}
}
+ public class SmartEyeGazeData : GazeData
+ {
+ //Debug messages
+ private const bool PrintPacketInfo = false;
+ private const bool PrintIntersectionInfo = true;
+ private const bool PrintPupilInfo = false;
+
+ //Lot of constants
+ //All subpacket IDs are uint16s
+ //These are in the same order as in the ProgrammersGuide, not in order of ID
+ private const UInt16 SEFrameNumber = 0x0001;
+ private const UInt16 SEEstimatedDelay = 0x0002;
+ private const UInt16 SETimeStamp = 0x0003;
+ private const UInt16 SEUserTimeStamp = 0x0004;
+ private const UInt16 SEFrameRate = 0x0005;
+ private const UInt16 SECameraPositions = 0x0006;
+ private const UInt16 SECameraRotations = 0x0007;
+ private const UInt16 SEUserDefinedData = 0x0008;
+ private const UInt16 SERealTimeClock = 0x0009;
+ private const UInt16 SEHeadPosition = 0x0010; //Yes, 0x10
+ private const UInt16 SEHeadPositionQ = 0x0011;
+ private const UInt16 SEHeadRotationRodrigues = 0x0012;
+ private const UInt16 SEHeadRotationQuaternion = 0x001D;
+ private const UInt16 SEHeadLeftEarDirection = 0x0015;
+ private const UInt16 SEHeadUpDirection = 0x0014;
+ private const UInt16 SEHeadNoseDirection = 0x0013;
+ private const UInt16 SEHeadHeading = 0x0016;
+ private const UInt16 SEHeadPitch = 0x0017;
+ private const UInt16 SEHeadRoll = 0x0018;
+ private const UInt16 SEHeadRotationQ = 0x0019;
+ private const UInt16 SEGazeOrigin = 0x001A;
+ private const UInt16 SELeftGazeOrigin = 0x001B;
+ private const UInt16 SERightGazeOrigin = 0x001C;
+ private const UInt16 SEEyePosition = 0x0020;
+ private const UInt16 SEGazeDirection = 0x0021;
+ private const UInt16 SEGazeDirectionQ = 0x0022;
+ private const UInt16 SELeftEyePosition = 0x0023;
+ private const UInt16 SELeftGazeDirection = 0x0024;
+ private const UInt16 SELeftGazeDirectionQ = 0x0025;
+ private const UInt16 SERightEyePosition = 0x0026;
+ private const UInt16 SERightGazeDirection = 0x0027;
+ private const UInt16 SERightGazeDirectionQ = 0x0028;
+ private const UInt16 SEGazeHeading = 0x0029;
+ private const UInt16 SEGazePitch = 0x002A;
+ private const UInt16 SELeftGazeHeading = 0x002B;
+ private const UInt16 SELeftGazePitch = 0x002C;
+ private const UInt16 SERightGazeHeading = 0x002D;
+ private const UInt16 SERightGazePitch = 0x002E;
+ private const UInt16 SEFilteredGazeDirection = 0x0030;
+ private const UInt16 SEFilteredLeftGazeDirection = 0x0032;
+ private const UInt16 SEFilteredRightGazeDirection = 0x0034;
+ private const UInt16 SEFilteredGazeHeading = 0x0036;
+ private const UInt16 SEFilteredGazePitch = 0x0037;
+ private const UInt16 SEFilteredLeftGazeHeading = 0x0038;
+ private const UInt16 SEFilteredLeftGazePitch = 0x0039;
+ private const UInt16 SEFilteredRightGazeHeading = 0x003A;
+ private const UInt16 SEFilteredRightGazePitch = 0x003B;
+ private const UInt16 SESaccade = 0x003D;
+ private const UInt16 SEFixation = 0x003E;
+ private const UInt16 SEBlink = 0x003F;
+ private const UInt16 SEClosestWorldIntersection = 0x0040;
+ private const UInt16 SEFilteredClosestWorldIntersectionId = 0x0041;
+ private const UInt16 SEAllWorldIntersections = 0x0042;
+ private const UInt16 SEFilteredAllWorldIntersections = 0x0043;
+ private const UInt16 SEZoneId = 0x0044;
+ private const UInt16 SEEstimatedClosestWorldIntersection = 0x0045;
+ private const UInt16 SEEstimatedAllWorldIntersections = 0x0046;
+ private const UInt16 SEHeadClosestWorldIntersection = 0x0049;
+ private const UInt16 SEHeadAllWorldIntersections = 0x004A;
+ private const UInt16 SEEyelidOpening = 0x0050;
+ private const UInt16 SEEyelidOpeningQ = 0x0051;
+ private const UInt16 SELeftEyelidOpening = 0x0052;
+ private const UInt16 SELeftEyelidOpeningQ = 0x0053;
+ private const UInt16 SERightEyelidOpening = 0x0054;
+ private const UInt16 SERightEyelidOpeningQ = 0x0055;
+ private const UInt16 SEKeyboardState = 0x0056;
+ private const UInt16 SELeftLowerEyelidExtremePoint = 0x0058;
+ private const UInt16 SELeftUpperEyelidExtremePoint = 0x0059;
+ private const UInt16 SERightLowerEyelidExtremePoint = 0x005A;
+ private const UInt16 SERightUpperEyelidExtremePoint = 0x005B;
+ private const UInt16 SEPupilDiameter = 0x0060;
+ private const UInt16 SEPupilDiameterQ = 0x0061;
+ private const UInt16 SELeftPupilDiameter = 0x0062;
+ private const UInt16 SELeftPupilDiamterQ = 0x0063;
+ private const UInt16 SERightPupilDiameter = 0x0064;
+ private const UInt16 SERightPupilDiameterQ = 0x0065;
+ private const UInt16 SEFilteredPupilDiameter = 0x0066;
+ private const UInt16 SEFilteredPupilDiameterQ = 0x0067;
+ private const UInt16 SEFilteredLeftPupilDiameter = 0x0068;
+ private const UInt16 SEFilteredLeftPupilDiamterQ = 0x0069;
+ private const UInt16 SEFilteredRightPupilDiameter = 0x006A;
+ private const UInt16 SEFilteredERightPupilDiameterQ = 0x006B;
+ private const UInt16 SEGPSPosition = 0x0070;
+ private const UInt16 SEGPSGroundSpeed = 0x0071;
+ private const UInt16 SEGPSCourse = 0x0072;
+ private const UInt16 SEGPSTime = 0x0073;
+ private const UInt16 SEEstimatedGazeOrigin = 0x007A;
+ private const UInt16 SEEstimatedLeftGazeOrigin = 0x007B;
+ private const UInt16 SEEstimatedRightGazeOrigin = 0x007C;
+ private const UInt16 SEEstimatedEyePosition = 0x0080;
+ private const UInt16 SEEstimatedGazeDirection = 0x0081;
+ private const UInt16 SEEstimatedGazeDirectionQ = 0x0082;
+ private const UInt16 SEEstimatedGazeHeading = 0x0083;
+ private const UInt16 SEEstimatedGazePitch = 0x0084;
+ private const UInt16 SEEstimatedLeftEyePosition = 0x0085;
+ private const UInt16 SEEstimatedLeftGazeDirection = 0x0086;
+ private const UInt16 SEEstimatedLeftGazeDirectionQ = 0x0087;
+ private const UInt16 SEEstimatedLeftGazeHeading = 0x0088;
+ private const UInt16 SEEstimatedLeftGazePitch = 0x0089;
+ private const UInt16 SEEstimatedRightEyePosition = 0x008A;
+ private const UInt16 SEEstimatedRightGazeDirection = 0x008B;
+ private const UInt16 SEEstimatedRightGazeDirectionQ = 0x008C;
+ private const UInt16 SEEstimatedRightGazeHeading = 0x008D;
+ private const UInt16 SEEstimatedRightGazePitch = 0x008E;
+ private const UInt16 SEFilteredEstimatedGazeDirection = 0x0091;
+ private const UInt16 SEFilteredEstimatedGazeHeading = 0x0093;
+ private const UInt16 SEFilteredEstimatedGazePitch = 0x0094;
+ private const UInt16 SEFilteredEstimatedLeftGazeDirection = 0x0096;
+ private const UInt16 SEFilteredEstimatedLeftGazeHeading = 0x0098;
+ private const UInt16 SEFilteredEstimatedLeftGazePitch = 0x0099;
+ private const UInt16 SEFilteredEstimatedRightGazeDirection = 0x009B;
+ private const UInt16 SEFilteredEstimatedRightGazeHeading = 0x009D;
+ private const UInt16 SEFilteredEstimatedRightGazePitch = 0x009E;
+ private const UInt16 SEASCIIKeyboardState = 0x00A4;
+ private const UInt16 SECalibrationGazeIntersection = 0x00B0;
+ private const UInt16 SETaggedGazeIntersection = 0x00B1;
+ private const UInt16 SELeftClosestWorldIntersection = 0x00B2;
+ private const UInt16 SELeftAllWorldIntersections = 0x00B3;
+ private const UInt16 SERightClosestWorldIntersection = 0x00B4;
+ private const UInt16 SERightAllWorldIntersections = 0x00B5;
+ private const UInt16 SEFilteredLeftClosestWorldIntersection = 0x00B6;
+ private const UInt16 SEFilteredLeftAllWorldIntersections = 0x00B7;
+ private const UInt16 SEFilteredRightClosestWorldIntersection = 0x00B8;
+ private const UInt16 SEFilteredRightAllWorldIntersections = 0x00B9;
+ private const UInt16 SEEstimatedLeftClosestWorldIntersection = 0x00BA;
+ private const UInt16 SEEstimatedLeftAllWorldIntersections = 0x00BB;
+ private const UInt16 SEEstimatedRightClosestWorldIntersection = 0x00BC;
+ private const UInt16 SEEstimatedRightAllWorldIntersections = 0x00BD;
+ private const UInt16 SEOptimalReflexReductionMode = 0x00C3;
+ private const UInt16 SELeftBlinkClosingMidTime = 0x00E0;
+ private const UInt16 SELeftBlinkOpeningMidTime = 0x00E1;
+ private const UInt16 SELeftBlinkClosingAmplitude = 0x00E2;
+ private const UInt16 SELeftBlinkOpeningAmplitude = 0x00E3;
+ private const UInt16 SELeftBlinkClosingSpeed = 0x00E4;
+ private const UInt16 SELeftBlinkOpeningSpeed = 0x00E5;
+ private const UInt16 SERightBlinkClosingMidTime = 0x00E6;
+ private const UInt16 SERightBlinkOpeningMidTime = 0x00E7;
+ private const UInt16 SERightBlinkClosingAmplitude = 0x00E8;
+ private const UInt16 SERightBlinkOpeningAmplitude = 0x00E9;
+ private const UInt16 SERightBlinkClosingSpeed = 0x00EA;
+ private const UInt16 SERightBlinkOpeningSpeed = 0x00EB;
+ private const UInt16 SELeftEyeOuterCorner3D = 0x0300;
+ private const UInt16 SELeftEyeInnerCorner3D = 0x0301;
+ private const UInt16 SERightEyeInnerCorner3D = 0x0302;
+ private const UInt16 SERightEyeOuterCorner3D = 0x0303;
+ private const UInt16 SELeftNostril3D = 0x0304;
+ private const UInt16 SERightNostril3D = 0x0305;
+ private const UInt16 SELeftMouthCorner3D = 0x0306;
+ private const UInt16 SERightMouthCorner3D = 0x0307;
+ private const UInt16 SELeftEar3D = 0x0308;
+ private const UInt16 SERightEar3D = 0x0309;
+ private const UInt16 SENoseTip3D = 0x0360;
+ private const UInt16 SELeftEyeOuterCorner2D = 0x0310;
+ private const UInt16 SELeftEyeInnerCorner2D = 0x0311;
+ private const UInt16 SERightEyeInnerCorner2D = 0x0312;
+ private const UInt16 SERightEyeOuterCorner2D = 0x0313;
+ private const UInt16 SELeftNostril2D = 0x0314;
+ private const UInt16 SERightNostril2D = 0x0315;
+ private const UInt16 SELeftMouthCorner2D = 0x0316;
+ private const UInt16 SERightMouthCorner2D = 0x0317;
+ private const UInt16 SELeftEar2D = 0x0318;
+ private const UInt16 SERightEar2D = 0x0319;
+ private const UInt16 SEIrisMatch = 0x0350;
+ private const UInt16 SEPupilMatch = 0x0351;
+ private const UInt16 SERobustIrisMatch = 0x0352;
+ private const UInt16 SENoseTip2D = 0x0370;
+ private const UInt16 SELowerEyelidPoints = 0x0380;
+ private const UInt16 SEUpperEyelidPoints = 0x0381;
+ private const UInt16 SELeftEyelidState = 0x0390;
+ private const UInt16 SERightEyelidState = 0x0391;
+ private const UInt16 SEUserMarker = 0x03A0;
+ private const UInt16 SECameraClocks = 0x03A1;
+
+ private const int SEType_u16_Size = 2;
+ private const int SEType_u32_Size = 4;
+ private const int SEType_u64_Size = 8;
+ private const int SEType_f64_Size = 8;
+
+ // TODO: these should be per-session
+ private static UInt32 lastFixation = 0;
+ private static UInt32 lastBlink = 0;
+
+ // Intersection name used primarily for multiple screens
+ public string intersectionName = "";
+
+ public SmartEyeGazeData(byte[] packet)
+ {
+ //Print packet header
+ UInt16 PacketType = ParseSEType_u16(packet, 4);
+ UInt16 PacketLength = ParseSEType_u16(packet, 6);
+
+ if (PrintPacketInfo)
+ Console.WriteLine("Packet Type: {0} Length: {1}", PacketType, PacketLength);
+
+ //Print subpackets and their IDs
+ //Start of first subpacket at 8 bytes
+ Int32 Index = 8;
+
+ while (Index < PacketLength)
+ {
+ //Pg 9 in the SmartEye Programmers Guide gives the following offsets to the Subpacket Id and Length
+ UInt16 SubpacketId = ParseSEType_u16(packet, Index);
+ UInt16 SubpacketLength = ParseSEType_u16(packet, Index + SEType_u16_Size);
+
+ //Advance beyond the 4 byte packet header
+ Index += 2 * SEType_u16_Size;
+
+ if (PrintPacketInfo)
+ Console.WriteLine("\tSubpacketType: 0x{0:X} Length: {1}", SubpacketId, SubpacketLength);
+
+ Int32 SubpacketOffset = Index;
+
+ //Look for a left right or combined world intersection subpacket
+ if (SubpacketId == SEFilteredClosestWorldIntersectionId ||
+ SubpacketId == SEFilteredLeftClosestWorldIntersection ||
+ SubpacketId == SEFilteredRightClosestWorldIntersection)
+ {
+
+ //Check if an intersection exists, the first U16 will be 1 if this is the case.
+ if (ParseSEType_u16(packet, SubpacketOffset) == 1)
+ {
+ int x;
+ int y;
+
+ GetScreenCoordsFromWorldIntersection(packet, SubpacketOffset, out x, out y);
+
+ //Skip over fields, need a better way of conveying where things are
+ SubpacketOffset += SEType_u16_Size + 6 * SEType_f64_Size;
+
+ //Read name of intersected object
+ UInt16 intersectNameLength = ParseSEType_u16(packet, SubpacketOffset);
+
+ SubpacketOffset += SEType_u16_Size;
+
+ String intersectName = System.Text.Encoding.ASCII.GetString(packet, SubpacketOffset, intersectNameLength);
+
+ switch (SubpacketId)
+ {
+ case SEFilteredClosestWorldIntersectionId:
+ this.X = x;
+ this.Y = y;
+ this.intersectionName = intersectName;
+ if (PrintIntersectionInfo)
+ Console.WriteLine("Combined Intersection \"{0}\" at coords {1}, {2}", intersectName, x, y);
+ break;
+
+ case SEFilteredLeftClosestWorldIntersection:
+ this.LeftX = x;
+ this.LeftY = y;
+ this.LeftValidation = 1;
+ if (PrintIntersectionInfo)
+ Console.WriteLine("Left Intersection \"{0}\" at coords {1}, {2}", intersectName, x, y);
+ break;
+
+ case SEFilteredRightClosestWorldIntersection:
+ this.RightX = x;
+ this.RightY = y;
+ this.RightValidation = 1;
+ if (PrintIntersectionInfo)
+ Console.WriteLine("Right Intersection \"{0}\" at coords {1}, {2}", intersectName, x, y);
+ break;
+ }
+ }
+ }
+ else if (SubpacketId == SETimeStamp)
+ {
+ this.TrackerTime = (long)ParseSEType_u64(packet, SubpacketOffset);
+ }
+ else if (SubpacketId == SELeftGazeOrigin)
+ {
+ this.UserLeftX = ParseSEType_f64(packet, SubpacketOffset);
+ this.UserLeftY = ParseSEType_f64(packet, SubpacketOffset + SEType_f64_Size);
+ this.UserLeftZ = ParseSEType_f64(packet, SubpacketOffset + 2 * SEType_f64_Size);
+ }
+ else if (SubpacketId == SERightGazeOrigin)
+ {
+ this.UserRightX = ParseSEType_f64(packet, SubpacketOffset);
+ this.UserRightY = ParseSEType_f64(packet, SubpacketOffset + SEType_f64_Size);
+ this.UserRightZ = ParseSEType_f64(packet, SubpacketOffset + 2 * SEType_f64_Size);
+ }
+ else if (SubpacketId == SEFixation)
+ {
+ UInt32 fixationNumber = ParseSEType_u32(packet, SubpacketOffset);
+
+ if (fixationNumber > lastFixation)
+ {
+ if (true) //TODO: Add toggle
+ Console.WriteLine("Fixation: {0}", fixationNumber);
+
+ lastFixation = fixationNumber;
+ }
+ }
+ else if (SubpacketId == SEBlink)
+ {
+ UInt32 blinkNumber = ParseSEType_u32(packet, SubpacketOffset);
+
+ if (blinkNumber > lastBlink)
+ {
+ if (true) //TODO: Add toggle
+ Console.WriteLine("Blink: {0}", blinkNumber);
+
+ lastBlink = blinkNumber;
+ }
+ }
+
+ if (SubpacketId == SEFilteredLeftPupilDiameter || SubpacketId == SEFilteredRightPupilDiameter)
+ {
+ double diam = ParseSEType_f64(packet, SubpacketOffset);
+
+ switch (SubpacketId)
+ {
+ case SEFilteredLeftPupilDiameter:
+ this.LeftPupil = diam;
+ if (PrintPupilInfo)
+ Console.WriteLine("Left Pupil: {0}", diam);
+ break;
+
+ case SEFilteredRightPupilDiameter:
+ this.RightPupil = diam;
+ if (PrintPupilInfo)
+ Console.WriteLine("Right Pupil: {0}", diam);
+ break;
+ }
+ }
+
+ //Advance to the next Subpacket
+ Index += SubpacketLength;
+ }
+ }
+
+ //Offset by screen position, for multi-screen use
+ public void Offset(int x, int y)
+ {
+ this.X += x;
+ this.Y += y;
+ }
+
+ //Parse a UInt16 from an SmartEye packet, accounting for endianness
+ private UInt16 ParseSEType_u16(byte[] packet, Int32 offset)
+ {
+ byte[] bytes = new byte[SEType_u16_Size];
+ Array.ConstrainedCopy(packet, offset, bytes, 0, SEType_u16_Size);
+
+ //Reverse bytes if system endianness is not Big
+ if (BitConverter.IsLittleEndian)
+ Array.Reverse(bytes);
+
+ return BitConverter.ToUInt16(bytes, 0);
+ }
+
+ private UInt32 ParseSEType_u32(byte[] packet, Int32 offset)
+ {
+ byte[] bytes = new byte[SEType_u32_Size];
+ Array.ConstrainedCopy(packet, offset, bytes, 0, SEType_u32_Size);
+
+ if (BitConverter.IsLittleEndian)
+ Array.Reverse(bytes);
+
+ return BitConverter.ToUInt32(bytes, 0);
+ }
+
+ private UInt64 ParseSEType_u64(byte[] packet, Int32 offset)
+ {
+ byte[] bytes = new byte[SEType_u64_Size];
+ Array.ConstrainedCopy(packet, offset, bytes, 0, SEType_u64_Size);
+
+ if (BitConverter.IsLittleEndian)
+ Array.Reverse(bytes);
+
+ return BitConverter.ToUInt64(bytes, 0);
+ }
+
+ private double ParseSEType_f64(byte[] packet, Int32 offset)
+ {
+ byte[] bytes = new byte[SEType_f64_Size];
+ Array.ConstrainedCopy(packet, offset, bytes, 0, SEType_f64_Size);
+
+ if (BitConverter.IsLittleEndian)
+ Array.Reverse(bytes);
+
+ return BitConverter.ToDouble(bytes, 0);
+ }
+
+ //Get X and Y coordinates from an SEType_WorldIntersection
+ private void GetScreenCoordsFromWorldIntersection(byte[] packet, Int32 offset, out int x, out int y)
+ {
+ //Skip over fields SEType_u16 intersections, and SEType_Point3D worldPoint (3 floats of 8 bytes each)
+ offset += SEType_u16_Size + 3 * SEType_f64_Size;
+
+ x = (int)ParseSEType_f64(packet, offset);
+ y = (int)ParseSEType_f64(packet, offset + SEType_f64_Size);
+ }
+ }
+
public class GazepointGazeData : GazeData
{
public GazepointGazeData(String gazePointRawGaze) : base()
@@ -179,20 +594,42 @@ public GazepointGazeData(String gazePointRawGaze) : base()
Y = null;
return;
}
+ Screen screen = Screen.AllScreens[Settings.Default.calibration_monitor];
- X = Convert.ToInt32(Double.Parse(recNode.Attributes["BPOGX"].Value) * Screen.PrimaryScreen.Bounds.Width);
- Y = Convert.ToInt32(Double.Parse(recNode.Attributes["BPOGY"].Value) * Screen.PrimaryScreen.Bounds.Height);
+ X = Convert.ToInt32(Double.Parse(recNode.Attributes["BPOGX"].Value) * screen.Bounds.Width + screen.Bounds.Left);
+ Y = Convert.ToInt32(Double.Parse(recNode.Attributes["BPOGY"].Value) * screen.Bounds.Height + screen.Bounds.Top);
RightX = Double.Parse(recNode.Attributes["RPOGX"].Value) * Screen.PrimaryScreen.Bounds.Width;
RightY = Double.Parse(recNode.Attributes["RPOGY"].Value) * Screen.PrimaryScreen.Bounds.Height;
- RightPupil = Double.Parse(recNode.Attributes["RPD"].Value);
RightValidation = Int32.Parse(recNode.Attributes["RPOGV"].Value);
LeftX = Double.Parse(recNode.Attributes["LPOGX"].Value) * Screen.PrimaryScreen.Bounds.Width;
LeftY = Double.Parse(recNode.Attributes["LPOGY"].Value) * Screen.PrimaryScreen.Bounds.Height;
- LeftPupil = Double.Parse(recNode.Attributes["LPD"].Value);
+
LeftValidation = Int32.Parse(recNode.Attributes["LPOGV"].Value);
+
+ bool rightPupilValid = Int32.Parse(recNode.Attributes["RPUPILV"].Value) == 1;
+ if (rightPupilValid)
+ {
+ RightPupil = Double.Parse(recNode.Attributes["RPUPILD"].Value) * 1000.0;
+ }
+ else
+ {
+ RightPupil = -1;
+ }
+
+ bool leftPupilValid = Int32.Parse(recNode.Attributes["LPUPILV"].Value) == 1;
+ if (leftPupilValid)
+ {
+ LeftPupil = Double.Parse(recNode.Attributes["LPUPILD"].Value) * 1000.0;
+ }
+ else
+ {
+ LeftPupil = -1;
+ }
+
+
UserLeftX = Double.Parse(recNode.Attributes["LEYEX"].Value);
UserLeftY = Double.Parse(recNode.Attributes["LEYEY"].Value);
UserLeftZ = Double.Parse(recNode.Attributes["LEYEZ"].Value);
diff --git a/itrace_core/GazeHandler.cs b/itrace_core/GazeHandler.cs
index 9af6b1a..3efd0e5 100644
--- a/itrace_core/GazeHandler.cs
+++ b/itrace_core/GazeHandler.cs
@@ -1,4 +1,15 @@
-using System;
+/********************************************************************************************************************************************************
+* @file GazeHandler.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -40,7 +51,7 @@ private void DequeueGaze()
{
OnGazeDataReceived(this, new GazeDataReceivedEventArgs(gd));
}
- Console.WriteLine(gd.Output());
+ //Console.WriteLine(gd.Output());
gd = GazeQueue.Take();
}
Console.WriteLine("Queue Thread Done!");
diff --git a/itrace_core/GazePointTracker.cs b/itrace_core/GazePointTracker.cs
index 285b614..5e71285 100644
--- a/itrace_core/GazePointTracker.cs
+++ b/itrace_core/GazePointTracker.cs
@@ -1,4 +1,15 @@
-using System;
+/********************************************************************************************************************************************************
+* @file GazePointTracker.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -21,6 +32,13 @@ public GazePointTracker()
try
{
Client = new System.Net.Sockets.TcpClient();
+ IAsyncResult conn = Client.BeginConnect(GAZEPOINT_ADDRESS, GAZEPOINT_PORT, null, null);
+
+ bool asyncConnectSuccess = conn.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(5));
+
+ if (!asyncConnectSuccess)
+ throw new Exception("Gazepoint timed out while connecting (Most likely there is no Gazepoint device available)");
+
Client.Connect(GAZEPOINT_ADDRESS, GAZEPOINT_PORT);
Reader = new System.IO.StreamReader(Client.GetStream());
Writer = new System.IO.StreamWriter(Client.GetStream());
@@ -64,7 +82,7 @@ public String GetTrackerSerialNumber()
return TrackerSerialNumber;
}
- public void StartTracker()
+ public bool StartTracker()
{
new System.Threading.Thread(() =>
{
@@ -74,6 +92,8 @@ public void StartTracker()
Console.WriteLine("START GP TRACKING");
Writer.Write("\r\n");
Writer.Flush();
+
+ return true;
}
public void StopTracker()
diff --git a/itrace_core/ITracker.cs b/itrace_core/ITracker.cs
index b367dbc..5d6e75b 100644
--- a/itrace_core/ITracker.cs
+++ b/itrace_core/ITracker.cs
@@ -1,4 +1,15 @@
-using System;
+/********************************************************************************************************************************************************
+* @file ITracker.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System;
namespace iTrace_Core
{
@@ -6,7 +17,7 @@ interface ITracker
{
void EnterCalibration();
void LeaveCalibration();
- void StartTracker();
+ bool StartTracker();
void StopTracker();
String GetTrackerName();
String GetTrackerSerialNumber();
diff --git a/itrace_core/MainWindow.xaml b/itrace_core/MainWindow.xaml
index 54ec467..f3b1688 100644
--- a/itrace_core/MainWindow.xaml
+++ b/itrace_core/MainWindow.xaml
@@ -1,60 +1,111 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/itrace_core/MainWindow.xaml.cs b/itrace_core/MainWindow.xaml.cs
index 95257ee..ad7a813 100644
--- a/itrace_core/MainWindow.xaml.cs
+++ b/itrace_core/MainWindow.xaml.cs
@@ -1,275 +1,478 @@
-using System;
-using System.Collections.Generic;
-using System.Windows;
-using System.Windows.Controls;
-using iTrace_Core.Properties;
-
-namespace iTrace_Core
-{
- public partial class MainWindow : Window
- {
- TrackerManager TrackerManager;
- Recorder rec;
- ReticleController reticleController;
- SocketServer socketServer;
- WebSocketServer webSocketServer;
- XMLGazeDataWriter xmlGazeDataWriter;
- SessionSetupWindow sessionInformation;
- List settings;
-
- class Setting
- {
- public Setting(string option)
- {
- Option = option;
- }
-
- public string Option { get; }
- public string Value { get; set; }
- }
-
- public MainWindow()
- {
- InitializeComponent();
- TrackerManager = new TrackerManager();
-
- // Initialize Session
- SessionManager.GetInstance().SetScreenDimensions(SystemParameters.PrimaryScreenWidth, SystemParameters.PrimaryScreenHeight);
-
- // Default the session to the last used output directory and empty everything else
- SessionManager.GetInstance().SetupSession("", "", "", Environment.GetFolderPath(Environment.SpecialFolder.Desktop));
- SessionManager.GetInstance().ClearCalibration();
-
- xmlGazeDataWriter = new XMLGazeDataWriter();
-
- InitializeSettingsGrid();
- }
- private void ApplicationLoaded(object sender, RoutedEventArgs e)
- {
- RefreshTrackerList();
- socketServer = new SocketServer();
- webSocketServer = new WebSocketServer();
- }
-
- private void InitializeSettingsGrid()
- {
- settings = new List()
- {
- new Setting("socket_port") { Value = Settings.Default.socket_port.ToString() },
- new Setting("websocket_port") { Value = Settings.Default.websocket_port.ToString() }
- };
-
- settingsDataGrid.ItemsSource = settings;
- }
-
- private void ApplySettings(object sender, RoutedEventArgs e)
- {
- int socketPort = 0;
- int websocketPort = 0;
- if (! (int.TryParse(settings[0].Value, out socketPort) && int.TryParse(settings[1].Value, out websocketPort)) )
- {
- MessageBox.Show("Port values must be numeric!", "Invalid Port Value", MessageBoxButton.OK, MessageBoxImage.Error);
- return;
- }
-
- if (socketPort == websocketPort)
- {
- MessageBox.Show("Port values cannot be the same value!", "Duplicate Port Values", MessageBoxButton.OK, MessageBoxImage.Error);
- return;
- }
-
- // These are separate in the event that the max values are actually different
- if (! ((socketPort <= SocketServer.MAX_PORT_NUM) && (socketPort >= SocketServer.MIN_PORT_NUM)) )
- {
- MessageBox.Show("Socket port values must be in the range: " + SocketServer.MIN_PORT_NUM + "-" + SocketServer.MAX_PORT_NUM + "!", "Invalid Socket Port Value", MessageBoxButton.OK, MessageBoxImage.Error);
- return;
- }
-
- if (! ((websocketPort <= WebSocketServer.MAX_WEBSOCKET_PORT_NUM) && (websocketPort >= WebSocketServer.MIN_WEBSOCKET_PORT_NUM)) )
- {
- MessageBox.Show("Websocket port values must be in the range: " + WebSocketServer.MIN_WEBSOCKET_PORT_NUM + "-" + WebSocketServer.MAX_WEBSOCKET_PORT_NUM + "!", "Invalid Websocket Port Value", MessageBoxButton.OK, MessageBoxImage.Error);
- return;
- }
-
- Settings.Default.socket_port = Convert.ToInt32(settings[0].Value);
- Settings.Default.websocket_port = Convert.ToInt32(settings[1].Value);
- Settings.Default.Save();
- }
-
- // Cleanup for when core closes
- private void ApplicationClosed(object sender, EventArgs e)
- {
- if (TrackerManager.Running())
- {
- TrackerManager.StopTracker();
- }
-
- if (sessionInformation != null && sessionInformation.IsLoaded)
- {
- sessionInformation.Close();
- }
-
- if (reticleController != null)
- reticleController.Close();
-
- if (xmlGazeDataWriter.Writing)
- xmlGazeDataWriter.StopWriting();
- }
-
- private void MenuExitClick(object sender, RoutedEventArgs e)
- {
- Close();
- }
-
- private void TrackerListChanged(object sender, SelectionChangedEventArgs e)
- {
- if (TrackerList.SelectedIndex >= 0)
- {
- System.Console.WriteLine(TrackerList.SelectedItem.ToString());
- TrackerManager.SetActiveTracker(TrackerList.SelectedItem.ToString());
- if (TrackerList.SelectedItem.ToString() == "Mouse")
- {
- ActivateCalibrationButton.IsEnabled = false;
- ShowEyeStatusButton.IsEnabled = false;
- }
- else
- {
- ActivateCalibrationButton.IsEnabled = true;
- ShowEyeStatusButton.IsEnabled = true;
- }
- }
- }
-
- private void RefreshAttachedTrackers(object sender, RoutedEventArgs e)
- {
- RefreshTrackerList();
- }
-
- private void RefreshTrackerList()
- {
- TrackerManager.FindTrackers();
- TrackerList.ItemsSource = TrackerManager.GetAttachedTrackers();
-
- /* Default back to mouse tracker
- Nearly all setups should have a pointing device installed meaning this prevents having
- to perform additional UI checks to make sure the user can't start a tracking session
- without a tracker.
- */
- TrackerList.SelectedIndex = 0;
- }
-
- private void StartTracker(object sender, RoutedEventArgs e)
- {
- if (TrackerManager.Running())
- {
- SessionManager.GetInstance().StopSession();
-
- ActivateTrackerButton.Content = Properties.Resources.StartTracking;
- TrackerManager.StopTracker();
-
- socketServer.SendEndSession();
- webSocketServer.SendEndSession();
-
- if (CheckScreenCap.IsChecked.HasValue && CheckScreenCap.IsChecked.Value)
- {
- rec.Dispose();
- }
-
- xmlGazeDataWriter.StopWriting();
-
- if (TrackerManager.GetActiveTracker().GetTrackerName() != "Mouse")
- {
- ActivateCalibrationButton.IsEnabled = true;
- ShowEyeStatusButton.IsEnabled = true;
- }
- TrackerList.IsEnabled = true;
- TrackerRefreshButton.IsEnabled = true;
- SessionSetupButton.IsEnabled = true;
- settingsDataGrid.IsEnabled = true;
- ApplyButton.IsEnabled = true;
- CheckScreenCap.IsEnabled = true;
- }
- else
- {
- string trackerID = TrackerManager.GetActiveTracker().GetTrackerName() + TrackerManager.GetActiveTracker().GetTrackerSerialNumber();
- if (trackerID != SessionManager.GetInstance().CalibratedTrackerID)
- SessionManager.GetInstance().ClearCalibration();
-
- //Start the session (SHOULD HAPPPEN FIRST)
- SessionManager.GetInstance().StartSession();
-
- //Name of .avi hardcoded for now
- if (CheckScreenCap.IsChecked.HasValue && CheckScreenCap.IsChecked.Value)
- {
- rec = new Recorder(new RecorderParams(SessionManager.GetInstance().DataRootDir + "/screen_rec" + "-" + SessionManager.GetInstance().CurrentSessionTimeStamp + ".avi", 10, SharpAvi.KnownFourCCs.Codecs.MotionJpeg, 80)); //screenrecording start
- SessionManager.GetInstance().GenerateScreenRecordingStart();
- }
-
- // Load previously use directory for data storage or the current users desktop directory
- xmlGazeDataWriter.StartWriting(SessionManager.GetInstance().DataRootDir);
-
- socketServer.SendSessionData();
- webSocketServer.SendSessionData();
-
- ActivateTrackerButton.Content = Properties.Resources.StopTracking;
-
- TrackerManager.StartTracker();
- ActivateCalibrationButton.IsEnabled = false;
- ShowEyeStatusButton.IsEnabled = false;
- TrackerList.IsEnabled = false;
- TrackerRefreshButton.IsEnabled = false;
- SessionSetupButton.IsEnabled = false;
- settingsDataGrid.IsEnabled = false;
- ApplyButton.IsEnabled = false;
- CheckScreenCap.IsEnabled = false;
-
- }
- }
-
- private void CalibrateTracker(object sender, RoutedEventArgs e)
- {
- TrackerManager.CalibrateActiveTracker();
- }
-
- private void ShowReticle(object sender, RoutedEventArgs e)
- {
- if (reticleController == null)
- reticleController = new ReticleController();
-
- if (reticleController.IsShown())
- {
- reticleController.HideReticle();
- ActivateReticleButton.Content = Properties.Resources.ShowReticle;
- }
- else
- {
- reticleController.ShowReticle();
- ActivateReticleButton.Content = Properties.Resources.HideReticle;
- }
- }
-
- private void SessionSetupButton_Click(object sender, RoutedEventArgs e)
- {
- // Short circuit evaluation
- // Before first use it will be null second time around it is unloaded
- if (sessionInformation == null || !sessionInformation.IsLoaded)
- {
- sessionInformation = new SessionSetupWindow();
- sessionInformation.Show();
- }
- if (sessionInformation.WindowState == WindowState.Minimized)
- {
- sessionInformation.WindowState = WindowState.Normal;
- }
- if (sessionInformation.IsLoaded)
- {
- sessionInformation.Activate();
- }
- }
-
- private void ShowEyeStatusWindow(object sender, RoutedEventArgs e)
- {
- TrackerManager.ShowEyeStatusWindow();
- }
- }
-}
+/********************************************************************************************************************************************************
+* @file MainWindow.xaml.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Windows;
+using System.Windows.Controls;
+using iTrace_Core.Properties;
+//
+namespace iTrace_Core
+{
+ enum ReplayType
+ {
+ Fixed,
+ Proportional,
+ Bidirectional
+ }
+ public partial class MainWindow : Window
+ {
+ TrackerManager TrackerManager;
+ Recorder rec;
+ ReticleController reticleController;
+ SocketServer socketServer = SocketServer.Instance();
+ WebSocketServer webSocketServer = WebSocketServer.Instance();
+ XMLGazeDataWriter xmlGazeDataWriter;
+ SessionSetupWindow sessionInformation;
+ List settings;
+
+ class Setting
+ {
+ public Setting(string option)
+ {
+ Option = option;
+ }
+
+ public string Option { get; }
+ public string Value { get; set; }
+ }
+
+ // DejaVu
+ EventRecorder eventRecorder;
+ EventReplayer eventReplayer;
+ ReplayType replayType = ReplayType.Fixed;
+ private WindowPositionManager windowPositionManager;
+
+ //Session setup save warning
+ bool sessionInfoUnsaved = false;
+
+ public MainWindow()
+ {
+ InitializeComponent();
+ TrackerManager = new TrackerManager();
+
+ // DejaVu
+ windowPositionManager = new WindowPositionManager();
+
+ // Initialize Session
+ SessionManager.GetInstance().SetScreenDimensions(SystemParameters.PrimaryScreenWidth, SystemParameters.PrimaryScreenHeight);
+
+ // Default the session to the last used output directory and empty everything else
+ SessionManager.GetInstance().SetupSession("", "", "", Environment.GetFolderPath(Environment.SpecialFolder.Desktop));
+ SessionManager.GetInstance().ClearCalibration();
+
+ xmlGazeDataWriter = new XMLGazeDataWriter();
+
+ DataOutputDir.Text = SessionManager.GetInstance().DataRootDir;
+
+ InitializeSettingsGrid();
+ }
+ private void StopWindowPositionManager(object sender, EventArgs e)
+ {
+ Console.WriteLine("StopWindowManager");
+ windowPositionManager.Stop();
+ }
+ private void RestoreWindowState(object sender, EventArgs e)
+ {
+ this.Dispatcher.Invoke(() =>
+ {
+ this.WindowState = WindowState.Normal;
+ });
+ }
+ private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
+ {
+ Console.WriteLine("Window Closing");
+ if (eventRecorder != null && eventRecorder.IsRecordInProgress) eventRecorder.Dispose();
+ if (eventReplayer != null && eventReplayer.IsReplayInProgress) eventReplayer.StopReplay();
+ windowPositionManager.Stop();
+ }
+ private void ApplicationLoaded(object sender, RoutedEventArgs e)
+ {
+ RefreshTrackerList();
+
+ System.Windows.Forms.Integration.WindowsFormsHost host = new System.Windows.Forms.Integration.WindowsFormsHost();
+ }
+ private void InitializeSettingsGrid()
+ {
+ settings = new List()
+ {
+ new Setting("socket_port") { Value = Settings.Default.socket_port.ToString() },
+ new Setting("websocket_port") { Value = Settings.Default.websocket_port.ToString() },
+ new Setting("smarteye_ip_address") { Value = Settings.Default.smarteye_ip_address.ToString() },
+ new Setting("smarteye_ip_port") { Value = Settings.Default.smarteye_ip_port.ToString() },
+ new Setting("calibration_monitor") { Value = Settings.Default.calibration_monitor.ToString() }
+ };
+
+ settingsDataGrid.ItemsSource = settings;
+ }
+
+
+ private void ApplySettings(object sender, RoutedEventArgs e)
+ {
+ int socketPort = 0;
+ int websocketPort = 0;
+ int calibrationMonitor = 0;
+ if (!(int.TryParse(settings[0].Value, out socketPort) && int.TryParse(settings[1].Value, out websocketPort) && int.TryParse(settings[2].Value, out calibrationMonitor)))
+ {
+ MessageBox.Show(Properties.Resources.PortValuesMustBeNumeric, Properties.Resources.InvalidPortValue, MessageBoxButton.OK, MessageBoxImage.Error);
+ return;
+ }
+
+ if (socketPort == websocketPort)
+ {
+ MessageBox.Show(Properties.Resources.PortValuesCannotBeSameValue, Properties.Resources.DuplicatePortValues, MessageBoxButton.OK, MessageBoxImage.Error);
+ return;
+ }
+
+ // These are separate in the event that the max values are actually different
+ if (!((socketPort <= SocketServer.MAX_PORT_NUM) && (socketPort >= SocketServer.MIN_PORT_NUM)))
+ {
+ MessageBox.Show(Properties.Resources.SocketValuesMustBeInRange + SocketServer.MIN_PORT_NUM + "-" + SocketServer.MAX_PORT_NUM + "!", Properties.Resources.InvalidSocketPortValue, MessageBoxButton.OK, MessageBoxImage.Error);
+ return;
+ }
+
+ if (!((websocketPort <= WebSocketServer.MAX_WEBSOCKET_PORT_NUM) && (websocketPort >= WebSocketServer.MIN_WEBSOCKET_PORT_NUM)))
+ {
+ MessageBox.Show(Properties.Resources.WebSocketValuesMustBeInRange + WebSocketServer.MIN_WEBSOCKET_PORT_NUM + "-" + WebSocketServer.MAX_WEBSOCKET_PORT_NUM + "!", Properties.Resources.InvalidWebSocketPortValue, MessageBoxButton.OK, MessageBoxImage.Error);
+ return;
+ }
+
+ if (!((calibrationMonitor <= System.Windows.Forms.Screen.AllScreens.Length) && (calibrationMonitor > 0)))
+ {
+ MessageBox.Show(Properties.Resources.MonitorIndexOutOfRange, Properties.Resources.MonitorIndexIncorrectValue, MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+
+ Settings.Default.socket_port = Convert.ToInt32(settings[0].Value);
+ Settings.Default.websocket_port = Convert.ToInt32(settings[1].Value);
+ Settings.Default.calibration_monitor = Convert.ToInt32(settings[2].Value);
+ Settings.Default.Save();
+ }
+ // Cleanup for when core closes
+ private void ApplicationClosed(object sender, EventArgs e)
+ {
+ if (TrackerManager.Running())
+ {
+ TrackerManager.StopTracker();
+ }
+
+ if (sessionInformation != null && sessionInformation.IsLoaded)
+ {
+ sessionInformation.Close();
+ }
+
+ if (reticleController != null)
+ reticleController.Close();
+
+ if (xmlGazeDataWriter.Writing)
+ xmlGazeDataWriter.StopWriting();
+ }
+ private void MenuExitClick(object sender, RoutedEventArgs e)
+ {
+ Close();
+ }
+ //////////////////////////////
+ /// Session Setup Tab
+ //////////////////////////////
+ private void DirectoryBrowseButton_Click(object sender, RoutedEventArgs e)
+ {
+ System.Windows.Forms.FolderBrowserDialog folderDialogue = new System.Windows.Forms.FolderBrowserDialog();
+ folderDialogue.SelectedPath = SessionManager.GetInstance().DataRootDir;
+
+ System.Windows.Forms.DialogResult result = folderDialogue.ShowDialog();
+
+ if (result == System.Windows.Forms.DialogResult.OK && !string.IsNullOrWhiteSpace(folderDialogue.SelectedPath))
+ {
+ sessionInfoUnsaved = true;
+ DataOutputDir.Text = folderDialogue.SelectedPath;
+ }
+ }
+
+ private void SaveButton_Click(object sender, RoutedEventArgs e)
+ {
+ sessionInfoUnsaved = false; //Saved!
+ SessionManager.GetInstance().SetupSession(TaskName.Text, ResearcherName.Text, ParticipantID.Text, DataOutputDir.Text);
+ //Close();
+ }
+
+ private void ClearButton_Click(object sender, RoutedEventArgs e)
+ {
+ sessionInfoUnsaved = false; //Don't care about not saving a blank entry
+ TaskName.Text = "";
+ ResearcherName.Text = "";
+ ParticipantID.Text = "";
+ DataOutputDir.Text = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
+ SessionManager.GetInstance().SetupSession(TaskName.Text, ResearcherName.Text, ParticipantID.Text, DataOutputDir.Text);
+ }
+
+ //////////////////////////////
+ /// iTrace Tracking Tab
+ //////////////////////////////
+
+ private void TrackerListChanged(object sender, SelectionChangedEventArgs e)
+ {
+ if (TrackerList.SelectedIndex >= 0)
+ {
+ TrackerManager.SetActiveTracker(TrackerList.SelectedItem.ToString());
+ if (TrackerList.SelectedItem.ToString() == "Mouse")
+ {
+ ActivateCalibrationButton.IsEnabled = false;
+ ShowEyeStatusButton.IsEnabled = false;
+ }
+ else
+ {
+ ActivateCalibrationButton.IsEnabled = true;
+ ShowEyeStatusButton.IsEnabled = true;
+ }
+ }
+ }
+
+ private void RefreshAttachedTrackers(object sender, RoutedEventArgs e)
+ {
+ RefreshTrackerList();
+ }
+
+ private void RefreshTrackerList()
+ {
+ TrackerManager.FindTrackers();
+ TrackerList.ItemsSource = TrackerManager.GetAttachedTrackers();
+
+ /* Default back to mouse tracker
+ Nearly all setups should have a pointing device installed meaning this prevents having
+ to perform additional UI checks to make sure the user can't start a tracking session
+ without a tracker.
+ */
+ TrackerList.SelectedIndex = 0;
+ }
+
+ private void StartTracker(object sender, RoutedEventArgs e)
+ {
+ if (TrackerManager.Running())
+ {
+ SessionManager.GetInstance().StopSession();
+
+ ActivateTrackerButton.Content = Properties.Resources.StartTracking;
+ TrackerManager.StopTracker();
+
+ socketServer.SendEndSession();
+ webSocketServer.SendEndSession();
+
+ if (CheckScreenCap.IsChecked.HasValue && CheckScreenCap.IsChecked.Value)
+ {
+ rec.Dispose();
+ }
+
+ xmlGazeDataWriter.StopWriting();
+
+ if (TrackerManager.GetActiveTracker().GetTrackerName() != "Mouse")
+ {
+ ActivateCalibrationButton.IsEnabled = true;
+ ShowEyeStatusButton.IsEnabled = true;
+ }
+ TrackerList.IsEnabled = true;
+ TrackerRefreshButton.IsEnabled = true;
+ settingsDataGrid.IsEnabled = true;
+ ApplyButton.IsEnabled = true;
+ CheckScreenCap.IsEnabled = true;
+
+ if (CheckDejavuRecord.IsChecked.HasValue && CheckDejavuRecord.IsChecked.Value)
+ {
+ windowPositionManager.Stop();
+ eventRecorder.StopRecording();
+ eventRecorder.Dispose();
+ }
+ }
+ else
+ {
+ bool trackerStarted = TrackerManager.StartTracker();
+
+ if (!trackerStarted)
+ {
+ //Fail to start
+ Console.WriteLine("Failed to start tracker: " + TrackerManager.GetActiveTracker().GetTrackerName());
+ return;
+ }
+
+ //Maybe figure out something less janky for this AL
+ string trackerID = TrackerManager.GetActiveTracker().GetTrackerName() + TrackerManager.GetActiveTracker().GetTrackerSerialNumber();
+ if (trackerID != SessionManager.GetInstance().CalibratedTrackerID)
+ SessionManager.GetInstance().ClearCalibration();
+
+ //Start the session (SHOULD HAPPPEN FIRST)
+ SessionManager.GetInstance().StartSession();
+
+ //Name of .avi hardcoded for now
+ if (CheckScreenCap.IsChecked.HasValue && CheckScreenCap.IsChecked.Value)
+ {
+ rec = new Recorder(new RecorderParams(SessionManager.GetInstance().DataRootDir + "/screen_rec" + "-" + SessionManager.GetInstance().CurrentSessionTimeStamp + ".avi", 10, SharpAvi.KnownFourCCs.Codecs.MotionJpeg, 80)); //screenrecording start
+ SessionManager.GetInstance().GenerateScreenRecordingStart();
+ }
+
+ // Load previously use directory for data storage or the current users desktop directory
+ xmlGazeDataWriter.StartWriting(SessionManager.GetInstance().DataRootDir);
+
+ socketServer.SendSessionData();
+ webSocketServer.SendSessionData();
+
+ ActivateTrackerButton.Content = Properties.Resources.StopTracking;
+
+ ActivateCalibrationButton.IsEnabled = false;
+ ShowEyeStatusButton.IsEnabled = false;
+ TrackerList.IsEnabled = false;
+ TrackerRefreshButton.IsEnabled = false;
+ //SessionSetupButton.IsEnabled = false;
+ settingsDataGrid.IsEnabled = false;
+ ApplyButton.IsEnabled = false;
+ CheckScreenCap.IsEnabled = false;
+
+ // DejaVu Record
+ if (CheckDejavuRecord.IsChecked.HasValue && CheckDejavuRecord.IsChecked.Value)
+ {
+ eventRecorder = new EventRecorder(new ComputerEventWriter(SessionManager.GetInstance().DataRootDir + "\\out.csv"));
+ windowPositionManager.Start();
+ eventRecorder.ConnectToCore();
+ eventRecorder.StartRecording();
+ }
+ }
+ }
+
+ private void CalibrateTracker(object sender, RoutedEventArgs e)
+ {
+ TrackerManager.CalibrateActiveTracker();
+ }
+
+ private void ShowReticle(object sender, RoutedEventArgs e)
+ {
+ if (reticleController == null)
+ reticleController = new ReticleController();
+
+ if (reticleController.IsShown())
+ {
+ reticleController.HideReticle();
+ ActivateReticleButton.Content = Properties.Resources.ShowReticle;
+ }
+ else
+ {
+ reticleController.ShowReticle();
+ ActivateReticleButton.Content = Properties.Resources.HideReticle;
+ }
+ }
+
+ private void SessionSetupButton_Click(object sender, RoutedEventArgs e)
+ {
+ // Short circuit evaluation
+ // Before first use it will be null second time around it is unloaded
+ if (sessionInformation == null || !sessionInformation.IsLoaded)
+ {
+ sessionInformation = new SessionSetupWindow();
+ sessionInformation.Show();
+ }
+ if (sessionInformation.WindowState == WindowState.Minimized)
+ {
+ sessionInformation.WindowState = WindowState.Normal;
+ }
+ if (sessionInformation.IsLoaded)
+ {
+ sessionInformation.Activate();
+ }
+ }
+
+ private void ShowEyeStatusWindow(object sender, RoutedEventArgs e)
+ {
+ TrackerManager.ShowEyeStatusWindow();
+ }
+
+ //////////////////////////////
+ /// DejaVu Replay Tab
+ //////////////////////////////
+ private void ReplayButtonClicked(object sender, RoutedEventArgs e)
+ {
+ System.Windows.Forms.OpenFileDialog fileDialog = new System.Windows.Forms.OpenFileDialog();
+ fileDialog.InitialDirectory = SessionManager.GetInstance().DataRootDir;
+ fileDialog.Filter = "csv files (*.csv)|*.csv|All files (*.*)|*.*";
+ fileDialog.FilterIndex = 2;
+ fileDialog.RestoreDirectory = true;
+
+ if (fileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
+ {
+ string path = fileDialog.FileName;
+
+ Console.WriteLine(replayType);
+ int option = Int32.Parse(ReplayOption.Text);
+ switch (replayType)
+ {
+ case ReplayType.Fixed:
+ eventReplayer = new FixedPauseEventReplayer(path, option);
+ break;
+ case ReplayType.Proportional:
+ eventReplayer = new ProportionalEventReplayer(path, option);
+ break;
+ case ReplayType.Bidirectional:
+ eventReplayer = new BidirectionalCommunicationEventReplayer(path, option);
+ break;
+ }
+
+ eventReplayer.OnReplayFinished += StopWindowPositionManager;
+ eventReplayer.OnReplayFinished += RestoreWindowState;
+
+ windowPositionManager.Start();
+ SocketServer.Instance().ReplayAcceptQueuedClients();
+ eventReplayer.StartReplay();
+
+ // TODO: Disable buttons, minimize window
+ this.WindowState = (WindowState)System.Windows.Forms.FormWindowState.Minimized;
+ }
+ }
+
+ private void FixedPauseChecked(object sender, RoutedEventArgs e)
+ {
+ replayType = ReplayType.Fixed;
+ OptionLabel.Content = "Pause Length (ms)";
+ ReplayOption.Text = "10";
+ OptionLabel.Visibility = Visibility.Visible;
+ //OptionHeader.Visibility = Visibility.Visible;
+ ReplayOption.Show();
+ }
+
+ private void ProportionalPauseChecked(object sender, RoutedEventArgs e)
+ {
+ replayType = ReplayType.Proportional;
+ OptionLabel.Content = "Scale Factor";
+ ReplayOption.Text = "3";
+ OptionLabel.Visibility = Visibility.Visible;
+ //OptionHeader.Visibility = Visibility.Visible;
+ ReplayOption.Show();
+ }
+
+ private void BidirectionalPauseChecked(object sender, RoutedEventArgs e)
+ {
+ replayType = ReplayType.Bidirectional;
+ OptionLabel.Visibility = Visibility.Hidden;
+ //OptionHeader.Visibility = Visibility.Hidden;
+ ReplayOption.Hide();
+
+ }
+
+ private void SessionSetupChanged(object sender, TextChangedEventArgs e)
+ {
+ sessionInfoUnsaved = true;
+ }
+
+ private void LeavingSessionSetup(object sender, RoutedEventArgs e)
+ {
+ TabItem sendingTab = (TabItem)sender;
+
+ if (sendingTab.Name.Equals("SessionSetup") && sessionInfoUnsaved)
+ {
+ MessageBox.Show("Session setup has not been saved!");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/itrace_core/MouseTracker.cs b/itrace_core/MouseTracker.cs
index 5ed9dc0..0c17094 100644
--- a/itrace_core/MouseTracker.cs
+++ b/itrace_core/MouseTracker.cs
@@ -1,4 +1,15 @@
-using System;
+/********************************************************************************************************************************************************
+* @file MouseTracker.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System;
namespace iTrace_Core
{
@@ -31,9 +42,10 @@ public String GetTrackerSerialNumber()
return "";
}
- public void StartTracker()
+ public bool StartTracker()
{
MouseLocationTick.Start();
+ return true;
}
public void StopTracker()
diff --git a/itrace_core/PreciseSystemTime.cs b/itrace_core/PreciseSystemTime.cs
index e76a106..dda55e4 100644
--- a/itrace_core/PreciseSystemTime.cs
+++ b/itrace_core/PreciseSystemTime.cs
@@ -1,4 +1,15 @@
-using System;
+/********************************************************************************************************************************************************
+* @file PreciseSystemTime.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
diff --git a/itrace_core/Properties/Resources.Designer.cs b/itrace_core/Properties/Resources.Designer.cs
index 0c6ddab..c47f911 100644
--- a/itrace_core/Properties/Resources.Designer.cs
+++ b/itrace_core/Properties/Resources.Designer.cs
@@ -1,198 +1,306 @@
-//------------------------------------------------------------------------------
-//
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-//
-//------------------------------------------------------------------------------
-
-namespace iTrace_Core.Properties {
- using System;
-
-
- ///
- /// A strongly-typed resource class, for looking up localized strings, etc.
- ///
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- public class Resources {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Resources() {
- }
-
- ///
- /// Returns the cached ResourceManager instance used by this class.
- ///
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- public static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("iTrace_Core.Properties.Resources", typeof(Resources).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- ///
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- ///
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- public static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- ///
- /// Looks up a localized string similar to Calibrate.
- ///
- public static string Calibrate {
- get {
- return ResourceManager.GetString("Calibrate", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Changes require a program restart to be applied.
- ///
- public static string ChangeRequiresRestart {
- get {
- return ResourceManager.GetString("ChangeRequiresRestart", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Enable Screen Recording.
- ///
- public static string EnableScreenRecording {
- get {
- return ResourceManager.GetString("EnableScreenRecording", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Hide Reticle.
- ///
- public static string HideReticle {
- get {
- return ResourceManager.GetString("HideReticle", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Port values must be numeric!.
- ///
- public static string PortValuesNumericWarningContent {
- get {
- return ResourceManager.GetString("PortValuesNumericWarningContent", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Invalid Port Value.
- ///
- public static string PortValuesNumericWarningTitle {
- get {
- return ResourceManager.GetString("PortValuesNumericWarningTitle", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Refresh.
- ///
- public static string Refresh {
- get {
- return ResourceManager.GetString("Refresh", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Session Setup.
- ///
- public static string SessionSetup {
- get {
- return ResourceManager.GetString("SessionSetup", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Show Eye Status.
- ///
- public static string ShowEyeStatus {
- get {
- return ResourceManager.GetString("ShowEyeStatus", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Show Reticle.
- ///
- public static string ShowReticle {
- get {
- return ResourceManager.GetString("ShowReticle", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Socket Server Cannot Start.
- ///
- public static string SocketServerCannotStart {
- get {
- return ResourceManager.GetString("SocketServerCannotStart", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Start Tracking.
- ///
- public static string StartTracking {
- get {
- return ResourceManager.GetString("StartTracking", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Stop Tracking.
- ///
- public static string StopTracking {
- get {
- return ResourceManager.GetString("StopTracking", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Trackers:.
- ///
- public static string Trackers {
- get {
- return ResourceManager.GetString("Trackers", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Websocket Server Cannot Start.
- ///
- public static string WebSocketServerCannotStart {
- get {
- return ResourceManager.GetString("WebSocketServerCannotStart", resourceCulture);
- }
- }
- }
-}
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace iTrace_Core.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ public class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("iTrace_Core.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Another service is running on port .
+ ///
+ public static string AnotherServiceIsRunningOnPort {
+ get {
+ return ResourceManager.GetString("AnotherServiceIsRunningOnPort", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Calibrate.
+ ///
+ public static string Calibrate {
+ get {
+ return ResourceManager.GetString("Calibrate", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Changes require a program restart to be applied.
+ ///
+ public static string ChangeRequiresRestart {
+ get {
+ return ResourceManager.GetString("ChangeRequiresRestart", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Duplicate Port Values.
+ ///
+ public static string DuplicatePortValues {
+ get {
+ return ResourceManager.GetString("DuplicatePortValues", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Enable Screen Recording.
+ ///
+ public static string EnableScreenRecording {
+ get {
+ return ResourceManager.GetString("EnableScreenRecording", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Hide Reticle.
+ ///
+ public static string HideReticle {
+ get {
+ return ResourceManager.GetString("HideReticle", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Invalid Port Value.
+ ///
+ public static string InvalidPortValue {
+ get {
+ return ResourceManager.GetString("InvalidPortValue", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Invalid Socket Port Value.
+ ///
+ public static string InvalidSocketPortValue {
+ get {
+ return ResourceManager.GetString("InvalidSocketPortValue", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Invalid Websocket Port Value".
+ ///
+ public static string InvalidWebSocketPortValue {
+ get {
+ return ResourceManager.GetString("InvalidWebSocketPortValue", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Invalid Monitor.
+ ///
+ public static string MonitorIndexIncorrectValue {
+ get {
+ return ResourceManager.GetString("MonitorIndexIncorrectValue", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Selected monitor must exist.
+ ///
+ public static string MonitorIndexOutOfRange {
+ get {
+ return ResourceManager.GetString("MonitorIndexOutOfRange", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Port values cannot be the same value!.
+ ///
+ public static string PortValuesCannotBeSameValue {
+ get {
+ return ResourceManager.GetString("PortValuesCannotBeSameValue", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Port values must be numeric!.
+ ///
+ public static string PortValuesMustBeNumeric {
+ get {
+ return ResourceManager.GetString("PortValuesMustBeNumeric", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Refresh.
+ ///
+ public static string Refresh {
+ get {
+ return ResourceManager.GetString("Refresh", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Session Setup.
+ ///
+ public static string SessionSetup {
+ get {
+ return ResourceManager.GetString("SessionSetup", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Show Eye Status.
+ ///
+ public static string ShowEyeStatus {
+ get {
+ return ResourceManager.GetString("ShowEyeStatus", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Show Reticle.
+ ///
+ public static string ShowReticle {
+ get {
+ return ResourceManager.GetString("ShowReticle", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Socket Server Cannot Start.
+ ///
+ public static string SocketServerCannotStart {
+ get {
+ return ResourceManager.GetString("SocketServerCannotStart", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Socket port values must be in the range: .
+ ///
+ public static string SocketValuesMustBeInRange {
+ get {
+ return ResourceManager.GetString("SocketValuesMustBeInRange", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Replay.
+ ///
+ public static string StartReplay {
+ get {
+ return ResourceManager.GetString("StartReplay", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Start Tracking.
+ ///
+ public static string StartTracking {
+ get {
+ return ResourceManager.GetString("StartTracking", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Stop that service or change the port for iTrace Core in settings and restart the Core..
+ ///
+ public static string StopServiceOrChangePortThenRestart {
+ get {
+ return ResourceManager.GetString("StopServiceOrChangePortThenRestart", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Stop Tracking.
+ ///
+ public static string StopTracking {
+ get {
+ return ResourceManager.GetString("StopTracking", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Record with DejaVu.
+ ///
+ public static string ToggleDejaVu {
+ get {
+ return ResourceManager.GetString("ToggleDejaVu", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Trackers:.
+ ///
+ public static string Trackers {
+ get {
+ return ResourceManager.GetString("Trackers", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Websocket Server Cannot Start.
+ ///
+ public static string WebSocketServerCannotStart {
+ get {
+ return ResourceManager.GetString("WebSocketServerCannotStart", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Websocket port values must be in the range: .
+ ///
+ public static string WebSocketValuesMustBeInRange {
+ get {
+ return ResourceManager.GetString("WebSocketValuesMustBeInRange", resourceCulture);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/itrace_core/Properties/Resources.resx b/itrace_core/Properties/Resources.resx
index 4637f96..02a2d3f 100644
--- a/itrace_core/Properties/Resources.resx
+++ b/itrace_core/Properties/Resources.resx
@@ -1,165 +1,201 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- Calibrate
-
-
- Changes require a program restart to be applied
-
-
- Enable Screen Recording
-
-
- Hide Reticle
-
-
- Port values must be numeric!
-
-
- Invalid Port Value
-
-
- Refresh
-
-
- Session Setup
-
-
- Show Eye Status
-
-
- Show Reticle
-
-
- Socket Server Cannot Start
-
-
- Start Tracking
-
-
- Stop Tracking
-
-
- Trackers:
-
-
- Websocket Server Cannot Start
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Another service is running on port
+
+
+ Calibrate
+
+
+ Changes require a program restart to be applied
+
+
+ Duplicate Port Values
+
+
+ Enable Screen Recording
+
+
+ Hide Reticle
+
+
+ Invalid Port Value
+
+
+ Invalid Socket Port Value
+
+
+ Invalid Websocket Port Value"
+
+
+ Invalid Monitor
+
+
+ Selected monitor must exist
+
+
+ Port values cannot be the same value!
+
+
+ Port values must be numeric!
+
+
+ Refresh
+
+
+ Session Setup
+
+
+ Show Eye Status
+
+
+ Show Reticle
+
+
+ Socket Server Cannot Start
+
+
+ Socket port values must be in the range:
+
+
+ Replay
+
+
+ Start Tracking
+
+
+ Stop that service or change the port for iTrace Core in settings and restart the Core.
+
+
+ Stop Tracking
+
+
+ Record with DejaVu
+
+
+ Trackers:
+
+
+ Websocket Server Cannot Start
+
+
+ Websocket port values must be in the range:
+
\ No newline at end of file
diff --git a/itrace_core/Properties/Settings.Designer.cs b/itrace_core/Properties/Settings.Designer.cs
index 3841749..a4d43c1 100644
--- a/itrace_core/Properties/Settings.Designer.cs
+++ b/itrace_core/Properties/Settings.Designer.cs
@@ -12,7 +12,7 @@ namespace iTrace_Core.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.10.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@@ -46,5 +46,41 @@ public int websocket_port {
this["websocket_port"] = value;
}
}
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("192.169.100.42")]
+ public string smarteye_ip_address {
+ get {
+ return ((string)(this["smarteye_ip_address"]));
+ }
+ set {
+ this["smarteye_ip_address"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("5800")]
+ public int smarteye_ip_port {
+ get {
+ return ((int)(this["smarteye_ip_port"]));
+ }
+ set {
+ this["smarteye_ip_port"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("0")]
+ public int calibration_monitor {
+ get {
+ return ((int)(this["calibration_monitor"]));
+ }
+ set {
+ this["calibration_monitor"] = value;
+ }
+ }
}
}
diff --git a/itrace_core/Properties/Settings.settings b/itrace_core/Properties/Settings.settings
index cb1b9af..087ef82 100644
--- a/itrace_core/Properties/Settings.settings
+++ b/itrace_core/Properties/Settings.settings
@@ -8,5 +8,14 @@
7007
+
+ 192.169.100.42
+
+
+ 5800
+
+
+ 0
+
-
\ No newline at end of file
+
diff --git a/itrace_core/Reticle.cs b/itrace_core/Reticle.cs
index d6934ef..52bd3d3 100644
--- a/itrace_core/Reticle.cs
+++ b/itrace_core/Reticle.cs
@@ -75,9 +75,12 @@ public void UpdateReticle(int x, int y)
if (!display)
return;
- // Invalid screen coordinates...
- if (x < 0 || y < 0)
- return;
+ //AL: This check needs redone, negative coordinates are possible when using multiple screens
+ //A similar check can be done by consulting the Screen class
+
+ //// Invalid screen coordinates...
+ //if (x < 0 || y < 0)
+ // return;
//Sum up all the x and y we have seen
totalX += x;
diff --git a/itrace_core/ReticleController.cs b/itrace_core/ReticleController.cs
index 0e9e359..9cf2591 100644
--- a/itrace_core/ReticleController.cs
+++ b/itrace_core/ReticleController.cs
@@ -1,4 +1,15 @@
-using System.Threading;
+/********************************************************************************************************************************************************
+* @file ReticleController.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System.Threading;
namespace iTrace_Core
{
diff --git a/itrace_core/SEWorldModel.cs b/itrace_core/SEWorldModel.cs
new file mode 100644
index 0000000..7d43d09
--- /dev/null
+++ b/itrace_core/SEWorldModel.cs
@@ -0,0 +1,391 @@
+/********************************************************************************************************************************************************
+* @file SEWorldModel.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Numerics;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml;
+
+namespace iTrace_Core
+{
+ //A SmartEye world model which can be parsed from or serialized to a string
+ public class SEWorldModel
+ {
+ private List objects;
+
+ public SEWorldScreen[] GetScreens()
+ {
+ List screens = new List();
+
+ foreach (SEWorldObject obj in objects)
+ if (obj is SEWorldScreen)
+ screens.Add(obj as SEWorldScreen);
+
+ return screens.ToArray();
+ }
+
+ public static string StripWorldModelString(string worldModelString)
+ {
+ StringBuilder sb = new StringBuilder(worldModelString.Length);
+
+ bool lastCharWhitespace = false;
+ bool comment = false;
+
+ //Removes extra whitespace, and removes comments starting with //
+ for (int i = 0; i < worldModelString.Length; i++)
+ {
+ bool isWhitespace = Char.IsWhiteSpace(worldModelString[i]);
+
+ if (!comment)
+ {
+ if (lastCharWhitespace && isWhitespace)
+ continue;
+ else if (worldModelString[i].Equals('/'))
+ {
+ if (i < worldModelString.Length - 1 && worldModelString[i+1].Equals('/'))
+ {
+ comment = true;
+ }
+ }
+ else
+ sb.Append(worldModelString[i]);
+
+ lastCharWhitespace = isWhitespace;
+ }
+ else
+ {
+ bool isNewline = worldModelString[i].Equals('\n') || worldModelString[i].Equals('\r');
+
+ if (isNewline)
+ {
+ comment = false;
+ sb.Append('\n');
+ }
+
+ lastCharWhitespace = false;
+ }
+ }
+
+ worldModelString = sb.ToString();
+ sb = new StringBuilder(worldModelString.Length);
+
+ //Remove extra \n
+ //Whitespace does not reset "lastCharNewline"
+
+ bool lastCharNewline = true;
+ lastCharWhitespace = true; //A newline is whitespace
+
+ for (int i = 0; i < worldModelString.Length; i++)
+ {
+ bool isNewline = worldModelString[i].Equals('\n') || worldModelString[i].Equals('\r');
+ bool isWhitespace = Char.IsWhiteSpace(worldModelString[i]);
+
+ if (isNewline)
+ {
+ if (!lastCharNewline)
+ sb.Append(worldModelString[i]);
+
+ lastCharWhitespace = true;
+ lastCharNewline = true;
+
+ } else if (isWhitespace)
+ {
+ if (!lastCharWhitespace)
+ sb.Append(worldModelString[i]);
+
+ lastCharWhitespace = true;
+ //lastCharNewline = false;
+
+ } else //if(!isWhitespace): is a printing character
+ {
+ sb.Append(worldModelString[i]);
+
+ lastCharWhitespace = false;
+ lastCharNewline = false;
+ }
+ }
+
+ return sb.ToString();
+ }
+
+ public SEWorldModel(string worldModelString)
+ {
+ objects = new List();
+
+ //If a // is encountered, skip to the next new line
+ //If a keyword is encountered, switch on that keyword and pass the contents of the next curly brackets to the respective class constructor
+ try
+ {
+ int i = 0; //Start of imaginary cursor for parsing words
+
+
+ //Strip comments and extra whitespace
+ string worldModelCode = StripWorldModelString(worldModelString);
+
+ i = 0;
+ int j = 0;
+
+ //TODO: huge number of messy edge cases. Need better string normalization
+ while (j < worldModelCode.Length)
+ {
+ //Consume word
+ if (Char.IsWhiteSpace(worldModelCode[j]))
+ {
+ string word = worldModelCode.Substring(i, j - i).Trim();
+
+ switch (word)
+ {
+ case "Screen":
+ objects.Add(new SEWorldScreen(PeelBraces(worldModelCode, i)));
+ break;
+ case "Plane":
+ objects.Add(new SEWorldPlane(PeelBraces(worldModelCode, i)));
+ break;
+ case "CalibrationPoint3D":
+ objects.Add(new SEWorldCalibrationPoint(PeelBraces(worldModelCode, i)));
+ break;
+ }
+
+ i = j + 1; //Move start cursor to after word
+ }
+
+ j++;
+ }
+ } catch (Exception e)
+ {
+ Console.WriteLine("Failed to parse WorldModel string: " + e);
+ }
+ }
+
+ //Removes one layer of braces from a code block
+ public static string PeelBraces(string bracedBlock, int startIndex)
+ {
+ int i = startIndex;
+ int j = i;
+
+ while (!bracedBlock[i++].Equals('{')) ;
+ j = i;
+
+ //Number of brace layers (this may not be necessary, check with programming guide to see if nested braces even exist in WorldModel format)
+ int braceLevel = 1;
+
+ while (j < bracedBlock.Length)
+ {
+ if (bracedBlock[j].Equals('{'))
+ braceLevel++;
+ else if (bracedBlock[j].Equals('}'))
+ braceLevel--;
+
+ if (braceLevel == 0)
+ break;
+
+ j++;
+ }
+
+ return bracedBlock.Substring(i, j - i);
+ }
+
+ //Doesn't go here
+ public static Vector3 ParseVector3(string vectorString)
+ {
+ string[] terms = vectorString.Split(',');
+
+ if (terms.Length != 3)
+ throw new Exception("Invalid vector string: " + vectorString);
+
+ return new Vector3(float.Parse(terms[0]), float.Parse(terms[1]), float.Parse(terms[2]));
+ }
+
+ public static Vector2 ParseVector2(string vectorString)
+ {
+ string[] terms = vectorString.Split(',');
+
+ if (terms.Length != 2)
+ throw new Exception("Invalid vector string: " + vectorString);
+
+ return new Vector2(float.Parse(terms[0]), float.Parse(terms[1]));
+ }
+
+ public void WriteToXMLWriter(XmlTextWriter writer)
+ {
+ writer.WriteStartElement("worldModel");
+
+ foreach (SEWorldObject worldObject in objects)
+ worldObject.WriteToXMLWriter(writer);
+
+ writer.WriteEndElement();
+ }
+ }
+
+ public abstract class SEWorldObject
+ {
+ public string name { get; protected set; }
+
+ protected SEWorldObject(string block)
+ {
+ string[] lines = block.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
+
+ foreach (string line in lines)
+ {
+ if (String.IsNullOrWhiteSpace(line))
+ continue;
+
+ string[] terms = line.Split('=');
+
+ if (terms.Length != 2)
+ throw new Exception("Line contains more than one assignment?: " + line);
+
+ string assignee = terms[0].Trim();
+ string value = terms[1].Trim();
+
+ ParseParameter(assignee, value);
+ }
+
+ if (name == null)
+ throw new Exception("SEWorld object Not fully initialized!");
+ }
+
+ protected virtual void ParseParameter(string assignee, string value)
+ {
+ if (assignee == "name")
+ this.name = value.Replace("\"", "");
+ }
+
+ public abstract void WriteToXMLWriter(XmlTextWriter writer);
+ }
+
+ public class SEWorldPlane : SEWorldObject
+ {
+ public Vector3 lowerLeft { get; private set; }
+ public Vector3 xAxis { get; private set; }
+ public Vector3 yAxis { get; private set; }
+ public Vector2 size { get; private set; }
+
+ public static string example = "\tname = \"ControllerLeft\"\n\tlowerLeft = 0.086, -0.003, 0.249\n\txAxis = 0.087, 0.011, 0.039\n\tyAxis = 0.042, 0.047, -0.156\n\tsize = 0.096, 0.168";
+
+ //Parse a Plane from the world model, takes the contents of the Plane block inside and excluding curly braces
+ public SEWorldPlane(String planeBlock) : base(planeBlock)
+ {
+ if (!(lowerLeft != null && xAxis != null && yAxis != null && size != null))
+ throw new Exception("SEWorld object Not fully initialized!");
+ }
+
+ protected override void ParseParameter(string assignee, string value)
+ {
+ base.ParseParameter(assignee, value);
+
+ switch (assignee)
+ {
+ case "lowerLeft":
+ this.lowerLeft = SEWorldModel.ParseVector3(value);
+ break;
+
+ case "xAxis":
+ this.xAxis = SEWorldModel.ParseVector3(value);
+ break;
+
+ case "yAxis":
+ this.yAxis = SEWorldModel.ParseVector3(value);
+ break;
+
+ case "size":
+ this.size = SEWorldModel.ParseVector2(value);
+ break;
+ }
+ }
+
+ protected virtual void WriteXMLAttributes(XmlTextWriter writer)
+ {
+ writer.WriteAttributeString("name", name);
+ writer.WriteAttributeString("lowerLeft", lowerLeft.ToString());
+ writer.WriteAttributeString("xAxis", xAxis.ToString());
+ writer.WriteAttributeString("yAxis", yAxis.ToString());
+ writer.WriteAttributeString("size", size.ToString());
+ }
+
+ public override void WriteToXMLWriter(XmlTextWriter writer)
+ {
+ writer.WriteStartElement("plane");
+ WriteXMLAttributes(writer);
+ writer.WriteEndElement();
+ }
+ }
+
+ public class SEWorldScreen : SEWorldPlane
+ {
+ public int[] resolution { get; private set; }
+
+ public static string example = "name = \"ScreenRight\"\n\tlowerLeft = 0.023, 0.058, -0.175\n\txAxis = 0.573, 0.005, 0.172\n\tyAxis = -0.002, 0.329, -0.006\n\tsize = 0.598, 0.329\n\tresolution = 1920, 1080";
+
+ protected override void ParseParameter(string assignee, string value)
+ {
+ base.ParseParameter(assignee, value);
+
+ if (assignee == "resolution")
+ {
+ string[] res = value.Split(',');
+
+ this.resolution = new int[2];
+ this.resolution[0] = int.Parse(res[0]);
+ this.resolution[1] = int.Parse(res[1]);
+ }
+ }
+
+ public SEWorldScreen(string screenBlock) : base(screenBlock)
+ {
+ if (resolution == null)
+ throw new Exception("SEWorld object Not fully initialized!");
+ }
+
+ protected override void WriteXMLAttributes(XmlTextWriter writer)
+ {
+ base.WriteXMLAttributes(writer);
+ writer.WriteAttributeString("resolution", "(" + resolution[0].ToString() + "," + resolution[1].ToString() + ")" );
+ }
+
+ public override void WriteToXMLWriter(XmlTextWriter writer)
+ {
+ writer.WriteStartElement("screen");
+ WriteXMLAttributes(writer);
+ writer.WriteEndElement();
+ }
+ }
+
+ public class SEWorldCalibrationPoint : SEWorldObject
+ {
+ public Vector3 center { get; private set; }
+
+ protected override void ParseParameter(string assignee, string value)
+ {
+ base.ParseParameter(assignee, value);
+
+ if (assignee == "center")
+ this.center = SEWorldModel.ParseVector3(value);
+ }
+
+ public SEWorldCalibrationPoint(string block) : base(block)
+ {
+ if (center == null)
+ throw new Exception("SEWorld object Not fully initialized!");
+ }
+
+ public override void WriteToXMLWriter(XmlTextWriter writer)
+ {
+ writer.WriteStartElement("calibrationPoint3D");
+ writer.WriteAttributeString("center", center.ToString());
+ writer.WriteEndElement();
+ }
+ }
+}
diff --git a/itrace_core/ScreenMapping.cs b/itrace_core/ScreenMapping.cs
new file mode 100644
index 0000000..291d0f8
--- /dev/null
+++ b/itrace_core/ScreenMapping.cs
@@ -0,0 +1,217 @@
+/********************************************************************************************************************************************************
+* @file ScreenMapping.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace iTrace_Core
+{
+ ///
+ /// An association between SmartEye WorldModel screens and Screen objects in .NET (Screens as understood by the host OS)
+ ///
+ class ScreenMapping
+ {
+ List rules;
+ Dictionary mapping;
+
+ public ScreenMapping(SEWorldScreen[] seScreens, Screen[] osScreens)
+ {
+ rules = new List();
+
+ //Mapping rules in order of priority
+ rules.Add(new ExactNameRule(seScreens, osScreens));
+ rules.Add(new ExactResolutionRule(seScreens, osScreens));
+ rules.Add(new NumberRule(seScreens, osScreens));
+
+ mapping = new Dictionary();
+
+ foreach (ScreenMappingRule rule in rules)
+ if (rule.IsOneToOne())
+ {
+ mapping = rule.GetMapping();
+ break;
+ }
+ }
+
+ //Check that world model maps any screens
+ public bool IsValid()
+ {
+ return mapping.Count > 0;
+ }
+
+ ///
+ /// Get the screen whose name matches the name of the provided seScreenName, presumably obtained from an intersection
+ /// Returns null if no screen has this name.
+ ///
+ ///
+ ///
+ public Screen GetSEToScreenMapping(String seScreenName)
+ {
+ foreach (KeyValuePair entry in mapping)
+ {
+ if (entry.Key.name == seScreenName)
+ return entry.Value;
+ }
+
+ return null;
+ }
+ }
+
+ ///
+ /// Represents a rule by which screens can be associated in the ScreensAssociater
+ ///
+ public abstract class ScreenMappingRule
+ {
+ private List associations;
+
+ public ScreenMappingRule(SEWorldScreen[] seScreens, Screen[] osScreens)
+ {
+ this.associations = new List();
+
+ for (int i = 0; i < seScreens.Length; i++)
+ for (int j = 0; j < osScreens.Length; j++)
+ if (Matches(seScreens[i], osScreens[j]))
+ associations.Add(new ScreenAssociation(seScreens[i], osScreens[j]));
+ }
+
+ public abstract bool Matches(SEWorldScreen seScreen, Screen osScreen);
+
+ public Dictionary GetMapping()
+ {
+ Dictionary map = new Dictionary();
+
+ foreach (ScreenAssociation assoc in associations)
+ map.Add(assoc.seScreen, assoc.osScreen);
+
+ return map;
+ }
+
+ ///
+ /// Returns true if this term contains exactly one entry per screen
+ /// And crucially, has any associations
+ ///
+ ///
+ public bool IsOneToOne()
+ {
+ if (associations.Count == 0)
+ return false;
+
+ for (int i = 0; i < associations.Count; i++)
+ for (int j = 0; j < associations.Count; j++)
+ if (i != j && associations[i].Overlaps(associations[j]))
+ return false;
+
+ return true;
+ }
+
+ public override string ToString()
+ {
+ string s = "";
+
+ if (IsOneToOne())
+ s += "One-to-One Association: ";
+ else
+ s += "Non One-to-One Association: ";
+
+ foreach (ScreenAssociation a in associations)
+ s += a.ToString() + ", ";
+
+ return s;
+ }
+
+ private class ScreenAssociation
+ {
+ public SEWorldScreen seScreen { get; private set; }
+ public Screen osScreen { get; private set; }
+
+ public ScreenAssociation(SEWorldScreen seScreen, Screen osScreen)
+ {
+ this.seScreen = seScreen;
+ this.osScreen = osScreen;
+ }
+
+ public bool Overlaps(ScreenAssociation b)
+ {
+ return this.seScreen.Equals(b.seScreen) || this.osScreen.Equals(b.osScreen);
+ }
+
+ public override string ToString()
+ {
+ return seScreen.name + " -> " + osScreen.DeviceName;
+ }
+ }
+ }
+
+ ///
+ /// Check if screens have exactly the same name
+ ///
+ public class ExactNameRule : ScreenMappingRule
+ {
+ public ExactNameRule(SEWorldScreen[] seScreens, Screen[] osScreens) : base(seScreens, osScreens) { }
+
+ public override bool Matches(SEWorldScreen seScreen, Screen osScreen)
+ {
+ return seScreen.name.Equals(osScreen.DeviceName, StringComparison.InvariantCultureIgnoreCase);
+ }
+ }
+
+ ///
+ /// Check if screens share an exact resolution
+ ///
+ public class ExactResolutionRule : ScreenMappingRule
+ {
+ public ExactResolutionRule(SEWorldScreen[] seScreens, Screen[] osScreens) : base(seScreens, osScreens) { }
+
+ public override bool Matches(SEWorldScreen seScreen, Screen osScreen)
+ {
+ return seScreen.resolution[0] == osScreen.Bounds.Size.Width && seScreen.resolution[1] == osScreen.Bounds.Size.Height;
+ }
+ }
+
+ ///
+ /// Check if screens share a common number somewhere in their name
+ ///
+ public class NumberRule : ScreenMappingRule
+ {
+ public NumberRule(SEWorldScreen[] seScreens, Screen[] osScreens) : base(seScreens, osScreens) { }
+
+ public override bool Matches(SEWorldScreen seScreen, Screen osScreen)
+ {
+ int seNumber;
+ int osNumber;
+
+ if (Int32.TryParse(Regex.Match(seScreen.name, @"\d+").Value, out seNumber) &&
+ Int32.TryParse(Regex.Match(osScreen.DeviceName, @"\d+").Value, out osNumber))
+
+ return seNumber == osNumber;
+
+ return false;
+ }
+ }
+
+ ///
+ /// Look for spatial words like Left Center and Right and match them to Screen bounds
+ ///
+ public class SpatialWordRule : ScreenMappingRule
+ {
+ public SpatialWordRule(SEWorldScreen[] seScreens, Screen[] osScreens) : base(seScreens, osScreens) { }
+
+ public override bool Matches(SEWorldScreen seScreen, Screen osScreen)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/itrace_core/ScreenRecorder.cs b/itrace_core/ScreenRecorder.cs
index ed64c1f..df4ff5b 100644
--- a/itrace_core/ScreenRecorder.cs
+++ b/itrace_core/ScreenRecorder.cs
@@ -1,4 +1,15 @@
-using System;
+/********************************************************************************************************************************************************
+* @file ScreenRecorder.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
@@ -8,10 +19,54 @@
using SharpAvi.Codecs;
using SharpAvi.Output;
using System.Windows.Forms;
-
+//using static iTrace_Core.RecorderParams;
namespace iTrace_Core
{
+ // Used to retrieve Mouse Info
+ public static class MouseCursor
+ {
+ public const Int32 CURSOR_SHOWING = 0x00000001;
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct ICONINFO
+ {
+ public bool fIcon;
+ public Int32 xHotspot;
+ public Int32 yHotspot;
+ public IntPtr hbmMask;
+ public IntPtr hbmColor;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct POINT
+ {
+ public Int32 x;
+ public Int32 y;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct CURSORINFO
+ {
+ public Int32 cbSize;
+ public Int32 flags;
+ public IntPtr hCursor;
+ public POINT ptScreenPos;
+ }
+
+ [DllImport("user32.dll")]
+ public static extern bool GetCursorInfo(out CURSORINFO pci);
+
+ [DllImport("user32.dll")]
+ public static extern IntPtr CopyIcon(IntPtr hIcon);
+
+ [DllImport("user32.dll")]
+ public static extern bool DrawIcon(IntPtr hdc, int x, int y, IntPtr hIcon);
+
+ [DllImport("user32.dll")]
+ public static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO piconinfo);
+ }
+
// Used to Configure the Recorder
public class RecorderParams
{
@@ -143,9 +198,27 @@ public void Screenshot(byte[] Buffer)
using (var g = Graphics.FromImage(BMP))
{
g.CopyFromScreen(Point.Empty, Point.Empty, new Size(Params.Width, Params.Height), CopyPixelOperation.SourceCopy);
-
+ MouseCursor.CURSORINFO cursorInfo;
+ cursorInfo.cbSize = Marshal.SizeOf(typeof(MouseCursor.CURSORINFO));
+ if(MouseCursor.GetCursorInfo(out cursorInfo))
+ {
+ if(cursorInfo.flags == MouseCursor.CURSOR_SHOWING)
+ {
+ var iconPointer = MouseCursor.CopyIcon(cursorInfo.hCursor);
+ MouseCursor.ICONINFO iconInfo;
+ int iconX, iconY;
+
+ if(MouseCursor.GetIconInfo(iconPointer, out iconInfo))
+ {
+ iconX = cursorInfo.ptScreenPos.x - ((int)iconInfo.xHotspot);
+ iconY = cursorInfo.ptScreenPos.y - ((int)iconInfo.yHotspot);
+
+ MouseCursor.DrawIcon(g.GetHdc(), iconX, iconY, cursorInfo.hCursor);
+ g.ReleaseHdc();
+ }
+ }
+ }
g.Flush();
-
var bits = BMP.LockBits(new Rectangle(0, 0, Params.Width, Params.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);
Marshal.Copy(bits.Scan0, Buffer, 0, Buffer.Length);
BMP.UnlockBits(bits);
diff --git a/itrace_core/SessionManager.cs b/itrace_core/SessionManager.cs
index 55737ae..664dba9 100644
--- a/itrace_core/SessionManager.cs
+++ b/itrace_core/SessionManager.cs
@@ -1,4 +1,15 @@
-using System;
+/********************************************************************************************************************************************************
+* @file SessionManager.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System;
namespace iTrace_Core
{
diff --git a/itrace_core/SessionSetupWindow.xaml b/itrace_core/SessionSetupWindow.xaml
index 6cf2fac..f1fcad1 100644
--- a/itrace_core/SessionSetupWindow.xaml
+++ b/itrace_core/SessionSetupWindow.xaml
@@ -1,4 +1,16 @@
-.
+********************************************************************************************************************************************************/
+-->
+.
+********************************************************************************************************************************************************/
+
+using System;
using System.Windows;
namespace iTrace_Core
diff --git a/itrace_core/SmartEyeRPCUtils.cs b/itrace_core/SmartEyeRPCUtils.cs
new file mode 100644
index 0000000..2298fc2
--- /dev/null
+++ b/itrace_core/SmartEyeRPCUtils.cs
@@ -0,0 +1,156 @@
+/********************************************************************************************************************************************************
+* @file SmartEyeRPCUtils.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace iTrace_Core
+{
+ //Classes for serialization of various SmartEye RPC commands and responses
+ public class SERPC
+ {
+ public string jsonrpc = "2.0";
+ public string method = "ping";
+ public int id = 0;
+
+ //Notification type RPCs do not get a response from the server
+ bool isNotification = true;
+
+ public SERPC()
+ {
+
+ }
+
+ public SERPC(string methodName)
+ {
+ this.method = methodName;
+ }
+
+ public SERPC(string methodName, bool isNotif)
+ {
+ this.method = methodName;
+ this.isNotification = isNotif;
+ }
+
+ //Serialize command to JSON and convert to Netstring
+ public String GetNetstring()
+ {
+ String json = JsonConvert.SerializeObject(this);
+ return NetstringUtils.MakeSENetstring(json);
+ }
+ }
+
+ public class SERPCOpenTcp
+ {
+ public string ip;
+
+ public SERPCOpenTcp(IPAddress remoteAddress)
+ {
+
+ }
+ }
+
+ public class SERPCGetTargetStats : SERPC
+ {
+ [JsonProperty("params")]
+ public int[] rpcParams;
+
+ public SERPCGetTargetStats(int targetId) : base("retrieveTargetStatistics", false)
+ {
+ rpcParams = new int[1];
+ rpcParams[0] = targetId;
+ }
+ }
+
+ //Put this somewhere else
+ //SmartEye target calibration result, deserialized from a call to retrieveTargetStatistics
+ public class SETarget
+ {
+ public int targetId;
+
+ //Stats
+ public double[] stdDev;
+ public double[] accuracy;
+
+ //Error vectors
+ public double[] errorsxl;
+ public double[] errorsyl;
+ public double[] errorsxr;
+ public double[] errorsyr;
+
+ //A target should only be considered if it has at least one error vector in either eye
+ public Boolean TargetValid()
+ {
+ return errorsxl.Length > 0 || errorsxr.Length > 0;
+ }
+ }
+
+ public class NetstringUtils
+ {
+ //Converts raw rpc command into a netstring as defined in the Programmers Guide
+ public static String MakeSENetstring(String rpc)
+ {
+ return rpc.Length.ToString() + ":" + rpc + ",";
+ }
+
+ //Converts a netstring to a json string
+ //A netstring may look like this 16:{"memes":"dank"},
+ //Where 16 is the length of the json before it was formatted to a netstring
+ public static String TrimSENetstring(String netstring)
+ {
+ int length = 0;
+ String json = null;
+
+ //Netstring must end with comma
+ if (!(netstring[netstring.Length - 1] == ','))
+ throw new ArgumentException("Netstring does not end with a comma");
+
+ for (int i = 0; i < netstring.Length; i++)
+ {
+ //Find end of length prefix
+ if (!(netstring[i] >= '0' && netstring[i] <= '9'))
+ {
+ if (i == 0)
+ throw new ArgumentException("Netstring does not start with a length field");
+
+ //Parse length prefix
+ length = int.Parse(netstring.Substring(0, i));
+
+ //Make sure seperator present
+ if (!(netstring[i] == ':'))
+ throw new ArgumentException("Netstring does not have a seperator between the length field and content");
+
+ //Trim off length prefix
+ json = netstring.Substring(i + 1);
+
+ //Trim string based on length
+ json = json.Substring(0, length);
+
+ break;
+ }
+ }
+
+ if (json == null)
+ throw new Exception("Tried to parse invalid netstring: " + netstring);
+
+ //Check length
+ if (json.Length != length)
+ throw new Exception("Netstring body does not match length prefix: " + netstring);
+
+ return json;
+ }
+ }
+}
diff --git a/itrace_core/SmartEyeTracker.cs b/itrace_core/SmartEyeTracker.cs
new file mode 100644
index 0000000..1c461cb
--- /dev/null
+++ b/itrace_core/SmartEyeTracker.cs
@@ -0,0 +1,382 @@
+/********************************************************************************************************************************************************
+* @file SmartEyeTracker.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using Newtonsoft.Json;
+using System;
+using System.Net;
+using System.Text;
+using iTrace_Core.Properties;
+using Newtonsoft.Json.Linq;
+using System.Collections.Generic;
+using System.Windows.Forms;
+
+namespace iTrace_Core
+{
+ class SmartEyeTracker : ITracker
+ {
+ private readonly int SMARTEYE_PORT_LATENT = 5799;
+ private readonly int SMARTEYE_PORT_RPC = 8100; //This is the default from SE software
+
+ private System.Net.Sockets.UdpClient RealtimeClient;
+ private System.Net.Sockets.TcpClient LatentClient;
+ private System.Net.Sockets.TcpClient RpcClient;
+ private IPEndPoint realtimeEndpoint; //For real time data
+ private IPEndPoint latentEndpoint; //For processed/filtered data
+ private IPEndPoint rpcEndpoint; //For sending json RPC commands to SmartEye
+ private String TrackerName;
+ private String TrackerSerialNumber;
+
+ public SmartEyeCalibrationResult seCalibrationResult { get; private set; }
+
+ private byte[] recvBuffer;
+
+ private bool Listen;
+
+ public SmartEyeTracker()
+ {
+ TrackerName = "SmartEye Tracker";
+ TrackerSerialNumber = "Unknown"; //SE does not report a serial, make up some kind of hash?
+
+ //TODO catch parse exception?
+ IPAddress rpcAddress = IPAddress.Parse(Settings.Default.smarteye_ip_address);
+ rpcEndpoint = new IPEndPoint(rpcAddress, SMARTEYE_PORT_RPC);
+
+ try
+ {
+ //Try to connect to the RPC server on SmartEye host machine
+ Console.WriteLine("Attempting to connect to Smarteye...");
+ RpcClient = new System.Net.Sockets.TcpClient();
+ IAsyncResult conn = RpcClient.BeginConnect(rpcEndpoint.Address, rpcEndpoint.Port, null, null);
+
+ bool rpcConnectSuccess = conn.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(1));
+
+ if (!rpcConnectSuccess)
+ throw new Exception("Rpc connection timed out");
+
+ recvBuffer = new byte[RpcClient.ReceiveBufferSize];
+ System.Net.Sockets.NetworkStream recvStream = RpcClient.GetStream();
+
+ SendRpc(new SERPC("getState").GetNetstring());
+ ReceiveRpcResponse(); //Dummy
+
+ //Get actual tracker name
+
+ SendRpc(new SERPC("getProductName").GetNetstring());
+ JToken prod = ReceiveRpcResponse().GetValue("result");
+ TrackerName = prod.Value();
+
+ //Shouldn't do this here?
+
+ //Retrieve calibration from SE (stdDev and accuracy)
+ SendRpc(new SERPC("retrieveCalibrationResults").GetNetstring());
+ JToken cal = ReceiveRpcResponse().GetValue("result");
+
+ JToken stdDev = cal.SelectToken("stdDev");
+ double stdLeft = stdDev.Value(0).Value();
+ double stdRight = stdDev.Value(1).Value();
+
+ JToken accuracy = cal.SelectToken("accuracy");
+ double accLeft = accuracy.Value(0).Value();
+ double accRight = accuracy.Value(1).Value();
+
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("SmartEye Connection Failed, could not connect to RPC server" +
+ " on " + rpcEndpoint.ToString() + " : " + e);
+
+ RpcClient = null;
+ return;
+ }
+
+ IPAddress selfAddress = null;
+ try
+ {
+ IPHostEntry hostname = Dns.GetHostEntry(Dns.GetHostName());
+ //Search our IP addresses for one on the same network as the smarteye server
+
+ //TODO: test if this gives the same selfAddress
+ IPEndPoint iep = (IPEndPoint)RpcClient.Client.LocalEndPoint;
+ Console.WriteLine($"SmartEyeTracker Own (iTrace machine) Address is: {iep.Address}");
+ selfAddress = iep.Address;
+
+ if (selfAddress == null)
+ {
+ Console.WriteLine("Not connected to the same network as the specified SmartEye server!");
+ RealtimeClient = null;
+ return;
+ }
+
+ //Try pinging the SmartEye host computer
+ using (System.Net.NetworkInformation.Ping p = new System.Net.NetworkInformation.Ping())
+ {
+ System.Net.NetworkInformation.PingReply reply = p.Send(rpcAddress);
+ if (reply.Status != System.Net.NetworkInformation.IPStatus.Success)
+ throw new Exception("Provided SmartEye IP address is not responding.");
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("Problem connecting to SmartEye: " + e);
+ RealtimeClient = null;
+ return;
+ }
+
+ try
+ {
+ realtimeEndpoint = new IPEndPoint(selfAddress, Settings.Default.smarteye_ip_port);
+ RealtimeClient = new System.Net.Sockets.UdpClient(realtimeEndpoint);
+
+ //TODO: Configure SmartEye output settings here
+
+ } catch (Exception e)
+ {
+ Console.WriteLine("Couldn't open realtime data port (UDP) for SmartEye: " + e);
+ RealtimeClient = null;
+ return;
+ }
+
+ try
+ {
+ latentEndpoint = new IPEndPoint(IPAddress.Parse(Settings.Default.smarteye_ip_address), SMARTEYE_PORT_LATENT);
+ LatentClient = new System.Net.Sockets.TcpClient();
+ LatentClient.Connect(latentEndpoint);
+
+ //TODO: Configure SmartEye output settings here
+
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("Couldn't open filtered data port (TCP) for SmartEye: " + e);
+ LatentClient = null;
+ }
+
+ TrackerInit();
+ }
+
+ //Disconnect any connections, performed before refreshing the tracker list
+ public void CleanupConnections()
+ {
+ if (LatentClient.Connected)
+ LatentClient.Close();
+
+ if (RpcClient.Connected)
+ RpcClient.Close();
+
+ RealtimeClient.Close();
+ }
+
+ //Retrieve the World Model and calibration error vectors. Returns true on success
+ private Boolean GetWorldCalibration()
+ {
+ List targets = new List();
+ String WorldModelString;
+
+ try
+ {
+ //Retrieve WorldModel
+ SendRpc(new SERPC("getWorldModel").GetNetstring());
+ JToken result = ReceiveRpcResponse().GetValue("result");
+
+ //Escaped String representing the world model
+ WorldModelString = result.Value("worldModel");
+
+ //Retrieve calibration targets
+ int targetNumber = 0;
+ while (true)
+ {
+ //Retrieve a target
+ SendRpc(new SERPCGetTargetStats(targetNumber++).GetNetstring());
+ SETarget target = ReceiveRpcResponse().GetValue("result").ToObject();
+
+ //Ran out of targets
+ if (!target.TargetValid())
+ break;
+
+ targets.Add(target);
+ }
+
+ if (targets.Count == 0)
+ {
+ //No targets, most likely user has not done a gaze calibration in SmartEye
+ System.Windows.Forms.MessageBox.Show("SmartEye returned no calibration points. Perform a calibration from the SmartEye program before proceeding. \n\nMake sure that the correct World Model is selected before calibrating, or you will have to do it again!");
+ return false;
+ }
+
+ } catch (Exception e)
+ {
+ throw new Exception("Failed to retrieve SmartEye WorldModel and calibration points");
+ }
+
+ //Store world model string and calibration data
+ //TODO exception handling for invalid worldModelString
+ SEWorldModel worldModel = new SEWorldModel(WorldModelString);
+
+ seCalibrationResult = new SmartEyeCalibrationResult(worldModel, targets);
+
+ if (!seCalibrationResult.IsValid())
+ {
+ System.Windows.Forms.MessageBox.Show("The SmartEye World Model does not match the displays present on this computer. Make sure the correct world model is selected and perform another SmartEye calibration.");
+ }
+
+ SessionManager.GetInstance().SetCalibration(seCalibrationResult, this);
+
+ return true;
+ }
+
+ public JObject ReceiveRpcResponse()
+ {
+ System.Net.Sockets.NetworkStream recvStream = RpcClient.GetStream();
+ recvStream.Read(recvBuffer, 0, RpcClient.ReceiveBufferSize);
+ string response = Encoding.UTF8.GetString(recvBuffer);
+ response = NetstringUtils.TrimSENetstring(response.TrimEnd('\0'));
+ return JsonConvert.DeserializeObject(response);
+ }
+
+ //An Rpc connection and UDP (Realtime) socket are required to track
+ public bool TrackerFound()
+ {
+ return (RealtimeClient != null) && (RpcClient != null);
+ }
+
+ public String GetTrackerName()
+ {
+ return TrackerName;
+ }
+
+ public String GetTrackerSerialNumber()
+ {
+ return TrackerSerialNumber;
+ }
+
+ //Send RPC netstring using TCP
+ public void SendRpc(String netstring)
+ {
+ //Unsure if this econding is right
+ byte[] buf = Encoding.UTF8.GetBytes(netstring);
+ RpcClient.GetStream().Write(buf, 0, buf.Length);
+ }
+
+ public bool StartTracker()
+ {
+ //Before tracking starts the worldmodel must be valid and contain a set of calibration points
+ //Get world and calibration points
+ if (!GetWorldCalibration())
+ return false;
+
+ //Tell SmartEye to start tracking (same as clicking track eye button)
+ SendRpc(new SERPC("startTracking").GetNetstring());
+ ReceiveRpcResponse(); // Dummy receive to eat any error
+
+ //Used to stop listening thread loops
+ Listen = true;
+
+ //Realtime (UDP) thread
+ new System.Threading.Thread(() =>
+ {
+ System.Threading.Thread.CurrentThread.IsBackground = true;
+ ListenForData();
+ }).Start();
+
+ //Latent (TCP) thread
+ new System.Threading.Thread(() =>
+ {
+ System.Threading.Thread.CurrentThread.IsBackground = true;
+ ListenForLatent();
+ }).Start();
+
+ //Ok
+ Console.WriteLine("START SE TRACKING");
+ return true;
+ }
+
+ //Important to make sure the tracker is actually running before sending this, otherwise an error will be returned
+ public void StopTracker()
+ {
+ //Send stop command to SmartEye
+ if (Listen)
+ {
+ SendRpc(new SERPC("stopTracking").GetNetstring());
+ ReceiveRpcResponse(); // Dummy receive to eat any error
+ }
+
+ Listen = false;
+ }
+
+ public void EnterCalibration()
+ {
+ //Open calibration window (not working yet)
+ //SendRpc(new SERPC("calibrateGaze").GetNetstring());
+
+ //TODO: receive and store response
+ }
+
+ public void LeaveCalibration()
+ {
+
+ }
+
+ public void ShowEyeStatusWindow()
+ {
+
+ }
+
+ private void TrackerInit()
+ {
+
+ }
+
+ private void ListenForData()
+ {
+ while (Listen)
+ {
+ //Receive UDP packet
+ //TODO listen for termination
+
+ byte[] packet = RealtimeClient.Receive(ref realtimeEndpoint);
+
+ SmartEyeGazeData gaze = new SmartEyeGazeData(packet);
+
+ if (gaze.IsValid()) //Sending gaze data that isn't valid breaks the plugins?
+ {
+ //TODO: Adjust coordinates based on Screens
+ //Converts Screen space coords to one space as used by OS
+ //Needed in order for plugins to recognize multiple screens
+
+ bool hasIntersection = !String.IsNullOrWhiteSpace(gaze.intersectionName);
+ bool hasXY = (gaze.X != null) && (gaze.Y != null);
+
+ if (hasIntersection && hasXY)
+ {
+ Screen targetScreen = seCalibrationResult.screenMapping.GetSEToScreenMapping(gaze.intersectionName);
+
+ if (targetScreen != null)
+ gaze.Offset(targetScreen.Bounds.X, targetScreen.Bounds.Y);
+
+ GazeHandler.Instance.EnqueueGaze(gaze);
+ }
+ }
+ }
+ }
+
+ private void ListenForLatent()
+ {
+ while (Listen)
+ {
+ //byte[] packet = LatentClient.Receive(ref latentEndpoint);
+
+ //SmartEyeGazeData gaze = new SmartEyeGazeData(packet);
+
+ //TODO: store fixations and blinks somewhere
+ }
+ }
+ }
+}
diff --git a/itrace_core/SocketServer.cs b/itrace_core/SocketServer.cs
index 550c6ad..66ed72b 100644
--- a/itrace_core/SocketServer.cs
+++ b/itrace_core/SocketServer.cs
@@ -1,13 +1,27 @@
-using System;
+/********************************************************************************************************************************************************
+* @file SocketServer.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
+using System.Threading.Tasks;
using iTrace_Core.Properties;
using System.Windows;
+using System.IO;
+
namespace iTrace_Core
{
class SocketServer
@@ -16,6 +30,7 @@ class SocketServer
List clients;
BlockingCollection clientAcceptQueue;
Thread connectionsListener;
+ CancellationTokenSource cancellationTokenSource;
const string localhostAddress = "127.0.0.1";
const int defaultPort = 8008;
@@ -25,10 +40,11 @@ class SocketServer
public bool Started { get; private set; }
- public SocketServer()
+ private SocketServer()
{
try
{
+
clients = new List();
clientAcceptQueue = new BlockingCollection();
@@ -52,9 +68,8 @@ public SocketServer()
{
if (e.SocketErrorCode.Equals(SocketError.AddressAlreadyInUse))
{
- string content = "Another service is running on port " + port +
- ".\nStop that service or change the port for iTrace Core in settings and restart the Core.";
- string title = "Socket Server Cannot Start";
+ string content = Resources.AnotherServiceIsRunningOnPort + port + ".\n" + Resources.StopServiceOrChangePortThenRestart;
+ string title = Resources.SocketServerCannotStart;
MessageBox.Show(content, title, MessageBoxButton.OK, MessageBoxImage.Error);
}
@@ -62,6 +77,14 @@ public SocketServer()
}
}
+ static SocketServer instance;
+ public static SocketServer Instance()
+ {
+ if (instance == null)
+ instance = new SocketServer();
+ return instance;
+ }
+
public void SendSessionData()
{
SendToClients(SessionManager.GetInstance().Serialize());
@@ -81,6 +104,7 @@ private void ListenForConnections()
{
TcpClient client;
+ //For some reason this now doesn't stop when you close the main window, I think it did before. AL
while (true)
{
client = server.AcceptTcpClient();
@@ -105,11 +129,21 @@ private void AcceptQueuedClients()
}
}
- private void SendToClients(string message)
+ public void ReplayAcceptQueuedClients()
+ {
+ int clientQueueCount = clientAcceptQueue.Count;
+ for(int i = clientQueueCount; i != 0; --i)
+ {
+ TcpClient client = clientAcceptQueue.Take();
+ clients.Add(client);
+ }
+ }
+
+ public void SendToClients(string message)
{
+
if (!Started)
return;
-
byte[] messageInBytes = Encoding.ASCII.GetBytes(message);
for (int i = clients.Count - 1; i >= 0; i--)
@@ -120,10 +154,14 @@ private void SendToClients(string message)
}
catch (System.IO.IOException e)
{ //client was disconnected
+ Console.WriteLine("Lost client " + i.ToString() + " due to IO");
+ Console.WriteLine(e.Message);
clients.RemoveAt(i);
}
catch (InvalidOperationException e)
{
+ Console.WriteLine("Lost client " + i.ToString() + " due to InvOp");
+ Console.WriteLine(e.Message);
clients.RemoveAt(i);
}
}
@@ -152,5 +190,61 @@ private void ReceiveGazeData(object sender, GazeDataReceivedEventArgs e)
SendToClients(e.ReceivedGazeData.Serialize());
}
}
+
+ private async Task WaitForMessageFromClient(int clientIndex, CancellationToken cancellationToken, int timeout)
+ {
+ const int sleepLength = 10;
+ int slept = 0;
+ byte[] buffer = new byte[8];
+
+ try
+ {
+ NetworkStream networkStream = clients[clientIndex].GetStream();
+ //clients[clientIndex].Client.Poll(1000, SelectMode.SelectRead);
+
+ while (true)
+ {
+ if (cancellationToken.IsCancellationRequested || slept > timeout)
+ {
+ return;
+ }
+
+ if (networkStream.DataAvailable)
+ {
+ networkStream.Read(buffer, 0, buffer.Length);
+
+ return;
+ }
+
+ Thread.Sleep(sleepLength);
+ slept += sleepLength;
+ }
+ }
+ catch (System.IO.IOException) { } // Disconnect will be handled by SendToClients
+
+ return;
+ }
+
+ public void WaitUntilClientsAreReady(int timeoutMilliseconds)
+ {
+ int clientCount = clients.Count;
+ List tasks = new List();
+
+ for (int i = 0; i < clientCount; ++i)
+ {
+ var clientTask = WaitForMessageFromClient(i, cancellationTokenSource.Token, timeoutMilliseconds);
+ tasks.Add(clientTask);
+ }
+
+ Task.WhenAll(tasks).Wait();
+
+ cancellationTokenSource.Dispose();
+ cancellationTokenSource = new CancellationTokenSource();
+ }
+
+ public void CancelWait()
+ {
+ cancellationTokenSource.Cancel();
+ }
}
-}
+}
\ No newline at end of file
diff --git a/itrace_core/TobiiTracker.cs b/itrace_core/TobiiTracker.cs
index 09c730d..0aff130 100644
--- a/itrace_core/TobiiTracker.cs
+++ b/itrace_core/TobiiTracker.cs
@@ -1,4 +1,15 @@
-using System;
+/********************************************************************************************************************************************************
+* @file TobiiTracker.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System;
using System.ComponentModel;
namespace iTrace_Core
@@ -12,14 +23,25 @@ class TobiiTracker : ITracker
private EyeStatusWindow eyeStatusWindow;
private bool isEyeStatusOpen;
+ private string fallbackDeviceName;
+
public TobiiTracker(Tobii.Research.IEyeTracker foundDevice)
{
TrackingDevice = foundDevice;
isEyeStatusOpen = false;
+
+ fallbackDeviceName = $"{foundDevice.Model} ({foundDevice.SerialNumber})";
}
public String GetTrackerName()
{
+ //Temp fix for returning blank name
+ string deviceName = TrackingDevice.DeviceName;
+
+ //Use an alternate useful name
+ if (String.IsNullOrWhiteSpace(deviceName))
+ return fallbackDeviceName;
+
return TrackingDevice.DeviceName;
}
@@ -28,9 +50,10 @@ public String GetTrackerSerialNumber()
return TrackingDevice.SerialNumber;
}
- public void StartTracker()
+ public bool StartTracker()
{
TrackingDevice.GazeDataReceived += ReceiveRawGaze;
+ return true;
}
public void StopTracker()
diff --git a/itrace_core/TrackerManager.cs b/itrace_core/TrackerManager.cs
index d50c75a..e2602ec 100644
--- a/itrace_core/TrackerManager.cs
+++ b/itrace_core/TrackerManager.cs
@@ -1,4 +1,15 @@
-using System;
+/********************************************************************************************************************************************************
+* @file TrackerManager.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System;
using System.Collections.Generic;
namespace iTrace_Core
@@ -17,14 +28,35 @@ public TrackerManager()
public void FindTrackers()
{
+ //Ensure all trackers are cleaned up (Namely SmartEye must be fully disconnected)
+ foreach (ITracker tracker in EyeTrackers)
+ {
+ //Consider making this common to ITracker
+ if (tracker is SmartEyeTracker)
+ ((SmartEyeTracker)tracker).CleanupConnections();
+ }
+
EyeTrackers.Clear();
EyeTrackers.Add(new MouseTracker());
FindTobiiDevices();
FindGazePointDevice();
+ FindSmartEyeDevice();
+ }
+
+ private void FindSmartEyeDevice()
+ {
+ SmartEyeTracker seTracker = new SmartEyeTracker();
+ if (seTracker.TrackerFound())
+ {
+ EyeTrackers.Add(seTracker);
+ }
}
private void FindTobiiDevices()
{
+ //Sometimes the returned device will have a blank string DeviceName. I have no idea what causes this but it appears to be on Tobii's side, possible API problem.
+ //AL
+
Tobii.Research.EyeTrackerCollection eyeTrackers = Tobii.Research.EyeTrackingOperations.FindAllEyeTrackers();
foreach (Tobii.Research.IEyeTracker eyeTracker in eyeTrackers)
{
@@ -77,9 +109,11 @@ public Boolean Running()
public Boolean StartTracker()
{
- Tracking = true;
+
GazeHandler.Instance.StartHandler();
- ActiveTracker.StartTracker();
+ if (ActiveTracker.StartTracker())
+ Tracking = true;
+
return Tracking;
}
diff --git a/itrace_core/WebSocket.cs b/itrace_core/WebSocket.cs
index d1432c0..52e9785 100644
--- a/itrace_core/WebSocket.cs
+++ b/itrace_core/WebSocket.cs
@@ -1,4 +1,15 @@
-using System;
+/********************************************************************************************************************************************************
+* @file WebSocket.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
@@ -21,6 +32,17 @@ public WebSocket(TcpClient connection)
stream = client.GetStream();
}
+ public void WaitForConnection(string address, int port)
+ {
+ TcpListener server = new TcpListener(IPAddress.Parse(address), port);
+ server.Start();
+
+ client = server.AcceptTcpClient();
+ stream = client.GetStream();
+
+ server.Stop();
+ }
+
//Returns true if succeeded, false if failed.
public bool PerformHandshake(long timeout)
{
@@ -81,19 +103,56 @@ public void SendMessage(string message)
}
byte[] messageBytes = Encoding.UTF8.GetBytes(message);
- byte[] frame = new byte[2 + messageBytes.Length];
+ int websocketFrameOffset = 2 ;
+ if (messageBytes.Length > 125)
+ {
+ if (messageBytes.Length <= 65535)
+ {
+ websocketFrameOffset += 2;
+ }
+ else
+ {
+ websocketFrameOffset += 8;
+ }
+ }
+ byte[] frame = new byte[websocketFrameOffset + messageBytes.Length];
//Whole message sent in this frame, no extensions, Opcode type is text message
frame[0] = 129;
- //Length of message, with mask bit set to 0. Max message length is 128 characters
- frame[1] = Convert.ToByte(messageBytes.Length);
+ //Length of message, with mask bit set to 0.
+ // bottom 7 bits are length if length is less than 125
+ if (messageBytes.Length <= 125)
+ {
+ frame[1] = Convert.ToByte(messageBytes.Length);
+ }
+ // bottom 7 bits are 126, the next two bytes are the length
+ else if (messageBytes.Length <= 65535)
+ {
+ frame[1] = Convert.ToByte(126);
+ frame[2] = Convert.ToByte((messageBytes.Length & 0xff00) >> 8);
+ frame[3] = Convert.ToByte(messageBytes.Length & 0x00ff);
+ }
+ // bottom 7 bits are 127, the next 8 bytes are the length
+ else
+ {
+ frame[1] = Convert.ToByte(127);
+ frame[2] = Convert.ToByte(0);
+ frame[3] = Convert.ToByte(0);
+ frame[4] = Convert.ToByte(0);
+ frame[5] = Convert.ToByte(0);
+ frame[6] = Convert.ToByte((messageBytes.Length & 0xff000000) >> 24);
+ frame[7] = Convert.ToByte((messageBytes.Length & 0x00ff0000) >> 16);
+ frame[8] = Convert.ToByte((messageBytes.Length & 0x0000ff00) >> 8);
+ frame[9] = Convert.ToByte(messageBytes.Length & 0x000000ff);
+ }
+
//Masked message
for (int i = 0; i < messageBytes.Length; ++i)
{
- frame[2 + i] = Convert.ToByte(messageBytes[i]);
+ frame[websocketFrameOffset + i] = Convert.ToByte(messageBytes[i]);
}
try
diff --git a/itrace_core/WebSocketServer.cs b/itrace_core/WebSocketServer.cs
index f4162bd..cd4fe31 100644
--- a/itrace_core/WebSocketServer.cs
+++ b/itrace_core/WebSocketServer.cs
@@ -1,4 +1,15 @@
-using iTrace_Core.Properties;
+/********************************************************************************************************************************************************
+* @file WebSocketServer.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using iTrace_Core.Properties;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net;
@@ -20,7 +31,7 @@ class WebSocketServer
public const int MAX_WEBSOCKET_PORT_NUM = 65535;
int port;
- public WebSocketServer()
+ private WebSocketServer()
{
try
{
@@ -44,14 +55,21 @@ public WebSocketServer()
{
if (e.SocketErrorCode.Equals(SocketError.AddressAlreadyInUse))
{
- string content = "Another service is running on port " + port +
- ".\nStop that service or change the port for iTrace Core in settings and restart the Core.";
- string title = "Websocket Server Cannot Start";
+ string content = Resources.AnotherServiceIsRunningOnPort + port + '\n' + Resources.StopServiceOrChangePortThenRestart;
+ string title = Resources.WebSocketServerCannotStart;
MessageBox.Show(content, title, MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
+ static WebSocketServer instance;
+ public static WebSocketServer Instance()
+ {
+ if (instance == null)
+ instance = new WebSocketServer();
+ return instance;
+ }
+
void AcceptIncomingWebsocketConnections()
{
while (true)
@@ -73,7 +91,7 @@ void AcceptIncomingWebsocketConnections()
}
}
- void SendToClients(string message)
+ public void SendToClients(string message)
{
for (int i = clients.Count - 1; i >= 0; --i)
{
diff --git a/itrace_core/XMLGazeDataWriter.cs b/itrace_core/XMLGazeDataWriter.cs
index 16daa44..1ce8bdd 100644
--- a/itrace_core/XMLGazeDataWriter.cs
+++ b/itrace_core/XMLGazeDataWriter.cs
@@ -1,4 +1,15 @@
-using System.Text;
+/********************************************************************************************************************************************************
+* @file XMLGazeDataWriter.cs
+*
+* @Copyright (C) 2022 i-trace.org
+*
+* This file is part of iTrace Infrastructure http://www.i-trace.org/.
+* iTrace Infrastructure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+* iTrace Infrastructure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+* You should have received a copy of the GNU General Public License along with iTrace Infrastructure. If not, see .
+********************************************************************************************************************************************************/
+
+using System.Text;
using System.Threading;
using System.Xml;
@@ -94,6 +105,14 @@ private void WriteGaze(GazeData gazeData)
xmlTextWriter.WriteAttributeString("user_right_y", gazeData.UserRightY.ToString());
xmlTextWriter.WriteAttributeString("user_right_z", gazeData.UserRightZ.ToString());
+ //TODO: kinda bad, this method should actually call a method in GazeData which can be overidden for extra attributes
+ if (gazeData is SmartEyeGazeData)
+ {
+ SmartEyeGazeData segd = gazeData as SmartEyeGazeData;
+
+ xmlTextWriter.WriteAttributeString("intersection_name", segd.intersectionName);
+ }
+
xmlTextWriter.WriteEndElement();
}
diff --git a/itrace_core/itrace_core.csproj b/itrace_core/itrace_core.csproj
index 56ee582..0a1f250 100644
--- a/itrace_core/itrace_core.csproj
+++ b/itrace_core/itrace_core.csproj
@@ -1,238 +1,263 @@
-
-
-
-
- Debug
- AnyCPU
- {5783FB9E-5502-48CF-89BF-12B3A4DFFCB2}
- WinExe
- iTrace_Core
- iTrace-Core
- v4.6.1
- 512
- {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
- 4
- true
-
-
- false
- publish\
- true
- Disk
- false
- Foreground
- 7
- Days
- false
- false
- true
- 2
- 1.0.0.%2a
- false
- true
- true
-
-
- x64
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
-
-
- x64
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
-
-
- true
- bin\x64\Debug\
- DEBUG;TRACE
- full
- x64
- prompt
- MinimumRecommendedRules.ruleset
- true
-
-
- bin\x64\Release\
- TRACE
- true
- pdbonly
- x64
- prompt
- MinimumRecommendedRules.ruleset
- true
-
-
- icon.ico
-
-
-
- ..\packages\SharpAvi.2.1.1\lib\net45\SharpAvi.dll
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 4.0
-
-
- ..\packages\Tobii.Research.x64.1.6.1.987\lib\net45\Tobii.Research.dll
-
-
-
-
-
-
-
- MSBuild:Compile
- Designer
-
-
- EyeStatusWindow.xaml
-
-
-
- SessionSetupWindow.xaml
-
-
-
- Designer
- MSBuild:Compile
-
-
- Designer
- MSBuild:Compile
-
-
- MSBuild:Compile
- Designer
-
-
- App.xaml
- Code
-
-
- CalibrationWindow.xaml
-
-
-
-
-
-
- Resources.en.resx
- True
- True
-
-
- Resources.ru-RU.resx
- True
- True
-
-
- Form
-
-
-
-
-
-
-
-
-
-
-
-
- MainWindow.xaml
- Code
-
-
- Designer
- MSBuild:Compile
-
-
-
-
- Code
-
-
- True
- True
- Resources.resx
-
-
- True
- Settings.settings
- True
-
-
- PublicResXFileCodeGenerator
- Resources.en.Designer.cs
-
-
- PublicResXFileCodeGenerator
- Resources.ru-RU.Designer.cs
-
-
- PublicResXFileCodeGenerator
- Resources.Designer.cs
-
-
-
- SettingsSingleFileGenerator
- Settings.Designer.cs
-
-
-
-
-
-
-
-
-
-
-
-
-
- False
- Microsoft .NET Framework 4.6.1 %28x86 and x64%29
- true
-
-
- False
- .NET Framework 3.5 SP1
- false
-
-
-
-
- tobii_pro.dll
- PreserveNewest
-
-
-
-
-
-
- This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
-
+
+
+
+
+ Debug
+ AnyCPU
+ {5783FB9E-5502-48CF-89BF-12B3A4DFFCB2}
+ Exe
+ iTrace_Core
+ iTrace-Core
+ v4.6.1
+ 512
+ {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 4
+ true
+
+
+ false
+ publish\
+ true
+ Disk
+ false
+ Foreground
+ 7
+ Days
+ false
+ false
+ true
+ 2
+ 1.0.0.%2a
+ false
+ true
+ true
+
+
+ x64
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ x64
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+ true
+ bin\x64\Debug\
+ DEBUG;TRACE
+ full
+ x64
+ prompt
+ MinimumRecommendedRules.ruleset
+ true
+
+
+ bin\x64\Release\
+ TRACE
+ true
+ pdbonly
+ x64
+ prompt
+ MinimumRecommendedRules.ruleset
+ true
+
+
+ icon.ico
+
+
+
+
+
+
+ ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll
+
+
+ ..\packages\SharpAvi.2.1.1\lib\net45\SharpAvi.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 4.0
+
+
+ ..\packages\Tobii.Research.x64.1.6.1.987\lib\net45\Tobii.Research.dll
+
+
+
+
+
+
+
+
+ MSBuild:Compile
+ Designer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ EyeStatusWindow.xaml
+
+
+
+
+ SessionSetupWindow.xaml
+
+
+
+
+
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ MSBuild:Compile
+ Designer
+
+
+ App.xaml
+ Code
+
+
+ CalibrationWindow.xaml
+
+
+
+
+
+
+ Resources.en.resx
+ True
+ True
+
+
+ Resources.ru-RU.resx
+ True
+ True
+
+
+ Form
+
+
+
+
+
+
+
+
+
+
+
+ MainWindow.xaml
+ Code
+
+
+ Designer
+ MSBuild:Compile
+
+
+
+
+ Code
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ Settings.settings
+ True
+
+
+ PublicResXFileCodeGenerator
+ Resources.en.Designer.cs
+
+
+ PublicResXFileCodeGenerator
+ Resources.ru-RU.Designer.cs
+
+
+ PublicResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+
+
+
+ False
+ Microsoft .NET Framework 4.6.1 %28x86 and x64%29
+ true
+
+
+ False
+ .NET Framework 3.5 SP1
+ false
+
+
+
+
+ tobii_pro.dll
+ PreserveNewest
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
\ No newline at end of file
diff --git a/itrace_core/packages.config b/itrace_core/packages.config
index 992c0a5..abca7a7 100644
--- a/itrace_core/packages.config
+++ b/itrace_core/packages.config
@@ -1,5 +1,6 @@
+
\ No newline at end of file