PsychHID('KbCheck') starts event queue under Windows 10

Dear Mario and others,

I found that KbCheck(-1) implicitly starts KbQueue under Windows:

clear all % prepare for the test, **line by line**
KbEventAvail % returns 0 as expected, since no queue created or started
KbCheck(-1); % check all keyboards
KbEventAvail % now it returns 5, indicating queue started somehow

clear all % try again
KbCheck; % no input, but it is still for all keyboards under Windows
KbEventAvail % return 0 as expected

Is this behavior expected, or it is some kind of side effect? Any workaround?

A quick test under OSX and Linux 18.04 did not show the issue, KbEventAvail returning 0 always.

Let me know if you need more information. Thanks.

-Xiangrui

=========================
EDIT:
Digging into KbCheck.m, under Windows, KbCheck(-1) calls PsychHID('KbCheck')
while KbCheck without input calls Screen('GetMouseHelper'). So it makes sense that the latter won’t affect KbEventAvail.
So the symptom is that PsychHID('KbCheck') starts queue, and fills event buffer. I changed the subject to reflect this.

Weird. There isn’t anything in PsychHID(‘KbCheck’) that would interact with keyboard queues. I wonder if something else triggers this. Can you send a minimal script that reliably reproduces this effect when run after quitting and restarting Octave or Matlab?

Note this happens only under windows, for both Matlab and Octave. As shown by running following line by line:

clear all % prepare for the test
KbEventAvail % returns 0 as expected, since no queue created or started
KbEventAvail % again, it still gives 0. Good
[down, secs, keyCode]= PsychHID('KbCheck', 0); % check all keyboards for Windows
KbEventAvail % now it returns non-zero, like 5, indicating queue started somehow
KbEventAvail % try again, the nEvents increased, since more key pressed

I peeked into the source code for PsychHIDOSKbCheck(int deviceIndex, double* scanList) in
/Source/Windows/PsychHID/PsychHIDStandardInterfaces.cpp
and it does create and start the KbQueue. Is it necessary, or they were copied/pasted, and left there by accident?
Thanks.
-Xiangrui

Oh, you’re right. Unfortunately that code isn’t redundant. We need to piggyback onto the same DirectInput machinery used by the keyboard queue for querying any input device other than keyboards via KbCheck, ie. for implementing KbCheck() also for mouse and joystick buttons. That’s messy, but that’s Windows. One workaround would be to skip the PsychHID call iff the wanted input device is a keyboard/keypad, because all keyboards and keypads are the same from Windows DirectInput PoV and also all keyboards are indistinguishable, ie. not individually selectable as on Linux or macOS.

Iow. KbCheck(-1) is the same as KbCheck(-2) and KbCheck(-3) and KbCheck(anykeyboardorkeypad) and simply KbCheck, so it could all just be mapped to the Screen(‘GetMouseHelper’,…) call as if it is a simple KbCheck.

I guess replacing in line 226 of KbCheck.m:

if allowPsychHID && (~IsWin || (IsWin && ~isempty(deviceNumber)))

by

if allowPsychHID && (~IsWin || (IsWin && ~isempty(deviceNumber) && deviceNumber >= 0))

would do the trick at least for KbCheck(-1) / (-2) / (-3).

Still messy, but would at east “fix” the common case.

It is unfortunate there is no perfect solution. If there is no extra benefit to use PsychHID for Windows KbCheck, your suggested change in KbCheck.m should be the easy “fix”.

I would suggest another fix in Windows PsychHIDOSKbCheck(). My test indicates, if the queue is already started with several number keys, PsychHID(‘KbCheck’) can detect Escape key without problem. This indicates the queue keys is not used by PsychHIDOSKbCheck. If this is true, when PsychHIDOSKbCheck() needs to create the queue, we can simply set keyMask to detect no keys. This will avoid buffer overflow problem, and the unneeded queue will cause the least harm.

If I understand the fix correctly, the change will be to define keyMask in PsychHIDOSKbCheck,

int keyMask[256];

and change Line 523 to

PsychHIDOSKbQueueCreate(deviceIndex, 256, &keyMask[0], 0, 0, 0, 0);

Another possibility is GetDeviceState() might work without starting queue? This is my guess based on the test that Escape key does not needed to be queued to detect. If this is true, this should be a real fix.

Thanks.
-Xiangrui

No the queue isn’t used, but DirectInput needs some additional configuration before kb->GetDeviceState can work, and that setup is done as a side-effect of starting the keyboard queue.

A better solution would be to have that setup code also be called from the PsychHIDOSKbCheck() routine. But then one would need state transition logic that does different setup depending on if a kbqueue is active at time of KbCheck or not, or if a kbqueue gets started after a KbCheck or a KbCheck gets called after a kbqueue was just started or stopped, or any other transition between kbqueues and KbCheck. And to figure out if the setup should be the same for pure kbqueue ops, vs. pure KbCheck, vs. both at the same time, and what kind of cross-talk and side-effects could happen.

Not impossible to do, but time consuming to do with all the meticulous testing of all cases that may happen. I don’t have time for that atm. It’s one of these things that can turn from a “should take less than an hour to fix” into a “two days later …” things.

I added a commit to my master branch, slightly refined, now PsychHID only gets called when KbCheck()'ing mice and joysticks on Windows. Seems to work.

That should take care of most common uses. Please let me know if you need me to do any testing. Thanks.