2

After a decade on Linux, I've recently transitioned to an ARM MacBook for its convenience and user experience.

On my previous Linux-powered HP, I configured the Caps Lock LED to indicate when the Bulgarian keyboard layout was active. I primarily use English and Bulgarian input languages, so having the Caps Lock LED illuminate for Bulgarian (and remain off for English) would be ideal.

I've already set up my Caps Lock key to switch input languages on the Mac. Is there a straightforward method to replicate the LED behavior here?

Note: I frequently use applications in full-screen mode, obscuring the top bar, which is why I prefer a visual keyboard indicator to avoid the need to switch out of full-screen to check the current language, thereby reducing typing errors.

Thank you in advance!

4
  • 1
    MacOS Sonoma comes with a visual indicator which most people want to get rid of, but which you might find helpful? apple.stackexchange.com/questions/466101/… Commented Feb 6 at 14:57
  • 1
    I appreciate your suggestion and I actually love this feature. I think Gnome should also implement it in the future. The thing is this indicator only becomes visible once you actually change the language, which is an additional step. My main goal is high productivity configuration.
    – bteo
    Commented Feb 6 at 15:29
  • Why, though? Would displaying the keyboard type on the top menu ribbon not work for you? Commented Feb 9 at 18:38
  • Just because I notice flashing lights better than changes in UI. xD
    – bteo
    Commented Feb 9 at 18:56

1 Answer 1

5

Okay, I've developed a solution that I believe will be beneficial for Apple Silicon Macs running Sonoma and likely compatible with other macOS versions as well. My approach uses a bash script to manage the Caps Lock LED based on the keyboard layout, activating it for layouts other than "U.S." and deactivating it for the "U.S." layout. This script is both CPU and battery efficient.

It leverages a Mac version of the setleds binary, akin to the one found on Linux, available here: https://github.com/damieng/setledsmac.

Below is the bash script, complete with --install, --uninstall, and --help options for convenience.

#!/bin/bash
# This is the path where setleds resides on my machine
export PATH=$PATH:$HOME/Tools/Shell

# Define the plist path
plist_path="$HOME/Library/LaunchAgents/com.example.keyboardlayoutmonitor.plist"
plist_label="com.example.keyboardlayoutmonitor"

# Obtain the full path of the current script
script_path=$(realpath "$BASH_SOURCE")

function install_service {
    # Create a LaunchAgents directory if it doesn't exist
    mkdir -p "$HOME/Library/LaunchAgents"
    
    # Create the plist file
    cat << EOF > "$plist_path"
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>$plist_label</string>
    <key>ProgramArguments</key>
    <array>
        <string>$script_path</string>
        <string>--main</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
</dict>
</plist>
EOF

    # Load the plist to start the service
    launchctl load "$plist_path"
    echo "Service installed and started."
}

function uninstall_service {
    # Unload the plist to stop the service
    launchctl unload "$plist_path"
    # Remove the plist file
    rm -f "$plist_path"
    echo "Service uninstalled."
}

function show_help {
    echo "Usage: $(basename "$0") [OPTION]"
    echo "--install    Install the service to start automatically at user login."
    echo "--uninstall  Uninstall and stop the service, removing it from automatic startup."
    echo "--help       Display this help and exit."
    echo "--main       Run the main script functionality. Intended for internal use when the service starts."
    echo ""
    echo "The --install option sets up the service to be automatically executed at user login through launchd, ensuring the script runs in the background and monitors keyboard layout changes."
}

function main_execution {
    # Specify the log file location in StudlyCase
    log_file="$HOME/Library/Logs/KeyboardLayoutMonitor.log"

    # Redirecting standard output and standard error to the log file
    exec >> "$log_file" 2>&1

    # Existing main_execution function continues here...

    # Initialize previous layout variable
    previous_layout=""
    
    while true; do
      current_layout=$(defaults read ~/Library/Preferences/com.apple.HIToolbox.plist AppleSelectedInputSources | grep -o '"KeyboardLayout Name" = "[^"]*"' | awk -F'"' '{print $(NF-1)}' | head -n 1)

      # Only update LED if layout has changed
      if [ "$current_layout" != "$previous_layout" ]; then
        if [ "$current_layout" == "U.S." ]; then
          setleds -caps > /dev/null
        else
          setleds +caps > /dev/null
        fi
        # Update previous layout to current layout
        previous_layout="$current_layout"
      fi

      sleep 0.25
    done
}

# Main script logic for parameters handling
case "$1" in
    --install)
        install_service
        ;;
    --uninstall)
        uninstall_service
        ;;
    --help)
        show_help
        ;;
    --main)
        main_execution
        ;;
    *)
        echo "Error: Invalid argument."
        show_help
        ;;
esac

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .