Table of Contents
Overview
When switching from Windows to Linux, the thing I miss most is AutoHotkey, especially the ability to quickly create shortcuts/hotkeys to start/switch applications. For example, press Alt+J to start Intellij (if it’s not running yet, otherwise, just activate and focus to that window.).
With X11 being in maintenance mode, I got to switch to Wayland. Most of things are great but wmctrl broke on Wayland so I’m on my journey to find the solution for my issue again. Good thing I found kdotool.
In this post, I’m going to show you how you can do the same and quickly get the hotkeys setup so you can quickly change windows -> Rely less on your mouse/touchpad.
Step 1: Install kdotool
I’m on arch but I believe you can install kdotool on other distros too.
yay -S kdotool
Setp 2: Find your window class
Run the following to get your window class:
(echo "Window-ID Class"; for id in $(kdotool search .); do echo "$id $(kdotool getwindowclassname "$id")"; done) | column -t
This command will list all of your open windows, you will need to get the desired window class for the next step

Step 3: Find the executable of the equivalent class
The next step is to find the executable of the program with the class you want to create hotkey for. The most straight forward way is to use the which command

This is all you need for step 4, create the script to do the logic.
Step 3: Create script to run/activate the program
Here is the bash script to activate the program’s window or run the executable if it’s not already running
#!/bin/bash
#
# smart-focus.sh (kdotool - Search-and-Verify Version with Debugging)
#
# This script launches an app or focuses it if already running.
# It implements a robust "search, then verify" flow.
# --- CONFIGURATION ---
# Set to true for verbose output, or false to disable
DEBUG=false
LOG_FILE="/tmp/smart-focus.log"
CLASS_NAME="$1"
# ---------------------
# A simple function to print debug messages to stderr
debug() {
    if [ "$DEBUG" = true ]; then
        echo "[DEBUG] $1" >> $LOG_FILE
    fi
}
debug "Script started for class: '$CLASS_NAME'"
# 1. --- APPLICATION MAP ---
declare -A LAUNCH_MAP
LAUNCH_MAP["jetbrains-idea"]="cd /home/dat/data/jetbrains/intellij-idea-ultimate/bin/ && ./idea.sh &"
LAUNCH_MAP["firefox"]="firefox &"
LAUNCH_MAP["google-chrome"]="google-chrome-stable &"
debug "Application map loaded."
# -------------------------
# 2. Check arguments and map
if [ -z "$CLASS_NAME" ]; then
    echo "Usage: $0 \"resourceClass\"" >&2
    debug "Error: No resourceClass argument provided. Exiting."
    exit 1
fi
debug "Argument check passed."
if ! [[ -v LAUNCH_MAP[$CLASS_NAME] ]]; then
    echo "Error: Unknown resourceClass '$CLASS_NAME'." >&2
    echo "Please add it to the 'APPLICATION MAP' in $0" >&2
    debug "Error: resourceClass '$CLASS_NAME' not found in map. Exiting."
    exit 1
fi
debug "resourceClass '$CLASS_NAME' found in map."
# 3. Check for kdotool
if ! command -v kdotool &> /dev/null; then
    echo "Error: kdotool is not installed. Please install it from the AUR." >&2
    debug "Error: kdotool command not found. Exiting."
    exit 1
fi
debug "kdotool command found at: $(which kdotool)"
# 4. Implement your "Search, Inspect, Activate" flow
#
# Get the launch command now, in case we need it
LAUNCH_COMMAND=${LAUNCH_MAP[$CLASS_NAME]}
debug "Launch command set to: $LAUNCH_COMMAND"
# Search for all possible window IDs
debug "Searching for windows with class: $CLASS_NAME"
POSSIBLE_IDS=$(kdotool search --class "$CLASS_NAME")
FOUND_ID=""
if [ -z "$POSSIBLE_IDS" ]; then
    debug "Search returned no results."
else
    # Read the IDs into an array to handle multi-line output
    readarray -t ID_ARRAY <<< "$POSSIBLE_IDS"
    debug "Search returned ${#ID_ARRAY[@]} IDs: ${ID_ARRAY[*]}"
    
    # If we have results, loop through them
    for id in "${ID_ARRAY[@]}"; do
        debug "Inspecting ID: $id"
        # Inspect the class name for an exact match
        ACTUAL_CLASS=$(kdotool getwindowclassname "$id")
        debug "  -> Actual class: '$ACTUAL_CLASS'"
        
        if [ "$ACTUAL_CLASS" = "$CLASS_NAME" ]; then
            debug "  -> Match! This is the window."
            # Found an exact match!
            FOUND_ID="$id"
            break # Stop looping, we only want the first match
        else
            debug "  -> No match. Continuing loop."
        fi
    done
fi
# 5. Take action
if [ -n "$FOUND_ID" ]; then
    # An exact match was found and verified
    debug "Action: Activating window $FOUND_ID"
    kdotool windowactivate "$FOUND_ID"
else
    # No results, or results were not an exact match
    debug "Action: No matching window found. Executing launch command."
    eval $LAUNCH_COMMAND
fi
debug "Script finished."
The most important thing in this script is LAUNCH_MAP, here you will create a map (or associative array) of the class and the executable. In my code here, you can see that I setup for firefox, chrome and intellij.
Let’s save this script somewhere on your machine and make it executable by calling chmod +x script_name.sh. I created the file at this location /home/dat/scripts/active-windows/activate.sh but you can put it any where on your machine as you see fit.
You need the path for the next step
Step 5: Create the hotkey
Open system settings and search for Shortcuts
Click on add new and enter the following command:
/home/dat/scripts/active-windows/activate.sh "YOUR_PROGRAM_CLASS"
For example, I want to activate intellij, I would do this:
/home/dat/scripts/active-windows/activate.sh "jetbrains-idea"

Click on add and then assign a shortcut for this command

Press your desired shortcut and it’s done

Now the setup is ready. You may need to logout/restart your computer for the change to take effect.
Conclusion
In this post, I’ve shown you how to setup hotkeys/shortcuts to activate programs in KDE Wayland. It really helps me find the correct application. Hope this is helpful to you too.

I build softwares that solve problems. I also love writing/documenting things I learn/want to learn.