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 0x401
56 #include "wine/test.h"
60 static long timetag
= 0x10000000;
62 static UINT (WINAPI
*ptr_SendInput
) (UINT
, INPUT
*, size_t);
64 #define MAXKEYEVENTS 6
65 #define MAXKEYMESSAGES MAXKEYEVENTS /* assuming a key event generates one
66 and only one message */
68 /* keyboard message names, sorted as their value */
69 static const char *MSGNAME
[]={"WM_KEYDOWN", "WM_KEYUP", "WM_CHAR","WM_DEADCHAR",
70 "WM_SYSKEYDOWN", "WM_SYSKEYUP", "WM_SYSCHAR", "WM_SYSDEADCHAR" ,"WM_KEYLAST"};
72 /* keyevents, add more as needed */
74 { ALTDOWN
= 1, ALTUP
, XDOWN
, XUP
, SHIFTDOWN
, SHIFTUP
, CTRLDOWN
, CTRLUP
} KEV
;
76 static const int GETVKEY
[]={0, VK_MENU
, VK_MENU
, 'X', 'X', VK_SHIFT
, VK_SHIFT
, VK_CONTROL
, VK_CONTROL
};
77 /* matching scan codes */
78 static const int GETSCAN
[]={0, 0x38, 0x38, 0x2D, 0x2D, 0x2A, 0x2A, 0x1D, 0x1D };
79 /* matching updown events */
80 static const int GETUPDOWN
[]={0, 0, KEYEVENTF_KEYUP
, 0, KEYEVENTF_KEYUP
, 0, KEYEVENTF_KEYUP
, 0, KEYEVENTF_KEYUP
};
81 /* matching descripts */
82 static const char *getdesc
[]={"", "+alt","-alt","+X","-X","+shift","-shift","+ctrl","-ctrl"};
84 /* The MSVC headers ignore our NONAMELESSUNION requests so we have to define our own type */
96 #define ADDTOINPUTS(kev) \
97 inputs[evtctr].type = INPUT_KEYBOARD; \
98 ((TEST_INPUT*)inputs)[evtctr].u.ki.wVk = GETVKEY[ kev]; \
99 ((TEST_INPUT*)inputs)[evtctr].u.ki.wScan = GETSCAN[ kev]; \
100 ((TEST_INPUT*)inputs)[evtctr].u.ki.dwFlags = GETUPDOWN[ kev]; \
101 ((TEST_INPUT*)inputs)[evtctr].u.ki.dwExtraInfo = 0; \
102 ((TEST_INPUT*)inputs)[evtctr].u.ki.time = ++timetag; \
111 /*******************************************
112 * add new test sets here
113 * the software will make all combinations of the
114 * keyevent defined here
116 static const struct {
118 KEV keydwn
[MAXKEYEVENTS
];
119 KEV keyup
[MAXKEYEVENTS
];
121 { 2, { ALTDOWN
, XDOWN
}, { ALTUP
, XUP
}},
122 { 3, { ALTDOWN
, XDOWN
, SHIFTDOWN
}, { ALTUP
, XUP
, SHIFTUP
}},
123 { 3, { ALTDOWN
, XDOWN
, CTRLDOWN
}, { ALTUP
, XUP
, CTRLUP
}},
124 { 3, { SHIFTDOWN
, XDOWN
, CTRLDOWN
}, { SHIFTUP
, XUP
, CTRLUP
}},
125 { 0 } /* mark the end */
128 /**********************adapted from input.c **********************************/
130 static BYTE InputKeyStateTable
[256];
131 static BYTE AsyncKeyStateTable
[256];
132 static BYTE TrackSysKey
= 0; /* determine whether ALT key up will cause a WM_SYSKEYUP
133 or a WM_KEYUP message */
138 unsigned long count
: 16;
139 unsigned long code
: 8;
140 unsigned long extended
: 1;
141 unsigned long unused
: 2;
142 unsigned long win_internal
: 2;
143 unsigned long context
: 1;
144 unsigned long previous
: 1;
145 unsigned long transition
: 1;
150 static int KbdMessage( KEV kev
, WPARAM
*pwParam
, LPARAM
*plParam
)
153 int VKey
= GETVKEY
[kev
];
159 keylp
.lp1
.code
= GETSCAN
[kev
];
160 keylp
.lp1
.extended
= 0 ;/* FIXME (ki->dwFlags & KEYEVENTF_EXTENDEDKEY) != 0; */
161 keylp
.lp1
.win_internal
= 0;
163 if (GETUPDOWN
[kev
] & KEYEVENTF_KEYUP
)
166 if( (InputKeyStateTable
[VK_MENU
] & 0x80) && (
167 (VKey
== VK_MENU
) || (VKey
== VK_CONTROL
) ||
168 !(InputKeyStateTable
[VK_CONTROL
] & 0x80))) {
169 if( TrackSysKey
== VK_MENU
|| /* <ALT>-down/<ALT>-up sequence */
170 (VKey
!= VK_MENU
)) /* <ALT>-down...<something else>-up */
171 message
= WM_SYSKEYUP
;
174 InputKeyStateTable
[VKey
] &= ~0x80;
175 keylp
.lp1
.previous
= 1;
176 keylp
.lp1
.transition
= 1;
180 keylp
.lp1
.previous
= (InputKeyStateTable
[VKey
] & 0x80) != 0;
181 keylp
.lp1
.transition
= 0;
182 if (!(InputKeyStateTable
[VKey
] & 0x80)) InputKeyStateTable
[VKey
] ^= 0x01;
183 InputKeyStateTable
[VKey
] |= 0x80;
184 AsyncKeyStateTable
[VKey
] |= 0x80;
186 message
= WM_KEYDOWN
;
187 if( (InputKeyStateTable
[VK_MENU
] & 0x80) &&
188 !(InputKeyStateTable
[VK_CONTROL
] & 0x80)) {
189 message
= WM_SYSKEYDOWN
;
194 keylp
.lp1
.context
= (InputKeyStateTable
[VK_MENU
] & 0x80) != 0; /* 1 if alt */
196 if( plParam
) *plParam
= keylp
.lp2
;
197 if( pwParam
) *pwParam
= VKey
;
201 /****************************** end copy input.c ****************************/
204 * . prepare the keyevents for SendInputs
205 * . calculate the "expected" messages
206 * . Send the events to our window
207 * . retrieve the messages from the input queue
210 static void do_test( HWND hwnd
, int seqnr
, const KEV td
[] )
213 INPUT inputs
[MAXKEYEVENTS
];
214 KMSG expmsg
[MAXKEYEVENTS
];
220 module
= GetModuleHandleA("user32");
222 ptr_SendInput
= (void *)GetProcAddress(module
, "SendInput");
223 if (!ptr_SendInput
) return;
226 TrackSysKey
=0; /* see input.c */
227 for( i
= 0; i
< MAXKEYEVENTS
; i
++) {
229 strcat(buf
, getdesc
[td
[i
]]);
231 expmsg
[i
].message
= KbdMessage(td
[i
], &(expmsg
[i
].wParam
), &(expmsg
[i
].lParam
)); /* see queue_kbd_event() */
233 expmsg
[i
].message
= 0;
235 for( kmctr
= 0; kmctr
< MAXKEYEVENTS
&& expmsg
[kmctr
].message
; kmctr
++)
237 assert( evtctr
<= MAXKEYEVENTS
);
238 assert( evtctr
== ptr_SendInput(evtctr
, &inputs
[0], sizeof(INPUT
)));
240 trace("======== key stroke sequence #%d: %s =============\n",
242 while( PeekMessage(&msg
,hwnd
,WM_KEYFIRST
,WM_KEYLAST
,PM_REMOVE
) ) {
243 trace("message[%d] %-15s wParam %04x lParam %08lx time %lx\n", i
,
244 MSGNAME
[msg
.message
- WM_KEYFIRST
], msg
.wParam
, msg
.lParam
, msg
.time
);
246 ok( msg
.message
== expmsg
[i
].message
&&
247 msg
.wParam
== expmsg
[i
].wParam
&&
248 msg
.lParam
== expmsg
[i
].lParam
,
249 "wrong message! expected:\n"
250 "message[%d] %-15s wParam %04x lParam %08lx\n",i
,
251 MSGNAME
[(expmsg
[i
]).message
- WM_KEYFIRST
],
252 expmsg
[i
].wParam
, expmsg
[i
].lParam
);
256 trace("%d messages retrieved\n", i
);
257 ok( i
== kmctr
, "message count is wrong: got %d expected: %d\n", i
, kmctr
);
260 /* test all combinations of the specified key events */
261 static void TestASet( HWND hWnd
, int nrkev
, const KEV kevdwn
[], const KEV kevup
[] )
265 KEV kbuf
[MAXKEYEVENTS
];
266 assert( nrkev
==2 || nrkev
==3);
267 for(i
=0;i
<MAXKEYEVENTS
;i
++) kbuf
[i
]=0;
268 /* two keys involved gives 4 test cases */
270 for(i
=0;i
<nrkev
;i
++) {
271 for(j
=0;j
<nrkev
;j
++) {
273 kbuf
[1] = kevdwn
[1-i
];
275 kbuf
[3] = kevup
[1-j
];
276 do_test( hWnd
, count
++, kbuf
);
280 /* three keys involved gives 36 test cases */
282 for(i
=0;i
<nrkev
;i
++){
283 for(j
=0;j
<nrkev
;j
++){
285 for(k
=0;k
<nrkev
;k
++){
286 if(k
==i
|| k
==j
) continue;
287 for(l
=0;l
<nrkev
;l
++){
288 for(m
=0;m
<nrkev
;m
++){
290 for(n
=0;n
<nrkev
;n
++){
291 if(n
==l
||n
==m
) continue;
298 do_test( hWnd
, count
++, kbuf
);
308 /* test each set specified in the global testkeyset array */
309 static void TestSysKeys( HWND hWnd
)
312 for(i
=0; testkeyset
[i
].nrkev
;i
++)
313 TestASet( hWnd
, testkeyset
[i
].nrkev
, testkeyset
[i
].keydwn
,
314 testkeyset
[i
].keyup
);
317 static LRESULT CALLBACK
WndProc( HWND hWnd
, UINT msg
, WPARAM wParam
,
323 /* window has focus, now do the test */
324 if( hWnd
== hWndTest
) TestSysKeys( hWnd
);
329 PostQuitMessage( 0 );
333 return( DefWindowProcA( hWnd
, msg
, wParam
, lParam
) );
343 HANDLE hInstance
= GetModuleHandleA( NULL
);
345 wclass
.lpszClassName
= "InputSysKeyTestClass";
346 wclass
.style
= CS_HREDRAW
| CS_VREDRAW
;
347 wclass
.lpfnWndProc
= WndProc
;
348 wclass
.hInstance
= hInstance
;
349 wclass
.hIcon
= LoadIconA( 0, (LPSTR
)IDI_APPLICATION
);
350 wclass
.hCursor
= LoadCursorA( NULL
, IDC_ARROW
);
351 wclass
.hbrBackground
= (HBRUSH
)( COLOR_WINDOW
+ 1);
352 wclass
.lpszMenuName
= 0;
353 wclass
.cbClsExtra
= 0;
354 wclass
.cbWndExtra
= 0;
355 assert (RegisterClassA( &wclass
));
356 /* create the test window that will receive the keystrokes */
357 assert ( hWndTest
= CreateWindowA( wclass
.lpszClassName
, "InputSysKeyTest",
358 WS_OVERLAPPEDWINDOW
, CW_USEDEFAULT
, 0, 100, 100,
359 NULL
, NULL
, hInstance
, NULL
) );
360 ShowWindow( hWndTest
, SW_SHOW
);
361 UpdateWindow( hWndTest
);
363 /* flush pending messages */
364 while (PeekMessage( &msg
, 0, 0, 0, PM_REMOVE
)) DispatchMessageA( &msg
);
366 SendMessageA(hWndTest
, WM_USER
, 0, 0);
367 DestroyWindow(hWndTest
);