From 0034ac6d87519655d5ebaa9dec23b3a6a974ba1f Mon Sep 17 00:00:00 2001 From: Michael Housh Date: Sun, 12 Oct 2025 20:43:11 -0400 Subject: [PATCH] feat: Adds kanatactl to manage kanata installation, building, systemd service, etc. --- env/.config/kanata/config.kbd | 23 ++- env/.local/scripts/hypr/logging | 7 +- env/.local/scripts/kanatactl | 255 ++++++++++++++++++++++++++ env/etc/systemd/system/kanata.service | 12 ++ runs/kanata | 10 +- 5 files changed, 284 insertions(+), 23 deletions(-) create mode 100755 env/.local/scripts/kanatactl create mode 100644 env/etc/systemd/system/kanata.service diff --git a/env/.config/kanata/config.kbd b/env/.config/kanata/config.kbd index 64b0ca4..a0742ad 100644 --- a/env/.config/kanata/config.kbd +++ b/env/.config/kanata/config.kbd @@ -59,7 +59,7 @@ mretu (tap-hold $tap-time $hold-time tab ret) - mlnums (tap-hold-press $tap-time $hold-time lctl (layer-while-held nums_and_symbols)) + mlnums (tap-hold-press $tap-time $hold-time esc (layer-while-held nums_and_symbols)) mrnums (tap-hold-press $tap-time $hold-time ret (layer-while-held nums_and_symbols)) ;; Multi @@ -80,21 +80,21 @@ ) ;; Blank layer -;;(deflayer {{NAME}} -;; esc XX XX XX XX XX XX XX XX XX XX XX XX ;; function row -;; grv XX XX XX XX XX XX XX XX XX XX XX XX bspc ;; number row -;; tab XX XX XX XX XX XX XX XX XX XX XX XX XX ;; top letter row -;; caps XX XX XX XX XX XX XX XX XX XX XX ret ;; home row -;; lsft XX XX XX XX XX XX XX XX XX XX XX rsft ;; bottom letter row -;; lctl lalt lmet spc rmet ralt ◀ ▼ ▶ ;; control row -;;) +;; (deflayer {{NAME}} +;; esc XX XX XX XX XX XX XX XX XX XX XX XX ;; function row +;; grv XX XX XX XX XX XX XX XX XX XX XX XX bspc ;; number row +;; tab XX XX XX XX XX XX XX XX XX XX XX XX XX ;; top letter row +;; caps XX XX XX XX XX XX XX XX XX XX XX ret ;; home row +;; lsft XX XX XX XX XX XX XX XX XX XX XX rsft ;; bottom letter row +;; lctl lalt lmet spc rmet ralt ◀ ▼ ▶ ;; control row +;; ) ;; Main layer ;; ;; Disable most function keys, and add homerow mods. ;; (deflayer main - esc brd bru XX XX XX XX XX XX XX mute vold volu + esc brdn bru XX XX XX XX XX XX XX mute vold volu grv 1 2 3 4 5 6 7 8 9 0 - = bspc tab q w e r t y u i o p [ ] \ @mlnums @mlctl @mlalt @mlmet @mlsft g h @mrsft @mrmet @mralt @mrctl ' @mrnums @@ -109,6 +109,5 @@ S-grv S-1 S-9 S-0 S-2 XX XX 1 2 3 \ XX XX XX XX [ ] S-[ S-] XX XX 4 5 6 . XX ret lsft S-7 S-8 S-4 S-3 XX XX 7 8 9 / XX rsft - lctl lalt lmet spc 0 ralt ◀ ▼ ▶ + lctl lalt lmet 0 rmet ralt ◀ ▼ ▶ ) - diff --git a/env/.local/scripts/hypr/logging b/env/.local/scripts/hypr/logging index 31a9355..5429894 100755 --- a/env/.local/scripts/hypr/logging +++ b/env/.local/scripts/hypr/logging @@ -78,7 +78,7 @@ logging() { args=() while [[ $# -gt 0 ]]; do - if [[ $1 == "-w" ]] || [[ $1 =~ ^--warn ]]; then + if [[ $1 == "-w" ]] || [[ $1 == "--warn" ]] || [[ $1 == "--warning" ]]; then log_flag="1" warn_flag="1" elif [[ $1 == "-e" ]] || [[ $1 =~ ^--error ]]; then @@ -116,9 +116,10 @@ logging() { local time=$(date '+%D %H:%M:%S') if [[ -z $file ]] || [[ -z $id ]] || [[ -z $label ]]; then - echo "Loggging not properly setup." + echo "Logging not properly setup." exit 1 fi + local prefix="[id: $id][time: $time][source: \e[32m$source_file\e[0m][\e[34m$label\e[0m] :" local m="$prefix $msg" echo -e "$m" >>"$file" @@ -127,6 +128,8 @@ logging() { # Also log errors and warning messages to the console. if [[ $error_flag == "1" ]] || [[ $warn_flag == "1" ]]; then echo -e "[id: $id]$msg" + elif [[ $warn_flag == "1" ]]; then + echo -e "[id: $id]$msg" fi else # Dry run mode, so just log to the console diff --git a/env/.local/scripts/kanatactl b/env/.local/scripts/kanatactl new file mode 100755 index 0000000..62d0b0e --- /dev/null +++ b/env/.local/scripts/kanatactl @@ -0,0 +1,255 @@ +#!/usr/bin/env bash + +set -e +set -o nounset +set -o pipefail + +SCRIPTS=${SCRIPTS:-$HOME/.local/scripts} +THIS_FILE=${BASH_SOURCE[0]} +LOG_LABEL=$(basename "$THIS_FILE") +THIS=${THIS:-$LOG_LABEL} +LOG_FILE=${LOG_FILE:-"$LOG_LABEL.log"} +XDG_DATA_HOME=${XDG_DATA_HOME:-"$HOME/.local/share"} +XDG_CONFIG_HOME=${XDG_CONFIG_HOME:-"$HOME/.config"} +DEV_ENV=${DEV_ENV:-""} + +usage() { + cat < + +FLAGS: + -h | --help: Show this help page. + +COMMANDS: + bootstrap: Bootstrap a new machine, performs installation, enables, and starts kanata systemd service. + disable: Stop and disable the kanata systemd service. + install: Build and install kanata, prompts you to choose the version to install. + logs: View the log file. + restart: Restart the kanata systemd service. + start: Enable and start the kanata systemd service. + status: Get the status of the kanata systemd service. + update: Pull from git and check for updates. + +EOF +} + +kanata_dir="$XDG_DATA_HOME/kanata" +kanata_url="https://github.com/jtroo/kanata.git" +kanata_current_version="" + +# Logging utility function, use in place of echo. +log() { + logging log --source "$THIS_FILE" "$@" +} + +# Get the version of currently installed katana execuatable, if available. +get_current_version() { + if [[ $(command -v /usr/bin/kanata) ]]; then + kanata_current_version=$(/usr/bin/kanata --version) + # Remove 'kanata ' from output of the version command. + kanata_current_version="${version#kanata *}" + fi +} + +# Get's kanat versions by git tag and filter's out versions that are less than our +# current version number. +get_versions() { + get_current_version + local rows=() + local tag="" + local has_seen_current="0" + for tag in $(git tag --list 'v*' | sort --version-sort); do + if [[ $has_seen_current == "1" ]] || [[ -z $kanata_current_version ]]; then + rows+=("$tag\n") + elif [[ $tag =~ $kanata_current_version ]]; then + has_seen_current="1" + fi + done + echo "$(echo -e "${rows[@]}" | sort --version-sort --reverse | tr -d ' ')" +} + +# Present an fzf menu to choose a version to install / update to. +prompt_for_version_to_install() { + local rows=$(get_versions) + if [[ -z $rows ]]; then + log --error "No versions to select." && exit 1 + else + echo $(printf '%s\n' "${rows[@]}" | fzf --header='Which version would you like to install?') + fi +} + +# Compares the selected version to the installed version. +compare_versions() { + # An example selection at this point: 'v1.9.0' + local selection="" + read -r selection + + if [[ $(command -v /usr/bin/kanata) ]] && [[ -n $selection ]]; then + + [[ -z $kanata_current_version ]] && get_current_version + local selected_version=${selection#v*} # remove the 'v' from selected version. + + log "Comparing selected: '$selected_version' to installed '$kanada_current_version'" + if [[ $selected_version == $kanada_current_version ]]; then + log --warn "Selected version matches the currently installed version." && exit 1 + fi + fi + echo "$selection" +} + +# Check's out the selected version tag and builds the kanata executable. +build_selection() { + # An example selection at this point: 'v1.9.0' + local selection="" + read -r selection + + if [[ -z $selection ]]; then + log --error "Selection is empty." && exit 1 + # Handle logged messages instead of an actual selection. + elif [[ $selection =~ "[WARN]" ]] || [[ $selection =~ "[ERROR]" ]]; then + echo $selection && exit 1 + fi + + if [[ $selection =~ ^v ]]; then + log "Building kanata..." + # checkout the selected version tag and build. + git checkout $selection + cargo build --release --features cmd + echo "done" + fi +} + +# Copies the most recently built kanata executable to the '/usr/bin' directory. +# +copy_to_usr_bin() { + + # This is the end of the install / update pipe, so it loops over output of + # the other commands in the pipe printing it to the console, while waiting on the + # build to be done. + while read line; do + if [[ $line == "done" ]]; then + log "Copying to '/usr/bin/kanata'" && echo "Copying to '/usr/bin/kanata'" + sudo cp target/release/kanata /usr/bin + echo "Done!" + else + echo "$line" + fi + done +} + +# Installs the systemd service files and ensures that kanata configuration has been +# installed. +install_service() { + if [[ -z $DEV_ENV ]]; then + log --error "DEV_ENV is not set properly." && exit 1 + fi + + # Ensure the configuration is copied, this is generally handled by the 'dev-env' command in + # dotfiles, but just encase it hasn't been ran or removed we need to copy the configuration + # over, otherwise the keyboard may not work. + if [[ ! -d $XDG_CONFIG_HOME/kanata ]] || [[ ! -f $XDG_CONFIG_HOME/kanata/config.kbd ]]; then + log "Copying kanata configuration." + cp -R $DEV_ENV/env/.config/kanata $XDG_CONFIG_HOME/kanata + fi + + log "Installing kanata service..." + [[ -f /etc/systemd/system/kanata.service ]] && sudo rm -rf /etc/systemd/system/kanata.service + sudo cp "$DEV_ENV/env/etc/systemd/system/kanata.service" /etc/systemd/system + sudo systemctl daemon-reload +} + +# Enables and starts the kanata systemd service. +enable_and_start_service() { + log "Enabling kanata service..." + sudo systemctl enable kanata.service + sudo systemctl start kanata.service +} + +# Get the status of the kanata systemd service. +get_status() { + log "Getting kanata service status..." + systemctl status kanata.service +} + +# Disable the kanata systemd service. +disable_service() { + log "Disabling kanata service..." + sudo systemctl stop kanata.service + sudo systemctl disable kanata.service +} + +# Restart the kanata systemd service. +restart_service() { + log "Restarting kanata service..." + sudo systemctl restart kanata.service +} + +# Handles both install or update commands, as they do the same thing, just need to pass in the +# "Updating" argument when updating, so log messages are clear. +install_or_update() { + local mode=${1:-"Installing"} + + log "$mode kanata..." + local should_pull="1" + + if [[ ! -d $kanata_dir ]]; then + log "Cloning repo." + should_pull="0" + git clone $kanata_url $kanata_dir + fi + + pushd $kanata_dir &>/dev/null + ( + [[ $should_pull == "1" ]] && git pull origin main >/dev/null 2>&1 + prompt_for_version_to_install | compare_versions | build_selection | copy_to_usr_bin + ) + popd &>/dev/null +} + +# Bootstrap a new machine, by building and installing the kanata executable, +# installing the systemd service files, and enable / start the service. +bootstrap() { + log "Bootstrapping new system..." + install_or_update + install_service + enable_and_start_service +} + +################################################################################ +# MAIN +################################################################################ + +# Setup logging file and label. +source "$SCRIPTS/hypr/logging" +setup-logging "$LOG_FILE" "$LOG_LABEL" + +while [[ $# -gt 0 ]]; do + if [[ $1 == "-h" ]] || [[ $1 == "--help" ]]; then + usage && exit 0 + elif [[ $1 == "bootstrap" ]]; then + bootstrap && exit 0 + elif [[ $1 == "disable" ]]; then + disable_service && exit $? + elif [[ $1 == "start" ]]; then + enable_and_start_service && exit 1 + elif [[ $1 == "install" ]]; then + install_or_update && exit $? + elif [[ $1 == "logs" ]]; then + bat ${LOG_DIR:-/tmp/logs}/$LOG_FILE && exit 0 + elif [[ $1 == "status" ]]; then + get_status && exit $? + elif [[ $1 == "restart" ]]; then + restart_service && exit 0 + elif [[ $1 == "update" ]]; then + install_or_update "Updating" && exit 0 + fi +done + +# If we've made it here, then we didn't handle the command. +usage && exit 1 diff --git a/env/etc/systemd/system/kanata.service b/env/etc/systemd/system/kanata.service new file mode 100644 index 0000000..30e471b --- /dev/null +++ b/env/etc/systemd/system/kanata.service @@ -0,0 +1,12 @@ +[Unit] +Description=Kanata Service +Requires=local-fs.target +After=local-fs.target + +[Service] +ExecStartPre=/usr/bin/modprobe uinput +ExecStart=/usr/bin/kanata --cfg /home/michael/.config/kanata/config.kbd +Restart=no + +[Install] +WantedBy=sysinit.target diff --git a/runs/kanata b/runs/kanata index 721aeed..bdc22c0 100755 --- a/runs/kanata +++ b/runs/kanata @@ -1,13 +1,5 @@ #!/usr/bin/env bash -XDG_DATA_HOME=${XDG_DATA_HOME:-"$HOME/.local/share"} -kanata_dir="$XDG_DATA_HOME/kanata" - yay ${1:-"-S --noconfirm"} rustup -git clone https://github.com/jtroo/kanata.git $kanata_dir -pushd $kanata_dir &>/dev/null -( - cargo install --features cmd --path . -) -popd &>/dev/null +$DEV_ENV/env/.local/scripts/kanatactl bootstrap