// SPDX-License-Identifier: LGPL-2.1-or-later #include #include #include #include #define REVIEW_CONFIG (WM_APP + 10) #define REVIEW_CHECK (WM_APP + 11) #define REVIEW_CHECK_RIGHT (WM_APP + 12) #define REVIEW_CHECK_STRICT (WM_APP + 13) #define REVIEW_PRIVACY_CHECK (WM_APP + 14) #define REVIEW_REMOTE_CHECK (WM_APP + 15) static HWND editor, output, password, hidden; static HWND mdi, mdi_active, mdi_background; static WCHAR *large; static WNDPROC original_edit; static BOOL replace_during_read; static BOOL stall_accessible; static BOOL secondary; static unsigned int key_down, key_up; static const WCHAR *expected_text = L"alpha beta\r\nsecond line"; static BOOL protect_during_length, replace_list_during_read; static unsigned int protected_text_reads; static WNDPROC original_list; static WNDPROC original_table; static BOOL replace_table_during_read; static BOOL delay_table_read, freed_table_memory, scroll_during_position; static BOOL frame_pump; static BOOL delay_batch; static unsigned int synthetic_containers; static LRESULT CALLBACK status_proc(HWND window,UINT message,WPARAM wp,LPARAM lp) { // Like MUSHclient's status bar, report a length beyond the first NUL. // The standard MSAA fallback retains that padding in its BSTR name. const WCHAR text[]=L"Ready\0Connected"; if(message==WM_GETTEXTLENGTH)return (sizeof(text)/sizeof(*text))-1; if(message==WM_GETTEXT && wp) { size_t copied=(sizeof(text)/sizeof(*text))-1; if(copied>wp-1)copied=wp-1; memcpy((void*)lp,text,copied*sizeof(*text)); ((WCHAR*)lp)[copied]=0; return copied; } return DefWindowProcW(window,message,wp,lp); } static LRESULT CALLBACK container_proc(HWND window,UINT message,WPARAM wp,LPARAM lp) { if(message==WM_GETOBJECT && lp==OBJID_QUERYCLASSNAMEIDX){++synthetic_containers;Sleep(25);} return DefWindowProcW(window,message,wp,lp); } static LRESULT CALLBACK table_proc(HWND window, UINT message, WPARAM wp, LPARAM lp) { if (message == LVM_GETITEMTEXTW && delay_table_read) { delay_table_read=FALSE; Sleep(650); MEMORY_BASIC_INFORMATION memory={0}; if (!VirtualQuery((void*)lp,&memory,sizeof(memory)) || memory.State != MEM_COMMIT) { freed_table_memory=TRUE; return 0; // Observe the unsafe lifetime without dereferencing freed data. } } LRESULT result=CallWindowProcW(original_table,window,message,wp,lp); if (message == LVM_GETITEMTEXTW && replace_table_during_read) { replace_table_during_read=FALSE; LVITEMW item={0}; item.pszText=L"other & item"; SendMessageW(window,LVM_SETITEMTEXTW,0,(LPARAM)&item); } return result; } static LRESULT CALLBACK list_proc(HWND window, UINT message, WPARAM wp, LPARAM lp) { LRESULT result = CallWindowProcW(original_list,window,message,wp,lp); if (message == LB_GETTEXT && replace_list_during_read) { replace_list_during_read = FALSE; SendMessageW(window,LB_DELETESTRING,0,0); SendMessageW(window,LB_INSERTSTRING,0,(LPARAM)L"update & item"); } return result; } static LRESULT CALLBACK stall_proc(HWND window, UINT message, WPARAM wp, LPARAM lp) { if (message == WM_GETOBJECT && stall_accessible) { stall_accessible = FALSE; Sleep(2000); } return DefWindowProcW(window,message,wp,lp); } static LRESULT CALLBACK edit_proc(HWND window, UINT message, WPARAM wp, LPARAM lp) { if(message==EM_LINEINDEX && delay_batch){delay_batch=FALSE;Sleep(650);} if (message == WM_GETTEXT && (GetWindowLongPtrW(window,GWL_STYLE) & ES_PASSWORD)) ++protected_text_reads; if (message == WM_KEYDOWN) ++key_down; if (message == WM_KEYUP) ++key_up; LRESULT result = CallWindowProcW(original_edit, window, message, wp, lp); if (message == WM_GETTEXTLENGTH && protect_during_length) { protect_during_length = FALSE; SendMessageW(window,EM_SETPASSWORDCHAR,L'*',0); } if (message == WM_GETTEXT && replace_during_read) { replace_during_read = FALSE; SetWindowTextW(window, L"omega zeta\r\nsecond line"); } if (message == EM_POSFROMCHAR && scroll_during_position) { scroll_during_position=FALSE; SendMessageW(window,EM_LINESCROLL,0,1); } return result; } static LRESULT CALLBACK window_proc(HWND window, UINT message, WPARAM wp, LPARAM lp) { (void)lp; if (message == WM_TIMER && secondary) { KillTimer(window,1); SetForegroundWindow(window); SetFocus(GetDlgItem(window,105)); return 0; } if (message == REVIEW_CONFIG) { frame_pump=FALSE; delay_batch=FALSE; synthetic_containers=0; ShowWindow(GetDlgItem(window,112),SW_HIDE); ShowWindow(GetDlgItem(window,114),SW_HIDE); ShowWindow(mdi,SW_HIDE); SendMessageW(editor,EM_SETPASSWORDCHAR,0,0); protect_during_length = replace_list_during_read = FALSE; replace_table_during_read=FALSE; delay_table_read=freed_table_memory=scroll_during_position=FALSE; protected_text_reads = 0; key_down = key_up = 0; expected_text = L"alpha beta\r\nsecond line"; ShowWindow(GetDlgItem(window,106),SW_SHOW); ShowWindow(GetDlgItem(window,107),SW_HIDE); ShowWindow(GetDlgItem(window,109),SW_HIDE); ShowWindow(GetDlgItem(window,110),SW_HIDE); ShowWindow(GetDlgItem(window,111),SW_HIDE); SetWindowTextW(editor, L"alpha beta\r\nsecond line"); SetWindowTextW(output, L"output first\r\noutput second"); SendMessageW(editor, EM_SETREADONLY, FALSE, 0); SendMessageW(editor, EM_SETSEL, 0, 0); MoveWindow(editor, 20, 35, 320, 90, TRUE); switch (wp) { case 33: ShowWindow(GetDlgItem(window,114),SW_SHOW); break; case 32: { WCHAR text[16384]; unsigned used = 0; for (unsigned row = 0; row < 400; ++row) { const WCHAR line[] = L"deep clipped visible line\r\n"; for (unsigned j = 0; j < (sizeof(line)/sizeof(*line))-1; ++j) text[used++] = line[j]; } text[used] = 0; SetWindowTextW(editor, text); MoveWindow(editor, 20, -5000, 320, 5100, TRUE); break; } case 30: ShowWindow(mdi,SW_SHOW); SendMessageW(mdi,WM_MDIACTIVATE,(WPARAM)mdi_active,0); SendMessageW(mdi,WM_MDIMAXIMIZE,(WPARAM)mdi_active,0); break; case 31: ShowWindow(mdi,SW_SHOW); SendMessageW(mdi,WM_MDIRESTORE,(WPARAM)mdi_active,0); SendMessageW(mdi,WM_MDIRESTORE,(WPARAM)mdi_background,0); MoveWindow(mdi_background,0,0,240,300,TRUE); MoveWindow(mdi_active,245,0,240,300,TRUE); SendMessageW(mdi,WM_MDIACTIVATE,(WPARAM)mdi_active,0); break; case 28: { WCHAR text[4096];unsigned used=0; for(unsigned row=0;row<80;++row) { const WCHAR line[]=L"top clipped visible line\r\n"; for(unsigned j=0;j<(sizeof(line)/sizeof(*line))-1;++j)text[used++]=line[j]; } text[used]=0;SetWindowTextW(editor,text); MoveWindow(editor,20,-250,320,380,TRUE); break; } case 29:ShowWindow(GetDlgItem(window,112),SW_SHOW);break; case 27:delay_batch=TRUE;break; case 26: { WCHAR text[4096];unsigned used=0; for(unsigned row=0;row<80;++row) { const WCHAR line[]=L"stable buffered output line\r\n"; for(unsigned j=0;j<(sizeof(line)/sizeof(*line))-1;++j)text[used++]=line[j]; } text[used]=0;SetWindowTextW(editor,text); MoveWindow(editor,20,35,320,380,TRUE); ShowWindow(output,SW_HIDE);ShowWindow(GetDlgItem(window,106),SW_HIDE); frame_pump=TRUE; break; } case 1: // surrogate before first displayed row, absolute offsets SetWindowTextW(editor, L"\U0001f600alpha\r\nbeta\r\nlast"); SendMessageW(editor, EM_LINESCROLL, 0, 1); break; case 2: SetWindowTextW(editor, L"wrapped words repeated wrapped words repeated wrapped words repeated wrapped words\r\n\r\nlast"); MoveWindow(editor, 20, 35, 150, 150, TRUE); break; case 3: SetWindowTextW(editor, L""); break; case 4: SetForegroundWindow(window); SetFocus(password); return 1; case 5: MoveWindow(editor, 500, 35, 320, 90, TRUE); break; case 6: expected_text = L"omega zeta\r\nsecond line"; SetWindowTextW(editor, expected_text); break; case 7: large = malloc(1000002 * sizeof(WCHAR)); if (!large) ExitProcess(3); // Avoid quadratic native wrapping work in the fixture itself. for (int i = 0; i < 1000001; ++i) large[i] = i % 32 == 31 ? L'\n' : L'x'; large[1000001] = 0; SendMessageW(editor, EM_LIMITTEXT, 2000000, 0); SetWindowTextW(editor, large); free(large); large = NULL; break; case 8: SetWindowPos(window, NULL, -20, -20, 0, 0, SWP_NOSIZE | SWP_NOZORDER); break; case 9: replace_during_read = TRUE; break; case 10: stall_accessible = TRUE; break; case 11: SetForegroundWindow(window); SetFocus(output); return 1; case 12: ShowWindow(GetDlgItem(window,106),SW_HIDE); break; case 14: protect_during_length = TRUE; break; case 15: replace_list_during_read = TRUE; break; case 17: ShowWindow(GetDlgItem(window,107),SW_SHOW); break; case 18: ShowWindow(GetDlgItem(window,107),SW_SHOW); replace_table_during_read=TRUE; break; case 21: ShowWindow(GetDlgItem(window,107),SW_SHOW); delay_table_read=TRUE; break; case 22: ShowWindow(GetDlgItem(window,109),SW_SHOW); SendMessageW(GetDlgItem(window,109),EM_SETSEL,INT_MAX,INT_MAX); SendMessageW(GetDlgItem(window,109),EM_SCROLLCARET,0,0); break; case 23: SetWindowTextW(editor,L"first\r\nsecond\r\nthird\r\nfourth\r\nfifth\r\nsixth\r\nseventh\r\neighth\r\nninth\r\ntenth"); scroll_during_position=TRUE; break; case 24: ShowWindow(GetDlgItem(window,110),SW_SHOW); SendMessageW(GetDlgItem(window,110),EM_SETSEL,INT_MAX,INT_MAX); SendMessageW(GetDlgItem(window,110),EM_SCROLLCARET,0,0); break; case 25: ShowWindow(GetDlgItem(window,111),SW_SHOW); break; } SetForegroundWindow(window); SetFocus(editor); return 1; } if (message == REVIEW_PRIVACY_CHECK) return protected_text_reads == 0; if (message == WM_APP+16) return synthetic_containers==0; if (message == REVIEW_REMOTE_CHECK) return !freed_table_memory; if (message == REVIEW_CHECK || message == REVIEW_CHECK_RIGHT || message == REVIEW_CHECK_STRICT) { DWORD start = 0, end = 0; SendMessageW(editor, EM_GETSEL, (WPARAM)&start, (LPARAM)&end); if (message == REVIEW_CHECK) return start == 0 && end == 0 && GetFocus() == editor; WCHAR text[128], output_text[128]; GetWindowTextW(editor,text,128); GetWindowTextW(output,output_text,128); if (message == REVIEW_CHECK_RIGHT) return start == 1 && end == 1 && key_down == 1 && key_up == 1 && GetFocus() == editor && lstrcmpW(text,expected_text) == 0; return start == 0 && end == 0 && GetFocus() == editor && key_down == 0 && key_up == 0 && lstrcmpW(text,expected_text) == 0 && lstrcmpW(output_text,L"output first\r\noutput second") == 0; } if (message == WM_CLOSE) { DestroyWindow(window); return 0; } if (message == WM_DESTROY) { PostQuitMessage(0); return 0; } return DefWindowProcW(window, message, wp, lp); } int WINAPI WinMain(HINSTANCE instance, HINSTANCE previous, LPSTR args, int show) { (void)previous; (void)show; secondary=strcmp(args,"--secondary")==0; if (strncmp(args,"--configure ",12)==0 || strcmp(args,"--check")==0 || strcmp(args,"--check-strict")==0 || strcmp(args,"--check-right")==0 || strcmp(args,"--check-remote")==0 || strcmp(args,"--close")==0) { HWND target=FindWindowW(L"WineAccessReviewFixture",NULL); if (!target) return 3; if (strcmp(args,"--close")==0) { PostMessageW(target,WM_CLOSE,0,0); return 0; } DWORD_PTR result=0; UINT message=strcmp(args,"--check")==0 ? REVIEW_CHECK : strcmp(args,"--check-strict")==0 ? REVIEW_CHECK_STRICT : strcmp(args,"--check-right")==0 ? REVIEW_CHECK_RIGHT : strcmp(args,"--check-remote")==0 ? REVIEW_REMOTE_CHECK : REVIEW_CONFIG; WPARAM mode=message==REVIEW_CONFIG ? (WPARAM)atoi(args+12) : 0; if (!SendMessageTimeoutW(target,message,mode,0,SMTO_ABORTIFHUNG,3000,&result)) return 4; return result ? 0 : 5; } WNDCLASSW cls = {0}; cls.hInstance = instance; cls.lpfnWndProc = window_proc; cls.lpszClassName = secondary ? L"WineAccessReviewSecondary" : L"WineAccessReviewFixture"; cls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); RegisterClassW(&cls); WNDCLASSW stall=cls; stall.lpszClassName=L"WineReviewStall"; stall.lpfnWndProc=stall_proc; RegisterClassW(&stall); HWND window = CreateWindowW(cls.lpszClassName, secondary ? L"Wine secondary fixture" : L"Wine review fixture", WS_OVERLAPPEDWINDOW, 100, 100, 560, 500, NULL, NULL, instance, NULL); CreateWindowW(L"Static", L"&Command:", WS_CHILD | WS_VISIBLE, 20, 10, 200, 20, window, NULL, instance, NULL); editor = CreateWindowW(L"Edit", L"", WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER | ES_MULTILINE | ES_AUTOVSCROLL | WS_VSCROLL, 20, 35, 320, 90, window, (HMENU)101, instance, NULL); original_edit = (WNDPROC)SetWindowLongPtrW(editor, GWLP_WNDPROC, (LONG_PTR)edit_proc); output = CreateWindowW(L"Edit", L"output first\r\noutput second", WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_READONLY | WS_BORDER, 20, 205, 320, 80, window, (HMENU)102, instance, NULL); password = CreateWindowW(L"Edit", L"private-review-secret", WS_CHILD | WS_VISIBLE | ES_PASSWORD | WS_BORDER, 20, 320, 200, 25, window, (HMENU)103, instance, NULL); hidden = CreateWindowW(L"Static", L"hidden-review-secret", WS_CHILD, 20, 350, 200, 25, window, (HMENU)104, instance, NULL); CreateWindowW(L"Button", L"&Open && close", WS_CHILD | WS_VISIBLE, 20, 390, 160, 25, window, (HMENU)105, instance, NULL); HWND list = CreateWindowW(L"ListBox", L"", WS_CHILD | WS_VISIBLE | LBS_NOTIFY, 350, 35, 150, 70, window, (HMENU)106, instance, NULL); original_list = (WNDPROC)SetWindowLongPtrW(list,GWLP_WNDPROC,(LONG_PTR)list_proc); SendMessageW(list, LB_ADDSTRING, 0, (LPARAM)L"native & item"); SendMessageW(list, LB_ADDSTRING, 0, (LPARAM)L"another item"); INITCOMMONCONTROLSEX controls = {sizeof(controls), ICC_LISTVIEW_CLASSES}; InitCommonControlsEx(&controls); HWND table = CreateWindowW(WC_LISTVIEWW,L"",WS_CHILD|LVS_REPORT, 350,200,170,100,window,(HMENU)107,instance,NULL); LVCOLUMNW column = {0}; column.mask=LVCF_TEXT|LVCF_WIDTH; column.cx=100; column.pszText=L"Plugin"; SendMessageW(table,LVM_INSERTCOLUMNW,0,(LPARAM)&column); column.cx=60; column.pszText=L"State"; SendMessageW(table,LVM_INSERTCOLUMNW,1,(LPARAM)&column); LVITEMW item={0}; item.mask=LVIF_TEXT; item.pszText=L"table & item"; SendMessageW(table,LVM_INSERTITEMW,0,(LPARAM)&item); item.iSubItem=1; item.pszText=L"ready"; SendMessageW(table,LVM_SETITEMTEXTW,0,(LPARAM)&item); original_table=(WNDPROC)SetWindowLongPtrW(table,GWLP_WNDPROC,(LONG_PTR)table_proc); CreateWindowW(L"Edit",L"abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz visible suffix", WS_CHILD|ES_AUTOHSCROLL|WS_BORDER,20,130,150,25,window,(HMENU)109,instance,NULL); CreateWindowW(L"Edit",L"abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz visible suffix", WS_CHILD|ES_MULTILINE|ES_AUTOHSCROLL|WS_HSCROLL|WS_BORDER,20,165,150,50,window,(HMENU)110,instance,NULL); CreateWindowW(L"Static",L"literal & label",WS_CHILD|SS_NOPREFIX, 20,365,200,20,window,(HMENU)111,instance,NULL); CreateWindowW(stall.lpszClassName,L"",WS_CHILD|WS_VISIBLE,350,150,150,20,window,NULL,instance,NULL); WNDCLASSW container={0};container.lpfnWndProc=container_proc;container.hInstance=instance;container.lpszClassName=L"WineAccessNativeContainer"; RegisterClassW(&container); HWND parent=CreateWindowW(container.lpszClassName,L"",WS_CHILD,20,35,320,100,window,(HMENU)112,instance,NULL); for(unsigned level=0;level<8;++level)parent=CreateWindowW(container.lpszClassName,L"",WS_CHILD|WS_VISIBLE,0,0,320,100,parent,NULL,instance,NULL); CreateWindowW(L"Edit",L"nested output ready",WS_CHILD|WS_VISIBLE|ES_READONLY,0,0,320,80,parent,NULL,instance,NULL); WNDCLASSW mdi_child={0};mdi_child.lpfnWndProc=DefMDIChildProcW;mdi_child.hInstance=instance;mdi_child.lpszClassName=L"WineAccessMdiChild"; RegisterClassW(&mdi_child); CLIENTCREATESTRUCT client_create={NULL,200}; mdi=CreateWindowW(L"MDIClient",L"",WS_CHILD|WS_CLIPCHILDREN,20,35,500,350,window,(HMENU)113,instance,&client_create); mdi_background=CreateMDIWindowW(mdi_child.lpszClassName,L"Background",WS_OVERLAPPEDWINDOW|WS_VISIBLE,0,0,240,300,mdi,instance,0); CreateWindowW(L"Edit",L"covered mdi text",WS_CHILD|WS_VISIBLE|ES_READONLY,0,0,220,100,mdi_background,NULL,instance,NULL); mdi_active=CreateMDIWindowW(mdi_child.lpszClassName,L"Active",WS_OVERLAPPEDWINDOW|WS_VISIBLE,245,0,240,300,mdi,instance,0); CreateWindowW(L"Edit",L"active mdi text",WS_CHILD|WS_VISIBLE|ES_READONLY,0,0,220,100,mdi_active,NULL,instance,NULL); WNDCLASSW status=cls;status.lpszClassName=L"WineAccessStatusFixture";status.lpfnWndProc=status_proc; RegisterClassW(&status); CreateWindowW(status.lpszClassName,L"",WS_CHILD,20,420,480,20,window,(HMENU)114,instance,NULL); ShowWindow(window, SW_SHOW); SendMessageW(window, REVIEW_CONFIG, 0, 0); if (secondary) SetTimer(window,1,400,NULL); MSG message; for (;;) { if (frame_pump) { if (!PeekMessageW(&message,NULL,0,0,PM_REMOVE)) { Sleep(16);continue; } if (message.message==WM_QUIT)break; } else if (GetMessageW(&message,NULL,0,0)<=0)break; TranslateMessage(&message); DispatchMessageW(&message); } return 0; }