summaryrefslogtreecommitdiff
path: root/bin/emacsclient
blob: 770ef66ad8ddfcb272057a3ace2d4ff65835c5c6 (plain)
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
#!/bin/bash

# Copyright (C) 2022  Sean Whitton
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.



# emacsclient(1) wrapper to handle updating the environment of existing
# daemons and choosing between installed and in-tree builds of Emacs.

min_arg=0
may_start=true
want_update=false
want_installed=false
devel_running=false
installed_running=false
socket_dir="/run/user/$(id -u)/emacs/"

get_listener () {
    local socket="$1"
    local listener=$(ss -Hplx src "$socket" \
			 | perl -wne'/pid=(\d+)/ and print $1')
    if [ -z "$listener" -a -e "$socket" ]; then
	# Nothing is listening: remove dangling socket.
	rm "$socket"
    else
	echo "$listener"
    fi
}

for arg do
    shift
    case "$arg" in
	'--spw/installed')               want_installed=true	 ;;
	'--spw/no-start')                may_start=false	 ;;
	'--spw/update-environment')      want_update=true	 ;;
	'-s'|'--socket-name') min_arg=2; daemon_name="$1"        ;;&
	'-s'?*)               min_arg=1; daemon_name="${arg:2}"  ;;&
	'--socket-name='?*)   min_arg=1; daemon_name="${arg:14}" ;;&
	*) set -- "$@" "$arg" ;;
    esac
done

devel_emacs="$HOME/src/emacs/src/emacs"
devel_emacsclient="$HOME/src/emacs/lib-src/emacsclient"

installed_emacs=$(command -v emacs)
installed_emacsclient=$(PATH=$(echo "$PATH" \
				   | sed -e "s#$HOME/src/dotfiles/bin:##") \
			    command -v emacsclient)

command -v ss >/dev/null || exec "$installed_emacsclient" -a "" "$@"

socket="$socket_dir${daemon_name:-server}"
listener="$(get_listener $socket)"
if [ -n "$listener" ]; then
    case "$(ps h -o exe $listener | sed -e 's/ (deleted)$//')" in
	"$devel_emacs")                 devel_running=true     ;;
	"$(realpath $installed_emacs)") installed_running=true ;;
	*) echo >&2 "Unknown Emacs listening on $socket"; exit 1  ;;
    esac
fi

if [ -z "$daemon_name" ] && $devel_running && $want_installed; then
    kill "$listener"

    # Detach gdb so that Emacs can handle the SIGTERM.
    # We also have to quit gdb so that `gdb-inferior-status' is reset from
    # "signal-received"; see `spw/gdbmacs-attach' command.
    [ -n "$(get_listener ${socket_dir}gdbmacs)" ] \
	&& emacsclient -sgdbmacs --eval '(gud-basic-call "detach")' \
		       --eval '(gud-basic-call "quit")'

    [ -e "$socket" ] && inotifywait -qqt 2 -e delete "$socket"
    devel_running=false
fi

[ -z "$listener" ] && ! $may_start && exit 0

# We check whether there is something which looks like the installed Emacs
# running because we do not want the in-tree emacsclient to try to connect to
# that, and we check whether there is a build in progress and no running
# daemon, as we don't want to start a new daemon until the build is finished.
# The idea is that I can always hit C-i e without worrying about builds.
if ! $want_installed && ! $installed_running \
	&& [ -e "$HOME/src/emacs/admin/flagfile.melete" \
		-a -x "$HOME/src/emacs/lib-src/emacsclient" \
		-a -d "/usr/share/emacs-snapshot/site-lisp/elpa" ] \
	&& ! ( ! $devel_running && ps h -o pid -C make \
		       | xargs pwdx | grep -q "$HOME/src/emacs" ); then
    emacs="$devel_emacs"
    emacsclient="$devel_emacsclient"
else
    emacs="$installed_emacs"
    emacsclient="$installed_emacsclient"
fi

# Update Emacs daemon environment vars for a new interactive session.  This
# allows us to have the Emacs daemon survive restarts of the graphical desktop
# environment, and to update Emacs on a remote host when there is a new
# forwarded SSH agent socket.  See 'upenv' in .bash_defns.
if $want_update && [ -n "$listener" ]; then
    for var in DISPLAY WAYLAND_DISPLAY \
		       XAUTHORITY WINDOWID \
		       XDG_SESSION_TYPE XDG_CURRENT_DESKTOP \
		       SWAYSOCK I3SOCK \
		       SSH_ASKPASS SSH_CONNECTION \
		       SSH_AUTH_SOCK SSH_AGENT_PID; do
	eval isset=\${$var+x}
	if [ "$isset" = "x" ]; then
	    eval val=\$$var
	    args="$args"' "'$var'" "'$val'"'
	fi
    done
    [ -n "$args" ] \
	&& "$emacsclient" -s"${daemon_name:-server}" \
			  --eval "(spw/update-environment$args))"
fi

# emacsclient(1) requires an argument.  As a special case, if there are no
# arguments except -s/--socket-name, and no daemon is running, just start one.
# And if there were no arguments but we were requested just to update env
# vars, exit peacefully.
if [ "$#" -eq "$min_arg" -a -z "$listener" ]; then
    exec "$emacs" --daemon"${daemon_name:+=$daemon_name}"
elif ! ( [ "$#" -eq "$min_arg" ] && $want_update ); then
    exec "$emacsclient" -a "" "$@"
fi