x11drv: (bug 4685) properly handle VK_LMENU and friends
tkho at ucla.edu
tkho at ucla.edu
Thu Mar 30 20:50:00 CST 2006
Hi,
This issue was brought up in bug 4685:
http://bugs.winehq.org/show_bug.cgi?id=4685
with some comments from Vitaliy Margolen at
http://www.winehq.org/pipermail/wine-devel/2006-March/045771.html
Thanks,
Thomas Kho
2006-03-30 Thomas Kho <tkho at ucla.edu>
* dlls/user/tests/input.c:
user: add black box test for SendInput()
* dlls/x11drv/keyboard.c:
x11drv: flag additional keystate for CTRL, ALT, SHIFT and send correct
messages
* server/queue.c:
server: flag additional keystate for CTRL, ALT, SHIFT
dlls/user/tests/input.c | 148 +++++++++++++++++++++++++++++++++++++++++++++++-
dlls/x11drv/keyboard.c | 22 ++++++-
server/queue.c | 12 +++
3 files changed, 178 insertions(+), 4 deletions(-)
Signed-off-by: Thomas Kho <tkho at ucla.edu>
Index: dlls/user/tests/input.c
===================================================================
RCS file: /home/wine/wine/dlls/user/tests/input.c,v
retrieving revision 1.7
diff -u -r1.7 input.c
--- dlls/user/tests/input.c 6 Dec 2004 11:51:29 -0000 1.7
+++ dlls/user/tests/input.c 31 Mar 2006 02:42:24 -0000
@@ -336,7 +336,7 @@
return 0;
}
-START_TEST(input)
+static void test_Input_whitebox(void)
{
MSG msg;
WNDCLASSA wclass;
@@ -366,3 +366,149 @@
SendMessageA(hWndTest, WM_USER, 0, 0);
DestroyWindow(hWndTest);
}
+
+static void empty_message_queue(void) {
+ MSG msg;
+ while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+}
+
+struct transition_s {
+ BYTE wVk;
+ BYTE before_state;
+ BYTE _todo_wine;
+};
+
+struct sendinput_test_s {
+ WORD wVk;
+ DWORD dwFlags;
+ struct transition_s expected_transitions[MAXKEYEVENTS+1];
+ UINT expected_messages[MAXKEYMESSAGES+1];
+} sendinput_test[] = {
+ /* test ALT+F */
+ {VK_LMENU, 0, {{0x12, 0x00, 0}, {0xa4, 0x00, 0}, {0}}, {0x104, 0}},
+ {'F', 0, {{0x46, 0x00, 0}, {0}}, {0x104, 0x106, 0x112, 0}},
+ {'F', KEYEVENTF_KEYUP, {{0x46, 0x80, 0}, {0}}, {0x105, 0}},
+ {VK_LMENU, KEYEVENTF_KEYUP, {{0x12, 0x80, 0}, {0xa4, 0x80, 0}, {0}},
+ {0x101, 0}},
+
+ /* test CTRL+O */
+ {VK_LCONTROL, 0, {{0x11, 0x00, 0}, {0xa2, 0x00, 0}, {0}}, {0x100, 0}},
+ {'O', 0, {{0x4f, 0x00, 0}, {0}}, {0x100, 0x102, 0}},
+ {'O', KEYEVENTF_KEYUP, {{0x4f, 0x80, 0}, {0}}, {0x101, 0}},
+ {VK_LCONTROL, KEYEVENTF_KEYUP,
+ {{0x11, 0x80, 0}, {0xa2, 0x80, 0}, {0}}, {0x101, 0}},
+
+ /* test ALT+CTRL+X */
+ {VK_LMENU, 0, {{0x12, 0x00, 0}, {0xa4, 0x00, 0}, {0}}, {0x104, 0}},
+ {VK_LCONTROL, 0, {{0x11, 0x00, 0}, {0xa2, 0x00, 0}, {0}}, {0x100, 0}},
+ {'X', 0, {{0x58, 0x00, 0}, {0}}, {0x100, 0}},
+ {'X', KEYEVENTF_KEYUP, {{0x58, 0x80, 0}, {0}}, {0x101, 0}},
+ {VK_LCONTROL, KEYEVENTF_KEYUP,
+ {{0x11, 0x80, 0}, {0xa2, 0x80, 0}, {0}}, {0x105, 0}},
+ {VK_LMENU, KEYEVENTF_KEYUP, {{0x12, 0x80, 0}, {0xa4, 0x80, 0}, {0}},
+ {0x101, 0}},
+
+ {0, 0, {{0}}, {0}} /* end */
+};
+
+static struct sendinput_test_s *pTest = sendinput_test;
+static UINT *pMsg = sendinput_test[0].expected_messages;
+
+/* Verify that only specified key state transitions occur */
+void compare_and_check(int id, BYTE *ks1, BYTE *ks2, struct transition_s *t) {
+ int i;
+ while (t->wVk) {
+ int matched = ((ks1[t->wVk]&0x80) == (t->before_state&0x80)
+ && (ks2[t->wVk]&0x80) == (~t->before_state&0x80));
+ if (t->_todo_wine) {
+ todo_wine {
+ ok(matched, "%02d: %02x from %02x -> %02x "
+ "instead of %02x -> %02x\n", id, t->wVk,
+ ks1[t->wVk]&0x80, ks2[t->wVk]&0x80, t->before_state,
+ ~t->before_state&0x80);
+ }
+ } else {
+ ok(matched, "%02d: %02x from %02x -> %02x "
+ "instead of %02x -> %02x\n", id, t->wVk,
+ ks1[t->wVk]&0x80, ks2[t->wVk]&0x80, t->before_state,
+ ~t->before_state&0x80);
+ }
+ ks2[t->wVk] = ks1[t->wVk]; /* clear the match */
+ t++;
+ }
+ for (i = 0; i < 256; i++)
+ ok(ks2[i] == ks1[i], "%02d: %02x from %02x -> %02x unexpected\n",
+ id, i, ks1[i], ks2[i]);
+}
+
+/* WndProc2 checks that we get at least the messages specified */
+static LRESULT CALLBACK WndProc2(HWND hWnd, UINT Msg, WPARAM wParam,
+ LPARAM lParam)
+{
+ if (pTest->wVk != 0) { /* not end */
+ while(pTest->wVk != 0 && *pMsg == 0) {
+ pTest++;
+ pMsg = pTest->expected_messages;
+ }
+ if (Msg == *pMsg)
+ pMsg++;
+ }
+ return DefWindowProc(hWnd, Msg, wParam, lParam);
+}
+
+static void test_Input_blackbox(void)
+{
+ INPUT i;
+ int ii;
+ BYTE ks1[256], ks2[256];
+ LONG_PTR prevWndProc;
+
+ HWND window;
+ window = CreateWindow("Static", NULL, WS_POPUP|WS_HSCROLL|WS_VSCROLL
+ |WS_VISIBLE, 0, 0, 200, 60, NULL, NULL,
+ NULL, NULL);
+ ok(window != NULL, "error: %d\n", (int) GetLastError());
+
+ /* must process all initial messages, otherwise X11DRV_KeymapNotify unsets
+ * key state set by SendInput(). */
+ empty_message_queue();
+
+ prevWndProc = SetWindowLongPtr(window, GWLP_WNDPROC, (LONG_PTR) WndProc2);
+ ok(prevWndProc != 0 || (prevWndProc == 0 && GetLastError() == 0),
+ "error: %d\n", (int) GetLastError());
+
+ i.type = INPUT_KEYBOARD;
+ i.ki.wScan = 0;
+ i.ki.time = 0;
+ i.ki.dwExtraInfo = 0;
+
+ for (ii = 0; ii < sizeof(sendinput_test)/sizeof(struct sendinput_test_s)-1;
+ ii++) {
+ GetKeyboardState(ks1);
+ i.ki.dwFlags = sendinput_test[ii].dwFlags;;
+ i.ki.wVk = sendinput_test[ii].wVk;
+ SendInput(1, &i, sizeof(INPUT));
+ empty_message_queue();
+ GetKeyboardState(ks2);
+ compare_and_check(ii, ks1, ks2,
+ sendinput_test[ii].expected_transitions);
+ }
+
+ /* *pMsg should be 0 and (++pTest)->wVk should be 0 */
+ if (pTest->wVk && *pMsg == 0) pTest++;
+ while(pTest->wVk && pTest->expected_messages[0] == 0) {
+ ++pTest;
+ }
+ ok(*pMsg == 0 && pTest->wVk == 0,
+ "not enough messages found; looking for %x\n", *pMsg);
+ DestroyWindow(window);
+}
+
+START_TEST(input)
+{
+ test_Input_whitebox();
+ test_Input_blackbox();
+}
Index: dlls/x11drv/keyboard.c
===================================================================
RCS file: /home/wine/wine/dlls/x11drv/keyboard.c,v
retrieving revision 1.86
diff -u -r1.86 keyboard.c
--- dlls/x11drv/keyboard.c 30 Mar 2006 15:42:09 -0000 1.86
+++ dlls/x11drv/keyboard.c 31 Mar 2006 02:42:24 -0000
@@ -1102,6 +1102,19 @@
static BOOL NumState=FALSE, CapsState=FALSE;
+/***********************************************************************
+ * VkStripLR
+ */
+static WORD VkStripLR(WORD wVk)
+{
+ if (wVk == VK_LMENU || wVk == VK_RMENU)
+ return VK_MENU;
+ else if (wVk == VK_LCONTROL || wVk == VK_RCONTROL)
+ return VK_CONTROL;
+ else if (wVk == VK_LSHIFT || wVk == VK_RSHIFT)
+ return VK_SHIFT;
+ return wVk;
+}
/***********************************************************************
* X11DRV_send_keyboard_input
@@ -1127,14 +1140,16 @@
{
message = WM_KEYUP;
if ((key_state_table[VK_MENU] & 0x80) &&
- ((wVk == VK_MENU) || (wVk == VK_CONTROL) || !(key_state_table[VK_CONTROL] & 0x80)))
+ ((VkStripLR(wVk) == VK_MENU) || (VkStripLR(wVk) == VK_CONTROL)
+ || !(key_state_table[VK_CONTROL] & 0x80)))
{
if( TrackSysKey == VK_MENU || /* <ALT>-down/<ALT>-up sequence */
- (wVk != VK_MENU)) /* <ALT>-down...<something else>-up */
+ (VkStripLR(wVk) != VK_MENU)) /* <ALT>-down...<something else>-up */
message = WM_SYSKEYUP;
TrackSysKey = 0;
}
key_state_table[wVk] &= ~0x80;
+ key_state_table[VkStripLR(wVk)] &= ~0x80;
keylp.lp1.previous = 1;
keylp.lp1.transition = 1;
}
@@ -1144,12 +1159,13 @@
keylp.lp1.transition = 0;
if (!(key_state_table[wVk] & 0x80)) key_state_table[wVk] ^= 0x01;
key_state_table[wVk] |= 0xc0;
+ key_state_table[VkStripLR(wVk)] |= 0xc0;
message = WM_KEYDOWN;
if ((key_state_table[VK_MENU] & 0x80) && !(key_state_table[VK_CONTROL] & 0x80))
{
message = WM_SYSKEYDOWN;
- TrackSysKey = wVk;
+ TrackSysKey = VkStripLR(wVk);
}
}
Index: server/queue.c
===================================================================
RCS file: /home/wine/wine/server/queue.c,v
retrieving revision 1.76
diff -u -r1.76 queue.c
--- server/queue.c 14 Feb 2006 13:31:10 -0000 1.76
+++ server/queue.c 31 Mar 2006 02:42:24 -0000
@@ -1131,6 +1131,18 @@
case VK_MENU:
set_input_key_state( input, extended ? VK_RMENU : VK_LMENU, down );
break;
+ case VK_LCONTROL:
+ case VK_RCONTROL:
+ set_input_key_state( input, VK_CONTROL, down );
+ break;
+ case VK_LMENU:
+ case VK_RMENU:
+ set_input_key_state( input, VK_MENU, down );
+ break;
+ case VK_LSHIFT:
+ case VK_RSHIFT:
+ set_input_key_state( input, VK_SHIFT, down );
+ break;
}
break;
}
More information about the wine-patches
mailing list