I had a proper play with this tonight, it's very cool.
Rather than do dynamically compiled C#, I am more interested at the moment in calling pre-compiled C# DLLs from AHK.
Attached is a C# solution with a DLL project that implements SharpDX to read joystick information via DirectInput
This will allow us to read the full 8 axes from sticks, rather than the 6 that AHK currently supports.
Demo AHK code that uses CLR to interact with the DLL:
To run, download the ZIP and extract
JoystickWrapper\bin\Debug\*.dll to the same folder as the script
Code: Select all
#SingleInstance force
#include CLR.ahk
polled_guids := 0
device_list := {}
asm := CLR_LoadLibrary("JoystickWrapper.dll")
jw := CLR_CreateObject(asm, "JWNameSpace.JoystickWrapper")
_device_list := jw.GetDevices()
Gui, Add, ListView, w300 h100 Checked hwndhDeviceSelect gDeviceSelected Altsubmit, Name|Guid
ct := _device_list.Length()
Loop % ct {
dev := _device_list.GetIndex(A_Index - 1)
LV_Add(, dev.Name, dev.Guid)
device_list[dev.Guid] := dev.Name
}
LV_ModifyCol(1, 200)
Gui, Add, ListView, w300 h300 hwndhDeviceReports, Device|Input|Value
LV_ModifyCol(1, 100)
LV_ModifyCol(2, 150)
Gui, Show
Loop {
if (IsObject(polled_guids)){
Gui, ListView, % hDeviceReports
for polled_guid, unused in polled_guids{
state := jw.PollStick(polled_guid)
rpt_ct := state.Length()
Loop % rpt_ct {
rpt := state.GetIndex(A_Index-1)
row := LV_Add(,device_list[polled_guid], rpt.InputName, rpt.Value)
LV_Modify(row, "vis")
inp := rpt.InputName
}
}
}
Sleep 10
}
return
DeviceSelected:
if (A_GuiEvent != "I")
return
Gui, ListView, % hDeviceSelect
if (ErrorLevel = "c"){
; Check / Uncheck
state := ErrorLevel == "C" ? 1 : 0
LV_GetText(guid, A_EventInfo, 2)
if (state){
if (!IsObject(polled_guids))
polled_guids := {}
polled_guids[guid] := 1
} else {
if (ObjHasKey(polled_guids, guid)){
polled_guids.Delete(guid)
if (IsEmptyAssoc(polled_guids))
polled_guids := 0
}
}
}
return
IsEmptyAssoc(arr){
for k, v in arr
return 0
return 1
}
GuiClose:
ExitApp
Demo video showing input from 2 physical sticks, plus input coming from vJoy (which is being driven by UCR) so I can show it reporting Axis 8, button ID 128 and POV 4.
The C# code (Project included in attached ZIP):
Code: Select all
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SharpDX.DirectInput;
using System.Diagnostics;
namespace JWNameSpace
{
public class JoystickWrapper
{
private DirectInput directInput;
private Dictionary<string, Joystick> acquiredSticks = new Dictionary<string, Joystick>(StringComparer.OrdinalIgnoreCase);
public JoystickWrapper()
{
directInput = new DirectInput();
}
public int AcquireStick(string guid)
{
var joystick = new Joystick(directInput, new Guid(guid));
// Set BufferSize in order to use buffered data.
joystick.Properties.BufferSize = 128;
joystick.Acquire();
acquiredSticks[guid] = joystick;
return 1;
}
public DeviceList GetDevices()
{
var deviceList = new DeviceList();
foreach (var deviceInstance in directInput.GetDevices())
{
if (deviceInstance.Type != DeviceType.Joystick
&& deviceInstance.Type != DeviceType.Gamepad
&& deviceInstance.Type != DeviceType.FirstPerson
&& deviceInstance.Type != DeviceType.Flight
&& deviceInstance.Type != DeviceType.Driving)
continue;
deviceList.Add(new DeviceInfo(deviceInstance));
//Console.WriteLine(String.Format("Found stick {0}", deviceInstance.InstanceName));
}
return deviceList;
}
public DeviceReportList PollStick(string joystickGuid)
{
if (!acquiredSticks.ContainsKey(joystickGuid))
{
AcquireStick(joystickGuid);
}
var joystick = acquiredSticks[joystickGuid];
joystick.Poll();
var datas = joystick.GetBufferedData();
var rep = new DeviceReportList() { Guid = joystickGuid };
foreach (var state in datas)
{
rep.Add(new DeviceReport() { InputName = state.Offset.ToString(), Value = state.Value});
//Debug.WriteLine(String.Format("AHK| Input {0} changed state to {1}", state.Offset.ToString(), state.Value));
}
return rep;
}
public void ConsolePollStick(string joystickGuid)
{
if (!acquiredSticks.ContainsKey(joystickGuid))
{
AcquireStick(joystickGuid);
}
var joystick = acquiredSticks[joystickGuid];
while (true)
{
joystick.Poll();
var datas = joystick.GetBufferedData();
foreach (var state in datas)
Console.WriteLine(state);
}
}
}
public class DeviceList
{
public List<DeviceInfo> List { get; set; } = new List<DeviceInfo>();
public void Add(DeviceInfo obj)
{
List.Add(obj);
}
public DeviceInfo GetIndex(int id)
{
return List[id];
}
public int Length()
{
return List.Count;
}
}
public class DeviceInfo
{
public DeviceInfo(DeviceInstance deviceInstance)
{
Name = deviceInstance.InstanceName;
Guid = deviceInstance.InstanceGuid.ToString();
}
public string Name { get; set; }
public string Guid { get; set; }
}
public class DeviceReportList
{
public List<DeviceReport> List { get; set; } = new List<DeviceReport>();
public string Guid { get; set; }
public void Add(DeviceReport obj)
{
List.Add(obj);
}
public DeviceReport GetIndex(int id)
{
return List[id];
}
public int Length()
{
return List.Count;
}
}
public class DeviceReport
{
public string InputName { get; set; }
public int Value { get; set; }
}
public class ReturnedObject
{
public int BaseInt { get; set; }
}
}
Some questions:
I seem to be able to return an array[], but do not seem to be able to get by index (using
[<index>])) or get the length, so I had to wrap it in a class and provide my own methods to do this.
I take it there is a better way to do this?
At some point I would also like to have the C# code callback to the AHK code (ie an ahk func gets called every time the joystick changes) - I take it I just need to work through the provided Events example to achieve this?