// SPDX-License-Identifier: LGPL-2.1-or-later // Drive Wine's actual Notepad; the standard-control fixture covers edge cases. #include #include static DWORD notepad_pid; static HWND notepad; static HWND saveDialog; static BOOL CALLBACK find_save_dialog(HWND window, LPARAM unused) { (void)unused; DWORD pid; GetWindowThreadProcessId(window, &pid); if (pid == notepad_pid && IsWindowVisible(window) && GetDlgItem(window, IDYES) && GetDlgItem(window, IDNO) && GetDlgItem(window, IDCANCEL)) { saveDialog = window; return FALSE; } return TRUE; } static BOOL CALLBACK find_notepad(HWND window, LPARAM unused) { (void)unused; DWORD pid; GetWindowThreadProcessId(window, &pid); if (pid == notepad_pid && IsWindowVisible(window) && FindWindowExW(window, NULL, L"Edit", NULL)) { notepad = window; return FALSE; } return TRUE; } static void key(WORD vk, BOOL control, BOOL shift) { INPUT input[6] = {0}; UINT count = 0; if (control) { input[count].ki.wVk = VK_CONTROL; ++count; } if (shift) { input[count].ki.wVk = VK_SHIFT; ++count; } input[count].ki.wVk = vk; ++count; input[count].ki.wVk = vk; input[count].ki.dwFlags = KEYEVENTF_KEYUP; ++count; if (shift) { input[count].ki.wVk = VK_SHIFT; input[count].ki.dwFlags = KEYEVENTF_KEYUP; ++count; } if (control) { input[count].ki.wVk = VK_CONTROL; input[count].ki.dwFlags = KEYEVENTF_KEYUP; ++count; } for (UINT i = 0; i < count; ++i) input[i].type = INPUT_KEYBOARD; if (SendInput(count, input, sizeof(INPUT)) != count) ExitProcess(3); Sleep(200); } int WINAPI WinMain(HINSTANCE instance, HINSTANCE previous, LPSTR args, int show) { (void)instance; (void)previous; (void)args; (void)show; STARTUPINFOW startup = {0}; PROCESS_INFORMATION process = {0}; startup.cb = sizeof(startup); WCHAR command[] = L"notepad.exe"; if (!CreateProcessW(NULL, command, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &process)) return 2; notepad_pid = process.dwProcessId; for (int i = 0; i < 100 && !notepad; ++i) { EnumWindows(find_notepad, 0); Sleep(50); } int result = 2; if (notepad) { HWND edit = FindWindowExW(notepad, NULL, L"Edit", NULL); SetForegroundWindow(notepad); Sleep(400); key('Z', FALSE, FALSE); key('Z', FALSE, FALSE); key(VK_SPACE, FALSE, FALSE); key(VK_RETURN, FALSE, FALSE); WCHAR typed[16] = {0}; SendMessageW(edit, WM_GETTEXT, 16, (LPARAM)typed); if (lstrcmpW(typed, L"zz \r\n")) { fprintf(stderr, "Notepad typing did not reach its edit: units=%d first=%04x foreground=%p expected=%p\n", lstrlenW(typed), (unsigned)typed[0], (void *)GetForegroundWindow(), (void *)notepad); ExitProcess(2); } SendMessageW(edit, WM_SETTEXT, 0, (LPARAM)L"hello beta\r\nNotepad second line"); SendMessageW(edit, EM_SETSEL, 0, 0); Sleep(400); key(VK_RIGHT, FALSE, FALSE); // e key(VK_RIGHT, FALSE, FALSE); // l key(VK_RIGHT, FALSE, FALSE); // l key(VK_RIGHT, TRUE, FALSE); // beta key(VK_DOWN, FALSE, FALSE); // second line key(VK_HOME, FALSE, FALSE); key(VK_RIGHT, FALSE, TRUE); // N selected key(VK_LEFT, FALSE, TRUE); // N unselected key(VK_DELETE, FALSE, FALSE);// N deleted key(VK_MENU, FALSE, FALSE); key(VK_ESCAPE, FALSE, FALSE); key(VK_ESCAPE, FALSE, FALSE); WCHAR text[80] = {0}; SendMessageW(edit, WM_GETTEXT, 80, (LPARAM)text); if (!lstrcmpW(text, L"hello beta\r\notepad second line")) result = 0; else fprintf(stderr, "Notepad final document mismatch: units=%d\n", lstrlenW(text)); PostMessageW(notepad, WM_CLOSE, 0, 0); for (int i = 0; i < 100 && !saveDialog; ++i) { EnumWindows(find_save_dialog, 0); Sleep(50); } if (!saveDialog) { fprintf(stderr, "Notepad save dialog missing\n"); result = 2; } else { SetForegroundWindow(saveDialog); Sleep(400); key(VK_TAB, FALSE, FALSE); // Yes -> No key(VK_TAB, FALSE, FALSE); // No -> Cancel key(VK_TAB, FALSE, TRUE); // Cancel -> No key(VK_ESCAPE, FALSE, FALSE); } // Suppress the save prompt only in this test-owned, unsaved document. SendMessageW(edit, EM_SETMODIFY, FALSE, 0); PostMessageW(notepad, WM_CLOSE, 0, 0); if (WaitForSingleObject(process.hProcess, 3000) != WAIT_OBJECT_0) result = 2; } CloseHandle(process.hThread); CloseHandle(process.hProcess); return result; }