The key is to condense only statements that are completely alike:
const UINT k[5] = { VK_LBUTTON, VK_RBUTTON, VK_MBUTTON, VK_XBUTTON1, VK_XBUTTON2 };
static_assert( (RI_MOUSE_LEFT_BUTTON_DOWN << 2) == RI_MOUSE_RIGHT_BUTTON_DOWN );
for ( UINT i = 0; i < _count_of(k); ++i ) {
if (pMouseData->usButtonFlags & (RI_MOUSE_LEFT_BUTTON_DOWN << 2*i))
SetVKeyState(k[i], RI_KEY_MAKE);
if (pMouseData->usButtonFlags & (RI_MOUSE_LEFT_BUTTON_UP << 2*i)))
SetVKeyState(k[i], RI_KEY_BREAK);
}
All the comma operators are gone, unusual loop increment is gone, symbolic variables are still used for the key state.
I think this is actually easier to read than the original, because it fits on one page of code, and the repetition is obvious.
EDIT: And now the flag-relation assumption is documented.
I might actually go as far as:
struct { UINT vk; UINT downflag; UINT upflag;
} const k[] = {
{ VK_LBUTTON, RI_MOUSE_LEFT_BUTTON_DOWN, RI_MOUSE_LEFT_BUTTON_UP },
{ VK_RBUTTON, RI_MOUSE_RIGHT_BUTTON_DOWN, RI_MOUSE_RIGHT_BUTTON_UP },
...
};
for ( UINT i = 0; i < _count_of(k); ++i ) {
if (pMouseData->usButtonFlags & k[i].downflag)
SetVKeyState(k[i].vk, RI_KEY_MAKE);
if (pMouseData->usButtonFlags & k[i].upflag)
SetVKeyState(k[i].vk, RI_KEY_BREAK);
}
in order to remove the assumption about the flags using adjacent bits in the correct order.
You could use the latter version making the second argument to SetVKeyState
one of the table columns, but IMO that loses the valuable pairing structure.