1 /* Test Key event to Key message translation
3 * Copyright 2003 Rein Klazes
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 /* test whether the right type of messages:
21 * WM_KEYUP/DOWN vs WM_SYSKEYUP/DOWN are sent in case of combined
24 * For instance <ALT>-X can be accompished by
25 * the sequence ALT-KEY-DOWN, X-KEY-DOWN, ALT-KEY-UP, X-KEY-UP
26 * but also X-KEY-DOWN, ALT-KEY-DOWN, X-KEY-UP, ALT-KEY-UP
27 * Whether a KEY or a SYSKEY message is sent is not always clear, it is
28 * also not the same in WINNT as in WIN9X */
30 /* NOTE that there will be test failures under WIN9X
31 * No applications are known to me that rely on this
32 * so I don't fix it */
35 * 1. extend it to the wm_command and wm_syscommand notifications
36 * 2. add some more tests with special cases like dead keys or right (alt) key
37 * 3. there is some adapted code from input.c in here. Should really
38 * make that code exactly the same.
39 * 4. resolve the win9x case when there is a need or the testing frame work
41 * 5. The test app creates a window, the user should not take the focus
42 * away during its short existence. I could do something to prevent that
47 #define _WIN32_WINNT 0x403
54 #include "wine/test.h"
58 static long timetag
= 0x10000000;
60 static UINT (WINAPI
*ptr_SendInput
) (UINT
, INPUT
*, size_t);
62 #define MAXKEYEVENTS 6
63 #define MAXKEYMESSAGES MAXKEYEVENTS /* assuming a key event generates one
64 and only one message */
66 /* keyboard message names, sorted as their value */
67 static const char *MSGNAME
[]={"WM_KEYDOWN", "WM_KEYUP", "WM_CHAR","WM_DEADCHAR",
68 "WM_SYSKEYDOWN", "WM_SYSKEYUP", "WM_SYSCHAR", "WM_SYSDEADCHAR" ,"WM_KEYLAST"};
70 /* keyevents, add more as needed */
72 { ALTDOWN
= 1, ALTUP
, XDOWN
, XUP
, SHIFTDOWN
, SHIFTUP
, CTRLDOWN
, CTRLUP
} KEV
;
74 static const int GETVKEY
[]={0, VK_MENU
, VK_MENU
, 'X', 'X', VK_SHIFT
, VK_SHIFT
, VK_CONTROL
, VK_CONTROL
};
75 /* matching scan codes */
76 static const int GETSCAN
[]={0, 0x38, 0x38, 0x2D, 0x2D, 0x2A, 0x2A, 0x1D, 0x1D };
77 /* matching updown events */
78 static const int GETUPDOWN
[]={0, 0, KEYEVENTF_KEYUP
, 0, KEYEVENTF_KEYUP
, 0, KEYEVENTF_KEYUP
, 0, KEYEVENTF_KEYUP
};
79 /* matching descripts */
80 static const char *getdesc
[]={"", "+alt","-alt","+X","-X","+shift","-shift","+ctrl","-ctrl"};
82 /* The MSVC headers ignore our NONAMELESSUNION requests so we have to define our own type */
94 #define ADDTOINPUTS(kev) \
95 inputs[evtctr].type = INPUT_KEYBOARD; \
96 ((TEST_INPUT*)inputs)[evtctr].u.ki.wVk = GETVKEY[ kev]; \
97 ((TEST_INPUT*)inputs)[evtctr].u.ki.wScan = GETSCAN[ kev]; \
98 ((TEST_INPUT*)inputs)[evtctr].u.ki.dwFlags = GETUPDOWN[ kev]; \
99 ((TEST_INPUT*)inputs)[evtctr].u.ki.dwExtraInfo = 0; \
100 ((TEST_INPUT*)inputs)[evtctr].u.ki.time = ++timetag; \
109 /*******************************************
110 * add new test sets here
111 * the software will make all combinations of the
112 * keyevent defined here
114 static const struct {
116 KEV keydwn
[MAXKEYEVENTS
];
117 KEV keyup
[MAXKEYEVENTS
];
119 { 2, { ALTDOWN
, XDOWN
}, { ALTUP
, XUP
}},
120 { 3, { ALTDOWN
, XDOWN
, SHIFTDOWN
}, { ALTUP
, XUP
, SHIFTUP
}},
121 { 3, { ALTDOWN
, XDOWN
, CTRLDOWN
}, { ALTUP
, XUP
, CTRLUP
}},
122 { 3, { SHIFTDOWN
, XDOWN
, CTRLDOWN
}, { SHIFTUP
, XUP
, CTRLUP
}},
123 { 0 } /* mark the end */
126 /**********************adapted from input.c **********************************/
128 static BYTE InputKeyStateTable
[256];
129 static BYTE AsyncKeyStateTable
[256];
130 static BYTE TrackSysKey
= 0; /* determine whether ALT key up will cause a WM_SYSKEYUP
131 or a WM_KEYUP message */
136 unsigned long count
: 16;
137 unsigned long code
: 8;
138 unsigned long extended
: 1;
139 unsigned long unused
: 2;
140 unsigned long win_internal
: 2;
141 unsigned long context
: 1;
142 unsigned long previous
: 1;
143 unsigned long transition
: 1;
148 static int KbdMessage( KEV kev
, WPARAM
*pwParam
, LPARAM
*plParam
)
151 int VKey
= GETVKEY
[kev
];
157 keylp
.lp1
.code
= GETSCAN
[kev
];
158 keylp
.lp1
.extended
= 0 ;/* FIXME (ki->dwFlags & KEYEVENTF_EXTENDEDKEY) != 0; */
159 keylp
.lp1
.win_internal
= 0;
161 if (GETUPDOWN
[kev
] & KEYEVENTF_KEYUP
)
164 if( (InputKeyStateTable
[VK_MENU
] & 0x80) && (
165 (VKey
== VK_MENU
) || (VKey
== VK_CONTROL
) ||
166 !(InputKeyStateTable
[VK_CONTROL
] & 0x80))) {
167 if( TrackSysKey
== VK_MENU
|| /* <ALT>-down/<ALT>-up sequence */
168 (VKey
!= VK_MENU
)) /* <ALT>-down...<something else>-up */
169 message
= WM_SYSKEYUP
;
172 InputKeyStateTable
[VKey
] &= ~0x80;
173 keylp
.lp1
.previous
= 1;
174 keylp
.lp1
.transition
= 1;
178 keylp
.lp1
.previous
= (InputKeyStateTable
[VKey
] & 0x80) != 0;
179 keylp
.lp1
.transition
= 0;
180 if (!(InputKeyStateTable
[VKey
] & 0x80)) InputKeyStateTable
[VKey
] ^= 0x01;
181 InputKeyStateTable
[VKey
] |= 0x80;
182 AsyncKeyStateTable
[VKey
] |= 0x80;
184 message
= WM_KEYDOWN
;
185 if( (InputKeyStateTable
[VK_MENU
] & 0x80) &&
186 !(InputKeyStateTable
[VK_CONTROL
] & 0x80)) {
187 message
= WM_SYSKEYDOWN
;
192 keylp
.lp1
.context
= (InputKeyStateTable
[VK_MENU
] & 0x80) != 0; /* 1 if alt */
194 if( plParam
) *plParam
= keylp
.lp2
;
195 if( pwParam
) *pwParam
= VKey
;
199 /****************************** end copy input.c ****************************/
202 * . prepare the keyevents for SendInputs
203 * . calculate the "expected" messages
204 * . Send the events to our window
205 * . retrieve the messages from the input queue
208 static void do_test( HWND hwnd
, int seqnr
, const KEV td
[] )
211 INPUT inputs
[MAXKEYEVENTS
];
212 KMSG expmsg
[MAXKEYEVENTS
];
218 module
= GetModuleHandleA("user32");
220 ptr_SendInput
= (void *)GetProcAddress(module
, "SendInput");
221 if (!ptr_SendInput
) return;
224 TrackSysKey
=0; /* see input.c */
225 for( i
= 0; i
< MAXKEYEVENTS
; i
++) {
227 strcat(buf
, getdesc
[td
[i
]]);
229 expmsg
[i
].message
= KbdMessage(td
[i
], &(expmsg
[i
].wParam
), &(expmsg
[i
].lParam
)); /* see queue_kbd_event() */
231 expmsg
[i
].message
= 0;
233 for( kmctr
= 0; kmctr
< MAXKEYEVENTS
&& expmsg
[kmctr
].message
; kmctr
++)
235 assert( evtctr
<= MAXKEYEVENTS
);
236 assert( evtctr
== ptr_SendInput(evtctr
, &inputs
[0], sizeof(INPUT
)));
238 trace("======== key stroke sequence #%d: %s =============\n",
240 while( PeekMessage(&msg
,hwnd
,WM_KEYFIRST
,WM_KEYLAST
,PM_REMOVE
) ) {
241 trace("message[%d] %-15s wParam %04x lParam %08lx time %lx\n", i
,
242 MSGNAME
[msg
.message
- WM_KEYFIRST
], msg
.wParam
, msg
.lParam
, msg
.time
);
244 ok( msg
.message
== expmsg
[i
].message
&&
245 msg
.wParam
== expmsg
[i
].wParam
&&
246 msg
.lParam
== expmsg
[i
].lParam
,
247 "wrong message! expected:\n"
248 "message[%d] %-15s wParam %04x lParam %08lx\n",i
,
249 MSGNAME
[(expmsg
[i
]).message
- WM_KEYFIRST
],
250 expmsg
[i
].wParam
, expmsg
[i
].lParam
);
254 trace("%d messages retrieved\n", i
);
255 ok( i
== kmctr
, "message count is wrong: got %d expected: %d\n", i
, kmctr
);
258 /* test all combinations of the specified key events */
259 static void TestASet( HWND hWnd
, int nrkev
, const KEV kevdwn
[], const KEV kevup
[] )
263 KEV kbuf
[MAXKEYEVENTS
];
264 assert( nrkev
==2 || nrkev
==3);
265 for(i
=0;i
<MAXKEYEVENTS
;i
++) kbuf
[i
]=0;
266 /* two keys involved gives 4 test cases */
268 for(i
=0;i
<nrkev
;i
++) {
269 for(j
=0;j
<nrkev
;j
++) {
271 kbuf
[1] = kevdwn
[1-i
];
273 kbuf
[3] = kevup
[1-j
];
274 do_test( hWnd
, count
++, kbuf
);
278 /* three keys involved gives 36 test cases */
280 for(i
=0;i
<nrkev
;i
++){
281 for(j
=0;j
<nrkev
;j
++){
283 for(k
=0;k
<nrkev
;k
++){
284 if(k
==i
|| k
==j
) continue;
285 for(l
=0;l
<nrkev
;l
++){
286 for(m
=0;m
<nrkev
;m
++){
288 for(n
=0;n
<nrkev
;n
++){
289 if(n
==l
||n
==m
) continue;
296 do_test( hWnd
, count
++, kbuf
);
306 /* test each set specified in the global testkeyset array */
307 static void TestSysKeys( HWND hWnd
)
310 for(i
=0; testkeyset
[i
].nrkev
;i
++)
311 TestASet( hWnd
, testkeyset
[i
].nrkev
, testkeyset
[i
].keydwn
,
312 testkeyset
[i
].keyup
);
315 static LRESULT CALLBACK
WndProc( HWND hWnd
, UINT msg
, WPARAM wParam
,
321 /* window has focus, now do the test */
322 if( hWnd
== hWndTest
) TestSysKeys( hWnd
);
327 PostQuitMessage( 0 );
331 return( DefWindowProcA( hWnd
, msg
, wParam
, lParam
) );
341 HANDLE hInstance
= GetModuleHandleA( NULL
);
343 wclass
.lpszClassName
= "InputSysKeyTestClass";
344 wclass
.style
= CS_HREDRAW
| CS_VREDRAW
;
345 wclass
.lpfnWndProc
= WndProc
;
346 wclass
.hInstance
= hInstance
;
347 wclass
.hIcon
= LoadIconA( 0, (LPSTR
)IDI_APPLICATION
);
348 wclass
.hCursor
= LoadCursorA( NULL
, IDC_ARROW
);
349 wclass
.hbrBackground
= (HBRUSH
)( COLOR_WINDOW
+ 1);
350 wclass
.lpszMenuName
= 0;
351 wclass
.cbClsExtra
= 0;
352 wclass
.cbWndExtra
= 0;
353 assert (RegisterClassA( &wclass
));
354 /* create the test window that will receive the keystrokes */
355 assert ( hWndTest
= CreateWindowA( wclass
.lpszClassName
, "InputSysKeyTest",
356 WS_OVERLAPPEDWINDOW
, CW_USEDEFAULT
, 0, 100, 100,
357 NULL
, NULL
, hInstance
, NULL
) );
358 ShowWindow( hWndTest
, SW_SHOW
);
359 UpdateWindow( hWndTest
);
361 /* flush pending messages */
362 while (PeekMessage( &msg
, 0, 0, 0, PM_REMOVE
)) DispatchMessageA( &msg
);
364 SendMessageA(hWndTest
, WM_USER
, 0, 0);
365 DestroyWindow(hWndTest
);