QB64Team / qb64

BASIC for the modern era.
https://www.qb64.org
Other
671 stars 96 forks source link

_KEYDOWN shift bug causing keys to stick pressed down #101

Open johannhowitzer opened 3 years ago

johannhowitzer commented 3 years ago

The state of _KEYDOWN will stick to true for a shift-modifiable key code, if the state of the shift key changes while the key is pressed. This affects any program using any shift-modifiable keys, even if shift is not explicitly a part of their function in any way. Bug was confirmed via testing already, in a forum thread some months ago (https://www.qb64.org/forum/index.php?topic=2798.0), so I'm making sure it is recorded properly here.

To Reproduce Compile following code;

DO _LIMIT 60 CLS IF _KEYDOWN(90) THEN PRINT "uppercase" IF _KEYDOWN(122) THEN PRINT "lowercase" _DISPLAY LOOP

  1. Start holding Z
  2. Start holding Shift
  3. Stop holding Z
  4. Stop holding Shift OR
  5. Start holding Shift
  6. Start holding Z
  7. Stop holding Shift
  8. Stop holding Z

Expected behavior First series of steps causes "lowercase" to persist onscreen, second causes "uppercase" to persist. It is also possible to un-stick both - to un-stick lowercase, press Z, and to un-stick uppercase, press and release Z while shift is held.

FellippeHeitor commented 3 years ago

I'm thinking about this again, since we're getting ready to release a new version, and since we allow you to poll whether "z" or "Z" are being pressed - as opposed to having a single code for that physical key - the code is actually giving you the expected results.

You hold small z, then, as Shift is being held, you are actually releasing big Z. You never really released small z.

Unless we had a unique code for each physical key, I believe we will have to stick with the current behavior and "call it a feature".

Since the _keyhit alternative I provided worked as expected, maybe it's the case of recommending _keydown is used only for single-glyph keys, like arrow keys, enter, etc - and add a wiki observation about this undesirable - yet expected - result.

FellippeHeitor commented 3 years ago

@flukiluke Do you see a workaround?

oitofelix commented 1 month ago

Just want to share this workaround I wrote last night. It works surprisingly well for alpha keys, provided it's called in a tight-enough loop. It can be extended to other keys as well if a lookup table of equivalent keys modulo shift is provided. I didn't bother because the program it's serving, of shiftable keys, only monitors letters for now, and those are trivial to map in respect to their equivalence under shift. Fundamentally this code works by monitoring keydown and keyup events using _KEYHIT and doesn't use _KEYDOWN at all. The limitations of _KEYHIT's (or INKEY$'s) typematic rate don't apply, because the last state is buffered until next update. A call to this function consumes the whole current consecutive sequence of _KEYHITs available, therefore, any external code detecting _KEYHITs must be conciliated within it. It was not an issue for me, because I use _KEYDOWN to handle everything that is invariant under shift. Hope it works well for you, until this issue is resolved in QB64 itself. Any suggestions, remarks or corrections are welcome.

'
'Return whether alpha key given by ascii CODE is pressed.  This works around
'a bug in QB64's _KEYDOWN that sticks down shifted alpha keys.
'
FUNCTION IsAlphaKeyDown% (code AS INTEGER)
  STATIC KeyCode(65 TO 122) AS INTEGER
  DO
    DIM k AS LONG: k = _KEYHIT
    DIM ak AS LONG: ak = ABS(k)
    IF 65 <= ak AND ak <= 122 THEN
      DIM k$: k$ = CHR$(ak)
      KeyCode(ASC(UCASE$(k$))) = k
      KeyCode(ASC(LCASE$(k$))) = k
    END IF
  LOOP WHILE k <> 0
  IF KeyCode(code) <> code THEN EXIT FUNCTION
  IsAlphaKeyDown = -1
END FUNCTION