141 lines
6.0 KiB
C
141 lines
6.0 KiB
C
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
// Drive an actual Notepad++ instance in the runner's disposable Wine prefix.
|
|
#include <windows.h>
|
|
#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;
|
|
}
|