4

How can a Win32 application respond to only the first WM_KEYDOWN notification? The MSDN docs claim bit 30 "Specifies the previous key state. The value is 1 if the key is down before the message is sent, or it is zero if the key is up." but bit 30 is always 0 in my WndProc.

case WM_KEYDOWN:
    // ToDo - stop multiple notifications for repeating keys
    printf("WM_KEYDOWN %i %i", wParam, lParam & 30);
    return 0;

Is lParam & 30 the wrong way to ask for this? Am I doing something else wrong?

Nick Van Brunt
  • 15,244
  • 11
  • 66
  • 92
  • How did you end up implementing this? repeatCount=(lParam & 0xffff); if(repeatCount < 1) Action(); Doesn't seem to work for me. Nor does repeatCount < 2. I either get repeating keys or no key press at all. – Sam Becker Oct 12 '09 at 04:16
  • 1
    I used: if ((lParam & (1 << 30)) == 0)... although if (lParam & 0x40000000) worked as well if I remember correctly. My choice was based on readability (1 << 30) makes more sense to me than 0x40000000 – Nick Van Brunt Oct 13 '09 at 15:09

4 Answers4

12

To test bit 30 don't AND with 30, instead AND with 1 << 30.

const bool isBitSet = lParam & (1 << 30);
sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • It probably does read better but it has to be computed every time (unless the compiler is optimizing this). 0x4000000 Will be faster. – kjfletch Sep 03 '09 at 13:19
  • 3
    VC++7 is optimizing it to a constant value. – sharptooth Sep 03 '09 at 13:40
  • 1
    @kjfletch those kind of micro optimizations are certainly not needed anymore.. Any simple compiler could optimize that.. – Davy Landman Sep 11 '09 at 11:08
  • @kjfletch How much faster was it even if not optimized when you timed it? WM_KEYDOWN events come in only once every 1 million nanoseconds in my test, and it takes what, like 1 ns to shift a bit? Unless the bit is really, really, *REALLY* heavy; then it takes _maybe_ 3.5 ns. _Max._ – SO_fix_the_vote_sorting_bug Mar 14 '23 at 02:22
4

To get bit 30, you need this:

(lParam & 0x40000000)

An alternative would be to use bits 0-15 to get the repeat count:

int repeatCount = (lParam & 0xffff)

and only do anything if the repeat count is 0 (or possibly 1; I'm not sure whether the first message gets a repeat count of 0 or 1, and it's not clear from the documentation).

RichieHindle
  • 272,464
  • 47
  • 358
  • 399
  • This worked - thank you. My bit fiddling-fu is a little shaky. What exactly is 0x40000000? – Nick Van Brunt Sep 01 '09 at 14:36
  • 2
    That's a DWORD representing a bitmask consisting of bit 30 set and all other bits cleared. – sharptooth Sep 01 '09 at 14:37
  • You might want to read an introduction to hexadecimal, eg. http://www.learn-programming.za.net/articles_decbinhexoct.html It's not too hard, but not so trivial that I can explain it all here. – RichieHindle Sep 01 '09 at 14:40
4

Bitwise AND (lParam & 0x4000000) will work but you can easily make it more readable using the defines included with Windows.h (which you're already using for WM_KEYDOWN):

case WM_KEYDOWN:
    if((HIWORD(lParam) & KF_REPEAT) == 0) //process wParam
    return 0;

HIWORD takes the most significant 16 bits.

KF_REPEAT (= 0x4000) flags the location of the bit representing a repeat WM_KEYDOWN message.

The bitwise AND of these values equals 0 when the repeat flag is off (the initial keypress) and equals 1 every subsequent time (the autorepeat feature) until the key is released.

It's a small thing but it goes a long way to improving the readability of your code. 0x40000000 does not mean anything to the reader while much can be inferred from KF_REPEAT.

Sanders
  • 109
  • 5
1

The problem with doing lParam & 30 is that, '30' over here is considered to be in decimal, which when converted to binary would '11110'. Hence you are not testing bit 30 but just getting the result for lparam & 11110.

Hope this helps in clarifying the problem a bit.

Pratik Bhatt
  • 687
  • 4
  • 15