1 /* Unit test suite for Button control.
2  *
3  * Copyright 1999 Ove Kaaven
4  * Copyright 2003 Dimitrie O. Paun
5  * Copyright 2004, 2005 Dmitry Timoshkov
6  * Copyright 2014 Nikolay Sivov for CodeWeavers
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22 
23 #undef USE_WINE_TODOS
24 
25 #include "wine/test.h"
26 
27 #include <assert.h>
28 #include <wingdi.h>
29 #include <winuser.h>
30 #include <commctrl.h>
31 
32 #include "v6util.h"
33 #include "msg.h"
34 
35 #define IS_WNDPROC_HANDLE(x) (((ULONG_PTR)(x) >> 16) == (~0u >> 16))
36 
37 static BOOL (WINAPI *pSetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR);
38 static BOOL (WINAPI *pRemoveWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR);
39 static LRESULT (WINAPI *pDefSubclassProc)(HWND, UINT, WPARAM, LPARAM);
40 
41 /****************** button message test *************************/
42 #define ID_BUTTON 0x000e
43 
44 #define COMBINED_SEQ_INDEX 0
45 #define NUM_MSG_SEQUENCES  1
46 
47 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
48 
49 struct wndclass_redirect_data
50 {
51     ULONG size;
52     DWORD res;
53     ULONG name_len;
54     ULONG name_offset;
55     ULONG module_len;
56     ULONG module_offset;
57 };
58 
59 /* returned pointer is valid as long as activation context is alive */
60 static WCHAR* get_versioned_classname(const WCHAR *name)
61 {
62     BOOL (WINAPI *pFindActCtxSectionStringW)(DWORD,const GUID *,ULONG,LPCWSTR,PACTCTX_SECTION_KEYED_DATA);
63     struct wndclass_redirect_data *wnddata;
64     ACTCTX_SECTION_KEYED_DATA data;
65     BOOL ret;
66 
67     pFindActCtxSectionStringW = (void*)GetProcAddress(GetModuleHandleA("kernel32"), "FindActCtxSectionStringW");
68 
69     memset(&data, 0, sizeof(data));
70     data.cbSize = sizeof(data);
71 
72     ret = pFindActCtxSectionStringW(0, NULL,
73                                     ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION,
74                                     name, &data);
75     ok(ret, "got %d, error %u\n", ret, GetLastError());
76     wnddata = (struct wndclass_redirect_data*)data.lpData;
77     return (WCHAR*)((BYTE*)wnddata + wnddata->name_offset);
78 }
79 
80 static void init_functions(void)
81 {
82     HMODULE hmod = GetModuleHandleA("comctl32.dll");
83     ok(hmod != NULL, "got %p\n", hmod);
84 
85 #define MAKEFUNC_ORD(f, ord) (p##f = (void*)GetProcAddress(hmod, (LPSTR)(ord)))
86     MAKEFUNC_ORD(SetWindowSubclass, 410);
87     MAKEFUNC_ORD(RemoveWindowSubclass, 412);
88     MAKEFUNC_ORD(DefSubclassProc, 413);
89 #undef MAKEFUNC_ORD
90 }
91 
92 /* try to make sure pending X events have been processed before continuing */
93 static void flush_events(void)
94 {
95     MSG msg;
96     int diff = 200;
97     int min_timeout = 100;
98     DWORD time = GetTickCount() + diff;
99 
100     while (diff > 0)
101     {
102         if (MsgWaitForMultipleObjects( 0, NULL, FALSE, min_timeout, QS_ALLINPUT ) == WAIT_TIMEOUT) break;
103         while (PeekMessageA( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessageA( &msg );
104         diff = time - GetTickCount();
105     }
106 }
107 
108 static BOOL ignore_message( UINT message )
109 {
110     /* these are always ignored */
111     return (message >= 0xc000 ||
112             message == WM_GETICON ||
113             message == WM_GETOBJECT ||
114             message == WM_TIMECHANGE ||
115             message == WM_DISPLAYCHANGE ||
116             message == WM_DEVICECHANGE ||
117             message == WM_DWMNCRENDERINGCHANGED ||
118             message == WM_GETTEXTLENGTH ||
119             message == WM_GETTEXT);
120 }
121 
122 static LRESULT CALLBACK button_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR id, DWORD_PTR ref_data)
123 {
124     static LONG defwndproc_counter = 0;
125     struct message msg = { 0 };
126     LRESULT ret;
127 
128     if (ignore_message( message )) return pDefSubclassProc(hwnd, message, wParam, lParam);
129 
130     switch (message)
131     {
132     case WM_SYNCPAINT:
133         break;
134     case BM_SETSTATE:
135         if (GetCapture())
136             ok(GetCapture() == hwnd, "GetCapture() = %p\n", GetCapture());
137         /* fall through */
138     default:
139         msg.message = message;
140         msg.flags = sent|wparam|lparam;
141         if (defwndproc_counter) msg.flags |= defwinproc;
142         msg.wParam = wParam;
143         msg.lParam = lParam;
144         add_message(sequences, COMBINED_SEQ_INDEX, &msg);
145     }
146 
147     if (message == WM_NCDESTROY)
148         pRemoveWindowSubclass(hwnd, button_subclass_proc, 0);
149 
150     defwndproc_counter++;
151     ret = pDefSubclassProc(hwnd, message, wParam, lParam);
152     defwndproc_counter--;
153 
154     return ret;
155 }
156 
157 static LRESULT WINAPI test_parent_wndproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
158 {
159     static LONG defwndproc_counter = 0;
160     static LONG beginpaint_counter = 0;
161     struct message msg = { 0 };
162     LRESULT ret;
163 
164     if (ignore_message( message )) return 0;
165 
166     if (message == WM_PARENTNOTIFY || message == WM_CANCELMODE ||
167         message == WM_SETFOCUS || message == WM_KILLFOCUS ||
168         message == WM_ENABLE || message == WM_ENTERIDLE ||
169         message == WM_DRAWITEM || message == WM_COMMAND ||
170         message == WM_IME_SETCONTEXT)
171     {
172         msg.message = message;
173         msg.flags = sent|parent|wparam|lparam;
174         if (defwndproc_counter) msg.flags |= defwinproc;
175         if (beginpaint_counter) msg.flags |= beginpaint;
176         msg.wParam = wParam;
177         msg.lParam = lParam;
178         add_message(sequences, COMBINED_SEQ_INDEX, &msg);
179     }
180 
181     if (message == WM_PAINT)
182     {
183         PAINTSTRUCT ps;
184         beginpaint_counter++;
185         BeginPaint( hwnd, &ps );
186         beginpaint_counter--;
187         EndPaint( hwnd, &ps );
188         return 0;
189     }
190 
191     defwndproc_counter++;
192     ret = DefWindowProcA(hwnd, message, wParam, lParam);
193     defwndproc_counter--;
194 
195     return ret;
196 }
197 
198 static const struct message setfocus_seq[] =
199 {
200     { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
201     { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 2 },
202     { BM_GETSTATE, sent|optional }, /* when touchscreen is present */
203     { WM_SETFOCUS, sent|wparam },
204     { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_SETFOCUS) },
205     { WM_APP, sent|wparam|lparam },
206     { WM_PAINT, sent },
207     { 0 }
208 };
209 
210 static const struct message killfocus_seq[] =
211 {
212     { WM_KILLFOCUS, sent|wparam, 0 },
213     { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_KILLFOCUS) },
214     { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
215     { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 1 },
216     { WM_APP, sent|wparam|lparam, 0, 0 },
217     { WM_PAINT, sent },
218     { 0 }
219 };
220 
221 static const struct message setfocus_static_seq[] =
222 {
223     { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
224     { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 2 },
225     { BM_GETSTATE, sent|optional }, /* when touchscreen is present */
226     { WM_SETFOCUS, sent|wparam, 0 },
227     { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_SETFOCUS) },
228     { WM_COMMAND, sent|wparam|parent|optional, MAKEWPARAM(ID_BUTTON, BN_CLICKED) }, /* radio button */
229     { WM_APP, sent|wparam|lparam, 0, 0 },
230     { WM_PAINT, sent },
231     { 0 }
232 };
233 
234 static const struct message setfocus_groupbox_seq[] =
235 {
236     { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
237     { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 2 },
238     { BM_GETSTATE, sent|optional }, /* when touchscreen is present */
239     { WM_SETFOCUS, sent|wparam, 0 },
240     { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_SETFOCUS) },
241     { WM_COMMAND, sent|wparam|parent|optional, MAKEWPARAM(ID_BUTTON, BN_CLICKED) }, /* radio button */
242     { WM_APP, sent|wparam|lparam, 0, 0 },
243     { WM_PAINT, sent },
244     { 0 }
245 };
246 
247 static const struct message killfocus_static_seq[] =
248 {
249     { WM_KILLFOCUS, sent|wparam, 0 },
250     { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_KILLFOCUS) },
251     { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
252     { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 1 },
253     { WM_APP, sent|wparam|lparam, 0, 0 },
254     { WM_PAINT, sent },
255     { 0 }
256 };
257 
258 static const struct message setfocus_ownerdraw_seq[] =
259 {
260     { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
261     { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 2 },
262     { BM_GETSTATE, sent|optional }, /* when touchscreen is present */
263     { WM_SETFOCUS, sent|wparam, 0 },
264     { WM_DRAWITEM, sent|wparam|parent, ID_BUTTON },
265     { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_SETFOCUS) },
266     { WM_APP, sent|wparam|lparam, 0, 0 },
267     { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
268     { 0 }
269 };
270 
271 static const struct message killfocus_ownerdraw_seq[] =
272 {
273     { WM_KILLFOCUS, sent|wparam, 0 },
274     { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_KILLFOCUS) },
275     { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
276     { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 1 },
277     { WM_APP, sent|wparam|lparam, 0, 0 },
278     { WM_PAINT, sent },
279     { WM_DRAWITEM, sent|wparam|parent, ID_BUTTON },
280     { 0 }
281 };
282 
283 static const struct message lbuttondown_seq[] =
284 {
285     { WM_LBUTTONDOWN, sent|wparam|lparam, 0, 0 },
286     { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
287     { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 2 },
288     { BM_GETSTATE, sent|defwinproc|optional }, /* when touchscreen is present */
289     { WM_SETFOCUS, sent|wparam|defwinproc, 0 },
290     { BM_SETSTATE, sent|wparam|defwinproc, TRUE },
291     { 0 }
292 };
293 
294 static const struct message lbuttonup_seq[] =
295 {
296     { WM_LBUTTONUP, sent|wparam|lparam, 0, 0 },
297     { BM_SETSTATE, sent|wparam|defwinproc, FALSE },
298     { WM_CAPTURECHANGED, sent|wparam|defwinproc, 0 },
299     { WM_COMMAND, sent|wparam|defwinproc, 0 },
300     { 0 }
301 };
302 
303 static const struct message setfont_seq[] =
304 {
305     { WM_SETFONT, sent },
306     { 0 }
307 };
308 
309 static const struct message setstyle_seq[] =
310 {
311     { BM_SETSTYLE, sent },
312     { WM_STYLECHANGING, sent|wparam|defwinproc, GWL_STYLE },
313     { WM_STYLECHANGED, sent|wparam|defwinproc, GWL_STYLE },
314     { WM_APP, sent|wparam|lparam, 0, 0 },
315     { WM_PAINT, sent },
316     { WM_NCPAINT, sent|defwinproc|optional }, /* FIXME: Wine sends it */
317     { WM_ERASEBKGND, sent|defwinproc|optional },
318     { WM_PAINT, sent|optional },
319     { 0 }
320 };
321 
322 static const struct message setstyle_static_seq[] =
323 {
324     { BM_SETSTYLE, sent },
325     { WM_STYLECHANGING, sent|wparam|defwinproc, GWL_STYLE },
326     { WM_STYLECHANGED, sent|wparam|defwinproc, GWL_STYLE },
327     { WM_APP, sent|wparam|lparam, 0, 0 },
328     { WM_PAINT, sent },
329     { WM_NCPAINT, sent|defwinproc|optional }, /* FIXME: Wine sends it */
330     { WM_ERASEBKGND, sent|defwinproc|optional },
331     { 0 }
332 };
333 
334 static const struct message setstyle_user_seq[] =
335 {
336     { BM_SETSTYLE, sent },
337     { WM_STYLECHANGING, sent|wparam|defwinproc, GWL_STYLE },
338     { WM_STYLECHANGED, sent|wparam|defwinproc, GWL_STYLE },
339     { WM_APP, sent|wparam|lparam, 0, 0 },
340     { WM_PAINT, sent },
341     { WM_NCPAINT, sent|defwinproc|optional }, /* FIXME: Wine sends it */
342     { WM_ERASEBKGND, sent|defwinproc|optional },
343     { 0 }
344 };
345 
346 static const struct message setstyle_ownerdraw_seq[] =
347 {
348     { BM_SETSTYLE, sent },
349     { WM_STYLECHANGING, sent|wparam|defwinproc, GWL_STYLE },
350     { WM_STYLECHANGED, sent|wparam|defwinproc, GWL_STYLE },
351     { WM_APP, sent|wparam|lparam, 0, 0 },
352     { WM_PAINT, sent },
353     { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
354     { WM_ERASEBKGND, sent|defwinproc|optional },
355     { WM_DRAWITEM, sent|wparam|parent, ID_BUTTON },
356     { 0 }
357 };
358 
359 static const struct message setstate_seq[] =
360 {
361     { BM_SETSTATE, sent },
362     { WM_APP, sent|wparam|lparam, 0, 0 },
363     { WM_PAINT, sent },
364     { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
365     { WM_ERASEBKGND, sent|defwinproc|optional },
366     { WM_PAINT, sent|optional },
367     { 0 }
368 };
369 
370 static const struct message setstate_static_seq[] =
371 {
372     { BM_SETSTATE, sent },
373     { WM_APP, sent|wparam|lparam, 0, 0 },
374     { WM_PAINT, sent },
375     { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
376     { WM_ERASEBKGND, sent|defwinproc|optional },
377     { 0 }
378 };
379 
380 static const struct message setstate_user_seq[] =
381 {
382     { BM_SETSTATE, sent },
383     { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_HILITE) },
384     { WM_APP, sent|wparam|lparam, 0, 0 },
385     { WM_PAINT, sent },
386     { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
387     { WM_ERASEBKGND, sent|defwinproc|optional },
388     { 0 }
389 };
390 
391 static const struct message setstate_ownerdraw_seq[] =
392 {
393     { BM_SETSTATE, sent },
394     { WM_APP, sent|wparam|lparam, 0, 0 },
395     { WM_PAINT, sent },
396     { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
397     { WM_ERASEBKGND, sent|defwinproc|optional },
398     { WM_DRAWITEM, sent|wparam|parent, ID_BUTTON },
399     { 0 }
400 };
401 
402 static const struct message clearstate_seq[] =
403 {
404     { BM_SETSTATE, sent },
405     { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_UNHILITE) },
406     { WM_APP, sent|wparam|lparam, 0, 0 },
407     { WM_PAINT, sent },
408     { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
409     { WM_ERASEBKGND, sent|defwinproc|optional },
410     { 0 }
411 };
412 
413 static const struct message clearstate_ownerdraw_seq[] =
414 {
415     { BM_SETSTATE, sent },
416     { WM_APP, sent|wparam|lparam, 0, 0 },
417     { WM_PAINT, sent },
418     { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
419     { WM_ERASEBKGND, sent|defwinproc|optional },
420     { WM_DRAWITEM, sent|wparam|parent, ID_BUTTON },
421     { 0 }
422 };
423 
424 static const struct message setcheck_ignored_seq[] =
425 {
426     { BM_SETCHECK, sent },
427     { WM_APP, sent|wparam|lparam, 0, 0 },
428     { WM_PAINT, sent|optional },
429     { 0 }
430 };
431 
432 static const struct message setcheck_static_seq[] =
433 {
434     { BM_SETCHECK, sent },
435     { WM_APP, sent|wparam|lparam, 0, 0 },
436     { WM_PAINT, sent },
437     { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
438     { WM_ERASEBKGND, sent|defwinproc|optional },
439     { 0 }
440 };
441 
442 static const struct message setcheck_radio_seq[] =
443 {
444     { BM_SETCHECK, sent },
445     { WM_STYLECHANGING, sent|wparam|defwinproc, GWL_STYLE },
446     { WM_STYLECHANGED, sent|wparam|defwinproc, GWL_STYLE },
447     { WM_APP, sent|wparam|lparam, 0, 0 },
448     { 0 }
449 };
450 
451 static const struct message setcheck_radio_redraw_seq[] =
452 {
453     { BM_SETCHECK, sent },
454     { WM_STYLECHANGING, sent|wparam|defwinproc, GWL_STYLE },
455     { WM_STYLECHANGED, sent|wparam|defwinproc, GWL_STYLE },
456     { WM_APP, sent|wparam|lparam, 0, 0 },
457     { WM_PAINT, sent },
458     { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
459     { WM_ERASEBKGND, sent|defwinproc|optional },
460     { 0 }
461 };
462 
463 static HWND create_button(DWORD style, HWND parent)
464 {
465     HMENU menuid = 0;
466     HWND hwnd;
467 
468     if (parent)
469     {
470         style |= WS_CHILD|BS_NOTIFY;
471         menuid = (HMENU)ID_BUTTON;
472     }
473     hwnd = CreateWindowExA(0, "Button", "test", style, 0, 0, 50, 14, parent, menuid, 0, NULL);
474     ok(hwnd != NULL, "failed to create a button, 0x%08x, %p\n", style, parent);
475     pSetWindowSubclass(hwnd, button_subclass_proc, 0, 0);
476     return hwnd;
477 }
478 
479 static void test_button_messages(void)
480 {
481     static const struct
482     {
483         DWORD style;
484         DWORD dlg_code;
485         const struct message *setfocus;
486         const struct message *killfocus;
487         const struct message *setstyle;
488         const struct message *setstate;
489         const struct message *clearstate;
490         const struct message *setcheck;
491     } button[] = {
492         { BS_PUSHBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON,
493           setfocus_seq, killfocus_seq, setstyle_seq,
494           setstate_seq, setstate_seq, setcheck_ignored_seq },
495         { BS_DEFPUSHBUTTON, DLGC_BUTTON | DLGC_DEFPUSHBUTTON,
496           setfocus_seq, killfocus_seq, setstyle_seq,
497           setstate_seq, setstate_seq, setcheck_ignored_seq },
498         { BS_CHECKBOX, DLGC_BUTTON,
499           setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
500           setstate_static_seq, setstate_static_seq, setcheck_static_seq },
501         { BS_AUTOCHECKBOX, DLGC_BUTTON,
502           setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
503           setstate_static_seq, setstate_static_seq, setcheck_static_seq },
504         { BS_RADIOBUTTON, DLGC_BUTTON | DLGC_RADIOBUTTON,
505           setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
506           setstate_static_seq, setstate_static_seq, setcheck_radio_redraw_seq },
507         { BS_3STATE, DLGC_BUTTON,
508           setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
509           setstate_static_seq, setstate_static_seq, setcheck_static_seq },
510         { BS_AUTO3STATE, DLGC_BUTTON,
511           setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
512           setstate_static_seq, setstate_static_seq, setcheck_static_seq },
513         { BS_GROUPBOX, DLGC_STATIC,
514           setfocus_groupbox_seq, killfocus_static_seq, setstyle_static_seq,
515           setstate_static_seq, setstate_static_seq, setcheck_ignored_seq },
516         { BS_USERBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON,
517           setfocus_seq, killfocus_seq, setstyle_user_seq,
518           setstate_user_seq, clearstate_seq, setcheck_ignored_seq },
519         { BS_AUTORADIOBUTTON, DLGC_BUTTON | DLGC_RADIOBUTTON,
520           setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
521           setstate_static_seq, setstate_static_seq, setcheck_radio_redraw_seq },
522         { BS_OWNERDRAW, DLGC_BUTTON,
523           setfocus_ownerdraw_seq, killfocus_ownerdraw_seq, setstyle_ownerdraw_seq,
524           setstate_ownerdraw_seq, clearstate_ownerdraw_seq, setcheck_ignored_seq },
525     };
526     const struct message *seq;
527     unsigned int i;
528     HWND hwnd, parent;
529     DWORD dlg_code;
530     HFONT zfont;
531     BOOL todo;
532 
533     /* selection with VK_SPACE should capture button window */
534     hwnd = create_button(BS_CHECKBOX | WS_VISIBLE | WS_POPUP, NULL);
535     ok(hwnd != 0, "Failed to create button window\n");
536     ReleaseCapture();
537     SetFocus(hwnd);
538     SendMessageA(hwnd, WM_KEYDOWN, VK_SPACE, 0);
539     ok(GetCapture() == hwnd, "Should be captured on VK_SPACE WM_KEYDOWN\n");
540     SendMessageA(hwnd, WM_KEYUP, VK_SPACE, 0);
541     DestroyWindow(hwnd);
542 
543     parent = CreateWindowExA(0, "TestParentClass", "Test parent", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
544                              100, 100, 200, 200, 0, 0, 0, NULL);
545     ok(parent != 0, "Failed to create parent window\n");
546 
547     for (i = 0; i < sizeof(button)/sizeof(button[0]); i++)
548     {
549         MSG msg;
550         DWORD style, state;
551 
552         trace("%d: button test sequence\n", i);
553         hwnd = create_button(button[i].style, parent);
554 
555         style = GetWindowLongA(hwnd, GWL_STYLE);
556         style &= ~(WS_CHILD | BS_NOTIFY);
557         /* XP turns a BS_USERBUTTON into BS_PUSHBUTTON */
558         if (button[i].style == BS_USERBUTTON)
559             ok(style == BS_PUSHBUTTON, "expected style BS_PUSHBUTTON got %x\n", style);
560         else
561             ok(style == button[i].style, "expected style %x got %x\n", button[i].style, style);
562 
563         dlg_code = SendMessageA(hwnd, WM_GETDLGCODE, 0, 0);
564         ok(dlg_code == button[i].dlg_code, "%u: wrong dlg_code %08x\n", i, dlg_code);
565 
566         ShowWindow(hwnd, SW_SHOW);
567         UpdateWindow(hwnd);
568         SetFocus(0);
569         flush_events();
570         SetFocus(0);
571         flush_sequences(sequences, NUM_MSG_SEQUENCES);
572 
573         todo = button[i].style != BS_OWNERDRAW;
574         ok(GetFocus() == 0, "expected focus 0, got %p\n", GetFocus());
575         SetFocus(hwnd);
576         SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
577         while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
578         ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setfocus, "SetFocus(hwnd) on a button", todo);
579 
580         todo = button[i].style == BS_OWNERDRAW;
581         SetFocus(0);
582         SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
583         while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
584         ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].killfocus, "SetFocus(0) on a button", todo);
585         ok(GetFocus() == 0, "expected focus 0, got %p\n", GetFocus());
586 
587         SendMessageA(hwnd, BM_SETSTYLE, button[i].style | BS_BOTTOM, TRUE);
588         SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
589         while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
590         ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setstyle, "BM_SETSTYLE on a button", TRUE);
591 
592         style = GetWindowLongA(hwnd, GWL_STYLE);
593         style &= ~(WS_VISIBLE | WS_CHILD | BS_NOTIFY);
594         /* XP doesn't turn a BS_USERBUTTON into BS_PUSHBUTTON here! */
595         ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
596 
597         state = SendMessageA(hwnd, BM_GETSTATE, 0, 0);
598         ok(state == 0, "expected state 0, got %04x\n", state);
599 
600         flush_sequences(sequences, NUM_MSG_SEQUENCES);
601 
602         SendMessageA(hwnd, BM_SETSTATE, TRUE, 0);
603         SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
604         while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
605         ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setstate, "BM_SETSTATE/TRUE on a button", TRUE);
606 
607         state = SendMessageA(hwnd, BM_GETSTATE, 0, 0);
608         ok(state == BST_PUSHED, "expected state 0x0004, got %04x\n", state);
609 
610         style = GetWindowLongA(hwnd, GWL_STYLE);
611         style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE);
612         ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
613 
614         flush_sequences(sequences, NUM_MSG_SEQUENCES);
615 
616         SendMessageA(hwnd, BM_SETSTATE, FALSE, 0);
617         SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
618         while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
619         ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].clearstate, "BM_SETSTATE/FALSE on a button", TRUE);
620 
621         state = SendMessageA(hwnd, BM_GETSTATE, 0, 0);
622         ok(state == 0, "expected state 0, got %04x\n", state);
623 
624         style = GetWindowLongA(hwnd, GWL_STYLE);
625         style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE);
626         ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
627 
628         state = SendMessageA(hwnd, BM_GETCHECK, 0, 0);
629         ok(state == BST_UNCHECKED, "expected BST_UNCHECKED, got %04x\n", state);
630 
631         flush_sequences(sequences, NUM_MSG_SEQUENCES);
632 
633         if (button[i].style == BS_RADIOBUTTON ||
634             button[i].style == BS_AUTORADIOBUTTON)
635         {
636             seq = setcheck_radio_seq;
637             todo = TRUE;
638         }
639         else
640         {
641             seq = setcheck_ignored_seq;
642             todo = FALSE;
643         }
644         SendMessageA(hwnd, BM_SETCHECK, BST_UNCHECKED, 0);
645         SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
646         while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
647         ok_sequence(sequences, COMBINED_SEQ_INDEX, seq, "BM_SETCHECK on a button", todo);
648 
649         state = SendMessageA(hwnd, BM_GETCHECK, 0, 0);
650         ok(state == BST_UNCHECKED, "expected BST_UNCHECKED, got %04x\n", state);
651 
652         style = GetWindowLongA(hwnd, GWL_STYLE);
653         style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE);
654         ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
655 
656         flush_sequences(sequences, NUM_MSG_SEQUENCES);
657 
658         SendMessageA(hwnd, BM_SETCHECK, BST_CHECKED, 0);
659         SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
660         while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
661 
662         if (button[i].style == BS_PUSHBUTTON ||
663             button[i].style == BS_DEFPUSHBUTTON ||
664             button[i].style == BS_GROUPBOX ||
665             button[i].style == BS_USERBUTTON ||
666             button[i].style == BS_OWNERDRAW)
667         {
668             ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setcheck, "BM_SETCHECK on a button", FALSE);
669             state = SendMessageA(hwnd, BM_GETCHECK, 0, 0);
670             ok(state == BST_UNCHECKED, "expected check BST_UNCHECKED, got %04x\n", state);
671         }
672         else
673         {
674             ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setcheck, "BM_SETCHECK on a button", TRUE);
675             state = SendMessageA(hwnd, BM_GETCHECK, 0, 0);
676             ok(state == BST_CHECKED, "expected check BST_CHECKED, got %04x\n", state);
677         }
678 
679         style = GetWindowLongA(hwnd, GWL_STYLE);
680         style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE);
681         if (button[i].style == BS_RADIOBUTTON ||
682             button[i].style == BS_AUTORADIOBUTTON)
683             ok(style == (button[i].style | WS_TABSTOP), "expected style %04x | WS_TABSTOP got %04x\n", button[i].style, style);
684         else
685             ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
686 
687         DestroyWindow(hwnd);
688     }
689 
690     DestroyWindow(parent);
691 
692     hwnd = create_button(BS_PUSHBUTTON, NULL);
693 
694     SetForegroundWindow(hwnd);
695     flush_events();
696 
697     SetActiveWindow(hwnd);
698     SetFocus(0);
699     flush_sequences(sequences, NUM_MSG_SEQUENCES);
700 
701     SendMessageA(hwnd, WM_LBUTTONDOWN, 0, 0);
702     ok_sequence(sequences, COMBINED_SEQ_INDEX, lbuttondown_seq, "WM_LBUTTONDOWN on a button", FALSE);
703 
704     SendMessageA(hwnd, WM_LBUTTONUP, 0, 0);
705     ok_sequence(sequences, COMBINED_SEQ_INDEX, lbuttonup_seq, "WM_LBUTTONUP on a button", TRUE);
706 
707     flush_sequences(sequences, NUM_MSG_SEQUENCES);
708     zfont = GetStockObject(SYSTEM_FONT);
709     SendMessageA(hwnd, WM_SETFONT, (WPARAM)zfont, TRUE);
710     UpdateWindow(hwnd);
711     ok_sequence(sequences, COMBINED_SEQ_INDEX, setfont_seq, "WM_SETFONT on a button", FALSE);
712 
713     DestroyWindow(hwnd);
714 }
715 
716 static void test_button_class(void)
717 {
718     static const WCHAR testW[] = {'t','e','s','t',0};
719     WNDCLASSEXW exW, ex2W;
720     WNDCLASSEXA exA;
721     char buffA[100];
722     WCHAR *nameW;
723     HWND hwnd;
724     BOOL ret;
725     int len;
726 
727     ret = GetClassInfoExA(NULL, WC_BUTTONA, &exA);
728     ok(ret, "got %d\n", ret);
729 todo_wine
730     ok(IS_WNDPROC_HANDLE(exA.lpfnWndProc), "got %p\n", exA.lpfnWndProc);
731 
732     ret = GetClassInfoExW(NULL, WC_BUTTONW, &exW);
733     ok(ret, "got %d\n", ret);
734     ok(!IS_WNDPROC_HANDLE(exW.lpfnWndProc), "got %p\n", exW.lpfnWndProc);
735 
736     /* check that versioned class is also accessible */
737     nameW = get_versioned_classname(WC_BUTTONW);
738     ok(lstrcmpW(nameW, WC_BUTTONW), "got %s\n", wine_dbgstr_w(nameW));
739 
740     ret = GetClassInfoExW(NULL, nameW, &ex2W);
741 todo_wine
742     ok(ret, "got %d\n", ret);
743 if (ret) /* TODO: remove once Wine is fixed */
744     ok(ex2W.lpfnWndProc == exW.lpfnWndProc, "got %p, %p\n", exW.lpfnWndProc, ex2W.lpfnWndProc);
745 
746     /* Check reported class name */
747     hwnd = create_button(BS_CHECKBOX, NULL);
748     len = GetClassNameA(hwnd, buffA, sizeof(buffA));
749     ok(len == strlen(buffA), "got %d\n", len);
750     ok(!strcmp(buffA, "Button"), "got %s\n", buffA);
751 
752     len = RealGetWindowClassA(hwnd, buffA, sizeof(buffA));
753     ok(len == strlen(buffA), "got %d\n", len);
754     ok(!strcmp(buffA, "Button"), "got %s\n", buffA);
755     DestroyWindow(hwnd);
756 
757     /* explicitly create with versioned class name */
758     hwnd = CreateWindowExW(0, nameW, testW, BS_CHECKBOX, 0, 0, 50, 14, NULL, 0, 0, NULL);
759 todo_wine
760     ok(hwnd != NULL, "failed to create a window %s\n", wine_dbgstr_w(nameW));
761 if (hwnd)
762 {
763     len = GetClassNameA(hwnd, buffA, sizeof(buffA));
764     ok(len == strlen(buffA), "got %d\n", len);
765     ok(!strcmp(buffA, "Button"), "got %s\n", buffA);
766 
767     len = RealGetWindowClassA(hwnd, buffA, sizeof(buffA));
768     ok(len == strlen(buffA), "got %d\n", len);
769     ok(!strcmp(buffA, "Button"), "got %s\n", buffA);
770 
771     DestroyWindow(hwnd);
772 }
773 }
774 
775 static void register_parent_class(void)
776 {
777     WNDCLASSA cls;
778 
779     cls.style = 0;
780     cls.lpfnWndProc = test_parent_wndproc;
781     cls.cbClsExtra = 0;
782     cls.cbWndExtra = 0;
783     cls.hInstance = GetModuleHandleA(0);
784     cls.hIcon = 0;
785     cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
786     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
787     cls.lpszMenuName = NULL;
788     cls.lpszClassName = "TestParentClass";
789     RegisterClassA(&cls);
790 }
791 
792 START_TEST(button)
793 {
794     ULONG_PTR ctx_cookie;
795     HANDLE hCtx;
796 
797     if (!load_v6_module(&ctx_cookie, &hCtx))
798         return;
799 
800     register_parent_class();
801 
802     init_functions();
803     init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
804 
805     test_button_class();
806     test_button_messages();
807 
808     unload_v6_module(ctx_cookie, hCtx);
809 }
810