You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
232 lines
11 KiB
232 lines
11 KiB
using System ;
|
|
using System.IO ;
|
|
using System.Xml ;
|
|
using System.Runtime.InteropServices ;
|
|
using System.Collections.Generic ;
|
|
|
|
namespace MouseInterception
|
|
{
|
|
class AppConfig
|
|
{
|
|
|
|
// IMPORTANT! The definitions here must be kept in sync with their C equivalents in api.hpp
|
|
// FUDGE: Marshaling multiple levels of nested arrays of struct's proved to be tricky. C# seems to be
|
|
// able to handle it, but unpacking it on the C++ side was fiddly. Since we can't simply get
|
|
// a plain old pointer to an array (since they're managed objects), and we have to marshal arrays ByVal,
|
|
// defining C-struct's was impossible (one level of nesting was OK, but no deeper). We could have maybe
|
|
// done some trickery by manipulating pointers and using sizeof(), but it's risky since there are
|
|
// alignment issues. It's safer to send through a separate array for each type of struct, and then
|
|
// a start index & item count specifying which ones are required. Sigh...
|
|
|
|
public enum KeyModifiers { ctrl=0x0001 , alt=0x0002 , shift=0x0004 , caps=0x0008 }
|
|
|
|
// --- SETTINGS: application settings ---
|
|
[StructLayout( LayoutKind.Sequential , CharSet=CharSet.Unicode , Pack=1 )]
|
|
public struct ApiSettings
|
|
{
|
|
}
|
|
private ApiSettings mSettings ;
|
|
public ApiSettings settings { get { return mSettings ; } }
|
|
|
|
// --- DEVICE: device attributes ---
|
|
[StructLayout( LayoutKind.Sequential , CharSet=CharSet.Unicode , Pack=1 )]
|
|
public struct ApiDevice
|
|
{
|
|
public int mDeviceId ;
|
|
public string mHID ;
|
|
public int mDeviceNumber ;
|
|
public string mDisplayName ;
|
|
public bool mIsEnabled ;
|
|
}
|
|
private ApiDevice[] mDevices ;
|
|
public ApiDevice[] devices { get { return mDevices ; } }
|
|
|
|
// --- DEVICE CONFIG: device configuration ---
|
|
[StructLayout( LayoutKind.Sequential , CharSet=CharSet.Unicode , Pack=1 )]
|
|
public struct ApiDeviceConfig
|
|
{
|
|
public int mDeviceId ;
|
|
public int mStrokeHistoryResetInterval ;
|
|
public int mAppProfileStartIndex ;
|
|
public int mAppProfileCount ;
|
|
}
|
|
private ApiDeviceConfig[] mDeviceConfigs ;
|
|
public ApiDeviceConfig[] deviceConfigs { get { return mDeviceConfigs ; } }
|
|
|
|
// --- APP PROFILE: device configuration for an application ---
|
|
[StructLayout( LayoutKind.Sequential , CharSet=CharSet.Unicode , Pack=1 )]
|
|
public struct ApiAppProfile
|
|
{
|
|
public string mApp ;
|
|
public int mSensitivityX ;
|
|
public int mSensitivityY ;
|
|
public bool mFallbackToDefaultAppProfile ;
|
|
public int mEventStartIndex ;
|
|
public int mEventCount ;
|
|
}
|
|
private ApiAppProfile[] mAppProfiles ;
|
|
public ApiAppProfile[] appProfiles { get { return mAppProfiles ; } }
|
|
|
|
// --- API EVENT: device event ---
|
|
[StructLayout( LayoutKind.Sequential , CharSet=CharSet.Unicode , Pack=1 )]
|
|
public struct ApiEvent
|
|
{
|
|
public enum EventType {
|
|
mouseLeft=1 , mouseRight=2 , mouseUp=3 , mouseDown=4 ,
|
|
wheelLeft=5 , wheelRight=6 , wheelUp=7 , wheelDown=8
|
|
}
|
|
public int mEventType ;
|
|
public int mKeyModifiers ;
|
|
public int mActionStartIndex ;
|
|
public int mActionCount ;
|
|
}
|
|
private ApiEvent[] mEvents ;
|
|
public ApiEvent[] events { get { return mEvents ; } }
|
|
|
|
// --- API ACTION: action to perform in response to a device event ---
|
|
[StructLayout( LayoutKind.Sequential , CharSet=CharSet.Unicode , Pack=1 )]
|
|
public struct ApiAction
|
|
{
|
|
public enum ActionType {
|
|
mouseLeft=1 , mouseRight=2 , mouseUp=3 , mouseDown=4 ,
|
|
wheelLeft=5 , wheelRight=6 , wheelUp=7 , wheelDown=8 ,
|
|
keyPress=10
|
|
}
|
|
public int mActionType ;
|
|
public int mActionParam ;
|
|
public int mKeyModifiers ;
|
|
}
|
|
public ApiAction[] mActions ;
|
|
public ApiAction[] actions { get { return mActions ; } }
|
|
|
|
// ------------------------------------------------------------
|
|
|
|
public AppConfig( string fname )
|
|
{
|
|
// initialize
|
|
HashSet<int> deviceIds = new HashSet<int>() ;
|
|
HashSet<int> deviceNumbers = new HashSet<int>() ;
|
|
mSettings = new ApiSettings() ;
|
|
mDevices = new ApiDevice[ 0 ] ;
|
|
mDeviceConfigs = new ApiDeviceConfig[ 0 ] ;
|
|
mAppProfiles = new ApiAppProfile[ 0 ] ;
|
|
mEvents = new ApiEvent[ 0 ] ;
|
|
mActions = new ApiAction[ 0 ] ;
|
|
|
|
// load the AppConfig
|
|
if ( ! File.Exists( fname ) )
|
|
return ; // FIXME! can't do this - we have to notify the DLL
|
|
XmlReaderSettings xmlReaderSettings = new XmlReaderSettings() ;
|
|
xmlReaderSettings.IgnoreComments = true ;
|
|
xmlReaderSettings.IgnoreProcessingInstructions = true ;
|
|
xmlReaderSettings.IgnoreWhitespace = true ;
|
|
XmlDocument xmlDoc = new XmlDocument() ;
|
|
using ( XmlReader xmlReader = XmlReader.Create( fname , xmlReaderSettings ) )
|
|
xmlDoc.Load( xmlReader ) ;
|
|
|
|
// parse the app config
|
|
XmlNode configXmlNode = xmlDoc.SelectSingleNode( "/config" ) ;
|
|
|
|
// parse the devices
|
|
List<ApiDevice> devices = new List<ApiDevice>() ;
|
|
foreach( XmlNode xn in configXmlNode.SelectNodes("device") )
|
|
{
|
|
ApiDevice device = new ApiDevice() ;
|
|
device.mDeviceId = Int32.Parse( xn.Attributes["id"].Value ) ;
|
|
if ( deviceIds.Contains( device.mDeviceId ) )
|
|
throw new Exception( String.Format( "Duplicate device ID: {0}" , device.mDeviceId ) ) ;
|
|
deviceIds.Add( device.mDeviceId ) ;
|
|
device.mHID = xn.SelectSingleNode("hid").InnerText.Trim() ;
|
|
device.mDeviceNumber = Int32.Parse( xn.SelectSingleNode("deviceNumber").InnerText ) ;
|
|
if ( deviceNumbers.Contains( device.mDeviceNumber ) )
|
|
throw new Exception( String.Format( "Duplicate device number: {0}" , device.mDeviceNumber ) ) ;
|
|
deviceNumbers.Add( device.mDeviceNumber ) ;
|
|
if ( deviceNumbers.Contains ( device.mDeviceNumber ) )
|
|
device.mDisplayName = Utils.getXmlChildVal( xn , "displayName" ) ;
|
|
device.mIsEnabled = Utils.getXmlChildVal( xn , "enabled" , false ) ;
|
|
devices.Add( device ) ;
|
|
}
|
|
mDevices = devices.ToArray() ;
|
|
|
|
// parse the device configs
|
|
List<ApiDeviceConfig> deviceConfigs = new List<ApiDeviceConfig>() ;
|
|
List<ApiAppProfile> appProfiles = new List<ApiAppProfile>() ;
|
|
List<ApiEvent> events = new List<ApiEvent>() ;
|
|
List<ApiAction> actions = new List<ApiAction>() ;
|
|
foreach( XmlNode deviceConfigXmlNode in configXmlNode.SelectNodes("deviceConfig") )
|
|
{
|
|
ApiDeviceConfig deviceConfig = new ApiDeviceConfig() ;
|
|
deviceConfig.mDeviceId = Int32.Parse( deviceConfigXmlNode.Attributes["id"].Value ) ;
|
|
deviceConfig.mStrokeHistoryResetInterval = Utils.getXmlChildVal( deviceConfigXmlNode , "strokeHistoryResetInterval" , -1 ) ;
|
|
deviceConfig.mAppProfileStartIndex = appProfiles.Count ;
|
|
deviceConfig.mAppProfileCount = 0 ;
|
|
// parse the app profiles
|
|
foreach( XmlNode appProfileXmlNode in deviceConfigXmlNode.SelectNodes("appProfile") )
|
|
{
|
|
ApiAppProfile appProfile = new ApiAppProfile() ;
|
|
appProfile.mApp = Utils.getXmlAttr( appProfileXmlNode , "app" , "" ) ;
|
|
XmlNode xn = appProfileXmlNode.SelectSingleNode( "sensitivity" ) ;
|
|
if ( xn != null )
|
|
{
|
|
appProfile.mSensitivityX = Utils.getXmlAttr( xn , "x" , 0 ) ;
|
|
appProfile.mSensitivityY = Utils.getXmlAttr( xn , "y" , 0 ) ;
|
|
}
|
|
appProfile.mFallbackToDefaultAppProfile = Utils.getXmlChildVal( appProfileXmlNode , "fallbackToDefaultAppProfile" , false ) ;
|
|
appProfile.mEventStartIndex = events.Count ;
|
|
appProfile.mEventCount = 0 ;
|
|
// parse the events
|
|
foreach( XmlNode eventXmlNode in appProfileXmlNode.SelectNodes("event" ) )
|
|
{
|
|
ApiEvent evt = new ApiEvent() ;
|
|
evt.mEventType = (int) Enum.Parse( typeof(ApiEvent.EventType) , eventXmlNode.Attributes["type"].Value , true ) ;
|
|
evt.mKeyModifiers = getKeyModifiers( eventXmlNode ) ;
|
|
// parse the actions
|
|
evt.mActionStartIndex = actions.Count ;
|
|
evt.mActionCount = 0 ;
|
|
foreach( XmlNode actionXmlNode in eventXmlNode.SelectNodes("action") )
|
|
{
|
|
ApiAction action = new ApiAction() ;
|
|
action.mActionType = (int) Enum.Parse( typeof(ApiAction.ActionType) , actionXmlNode.Attributes["type"].Value , true ) ;
|
|
if ( action.mActionType == (int)ApiAction.ActionType.keyPress )
|
|
{
|
|
action.mActionParam = Utils.getXmlAttr( actionXmlNode , "vKey" , 0 ) ;
|
|
if ( action.mActionParam <= 0 )
|
|
throw new Exception( "Missing vKey." ) ;
|
|
}
|
|
else
|
|
action.mActionParam = Utils.getXmlAttr( actionXmlNode , "speed" , 0 ) ;
|
|
action.mKeyModifiers = getKeyModifiers( actionXmlNode ) ;
|
|
actions.Add( action ) ;
|
|
evt.mActionCount ++ ;
|
|
}
|
|
events.Add( evt ) ;
|
|
appProfile.mEventCount ++ ;
|
|
}
|
|
// add the app profile to the table
|
|
appProfiles.Add( appProfile ) ;
|
|
deviceConfig.mAppProfileCount ++ ;
|
|
}
|
|
// add the device config to the table
|
|
deviceConfigs.Add( deviceConfig ) ;
|
|
}
|
|
mDeviceConfigs = deviceConfigs.ToArray() ;
|
|
mAppProfiles = appProfiles.ToArray() ;
|
|
mEvents = events.ToArray() ;
|
|
mActions = actions.ToArray() ;
|
|
}
|
|
|
|
private static int getKeyModifiers( XmlNode xmlNode )
|
|
{
|
|
// get the key modifiers
|
|
int keyModifierFlags = 0 ;
|
|
string keyModifiers = Utils.getXmlAttr( xmlNode , "keyModifiers" , "" ) ;
|
|
if ( keyModifiers.Length > 0 )
|
|
{
|
|
foreach( string km in keyModifiers.Split('+') )
|
|
keyModifierFlags += (int) Enum.Parse( typeof(KeyModifiers) , km.ToLower() , true ) ;
|
|
}
|
|
return keyModifierFlags ;
|
|
}
|
|
|
|
}
|
|
}
|
|
|