+/* key-handling code is always ugly. It just ends up working out
+ that way.
+
+ #### Most of the sticky-modifier code below is copied from similar
+ code in event-Xt.c. They should somehow or other be merged.
+
+ Here are some pointers:
+
+ -- DOWN_MASK indicates which modifiers should be treated as "down"
+ when the corresponding upstroke happens. It gets reset for
+ a particular modifier when that modifier goes up, and reset
+ for all modifiers when a non-modifier key is pressed. Example:
+
+ I press Control-A-Shift and then release Control-A-Shift.
+ I want the Shift key to be sticky but not the Control key.
+
+ -- If a modifier key is sticky, I can unstick it by pressing
+ the modifier key again. */
+
+static WPARAM last_downkey;
+static int need_to_add_mask, down_mask;
+
+#define XEMSW_LCONTROL (1<<0)
+#define XEMSW_RCONTROL (1<<1)
+#define XEMSW_LSHIFT (1<<2)
+#define XEMSW_RSHIFT (1<<3)
+#define XEMSW_LMENU (1<<4)
+#define XEMSW_RMENU (1<<5)
+
+static int
+mswindows_handle_sticky_modifiers (WPARAM wParam, LPARAM lParam,
+ int downp, int keyp)
+{
+ int mods = 0;
+
+ if (!modifier_keys_are_sticky) /* Optimize for non-sticky modifiers */
+ return 0;
+
+ if (! (keyp &&
+ (wParam == VK_CONTROL || wParam == VK_LCONTROL ||
+ wParam == VK_RCONTROL ||
+ wParam == VK_MENU || wParam == VK_LMENU ||
+ wParam == VK_RMENU ||
+ wParam == VK_SHIFT || wParam == VK_LSHIFT ||
+ wParam == VK_RSHIFT)))
+ { /* Not a modifier key */
+ if (downp && keyp && !last_downkey)
+ last_downkey = wParam;
+ /* If I hold press-and-release the Control key and then press
+ and hold down the right arrow, I want it to auto-repeat
+ Control-Right. On the other hand, if I do the same but
+ manually press the Right arrow a bunch of times, I want
+ to see one Control-Right and then a bunch of Rights.
+ This means that we need to distinguish between an
+ auto-repeated key and a key pressed and released a bunch
+ of times. */
+ else if ((downp && !keyp) ||
+ (downp && keyp && last_downkey &&
+ (wParam != last_downkey ||
+ /* the "previous key state" bit indicates autorepeat */
+ ! (lParam & (1 << 30)))))
+ {
+ need_to_add_mask = 0;
+ last_downkey = 0;
+ }
+ if (downp)
+ down_mask = 0;
+
+ mods = need_to_add_mask;
+ }
+ else /* Modifier key pressed */
+ {
+ /* If a non-modifier key was pressed in the middle of a bunch
+ of modifiers, then it unsticks all the modifiers that were
+ previously pressed. We cannot unstick the modifiers until
+ now because we want to check for auto-repeat of the
+ non-modifier key. */
+
+ if (last_downkey)
+ {
+ last_downkey = 0;
+ need_to_add_mask = 0;
+ }
+
+#define FROB(mask) \
+do { \
+ if (downp && keyp) \
+ { \
+ /* If modifier key is already sticky, \
+ then unstick it. Note that we do \
+ not test down_mask to deal with the \
+ unlikely but possible case that the \
+ modifier key auto-repeats. */ \
+ if (need_to_add_mask & mask) \
+ { \
+ need_to_add_mask &= ~mask; \
+ down_mask &= ~mask; \
+ } \
+ else \
+ down_mask |= mask; \
+ } \
+ else \
+ { \
+ if (down_mask & mask) \
+ { \
+ down_mask &= ~mask; \
+ need_to_add_mask |= mask; \
+ } \
+ } \
+} while (0)
+
+ if ((wParam == VK_CONTROL && (lParam & 0x1000000))
+ || wParam == VK_RCONTROL)
+ FROB (XEMSW_RCONTROL);
+ if ((wParam == VK_CONTROL && !(lParam & 0x1000000))
+ || wParam == VK_LCONTROL)
+ FROB (XEMSW_LCONTROL);
+
+ if ((wParam == VK_SHIFT && (lParam & 0x1000000))
+ || wParam == VK_RSHIFT)
+ FROB (XEMSW_RSHIFT);
+ if ((wParam == VK_SHIFT && !(lParam & 0x1000000))
+ || wParam == VK_LSHIFT)
+ FROB (XEMSW_LSHIFT);
+
+ if ((wParam == VK_MENU && (lParam & 0x1000000))
+ || wParam == VK_RMENU)
+ FROB (XEMSW_RMENU);
+ if ((wParam == VK_MENU && !(lParam & 0x1000000))
+ || wParam == VK_LMENU)
+ FROB (XEMSW_LMENU);
+ }
+#undef FROB
+
+ if (mods && downp)
+ {
+ BYTE keymap[256];
+
+ GetKeyboardState (keymap);
+
+ if (mods & XEMSW_LCONTROL)
+ {
+ keymap [VK_CONTROL] |= 0x80;
+ keymap [VK_LCONTROL] |= 0x80;
+ }
+ if (mods & XEMSW_RCONTROL)
+ {
+ keymap [VK_CONTROL] |= 0x80;
+ keymap [VK_RCONTROL] |= 0x80;
+ }
+
+ if (mods & XEMSW_LSHIFT)
+ {
+ keymap [VK_SHIFT] |= 0x80;
+ keymap [VK_LSHIFT] |= 0x80;
+ }
+ if (mods & XEMSW_RSHIFT)
+ {
+ keymap [VK_SHIFT] |= 0x80;
+ keymap [VK_RSHIFT] |= 0x80;
+ }
+
+ if (mods & XEMSW_LMENU)
+ {
+ keymap [VK_MENU] |= 0x80;
+ keymap [VK_LMENU] |= 0x80;
+ }
+ if (mods & XEMSW_RMENU)
+ {
+ keymap [VK_MENU] |= 0x80;
+ keymap [VK_RMENU] |= 0x80;
+ }
+
+ SetKeyboardState (keymap);
+ return 1;
+ }
+
+ return 0;
+}
+
+static void
+clear_sticky_modifiers (void)
+{
+ need_to_add_mask = 0;
+ last_downkey = 0;
+ down_mask = 0;
+}
+
+#ifdef DEBUG_XEMACS
+
+#if 0
+
+static void
+output_modifier_keyboard_state (void)
+{
+ BYTE keymap[256];
+
+ GetKeyboardState (keymap);
+
+ stderr_out ("GetKeyboardState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n",
+ keymap[VK_MENU] & 0x80 ? 1 : 0,
+ keymap[VK_MENU] & 0x1 ? 1 : 0,
+ keymap[VK_LMENU] & 0x80 ? 1 : 0,
+ keymap[VK_LMENU] & 0x1 ? 1 : 0,
+ keymap[VK_RMENU] & 0x80 ? 1 : 0,
+ keymap[VK_RMENU] & 0x1 ? 1 : 0);
+ stderr_out ("GetKeyboardState VK_CONTROL %d %d VK_LCONTROL %d %d VK_RCONTROL %d %d\n",
+ keymap[VK_CONTROL] & 0x80 ? 1 : 0,
+ keymap[VK_CONTROL] & 0x1 ? 1 : 0,
+ keymap[VK_LCONTROL] & 0x80 ? 1 : 0,
+ keymap[VK_LCONTROL] & 0x1 ? 1 : 0,
+ keymap[VK_RCONTROL] & 0x80 ? 1 : 0,
+ keymap[VK_RCONTROL] & 0x1 ? 1 : 0);
+ stderr_out ("GetKeyboardState VK_SHIFT %d %d VK_LSHIFT %d %d VK_RSHIFT %d %d\n",
+ keymap[VK_SHIFT] & 0x80 ? 1 : 0,
+ keymap[VK_SHIFT] & 0x1 ? 1 : 0,
+ keymap[VK_LSHIFT] & 0x80 ? 1 : 0,
+ keymap[VK_LSHIFT] & 0x1 ? 1 : 0,
+ keymap[VK_RSHIFT] & 0x80 ? 1 : 0,
+ keymap[VK_RSHIFT] & 0x1 ? 1 : 0);
+}
+
+#endif
+
+/* try to debug the stuck-alt-key problem.
+
+ #### this happens only inconsistently, and may only happen when using
+ StickyKeys in the Win2000 accessibility section of the control panel,
+ which is extremely broken for other reasons. */
+
+static void
+output_alt_keyboard_state (void)
+{
+ BYTE keymap[256];
+ SHORT keystate[3];
+ // SHORT asyncstate[3];
+
+ GetKeyboardState (keymap);
+ keystate[0] = GetKeyState (VK_MENU);
+ keystate[1] = GetKeyState (VK_LMENU);
+ keystate[2] = GetKeyState (VK_RMENU);
+ /* Doing this interferes with key processing. */
+/* asyncstate[0] = GetAsyncKeyState (VK_MENU); */
+/* asyncstate[1] = GetAsyncKeyState (VK_LMENU); */
+/* asyncstate[2] = GetAsyncKeyState (VK_RMENU); */
+
+ stderr_out ("GetKeyboardState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n",
+ keymap[VK_MENU] & 0x80 ? 1 : 0,
+ keymap[VK_MENU] & 0x1 ? 1 : 0,
+ keymap[VK_LMENU] & 0x80 ? 1 : 0,
+ keymap[VK_LMENU] & 0x1 ? 1 : 0,
+ keymap[VK_RMENU] & 0x80 ? 1 : 0,
+ keymap[VK_RMENU] & 0x1 ? 1 : 0);
+ stderr_out ("GetKeyState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n",
+ keystate[0] & 0x8000 ? 1 : 0,
+ keystate[0] & 0x1 ? 1 : 0,
+ keystate[1] & 0x8000 ? 1 : 0,
+ keystate[1] & 0x1 ? 1 : 0,
+ keystate[2] & 0x8000 ? 1 : 0,
+ keystate[2] & 0x1 ? 1 : 0);
+/* stderr_out ("GetAsyncKeyState VK_MENU %d %d VK_LMENU %d %d VK_RMENU %d %d\n", */
+/* asyncstate[0] & 0x8000 ? 1 : 0, */
+/* asyncstate[0] & 0x1 ? 1 : 0, */
+/* asyncstate[1] & 0x8000 ? 1 : 0, */
+/* asyncstate[1] & 0x1 ? 1 : 0, */
+/* asyncstate[2] & 0x8000 ? 1 : 0, */
+/* asyncstate[2] & 0x1 ? 1 : 0); */
+}
+
+#endif /* DEBUG_XEMACS */
+
+