[Z88dk-users] Reading multiple keys

Bridge to the z88dk-users mailing list
Post Reply
thricenightly
Member
Posts: 28
Joined: Thu Jun 01, 2017 5:46 pm

[Z88dk-users] Reading multiple keys

Post by thricenightly »

I'm trying to work out why this program won't show all 5 keys being pressed simultaneously:

Code: Select all

#include <stdio.h>
#include <input.h>
#include <arch/zx.h>

int main()
{
zx_cls(PAPER_WHITE);
while( 1 ) {

printf("\x16\x01\x01");

printf("Scancode is 0x%04X\n",   in_key_pressed( IN_KEY_SCANCODE_q ));
printf("Scancode is 0x%04X\n",   in_key_pressed( IN_KEY_SCANCODE_a ));
printf("Scancode is 0x%04X\n",   in_key_pressed( IN_KEY_SCANCODE_o ));
printf("Scancode is 0x%04X\n",   in_key_pressed( IN_KEY_SCANCODE_p ));
printf("Scancode is 0x%04X\n\n", in_key_pressed( IN_KEY_SCANCODE_SPACE ));
}
}
I think it's the nature of the keyboard matrix as described here:

http://www.worldofspectrum.org/faq/refe ... erence.htm

but I can't quite get my head around what's happening. Can someone explain?



------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

I think it's the nature of the keyboard matrix as described here:

http://www.worldofspectrum.org/faq/refe ... erence.htm

but I can't quite get my head around what's happening. Can someone explain?
That is odd. It will be something to do with the matrix but it may also be that the in_key_pressed function is also checking the state of the CAPS and SYM SHIFT keys so that it can distinguish between 'Q' and 'q', eg. With multiple keys pressed, CAPS and/or SYM SHIFT could be erroneously detected.

I'll take a closer look at this and it may turn out we should change in_key_pressed to stop detecting shifts or we should provide another similar function that does not check shifts.



------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

in_key_pressed function is also checking the state of the CAPS and SYM SHIFT keys so that it can distinguish between 'Q' and 'q', eg. With multiple keys pressed, CAPS and/or SYM SHIFT could be erroneously detected.
There's no problem with this -- this issue has been thought of before. in_key_pressed does not check if caps and sym shift are not pressed for keys like 'q'. Their state is only check when they have to be pressed as in 'Q' or '$'.

I'm now wondering if this is a PC keyboard issue. If you run your test program and press q, then a, then o. The o will not be registered. However if you press o, then a, then q the q will not be registered. That doesn't make any sense unless the PC keyboard driver / emulator cannot detect some keypress combinations.

I will try with several other emulators and see if the same behaviour results.



------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

I'm pretty sure this is an emulator issue / an issue with modern PC keyboards but I don't have a physical machine to test if the same behaviour occurs.

In the emulators I've tried, pressing q then a then o registers the qa keys but not o. Pressing o then a then q registers the oa keys but not q. This can only make sense if the issue is in the emulator.

If anyone has a real machine and would like to try it:
https://drive.google.com/file/d/0B6XhJJ ... sp=sharing

The program should detect simultaneous keypresses of qaop space.



------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
Timmy
Member
Posts: 135
Joined: Sat Mar 10, 2012 4:18 pm

Post by Timmy »

As I said it on the other forum, this is a "feature" of most PC keyboards.

The Spectrum can handle more than 3 keys at once, but it still have some limits.

The solution is easy: Buy a quality keyboard with claims that they can handle more than 3 keypresses at once. Or test it on a real Spectrum.

(for example, most gaming keyboards have WASD on a separate detection loop to just handle those keypresses.)



------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
thricenightly
Member
Posts: 28
Joined: Thu Jun 01, 2017 5:46 pm

Post by thricenightly »

in_key_pressed function is also checking the state of the CAPS and SYM SHIFT keys so that it can distinguish between 'Q' and 'q', eg. With multiple keys pressed, CAPS and/or SYM SHIFT could be erroneously detected.
There's no problem with this -- this issue has been thought of before. in_key_pressed does not check if caps and sym shift are not pressed for keys like 'q'. Their state is only check when they have to be pressed as in 'Q' or '$'.
We've got to the bottom of this (PC keyboard limitation) but I'm puzzled by the above statements you made regarding the shift keys. I can see in the ASM for in_key_pressed() that SHIFT and SYM are specifically checked for, but I'm not sure why. input_zx.h doesn't list scancodes for upper case characters, so I thought shifted key press detection would require 2 scans, one of the shift key and one for the letter key.

I'm thinking I've missed something important here. :)



------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

I'm puzzled by the above statements you made regarding the shift keys. I can see in the ASM for in_key_pressed() that SHIFT and SYM are specifically checked for, but I'm not sure why. input_zx.h doesn't list scancodes for upper case characters, so I thought shifted key press detection would require 2 scans, one of the shift key and one for the letter key.
The scan code includes the byte that must be placed on the upper 8 bits of port 0xfe to identify a key row, a bitmask to identify one bit out of the 5 column bits and two bits to indicate whether CAPS and/or SYM should also be pressed.

Besides the compile time constants defined in input_zx.h these scan codes can also be generated at runtime with function in_key_scancode(ascii character). The set of ascii codes that can be identified on the keyboard is in the key_translation_table: https://github.com/z88dk/z88dk/blob/mas ... _table.asm

So at runtime you could look up the scan code for '$' like this:

dollar_code = in_key_scancode('$');

and it will return a scan code that indicates SYM+4 needs to be pressed.

When you run in_key_pressed, that routine will make sure both sym shift and 4 are simultaneously pressed before returning true.

So the constants defined in input_zx.h are only a subset of the scan codes that are supported and represent what you would want to use in a game, eg, because what you care about are keypresses and not shifted keypresses.

The advantage of using the constants is you save the cost of having the key translation table in memory and the cost of the in_key_scancode() function. The other function that uses the key translation table is in_inkey(). So even though it's legitimate to detect that no keys are pressed with in_inkey()==0, it's cheaper and faster to use in_testkey() to avoid pulling that table into the binary.

What we are talking about is probably 200 bytes here but many games end up looking for extra memory so every byte can count.

The issue I was wondering about is if pressing multiple keys on the spectrum keyboard could generate false positives for caps and sym shift. So, for key 'a' which is lower case, I was wondering if a false CAPS would return false even if 'a' were pressed. But this won't happen because in_key_pressed() does not check if the shifts are not pressed - it only checks, if necessary, if the shifts are pressed. So a check on 'a' won't bother looking at the shift keys and there is no ghosted shift keys problem.



------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
thricenightly
Member
Posts: 28
Joined: Thu Jun 01, 2017 5:46 pm

Post by thricenightly »

The scan code includes the byte that must be placed on the upper 8 bits of port 0xfe to identify a key row, a bitmask to identify one bit out of the 5 column bits and two bits to indicate whether CAPS and/or SYM should also be pressed.
OK, all that's understood. There's more though. :)

I was trying to work out how you might detect the "Break" combination - CAPS SHIFT+Space. There doesn't appear to be a way to get in_key_scancode() to give you that, so I guessed it uses the top bits, something like:

Code: Select all

  uint16_t break_scancode = in_key_scancode(' ');
break_scancode |= 0x8000;
...
printf("Scan for <break> returns 0x%04X\n",  in_key_pressed( break_scancode ));
Empirically, it seems the top bit looks for CAPS and the second top bit looks for SYM. Since I'm writing it up, can you confirm that's the way to do it?



------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

I was trying to work out how you might detect the "Break" combination - CAPS SHIFT+Space. There doesn't appear to be a way to get in_key_scancode() to give you that, so I guessed it uses the top bits, something like:

Code: Select all

  uint16_t break_scancode = in_key_scancode(' ');
break_scancode |= 0x8000;
...
printf("Scan for <break> returns 0x%04X\n",  in_key_pressed( break_scancode ));
Empirically, it seems the top bit looks for CAPS and the second top bit looks for SYM. Since I'm writing it up, can you confirm that's the way to do it?
Yes that's the way to do it.

0x8000 for caps, 0x4000 for sym



------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
Post Reply