XDOTOOL(1)

๐Ÿ’– XDOTOOL(1) ๐Ÿ’–

Bestie, welcome to xdotool(1) โ€” command-line X11 automation tool. She is literally the main character of X11 scripting, no cap.


๐ŸŒธ NAME ๐ŸŒธ

xdotool โ€” command-line X11 automation tool. Thatโ€™s the name, thatโ€™s the vibe. Iconic.

โœจ SYNOPSIS โœจ

xdotool cmd args...

Quick notation check, bestie: some documentation uses [window] to denote an optional window argument. In this case it means the argument, if not present, will default to "%1". See WINDOW STACK for what "%1" means. Iykyk.

๐Ÿฆ‹ DESCRIPTION ๐Ÿฆ‹

xdotool lets you programmatically (or manually) simulate keyboard input and mouse activity, move and resize windows, etc. It does this using X11's XTEST extension and other Xlib functions. Highkey magical.

There is some support for Extended Window Manager Hints (aka EWMH or NetWM). See the EXTENDED WINDOW MANAGER HINTS section for more information.


๐ŸŽ€ KEYBOARD COMMANDS ๐ŸŽ€

๐Ÿ’… key [options] keystroke [keystroke ...]

Okay so this is the command that sends keystrokes. Options first:

  • --window window โ€” Send keystrokes to a specific window id. You can use WINDOW STACK references like %1 and %@ here. If there is a window stack, then %1 is the default, otherwise the current window is used.

See also: SENDEVENT NOTES and WINDOW STACK.

  • --clearmodifiers โ€” Clear modifiers before sending keystrokes. See CLEARMODIFIERS below.
  • --delay milliseconds โ€” Delay between keystrokes. Default is 12ms.

Type a given keystroke. Examples being alt+r, Control_L+J, ctrl+alt+n, BackSpace.

Generally, any valid X Keysym string will work. Multiple keys are separated by +. Aliases exist for alt, ctrl, shift, super, and meta which all map to Foo_L, such as Alt_L and Control_L, etc.

In cases where your keyboard doesn't actually have the key you want to type, xdotool will automatically find an unused keycode and use that to type the key. Bestie behavior fr fr.

With respect to COMMAND CHAINING, this command consumes the remainder of the arguments or until a new xdotool command is seen, because no xdotool commands are valid keystrokes.

Example: Send the keystroke F2

xdotool key F2

Example: Send 'a' with an accent over it (not on English keyboards, but still works with xdotool)

xdotool key Aacute

Example: Send ctrl+l and then BackSpace as separate keystrokes:

xdotool key ctrl+l BackSpace

Example: Send ctrl+c to all windows matching title 'gdb' (See COMMAND CHAINING)

xdotool search --name gdb key ctrl+c

keydown [options] keystroke

Same as above, except only keydown (press) events are sent. Just the down half of the keystroke energy.

keyup keystroke

Same as above, except only keyup (release) events are sent. Just the release, periodt.

type [options] something to type

Options, bestie:

  • --window windowid โ€” Send keystrokes to a specific window id. See SENDEVENT NOTES below. The default, if no window is given, depends on the window stack. If the window stack is empty the current window is typed at using XTEST. Otherwise, the default is "%1" (see WINDOW STACK).
  • --delay milliseconds โ€” Delay between keystrokes. Default is 12ms.
  • --clearmodifiers โ€” Clear modifiers before sending keystrokes. See CLEARMODIFIERS below.

Types as if you had typed it. Supports newlines and tabs (ASCII newline and tab). Each keystroke is separated by a delay given by the --delay option.

With respect to COMMAND CHAINING, this command consumes the remainder of the arguments and types them. That is, no commands can chain after 'type'.

Example: to type 'Hello world!' you would do:

xdotool type 'Hello world!'

๐Ÿญ MOUSE COMMANDS ๐Ÿญ

mousemove [options] x y OR 'restore'

Move the mouse to the specific X and Y coordinates on the screen. Very demure, very mindful.

You can move the mouse to the previous location if you specify 'restore' instead of an X and Y coordinate. Restoring only works if you have moved previously in this same command invocation. Further, it does not work with the --window option.

For example, to click the top-left corner of the screen and move the mouse to the original position before you moved it, use this:

xdotool mousemove 0 0 click 1 mousemove restore
  • --window WINDOW โ€” Specify a window to move relative to. Coordinates 0,0 are at the top left of the window you choose.

WINDOW STACK references are valid here, such as %1 and %@. Though, using %@ probably doesn't make sense.

  • --screen SCREEN โ€” Move the mouse to the specified screen to move to. This is only useful if you have multiple screens and ARE NOT using Xinerama.

The default is the current screen. If you specify --window, the --screen flag is ignored.

  • --polar โ€” Use polar coordinates. This makes x an angle (in degrees, 0-360, etc) and y the distance.

Rotation starts at 'up' (0 degrees) and rotates clockwise: 90 = right, 180 = down, 270 = left.

The origin defaults to the center of the current screen. If you specify a --window, then the origin is the center of that window.

  • --clearmodifiers โ€” See CLEARMODIFIERS.
  • --sync โ€” After sending the mouse move request, wait until the mouse is actually moved. If no movement is necessary, we will not wait. This is useful for scripts that depend on actions being completed before moving on.

Note: We wait until the mouse moves at all, not necessarily that it actually reaches your intended destination. Some applications lock the mouse cursor to certain regions of the screen, so waiting for any movement is better in the general case than waiting for a specific target.

mousemove_relative [options] x y

Move the mouse x,y pixels relative to the current position of the mouse cursor.

  • --polar โ€” Use polar coordinates. This makes x an angle (in degrees, 0-360, etc) and y the distance.

Rotation starts at 'up' (0 degrees) and rotates clockwise: 90 = right, 180 = down, 270 = left.

  • --sync โ€” After sending the mouse move request, wait until the mouse is actually moved. If no movement is necessary, we will not wait. This is useful for scripts that depend on actions being completed before moving on.

Note that we wait until the mouse moves at all, not necessarily that it actually reaches your intended destination. Some applications lock the mouse cursor to certain regions of the screen, so waiting for any movement is better in the general case than waiting for a specific target.

  • --clearmodifiers โ€” See CLEARMODIFIERS.

click [options] button

Send a click, that is, a mousedown followed by mouseup for the given button with a short delay between the two (currently 12ms).

Buttons generally map this way: Left mouse is 1, middle is 2, right is 3, wheel up is 4, wheel down is 5.

  • --clearmodifiers โ€” Clear modifiers before clicking. See CLEARMODIFIERS below.
  • --repeat REPEAT โ€” Specify how many times to click. Default is 1. For a double-click, use --repeat 2.
  • --delay MILLISECONDS โ€” Specify how long, in milliseconds, to delay between clicks. This option is not used if the --repeat flag is set to 1 (default).
  • --window WINDOW โ€” Specify a window to send a click to. See SENDEVENT NOTES below for caveats. Uses the current mouse position when generating the event.

The default, if no window is given, depends on the window stack. If the window stack is empty the current window is typed at using XTEST. Otherwise, the default is "%1" (see WINDOW STACK).

mousedown [options] button

Same as click, except only a mouse down is sent.

mouseup [options] button

Same as click, except only a mouse up is sent.

getmouselocation [--shell]

Outputs the x, y, screen, and window id of the mouse cursor. Screen numbers will be nonzero if you have multiple monitors and are not using Xinerama.

This command updates the window stack with the window id of the window directly underneath the mouse. Love that for us.

  • --shell โ€” This makes getmouselocation output shell data you can eval.

Example:

% xdotool getmouselocation --shell
X=880
Y=443
SCREEN=0
WINDOW=16777250

% eval $(xdotool getmouselocation --shell)
% echo $X,$Y
714,324

behave_screen_edge [options] where command ...

Bind an action to events when the mouse hits the screen edge or corner. This is lowkey one of the coolest ones, bestie.

Options are:

  • --delay MILLISECONDS โ€” Delay in milliseconds before running the command. This allows you to require a given edge or corner to be held for a short period before your command will run. If you leave the edge or corner before the delay expires then the time will reset.
  • --quiesce MILLISECONDS โ€” Delay in milliseconds before the next command will run. This helps prevent accidentally running your command extra times; especially useful if you have a very short --delay (like the default of 0).

โณ Event timeline โณ

  • Mouse hits an edge or corner.
  • If delay is nonzero, the mouse must stay in this edge or corner until delay time expires.
  • If still in the edge/corner, trigger.
  • If quiesce is nonzero, then there is a cool-down period where the next trigger cannot occur.

Valid 'where' values are:

  • left
  • top-left
  • top
  • top-right
  • right
  • bottom-left
  • bottom
  • bottom-right

Examples:

# Activate google-chrome when you move the mouse to the bottom-left corner:
xdotool behave_screen_edge bottom-left \
  search --class google-chrome windowactivate

# Go to the next workspace (right). Known to work in GNOME (metacity and compiz)
xdotool behave_screen_edge --delay 500 bottom-right key XF86Forward

# Activate firefox and do a web search in a new tab for text in your clipboard
xdotool behave_screen_edge --delay 1000 top-left \
    search --classname Navigator \
    windowactivate --sync key --delay 250 ctrl+t ctrl+k ctrl+v Return

โœจ WINDOW COMMANDS โœจ

  • search [options] pattern โ€” bestie this is THE one. It hunts down windows whose titles, names, or classes match a regular expression pattern, and spits out a line-delimited list of X window identifiers. So like, one window id per line, very demure. ๐Ÿ’… If you're doing COMMAND CHAINING, the search command will only write window ids to stdout if it's the last (or only) command in the chain โ€” otherwise it stays silent. Sneaky but iconic.

The result gets saved to the window stack for future chained commands. Peep WINDOW STACK and COMMAND CHAINING for the deets.

Patterns are POSIX extended regular expressions (ERE), e.g. Chrom(e|ium)$ for windows ending in "Chrome" or "Chromium". See regex(7) for syntax details. Matches are case-insensitive โ€” no cap, it does not care about your caps lock. ๐Ÿซถ

The default options are --name --class --classname --role (unless you specify one or more of --name, --class, --classname, or --role).

The options available are:

  • --class โ€” match against the window class.
  • --classname โ€” match against the window classname.
  • --role โ€” match against the window role.
  • --maxdepth N โ€” set the recursion/child search depth. Default is -1, meaning infinite (go off, queen). 0 means no depth, only root windows will be searched. If you only want toplevel windows, set a maxdepth of 1 (or 2, depending on how your window manager does decorations โ€” some WMs are extra like that ๐Ÿ™ˆ).
  • --name โ€” match against the window name. This is the same string that's displayed in the window titlebar.
  • --onlyvisible โ€” show only visible windows in the results. That means ones with map state IsViewable. No ghosts allowed, periodt. ๐Ÿ‘ป
  • --pid PID โ€” match windows that belong to a specific process id. Ngl this may not work for some X applications that don't set this metadata on their windows โ€” not their fault, they're just built different.
  • --screen N โ€” select windows only on a specific screen. Default is to search all screens. Only meaningful if you have multiple displays and aren't using Xinerama.
  • --desktop N โ€” only match windows on a certain desktop. 'N' is a number. The default is to search all desktops.
  • --limit N โ€” stop searching after finding N matching windows. Specifying a limit will help speed up your search if you only want a few results. Speedrun strats fr fr. The default is no search limit (which is equivalent to --limit 0).
  • --title โ€” DEPRECATED. See --name. She's retired, let her rest ๐Ÿ’€.
  • --all โ€” require that all conditions be met. For example:
xdotool search --all --pid 1424 --name "Hello World"

This will match only windows that have "Hello World" as a name and are owned by pid 1424.

  • --any โ€” match windows that match any condition (logically, 'or'). This is on by default. For example:
xdotool search --any --pid 1424 --name "Hello World"

This will match any windows owned by pid 1424 or windows with name "Hello World".

  • --sync โ€” block until there are results. This is useful when you're launching an application and want to wait until the application window is visible. Like waiting for your friend to finally show up to the plans. For example:
google-chrome &
xdotool search --sync --onlyvisible --class "google-chrome"
  • selectwindow โ€” get the window id (for a client) by clicking on it. Super useful for having scripts query you humans for what window to act on (yes, the script needs your opinion, main character energy ๐ŸŽ€). For example, killing a window by clicking on it:
xdotool selectwindow windowkill
  • behave window action command ... โ€” bind an action to an event on a window. This lets you run additional xdotool commands whenever a matched event occurs. Set it and forget it, the automation slays. โœจ

The command run as a result of the behavior is run with %1 being the window that was acted upon. Examples follow after the event list.

The following are valid events:

  • mouse-enter โ€” fires when the mouse enters a window. This is similar to 'mouse over' events in javascript, if that helps (it does, it so does).
  • mouse-leave โ€” fires when the mouse leaves a window. This is the opposite of 'mouse-enter'. Duh, but we said it nicely. ๐Ÿ’–
  • mouse-click โ€” fires when the mouse is clicked. Specifically, when the mouse button is released. Precision matters bestie.
  • focus โ€” fires when the window gets input focus.
  • blur โ€” fires when the window loses focus.

Examples:

# Print the cursor location whenever the mouse enters a currently-visible
# window:
xdotool search --onlyvisible . behave %@ mouse-enter getmouselocation

# Print the window title and pid whenever an xterm gets focus
xdotool search --class xterm behave %@ focus getwindowname getwindowpid

# Emulate focus-follows-mouse
xdotool search . behave %@ mouse-enter windowfocus
  • getwindowpid [window] โ€” output the PID owning a given window. This requires effort from the application owning a window and may not work for all windows. It uses the _NET_WM_PID property of the window. See EXTENDED WINDOW MANAGER HINTS below for more info.

If no window is given, the default is %1. If no windows are on the stack, then this is an error. See WINDOW STACK for more details.

Example: find the PID for all xterms:

xdotool search --class xterm getwindowpid %@
  • getwindowname [window] โ€” output the name of a given window, also known as the title. This is the text displayed in the window's titlebar by your window manager. Her government name, if you will. ๐Ÿ‘‘

If no window is given, the default is %1. If no windows are on the stack, then this is an error. See WINDOW STACK for more details.

  • getwindowgeometry [options] [window] โ€” output the geometry (location and position) of a window. The values include: x, y, width, height, and screen number. A whole little vibe check for your window. ๐Ÿ’…
    • --shell โ€” output values suitable for 'eval' in a shell. Fancy.
  • getwindowfocus [-f] โ€” prints the window id of the currently focused window. Saves the result to the window stack. See WINDOW STACK for more details.

If the current window has no WM_CLASS property, we assume it's not a normal top-level window and traverse up the parents until we find a window with a WM_CLASS set and return that window id. It's giving "we asked around the friend group until someone knew." ๐Ÿ˜ญ

If you really want the window currently having focus and don't care if it has a WM_CLASS setting, then use getwindowfocus -f.

  • windowsize [options] [window] width height โ€” set the window size of the given window. If no window is given, %1 is the default. See WINDOW STACK and COMMAND CHAINING for more details.

Percentages are valid for width and height. They are relative to the geometry of the screen the window is on. For example, to make a window the full width of the screen, but half height:

xdotool windowsize <window> 100% 50%

Percentages are valid with --usehints and still mean pixel-width relative to the screen size. Yes, even then. Don't overthink it. ๐Ÿงš

The options available are:

  • --usehints โ€” use window sizing hints (when available) to set width and height. This is useful on terminals for setting the size based on row/column of text rather than pixels. Text-mode girlies rise up. ๐Ÿ“
  • --sync โ€” after sending the window size request, wait until the window is actually resized. If no change is necessary, we will not wait. This is useful for scripts that depend on actions being completed before moving on.
Note: because many window managers may ignore or alter the original resize request, we will wait until the size changes from its original size, not necessary to the requested size. So like, we're watching for a change, not the change. Big difference, no cap.

Example: to set a terminal to be 80x24 characters, you would use:

xdotool windowsize --usehints <some_windowid> 80 24
  • windowmove [options] [window] x y โ€” move the window to the given position. If no window is given, %1 is the default. See WINDOW STACK and COMMAND CHAINING for more details.

If the given x coordinate is literally x, then the window's current x position will be unchanged. The same applies for y. Iconic little stay-in-your-lane behavior. ๐Ÿ’…

Examples:

xdotool getactivewindow windowmove 100 100    # Moves to 100,100
xdotool getactivewindow windowmove x 100      # Moves to x,100
xdotool getactivewindow windowmove 100 y      # Moves to 100,y
xdotool getactivewindow windowmove 100 y      # Moves to 100,y

Percentages are valid for width and height. They are relative to the geometry of the screen the window is on. For example, to make a window the full width of the screen, but half height:

xdotool windowmove <window> 100% 50%
  • --sync โ€” after sending the window move request, wait until the window is actually moved. If no movement is necessary, we will not wait. This is useful for scripts that depend on actions being completed before moving on.
  • --relative โ€” make movement relative to the current window position. So like, nudge nudge instead of teleport. ๐Ÿฆ‹
  • windowfocus [options] [window] โ€” focus a window. If no window is given, %1 is the default. See WINDOW STACK and COMMAND CHAINING for more details.

Uses XSetInputFocus which may be ignored by some window managers or programs. Rude of them, but okay. ๐Ÿ™ˆ

  • --sync โ€” after sending the window focus request, wait until the window is actually focused. This is useful for scripts that depend on actions being completed before moving on.
  • windowmap [options] [window] โ€” map a window. In X11 terminology, mapping a window means making it visible on the screen. If no window is given, %1 is the default. See WINDOW STACK and COMMAND CHAINING for more details. She has arrived. ๐ŸŒˆ
    • --sync โ€” after requesting the window map, wait until the window is actually mapped (visible). This is useful for scripts that depend on actions being completed before moving on.
  • windowminimize [options] [window] โ€” minimize a window. In X11 terminology, this is called 'iconify.' If no window is given, %1 is the default. See WINDOW STACK and COMMAND CHAINING for more details. Go to sleep bestie, you've done enough. ๐Ÿ˜ด
    • --sync โ€” after requesting the window minimize, wait until the window is actually minimized. This is useful for scripts that depend on actions being completed before moving on.
  • windowraise [window_id=%1] โ€” raise the window to the top of the stack. This may not work on all window managers. If no window is given, %1 is the default. See WINDOW STACK and COMMAND CHAINING for more details. Main character moment: she's in front now. ๐Ÿ‘‘
  • windowlower [window_id=%1] โ€” lower the window to the bottom of the stack. This may not work on all window managers. If no window is given, %1 is the default. See WINDOW STACK and COMMAND CHAINING for more details. Background character era, respectfully. ๐Ÿซถ
  • windowreparent [source_window] destination_window โ€” Okay so this one literally moves a whole window into another window ๐Ÿช†. It takes source_window and makes it a child window of destination_window. No source given? %1 is your default bestie. And here's the slay part: "WINDOW STACK" window references (like %1) are valid for both source_window and destination_window. Peep "WINDOW STACK" and "COMMAND CHAINING" for the full lore โœจ.
  • windowclose [window] โ€” Closes a window ๐Ÿ’€. This destroys the window itself but does not try to kill the client controlling it โ€” so the app can keep living its truth. No window given? %1 is the default. See "WINDOW STACK" and "COMMAND CHAINING" ๐Ÿซถ.
  • windowquit [window] โ€” The polite version ๐Ÿ‘‘. Closes a window gracefully by sending a request, which lets the application do its close-confirmation thing (you know, the "are u sure bestie" popup). No window given? %1 is the default. See "WINDOW STACK" and "COMMAND CHAINING" ๐ŸŽ€.
  • windowkill [window] โ€” No thoughts, just violence ๐Ÿ”ฅ. This destroys the window and kills the client controlling it. Rude, but sometimes necessary. No window given? %1 is the default. See WINDOW STACK and "COMMAND CHAINING".
  • windowunmap [options] [window_id=%1] โ€” Unmaps a window, which means it's no longer showing up on your screen ๐ŸŒซ๏ธ. It's still there, just not visible, main-character-invisible mode. No window given? %1 is the default. See "WINDOW STACK" and "COMMAND CHAINING".
    • --sync โ€” After requesting the window unmap, it waits until the window is actually unmapped (hidden). Iconic for scripts that depend on actions being completed before moving on โณ.
  • set_window [options] [windowid=%1] โ€” Sets properties about a window ๐ŸŽจ. No window given? %1 is the default. See "WINDOW STACK" and "COMMAND CHAINING".

Options, bestie:

  • --name newname โ€” Sets the window WM_NAME (that's the window title, usually ๐Ÿ’…).
  • --icon-name newiconname โ€” Sets the window WM_ICON_NAME (the window title when minimized, usually).
  • --role newrole โ€” Sets the window WM_WINDOW_ROLE.
  • --classname newclassname โ€” Sets the window class name. Do not confuse it with window class, they're different girlies, iykyk ๐Ÿ‘€.
  • --class newclass โ€” Sets the window class. Again, not to be confused with window class name ๐Ÿ™ˆ.
  • --urgency value โ€” Sets the window urgency hint. If the value is 1, the window gets marked urgent and the window manager will somehow highlight it to grab the user's attention ๐Ÿšจ. If the value is 0, the window gets marked non-urgent ๐Ÿ˜Œ.
  • --overrideredirect value โ€” Sets the window's override_redirect value. This is a hint to the window manager about whether it should be managed at all. If the redirect value is 0, the window manager draws borders and treats the window normally ๐Ÿ’–. If the value is 1, the window manager straight-up ignores the window ๐Ÿ’€.
Fr fr: if you change this value, your window manager may not notice the change until the window is mapped again. So you probably wanna issue windowunmap and windowmap to make the window manager take note ๐Ÿ“Œ.

๐ŸŒˆ DESKTOP AND WINDOW COMMANDS ๐ŸŒˆ

These commands follow the EWMH standard. See the section "EXTENDED WINDOW MANAGER HINTS" for more info, bestie ๐Ÿงš.

  • windowactivate [options] [window] โ€” Activates the window โœจ. This is not the same as windowfocus: if the window is chilling on another desktop, we will switch to that desktop for you ๐Ÿ–ฅ๏ธ. It also uses a different method for bringing the window up. Genuine rec: try this command before using windowfocus, because it will work on more window managers ๐Ÿ’….

If no window is given, %1 is the default. See "WINDOW STACK" and "COMMAND CHAINING" for more deets.

  • --sync โ€” After sending the window activation, wait until the window is actually activated. Perfect for scripts that depend on actions being completed before moving on โณ.
  • getactivewindow โ€” Outputs the current active window ๐Ÿ‘‘. This command is often more reliable than getwindowfocus. The result is saved to the window stack โ€” see "WINDOW STACK" for the details ๐Ÿซถ.
  • windowstate [--add PROPERTY] [--remove PROPERTY] [--toggle PROPERTY] [window] โ€” Changes a property on a window ๐Ÿ”ง.

Heads up: some properties may have no effect on some windows, or in some window managers. Not every WM is on the same wavelength ๐Ÿ˜ญ.

Properties can be any of:

  • MODAL โ€” makes the window into a modal
  • STICKY โ€” makes the window appear on all workspaces
  • MAXIMIZED_VERT โ€” sizes the window maximized vertically
  • MAXIMIZED_HORZ โ€” sizes the window maximized horizontally
  • ABOVE โ€” Show window above all others (always on top)
  • BELOW โ€” Show window below all others
  • SKIP_TASKBAR โ€” hides the window from the taskbar
  • SKIP_PAGER โ€” hides the window from the window pager
  • FULLSCREEN โ€” makes window fullscreen
  • HIDDEN โ€” unmaps the window
  • SHADED โ€” rolls the window up
  • DEMANDS_ATTENTION โ€” marks window urgent or needing attention

This feature requires a window manager which supports EWMH. Most window managers probably support this :)

  • getwindowclassname [window] โ€” Prints the class name for the window ๐Ÿท๏ธ.
  • set_num_desktops number โ€” Changes the number of desktops or workspaces ๐Ÿ”ข.
  • get_num_desktops โ€” Outputs the current number of desktops.
  • get_desktop_viewport [--shell] โ€” Reports the current viewport's position ๐Ÿ“. If --shell is given, the output is friendly to shell eval ๐Ÿš.

Viewports are sometimes used instead of 'virtual desktops' on some window managers. A viewport is simply a view on a very large desktop area, no cap.

  • set_desktop_viewport x y โ€” Moves the viewport to the given position ๐Ÿ›ธ. Not all requests will be obeyed โ€” some window managers only obey requests that align to workspace boundaries, such as the screen size. So like, don't take it personally ๐Ÿ™ˆ.

For example, if your screen is 1280x800, you can move to the 2nd workspace by doing:

xdotool set_desktop_viewport 1280 0
  • set_desktop [options] desktop_number โ€” Changes the current view to the specified desktop ๐Ÿ’ป.
    • --relative โ€” Use relative movements instead of absolute. This lets you move relative to the current desktop.

Examples:

# Change to desktop #5
xdotool set_desktop 5

# Change to the previous desktop. (Note the use of "--")
xdotool set_desktop --relative -- -1
  • get_desktop โ€” Outputs the current desktop in view ๐Ÿ‘๏ธ.
  • set_desktop_for_window [window] desktop_number โ€” Moves a window to a different desktop ๐Ÿšš. If no window is given, %1 is the default. See "WINDOW STACK" and "COMMAND CHAINING" for more details.
  • get_desktop_for_window [window] โ€” Outputs the desktop currently containing the given window. Move a window to a different desktop. If no window is given, %1 is the default. See WINDOW STACK and "COMMAND CHAINING" for more details ๐Ÿ“š.

๐Ÿ“ MISCELLANEOUS COMMANDS ๐Ÿ“

  • exec [options] command [...] โ€” Executes a program ๐Ÿš€. This is often super useful when combined with behave_screen_edge to do things like locking your screen ๐Ÿ”’.

Options:

  • --sync โ€” Blocks until the child process exits. The child process exit status is then passed to the parent process (xdotool), which copies it ๐Ÿ“‹.

Examples:

# Lock the screen when the mouse sits in the top-right corner
xdotool behave_screen_edge --delay 1000 top-right \
  exec gnome-screensaver-command --lock
# Substitute 'xscreensaver-command -lock' if you use that program.

# The following will fail to move the mouse because we use '--sync' and
# /bin/false exits nonzero:
xdotool exec --sync /bin/false mousemove 0 0

# This succeeds, though, since we do not use --sync on the exec command.
xdotool exec /bin/false mousemove 0 0
  • sleep seconds โ€” Sleeps for a specified period ๐Ÿ˜ด. Fractions of seconds (like 1.3, or 0.4) are valid here, periodt โฑ๏ธ.

๐Ÿฆ‹ SCRIPTS ๐Ÿฆ‹

xdotool can read a list of commands via stdin or a file if you want ๐Ÿ“„. A script will fail when any command fails โ€” no silent failures, we don't do that here ๐Ÿ’….

Truthfully, 'script' mode isn't fully fleshed out and may fall below your expectations ๐Ÿ˜ฌ. If you have suggestions, please email the list or file a bug (See CONTACT) โ€” feedback is a love language ๐Ÿซถ.

Scripts can use positional arguments (Represented by $1, $2, ...) and environment variables (like $HOME or $WINDOWID). Quoting arguments should work as expected โœจ.

Scripts are processed for parameter and environment variable expansion and then run as if you had invoked xdotool with the entire script on one line (using COMMAND CHAINING).

  • Read commands from a file:
xdotool filename
  • Read commands from stdin:
xdotool -
  • Read commands from a redirected file:
xdotool - < myfile

You can also write scripts that only execute xdotool. Example:

#!/usr/local/bin/xdotool
search --onlyvisible --classname $1

windowsize %@ $2 $3
windowraise %@

windowmove %1 0 0
windowmove %2 $2 0
windowmove %3 0 $3
windowmove %4 $2 $3

This script will take all windows matched by the classname query given by arg1 ($1) and sizes/moves them into a 2x2 grid with windows sized by the 2nd and 3rd parameters ๐Ÿ”ฒ๐Ÿ”ฒ.

Here's an example usage:

% ./myscript xterm 600 400

Running it like this will take 4 visible xterms, raise them, and move them into a 2x2 tile grid with each window 600x400 pixels in size. Iconic behavior honestly ๐Ÿ‘‘.

๐Ÿ’œ CLEARMODIFIERS ๐Ÿ’œ

Any command taking the --clearmodifiers flag will attempt to clear any active input modifiers during the command and restore them afterwards ๐Ÿงน.

For example, if you were to run this command:

xdotool key a

The result would be a or A depending on whether or not you were holding the shift key on your keyboard ๐Ÿ˜ญ. Often it is undesirable to have any modifiers active, so you can tell xdotool to clear any active modifiers. So thoughtful fr fr ๐Ÿ’–.

The order of operations if you hold shift while running xdotool key --clearmodifiers a is this:

  1. Query for all active modifiers (finds shift, in this case)
  2. Try to clear shift by sending 'key up' for the shift key
  3. Runs normal xdotool key a
  4. Restore shift key by sending 'key down' for shift

The --clearmodifiers flag can currently clear of the following:

  • any key in your active keymap that has a modifier associated with it. (See xmodmap(1)'s xmodmap -pm output)
  • mouse buttons (1, 2, 3, 4, and 5)
  • caps lock

๐Ÿ”ฅ SENDEVENT NOTES ๐Ÿ”ฅ

okay bestie, this is the part where we talk about the โœจfamousโœจ "why is my typing not going anywhere" problem. Listen up ๐Ÿ’…

So โ€” if you're trying to send key input to a specific window and it's justโ€ฆ not working? It's probably not you. It's lowkey your app ignoring the events xdotool is generating. This is fairly common, ngl. ๐Ÿ™ˆ

Here's the tea ๐Ÿ“: sending keystrokes to a specific window uses a different API than just typing to the active window. Like, if you specify xdotool type --window 12345 hello, xdotool will generate key events and send them directly to window 12345. But โ€” and this is the plot twist โ€” X11 servers set a special flag on all events generated this way (see XEvent.xany.send_event in X11's manual). And lots of programs observe that flag and reject those events. Rude, honestly. ๐Ÿ˜ญ

Key thing to remember, no cap: for key and mouse events, we only use XSendEvent when a specific window is targeted. Otherwise, we use XTEST. Periodt.

Some programs can be configured to accept events even if they're generated by xdotool โ€” so go read the docs of your app, bestie ๐Ÿซถ

Specific application notes (from the author's testing, iykyk):

  • Firefox 3 seems to ignore all input when it does not have focus.
  • xterm can be configured while running with ctrl+leftclick, 'Allow SendEvents'
  • gnome-terminal appears to accept generated input by default.

๐ŸชŸ WINDOW STACK ๐ŸชŸ

Certain commands (search, getactivewindow, getwindowfocus) will find windows for you โœจ. These results are generally printed to stdout, but they're also saved to memory for future use during the lifetime of the xdotool process. See "COMMAND CHAINING" for more info ๐Ÿ‘€

Now, the only modification supported for the window stack is to replace it. Like, that's it. That's the whole feature. So if you run two sequential searches, only the last one's results will be the window stack. Sorry babes ๐Ÿ’€


โ›“๏ธ COMMAND CHAINING โ›“๏ธ

xdotool supports running multiple commands on a single invocation ๐Ÿ‘‘ Generally you'll start with a search command (peep "WINDOW STACK") and then perform a set of actions on those results.

To query the window stack, you can use the special notation %N where N is a number or the @ symbol. If %N is given, the Nth window will be selected from the window stack. Generally you'll only want the first window or all the windows ๐Ÿ’–

Note that the order of windows in the window stack corresponds to the window stacking order, i.e. the bottom-most window will be reported first (see XQueryTree(3)). So yeah โ€” the order of the windows in the window stack may not be consistent across invocations. Vibe check your expectations accordingly ๐ŸŒธ

The notation described above is used as the window argument for any given command.

For example, to resize all xterms to 80x24:

xdotool search --class xterm -- windowsize --usehints %@ 80 24

Resize move the current window:

xdotool getactivewindow windowmove 0 0

In all cases, the default window argument, if omitted, will default to "%1". It is obviously an error if you omit the window argument and the window stack is empty ๐Ÿšจ And if you try to use the window stack and it's empty, that's also an error. Double error. Maximum chaos. ๐Ÿ˜ค

To activate the first firefox window found:

xdotool search --class firefox windowactivate

These would error:

xdotool windowactivate
xdotool windowactivate %1
xdotool windowactivate %@

When xdotool exits, the current window stack is lost. Gone. Poof. ๐Ÿฆ‹

Also! Commands that modify the "WINDOW STACK" will not print the results if they are not the last command. For example:

# Output the active window:
% xdotool getactivewindow
20971533

# Output the pid of the active window, but not the active window id:
% xdotool getactivewindow getwindowpid
4686

๐Ÿ›๏ธ EXTENDED WINDOW MANAGER HINTS ๐Ÿ›๏ธ

The following pieces of the EWMH standard are supported, bestie โ€” this is the alphabet soup section, but it slaps ๐Ÿ’…

_NET_SUPPORTED

Asks the window manager what is supported.

_NET_CURRENT_DESKTOP

Query and set the current desktop. Support for this enables these commands: set_desktop, get_desktop.

_NET_WM_DESKTOP

Query and set what desktop a window is living in. Support for this enables these commands: set_desktop_for_window, get_desktop_for_window.

_NET_ACTIVE_WINDOW

Allows you to query and set the active window by asking the window manager to bring it forward. Support for this enables these commands: windowactivate, getactivewindow.

_NET_WM_PID

This feature is application dependent, not window-manager dependent. Query the PID owning a given window. Support for this enables these commands: getwindowpid.


โœจ SUPPORTED FEATURES โœจ

xdotool (and libxdo) will try to function under all circumstances ๐Ÿ’ช. However, there may be some cases where functionality is justโ€ฆ not provided by your X server or by your window manager. In these cases, xdotool will try to detect that and tell you if an action requires a feature that isn't currently supported by your system. Iconic behavior, honestly ๐ŸŽ€

For window-manager specific features, see "EXTENDED WINDOW MANAGER HINTS".

XTEST

If your X server does not support XTEST, then some typing and mouse movement features may not work ๐Ÿ˜ฌ Specifically, typing and mouse actions that act on the "current window" (window 0 in libxdo) are unlikely to work.

In most cases, XTEST is a feature you can enable on your X server if it isn't enabled by default.

You can see the list of supported X extensions by typing xdpyinfo and looking for the text number of extensions: ... ๐Ÿ”


๐Ÿ’€ BUGS ๐Ÿ’€

Typing unusual symbols under non-us keybindings is known to occasionally send the wrong character. That's it. That's the bug. It happens, we know, we're working on the vibe ๐Ÿ™ˆ


๐Ÿ‘€ SEE ALSO ๐Ÿ‘€

Project site: <http://www.semicomplete.com/projects/xdotool>

Source code and Issues: <https://github.com/jordansissel/xdotool>

EWMH specification: <http://standards.freedesktop.org/wm-spec/wm-spec-1.3.html>


๐Ÿซถ CONTACT ๐Ÿซถ

Please send questions to xdotool-users@googlegroups.com. File bugs and feature requests at the following URL:

<https://github.com/jordansissel/xdotool/issues>

Alternately, if you prefer email, feel free to file bugs by emailing the list. What works for you :) ๐Ÿ’œ


๐Ÿ‘‘ AUTHOR ๐Ÿ‘‘

xdotool was written by Jordan Sissel.

This manual page was written originally by Daniel Kahn Gillmor <dkg@fifthhorseman.net> for the Debian project (but may be used by others). It is maintained by Jordan Sissel.

Patches, ideas, and other contributions by many, nice folks. See the CHANGELIST file for who provided what ๐ŸŒˆ


okay that's the whole page bestie, we did it ๐ŸŽ‰ this one's the XDOTOOL(1) manual, dated 2026-03-04 โ€” and that's the footer, so consider it mentioned with love ๐Ÿ’– go forth and windowactivate responsibly, mwah ๐Ÿงšโœจ

๐Ÿ“ฆ where this page comes from