// SPDX-License-Identifier: LGPL-2.1-or-later // Drive an actual Notepad++ instance in the runner's disposable Wine prefix. #include #include "../scintilla_messages.h" static DWORD editorPid; static HWND editor, owner; static BOOL CALLBACK find_editor(HWND window, LPARAM unused) { (void)unused; WCHAR name[64]; if (IsWindowVisible(window) && GetClassNameW(window, name, 64) && !lstrcmpW(name, L"Scintilla")) { editor = window; return FALSE; } return TRUE; } static BOOL CALLBACK find_owner(HWND window, LPARAM unused) { (void)unused; DWORD pid; GetWindowThreadProcessId(window, &pid); if (pid == editorPid && IsWindowVisible(window)) { EnumChildWindows(window, find_editor, 0); if (editor) { owner = 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); } static void move_to(ptrdiff_t position) { SendMessageW(editor, SCI_SETSEL, position, position); Sleep(100); } int WINAPI WinMain(HINSTANCE instance, HINSTANCE previous, LPSTR args, int show) { (void)instance; (void)previous; (void)args; (void)show; WCHAR executable[2048], document[2048], command[4600]; if (!GetEnvironmentVariableW(L"WINE_ACCESS_NOTEPADPP_EXE", executable, 2048) || !GetEnvironmentVariableW(L"WINE_ACCESS_SCINTILLA_DOCUMENT", document, 2048)) return 2; wsprintfW(command, L"\"%s\" -multiInst -nosession -noPlugin \"%s\"", executable, document); STARTUPINFOW startup = {0}; PROCESS_INFORMATION process = {0}; startup.cb = sizeof(startup); if (!CreateProcessW(NULL, command, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &process)) return 2; editorPid = process.dwProcessId; for (int i = 0; i < 100 && !editor; ++i) { EnumWindows(find_owner, 0); Sleep(50); } if (!editor || SendMessageW(editor, SCI_GETLENGTH, 0, 0) < 40) return 2; ptrdiff_t originalLength = SendMessageW(editor, SCI_GETLENGTH, 0, 0); SetForegroundWindow(owner); key(VK_HOME, TRUE, FALSE); WCHAR ready[1024]; if (GetEnvironmentVariableW(L"WINE_ACCESS_FIXTURE_READY", ready, 1024)) { HANDLE file = CreateFileW(ready, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL); if (file == INVALID_HANDLE_VALUE) return 2; CloseHandle(file); } Sleep(1500); 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_LEFT, TRUE, FALSE); // hello plus trailing space key(VK_RIGHT, TRUE, FALSE); key(VK_DOWN, FALSE, FALSE); // Scintilla second line key(VK_UP, FALSE, FALSE); // hello beta key(VK_DOWN, FALSE, FALSE); key(VK_HOME, FALSE, FALSE); key(VK_RIGHT, FALSE, TRUE); // S selected key(VK_RIGHT, FALSE, TRUE); // c selected key(VK_LEFT, FALSE, TRUE); // c unselected key(VK_LEFT, FALSE, FALSE); // S unselected key(VK_DELETE, FALSE, FALSE);// S deleted key(VK_RIGHT, FALSE, FALSE); key(VK_BACK, FALSE, FALSE); // c (without deleted suffix) key(VK_MENU, FALSE, FALSE); key(VK_ESCAPE, FALSE, FALSE); key(VK_ESCAPE, FALSE, FALSE);// return at "intilla second line" key('Z', TRUE, FALSE); // undo c key('Z', TRUE, FALSE); // undo S key(VK_HOME, FALSE, FALSE); key(VK_RIGHT, FALSE, FALSE);// c from restored document SendMessageW(editor, SCI_SETSEL, 12, 21); // Scintilla selected Sleep(100); key('X', TRUE, FALSE); // Scintilla deleted key('Z', TRUE, FALSE); key(VK_HOME, FALSE, FALSE); key(VK_DOWN, FALSE, FALSE); // café 😀 tail ptrdiff_t lineStart = SendMessageW(editor, SCI_POSITIONFROMLINE, 2, 0); move_to(lineStart + 2); key(VK_RIGHT, FALSE, FALSE);// é (two UTF-8 bytes) move_to(lineStart + 6); key(VK_LEFT, FALSE, FALSE); // space key(VK_RIGHT, FALSE, FALSE);// 😀 (four UTF-8 bytes) key(VK_RIGHT, FALSE, TRUE); // 😀 selected key(VK_LEFT, FALSE, TRUE); // 😀 unselected key(VK_DELETE, FALSE, FALSE);// 😀 deleted key('Z', TRUE, FALSE); move_to(lineStart + 10); key(VK_BACK, FALSE, FALSE); // 😀 key('Z', TRUE, FALSE); SendMessageW(editor, 2171 /* SCI_SETREADONLY */, TRUE, 0); SendMessageW(editor, RegisterWindowMessageW(L"CthulhuWineEditFocus"), 0, 0); key(VK_DELETE, FALSE, FALSE);// read-only: must leave document unchanged if (SendMessageW(editor, SCI_GETLENGTH, 0, 0) != originalLength) return 4; SendMessageW(editor, 2171 /* SCI_SETREADONLY */, FALSE, 0); move_to(SendMessageW(editor, SCI_POSITIONFROMLINE, 3, 0)); key(VK_END, TRUE, FALSE); // truncate before a surrogate pair in the last line key(VK_UP, FALSE, FALSE);// very large document line, bounded range read move_to(0); LRESULT codePage = SendMessageW(editor, SCI_GETCODEPAGE, 0, 0); SendMessageW(editor, 2037 /* SCI_SETCODEPAGE */, 0, 0); SendMessageW(editor, 2066 /* SCI_STYLESETCHARACTERSET */, 32, DEFAULT_CHARSET); SendMessageW(editor, RegisterWindowMessageW(L"CthulhuWineEditFocus"), 0, 0); Sleep(100); SendMessageW(editor, 2066 /* SCI_STYLESETCHARACTERSET */, 32, ANSI_CHARSET); SendMessageW(editor, RegisterWindowMessageW(L"CthulhuWineEditFocus"), 0, 0); Sleep(100); SendMessageW(editor, 2037 /* SCI_SETCODEPAGE */, codePage, 0); // Mark this disposable document clean to close without a save prompt. SendMessageW(editor, 2014 /* SCI_SETSAVEPOINT */, 0, 0); PostMessageW(owner, WM_CLOSE, 0, 0); int result = WaitForSingleObject(process.hProcess, 3000) == WAIT_OBJECT_0 ? 0 : 2; CloseHandle(process.hThread); CloseHandle(process.hProcess); return result; }