1# First arg always left|right. No need for guards b/c this is private function.
2direction=$1
3shift
4# create fifo for collecting event stream
5temp=$(mktemp -d kak-niri-XXXXXX)
6fifo="$temp/fifo"
7mkfifo "$fifo"
8# register func to cleanup fifo
9cleanup() {
10 rm -rf "$temp"
11}
12trap cleanup EXIT INT TERM
13# read event stream to fifo
14niri msg -j event-stream >"$fifo" &
15stream_pid=$!
16# register timeout fallback
17(
18 sleep 3
19 kill "$!" 2>/dev/null
20 kill "$stream_pid" 2>/dev/null
21) >/dev/null 2>&1 </dev/null &
22watchdog_pid=$!
23
24# safely quote arguments for passing to new terminal
25kakquote() {
26 set -- "$*" ""
27 while [ "${1#*\'}" != "$1" ]; do
28 set -- "${1#*\'}" "$2${1%%\'*}''"
29 done
30 printf "'%s' " "$2$1"
31}
32# loop over args and quote all
33quoted_args=''
34for arg in "$@"; do
35 quoted_args="$quoted_args$(kakquote "$arg")"
36done
37# run this block async to avoid user-facing lag
38{
39 # track when new window is ready
40 ready=false
41 # read lines of event stream from fifo
42 # Note: each line is one event.
43 # We wait for the event that indicates stream is initialized,
44 # then we spawn the new terminal window. The next WindowOpenedOrChanged
45 # event immediately after running niri-terminal-window SHOULD be
46 # the event for this window, but it's not guaranteed. There is a very,
47 # very tiny window for a race condition here. Unfortunately, I don't
48 # believe we have a good way to handle it because there's no way to
49 # know the window's ID before it's spawned. This may be a good job for
50 # the Ostrich algorithm (https://en.wikipedia.org/wiki/Ostrich_algorithm)
51 while IFS= read -r line; do
52 # each line is a JSON object
53 case "$line" in
54 # this line means stream is initialized
55 *"OverviewOpenedOrClosed"*)
56 # guard to avoid spawning terminal twice
57 if [ "$ready" = false ]; then
58 ready=true
59 # spawn terminal with command
60 printf 'niri-terminal-window %s' "$quoted_args" >"$kak_command_fifo"
61 fi
62 ;;
63 # newly opened window is now available to act on
64 *"WindowOpenedOrChanged"*)
65 # only operate if window isn't floating
66 # This accursed incantation is to access the JSON field
67 # without depending on jq.
68 if expr "${line}" : '.*"is_floating":[[:space:]]*false.*' \
69 >/dev/null; then
70 # handle new window being unfocused
71 if expr "${line}" : '.*"is_focused":[[:space:]]*false.*' \
72 >/dev/null; then
73 # extract the window ID
74 # (POSIX compliant JSON parsing is ugly, sorry)
75 id="$(echo "$line" | sed -n 's/.*"id":[[:space:]]*\([0-9][0-9]*\).*/\1/p')"
76 # focus the new window
77 niri msg action focus-window --id "$id"
78 fi
79 # consume the new window in specified direction
80 niri msg action consume-or-expel-window-${direction}
81 fi
82 # close the stream reader and timeout watchdog
83 kill "$stream_pid"
84 wait "$stream_pid" 2>/dev/null
85 kill "$watchdog_pid" 2>/dev/null
86 exit 0
87 ;;
88 esac
89 done <"$fifo"
90} >/dev/null 2>&1 </dev/null &