Win API
This API exposes a small subset of the Win32 window, clipboard, keyboard, mouse and messaging functions to Lua:
Work with windows via handles (
hwndas integer).Inspect window rectangles and sizes.
Check if a window is foreground or “active”.
Read / write text to the clipboard (UTF-8).
Look up the thread & process IDs for a window.
Send messages, keys, and characters to a window.
Send global keyboard and mouse input via
SendInput.
Window Lookup & Info
find_window(title [, class]) -> hwnd | nil
find_window(title [, class]) -> hwnd | nilSearch for a top-level window.
title— window title to match (UTF-8 string).class— optional window class name (UTF-8 string).
If both are provided, both must match.
Returns hwnd (integer) or nil if not found.
get_window_size(hwnd) -> width, height | nil, nil
get_window_size(hwnd) -> width, height | nil, nilGet the window’s outer size in pixels.
hwnd— window handle (integer).
Returns:
width,height(integers), ornil, nilif the handle is invalid or the call fails.
get_window_rect(hwnd) -> x, y, width, height | nil, nil, nil, nil
get_window_rect(hwnd) -> x, y, width, height | nil, nil, nil, nilGet the window’s full rectangle in screen coordinates.
x,y— top-left screen coordinates.width,height— size in pixels.
Returns all nil on failure.
Window Focus & Activity
is_foreground_window(hwnd) -> bool
is_foreground_window(hwnd) -> boolChecks if hwnd is the current foreground window.
is_window_active(hwnd) -> bool
is_window_active(hwnd) -> boolChecks if a window is “active” in a broad sense:
is a valid window,
is visible,
is not minimized.
Returns true or false.
Window Text & Class
get_window_title(hwnd) -> string | nil
get_window_title(hwnd) -> string | nilGet the window’s title as a UTF-8 string.
Returns
""for empty titles.Returns
nilif the handle is invalid.
get_window_class(hwnd) -> string | nil
get_window_class(hwnd) -> string | nilGet the window’s class name.
Returns
""if class name can’t be read.Returns
nilif the handle is invalid.
set_foreground_window(hwnd) -> bool
set_foreground_window(hwnd) -> boolAttempts to bring the given window to the foreground.
Returns true on success, false otherwise.
Clipboard
All clipboard operations use UTF-8 strings.
copy_to_clipboard(text) -> bool
copy_to_clipboard(text) -> boolCopies a Lua string into the system clipboard.
text— UTF-8 string.Returns
trueon success,falseon failure.
copy_from_clipboard() -> string
copy_from_clipboard() -> stringReads UTF-8 text from the system clipboard.
Returns
""(empty string) if:clipboard cannot be opened,
the clipboard doesn’t contain text, or
any error occurs.
Thread / Process Info
get_window_thread_process_id(hwnd) -> thread_id, process_id | nil, nil
get_window_thread_process_id(hwnd) -> thread_id, process_id | nil, nilReturns IDs associated with a given window.
thread_id— thread that owns the window.process_id— process that owns the thread.
If the handle is invalid or the IDs can’t be obtained, returns nil, nil.
Messages & Keys
post_message(hwnd, msg, wparam, lparam) -> bool
post_message(hwnd, msg, wparam, lparam) -> boolLow-level API to post a message to a window.
hwnd— window handle.msg— message ID (e.g.,0x0010forWM_CLOSE).wparam,lparam— integer parameters.
Returns true on success.
Global key input (SendInput)
SendInput)These functions send global keyboard input (not scoped to a specific window). Use when you simply want to simulate key presses.
Virtual keys are integers (e.g. 0x41 for A, 0x11 for Ctrl, 0x0D for Enter).
win_key_down(vk)
Simulate key down for virtual key vk.
win_key_up(vk)
Simulate key up for virtual key vk.
win_key_press(vk [, delay_ms])
Press and release a key, with an optional delay.
vk— virtual key code.delay_ms— optional delay in milliseconds between down and up (default ~30, clamped to0..1000).
Message-based character & key sending
These functions target a specific window using messages (WM_CHAR, WM_KEYDOWN, WM_KEYUP).
send_char(hwnd, text) -> bool
Sends a single character via WM_CHAR.
text— Lua string; only the first UTF-16 code unit is used, which is fine for most BMP characters.
Returns true on success.
send_key(hwnd, vk) -> bool
Sends a key to a specific window using WM_KEYDOWN and WM_KEYUP.
vk— virtual key code.
Returns true if both messages were posted successfully.
Mouse Input
All mouse functions send global input via SendInput.
Coordinates are in screen pixels (primary monitor), unless noted otherwise.
mouse_move(x, y)
mouse_move(x, y)Moves the cursor to absolute screen coordinates.
(0,0)is top-left of the primary display.Coordinates are converted to the normalized range expected by
SendInput.
mouse_move_relative(dx, dy)
mouse_move_relative(dx, dy)Moves the cursor relative to its current position.
dx— horizontal delta (pixels).dy— vertical delta (pixels).
mouse_left_click()
mouse_left_click()Performs a left button click at the current cursor position.
mouse_right_click()
mouse_right_click()Performs a right button click.
mouse_middle_click()
mouse_middle_click()Performs a middle button click.
Each click sends a down event, waits ~10 ms, then sends an up event.
mouse_scroll(amount)
mouse_scroll(amount)Scrolls the mouse wheel vertically.
amountis in “notches”:positive → scroll up
negative → scroll down
Example: mouse_scroll(3) scrolls up 3 notches.
Util
get_tickcount64()
GetTickCount64()
Usage Examples
Find a window and print info
local title = "Untitled - Notepad"
local hwnd = find_window(title)
if not hwnd then
print("Window not found:", title)
return
end
print("HWND:", string.format("0x%X", hwnd))
local x, y, w, h = get_window_rect(hwnd)
print("Rect:", x, y, w, h)
local winTitle = get_window_title(hwnd) or "<nil>"
local winClass = get_window_class(hwnd) or "<nil>"
print("Title:", winTitle)
print("Class:", winClass)
local tid, pid = get_window_thread_process_id(hwnd)
print("Thread ID:", tid, "Process ID:", pid)Clipboard
copy_to_clipboard("Hello from Lua! こんにちは 🌸")
local txt = copy_from_clipboard()
print("Clipboard:", txt)Global keystrokes
-- Type 'HELLO'
local letters = { 0x48, 0x45, 0x4C, 0x4C, 0x4F } -- H E L L O
for _, vk in ipairs(letters) do
win_key_press(vk, 40)
end
-- Ctrl+V
local VK_CONTROL = 0x11
local VK_V = 0x56
win_key_down(VK_CONTROL)
win_key_press(VK_V, 30)
win_key_up(VK_CONTROL)Message-based typing into a window
local hwnd = find_window("Untitled - Notepad")
if not hwnd then return end
-- make sure Notepad has focus (if you expose set_foreground_window)
set_foreground_window(hwnd)
send_char(hwnd, "H")
send_char(hwnd, "i")
send_char(hwnd, " ")
send_char(hwnd, "🌸")
local VK_RETURN = 0x0D
send_key(hwnd, VK_RETURN)Mouse movement & clicks
-- Move to near top-left, left-click
mouse_move(100, 100)
mouse_left_click()
-- Move a bit to the right and right-click
mouse_move_relative(80, 0)
mouse_right_click()
-- Move down and middle-click
mouse_move_relative(0, 60)
mouse_middle_click()
-- Scroll up then down
mouse_scroll(3)
mouse_scroll(-3)Full API Test
local TARGET_TITLE = "Untitled - Notepad" -- change to your target window
local function has_global(name)
return _G[name] ~= nil
end
local function get_target_hwnd()
if not has_global("find_window") then
log("ERROR: find_window is not available.")
return nil
end
local hwnd = find_window(TARGET_TITLE)
if not hwnd then
log("Window not found:", TARGET_TITLE)
return nil
end
log("Found hwnd:", string.format("0x%X", hwnd))
if has_global("set_foreground_window") then
local ok = set_foreground_window(hwnd)
log("set_foreground_window:", ok)
else
log("set_foreground_window not available, skipping.")
end
return hwnd
end
local function test_mouse_basic()
log("=== TEST: mouse movement & clicks ===")
log("Move your eyes to where the cursor goes. :)")
-- Move to approximate top-left of the primary screen
mouse_move(100, 100)
mouse_left_click()
log(" mouse_move(100,100) + left click")
-- Move a bit to the right and right-click
mouse_move_relative(80, 0)
mouse_right_click()
log(" mouse_move_relative(80,0) + right click")
-- Move a bit down and middle-click
mouse_move_relative(0, 60)
mouse_middle_click()
log(" mouse_move_relative(0,60) + middle click")
-- Scroll up then down a bit
mouse_scroll(3) -- up 3 notches
mouse_scroll(-3) -- down 3 notches
log(" mouse_scroll(3) then mouse_scroll(-3)")
end
local function test_send_char_key(hwnd)
if not hwnd then
log("No hwnd, skipping send_char/send_key test.")
return
end
if not has_global("send_char") or not has_global("send_key") then
log("send_char / send_key not available, skipping.")
return
end
log("=== TEST: send_char / send_key ===")
log("Make sure the target window has a focused text field.")
-- Send "Hi " + emoji via WM_CHAR
send_char(hwnd, "H")
send_char(hwnd, "i")
send_char(hwnd, " ")
send_char(hwnd, "🌸") -- only
-- Press Enter via WM_KEYDOWN/UP
local VK_RETURN = 0x0D
local ok = send_key(hwnd, VK_RETURN)
log("send_key(VK_RETURN) ->", ok)
end
function main()
log("Target window title:", TARGET_TITLE)
local hwnd = get_target_hwnd()
test_mouse_basic()
test_send_char_key(hwnd)
log("=== winapi_input_test.lua done ===")
return 1;
endLast updated