Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions QuickLook.Native/QuickLook.Native32/FilePilot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ namespace
constexpr auto CLIPBOARD_POLL_INTERVAL_MS = 5;
constexpr auto MAX_CLASS_NAME_LENGTH = 256;

// Tag synthetic key events generated by QuickLook (SendInput path) for
// third-party file manager interoperability. The managed low-level hook uses
// this marker to recognize and bypass these replayed keys.
// 0x514C5452 == 'QLTR' (QuickLook Third-party Replay)
constexpr ULONG_PTR QUICKLOOK_THIRD_PARTY_HOTKEY_REPLAY_EXTRA_INFO = 0x514C5452;

bool StartsWith(PCWSTR value, PCWSTR prefix)
{
return wcsncmp(value, prefix, wcslen(prefix)) == 0;
Expand Down Expand Up @@ -183,24 +189,30 @@ namespace

inputs[0].type = INPUT_KEYBOARD;
inputs[0].ki.wVk = VK_CONTROL;
inputs[0].ki.dwExtraInfo = QUICKLOOK_THIRD_PARTY_HOTKEY_REPLAY_EXTRA_INFO;

inputs[1].type = INPUT_KEYBOARD;
inputs[1].ki.wVk = VK_SHIFT;
inputs[1].ki.dwExtraInfo = QUICKLOOK_THIRD_PARTY_HOTKEY_REPLAY_EXTRA_INFO;

inputs[2].type = INPUT_KEYBOARD;
inputs[2].ki.wVk = L'C';
inputs[2].ki.dwExtraInfo = QUICKLOOK_THIRD_PARTY_HOTKEY_REPLAY_EXTRA_INFO;

inputs[3].type = INPUT_KEYBOARD;
inputs[3].ki.wVk = L'C';
inputs[3].ki.dwFlags = KEYEVENTF_KEYUP;
inputs[3].ki.dwExtraInfo = QUICKLOOK_THIRD_PARTY_HOTKEY_REPLAY_EXTRA_INFO;

inputs[4].type = INPUT_KEYBOARD;
inputs[4].ki.wVk = VK_SHIFT;
inputs[4].ki.dwFlags = KEYEVENTF_KEYUP;
inputs[4].ki.dwExtraInfo = QUICKLOOK_THIRD_PARTY_HOTKEY_REPLAY_EXTRA_INFO;

inputs[5].type = INPUT_KEYBOARD;
inputs[5].ki.wVk = VK_CONTROL;
inputs[5].ki.dwFlags = KEYEVENTF_KEYUP;
inputs[5].ki.dwExtraInfo = QUICKLOOK_THIRD_PARTY_HOTKEY_REPLAY_EXTRA_INFO;

SendInput(_countof(inputs), inputs, sizeof(INPUT));
}
Expand Down
16 changes: 16 additions & 0 deletions QuickLook/Helpers/GlobalKeyboardHook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ internal class GlobalKeyboardHook : IDisposable
{
private static GlobalKeyboardHook _instance;

// Marker written to INPUT::dwExtraInfo for synthetic hotkeys replayed by QuickLook
// through SendInput when integrating with third-party file managers.
//
// The low-level keyboard hook checks this value and immediately forwards the event,
// so QuickLook does not treat its own replayed keystrokes as real user input.
// This prevents false invalid-key suppression and accidental preview state changes.
// 0x514C5452 == 'QLTR' (QuickLook Third-party Replay)
public const int QUICKLOOK_THIRD_PARTY_HOTKEY_REPLAY_EXTRA_INFO = 0x514C5452;

private User32.KeyboardHookProc _callback;
private nint _hHook = IntPtr.Zero;

Expand Down Expand Up @@ -77,6 +86,13 @@ private int HookProc(int code, int wParam, ref User32.KeyboardHookStruct lParam)
if (IsWindowsKeyPressed())
return User32.CallNextHookEx(_hHook, code, wParam, ref lParam);

// QuickLook itself may replay third-party hotkeys through SendInput.
// These events are tagged in dwExtraInfo and must be forwarded without
// entering QuickLook's hotkey pipeline, otherwise they can pollute state
// (for example invalid-key timing windows) and cause false toggles.
if (lParam.dwExtraInfo == User32.QUICKLOOK_THIRD_PARTY_HOTKEY_REPLAY_EXTRA_INFO)
return User32.CallNextHookEx(_hHook, code, wParam, ref lParam);

var key = (Keys)lParam.vkCode;
key = AddModifiers(key);

Expand Down
Loading