AndersMalmgren / FreePIE

Programmable Input Emulator
650 stars 144 forks source link

Iteration over keys of joystick[] dict / seq? #111

Closed PWBENNETT closed 7 years ago

PWBENNETT commented 7 years ago

I'm trying to find out what FreePIE calls my joysticks, with something like

def update():
  for j in joystick.keys():
    diagnostics.watch(j)

However, the error message "... object has no attribute 'keys'" is utterly opaque (to me). Any hints or tips on how I can proceed? The API documentation seems to be a bit slim on this subject.

MarijnS95 commented 7 years ago

joystick isn't really a collection or array, it's just an object that happens to have an indexer (which allows you to use [] to get an item from it). When you use it to get an object by name or by index, it simply calls a function that gets the i'th item from an internal list, or searches that list for the given name. The names and/or collection length are not publicly exposed, even if you get a joystick object (there's no joystick[i].name() or equivalent). The error is actually a general python error, telling you that "keys" does not exist on the object (neither as function or other type). For implementation details, the joystick object is a globalindexer, which is backed by these 2 functions to read from the list.

In other words, retrieving the names is not possible unless you add such a function and recompile FreePIE, or through reflection.

PWBENNETT commented 7 years ago

Thanks. I'll start RTFMing on IronPython, then. In the meantime, I suppose I can loop over the integers from 0 thru number_of_joysticks_plugged_in - 1 and at least learn which one has which number.

Do you have an alternative method for finding out which name FreePIE uses for which device? Windows 10 64-bit. Thanks.

PWBENNETT commented 7 years ago

At first glance, it looks like the joystick object includes my vJoy devices (joystick[0].x matches what I'm (elsewhere) setting vJoy[0].x to). Is this a bug or a feature?

PWBENNETT commented 7 years ago

Never mind. It looks like the USB HID device I want to reprogram (the thumbstick on my Logitech G13) does not show up in the joystick[] object. I think I'm going to have to learn how to write FreePIE plugins, and then write one for the G13.

PWBENNETT commented 7 years ago

How do I do "joystick[n].update += update"? That's another case of "... object has no attribute 'update'"

Basically, I want update() to execute every time a given joystick moves, in just the same way I can midi[1].update += update. Well, it doesn't have to be "in exactly the same way". Any way that works is fine by me.

PWBENNETT commented 7 years ago

I feel like I'm tantalizingly close with ...

import clr
from pprint import pformat

def update():
  clr_type = clr.GetClrType(type(joystick))
  all_fields = clr_type.GetMembers()
  field_names = [ m.ToString() for m in all_fields if m.Name == 'Item' ]
  watchme = pformat(field_names)
  diagnostics.watch(watchme)

... but I don't quite know enough IronPython or .NET to (quickly) get over the next hump. I'll keep plugging away at it. It's a great learning experience.

MarijnS95 commented 7 years ago

At first glance, it looks like the joystick object includes my vJoy devices (joystick[0].x matches what I'm (elsewhere) setting vJoy[0].x to). Is this a bug or a feature?

This is part of how vJoy and DirectInput work - vJoy simulates a DirectInput device with a driver that's installed, hence why it shows up when enumerating DirectInput devices (otherwise it won't work in games either). There's no feature built into FreePIE to remove all vJoy devices from the list, but that's a nice feature to add since it's quite hard to get the device name.

Never mind. It looks like the USB HID device I want to reprogram (the thumbstick on my Logitech G13) does not show up in the joystick[] object. I think I'm going to have to learn how to write FreePIE plugins, and then write one for the G13.

I don't have a G13 so I can't comment with authority - it seems you should be able to set the thumbstick to act like a joystick from the Logitech Gaming Software, perhaps that works. You can access it through joystick[] as soon as it shows up in the "Set up USB game controllers" panel.

It's a good idea to pull the FreePIE source code and run your own build, makes it a lot easier to see the internals and make your own changes. It's not necessary for plugin development though.

I feel like I'm tantalizingly close with ...

Not so fast :wink:, it's a little more complicated to get the device name. Like I said, joystick is just an object with a function to iterate over a collection, it doesn't contain this collection directly (JoystickPlugin has it). As such, I find it easier to extract the private device field from a JoystickGlobal object (which is returned by calling joystick[i]). Then, get the private SlimDX.DirectInput.Joystick field joystick, from where the device name can be accessed (it's both in the Properties as in the Information field).

import clr
import System
from System.Reflection import *

if starting:
    num_devices = 1
    devs = [joystick[i] for i in range(num_devices)]
    joystick_global = clr.GetClrType(type(devs[0]))
    device = joystick_global.GetField("device", BindingFlags.NonPublic | BindingFlags.Instance)
    di_joystick = device.FieldType.GetField("joystick", BindingFlags.NonPublic | BindingFlags.Instance)
    for dev in devs:
        device_val = device.GetValue(dev)
        joystick_val = di_joystick.GetValue(device_val)
        diagnostics.debug("dev: {0}", joystick_val.Properties.InstanceName)
PWBENNETT commented 7 years ago

Aha... I think I kinda sorta understand what you're doing there. Enough of it for now. Thank you so very much.

MarijnS95 commented 7 years ago

Aha... I think I kinda sorta understand what you're doing there. Enough of it for now. Thank you so very much.

You're welcome. It's a little complicated so I recommend you to look at the source code, which is where I got the class/field/type structure from.

PWBENNETT commented 7 years ago

I couldn't work out where diagnostics.debug() writes to (it doesn't seem to be the Console tab). I ended up using diagnostics.watch() and some hard-coded numbers to extract the joystick names, and it worked a treat. Thanks again!

PWBENNETT commented 7 years ago

https://gist.github.com/PWBENNETT/0c178601cf6e614e14b7593ef78ff230 is the whole shebang, as it stands right now.