I’m not sure how my post got hijacked into the other thread. Anyway, GetMouseWheel doesn’t work, it is just zero for the first call and then for the second and subsequent, it returns zero again after a few seconds. But the program is helpful anyway in showing how PsychHID(‘GetReport’) works. I will try this again and if I can’t get it to work I will write a Mex file to do it.
I have a wheel device I’d like to read with PsychHID, but I can’t figure out how to do it.
I have the device and element number of the wheel (see below). What I’d really like to do is to read the instantaneous state of the wheel, but PsychHID(‘RawState, 13, 8) doesn’t do anything. I’m not sure which parameters to use for PsychHID(‘GetReport’), but there are 5 buttons and a wheel on this device so I assume it would report 6 bytes….
If you print the rep variable in that code while playing with the wheel, you should probably see one of the bytes / elements change. One HID report is sent for each “wheel movement increment”, encoding the direction of the movement.
Maybe your wheel uses a different element in that report. Or maybe a maximum report size of 10 is not enough for that mouse?
I was not able to reliably read the reports fast enough (there is some delay using this functionality). However, the OS is interpreting the wheel movement as a mouse movement in the x-direction, so I was able to take advantage of that…
Kind of clunky, but at least I don’t have to write a Mex file….
% wheel test
clear all
allHidDevices=PsychHID(‘Devices’);
ndevices = size(allHidDevices, 2);
for i=1:ndevices
b(i)=allHidDevices(i).wheels;
end
wheel_index = find(b);
full_revolution = 150; % distance wheel moves for one revolution (pixels)
r = 500; % radius of orbit (pixels)
s=50; % radius of circle (pixels)
theta = 0;
time0 = GetSecs;
abort_time = 10; % stop after this many seconds
HideCursor;
[w,rect]=Screen(‘OpenWindow’,0, 0);
xc = rect(3)/2;
yc = rect(4)/2;
SetMouse(xc, yc, w, wheel_index);
while(GetSecs <= time0 + abort_time)
[x y b] = GetMouse(w, wheel_index); % new position
You mean you got it to work with the PsychHID GetReport method, just not reliably to catch all wheel movements? This is on macOS, right?
That is very weird and unusual, that the OS would do that by default. Is this some special mouse?
There’s always Linux as an option, and MouseMotionRecordingDemo.m (Linux and Windows only) is a good demo to show how to record mouse motion trajectories with bells and whistles and wheel movements.
Okay, now the only problem is that I have two wheels, and they both act like the same mouse in OsX because the mouseDev parameter is ignored. Will use this on Linux for now until this is changed or I figure out how to read the PsychHID reports properly.
So on Linux, the behavior is somewhat different. I plug each of the two wheels into a separate USB port, and now one wheel is read as a mouse moving in the X-direction, and the other wheel is read as a mouse moving in the Y-direction. And neither of them shows up as a “wheel” in PsychHID(‘devices’).wheels.
Using PsychHID(‘GetReport’) on OSX worked “somewhat”. I would read all of the reports I could during each video frame until it was time to flip, and resuming reading them after the flip. But there was a delay such that the delta position I calculated was not accurate for the frame but would lag some number of frames behind. For example, if I turned the wheel a lot, it might take a second or two for the position to catch up.
I then tried Psych(‘GiveMeReports’) to try to just read all of the available reports each frame, but then for some reason the whole thing stopped working (no reports). I’m not sure what happened, but this is when I gave up and switched the to GetMouse solution.
The ‘wheel’ property and various others are only reported on macOS atm. The way HID devices are handled and exposed to applications is substantially different between the three operating systems, that’s why the approaches of dealing with them internally is quite different and platform differences can’t be completely hidden. You see lots of special case handling code in functions like GetMouseIndices / GetKeyboardIndices / … to deal with OS differences, just as in GetMouseWheel() and such.
E.g., the properties “wheels, sliders, dials, hats” are not implemented on Linux and Windows atm. and always return zero.
Another difference is how standard HID input devices like mouse and keyboard are dealt with different OS’es, mostly for security reasons. On Windows you can’t differentiate/select between different mice and keyboards and don’t get any PsychHID low-level access to them. On macOS you can differentiate for keyboards, but not for mice, at least when it comes to mouse x,y position
But now I get it! Your wheel device is not actually a computer mouse with mouse wheel at all, but some special purpose wheel device that just pretends/fakes/lies to the operating system about being a mouse wheel! So normal rules about handling mouse wheels do not apply, and GetMouseWheel() was not meant to handle a non-mouse wheel.
It’s this thing:
Of course it needs some special snowflake treatment.
Apparently it doesn’t report wheel movement to the OS in the normal way, but instead turns it into fake mouse movement along the x or y axis, and one can configure if the wheel should drive the x or y axis, according to that web site.
On Linux, MouseMotionRecordingDemo.m also might be useful for handling such a weird fake-mouse, e.g., to retrieve relative mouse motion and integrate wheel position from accumulating that, and avoid unwanted effects/influence of “ballistic pointer acceleration”.
There are also ways to decouple a “mouse device” from actual mouse cursor motion on the screen, via the xinput terminal command or a custom xorg configuration file, so it no longer drives the mouse cursor.
For finding the right “mouse device” you’d use GetMouseIndices(‘slavePointer’) and the other GetMouseIndices parameters like productName etc. to select your wheel thingy.
Hmm. A closer example for low-level access apart from the mouse method would be our support for the Griffin PowerMate USB turn-knob, spinning dial solution that is tested with Psychtoolbox on all operating systems. See PsychPowerMate.m as sample. Should work with multiple devices, but was only ever tested by myself with the one turn knob I have, so don’t know if somebody else tested with multiple devices.
It does use a special way of GetMouse and Keyboard queues on Linux for some extra efficiency and some goodies like knob movement history recording. On macOS / Windows it does use PsychHID('ReceiveReports') ‘GiveMeReports’ etc. to support the basics in a less efficient fashion.
Another way to use the “reports wheel motion as mouse x-axis movement” behaviour on OSX without the hacks in your script is to:
Detach the “mouse” from the mouse cursor, so the mouse cursor is not moved by mice anymore, but only manually via SetMouse, e.g.: SetMouse(0,0,[],[],1); KbStrokeWait ; SetMouse(0,0)
Might not be needed if you HideCursor() the cursor anyway though.
Look at the first two entries returned in the ‘valuator’ array returned as 5th optional return value of GetMouse(). These report relative mouse movement on macOS, so a less clunky way of getting relative mouse motion == relative wheel motion.
More elegant. One problem that may always be present by simple use of GetMouse() is that reported mouse movements are subject to ballistic pointer acceleration: If the user moves the mouse at constant speed over a longer distance, the mouse will be reported to “speed up” to make large cursor movements onscreen possible with small mouse movements on table. Or potential scaling depending on the setting on Retina displays on macOS.
Great, thanks for all of the explanations and suggestions. I’m going to try these various options out when I get the time (although my crude implementation does work at the moment).