Automate X11 Windows and Input with xdotool

xdotool can find, click and type into an X11 window without you touching a mouse driver. This covers identifying a window, inspecting it, activating it, sending a keystroke and moving it, all without guessing a window ID. Examples use the installed Ubuntu package, version 3.20160805.1. Allow about ten minutes if xdotool is already installed.

This is an X11 guide. Upstream documents that xdotool does not work correctly on Wayland. Check the session before debugging a command that appears to do nothing:

printf '%s\n' "${XDG_SESSION_TYPE:-unknown}"

Expected output for these examples is x11. If it says wayland, use a tool designed for your compositor instead; installing more xdotool flags will not change that boundary.

1. Confirm the command and display

Run this as your ordinary desktop user:

$ xdotool --version
xdotool version 3.20160805.1
$ printf '%s\n' "${DISPLAY:-unset}"
:0

Your display value can differ. An unset DISPLAY, a remote session without X authorisation, or a terminal outside the graphical session will make otherwise valid commands fail. No root privilege is needed, and running xdotool with sudo usually makes authorisation problems worse.

Checkpoint: continue only when the version command succeeds and DISPLAY names the X11 display containing the windows you want to control.

2. Find a window and inspect it

Open a harmless test window, such as a terminal, then search by its visible title. The pattern is a regular expression, not a literal string:

xdotool search --onlyvisible --name 'Terminal'

Success prints one X window identifier per matching window, for example 4194309; the exact number differs on every session. If there is no output, loosen the title pattern or inspect the window interactively:

xdotool selectwindow

Click the target window when the cursor changes. The selected identifier is printed; this command changes nothing, it just tells you which window the X server sees.

Save one result in a shell variable only after checking the search is narrow enough:

window_id=$(xdotool search --onlyvisible --limit 1 --name 'Terminal')
test -n "$window_id" && xdotool getwindowname "$window_id"
test -n "$window_id" && xdotool getwindowgeometry "$window_id"

The final two commands print the title and geometry when a match exists. --limit 1 stops an accidental action from hitting several matching windows, but it does not prove the first match is the one you actually meant.

3. Activate and move the selected window

Use the identifier explicitly for a predictable action. Activation may switch to the window's desktop:

xdotool windowactivate --sync "$window_id"
xdotool windowmove --sync "$window_id" 100 100

--sync waits for the requested state change, which is useful in scripts that must type only after activation or continue only once a move has actually taken effect.

Recovery: record the window's coordinates first, then use them to undo a move:

xdotool getwindowgeometry "$window_id"
# Later, replace OLD_X and OLD_Y with the recorded values.
xdotool windowmove --sync "$window_id" OLD_X OLD_Y

Window managers can constrain or reinterpret geometry requests, so verify the result with getwindowgeometry rather than assuming the requested coordinates were accepted.

4. Send input with a visible safety boundary

Typing into the active window is easy to demonstrate, but it changes application state. Focus a disposable text field or terminal first, then send a short test:

xdotool type --delay 50 'xdotool test'
xdotool key Return

The default delay is 12 milliseconds between keystrokes; the explicit delay just makes the example easier to watch. key accepts X keysyms such as ctrl+l, F2 and Return, and several can be supplied in one command.

Targeting a window directly is not the same as typing into the active window:

xdotool type --window "$window_id" 'text for the chosen window'

For a specific window, xdotool uses XSendEvent, and some applications reject events carrying the synthetic-event marker. If that happens, activate the window and use the normal current-window path, or check the application's own setting for accepting generated events; that is an application behaviour issue, not proof the text failed to send.

Mouse commands are similarly direct. Button 1 is normally left click, 2 middle, 3 right:

xdotool mousemove --sync 100 100
xdotool click 1

Warning: never test click or typing commands while a password field, unsaved editor, terminal running a destructive command, or privileged prompt has focus. xdotool has no understanding of what the receiving field actually means.

5. Use command chaining without losing the target

Search results sit in a temporary window stack for the lifetime of one xdotool invocation. %1 means the first result, %@ means all of them, and a separator of two hyphens marks the boundary between search options and the next command:

xdotool search --onlyvisible --class xterm -- windowactivate --sync %1

This finds visible windows whose X class is xterm and activates the first match. With no search result, a command relying on the default %1 fails. With multiple results, %@ can affect every match, so prefer %1 or a tighter search while testing.

For a script that launches an application, search --sync waits until a matching window exists. Pair it with a specific class or name and an application-specific timeout outside xdotool if the launch might hang. Do not use an unrestricted pattern such as . for a state-changing command unless acting on every visible window is genuinely what you want.

Common failures

Done means