summaryrefslogtreecommitdiff
path: root/autoload/windowing
diff options
context:
space:
mode:
Diffstat (limited to 'autoload/windowing')
-rw-r--r--autoload/windowing/appleterminal.kak44
-rw-r--r--autoload/windowing/detection.kak71
-rw-r--r--autoload/windowing/iterm.kak124
-rw-r--r--autoload/windowing/kitty.kak84
-rw-r--r--autoload/windowing/new-client.kak9
-rw-r--r--autoload/windowing/repl/dtach.kak38
-rw-r--r--autoload/windowing/repl/kitty.kak56
-rw-r--r--autoload/windowing/repl/tmux.kak91
-rw-r--r--autoload/windowing/repl/x11.kak41
-rw-r--r--autoload/windowing/screen.kak75
-rw-r--r--autoload/windowing/sway.kak81
-rw-r--r--autoload/windowing/tmux.kak82
-rw-r--r--autoload/windowing/wayland.kak66
-rw-r--r--autoload/windowing/wezterm.kak70
-rw-r--r--autoload/windowing/x11.kak78
-rw-r--r--autoload/windowing/zellij.kak67
16 files changed, 1077 insertions, 0 deletions
diff --git a/autoload/windowing/appleterminal.kak b/autoload/windowing/appleterminal.kak
new file mode 100644
index 0000000..f47c83c
--- /dev/null
+++ b/autoload/windowing/appleterminal.kak
@@ -0,0 +1,44 @@
+# macOS Terminal.app
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+provide-module appleterminal %{
+
+# ensure that we're running in Terminal.app
+evaluate-commands %sh{
+ [ -z "${kak_opt_windowing_modules}" ] || [ "$TERM_PROGRAM" = "Apple_Terminal" ] || echo 'fail Terminal.app not detected'
+}
+
+define-command appleterminal-terminal-window -params 1.. -docstring '
+appleterminal-terminal-window <program> [<arguments>]: create a new terminal as a Terminal.app window
+The program passed as argument will be executed in the new terminal'\
+%{
+ nop %sh{
+ # join the arguments as one string for the shell execution (see x11.kak)
+ quoted=$(
+ for i in exec env "PATH=$PATH" "TMPDIR=$TMPDIR" "$@"; do
+ printf "'%s' " "$(printf %s "$i" | sed "s|'|'\\\\''|g")"
+ done
+ )
+
+ # since Terminal.app runs the command in the interactive shell, add initial space to prevent adding it to shell history
+ cmd=" $quoted"
+ echo "$cmd" | osascript \
+ -e 'set s to (do shell script "cat 0<&3")' \
+ -e 'tell application "Terminal" to do script s' >/dev/null 3<&0
+ }
+}
+complete-command appleterminal-terminal-window shell
+
+alias global terminal appleterminal-terminal-window
+
+define-command appleterminal-focus -params ..1 -docstring '
+appleterminal-focus [<client>]: focus the given client
+If no client is passed then the current one is used' \
+%{
+ fail 'Focusing Terminal.app client is unimplemented'
+}
+complete-command -menu appleterminal-focus client
+
+alias global focus appleterminal-focus
+
+}
diff --git a/autoload/windowing/detection.kak b/autoload/windowing/detection.kak
new file mode 100644
index 0000000..2fde81c
--- /dev/null
+++ b/autoload/windowing/detection.kak
@@ -0,0 +1,71 @@
+# Attempt to detect the windowing environment we're operating in
+#
+# We try to load modules from the windowing_modules str-list option in order,
+# stopping when one of the modules loads successfully. This ensures that only
+# a single module is loaded by default.
+#
+# On load each module must attempt to detect the environment it's appropriate
+# for, and if the environment isn't appropriate it must fail with an error.
+# In addition, each module must check for the length of the windowing_modules
+# str-list option defined below, and must /not/ check for an appropriate
+# environment if the list is empty. An example of this test:
+#
+# evaluate-commands %sh{
+# [ -z "${kak_opt_windowing_modules}" ] || [ -n "$TMUX" ] || echo 'fail tmux not detected'
+# }
+#
+# Each module is expected to define at least two aliases:
+# * terminal - create a new terminal with sensible defaults
+# * focus - focus the specified client, defaulting to the current client
+#
+
+declare-option -docstring \
+"Ordered list of windowing modules to try and load. An empty list disables
+both automatic module loading and environment detection, enabling complete
+manual control of the module loading." \
+str-list windowing_modules 'tmux' 'screen' 'zellij' 'kitty' 'iterm' 'appleterminal' 'sway' 'wayland' 'x11' 'wezterm'
+
+declare-option -docstring %{
+ windowing module to use in the 'terminal' command
+} str windowing_module
+
+declare-option -docstring %{
+ where to create new windows in the 'terminal' command.
+
+ Possible values:
+ - "window" (default) - new window
+ - "horizontal" - horizontal split (left/right)
+ - "vertical" - vertical split (top/bottom)
+ - "tab" - new tab besides current window
+} str windowing_placement window
+
+define-command terminal -params 1.. -docstring %{
+ terminal <program> [<arguments>]: create a new terminal using the preferred windowing environment and placement
+
+ This executes "%opt{windowing_module}-terminal-%opt{windowing_placement}" with the given arguments.
+ If the windowing module is 'wayland', 'sway' or 'x11', then the 'termcmd' option is used as terminal program.
+
+ Example usage:
+
+ terminal sh
+ evaluate-commands %{ set local windowing_placement horizontal; terminal sh }
+
+ See also the 'new' command.
+} %{
+ "%opt{windowing_module}-terminal-%opt{windowing_placement}" %arg{@}
+}
+complete-command terminal shell
+
+hook -group windowing global KakBegin .* %{
+ evaluate-commands %sh{
+ set -- ${kak_opt_windowing_modules}
+ if [ $# -gt 0 ]; then
+ echo 'try %{ '
+ while [ $# -ge 1 ]; do
+ echo "require-module ${1}; set-option global windowing_module ${1} } catch %{ "
+ shift
+ done
+ echo "echo -debug 'no windowing module detected' }"
+ fi
+ }
+}
diff --git a/autoload/windowing/iterm.kak b/autoload/windowing/iterm.kak
new file mode 100644
index 0000000..2ca46df
--- /dev/null
+++ b/autoload/windowing/iterm.kak
@@ -0,0 +1,124 @@
+# https://www.iterm2.com
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+provide-module iterm %{
+
+# ensure that we're running on iTerm
+evaluate-commands %sh{
+ [ -z "${kak_opt_windowing_modules}" ] || [ "$TERM_PROGRAM" = "iTerm.app" ] || echo 'fail iTerm not detected'
+}
+
+define-command -hidden -params 2.. iterm-terminal-impl %{
+ nop %sh{
+ direction="$1"
+ shift
+ # join the arguments as one string for the shell execution (see x11.kak)
+ args=$(
+ for i in "$@"; do
+ printf "'%s' " "$(printf %s "$i" | sed "s|'|'\\\\''|g")"
+ done
+ )
+
+ # go through another round of escaping for osascript
+ # \ -> \\
+ # " -> \"
+ do_esc() {
+ printf %s "$*" | sed -e 's|\\|\\\\|g; s|"|\\"|g'
+ }
+
+ escaped=$(do_esc "$args")
+ esc_path=$(do_esc "$PATH")
+ esc_tmp=$(do_esc "$TMPDIR")
+ cmd="env PATH='${esc_path}' TMPDIR='${esc_tmp}' $escaped"
+ if [ "$direction" = 'tab' ]; then
+ osascript \
+ -e "tell application \"iTerm\"" \
+ -e " tell current window" \
+ -e " create tab with default profile command \"${cmd}\"" \
+ -e " end tell" \
+ -e "end tell" >/dev/null
+ elif [ "$direction" = 'window' ]; then
+ osascript \
+ -e "tell application \"iTerm\"" \
+ -e " create window with default profile command \"${cmd}\"" \
+ -e "end tell" >/dev/null
+ else
+ osascript \
+ -e "tell application \"iTerm\"" \
+ -e " tell current session of current window" \
+ -e " tell (split ${direction} with same profile command \"${cmd}\") to select" \
+ -e " end tell" \
+ -e "end tell" >/dev/null
+ fi
+ }
+}
+
+define-command iterm-terminal-vertical -params 1.. -docstring '
+iterm-terminal-vertical <program> [<arguments>]: create a new terminal as an iterm pane
+The current pane is split into two, left and right
+The program passed as argument will be executed in the new terminal'\
+%{
+ iterm-terminal-impl 'vertically' %arg{@}
+}
+complete-command iterm-terminal-vertical shell
+
+define-command iterm-terminal-horizontal -params 1.. -docstring '
+iterm-terminal-horizontal <program> [<arguments>]: create a new terminal as an iterm pane
+The current pane is split into two, top and bottom
+The program passed as argument will be executed in the new terminal'\
+%{
+ iterm-terminal-impl 'horizontally' %arg{@}
+}
+complete-command iterm-terminal-horizontal shell
+
+define-command iterm-terminal-tab -params 1.. -docstring '
+iterm-terminal-tab <program> [<arguments>]: create a new terminal as an iterm tab
+The program passed as argument will be executed in the new terminal'\
+%{
+ iterm-terminal-impl 'tab' %arg{@}
+}
+complete-command iterm-terminal-tab shell
+
+define-command iterm-terminal-window -params 1.. -docstring '
+iterm-terminal-window <program> [<arguments>]: create a new terminal as an iterm window
+The program passed as argument will be executed in the new terminal'\
+%{
+ iterm-terminal-impl 'window' %arg{@}
+}
+complete-command iterm-terminal-window shell
+
+define-command iterm-focus -params ..1 -docstring '
+iterm-focus [<client>]: focus the given client
+If no client is passed then the current one is used' \
+%{
+ evaluate-commands %sh{
+ if [ $# -eq 1 ]; then
+ printf %s\\n "evaluate-commands -client '$1' focus"
+ else
+ session="${kak_client_env_ITERM_SESSION_ID#*:}"
+ osascript \
+ -e "tell application \"iTerm\" to repeat with aWin in windows" \
+ -e " tell aWin to repeat with aTab in tabs" \
+ -e " tell aTab to repeat with aSession in sessions" \
+ -e " tell aSession" \
+ -e " if (unique id = \"${session}\") then" \
+ -e " tell aWin" \
+ -e " select" \
+ -e " end tell" \
+ -e " tell aTab" \
+ -e " select" \
+ -e " end tell" \
+ -e " select" \
+ -e " end if" \
+ -e " end tell" \
+ -e " end repeat" \
+ -e " end repeat" \
+ -e "end repeat"
+ fi
+ }
+}
+complete-command -menu iterm-focus client
+
+alias global focus iterm-focus
+
+}
diff --git a/autoload/windowing/kitty.kak b/autoload/windowing/kitty.kak
new file mode 100644
index 0000000..a81f969
--- /dev/null
+++ b/autoload/windowing/kitty.kak
@@ -0,0 +1,84 @@
+# https://sw.kovidgoyal.net/kitty/index.html
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+provide-module kitty %{
+
+# ensure that we're running on kitty
+evaluate-commands %sh{
+ [ -z "${kak_opt_windowing_modules}" ] || [ "$TERM" = "xterm-kitty" ] || echo 'fail Kitty not detected'
+}
+
+declare-option -docstring %{window type that kitty creates on new and repl calls (window|os-window)} str kitty_window_type window
+
+define-command kitty-terminal-window -params 1.. -docstring '
+kitty-terminal-window <program> [<arguments>]: create a new terminal as a kitty window
+The program passed as argument will be executed in the new terminal' \
+%{
+ nop %sh{
+ match=""
+ if [ -n "$kak_client_env_KITTY_WINDOW_ID" ]; then
+ match="--match=window_id:$kak_client_env_KITTY_WINDOW_ID"
+ fi
+
+ listen=""
+ if [ -n "$kak_client_env_KITTY_LISTEN_ON" ]; then
+ listen="--to=$kak_client_env_KITTY_LISTEN_ON"
+ fi
+
+ kitty @ $listen launch --no-response --type="$kak_opt_kitty_window_type" --cwd="$PWD" $match "$@"
+ }
+}
+complete-command kitty-terminal-window shell
+
+define-command kitty-terminal-tab -params 1.. -docstring '
+kitty-terminal-tab <program> [<arguments>]: create a new terminal as kitty tab
+The program passed as argument will be executed in the new terminal' \
+%{
+ nop %sh{
+ match=""
+ if [ -n "$kak_client_env_KITTY_WINDOW_ID" ]; then
+ match="--match=window_id:$kak_client_env_KITTY_WINDOW_ID"
+ fi
+
+ listen=""
+ if [ -n "$kak_client_env_KITTY_LISTEN_ON" ]; then
+ listen="--to=$kak_client_env_KITTY_LISTEN_ON"
+ fi
+
+ kitty @ $listen launch --no-response --type=tab --cwd="$PWD" $match "$@"
+ }
+}
+complete-command kitty-terminal-tab shell
+
+define-command kitty-focus -params ..1 -docstring '
+kitty-focus [<client>]: focus the given client
+If no client is passed then the current one is used' \
+%{
+ evaluate-commands %sh{
+ if [ $# -eq 1 ]; then
+ printf "evaluate-commands -client '%s' focus" "$1"
+ else
+ match=""
+ if [ -n "$kak_client_env_KITTY_WINDOW_ID" ]; then
+ match="--match=id:$kak_client_env_KITTY_WINDOW_ID"
+ fi
+
+ listen=""
+ if [ -n "$kak_client_env_KITTY_LISTEN_ON" ]; then
+ listen="--to=$kak_client_env_KITTY_LISTEN_ON"
+ fi
+
+ kitty @ $listen focus-window --no-response $match
+ fi
+ }
+}
+complete-command -menu kitty-focus client
+
+alias global focus kitty-focus
+
+# deprecated
+define-command -hidden kitty-terminal -params 1.. %{
+ kitty-terminal-window %arg{@}
+}
+
+}
diff --git a/autoload/windowing/new-client.kak b/autoload/windowing/new-client.kak
new file mode 100644
index 0000000..f852b39
--- /dev/null
+++ b/autoload/windowing/new-client.kak
@@ -0,0 +1,9 @@
+define-command new -params .. -docstring '
+new [<commands>]: create a new Kakoune client
+The ''terminal'' command is used to determine the user''s preferred terminal emulator
+The optional arguments are passed as commands to the new client' \
+%{
+ terminal kak -c %val{session} -e "%arg{@}"
+}
+
+complete-command -menu new command
diff --git a/autoload/windowing/repl/dtach.kak b/autoload/windowing/repl/dtach.kak
new file mode 100644
index 0000000..02d11ec
--- /dev/null
+++ b/autoload/windowing/repl/dtach.kak
@@ -0,0 +1,38 @@
+provide-module dtach-repl %{
+
+# test if dtach is installed
+evaluate-commands %sh{
+ [ -n "$(command -v dtach)" ] || echo 'fail dtach not found'
+}
+
+declare-option -docstring "id of the REPL" str dtach_repl_id
+
+define-command -docstring %{
+ dtach-repl [<arguments>]: create a new terminal window for repl interaction
+ All optional parameters are forwarded to the new terminal window
+} \
+ -params .. \
+ dtach-repl %{ terminal sh -c %{
+ file="$(mktemp -u -t kak_dtach_repl.XXXXX)"
+ trap 'rm -f "${file}"' EXIT
+ printf "evaluate-commands -try-client $1 \
+ 'set-option current dtach_repl_id ${file}'" | kak -p "$2"
+ shift 2
+ dtach -c "${file}" -E sh -c "${@:-$SHELL}" || "${@:-$SHELL}"
+ } -- %val{client} %val{session} %arg{@}
+}
+complete-command dtach-repl shell
+
+define-command dtach-send-text -params 0..1 -docstring %{
+ dtach-send-text [text]: Send text to the REPL.
+ If no text is passed, then the selection is used
+ } %{
+ nop %sh{
+ printf "%s" "${@:-$kak_selection}" | dtach -p "$kak_opt_dtach_repl_id"
+ }
+}
+
+alias global repl-new dtach-repl
+alias global repl-send-text dtach-send-text
+
+}
diff --git a/autoload/windowing/repl/kitty.kak b/autoload/windowing/repl/kitty.kak
new file mode 100644
index 0000000..1f60366
--- /dev/null
+++ b/autoload/windowing/repl/kitty.kak
@@ -0,0 +1,56 @@
+hook global ModuleLoaded kitty %{
+ require-module kitty-repl
+}
+
+provide-module kitty-repl %{
+
+define-command -params .. \
+ -docstring %{
+ kitty-repl [<arguments>]: Create a new window for repl interaction.
+
+ All optional parameters are forwarded to the new window.
+ } \
+ kitty-repl %{
+ nop %sh{
+ if [ $# -eq 0 ]; then
+ cmd="${SHELL:-/bin/sh}"
+ else
+ cmd="$*"
+ fi
+
+ match=""
+ if [ -n "$kak_client_env_KITTY_WINDOW_ID" ]; then
+ match="--match=window_id:$kak_client_env_KITTY_WINDOW_ID"
+ fi
+
+ listen=""
+ if [ -n "$kak_client_env_KITTY_LISTEN_ON" ]; then
+ listen="--to=$kak_client_env_KITTY_LISTEN_ON"
+ fi
+
+ kitty @ $listen launch --no-response --keep-focus --type="$kak_opt_kitty_window_type" --title=kak_repl_window --cwd="$PWD" $match $cmd
+ }
+}
+complete-command kitty-repl shell
+
+define-command -hidden -params 0..1 \
+ -docstring %{
+ kitty-send-text [text]: Send text to the REPL window.
+
+ If no text is passed, the selection is used.
+ } \
+ kitty-send-text %{
+ nop %sh{
+ if [ $# -eq 0 ]; then
+ text="$kak_selection"
+ else
+ text="$1"
+ fi
+ kitty @ send-text --match=title:kak_repl_window "$text"
+ }
+}
+
+alias global repl-new kitty-repl
+alias global repl-send-text kitty-send-text
+
+}
diff --git a/autoload/windowing/repl/tmux.kak b/autoload/windowing/repl/tmux.kak
new file mode 100644
index 0000000..5b77633
--- /dev/null
+++ b/autoload/windowing/repl/tmux.kak
@@ -0,0 +1,91 @@
+# http://tmux.github.io/
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+# Tmux version >= 2 is required to use this module
+
+hook global ModuleLoaded tmux %{
+ require-module tmux-repl
+}
+
+provide-module tmux-repl %{
+
+declare-option -docstring "tmux pane id in which the REPL is running" str tmux_repl_id
+
+define-command -hidden -params 1.. tmux-repl-impl %{
+ evaluate-commands %sh{
+ if [ -z "$TMUX" ]; then
+ echo 'fail This command is only available in a tmux session'
+ exit
+ fi
+ tmux_args="$1"
+ if [ "${1%%-*}" = split ]; then
+ tmux_args="$tmux_args -t ${kak_client_env_TMUX_PANE}"
+ elif [ "${1%% *}" = new-window ]; then
+ session_id=$(tmux display-message -p -t ${kak_client_env_TMUX_PANE} '#{session_id}')
+ tmux_args="$tmux_args -t $session_id"
+ fi
+ shift
+ repl_pane_id=$(tmux $tmux_args -P -F '#{pane_id}' "$@")
+ printf "set-option current tmux_repl_id '%s'" "$repl_pane_id"
+ }
+}
+
+define-command tmux-repl-vertical -params 0.. -docstring "Create a new vertical pane for repl interaction" %{
+ tmux-repl-impl 'split-window -v' %arg{@}
+}
+complete-command tmux-repl-vertical shell
+
+define-command tmux-repl-horizontal -params 0.. -docstring "Create a new horizontal pane for repl interaction" %{
+ tmux-repl-impl 'split-window -h' %arg{@}
+}
+complete-command tmux-repl-horizontal shell
+
+define-command tmux-repl-window -params 0.. -docstring "Create a new window for repl interaction" %{
+ tmux-repl-impl 'new-window' %arg{@}
+}
+complete-command tmux-repl-window shell
+
+define-command -params 0..1 tmux-repl-set-pane -docstring %{
+ tmux-repl-set-pane [pane number]: Set an existing tmux pane for repl interaction
+ If the address of new pane is not given, next pane is used
+ (To get the pane number in tmux,
+ use 'tmux display-message -p '#{pane_id}'" in that pane)
+ } %{
+ evaluate-commands %sh{
+ if [ -z "$TMUX" ]; then
+ echo 'fail This command is only available in a tmux session'
+ exit
+ fi
+ if [ $# -eq 0 ]; then
+ curr_pane_no="${kak_client_env_TMUX_PANE#%}"
+ tgt_pane=$((curr_pane_no+1))
+ else
+ tgt_pane="$1"
+ fi
+ curr_win="$(tmux display-message -t ${kak_client_env_TMUX_PANE} -p '#{window_id}')"
+ if tmux list-panes -t "$curr_win" -F \#D | grep -Fxq "%"$tgt_pane; then
+ printf "set-option current tmux_repl_id '%s'" %$tgt_pane
+ else
+ echo 'fail The correct pane is not there. Activate using tmux-terminal-* or some other way'
+ fi
+ }
+}
+
+define-command -hidden tmux-send-text -params 0..1 -docstring %{
+ tmux-send-text [text]: Send text to the REPL pane.
+ If no text is passed, then the selection is used
+ } %{
+ evaluate-commands %sh{
+ if [ $# -eq 0 ]; then
+ tmux set-buffer -b kak_selection -- "${kak_selection}"
+ else
+ tmux set-buffer -b kak_selection -- "$1"
+ fi
+ tmux paste-buffer -b kak_selection -t "$kak_opt_tmux_repl_id" ||
+ echo 'fail tmux-send-text: failed to send text, see *debug* buffer for details'
+ }
+}
+
+alias global repl-new tmux-repl-horizontal
+alias global repl-send-text tmux-send-text
+
+}
diff --git a/autoload/windowing/repl/x11.kak b/autoload/windowing/repl/x11.kak
new file mode 100644
index 0000000..8f048a7
--- /dev/null
+++ b/autoload/windowing/repl/x11.kak
@@ -0,0 +1,41 @@
+hook global ModuleLoaded x11 %{
+ require-module x11-repl
+}
+
+provide-module x11-repl %{
+
+declare-option -docstring "window id of the REPL window" str x11_repl_id
+
+define-command -docstring %{
+ x11-repl [<arguments>]: create a new window for repl interaction
+ All optional parameters are forwarded to the new window
+} \
+ -params .. \
+ x11-repl %{ x11-terminal-window sh -c %{
+ winid="${WINDOWID:-$(xdotool search --pid ${PPID} | tail -1)}"
+ printf "evaluate-commands -try-client $1 \
+ 'set-option current x11_repl_id ${winid}'" | kak -p "$2"
+ shift 2;
+ [ "$1" ] && "$@" || "$SHELL"
+ } -- %val{client} %val{session} %arg{@}
+}
+complete-command x11-repl shell
+
+define-command x11-send-text -params 0..1 -docstring %{
+ x11-send-text [text]: Send text to the REPL window.
+ If no text is passed, then the selection is used
+ } %{
+ evaluate-commands %sh{
+ ([ "$#" -gt 0 ] && printf "%s" "$1" || printf "%s" "${kak_selection}" ) | xsel -i ||
+ echo 'fail x11-send-text: failed to run xsel, see *debug* buffer for details' &&
+ kak_winid=$(xdotool getactivewindow) &&
+ xdotool windowactivate "${kak_opt_x11_repl_id}" key --clearmodifiers Shift+Insert &&
+ xdotool windowactivate "${kak_winid}" ||
+ echo 'fail x11-send-text: failed to run xdotool, see *debug* buffer for details'
+ }
+}
+
+alias global repl-new x11-repl
+alias global repl-send-text x11-send-text
+
+}
diff --git a/autoload/windowing/screen.kak b/autoload/windowing/screen.kak
new file mode 100644
index 0000000..d5515c9
--- /dev/null
+++ b/autoload/windowing/screen.kak
@@ -0,0 +1,75 @@
+# http://gnu.org/software/screen/
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+
+provide-module screen %{
+
+# ensure that we're running under screen
+evaluate-commands %sh{
+ [ -z "${kak_opt_windowing_modules}" ] || [ -n "$STY" ] || echo 'fail screen not detected'
+}
+
+define-command screen-terminal-impl -hidden -params 3.. %{
+ nop %sh{
+ tty="$(ps -o tty ${kak_client_pid} | tail -n 1)"
+ screen -X eval "$1" "$2"
+ shift 2
+ # see x11.kak for what this achieves
+ args=$(
+ for i in "$@"; do
+ printf "'%s' " "$(printf %s "$i" | sed "s|'|'\\\\''|g")"
+ done
+ )
+ screen -X screen sh -c "${args} ; screen -X remove" < "/dev/$tty"
+ }
+}
+
+define-command screen-terminal-vertical -params 1.. -docstring '
+screen-terminal-vertical <program> [<arguments>]: create a new terminal as a screen pane
+The current pane is split into two, left and right
+The program passed as argument will be executed in the new terminal' \
+%{
+ screen-terminal-impl 'split -v' 'focus right' %arg{@}
+}
+complete-command screen-terminal-vertical shell
+
+define-command screen-terminal-horizontal -params 1.. -docstring '
+screen-terminal-horizontal <program> [<arguments>]: create a new terminal as a screen pane
+The current pane is split into two, top and bottom
+The program passed as argument will be executed in the new terminal' \
+%{
+ screen-terminal-impl 'split -h' 'focus down' %arg{@}
+}
+complete-command screen-terminal-horizontal shell
+
+define-command screen-terminal-window -params 1.. -docstring '
+screen-terminal-window <program> [<arguments>]: create a new terminal as a screen window
+The program passed as argument will be executed in the new terminal' \
+%{
+ nop %sh{
+ tty="$(ps -o tty ${kak_client_pid} | tail -n 1)"
+ screen -X screen "$@" < "/dev/$tty"
+ }
+}
+complete-command screen-terminal-window shell
+
+define-command screen-focus -params ..1 -docstring '
+screen-focus [<client>]: focus the given client
+If no client is passed then the current one is used' \
+%{
+ evaluate-commands %sh{
+ if [ $# -eq 1 ]; then
+ printf %s\\n "
+ evaluate-commands -client '$1' focus
+ "
+ elif [ -n "${kak_client_env_STY}" ]; then
+ tty="$(ps -o tty ${kak_client_pid} | tail -n 1)"
+ screen -X select "${kak_client_env_WINDOW}" < "/dev/$tty"
+ fi
+ }
+}
+complete-command -menu screen-focus client
+
+alias global focus screen-focus
+
+}
diff --git a/autoload/windowing/sway.kak b/autoload/windowing/sway.kak
new file mode 100644
index 0000000..c4501c2
--- /dev/null
+++ b/autoload/windowing/sway.kak
@@ -0,0 +1,81 @@
+provide-module sway %{
+
+# Ensure we're actually in Sway
+evaluate-commands %sh{
+ [ -z "${kak_opt_windowing_modules}" ] ||
+ [ -n "$SWAYSOCK" ] ||
+ echo 'fail SWAYSOCK is not set'
+}
+
+require-module 'wayland'
+
+alias global sway-terminal-window wayland-terminal-window
+
+define-command sway-terminal-vertical -params 1.. -docstring '
+ sway-terminal-vertical <program> [<arguments>]: create a new terminal as a Sway window
+ The current pane is split into two, top and bottom
+ The program passed as argument will be executed in the new terminal' \
+%{
+ nop %sh{swaymsg split vertical}
+ wayland-terminal-window %arg{@}
+}
+complete-command sway-terminal-vertical shell
+
+define-command sway-terminal-horizontal -params 1.. -docstring '
+ sway-terminal-horizontal <program> [<arguments>]: create a new terminal as a Sway window
+ The current pane is split into two, left and right
+ The program passed as argument will be executed in the new terminal' \
+%{
+ nop %sh{swaymsg split horizontal}
+ wayland-terminal-window %arg{@}
+}
+complete-command sway-terminal-horizontal shell
+
+define-command sway-terminal-tab -params 1.. -docstring '
+ sway-terminal-tab <program> [<arguments>]: create a new terminal as a Sway window
+ The program passed as argument will be executed in the new terminal' \
+%{
+ nop %sh{swaymsg 'split horizontal; layout tabbed'}
+ wayland-terminal-window %arg{@}
+}
+complete-command sway-terminal-tab shell
+
+define-command sway-focus-pid -hidden %{
+ evaluate-commands %sh{
+ pid=$kak_client_pid
+
+ # Try to focus a window with the current PID, walking up the tree of
+ # parent processes until the focus eventually succeeds
+ while ! swaymsg [pid=$pid] focus > /dev/null 2> /dev/null ; do
+ # Replace the current PID with its parent PID
+ pid=$(ps -p $pid -o ppid=)
+
+ # If we couldn't get a PPID for some reason, or it's 1 or less, we
+ # should just fail.
+ if [ -z $pid ] || [ $pid -le 1 ]; then
+ echo "fail Can't find PID for Sway window to focus"
+ break
+ fi
+ done
+ }
+}
+
+define-command sway-focus -params ..1 -docstring '
+sway-focus [<kakoune_client>]: focus a given client''s window.
+If no client is passed, then the current client is used' \
+%{
+ # Quick branch to make sure we're calling sway-focus-pid from the client
+ # the user wants to focus on.
+ evaluate-commands %sh{
+ if [ $# -eq 1 ]; then
+ printf "evaluate-commands -client '%s' sway-focus-pid" "$1"
+ else
+ echo sway-focus-pid
+ fi
+ }
+}
+complete-command -menu sway-focus client
+
+alias global focus sway-focus
+
+}
diff --git a/autoload/windowing/tmux.kak b/autoload/windowing/tmux.kak
new file mode 100644
index 0000000..2d43db0
--- /dev/null
+++ b/autoload/windowing/tmux.kak
@@ -0,0 +1,82 @@
+# http://tmux.github.io/
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+provide-module tmux %{
+
+# ensure we're running under tmux
+evaluate-commands %sh{
+ [ -z "${kak_opt_windowing_modules}" ] || [ -n "$TMUX" ] || echo 'fail tmux not detected'
+}
+
+define-command -hidden -params 2.. tmux-terminal-impl %{
+ evaluate-commands %sh{
+ tmux=${kak_client_env_TMUX:-$TMUX}
+ if [ -z "$tmux" ]; then
+ echo "fail 'This command is only available in a tmux session'"
+ exit
+ fi
+ tmux_args="$1"
+ if [ "${1%%-*}" = split ]; then
+ tmux_args="$tmux_args -t ${kak_client_env_TMUX_PANE}"
+ elif [ "${1%% *}" = new-window ]; then
+ session_id=$(tmux display-message -p -t ${kak_client_env_TMUX_PANE} '#{session_id}')
+ tmux_args="$tmux_args -t $session_id"
+ fi
+ shift
+ # ideally we should escape single ';' to stop tmux from interpreting it as a new command
+ # but that's probably too rare to care
+ if [ -n "$TMPDIR" ]; then
+ TMUX=$tmux tmux $tmux_args env TMPDIR="$TMPDIR" "$@"
+ else
+ TMUX=$tmux tmux $tmux_args "$@"
+ fi
+ }
+}
+
+define-command tmux-terminal-vertical -params 1.. -docstring '
+tmux-terminal-vertical <program> [<arguments>]: create a new terminal as a tmux pane
+The current pane is split into two, top and bottom
+The program passed as argument will be executed in the new terminal' \
+%{
+ tmux-terminal-impl 'split-window -v' %arg{@}
+}
+complete-command tmux-terminal-vertical shell
+
+define-command tmux-terminal-horizontal -params 1.. -docstring '
+tmux-terminal-horizontal <program> [<arguments>]: create a new terminal as a tmux pane
+The current pane is split into two, left and right
+The program passed as argument will be executed in the new terminal' \
+%{
+ tmux-terminal-impl 'split-window -h' %arg{@}
+}
+complete-command tmux-terminal-horizontal shell
+
+define-command tmux-terminal-window -params 1.. -docstring '
+tmux-terminal-window <program> [<arguments>]: create a new terminal as a tmux window
+The program passed as argument will be executed in the new terminal' \
+%{
+ tmux-terminal-impl 'new-window' %arg{@}
+}
+complete-command tmux-terminal-window shell
+
+define-command tmux-focus -params ..1 -docstring '
+tmux-focus [<client>]: focus the given client
+If no client is passed then the current one is used' \
+%{
+ evaluate-commands %sh{
+ if [ $# -eq 1 ]; then
+ printf "evaluate-commands -client '%s' focus" "$1"
+ elif [ -n "${kak_client_env_TMUX}" ]; then
+ # select-pane makes the pane active in the window, but does not select the window. Both select-pane
+ # and select-window should be invoked in order to select a pane on a currently not focused window.
+ TMUX="${kak_client_env_TMUX}" tmux select-window -t "${kak_client_env_TMUX_PANE}" \; \
+ select-pane -t "${kak_client_env_TMUX_PANE}" > /dev/null
+ fi
+ }
+}
+complete-command -menu tmux-focus client
+
+## The default behaviour for the `new` command is to open an horizontal pane in a tmux session
+alias global focus tmux-focus
+
+}
diff --git a/autoload/windowing/wayland.kak b/autoload/windowing/wayland.kak
new file mode 100644
index 0000000..33737f7
--- /dev/null
+++ b/autoload/windowing/wayland.kak
@@ -0,0 +1,66 @@
+# wayland
+
+provide-module wayland %{
+
+# ensure that we're running in the right environment
+evaluate-commands %sh{
+ [ -z "${kak_opt_windowing_modules}" ] || [ -n "$WAYLAND_DISPLAY" ] || echo 'fail WAYLAND_DISPLAY is not set'
+}
+
+# termcmd should be set such as the next argument is the whole
+# command line to execute
+declare-option -docstring %{shell command run to spawn a new terminal
+A shell command is appended to the one set in this option at runtime} \
+ str termcmd %sh{
+ for termcmd in 'alacritty -e sh -c' \
+ 'kitty sh -c' \
+ 'foot sh -c' \
+ 'termite -e ' \
+ 'wterm -e sh -c' \
+ 'gnome-terminal -e ' \
+ 'xfce4-terminal -e ' \
+ 'konsole -e '; do
+ terminal=${termcmd%% *}
+ if command -v $terminal >/dev/null 2>&1; then
+ printf %s\\n "$termcmd"
+ exit
+ fi
+ done
+}
+
+define-command wayland-terminal-window -params 1.. -docstring '
+wayland-terminal-window <program> [<arguments>]: create a new terminal as a Wayland window
+The program passed as argument will be executed in the new terminal' \
+%{
+ evaluate-commands -save-regs 'a' %{
+ set-register a %arg{@}
+ evaluate-commands %sh{
+ if [ -z "${kak_opt_termcmd}" ]; then
+ echo "fail 'termcmd option is not set'"
+ exit
+ fi
+ termcmd=$kak_opt_termcmd
+ args=$kak_quoted_reg_a
+ unset kak_opt_termcmd kak_quoted_reg_a
+ setsid ${termcmd} "$args" < /dev/null > /dev/null 2>&1 &
+ }
+ }
+}
+complete-command wayland-terminal-window shell
+
+define-command wayland-focus -params ..1 -docstring '
+wayland-focus [<kakoune_client>]: focus a given client''s window
+If no client is passed, then the current client is used' \
+%{
+ fail 'Focusing specific windows in most Wayland window managers is unsupported'
+}
+complete-command -menu wayland-focus client
+
+alias global focus wayland-focus
+
+# deprecated
+define-command -hidden wayland-terminal -params 1.. %{
+ wayland-terminal-window %arg{@}
+}
+
+}
diff --git a/autoload/windowing/wezterm.kak b/autoload/windowing/wezterm.kak
new file mode 100644
index 0000000..1e3fafd
--- /dev/null
+++ b/autoload/windowing/wezterm.kak
@@ -0,0 +1,70 @@
+# https://wezfurlong.org/wezterm/index.html
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+
+provide-module wezterm %{
+
+# ensure that we're running under screen
+evaluate-commands %sh{
+ [ -z "${kak_opt_windowing_modules}" ] || [ -n "$WEZTERM_UNIX_SOCKET" ] || echo 'fail wezterm not detected'
+}
+
+define-command wezterm-terminal-impl -hidden -params 2.. %{
+ nop %sh{
+ wezterm cli "$@"
+ }
+}
+
+define-command wezterm-terminal-vertical -params 1.. -docstring '
+wezterm-terminal-vertical <program> [<arguments>]: create a new terminal as a wezterm pane
+The current pane is split into two, top and bottom
+The program passed as argument will be executed in the new terminal' \
+%{
+ wezterm-terminal-impl split-pane --cwd "%val{client_env_PWD}" --bottom --pane-id "%val{client_env_WEZTERM_PANE}" -- %arg{@}
+}
+complete-command wezterm-terminal-vertical shell
+
+define-command wezterm-terminal-horizontal -params 1.. -docstring '
+wezterm-terminal-horizontal <program> [<arguments>]: create a new terminal as a wezterm pane
+The current pane is split into two, left and right
+The program passed as argument will be executed in the new terminal' \
+%{
+ wezterm-terminal-impl split-pane --cwd "%val{client_env_PWD}" --right --pane-id "%val{client_env_WEZTERM_PANE}" -- %arg{@}
+}
+complete-command wezterm-terminal-horizontal shell
+
+define-command wezterm-terminal-tab -params 1.. -docstring '
+wezterm-terminal-tab <program> [<arguments>]: create a new terminal as a wezterm tab
+The program passed as argument will be executed in the new terminal' \
+%{
+ wezterm-terminal-impl spawn --cwd "%val{client_env_PWD}" --pane-id "%val{client_env_WEZTERM_PANE}" -- %arg{@}
+}
+complete-command wezterm-terminal-tab shell
+
+define-command wezterm-terminal-window -params 1.. -docstring '
+wezterm-terminal-window <program> [<arguments>]: create a new terminal as a wezterm window
+The program passed as argument will be executed in the new terminal' \
+%{
+ wezterm-terminal-impl spawn --cwd "%val{client_env_PWD}" --new-window --pane-id "%val{client_env_WEZTERM_PANE}" -- %arg{@}
+}
+complete-command wezterm-terminal-window shell
+
+define-command wezterm-focus -params ..1 -docstring '
+wezterm-focus [<client>]: focus the given client
+If no client is passed then the current one is used' \
+%{
+ evaluate-commands %sh{
+ if [ $# -eq 1 ]; then
+ printf %s\\n "
+ evaluate-commands -client '$1' focus
+ "
+ elif [ -n "${kak_client_env_WEZTERM_PANE}" ]; then
+ wezterm cli activate-pane --pane-id "${kak_client_env_WEZTERM_PANE}" > /dev/null 2>&1
+ fi
+ }
+}
+complete-command -menu wezterm-focus client
+
+alias global focus wezterm-focus
+
+}
diff --git a/autoload/windowing/x11.kak b/autoload/windowing/x11.kak
new file mode 100644
index 0000000..f65718b
--- /dev/null
+++ b/autoload/windowing/x11.kak
@@ -0,0 +1,78 @@
+# x11
+
+provide-module x11 %{
+
+# ensure that we're running in the right environment
+evaluate-commands %sh{
+ [ -z "${kak_opt_windowing_modules}" ] || [ -n "$DISPLAY" ] || echo 'fail DISPLAY is not set'
+}
+
+# termcmd should be set such as the next argument is the whole
+# command line to execute
+declare-option -docstring %{shell command run to spawn a new terminal
+A shell command is appended to the one set in this option at runtime} \
+ str termcmd %sh{
+ for termcmd in 'alacritty -e sh -c' \
+ 'kitty sh -c' \
+ 'termite -e ' \
+ 'urxvt -e sh -c' \
+ 'rxvt -e sh -c' \
+ 'st -e sh -c' \
+ 'xterm -e sh -c' \
+ 'roxterm -e sh -c' \
+ 'mintty -e sh -c' \
+ 'sakura -x ' \
+ 'gnome-terminal -e ' \
+ 'xfce4-terminal -x sh -c' \
+ 'konsole -e '; do
+ terminal=${termcmd%% *}
+ if command -v $terminal >/dev/null 2>&1; then
+ printf %s\\n "$termcmd"
+ exit
+ fi
+ done
+}
+
+define-command x11-terminal-window -params 1.. -docstring '
+x11-terminal-window <program> [<arguments>]: create a new terminal as an X11 window
+The program passed as argument will be executed in the new terminal' \
+%{
+ evaluate-commands -save-regs 'a' %{
+ set-register a %arg{@}
+ evaluate-commands %sh{
+ if [ -z "${kak_opt_termcmd}" ]; then
+ echo "fail 'termcmd option is not set'"
+ exit
+ fi
+ termcmd=$kak_opt_termcmd
+ args=$kak_quoted_reg_a
+ unset kak_opt_termcmd kak_quoted_reg_a
+ setsid ${termcmd} "$args" < /dev/null > /dev/null 2>&1 &
+ }
+ }
+}
+complete-command x11-terminal-window shell
+
+define-command x11-focus -params ..1 -docstring '
+x11-focus [<kakoune_client>]: focus a given client''s window
+If no client is passed, then the current client is used' \
+%{
+ evaluate-commands %sh{
+ if [ $# -eq 1 ]; then
+ printf "evaluate-commands -client '%s' focus" "$1"
+ else
+ xdotool windowactivate $kak_client_env_WINDOWID > /dev/null ||
+ echo 'fail failed to run x11-focus, see *debug* buffer for details'
+ fi
+ }
+}
+complete-command -menu x11-focus client
+
+alias global focus x11-focus
+
+# deprecated
+define-command -hidden x11-terminal -params 1.. %{
+ x11-terminal-window %arg{@}
+}
+
+}
diff --git a/autoload/windowing/zellij.kak b/autoload/windowing/zellij.kak
new file mode 100644
index 0000000..71f962e
--- /dev/null
+++ b/autoload/windowing/zellij.kak
@@ -0,0 +1,67 @@
+# https://zellij.dev/
+# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
+
+provide-module zellij %{
+
+# ensure we're running under zellij
+evaluate-commands %sh{
+ [ -z "${kak_opt_windowing_modules}" ] || [ -n "$ZELLIJ" -a -n "$ZELLIJ_SESSION_NAME" ] || echo 'fail zellij not detected'
+}
+
+define-command -params 1.. zellij-action %{ nop %sh{
+ zellij --session "$kak_client_env_ZELLIJ_SESSION_NAME" action "$@"
+}}
+define-command -hidden -params 2.. zellij-run %{ nop %sh{
+ zellij_run_options=$1
+ shift
+ zellij --session "$kak_client_env_ZELLIJ_SESSION_NAME" run $zellij_run_options -- "$@"
+}}
+
+define-command -hidden -params 1.. zellij-terminal-window %{
+ zellij-run "--close-on-exit" %arg{@}
+}
+complete-command zellij-terminal-window shell
+
+define-command zellij-terminal-vertical -params 1.. -docstring '
+zellij-terminal-vertical <program> [<arguments>]: create a new terminal as a zellij pane
+The current pane is split into two, top and bottom
+The program passed as argument will be executed in the new terminal' \
+%{
+ zellij-run "--direction down" %arg{@}
+}
+complete-command zellij-terminal-vertical shell
+
+define-command zellij-terminal-horizontal -params 1.. -docstring '
+zellij-terminal-horizontal <program> [<arguments>]: create a new terminal as a zellij pane
+The current pane is split into two, left and right
+The program passed as argument will be executed in the new terminal' \
+%{
+ zellij-run "--direction right" %arg{@}
+}
+complete-command zellij-terminal-horizontal shell
+
+define-command zellij-focus -params ..1 -docstring '
+zellij-focus [<client>]: focus the given client
+If no client is passed then the current one is used' \
+%{ evaluate-commands %sh{
+ if [ $# -eq 1 ]; then
+ printf "evaluate-commands -client '%s' focus" "$1"
+ elif [ -n "${kak_client_env_ZELLIJ}" ]; then
+ output=$(mktemp -d "${TMPDIR:-/tmp}"/kak-zellij.XXXXXXXX)/dump-screen
+ pane_count=0
+ while [ $((pane_count+=1)) -lt 10 ]; do
+ if zellij action dump-screen "$output" && grep "${kak_client}@\[$kak_session\]" "$output" > /dev/null ; then
+ break;
+ fi
+ zellij action focus-next-pane
+ done
+ rm -r $(dirname $output)
+ fi
+}}
+complete-command -menu zellij-focus client
+
+## The default behaviour for the `new` command is to open an horizontal pane in a zellij session
+alias global focus zellij-focus
+
+}
+