summary refs log tree commit diff
path: root/bin/swx
blob: 11efac0a7bde1a97263f47c2ba3aebc5cc6c152e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#!/bin/sh
#
# DESCRIPTION:
#  wrapper for `swaymsg exec` with many useful additions
#

set -e
[ -n "$DEBUG" ] && set -x

# check requirements
for app in swaymsg jq swq centercon; do
	if ! command -v "$app" >/dev/null; then
		printf "%s: %s not install but required" "$0" "$app" >&2
		exit 1
	fi
done

patience=
border=
wspace=
output=
geometry=
termify=
rotate=
sticky=
floating=
autotile=
into_scratchpad=
mark=
term_title=
while getopts b:w:o:g:I:r:stfF:aSM:T:h name; do
	case "$name" in
	b) # border
		border="${OPTARG}" ;;
	w) # move to workspace
		wspace="${OPTARG}" ;;
	o) # move to output
		output="${OPTARG}" ;;
	g) # geometry
		geometry="${OPTARG}"
		floating=1
		;;
	t) # termify
		termify=1 ;;
	r) # rotate
		if ! command -v cage >/dev/null; then
			printf "%s: cage not installed but required to rotate containers" "$0" >&2
			exit 1
		fi
		rotate="${OPTARG}"
		;;
	s) # set container to sticky
		sticky=1 ;;
	f) # set container to float
		floating=1 ;;
	F)
		floating="${OPTARG}"
		;;
	a) # auto-tiling enabled
		autotile=1 ;;
	S) # launch into scratchpad
		into_scratchpad=1 ;;
	M) # mark
		mark="${OPTARG}" ;;
	T) # set terminal title
		term_title="${OPTARG}" ;;

	h | ?)
		printf "usage: %s [-bwogIrsfoSMT] COMMAND\n" "$(basename "$0")"
		printf "\n"
		printf "  -t \t\trun COMMAND in \$TERM\n"
		printf "  -b BORDER \tset border property\n"
		printf "  -w WORKSPACE \tmove to workspace\n"
		printf "  -o OUTPUT \tmove to output\n"
		printf "  -g GEOMETRY \tresize/position window\n"
		printf "  -r ROTATION \trotate the container, requires cage(1)\n"
		printf "  -s \t\tmake COMMAND a sticky window, appearing on all workspaces\n"
		printf "  -f \t\tmake COMMAND a floating window\n"
		printf "  -F OPTIONS\tmake COMMAND a floating window\n\t\tsee centercon(1) for possible options\n"
		printf "  -a \t\tauto-tiling; always open COMMAND on the shorter edge of\n\t\tthe current container\n"
		printf "  -S \t\tmove the container into the scratchpad\n"
		printf "  -M MARK\t\set MARK on the container\n"
		printf "  -T TERM_TITLE\t\set a title on the terminal (only makes sense with -t)\n"
		printf "\n"
		printf "See sway(5) for possible options for BORDER, WORKSPACE, OUTPUT and GEOMETRY.\n"
		exit 2
		;;
	esac
	# block until we have a con_id matching on pid of the spawned process
	patience=1
done
shift $((OPTIND - 1))

arg0=$1
if [ -z "${arg0}" ]; then
	exec "$0" -h
fi
shift

command="${arg0} ${*}"

# needs to be termified
# spawn a $TERM instance and exec that
if [ -n "${termify}" ]; then
	if [ -n "${floating}" ]; then
		term_args="floaterm"
	else
		term_args="foot"
	fi
	term_args="${term_args} --title=${term_title:-$(basename "${arg0}")} --app-id=${term_title:-$(basename "${arg0}")}"
	if [ -n "${SWX_TERM_ARGS}" ]; then
		term_args="${term_args} ${SWX_TERM_ARGS}"
	fi
	command="${term_args} -- ${command}"
fi

# needs to be rotated
# spawn a cage(1) instance and exec that, takes previous $command
if [ -n "${rotate}" ]; then
	# figure out how many "-r" flags we need
	rotate_args=""
	while [ $(((rotate / 90) >= 1)) = 1 ]; do
		rotate_args="${rotate_args} -r"
		rotate=$((rotate - 90))
	done
	# allow for the firefox workaround that spawns a kiosk window
	# under firejail (to avoid profile folder issues)
	if echo "$command" | grep -E 'firefox'; then
		# command="firejail --overlay-tmpfs --profile=firefox ${command} --no-remote --kiosk"
		command="firefox-kiosk ${command}"
	fi
	command="cage -d ${rotate_args} -- \"${command}\""
fi

if [ -n "${autotile}" ]; then
	layout=$(swq current con | jq -r '.layout')
	# layout=$(swq current workspace | jq -r '.. | select(.type?=="con" and .name?==null) | .layout')
	# notify-send swx "${layout}"
	case "${layout}" in
	split*)
		if [ "$(swq current con | jq '.rect | .width > .height')" = "true" ]; then
			swaymsg splith
		else
			swaymsg splitv
		fi
		;;
	esac
fi

swaymsg exec -- "${command}" >/dev/null
[ $? ] || {
	printf "error: failed to spawn \"${command}\"\n"
	exit 1
}
[ -z "$patience" ] && exit 0
# waiting for container to appear
con_id="$(swaymsg -t subscribe '["window"]' | jq -s '.[]|select(.change=="new")|.container.id')"

sway_props_done=
while [ -z "${sway_props_done}" ]; do
	if [ -n "${floating}" ]; then
		swaymsg -- [con_id="${con_id}"] fullscreen disable
		swaymsg -- [con_id="${con_id}"] floating enable
		[ "${floating}" != "1" ] && centercon -w "${floating}"
	fi
	if [ -n "${output}" ]; then
		swaymsg -- [con_id="${con_id}"] move output "${output}"
	fi
	if [ -n "${wspace}" ]; then
		swaymsg -- [con_id="${con_id}"] move workspace "${wspace}"
	fi
	if [ -n "${into_scratchpad}" ]; then
		swaymsg -- [con_id="${con_id}"] move to scratchpad
	fi
	if [ -n "${mark}" ]; then
		swaymsg -- [con_id="${con_id}"] mark --add "${mark}"
	fi
	if [ -n "${geometry}" ]; then
		wh=$(printf "%s" "${geometry}" | cut -d' ' -f2)
		swaymsg -- [con_id="${con_id}"] resize set \
			width "$(printf "%s" "${wh}" | cut -d'x' -f1)" px \
			height "$(printf "%s" "${wh}" | cut -d'x' -f2)" px
		xy=$(printf "%s" "${geometry}" | cut -d' ' -f1)
		swaymsg -- [con_id="${con_id}"] move absolute position \
			"$(printf "%s" "${xy}" | cut -d',' -f1)" px \
			"$(printf "%s" "${xy}" | cut -d',' -f2)" px
	fi
	if [ -n "${sticky}" ]; then
		swaymsg -- [con_id="${con_id}"] sticky enable
	fi
	if [ -n "${border}" ]; then
		swaymsg -- [con_id="${con_id}"] border "${border}"
	fi
	sway_props_done=1
done >/dev/null